Browse Source

Merge remote branch 'origin/carstenbock/rtpproxy2'

* origin/carstenbock/rtpproxy2:
  - Updated the patch to the latest changes (see previous commit)
  Add a parameter for the type of the RTP-Timeout-Socket (defaults to 1 for Kamailio-XMLRPC)
  Added the changes to the RTP-Proxy as a patch, for easier review.
  - fixed identation
  The Timeout socket must be sent with Update after session confirmation.
  Tiny typo.
  - added support for the retrieval of RTP-Statistics through $rtpstat
Carsten Bock 15 years ago
parent
commit
68ed88ef4e

+ 67 - 1
modules_k/rtpproxy/doc/rtpproxy_admin.xml

@@ -231,8 +231,56 @@ modparam("rtpproxy", "nortpproxy_str", "a=sdpmangled:yes\r\n")
 </programlisting>
 		</example>
 	</section>
+	<section>
+		<title><varname>timeout_socket</varname> (string)</title>
+		<para>
+		The parameter sets timeout socket, which is transmitted to the RTP-Proxy.
+		</para>
+		<para>
+		If it is an empty string, no timeout socket will be transmitted to the RTP-Proxy.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote></quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>timeout_socket</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("nathelper", "timeout_socket", "http://127.0.0.1:8000/RPC2")
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>timeout_socket_type</varname> (int)</title>
+		<para>
+		The parameter sets type of the timeout socket, which is transmitted to the RTP-Proxy.
+		</para>
+		<para>
+		If it is not set, type 1 (Kamailio XML-RPC-Socket) is transmitted to the RTP-Proxy.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>1</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>timeout_socket_type</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("nathelper", "timeout_socket_type", 42)
+...
+</programlisting>
+		</example>
+		<para>
+		<emphasis>
+			The only supported Type on the RTP-Proxy is currently <quote>1</quote> or <quote>0</quote> which is the default socket-type of the RTP-Proxy which is not compatible to Kamailio.
+		</emphasis>
+		</para>
+	</section>
 	</section>
-
 
 	<section>
 	<title>Exported Functions</title>
@@ -589,6 +637,24 @@ start_recording();
 
 	<section>
 		<title>Exported Pseudo Variables</title>
+		<section>
+			<title><function moreinfo="none">$rtpstart</function></title>
+			<para>
+			Returns the RTP-Statistics from the RTP-Proxy. The RTP-Statistics from the RTP-Proxy
+			are provided as a string and it does contain several packet-counters. The statistics
+			must be retrieved before the session is deleted	(before unforce_rtpproxy).
+			</para>
+
+		<example>
+		<title>$rtpstat-Usage</title>
+		<programlisting format="linespecific">
+...
+    append_hf("X-RTP-Statistics: $rtpstat\r\n");
+...
+		</programlisting>
+		</example>
+	        </section>
+
 	</section>
 
 	<section>

+ 105 - 11
modules_k/rtpproxy/rtpproxy.c

@@ -306,6 +306,9 @@ static int mod_init(void);
 static int child_init(int);
 static void mod_destroy(void);
 
+/* Pseudo-Variables */
+static int pv_get_rtpstat_f(struct sip_msg *, pv_param_t *, pv_value_t *);
+
 /*mi commands*/
 static struct mi_root* mi_enable_rtp_proxy(struct mi_root* cmd_tree,
 		void* param );
@@ -337,6 +340,9 @@ static int *rtpp_socks = 0;
 /*0-> disabled, 1 ->enabled*/
 unsigned int *natping_state=0;
 
+static str timeout_socket_str = {0, 0};
+static int timeout_socket_type = 1;
+
 static cmd_export_t cmds[] = {
 	{"set_rtp_proxy_set",  (cmd_function)set_rtp_proxy_set_f,    1,
 		fixup_set_id, 0,
@@ -390,6 +396,8 @@ static cmd_export_t cmds[] = {
 };
 
 static pv_export_t mod_pvs[] = {
+    {{"rtpstat", (sizeof("rtpstat")-1)}, /* RTP-Statistics */
+     PVT_OTHER, pv_get_rtpstat_f, 0, 0, 0, 0, 0},
     {{0, 0}, 0, 0, 0, 0, 0, 0, 0}
 };
 
@@ -400,6 +408,8 @@ static param_export_t params[] = {
 	{"rtpproxy_disable_tout", INT_PARAM, &rtpproxy_disable_tout },
 	{"rtpproxy_retr",         INT_PARAM, &rtpproxy_retr         },
 	{"rtpproxy_tout",         INT_PARAM, &rtpproxy_tout         },
+	{"timeout_socket",    	  STR_PARAM, &timeout_socket_str.s  },
+	{"timeout_socket_type",   INT_PARAM, &timeout_socket_type   },
 	{0, 0, 0}
 };
 
@@ -854,9 +864,17 @@ mod_init(void)
 		if(rtpp_strings[i])
 			pkg_free(rtpp_strings[i]);
 	}
+	if (timeout_socket_str.s==NULL || timeout_socket_str.s[0]==0) {
+		timeout_socket_str.len = 0;
+		timeout_socket_str.s = NULL;
+	} else {
+		timeout_socket_str.len = strlen(timeout_socket_str.s);
+	}
+
 	if (rtpp_strings)
 		pkg_free(rtpp_strings);
 
+
 	return 0;
 }
 
@@ -1798,12 +1816,18 @@ force_rtp_proxy(struct sip_msg* msg, char* str1, char* str2, int offer)
 		{" ", 1},	/* separator */
 		{NULL, 0},	/* to_tag */
 		{";", 1},	/* separator */
