etc/sip-router.cfg
6f251f6b
 #
 # $Id$
 #
99fab38d
 # Example configuration file (simpler then ser-oob.cfg, but more
 # complex then ser-basic.cfg).
 #
bfb7651e
 # First start SER sample config script with:
 #   database, accounting, authentication, multi-domain support
 #   PSTN GW section, named flags, named routes, global-,
 #   domain- and user-preferences with AVPs
 # Several of these features are only here for demonstration purpose
 # what can be achieved with the SER config script language.
6f251f6b
 #
bfb7651e
 # If you look for a simpler version with a lot less dependencies
950216b3
 # please refer to the ser-basic.cfg file in your SER distribution.
99fab38d
 #
 # If you look for documentation, try http://sip-router.org/wiki/.
 # The right mailing lists for questions about this file is
b2b2b981
 # <sr-users@lists.kamailio.org>.
bfb7651e
 
 # To get this config running you need to execute the following commands
 # with the new serctl (the capital word are just place holders)
 # - ser_ctl domain add DOMAINNAME
 # - ser_ctl user add USERNAME@DOMAINNAME -p PASSWORD
99fab38d
 # ser_ctl can be obtained from
 # http://ftp.iptel.org/pub/serctl/daily-snapshots/.
 #
bfb7651e
 # If you want to have PID header for your user
 # - ser_attr add uid=UID asserted_id="PID"
 # If you want to have gateway support
 # - ser_db add attr_types name=gw_ip rich_type=string raw_type=2 description="The gateway IP for the default ser.cfg" default_flags=33
 # - ser_attr add global gw_ip=GATEWAY-IP
6f251f6b
 
99fab38d
 
 # ----------- Global Defines / Extra Features -------------------------------
 # (can be enabled either by uncommenting the corresponding #!define 
 #  statement or by starting with -A WITH_<FEATURE_NAME>, e.g.
 #  ser -A WITH_TLS -f /etc/ser/ser-oob.cfg )
 
 # enable TLS
 ##!define WITH_TLS
 
 # started from compile directory (not installed)
 ##!define LOCAL_TEST_RUN
 
 # xmlrpc allowed subnets (if defined XMLRPC requests with source ip matching
 # this network addresses will be allowed, if no XMLRPC_ALLOWED_SUBNETx is
 # defined only requests coming from localhost will be allowed).
 # E.g.: ser -A XMLRPC_ALLOW_NET1=192.168.1.0/24 -f ser-oob.cfg
 ##!define XMLRPC_ALLOW_NET1  192.168.0.0/16
 ##!define XMLRPC_ALLOW_NET2  10.0.0.0/255.0.0.0
 ##!define XMLRPC_ALLOW_NET3  172.16.0.0/12
 
 
6f251f6b
 # ----------- global configuration parameters ------------------------
 
ec8e49e8
 debug=2         # debug level (cmd line: -dddddddddd)
bfb7651e
 #memdbg=10 # memory debug log level
 #memlog=10 # memory statistics log level
ede3897b
 #log_facility=LOG_LOCAL0 # sets the facility used for logging (see syslog(3))
66304f28
 
91c5f901
 /* Uncomment these lines to enter debugging mode 
a0775846
 fork=no
66304f28
 log_stderror=yes
3e1eaf9d
 */
66304f28
 
bfb7651e
 check_via=no    # (cmd. line: -v)
 dns=no          # (cmd. line: -r)
6f251f6b
 rev_dns=no      # (cmd. line: -R)
06aaa54f
 #port=5060
 #children=4
ede3897b
 #user=ser
 #group=ser
 #disable_core=yes #disables core dumping
 #open_fd_limit=1024 # sets the open file descriptors limit
 #mhomed=yes  # usefull for multihomed hosts, small performance penalty
 #disable_tcp=yes 
 #tcp_accept_aliases=yes # accepts the tcp alias via option (see NEWS)
48c2ffc2
 sip_warning=yes
99fab38d
 #!ifdef WITH_TLS
c19a9ff3
 enable_tls=yes
99fab38d
 #!endif
ede3897b
 
 #
6f251f6b
 
 # ------------------ module loading ----------------------------------
 
