#
# $Id$
#
# iptel.org real world configuration
#

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

debug=3
fork=yes
port=5060
log_stderror=no
memlog=3

# uncomment to override config values for test 
/*
debug=3             # debug level (cmd line: -ddd)
fork=no
port=5068
log_stderror=yes	# (cmd line: -E)
*/

check_via=yes       # (cmd. line: -v)
dns=no              # (cmd. line: -r)
rev_dns=no          # (cmd. line: -R)
children=4
fifo="/tmp/ser_fifo"
# 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 ----------------------------------

loadmodule "../new_ser/modules/sl/sl.so"
loadmodule "../new_ser/modules/tm/tm.so"
loadmodule "../new_ser/modules/acc/acc.so"
loadmodule "../new_ser/modules/rr/rr.so"
loadmodule "../new_ser/modules/maxfwd/maxfwd.so"
loadmodule "../new_ser/modules/mysql/mysql.so"
loadmodule "../new_ser/modules/usrloc/usrloc.so"
loadmodule "../new_ser/modules/registrar/registrar.so"
loadmodule "../new_ser/modules/auth/auth.so"
loadmodule "../new_ser/modules/textops/textops.so"

# ----------------- setting module-specific parameters ---------------

# -- usrloc params --

/* 0 -- dont use mysql, 1 -- write_through, 2--write_back */
modparam("usrloc", "db_mode",   2)
modparam("usrloc", "timer_interval", 10)
modparam("usrloc", "db_url","sql://ser:heslo@dbhost/ser")

# -- auth params --

#modparam("auth", "calculate_ha1", yes)

modparam("auth", "db_url","sql://ser:heslo@dbhost/ser")
modparam("auth", "user_column",   "user_id")
# nonce generation secret; particularly useful if multiple servers
# in a proxy farm are configured to authenticate
#modparam("auth", "secret", "43sjkl33T9kjj_5jvlA384hg")

modparam("auth", "nonce_expire",  300)
modparam("auth", "retry_count",   5)

# -- acc params --
# report ACKs too for sake of completeness -- as we account PSTN
# destinations which are RR, ACKs should show up
modparam("acc", "report_ack", 1)
modparam("acc", "log_level", 1)
# that is the flag for which we will account -- don't forget to
# set the same one :-)
/* Usage of flags is as follows: 1==should account(all to gateway), 
   2==should rr (all to gateway, MESSAGES, etc), 3==should report 
   on missed calls (transactions to iptel.org's users), 4==destination
   user wishes to use voicemail
*/
modparam("acc", "acc_flag", 1 )
modparam("acc", "missed_flag", 3 )

# -- tm params --
modparam("tm", "fr_timer", 20 )
modparam("tm", "fr_inv_timer", 90 )
modparam("tm", "wt_timer", 20 )
modparam("tm", "uac_from", "sip:daemon@iptel.org" )

