>From 9830e907a5a14e9405a6cabd275b405419951365 Mon Sep 17 00:00:00 2001
From: Tristan Bruns <bruns@decoit.de>
Date: Fri, 5 Aug 2011 09:24:14 +0200
Subject: [PATCH 3/5] modules_k/siptrace: Add "x-headers" feature

The "x-headers" feature stores the fromip, toip, method and direction
in the message body (using X-* headers). This allows to transmit them
using duplicate_uri from one kamailio to an other.

Signed-off-by: Tristan Bruns <bruns@decoit.de>
---
 modules_k/siptrace/doc/siptrace_admin.xml |   60 ++++++++++
 modules_k/siptrace/siptrace.c             |  175 ++++++++++++++++++++++++++++-
 2 files changed, 234 insertions(+), 1 deletions(-)

diff --git a/modules_k/siptrace/doc/siptrace_admin.xml b/modules_k/siptrace/doc/siptrace_admin.xml
index cbca2e6..3924f1e 100644
--- a/modules_k/siptrace/doc/siptrace_admin.xml
+++ b/modules_k/siptrace/doc/siptrace_admin.xml
@@ -305,6 +305,66 @@ modparam("siptrace", "trace_sl_acks", 0)
 </programlisting>
 		</example>
 	</section>
+	<section>
+		<title><varname>xheaders_write</varname> (integer)</title>
+		<para>
+		Parameter to enable/disable writing of x-headers.
+		</para>
+		<para>
+		Stores fromip, toip, method and direction in X-Siptrace-* headers.
+		This allows to transmit them to a second kamailio server
+		using the duplicate_uri feature.
+		Because the headers are added after the data is written to the database,
+		the headers only show up in the packets sent by duplicate_uri.
+		</para>
+		<para>
+		See <varname>xheaders_read</varname>, it should be used on the receiving
+		side.
+		</para>
+		<para>
+		Note: The headers are first read, then written. This allows to relay
+		the information over more then two kamailio servers by setting both
+		<varname>xheaders_write</varname> and <varname>xheaders_read</varname>
+		to "1" on the servers in the middle.
+		</para>
+		<para>
+		<emphasis>
+			Default value is "0".
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>xheaders_write</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("siptrace", "xheaders_write", 0)
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>xheaders_read</varname> (integer)</title>
+		<para>
+		Parameter to enable/disable reading of x-headers.
+		</para>
+		<para>
+		Reads and removes the X-Siptrace-* headers. Packets not containing the
+		headers are neither stored to the database nor relayed (duplicate_uri).
+		See <varname>xheaders_write</varname> for further information.
+		</para>
+		<para>
+		<emphasis>
+			Default value is "0".
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>xheaders_read</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("siptrace", "xheaders_read", 0)
+...
+</programlisting>
+		</example>
+	</section>
 	</section>
 	
 	<section>
diff --git a/modules_k/siptrace/siptrace.c b/modules_k/siptrace/siptrace.c
index c970002..939f290 100644
--- a/modules_k/siptrace/siptrace.c
+++ b/modules_k/siptrace/siptrace.c
@@ -113,18 +113,26 @@ static str direction_column   = str_init("direction");   /* 09 */
 
 #define NR_KEYS 10
 
+#define XHEADERS_BUFSIZE 512
+
 int trace_flag = -1;
 int trace_on   = 0;
 int trace_sl_acks = 1;
 
 int trace_to_database = 1;
 
+int xheaders_write = 0;
+int xheaders_read = 0;
+
 str    dup_uri_str      = {0, 0};
 struct sip_uri *dup_uri = 0;
 
 int *trace_on_flag = NULL;
 int *trace_to_database_flag = NULL;
 
+int *xheaders_write_flag = NULL;
+int *xheaders_read_flag = NULL;
+
 static unsigned short traced_user_avp_type = 0;
 static int_str traced_user_avp;
 static str traced_user_avp_str = {NULL, 0};
