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