Browse Source

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

Jason Penton 11 years ago
parent
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 ...) 
 # note: sed with POSIX.1 regex doesn't support |, + or ? (darwin, solaris ...) 
 install-cfg: $(cfg_prefix)/$(cfg_dir)
 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),\
 			sed $(foreach m,$(modules_dirs),\
 					-e "s#/usr/[^:]*lib/$(CFG_NAME)/$(m)\([:/\"]\)#$($(m)_target)\1#g") \
 					-e "s#/usr/[^:]*lib/$(CFG_NAME)/$(m)\([:/\"]\)#$($(m)_target)\1#g") \
 					-e "s#/usr/local/etc/$(CFG_NAME)/#$(cfg_target)#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; \
 					$(cfg_prefix)/$(cfg_dir)$(MAIN_NAME).cfg; \
 			fi; \
 			fi; \
 		fi
 		fi
-		@if [ -f etc/$(CFG_NAME)-basic.cfg ]; then \
+	@if [ -f etc/$(CFG_NAME)-basic.cfg ]; then \
 			sed $(foreach m,$(modules_dirs),\
 			sed $(foreach m,$(modules_dirs),\
 					-e "s#/usr/[^:]*lib/$(CFG_NAME)/$(m)\([:/\"]\)#$($(m)_target)\1#g") \
 					-e "s#/usr/[^:]*lib/$(CFG_NAME)/$(m)\([:/\"]\)#$($(m)_target)\1#g") \
 					-e "s#/usr/local/etc/$(CFG_NAME)/#$(cfg_target)#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; \
 					$(cfg_prefix)/$(cfg_dir)$(MAIN_NAME)-basic.cfg; \
 			fi; \
 			fi; \
 		fi
 		fi
-		@if [ -f etc/$(CFG_NAME)-oob.cfg ]; then \
+	@if [ -f etc/$(CFG_NAME)-oob.cfg ]; then \
 			sed $(foreach m,$(modules_dirs),\
 			sed $(foreach m,$(modules_dirs),\
 					-e "s#/usr/[^:]*lib/$(CFG_NAME)/$(m)\([:/\"]\)#$($(m)_target)\1#g") \
 					-e "s#/usr/[^:]*lib/$(CFG_NAME)/$(m)\([:/\"]\)#$($(m)_target)\1#g") \
 					-e "s#/usr/local/etc/$(CFG_NAME)/#$(cfg_target)#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; \
 					$(cfg_prefix)/$(cfg_dir)$(MAIN_NAME)-advanced.cfg; \
 			fi; \
 			fi; \
 		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 [ -n "$$r" ]; then \
 				if [ -f "$$r" ]; then \
 				if [ -f "$$r" ]; then \
 					n=`basename "$$r"` ; \
 					n=`basename "$$r"` ; \
@@ -818,14 +818,15 @@ install-cfg: $(cfg_prefix)/$(cfg_dir)
 					fi ; \
 					fi ; \
 				fi ; \
 				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-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)
 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
 # - extra used modules, with no extra dependency
 mod_list_extra=avp auth_diameter call_control cnxcc dmq domainpolicy msrp pdb \
 mod_list_extra=avp auth_diameter call_control cnxcc dmq domainpolicy msrp pdb \
 			     qos sca seas sms sst timer tmrec uac_redirect xhttp \
 			     qos sca seas sms sst timer tmrec uac_redirect xhttp \
-				 xhttp_rpc xprint
+				 xhttp_rpc xprint jsonrpc-s nosip
 
 
 # - common modules depending on database
 # - common modules depending on database
 mod_list_db=acc alias_db auth_db avpops cfg_db db_text db_flatstore \
 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=
 LIBS=
 
 
 SERLIBPATH=..
 SERLIBPATH=..
