siproxd/src/siproxd.c
Thomas Ries acbdb707ec - some changes & enhancements inspired by Cris Ross:
* 183 Trying *may* contain SDP data
  * compare_url: now does compare the scheme,
    if a host is not resolveable hostnames will be
    compares as strings
2003-12-05 18:16:28 +00:00

407 lines
11 KiB
C

/* -*- Mode: C; c-basic-offset: 3 -*-
Copyright (C) 2002 Thomas Ries <tries@gmx.net>
This file is part of Siproxd.
Siproxd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Siproxd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Siproxd; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#include <osipparser2/osip_parser.h>
#include "siproxd.h"
#include "log.h"
static char const ident[]="$Id: " __FILE__ ": " PACKAGE "-" VERSION "-"\
BUILDSTR " $";
/* configuration storage */
struct siproxd_config configuration;
/* socket used for sending SIP datagrams */
int sip_socket=0;
/* -h help option text */
static const char str_helpmsg[] =
PACKAGE"-"VERSION"-"BUILDSTR" (c) 2002-2003 Thomas Ries\n" \
"\nUsage: siproxd [options]\n\n" \
"options:\n" \
" --help (-h) help\n" \
" --debug <pattern> (-d) set debug-pattern\n" \
" --config <cfgfile> (-c) use the specified config file\n"\
"";
/*
* module local data
*/
static int dmalloc_dump=0;
static int exit_program=0;
/*
* local prototypes
*/
static void sighandler(int sig);
int main (int argc, char *argv[])
{
int sts;
int i;
int access;
struct sockaddr_in from;
char buff [BUFFER_SIZE];
osip_message_t *my_msg=NULL;
extern char *optarg;
int ch1;
char configfile[64]="siproxd"; /* basename of configfile */
int config_search=1; /* search the config file */
int cmdline_debuglevel=0;
struct sigaction act;
/*
* setup signal handlers
*/
act.sa_handler=sighandler;
sigemptyset(&act.sa_mask);
act.sa_flags=SA_RESTART;
if (sigaction(SIGTERM, &act, NULL)) {
ERROR("Failed to install SIGTERM handler");
}
if (sigaction(SIGINT, &act, NULL)) {
ERROR("Failed to install SIGINT handler");
}
if (sigaction(SIGUSR1, &act, NULL)) {
ERROR("Failed to install SIGUSR1 handler");
}
/*
* prepare default configuration
*/
memset (&configuration, 0, sizeof(configuration));
configuration.sip_listen_port=SIP_PORT;
log_set_pattern(configuration.debuglevel);
/*
* parse command line
*/
{
#ifdef HAVE_GETOPT_LONG
int option_index = 0;
static struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
{"config", required_argument, NULL, 'c'},
{"debug", required_argument, NULL, 'd'},
{0,0,0,0}
};
while ((ch1 = getopt_long(argc, argv, "hc:d:n",
long_options, &option_index)) != -1) {
#else /* ! HAVE_GETOPT_LONG */
while ((ch1 = getopt(argc, argv, "hc:d:n:")) != -1) {
#endif
switch (ch1) {
case 'h': /* help */
DEBUGC(DBCLASS_CONFIG,"option: help");
fprintf(stderr,str_helpmsg);
exit(0);
break;
case 'c': /* load config file */
DEBUGC(DBCLASS_CONFIG,"option: config file=%s",optarg);
strncpy(configfile,optarg,sizeof(configfile)-1);
configfile[sizeof(configfile)]='\0';
config_search=0;
break;
case 'd': /* set debug level */
DEBUGC(DBCLASS_CONFIG,"option: set debug level: %s",optarg);
cmdline_debuglevel=atoi(optarg);
log_set_pattern(cmdline_debuglevel);
break;
default:
DEBUGC(DBCLASS_CONFIG,"no command line options");
break;
}
}
}
/*
* Init stuff
*/
/* read the config file */
if (read_config(configfile, config_search) == STS_FAILURE) exit(1);
/* if a debug level > 0 has been given on the commandline use its
value and not what is in the config file */
if (cmdline_debuglevel != 0) {
configuration.debuglevel=cmdline_debuglevel;
}
/* set debug level as desired */
log_set_pattern(configuration.debuglevel);
/* change user and group IDs */
secure_enviroment();
/* daemonize if requested to */
if (configuration.daemonize) {
DEBUGC(DBCLASS_CONFIG,"daemonizing");
if (fork()!=0) exit(0);
setsid();
if (fork()!=0) exit(0);
log_set_tosyslog(1);
}
#ifdef MOREDEBUG /*&&&&*/
INFO("daemonizing done (pid=%i)", getpid());
#endif
/* initialize the RTP proxy */
sts=rtpproxy_init();
if (sts != STS_SUCCESS) {
ERROR("unable to initialize RTP proxy - aborting");
exit(1);
}
/* init the oSIP parser */
parser_init();
/* listen for incoming messages */
sts=sipsock_listen();
if (sts == STS_FAILURE) {
/* failure to allocate SIP socket... */
ERROR("unable to bind to SIP listening socket - aborting");
exit(1);
}
/* initialize the registration facility */
register_init();
INFO(PACKAGE"-"VERSION"-"BUILDSTR" started");
/*
* silence the log - if so required...
*/
log_set_silence(configuration.silence_log);
/*
* Main loop
*/
while (!exit_program) {
DEBUGC(DBCLASS_BABBLE,"going into sip_wait\n");
while (sipsock_wait()<=0) {
/* got no input, here by timeout. do aging */
register_agemap();
/* dump memory stats if requested to do so */
if (dmalloc_dump) {
dmalloc_dump=0;
#ifdef DMALLOC
INFO("SIGUSR1 - DMALLOC statistics is dumped");
dmalloc_log_stats();
dmalloc_log_unfreed();
#else
INFO("SIGUSR1 - DMALLOC support is not compiled in");
#endif
}
if (exit_program) goto exit_prg;
}
/* got input, process */
DEBUGC(DBCLASS_BABBLE,"back from sip_wait");
i=sipsock_read(&buff, sizeof(buff)-1, &from);
buff[i]='\0';
#ifdef MOREDEBUG /*&&&&*/
{char tmp[32];
strncpy(tmp, buff, 30);
tmp[30]='\0';
INFO("got packet [%i bytes] from %s [%s]", i,
utils_inet_ntoa(from.sin_addr), tmp);}
#endif
/* evaluate the access lists (IP based filter)*/
access=accesslist_check(from);
if (access == 0) continue; /* there are no resources to free */
/* integrity checks */
sts=security_check_raw(buff, i);
if (sts != STS_SUCCESS) continue; /* there are no resources to free */
/* parse the received message */
sts=osip_message_init(&my_msg);
my_msg->message=NULL;
if (sts != 0) {
ERROR("osip_message_init() failed... this is not good");
continue; /* skip, there are no resources to free */
}
sts=osip_message_parse(my_msg, buff);
if (sts != 0) {
ERROR("osip_message_parse() failed... this is not good");
DUMP_BUFFER(-1, buff, i);
goto end_loop; /* skip and free resources */
}
/* integrity checks - parsed buffer*/
sts=security_check_sip(my_msg);
if (sts != STS_SUCCESS) {
ERROR("security_check_sip() failed... this is not good");
DUMP_BUFFER(-1, buff, i);
goto end_loop; /* skip and free resources */
}
DEBUGC(DBCLASS_SIP,"received SIP type %s:%s",
(MSG_IS_REQUEST(my_msg))? "REQ" : "RES",
(my_msg->sip_method)?
my_msg->sip_method : "NULL") ;
/*
* if an RQ REGISTER, check if it is directed to myself,
* or am I just the outbound proxy but no registrar.
* - If I'm the registrar, register & generate answer
* - If I'm just the outbound proxy, register, rewrite & forward
*/
if (MSG_IS_REGISTER(my_msg) && MSG_IS_REQUEST(my_msg)) {
if (access & ACCESSCTL_REG) {
osip_uri_t *url;
struct in_addr addr1, addr2, addr3;
url = osip_message_get_uri(my_msg);
sts = get_ip_by_host(url->host, &addr1);
get_ip_by_ifname(configuration.inbound_if,&addr2);
get_ip_by_ifname(configuration.outbound_if,&addr3);
if ((sts == STS_SUCCESS) &&
((memcmp(&addr1, &addr2, sizeof(addr1)) == 0) ||
(memcmp(&addr1, &addr3, sizeof(addr1)) == 0))) {
/* I'm the registrar, send response myself */
sts = register_client(my_msg, 0);
sts = register_response(my_msg, sts);
} else {
/* I'm just the outbound proxy */
DEBUGC(DBCLASS_SIP,"proxying REGISTER request to:%s",url->host);
sts = register_client(my_msg, 1);
sts = proxy_request(my_msg);
}
} else {
WARN("non-authorized registration attempt from %s",
utils_inet_ntoa(from.sin_addr));
}
/*
* check if outbound interface is UP.
* If not, send back error to UA and
* skip any proxying attempt
*/
} else if (get_ip_by_ifname(configuration.outbound_if,NULL) !=
STS_SUCCESS) {
DEBUGC(DBCLASS_SIP, "got a %s to proxy, but outbound interface "
"is down", (MSG_IS_REQUEST(my_msg))? "REQ" : "RES");
if (MSG_IS_REQUEST(my_msg))
sip_gen_response(my_msg, 408 /*request timeout*/);
/*
* MSG is a request, add current via entry,
* do a lookup in the URLMAP table and
* send to the final destination
*/
} else if (MSG_IS_REQUEST(my_msg)) {
if (access & ACCESSCTL_SIP) {
sts = proxy_request(my_msg);
} else {
INFO("non-authorized request received from %s",
utils_inet_ntoa(from.sin_addr));
}
/*
* MSG is a response, remove current via and
* send to the next VIA in chain
*/
} else if (MSG_IS_RESPONSE(my_msg)) {
if (access & ACCESSCTL_SIP) {
sts = proxy_response(my_msg);
} else {
INFO("non-authorized response received from %s",
utils_inet_ntoa(from.sin_addr));
}
/*
* unsupported message
*/
} else {
ERROR("received unsupported SIP type %s %s",
(MSG_IS_REQUEST(my_msg))? "REQ" : "RES",
my_msg->sip_method);
}
/*
* free the SIP message buffers
*/
end_loop:
osip_message_free(my_msg);
} /* while TRUE */
exit_prg:
register_shut();
INFO("properly terminating siproxd");
return 0;
} /* main */
/*
* Signal handler
*
* this one is called asynchronously whevener a registered
* signal is applied. Just set a flag and don't do any funny
* things here.
*/
static void sighandler(int sig) {
if (sig==SIGTERM) exit_program=1;
if (sig==SIGINT) exit_program=1;
if (sig==SIGUSR1) dmalloc_dump=1;
return;
}