Ver código fonte

Merge remote branch 'origin/master' into tmp/hpw_curl_improvements

Hugh Waite 9 anos atrás
pai
commit
5ff788247f
100 arquivos alterados com 5618 adições e 752 exclusões
  1. 2 2
      Makefile.defs
  2. 2 2
      Makefile.groups
  3. 15 1
      dprint.c
  4. 2 0
      dprint.h
  5. 39 0
      events.c
  6. 6 0
      events.h
  7. 20 2
      forward.h
  8. 5 0
      ip_addr.h
  9. 15 1
      lib/srdb1/schema/uacreg.xml
  10. 2 0
      modules/auth_radius/authrad_mod.c
  11. 1 0
      modules/auth_radius/authrad_mod.h
  12. 5 0
      modules/auth_radius/doc/auth_radius.xml
  13. 17 0
      modules/auth_radius/doc/auth_radius_admin.xml
  14. 1 1
      modules/auth_radius/sterman.c
  15. 9 1
      modules/carrierroute/cr_fifo.c
  16. 2 0
      modules/cdp/routing.c
  17. 17 0
      modules/cfgt/Makefile
  18. 159 0
      modules/cfgt/README
  19. 45 0
      modules/cfgt/cfgt.c
  20. 40 0
      modules/cfgt/cfgt.h
  21. 797 0
      modules/cfgt/cfgt_int.c
  22. 67 0
      modules/cfgt/cfgt_int.h
  23. 389 0
      modules/cfgt/cfgt_json.c
  24. 38 0
      modules/cfgt/cfgt_json.h
  25. 112 0
      modules/cfgt/cfgt_mod.c
  26. 29 0
      modules/cfgt/cfgt_mod.h
  27. 4 0
      modules/cfgt/doc/Makefile
  28. 36 0
      modules/cfgt/doc/cfgt.xml
  29. 183 0
      modules/cfgt/doc/cfgt_admin.xml
  30. 0 24
      modules/cnxcc/cnxcc.c
  31. 24 3
      modules/cnxcc/cnxcc.h
  32. 37 0
      modules/crypto/Makefile
  33. 106 0
      modules/crypto/README
  34. 383 0
      modules/crypto/crypto_mod.c
  35. 4 0
      modules/crypto/doc/Makefile
  36. 37 0
      modules/crypto/doc/crypto.xml
  37. 107 0
      modules/crypto/doc/crypto_admin.xml
  38. 4 0
      modules/curl/TODO.txt
  39. 28 1
      modules/curl/curl_api.h
  40. 7 3
      modules/db_mysql/km_dbase.c
  41. 129 0
      modules/db_text/dbt_base.c
  42. 2 2
      modules/db_text/dbt_lib.c
  43. 1 1
      modules/db_text/dbt_raw_util.c
  44. 2 1
      modules/db_text/dbtext.c
  45. 6 0
      modules/db_text/dbtext.h
  46. 1 0
      modules/debugger/Makefile
  47. 31 14
      modules/debugger/README
  48. 148 12
      modules/debugger/debugger_api.c
  49. 1 0
      modules/debugger/debugger_api.h
  50. 472 154
      modules/debugger/debugger_mod.c
  51. 181 0
      modules/debugger/doc/debugger_admin.xml
  52. 1 0
      modules/dispatcher/README
  53. 13 0
      modules/dispatcher/dispatch.c
  54. 1 0
      modules/dispatcher/doc/dispatcher.cfg
  55. 7 4
      modules/dmq_usrloc/usrloc_sync.c
  56. 1 1
      modules/htable/htable.c
  57. 3 1
      modules/ims_auth/cxdx_mar.c
  58. 37 99
      modules/ims_charging/dialog.c
  59. 1 3
      modules/ims_charging/dialog.h
  60. 69 5
      modules/ims_charging/ims_ro.c
  61. 6 1
      modules/ims_charging/ims_ro.h
  62. 101 24
      modules/ims_charging/mod.c
  63. 1 1
      modules/ims_charging/ro_session_hash.c
  64. 8 1
      modules/ims_charging/ro_session_hash.h
  65. 21 0
      modules/ims_charging/ro_timer.c
  66. 1 1
      modules/ims_qos/rx_str.c
  67. 50 68
      modules/ims_registrar_scscf/registrar_notify.c
  68. 6 4
      modules/ims_registrar_scscf/registrar_notify.h
  69. 10 3
      modules/ims_usrloc_scscf/udomain.c
  70. 1 1
      modules/ims_usrloc_scscf/udomain.h
  71. 1 1
      modules/ims_usrloc_scscf/usrloc.h
  72. 3 3
      modules/matrix/README
  73. 6 6
      modules/matrix/doc/matrix_admin.xml
  74. 34 38
      modules/nat_traversal/nat_traversal.c
  75. 1 1
      modules/ndb_mongodb/README
  76. 1 1
      modules/ndb_mongodb/doc/ndb_mongodb_admin.xml
  77. 36 21
      modules/permissions/README
  78. 20 0
      modules/permissions/doc/permissions_admin.xml
  79. 3 0
      modules/permissions/hash.c
  80. 0 2
      modules/permissions/hash.h
  81. 3 0
      modules/permissions/permissions.c
  82. 17 5
      modules/presence/notify.c
  83. 1 1
      modules/presence/publish.c
  84. 1 0
      modules/presence_dialoginfo/add_events.c
  85. 36 2
      modules/presence_dialoginfo/notify_body.c
  86. 33 10
      modules/presence_xml/notify_body.c
  87. 6 1
      modules/registrar/reg_mod.c
  88. 122 3
      modules/rtpengine/doc/rtpengine_admin.xml
  89. 250 48
      modules/rtpengine/rtpengine.c
  90. 0 1
      modules/rtpengine/rtpengine.h
  91. 544 0
      modules/rtpengine/rtpengine_hash.c
  92. 40 0
      modules/rtpengine/rtpengine_hash.h
  93. 27 7
      modules/siptrace/README
  94. 24 0
      modules/siptrace/doc/siptrace_admin.xml
  95. 225 128
      modules/siptrace/siptrace.c
  96. 1 1
      modules/smsops/doc/smsops_admin.xml
  97. 19 19
      modules/statsd/lib_statsd.c
  98. 9 11
      modules/statsd/lib_statsd.h
  99. 45 0
      modules/tcpops/tcpops.c
  100. 2 0
      modules/tcpops/tcpops.h

+ 2 - 2
Makefile.defs

@@ -97,7 +97,7 @@ INSTALL_FLAVOUR=$(FLAVOUR)
 VERSION = 4
 PATCHLEVEL = 4
 SUBLEVEL =  0
-EXTRAVERSION = -dev7
+EXTRAVERSION = -dev8
 
 # memory manager switcher
 # 0 - f_malloc (fast malloc)
@@ -1785,7 +1785,7 @@ ifeq ($(CC_NAME), suncc)
 endif
 	OLD_SOLARIS= $(shell echo "$(OSREL)" | \
 				sed -e 's/^5\.[0-6][^0-9]*$$/yes/' )
-	LIBS+= -L$(LOCALBASE)/lib -lxnet -lnsl 
+	LIBS+= -L$(LOCALBASE)/lib -lxnet -lsocket -lnsl 
 ifeq	($(OLD_SOLARIS), yes)
 		LIBS+=-lposix4
 else

+ 2 - 2
Makefile.groups

@@ -15,7 +15,7 @@ mod_list_basic=async auth benchmark blst cfg_rpc cfgutils corex counters \
 				   nat_traversal nathelper path pike pv ratelimit rr rtimer \
 				   rtpproxy sanity sdpops siputils sl statistics textops \
 				   textopsx tm tmx topoh xlog rtpengine stun sipt tcpops \
-				   auth_xkeys smsops tsilo
+				   auth_xkeys smsops tsilo cfgt
 
 # - extra used modules, with no extra dependency
 mod_list_extra=avp auth_diameter call_control dmq domainpolicy msrp pdb \
@@ -90,7 +90,7 @@ mod_list_purple=purple
 mod_list_memcached=memcached
 
 # - modules depending on openssl library
-mod_list_tlsdeps=auth_identity tls
+mod_list_tlsdeps=auth_identity crypto tls
 
 # - modules depending on openssl library
 mod_list_outbound=outbound

+ 15 - 1
dprint.c

@@ -90,13 +90,27 @@ int str2facility(char *s)
 {
 	int i;
 
-	for( i=0; str_fac[i] ; i++) {
+	for (i=0; str_fac[i]; i++) {
 		if (!strcasecmp(s,str_fac[i]))
 			return int_fac[i];
 	}
 	return -1;
 }
 
+char* facility2str(int fl, int *len)
+{
+	int i;
+
+	for (i=0; str_fac[i]; i++) {
+		if (fl == int_fac[i]) {
+			*len = strlen(str_fac[i]);
+			return str_fac[i];
+		}
+	}
+
+	return NULL;
+}
+
 /* fixup function for log_facility cfg parameter */
 int log_facility_fixup(void *handle, str *gname, str *name, void **val)
 {

+ 2 - 0
dprint.h

@@ -160,6 +160,8 @@ extern volatile int dprint_crit;
 #endif
 
 int str2facility(char *s);
+char* facility2str(int fl, int *len);
+
 int log_facility_fixup(void *handle, str *gname, str *name, void **val);
 
 void dprint_color(int level);

+ 39 - 0
events.c

@@ -165,6 +165,21 @@ int sr_event_register_cb(int type, sr_event_cb_f f)
 					_sr_events_list.rcv_nosip = f;
 				else return -1;
 			break;
+		case SREV_TCP_CLOSED:
+				if(_sr_events_list.tcp_closed==0)
+					_sr_events_list.tcp_closed = f;
+				else return -1;
+			break;
+		case SREV_NET_DATA_RECV:
+				if(_sr_events_list.net_data_recv==0)
+					_sr_events_list.net_data_recv = f;
+				else return -1;
+			break;
+		case SREV_NET_DATA_SEND:
+				if(_sr_events_list.net_data_send==0)
+					_sr_events_list.net_data_send = f;
+				else return -1;
+			break;
 		default:
 			return -1;
 	}
@@ -284,6 +299,24 @@ int sr_event_exec(int type, void *data)
 					ret = _sr_events_list.rcv_nosip(data);
 					return ret;
 				} else return 1;
+		case SREV_TCP_CLOSED:
+				if(unlikely(_sr_events_list.tcp_closed!=0))
+				{
+					ret = _sr_events_list.tcp_closed(data);
+					return ret;
+				} else return 1;
+		case SREV_NET_DATA_RECV:
+				if(unlikely(_sr_events_list.net_data_recv!=0))
+				{
+					ret = _sr_events_list.net_data_recv(data);
+					return ret;
+				} else return 1;
+		case SREV_NET_DATA_SEND:
+				if(unlikely(_sr_events_list.net_data_send!=0))
+				{
+					ret = _sr_events_list.net_data_send(data);
+					return ret;
+				} else return 1;
 		default:
 			return -1;
 	}
@@ -319,6 +352,12 @@ int sr_event_enabled(int type)
 				return (_sr_events_list.stun_in!=0)?1:0;
 		case SREV_RCV_NOSIP:
 				return (_sr_events_list.rcv_nosip!=0)?1:0;
+		case SREV_TCP_CLOSED:
+				return (_sr_events_list.tcp_closed!=0)?1:0;
+		case SREV_NET_DATA_RECV:
+				return (_sr_events_list.net_data_recv!=0)?1:0;
+		case SREV_NET_DATA_SEND:
+				return (_sr_events_list.net_data_send!=0)?1:0;
 	}
 	return 0;
 }

+ 6 - 0
events.h

@@ -34,6 +34,9 @@
 #define SREV_TCP_WS_FRAME_IN		10
 #define SREV_TCP_WS_FRAME_OUT		11
 #define SREV_STUN_IN			12
+#define SREV_TCP_CLOSED			13
+#define SREV_NET_DATA_RECV		14
+#define SREV_NET_DATA_SEND		15
 
 #define SREV_CB_LIST_SIZE	3
 
@@ -52,6 +55,9 @@ typedef struct sr_event_cb {
 	sr_event_cb_f tcp_ws_frame_out;
 	sr_event_cb_f stun_in;
 	sr_event_cb_f rcv_nosip;
+	sr_event_cb_f tcp_closed;
+	sr_event_cb_f net_data_recv;
+	sr_event_cb_f net_data_send;
 } sr_event_cb_t;
 
 void sr_event_cb_init(void);

+ 20 - 2
forward.h

@@ -56,6 +56,7 @@ enum ss_mismatch {
 	SS_MISMATCH_MCAST  /* mcast forced send socket */
 };
 
+
 struct socket_info* get_send_socket2(struct socket_info* force_send_socket,
 									union sockaddr_union* su, int proto,
 									enum ss_mismatch* mismatch);
@@ -97,6 +98,7 @@ void forward_set_send_info(int v);
 
 int is_check_self_func_list_set(void);
 
+#define msg_send(_dst, _buf, _len) msg_send_buffer((_dst), (_buf), (_len), 0)
 
 /* params:
  * dst = struct dest_info containing:
@@ -109,12 +111,17 @@ int is_check_self_func_list_set(void);
  *        (useful for sending replies on  the same connection as the request
  *         that generated them; use 0 if you don't want this)
  * buf, len = buffer
+ * flags = control internal behavior
+ *    * 1 - skip executing event SREV_NET_DATA_OUT
  * returns: 0 if ok, -1 on error*/
 
-static inline int msg_send(struct dest_info* dst, char* buf, int len)
+static inline int msg_send_buffer(struct dest_info* dst, char* buf, int len,
+		int flags)
 {
 	struct dest_info new_dst;
 	str outb;
+	sr_net_info_t netinfo;
+
 #ifdef USE_TCP 
 	int port;
 	struct ip_addr ip;
@@ -127,7 +134,9 @@ static inline int msg_send(struct dest_info* dst, char* buf, int len)
 	
 	outb.s = buf;
 	outb.len = len;
-	sr_event_exec(SREV_NET_DATA_OUT, (void*)&outb);
+	if(!(flags&1)) {
+		sr_event_exec(SREV_NET_DATA_OUT, (void*)&outb);
+	}
 
 	if(outb.s==NULL) {
 		LM_ERR("failed to update outgoing buffer\n");
@@ -265,6 +274,15 @@ static inline int msg_send(struct dest_info* dst, char* buf, int len)
 	}
 	ret = 0;
 done:
+
+	if(!(flags&1)) {
+		memset(&netinfo, 0, sizeof(sr_net_info_t));
+		netinfo.data.s = outb.s;
+		netinfo.data.len = outb.len;
+		netinfo.dst = dst;
+		sr_event_exec(SREV_NET_DATA_SEND, (void*)&netinfo);
+	}
+
 	if(outb.s != buf)
 		pkg_free(outb.s);
 	return ret;

+ 5 - 0
ip_addr.h

@@ -136,6 +136,11 @@ struct receive_info{
 	/* no need for dst_su yet */
 };
 
+typedef struct sr_net_info {
+	str data;
+	struct dest_info* dst;
+	struct receive_info* rcv;
+} sr_net_info_t;
 
 /* send flags */
 #define SND_F_FORCE_CON_REUSE	1 /* reuse an existing connection or fail */

+ 15 - 1
lib/srdb1/schema/uacreg.xml

@@ -9,7 +9,7 @@
 
 <table id="uacreg" xmlns:db="http://docbook.org/ns/docbook">
     <name>uacreg</name>
-    <version>1</version>
+    <version>2</version>
     <type db="mysql">&MYSQL_TABLE_TYPE;</type>
     <description>
         <db:para>This table is used by theuac module to load user details for remote server registration: &KAMAILIO_MOD_DOC;uac.html
@@ -105,6 +105,20 @@
         <description>Expiration time (in seconds, 0 means disabled)</description>
     </column>
 
+    <column id="flags">
+        <name>flags</name>
+        <type>int</type>
+        <default>0</default>
+        <description>Flags to control the behavior</description>
+    </column>
+
+    <column id="reg_delay">
+        <name>reg_delay</name>
+        <type>int</type>
+        <default>0</default>
+        <description>Initial registration delay</description>
+    </column>
+
 	<index>
         <name>l_uuid_idx</name>
         <colref linkend="l_uuid"/>

+ 2 - 0
modules/auth_radius/authrad_mod.c

@@ -66,6 +66,7 @@ static int service_type = -1;
 
 int use_ruri_flag = -1;
 int ar_radius_avps_mode = 0;
+int append_realm_to_username = 1;
 
 static char *auth_extra_str = 0;
 struct extra_attr *auth_extra = 0;
@@ -95,6 +96,7 @@ static param_export_t params[] = {
 	{"use_ruri_flag",    INT_PARAM, &use_ruri_flag	},
 	{"auth_extra",       PARAM_STRING, &auth_extra_str	},
 	{"radius_avps_mode",	 INT_PARAM, &ar_radius_avps_mode	},
+	{"append_realm_to_username", INT_PARAM, &append_realm_to_username       },
 	{0, 0, 0}
 };
 

+ 1 - 0
modules/auth_radius/authrad_mod.h

@@ -41,6 +41,7 @@ extern struct extra_attr *auth_extra;
 
 extern int use_ruri_flag;
 extern int ar_radius_avps_mode;
+extern int append_realm_to_username;
 
 extern auth_api_s_t auth_api;
 

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

@@ -38,6 +38,11 @@
 		<surname>Janak</surname>
 		<email>[email protected]</email>
 		</editor>
+		<editor>
+		<firstname>Phil</firstname>
+		<surname>Lavin</surname>
+		<email>[email protected]</email>
+		</editor>
 	</authorgroup>
 	<copyright>
 		<year>2002</year>

+ 17 - 0
modules/auth_radius/doc/auth_radius_admin.xml

@@ -206,6 +206,23 @@ modparam("auth_radius", "radius_avps_mode", 1)
 		</programlisting>
 		</example>
 	</section>
+	<section id="auth_radius.p.append_realm_to_username">
+		<title><varname>append_realm_to_username</varname> (integer)</title>
+		<para>
+			If set to 1, the username passed to the RADIUS server will have the
+			digest realm appended to it, if no domain is provided in the digest
+			username.
+		</para>
+		<para>
+			Default value is 1.
+		</para>
+		<example>
+		<title><varname>append_realm_to_username</varname> parameter usage</title>
+		<programlisting format="linespecific">
+modparam("auth_radius", "append_realm_to_username", 0)
+		</programlisting>
+		</example>
+	</section>
 	</section>
 
 	<section>

+ 1 - 1
modules/auth_radius/sterman.c

@@ -243,7 +243,7 @@ int radius_authorize_sterman(struct sip_msg* _msg, dig_cred_t* _cred, str* _meth
 	 * Add all the user digest parameters according to the qop defined.
 	 * Most devices tested only offer support for the simplest digest.
 	 */
-	if (_cred->username.domain.len) {
+	if (_cred->username.domain.len || !append_realm_to_username) {
 		if (!rc_avpair_add(rh, &send, attrs[A_USER_NAME].v, _cred->username.whole.s, _cred->username.whole.len, 0)) {
 			LM_ERR("unable to add User-Name attribute\n");
 			goto err;

+ 9 - 1
modules/carrierroute/cr_fifo.c

@@ -490,7 +490,15 @@ static int get_fifo_opts(str * buf, fifo_opt_t * opts, unsigned int opt_set[]) {
 		LM_DBG("token %.*s", opt_argv[i].len, opt_argv[i].s);
 		if (opt_argv[i].len >= 1) {
 			switch(*opt_argv[i].s) {
-					case '-': switch(opt_argv[i].s[1]) {
+					case '-': 
+						  /* -{OPTION}{PARAMETER} is not allowed */
+                                                  if (opt_argv[i].len != 2) {
+                                                        FIFO_ERR(E_WRONGOPT);
+                                                        LM_DBG("Unknown option: %.*s\n", opt_argv[i].len, opt_argv[i].s);
+                                                        return -1;
+                                                  }
+
+						  switch(opt_argv[i].s[1]) {
 							case OPT_DOMAIN_CHR:
 							op = OPT_DOMAIN;
 							used_opts |= O_DOMAIN;

+ 2 - 0
modules/cdp/routing.c

@@ -89,7 +89,9 @@ peer* get_first_connected_route(cdp_session_t* cdp_session, routing_entry *r, in
         /*try and find an already used peer for this session - sticky*/
         if ((cdp_session->sticky_peer_fqdn.len > 0) && cdp_session->sticky_peer_fqdn.s) {
             //we have an old sticky peer. let's make sure it's up and connected before we use it.
+            AAASessionsUnlock(cdp_session->hash); /*V1.1 - Don't attempt to hold two locks at same time */
             p = get_peer_by_fqdn(&cdp_session->sticky_peer_fqdn);
+            AAASessionsLock(cdp_session->hash); /*V1.1 - As we were...no call seems to pass cdp_session unlocked */  
             if (p && !p->disabled && (p->state == I_Open || p->state == R_Open) && peer_handles_application(p, app_id, vendor_id)) {
                 p->last_selected = time(NULL);
                 LM_DBG("Found a sticky peer [%.*s] for this session - re-using\n", p->fqdn.len, p->fqdn.s);

+ 17 - 0
modules/cfgt/Makefile

@@ -0,0 +1,17 @@
+#
+# cfgt module makefile
+#
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=cfgt.so
+LIBS=
+
+DEFS+=-DKAMAILIO_MOD_INTERFACE
+
+SERLIBPATH=../../lib
+SER_LIBS+=$(SERLIBPATH)/kcore/kcore
+SER_LIBS+=$(SERLIBPATH)/srutils/srutils
+
+include ../../Makefile.modules

+ 159 - 0
modules/cfgt/README

@@ -0,0 +1,159 @@
+cfgt Module
+
+Victor Seva
+
+   sipwise.com
+
+Edited by
+
+Victor Seva
+
+   <[email protected]>
+
+   Copyright © 2015 Victor Seva (sipwise.com)
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+        2. Dependencies
+
+              2.1. Kamailio Modules
+              2.2. External Libraries or Applications
+
+        3. Parameters
+
+              3.1. basedir (string)
+              3.2. mask (int)
+              3.3. callid_prefix (string)
+
+        4. Usage
+
+   List of Examples
+
+   1.1. Set cfgtrace parameter
+   1.2. Set mask parameter
+   1.3. Set callid_prefix parameter
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+   2. Dependencies
+
+        2.1. Kamailio Modules
+        2.2. External Libraries or Applications
+
+   3. Parameters
+
+        3.1. basedir (string)
+        3.2. mask (int)
+        3.3. callid_prefix (string)
+
+   4. Usage
+
+1. Overview
+
+   This module provides a report of the way Kamailio SIP Server Platform
+   configuration has been executed as part of a unit test for different
+   SIP scenarios.
+
+   In order to identify different scenarios a prefix string should be used
+   inside the Call-ID header.
+
+2. Dependencies
+
+   2.1. Kamailio Modules
+   2.2. External Libraries or Applications
+
+2.1. Kamailio Modules
+
+   The following modules must be loaded before this module:
+     * None.
+
+2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed before
+   running Kamailio with this module loaded:
+     * None.
+
+3. Parameters
+
+   3.1. basedir (string)
+   3.2. mask (int)
+   3.3. callid_prefix (string)
+
+3.1. basedir (string)
+
+   Control where the config reports should be stored. The dir must exist
+   and Kamailio SIP Server Platform must have permissions to write on it.
+
+   Default value is “/tmp”.
+
+   Example 1.1. Set cfgtrace parameter
+...
+modparam("cfgt", "basedir", "/var/run/kamailio/cfgtest")
+...
+
+3.2. mask (int)
+
+   mask - Control the type of vars it should display in the report:
+     * 1 - dump null values
+     * 2 - dump avp vars
+     * 4 - dump script vars
+     * 8 - dump xavp vars
+     * 16 - dump DP_OTHER vars
+     * 32 - dump ALL vars
+
+   Default value is “32” (ALL).
+
+   Example 1.2. Set mask parameter
+...
+# dump xavp(8) and avp(4) vars
+modparam("cfgt", "mask", 12)
+...
+
+3.3. callid_prefix (string)
+
+   Prefix used to identify test scenario messages. Last char of the string
+   will be used as delimiter for the scenario ID. With parameter set to
+   “NGCP%” and Call-ID “NGCP%123%456” the scenario identified will be
+   “123”.
+
+   Default value is “NGCP%” (using “%” as delimiter).
+
+   Example 1.3. Set callid_prefix parameter
+...
+# using '%' as delimiter
+modparam("cfgt", "callid_prefix", "TEST-ID%")
+...
+
+4. Usage
+
+   This module is used by the debugger module, so it must be loaded first.
+   To enable the generation of the reports, the debugger module must be
+   configured by setting the cfgtest parameter to "1".
+
+   Example of configuration:
+...
+#!ifdef WITH_DEBUG
+loadmodule "cfgt.so"
+loadmodule "debugger.so"
+#!endif
+...
+#!ifdef WITH_DEBUG
+# ----- cfgt params -----
+modparam("cfgt", "basedir", "/tmp/kamailio/cfgtest")
+modparam("cfgt", "callid_prefix", "TRACE-ID%")
+modparam("cfgt", "mask", 32)
+
+# ----- debugger params -----
+modparam("debugger", "cfgtrace", 1)
+modparam("debugger", "log_level_name", "exec")
+
+modparam("debugger", "cfgtest", 1)
+#!endif
+...

+ 45 - 0
modules/cfgt/cfgt.c

@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 Victor Seva (sipwise.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 "cfgt_mod.h"
+#include "cfgt.h"
+#include "cfgt_int.h"
+
+/*!
+ * \brief cfgt module API export bind function
+ * \param api cfgt API
+ * \return 0 on success, -1 on failure
+ */
+int bind_cfgt(cfgt_api_t* api)
+{
+	if (!api) {
+		LM_ERR("invalid parameter value\n");
+		return -1;
+	}
+	if (init_flag==0) {
+		LM_ERR("configuration error - trying to bind to cfgt module"
+				" before being initialized\n");
+		return -1;
+	}
+
+	api->cfgt_process_route   = cfgt_process_route;
+	return 0;
+}

+ 40 - 0
modules/cfgt/cfgt.h

@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 Victor Seva (sipwise.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 _CFGT_BIND_H
+#define _CFGT_BIND_H
+
+#include "../../sr_module.h"
+
+/* export not usable from scripts */
+#define NO_SCRIPT	-1
+
+typedef int (*cfgt_process_route_f)(struct sip_msg *msg, struct action *a);
+
+typedef struct cfgt_api {
+	cfgt_process_route_f cfgt_process_route;
+} cfgt_api_t;
+
+/*! cfgt API export bind function */
+typedef int (*bind_cfgt_t)(cfgt_api_t* api);
+
+#endif

+ 797 - 0
modules/cfgt/cfgt_int.c

@@ -0,0 +1,797 @@
+/**
+ *
+ * Copyright (C) 2015 Victor Seva (sipwise.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#include <stdio.h>
+#include <sys/stat.h>
+
+#include "../../events.h"
+#include "../../lib/kcore/cmpapi.h"
+#include "../../pvar.h"
+#include "../../rpc.h"
+#include "../../rpc_lookup.h"
+
+#include "cfgt_int.h"
+#include "cfgt_json.h"
+
+static str _cfgt_route_prefix[] = {
+	str_init("start|"),
+	str_init("exit|"),
+	str_init("drop|"),
+	str_init("return|"),
+	{0, 0}
+};
+cfgt_node_p _cfgt_node = NULL;
+cfgt_hash_p _cfgt_uuid = NULL;
+str cfgt_hdr_prefix = {"NGCP%", 5};
+str cfgt_basedir = {"/tmp", 4};
+int cfgt_mask = CFGT_DP_ALL;
+
+static int shm_str_hash_alloc(struct str_hash_table *ht, int size)
+{
+	ht->table = shm_malloc(sizeof(struct str_hash_head) * size);
+
+	if (!ht->table)
+		return -1;
+
+	ht->size = size;
+	return 0;
+}
+
+static int _cfgt_init_hashtable(struct str_hash_table *ht)
+{
+	if (shm_str_hash_alloc(ht, CFGT_HASH_SIZE) != 0)
+	{
+		LM_ERR("Error allocating shared memory hashtable\n");
+		return -1;
+	}
+
+	str_hash_init(ht);
+
+	return 0;
+}
+
+int _cfgt_pv_parse(str *param, pv_elem_p *elem)
+{
+	if (param->s && param->len > 0)
+	{
+		if (pv_parse_format(param, elem)<0)
+		{
+			LM_ERR("malformed or non AVP %.*s AVP definition\n",
+					param->len, param->s);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+void _cfgt_remove_uuid(const str *uuid)
+{
+	struct str_hash_head *head;
+	struct str_hash_entry *entry, *back;
+	int i;
+
+	if(_cfgt_uuid==NULL) return;
+	if(uuid)
+	{
+		lock_get(&_cfgt_uuid->lock);
+		entry = str_hash_get(&_cfgt_uuid->hash, uuid->s, uuid->len);
+		if(entry)
+		{
+			str_hash_del(entry);
+			shm_free(entry->key.s);
+			shm_free(entry);
+			LM_DBG("uuid[%.*s] removed from hash\n", uuid->len, uuid->s);
+		}
+		else LM_DBG("uuid[%.*s] not found in hash\n", uuid->len, uuid->s);
+		lock_release(&_cfgt_uuid->lock);
+	}
+	else
+	{
+		lock_get(&_cfgt_uuid->lock);
+		for(i=0; i<CFGT_HASH_SIZE; i++)
+		{
+			head = _cfgt_uuid->hash.table+i;
+			clist_foreach_safe(head, entry, back, next)
+			{
+				LM_DBG("uuid[%.*s] removed from hash\n",
+					entry->key.len, entry->key.s);
+				str_hash_del(entry);
+				shm_free(entry->key.s);
+				shm_free(entry);
+			}
+			lock_release(&_cfgt_uuid->lock);
+		}
+		LM_DBG("remove all uuids. done\n");
+	}
+}
+
+int _cfgt_get_uuid_id(cfgt_node_p node)
+{
+	struct str_hash_entry *entry;
+
+	if(_cfgt_uuid==NULL || node==NULL || node->uuid.len == 0) return -1;
+	lock_get(&_cfgt_uuid->lock);
+	entry = str_hash_get(&_cfgt_uuid->hash, node->uuid.s, node->uuid.len);
+	if(entry)
+	{
+		entry->u.n = entry->u.n + 1;
+		node->msgid = entry->u.n;
+	}
+	else
+	{
+		entry = shm_malloc(sizeof(struct str_hash_entry));
+		if(entry==NULL)
+		{
+			lock_release(&_cfgt_uuid->lock);
+			LM_ERR("No shared memory left\n");
+			return -1;
+		}
+		if (shm_str_dup(&entry->key, &node->uuid) != 0)
+		{
+			lock_release(&_cfgt_uuid->lock);
+			shm_free(entry);
+			LM_ERR("No shared memory left\n");
+			return -1;
+		}
+		entry->u.n = 1;
+		node->msgid = 1;
+		LM_DBG("Add new entry[%.*s]\n", node->uuid.len, node->uuid.s);
+		str_hash_add(&_cfgt_uuid->hash, entry);
+	}
+	lock_release(&_cfgt_uuid->lock);
+	LM_DBG("msgid:[%d]\n", node->msgid);
+	return 1;
+}
+
+int _cfgt_get_hdr_helper(struct sip_msg *msg, str *res, int mode)
+{
+	struct hdr_field *hf;
+	char *delimiter, *end;
+	str tmp = STR_NULL;
+
+	if(msg==NULL || (mode==0 && res==NULL))
+		return -1;
+
+	/* we need to be sure we have parsed all headers */
+	if(parse_headers(msg, HDR_EOH_F, 0)<0)
+	{
+		LM_ERR("error parsing headers\n");
+		return -1;
+	}
+
+	hf = msg->callid;
+	if(!hf) return 1;
+
+	if(strncmp(hf->body.s, cfgt_hdr_prefix.s, cfgt_hdr_prefix.len)==0)
+	{
+		tmp.s = hf->body.s+cfgt_hdr_prefix.len;
+		delimiter = tmp.s-1;
+		LM_DBG("Prefix detected. delimiter[%c]\n", *delimiter);
+		if(mode==0)
+		{
+			end = strchr(tmp.s, *delimiter);
+			if(end)
+			{
+				tmp.len = end-tmp.s;
+				if(pkg_str_dup(res, &tmp)<0)
+				{
+					LM_ERR("error copying header\n");
+					return -1;
+				}
+				LM_DBG("cfgtest uuid:[%.*s]\n", res->len, res->s);
+				return 0;
+			}
+		}
+		else
+		{
+			tmp.len = res->len;
+			LM_DBG("tmp[%.*s] res[%.*s]\n", tmp.len, tmp.s, res->len, res->s);
+			return STR_EQ(tmp, *res);
+		}
+	}
+	return 1; /* not found */
+}
+
+int _cfgt_get_hdr(struct sip_msg *msg, str *res)
+{
+	return _cfgt_get_hdr_helper(msg, res, 0);
+}
+
+int _cfgt_cmp_hdr(struct sip_msg *msg, str *res)
+{
+	return _cfgt_get_hdr_helper(msg, res, 1);
+}
+
+cfgt_node_p cfgt_create_node(struct sip_msg *msg)
+{
+	cfgt_node_p node;
+
+	node = (cfgt_node_p) pkg_malloc(sizeof(cfgt_node_t));
+	if(node==NULL)
+	{
+		LM_ERR("cannot allocate cfgtest msgnode\n");
+		return node;
+	}
+	memset(node, 0, sizeof(cfgt_node_t));
+	srjson_InitDoc(&node->jdoc, NULL);
+	if (msg)
+	{
+		node->msgid = msg->id;
+		LM_DBG("msgid:%d\n", node->msgid);
+		if(_cfgt_get_hdr(msg, &node->uuid)!=0 || node->uuid.len==0)
+		{
+			LM_ERR("cannot get value of cfgtest uuid header!!\n");
+			goto error;
+		}
+	}
+	node->jdoc.root = srjson_CreateObject(&node->jdoc);
+	if(node->jdoc.root==NULL)
+	{
+		LM_ERR("cannot create json root\n");
+		goto error;
+	}
+	node->flow = srjson_CreateArray(&node->jdoc);
+	if(node->flow==NULL)
+	{
+		LM_ERR("cannot create json object\n");
+		goto error;
+	}
+	srjson_AddItemToObject(&node->jdoc, node->jdoc.root, "flow\0", node->flow);
+	node->in = srjson_CreateArray(&node->jdoc);
+	if(node->in==NULL)
+	{
+		LM_ERR("cannot create json object\n");
+		goto error;
+	}
+	srjson_AddItemToObject(&node->jdoc, node->jdoc.root, "sip_in\0", node->in);
+	node->out = srjson_CreateArray(&node->jdoc);
+	if(node->out==NULL)
+	{
+		LM_ERR("cannot create json object\n");
+		goto error;
+	}
+	srjson_AddItemToObject(&node->jdoc, node->jdoc.root, "sip_out\0", node->out);
+	LM_DBG("node created\n");
+	return node;
+
+error:
+	srjson_DestroyDoc(&node->jdoc);
+	pkg_free(node);
+	return NULL;
+}
+
+void _cfgt_remove_node(cfgt_node_p node)
+{
+	if(!node) return;
+	srjson_DestroyDoc(&node->jdoc);
+	if(node->uuid.s) pkg_free(node->uuid.s);
+	while(node->flow_head)
+	{
+		node->route = node->flow_head;
+		node->flow_head = node->route->next;
+		pkg_free(node->route);
+		node->route = NULL;
+	}
+	pkg_free(node);
+}
+
+int _cfgt_get_filename(int msgid, str uuid, str *dest, int *dir)
+{
+	int i, lid;
+	char buff_id[INT2STR_MAX_LEN];
+	char *sid;
+	if(dest==NULL || uuid.len == 0) return -1;
+
+	dest->len = cfgt_basedir.len + uuid.len;
+	if(cfgt_basedir.s[cfgt_basedir.len-1]!='/')
+		dest->len = dest->len + 1;
+	sid = sint2strbuf(msgid, buff_id, INT2STR_MAX_LEN, &lid);
+	dest->len += lid + 6;
+	dest->s = (char *) pkg_malloc((dest->len*sizeof(char)+1));
+	if(dest->s==NULL)
+	{
+		LM_ERR("no more memory.\n");
+		return -1;
+	}
+	strncpy(dest->s, cfgt_basedir.s, cfgt_basedir.len);
+	i = cfgt_basedir.len;
+	if(cfgt_basedir.s[cfgt_basedir.len-1]!='/')
+	{
+		strncpy(dest->s+i, "/", 1);
+		i = i + 1;
+	}
+	strncpy(dest->s+i, uuid.s, uuid.len);
+	i = i + uuid.len; (*dir) = i;
+	strncpy(dest->s+i, "\0", 1);
+	i = i + 1;
+	strncpy(dest->s+i, sid, lid);
+	i = i + lid;
+	strncpy(dest->s+i, ".json\0", 6);
+	return 0;
+}
+
+int _cfgt_node2json(cfgt_node_p node)
+{
+       srjson_t *jobj;
+
+       if(!node) return -1;
+       jobj = srjson_CreateStr(&node->jdoc, node->uuid.s, node->uuid.len);
+       if(jobj==NULL)
+       {
+               LM_ERR("cannot create json object\n");
+               return -1;
+       }
+       srjson_AddItemToObject(&node->jdoc, node->jdoc.root, "uuid\0", jobj);
+
+       jobj = srjson_CreateNumber(&node->jdoc, (double)node->msgid);
+       if(jobj==NULL)
+       {
+               LM_ERR("cannot create json object\n");
+               return -1;
+       }
+       srjson_AddItemToObject(&node->jdoc, node->jdoc.root, "msgid\0", jobj);
+       return 0;
+}
+
+void cfgt_save_node(cfgt_node_p node)
+{
+	FILE *fp;
+	str dest = STR_NULL;
+	int dir = 0;
+	if(_cfgt_get_filename(node->msgid, node->uuid, &dest, &dir)<0)
+	{
+		LM_ERR("can't build filename\n");
+		return;
+	}
+	LM_DBG("dir [%s]\n", dest.s);
+	mkdir(dest.s, S_IRWXO|S_IXGRP|S_IRWXU);
+	dest.s[dir] = '/';
+	fp = fopen(dest.s, "w");
+	LM_DBG("file [%s]\n", dest.s);
+	if(fp) {
+		pkg_free(dest.s);
+		dest.s = srjson_Print(&node->jdoc, node->jdoc.root);
+	    if(dest.s==NULL)
+        {
+           LM_ERR("Cannot get the json string\n");
+           fclose(fp);
+           return;
+        }
+        if(fputs(dest.s, fp)<0){
+        	LM_ERR("failed writing to file\n");
+        }
+        fclose(fp);
+        node->jdoc.free_fn(dest.s);
+	}
+	else {
+		LM_ERR("Can't open file [%s] to write\n", dest.s);
+		pkg_free(dest.s);
+	}
+}
+
+void _cfgt_print_node(cfgt_node_p node, int json)
+{
+	char *buf = NULL;
+	cfgt_str_list_p route;
+
+	if(!node) return;
+	if(node->flow_head)
+	{
+		route = node->flow_head;
+		while(route)
+		{
+			if(route == node->route)
+				LM_DBG("[--[%.*s][%d]--]\n",
+					route->s.len, route->s.s, route->type);
+			else LM_DBG("[%.*s][%d]\n",
+					route->s.len, route->s.s, route->type);
+			route = route->next;
+		}
+	}
+	else LM_DBG("flow:empty\n");
+	if(json) {
+		buf = srjson_PrintUnformatted(&node->jdoc, node->jdoc.root);
+		if(buf==NULL)
+		{
+			LM_ERR("Cannot get the json string\n");
+			return;
+		}
+		LM_DBG("node[%p]: id:[%d] uuid:[%.*s] info:[%s]\n",
+			node, node->msgid, node->uuid.len, node->uuid.s, buf);
+		node->jdoc.free_fn(buf);
+	}
+}
+
+int _cfgt_set_dump(struct sip_msg *msg, cfgt_node_p node, str *flow)
+{
+	srjson_t *f, *vars;
+
+	if(node==NULL || flow == NULL) return -1;
+	vars = srjson_CreateObject(&node->jdoc);
+	if(vars==NULL)
+	{
+		LM_ERR("cannot create json object\n");
+		return -1;
+	}
+	if(cfgt_get_json(msg, 30, &node->jdoc, vars)<0)
+	{
+		LM_ERR("cannot get var info\n");
+		return -1;
+	}
+	f = srjson_CreateObject(&node->jdoc);
+	if(f==NULL)
+	{
+		LM_ERR("cannot create json object\n");
+		srjson_Delete(&node->jdoc, vars);
+		return -1;
+	}
+	srjson_AddStrItemToObject(&node->jdoc, f,
+		flow->s, flow->len, vars);
+	srjson_AddItemToArray(&node->jdoc, node->flow, f);
+	LM_DBG("node[%.*s] flow created\n", flow->len, flow->s);
+	return 0;
+}
+
+void _cfgt_set_type(cfgt_str_list_p route, struct action *a)
+{
+	switch(a->type)
+	{
+		case DROP_T:
+			if(a->val[1].u.number&DROP_R_F) {
+				route->type = CFGT_DROP_D;
+				LM_DBG("set[%.*s]->CFGT_DROP_D\n", route->s.len, route->s.s);
+			}
+			if(a->val[1].u.number&RETURN_R_F){
+				route->type = CFGT_DROP_R;
+				LM_DBG("set[%.*s]->CFGT_DROP_R\n", route->s.len, route->s.s);
+			}
+			else {
+				route->type = CFGT_DROP_E;
+				LM_DBG("set[%.*s]->CFGT_DROP_E\n", route->s.len, route->s.s);
+			}
+			break;
+		case ROUTE_T:
+			route->type = CFGT_ROUTE;
+			LM_DBG("set[%.*s]->CFGT_ROUTE\n", route->s.len, route->s.s);
+			break;
+		default:
+			if(route->type!=CFGT_DROP_E)
+			{
+				route->type = CFGT_DROP_R;
+				LM_DBG("[%.*s] no relevant action: CFGT_DROP_R[%d]\n",
+					route->s.len, route->s.s, a->type);
+			}
+			else
+			{
+				LM_DBG("[%.*s] already set to CFGT_DROP_E[%d]\n",
+					route->s.len, route->s.s, a->type);
+			}
+			break;
+	}
+}
+
+int _cfgt_add_routename(cfgt_node_p node, struct action *a,
+		str *routename)
+{
+	cfgt_str_list_p route;
+	int ret = 0;
+
+	if(!node->route) /* initial */
+	{
+		node->route = pkg_malloc(sizeof(cfgt_str_list_t));
+		if(!node->route)
+		{
+			LM_ERR("No more pkg mem\n");
+			return -1;
+		}
+		memset(node->route, 0, sizeof(cfgt_str_list_t));
+		node->flow_head = node->route;
+		node->route->type = CFGT_ROUTE;
+		ret = 1;
+	}
+	else
+	{
+		LM_DBG("actual routename:[%.*s][%d]\n", node->route->s.len,
+			node->route->s.s, node->route->type);
+		if(node->route->prev)
+			LM_DBG("prev routename:[%.*s][%d]\n", node->route->prev->s.len,
+				node->route->prev->s.s,	node->route->prev->type);
+		if(node->route->next)
+			LM_DBG("next routename:[%.*s][%d]\n", node->route->next->s.len,
+				node->route->next->s.s,	node->route->next->type);
+		if(STR_EQ(*routename, node->route->s))
+		{
+			LM_DBG("same route\n");
+			_cfgt_set_type(node->route, a);
+			return 2;
+		}
+		else if(node->route->prev &&
+				STR_EQ(*routename, node->route->prev->s))
+		{
+			LM_DBG("back to route[%.*s]\n", node->route->prev->s.len,
+				node->route->prev->s.s);
+			_cfgt_set_type(node->route->prev, a);
+			return 3;
+		}
+		route = pkg_malloc(sizeof(cfgt_str_list_t));
+		if(!route)
+		{
+			LM_ERR("No more pkg mem\n");
+			return -1;
+		}
+		memset(route, 0, sizeof(cfgt_str_list_t));
+		route->prev = node->route;
+		node->route->next = route;
+		node->route = route;
+		_cfgt_set_type(node->route, a);
+	}
+	node->route->s.s = routename->s;
+	node->route->s.len = routename->len;
+	LM_DBG("add[%d] route:[%.*s]\n", ret, node->route->s.len, node->route->s.s);
+	_cfgt_print_node(node, 0);
+	return ret;
+}
+
+void _cfgt_del_routename(cfgt_node_p node)
+{
+	if(node->route->next!=NULL) {
+		LM_ERR("wtf!! route->next[%p] not null!!\n", node->route->next);
+		_cfgt_print_node(node, 0);
+	}
+	LM_DBG("del route[%.*s]\n", node->route->s.len, node->route->s.s);
+	node->route = node->route->prev;
+	pkg_free(node->route->next);
+	node->route->next = NULL;
+}
+/* dest has to be freed */
+int _cfgt_node_get_flowname(cfgt_str_list_p route, int *indx, str *dest)
+{
+	int i;
+	if(route==NULL) return -1;
+	LM_DBG("routename:[%.*s][%d]\n", route->s.len, route->s.s,
+		route->type);
+	if(indx) i = *indx;
+	else i = route->type-1;
+	if(str_append(&_cfgt_route_prefix[i],
+		&route->s, dest)<0)
+	{
+		LM_ERR("Cannot create route name\n");
+		return -1;
+	}
+	return 0;
+}
+
+int cfgt_process_route(struct sip_msg *msg, struct action *a)
+{
+	str routename;
+	int ret = -1;
+	int indx = 0;
+	str flowname = STR_NULL;
+	if(!_cfgt_node) {
+		LM_ERR("node empty\n");
+		return -1;
+	}
+	if (a->rname==NULL) {
+		LM_DBG("no routename. type:%d\n", a->type);
+		return 0;
+	}
+	LM_DBG("route from action:[%s]\n", a->rname);
+	routename.s = a->rname; routename.len = strlen(a->rname);
+	switch(_cfgt_add_routename(_cfgt_node, a, &routename))
+	{
+		case 2: /* same name */
+			return 0;
+		case 1: /* initial */
+			LM_DBG("Initial route[%.*s]. dump vars\n",
+				_cfgt_node->route->s.len, _cfgt_node->route->s.s);
+			if(_cfgt_node_get_flowname(_cfgt_node->route, &indx, &flowname)<0)
+			{
+				LM_ERR("cannot create flowname\n");
+				return -1;
+			}
+			ret = _cfgt_set_dump(msg, _cfgt_node, &flowname);
+			break;
+		case 0: /* new */
+			LM_DBG("Change from[%.*s] route to route[%.*s]. dump vars\n",
+				_cfgt_node->route->prev->s.len, _cfgt_node->route->prev->s.s,
+				_cfgt_node->route->s.len, _cfgt_node->route->s.s);
+			if(_cfgt_node_get_flowname(_cfgt_node->route, &indx, &flowname)<0)
+			{
+				LM_ERR("cannot create flowname\n");
+				return -1;
+			}
+			ret = _cfgt_set_dump(msg, _cfgt_node, &flowname);
+			break;
+		case 3: /* back to previous */
+			if(_cfgt_node_get_flowname(_cfgt_node->route, 0, &flowname)<0)
+			{
+				LM_ERR("cannot create flowname\n");
+				return -1;
+			}
+			ret = _cfgt_set_dump(msg, _cfgt_node, &flowname);
+			_cfgt_del_routename(_cfgt_node);
+			break;
+		default:
+			return -1;
+	}
+	if(flowname.s) pkg_free(flowname.s);
+	return ret;
+}
+
+/*
+TODO:
+- parse first line, check if is SIP
+- parse for header cfgtest
+*/
+int cfgt_msgin(void *data)
+{
+	srjson_t *jobj;
+	str *buf = (str *) data;
+	if(buf==NULL) return 0;
+	if(_cfgt_node) {
+		cfgt_save_node(_cfgt_node);
+		_cfgt_remove_node(_cfgt_node);
+		LM_DBG("node removed\n");
+		_cfgt_node = NULL;
+	}
+	LM_DBG("msg in:{%.*s}\n", buf->len, buf->s);
+	_cfgt_node = cfgt_create_node(NULL);
+	if(_cfgt_node)
+	{
+		jobj = srjson_CreateStr(&_cfgt_node->jdoc, buf->s, buf->len);
+		if(jobj==NULL)
+		{
+			LM_ERR("cannot create json object\n");
+			return -1;
+		}
+		srjson_AddItemToArray(&_cfgt_node->jdoc, _cfgt_node->in, jobj);
+		return 0;
+	}
+	LM_ERR("_cfgt_node empty\n");
+	return -1;
+}
+
+int cfgt_pre(struct sip_msg *msg, unsigned int flags, void *bar) {
+	str unknown = {"unknown", 7};
+
+	if(_cfgt_node)
+	{
+		if (_cfgt_node->msgid == 0)
+		{
+			LM_DBG("new node\n");
+			if(_cfgt_get_hdr(msg, &_cfgt_node->uuid)!=0 ||
+				_cfgt_node->uuid.len==0)
+			{
+				LM_ERR("cannot get value of cfgtest uuid header."
+					" Using unknown\n");
+				pkg_str_dup(&_cfgt_node->uuid, &unknown);
+			}
+			return _cfgt_get_uuid_id(_cfgt_node);
+		}
+		else
+		{
+			LM_DBG("_cfgt_node->uuid:[%.*s]\n", _cfgt_node->uuid.len,
+				_cfgt_node->uuid.s);
+			if(_cfgt_cmp_hdr(msg, &_cfgt_node->uuid))
+			{
+				LM_DBG("same uuid\n");
+				return 1;
+			}
+			else {
+				LM_DBG("different uuid\n");
+			}
+		}
+	}
+	else { LM_ERR("node empty??\n"); }
+	_cfgt_node = cfgt_create_node(msg);
+	if(_cfgt_node) {
+		LM_DBG("node created\n");
+		return 1;
+	}
+	return -1;
+}
+int cfgt_post(struct sip_msg *msg, unsigned int flags, void *bar) {
+	str flowname = STR_NULL;
+
+	if(_cfgt_node) {
+		LM_DBG("dump last flow\n");
+		if(_cfgt_node_get_flowname(_cfgt_node->route, 0, &flowname)<0)
+			LM_ERR("cannot create flowname\n");
+		else _cfgt_set_dump(msg, _cfgt_node, &flowname);
+		if(flowname.s) pkg_free(flowname.s);
+		cfgt_save_node(_cfgt_node);
+	}
+	return 1;
+}
+
+int cfgt_msgout(void *data)
+{
+	srjson_t *jobj;
+	str *buf = (str *) data;
+	if(buf==NULL) return 0;
+	LM_DBG("msg out:{%.*s}\n", buf->len, buf->s);
+
+	if(_cfgt_node)
+	{
+		jobj = srjson_CreateStr(&_cfgt_node->jdoc, buf->s, buf->len);
+		if(jobj==NULL)
+		{
+			LM_ERR("cannot create json object\n");
+			return -1;
+		}
+		srjson_AddItemToArray(&_cfgt_node->jdoc, _cfgt_node->out, jobj);
+		return 0;
+	}
+	LM_ERR("node empty\n");
+	return -1;
+}
+
+/**
+ *
+ */
+static const char* cfgt_rpc_mask_doc[2] = {
+	"Specify module mask",
+	0
+};
+
+static void cfgt_rpc_mask(rpc_t* rpc, void* ctx){
+	int mask = CFGT_DP_ALL;
+
+	if (rpc->scan(ctx, "*d", &mask) != 1)
+	{
+		rpc->fault(ctx, 500, "invalid parameters");
+		return;
+	}
+	cfgt_mask = mask;
+	rpc->add(ctx, "s", "200 ok");
+}
+
+rpc_export_t cfgt_rpc[] = {
+	{"dbg.mask", cfgt_rpc_mask, cfgt_rpc_mask_doc, 0},
+	{0, 0, 0, 0}
+};
+
+int cfgt_init(void)
+{
+	if (rpc_register_array(cfgt_rpc)!=0)
+	{
+		LM_ERR("failed to register RPC commands\n");
+		return -1;
+	}
+	_cfgt_uuid = shm_malloc(sizeof(cfgt_hash_t));
+	if(_cfgt_uuid==NULL)
+	{
+		LM_ERR("Cannot allocate shared memory\n");
+		return -1;
+	}
+	if(!lock_init(&_cfgt_uuid->lock))
+	{
+		LM_ERR("cannot init the lock\n");
+		shm_free(_cfgt_uuid);
+		_cfgt_uuid = NULL;
+		return -1;
+	}
+	if(_cfgt_init_hashtable(&_cfgt_uuid->hash)<0)
+		return -1;
+	sr_event_register_cb(SREV_NET_DATA_IN, cfgt_msgin);
+	sr_event_register_cb(SREV_NET_DATA_OUT, cfgt_msgout);
+	return 0;
+}

+ 67 - 0
modules/cfgt/cfgt_int.h

@@ -0,0 +1,67 @@
+/**
+ *
+ * Copyright (C) 2015 Victor Seva (sipwise.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#ifndef _CFGT_INT_H_
+#define _CFGT_INT_H_
+
+#include "../../lib/srutils/srjson.h"
+#include "../../locking.h"
+#include "../../route_struct.h"
+#include "../../str_hash.h"
+
+#define CFGT_HASH_SIZE 32
+
+enum _cfgt_action_type {
+	CFGT_ROUTE=1,
+	CFGT_DROP_E, CFGT_DROP_D, CFGT_DROP_R
+};
+
+typedef struct _cfgt_hash
+{
+	gen_lock_t lock;
+	struct str_hash_table hash;
+	str save_uuid; /* uuid to be save */
+} cfgt_hash_t, *cfgt_hash_p;
+
+typedef struct _cfgt_str_list
+{
+	str s;
+	enum _cfgt_action_type type;
+	struct _cfgt_str_list *next, *prev;
+} cfgt_str_list_t, *cfgt_str_list_p;
+
+typedef struct _cfgt_node
+{
+	srjson_doc_t jdoc;
+	str uuid;
+	int msgid;
+	cfgt_str_list_p flow_head;
+	cfgt_str_list_p route;
+	srjson_t *in, *out, *flow;
+	struct _cfgt_node *next, *prev;
+} cfgt_node_t, *cfgt_node_p;
+
+int cfgt_init(void);
+cfgt_node_p cfgt_create_node(struct sip_msg *msg);
+int cfgt_process_route(struct sip_msg *msg, struct action *a);
+int cfgt_pre(struct sip_msg *msg, unsigned int flags, void *bar);
+int cfgt_post(struct sip_msg *msg, unsigned int flags, void *bar);
+#endif

+ 389 - 0
modules/cfgt/cfgt_json.c

@@ -0,0 +1,389 @@
+/**
+ *
+ * Copyright (C) 2013-2015 Victor Seva (sipwise.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#include <stdio.h>
+
+#include "../../pvar.h"
+#include "../../mem/shm_mem.h"
+#include "../../xavp.h"
+#include "../pv/pv_xavp.h"
+
+#include "cfgt_json.h"
+
+int _cfgt_get_array_avp_vals(struct sip_msg *msg,
+		pv_param_t *param, srjson_doc_t *jdoc, srjson_t **jobj,
+		str *item_name)
+{
+	struct usr_avp *avp;
+	unsigned short name_type;
+	int_str avp_name;
+	int_str avp_value;
+	struct search_state state;
+	srjson_t *jobjt;
+	memset(&state, 0, sizeof(struct search_state));
+
+	if(pv_get_avp_name(msg, param, &avp_name, &name_type)!=0)
+	{
+		LM_ERR("invalid name\n");
+		return -1;
+	}
+	*jobj = srjson_CreateArray(jdoc);
+	if(*jobj==NULL)
+	{
+		LM_ERR("cannot create json object\n");
+		return -1;
+	}
+	if ((avp=search_first_avp(name_type, avp_name, &avp_value, &state))==0)
+	{
+		goto ok;
+	}
+	do
+	{
+		if(avp->flags & AVP_VAL_STR)
+		{
+			jobjt = srjson_CreateStr(jdoc, avp_value.s.s, avp_value.s.len);
+			if(jobjt==NULL)
+			{
+				LM_ERR("cannot create json object\n");
+				return -1;
+			}
+		} else {
+			jobjt = srjson_CreateNumber(jdoc, avp_value.n);
+			if(jobjt==NULL)
+			{
+				LM_ERR("cannot create json object\n");
+				return -1;
+			}
+		}
+		srjson_AddItemToArray(jdoc, *jobj, jobjt);
+	} while ((avp=search_next_avp(&state, &avp_value))!=0);
+ok:
+	item_name->s = avp_name.s.s;
+	item_name->len = avp_name.s.len;
+	return 0;
+}
+#define CFGT_XAVP_DUMP_SIZE 32
+static str* _cfgt_xavp_dump[CFGT_XAVP_DUMP_SIZE];
+int _cfgt_xavp_dump_lookup(pv_param_t *param)
+{
+	unsigned int i = 0;
+	pv_xavp_name_t *xname;
+
+	if(param==NULL)
+		return -1;
+
+	xname = (pv_xavp_name_t*)param->pvn.u.dname;
+
+	while(_cfgt_xavp_dump[i]!=NULL&&i<CFGT_XAVP_DUMP_SIZE)
+	{
+		if(_cfgt_xavp_dump[i]->len==xname->name.len)
+		{
+			if(strncmp(_cfgt_xavp_dump[i]->s, xname->name.s, xname->name.len)==0)
+				return 1; /* already dump before */
+		}
+		i++;
+	}
+	if(i==CFGT_XAVP_DUMP_SIZE)
+	{
+		LM_WARN("full _cfgt_xavp_dump cache array\n");
+		return 0; /* end cache names */
+	}
+	_cfgt_xavp_dump[i] = &xname->name;
+	return 0;
+}
+
+void _cfgt_get_obj_xavp_val(sr_xavp_t *avp, srjson_doc_t *jdoc, srjson_t **jobj)
+{
+	static char _pv_xavp_buf[128];
+	int result = 0;
+
+	switch(avp->val.type) {
+		case SR_XTYPE_NULL:
+			*jobj = srjson_CreateNull(jdoc);
+		break;
+		case SR_XTYPE_INT:
+			*jobj = srjson_CreateNumber(jdoc, avp->val.v.i);
+		break;
+		case SR_XTYPE_STR:
+			*jobj = srjson_CreateStr(jdoc, avp->val.v.s.s, avp->val.v.s.len);
+		break;
+		case SR_XTYPE_TIME:
+			result = snprintf(_pv_xavp_buf, 128, "%lu", (long unsigned)avp->val.v.t);
+		break;
+		case SR_XTYPE_LONG:
+			result = snprintf(_pv_xavp_buf, 128, "%ld", (long unsigned)avp->val.v.l);
+		break;
+		case SR_XTYPE_LLONG:
+			result = snprintf(_pv_xavp_buf, 128, "%lld", avp->val.v.ll);
+		break;
+		case SR_XTYPE_XAVP:
+			result = snprintf(_pv_xavp_buf, 128, "<<xavp:%p>>", avp->val.v.xavp);
+		break;
+		case SR_XTYPE_DATA:
+			result = snprintf(_pv_xavp_buf, 128, "<<data:%p>>", avp->val.v.data);
+		break;
+		default:
+			LM_WARN("unknown data type\n");
+			*jobj = srjson_CreateNull(jdoc);
+	}
+	if(result<0)
+	{
+		LM_ERR("cannot convert to str\n");
+		*jobj = srjson_CreateNull(jdoc);
+	}
+	else if(*jobj==NULL)
+	{
+		*jobj = srjson_CreateStr(jdoc, _pv_xavp_buf, 128);
+	}
+}
+
+int _cfgt_get_obj_avp_vals(str name, sr_xavp_t *xavp, srjson_doc_t *jdoc, srjson_t **jobj)
+{
+	sr_xavp_t *avp = NULL;
+	srjson_t *jobjt = NULL;
+
+	*jobj = srjson_CreateArray(jdoc);
+	if(*jobj==NULL)
+	{
+		LM_ERR("cannot create json object\n");
+		return -1;
+	}
+	avp = xavp;
+	while(avp!=NULL&&!STR_EQ(avp->name,name))
+	{
+		avp = avp->next;
+	}
+	while(avp!=NULL)
+	{
+		_cfgt_get_obj_xavp_val(avp, jdoc, &jobjt);
+		srjson_AddItemToArray(jdoc, *jobj, jobjt);
+		jobjt = NULL;
+		avp = xavp_get_next(avp);
+	}
+
+	return 0;
+}
+
+int _cfgt_get_obj_xavp_vals(struct sip_msg *msg,
+		pv_param_t *param, srjson_doc_t *jdoc, srjson_t **jobjr,
+		str *item_name)
+{
+	pv_xavp_name_t *xname = (pv_xavp_name_t*)param->pvn.u.dname;
+	sr_xavp_t *xavp = NULL;
+	sr_xavp_t *avp = NULL;
+	srjson_t *jobj = NULL;
+	srjson_t *jobjt = NULL;
+	struct str_list *keys;
+	struct str_list *k;
+
+	*jobjr = srjson_CreateArray(jdoc);
+	if(*jobjr==NULL)
+	{
+		LM_ERR("cannot create json object\n");
+		return -1;
+	}
+
+	item_name->s = xname->name.s;
+	item_name->len = xname->name.len;
+	xavp = xavp_get_by_index(&xname->name, 0, NULL);
+	if(xavp==NULL)
+	{
+		return 0; /* empty */
+	}
+
+	do
+	{
+		if(xavp->val.type==SR_XTYPE_XAVP)
+		{
+			avp = xavp->val.v.xavp;
+			jobj = srjson_CreateObject(jdoc);
+			if(jobj==NULL)
+			{
+				LM_ERR("cannot create json object\n");
+				return -1;
+			}
+			keys = xavp_get_list_key_names(xavp);
+			if(keys!=NULL)
+			{
+				do
+				{
+					_cfgt_get_obj_avp_vals(keys->s, avp, jdoc, &jobjt);
+					srjson_AddStrItemToObject(jdoc, jobj, keys->s.s,
+						keys->s.len, jobjt);
+					k = keys;
+					keys = keys->next;
+					pkg_free(k);
+					jobjt = NULL;
+				}while(keys!=NULL);
+			}
+		}
+		if(jobj!=NULL)
+		{
+			srjson_AddItemToArray(jdoc, *jobjr, jobj);
+			jobj = NULL;
+		}
+	}while((xavp = xavp_get_next(xavp))!=0);
+
+	return 0;
+}
+
+int cfgt_get_json(struct sip_msg* msg, unsigned int mask, srjson_doc_t *jdoc,
+	srjson_t *head)
+{
+	int i;
+	pv_value_t value;
+	pv_cache_t **_pv_cache = pv_cache_get_table();
+	pv_cache_t *el = NULL;
+	srjson_t *jobj = NULL;
+	str item_name = STR_NULL;
+	static char iname[128];
+
+	if(_pv_cache==NULL)
+	{
+		LM_ERR("cannot access pv_cache\n");
+		return -1;
+	}
+	if(jdoc==NULL){
+		LM_ERR("jdoc is null\n");
+		return -1;
+	}
+	if(head==NULL){
+		LM_ERR("head is null\n");
+		return -1;
+	}
+
+	memset(_cfgt_xavp_dump, 0, sizeof(str*)*CFGT_XAVP_DUMP_SIZE);
+	for(i=0;i<PV_CACHE_SIZE;i++)
+	{
+		el = _pv_cache[i];
+		while(el)
+		{
+			if(!(el->spec.type==PVT_AVP||
+				el->spec.type==PVT_SCRIPTVAR||
+				el->spec.type==PVT_XAVP||
+				el->spec.type==PVT_OTHER)||
+				!((el->spec.type==PVT_AVP&&mask&CFGT_DP_AVP)||
+				(el->spec.type==PVT_XAVP&&mask&CFGT_DP_XAVP)||
+				(el->spec.type==PVT_SCRIPTVAR&&mask&CFGT_DP_SCRIPTVAR)||
+				(el->spec.type==PVT_OTHER&&mask&CFGT_DP_OTHER))||
+				(el->spec.trans!=NULL))
+			{
+				el = el->next;
+				continue;
+			}
+			jobj = NULL;
+			item_name.len = 0;
+			item_name.s = 0;
+			iname[0] = '\0';
+			if(el->spec.type==PVT_AVP)
+			{
+				if(el->spec.pvp.pvi.type==PV_IDX_ALL||
+					(el->spec.pvp.pvi.type==PV_IDX_INT&&el->spec.pvp.pvi.u.ival!=0))
+				{
+					el = el->next;
+					continue;
+				}
+				else
+				{
+					if(_cfgt_get_array_avp_vals(msg, &el->spec.pvp, jdoc, &jobj, &item_name)!=0)
+					{
+						LM_WARN("can't get value[%.*s]\n", el->pvname.len, el->pvname.s);
+						el = el->next;
+						continue;
+					}
+					if(srjson_GetArraySize(jdoc, jobj)==0 && !(mask&CFGT_DP_NULL))
+					{
+						el = el->next;
+						continue;
+					}
+					snprintf(iname, 128, "$avp(%.*s)", item_name.len, item_name.s);
+				}
+			}
+			else if(el->spec.type==PVT_XAVP)
+			{
+				if(_cfgt_xavp_dump_lookup(&el->spec.pvp)!=0)
+				{
+					el = el->next;
+					continue;
+				}
+				if(_cfgt_get_obj_xavp_vals(msg, &el->spec.pvp, jdoc, &jobj, &item_name)!=0)
+				{
+					LM_WARN("can't get value[%.*s]\n", el->pvname.len, el->pvname.s);
+					el = el->next;
+					continue;
+				}
+				if(srjson_GetArraySize(jdoc, jobj)==0 && !(mask&CFGT_DP_NULL))
+				{
+					el = el->next;
+					continue;
+				}
+				snprintf(iname, 128, "$xavp(%.*s)", item_name.len, item_name.s);
+			}
+			else
+			{
+				if(pv_get_spec_value(msg, &el->spec, &value)!=0)
+				{
+					LM_WARN("can't get value[%.*s]\n", el->pvname.len, el->pvname.s);
+					el = el->next;
+					continue;
+				}
+				if(value.flags&(PV_VAL_NULL|PV_VAL_EMPTY|PV_VAL_NONE))
+				{
+					if(mask&CFGT_DP_NULL)
+					{
+						jobj = srjson_CreateNull(jdoc);
+					}
+					else
+					{
+						el = el->next;
+						continue;
+					}
+				}else if(value.flags&(PV_VAL_INT)){
+					jobj = srjson_CreateNumber(jdoc, value.ri);
+				}else if(value.flags&(PV_VAL_STR)){
+					jobj = srjson_CreateStr(jdoc, value.rs.s, value.rs.len);
+				}else {
+					LM_WARN("el->pvname[%.*s] value[%d] unhandled\n", el->pvname.len, el->pvname.s,
+						value.flags);
+					el = el->next;
+					continue;
+				}
+				if(jobj==NULL)
+				{
+					LM_ERR("el->pvname[%.*s] empty json object\n", el->pvname.len,
+						el->pvname.s);
+					goto error;
+				}
+				snprintf(iname, 128, "%.*s", el->pvname.len, el->pvname.s);
+			}
+			if(jobj!=NULL)
+			{
+				srjson_AddItemToObject(jdoc, head, iname, jobj);
+			}
+			el = el->next;
+		}
+	}
+	return 0;
+
+error:
+	srjson_Delete(jdoc, head);
+	return -1;
+}

+ 38 - 0
modules/cfgt/cfgt_json.h

@@ -0,0 +1,38 @@
+/**
+ *
+ * Copyright (C) 2013-2015 Victor Seva (sipwise.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef _CFGT_JSON_H
+#define _CFGT_JSON_H
+
+#include "../../lib/srutils/srjson.h"
+#include "../../route_struct.h"
+
+#define CFGT_DP_NULL       1
+#define CFGT_DP_AVP        2
+#define CFGT_DP_SCRIPTVAR  4
+#define CFGT_DP_XAVP       8
+#define CFGT_DP_OTHER     16
+#define CFGT_DP_ALL       31
+
+int cfgt_get_json(struct sip_msg* msg, unsigned int mask, srjson_doc_t *jdoc,
+	srjson_t *head);
+#endif

+ 112 - 0
modules/cfgt/cfgt_mod.c

@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2015 Victor Seva (sipwise.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 "../../events.h"
+#include "../../script_cb.h"
+#include "../../sr_module.h"
+
+#include "cfgt_mod.h"
+#include "cfgt_int.h"
+#include "cfgt_json.h"
+#include "cfgt.h"
+
+MODULE_VERSION
+
+static int mod_init(void);        /*!< Module initialization function */
+static void destroy(void);        /*!< Module destroy function */
+static int child_init(int rank);  /*!< Per-child init function */
+
+extern int bind_cfgt(cfgt_api_t* api);
+
+/*! flag to protect against wrong initialization */
+unsigned int init_flag = 0;
+extern int cfgt_mask;
+extern str cfgt_basedir;
+extern str cfgt_hdr_prefix;
+
+/*! \brief
+ * Exported functions
+ */
+static cmd_export_t cmds[] = {
+	{"cfgt_bind_cfgt", (cmd_function)bind_cfgt, 1, 0, 0, 0},
+	{0, 0, 0, 0, 0, 0}
+};
+
+/*! \brief
+ * Exported parameters
+ */
+static param_export_t params[] = {
+	{"basedir",  PARAM_STR, &cfgt_basedir},
+	{"mask", INT_PARAM, &cfgt_mask },
+	{"callid_prefix", PARAM_STR, &cfgt_hdr_prefix },
+	{0, 0, 0}
+};
+
+struct module_exports exports = {
+	"cfgt",
+	DEFAULT_DLFLAGS, /*!< dlopen flags */
+	cmds,       /*!< Exported functions */
+	params,     /*!< Export parameters */
+	0,          /*!< exported statistics */
+	0,          /*!< exported MI functions */
+	0,          /*!< exported pseudo-variables */
+	0,          /*!< extra processes */
+	mod_init,   /*!< Module initialization function */
+	0,          /*!< Response function */
+	destroy,    /*!< Destroy function */
+	child_init  /*!< Child initialization function */
+};
+
+/*! \brief
+ * Module initialization function
+ */
+static int mod_init(void)
+{
+	unsigned int ALL = REQUEST_CB+FAILURE_CB+ONREPLY_CB
+		+BRANCH_CB+ONSEND_CB+ERROR_CB+LOCAL_CB+EVENT_CB+BRANCH_FAILURE_CB;
+	if(cfgt_init()<0) return -1;
+	if (register_script_cb(cfgt_pre, PRE_SCRIPT_CB|ALL, 0) != 0)
+	{
+		LM_ERR("could not insert PRE_SCRIPT callback");
+		return -1;
+	}
+	if (register_script_cb(cfgt_post, POST_SCRIPT_CB|ALL, 0) != 0)
+	{
+		LM_ERR("could not insert POST_SCRIPT callback");
+		return -1;
+	}
+
+	init_flag = 1;
+	return 0;
+}
+
+static int child_init(int _rank)
+{
+	return 0;
+}
+
+/*! \brief
+ * Module destroy function
+ */
+static void destroy(void)
+{
+}

+ 29 - 0
modules/cfgt/cfgt_mod.h

@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 Victor Seva (sipwise.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 _CFGT_MOD_H
+#define _CFGT_MOD_H
+
+/*! flag to protect against wrong initialization */
+extern unsigned int init_flag;
+
+#endif

+ 4 - 0
modules/cfgt/doc/Makefile

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

+ 36 - 0
modules/cfgt/doc/cfgt.xml

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+    <bookinfo>
+	<title>cfgt Module</title>
+	<productname class="trade">&kamailioname;</productname>
+	<authorgroup>
+	    <author>
+		<firstname>Victor</firstname>
+		<surname>Seva</surname>
+		<affiliation><orgname>sipwise.com</orgname></affiliation>
+	    </author>
+	    <editor>
+		<firstname>Victor</firstname>
+		<surname>Seva</surname>
+		    <email>[email protected]</email>
+	    </editor>
+	</authorgroup>
+	<copyright>
+	    <year>2015</year>
+	    <holder>Victor Seva (sipwise.com)</holder>
+	</copyright>
+    </bookinfo>
+    <toc></toc>
+
+	<xi:include href="cfgt_admin.xml"/>
+
+</book>

+ 183 - 0
modules/cfgt/doc/cfgt_admin.xml

@@ -0,0 +1,183 @@
+<?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 a report of the way &kamailioname; configuration
+		has been executed as part of a unit test for different
+		SIP scenarios.
+	</para>
+	<para>
+		In order to identify different scenarios a prefix string should be
+		used inside the Call-ID header.
+	</para>
+	</section>
+	<section>
+	<title>Dependencies</title>
+	<section>
+		<title>&kamailio; Modules</title>
+		<para>
+		The following modules must be loaded before this module:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>None</emphasis>.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	<section>
+		<title>External Libraries or Applications</title>
+		<para>
+		The following libraries or applications must be installed before running
+		&kamailio; with this module loaded:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>None</emphasis>.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	</section>
+	<section>
+	<title>Parameters</title>
+	<section id="cfg.p.basedir">
+		<title><varname>basedir</varname> (string)</title>
+		<para>
+			Control where the config reports should be stored. The dir must
+			exist and &kamailioname; must have permissions to write on it.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>/tmp</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>cfgtrace</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("cfgt", "basedir", "/var/run/kamailio/cfgtest")
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section id="cfgt.p.mask">
+		<title><varname>mask</varname> (int)</title>
+		<itemizedlist>
+			<para><emphasis>mask</emphasis> - Control the type of vars it should
+			display in the report:
+			</para>
+			<itemizedlist>
+			<listitem><para>
+			  1 - dump null values
+			</para></listitem>
+			<listitem><para>
+			  2 - dump avp vars
+			</para></listitem>
+			<listitem><para>
+			  4 - dump script vars
+			</para></listitem>
+			<listitem><para>
+			  8 - dump xavp vars
+			</para></listitem>
+			<listitem><para>
+			  16 - dump DP_OTHER vars
+			</para></listitem>
+			<listitem><para>
+			  32 - dump ALL vars
+			</para></listitem>
+			</itemizedlist>
+		</itemizedlist>
+		<para>
+		<emphasis>
+			Default value is <quote>32</quote> (ALL).
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>mask</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+# dump xavp(8) and avp(4) vars
+modparam("cfgt", "mask", 12)
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section id="cfgt.p.callid_prefix">
+		<title><varname>callid_prefix</varname> (string)</title>
+		<para>
+			Prefix used to identify test scenario messages. Last char of the
+			string will be used as delimiter for the scenario ID. With parameter
+			set to <quote>NGCP%</quote> and Call-ID <quote>NGCP%123%456</quote> 
+			the scenario identified will be <quote>123</quote>.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>NGCP%</quote>
+			(using <quote>%</quote> as delimiter).
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>callid_prefix</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+# using '%' as delimiter
+modparam("cfgt", "callid_prefix", "TEST-ID%")
+...
+</programlisting>
+		</example>
+	</section>
+
+	</section>
+	<section>
+		<title>Usage</title>
+		<para>
+		This module is used by the <emphasis>debugger</emphasis> module, so it must be loaded
+		first. To enable the generation of the reports, the <emphasis>debugger</emphasis> module
+		must be configured by setting the <emphasis>cfgtest</emphasis> parameter to "1".
+		</para>
+		<para>
+		Example of configuration:
+		</para>
+<programlisting  format="linespecific">
+...
+#!ifdef WITH_DEBUG
+loadmodule "cfgt.so"
+loadmodule "debugger.so"
+#!endif
+...
+#!ifdef WITH_DEBUG
+# ----- cfgt params -----
+modparam("cfgt", "basedir", "/tmp/kamailio/cfgtest")
+modparam("cfgt", "callid_prefix", "TRACE-ID%")
+modparam("cfgt", "mask", 32)
+
+# ----- debugger params -----
+modparam("debugger", "cfgtrace", 1)
+modparam("debugger", "log_level_name", "exec")
+
+modparam("debugger", "cfgtest", 1)
+#!endif
+...
+</programlisting>
+    </section>
+</chapter>

+ 0 - 24
modules/cnxcc/cnxcc.c

@@ -28,30 +28,6 @@
 
 #include "cnxcc.h"
 
-inline void get_datetime(str *dest)
-{
-	timestamp2isodt(dest, get_current_timestamp());
-}
-
-inline unsigned int get_current_timestamp()
-{
-	return time(NULL);
-}
-
-inline int timestamp2isodt(str *dest, unsigned int timestamp)
-{
-	time_t  		tim;
-	struct tm 		*tmPtr;
-
-	tim 		= timestamp;
-	tmPtr 		= localtime(&tim);
-
-	strftime( dest->s, DATETIME_SIZE, "%Y-%m-%d %H:%M:%S", tmPtr);
-	dest->len	= DATETIME_LENGTH;
-
-	return 0;
-}
-
 double str2double(str *string)
 {
 	char buffer[string->len + 1];

+ 24 - 3
modules/cnxcc/cnxcc.h

@@ -31,9 +31,30 @@
 #define DATETIME_LENGTH		DATETIME_SIZE - 1
 
 
-inline void get_datetime(str *dest);
-inline unsigned int get_current_timestamp();
-inline int timestamp2isodt(str *dest, unsigned int timestamp);
+static inline unsigned int get_current_timestamp()
+{
+	return time(NULL);
+}
+
+static inline int timestamp2isodt(str *dest, unsigned int timestamp)
+{
+	time_t  		tim;
+	struct tm 		*tmPtr;
+
+	tim 		= timestamp;
+	tmPtr 		= localtime(&tim);
+
+	strftime( dest->s, DATETIME_SIZE, "%Y-%m-%d %H:%M:%S", tmPtr);
+	dest->len	= DATETIME_LENGTH;
+
+	return 0;
+}
+
+static inline void get_datetime(str *dest)
+{
+	timestamp2isodt(dest, get_current_timestamp());
+}
+
 double str2double(str *string);
 
 #endif /* _CNXCC_H */

+ 37 - 0
modules/crypto/Makefile

@@ -0,0 +1,37 @@
+#
+# 
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=crypto.so
+
+ifeq ($(CROSS_COMPILE),)
+SSL_BUILDER=$(shell \
+	if pkg-config --exists libssl; then \
+		echo 'pkg-config libssl'; \
+	fi)
+
+ifneq ($(SSL_BUILDER),)
+SSL_BUILDER+=$(shell \
+	if pkg-config --exists libcrypto; then \
+		echo 'libcrypto'; \
+	fi)
+endif
+
+endif
+
+ifneq ($(SSL_BUILDER),)
+	DEFS += $(shell $(SSL_BUILDER) --cflags)
+	LIBS += $(shell $(SSL_BUILDER) --libs)
+else
+	DEFS += -I$(LOCALBASE)/ssl/include
+	LIBS += -L$(LOCALBASE)/lib -L$(LOCALBASE)/ssl/lib \
+			-L$(LOCALBASE)/lib64 -L$(LOCALBASE)/ssl/lib64 \
+			-lssl -lcrypto
+endif
+
+DEFS+=-DKAMAILIO_MOD_INTERFACE
+
+include ../../Makefile.modules
+

+ 106 - 0
modules/crypto/README

@@ -0,0 +1,106 @@
+CRYPTO Module
+
+Daniel-Constantin Mierla
+
+   <[email protected]>
+
+Edited by
+
+Daniel-Constantin Mierla
+
+   <[email protected]>
+
+   Copyright © 2016 asipto.com
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+        2. Dependencies
+
+              2.1. Kamailio Modules
+              2.2. External Libraries or Applications
+
+        3. Functions
+
+              3.1. crypto_aes_encrypt(text, key, res)
+              3.2. crypto_aes_decrypt(text, key, res)
+
+   List of Examples
+
+   1.1. crypto_aes_encrypt usage
+   1.2. crypto_aes_decrypt usage
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+   2. Dependencies
+
+        2.1. Kamailio Modules
+        2.2. External Libraries or Applications
+
+   3. Functions
+
+        3.1. crypto_aes_encrypt(text, key, res)
+        3.2. crypto_aes_decrypt(text, key, res)
+
+1. Overview
+
+   This module provides various cryptography tools for use in Kamailio
+   configuration file.
+
+   It relies on OpenSSL libraries for cryptographic operations (libssl,
+   libcrypto).
+
+2. Dependencies
+
+   2.1. Kamailio Modules
+   2.2. External Libraries or Applications
+
+2.1. Kamailio Modules
+
+   The following modules must be loaded before this module:
+     * none.
+
+2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed before
+   running Kamailio with this module loaded:
+     * libcrypto - part of OpenSSL project
+
+3. Functions
+
+   3.1. crypto_aes_encrypt(text, key, res)
+   3.2. crypto_aes_decrypt(text, key, res)
+
+3.1. crypto_aes_encrypt(text, key, res)
+
+   Encrypts the text with the key using AES encryption algorithm. The
+   result is encoded in base64 format and stored in res. The parameter res
+   must be a read-write variables. The parameters text and key can be
+   static strings or strings with variables (dynamic strings).
+
+   This function can be used from ANY_ROUTE.
+
+   Example 1.1. crypto_aes_encrypt usage
+...
+crypto_aes_encrypt("$rb", "my-secret-key", "$var(encrypted)");
+...
+
+3.2. crypto_aes_decrypt(text, key, res)
+
+   Decrypts the text with the key using AES encryption algorithm. The text
+   has to be encoded in base64 format. The parameter res must be a
+   read-write variables. The parameters text and key can be static strings
+   or strings with variables (dynamic strings).
+
+   This function can be used from ANY_ROUTE.
+
+   Example 1.2. crypto_aes_decrypt usage
+...
+crypto_aes_decrypt("$var(encrypted)", "my-secret-key", "$var(text)");
+...

+ 383 - 0
modules/crypto/crypto_mod.c

@@ -0,0 +1,383 @@
+/**
+ * Copyright (C) 2011 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../sr_module.h"
+#include "../../dprint.h"
+#include "../../mod_fix.h"
+#include "../../pvapi.h"
+#include "../../lvalue.h"
+#include "../../basex.h"
+
+#include <openssl/evp.h>
+
+#define AES_BLOCK_SIZE 256
+
+MODULE_VERSION
+
+int crypto_aes_init(unsigned char *key_data, int key_data_len,
+		unsigned char *salt, EVP_CIPHER_CTX *e_ctx, EVP_CIPHER_CTX *d_ctx);
+unsigned char *crypto_aes_encrypt(EVP_CIPHER_CTX *e, unsigned char *plaintext,
+		int *len);
+unsigned char *crypto_aes_decrypt(EVP_CIPHER_CTX *e, unsigned char *ciphertext,
+		int *len);
+
+static int  mod_init(void);
+static int  child_init(int);
+static void mod_destroy(void);
+
+static int w_crypto_aes_encrypt(sip_msg_t* msg, char* inb, char* keyb, char* outb);
+static int fixup_crypto_aes_encrypt(void** param, int param_no);
+static int w_crypto_aes_decrypt(sip_msg_t* msg, char* inb, char* keyb, char* outb);
+static int fixup_crypto_aes_decrypt(void** param, int param_no);
+
+
+static cmd_export_t cmds[]={
+	{"crypto_aes_encrypt", (cmd_function)w_crypto_aes_encrypt, 3,
+		fixup_crypto_aes_encrypt, 0, ANY_ROUTE},
+	{"crypto_aes_decrypt", (cmd_function)w_crypto_aes_decrypt, 3,
+		fixup_crypto_aes_decrypt, 0, ANY_ROUTE},
+	{0, 0, 0, 0, 0, 0}
+};
+
+static param_export_t params[]={
+	{0, 0, 0}
+};
+
+struct module_exports exports = {
+	"crypto",
+	DEFAULT_DLFLAGS, /* dlopen flags */
+	cmds,
+	params,
+	0,
+	0,              /* exported MI functions */
+	0,              /* exported pseudo-variables */
+	0,              /* extra processes */
+	mod_init,       /* module initialization function */
+	0,              /* response function */
+	mod_destroy,    /* destroy function */
+	child_init      /* per child init function */
+};
+
+
+/**
+ * @brief Initialize crypto module function
+ */
+static int mod_init(void)
+{
+	return 0;
+}
+
+/**
+ * @brief Initialize crypto module children
+ */
+static int child_init(int rank)
+{
+	return 0;
+}
+
+/**
+ * destroy module function
+ */
+static void mod_destroy(void)
+{
+	return;
+}
+
+/**
+ *
+ */
+static int w_crypto_aes_encrypt(sip_msg_t* msg, char* inb, char* keyb, char* outb)
+{
+	str ins;
+	str keys;
+	pv_spec_t *dst;
+	pv_value_t val;
+	EVP_CIPHER_CTX en;
+	str etext;
+	unsigned char salt[] = {1,2,3,4,5,6,7,8};
+
+	if (fixup_get_svalue(msg, (gparam_t*)inb, &ins) != 0) {
+		LM_ERR("cannot get input value\n");
+		return -1;
+	}
+	if (fixup_get_svalue(msg, (gparam_t*)keyb, &keys) != 0) {
+		LM_ERR("cannot get key value\n");
+		return -1;
+	}
+	dst = (pv_spec_t*)outb;
+
+	/* gen key and iv. init the cipher ctx object */
+	if (crypto_aes_init((unsigned char *)keys.s, keys.len, salt, &en, NULL)) {
+		LM_ERR("couldn't initialize AES cipher\n");
+		return -1;
+	}
+	etext.len = ins.len;
+	etext.s = (char *)crypto_aes_encrypt(&en, (unsigned char *)ins.s, &etext.len);
+
+	memset(&val, 0, sizeof(pv_value_t));
+	val.rs.s = pv_get_buffer();
+	val.rs.len = base64_enc((unsigned char *)etext.s, etext.len,
+					(unsigned char *)val.rs.s, pv_get_buffer_size()-1);
+	if (val.rs.len < 0) {
+		LM_ERR("base64 output of encrypted value is too large (need %d)\n",
+				-val.rs.len);
+		goto error;
+	}
+	LM_DBG("base64 encrypted result: [%.*s]\n", val.rs.len, val.rs.s);
+	val.flags = PV_VAL_STR;
+	dst->setf(msg, &dst->pvp, (int)EQ_T, &val);
+
+	free(etext.s);
+	EVP_CIPHER_CTX_cleanup(&en);
+	return 1;
+
+error:
+	free(etext.s);
+	EVP_CIPHER_CTX_cleanup(&en);
+	return -1;
+}
+
+/**
+ *
+ */
+static int fixup_crypto_aes_encrypt(void** param, int param_no)
+{
+	if(param_no==1 || param_no==2) {
+		if(fixup_spve_null(param, 1)<0)
+			return -1;
+		return 0;
+	} else if(param_no==3) {
+		if (fixup_pvar_null(param, 1) != 0) {
+			LM_ERR("failed to fixup result pvar\n");
+			return -1;
+		}
+		if (((pv_spec_t *)(*param))->setf == NULL) {
+			LM_ERR("result pvar is not writeble\n");
+			return -1;
+		}
+	}
+	return 0;
+}
+
+/**
+ *
+ */
+static int w_crypto_aes_decrypt(sip_msg_t* msg, char* inb, char* keyb, char* outb)
+{
+	return 1;
+}
+
+/**
+ *
+ */
+static int fixup_crypto_aes_decrypt(void** param, int param_no)
+{
+	if(param_no==1 || param_no==2) {
+		if(fixup_spve_null(param, 1)<0)
+			return -1;
+		return 0;
+	} else if(param_no==3) {
+		if (fixup_pvar_null(param, 1) != 0) {
+			LM_ERR("failed to fixup result pvar\n");
+			return -1;
+		}
+		if (((pv_spec_t *)(*param))->setf == NULL) {
+			LM_ERR("result pvar is not writeble\n");
+			return -1;
+		}
+	}
+	return 0;
+}
+
+
+/**
+ * Create an 256 bit key and IV using the supplied key_data and salt.
+ * Fills in the encryption and decryption ctx objects and returns 0 on success
+ */
+int crypto_aes_init(unsigned char *key_data, int key_data_len,
+		unsigned char *salt, EVP_CIPHER_CTX *e_ctx, EVP_CIPHER_CTX *d_ctx)
+{
+	int i, nrounds = 5;
+	unsigned char key[32], iv[32];
+
+	/*
+	 * Gen key & IV for AES 256 CBC mode. A SHA1 digest is used to hash
+	 * the supplied key material.
+	 * nrounds is the number of times the we hash the material. More rounds
+	 * are more secure but slower.
+	 */
+	i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt,
+			key_data, key_data_len, nrounds, key, iv);
+	if (i != 32) {
+		LM_ERR("key size is %d bits - should be 256 bits\n", i);
+		return -1;
+	}
+
+	for(int x = 0; x<32; ++x)
+		LM_DBG("key: %x iv: %x \n", key[x], iv[x]);
+
+	for(int x = 0; x<8; ++x)
+		LM_DBG("salt: %x\n", salt[x]);
+
+	if(e_ctx) {
+		EVP_CIPHER_CTX_init(e_ctx);
+		EVP_EncryptInit_ex(e_ctx, EVP_aes_256_cbc(), NULL, key, iv);
+	}
+	if(d_ctx) {
+		EVP_CIPHER_CTX_init(d_ctx);
+		EVP_DecryptInit_ex(d_ctx, EVP_aes_256_cbc(), NULL, key, iv);
+	}
+
+	return 0;
+}
+
+/*
+ * Encrypt *len bytes of data
+ * All data going in & out is considered binary (unsigned char[])
+ */
+unsigned char *crypto_aes_encrypt(EVP_CIPHER_CTX *e, unsigned char *plaintext,
+		int *len)
+{
+	/* max ciphertext len for a n bytes of plaintext is
+	 * n + AES_BLOCK_SIZE -1 bytes */
+	int c_len = *len + AES_BLOCK_SIZE - 1, f_len = 0;
+	unsigned char *ciphertext = (unsigned char *)malloc(c_len);
+
+	/* allows reusing of 'e' for multiple encryption cycles */
+	if(!EVP_EncryptInit_ex(e, NULL, NULL, NULL, NULL)){
+		LM_ERR("failure in EVP_EncryptInit_ex \n");
+		return NULL;
+	}
+
+	/* update ciphertext, c_len is filled with the length of ciphertext
+	 * generated, *len is the size of plaintext in bytes */
+	if(!EVP_EncryptUpdate(e, ciphertext, &c_len, plaintext, *len)){
+		LM_ERR("failure in EVP_EncryptUpdate \n");
+		return NULL;
+	}
+
+	/* update ciphertext with the final remaining bytes */
+	if(!EVP_EncryptFinal_ex(e, ciphertext+c_len, &f_len)){
+		LM_ERR("failure in EVP_EncryptFinal_ex \n");
+		return NULL;
+	}
+
+	*len = c_len + f_len;
+	return ciphertext;
+}
+
+/*
+ * Decrypt *len bytes of ciphertext
+ */
+unsigned char *crypto_aes_decrypt(EVP_CIPHER_CTX *e, unsigned char *ciphertext,
+		int *len)
+{
+	/* plaintext will always be equal to or lesser than length of ciphertext*/
+	int p_len = *len, f_len = 0;
+	unsigned char *plaintext = (unsigned char *)malloc(p_len);
+
+	if(!EVP_DecryptInit_ex(e, NULL, NULL, NULL, NULL)){
+		LM_ERR("failure in EVP_DecryptInit_ex \n");
+		return NULL;
+	}
+
+	if(!EVP_DecryptUpdate(e, plaintext, &p_len, ciphertext, *len)){
+		LM_ERR("failure in EVP_DecryptUpdate\n");
+		return NULL;
+	}
+
+	if(!EVP_DecryptFinal_ex(e, plaintext+p_len, &f_len)){
+		LM_ERR("failure in EVP_DecryptFinal_ex\n");
+		return NULL;
+	}
+
+	*len = p_len + f_len;
+	return plaintext;
+}
+
+/**
+ * testing function
+ */
+int crypto_aes_test(void)
+{
+	/* "opaque" encryption, decryption ctx structures
+	 * that libcrypto uses to record status of enc/dec operations */
+	EVP_CIPHER_CTX en, de;
+
+
+	/* The salt paramter is used as a salt in the derivation:
+	 * it should point to an 8 byte buffer or NULL if no salt is used. */
+	unsigned char salt[] = {1,2,3,4,5,6,7,8};
+
+	unsigned char *key_data;
+	int key_data_len, i;
+	char *input[] = {"Kamailio - The Open Source SIP Server",
+		"Thank you for flying Kamailio!",
+		"100 Trying\nYour call is important to us",
+		NULL
+	};
+
+	/* the key_data for testing */
+	key_data = (unsigned char *)"kamailio-sip-server";
+	key_data_len = strlen((const char *)key_data);
+
+	/* gen key and iv. init the cipher ctx object */
+	if (crypto_aes_init(key_data, key_data_len, salt, &en, &de)) {
+		LM_ERR("couldn't initialize AES cipher\n");
+		return -1;
+	}
+
+	/* encrypt and decrypt each input string and compare with the original */
+	for (i = 0; input[i]; i++) {
+		char *plaintext;
+		unsigned char *ciphertext;
+		int olen, len;
+
+		/* The enc/dec functions deal with binary data and not C strings.
+		 * strlen() will return length of the string without counting the '\0'
+		 * string marker. We always pass in the marker byte to the
+		 * encrypt/decrypt functions so that after decryption we end up with
+		 * a legal C string */
+		olen = len = strlen(input[i])+1;
+
+		ciphertext = crypto_aes_encrypt(&en, (unsigned char *)input[i], &len);
+		plaintext = (char *)crypto_aes_decrypt(&de, ciphertext, &len);
+
+		if (strncmp(plaintext, input[i], olen))
+			LM_ERR("FAIL: enc/dec failed for \"%s\"\n", input[i]);
+		else
+			LM_NOTICE("OK: enc/dec ok for \"%s\"\n", plaintext);
+
+		free(ciphertext);
+		free(plaintext);
+	}
+
+	EVP_CIPHER_CTX_cleanup(&de);
+	EVP_CIPHER_CTX_cleanup(&en);
+
+	return 0;
+}

+ 4 - 0
modules/crypto/doc/Makefile

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

+ 37 - 0
modules/crypto/doc/crypto.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>CRYPTO Module</title>
+	<productname class="trade">sip-router.org</productname>
+	<authorgroup>
+	    <author>
+		<firstname>Daniel-Constantin</firstname>
+		<surname>Mierla</surname>
+		<email>[email protected]</email>
+	    </author>
+	    <editor>
+		<firstname>Daniel-Constantin</firstname>
+		<surname>Mierla</surname>
+		<email>[email protected]</email>
+	    </editor>
+	</authorgroup>
+	<copyright>
+	    <year>2016</year>
+	    <holder>asipto.com</holder>
+	</copyright>
+    </bookinfo>
+    <toc></toc>
+
+    <xi:include href="crypto_admin.xml"/>
+
+
+</book>

+ 107 - 0
modules/crypto/doc/crypto_admin.xml

@@ -0,0 +1,107 @@
+<?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 various cryptography tools for use
+		in &kamailio; configuration file.
+	</para>
+	<para>
+		It relies on OpenSSL libraries for cryptographic operations
+		(libssl, libcrypto).
+	</para>
+	</section>
+
+	<section>
+	<title>Dependencies</title>
+	<section>
+		<title>&kamailio; Modules</title>
+		<para>
+		The following modules must be loaded before this module:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>none</emphasis>.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	<section>
+		<title>External Libraries or Applications</title>
+		<para>
+		The following libraries or applications must be installed before running
+		&kamailio; with this module loaded:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>libcrypto</emphasis> - part of OpenSSL project
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	</section>
+
+	<section>
+	<title>Functions</title>
+	<section id="async.f.crypto_aes_encrypt">
+	    <title>
+		<function moreinfo="none">crypto_aes_encrypt(text, key, res)</function>
+	    </title>
+	    <para>
+		Encrypts the text with the key using AES encryption algorithm. The
+		result is encoded in base64 format and stored in res. The parameter
+		res must be a read-write variables. The parameters text and key can
+		be static strings or strings with variables (dynamic strings).
+		</para>
+		<para>
+		This function can be used from ANY_ROUTE.
+		</para>
+		<example>
+		<title><function>crypto_aes_encrypt</function> usage</title>
+		<programlisting format="linespecific">
+...
+crypto_aes_encrypt("$rb", "my-secret-key", "$var(encrypted)");
+...
+</programlisting>
+	    </example>
+	</section>
+
+	<section id="async.f.crypto_aes_decrypt">
+	    <title>
+		<function moreinfo="none">crypto_aes_decrypt(text, key, res)</function>
+	    </title>
+	    <para>
+		Decrypts the text with the key using AES encryption algorithm. The
+		text has to be encoded in base64 format. The parameter
+		res must be a read-write variables. The parameters text and key can
+		be static strings or strings with variables (dynamic strings).
+		</para>
+		<para>
+		This function can be used from ANY_ROUTE.
+		</para>
+		<example>
+		<title><function>crypto_aes_decrypt</function> usage</title>
+		<programlisting format="linespecific">
+...
+crypto_aes_decrypt("$var(encrypted)", "my-secret-key", "$var(text)");
+...
+</programlisting>
+	    </example>
+	</section>
+	</section>
+</chapter>

+ 4 - 0
modules/curl/TODO.txt

@@ -48,8 +48,12 @@ The curl module - todo
 
 
 - Async - event_route when done
+	(with and without suspending transaction)
 	Need background task process to run curl request
 	event_route[curl:connectioncomplete]
 	$curlcon == connection
 	$curl == URL
 	$curlres == result
+
+- API for other modules - done by Hugh Waite
+	- Needs documentation

+ 28 - 1
modules/curl/curl_api.h

@@ -1,4 +1,31 @@
-#ifndef _CURL_CPI_H_
+/*
+ * Copyright (C) 2015 Hugh Waite
+ *
+ * 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
+ *
+ */
+
+/*!
+ * \file
+ * \brief Kamailio curl :: Core API include file
+ * \ingroup curl
+ * Module: \ref curl
+ */
+#ifndef _CURL_API_H_
 #define _CURL_API_H_
 
 #include "../../sr_module.h"

+ 7 - 3
modules/db_mysql/km_dbase.c

@@ -80,9 +80,13 @@ static int db_mysql_submit_query(const db1_con_t* _h, const str* _s)
 	if (my_ping_interval) {
 		t = time(0);
 		if ((t - CON_TIMESTAMP(_h)) > my_ping_interval) {
-			if (mysql_ping(CON_CONNECTION(_h))) {
-				LM_WARN("driver error on ping: %s\n", mysql_error(CON_CONNECTION(_h)));
-				counter_inc(mysql_cnts_h.driver_err);
+			for (i=0; i < (db_mysql_auto_reconnect ? 3 : 1); i++) {
+				if (mysql_ping(CON_CONNECTION(_h))) {
+					LM_WARN("driver error on ping: %s\n", mysql_error(CON_CONNECTION(_h)));
+					counter_inc(mysql_cnts_h.driver_err);
+				} else {
+					break;
+				}
 			}
 		}
 		/*

+ 129 - 0
modules/db_text/dbt_base.c

@@ -617,3 +617,132 @@ error:
 	return -1;
 }
 
+int dbt_replace(db1_con_t* _h, db_key_t* _k, db_val_t* _v,
+	      int _n, int _nk, int _m)
+{
+	dbt_table_p _tbc = NULL;
+	dbt_row_p _drp = NULL;
+	int i, j;
+	int *lkey=NULL, *lres=NULL;
+
+	if (!_h || !CON_TABLE(_h) || _nk <= 0)
+	{
+		LM_ERR("invalid parameters\n");
+		return -1;
+	}
+
+	((dbt_con_p)_h->tail)->affected = 0;
+
+	/* lock database */
+	_tbc = dbt_db_get_table(DBT_CON_CONNECTION(_h), CON_TABLE(_h));
+	if(!_tbc)
+	{
+		LM_ERR("table %.*s does not exist!\n", CON_TABLE(_h)->len, CON_TABLE(_h)->s);
+		return -1;
+	}
+
+	if(_k)
+	{
+		lkey = dbt_get_refs(_tbc, _k, _nk);
+		if(!lkey)
+			goto error;
+	}
+	lres = dbt_get_refs(_tbc, _k, _n);
+	if(!lres)
+		goto error;
+	_drp = _tbc->rows;
+	while(_drp)
+	{
+		if(dbt_row_match(_tbc, _drp, lkey, NULL, _v, _nk))
+		{ // update fields
+			for(i=0; i<_n; i++)
+			{
+				if(dbt_is_neq_type(_tbc->colv[lres[i]]->type, _v[i].type))
+				{
+					LM_ERR("incompatible types!\n");
+					goto error;
+				}
+
+				if(dbt_row_update_val(_drp, &(_v[i]),
+							_tbc->colv[lres[i]]->type, lres[i]))
+				{
+					LM_ERR("cannot set v[%d] in c[%d]!\n",
+							i, lres[i]);
+					goto error;
+				}
+			}
+
+			((dbt_con_p)_h->tail)->affected++;
+
+		}
+		_drp = _drp->next;
+	}
+
+	if(((dbt_con_p)_h->tail)->affected == 0) {
+		_drp = dbt_row_new(_tbc->nrcols);
+		if(!_drp)
+		{
+			LM_ERR("no shm memory for a new row!!\n");
+			goto error;
+		}
+
+		for(i=0; i<_n; i++)
+		{
+			j = lres[i];
+			if(dbt_is_neq_type(_tbc->colv[j]->type, _v[i].type))
+			{
+				LM_ERR("incompatible types v[%d] - c[%d]!\n", i, j);
+				goto error;
+			}
+			if(_v[i].type == DB1_STRING && !_v[i].nul)
+				_v[i].val.str_val.len = strlen(_v[i].val.string_val);
+			if(dbt_row_set_val(_drp, &(_v[i]), _tbc->colv[j]->type, j))
+			{
+				LM_ERR("cannot set v[%d] in c[%d]!\n", i, j);
+				goto error;
+			}
+		}
+
+		if(dbt_table_add_row(_tbc, _drp))
+		{
+			LM_ERR("cannot insert the new row!!\n");
+			goto error;
+		}
+
+		((dbt_con_p)_h->tail)->affected = 1;
+
+	}
+
+	if( ((dbt_con_p)_h->tail)->affected )
+		dbt_table_update_flags(_tbc, DBT_TBFL_MODI, DBT_FL_SET, 1);
+
+	/* dbt_print_table(_tbc, NULL); */
+
+	/* unlock database */
+	dbt_release_table(DBT_CON_CONNECTION(_h), CON_TABLE(_h));
+
+	if(lkey)
+		pkg_free(lkey);
+	if(lres)
+		pkg_free(lres);
+
+    return 0;
+
+error:
+
+	if(lkey)
+		pkg_free(lkey);
+	if(lres)
+		pkg_free(lres);
+	if(_drp) // free row
+		dbt_row_free(_tbc, _drp);
+
+	/* unlock database */
+	dbt_release_table(DBT_CON_CONNECTION(_h), CON_TABLE(_h));
+
+	LM_ERR("failed to update the table!\n");
+
+	return -1;
+}
+
+

+ 2 - 2
modules/db_text/dbt_lib.c

@@ -443,13 +443,13 @@ int dbt_is_neq_type(db_type_t _t0, db_type_t _t1)
 		case DB1_DOUBLE:
 			break;
 		case DB1_STRING:
-			if(_t0==DB1_STR)
+			if(_t0==DB1_STR || _t0==DB1_BLOB)
 				return 0;
 		case DB1_STR:
 			if(_t0==DB1_STRING || _t0==DB1_BLOB)
 				return 0;
 		case DB1_BLOB:
-			if(_t0==DB1_STR)
+			if(_t0==DB1_STR || _t0==DB1_STRING)
 				return 0;
 		case DB1_BITMAP:
 			if (_t0==DB1_INT)

+ 1 - 1
modules/db_text/dbt_raw_util.c

@@ -216,7 +216,7 @@ int dbt_build_where(char* where, db_key_t** _k, db_op_t** _o, db_val_t** _v)
 		//      needs changes in dbt_query / dbt_row_match
 
 		l = matches[2].rm_eo - matches[2].rm_so;
-		_k1[idx] = pkg_malloc(sizeof(db_key_t));
+		_k1[idx] = pkg_malloc(sizeof(str));
 		_k1[idx]->len = l;
 		_k1[idx]->s = pkg_malloc(sizeof(char) * (l+1));
 		strncpy(_k1[idx]->s, buffer+matches[2].rm_so, l);

+ 2 - 1
modules/db_text/dbtext.c

@@ -125,9 +125,10 @@ int dbt_bind_api(db_func_t *dbb)
 	dbb->insert      = (db_insert_f)dbt_insert;
 	dbb->delete      = (db_delete_f)dbt_delete; 
 	dbb->update      = (db_update_f)dbt_update;
+	dbb->replace     = (db_replace_f)dbt_replace;
 	dbb->affected_rows = (db_affected_rows_f) dbt_affected_rows;
 	dbb->raw_query   = (db_raw_query_f) dbt_raw_query;
-	dbb->cap         = DB_CAP_ALL | DB_CAP_AFFECTED_ROWS | DB_CAP_RAW_QUERY;
+	dbb->cap         = DB_CAP_ALL | DB_CAP_AFFECTED_ROWS | DB_CAP_RAW_QUERY | DB_CAP_REPLACE;
 
 	return 0;
 }

+ 6 - 0
modules/db_text/dbtext.h

@@ -81,6 +81,12 @@ int dbt_delete(db1_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v, int _n);
 int dbt_update(db1_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v,
 	      db_key_t* _uk, db_val_t* _uv, int _n, int _un);
 
+/*
+ * replace a row in table
+ */
+int dbt_replace(db1_con_t* _h, db_key_t* _k, db_val_t* _v,
+	      int _n, int _nk, int _m);
+
 /*
  * Affected rows
  */

+ 1 - 0
modules/debugger/Makefile

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

+ 31 - 14
modules/debugger/README

@@ -41,6 +41,7 @@ Daniel-Constantin Mierla
               3.14. log_assign (int)
               3.15. cfgpkgcheck (int)
               3.16. reset_msgid (int)
+              3.17. cfgtest (int)
 
         4. Functions
 
@@ -76,9 +77,10 @@ Daniel-Constantin Mierla
    1.14. Set log_assign parameter
    1.15. Set cfgpkgcheck parameter
    1.16. Set reset_msgid parameter
-   1.17. dbg_breakpoint usage
-   1.18. dbg_pv_dump usage
-   1.19. dbg_sip_msg usage
+   1.17. Set cfgtest parameter
+   1.18. dbg_breakpoint usage
+   1.19. dbg_pv_dump usage
+   1.20. dbg_sip_msg usage
 
 Chapter 1. Admin Guide
 
@@ -108,6 +110,7 @@ Chapter 1. Admin Guide
         3.14. log_assign (int)
         3.15. cfgpkgcheck (int)
         3.16. reset_msgid (int)
+        3.17. cfgtest (int)
 
    4. Functions
 
@@ -183,6 +186,7 @@ Chapter 1. Admin Guide
    3.14. log_assign (int)
    3.15. cfgpkgcheck (int)
    3.16. reset_msgid (int)
+   3.17. cfgtest (int)
 
 3.1. cfgtrace (int)
 
@@ -389,13 +393,26 @@ modparam("debugger", "cfgpkgcheck", 1)
 modparam("debugger", "reset_msgid", 1)
 ...
 
+3.17. cfgtest (int)
+
+   Control whether the cfgt module is enabled or disabled at startup.
+   Module cfgt needs to be loaded before.
+
+   Default value is "0" (disabled).
+
+   Example 1.17. Set cfgtest parameter
+...
+loadmodule "cfgt.so"
+modparam("debugger", "cfgtest", 1)
+...
+
 4. Functions
 
    4.1. dbg_breakpoint(mode)
    4.2. dbg_pv_dump([mask] [, level])
    4.3. dbg_sip_msg([log_level], [facility])
 
-4.1. dbg_breakpoint(mode)
+4.1.  dbg_breakpoint(mode)
 
    Anchor a breakpoint at the current line of the config (the one on which
    this function is called). The 'mode' specifies whether the breakpoint
@@ -404,13 +421,13 @@ modparam("debugger", "reset_msgid", 1)
    Note that this version of the module does not export this anchors to
    RPC for interactive debugging (temporarily disabled).
 
-   Example 1.17. dbg_breakpoint usage
+   Example 1.18. dbg_breakpoint usage
 ...
 if($si=="10.0.0.10")
         dbg_breakpoint("1");
 ...
 
-4.2. dbg_pv_dump([mask] [, level])
+4.2.  dbg_pv_dump([mask] [, level])
 
    Prints the content of pv_cache on json format. Defaults are mask=31 and
    level = "L_DBG"
@@ -432,7 +449,7 @@ if($si=="10.0.0.10")
      * L_INFO - log level 2
      * L_DBG - log level 3
 
-   Example 1.18. dbg_pv_dump usage
+   Example 1.19. dbg_pv_dump usage
 ...
 $var(temp) = 1;
 $avp(s:more_avp) = 2;
@@ -455,7 +472,7 @@ vp(x)":[{"different":["foo"]},{"other":[2,1],"more":["hi","bye"]}],"$T_branch_id
 x":0,"$var(empty)":0}
  ...
 
-4.3. dbg_sip_msg([log_level], [facility])
+4.3.  dbg_sip_msg([log_level], [facility])
 
    Prints how the sip message would look like if it would be sent out at
    that point in the config(i.e. if the current lump lists would have been
@@ -475,7 +492,7 @@ x":0,"$var(empty)":0}
    force the lump application using msg_apply_changes() function from
    textopsx module.
 
-   Example 1.19. dbg_sip_msg usage
+   Example 1.20. dbg_sip_msg usage
 ...
     dbg_sip_msg();
     dbg_sip_msg("L_ERR");
@@ -512,7 +529,7 @@ P-Hint: My hint
    5.4. dbg.mod_level
    5.5. dbg.reset_msgid
 
-5.1. dbg.ls
+5.1.  dbg.ls
 
    List Kamailio processes with info related to interactive debugging.
 
@@ -526,7 +543,7 @@ P-Hint: My hint
                 dbg.ls
                 dbg.ls 1234
 
-5.2. dbg.trace
+5.2.  dbg.trace
 
    Control config script running trace.
 
@@ -543,7 +560,7 @@ P-Hint: My hint
                 dbg.trace off
                 dbg.trace on 1234
 
-5.3. dbg.bp
+5.3.  dbg.bp
 
    Control breakpoints and config execution.
 
@@ -581,7 +598,7 @@ P-Hint: My hint
                 dbg.bp eval 1234 $fu
                 dbg.bp move 1234
 
-5.4. dbg.mod_level
+5.4.  dbg.mod_level
 
    Specify module log level.
 
@@ -595,7 +612,7 @@ P-Hint: My hint
                 dbg.mod_level core 3
                 dbg.mod_level tm 3
 
-5.5. dbg.reset_msgid
+5.5.  dbg.reset_msgid
 
    Resets the message sequence ($mi). Internally there is no real change.
    This can be useful for unit test cases in order to be able to replicate

+ 148 - 12
modules/debugger/debugger_api.c

@@ -26,6 +26,7 @@
 #include <stdlib.h>
 #include <unistd.h>
 
+#include "../cfgt/cfgt.h"
 #include "../../dprint.h"
 #include "../../events.h"
 #include "../../locking.h"
@@ -70,6 +71,7 @@ str *dbg_get_state_name(int t)
 #define DBG_CFGTRACE_ON	(1<<0)
 #define DBG_ABKPOINT_ON	(1<<1)
 #define DBG_LBKPOINT_ON	(1<<2)
+#define DBG_CFGTEST_ON	(1<<3)
 
 static str _dbg_status_list[] = {
 	str_init("cfgtrace-on"),
@@ -78,6 +80,8 @@ static str _dbg_status_list[] = {
 	str_init("abkpoint-off"),
 	str_init("lbkpoint-on"),
 	str_init("lbkpoint-off"),
+	str_init("cfgtest-on"),
+	str_init("cfgtest-off"),
 	{0, 0}
 };
 
@@ -89,6 +93,8 @@ str *dbg_get_status_name(int t)
 		return &_dbg_status_list[2];
 	if(t&DBG_LBKPOINT_ON)
 		return &_dbg_status_list[4];
+	if(t&DBG_CFGTEST_ON)
+		return &_dbg_status_list[6];
 
 	return &_dbg_state_list[0];
 }
@@ -188,6 +194,12 @@ int _dbg_step_loops = 200;
  */
 int _dbg_reset_msgid = 0;
 
+/**
+ * disabled by default
+ */
+int _dbg_cfgtest = 0;
+cfgt_api_t _dbg_cfgt;
+
 /**
  *
  */
@@ -356,6 +368,11 @@ int dbg_cfg_trace(void *data)
 				);
 		}
 	}
+	if(_dbg_pid_list[process_no].set&DBG_CFGTEST_ON)
+	{
+		if(_dbg_cfgt.cfgt_process_route(msg, a)<0)
+				LM_ERR("Error processing route\n");
+	}
 	if(!(_dbg_pid_list[process_no].set&DBG_ABKPOINT_ON))
 	{
 		/* no breakpoints to be considered */
@@ -590,6 +607,8 @@ int dbg_init_mypid(void)
 		_dbg_pid_list[process_no].set |= DBG_ABKPOINT_ON;
 	if(_dbg_cfgtrace==1)
 		_dbg_pid_list[process_no].set |= DBG_CFGTRACE_ON;
+	if(_dbg_cfgtest==1)
+		_dbg_pid_list[process_no].set |= DBG_CFGTEST_ON;
 	if(_dbg_reset_msgid==1)
 	{
 		LM_DBG("[%d] create locks\n", process_no);
@@ -962,12 +981,12 @@ static void  dbg_rpc_trace(rpc_t* rpc, void* ctx)
 /**
  *
  */
-static const char* dbg_rpc_mod_level_doc[2] = {
-	"Specify module log level",
+static const char* dbg_rpc_set_mod_level_doc[2] = {
+	"Set module log level",
 	0
 };
 
-static void dbg_rpc_mod_level(rpc_t* rpc, void* ctx){
+static void dbg_rpc_set_mod_level(rpc_t* rpc, void* ctx){
 	int l;
 	str value = {0,0};
 
@@ -988,12 +1007,12 @@ static void dbg_rpc_mod_level(rpc_t* rpc, void* ctx){
 /**
  *
  */
-static const char* dbg_rpc_mod_facility_doc[2] = {
-	"Specify module log facility",
+static const char* dbg_rpc_set_mod_facility_doc[2] = {
+	"Set module log facility",
 	0
 };
 
-static void dbg_rpc_mod_facility(rpc_t* rpc, void* ctx) {
+static void dbg_rpc_set_mod_facility(rpc_t* rpc, void* ctx) {
 	int fl;
 	str value = {0, 0};
 	str facility = {0, 0};
@@ -1017,6 +1036,55 @@ static void dbg_rpc_mod_facility(rpc_t* rpc, void* ctx) {
 	rpc->add(ctx, "s", "200 ok");
 }
 
+/**
+ *
+ */
+static const char* dbg_rpc_get_mod_level_doc[2] = {
+	"Get module log level",
+	0
+};
+
+static void dbg_rpc_get_mod_level(rpc_t* rpc, void* ctx){
+	int l;
+	str value = {0,0};
+
+	if (rpc->scan(ctx, "S", &value) < 1)
+	{
+		rpc->fault(ctx, 500, "invalid parameters");
+		return;
+	}
+
+	l = get_debug_level(value.s, value.len);
+
+	rpc->add(ctx, "d", l);
+}
+
+/**
+ *
+ */
+static const char* dbg_rpc_get_mod_facility_doc[2] = {
+	"Get module log facility",
+	0
+};
+
+static void dbg_rpc_get_mod_facility(rpc_t* rpc, void* ctx) {
+	int fl;
+	str value = {0, 0};
+	str facility = {0, 0};
+
+	if (rpc->scan(ctx, "S", &value) < 1)
+	{
+	    rpc->fault(ctx, 500, "invalid parameters");
+	    return;
+	}
+
+	fl = get_debug_facility(value.s, value.len);
+	facility.s = facility2str(fl, &facility.len);
+
+	rpc->add(ctx, "S", &facility);
+}
+
+
 /**
  *
  */
@@ -1057,8 +1125,10 @@ rpc_export_t dbg_rpc[] = {
 	{"dbg.bp",        dbg_rpc_bp,        dbg_rpc_bp_doc,        0},
 	{"dbg.ls",        dbg_rpc_list,      dbg_rpc_list_doc,      0},
 	{"dbg.trace",     dbg_rpc_trace,     dbg_rpc_trace_doc,     0},
-	{"dbg.mod_level", dbg_rpc_mod_level, dbg_rpc_mod_level_doc, 0},
-	{"dbg.mod_facility", dbg_rpc_mod_facility, dbg_rpc_mod_facility_doc, 0},
+	{"dbg.set_mod_level", dbg_rpc_set_mod_level, dbg_rpc_set_mod_level_doc, 0},
+	{"dbg.set_mod_facility", dbg_rpc_set_mod_facility, dbg_rpc_set_mod_facility_doc, 0},
+	{"dbg.get_mod_level", dbg_rpc_get_mod_level, dbg_rpc_get_mod_level_doc, 0},
+	{"dbg.get_mod_facility", dbg_rpc_get_mod_facility, dbg_rpc_get_mod_facility_doc, 0},
 	{"dbg.reset_msgid", dbg_rpc_reset_msgid, dbg_rpc_reset_msgid_doc, 0},
 	{0, 0, 0, 0}
 };
@@ -1119,6 +1189,7 @@ int dbg_init_mod_levels(int dbg_mod_hash_size)
 		return -1;
 	}
 	memset(_dbg_mod_table, 0, _dbg_mod_table_size*sizeof(dbg_mod_slot_t));
+	LM_DBG("Created _dbg_mod_table, size %d\n", _dbg_mod_table_size);
 
 	for(i=0; i<_dbg_mod_table_size; i++)
 	{
@@ -1141,6 +1212,63 @@ int dbg_init_mod_levels(int dbg_mod_hash_size)
 	return 0;
 }
 
+/**
+ *
+ */
+int dbg_destroy_mod_levels()
+{
+	int i;
+	dbg_mod_level_t *itl = NULL;
+	dbg_mod_level_t *itlp = NULL;
+
+	dbg_mod_facility_t *itf = NULL;
+	dbg_mod_facility_t *itfp = NULL;
+
+	if (_dbg_mod_table_size <= 0)
+		return 0;
+
+	if (_dbg_mod_table == NULL)
+		return 0;
+
+	for (i = 0; i < _dbg_mod_table_size; i++) {
+		// destroy level list
+		lock_get(&_dbg_mod_table[i].lock);
+		itl = _dbg_mod_table[i].first;
+		while (itl) {
+			itlp = itl;
+			itl = itl->next;
+			shm_free(itlp);
+		}
+		lock_release(&_dbg_mod_table[i].lock);
+
+		// destroy facility list
+		lock_get(&_dbg_mod_table[i].lock_ft);
+		itf = _dbg_mod_table[i].first_ft;
+		while (itf) {
+			itfp = itf;
+			itf = itf->next;
+			shm_free(itfp);
+		}
+		lock_release(&_dbg_mod_table[i].lock_ft);
+
+		// destroy locks
+		lock_destroy(&_dbg_mod_table[i].lock);
+		lock_destroy(&_dbg_mod_table[i].lock_ft);
+
+		// reset all
+		_dbg_mod_table[i].first = NULL;
+		_dbg_mod_table[i].first_ft = NULL;
+	}
+
+	// free table
+	shm_free(_dbg_mod_table);
+	_dbg_mod_table = NULL;
+
+	LM_DBG("Destroyed _dbg_mod_table, size %d\n", _dbg_mod_table_size);
+
+	return 0;
+}
+
 /*
  * case insensitive hashing - clone here to avoid usage of LOG*()
  * - s1 - str to hash
@@ -1216,15 +1344,14 @@ int dbg_set_mod_debug_level(char *mname, int mnlen, int *mlevel)
 		itp = it;
 		it = it->next;
 	}
+	lock_release(&_dbg_mod_table[idx].lock);
 	/* not found - add */
 	if(mlevel==NULL) {
-		lock_release(&_dbg_mod_table[idx].lock);
 		return 0;
 	}
 	itn = (dbg_mod_level_t*)shm_malloc(sizeof(dbg_mod_level_t) + (mnlen+1)*sizeof(char));
 	if(itn==NULL) {
 		LM_ERR("no more shm\n");
-		lock_release(&_dbg_mod_table[idx].lock);
 		return -1;
 	}
 	memset(itn, 0, sizeof(dbg_mod_level_t) + (mnlen+1)*sizeof(char));
@@ -1235,6 +1362,7 @@ int dbg_set_mod_debug_level(char *mname, int mnlen, int *mlevel)
 	strncpy(itn->name.s, mname, mnlen);
 	itn->name.s[itn->name.len] = '\0';
 
+	lock_get(&_dbg_mod_table[idx].lock);
 	if(itp==NULL) {
 		itn->next = _dbg_mod_table[idx].first;
 		_dbg_mod_table[idx].first = itn;
@@ -1292,15 +1420,14 @@ int dbg_set_mod_debug_facility(char *mname, int mnlen, int *mfacility)
 		itp = it;
 		it = it->next;
 	}
+	lock_release(&_dbg_mod_table[idx].lock_ft);
 	/* not found - add */
 	if(mfacility==NULL) {
-		lock_release(&_dbg_mod_table[idx].lock_ft);
 		return 0;
 	}
 	itn = (dbg_mod_facility_t*)shm_malloc(sizeof(dbg_mod_facility_t) + (mnlen+1)*sizeof(char));
 	if(itn==NULL) {
 		LM_ERR("no more shm\n");
-		lock_release(&_dbg_mod_table[idx].lock_ft);
 		return -1;
 	}
 	memset(itn, 0, sizeof(dbg_mod_facility_t) + (mnlen+1)*sizeof(char));
@@ -1311,6 +1438,7 @@ int dbg_set_mod_debug_facility(char *mname, int mnlen, int *mfacility)
 	strncpy(itn->name.s, mname, mnlen);
 	itn->name.s[itn->name.len] = '\0';
 
+	lock_get(&_dbg_mod_table[idx].lock_ft);
 	if(itp==NULL) {
 		itn->next = _dbg_mod_table[idx].first_ft;
 		_dbg_mod_table[idx].first_ft = itn;
@@ -1335,6 +1463,10 @@ int dbg_get_mod_debug_level(char *mname, int mnlen, int *mlevel)
 	if(_dbg_mod_table==NULL)
 		return -1;
 
+	if (!dbg_cfg) {
+		return -1;
+	}
+
 	if(cfg_get(dbg, dbg_cfg, mod_level_mode)==0)
 		return -1;
 
@@ -1378,6 +1510,10 @@ int dbg_get_mod_debug_facility(char *mname, int mnlen, int *mfacility)
 	if(_dbg_mod_table==NULL)
 		return -1;
 
+	if (!dbg_cfg) {
+		return -1;
+	}
+
 	if(cfg_get(dbg, dbg_cfg, mod_facility_mode)==0)
 		return -1;
 

+ 1 - 0
modules/debugger/debugger_api.h

@@ -34,6 +34,7 @@ int dbg_init_mypid(void);
 int dbg_init_rpc(void);
 
 int dbg_init_mod_levels(int _dbg_mod_hash_size);
+int dbg_destroy_mod_levels();
 int dbg_set_mod_debug_level(char *mname, int mnlen, int *mlevel);
 int dbg_set_mod_debug_facility(char *mname, int mnlen, int *mfacility);
 void dbg_enable_mod_levels(void);

+ 472 - 154
modules/debugger/debugger_mod.c

@@ -27,6 +27,9 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "../cfgt/cfgt.h"
+#include "../../lib/kmi/mi.h"
+#include "../../lib/kmi/tree.h"
 #include "../../sr_module.h"
 #include "../../dprint.h"
 #include "../../ut.h"
@@ -60,6 +63,12 @@ static int w_dbg_sip_msg(struct sip_msg* msg, char *level, char *facility);
 
 extern char* dump_lump_list(struct lump *list, int s_offset, char *s_buf);
 
+/* mi commands */
+static struct mi_root* mi_set_dbg_mod_level(struct mi_root *cmd_tree, void *param);
+static struct mi_root* mi_set_dbg_mod_facility(struct mi_root *cmd_tree, void *param);
+static struct mi_root* mi_get_dbg_mod_level(struct mi_root *cmd_tree, void *param);
+static struct mi_root* mi_get_dbg_mod_facility(struct mi_root *cmd_tree, void *param);
+
 /* parameters */
 extern int _dbg_cfgtrace;
 extern int _dbg_cfgpkgcheck;
@@ -71,6 +80,10 @@ extern char *_dbg_cfgtrace_lname;
 extern int _dbg_step_usleep;
 extern int _dbg_step_loops;
 extern int _dbg_reset_msgid;
+extern int _dbg_cfgtest;
+
+/* cfgt api */
+extern cfgt_api_t _dbg_cfgt;
 
 static int _dbg_sip_msg_cline;
 static char * _dbg_cfgtrace_facility_str = 0;
@@ -85,12 +98,12 @@ static cmd_export_t cmds[]={
 		fixup_dbg_pv_dump, 0, ANY_ROUTE},
 	{"dbg_pv_dump", (cmd_function)w_dbg_dump, 2,
 		fixup_dbg_pv_dump, 0, ANY_ROUTE},
-    {"dbg_sip_msg", (cmd_function)w_dbg_sip_msg, 0,
-        fixup_dbg_sip_msg, 0, REQUEST_ROUTE},
-    {"dbg_sip_msg", (cmd_function)w_dbg_sip_msg, 1,
-        fixup_dbg_sip_msg, 0, REQUEST_ROUTE},
-    {"dbg_sip_msg", (cmd_function)w_dbg_sip_msg, 2,
-        fixup_dbg_sip_msg, 0, REQUEST_ROUTE},
+	{"dbg_sip_msg", (cmd_function)w_dbg_sip_msg, 0,
+		fixup_dbg_sip_msg, 0, REQUEST_ROUTE|ONREPLY_ROUTE},
+	{"dbg_sip_msg", (cmd_function)w_dbg_sip_msg, 1,
+		fixup_dbg_sip_msg, 0, REQUEST_ROUTE|ONREPLY_ROUTE},
+	{"dbg_sip_msg", (cmd_function)w_dbg_sip_msg, 2,
+		fixup_dbg_sip_msg, 0, REQUEST_ROUTE|ONREPLY_ROUTE},
 	{0, 0, 0, 0, 0, 0}
 };
 
@@ -111,16 +124,25 @@ static param_export_t params[]={
 	{"mod_facility",      PARAM_STRING|USE_FUNC_PARAM, (void*)dbg_mod_facility_param},
 	{"reset_msgid",       INT_PARAM, &_dbg_reset_msgid},
 	{"cfgpkgcheck",       INT_PARAM, &_dbg_cfgpkgcheck},
+	{"cfgtest",           INT_PARAM, &_dbg_cfgtest},
 	{0, 0, 0}
 };
 
+static mi_export_t mi_cmds[] = {
+	{"set_dbg_mod_level",         mi_set_dbg_mod_level, 0, 0, 0},
+	{"set_dbg_mod_facility",      mi_set_dbg_mod_facility, 0, 0, 0},
+	{"get_dbg_mod_level",         mi_get_dbg_mod_level, 0, 0, 0},
+	{"get_dbg_mod_facility",      mi_get_dbg_mod_facility, 0, 0, 0},
+	{ 0, 0, 0, 0, 0}
+};
+
 struct module_exports exports = {
 	"debugger",
 	DEFAULT_DLFLAGS, /* dlopen flags */
 	cmds,
 	params,
 	0,
-	0,              /* exported MI functions */
+	mi_cmds,        /* exported MI functions */
 	0,              /* exported pseudo-variables */
 	0,              /* extra processes */
 	mod_init,       /* module initialization function */
@@ -130,12 +152,267 @@ struct module_exports exports = {
 };
 
 
+static struct mi_root* mi_set_dbg_mod_level(struct mi_root *cmd_tree, void *param) {
+	struct mi_node *node;
+	str mod_str, level_str;
+	int l;
+
+	/* get first param */
+	node = cmd_tree->node.kids;
+	if (node == NULL) {
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+	}
+
+	if (node->value.s == NULL || node->value.len == 0) {
+		return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
+	}
+
+	/* get module str */
+	mod_str = node->value;
+
+	/* get second param */
+	node = node->next;
+	if (node == NULL) {
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+	}
+
+	if (node->value.s == NULL || node->value.len == 0) {
+		return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
+	}
+
+	/* get level str */
+	level_str = node->value;
+
+	/* no further params expected */
+	node = node->next;
+	if (node != NULL) {
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+	}
+
+	/* get level int */
+	if (str2sint(&level_str, &l) < 0) {
+		LM_ERR("invalid parameter - level value: %.*s\n",
+			level_str.len, level_str.s);
+		return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
+	}
+
+	/* set level int */
+	if (default_dbg_cfg.mod_hash_size <= 0 || default_dbg_cfg.mod_level_mode <= 0) {
+		LM_ERR("can't set level for module=%.*s; enable mod_hash_size and mod_level_mode config parameters!\n",
+			mod_str.len, mod_str.s);
+		return init_mi_tree(500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
+	} else if (dbg_set_mod_debug_level(mod_str.s, mod_str.len, &l) < 0) {
+		LM_ERR("failed set level for module=%.*s\n", mod_str.len, mod_str.s);
+		return init_mi_tree(500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
+	} else {
+		LM_DBG("module=%.*s level_str=%.*s level_int=%d\n",
+			mod_str.len, mod_str.s, level_str.len, level_str.s, l);
+	}
+
+	return init_mi_tree(200, MI_OK_S, MI_OK_LEN);
+}
+
+static struct mi_root* mi_set_dbg_mod_facility(struct mi_root *cmd_tree, void *param) {
+	struct mi_node *node;
+	str mod_str, facility_str;
+	int fl;
+
+	/* get first param */
+	node = cmd_tree->node.kids;
+	if (node == NULL) {
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+	}
+
+	if (node->value.s == NULL || node->value.len == 0) {
+		return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
+	}
+
+	/* get module str */
+	mod_str = node->value;
+
+	/* get second param */
+	node = node->next;
+	if (node == NULL) {
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+	}
+
+	if (node->value.s == NULL || node->value.len == 0) {
+		return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
+	}
+
+	/* get facility str */
+	facility_str = node->value;
+
+	/* no further params expected */
+	node = node->next;
+	if (node != NULL) {
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+	}
+
+	/* get facility int */
+	facility_str.s[facility_str.len] = '\0';
+	if ((fl = str2facility(facility_str.s)) == -1) {
+		LM_ERR("invalid parameter - facility value: %.*s\n",
+			facility_str.len, facility_str.s);
+		return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
+	}
+
+	/* set facility int */
+	if (default_dbg_cfg.mod_hash_size <= 0 || default_dbg_cfg.mod_facility_mode <= 0) {
+		LM_ERR("can't set facility for module=%.*s; enable mod_hash_size and mod_facility_mode config parameters!\n",
+			mod_str.len, mod_str.s);
+		return init_mi_tree(500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
+	} else if (dbg_set_mod_debug_facility(mod_str.s, mod_str.len, &fl) < 0) {
+		LM_ERR("failed set facility for module=%.*s\n", mod_str.len, mod_str.s);
+		return init_mi_tree(500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
+	} else {
+		LM_DBG("module=%.*s facility_str=%.*s facility_int=%d\n",
+			mod_str.len, mod_str.s, facility_str.len, facility_str.s, fl);
+	}
+
+	return init_mi_tree(200, MI_OK_S, MI_OK_LEN);
+}
+
+static struct mi_root* mi_get_dbg_mod_level(struct mi_root *cmd_tree, void *param) {
+	struct mi_node *node, *crt_node;
+	struct mi_root *root = NULL;
+	struct mi_attr *attr;
+	int l;
+	str mod_str, level_str;
+	str level_attr = {"level", strlen("level")};
+
+	/* get first param */
+	node = cmd_tree->node.kids;
+	if (node == NULL) {
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+	}
+
+	if (node->value.s == NULL || node->value.len == 0) {
+		return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
+	}
+
+	/* get module str */
+	mod_str = node->value;
+
+	/* no further params expected */
+	node = node->next;
+	if (node != NULL) {
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+	}
+
+	/* get module log level */
+	l = get_debug_level(mod_str.s, mod_str.len);
+	level_str.s = sint2str(l, &level_str.len);
+	LM_DBG("module=%.*s level_str=%.*s level_int=%d\n",
+		mod_str.len, mod_str.s, level_str.len, level_str.s, l);
+
+	/* return module log level */
+	root = init_mi_tree(200, MI_OK_S, MI_OK_LEN);
+	if (!root) {
+		LM_ERR("the MI tree cannot be initialized!\n");
+		goto error;
+	}
+	node = &root->node;
+
+	if (!(crt_node = add_mi_node_child(node, 0, mod_str.s, mod_str.len, 0, 0)) ) {
+		LM_ERR("cannot add the child node to the tree\n");
+		goto error;
+	}
+
+	if ((attr = add_mi_attr(crt_node, MI_DUP_VALUE,
+		level_attr.s, level_attr.len,
+		level_str.s, level_str.len)) == 0) {
+		LM_ERR("cannot add attributes to the node\n");
+		goto error;
+	}
+
+	return root;
+
+error:
+	if (root) {
+		free_mi_tree(root);
+	}
+
+	return NULL;
+}
+
+static struct mi_root* mi_get_dbg_mod_facility(struct mi_root *cmd_tree, void *param) {
+	struct mi_node *node, *crt_node;
+	struct mi_root *root = NULL;
+	struct mi_attr *attr;
+	int fl;
+	str mod_str, facility_str;
+	str facility_attr = {"facility", strlen("facility")};
+
+	/* get first param */
+	node = cmd_tree->node.kids;
+	if (node == NULL) {
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+	}
+
+	if (node->value.s == NULL || node->value.len == 0) {
+		return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
+	}
+
+	/* get module str */
+	mod_str = node->value;
+
+	/* no further params expected */
+	node = node->next;
+	if (node != NULL) {
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+	}
+
+	/* get module log facility */
+	fl = get_debug_facility(mod_str.s, mod_str.len);
+	facility_str.s = facility2str(fl, &facility_str.len);
+	LM_DBG("module=%.*s facility_str=%.*s facility_int=%d\n",
+		mod_str.len, mod_str.s, facility_str.len, facility_str.s, fl);
+
+	/* return module log facility */
+	root = init_mi_tree(200, MI_OK_S, MI_OK_LEN);
+	if (!root) {
+		LM_ERR("the MI tree cannot be initialized!\n");
+		goto error;
+	}
+	node = &root->node;
+
+	if (!(crt_node = add_mi_node_child(node, 0, mod_str.s, mod_str.len, 0, 0)) ) {
+		LM_ERR("cannot add the child node to the tree\n");
+		goto error;
+	}
+
+	if ((attr = add_mi_attr(crt_node, MI_DUP_VALUE,
+		facility_attr.s, facility_attr.len,
+		facility_str.s, facility_str.len)) == 0) {
+		LM_ERR("cannot add attributes to the node\n");
+		goto error;
+	}
+
+	return root;
+
+error:
+	if (root) {
+		free_mi_tree(root);
+	}
+
+	return NULL;
+}
+
 /**
  * init module function
  */
 static int mod_init(void)
 {
 	int fl;
+	bind_cfgt_t bind_cfgt;
+
+	if (register_mi_mod(exports.name, mi_cmds) != 0)
+	{
+		LM_ERR("failed to register MI commands\n");
+		return -1;
+	}
+
 	if (_dbg_cfgtrace_facility_str!=NULL)
 	{
 		fl = str2facility(_dbg_cfgtrace_facility_str);
@@ -159,6 +436,12 @@ static int mod_init(void)
 		LM_ERR("Fail to declare the configuration\n");
 		return -1;
 	}
+
+	/* anyhow, should fail before */
+	if (!dbg_cfg) {
+		return -1;
+	}
+
 	LM_DBG("cfg level_mode:%d facility_mode:%d hash_size:%d\n",
 		cfg_get(dbg, dbg_cfg, mod_level_mode),
 		cfg_get(dbg, dbg_cfg, mod_facility_mode),
@@ -187,6 +470,19 @@ static int mod_init(void)
 			return -1;
 		}
 	}
+	if(_dbg_cfgtest==1)
+	{
+		bind_cfgt = (bind_cfgt_t)find_export("cfgt_bind_cfgt", 1, 0);
+		if (!bind_cfgt) {
+			LM_ERR("can't find cfgt module\n");
+			return -1;
+		}
+
+		if (bind_cfgt(&_dbg_cfgt) < 0) {
+			return -1;
+		}
+		LM_INFO("bind to cfgt module\n");
+	}
 	return dbg_init_bp_list();
 }
 
@@ -210,6 +506,8 @@ static int child_init(int rank)
  */
 static void mod_destroy(void)
 {
+	dbg_cfg = NULL;
+	dbg_destroy_mod_levels();
 }
 
 /**
@@ -258,7 +556,7 @@ static int fixup_dbg_pv_dump(void** param, int param_no)
 		break;
 	}
 
-    return 0;
+	return 0;
 }
 
 /**
@@ -306,7 +604,7 @@ static int fixup_dbg_breakpoint(void** param, int param_no)
 	a = dbg_fixup_get_action(param, param_no);
 	p = (char*)(*param);
 
-    return dbg_add_breakpoint(a, (*p=='0')?0:1);
+	return dbg_add_breakpoint(a, (*p=='0')?0:1);
 }
 
 static int dbg_mod_level_param(modparam_t type, void *val)
@@ -331,19 +629,27 @@ static int dbg_mod_level_param(modparam_t type, void *val)
 	}
 	s.s = (char*)val;
 	s.len = p - s.s;
+
+	if (!dbg_cfg) {
+		return -1;
+	}
+
 	LM_DBG("cfg level_mode:%d hash_size:%d\n",
 		cfg_get(dbg, dbg_cfg, mod_level_mode),
 		cfg_get(dbg, dbg_cfg, mod_hash_size));
+
 	if(dbg_init_mod_levels(cfg_get(dbg, dbg_cfg, mod_hash_size))<0)
 	{
 		LM_ERR("failed to init per module log level\n");
 		return -1;
 	}
+
 	if(dbg_set_mod_debug_level(s.s, s.len, &l)<0)
 	{
 		LM_ERR("cannot store parameter: %s\n", (char*)val);
 		return -1;
 	}
+
 	return 0;
 
 }
@@ -371,65 +677,85 @@ static int dbg_mod_facility_param(modparam_t type, void *val)
 
 	s.s = (char*)val;
 	s.len = p - s.s;
+
+	if (!dbg_cfg) {
+		return -1;
+	}
+
 	LM_DBG("cfg facility_mode:%d hash_size:%d\n",
 		cfg_get(dbg, dbg_cfg, mod_facility_mode),
 		cfg_get(dbg, dbg_cfg, mod_hash_size));
+
 	if(dbg_init_mod_levels(cfg_get(dbg, dbg_cfg, mod_hash_size))<0)
 	{
 		LM_ERR("failed to init per module log level\n");
 		return -1;
 	}
+
 	if(dbg_set_mod_debug_facility(s.s, s.len, &fl)<0)
 	{
 		LM_ERR("cannot store parameter: %s\n", (char*)val);
 		return -1;
 	}
+
 	return 0;
 }
 
 static int fixup_dbg_sip_msg(void** param, int param_no)
 {
-    int facility;
-    int level;
-    struct action *dbg_sip_msg_action;
-
-    switch(param_no)
-    {
-        case 2:
-            facility = str2facility((char*)*(param));
-            if (facility == -1) {
-                LM_ERR("invalid log facility configured");
-                return E_UNSPEC;
-            }
-
-                         *param = (void*)(long)facility;    
-        break;
-        case 1:
-            switch(((char*)(*param))[2])
-            {
-                /* add L_OFFSET because L_WARN is consdered null pointer */
-                case 'A': level = L_ALERT + L_OFFSET; break;
-                case 'B': level = L_BUG + L_OFFSET; break;
-                case 'C': level = L_CRIT2 + L_OFFSET; break;
-                case 'E': level = L_ERR + L_OFFSET; break;
-                case 'W': level = L_WARN + L_OFFSET; break;
-                case 'N': level = L_NOTICE + L_OFFSET; break;
-                case 'I': level = L_INFO + L_OFFSET; break;
-                case 'D': level = L_DBG + L_OFFSET; break;
-                default:
-                    LM_ERR("unknown log level\n");
-                    return E_UNSPEC;
-            }
-
-            *param = (void*)(long)level;
-        break;
-    }
-
-    /* save the config line where this config function was called */
-    dbg_sip_msg_action = dbg_fixup_get_action(param, param_no);
-    _dbg_sip_msg_cline = dbg_sip_msg_action->cline;
-
-     return 0;
+	int facility;
+	int level;
+	struct action *dbg_sip_msg_action;
+
+	LM_DBG("dbg_sip_msg() called with %d params\n", param_no);
+
+	switch(param_no)
+	{
+		case 2:
+			facility = str2facility((char*)*(param));
+			if (facility == -1) {
+				LM_ERR("invalid log facility configured");
+				return E_UNSPEC;
+			}
+
+			*param = (void*)(long)facility;
+			break;
+
+		case 1:
+			switch(((char*)(*param))[2])
+			{
+				/* add L_OFFSET because L_WARN is consdered null pointer */
+				case 'A': level = L_ALERT + L_OFFSET; break;
+				case 'B': level = L_BUG + L_OFFSET; break;
+				case 'C': level = L_CRIT2 + L_OFFSET; break;
+				case 'E': level = L_ERR + L_OFFSET; break;
+				case 'W': level = L_WARN + L_OFFSET; break;
+				case 'N': level = L_NOTICE + L_OFFSET; break;
+				case 'I': level = L_INFO + L_OFFSET; break;
+				case 'D': level = L_DBG + L_OFFSET; break;
+				default:
+					LM_ERR("unknown log level\n");
+					return E_UNSPEC;
+			}
+
+			*param = (void*)(long)level;
+			break;
+
+		case 0:
+			_dbg_sip_msg_cline = -1;
+			return 0;
+
+		default:
+			// should not reach here
+			_dbg_sip_msg_cline = -1;
+			return -1;
+	}
+
+	/* save the config line where this config function was called */
+	dbg_sip_msg_action = dbg_fixup_get_action(param, param_no);
+	_dbg_sip_msg_cline = dbg_sip_msg_action->cline;
+
+	return 0;
 }
 
 /**
@@ -438,108 +764,100 @@ static int fixup_dbg_sip_msg(void** param, int param_no)
   */
 static int w_dbg_sip_msg(struct sip_msg* msg, char *level, char *facility)
 {
-    int ilevel = cfg_get(core, core_cfg, debug);
-    int ifacility= cfg_get(core, core_cfg, log_facility);
-    int flag = FLAG_MSG_LUMPS_ONLY; // copy lumps only, not the whole message
-    unsigned int new_buf_offs=0, orig_offs = 0;
-    char *hdr_lumps = NULL;
-    char *bdy_lumps = NULL;
-    const char *start_txt = "------------------------- START OF SIP message debug --------------------------\n";
-    const char *hdr_txt =   "------------------------------ SIP header diffs -------------------------------\n";
-    const char *bdy_txt =   "------------------------------- SIP body diffs --------------------------------\n";
-    const char *end_txt =   "-------------------------- END OF SIP message debug ---------------------------\n\n";
-    struct dest_info send_info;
-    str obuf;
-
-    if (level != NULL) {
-        /* substract L_OFFSET previously added */
-        ilevel = (int)(long)level - L_OFFSET;
-    }
-
-    if (facility != NULL) {
-        ifacility = (int)(long)facility;
-    }
-
-    /* msg_apply_changes_f code needed to get the current msg */
-    init_dest_info(&send_info);
-    send_info.proto = PROTO_UDP;
-    if(msg->first_line.type == SIP_REPLY) {
-        obuf.s = generate_res_buf_from_sip_res(msg,
-                (unsigned int*)&obuf.len, BUILD_NO_VIA1_UPDATE);
-    } else {
-        obuf.s = build_req_buf_from_sip_req(msg,
-                (unsigned int*)&obuf.len, &send_info,
-                BUILD_NO_PATH|BUILD_NO_LOCAL_VIA|BUILD_NO_VIA1_UPDATE);
-    }
-
-    if(obuf.s == NULL)
-    {
-        LM_ERR("couldn't update msg buffer content\n");
-        return -1;
-    }
-
-    if(obuf.len >= BUF_SIZE)
-    {
-        LM_ERR("new buffer overflow (%d)\n", obuf.len);
-        pkg_free(obuf.s);
-        return -1;
-    }
-
-    /* skip original uri */
-    if (msg->new_uri.s){
-        orig_offs=msg->first_line.u.request.uri.s - msg->buf;
-        orig_offs=msg->first_line.u.request.uri.len;
-    }
-
-    /* alloc private mem and copy lumps */
-    hdr_lumps = pkg_malloc(BUF_SIZE);
-    bdy_lumps = pkg_malloc(BUF_SIZE);
-
-    new_buf_offs = 0;
-    process_lumps(msg, msg->add_rm, hdr_lumps, &new_buf_offs, &orig_offs, &send_info, flag);
-
-    new_buf_offs = 0;
-    process_lumps(msg, msg->body_lumps, bdy_lumps, &new_buf_offs, &orig_offs, &send_info, flag);
-
-    /* do the print */
-    if (hdr_lumps != NULL && bdy_lumps != NULL) {
-        LOG_FC(ifacility, ilevel, "CONFIG LINE %d\n%s%.*s%s%s%s%s%s",
-            _dbg_sip_msg_cline,
-            start_txt,
-            obuf.len, obuf.s,
-            hdr_txt, hdr_lumps,
-            bdy_txt, bdy_lumps,
-            end_txt);
-    } else if (hdr_lumps != NULL) {
-        LOG_FC(ifacility, ilevel, "CONFIG LINE %d\n%s%.*s%s%s%s",
-            _dbg_sip_msg_cline,
-            start_txt,
-            obuf.len, obuf.s,
-            hdr_txt, hdr_lumps,
-            end_txt);
-    } else if (bdy_lumps != NULL) {
-        LOG_FC(ifacility, ilevel, "CONFIG LINE %d\n%s%.*s%s%s%s",
-            _dbg_sip_msg_cline,
-            start_txt,
-            obuf.len, obuf.s,
-            bdy_txt, bdy_lumps,
-            end_txt);
-    } else {
-        LOG_FC(ifacility, ilevel, "CONFIG LINE %d\n%s%.*s%s",
-            _dbg_sip_msg_cline,
-            start_txt,
-            obuf.len, obuf.s,
-            end_txt);
-    }
-
-    /* free lumps */
-    if (hdr_lumps) {
-        pkg_free(hdr_lumps);
-    }
-
-    if (bdy_lumps) {
-        pkg_free(bdy_lumps);
-    }
-
-    return 1;
+	int ilevel = cfg_get(core, core_cfg, debug);
+	int ifacility= cfg_get(core, core_cfg, log_facility);
+	int flag = FLAG_MSG_LUMPS_ONLY; // copy lumps only, not the whole message
+	unsigned int new_buf_offs=0, orig_offs = 0;
+	char *hdr_lumps = NULL;
+	char *bdy_lumps = NULL;
+	const char *start_txt = "------------------------- START OF SIP message debug --------------------------\n";
+	const char *hdr_txt =   "------------------------------ SIP header diffs -------------------------------\n";
+	const char *bdy_txt =   "------------------------------- SIP body diffs --------------------------------\n";
+	const char *end_txt =   "-------------------------- END OF SIP message debug ---------------------------\n\n";
+	struct dest_info send_info;
+	str obuf;
+
+	if (msg->first_line.type != SIP_REPLY && get_route_type() != REQUEST_ROUTE) {
+		LM_ERR("invalid usage - not in request route\n");
+		return -1;
+	}
+
+	if (level != NULL) {
+		/* substract L_OFFSET previously added */
+		ilevel = (int)(long)level - L_OFFSET;
+	}
+
+	if (facility != NULL) {
+		ifacility = (int)(long)facility;
+	}
+
+	/* msg_apply_changes_f code needed to get the current msg */
+	init_dest_info(&send_info);
+	send_info.proto = PROTO_UDP;
+	if(msg->first_line.type == SIP_REPLY) {
+		obuf.s = generate_res_buf_from_sip_res(msg,
+				(unsigned int*)&obuf.len, BUILD_NO_VIA1_UPDATE);
+	} else {
+		obuf.s = build_req_buf_from_sip_req(msg,
+				(unsigned int*)&obuf.len, &send_info,
+				BUILD_NO_PATH|BUILD_NO_LOCAL_VIA|BUILD_NO_VIA1_UPDATE);
+	}
+
+	if(obuf.s == NULL)
+	{
+		LM_ERR("couldn't update msg buffer content\n");
+		return -1;
+	}
+
+	if(obuf.len >= BUF_SIZE)
+	{
+		LM_ERR("new buffer overflow (%d)\n", obuf.len);
+		pkg_free(obuf.s);
+		return -1;
+	}
+
+	/* skip original uri */
+	if (msg->new_uri.s){
+		orig_offs=msg->first_line.u.request.uri.s - msg->buf;
+		orig_offs=msg->first_line.u.request.uri.len;
+	}
+
+	/* alloc private mem and copy lumps */
+	hdr_lumps = pkg_malloc(BUF_SIZE);
+	bdy_lumps = pkg_malloc(BUF_SIZE);
+
+	new_buf_offs = 0;
+	process_lumps(msg, msg->add_rm, hdr_lumps, &new_buf_offs, &orig_offs, &send_info, flag);
+
+	new_buf_offs = 0;
+	process_lumps(msg, msg->body_lumps, bdy_lumps, &new_buf_offs, &orig_offs, &send_info, flag);
+
+	/* do the print */
+	if (_dbg_sip_msg_cline < 0 ) {
+		LOG_FC(ifacility, ilevel, "CONFIG LINE unknown\n%s%.*s%s%s%s%s%s",
+			start_txt,
+			obuf.len, obuf.s,
+			hdr_txt, hdr_lumps,
+			bdy_txt, bdy_lumps,
+			end_txt);
+	} else {
+		LOG_FC(ifacility, ilevel, "CONFIG LINE %d\n%s%.*s%s%s%s%s%s",
+			_dbg_sip_msg_cline,
+			start_txt,
+			obuf.len, obuf.s,
+			hdr_txt, hdr_lumps,
+			bdy_txt, bdy_lumps,
+			end_txt);
+	}
+
+	/* free lumps */
+	if (hdr_lumps) {
+		pkg_free(hdr_lumps);
+	}
+
+	if (bdy_lumps) {
+		pkg_free(bdy_lumps);
+	}
+
+	return 1;
 }

+ 181 - 0
modules/debugger/doc/debugger_admin.xml

@@ -57,6 +57,10 @@
 		    </listitem>
 	    	</itemizedlist>
 	    </para>
+	    <para>
+		NOTE: Due to the debugger module child_init() function, one should load the module first in the module sequence in order to initialize _dbg_pid_list.
+		Otherwise, another module (i.e. p_usrloc) forking a process with rank != PROC_INIT will fail.
+	    </para>
 	</section>
 	<section>
 	    <title>External Libraries or Applications</title>
@@ -418,6 +422,28 @@ modparam("debugger", "reset_msgid", 1)
 	    </example>
 	</section>
 
+	<section id="dbg.p.cfgtest">
+	    <title><varname>cfgtest</varname> (int)</title>
+	    <para>
+			Control whether the cfgt module is enabled or disabled
+			at startup. Module cfgt needs to be loaded before.
+	    </para>
+	    <para>
+		<emphasis>
+		    Default value is <quote>0</quote> (disabled).
+		</emphasis>
+	    </para>
+	    <example>
+		<title>Set <varname>cfgtest</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+loadmodule "cfgt.so"
+modparam("debugger", "cfgtest", 1)
+...
+</programlisting>
+	    </example>
+	</section>
+
 	</section>
 
     <section>
@@ -594,6 +620,83 @@ P-Hint: My hint
     </section>
 
 
+
+	<section>
+		<title>Exported MI Functions</title>
+
+		<section id="debugger.m.set_dbg_mod_level mod_name level">
+			<title><function moreinfo="none">set_dbg_mod_level mod_name level</function></title>
+			<para>
+				Set the module log level.
+				If module does not exist in kamailio, the entry in the level hashtable is still added for the bogus module.
+			</para>
+			<example>
+				<title><function moreinfo="none">set_dbg_mod_level</function> usage</title>
+				<programlisting format="linespecific">
+...
+$ &ctltool; fifo set_dbg_mod_level core 2
+$ &ctltool; fifo set_dbg_mod_level debugger 3
+...
+				</programlisting>
+			</example>
+		</section>
+
+		<section id="debugger.m.set_dbg_mod_facility mod_name facility">
+			<title><function moreinfo="none">set_dbg_mod_facility mod_name facility</function></title>
+			<para>
+				Set the mod_name log facility.
+				If mod_name does not exist in kamailio, the entry in the facility hashtable is still added for the bogus mod_name.
+			</para>
+			<example>
+				<title><function moreinfo="none">set_dbg_mod_facility</function> usage</title>
+				<programlisting format="linespecific">
+...
+$ &ctltool; fifo set_dbg_mod_facility core LOG_LOCAL1
+$ &ctltool; fifo set_dbg_mod_facility debugger LOG_LOCAL0
+...
+				</programlisting>
+			</example>
+		</section>
+
+		<section id="debugger.m.get_dbg_mod_level mod_name">
+			<title><function moreinfo="none">get_dbg_mod_level mod_name</function></title>
+			<para>
+				Get the mod_name log level.
+				If mod_name does not exist in the level hashtable, returns the config file value.
+			</para>
+			<example>
+				<title><function moreinfo="none">get_dbg_mod_level</function> usage</title>
+				<programlisting format="linespecific">
+...
+$ &ctltool; fifo get_dbg_mod_level core
+$ &ctltool; fifo get_dbg_mod_level debugger
+...
+				</programlisting>
+			</example>
+		</section>
+
+		<section id="debugger.m.get_dbg_mod_facility mod_name">
+			<title><function moreinfo="none">get_dbg_mod_facility mod_name</function></title>
+			<para>
+				Get the mod_name log facility.
+				If mod_name does not exist in the facility hashtable, returns the config file value.
+			</para>
+			<example>
+				<title><function moreinfo="none">get_dbg_mod_facility</function> usage</title>
+				<programlisting format="linespecific">
+...
+$ &ctltool; fifo get_dbg_mod_facility core
+$ &ctltool; fifo get_dbg_mod_facility debugger
+...
+				</programlisting>
+			</example>
+		</section>
+
+	</section>
+
+
+
+
 	<section>
 		<title>Exported RPC Functions</title>
 
@@ -775,7 +878,85 @@ P-Hint: My hint
 		</programlisting>
     </section>
 
+	<section id="dbg.r.set_mod_level">
+		<title>
+			<function moreinfo="none">dbg.set_mod_level</function>
+		</title>
+		<para>
+			Set the module log level.
+			If module does not exist in kamailio, the entry in the level hashtable is still added for the bogus module.
+		</para>
+		<para>
+			Name: <emphasis>dbg.set_mod_level</emphasis>
+		</para>
+		<para>
+			Examples of use with &sercmd;:
+		</para>
+		<programlisting  format="linespecific">
+			dbg.set_mod_level core 1
+		</programlisting>
+	</section>
+
+	<section id="dbg.r.set_mod_facility">
+		<title>
+			<function moreinfo="none">dbg.set_mod_facility</function>
+		</title>
+		<para>
+			Set the module log facility.
+			If module does not exist in kamailio, the entry in the facility hashtable is still added for the bogus module.
+		</para>
+		<para>
+			Name: <emphasis>dbg.set_mod_facility</emphasis>
+		</para>
+		<para>
+			Examples of use with &sercmd;:
+		</para>
+		<programlisting  format="linespecific">
+			dbg.set_mod_facility core LOG_LOCAL1
+		</programlisting>
+	</section>
+
+	<section id="dbg.r.get_mod_level">
+		<title>
+			<function moreinfo="none">dbg.get_mod_level</function>
+		</title>
+		<para>
+			Get the module log level.
+			If mod_name does not exist in the level hashtable, returns the config file value.
+		</para>
+		<para>
+			Name: <emphasis>dbg.get_mod_level</emphasis>
+		</para>
+		<para>
+			Examples of use with &sercmd;:
+		</para>
+		<programlisting  format="linespecific">
+			dbg.get_mod_level core
+		</programlisting>
+	</section>
+
+	<section id="dbg.r.get_mod_facility">
+		<title>
+			<function moreinfo="none">dbg.get_mod_facility</function>
+		</title>
+		<para>
+			Get the module log facility.
+			If mod_name does not exist in the facility hashtable, returns the config file value.
+		</para>
+		<para>
+			Name: <emphasis>dbg.get_mod_facility</emphasis>
+		</para>
+		<para>
+			Examples of use with &sercmd;:
+		</para>
+		<programlisting  format="linespecific">
+			dbg.get_mod_facility core
+		</programlisting>
+	</section>
+
     </section>
+
+
 	<section>
 		<title>Usage</title>
 		<para>

+ 1 - 0
modules/dispatcher/README

@@ -1373,6 +1373,7 @@ modparam("dispatcher", "flags", 2)
 modparam("dispatcher", "dst_avp", "$avp(AVP_DST)")
 modparam("dispatcher", "grp_avp", "$avp(AVP_GRP)")
 modparam("dispatcher", "cnt_avp", "$avp(AVP_CNT)")
+modparam("dispatcher", "sock_avp", "$avp(AVP_SOCK)")
 
 ####### Routing Logic ########
 

+ 13 - 0
modules/dispatcher/dispatch.c

@@ -255,6 +255,7 @@ int add_dest2list(int id, str uri, int flags, int priority, str *attrs,
 	int orig_id = 0, orig_nr = 0;
 	str host;
 	int port, proto;
+	char c = 0;
 	ds_set_t *orig_ds_lists = ds_lists[list_idx];
 
 	/* check uri */
@@ -329,11 +330,23 @@ int add_dest2list(int id, str uri, int flags, int priority, str *attrs,
 
 	/* check socket attribute */
 	if (dp->attrs.socket.s && dp->attrs.socket.len > 0) {
+		/* parse_phostport(...) expects 0-terminated string
+		 * - after socket parameter is either ';' or '\0' */
+		if(dp->attrs.socket.s[dp->attrs.socket.len]!='\0') {
+			c = dp->attrs.socket.s[dp->attrs.socket.len];
+			dp->attrs.socket.s[dp->attrs.socket.len] = '\0';
+		}
 		if (parse_phostport(dp->attrs.socket.s, &host.s, &host.len,
 				&port, &proto)!=0) {
 			LM_ERR("bad socket <%.*s>\n", dp->attrs.socket.len, dp->attrs.socket.s);
+			if(c!=0) {
+				dp->attrs.socket.s[dp->attrs.socket.len] = c;
+			}
 			goto err;
 		}
+		if(c!=0) {
+			dp->attrs.socket.s[dp->attrs.socket.len] = c;
+		}
 		dp->sock = grep_sock_info( &host, (unsigned short)port, proto);
 		if (dp->sock==0) {
 			LM_ERR("non-local socket <%.*s>\n", dp->attrs.socket.len, dp->attrs.socket.s);

+ 1 - 0
modules/dispatcher/doc/dispatcher.cfg

@@ -115,6 +115,7 @@ modparam("dispatcher", "flags", 2)
 modparam("dispatcher", "dst_avp", "$avp(AVP_DST)")
 modparam("dispatcher", "grp_avp", "$avp(AVP_GRP)")
 modparam("dispatcher", "cnt_avp", "$avp(AVP_CNT)")
+modparam("dispatcher", "sock_avp", "$avp(AVP_SOCK)")
 
 ####### Routing Logic ########
 

+ 7 - 4
modules/dmq_usrloc/usrloc_sync.c

@@ -56,10 +56,13 @@ static int add_contact(str aor, ucontact_info_t* ci)
 	str contact;
 	int res;
 
-        if (dmq_ul.get_udomain("location", &_d) < 0) {
-                LM_ERR("Failed to get domain\n");
-                return -1;
-        }
+	if (dmq_ul.get_udomain("location", &_d) < 0) {
+		LM_ERR("Failed to get domain\n");
+		return -1;
+	}
+
+	dmq_ul.lock_udomain(_d, &aor);
+
 	res = dmq_ul.get_urecord(_d, &aor, &r);
 	if (res < 0) {
 		LM_ERR("failed to retrieve record from usrloc\n");

+ 1 - 1
modules/htable/htable.c

@@ -1125,7 +1125,7 @@ static void  htable_rpc_stats(rpc_t* rpc, void* c)
 			ht_slot_unlock(ht, i);
 		}
 
-		if(rpc->struct_add(th, "Sddd",
+		if(rpc->struct_add(th, "Sdddd",
 						"name", &ht->name,	/* str */
 						"slots", (int)ht->htsize,	/* uint */
 						"all", (int)all,	/* uint */

+ 3 - 1
modules/ims_auth/cxdx_mar.c

@@ -137,7 +137,7 @@ void async_cdp_callback(int is_timeout, void *param, AAAMessage *maa, long elaps
     if (tmb.t_lookup_ident(&t, data->tindex, data->tlabel) < 0) {
         LM_ERR("t_continue: transaction not found\n");
         result = CSCF_RETURN_ERROR;
-        goto error;
+        goto error1;
     }
 
     /* get the private_identity */
@@ -485,6 +485,8 @@ error:
         tmb.unref_cell(t);
     }
     tmb.t_continue(data->tindex, data->tlabel, data->act);
+    
+error1:    
     free_saved_transaction_data(data);
 }
 

+ 37 - 99
modules/ims_charging/dialog.c

@@ -1,27 +1,31 @@
 #include "mod.h"
 #include "dialog.h"
 #include "ro_session_hash.h"
-#include "../ims_usrloc_scscf/usrloc.h"
-#include "../ims_usrloc_scscf/udomain.h"
 #include "ro_db_handler.h"
+#include "ims_charging_stats.h"
 
 struct cdp_binds cdpb;
 
-extern usrloc_api_t ul;
 extern int ro_db_mode;
 extern char *domain;
 extern struct dlg_binds dlgb;
+extern struct ims_charging_counters_h ims_charging_cnts_h;
 
 void dlg_callback_received(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) {
     LM_DBG("Received dialog callback event [%d]\n", type);
+    unsigned int termcode = 0;
     switch (type) {
         case DLGCB_CONFIRMED:
             dlg_answered(dlg, type, _params);
             break;
         case DLGCB_TERMINATED:
+            dlg_terminated(dlg, type, termcode, "normal call clearing", _params);
+            break;
         case DLGCB_FAILED:
+            dlg_terminated(dlg, type, termcode, "call failed", _params);
+            break;
         case DLGCB_EXPIRED:
-            dlg_terminated(dlg, type, _params);
+            dlg_terminated(dlg, type, termcode, "dialog timeout", _params);
             break;
         default:
             LM_WARN("Received unknown dialog callback [%d]\n", type);
@@ -29,7 +33,6 @@ void dlg_callback_received(struct dlg_cell *dlg, int type, struct dlg_cb_params
 }
 
 void dlg_answered(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) {
-    struct sip_msg *reply;
     struct ro_session* session = 0;
     struct ro_session_entry* ro_session_entry;
     time_t now = get_current_time_micro();
@@ -114,19 +117,38 @@ void dlg_answered(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
 
 }
 
-void dlg_terminated(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) {
+void dlg_terminated(struct dlg_cell *dlg, int type, unsigned int termcode, char* reason, struct dlg_cb_params *_params) {
 	//int i;
 	int unref = 0;
 	struct ro_session *ro_session = 0;
 	struct ro_session_entry *ro_session_entry;
 	struct sip_msg *request;
+        str s_reason;
+        
+        s_reason.s = reason;
+        s_reason.len = strlen(reason);
 	
-	LM_DBG("dialog [%p] terminated, lets send stop record\n", dlg);
+	LM_DBG("dialog [%p] terminated on type [%d], lets send stop record\n", dlg, type);
 
 	if (!_params) {
 		return;
 	}
 	
+	LM_DBG("Direction is %d\n", _params->direction);
+	if (_params->req) {
+		if (_params->req->first_line.u.request.method_value == METHOD_BYE) {
+			if (_params->direction == DLG_DIR_DOWNSTREAM) {
+				LM_DBG("Dialog ended by Caller\n");
+			} else {
+				LM_DBG("Dialog ended by Callee\n");
+			}
+		} else {
+			LM_DBG("Request is %.*s\n", _params->req->first_line.u.request.method.len, _params->req->first_line.u.request.method.s);
+		}
+	} else if (_params->rpl) {
+		LM_DBG("Reply is [%d - %.*s]", _params->rpl->first_line.u.reply.statuscode, _params->rpl->first_line.u.reply.reason.len, _params->rpl->first_line.u.reply.reason.s);
+	}
+	
 	ro_session = (struct ro_session*)*_params->param;
 	if (!ro_session) {
 		LM_ERR("Ro Session object is NULL...... aborting\n");
@@ -153,14 +175,14 @@ void dlg_terminated(struct dlg_cell *dlg, int type, struct dlg_cb_params *_param
 				//double processing for various dialog_terminated callback events.
 				//If however, the call was never answered, then we can continue as normal
 				ro_session_lock(ro_session_table, ro_session_entry);
-				if (!ro_session->active && (ro_session->start_time != 0)) {
-					unref_ro_session(ro_session,1);
-					LM_ERR("Ro Session is not active, but may have been answered [%d]\n", (int)ro_session->start_time);
+                                
+                                LM_DBG("processing dlg_terminated in Ro and session [%.*s] has active = %d", ro_session->ro_session_id.len, ro_session->ro_session_id.s, ro_session->active);
+				if ((!ro_session->active && (ro_session->start_time != 0)) || (ro_session->ccr_sent == 1)) {
+					unref_ro_session_unsafe(ro_session,1,ro_session_entry);
+					LM_ERR("CCR already sent or Ro Session is not active, but may have been answered [%d]\n", (int)ro_session->start_time);
 					ro_session_unlock(ro_session_table, ro_session_entry);
 					return;
 				}
-			
-	
 
 				if (ro_session->active) { // if the call was never activated, there's no timer to remove
 					int ret = remove_ro_timer(&ro_session->ro_tl);
@@ -176,8 +198,10 @@ void dlg_terminated(struct dlg_cell *dlg, int type, struct dlg_cb_params *_param
 				}
 
 				LM_DBG("Sending CCR STOP on Ro_Session [%p]\n", ro_session);
-				send_ccr_stop(ro_session);
+				send_ccr_stop_with_param(ro_session, termcode, &s_reason);
 				ro_session->active = -1;    //deleted.... terminated ....
+                                ro_session->ccr_sent = 1;
+//                                counter_add(ims_charging_cnts_h.active_ro_sessions, -1);
 				
 				if (ro_db_mode == DB_MODE_REALTIME) {
 				    ro_session->flags |= RO_SESSION_FLAG_DELETED;
@@ -194,89 +218,3 @@ void dlg_terminated(struct dlg_cell *dlg, int type, struct dlg_cb_params *_param
 		//}
 	}
 }
-
-void remove_dlg_data_from_contact(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) {
-        
-    struct impu_data *impu_data;
-    impurecord_t* implicit_impurecord = 0;
-    struct ucontact* ucontact;
-    str callid = {0, 0};
-    str path = {0, 0};
-    udomain_t* domain_t;
-    
-    LM_DBG("dialog [%p] terminated, lets remove dlg data from contact\n", dlg);
-    
-    if (ul.register_udomain(domain, &domain_t) < 0) {
-	    LM_ERR("Unable to register usrloc domain....aborting\n");
-	    return;
-    }
-    
-    if(_params && _params->param){
-	impu_data = (struct impu_data*)*_params->param;
-	if (!impu_data) {
-		LM_ERR("IMPU data object is NULL...... aborting\n");
-		return;
-	}
-	
-	LM_DBG("IMPU data is present, contact: <%.*s> identity <%.*s>", impu_data->contact.len, impu_data->contact.s, impu_data->identity.len, impu_data->identity.s);
-	LM_DBG("IMPU data domain <%.*s>", domain_t->name->len, domain_t->name->s);
-	
-	ul.lock_udomain(domain_t, &impu_data->identity);
-	if (ul.get_impurecord(domain_t, &impu_data->identity, &implicit_impurecord) != 0) {
-	    LM_DBG("usrloc does not have imprecord for implicity IMPU, ignore\n");
-	}else {
-	    if (ul.get_ucontact(&impu_data->contact, &callid, &path, 0/*cseq*/,  &ucontact) != 0) { //contact does not exist
-		LM_DBG("This contact: <%.*s> is not in usrloc, ignore - NOTE: You need S-CSCF usrloc set to match_mode CONTACT_PORT_IP_ONLY\n", impu_data->contact.len, impu_data->contact.s);
-	    } else {//contact exists so add dialog data to it
-		ul.remove_dialog_data_from_contact(ucontact, dlg->h_entry, dlg->h_id);
-		ul.release_ucontact(ucontact);
-	    }
-	}
-	ul.unlock_udomain(domain_t, &impu_data->identity);
-	free_impu_data(impu_data);
-    }
-    
-    //we referenced the dialog when we registered for callbacks on it...
-    dlgb.release_dlg(dlg);
-}
-
-void add_dlg_data_to_contact(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) {
-    
-    struct impu_data *impu_data;
-    impurecord_t* implicit_impurecord = 0;
-    struct ucontact* ucontact;
-    str callid = {0, 0};
-    str path = {0, 0};
-    udomain_t* domain_t;
-    
-    LM_DBG("dialog [%p] confirmed, lets add dlg data to contact\n", dlg);
-    
-    if (ul.register_udomain(domain, &domain_t) < 0) {
-	    LM_ERR("Unable to register usrloc domain....aborting\n");
-	    return;
-    }
-    
-    if(_params && _params->param){
-	impu_data = (struct impu_data*)*_params->param;
-	if (!impu_data) {
-		LM_ERR("IMPU data object is NULL...... aborting\n");
-		return;
-	}
-	
-	LM_DBG("IMPU data is present, contact: <%.*s> identity <%.*s>", impu_data->contact.len, impu_data->contact.s, impu_data->identity.len, impu_data->identity.s);
-	LM_DBG("IMPU data domain <%.*s>", domain_t->name->len, domain_t->name->s);
-	
-	ul.lock_udomain(domain_t, &impu_data->identity);
-	if (ul.get_impurecord(domain_t, &impu_data->identity, &implicit_impurecord) != 0) {
-	    LM_DBG("usrloc does not have imprecord for implicity IMPU, ignore\n");
-	}else {
-	    if (ul.get_ucontact(&impu_data->contact, &callid, &path, 0/*cseq*/,  &ucontact) != 0) { //contact does not exist
-		LM_DBG("This contact: <%.*s> is not in usrloc, ignore - NOTE: You need S-CSCF usrloc set to match_mode CONTACT_PORT_IP_ONLY\n", impu_data->contact.len, impu_data->contact.s);
-	    } else {//contact exists so add dialog data to it
-		ul.add_dialog_data_to_contact(ucontact, dlg->h_entry, dlg->h_id);
-		ul.release_ucontact(ucontact);
-	    }
-	}
-	ul.unlock_udomain(domain_t, &impu_data->identity);
-    }
-} 

+ 1 - 3
modules/ims_charging/dialog.h

@@ -9,9 +9,7 @@
 extern int ro_timer_buffer;
 
 void dlg_callback_received(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params);
-void dlg_terminated(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params);
+void dlg_terminated(struct dlg_cell *dlg, int type, unsigned int termcode, char* reason, struct dlg_cb_params *_params);
 void dlg_answered(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params);
-void add_dlg_data_to_contact(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params);
-void remove_dlg_data_from_contact(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params);
 
 #endif

+ 69 - 5
modules/ims_charging/ims_ro.c

@@ -42,6 +42,8 @@ extern cdp_avp_bind_t *cdp_avp;
 extern str ro_forced_peer;
 extern int ro_db_mode;
 extern struct ims_charging_counters_h ims_charging_cnts_h;
+extern int vendor_specific_id;
+extern int vendor_specific_chargeinfo;
 
 struct session_setup_data {
     struct ro_session *ro_session;
@@ -180,6 +182,21 @@ inline int Ro_add_termination_cause(AAAMessage *msg, unsigned int term_code) {
     return Ro_add_avp(msg, s.s, s.len, AVP_Termination_Cause, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);
 }
 
+inline int Ro_add_vendor_specific_termination_cause(AAAMessage *msg, unsigned int term_code) {
+    char x[4];
+    str s = {x, 4};
+    uint32_t code = htonl(term_code);
+    memcpy(x, &code, sizeof (uint32_t));
+
+    return Ro_add_avp(msg, s.s, s.len, VS_TERMCODE, AAA_AVP_FLAG_VENDOR_SPECIFIC, 10, AVP_DUPLICATE_DATA, __FUNCTION__);
+}
+
+inline int Ro_add_vendor_specific_termination_reason(AAAMessage *msg, str* reason) {
+    return Ro_add_avp(msg, reason->s, reason->len, VS_TERMREASON, AAA_AVP_FLAG_VENDOR_SPECIFIC, 10, AVP_DUPLICATE_DATA, __FUNCTION__);
+}
+
+
+
 /* called only when building stop record AVPS */
 inline int Ro_add_multiple_service_credit_Control_stop(AAAMessage *msg, int used_unit, int active_rating_group, int active_service_identifier) {
     char x[4];
@@ -721,6 +738,7 @@ static void resume_on_interim_ccr(int is_timeout, void *param, AAAMessage *cca,
     goto success;
 
 error:
+    counter_inc(ims_charging_cnts_h.failed_interim_ccr);  
     if (ro_cca_data)
         Ro_free_CCA(ro_cca_data);
 
@@ -744,7 +762,7 @@ long get_current_time_micro() {
     return tv.tv_sec*1000000 + tv.tv_usec;
 }
 
-void send_ccr_stop(struct ro_session *ro_session) {
+void send_ccr_stop_with_param(struct ro_session *ro_session, unsigned int code, str* reason) {
     AAASession * auth = 0;
     Ro_CCR_t * ro_ccr_data = 0;
     AAAMessage * ccr = 0;
@@ -755,9 +773,29 @@ void send_ccr_stop(struct ro_session *ro_session) {
     long used = 0;
     str user_name = {0, 0};
     int ret = 0;
+    time_t stop_time;
+    time_t actual_time_micros;
+    int actual_time_seconds;
+
+    stop_time = get_current_time_micro();
+
+    if (ro_session->start_time == 0)
+        actual_time_micros = 0;
+    else
+        actual_time_micros = stop_time - ro_session->start_time;
+
+    actual_time_seconds = (actual_time_micros + (1000000 - 1)) / (float) 1000000;
 
     if (ro_session->event_type != pending) {
-        used = rint((get_current_time_micro() - ro_session->last_event_timestamp)/(float)1000000);
+        used = rint((stop_time - ro_session->last_event_timestamp) / (float) 1000000);
+        LM_DBG("Final used number of seconds for session is %ld\n", used);
+    }
+
+    LM_DBG("Call started at %ld and ended at %ld and lasted %d seconds and so far we have billed for %ld seconds\n", ro_session->start_time, stop_time,
+            actual_time_seconds, ro_session->billed + used);
+    if (ro_session->billed + used < actual_time_seconds) {
+        LM_DBG("Making adjustment by adding %ld seconds\n", actual_time_seconds - (ro_session->billed + used));
+        used += actual_time_seconds - (ro_session->billed + used);
     }
 
     counter_add(ims_charging_cnts_h.billed_secs, (int)used);
@@ -869,6 +907,16 @@ void send_ccr_stop(struct ro_session *ro_session) {
         LM_ERR("problem add Termination cause AVP to STOP record.\n");
     }
 
+    if (vendor_specific_chargeinfo) {
+        if (!Ro_add_vendor_specific_termination_cause(ccr, code)) {
+            LM_ERR("problem add Termination cause AVP to STOP record.\n");
+        }
+
+        if (!Ro_add_vendor_specific_termination_reason(ccr, reason)) {
+            LM_ERR("problem add Termination cause AVP to STOP record.\n");
+        }
+    }
+
     cdpb.AAASessionsUnlock(auth->hash);
 
     if (ro_forced_peer.len > 0) {
@@ -884,7 +932,7 @@ void send_ccr_stop(struct ro_session *ro_session) {
     Ro_free_CCR(ro_ccr_data);
 
     counter_inc(ims_charging_cnts_h.final_ccrs);
-    counter_add(ims_charging_cnts_h.active_ro_sessions, -1);
+//    counter_add(ims_charging_cnts_h.active_ro_sessions, -1);
     return;
 
 error1:
@@ -915,6 +963,7 @@ static void resume_on_termination_ccr(int is_timeout, void *param, AAAMessage *c
 
     if (!cca) {
         LM_ERR("Error in termination CCR.\n");
+        counter_inc(ims_charging_cnts_h.failed_final_ccrs); 
         return;
     }
 
@@ -922,6 +971,7 @@ static void resume_on_termination_ccr(int is_timeout, void *param, AAAMessage *c
 
     if (ro_cca_data == NULL) {
         LM_DBG("Could not parse CCA message response.\n");
+        counter_inc(ims_charging_cnts_h.failed_final_ccrs); 
         return;
     }
 
@@ -933,8 +983,14 @@ static void resume_on_termination_ccr(int is_timeout, void *param, AAAMessage *c
     }
 
     counter_inc(ims_charging_cnts_h.successful_final_ccrs);
+    Ro_free_CCA(ro_cca_data);
+    if (!is_timeout && cca) {
+        cdpb.AAAFreeMessage(&cca);
+    }
+    return;
 
 error:
+    counter_inc(ims_charging_cnts_h.failed_final_ccrs);      
     Ro_free_CCA(ro_cca_data);
     if (!is_timeout && cca) {
         cdpb.AAAFreeMessage(&cca);
@@ -1138,6 +1194,7 @@ int Ro_Send_CCR(struct sip_msg *msg, struct dlg_cell *dlg, int dir, int reservat
     LM_DBG("new CC Ro Session ID: [%.*s] stored in shared memory address [%p]\n", cc_acc_session->id.len, cc_acc_session->id.s, new_session);
 
     LM_DBG("Sending CCR Diameter message.\n");
+//    new_session->ccr_sent = 1;      //assume we will send successfully
     cdpb.AAASessionsUnlock(cc_acc_session->hash);
 
     if (ro_forced_peer.len > 0) {
@@ -1150,6 +1207,7 @@ int Ro_Send_CCR(struct sip_msg *msg, struct dlg_cell *dlg, int dir, int reservat
 
     if (ret != 1) {
         LM_ERR("Failed to send Diameter CCR\n");
+//        new_session->ccr_sent = 0;
         goto error;
     }
 
@@ -1164,6 +1222,7 @@ int Ro_Send_CCR(struct sip_msg *msg, struct dlg_cell *dlg, int dir, int reservat
     }
 
     counter_inc(ims_charging_cnts_h.initial_ccrs);
+    counter_inc(ims_charging_cnts_h.active_ro_sessions);
 
     if (free_called_asserted_identity) shm_free(called_asserted_identity.s); // shm_malloc in cscf_get_public_identity_from_requri	
     return RO_RETURN_BREAK;
@@ -1255,13 +1314,17 @@ static void resume_on_initial_ccr(int is_timeout, void *param, AAAMessage *cca,
     ssd->ro_session->event_type = pending;
     ssd->ro_session->reserved_secs = ro_cca_data->mscc->granted_service_unit->cc_time;
     ssd->ro_session->valid_for = ro_cca_data->mscc->validity_time;
+    ssd->ro_session->is_final_allocation = 0;
+
+    if (ro_cca_data->mscc->final_unit_action && (ro_cca_data->mscc->final_unit_action->action == 0))
+        ssd->ro_session->is_final_allocation = 1;
 
     Ro_free_CCA(ro_cca_data);
 
     LM_DBG("Freeing CCA message\n");
     cdpb.AAAFreeMessage(&cca);
 
-    link_ro_session(ssd->ro_session, 1); /* create extra ref for the fact that dialog has a handle in the callbacks */
+    link_ro_session(ssd->ro_session, 0); 
 
     if (ro_db_mode == DB_MODE_REALTIME) {
         ssd->ro_session->flags |= RO_SESSION_FLAG_NEW;
@@ -1281,7 +1344,6 @@ static void resume_on_initial_ccr(int is_timeout, void *param, AAAMessage *cca,
     shm_free(ssd);
 
     counter_inc(ims_charging_cnts_h.successful_initial_ccrs);
-    counter_inc(ims_charging_cnts_h.active_ro_sessions);
 
     return;
 
@@ -1290,6 +1352,8 @@ error1:
 
 error0:
     LM_DBG("Trying to reserve credit on initial INVITE failed on cdp callback\n");
+//    counter_add(ims_charging_cnts_h.active_ro_sessions, -1); /*we bumped active on the original initial ccr sent */
+    counter_inc(ims_charging_cnts_h.failed_initial_ccrs);      /* drop by one as theoretically this is failed initial ccr */
     create_cca_return_code(error_code);
 
     if (!is_timeout && cca) {

+ 6 - 1
modules/ims_charging/ims_ro.h

@@ -6,6 +6,11 @@
 #include "../dialog_ng/dlg_hash.h"
 #include "ro_session_hash.h"
 
+typedef enum {
+    VS_TERMCODE = 3,
+    VS_TERMREASON = 2
+} vs_term_avp;
+
 struct interim_ccr {
 	struct ro_session* ro_session;
 	int new_credit;
@@ -19,7 +24,7 @@ int Ro_Send_CCR(struct sip_msg *msg, struct dlg_cell *dlg, int dir, int reservat
 	    str *incoming_trunk_id, str *outgoing_trunk_id, str *enb_cell_id, cfg_action_t* action, unsigned int tindex, unsigned int tlabel);
 long get_current_time_micro();
 void send_ccr_interim(struct ro_session* ro_session, unsigned int used, unsigned int reserve);
-void send_ccr_stop(struct ro_session *ro_session);
+void send_ccr_stop_with_param(struct ro_session *ro_session, unsigned int code, str* reason);
 int get_direction_as_int(str* direction);
 
 #endif /* CLIENT_RF_IMS_RO_H */

+ 101 - 24
modules/ims_charging/mod.c

@@ -17,10 +17,11 @@
 #include "ims_ro.h"
 #include "config.h"
 #include "dialog.h"
-#include "../ims_usrloc_scscf/usrloc.h"
 #include "../../lib/ims/ims_getters.h"
 #include "ro_db_handler.h"
 #include "ims_charging_stats.h"
+#include "ro_session_hash.h"
+#include "ims_charging_stats.h"
 
 MODULE_VERSION
 
@@ -31,6 +32,9 @@ char* ro_service_context_id_ext_s = "ext";
 char* ro_service_context_id_mnc_s = "01";
 char* ro_service_context_id_mcc_s = "001";
 char* ro_service_context_id_release_s = "8";
+int	termination_code = 0;
+int vendor_specific_id = 10;
+int vendor_specific_chargeinfo = 0;
 static int ro_session_hash_size = 4096;
 int ro_timer_buffer = 5;
 int interim_request_credits = 30;
@@ -57,13 +61,12 @@ client_ro_cfg cfg = { str_init("scscf.ims.smilecoms.com"),
     0
 };
 
+extern struct ims_charging_counters_h ims_charging_cnts_h;
 struct cdp_binds cdpb;
 struct dlg_binds dlgb;
 cdp_avp_bind_t *cdp_avp;
 struct tm_binds tmb;
 
-usrloc_api_t ul; /*!< Structure containing pointers to usrloc functions*/
-
 char* rx_dest_realm_s = "ims.smilecoms.com";
 str rx_dest_realm;
 /* Only used if we want to force the Ro peer usually this is configured at a stack level and the first request uses realm routing */
@@ -81,6 +84,7 @@ static int mod_child_init(int);
 static void mod_destroy(void);
 
 static int w_ro_ccr(struct sip_msg *msg, char* route_name, char* direction, int reservation_units, char* incoming_trunk_id, char* outgoing_trunk_id);
+static int w_ro_ccr_stop(struct sip_msg *msg, char* direction, char* _code, char* _reason);
 //void ro_session_ontimeout(struct ro_tl *tl);
 
 
@@ -88,10 +92,12 @@ int create_response_avp_string(char* name, str* val);
 static int w_ro_set_session_id_avp(struct sip_msg *msg, char *str1, char *str2);
 
 static int ro_fixup(void **param, int param_no);
+static int ro_fixup_stop(void **param, int param_no);
 
 static cmd_export_t cmds[] = {
 		{ "Ro_CCR", 	(cmd_function) w_ro_ccr, 5, ro_fixup, 0, REQUEST_ROUTE },
-                { "Ro_set_session_id_avp", 	(cmd_function) w_ro_set_session_id_avp, 0, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE },
+		{ "Ro_CCR_Stop",(cmd_function) w_ro_ccr_stop, 3, ro_fixup_stop, 0, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE},
+        { "Ro_set_session_id_avp", 	(cmd_function) w_ro_set_session_id_avp, 0, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE },
 		{ 0, 0, 0, 0, 0, 0 }
 };
 
@@ -124,6 +130,8 @@ static param_export_t params[] = {
 		{ "db_mode",			INT_PARAM,			&ro_db_mode_param		},
 		{ "db_url",			PARAM_STRING,			&db_url 			},
 		{ "db_update_period",		INT_PARAM,			&db_update_period		},
+		{ "vendor_specific_chargeinfo",		INT_PARAM,	&vendor_specific_chargeinfo		}, /* VSI for extra charing info in Ro */
+		{ "vendor_specific_id",		INT_PARAM,			&vendor_specific_id		}, /* VSI for extra charing info in Ro */
 		{ 0, 0, 0 }
 };
 
@@ -173,7 +181,6 @@ int fix_parameters() {
 static int mod_init(void) {
 	int n;
 	load_tm_f load_tm;
-	bind_usrloc_t bind_usrloc;
 
 	if (!fix_parameters()) {
 		LM_ERR("unable to set Ro configuration parameters correctly\n");
@@ -204,16 +211,6 @@ static int mod_init(void) {
 		goto error;
 	}
         
-        bind_usrloc = (bind_usrloc_t) find_export("ul_bind_usrloc", 1, 0);
-	if (!bind_usrloc) {
-	    LM_ERR("can't bind usrloc\n");
-	    return -1;
-	}
-	
-	if (bind_usrloc(&ul) < 0) {
-	    return -1;
-	}
-
 	/* init timer lists*/
 	if (init_ro_timer(ro_session_ontimeout) != 0) {
 		LM_ERR("cannot init timer list\n");
@@ -240,15 +237,6 @@ static int mod_init(void) {
 		LM_ERR("failed to register timer \n");
 		return -1;
 	}
-
-	
-
-	/*Register for callback of URECORD being deleted - so we can send a SAR*/
-
-	if (ul.register_ulcb == NULL) {
-	    LM_ERR("Could not import ul_register_ulcb\n");
-	    return -1;
-	}
 	
 	if (ims_charging_init_counters() != 0) {
 	    LM_ERR("Failed to register counters for ims_charging module\n");
@@ -352,11 +340,93 @@ static int w_ro_set_session_id_avp(struct sip_msg *msg, char *str1, char *str2)
     //set avp response with session id
     res = create_response_avp_string("ro_session_id", &ro_session->ro_session_id);
     dlgb.release_dlg(dlg);
+    unref_ro_session(ro_session, 1);
     return res;
 }
 
+static int w_ro_ccr_stop(struct sip_msg *msg, char* c_direction, char* _code, char* _reason) {
+    struct ro_session* ro_session;
+    struct ro_session_entry *ro_session_entry;
+    unsigned int h_entry;
+    str s_code, s_reason;
+    unsigned int code;
+    int dir = 0; /*any side*/
+
+    LM_DBG("Inside Ro_CCR_Stop with direction [%s]\n", c_direction);
+    if (strlen(c_direction) == 4) {
+        if (c_direction[0] == 'O' || c_direction[0] == 'o') {
+            dir = RO_ORIG_DIRECTION;
+        } else {
+            dir = RO_TERM_DIRECTION;
+        }
+    } else {
+        LM_ERR("Unknown direction [%s] to terminate\n", c_direction);
+        return RO_RETURN_FALSE;
+    }
+    struct dlg_cell* dlg = dlgb.get_dlg(msg);
+    if (!dlg) {
+        LM_ERR("Unable to find dialog to send CCR STOP record\n");
+        return RO_RETURN_ERROR;
+    }
+
+    if (get_str_fparam(&s_code, msg, (fparam_t*) _code) < 0) {
+        LM_ERR("failed to get code\n");
+        return RO_RETURN_ERROR;
+    }
+    LM_DBG("Code is [%.*s]\n", s_code.len, s_code.s);
+    if (get_str_fparam(&s_reason, msg, (fparam_t*) _reason) < 0) {
+        LM_ERR("failed to get reason\n");
+        return RO_RETURN_ERROR;
+    }
+
+    if (str2int(&s_code, &code) != 0) {
+        LM_ERR("Bad response code: [%.*s]\n", s_code.len, s_code.s);
+        return RO_RETURN_FALSE;
+    }
 
+//    switch (code) {
+//        case 486:
+//            termcode = VS_TERMCODE_BUSYHERE;
+//            break;
+//        case 487:
+//            termcode = VS_TERMCODE_CANCELLED;
+//            break;
+//        case 480:
+//        case 408:
+//            /* subscriber not available */
+//            termcode = VS_TERMCODE_NOTFOUND;
+//            break;
+//    }
+
+    LM_DBG("Sending Stop record with code [%d] and reason [%.*s]\n", code, s_reason.len, s_reason.s);
+
+    LM_DBG("Found DLG [%d : %d]\n", dlg->h_id, dlg->h_entry);
+
+    ro_session = lookup_ro_session(dlg->h_entry, &dlg->callid, dir, 0);
+    if (ro_session == NULL) {
+        LM_DBG("no ro_session - ignoring\n");
+        return RO_RETURN_TRUE;
+    }
+    h_entry = ro_session->h_entry;
+    ro_session_entry = &(ro_session_table->entries[h_entry]);
 
+    ro_session_lock(ro_session_table, ro_session_entry);
+    
+    if (ro_session->ccr_sent == 1) {
+        LM_DBG("Ro CCR already sent for session [%.*s]\n", ro_session->ro_session_id.len, ro_session->ro_session_id.s);
+        goto done;
+    }
+    send_ccr_stop_with_param(ro_session, code, &s_reason);
+    //TODO = check the CCR was sent successfully.
+    LM_DBG("Setting Ro session [%.*s] ccr_sent to 1\n", ro_session->ro_session_id.len, ro_session->ro_session_id.s);
+    ro_session->ccr_sent = 1;
+    ro_session->active = -1;
+//    counter_add(ims_charging_cnts_h.active_ro_sessions, -1);
+done:
+    unref_ro_session_unsafe(ro_session, 1, ro_session_entry);
+    ro_session_unlock(ro_session_table, ro_session_entry);
+    return RO_RETURN_TRUE;
+}
 
 static int w_ro_ccr(struct sip_msg *msg, char* c_route_name, char* c_direction, int reservation_units, char* c_incoming_trunk_id, char* c_outgoing_trunk_id) {
 	/* PSEUDOCODE/NOTES
@@ -541,3 +611,10 @@ static int ro_fixup(void **param, int param_no) {
 	
 	return 0;
 }
+
+static int ro_fixup_stop(void **param, int param_no) {
+	if (param_no == 2 || param_no == 3) {
+		return fixup_var_pve_12(param, param_no);
+	}
+	return 0;
+}

+ 1 - 1
modules/ims_charging/ro_session_hash.c

@@ -227,7 +227,7 @@ struct ro_session* build_new_ro_session(int direction, int auth_appid, int auth_
 
     new_ro_session->h_entry = dlg_h_entry; /* we will use the same entry ID as the dlg - saves us using our own hash function */
     new_ro_session->h_id = 0;
-    new_ro_session->ref = 0;
+    new_ro_session->ref = 1;
     
     new_ro_session->rating_group = active_rating_group;
     new_ro_session->service_identifier = active_service_identifier;

+ 8 - 1
modules/ims_charging/ro_session_hash.h

@@ -10,7 +10,7 @@
 
 #include "ro_timer.h"
 #include "../../mem/shm_mem.h"
-#include "../ims_usrloc_scscf/usrloc.h"
+#include "ims_charging_stats.h"
 #include <stdlib.h>
 
 
@@ -22,6 +22,8 @@
 
 #define MAX_PANI_LEN 100
 
+extern struct ims_charging_counters_h ims_charging_cnts_h;
+
 enum ro_session_event_type {
     pending,
     answered,
@@ -71,6 +73,9 @@ struct ro_session {
     str mac;
     int rating_group;
     int service_identifier;
+    unsigned int is_final_allocation;
+    long billed;
+    unsigned int ccr_sent;
 };
 
 /*! entries in the main ro_session table */
@@ -161,6 +166,8 @@ static inline void unlink_unsafe_ro_session(struct ro_session_entry *ro_session_
         ro_session_entry->first = ro_session->next;
 
     ro_session->next = ro_session->prev = 0;
+    
+    counter_add(ims_charging_cnts_h.active_ro_sessions, -1);
 
     return;
 }

+ 21 - 0
modules/ims_charging/ro_timer.c

@@ -380,6 +380,7 @@ void resume_ro_session_ontimeout(struct interim_ccr *i_req) {
 void ro_session_ontimeout(struct ro_tl *tl) {
     time_t now, call_time;
     long used_secs;
+	int adjustment;
 
     LM_DBG("We have a fired timer [p=%p] and tl=[%i].\n", tl, tl->timeout);
 
@@ -400,12 +401,31 @@ void ro_session_ontimeout(struct ro_tl *tl) {
     //		return;
     //	}
 
+
+    if(ro_session->is_final_allocation) {
+        now = get_current_time_micro();
+        used_secs = now - ro_session->last_event_timestamp;
+        if((ro_session->reserved_secs - used_secs) > 0) {
+            update_ro_timer(&ro_session->ro_tl, (ro_session->reserved_secs - used_secs));
+            return;
+        }
+        else {
+            ro_session->event_type = no_more_credit;
+        }
+    }
+
     switch (ro_session->event_type) {
         case answered:
             now = get_current_time_micro();
             used_secs = rint((now - ro_session->last_event_timestamp) / (float) 1000000);
             call_time = rint((now - ro_session->start_time) / (float) 1000000);
 
+			if ((used_secs + ro_session->billed) < (call_time)) {
+				adjustment = call_time - (used_secs + ro_session->billed);
+				LM_DBG("Making adjustment for Ro interim timer by adding %d seconds\n", adjustment);
+				used_secs += adjustment;
+			}
+			
             counter_add(ims_charging_cnts_h.billed_secs, used_secs);
 
             if (ro_session->callid.s != NULL
@@ -431,6 +451,7 @@ void ro_session_ontimeout(struct ro_tl *tl) {
                 // Apply for more credit.
                 //
                 // The function call will return immediately and we will receive the reply asynchronously via a callback
+				ro_session->billed += used_secs;
                 send_ccr_interim(ro_session, (unsigned int) used_secs, interim_request_credits);
                 return;
             } else {

+ 1 - 1
modules/ims_qos/rx_str.c

@@ -107,7 +107,7 @@ int rx_send_str(str *rx_session_id) {
         // so just wait for STA or for Grace Timout to happen
         LM_DBG("Hmmm, auth session already in disconnected state\n");
         cdpb.AAASessionsUnlock(auth->hash);
-		CSCF_RETURN_FALSE;
+        return CSCF_RETURN_FALSE;
     }
 
     LM_DBG("Creating STR\n");

+ 50 - 68
modules/ims_registrar_scscf/registrar_notify.c

@@ -57,6 +57,7 @@
 #include <libxml/parser.h>
 
 #include "../../lib/ims/useful_defs.h"
+#include "../ims_usrloc_scscf/udomain.h"
 
 #define STATE_ACTIVE 1
 #define STATE_TERMINATED 0
@@ -437,8 +438,6 @@ error:
  * return 0 on success. anything else failure
  */
 int event_reg(udomain_t* _d, impurecord_t* r_passed, int event_type, str *presentity_uri, str *watcher_contact) {
-
-    str content = {0, 0};
     impurecord_t* r;
     int num_impus;
     str* impu_list;
@@ -471,7 +470,7 @@ int event_reg(udomain_t* _d, impurecord_t* r_passed, int event_type, str *presen
 
             //get IMPU set from the presentity's subscription
             res = ul.get_impus_from_subscription_as_string(_d, r,
-                    0/*all unbarred impus*/, &impu_list, &num_impus);
+                    0/*all unbarred impus*/, &impu_list, &num_impus, 0/*pkg*/);
             if (res != 0) {
                 LM_WARN("failed to get IMPUs from subscription\n");
                 ul.unlock_udomain(_d, presentity_uri);
@@ -481,19 +480,9 @@ int event_reg(udomain_t* _d, impurecord_t* r_passed, int event_type, str *presen
                 return 1;
             }
             ul.unlock_udomain((udomain_t*) _d, presentity_uri);
-
-            content = generate_reginfo_full(_d, impu_list,
-                    num_impus, 0, 0);
-
-            if (impu_list) {
-                pkg_free(impu_list);
-            }
-
             LM_DBG("About to ceate notification");
 
-            create_notifications(_d, r_passed, presentity_uri, watcher_contact, content, event_type);
-            if (content.s) pkg_free(content.s);
-            //            if (send_now) notification_timer(0, 0);
+            create_notifications(_d, r_passed, presentity_uri, watcher_contact, &impu_list, num_impus, event_type);
             return 0;
             break;
 
@@ -507,12 +496,9 @@ int event_reg(udomain_t* _d, impurecord_t* r_passed, int event_type, str *presen
                 LM_ERR("this is a contact change passed from ul callback: r_passed and c_passed should both be valid and presentity_uri, watcher_contact and _d should be 0 for ul callback");
                 return 0;
             }
-
-            //content = get_reginfo_partial(r_passed, c_passed, event_type);
-
             //this is a ulcallback so r_passed domain is already locked
             res = ul.get_impus_from_subscription_as_string(_d, r_passed,
-                    0/*all unbarred impus*/, &impu_list, &num_impus);
+                    0/*all unbarred impus*/, &impu_list, &num_impus, 0/*pkg*/);
             if (res != 0) {
                 LM_WARN("failed to get IMPUs from subscription\n");
                 if (impu_list) {
@@ -526,19 +512,9 @@ int event_reg(udomain_t* _d, impurecord_t* r_passed, int event_type, str *presen
                 LM_ERR("Unable to register usrloc domain....aborting\n");
                 return 0;
             }
-            //	    
-            content = generate_reginfo_full(udomain, impu_list,
-                    num_impus, &r_passed->public_identity, 1);
-            //	    
-            if (impu_list) {
-                pkg_free(impu_list);
-            }
-
             LM_DBG("About to ceate notification");
 
-            create_notifications(_d, r_passed, presentity_uri, watcher_contact, content, event_type);
-            if (content.s) pkg_free(content.s);
-            //                        if (send_now) notification_timer(0, 0);
+            create_notifications(_d, r_passed, presentity_uri, watcher_contact, &impu_list, num_impus, event_type);
             return 1;
 
         default:
@@ -1391,7 +1367,7 @@ static str subs_active = {"active;expires=", 15};
  * @param content - the body content
  * @param expires - the remaining subcription expiration time in seconds
  */
-void create_notifications(udomain_t* _t, impurecord_t* r_passed, str *presentity_uri, str *watcher_contact, str content, int event_type) {
+void create_notifications(udomain_t* _t, impurecord_t* r_passed, str *presentity_uri, str *watcher_contact, str** impus, int num_impus, int event_type) {
 
     reg_notification *n;
     reg_subscriber *s;
@@ -1432,7 +1408,7 @@ void create_notifications(udomain_t* _t, impurecord_t* r_passed, str *presentity
 
         if (s->expires > act_time) {
             LM_DBG("Expires is greater than current time!");
-            subscription_state.s = pkg_malloc(32);
+            subscription_state.s = (char*) pkg_malloc(32 * sizeof (char*));
             subscription_state.len = 0;
             if (subscription_state.s) {
 
@@ -1462,8 +1438,9 @@ void create_notifications(udomain_t* _t, impurecord_t* r_passed, str *presentity
                 version = s->version + 1;
                 ul.update_subscriber(r, &s, 0, &local_cseq, &version);
 
-                n = new_notification(subscription_state, content_type, content, s);
+                n = new_notification(subscription_state, content_type, impus, num_impus, s);
                 if (n) {
+                    n->_d = _t;
                     LM_DBG("Notification exists - about to add it");
                     add_notification(n);
                 } else {
@@ -1488,15 +1465,16 @@ void create_notifications(udomain_t* _t, impurecord_t* r_passed, str *presentity
                 version = s->version + 1;
                 ul.update_subscriber(r, &s, 0, &local_cseq, &version);
 
-                n = new_notification(subscription_state, content_type, content, s);
-                if (n) {
-                    LM_DBG("Notification exists - about to add it");
-                    add_notification(n);
+            n = new_notification(subscription_state, content_type, impus, num_impus, s);
+            if (n) {
+                n->_d = _t;
+                LM_DBG("Notification exists - about to add it");
+                add_notification(n);
 
-                } else {
-                    LM_DBG("Notification does not exist");
-                }
-//            }
+            } else {
+                LM_DBG("Notification does not exist");
+            }
+            //            }
         }
         s = s->next;
 
@@ -1655,7 +1633,7 @@ static void process_xml_for_contact(str* buf, str* pad, ucontact_t* ptr) {
  * @returns the str with the XML content
  * if its a new subscription we do things like subscribe to updates on IMPU, etc
  */
-str generate_reginfo_full(udomain_t* _t, str* impu_list, int num_impus, str *primary_impu, int primary_locked) {
+str generate_reginfo_full(udomain_t* _t, str* impu_list, int num_impus) {
     str x = {0, 0};
     str buf, pad;
     char bufc[MAX_REGINFO_SIZE], padc[MAX_REGINFO_SIZE];
@@ -1927,12 +1905,23 @@ static int free_tm_dlg(dlg_t * td) {
 
 void send_notification(reg_notification * n) {
     str h = {0, 0};
-
+    str content = {0, 0};
     uac_req_t uac_r;
     dlg_t* td = NULL;
 
+    struct udomain* domain = (struct udomain*) n->_d;
+    if (!domain) {
+        ul.register_udomain("location", &domain);
+    }
+
+    LM_DBG("Have a notification to send for the following IMPUs using domain [%.*s]\n", domain->name->len, domain->name->s);
+
+
+    content = generate_reginfo_full(domain, n->impus, n->num_impus);
+
     str method = {"NOTIFY", 6};
 
+    LM_DBG("Notification content: [%.*s]", content.len, content.s);
     LM_DBG("DBG:send_notification: NOTIFY about <%.*s>\n", n->watcher_uri.len, n->watcher_uri.s);
 
     h.len = 0;
@@ -1975,14 +1964,12 @@ void send_notification(reg_notification * n) {
         return;
     }
 
-
-    if (n->content.len) {
-
+    if (content.len) {
         LM_DBG("Notification content exists - about to send notification with subscription state: [%.*s] content_type: [%.*s] content: [%.*s] : presentity_uri: [%.*s] watcher_uri: [%.*s]",
-                n->subscription_state.len, n->subscription_state.s, n->content_type.len, n->content_type.s, n->content.len, n->content.s,
+                n->subscription_state.len, n->subscription_state.s, n->content_type.len, n->content_type.s, content.len, content.s,
                 n->presentity_uri.len, n->presentity_uri.s, n->watcher_uri.len, n->watcher_uri.s);
 
-        set_uac_req(&uac_r, &method, &h, &n->content, td, TMCB_LOCAL_COMPLETED,
+        set_uac_req(&uac_r, &method, &h, &content, td, TMCB_LOCAL_COMPLETED,
                 uac_request_cb, 0);
         tmb.t_request_within(&uac_r);
     } else {
@@ -2012,26 +1999,17 @@ void send_notification(reg_notification * n) {
  * @returns the r_notification or NULL on error
  */
 reg_notification * new_notification(str subscription_state,
-        str content_type, str content, reg_subscriber * r) {
-
+        str content_type, str** impus, int num_impus, reg_subscriber * r) {
+    int i;
     reg_notification *n = 0;
-
-    str buf;
-    char bufc[MAX_REGINFO_SIZE];
-
-    if (content.len > MAX_REGINFO_SIZE) {
-        LM_ERR("content size (%d) exceeds MAX_REGINFO_SIZE (%d)!\n", content.len, MAX_REGINFO_SIZE);
-        return 0;
-    }
-
-    buf.s = bufc;
-    buf.len = snprintf(buf.s, MAX_REGINFO_SIZE, content.s, r->version);
-
     int len;
     char *p;
 
     len = sizeof (reg_notification) + r->call_id.len + r->from_tag.len + r->to_tag.len + r->watcher_uri.len + r->watcher_contact.len +
-            r->record_route.len + r->sockinfo_str.len + r->presentity_uri.len + subscription_state.len + content_type.len + buf.len;
+            r->record_route.len + r->sockinfo_str.len + r->presentity_uri.len + subscription_state.len + content_type.len + (num_impus*sizeof(str)); // + buf.len;
+    for (i=0; i<num_impus; i++) {
+        len += impus[i]->len;
+    }
 
     LM_DBG("Creating new notification");
 
@@ -2106,12 +2084,16 @@ reg_notification * new_notification(str subscription_state,
     p += content_type.len;
     LM_DBG("Notification content type: [%.*s]", n->content_type.len, n->content_type.s);
 
-    n->content.s = p;
-    n->content.len = buf.len;
-    memcpy(p, buf.s, buf.len);
-    p += buf.len;
-    LM_DBG("Notification content: [%.*s]", n->content.len, n->content.s);
-
+    n->impus = p;
+    p += sizeof(str)*num_impus;
+    for (i=0; i<num_impus; i++) {
+        n->impus[i].s = p;
+        memcpy(p, impus[i]->s, impus[i]->len);
+        n->impus[i].len = impus[i]->len;
+        p += impus[i]->len;
+    }
+    n->num_impus = num_impus;
+    
     if (p != (((char*) n) + len)) {
         LM_CRIT("buffer overflow\n");
         free_notification(n);

+ 6 - 4
modules/ims_registrar_scscf/registrar_notify.h

@@ -62,12 +62,14 @@ typedef struct _reg_notification {
     
     str subscription_state; /**< Subscription-state header value*/
     str content_type; /**< content type					*/
-    str content; /**< content						*/
     
     str watcher_contact;
     str watcher_uri;
     str presentity_uri;
     
+    struct udomain* _d;
+    str* impus;
+    int num_impus;
     
     unsigned int local_cseq;
     str call_id;
@@ -126,11 +128,11 @@ int subscribe_reply(struct sip_msg *msg, int code, char *text, int *expires, str
 int event_reg(udomain_t* _d, impurecord_t* r_passed, int event_type, str *presentity_uri, str *watcher_contact);
 
 
-str generate_reginfo_full(udomain_t* _t, str* impu_list, int new_subscription, str *primary_impu, int primary_locked);
+str generate_reginfo_full(udomain_t* _t, str* impu_list, int new_subscription);
 
 str get_reginfo_partial(impurecord_t *r, ucontact_t *c, int event_type);
 
-void create_notifications(udomain_t* _t, impurecord_t* r_passed, str *presentity_uri, str *watcher_contact, str content, int event_type);
+void create_notifications(udomain_t* _t, impurecord_t* r_passed, str *presentity_uri, str *watcher_contact, str** impus, int num_impus, int event_type);
 
 void notification_event_process();
 
@@ -141,7 +143,7 @@ void send_notification(reg_notification * n);
 void add_notification(reg_notification *n);
 
 reg_notification* new_notification(str subscription_state,
-        str content_type, str content, reg_subscriber* r);
+        str content_type, str** impus, int num_impus, reg_subscriber* r);
 
 dlg_t* build_dlg_t_from_notification(reg_notification* n);
 

+ 10 - 3
modules/ims_usrloc_scscf/udomain.c

@@ -626,7 +626,7 @@ int delete_impurecord(udomain_t* _d, str* _aor, struct impurecord* _r) {
  * barring-(-1) get all records
  * NB. Remember to free the block of memory pointed to by impus (pkg_malloc)
  */
-int get_impus_from_subscription_as_string(udomain_t* _d, impurecord_t* impu_rec, int barring, str** impus, int* num_impus) {
+int get_impus_from_subscription_as_string(udomain_t* _d, impurecord_t* impu_rec, int barring, str** impus, int* num_impus, int is_shm) {
     int i, j, count;
     *num_impus = 0;
     *impus = 0;
@@ -669,9 +669,16 @@ int get_impus_from_subscription_as_string(udomain_t* _d, impurecord_t* impu_rec,
     LM_DBG("num of records returned is %d and we need %d bytes\n", *num_impus, bytes_needed);
 
     len = (sizeof (str)*(*num_impus)) + bytes_needed;
-    *impus = (str*) pkg_malloc(len); //TODO: rather put this on the stack... dont' fragment pkg....
+    if (is_shm)
+        *impus = (str*) shm_malloc(len); 
+    else 
+        *impus = (str*) pkg_malloc(len); //TODO: rather put this on the stack... dont' fragment pkg....
+    
     if (*impus == 0) {
-        LM_ERR("no more pkg_mem\n");
+        if (is_shm)
+            LM_ERR("no more shm_mem\n");
+        else 
+            LM_ERR("no more pkg_mem\n");
         return 1;
     }
     char* ptr = (char*) (*impus + *num_impus);

+ 1 - 1
modules/ims_usrloc_scscf/udomain.h

@@ -226,7 +226,7 @@ int delete_impurecord(udomain_t* _d, str* _aor, struct impurecord* _r);
  * barring-0 get all unbarred
  * barring-(-1) get all records
  */
-int get_impus_from_subscription_as_string(udomain_t* _d, impurecord_t* impu_rec, int barring, str** impus, int* num_impus);
+int get_impus_from_subscription_as_string(udomain_t* _d, impurecord_t* impu_rec, int barring, str** impus, int* num_impus, int is_shm);
 
 int get_subscription(str* impi_s, ims_subscription** s, int leave_slot_locked);
 void add_subscription(ims_subscription* s);

+ 1 - 1
modules/ims_usrloc_scscf/usrloc.h

@@ -496,7 +496,7 @@ typedef int (*get_subscriber_t)(impurecord_t* urec, str *watcher_contact, str *p
 typedef int (*add_subscriber_t)(impurecord_t* urec,
 		subscriber_data_t* subscriber_data, reg_subscriber** _reg_subscriber, int db_load);
 
-typedef int (*get_impus_from_subscription_as_string_t)(udomain_t* _d, impurecord_t* impu_rec, int barring, str** impus, int* num_impus);
+typedef int (*get_impus_from_subscription_as_string_t)(udomain_t* _d, impurecord_t* impu_rec, int barring, str** impus, int* num_impus, int is_shm);
 
 typedef str (*get_presentity_from_subscriber_dialog_t)(str *callid, str *to_tag, str *from_tag);
 

+ 3 - 3
modules/matrix/README

@@ -139,11 +139,11 @@ Chapter 1. Admin Guide
 
    The URL for the database connection.
 
-   Default value is "mysql://openserro:openserro@localhost/openser".
+   Default value is "mysql://kamailioro:kamailioro@localhost/kamailio".
 
    Example 1.1. Set db_url parameter
 ...
-modparam("matrix", "db_url", "mysql://openserro:openserro@localhost/openser")
+modparam("matrix", "db_url", "mysql://kamailioro:kamailioro@localhost/kamailio")
 ...
 
 3.2. matrix_table (string)
@@ -264,7 +264,7 @@ Chapter 2. Module parameter for database access.
 
    URL to the database containing the data.
 
-   Default value is "mysql://openserro:openserro@localhost/openser".
+   Default value is "mysql://kamailioro:kamailioro@localhost/kamailio".
 
    Example 2.1. Set db_url parameter
 ...

+ 6 - 6
modules/matrix/doc/matrix_admin.xml

@@ -62,7 +62,7 @@
 
 <section>
 	<title>Parameters</title>
-	<section>
+	<section id="matrix.p.db_url">
 		<title><varname>db_url</varname> (string)</title>
 	  <para>
 			The URL for the database connection.
@@ -81,7 +81,7 @@ modparam("matrix", "db_url", "&defaultrodb;")
 		  </programlisting>
 	  </example>
   </section>
-	<section>
+	<section id="matrix.p.matrix_table">
 		<title><varname>matrix_table</varname> (string)</title>
 	  <para>
 			The name of the table containing the matrix data.
@@ -100,7 +100,7 @@ modparam("matrix", "matrix_table", "matrix")
 		  </programlisting>
 	  </example>
   </section>
-	<section>
+	<section id="matrix.p.matrix_first_col">
 		<title><varname>matrix_first_col</varname> (string)</title>
 	  <para>
 			The name of the column containing the first row in the matrix.
@@ -119,7 +119,7 @@ modparam("matrix", "matrix_first_col", "first")
 		  </programlisting>
 	  </example>
   </section>
-	<section>
+	<section id="matrix.p.matrix_second_col">
 		<title><varname>matrix_second_col</varname> (string)</title>
 	  <para>
 			The name of the column containing the second row in the matrix.
@@ -138,7 +138,7 @@ modparam("matrix", "matrix_second_col", "second")
 		  </programlisting>
 	  </example>
   </section>
-	<section>
+	<section id="matrix.p.matrix_res_col">
 		<title><varname>matrix_res_col</varname> (string)</title>
 	  <para>
 			The name of the column containing the result ID to be used.
@@ -160,7 +160,7 @@ modparam("matrix", "matrix_res_col", "res")
 </section>
 <section>
 	<title>Functions</title>
-	<section>
+	<section id="matrix.f.matrix">
 	  <title>
 			<function moreinfo="none">matrix (string first, string second, string dstavp)</function>
 		</title>

+ 34 - 38
modules/nat_traversal/nat_traversal.c

@@ -34,6 +34,7 @@
    traversal for SIP signaling. 
  */
 
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -98,12 +99,7 @@ MODULE_VERSION
 #define STR_HAS_IPREFIX(str, prefix) ((str).len>(prefix).len && strncasecmp((prefix).s, (str).s, (prefix).len)==0)
 
 
-typedef int Bool;
-#define True  1
-#define False 0
-
-
-typedef Bool (*NatTestFunction)(struct sip_msg *msg);
+typedef bool (*NatTestFunction)(struct sip_msg *msg);
 
 typedef enum {
     NTNone=0,
@@ -162,7 +158,7 @@ typedef struct Dialog_Param {
     char *caller_uri;
     char *callee_uri;
     time_t expire;
-    Bool confirmed;
+    bool confirmed;
     gen_lock_t lock;
     struct {
         char **uri;
@@ -194,9 +190,9 @@ static int NAT_Keepalive(struct sip_msg *msg);
 static int FixContact(struct sip_msg *msg);
 static int ClientNatTest(struct sip_msg *msg, unsigned int tests);
 
-static Bool test_private_contact(struct sip_msg *msg);
-static Bool test_source_address(struct sip_msg *msg);
-static Bool test_private_via(struct sip_msg *msg);
+static bool test_private_contact(struct sip_msg *msg);
+static bool test_source_address(struct sip_msg *msg);
+static bool test_private_via(struct sip_msg *msg);
 
 static INLINE char* shm_strdup(char *source);
 
@@ -215,7 +211,7 @@ static int pv_get_source_uri(struct sip_msg *msg, pv_param_t *param, pv_value_t
 //
 static HashTable *nat_table = NULL;
 
-static Bool keepalive_disabled = False;
+static bool keepalive_disabled = false;
 
 static unsigned int keepalive_interval = 60;
 
@@ -225,7 +221,7 @@ static Keepalive_Params keepalive_params = {"NOTIFY", NULL, "", "", 0, 0, ""};
 
 struct tm_binds  tm_api;
 struct dlg_binds dlg_api;
-Bool have_dlg_api = False;
+bool have_dlg_api = false;
 
 static int dialog_flag = -1;
 static unsigned dialog_default_timeout = 12*3600;  // 12 hours
@@ -466,7 +462,7 @@ NAT_Contact_del(NAT_Contact *contact)
 }
 
 
-static Bool
+static bool
 NAT_Contact_match(NAT_Contact *contact, const char *uri)
 {
     return strcmp(contact->uri, uri)==0;
@@ -674,24 +670,24 @@ Dialog_Param_del(Dialog_Param *param)
 
 // This function assumes the caller has locked the Dialog_Param while operating on it
 //
-static Bool
+static bool
 Dialog_Param_has_candidate(Dialog_Param *param, char *candidate)
 {
     int i;
 
     for (i=0; i<param->callee_candidates.count; i++) {
         if (strcmp(candidate, param->callee_candidates.uri[i])==0) {
-            return True;
+            return true;
         }
     }
 
-    return False;
+    return false;
 }
 
 
 // This function assumes the caller has locked the Dialog_Param while operating on it
 //
-static Bool
+static bool
 Dialog_Param_add_candidate(Dialog_Param *param, char *candidate)
 {
     char **new_uri, *new_candidate;
@@ -703,7 +699,7 @@ Dialog_Param_add_candidate(Dialog_Param *param, char *candidate)
         new_uri = shm_realloc(param->callee_candidates.uri, new_size * sizeof(char*));
         if (!new_uri) {
             LM_ERR("failed to grow callee_candidates uri list\n");
-            return False;
+            return false;
         }
         param->callee_candidates.uri = new_uri;
         param->callee_candidates.size = new_size;
@@ -712,13 +708,13 @@ Dialog_Param_add_candidate(Dialog_Param *param, char *candidate)
     new_candidate = shm_strdup(candidate);
     if (!new_candidate) {
         LM_ERR("cannot allocate shared memory for new candidate uri\n");
-        return False;
+        return false;
     }
 
     param->callee_candidates.uri[param->callee_candidates.count] = new_candidate;
     param->callee_candidates.count++;
 
-    return True;
+    return true;
 }
 
 
@@ -774,30 +770,30 @@ shm_strdup(char *source)
 }
 
 
-static Bool
+static bool
 get_contact_uri(struct sip_msg* msg, struct sip_uri *uri, contact_t **_c)
 {
 
     if ((parse_headers(msg, HDR_CONTACT_F, 0) == -1) || !msg->contact)
-        return False;
+        return false;
 
     if (!msg->contact->parsed && parse_contact(msg->contact) < 0) {
         LM_ERR("cannot parse the Contact header\n");
-        return False;
+        return false;
     }
 
     *_c = ((contact_body_t*)msg->contact->parsed)->contacts;
 
     if (*_c == NULL) {
-        return False;
+        return false;
     }
 
     if (parse_uri((*_c)->uri.s, (*_c)->uri.len, uri) < 0 || uri->host.len <= 0) {
         LM_ERR("cannot parse the Contact URI\n");
-        return False;
+        return false;
     }
 
-    return True;
+    return true;
 }
 
 
@@ -828,10 +824,10 @@ rfc1918address(str *address)
 
 
 // Test if address of signaling is different from address in 1st Via field
-static Bool
+static bool
 test_source_address(struct sip_msg *msg)
 {
-    Bool different_ip, different_port;
+    bool different_ip, different_port;
     int via1_port;
 
     different_ip = received_via_test(msg);
@@ -843,21 +839,21 @@ test_source_address(struct sip_msg *msg)
 
 
 // Test if Contact field contains a private IP address as defined in RFC1918
-static Bool
+static bool
 test_private_contact(struct sip_msg *msg)
 {
     struct sip_uri uri;
     contact_t* contact;
 
     if (!get_contact_uri(msg, &uri, &contact))
-        return False;
+        return false;
 
     return is_private_address(&(uri.host));
 }
 
 
 // Test if top Via field contains a private IP address as defined in RFC1918
-static Bool
+static bool
 test_private_via(struct sip_msg *msg)
 {
     return is_private_address(&(msg->via1->host));
@@ -898,7 +894,7 @@ get_register_expire(struct sip_msg *request, struct sip_msg *reply)
     param_t *expires_param;
     time_t now, expire=0;
     unsigned exp;
-    Bool matched;
+    bool matched;
 
     if (!request->contact)
         return 0;
@@ -935,7 +931,7 @@ get_register_expire(struct sip_msg *request, struct sip_msg *reply)
         }
 
         for (contact=contact_body->contacts; contact; contact=contact->next) {
-            for (r_hdr=reply->contact, matched=False; r_hdr && !matched; r_hdr=next_sibling_hdr(r_hdr)) {
+            for (r_hdr=reply->contact, matched=false; r_hdr && !matched; r_hdr=next_sibling_hdr(r_hdr)) {
                 if (!r_hdr->parsed && parse_contact(r_hdr) < 0) {
                     LM_ERR("failed to parse the Contact header body in reply\n");
                     continue;
@@ -946,7 +942,7 @@ get_register_expire(struct sip_msg *request, struct sip_msg *reply)
                         expires_param = r_contact->expires;
                         if (expires_param && expires_param->body.len && str2int(&expires_param->body, &exp) == 0)
                             expire = max(expire, exp);
-                        matched = True;
+                        matched = true;
                         break;
                     }
                 }
@@ -1092,7 +1088,7 @@ __dialog_confirmed(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params
 
     lock_get(&param->lock);
 
-    param->confirmed = True;
+    param->confirmed = true;
 
     callee_uri = get_source_uri(_params->rpl);
 
@@ -1684,7 +1680,7 @@ restore_keepalive_state(void)
 
     res = fscanf(f, STATE_FILE_HEADER); // skip header
 
-    while (True) {
+    while (true) {
         res = fscanf(f, "%63s %63s %ld %ld", uri, socket, &rtime, &stime);
         if (res == EOF) {
             if (ferror(f))
@@ -1734,7 +1730,7 @@ mod_init(void)
 
     if (keepalive_interval <= 0) {
         LM_NOTICE("keepalive functionality is disabled from the configuration\n");
-        keepalive_disabled = True;
+        keepalive_disabled = true;
         return 0;
     }
 
@@ -1764,7 +1760,7 @@ mod_init(void)
         param = find_param_export(find_module_by_name("dialog"),
 				"dlg_flag", INT_PARAM, &type);
         if (param) {
-		have_dlg_api = True;
+		have_dlg_api = true;
 
 		dialog_flag = *param;
 

+ 1 - 1
modules/ndb_mongodb/README

@@ -105,7 +105,7 @@ Chapter 1. Admin Guide
 
 3.1. server (str)
 
-   Specify the details to connect to REDIS server. It takes a list of
+   Specify the details to connect to MongoDB server. It takes a list of
    attribute=value separated by semicolon, the attributes can be name and
    uri. Name is a generic identifier to be used with module functions. The
    uri parameter must be a valid MongoDB database connection string.

+ 1 - 1
modules/ndb_mongodb/doc/ndb_mongodb_admin.xml

@@ -62,7 +62,7 @@
 	<section id="ndb_mongodb.p.server">
 		<title><varname>server</varname> (str)</title>
 		<para>
-			Specify the details to connect to REDIS server. It takes a list of
+			Specify the details to connect to MongoDB server. It takes a list of
 			attribute=value separated by semicolon, the attributes can be
 			name and uri. Name is a generic identifier to be used
 			with module functions. The uri parameter must be a valid

+ 36 - 21
modules/permissions/README

@@ -63,6 +63,7 @@ Emmanuel Schmidbauer
               3.19. priority_col (string)
               3.20. peer_tag_avp (AVP string)
               3.21. peer_tag_mode (integer)
+              3.22. max_subnets (int)
 
         4. Functions
 
@@ -119,17 +120,18 @@ Emmanuel Schmidbauer
    1.19. Set priority_col parameter
    1.20. Set peer_tag_avp parameter
    1.21. Set peer_tag_mode parameter
-   1.22. allow_routing usage
-   1.23. allow_routing(basename) usage
-   1.24. allow_routing(allow_file, deny_file) usage
-   1.25. allow_register(basename) usage
-   1.26. allow_register(allow_file, deny_file) usage
-   1.27. allow_uri(basename, pvar) usage
-   1.28. allow_address() usage
-   1.29. allow_source_address(group_id) usage
-   1.30. allow_source_address_group() usage
+   1.22. Set max_subnets parameter
+   1.23. allow_routing usage
+   1.24. allow_routing(basename) usage
+   1.25. allow_routing(allow_file, deny_file) usage
+   1.26. allow_register(basename) usage
+   1.27. allow_register(allow_file, deny_file) usage
+   1.28. allow_uri(basename, pvar) usage
+   1.29. allow_address() usage
+   1.30. allow_source_address(group_id) usage
    1.31. allow_source_address_group() usage
-   1.32. allow_trusted() usage
+   1.32. allow_source_address_group() usage
+   1.33. allow_trusted() usage
 
 Chapter 1. Admin Guide
 
@@ -171,6 +173,7 @@ Chapter 1. Admin Guide
         3.19. priority_col (string)
         3.20. peer_tag_avp (AVP string)
         3.21. peer_tag_mode (integer)
+        3.22. max_subnets (int)
 
    4. Functions
 
@@ -403,6 +406,7 @@ Chapter 1. Admin Guide
    3.19. priority_col (string)
    3.20. peer_tag_avp (AVP string)
    3.21. peer_tag_mode (integer)
+   3.22. max_subnets (int)
 
 3.1. default_allow_file (string)
 
@@ -683,6 +687,17 @@ modparam("permissions", "peer_tag_avp", "$avp(i:707)")
 modparam("permissions", "peer_tag_mode", 1)
 ...
 
+3.22. max_subnets (int)
+
+   The maximum number of subnet addresses to be loaded from address table.
+
+   Default value is "512".
+
+   Example 1.22. Set max_subnets parameter
+...
+modparam("permissions", "max_subnets", 1024)
+...
+
 4. Functions
 
    4.1. allow_routing()
@@ -706,7 +721,7 @@ modparam("permissions", "peer_tag_mode", 1)
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
-   Example 1.22. allow_routing usage
+   Example 1.23. allow_routing usage
 ...
 if (allow_routing()) {
         t_relay();
@@ -729,7 +744,7 @@ if (allow_routing()) {
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
-   Example 1.23. allow_routing(basename) usage
+   Example 1.24. allow_routing(basename) usage
 ...
 if (allow_routing("basename")) {
         t_relay();
@@ -754,7 +769,7 @@ if (allow_routing("basename")) {
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
-   Example 1.24. allow_routing(allow_file, deny_file) usage
+   Example 1.25. allow_routing(allow_file, deny_file) usage
 ...
 if (allow_routing("rules.allow", "rules.deny")) {
         t_relay();
@@ -777,7 +792,7 @@ if (allow_routing("rules.allow", "rules.deny")) {
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
-   Example 1.25. allow_register(basename) usage
+   Example 1.26. allow_register(basename) usage
 ...
 if (method=="REGISTER") {
         if (allow_register("register")) {
@@ -807,7 +822,7 @@ if (method=="REGISTER") {
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
-   Example 1.26. allow_register(allow_file, deny_file) usage
+   Example 1.27. allow_register(allow_file, deny_file) usage
 ...
 if (method=="REGISTER") {
         if (allow_register("register.allow", "register.deny")) {
@@ -836,7 +851,7 @@ if (method=="REGISTER") {
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
-   Example 1.27. allow_uri(basename, pvar) usage
+   Example 1.28. allow_uri(basename, pvar) usage
 ...
 if (allow_uri("basename", "$rt")) {  // Check Refer-To URI
         t_relay();
@@ -862,7 +877,7 @@ if (allow_uri("basename", "$avp(i:705)") {  // Check URI stored in $avp(i:705)
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
-   Example 1.28. allow_address() usage
+   Example 1.29. allow_address() usage
 ...
 
 // Check if source address/port is in group 1
@@ -884,7 +899,7 @@ if (!allow_address("2", "$avp(dst_adr)", "$avp(dst_port)") {
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
-   Example 1.29. allow_source_address(group_id) usage
+   Example 1.30. allow_source_address(group_id) usage
 ...
 
 // Check source address/port of request
@@ -901,7 +916,7 @@ if (!allow_source_address("1")) {
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
-   Example 1.30. allow_source_address_group() usage
+   Example 1.31. allow_source_address_group() usage
 ...
 
 $var(group) = allow_source_address_group();
@@ -919,7 +934,7 @@ if ($var(group) != -1) {
 
    This function can be used from ANY_ROUTE.
 
-   Example 1.31. allow_source_address_group() usage
+   Example 1.32. allow_source_address_group() usage
 ...
 
 $var(group) = allow_address_group("1.2.3.4", "5060");
@@ -944,7 +959,7 @@ if ($var(group) != -1) {
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
-   Example 1.32. allow_trusted() usage
+   Example 1.33. allow_trusted() usage
 ...
 if (allow_trusted()) {
         t_relay();

+ 20 - 0
modules/permissions/doc/permissions_admin.xml

@@ -769,6 +769,26 @@ modparam("permissions", "peer_tag_avp", "$avp(i:707)")
 ...
 modparam("permissions", "peer_tag_mode", 1)
 ...
+</programlisting>
+		</example>
+	</section>
+	<section id ="permissions.p.max_subnets">
+		<title><varname>max_subnets</varname> (int)</title>
+		<para>
+			The maximum number of subnet addresses to be loaded from
+			address table.
+		</para>
+		<para>
+		<emphasis>
+		Default value is <quote>512</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>max_subnets</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("permissions", "max_subnets", 1024)
+...
 </programlisting>
 		</example>
 	</section>

+ 3 - 0
modules/permissions/hash.c

@@ -44,6 +44,9 @@ static int_str tag_avp;
 extern int peer_tag_mode;
 
 
+extern int _perm_max_subnets;
+
+#define PERM_MAX_SUBNETS _perm_max_subnets
 
 /*
  * Parse and set tag AVP specs

+ 0 - 2
modules/permissions/hash.h

@@ -176,8 +176,6 @@ int addr_hash_table_rpc_print(struct addr_list** table, rpc_t* rpc, void* c);
 void empty_addr_hash_table(struct addr_list** hash_table);
 
 
-#define PERM_MAX_SUBNETS 128 
-
 
 /*
  * Structure used to store a subnet

+ 3 - 0
modules/permissions/permissions.c

@@ -88,6 +88,8 @@ str port_col = str_init("port");           /* Name of port column */
 static int check_all_branches = 1;
 
 
+int _perm_max_subnets = 512;
+
 /*  
  * Convert the name of the files into table index
  */
@@ -178,6 +180,7 @@ static param_export_t params[] = {
 	{"ip_addr_col",        PARAM_STR, &ip_addr_col     },
 	{"mask_col",           PARAM_STR, &mask_col        },
 	{"port_col",           PARAM_STR, &port_col        },
+	{"max_subnets",        PARAM_INT, &_perm_max_subnets },
 	{0, 0, 0}
 };
 

+ 17 - 5
modules/presence/notify.c

@@ -1470,6 +1470,7 @@ int send_notify_request(subs_t* subs, subs_t * watcher_subs,
 	c_back_param *cb_param= NULL;
 	str* final_body= NULL;
 	uac_req_t uac_r;
+	str* aux_body = NULL;
 	
 	LM_DBG("dialog info:\n");
 	printf_subs(subs);
@@ -1532,7 +1533,11 @@ int send_notify_request(subs_t* subs, subs_t * watcher_subs,
 					/* call aux_body_processing if exists */
 					if(subs->event->aux_body_processing)
 					{
-						subs->event->aux_body_processing(subs, notify_body);
+						aux_body = subs->event->aux_body_processing(subs, notify_body);
+						if(aux_body) {
+							free_notify_body(notify_body, subs->event);
+							notify_body = aux_body;
+						}
 					}
 
 					/* apply authorization rules if exists */
@@ -1607,11 +1612,12 @@ jump_over_body:
 	}
 
 	LM_GEN1(pres_local_log_level,
-		"NOTIFY %.*s via %.*s on behalf of %.*s for event %.*s\n",
+		"NOTIFY %.*s via %.*s on behalf of %.*s for event %.*s : %.*s\n",
 		td->rem_uri.len, td->rem_uri.s, td->hooks.next_hop->len,
 		td->hooks.next_hop->s,
-		td->loc_uri.len, td->loc_uri.s, subs->event->name.len,
-		subs->event->name.s);
+		td->loc_uri.len, td->loc_uri.s,
+		subs->event->name.len, subs->event->name.s,
+		subs->callid.len, subs->callid.s);
 
 	ps_free_tm_dlg(td);
 	
@@ -2854,7 +2860,13 @@ int process_dialogs(int round, int presence_winfo)
 
 		if (dialog->n > 1)
 		{
-			LM_ERR("multiple records found\n");
+			LM_ERR("multiple records found for %.*s, ci : %.*s, tt : %.*s, ft : %.*s, ev : %.*s\n",
+					sub.pres_uri.len, sub.pres_uri.s,
+					sub.callid.len, sub.callid.s,
+					sub.to_tag.len, sub.to_tag.s,
+					sub.from_tag.len, sub.from_tag.s,
+					ev_sname.len, ev_sname.s
+					);
 			goto delete_dialog;
 		}
 

+ 1 - 1
modules/presence/publish.c

@@ -509,7 +509,7 @@ error:
 int update_hard_presentity(str *pres_uri, pres_ev_t *event, str *file_uri, str *filename)
 {
 	int ret = -1, new_t, pidf_result;
-	str *pidf_doc;
+	str *pidf_doc = 0;
 	char *sphere = NULL;
 	presentity_t *pres = NULL;
 	struct sip_uri parsed_uri;

+ 1 - 0
modules/presence_dialoginfo/add_events.c

@@ -57,6 +57,7 @@ int dlginfo_add_events(void)
 
 	/* modify XML body for each watcher to set the correct "version" */
     event.aux_body_processing = dlginfo_body_setversion;
+    event.aux_free_body = free_xml_body;
 
 	
     if (pres_add_event(&event) < 0) {

+ 36 - 2
modules/presence_dialoginfo/notify_body.c

@@ -538,6 +538,7 @@ str *dlginfo_body_setversion(subs_t *subs, str *body) {
 	char *version_start=0;
 	char version[MAX_INT_LEN + 2]; /* +2 becasue of trailing " and \0 */
 	int version_len;
+	str* aux_body = NULL;
 
 	if (!body) {
 		return NULL;
@@ -564,9 +565,29 @@ str *dlginfo_body_setversion(subs_t *subs, str *body) {
 	version_len = snprintf(version, MAX_INT_LEN + 2,"%d\"", subs->version);
 	if (version_len >= MAX_INT_LEN + 2) {
 		LM_ERR("failed to convert 'version' to string\n");
-		memcpy(version_start, "00000000000\"", 12);
 		return NULL;
 	}
+
+	aux_body= (str*)pkg_malloc(sizeof(str));
+	if(aux_body== NULL)
+	{
+		LM_ERR("error allocating memory for aux body str\n");
+		return NULL;
+	}
+	memset(aux_body, 0, sizeof(str));
+	aux_body->s= (char*)pkg_malloc( body->len * sizeof(char));
+	if(aux_body->s== NULL)
+	{
+		pkg_free(aux_body);
+		LM_ERR("error allocating memory for aux body buffer\n");
+		return NULL;
+	}
+	memcpy(aux_body->s, body->s, body->len);
+	aux_body->len= body->len;
+
+	/* again but on the copied str, no checks needed */
+	version_start = strstr(aux_body->s + 34, "version=");
+	version_start += 9;
 	/* Replace the placeholder 00000000000 with the version.
 	 * Put the padding behind the ""
 	 */
@@ -574,5 +595,18 @@ str *dlginfo_body_setversion(subs_t *subs, str *body) {
 	memcpy(version_start, version, version_len);
 	memset(version_start + version_len, ' ', 12 - version_len);
 
-	return NULL;
+	xmlDocPtr doc = xmlReadMemory(aux_body->s, aux_body->len, "noname.xml", NULL, 0);
+        if (doc == NULL) {
+		LM_ERR("error allocation xmldoc\n");
+		pkg_free(aux_body->s);
+		pkg_free(aux_body);
+		return NULL;
+	}
+	pkg_free(aux_body->s);
+        xmlDocDumpFormatMemory(doc,(xmlChar**)(void*)&aux_body->s, &aux_body->len, 1);
+	xmlFreeDoc(doc);
+        xmlCleanupParser();
+        xmlMemoryDump();
+
+	return aux_body;
 }

+ 33 - 10
modules/presence_xml/notify_body.c

@@ -58,27 +58,51 @@ void free_xml_body(char* body)
 	body= NULL;
 }
 
-#define PRESENCE_EMPTY_BODY_SIZE 512
+#define PRESENCE_EMPTY_BODY_SIZE 1024
 
-#define PRESENCE_EMPTY_BODY "<presence> \
-<tuple id=\"615293b33c62dec073e05d9421e9f48b\">\
+#define PRESENCE_EMPTY_BODY  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
+<presence xmlns=\"urn:ietf:params:xml:ns:pidf\" xmlns:dm=\"urn:ietf:params:xml:ns:pidf:data-model\" xmlns:rpid=\"urn:ietf:params:xml:ns:pidf:rpid\" xmlns:c=\"urn:ietf:params:xml:ns:pidf:cipid\" entity=\"%.*s\"> \
+<tuple xmlns=\"urn:ietf:params:xml:ns:pidf\" id=\"615293b33c62dec073e05d9421e9f48b\">\
 <status>\
 <basic>open</basic>\
 </status>\
 </tuple>\
 <note xmlns=\"urn:ietf:params:xml:ns:pidf\">Available</note>\
+<dm:person xmlns:dm=\"urn:ietf:params:xml:ns:pidf:data-model\" xmlns:rpid=\"urn:ietf:params:xml:ns:pidf:rpid\" id=\"1\">\
+<rpid:activities/>\
+<dm:note>Available</dm:note>\
+</dm:person>\
 </presence>"
 
 str* pres_agg_nbody_empty(str* pres_user, str* pres_domain)
 {
 	str* n_body= NULL;
 
+	str* body_array;
+	char* body;
+
 	LM_DBG("creating empty presence for [pres_user]=%.*s [pres_domain]= %.*s\n",
 			pres_user->len, pres_user->s, pres_domain->len, pres_domain->s);
 
-	str* body_array = (str*)pkg_malloc(sizeof(str));
-	char* body = (char*)pkg_malloc(PRESENCE_EMPTY_BODY_SIZE);
-	sprintf(body, PRESENCE_EMPTY_BODY);
+	if(pres_user->len+sizeof(PRESENCE_EMPTY_BODY)
+			>= PRESENCE_EMPTY_BODY_SIZE - 1) {
+		LM_ERR("insufficient buffer to add user (its len is: %d)\n",
+				pres_user->len);
+		return NULL;
+	}
+	body_array = (str*)pkg_malloc(sizeof(str));
+	if(body_array==NULL) {
+		LM_ERR("no more pkg\n");
+		return NULL;
+	}
+
+	body = (char*)pkg_malloc(PRESENCE_EMPTY_BODY_SIZE);
+	if(body_array==NULL) {
+		LM_ERR("no more pkg\n");
+		pkg_free(body_array);
+		return NULL;
+	}
+	snprintf(body, PRESENCE_EMPTY_BODY_SIZE, PRESENCE_EMPTY_BODY, pres_user->len, pres_user->s);
 	body_array->s = body;
 	body_array->len = strlen(body);
 
@@ -86,11 +110,10 @@ str* pres_agg_nbody_empty(str* pres_user, str* pres_domain)
 	n_body= agregate_xmls(pres_user, pres_domain, &body_array, 1);
 	LM_DBG("[n_body]=%p\n", n_body);
 	if(n_body) {
-		LM_DBG("[*n_body]=%.*s\n",n_body->len, n_body->s);
+		LM_DBG("[*n_body]=%.*s\n", n_body->len, n_body->s);
 	}
-	if(n_body== NULL)
-	{
-		LM_ERR("while aggregating body\n");
+	if(n_body== NULL) {
+		LM_ERR("while aggregating body for: %.*s\n", pres_user->len, pres_user->s);
 	}
 
 	pkg_free(body);

+ 6 - 1
modules/registrar/reg_mod.c

@@ -362,8 +362,13 @@ static int mod_init(void)
 			sock_hdr_name.len = 0;
 			sock_flag = -1;
 		}
+	} else if (reg_xavp_cfg.s) {
+		if (reg_xavp_cfg.len == 0 || sock_flag == -1) {
+			LM_WARN("empty reg_xavp_cfg or sock_flag no set -> resetting\n");
+			sock_flag = -1;
+		}
 	} else if (sock_flag!=-1) {
-		LM_WARN("sock_flag defined but no sock_hdr_name -> reseting flag\n");
+		LM_WARN("sock_flag defined but no sock_hdr_name or no reg_xavp_cfg -> resetting flag\n");
 		sock_flag = -1;
 	}
 

+ 122 - 3
modules/rtpengine/doc/rtpengine_admin.xml

@@ -73,6 +73,38 @@
 		If the set was selected using setid_avp, the avp needs to be
 		set only once before rtpengine_offer() or rtpengine_manage() call.
 	</para>
+	<para>
+		From the current implementation point of view, the sets of rtpproxy nodes
+		are shared memory(shm), so all processes can see a common list of nodes.
+		There is no locking when setting the nodes enabled/disabled (to keep the
+		memory access as fast as possible). Thus, problems related to node state
+		might appear for concurent processes that might set the nodes
+		enabled/disabled(e.g. by fifo command). This robustness problems are overcomed as follows.
+	</para>
+
+	<para>
+		If the current process sees the selected node as disabled, the node is
+		<emphasis>force tested</emphasis> before the current process actually
+		takes the disabled decision. If the test succeeds, the process will set
+		the node as enabled (but other concurrent process might still see it as disabled).
+.
+	</para>
+
+	<para>
+		If the current process sees the selected node as enabled, it does no additional checks
+		and sends the command which will fail in case the machine is actually broken.
+		The process will set the node as disabled (but other concurrent process might still see it as enabled).
+	</para>
+
+	<para>
+		The 'kamctl fifo' commands (including rtpengin ones) are executed by an exclusive
+		process which operate on the same shared memory node list.
+	</para>
+
+	<para>
+		All the nodes are pinged in the beginning by all the processes,
+		even if the node list is shared memory.
+	</para>
 	</section>
 
 	<section>
@@ -175,6 +207,30 @@ modparam("rtpengine", "rtpengine_disable_tout", 20)
 ...
 modparam("rtpengine", "rtpengine_tout_ms", 2000)
 ...
+</programlisting>
+		</example>
+	</section>
+	<section id="rtpengine.p.rtpengine_allow_op">
+		<title><varname>rtpengine_allow_op</varname> (integer)</title>
+		<para>
+		Enable this to allow finishing the current sessions while denying new sessions for the
+		<emphasis>manually deactivated nodes </emphasis> via kamctl command i.e. "disabled(permanent)" nodes.
+		Probably the manually deactivated machine is still running(did not crash).
+		</para>
+		<para>
+		This is <emphasis>useful</emphasis> when deactivating a node for maintanance and reject new sessions but allow current ones to finish.
+		</para>
+		<para>
+		<emphasis>
+		Default value is <quote>0</quote> to keep the current behaviour.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>rtpengine_allow_op</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("rtpengine", "rtpengine_allow_op", 1)
+...
 </programlisting>
 		</example>
 	</section>
@@ -305,7 +361,7 @@ modparam("rtpengine", "read_sdp_pv", "$var(sdp)")
 route {
 	...
 	$var(sdp) = $rb + "a=foo:bar\r\n";
-	rtpproxy_manage();
+	rtpengine_manage();
 }
 </programlisting>
 		</example>
@@ -330,7 +386,7 @@ modparam("rtpengine", "write_sdp_pv", "$avp(sdp)")
 ...
 route {
 	...
-	rtpproxy_manage();
+	rtpengine_manage();
 	set_body("$avp(sdp)a=baz123\r\n", "application/sdp");
 }
 </programlisting>
@@ -350,12 +406,58 @@ route {
 		<title>Set <varname>rtp_inst_pvar</varname> parameter</title>
 <programlisting format="linespecific">
 ...
-modparam("rtpproxy", "rtp_inst_pvar", "$avp(RTP_INSTANCE)")
+modparam("rtpengine", "rtp_inst_pvar", "$avp(RTP_INSTANCE)")
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section id="rtpengine.p.hash_table_size">
+		<title><varname>hash_table_size</varname> (integer)</title>
+		<para>
+			Size of the hash table. Default value is 256.
+		</para>
+		<para>
+			NOTE: If configured size is <emphasis>less than</emphasis> 1, the size will be defaulted to 1.
+		</para>
+		<example>
+		<title>Set <varname>hash_table_size</varname> parameter</title>
+<programlisting format="linespecific">
+...
+modparam("rtpengine", "hash_table_size", "123")
 ...
 </programlisting>
 		</example>
 	</section>
 
+	<section id="rtpengine.p.hash_table_tout">
+		<title><varname>hash_table_tout</varname> (integer)</title>
+		<para>
+			Number of seconds after an rtpengine hash table entry is marked for deletion.
+			By default, this parameter is set to 3600 (seconds).
+		</para>
+		<para>
+			To maintain information about a selected rtp machine node, for a given call, entries are added in a hashtable of (callid, node) pairs.
+			When command comes, lookup callid. If found, return chosen node. If not found, choose a new node, insert it in the hastable and return the chosen node.
+		</para>
+		<para>
+			NOTE: In the current implementation, the actual deletion happens <emphasis>on the fly</emphasis>,
+			while insert/remove/lookup the hastable, <emphasis>only</emphasis> for the entries in the insert/remove/lookup path.
+		</para>
+		<para>
+			NOTE: When configuring this parameter, one should consider maximum call time VS share memory for unfinished calls.
+		</para>
+		<example>
+		<title>Set <varname>hash_table_tout</varname> parameter</title>
+<programlisting format="linespecific">
+...
+modparam("rtpengine", "hash_table_tout", "300")
+...
+</programlisting>
+		</example>
+	</section>
+
+
 	</section>
 
 	<section>
@@ -964,6 +1066,23 @@ $ &ctltool; fifo nh_ping_rtpp all
 			</programlisting>
 			</example>
 		</section>
+
+	    <section id="rtpengine.m.nh_show_hash_total">
+			<title><function moreinfo="none">nh_show_hash_total</function></title>
+			<para>
+				Print the total number of hash entries in the hash table at a given moment.
+			</para>
+			<example>
+			<title>
+				<function moreinfo="none">nh_show_hash_total</function> usage</title>
+			<programlisting format="linespecific">
+...
+$ &ctltool; fifo nh_show_hash_total
+...
+			</programlisting>
+			</example>
+		</section>
+
 	</section>
 
 </chapter>

+ 250 - 48
modules/rtpengine/rtpengine.c

@@ -78,6 +78,7 @@
 #include "../../modules/tm/tm_load.h"
 #include "rtpengine.h"
 #include "rtpengine_funcs.h"
+#include "rtpengine_hash.h"
 #include "bencode.h"
 
 MODULE_VERSION
@@ -108,6 +109,7 @@ MODULE_VERSION
 #define MI_ENABLE_RTP_PROXY			"nh_enable_rtpp"
 #define MI_SHOW_RTP_PROXIES			"nh_show_rtpp"
 #define MI_PING_RTP_PROXY           "nh_ping_rtpp"
+#define MI_SHOW_HASH_TOTAL          "nh_show_hash_total"
 
 #define MI_RTP_PROXY_NOT_FOUND		"RTP proxy not found"
 #define MI_RTP_PROXY_NOT_FOUND_LEN	(sizeof(MI_RTP_PROXY_NOT_FOUND)-1)
@@ -142,6 +144,10 @@ MODULE_VERSION
 #define MI_SUCCESS_LEN         		(sizeof(MI_SUCCESS)-1)
 #define MI_FAIL                     "fail"
 #define MI_FAIL_LEN         		(sizeof(MI_FAIL)-1)
+#define MI_HASH_ENTRIES				"entries"
+#define MI_HASH_ENTRIES_LEN			(sizeof(MI_HASH_ENTRIES)-1)
+#define MI_HASH_ENTRIES_FAIL		"Fail to get entry details"
+#define MI_HASH_ENTRIES_FAIL_LEN	(sizeof(MI_HASH_ENTRIES_FAIL)-1)
 
 #define MI_FOUND_ALL                   2
 #define MI_FOUND_ONE                   1
@@ -187,7 +193,9 @@ static int rtpengine_offer_answer(struct sip_msg *msg, const char *flags, int op
 static int fixup_set_id(void ** param, int param_no);
 static int set_rtpengine_set_f(struct sip_msg * msg, char * str1, char * str2);
 static struct rtpp_set * select_rtpp_set(int id_set);
-static struct rtpp_node *select_rtpp_node(str, int);
+static struct rtpp_node *select_rtpp_node_new(str, str, int);
+static struct rtpp_node *select_rtpp_node_old(str, str, int);
+static struct rtpp_node *select_rtpp_node(str, str, int);
 static char *send_rtpp_command(struct rtpp_node *, bencode_item_t *, int *);
 static int get_extra_id(struct sip_msg* msg, str *id_str);
 
@@ -210,17 +218,17 @@ static int rtpp_test_ping(struct rtpp_node *node);
 
 /* Pseudo-Variables */
 static int pv_get_rtpstat_f(struct sip_msg *, pv_param_t *, pv_value_t *);
+static int set_rtp_inst_pvar(struct sip_msg *msg, const str * const uri);
 
 /*mi commands*/
-static struct mi_root* mi_enable_rtp_proxy(struct mi_root* cmd_tree,
-		void* param );
-static struct mi_root* mi_show_rtp_proxy(struct mi_root* cmd_tree,
-		void* param);
-static struct mi_root* mi_ping_rtp_proxy(struct mi_root* cmd_tree,
-        void* param);
+static struct mi_root* mi_enable_rtp_proxy(struct mi_root* cmd_tree, void* param);
+static struct mi_root* mi_show_rtp_proxy(struct mi_root* cmd_tree, void* param);
+static struct mi_root* mi_ping_rtp_proxy(struct mi_root* cmd_tree, void* param);
+static struct mi_root* mi_show_hash_total(struct mi_root* cmd_tree, void* param);
 
 
 static int rtpengine_disable_tout = 60;
+static int rtpengine_allow_op = 0;
 static int rtpengine_retr = 5;
 static int rtpengine_tout_ms = 1000;
 static int queried_nodes_limit = MAX_RTPP_TRIED_NODES;
@@ -228,6 +236,8 @@ static pid_t mypid;
 static unsigned int myseqn = 0;
 static str extra_id_pv_param = {NULL, 0};
 static char *setid_avp_param = NULL;
+static int hash_table_tout = 3600;
+static int hash_table_size = 256;
 
 static char ** rtpp_strings=0;
 static int rtpp_sets=0; /*used in rtpengine_set_store()*/
@@ -326,6 +336,7 @@ static param_export_t params[] = {
 	{"rtpengine_disable_tout",INT_PARAM, &rtpengine_disable_tout },
 	{"rtpengine_retr",        INT_PARAM, &rtpengine_retr         },
 	{"rtpengine_tout_ms",     INT_PARAM, &rtpengine_tout_ms      },
+	{"rtpengine_allow_op",    INT_PARAM, &rtpengine_allow_op     },
 	{"queried_nodes_limit",   INT_PARAM, &queried_nodes_limit    },
 	{"db_url",                PARAM_STR, &rtpp_db_url },
 	{"table_name",            PARAM_STR, &rtpp_table_name },
@@ -336,6 +347,8 @@ static param_export_t params[] = {
 	{"rtp_inst_pvar",         PARAM_STR, &rtp_inst_pv_param },
 	{"write_sdp_pv",          PARAM_STR, &write_sdp_pvar_str          },
 	{"read_sdp_pv",           PARAM_STR, &read_sdp_pvar_str          },
+	{"hash_table_tout",       INT_PARAM, &hash_table_tout        },
+	{"hash_table_size",       INT_PARAM, &hash_table_size        },
 	{0, 0, 0}
 };
 
@@ -343,6 +356,7 @@ static mi_export_t mi_cmds[] = {
 	{MI_ENABLE_RTP_PROXY,     mi_enable_rtp_proxy,  0,  0,  0},
 	{MI_SHOW_RTP_PROXIES,     mi_show_rtp_proxy,    0,  0,  0},
 	{MI_PING_RTP_PROXY,       mi_ping_rtp_proxy,    0,  0,  0},
+	{MI_SHOW_HASH_TOTAL,      mi_show_hash_total,    0,  0,  0},
 	{ 0, 0, 0, 0, 0}
 };
 
@@ -1099,8 +1113,7 @@ error:
 	return -1;
 }
 
-static struct mi_root* mi_show_rtp_proxy(struct mi_root* cmd_tree,
-												void* param)
+static struct mi_root* mi_show_rtp_proxy(struct mi_root* cmd_tree, void* param)
 {
 	struct mi_node *node;
 	struct mi_root *root = NULL;
@@ -1189,8 +1202,7 @@ error:
 	return init_mi_tree(404, MI_ERROR, MI_ERROR_LEN);
 }
 
-static struct mi_root* mi_ping_rtp_proxy(struct mi_root* cmd_tree,
-												void* param)
+static struct mi_root* mi_ping_rtp_proxy(struct mi_root* cmd_tree, void* param)
 {
 	struct mi_node *node, *crt_node;
 	struct mi_attr *attr;
@@ -1315,6 +1327,48 @@ error:
 }
 
 
+static struct mi_root* mi_show_hash_total(struct mi_root* cmd_tree, void* param)
+{
+	struct mi_node *node, *crt_node;
+	struct mi_attr *attr;
+	struct mi_root *root = NULL;
+	unsigned int total;
+	str total_str;
+
+	// Init print tree
+	root = init_mi_tree(200, MI_OK_S, MI_OK_LEN);
+	if (!root) {
+		LM_ERR("the MI tree cannot be initialized!\n");
+		return 0;
+	}
+	node = &root->node;
+
+	// Create new node and add it to the roots's kids
+	if(!(crt_node = add_mi_node_child(node, MI_DUP_NAME, "total", strlen("total"), 0, 0))) {
+		LM_ERR("cannot add the child node to the tree\n");
+		goto error;
+	}
+
+	// Get total number of entries
+	total = rtpengine_hash_table_total();
+	total_str.s = int2str(total, &total_str.len);
+
+	// Add node attributes
+	if ((attr = add_mi_attr(crt_node, MI_DUP_VALUE, MI_HASH_ENTRIES, MI_HASH_ENTRIES_LEN, total_str.s, total_str.len)) == 0) {
+		LM_ERR("cannot add attributes to the node\n");
+		goto error;
+	}
+
+	return root;
+
+error:
+	if (root) {
+	    free_mi_tree(root);
+	}
+
+	return init_mi_tree(404, MI_HASH_ENTRIES_FAIL, MI_HASH_ENTRIES_FAIL_LEN);
+}
+
 
 static int
 mod_init(void)
@@ -1440,6 +1494,14 @@ mod_init(void)
 		return -1;
 	}
 
+	/* init the hastable which keeps the call-id <-> selected_node relation */
+	if (!rtpengine_hash_table_init(hash_table_size)) {
+		LM_ERR("rtpengine_hash_table_init(%d) failed!\n", hash_table_size);
+		return -1;
+	} else {
+		LM_DBG("rtpengine_hash_table_init(%d) success!\n", hash_table_size);
+	}
+
 	return 0;
 }
 
@@ -1579,6 +1641,13 @@ static void mod_destroy(void)
 	}
 
 	shm_free(rtpp_set_list);
+
+	/* destroy the hastable which keeps the call-id <-> selected_node relation */
+	if (!rtpengine_hash_table_destroy()) {
+		LM_ERR("rtpengine_hash_table_destroy() failed!\n");
+	} else {
+		LM_DBG("rtpengine_hash_table_destroy() success!\n");
+	}
 }
 
 
@@ -1790,7 +1859,8 @@ static bencode_item_t *rtpp_function_call(bencode_buffer_t *bencbuf, struct sip_
 {
 	struct ng_flags_parse ng_flags;
 	bencode_item_t *item, *resp;
-	str callid, from_tag, to_tag, body, viabranch, error;
+	str callid = STR_NULL, from_tag = STR_NULL, to_tag = STR_NULL, viabranch = STR_NULL;
+	str body = STR_NULL, error = STR_NULL;
 	int ret, queried_nodes;
 	struct rtpp_node *node;
 	char *cp;
@@ -1923,16 +1993,17 @@ select_node:
 			LM_ERR("queried nodes limit reached\n");
 			goto error;
 		}
-		node = select_rtpp_node(callid, 1);
+		node = select_rtpp_node(callid, viabranch, 1);
 		if (!node) {
 			LM_ERR("no available proxies\n");
 			goto error;
 		}
+
 		cp = send_rtpp_command(node, ng_flags.dict, &ret);
-        if (cp == NULL) {
-    	    node->rn_disabled = 1;
-        	node->rn_recheck_ticks = get_ticks() + rtpengine_disable_tout;
-        }
+		if (cp == NULL) {
+			node->rn_disabled = 1;
+			node->rn_recheck_ticks = get_ticks() + rtpengine_disable_tout;
+		}
 	} while (cp == NULL);
 	LM_DBG("proxy reply: %.*s\n", ret, cp);
 
@@ -1944,12 +2015,13 @@ select_node:
 		LM_ERR("failed to decode bencoded reply from proxy: %.*s\n", ret, cp);
 		goto error;
 	}
+
 	if (!bencode_dictionary_get_strcmp(resp, "result", "error")) {
 		if (!bencode_dictionary_get_str(resp, "error-reason", &error)) {
 		    LM_ERR("proxy return error but didn't give an error reason: %.*s\n", ret, cp);
 		}
 		else {
-			if ((RTPENGINE_SESS_LIMIT_MSG_LEN == error.len) && 
+			if ((RTPENGINE_SESS_LIMIT_MSG_LEN == error.len) &&
 			    (strncmp(error.s, RTPENGINE_SESS_LIMIT_MSG, RTPENGINE_SESS_LIMIT_MSG_LEN) == 0))
 			{
 				LM_WARN("proxy %.*s: %.*s", node->rn_url.len, node->rn_url.s , error.len, error.s);
@@ -1957,12 +2029,23 @@ select_node:
 			}
 			LM_ERR("proxy replied with error: %.*s\n", error.len, error.s);
 		}
-        goto error;
+		goto error;
 	}
 
 	if (body_out)
 		*body_out = body;
 
+	if (op == OP_DELETE) {
+		/* Delete the key<->value from the hashtable */
+		if (!rtpengine_hash_table_remove(callid, viabranch)) {
+			LM_ERR("rtpengine hash table failed to remove entry for callen=%d callid=%.*s viabranch=%.*s\n",
+				callid.len, callid.len, callid.s, viabranch.len, viabranch.s);
+		} else {
+			LM_DBG("rtpengine hash table remove entry for callen=%d callid=%.*s viabranch=%.*s\n",
+				callid.len, callid.len, callid.s, viabranch.len, viabranch.s);
+		}
+	}
+
 	return resp;
 
 error:
@@ -2191,81 +2274,199 @@ static struct rtpp_set * select_rtpp_set(int id_set ){
 
 	return rtpp_list;
 }
+
 /*
- * Main balancing routine. This does not try to keep the same proxy for
- * the call if some proxies were disabled or enabled; proxy death considered
- * too rare. Otherwise we should implement "mature" HA clustering, which is
- * too expensive here.
+ * run the selection algorithm and return the new selected node
  */
 static struct rtpp_node *
-select_rtpp_node(str callid, int do_test)
+select_rtpp_node_new(str callid, str viabranch, int do_test)
 {
-	unsigned sum, sumcut, weight_sum;
 	struct rtpp_node* node;
-	int was_forced;
-
-	if(!active_rtpp_set){
-		LM_ERR("script error -no valid set selected\n");
-		return NULL;
-	}
-	/* Most popular case: 1 proxy, nothing to calculate */
-	if (active_rtpp_set->rtpp_node_count == 1) {
-		node = active_rtpp_set->rn_first;
-		if (node->rn_disabled && node->rn_recheck_ticks <= get_ticks())
-			node->rn_disabled = rtpp_test(node, 1, 0);
-		return node->rn_disabled ? NULL : node;
-	}
+	unsigned i, sum, sumcut, weight_sum;
+	int was_forced = 0;
 
 	/* XXX Use quick-and-dirty hashing algo */
-	for(sum = 0; callid.len > 0; callid.len--)
-		sum += callid.s[callid.len - 1];
+	sum = 0;
+	for(i = 0; i < callid.len; i++)
+		sum += callid.s[i];
 	sum &= 0xff;
 
-	was_forced = 0;
 retry:
 	weight_sum = 0;
-	for (node=active_rtpp_set->rn_first; node!=NULL; node=node->rn_next) {
 
+	for (node=active_rtpp_set->rn_first; node!=NULL; node=node->rn_next) {
+		/* Try to enable if it's time to try. */
 		if (node->rn_disabled && node->rn_recheck_ticks <= get_ticks()){
-			/* Try to enable if it's time to try. */
 			node->rn_disabled = rtpp_test(node, 1, 0);
 		}
-		if (!node->rn_disabled)
+
+		/* Select only between enabled machines */
+		if (!node->rn_disabled) {
 			weight_sum += node->rn_weight;
+		}
 	}
+
+	/* No proxies? Force all to be redetected, if not yet */
 	if (weight_sum == 0) {
-		/* No proxies? Force all to be redetected, if not yet */
-		if (was_forced)
+		if (was_forced) {
 			return NULL;
+		}
+
 		was_forced = 1;
+
 		for(node=active_rtpp_set->rn_first; node!=NULL; node=node->rn_next) {
 			node->rn_disabled = rtpp_test(node, 1, 1);
 		}
+
 		goto retry;
 	}
+
+	/* sumcut here lays from 0 to weight_sum-1 */
 	sumcut = sum % weight_sum;
+
 	/*
-	 * sumcut here lays from 0 to weight_sum-1.
 	 * Scan proxy list and decrease until appropriate proxy is found.
 	 */
 	for (node=active_rtpp_set->rn_first; node!=NULL; node=node->rn_next) {
+		/* Select only between enabled machines */
 		if (node->rn_disabled)
 			continue;
+
+		/* Found enabled machine */
 		if (sumcut < node->rn_weight)
 			goto found;
+
+		/* Update sumcut if enabled machine */
 		sumcut -= node->rn_weight;
 	}
+
 	/* No node list */
 	return NULL;
+
 found:
 	if (do_test) {
 		node->rn_disabled = rtpp_test(node, node->rn_disabled, 0);
 		if (node->rn_disabled)
 			goto retry;
 	}
+
+	/* build the entry */
+	struct rtpengine_hash_entry *entry = shm_malloc(sizeof(struct rtpengine_hash_entry));
+	if (!entry) {
+		LM_ERR("rtpengine hash table fail to create entry for calllen=%d callid=%.*s viabranch=%.*s\n",
+			callid.len, callid.len, callid.s, viabranch.len, viabranch.s);
+		return node;
+	}
+        memset(entry, 0, sizeof(struct rtpengine_hash_entry));
+
+	/* fill the entry */
+        if (callid.s && callid.len > 0) {
+		if (shm_str_dup(&entry->callid, &callid) < 0) {
+			LM_ERR("rtpengine hash table fail to duplicate calllen=%d callid=%.*s\n",
+				callid.len, callid.len, callid.s);
+			rtpengine_hash_table_free_entry(entry);
+			return node;
+		}
+	}
+        if (viabranch.s && viabranch.len > 0) {
+		if (shm_str_dup(&entry->viabranch, &viabranch) < 0) {
+			LM_ERR("rtpengine hash table fail to duplicate calllen=%d viabranch=%.*s\n",
+				callid.len, viabranch.len, viabranch.s);
+			rtpengine_hash_table_free_entry(entry);
+			return node;
+		}
+	}
+	entry->node = node;
+	entry->next = NULL;
+	entry->tout = get_ticks() + hash_table_tout;
+
+	/* insert the key<->entry from the hashtable */
+	if (!rtpengine_hash_table_insert(callid, viabranch, entry)) {
+		LM_ERR("rtpengine hash table fail to insert node=%.*s for calllen=%d callid=%.*s viabranch=%.*s\n",
+			node->rn_url.len, node->rn_url.s, callid.len, callid.len, callid.s, viabranch.len, viabranch.s);
+		rtpengine_hash_table_free_entry(entry);
+		return node;
+	} else {
+		LM_DBG("rtpengine hash table insert node=%.*s for calllen=%d callid=%.*s viabranch=%.*s\n",
+			node->rn_url.len, node->rn_url.s, callid.len, callid.len, callid.s, viabranch.len, viabranch.s);
+	}
+
+	/* return selected node */
 	return node;
 }
 
+/*
+ * lookup the hastable (key=callid value=node) and get the old node (e.g. for answer/delete)
+ */
+static struct rtpp_node *
+select_rtpp_node_old(str callid, str viabranch, int do_test)
+{
+	struct rtpp_node *node = NULL;
+
+	node = rtpengine_hash_table_lookup(callid, viabranch);
+
+	if (!node) {
+		LM_NOTICE("rtpengine hash table lookup failed to find node for calllen=%d callid=%.*s viabranch=%.*s\n",
+			callid.len, callid.len, callid.s, viabranch.len, viabranch.s);
+		return NULL;
+	} else {
+		LM_DBG("rtpengine hash table lookup find node=%.*s for calllen=%d callid=%.*s viabranch=%.*s\n",
+			node->rn_url.len, node->rn_url.s, callid.len, callid.len, callid.s, viabranch.len, viabranch.s);
+	}
+
+	return node;
+}
+
+/*
+ * Main balancing routine. This DO try to keep the same proxy for
+ * the call if some proxies were disabled or enabled (e.g. kamctl command)
+ */
+static struct rtpp_node *
+select_rtpp_node(str callid, str viabranch, int do_test)
+{
+	struct rtpp_node *node = NULL;
+
+	if(!active_rtpp_set) {
+		LM_ERR("script error - no valid set selected\n");
+		return NULL;
+	}
+
+	// lookup node
+	node = select_rtpp_node_old(callid, viabranch, do_test);
+
+	// check node
+	if (!node) {
+		// run the selection algorithm
+		node = select_rtpp_node_new(callid, viabranch, do_test);
+
+		// check node
+		if (!node) {
+			LM_ERR("rtpengine failed to select new for calllen=%d callid=%.*s\n",
+				callid.len, callid.len, callid.s);
+			return NULL;
+		}
+	}
+
+	// if node enabled, return it
+	if (!node->rn_disabled) {
+		return node;
+	}
+
+	// if proper configuration and node manually or timeout disabled, return it
+	if (rtpengine_allow_op) {
+		if (node->rn_recheck_ticks == MI_MAX_RECHECK_TICKS) {
+			LM_DBG("node=%.*s for calllen=%d callid=%.*s is disabled(permanent) (probably still UP)! Return it\n",
+				node->rn_url.len, node->rn_url.s, callid.len, callid.len, callid.s);
+		} else {
+			LM_DBG("node=%.*s for calllen=%d callid=%.*s is disabled, either broke or timeout disabled! Return it\n",
+				node->rn_url.len, node->rn_url.s, callid.len, callid.len, callid.s);
+		}
+		return node;
+	}
+
+	return NULL;
+}
+
 static int
 get_extra_id(struct sip_msg* msg, str *id_str) {
 	if(msg==NULL || extra_id_pv==NULL || id_str==NULL) {
@@ -2682,7 +2883,8 @@ pv_get_rtpstat_f(struct sip_msg *msg, pv_param_t *param,
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_rtpstat_wrap, parms, 1);
 }
 
-int set_rtp_inst_pvar(struct sip_msg *msg, const str * const uri) {
+static int
+set_rtp_inst_pvar(struct sip_msg *msg, const str * const uri) {
 	pv_value_t val;
 
 	if (rtp_inst_pvar == NULL)

+ 0 - 1
modules/rtpengine/rtpengine.h

@@ -61,7 +61,6 @@ struct rtpp_set_head{
 struct rtpp_set *get_rtpp_set(int set_id);
 int add_rtpengine_socks(struct rtpp_set * rtpp_list, char * rtpproxy);
 
-int set_rtp_inst_pvar(struct sip_msg *msg, const str * const uri);
 
 int init_rtpproxy_db(void);
 

+ 544 - 0
modules/rtpengine/rtpengine_hash.c

@@ -0,0 +1,544 @@
+#include "rtpengine_hash.h"
+
+#include "../../str.h"
+#include "../../dprint.h"
+#include "../../mem/shm_mem.h"
+#include "../../locking.h"
+#include "../../timer.h"
+
+static void rtpengine_hash_table_free_row_lock(gen_lock_t *row_lock);
+
+
+static struct rtpengine_hash_table *rtpengine_hash_table;
+
+/* from sipwise rtpengine */
+static int str_cmp_str(const str a, const str b) {
+	if (a.len < b.len)
+		return -1;
+	if (a.len > b.len)
+		return 1;
+	if (a.len == 0 && b.len == 0)
+		return 0;
+	return memcmp(a.s, b.s, a.len);
+}
+
+/* from sipwise rtpengine */
+static int str_equal(str a, str b) {
+	return (str_cmp_str(a, b) == 0);
+}
+
+/* from sipwise rtpengine */
+static unsigned int str_hash(str s) {
+	unsigned int ret = 5381;
+	str it = s;
+
+	while (it.len > 0) {
+		ret = (ret << 5) + ret + *it.s;
+		it.s++;
+		it.len--;
+	}
+
+	return ret % rtpengine_hash_table->size;
+}
+
+/* rtpengine hash API */
+int rtpengine_hash_table_init(int size) {
+	int i;
+	int hash_table_size;
+
+	// init hash table size
+	if (size < 1) {
+		hash_table_size = 1;
+	} else {
+		hash_table_size = size;
+	}
+	LM_DBG("rtpengine_hash_table size = %d\n", hash_table_size);
+
+	// init hashtable
+	rtpengine_hash_table = shm_malloc(sizeof(struct rtpengine_hash_table));
+	if (!rtpengine_hash_table) {
+		LM_ERR("no shm left to create rtpengine_hash_table\n");
+		return 0;
+	}
+	memset(rtpengine_hash_table, 0, sizeof(struct rtpengine_hash_table));
+	rtpengine_hash_table->size = hash_table_size;
+
+	// init hashtable row_locks
+	rtpengine_hash_table->row_locks = shm_malloc(hash_table_size * sizeof(gen_lock_t*));
+	if (!rtpengine_hash_table->row_locks) {
+		LM_ERR("no shm left to create rtpengine_hash_table->row_locks\n");
+		rtpengine_hash_table_destroy();
+		return 0;
+	}
+	memset(rtpengine_hash_table->row_locks, 0, hash_table_size * sizeof(gen_lock_t*));
+
+	// init hashtable row_entry_list
+	rtpengine_hash_table->row_entry_list = shm_malloc(rtpengine_hash_table->size * sizeof(struct rtpengine_hash_entry*));
+	if (!rtpengine_hash_table->row_entry_list) {
+		LM_ERR("no shm left to create rtpengine_hash_table->row_entry_list\n");
+		rtpengine_hash_table_destroy();
+		return 0;
+	}
+	memset(rtpengine_hash_table->row_entry_list, 0, rtpengine_hash_table->size * sizeof(struct rtpengine_hash_entry*));
+
+	// init hashtable row_totals
+	rtpengine_hash_table->row_totals = shm_malloc(hash_table_size * sizeof(unsigned int));
+	if (!rtpengine_hash_table->row_totals) {
+		LM_ERR("no shm left to create rtpengine_hash_table->row_totals\n");
+		rtpengine_hash_table_destroy();
+		return 0;
+	}
+	memset(rtpengine_hash_table->row_totals, 0, hash_table_size * sizeof(unsigned int));
+
+	// init hashtable  row_locks[i], row_entry_list[i] and row_totals[i]
+	for (i = 0; i < hash_table_size; i++) {
+		// init hashtable row_locks[i]
+		rtpengine_hash_table->row_locks[i] = lock_alloc();
+		if (!rtpengine_hash_table->row_locks[i]) {
+			LM_ERR("no shm left to create rtpengine_hash_table->row_locks[%d]\n", i);
+			rtpengine_hash_table_destroy();
+			return 0;
+		}
+
+		// init hashtable row_entry_list[i]
+		rtpengine_hash_table->row_entry_list[i] = shm_malloc(sizeof(struct rtpengine_hash_entry));
+		if (!rtpengine_hash_table->row_entry_list[i]) {
+			LM_ERR("no shm left to create rtpengine_hash_table->row_entry_list[%d]\n", i);
+			rtpengine_hash_table_destroy();
+			return 0;
+		}
+		memset(rtpengine_hash_table->row_entry_list[i], 0, sizeof(struct rtpengine_hash_entry));
+
+		rtpengine_hash_table->row_entry_list[i]->tout = -1;
+		rtpengine_hash_table->row_entry_list[i]->next = NULL;
+
+		// init hashtable row_totals[i]
+		rtpengine_hash_table->row_totals[i] = 0;
+	}
+
+	return 1;
+}
+
+int rtpengine_hash_table_destroy() {
+	int i;
+
+	// check rtpengine hashtable
+	if (!rtpengine_hash_table) {
+		LM_ERR("NULL rtpengine_hash_table\n");
+		return 1;
+	}
+
+	// check rtpengine hashtable->row_locks
+	if (!rtpengine_hash_table->row_locks) {
+		LM_ERR("NULL rtpengine_hash_table->row_locks\n");
+		shm_free(rtpengine_hash_table);
+		rtpengine_hash_table = NULL;
+		return 1;
+	}
+
+	// destroy hashtable content
+	for (i = 0; i < rtpengine_hash_table->size; i++) {
+		// lock
+		if (!rtpengine_hash_table->row_locks[i]) {
+			LM_ERR("NULL rtpengine_hash_table->row_locks[%d]\n", i);
+			continue;
+		} else {
+			lock_get(rtpengine_hash_table->row_locks[i]);
+		}
+
+		// check rtpengine hashtable->row_entry_list
+		if (!rtpengine_hash_table->row_entry_list) {
+			LM_ERR("NULL rtpengine_hash_table->row_entry_list\n");
+		} else {
+			// destroy hashtable row_entry_list[i]
+			rtpengine_hash_table_free_row_entry_list(rtpengine_hash_table->row_entry_list[i]);
+			rtpengine_hash_table->row_entry_list[i] = NULL;
+		}
+
+		// unlock
+		lock_release(rtpengine_hash_table->row_locks[i]);
+
+		// destroy hashtable row_locks[i]
+		rtpengine_hash_table_free_row_lock(rtpengine_hash_table->row_locks[i]);
+		rtpengine_hash_table->row_locks[i] = NULL;
+	}
+
+	// destroy hashtable row_entry_list
+	if (!rtpengine_hash_table->row_entry_list) {
+		LM_ERR("NULL rtpengine_hash_table->row_entry_list\n");
+	} else {
+		shm_free(rtpengine_hash_table->row_entry_list);
+		rtpengine_hash_table->row_entry_list = NULL;
+	}
+
+	// destroy hashtable row_totals
+	if (!rtpengine_hash_table->row_totals) {
+		LM_ERR("NULL rtpengine_hash_table->row_totals\n");
+	} else {
+		shm_free(rtpengine_hash_table->row_totals);
+		rtpengine_hash_table->row_totals = NULL;
+	}
+
+	// destroy hashtable row_locks
+	if (!rtpengine_hash_table->row_locks) {
+		// should not be the case; just for code symmetry
+		LM_ERR("NULL rtpengine_hash_table->row_locks\n");
+	} else {
+		shm_free(rtpengine_hash_table->row_locks);
+		rtpengine_hash_table->row_locks = NULL;
+	}
+
+	// destroy hashtable
+	if (!rtpengine_hash_table) {
+		// should not be the case; just for code symmetry
+		LM_ERR("NULL rtpengine_hash_table\n");
+	} else {
+		shm_free(rtpengine_hash_table);
+		rtpengine_hash_table = NULL;
+	}
+
+	return 1;
+}
+
+int rtpengine_hash_table_insert(str callid, str viabranch, struct rtpengine_hash_entry *value) {
+	struct rtpengine_hash_entry *entry, *last_entry;
+	struct rtpengine_hash_entry *new_entry = (struct rtpengine_hash_entry *) value;
+	unsigned int hash_index;
+
+	// sanity checks
+	if (!rtpengine_hash_table_sanity_checks()) {
+		LM_ERR("sanity checks failed\n");
+		return 0;
+	}
+
+	// get entry list
+	hash_index = str_hash(callid);
+	entry = rtpengine_hash_table->row_entry_list[hash_index];
+	last_entry = entry;
+
+	// lock
+	if (rtpengine_hash_table->row_locks[hash_index]) {
+		lock_get(rtpengine_hash_table->row_locks[hash_index]);
+	} else {
+		LM_ERR("NULL rtpengine_hash_table->row_locks[%d]\n", hash_index);
+		return 0;
+	}
+
+	while (entry) {
+		// if found, don't add new entry
+		if (str_equal(entry->callid, new_entry->callid) &&
+		    str_equal(entry->viabranch, new_entry->viabranch)) {
+			// unlock
+			lock_release(rtpengine_hash_table->row_locks[hash_index]);
+			LM_NOTICE("callid=%.*s, viabranch=%.*s already in hashtable, ignore new value\n",
+				entry->callid.len, entry->callid.s,
+				entry->viabranch.len, entry->viabranch.s);
+			return 0;
+		}
+
+		// if expired entry discovered, delete it
+		if (entry->tout < get_ticks()) {
+			// set pointers; exclude entry
+			last_entry->next = entry->next;
+
+			// free current entry; entry points to unknown
+			rtpengine_hash_table_free_entry(entry);
+
+			// set pointers
+			entry = last_entry;
+
+			// update total
+			rtpengine_hash_table->row_totals[hash_index]--;
+		}
+
+		// next entry in the list
+		last_entry = entry;
+		entry = entry->next;
+	}
+
+	last_entry->next = new_entry;
+
+	// update total
+	rtpengine_hash_table->row_totals[hash_index]++;
+
+	// unlock
+	lock_release(rtpengine_hash_table->row_locks[hash_index]);
+
+	return 1;
+}
+
+int rtpengine_hash_table_remove(str callid, str viabranch) {
+	struct rtpengine_hash_entry *entry, *last_entry;
+	unsigned int hash_index;
+
+	// sanity checks
+	if (!rtpengine_hash_table_sanity_checks()) {
+		LM_ERR("sanity checks failed\n");
+		return 0;
+	}
+
+	// get first entry from entry list; jump over unused list head
+	hash_index = str_hash(callid);
+	entry = rtpengine_hash_table->row_entry_list[hash_index];
+	last_entry = entry;
+
+	// lock
+	if (rtpengine_hash_table->row_locks[hash_index]) {
+		lock_get(rtpengine_hash_table->row_locks[hash_index]);
+	} else {
+		LM_ERR("NULL rtpengine_hash_table->row_locks[%d]\n", hash_index);
+		return 0;
+	}
+
+	while (entry) {
+		// if callid found, delete entry
+		if (str_equal(entry->callid, callid) &&
+		    str_equal(entry->viabranch, viabranch)) {
+			// free entry
+			last_entry->next = entry->next;
+			rtpengine_hash_table_free_entry(entry);
+
+			// update total
+			rtpengine_hash_table->row_totals[hash_index]--;
+
+			// unlock
+			lock_release(rtpengine_hash_table->row_locks[hash_index]);
+
+			return 1;
+		}
+
+		// if expired entry discovered, delete it
+		if (entry->tout < get_ticks()) {
+			// set pointers; exclude entry
+			last_entry->next = entry->next;
+
+			// free current entry; entry points to unknown
+			rtpengine_hash_table_free_entry(entry);
+
+			// set pointers
+			entry = last_entry;
+
+			// update total
+			rtpengine_hash_table->row_totals[hash_index]--;
+		}
+
+		last_entry = entry;
+		entry = entry->next;
+	}
+
+	// unlock
+	lock_release(rtpengine_hash_table->row_locks[hash_index]);
+
+	return 0;
+}
+
+struct rtpp_node *rtpengine_hash_table_lookup(str callid, str viabranch) {
+	struct rtpengine_hash_entry *entry, *last_entry;
+	unsigned int hash_index;
+	struct rtpp_node *node;
+
+	// sanity checks
+	if (!rtpengine_hash_table_sanity_checks()) {
+		LM_ERR("sanity checks failed\n");
+		return 0;
+	}
+
+	// get first entry from entry list; jump over unused list head
+	hash_index = str_hash(callid);
+	entry = rtpengine_hash_table->row_entry_list[hash_index];
+	last_entry = entry;
+
+	// lock
+	if (rtpengine_hash_table->row_locks[hash_index]) {
+		lock_get(rtpengine_hash_table->row_locks[hash_index]);
+	} else {
+		LM_ERR("NULL rtpengine_hash_table->row_locks[%d]\n", hash_index);
+		return 0;
+	}
+
+	while (entry) {
+		// if callid found, return entry
+		if (str_equal(entry->callid, callid) &&
+		    str_equal(entry->viabranch, viabranch)) {
+			node = entry->node;
+
+			// unlock
+			lock_release(rtpengine_hash_table->row_locks[hash_index]);
+
+			return node;
+		}
+
+		// if expired entry discovered, delete it
+		if (entry->tout < get_ticks()) {
+			// set pointers; exclude entry
+			last_entry->next = entry->next;
+
+			// free current entry; entry points to unknown
+			rtpengine_hash_table_free_entry(entry);
+
+			// set pointers
+			entry = last_entry;
+
+			// update total
+			rtpengine_hash_table->row_totals[hash_index]--;
+		}
+
+		last_entry = entry;
+		entry = entry->next;
+	}
+
+	// unlock
+	lock_release(rtpengine_hash_table->row_locks[hash_index]);
+
+	return NULL;
+}
+
+// print hash table entries while deleting expired entries
+void rtpengine_hash_table_print() {
+	int i;
+	struct rtpengine_hash_entry *entry, *last_entry;
+
+	// sanity checks
+	if (!rtpengine_hash_table_sanity_checks()) {
+		LM_ERR("sanity checks failed\n");
+		return ;
+	}
+
+	// print hashtable
+	for (i = 0; i < rtpengine_hash_table->size; i++) {
+		// lock
+		if (rtpengine_hash_table->row_locks[i]) {
+			lock_get(rtpengine_hash_table->row_locks[i]);
+		} else {
+			LM_ERR("NULL rtpengine_hash_table->row_locks[%d]\n", i);
+			return ;
+		}
+
+		entry = rtpengine_hash_table->row_entry_list[i];
+		last_entry = entry;
+
+		while (entry) {
+			// if expired entry discovered, delete it
+			if (entry->tout < get_ticks()) {
+				// set pointers; exclude entry
+				last_entry->next = entry->next;
+
+				// free current entry; entry points to unknown
+				rtpengine_hash_table_free_entry(entry);
+
+				// set pointers
+				entry = last_entry;
+
+				// update total
+				rtpengine_hash_table->row_totals[i]--;
+			} else {
+				LM_DBG("hash_index=%d callid=%.*s tout=%u\n",
+					i, entry->callid.len, entry->callid.s, entry->tout - get_ticks());
+			}
+
+			last_entry = entry;
+			entry = entry->next;
+		}
+
+		// unlock
+		lock_release(rtpengine_hash_table->row_locks[i]);
+	}
+
+}
+
+unsigned int rtpengine_hash_table_total() {
+	int i;
+	unsigned int total = 0;
+
+	// sanity checks
+	if (!rtpengine_hash_table_sanity_checks()) {
+		LM_ERR("sanity checks failed\n");
+		return 0;
+	}
+
+	for (i = 0; i < rtpengine_hash_table->size; i++) {
+		total += rtpengine_hash_table->row_totals[i];
+	}
+
+	return total;
+}
+
+void rtpengine_hash_table_free_entry(struct rtpengine_hash_entry *entry) {
+	if (!entry) {
+		LM_ERR("try to free a NULL entry\n");
+		return ;
+	}
+
+	// free callid
+	if (entry->callid.s) {
+		shm_free(entry->callid.s);
+	}
+
+	// free viabranch
+	if (entry->viabranch.s) {
+		shm_free(entry->viabranch.s);
+	}
+
+	// free entry
+	shm_free(entry);
+
+	return ;
+}
+
+void rtpengine_hash_table_free_row_entry_list(struct rtpengine_hash_entry *row_entry_list) {
+	struct rtpengine_hash_entry *entry, *last_entry;
+
+	if (!row_entry_list) {
+		LM_ERR("try to free a NULL row_entry_list\n");
+		return ;
+	}
+
+	entry = row_entry_list;
+	while (entry) {
+		last_entry = entry;
+		entry = entry->next;
+		rtpengine_hash_table_free_entry(last_entry);
+		last_entry = NULL;
+	}
+
+	return ;
+}
+
+static void rtpengine_hash_table_free_row_lock(gen_lock_t *row_lock) {
+	if (!row_lock) {
+		LM_ERR("try to free a NULL lock\n");
+		return ;
+	}
+
+	lock_destroy(row_lock);
+
+	return ;
+}
+
+int rtpengine_hash_table_sanity_checks() {
+	// check rtpengine hashtable
+	if (!rtpengine_hash_table) {
+		LM_ERR("NULL rtpengine_hash_table\n");
+		return 0;
+	}
+
+	// check rtpengine hashtable->row_locks
+	if (!rtpengine_hash_table->row_locks) {
+		LM_ERR("NULL rtpengine_hash_table->row_locks\n");
+		return 0;
+	}
+
+	// check rtpengine hashtable->row_entry_list
+	if (!rtpengine_hash_table->row_entry_list) {
+		LM_ERR("NULL rtpengine_hash_table->row_entry_list\n");
+		return 0;
+	}
+
+	// check rtpengine hashtable->row_totals
+	if (!rtpengine_hash_table->row_totals) {
+		LM_ERR("NULL rtpengine_hash_table->row_totals\n");
+		return 0;
+	}
+
+	return 1;
+}

+ 40 - 0
modules/rtpengine/rtpengine_hash.h

@@ -0,0 +1,40 @@
+#ifndef _RTPENGINE_HASH_H
+#define _RTPENGINE_HASH_H
+
+#include "../../str.h"
+#include "../../locking.h"
+
+
+/* table entry */
+struct rtpengine_hash_entry {
+	str callid;				// call callid
+	str viabranch;				// call viabranch
+	struct rtpp_node *node;			// call selected node
+
+	unsigned int tout;			// call timeout
+	struct rtpengine_hash_entry *next;	// call next
+};
+
+/* table */
+struct rtpengine_hash_table {
+	struct rtpengine_hash_entry **row_entry_list;	// vector of size pointers to entry
+	gen_lock_t **row_locks;				// vector of size pointers to locks
+	unsigned int *row_totals;			// vector of size numbers of entries in the hashtable rows
+	unsigned int size;				// hash table size
+};
+
+
+int rtpengine_hash_table_init(int size);
+int rtpengine_hash_table_destroy();
+int rtpengine_hash_table_insert(str callid, str viabranch, struct rtpengine_hash_entry *value);
+int rtpengine_hash_table_remove(str callid, str viabranch);
+struct rtpp_node *rtpengine_hash_table_lookup(str callid, str viabranch);
+void rtpengine_hash_table_print();
+unsigned int rtpengine_hash_table_total();
+
+void rtpengine_hash_table_free_entry(struct rtpengine_hash_entry *entry);
+void rtpengine_hash_table_free_row_entry_list(struct rtpengine_hash_entry *row_entry_list);
+
+int rtpengine_hash_table_sanity_checks();
+
+#endif

+ 27 - 7
modules/siptrace/README

@@ -16,9 +16,9 @@ Daniel-Constantin Mierla
 
    <[email protected]>
 
-   Copyright (c) 2010 asipto.com
+   Copyright © 2010 asipto.com
 
-   Copyright (c) 2006 voice-system.ro
+   Copyright © 2006 voice-system.ro
      __________________________________________________________________
 
    Table of Contents
@@ -50,6 +50,7 @@ Daniel-Constantin Mierla
               3.15. hep_capture_id (integer)
               3.16. trace_delayed (integer)
               3.17. force_send_sock (str)
+              3.18. trace_mode (integer)
 
         4. Functions
 
@@ -85,7 +86,8 @@ Daniel-Constantin Mierla
    1.15. Set hep_capture_id parameter
    1.16. Set trace_delayed parameter
    1.17. Set force_send_sock parameter
-   1.18. sip_trace() usage
+   1.18. Set trace_mode parameter
+   1.19. sip_trace() usage
 
 Chapter 1. Admin Guide
 
@@ -116,6 +118,7 @@ Chapter 1. Admin Guide
         3.15. hep_capture_id (integer)
         3.16. trace_delayed (integer)
         3.17. force_send_sock (str)
+        3.18. trace_mode (integer)
 
    4. Functions
 
@@ -190,6 +193,7 @@ Chapter 1. Admin Guide
    3.15. hep_capture_id (integer)
    3.16. trace_delayed (integer)
    3.17. force_send_sock (str)
+   3.18. trace_mode (integer)
 
 3.1. db_url (str)
 
@@ -420,11 +424,27 @@ modparam("siptrace", "trace_delayed", 1)
 modparam("siptrace", "force_send_sock", "sip:10.1.1.2:5000")
 ...
 
+3.18. trace_mode (integer)
+
+   If set to 1, the module uses core events triggered when receiving or
+   sending SIP traffic to mirror traffic to a SIP capture server using
+   HEP. It will automatically do the mirroring of all traffic, no need to
+   set the siptrace flag per request.
+
+   If set to 0, no automatic mirroring of SIP traffic via HEP.
+
+   Default value is 0.
+
+   Example 1.18. Set trace_mode parameter
+...
+modparam("siptrace", "trace_mode", 1)
+...
+
 4. Functions
 
    4.1. sip_trace([address])
 
-4.1.  sip_trace([address])
+4.1. sip_trace([address])
 
    Store or forward the current processed SIP message in database. It is
    stored in the form prior applying changes made to it.
@@ -438,7 +458,7 @@ modparam("siptrace", "force_send_sock", "sip:10.1.1.2:5000")
    ONREPLY_ROUTE, BRANCH_ROUTE.
    Default value is "NULL".
 
-   Example 1.18. sip_trace() usage
+   Example 1.19. sip_trace() usage
 ...
 sip_trace();
 ...
@@ -449,7 +469,7 @@ sip_trace("sip:10.1.1.2:5085");
 
    5.1. sip_trace
 
-5.1.  sip_trace
+5.1. sip_trace
 
    Name: sip_trace
 
@@ -470,7 +490,7 @@ sip_trace("sip:10.1.1.2:5085");
 
    6.1. siptrace.status param
 
-6.1.  siptrace.status param
+6.1. siptrace.status param
 
    Name: siptrace.status
 

+ 24 - 0
modules/siptrace/doc/siptrace_admin.xml

@@ -466,6 +466,30 @@ modparam("siptrace", "trace_delayed", 1)
 ...
 modparam("siptrace", "force_send_sock", "sip:10.1.1.2:5000")
 ...
+</programlisting>
+                </example>
+        </section>
+	<section id="siptrace.p.trace_mode">
+                <title><varname>trace_mode</varname> (integer)</title>
+                <para>
+				If set to 1, the module uses core events triggered when receiving
+				or sending SIP traffic to mirror traffic to a SIP capture server
+				using HEP. It will automatically do the mirroring of all traffic,
+				no need to set the siptrace flag per request.
+                </para>
+                <para>
+                If set to 0, no automatic mirroring of SIP traffic via HEP.
+                </para>
+                <para>
+                Default value is <emphasis>0</emphasis>.
+                </para>
+                <example>
+                <title>Set <varname>trace_mode</varname>
+                parameter</title>
+                <programlisting format="linespecific">
+...
+modparam("siptrace", "trace_mode", 1)
+...
 </programlisting>
                 </example>
         </section>

+ 225 - 128
modules/siptrace/siptrace.c

@@ -50,6 +50,7 @@
 #include "../../modules/sl/sl.h"
 #include "../../str.h"
 #include "../../onsend.h"
+#include "../../events.h"
 
 #include "../../modules/sipcapture/hep.h"
 
@@ -106,6 +107,10 @@ static void trace_sl_ack_in(sl_cbp_t *slcb);
 static int trace_send_hep_duplicate(str *body, str *from, str *to, struct dest_info*);
 static int pipport2su (char *pipport, union sockaddr_union *tmp_su, unsigned int *proto);
 
+int siptrace_net_data_recv(void *data);
+int siptrace_net_data_send(void *data);
+static int _siptrace_mode = 0;
+
 
 static struct mi_root* sip_trace_mi(struct mi_root* cmd, void* param );
 
@@ -174,7 +179,7 @@ db_func_t db_funcs;      		/*!< Database functions */
  */
 static cmd_export_t cmds[] = {
 	{"sip_trace", (cmd_function)sip_trace, 0, 0, 0, ANY_ROUTE},
-    {"sip_trace", (cmd_function)sip_trace, 1, fixup_siptrace, 0, ANY_ROUTE},
+	{"sip_trace", (cmd_function)sip_trace, 1, fixup_siptrace, 0, ANY_ROUTE},
 	{0, 0, 0, 0, 0, 0}
 };
 
@@ -207,10 +212,11 @@ static param_export_t params[] = {
 	{"xheaders_write",     INT_PARAM, &xheaders_write       },
 	{"xheaders_read",      INT_PARAM, &xheaders_read        },
 	{"hep_mode_on",        INT_PARAM, &hep_mode_on          },	 
-    {"force_send_sock",    PARAM_STR, &force_send_sock_str	},
+	{"force_send_sock",    PARAM_STR, &force_send_sock_str	},
 	{"hep_version",        INT_PARAM, &hep_version          },
 	{"hep_capture_id",     INT_PARAM, &hep_capture_id       },	        
 	{"trace_delayed",      INT_PARAM, &trace_delayed        },
+	{"trace_mode",         PARAM_INT, &_siptrace_mode       },
 	{0, 0, 0}
 };
 
@@ -297,8 +303,8 @@ static int mod_init(void)
 	*trace_to_database_flag = trace_to_database;
 
 	if(hep_version != 1 && hep_version != 2) {
-	    LM_ERR("unsupported version of HEP");
-	    return -1;
+		LM_ERR("unsupported version of HEP");
+		return -1;
 	}
 
 	/* Find a database module if needed */
@@ -316,12 +322,11 @@ static int mod_init(void)
 		}
 	}
 
-        if(hep_version != 1 && hep_version != 2) {
-  
-                  LM_ERR("unsupported version of HEP");
-                  return -1;
-        }                                          
+	if(hep_version != 1 && hep_version != 2) {
 
+		LM_ERR("unsupported version of HEP");
+		return -1;
+	}
 
 	trace_on_flag = (int*)shm_malloc(sizeof(int));
 	if(trace_on_flag==NULL) {
@@ -391,19 +396,19 @@ static int mod_init(void)
 
 	if(force_send_sock_str.s!=0)
 	{
-	    force_send_sock_str.len = strlen(force_send_sock_str.s);
-	    force_send_sock_uri = (struct sip_uri *)pkg_malloc(sizeof(struct sip_uri));
-	    if(force_send_sock_uri==0)
-	    {
-	        LM_ERR("no more pkg memory left\n");
-	        return -1;
-	    }
-	    memset(force_send_sock_uri, 0, sizeof(struct sip_uri));
-	    if(parse_uri(force_send_sock_str.s, force_send_sock_str.len, force_send_sock_uri)<0)
-	    {
-	        LM_ERR("bad dup uri\n");
-	        return -1;
-	    }
+		force_send_sock_str.len = strlen(force_send_sock_str.s);
+		force_send_sock_uri = (struct sip_uri *)pkg_malloc(sizeof(struct sip_uri));
+		if(force_send_sock_uri==0)
+		{
+			LM_ERR("no more pkg memory left\n");
+			return -1;
+		}
+		memset(force_send_sock_uri, 0, sizeof(struct sip_uri));
+		if(parse_uri(force_send_sock_str.s, force_send_sock_str.len, force_send_sock_uri)<0)
+		{
+			LM_ERR("bad dup uri\n");
+			return -1;
+		}
 	}
 
 	if(traced_user_avp_str.s && traced_user_avp_str.len > 0)
@@ -449,6 +454,10 @@ static int mod_init(void)
 		trace_table_avp_type = 0;
 	}
 
+	if(_siptrace_mode==1) {
+		sr_event_register_cb(SREV_NET_DATA_RECV, siptrace_net_data_recv);
+		sr_event_register_cb(SREV_NET_DATA_SEND, siptrace_net_data_send);
+	}
 	return 0;
 }
 
@@ -466,7 +475,7 @@ static int child_init(int rank)
 			return -1;
 		}
 		if (db_check_table_version(&db_funcs, db_con, &siptrace_table,
-					   SIP_TRACE_TABLE_VERSION) < 0) {
+					SIP_TRACE_TABLE_VERSION) < 0) {
 			LM_ERR("error during table version check\n");
 			db_funcs.close(db_con);		
 			return -1;
@@ -752,7 +761,7 @@ static int sip_trace_store(struct _siptrace_data *sto, struct dest_info *dst)
 		return -1;
 
 	if(hep_mode_on) trace_send_hep_duplicate(&sto->body, &sto->fromip, &sto->toip, dst);
-    else trace_send_duplicate(sto->body.s, sto->body.len, dst);
+	else trace_send_duplicate(sto->body.s, sto->body.len, dst);
 
 	if (sip_trace_xheaders_free(sto) != 0)
 		return -1;
@@ -961,14 +970,14 @@ static int sip_trace(struct sip_msg *msg, struct dest_info * dst, char *dir)
 	struct onsend_info *snd_inf = NULL;
 
 	if (dst){
-	    if (dst->send_sock == 0){
-	        dst->send_sock=get_send_socket(0, &dst->to, dst->proto);
-	        if (dst->send_sock==0){
-	            LM_ERR("can't forward to af %d, proto %d no corresponding"
-	                    " listening socket\n", dst->to.s.sa_family, dst->proto);
-	            return -1;
-	        }
-	    }
+		if (dst->send_sock == 0){
+			dst->send_sock=get_send_socket(0, &dst->to, dst->proto);
+			if (dst->send_sock==0){
+				LM_ERR("can't forward to af %d, proto %d no corresponding"
+						" listening socket\n", dst->to.s.sa_family, dst->proto);
+				return -1;
+			}
+		}
 	}
 
 	if(msg==NULL) {
@@ -1584,23 +1593,23 @@ static int trace_send_duplicate(char *buf, int len, struct dest_info *dst2)
 	}
 
 	if (!dst2){
-	    init_dest_info(&dst);
-	    /* create a temporary proxy*/
-	    dst.proto = PROTO_UDP;
-	    p=mk_proxy(&dup_uri->host, (dup_uri->port_no)?dup_uri->port_no:SIP_PORT,
-	             dst.proto);
-	    if (p==0){
-	        LM_ERR("bad host name in uri\n");
-	        return -1;
-	    }
-	    hostent2su(&dst.to, &p->host, p->addr_idx, (p->port)?p->port:SIP_PORT);
-
-	    dst.send_sock=get_send_socket(0, &dst.to, dst.proto);
-	    if (dst.send_sock==0){
-	        LM_ERR("can't forward to af %d, proto %d no corresponding"
-	                " listening socket\n", dst.to.s.sa_family, dst.proto);
-	        goto error;
-	    }
+		init_dest_info(&dst);
+		/* create a temporary proxy*/
+		dst.proto = PROTO_UDP;
+		p=mk_proxy(&dup_uri->host, (dup_uri->port_no)?dup_uri->port_no:SIP_PORT,
+				dst.proto);
+		if (p==0){
+			LM_ERR("bad host name in uri\n");
+			return -1;
+		}
+		hostent2su(&dst.to, &p->host, p->addr_idx, (p->port)?p->port:SIP_PORT);
+
+		dst.send_sock=get_send_socket(0, &dst.to, dst.proto);
+		if (dst.send_sock==0){
+			LM_ERR("can't forward to af %d, proto %d no corresponding"
+					" listening socket\n", dst.to.s.sa_family, dst.proto);
+			goto error;
+		}
 	}
 
 	if (msg_send((dst2)?dst2:&dst, buf, len)<0)
@@ -1610,15 +1619,15 @@ static int trace_send_duplicate(char *buf, int len, struct dest_info *dst2)
 	}
 
 	if (p){
-	    free_proxy(p); /* frees only p content, not p itself */
-	    pkg_free(p);
+		free_proxy(p); /* frees only p content, not p itself */
+		pkg_free(p);
 	}
 	return 0;
 error:
-    if (p){
-	free_proxy(p); /* frees only p content, not p itself */
-	pkg_free(p);
-    }
+	if (p){
+		free_proxy(p); /* frees only p content, not p itself */
+		pkg_free(p);
+	}
 	return -1;
 }
 
@@ -1637,7 +1646,7 @@ static int trace_send_hep_duplicate(str *body, str *from, str *to, struct dest_i
 	struct hep_timehdr hep_time;
 	struct timeval tvb;
 	struct timezone tz;
-	                 
+
 	struct hep_ip6hdr hep_ip6header;
 
 	if(body->s==NULL || body->len <= 0)
@@ -1647,8 +1656,8 @@ static int trace_send_hep_duplicate(str *body, str *from, str *to, struct dest_i
 		return 0;
 
 
-        gettimeofday( &tvb, &tz );
-        
+	gettimeofday( &tvb, &tz );
+
 
 	/* message length */
 	len = body->len 
@@ -1667,50 +1676,50 @@ static int trace_send_hep_duplicate(str *body, str *from, str *to, struct dest_i
 
 	/* check if from and to are in the same family*/
 	if(from_su.s.sa_family != to_su.s.sa_family) {
-		LOG(L_ERR, "ERROR: trace_send_hep_duplicate: interworking detected ?\n");
+		LM_ERR("interworking detected ?\n");
 		goto error;
 	}
 
-    if (!dst2){
-	init_dest_info(&dst);
-	/* create a temporary proxy*/
-	dst.proto = PROTO_UDP;
-	p=mk_proxy(&dup_uri->host, (dup_uri->port_no)?dup_uri->port_no:SIP_PORT,
-			dst.proto);
-	if (p==0)
-	{
-		LM_ERR("bad host name in uri\n");
-		goto error;
+	if (!dst2){
+		init_dest_info(&dst);
+		/* create a temporary proxy*/
+		dst.proto = PROTO_UDP;
+		p=mk_proxy(&dup_uri->host, (dup_uri->port_no)?dup_uri->port_no:SIP_PORT,
+				dst.proto);
+		if (p==0)
+		{
+			LM_ERR("bad host name in uri\n");
+			goto error;
+		}
+
+		hostent2su(&dst.to, &p->host, p->addr_idx, (p->port)?p->port:SIP_PORT);
+		LM_DBG("setting up the socket_info\n");
+		dst_fin = &dst;
+	} else {
+		dst_fin = dst2;
+	}
+
+	if (force_send_sock_str.s) {
+		LM_DBG("force_send_sock activated, grep for the sock_info\n");
+		si = grep_sock_info(&force_send_sock_uri->host,
+				(force_send_sock_uri->port_no)?force_send_sock_uri->port_no:SIP_PORT,
+				PROTO_UDP);
+		if (!si) {
+			LM_WARN("cannot grep socket info\n");
+		} else {
+			LM_DBG("found socket while grep: [%.*s] [%.*s]\n", si->name.len, si->name.s, si->address_str.len, si->address_str.s);
+			dst_fin->send_sock = si;
+		}
 	}
 
-	hostent2su(&dst.to, &p->host, p->addr_idx, (p->port)?p->port:SIP_PORT);
-	LM_DBG("setting up the socket_info\n");
-	dst_fin = &dst;
-    } else {
-        dst_fin = dst2;
-    }
-
-    if (force_send_sock_str.s) {
-        LM_DBG("force_send_sock activated, grep for the sock_info\n");
-        si = grep_sock_info(&force_send_sock_uri->host,
-                (force_send_sock_uri->port_no)?force_send_sock_uri->port_no:SIP_PORT,
-                PROTO_UDP);
-        if (!si) {
-             LM_WARN("cannot grep socket info\n");
-        } else {
-            LM_DBG("found socket while grep: [%.*s] [%.*s]\n", si->name.len, si->name.s, si->address_str.len, si->address_str.s);
-            dst_fin->send_sock = si;
-        }
-    }
-
-    if (dst_fin->send_sock == 0) {
-        dst_fin->send_sock=get_send_socket(0, &dst_fin->to, dst_fin->proto);
-        if (dst_fin->send_sock == 0) {
-            LM_ERR("can't forward to af %d, proto %d no corresponding"
-                    " listening socket\n", dst_fin->to.s.sa_family, dst_fin->proto);
-            goto error;
-        }
-    }
+	if (dst_fin->send_sock == 0) {
+		dst_fin->send_sock=get_send_socket(0, &dst_fin->to, dst_fin->proto);
+		if (dst_fin->send_sock == 0) {
+			LM_ERR("can't forward to af %d, proto %d no corresponding"
+					" listening socket\n", dst_fin->to.s.sa_family, dst_fin->proto);
+			goto error;
+		}
+	}
 
 	/* Version && proto && length */
 	hdr.hp_l = sizeof(struct hep_hdr);
@@ -1744,7 +1753,7 @@ static int trace_send_hep_duplicate(str *body, str *from, str *to, struct dest_i
 		len = sizeof(struct hep_ip6hdr);
 	}
 	else {
-		LOG(L_ERR, "ERROR: trace_send_hep_duplicate: Unsupported protocol family\n");
+		LM_ERR("Unsupported protocol family\n");
 		goto error;;
 	}
 
@@ -1755,7 +1764,7 @@ static int trace_send_hep_duplicate(str *body, str *from, str *to, struct dest_i
 	len += sizeof(struct hep_hdr) + body->len;
 	buffer = (void *)pkg_malloc(len+1);
 	if (buffer==0){
-		LOG(L_ERR, "ERROR: trace_send_hep_duplicate: out of memory\n");
+		LM_ERR("out of memory\n");
 		goto error;
 	}
 
@@ -1778,28 +1787,28 @@ static int trace_send_hep_duplicate(str *body, str *from, str *to, struct dest_i
 
 	if(hep_version == 2) {
 
-                hep_time.tv_sec = tvb.tv_sec;
-                hep_time.tv_usec = tvb.tv_usec;
-                hep_time.captid = hep_capture_id;
+		hep_time.tv_sec = tvb.tv_sec;
+		hep_time.tv_usec = tvb.tv_usec;
+		hep_time.captid = hep_capture_id;
 
-                memcpy((void*)buffer+buflen, &hep_time, sizeof(struct hep_timehdr));
-                buflen += sizeof(struct hep_timehdr);
-        }
+		memcpy((void*)buffer+buflen, &hep_time, sizeof(struct hep_timehdr));
+		buflen += sizeof(struct hep_timehdr);
+	}
 
 	/* PAYLOAD */
 	memcpy((void*)(buffer + buflen) , (void*)body->s, body->len);
 	buflen +=body->len;
 
-	if (msg_send(dst_fin, buffer, buflen)<0)
+	if (msg_send_buffer(dst_fin, buffer, buflen, 1)<0)
 	{
 		LM_ERR("cannot send hep duplicate message\n");
 		goto error;
 	}
 
-    if (p) {
-	free_proxy(p); /* frees only p content, not p itself */
-	pkg_free(p);
-    }
+	if (p) {
+		free_proxy(p); /* frees only p content, not p itself */
+		pkg_free(p);
+	}
 	pkg_free(buffer);
 	return 0;
 error:
@@ -1843,14 +1852,14 @@ static int pipport2su (char *pipport, union sockaddr_union *tmp_su, unsigned int
 		LM_ERR("bad protocol %s\n", pipport);
 		return -1;
 	}
-	
+
 	if((len = strlen(pipport)) >= 256) {
 		LM_ERR("too big pipport\n");
 		goto error;
 	}
 
 	/* our tmp string */
-        strncpy(tmp_piport, pipport, len+1);
+	strncpy(tmp_piport, pipport, len+1);
 
 	len = 0;
 
@@ -1867,19 +1876,19 @@ static int pipport2su (char *pipport, union sockaddr_union *tmp_su, unsigned int
 		port_no = 0;
 	}
 	else {
-        	/*the address contains a port number*/
-        	*p = '\0';
-        	p++;
-        	port_str.s = p;
-        	port_str.len = strlen(p);
-        	LM_DBG("the port string is %s\n", p);
-        	if(str2int(&port_str, &port_no) != 0 ) {
-	        	LM_ERR("there is not a valid number port\n");
-	        	goto error;
-        	}
-        	*p = '\0';
-        }
-        
+		/*the address contains a port number*/
+		*p = '\0';
+		p++;
+		port_str.s = p;
+		port_str.len = strlen(p);
+		LM_DBG("the port string is %s\n", p);
+		if(str2int(&port_str, &port_no) != 0 ) {
+			LM_ERR("there is not a valid number port\n");
+			goto error;
+		}
+		*p = '\0';
+	}
+
 	/* now IPv6 address has no brakets. It should be fixed! */
 	if (host_s[0] == '[') {
 		len = strlen(host_s + 1) - 1;
@@ -1907,6 +1916,95 @@ error:
 	return -1;
 }
 
+
+/**
+ *
+ */
+int siptrace_net_data_recv(void *data)
+{
+	sr_net_info_t *nd;
+	struct _siptrace_data sto;
+
+	if(data==0)
+		return -1;
+
+	nd = (sr_net_info_t*)data;
+	if(nd->rcv==NULL || nd->data.s==NULL || nd->data.len<=0)
+		return -1;
+
+	memset(&sto, 0, sizeof(struct _siptrace_data));
+
+	sto.body.s   = nd->data.s;
+	sto.body.len = nd->data.len;
+
+	siptrace_copy_proto(nd->rcv->proto, sto.fromip_buff);
+	strcat(sto.fromip_buff, ip_addr2a(&nd->rcv->src_ip));
+	strcat(sto.fromip_buff,":");
+	strcat(sto.fromip_buff, int2str(nd->rcv->src_port, NULL));
+	sto.fromip.s = sto.fromip_buff;
+	sto.fromip.len = strlen(sto.fromip_buff);
+
+	siptrace_copy_proto(nd->rcv->proto, sto.toip_buff);
+	strcat(sto.toip_buff, ip_addr2a(&nd->rcv->dst_ip));
+	strcat(sto.toip_buff,":");
+	strcat(sto.toip_buff, int2str(nd->rcv->dst_port, NULL));
+	sto.toip.s = sto.toip_buff;
+	sto.toip.len = strlen(sto.toip_buff);
+
+	sto.dir = "in";
+
+	trace_send_hep_duplicate(&sto.body, &sto.fromip, &sto.toip, NULL);
+	return 0;
+
+}
+
+/**
+ *
+ */
+int siptrace_net_data_send(void *data)
+{
+	sr_net_info_t *nd;
+	struct dest_info new_dst;
+	struct _siptrace_data sto;
+
+	if(data==0)
+		return -1;
+
+	nd = (sr_net_info_t*)data;
+	if(nd->dst==NULL || nd->data.s==NULL || nd->data.len<=0)
+		return -1;
+
+	new_dst=*nd->dst;
+	new_dst.send_sock=get_send_socket(0, &nd->dst->to, nd->dst->proto);
+
+	memset(&sto, 0, sizeof(struct _siptrace_data));
+
+	sto.body.s   = nd->data.s;
+	sto.body.len = nd->data.len;
+
+	if (unlikely(new_dst.send_sock==0)) {
+		LM_WARN("no sending socket found\n");
+		strcpy(sto.fromip_buff, "any:255.255.255.255:5060");
+	} else {
+		strncpy(sto.fromip_buff, new_dst.send_sock->sock_str.s,
+			new_dst.send_sock->sock_str.len);
+	}
+	sto.fromip.s = sto.fromip_buff;
+	sto.fromip.len = strlen(sto.fromip_buff);
+
+	siptrace_copy_proto(new_dst.send_sock->proto, sto.toip_buff);
+	strcat(sto.toip_buff, suip2a(&new_dst.to, sizeof(new_dst.to)));
+	strcat(sto.toip_buff,":");
+	strcat(sto.toip_buff, int2str((int)su_getport(&new_dst.to), NULL));
+	sto.toip.s = sto.toip_buff;
+	sto.toip.len = strlen(sto.toip_buff);
+
+	sto.dir = "out";
+
+	trace_send_hep_duplicate(&sto.body, &sto.fromip, &sto.toip, NULL);
+	return 0;
+}
+
 static void siptrace_rpc_status (rpc_t* rpc, void* c) {
 	str status = {0, 0};
 
@@ -1939,8 +2037,8 @@ static void siptrace_rpc_status (rpc_t* rpc, void* c) {
 }
 
 static const char* siptrace_status_doc[2] = {
-        "Get status or turn on/off siptrace. Parameters: on, off or check.",
-        0
+	"Get status or turn on/off siptrace. Parameters: on, off or check.",
+	0
 };
 
 rpc_export_t siptrace_rpc[] = {
@@ -1957,4 +2055,3 @@ static int siptrace_init_rpc(void)
 	}
 	return 0;
 }
-

+ 1 - 1
modules/smsops/doc/smsops_admin.xml

@@ -77,7 +77,7 @@ if (isRPDATA())
 </programlisting>
 		</example>
 		</section>
-		<section id="smsops.f.isRPDATA">
+		<section id="smsops.f.smsdump">
 		<title><function moreinfo="none">smsdump()</function></title>
 		<para>Dumps the content of a 3GPP-SMS message to the Debug-Log.</para>
 		<para>Please make sure, to have debug-Log enabled. Otherwise, you won't see anything.</para>

+ 19 - 19
modules/statsd/lib_statsd.c

@@ -24,13 +24,13 @@ static StatsConnection statsd_connection = {
     "8125"
 };
 
-int statsd_connect(void){
+bool statsd_connect(void){
 
     struct addrinfo *serverAddr;
     int rc, error;
 
     if (statsd_socket.sock > 0){
-        return True;
+        return true;
     }
 
     error = getaddrinfo(
@@ -41,62 +41,62 @@ int statsd_connect(void){
         LM_ERR(
             "Statsd: could not initiate server information (%s)\n",
             gai_strerror(error));
-        return False;
+        return false;
     }
 
     statsd_socket.sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
     if (statsd_socket.sock == 0 ){
         LM_ERR("Statsd: could not initiate a connect to statsd\n");
-        return False;
+        return false;
     }
 
     rc = connect(
         statsd_socket.sock, serverAddr->ai_addr, serverAddr->ai_addrlen);
     if (rc < 0){
         LM_ERR("Statsd: could not initiate a connect to statsd\n");
-        return False;
+        return false;
     }
-    return True;
+    return true;
 }
 
-int send_command(char *command){
+bool send_command(char *command){
     int send_result;
 
     if (!statsd_connect()){
-        return False;
+        return false;
     }
 
     send_result = send(statsd_socket.sock, command, strlen(command), 0);
     if ( send_result < 0){
         LM_ERR("could not send the correct info to statsd (%i| %s)\n",
             send_result, strerror(errno));
-        return True;
+        return true;
     }
     LM_DBG("Sent to statsd (%s)", command);
-    return True;
+    return true;
 }
 
-int statsd_set(char *key, char *value){
+bool statsd_set(char *key, char *value){
    char* end = 0;
    char command[254];
    int val;
    val = strtol(value, &end, 0);
    if (*end){
        LM_ERR("statsd_count could not  use the provide value(%s)\n", value);
-       return False;
+       return false;
    }
    snprintf(command, sizeof command, "%s:%i|s\n", key, val);
    return send_command(command);
 }
 
 
-int statsd_gauge(char *key, char *value){
+bool statsd_gauge(char *key, char *value){
    char command[254];
    snprintf(command, sizeof command, "%s:%s|g\n", key, value);
    return send_command(command);
 }
 
-int statsd_count(char *key, char *value){
+bool statsd_count(char *key, char *value){
    char* end = 0;
    char command[254];
    int val;
@@ -104,19 +104,19 @@ int statsd_count(char *key, char *value){
    val = strtol(value, &end, 0);
    if (*end){
        LM_ERR("statsd_count could not  use the provide value(%s)\n", value);
-       return False;
+       return false;
    }
    snprintf(command, sizeof command, "%s:%i|c\n", key, val);
    return send_command(command);
 }
 
-int statsd_timing(char *key, int value){
+bool statsd_timing(char *key, int value){
    char command[254];
    snprintf(command, sizeof command, "%s:%i|ms\n", key, value);
    return send_command(command);
 }
 
-int statsd_init(char *ip, char *port){
+bool statsd_init(char *ip, char *port){
 
     if (ip != NULL){
         statsd_connection.ip = ip;
@@ -127,7 +127,7 @@ int statsd_init(char *ip, char *port){
     return statsd_connect();
 }
 
-int statsd_destroy(void){
+bool statsd_destroy(void){
     statsd_socket.sock = 0;
-    return True;
+    return true;
 }

+ 9 - 11
modules/statsd/lib_statsd.h

@@ -1,8 +1,6 @@
+#include <stdbool.h>
 
 #define BUFFER_SIZE 8192
-typedef int Bool;
-#define True 1
-#define False 0
 
 typedef struct StatsConnection{
     char *ip;
@@ -17,11 +15,11 @@ typedef struct StatsdSocket {
     char data[BUFFER_SIZE]; // buffer for the answer data
 } StatsdSocket;
 
-int statsd_connect(void);
-int send_command(char *command);
-int statsd_set(char *key, char *value);
-int statsd_gauge(char *key, char *value);
-int statsd_count(char *key, char *value);
-int statsd_timing(char *key, int value);
-int statsd_init(char *ip, char *port);
-int statsd_destroy(void);
+bool statsd_connect(void);
+bool send_command(char *command);
+bool statsd_set(char *key, char *value);
+bool statsd_gauge(char *key, char *value);
+bool statsd_count(char *key, char *value);
+bool statsd_timing(char *key, int value);
+bool statsd_init(char *ip, char *port);
+bool statsd_destroy(void);

+ 45 - 0
modules/tcpops/tcpops.c

@@ -31,6 +31,8 @@
 #include "../../globals.h"
 #include "../../pass_fd.h"
 #include "../../timer.h"
+#include "../../fmsg.h"
+#include "../../sr_module.h"
 
 /**
  * gets the fd of the current message source connection
@@ -185,3 +187,46 @@ int tcpops_set_connection_lifetime(struct tcp_connection* con, int time) {
 	LM_DBG("new connection lifetime for conid=%d: %d\n", con->id, con->timeout);
 	return 1;
 }
+
+static void tcpops_tcp_closed_run_route(struct tcp_connection *con)
+{
+	int rt, backup_rt;
+	struct run_act_ctx ctx;
+	sip_msg_t *fmsg;
+	LM_DBG("tcp_closed_run_route event_route[tcp:closed]\n");
+
+	rt = route_get(&event_rt, "tcp:closed");
+	if (rt < 0 || event_rt.rlist[rt] == NULL)
+	{
+		LM_DBG("route does not exist");
+		return;
+	}
+
+	if (faked_msg_init() < 0)
+	{
+		LM_ERR("faked_msg_init() failed\n");
+		return;
+	}
+	fmsg = faked_msg_next();
+	fmsg->rcv = con->rcv;
+
+	backup_rt = get_route_type();
+	set_route_type(EVENT_ROUTE);
+	init_run_actions_ctx(&ctx);
+	run_top_route(event_rt.rlist[rt], fmsg, 0);
+	set_route_type(backup_rt);
+}
+
+int tcpops_handle_tcp_closed(void *data)
+{
+	tcp_event_info_t *tev = (tcp_event_info_t *) data;
+
+	if (tev == NULL || tev->con == NULL) {
+		LM_WARN("received bad TCP closed event\n");
+		return -1;
+	}
+
+	tcpops_tcp_closed_run_route(tev->con);
+
+	return 0;
+}

+ 2 - 0
modules/tcpops/tcpops.h

@@ -25,11 +25,13 @@
 #define TCP_KEEPALIVE_H_
 
 #include "../../tcp_conn.h"
+#include "../../events.h"
 
 int tcpops_get_current_fd(int conid, int *fd);
 int tcpops_acquire_fd_from_tcpmain(int conid, int *fd);
 int tcpops_keepalive_enable(int fd, int idle, int count, int interval, int closefd);
 int tcpops_keepalive_disable(int fd, int closefd);
 int tcpops_set_connection_lifetime(struct tcp_connection* con, int time);
+int tcpops_handle_tcp_closed(void *data);
 
 #endif /* TCP_KEEPALIVE_H_ */

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff