Bläddra i källkod

sipdump: new module saving sip traffic and additional details into local files

- aiming to help troubleshooting during development or testing,
  especially when dealing with SIP over TLS with forward privacy
Daniel-Constantin Mierla 8 år sedan
förälder
incheckning
c7f40f8a2a

+ 11 - 0
src/modules/sipdump/Makefile

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

+ 216 - 0
src/modules/sipdump/README

@@ -0,0 +1,216 @@
+SIPDUMP Module
+
+Daniel-Constantin Mierla
+
+   <[email protected]>
+
+Edited by
+
+Daniel-Constantin Mierla
+
+   <[email protected]>
+
+   Copyright © 2017 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. enable (int)
+              3.2. wait (int)
+              3.3. rotate (int)
+              3.4. folder (str)
+              3.5. fprefix (str)
+
+        4. Functions
+
+              4.1. sipdump_send(tag)
+
+        5. RPC Commands
+
+              5.1. sipdump.enable
+
+   List of Examples
+
+   1.1. Set enable parameter
+   1.2. Set wait parameter
+   1.3. Set rotate parameter
+   1.4. Set folder parameter
+   1.5. Set fprefix parameter
+   1.6. sipdump_send usage
+   1.7. sipdump.enable usage
+
+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. enable (int)
+        3.2. wait (int)
+        3.3. rotate (int)
+        3.4. folder (str)
+        3.5. fprefix (str)
+
+   4. Functions
+
+        4.1. sipdump_send(tag)
+
+   5. RPC Commands
+
+        5.1. sipdump.enable
+
+1. Overview
+
+   This module writes SIP traffic and some associated details into local
+   files. It intercepts automatically all the SIP traffic received or sent
+   by Kamailio and provides a function to trigger storage from
+   configuration file.
+
+   Received traffic has the tag 'rcv' and the one to be sent has the tag
+   'snd'. The tag value is provided as parameter for the config function.
+
+   Besides the SIP packets, the module aims to save details related to
+   Kamailio runtime environment that are useful for troubleshooting, like
+   process id, child rank, a.s.o.
+
+   The module should be useful for troubleshooting during development or
+   testing of new deployments, especially when dealing with traffic over
+   TLS with forward privacy, when other tools such as wireshark cannot
+   decrypt. For production environments with a lot of SIP traffic, look at
+   siptrace and sipcapture modules for a scalable alternative to capture
+   all the SIP traffic and then search using Homer Sipcapture web toolkit.
+
+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. enable (int)
+   3.2. wait (int)
+   3.3. rotate (int)
+   3.4. folder (str)
+   3.5. fprefix (str)
+
+3.1. enable (int)
+
+   Enable storage.
+
+   Default value is 1 (0 - off; 1 - on).
+
+   Example 1.1. Set enable parameter
+...
+modparam("sipdump", "enable", 0)
+...
+
+3.2. wait (int)
+
+   Wait time (microseconds) when no SIP traffic is received.
+
+   Default value is 100.
+
+   Example 1.2. Set wait parameter
+...
+modparam("sipdump", "wait", 2000)
+...
+
+3.3. rotate (int)
+
+   Time interval in seconds to rotate files.
+
+   Default value is 7200 (2 hours).
+
+   Example 1.3. Set rotate parameter
+...
+modparam("sipdump", "rotate", 3600)
+...
+
+3.4. folder (str)
+
+   Path to the folder where to save the files.
+
+   Default value is "/tmp".
+
+   Example 1.4. Set folder parameter
+...
+modparam("sipdump", "folder", "/var/run/kamailio")
+...
+
+3.5. fprefix (str)
+
+   File name prefix. The date is appended to this prefix in the format
+   yyyy-mm-dd--hh-mm-ss. The extension of the file is ".data".
+
+   Default value is "kamailio-sipdump-".
+
+   Example 1.5. Set fprefix parameter
+...
+modparam("sipdump", "fprefix", "ksipdump-")
+...
+
+4. Functions
+
+   4.1. sipdump_send(tag)
+
+4.1. sipdump_send(tag)
+
+   Send the details of the current SIP message to the writter process and
+   get it stored in the file.
+
+   The parameter "tag" can be any string, it is going to be written in the
+   tag attribute inside the storage file.
+
+   This function can be used from ANY_ROUTE.
+
+   Example 1.6. sipdump_send usage
+...
+sipdump_send("cfg");
+...
+
+5. RPC Commands
+
+   5.1. sipdump.enable
+
+5.1. sipdump.enable
+
+   Control the value for "enable" parameter.
+
+   If no value is provided to this command, the value of "enable"
+   parameter is returned.
+
+   This function can be used from ANY_ROUTE.
+
+   Example 1.7. sipdump.enable usage
+...
+kamcmd sipdump.enable
+kamcmd sipdump.enable 1
+kamcmd sipdump.enable 0
+...

