Browse Source

Merge remote branch 'origin/tmp/core_events'

* origin/tmp/core_events:
  core ev: minor cleanups
  topoh: new module for hiding topology details
  core: new sr events system
  core: execute callbacks for NET_DATA_IN and NET_DATA_OUT
Andrei Pelinescu-Onciul 16 years ago
parent
commit
e57768f782

+ 98 - 0
events.c

@@ -0,0 +1,98 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2009 SIP-Router.org
+ *
+ * This file is part of Extensible SIP Router, a free SIP server.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "dprint.h"
+#include "mem/mem.h"
+#include "events.h"
+
+static sr_event_cb_t _sr_events_list;
+static int _sr_events_inited = 0;
+
+void sr_event_cb_init(void)
+{
+	if(_sr_events_inited == 0)
+	{
+		memset(&_sr_events_list, 0, sizeof(sr_event_cb_t));
+		_sr_events_inited = 1;
+	}
+}
+
+int sr_event_register_cb(int type, sr_event_cb_f f)
+{
+	sr_event_cb_init();
+	switch(type) {
+		case SREV_NET_DATA_IN:
+				if(_sr_events_list.net_data_in==0)
+					_sr_events_list.net_data_in = f;
+				else return -1;
+			break;
+		case SREV_NET_DATA_OUT:
+				if(_sr_events_list.net_data_out==0)
+					_sr_events_list.net_data_out = f;
+				else return -1;
+			break;
+		default:
+			return -1;
+	}
+	return 0;
+}
+
+int sr_event_exec(int type, void *data)
+{
+	int ret;
+	str *p;
+	switch(type) {
+		case SREV_NET_DATA_IN:
+				if(unlikely(_sr_events_list.net_data_in!=0))
+				{
+					p = (str*)data;
+#ifdef EXTRA_DEBUG
+					LM_DBG("PRE-IN ++++++++++++++++++++++++++++++++\n"
+							"%.*s\n+++++\n", p->len, p->s);
+#endif /* EXTRA_DEBUG */
+					ret = _sr_events_list.net_data_in(data);
+#ifdef EXTRA_DEBUG
+					LM_DBG("POST-IN ++++++++++++++++++++++++++++++++\n"
+							"%.*s\n+++++\n", p->len, p->s);
+#endif /* EXTRA_DEBUG */
+					return ret;
+				} else return 1;
+			break;
+		case SREV_NET_DATA_OUT:
+				if(unlikely(_sr_events_list.net_data_out!=0))
+				{
+					p = (str*)data;
+#ifdef EXTRA_DEBUG
+					LM_DBG("PRE-OUT ++++++++++++++++++++\n"
+							"%.*s\n+++++++++++++++++++\n", p->len, p->s);
+#endif /* EXTRA_DEBUG */
+					ret = _sr_events_list.net_data_out(data);
+#ifdef EXTRA_DEBUG
+					LM_DBG("POST-OUT ++++++++++++++++++++\n"
+							"%.*s\n+++++++++++++++++++\n", p->len, p->s);
+#endif /* EXTRA_DEBUG */
+					return ret;
+				} else return 1;
+			break;
+		default:
+			return -1;
+	}
+}
+

+ 40 - 0
events.h

@@ -0,0 +1,40 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2009 SIP-Router.org
+ *
+ * This file is part of Extensible SIP Router, a free SIP server.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _SR_EVENTS_H_
+#define _SR_EVENTS_H_
+
+#include "parser/msg_parser.h"
+
+#define SREV_NET_DATA_IN	1
+#define SREV_NET_DATA_OUT	2
+
+typedef int (*sr_event_cb_f)(void *data);
+
+typedef struct sr_event_cb {
+	sr_event_cb_f net_data_in;
+	sr_event_cb_f net_data_out;
+} sr_event_cb_t;
+
+void sr_event_cb_init(void);
+int sr_event_register_cb(int type, sr_event_cb_f f);
+int sr_event_exec(int type, void *data);
+
+#endif

+ 14 - 4
forward.h

@@ -60,6 +60,7 @@
 #endif
 
 #include "compiler_opt.h"
