Przeglądaj źródła

Merge branch 'master' of ssh://git.sip-router.org/sip-router

Jason Penton 11 lat temu
rodzic
commit
11ade9c99d

+ 12 - 11
Makefile

@@ -755,7 +755,7 @@ $(man_prefix)/$(man_dir)/man5:
 
 # note: sed with POSIX.1 regex doesn't support |, + or ? (darwin, solaris ...) 
 install-cfg: $(cfg_prefix)/$(cfg_dir)
-		@if [ -f etc/$(CFG_NAME).cfg ]; then \
+	@if [ -f etc/$(CFG_NAME).cfg ]; then \
 			sed $(foreach m,$(modules_dirs),\
 					-e "s#/usr/[^:]*lib/$(CFG_NAME)/$(m)\([:/\"]\)#$($(m)_target)\1#g") \
 					-e "s#/usr/local/etc/$(CFG_NAME)/#$(cfg_target)#g" \
@@ -768,7 +768,7 @@ install-cfg: $(cfg_prefix)/$(cfg_dir)
 					$(cfg_prefix)/$(cfg_dir)$(MAIN_NAME).cfg; \
 			fi; \
 		fi
-		@if [ -f etc/$(CFG_NAME)-basic.cfg ]; then \
+	@if [ -f etc/$(CFG_NAME)-basic.cfg ]; then \
 			sed $(foreach m,$(modules_dirs),\
 					-e "s#/usr/[^:]*lib/$(CFG_NAME)/$(m)\([:/\"]\)#$($(m)_target)\1#g") \
 					-e "s#/usr/local/etc/$(CFG_NAME)/#$(cfg_target)#g" \
@@ -781,7 +781,7 @@ install-cfg: $(cfg_prefix)/$(cfg_dir)
 					$(cfg_prefix)/$(cfg_dir)$(MAIN_NAME)-basic.cfg; \
 			fi; \
 		fi
-		@if [ -f etc/$(CFG_NAME)-oob.cfg ]; then \
+	@if [ -f etc/$(CFG_NAME)-oob.cfg ]; then \
 			sed $(foreach m,$(modules_dirs),\
 					-e "s#/usr/[^:]*lib/$(CFG_NAME)/$(m)\([:/\"]\)#$($(m)_target)\1#g") \
 					-e "s#/usr/local/etc/$(CFG_NAME)/#$(cfg_target)#g" \
@@ -795,8 +795,8 @@ install-cfg: $(cfg_prefix)/$(cfg_dir)
 					$(cfg_prefix)/$(cfg_dir)$(MAIN_NAME)-advanced.cfg; \
 			fi; \
 		fi
-		@# other configs
-		@for r in $(C_INSTALL_CFGS) ""; do \
+	@# other configs
+	@for r in $(C_INSTALL_CFGS) ""; do \
 			if [ -n "$$r" ]; then \
 				if [ -f "$$r" ]; then \
 					n=`basename "$$r"` ; \
@@ -818,14 +818,15 @@ install-cfg: $(cfg_prefix)/$(cfg_dir)
 					fi ; \
 				fi ; \
 			fi ; \
-		done; true
-		# radius dictionary
-		$(INSTALL_TOUCH) $(cfg_prefix)/$(cfg_dir)/dictionary.$(CFG_NAME)
-		$(INSTALL_CFG) etc/dictionary.$(CFG_NAME) $(cfg_prefix)/$(cfg_dir)
+			: ; done; true
+	@# radius dictionary
+	@$(INSTALL_TOUCH) $(cfg_prefix)/$(cfg_dir)/dictionary.$(CFG_NAME)
+	@$(INSTALL_CFG) etc/dictionary.$(CFG_NAME) $(cfg_prefix)/$(cfg_dir)
+	@echo "config files installed"
 
 install-bin: $(bin_prefix)/$(bin_dir) $(NAME)
-		$(INSTALL_TOUCH) $(bin_prefix)/$(bin_dir)/$(NAME)
-		$(INSTALL_BIN) $(NAME) $(bin_prefix)/$(bin_dir)
+	$(INSTALL_TOUCH) $(bin_prefix)/$(bin_dir)/$(NAME)
+	$(INSTALL_BIN) $(NAME) $(bin_prefix)/$(bin_dir)
 
 
 install-share: $(share_prefix)/$(share_dir)

+ 1 - 1
Makefile.groups

@@ -18,7 +18,7 @@ mod_list_basic=async auth benchmark blst cfg_rpc cfgutils corex counters \
 # - extra used modules, with no extra dependency
 mod_list_extra=avp auth_diameter call_control cnxcc dmq domainpolicy msrp pdb \
 			     qos sca seas sms sst timer tmrec uac_redirect xhttp \
-				 xhttp_rpc xprint
+				 xhttp_rpc xprint jsonrpc-s nosip
 
 # - common modules depending on database
 mod_list_db=acc alias_db auth_db avpops cfg_db db_text db_flatstore \

+ 1 - 1
lib/kcore/Makefile

@@ -7,6 +7,6 @@ BUGFIX_VER=0
 LIBS=
 
 SERLIBPATH=..
-SER_LIBS=$(SERLIBPATH)/srutils/srutils
+SER_LIBS=
 
 include ../../Makefile.libs

+ 0 - 38
lib/kcore/strcommon.c

@@ -110,44 +110,6 @@ int unescape_common(char *dst, char *src, int src_len)
 	return j;
 }
 
