- core warning fixes for 64 bits archs
[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  * History
28  * --------
29  *  2003-01-28  packet zero-termination moved to receive_msg (jiri)
30  *  2003-02-10  undoed the above changes (andrei)
31  *  2003-03-19  replaced all the mallocs/frees w/ pkg_malloc/pkg_free (andrei)
32  *  2003-04-14  set sockopts to TOS low delay (andrei)
33  */
34
35
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <netinet/in.h>
41 #include <netinet/in_systm.h>
42 #include <netinet/ip.h>
43 #include <errno.h>
44 #include <arpa/inet.h>
45 #ifdef __linux__
46         #include <linux/types.h>
47         #include <linux/errqueue.h>
48 #endif
49
50
51 #include "udp_server.h"
52 #include "globals.h"
53 #include "config.h"
54 #include "dprint.h"
55 #include "receive.h"
56 #include "mem/mem.h"
57 #include "ip_addr.h"
58
59
60 #ifdef DBG_MSG_QA
61 /* message quality assurance -- frequently, bugs in ser have
62    been indicated by zero characters or long whitespaces
63    in generated messages; this debugging option aborts if
64    any such message is sighted
65 */
66 static int dbg_msg_qa(char *buf, int len)
67 {
68 #define _DBG_WS_LEN 3
69 #define _DBG_WS "   "
70
71         char *scan;
72         int my_len;
73         int space_cnt;
74         enum { QA_ANY, QA_SPACE, QA_EOL1 } state;
75
76
77         /* is there a zero character inthere ? */       
78         if (memchr(buf, 0, len)) {
79                 LOG(L_CRIT, "BUG: message with 0 in it\n");
80                 return 0;
81         }
82
83         my_len=len;
84         scan=buf;
85         state=QA_ANY;
86         space_cnt=0;
87
88         while(my_len) {
89                 switch(*scan) {
90                         case ' ':       if (state==QA_SPACE) {
91                                                         space_cnt++;
92                                                         if (space_cnt==4) {
93                                                                 LOG(L_CRIT, "BUG(propably): DBG_MSG_QA: "
94                                                                         "too many spaces\n");
95                                                                 return 0;
96                                                         }
97                                                 } else space_cnt=0;
98                                                 state=QA_SPACE; 
99                                                 break;
100
101                         case '\r':      /* ignore */
102                                                 space_cnt=0;
103                                                 break;
104
105                         case '\n': /* don't proceed to body on EoH */
106                                                 if (state==QA_EOL1) goto qa_passed;
107                                                 space_cnt=0;
108                                                 state=QA_EOL1;
109                                                 break;
110
111                         default:        space_cnt=0;
112                                                 state=QA_ANY;
113                                                 break;
114                 }
115                 scan++;
116                 my_len--;
117         }
118
119
120 qa_passed:
121         return 1;
122 }
123
124 #endif
125
126
127 int probe_max_receive_buffer( int udp_sock )
128 {
129         int optval;
130         int ioptval;
131         unsigned int ioptvallen;
132         int foptval;
133         unsigned int foptvallen;
134         int voptval;
135         unsigned int voptvallen;
136         int phase=0;
137
138         /* jku: try to increase buffer size as much as we can */
139         ioptvallen=sizeof(ioptval);
140         if (getsockopt( udp_sock, SOL_SOCKET, SO_RCVBUF, (void*) &ioptval,
141                     &ioptvallen) == -1 )
142         {
143                 LOG(L_ERR, "ERROR: udp_init: getsockopt: %s\n", strerror(errno));
144                 return -1;
145         }
146         if ( ioptval==0 ) 
147         {
148                 LOG(L_DBG, "DEBUG: udp_init: SO_RCVBUF initialy set to 0; resetting to %d\n",
149                         BUFFER_INCREMENT );
150                 ioptval=BUFFER_INCREMENT;
151         } else LOG(L_INFO, "INFO: udp_init: SO_RCVBUF is initially %d\n", ioptval );
152         for (optval=ioptval; ;  ) {
153                 /* increase size; double in initial phase, add linearly later */
154                 if (phase==0) optval <<= 1; else optval+=BUFFER_INCREMENT;
155                 if (optval > maxbuffer){
156                         if (phase==1) break; 
157                         else { phase=1; optval >>=1; continue; }
158                 }
159                 LOG(L_DBG, "DEBUG: udp_init: trying SO_RCVBUF: %d\n", optval );
160                 if (setsockopt( udp_sock, SOL_SOCKET, SO_RCVBUF,
161                         (void*)&optval, sizeof(optval)) ==-1){
162                         /* Solaris returns -1 if asked size too big; Linux ignores */
163                         LOG(L_DBG, "DEBUG: udp_init: SOL_SOCKET failed"
164                                         " for %d, phase %d: %s\n", optval, phase, strerror(errno));
165                         /* if setting buffer size failed and still in the aggressive
166                            phase, try less agressively; otherwise give up 
167                         */
168                         if (phase==0) { phase=1; optval >>=1 ; continue; } 
169                         else break;
170                 } 
171                 /* verify if change has taken effect */
172                 /* Linux note -- otherwise I would never know that; funny thing: Linux
173                    doubles size for which we asked in setsockopt
174                 */
175                 voptvallen=sizeof(voptval);
176                 if (getsockopt( udp_sock, SOL_SOCKET, SO_RCVBUF, (void*) &voptval,
177                     &voptvallen) == -1 )
178                 {
179                         LOG(L_ERR, "ERROR: udp_init: getsockopt: %s\n", strerror(errno));
180                         return -1;
181                 } else {
182                         LOG(L_DBG, "DEBUG: setting SO_RCVBUF; set=%d,verify=%d\n", 
183                                 optval, voptval);
184                         if (voptval<optval) {
185                                 LOG(L_DBG, "DEBUG: setting SO_RCVBUF has no effect\n");
186                                 /* if setting buffer size failed and still in the aggressive
187                                 phase, try less agressively; otherwise give up 
188                                 */
189                                 if (phase==0) { phase=1; optval >>=1 ; continue; } 
190                                 else break;
191                         } 
192                 }
193         
194         } /* for ... */
195         foptvallen=sizeof(foptval);
196         if (getsockopt( udp_sock, SOL_SOCKET, SO_RCVBUF, (void*) &foptval,
197                     &foptvallen) == -1 )
198         {
199                 LOG(L_ERR, "ERROR: udp_init: getsockopt: %s\n", strerror(errno));
200                 return -1;
201         }
202         LOG(L_INFO, "INFO: udp_init: SO_RCVBUF is finally %d\n", foptval );
203
204         return 0;
205
206         /* EoJKU */
207 }
208
209 int udp_init(struct socket_info* sock_info)
210 {
211         union sockaddr_union* addr;
212         int optval;
213
214         addr=&sock_info->su;
215 /*
216         addr=(union sockaddr_union*)pkg_malloc(sizeof(union sockaddr_union));
217         if (addr==0){
218                 LOG(L_ERR, "ERROR: udp_init: out of memory\n");
219                 goto error;
220         }
221 */
222         sock_info->proto=PROTO_UDP;
223         if (init_su(addr, &sock_info->address, sock_info->port_no)<0){
224                 LOG(L_ERR, "ERROR: udp_init: could not init sockaddr_union\n");
225                 goto error;
226         }
227         
228         sock_info->socket = socket(AF2PF(addr->s.sa_family), SOCK_DGRAM, 0);
229         if (sock_info->socket==-1){
230                 LOG(L_ERR, "ERROR: udp_init: socket: %s\n", strerror(errno));
231                 goto error;
232         }
233         /* set sock opts? */
234         optval=1;
235         if (setsockopt(sock_info->socket, SOL_SOCKET, SO_REUSEADDR ,
236                                         (void*)&optval, sizeof(optval)) ==-1){
237                 LOG(L_ERR, "ERROR: udp_init: setsockopt: %s\n", strerror(errno));
238                 goto error;
239         }
240         /* tos */
241         optval=IPTOS_LOWDELAY;
242         if (setsockopt(sock_info->socket, IPPROTO_IP, IP_TOS, (void*)&optval, 
243                         sizeof(optval)) ==-1){
244                 LOG(L_WARN, "WARNING: udp_init: setsockopt tos: %s\n", strerror(errno));
245                 /* continue since this is not critical */
246         }
247 #if defined (__linux__) && defined(UDP_ERRORS)
248         optval=1;
249         /* enable error receiving on unconnected sockets */
250         if(setsockopt(sock_info->socket, SOL_IP, IP_RECVERR,
251                                         (void*)&optval, sizeof(optval)) ==-1){
252                 LOG(L_ERR, "ERROR: udp_init: setsockopt: %s\n", strerror(errno));
253                 goto error;
254         }
255 #endif
256
257
258         if ( probe_max_receive_buffer(sock_info->socket)==-1) goto error;
259         
260         if (bind(sock_info->socket,  &addr->s, sockaddru_len(*addr))==-1){
261                 LOG(L_ERR, "ERROR: udp_init: bind(%x, %p, %d) on %s: %s\n",
262                                 sock_info->socket, &addr->s, 
263                                 sockaddru_len(*addr),
264                                 sock_info->address_str.s,
265                                 strerror(errno));
266         #ifdef USE_IPV6
267                 if (addr->s.sa_family==AF_INET6)
268                         LOG(L_ERR, "ERROR: udp_init: might be caused by using a link "
269                                         " local address, try site local or global\n");
270         #endif
271                 goto error;
272         }
273
274 /*      pkg_free(addr);*/
275         return 0;
276
277 error:
278 /*      if (addr) pkg_free(addr);*/
279         return -1;
280 }
281
282
283
284 int udp_rcv_loop()
285 {
286         unsigned len;
287 #ifdef DYN_BUF
288         char* buf;
289 #else
290         static char buf [BUF_SIZE+1];
291 #endif
292
293         union sockaddr_union* from;
294         unsigned int fromlen;
295         struct receive_info ri;
296
297
298         from=(union sockaddr_union*) pkg_malloc(sizeof(union sockaddr_union));
299         if (from==0){
300                 LOG(L_ERR, "ERROR: udp_rcv_loop: out of memory\n");
301                 goto error;
302         }
303         memset(from, 0 , sizeof(union sockaddr_union));
304         ri.bind_address=bind_address; /* this will not change, we do it only once*/
305         ri.dst_port=bind_address->port_no;
306         ri.dst_ip=bind_address->address;
307         ri.proto=PROTO_UDP;
308         ri.proto_reserved1=ri.proto_reserved2=0;
309         for(;;){
310 #ifdef DYN_BUF
311                 buf=pkg_malloc(BUF_SIZE+1);
312                 if (buf==0){
313                         LOG(L_ERR, "ERROR: udp_rcv_loop: could not allocate receive"
314                                          " buffer\n");
315                         goto error;
316                 }
317 #endif
318                 fromlen=sockaddru_len(bind_address->su);
319                 len=recvfrom(bind_address->socket, buf, BUF_SIZE, 0, &from->s,
320                                                                                         &fromlen);
321                 if (len==-1){
322                         LOG(L_ERR, "ERROR: udp_rcv_loop:recvfrom:[%d] %s\n",
323                                                 errno, strerror(errno));
324                         if ((errno==EINTR)||(errno==EAGAIN)||(errno==EWOULDBLOCK)||
325                                         (errno==ECONNREFUSED))
326                                 continue; /* goto skip;*/
327                         else goto error;
328                 }
329                 /* we must 0-term the messages, receive_msg expects it */
330                 buf[len]=0; /* no need to save the previous char */
331
332 #ifndef NO_ZERO_CHECKS
333                 if (len==0) {
334                         LOG(L_WARN, "WARNING: empty packet received\n");
335                         continue;
336                 }
337                 if (buf[len-1]==0) {
338                         LOG(L_WARN, "WARNING: upstream bug - 0-terminated packet\n");
339                         len--;
340                 }
341 #endif
342 #ifdef DBG_MSG_QA
343                 if (!dbg_msg_qa(buf, len)) {
344                         LOG(L_WARN, "WARNING: an incoming message didn't pass test,"
345                                                 "  drop it: %.*s\n", len, buf );
346                         continue;
347                 }
348 #endif
349                 ri.src_su=*from;
350                 su2ip_addr(&ri.src_ip, from);
351                 ri.src_port=su_getport(from);
352                 
353                 
354                 /* receive_msg must free buf too!*/
355                 receive_msg(buf, len, &ri);
356                 
357         /* skip: do other stuff */
358                 
359         }
360         /*
361         if (from) pkg_free(from);
362         return 0;
363         */
364         
365 error:
366         if (from) pkg_free(from);
367         return -1;
368 }
369
370
371
372
373 /* which socket to use? main socket or new one? */
374 int udp_send(struct socket_info *source, char *buf, unsigned len,
375                                                                                 union sockaddr_union*  to)
376 {
377
378         int n;
379         int tolen;
380
381 #ifdef DBG_MSG_QA
382         /* aborts on error, does nothing otherwise */
383         if (!dbg_msg_qa( buf, len )) {
384                 LOG(L_ERR, "ERROR: udp_send: dbg_msg_qa failed\n");
385                 abort();
386         }
387 #endif
388
389         tolen=sockaddru_len(*to);
390 again:
391         n=sendto(source->socket, buf, len, 0, &to->s, tolen);
392 #ifdef XL_DEBUG
393         LOG(L_INFO, "INFO: send status: %d\n", n);
394 #endif
395         if (n==-1){
396                 LOG(L_ERR, "ERROR: udp_send: sendto(sock,%p,%d,0,%p,%d): %s(%d)\n",
397                                 buf,len,to,tolen,
398                                 strerror(errno),errno);
399                 if (errno==EINTR) goto again;
400                 if (errno==EINVAL) {
401                         LOG(L_CRIT,"CRITICAL: invalid sendtoparameters\n"
402                         "one possible reason is the server is bound to localhost and\n"
403                         "attempts to send to the net\n");
404                 }
405         }
406         return n;
407 }