<?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 id="msrp.overview">
	<title>Overview</title>
	<para>
		This module provides a MSRP routing engine, a.k.a. MSRP relay.
		MSRP (Message Session Relay Protocol) is defined by RFC4975,
		and the extensions for an MSRP relay are covered in RFC4976.
	</para>
	<para>
		A typical use of MSRP is instant messaging sessions initiated
		via SIP. Unlike page-mode instant messaging, which is done via the SIP
		MESSAGE request, MSRP uses a different communication channel which
		is negotiated via INVITE-200 OK-ACK.
	</para>
	<para>
		However, MSRP is still a text-based protocol. It uses several routing
		mechanisms similar to what exists in SIP. Furthermore,
		MSRP requires TCP, and recommends TLS for confidentiality and security.
		In light of the scalability and performance of &kamailio; in handling
		TCP/TLS, this module reuses &kamailio;'s core framework to
		offer MSRP routing capabilities. Along with embedded Presence and XCAP
		servers, &kamailio; now offers a complete solution for SIP beyond VoIP.
	</para>
	<para>
		One of the main benefits of this module is the ability to reuse
		all the other extensions that exist in the SIP server, including 
	        accounting, authentication, authorization to database connectors, 
		security and DoS attack protections.
	</para>
	<para>
		&kamailio; can handle SIP and MSRP traffic received on the same port;
		the appropriate configuration file block being executed based on the
		type of message. Therefore, you can use &kamailio; as a stand-alone
		MSRP relay or you can have an instance handling both SIP and MSRP. Another
		option is to configure &kamailio; to listen on multiple ports, some
		of them for SIP and others for MSRP.
	</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>
		<para>
		The following modules are required to make proper use of this
		module:
			<itemizedlist>
			<listitem>
			<para>
				<emphasis>tls</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 id="msrp.p.sipmsg">
		<title><varname>sipmsg</varname> (int)</title>
		<para>
			If set to 1, the module will build a SIP message from MSRP frame headers,
			providing it to <quote>event_route[msrp:frame-in]</quote>.
			All the config file functions (apart from SIP request relay) that can be used
			in a request route block can be used in the 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 id="msrp.p.cmap_size">
		<title><varname>cmap_size</varname> (int)</title>
		<para>
		The size of connection map table, to be computed as power of 2 (e.g.,
		if the value is 4, then the number of slots in map table is 2^4 = 16).
		</para>
		<para>
		<emphasis>
			Default value is '0' (no internal map table to be used).
		</emphasis>
		</para>
		<example>
		<title>Set <varname>cmap_size</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("msrp", "cmap_size", 8)
...
</programlisting>
		</example>
	</section>
	<section id="msrp.p.timer_interval">
		<title><varname>timer_interval</varname> (int)</title>
		<para>
		The timer interval in seconds to run the procedure for cleaning
		expired connections.
		</para>
		<para>
		<emphasis>
			Default value is '60'.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>timer_interval</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("msrp", "timer_interval", 90)
...
</programlisting>
		</example>
	</section>
	<section id="msrp.p.auth_min_expires">
		<title><varname>auth_min_expires</varname> (int)</title>
		<para>
		The minimum value accepted for the <quote>Expires</quote> header in AUTH requests.
		</para>
		<para>
		<emphasis>
			Default value is '60'.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>auth_min_expires</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("msrp", "auth_min_expiresl", 90)
...
</programlisting>
		</example>
	</section>
	<section id="msrp.p.auth_max_expires">
		<title><varname>auth_max_expires</varname> (int)</title>
		<para>
		The maximum value accepted for <quote>Expires</quote> header in AUTH requests.
		</para>
		<para>
		<emphasis>
			Default value is '3600'.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>auth_max_expires</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("msrp", "auth_max_expiresl", 1800)
...
</programlisting>
		</example>
	</section>
	<section id="msrp.p.use_path_addr">
		<title><varname>use_path_addr</varname> (str)</title>
		<para>
		The hostname:port to be used when building the Path header.
		</para>
		<para>
		<emphasis>
			Default value is NULL (server IP and port are used).
		</emphasis>
		</para>
		<example>
		<title>Set <varname>use_path_addr</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("msrp", "use_path_addr", "msrp.kamailio.org:5061")
...
</programlisting>
		</example>
	</section>
	</section>

	<section>
	<title>Functions</title>
	<section id="msrp.f.msrp_relay">
	    <title>
		<function moreinfo="none">msrp_relay()</function>
	    </title>
	    <para>
			Relay MSRP frame according to the To-Path. This function has to be 
			executed for each MSRP request or reply that has to be forwarded. Note
			that due to nature of the 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_relay</function> usage</title>
		<programlisting format="linespecific">
