#!KAMAILIO
#
# This config file implements the basic I-CSCF functionality
#     - web: https://www.kamailio.org
#     - git: http://sip-router.org
#
# Refer to the Core CookBook at https://www.kamailio.org/w/documentation/
# for an explanation of possible statements, functions and parameters.
#
# Direct your questions about this file to: <sr-users@lists.kamailio.org>.
#
# For more information about the various parameters, functions and statements
# try http://sip-router.org/wiki/ .
#

system.shutdownmode = 0 desc "System shutdown mode"
system.service = "Interrogating-CSCF" desc "Function of this server"

include_file "icscf.cfg"

####### Defined Values #########
# *** Value defines - IDs used later in config

# - flags
#	FLT_ - per transaction (message) flags
#	FLB_ - per branch flags

#!define FLT_CAPTURE 1

#!ifdef WITH_XMLRPC
listen=tcp:127.0.0.1:4060
#!endif

####### Global Parameters #########
#!ifdef WITH_DEBUG
debug=5
log_stderror=yes
sip_warning=yes
#!else
debug=2
log_stderror=no
sip_warning=no
#!endif

user_agent_header="User-Agent: Kamailio I-CSCF"
server_header="Server: Kamailio I-CSCF"

/* comment the next line to enable the auto discovery of local aliases
   based on reverse DNS on IPs (default on) */
auto_aliases=no

# Do SRV-Loadbalancing:
dns_srv_lb=yes
# Always: Also try IPv6:
dns_try_ipv6=yes
# Query NAPTR-Records as well:
dns_try_naptr=no

#!ifdef WITH_XMLRPC
#!ifndef WITH_TCP
#!define WITH_TCP
#!endif
#!ifndef TCP_PROCESSES
# Number of TCP Processes
#!define TCP_PROCESSES 3
#!endif
#!endif

#!ifdef WITH_TCP
# life time of TCP connection when there is no traffic
# - a bit higher than registration expires to cope with UA behind NAT
tcp_connection_lifetime=3615
#!ifdef TCP_PROCESSES
tcp_children=TCP_PROCESSES
#!endif
#!else
disable_tcp=yes
#!endif

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

children=64

# ------------------ module loading ----------------------------------
mpath="/usr/lib64/kamailio/modules_k/:/usr/lib64/kamailio/modules/:/usr/lib/kamailio/modules_k/:/usr/lib/kamailio/modules/"
# (we try both the lib64 and the lib directory)
loadmodule "tm"
loadmodule "sl"
loadmodule "rr"
loadmodule "pv"
loadmodule "textops"
loadmodule "maxfwd"
loadmodule "sanity"
loadmodule "siputils"
loadmodule "kex"
loadmodule "tmx"
loadmodule "pike"
loadmodule "corex"

# Control interfaces:
loadmodule "ctl"
loadmodule "cfg_rpc"
#!ifdef WITH_XMLRPC
loadmodule "xmlrpc"
#!endif

# Load the according DB-Module:
loadmodule "db_mysql"
#!ifdef DB_URL2
loadmodule "db_cluster"
#!endif

loadmodule "cdp.so"
loadmodule "cdp_avp.so"
loadmodule "xlog.so"

loadmodule "ims_icscf.so"

#!ifdef CAPTURE_NODE
loadmodule "siptrace.so"
#!endif

#!ifdef WITH_DEBUG
loadmodule "debugger.so"
#!endif

#!ifdef WITH_TLS
loadmodule "tls.so"
#!endif

#!ifdef PEERING
loadmodule "enum"
#!endif

# ----------------- setting module-specific parameters ---------------
#!ifdef DB_URL2
# ----- db_cluster params -----
modparam("db_cluster", "connection", DB_URL)
modparam("db_cluster", "connection", DB_URL2)
modparam("db_cluster", "cluster", "cluster1=>con1=2s2s;con2=1s1s")
#!endif

loadmodule "jsonrpcs.so"
# ----- jsonrpcs params -----
modparam("jsonrpcs", "pretty_format", 1)
/* set the path to RPC fifo control file */
# modparam("jsonrpcs", "fifo_name", "/run/kamailio/kamailio_rpc.fifo")
/* set the path to RPC unix socket control file */
# modparam("jsonrpcs", "dgram_socket", "/run/kamailio/kamailio_rpc.sock")

# -- rr params --
# add value to ;lr param to make some broken UAs happy
modparam("rr", "enable_full_lr", 1)

# -- cdp params --
modparam("cdp", "config_file", "/etc/kamailio_icscf/icscf.xml")