-SER_LIBS=$(SERLIBPATH)/srutils/srutils
+SER_LIBS=
 
 
 include ../../Makefile.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;
 	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 */
 /*! \brief Unscape all printable ASCII characters */
 int unescape_user(str *sin, str *sout)
 int unescape_user(str *sin, str *sout)
 {
 {

+ 0 - 10
lib/kcore/strcommon.h

@@ -29,8 +29,6 @@
 #define _STRCOMMON_H_
 #define _STRCOMMON_H_
 
 
 #include "../../str.h"
 #include "../../str.h"
-#include "../../md5.h"
-#include "../srutils/sha256.h"
 
 
 /*
 /*
  * add backslashes to special characters
  * 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);
 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 escape_user(str *sin, str *sout);
 
 
 int unescape_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]>
    <[email protected]>
 
 
-   Copyright © 2012 asipto.com
+Edited by
+
+Muhammad Shahzad Shafi
+
+   <[email protected]>
+
+   Copyright © 2012 asipto.com
      __________________________________________________________________
      __________________________________________________________________
 
 
    Table of Contents
    Table of Contents
@@ -26,6 +32,9 @@ Daniel-Constantin Mierla
         3. Parameters
         3. Parameters
 
 
               3.1. alias_subdomains (string)
               3.1. alias_subdomains (string)
+              3.2. network_io_intercept (int)
+              3.3. msg_min_len (int)
+              3.4. msg_avp (string)
 
 
         4. Functions
         4. Functions
 
 
@@ -33,6 +42,7 @@ Daniel-Constantin Mierla
               4.2. send([ host [ :port ] ])
               4.2. send([ host [ :port ] ])
               4.3. send_tcp([ host [ :port ] ])
               4.3. send_tcp([ host [ :port ] ])
               4.4. send_data(uri, data)
               4.4. send_data(uri, data)
+              4.5. is_incoming()
 
 
         5. RPC Commands
         5. RPC Commands
 
 
@@ -41,12 +51,24 @@ Daniel-Constantin Mierla
               5.3. corex.shm_status
               5.3. corex.shm_status
               5.4. corex.shm_summary
               5.4. corex.shm_summary
 
 
+        6. Event Routes
+
+              6.1. event_route[network:msg]
+
+        7. Examples of Usage
+
    List of Examples
    List of Examples
 
 
    1.1. Set alias_subdomains parameter
    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
 Chapter 1. Admin Guide
 
 
@@ -61,6 +83,9 @@ Chapter 1. Admin Guide
    3. Parameters
    3. Parameters
 
 
         3.1. alias_subdomains (string)
         3.1. alias_subdomains (string)
+        3.2. network_io_intercept (int)
+        3.3. msg_min_len (int)
+        3.4. msg_avp (string)
 
 
    4. Functions
    4. Functions
 
 
@@ -68,6 +93,7 @@ Chapter 1. Admin Guide
         4.2. send([ host [ :port ] ])
         4.2. send([ host [ :port ] ])
         4.3. send_tcp([ host [ :port ] ])
         4.3. send_tcp([ host [ :port ] ])
         4.4. send_data(uri, data)
         4.4. send_data(uri, data)
+        4.5. is_incoming()
 
 
    5. RPC Commands
    5. RPC Commands
 
 
@@ -76,6 +102,12 @@ Chapter 1. Admin Guide
         5.3. corex.shm_status
         5.3. corex.shm_status
         5.4. corex.shm_summary
         5.4. corex.shm_summary
 
 
+   6. Event Routes
+
+        6.1. event_route[network:msg]
+
+   7. Examples of Usage
+
 1. Overview
 1. Overview
 
 
    This module provides reimplementation of a few very old functions that
    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
    Contributions to this module must be done under the BSD license, to
    follow the requirements of the core contributions.
    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. Dependencies
 
 
    2.1. Kamailio Modules
    2.1. Kamailio Modules
@@ -108,15 +149,18 @@ Chapter 1. Admin Guide
 3. Parameters
 3. Parameters
 
 
    3.1. alias_subdomains (string)
    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)
 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:
    condition. It can be set many times. Its full format is:
    'proto:domain:port', allowing to set restrictions on protocol and port
    'proto:domain:port', allowing to set restrictions on protocol and port
    as well. Protocol and port are optional.
    as well. Protocol and port are optional.
 
 
-   Default value is "NULL".
+   Default value is “NULL�.
 
 
    Example 1.1. Set alias_subdomains parameter
    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")
 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. Functions
 
 
    4.1. append_branch([ uri, [ q ] ])
    4.1. append_branch([ uri, [ q ] ])
    4.2. send([ host [ :port ] ])
    4.2. send([ host [ :port ] ])
    4.3. send_tcp([ host [ :port ] ])
    4.3. send_tcp([ host [ :port ] ])
    4.4. send_data(uri, data)
    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
    Append a new branch to the destination set, useful to build the set of
    destination addresses for parallel forking or redirect replies.
    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.
    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();
     append_branch("$avp(uri)", "0.5");
     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
    Send the original SIP message to a specific destination in stateless
    mode. No changes are applied to received message, no Via header is
    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.
    This function can be used from REQUEST_ROUTE or FAILURE_ROUTE.
 
 
-   Example 1.3. send usage
+   Example 1.6. send usage
 ...
 ...
         send();
         send();
         send("10.20.15.10");
         send("10.20.15.10");
@@ -175,12 +261,12 @@ modparam("corex", "alias_subdomains", "udp:sip-router.org:5060")
         send("$var(res)");
         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
    This function is identical to send() described above, except that it
    sends the SIP message using the TCP protocol instead of UDP.
    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
    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
    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.
    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");
         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. RPC Commands
 
 
    5.1. corex.list_sockets
    5.1. corex.list_sockets
@@ -200,30 +308,169 @@ modparam("corex", "alias_subdomains", "udp:sip-router.org:5060")
    5.3. corex.shm_status
    5.3. corex.shm_status
    5.4. corex.shm_summary
    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.
    Print the list of sockets the application is listening on.
 
 
    Example:
    Example:
                 kamcmd corex.list_sockets
                 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.
    Print the list of hostname aliases used to match the myself condition.
 
 
    Example:
    Example:
                 kamcmd corex.list_aliases
                 kamcmd corex.list_aliases
 
 
-5.3. corex.shm_status
+5.3.  corex.shm_status
 
 
    Trigger shm status dump to syslog.
    Trigger shm status dump to syslog.
 
 
    Example:
    Example:
                 kamcmd corex.shm_status
                 kamcmd corex.shm_status
 
 
-5.4. corex.shm_summary
+5.4.  corex.shm_summary
 
 
    Trigger shm summary dump to syslog.
    Trigger shm summary dump to syslog.
 
 
    Example:
    Example:
                 kamcmd corex.shm_summary
                 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_lib.h"
 #include "corex_rpc.h"
 #include "corex_rpc.h"
 #include "corex_var.h"
 #include "corex_var.h"
+#include "corex_nio.h"
 
 
 MODULE_VERSION
 MODULE_VERSION
 
 
+static int nio_intercept = 0;
 static int w_append_branch(sip_msg_t *msg, char *su, char *sq);
 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(sip_msg_t *msg, char *su, char *sq);
 static int w_send_tcp(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 },
 			0, REQUEST_ROUTE | FAILURE_ROUTE },
 	{"send_data", (cmd_function)w_send_data, 2, fixup_spve_spve,
 	{"send_data", (cmd_function)w_send_data, 2, fixup_spve_spve,
 			0, ANY_ROUTE },
 			0, ANY_ROUTE },
-
+	{"is_incoming",    (cmd_function)nio_check_incoming, 0, 0,
+    	    0, ANY_ROUTE },
 
 
 	{0, 0, 0, 0, 0, 0}
 	{0, 0, 0, 0, 0, 0}
 };
 };
 
 
 static param_export_t params[]={
 static param_export_t params[]={
-	{"alias_subdomains",  PARAM_STRING|USE_FUNC_PARAM,
+	{"alias_subdomains",		STR_PARAM|USE_FUNC_PARAM,
 								(void*)corex_alias_subdomains_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}
 	{0, 0, 0}
 };
 };
 
 
@@ -114,6 +121,12 @@ static int mod_init(void)
 		return -1;
 		return -1;
 	}
 	}
 
 
+	if((nio_intercept > 0) && (nio_intercept_init() < 0))
+	{
+		LM_ERR("failed to register network io intercept callback\n");
+		return -1;
+	}
+
 	return 0;
 	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[] = {
 rpc_export_t corex_rpc_cmds[] = {
 	{"corex.list_sockets", corex_rpc_list_sockets,
 	{"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.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.shm_status", corex_rpc_shm_status,
 		corex_rpc_shm_status_doc, 0},
 		corex_rpc_shm_status_doc, 0},
 	{"corex.shm_summary", corex_rpc_shm_summary,
 	{"corex.shm_summary", corex_rpc_shm_summary,

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

@@ -23,6 +23,11 @@
 		<surname>Mierla</surname>
 		<surname>Mierla</surname>
 		<email>[email protected]</email>
 		<email>[email protected]</email>
 	    </editor>
 	    </editor>
+	    <editor>
+		<firstname>Muhammad Shahzad</firstname>
+		<surname>Shafi</surname>
+		<email>[email protected]</email>
+	    </editor>
 	</authorgroup>
 	</authorgroup>
 	<copyright>
 	<copyright>
 	    <year>2012</year>
 	    <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
 		Contributions to this module must be done under the BSD license, to
 		follow the requirements of the core contributions.
 		follow the requirements of the core contributions.
 	</para>
 	</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>
 
 
 	<section>
 	<section>
@@ -87,6 +96,69 @@ modparam("corex", "alias_subdomains", "udp:sip-router.org:5060")
 </programlisting>
 </programlisting>
 	    </example>
 	    </example>
 	</section>
 	</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>
 
 
 	<section>
 	<section>
@@ -200,6 +272,36 @@ modparam("corex", "alias_subdomains", "udp:sip-router.org:5060")
 </programlisting>
 </programlisting>
 		</example>
 		</example>
 	</section>
 	</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>
 	</section>
 
 
@@ -263,5 +365,160 @@ modparam("corex", "alias_subdomains", "udp:sip-router.org:5060")
 		</programlisting>
 		</programlisting>
     </section>
     </section>
     </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>
 </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 the dialog*/
-			link_dlg(dlg, 0);
+			link_dlg(dlg, 0, 0);
 
 
 			dlg->h_id = VAL_INT(values+1);
 			dlg->h_id = VAL_INT(values+1);
 			next_id = d_table->entries[dlg->h_entry].next_id;
 			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 "../../lib/kcore/statistics.h"
 #include "../../action.h"
 #include "../../action.h"
 #include "../../script_cb.h"
 #include "../../script_cb.h"
+#include "../../pt.h"
 #include "../../lib/kcore/faked_msg.h"
 #include "../../lib/kcore/faked_msg.h"
 #include "../../parser/parse_from.h"
 #include "../../parser/parse_from.h"
 #include "../../parser/parse_cseq.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 ttag;
     str req_uri;
     str req_uri;
     unsigned int dir;
     unsigned int dir;
+    int mlock;
 
 
 	dlg = dlg_get_ctx_dialog();
 	dlg = dlg_get_ctx_dialog();
     if(dlg != NULL) {
     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);
     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",
 				LM_DBG("Callid '%.*s' found, must be a spiraled request\n",
 					callid.len, callid.s);
 					callid.len, callid.s);
 				spiral_detected = 1;
 				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)
 				if (run_initial_cbs)
 					run_dlg_callbacks( DLGCB_SPIRALED, dlg, req, NULL,
 					run_dlg_callbacks( DLGCB_SPIRALED, dlg, req, NULL,
 							DLG_DIR_DOWNSTREAM, 0);
 							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);
 			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*/,
                          &ftag/*from_tag*/,
                          &req_uri /*r-uri*/ );
                          &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");
 		LM_ERR("failed to create new dialog\n");
 		return -1;
 		return -1;
 	}
 	}
 
 
 	/* save caller's tag, cseq, contact and record route*/
 	/* save caller's tag, cseq, contact and record route*/
 	if (populate_leg_info(dlg, req, t, DLG_CALLER_LEG,
 	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");
 		LM_ERR("could not add further info to the dialog\n");
 		shm_free(dlg);
 		shm_free(dlg);
 		return -1;
 		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: */
 	/* Populate initial varlist: */
 	dlg->vars = get_local_varlist_pointer(req, 1);
 	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);
 	dlg->lifetime = get_dlg_timeout(req);
 	s.s   = _dlg_ctx.to_route_name;
 	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;
 		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);
     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_entry = dlg->h_entry;
     _dlg_ctx.iuid.h_id = dlg->h_id;
     _dlg_ctx.iuid.h_id = dlg->h_id;
     set_current_dialog(req, dlg);
     set_current_dialog(req, dlg);