99fab38d
 #!ifdef LOCAL_TEST_RUN
 loadpath "modules:modules_s"
 #!else
ec8e49e8
 loadpath "/usr/lib/ser/modules:/usr/lib/ser/modules_s"
99fab38d
 #!endif
ec8e49e8
 
bfb7651e
 # load a SQL database for authentication, domains, user AVPs etc.
ec8e49e8
 loadmodule "db_mysql"
 
 loadmodule "tm"
99fab38d
 loadmodule "sl"
ec8e49e8
 loadmodule "rr"
 loadmodule "maxfwd"
 loadmodule "usrloc"
 loadmodule "registrar"
 loadmodule "xlog"
 loadmodule "textops"
 loadmodule "ctl"
 loadmodule "cfg_rpc"
 loadmodule "auth"
 loadmodule "auth_db"
 loadmodule "gflags"
 loadmodule "domain"
 loadmodule "uri_db"
 loadmodule "avp"
 loadmodule "avp_db"
 loadmodule "acc_db"
 loadmodule "xmlrpc"
99fab38d
 #!ifdef WITH_TLS
 loadmodule "tls"
 #!endif
bfb7651e
 
 # ----------------- setting script FLAGS -----------------------------
 flags
0ee79c04
   FLAG_ACC          : 1,  # include message in accounting
7cc2f6e1
   FLAG_FAILUREROUTE : 2;  # we are operating from a failure route
6f251f6b
 
bfb7651e
 avpflags
   dialog_cookie;        # handled by rr module
0338eb56
 
6f251f6b
 # ----------------- setting module-specific parameters ---------------
 
bfb7651e
 # specify the path to you database here
da6e3789
 modparam("acc_db|auth_db|avp_db|domain|gflags|usrloc|uri_db", "db_url", "mysql://ser:heslo@127.0.0.1/ser")
6f251f6b
 
bfb7651e
 # -- usrloc params --
6f251f6b
 
bfb7651e
 # as we use the database anyway we will use it for usrloc as well
 modparam("usrloc", "db_mode", 1)
0338eb56
 
 # -- auth params --
bfb7651e
 modparam("auth_db", "calculate_ha1", yes)
8368b5f9
 modparam("auth_db", "plain_password_column", "password")
0338eb56
 
ef297ad9
 # -- rr params --
 # add value to ;lr param to make some broken UAs happy
 modparam("rr", "enable_full_lr", 1)
bfb7651e
 #
 # limit the length of the AVP cookie to only necessary ones
 modparam("rr", "cookie_filter", "(account)")
 #
 # you probably do not want that someone can simply read and change
 # the AVP cookie in your Routes, thus should really change this
 # secret value below
 modparam("rr", "cookie_secret", "MyRRAVPcookiesecret")
 
 # -- gflags params --
 # load the global AVPs
 modparam("gflags", "load_global_attrs", 1)
 
 # -- domain params --
 # load the domain AVPs
 modparam("domain", "load_domain_attrs", 1)
ef297ad9
 
bfb7651e
 # -- ctl params --
6309eded
 # by default ctl listens on unixs:/tmp/ser_ctl if no other address is
 # specified in modparams; this is also the default for sercmd
 modparam("ctl", "binrpc", "unixs:/tmp/ser_ctl")
 # listen on the "standard" fifo for backward compatibility
 modparam("ctl", "fifo", "fifo:/tmp/ser_fifo")
 # listen on tcp, localhost
99fab38d
 modparam("ctl", "binrpc", "tcp:127.0.0.1:2046")
6309eded
 
bfb7651e
 # -- acc_db params --
91d6466b
 # failed transactions (=negative responses) should be logged to
bfb7651e
 modparam("acc_db", "failed_transactions", 1)
 
0ee79c04
 # comment the next line if you dont want to have accounting to DB
bfb7651e
 modparam("acc_db", "log_flag", "FLAG_ACC")
 
91d6466b
 # -- tm params --
 # uncomment the following line if you want to avoid that each new reply
 # restarts the resend timer (see INBOUND route below)
 #modparam("tm", "restart_fr_on_each_reply", "0")
 
