scripts/serconf.sh
6aeac0d8
 #!/bin/sh
 #
 # $Id$
 #
 # SER configuration script
 #
 # disclaimer: extremely simplistic and experimental
 # useful only for people who know what they are doing
 # and want to save some typing
 #
 # call it to generate a basic script -- you have to
 # carry out any subsequent changes manually
 #
 
 # ------------------- Variables ------------------------
 
 # prompted variables
 # SER_DOMAIN -- name of served domain, e.g., foo.bar.com
 # SER_GWIP -- IP address of PSTN gateway, e.g. 10.0.0.1
 
 # parameters that are typically not changed
3ee34899
 SER_SQL_URI="mysql://ser:heslo@localhost/ser"
6aeac0d8
 # set LIB_PATH if all modules are installed in a single
 # directory; otherwise, modules are sought in 'modules'
 # subdirectories
 #SER_LIB_PATH="/usr/local/lib/ser/modules"
 
 
 # --------------------- functions ---------------------------
 function go_to_pstn()
 {
 	if [ -n "$SER_GWIP" ] ; then 
 		cat << EOGOTOPSTN
 	# now check if it's about PSTN destinations through our gateway;
 	# note that 8.... is exempted for numerical non-gw destinations
 	if (uri=~"sip:\+?[0-79][0-9]*@.*") {
 		route(3);
 		break;
 	}; 
 EOGOTOPSTN
 	fi
 }
 
 
 function addr2re()
 {
 	echo $1 |  sed -ne "s/\./\\\./gp"
 }
 
 function gw_check()
 {
 	if [ -n "$SER_GWIP" ] ; then 
 		cat << EOGWTEST
 		if (uri=~"sip:[+0-9]+@$SER_GWIP_RE") {
 			# it is gateway -- proceed to ACLs
 			route(3);
 			break;
 		};
 EOGWTEST
 	fi
 }
 
 function mine_check()
 {
 	printf "uri=~\"[@:](sip[\.)?$SER_DOMAIN_TEST_RE([;:].*)*\" $SER_GW_TEST_RE"
 }
 
 function gw_m_check()
 {
 	if [ -n "$SER_GWIP" ] ; then 
 		cat << EOMCHECK
 		if (search("^(Contact|m): .*$SER_GWIP_RE")) {
 			log(1, "LOG: alert: protected contacts\n");
 			sl_send_reply("476", "No Server Address in Contacts Allowed" );
 			break;
 		};
 EOMCHECK
 	fi
 }
 
 function help()
 {
 	cat << EOHELP
 Numbering plan is as follows:
 - numbers beginning with 8 are considered aliases
 - numbers beginning with + are considered ENUM destinations
 EOHELP
 	if [ -n "$SER_GWIP" ] ; then
 		cat << EOHELP2
 - all other numbers are considered PSTN destinations
   ... to dial PSTN, a user must have 'int' privilege
 EOHELP2
 	else
 		echo "- all other numbers are considered usernames"
 	fi
 }
 
 function usage()
 {
 	echo "Usage: $0 <domain_name> [<ip_address_of_gateway>]" \
 		'> <config_file>' > /dev/stderr
 	exit 1
 }
 
 function load_mod()
 {
 	if [ -n "$SER_LIB_PATH" ] ; then
 		echo "loadmodule \"$SER_LIB_PATH/$1.so\""
 	else
 		echo "loadmodule \"modules/$1/$1.so\""
 	fi
 }
 
 # ----------------------- user-parameter check ---------------
 # SER_DOMAIN -- name of served domain, e.g., foo.bar.com
 # SER_GWIP -- IP address of PSTN gateway, e.g. 10.0.0.1
 
 if [ $# -gt 0 ] ; then
 	SER_DOMAIN="$1"
 	shift
 	if [ $# -gt 0 ] ; then
 		SER_GWIP="$1"
 		shift
 	fi
 	if [ $# -gt 0 ] ; then
 		usage
 	fi
 else
 	usage
 fi
 
 # ---------------------- initialization -------------------------
 
 # autodetection parameters
 SER_IP=`/sbin/ifconfig eth0 | 
 	sed -ne 's/\( \)*\(inet addr:\)\([0-9\.]*\).*/\3/gp'`
 
 # construction of regular expressions
 SER_IP_RE=`addr2re $SER_IP`
 SER_DOMAIN_RE=`addr2re $SER_DOMAIN`
 
 # tests
 # - is this for my domain
 SER_DOMAIN_TEST_RE=`printf "($SER_DOMAIN_RE|$SER_IP_RE)"`
 # - is this for my gateway ?
 if [ -n "$SER_GWIP" ] ; then
 	SER_GWIP_RE=`addr2re $SER_GWIP`
 	SER_GW_TEST_RE=`printf "| uri=~\"@$SER_GWIP_RE([;:].*)*\""`
 fi
 
 SER_REGISTRAR="registrar@$SER_DOMAIN"
 
 # ---------------------- verficiation --------------------------
 set | grep ^SER_ > /dev/stderr
 echo > /dev/stderr
 echo "IS EVERYTHING OK ???? (press ^C to interrupt)" > /dev/stderr
 read
 
 
 # --------------------- dump it here -------------------------
 
 cat << EOF
 
 #
 # \$Id$
 #
 # autogenerated SER configuration 
 #
 # user: `id`
 # system: `uname -a`
 # date: `date`
 #
 
 # ----------- global configuration parameters ------------------------
 
 debug=3
 fork=yes
 port=5060
 log_stderror=no
 memlog=5
 
 mhomed=yes
 
 fifo="/tmp/ser_fifo"
 
 alias=$SER_DOMAIN
 
 # uncomment to override config values for test 
 /* 
 debug=3             # debug level (cmd line: -ddd)
 fork=no
 port=5068
 log_stderror=yes	# (cmd line: -E)
 fifo="/tmp/ser_fifox"
  */
 
 
 check_via=no		# (cmd. line: -v)
 dns=no              # (cmd. line: -r)
 rev_dns=no          # (cmd. line: -R)
 children=16
 # if changing fifo mode to a more restrictive value, put
 # decimal value in there, e.g. dec(rw|rw|rw)=dec(666)=438
 #fifo_mode=438
 
 # ------------------ module loading ----------------------------------
 
 `load_mod tm`
 `load_mod sl`
 `load_mod acc`
 `load_mod rr`
 `load_mod maxfwd`
 `load_mod mysql`
 `load_mod usrloc`
 `load_mod registrar`
 `load_mod auth`
 `load_mod auth_db`
 `load_mod textops`
 `load_mod uri`
 `load_mod group`
 `load_mod msilo`
 `load_mod enum`
 
 
 
 # ----------------- setting module-specific parameters ---------------
 
 # all DB urls here
 modparam("usrloc|acc|auth_db|group|msilo|uri", "db_url",
 	"$SER_SQL_URI")
 
 # -- usrloc params --
 /* 0 -- dont use mysql, 1 -- write_through, 2--write_back */
 modparam("usrloc", "db_mode",   2)
 modparam("usrloc", "timer_interval", 10)
 
 # -- auth params --
 
 modparam("auth_db", "calculate_ha1", yes)
 #modparam("auth_db", "user_column",   "user_id")
 modparam("auth_db", "password_column",   "password")
 modparam("auth", "nonce_expire",  300)
 
 # -- rr params --
 # add value to ;lr param to make some broken UAs happy
 modparam("rr", "enable_full_lr", 1)
 
 # -- acc params --
 # that is the flag for which we will account -- don't forget to
 modparam("acc", "db_flag", 1 )
 modparam("acc", "db_missed_flag", 3 )
 
 # -- tm params --
 modparam("tm", "fr_timer", 20 )
 modparam("tm", "fr_inv_timer", 90 )
 modparam("tm", "wt_timer", 20 )
 
 # -- msilo params
 modparam("msilo", "registrar", "sip:$SER_REGISTRAR")
 
 # -- enum params --
 #
 modparam("enum", "domain_suffix", "e164.arpa.")
 
 
 # -------------------------  request routing logic -------------------
 
 # main routing logic
 
 route{
 
 	/* ********* ROUTINE CHECKS  ********************************** */
 
 	# filter too old messages
 	if (!mf_process_maxfwd_header("10")) {
 		log("LOG: Too many hops\n");
 		sl_send_reply("483","Alas Too Many Hops");
 		break;
 	};
 	if (len_gt( max_len )) {
 		sl_send_reply("513", "Message too large sorry");
 		break;
 	};
 
 
 	# Make sure that requests dont advertise addresses 
 	# from private IP space (RFC1918) in Contact HF
 	# (note: does not match with folded lines)
 	if (search("^(Contact|m): .*@(192\.168\.|10\.|172\.16)")) {
 		# allow RR-ed requests, as these may indicate that
 		# a NAT-enabled proxy takes care of it; unless it is
 		# a REGISTER
 		if ((method=="REGISTER" || ! search("^Record-Route:")) 
 					&& !( src_ip==192.168.0.0/16 ||
 						src_ip==10.0.0.0/8 || src_ip==172.16.0.0/12 )) {
 			log("LOG: Someone trying to register from private IP again\n");
 			sl_send_reply("479", "We dont accept private IP contacts" );
 			break;
 		};
 	};
 
 	# anti-spam -- if somene claims to belong to our domain in From,
 	# challenge him (skip REGISTERs -- we will chalenge them later)
 	if (search("(From|F):.*$SER_DOMAIN_TEST_RE")) {
 		# invites forwarded to other domains, like FWD may cause subsequent 
 		# request to come from there but have iptel in From -> verify
 		# only INVITEs (ignore FIFO/UAC's requests, i.e. src_ip==myself)
 		if (method=="INVITE" &  !(src_ip==$SER_IP)) {
 			if  (!(proxy_authorize(	"$SER_DOMAIN" /* realm */,
 					"subscriber" /* table name */ ))) {
 				proxy_challenge("$SER_DOMAIN" /* realm */, "0" /* no-qop */);
 				break;
 			};
 			# to maintain outside credibility of our proxy, we enforce
 			# username in From to equal digest username; user with
 			# "john.doe" id could advertise "bill.gates" in From otherwise;
 			if (!check_from()) {
 				log("LOG: From Cheating attempt in INVITE\n");
 				sl_send_reply("403", "That is ugly -- use From=id next time (OB)");
 				break;
 			};
             		# we better don't consume credentials -- some requests may be
             		# spiraled through our server (sfo@iptel->7141@iptel) and the
             		# subsequent iteration may challenge too, for example because of
             		# iptel claim in From; UACs then give up because they
         		# already submitted credentials for the given realm
 			#consume_credentials();
 		}; # INVITEs claiming to come from our domain
 	} else if (method=="INVITE" && !(uri=~"[@:\.]$SER_DOMAIN_TEST_RE([;:].*)*" 
 			# ... and we serve our gateway too if present
 			$SER_GW_TEST_RE )) {
 		#the INVITE neither claims to come from our domain nor is it targeted to it
 		# -> junk it
 		sl_send_reply("403", "No relaying");
 		break;
 	};
 
 
 	/* ********* RR ********************************** */
 	# to be safe, record route everything; UAs may use different
 	# transport protocols and need to have SER in path
 	record_route();
 	# if route forces us to forward to some explicit destination,
 	# do so; check however first that a cheater didn't preload 
 	# a gateway destination to bypass PSTN ACLs
 
 	if (loose_route()) {
 		`gw_check`
 		# route HF determined next hop; forward there
 		append_hf("P-hint: rr-enforced\r\n");
 		t_relay();
 		break;
 	};
 
 
 	/*  *********  check for requests targeted out of our domain... ******* */
 	# sign of our domain: there is '@' (username) or  : (nothing) in 
 	# front of our domain name	; ('.' is not there -- we handle all
 	# xxx.iptel.org as outbound hosts);if none of these cases matches, 
 	# proceed with processing of outbound requests in route[2]
 	if (!(`mine_check`)) {
 		route(2);
 		break;
 	};
 
 
 	/* ************ requests for our domain ********** */
 
 
 	/* now, the request is for sure for our domain */
 
 
 	# registers always MUST be authenticated to
 	# avoid stealing incoming calls	
 	if (method=="REGISTER") {
 
 		# Make sure that user's dont register infinite loops
 		# (note: does not match with folded lines)
 		if (search("^(Contact|m): .*@$SER_DOMAIN_TEST_RE")) {
 			log(1, "LOG: alert: someone trying to set aor==contact\n");
 			sl_send_reply("476", "No Server Address in Contacts Allowed" );
 			break;
 		};
 		`gw_m_check`
 
 		if (!www_authorize(	"$SER_DOMAIN" /* realm */, 
 			 				"subscriber" /* table name */ )) {
 			# challenge if none or invalid credentials
  			www_challenge(	"$SER_DOMAIN" /* realm */, 
 							"0" /* no qop -- some phones can't deal with it */);
 			break;
 		};
 
 		# prohibit attempts to grab someone else's To address 
 		# using  valid credentials; 
 
 		if (!check_to()) {
 			log("LOG: To Cheating attempt\n");
 			sl_send_reply("403", "That is ugly -- use To=id in REGISTERs");
 			break;
 		};
 		# it is an authenticated request, update Contact database now
 		if (!save("location")) {
 			sl_reply_error();
 		};
 		m_dump();
 		break;
 	};
 
 	# some UACs might be fooled by Contacts our UACs generate to make MSN 
 	# happy (web-im, e.g.) -- tell its urneachable
 	if (uri=~"sip:daemon@" ) {
 		sl_send_reply("410", "daemon is gone");
 		break;
 	};
 
 	# is this an ENUM destination (leading +?)? give it a try, if the lookup
 	# doesn't change URI, just continue
 	if (uri=~"sip:\+[0-9]+@") {
 		if (!enum_query("voice")) { # if parameter empty, it defaults to "e2u+sip"
 			enum_query(""); # E2U+sip
 		};
 	} else {
 		# aliases  (take precedences over PSTN number; provisioning interface
 		# is set up to assinge aliases beginning with 8)
 		lookup("aliases");
 	};
 
 
 	# check again, if it is still for our domain after aliases are resolved
 	if (!(`mine_check`)) {
 		route(5);
 		break;
 	};
 
 	`go_to_pstn`
 
 	# native SIP destinations are handled using our USRLOC DB
 	if (!lookup("location")) {
 		# handle user which was not found ...
 		route(4);
 		break;
 	};
 	# check whether some inventive user has uploaded  gateway 
 	# contacts to UsrLoc to bypass our authorization logic
 	`gw_check`
 
 	/* ... and also report on missed calls ... */
 	setflag(3);
 
 	# we now know we may, we know where, let it go out now!
 	append_hf("P-hint: USRLOC\r\n");
 	if (!t_relay()) {
 		sl_reply_error();
 		break;
 	};
 }
 #------------------- OUTBOUND ----------------------------------------
 
 # routing logic for outbound requests targeted out of our domain
 # (keep in mind messages to our users can end up here too: for example,
 #  an INVITE may be UsrLoc-ed, then the other party uses outbound
 #  proxy with r-uri=the usr_loced addredd (typically IP))
 route[2] {
 	append_hf("P-hint: OUTBOUND\r\n");
 	t_relay();
 }
 
 #------- ALIASED OUTBOUND --------------------------------------------
 
 # routing logic for inbound requests aliased outbound; unlike
 # with real outbound requests we do not force authentication
 # as these calls are server by our server and we do not want
 # to disqualify unathenticated request originatiors from other
 # domains
 route[5] {
 	append_hf("P-hint: ALIASED-OUTBOUND\r\n");
 	t_relay();
 }
 
 #----------------- PSTN ----------------------------------------------
 
 # logic for calls to the PSTN
 route[3] {
 	# turn accounting on
 	setflag(1);
 
 	/* require all who call PSTN to be members of the "int" group;
 	   apply ACLs only to INVITEs -- we don't need to protect other requests, as they
 	   don't imply charges; also it could cause troubles when a call comes in via PSTN
 	   and goes to a party that can't authenticate (voicemail, other domain) -- BYEs would
 	   fail then; exempt Cisco gateway from authentication by IP address -- it does not
 	   support digest
 	*/
 	if (method=="INVITE" && (!src_ip==$SER_GWIP)) {
 		if (!proxy_authorize(	"$SER_DOMAIN" /* realm */,
 						"subscriber" /* table name */))  {
 			proxy_challenge( "$SER_DOMAIN" /* realm */, "0" /* no qop */ );
 			break;
 		};
 		# let's check from=id ... avoids accounting confusion
 		if (method=="INVITE" & !check_from()) {
 			log("LOG: From Cheating attempt\n");
 			sl_send_reply("403", "That is ugly -- use From=id next time (gw)");
 			break;
 		};
 
 		if(!is_user_in("credentials", "int")) {
 			sl_send_reply("403", "NO PSTN Privileges...");
 			break;
 		};
 		consume_credentials();
 
 	}; # INVITE to authorized PSTN
 
 	# if you have passed through all the checks, let your call go to GW!
 	rewritehostport("$SER_GWIP:5060");
 
 	# snom conditioner
 	if (method=="INVITE" && search("User-Agent: snom")) {
 		replace("100rel, ", "");
 	};
 
 	append_hf("P-hint: GATEWAY\r\n");
 	# use UDP to guarantee well-known sender port (TCP ephemeral)
 	t_relay_to_udp("$SER_GWIP","5060");
 }
 
 
 
 /* *********** handling of unavailable user ******************* */
 
 route[4] {
 /**/
 	# message store 
 	if (method=="MESSAGE") {
 		t_newtran();
 		if (m_store("0")) {
 			t_reply("202", "Accepted for Later Delivery");
 		} else {
 			t_reply("503", "Service Unavailable");
 		};
 		break;
 	};
 /**/
 	# non-Voip -- just send "off-line"
 	if (!(method=="INVITE" || method=="ACK" || method=="CANCEL")) {
 		sl_send_reply("404", "Not Found");
 		break;
 	};
 	# voicemail subscribers ...
 	t_newtran();
 	t_reply("404", "Not Found");
 	# we account missed incoming calls; previous statteful processing
 	# guarantees that retransmissions are not accounted
 	if (method=="INVITE") {
 		acc_db_request("404 missed call", "missed_calls");
 	};
 }
 
 EOF
 
 help > /dev/stderr