+#include "events.h"
 
 
 enum ss_mismatch {
@@ -117,9 +118,14 @@ int forward_reply( struct sip_msg* msg);
  *         that generated them; use 0 if you don't want this)
  * buf, len = buffer
  * returns: 0 if ok, -1 on error*/
+
 static inline int msg_send(struct dest_info* dst, char* buf, int len)
 {
 	struct dest_info new_dst;
+	str outb;
+	outb.s = buf;
+	outb.len = len;
+	sr_event_exec(SREV_NET_DATA_OUT, (void*)&outb);
 	
 	if (likely(dst->proto==PROTO_UDP)){
 		if (unlikely((dst->send_sock==0) || 
@@ -132,7 +138,7 @@ static inline int msg_send(struct dest_info* dst, char* buf, int len)
 			}
 			dst=&new_dst;
 		}
-		if (unlikely(udp_send(dst, buf, len)==-1)){
+		if (unlikely(udp_send(dst, outb.s, outb.len)==-1)){
 			STATS_TX_DROPS;
 			LOG(L_ERR, "msg_send: ERROR: udp_send failed\n");
 			goto error;
@@ -146,7 +152,7 @@ static inline int msg_send(struct dest_info* dst, char* buf, int len)
 					" support is disabled\n");
 			goto error;
 		}else{
-			if (unlikely(tcp_send(dst, 0, buf, len)<0)){
+			if (unlikely(tcp_send(dst, 0, outb.s, outb.len)<0)){
 				STATS_TX_DROPS;
 				LOG(L_ERR, "msg_send: ERROR: tcp_send failed\n");
 				goto error;
@@ -161,7 +167,7 @@ static inline int msg_send(struct dest_info* dst, char* buf, int len)
 					" support is disabled\n");
 			goto error;
 		}else{
-			if (unlikely(tcp_send(dst, 0, buf, len)<0)){
+			if (unlikely(tcp_send(dst, 0, outb.s, outb.len)<0)){
 				STATS_TX_DROPS;
 				LOG(L_ERR, "msg_send: ERROR: tcp_send failed\n");
 				goto error;
@@ -187,7 +193,7 @@ static inline int msg_send(struct dest_info* dst, char* buf, int len)
 				}
 				dst=&new_dst;
 			}
-			if (unlikely(sctp_msg_send(dst, buf, len)<0)){
+			if (unlikely(sctp_msg_send(dst, outb.s, outb.len)<0)){
 				STATS_TX_DROPS;
 				LOG(L_ERR, "msg_send: ERROR: sctp_msg_send failed\n");
 				goto error;
@@ -199,8 +205,12 @@ static inline int msg_send(struct dest_info* dst, char* buf, int len)
 			LOG(L_CRIT, "BUG: msg_send: unknown proto %d\n", dst->proto);
 			goto error;
 	}
+	if(outb.s != buf)
+		pkg_free(outb.s);
 	return 0;
 error:
+	if(outb.s != buf)
+		pkg_free(outb.s);
 	return -1;
 }
 

+ 15 - 0
modules/topoh/Makefile

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

+ 186 - 0
modules/topoh/README

@@ -0,0 +1,186 @@
+topoh Module
+
+Daniel-Constantin Mierla
+
+   <[email protected]>
+
+Edited by
+
+Daniel-Constantin Mierla
+
+   <[email protected]>
+
+   Copyright © 2009 FhG FOKUS
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+        2. Dependencies
+
+              2.1. Kamailio Modules
+              2.2. External Libraries or Applications
+
+        3. Exported Parameters
+
+              3.1. mask_key (str)
+              3.2. mask_callid (integer)
+              3.3. uparam_name (str)
+              3.4. uparam_prefix (str)
+              3.5. vparam_name (str)
+              3.6. vparam_prefix (str)
+
+        4. Exported Functions
+
+              4.1.
+
+   List of Examples
+
+   1.1. Set mask_key parameter
+   1.2. Set mask_callid parameter
+   1.3. Set uparam_name parameter
+   1.4. Set uparam_prefix parameter
+   1.5. Set vparam_name parameter
+   1.6. Set vparam_prefix parameter
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+   2. Dependencies
+
+        2.1. Kamailio Modules
+        2.2. External Libraries or Applications
+
+   3. Exported Parameters
+
+        3.1. mask_key (str)
+        3.2. mask_callid (integer)
+        3.3. uparam_name (str)
+        3.4. uparam_prefix (str)
+        3.5. vparam_name (str)
+        3.6. vparam_prefix (str)
+
+   4. Exported Functions
+
+        4.1.
+
+1. Overview
+
+   This module hides the routing headers that show topology details. It it
+   is not affected by the server being transaction statless or stateful.
+   The script interpretor gets the SIP messages decoded, so all
+   functionality existing so far is preserved.
+
+   The module is transparent for config writer. It only needs to be loaded
+   (tune the parameters if wanted). The SIP server can be restarted
+   whitout affecting ongoing calls - once it is up, can encode/decode
+   topology details, thus no call is lost.
+
+   By using same mask_key, many SIP servers can decode the message, for
+   examlple, applicable for servers behind load balancers.
+
+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:
+     * rr module - server must perform record routing to ensure in-dialog
+       requests are encoded/decoded.
+
+2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed before
+   running Kamailio with this module loaded:
+     * None. In the future the module can be enhnaced to use a stronger
+       encryption algorithm.
+
+3. Exported Parameters
+
+   3.1. mask_key (str)
+   3.2. mask_callid (integer)
+   3.3. uparam_name (str)
+   3.4. uparam_prefix (str)
+   3.5. vparam_name (str)
+   3.6. vparam_prefix (str)
+
+3.1. mask_key (str)
+
+   Keyword to mask the headers.
+
+   Default value is "_static_value_".
+
+   Example 1.1. Set mask_key parameter
+...
+modparam("topoh", "mask_key", "some secret here")
+...
+
+3.2. mask_callid (integer)
+
+   Whether to encode or not the call-id. Some SIP extensions include the
+   call-id in SIP message payload or header, so it is safe to not encode
+   call-id in such cases. Well-known extensions such as call transfer or
+   conference join will be added to work with encoded call-id.
+
+   Default value is 0 (do not mask).
+
+   Example 1.2. Set mask_callid parameter
+...
+modparam("topoh", "mask_callid", 1)
+...
+
+3.3. uparam_name (str)
+
+   Name of URI param where to store encoded value.
+
+   Default value is "line".
+
+   Example 1.3. Set uparam_name parameter
+...
+modparam("topoh", "uparam_name", "myparam")
+...
+
+3.4. uparam_prefix (str)
+
+   Prefix to be added in encoded URI params.
+
+   Default value is "sr-".
+
+   Example 1.4. Set uparam_prefix parameter
+...
+modparam("topoh", "uparam_prefix", "xyz")
+...
+
+3.5. vparam_name (str)
+
+   Name of Via param where to store encoded value.
+
+   Default value is "branch".
+
+   Example 1.5. Set vparam_name parameter
+...
+modparam("topoh", "vparam_name", "myv")
+...
+
+3.6. vparam_prefix (str)
+
+   Prefix to be added in encoded Via params.
+
+   Default value is "z9hG4bKsr-".
+
+   Example 1.6. Set vparam_prefix parameter
+...
+modparam("topoh", "vparam_prefix", "xyz")
+...
+
+4. Exported Functions
+
+   4.1.
+
+   None.

+ 4 - 0
modules/topoh/doc/Makefile

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

+ 37 - 0
modules/topoh/doc/topoh.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>topoh 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>2009</year>
+	    <holder>&fhg;</holder>
+	</copyright>
+    </bookinfo>
+    <toc></toc>
+    
+    <xi:include href="topoh_admin.xml"/>
+    
+    
+</book>

+ 198 - 0
modules/topoh/doc/topoh_admin.xml

@@ -0,0 +1,198 @@
+<?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 hides the routing headers that show topology details.
+		It it is not affected by the server being transaction statless or
+		stateful. The script interpretor gets the SIP messages decoded,
+		so all functionality existing so far is preserved.
+	</para>
+	<para>
+		The module is transparent for config writer. It only needs to be
+		loaded (tune the parameters if wanted). The SIP server can be restarted
+		whitout affecting ongoing calls - once it is up, can encode/decode
+		topology details, thus no call is lost.
+	</para>
+	<para>
+		By using same mask_key, many SIP servers can decode the message,
+		for examlple, applicable for servers behind load balancers.
+	</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>rr module</emphasis> - server must perform record
+				routing to ensure in-dialog requests are encoded/decoded.
+			</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>. In the future the module can be
+				enhnaced to use a stronger encryption algorithm.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	</section>
+	<section>
+	<title>Exported Parameters</title>
+	<section>
+		<title><varname>mask_key</varname> (str)</title>
+		<para>
+		Keyword to mask the headers.
+		</para>
+		<para>
+		<emphasis>
+			Default value is "_static_value_".
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>mask_key</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("topoh", "mask_key", "some secret here")
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>mask_callid</varname> (integer)</title>
+		<para>
+			Whether to encode or not the call-id. Some SIP extensions include
+			the call-id in SIP message payload or header, so it is safe to
+			not encode call-id in such cases. Well-known extensions such as
+			call transfer or conference join will be added to work with encoded
+			call-id.
+		</para>
+		<para>
+		<emphasis>
+			Default value is 0 (do not mask).
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>mask_callid</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("topoh", "mask_callid", 1)
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>uparam_name</varname> (str)</title>
+		<para>
+		Name of URI param where to store encoded value.
+		</para>
+		<para>
+		<emphasis>
+			Default value is "line".
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>uparam_name</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("topoh", "uparam_name", "myparam")
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>uparam_prefix</varname> (str)</title>
+		<para>
+		Prefix to be added in encoded URI params.
+		</para>
+		<para>
+		<emphasis>
+			Default value is "sr-".
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>uparam_prefix</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("topoh", "uparam_prefix", "xyz")
+...
+</programlisting>
+		</example>
+	</section>
+		<section>
+		<title><varname>vparam_name</varname> (str)</title>
+		<para>
+		Name of Via param where to store encoded value.
+		</para>
+		<para>
+		<emphasis>
+			Default value is "branch".
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>vparam_name</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("topoh", "vparam_name", "myv")
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>vparam_prefix</varname> (str)</title>
+		<para>
+		Prefix to be added in encoded Via params.
+		</para>
+		<para>
+		<emphasis>
+			Default value is "z9hG4bKsr-".
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>vparam_prefix</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("topoh", "vparam_prefix", "xyz")
+...
+</programlisting>
+		</example>
+	</section>
+
+	</section>
+	<section>
+	<title>Exported Functions</title>
+	<section>
+		<para>
+			None.
+		</para>
+	</section>
+	</section>
+</chapter>
+

+ 175 - 0
modules/topoh/th_mask.c

@@ -0,0 +1,175 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2009 SIP-Router.org
+ *
+ * This file is part of Extensible SIP Router, a free SIP server.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <string.h>
+
+#include "../../dprint.h"
+#include "../../md5.h"
+#include "../../crc.h"
+#include "../../mem/mem.h"
+#include "th_mask.h"
+
+#define TH_EB64I \
+		"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.-"
+
+char _th_EB64[65];
+int _th_DB64[256];
+char *_th_PD64 = "*";
+
+extern str _th_key;
+
+void th_shuffle(char *in, int size)
+{
+	char tmp;
+	int last;
+	unsigned int r;
+	unsigned char md5[16];
+	unsigned int *md5i;
+	unsigned int crc;
+	MD5_CTX ctx;
+
+	MD5Init(&ctx);
+	MD5Update(&ctx, _th_key.s, _th_key.len);
+	MD5Update(&ctx, _th_key.s, _th_key.len);
+	MD5Final(md5, &ctx);
+
+	md5i = (unsigned int*)md5;
+
+	crc = (unsigned int)crcitt_string(_th_key.s, _th_key.len);
+	for (last = size; last > 1; last--)
+	{
+		r = (md5i[(crc+last+_th_key.len)%4]
+				+ _th_key.s[(crc+last+_th_key.len)%_th_key.len]) % last;
+		tmp = in[r];
+		in[r] = in[last - 1];
+		in[last - 1] = tmp;
+	}
+}
+
+void th_mask_init(void)
+{
+	int i;
+
+	_th_key.len = strlen(_th_key.s);
+	memcpy(_th_EB64, TH_EB64I, sizeof(TH_EB64I));
+	th_shuffle(_th_EB64, 64);
+	LM_ERR("+++ %s\n", TH_EB64I);
+	LM_ERR("+++ %s\n", _th_EB64);
+	for(i=0; i<256; i++)
+		_th_DB64[i] = -1;
+	for(i=0; i<64; i++)
+		_th_DB64[(int)_th_EB64[i]] = i;
+
+	return;
+}
+
+char* th_mask_encode(char *in, int ilen, str *prefix, int *olen)
+{
+	char *out;
+	int  left;
+	int  idx;
+	int  i;
+	int  r;
+	char *p;
+	int  block;
+
+	*olen = (((ilen+2)/3)<<2) + ((prefix!=NULL&&prefix->len>0)?prefix->len:0);
+	out = (char*)pkg_malloc((*olen+1)*sizeof(char));
+	if(out==NULL)
+	{
+		LM_ERR("no more pkg\n");
+		*olen = 0;
+		return NULL;
+	}
+	memset(out, 0, (*olen+1)*sizeof(char));
+	if(prefix!=NULL&&prefix->len>0)
+		memcpy(out, prefix->s, prefix->len);
+
+	p = out + (int)((prefix!=NULL&&prefix->len>0)?prefix->len:0);
+	for(idx=0; idx<ilen; idx+=3)
+	{
+		left = ilen - idx - 1 ;
+		left = (left>1)?2:left;
+
+		block = 0;
+		for(i=0, r=16; i<=left; i++, r-=8)
+			block += ((unsigned char)in[idx+i]) << r;
+
+		*(p++) = _th_EB64[(block >> 18) & 0x3f];
+		*(p++) = _th_EB64[(block >> 12) & 0x3f];
+		*(p++) = (left>0)?_th_EB64[(block >> 6) & 0x3f]:_th_PD64[0];
+		*(p++) = (left>1)?_th_EB64[block & 0x3f]:_th_PD64[0];
+	}
+
+	return out;
+}
+
+char* th_mask_decode(char *in, int ilen, str *prefix, int extra, int *olen)
+{
+	char *out;
+	int n;
+	int block;
+	int idx;
+	int i;
+	int j;
+	int end;
+	char c;
+
+	for(n=0,i=ilen-1; in[i]==_th_PD64[0]; i--)
+		n++;
+
+	*olen = (((ilen-((prefix!=NULL&&prefix->len>0)?prefix->len:0)) * 6) >> 3)
+				- n;
+	out = (char*)pkg_malloc((*olen+1+extra)*sizeof(char));
+
+	if(out==NULL)
+	{
+		LM_ERR("no more pkg\n");
+		*olen = 0;
+		return NULL;
+	}
+	memset(out, 0, (*olen+1+extra)*sizeof(char));
+
+	end = ilen - n;
+	i = (prefix!=NULL&&prefix->len>0)?prefix->len:0;
+	for(idx=0; i<end; idx+=3)
+	{
+		block = 0;
+		for(j=0; j<4 && i<end ; j++)
+		{
+			c = _th_DB64[(int)in[i++]];
+			if(c<0)
+			{
+				LM_ERR("invalid input string\"%.*s\"\n", ilen, in);
+				pkg_free(out);
+				*olen = 0;
+				return NULL;
+			}
+			block += c << (18 - 6*j);
+		}
+
+		for(j=0, n=16; j<3 && idx+j< *olen; j++, n-=8)
+			out[idx+j] = (char)((block >> n) & 0xff);
+	}
+
+	return out;
+}
+
+

+ 30 - 0
modules/topoh/th_mask.h

@@ -0,0 +1,30 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2009 SIP-Router.org
+ *
+ * This file is part of Extensible SIP Router, a free SIP server.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _TH_MASK_H_
+#define _TH_MASK_H_
+
+#include "../../str.h"
+
+void th_mask_init(void);
+char* th_mask_encode(char *in, int ilen, str *prefix, int *olen);
+char* th_mask_decode(char *in, int ilen, str *prefix, int extra, int *olen);
+
+#endif

+ 913 - 0
modules/topoh/th_msg.c

@@ -0,0 +1,913 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2009 SIP-Router.org
+ *
+ * This file is part of Extensible SIP Router, a free SIP server.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <string.h>
+
+#include "../../dprint.h"
+#include "../../mem/mem.h"
+#include "../../data_lump.h"
+#include "../../forward.h"
+#include "../../msg_translator.h"
+#include "../../parser/parse_rr.h"
+#include "../../parser/parse_uri.h"
+#include "../../parser/parse_param.h"
+#include "../../parser/parse_from.h"
+#include "../../parser/parse_to.h"
+#include "../../parser/parse_via.h"
+#include "../../parser/contact/parse_contact.h"
+#include "th_mask.h"
+#include "th_msg.h"
+
+extern str th_cookie_name;
+extern str th_cookie_value;
+extern str th_via_prefix;
+extern str th_uri_prefix;
+
+extern str th_ip;
+extern str th_uparam_name;
+extern str th_uparam_prefix;
+extern str th_vparam_name;
+extern str th_vparam_prefix;
+
+extern int th_param_mask_callid;
+
+int th_skip_rw(char *s, int len)
+{
+	while(len>0)
+	{
+		if(s[len-1]==' ' || s[len-1]=='\t' || s[len-1]=='\n' || s[len-1]=='\r'
+				|| s[len-1]==',')
+			len--;
+		else return len;
+	}
+	return 0;
+}
+
+struct via_param *th_get_via_param(struct via_body *via, str *name)
+{
+	struct via_param *p;
+	for(p=via->param_lst; p; p=p->next)
+	{
+		if(p->name.len==name->len
+				&& strncasecmp(p->name.s, name->s, name->len)==0)
+			return p;
+	}
+	return NULL;
+}
+
+int th_get_param_value(str *in, str *name, str *value)
+{
+	param_t* params = NULL;
+	param_t* p = NULL;
+	param_hooks_t phooks;
+	if (parse_params(in, CLASS_ANY, &phooks, &params)<0)
+		return -1;
+	for (p = params; p; p=p->next)
+	{
+		if (p->name.len==name->len
+				&& strncasecmp(p->name.s, name->s, name->len)==0)
+		{
+			*value = p->body;
+			free_params(params);
+			return 0;
+		}
+	}
+	
+	if(params) free_params(params);
+	return 1;
+
+}
+
+int th_get_uri_param_value(str *uri, str *name, str *value)
+{
+	struct sip_uri puri;
+
+	memset(value, 0, sizeof(str));
+	if(parse_uri(uri->s, uri->len, &puri)<0)
+		return -1;
+	return th_get_param_value(&puri.params, name, value);
+}
+
+int th_get_uri_type(str *uri, int *mode, str *value)
+{
+	struct sip_uri puri;
+	int ret;
+	str r2 = {"r2", 2};
+
+	memset(value, 0, sizeof(str));
+	*mode = 0;
+	if(parse_uri(uri->s, uri->len, &puri)<0)
+		return -1;
+
+	LM_DBG("+++++++++++ PARAMS [%.*s]\n", puri.params.len, puri.params.s);
+	if(puri.host.len==th_ip.len
+			&& strncasecmp(puri.host.s, th_ip.s, th_ip.len)==0)
+	{
+		/* host matches TH ip */
+		ret = th_get_param_value(&puri.params, &th_uparam_name, value);
+		if(ret<0)
+			return -1;
+		return 2; /* decode */
+	} else {
+		if(check_self(&puri.host, (puri.port_no)?puri.port_no:SIP_PORT, 0)==1)
+		{
+			/* myself -- matched on all protos */
+			ret = th_get_param_value(&puri.params, &r2, value);
+			if(ret<0)
+				return -1;
+			if(ret==1) /* not found */
+				return 0; /* skip */
+			LM_DBG("+++++++++++++++++++************ [%.*s]\n",
+					value->len, value->s);
+			if(value->len==2 && strncasecmp(value->s, "on", 2)==0)
+				*mode = 1;
+			memset(value, 0, sizeof(str));
+			return 0; /* skip */
+		} else {
+			return 1; /* encode */
+		}
+	}
+}
+
+int th_mask_via(sip_msg_t *msg)
+{
+	hdr_field_t *hdr;
+	struct via_body *via;
+	struct lump* l;
+	int i;
+	str out;
+	int vlen;
+
+	i=0;
+	for(hdr=msg->h_via1; hdr; hdr=next_sibling_hdr(hdr))
+	{
+		for(via=(struct via_body*)hdr->parsed; via; via=via->next)
+		{
+			i++;
+			LM_DBG("=======via[%d]\n", i);
+			LM_DBG("hdr: [%.*s]\n", via->hdr.len, via->hdr.s);
+			vlen = th_skip_rw(via->name.s, via->bsize);
+			LM_DBG("body: %d: [%.*s]\n", vlen, vlen, via->name.s);
+			if(i!=1)
+			{
+				out.s = th_mask_encode(via->name.s, vlen, &th_via_prefix,
+						&out.len);
+				if(out.s==NULL)
+				{
+					LM_ERR("cannot encode via %d\n", i);
+					return -1;
+				}
+					
+				LM_DBG("+body: %d: [%.*s]\n", out.len, out.len, out.s);
+				l=del_lump(msg, via->name.s-msg->buf, vlen, 0);
+				if (l==0)
+				{
+					LM_ERR("failed deleting via [%d]\n", i);
+					pkg_free(out.s);
+					return -1;
+				}
+				if (insert_new_lump_after(l, out.s, out.len, 0)==0){
+					LM_ERR("could not insert new lump\n");
+					pkg_free(out.s);
+					return -1;
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+int th_mask_callid(sip_msg_t *msg)
+{
+	struct lump* l;
+	str out;
+
+	if(th_param_mask_callid==0)
+		return 0;
+
+	if(msg->callid==NULL)
+	{
+		LM_ERR("cannot get Call-Id header\n");
+		return -1;
+	}
+				
+	out.s = th_mask_encode(msg->callid->body.s, msg->callid->body.len, 0,
+						&out.len);
+	if(out.s==NULL)
+	{
+		LM_ERR("cannot encode callid\n");
+		return -1;
+	}
+				
+	l=del_lump(msg, msg->callid->body.s-msg->buf, msg->callid->body.len, 0);
+	if (l==0)
+	{
+		LM_ERR("failed deleting callid\n");
+		pkg_free(out.s);
+		return -1;
+	}
+	if (insert_new_lump_after(l, out.s, out.len, 0)==0) {
+		LM_ERR("could not insert new lump\n");
+		pkg_free(out.s);
+		return -1;
+	}
+
+	return 0;
+}
+
+int th_mask_contact(sip_msg_t *msg)
+{
+	struct lump* l;
+	str out;
+	str in;
+	contact_t *c;
+
+	if(msg->contact==NULL) 
+	{
+		LM_DBG("no contact header\n");
+		return 0;
+	}
+
+	if(parse_contact(msg->contact) < 0)
+	{
+		LM_ERR("failed parsing contact header\n");
+		return -1;
+	}
+	
+	c = ((contact_body_t*)msg->contact->parsed)->contacts;
+	in = c->uri;
+
+	out.s = th_mask_encode(in.s, in.len, &th_uri_prefix, &out.len);
+	if(out.s==NULL)
+	{
+		LM_ERR("cannot encode contact uri\n");
+		return -1;
+	}
+				
+	l=del_lump(msg, in.s-msg->buf, in.len, 0);
+	if (l==0)
+	{
+		LM_ERR("failed deleting contact uri\n");
+		pkg_free(out.s);
+		return -1;
+	}
+	if (insert_new_lump_after(l, out.s, out.len, 0)==0) {
+		LM_ERR("could not insert new lump\n");
+		pkg_free(out.s);
+		return -1;
+	}
+
+	return 0;
+}
+
+int th_mask_record_route(sip_msg_t *msg)
+{
+	hdr_field_t *hdr;
+	struct lump* l;
+	int i;
+	rr_t *rr;
+	str out;
+
+	if(msg->record_route==NULL)
+	{
+		LM_DBG("no record route header\n");
+		return 0;
+	}
+	hdr = msg->record_route;
+	i = 0;
+	while(hdr!=NULL) 
+	{
+		if (parse_rr(hdr) < 0) 
+		{
+			LM_ERR("failed to parse RR\n");
+			return -1;
+		}
+
+		rr =(rr_t*)hdr->parsed;
+		while(rr)
+		{
+			i++;
+			if(i!=1)
+			{
+				out.s = th_mask_encode(rr->nameaddr.uri.s, rr->nameaddr.uri.len,
+						&th_uri_prefix, &out.len);
+				if(out.s==NULL)
+				{
+					LM_ERR("cannot encode r-r %d\n", i);
+					return -1;
+				}
+				l=del_lump(msg, rr->nameaddr.uri.s-msg->buf,
+						rr->nameaddr.uri.len, 0);
+				if (l==0)
+				{
+					LM_ERR("failed deleting r-r [%d]\n", i);
+					pkg_free(out.s);
+					return -1;
+				}
+				if (insert_new_lump_after(l, out.s, out.len, 0)==0){
+					LM_ERR("could not insert new lump\n");
+					pkg_free(out.s);
+					return -1;
+				}
+			}
+			rr = rr->next;
+		}
+		hdr = next_sibling_hdr(hdr);
+	}
+
+	return 0;
+}
+
+int th_unmask_via(sip_msg_t *msg, str *cookie)
+{
+	hdr_field_t *hdr;
+	struct via_body *via;
+	struct via_body *via2;
+	struct via_param *vp;
+	struct lump* l;
+	int i;
+	str out;
+	int vlen;
+
+	i=0;
+	for(hdr=msg->h_via1; hdr; hdr=next_sibling_hdr(hdr))
+	{
+		for(via=(struct via_body*)hdr->parsed; via; via=via->next)
+		{
+			i++;
+			LM_DBG("=======via[%d]\n", i);
+			LM_DBG("hdr: [%.*s]\n", via->hdr.len, via->hdr.s);
+			vlen = th_skip_rw(via->name.s, via->bsize);
+			LM_DBG("body: %d: [%.*s]\n", vlen, vlen, via->name.s);
+			if(i!=1)
+			{
+				vp = th_get_via_param(via, &th_vparam_name);
+				if(vp==NULL)
+				{
+					LM_ERR("cannot find param in via %d\n", i);
+					return -1;
+				}
+				if(i==2)
+					out.s = th_mask_decode(vp->value.s, vp->value.len,
+							&th_vparam_prefix, CRLF_LEN+1, &out.len);
+				else
+					out.s = th_mask_decode(vp->value.s, vp->value.len,
+							&th_vparam_prefix, 0, &out.len);
+				if(out.s==NULL)
+				{
+					LM_ERR("cannot encode via %d\n", i);
+					return -1;
+				}
+					
+				LM_DBG("+body: %d: [%.*s]\n", out.len, out.len, out.s);
+				if(i==2)
+				{
+					via2=pkg_malloc(sizeof(struct via_body));
+					if (via2==0)
+					{
+						LM_ERR("out of memory\n");
+						pkg_free(out.s);
+						return -1;
+
+					}
+					
+					memset(via2, 0, sizeof(struct via_body));
+					memcpy(out.s+out.len, CRLF, CRLF_LEN);
+					out.s[out.len+CRLF_LEN]='X';
+					if(parse_via(out.s, out.s+out.len+CRLF_LEN+1, via2)==NULL)
+					{
+						LM_ERR("error parsing decoded via2\n");
+						free_via_list(via2);
+						pkg_free(out.s);
+						return -1;
+					}
+					out.s[out.len] = '\0';
+					vp = th_get_via_param(via2, &th_cookie_name);
+					if(vp==NULL)
+					{
+						LM_ERR("cannot find cookie in via2\n");
+						free_via_list(via2);
+						pkg_free(out.s);
+						return -1;
+					}
+					*cookie = vp->value;
+					free_via_list(via2);
+				}
+				l=del_lump(msg, via->name.s-msg->buf, vlen, 0);
+				if (l==0)
+				{
+					LM_ERR("failed deleting via [%d]\n", i);
+					pkg_free(out.s);
+					return -1;
+				}
+				if (insert_new_lump_after(l, out.s, out.len, 0)==0)
+				{
+					LM_ERR("could not insert new lump\n");
+					pkg_free(out.s);
+					return -1;
+				}
+			}
+		}
+	}
+
+	return 0;
+}
+
+int th_unmask_callid(sip_msg_t *msg)
+{
+	struct lump* l;
+	str out;
+	
+	if(th_param_mask_callid==0)
+		return 0;
+	
+	if(msg->callid==NULL)
+	{
+		LM_ERR("cannot get Call-Id header\n");
+		return -1;
+	}
+				
+	out.s = th_mask_decode(msg->callid->body.s, msg->callid->body.len, 0, 0,
+						&out.len);
+	if(out.s==NULL)
+	{
+		LM_ERR("cannot decode callid\n");
+		return -1;
+	}
+				
+	l=del_lump(msg, msg->callid->body.s-msg->buf, msg->callid->body.len, 0);
+	if (l==0)
+	{
+		LM_ERR("failed deleting callid\n");
+		pkg_free(out.s);
+		return -1;
+	}
+	if (insert_new_lump_after(l, out.s, out.len, 0)==0) {
+		LM_ERR("could not insert new lump\n");
+		pkg_free(out.s);
+		return -1;
+	}
+
+	return 0;
+}
+
+int th_flip_record_route(sip_msg_t *msg, int mode)
+{
+	hdr_field_t *hdr;
+	struct lump* l;
+	int i;
+	rr_t *rr;
+	str out;
+	int utype;
+	str pval;
+	int r2;
+	int act;
+
+	if(msg->record_route==NULL)
+	{
+		LM_DBG("no record route header\n");
+		return 0;
+	}
+	hdr = msg->record_route;
+	i = 0;
+	act = 0;
+	if(mode==1)
+		act = 2;
+	while(hdr!=NULL) 
+	{
+		if (parse_rr(hdr) < 0) 
+		{
+			LM_ERR("failed to parse RR\n");
+			return -1;
+		}
+
+		rr =(rr_t*)hdr->parsed;
+		while(rr)
+		{
+			i++;
+			r2 = 0;
+			utype = th_get_uri_type(&rr->nameaddr.uri, &r2, &pval);
+			if(utype==0 && mode==1)
+			{
+				if(r2==1)
+				{
+					act--;
+					if(act==0)
+						return 0;
+					utype = 1;
+				} else {
+					return 0;
+				}
+			}
+			out.s = NULL;
+			switch(utype) {
+				case 1: /* encode */
+					if(act!=0 && mode==1)
+					{
+						out.s = th_mask_encode(rr->nameaddr.uri.s,
+							rr->nameaddr.uri.len, &th_uri_prefix, &out.len);
+						if(out.s==NULL)
+						{
+							LM_ERR("cannot encode r-r %d\n", i);
+							return -1;
+						}
+					}
+				break;
+				case 2: /* decode */
+					if(mode==0)
+					{
+						out.s = th_mask_decode(pval.s,
+							pval.len, &th_uparam_prefix, 0, &out.len);
+						if(out.s==NULL)
+						{
+							LM_ERR("cannot decode r-r %d\n", i);
+							return -1;
+						}
+					}
+				break;
+			}
+			if(out.s!=NULL)
+			{
+				l=del_lump(msg, rr->nameaddr.uri.s-msg->buf,
+						rr->nameaddr.uri.len, 0);
+				if (l==0)
+				{
+					LM_ERR("failed deleting r-r [%d]\n", i);
+					pkg_free(out.s);
+					return -1;
+				}
+				if (insert_new_lump_after(l, out.s, out.len, 0)==0){
+					LM_ERR("could not insert new lump\n");
+					pkg_free(out.s);
+					return -1;
+				}
+			}
+			rr = rr->next;
+		}
+		hdr = next_sibling_hdr(hdr);
+	}
+
+	return 0;
+}
+
+int th_unmask_route(sip_msg_t *msg)
+{
+	hdr_field_t *hdr;
+	struct lump* l;
+	int i;
+	rr_t *rr;
+	str out;
+	str eval;
+
+	if(msg->route==NULL)
+	{
+		LM_DBG("no record route header\n");
+		return 0;
+	}
+	hdr = msg->route;
+	i = 0;
+	while(hdr!=NULL) 
+	{
+		if (parse_rr(hdr) < 0) 
+		{
+			LM_ERR("failed to parse RR\n");
+			return -1;
+		}
+
+		rr =(rr_t*)hdr->parsed;
+		while(rr)
+		{
+			i++;
+			if(i!=1)
+			{
+				if(th_get_uri_param_value(&rr->nameaddr.uri, &th_uparam_name,
+							&eval)<0 || eval.len<=0)
+					return -1;
+	
+				out.s = th_mask_decode(eval.s, eval.len,
+							&th_uparam_prefix, 0, &out.len);
+
+				if(out.s==NULL)
+				{
+					LM_ERR("cannot decode R %d\n", i);
+					return -1;
+				}
+				l=del_lump(msg, rr->nameaddr.uri.s-msg->buf,
+						rr->nameaddr.uri.len, 0);
+				if (l==0)
+				{
+					LM_ERR("failed deleting R [%d]\n", i);
+					pkg_free(out.s);
+					return -1;
+				}
+				if (insert_new_lump_after(l, out.s, out.len, 0)==0){
+					LM_ERR("could not insert new lump\n");
+					pkg_free(out.s);
+					return -1;
+				}
+			}
+			rr = rr->next;
+		}
+		hdr = next_sibling_hdr(hdr);
+	}
+
+	return 0;
+}
+
+int th_unmask_ruri(sip_msg_t *msg)
+{
+	str eval;
+	struct lump* l;
+	str out;
+
+	if(th_get_uri_param_value(&REQ_LINE(msg).uri, &th_uparam_name, &eval)<0
+			|| eval.len<=0)
+		return -1;
+	
+	out.s = th_mask_decode(eval.s, eval.len,
+				&th_uparam_prefix, 0, &out.len);
+	if(out.s==NULL)
+	{
+		LM_ERR("cannot decode r-uri\n");
+		return -1;
+	}
+					
+	LM_DBG("+decoded: %d: [%.*s]\n", out.len, out.len, out.s);
+	l=del_lump(msg, REQ_LINE(msg).uri.s-msg->buf, REQ_LINE(msg).uri.len, 0);
+	if (l==0)
+	{
+		LM_ERR("failed deleting r-uri\n");
+		pkg_free(out.s);
+		return -1;
+	}
+	if (insert_new_lump_after(l, out.s, out.len, 0)==0)
+	{
+		LM_ERR("could not insert new lump\n");
+		pkg_free(out.s);
+		return -1;
+	}
+
+	return 0;
+}
+
+char* th_msg_update(sip_msg_t *msg, unsigned int *olen)
+{
+	struct dest_info dst;
+
+	init_dest_info(&dst);
+	dst.proto = PROTO_UDP;
+	return build_req_buf_from_sip_req(msg,
+			olen, &dst, BUILD_NO_LOCAL_VIA|BUILD_NO_VIA1_UPDATE);
+}
+
+int th_add_via_cookie(sip_msg_t *msg, struct via_body *via)
+{
+	struct lump* l;
+	int viap;
+	str out;
+
+	if (via->params.s) {
+		viap = via->params.s - via->hdr.s - 1;
+	} else {
+		viap = via->host.s - via->hdr.s + via->host.len;
+		if (via->port!=0)
+			viap += via->port_str.len + 1; /* +1 for ':'*/
+	}
+	l = anchor_lump(msg, via->hdr.s - msg->buf + viap, 0, 0);
+	if (l==0)
+	{
+		LM_ERR("failed adding cookie to via [%p]\n", via);
+		return -1;
+	}
+	
+	out.len = 1+th_cookie_name.len+1+th_cookie_value.len+1;
+	out.s = (char*)pkg_malloc(out.len+1);
+	if(out.s==0)
+	{
+		LM_ERR("no pkg memory\n");
+		return -1;
+	}
+	out.s[0] = ';';
+	memcpy(out.s+1, th_cookie_name.s, th_cookie_name.len);
+	out.s[th_cookie_name.len+1]='=';
+	memcpy(out.s+th_cookie_name.len+2, th_cookie_value.s, th_cookie_value.len);
+	out.s[out.len-1] = 'v';
+	out.s[out.len] = '\0';
+	if (insert_new_lump_after(l, out.s, out.len, 0)==0){
+		LM_ERR("could not insert new lump!\n");
+		pkg_free(out.s);
+		return -1;
+	}
+	return 0;
+}
+
+int th_add_hdr_cookie(sip_msg_t *msg)
+{
+	struct lump* anchor;
+	str h;
+
+	h.len = th_cookie_name.len + 2 + th_cookie_value.len + 1 + CRLF_LEN;
+	h.s = (char*)pkg_malloc(h.len+1);
+	if(h.s == 0)
+	{
+		LM_ERR("no more pkg\n");
+		return -1;
+	}
+	anchor = anchor_lump(msg, msg->unparsed - msg->buf, 0, 0);
+	if(anchor == 0)
+	{
+		LM_ERR("can't get anchor\n");
+		pkg_free(h.s);
+		return -1;
+	}
+	memcpy(h.s, th_cookie_name.s, th_cookie_name.len);
+	memcpy(h.s+th_cookie_name.len, ": ", 2);
+	memcpy(h.s+th_cookie_name.len+2, th_cookie_value.s, th_cookie_value.len);
+	memcpy(h.s+th_cookie_name.len+2+th_cookie_value.len+1, CRLF, CRLF_LEN);
+	h.s[h.len-1-CRLF_LEN] = 'h';
+	h.s[h.len] = '\0';
+	if (insert_new_lump_before(anchor, h.s, h.len, 0) == 0)
+	{
+		LM_ERR("can't insert lump\n");
+		pkg_free(h.s);
+		return -1;
+	}
+	LM_DBG("+++++++++++++ added cookie header [%s]\n", h.s);
+	return 0;
+}
+
+struct via_param *th_get_via_cookie(sip_msg_t *msg, struct via_body *via)
+{
+	struct via_param *p;
+	for(p=via->param_lst; p; p=p->next)
+	{
+		if(p->name.len==th_cookie_name.len
+				&& strncasecmp(p->name.s, th_cookie_name.s,
+					th_cookie_name.len)==0)
+			return p;
+	}
+	return NULL;
+}
+
+hdr_field_t *th_get_hdr_cookie(sip_msg_t *msg)
+{
+	hdr_field_t *hf;
+	for (hf=msg->headers; hf; hf=hf->next)
+	{
+		if (hf->name.len==th_cookie_name.len
+				&& strncasecmp(hf->name.s, th_cookie_name.s,
+					th_cookie_name.len)==0)
+			return hf;
+	}
+	return NULL;
+}
+
+int th_add_cookie(sip_msg_t *msg)
+{
+	if(th_cookie_value.len<=0)
+		return 0;
+	th_add_hdr_cookie(msg);
+	th_add_via_cookie(msg, msg->via1);
+	return 0;
+}
+
+int th_del_hdr_cookie(sip_msg_t *msg)
+{
+	hdr_field_t *hf;
+	struct lump* l;
+	for (hf=msg->headers; hf; hf=hf->next)
+	{
+		if (hf->name.len==th_cookie_name.len
+				&& strncasecmp(hf->name.s, th_cookie_name.s,
+					th_cookie_name.len)==0)
+		{
+			l=del_lump(msg, hf->name.s-msg->buf, hf->len, 0);
+			if (l==0) {
+				LM_ERR("unable to delete cookie header\n");
+				return -1;
+			}
+			return 0;
+		}
+	}
+	return 0;
+}
+
+int th_del_via_cookie(sip_msg_t *msg, struct via_body *via)
+{
+	struct via_param *p;
+	struct lump* l;
+	for(p=via->param_lst; p; p=p->next)
+	{
+		if(p->name.len==th_cookie_name.len
+				&& strncasecmp(p->name.s, th_cookie_name.s,
+					th_cookie_name.len)==0)
+		{
+			l=del_lump(msg, p->start-msg->buf-1, p->size+1, 0);
+			if (l==0) {
+				LM_ERR("unable to delete cookie header\n");
+				return -1;
+			}
+			return 0;
+		}
+	}
+	return 0;
+}
+
+int th_del_cookie(sip_msg_t *msg)
+{
+	th_del_hdr_cookie(msg);
+	if(msg->first_line.type==SIP_REPLY)
+		th_del_via_cookie(msg, msg->via1);
+	return 0;
+}
+
+
+char* th_get_cookie(sip_msg_t *msg, int *clen)
+{
+	hdr_field_t *hf;
+	struct via_param *p;
+
+	hf = th_get_hdr_cookie(msg);
+	if(hf!=NULL)
+	{
+		*clen = hf->body.len;
+		return hf->body.s;
+	}
+	p = th_get_via_cookie(msg, msg->via1);
+	if(p!=NULL)
+	{
+		*clen = p->value.len;
+		return p->value.s;
+	}
+
+	*clen = 3;
+	return "xxx";
+}
+
+int th_route_direction(sip_msg_t *msg)
+{
+	rr_t *rr;
+	struct sip_uri puri;
+	str ftn = {"ftag", 4};
+	str ftv = {0, 0};
+
+	if(get_from(msg)->tag_value.len<=0)
+	{
+		LM_ERR("failed to get from header tag\n");
+		return -1;
+	}
+	if(msg->route==NULL)
+	{
+		LM_DBG("no route header - downstream\n");
+		return 0;
+	}
+	if (parse_rr(msg->route) < 0) 
+	{
+		LM_ERR("failed to parse route header\n");
+		return -1;
+	}
+
+	rr =(rr_t*)msg->route->parsed;
+
+	if (parse_uri(rr->nameaddr.uri.s, rr->nameaddr.uri.len, &puri) < 0) {
+		LM_ERR("failed to parse the first route URI\n");
+		return -1;
+	}
+	if(th_get_param_value(&puri.params, &ftn, &ftv)!=0)
+		return 0;
+
+	if(get_from(msg)->tag_value.len!=ftv.len
+			|| strncmp(get_from(msg)->tag_value.s, ftv.s, ftv.len)!=0)
+	{
+		LM_DBG("ftag mismatch\n");
+		return 1;
+	}
+	LM_DBG("ftag match\n");
+	return 0;
+}
+
+int th_skip_msg(sip_msg_t *msg)
+{
+	if((get_cseq(msg)->method_id)&(METHOD_REGISTER|METHOD_PUBLISH))
+		return 1;
+
+	return 0;
+}
+

+ 45 - 0
modules/topoh/th_msg.h

@@ -0,0 +1,45 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2009 SIP-Router.org
+ *
+ * This file is part of Extensible SIP Router, a free SIP server.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _TH_MSG_H_
+#define _TH_MSG_H_
+
+#include "../../parser/msg_parser.h"
+
+int th_mask_via(sip_msg_t *msg);
+int th_mask_callid(sip_msg_t *msg);
+int th_mask_contact(sip_msg_t *msg);
+int th_mask_record_route(sip_msg_t *msg);
+int th_unmask_via(sip_msg_t *msg, str *cookie);
+int th_unmask_callid(sip_msg_t *msg);
+int th_flip_record_route(sip_msg_t *msg, int mode);
+int th_unmask_ruri(sip_msg_t *msg);
+int th_unmask_route(sip_msg_t *msg);
+char* th_msg_update(sip_msg_t *msg, unsigned int *olen);
+int th_add_via_cookie(sip_msg_t *msg, struct via_body *via);
+int th_add_hdr_cookie(sip_msg_t *msg);
+hdr_field_t *th_get_hdr_cookie(sip_msg_t *msg);
+int th_add_cookie(sip_msg_t *msg);
+int th_route_direction(sip_msg_t *msg);
+char* th_get_cookie(sip_msg_t *msg, int *clen);
+int th_del_cookie(sip_msg_t *msg);
+int th_skip_msg(sip_msg_t *msg);
+
+#endif

+ 332 - 0
modules/topoh/topoh_mod.c

@@ -0,0 +1,332 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2009 SIP-Router.org
+ *
+ * This file is part of Extensible SIP Router, a free SIP server.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "../../sr_module.h"
+#include "../../events.h"
+#include "../../dprint.h"
+#include "../../ut.h"
+#include "../../parser/msg_parser.h"
+#include "../../parser/parse_to.h"
+#include "../../parser/parse_from.h"
+
+#include "th_mask.h"
+#include "th_msg.h"
+
+MODULE_VERSION
+
+
+/** module parameters */
+str _th_key = { "aL9.n8~Hm]Z", 0 };
+str th_cookie_name = {"TH", 0};
+str th_cookie_value = {0, 0};
+str th_ip = {"10.1.1.2", 0};
+str th_uparam_name = {"line", 0};
+str th_uparam_prefix = {"sr-", 0};
+str th_vparam_name = {"branch", 0};
+str th_vparam_prefix = {"z9hG4bKsr-", 0};
+
+str th_via_prefix = {0, 0};
+str th_uri_prefix = {0, 0};
+
+int th_param_mask_callid = 0;
+
+int th_msg_received(void *data);
+int th_msg_sent(void *data);
+
+/** module functions */
+static int mod_init(void);
+
+static param_export_t params[]={
+	{"mask_key",		STR_PARAM, &_th_key.s},
+	{"mask_callid",		INT_PARAM, &th_param_mask_callid},
+	{"uparam_name",		STR_PARAM, &th_uparam_name.s},
+	{"uparam_prefix",	STR_PARAM, &th_uparam_prefix.s},
+	{"vparam_name",		STR_PARAM, &th_vparam_name.s},
+	{"vparam_prefix",	STR_PARAM, &th_vparam_prefix.s},
+	{0,0,0}
+};
+
+
+/** module exports */
+struct module_exports exports= {
+	"topoh",
+	DEFAULT_DLFLAGS, /* dlopen flags */
+	0,
+	params,
+	0,          /* exported statistics */
+	0,          /* exported MI functions */
+	0,          /* exported pseudo-variables */
+	0,          /* extra processes */
+	mod_init,   /* module initialization function */
+	0,
+	0,
+	0           /* per-child init function */
+};
+
+/**
+ * init module function
+ */
+static int mod_init(void)
+{
+	th_cookie_name.len = strlen(th_cookie_name.s);
+	th_ip.len = strlen(th_ip.s);
+	th_uparam_name.len = strlen(th_uparam_name.s);
+	th_uparam_prefix.len = strlen(th_uparam_prefix.s);
+	th_vparam_name.len = strlen(th_vparam_name.s);
+	th_vparam_prefix.len = strlen(th_vparam_prefix.s);
+
+	/* 'SIP/2.0/UDP ' + ip + ';' + param + '=' + prefix (+ '\0') */
+	th_via_prefix.len = 12 + th_ip.len + 1 + th_vparam_name.len + 1
+		+ th_vparam_prefix.len;
+	th_via_prefix.s = (char*)pkg_malloc(th_via_prefix.len+1);
+	if(th_via_prefix.s==NULL)
+		goto error;
+	/* 'sip:' + ip + ';' + param + '=' + prefix (+ '\0') */
+	th_uri_prefix.len = 4 + th_ip.len + 1 + th_uparam_name.len + 1
+		+ th_uparam_prefix.len;
+	th_uri_prefix.s = (char*)pkg_malloc(th_uri_prefix.len+1);
+	if(th_uri_prefix.s==NULL)
+		goto error;
+	/* build via prefix */
+	memcpy(th_via_prefix.s, "SIP/2.0/UDP ", 12);
+	memcpy(th_via_prefix.s+12, th_ip.s, th_ip.len);
+	th_via_prefix.s[12+th_ip.len] = ';';
+	memcpy(th_via_prefix.s+12+th_ip.len+1, th_vparam_name.s, th_vparam_name.len);
+	th_via_prefix.s[12+th_ip.len+1+th_vparam_name.len] = '=';
+	memcpy(th_via_prefix.s+12+th_ip.len+1+th_vparam_name.len+1,
+			th_vparam_prefix.s, th_vparam_prefix.len);
+	th_via_prefix.s[th_via_prefix.len] = '\0';
+	LM_DBG("VIA prefix: [%s]\n", th_via_prefix.s);
+	/* build uri prefix */
+	memcpy(th_uri_prefix.s, "sip:", 4);
+	memcpy(th_uri_prefix.s+4, th_ip.s, th_ip.len);
+	th_uri_prefix.s[4+th_ip.len] = ';';
+	memcpy(th_uri_prefix.s+4+th_ip.len+1, th_uparam_name.s, th_uparam_name.len);
+	th_uri_prefix.s[4+th_ip.len+1+th_uparam_name.len] = '=';
+	memcpy(th_uri_prefix.s+4+th_ip.len+1+th_uparam_name.len+1,
+			th_uparam_prefix.s, th_uparam_prefix.len);
+	th_uri_prefix.s[th_uri_prefix.len] = '\0';
+	LM_DBG("URI prefix: [%s]\n", th_uri_prefix.s);
+
+	th_mask_init();
+	sr_event_register_cb(SREV_NET_DATA_IN, th_msg_received);
+	sr_event_register_cb(SREV_NET_DATA_OUT, th_msg_sent);
+	return 0;
+error:
+	return -1;
+}
+
+int th_prepare_msg(sip_msg_t *msg)
+{
+	if (parse_msg(msg->buf, msg->len, msg)!=0)
+	{
+		LM_DBG("outbuf buffer parsing failed!");
+		return 1;
+	}
+
+	if (parse_headers(msg, HDR_EOH_F, 0)==-1)
+	{
+		LM_DBG("parsing headers failed");
+		return 2;
+	}
+
+	if(parse_from_header(msg)<0)
+	{
+		LM_ERR("cannot parse FROM header\n");
+		return 3;
+	}
+	
+	return 0;
+}
+
+int th_msg_received(void *data)
+{
+	sip_msg_t msg;
+	str *obuf;
+	char *nbuf;
+	int direction;
+	int dialog;
+
+	obuf = (str*)data;
+	memset(&msg, 0, sizeof(sip_msg_t));
+	msg.buf = obuf->s;
+	msg.len = obuf->len;
+
+	th_prepare_msg(&msg);
+
+	if(th_skip_msg(&msg))
+	{
+		goto done;
+	}
+
+	direction = 0;
+	th_cookie_value.s = "xx";
+	th_cookie_value.len = 2;
+	if(msg.first_line.type==SIP_REQUEST)
+	{
+		dialog = (get_to(&msg)->tag_value.len>0)?1:0;
+		if(dialog)
+		{
+			direction = th_route_direction(&msg);
+			if(direction<0)
+			{
+				LM_ERR("not able to detect direction\n");
+				goto done;
+			}
+			th_cookie_value.s = (direction==0)?"dc":"uc";
+		} else {
+			th_cookie_value.s = "di";
+		}
+		if(dialog)
+		{
+			/* dialog request */
+			th_unmask_ruri(&msg);
+			th_unmask_route(&msg);
+			if(direction==1)
+			{
+				th_unmask_callid(&msg);
+			}
+		}
+	} else {
+		/* reply */
+		th_unmask_via(&msg, &th_cookie_value);
+		th_flip_record_route(&msg, 0);
+		if(th_cookie_value.s[0]=='u')
+		{
+			th_cookie_value.s = "dc";
+		} else {
+			th_cookie_value.s = "uc";
+			th_unmask_callid(&msg);
+		}
+		th_cookie_value.len = 2;
+	}
+
+	th_add_cookie(&msg);
+	nbuf = th_msg_update(&msg, (unsigned int*)&obuf->len);
+
+	if(obuf->len>=BUF_SIZE)
+	{
+		LM_ERR("new buffer overflow (%d)\n", obuf->len);
+		pkg_free(nbuf);
+		return -1;
+	}
+	memcpy(obuf->s, nbuf, obuf->len);
+	obuf->s[obuf->len] = '\0';
+
+done:
+	free_sip_msg(&msg);
+	return 0;
+}
+
+int th_msg_sent(void *data)
+{
+	sip_msg_t msg;
+	str *obuf;
+	int direction;
+	int dialog;
+	int local;
+
+	obuf = (str*)data;
+	memset(&msg, 0, sizeof(sip_msg_t));
+	msg.buf = obuf->s;
+	msg.len = obuf->len;
+
+	th_prepare_msg(&msg);
+
+	if(th_skip_msg(&msg))
+	{
+		goto done;
+	}
+
+	th_cookie_value.s = th_get_cookie(&msg, &th_cookie_value.len);
+	LM_DBG("the COOKIE is [%.*s]\n", th_cookie_value.len, th_cookie_value.s);
+	if(th_cookie_value.s[0]!='x')
+		th_del_cookie(&msg);
+	if(msg.first_line.type==SIP_REQUEST)
+	{
+		direction = (th_cookie_value.s[0]=='u')?1:0; /* upstream/downstram */
+		dialog = (get_to(&msg)->tag_value.len>0)?1:0;
+		local = (th_cookie_value.s[0]!='d'&&th_cookie_value.s[0]!='u')?1:0;
+		/* local generated requests */
+		if(local)
+		{
+			/* ACK and CANCEL go downstream */
+			if(get_cseq(&msg)->method_id==METHOD_ACK
+					|| get_cseq(&msg)->method_id==METHOD_CANCEL)
+			{
+				th_mask_callid(&msg);
+				goto ready;
+			} else {
+				/* should be for upstream */
+				goto done;
+			}
+		}
+		th_mask_via(&msg);
+		th_mask_contact(&msg);
+		th_mask_record_route(&msg);
+		if(dialog)
+		{
+			/* dialog request */
+			if(direction==0)
+			{
+				/* downstream */
+				th_mask_callid(&msg);
+			}
+		} else {
+			/* initial request */
+			th_mask_callid(&msg);
+		}
+	} else {
+		/* reply */
+		if(th_cookie_value.s[th_cookie_value.len-1]=='x')
+		{
+			/* ?!?! - we should have a cookie in any reply case */
+			goto done;
+		}
+		if(th_cookie_value.s[th_cookie_value.len-1]=='v')
+		{
+			/* reply generated locally - direction was set by request */
+			if(th_cookie_value.s[0]=='u')
+			{
+				th_mask_callid(&msg);
+			}
+		} else {
+			th_flip_record_route(&msg, 1);
+			th_mask_contact(&msg);
+			if(th_cookie_value.s[0]=='d')
+			{
+				th_mask_callid(&msg);
+			}
+		}
+	}
+
+ready:
+	obuf->s = th_msg_update(&msg, (unsigned int*)&obuf->len);
+
+done:
+	free_sip_msg(&msg);
+	return 0;
+}
+

+ 6 - 0
receive.c

@@ -98,6 +98,12 @@ int receive_msg(char* buf, unsigned int len, struct receive_info* rcv_info)
 	struct timezone tz;
 	unsigned int diff;
 #endif
+	str inb;
+
+	inb.s = buf;
+	inb.len = len;
+	sr_event_exec(SREV_NET_DATA_IN, (void*)&inb);
+	len = inb.len;
 
 	msg=pkg_malloc(sizeof(struct sip_msg));
 	if (msg==0) {