#
# simple quick-start config script
#

# ----------- global configuration parameters ------------------------

debug=3        # debug level (cmd line: -dddddddddd)
fork=yes
log_stderror=no  # (cmd line: -E)
# memlog=5 # memory debug log level
# log_facility=LOG_LOCAL0 # sets the facility used for logging (see syslog(3))
children = 1

check_via=no	# (cmd. line: -v)
dns=no           # (cmd. line: -r)
rev_dns=no      # (cmd. line: -R)

listen=127.0.0.1:5060
listen=1.2.3.4:5060

loadmodule "/usr/local/lib/ser/modules/maxfwd.so"

# Database drivers
loadmodule "/usr/local/lib/ser/modules/mysql.so"

loadmodule "/usr/local/lib/ser/modules/sl.so"

# AVP
loadmodule "/usr/local/lib/ser/modules/avp.so"
loadmodule "/usr/local/lib/ser/modules/avp_db.so"
loadmodule "/usr/local/lib/ser/modules/avpops.so"

loadmodule "/usr/local/lib/ser/modules/domain.so"

# Authentication
loadmodule "/usr/local/lib/ser/modules/auth.so"
loadmodule "/usr/local/lib/ser/modules/auth_db.so"

# Registrar
loadmodule "/usr/local/lib/ser/modules/usrloc.so"
loadmodule "/usr/local/lib/ser/modules/registrar.so"

loadmodule "/usr/local/lib/ser/modules/gflags.so"
loadmodule "/usr/local/lib/ser/modules/rr.so"

# NAT traversal
loadmodule "/usr/local/lib/ser/modules/nathelper.so"

loadmodule "/usr/local/lib/ser/modules/tm.so"
loadmodule "/usr/local/lib/ser/modules/textops.so"
loadmodule "/usr/local/lib/ser/modules/xlog.so"

# Management
loadmodule "/usr/local/lib/ser/modules/xmlrpc.so"

loadmodule "/usr/local/lib/ser/modules/uri.so"
loadmodule "/usr/local/lib/ser/modules/uri_db.so"

loadmodule "/usr/local/lib/ser/modules/acc_syslog.so"
loadmodule "/usr/local/lib/ser/modules/eval.so"

# ----------------- setting script FLAGS -----------------------------
flags
	FLAG_NAT_UAC	:1,	# UA will be marked as behind NAT if this flag is set 
	FLAG_NAT_UAS	:2;

avpflags 
	dialog_cookie;		# handled by rr module

modparam("usrloc|domain|avp_db|uri_db|auth_db|gflags|msilo|speeddial|acc_db", "db_url", "mysql://ser:heslo@localhost/ser")
modparam("auth_db", "calculate_ha1", yes)
modparam("auth_db", "password_column", "password")
modparam("domain", "db_mode", 1)
modparam("domain", "load_domain_attrs", yes)
modparam("gflags", "load_global_attrs", yes)
modparam("usrloc", "db_mode", 1)

modparam("registrar", "save_nat_flag", "FLAG_NAT_UAC")
modparam("registrar", "load_nat_flag", "FLAG_NAT_UAS")
# modparam("usrloc|registrar", "use_domain", 1)

modparam("nathelper", "natping_interval", 30) 
modparam("nathelper", "ping_nated_only", 1)   


# If using more RTP proxies they are addressed by index starting from index 0 
modparam("nathelper", "rtpproxy_sock", "unix:/var/run/rtpproxy.sock udp:5.6.7.8:22222")

modparam("acc_syslog", "early_media", no)
modparam("acc_syslog", "failed_transactions", no)
modparam("acc_syslog", "report_ack", no)
modparam("acc_syslog", "report_cancels", no)
modparam("acc_syslog", "log_flag", "acc.log")
modparam("acc_syslog", "log_missed_flag", "acc.missed");

modparam("rr", "enable_full_lr", 1)    # testing purpose