99fab38d
 #!ifdef WITH_TLS
 # -- tls params --
 modparam("tls", "verify_certificate", 0)
 #!ifdef  LOCAL_TEST_RUN
 modparam("tls", "certificate", "./modules/tls/sip-router-selfsigned.pem")
 modparam("tls", "private_key", "./modules/tls/sip-router-selfsigned.key")
 #separate TLS config file
 #modparam("tls", "config", "./modules/tls/tls.cfg")
 #!else
 modparam("tls", "certificate", "ser-selfsigned.pem")
 modparam("tls", "private_key", "ser-selfsigned.key")
 #separate TLS config file
 #modparam("tls", "config", "tls.cfg")
 #!endif
 
 
7cc2f6e1
 # -- xmlrpc params --
 # using a sub-route from the module is a lot safer then relying on the
 # request method to distinguish HTTP from SIP
 modparam("xmlrpc", "route", "RPC");
 
6f251f6b
 # -------------------------  request routing logic -------------------
 
 # main routing logic
 
 route{
bfb7651e
 	# if you have a PSTN gateway just un-comment the follwoing line and 
 	# specify the IP address of it to route calls to it
 	#$gw_ip = "1.2.3.4"
 
 	# first do some initial sanity checks
 	route(INIT);
 
a0a27c73
 	# bypass the rest of the script for CANCELs if possible
 	route(CATCH_CANCEL);
 
bfb7651e
 	# check if the request is routed via Route header or
 	# needs a Record-Route header
 	route(RR);
 
 	# check if the request belongs to our proxy
 	route(DOMAIN);
 
 	# handle REGISTER requests
 	route(REGISTRAR);
 
 	# from here on we want to know you is calling
 	route(AUTHENTICATION);
 
 	# check if we should be outbound proxy for a local user
 	route(OUTBOUND);
 
 	# check if the request is for a local user
 	route(INBOUND);
 
7cc2f6e1
 	# here you could for example try to do an ENUM lookup before
bfb7651e
 	# the call gets routed to the PSTN
 	#route(ENUM);
 
 	# lets see if someone wants to call a PSTN number
 	route(PSTN);
 
 	# nothing matched, reject it finally
e89d3f83
 	sl_reply("404", "No route matched");
bfb7651e
 }
 
 route[FORWARD]
 {
 	# here you could decide wether this call needs a RTP relay or not
 
7cc2f6e1
 	# if this is called from the failure route we need to open a new branch
 	if (isflagset(FLAG_FAILUREROUTE)) {
 		append_branch();
 	}
 
 	# if this is an initial INVITE (without a To-tag) we might try another
 	# (forwarding or voicemail) target after receiving an error
ec8e49e8
 	if (method=="INVITE" && strempty(@to.tag)) {
7cc2f6e1
 		t_on_failure("FAILURE_ROUTE");
 	}
 
bfb7651e
 	# send it out now; use stateful forwarding as it works reliably
 	# even for UDP2TCP
 	if (!t_relay()) {
 		sl_reply_error();
37416ef0
 	}
bfb7651e
 	drop;
 }
 
 route[INIT]
 {
e908c20d
 	# initial sanity checks -- messages with
66304f28
 	# max_forwards==0, or excessively long requests
6f251f6b
 	if (!mf_process_maxfwd_header("10")) {
e89d3f83
 		sl_reply("483","Too Many Hops");
bfb7651e
 		drop;
37416ef0
 	}
bfb7651e
 
ec8e49e8
 	if (msg:len >=  4096 ) {
e89d3f83
 		sl_reply("513", "Message too big");
bfb7651e
 		drop;
37416ef0
 	}
0d35f439
 
bfb7651e
 	# you could add some NAT detection here for example
 
 	# or you cuold call here some of the check from the sanity module
 
7cc2f6e1
 	# lets account all initial INVITEs
 	# further in-dialog requests are accounted by a RR cookie (see below)
ec8e49e8
 	if (method=="INVITE" && strempty(@to.tag)) {
bfb7651e
 		setflag(FLAG_ACC);
 	}
 }
 
 route[RPC]
 {
 	# allow XMLRPC from localhost
 	if ((method=="POST" || method=="GET") &&
99fab38d
 		(src_ip==127.0.0.1
 	#!ifdef XMLRPC_ALLOW_NET1
 		|| src_ip == XMLRPC_ALLOW_NET1
 	#!endif
 	#!ifdef XMLRPC_ALLOW_NET2
 		|| src_ip == XMLRPC_ALLOW_NET2
 	#!endif
 	#!ifdef XMLRPC_ALLOW_NET3
 		|| src_ip == XMLRPC_ALLOW_NET3
 	#!endif
 		)) {
bfb7651e
 
 		if (msg:len >= 8192) {
e89d3f83
 			sl_reply("513", "Request to big");
bfb7651e
 			drop;
 		}
 
99fab38d
 		# close connection only for xmlrpclib user agents (there is a bug in
 		# xmlrpclib: it waits for EOF before interpreting the response).
 		if (search("^User-Agent:.*xmlrpclib"))
 			set_reply_close();
 		set_reply_no_connect(); # optional
7cc2f6e1
 		# lets see if a module wants to answer this
bfb7651e
 		dispatch_rpc();
 		drop;
 	}
 }
 
 route[RR]
 {
0d35f439
 	# subsequent messages withing a dialog should take the
 	# path determined by record-routing
caf91a1c
 	if (loose_route()) {
0d35f439
 		# mark routing logic in request
 		append_hf("P-hint: rr-enforced\r\n"); 
 
bfb7651e
 		# if the Route contained the accounting AVP cookie we
 		# set the accounting flag for the acc_db module.
 		# this is more for demonstration purpose as this could
 		# also be solved without RR cookies.
 		# Note: this means all in-dialog request will show up in the
0ee79c04
 		# accounting tables, so prepare your accounting software for this ;-)
bfb7651e
 		if ($account == "yes") {
 			setflag(FLAG_ACC);
 		}
6f251f6b
 
bfb7651e
 		# for broken devices which overwrite their Route's with each
 		# (not present) RR from within dialog requests it is better
 		# to repeat the RRing
 		# and if we call rr after loose_route the AVP cookies are restored
 		# automatically :)
 		record_route();
6f251f6b
 
bfb7651e
 		route(FORWARD);
 	} else if (!method=="REGISTER") {
 		# we record-route all messages -- to make sure that
 		# subsequent messages will go through our proxy; that's
 		# particularly good if upstream and downstream entities
 		# use different transport protocol
3d4a32d0
 
bfb7651e
 		# if the inital INVITE got the ACC flag store this in
 		# an RR AVP cookie. this is more for demonstration purpose
 		if (isflagset(FLAG_ACC)) {
 			$account = "yes";
 			setavpflag($account, "dialog_cookie");
 		}
3d4a32d0
 
bfb7651e
 		record_route();
 	}
 }