+ 4 - 0
src/modules/sipdump/doc/Makefile

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

+ 36 - 0
src/modules/sipdump/doc/sipdump.xml

@@ -0,0 +1,36 @@
+<?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 "../../../../doc/docbook/entities.xml">
+%docentities;
+
+]>
+
+<book>
+    <bookinfo>
+	<title>SIPDUMP Module</title>
+	<productname class="trade">kamailio.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>2017</year>
+	    <holder>asipto.com</holder>
+	</copyright>
+    </bookinfo>
+    <toc></toc>
+
+    <xi:include  xmlns:xi="http://www.w3.org/2001/XInclude" href="sipdump_admin.xml"/>
+
+</book>

+ 233 - 0
src/modules/sipdump/doc/sipdump_admin.xml

@@ -0,0 +1,233 @@
+<?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 "../../../../doc/docbook/entities.xml">
+%docentities;
+
+]>
+<!-- Module User's Guide -->
+
+<chapter>
+
+	<title>&adminguide;</title>
+
+	<section>
+	<title>Overview</title>
+	<para>
+		This module writes SIP traffic and some associated details into local
+		files. It intercepts automatically all the SIP traffic received
+		or sent by &kamailio; and provides a function to trigger storage from
+		configuration file.
+	</para>
+	<para>
+		Received traffic has the tag 'rcv' and the one to be sent has the tag
+		'snd'. The tag value is provided as parameter for the config function.
+	</para>
+	<para>
+		Besides the SIP packets, the module aims to save details related to
+		&kamailio; runtime environment that are useful for troubleshooting, like
+		process id, child rank, a.s.o.
+	</para>
+	<para>
+		The module should be useful for troubleshooting during development or
+		testing of new deployments, especially when dealing with traffic over
+		TLS with forward privacy, when other tools such as wireshark cannot
+		decrypt. For production environments with a lot of SIP traffic, look
+		at siptrace and sipcapture modules for a scalable alternative to
+		capture all the SIP traffic and then search using Homer Sipcapture
+		web toolkit.
+	</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 id="sipdump.p.enable">
+		<title><varname>enable</varname> (int)</title>
+		<para>
+			Enable storage.
+		</para>
+		<para>
+		<emphasis>
+			Default value is 1 (0 - off; 1 - on).
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>enable</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("sipdump", "enable", 0)
+...
+</programlisting>
+		</example>
+	</section>
+	<section id="sipdump.p.wait">
+		<title><varname>wait</varname> (int)</title>
+		<para>
+			Wait time (microseconds) when no SIP traffic is received.
+		</para>
+		<para>
+		<emphasis>
+			Default value is 100.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>wait</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("sipdump", "wait", 2000)
+...
+</programlisting>
+		</example>
+	</section>
+	<section id="sipdump.p.rotate">
+		<title><varname>rotate</varname> (int)</title>
+		<para>
+			Time interval in seconds to rotate files.
+		</para>
+		<para>
+		<emphasis>
+			Default value is 7200 (2 hours).
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>rotate</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("sipdump", "rotate", 3600)
+...
+</programlisting>
+		</example>
+	</section>
+	<section id="sipdump.p.folder">
+		<title><varname>folder</varname> (str)</title>
+		<para>
+			Path to the folder where to save the files.
+		</para>
+		<para>
+		<emphasis>
+			Default value is "/tmp".
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>folder</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("sipdump", "folder", "/var/run/kamailio")
+...
+</programlisting>
+		</example>
+	</section>
+	<section id="sipdump.p.fprefix">
+		<title><varname>fprefix</varname> (str)</title>
+		<para>
+			File name prefix. The date is appended to this prefix in the format
+			yyyy-mm-dd--hh-mm-ss. The extension of the file is ".data".
+		</para>
+		<para>
+		<emphasis>
+			Default value is "kamailio-sipdump-".
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>fprefix</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("sipdump", "fprefix", "ksipdump-")
+...
+</programlisting>
+		</example>
+	</section>
+
+	</section>
+
+	<section>
+	<title>Functions</title>
+	<section id="sipdump.f.sipdump_send">
+	    <title>
+		<function moreinfo="none">sipdump_send(tag)</function>
+	    </title>
+	    <para>
+		Send the details of the current SIP message to the writter process and
+		get it stored in the file.
+		</para>
+		<para>
+		The parameter "tag" can be any string, it is going to be written in
+		the tag attribute inside the storage file.
+		</para>
+		<para>
+		This function can be used from ANY_ROUTE.
+		</para>
+		<example>
+		<title><function>sipdump_send</function> usage</title>
+		<programlisting format="linespecific">
+...
+sipdump_send("cfg");
+...
+</programlisting>
+	    </example>
+	</section>
+	</section>
+
+	<section>
+	<title>RPC Commands</title>
+	<section id="sipdump.rpc.enable">
+	    <title>
+		<function moreinfo="none">sipdump.enable</function>
+	    </title>
+	    <para>
+		Control the value for "enable" parameter.
+		</para>
+		<para>
+		If no value is provided to this command, the value of "enable"
+		parameter is returned.
+		</para>
+		<para>
+		This function can be used from ANY_ROUTE.
+		</para>
+		<example>
+		<title><function>sipdump.enable</function> usage</title>
+		<programlisting format="linespecific">
+...
+&kamcmd; sipdump.enable
+&kamcmd; sipdump.enable 1
+&kamcmd; sipdump.enable 0
+...
+</programlisting>
+	    </example>
+	</section>
+	</section>
+
+</chapter>
+

