#include "snmpsock.h"
#include <assert.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#define MAXPACKSIZE 10240
unsigned int maxaddrlen=sizeof(sockaddr);
struct request_t{
// int magic1;
int len;
char *addr;
pthread_cond_t req_cv;
request_t *next;
request_t *prev;
request_t(int length, char *address):len(length),addr(address),
next(NULL),prev(NULL),buf(NULL){ //,magic1(0xbadcab88),magic2(0xdeadbeef){
assert(addr!=NULL || addr!=(char*)1);
pthread_cond_init(&req_cv,NULL);
}
//filled in on the return
unsigned char *buf;
int buflen;
int errnum;
// char padding[200];
// int magic2;
};
void unlink(request_t **head,request_t *cur){
if(cur->prev)
cur->prev->next=cur->next;
if(cur->next)
cur->next->prev=cur->prev;
if(cur==*head)
*head=cur->next;
}
pthread_mutex_t pending_m=PTHREAD_MUTEX_INITIALIZER;
request_t *pending=NULL;
// void check_plist(char *file,unsigned int line){
// fprintf(stderr,"debug: thread: %lu checking at %s %u ",pthread_self(),file,
// line);
// pthread_mutex_lock(&pending_m);
// for(request_t *cur=pending;cur!=NULL;cur=cur->next){
// fprintf(stderr,"0x%x",cur);
// assert(cur->magic1==0xbadcab88);
// fputs("->",stderr);
// }
// fputc('\n',stderr);
// pthread_mutex_unlock(&pending_m);
// }
void *receiver(void *sockp){
int sock=*(int*)sockp;
int readcnt;
unsigned int fromlen;
for(;;){
sockaddr_in from;
memset(&from,0,sizeof(from));
fromlen=sizeof(sockaddr_in);
unsigned char *buf=new unsigned char[MAXPACKSIZE];
assert(buf);
fd_set rfds;
timeval tv;
int retval;
FD_ZERO(&rfds);
FD_SET(sock,&rfds);
tv.tv_sec=5;
tv.tv_usec=0;
assert((retval=select(sock+1,&rfds,NULL,NULL,&tv))!=-1);
pthread_testcancel();
// check every 5 seconds to see if we were cancelled
if(retval!=1 || !FD_ISSET(sock,&rfds))
continue;
// actually got some data
readcnt=recvfrom(sock,buf,MAXPACKSIZE,0,(sockaddr*)&from,&fromlen);
if(readcnt==-1){
if(errno==ECONNREFUSED)
continue; // just ignore those pesky icmp unreachable errors
perror("bad read in reciever thread");
exit(5);
}
pthread_mutex_lock(&pending_m);
request_t *cur;
for(cur=pending;cur!=NULL;cur=cur->next){
// assert(cur->magic1==0xbadcab88);
// assert(cur->magic2==0xdeadbeef);
if(!memcmp(&from.sin_addr,cur->addr,cur->len))
break;
}
// now cur equals either null i.e. not found or the correct element
if(cur==NULL){
fprintf(stderr,"Warning: stray packet recieved from %u.%u.%u.%u\n",
((char*)&from.sin_addr)[0]&0xff,((char*)&from.sin_addr)[1]&0xff,
((char*)&from.sin_addr)[2]&0xff,((char*)&from.sin_addr)[3]&0xff);
pthread_mutex_unlock(&pending_m);
continue;
}
cur->buf=buf;
cur->buflen=readcnt;
cur->errnum=errno;
//unlink everything
unlink(&pending,cur);
pthread_cond_signal(&cur->req_cv);
pthread_mutex_unlock(&pending_m);
}
}
SNMP_socket::SNMP_socket(int tmo, int rt, int pt):timeout(tmo),retries(rt),
port(pt){
// create socket
struct protoent *pe;
struct servent *se;
assert(pe=getprotobyname("udp"));
assert((sock=socket(AF_INET,SOCK_DGRAM,pe->p_proto))!=-1);
if(port==0){
se = getservbyname("snmp","udp");
if (NULL != se){
port=se->s_port;
endservent();
} else
port = 161; /* Fall back to hardcoded value */
} else
port=htons(port);
// create listening thread
assert(pthread_create(&listening_thr,NULL,receiver,&sock)==0);
}
SNMP_socket::~SNMP_socket(){
// zap listening thread
pthread_cancel(listening_thr);
pthread_join(listening_thr,NULL);
// close socket
close(sock);
}
unsigned char *SNMP_socket::call(int len,int type,char *addr,char *data,
int &buflen){
request_t curreq(len,addr);
if(len>maxaddrlen)
maxaddrlen=len;
// send packet
struct sockaddr_in sin;
memset((caddr_t)&sin,0,sizeof(sin));
sin.sin_family=type,
sin.sin_port=port;
memcpy((caddr_t)&sin.sin_addr,addr,len);
// insert it on the list
pthread_mutex_lock(&pending_m);
curreq.next=pending;
if(pending)
pending->prev=&curreq;
pending=&curreq;
//while there are still retries
int rt;
for(rt=retries;rt>=0;rt--){
retry:
int sendcnt=sendto(sock,data,buflen,0,(sockaddr*)&sin,
sizeof(sockaddr_in));
/* work around potential problem where ICMP port unreachable
propegates up IP stack and can appear on socket even
though problem is not with this particular packet. */
if(sendcnt==-1 && errno==ECONNREFUSED)
goto retry;
assert(sendcnt==-1 || sendcnt==buflen);
if(sendcnt==-1){
perror("Error sending packet");
// remove from list and return error
buflen=errno;
pthread_mutex_lock(&pending_m);
unlink(&pending,&curreq);
pthread_mutex_unlock(&pending_m);
return NULL;
}
// wait on condition variable
timespec tv;
tv.tv_sec=time(NULL)+timeout;
tv.tv_nsec=0;
int waitret=pthread_cond_timedwait(&curreq.req_cv,&pending_m,&tv);
if(waitret!=ETIMEDOUT)
break;
}
if(rt<0)
unlink(&pending,&curreq);
pthread_mutex_unlock(&pending_m);
// kick out bad data
if(curreq.buflen==-1){
delete curreq.buf;
buflen=curreq.errnum;
return NULL;
}
buflen=curreq.buflen;
return curreq.buf;
}
syntax highlighted by Code2HTML, v. 0.9.1