- malloc replacements
[sip-router] / udp_server.c
1 /*
2  * $Id$
3  */
4
5 #include <stdlib.h>
6 #include <string.h>
7 #include <sys/types.h>
8 #include <sys/socket.h>
9 #include <netinet/in.h>
10 #include <errno.h>
11
12
13 #include "udp_server.h"
14 #include "config.h"
15 #include "dprint.h"
16 #include "receive.h"
17 #include "mem.h"
18
19 #ifdef DEBUG_DMALLOC
20 #include <dmalloc.h>
21 #endif
22
23 int udp_sock;
24
25 int probe_max_receive_buffer( int udp_sock )
26 {
27         int optval, optvallen;
28         int ioptval, ioptvallen;
29         int foptval, foptvallen;
30         int voptval, voptvallen;
31         int i;
32         int phase=0;
33
34         /* jku: try to increase buffer size as much as we can */
35         ioptvallen=sizeof(ioptval);
36         if (getsockopt( udp_sock, SOL_SOCKET, SO_RCVBUF, (void*) &ioptval,
37                     &ioptvallen) == -1 )
38         {
39                 LOG(L_ERR, "ERROR: udp_init: getsockopt: %s\n", strerror(errno));
40                 return -1;
41         }
42         if ( ioptval==0 ) 
43         {
44                 LOG(L_DBG, "DEBUG: udp_init: SO_RCVBUF initialy set to 0; resetting to %d\n",
45                         BUFFER_INCREMENT );
46                 ioptval=BUFFER_INCREMENT;
47         } else LOG(L_INFO, "INFO: udp_init: SO_RCVBUF is initially %d\n", ioptval );
48         for (optval=ioptval; ;  ) {
49                 /* increase size; double in initial phase, add linearly later */
50                 if (phase==0) optval <<= 1; else optval+=BUFFER_INCREMENT;
51                 if (optval > maxbuffer) if (phase==1) break; else { phase=1; optval >>=1; continue; }
52                 LOG(L_DBG, "DEBUG: udp_init: trying SO_RCVBUF: %d\n", optval );
53                 if (setsockopt( udp_sock, SOL_SOCKET, SO_RCVBUF,
54                              (void*)&optval, sizeof(optval)) ==-1)
55                 {
56                         /* Solaris returns -1 if asked size too big; Linux ignores */
57                         LOG(L_DBG, "DEBUG: udp_init: SOL_SOCKET failed for %d, phase %d: %s\n",
58                             optval,  phase, strerror(errno) );
59                         /* if setting buffer size failed and still in the aggressive
60                            phase, try less agressively; otherwise give up 
61                         */
62                         if (phase==0) { phase=1; optval >>=1 ; continue; } 
63                         else break;
64                 } 
65                 /* verify if change has taken effect */
66                 /* Linux note -- otherwise I would never know that; funny thing: Linux
67                    doubles size for which we asked in setsockopt
68                 */
69                 voptvallen=sizeof(voptval);
70                 if (getsockopt( udp_sock, SOL_SOCKET, SO_RCVBUF, (void*) &voptval,
71                     &voptvallen) == -1 )
72                 {
73                         LOG(L_ERR, "ERROR: udp_init: getsockopt: %s\n", strerror(errno));
74                         return -1;
75                 } else {
76                         LOG(L_DBG, "DEBUG: setting SO_RCVBUF; set=%d,verify=%d\n", 
77                                 optval, voptval);
78                         if (voptval<optval) {
79                                 LOG(L_DBG, "DEBUG: setting SO_RCVBUF has no effect\n");
80                                 /* if setting buffer size failed and still in the aggressive
81                                 phase, try less agressively; otherwise give up 
82                                 */
83                                 if (phase==0) { phase=1; optval >>=1 ; continue; } 
84                                 else break;
85                         } 
86                 }
87
88         } /* for ... */
89         foptvallen=sizeof(foptval);
90         if (getsockopt( udp_sock, SOL_SOCKET, SO_RCVBUF, (void*) &foptval,
91                     &foptvallen) == -1 )
92         {
93                 LOG(L_ERR, "ERROR: udp_init: getsockopt: %s\n", strerror(errno));
94                 return -1;
95         }
96         LOG(L_INFO, "INFO: udp_init: SO_RCVBUF is finally %d\n", foptval );
97
98         return 0;
99
100         /* EoJKU */
101 }
102
103 int udp_init(unsigned long ip, unsigned short port)
104 {
105         struct sockaddr_in* addr;
106         int optval, optvallen;
107
108
109         addr=(struct sockaddr_in*)malloc(sizeof(struct sockaddr));
110         if (addr==0){
111                 LOG(L_ERR, "ERROR: udp_init: out of memory\n");
112                 goto error;
113         }
114         addr->sin_family=AF_INET;
115         addr->sin_port=htons(port);
116         addr->sin_addr.s_addr=ip;
117
118         udp_sock = socket(PF_INET, SOCK_DGRAM, 0);
119         if (udp_sock==-1){
120                 LOG(L_ERR, "ERROR: udp_init: socket: %s\n", strerror(errno));
121                 goto error;
122         }
123         /* set sock opts? */
124         optval=1;
125         if (setsockopt(udp_sock, SOL_SOCKET, SO_REUSEADDR,
126                                         (void*)&optval, sizeof(optval)) ==-1)
127         {
128                 LOG(L_ERR, "ERROR: udp_init: setsockopt: %s\n", strerror(errno));
129                 goto error;
130         }
131
132         if ( probe_max_receive_buffer(udp_sock)==-1) goto error;
133
134
135         if (bind(udp_sock, (struct sockaddr*) addr, sizeof(struct sockaddr))==-1){
136                 LOG(L_ERR, "ERROR: udp_init: bind: %s\n", strerror(errno));
137                 goto error;
138         }
139
140         free(addr);
141         return 0;
142
143 error:
144         if (addr) free(addr);
145         return -1;
146 }
147
148
149
150 int udp_rcv_loop()
151 {
152         unsigned len;
153         char* buf;
154         struct sockaddr* from;
155         int fromlen;
156
157         buf=pkg_malloc(BUF_SIZE+1);
158         if (buf==0){
159                 LOG(L_ERR, "ERROR: udp_rcv_loop: could not allocate receive"
160                                  " buffer\n");
161                 goto error;
162         }
163         from=(struct sockaddr*) malloc(sizeof(struct sockaddr));
164         if (from==0){
165                 LOG(L_ERR, "ERROR: udp_rcv_loop: out of memory\n");
166                 goto error;
167         }
168
169         for(;;){
170                 fromlen=sizeof(struct sockaddr);
171                 len=recvfrom(udp_sock, buf, BUF_SIZE, 0, from, &fromlen);
172                 if (len==-1){
173                         LOG(L_ERR, "ERROR: udp_rcv_loop:recvfrom: %s\n",
174                                                 strerror(errno));
175                         if (errno==EINTR)       goto skip;
176                         else goto error;
177                 }
178                 /*debugging, make print* msg work */
179                 buf[len+1]=0;
180                 
181                 /* receive_msg must free buf too!*/
182                 receive_msg(buf, len, ((struct sockaddr_in*)from)->sin_addr.s_addr);
183                 
184         skip: /* do other stuff */
185                 
186         }
187         
188         if (from) free(from);
189         return 0;
190         
191 error:
192         if (from) free(from);
193         return -1;
194 }
195
196
197
198 /* which socket to use? main socket or new one? */
199 int udp_send(char *buf, unsigned len, struct sockaddr*  to, unsigned tolen)
200 {
201
202         int n;
203 again:
204         n=sendto(udp_sock, buf, len, 0, to, tolen);
205         if (n==-1){
206                 LOG(L_ERR, "ERROR: udp_send: sendto: %s\n", strerror(errno));
207                 if (errno==EINTR) goto again;
208         }
209         return n;
210 }