-/*! \brief Compute MD5 checksum */
-void compute_md5(char *dst, char *src, int src_len)
-{
-	MD5_CTX context;
-	unsigned char digest[16];
-	MD5Init (&context);
-  	MD5Update (&context, src, src_len);
-	U_MD5Final (digest, &context);
-	string2hex(digest, 16, dst);
-}
-
-/*! \brief Compute SHA256 checksum */
-void compute_sha256(char *dst, u_int8_t *src, int src_len)
-{
-	SHA256_CTX ctx256;
-	SHA256_Init(&ctx256);
-	SHA256_Update(&ctx256, src, src_len);
-	SHA256_End(&ctx256, dst);
-}
-
-/*! \brief Compute SHA384 checksum */
-void compute_sha384(char *dst, u_int8_t *src, int src_len)
-{
-	SHA384_CTX ctx384;
-	SHA384_Init(&ctx384);
-	SHA384_Update(&ctx384, src, src_len);
-	SHA384_End(&ctx384, dst);
-}
-
-/*! \brief Compute SHA512 checksum */
-void compute_sha512(char *dst, u_int8_t *src, int src_len)
-{
-	SHA512_CTX ctx512;
-	SHA512_Init(&ctx512);
-	SHA512_Update(&ctx512, src, src_len);
-	SHA512_End(&ctx512, dst);
-}
-
 /*! \brief Unscape all printable ASCII characters */
 int unescape_user(str *sin, str *sout)
 {

+ 0 - 10
lib/kcore/strcommon.h

@@ -29,8 +29,6 @@
 #define _STRCOMMON_H_
 
 #include "../../str.h"
-#include "../../md5.h"
-#include "../srutils/sha256.h"
 
 /*
  * add backslashes to special characters
@@ -41,14 +39,6 @@ int escape_common(char *dst, char *src, int src_len);
  */
 int unescape_common(char *dst, char *src, int src_len);
 
-void compute_md5(char *dst, char *src, int src_len);
-
-void compute_sha256(char *dst, u_int8_t *src, int src_len);
-
-void compute_sha384(char *dst, u_int8_t *src, int src_len);
-
-void compute_sha512(char *dst, u_int8_t *src, int src_len);
-
 int escape_user(str *sin, str *sout);
 
 int unescape_user(str *sin, str *sout);

+ 63 - 0
lib/srutils/shautils.c

@@ -0,0 +1,63 @@
+/*
+ * sha and other hashing utilities
+ *
+ * Copyright (C) 2014 1&1 Germany
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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 "../../md5.h"
+#include "../../ut.h"
+#include "shautils.h"
+
+/*! \brief Compute MD5 checksum */
+void compute_md5(char *dst, char *src, int src_len)
+{
+	MD5_CTX context;
+	unsigned char digest[16];
+	MD5Init (&context);
+  	MD5Update (&context, src, src_len);
+	U_MD5Final (digest, &context);
+	string2hex(digest, 16, dst);
+}
+
+/*! \brief Compute SHA256 checksum */
+void compute_sha256(char *dst, u_int8_t *src, int src_len)
+{
+	SHA256_CTX ctx256;
+	SHA256_Init(&ctx256);
+	SHA256_Update(&ctx256, src, src_len);
+	SHA256_End(&ctx256, dst);
+}
+
+/*! \brief Compute SHA384 checksum */
+void compute_sha384(char *dst, u_int8_t *src, int src_len)
+{
+	SHA384_CTX ctx384;
+	SHA384_Init(&ctx384);
+	SHA384_Update(&ctx384, src, src_len);
+	SHA384_End(&ctx384, dst);
+}
+
+/*! \brief Compute SHA512 checksum */
+void compute_sha512(char *dst, u_int8_t *src, int src_len)
+{
+	SHA512_CTX ctx512;
+	SHA512_Init(&ctx512);
+	SHA512_Update(&ctx512, src, src_len);
+	SHA512_End(&ctx512, dst);
+}

+ 36 - 0
lib/srutils/shautils.h

@@ -0,0 +1,36 @@
+/*
+ * sha and other hashing utilities
+ *
+ * Copyright (C) 2014 1&1 Germany
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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 _SHAUTILS_H_
+#define _SHAUTILS_H_
+
+#include "sha256.h"
+
+void compute_md5(char *dst, char *src, int src_len);
+
+void compute_sha256(char *dst, u_int8_t *src, int src_len);
+
+void compute_sha384(char *dst, u_int8_t *src, int src_len);
+
+void compute_sha512(char *dst, u_int8_t *src, int src_len);
+
+#endif

+ 264 - 17
modules/corex/README

@@ -10,7 +10,13 @@ Daniel-Constantin Mierla
 
    <[email protected]>
 
-   Copyright © 2012 asipto.com
+Edited by
+
+Muhammad Shahzad Shafi
+
+   <[email protected]>
+
+   Copyright © 2012 asipto.com
      __________________________________________________________________
 
    Table of Contents
@@ -26,6 +32,9 @@ Daniel-Constantin Mierla
         3. Parameters
 
               3.1. alias_subdomains (string)
+              3.2. network_io_intercept (int)
+              3.3. msg_min_len (int)
+              3.4. msg_avp (string)
 
         4. Functions
 
@@ -33,6 +42,7 @@ Daniel-Constantin Mierla
               4.2. send([ host [ :port ] ])
               4.3. send_tcp([ host [ :port ] ])
               4.4. send_data(uri, data)
+              4.5. is_incoming()
 
         5. RPC Commands
 
@@ -41,12 +51,24 @@ Daniel-Constantin Mierla
               5.3. corex.shm_status
               5.4. corex.shm_summary
 
+        6. Event Routes
+
+              6.1. event_route[network:msg]
+
+        7. Examples of Usage
+
    List of Examples
 
    1.1. Set alias_subdomains parameter
-   1.2. append_branch usage
-   1.3. send usage
-   1.4. send_data usage
+   1.2. Set network_io_intercept parameter
+   1.3. Set msg_min_len parameter
+   1.4. Set msg_avp parameter
+   1.5. append_branch usage
+   1.6. send usage
+   1.7. send_data usage
+   1.8. is_incoming usage
+   1.9. event_route[network:msg] use cases
+   1.10. Sample PERL code for do_compress and do_uncompress
 
 Chapter 1. Admin Guide
 
@@ -61,6 +83,9 @@ Chapter 1. Admin Guide
    3. Parameters
 
         3.1. alias_subdomains (string)
+        3.2. network_io_intercept (int)
+        3.3. msg_min_len (int)
+        3.4. msg_avp (string)
 
    4. Functions
 
@@ -68,6 +93,7 @@ Chapter 1. Admin Guide
         4.2. send([ host [ :port ] ])
         4.3. send_tcp([ host [ :port ] ])
         4.4. send_data(uri, data)
+        4.5. is_incoming()
 
    5. RPC Commands
 
@@ -76,6 +102,12 @@ Chapter 1. Admin Guide
         5.3. corex.shm_status
         5.4. corex.shm_summary
 
+   6. Event Routes
+
+        6.1. event_route[network:msg]
+
+   7. Examples of Usage
+
 1. Overview
 
    This module provides reimplementation of a few very old functions that
@@ -89,6 +121,15 @@ Chapter 1. Admin Guide
    Contributions to this module must be done under the BSD license, to
    follow the requirements of the core contributions.
 
+   This module now also provides access to network input / output data
+   through event_route[network:msg]. The raw data received from a remote
+   host or about to be sent to a remote host is available in variable $mb.
+   The script writer may manipulate this data and save the final result in
+   an AVP defined by msg_avp module parameter. The content of this AVP
+   will then be processed by SIP worker as normal, i.e. a received message
+   will be parsed and sent to appropriate route block while a sent message
+   is forwarded to remote host.
+
 2. Dependencies
 
    2.1. Kamailio Modules
@@ -108,15 +149,18 @@ Chapter 1. Admin Guide
 3. Parameters
 
    3.1. alias_subdomains (string)
+   3.2. network_io_intercept (int)
+   3.3. msg_min_len (int)
+   3.4. msg_avp (string)
 
 3.1. alias_subdomains (string)
 
-   Register a domain and all its sub-domains to match the "myself"
+   Register a domain and all its sub-domains to match the “myself�
    condition. It can be set many times. Its full format is:
    'proto:domain:port', allowing to set restrictions on protocol and port
    as well. Protocol and port are optional.
 
-   Default value is "NULL".
+   Default value is “NULL�.
 
    Example 1.1. Set alias_subdomains parameter
 ...
@@ -124,14 +168,56 @@ modparam("corex", "alias_subdomains", "kamailio.org")
 modparam("corex", "alias_subdomains", "udp:sip-router.org:5060")
 ...
 
+3.2. network_io_intercept (int)
+
+   If set to non-zero then raw data received from a remote host or about
+   to be sent to a remote host is made available in
+   event_route[network:msg]. The script writer may modify this and save to
+   msg_avp, which will then be processed by SIP worker as normal.
+
+   Default value is 0, i.e. do not allow access to network io data.
+
+   Example 1.2. Set network_io_intercept parameter
+...
+modparam("corex", "network_io_intercept", 1)
+...
+
+3.3. msg_min_len (int)
+
+   Minimum content length of the packet to execute the
+   event_route[network:msg]. This only works if network_io_intercept
+   parameter is set to non-zero.
+
+   Default value is 0.
+
+   Example 1.3. Set msg_min_len parameter
+...
+modparam("corex", "msg_min_len", 32)
+...
+
+3.4. msg_avp (string)
+
+   AVP name to store modified content to be set in the packet. If not set
+   in event_route[network:msg], then all changes are lost and original
+   contents are used. This only works if network_io_intercept parameter is
+   to set non-zero.
+
+   Default value is empty.
+
+   Example 1.4. Set msg_avp parameter
+...
+modparam("corex", "msg_avp", "$avp(msg)")
+...
+
 4. Functions
 
    4.1. append_branch([ uri, [ q ] ])
    4.2. send([ host [ :port ] ])
    4.3. send_tcp([ host [ :port ] ])
    4.4. send_data(uri, data)
+   4.5. is_incoming()
 
-4.1. append_branch([ uri, [ q ] ])
+4.1.  append_branch([ uri, [ q ] ])
 
    Append a new branch to the destination set, useful to build the set of
    destination addresses for parallel forking or redirect replies.
@@ -147,13 +233,13 @@ modparam("corex", "alias_subdomains", "udp:sip-router.org:5060")
 
    This function can be used from REQUEST_ROUTE or FAILURE_ROUTE.
 
-   Example 1.2. append_branch usage
+   Example 1.5. append_branch usage
 ...
     append_branch();
     append_branch("$avp(uri)", "0.5");
 ...
 
-4.2. send([ host [ :port ] ])
+4.2.  send([ host [ :port ] ])
 
    Send the original SIP message to a specific destination in stateless
    mode. No changes are applied to received message, no Via header is
@@ -167,7 +253,7 @@ modparam("corex", "alias_subdomains", "udp:sip-router.org:5060")
 
    This function can be used from REQUEST_ROUTE or FAILURE_ROUTE.
 
-   Example 1.3. send usage
+   Example 1.6. send usage
 ...
         send();
         send("10.20.15.10");
@@ -175,12 +261,12 @@ modparam("corex", "alias_subdomains", "udp:sip-router.org:5060")
         send("$var(res)");
 ...
 
-4.3. send_tcp([ host [ :port ] ])
+4.3.  send_tcp([ host [ :port ] ])
 
    This function is identical to send() described above, except that it
    sends the SIP message using the TCP protocol instead of UDP.
 
-4.4. send_data(uri, data)
+4.4.  send_data(uri, data)
 
    Send the data to address specified by uri. Both parameters can contain
    pseudo-variables. The uri parameter has to be a valid SIP URI. The data
@@ -188,11 +274,33 @@ modparam("corex", "alias_subdomains", "udp:sip-router.org:5060")
 
    This function can be used from ANY_ROUTE.
 
-   Example 1.4. send_data usage
+   Example 1.7. send_data usage
 ...
         send("sip:example.com:5070;transport=sctp", "Message at $Ts");
 ...
 
+4.5.  is_incoming()
+
+   Returns true if contents of message buffer $mb are the data received
+   from remote host, otherwise false indicating that the contents of $mb
+   are data that is about to be sent out to remote host. This only works
+   if network_io_intercept parameter is set to non-zero.
+
+   This function can be used from event_route[network:msg].
+
+   Example 1.8. is_incoming usage
+...
+event_route[network:msg] {
+    if (is_incoming()) {
+        xlog("L_INFO", "Received message '$mb' \n");
+        $avp(msg) = $mb;
+    } else {
+        xlog("L_INFO", "Sending message '$mb' \n");
+        $avp(msg) = $mb;
+    };
+}
+...
+
 5. RPC Commands
 
    5.1. corex.list_sockets
@@ -200,30 +308,169 @@ modparam("corex", "alias_subdomains", "udp:sip-router.org:5060")
    5.3. corex.shm_status
    5.4. corex.shm_summary
 
-5.1. corex.list_sockets
+5.1.  corex.list_sockets
 
    Print the list of sockets the application is listening on.
 
    Example:
                 kamcmd corex.list_sockets
 
-5.2. corex.list_aliases
+5.2.  corex.list_aliases
 
    Print the list of hostname aliases used to match the myself condition.
 
    Example:
                 kamcmd corex.list_aliases
 
-5.3. corex.shm_status
+5.3.  corex.shm_status
 
    Trigger shm status dump to syslog.
 
    Example:
                 kamcmd corex.shm_status
 
-5.4. corex.shm_summary
+5.4.  corex.shm_summary
 
    Trigger shm summary dump to syslog.
 
    Example:
                 kamcmd corex.shm_summary
+
+6. Event Routes
+
+   6.1. event_route[network:msg]
+
+6.1.  event_route[network:msg]
+
+   Event route block to be executed when new data is received from network
+   or the data that is about to be sent to a remote host by a SIP worker
+   process.
+
+   The kamailio script writer can check which type of data triggered this
+   event route using is_incoming method.
+
+   After executing of this event route, if msg_avp was defined and set
+   then its value is used for further processing, otherwise, original
+   value of $mb is used. If message was received from remote host, then it
+   is parsed and proceeds to appropriate route. Oterhwise if message set
+   to send out, then is sent to remote host per configured SIP timers in
+   config script.
+
+   Please note this event route is meant to prepare the message for
+   on-wire communication, e.g. to do custom encryption or decryption,
+   compression/decompression etc. of the message sent to or received from
+   remote host. Therefore, except text operations, no module fucntions or
+   pseudo variables are available in this event route.
+
+7. Examples of Usage
+
+   To use network event_route[network:msg] the remote SIP UA must also
+   implement and understand the encoding / decoding done in this event
+   route. It is up to Kamailio config script writer to define and
+   implement how encoding and decoding is done. Any language module such
+   as app_perl or app_lua can be called in in event_route[network:msg] to
+   implement desired logic.
+
+   The most simple use case is to compress the SIP packet on-wire. As SIP
+   is a text based protocol, so it is highly compressable. Using this
+   module, one can compress entire SIP message, including headers and
+   message body before sending it to remote host using any compression
+   algorithm of choice, thus saving significant bandwidth on mobile data
+   networks.
+
+   A useful case is to use this function between SIP edge proxy and SIP
+   application server. The SIP messages received from end-user at SIP edge
+   proxy may be decrypted and sent to SIP application server at remote
+   location unencrypted, where they are processed as normal. One the way
+   back, the messages received from SIP application server at edge proxy
+   can be encrpyted before being sent to actual destination. The edge
+   proxy can check whether received message came from end-user or SIP
+   application server by using simple regular expressions.
+
+   Another use case is to implement a virtual HTTP tunnel for SIP
+   messages. The SIP client app can convert SIP message to binary e.g. by
+   doing XOR, Base64 etc., then prepend some fake HTTP headers to make it
+   look like an HTTP request before sending it to kamailio over SIP TCP
+   socket. At kamailio, the fake headers are removed and data is decoded
+   back to normal SIP and processed per config script logic. For the data
+   that is to be sent to SIP client app, one can prepend fake HTTP reply
+   headers to encoded data before sending it to client app.
+
+   More advance use cases may involve custom encryption algorithms such as
+   ITV encryption algorithm, https://github.com/mshary/itv
+
+   For example, the client app running on Android or iPhone, may send
+   device UUID along with ITV key, encrypted using RSA or AES256 with
+   pre-shared secret, as first packet, which is set as cookie by server in
+   e.g. memcache. This cookie is referred by client app in each next
+   packet, so server can retrive encyption key from cache and use that for
+   decryption. Same can be done for server at client app side, so messages
+   encrypted by server can be decrypted at client app.
+
+   Next is a basic usage example where encoding and decoding is done using
+   PERL,
+
+   Example 1.9. event_route[network:msg] use cases
+...
+loadmodule "app_perl.so"
+loadmodule "corex.so"
+...
+# ----- app_perl params -----
+modparam("app_perl", "filename", "/usr/local/etc/kamailio/custom_compress.pl")
+modparam("app_perl", "modpath", "/usr/local/lib64/kamailio/perl")
+
+# ----- corex params -----
+modparam("corex", "network_io_intercept", 32)
+modparam("corex", "min_msg_len", 32)
+modparam("corex", "msg_avp", "$avp(msg)")
+...
+event_route[network:msg] {
+        if (is_incoming()) {
+                if (perl_exec_simple("do_uncompress", "" + $mb + "")) {
+                        xlog("L_INFO", "Received message '$avp(msg)' \n");
+                } else {
+                        xlog("L_INFO", "Received message '$mb' \n");
+                        $avp(msg) = $mb;
+                };
+        } else {
+                xlog("L_INFO", "Sending message '$mb' \n");
+                if (!perl_exec_simple("do_compress", "" + $mb + "")) {
+                        $avp(msg) = $mb;
+                };
+        };
+}
+...
+
+   Example 1.10. Sample PERL code for do_compress and do_uncompress
+...
+use strict;
+use warnings;
+use IO::Compress::Gzip qw(gzip $GzipError) ;
+use IO::Uncompress::Gunzip qw(gunzip $GunzipError) ;
+
+sub do_compress() {
+        my $input = shift;
+        my $output;
+
+        gzip \$input => \$output
+                or eval {
+                        Kamailio::log(L_WARN, "GZIP failed: $GzipError\n");
+                        $output = $input;
+                };
+
+        Kamailio::AVP::add("msg", $output);
+}
+
+sub do_uncompress() {
+        my $input = shift;
+        my $output;
+
+        gunzip \$input => \$output
+                or eval {
+                        Kamailio::log(L_WARN, "GUNZIP failed: $GzipError\n");
+                        $output = $input;
+                };
+
+        Kamailio::AVP::add("msg", $output);
+}
+...

+ 15 - 2
modules/corex/corex_mod.c

@@ -31,9 +31,11 @@
 #include "corex_lib.h"
 #include "corex_rpc.h"
 #include "corex_var.h"
+#include "corex_nio.h"
 
 MODULE_VERSION
 
+static int nio_intercept = 0;
 static int w_append_branch(sip_msg_t *msg, char *su, char *sq);
 static int w_send(sip_msg_t *msg, char *su, char *sq);
 static int w_send_tcp(sip_msg_t *msg, char *su, char *sq);
@@ -69,14 +71,19 @@ static cmd_export_t cmds[]={
 			0, REQUEST_ROUTE | FAILURE_ROUTE },
 	{"send_data", (cmd_function)w_send_data, 2, fixup_spve_spve,
 			0, ANY_ROUTE },
-
+	{"is_incoming",    (cmd_function)nio_check_incoming, 0, 0,
+    	    0, ANY_ROUTE },
 
 	{0, 0, 0, 0, 0, 0}
 };
 
 static param_export_t params[]={
-	{"alias_subdomains",  PARAM_STRING|USE_FUNC_PARAM,
+	{"alias_subdomains",		STR_PARAM|USE_FUNC_PARAM,
 								(void*)corex_alias_subdomains_param},
+    {"network_io_intercept",	INT_PARAM, &nio_intercept},
+    {"min_msg_len",				INT_PARAM, &nio_min_msg_len},
+    {"msg_avp",			  		PARAM_STR, &nio_msg_avp_param},
+
 	{0, 0, 0}
 };
 
@@ -114,6 +121,12 @@ static int mod_init(void)
 		return -1;
 	}
 
+	if((nio_intercept > 0) && (nio_intercept_init() < 0))
+	{
+		LM_ERR("failed to register network io intercept callback\n");
+		return -1;
+	}
+
 	return 0;
 }
 

+ 199 - 0
modules/corex/corex_nio.c

@@ -0,0 +1,199 @@
+/**
+ * $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 "corex_nio.h"
+
+/**
+ * init nio function
+ */
+int nio_intercept_init(void)
+{
+	int route_no;
+	pv_spec_t avp_spec;
+
+	route_no=route_get(&event_rt, "network:msg");
+
+	if (route_no==-1)
+	{
+		LM_ERR("failed to find event_route[network:msg]\n");
+		return -1;
+	}
+
+	if (event_rt.rlist[route_no]==0)
+	{
+		LM_ERR("event_route[network:msg] is empty\n");
+		return -1;
+	}
+
+	nio_route_no=route_no;
+	
+	if (nio_min_msg_len < 0)
+	{
+		LM_WARN("min_msg_len is less then zero, setting it to zero");
+		nio_min_msg_len = 0;
+	}
+
+	if (nio_msg_avp_param.s && nio_msg_avp_param.len > 0) 
+	{
+		if (pv_parse_spec(&nio_msg_avp_param, &avp_spec)==0
+				|| avp_spec.type!=PVT_AVP)
+		{
+			LM_ERR("malformed or non AVP %.*s AVP definition\n",
+					nio_msg_avp_param.len, nio_msg_avp_param.s);
+			return -1;
+		}
+
+		if(pv_get_avp_name(0, &(avp_spec.pvp), &nio_msg_avp_name,
+					&nio_msg_avp_type)!=0)
+		{
+			LM_ERR("[%.*s]- invalid AVP definition\n",
+					nio_msg_avp_param.len, nio_msg_avp_param.s);
+			return -1;
+		}
+	} else {
+		LM_WARN("no AVP defined to store modified message\n");
+	}
+
+    /* register network hooks */
+    sr_event_register_cb(SREV_NET_DATA_IN, nio_msg_received);
+    sr_event_register_cb(SREV_NET_DATA_OUT, nio_msg_sent);
+#ifdef USE_TCP
+    tcp_set_clone_rcvbuf(1);
+#endif
+    return 0;
+}
+
+/**
+ *
+ */
+int nio_msg_received(void *data)
+{
+    sip_msg_t msg;
+    str *obuf;
+    char *nbuf = NULL;
+    int_str avp_value;
+    struct usr_avp *avp;
+    struct run_act_ctx ra_ctx;
+
+    obuf = (str*)data;
+
+    if (obuf->len < nio_min_msg_len) {
+        return -1;
+    }
+
+    memset(&msg, 0, sizeof(sip_msg_t));
+    msg.buf = obuf->s;
+    msg.len = obuf->len;
+
+    nio_is_incoming = 1;
+    init_run_actions_ctx(&ra_ctx);
+    run_actions(&ra_ctx, event_rt.rlist[nio_route_no], &msg);
+
+    if(nio_msg_avp_name.n!=0) {
+        avp = NULL;
+        avp=search_first_avp(nio_msg_avp_type, nio_msg_avp_name,
+            &avp_value, 0);
+        if(avp!=NULL && is_avp_str_val(avp)) {
+            msg.buf = avp_value.s.s;
+            msg.len = avp_value.s.len;
+            nbuf = nio_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';
+        } else {
+            LM_WARN("no value set for AVP %.*s, using unmodified message\n",
+                nio_msg_avp_param.len, nio_msg_avp_param.s);
+        }
+    }
+
+    if(nbuf!=NULL)
+        pkg_free(nbuf);
+    free_sip_msg(&msg);
+    return 0;
+}
+
+/**
+ *
+ */
+int nio_msg_sent(void *data)
+{
+    sip_msg_t msg;
+    str *obuf;
+    int_str avp_value;
+    struct usr_avp *avp;
+    struct run_act_ctx ra_ctx;
+
+    obuf = (str*)data;
+
+    if (obuf->len < nio_min_msg_len) {
+        return -1;
+    }
+
+    memset(&msg, 0, sizeof(sip_msg_t));
+    msg.buf = obuf->s;
+    msg.len = obuf->len;
+
+    nio_is_incoming = 0;
+    init_run_actions_ctx(&ra_ctx);
+    run_actions(&ra_ctx, event_rt.rlist[nio_route_no], &msg);
+
+    if(nio_msg_avp_name.n!=0) {
+        avp = NULL;
+        avp=search_first_avp(nio_msg_avp_type, nio_msg_avp_name,
+                &avp_value, 0);
+        if(avp!=NULL && is_avp_str_val(avp)) {
+            msg.buf = avp_value.s.s;
+            msg.len = avp_value.s.len;
+            obuf->s = nio_msg_update(&msg, (unsigned int*)&obuf->len);
+        } else {
+            LM_WARN("no value set for AVP %.*s, using unmodified message\n",
+                nio_msg_avp_param.len, nio_msg_avp_param.s);
+        }
+    }
+
+    free_sip_msg(&msg);
+    return 0;
+}
+
+/**
+ *
+ */
+int nio_check_incoming(void)
+{
+    return (nio_is_incoming) ? 1 : -1;
+}
+
+/**
+ *
+ */
+char* nio_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);
+}
+

