4352e2ad2c3d37c5c85898fc1d784823668ce7b7
[sip-router] / udp_server.c
1 /*
2  * $Id$
3  *
4  * Copyright (C) 2001-2003 Fhg Fokus
5  *
6  * This file is part of ser, a free SIP server.
7  *
8  * ser is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version
12  *
13  * For a license to use the ser software under conditions
14  * other than those described here, or to purchase support for this
15  * software, please contact iptel.org by e-mail at the following addresses:
16  *    info@iptel.org
17  *
18  * ser is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License 
24  * along with this program; if not, write to the Free Software 
25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  */
27
28
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <errno.h>
35 #include <arpa/inet.h>
36 #ifdef __linux__
37         #include <linux/types.h>
38         #include <linux/errqueue.h>
39 #endif
40
41
42 #include "udp_server.h"
43 #include "globals.h"
44 #include "config.h"
45 #include "dprint.h"
46 #include "receive.h"
47 #include "mem/mem.h"
48 #include "ip_addr.h"
49
50 #ifdef DEBUG_DMALLOC
51 #include <mem/dmalloc.h>
52 #endif
53
54 #ifdef DBG_MSG_QA
55 /* message quality assurance -- frequently, bugs in ser have
56    been indicated by zero characters or long whitespaces
57    in generated messages; this debugging option aborts if
58    any such message is sighted
59 */
60 static int dbg_msg_qa(char *buf, int len)
61 {
62 #define _DBG_WS_LEN 3
63 #define _DBG_WS "   "
64
65         char *scan;
66         int my_len;
67         int space_cnt;
68         enum { QA_ANY, QA_SPACE, QA_EOL1 } state;
69
70
71         /* is there a zero character inthere ? */       
72         if (memchr(buf, 0, len)) {
73                 LOG(L_CRIT, "BUG: message with 0 in it\n");
74                 return 0;
75         }
76
77         my_len=len;
78         scan=buf;
79         state=QA_ANY;
80         space_cnt=0;
81
82         while(my_len) {
83                 switch(*scan) {
84                         case ' ':       if (state==QA_SPACE) {
85                                                         space_cnt++;
86                                                         if (space_cnt==4) {
87                                                                 LOG(L_CRIT, "BUG(propably): DBG_MSG_QA: "
88                                                                         "too many spaces\n");
89                                                                 return 0;
90                                                         }
91                                                 } else space_cnt=0;
92                                                 state=QA_SPACE; 
93                                                 break;
94
95                         case '\r':      /* ignore */
96                                                 space_cnt=0;
97                                                 break;
98
99                         case '\n': /* don't proceed to body on EoH */
100                                                 if (state==QA_EOL1) goto qa_passed;
101                                                 space_cnt=0;
102                                                 state=QA_EOL1;
103                                                 break;
104
105                         default:        space_cnt=0;
106                                                 state=QA_ANY;
107                                                 break;
108                 }
109                 scan++;
110                 my_len--;
111         }
112
113
114 qa_passed:
115         return 1;
116 }
117
118 #endif
119
120
121 int probe_max_receive_buffer( int udp_sock )
122 {
123         int optval;
124         int ioptval;
125         unsigned int ioptvallen;
126         int foptval;
127         unsigned int foptvallen;
128         int voptval;
129         unsigned int voptvallen;
130         int phase=0;
131
132         /* jku: try to increase buffer size as much as we can */
133         ioptvallen=sizeof(ioptval);
134         if (getsockopt( udp_sock, SOL_SOCKET, SO_RCVBUF, (void*) &ioptval,
135                     &ioptvallen) == -1 )
136         {
137                 LOG(L_ERR, "ERROR: udp_init: getsockopt: %s\n", strerror(errno));
138                 return -1;
139         }
140         if ( ioptval==0 ) 
141         {
142                 LOG(L_DBG, "DEBUG: udp_init: SO_RCVBUF initialy set to 0; resetting to %d\n",
143                         BUFFER_INCREMENT );
144                 ioptval=BUFFER_INCREMENT;
145         } else LOG(L_INFO, "INFO: udp_init: SO_RCVBUF is initially %d\n", ioptval );
146         for (optval=ioptval; ;  ) {
147                 /* increase size; double in initial phase, add linearly later */
148                 if (phase==0) optval <<= 1; else optval+=BUFFER_INCREMENT;
149                 if (optval > maxbuffer){
150                         if (phase==1) break; 
151                         else { phase=1; optval >>=1; continue; }
152                 }
153                 LOG(L_DBG, "DEBUG: udp_init: trying SO_RCVBUF: %d\n", optval );
154                 if (setsockopt( udp_sock, SOL_SOCKET, SO_RCVBUF,
155                         (void*)&optval, sizeof(optval)) ==-1){
156                         /* Solaris returns -1 if asked size too big; Linux ignores */
157                         LOG(L_DBG, "DEBUG: udp_init: SOL_SOCKET failed"
158                                         " for %d, phase %d: %s\n", optval, phase, strerror(errno));
159                         /* if setting buffer size failed and still in the aggressive
160                            phase, try less agressively; otherwise give up 
161                         */
162                         if (phase==0) { phase=1; optval >>=1 ; continue; } 
163                         else break;
164                 } 
165                 /* verify if change has taken effect */
166                 /* Linux note -- otherwise I would never know that; funny thing: Linux
167                    doubles size for which we asked in setsockopt
168                 */
169                 voptvallen=sizeof(voptval);
170                 if (getsockopt( udp_sock, SOL_SOCKET, SO_RCVBUF, (void*) &voptval,
171                     &voptvallen) == -1 )
172                 {
173                         LOG(L_ERR, "ERROR: udp_init: getsockopt: %s\n", strerror(errno));
174                         return -1;
175                 } else {
176                         LOG(L_DBG, "DEBUG: setting SO_RCVBUF; set=%d,verify=%d\n", 
177                                 optval, voptval);
178                         if (voptval<optval) {
179                                 LOG(L_DBG, "DEBUG: setting SO_RCVBUF has no effect\n");
180                                 /* if setting buffer size failed and still in the aggressive
181                                 phase, try less agressively; otherwise give up 
182                                 */
183                                 if (phase==0) { phase=1; optval >>=1 ; continue; } 
184                                 else break;
185                         } 
186                 }
187         
188         } /* for ... */
189         foptvallen=sizeof(foptval);
190         if (getsockopt( udp_sock, SOL_SOCKET, SO_RCVBUF, (void*) &foptval,
191                     &foptvallen) == -1 )
192         {
193                 LOG(L_ERR, "ERROR: udp_init: getsockopt: %s\n", strerror(errno));
194                 return -1;
195         }
196         LOG(L_INFO, "INFO: udp_init: SO_RCVBUF is finally %d\n", foptval );
197
198         return 0;
199
200         /* EoJKU */
201 }
202
203 int udp_init(struct socket_info* sock_info)
204 {
205         union sockaddr_union* addr;
206         int optval;
207
208         addr=&sock_info->su;
209 /*
210         addr=(union sockaddr_union*)malloc(sizeof(union sockaddr_union));
211         if (addr==0){
212                 LOG(L_ERR, "ERROR: udp_init: out of memory\n");
213                 goto error;
214         }
215 */
216         if (init_su(addr, &sock_info->address, htons(sock_info->port_no))<0){
217                 LOG(L_ERR, "ERROR: udp_init: could not init sockaddr_union\n");
218                 goto error;
219         }
220         
221         sock_info->socket = socket(AF2PF(addr->s.sa_family), SOCK_DGRAM, 0);
222         if (sock_info->socket==-1){
223                 LOG(L_ERR, "ERROR: udp_init: socket: %s\n", strerror(errno));
224                 goto error;
225         }
226         /* set sock opts? */
227         optval=1;
228         if (setsockopt(sock_info->socket, SOL_SOCKET, SO_REUSEADDR ,
229                                         (void*)&optval, sizeof(optval)) ==-1){
230                 LOG(L_ERR, "ERROR: udp_init: setsockopt: %s\n", strerror(errno));
231                 goto error;
232         }
233 #if defined (__linux__) && defined(UDP_ERRORS)
234         optval=1;
235         /* enable error receiving on unconnected sockets */
236         if(setsockopt(sock_info->socket, SOL_IP, IP_RECVERR,
237                                         (void*)&optval, sizeof(optval)) ==-1){
238                 LOG(L_ERR, "ERROR: udp_init: setsockopt: %s\n", strerror(errno));
239                 goto error;
240         }
241 #endif
242
243
244         if ( probe_max_receive_buffer(sock_info->socket)==-1) goto error;
245         
246         if (bind(sock_info->socket,  &addr->s, sockaddru_len(*addr))==-1){
247                 LOG(L_ERR, "ERROR: udp_init: bind(%x, %p, %d) on %s: %s\n",
248                                 sock_info->socket, &addr->s, 
249                                 sockaddru_len(*addr),
250                                 sock_info->address_str.s,
251                                 strerror(errno));
252         #ifdef USE_IPV6
253                 if (addr->s.sa_family==AF_INET6)
254                         LOG(L_ERR, "ERROR: udp_init: might be caused by using a link "
255                                         " local address, try site local or global\n");
256         #endif
257                 goto error;
258         }
259
260 /*      free(addr);*/
261         return 0;
262
263 error:
264 /*      if (addr) free(addr);*/
265         return -1;
266 }
267
268
269
270 int udp_rcv_loop()
271 {
272         unsigned len;
273 #ifdef DYN_BUF
274         char* buf;
275 #else
276         static char buf [BUF_SIZE+1];
277 #endif
278
279         union sockaddr_union* from;
280         unsigned int fromlen;
281
282
283         from=(union sockaddr_union*) malloc(sizeof(union sockaddr_union));
284         if (from==0){
285                 LOG(L_ERR, "ERROR: udp_rcv_loop: out of memory\n");
286                 goto error;
287         }
288         memset(from, 0 , sizeof(union sockaddr_union));
289
290         for(;;){
291 #ifdef DYN_BUF
292                 buf=pkg_malloc(BUF_SIZE+1);
293                 if (buf==0){
294                         LOG(L_ERR, "ERROR: udp_rcv_loop: could not allocate receive"
295                                          " buffer\n");
296                         goto error;
297                 }
298 #endif
299                 fromlen=sockaddru_len(bind_address->su);
300                 len=recvfrom(bind_address->socket, buf, BUF_SIZE, 0, &from->s,
301                                                                                         &fromlen);
302                 if (len==-1){
303                         LOG(L_ERR, "ERROR: udp_rcv_loop:recvfrom:[%d] %s\n",
304                                                 errno, strerror(errno));
305                         if ((errno==EINTR)||(errno==EAGAIN)||(errno==EWOULDBLOCK)||
306                                         (errno==ECONNREFUSED))
307                                 continue; /* goto skip;*/
308                         else goto error;
309                 }
310                 /*debugging, make print* msg work */
311                 buf[len+1]=0;
312
313 #ifndef NO_ZERO_CHECKS
314                 if (len==0) {
315                         LOG(L_WARN, "WARNING: empty packet received\n");
316                         continue;
317                 }
318                 if (buf[len-1]==0) {
319                         LOG(L_WARN, "WARNING: upstream bug - 0-terminated packet\n");
320                         len--;
321                 }
322 #endif
323 #ifdef DBG_MSG_QA
324                 if (!dbg_msg_qa(buf, len)) {
325                         LOG(L_WARN, "WARNING: an incoming message didn't pass test,"
326                                                 "  drop it: %.*s\n", len, buf );
327                         continue;
328                 }
329 #endif
330                 
331                 /* receive_msg must free buf too!*/
332                 receive_msg(buf, len, from);
333                 
334         /* skip: do other stuff */
335                 
336         }
337         /*
338         if (from) free(from);
339         return 0;
340         */
341         
342 error:
343         if (from) free(from);
344         return -1;
345 }
346
347
348
349
350 /* which socket to use? main socket or new one? */
351 int udp_send(struct socket_info *source, char *buf, unsigned len,
352                                                                                 union sockaddr_union*  to)
353 {
354
355         int n;
356         int tolen;
357
358 #ifdef DBG_MSG_QA
359         /* aborts on error, does nothing otherwise */
360         if (!dbg_msg_qa( buf, len )) {
361                 LOG(L_ERR, "ERROR: udp_send: dbg_msg_qa failed\n");
362                 abort();
363         }
364 #endif
365
366         tolen=sockaddru_len(*to);
367 again:
368         n=sendto(source->socket, buf, len, 0, &to->s, tolen);
369 #ifdef XL_DEBUG
370         LOG(L_INFO, "INFO: send status: %d\n", n);
371 #endif
372         if (n==-1){
373                 LOG(L_ERR, "ERROR: udp_send: sendto(sock,%p,%d,0,%p,%d): %s(%d)\n",
374                                 buf,len,to,tolen,
375                                 strerror(errno),errno);
376                 if (errno==EINTR) goto again;
377                 if (errno==EINVAL) {
378                         LOG(L_CRIT,"CRITICAL: invalid sendtoparameters\n"
379                         "one possible reason is the server is bound to localhost and\n"
380                         "attempts to send to the net\n");
381                 }
382         }
383         return n;
384 }