1 /* -*- Mode: C; c-basic-offset: 3 -*-
2 Copyright (C) 2002-2005 Thomas Ries <tries@gmx.net>
4 This file is part of Siproxd.
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.
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.
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
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
36 #include <osipparser2/osip_parser.h>
41 static char const ident[]="$Id: siproxd.c,v 1.1.1.1 2006/05/18 10:47:25 michaelc Exp $";
43 /* configuration storage */
44 struct siproxd_config configuration;
46 /* Global File instance on pw file */
47 FILE *siproxd_passwordfile;
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"
54 #ifdef HAVE_GETOPT_LONG
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"
61 " -d <pattern> set debug-pattern\n"
62 " -c <cfgfile> use the specified config file\n"
63 " -p <pidfile> create pid file <pidfile>\n"
72 static int dmalloc_dump=0;
73 static int exit_program=0;
78 static void sighandler(int sig);
82 int siproxd_main(int argc, char *argv[])
84 int main (int argc, char *argv[])
90 char buff [BUFFER_SIZE];
93 extern char *optarg; /* Defined in libc getopt and unistd.h */
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;
105 * setup signal handlers
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");
113 if (sigaction(SIGINT, &act, NULL)) {
114 ERROR("Failed to install SIGINT handler");
116 if (sigaction(SIGUSR2, &act, NULL)) {
117 ERROR("Failed to install SIGUSR2 handler");
122 * prepare default configuration
124 make_default_config();
126 log_set_pattern(configuration.debuglevel);
129 * open a the pwfile instance, so we still have access after
130 * we possibly have chroot()ed to somewhere.
132 if (configuration.proxy_auth_pwfile) {
133 siproxd_passwordfile = fopen(configuration.proxy_auth_pwfile, "r");
135 siproxd_passwordfile = NULL;
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'},
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) {
159 DEBUGC(DBCLASS_CONFIG,"option: help");
160 fprintf(stderr,str_helpmsg);
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);
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);
179 pidfilename = optarg;
183 DEBUGC(DBCLASS_CONFIG,"no command line options");
192 INFO(PACKAGE"-"VERSION"-"BUILDSTR" "UNAME" starting up");
194 /* read the config file */
195 if (read_config(configfile, config_search) == STS_FAILURE) exit(1);
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;
203 /* set debug level as desired */
204 log_set_pattern(configuration.debuglevel);
205 log_set_listen_port(configuration.debugport);
207 /* change user and group IDs */
210 /* daemonize if requested to */
211 if (configuration.daemonize) {
212 DEBUGC(DBCLASS_CONFIG,"daemonizing");
213 if (fork()!=0) exit(0);
215 if (fork()!=0) exit(0);
218 INFO("daemonized, pid=%i", getpid());
221 /* write PID file of main thread */
222 if (pidfilename == NULL) pidfilename = configuration.pid_file;
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());
232 WARN("couldn't create new PID file: %s", strerror(errno));
235 WARN("couldn't delete old PID file: %s", strerror(errno));
239 /* initialize the RTP proxy */
241 if (sts != STS_SUCCESS) {
242 ERROR("unable to initialize RTP proxy - aborting");
246 /* init the oSIP parser */
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");
257 /* initialize the registration facility */
261 * silence the log - if so required...
263 log_set_silence(configuration.silence_log);
265 INFO(PACKAGE"-"VERSION"-"BUILDSTR" "UNAME" started");
270 while (!exit_program) {
272 DEBUGC(DBCLASS_BABBLE,"going into sipsock_wait\n");
273 while (sipsock_wait()<=0) {
274 /* got no input, here by timeout. do aging */
277 /* TCP log: check for a connection */
280 /* dump memory stats if requested to do so */
284 INFO("SIGUSR2 - DMALLOC statistics is dumped");
286 dmalloc_log_unfreed();
288 INFO("SIGUSR2 - DMALLOC support is not compiled in");
292 if (exit_program) goto exit_prg;
295 /* got input, process */
296 DEBUGC(DBCLASS_BABBLE,"back from sipsock_wait");
298 i=sipsock_read(&buff, sizeof(buff)-1, &ticket.from, &ticket.protocol);
301 /* evaluate the access lists (IP based filter)*/
302 access=accesslist_check(ticket.from);
304 DEBUGC(DBCLASS_ACCESS,"access for this packet was denied");
305 continue; /* there are no resources to free */
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 */
316 sts=osip_message_init(&ticket.sipmsg);
317 ticket.sipmsg->message=NULL;
319 ERROR("osip_message_init() failed... this is not good");
320 continue; /* skip, there are no resources to free */
324 * RFC 3261, Section 16.3 step 1
325 * Proxy Behavior - Request Validation - Reasonable Syntax
326 * (parse the received message)
328 sts=osip_message_parse(ticket.sipmsg, buff);
330 ERROR("osip_message_parse() failed... this is not good");
331 DUMP_BUFFER(-1, buff, i);
332 goto end_loop; /* skip and free resources */
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 */
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)
348 /* NOT IMPLEMENTED */
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)
356 osip_header_t *max_forwards;
357 int forwards_count = DEFAULT_MAXFWD;
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);
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 */
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)
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");
385 DEBUGC(DBCLASS_SIP,"via loop detected, ignoring request");
386 sip_gen_response(&ticket, 482 /*Loop detected*/);
388 goto end_loop; /* skip and free resources */
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)
396 /* NOT IMPLEMENTED */
399 * RFC 3261, Section 16.5
400 * Proxy Behavior - Determining Request Targets
402 /* NOT IMPLEMENTED */
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")));
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
418 if (MSG_IS_REGISTER(ticket.sipmsg) &&
419 MSG_IS_REQUEST(ticket.sipmsg)) {
420 if (access & ACCESSCTL_REG) {
422 struct in_addr addr1, addr2, addr3;
425 url = osip_message_get_uri(ticket.sipmsg);
426 dest_port= (url->port)?atoi(url->port):SIP_PORT;
428 if ( (get_ip_by_host(url->host, &addr1) == STS_SUCCESS) &&
429 (get_ip_by_ifname(configuration.inbound_if,&addr2) ==
431 (get_ip_by_ifname(configuration.outbound_if,&addr3) ==
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);
441 /* I'm just the outbound proxy */
442 DEBUGC(DBCLASS_SIP,"proxying REGISTER request to:%s",
444 sts = register_client(&ticket, 1);
445 sts = proxy_request(&ticket);
448 if (MSG_IS_REQUEST(ticket.sipmsg)) {
449 sip_gen_response(&ticket, 408 /*request timeout*/);
453 WARN("non-authorized registration attempt from %s",
454 utils_inet_ntoa(ticket.from.sin_addr));
458 * check if outbound interface is UP.
459 * If not, send back error to UA and
460 * skip any proxying attempt
462 } else if (get_ip_by_ifname(configuration.outbound_if,NULL) !=
464 DEBUGC(DBCLASS_SIP, "got a %s to proxy, but outbound interface "
465 "is down", (MSG_IS_REQUEST(ticket.sipmsg))? "REQ" : "RES");
467 if (MSG_IS_REQUEST(ticket.sipmsg))
468 sip_gen_response(&ticket, 408 /*request timeout*/);
471 * MSG is a request, add current via entry,
472 * do a lookup in the URLMAP table and
473 * send to the final destination
475 } else if (MSG_IS_REQUEST(ticket.sipmsg)) {
476 if (access & ACCESSCTL_SIP) {
477 sts = proxy_request(&ticket);
479 INFO("non-authorized request received from %s",
480 utils_inet_ntoa(ticket.from.sin_addr));
484 * MSG is a response, remove current via and
485 * send to the next VIA in chain
487 } else if (MSG_IS_RESPONSE(ticket.sipmsg)) {
488 if (access & ACCESSCTL_SIP) {
489 sts = proxy_response(&ticket);
491 INFO("non-authorized response received from %s",
492 utils_inet_ntoa(ticket.from.sin_addr));
496 * unsupported message
499 ERROR("received unsupported SIP type %s %s",
500 (MSG_IS_REQUEST(ticket.sipmsg))? "REQ" : "RES",
501 ticket.sipmsg->sip_method);
506 * free the SIP message buffers
509 osip_message_free(ticket.sipmsg);
514 /* dump current known SIP registrations */
516 INFO("properly terminating siproxd");
518 /* remove PID file */
520 DEBUGC(DBCLASS_CONFIG,"deleting PID file [%s]", pidfilename);
521 sts=unlink(pidfilename);
523 WARN("couldn't delete old PID file: %s", strerror(errno));
534 * this one is called asynchronously whevener a registered
535 * signal is applied. Just set a flag and don't do any funny
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;