-		{NULL, 0}	/* medianum */
+		{NULL, 0},	/* medianum */
+		{" ", 1},	/* separator */
+		{NULL, 0},	/* Type of timeout-socket: 1 Kamailio-XML-RPC */
+		{" ", 1},	/* separator */
+		{NULL, 0},	/* Timeout-Socket */
 	};
+	int iovec_param_count;
+
 	char *c1p, *c2p, *bodylimit, *o1p;
-	char medianum_buf[20];
+	char itoabuf_buf[20];
 	int medianum, media_multi;
-	str medianum_str;
+	str itoabuf_str;
 	int c1p_altered;
 	static int swap_warned = 0;
 
@@ -2082,14 +2106,14 @@ force_rtp_proxy(struct sip_msg* msg, char* str1, char* str2, int offer)
 #endif
 			if (1 || media_multi) /* XXX netch: can't choose now*/
 			{
-				snprintf(medianum_buf, sizeof medianum_buf, "%d", medianum);
-				medianum_str.s = medianum_buf;
-				medianum_str.len = strlen(medianum_buf);
-				STR2IOVEC(medianum_str, v[13]);
-				STR2IOVEC(medianum_str, v[17]);
+				snprintf(itoabuf_buf, sizeof itoabuf_buf, "%d", medianum);
+				itoabuf_str.s = itoabuf_buf;
+				itoabuf_str.len = strlen(itoabuf_buf);
+				STR2IOVEC(itoabuf_str, v[13]);
+				STR2IOVEC(itoabuf_str, v[17]);
 #ifdef EXTRA_DEBUG
-				LM_DBG("STR2IOVEC(medianum_str, v[13])\n");
-				LM_DBG("STR2IOVEC(medianum_str, v[17])\n");
+				LM_DBG("STR2IOVEC(itoabuf_str, v[13])\n");
+				LM_DBG("STR2IOVEC(itoabuf_str, v[17])\n");
 #endif
 			} else {
 				v[12].iov_len = v[13].iov_len = 0;
@@ -2151,7 +2175,21 @@ force_rtp_proxy(struct sip_msg* msg, char* str1, char* str2, int offer)
 				} else {
 					v[3].iov_len = 0;
 				}
-				cp = send_rtpp_command(node, v, (to_tag.len > 0) ? 18 : 14);
+				if (to_tag.len > 0) {
+					iovec_param_count = 18;
+					if (timeout_socket_str.len > 0) {
+						iovec_param_count = 22;
+						snprintf(itoabuf_buf, sizeof itoabuf_buf, "%d", timeout_socket_type);
+						itoabuf_str.s = itoabuf_buf;
+						itoabuf_str.len = strlen(itoabuf_buf);
+						STR2IOVEC(itoabuf_str, v[19]);
+						STR2IOVEC(timeout_socket_str, v[21]);
+					}
+				} else {
+					iovec_param_count = 14;
+				}
+
+				cp = send_rtpp_command(node, v, iovec_param_count);
 			} while (cp == NULL);
 			LM_DBG("proxy reply: %s\n", cp);
 			/* Parse proxy reply to <argc,argv> */
@@ -2389,3 +2427,59 @@ static int start_recording_f(struct sip_msg* msg, char *foo, char *bar)
 	return 1;
 }
 
+/*
+ * Returns the current RTP-Statistics from the RTP-Proxy
+ */
+static int
+pv_get_rtpstat_f(struct sip_msg *msg, pv_param_t *param,
+		  pv_value_t *res)
+{
+    str ret_val = {0, 0};
+    int nitems;
+    str callid = {0, 0};
+    str from_tag = {0, 0};
+    str to_tag = {0, 0};
+    struct rtpp_node *node;
+    struct iovec v[1 + 4 + 3 + 1] = {{NULL, 0}, {"Q", 1}, {" ", 1}, {NULL, 0}, {" ", 1}, {NULL, 0}, {";1 ", 3}, {";1", }, {NULL, 0}};
+
+    if (get_callid(msg, &callid) == -1 || callid.len == 0) {
+        LM_ERR("can't get Call-Id field\n");
+        return -1;
+    }
+    if (get_to_tag(msg, &to_tag) == -1) {
+        LM_ERR("can't get To tag\n");
+        return -1;
+    }
+    if (get_from_tag(msg, &from_tag) == -1 || from_tag.len == 0) {
+        LM_ERR("can't get From tag\n");
+        return -1;
+    }
+    if(msg->id != current_msg_id){
+        selected_rtpp_set = default_rtpp_set;
+    }
+
+    STR2IOVEC(callid, v[3]);
+    STR2IOVEC(from_tag, v[5]);
+    STR2IOVEC(to_tag, v[7]);
+    node = select_rtpp_node(callid, 1);
+    if (!node) {
+        LM_ERR("no available proxies\n");
+        return -1;
+    }
+    nitems = 8;
+    if (msg->first_line.type == SIP_REPLY) {
+        if (to_tag.len == 0)
+            return -1;
+        STR2IOVEC(to_tag, v[5]);
+        STR2IOVEC(from_tag, v[7]);
+    } else {
+        STR2IOVEC(from_tag, v[5]);
+        STR2IOVEC(to_tag, v[7]);
+        if (to_tag.len <= 0)
+            nitems = 6;
+    }
+    ret_val.s = send_rtpp_command(node, v, nitems);
+    ret_val.len = strlen(ret_val.s);
+    return pv_get_strval(msg, param, res, &ret_val);
+}
+