route {
	############### Generic processing independend of domains ################
	route("generic");

	lookup_domain("$fd","@from.uri.host");
#	lookup_domain("From");
 
	lookup_domain("$td","@ruri.host");
#	lookup_domain("Request-URI");

	# First of all check for 3rd party relaying attempts. This must be at
	# The beginning of the script, because rewrite actions below can change
	# local destination domain to 3rd party domain. In such a case 3rdparty
	# Inbound calls would match this condition (if it was at the end of
	# the configuration file) and reject an otherwise valid call.
	if (!$f.did && !$t.did) {
        	sl_reply("400", "No Relaying");
        	drop;
	}

	# Processing based purely on From domain, that includes authentication
	# and identity verification, caller profile loading, NAT testing
	if ($f.did) { 
		route("nat.detect");            # NAT detection comes here, this section sets flag FLAG_NAT_UAC
		route("authenticate");

		if (!lookup_user("From")) {
#		if (!lookup_user("$fu", "@from.uri.user")) {
	    		sl_reply("400", "Fake Identity");
	    		drop;
		}
		load_attrs("$fu", "$f.uid");
	}

    ################ Processing based on the called domain ####################
	if ($t.did) {
        	if (method == REGISTER) {
	    		route("registrar");
	    		exit(1);
		}


		# (redirect)
		if ($f.did) {
           		;
        	}

		# Once we get here, the request can be either from one of our
		# domains or from a 3rd party domain. Thus we can put actions
		# independent of the initiator of the request here.

		# 1) Access control
		# 2) ENUM
		# 3) Test for PSTN gateways and other SIP servers that belong
		#    to the same domain

		#route("enum");
		# Test whether $t.did has changed 

		# We have finished all the destination rewrites here, the request
		# is neither for another server in our domain nor for a 3rd party
		# domain, thus try to lookup end-user in the user location database.

		if (!lookup_user("Request-URI")) {
#		if (!lookup_user("$tu", "@ruri.user")) {
			sl_reply("404", "Not Found");
			drop;
        	}

        	# User found, load his profile
        	load_attrs("$tu", "$t.uid");
#		load_attrs("t.uid");
			
        	if (!lookup_contacts("location")) {
            		sl_reply("483", "Temporarily Unavailable");
	    		xlog("L_ERR","LXLOG: 483 - Callee temporarily unavailable");
            		drop;
        	}
        	if (isflagset("FLAG_NAT_UAS")) {
			xlog("L_ERR","LXLOG: callee is behind NAT - setting t.nat = true");
			$t.nat = true;
		}
		else $t.nat = false;
	}

	# Outbound requests

	# NAT traversal based on $f.nat and $t.nat
	route("send");
}


route["generic"]
{
    # Handle XMLRPC requests first
	if (method == "POST" || method == "GET") {
		route("xmlrpc");
		drop;
	}

	if (!process_maxfwd("10")) {
		sl_reply("483", "Too Many Hops");
		drop;
	}

	if (msg:len >= max_len) {
		sl_reply("513", "Message Too Large");
		drop;
	}

	loose_route();

    ################### Mid-dialog requests need special treatment ############
	if (has_totag()) {
		route("in_dialog");
		drop;
	}
}


#
# Handling of in-dialog requests
#
route["in_dialog"]
{
	dump_attrs();
	route("send");
	drop;
}


#
# Handle REGISTER requests
#
route["registrar"]
{
    # Make sure this is domain our registrar is responsible for
    
	if (!lookup_domain("$td","@to.uri.host")) {
#	if (!lookup_domain("To")) {
        	sl_reply("404", "Domain Not Local For Registrar");
		drop;
    	}


    # Make sure that this is one of our users
#	if (!lookup_user("$tu", "@to.uri.user")) {
    	if (!lookup_user("To")) {
        	sl_reply("404", "Registrant Not Found");
		drop;
    	}

    # Load registrant's profile
    	load_attrs("$tu", "$t.uid");
#	load_attrs("t.uid");

    
    # Do whatever is needed here based on registrant's profile

   	if (!save_contacts("location")) {
        	sl_reply("400", "Invalid REGISTER Request");
        	drop;
    	}
}


#
# Digest authentication using database
#
route["auth_db"]
{
 
	if (!proxy_authenticate("@to.uri.host", "credentials")) {
		append_to_reply("%$digest_challenge");
		sl_reply("407", "Proxy Authentication Required"); 
		drop; 
	}

}