@@ -172,6 +180,8 @@ static param_export_t params[] = {
 	{"trace_to_database",  INT_PARAM, &trace_to_database    },
 	{"trace_local_ip",     STR_PARAM, &trace_local_ip.s     },
 	{"trace_sl_acks",      INT_PARAM, &trace_sl_acks        },
+	{"xheaders_write",     INT_PARAM, &xheaders_write       },
+	{"xheaders_read",      INT_PARAM, &xheaders_read        },
 	{0, 0, 0}
 };
 
@@ -294,6 +304,15 @@ static int mod_init(void)
 	
 	*trace_to_database_flag = trace_to_database;
 	
+	xheaders_write_flag = (int*)shm_malloc(sizeof(int));
+	xheaders_read_flag = (int*)shm_malloc(sizeof(int));
+	if (!(xheaders_write_flag && xheaders_read_flag)) {
+		LM_ERR("no more shm memory left\n");
+		return -1;
+	}
+	*xheaders_write_flag = xheaders_write;
+	*xheaders_read_flag = xheaders_read;
+
 	/* register callbacks to TM */
 	if (load_tm_api(&tmb)!=0)
 	{
@@ -474,6 +493,153 @@ error:
 	return -1;
 }
 
+// Appends x-headers to the message in sto->body containing data from sto
+static int sip_trace_xheaders_write(struct _siptrace_data *sto)
+{
+	if(xheaders_write_flag==NULL || *xheaders_write_flag==0)
+		return 0;
+
+	// Memory for the message with some additional headers.
+	// It gets free()ed in sip_trace_xheaders_free().
+	char* buf = malloc(sto->body.len + XHEADERS_BUFSIZE);
+	if (buf == NULL) {
+		LM_ERR("sip_trace_xheaders_write: out of memory\n");
+		return -1;
+	}
+
+	// Copy the whole message to buf first; it must be \0-terminated for
+	// strstr() to work. Then search for the end-of-header sequence.
+	memcpy(buf, sto->body.s, sto->body.len);
+	buf[sto->body.len] = '\0';
+	char* eoh = strstr(buf, "\r\n\r\n");
+	if (eoh == NULL) {
+		LM_ERR("sip_trace_xheaders_write: malformed message\n");
+		return -1;
+	}
+	eoh += 2; // the first \r\n belongs to the last header => skip it
+
+	// Write the new headers a the end-of-header position. This overwrites
+	// the \r\n terminating the old headers and the beginning of the message
+	// body. Both will be recovered later.
+	int bytes_written = snprintf(eoh, XHEADERS_BUFSIZE,
+		"X-Siptrace-Fromip: %.*s\r\n"
+		"X-Siptrace-Toip: %.*s\r\n"
+		"X-Siptrace-Method: %.*s\r\n"
+		"X-Siptrace-Dir: %s\r\n",
+		sto->fromip.len, sto->fromip.s,
+		sto->toip.len, sto->toip.s,
+		sto->method.len, sto->method.s,
+		sto->dir);
+	if (bytes_written >= XHEADERS_BUFSIZE) {
+		LM_ERR("sip_trace_xheaders_write: string too long\n");
+		return -1;
+	}
+
+	// Copy the \r\n terminating the old headers and the message body from the
+	// old buffer in sto->body.s to the new end-of-header in buf.
+	int eoh_offset = eoh - buf;
+	char* new_eoh = eoh + bytes_written;
+	memcpy(new_eoh, sto->body.s + eoh_offset, sto->body.len - eoh_offset);
+
+	// Change sto to point to the new buffer.
+	sto->body.s = buf;
+	sto->body.len += bytes_written;
+	return 0;
+}
+
+// Parses x-headers, saves the data back to sto, and removes the x-headers
+// from the message in sto->buf
+static int sip_trace_xheaders_read(struct _siptrace_data *sto)
+{
+	if(xheaders_read_flag==NULL || *xheaders_read_flag==0)
+		return 0;
+
+	// Find the end-of-header marker \r\n\r\n
+	char* searchend = sto->body.s + sto->body.len - 3;
+	char* eoh = memchr(sto->body.s, '\r', searchend - eoh);
+	while (eoh != NULL && eoh < searchend) {
+		if (memcmp(eoh, "\r\n\r\n", 4) == 0)
+			break;
+		eoh = memchr(eoh + 1, '\r', searchend - eoh);
+	}
+	if (eoh == NULL) {
+		LM_ERR("sip_trace_xheaders_read: malformed message\n");
+		return -1;
+	}
+
+	// Find x-headers: eoh will be overwritten by \0 to allow the use of
+	// strstr(). The byte at eoh will later be recovered, when the
+	// message body is shifted towards the beginning of the message
+	// to remove the x-headers.
+	*eoh = '\0';
+	char* xheaders = strstr(sto->body.s, "\r\nX-Siptrace-Fromip: ");
+	if (xheaders == NULL) {
+		LM_ERR("sip_trace_xheaders_read: message without x-headers "
+			"from %.*s, callid %.*s\n",
+			sto->fromip.len, sto->fromip.s, sto->callid.len, sto->callid.s);
+		return -1;
+	}
+
+	// Allocate memory for new strings in sto
+	// (gets free()ed in sip_trace_xheaders_free() )
+	sto->fromip.s = malloc(51);
+	sto->toip.s = malloc(51);
+	sto->method.s = malloc(51);
+	sto->dir = malloc(4);
+	if (!(sto->fromip.s && sto->toip.s && sto->method.s && sto->dir)) {
+		LM_ERR("sip_trace_xheaders_read: out of memory\n");
+		goto erroraftermalloc;
+	}
+
+	// Parse the x-headers: scanf()
+	if (sscanf(xheaders, "\r\n"
+			"X-Siptrace-Fromip: %50s\r\n"
+			"X-Siptrace-Toip: %50s\r\n"
+			"X-Siptrace-Method: %50s\r\n"
+			"X-Siptrace-Dir: %3s",
+			sto->fromip.s, sto->toip.s,
+			sto->method.s,
+			sto->dir) == EOF) {
+		LM_ERR("sip_trace_xheaders_read: malformed x-headers\n");
+		goto erroraftermalloc;
+	}
+	sto->fromip.len = strlen(sto->fromip.s);
+	sto->toip.len = strlen(sto->toip.s);
+	sto->method.len = strlen(sto->method.s);
+
+	// Remove the x-headers: the message body is shifted towards the beginning
+	// of the message, overwriting the x-headers. Before that, the byte at eoh
+	// is recovered.
+	*eoh = '\r';
+	memmove(xheaders, eoh, sto->body.len - (eoh - sto->body.s));
+	sto->body.len -= eoh - xheaders;
+
+	return 0;
+
+erroraftermalloc:
+	if (sto->fromip.s) free(sto->fromip.s);
+	if (sto->toip.s) free(sto->toip.s);
+	if (sto->method.s) free(sto->method.s);
+	if (sto->dir) free(sto->dir);
+	return -1;
+}
+
+// Frees the memory allocated by sip_trace_xheaders_{write,read}
+static int sip_trace_xheaders_free(struct _siptrace_data *sto)
+{
+	if (xheaders_write_flag != NULL && *xheaders_write_flag != 0) {
+		free(sto->body.s);
+	}
+
+	if (xheaders_read_flag != NULL && *xheaders_read_flag != 0) {
+		free(sto->fromip.s);
+		free(sto->toip.s);
+		free(sto->dir);
+	}
+
+	return 0;
+}
+
 static int sip_trace_store(struct _siptrace_data *sto)
 {
 	if(sto==NULL)
@@ -482,8 +648,15 @@ static int sip_trace_store(struct _siptrace_data *sto)
 		return -1;
 	}
 	
+	if (sip_trace_xheaders_read(sto) != 0) return -1;
+	int ret = sip_trace_store_db(sto);
+
+	if (sip_trace_xheaders_write(sto) != 0) return -1;
 	trace_send_duplicate(sto->body.s, sto->body.len);
-	return sip_trace_store_db(sto);
+
+	if (sip_trace_xheaders_free(sto) != 0) return -1;
+
+	return ret;
 }
 
 static int sip_trace_store_db(struct _siptrace_data *sto)
-- 
1.7.3.4