+ 3 - 0
modules_k/rtpproxy/test/exec_pjsua.txt

@@ -0,0 +1,3 @@
+./pjsua-i686-pc-linux-gnu --registrar=sip:localhost --id=sip:1@localhost --realm=localhost --username=1 --password=none --local-port=15060 --null-audio --auto-answer=200
+
+./pjsua-i686-pc-linux-gnu --registrar=sip:localhost --id=sip:2@localhost --realm=localhost --username=2 --password=none --local-port=15061 --null-audio sip:1@localhost

+ 1 - 0
modules_k/rtpproxy/test/exec_rtpproxy.txt

@@ -0,0 +1 @@
+./rtpproxy -T 10 -f -F -i -l 195.71.4.179 -s udp:*:22222 -d DBUG

+ 359 - 0
modules_k/rtpproxy/test/kamailio.cfg

@@ -0,0 +1,359 @@
+#!KAMAILIO
+
+# ----------------- Error-Logging ---------------
+debug=2
+log_stderror=no
+#configure the /etc/syslog.conf for this feature
+#log_facility=LOG_LOCAL1
+
+# ----------------- Basic-Settings ---------------
+fork=yes
+children=2
+check_via=no
+dns=no
+rev_dns=no
+listen=127.0.0.1
+
+port=5060
+
+sip_warning=0
+
+############################
+# TCP-Support
+############################
+disable_tcp=yes
+
+disable_core_dump=yes
+auto_aliases=no
+tos=0x80
+
+# ------------------ module loading ----------------------------------
+mpath="/usr/local/lib/kamailio/modules_k/:/usr/local/lib/kamailio/modules/"
+# To Check/modufy the maximum forwards
+loadmodule "maxfwd.so"
+# Transaction-Module
+loadmodule "tm.so"
+# For Stateless replies
+loadmodule "sl.so"
+# Extended Logging
+loadmodule "xlog.so"
+# Record-/Loose-Routing-Module (RFC3261)
+loadmodule "rr.so"
+# NAT-Traversal
+loadmodule "nathelper.so"
+loadmodule "rtpproxy.so"
+# Generic Utilities (formerly options.so and uri.so)
+loadmodule "siputils.so"
+# MI-FIFO-Wrapper
+loadmodule "mi_fifo.so"
+# MI-XML-RPC-Wrapper
+loadmodule "mi_rpc.so"
+loadmodule "mi_xmlrpc.so"
+
+# Module for Pseudo-Variables
+loadmodule "pv.so"
+# Module for Dialog 
+loadmodule "dialog.so"
+# Sanity: Check if request is ok by syntax
+loadmodule "sanity.so"
+# Textops provides the method "is_method"
+loadmodule "textops.so"
+# UAC provides the method "uac_replace_from"
+loadmodule "uac.so"
+loadmodule "kex.so"
+loadmodule "tmx.so"
+loadmodule "usrloc.so"
+loadmodule "registrar.so"
+
+# ----------------- Settings for Nathelper ---------------
+# Nathelper: RTP-Proxy-List
+modparam("rtpproxy", "rtpproxy_sock", "udp:localhost:22222")
+# Nathelper: RTP-Proxy Timeout
+modparam("rtpproxy", "rtpproxy_tout", 3)
+modparam("rtpproxy", "timeout_socket", "http://localhost:8000/RPC2")
+
+# ----------------- Settings for max-fwd ---------------
+# Max-Forward-Module: Set the maximum to 20
+modparam("maxfwd", "max_limit", 20)
+
+# ----------------- Settings for rr ---------------
+# RR Module: Enable the From-Tag in RR-Header
+modparam("rr", "append_fromtag", 1)
+
+# ----------------- Settings for mi_fifo-Wrapper ---------------
+modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo")
+
+# ----------------- Settings for mi_XML_RPC ---------------
+# The Port for incoming XML-RPC requests
+modparam("mi_xmlrpc", "port", 8000)
+# Method-Response: A single string parameter should be replied.
+# See: http://www.openser.org/docs/modules/1.2.x/mi_xmlrpc.html#AEN102
+modparam("mi_xmlrpc", "reply_option", 1)
+# The size of the Buffer used to create the XML-RPC-Request-Node
+modparam("mi_xmlrpc", "buffer_size",  8192)
+# The logfile for the xml-rpc server 
+# #modparam("mi_xmlrpc", "log_file", "/tmp/openser_xmlrpc")
+
+# ----------------- Settings for Dialog-Module ---------------
+# The Name of the Tag in the RR-Route-Header (default "did")
+modparam("dialog", "rr_param", "rtp")
+# Flag to be used for marking if a dialog should be constructed for the current request (make sense only for initial requests).
+modparam("dialog", "dlg_flag", 1)
+# The default dialog timeout (in seconds) if no custom one is set. Default value is "43200 (12 hours)".
+modparam("dialog", "default_timeout", 7230)
+
+# How the seqential requests should be matched against the known dialogs.
+# The modes are a combination between matching based on a cookie (DID) stored as cookie in Record-Route header and the matching based on SIP elements (as in RFC3261).
+# The supported modes are:
+# 0 - DID_ONLY - the match is done exclusivly based on DID (default);
+# 1 - DID_FALLBACK - the match is first tried based on DID and if not present, it will fallback to SIP matching;
+# 2 - DID_NONE - the match is done exclusivly based on SIP elements; no DID information is added in RR.
+modparam("dialog", "dlg_match_mode", 1)
+# Describe how to push into the DB the dialogs' information from memory.
+# The supported modes are:
+# 0 - NO_DB - the memory content is not flushed into DB;
+# 1 - REALTIME - any dialog information changes will be reflected into the database immediatly.
+# 2 - DELAYED - the dialog information changes will be flushed into DB periodically, based on a timre routine.
+modparam("dialog", "db_mode", 0)
+# The interval (seconds) at which to update dialogs' information if you chose to store the dialogs' info at a given interval.
+# A too short interval will generate intensiv database operations, a too large one will not notice short dialogs.
+# Default value is "60".
+# modparam("dialog", "db_update_period", 100)
+# The number of dialogs to be fetched per loop
+#modparam("dialog", "db_fetch_rows", 120)
+
+modparam("registrar", "max_expires", 60);
+modparam("registrar", "received_avp", "$avp(i:100)");
+modparam("nathelper", "received_avp", "$avp(i:100)");
+
+route {
+	if (!is_method("REGISTER"))
+		xlog("L_ERR", "$rm from $fU to $rU ($ci)\n");
+	route(1);
+	
+	##################################################################################################################
+	# Subsequential requests
+	###############################################################################################################
+	if (has_totag() && !is_method("REGISTER")) {
+		route(2);
+	}
+
+	##################################################################################################################
+	# Record-Route, damit alle Nachrichten ueber diesen SIP-Proxy laufen
+	###############################################################################################################
+	if (!is_method("REGISTER|MESSAGE")) record_route();
+
+	if (loose_route()) {
+		# This is not from a proxy, decline the request. This is not allowed.
+		log(1, "Preloaded Route, not to ourselfs\n");
+		if (!is_method("ACK")) send_reply("403", "Preload Route denied");
+		exit;
+	}
+
+	##################################################################################################################
+	# BYE and ACK Requests should always have an To-Tag, if they follow RFC
+	###############################################################################################################
+	if (is_method("BYE")) {
+		send_reply("400", "Missing to-tag");
+		exit;
+	}
+	if (is_method("ACK")) {
+		exit;		
+	}
+
+	##################################################################################################################
+	# We only allow INVITE's towards the Class IV, no other methods allowed:
+	# RFC: 21.4.6 405 Method Not Allowed
+	# The method specified in the Request-Line is understood, but not allowed for the address identified by the Request-URI.
+	###############################################################################################################
+	if (is_method("REGISTER")) {
+		fix_nated_register();
+		save("location");
+		exit;
+	}
+
+
+	##################################################################################################################
+	# We only allow INVITE's towards the Class IV, no other methods allowed:
+	# RFC: 21.4.6 405 Method Not Allowed
+	# The method specified in the Request-Line is understood, but not allowed for the address identified by the Request-URI.
+	###############################################################################################################
+	if (!is_method("INVITE")) {
+		send_reply("405", "Method Not Allowed");
+		exit;
+	}
+	fix_nated_contact();
+
+	if (!lookup("location")) {
+		send_reply("404", "Not found");
+		exit;
+	}
+
+	##################################################################################################################
+	# The dialog module should be aware of this dialog:
+	###############################################################################################################
+	setflag(1);
+
+	##################################################################################################################
+	# Use RTP-Proxy
+	###############################################################################################################
+	if (!force_rtp_proxy("arf")) {
+		sl_send_reply("503", "No RTP-Relay available");
+		exit;
+	}
+	t_on_reply("1");
+
+	# Relay this statefully
+	t_relay();
+
+	exit;	
+}
+
+##################################################################################################################
+# Route for standard-checks (e. g. too many hops, re-transmits, CANCEL without a transaction)
+###############################################################################################################
+route[1] {
+	##################################################################################################################
+	# Sanity: Check if the syntax of the request is ok
+	# The following checks are available:
+	#
+	# * ruri sip version - (1) - checks if the SIP version in the request URI is supported, currently only 2.0.
+	# * ruri scheme - (2) - checks if the URI scheme of the request URI is supported (sip[s]|tel[s]) by Kamailio.
+	# * required headers - (4) -checks if the minimum set of required headers to, from, cseq, callid and via is 
+	#	present in the request.
+	# * via sip version - (8) - not working because parser fails already when another version then 2.0 is present.
+	# * via protocol - (16) - not working because parser fails already if an unsupported transport is present.
+	# * cseq method - (32) - checks if the method from the cseq header is equal to the request method.
+	# * cseq value - (64) - checks if the number in the cseq header is a valid unsigend integer.
+	# * content length - (128) - checks if the size of the body matches with the value from the content length header.
+	# * expires value - (256) - checks if the value of the expires header is a valid unsigned integer.
+	# * proxy require - (512) - checks if all items of the proxy require header are present in the list of the
+	#	extensions from the module parameter proxy_require.
+	# * parse uri's - (1024) - checks if the specified URIs are present and parseable by the Kamailio parsers
+	# * digest credentials (2048) Check all instances of digest credentials in a message. The test checks whether
+	#	there are all required digest parameters and have meaningful values. 
+	###############################################################################################################
+	if (is_method("INVITE")) {
+		# Checks: 1, 2, 4, 32, 64, 128, 1024 (with all URI's)
+		if (!sanity_check("1255")) {
+			xlog("malformed message from $si:$sp ($rm)\n");
+			exit;
+		}	
+	} else if (is_method("REGISTER")) {
+		# Checks: 1, 2, 4, 32, 64, 256, 1024 (with all URI's)
+		if (!sanity_check("1383")) {
+			xlog("malformed message from $si:$sp ($rm)\n");
+			exit;
+		}	
+	}
+
+	##################################################################################################################
+	# Check for too many hops
+	###############################################################################################################
+	if (!mf_process_maxfwd_header("20")) {
+	 	if (!is_method("ACK")) send_reply("483","To Many Hops");
+	 	exit;
+	};
+	
+	##################################################################################################################
+	# Standard-Options-Requests beantworten
+	###############################################################################################################
+	# xlog("L_ERR", "$rm $ru ($rU)\n");
+	if (is_method("OPTIONS")) {
+		# xlog("L_ERR", "$$rU not set or ping: $rU\n");
+		options_reply();
+		exit;
+	}	
+
+	##################################################################################################################
+	# Check for Re-Transmissions (nicht bei ACK/CANCEL)
+	###############################################################################################################
+	if (!is_method("CANCEL|ACK|PRACK")) {
+		if (t_check_trans()) {
+			# log(1, "Re-Transmission detected, message dropped.\n");
+			# Drop the message silently.
+			exit;
+		}
+	}
+
+	##################################################################################################################
+	# CANCEL without Transaction? Drop it. Otherwise: relay them.
+	###############################################################################################################
+	if (is_method("CANCEL")) {
+		# In case the INVITE is not finally processed (and so no TA is available), the SIP-UA must re-transmit.
+		if (!t_check_trans()) {
+			# No according transaction exists, just relay the cancel.
+			if (!forward()) {
+				xlog("L_ERR", "Unable to forward $rm $ru\n"); 
+			}
+			exit;
+		}
+		# There is a Transaction for this CANCEL
+		if (!t_relay()) {
+			exit;
+		}
+		exit;
+	}
+}
+
+##################################################################################################################
+# Route processing subsequential requests
+###############################################################################################################
+route[2] {
+	##################################################################################################################
+	# Record-Route, damit alle Nachrichten �ber diesen SIP-Proxy laufen
+	###############################################################################################################
+	if (!is_method("MESSAGE")) record_route();
+	
+	##################################################################################################################
+	# Loose-Routing (RFC3261)
+	###############################################################################################################
+	# Record-Route-Header entfernen
+	if (!loose_route()) {
+		##################################################################################################################
+		# Non loose, but stateful ACK: Relay.
+		###############################################################################################################
+		if (is_method("ACK") && (!t_check_trans())) {
+			forward();
+			exit;
+		}
+	}
+		
+	##################################################################################################################
+	# Retrieve NAT-Information from URI
+	# + NAT-Handling
+	###############################################################################################################
+	t_on_reply("1");
+
+	# This is a BYE? Tear down session on the RTP-Proxy.
+	if (is_method("BYE")) {
+		xlog("L_ERR", "RTP-Statistic: $rtpstat\n");
+		unforce_rtp_proxy();
+	}
+
+	# Relay this statefully
+	t_relay();
+	# Exit here.
+	exit;	
+}
+
+##################################################################################################################
+# Antwort-Route (Nathelper/RTP-Proxy)
+###############################################################################################################
+onreply_route[1] {	
+	# A Transaction from a NATed Client to a NATed Client? Use the RTP-Proxy!
+	if (status=~"(180)|(183)|(2[0-9][0-9])") {
+		fix_nated_contact();
+		# Content-Length prüfen
+		if (!search("^Content-Length:[ ]*0")) {
+			force_rtp_proxy("arf");
+                }
+        }
+}
+
+#route[XMLRPC]{
+#	if search("^User-Agent:.*xmlrpclib"))
+#		set_reply_close();
+#	set_reply_no_connect(); # optional
+#	dispatch_rpc();
+#}