...
event_route[msrp:frame-in] {
    msrp_relay();
}
...
</programlisting>
	    </example>
	</section>

	<section id="msrp.f.msrp_reply">
	    <title>
		<function moreinfo="none">msrp_reply(code, text [, hdrs])</function>
	    </title>
	    <para>
		Send a reply for the current MSRP request, adding optional headers.
		</para>
		<para>
		The parameter can be a 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 id="msrp.f.msrp_is_request">
	    <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 id="msrp.f.msrp_is_reply">
	    <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 id="msrp.f.msrp_set_dst">
	    <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 a 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 id="msrp.f.msrp_relay_flags">
	    <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 a 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 id="msrp.f.msrp_reply_flags">
	    <title>
		<function moreinfo="none">msrp_reply_flags(flags)</function>
	    </title>
	    <para>
		Set transport layer sending flags for replies to the 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 a 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 id="msrp.f.msrp_cmap_save">
	    <title>
		<function moreinfo="none">msrp_cmap_save()</function>
	    </title>
	    <para>
		Save details of a MSRP connection upon AUTH request inside
		the internal map table, indexed by session id.
		</para>
		<para>
		This function can be used in ANY_ROUTE.
		</para>
		<example>
		<title><function>msrp_cmap_save</function> usage</title>
		<programlisting format="linespecific">
...
event_route[msrp:frame-in] {
    ...
	if(method=="AUTH") { msrp_cmap_save(); exit; }
    ...
}
...
</programlisting>
	    </example>
	</section>

	<section id="msrp.f.msrp_cmap_lookup">
	    <title>
		<function moreinfo="none">msrp_cmap_lookup()</function>
	    </title>
	    <para>
		Lookup MSRP connection details for current session id.
		</para>
		<para>
		This function can be used in ANY_ROUTE.
		</para>
		<example>
		<title><function>msrp_cmap_lookup</function> usage</title>
		<programlisting format="linespecific">
...
event_route[msrp:frame-in] {
    ...
	if(method=="SEND" and $msrp(nexthops)==1) {
		if(msrp_cmap_lookup()) {
			msrp_relay();
		} else {
			msrp_reply("481", "Session not found");
		}
	}
    ...
}
...
</programlisting>
	    </example>
	</section>

	</section>

	<section>
		<title>Pseudo Variables</title>
		<para>
			The module exports a pseudo-variable 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 allow
			access attributes of a MSRP URI.
		</para>
		<para>
			These are documented in the appropriate Wiki pages hosted on the
			project web site.
		</para>
	</section>

	<section>
	<title>RPC Commands</title>
	<section id="msrp.r.msrp.cmaplist">
		<title>
		<function moreinfo="none">msrp.cmaplist</function>
		</title>
		<para>
		List active MSRP connections.
		</para>
		<para>
		Example:
		</para>
<programlisting  format="linespecific">
...
&sercmd; msrp.cmaplist
...
</programlisting>
    </section>
    </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 id="msrp.usage">
		<title>Usage</title>
		<para>
			When the <varname>sipmsg</varname> parameter is set to 1 (which is the
			default), the module internally builds a SIP request from the MSRP frame
			and exposes it to the config file interpreter. This way, all the functions
			that are valid for SIP requests can be used safely in event_route[msrp:frame-in].
		</para>
		<para>
			To build the SIP request, the module takes the first line and the
			headers from an MSRP message and appends them to a static buffer. The 
			next two examples show an MSRP frame and the resulting SIP request.
		</para>
		<programlisting format="linespecific">
<![CDATA[
...
MSRP 6aef SEND
To-Path: msrps://a.example.org:9000/kjfjan;tcp \
  msrps://b.example.net:9000/aeiug;tcp \
  msrps://bob.example.net:8145/foo;tcp
From-Path: msrps://alice.example.org:7965/bar;tcp
Success-Report: yes
Byte-Range: 1-*/*
Message-ID: 87652
Content-Type: text/plain

Hi Bob, I'm about to send you a photo.
-------6aef$
...
]]>
</programlisting>

		<programlisting format="linespecific">
<![CDATA[
...
MSRP sip:a@127.0.0.1 SIP/2.0
Via: SIP/2.0/UDP 127.0.0.1:9;branch=z9hG4bKa
From: <b@127.0.0.1>;tag=a
To: <a@127.0.0.1>
Call-ID: a
CSeq: 1 MSRP
Content-Length: 0
MSRP-First-Line: MSRP 6aef SEND
To-Path: msrps://a.example.org:9000/kjfjan;tcp \
  msrps://b.example.net:9000/aeiug;tcp \
  msrps://bob.example.net:8145/foo;tcp
From-Path: msrps://alice.example.org:7965/bar;tcp
Success-Report: yes
Byte-Range: 1-*/*
Message-ID: 87652
Content-Type: text/plain

...
]]>
</programlisting>
		<para>
			Note that MSRP does not permit line folding.  A "\" in the examples
			shows a line continuation due to the limitations of line length of this
			document.  Neither the backslash nor the extra CRLF is included in
			the actual request or response.
		</para>
		<para>
			As can be observed, the MSRP frame content starts with the body
			of the 'MSRP-First-Line:' header. Using static content to get to a
			valid SIP request is a perfect trade-off for performance.
		</para>
		<para>
			Besides the option to access parts of MSRP frame via an 
			internally-built SIP message, the module exports a new pseudo-variable 
			class $msrp(key) which returns attributes from the MSRP frame. There is
			also a new transformation, {msrpuri.key}, to get access to parts
			of an MSRP URI. See the appropriate Wiki pages on the project's
			web site for full details about new pseudo-variable and 
			transformation classes.
		</para>
		<para>
			Next is an example of configuration file with the routing block
			for MSRP frames. In this config, the SIP traffic is rejected.
		</para>
		<example>
		<title>Event Route (using htable for MSRP connection tracking)</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"

modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo")

modparam("auth", "nonce_count", 1)
modparam("auth", "qop", "auth")

#!substdef "!MSRP_MIN_EXPIRES!1800!g"
#!substdef "!MSRP_MAX_EXPIRES!3600!g"
modparam("htable", "htable", "msrp=>size=8;autoexpire=MSRP_MAX_EXPIRES;")

request_route {
	sl_send_reply("403", "No SIP Here");
	exit;
}

reply_route {
	drop;
}

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();
	}
	else 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",
					"$msrp(method)"))
		{
			if(auth_get_www_authenticate("myrealm", "1",
							"$var(wauth)"))
			{
				msrp_reply("401", "Unauthorized",
						"$var(wauth)");
			} else {
				msrp_reply("500", "Server Error");
			}
			exit;
		}

		if ($hdr(Expires) != $null) {
			$var(expires) = (int) $hdr(Expires);
			if ($var(expires) < MSRP_MIN_EXPIRES) {
				msrp_reply("423", "Interval Out-of-Bounds",
					"Min-Expires: MSRP_MIN_EXPIRES\r\n");
				exit;
			} else {
				msrp_reply("423", "Interval Out-of-Bounds",
					"Max-Expires: MSRP_MAX_EXPIRES\r\n");
				exit;
			}

		} else
			$var(expires) = MSRP_MAX_EXPIRES;
		
		$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);
		$shtex(msrp=>$var(sessid)) = $var(expires) + 5;
		# - Use-Path: the MSRP address for server + session id
		$var(headers) = "Use-Path: msrp://127.0.0.1:5060/"
				+ $var(sessid) + ";tcp\r\n"
				+ "Expires: " + $var(expires) + "\r\n";
		msrp_reply("200", "OK", "$var(headers)");
	}
	else if($msrp(method)=="SEND" || $msrp(method)=="REPORT")
	{
		if($msrp(nexthops)>1)
		{
			if ($msrp(method)!="REPORT")
			{
				msrp_reply("200", "OK");
			}
			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
			if ($msrp(method)!="REPORT")
			{
				msrp_reply("481", "Session-does-not-exist");
			}
			exit;
		}
		else if($msrp(method)!="REPORT")
		{
			msrp_reply("200", "OK");
		}
		msrp_relay_flags("1");
		msrp_set_dst("$sht(msrp=>$var(sessid)::srcaddr)",
				"$sht(msrp=>$var(sessid)::srcsock)");
		msrp_relay();
	}
	else
	{
		msrp_reply("501", "Request-method-not-understood");
	}
}
]]>
...
</programlisting>
		</example>
		<example>
		<title>Event Route (using msrp_cmap_ functions for connection tracking)</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 "xlog.so"

modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo")

modparam("auth", "nonce_count", 1)
modparam("auth", "qop", "auth")

modparam("msrp", "cmap_size", 8)
modparam("msrp", "use_path_addr", "msrp://127.0.0.1:5060")

request_route {
	sl_send_reply("403", "No SIP Here");
	exit;
}

reply_route {
	drop;
}

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();
	}
	else 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",
					"$msrp(method)"))
		{
			if(auth_get_www_authenticate("myrealm", "1",
							"$var(wauth)"))
			{
				msrp_reply("401", "Unauthorized",
						"$var(wauth)");
			} else {
				msrp_reply("500", "Server Error");
			}
			exit;
		}

		msrp_cmap_save();
	}
	else if ($msrp(method)=="SEND" || $msrp(method)=="REPORT")
	{
		if ($msrp(nexthops)>1)
		{
			if ($msrp(method)!="REPORT")
			{
				msrp_reply("200", "OK");
			}
			msrp_relay();
			exit;
		}

		if (msrp_cmap_lookup())
		{
			if ($msrp(method)!="REPORT")
			{
				msrp_reply("200", "OK");
			}
			msrp_relay_flags("1");
			msrp_relay();
		}
		else
		{
			msrp_reply("481", "Session-does-not-exist");
		}
	}
	else
	{
		msrp_reply("501", "Request-method-not-understood");
	}
}
]]>
...
</programlisting>
		</example>

	</section>

</chapter>

