udp_server.c
e60a9728
 /*
  * $Id$
  */
 
3e429f5c
 #include <stdlib.h>
 #include <string.h>
e60a9728
 #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"
3e429f5c
 #include "receive.h"
6fa79282
 #include "mem.h"
e60a9728
 
03150098
 #ifdef DEBUG_DMALLOC
 #include <dmalloc.h>
 #endif
e60a9728
 
 int udp_sock;
 
8eddd6f6
 int probe_max_receive_buffer( int udp_sock )
e60a9728
 {
d24a2059
 	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));
8eddd6f6
 		return -1;
d24a2059
 	}
 	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 );
53af8794
 	for (optval=ioptval; ;  ) {
d24a2059
 		/* increase size; double in initial phase, add linearly later */
 		if (phase==0) optval <<= 1; else optval+=BUFFER_INCREMENT;
53af8794
 		if (optval > maxbuffer) if (phase==1) break; else { phase=1; optval >>=1; continue; }
d24a2059
 		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)
         	{
af6fb476
 			/* Solaris returns -1 if asked size too big; Linux ignores */
d24a2059
 			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 */
af6fb476
 		/* Linux note -- otherwise I would never know that; funny thing: Linux
 		   doubles size for which we asked in setsockopt
 		*/
d24a2059
 		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));
8eddd6f6
 			return -1;
d24a2059
 		} 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;
8eddd6f6
 			} 
d24a2059
 		}
 
 	} /* 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));
8eddd6f6
 		return -1;
d24a2059
 	}
  	LOG(L_INFO, "INFO: udp_init: SO_RCVBUF is finally %d\n", foptval );
 
8eddd6f6
 	return 0;
d24a2059
 
 	/* EoJKU */
8eddd6f6
 }
 
 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;
 
d24a2059
 
dc862225
 	if (bind(udp_sock, (struct sockaddr*) addr, sizeof(struct sockaddr))==-1){
3e429f5c
 		LOG(L_ERR, "ERROR: udp_init: bind: %s\n", strerror(errno));
e60a9728
 		goto error;
 	}
 
dc862225
 	free(addr);
e60a9728
 	return 0;
 
 error:
dc862225
 	if (addr) free(addr);
e60a9728
 	return -1;
 }
 
 
 
 int udp_rcv_loop()
 {
3e429f5c
 	unsigned len;
22d4aa5d
 	char* buf;
e60a9728
 	struct sockaddr* from;
 	int fromlen;
 
 	from=(struct sockaddr*) malloc(sizeof(struct sockaddr));
 	if (from==0){
efeaaf53
 		LOG(L_ERR, "ERROR: udp_rcv_loop: out of memory\n");
e60a9728
 		goto error;
 	}
 
 	for(;;){
6bd84753
 		buf=pkg_malloc(BUF_SIZE+1);
 		if (buf==0){
 			LOG(L_ERR, "ERROR: udp_rcv_loop: could not allocate receive"
 					 " buffer\n");
 			goto error;
 		}
0c189116
 		fromlen=sizeof(struct sockaddr);
e60a9728
 		len=recvfrom(udp_sock, buf, BUF_SIZE, 0, from, &fromlen);
 		if (len==-1){
3e429f5c
 			LOG(L_ERR, "ERROR: udp_rcv_loop:recvfrom: %s\n",
 						strerror(errno));
e60a9728
 			if (errno==EINTR)	goto skip;
 			else goto error;
 		}
 		/*debugging, make print* msg work */
 		buf[len+1]=0;
22d4aa5d
 		
 		/* receive_msg must free buf too!*/
1b1b19d8
 		receive_msg(buf, len, ((struct sockaddr_in*)from)->sin_addr.s_addr);
 		
e60a9728
 	skip: /* do other stuff */
 		
 	}
1b1b19d8
 	
 	if (from) free(from);
e60a9728
 	return 0;
 	
 error:
1b1b19d8
 	if (from) free(from);
e60a9728
 	return -1;
 }
 
 
 
 /* which socket to use? main socket or new one? */
3e429f5c
 int udp_send(char *buf, unsigned len, struct sockaddr*  to, unsigned tolen)
e60a9728
 {
 
 	int n;
 again:
 	n=sendto(udp_sock, buf, len, 0, to, tolen);
 	if (n==-1){
3e429f5c
 		LOG(L_ERR, "ERROR: udp_send: sendto: %s\n", strerror(errno));
e60a9728
 		if (errno==EINTR) goto again;
 	}
 	return n;
 }