6f251f6b
 
bfb7651e
 route[DOMAIN]
 {
 	# check if the caller is from a local domain
 	lookup_domain("$fd", "@from.uri.host");
0d35f439
 
bfb7651e
 	# check if the callee is at a local domain
 	lookup_domain("$td", "@ruri.host");
 
 	# we dont know the domain of the caller and also not
 	# the domain of the callee -> somone uses our proxy as
 	# a relay
ec8e49e8
 	if (strempty($t.did) && strempty($f.did)) {
e89d3f83
 		sl_reply("403", "Relaying Forbidden");
bfb7651e
 		drop;
 	}
 }
 
 route[REGISTRAR]
 {
 	# if the request is a REGISTER lets take care of it
 	if (method=="REGISTER") {
 		# check if the REGISTER if for one of our local domains
ec8e49e8
 		if (strempty($t.did)) {
e89d3f83
 			sl_reply("403", "Register forwarding forbidden");
bfb7651e
 			drop;
 		}
 
 		# we want only authenticated users to be registered
 		if (!www_authenticate("$fd.digest_realm", "credentials")) {
 			if ($? == -2) {
e89d3f83
 				sl_reply("500", "Internal Server Error");
bfb7651e
 			} else if ($? == -3) {
e89d3f83
 				sl_reply("400", "Bad Request");
bfb7651e
 			} else {
ec8e49e8
 				if ($digest_challenge != "") {
bfb7651e
 					append_to_reply("%$digest_challenge");
 				}
e89d3f83
 				sl_reply("401", "Unauthorized");
bfb7651e
 			}
 			drop;
37416ef0
 		}
bfb7651e
 
 		# check if the authenticated user is the same as the target user
debaca56
 		if (!lookup_user("$tu.uid", "@to.uri")) {
e89d3f83
 			sl_reply("404", "Unknown user in To");
bfb7651e
 			drop;
 		}
 
 		if ($f.uid != $t.uid) {
e89d3f83
 			sl_reply("403", "Authentication and To-Header mismatch");
bfb7651e
 			drop;
 		}
 
29edaf93
 		# check if the authenticated user is the same as the request originator
 		# you may uncomment it if you care, what uri is in From header
debaca56
 		#if (!lookup_user("$fu.uid", "@from.uri")) {
c82093ef
 		#	sl_reply("404", "Unknown user in From");
29edaf93
 		#	drop;
 		#}
debaca56
 		#if ($fu.uid != $tu.uid) {
c82093ef
 		#	sl_reply("403", "Authentication and From-Header mismatch");
29edaf93
 		#	drop;
 		#}
 
11e0bbb6
 		# everything is fine so lets store the binding
37416ef0
 		if (!save_contacts("location")) {
 			sl_reply("400", "Invalid REGISTER Request");
 			drop;
 		}
bfb7651e
 		drop;
37416ef0
 	}
0d35f439
 }
 