+ 53 - 0
modules/corex/corex_nio.h

@@ -0,0 +1,53 @@
+/**
+ * $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 _COREX_NIO_H_
+#define _COREX_NIO_H_
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "../../sr_module.h"
+#include "../../dprint.h"
+#include "../../events.h"
+#include "../../ut.h"
+
+#include "../../tcp_options.h"
+#include "../../msg_translator.h"
+
+int nio_route_no;
+int nio_min_msg_len;
+int nio_is_incoming;
+
+str nio_msg_avp_param;
+int_str nio_msg_avp_name;
+unsigned short nio_msg_avp_type;
+
+int nio_msg_received(void *data);
+int nio_msg_sent(void *data);
+
+int nio_check_incoming(void);
+char* nio_msg_update(sip_msg_t *msg, unsigned int *olen);
+
+int nio_intercept_init(void);
+
+#endif

+ 2 - 2
modules/corex/corex_rpc.c

@@ -174,9 +174,9 @@ static void corex_rpc_shm_summary(rpc_t* rpc, void* ctx)
 
 rpc_export_t corex_rpc_cmds[] = {
 	{"corex.list_sockets", corex_rpc_list_sockets,
-		corex_rpc_list_sockets_doc, 0},
+		corex_rpc_list_sockets_doc, RET_ARRAY},
 	{"corex.list_aliases", corex_rpc_list_aliases,
-		corex_rpc_list_aliases_doc, 0},
+		corex_rpc_list_aliases_doc, RET_ARRAY},
 	{"corex.shm_status", corex_rpc_shm_status,
 		corex_rpc_shm_status_doc, 0},
 	{"corex.shm_summary", corex_rpc_shm_summary,

+ 5 - 0
modules/corex/doc/corex.xml

@@ -23,6 +23,11 @@
 		<surname>Mierla</surname>
 		<email>[email protected]</email>
 	    </editor>
+	    <editor>
+		<firstname>Muhammad Shahzad</firstname>
+		<surname>Shafi</surname>
+		<email>[email protected]</email>
+	    </editor>
 	</authorgroup>
 	<copyright>
 	    <year>2012</year>

+ 257 - 0
modules/corex/doc/corex_admin.xml

@@ -29,6 +29,15 @@
 		Contributions to this module must be done under the BSD license, to
 		follow the requirements of the core contributions.
 	</para>
+	<para>
+		This module now also provides access to network input / output data through
+		event_route[network:msg]. The raw data received from a remote host or about to
+		be sent to a remote host is available in variable $mb. The script writer may
+		manipulate this data and save the final result in an AVP defined by msg_avp
+		module parameter. The content of this AVP will then be processed by SIP worker
+		as normal, i.e. a received message will be parsed and sent to appropriate
+		route block while a sent message is forwarded to remote host.
+	</para>
 	</section>
 
 	<section>
@@ -87,6 +96,69 @@ modparam("corex", "alias_subdomains", "udp:sip-router.org:5060")
 </programlisting>
 	    </example>
 	</section>
+    <section>
+        <title><varname>network_io_intercept</varname> (int)</title>
+        <para>
+            If set to non-zero then raw data received from a remote host or about to
+			be sent to a remote host is made available in event_route[network:msg].
+			The script writer may modify this and save to msg_avp, which will then
+			be processed by SIP worker as normal.
+        </para>
+        <para>
+        <emphasis>
+            Default value is 0, i.e. do not allow access to network io data.
+        </emphasis>
+        </para>
+        <example>
+        <title>Set <varname>network_io_intercept</varname> parameter</title>
+        <programlisting format="linespecific">
+...
+modparam("corex", "network_io_intercept", 1)
+...
+</programlisting>
+        </example>
+    </section>
+    <section>
+        <title><varname>msg_min_len</varname> (int)</title>
+        <para>
+            Minimum content length of the packet to execute the event_route[network:msg].
+			This only works if network_io_intercept parameter is set to non-zero.
+        </para>
+        <para>
+        <emphasis>
+            Default value is 0.
+        </emphasis>
+        </para>
+        <example>
+        <title>Set <varname>msg_min_len</varname> parameter</title>
+        <programlisting format="linespecific">
+...
+modparam("corex", "msg_min_len", 32)
+...
+</programlisting>
+        </example>
+    </section>
+    <section>
+        <title><varname>msg_avp</varname> (string)</title>
+        <para>
+            AVP name to store modified content to be set in the packet. If not set in
+            event_route[network:msg], then all changes are lost and original contents
+            are used. This only works if network_io_intercept parameter is to set non-zero.
+        </para>
+        <para>
+        <emphasis>
+            Default value is empty.
+        </emphasis>
+        </para>
+        <example>
+        <title>Set <varname>msg_avp</varname> parameter</title>
+        <programlisting format="linespecific">
+...
+modparam("corex", "msg_avp", "$avp(msg)")
+...
+</programlisting>
+        </example>
+    </section>
 	</section>
 
 	<section>
@@ -200,6 +272,36 @@ modparam("corex", "alias_subdomains", "udp:sip-router.org:5060")
 </programlisting>
 		</example>
 	</section>
+    <section>
+		<title>
+			<function moreinfo="none">is_incoming()</function>
+		</title>
+        <para>
+            Returns true if contents of message buffer $mb are the data received from
+            remote host, otherwise false indicating that the contents of $mb are data
+            that is about to be sent out to remote host. This only works if 
+			network_io_intercept parameter is set to non-zero.
+        </para>
+        <para>
+        This function can be used from event_route[network:msg].
+        </para>
+        <example>
+        <title><function>is_incoming</function> usage</title>
+        <programlisting format="linespecific">
+...
+event_route[network:msg] {
+    if (is_incoming()) {
+        xlog("L_INFO", "Received message '$mb' \n");
+        $avp(msg) = $mb;
+    } else {
+        xlog("L_INFO", "Sending message '$mb' \n");
+        $avp(msg) = $mb;
+    };
+}
+...
+</programlisting>
+        </example>
+    </section>
 
 	</section>
 
@@ -263,5 +365,160 @@ modparam("corex", "alias_subdomains", "udp:sip-router.org:5060")
 		</programlisting>
     </section>
     </section>
+
+    <section>
+    <title>Event Routes</title>
+    <section id="async.evr.network_io">
+        <title>
+        <function moreinfo="none">event_route[network:msg]</function>
+        </title>
+        <para>
+            Event route block to be executed when new data is received from network
+            or the data that is about to be sent to a remote host by a SIP worker
+            process.
+        </para>
+        <para>
+            The kamailio script writer can check which type of data triggered this
+            event route using is_incoming method.
+        </para>
+        <para>
+            After executing of this event route, if msg_avp was defined and set then
+            its value is used for further processing, otherwise, original value of
+            $mb is used. If message was received from remote host, then it is parsed
+            and proceeds to appropriate route. Oterhwise if message set to send out,
+            then is sent to remote host per configured SIP timers in config script.
+        </para>
+        <para>
+            Please note this event route is meant to <emphasis>prepare</emphasis>
+            the message for on-wire communication, e.g. to do custom encryption or
+            decryption, compression/decompression etc. of the message sent to or 
+			received from remote host. Therefore, except text operations,
+			no module fucntions or pseudo variables are available in this event route.
+        </para>
+    </section>
+    </section>
+
+    <section>
+    <title>Examples of Usage</title>
+        <para>
+			To use network event_route[network:msg] the remote SIP UA must also implement and 
+			understand the encoding / decoding done in this event route. It is up to  &kamailio;
+			config script writer to define and implement how encoding and decoding is done. 
+			Any language module such as app_perl or app_lua can be called in
+			in event_route[network:msg] to implement desired logic.
+        </para>
+        <para>
+			The most simple use case is to compress the SIP packet on-wire. As SIP is a
+			text based protocol, so it is highly compressable. Using this module, one can
+			compress entire SIP message, including headers and message body before sending
+			it to remote host using any compression algorithm of choice, thus saving
+			significant bandwidth on mobile data networks.
+        </para>
+        <para>
+			A useful case is to use this function between SIP edge proxy and SIP application
+			server. The SIP messages received from end-user at SIP edge proxy may be decrypted
+			and sent to SIP application server at remote location unencrypted, where they are
+			processed as normal. One the way back, the messages received from SIP application
+			server at edge proxy can be encrpyted before being sent to actual destination.
+			The edge proxy can check whether received message came from end-user or SIP 
+			application server by using simple regular expressions.
+        </para>
+        <para>
+			Another use case is to implement a virtual HTTP tunnel for SIP messages. The SIP
+			client app can convert SIP message to binary e.g. by doing XOR, Base64 etc., then
+			prepend some fake HTTP headers to make it look like an HTTP request before sending
+			it to kamailio over SIP TCP socket. At kamailio, the fake headers are removed and data
+			is decoded back to normal SIP and processed per config script logic. For the data that
+			is to be sent to SIP client app, one can prepend fake HTTP reply headers to encoded
+			data before sending it to client app.
+        </para>
+        <para>
+			More advance use cases may involve custom encryption algorithms such as
+			ITV encryption algorithm,
+			<ulink url="https://github.com/mshary/itv"></ulink>
+        </para>
+        <para>
+			For example, the client app running on Android or iPhone, may send device UUID along
+			with ITV key, encrypted using RSA or AES256 with pre-shared secret, as first packet,
+			which is set as cookie by server in e.g. memcache. This cookie is referred by client
+			app in each next packet, so server can retrive encyption key from cache and use that
+			for decryption. Same can be done for server at client app side, so messages encrypted
+			by server can be decrypted at client app.
+        </para>
+        <para>
+			Next is a basic usage example where encoding and decoding is done using PERL,
+        </para>
+        <example>
+        <title><function>event_route[network:msg]</function> use cases</title>
+        <programlisting format="linespecific">
+...
+loadmodule "app_perl.so"
+loadmodule "corex.so"
+...
+# ----- app_perl params -----
+modparam("app_perl", "filename", "/usr/local/etc/kamailio/custom_compress.pl")
+modparam("app_perl", "modpath", "/usr/local/lib64/kamailio/perl")
+
+# ----- corex params -----
+modparam("corex", "network_io_intercept", 32)
+modparam("corex", "min_msg_len", 32)
+modparam("corex", "msg_avp", "$avp(msg)")
+...
+event_route[network:msg] {
+	if (is_incoming()) {
+		if (perl_exec_simple("do_uncompress", "" + $mb + "")) {
+			xlog("L_INFO", "Received message '$avp(msg)' \n");
+		} else {
+			xlog("L_INFO", "Received message '$mb' \n");
+			$avp(msg) = $mb;
+		};
+	} else {
+		xlog("L_INFO", "Sending message '$mb' \n");
+		if (!perl_exec_simple("do_compress", "" + $mb + "")) {
+			$avp(msg) = $mb;
+		};
+	};
+}
+...
+</programlisting>
+        </example>
+        <example>
+        <title>Sample PERL code for do_compress and do_uncompress</title>
+        <programlisting format="linespecific">
+...
+use strict;
+use warnings;
+use IO::Compress::Gzip qw(gzip $GzipError) ;
+use IO::Uncompress::Gunzip qw(gunzip $GunzipError) ;
+
+sub do_compress() {
+	my $input = shift;
+	my $output;
+
+	gzip \$input => \$output
+		or eval {
+			Kamailio::log(L_WARN, "GZIP failed: $GzipError\n");
+			$output = $input;
+		};
+
+	Kamailio::AVP::add("msg", $output);
+}
+
+sub do_uncompress() {
+	my $input = shift;
+	my $output;
+
+	gunzip \$input => \$output
+		or eval {
+			Kamailio::log(L_WARN, "GUNZIP failed: $GzipError\n");
+			$output = $input;
+		};
+
+	Kamailio::AVP::add("msg", $output);
+}
+...
+</programlisting>
+        </example>
+    </section>
 </chapter>
 

+ 1 - 1
modules/dialog/dlg_db_handler.c

@@ -360,7 +360,7 @@ static int load_dialog_info_from_db(int dlg_hash_size, int fetch_num_rows)
 			}
 
 			/*link the dialog*/