# ----- icscf params -----
# Comment the following line to enable realm routing
#!ifdef CXDX_FORCED_PEER
modparam("ims_icscf", "cxdx_forced_peer", CXDX_FORCED_PEER)
#!endif
modparam("ims_icscf", "cxdx_dest_realm", NETWORKNAME)

# DB-URL, where information about S-CSCF-Server can be found:
#!ifdef DB_URL2
modparam("ims_icscf", "db_url", "cluster://cluster1")
#!else
modparam("ims_icscf", "db_url", DB_URL)
#!endif

#!ifdef PEERING
# Route which is executed, in case HSS returned "User-Unknown" on LIR request
modparam("ims_icscf", "route_lir_user_unknown", "lir_term_user_unknown")
#!endif
#!ifdef FALLBACK_AUTH
# Route which is executed, in case HSS returned "User-Unknown" on UAR request
modparam("ims_icscf", "route_uar_user_unknown", "uar_term_user_unknown")
#!endif

#!ifdef WITH_TLS
# ----- tls params -----
modparam("tls", "config", "/etc/kamailio/tls.cfg")
#!endif

#!ifdef WITH_XMLRPC
# ----- xmlrpc params -----
modparam("xmlrpc", "route", "XMLRPC");
modparam("xmlrpc", "url_match", "^/RPC")
#!endif

# ----- ctl params -----
modparam("ctl", "binrpc", "unix:/run/kamailio_icscf/kamailio_ctl")

#!ifdef WITH_DEBUG
# ----- debugger params -----
modparam("debugger", "cfgtrace", 1)
#!endif

#!ifdef CAPTURE_NODE
# Destination, where to send the traffic
modparam("siptrace", "duplicate_uri", CAPTURE_NODE)
# Trace all traffic
modparam("siptrace", "trace_on", 1)
modparam("siptrace", "trace_to_database", 0)
modparam("siptrace", "trace_flag", FLT_CAPTURE)
modparam("siptrace", "hep_mode_on", 1)
#!endif

#!ifdef PEERING
# ----- enum params -----
modparam("enum", "domain_suffix", ENUM_SUFFIX)
#!endif

# ----- tm params -----
# auto-discard branches from previous serial forking leg
modparam("tm", "failure_reply_mode", 3)
# default retransmission timeout: 10sec
modparam("tm", "fr_timer", 10000)
# default invite retransmission timeout after 1xx: 120sec
modparam("tm", "fr_inv_timer", 120000)

#!ifdef WITH_DEBUG
loadmodule "debugger.so"
modparam("debugger", "mod_hash_size", 5)
modparam("debugger", "mod_level_mode", 1)
modparam("debugger", "mod_level", "cdp=3")
modparam("debugger", "mod_level", "ims_icscf=3")
#!endif

# -------------------------  request routing logic -------------------
# main routing logic

route{
#!ifdef WITH_DEBUG
	xlog("I-CSCF >>>>>>>>>>>>>>>>>>>> $rm $ru ($fu => $tu ($si:$sp) to $tu, $ci)\n");
#!endif

	if !($rU =~ "\+.*") {
		prefix("+");
	}

	# per request initial checks
	route(REQINIT);

	if (is_method("REGISTER")) {
		route(register);
	}

	if (is_method("INVITE|SUBSCRIBE|MESSAGE|INFO|PUBLISH|CANCEL")) {
		route(initial_request);
	} else {
		# Shouldn't get here unless missconfigured (add more methods as initial) or
		# somebody is routing unknown messages
		append_to_reply("Allow: INVITE,SUBSCRIBE,MESSAGE,INFO,PUBLISH,CANCEL\r\n");
		send_reply("406", "Initial Request Method not allowed at the I-CSCF");
		break;
	}
}

######################################################################
# Helper routes (Basic-Checks, NAT-Handling/RTP-Control, XML-RPC)
######################################################################
# Per SIP request initial checks
route[REQINIT] {
	$var(used) = 1 - ($stat(free_size) / $stat(total_size));
	xlog("L_DBG", "Mem: Total $stat(total_size), Free $stat(free_size) [$var(used)% used]\n");
	if ($var(used) > 95) {
		send_reply("503", "Server overloaded");
		exit;
	}


	# Trace this message
#!ifdef CAPTURE_NODE
	sip_trace();	
	setflag(FLT_CAPTURE);
#!endif
	if (!mf_process_maxfwd_header("10")) {
		sl_send_reply("483", "Too Many Hops");
		exit;
	}
	if(!sanity_check("1511", "7")) {
		xlog("Malformed SIP message from $si:$sp\n");
		exit;
	}

	# Check for shutdown mode:
	if (!has_totag() && ($sel(cfg_get.system.shutdownmode) > 0)) {
		send_reply("503", "Server shutting down");
		exit;
	}

	# Reply to OPTIONS:
	if (is_method("OPTIONS") && (uri==myself)) {
		options_reply();
		exit;
	}	
	
	# Ignore Re-Transmits:
	if (t_lookup_request()) {
		exit;
	}

	if (is_method("INVITE|REGISTER")) {
		send_reply("100", "Trying");
	}
}