bfb7651e
 route[AUTHENTICATION]
0d35f439
 {
bfb7651e
 	if (method=="CANCEL" || method=="ACK") {
 		# you are not allowed to challenge these methods
 		break;
 	}
 
 	# requests from non-local to local domains should be permitted
 	# remove this if you want a walled garden
ec8e49e8
 	if (strempty($f.did)) {
bfb7651e
 		break;
 	}
 
 	# as gateways are usually not able to authenticate for their
 	# requests you will have trust them base on some other information
 	# like the source IP address. WARNING: if at all this is only safe
 	# in a local network!!!
 	#if (src_ip==a.b.c.d) {
 	#	break;
 	#}
 
 	if (!proxy_authenticate("$fd.digest_realm", "credentials")) {
 		if ($? == -2) {
e89d3f83
 			sl_reply("500", "Internal Server Error");
bfb7651e
 		} else if ($? == -3) {
e89d3f83
 			sl_reply("400", "Bad Request");
bfb7651e
 		} else {
ec8e49e8
 			if ($digest_challenge != "") {
bfb7651e
 				append_to_reply("%$digest_challenge");
 			}
e89d3f83
 			sl_reply("407", "Proxy Authentication Required");
bfb7651e
 		}
 		drop;
 	}
 
 	# check if the UID from the authentication meets the From header
 	$authuid = $uid;
debaca56
 	if (!lookup_user("$fu.uid", "@from.uri")) {
bfb7651e
 		del_attr("$uid");
 	}
debaca56
 	if ($fu.uid != $fr.authuid) {
e89d3f83
 		sl_reply("403", "Fake Identity");
bfb7651e
 		drop;
 	}
 	# load the user AVPs (preferences) of the caller, e.g. for RPID header
 	load_attrs("$fu", "$f.uid");
 }
 
 route[OUTBOUND]
 {
 	# if a local user calls to a foreign domain we play outbound proxy for him
 	# comment this out if you want a walled garden
ec8e49e8
 	if ($f.did != ""  && $t.did == "") {
bfb7651e
 		append_hf("P-hint: outbound\r\n");
 		route(FORWARD);
 	}
 }
 
 route[INBOUND]
 {
 	# lets see if know the callee
debaca56
 	if (lookup_user("$tu.uid", "@ruri")) {
bfb7651e
 
91d6466b
 		# load the preferences of the callee to have his timeout values loaded
 		load_attrs("$tu", "$t.uid");
bfb7651e
 
29edaf93
 		# if you want to know if the callee username was an alias
bfb7651e
 		# check it like this
ec8e49e8
 		#if (strempty($tu.uri_canonical)) {
29edaf93
 			# if the alias URI has different AVPs/preferences
 			# you can load them into the URI track like this
 			#load_attrs("$tr", "@ruri");
 		#}
bfb7651e
 
7cc2f6e1
 		# check for call forwarding of the callee
 		# Note: the forwarding target has to be full routable URI
 		#       in this example
ec8e49e8
 		if ($tu.fwd_always_target != "") {
7cc2f6e1
 			attr2uri("$tu.fwd_always_target");
 			route(FORWARD);
 		}
 
bfb7651e
 		# native SIP destinations are handled using our USRLOC DB
 		if (lookup_contacts("location")) {
 			append_hf("P-hint: usrloc applied\r\n");
91d6466b
 
 			# we set the TM module timers according to the prefences
 			# of the callee (avoid too long ringing of his phones)
 			# Note1: timer values have to be in ms now!
 			# Note2: this makes even more sense if you switch to a voicemail
7cc2f6e1
 			#        from the FAILURE_ROUTE below
ec8e49e8
 			if ($t.fr_inv_timer != 0) {
 				if ($t.fr_timer != 0) {
91d6466b
 					t_set_fr("$t.fr_inv_timer", "$t.fr_timer");
 				} else {
 					t_set_fr("$t.fr_inv_timer");
 				}
 			}
 
bfb7651e
 			route(FORWARD);
 		} else {
e89d3f83
 			sl_reply("480", "User temporarily not available");
bfb7651e
 			drop;
 		}
37416ef0
 	}
6f251f6b
 }
 