#
# Authentication stanza
#
route["authenticate"]
{
 
    # Test for trusted peers here that will not be authenticated

	if (proto == TLS) {
		route("auth_tls");
	} else {
		route("auth_db");
	# route("auth_radius");    
	}
}


#
# XML-RPC based management interface
#
route["xmlrpc"]
{
 xlog("L_ERR","LXLOG: SIP message trace - entered xmlrpc route");
 
	create_via();

	# XML-RPC requests must be secured using TLS
	if (proto != TCP) {
		sl_reply("490", "Invalid Transport Protocol");
        	drop;
	}

	dispatch_rpc();
	drop;
}


############### Forward blocks ###############


#
# Forward the request, do NAT mangling if necessary before
# forwarding
#
route["send"]
{
xlog("L_ERR","LXLOG: SIP message trace - entered send route - From <%fu> To <%tu> request <%rm>");
 
	route("nat.mangle");
	t_on_reply("nat.mangle");

	# if record routed store following AVPs to Record Route header
	if ($record_route) {
		setavpflag("$f.nat", "dialog_cookie");
		setavpflag("$t.nat", "dialog_cookie");
		setavpflag("$rtpproxy", "dialog_cookie");
		setavpflag("$con_realm", "dialog_cookie");
		setavpflag("$rtp_proxy_node", "dialog_cookie");
		setavpflag("$f.comedia", "dialog_cookie");
		setavpflag("$t.comedia", "dialog_cookie");

		record_route();
	}


	if (!t_relay()) {
        	sl_reply_error();
        	drop;
    	}
}


#
# NAT detection, this block is executed early in the script and it's
# purpose is to detect whether the UAC is behind NAT. This block does
# no NAT mangling, this is postponed till the end of the script. This
# block only sets FLAG_NAT_UAC flag if the UAC is behind NAT
#
route["nat.detect"]
{
 xlog("L_ERR","LXLOG: SIP message trace - entered nat.detect route");
 
	# Skip spiralling requets
	if (src_ip == 127.0.0.1) return;

	# Skip requests that already passed a proxy
	if (is_present_hf("^Record-Route:")) {
		xlog("L_ERR","LXLOG: Record Route header is present");
		return;
	}

	if (nat_uac_test("3")) {
		force_rport();
		setflag("FLAG_NAT_UAC");
		$f.nat = true;
	}
	else $f.nat = false;
	return;
}


#
# This block does whatever is necessary to make UAS behind NAT
# reachable, that includes rewriting contact and forcing RTP
# proxy if necessary. The logic in this block is base on
# the value of FLAG_NAT_UAC and FLAG_NAT_UAS flags
#
onreply_route["nat.mangle"]
{
 xlog("L_ERR","LXLOG: SIP message trace - entered onreply nat.mangle route <%rr><%rs>");


	# Rewrite Contact in 200 OK if UAS is behind NAT

	if ($t.nat || $tr.nat) {
		xlog("L_ERR","LXLOG: Onreply - fixing NATed contact");
		fix_nated_contact();
	}

	# Apply RTP proxy if necessary, but only for INVITE transactions
	# and 183 or 2xx replie
	if (!$f.nat && (!$t.nat || !$tr.nat)) return;
    
	# checks if both UAC and UAS are in the same connectivity realm
	if ($con_realm || $tr.con_realm) return;
    
	if (@cseq.method != "INVITE") return;
	if ((status =~ "(183)|2[0-9][0-9]") && !search("^Content-Length: 0")) {

		if (!$rtpproxy && !$tr.rtpproxy) {
	
			xlog("L_ERR","LXLOG: UA supports symmettric RTP and passive role");
			if ($t.nat || $tr.nat) {
				fix_nated_sdp("8");
				return;
			}
        	        else return;
	        }	
		if ($rtp_proxy_node) 
			eval_push("x:N%$rtp_proxy_node");

		else if ($tr.rtp_proxy_node) 
			eval_push("x:N%$tr.rtp_proxy_node");
	
		force_rtp_proxy("@eval.pop[-1]");
	}
	return;
}