-			link_dlg(dlg, 0);
+			link_dlg(dlg, 0, 0);
 
 			dlg->h_id = VAL_INT(values+1);
 			next_id = d_table->entries[dlg->h_entry].next_id;

+ 29 - 24
modules/dialog/dlg_handlers.c

@@ -58,6 +58,7 @@
 #include "../../lib/kcore/statistics.h"
 #include "../../action.h"
 #include "../../script_cb.h"
+#include "../../pt.h"
 #include "../../lib/kcore/faked_msg.h"
 #include "../../parser/parse_from.h"
 #include "../../parser/parse_cseq.h"
@@ -775,6 +776,7 @@ int dlg_new_dialog(sip_msg_t *req, struct cell *t, const int run_initial_cbs)
     str ttag;
     str req_uri;
     unsigned int dir;
+    int mlock;
 
 	dlg = dlg_get_ctx_dialog();
     if(dlg != NULL) {
@@ -799,18 +801,20 @@ int dlg_new_dialog(sip_msg_t *req, struct cell *t, const int run_initial_cbs)
     }
     trim(&req_uri);
 
-    if (detect_spirals)
-    {
-        if (spiral_detected == 1)
-            return 0;
-
-        dir = DLG_DIR_NONE;
+	dir = DLG_DIR_NONE;
+	mlock = 1;
+	/* search dialog by SIP attributes
+	 * - if not found, hash table slot is left locked, to avoid races
+	 *   to add 'same' dialog on parallel forking or not-handled-yet
+	 *   retransmissions. Release slot after linking new dialog */
+	dlg = search_dlg(&callid, &ftag, &ttag, &dir);
+	if(dlg) {
+		mlock = 0;
+		if (detect_spirals) {
+			if (spiral_detected == 1)
+				return 0;
 
-        dlg = get_dlg(&callid, &ftag, &ttag, &dir);
-        if (dlg)
-        {
-			if ( dlg->state != DLG_STATE_DELETED )
-			{
+			if ( dlg->state != DLG_STATE_DELETED ) {
 				LM_DBG("Callid '%.*s' found, must be a spiraled request\n",
 					callid.len, callid.s);
 				spiral_detected = 1;
@@ -818,9 +822,12 @@ int dlg_new_dialog(sip_msg_t *req, struct cell *t, const int run_initial_cbs)
 				if (run_initial_cbs)
 					run_dlg_callbacks( DLGCB_SPIRALED, dlg, req, NULL,
 							DLG_DIR_DOWNSTREAM, 0);
-				/* get_dlg() has incremented the ref count by 1
-				 * - it's ok, dlg will be unref at the end of function */
-				goto finish;
+				/* set ctx dlg id shortcuts */
+				_dlg_ctx.iuid.h_entry = dlg->h_entry;
+				_dlg_ctx.iuid.h_id = dlg->h_id;
+				/* search_dlg() has incremented the ref count by 1 */
+				dlg_release(dlg);
+				return 0;
 			}
 			dlg_release(dlg);
         }
@@ -833,16 +840,16 @@ int dlg_new_dialog(sip_msg_t *req, struct cell *t, const int run_initial_cbs)
                          &ftag/*from_tag*/,
                          &req_uri /*r-uri*/ );
 
-	if (dlg==0)
-	{
+	if (dlg==0) {
+		if(likely(mlock==1)) dlg_hash_release(&callid);
 		LM_ERR("failed to create new dialog\n");
 		return -1;
 	}
 
 	/* save caller's tag, cseq, contact and record route*/
 	if (populate_leg_info(dlg, req, t, DLG_CALLER_LEG,
-			&(get_from(req)->tag_value)) !=0)
-	{
+			&(get_from(req)->tag_value)) !=0) {
+		if(likely(mlock==1)) dlg_hash_release(&callid);
 		LM_ERR("could not add further info to the dialog\n");
 		shm_free(dlg);
 		return -1;
@@ -851,7 +858,9 @@ int dlg_new_dialog(sip_msg_t *req, struct cell *t, const int run_initial_cbs)
 	/* Populate initial varlist: */
 	dlg->vars = get_local_varlist_pointer(req, 1);
 
-	link_dlg(dlg, 0);
+	/* if search_dlg() returned NULL, slot was kept locked */
+	link_dlg(dlg, 0, mlock);
+	if(likely(mlock==1)) dlg_hash_release(&callid);
 
 	dlg->lifetime = get_dlg_timeout(req);
 	s.s   = _dlg_ctx.to_route_name;
@@ -872,16 +881,12 @@ int dlg_new_dialog(sip_msg_t *req, struct cell *t, const int run_initial_cbs)
 		goto error;
 	}
 
-	/* new dlg - reference it once more for current dialog iuid shortcut */
-    dlg_ref(dlg, 1);
-
     if_update_stat( dlg_enable_stats, processed_dlgs, 1);
 
-finish:
+	_dlg_ctx.cpid = my_pid();
     _dlg_ctx.iuid.h_entry = dlg->h_entry;
     _dlg_ctx.iuid.h_id = dlg->h_id;
     set_current_dialog(req, dlg);
-	dlg_release(dlg);
 
 	return 0;
 

+ 59 - 6
modules/dialog/dlg_hash.c

@@ -691,10 +691,12 @@ dlg_cell_t* dlg_get_by_iuid(dlg_iuid_t *diuid)
  * \param ftag from tag
  * \param ttag to tag
  * \param dir direction
+ * \param mode let hash table slot locked if dialog is not found
  * \return dialog structure on success, NULL on failure
  */
 static inline struct dlg_cell* internal_get_dlg(unsigned int h_entry,
-						str *callid, str *ftag, str *ttag, unsigned int *dir)
+						str *callid, str *ftag, str *ttag,
+						unsigned int *dir, int mode)
 {
 	struct dlg_cell *dlg;
 	struct dlg_entry *d_entry;
@@ -714,7 +716,7 @@ static inline struct dlg_cell* internal_get_dlg(unsigned int h_entry,
 		}
 	}
 
-	dlg_unlock( d_table, d_entry);
+	if(likely(mode==0)) dlg_unlock( d_table, d_entry);
 	LM_DBG("no dialog callid='%.*s' found\n", callid->len, callid->s);
 	return 0;
 }
@@ -743,7 +745,7 @@ struct dlg_cell* get_dlg( str *callid, str *ftag, str *ttag, unsigned int *dir)
 	unsigned int he;
 
 	he = core_hash(callid, 0, d_table->size);
-	dlg = internal_get_dlg(he, callid, ftag, ttag, dir);
+	dlg = internal_get_dlg(he, callid, ftag, ttag, dir, 0);
 
 	if (dlg == 0) {
 		LM_DBG("no dialog callid='%.*s' found\n", callid->len, callid->s);
@@ -753,18 +755,69 @@ struct dlg_cell* get_dlg( str *callid, str *ftag, str *ttag, unsigned int *dir)
 }
 
 
+/*!
+ * \brief Search dialog that corresponds to CallId, From Tag and To Tag
+ *
+ * Get dialog that correspond to CallId, From Tag and To Tag.
+ * See RFC 3261, paragraph 4. Overview of Operation:
+ * "The combination of the To tag, From tag, and Call-ID completely
+ * defines a peer-to-peer SIP relationship between [two UAs] and is
+ * referred to as a dialog."
+ * Note that the caller is responsible for decrementing (or reusing)
+ * the reference counter by one again if a dialog has been found.
+ * If the dialog is not found, the hash slot is left locked, to allow
+ * linking the structure of a new dialog.
+ * \param callid callid
+ * \param ftag from tag
+ * \param ttag to tag
+ * \param dir direction
+ * \return dialog structure on success, NULL on failure (and slot locked)
+ */
+dlg_cell_t* search_dlg( str *callid, str *ftag, str *ttag, unsigned int *dir)
+{
+	struct dlg_cell *dlg;
+	unsigned int he;
+
+	he = core_hash(callid, 0, d_table->size);
+	dlg = internal_get_dlg(he, callid, ftag, ttag, dir, 1);
+
+	if (dlg == 0) {
+		LM_DBG("dialog with callid='%.*s' not found\n", callid->len, callid->s);
+		return 0;
+	}
+	return dlg;
+}
+
+
+/*!
+ * \brief Release hash table slot by call-id
+ * \param callid call-id value
+ */
+void dlg_hash_release(str *callid)
+{
+	unsigned int he;
+	struct dlg_entry *d_entry;
+
+	he = core_hash(callid, 0, d_table->size);
+	d_entry = &(d_table->entries[he]);
+	dlg_unlock(d_table, d_entry);
+}
+
+
+
 /*!
  * \brief Link a dialog structure
  * \param dlg dialog
  * \param n extra increments for the reference counter
+ * \param mode link in safe mode (0 - lock slot; 1 - don't)
  */
-void link_dlg(struct dlg_cell *dlg, int n)
+void link_dlg(struct dlg_cell *dlg, int n, int mode)
 {
 	struct dlg_entry *d_entry;
 
 	d_entry = &(d_table->entries[dlg->h_entry]);
 
-	dlg_lock( d_table, d_entry);
+	if(unlikely(mode==0)) dlg_lock( d_table, d_entry);
 
 	/* keep id 0 for special cases */
 	dlg->h_id = 1 + d_entry->next_id++;
@@ -780,7 +833,7 @@ void link_dlg(struct dlg_cell *dlg, int n)
 
 	ref_dlg_unsafe(dlg, 1+n);
 
-	dlg_unlock( d_table, d_entry);
+	if(unlikely(mode==0)) dlg_unlock( d_table, d_entry);
 	return;
 }
 

+ 30 - 1
modules/dialog/dlg_hash.h

@@ -317,12 +317,41 @@ dlg_cell_t* dlg_get_by_iuid(dlg_iuid_t *diuid);
 dlg_cell_t* get_dlg(str *callid, str *ftag, str *ttag, unsigned int *dir);
 
 
+/*!
+ * \brief Search dialog that corresponds to CallId, From Tag and To Tag
+ *
+ * Get dialog that correspond to CallId, From Tag and To Tag.
+ * See RFC 3261, paragraph 4. Overview of Operation:
+ * "The combination of the To tag, From tag, and Call-ID completely
+ * defines a peer-to-peer SIP relationship between [two UAs] and is
+ * referred to as a dialog."
+ * Note that the caller is responsible for decrementing (or reusing)
+ * the reference counter by one again if a dialog has been found.
+ * If the dialog is not found, the hash slot is left locked, to allow
+ * linking the structure of a new dialog.
+ * \param callid callid
+ * \param ftag from tag
+ * \param ttag to tag
+ * \param dir direction
+ * \return dialog structure on success, NULL on failure (and slot locked)
+ */
+dlg_cell_t* search_dlg(str *callid, str *ftag, str *ttag, unsigned int *dir);
+
+
+/*!
+ * \brief Release hash table slot by call-id
+ * \param callid call-id value
+ */
+void dlg_hash_release(str *callid);
+
+
 /*!
  * \brief Link a dialog structure
  * \param dlg dialog
  * \param n extra increments for the reference counter
+ * \param mode link in safe mode (0 - lock slot; 1 - don't)
  */
-void link_dlg(dlg_cell_t *dlg, int n);
+void link_dlg(struct dlg_cell *dlg, int n, int mode);
 
 
 /*!

+ 7 - 2
modules/dialog/dlg_var.c

@@ -46,9 +46,14 @@ int dlg_cfg_cb(sip_msg_t *msg, unsigned int flags, void *cbp)
 		dlg = dlg_get_ctx_dialog();
 		if(dlg!=NULL) {
 			if(_dlg_ctx.t==0 && dlg->state==DLG_STATE_UNCONFIRMED) {
-				LM_DBG("new dialog with no trasaction after config execution\n");
-				dlg_release(dlg);
+				if(_dlg_ctx.cpid!=0 && _dlg_ctx.cpid==my_pid()) {
+					/* release to destroy dialog if created by this process
+					 * and request was not forwarded */
+					LM_DBG("new dialog with no trasaction after config execution\n");
+					dlg_release(dlg);
+				}
 			}