bfb7651e
 route[PSTN]
 {
 	# Only if the AVP 'gw_ip' is set and the request URI contains
 	# only a number we consider sending this to the PSTN GW.
 	# Only users from a local domain are permitted to make calls.
 	# Additionally you might want to check the acl AVP to verify
 	# that the user is allowed to make such expensives calls.
ec8e49e8
 	if ($f.did != "" && $gw_ip != "" &&
bfb7651e
 		uri=~"sips?:\+?[0-9]{3,18}@.*") {
 		# probably you need to convert the number in the request
 		# URI according to the requirements of your gateway here
 
 		# if an AVP 'asserted_id' is set we insert an RPID header
ec8e49e8
 		if ($asserted_id != "") {
bfb7651e
 			xlset_attr("$rpidheader", "<sip:%$asserted_id@%@ruri.host>;screen=yes");
 			replace_attr_hf("Remote-Party-ID", "$rpidheader");
 		}
 
 		# just replace the domain part of the RURI with the
 		# value from the AVP and send it out
 		attr2uri("$gw_ip", "domain");
 		route(FORWARD);
 	}
 }
7cc2f6e1
 
a0a27c73
 route[CATCH_CANCEL] {
 	# check whether there is a corresponding INVITE to the CANCEL,
 	# and bypass the rest of the script if possible
 
 	if (method == CANCEL) {
 		if (!t_relay_cancel()) { # implicit drop if the INVITE was found
 
 			# INVITE was found but some error occurred
 			sl_reply("500", "Internal Server Error");
 			drop;
 		}
 		# bad luck, no corresponding INVITE was found,
 		# we have to continue with the script
 	}
 }
 
7cc2f6e1
 failure_route[FAILURE_ROUTE]
 {
 	# mark for the other routes that we are operating from here on from a
 	# failure route
 	setflag(FLAG_FAILUREROUTE);
 
 	if (t_check_status("486|600")) {
 		# if we received a busy and a busy target is set, forward it there
 		# Note: again the forwarding target has to be a routeable URI
ec8e49e8
 		if ($tu.fwd_busy_target != "") {
7cc2f6e1
 			attr2uri("$tu.fwd_busy_target");
 			route(FORWARD);
 		}
 		# alternatively you could forward the request to SEMS/voicemail here
 	}
 	else if (t_check_status("408|480")) {
 		# if we received no answer and the noanswer target is set,
 		# forward it there
 		# Note: again the target has to be a routeable URI
ec8e49e8
 		if ($tu.fwd_noanswer_target != "") {
7cc2f6e1
 			attr2uri("$tu.fwd_noanswer_target");
 			route(FORWARD);
 		}
 		# alternatively you could forward the request to SEMS/voicemail here
 	}
 }