#
# This block does whatever is necessary to make UACs behind NAT
# reachable, that includes rewriting contact and forcing RTP
# proxy if necessary. The logic in this block is based on
# the value of FLAG_NAT_UAC and FLAG_NAT_UAS flags
#
route["nat.mangle"]
{
xlog("L_ERR","LXLOG: SIP message trace - entered nat.mangle route");
 
	# Rewrite Contact if the UAC is behind NAT
	if ($f.nat) {
		if (method == "REGISTER") {
			fix_nated_register();
		}
		else {
			fix_nated_contact();
		}
	}


	# AVP $rtp_proxy_node is stored in Record-Route HF and in URI CLASS
	# If this AVP attribute appears in this TO URI CLASS then it must be checked
	# by $tr.rtpproxy
    
	if (($tr.rtpproxy || $fr.rtpproxy) && (method == "BYE" || method == "CANCEL")) {
		xlog("L_ERR","LXLOG: BYE or CANCEL - UNFORCING RTP proxy");
		
		if ($tr.rtp_proxy_node)
			unforce_rtp_proxy("$tr.rtp_proxy_node");
		else
			unforce_rtp_proxy("$rtp_proxy_node");
		
	}	  
	else if (method == "INVITE") {
 	    
		# If either UAC or UAS is behind NAT then go on
		#
		if (!$f.nat && (!$t.nat || !$tr.nat)) return;

		# Force record routing if either party is behind NAT
		$record_route = true;
	   
		   
		# Checks if both UAC and UAS are in the same
		# connectivity realm
		if ($con_realm || $tr.con_realm) return;
		if ($f.connectivity_realm == $t.connectivity_realm) {
			$con_realm = true;
			return;
		}

		xlog("L_ERR","LXLOG: reguest message - is not in connectivity realm");


		# Checking if one UA is behind NAT and its peer 
		# in public supports symmetric RTP and passive attribute
		if (($f.sym_pass || $fr.comedia) && ($t.nat || $tr.nat) && !$f.nat) { 
			$f.comedia = $f.sym_pass;
			xlog("L_ERR","LXLOG: Caller supports sym_pass and callee is behind NAT");
			return;
		}
		if (($t.sym_pass || $tr.comedia) && $f.nat && (!$t.nat || !$tr.nat)) {
			$t.comedia = $t.sym_pass;
			fix_nated_sdp("8");
			xlog("L_ERR","LXLOG: Caller is behind NAT and callee supports sym_pass");
			xlog("L_ERR","LXLOG: Appending passive attr to INVITE for public UA");
			return;
		}
		
		
		# This section is for re-INVITEs : If RTP proxy was forced during initiating
		# a new call then here is performed a check for flags $rtpproxy and $rtp_proxy_node.
		# This will use the same RTP proxy as was chosen while initiating the call.
		# Parameter L performs lookup which only rewrite SDP when
		# corresponding session is already existing in RTP proxy.
		# Parameter N followed by AVP attribute is stored index(integer) of a particular 
		# RTP proxy defined at the beginning of this script in modparam
		
		xlog("L_ERR","LXLOG: INVITE - rewriting IP and port in SDP - force_rtp_proxy");

		if ($tr.rtpproxy || $rtpproxy) { 
			if ($tr.rtp_proxy_node)
				eval_push("x:LN%$tr.rtp_proxy_node");
			else 
				eval_push("x:LN%$rtp_proxy_node");
			
			force_rtp_proxy("@eval.pop[-1]");
		}
	
		# For initial call: sets the rtpproxy attribute to true and assign different AVP attribute
		# for RTP_proxy_node to make sure it is stored in Record-Route HF
		# $node comes from user_attrs table
	
		else {
		
			$rtpproxy = true;
			$rtp_proxy_node = $node;
			
			# this will simply makes a string e.g. N0
			# which is inserted in force_rtp_proxy function
			# as first parameter
			eval_push("x:N%$rtp_proxy_node");	    
			force_rtp_proxy("@eval.pop[-1]");
		}
	}
    
return 1;

}