-	dlg_release(dlg);
 
 
 	return 0;
 	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 ftag from tag
  * \param ttag to tag
  * \param ttag to tag
  * \param dir direction
  * \param dir direction
+ * \param mode let hash table slot locked if dialog is not found
  * \return dialog structure on success, NULL on failure
  * \return dialog structure on success, NULL on failure
  */
  */
 static inline struct dlg_cell* internal_get_dlg(unsigned int h_entry,
 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_cell *dlg;
 	struct dlg_entry *d_entry;
 	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);
 	LM_DBG("no dialog callid='%.*s' found\n", callid->len, callid->s);
 	return 0;
 	return 0;
 }
 }
@@ -743,7 +745,7 @@ struct dlg_cell* get_dlg( str *callid, str *ftag, str *ttag, unsigned int *dir)
 	unsigned int he;
 	unsigned int he;
 
 
 	he = core_hash(callid, 0, d_table->size);
 	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) {
 	if (dlg == 0) {
 		LM_DBG("no dialog callid='%.*s' found\n", callid->len, callid->s);
 		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
  * \brief Link a dialog structure
  * \param dlg dialog
  * \param dlg dialog
  * \param n extra increments for the reference counter
  * \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;
 	struct dlg_entry *d_entry;
 
 
 	d_entry = &(d_table->entries[dlg->h_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 */
 	/* keep id 0 for special cases */
 	dlg->h_id = 1 + d_entry->next_id++;
 	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);
 	ref_dlg_unsafe(dlg, 1+n);
 
 
-	dlg_unlock( d_table, d_entry);
+	if(unlikely(mode==0)) dlg_unlock( d_table, d_entry);
 	return;
 	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);
 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
  * \brief Link a dialog structure
  * \param dlg dialog
  * \param dlg dialog
  * \param n extra increments for the reference counter
  * \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();
 		dlg = dlg_get_ctx_dialog();
 		if(dlg!=NULL) {
 		if(dlg!=NULL) {
 			if(_dlg_ctx.t==0 && dlg->state==DLG_STATE_UNCONFIRMED) {
 			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);
 			dlg_release(dlg);
 		}
 		}
 	}
 	}