# -------------------------  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","Too Many Hops");
		break;
	};
	if (len_gt( max_len )) {
		sl_send_reply("513", "Wow -- Message too large");
		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:")) {
			log("LOG: Someone trying to register from private IP again\n");
			sl_send_reply("479", "We dont accept private IP contacts" );
			break;
		};
	};

	/* ********* RR ********************************** */

	/* Do strict routing if route headers present */
	rewriteFromRoute();

	/* ********* DIVERSION ********************************** */
	/* apply all diversions before we proceed with processing of
	   requests for us
	*/


	/* IM gateway diversions */
	if (uri=~"sip:.*@icq\.iptel\.org"
			| uri=~"sip:.*@msn\.iptel\.org"
			| uri=~"sip:.*@aim\.iptel\.org"
			| uri=~"sip:.*@yahoo\.iptel\.org" ) {
		append_hf("P-hint: IMGW\r\n");
		if (!t_relay_to("195.37.77.100", "5070")) {
			sl_reply_error();
		};
		break;
	};

	/* divert voicemail requests */
	if (uri=~"mail\.iptel\.org" | uri=~":5066"| uri=~":6060") {
		sethost("iptel.org");
		append_hf("P-hint: VOICEMAIL\r\n");
		if ( !t_relay_to("fox.iptel.org", "6060")) {
			sl_reply_error();
		};
		break;
	};

	# this host is excempted from default processing for our domain
	# for testing purposes
	if (uri=~"@bat\.iptel\.org") {
		append_hf("P-hint: BATTEST\r\n");
		forward("bat.iptel.org", 5060);
		break;
	};

	/* ********* RR ********************************** */

	# look at whether we need record-routing; 
	# - we need it for calls  from gateways (otherwise, subsequent 
	#   requests from the other # party will attempt to contact gateway 
	#  	directly through blocked ports)
	# - we need it for Windows Messanger's IM sessions to cross
	#   some firewalls -- we force all MESSAGEs to go via our server
	#   to avoid blocking port numbers (some firewalls can do
	#   standard SIP but are puzzled by Microsoft's use of obsoleted
	#   IM session model)
	# - some other places may decide to set the record-routing
	#   flag (2 chosen) too; particularly, INVITEs to our gw
	if ( (src_ip==195.37.77.110 & method=="INVITE") || method=="MESSAGE" || method=="INFO" )  {
		setflag(2);
	};


	/*  *********  check for requests targeted out of our domain... ******* */
	# sign of our domain: there is @ (username), :
    # (nothing) or . (host) in front of our domain name	;
	# if none of these cases matches, proceed with proessing of
	# outbound requests in route[2]
	if (!(uri=~"[@:\.]iptel\.org([;:].*)*" 
			# ... some phones put IP address in URI instead ...
			| uri=~"[@:\.]195\.37\.77\.101([;:].*)*" 
			# ... and we serve our gateway too (we RR requests to it, so that
			# its address may show up in subsequent requests after
			# rewriteFromRoute
			| uri=~"@195\.37\.77\.110([;:].*)*" )) {
		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): .*@(195\.37\.77\.101|iptel\.org)")) {
			log(1, "LOG: alert: someone trying to set aor==contact\n");
			sl_send_reply("476", "No Server Address in Contacts Allowed" );
			break;
		};

		# prohibit attempts to grab someone else's To address 
		# using  valid credentials; the only exception is the user
		# 'replciator' permitted to generate 3-rd party registrations

		if (!www_authorize(	"iptel.org" /* realm */, 
			 				"subscriber" /* table name */ )) {
			# challenge if none or invalid credentials
 			www_challenge(	"iptel.org" /* realm */, 
							"0" /* no qop -- some phones can't deal with it */);
			break;
		};

		if (!is_user("replicator") & !check_to()) {
			log("LOG: To Cheating attempt\n");
			sl_send_reply("403", "That is ugly -- use To=id next time");
			break;
		};
		# it is an authenticated request, update Contact database now
		if (!save("location")) {
			sl_reply_error();
		};
		/* XXX not tested yet
		t_replicate("bat.iptel.org", "5060");
		*/
		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;
	};

	# various aliases (might use a database in future)
	lookup("aliases");
	# check again, if it is still for our domain after aliases
	# we have to include '.iptel.org' not to proceed to outbound
	# authentication for calls to mail.iptel.org and other hosts
	# served by us
	if ( !(uri=~"[@:\.]iptel\.org([;:].*)*" | 
		uri=~"[@:\.]195\.37\.77\.101([;:].*)*" |
		uri=~"@195\.37\.77\.110([;:].*)*" )) {
		route(5);
		break;
	};

	# 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;
	}; 

	# does the user wish redirection on no availability? (i.e., is he
	# in the voicemail group?) -- determine it now and store it in
	# flag 4, before we rewrite the flag using UsrLoc
	if (is_user_in("Request-URI", "voicemail")) {
		setflag(4);
	} ;

	# 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
	if (uri=~"@195\.37\.77\.110([;:].*)*" ) {
		log(1, "LOG: Weird! Gateway address in UsrLoc!\n");
		route(3);
		break;
	};

	# if user is on-line and is in voicemail group, enable redirection
	if (method=="INVITE" && isflagset(4)) {
		t_on_negative("1");
	};

	/* ... and also report on missed calls ... note that reporting
       on missed calls is mutually exclusive with silent C timer
    */
	setflag(3);



	# add RR to messages which were previously labeled for that
	if (isflagset(2)) {
		addRecordRoute();
	};

	# 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
