- ipv6 support (-DUSE_IPV6)
[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 #include <arpa/inet.h>
12
13
14 #include "udp_server.h"
15 #include "globals.h"
16 #include "config.h"
17 #include "dprint.h"
18 #include "receive.h"
19 #include "mem/mem.h"
20 #include "ip_addr.h"
21
22 #ifdef DEBUG_DMALLOC
23 #include <mem/dmalloc.h>
24 #endif
25
26 int udp_sock;
27
28 int probe_max_receive_buffer( int udp_sock )
29 {
30         int optval;
31         int ioptval;
32         unsigned int ioptvallen;
33         int foptval;
34         unsigned int foptvallen;
35         int voptval;
36         unsigned int voptvallen;
37         int phase=0;
38
39         /* jku: try to increase buffer size as much as we can */
40         ioptvallen=sizeof(ioptval);
41         if (getsockopt( udp_sock, SOL_SOCKET, SO_RCVBUF, (void*) &ioptval,
42                     &ioptvallen) == -1 )
43         {
44                 LOG(L_ERR, "ERROR: udp_init: getsockopt: %s\n", strerror(errno));
45                 return -1;
46         }
47         if ( ioptval==0 ) 
48         {
49                 LOG(L_DBG, "DEBUG: udp_init: SO_RCVBUF initialy set to 0; resetting to %d\n",
50                         BUFFER_INCREMENT );
51                 ioptval=BUFFER_INCREMENT;
52         } else LOG(L_INFO, "INFO: udp_init: SO_RCVBUF is initially %d\n", ioptval );
53         for (optval=ioptval; ;  ) {
54                 /* increase size; double in initial phase, add linearly later */
55                 if (phase==0) optval <<= 1; else optval+=BUFFER_INCREMENT;
56                 if (optval > maxbuffer){
57                         if (phase==1) break; 
58                         else { phase=1; optval >>=1; continue; }
59                 }
60                 LOG(L_DBG, "DEBUG: udp_init: trying SO_RCVBUF: %d\n", optval );
61                 if (setsockopt( udp_sock, SOL_SOCKET, SO_RCVBUF,
62                         (void*)&optval, sizeof(optval)) ==-1){
63                         /* Solaris returns -1 if asked size too big; Linux ignores */
64                         LOG(L_DBG, "DEBUG: udp_init: SOL_SOCKET failed"
65                                         " for %d, phase %d: %s\n", optval, phase, strerror(errno));
66                         /* if setting buffer size failed and still in the aggressive
67                            phase, try less agressively; otherwise give up 
68                         */
69                         if (phase==0) { phase=1; optval >>=1 ; continue; } 
70                         else break;
71                 } 
72                 /* verify if change has taken effect */
73                 /* Linux note -- otherwise I would never know that; funny thing: Linux
74                    doubles size for which we asked in setsockopt
75                 */
76                 voptvallen=sizeof(voptval);
77                 if (getsockopt( udp_sock, SOL_SOCKET, SO_RCVBUF, (void*) &voptval,
78                     &voptvallen) == -1 )
79                 {
80                         LOG(L_ERR, "ERROR: udp_init: getsockopt: %s\n", strerror(errno));
81                         return -1;
82                 } else {
83                         LOG(L_DBG, "DEBUG: setting SO_RCVBUF; set=%d,verify=%d\n", 
84                                 optval, voptval);
85                         if (voptval<optval) {
86                                 LOG(L_DBG, "DEBUG: setting SO_RCVBUF has no effect\n");
87                                 /* if setting buffer size failed and still in the aggressive
88                                 phase, try less agressively; otherwise give up 
89                                 */
90                                 if (phase==0) { phase=1; optval >>=1 ; continue; } 
91                                 else break;
92                         } 
93                 }
94         
95         } /* for ... */
96         foptvallen=sizeof(foptval);
97         if (getsockopt( udp_sock, SOL_SOCKET, SO_RCVBUF, (void*) &foptval,
98                     &foptvallen) == -1 )
99         {
100                 LOG(L_ERR, "ERROR: udp_init: getsockopt: %s\n", strerror(errno));
101                 return -1;
102         }
103         LOG(L_INFO, "INFO: udp_init: SO_RCVBUF is finally %d\n", foptval );
104
105         return 0;
106
107         /* EoJKU */
108 }
109
110 int udp_init(struct ip_addr* ip, unsigned short port)
111 {
112         union sockaddr_union* addr;
113         int optval;
114
115
116         addr=(union sockaddr_union*)malloc(sizeof(union sockaddr_union));
117         if (addr==0){
118                 LOG(L_ERR, "ERROR: udp_init: out of memory\n");
119                 goto error;
120         }
121         
122         if (init_su(addr, ip, htons(port)<0){
123                 LOG(L_ERR, "ERROR: udp_init: could not init sockaddr_union\n");
124                 goto error;
125         }
126         /*
127         addr->sin_family=AF_INET;
128         addr->sin_port=htons(port);
129         addr->sin_addr.s_addr=ip;
130         */
131
132         
133         udp_sock = socket(AF2PF(addr->s.sa_family), SOCK_DGRAM, 0);
134         if (udp_sock==-1){
135                 LOG(L_ERR, "ERROR: udp_init: socket: %s\n", strerror(errno));
136                 goto error;
137         }
138         /* set sock opts? */
139         optval=1;
140         if (setsockopt(udp_sock, SOL_SOCKET, SO_REUSEADDR ,
141                                         (void*)&optval, sizeof(optval)) ==-1)
142         {
143                 LOG(L_ERR, "ERROR: udp_init: setsockopt: %s\n", strerror(errno));
144                 goto error;
145         }
146
147         if ( probe_max_receive_buffer(udp_sock)==-1) goto error;
148         bind_address=ip;
149
150         if (bind(udp_sock,  &addr->s, sizeof(union sockaddr_union))==-1){
151                 LOG(L_ERR, "ERROR: udp_init: bind: %s\n", strerror(errno));
152                 goto error;
153         }
154
155         free(addr);
156         return 0;
157
158 error:
159         if (addr) free(addr);
160         return -1;
161 }
162
163
164
165 int udp_rcv_loop()
166 {
167         unsigned len;
168 #ifdef DYN_BUF
169         char* buf;
170 #else
171         static char buf [BUF_SIZE+1];
172 #endif
173
174         union sockaddr_union* from;
175         unsigned int fromlen;
176
177
178         from=(union sockaddr_union*) malloc(sizeof(union sockaddr_union));
179         if (from==0){
180                 LOG(L_ERR, "ERROR: udp_rcv_loop: out of memory\n");
181                 goto error;
182         }
183
184         for(;;){
185 #ifdef DYN_BUF
186                 buf=pkg_malloc(BUF_SIZE+1);
187                 if (buf==0){
188                         LOG(L_ERR, "ERROR: udp_rcv_loop: could not allocate receive"
189                                          " buffer\n");
190                         goto error;
191                 }
192 #endif
193                 fromlen=sizeof(union sockaddr_union);
194                 len=recvfrom(udp_sock, buf, BUF_SIZE, 0, &from->s,
195                                                 &fromlen);
196                 if (len==-1){
197                         LOG(L_ERR, "ERROR: udp_rcv_loop:recvfrom:[%d] %s\n",
198                                                 errno, strerror(errno));
199                         if ((errno==EINTR)||(errno==EAGAIN)||(errno==EWOULDBLOCK))
200                                 continue; /* goto skip;*/
201                         else goto error;
202                 }
203                 /*debugging, make print* msg work */
204                 buf[len+1]=0;
205                 
206                 /* receive_msg must free buf too!*/
207                 receive_msg(buf, len, from);
208                 
209         /* skip: do other stuff */
210                 
211         }
212         /*
213         if (from) free(from);
214         return 0;
215         */
216         
217 error:
218         if (from) free(from);
219         return -1;
220 }
221
222
223
224 /* which socket to use? main socket or new one? */
225 int udp_send(char *buf, unsigned len, union sockaddr_union*  to,
226                                 unsigned tolen)
227 {
228
229         int n;
230
231
232 again:
233         n=sendto(udp_sock, buf, len, 0, &to->s, tolen);
234         if (n==-1){
235                 LOG(L_ERR, "ERROR: udp_send: sendto(sock,%p,%d,0,%p,%d): %s(%d)\n",
236                                 buf,len,to,tolen,
237                                 strerror(errno),errno);
238                 if (errno==EINTR) goto again;
239                 if (errno==EINVAL) {
240                         LOG(L_CRIT,"CRITICAL: invalid sendtoparameters\n"
241                         "one possible reason is the server is bound to localhost and\n"
242                         "attempts to send to the net\n");
243                 }
244         }
245         return n;
246 }