http://downloads.netgear.com/files/GPL/GPL_Source_V361j_DM111PSP_series_consumer_rele...
[bcm963xx.git] / userapps / opensource / siproxd / src / siproxd.c
1 /* -*- Mode: C; c-basic-offset: 3 -*-
2     Copyright (C) 2002-2005  Thomas Ries <tries@gmx.net>
3
4     This file is part of Siproxd.
5     
6     Siproxd is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10     
11     Siproxd is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15     
16     You should have received a copy of the GNU General Public License
17     along with Siproxd; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
19 */
20
21 #include "config.h"
22
23 #include <stdio.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <signal.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31
32 #ifdef  HAVE_GETOPT_H
33 #include <getopt.h>
34 #endif
35
36 #include <osipparser2/osip_parser.h>
37
38 #include "siproxd.h"
39 #include "log.h"
40
41 static char const ident[]="$Id: siproxd.c,v 1.1.1.1 2006/05/18 10:47:25 michaelc Exp $";
42
43 /* configuration storage */
44 struct siproxd_config configuration;
45
46 /* Global File instance on pw file */
47 FILE *siproxd_passwordfile;
48
49 /* -h help option text */
50 static const char str_helpmsg[] =
51 PACKAGE "-" VERSION "-" BUILDSTR " (c) 2002-2005 Thomas Ries\n"
52 "\nUsage: siproxd [options]\n\n"
53 "options:\n"
54 #ifdef  HAVE_GETOPT_LONG
55 "       -h, --help                 help\n"
56 "       -d, --debug <pattern>      set debug-pattern\n"
57 "       -c, --config <cfgfile>     use the specified config file\n"
58 "       -p, --pid-file <pidfile>   create pid file <pidfile>\n"
59 #else
60 "       -h              help\n"
61 "       -d <pattern>    set debug-pattern\n"
62 "       -c <cfgfile>    use the specified config file\n"
63 "       -p <pidfile>    create pid file <pidfile>\n"
64 #endif
65 "";
66
67
68
69 /*
70  * module local data
71  */
72 static  int dmalloc_dump=0;
73 static  int exit_program=0;
74
75 /*
76  * local prototypes
77  */
78 static void sighandler(int sig);
79
80
81 #ifdef BUILD_STATIC
82 int siproxd_main(int argc, char *argv[])
83 #else
84 int main (int argc, char *argv[]) 
85 #endif
86 {
87    int sts;
88    int i;
89    int access;
90    char buff [BUFFER_SIZE];
91    sip_ticket_t ticket;
92
93    extern char *optarg;         /* Defined in libc getopt and unistd.h */
94    int ch1;
95    
96    char configfile[64]="siproxd";       /* basename of configfile */
97    int  config_search=1;                /* search the config file */
98    int  cmdline_debuglevel=0;
99    char *pidfilename=NULL;
100    struct sigaction act;
101
102    log_set_stderr(1);
103
104 /*
105  * setup signal handlers
106  */
107    act.sa_handler=sighandler;
108    sigemptyset(&act.sa_mask);
109    act.sa_flags=SA_RESTART;
110    if (sigaction(SIGTERM, &act, NULL)) {
111       ERROR("Failed to install SIGTERM handler");
112    }
113    if (sigaction(SIGINT, &act, NULL)) {
114       ERROR("Failed to install SIGINT handler");
115    }
116    if (sigaction(SIGUSR2, &act, NULL)) {
117       ERROR("Failed to install SIGUSR2 handler");
118    }
119
120
121 /*
122  * prepare default configuration
123  */
124    make_default_config();
125
126    log_set_pattern(configuration.debuglevel);      
127
128 /*
129  * open a the pwfile instance, so we still have access after
130  * we possibly have chroot()ed to somewhere.
131  */
132    if (configuration.proxy_auth_pwfile) {
133       siproxd_passwordfile = fopen(configuration.proxy_auth_pwfile, "r");
134    } else {
135       siproxd_passwordfile = NULL;
136    }
137
138 /*
139  * parse command line
140  */
141 {
142 #ifdef  HAVE_GETOPT_LONG
143    int option_index = 0;
144    static struct option long_options[] = {
145       {"help", no_argument, NULL, 'h'},
146       {"config", required_argument, NULL, 'c'},
147       {"debug", required_argument, NULL, 'd'},
148       {"pid-file", required_argument, NULL,'p'},
149       {0,0,0,0}
150    };
151
152     while ((ch1 = getopt_long(argc, argv, "hc:d:p:",
153                   long_options, &option_index)) != -1) {
154 #else   /* ! HAVE_GETOPT_LONG */
155     while ((ch1 = getopt(argc, argv, "hc:d:p:")) != -1) {
156 #endif
157       switch (ch1) {
158       case 'h': /* help */
159          DEBUGC(DBCLASS_CONFIG,"option: help");
160          fprintf(stderr,str_helpmsg);
161          exit(0);
162          break;
163
164       case 'c': /* load config file */
165          DEBUGC(DBCLASS_CONFIG,"option: config file=%s",optarg);
166          i=sizeof(configfile)-1;
167          strncpy(configfile,optarg,i-1);
168          configfile[i]='\0';
169          config_search=0;
170          break; 
171
172       case 'd': /* set debug level */
173          DEBUGC(DBCLASS_CONFIG,"option: set debug level: %s",optarg);
174          cmdline_debuglevel=atoi(optarg);
175          log_set_pattern(cmdline_debuglevel);
176          break;
177
178       case 'p':
179          pidfilename = optarg;
180          break;
181
182       default:
183          DEBUGC(DBCLASS_CONFIG,"no command line options");
184          break; 
185       }
186    }
187 }
188
189 /*
190  * Init stuff
191  */
192    INFO(PACKAGE"-"VERSION"-"BUILDSTR" "UNAME" starting up");
193
194    /* read the config file */
195    if (read_config(configfile, config_search) == STS_FAILURE) exit(1);
196
197    /* if a debug level > 0 has been given on the commandline use its
198       value and not what is in the config file */
199    if (cmdline_debuglevel != 0) {
200       configuration.debuglevel=cmdline_debuglevel;
201    }
202
203    /* set debug level as desired */
204    log_set_pattern(configuration.debuglevel);
205    log_set_listen_port(configuration.debugport);
206
207    /* change user and group IDs */
208    secure_enviroment();
209
210    /* daemonize if requested to */
211    if (configuration.daemonize) {
212       DEBUGC(DBCLASS_CONFIG,"daemonizing");
213       if (fork()!=0) exit(0);
214       setsid();
215       if (fork()!=0) exit(0);
216
217       log_set_stderr(0);
218       INFO("daemonized, pid=%i", getpid());
219    }
220
221    /* write PID file of main thread */
222    if (pidfilename == NULL) pidfilename = configuration.pid_file;
223    if (pidfilename) {
224       FILE *pidfile;
225       DEBUGC(DBCLASS_CONFIG,"creating PID file [%s]", pidfilename);
226       sts=unlink(configuration.pid_file);
227       if ((sts==0) ||(errno == ENOENT)) {
228          if ((pidfile=fopen(pidfilename, "w"))) {
229             fprintf(pidfile,"%i\n",(int)getpid());
230             fclose(pidfile);
231          } else {
232             WARN("couldn't create new PID file: %s", strerror(errno));
233          }
234       } else {
235          WARN("couldn't delete old PID file: %s", strerror(errno));
236       }
237    }
238
239    /* initialize the RTP proxy */
240    sts=rtpproxy_init();
241    if (sts != STS_SUCCESS) {
242       ERROR("unable to initialize RTP proxy - aborting"); 
243       exit(1);
244    }
245
246    /* init the oSIP parser */
247    parser_init();
248
249    /* listen for incoming messages */
250    sts=sipsock_listen();
251    if (sts == STS_FAILURE) {
252       /* failure to allocate SIP socket... */
253       ERROR("unable to bind to SIP listening socket - aborting"); 
254       exit(1);
255    }
256
257    /* initialize the registration facility */
258    register_init();
259
260 /*
261  * silence the log - if so required...
262  */
263    log_set_silence(configuration.silence_log);
264
265    INFO(PACKAGE"-"VERSION"-"BUILDSTR" "UNAME" started");
266
267 /*
268  * Main loop
269  */
270    while (!exit_program) {
271
272       DEBUGC(DBCLASS_BABBLE,"going into sipsock_wait\n");
273       while (sipsock_wait()<=0) {
274          /* got no input, here by timeout. do aging */
275          register_agemap();
276
277          /* TCP log: check for a connection */
278          log_tcp_connect();
279
280          /* dump memory stats if requested to do so */
281          if (dmalloc_dump) {
282             dmalloc_dump=0;
283 #ifdef DMALLOC
284             INFO("SIGUSR2 - DMALLOC statistics is dumped");
285             dmalloc_log_stats();
286             dmalloc_log_unfreed();
287 #else
288             INFO("SIGUSR2 - DMALLOC support is not compiled in");
289 #endif
290          }
291
292          if (exit_program) goto exit_prg;
293       }
294
295       /* got input, process */
296       DEBUGC(DBCLASS_BABBLE,"back from sipsock_wait");
297
298       i=sipsock_read(&buff, sizeof(buff)-1, &ticket.from, &ticket.protocol);
299       buff[i]='\0';
300
301       /* evaluate the access lists (IP based filter)*/
302       access=accesslist_check(ticket.from);
303       if (access == 0) {
304          DEBUGC(DBCLASS_ACCESS,"access for this packet was denied");
305          continue; /* there are no resources to free */
306       }
307
308       /* integrity checks */
309       sts=security_check_raw(buff, i);
310       if (sts != STS_SUCCESS) {
311          DEBUGC(DBCLASS_SIP,"security check (raw) failed");
312          continue; /* there are no resources to free */
313       }
314
315       /* init sip_msg */
316       sts=osip_message_init(&ticket.sipmsg);
317       ticket.sipmsg->message=NULL;
318       if (sts != 0) {
319          ERROR("osip_message_init() failed... this is not good");
320          continue; /* skip, there are no resources to free */
321       }
322
323       /*
324        * RFC 3261, Section 16.3 step 1
325        * Proxy Behavior - Request Validation - Reasonable Syntax
326        * (parse the received message)
327        */
328       sts=osip_message_parse(ticket.sipmsg, buff);
329       if (sts != 0) {
330          ERROR("osip_message_parse() failed... this is not good");
331          DUMP_BUFFER(-1, buff, i);
332          goto end_loop; /* skip and free resources */
333       }
334
335       /* integrity checks - parsed buffer*/
336       sts=security_check_sip(&ticket);
337       if (sts != STS_SUCCESS) {
338          ERROR("security_check_sip() failed... this is not good");
339          DUMP_BUFFER(-1, buff, i);
340          goto end_loop; /* skip and free resources */
341       }
342
343       /*
344        * RFC 3261, Section 16.3 step 2
345        * Proxy Behavior - Request Validation - URI scheme
346        * (check request URI and refuse with 416 if not understood)
347        */
348       /* NOT IMPLEMENTED */
349
350       /*
351        * RFC 3261, Section 16.3 step 3
352        * Proxy Behavior - Request Validation - Max-Forwards check
353        * (check Max-Forwards header and refuse with 483 if too many hops)
354        */
355       {
356       osip_header_t *max_forwards;
357       int forwards_count = DEFAULT_MAXFWD;
358
359       osip_message_get_max_forwards(ticket.sipmsg, 0, &max_forwards);
360       if (max_forwards && max_forwards->hvalue) {
361          forwards_count = atoi(max_forwards->hvalue);
362       }
363
364       DEBUGC(DBCLASS_PROXY,"checking Max-Forwards (=%i)",forwards_count);
365       if (forwards_count <= 0) {
366          DEBUGC(DBCLASS_SIP, "Forward count reached 0 -> 483 response");
367          sip_gen_response(&ticket, 483 /*Too many hops*/);
368          goto end_loop; /* skip and free resources */
369       }
370
371       }
372
373       /*
374        * RFC 3261, Section 16.3 step 4
375        * Proxy Behavior - Request Validation - Loop Detection check
376        * (check for loop and return 482 if a loop is detected)
377        */
378       if (check_vialoop(&ticket) == STS_TRUE) {
379          /* make sure we don't end up in endless loop when detecting
380           * an loop in an "loop detected" message - brrr */
381          if (MSG_IS_RESPONSE(ticket.sipmsg) && 
382              MSG_TEST_CODE(ticket.sipmsg, 482)) {
383             DEBUGC(DBCLASS_SIP,"loop in loop-response detected, ignoring");
384          } else {
385             DEBUGC(DBCLASS_SIP,"via loop detected, ignoring request");
386             sip_gen_response(&ticket, 482 /*Loop detected*/);
387          }
388          goto end_loop; /* skip and free resources */
389       }
390
391       /*
392        * RFC 3261, Section 16.3 step 5
393        * Proxy Behavior - Request Validation - Proxy-Require check
394        * (check Proxy-Require header and return 420 if unsupported option)
395        */
396       /* NOT IMPLEMENTED */
397
398       /*
399        * RFC 3261, Section 16.5
400        * Proxy Behavior - Determining Request Targets
401        */
402       /* NOT IMPLEMENTED */
403
404       DEBUGC(DBCLASS_SIP,"received SIP type %s:%s",
405              (MSG_IS_REQUEST(ticket.sipmsg))? "REQ" : "RES",
406              (MSG_IS_REQUEST(ticket.sipmsg) ?
407                 ((ticket.sipmsg->sip_method)?
408                    ticket.sipmsg->sip_method : "NULL") :
409                 ((ticket.sipmsg->reason_phrase) ? 
410                    ticket.sipmsg->reason_phrase : "NULL")));
411               
412       /*
413        * if an REQ REGISTER, check if it is directed to myself,
414        * or am I just the outbound proxy but no registrar.
415        * - If I'm the registrar, register & generate answer
416        * - If I'm just the outbound proxy, register, rewrite & forward
417        */
418       if (MSG_IS_REGISTER(ticket.sipmsg) && 
419           MSG_IS_REQUEST(ticket.sipmsg)) {
420          if (access & ACCESSCTL_REG) {
421             osip_uri_t *url;
422             struct in_addr addr1, addr2, addr3;
423             int dest_port;
424
425             url = osip_message_get_uri(ticket.sipmsg);
426             dest_port= (url->port)?atoi(url->port):SIP_PORT;
427
428             if ( (get_ip_by_host(url->host, &addr1) == STS_SUCCESS) &&
429                  (get_ip_by_ifname(configuration.inbound_if,&addr2) ==
430                   STS_SUCCESS) &&
431                  (get_ip_by_ifname(configuration.outbound_if,&addr3) ==
432                   STS_SUCCESS)) {
433
434                if ((configuration.sip_listen_port == dest_port) &&
435                    ((memcmp(&addr1, &addr2, sizeof(addr1)) == 0) ||
436                     (memcmp(&addr1, &addr3, sizeof(addr1)) == 0))) {
437                   /* I'm the registrar, send response myself */
438                   sts = register_client(&ticket, 0);
439                   sts = register_response(&ticket, sts);
440                } else {
441                   /* I'm just the outbound proxy */
442                   DEBUGC(DBCLASS_SIP,"proxying REGISTER request to:%s",
443                          url->host);
444                   sts = register_client(&ticket, 1);
445                   sts = proxy_request(&ticket);
446                }
447             } else {
448                if (MSG_IS_REQUEST(ticket.sipmsg)) {
449                   sip_gen_response(&ticket, 408 /*request timeout*/);
450                }
451             }
452          } else {
453             WARN("non-authorized registration attempt from %s",
454                  utils_inet_ntoa(ticket.from.sin_addr));
455          }
456
457       /*
458        * check if outbound interface is UP.
459        * If not, send back error to UA and
460        * skip any proxying attempt
461        */
462       } else if (get_ip_by_ifname(configuration.outbound_if,NULL) !=
463                  STS_SUCCESS) {
464          DEBUGC(DBCLASS_SIP, "got a %s to proxy, but outbound interface "
465                 "is down", (MSG_IS_REQUEST(ticket.sipmsg))? "REQ" : "RES");
466
467          if (MSG_IS_REQUEST(ticket.sipmsg))
468             sip_gen_response(&ticket, 408 /*request timeout*/);
469       
470       /*
471        * MSG is a request, add current via entry,
472        * do a lookup in the URLMAP table and
473        * send to the final destination
474        */
475       } else if (MSG_IS_REQUEST(ticket.sipmsg)) {
476          if (access & ACCESSCTL_SIP) {
477             sts = proxy_request(&ticket);
478          } else {
479             INFO("non-authorized request received from %s",
480                  utils_inet_ntoa(ticket.from.sin_addr));
481          }
482
483       /*
484        * MSG is a response, remove current via and
485        * send to the next VIA in chain
486        */
487       } else if (MSG_IS_RESPONSE(ticket.sipmsg)) {
488          if (access & ACCESSCTL_SIP) {
489             sts = proxy_response(&ticket);
490          } else {
491             INFO("non-authorized response received from %s",
492                  utils_inet_ntoa(ticket.from.sin_addr));
493          }
494          
495       /*
496        * unsupported message
497        */
498       } else {
499          ERROR("received unsupported SIP type %s %s",
500                (MSG_IS_REQUEST(ticket.sipmsg))? "REQ" : "RES",
501                ticket.sipmsg->sip_method);
502       }
503
504
505 /*
506  * free the SIP message buffers
507  */
508       end_loop:
509       osip_message_free(ticket.sipmsg);
510
511    } /* while TRUE */
512    exit_prg:
513
514    /* dump current known SIP registrations */
515    register_shut();
516    INFO("properly terminating siproxd");
517
518    /* remove PID file */
519    if (pidfilename) {
520       DEBUGC(DBCLASS_CONFIG,"deleting PID file [%s]", pidfilename);
521       sts=unlink(pidfilename);
522       if (sts != 0) {
523          WARN("couldn't delete old PID file: %s", strerror(errno));
524       }
525    }
526
527    /* END */
528    return 0;
529 } /* main */
530
531 /*
532  * Signal handler
533  *
534  * this one is called asynchronously whevener a registered
535  * signal is applied. Just set a flag and don't do any funny
536  * things here.
537  */
538 static void sighandler(int sig) {
539    if (sig==SIGTERM) exit_program=1;
540    if (sig==SIGINT)  exit_program=1;
541    if (sig==SIGUSR2) dmalloc_dump=1;
542    return;
543 }