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

system.shutdownmode = 0 desc "System shutdown mode"

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

/* uncomment and configure the following line if you want Kamailio to 
   bind on a specific interface/port/proto (default bind on all available) */
#!ifdef NETWORK_INTERFACE
listen=NETWORK_INTERFACE
#!endif

/* port to listen to
 * - can be specified more than once if needed to listen on many ports */
port=PORT
alias=HOSTNAME
alias=NETWORKNAME

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

#!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 "corex"

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

# Load the according DB-Module:
loadmodule DB_MODULE

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 ---------------
# ----- mi_fifo params -----
modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo")
modparam("mi_fifo", "fifo_mode", 0666)
modparam("mi_fifo", "fifo_user", "kamailio")
modparam("mi_fifo", "fifo_group", "kamailio")

# -- 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.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:
modparam("ims_icscf","db_url", DB_URL)
modparam("ims_icscf","cxdx_dest_realm", NETWORKNAME)
#!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

#!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

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

route{
	xlog("L_DBG", "$rm ($fu ($si:$sp) to $tu, $ci)\n");

	# per request initial checks
	route(REQINIT);

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

	if (is_method("INVITE|SUBSCRIBE|MESSAGE|INFO|PUBLISH|CANCEL")) {
		route(initial_request);
		break;
	} 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");
		t_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] {
	# 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;
	}
}

######################################################################
# 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]
{
	#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()) {
			t_reply("500","Error forwarding towards S-CSCF");
			break;
		}
		break;
       } 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:
                I_perform_user_authorization_request("REG_UAR_REPLY","0"); #0=REG/DEREG; 1=REG+Capabilities
                exit;    
	}
	break;
}

route[REG_UAR_REPLY]
{
    #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]
{
	xlog("L_DBG", "Enter register failure block");
	if (t_check_status("(408)|(480)")){
		xlog("L_DBG", "Got a failure on register");
		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]
{
	I_perform_location_information_request("LIR_REPLY", "0");
}

route[LIR_REPLY] {
	if ($avp(lia_return_code) == 1) {
		if (I_scscf_select("0")) {
			append_branch();
			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
