Переглянути джерело

msrp: new module to handle MSRP (RFC4975)

Daniel-Constantin Mierla 13 роки тому
батько
коміт
19e138b57e

+ 12 - 0
modules/msrp/Makefile

@@ -0,0 +1,12 @@
+# $Id$
+#
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=msrp.so
+LIBS=
+
+DEFS+=-DOPENSER_MOD_INTERFACE
+
+include ../../Makefile.modules

+ 416 - 0
modules/msrp/README

@@ -0,0 +1,416 @@
+MSRP Module
+
+Daniel-Constantin Mierla
+
+   <[email protected]>
+
+Edited by
+
+Daniel-Constantin Mierla
+
+   <[email protected]>
+
+   Copyright © 2012 asipto.com
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+        2. Dependencies
+
+              2.1. Kamailio Modules
+              2.2. External Libraries or Applications
+
+        3. Parameters
+
+              3.1. sipmsg (int)
+
+        4. Functions
+
+              4.1. msrp_relay()
+              4.2. msrp_reply(code, text [, hdrs])
+              4.3. msrp_is_request()
+              4.4. msrp_is_reply()
+              4.5. msrp_set_dst(addr, sock)
+              4.6. msrp_relay_flags(flags)
+              4.7. msrp_reply_flags(flags)
+
+        5. Pseudo Variables
+        6. Event Routes
+        7. Usage
+
+   List of Examples
+
+   1.1. Set sipmsg parameter
+   1.2. msrp usage
+   1.3. msrp_reply usage
+   1.4. msrp_is_request usage
+   1.5. msrp_is_reply usage
+   1.6. msrp_set_dst usage
+   1.7. msrp_relay_flags usage
+   1.8. msrp_reply_flags usage
+   1.9. Event Route
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+   2. Dependencies
+
+        2.1. Kamailio Modules
+        2.2. External Libraries or Applications
+
+   3. Parameters
+
+        3.1. sipmsg (int)
+
+   4. Functions
+
+        4.1. msrp_relay()
+        4.2. msrp_reply(code, text [, hdrs])
+        4.3. msrp_is_request()
+        4.4. msrp_is_reply()
+        4.5. msrp_set_dst(addr, sock)
+        4.6. msrp_relay_flags(flags)
+        4.7. msrp_reply_flags(flags)
+
+   5. Pseudo Variables
+   6. Event Routes
+   7. Usage
+
+1. Overview
+
+   This module provides a MSRP routing engike, a.k.a. MSRP relay. MSRP
+   (Message Session Relay Protocol) is defined by RFC4975, the extensions
+   for a MSRP Relay being covered in RFC4976.
+
+   A typical use of MSRP is to do Instant Messaging sessions initiated via
+   SIP. Unlike page-mode instant messaging, which is done via SIP request
+   MESSAGE, MSRP uses a different communication channel, which is
+   negotiated via INVITE-200OK-ACK.
+
+   However, MSRP is still a text based protocol, using several routing
+   mechanisms which are similar with what exists in SIP. Furthermore, MSRP
+   requres TCP (and recommends TLS for confidentiality and security).
+   Considering scalability and perormances of Kamailio for handling
+   TCP/TLS, this module reuses Kamailio's core framework to offer MSRP
+   routing capabilities. Along with embedded Presence and XCAP servers,
+   Kamailio offers now a complete solution for SIP beyond VoIP.
+
+   One of the main benefits with the new module, is the ability to reuse
+   all the other extensions that exist in the SIP server, from accounting,
+   authentication, authorization to database connectors, security and DoS
+   attacks protections.
+
+2. Dependencies
+
+   2.1. Kamailio Modules
+   2.2. External Libraries or Applications
+
+2.1. Kamailio Modules
+
+   The following modules must be loaded before this module:
+     * None.
+
+2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed before
+   running Kamailio with this module loaded:
+     * None
+
+3. Parameters
+
+   3.1. sipmsg (int)
+
+3.1. sipmsg (int)
+
+   If 1, the module will build a SIP message from MSRP frame headers,
+   providing it to event_route[msrp:frame-in]. The all the config file
+   functions (apart of SIP request relaying) that can be used in a request
+   route block can be used in msrp event_route.
+
+   Default value is '1'.
+
+   Example 1.1. Set sipmsg parameter
+...
+modparam("msrp", "sipmsg", 1)
+...
+
+4. Functions
+
+   4.1. msrp_relay()
+   4.2. msrp_reply(code, text [, hdrs])
+   4.3. msrp_is_request()
+   4.4. msrp_is_reply()
+   4.5. msrp_set_dst(addr, sock)
+   4.6. msrp_relay_flags(flags)
+   4.7. msrp_reply_flags(flags)
+
+4.1. msrp_relay()
+
+   Relay MSRP frame according to To-Path. It has to be executed for each
+   MSRP request or reply that has to be forwarded. Note that due to nature
+   of MSRP transport layer which is reliable (TCP/TLS), there is no
+   retransmission of MSRP frames.
+
+   This function can be used in ANY_ROUTE.
+
+   Example 1.2. msrp usage
+...
+event_route[msrp:frame-in] {
+    msrp_relay();
+}
+...
+
+4.2. msrp_reply(code, text [, hdrs])
+
+   Send a reply for current MSRP request, adding optional headers.
+
+   The parameter can be pseudo-variable.
+
+   This function can be used in ANY_ROUTE.
+
+   Example 1.3. msrp_reply usage
+...
+event_route[msrp:frame-in] {
+    msrp_reply("403", "Not allowed");
+}
+...
+
+4.3. msrp_is_request()
+
+   Return true if the MSRP frame is a request.
+
+   This function can be used in ANY_ROUTE.
+
+   Example 1.4. msrp_is_request usage
+...
+event_route[msrp:frame-in] {
+    if(msrp_is_request())
+    {
+        msrp_relay();
+        exit;
+    }
+}
+...
+
+4.4. msrp_is_reply()
+
+   Return true if the MSRP frame is a reply.
+
+   This function can be used in ANY_ROUTE.
+
+   Example 1.5. msrp_is_reply usage
+...
+event_route[msrp:frame-in] {
+    if(msrp_is_reply())
+    {
+        msrp_relay();
+        exit;
+    }
+}
+...
+
+4.5. msrp_set_dst(addr, sock)
+
+   Set destination attributes: addr - target address as MSRP URI; sock -
+   local socket to be used (format 'proto:ip:port').
+
+   The parameter can be pseudo-variable.
+
+   This function can be used in ANY_ROUTE.
+
+   Example 1.6. msrp_set_dst usage
+...
+event_route[msrp:frame-in] {
+    ...
+    msrp_set_dst("msrp://127.0.0.1:8000", "tcp:127.0.0.1:5060");
+    ...
+}
+...
+
+4.6. msrp_relay_flags(flags)
+
+   Set transport layer sending flags for forwarding current MSRP frame;
+   flags - a bitmask of flags - 1 (don't create a new connection), 2
+   (close connection after send).
+
+   The parameter can be pseudo-variable.
+
+   This function can be used in ANY_ROUTE.
+
+   Example 1.7. msrp_relay_flags usage
+...
+event_route[msrp:frame-in] {
+    ...
+    msrp_relay_flags("1");
+    ...
+}
+...
+
+4.7. msrp_reply_flags(flags)
+
+   Set transport layer sending flags for replies to current MSRP frame;
+   flags - a bitmask of flags - 1 (don't create a new connection), 2
+   (close connection after send).
+
+   The parameter can be pseudo-variable.
+
+   This function can be used in ANY_ROUTE.
+
+   Example 1.8. msrp_reply_flags usage
+...
+event_route[msrp:frame-in] {
+    ...
+    msrp_reply_flags("1");
+    ...
+}
+...
+
+5. Pseudo Variables
+
+   The module exports a pseudo-variables class $msrp(key) to access the
+   MSRP frame (e.g., first line attributes, body, all frame content).
+
+   The module exports a transformations class 'msrpuri' to be able to
+   access attributes of a MSRP URI.
+
+   These are documented in the appropriate Wiki pages hosted by the
+   project's web site.
+
+6. Event Routes
+
+   For each MSRP frame received from the network, the module executes
+   event_route[msrp:frame-in] block in the config file.
+
+7. Usage
+
+   Example routing block for MSRP frames.
+
+   Example 1.9. Event Route
+...
+
+#!KAMAILIO
+
+debug=2
+memdbg=5
+memlog=5
+
+children=4
+
+log_stderror=yes
+auto_aliases=no
+
+tcp_accept_no_cl=yes
+tcp_connection_lifetime=1810
+
+listen=127.0.0.1:5060
+
+mpath="modules_k/:modules/"
+
+loadmodule "sl.so"
+loadmodule "kex.so"
+loadmodule "mi_fifo.so"
+loadmodule "ctl.so"
+loadmodule "msrp.so"
+loadmodule "pv.so"
+loadmodule "auth.so"
+loadmodule "cfgutils.so"
+loadmodule "htable.so"
+loadmodule "xlog.so"
+
+# ----- mi_fifo params -----
+modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo")
+
+modparam("htable", "htable", "c=>size=8;autoexpire=1800;")
+
+request_route {
+        sl_send_reply("403", "No SIP Here");
+        exit;
+}
+
+event_route[msrp:frame-in] {
+        xdbg("============#[[$msrp(method)]]===========\n");
+        xdbg("============*[[$si:$sp]]\n");
+        xdbg("============ crthop:   [$msrp(crthop)]\n");
+        xdbg("============ prevhop:  [$msrp(prevhop)]\n");
+        xdbg("============ nexthop:  [$msrp(nexthop)]\n");
+        xdbg("============ firsthop: [$msrp(firsthop)]\n");
+        xdbg("============ lasthop:  [$msrp(lasthop)]\n");
+        xdbg("============ prevhops: [$msrp(prevhops)]\n");
+        xdbg("============ nexthops: [$msrp(nexthops)]\n");
+        xdbg("============ srcaddr:  [$msrp(srcaddr)]\n");
+        xdbg("============ srcsock:  [$msrp(srcsock)]\n");
+        xdbg("============ sessid:   [$msrp(sessid)]\n");
+
+        msrp_reply_flags("1");
+
+    if(msrp_is_reply())
+    {
+        msrp_relay();
+        exit;
+    }
+
+        # handle AUTH MSRP requests
+        if($msrp(method)=="AUTH")
+        {
+                if($msrp(nexthops)>0)
+                {
+                        msrp_relay();
+                        exit;
+                }
+                # frame for local server - send Use-Path
+                # -- passwd can be loaded from DB based on $au
+                $var(passwd) = "xyz123";
+                if(!pv_www_authenticate("myrealm", "$var(passwd)", "0"))
+                {
+                        if(auth_get_www_authenticate("myrealm", "0",
+                                                "$var(wauth)"))
+                        {
+                                msrp_reply("401", "Authorization Required",
+                                                "$var(wauth)");
+                        } else {
+                                msrp_reply("500", "Server Error");
+                        }
+                        exit;
+                }
+                $var(cnt) = $var(cnt) + 1;
+                pv_printf("$var(sessid)", "s.$(pp).$(var(cnt)).$(RANDOM)");
+                $sht(msrp=>$var(sessid)::srcaddr) = $msrp(srcaddr);
+                $sht(msrp=>$var(sessid)::srcsock) = $msrp(srcsock);
+                # - Use-Path: the MSRP address for server + session id
+                $var(UsePath) = "Use-Path: msrp://127.0.0.1:5060/"
+                                + $var(sessid) + ";tcp\r\n";
+                msrp_reply("200", "OK", "$var(UsePath)");
+                exit;
+        }
+
+        if($msrp(method)=="SEND")
+        {
+                if($msrp(nexthops)>1)
+                {
+                        msrp_reply("200", "Received");
+                        msrp_relay();
+                        exit;
+                }
+                $var(sessid) = $msrp(sessid);
+                if($sht(msrp=>$var(sessid)::srcaddr) == $null)
+                {
+                        # one more hop, but we don't have address in htable
+                        msrp_reply("481", "No Such Session");
+                        exit;
+                }
+                msrp_relay_flags("1");
+                msrp_set_dst("$sht(msrp=>$var(sessid)::srcaddr)",
+                                "$sht(msrp=>$var(sessid)::srcsock)");
+                msrp_relay();
+                exit;
+        }
+
+        msrp_relay();
+}
+
+...

+ 4 - 0
modules/msrp/doc/Makefile

@@ -0,0 +1,4 @@
+docs = msrp.xml
+
+docbook_dir = ../../../docbook
+include $(docbook_dir)/Makefile.module

+ 37 - 0
modules/msrp/doc/msrp.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+    <bookinfo>
+	<title>MSRP Module</title>
+	<productname class="trade">sip-router.org</productname>
+	<authorgroup>
+	    <author>
+		<firstname>Daniel-Constantin</firstname>
+		<surname>Mierla</surname>
+		<email>[email protected]</email>
+	    </author>
+	    <editor>
+		<firstname>Daniel-Constantin</firstname>
+		<surname>Mierla</surname>
+		<email>[email protected]</email>
+	    </editor>
+	</authorgroup>
+	<copyright>
+	    <year>2012</year>
+	    <holder>asipto.com</holder>
+	</copyright>
+    </bookinfo>
+    <toc></toc>
+    
+    <xi:include href="msrp_admin.xml"/>
+    
+    
+</book>

+ 457 - 0
modules/msrp/doc/msrp_admin.xml

@@ -0,0 +1,457 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+<!-- Module User's Guide -->
+
+<chapter>
+	
+	<title>&adminguide;</title>
+	
+	<section>
+	<title>Overview</title>
+	<para>
+		This module provides a MSRP routing engike, a.k.a. MSRP relay.
+		MSRP (Message Session Relay Protocol) is defined by RFC4975,
+		the extensions for a MSRP Relay being covered in RFC4976.
+	</para>
+	<para>
+		A typical use of MSRP is to do Instant Messaging sessions initiated
+		via SIP. Unlike page-mode instant messaging, which is done via SIP
+		request MESSAGE, MSRP uses a different communication channel, which
+		is negotiated via INVITE-200OK-ACK.
+	</para>
+	<para>
+		However, MSRP is still a text based protocol, using several routing
+		mechanisms which are similar with what exists in SIP. Furthermore,
+		MSRP requres TCP (and recommends TLS for confidentiality and security).
+		Considering scalability and perormances of &kamailio; for handling
+		TCP/TLS, this module reuses &kamailio;'s core framework to
+		offer MSRP routing capabilities. Along with embedded Presence and XCAP
+		servers, &kamailio; offers now a complete solution for SIP beyond VoIP.
+	</para>
+	<para>
+		One of the main benefits with the new module, is the ability to reuse
+		all the other extensions that exist in the SIP server, from accounting,
+		authentication, authorization to database connectors, security and
+		DoS attacks protections.
+	</para>
+	</section>
+
+	<section>
+	<title>Dependencies</title>
+	<section>
+		<title>&kamailio; Modules</title>
+		<para>
+		The following modules must be loaded before this module:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>None</emphasis>.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	<section>
+		<title>External Libraries or Applications</title>
+		<para>
+		The following libraries or applications must be installed before running
+		&kamailio; with this module loaded:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>None</emphasis>
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	</section>
+	<section>
+	<title>Parameters</title>
+	<section>
+		<title><varname>sipmsg</varname> (int)</title>
+		<para>
+			If 1, the module will build a SIP message from MSRP frame headers,
+			providing it to event_route[msrp:frame-in]. The all the config
+			file functions (apart of SIP request relaying) that can be used
+			in a request route block can be used in msrp event_route.
+		</para>
+		<para>
+		<emphasis>
+			Default value is '1'.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>sipmsg</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("msrp", "sipmsg", 1)
+...
+</programlisting>
+		</example>
+	</section>
+	</section>
+
+	<section>
+	<title>Functions</title>
+	<section>
+	    <title>
+		<function moreinfo="none">msrp_relay()</function>
+	    </title>
+	    <para>
+			Relay MSRP frame according to To-Path. It has to be executed
+			for each MSRP request or reply that has to be forwarded. Note
+			that due to nature of MSRP transport layer which is reliable
+			(TCP/TLS), there is no retransmission of MSRP frames.
+		</para>
+
+		<para>
+		This function can be used in ANY_ROUTE.
+		</para>
+		<example>
+		<title><function>msrp</function> usage</title>
+		<programlisting format="linespecific">
+...
+event_route[msrp:frame-in] {
+    msrp_relay();
+}
+...
+</programlisting>
+	    </example>
+	</section>
+
+	<section>
+	    <title>
+		<function moreinfo="none">msrp_reply(code, text [, hdrs])</function>
+	    </title>
+	    <para>
+		Send a reply for current MSRP request, adding optional headers.
+		</para>
+		<para>
+		The parameter can be pseudo-variable.
+		</para>
+		<para>
+		This function can be used in ANY_ROUTE.
+		</para>
+		<example>
+		<title><function>msrp_reply</function> usage</title>
+		<programlisting format="linespecific">
+...
+event_route[msrp:frame-in] {
+    msrp_reply("403", "Not allowed");
+}
+...
+</programlisting>
+	    </example>
+	</section>
+
+	<section>
+	    <title>
+		<function moreinfo="none">msrp_is_request()</function>
+	    </title>
+	    <para>
+			Return true if the MSRP frame is a request.
+		</para>
+		<para>
+		This function can be used in ANY_ROUTE.
+		</para>
+		<example>
+		<title><function>msrp_is_request</function> usage</title>
+		<programlisting format="linespecific">
+...
+event_route[msrp:frame-in] {
+    if(msrp_is_request())
+    {
+        msrp_relay();
+        exit;
+    }
+}
+...
+</programlisting>
+	    </example>
+	</section>
+
+
+	<section>
+	    <title>
+		<function moreinfo="none">msrp_is_reply()</function>
+	    </title>
+	    <para>
+			Return true if the MSRP frame is a reply.
+		</para>
+		<para>
+		This function can be used in ANY_ROUTE.
+		</para>
+		<example>
+		<title><function>msrp_is_reply</function> usage</title>
+		<programlisting format="linespecific">
+...
+event_route[msrp:frame-in] {
+    if(msrp_is_reply())
+    {
+        msrp_relay();
+        exit;
+    }
+}
+...
+</programlisting>
+	    </example>
+	</section>
+
+	<section>
+	    <title>
+		<function moreinfo="none">msrp_set_dst(addr, sock)</function>
+	    </title>
+	    <para>
+		Set destination attributes: addr - target address as MSRP URI;
+		sock - local socket to be used (format 'proto:ip:port').
+		</para>
+		<para>
+		The parameter can be pseudo-variable.
+		</para>
+		<para>
+		This function can be used in ANY_ROUTE.
+		</para>
+		<example>
+		<title><function>msrp_set_dst</function> usage</title>
+		<programlisting format="linespecific">
+...
+event_route[msrp:frame-in] {
+    ...
+    msrp_set_dst("msrp://127.0.0.1:8000", "tcp:127.0.0.1:5060");
+    ...
+}
+...
+</programlisting>
+	    </example>
+	</section>
+
+	<section>
+	    <title>
+		<function moreinfo="none">msrp_relay_flags(flags)</function>
+	    </title>
+	    <para>
+		Set transport layer sending flags for forwarding current MSRP frame;
+		flags - a bitmask of flags - 1 (don't create a new connection), 2
+		(close connection after send).
+		</para>
+		<para>
+		The parameter can be pseudo-variable.
+		</para>
+		<para>
+		This function can be used in ANY_ROUTE.
+		</para>
+		<example>
+		<title><function>msrp_relay_flags</function> usage</title>
+		<programlisting format="linespecific">
+...
+event_route[msrp:frame-in] {
+    ...
+    msrp_relay_flags("1");
+    ...
+}
+...
+</programlisting>
+	    </example>
+	</section>
+
+	<section>
+	    <title>
+		<function moreinfo="none">msrp_reply_flags(flags)</function>
+	    </title>
+	    <para>
+		Set transport layer sending flags for replies to current MSRP frame;
+		flags - a bitmask of flags - 1 (don't create a new connection),
+		2 (close connection after send).
+		</para>
+		<para>
+		The parameter can be pseudo-variable.
+		</para>
+		<para>
+		This function can be used in ANY_ROUTE.
+		</para>
+		<example>
+		<title><function>msrp_reply_flags</function> usage</title>
+		<programlisting format="linespecific">
+...
+event_route[msrp:frame-in] {
+    ...
+    msrp_reply_flags("1");
+    ...
+}
+...
+</programlisting>
+	    </example>
+	</section>
+
+	</section>
+
+	<section>
+		<title>Pseudo Variables</title>
+		<para>
+			The module exports a pseudo-variables class $msrp(key) to access
+			the MSRP frame (e.g., first line attributes, body, all frame
+			content).
+		</para>
+		<para>
+			The module exports a transformations class 'msrpuri' to be able
+			to access attributes of a MSRP URI.
+		</para>
+		<para>
+			These are documented in the appropriate Wiki pages hosted by the
+			project's web site.
+		</para>
+	</section>
+
+	<section>
+		<title>Event Routes</title>
+		<para>
+			For each MSRP frame received from the network, the module executes
+			event_route[msrp:frame-in] block in the config file.
+		</para>
+	</section>
+
+	<section>
+		<title>Usage</title>
+		<para>
+			Example routing block for MSRP frames.
+		</para>
+		<example>
+		<title>Event Route</title>
+		<programlisting format="linespecific">
+...
+<![CDATA[
+#!KAMAILIO
+
+debug=2
+memdbg=5
+memlog=5
+
+children=4
+
+log_stderror=yes
+auto_aliases=no
+
+tcp_accept_no_cl=yes
+tcp_connection_lifetime=1810
+
+listen=127.0.0.1:5060
+
+mpath="modules_k/:modules/"
+
+loadmodule "sl.so"
+loadmodule "kex.so"
+loadmodule "mi_fifo.so"
+loadmodule "ctl.so"
+loadmodule "msrp.so"
+loadmodule "pv.so"
+loadmodule "auth.so"
+loadmodule "cfgutils.so"
+loadmodule "htable.so"
+loadmodule "xlog.so"
+
+# ----- mi_fifo params -----
+modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo")
+
+modparam("htable", "htable", "c=>size=8;autoexpire=1800;")
+
+request_route {
+	sl_send_reply("403", "No SIP Here");
+	exit;
+}
+
+event_route[msrp:frame-in] {
+	xdbg("============#[[$msrp(method)]]===========\n");
+	xdbg("============*[[$si:$sp]]\n");
+	xdbg("============ crthop:   [$msrp(crthop)]\n");
+	xdbg("============ prevhop:  [$msrp(prevhop)]\n");
+	xdbg("============ nexthop:  [$msrp(nexthop)]\n");
+	xdbg("============ firsthop: [$msrp(firsthop)]\n");
+	xdbg("============ lasthop:  [$msrp(lasthop)]\n");
+	xdbg("============ prevhops: [$msrp(prevhops)]\n");
+	xdbg("============ nexthops: [$msrp(nexthops)]\n");
+	xdbg("============ srcaddr:  [$msrp(srcaddr)]\n");
+	xdbg("============ srcsock:  [$msrp(srcsock)]\n");
+	xdbg("============ sessid:   [$msrp(sessid)]\n");
+
+	msrp_reply_flags("1");
+
+    if(msrp_is_reply())
+    {
+        msrp_relay();
+        exit;
+    }
+
+	# handle AUTH MSRP requests
+	if($msrp(method)=="AUTH")
+	{
+		if($msrp(nexthops)>0)
+		{
+			msrp_relay();
+			exit;
+		}
+		# frame for local server - send Use-Path
+		# -- passwd can be loaded from DB based on $au
+		$var(passwd) = "xyz123";
+		if(!pv_www_authenticate("myrealm", "$var(passwd)", "0"))
+		{
+			if(auth_get_www_authenticate("myrealm", "0",
+						"$var(wauth)"))
+			{
+				msrp_reply("401", "Authorization Required",
+						"$var(wauth)");
+			} else {
+				msrp_reply("500", "Server Error");
+			}
+			exit;
+		}
+		$var(cnt) = $var(cnt) + 1;
+		pv_printf("$var(sessid)", "s.$(pp).$(var(cnt)).$(RANDOM)");
+		$sht(msrp=>$var(sessid)::srcaddr) = $msrp(srcaddr);
+		$sht(msrp=>$var(sessid)::srcsock) = $msrp(srcsock);
+		# - Use-Path: the MSRP address for server + session id
+		$var(UsePath) = "Use-Path: msrp://127.0.0.1:5060/"
+				+ $var(sessid) + ";tcp\r\n";
+		msrp_reply("200", "OK", "$var(UsePath)");
+		exit;
+	}
+
+	if($msrp(method)=="SEND")
+	{
+		if($msrp(nexthops)>1)
+		{
+			msrp_reply("200", "Received");
+			msrp_relay();
+			exit;
+		}
+		$var(sessid) = $msrp(sessid);
+		if($sht(msrp=>$var(sessid)::srcaddr) == $null)
+		{
+			# one more hop, but we don't have address in htable
+			msrp_reply("481", "No Such Session");
+			exit;
+		}
+		msrp_relay_flags("1");
+		msrp_set_dst("$sht(msrp=>$var(sessid)::srcaddr)",
+				"$sht(msrp=>$var(sessid)::srcsock)");
+		msrp_relay();
+		exit;
+	}
+
+	msrp_relay();
+}
+]]>
+...
+</programlisting>
+	    </example>
+	</section>
+
+</chapter>
+

+ 189 - 0
modules/msrp/msrp_env.c

@@ -0,0 +1,189 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2012 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../dprint.h"
+#include "../../globals.h"
+#include "../../ut.h"
+#include "../../dset.h"
+
+#include "msrp_parser.h"
+#include "msrp_netio.h"
+#include "msrp_env.h"
+
+extern int msrp_param_sipmsg;
+
+static msrp_env_t _msrp_env = {0};
+
+/**
+ *
+ */
+msrp_env_t *msrp_get_env(void)
+{
+	return &_msrp_env;
+}
+
+/**
+ *
+ */
+void msrp_reset_env(void)
+{
+	memset(&_msrp_env, 0, sizeof(struct msrp_env));
+}
+
+/**
+ *
+ */
+msrp_frame_t *msrp_get_current_frame(void)
+{
+	return _msrp_env.msrp;
+}
+
+/**
+ *
+ */
+int msrp_set_current_frame(msrp_frame_t *mf)
+{
+	_msrp_env.msrp = mf;
+	init_dst_from_rcv(&_msrp_env.srcinfo, mf->tcpinfo->rcv);
+	_msrp_env.envflags |= MSRP_ENV_SRCINFO;
+	return 0;
+}
+
+
+/**
+ *
+ */
+int msrp_env_set_dstinfo(msrp_frame_t *mf, str *addr, str *fsock, int flags)
+{
+	struct socket_info *si = NULL;
+	snd_flags_t sflags = {0};
+	
+	if(fsock!=NULL && fsock->len>0)
+	{
+		si = msrp_get_local_socket(fsock);
+		if(si==NULL)
+		{
+			LM_DBG("local socket not found [%.*s] - trying to continue\n",
+					fsock->len, fsock->s);
+		}
+	}
+	sflags.f = flags;
+	if(si==NULL)
+	{
+		sflags.f &= ~SND_F_FORCE_SOCKET;
+	} else {
+		sflags.f |= SND_F_FORCE_SOCKET;
+	}
+
+	sflags.f |=  _msrp_env.sndflags;
+	memset(&_msrp_env.dstinfo, 0, sizeof(struct dest_info));
+	if(msrp_uri_to_dstinfo(NULL, &_msrp_env.dstinfo, si, sflags, addr)==NULL)
+	{
+		LM_ERR("failed to set destination address [%.*s]\n",
+				addr->len, addr->s);
+		return -1;
+	}
+	_msrp_env.envflags |= MSRP_ENV_DSTINFO;
+	return 0;
+}
+
+/**
+ *
+ */
+int msrp_env_set_sndflags(msrp_frame_t *mf, int flags)
+{
+	_msrp_env.sndflags |= (flags & (~SND_F_FORCE_SOCKET));
+	if(_msrp_env.envflags & MSRP_ENV_DSTINFO)
+	{
+		_msrp_env.dstinfo.send_flags.f |= _msrp_env.sndflags;
+	}
+	return 0;
+}
+
+/**
+ *
+ */
+int msrp_env_set_rplflags(msrp_frame_t *mf, int flags)
+{
+	_msrp_env.rplflags |= (flags & (~SND_F_FORCE_SOCKET));
+	if(_msrp_env.envflags & MSRP_ENV_SRCINFO)
+	{
+		_msrp_env.srcinfo.send_flags.f |= _msrp_env.rplflags;
+	}
+	return 0;
+}
+
+
+/**
+ *
+ */
+#define MSRP_FAKED_SIPMSG_START "MSRP sip:[email protected] SIP/2.0\r\nVia: SIP/2.0/UDP 127.0.0.1:9;branch=z9hG4bKa\r\nFrom: <[email protected]>;tag=a\r\nTo: <[email protected]>\r\nCall-ID: a\r\nCSeq: 1 MSRP\r\nContent-Length: 0\r\nMSRP-First-Line: "
+#define MSRP_FAKED_SIPMSG_EXTRA 11240
+#define MSRP_FAKED_SIPMSG_SIZE (sizeof(MSRP_FAKED_SIPMSG_START)+MSRP_FAKED_SIPMSG_EXTRA)
+static char _msrp_faked_sipmsg_buf[MSRP_FAKED_SIPMSG_SIZE];
+static sip_msg_t _msrp_faked_sipmsg;
+static unsigned int _msrp_faked_sipmsg_no = 0;
+
+sip_msg_t *msrp_fake_sipmsg(msrp_frame_t *mf)
+{
+	int len;
+	if(msrp_param_sipmsg==0)
+		return NULL;
+	if(mf->buf.len >= MSRP_FAKED_SIPMSG_EXTRA-1)
+		return NULL;
+
+	len = sizeof(MSRP_FAKED_SIPMSG_START)-1;
+	memcpy(_msrp_faked_sipmsg_buf, MSRP_FAKED_SIPMSG_START, len);
+	memcpy(_msrp_faked_sipmsg_buf + len, mf->buf.s,
+			mf->fline.buf.len + mf->hbody.len);
+	len += mf->fline.buf.len + mf->hbody.len;
+	memcpy(_msrp_faked_sipmsg_buf + len, "\r\n", 2);
+	len += 2;
+	_msrp_faked_sipmsg_buf[len] = '\0';
+
+	memset(&_msrp_faked_sipmsg, 0, sizeof(sip_msg_t));
+
+	_msrp_faked_sipmsg.buf=_msrp_faked_sipmsg_buf;
+	_msrp_faked_sipmsg.len=len;
+
+	_msrp_faked_sipmsg.set_global_address=default_global_address;
+	_msrp_faked_sipmsg.set_global_port=default_global_port;
+
+	if (parse_msg(_msrp_faked_sipmsg.buf, _msrp_faked_sipmsg.len,
+				&_msrp_faked_sipmsg)!=0)
+	{
+		LM_ERR("parse_msg failed\n");
+		return NULL;
+	}
+
+	_msrp_faked_sipmsg.id = 1 + _msrp_faked_sipmsg_no++;
+	_msrp_faked_sipmsg.pid = my_pid();
+	clear_branches();
+	return &_msrp_faked_sipmsg;
+}

+ 52 - 0
modules/msrp/msrp_env.h

@@ -0,0 +1,52 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2012 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef _MSRP_ENV_H_
+#define _MSRP_ENV_H_
+
+#include "msrp_parser.h"
+
+#define MSRP_ENV_SRCINFO	(1<<0)
+#define MSRP_ENV_DSTINFO	(1<<1)
+
+typedef struct msrp_env {
+	msrp_frame_t *msrp;
+	struct dest_info srcinfo;
+	struct dest_info dstinfo;
+	int envflags;
+	int sndflags;
+	int rplflags;
+} msrp_env_t;
+
+msrp_env_t *msrp_get_env(void);
+void msrp_reset_env(void);
+msrp_frame_t *msrp_get_current_frame(void);
+int msrp_set_current_frame(msrp_frame_t *mf);
+sip_msg_t *msrp_fake_sipmsg(msrp_frame_t *mf);
+
+int msrp_env_set_dstinfo(msrp_frame_t *mf, str *addr, str *fsock, int flags);
+int msrp_env_set_sndflags(msrp_frame_t *mf, int flags);
+int msrp_env_set_rplflags(msrp_frame_t *mf, int flags);
+
+#endif

+ 394 - 0
modules/msrp/msrp_mod.c

@@ -0,0 +1,394 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2012 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../sr_module.h"
+#include "../../dprint.h"
+#include "../../ut.h"
+#include "../../dset.h"
+#include "../../action.h"
+#include "../../mod_fix.h"
+#include "../../events.h"
+#include "../../tcp_conn.h"
+#include "../../pvar.h"
+
+#include "msrp_parser.h"
+#include "msrp_netio.h"
+#include "msrp_vars.h"
+#include "msrp_env.h"
+
+MODULE_VERSION
+
+static int  mod_init(void);
+static int  child_init(int);
+static void mod_destroy(void);
+
+static int w_msrp_relay(sip_msg_t* msg, char* str1, char* str2);
+static int w_msrp_reply2(sip_msg_t* msg, char* code, char* text);
+static int w_msrp_reply3(sip_msg_t* msg, char* code, char* text, char* hdrs);
+static int w_msrp_is_request(sip_msg_t* msg, char* str1, char* str2);
+static int w_msrp_is_reply(sip_msg_t* msg, char* str1, char* str2);
+static int w_msrp_set_dst(sip_msg_t* msg, char* taddr, char* fsock);
+static int w_msrp_relay_flags(sip_msg_t* msg, char *tflags, char* str2);
+static int w_msrp_reply_flags(sip_msg_t* msg, char *tflags, char* str2);
+
+int msrp_param_sipmsg = 1;
+
+static int msrp_frame_received(void *data);
+sip_msg_t *msrp_fake_sipmsg(msrp_frame_t *mf);
+
+static tr_export_t mod_trans[] = {
+	{ {"msrpuri", sizeof("msrpuri")-1}, /* string class */
+		tr_parse_msrpuri },
+
+	{ { 0, 0 }, 0 }
+};
+
+static pv_export_t mod_pvs[] = {
+	{ {"msrp", (sizeof("msrp")-1)}, PVT_OTHER, pv_get_msrp,
+		pv_set_msrp, pv_parse_msrp_name, 0, 0, 0},
+
+	{ {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
+};
+
+static cmd_export_t cmds[]={
+	{"msrp_relay", (cmd_function)w_msrp_relay, 0, 0,
+		0, ANY_ROUTE},
+	{"msrp_reply", (cmd_function)w_msrp_reply2, 2, fixup_spve_spve,
+		0, ANY_ROUTE},
+	{"msrp_reply", (cmd_function)w_msrp_reply3, 3, fixup_spve_all,
+		0, ANY_ROUTE},
+	{"msrp_is_request", (cmd_function)w_msrp_is_request, 0, 0,
+		0, ANY_ROUTE},
+	{"msrp_is_reply", (cmd_function)w_msrp_is_reply, 0, 0,
+		0, ANY_ROUTE},
+	{"msrp_set_dst", (cmd_function)w_msrp_set_dst, 2, fixup_spve_all,
+		0, ANY_ROUTE},
+	{"msrp_relay_flags", (cmd_function)w_msrp_relay_flags, 1, fixup_igp_null,
+		0, ANY_ROUTE},
+	{"msrp_reply_flags", (cmd_function)w_msrp_reply_flags, 1, fixup_igp_null,
+		0, ANY_ROUTE},
+	{0, 0, 0, 0, 0, 0}
+};
+
+static param_export_t params[]={
+	{"sipmsg",     INT_PARAM,   &msrp_param_sipmsg},
+	{0, 0, 0}
+};
+
+struct module_exports exports = {
+	"msrp",
+	DEFAULT_DLFLAGS, /* dlopen flags */
+	cmds,
+	params,
+	0,
+	0,              /* exported MI functions */
+	mod_pvs,        /* exported pseudo-variables */
+	0,              /* extra processes */
+	mod_init,       /* module initialization function */
+	0,              /* response function */
+	mod_destroy,    /* destroy function */
+	child_init      /* per child init function */
+};
+
+
+
+/**
+ * init module function
+ */
+static int mod_init(void)
+{
+	sr_event_register_cb(SREV_TCP_MSRP_FRAME, msrp_frame_received);
+	return 0;
+}
+
+/**
+ * @brief Initialize async module children
+ */
+static int child_init(int rank)
+{
+	if (rank!=PROC_MAIN)
+		return 0;
+
+	return 0;
+}
+/**
+ * destroy module function
+ */
+static void mod_destroy(void)
+{
+	return;
+}
+
+/**
+ *
+ */
+int mod_register(char *path, int *dlflags, void *p1, void *p2)
+{
+	return register_trans_mod(path, mod_trans);
+}
+
+/**
+ *
+ */
+static int w_msrp_relay(sip_msg_t* msg, char* str1, char* str2)
+{
+	msrp_frame_t *mf;
+	int ret;
+
+	mf = msrp_get_current_frame();
+	if(mf==NULL)
+		return -1;
+
+	ret = msrp_relay(mf);
+	if(ret==0) ret = 1;
+	return ret;
+}
+
+
+/**
+ *
+ */
+static int w_msrp_reply(struct sip_msg* msg, char* code, char* text,
+		char *hdrs)
+{
+	str rcode;
+	str rtext;
+	str rhdrs;
+	msrp_frame_t *mf;
+	int ret;
+
+	if(fixup_get_svalue(msg, (gparam_t*)code, &rcode)!=0)
+	{
+		LM_ERR("no reply status code\n");
+		return -1;
+	}
+
+	if(fixup_get_svalue(msg, (gparam_t*)text, &rtext)!=0)
+	{
+		LM_ERR("no reply status phrase\n");
+		return -1;
+	}
+
+	if(hdrs!=NULL && fixup_get_svalue(msg, (gparam_t*)hdrs, &rhdrs)!=0)
+	{
+		LM_ERR("invalid extra headers\n");
+		return -1;
+	}
+
+	mf = msrp_get_current_frame();
+	if(mf==NULL)
+		return -1;
+
+	ret = msrp_reply(mf, &rcode, &rtext, (hdrs!=NULL)?&rhdrs:NULL);
+	if(ret==0) ret = 1;
+	return ret;
+}
+
+/**
+ *
+ */
+static int w_msrp_reply2(sip_msg_t* msg, char* code, char* text)
+{
+	return w_msrp_reply(msg, code, text, NULL);
+}
+
+/**
+ *
+ */
+static int w_msrp_reply3(sip_msg_t* msg, char* code, char* text,
+		char *hdrs)
+{
+	return w_msrp_reply(msg, code, text, hdrs);
+}
+
+/**
+ *
+ */
+static int w_msrp_is_request(sip_msg_t* msg, char* str1, char* str2)
+{
+	msrp_frame_t *mf;
+	mf = msrp_get_current_frame();
+	if(mf==NULL)
+		return -1;
+	if(mf->fline.msgtypeid==MSRP_REQUEST)
+		return 1;
+	return -1;
+}
+
+/**
+ *
+ */
+static int w_msrp_is_reply(sip_msg_t* msg, char* str1, char* str2)
+{
+	msrp_frame_t *mf;
+	mf = msrp_get_current_frame();
+	if(mf==NULL)
+		return -1;
+	if(mf->fline.msgtypeid==MSRP_REPLY)
+		return 1;
+	return -1;
+}
+
+/**
+ *
+ */
+static int w_msrp_set_dst(sip_msg_t* msg, char* taddr, char* fsock)
+{
+	str rtaddr  = {0};
+	str rfsock = {0};
+	msrp_frame_t *mf;
+	int ret;
+
+	if(fixup_get_svalue(msg, (gparam_t*)taddr, &rtaddr)!=0)
+	{
+		LM_ERR("invalid target address parameter\n");
+		return -1;
+	}
+
+	if(fixup_get_svalue(msg, (gparam_t*)fsock, &rfsock)!=0)
+	{
+		LM_ERR("invalid local socket parameter\n");
+		return -1;
+	}
+
+
+	mf = msrp_get_current_frame();
+	if(mf==NULL)
+		return -1;
+
+	ret = msrp_env_set_dstinfo(mf, &rtaddr, &rfsock, 0);
+	if(ret==0) ret = 1;
+	return ret;
+}
+
+/**
+ *
+ */
+static int w_msrp_relay_flags(sip_msg_t* msg, char *tflags, char* str2)
+{
+	int rtflags = 0;
+	msrp_frame_t *mf;
+	int ret;
+	if(fixup_get_ivalue(msg, (gparam_t*)tflags, &rtflags)!=0)
+	{
+		LM_ERR("invalid send flags parameter\n");
+		return -1;
+	}
+
+	mf = msrp_get_current_frame();
+	if(mf==NULL)
+		return -1;
+
+	ret = msrp_env_set_sndflags(mf, rtflags);
+	if(ret==0) ret = 1;
+	return ret;
+}
+
+/**
+ *
+ */
+static int w_msrp_reply_flags(sip_msg_t* msg, char *tflags, char* str2)
+{
+	int rtflags = 0;
+	msrp_frame_t *mf;
+	int ret;
+	if(fixup_get_ivalue(msg, (gparam_t*)tflags, &rtflags)!=0)
+	{
+		LM_ERR("invalid send flags parameter\n");
+		return -1;
+	}
+
+	mf = msrp_get_current_frame();
+	if(mf==NULL)
+		return -1;
+
+	ret = msrp_env_set_rplflags(mf, rtflags);
+	if(ret==0) ret = 1;
+	return ret;
+}
+
+/**
+ *
+ */
+static int msrp_frame_received(void *data)
+{
+	tcp_event_info_t *tev;
+	static msrp_frame_t mf;
+	msrp_uri_t uri;
+	sip_msg_t *fmsg;
+	struct run_act_ctx ctx;
+	int rtb, rt;
+
+
+	tev = (tcp_event_info_t*)data;
+
+	if(tev==NULL || tev->buf==NULL || tev->len<=0)
+	{
+		LM_DBG("invalid parameters\n");
+		return -1;
+	}
+
+	memset(&mf, 0, sizeof(msrp_frame_t));
+	mf.buf.s = tev->buf;
+	mf.buf.len = tev->len;
+	mf.tcpinfo = tev;
+	if(msrp_parse_frame(&mf)<0)
+	{
+		LM_ERR("error parsing msrp frame\n");
+		return -1;
+	}
+	msrp_reset_env();
+	msrp_set_current_frame(&mf);
+	rt = route_get(&event_rt, "msrp:frame-in");
+	if(rt>=0 && event_rt.rlist[rt]!=NULL) {
+		LM_DBG("executing event_route[msrp:frame-in] (%d)\n", rt);
+		fmsg = msrp_fake_sipmsg(&mf);
+		if(fmsg!=NULL)
+			fmsg->rcv = *tev->rcv;
+		rtb = get_route_type();
+		set_route_type(REQUEST_ROUTE);
+		init_run_actions_ctx(&ctx);
+		run_top_route(event_rt.rlist[rt], fmsg, &ctx);
+		if(ctx.run_flags&DROP_R_F)
+		{
+			LM_DBG("exit due to 'drop' in event route\n");
+		}
+		set_route_type(rtb);
+		if(fmsg!=NULL)
+			free_sip_msg(fmsg);
+	}
+	msrp_reset_env();
+	msrp_destroy_frame(&mf);
+	msrp_parse_uri("msrps://alice.example.com:9892/98cjs;tcp",
+			sizeof("msrps://alice.example.com:9892/98cjs;tcp")-1, &uri);
+	msrp_parse_uri("msrps://[email protected]:9892/98cjs;tcp;a=123",
+			sizeof("msrps://[email protected]:9892/98cjs;tcp;a=123")-1, &uri);
+
+	return 0;
+}

+ 345 - 0
modules/msrp/msrp_netio.c

@@ -0,0 +1,345 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2012 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../dprint.h"
+#include "../../ut.h"
+#include "../../cfg_core.h"
+#include "../../tcp_server.h"
+#include "../../forward.h"
+
+#include "msrp_env.h"
+#include "msrp_netio.h"
+
+/**
+ *
+ */
+int msrp_forward_frame(msrp_frame_t *mf, int flags)
+{
+#if 0
+	if ((msrp_uri_to_dstinfo(0, &dst, uac_r->dialog->send_sock, snd_flags,
+						uac_r->dialog->hooks.next_hop, PROTO_NONE)==0) ||
+				(dst.send_sock==0)){
+			LOG(L_ERR, "no send socket found\n");
+			return -1;
+		}
+#endif
+	return 0;
+}
+
+/**
+ *
+ */
+int msrp_send_buffer(str *buf, str *addr, int flags)
+{
+	return 0;
+}
+
+/**
+ *
+ */
+int msrp_relay(msrp_frame_t *mf)
+{
+	struct dest_info *dst;
+	char reqbuf[MSRP_MAX_FRAME_SIZE];
+	msrp_hdr_t *tpath;
+	msrp_hdr_t *fpath;
+	msrp_env_t *env;
+	str_array_t *sar;
+	char *p;
+	char *l;
+
+	if(mf->buf.len>=MSRP_MAX_FRAME_SIZE-1)
+		return -1;
+
+	tpath = msrp_get_hdr_by_id(mf, MSRP_HDR_TO_PATH);
+	if(tpath==NULL)
+	{
+		LM_ERR("To-Path header not found\n");
+		return -1;
+	}
+	fpath = msrp_get_hdr_by_id(mf, MSRP_HDR_FROM_PATH);
+	if(fpath==NULL)
+	{
+		LM_ERR("From-Path header not found\n");
+		return -1;
+	}
+
+	l = q_memchr(tpath->body.s, ' ', tpath->body.len);
+	if(l==NULL)
+	{
+		LM_DBG("To-Path has only one URI -- nowehere to forward\n");
+		return -1;
+	}
+
+	p = reqbuf;
+
+	memcpy(p, mf->buf.s, tpath->body.s - mf->buf.s);
+	p += tpath->body.s - mf->buf.s;
+
+	memcpy(p, l + 1, fpath->body.s - l - 1);
+	p += fpath->body.s - l - 1;
+
+	memcpy(p, tpath->body.s, l + 1 - tpath->body.s);
+	p += l + 1 - tpath->body.s;
+
+	memcpy(p, fpath->name.s + 2, mf->buf.s + mf->buf.len - fpath->name.s - 2);
+	p += mf->buf.s + mf->buf.len - fpath->name.s - 2;
+
+
+	env = msrp_get_env();
+	if(env->envflags&MSRP_ENV_DSTINFO)
+	{
+		dst = &env->dstinfo;
+		goto done;
+	}
+	if(msrp_parse_hdr_to_path(mf)<0)
+	{
+		LM_ERR("error parsing To-Path header\n");
+		return -1;
+	}
+	sar = (str_array_t*)tpath->parsed.data;
+	if(sar==NULL || sar->size<2)
+	{
+		LM_DBG("To-Path has no next hop URI -- nowehere to forward\n");
+		return -1;
+	}
+	if(msrp_env_set_dstinfo(mf, &sar->list[1], NULL, 0)<0)
+	{
+		LM_ERR("unable to set destination address\n");
+		return -1;
+	}
+	dst = &env->dstinfo;
+done:
+	if (tcp_send(dst, 0, reqbuf, p - reqbuf) < 0) {
+		LM_ERR("forwarding frame failed\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ *
+ */
+int msrp_reply(msrp_frame_t *mf, str *code, str *text, str *xhdrs)
+{
+	char rplbuf[MSRP_MAX_FRAME_SIZE];
+	msrp_hdr_t *hdr;
+	msrp_env_t *env;
+	char *p;
+	char *l;
+
+	/* no reply for a reply */
+	if(mf->fline.msgtypeid==MSRP_REPLY)
+		return 0;
+
+	if(mf->fline.msgtypeid==MSRP_REQ_REPORT)
+	{
+		/* it does not take replies */
+		return 0;
+	}
+
+	p = rplbuf;
+	memcpy(p, mf->fline.protocol.s, mf->fline.protocol.len);
+	p += mf->fline.protocol.len;
+	*p = ' '; p++;
+	memcpy(p, mf->fline.transaction.s, mf->fline.transaction.len);
+	p += mf->fline.transaction.len;
+	*p = ' '; p++;
+	memcpy(p, code->s, code->len);
+	p += code->len;
+	*p = ' '; p++;
+	memcpy(p, text->s, text->len);
+	p += text->len;
+	memcpy(p, "\r\n", 2);
+	p += 2;
+	memcpy(p, "To-Path: ", 9);
+	p += 9;
+	hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_FROM_PATH);
+	if(hdr==NULL)
+	{
+		LM_ERR("From-Path header not found\n");
+		return -1;
+	}
+	if(mf->fline.msgtypeid==MSRP_REQ_SEND)
+	{
+		l = q_memchr(hdr->body.s, ' ', hdr->body.len);
+		if(l==NULL) {
+			memcpy(p, hdr->body.s, hdr->body.len + 2);
+			p += hdr->body.len + 2;
+		} else {
+			memcpy(p, hdr->body.s, l - hdr->body.s);
+			p += l - hdr->body.s;
+			memcpy(p, "\r\n", 2);
+			p += 2;
+		}
+	} else {
+		memcpy(p, hdr->body.s, hdr->body.len + 2);
+		p += hdr->body.len + 2;
+	}
+	hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_TO_PATH);
+	if(hdr==NULL)
+	{
+		LM_ERR("To-Path header not found\n");
+		return -1;
+	}
+	memcpy(p, "From-Path: ", 11);
+	p += 11;
+	l = q_memchr(hdr->body.s, ' ', hdr->body.len);
+	if(l==NULL) {
+		memcpy(p, hdr->body.s, hdr->body.len + 2);
+		p += hdr->body.len + 2;
+	} else {
+		memcpy(p, hdr->body.s, l - hdr->body.s);
+		p += l - hdr->body.s;
+		memcpy(p, "\r\n", 2);
+		p += 2;
+	}
+	hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_MESSAGE_ID);
+	if(hdr!=NULL)
+	{
+		memcpy(p, hdr->buf.s, hdr->buf.len);
+		p += hdr->buf.len;
+	}
+
+	if(xhdrs!=NULL && xhdrs->s!=NULL)
+	{
+		memcpy(p, xhdrs->s, xhdrs->len);
+		p += xhdrs->len;
+	}
+
+	memcpy(p, mf->endline.s, mf->endline.len);
+	p += mf->endline.len;
+	*(p-3) = '$';
+
+	env = msrp_get_env();
+	if (tcp_send(&env->srcinfo, 0, rplbuf, p - rplbuf) < 0) {
+		LM_ERR("sending reply failed\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ *
+ */
+struct dest_info *msrp_uri_to_dstinfo(struct dns_srv_handle* dns_h,
+		struct dest_info* dst, struct socket_info *force_send_socket,
+		snd_flags_t sflags, str *uri)
+{
+	msrp_uri_t parsed_uri;
+	str* host;
+	int port;
+	int ip_found;
+	union sockaddr_union to;
+	int err;
+
+	if (msrp_parse_uri(uri->s, uri->len, &parsed_uri) < 0) {
+		LM_ERR("bad msrp uri: %.*s\n", uri->len, uri->s );
+		return 0;
+	}
+	
+	if (parsed_uri.scheme_no==MSRP_SCHEME_MSRPS){
+		dst->proto = PROTO_TLS;
+	} else {
+		dst->proto = PROTO_TCP;
+	}
+	
+	init_dest_info(dst);
+	dst->send_flags=sflags;
+	host=&parsed_uri.host;
+	port = parsed_uri.port_no;
+
+	if (dns_h && cfg_get(core, core_cfg, use_dns_failover)){
+		ip_found=0;
+		do{
+			/* try all the ips until we find a good send socket */
+			err=dns_sip_resolve2su(dns_h, &to, host,
+								port, &dst->proto, dns_flags);
+			if (err!=0){
+				if (ip_found==0){
+					if (err!=-E_DNS_EOR)
+						LM_ERR("failed to resolve \"%.*s\" :"
+								"%s (%d)\n", host->len, ZSW(host->s),
+									dns_strerror(err), err);
+					return 0; /* error, no ip found */
+				}
+				break;
+			}
+			if (ip_found==0){
+				dst->to=to;
+				ip_found=1;
+			}
+			dst->send_sock = get_send_socket2(force_send_socket, &to,
+												dst->proto, 0);
+			if (dst->send_sock){
+				dst->to=to;
+				return dst; /* found a good one */
+			}
+		} while(dns_srv_handle_next(dns_h, err));
+		ERR("no corresponding socket for \"%.*s\" af %d\n", host->len, 
+				ZSW(host->s), dst->to.s.sa_family);
+		/* try to continue */
+		return dst;
+	}
+
+	if (sip_hostport2su(&dst->to, host, port, &dst->proto)!=0){
+		ERR("failed to resolve \"%.*s\"\n", host->len, ZSW(host->s));
+		return 0;
+	}
+	dst->send_sock = get_send_socket2(force_send_socket, &dst->to,
+			dst->proto, 0);
+	if (dst->send_sock==0) {
+		ERR("no corresponding socket for af %d\n", dst->to.s.sa_family);
+		/* try to continue */
+	}
+	return dst;
+}
+
+struct socket_info *msrp_get_local_socket(str *sockaddr)
+{
+	int port, proto;
+	str host;
+	char backup;
+	struct socket_info *si;
+
+	backup = sockaddr->s[sockaddr->len];
+	sockaddr->s[sockaddr->len] = '\0';
+	if (parse_phostport(sockaddr->s, &host.s, &host.len, &port, &proto) < 0)
+	{
+		LM_ERR("invalid socket specification\n");
+		sockaddr->s[sockaddr->len] = backup;
+		return NULL;
+	}
+	sockaddr->s[sockaddr->len] = backup;
+	si = grep_sock_info(&host, (unsigned short)port, (unsigned short)proto);
+	return si;
+}

+ 43 - 0
modules/msrp/msrp_netio.h

@@ -0,0 +1,43 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2012 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef _MSRP_NETIO_H_
+#define _MSRP_NETIO_H_
+
+#include "../../dns_cache.h"
+
+#include "msrp_parser.h"
+
+int msrp_forward_frame(msrp_frame_t *mf, int flags);
+int msrp_send_buffer(str *buf, str *addr, int flags);
+
+int msrp_relay(msrp_frame_t *mf);
+int msrp_reply(msrp_frame_t *mf, str *code, str *text, str *xhdrs);
+
+struct dest_info *msrp_uri_to_dstinfo(struct dns_srv_handle* dns_h,
+		struct dest_info* dst, struct socket_info *force_send_socket,
+		snd_flags_t sflags, str *uri);
+struct socket_info *msrp_get_local_socket(str *sockaddr);
+
+#endif

+ 698 - 0
modules/msrp/msrp_parser.c

@@ -0,0 +1,698 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2012 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../dprint.h"
+#include "../../ut.h"
+#include "../../trim.h"
+#include "../../mem/mem.h"
+
+#include "msrp_parser.h"
+
+typedef struct msrp_str_id {
+	str sval;
+	int ival;
+} msrp_str_id_t;
+
+
+static msrp_str_id_t _msrp_rtypes[] = {
+	{ str_init("SEND"),      MSRP_REQ_SEND },
+	{ str_init("AUTH"),      MSRP_REQ_AUTH },
+	{ str_init("REPORT"),    MSRP_REQ_REPORT },
+	{ {0, 0}, 0}
+};
+
+
+static msrp_str_id_t _msrp_htypes[] = {
+	{ str_init("From-Path"),            MSRP_HDR_FROM_PATH },
+	{ str_init("To-Path"),              MSRP_HDR_TO_PATH },
+	{ str_init("Use-Path"),             MSRP_HDR_USE_PATH },
+	{ str_init("Message-ID"),           MSRP_HDR_MESSAGE_ID },
+	{ str_init("Byte-Range"),           MSRP_HDR_BYTE_RANGE },
+	{ str_init("Status"),               MSRP_HDR_STATUS },
+	{ str_init("Success-Report"),       MSRP_HDR_SUCCESS_REPORT },
+	{ str_init("Content-Type"),         MSRP_HDR_CONTENT_TYPE },
+	{ str_init("Authorization"),        MSRP_HDR_AUTH },
+	{ str_init("WWW-Authenticate"),     MSRP_HDR_WWWAUTH },
+	{ str_init("Authentication-Info"),  MSRP_HDR_AUTHINFO },
+	{ {0, 0}, 0}
+};
+
+
+/* */
+int msrp_fline_set_rtypeid(msrp_frame_t *mf);
+int msrp_hdr_set_type(msrp_hdr_t *hdr);
+
+/**
+ *
+ */
+int msrp_parse_frame(msrp_frame_t *mf)
+{
+	if(msrp_parse_fline(mf)<0)
+	{
+		LM_ERR("unable to parse first line\n");
+		return -1;
+	}
+	if(msrp_parse_headers(mf)<0)
+	{
+		LM_ERR("unable to parse first line\n");
+		return -1;
+	}
+	return 0;
+}
+
+/**
+ *
+ */
+int msrp_parse_fline(msrp_frame_t *mf)
+{
+	char *p;
+	char *s;
+
+	mf->fline.buf.s = mf->buf.s;
+	s = mf->buf.s;
+	p =  q_memchr(mf->fline.buf.s, '\n', mf->buf.len);
+	if(p==NULL) {
+		LM_ERR("no end of line\n");
+		return -1;
+	}
+	mf->fline.buf.len = p - mf->fline.buf.s + 1;
+
+	if(mf->fline.buf.len<10) {
+		LM_ERR("too short for a valid first line [%.*s] (%d)\n",
+				mf->fline.buf.len, mf->fline.buf.s, mf->fline.buf.len);
+		return -1;
+	}
+	if(memcmp(mf->fline.buf.s, "MSRP ", 5)!=0) {
+		LM_ERR("first line does not start with MSRP [%.*s] (%d)\n",
+				mf->fline.buf.len, mf->fline.buf.s, mf->fline.buf.len);
+		return -1;
+	}
+	mf->fline.protocol.s = mf->fline.buf.s;
+	mf->fline.protocol.len = 4;
+	s += 5;
+	p =  q_memchr(s, ' ', mf->fline.buf.s + mf->fline.buf.len - s);
+	/* eat whitespaces */
+	while (p!=NULL && p==s) {
+		s++;
+		p =  q_memchr(s, ' ', mf->fline.buf.s + mf->fline.buf.len - s);
+	}
+	if(p==NULL) {
+		LM_ERR("cannot find transaction id in first line [%.*s] (%d)\n",
+				mf->fline.buf.len, mf->fline.buf.s, mf->fline.buf.len);
+		return -1;
+	}
+	mf->fline.transaction.s = s;
+	mf->fline.transaction.len = p - s;
+	s = p+1;
+	p =  q_memchr(s, ' ', mf->fline.buf.s + mf->fline.buf.len - s);
+	/* eat whitespaces */
+	while (p!=NULL && p==s) {
+		s++;
+		p =  q_memchr(s, ' ', mf->fline.buf.s + mf->fline.buf.len - s);
+	}
+	if(p==NULL) {
+		if(s>=mf->fline.buf.s + mf->fline.buf.len - 2) {
+			LM_ERR("cannot method in first line [%.*s] (%d)\n",
+					mf->fline.buf.len, mf->fline.buf.s, mf->fline.buf.len);
+			return -1;
+		}
+		mf->fline.rtype.s = s;
+		mf->fline.rtype.len = mf->fline.buf.s + mf->fline.buf.len - s;
+		trim(&mf->fline.rtype);
+		mf->fline.msgtypeid = MSRP_REQUEST;
+		goto done;
+	}
+	mf->fline.rtype.s = s;
+	mf->fline.rtype.len = p - s;
+	s = p+1;
+	mf->fline.rtext.s = s;
+	mf->fline.rtext.len = mf->fline.buf.s + mf->fline.buf.len - s;
+	trim(&mf->fline.rtext);
+	mf->fline.msgtypeid = MSRP_REPLY;
+
+done:
+	if(msrp_fline_set_rtypeid(mf)<0)
+	{
+		LM_ERR("cannot set rtype-id in first line [%.*s] (%d)\n",
+			mf->fline.buf.len, mf->fline.buf.s, mf->fline.buf.len);
+		return -1;
+	}
+	LM_DBG("MSRP FLine: [%d] [%.*s] [%.*s] [%.*s] [%d] [%.*s]\n",
+			mf->fline.msgtypeid,
+			mf->fline.protocol.len, mf->fline.protocol.s,
+			mf->fline.transaction.len, mf->fline.transaction.s,
+			mf->fline.rtype.len, mf->fline.rtype.s,
+			mf->fline.rtypeid,
+			mf->fline.rtext.len, mf->fline.rtext.s);
+	return 0;
+}
+
+/**
+ *
+ */
+int msrp_fline_set_rtypeid(msrp_frame_t *mf)
+{
+	unsigned int i;
+	if(mf->fline.msgtypeid==MSRP_REPLY)
+	{
+		if(str2int(&mf->fline.rtype, &i)<0)
+		{
+			LM_ERR("invalid status code [%.*s]\n",
+					mf->fline.rtype.len, mf->fline.rtype.s);
+			return -1;
+		}
+		mf->fline.rtypeid = MSRP_REQ_RPLSTART + i;
+		return 0;
+	}
+	if(mf->fline.msgtypeid==MSRP_REQUEST)
+	{
+		for(i=0; _msrp_rtypes[i].sval.s!=NULL; i++)
+		{
+			if(mf->fline.rtype.len==_msrp_rtypes[i].sval.len
+					&& strncmp(_msrp_rtypes[i].sval.s,
+						mf->fline.rtype.s,
+						_msrp_rtypes[i].sval.len)==0)
+			{
+				mf->fline.rtypeid = _msrp_rtypes[i].ival;
+				return 0;
+			}
+		}
+		return 0;
+	}
+	return -1;
+}
+
+/**
+ *
+ */
+int msrp_parse_headers(msrp_frame_t *mf)
+{
+	char *e; /* end of message */
+	char *l; /* end of line */
+	char *p; /* searched location */
+	char *s; /* start for search */
+	msrp_hdr_t *hdr;
+	msrp_hdr_t *last;
+
+	/* already parsed?!? */
+	if(mf->headers != NULL)
+		return 0;
+
+	last = NULL;
+	mf->hbody.s = mf->fline.buf.s + mf->fline.buf.len;
+	e = mf->buf.s + mf->buf.len;
+	s = mf->hbody.s;
+	p = s;
+	while(p!=NULL)
+	{
+		l = q_memchr(s, '\n', e - s);
+		if(l==NULL) {
+			LM_ERR("broken msrp frame message\n");
+			return -1;
+		}
+		p = q_memchr(s, ':', l - s);
+		if(p==NULL)
+		{
+			/* line without ':' - end of headers */
+			if(s[0]=='-')
+			{
+				mf->endline.len = 7 + mf->fline.transaction.len + 1 + 2;
+				/* check if it is end-line (i.e., no msg body) */
+				if((l-s+1 == mf->endline.len)
+						&& strncmp(s, "-------", 7)==0
+						&& strncmp(s+7, mf->fline.transaction.s,
+							mf->fline.transaction.len)==0)
+				{
+					mf->hbody.len = s - mf->hbody.s;
+					mf->endline.s = s;
+					goto ateoh;
+				}
+				mf->endline.len = 0;
+				LM_ERR("mismatch msrp frame message eoh endline\n");
+				return -1;
+			} else if (s[0]=='\r' || s[0]=='\n') {
+				mf->hbody.len = s - mf->hbody.s;
+				mf->mbody.s = l + 1;
+				goto ateoh;
+			}
+			LM_ERR("broken msrp frame message eoh\n");
+			return -1;
+		}
+		/* new header */
+		hdr = (msrp_hdr_t*)pkg_malloc(sizeof(msrp_hdr_t));
+		if(hdr==NULL)
+		{
+			LM_ERR("no more pkg\n");
+			return -1;
+		}
+		memset(hdr, 0, sizeof(msrp_hdr_t));
+		hdr->buf.s = s;
+		hdr->buf.len = l - s + 1;
+		hdr->name.s = s;
+		hdr->name.len = p - s;
+		hdr->body.s = p + 1;
+		hdr->body.len = l - p - 1;
+		trim(&hdr->body);
+
+		if(last==NULL)
+		{
+			mf->headers = hdr;
+			last = hdr;
+		} else {
+			last->next = hdr;
+			last = hdr;
+		}
+		msrp_hdr_set_type(hdr);
+	
+		LM_DBG("MSRP Header: (%p) [%.*s] [%d] [%.*s]\n",
+				hdr, hdr->name.len, hdr->name.s, hdr->htype,
+				hdr->body.len, hdr->body.s);
+		s = l + 1;
+	}
+
+ateoh:
+	if(mf->mbody.s!=NULL)
+	{
+		/* last header must be Content-Type */
+
+		/* get now body.len and endline */
+		mf->endline.len = 7 + mf->fline.transaction.len + 1 + 2;
+		mf->endline.s = e - mf->endline.len;
+		s = mf->endline.s;
+		if(s[-1]!='\n')
+		{
+			LM_ERR("broken msrp frame message body endline\n");
+			return -1;
+		}
+		if(strncmp(s, "-------", 7)==0
+				&& strncmp(s+7, mf->fline.transaction.s,
+				mf->fline.transaction.len)==0)
+		{
+			mf->mbody.len = s - mf->mbody.s;
+			LM_DBG("MSRP Body: [%d] [[\n%.*s\n]]\n",
+					mf->mbody.len, mf->mbody.len, mf->mbody.s);
+			return 0;
+		}
+		LM_ERR("mismatch msrp frame message body endline\n");
+		return -1;
+
+	}
+	return 0;
+}
+
+/**
+ *
+ */
+int msrp_hdr_set_type(msrp_hdr_t *hdr)
+{
+	int i;
+	if(hdr==NULL)
+		return -1;
+
+	for(i=0; _msrp_htypes[i].sval.s!=NULL; i++)
+	{
+		if(hdr->name.len==_msrp_htypes[i].sval.len
+				&& strncmp(_msrp_htypes[i].sval.s,
+					hdr->name.s,
+					_msrp_htypes[i].sval.len)==0)
+		{
+			hdr->htype = _msrp_htypes[i].ival;
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/**
+ *
+ */
+void msrp_destroy_frame(msrp_frame_t *mf)
+{
+	msrp_hdr_t *hdr;
+	msrp_hdr_t *nxt;
+
+	if(mf==NULL || mf->headers==NULL)
+		return;
+
+	hdr = mf->headers;
+	while(hdr!=NULL)
+	{
+		nxt = hdr->next;
+		if(hdr->parsed.flags&MSRP_DATA_SET)
+		{
+			if(hdr->parsed.free_fn)
+			{
+				hdr->parsed.free_fn(hdr->parsed.data);
+			}
+		}
+		pkg_free(hdr);
+		hdr = NULL;
+		hdr = nxt;
+	}
+
+	return;
+}
+
+/**
+ *
+ */
+void msrp_free_frame(msrp_frame_t *mf)
+{
+	msrp_destroy_frame(mf);
+	pkg_free(mf);
+	return;
+}
+
+/**
+ *
+ */
+int msrp_parse_uri(char *start, int len, msrp_uri_t *uri)
+{
+	char *e;
+	char *l;
+	char *p;
+	char *s;
+	str *hook;
+
+	if(start==NULL || uri==NULL || len<8)
+	{
+		LM_ERR("invalid parameters\n");
+		return -1;
+	}
+
+	memset(uri, 0, sizeof(msrp_uri_t));
+	e = start + len;
+	s = start;
+	uri->buf.s = s;
+	uri->scheme.s = s;
+
+	if(strncasecmp(s, "msrp://", 7)==0) {
+		uri->scheme.len = 4;
+		uri->scheme_no = MSRP_SCHEME_MSRP;
+		s += 7;
+	} else if(strncasecmp(s, "msrps://", 8)==0) {
+		uri->scheme.len = 5;
+		uri->scheme_no = MSRP_SCHEME_MSRPS;
+		s += 8;
+	} else {
+		LM_ERR("invalid scheme in [%.*s]\n", len, start);
+		goto error;
+	}
+
+	p = q_memchr(s, '@', e - s);
+	if(p!=NULL)
+	{
+		l = q_memchr(s, ';', e - s);
+		if(l==NULL || p<l)
+		{
+			/* user info part */
+			uri->userinfo.s = s;
+			uri->userinfo.len = p - s;
+			uri->user.s = s;
+			l = q_memchr(uri->userinfo.s, ';', uri->userinfo.len);
+			if(l!=NULL)
+			{
+				uri->user.len = l - uri->user.s;
+			} else {
+				l = q_memchr(uri->userinfo.s, ':', uri->userinfo.len);
+				if(l!=NULL)
+				{
+					uri->user.len = l - uri->user.s;
+				} else {
+					uri->user.len = uri->userinfo.len;
+				}
+			}
+		}
+		s = p + 1;
+		if(s>=e) goto error;
+	}
+	hook = &uri->host;
+	hook->s = s;
+	p = q_memchr(s, ':', e - s);
+	if(p!=NULL)
+	{
+		hook->len = p - hook->s;
+		hook = &uri->port;
+		s = p+1;
+		if(s>=e) goto error;
+	}
+	hook->s = s;
+	p = q_memchr(s, '/', e - s);
+	if(p!=NULL)
+	{
+		hook->len = p - hook->s;
+		hook = &uri->session;
+		s = p+1;
+		if(s>=e) goto error;
+	}
+	hook->s = s;
+	p = q_memchr(s, ';', e - s);
+	if(p!=NULL)
+	{
+		hook->len = p - hook->s;
+		hook = &uri->params;
+		s = p+1;
+		if(s>=e) goto error;
+	}
+	hook->s = s;
+	hook->len = e - hook->s;
+	trim(hook);
+
+	if(uri->host.len<=0)
+	{
+		LM_ERR("bad host part in [%.*s] at [%ld]\n",
+				len, start, s - start);
+		goto error;
+	}
+	if(uri->port.len <= 0)
+	{
+		uri->port_no = 0;
+	} else {
+		str2sint(&uri->port, &uri->port_no);
+		if(uri->port_no<1 || uri->port_no > ((1<<16)-1))
+		{
+			LM_ERR("bad port part in [%.*s] at [%ld]\n",
+					len, start, s - start);
+			goto error;
+		}
+	}
+	if(uri->params.len > 0)
+	{
+		uri->proto.s = uri->params.s;
+		if(uri->params.len > 3 && strncasecmp(uri->params.s, "tcp", 3)==0)
+		{
+			uri->proto.len = 3;
+			uri->proto_no = MSRP_PROTO_TCP;
+		} else {
+			p = q_memchr(uri->params.s, ';', uri->params.len);
+			if(p!=NULL) {
+				uri->proto.len = p - uri->proto.s;
+				uri->params.len = uri->params.s + uri->params.len - p - 1;
+				uri->params.s = p + 1;
+			} else {
+				uri->proto.len = uri->params.len;
+				uri->params.s = NULL;
+				uri->params.len = 0;
+			}
+		}
+	}
+
+	LM_DBG("MSRP URI: [%.*s] [%.*s] [%.*s] [%.*s] [%.*s] [%.*s] [%.*s]\n",
+			uri->scheme.len, uri->scheme.s,
+			uri->user.len, (uri->user.s)?uri->user.s:"",
+			uri->host.len, uri->host.s,
+			uri->port.len, (uri->port.s)?uri->port.s:"",
+			uri->session.len, (uri->session.s)?uri->session.s:"",
+			uri->proto.len, (uri->proto.s)?uri->proto.s:"",
+			uri->params.len, (uri->params.s)?uri->params.s:"");
+	return 0;
+
+error:
+	LM_ERR("parsing error in [%.*s] at [%ld]\n",
+			len, start, s - start);
+	memset(uri, 0, sizeof(msrp_uri_t));
+	return -1;
+}
+
+/**
+ *
+ */
+msrp_hdr_t *msrp_get_hdr_by_id(msrp_frame_t *mf, int hdrid)
+{
+	msrp_hdr_t *hdr;
+	for(hdr=mf->headers; hdr; hdr=hdr->next)
+		if(hdr->htype==hdrid)
+			return hdr;
+	return NULL;
+}
+
+
+/**
+ *
+ */
+int msrp_explode_str(str **arr, str *in, str *del)
+{
+	str *larr;
+	int i;
+	int j;
+	int k;
+	int n;
+
+	/* Find number of strings */
+	n = 0;
+	for(i=0; i<in->len; i++)
+	{
+		for(j=0; j<del->len; j++)
+		{
+			if(in->s[i]==del->s[j])
+			{
+				n++;
+				break;
+			}
+		}
+	}
+	n++;
+
+	larr = pkg_malloc(n * sizeof(str));
+	if(larr==NULL)
+	{
+		LM_ERR("no more pkg\n");
+		return -1;
+	}
+	memset(larr, 0, n * sizeof(str));
+
+	k = 0;
+	if(n==1)
+	{
+		larr[k].s = in->s;
+		larr[k].len = in->len;
+		*arr = larr;
+		return n;
+	}
+
+	larr[k].s = in->s;
+	for(i=0; i<in->len; i++)
+	{
+		for(j=0; j<del->len; j++)
+		{
+			if(in->s[i]==del->s[j])
+			{
+				larr[k].len = in->s + i - larr[k].s;
+				k++;
+				if(k<n)
+					larr[k].s = in->s + i + 1;
+				break;
+			}
+		}
+	}
+	larr[k].len = in->s + i - larr[k].s;
+
+	*arr = larr;
+
+	return n;
+}
+
+/**
+ *
+ */
+int msrp_explode_strz(str **arr, str *in, char *del)
+{
+	str s;
+
+	s.s = del;
+	s.len = strlen(s.s);
+	return msrp_explode_str(arr, in, &s);
+}
+
+void msrp_str_array_destroy(void *data)
+{
+	str_array_t *arr;
+	if(data==NULL)
+		return;
+	arr = (str_array_t*)data;
+	if(arr->list!=NULL)
+			pkg_free(arr->list);
+	pkg_free(arr);
+}
+
+/**
+ *
+ */
+int msrp_parse_hdr_uri_list(msrp_hdr_t *hdr)
+{
+	str_array_t *arr;
+	str s;
+
+	arr = pkg_malloc(sizeof(str_array_t));
+	if(arr==NULL)
+	{
+		LM_ERR("no more pkg\n");
+		return -1;
+	}
+	memset(arr, 0, sizeof(str_array_t));
+
+	s = hdr->body;
+	trim(&s);
+	arr->size = msrp_explode_strz(&arr->list, &s, " ");
+	hdr->parsed.flags |= MSRP_DATA_SET;
+	hdr->parsed.free_fn = msrp_str_array_destroy;
+	hdr->parsed.data = arr;
+	return 0;
+}
+
+/**
+ *
+ */
+int msrp_parse_hdr_from_path(msrp_frame_t *mf)
+{
+	msrp_hdr_t *hdr;
+
+	hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_FROM_PATH);
+	if(hdr==NULL)
+		return -1;
+	if(hdr->parsed.flags&MSRP_DATA_SET)
+		return 0;
+	return msrp_parse_hdr_uri_list(hdr);
+}
+
+/**
+ *
+ */
+int msrp_parse_hdr_to_path(msrp_frame_t *mf)
+{
+	msrp_hdr_t *hdr;
+
+	hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_TO_PATH);
+	if(hdr==NULL)
+		return -1;
+	if(hdr->parsed.flags&MSRP_DATA_SET)
+		return 0;
+	return msrp_parse_hdr_uri_list(hdr);
+}
+

+ 138 - 0
modules/msrp/msrp_parser.h

@@ -0,0 +1,138 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2012 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef _MSRP_PARSER_H_
+#define _MSRP_PARSER_H_
+
+#include "../../str.h"
+#include "../../tcp_conn.h"
+
+#define MSRP_REQUEST	1
+#define MSRP_REPLY		2
+
+#define MSRP_PORT 2855
+
+#define MSRP_MAX_HDRS_SIZE	4096
+#define MSRP_MAX_BODY_SIZE	10240
+#define MSRP_MAX_FRAME_SIZE (MSRP_MAX_HDRS_SIZE + 2 + MSRP_MAX_BODY_SIZE)
+
+#define MSRP_REQ_OTHER		0
+#define MSRP_REQ_SEND		1
+#define MSRP_REQ_AUTH		2
+#define MSRP_REQ_REPORT		3
+
+#define MSRP_REQ_RPLSTART	10000
+
+typedef struct msrp_fline {
+	str buf;
+	int msgtypeid;
+	str protocol;
+	str transaction;
+	str rtype;
+	int rtypeid;
+	str rtext;
+} msrp_fline_t;
+
+#define MSRP_SCHEME_MSRP	1
+#define MSRP_SCHEME_MSRPS	2
+
+#define MSRP_PROTO_TCP	1
+
+typedef struct msrp_uri {
+	str buf;
+	str scheme;
+	int scheme_no;
+	str userinfo;
+	str user;
+	str host;
+	str port;
+	int port_no;
+	str session;
+	str proto;
+	int proto_no;
+	str params;
+} msrp_uri_t;
+
+int msrp_parse_uri(char *start, int len, msrp_uri_t *uri);
+
+#define MSRP_HDR_OTHER			0
+#define MSRP_HDR_FROM_PATH		1
+#define MSRP_HDR_TO_PATH		2
+#define MSRP_HDR_USE_PATH		3
+#define MSRP_HDR_MESSAGE_ID		4
+#define MSRP_HDR_BYTE_RANGE		5
+#define MSRP_HDR_STATUS			6
+#define MSRP_HDR_SUCCESS_REPORT	7
+#define MSRP_HDR_CONTENT_TYPE	8
+#define MSRP_HDR_AUTH			9
+#define MSRP_HDR_WWWAUTH		10
+#define MSRP_HDR_AUTHINFO		11
+
+#define MSRP_DATA_SET	1
+
+typedef struct msrp_data {
+	void (*free_fn)(void*);
+	int flags;
+	void *data;
+} msrp_data_t;
+
+typedef struct msrp_hdr {
+	str buf;
+	int htype;
+	str name;
+	str body;
+	msrp_data_t parsed;
+	struct msrp_hdr *next;
+} msrp_hdr_t;
+
+typedef struct msrp_frame {
+	str buf;             /* the whole message */
+	msrp_fline_t fline;  /* first line parsed */
+	str hbody;           /* all headers as a buf */
+	str mbody;           /* the message body */
+	str endline;         /* end line of the */
+	msrp_hdr_t *headers; /* list of parsed headers */
+	tcp_event_info_t *tcpinfo;
+} msrp_frame_t;
+
+int msrp_parse_frame(msrp_frame_t *mf);
+int msrp_parse_fline(msrp_frame_t *mf);
+int msrp_parse_headers(msrp_frame_t *mf);
+
+int msrp_parse_hdr_to_path(msrp_frame_t *mf);
+int msrp_parse_hdr_from_path(msrp_frame_t *mf);
+
+void msrp_destroy_frame(msrp_frame_t *mf);
+void msrp_free_frame(msrp_frame_t *mf);
+
+msrp_hdr_t *msrp_get_hdr_by_id(msrp_frame_t *mf, int hdrid);
+
+msrp_frame_t *msrp_get_current_frame(void);
+
+typedef struct str_array {
+	unsigned int size;
+	str *list;
+} str_array_t;
+
+#endif

+ 456 - 0
modules/msrp/msrp_vars.c

@@ -0,0 +1,456 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2012 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../dprint.h"
+#include "../../trim.h"
+#include "../../ut.h"
+#include "../../pvapi.h"
+
+#include "msrp_parser.h"
+#include "msrp_vars.h"
+
+/**
+ *
+ */
+int pv_parse_msrp_name(pv_spec_t *sp, str *in)
+{
+	if(sp==NULL || in==NULL || in->len<=0)
+		return -1;
+
+	switch(in->len)
+	{
+		case 3: 
+			if(strncmp(in->s, "buf", 3)==0)
+				sp->pvp.pvn.u.isname.name.n = 1;
+			else goto error;
+		break;
+		case 4: 
+			if(strncmp(in->s, "body", 4)==0)
+				sp->pvp.pvn.u.isname.name.n = 2;
+			else if(strncmp(in->s, "code", 4)==0)
+				sp->pvp.pvn.u.isname.name.n = 3;
+			else if(strncmp(in->s, "hdrs", 4)==0)
+				sp->pvp.pvn.u.isname.name.n = 4;
+			else goto error;
+		break;
+		case 5:
+			if(strncmp(in->s, "msgid", 5)==0)
+				sp->pvp.pvn.u.isname.name.n = 5;
+			else goto error;
+		case 6:
+			if(strncmp(in->s, "method", 6)==0)
+				sp->pvp.pvn.u.isname.name.n = 6;
+			else if(strncmp(in->s, "buflen", 6)==0)
+				sp->pvp.pvn.u.isname.name.n = 7;
+			else if(strncmp(in->s, "sessid", 6)==0)
+				sp->pvp.pvn.u.isname.name.n = 8;
+			else if(strncmp(in->s, "reason", 6)==0)
+				sp->pvp.pvn.u.isname.name.n = 9;
+			else if(strncmp(in->s, "crthop", 6)==0)
+				sp->pvp.pvn.u.isname.name.n = 12;
+			else goto error;
+		break;
+		case 7: 
+			if(strncmp(in->s, "bodylen", 7)==0)
+				sp->pvp.pvn.u.isname.name.n = 10;
+			else if(strncmp(in->s, "transid", 7)==0)
+				sp->pvp.pvn.u.isname.name.n = 11;
+			else if(strncmp(in->s, "prevhop", 7)==0)
+				sp->pvp.pvn.u.isname.name.n = 13;
+			else if(strncmp(in->s, "nexthop", 7)==0)
+				sp->pvp.pvn.u.isname.name.n = 14;
+			else if(strncmp(in->s, "lasthop", 7)==0)
+				sp->pvp.pvn.u.isname.name.n = 15;
+			else if(strncmp(in->s, "srcaddr", 7)==0)
+				sp->pvp.pvn.u.isname.name.n = 19;
+			else if(strncmp(in->s, "srcsock", 7)==0)
+				sp->pvp.pvn.u.isname.name.n = 20;
+			else goto error;
+		break;
+		case 8: 
+			if(strncmp(in->s, "firsthop", 8)==0)
+				sp->pvp.pvn.u.isname.name.n = 16;
+			else if(strncmp(in->s, "prevhops", 7)==0)
+				sp->pvp.pvn.u.isname.name.n = 17;
+			else if(strncmp(in->s, "nexthops", 7)==0)
+				sp->pvp.pvn.u.isname.name.n = 18;
+			else goto error;
+		break;
+		default:
+			/* max is 20 */
+			goto error;
+	}
+	sp->pvp.pvn.type = PV_NAME_INTSTR;
+	sp->pvp.pvn.u.isname.type = 0;
+
+	return 0;
+
+error:
+	LM_ERR("unknown PV msrp name %.*s\n", in->len, in->s);
+	return -1;
+}
+
+/**
+ *
+ */
+int pv_get_msrp(sip_msg_t *msg,  pv_param_t *param, pv_value_t *res)
+{
+	msrp_frame_t *mf;
+	msrp_hdr_t *hdr;
+	str_array_t *sar;
+	msrp_uri_t uri;
+	str s;
+	char *p;
+
+	mf = msrp_get_current_frame();
+	if(mf==NULL || param==NULL)
+		return -1;
+
+	sar = NULL;
+	hdr = NULL;
+
+	switch(param->pvn.u.isname.name.n)
+	{
+		case 1:
+			s.s   = mf->buf.s;
+			s.len = mf->buf.len;
+			return pv_get_strval(msg, param, res, &s);
+		case 2:
+			if(mf->mbody.s==NULL)
+				return pv_get_null(msg, param, res);
+			s.s   = mf->mbody.s;
+			s.len = mf->mbody.len;
+			return pv_get_strval(msg, param, res, &s);
+		case 3:
+			if(mf->fline.msgtypeid==MSRP_REQUEST)
+				return pv_get_null(msg, param, res);
+			return pv_get_intstrval(msg, param, res, mf->fline.rtypeid,
+					&mf->fline.rtype);
+		case 4:
+			if(mf->hbody.s==NULL)
+				return pv_get_null(msg, param, res);
+			s.s   = mf->hbody.s;
+			s.len = mf->hbody.len;
+			return pv_get_strval(msg, param, res, &s);
+		case 5:
+			hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_MESSAGE_ID);
+			if(hdr==NULL)
+				return pv_get_null(msg, param, res);
+			s.s   = hdr->body.s;
+			s.len = hdr->body.len;
+			trim(&s);
+			return pv_get_strval(msg, param, res, &s);
+		case 6:
+			if(mf->fline.msgtypeid==MSRP_REPLY)
+				return pv_get_null(msg, param, res);
+			return pv_get_strintval(msg, param, res, &mf->fline.rtype,
+					mf->fline.rtypeid);
+		case 7:
+			return pv_get_uintval(msg, param, res, mf->buf.len);
+		case 8:
+			if(msrp_parse_hdr_to_path(mf)<0)
+				return pv_get_null(msg, param, res);
+			hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_TO_PATH);
+			if(hdr==NULL)
+				return pv_get_null(msg, param, res);
+			sar = (str_array_t*)hdr->parsed.data;
+			s = sar->list[0];
+			trim(&s);
+			if(msrp_parse_uri(s.s, s.len, &uri)<0 || uri.session.len<=0)
+				return pv_get_null(msg, param, res);
+			s = uri.session;
+			trim(&s);
+			return pv_get_strval(msg, param, res, &s);
+		case 9:
+			if(mf->fline.msgtypeid==MSRP_REQUEST || mf->fline.rtext.s==NULL)
+				return pv_get_null(msg, param, res);
+			return pv_get_strval(msg, param, res, &mf->fline.rtext);
+		case 10:
+			return pv_get_uintval(msg, param, res, mf->mbody.len);
+		case 11:
+			s = mf->fline.transaction;
+			trim(&s);
+			return pv_get_strval(msg, param, res, &s);
+		case 12:
+			if(msrp_parse_hdr_to_path(mf)<0)
+				return pv_get_null(msg, param, res);
+			hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_TO_PATH);
+			if(hdr==NULL)
+				return pv_get_null(msg, param, res);
+			sar = (str_array_t*)hdr->parsed.data;
+			s = sar->list[0];
+			trim(&s);
+			return pv_get_strval(msg, param, res, &s);
+		case 13:
+			if(msrp_parse_hdr_from_path(mf)<0)
+				return pv_get_null(msg, param, res);
+			hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_FROM_PATH);
+			if(hdr==NULL)
+				return pv_get_null(msg, param, res);
+			sar = (str_array_t*)hdr->parsed.data;
+			s = sar->list[0];
+			trim(&s);
+			return pv_get_strval(msg, param, res, &s);
+		case 14:
+			if(msrp_parse_hdr_to_path(mf)<0)
+				return pv_get_null(msg, param, res);
+			hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_TO_PATH);
+			if(hdr==NULL)
+				return pv_get_null(msg, param, res);
+			sar = (str_array_t*)hdr->parsed.data;
+			if(sar->size<2)
+				return pv_get_null(msg, param, res);
+			s = sar->list[1];
+			trim(&s);
+			return pv_get_strval(msg, param, res, &s);
+		case 15:
+			if(msrp_parse_hdr_to_path(mf)<0)
+				return pv_get_null(msg, param, res);
+			hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_TO_PATH);
+			if(hdr==NULL)
+				return pv_get_null(msg, param, res);
+			sar = (str_array_t*)hdr->parsed.data;
+			s = sar->list[sar->size-1];
+			trim(&s);
+			return pv_get_strval(msg, param, res, &s);
+		case 16:
+			if(msrp_parse_hdr_from_path(mf)<0)
+				return pv_get_null(msg, param, res);
+			hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_FROM_PATH);
+			if(hdr==NULL)
+				return pv_get_null(msg, param, res);
+			sar = (str_array_t*)hdr->parsed.data;
+			s = sar->list[sar->size-1];
+			trim(&s);
+			return pv_get_strval(msg, param, res, &s);
+		case 17:
+			if(msrp_parse_hdr_from_path(mf)<0)
+				return pv_get_null(msg, param, res);
+			hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_FROM_PATH);
+			if(hdr==NULL)
+				return pv_get_null(msg, param, res);
+			sar = (str_array_t*)hdr->parsed.data;
+			return pv_get_uintval(msg, param, res, sar->size);
+		case 18:
+			if(msrp_parse_hdr_from_path(mf)<0)
+				return pv_get_null(msg, param, res);
+			hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_TO_PATH);
+			if(hdr==NULL)
+				return pv_get_null(msg, param, res);
+			sar = (str_array_t*)hdr->parsed.data;
+			return pv_get_uintval(msg, param, res, sar->size-1);
+		case 19:
+			if(pv_get_buffer_size()<100)
+				return pv_get_null(msg, param, res);
+			s.s = pv_get_buffer();
+			p = s.s;
+			if(mf->tcpinfo->rcv->proto==PROTO_TLS)
+			{
+				memcpy(p, "msrps://", 8);
+				p+=8;
+			} else {
+				memcpy(p, "msrp://", 7);
+				p+=7;
+			}
+			strcpy(p, ip_addr2a(&mf->tcpinfo->rcv->src_ip));
+			strcat(p, ":");
+			strcat(p, int2str(mf->tcpinfo->rcv->src_port, NULL));
+			s.len = strlen(s.s);
+			return pv_get_strval(msg, param, res, &s);
+		case 20:
+			return pv_get_strval(msg, param, res,
+					&mf->tcpinfo->rcv->bind_address->sock_str);
+		default:
+			return pv_get_null(msg, param, res);
+	}
+
+	return 0;
+}
+
+/**
+ *
+ */
+int pv_set_msrp(sip_msg_t *msg, pv_param_t *param, int op,
+		pv_value_t *val)
+{
+	return 0;
+}
+
+enum _tr_msrp_type { TR_MSRP_NONE=0, TR_MSRPURI };
+enum _tr_msrpuri_subtype { 
+	TR_MSRPURI_NONE=0, TR_MSRPURI_USER, TR_MSRPURI_HOST, TR_MSRPURI_PORT,
+	TR_MSRPURI_SESSION, TR_MSRPURI_PROTO, TR_MSRPURI_USERINFO,
+	TR_MSRPURI_PARAMS, TR_MSRPURI_SCHEME};
+
+static str _tr_empty = { "", 0 };
+static str _tr_msrpuri = {0, 0};
+static msrp_uri_t _tr_parsed_msrpuri;
+
+/**
+ *
+ */
+int tr_msrp_eval_msrpuri(struct sip_msg *msg, tr_param_t *tp, int subtype,
+		pv_value_t *val)
+{
+	if(val==NULL || (!(val->flags&PV_VAL_STR)) || val->rs.len<=0)
+		return -1;
+
+	if(_tr_msrpuri.len==0 || _tr_msrpuri.len!=val->rs.len ||
+			strncmp(_tr_msrpuri.s, val->rs.s, val->rs.len)!=0)
+	{
+		if(val->rs.len>_tr_msrpuri.len)
+		{
+			if(_tr_msrpuri.s) pkg_free(_tr_msrpuri.s);
+			_tr_msrpuri.s = (char*)pkg_malloc((val->rs.len+1)*sizeof(char));
+			if(_tr_msrpuri.s==NULL)
+			{
+				LM_ERR("no more private memory\n");
+				memset(&_tr_msrpuri, 0, sizeof(str));
+				memset(&_tr_parsed_msrpuri, 0, sizeof(msrp_uri_t));
+				return -1;
+			}
+		}
+		_tr_msrpuri.len = val->rs.len;
+		memcpy(_tr_msrpuri.s, val->rs.s, val->rs.len);
+		_tr_msrpuri.s[_tr_msrpuri.len] = '\0';
+		/* reset old values */
+		memset(&_tr_parsed_msrpuri, 0, sizeof(msrp_uri_t));
+		/* parse uri */
+		if(msrp_parse_uri(_tr_msrpuri.s, _tr_msrpuri.len,
+					&_tr_parsed_msrpuri)!=0)
+		{
+			LM_ERR("invalid uri [%.*s]\n", val->rs.len,
+					val->rs.s);
+			pkg_free(_tr_msrpuri.s);
+			memset(&_tr_msrpuri, 0, sizeof(str));
+			memset(&_tr_parsed_msrpuri, 0, sizeof(msrp_uri_t));
+			return -1;
+		}
+	}
+	memset(val, 0, sizeof(pv_value_t));
+	val->flags = PV_VAL_STR;
+
+	switch(subtype)
+	{
+		case TR_MSRPURI_USER:
+			val->rs = (_tr_parsed_msrpuri.user.s)?_tr_parsed_msrpuri.user:_tr_empty;
+			break;
+		case TR_MSRPURI_USERINFO:
+			val->rs = (_tr_parsed_msrpuri.userinfo.s)?_tr_parsed_msrpuri.userinfo:_tr_empty;
+			break;
+		case TR_MSRPURI_HOST:
+			val->rs = (_tr_parsed_msrpuri.host.s)?_tr_parsed_msrpuri.host:_tr_empty;
+			break;
+		case TR_MSRPURI_PORT:
+			val->rs = (_tr_parsed_msrpuri.port.s)?_tr_parsed_msrpuri.port:_tr_empty;
+			break;
+		case TR_MSRPURI_SESSION:
+			val->rs = (_tr_parsed_msrpuri.session.s)?_tr_parsed_msrpuri.session:_tr_empty;
+			break;
+		case TR_MSRPURI_PROTO:
+			val->rs = (_tr_parsed_msrpuri.proto.s)?_tr_parsed_msrpuri.proto:_tr_empty;
+			break;
+		case TR_MSRPURI_PARAMS:
+			val->rs = (_tr_parsed_msrpuri.params.s)?_tr_parsed_msrpuri.params:_tr_empty;
+			break;
+		case TR_MSRPURI_SCHEME:
+			val->rs = (_tr_parsed_msrpuri.scheme.s)?_tr_parsed_msrpuri.scheme:_tr_empty;
+			break;
+		default:
+			LM_ERR("unknown subtype %d\n",
+					subtype);
+			return -1;
+	}
+	return 0;
+}
+
+/**
+ *
+ */
+char* tr_parse_msrpuri(str* in, trans_t *t)
+{
+	str name;
+	char *p;
+
+	if(in==NULL || t==NULL)
+		return NULL;
+
+	p = in->s;
+	name.s = in->s;
+	t->type = TR_MSRPURI;
+	t->trf = tr_msrp_eval_msrpuri;
+
+	/* find next token */
+	while(is_in_str(p, in) && *p!=TR_PARAM_MARKER && *p!=TR_RBRACKET) p++;
+	if(*p=='\0')
+	{
+		LM_ERR("invalid transformation: %.*s\n",
+				in->len, in->s);
+		goto error;
+	}
+	name.len = p - name.s;
+	trim(&name);
+
+	if(name.len==4 && strncasecmp(name.s, "user", 4)==0)
+	{
+		t->subtype = TR_MSRPURI_USER;
+		goto done;
+	} else if(name.len==4 && strncasecmp(name.s, "host", 4)==0) {
+		t->subtype = TR_MSRPURI_HOST;
+		goto done;
+	} else if(name.len==4 && strncasecmp(name.s, "port", 4)==0) {
+		t->subtype = TR_MSRPURI_PORT;
+		goto done;
+	} else if(name.len==7 && strncasecmp(name.s, "session", 7)==0) {
+		t->subtype = TR_MSRPURI_SESSION;
+		goto done;
+	} else if(name.len==5 && strncasecmp(name.s, "proto", 5)==0) {
+		t->subtype = TR_MSRPURI_PROTO;
+		goto done;
+	} else if(name.len==6 && strncasecmp(name.s, "scheme", 6)==0) {
+		t->subtype = TR_MSRPURI_SCHEME;
+		goto done;
+	} else if(name.len==6 && strncasecmp(name.s, "params", 6)==0) {
+		t->subtype = TR_MSRPURI_PARAMS;
+		goto done;
+	} else if(name.len==8 && strncasecmp(name.s, "userinfo", 8)==0) {
+		t->subtype = TR_MSRPURI_USERINFO;
+		goto done;
+	}
+
+
+	LM_ERR("unknown transformation: %.*s/%.*s/%d!\n", in->len, in->s,
+			name.len, name.s, name.len);
+error:
+	return NULL;
+done:
+	t->name = name;
+	return p;
+
+}

+ 38 - 0
modules/msrp/msrp_vars.h

@@ -0,0 +1,38 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2012 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef _MSRP_VARS_H_
+#define _MSRP_VARS_H_
+
+#include "../../pvar.h"
+#include "msrp_parser.h"
+
+int pv_parse_msrp_name(pv_spec_t *sp, str *in);
+int pv_get_msrp(sip_msg_t *msg,  pv_param_t *param, pv_value_t *res);
+int pv_set_msrp(sip_msg_t *msg, pv_param_t *param, int op,
+		pv_value_t *val);
+
+char* tr_parse_msrpuri(str* in, trans_t *t);
+
+#endif