######################################################################
# XMLRPC routing
######################################################################
#!ifdef WITH_XMLRPC
route[XMLRPC] {
	if ((method=="POST" || method=="GET")
#!ifdef XMLRPC_WHITELIST_1
&& ((src_ip == XMLRPC_WHITELIST_1)
#!ifdef XMLRPC_WHITELIST_2
 || (src_ip == XMLRPC_WHITELIST_2)
#!endif
#!ifdef XMLRPC_WHITELIST_3
 || (src_ip == XMLRPC_WHITELIST_3)
#!endif
)
#!endif
) {
		# close connection only for xmlrpclib user agents (there is a bug in
		# xmlrpclib: it waits for EOF before interpreting the response).
		if ($hdr(User-Agent) =~ "xmlrpclib")
			set_reply_close();
		set_reply_no_connect();
		dispatch_rpc();
		exit;
	}
	send_reply("403", "Forbidden");
	exit;
}
#!endif

######################################################################
# Handling of REGISTER requests
######################################################################
route[register]
{
	t_set_fr(5000, 5000); 
	#first check if we have an S-CSCF list
	if (I_scscf_select("0")) {
		#there is an S-CSCF list - no need to do a UAR
		t_on_reply("register_reply");
		t_on_failure("register_failure");
		if (!t_relay()) {
			send_reply("500", "Error forwarding towards S-CSCF");
		}
       } else {
		#no S-CSCF list therefore must do UAR
		#free this from the failed I_scscf_select call
		I_scscf_drop();
		# Do an asynchronous UAR:
		#0=REG/DEREG; 1=REG+Capabilities
                if (!I_perform_user_authorization_request("REG_UAR_REPLY", "0")) {
			send_reply("500", "Error in Request");
		}
	}
	exit;
}

route[REG_UAR_REPLY]
{
    #xlog("$$avp(s:uaa_return_code) = $avp(s:uaa_return_code)\n");
    #this is async so to know status we have to check the reply avp
    switch ($avp(s:uaa_return_code)){
            case 1: #success
                    if (I_scscf_select("0")){
                            t_on_failure("register_failure");
                            t_on_reply("register_reply");
                            #now relay to appropriate SCSCF
                            if (!t_relay()) {
                                    t_reply("500", "Error forwarding to SCSCF");
                            }
                    } else {#select failed
                            I_scscf_drop();
                            t_reply("500", "Server error on SCSCF Select (UAR)");
                    }
                    break;
            case -1: #failure
                    xlog("L_ERR", "UAR failure - error response sent from module\n");
                    break;
            case -2: #error
                    xlog("L_ERR", "UAR error - sending error response now\n");
                    t_reply("500", "UAR failed");
                    break;
            default:
                    xlog("L_ERR", "Unknown return code from UAR, value is [$avp(s:uaa_return_code)]\n");
                    t_reply("500", "Unknown response code from UAR");
                    break;
    }
}


######################################################################
# Replies to REGISTER requests, 
######################################################################
onreply_route[register_reply]
{
	xlog("L_DBG", "Enter register reply block");
	if (!t_check_status("(408)|(480)")){
		if (!t_check_status("(401)")){
			xlog("L_DBG", "dropping scscf list on register failure");
			I_scscf_drop();
		} else {
			xlog("L_DBG", "This is a 401 - keep scscf list to do optimisation");
		}
	}
	break;
}

######################################################################
# Failed REGISTERs
######################################################################
failure_route[register_failure]
{
	if (t_branch_timeout() || t_check_status("([5-6][0-9][0-9])")){
		if (I_scscf_select("1")) {
			t_on_reply("register_reply");
			t_on_failure("register_failure");
			if (!t_relay()) {
				t_reply("500", "Error forwarding towards next S-CSCF");
				break;
			}
			break;
		} else {
			t_reply("500", "Server error on UAR select next S-CSCF");
			break;
		}
	} else {
		if (!t_check_status("(401)")){
			xlog("L_DBG", "dropping scscf list on register failure");
			I_scscf_drop();
		} else {
			xlog("L_DBG", "This is a 401 - keep scscf list to do optimisation");
		}    
		break;
	}
}

######################################################################
# Initial requests
######################################################################
route[initial_request]
{
	xlog("$$ru => $ru\n");
	I_perform_location_information_request("LIR_REPLY", "0");
}