+ 422 - 0
modules_k/rtpproxy/test/rtpproxy.patch

@@ -0,0 +1,422 @@
+diff --git a/Makefile.am b/Makefile.am
+index b47241b..a737d77 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -4,7 +4,7 @@ rtpproxy_SOURCES=main.c rtp.h rtp_server.c rtp_server.h \
+   rtpp_util.c rtpp_util.h rtp.c rtp_resizer.c rtp_resizer.h rtpp_session.c \
+   rtpp_command.c rtpp_command.h rtpp_log.c rtpp_network.h rtpp_network.c \
+   rtpp_syslog_async.c rtpp_syslog_async.h rtpp_notify.c rtpp_notify.h
+-rtpproxy_LDADD=-lm -lpthread
++rtpproxy_LDADD=-lm -lpthread @LIBS_XMLRPC@
+ dist_man_MANS=rtpproxy.8
+ makeann_SOURCES=makeann.c rtp.h g711.h
+ makeann_LDADD=@LIBS_G729@ @LIBS_GSM@
+diff --git a/config.h.in b/config.h.in
+index cd0c23a..87491e3 100644
+--- a/config.h.in
++++ b/config.h.in
+@@ -14,6 +14,9 @@
+ /* Define if you have libgsm library installed */
+ #undef ENABLE_GSM
+ 
++/* Define if you have xmlrpc library installed */
++#undef ENABLE_XMLRPC
++
+ /* Define to 1 if you have `alloca', as a function or macro. */
+ #undef HAVE_ALLOCA
+ 
+diff --git a/configure.ac b/configure.ac
+index 54c1a60..1c88b99 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -53,6 +53,17 @@ then
+    AC_DEFINE([ENABLE_G729], 1, [Define if you have libg729 library installed])
+   )
+ fi
++
++# XML-RPC-Libs:
++AC_CHECK_HEADERS(xmlrpc_client.h xmlrpc.h, found_xmlrpc=yes)
++if test "$found_xmlrpc" = yes
++then
++  AC_CHECK_LIB(curl, curl_version,
++   LIBS_XMLRPC="-lcurl -lxmlrpc_client -lxmlrpc -lxmlrpc_util -lxmlrpc_xmlparse -lxmlrpc_xmltok"
++   AC_DEFINE([ENABLE_XMLRPC], 1, [Define if you have XML-RPC-Client library installed])
++  )
++fi
++
+ ##if test -z "$G729_SUPPORT"
+ ##then
+ ##  echo "*************************************************************************** $ECHO_C" 1>&6
+@@ -94,4 +105,5 @@ AC_CONFIG_FILES([Makefile])
+ AC_SUBST(AM_CFLAGS)
+ AC_SUBST(LIBS_GSM)
+ AC_SUBST(LIBS_G729)
++AC_SUBST(LIBS_XMLRPC)
+ AC_OUTPUT
+diff --git a/rtpp_command.c b/rtpp_command.c
+index fa342b8..53c4a40 100644
+--- a/rtpp_command.c
++++ b/rtpp_command.c
+@@ -52,6 +52,7 @@
+ #include "rtpp_record.h"
+ #include "rtpp_session.h"
+ #include "rtpp_util.h"
++#include "rtpp_notify.h"
+ 
+ struct proto_cap proto_caps[] = {
+     /*
+@@ -67,6 +68,9 @@ struct proto_cap proto_caps[] = {
+     { "20081102", "Support for setting codecs in the update/lookup command" },
+     { "20081224", "Support for session timeout notifications" },
+     { "20090810", "Support for automatic bridging" },
++#ifdef ENABLE_XMLRPC
++    { "20100819", "Support for timeout notifications using XML-RPC towards Kamailio" },
++#endif
+     { NULL, NULL }
+ };
+ 
+@@ -266,7 +270,9 @@ handle_command(struct cfg *cf, int controlfd, double dtime)
+     int max_argc;
+     char *socket_name_u, *notify_tag;
+     struct sockaddr *local_addr;
++    struct rtpp_timeout_handler * my_timeout_h;
+     char c;
++    int rtp_timeout_type;
+ 
+     requested_nsamples = -1;
+     ia[0] = ia[1] = NULL;
+@@ -309,6 +315,7 @@ handle_command(struct cfg *cf, int controlfd, double dtime)
+ 	    if (++ap >= &argv[10])
+ 		break;
+ 	}
++	
+     cookie = NULL;
+     if (argc < 1 || (cf->umode != 0 && argc < 2)) {
+ 	rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error");
+@@ -459,22 +466,52 @@ handle_command(struct cfg *cf, int controlfd, double dtime)
+     }
+     call_id = argv[1];
+     if (op == UPDATE || op == LOOKUP || op == PLAY) {
+-	max_argc = (op == UPDATE ? 8 : 6);
++	if (op == UPDATE || op == LOOKUP) max_argc = 9;
++	else max_argc = 6;
+ 	if (argc < 5 || argc > max_argc) {
+-	    rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error");
++	    rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error (%d/%d)", argc, max_argc);
+ 	    reply_error(cf, controlfd, &raddr, rlen, cookie, 4);
+ 	    return 0;
+ 	}
+ 	from_tag = argv[4];
+-	to_tag = argv[5];
++	if ((op == UPDATE || op == LOOKUP) && argc > 5)
++	    to_tag = argv[5];
+ 	if (op == PLAY && argv[0][1] != '\0')
+ 	    playcount = atoi(argv[0] + 1);
+-	if (op == UPDATE && argc > 6) {
+-	    socket_name_u = argv[6];
++	if ((op == UPDATE || op == LOOKUP) && argc > 6) {
++	    if (argc == 7) {
++		// Only the Timeout-Socket is set:
++		socket_name_u = argv[6];
++		rtp_timeout_type = RTP_TIMEOUT_TYPE_NATIVE;
++	    } else {
++		notify_tag = 0;
++		if (argc == 9) {
++		    // 9 Parameters: timeout_type socket notify_tag
++		    rtp_timeout_type = atoi(argv[6]);
++		    socket_name_u = argv[7];
++		    notify_tag = argv[8];
++		} else {
++		    // 8 Parameters: May be timeout_type socket or socket tag
++		    rtp_timeout_type = -1;	
++		    for (t = argv[6]; *t != '\0'; t++) {
++		        // Parameter 6 is not numerical, lets assume it is std. timeout-socket + tag
++		        if (!isdigit(*t)) {
++			    socket_name_u = argv[6];
++		            rtp_timeout_type = RTP_TIMEOUT_TYPE_NATIVE;
++			    notify_tag = argv[7];	
++		            break;
++		        }
++		    }
++		    // Param 6 was numeric? So it must be a timeout_type_indicator
++		    if (rtp_timeout_type == -1) {
++			rtp_timeout_type = atoi(argv[6]);
++			socket_name_u = argv[7];
++		    }
++		}
++	    }
+ 	    if (strncmp("unix:", socket_name_u, 5) == 0)
+ 		socket_name_u += 5;
+-	    if (argc == 8) {
+-		notify_tag = argv[7];
++	    if (notify_tag != 0) {
+ 		len = url_unquote((uint8_t *)notify_tag, strlen(notify_tag));
+ 		if (len == -1) {
+ 		    rtpp_log_write(RTPP_LOG_ERR, cf->glog,
+@@ -924,9 +961,29 @@ handle_command(struct cfg *cf, int controlfd, double dtime)
+ 	}
+     }
+ 
+-    if (op == UPDATE) {
+-	if (cf->timeout_handler.socket_name == NULL && socket_name_u != NULL)
+-	    rtpp_log_write(RTPP_LOG_ERR, spa->log, "must permit notification socket with -n");
++    if (op == UPDATE || op == LOOKUP) {
++	if (cf->timeout_handler.socket_name == NULL && socket_name_u != NULL) {
++	    if ((rtp_timeout_type > 0) && (rtp_timeout_type <= RTP_TIMEOUT_TYPE_MAX)) {
++		rtpp_log_write(RTPP_LOG_INFO, spa->log, "setting custom timeout handler (%d)", rtp_timeout_type);
++		my_timeout_h = malloc(sizeof(struct rtpp_timeout_handler));
++		if (my_timeout_h == NULL) {
++			rtpp_log_write(RTPP_LOG_ERR, spa->log, "Unable to allocate memory");
++		} else {
++			memset(my_timeout_h, 0, sizeof(struct rtpp_timeout_handler));
++	    		my_timeout_h->socket_name = (char *)malloc(strlen(socket_name_u) + 1);
++	    		if(my_timeout_h->socket_name != NULL) {
++				strcpy(my_timeout_h->socket_name, socket_name_u);
++				spa->timeout_data.rtp_timeout_type = rtp_timeout_type;
++				spa->timeout_data.handler = my_timeout_h;
++				spa->timeout_data.notify_tag = 0;
++			} else {
++				rtpp_log_write(RTPP_LOG_ERR, spa->log, "Unable to allocate memory");
++				free(my_timeout_h);
++			}
++		}
++	    } else
++                rtpp_log_write(RTPP_LOG_ERR, spa->log, "must permit notification socket with -n");
++	}
+ 	if (spa->timeout_data.notify_tag != NULL) {
+ 	    free(spa->timeout_data.notify_tag);
+ 	    spa->timeout_data.notify_tag = NULL;
+diff --git a/rtpp_notify.c b/rtpp_notify.c
+index 26bb9a5..1553cb4 100644
+--- a/rtpp_notify.c
++++ b/rtpp_notify.c
+@@ -38,11 +38,26 @@
+ 
+ #include "rtpp_log.h"
+ #include "rtpp_session.h"
++#include "rtpp_notify.h"
++
++#ifdef ENABLE_XMLRPC
++#include <xmlrpc.h>
++#include <xmlrpc_client.h>
++#define XMLRPC_CLIENT_NAME       "XML-RPC RTPProxy Client"
++#define XMLRPC_CLIENT_VERSION    "0.1"
++#endif
+ 
+ struct rtpp_notify_wi
+ {
+     char *notify_buf;
+     int len;
++    int rtp_timeout_type;
++#ifdef ENABLE_XMLRPC
++    char *call_id;
++    int call_id_len;
++    char *param;
++    int param_len;
++#endif
+     struct rtpp_timeout_handler *th;
+     rtpp_log_t glog;
+     struct rtpp_notify_wi *next;
+@@ -170,7 +185,7 @@ rtpp_notify_schedule(struct cfg *cf, struct rtpp_session *sp)
+     struct rtpp_timeout_handler *th = sp->timeout_data.handler;
+     int len;
+     char *notify_buf;
+-
++    
+     if (th == NULL) {
+         /* Not an error, just nothing to do */
+         return 0;
+@@ -202,8 +217,6 @@ rtpp_notify_schedule(struct cfg *cf, struct rtpp_session *sp)
+         }
+         wi->notify_buf = notify_buf;
+     }
+-    wi->len = len;
+-
+     if (sp->timeout_data.notify_tag == NULL) {
+         len = snprintf(wi->notify_buf, len, "%d %d\n",
+           sp->ports[0], sp->ports[1]);
+@@ -211,6 +224,55 @@ rtpp_notify_schedule(struct cfg *cf, struct rtpp_session *sp)
+         len = snprintf(wi->notify_buf, len, "%s\n",
+           sp->timeout_data.notify_tag);
+     }
++    wi->len = len;
++
++    // Take RTP-Timeout type
++    wi->rtp_timeout_type = sp->timeout_data.rtp_timeout_type;
++#ifdef ENABLE_XMLRPC
++    if (wi->rtp_timeout_type == RTP_TIMEOUT_TYPE_KAMAILIO_XMLRPC) {
++	    // Copy the Socket-Name
++	    len = strlen(wi->th->socket_name)+1;
++	    if (wi->param == NULL) {
++		wi->param = malloc(len);
++		if (wi->param == NULL) {
++		    rtpp_notify_queue_return_free_item(wi);
++		    return -1;
++		}
++	    } else {
++		notify_buf = realloc(wi->param, len);
++		if (notify_buf == NULL) {
++		    rtpp_notify_queue_return_free_item(wi);
++		    return -1;
++		}
++		wi->param = notify_buf;
++	    }
++	    memset(wi->param, '\0', len);
++	    len = snprintf(wi->param, len, "%s",
++		  wi->th->socket_name);
++	    wi->param_len = len;
++
++	    // Copy the Call-ID:
++	    len = strlen(sp->call_id)+1;
++	    if (wi->call_id == NULL) {
++		wi->call_id = malloc(len);
++		if (wi->call_id == NULL) {
++		    rtpp_notify_queue_return_free_item(wi);
++		    return -1;
++		}
++	    } else {
++		notify_buf = realloc(wi->call_id, len);
++		if (notify_buf == NULL) {
++		    rtpp_notify_queue_return_free_item(wi);
++		    return -1;
++		}
++		wi->call_id = notify_buf;
++	    }
++	    memset(wi->call_id, '\0', len);
++	    len = snprintf(wi->call_id, len, "%s",
++		  sp->call_id);
++	    wi->call_id_len = len;
++    }
++#endif
+ 
+     wi->glog = cf->glog;
+ 
+@@ -249,24 +311,70 @@ reconnect_timeout_handler(rtpp_log_t log, struct rtpp_timeout_handler *th)
+     }
+ }
+ 
++#ifdef ENABLE_XMLRPC
++static int
++do_xmlrpc_timeout_notification(rtpp_log_t log, struct rtpp_notify_wi *wi) {
++    xmlrpc_env env;
++    xmlrpc_value *result;
++    
++    /* Start up our XML-RPC client library. */
++    xmlrpc_client_init(XMLRPC_CLIENT_NO_FLAGS, XMLRPC_CLIENT_NAME, XMLRPC_CLIENT_VERSION);
++    xmlrpc_env_init(&env);
++
++    /* Get the dialog-Info: */
++    result = xmlrpc_client_call(&env, wi->param,
++                                "dlg_terminate_dlg", "(s)",
++                                wi->call_id);
++    if (env.fault_occurred) {
++        rtpp_log_write(RTPP_LOG_ERR, wi->glog, "XML-RPC Fault: %s (%d)\n", env.fault_string, env.fault_code);
++        return -1;
++    }
++    
++    /* Dispose of our result value. */
++    xmlrpc_DECREF(result);
++
++    /* Shutdown our XML-RPC client library. */
++    xmlrpc_env_clean(&env);
++    xmlrpc_client_cleanup();
++
++    return 0;
++}
++#endif
++
++
+ static void
+ do_timeout_notification(struct rtpp_notify_wi *wi, int retries)
+ {
+     int result;
+-
+-    if (wi->th->connected == 0) {
+-        reconnect_timeout_handler(wi->glog, wi->th);
+-
+-        /* If connect fails, no notification will be sent */
++    if (wi->rtp_timeout_type > 0) {
++	switch (wi->rtp_timeout_type) {
++#ifdef ENABLE_XMLRPC
++		case RTP_TIMEOUT_TYPE_KAMAILIO_XMLRPC:
++			rtpp_log_write(RTPP_LOG_INFO, wi->glog, "Using XML-RPC-Timeout handler (%s)", wi->param);
++			result = do_xmlrpc_timeout_notification(wi->glog, wi);
++			break;
++#endif
++		default:
++			rtpp_log_write(RTPP_LOG_ERR, wi->glog, "Invalid/unsupported timeout handler type (%d)", wi->rtp_timeout_type);
++			break;
++	}
++    } else {
+         if (wi->th->connected == 0) {
+-            rtpp_log_write(RTPP_LOG_ERR, wi->glog, "unable to send timeout notification");
+-            return;
+-        }
++	    reconnect_timeout_handler(wi->glog, wi->th);
++
++    	    /* If connect fails, no notification will be sent */
++            if (wi->th->connected == 0) {
++	        rtpp_log_write(RTPP_LOG_ERR, wi->glog, "unable to send timeout notification");
++    	        return;
++            }
++	}
++
++        do {
++	    result = send(wi->th->fd, wi->notify_buf, wi->len, 0);
++        } while (result == -1 && errno == EINTR);
++#ifdef ENABLE_XMLRPC
+     }
+-
+-    do {
+-        result = send(wi->th->fd, wi->notify_buf, wi->len, 0);
+-    } while (result == -1 && errno == EINTR);
++#endif
+ 
+     if (result < 0) {
+         wi->th->connected = 0;
+diff --git a/rtpp_notify.h b/rtpp_notify.h
+index 10077a4..3f411cb 100644
+--- a/rtpp_notify.h
++++ b/rtpp_notify.h
+@@ -31,6 +31,10 @@
+ #include "rtpp_defines.h"
+ #include "rtpp_session.h"
+ 
++#define RTP_TIMEOUT_TYPE_NATIVE 0
++#define RTP_TIMEOUT_TYPE_KAMAILIO_XMLRPC 1
++#define RTP_TIMEOUT_TYPE_MAX 1
++
+ int rtpp_notify_schedule(struct cfg *, struct rtpp_session *);
+ int rtpp_notify_init(void);
+ 
+diff --git a/rtpp_session.c b/rtpp_session.c
+index 9962cac..b84cdf3 100644
+--- a/rtpp_session.c
++++ b/rtpp_session.c
+@@ -205,6 +205,14 @@ remove_session(struct cfg *cf, struct rtpp_session *sp)
+ 	if (sp->rtcp->codecs[i] != NULL)
+ 	    free(sp->rtcp->codecs[i]);
+     }
++#ifdef ENABLE_XMLRPC
++    // In case we use the Kamailio-XML-RPC-Timeout, we have our own timeout-handler
++    if (sp->timeout_data.handler != &cf->timeout_handler) {
++ 	free(sp->timeout_data.handler->socket_name);
++	free(sp->timeout_data.handler);
++    }
++#endif
++	
+     if (sp->timeout_data.notify_tag != NULL)
+ 	free(sp->timeout_data.notify_tag);
+     hash_table_remove(cf, sp);
+diff --git a/rtpp_session.h b/rtpp_session.h
+index 07aa794..9022f12 100644
+--- a/rtpp_session.h
++++ b/rtpp_session.h
+@@ -38,6 +38,7 @@
+ 
+ struct rtpp_timeout_data {
+     char *notify_tag;
++    int rtp_timeout_type;
+     struct rtpp_timeout_handler *handler;
+ };
+