+			/* get ctx dlg increased ref count - release now */
 			dlg_release(dlg);
 		}
 	}

+ 1 - 0
modules/dialog/dlg_var.h

@@ -39,6 +39,7 @@ typedef struct _dlg_ctx {
 	int timeout;
 	dlg_cell_t *dlg1;
 	dlg_iuid_t iuid;
+	int cpid;
 	int set;
 	unsigned int dir;
 	int t;

+ 1 - 4
modules/ims_qos/mod.c

@@ -552,10 +552,7 @@ static int w_rx_aar(struct sip_msg *msg, char *route, char* str1, char* bar) {
         LM_DBG("Attached CDP auth session [%.*s] for Rx to dialog in %s mode\n", auth_session->id.len, auth_session->id.s, direction);
     } else {
         LM_DBG("Update AAR session for this dialog in mode %s\n", direction);
-        if (saved_t_data)
-                free_saved_transaction_global_data(saved_t_data); //only free global data if no AARs were sent. if one was sent we have to rely on the callback (CDP) to free
-        create_return_code(CSCF_RETURN_TRUE);
-        return CSCF_RETURN_TRUE;
+	auth_session = cdpb.AAAGetAuthSession(*rx_session_id);
     }
 
     LM_DBG("Suspending SIP TM transaction\n");

+ 11 - 6
modules/ims_qos/rx_aar.c

@@ -380,12 +380,17 @@ int add_media_components(AAAMessage* aar, struct sip_msg *req,
             //is this a stream to add to AAR.
             if (req_sdp_stream->is_rtp) {
 
-                rx_add_media_component_description_avp(aar, sdp_stream_num + 1,
-                        &req_sdp_stream->media, &req_sdp_session->ip_addr,
-                        &req_sdp_stream->port, &rpl_sdp_session->ip_addr,
-                        &rpl_sdp_stream->port, &rpl_sdp_stream->transport,
-                        &req_sdp_stream->raw_stream,
-                        &rpl_sdp_stream->raw_stream, direction);
+		//check if the src or dst port is 0 and if so then don't add to rx
+		int intportA = atoi(req_sdp_stream->port.s);
+		int intportB = atoi(rpl_sdp_stream->port.s);
+		if(intportA != 0 && intportB != 0){
+                	rx_add_media_component_description_avp(aar, sdp_stream_num + 1,
+                        	&req_sdp_stream->media, &req_sdp_session->ip_addr,
+	                        &req_sdp_stream->port, &rpl_sdp_session->ip_addr,
+        	                &rpl_sdp_stream->port, &rpl_sdp_stream->transport,
+                	        &req_sdp_stream->raw_stream,
+                        	&rpl_sdp_stream->raw_stream, direction);	
+		}
             }
             sdp_stream_num++;
         }

+ 12 - 0
modules/jsonrpc-s/Makefile

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

+ 162 - 0
modules/jsonrpc-s/README

@@ -0,0 +1,162 @@
+JSONRPC-S Module
+
+Daniel-Constantin Mierla
+
+   <[email protected]>
+
+Edited by
+
+Daniel-Constantin Mierla
+
+   <[email protected]>
+
+   Copyright © 2014 asipto.com
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+
+              1.1. Limitations
+
+        2. Dependencies
+
+              2.1. Kamailio Modules
+              2.2. External Libraries or Applications
+
+        3. Parameters
+
+              3.1. pretty_format (int)
+
+        4. Functions
+
+              4.1. jsonrpc_dispatch()
+
+   List of Examples
+
+   1.1. Set pretty_format parameter
+   1.2. jsonrpc_dispatch usage
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+
+        1.1. Limitations
+
+   2. Dependencies
+
+        2.1. Kamailio Modules
+        2.2. External Libraries or Applications
+
+   3. Parameters
+
+        3.1. pretty_format (int)
+
+   4. Functions
+
+        4.1. jsonrpc_dispatch()
+
+1. Overview
+
+   1.1. Limitations
+
+   This module provides JSONRPC server over HTTP implementation, tailored
+   for the needs of Kamailio.
+
+   The JSONRPC-S module uses the xHTTP module to handle HTTP requests.
+   Read the documentation of the xHTTP module for more details.
+
+1.1. Limitations
+
+     * This module does not implement asynchronous RPC commands. It is
+       unlikely that asynchronous RPC commands will be executed from an
+       JSONRPC over HTTP client.
+     * This module does not accept parameters embedded in a structure (see
+       RPC documentation for more info about how parameters can be passed
+       to RPC).
+
+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:
+     * xhttp - xHTTP.
+
+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. pretty_format (int)
+
+3.1. pretty_format (int)
+
+   Pretty format for JSONRPC response document.
+
+   Default value is '0'.
+
+   Example 1.1. Set pretty_format parameter
+...
+modparam("jsonrpc-s", "pretty_format", 1)
+...
+
+4. Functions
+
+   4.1. jsonrpc_dispatch()
+
+4.1. jsonrpc_dispatch()
+
+   Handle the JSONRPC request and generate a response.
+
+   Example 1.2. jsonrpc_dispatch usage
+...
+#!KAMAILIO
+
+memdbg=5
+memlog=5
+
+debug=3
+log_stderror=yes
+
+fork=yes
+children=2
+
+tcp_accept_no_cl=yes
+
+mpath="modules/"
+
+loadmodule "sl.so"
+loadmodule "pv.so"
+loadmodule "xhttp.so"
+loadmodule "jsonrpc-s.so"
+
+request_route {
+        send_reply("404", "not found");
+        exit;
+}
+
+event_route[xhttp:request] {
+    if(src_ip!=127.0.0.1) {
+        xhttp_reply("403", "Forbidden", "text/html",
+            "<html><body>Not allowed from $si</body></html>");
+        exit;
+        }
+        if ($hu =~ "^/RPC") {
+                jsonrpc_dispatch();
+        } else {
+        xhttp_reply("200", "OK", "text/html",
+            "<html><body>Wrong URL $hu</body></html>");
+    }
+    return;
+}
+...

+ 4 - 0
modules/jsonrpc-s/doc/Makefile

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

+ 37 - 0
modules/jsonrpc-s/doc/jsonrpc-s.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>JSONRPC-S 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>2014</year>
+	    <holder>asipto.com</holder>
+	</copyright>
+    </bookinfo>
+    <toc></toc>
+    
+    <xi:include href="jsonrpc-s_admin.xml"/>
+    
+    
+</book>

+ 159 - 0
modules/jsonrpc-s/doc/jsonrpc-s_admin.xml

@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+<!-- Module User's Guide -->
+
+<chapter>
+	
+	<title>&adminguide;</title>
+	
+	<section>
+	<title>Overview</title>
+	<para>
+		This module provides JSONRPC server over HTTP implementation,
+		tailored for the needs of &kamailio;.
+	<para>
+	</para>
+		The JSONRPC-S module uses the xHTTP module to handle HTTP requests.
+		Read the documentation of the xHTTP module for more details.
+	</para>
+
+	<section>
+	<title>Limitations</title>
+	<itemizedlist>
+	<listitem>
+	<para>
+		This module does not implement asynchronous RPC commands.
+		It is unlikely that asynchronous RPC commands will be executed
+		from an JSONRPC over HTTP client.
+	</para>
+	</listitem>
+	<listitem>
+	<para>
+		This module does not accept parameters embedded in a structure
+		(see RPC documentation for more info about how parameters can be
+		passed to RPC).
+	</para>
+	</listitem>
+	</itemizedlist>
+	</section>
+	</section>
+
+	<section>
+	<title>Dependencies</title>
+	<section>
+		<title>&kamailio; Modules</title>
+		<para>
+		The following modules must be loaded before this module:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>xhttp</emphasis> - xHTTP.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	<section>
+		<title>External Libraries or Applications</title>
+		<para>
+		The following libraries or applications must be installed before running
+		&kamailio; with this module loaded:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>None</emphasis>
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	</section>
+	<section>
+	<title>Parameters</title>
+	<section>
+		<title><varname>pretty_format</varname> (int)</title>
+		<para>
+			Pretty format for JSONRPC response document. 
+		</para>
+		<para>
+		<emphasis>
+			Default value is '0'.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>pretty_format</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("jsonrpc-s", "pretty_format", 1)
+...
+</programlisting>
+		</example>
+	</section>
+	</section>
+
+	<section>
+	<title>Functions</title>
+ 	<section>
+	    <title>
+		<function moreinfo="none">jsonrpc_dispatch()</function>
+	    </title>
+	    <para>
+		Handle the JSONRPC request and generate a response.
+	    </para>
+		<example>
+		<title><function>jsonrpc_dispatch</function> usage</title>
+		<programlisting format="linespecific">
+...
+#!KAMAILIO
+
+memdbg=5
+memlog=5
+
+debug=3
+log_stderror=yes
+
+fork=yes
+children=2
+
+tcp_accept_no_cl=yes
+
+mpath="modules/"
+
+loadmodule "sl.so"
+loadmodule "pv.so"
+loadmodule "xhttp.so"
+loadmodule "jsonrpc-s.so"
+
+request_route {
+	send_reply("404", "not found");
+	exit;
+}
+
+event_route[xhttp:request] {
+    if(src_ip!=127.0.0.1) {
+        xhttp_reply("403", "Forbidden", "text/html",
+            "&lt;html&gt;&lt;body&gt;Not allowed from $si&lt;/body&gt;&lt;/html&gt;");
+        exit;
+	}
+	if ($hu =~ "^/RPC") {
+		jsonrpc_dispatch();
+	} else {
+        xhttp_reply("200", "OK", "text/html",
+            "&lt;html&gt;&lt;body&gt;Wrong URL $hu&lt;/body&gt;&lt;/html&gt;");
+    }
+    return;
+}
+...
+</programlisting>
+	    </example>
+	</section>
+	</section>
+</chapter>
+

+ 910 - 0
modules/jsonrpc-s/jsonrpc-s_mod.c

@@ -0,0 +1,910 @@
+/**
+ * Copyright (C) 2014 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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 <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "../../ver.h"
+#include "../../trim.h"
+#include "../../sr_module.h"
+#include "../../nonsip_hooks.h"
+#include "../../modules/xhttp/api.h"
+
+#include "jsonrpc-s_mod.h"
+
+/** @addtogroup jsonrpc-s
+ * @ingroup modules
+ * @{
+ *
+ * <h1>Overview of Operation</h1>
+ * This module provides jsonrpc over http server implementation.
+ */
+
+/** @file
+ *
+ * This is the main file of jsonrpc-s module which contains all the functions
+ * related to http processing, as well as the module interface.
+ */
+
+MODULE_VERSION
+
+
+#define jsonrpc_malloc	pkg_malloc
+#define jsonrpc_free	pkg_free
+
+str JSONRPC_REASON_OK = str_init("OK");
+str JSONRPC_CONTENT_TYPE_HTML = str_init("application/json");
+
+static int jsonrpc_pretty_format = 0;
+
+static int jsonrpc_register_rpc(void);
+
+static int mod_init(void);
+static int child_init(int rank);
+static int jsonrpc_dispatch(sip_msg_t* msg, char* s1, char* s2);
+
+
+/** The context of the jsonrpc request being processed.
+ *
+ * This is a global variable that records the context of the jsonrpc request
+ * being currently processed.
+ * @sa rpc_ctx
+ */
+static jsonrpc_ctx_t _jsonrpc_ctx;
+
+static xhttp_api_t xhttp_api;
+
+/** Pointers to the functions that implement the RPC interface
+ * of jsonrpc module
+ */
+static rpc_t func_param;
+
+#define JSONRPC_ERROR_REASON_BUF_LEN	128
+#define JSONRPC_PRINT_VALUE_BUF_LEN		1024
+
+char jsonrpc_error_buf[JSONRPC_ERROR_REASON_BUF_LEN];
+
+static cmd_export_t cmds[] = {
+	{"jsonrpc_dispatch", (cmd_function)jsonrpc_dispatch, 0, 0, 0, REQUEST_ROUTE},
+	{0, 0, 0, 0, 0, 0}
+};
+
+static param_export_t params[] = {
+	{"pretty_format",   PARAM_INT,    &jsonrpc_pretty_format},
+	{0, 0, 0}
+};
+
+/** module exports */
+struct module_exports exports= {
+	"jsonrpc-s",
+	DEFAULT_DLFLAGS, /* dlopen flags */
+	cmds,
+	params,
+	0,		/* exported statistics */
+	0,		/* exported MI functions */
+	0,		/* exported pseudo-variables */
+	0,		/* extra processes */
+	mod_init,	/* module initialization function */
+	0,
+	0,
+	child_init	/* per-child init function */
+};
+
+
+typedef struct jsonrpc_error {
+	int code;
+	str text;
+} jsonrpc_error_t;
+
+static jsonrpc_error_t _jsonrpc_error_table[] = {
+	{ -32700, { "Parse Error", 11 } },
+	{ -32600, { "Invalid Request", 15 } },
+	{ -32601, { "Method Not Found", 16 } },
+	{ -32602, { "Invalid Parameters", 18 } },
+	{ -32603, { "Internal Error", 14 } },
+	{ -32000, { "Execution Error", 15 } },
+	{0, { 0, 0 } } 
+};
+
+/** Implementation of rpc_fault function required by the management API.
+ *
+ * This function will be called whenever a management function
+ * indicates that an error ocurred while it was processing the request. The
+ * function takes the reply code and reason phrase as parameters, these will
+ * be put in the body of the reply.
+ *
+ * @param ctx A pointer to the context structure of the request being
+ *            processed.
+ * @param code Reason code.
+ * @param fmt Formatting string used to build the reason phrase.
+ */
+static void jsonrpc_fault(jsonrpc_ctx_t* ctx, int code, char* fmt, ...)
+{
+	va_list ap;
+
+	ctx->http_code = code;
+	va_start(ap, fmt);
+	vsnprintf(jsonrpc_error_buf, JSONRPC_ERROR_REASON_BUF_LEN, fmt, ap);
+	va_end(ap);
+	ctx->http_text.len = strlen(jsonrpc_error_buf);
+	ctx->http_text.s = jsonrpc_error_buf;
+	if(ctx->error_code == 0) ctx->error_code = -32000;
+
+	return;
+}
+
+
+
+/** Initialize jsonrpc reply data structure.
+ *
+ * This function initializes the data structure that contains all data related
+ * to the jsonrpc reply being created. The function must be called before any
+ * other function that adds data to the reply.
+ * @param ctx jsonrpc_ctx_t structure to be initialized.
+ * @return 0 on success, a negative number on error.
+ */
+static int jsonrpc_init_reply(jsonrpc_ctx_t *ctx)
+{
+	ctx->http_code = 200;
+	ctx->http_text = JSONRPC_REASON_OK;
+	ctx->jrpl = srjson_NewDoc(NULL);
+	if(ctx->jrpl==NULL) {
+		LM_ERR("Failed to init the reply json document\n");
+		return -1;
+	}
+	ctx->jrpl->root = srjson_CreateObject(ctx->jrpl);
+	if(ctx->jrpl->root==NULL) {
+		LM_ERR("Failed to init the reply json root node\n");
+		return -1;
+	}
+	srjson_AddStrStrToObject(ctx->jrpl, ctx->jrpl->root,
+					"jsonrpc", 7,
+					"2.0", 3);
+
+	return 0;
+}
+
+/** Implementation of rpc_send function required by the management API.
+ *
+ * This is the function that will be called whenever a management function
+ * asks the management interface to send the reply to the client.
+ * The SIP/HTTP reply sent to
+ * the client will be always 200 OK, if an error ocurred on the server then it
+ * will be indicated in the html document in body.
+ *
+ * @param ctx A pointer to the context structure of the jsonrpc request that
+ *            generated the reply.
+ * @return 1 if the reply was already sent, 0 on success, a negative number on
+ *            error
+ */
+static int jsonrpc_send(jsonrpc_ctx_t* ctx)
+{
+	srjson_t *nj = NULL;
+	int i;
+	str rbuf;
+
+	if (ctx->reply_sent) return 1;
+
+	ctx->reply_sent = 1;
+
+	if(ctx->error_code != 0) {
+		/* fault handling */
+		nj = srjson_CreateObject(ctx->jrpl);
+		if(nj!=NULL) {
+			srjson_AddNumberToObject(ctx->jrpl, nj, "code",
+					ctx->error_code);
+			for(i=0; _jsonrpc_error_table[i].code!=0
+					&& _jsonrpc_error_table[i].code!=ctx->error_code; i++);
+			if(_jsonrpc_error_table[i].code!=0) {
+				srjson_AddStrStrToObject(ctx->jrpl, nj,
+					"message", 7,
+					_jsonrpc_error_table[i].text.s,
+					_jsonrpc_error_table[i].text.len);
+			} else {
+				srjson_AddStrStrToObject(ctx->jrpl, nj,
+					"message", 7, "Unexpected Error", 16);
+			}
+			srjson_AddItemToObject(ctx->jrpl, ctx->jrpl->root, "error", nj);
+		}
+	} else {
+		nj = srjson_GetObjectItem(ctx->jrpl, ctx->jrpl->root, "result");
+		if(nj==NULL) {
+			if(ctx->rpl_node!=NULL) {
+				srjson_AddItemToObject(ctx->jrpl, ctx->jrpl->root,
+					"result", ctx->rpl_node);
+				ctx->rpl_node = 0;
+			} else {
+				srjson_AddStrStrToObject(ctx->jrpl, ctx->jrpl->root,
+					"result", 6, "ok", 2);
+			}
+		}
+	}
+	nj = srjson_GetObjectItem(ctx->jreq, ctx->jreq->root, "id");
+	if(nj!=NULL) {
+		if(nj->valuestring!=NULL) {
+			srjson_AddStrStrToObject(ctx->jrpl, ctx->jrpl->root,
+					"id", 2,
+					nj->valuestring, strlen(nj->valuestring));
+		} else {
+			srjson_AddNumberToObject(ctx->jrpl, ctx->jrpl->root, "id",
+					nj->valueint);
+		}
+	}
+
+	if(jsonrpc_pretty_format==0) {
+		rbuf.s = srjson_PrintUnformatted(ctx->jrpl, ctx->jrpl->root);
+	} else {
+		rbuf.s = srjson_Print(ctx->jrpl, ctx->jrpl->root);
+	}
+	if(rbuf.s!=NULL) {
+		rbuf.len = strlen(rbuf.s);
+	}
+	if (rbuf.s!=NULL) {
+		xhttp_api.reply(ctx->msg, ctx->http_code, &ctx->http_text,
+			&JSONRPC_CONTENT_TYPE_HTML, &rbuf);
+	} else {
+		xhttp_api.reply(ctx->msg, ctx->http_code, &ctx->http_text,
+				NULL, NULL);
+	}
+	if (rbuf.s!=NULL) {
+		ctx->jrpl->free_fn(rbuf.s);
+	}
+
+	return 0;
+}
+
+
+/** Converts the variables provided in parameter ap according to formatting
+ * string provided in parameter fmt into HTML format.
+ *
+ * This function takes the parameters provided in ap parameter and creates
+ * HTML formatted parameters that will be put in the html document.
+ * The format of input parameters is described in formatting string
+ * fmt which follows the syntax of the management API. In the case of
+ * an error the function will generate an error reply in err_reply parameter
+ * instead.
+ * @param ctx An error reply document will be generated here if the
+ *                  function encounters a problem while processing input
+ *                  parameters.
+ * @param fmt Formatting string of the management API.
+ * @param ap A pointer to the array of input parameters.
+ *
+ */
+static srjson_t* jsonrpc_print_value(jsonrpc_ctx_t* ctx, char fmt, va_list* ap)
+
+{
+	srjson_t *nj = NULL;
+	char buf[JSONRPC_PRINT_VALUE_BUF_LEN];
+	time_t dt;
+	struct tm* t;
+	str *sp;
+
+	switch(fmt) {
+	case 'd':
+		nj = srjson_CreateNumber(ctx->jrpl, va_arg(*ap, int));
+		break;
+	case 'f':
+		nj = srjson_CreateNumber(ctx->jrpl, va_arg(*ap, double));
+		break;
+	case 'b':
+		nj = srjson_CreateBool(ctx->jrpl, ((va_arg(*ap, int)==0)?0:1));
+		break;
+	case 't':
+		dt = va_arg(*ap, time_t);
+		t = gmtime(&dt);
+		if (strftime(buf, JSONRPC_PRINT_VALUE_BUF_LEN,
+				"%Y%m%dT%H:%M:%S", t) == 0) {
+			LM_ERR("Error while converting time\n");
+			return NULL;
+		}
+		nj = srjson_CreateString(ctx->jrpl, buf);
+		break;
+	case 's':
+		nj = srjson_CreateString(ctx->jrpl, va_arg(*ap, char*));
+		break;
+	case 'S':
+		sp = va_arg(*ap, str*);
+		nj = srjson_CreateStr(ctx->jrpl, sp->s, sp->len);
+		break;
+	default:
+		LM_ERR("Invalid formatting character [%c]\n", fmt);
+		return NULL;
+	}
+	return nj;
+}
+
+
+
+/** Implementation of rpc_add function required by the management API.
+ *
+ * This function will be called when an RPC management function calls
+ * rpc->add to add a parameter to the jsonrpc reply being generated.
+ */
+static int jsonrpc_add(jsonrpc_ctx_t* ctx, char* fmt, ...)
+{
+	srjson_t *nj = NULL;
+	void **void_ptr;
+	va_list ap;
+
+	va_start(ap, fmt);
+	while(*fmt) {
+		if (*fmt == '{' || *fmt == '[') {
+			void_ptr = va_arg(ap, void**);
+			if (*fmt == '{') {
+				nj = srjson_CreateObject(ctx->jrpl);
+			} else {
+				nj = srjson_CreateArray(ctx->jrpl);
+			}
+			*void_ptr = nj;
+		} else {
+			nj = jsonrpc_print_value(ctx, *fmt, &ap);
+		}
+
+		if(nj==NULL) goto err;
+		if(ctx->flags & RET_ARRAY) {
+			if (ctx->rpl_node==NULL) {
+				ctx->rpl_node = srjson_CreateArray(ctx->jrpl);
+				if(ctx->rpl_node == 0) {
+					LM_ERR("failed to create the root array node\n");
+					goto err;
+				}
+			}
+			srjson_AddItemToArray(ctx->jrpl, ctx->rpl_node, nj);
+		} else {
+			if (ctx->rpl_node) srjson_Delete(ctx->jrpl, ctx->rpl_node);
+			ctx->rpl_node = nj;
+		}
+
+		fmt++;
+	}
+	va_end(ap);
+	return 0;
+err:
+	va_end(ap);
+	return -1;
+}
+
+
+/** Implementation of rpc->scan function required by the management API.
+ *
+ * This is the function that will be called whenever a management function
+ * calls rpc->scan to get the value of parameter from the jsonrpc
+ * request. This function will extract the current parameter from the jsonrpc
+ * URL and attempts to convert it to the type requested by the management
+ * function that called it.
+ */
+static int jsonrpc_scan(jsonrpc_ctx_t* ctx, char* fmt, ...)
+{
+	int *int_ptr;
+	char **char_ptr;
+	double *double_ptr;
+	str *str_ptr;
+	int mandatory_param = 1;
+	int modifiers = 0;
+	int auto_convert = 0;
+	char* orig_fmt;
+	va_list ap;
+
+	if(ctx->req_node==NULL)
+		return 0;
+
+	orig_fmt=fmt;
+	va_start(ap, fmt);
+	while(*fmt && ctx->req_node) {
+		switch(*fmt) {
+		case '*': /* start of optional parameters */
+			mandatory_param = 0;
+			modifiers++;
+			fmt++;
+			continue;
+		case '.': /* autoconvert */
+			modifiers++;
+			fmt++;
+			auto_convert = 1;
+			continue;
+		case 'b': /* Bool */
+		case 't': /* Date and time */
+		case 'd': /* Integer */
+			int_ptr = va_arg(ap, int*);
+			*int_ptr = ctx->req_node->valueint;
+			break;
+		case 'f': /* double */
+			double_ptr = va_arg(ap, double*);
+			*double_ptr = ctx->req_node->valuedouble;
+			break;
+		case 's': /* zero terminated string */
+			char_ptr = va_arg(ap, char**);
+			*char_ptr = ctx->req_node->valuestring;
+			break;
+		case 'S': /* str structure */
+			str_ptr = va_arg(ap, str*);
+			str_ptr->s = ctx->req_node->valuestring;
+			str_ptr->len = strlen(ctx->req_node->valuestring);
+			break;
+		case '{':
+		case '[':
+			LM_ERR("Unsupported param type '%c'\n", *fmt);
+			jsonrpc_fault(ctx, 500, "Unsupported param type");
+			goto error;
+		default:
+			LM_ERR("Invalid param type in formatting string: [%c]\n", *fmt);
+			jsonrpc_fault(ctx, 500,
+				"Internal Server Error (inval formatting str)");
+			goto error;
+		}
+		fmt++;
+		auto_convert = 0;
+		ctx->req_node = ctx->req_node->next;
+	}
+	va_end(ap);
+	return (int)(fmt-orig_fmt)-modifiers;
+error:
+	va_end(ap);
+	return -((int)(fmt-orig_fmt)-modifiers);
+}
+
+
+/** Implementation of rpc_printf function required by the management API.
+ *
+ * This function will be called whenever an RPC management function calls
+ * rpc-printf to add a parameter to the jsonrpc reply being constructed.
+ */
+static int jsonrpc_printf(jsonrpc_ctx_t* ctx, char* fmt, ...)
+{
+	int n, buf_size;
+	char *buf = 0;
+	char tbuf[JSONRPC_PRINT_VALUE_BUF_LEN];
+	va_list ap;
+	srjson_t *nj = NULL;
+
+	buf = tbuf;
+	buf_size = JSONRPC_PRINT_VALUE_BUF_LEN;
+	while (1) {
+		/* try to print in the allocated space. */
+		va_start(ap, fmt);
+		n = vsnprintf(buf, buf_size, fmt, ap);
+		va_end(ap);
+		/* if that worked, return the string. */
+		if (n > -1 && n < buf_size) {
+			nj = srjson_CreateString(ctx->jrpl, buf);
+			if(nj==NULL) {
+				LM_ERR("failed to create the value node\n");
+				if(buf && buf!=tbuf) jsonrpc_free(buf);
+				return -1;
+			}
+			if(ctx->flags & RET_ARRAY) {
+				if (ctx->rpl_node==NULL) {
+					ctx->rpl_node = srjson_CreateArray(ctx->jrpl);
+					if(ctx->rpl_node == 0) {
+						LM_ERR("failed to create the root array node\n");
+						if(buf && buf!=tbuf) jsonrpc_free(buf);
+						return -1;
+					}
+				}
+				srjson_AddItemToArray(ctx->jrpl, ctx->rpl_node, nj);
+			} else {
+				if (ctx->rpl_node) srjson_Delete(ctx->jrpl, ctx->rpl_node);
+				ctx->rpl_node = nj;
+			}
+			if(buf && buf!=tbuf) jsonrpc_free(buf);
+			return 0;
+		}
+		/* else try again with more space. */
+		if (n > -1) {   /* glibc 2.1 */
+			buf_size = n + 1; /* precisely what is needed */
+		} else {          /* glibc 2.0 */
+			buf_size *= 2;  /* twice the old size */
+		}
+		if(buf && buf!=tbuf) jsonrpc_free(buf);
+		if ((buf = jsonrpc_malloc(buf_size)) == 0) {
+			jsonrpc_fault(ctx, 500, "Internal Server Error (No memory left)");
+			LM_ERR("no memory left for rpc printf\n");
+			return -1;
+		}
+	}
+}
+
+
+/** Adds a new member to structure.
+ */
+static int jsonrpc_struct_add(srjson_t *jnode, char* fmt, ...)
+{
+	srjson_t *nj = NULL;
+	jsonrpc_ctx_t* ctx;
+	va_list ap;
+	void **void_ptr;
+	str mname;
+
+	if(jnode==NULL) {
+		LM_ERR("invalid json node parameter\n");
+		return -1;
+	}
+	if(jnode->type!=srjson_Object) {
+		LM_ERR("json node parameter is not object (%d)\n", jnode->type);
+		return -1;
+	}
+
+	ctx = &_jsonrpc_ctx;
+	if(ctx->jrpl==NULL) {
+		LM_ERR("reply object not initialized in rpl context\n");
+		return -1;
+	}
+
+	va_start(ap, fmt);
+	while(*fmt) {
+		mname.s = va_arg(ap, char*);
+		mname.len = (mname.s?strlen(mname.s):0);
+
+		if (*fmt == '{' || *fmt == '[') {
+			void_ptr = va_arg(ap, void**);
+			if (*fmt == '{') {
+				nj = srjson_CreateObject(ctx->jrpl);
+			} else {
+				nj = srjson_CreateArray(ctx->jrpl);
+			}
+			*void_ptr = nj;
+		} else {
+			nj = jsonrpc_print_value(ctx, *fmt, &ap);
+		}
+
+		if(nj==NULL) goto err;
+		srjson_AddItemToObject(ctx->jrpl, jnode,
+					mname.s, nj);
+		fmt++;
+	}
+	va_end(ap);
+	return 0;
+err:
+	va_end(ap);
+	return -1;
+}
+
+
+/** Adds a new member to structure.
+ */
+static int jsonrpc_array_add(srjson_t *jnode, char* fmt, ...)
+{
+	srjson_t *nj = NULL;
+	jsonrpc_ctx_t* ctx;
+	va_list ap;
+	void **void_ptr;
+
+	if(jnode==NULL) {
+		LM_ERR("invalid json node parameter\n");
+		return -1;
+	}
+	if(jnode->type!=srjson_Array) {
+		LM_ERR("json node parameter is not array (%d)\n", jnode->type);
+		return -1;
+	}
+
+	ctx = &_jsonrpc_ctx;
+	if(ctx->jrpl==NULL) {
+		LM_ERR("reply object not initialized in rpl context\n");
+		return -1;
+	}
+
+	va_start(ap, fmt);
+	while(*fmt) {
+		if (*fmt == '{' || *fmt == '[') {
+			void_ptr = va_arg(ap, void**);
+			if (*fmt == '{') {
+				nj = srjson_CreateObject(ctx->jrpl);
+			} else {
+				nj = srjson_CreateArray(ctx->jrpl);
+			}
+			*void_ptr = nj;
+		} else {
+			nj = jsonrpc_print_value(ctx, *fmt, &ap);
+		}
+
+		if(nj==NULL) goto err;
+		srjson_AddItemToArray(ctx->jrpl, jnode, nj);
+		fmt++;
+	}
+	va_end(ap);
+	return 0;
+err:
+	va_end(ap);
+	return -1;
+}
+
+
+static int jsonrpc_struct_scan(void* s, char* fmt, ...)
+{
+	LM_ERR("Not implemented\n");
+	return -1;
+}
+
+
+/** Create a new member from formatting string and add it to a structure.
+ */
+static int jsonrpc_struct_printf(srjson_t *jnode, char* mname, char* fmt, ...)
+{
+	jsonrpc_ctx_t* ctx;
+	int n, buf_size;
+	char *buf = 0;
+	char tbuf[JSONRPC_PRINT_VALUE_BUF_LEN];
+	va_list ap;
+	srjson_t *nj = NULL;
+
+	if(jnode==NULL || mname==NULL) {
+		LM_ERR("invalid json node or member name parameter (%p/%p)\n",
+				jnode, mname);
+		return -1;
+	}
+	if(jnode->type!=srjson_Object) {
+		LM_ERR("json node parameter is not object (%d)\n", jnode->type);
+		return -1;
+	}
+
+	ctx = &_jsonrpc_ctx;
+	if(ctx->jrpl==NULL) {
+		LM_ERR("reply object not initialized in rpl context\n");
+		return -1;
+	}
+
+	buf = tbuf;
+	buf_size = JSONRPC_PRINT_VALUE_BUF_LEN;
+	while (1) {
+		/* try to print in the allocated space. */
+		va_start(ap, fmt);
+		n = vsnprintf(buf, buf_size, fmt, ap);
+		va_end(ap);
+		/* if that worked, return the string. */
+		if (n > -1 && n < buf_size) {
+			nj = srjson_CreateString(ctx->jrpl, buf);
+			if(nj==NULL) {
+				LM_ERR("failed to create the value node\n");
+				if(buf && buf!=tbuf) jsonrpc_free(buf);
+				return -1;
+			}
+			srjson_AddItemToObject(ctx->jrpl, jnode, mname, nj);
+			if(buf && buf!=tbuf) jsonrpc_free(buf);
+			return 0;
+		}
+		/* else try again with more space. */
+		if (n > -1) {   /* glibc 2.1 */
+			buf_size = n + 1; /* precisely what is needed */
+		} else {          /* glibc 2.0 */
+			buf_size *= 2;  /* twice the old size */
+		}
+		if(buf && buf!=tbuf) jsonrpc_free(buf);
+		if ((buf = jsonrpc_malloc(buf_size)) == 0) {
+			jsonrpc_fault(ctx, 500, "Internal Server Error (No memory left)");
+			LM_ERR("no memory left for rpc printf\n");
+			return -1;
+		}
+	}
+	return -1;
+}
+
+
+/** Returns the RPC capabilities supported by the xmlrpc driver.
+ */
+static rpc_capabilities_t jsonrpc_capabilities(jsonrpc_ctx_t* ctx)
+{
+	/* No support for async commands.
+	 */
+	return 0;
+}
+
+
+/** Returns a new "delayed reply" context.
+ * Creates a new delayed reply context in shm and returns it.
+ * @return 0 - not supported, already replied, or no more memory;
+ *         !=0 pointer to the special delayed ctx.
+ * Note1: one should use the returned ctx reply context to build a reply and
+ *  when finished call rpc_delayed_ctx_close().
+ * Note2: adding pieces to the reply in different processes is not supported.
+ */
+static struct rpc_delayed_ctx* jsonrpc_delayed_ctx_new(jsonrpc_ctx_t* ctx)
+{
+	return NULL;
+}
+
+
+/** Closes a "delayed reply" context and sends the reply.
+ * If no reply has been sent the reply will be built and sent automatically.
+ * See the notes from rpc_new_delayed_ctx()
+ */
+static void jsonrpc_delayed_ctx_close(struct rpc_delayed_ctx* dctx)
+{
+	return;
+}
+
+
+static void jsonrpc_clean_context(jsonrpc_ctx_t* ctx)
+{
+	if (!ctx) return;
+	srjson_DeleteDoc(ctx->jreq);
+	if(ctx->rpl_node!=NULL) {
+		srjson_Delete(ctx->jrpl, ctx->rpl_node);
+		ctx->rpl_node = NULL;
+	}
+	srjson_DeleteDoc(ctx->jrpl);
+}
+
+static int mod_init(void)
+{
+	/* bind the XHTTP API */
+	if (xhttp_load_api(&xhttp_api) < 0) {
+		LM_ERR("cannot bind to XHTTP API\n");
+		return -1;
+	}
+
+	memset(&func_param, 0, sizeof(func_param));
+	func_param.send              = (rpc_send_f)jsonrpc_send;
+	func_param.fault             = (rpc_fault_f)jsonrpc_fault;
+	func_param.add               = (rpc_add_f)jsonrpc_add;
+	func_param.scan              = (rpc_scan_f)jsonrpc_scan;
+	func_param.printf            = (rpc_printf_f)jsonrpc_printf;
+	func_param.struct_add        = (rpc_struct_add_f)jsonrpc_struct_add;
+	func_param.array_add         = (rpc_struct_add_f)jsonrpc_array_add;
+	func_param.struct_scan       = (rpc_struct_scan_f)jsonrpc_struct_scan;
+	func_param.struct_printf     = (rpc_struct_printf_f)jsonrpc_struct_printf;
+	func_param.capabilities      = (rpc_capabilities_f)jsonrpc_capabilities;
+	func_param.delayed_ctx_new   = (rpc_delayed_ctx_new_f)jsonrpc_delayed_ctx_new;
+	func_param.delayed_ctx_close =
+		(rpc_delayed_ctx_close_f)jsonrpc_delayed_ctx_close;
+
+	jsonrpc_register_rpc();
+
+	return 0;
+}
+
+static int child_init(int rank)
+{
+	if(rank==PROC_MAIN || rank==PROC_TCP_MAIN)
+		return 0; /* do nothing for the main process */
+
+	return 0;
+}
+
+
+static int jsonrpc_dispatch(sip_msg_t* msg, char* s1, char* s2)
+{
+	rpc_export_t* rpce;
+	jsonrpc_ctx_t* ctx;
+	int ret = 0;
+	srjson_t *nj = NULL;
+	str val;
+
+	if(!IS_HTTP(msg)) {
+		LM_DBG("Got non HTTP msg\n");
+		return NONSIP_MSG_PASS;
+	}
+
+	/* initialize jsonrpc context */
+	ctx = &_jsonrpc_ctx;
+	memset(ctx, 0, sizeof(jsonrpc_ctx_t));
+	ctx->msg = msg;
+	/* parse the jsonrpc request */
+	ctx->jreq = srjson_NewDoc(NULL);
+	if(ctx->jreq==NULL) {
+		LM_ERR("Failed to init the json document\n");
+		return NONSIP_MSG_PASS;
+	}
+
+	ctx->jreq->buf.s = get_body(msg);
+	ctx->jreq->buf.len = strlen(ctx->jreq->buf.s);
+	ctx->jreq->root = srjson_Parse(ctx->jreq, ctx->jreq->buf.s);
+	if(ctx->jreq->root == NULL)
+	{
+		LM_ERR("invalid json doc [[%s]]\n", ctx->jreq->buf.s);
+		return NONSIP_MSG_PASS;
+	}
+	if (jsonrpc_init_reply(ctx) < 0) goto send_reply;
+
+	/* sanity checks on jsonrpc request */
+	nj = srjson_GetObjectItem(ctx->jreq, ctx->jreq->root, "jsonrpc");
+	if(nj==NULL) {
+		LM_ERR("missing jsonrpc field in request\n");
+		goto send_reply;
+	}
+	val.s = nj->valuestring;
+	val.len = strlen(val.s);
+	if(val.len!=3 || strncmp(val.s, "2.0", 3)!=0) {
+		LM_ERR("unsupported jsonrpc version [%.*s]\n", val.len, val.s);
+		goto send_reply;
+	}
+	/* run jsonrpc command */
+	nj = srjson_GetObjectItem(ctx->jreq, ctx->jreq->root, "method");
+	if(nj==NULL) {
+		LM_ERR("missing jsonrpc method field in request\n");
+		goto send_reply;
+	}
+	val.s = nj->valuestring;
+	val.len = strlen(val.s);
+	ctx->method = val.s;
+	rpce = find_rpc_export(ctx->method, 0);
+	if (!rpce || !rpce->function) {
+		LM_ERR("method callback not found [%.*s]\n", val.len, val.s);
+		jsonrpc_fault(ctx, 500, "Method Not Found");
+		goto send_reply;
+	}
+	ctx->flags = rpce->flags;
+	nj = srjson_GetObjectItem(ctx->jreq, ctx->jreq->root, "params");
+	if(nj!=NULL && nj->type!=srjson_Array && nj->type!=srjson_Object) {
+		LM_ERR("params field is not an array or object\n");
+		goto send_reply;
+	}
+	if(nj!=NULL) ctx->req_node = nj->child;
+	rpce->function(&func_param, ctx);
+
+send_reply:
+	if (!ctx->reply_sent) {
+		ret = jsonrpc_send(ctx);
+	}
+	jsonrpc_clean_context(ctx);
+	if (ret < 0) return -1;
+	return 1;
+}
+
+
+/**
+ *
+ */
+static const char* jsonrpc_rpc_echo_doc[2] = {
+	"Sample echo command",
+	0
+};
+
+/**
+ *
+ */
+static void jsonrpc_rpc_echo(rpc_t* rpc, void* ctx)
+{
+	str sval;
+	int ival = 0;
+	int ret;
+	ret = rpc->scan(ctx, "S*d", &sval, &ival);
+	if(ret>0) {
+		LM_DBG("READ STR: %.*s\n", sval.len, sval.s);
+		rpc->add(ctx, "S", &sval);
+	}
+	if(ret>1) {
+		LM_DBG("READ INT: %d\n", ival);
+		rpc->add(ctx, "d", ival);
+	}
+}
+/**
+ *
+ */
+static rpc_export_t jsonrpc_rpc[] = {
+	{"jsonrpc.echo", jsonrpc_rpc_echo,  jsonrpc_rpc_echo_doc,       RET_ARRAY},
+	{0, 0, 0, 0}
+};
+
+/**
+ *
+ */
+static int jsonrpc_register_rpc(void)
+{
+	if (rpc_register_array(jsonrpc_rpc)!=0)
+	{
+		LM_ERR("failed to register RPC commands\n");
+		return -1;
+	}
+	return 0;
+}

+ 57 - 0
modules/jsonrpc-s/jsonrpc-s_mod.h

@@ -0,0 +1,57 @@
+/**
+ *
+ * Copyright (C) 2014 Daniel-Constantin Mierla (asipto.com) 
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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 _JSONRPC_S_H_
+#define _JSONRPC_S_H_
+
+#include "../../str.h"
+#include "../../rpc_lookup.h"
+#include "../../parser/msg_parser.h"
+#include "../../lib/srutils/srjson.h"
+
+
+/** The context of the jsonrpc request being processed.
+ *
+ * This is the data structure that contains all data related to the xhttp_rpc
+ * request being processed, such as the reply code and reason, data to be sent
+ * to the client in the reply, and so on.
+ *
+ * There is always one context per jsonrpc request.
+ */
+typedef struct jsonrpc_ctx {
+	sip_msg_t* msg;        /**< The SIP/HTTP received message. */
+	char* method;          /**< Name of the management function to be called */
+	unsigned int flags;    /**< Various flags, such as return value type */
+	srjson_doc_t *jreq;    /**< JSON request document */
+	srjson_t *req_node;    /**< Pointer to crt node in json req parameter list */
+	srjson_doc_t *jrpl;    /**< JSON reply document */
+	srjson_t *rpl_node;    /**< Pointer to crt node in json reply doc */
+	int reply_sent;        /**< Flag set if the json reply was sent */
+	int error_code;        /**< Json error code */
+	int http_code;         /**< http reply code */
+	str http_text;         /**< http reply reason text */
+} jsonrpc_ctx_t;
+
+
+#endif
+

+ 5 - 0
modules/outbound/ob_mod.c

@@ -385,6 +385,8 @@ static int use_outbound_non_reg(struct sip_msg *msg)
 			LM_ERR("parsing Route-URI parameters\n");
 			return 0;
 		}