+ 315 - 0
src/modules/sipdump/sipdump_mod.c

@@ -0,0 +1,315 @@
+/**
+ * Copyright (C) 2017 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include "../../core/sr_module.h"
+#include "../../core/dprint.h"
+#include "../../core/ut.h"
+#include "../../core/pvar.h"
+#include "../../core/pt.h"
+#include "../../core/timer_proc.h"
+#include "../../core/mod_fix.h"
+#include "../../core/events.h"
+#include "../../core/kemi.h"
+
+#include "sipdump_write.h"
+
+MODULE_VERSION
+
+static int sipdump_enable = 1;
+int sipdump_rotate = 7200;
+static int sipdump_wait = 100;
+static str sipdump_folder = str_init("/tmp");
+static str sipdump_fprefix = str_init("kamailio-sipdump-");
+
+static int mod_init(void);
+static int child_init(int);
+static void mod_destroy(void);
+
+static int w_sipdump_send(sip_msg_t *msg, char *ptag, char *str2);
+
+int sipdump_msg_received(sr_event_param_t *evp);
+int sipdump_msg_sent(sr_event_param_t *evp);
+
+/* clang-format off */
+static cmd_export_t cmds[]={
+	{"sipdump_send", (cmd_function)w_sipdump_send, 1, fixup_spve_null,
+		0, ANY_ROUTE},
+	{0, 0, 0, 0, 0, 0}
+};
+
+static param_export_t params[]={
+	{"enable",   PARAM_INT,   &sipdump_enable},
+	{"wait",     PARAM_INT,   &sipdump_wait},
+	{"rotate",   PARAM_INT,   &sipdump_rotate},
+	{"folder",   PARAM_STR,   &sipdump_folder},
+	{"fprefix",  PARAM_STR,   &sipdump_fprefix},
+	{0, 0, 0}
+};
+
+struct module_exports exports = {
+	"sipdump",
+	DEFAULT_DLFLAGS, /* dlopen flags */
+	cmds,
+	params,
+	0,
+	0,              /* exported MI functions */
+	0,              /* exported pseudo-variables */
+	0,              /* extra processes */
+	mod_init,       /* module initialization function */
+	0,              /* response function */
+	mod_destroy,    /* destroy function */
+	child_init      /* per child init function */
+};
+/* clang-format on */
+
+
+/**
+ * init module function
+ */
+static int mod_init(void)
+{
+	if(sipdump_file_init(&sipdump_folder, &sipdump_fprefix) < 0) {
+		LM_ERR("cannot initialize storage file\n");
+		return -1;
+	}
+
+	if(sipdump_list_init(sipdump_enable) < 0) {
+		LM_ERR("cannot initialize internal structure\n");
+		return -1;
+	}
+
+	register_basic_timers(1);
+	sr_event_register_cb(SREV_NET_DATA_IN, sipdump_msg_received);
+	sr_event_register_cb(SREV_NET_DATA_OUT, sipdump_msg_sent);
+
+	return 0;
+}
+
+/**
+ * @brief Initialize async module children
+ */
+static int child_init(int rank)
+{
+	int i;
+
+	if(rank != PROC_MAIN)
+		return 0;
+
+	if(fork_basic_utimer(PROC_TIMER, "SIPDUMP WRITE TIMER", 1 /*socks flag*/,
+			   sipdump_timer_exec, NULL, sipdump_wait /*usec*/)
+				< 0) {
+		LM_ERR("failed to register timer routine as process (%d)\n", i);
+		return -1; /* error */
+	}
+
+	return 0;
+}
+
+/**
+ * destroy module function
+ */
+static void mod_destroy(void)
+{
+	sipdump_list_destroy();
+}
+
+#define SIPDUMP_WBUF_SIZE 65536
+static char _sipdump_wbuf[SIPDUMP_WBUF_SIZE];
+
+typedef struct sipdump_info {
+	str tag;
+	str buf;
+	int af;
+	int proto;
+	str src_ip;
+	int src_port;
+	str dst_ip;
+	int dst_port;
+} sipdump_info_t;
+
+/**
+ *
+ */
+int sipdump_buffer_write(sipdump_info_t *sdi, str *obuf)
+{
+	struct timeval tv;
+	struct tm *ti;
+
+	gettimeofday(&tv, NULL);
+	ti = localtime(&tv.tv_sec);
+	obuf->len = snprintf(_sipdump_wbuf, SIPDUMP_WBUF_SIZE,
+		"====================\n"
+		"v: 1.0\n"
+		"tag: %.*s\n"
+		"pid: %d\n"
+		"process: %d\n"
+		"time: %lu.%06lu\n"
+		"date: %s"
+		"~~~~~~~~~~~~~~~~~~~~\n"
+		"%.*s"
+		"||||||||||||||||||||\n",
+		sdi->tag.len, sdi->tag.s,
+		my_pid(),
+		process_no,
+		(unsigned long)tv.tv_sec, (unsigned long)tv.tv_usec,
+		asctime(ti),
+		sdi->buf.len, sdi->buf.s
+	);
+	obuf->s = _sipdump_wbuf;
+
+	return 0;
+}
+
+/**
+ *
+ */
+int ki_sipdump_send(sip_msg_t *msg, str *stag)
+{
+	str wdata;
+	sipdump_info_t sdi;
+
+	if(!sipdump_enabled())
+		return 1;
+	
+	memset(&sdi, 0, sizeof(sipdump_info_t));
+
+	sdi.buf.s = msg->buf;
+	sdi.buf.len = msg->len;
+	sdi.tag = *stag;
+
+	if(sipdump_buffer_write(&sdi, &wdata)<0) {
+		LM_ERR("failed to write to buffer\n");
+		return -1;
+	}
+
+	if(sipdump_list_add(&wdata)<0) {
+		LM_ERR("failed to add data to write list\n");
+		return -1;
+	}
+	return 1;
+}
+
+/**
+ *
+ */
+static int w_sipdump_send(sip_msg_t *msg, char *ptag, char *str2)
+{
+	str stag;
+
+	if(!sipdump_enabled())
+		return 1;
+
+	if(fixup_get_svalue(msg, (gparam_t*)ptag, &stag)<0) {
+		LM_ERR("failed to get tag parameter\n");
+		return -1;
+	}
+	return ki_sipdump_send(msg, &stag);
+}
+
+/**
+ * 
+ */
+int sipdump_msg_received(sr_event_param_t *evp)
+{
+	str wdata;
+	sipdump_info_t sdi;
+
+	if(!sipdump_enabled())
+		return 0;
+
+	memset(&sdi, 0, sizeof(sipdump_info_t));
+
+	sdi.buf = *((str*)evp->data);
+	sdi.tag.s = "rcv";
+	sdi.tag.len = 3;
+
+	if(sipdump_buffer_write(&sdi, &wdata)<0) {
+		LM_ERR("failed to write to buffer\n");
+		return -1;
+	}
+
+	if(sipdump_list_add(&wdata)<0) {
+		LM_ERR("failed to add data to write list\n");
+		return -1;
+	}
+	return 0;
+}
+
+/**
+ * 
+ */
+int sipdump_msg_sent(sr_event_param_t *evp)
+{
+	str wdata;
+	sipdump_info_t sdi;
+
+	if(!sipdump_enabled())
+		return 0;
+
+	memset(&sdi, 0, sizeof(sipdump_info_t));
+
+	sdi.buf = *((str*)evp->data);
+	sdi.tag.s = "snd";
+	sdi.tag.len = 3;
+
+	if(sipdump_buffer_write(&sdi, &wdata)<0) {
+		LM_ERR("failed to write to buffer\n");
+		return -1;
+	}
+
+	if(sipdump_list_add(&wdata)<0) {
+		LM_ERR("failed to add data to write list\n");
+		return -1;
+	}
+	return 0;
+}
+
+/**
+ *
+ */
+/* clang-format off */
+static sr_kemi_t sr_kemi_sipdump_exports[] = {
+	{ str_init("sipdump"), str_init("send"),
+		SR_KEMIP_INT, ki_sipdump_send,
+		{ SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
+			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
+	},
+
+	{ {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } }
+};
+/* clang-format on */
+
+/**
+ *
+ */
+int mod_register(char *path, int *dlflags, void *p1, void *p2)
+{
+	sr_kemi_modules_add(sr_kemi_sipdump_exports);
+	return 0;
+}

+ 228 - 0
src/modules/sipdump/sipdump_write.c

@@ -0,0 +1,228 @@
+/**
+ * Copyright (C) 2017 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../core/dprint.h"
+#include "../../core/ut.h"
+
+#include "sipdump_write.h"
+
+extern int sipdump_rotate;
+
+static time_t sipdump_last_rotate = 0;
+
+static sipdump_list_t *_sipdump_list = NULL;
+
+#define SIPDUMP_FPATH_SIZE 256
+static char _sipdump_fpath[SIPDUMP_FPATH_SIZE];
+static str _sipdump_fpath_prefix = {0, 0};
+
+static FILE *_sipdump_file = NULL;
+
+/**
+ *
+ */
+int sipdump_list_init(int en)
+{
+	if(_sipdump_list!=NULL)
+		return 0;
+	_sipdump_list = (sipdump_list_t*)shm_malloc(sizeof(sipdump_list_t));
+	if(_sipdump_list==NULL) {
+		LM_ERR("not enough shared memory\n");
+		return -1;
+	}
+	memset(_sipdump_list, 0, sizeof(sipdump_list_t));
+	if(lock_init(&_sipdump_list->lock)==NULL) {
+		shm_free(_sipdump_list);
+		LM_ERR("failed to init lock\n");
+		return -1;
+	}
+	_sipdump_list->enable = en;
+	return 0;
+}
+
+/**
+ * 
+ */
+int sipdump_enabled(void)
+{
+	if(_sipdump_list==NULL)
+		return 0;
+	if(_sipdump_list->enable==0)
+		return 0;
+	return 1;
+}
+/**
+ *
+ */
+int sipdump_list_destroy(void)
+{
+	sipdump_data_t *sdd = NULL;
+	sipdump_data_t *sdd0 = NULL;
+	if(_sipdump_list==NULL)
+		return 0;
+	
+	sdd=_sipdump_list->first;
+	while(sdd!=NULL) {
+		sdd0 = sdd;
+		sdd=sdd->next;
+		shm_free(sdd0);
+	}
+	return 0;
+}
+
+/**
+ *
+ */
+int sipdump_list_add(str *data)
+{
+	sipdump_data_t *sdd = NULL;
+
+	sdd = (sipdump_data_t*)shm_malloc(sizeof(sipdump_data_t)
+				+ (data->len+1)*sizeof(char));
+	if(sdd==NULL) {
+		LM_ERR("no more shared memory\n");
+		return -1;
+	}
+	memset(sdd, 0, sizeof(sipdump_data_t));
+	sdd->data.s = (char*)sdd + sizeof(sipdump_data_t);
+	sdd->data.len = data->len;
+	memcpy(sdd->data.s, data->s, data->len);
+	sdd->data.s[data->len] = '\0';
+	lock_get(&_sipdump_list->lock);
+	if(_sipdump_list->last) {
+		_sipdump_list->last->next = sdd;
+	} else {
+		_sipdump_list->first = sdd;
+	}
+	_sipdump_list->last = sdd;
+	lock_release(&_sipdump_list->lock);
+	return 0;
+}
+
+/**
+ * 
+ */
+static int sipdump_rotate_file(void)
+{
+	time_t tv;
+	struct tm *ti = NULL;
+	int n;
+
+	tv = time(NULL);
+
+	if(_sipdump_file!=NULL
+			&& sipdump_last_rotate>0
+			&& sipdump_last_rotate+sipdump_rotate>tv) {
+		/* not yet the time for rotation */
+		return 0;
+	}
+
+	if(_sipdump_file != NULL) {
+		fclose(_sipdump_file);
+	}
+	ti = localtime(&tv);
+	n = snprintf(_sipdump_fpath+_sipdump_fpath_prefix.len,
+			SIPDUMP_FPATH_SIZE-_sipdump_fpath_prefix.len,
+			"%d-%02d-%02d--%02d-%02d-%02d.data",
+			1900+ti->tm_year, ti->tm_mon, ti->tm_mday,
+			ti->tm_hour, ti->tm_min, ti->tm_sec);
+	_sipdump_file = fopen( _sipdump_fpath , "w" );
+	if(_sipdump_file==NULL) {
+		LM_ERR("failed to open file %s\n", _sipdump_fpath);
+		return -1;
+	}
+	sipdump_last_rotate = tv;
+	
+	return 0;
+}
+
+/**
+ * 
+ */
+int sipdump_file_init(str *folder, str *fprefix)
+{
+	_sipdump_fpath_prefix.len = snprintf(_sipdump_fpath, SIPDUMP_FPATH_SIZE-64,
+			"%.*s/%.*s",
+			folder->len, folder->s,
+			fprefix->len, fprefix->s);
+	if(_sipdump_fpath_prefix.len<0
+			|| _sipdump_fpath_prefix.len>=SIPDUMP_FPATH_SIZE-64) {
+		LM_ERR("sipdump file path failed or is too long\n");
+		return -1;
+	}
+	_sipdump_fpath_prefix.s = _sipdump_fpath;
+	return 0;
+}
+
+/**
+ *
+ */
+void sipdump_timer_exec(unsigned int ticks, void *param)
+{
+	sipdump_data_t *sdd = NULL;
+	int cnt = 0;
+
+	if(_sipdump_list==NULL || _sipdump_list->first==NULL)
+		return;
+
+	if(sipdump_rotate_file()<0) {
+		LM_ERR("sipdump rotate file failed\n");
+		return;
+	}
+
+	while(1) {
+		lock_get(&_sipdump_list->lock);
+		if(_sipdump_list->first==NULL) {
+			lock_release(&_sipdump_list->lock);
+			if(_sipdump_file) fflush(_sipdump_file);
+			return;
+		}
+		sdd = _sipdump_list->first;
+		_sipdump_list->first = sdd->next;
+		if(sdd->next==NULL) {
+			_sipdump_list->last = NULL;
+		}
+		_sipdump_list->count--;
+		lock_release(&_sipdump_list->lock);
+		cnt++;
+		if(cnt>2000) {
+			if(sipdump_rotate_file()<0) {
+				LM_ERR("sipdump rotate file failed\n");
+				return;
+			}
+			cnt=0;
+		}
+		if(_sipdump_file==NULL) {
+			LM_ERR("sipdump file is not open\n");
+			return;
+		}
+		/* LM_NOTICE("writing: [[%.*s]] (%d)\n",
+			sdd->data.len, sdd->data.s, sdd->data.len); */
+		fwrite(sdd->data.s, 1, sdd->data.len, _sipdump_file);
+		shm_free(sdd);
+	}
+}

+ 54 - 0
src/modules/sipdump/sipdump_write.h

@@ -0,0 +1,54 @@
+/**
+ * Copyright (C) 2017 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef _SIPDUMP_WRITE_H_
+#define _SIPDUMP_WRITE_H_
+
+#include "../../core/str.h"
+#include "../../core/locking.h"
+
+typedef struct sipdump_data {
+	str data;
+	struct sipdump_data *next;
+} sipdump_data_t;
+
+typedef struct sipdump_list {
+	int count;
+	int enable;
+	gen_lock_t lock;
+	struct sipdump_data *first;
+	struct sipdump_data *last;
+} sipdump_list_t;
+
+int sipdump_list_init(int en);
+
+int sipdump_list_destroy(void);
+
+int sipdump_list_add(str *data);
+
+void sipdump_timer_exec(unsigned int ticks, void *param);
+
+int sipdump_file_init(str *folder, str *fprefix);
+
+int sipdump_enabled(void);
+
+#endif