+ 1 - 0
modules/dialog/dlg_var.h

@@ -39,6 +39,7 @@ typedef struct _dlg_ctx {
 	int timeout;
 	int timeout;
 	dlg_cell_t *dlg1;
 	dlg_cell_t *dlg1;
 	dlg_iuid_t iuid;
 	dlg_iuid_t iuid;
+	int cpid;
 	int set;
 	int set;
 	unsigned int dir;
 	unsigned int dir;
 	int t;
 	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);
         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 {
     } else {
         LM_DBG("Update AAR session for this dialog in mode %s\n", direction);
         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");
     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.
             //is this a stream to add to AAR.
             if (req_sdp_stream->is_rtp) {
             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++;
             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");
 			LM_ERR("parsing Route-URI parameters\n");
 			return 0;
 			return 0;
 		}
 		}
+		/* Not interested in param body - just the hooks */
+		free_params(params);
 
 
 		if (hooks.uri.ob)
 		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");
 			LM_ERR("parsing Contact-URI parameters\n");
 			return 0;
 			return 0;
 		}
 		}
+		/* Not interested in param body - just the hooks */
+		free_params(params);
+
 		if (hooks.contact.ob)
 		if (hooks.contact.ob)
 		{
 		{
 			LM_DBG("found ;ob parameter on Contact-URI - outbound"
 			LM_DBG("found ;ob parameter on Contact-URI - outbound"

+ 1 - 0
modules/pv/Makefile

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

+ 1 - 0
modules/pv/pv_trans.c

@@ -47,6 +47,7 @@
 #include "../../parser/parse_nameaddr.h"
 #include "../../parser/parse_nameaddr.h"
 
 
 #include "../../lib/kcore/strcommon.h"
 #include "../../lib/kcore/strcommon.h"
+#include "../../lib/srutils/shautils.h"
 #include "pv_trans.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.free_result(reg_db_con, db_res);
 	reg_dbf.close(reg_db_con);
 	reg_dbf.close(reg_db_con);
 
 
-done:
 	return 0;
 	return 0;
 
 
 error:
 error:
@@ -1586,9 +1585,6 @@ static void rpc_uac_reg_update_flag(rpc_t* rpc, void* ctx, int mode, int fval)
 {
 {
 	int i;
 	int i;
 	reg_item_t *reg = NULL;
 	reg_item_t *reg = NULL;
-	void* th;
-	str none = {"none", 4};
-	time_t tn;
 	str attr = {0};
 	str attr = {0};
 	str val = {0};
 	str val = {0};
 	str *rval;
 	str *rval;
@@ -1611,8 +1607,6 @@ static void rpc_uac_reg_update_flag(rpc_t* rpc, void* ctx, int mode, int fval)
 		return;
 		return;
 	}
 	}
 
 
-	tn = time(NULL);
-
 	for(i=0; i<_reg_htable->htsize; i++)
 	for(i=0; i<_reg_htable->htsize; i++)
 	{
 	{
 		lock_get(&_reg_htable->entries[i].lock);
 		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:
  error:
-	if (r) pkg_free(r);
+	if (r) free_rr(&r);
 	free_rr(head); /* Free any contacts created so far */
 	free_rr(head); /* Free any contacts created so far */
 	return -1;
 	return -1;