# (beware, 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] {
	# outbound requests are allowed only for our users -- we don't
	# support relaying and don't like strangers bothering us
	# with resolving DNS; except our gateway
	if (!(src_ip==195.37.77.110) & 
		!(proxy_authorize(	"iptel.org" /* realm */,
						"subscriber" /* table name */ ))) {
		# ACK/CANCEL have inherently no security -- just log if
		# included credentials are wrong and proceed
		if (method=="ACK" ) {
			log("LOG: failed outbound authentication for ACK granted\n");
		} else if (method=="CANCEL") {
			log("LOG: failed outbound authentication for CANCEL granted\n");
		} else {
			proxy_challenge("iptel.org" /* realm */, "0" /* no-qop */);
			break;
		};
	};
	# to maintain credibility of our proxy, we check From in INVITEs to be
	# equal to credential id  so that user john.doe does not put bill.gates
	# in his From; we don't do that for other requests: within a dialogue,
	# subsequent transactions coming from the other side will have in From
	# To of the original transaction to match the dialog; it may be 
	# however different from user's default From and digest; example:
	# user_a sends INVITE to secretary; secretary is translated to user_b;
	# user_b accepts and later BYEs; to match the dialog, it puts
	# secretary in From and sends user_b's credentials -- check_from
	# would fail
	if (!src_ip==195.37.77.110 & method=="INVITE" & !check_from()) {
		log("LOG: From Cheating attempt\n");
		sl_send_reply("403", "That is ugly -- use From=id next time (OB)");
		break;
	};

	if (isflagset(2)) {
		addRecordRoute();
		;
	};
	append_hf("P-hint: OUTBOUND\r\n");
	if (!t_relay()) {
		sl_reply_error();
		break;
	};
}

#------- 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");
	if (!t_relay()) {
		sl_reply_error();
		break;
	};
}

#----------------- PSTN ----------------------------------------------

