#include "snmpsock.h" #include #include #include #include #include #include #include #include #include #include #include #include #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; }