route[LIR_REPLY] {
	if ($avp(lia_return_code) == 1) {
		if (I_scscf_select("0")) {
			xlog("L_DBG", "ru = $ru, du = $du\n");
			t_on_reply("initial_request_reply");
			t_on_failure("initial_request_failure");
			if (!t_relay()) {
				t_reply("500", "Error forwarding towards S-CSCF");
				break;
			}
			break;
		} else {
			xlog("L_DBG", "dropping scscf list on initial request");
			I_scscf_drop();
			t_reply("500", "Server error on LIR select S-CSCF");
			break;
		}
	} else {
		t_reply("500", "Server error on LIR");
		break;
	}
	break;
}

######################################################################
# Replies to initial requests
######################################################################
onreply_route[initial_request_reply]
{
	xlog("L_DBG", "Enter initial request request block");
	if (!t_check_status("(408)")){
		xlog("L_DBG", "dropping scscf list on initial request reply");
		I_scscf_drop();
	}
	break;
}

######################################################################
# Failed initial requests
######################################################################
failure_route[initial_request_failure]
{
	xlog("L_DBG", "Enter initial request failure block");
	if (t_check_status("(408)")){
		xlog("L_DBG", "Got a failure for initial request");
		if (I_scscf_select("1")) {
			t_on_reply("initial_request_reply");
			t_on_failure("initial_request_failure");
			if (!t_relay()) {
				t_reply("500", "Error forwarding towards next S-CSCF");
				break;
			}
			break;
		} else {
			t_reply("500", "Server error on LIR select next S-CSCF");
			break;
		}
	} else {
		xlog("L_DBG", "dropping scscf list on  initial request failure");
		I_scscf_drop();
	}
	break;
}

#!ifdef PEERING
######################################################################
# HSS returned "User-Unknown" on LIR request
######################################################################
route[lir_term_user_unknown]
{
	if (uri =~ "tel:.*") {
		# Let's check, if the number can be found in ENUM:
		if(!enum_query()) {
			# ENUM failed, send it to the PSTN-Gateway:
			route(PSTN);
			break;
		}

		# ENUM resolved to another domain
		if ($rd != NETWORKNAME) {
			t_on_reply("initial_request_reply");
			t_on_failure("initial_request_failure");
			if (!t_relay()) {
				t_reply("500", "Error forwarding to external domain");
				exit;
			};
			exit;
		} else {
			t_reply("604", "Does not exist anywhere - HSS User Unknown");
			exit;
		};
	} else {
		# we received a request for our domain (non-tel), but HSS said "User Unknown"
		if ($rd != NETWORKNAME) { 
			t_reply("604", "Does not exist anywhere - HSS User Unknown");
			exit;
		} else {
			# try to forward non-tel request to other domain
			t_on_reply("Initial_Request_reply");
			t_on_failure("Initial_Request_failure");
			if (!t_relay()) {
				t_reply("500", "Error forwarding to external domain");
				exit;
			};
			exit;
		};
	};
}

}

######################################################################
# Send calls to the PSTN-Gateways:
######################################################################
route[PSTN]
{
	t_on_failure("PSTN_failure");
	# Relay the request towards the PSTN-Gateway:
	if (!ds_select_dst("1", "4")) {
		send_reply("503", "Service not available");
		exit;
	}
	# Relay the request:
	if (!t_relay()) {
		send_reply("503", "Service not available");
		exit;
	};
	exit;
}

######################################################################
# manage failure routing cases, perform failover
######################################################################
failure_route[PSTN_failure] {
	# Choose another gateway, in case we
	# - get a local generated "408"
	# - receive a 5xx or 6xx reply from the proxy.
	if (t_branch_timeout() || t_check_status("[5-6]..")) {
		if (ds_next_dst()) {
			# Do Failover in case problems:		
			t_on_failure("PSTN_failure");
			# Relay the request:
			if (!t_relay()) {
				send_reply("503", "Service not available");
				exit;
			};
		} else {
			# Add a header, to indicate the phone should try again in 30 seconds.
			append_hf("Retry-After: 30\r\n");
			send_reply("503", "Service not available");
		}
		exit;
	}
}
#!endif

#!ifdef FALLBACK_AUTH
######################################################################
# HSS returned "User-Unknown" on UAR request,
# try to send it to any S-CSCF for authentication
######################################################################
route[uar_term_user_unknown]
{
	$rd = "scscf."+NETWORKNAME;
	t_on_reply("register_reply");
	t_on_failure("register_failure");
	if (!t_relay()) {
		t_reply("500", "Error forwarding towards S-CSCF");
		break;
	}
	break;	
}
#!endif