+		/* Not interested in param body - just the hooks */
+		free_params(params);
 
 		if (hooks.uri.ob)
 		{
@@ -450,6 +452,9 @@ static int use_outbound_non_reg(struct sip_msg *msg)
 			LM_ERR("parsing Contact-URI parameters\n");
 			return 0;
 		}
+		/* Not interested in param body - just the hooks */
+		free_params(params);
+
 		if (hooks.contact.ob)
 		{
 			LM_DBG("found ;ob parameter on Contact-URI - outbound"

+ 1 - 0
modules/pv/Makefile

@@ -15,4 +15,5 @@ DEFS+=-DKAMAILIO_MOD_INTERFACE
 SERLIBPATH=../../lib
 SER_LIBS+=$(SERLIBPATH)/kmi/kmi
 SER_LIBS+=$(SERLIBPATH)/kcore/kcore
+SER_LIBS+=$(SERLIBPATH)/srutils/srutils
 include ../../Makefile.modules

+ 1 - 0
modules/pv/pv_trans.c

@@ -47,6 +47,7 @@
 #include "../../parser/parse_nameaddr.h"
 
 #include "../../lib/kcore/strcommon.h"
+#include "../../lib/srutils/shautils.h"
 #include "pv_trans.h"
 
 

+ 0 - 6
modules/uac/uac_reg.c

@@ -1274,7 +1274,6 @@ int uac_reg_db_refresh(str *pl_uuid)
 	reg_dbf.free_result(reg_db_con, db_res);
 	reg_dbf.close(reg_db_con);
 
-done:
 	return 0;
 
 error:
@@ -1586,9 +1585,6 @@ static void rpc_uac_reg_update_flag(rpc_t* rpc, void* ctx, int mode, int fval)
 {
 	int i;
 	reg_item_t *reg = NULL;
-	void* th;
-	str none = {"none", 4};
-	time_t tn;
 	str attr = {0};
 	str val = {0};
 	str *rval;
@@ -1611,8 +1607,6 @@ static void rpc_uac_reg_update_flag(rpc_t* rpc, void* ctx, int mode, int fval)
 		return;
 	}
 
-	tn = time(NULL);
-
 	for(i=0; i<_reg_htable->htsize; i++)
 	{
 		lock_get(&_reg_htable->entries[i].lock);

+ 1 - 1
parser/parse_rr.c

@@ -138,7 +138,7 @@ static inline int do_parse_rr_body(char *buf, int len, rr_t **head)
 	}
 
  error:
-	if (r) pkg_free(r);
+	if (r) free_rr(&r);
 	free_rr(head); /* Free any contacts created so far */
 	return -1;