/* * $Id$ */ #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <errno.h> #include "udp_server.h" #include "config.h" #include "dprint.h" #include "receive.h" #include "mem.h" #ifdef DEBUG_DMALLOC #include <dmalloc.h> #endif int udp_sock; int probe_max_receive_buffer( int udp_sock ) { int optval, optvallen; int ioptval, ioptvallen; int foptval, foptvallen; int voptval, voptvallen; int i; int phase=0; /* jku: try to increase buffer size as much as we can */ ioptvallen=sizeof(ioptval); if (getsockopt( udp_sock, SOL_SOCKET, SO_RCVBUF, (void*) &ioptval, &ioptvallen) == -1 ) { LOG(L_ERR, "ERROR: udp_init: getsockopt: %s\n", strerror(errno)); return -1; } if ( ioptval==0 ) { LOG(L_DBG, "DEBUG: udp_init: SO_RCVBUF initialy set to 0; resetting to %d\n", BUFFER_INCREMENT ); ioptval=BUFFER_INCREMENT; } else LOG(L_INFO, "INFO: udp_init: SO_RCVBUF is initially %d\n", ioptval ); for (optval=ioptval; ; ) { /* increase size; double in initial phase, add linearly later */ if (phase==0) optval <<= 1; else optval+=BUFFER_INCREMENT; if (optval > maxbuffer) if (phase==1) break; else { phase=1; optval >>=1; continue; } LOG(L_DBG, "DEBUG: udp_init: trying SO_RCVBUF: %d\n", optval ); if (setsockopt( udp_sock, SOL_SOCKET, SO_RCVBUF, (void*)&optval, sizeof(optval)) ==-1) { /* Solaris returns -1 if asked size too big; Linux ignores */ LOG(L_DBG, "DEBUG: udp_init: SOL_SOCKET failed for %d, phase %d: %s\n", optval, phase, strerror(errno) ); /* if setting buffer size failed and still in the aggressive phase, try less agressively; otherwise give up */ if (phase==0) { phase=1; optval >>=1 ; continue; } else break; } /* verify if change has taken effect */ /* Linux note -- otherwise I would never know that; funny thing: Linux doubles size for which we asked in setsockopt */ voptvallen=sizeof(voptval); if (getsockopt( udp_sock, SOL_SOCKET, SO_RCVBUF, (void*) &voptval, &voptvallen) == -1 ) { LOG(L_ERR, "ERROR: udp_init: getsockopt: %s\n", strerror(errno)); return -1; } else { LOG(L_DBG, "DEBUG: setting SO_RCVBUF; set=%d,verify=%d\n", optval, voptval); if (voptval<optval) { LOG(L_DBG, "DEBUG: setting SO_RCVBUF has no effect\n"); /* if setting buffer size failed and still in the aggressive phase, try less agressively; otherwise give up */ if (phase==0) { phase=1; optval >>=1 ; continue; } else break; } } } /* for ... */ foptvallen=sizeof(foptval); if (getsockopt( udp_sock, SOL_SOCKET, SO_RCVBUF, (void*) &foptval, &foptvallen) == -1 ) { LOG(L_ERR, "ERROR: udp_init: getsockopt: %s\n", strerror(errno)); return -1; } LOG(L_INFO, "INFO: udp_init: SO_RCVBUF is finally %d\n", foptval ); return 0; /* EoJKU */ } int udp_init(unsigned long ip, unsigned short port) { struct sockaddr_in* addr; int optval, optvallen; addr=(struct sockaddr_in*)malloc(sizeof(struct sockaddr)); if (addr==0){ LOG(L_ERR, "ERROR: udp_init: out of memory\n"); goto error; } addr->sin_family=AF_INET; addr->sin_port=htons(port); addr->sin_addr.s_addr=ip; udp_sock = socket(PF_INET, SOCK_DGRAM, 0); if (udp_sock==-1){ LOG(L_ERR, "ERROR: udp_init: socket: %s\n", strerror(errno)); goto error; } /* set sock opts? */ optval=1; if (setsockopt(udp_sock, SOL_SOCKET, SO_REUSEADDR, (void*)&optval, sizeof(optval)) ==-1) { LOG(L_ERR, "ERROR: udp_init: setsockopt: %s\n", strerror(errno)); goto error; } if ( probe_max_receive_buffer(udp_sock)==-1) goto error; if (bind(udp_sock, (struct sockaddr*) addr, sizeof(struct sockaddr))==-1){ LOG(L_ERR, "ERROR: udp_init: bind: %s\n", strerror(errno)); goto error; } free(addr); return 0; error: if (addr) free(addr); return -1; } int udp_rcv_loop() { unsigned len; char* buf; struct sockaddr* from; int fromlen; from=(struct sockaddr*) malloc(sizeof(struct sockaddr)); if (from==0){ LOG(L_ERR, "ERROR: udp_rcv_loop: out of memory\n"); goto error; } for(;;){ buf=pkg_malloc(BUF_SIZE+1); if (buf==0){ LOG(L_ERR, "ERROR: udp_rcv_loop: could not allocate receive" " buffer\n"); goto error; } fromlen=sizeof(struct sockaddr); len=recvfrom(udp_sock, buf, BUF_SIZE, 0, from, &fromlen); if (len==-1){ LOG(L_ERR, "ERROR: udp_rcv_loop:recvfrom: %s\n", strerror(errno)); if (errno==EINTR) goto skip; else goto error; } /*debugging, make print* msg work */ buf[len+1]=0; /* receive_msg must free buf too!*/ receive_msg(buf, len, ((struct sockaddr_in*)from)->sin_addr.s_addr); skip: /* do other stuff */ } if (from) free(from); return 0; error: if (from) free(from); return -1; } /* which socket to use? main socket or new one? */ int udp_send(char *buf, unsigned len, struct sockaddr* to, unsigned tolen) { int n; again: n=sendto(udp_sock, buf, len, 0, to, tolen); if (n==-1){ LOG(L_ERR, "ERROR: udp_send: sendto: %s\n", strerror(errno)); if (errno==EINTR) goto again; } return n; }