# logic for calls to the PSTN
route[3] {
	# if it is a MESSAGE pass it "as is" over to our SMS gateway
	# (which unfortunately lives at a different host due to
	# lack of serial interfaces)
	if (method=="MESSAGE") {
		# set accounting
		setflag(1);
		rewritehostport("195.37.77.100:5070");
		append_hf("P-hint: SMS\r\n");
		if (!t_relay()) {
			sl_reply_error(); 
		};
		break;
	};
	# continue with requests to PSTN gateway ...

	# the international + prefix
	if (uri=~"sip:\+" ) {
		strip(1);
		prefix("000");
	};
	# free call destinations ... no authentication needed
	if ( is_user_in("Request-URI", "free-pstn")  /* free destinations */
			|  uri=~"sip:[79][0-9][0-9][0-9]@.*"  /* local PBX */
			| uri=~"sip:98[0-9][0-9][0-9][0-9]") {
		# just log it, no authentication
		setflag(1);
	} else {
		# all other PSTN destinations only for authenticated users
		# (Cisco GW, which has no digest support, is authenticated
		# by its IP address -- that's for sure not very strong;
		# wth confirmed that we filter packets coming from outside
		# and bearing SRC IP address of our network)
		# we are forgiving about ACK/CANCEL as digest provides no
		# real security for them
		if (!(src_ip==195.37.77.110 | method==ACK | method=="CANCEL" )) {
			if (!proxy_authorize(	"iptel.org" /* realm */,
								"subscriber" /* table name */))  {
				proxy_challenge( "iptel.org" /* realm */, "0" /* no qop */ );
				break;
			# let's check from=id ... avoids accounting confusion
			} else 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;
			};
		};

		# authorize only for INVITEs -- RR/Contact may result in weird
		# things showing up in d-uri that would break our logic; our
		# major concern is INVITE which causes PSTN costs anyway

		if (method=="INVITE") {

			# does the authenticated user have a permission for local
			# calls? (i.e., is he in the "local" group?)
			if (uri=~"sip:0[1-9][0-9]+@.*") {
				if (!is_in_group("local")) {
					sl_send_reply("403", "Local Toodle Noodle...");
					break;
				};
			# the same for long-distance
			} else if (uri=~"sip:00[1-9][0-9]+@.*") {
				if (uri=~"sip:001[089]" | uri=~"sip:00900.*" ) {
					sl_send_reply("403", "Added Value Destinations not permitted...");
					break;
				};
				if (!is_in_group("ld")) {
					sl_send_reply("403", "LD Toodle Noodle...");
					break;
				};
			# the same for international calls
			} else if (uri=~"sip:000[1-9][0-9]+@.*") {
				if (!is_in_group("int")) {
					sl_send_reply("403", "International Toodle Noodle...");
					break;
				};
			# everything else (e.g., interplanetary calls) is denied
			} else {
				sl_send_reply("403", "interplanetary Toodle Noodle...");
				break;
			};

		}; # INVITE to authorized PSTN

		# we passed all authorization checks for PSTN -- move on!
		# tag this transaction for accounting
		setflag(1);

	}; # authorized PSTN

	# requests to gateway must be record-routed because the GW accepts
	# only reqeusts coming from our proxy
	if (isflagset(2) || method=="INVITE")
		addRecordRoute();

	# if you have passed through all the checks, let your call go to GW!

	rewritehostport("195.37.77.110:5060");

	append_hf("P-hint: GATEWAY\r\n");
	if (!t_relay()) {
		sl_reply_error(); 
		break; 
	};

}


/* *********** handling of unavailable user ******************* */

/* handling of users who are off-line */
route[4] {
	# user not found -- act as stateful UAS to avoid reporting
	# on each INVITE retranmission
	if (method=="INVITE" || method=="ACK" 
			|| method=="BYE" || method=="CANCEL" ) {

		/* requests to voicemail users will be fwded to voicemail */
		if (isflagset(4)) {
			rewritehostport("mail.iptel.org:6060");
			append_hf("P-hint: OFFLINE-VOICEMAIL\r\n");
			if (!t_relay()) {
				sl_reply_error();
			};
		} else { /* non-voicemail users get 404 */
			if (t_newtran()) {
				if (method=="ACK") {
					log("oops -- ACK to a non-existent transaction");
					drop;
				};
				# we reply statefuly to avoid accounting of all
				# retransmissions
				if (!t_reply("404", "Not Found")) {
					sl_reply_error();
				};
			} else {
				sl_reply_error();
			};
		};
		/* we account missed calls for all off-line users */
		if (method=="INVITE") acc_request("404 Not Found");
		break;
	};
	# non-VoIP messages: just return 404 statelessly
	sl_send_reply("404", "Not Found");
}

# initiate serial forking to voicemail for users who registered for it
# (those users will be accounted for missed calls too since the 
# accounting flag #3 is set)
# note: accounting relates to the status immediately after a final 
# negative reply came back, i.e., the order of execution is
# 1) account failed transaction 2) set up serial forking to voicemail
reply_route[1] {
	/* XX: note: unsafe if preloaded routes without username used */
	revert_uri();
	rewritehostport("mail.iptel.org:6060");
	append_branch();
}