Browse Source

Merge branch 'master' into hpw/branch_failure_route

* master: (33 commits)
  core: set TOS for IPv6 UDP sockets
  rtpproxy: add missing wrapper function for unforce_rtp_proxy
  modules/outbound: further improvement to the use_outbound() check
  rtpproxy: support pvars in function parameters
  modules/rr: copy the flow-token for "incoming" messages when using outbound
  modules/outbound: improved check for outbound
  modules/rr: only use flow-token for routing if it doesn't point to the source of the request
  pkg/kamailio/(centos|fedora): Updated .spec after addition of cnxcc module
  Makefile.groups: cnxcc module added to extra list
  core: auto-define cfg directive MOD_modname for each loaded module
  snmpstats Add tcpasync and tcpmaxconns
  snmpstats Add IDs to sections in documentation
  cnxcc: added new module for credit control
  snmpstats activate new parts of the KAMAILIO-MIB
  snmpstats Add information about version and tcp connections
  usrloc: new option for db_mode - DB_READONLY (4)
  enum: define the max size for numbers
  db_mysql: new module parameter - insert_delayed
  Makefile: exclude debian sym link when generating tarball
  msrp: fix compile warning of argument type in dbg message
  ...
Hugh Waite 12 years ago
parent
commit
464ce5d71d
66 changed files with 5972 additions and 227 deletions
  1. 1 0
      Makefile
  2. 3 3
      Makefile.groups
  3. 32 10
      examples/scscf/kamailio.cfg
  4. 0 7
      modules/auth_diameter/auth_diameter.c
  5. 1 3
      modules/auth_diameter/authorize.c
  6. 6 1
      modules/carrierroute/README
  7. 43 1
      modules/carrierroute/carrierroute.c
  8. 3 0
      modules/carrierroute/carrierroute.h
  9. 118 3
      modules/carrierroute/cr_func.c
  10. 7 1
      modules/carrierroute/doc/carrierroute_admin.xml
  11. 17 0
      modules/cnxcc/Makefile
  12. 270 0
      modules/cnxcc/README
  13. 63 0
      modules/cnxcc/cnxcc.c
  14. 39 0
      modules/cnxcc/cnxcc.h
  15. 176 0
      modules/cnxcc/cnxcc_check.c
  16. 16 0
      modules/cnxcc/cnxcc_check.h
  17. 1526 0
      modules/cnxcc/cnxcc_mod.c
  18. 156 0
      modules/cnxcc/cnxcc_mod.h
  19. 269 0
      modules/cnxcc/cnxcc_rpc.c
  20. 16 0
      modules/cnxcc/cnxcc_rpc.h
  21. 71 0
      modules/cnxcc/cnxcc_sip_msg_faker.c
  22. 29 0
      modules/cnxcc/cnxcc_sip_msg_faker.h
  23. 4 0
      modules/cnxcc/doc/Makefile
  24. 36 0
      modules/cnxcc/doc/cnxcc.xml
  25. 299 0
      modules/cnxcc/doc/cnxcc_admin.xml
  26. 983 0
      modules/cnxcc/example/kamailio-cnxcc.cfg
  27. 22 6
      modules/db_mysql/README
  28. 20 0
      modules/db_mysql/doc/db_mysql_admin.xml
  29. 1 0
      modules/db_mysql/km_db_mysql.c
  30. 1 0
      modules/db_mysql/km_db_mysql.h
  31. 8 3
      modules/db_mysql/km_dbase.c
  32. 1 0
      modules/db_mysql/mysql_mod.c
  33. 22 15
      modules/drouting/README
  34. 15 0
      modules/drouting/doc/drouting_admin.xml
  35. 38 0
      modules/drouting/drouting.c
  36. 4 4
      modules/enum/enum.c
  37. 3 2
      modules/enum/enum.h
  38. 1 1
      modules/msrp/msrp_parser.c
  39. 109 38
      modules/outbound/ob_mod.c
  40. 2 0
      modules/p_usrloc/udomain.c
  41. 2 0
      modules/p_usrloc/udomain.h
  42. 7 4
      modules/rr/loose.c
  43. 136 25
      modules/rr/record.c
  44. 4 4
      modules/rtpproxy/README
  45. 1 1
      modules/rtpproxy/doc/rtpproxy_admin.xml
  46. 55 19
      modules/rtpproxy/rtpproxy.c
  47. 8 8
      modules/snmpstats/README
  48. 10 10
      modules/snmpstats/doc/snmpstats_admin.xml
  49. 402 0
      modules/snmpstats/kamailioNet.c
  50. 21 0
      modules/snmpstats/kamailioNet.h
  51. 440 0
      modules/snmpstats/kamailioServer.c
  52. 52 0
      modules/snmpstats/kamailioServer.h
  53. 302 2
      modules/snmpstats/mibs/KAMAILIO-MIB
  54. 0 1
      modules/snmpstats/mibs/KAMAILIO-SIP-COMMON-MIB
  55. 5 15
      modules/snmpstats/snmpSIPMethodSupportedTable.c
  56. 1 1
      modules/snmpstats/snmpSIPServerObjects.c
  57. 10 0
      modules/snmpstats/sub_agent.c
  58. 28 23
      modules/usrloc/README
  59. 9 0
      modules/usrloc/doc/usrloc_admin.xml
  60. 9 5
      modules/usrloc/ul_mod.c
  61. 1 0
      modules/usrloc/urecord.c
  62. 1 0
      modules/usrloc/usrloc.h
  63. 4 0
      pkg/kamailio/fedora/17/kamailio.spec
  64. 16 0
      sr_module.c
  65. 17 4
      udp_server.c
  66. 0 7
      utils/sercmd/sercmd.c

+ 1 - 0
Makefile

@@ -622,6 +622,7 @@ tar: makefile_vars $(auto_gen_keep)
 	$(TAR) -C .. \
 	$(TAR) -C .. \
 		--exclude=$(notdir $(CURDIR))/test* \
 		--exclude=$(notdir $(CURDIR))/test* \
 		--exclude=$(notdir $(CURDIR))/tmp* \
 		--exclude=$(notdir $(CURDIR))/tmp* \
+		--exclude=$(notdir $(CURDIR))/debian \
 		--exclude=$(notdir $(CURDIR))/debian/$(MAIN_NAME) \
 		--exclude=$(notdir $(CURDIR))/debian/$(MAIN_NAME) \
 		--exclude=$(notdir $(CURDIR))/debian/$(MAIN_NAME)-* \
 		--exclude=$(notdir $(CURDIR))/debian/$(MAIN_NAME)-* \
 		--exclude=$(notdir $(CURDIR))/$(MAIN_NAME)_tls* \
 		--exclude=$(notdir $(CURDIR))/$(MAIN_NAME)_tls* \

+ 3 - 3
Makefile.groups

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

+ 32 - 10
examples/scscf/kamailio.cfg

@@ -430,16 +430,38 @@ route[XMLRPC] {
 # Route for handling Registrations:
 # Route for handling Registrations:
 ######################################################################
 ######################################################################
 route[REGISTER] {
 route[REGISTER] {
-	xlog("L_ERR", "Enter register block");
-	t_newtran();
-	
-	ims_www_authenticate(NETWORKNAME);
-
-	#check to see if user is authenticated - ie sip header has auth information - (already challenged)
-	if ($avp(maa_return_code) == 1) {
-		# user has not been authenticated. Lets send a challenge via 401 Unauthorized
-		ims_www_challenge("$td"); 
-		exit;
+        if (!ims_www_authenticate(NETWORKNAME)) {
+                if ($? == -2) {
+                        t_reply("403", "Authentication Failed");
+                        exit;
+                } else if ($? == -3) {
+                        t_reply("400", "Bad Request");
+                        exit;
+                } else {
+                        #user has not been authenticated. Lets send a challenge via 401 Unauthorized
+                        xlog("L_DBG","About to challenge! auth_ims\n");
+                        ims_www_challenge("$td");
+                        #this is async so to know status we have to check the reply avp
+                        xlog("L_DBG","maa_return code is $avp(s:maa_return_code)\n");
+
+                        switch ($avp(s:maa_return_code)){
+                                case 1: #success
+                                        xlog("L_DBG", "MAR success - 401/407 response sent from module");
+                                        break;
+                                case -1: #failure
+                                        xlog("L_ERR", "MAR failure - error response sent from module");
+                                        break;
+                                case -2: #error
+                                        xlog("L_ERR", "MAR error - sending error response now");
+                                        t_reply("500", "MAR failed");
+                                        break;
+                                default:
+                                        xlog("L_ERR", "Unknown return code from MAR, value is [$avp(s:uaa_return_code)]");
+                                        t_reply("500", "Unknown response code from MAR");
+                                        break;
+                        }
+                        exit;
+                }
 	} else {
 	} else {
 		# We need to check if this user is registered or not
 		# We need to check if this user is registered or not
 		if (!impu_registered("location")) {
 		if (!impu_registered("location")) {

+ 0 - 7
modules/auth_diameter/auth_diameter.c

@@ -212,13 +212,10 @@ int diameter_www_authorize(struct sip_msg* _msg, char* _realm, char* _s2)
 
 
 static int group_fixup(void** param, int param_no)
 static int group_fixup(void** param, int param_no)
 {
 {
-	void* ptr;
 	str* s;
 	str* s;
 
 
 	if (param_no == 1) 
 	if (param_no == 1) 
 	{
 	{
-		ptr = *param;
-		
 		if (!strcasecmp((char*)*param, "Request-URI")) 
 		if (!strcasecmp((char*)*param, "Request-URI")) 
 		{
 		{
 			*param = (void*)1;
 			*param = (void*)1;
@@ -245,8 +242,6 @@ static int group_fixup(void** param, int param_no)
 				
 				
 		LM_ERR("unsupported Header Field identifier\n");
 		LM_ERR("unsupported Header Field identifier\n");
 		return E_UNSPEC;
 		return E_UNSPEC;
-		
-		//pkg_free(ptr);
 	} 
 	} 
 	
 	
 	if (param_no == 2) 
 	if (param_no == 2) 
@@ -257,14 +252,12 @@ static int group_fixup(void** param, int param_no)
 			LM_ERR("no pkg memory left\n");
 			LM_ERR("no pkg memory left\n");
 			return E_UNSPEC;
 			return E_UNSPEC;
 		}
 		}
-		ptr = *param;
 		s->s = (char*)*param;
 		s->s = (char*)*param;
 		s->len = strlen(s->s);
 		s->len = strlen(s->s);
 		*param = (void*)s;
 		*param = (void*)s;
 	}
 	}
 
 
 end:
 end:
-//	pkg_free(ptr);
 	return 0;
 	return 0;
 }
 }
 
 

+ 1 - 3
modules/auth_diameter/authorize.c

@@ -295,7 +295,7 @@ int authorize(struct sip_msg* msg, pv_elem_t* realm, int hftype)
 int diameter_authorize(struct hdr_field* hdr, str* p_method, struct sip_uri uri,
 int diameter_authorize(struct hdr_field* hdr, str* p_method, struct sip_uri uri,
 						struct sip_uri ruri, unsigned int m_id, rd_buf_t* rb)
 						struct sip_uri ruri, unsigned int m_id, rd_buf_t* rb)
 {
 {
-	str method, user_name;
+	str user_name;
 	AAAMessage *req;
 	AAAMessage *req;
 	AAA_AVP *avp, *position; 
 	AAA_AVP *avp, *position; 
 	int name_flag, port_flag;
 	int name_flag, port_flag;
@@ -316,8 +316,6 @@ int diameter_authorize(struct hdr_field* hdr, str* p_method, struct sip_uri uri,
 	else
 	else
 		cred = NULL;
 		cred = NULL;
 			
 			
-	method = *p_method;
-
 	if(!cred)
 	if(!cred)
 	{
 	{
 		/* Username AVP */
 		/* Username AVP */

+ 6 - 1
modules/carrierroute/README

@@ -511,6 +511,11 @@ descavp)
    This is useful if you need some additional informations that belongs to
    This is useful if you need some additional informations that belongs to
    each gw, like the destination or the number of channels.
    each gw, like the destination or the number of channels.
 
 
+   The function pays special attention to the failurerouting cases, so
+   that any destination that has failed to provide a successful response
+   will not be reused in a subsequent call of cr_route. This situation can
+   appear when different route domains contain a set of common gateways.
+
    This function is only usable with rewrite_user and prefix_matching
    This function is only usable with rewrite_user and prefix_matching
    containing a valid string. This string needs to be numerical if the
    containing a valid string. This string needs to be numerical if the
    match_mode parameter is set to 10. It uses the standard CRC32 algorithm
    match_mode parameter is set to 10. It uses the standard CRC32 algorithm
@@ -1063,7 +1068,7 @@ Chapter 2. Module parameter for database access.
 
 
    URL to the database containing the data.
    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
    Example 2.1. Set db_url parameter
 ...
 ...

+ 43 - 1
modules/carrierroute/carrierroute.c

@@ -41,6 +41,7 @@
 #include "../../str.h"
 #include "../../str.h"
 #include "../../mem/mem.h"
 #include "../../mem/mem.h"
 #include "../../ut.h" /* for user2uid() */
 #include "../../ut.h" /* for user2uid() */
+#include "../../rpc_lookup.h" /* for sercmd */
 #include "carrierroute.h"
 #include "carrierroute.h"
 #include "cr_fixup.h"
 #include "cr_fixup.h"
 #include "cr_map.h"
 #include "cr_map.h"
@@ -51,6 +52,9 @@
 #include "config.h"
 #include "config.h"
 #include <sys/stat.h>
 #include <sys/stat.h>
 
 
+#define AVP_CR_URIS "_cr_uris"
+int_str cr_uris_avp; // contains all PSTN destinations
+
 MODULE_VERSION
 MODULE_VERSION
 
 
 str carrierroute_db_url = str_init(DEFAULT_RODB_URL);
 str carrierroute_db_url = str_init(DEFAULT_RODB_URL);
@@ -129,6 +133,8 @@ static mi_export_t mi_cmds[] = {
 	{ 0, 0, 0, 0, 0}
 	{ 0, 0, 0, 0, 0}
 };
 };
 
 
+static rpc_export_t rpc_methods[];
+
 struct module_exports exports = {
 struct module_exports exports = {
 	"carrierroute",
 	"carrierroute",
 	DEFAULT_DLFLAGS, /* dlopen flags */
 	DEFAULT_DLFLAGS, /* dlopen flags */
@@ -164,6 +170,11 @@ static int mod_init(void) {
 		return -1;
 		return -1;
 	}
 	}
 
 
+	if(rpc_register_array(rpc_methods)!=0) {
+		LM_ERR("failed to register RPC commands\n");
+		return -1;
+	}
+
 	subscriber_table.len = strlen(subscriber_table.s);
 	subscriber_table.len = strlen(subscriber_table.s);
 	subscriber_username_col.len = strlen(subscriber_username_col.s);
 	subscriber_username_col.len = strlen(subscriber_username_col.s);
 	subscriber_domain_col.len = strlen(subscriber_domain_col.s);
 	subscriber_domain_col.len = strlen(subscriber_domain_col.s);
@@ -241,6 +252,10 @@ static int mod_init(void) {
 	if(mode == CARRIERROUTE_MODE_DB){
 	if(mode == CARRIERROUTE_MODE_DB){
 		carrierroute_db_close();
 		carrierroute_db_close();
 	}
 	}
+
+	cr_uris_avp.s.s = AVP_CR_URIS;
+	cr_uris_avp.s.len = sizeof(AVP_CR_URIS) -1;
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -263,10 +278,37 @@ static int mi_child_init(void) {
 	return 0;
 	return 0;
 }
 }
 
 
-
 static void mod_destroy(void) {
 static void mod_destroy(void) {
 	if(mode == CARRIERROUTE_MODE_DB){
 	if(mode == CARRIERROUTE_MODE_DB){
 		carrierroute_db_close();
 		carrierroute_db_close();
 	}
 	}
 	destroy_route_data();
 	destroy_route_data();
 }
 }
+
+static const char *rpc_cr_reload_routes_doc[2] = {
+	"Reload routes", 0
+};
+
+static void rpc_cr_reload_routes(rpc_t *rpc, void *c) {
+
+	if(mode == CARRIERROUTE_MODE_DB){
+		if (carrierroute_dbh==NULL) {
+			carrierroute_dbh = carrierroute_dbf.init(&carrierroute_db_url);
+			if(carrierroute_dbh==0 ) {
+				LM_ERR("cannot initialize database connection\n");
+				return;
+			}
+		}
+	}
+
+	if ( (reload_route_data())!=0 ) {
+		LM_ERR("failed to load routing data\n");
+		return;
+	}
+}
+
+static rpc_export_t rpc_methods[] = {
+	{ "cr.reload_routes",  rpc_cr_reload_routes, rpc_cr_reload_routes_doc, 0},
+	{0, 0, 0, 0}
+};
+

+ 3 - 0
modules/carrierroute/carrierroute.h

@@ -31,6 +31,7 @@
 #define CARRIERROUTE_H
 #define CARRIERROUTE_H
 
 
 #include "../../str.h"
 #include "../../str.h"
+#include "../../usr_avp.h"
 
 
 #define DICE_MAX 1000
 #define DICE_MAX 1000
 
 
@@ -53,4 +54,6 @@ extern const str CR_EMPTY_PREFIX;
 extern int mode;
 extern int mode;
 extern int cr_match_mode;
 extern int cr_match_mode;
 
 
+extern int_str cr_uris_avp;
+
 #endif
 #endif

+ 118 - 3
modules/carrierroute/cr_func.c

@@ -49,6 +49,8 @@
 #include "carrierroute.h"
 #include "carrierroute.h"
 #include "config.h"
 #include "config.h"
 
 
+#define MAX_DESTINATIONS 64
+
 enum hash_algorithm {
 enum hash_algorithm {
 	alg_crc32 = 1, /*!< hashing algorithm is CRC32 */
 	alg_crc32 = 1, /*!< hashing algorithm is CRC32 */
 	alg_crc32_nofallback, /*!< same algorithm as alg_crc32, with only a backup rule, but no fallback tree is chosen
 	alg_crc32_nofallback, /*!< same algorithm as alg_crc32, with only a backup rule, but no fallback tree is chosen
@@ -269,6 +271,65 @@ static struct route_rule * get_rule_by_hash(const struct route_flags * rf,
 	return act_hash;
 	return act_hash;
 }
 }
 
 
+// debug functions for cr_uri_avp
+/*
+static void print_cr_uri_avp(){
+	struct search_state st;
+	int_str val;
+	int elem = 0;
+
+	if (!search_first_avp( AVP_VAL_STR | AVP_NAME_STR, cr_uris_avp, &val, &st)) {
+		LM_DBG("no AVPs - we are done!\n");
+		return;
+	}
+
+	LM_DBG("	cr_uri_avp[%d]=%.*s\n", elem++, val.s.len, val.s.s);
+
+	while (  search_next_avp(&st, &val) ) {
+		LM_DBG("	cr_uri_avp[%d]=%.*s\n", elem++, val.s.len, val.s.s);
+	}
+}
+*/
+
+static void build_used_uris_list(avp_value_t* used_dests, int* no_dests){
+	struct search_state st;
+	int_str val;
+	*no_dests = 0;
+
+	if (!search_first_avp( AVP_VAL_STR | AVP_NAME_STR, cr_uris_avp, &val, &st)) {
+		//LM_DBG("no AVPs - we are done!\n");
+		return;
+	}
+
+	used_dests[(*no_dests)++] = val;
+	//LM_DBG("	used_dests[%d]=%.*s \n", (*no_dests)-1, used_dests[(*no_dests)-1].s.len, used_dests[(*no_dests)-1].s.s);
+
+	while ( search_next_avp(&st, &val) ) {
+		if ( MAX_DESTINATIONS == *no_dests ) {
+			LM_ERR("Too many  AVPs - we are done!\n");
+			return;
+		}
+		used_dests[(*no_dests)++] = val;
+		//LM_DBG("	used_dests[%d]=%.*s \n", (*no_dests)-1, used_dests[(*no_dests)-1].s.len, used_dests[(*no_dests)-1].s.s);
+	}
+
+	//LM_DBG("sucessfully built used_uris list!\n");
+}
+
+int cr_uri_already_used(str dest , avp_value_t* used_dests, int no_dests){
+	int i;
+	for (i=0; i<no_dests; i++){
+		if ( (dest.len == used_dests[i].s.len) &&
+				(memcmp(dest.s, used_dests[i].s.s, dest.len)==0)){
+			LM_NOTICE("Candidate destination <%.*s> was previously used.\n", dest.len, dest.s);
+			return 1;
+		}
+
+	}
+	//LM_DBG("cr_uri_already_used: Candidate destination <%.*s> was NEVER USED.\n", dest.len, dest.s);
+	return 0;
+}
+
 
 
 /**
 /**
  * does the work for rewrite_on_rule, writes the new URI into dest
  * does the work for rewrite_on_rule, writes the new URI into dest
@@ -288,7 +349,6 @@ static int actually_rewrite(const struct route_rule *rs, str *dest,
 	char *p;
 	char *p;
 	int_str avp_val;
 	int_str avp_val;
 	int strip = 0;
 	int strip = 0;
-
 	str l_user;
 	str l_user;
 	
 	
 	if( !rs || !dest || !msg || !user) {
 	if( !rs || !dest || !msg || !user) {
@@ -406,6 +466,11 @@ static int rewrite_on_rule(struct route_flags *rf_head, flag_t flags, str * dest
 
 
 	switch (alg) {
 	switch (alg) {
 		case alg_crc32:
 		case alg_crc32:
+		{
+			static avp_value_t used_dests[MAX_DESTINATIONS];
+			static int no_dests = 0;
+			avp_value_t cr_new_uri;
+
 			if(rf->dice_max == 0) {
 			if(rf->dice_max == 0) {
 				LM_ERR("invalid dice_max value\n");
 				LM_ERR("invalid dice_max value\n");
 				return -1;
 				return -1;
@@ -414,15 +479,48 @@ static int rewrite_on_rule(struct route_flags *rf_head, flag_t flags, str * dest
 				LM_ERR("could not hash message with CRC32");
 				LM_ERR("could not hash message with CRC32");
 				return -1;
 				return -1;
 			}
 			}
+
 			/* This auto-magically takes the last rule if anything is broken.
 			/* This auto-magically takes the last rule if anything is broken.
 			 * Sometimes the hash result is zero. If the first rule is off
 			 * Sometimes the hash result is zero. If the first rule is off
 			 * (has a probablility of zero) then it has also a dice_to of
 			 * (has a probablility of zero) then it has also a dice_to of
 			 * zero and the message could not be routed at all if we use
 			 * zero and the message could not be routed at all if we use
 			 * '<' here. Thus the '<=' is necessary.
 			 * '<' here. Thus the '<=' is necessary.
+			 *
+			 * cr_uri_already_used is a function that checks that the selected
+			 * rule has not been previously used as a failed destinatin
 			 */
 			 */
+
 			for (rr = rf->rule_list;
 			for (rr = rf->rule_list;
-			        rr->next != NULL && rr->dice_to <= prob;
-		        rr = rr->next) {}
+				rr->next!= NULL && rr->dice_to <= prob ; rr = rr->next) {}
+
+			//LM_DBG("CR: candidate hashed destination is: <%.*s>\n", rr->host.len, rr->host.s);
+
+			if (is_route_type(FAILURE_ROUTE) && (mode == CARRIERROUTE_MODE_DB) ){
+				build_used_uris_list(used_dests, &no_dests);
+
+				if (cr_uri_already_used(rr->host, used_dests, no_dests) ) {
+					//LM_DBG("CR: selecting new destination !!! \n");
+					for (rr = rf->rule_list;
+								rr!= NULL && cr_uri_already_used(rr->host, used_dests, no_dests); rr = rr->next) {}
+					/* are there any destinations that were not already used? */
+					if (rr == NULL) {
+						LM_NOTICE("All gateways from this group were already used\n");
+						return -1;
+					}
+
+					/* this is a hack: we do not take probabilities into consideration if first destination
+					 * was previously tried */
+
+					do {
+						int rule_no = rand() % rf->rule_num;
+						//LM_DBG("CR: trying rule_no=%d \n", rule_no);
+						for (rr = rf->rule_list; (rule_no > 0) && (rr->next!=NULL) ; rule_no-- , rr = rr->next) {}
+					} while (cr_uri_already_used(rr->host, used_dests, no_dests));
+					LM_DBG("CR: candidate selected destination is: <%.*s>\n", rr->host.len, rr->host.s);
+				}
+			}
+			/*This should be regarded as an ELSE branch for the if above
+			 * ( status exists for mode == CARRIERROUTE_MODE_FILE */
 			if (!rr->status) {
 			if (!rr->status) {
 				if (!rr->backup) {
 				if (!rr->backup) {
 					LM_ERR("all routes are off\n");
 					LM_ERR("all routes are off\n");
@@ -435,7 +533,24 @@ static int rewrite_on_rule(struct route_flags *rf_head, flag_t flags, str * dest
 					rr = rr->backup->rr;
 					rr = rr->backup->rr;
 				}
 				}
 			}
 			}
+
+			//LM_DBG("CR: destination is: <%.*s>\n", rr->host.len, rr->host.s);
+			cr_new_uri.s = rr->host;
+			/* insert used destination into avp, in case corresponding request fails and
+			 * another destination has to be used; this new destination must not be one
+			 * that failed before
+			 */
+
+			if (mode == CARRIERROUTE_MODE_DB){
+				if ( add_avp( AVP_VAL_STR | AVP_NAME_STR, cr_uris_avp, cr_new_uri) < 0){
+					LM_ERR("set AVP failed\n");
+					return -1;
+				}
+				//print_cr_uri_avp();
+			}
+
 			break;
 			break;
+		}
 		case alg_crc32_nofallback:
 		case alg_crc32_nofallback:
 			if ((prob = (hash_func(msg, hash_source, rf->max_targets))) < 0) {
 			if ((prob = (hash_func(msg, hash_source, rf->max_targets))) < 0) {
 				LM_ERR("could not hash message with CRC32");
 				LM_ERR("could not hash message with CRC32");

+ 7 - 1
modules/carrierroute/doc/carrierroute_admin.xml

@@ -427,7 +427,13 @@ cr_tree_rewrite_uri(tree, domain)
         is stored in an AVP). This is useful if you need some additional
         is stored in an AVP). This is useful if you need some additional
         informations that belongs to each gw, like the destination or the
         informations that belongs to each gw, like the destination or the
         number of channels.
         number of channels.
-        </para>
+		</para>
+		<para>
+		The function pays special attention to the failurerouting cases, so that
+		any destination that has failed to provide a successful response will not
+		be reused in a subsequent call of cr_route. This situation can appear when
+		different route domains contain a set of common gateways.
+		</para>
         <para>
         <para>
         This function is only usable with rewrite_user and prefix_matching
         This function is only usable with rewrite_user and prefix_matching
         containing a valid string. This string needs to be numerical if the match_mode
         containing a valid string. This string needs to be numerical if the match_mode

+ 17 - 0
modules/cnxcc/Makefile

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

+ 270 - 0
modules/cnxcc/README

@@ -0,0 +1,270 @@
+
+cnxcc Module
+
+Carlos Ruiz Diaz
+
+   ConexionGroup SA
+
+   Copyright © 2012 Carlos Ruiz Diaz, [email protected]
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+        2. Dependencies
+
+              2.1. Modules
+
+        3. Parameters
+
+              3.1. dlg_flag (integer)
+              3.2. credit_check_period (integer)
+
+        4. Functions
+
+              4.1. cnxcc_set_max_credit()
+              4.2. cnxcc_set_max_time()
+
+        5. Exported RPC Commands
+
+              5.1. cnxcc.active_clients
+              5.2. cnxcc.check_client
+              5.3. cnxcc.kill_call
+
+        6. Events
+        7. Web Interface
+        8. Sample
+
+   List of Examples
+
+   1.1. dlg_flag
+   1.2. credit_check_period
+   1.3. cnxcc_set_max_credit()
+   1.4. cnxcc_set_max_time()
+   1.5. kamailio-cnxcc.cfg
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+   2. Dependencies
+
+        2.1. Modules
+
+   3. Parameters
+
+        3.1. dlg_flag (integer)
+        3.2. credit_check_period (integer)
+
+   4. Functions
+
+        4.1. cnxcc_set_max_credit()
+        4.2. cnxcc_set_max_time()
+
+   5. Exported RPC Commands
+
+        5.1. cnxcc.active_clients
+        5.2. cnxcc.check_client
+        5.3. cnxcc.kill_call
+
+   6. Events
+   7. Web Interface
+   8. Sample
+
+1. Overview
+
+   This module was designed to act as a mechanism to limit call duration
+   based on credit information parameters. After getting the credit
+   information of the call being set up, you can instruct the module to
+   start monitoring the consumed credit to shutdown a single call or a
+   group of calls in case of credit exhaustion.
+
+   Every call is associated to an unique client/customer identifier. If a
+   credit event occurs, all calls hooked to this identifier are
+   automatically shutdown.
+
+   Cnxcc is dialog-aware so there's no need to explicitly
+   allocate/deallocate the monitoring. Only a single function call inside
+   the script is needed upon reception of the INVITE.
+
+   The credit discount rate is proportional to the number of calls grouped
+   inside an identifier. Once the setup of the first call is done, the
+   information remains while the call is active. If the customer starts a
+   new call with the same routing criteria, it will land in the same
+   monitoring bag and it will consume the same pool of credit in rates
+   that are equal to the cost per second of both calls.
+
+   If your accounting program does not maintain the state of the call in
+   real time, this module can provide you that ability.
+
+2. Dependencies
+
+   2.1. Modules
+
+2.1. Modules
+
+   The following module must be loaded before this module:
+     * dialog
+
+3. Parameters
+
+   3.1. dlg_flag (integer)
+   3.2. credit_check_period (integer)
+
+3.1.  dlg_flag (integer)
+
+   Flag to indicate if the dialog must be monitored or not. Messages are
+   flagged with this value if we call one of the monitoring functions.
+
+   Example 1.1. dlg_flag
+...
+modparam("cnxcc", "dlg_flag", 29)
+...
+
+3.2. credit_check_period (integer)
+
+   Indicates how often the credit checking function should be called. It
+   is directly related to the precison of the module. The maximum
+   precision is 1, which means that every call is checked every one
+   second.
+
+   Values greater than 1 leads to precision lost but less CPU consumption.
+
+   Example 1.2. credit_check_period
+...
+modparam("cnxcc", "credit_check_period", 1)
+...
+
+4. Functions
+
+   4.1. cnxcc_set_max_credit()
+   4.2. cnxcc_set_max_time()
+
+4.1.  cnxcc_set_max_credit()
+
+   Specifies the initial pulse, final pulse, max credit and cost per
+   second of a call. The discount is calculated in pulses (30/6, 1/1, etc)
+   and sustracted from the pool of credit.
+
+   Return code:
+     * 1 - successful
+     * -1 - failed, error logged
+
+   Example 1.3. cnxcc_set_max_credit()
+...
+$var(customer) = "john-doe-123-premium";
+$var(credit) = "100";
+$var(cps)   = "2.00";         # cost per second
+$var(initial_p)   = "030";    # intial pulse
+$var(final_p)   = "006";      # final pulse
+
+cnxcc_set_max_credit("$var(customer)", "$var(credit)", "$var(cps)", "$var(initia
+l_p)", "$var(final_p)");
+...
+
+4.2.  cnxcc_set_max_time()
+
+   Specifies the amount of time the call should last at most.
+
+   Return code:
+     * 1 - successful
+     * -1 - failed, error logged
+
+   Example 1.4. cnxcc_set_max_time()
+...
+$var(customer) = "john-doe-123-basic";
+$var(max_time) = 120;
+
+cnxcc_set_max_tim ("$var(customer)", "$var(max_time)");
+...
+
+5. Exported RPC Commands
+
+   5.1. cnxcc.active_clients
+   5.2. cnxcc.check_client
+   5.3. cnxcc.kill_call
+
+5.1. cnxcc.active_clients
+
+   Retrieves all calls grouped by their identifiers.
+
+   Parameters: none
+
+   Example:
+            sercmd cnxcc.active_clients
+
+5.2. cnxcc.check_client
+
+   Retrives all calls from a particular identifier.
+
+   Parameters: client/customer identifier
+
+   Example:
+            sercmd cnxcc.check_client john-doe-123-premium
+
+5.3. cnxcc.kill_call
+
+   Kills an active call using its call ID.
+
+   Parameters: Call-ID
+
+   Example:
+            sercmd cnxcc.kill_call [email protected]
+
+6. Events
+
+   When a call is forced to end an event route is automatically invoked.
+   This route is suited with a fake OPTIONS message containing the call
+   ID, ftag and ttag of the original call so it can be located somehow in
+   the accounting database.
+
+   Example:
+...
+event_route[cnxcc:call-shutdown]
+{
+        xlog("L_INFO", "[$ci]: call killed");
+
+        # perform some kind of notification, database update, email sending, etc
+.
+}
+...
+
+7. Web Interface
+
+   The module contains a web management interface completely optional.
+   With it, you can review your calls in real time and hang them up if
+   necessary.
+
+   Link: https://github.com/caruizdiaz/cnxcc-web
+
+8. Sample
+
+   Example 1.5. kamailio-cnxcc.cfg
+...
+route[CNXCC]
+{
+        $var(client)              = "test-client-0-123-01";
+        $var(credit)              = "50";
+        $var(cost_per_sec)        = "0.5";
+        $var(i_pulse)             = "30";
+        $var(f_pulse)             = "6";
+
+        if (!cnxcc_set_max_credit("$var(client)",
+                          "$var(credit)",
+                          "$var(cost_per_sec)",
+                          "$var(i_pulse)",
+                          "$var(f_pulse)")) {
+                 xlog("Error setting up credit control");
+        }
+}
+
+event_route[cnxcc:call-shutdown]
+{
+        xlog("L_INFO", "[$ci]: call killed");
+
+
+}
+...

+ 63 - 0
modules/cnxcc/cnxcc.c

@@ -0,0 +1,63 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Carlos Ruiz Díaz (caruizdiaz.com),
+ *                    ConexionGroup (www.conexiongroup.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+#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];
+
+	buffer[string->len]	= '\0';
+	memcpy(buffer, string->s, string->len);
+
+	return atof(buffer);
+}

+ 39 - 0
modules/cnxcc/cnxcc.h

@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Carlos Ruiz Díaz (caruizdiaz.com),
+ *                    ConexionGroup (www.conexiongroup.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef _CNXCC_H
+#define _CNXCC_H
+
+#include "../../str.h"
+
+#define DATETIME_SIZE		sizeof("0001-01-01 00:00:00")
+#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);
+double str2double(str *string);
+
+#endif /* _CNXCC_H */

+ 176 - 0
modules/cnxcc/cnxcc_check.c

@@ -0,0 +1,176 @@
+/*
+ * cnxcc_check.c
+ *
+ *  Created on: Dec 10, 2012
+ *      Author: carlos
+ */
+
+#include <stdio.h>
+
+#include "../../locking.h"
+#include "../../lock_ops.h"
+
+#include "cnxcc_mod.h"
+#include "cnxcc.h"
+#include "cnxcc_check.h"
+
+extern data_t _data;
+
+void check_calls_by_money(unsigned int ticks, void *param)
+{
+	struct str_hash_entry *h_entry 	= NULL,
+						  *tmp		= NULL;
+	call_t *tmp_call				= NULL;
+	int i;
+
+	lock_get(&_data.money.lock);
+
+	if (_data.money.credit_data_by_client->table)
+		for(i = 0; i < _data.money.credit_data_by_client->size; i++)
+			clist_foreach_safe(&_data.money.credit_data_by_client->table[i], h_entry, tmp, next)
+			{
+				credit_data_t *credit_data	= (credit_data_t *) h_entry->u.p;
+				call_t *call				= NULL;
+				double total_consumed_money	= 0;
+
+				if (i > SAFE_ITERATION_THRESHOLD)
+				{
+					LM_ERR("Too many iterations for this loop: %d", i);
+					break;
+				}
+
+				lock_get(&credit_data->lock);
+
+				clist_foreach_safe(credit_data->call_list, call, tmp_call, next)
+				{
+					int consumed_time = 0;
+
+					if (!call->confirmed)
+						continue;
+
+					consumed_time 				= get_current_timestamp() - call->start_timestamp;
+
+					if (consumed_time > call->money_based.initial_pulse)
+					{
+						call->consumed_amount = (call->money_based.cost_per_second * call->money_based.initial_pulse)
+												+
+												call->money_based.cost_per_second *
+												( (consumed_time - call->money_based.initial_pulse) / call->money_based.final_pulse + 1 ) *
+												call->money_based.final_pulse;
+					}
+
+					total_consumed_money	+= call->consumed_amount;
+
+					if (call->consumed_amount > call->max_amount)
+					{
+						LM_ALERT("[%.*s] call has exhausted its credit. Breaking the loop\n", call->sip_data.callid.len, call->sip_data.callid.s);
+						break;
+					}
+
+					LM_DBG("CID [%.*s], start_timestamp [%d], seconds alive [%d], consumed credit [%f]\n",
+																			call->sip_data.callid.len, call->sip_data.callid.s,
+																			call->start_timestamp,
+																			consumed_time,
+																			call->consumed_amount
+																			);
+				}
+
+				if (credit_data->concurrent_calls == 0)
+				{
+					lock_release(&credit_data->lock);
+					continue;
+				}
+
+				credit_data->consumed_amount	= credit_data->ended_calls_consumed_amount + total_consumed_money;
+
+				LM_DBG("Client [%.*s] | Ended-Calls-Credit-Spent: %f  TotalCredit/MaxCredit: %f/%f\n", credit_data->call_list->client_id.len, credit_data->call_list->client_id.s,
+																									credit_data->ended_calls_consumed_amount,
+																									credit_data->consumed_amount,
+																									credit_data->max_amount);
+
+				if (credit_data->consumed_amount >= credit_data->max_amount)
+				{
+					terminate_all_calls(credit_data);
+					lock_release(&credit_data->lock);
+					break;
+				}
+
+				lock_release(&credit_data->lock);
+			}
+
+	lock_release(&_data.money.lock);
+}
+
+void check_calls_by_time(unsigned int ticks, void *param)
+{
+	struct str_hash_entry *h_entry 	= NULL,
+						  *tmp		= NULL;
+	call_t *tmp_call				= NULL;
+	int i;
+
+	lock_get(&_data.time.lock);
+
+	if (_data.time.credit_data_by_client->table)
+		for(i = 0; i < _data.time.credit_data_by_client->size; i++)
+			clist_foreach_safe(&_data.time.credit_data_by_client->table[i], h_entry, tmp, next)
+			{
+				credit_data_t *credit_data	= (credit_data_t *) h_entry->u.p;
+				call_t *call				= NULL;
+				int total_consumed_secs		= 0;
+
+				lock_get(&credit_data->lock);
+
+				if (i > SAFE_ITERATION_THRESHOLD)
+				{
+					LM_ERR("Too many iterations for this loop: %d", i);
+					break;
+				}
+
+				LM_DBG("Iterating through calls of client [%.*s]\n", credit_data->call_list->client_id.len, credit_data->call_list->client_id.s);
+
+				clist_foreach_safe(credit_data->call_list, call, tmp_call, next)
+				{
+					if (!call->confirmed)
+						continue;
+
+					call->consumed_amount		= get_current_timestamp() - call->start_timestamp;
+					total_consumed_secs			+= call->consumed_amount;
+
+					if (call->consumed_amount > call->max_amount)
+					{
+						LM_ALERT("[%.*s] call has exhausted its time. Breaking the loop\n", call->sip_data.callid.len, call->sip_data.callid.s);
+						break;
+					}
+
+					LM_DBG("CID [%.*s], start_timestamp [%d], seconds alive [%d]\n",
+																			call->sip_data.callid.len, call->sip_data.callid.s,
+																			call->start_timestamp,
+																			(int) call->consumed_amount
+																			);
+				}
+
+				if (credit_data->concurrent_calls == 0)
+				{
+					lock_release(&credit_data->lock);
+					continue;
+				}
+
+				credit_data->consumed_amount	= credit_data->ended_calls_consumed_amount + total_consumed_secs;
+
+				LM_DBG("Client [%.*s] | Ended-Calls-Time: %d  TotalTime/MaxTime: %d/%d\n", credit_data->call_list->client_id.len, credit_data->call_list->client_id.s,
+																									(int) credit_data->ended_calls_consumed_amount,
+																									(int) credit_data->consumed_amount,
+																									(int) credit_data->max_amount);
+
+				if (credit_data->consumed_amount >= credit_data->max_amount)
+				{
+					terminate_all_calls(credit_data);
+					lock_release(&credit_data->lock);
+					break;
+				}
+
+				lock_release(&credit_data->lock);
+			}
+
+	lock_release(&_data.time.lock);
+}

+ 16 - 0
modules/cnxcc/cnxcc_check.h

@@ -0,0 +1,16 @@
+/*
+ * cnxcc_check.h
+ *
+ *  Created on: Dec 10, 2012
+ *      Author: carlos
+ */
+
+#ifndef CNXCC_CHECK_H_
+#define CNXCC_CHECK_H_
+
+#define SAFE_ITERATION_THRESHOLD 5000
+
+void check_calls_by_time(unsigned int ticks, void *param);
+void check_calls_by_money(unsigned int ticks, void *param);
+
+#endif /* CNXCC_CHECK_H_ */

+ 1526 - 0
modules/cnxcc/cnxcc_mod.c

@@ -0,0 +1,1526 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Carlos Ruiz Díaz (caruizdiaz.com),
+ *                    ConexionGroup (www.conexiongroup.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "../../sr_module.h"
+#include "../../dprint.h"
+#include "../../error.h"
+#include "../../mem/mem.h"
+#include "../../shm_init.h"
+#include "../../mem/shm_mem.h"
+#include "../../pvar.h"
+#include "../../locking.h"
+#include "../../lock_ops.h"
+#include "../../str_hash.h"
+//#include "../../timer.h"
+#include "../../timer_proc.h"
+#include "../../modules/tm/tm_load.h"
+#include "../../parser/parse_from.h"
+#include "../../parser/parse_to.h"
+#include "../../parser/parse_uri.h"
+#include "../../parser/parse_cseq.h"
+#include "../../parser/contact/parse_contact.h"
+#include "../../parser/contact/contact.h"
+#include "../../parser/parse_rr.h"
+//#include "../../lib/kcore/parser_helpers.h"
+#include "../../mod_fix.h"
+#include "../dialog/dlg_load.h"
+#include "../dialog/dlg_hash.h"
+#include "../../mi/mi_types.h"
+#include "../../lib/kcore/faked_msg.h"
+#include "../../rpc.h"
+#include "../../rpc_lookup.h"
+
+#include "cnxcc_mod.h"
+#include "cnxcc.h"
+#include "cnxcc_sip_msg_faker.h"
+#include "cnxcc_check.h"
+#include "cnxcc_rpc.h"
+
+MODULE_VERSION
+
+#define HT_SIZE						229
+#define MODULE_NAME					"CNXCC"
+#define NUMBER_OF_TIMERS			2
+
+#define TRUE						1
+#define FALSE						0
+
+data_t _data;
+struct dlg_binds _dlgbinds;
+
+static int fixup_par(void** param, int param_no);
+
+/*
+ *  module core functions
+ */
+static int mod_init(void);
+static int child_init(int);
+static int init_hashtable(struct str_hash_table *ht);
+
+/*
+ * Memory management functions
+ */
+static int shm_str_hash_alloc(struct str_hash_table *ht, int size);
+static void free_credit_data_hash_entry(struct str_hash_entry *e);
+
+/*
+ * PV management functions
+ */
+static int pv_parse_calls_param(pv_spec_p sp, str *in);
+static int pv_get_calls(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
+//static int get_str_pv(struct sip_msg* msg, str *pv_name, str *pvvalue);
+
+/*
+ * Billing management functions
+ */
+static int set_max_time(struct sip_msg* msg, char* number, char* str2);
+static int set_max_credit(struct sip_msg* msg, char *str_pv_client, char *str_pv_credit, char *str_pv_cps, char *str_pv_inip, char *str_pv_finp);
+static void start_billing(str *callid, str tags[2]);
+static void setup_billing(str *callid, unsigned int h_entry, unsigned int h_id);
+static void stop_billing(str *callid);
+static int add_call_by_cid(str *cid, call_t *call, credit_type_t type);
+static credit_data_t *get_or_create_credit_data_entry(str *client_id, credit_type_t type);
+static call_t *alloc_new_call_by_time(credit_data_t *credit_data, struct sip_msg *msg, int max_secs);
+static call_t *alloc_new_call_by_money(credit_data_t *credit_data, struct sip_msg *msg, double credit, double cost_per_second, int initial_pulse, int final_pulse);
+static void notify_call_termination(str *callid, str *from_tag, str *to_tag);
+static void free_call(call_t *call);
+static int has_to_tag(struct sip_msg *msg);
+
+/*
+ * MI interface
+ */
+static struct mi_root *mi_credit_control_stats(struct mi_root *tree, void *param);
+
+/*
+ * Dialog management callback functions
+ */
+static void dialog_terminated_callback(struct dlg_cell *cell, int type, struct dlg_cb_params *params);
+static void dialog_confirmed_callback(struct dlg_cell *cell, int type, struct dlg_cb_params *params);
+static void dialog_created_callback(struct dlg_cell *cell, int type, struct dlg_cb_params *params);
+
+static pv_export_t mod_pvs[] =
+{
+	{ {"cnxcc", sizeof("cnxcc")-1 }, PVT_OTHER, pv_get_calls, 0,
+		                pv_parse_calls_param, 0, 0, 0 },
+	{ {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
+};
+
+static cmd_export_t cmds[] =
+{
+	{"cnxcc_set_max_time",   (cmd_function) set_max_time, 2, fixup_pvar_pvar, fixup_free_pvar_pvar, ANY_ROUTE},
+	{"cnxcc_set_max_credit",   (cmd_function) set_max_credit, 5, fixup_par, NULL, ANY_ROUTE},
+	{0,0,0,0,0,0}
+};
+
+static param_export_t params[] =
+{
+	{"dlg_flag",  				INT_PARAM,			&_data.ctrl_flag	},
+	{"credit_check_period",  	INT_PARAM,			&_data.check_period	},
+	{ 0, 0, 0 }
+};
+
+static const char* rpc_active_clients_doc[2] =
+{
+	"List of clients with active calls",
+	0
+};
+
+static const char* rpc_check_client_stats_doc[2] =
+{
+	"Check specific client calls",
+	0
+};
+
+static const char* rpc_kill_call_doc[2] =
+{
+	"Kill call using its call ID",
+	0
+};
+
+rpc_export_t ul_rpc[] =
+{
+    {"cnxcc.active_clients",	rpc_active_clients,	rpc_active_clients_doc,	0},
+    {"cnxcc.check_client",		rpc_check_client_stats,	rpc_check_client_stats_doc,	0},
+    {"cnxcc.kill_call",			rpc_kill_call,	rpc_kill_call_doc,	0},
+    {0, 0, 0, 0}
+};
+
+/** module exports */
+struct module_exports exports =
+{
+	"cnxcc",
+	DEFAULT_DLFLAGS, 	/* dlopen flags */
+	cmds,
+	params,
+	0,          		/* exported statistics */
+	0, 		    		/* exported MI functions */
+	mod_pvs,  			/* exported pseudo-variables */
+	0,          		/* extra processes */
+	mod_init,   		/* module initialization function */
+	0,
+	0,
+	child_init          /* per-child init function */
+};
+
+static int fixup_par(void** param, int param_no)
+{
+	str var;
+
+	var.s	= (char *) *param;
+	var.len = strlen(var.s);
+
+	if (fixup_pvar_null(param, 1))
+	{
+		LM_ERR("Invalid PV [%.*s] as parameter\n", var.len, var.s);
+		return E_CFG;
+	}
+/*
+	if (((pv_spec_t*)(*param))->setf == NULL)
+	{
+		LM_ERR("[%.*s] has to be writable\n", var.len, var.s);
+		return E_CFG;
+	} */
+
+	return 0;
+}
+
+static int mod_init(void)
+{
+	LM_ALERT("Loading " MODULE_NAME " module\n");
+
+	_data.cs_route_number = route_get(&event_rt, "cnxcc:call-shutdown");
+
+	if (_data.cs_route_number < 0)
+		LM_INFO("No cnxcc:call-shutdown event route found");
+
+	if (_data.cs_route_number > 0 && event_rt.rlist[_data.cs_route_number] == NULL)
+	{
+		LM_INFO("cnxcc:call-shutdown route is empty");
+		_data.cs_route_number	= -1;
+	}
+
+	if (_data.check_period <= 0)
+	{
+		LM_INFO("credit_check_period cannot be less than 1 second");
+		return -1;
+	}
+
+	_data.time.credit_data_by_client	= shm_malloc(sizeof(struct str_hash_table));
+	_data.time.call_data_by_cid 		= shm_malloc(sizeof(struct str_hash_table));
+	_data.money.credit_data_by_client	= shm_malloc(sizeof(struct str_hash_table));
+	_data.money.call_data_by_cid 		= shm_malloc(sizeof(struct str_hash_table));
+
+	_data.stats							= (stats_t *) shm_malloc(sizeof(stats_t));
+
+	if (!_data.stats)
+	{
+		LM_ERR("Error allocating shared memory stats\n");
+		return -1;
+	}
+
+	_data.stats->active		= 0;
+	_data.stats->dropped	= 0;
+	_data.stats->total		= 0;
+
+	if (init_hashtable(_data.time.credit_data_by_client) != 0)
+		return -1;
+
+	if (init_hashtable(_data.time.call_data_by_cid) != 0)
+		return -1;
+
+	if (init_hashtable(_data.money.credit_data_by_client) != 0)
+		return -1;
+
+	if (init_hashtable(_data.money.call_data_by_cid) != 0)
+		return -1;
+
+	lock_init(&_data.lock);
+	lock_init(&_data.time.lock);
+	lock_init(&_data.money.lock);
+
+	register_mi_cmd(mi_credit_control_stats, "cnxcc_stats", NULL, NULL, 0);
+
+	/*
+	 * One for time based monitoring
+	 * One for money based monitoring
+	 */
+	register_dummy_timers(NUMBER_OF_TIMERS);
+
+	if (rpc_register_array(ul_rpc) != 0)
+	{
+		LM_ERR("Failed registering RPC commands\n");
+		return -1;
+	}
+
+	if (load_dlg_api(&_dlgbinds) != 0)
+	{
+		LM_ERR("Error loading dialog API\n");
+	    return -1;
+	}
+
+	_dlgbinds.register_dlgcb(NULL, DLGCB_CREATED, dialog_created_callback, NULL, NULL);
+
+	return 0;
+}
+
+static int child_init(int rank)
+{
+	if (rank != PROC_MAIN)
+		return 0;
+
+
+	if(fork_dummy_timer(PROC_TIMER, "CNXCC TB TIMER", 1,
+			check_calls_by_money, NULL, _data.check_period) < 0)
+	{
+		LM_ERR("failed to register TB TIMER routine as process\n");
+		return -1;
+	}
+
+	if(fork_dummy_timer(PROC_TIMER, "CNXCC MB TIMER", 1,
+								check_calls_by_time, NULL, _data.check_period) < 0)
+	{
+		LM_ERR("failed to register MB TIMER routine as process\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int init_hashtable(struct str_hash_table *ht)
+{
+	if (shm_str_hash_alloc(ht, HT_SIZE) != 0)
+	{
+		LM_ERR("Error allocating shared memory hashtable\n");
+		return -1;
+	}
+
+	str_hash_init(ht);
+
+	return 0;
+}
+
+static void dialog_created_callback(struct dlg_cell *cell, int type, struct dlg_cb_params *params)
+{
+	struct sip_msg *msg	= NULL;
+
+	msg	= params->direction == SIP_REPLY ? params->rpl : params->req;
+
+	if (msg == NULL)
+	{
+		LM_ERR("Error getting direction of SIP msg\n");
+		return;
+	}
+
+	if (isflagset(msg, _data.ctrl_flag) == -1)
+	{
+		LM_DBG("Flag is not set for this message. Ignoring\n");
+		return;
+	}
+
+	LM_DBG("Dialog created for CID [%.*s]", cell->callid.len, cell->callid.s);
+
+	_dlgbinds.register_dlgcb(cell, DLGCB_CONFIRMED, dialog_confirmed_callback, NULL, NULL);
+	_dlgbinds.register_dlgcb(cell, DLGCB_TERMINATED|DLGCB_FAILED|DLGCB_EXPIRED, dialog_terminated_callback, NULL, NULL);
+
+	setup_billing(&cell->callid, cell->h_entry, cell->h_id);
+}
+
+static void dialog_confirmed_callback(struct dlg_cell *cell, int type, struct dlg_cb_params *params)
+{
+	LM_DBG("Dialog confirmed for CID [%.*s]", cell->callid.len, cell->callid.s);
+
+	start_billing(&cell->callid, cell->tag);
+}
+
+static void dialog_terminated_callback(struct dlg_cell *cell, int type, struct dlg_cb_params *params)
+{
+	LM_DBG("Dialog terminated for CID [%.*s]", cell->callid.len, cell->callid.s);
+
+	stop_billing(&cell->callid);
+}
+
+static void notify_call_termination(str *callid, str *from_tag, str *to_tag)
+{
+	struct run_act_ctx ra_ctx;
+	struct sip_msg *msg;
+
+	if (_data.cs_route_number < 0)
+		return;
+
+	if (faked_msg_init_with_dlg_info(callid, from_tag, to_tag,  &msg) != 0)
+	{
+		LM_ERR("[%.*s]: error generating faked sip message\n", callid->len, callid->s);
+		return;
+	}
+
+	init_run_actions_ctx(&ra_ctx);
+	//run_top_route(event_rt.rlist[_data.cs_route_number], msg, &ra_ctx);
+
+	if (run_actions(&ra_ctx, event_rt.rlist[_data.cs_route_number], msg) < 0)
+		LM_ERR("Error executing cnxcc:call-shutdown route");
+}
+
+int try_get_credit_data_entry(str *client_id, credit_data_t **credit_data)
+{
+	struct str_hash_entry *cd_entry	= NULL;
+	hash_tables_t *hts				= NULL;
+	*credit_data					= NULL;
+
+	hts					= &_data.money;
+	lock_get(&hts->lock);
+
+	cd_entry			= str_hash_get(hts->credit_data_by_client, client_id->s, client_id->len);
+
+	if (cd_entry != NULL)
+	{
+		*credit_data	= cd_entry->u.p;
+		lock_release(&hts->lock);
+		return 0;
+	}
+
+	lock_release(&hts->lock);
+
+	hts					= &_data.time;
+	lock_get(&hts->lock);
+
+	cd_entry			= str_hash_get(hts->call_data_by_cid, client_id->s, client_id->len);
+
+	if (cd_entry != NULL)
+	{
+		*credit_data	= cd_entry->u.p;
+		lock_release(&hts->lock);
+		return 0;
+	}
+
+	lock_release(&hts->lock);
+
+	return -1;
+}
+
+int try_get_call_entry(str *callid, call_t **call, hash_tables_t **hts)
+{
+	struct str_hash_entry *call_entry	= NULL;
+
+	*call					= NULL;
+
+	*hts					= &_data.money;
+	lock_get(&(*hts)->lock);
+
+	call_entry			= str_hash_get((*hts)->call_data_by_cid, callid->s, callid->len);
+
+	if (call_entry != NULL)
+	{
+		*call	= call_entry->u.p;
+		lock_release(&(*hts)->lock);
+		return 0;
+	}
+
+	lock_release(&(*hts)->lock);
+
+	*hts				= &_data.time;
+	lock_get(&(*hts)->lock);
+
+	call_entry			= str_hash_get((*hts)->call_data_by_cid, callid->s, callid->len);
+
+	if (call_entry != NULL)
+	{
+		*call	= call_entry->u.p;
+		lock_release(&(*hts)->lock);
+		return 0;
+	}
+
+	lock_release(&(*hts)->lock);
+
+	return -1;
+}
+
+static void stop_billing(str *callid)
+{
+	struct str_hash_entry *cd_entry		= NULL;
+	call_t *call						= NULL;
+	hash_tables_t *hts					= NULL;
+	credit_data_t *credit_data			= NULL;
+
+	/*
+	 * Search call data by call-id
+	 */
+	if (try_get_call_entry(callid, &call, &hts) != 0)
+	{
+		LM_ERR("Call [%.*s] not found", callid->len, callid->s);
+		return;
+	}
+
+	if (call == NULL)
+	{
+		LM_ERR("[%.*s] call pointer is null", callid->len, callid->s);
+		return;
+	}
+
+	if (hts == NULL)
+	{
+		LM_ERR("[%.*s] result hashtable pointer is null", callid->len, callid->s);
+		return;
+	}
+
+	lock_get(&hts->lock);
+
+	/*
+	 * Search credit_data by client_id
+	 */
+	cd_entry			= str_hash_get(hts->credit_data_by_client, call->client_id.s, call->client_id.len);
+
+	if (cd_entry == NULL)
+	{
+		LM_ERR("Credit data not found for CID [%.*s], client-ID [%.*s]\n", callid->len, callid->s, call->client_id.len, call->client_id.s);
+		lock_release(&hts->lock);
+		return;
+	}
+
+	credit_data	= (credit_data_t *) cd_entry->u.p;
+
+	if (credit_data == NULL)
+	{
+		LM_ERR("[%.*s]: credit_data pointer is null", callid->len, callid->s);
+		lock_release(&hts->lock);
+		return;
+	}
+
+	lock_release(&hts->lock);
+
+	/*
+	 * Update calls statistics
+	 */
+	lock_get(&_data.lock);
+
+	_data.stats->active--;
+	_data.stats->total--;
+
+	lock_release(&_data.lock);
+
+	lock(&credit_data->lock);
+
+	LM_DBG("Call [%.*s] of client-ID [%.*s], ended\n", callid->len, callid->s, call->client_id.len, call->client_id.s);
+
+	/*
+	 * This call just ended and we need to remove it from the summ.
+	 */
+	if (call->confirmed)
+	{
+		credit_data->concurrent_calls--;
+		credit_data->ended_calls_consumed_amount += call->consumed_amount;
+	}
+
+	credit_data->number_of_calls--;
+
+	if (credit_data->concurrent_calls < 0)
+	{
+		LM_ERR("[BUG]: number of concurrent calls dropped to negative value: %d", credit_data->concurrent_calls);
+	}
+
+	if (credit_data->number_of_calls < 0)
+	{
+		LM_ERR("[BUG]: number of calls dropped to negative value: %d", credit_data->number_of_calls);
+	}
+
+	/*
+	 * Remove (and free) the call from the list of calls of the current credit_data
+	 */
+	clist_rm(call, next, prev);
+	free_call(call);
+
+	/*
+	 * In case there are no active calls for a certain client, we remove the client-id from the hash table.
+	 * This way, we can save memory for useful clients.
+	 */
+	if (credit_data->number_of_calls == 0)
+	{
+		LM_DBG("Removing client [%.*s] and its calls from the list\n", credit_data->call_list->client_id.len, credit_data->call_list->client_id.s);
+
+		lock(&hts->lock);
+		/*
+		 * Remove the credit_data_t from the hash table
+		 */
+		str_hash_del(cd_entry);
+
+		lock_release(&hts->lock);
+
+		/*
+		 * Free client_id in list's root
+		 */
+		shm_free(credit_data->call_list->client_id.s);
+		shm_free(credit_data->call_list);
+
+		/*
+		 * Release the lock since we are going to free the entry down below
+		 */
+		lock_release(&credit_data->lock);
+
+		/*
+		 * Free the whole entry
+		 */
+		free_credit_data_hash_entry(cd_entry);
+
+		/*
+		 * return without releasing the acquired lock over credit_data. Why? Because we just freed it.
+		 */
+		return;
+	}
+
+	lock_release(&credit_data->lock);
+}
+
+static void setup_billing(str *callid, unsigned int h_entry, unsigned int h_id)
+{
+	call_t *call						= NULL;
+	hash_tables_t *hts					= NULL;
+
+	LM_DBG("Creating dialog for [%.*s], h_id [%u], h_entry [%u]", callid->len, callid->s, h_id, h_entry);
+
+//	lock_get(&_data.lock);
+
+	/*
+	 * Search call data by call-id
+	 */
+	if (try_get_call_entry(callid, &call, &hts) != 0)
+	{
+		LM_ERR("Call [%.*s] not found", callid->len, callid->s);
+		return;
+	}
+
+	if (call == NULL)
+	{
+		LM_ERR("[%.*s] call pointer is null", callid->len, callid->s);
+		return;
+	}
+
+	if (hts == NULL)
+	{
+		LM_ERR("[%.*s] result hashtable pointer is null", callid->len, callid->s);
+		return;
+	}
+
+	/*
+	 * Update calls statistics
+	 */
+	lock_get(&_data.lock);
+
+	_data.stats->active++;
+	_data.stats->total++;
+
+	lock_release(&_data.lock);
+
+	lock_get(&call->lock);
+
+	call->dlg_h_entry		= h_entry;
+	call->dlg_h_id			= h_id;
+
+	LM_DBG("Call [%.*s] from client [%.*s], created\n", callid->len, callid->s, call->client_id.len, call->client_id.s);
+
+	lock_release(&call->lock);
+}
+
+static void start_billing(str *callid, str tags[2])
+{
+	struct str_hash_entry *cd_entry		= NULL;
+	call_t *call						= NULL;
+	hash_tables_t *hts					= NULL;
+	credit_data_t *credit_data			= NULL;
+
+	LM_DBG("Billing started for call [%.*s]", callid->len, callid->s);
+
+//	lock_get(&_data.lock);
+
+	/*
+	 * Search call data by call-id
+	 */
+	if (try_get_call_entry(callid, &call, &hts) != 0)
+	{
+		LM_ERR("Call [%.*s] not found", callid->len, callid->s);
+		return;
+	}
+
+	if (call == NULL)
+	{
+		LM_ERR("[%.*s] call pointer is null", callid->len, callid->s);
+		return;
+	}
+
+	if (hts == NULL)
+	{
+		LM_ERR("[%.*s] result hashtable pointer is null", callid->len, callid->s);
+		return;
+	}
+
+	lock_get(&hts->lock);
+
+	/*
+	 * Search credit_data by client_id
+	 */
+	cd_entry			= str_hash_get(hts->credit_data_by_client, call->client_id.s, call->client_id.len);
+
+	if (cd_entry == NULL)
+	{
+		LM_ERR("Credit data not found for CID [%.*s], client-ID [%.*s]\n", callid->len, callid->s, call->client_id.len, call->client_id.s);
+		lock_release(&hts->lock);
+		return;
+	}
+
+	credit_data	= (credit_data_t *) cd_entry->u.p;
+
+	if (credit_data == NULL)
+	{
+		LM_ERR("[%.*s]: credit_data pointer is null", callid->len, callid->s);
+		lock_release(&hts->lock);
+		return;
+	}
+
+	lock_release(&hts->lock);
+
+	lock(&credit_data->lock);
+
+	/*
+	 * Now that the call is confirmed, we can increase the count of "concurrent_calls".
+	 * This will impact in the discount rate performed by the check_calls() function.
+	 *
+	 */
+	credit_data->concurrent_calls++;
+
+	if (credit_data->max_amount == 0)
+		credit_data->max_amount	= call->max_amount; // first time setup
+
+	if (call->max_amount > credit_data->max_amount)
+	{
+		LM_ALERT("Maximum-speak-time/credit changed, maybe a credit reload? %f > %f. Client [%.*s]", call->max_amount, credit_data->max_amount,
+																							call->client_id.len, call->client_id.s);
+
+		credit_data->max_amount += call->max_amount - credit_data->max_amount;
+	}
+
+	/*
+	 * Update max_amount, discounting what was already consumed by other calls of the same client
+	 */
+
+	call->max_amount = credit_data->max_amount - credit_data->consumed_amount;
+
+	lock_release(&credit_data->lock);
+
+	lock_get(&call->lock);
+
+	/*
+	 * Store from-tag value
+	 */
+	if (shm_str_dup(&call->sip_data.from_tag, &tags[0]) != 0)
+	{
+		LM_ERR("No more pkg memory\n");
+		goto exit;
+	}
+
+	/*
+	 * Store to-tag value
+	 */
+	if (shm_str_dup(&call->sip_data.to_tag, &tags[1]) != 0)
+	{
+		LM_ERR("No more pkg memory\n");
+		goto exit;
+	}
+
+	call->start_timestamp	= get_current_timestamp();
+	call->confirmed			= TRUE;
+
+	LM_DBG("Call [%.*s] from client [%.*s], confirmed\n", callid->len, callid->s, call->client_id.len, call->client_id.s);
+
+exit:
+	lock_release(&call->lock);
+}
+
+
+void terminate_all_calls(credit_data_t *credit_data)
+{
+	call_t 	*call 	= NULL,
+			*tmp 	= NULL;
+
+	clist_foreach_safe(credit_data->call_list, call, tmp, next)
+	{
+		LM_DBG("Killing call with CID [%.*s]\n", call->sip_data.callid.len, call->sip_data.callid.s);
+
+		/*
+		 * Update number of calls forced to end
+		 */
+		_data.stats->dropped++;
+
+		terminate_call(call);
+	}
+}
+
+/*
+ * WARNING: When calling this function, the proper lock should have been acquired
+ */
+static void free_call(call_t *call)
+{
+	struct str_hash_entry *e	= NULL;
+
+	LM_DBG("Freeing call [%.*s]\n", call->sip_data.callid.len, call->sip_data.callid.s);
+
+	e			= str_hash_get(_data.money.call_data_by_cid, call->sip_data.callid.s, call->sip_data.callid.len);
+
+	if (e == NULL)
+	{
+		e			= str_hash_get(_data.time.call_data_by_cid, call->sip_data.callid.s, call->sip_data.callid.len);
+
+		if (e == NULL)
+		{
+			LM_ERR("Call [%.*s] not found. Couldn't be able to free it from hashtable", call->sip_data.callid.len, call->sip_data.callid.s);
+			return;
+		}
+	}
+
+	str_hash_del(e);
+
+	shm_free(e->key.s);
+	shm_free(e);
+
+	str_shm_free_if_not_null(call->sip_data.callid);
+	str_shm_free_if_not_null(call->sip_data.to_tag);
+	str_shm_free_if_not_null(call->sip_data.from_tag);
+
+	shm_free(call);
+}
+
+/*
+ * WARNING: When calling this function, the proper lock should have been acquired
+ */
+static void free_credit_data_hash_entry(struct str_hash_entry *e)
+{
+	shm_free(e->key.s);
+//	shm_free(((credit_data_t *) e->u.p)->call);
+	shm_free(e->u.p);
+	shm_free(e);
+}
+
+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 credit_data_t *get_or_create_credit_data_entry(str *client_id, credit_type_t type)
+{
+	struct str_hash_table *ht	= type == CREDIT_MONEY ? _data.money.credit_data_by_client : _data.time.credit_data_by_client;
+	gen_lock_t *lock			= type == CREDIT_MONEY ? &_data.money.lock : &_data.time.lock;
+	struct str_hash_entry *e	= NULL;
+
+	lock_get(lock);
+	e							= str_hash_get(ht, client_id->s, client_id->len);
+	lock_release(lock);
+
+	/*
+	 * Alloc new call_array_t if it doesn't exist
+	 */
+	if (e != NULL)
+	{
+		LM_DBG("Found key %.*s in hash table\n", e->key.len, e->key.s);
+	}
+	else
+	{
+		credit_data_t *credit_data	= NULL;
+		e							= shm_malloc(sizeof(struct str_hash_entry));
+
+		if (e == NULL)
+		{
+			LM_ERR("No shared memory left\n");
+			return NULL;
+		}
+
+		if (shm_str_dup(&e->key, client_id) != 0)
+		{
+			LM_ERR("No shared memory left\n");
+			return NULL;
+		}
+
+		e->flags					= 0;
+		e->u.p						= (void *) shm_malloc(sizeof(credit_data_t));
+		credit_data					= (credit_data_t *) e->u.p;
+
+		lock_init(&credit_data->lock);
+
+		credit_data->call_list 		= shm_malloc(sizeof(call_t));
+
+		if (credit_data->call_list == NULL)
+		{
+			LM_ERR("No shared memory left\n");
+			return NULL;
+		}
+
+		credit_data->max_amount					= 0;
+		credit_data->concurrent_calls			= 0;
+		credit_data->consumed_amount			= 0;
+		credit_data->ended_calls_consumed_amount= 0;
+		credit_data->number_of_calls			= 0;
+
+		credit_data->type						= type;
+
+		/*
+		 * Copy the client_id value to the root of the calls list.
+		 * This will be used later to get the credit_data_t of the
+		 * call when it is being searched by call ID.
+		 */
+		if (shm_str_dup(&credit_data->call_list->client_id, client_id) != 0)
+		{
+			LM_ERR("No shared memory left\n");
+			return NULL;
+		}
+
+		clist_init(credit_data->call_list, next, prev);
+
+		lock_get(lock);
+		str_hash_add(ht, e);
+		lock_release(lock);
+
+		LM_DBG("Call didn't exist. Allocated new entry\n");
+	}
+
+	return (credit_data_t *) e->u.p;
+}
+
+int terminate_call(call_t *call)
+{
+	LM_DBG("Got kill signal for call [%.*s] client [%.*s] h_id [%u] h_entry [%u]. Dropping it now\n",
+						call->sip_data.callid.len,
+						call->sip_data.callid.s,
+						call->client_id.len,
+						call->client_id.s,
+						call->dlg_h_id,
+						call->dlg_h_entry);
+
+	struct mi_root *root, *result	= NULL;
+	struct mi_node *node, *node1	= NULL;
+	struct mi_cmd *end_dlg_cmd		= NULL;
+
+	root	= init_mi_tree(0, 0, 0);
+	if (root == NULL)
+	{
+		LM_ERR("Error initializing tree to terminate call\n");
+		goto error;
+	}
+
+	node	= &root->node;
+
+	node1	= addf_mi_node_child(node, MI_DUP_VALUE, MI_SSTR("h_entry"), "%u", call->dlg_h_entry);
+	if (node1 == NULL)
+	{
+		LM_ERR("Error initializing h_entry node to terminate call\n");
+		goto error;
+	}
+
+	node1	= addf_mi_node_child(node, MI_DUP_VALUE, MI_SSTR("h_id"), "%u", call->dlg_h_id);
+	if (node1 == NULL)
+	{
+		LM_ERR("Error initializing dlg_h_id node to terminate call\n");
+		goto error;
+	}
+
+	end_dlg_cmd = lookup_mi_cmd(MI_SSTR("dlg_end_dlg"));
+	if (node == NULL)
+	{
+		LM_ERR("Error initializing dlg_end_dlg command\n");
+		goto error;
+	}
+
+	result		= run_mi_cmd(end_dlg_cmd, root);
+	if (result == NULL)
+	{
+		LM_ERR("Error executing dlg_end_dlg command\n");
+		goto error;
+	}
+
+	if (result->code == 200)
+	{
+		LM_DBG("dlg_end_dlg sent to call [%.*s]\n", call->sip_data.callid.len, call->sip_data.callid.s);
+		free_mi_tree(root);
+		free_mi_tree(result);
+
+		notify_call_termination(&call->sip_data.callid, &call->sip_data.from_tag, &call->sip_data.to_tag);
+
+		return 0;
+	}
+
+	LM_ERR("Error executing dlg_end_dlg command. Return code was [%d]\n", result->code);
+error:
+	if (root)
+		free_mi_tree(root);
+
+	return -1;
+}
+
+static call_t *alloc_new_call_by_money(credit_data_t *credit_data,
+										struct sip_msg *msg, double credit, double cost_per_second, int initial_pulse, int final_pulse)
+{
+	call_t *call		= NULL;
+
+	lock_get(&credit_data->lock);
+
+	if (credit_data->call_list == NULL)
+	{
+		LM_ERR("Credit data call list is NULL\n");
+		goto error;
+	}
+
+	call 				= shm_malloc(sizeof(call_t));
+	if (call == NULL)
+	{
+		LM_ERR("No shared memory left\n");
+		goto error;
+	}
+
+	if ( (!msg->callid && parse_headers(msg, HDR_CALLID_F, 0) != 0) ||
+		   shm_str_dup(&call->sip_data.callid, &msg->callid->body) != 0 )
+	{
+		LM_ERR("Error processing CALLID hdr\n");
+		goto error;
+	}
+
+	call->sip_data.to_tag.s		= NULL;
+	call->sip_data.to_tag.len 	= 0;
+	call->sip_data.from_tag.s	= NULL;
+	call->sip_data.from_tag.len = 0;
+
+	call->consumed_amount		= initial_pulse * cost_per_second;
+	call->confirmed				= FALSE;
+	call->max_amount			= credit;
+
+	call->money_based.cost_per_second	= cost_per_second;
+	call->money_based.initial_pulse		= initial_pulse;
+	call->money_based.final_pulse		= final_pulse;
+
+	/*
+	 * Reference the client_id from the root of the list
+	 */
+	call->client_id.s			= credit_data->call_list->client_id.s;
+	call->client_id.len			= credit_data->call_list->client_id.len;
+
+	/*
+	 * Insert the newly created call to the list of calls
+	 */
+	clist_insert(credit_data->call_list, call, next, prev);
+
+	lock_init(&call->lock);
+
+	/*
+	 * Increase the number of calls for this client. This call is not yet confirmed.
+	 */
+	credit_data->number_of_calls++;
+
+	lock_release(&credit_data->lock);
+
+	LM_DBG("New call allocated for client [%.*s]\n", call->client_id.len, call->client_id.s);
+
+	return call;
+
+error:
+	lock_release(&credit_data->lock);
+	return NULL;
+}
+
+static call_t *alloc_new_call_by_time(credit_data_t *credit_data, struct sip_msg *msg, int max_secs)
+{
+	call_t *call		= NULL;
+
+	lock_get(&credit_data->lock);
+
+	if (credit_data->call_list == NULL)
+	{
+		LM_ERR("Credit data call list is NULL\n");
+		goto error;
+	}
+
+	call 				= shm_malloc(sizeof(call_t));
+	if (call == NULL)
+	{
+		LM_ERR("No shared memory left\n");
+		goto error;
+	}
+
+	if ( (!msg->callid && parse_headers(msg, HDR_CALLID_F, 0) != 0) ||
+		   shm_str_dup(&call->sip_data.callid, &msg->callid->body) != 0 )
+	{
+		LM_ERR("Error processing CALLID hdr\n");
+		goto error;
+	}
+
+	call->sip_data.to_tag.s		= NULL;
+	call->sip_data.to_tag.len 	= 0;
+	call->sip_data.from_tag.s	= NULL;
+	call->sip_data.from_tag.len = 0;
+
+	call->consumed_amount		= 0;
+	call->confirmed				= FALSE;
+	call->max_amount			= max_secs;
+
+	/*
+	 * Reference the client_id from the root of the list
+	 */
+	call->client_id.s			= credit_data->call_list->client_id.s;
+	call->client_id.len			= credit_data->call_list->client_id.len;
+
+	/*
+	 * Insert the newly created call to the list of calls
+	 */
+	clist_insert(credit_data->call_list, call, next, prev);
+
+	lock_init(&call->lock);
+
+	/*
+	 * Increase the number of calls for this client. This call is not yet confirmed.
+	 */
+	credit_data->number_of_calls++;
+
+	lock_release(&credit_data->lock);
+
+	LM_DBG("New call allocated for client [%.*s]\n", call->client_id.len, call->client_id.s);
+
+	return call;
+
+error:
+	lock_release(&credit_data->lock);
+	return NULL;
+}
+
+static int add_call_by_cid(str *cid, call_t *call, credit_type_t type)
+{
+	struct str_hash_entry *e	= NULL;
+	struct str_hash_table *ht	= type == CREDIT_MONEY ? _data.money.call_data_by_cid : _data.time.call_data_by_cid;
+	gen_lock_t *lock			= type == CREDIT_MONEY ? &_data.money.lock : &_data.time.lock;
+
+	e	= str_hash_get(ht, cid->s, cid->len);
+
+	if (e != NULL)
+	{
+		LM_DBG("e != NULL\n");
+
+		call_t *value	= (call_t *) e->u.p;
+
+		if (value == NULL)
+		{
+			LM_ERR("Value of CID [%.*s] is NULL\n", cid->len, cid->s);
+			return -1;
+		}
+
+		LM_WARN("value cid: len=%d | value [%.*s]", value->sip_data.callid.len, value->sip_data.callid.len, value->sip_data.callid.s);
+		LM_WARN("added cid: len=%d | value [%.*s]", cid->len, cid->len, cid->s);
+
+		if (value->sip_data.callid.len != cid->len ||
+			strncasecmp(value->sip_data.callid.s, cid->s, cid->len) != 0)
+		{
+			LM_ERR("Value of CID is [%.*s] and differs from value being added [%.*s]\n", cid->len, cid->s,
+																			value->sip_data.callid.len, value->sip_data.callid.s);
+			return -1;
+		}
+
+		LM_DBG("CID already present\n");
+
+		return 0;
+	}
+
+	e	= shm_malloc(sizeof(struct str_hash_entry));
+
+	if (e == NULL)
+	{
+		LM_ERR("No shared memory left\n");
+		return -1;
+	}
+
+	if (shm_str_dup(&e->key, cid) != 0)
+	{
+		LM_ERR("No shared memory left\n");
+		return -1;
+	}
+
+	e->u.p		= call;
+
+	lock_get(lock);
+	str_hash_add(ht, e);
+	lock_release(lock);
+
+	return 0;
+}
+
+static inline void set_ctrl_flag(struct sip_msg* msg)
+{
+	if (_data.ctrl_flag != -1)
+	{
+		LM_DBG("Flag set!\n");
+		setflag(msg, _data.ctrl_flag);
+	}
+}
+
+static inline int get_pv_value(struct sip_msg* msg, pv_spec_t* spec, pv_value_t* value)
+{
+	if (pv_get_spec_value(msg, spec, value) != 0)
+	{
+		LM_ERR("Can't get PV's value\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int set_max_credit(struct sip_msg* msg,
+							char *str_pv_client,
+							char *str_pv_credit, char *str_pv_cps,
+							char *str_pv_inip, char *str_pv_finp)
+{
+	credit_data_t *credit_data 	= NULL;
+	call_t *call				= NULL;
+
+	pv_spec_t *client_id_spec		= (pv_spec_t *) str_pv_client,
+			  *credit_spec			= (pv_spec_t *) str_pv_credit,
+			  *cps_spec				= (pv_spec_t *) str_pv_cps,
+			  *initial_pulse_spec	= (pv_spec_t *) str_pv_inip,
+			  *final_pulse_spec		= (pv_spec_t *) str_pv_finp;
+
+	pv_value_t client_id_val,
+				credit_val,
+				cps_val,
+				initial_pulse_val,
+				final_pulse_val;
+
+	double credit					= 0,
+		   cost_per_second			= 0;
+
+	unsigned int initial_pulse		= 0,
+			final_pulse				= 0;
+
+	if (msg->first_line.type == SIP_REQUEST && msg->first_line.u.request.method_value == METHOD_INVITE)
+	{
+		if (has_to_tag(msg))
+		{
+			LM_ERR("INVITE is a reINVITE\n");
+			return -1;
+		}
+
+		if (pv_get_spec_value(msg, client_id_spec, &client_id_val) != 0)
+		{
+			LM_ERR("Can't get client_id's value\n");
+			return -1;
+		}
+
+		if (pv_get_spec_value(msg, credit_spec, &credit_val) != 0)
+		{
+			LM_ERR("Can't get credit's value\n");
+			return -1;
+		}
+
+		credit	= str2double(&credit_val.rs);
+
+		if (credit <= 0)
+		{
+			LM_ERR("credit value must be > 0: %f", credit);
+			return -1;
+		}
+
+		if (pv_get_spec_value(msg, cps_spec, &cps_val) != 0)
+		{
+			LM_ERR("Can't get cost_per_sec's value\n");
+			return -1;
+		}
+
+		cost_per_second	= str2double(&cps_val.rs);
+
+		if (cost_per_second <= 0)
+		{
+			LM_ERR("cost_per_second value must be > 0: %f", cost_per_second);
+			return -1;
+		}
+
+		if (pv_get_spec_value(msg, initial_pulse_spec, &initial_pulse_val) != 0)
+		{
+			LM_ERR("Can't get initial_pulse's value\n");
+			return -1;
+		}
+
+		if (str2int(&initial_pulse_val.rs, &initial_pulse) != 0)
+		{
+			LM_ERR("initial_pulse value is invalid: %.*s", initial_pulse_val.rs.len, initial_pulse_val.rs.s);
+			return -1;
+		}
+
+		if (pv_get_spec_value(msg, final_pulse_spec, &final_pulse_val) != 0)
+		{
+			LM_ERR("Can't get final_pulse's value\n");
+			return -1;
+		}
+
+		if (str2int(&final_pulse_val.rs, &final_pulse) != 0)
+		{
+			LM_ERR("final_pulse value is invalid: %.*s", final_pulse_val.rs.len, final_pulse_val.rs.s);
+			return -1;
+		}
+
+		if (client_id_val.rs.len == 0 || client_id_val.rs.s == NULL)
+		{
+			LM_ERR("[%.*s]: client ID cannot be null\n", msg->callid->body.len, msg->callid->body.s);
+			return -1;
+		}
+
+		LM_DBG("Setting up new call for client [%.*s], max-credit[%f], "
+				"cost-per-sec[%f], initial-pulse [%d], "
+				"final-pulse [%d], call-id[%.*s]\n", client_id_val.rs.len, client_id_val.rs.s,
+													 credit,
+													 cost_per_second, initial_pulse,
+													 final_pulse, msg->callid->body.len, msg->callid->body.s);
+		set_ctrl_flag(msg);
+
+		if ((credit_data = get_or_create_credit_data_entry(&client_id_val.rs, CREDIT_MONEY)) == NULL)
+		{
+			LM_ERR("Error retrieving credit data from shared memory for client [%.*s]\n", client_id_val.rs.len, client_id_val.rs.s);
+			return -1;
+		}
+
+		if ((call = alloc_new_call_by_money(credit_data, msg, credit, cost_per_second, initial_pulse, final_pulse)) == NULL)
+		{
+			LM_ERR("Unable to allocate new call for client [%.*s]\n", client_id_val.rs.len, client_id_val.rs.s);
+			return -1;
+		}
+
+		if (add_call_by_cid(&call->sip_data.callid, call, CREDIT_MONEY) != 0)
+		{
+			LM_ERR("Unable to allocate new cid_by_client for client [%.*s]\n", client_id_val.rs.len, client_id_val.rs.s);
+			return -1;
+		}
+	}
+	else
+	{
+		LM_ALERT("MSG was not a request\n");
+		return -1;
+	}
+
+	return 1;
+}
+
+static int set_max_time(struct sip_msg* msg, char* str_pv_client, char* str_pv_maxsecs)
+{
+	credit_data_t *credit_data 	= NULL;
+	call_t *call				= NULL;
+	pv_spec_t *max_secs_spec	= (pv_spec_t *) str_pv_maxsecs,
+			  *client_id_spec	= (pv_spec_t *) str_pv_client;
+	pv_value_t max_secs_val, client_id_val;
+	int max_secs				= 0;
+
+	set_ctrl_flag(msg);
+
+	if (parse_headers(msg, HDR_CALLID_F, 0) != 0)
+	{
+		LM_ERR("Error parsing Call-ID");
+		return -1;
+	}
+
+	if (msg->first_line.type == SIP_REQUEST && msg->first_line.u.request.method_value == METHOD_INVITE)
+	{
+		if (has_to_tag(msg))
+		{
+			LM_ERR("INVITE is a reINVITE\n");
+			return -1;
+		}
+
+		if (pv_get_spec_value(msg, max_secs_spec, &max_secs_val) != 0)
+		{
+			LM_ERR("Can't get max_secs PV value\n");
+			return -1;
+		}
+		max_secs	= max_secs_val.ri;
+
+		if (max_secs <= 0)
+		{
+			LM_ERR("[%.*s] MAXSECS cannot be less than or equal to zero: %d\n", msg->callid->body.len, msg->callid->body.s, max_secs);
+			return -1;
+		}
+
+		if (pv_get_spec_value(msg, client_id_spec, &client_id_val) != 0)
+		{
+			LM_ERR("[%.*s]: can't get client_id PV value\n", msg->callid->body.len, msg->callid->body.s);
+			return -1;
+		}
+
+		if (client_id_val.rs.len == 0 || client_id_val.rs.s == NULL)
+		{
+			LM_ERR("[%.*s]: client ID cannot be null\n", msg->callid->body.len, msg->callid->body.s);
+			return -1;
+		}
+
+		LM_DBG("Setting up new call for client [%.*s], max-secs[%d], call-id[%.*s]\n", client_id_val.rs.len, client_id_val.rs.s,
+																		max_secs,
+																		msg->callid->body.len, msg->callid->body.s);
+
+		if ((credit_data = get_or_create_credit_data_entry(&client_id_val.rs, CREDIT_TIME)) == NULL)
+		{
+			LM_ERR("Error retrieving credit data from shared memory for client [%.*s]\n", client_id_val.rs.len, client_id_val.rs.s);
+			return -1;
+		}
+
+		if ((call = alloc_new_call_by_time(credit_data, msg, max_secs)) == NULL)
+		{
+			LM_ERR("Unable to allocate new call for client [%.*s]\n", client_id_val.rs.len, client_id_val.rs.s);
+			return -1;
+		}
+
+		if (add_call_by_cid(&call->sip_data.callid, call, CREDIT_TIME) != 0)
+		{
+			LM_ERR("Unable to allocate new cid_by_client for client [%.*s]\n", client_id_val.rs.len, client_id_val.rs.s);
+			return -1;
+		}
+	}
+	else
+	{
+		LM_ALERT("MSG was not a request\n");
+		return -1;
+	}
+
+	return 1;
+}
+
+static int has_to_tag(struct sip_msg *msg)
+{
+	if (msg->to == NULL && parse_headers(msg, HDR_TO_F, 0) != 0)
+	{
+		LM_ERR("Cannot parse to-tag\n");
+		return 0;
+	}
+
+	return !(get_to(msg)->tag_value.s == NULL || get_to(msg)->tag_value.len == 0);
+}
+
+static int pv_parse_calls_param(pv_spec_p sp, str *in)
+{
+	if (sp == NULL || in == NULL || in->len == 0)
+		return -1;
+
+	switch(in->len)
+	{
+	case 5:
+		if (strncmp("total", in->s, in->len) == 0)
+			sp->pvp.pvn.u.isname.name.n	= CNX_PV_TOTAL;
+		else
+			return -1;
+		break;
+	case 6:
+		if (strncmp("active", in->s, in->len) == 0)
+			sp->pvp.pvn.u.isname.name.n	= CNX_PV_ACTIVE;
+		else
+			return -1;
+		break;
+	case 7:
+		if (strncmp("dropped", in->s, in->len) == 0)
+			sp->pvp.pvn.u.isname.name.n	= CNX_PV_DROPPED;
+		else
+			return -1;
+		break;
+
+	}
+
+	sp->pvp.pvn.type = PV_NAME_INTSTR;
+	sp->pvp.pvn.u.isname.type = 0;
+
+	return 0;
+}
+
+static int pv_get_calls(struct sip_msg *msg, pv_param_t *param, pv_value_t *res)
+{
+	switch(param->pvn.u.isname.name.n)
+	{
+	case CNX_PV_ACTIVE:
+		return pv_get_uintval(msg, param, res, _data.stats->active);
+	case CNX_PV_TOTAL:
+		return pv_get_uintval(msg, param, res, _data.stats->total);
+	case CNX_PV_DROPPED:
+		return pv_get_uintval(msg, param, res, _data.stats->dropped);
+	default:
+		LM_ERR("Unknown PV type %d\n", param->pvn.u.isname.name.n);
+		break;
+	}
+
+	return -1;
+}
+
+static struct mi_root *mi_credit_control_stats(struct mi_root *tree, void *param)
+{
+	char *p;
+	int len;
+	struct mi_root *rpl_tree;
+	struct mi_node *node, *node1;
+
+	rpl_tree	= init_mi_tree(200, "OK", 2);
+	node		= &rpl_tree->node;
+
+	node1 = add_mi_node_child(node, 0, MI_SSTR("CNX Credit Control"), 0, 0);
+	if (node1 == NULL)
+	{
+		LM_ERR("Error creating child node\n");
+		goto error;
+	}
+
+	p	= int2str((unsigned long) _data.stats->active, &len);
+	if (p == NULL)
+	{
+		LM_ERR("Error converting INT to STR\n");
+		goto error;
+	}
+	add_mi_node_child(node1, MI_DUP_VALUE, MI_SSTR("active"), p, len);
+
+	p	= int2str((unsigned long) _data.stats->dropped, &len);
+	if (p == NULL)
+	{
+		LM_ERR("Error converting INT to STR\n");
+		goto error;
+	}
+	add_mi_node_child(node1, MI_DUP_VALUE, MI_SSTR("dropped"), p, len);
+
+	p	= int2str((unsigned long) _data.stats->total, &len);
+	if (p == NULL)
+	{
+		LM_ERR("Error converting INT to STR\n");
+		goto error;
+	}
+
+	add_mi_node_child(node1, MI_DUP_VALUE, MI_SSTR("total"), p, len);
+
+	return rpl_tree;
+
+error:
+	return init_mi_tree(500, MI_INTERNAL_ERR, MI_INTERNAL_ERR_LEN);
+}

+ 156 - 0
modules/cnxcc/cnxcc_mod.h

@@ -0,0 +1,156 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Carlos Ruiz Díaz (caruizdiaz.com),
+ *                    ConexionGroup (www.conexiongroup.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#ifndef _CNXCC_MOD_H
+#define _CNXCC_MOD_H
+
+// 3,778
+#include "../../locking.h"
+#include "../../str_hash.h"
+#include "../../parser/parse_rr.h"
+
+#define str_shm_free_if_not_null(_var_) if (_var_.s != NULL)  { shm_free(_var_.s); _var_.s = NULL; _var_.len = 0; }
+
+typedef struct stats
+{
+	unsigned int total;
+	unsigned int active;
+	unsigned int dropped;
+} stats_t;
+
+typedef enum cnxpvtypes
+{
+	CNX_PV_ACTIVE = 1,
+	CNX_PV_TOTAL,
+	CNX_PV_DROPPED
+} cnxpvtypes_t;
+
+typedef enum credit_type
+{
+	CREDIT_TIME,
+	CREDIT_MONEY
+} credit_type_t;
+
+typedef struct hash_tables
+{
+	struct str_hash_table *credit_data_by_client;
+	struct str_hash_table *call_data_by_cid;
+
+	gen_lock_t lock;
+} hash_tables_t;
+
+typedef struct data
+{
+	gen_lock_t lock;
+
+	hash_tables_t time;
+	hash_tables_t money;
+
+	/*struct str_hash_table *credit_data_by_client;
+	struct str_hash_table *call_data_by_cid;*/
+
+	stats_t *stats;
+
+	/*
+	 * Call Shutdown Route Number
+	 */
+	int cs_route_number;
+
+	/*
+	 * Dialog flag used to track the call
+	 */
+	flag_t ctrl_flag;
+
+	int check_period;
+	int number_of_timers;
+
+} data_t;
+
+typedef struct sip_data
+{
+	str callid;
+	str to_tag;
+	str from_tag;
+} sip_data_t;
+
+typedef struct money_spec_data
+{
+	double cost_per_second;
+	int initial_pulse;
+	int final_pulse;
+
+} money_spec_data_t;
+
+struct call;
+typedef struct call
+{
+	struct call *prev;
+	struct call *next;
+
+	gen_lock_t lock;
+
+	char confirmed;
+	double max_amount;
+	money_spec_data_t money_based;
+
+	unsigned int start_timestamp;
+	double consumed_amount;
+
+	unsigned int dlg_h_entry;
+	unsigned int dlg_h_id;
+
+	str client_id;
+
+	sip_data_t sip_data;
+} call_t;
+
+typedef struct call_array
+{
+	call_t *array;
+	int length;
+
+} call_array_t;
+
+typedef struct credit_data
+{
+	gen_lock_t lock;
+
+	double max_amount;
+	double consumed_amount;
+	double ended_calls_consumed_amount;
+	int number_of_calls;
+	int concurrent_calls;
+
+	credit_type_t type;
+
+	call_t *call_list;
+
+} credit_data_t;
+
+
+int try_get_call_entry(str *callid, call_t **call, hash_tables_t **hts);
+int try_get_credit_data_entry(str *client_id, credit_data_t **credit_data);
+int terminate_call(call_t *call);
+void terminate_all_calls(credit_data_t *credit_data);
+
+#endif /* _CNXCC_MOD_H */

+ 269 - 0
modules/cnxcc/cnxcc_rpc.c

@@ -0,0 +1,269 @@
+/*
+ * cnxcc_rpc.c
+ *
+ *  Created on: Dec 6, 2012
+ *      Author: carlos
+ */
+
+#include <stdio.h>
+
+#include "../../locking.h"
+#include "../../lock_ops.h"
+#include "../../rpc.h"
+#include "../../rpc_lookup.h"
+
+#include "cnxcc_mod.h"
+
+extern data_t _data;
+
+void rpc_kill_call(rpc_t* rpc, void* ctx)
+{
+	call_t *call;
+	hash_tables_t *hts;
+	str callid;
+
+	if (!rpc->scan(ctx, "S", &callid))
+	{
+		LM_ERR("%s: error reading RPC param\n", __FUNCTION__);
+		return;
+	}
+
+	if (try_get_call_entry(&callid, &call, &hts) != 0)
+	{
+		LM_ERR("%s: call [%.*s] not found\n", __FUNCTION__, callid.len, callid.s);
+		rpc->fault(ctx, 404, "CallID Not Found");
+		return;
+	}
+
+	if (call == NULL)
+	{
+		LM_ERR("%s: call [%.*s] is in null state\n", __FUNCTION__, callid.len, callid.s);
+		rpc->fault(ctx, 500, "Call is NULL");
+		return;
+	}
+
+	LM_ALERT("Killing call [%.*s] via XMLRPC request\n", callid.len, callid.s);
+
+	lock_get(&call->lock);
+
+	terminate_call(call);
+
+	lock_release(&call->lock);
+}
+
+void rpc_check_client_stats(rpc_t* rpc, void* ctx)
+{
+	call_t *call, *tmp;
+	int index	= 0;
+	str client_id, rows;
+	char row_buffer[512];
+	credit_data_t *credit_data;
+
+	if (!rpc->scan(ctx, "S", &client_id))
+	{
+		LM_ERR("%s: error reading RPC param\n", __FUNCTION__);
+		return;
+	}
+
+	if (try_get_credit_data_entry(&client_id, &credit_data) != 0)
+	{
+		LM_ERR("%s: client [%.*s] not found\n", __FUNCTION__, client_id.len, client_id.s);
+		rpc->fault(ctx, 404, "Not Found");
+		return;
+	}
+
+	if (credit_data == NULL)
+	{
+		LM_ERR("%s: credit data for client [%.*s] is NULL\n", __FUNCTION__, client_id.len, client_id.s);
+		rpc->fault(ctx, 500, "Internal Server Error");
+		return;
+	}
+
+	lock_get(&credit_data->lock);
+
+	if (credit_data->number_of_calls <= 0)
+	{
+		lock_release(&credit_data->lock);
+		LM_INFO("No calls for current client\n");
+		return;
+	}
+
+	rows.len = 0;
+	rows.s	 = pkg_malloc(10);
+
+	if (rows.s == NULL)
+		goto nomem;
+
+	clist_foreach_safe(credit_data->call_list, call, tmp, next)
+	{
+		int row_len = 0;
+
+		memset(row_buffer, 0, sizeof(row_buffer));
+
+		if (credit_data->type == CREDIT_MONEY)
+			snprintf(row_buffer, sizeof(row_buffer), "id:%d,confirmed:%s,local_consumed_amount:%f,global_consumed_amount:%f,local_max_amount:%f,global_max_amount:%f,call_id:%.*s,start_timestamp:%d"
+																",inip:%d,finp:%d,cps:%f;",
+																 index,
+																 call->confirmed ? "yes" : "no",
+																 call->consumed_amount,
+																 credit_data->consumed_amount,
+																 call->max_amount,
+																 credit_data->max_amount,
+																 call->sip_data.callid.len, call->sip_data.callid.s,
+																 call->start_timestamp,
+																 call->money_based.initial_pulse,
+																 call->money_based.final_pulse,
+																 call->money_based.cost_per_second
+																 );
+		else
+			snprintf(row_buffer, sizeof(row_buffer), "id:%d,confirmed:%s,local_consumed_amount:%d,global_consumed_amount:%d,local_max_amount:%d,global_max_amount:%d,call_id:%.*s,start_timestamp:%d;",
+					 	 	 	 	 	 	 	 	 	 	 	 index,
+																 call->confirmed ? "yes" : "no",
+																 (int) call->consumed_amount,
+																 (int) credit_data->consumed_amount,
+																 (int) call->max_amount,
+																 (int) credit_data->max_amount,
+																 call->sip_data.callid.len, call->sip_data.callid.s,
+																 call->start_timestamp);
+
+		row_len 	= strlen(row_buffer);
+		rows.s		= pkg_realloc(rows.s, rows.len + row_len);
+
+		if (rows.s == NULL)
+		{
+			lock_release(&credit_data->lock);
+			goto nomem;
+		}
+
+		memcpy(rows.s + rows.len, row_buffer, row_len);
+		rows.len += row_len;
+
+		index++;
+	}
+
+	lock_release(&credit_data->lock);
+
+	if (rpc->add(ctx, "S", &rows) < 0)
+	{
+		LM_ERR("%s: error creating RPC struct\n", __FUNCTION__);
+	}
+
+	if (rows.s != NULL)
+		pkg_free(rows.s);
+
+	return;
+
+nomem:
+	LM_ERR("No more pkg memory");
+	rpc->fault(ctx, 500, "No more memory\n");
+}
+
+static int iterate_over_table(hash_tables_t *hts, str *result, credit_type_t type)
+{
+	struct str_hash_entry *h_entry, *tmp;
+	char row_buffer[512];
+	int index = 0;
+
+	lock_get(&hts->lock);
+
+	if (hts->credit_data_by_client->table)
+		for(index = 0; index < hts->credit_data_by_client->size; index++)
+			clist_foreach_safe(&hts->credit_data_by_client->table[index], h_entry, tmp, next)
+			{
+				credit_data_t *credit_data	= (credit_data_t *) h_entry->u.p;
+				lock_get(&credit_data->lock);
+
+				int row_len = 0;
+
+				memset(row_buffer, 0, sizeof(row_buffer));
+
+				if (type == CREDIT_TIME)
+				{
+					snprintf(row_buffer, sizeof(row_buffer), "client_id:%.*s,"
+															 "number_of_calls:%d,"
+															 "concurrent_calls:%d,"
+															 "type:%d,"
+															 "max_amount:%d,"
+															 "consumed_amount:%d;",
+															 credit_data->call_list->client_id.len, credit_data->call_list->client_id.s,
+															 credit_data->number_of_calls,
+															 credit_data->concurrent_calls,
+															 type,
+															 (int) credit_data->max_amount,
+															 (int) credit_data->consumed_amount);
+				}
+				else if (type == CREDIT_MONEY)
+				{
+					snprintf(row_buffer, sizeof(row_buffer), "client_id:%.*s,"
+															 "number_of_calls:%d,"
+															 "concurrent_calls:%d,"
+															 "type:%d,"
+															 "max_amount:%f,"
+															 "consumed_amount:%f;",
+															 credit_data->call_list->client_id.len, credit_data->call_list->client_id.s,
+															 credit_data->number_of_calls,
+															 credit_data->concurrent_calls,
+															 type,
+															 credit_data->max_amount,
+															 credit_data->consumed_amount);
+				}
+				else
+				{
+					LM_ERR("Unknown credit type: %d", type);
+					return -1;
+				}
+
+				lock_release(&credit_data->lock);
+
+				row_len 	= strlen(row_buffer);
+				result->s	= pkg_realloc(result->s, result->len + row_len);
+
+				if (result->s == NULL)
+				{
+					lock_release(&hts->lock);
+					goto nomem;
+				}
+
+				memcpy(result->s + result->len, row_buffer, row_len);
+				result->len += row_len;
+
+			}
+
+	lock_release(&hts->lock);
+
+	return 0;
+
+nomem:
+	LM_ERR("No more pkg memory");
+	return -1;
+}
+
+void rpc_active_clients(rpc_t* rpc, void* ctx)
+{
+	str rows;
+
+	rows.s	 = pkg_malloc(10);
+
+	if (rows.s == NULL)
+		goto nomem;
+
+	rows.len = 0;
+
+	iterate_over_table(&_data.time, &rows, CREDIT_TIME);
+	iterate_over_table(&_data.money, &rows, CREDIT_MONEY);
+
+	if (!rpc->add(ctx, "S", &rows) < 0)
+	{
+		LM_ERR("%s: error creating RPC struct\n", __FUNCTION__);
+	}
+
+	if (rows.s != NULL)
+		pkg_free(rows.s);
+
+	return;
+
+nomem:
+	LM_ERR("No more pkg memory");
+	rpc->fault(ctx, 500, "No more memory\n");
+}
+

+ 16 - 0
modules/cnxcc/cnxcc_rpc.h

@@ -0,0 +1,16 @@
+/*
+ * cnxcc_rpc.h
+ *
+ *  Created on: Dec 6, 2012
+ *      Author: carlos
+ */
+
+#ifndef CNXCC_RPC_H_
+#define CNXCC_RPC_H_
+
+void rpc_active_clients(rpc_t* rpc, void* ctx);
+void rpc_kill_call(rpc_t* rpc, void* ctx);
+void rpc_active_clients(rpc_t* rpc, void* ctx);
+void rpc_check_client_stats(rpc_t* rpc, void* ctx);
+
+#endif /* CNXCC_RPC_H_ */

+ 71 - 0
modules/cnxcc/cnxcc_sip_msg_faker.c

@@ -0,0 +1,71 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Carlos Ruiz Díaz (caruizdiaz.com),
+ *                    ConexionGroup (www.conexiongroup.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "../../parser/msg_parser.h"
+#include "../../globals.h"
+
+#include <sys/socket.h>
+
+#define FAKED_SIP_MSG_FORMAT "OPTIONS sip:[email protected] SIP/2.0\r\nVia: SIP/2.0/UDP 127.0.0.1\r\nFrom: <[email protected]>;tag=%.*s\r\nTo: <[email protected]>;tag=%.*s\r\nCall-ID: %.*s\r\nCSeq: 1 OPTIONS\r\nContent-Length: 0\r\n\r\n"
+
+#define FAKED_SIP_MSG_BUF_LEN	1024
+char _faked_sip_msg_buf[FAKED_SIP_MSG_BUF_LEN];
+
+static struct sip_msg _faked_msg;
+
+int faked_msg_init_with_dlg_info(str *callid, str *from_tag, str *to_tag,  struct sip_msg **msg)
+{
+	memset(_faked_sip_msg_buf, 0, FAKED_SIP_MSG_BUF_LEN);
+
+	sprintf(_faked_sip_msg_buf, FAKED_SIP_MSG_FORMAT, from_tag->len, from_tag->s,
+													  to_tag->len, to_tag->s,
+													  callid->len, callid->s);
+
+	memset(&_faked_msg, 0, sizeof(struct sip_msg));
+
+	_faked_msg.buf = _faked_sip_msg_buf;
+	_faked_msg.len = strlen(_faked_sip_msg_buf);
+
+	_faked_msg.set_global_address	= default_global_address;
+	_faked_msg.set_global_port		= default_global_port;
+
+	if (parse_msg(_faked_msg.buf, _faked_msg.len, &_faked_msg) != 0)
+	{
+			LM_ERR("parse_msg failed\n");
+			return -1;
+	}
+
+	_faked_msg.rcv.proto = PROTO_UDP;
+	_faked_msg.rcv.src_port = 5060;
+	_faked_msg.rcv.src_ip.u.addr32[0] = 0x7f000001;
+	_faked_msg.rcv.src_ip.af = AF_INET;
+	_faked_msg.rcv.src_ip.len = 4;
+	_faked_msg.rcv.dst_port = 5060;
+	_faked_msg.rcv.dst_ip.u.addr32[0] = 0x7f000001;
+	_faked_msg.rcv.dst_ip.af = AF_INET;
+	_faked_msg.rcv.dst_ip.len = 4;
+
+	*msg	= &_faked_msg;
+	return 0;
+}

+ 29 - 0
modules/cnxcc/cnxcc_sip_msg_faker.h

@@ -0,0 +1,29 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Carlos Ruiz Díaz (caruizdiaz.com),
+ *                    ConexionGroup (www.conexiongroup.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#ifndef CNXCC_SIP_MSG_FAKER_H_
+#define CNXCC_SIP_MSG_FAKER_H_
+
+int faked_msg_init_with_dlg_info(str *callid, str *from_tag, str *to_tag,  struct sip_msg **msg);
+
+#endif /* CNXCC_SIP_MSG_FAKER_H_ */

+ 4 - 0
modules/cnxcc/doc/Makefile

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

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

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+    <bookinfo>
+    <title>cnxcc Module</title>
+    <productname class="trade">&kamailioname;</productname>
+    <authorgroup>
+	<author>
+	<firstname>Carlos</firstname>
+	<surname>Ruiz Diaz</surname>
+	<affiliation>
+	    <orgname>ConexionGroup SA</orgname>
+	</affiliation>
+	<address>
+	    <email>[email protected]</email>
+	</address>
+	</author>
+    </authorgroup>
+    <copyright>
+	<year>2012</year>
+	<holder>Carlos Ruiz Diaz, [email protected]</holder>
+    </copyright>
+    </bookinfo>
+
+    <toc></toc>
+
+    <xi:include href="cnxcc_admin.xml" />
+</book>

+ 299 - 0
modules/cnxcc/doc/cnxcc_admin.xml

@@ -0,0 +1,299 @@
+<?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 Admin Guide -->
+
+<chapter xmlns:xi="http://www.w3.org/2001/XInclude">
+    <title>&adminguide;</title>
+
+    <section>
+	<title>Overview</title>
+	<para>
+		This module was designed to act as a mechanism to limit call duration based on credit information parameters.
+		After getting the credit information of the call being set up, you can instruct the module to start monitoring
+		the consumed credit to shutdown a single call or a group of calls in case of credit exhaustion.	
+	</para>
+	<para>
+		Every call is associated to an unique client/customer identifier. If a credit event occurs, all calls hooked
+	   	to this identifier are automatically shutdown.
+	</para>
+	<para>
+		Cnxcc is dialog-aware so there's no need to explicitly allocate/deallocate the monitoring. Only a single function
+		call inside the script is needed upon reception of the INVITE.
+	</para>
+	<para>
+		The credit discount rate is proportional to the number of calls grouped inside an identifier. Once the setup
+		of the first call is done, the information remains while the call is active. If the customer starts a new call with
+		the same routing criteria, it will land in the same monitoring bag and it will consume the same pool of credit in
+		rates that are equal to the cost per second of both calls.
+	</para>
+	<para>
+		If your accounting program does not maintain the state of the call in real time, this module can provide you
+		that ability.
+	</para>
+    </section>
+
+    <section>
+	<title>Dependencies</title>
+	<section>
+	    <title>Modules</title>
+	    <para>
+		The following module must be loaded before this module: 
+		<itemizedlist>
+		    <listitem>
+		    <para>
+			<emphasis>dialog</emphasis>
+		    </para>
+		    </listitem>
+		</itemizedlist>
+	    </para>
+	</section>
+    </section>
+
+    <section>
+	<title>Parameters</title>
+	<section>
+		<title><varname> dlg_flag </varname> (integer)</title>
+	    <para>
+		Flag to indicate if the dialog must be monitored or not. Messages are flagged with this value if we call one of
+		the monitoring functions.
+	    </para>
+	    <example>
+
+		<title>dlg_flag</title>
+		<programlisting format="linespecific">
+...
+modparam("cnxcc", "dlg_flag", 29)
+...		
+		</programlisting>
+	    </example>
+	</section>
+
+	<section>
+	    <title><varname>credit_check_period</varname> (integer)</title>
+	    <para>
+		Indicates how often the credit checking function should be called. It is directly related to the precison of the
+		module. The maximum precision is 1, which means that every call is checked every one second.
+
+	    </para>
+	    <para>
+		Values greater than 1 leads to precision lost but less CPU consumption.
+	    </para>
+	    <example>
+		<title>credit_check_period</title>
+		<programlisting format="linespecific">
+...
+modparam("cnxcc", "credit_check_period", 1)
+...		
+		</programlisting>
+	    </example>
+	</section> 
+   </section>
+
+    <section>
+	<title>Functions</title>
+	<section>
+	    <title>
+		<function moreinfo="none">cnxcc_set_max_credit()</function>
+	    </title>
+	    <para>
+		Specifies the initial pulse, final pulse, max credit and cost per second of a call. The discount
+		is calculated in pulses (30/6, 1/1, etc) and sustracted from the pool of credit.
+	    </para>
+	    <para>
+		<emphasis>Return code:</emphasis>
+		<itemizedlist>
+		    <listitem>
+		    <para>
+			<emphasis>1 - successful</emphasis>
+		    </para>
+		    </listitem>
+
+		    <listitem>
+		    <para>
+			<emphasis>-1 - failed, error logged</emphasis>
+		    </para>
+		    </listitem>
+		</itemizedlist>
+	    </para>
+	    <example>
+		<title>cnxcc_set_max_credit()</title>
+		<programlisting format="linespecific">
+...
+$var(customer) = "john-doe-123-premium";
+$var(credit) = "100";
+$var(cps)   = "2.00";         # cost per second
+$var(initial_p)   = "030";    # intial pulse
+$var(final_p)   = "006";      # final pulse
+
+cnxcc_set_max_credit("$var(customer)", "$var(credit)", "$var(cps)", "$var(initial_p)", "$var(final_p)");
+...		
+		</programlisting>
+	    </example>
+	</section>
+
+        <section>
+            <title>
+                <function moreinfo="none">cnxcc_set_max_time()</function>
+            </title>
+            <para>
+		Specifies the amount of time the call should last at most.
+            </para>
+            <para>
+                <emphasis>Return code:</emphasis>
+                <itemizedlist>
+                    <listitem>
+                    <para>
+                        <emphasis>1 - successful</emphasis>
+                    </para>
+                    </listitem>
+                     
+                    <listitem>
+                    <para>
+                        <emphasis>-1 - failed, error logged</emphasis>
+                    </para>
+                    </listitem>
+                </itemizedlist>
+            </para>
+            <example>
+		<title>cnxcc_set_max_time()</title>
+                <programlisting format="linespecific">
+...
+$var(customer) = "john-doe-123-basic";
+$var(max_time) = 120;
+
+cnxcc_set_max_tim ("$var(customer)", "$var(max_time)");
+...
+		</programlisting>
+            </example>
+        </section>
+    </section>
+    
+    <section>
+	<title>Exported RPC Commands</title>
+
+	<section>
+	    <title><varname>cnxcc.active_clients</varname></title>
+	    <para>
+		Retrieves all calls grouped by their identifiers.
+	    </para>
+	    <para>
+		Parameters: <emphasis>none</emphasis>
+	    </para>
+	    <para>
+		Example:
+	    </para>
+	    <programlisting format="linespecific">
+	    &sercmd; cnxcc.active_clients
+	    </programlisting>
+	</section>
+
+	<section>
+            <title><varname>cnxcc.check_client</varname></title>
+            <para>
+		Retrives all calls from a particular identifier.
+            </para>
+            <para>
+		    Parameters: <emphasis>client/customer identifier</emphasis>
+            </para>
+            <para>
+                Example:
+            </para>
+            <programlisting format="linespecific">
+	    &sercmd; cnxcc.check_client john-doe-123-premium
+            </programlisting>
+        </section>
+
+        <section>
+            <title><varname>cnxcc.kill_call</varname></title>
+            <para>
+		    Kills an active call using its call ID.
+	    </para>
+            <para>
+                Parameters: <emphasis>Call-ID</emphasis>
+            </para>
+            <para>
+                Example:
+            </para>
+            <programlisting format="linespecific">
+            &sercmd; cnxcc.kill_call [email protected]
+            </programlisting>
+        </section>
+    </section>
+
+    <section>
+	    <title>Events</title>
+	    
+	    <para>
+		When a call is forced to end an event route is automatically invoked. This route is suited with a fake OPTIONS
+		message containing the call ID, ftag and ttag of the original call so it can be located somehow in the accounting
+		database.
+	    </para>
+	    <para>
+                Example:
+            </para>
+            	<programlisting format="linespecific">
+...
+event_route[cnxcc:call-shutdown]
+{
+	xlog("L_INFO", "[$ci]: call killed");
+
+        # perform some kind of notification, database update, email sending, etc.
+}
+...
+		</programlisting>
+	    
+    </section>
+    <section>
+            <title>Web Interface</title>
+
+            <para>
+		The module contains a web management interface completely optional. With it, you can review your calls in real time
+		and hang them up if necessary.    
+	    </para>
+            <para>
+                Link: https://github.com/caruizdiaz/cnxcc-web
+            </para>
+    </section>
+
+    <section>
+	<title>Sample</title>
+	<example>
+	    <title>kamailio-cnxcc.cfg</title>
+	    <programlisting format="linespecific">
+...
+route[CNXCC]
+{
+	$var(client)              = "test-client-0-123-01";
+      	$var(credit)              = "50";
+      	$var(cost_per_sec)        = "0.5";
+      	$var(i_pulse)             = "30";
+      	$var(f_pulse)             = "6";
+
+      	if (!cnxcc_set_max_credit("$var(client)",
+                          "$var(credit)",
+                          "$var(cost_per_sec)",
+                          "$var(i_pulse)",
+                          "$var(f_pulse)")) {
+		 xlog("Error setting up credit control");
+      	}
+}
+
+event_route[cnxcc:call-shutdown]
+{
+	xlog("L_INFO", "[$ci]: call killed");
+
+
+}	    
+...
+	    </programlisting>
+	</example>
+    </section>
+
+</chapter>

+ 983 - 0
modules/cnxcc/example/kamailio-cnxcc.cfg

@@ -0,0 +1,983 @@
+#!KAMAILIO
+#
+# Kamailio (OpenSER) SIP Server v3.2 - default configuration script
+#     - web: http://www.kamailio.org
+#     - git: http://sip-router.org
+#
+# Direct your questions about this file to: <[email protected]>
+#
+# Refer to the Core CookBook at http://www.kamailio.org/dokuwiki/doku.php
+# for an explanation of possible statements, functions and parameters.
+#
+# Several features can be enabled using '#!define WITH_FEATURE' directives:
+#
+# *** To run in debug mode: 
+#     - define WITH_DEBUG
+#
+# *** To enable mysql: 
+#     - define WITH_MYSQL
+#
+# *** To enable authentication execute:
+#     - enable mysql
+#     - define WITH_AUTH
+#     - add users using 'kamctl'
+#
+# *** To enable IP authentication execute:
+#     - enable mysql
+#     - enable authentication
+#     - define WITH_IPAUTH
+#     - add IP addresses with group id '1' to 'address' table
+#
+# *** To enable persistent user location execute:
+#     - enable mysql
+#     - define WITH_USRLOCDB
+#
+# *** To enable presence server execute:
+#     - enable mysql
+#     - define WITH_PRESENCE
+#
+# *** To enable nat traversal execute:
+#     - define WITH_NAT
+#     - install RTPProxy: http://www.rtpproxy.org
+#     - start RTPProxy:
+#        rtpproxy -l _your_public_ip_ -s udp:localhost:7722
+#
+# *** To enable PSTN gateway routing execute:
+#     - define WITH_PSTN
+#     - set the value of pstn.gw_ip
+#     - check route[PSTN] for regexp routing condition
+#
+# *** To enable database aliases lookup execute:
+#     - enable mysql
+#     - define WITH_ALIASDB
+#
+# *** To enable speed dial lookup execute:
+#     - enable mysql
+#     - define WITH_SPEEDDIAL
+#
+# *** To enable multi-domain support execute:
+#     - enable mysql
+#     - define WITH_MULTIDOMAIN
+#
+# *** To enable TLS support execute:
+#     - adjust CFGDIR/tls.cfg as needed
+#     - define WITH_TLS
+#
+# *** To enable XMLRPC support execute:
+#     - define WITH_XMLRPC
+#     - adjust route[XMLRPC] for access policy
+#
+# *** To enable anti-flood detection execute:
+#     - adjust pike and htable=>ipban settings as needed (default is
+#       block if more than 16 requests in 2 seconds and ban for 300 seconds)
+#     - define WITH_ANTIFLOOD
+#
+# *** To block 3XX redirect replies execute:
+#     - define WITH_BLOCK3XX
+#
+# *** To enable VoiceMail routing execute:
+#     - define WITH_VOICEMAIL
+#     - set the value of voicemail.srv_ip
+#     - adjust the value of voicemail.srv_port
+#
+# *** To enhance accounting execute:
+#     - enable mysql
+#     - define WITH_ACCDB
+#     - add following columns to database
+#!ifdef ACCDB_COMMENT
+  ALTER TABLE acc ADD COLUMN src_user VARCHAR(64) NOT NULL DEFAULT '';
+  ALTER TABLE acc ADD COLUMN src_domain VARCHAR(128) NOT NULL DEFAULT '';
+  ALTER TABLE acc ADD COLUMN src_ip varchar(64) NOT NULL default '';
+  ALTER TABLE acc ADD COLUMN dst_ouser VARCHAR(64) NOT NULL DEFAULT '';
+  ALTER TABLE acc ADD COLUMN dst_user VARCHAR(64) NOT NULL DEFAULT '';
+  ALTER TABLE acc ADD COLUMN dst_domain VARCHAR(128) NOT NULL DEFAULT '';
+  ALTER TABLE missed_calls ADD COLUMN src_user VARCHAR(64) NOT NULL DEFAULT '';
+  ALTER TABLE missed_calls ADD COLUMN src_domain VARCHAR(128) NOT NULL DEFAULT '';
+  ALTER TABLE missed_calls ADD COLUMN src_ip varchar(64) NOT NULL default '';
+  ALTER TABLE missed_calls ADD COLUMN dst_ouser VARCHAR(64) NOT NULL DEFAULT '';
+  ALTER TABLE missed_calls ADD COLUMN dst_user VARCHAR(64) NOT NULL DEFAULT '';
+  ALTER TABLE missed_calls ADD COLUMN dst_domain VARCHAR(128) NOT NULL DEFAULT '';
+#!endif
+
+####### Defined Values #########
+
+# *** Value defines - IDs used later in config
+#!ifdef WITH_MYSQL
+# - database URL - used to connect to database server by modules such
+#       as: auth_db, acc, usrloc, a.s.o.
+#!define DBURL "mysql://openser:openserrw@localhost/openser"
+#!endif
+#!ifdef WITH_MULTIDOMAIN
+# - the value for 'use_domain' parameters
+#!define MULTIDOMAIN 1
+#!else
+#!define MULTIDOMAIN 0
+#!endif
+
+# - flags
+#   FLT_ - per transaction (message) flags
+#	FLB_ - per branch flags
+#!define FLT_ACC 1
+#!define FLT_ACCMISSED 2
+#!define FLT_ACCFAILED 3
+#!define FLT_NATS 5
+
+#!define FLB_NATB 6
+#!define FLB_NATSIPPING 7
+
+#!define WITH_XMLRPC
+
+####### Global Parameters #########
+
+#!ifdef WITH_DEBUG
+debug=4
+log_stderror=yes
+#!else
+debug=2
+log_stderror=no
+#!endif
+
+memdbg=5
+memlog=5
+
+log_facility=LOG_LOCAL0
+
+fork=yes
+children=4
+
+/* uncomment the next line to disable TCP (default on) */
+#disable_tcp=yes
+
+/* uncomment the next line to disable the auto discovery of local aliases
+   based on reverse DNS on IPs (default on) */
+#auto_aliases=no
+
+/* add local domain aliases */
+#alias="sip.mydomain.com"
+
+/* uncomment and configure the following line if you want Kamailio to 
+   bind on a specific interface/port/proto (default bind on all available) */
+#listen=udp:10.0.0.10:5060
+
+/* port to listen to
+ * - can be specified more than once if needed to listen on many ports */
+port=5060
+
+#!ifdef WITH_TLS
+enable_tls=yes
+#!endif
+
+# life time of TCP connection when there is no traffic
+# - a bit higher than registration expires to cope with UA behind NAT
+tcp_connection_lifetime=3605
+
+####### Custom Parameters #########
+
+# These parameters can be modified runtime via RPC interface
+# - see the documentation of 'cfg_rpc' module.
+#
+# Format: group.id = value 'desc' description
+# Access: $sel(cfg_get.group.id) or @cfg_get.group.id
+#
+
+#!ifdef WITH_PSTN
+# PSTN GW Routing
+#
+# - pstn.gw_ip: valid IP or hostname as string value, example:
+# pstn.gw_ip = "10.0.0.101" desc "My PSTN GW Address"
+#
+# - by default is empty to avoid misrouting
+pstn.gw_ip = "" desc "PSTN GW Address"
+#!endif
+
+#!ifdef WITH_VOICEMAIL
+# VoiceMail Routing on offline, busy or no answer
+#
+# - by default Voicemail server IP is empty to avoid misrouting
+voicemail.srv_ip = "" desc "VoiceMail IP Address"
+voicemail.srv_port = "5060" desc "VoiceMail Port"
+#!endif
+
+####### Modules Section ########
+
+# set paths to location of modules (to sources or installation folders)
+#!ifdef WITH_SRCPATH
+mpath="modules_k:modules"
+#!else
+mpath="/usr/local/lib/kamailio/modules_k/:/usr/local/lib/kamailio/modules/"
+#!endif
+
+#!ifdef WITH_MYSQL
+loadmodule "db_mysql.so"
+#!endif
+
+loadmodule "mi_fifo.so"
+loadmodule "kex.so"
+loadmodule "tm.so"
+loadmodule "tmx.so"
+loadmodule "sl.so"
+loadmodule "rr.so"
+loadmodule "pv.so"
+loadmodule "maxfwd.so"
+loadmodule "usrloc.so"
+loadmodule "registrar.so"
+loadmodule "textops.so"
+loadmodule "siputils.so"
+loadmodule "xlog.so"
+loadmodule "sanity.so"
+loadmodule "ctl.so"
+loadmodule "cfg_rpc.so"
+loadmodule "mi_rpc.so"
+loadmodule "acc.so"
+
+#!ifdef WITH_AUTH
+loadmodule "auth.so"
+loadmodule "auth_db.so"
+#!ifdef WITH_IPAUTH
+loadmodule "permissions.so"
+#!endif
+#!endif
+
+#!ifdef WITH_ALIASDB
+loadmodule "alias_db.so"
+#!endif
+
+#!ifdef WITH_SPEEDDIAL
+loadmodule "speeddial.so"
+#!endif
+
+#!ifdef WITH_MULTIDOMAIN
+loadmodule "domain.so"
+#!endif
+
+#!ifdef WITH_PRESENCE
+loadmodule "presence.so"
+loadmodule "presence_xml.so"
+#!endif
+
+#!ifdef WITH_NAT
+loadmodule "nathelper.so"
+loadmodule "rtpproxy.so"
+#!endif
+
+#!ifdef WITH_TLS
+loadmodule "tls.so"
+#!endif
+
+#!ifdef WITH_ANTIFLOOD
+loadmodule "htable.so"
+loadmodule "pike.so"
+#!endif
+
+#!ifdef WITH_XMLRPC
+loadmodule "xmlrpc.so"
+#!endif
+
+#!ifdef WITH_DEBUG
+loadmodule "debugger.so"
+#!endif
+
+# ----------------- setting module-specific parameters ---------------
+
+
+# ----- mi_fifo params -----
+modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo")
+
+
+# ----- tm params -----
+# auto-discard branches from previous serial forking leg
+modparam("tm", "failure_reply_mode", 3)
+# default retransmission timeout: 30sec
+modparam("tm", "fr_timer", 30000)
+# default invite retransmission timeout after 1xx: 120sec
+modparam("tm", "fr_inv_timer", 120000)
+
+
+# ----- rr params -----
+# add value to ;lr param to cope with most of the UAs
+modparam("rr", "enable_full_lr", 1)
+# do not append from tag to the RR (no need for this script)
+modparam("rr", "append_fromtag", 0)
+
+
+# ----- registrar params -----
+modparam("registrar", "method_filtering", 1)
+/* uncomment the next line to disable parallel forking via location */
+# modparam("registrar", "append_branches", 0)
+/* uncomment the next line not to allow more than 10 contacts per AOR */
+#modparam("registrar", "max_contacts", 10)
+# max value for expires of registrations
+modparam("registrar", "max_expires", 3600)
+
+
+# ----- acc params -----
+/* what special events should be accounted ? */
+modparam("acc", "early_media", 0)
+modparam("acc", "report_ack", 0)
+modparam("acc", "report_cancels", 0)
+/* by default ww do not adjust the direct of the sequential requests.
+   if you enable this parameter, be sure the enable "append_fromtag"
+   in "rr" module */
+modparam("acc", "detect_direction", 0)
+/* account triggers (flags) */
+modparam("acc", "log_flag", FLT_ACC)
+modparam("acc", "log_missed_flag", FLT_ACCMISSED)
+modparam("acc", "log_extra", 
+	"src_user=$fU;src_domain=$fd;src_ip=$si;"
+	"dst_ouser=$tU;dst_user=$rU;dst_domain=$rd")
+modparam("acc", "failed_transaction_flag", FLT_ACCFAILED)
+/* enhanced DB accounting */
+#!ifdef WITH_ACCDB
+modparam("acc", "db_flag", FLT_ACC)
+modparam("acc", "db_missed_flag", FLT_ACCMISSED)
+modparam("acc", "db_url", DBURL)
+modparam("acc", "db_extra",
+	"src_user=$fU;src_domain=$fd;src_ip=$si;"
+	"dst_ouser=$tU;dst_user=$rU;dst_domain=$rd")
+#!endif
+
+
+# ----- usrloc params -----
+/* enable DB persistency for location entries */
+#!ifdef WITH_USRLOCDB
+modparam("usrloc", "db_url", DBURL)
+modparam("usrloc", "db_mode", 2)
+modparam("usrloc", "use_domain", MULTIDOMAIN)
+#!endif
+
+
+# ----- auth_db params -----
+#!ifdef WITH_AUTH
+modparam("auth_db", "db_url", DBURL)
+modparam("auth_db", "calculate_ha1", yes)
+modparam("auth_db", "password_column", "password")
+modparam("auth_db", "load_credentials", "")
+modparam("auth_db", "use_domain", MULTIDOMAIN)
+
+# ----- permissions params -----
+#!ifdef WITH_IPAUTH
+modparam("permissions", "db_url", DBURL)
+modparam("permissions", "db_mode", 1)
+#!endif
+
+#!endif
+
+
+# ----- alias_db params -----
+#!ifdef WITH_ALIASDB
+modparam("alias_db", "db_url", DBURL)
+modparam("alias_db", "use_domain", MULTIDOMAIN)
+#!endif
+
+
+# ----- speedial params -----
+#!ifdef WITH_SPEEDDIAL
+modparam("speeddial", "db_url", DBURL)
+modparam("speeddial", "use_domain", MULTIDOMAIN)
+#!endif
+
+
+# ----- domain params -----
+#!ifdef WITH_MULTIDOMAIN
+modparam("domain", "db_url", DBURL)
+# use caching
+modparam("domain", "db_mode", 1)
+# register callback to match myself condition with domains list
+modparam("domain", "register_myself", 1)
+#!endif
+
+
+#!ifdef WITH_PRESENCE
+# ----- presence params -----
+modparam("presence", "db_url", DBURL)
+
+# ----- presence_xml params -----
+modparam("presence_xml", "db_url", DBURL)
+modparam("presence_xml", "force_active", 1)
+#!endif
+
+
+#!ifdef WITH_NAT
+# ----- rtpproxy params -----
+modparam("rtpproxy", "rtpproxy_sock", "udp:127.0.0.1:7722")
+
+# ----- nathelper params -----
+modparam("nathelper", "natping_interval", 30)
+modparam("nathelper", "ping_nated_only", 1)
+modparam("nathelper", "sipping_bflag", FLB_NATSIPPING)
+modparam("nathelper", "sipping_from", "sip:[email protected]")
+
+# params needed for NAT traversal in other modules
+modparam("nathelper|registrar", "received_avp", "$avp(RECEIVED)")
+modparam("usrloc", "nat_bflag", FLB_NATB)
+#!endif
+
+
+#!ifdef WITH_TLS
+# ----- tls params -----
+modparam("tls", "config", "/usr/local/etc/kamailio/tls.cfg")
+#!endif
+
+#!ifdef WITH_ANTIFLOOD
+# ----- pike params -----
+modparam("pike", "sampling_time_unit", 2)
+modparam("pike", "reqs_density_per_unit", 16)
+modparam("pike", "remove_latency", 4)
+
+# ----- htable params -----
+# ip ban htable with autoexpire after 5 minutes
+modparam("htable", "htable", "ipban=>size=8;autoexpire=300;")
+#!endif
+
+#!ifdef WITH_XMLRPC
+# ----- xmlrpc params -----
+modparam("xmlrpc", "route", "XMLRPC");
+#modparam("xmlrpc", "url_match", "^/RPC")
+#!endif
+
+#!ifdef WITH_DEBUG
+# ----- debugger params -----
+modparam("debugger", "cfgtrace", 1)
+#!endif
+
+#!define DLG_FLAG 28
+#!define CC_FLAG 29
+
+loadmodule "dialog.so"
+modparam("dialog", "hash_size", 2048)
+modparam("dialog", "default_timeout", 3600)
+modparam("dialog", "db_mode", 0)
+modparam("dialog", "dlg_flag", DLG_FLAG)
+
+loadmodule "cnxcc.so"
+modparam("cnxcc", "dlg_flag", CC_FLAG)
+modparam("cnxcc", "credit_check_period", 1)
+
+####### Routing Logic ########
+
+
+# Main SIP request routing logic
+# - processing of any incoming SIP request starts with this route
+# - note: this is the same as route { ... }
+request_route {
+
+	setflag(DLG_FLAG);
+
+	# per request initial checks
+	route(REQINIT);
+
+	# NAT detection
+	route(NATDETECT);
+
+	# handle requests within SIP dialogs
+	route(WITHINDLG);
+
+	### only initial requests (no To tag)
+
+	# CANCEL processing
+	if (is_method("CANCEL"))
+	{
+		if (t_check_trans())
+			t_relay();
+		exit;
+	}
+
+	t_check_trans();
+
+	# authentication
+	route(AUTH);
+
+	# record routing for dialog forming requests (in case they are routed)
+	# - remove preloaded route headers
+	remove_hf("Route");
+	if (is_method("INVITE|SUBSCRIBE"))
+		record_route();
+
+	# account only INVITEs
+	if (is_method("INVITE"))
+	{
+		setflag(FLT_ACC); # do accounting
+	}
+
+	# dispatch requests to foreign domains
+	route(SIPOUT);
+
+	### requests for my local domains
+
+	# handle presence related requests
+	route(PRESENCE);
+
+	# handle registrations
+	route(REGISTRAR);
+
+	if ($rU==$null)
+	{
+		# request with no Username in RURI
+		sl_send_reply("484","Address Incomplete");
+		exit;
+	}
+
+	# dispatch destinations to PSTN
+	route(PSTN);
+
+	# user location service
+	route(LOCATION);
+
+	route(CNXCC);
+	
+	route(RELAY);
+}
+
+route[CNXCC]
+{
+	#
+	# In real life scenarios, all the authorization values 
+	# are retrieved from a database and calculed on-the-fly.
+	# 
+	# This hardcoded values are just for illustrative purposes
+	#
+
+	$var(client)		= "test-client-0-123-01";
+	$var(credit) 		= "50";
+	$var(cost_per_sec) 	= "0.5";
+	$var(i_pulse)		= "30";
+	$var(f_pulse)		= "6";
+
+	# if only one call is established, that call should last 1m, 36s
+
+	if (!cnxcc_set_max_credit("$var(client)",
+				  "$var(credit)", 
+				  "$var(cost_per_sec)", 
+				  "$var(i_pulse)", 
+				  "$var(f_pulse)")) {
+		xlog("Error setting up credit control");
+	}
+}
+
+event_route[cnxcc:call-shutdown]
+{
+        xlog("L_INFO", "[$ci]: call killed");
+	
+	# perform some kind of notification, database update, email sending, etc
+}
+
+route[RELAY] {
+
+	# enable additional event routes for forwarded requests
+	# - serial forking, RTP relaying handling, a.s.o.
+	if (is_method("INVITE|SUBSCRIBE")) {
+		t_on_branch("MANAGE_BRANCH");
+		t_on_reply("MANAGE_REPLY");
+	}
+	if (is_method("INVITE")) {
+		t_on_failure("MANAGE_FAILURE");
+	}
+
+	if (!t_relay()) {
+		sl_reply_error();
+	}
+	exit;
+}
+
+# Per SIP request initial checks
+route[REQINIT] {
+#!ifdef WITH_ANTIFLOOD
+	# flood dection from same IP and traffic ban for a while
+	# be sure you exclude checking trusted peers, such as pstn gateways
+	# - local host excluded (e.g., loop to self)
+	if(src_ip!=myself)
+	{
+		if($sht(ipban=>$si)!=$null)
+		{
+			# ip is already blocked
+			xdbg("request from blocked IP - $rm from $fu (IP:$si:$sp)\n");
+			exit;
+		}
+		if (!pike_check_req())
+		{
+			xlog("L_ALERT","ALERT: pike blocking $rm from $fu (IP:$si:$sp)\n");
+			$sht(ipban=>$si) = 1;
+			exit;
+		}
+	}
+#!endif
+
+	if (!mf_process_maxfwd_header("10")) {
+		sl_send_reply("483","Too Many Hops");
+		exit;
+	}
+
+	if(!sanity_check("1511", "7"))
+	{
+		xlog("Malformed SIP message from $si:$sp\n");
+		exit;
+	}
+}
+
+# Handle requests within SIP dialogs
+route[WITHINDLG] {
+	if (has_totag()) {
+		# sequential request withing a dialog should
+		# take the path determined by record-routing
+		if (loose_route()) {
+			if (is_method("BYE")) {
+				setflag(FLT_ACC); # do accounting ...
+				setflag(FLT_ACCFAILED); # ... even if the transaction fails
+			}
+			if ( is_method("ACK") ) {
+				# ACK is forwarded statelessy
+				route(NATMANAGE);
+			}
+			route(RELAY);
+		} else {
+			if (is_method("SUBSCRIBE") && uri == myself) {
+				# in-dialog subscribe requests
+				route(PRESENCE);
+				exit;
+			}
+			if ( is_method("ACK") ) {
+				if ( t_check_trans() ) {
+					# no loose-route, but stateful ACK;
+					# must be an ACK after a 487
+					# or e.g. 404 from upstream server
+					t_relay();
+					exit;
+				} else {
+					# ACK without matching transaction ... ignore and discard
+					exit;
+				}
+			}
+			sl_send_reply("404","Not here");
+		}
+		exit;
+	}
+}
+
+# Handle SIP registrations
+route[REGISTRAR] {
+	if (is_method("REGISTER"))
+	{
+		if(isflagset(FLT_NATS))
+		{
+			setbflag(FLB_NATB);
+			# uncomment next line to do SIP NAT pinging 
+			## setbflag(FLB_NATSIPPING);
+		}
+		if (!save("location"))
+			sl_reply_error();
+
+		exit;
+	}
+}
+
+# USER location service
+route[LOCATION] {
+
+#!ifdef WITH_SPEEDIAL
+	# search for short dialing - 2-digit extension
+	if($rU=~"^[0-9][0-9]$")
+		if(sd_lookup("speed_dial"))
+			route(SIPOUT);
+#!endif
+
+#!ifdef WITH_ALIASDB
+	# search in DB-based aliases
+	if(alias_db_lookup("dbaliases"))
+		route(SIPOUT);
+#!endif
+
+	$avp(oexten) = $rU;
+	if (!lookup("location")) {
+		$var(rc) = $rc;
+		route(TOVOICEMAIL);
+		t_newtran();
+		switch ($var(rc)) {
+			case -1:
+			case -3:
+				send_reply("404", "Not Found");
+				exit;
+			case -2:
+				send_reply("405", "Method Not Allowed");
+				exit;
+		}
+	}
+
+	# when routing via usrloc, log the missed calls also
+	if (is_method("INVITE"))
+	{
+		setflag(FLT_ACCMISSED);
+	}
+}
+
+# Presence server route
+route[PRESENCE] {
+	if(!is_method("PUBLISH|SUBSCRIBE"))
+		return;
+
+#!ifdef WITH_PRESENCE
+	if (!t_newtran())
+	{
+		sl_reply_error();
+		exit;
+	};
+
+	if(is_method("PUBLISH"))
+	{
+		handle_publish();
+		t_release();
+	}
+	else
+	if( is_method("SUBSCRIBE"))
+	{
+		handle_subscribe();
+		t_release();
+	}
+	exit;
+#!endif
+	
+	# if presence enabled, this part will not be executed
+	if (is_method("PUBLISH") || $rU==$null)
+	{
+		sl_send_reply("404", "Not here");
+		exit;
+	}
+	return;
+}
+
+# Authentication route
+route[AUTH] {
+#!ifdef WITH_AUTH
+	if (is_method("REGISTER"))
+	{
+		# authenticate the REGISTER requests (uncomment to enable auth)
+		if (!www_authorize("$td", "subscriber"))
+		{
+			www_challenge("$td", "0");
+			exit;
+		}
+
+		if ($au!=$tU)
+		{
+			sl_send_reply("403","Forbidden auth ID");
+			exit;
+		}
+	} else {
+
+#!ifdef WITH_IPAUTH
+		if(allow_source_address())
+		{
+			# source IP allowed
+			return;
+		}
+#!endif
+
+		# authenticate if from local subscriber
+		if (from_uri==myself)
+		{
+			if (!proxy_authorize("$fd", "subscriber")) {
+				proxy_challenge("$fd", "0");
+				exit;
+			}
+			if (is_method("PUBLISH"))
+			{
+				if ($au!=$fU || $au!=$tU) {
+					sl_send_reply("403","Forbidden auth ID");
+					exit;
+				}
+				if ($au!=$rU) {
+					sl_send_reply("403","Forbidden R-URI");
+					exit;
+				}
+#!ifdef WITH_MULTIDOMAIN
+				if ($fd!=$rd) {
+					sl_send_reply("403","Forbidden R-URI domain");
+					exit;
+				}
+#!endif
+			} else {
+				if ($au!=$fU) {
+					sl_send_reply("403","Forbidden auth ID");
+					exit;
+				}
+			}
+
+			consume_credentials();
+			# caller authenticated
+		} else {
+			# caller is not local subscriber, then check if it calls
+			# a local destination, otherwise deny, not an open relay here
+			if (!uri==myself)
+			{
+				sl_send_reply("403","Not relaying");
+				exit;
+			}
+		}
+	}
+#!endif
+	return;
+}
+
+# Caller NAT detection route
+route[NATDETECT] {
+#!ifdef WITH_NAT
+	force_rport();
+	if (nat_uac_test("19")) {
+		if (is_method("REGISTER")) {
+			fix_nated_register();
+		} else {
+			fix_nated_contact();
+		}
+		setflag(FLT_NATS);
+	}
+#!endif
+	return;
+}
+
+# RTPProxy control
+route[NATMANAGE] {
+#!ifdef WITH_NAT
+	if (is_request()) {
+		if(has_totag()) {
+			if(check_route_param("nat=yes")) {
+				setbflag(FLB_NATB);
+			}
+		}
+	}
+	if (!(isflagset(FLT_NATS) || isbflagset(FLB_NATB)))
+		return;
+
+	rtpproxy_manage();
+
+	if (is_request()) {
+		if (!has_totag()) {
+			add_rr_param(";nat=yes");
+		}
+	}
+	if (is_reply()) {
+		if(isbflagset(FLB_NATB)) {
+			fix_nated_contact();
+		}
+	}
+#!endif
+	return;
+}
+
+# Routing to foreign domains
+route[SIPOUT] {
+	if (!uri==myself)
+	{
+		append_hf("P-hint: outbound\r\n");
+		route(RELAY);
+	}
+}
+
+# PSTN GW routing
+route[PSTN] {
+#!ifdef WITH_PSTN
+	# check if PSTN GW IP is defined
+	if (strempty($sel(cfg_get.pstn.gw_ip))) {
+		xlog("SCRIPT: PSTN rotuing enabled but pstn.gw_ip not defined\n");
+		return;
+	}
+
+	# route to PSTN dialed numbers starting with '+' or '00'
+	#     (international format)
+	# - update the condition to match your dialing rules for PSTN routing
+	if(!($rU=~"^(\+|00)[1-9][0-9]{3,20}$"))
+		return;
+
+	# only local users allowed to call
+	if(from_uri!=myself) {
+		sl_send_reply("403", "Not Allowed");
+		exit;
+	}
+
+	$ru = "sip:" + $rU + "@" + $sel(cfg_get.pstn.gw_ip);
+
+	route(RELAY);
+	exit;
+#!endif
+
+	return;
+}
+
+# XMLRPC routing
+#!ifdef WITH_XMLRPC
+route[XMLRPC] {
+	# allow XMLRPC from localhost
+	if ((method=="POST" || method=="GET")
+			&& (src_ip==127.0.0.1)) {
+		# close connection only for xmlrpclib user agents (there is a bug in
+		# xmlrpclib: it waits for EOF before interpreting the response).
+		if ($hdr(User-Agent) =~ "xmlrpclib")
+			set_reply_close();
+		set_reply_no_connect();
+		dispatch_rpc();
+		exit;
+	}
+	send_reply("403", "Forbidden");
+	exit;
+}
+#!endif
+
+# route to voicemail server
+route[TOVOICEMAIL] {
+#!ifdef WITH_VOICEMAIL
+	if(!is_method("INVITE"))
+		return;
+
+	# check if VoiceMail server IP is defined
+	if (strempty($sel(cfg_get.voicemail.srv_ip))) {
+		xlog("SCRIPT: VoiceMail rotuing enabled but IP not defined\n");
+		return;
+	}
+	if($avp(oexten)==$null)
+		return;
+
+	$ru = "sip:" + $avp(oexten) + "@" + $sel(cfg_get.voicemail.srv_ip)
+				+ ":" + $sel(cfg_get.voicemail.srv_port);
+	route(RELAY);
+	exit;
+#!endif
+
+	return;
+}
+
+# manage outgoing branches
+branch_route[MANAGE_BRANCH] {
+	xdbg("new branch [$T_branch_idx] to $ru\n");
+	route(NATMANAGE);
+}
+
+# manage incoming replies
+onreply_route[MANAGE_REPLY] {
+	xdbg("incoming reply\n");
+	if(status=~"[12][0-9][0-9]")
+		route(NATMANAGE);
+}
+
+# manage failure routing cases
+failure_route[MANAGE_FAILURE] {
+	route(NATMANAGE);
+
+	if (t_is_canceled()) {
+		exit;
+	}
+
+#!ifdef WITH_BLOCK3XX
+	# block call redirect based on 3xx replies.
+	if (t_check_status("3[0-9][0-9]")) {
+		t_reply("404","Not found");
+		exit;
+	}
+#!endif
+
+#!ifdef WITH_VOICEMAIL
+	# serial forking
+	# - route to voicemail on busy or no answer (timeout)
+	if (t_check_status("486|408")) {
+		route(TOVOICEMAIL);
+		exit;
+	}
+#!endif
+}

+ 22 - 6
modules/db_mysql/README

@@ -28,6 +28,7 @@ Daniel-Constantin Mierla
               3.1. ping_interval (integer)
               3.1. ping_interval (integer)
               3.2. timeout_interval (integer)
               3.2. timeout_interval (integer)
               3.3. auto_reconnect (integer)
               3.3. auto_reconnect (integer)
+              3.4. insert_delayed (integer)
 
 
         4. Functions
         4. Functions
         5. Installation
         5. Installation
@@ -38,9 +39,10 @@ Daniel-Constantin Mierla
    1.1. Set ping_interval parameter
    1.1. Set ping_interval parameter
    1.2. Set timeout_interval parameter
    1.2. Set timeout_interval parameter
    1.3. Set auto_reconnect parameter
    1.3. Set auto_reconnect parameter
-   1.4. Set a my.cnf group in db_url parameter
-   1.5. Adding a kamailio group to my.cnf
-   1.6. Using [client] and specific group
+   1.4. Set insert_delayed parameter
+   1.5. Set a my.cnf group in db_url parameter
+   1.6. Adding a kamailio group to my.cnf
+   1.7. Using [client] and specific group
 
 
 Chapter 1. Admin Guide
 Chapter 1. Admin Guide
 
 
@@ -57,6 +59,7 @@ Chapter 1. Admin Guide
         3.1. ping_interval (integer)
         3.1. ping_interval (integer)
         3.2. timeout_interval (integer)
         3.2. timeout_interval (integer)
         3.3. auto_reconnect (integer)
         3.3. auto_reconnect (integer)
+        3.4. insert_delayed (integer)
 
 
    4. Functions
    4. Functions
    5. Installation
    5. Installation
@@ -89,6 +92,7 @@ Chapter 1. Admin Guide
    3.1. ping_interval (integer)
    3.1. ping_interval (integer)
    3.2. timeout_interval (integer)
    3.2. timeout_interval (integer)
    3.3. auto_reconnect (integer)
    3.3. auto_reconnect (integer)
+   3.4. insert_delayed (integer)
 
 
 3.1. ping_interval (integer)
 3.1. ping_interval (integer)
 
 
@@ -132,6 +136,18 @@ modparam("db_mysql", "timeout_interval", 2)
 modparam("db_mysql", "auto_reconnect", 0)
 modparam("db_mysql", "auto_reconnect", 0)
 ...
 ...
 
 
+3.4. insert_delayed (integer)
+
+   If set to 1, all INSERT SQL queries will be sent to MySQL server as
+   INSERT DELAYED.
+
+   Default value is 0 (1 - on / 0 - off).
+
+   Example 1.4. Set insert_delayed parameter
+...
+modparam("db_mysql", "insert_delayed", 1)
+...
+
 4. Functions
 4. Functions
 
 
    No function exported to be used from configuration file.
    No function exported to be used from configuration file.
@@ -158,12 +174,12 @@ modparam("db_mysql", "auto_reconnect", 0)
      * mysql://user:pass@[group]/db
      * mysql://user:pass@[group]/db
      * mysql://[group]/db
      * mysql://[group]/db
 
 
-   Example 1.4. Set a my.cnf group in db_url parameter
+   Example 1.5. Set a my.cnf group in db_url parameter
 ...
 ...
 modparam("usrloc", "db_url", "mysql://[kamailio]/kamailio)
 modparam("usrloc", "db_url", "mysql://[kamailio]/kamailio)
 ...
 ...
 
 
-   Example 1.5. Adding a kamailio group to my.cnf
+   Example 1.6. Adding a kamailio group to my.cnf
 ...
 ...
 [kamailio]
 [kamailio]
 socket = /path/to/mysql.sock
 socket = /path/to/mysql.sock
@@ -177,7 +193,7 @@ default-character-set = utf8
    both your specific group and the client group, then the value is taken
    both your specific group and the client group, then the value is taken
    from the last one.
    from the last one.
 
 
-   Example 1.6. Using [client] and specific group
+   Example 1.7. Using [client] and specific group
 ...
 ...
 [client]
 [client]
 socket = /var/run/mysql/mysqld.sock
 socket = /var/run/mysql/mysqld.sock

+ 20 - 0
modules/db_mysql/doc/db_mysql_admin.xml

@@ -118,6 +118,26 @@ modparam("db_mysql", "timeout_interval", 2)
 ...
 ...
 modparam("db_mysql", "auto_reconnect", 0)
 modparam("db_mysql", "auto_reconnect", 0)
 ...
 ...
+</programlisting>
+		</example>
+	</section>
+	<section id="db_mysql.p.insert_delayed">
+		<title><varname>insert_delayed</varname> (integer)</title>
+		<para>
+		If set to 1, all INSERT SQL queries will be sent to MySQL server as
+		INSERT DELAYED.
+		</para>
+		<para>
+		<emphasis>
+			Default value is 0 (1 - on / 0 - off).
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>insert_delayed</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("db_mysql", "insert_delayed", 1)
+...
 </programlisting>
 </programlisting>
 		</example>
 		</example>
 	</section>
 	</section>

+ 1 - 0
modules/db_mysql/km_db_mysql.c

@@ -50,6 +50,7 @@
 
 
 unsigned int db_mysql_timeout_interval = 2;   /* Default is 6 seconds */
 unsigned int db_mysql_timeout_interval = 2;   /* Default is 6 seconds */
 unsigned int db_mysql_auto_reconnect = 1;     /* Default is enabled   */
 unsigned int db_mysql_auto_reconnect = 1;     /* Default is enabled   */
+unsigned int db_mysql_insert_all_delayed = 0; /* Default is off */
 
 
 /* MODULE_VERSION */
 /* MODULE_VERSION */
 
 

+ 1 - 0
modules/db_mysql/km_db_mysql.h

@@ -42,6 +42,7 @@
 
 
 extern unsigned int db_mysql_timeout_interval;
 extern unsigned int db_mysql_timeout_interval;
 extern unsigned int db_mysql_auto_reconnect;
 extern unsigned int db_mysql_auto_reconnect;
+extern unsigned int db_mysql_insert_all_delayed;
 
 
 int db_mysql_bind_api(db_func_t *dbb);
 int db_mysql_bind_api(db_func_t *dbb);
 
 

+ 8 - 3
modules/db_mysql/km_dbase.c

@@ -404,10 +404,15 @@ int db_mysql_raw_query(const db1_con_t* _h, const str* _s, db1_res_t** _r)
  * \param _n number of key=value pairs
  * \param _n number of key=value pairs
  * \return zero on success, negative value on failure
  * \return zero on success, negative value on failure
  */
  */
-int db_mysql_insert(const db1_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n)
+int db_mysql_insert(const db1_con_t* _h, const db_key_t* _k, const db_val_t* _v,
+		const int _n)
 {
 {
-	return db_do_insert(_h, _k, _v, _n, db_mysql_val2str,
-	db_mysql_submit_query);
+	if(unlikely(db_mysql_insert_all_delayed==1))
+		return db_do_insert_delayed(_h, _k, _v, _n, db_mysql_val2str,
+				db_mysql_submit_query);
+	else
+		return db_do_insert(_h, _k, _v, _n, db_mysql_val2str,
+				db_mysql_submit_query);
 }
 }
 
 
 
 

+ 1 - 0
modules/db_mysql/mysql_mod.c

@@ -107,6 +107,7 @@ static param_export_t params[] = {
 
 
 	{"timeout_interval", INT_PARAM, &db_mysql_timeout_interval},
 	{"timeout_interval", INT_PARAM, &db_mysql_timeout_interval},
 	{"auto_reconnect",   INT_PARAM, &db_mysql_auto_reconnect},
 	{"auto_reconnect",   INT_PARAM, &db_mysql_auto_reconnect},
+	{"insert_delayed",   INT_PARAM, &db_mysql_insert_all_delayed},
 	{0, 0, 0}
 	{0, 0, 0}
 };
 };
 
 

+ 22 - 15
modules/drouting/README

@@ -304,18 +304,18 @@ Chapter 1. Admin Guide
        draft 09):
        draft 09):
        Table 1.4. Time recurrence attributes
        Table 1.4. Time recurrence attributes
 
 
-   Attribute Description
-   dastard Start of interval (RFC 2445 DATE-TIME)
-   duration Length of interval (RFC 2445 DURATION)
-   freq Frequency of recurrence (secondly,minutely,hourly, daily,weekly,
-   monthly, or yearly).
-   until bound of recurrence (RFC 2445 DATE-TIME)
-   interval How often the recurrence repeats
-   byday List of days of the week
-   bymonthday List of days of the month
-   byyearday List of days of the year
-   byweekno List of weeks of the year
-   bymonth List of months of the year
+     Attribute                            Description
+     dastard    Start of interval (RFC 2445 DATE-TIME)
+     duration   Length of interval (RFC 2445 DURATION)
+     freq       Frequency of recurrence (secondly,minutely,hourly, daily,weekly,
+                monthly, or yearly).
+     until      bound of recurrence (RFC 2445 DATE-TIME)
+     interval   How often the recurrence repeats
+     byday      List of days of the week
+     bymonthday List of days of the month
+     byyearday  List of days of the year
+     byweekno   List of weeks of the year
+     bymonth    List of months of the year
        The value stored in database has the format of:
        The value stored in database has the format of:
        <dtstart>|<duration>|<freq>|<until>|<interval>|<byday>|<bymonthday>
        <dtstart>|<duration>|<freq>|<until>|<interval>|<byday>|<bymonthday>
        |<byyearday>|<byweekno>|<bymonth>
        |<byyearday>|<byweekno>|<bymonth>
@@ -570,9 +570,16 @@ modparam("drouting", "drl_table", "my_gw_lists")
        destinations). Ex: 1,2;3,4,5;6 -> randomizer -> (A) 2,1;4,3,5;6 ->
        destinations). Ex: 1,2;3,4,5;6 -> randomizer -> (A) 2,1;4,3,5;6 ->
        usage 2,1,4,3,5,6 (B) 1,2;3,5,4;6 -> usage 1,2,3,5,4,6
        usage 2,1,4,3,5,6 (B) 1,2;3,5,4;6 -> usage 1,2,3,5,4,6
      * 2 - from each destination group, only a single destination is
      * 2 - from each destination group, only a single destination is
-       randomly selected; groups do maintain their order (as given); Ex:
-       1,2;3,4,5;6 -> randomizer -> (A) 2;4;6 -> usage 2,4,6 (B) 1;5;6 ->
-       usage 1,5,6
+       randomly selected; groups do maintain their order (as given);
+       Ex: 1,2;3,4,5;6 -> randomizer ->
+       (A) 2;4;6 -> usage 2,4,6
+       (B) 1;5;6 -> usage 1,5,6
+       It is ok to have repeating gateways in different groups. The module
+       will take care internally in case of failure not to choose a
+       gateway that was tried already.
+       Ex: 1,2,3; 1,2,3; 1,2,3 -> no gateway will be choosen twice. So in
+       case there are 2 failures, all the three gateways (1,2,3) will be
+       tried in a random order.
 
 
    Default value is “0”.
    Default value is “0”.
 
 

+ 15 - 0
modules/drouting/doc/drouting_admin.xml

@@ -942,9 +942,24 @@ modparam("drouting", "drl_table", "my_gw_lists")
 			<emphasis>2</emphasis> - from each destination group, only a 
 			<emphasis>2</emphasis> - from each destination group, only a 
 			single destination is randomly selected; groups do maintain their
 			single destination is randomly selected; groups do maintain their
 			order (as given);
 			order (as given);
+			<para>
 			Ex: 1,2;3,4,5;6 -> randomizer ->
 			Ex: 1,2;3,4,5;6 -> randomizer ->
+			</para>
+			<para>
 			(A) 2;4;6  -> usage 2,4,6
 			(A) 2;4;6  -> usage 2,4,6
+			</para>
+			<para>
 			(B) 1;5;6  -> usage 1,5,6
 			(B) 1;5;6  -> usage 1,5,6
+			</para>
+			<para>
+			It is ok to have repeating gateways in different groups. The module will
+			take care internally in case of failure not to choose a gateway that
+			was tried already.
+			</para>
+			<para>
+			Ex: 1,2,3; 1,2,3; 1,2,3 -> no gateway will be choosen twice. So in case there
+			are 2 failures, all the three gateways (1,2,3) will be tried in a random order.
+			</para>
 		</listitem>
 		</listitem>
 		</itemizedlist>
 		</itemizedlist>
 		</para>
 		</para>

+ 38 - 0
modules/drouting/drouting.c

@@ -646,6 +646,20 @@ static int use_next_gw(struct sip_msg* msg)
 	return 1;
 	return 1;
 }
 }
 
 
+int dr_already_choosen(rt_info_t* rt_info, int* local_gwlist, int lgw_size, int check)
+{
+	int l;
+
+	for ( l = 0; l<lgw_size; l++ ) {
+		if ( rt_info->pgwl[local_gwlist[l]].pgw == rt_info->pgwl[check].pgw ) {
+			LM_INFO("Gateway already choosen %.*s, local_gwlist[%d]=%d, %d\n",
+					rt_info->pgwl[check].pgw->ip.len, rt_info->pgwl[check].pgw->ip.s, l, local_gwlist[l], check);
+			return 1;
+		}
+	}
+
+	return 0;
+}
 
 
 static int do_routing(struct sip_msg* msg, dr_group_t *drg)
 static int do_routing(struct sip_msg* msg, dr_group_t *drg)
 {
 {
@@ -806,6 +820,30 @@ again:
 					}
 					}
 				}
 				}
 			}
 			}
+
+			if ( sort_order == 2 ) {
+				/* check not to use the same gateway as before */
+				if ( t>1 ) {
+					/* check if all in the current set were already chosen */
+					if (i-j <= t-1) {
+						for( l = j; l< i; l++) {
+							if ( ! dr_already_choosen(rt_info, local_gwlist, t-1, l) )
+								break;
+						}
+						if ( l == i ) {
+							LM_INFO("All gateways in group from %d - %d were already used\n", j, i);
+							t--; /* jump over this group, nothing to choose here */
+							j=i; continue;
+						}
+					}
+					while ( dr_already_choosen(rt_info, local_gwlist, t-1, local_gwlist[t-1]) ) {
+						local_gwlist[t-1]   = j + rand()%(i-j);
+					}
+				}
+				LM_DBG("The %d gateway is %.*s [%d]\n", t, rt_info->pgwl[local_gwlist[t-1]].pgw->ip.len,
+						rt_info->pgwl[local_gwlist[t-1]].pgw->ip.s, local_gwlist[t-1]);
+			}
+
 			/* next group starts from i */
 			/* next group starts from i */
 			j=i;
 			j=i;
 		}
 		}

+ 4 - 4
modules/enum/enum.c

@@ -256,7 +256,7 @@ static inline int is_e164(str* _user)
 	int i;
 	int i;
 	char c;
 	char c;
 	
 	
-	if ((_user->len > 2) && (_user->len < 17) && ((_user->s)[0] == '+')) {
+	if ((_user->len > 2) && (_user->len < MAX_NUM_LEN) && ((_user->s)[0] == '+')) {
 		for (i = 1; i < _user->len; i++) {
 		for (i = 1; i < _user->len; i++) {
 			c = (_user->s)[i];
 			c = (_user->s)[i];
 			if ((c < '0') || (c > '9')) return -1;
 			if ((c < '0') || (c > '9')) return -1;
@@ -309,7 +309,7 @@ int is_from_user_enum_2(struct sip_msg* _msg, char* _suffix, char* _service)
 	struct naptr_rdata* naptr;
 	struct naptr_rdata* naptr;
 
 
 	str pattern, replacement, result;
 	str pattern, replacement, result;
-	char string[17];
+	char string[MAX_NUM_LEN];
 
 
 	if (parse_from_header(_msg) < 0) {
 	if (parse_from_header(_msg) < 0) {
 	    LM_ERR("Failed to parse From header\n");
 	    LM_ERR("Failed to parse From header\n");
@@ -754,7 +754,7 @@ int enum_query(struct sip_msg* _msg, str* suffix, str* service)
 	char *user_s;
 	char *user_s;
 	int user_len, i, j;
 	int user_len, i, j;
 	char name[MAX_DOMAIN_SIZE];
 	char name[MAX_DOMAIN_SIZE];
-	char string[17];
+	char string[MAX_NUM_LEN];
 
 
 	LM_DBG("enum_query on suffix <%.*s> service <%.*s>\n",
 	LM_DBG("enum_query on suffix <%.*s> service <%.*s>\n",
 	       suffix->len, suffix->s, service->len, service->s);
 	       suffix->len, suffix->s, service->len, service->s);
@@ -818,7 +818,7 @@ int i_enum_query_2(struct sip_msg* _msg, char* _suffix, char* _service)
 	int cc_len;
 	int cc_len;
 	struct rdata* head;
 	struct rdata* head;
 
 
-	char string[17];
+	char string[MAX_NUM_LEN];
 
 
 	str *suffix, *service;
 	str *suffix, *service;
 
 

+ 3 - 2
modules/enum/enum.h

@@ -37,8 +37,9 @@
 
 
 
 
 #define MAX_DOMAIN_SIZE 256
 #define MAX_DOMAIN_SIZE 256
-#define MAX_COMPONENT_SIZE 32  /* separator, apex, ... This simplifies checks */
-		
+#define MAX_NUM_LEN 22
+#define MAX_COMPONENT_SIZE (MAX_NUM_LEN * 2)  /* separator, apex, ... This simplifies checks */
+
 
 
 /*
 /*
  * Check if from user is an e164 number and has a naptr record
  * Check if from user is an e164 number and has a naptr record

+ 1 - 1
modules/msrp/msrp_parser.c

@@ -538,7 +538,7 @@ int msrp_parse_uri(char *start, int len, msrp_uri_t *uri)
 
 
 error:
 error:
 	LM_ERR("parsing error in [%.*s] at [%ld]\n",
 	LM_ERR("parsing error in [%.*s] at [%ld]\n",
-			len, start, s - start);
+			len, start, (long int)(s - start));
 	memset(uri, 0, sizeof(msrp_uri_t));
 	memset(uri, 0, sizeof(msrp_uri_t));
 	return -1;
 	return -1;
 }
 }

+ 109 - 38
modules/outbound/ob_mod.c

@@ -206,7 +206,7 @@ int decode_flow_token(struct receive_info *rcv, str flow_token)
 
 
 	if (flow_token.s == NULL)
 	if (flow_token.s == NULL)
 	{
 	{
-		LM_INFO("no flow-token provided\n");
+		LM_DBG("no flow-token provided\n");
 		return -2;
 		return -2;
 	}
 	}
 
 
@@ -275,29 +275,10 @@ int decode_flow_token(struct receive_info *rcv, str flow_token)
 	return 0;
 	return 0;
 }
 }
 
 
-int use_outbound(struct sip_msg *msg)
+static int use_outbound_register(struct sip_msg *msg)
 {
 {
 	contact_t *contact;
 	contact_t *contact;
-	rr_t *rt;
-	struct sip_uri puri;
-	param_hooks_t hooks;
-	param_t *params;
-	int ret;
-
-	/* If Outbound is forced return success without any further checks */
-	if (ob_force_flag != -1 && isflagset(msg, ob_force_flag) > 0)
-	{
-		LM_INFO("outbound forced\n");
-		return 1;
-	}
-
-	/* Use Outbound when there is a single Via: header and:
-	    # It's a REGISTER request with a Contact-URI containing a ;reg-id
-	      parameter, or
-	    # The Contact-URI has an ;ob parameter, or
-	    # The top Route-URI points to use and has an ;ob parameter
-	*/
-
+	
 	/* Check there is a single Via: */
 	/* Check there is a single Via: */
 	if (!(parse_headers(msg, HDR_VIA2_F, 0) == -1 || msg->via2 == 0
 	if (!(parse_headers(msg, HDR_VIA2_F, 0) == -1 || msg->via2 == 0
 		|| msg->via2->error != PARSE_OK))
 		|| msg->via2->error != PARSE_OK))
@@ -306,8 +287,7 @@ int use_outbound(struct sip_msg *msg)
 		return 0;
 		return 0;
 	}
 	}
 
 
-	/* Look for ;reg-id in REGISTER Contact-URIs and ;ob in any
-	   Contact-URIs */
+	/* Look for ;reg-id in Contact-URIs */
 	if (msg->contact
 	if (msg->contact
 		|| (parse_headers(msg, HDR_CONTACT_F, 0) != -1 && msg->contact))
 		|| (parse_headers(msg, HDR_CONTACT_F, 0) != -1 && msg->contact))
 	{
 	{
@@ -323,21 +303,29 @@ int use_outbound(struct sip_msg *msg)
 			return 0;
 			return 0;
 		}
 		}
 		
 		
-		if (msg->REQ_METHOD == METHOD_REGISTER && contact->reg_id)
+		if (contact->reg_id)
 		{
 		{
 			LM_INFO("found REGISTER with ;reg-id paramter on"
 			LM_INFO("found REGISTER with ;reg-id paramter on"
 				" Contact-URI - outbound used\n");
 				" Contact-URI - outbound used\n");
 			return 1;
 			return 1;
 		}
 		}
 
 
-		if (hooks.contact.ob)
-		{
-			LM_INFO("found ;ob parameter on Contact-URI - outbound"
-				" used\n");
-			return 1;
-		}
 	}
 	}
 
 
+	LM_INFO("outbound not used\n");
+	return 0;
+}
+
+static int use_outbound_non_reg(struct sip_msg *msg)
+{
+	contact_t *contact;
+	rr_t *rt;
+	struct sip_uri puri;
+	param_hooks_t hooks;
+	param_t *params;
+	int ret;
+	struct receive_info rcv;
+
 	/* Check to see if the top Route-URI is me and has a ;ob parameter */
 	/* Check to see if the top Route-URI is me and has a ;ob parameter */
 	if (msg->route
 	if (msg->route
 		|| (parse_headers(msg, HDR_ROUTE_F, 0) != -1 && msg->route))
 		|| (parse_headers(msg, HDR_ROUTE_F, 0) != -1 && msg->route))
@@ -359,13 +347,6 @@ int use_outbound(struct sip_msg *msg)
 			LM_ERR("parsing Route-URI\n");
 			LM_ERR("parsing Route-URI\n");
 			return 0;
 			return 0;
 		}
 		}
-		if (parse_params(&puri.params, CLASS_URI, &hooks,
-			&params) != 0)
-		{
-			LM_ERR("parsing Route-URI parameters\n");
-			return 0;
-		}
-
 		ret = check_self(&puri.host,
 		ret = check_self(&puri.host,
 				puri.port_no ? puri.port_no : SIP_PORT, 0);
 				puri.port_no ? puri.port_no : SIP_PORT, 0);
 		if (ret < 1 || (ret == 1 && puri.gr.s != NULL))
 		if (ret < 1 || (ret == 1 && puri.gr.s != NULL))
@@ -377,10 +358,73 @@ int use_outbound(struct sip_msg *msg)
 			return 0;
 			return 0;
 		}
 		}
 
 
+		if (parse_params(&puri.params, CLASS_URI, &hooks,
+			&params) != 0)
+		{
+			LM_ERR("parsing Route-URI parameters\n");
+			return 0;
+		}
+
 		if (hooks.uri.ob)
 		if (hooks.uri.ob)
 		{
 		{
 			LM_INFO("found ;ob parameter on Route-URI - outbound"
 			LM_INFO("found ;ob parameter on Route-URI - outbound"
 				" used\n");
 				" used\n");
+
+			if (decode_flow_token(&rcv, puri.user) == 0)
+			{
+				if (!ip_addr_cmp(&rcv.src_ip, &msg->rcv.src_ip)
+					|| rcv.src_port != msg->rcv.src_port)
+				{
+					LM_INFO("\"incoming\" request found\n");
+					return 2;
+				}
+			}
+
+			LM_INFO("\"outgoing\" request found\n");
+			return 1;
+		}
+	}
+
+	/* Check there is a single Via: */
+	if (!(parse_headers(msg, HDR_VIA2_F, 0) == -1 || msg->via2 == 0
+		|| msg->via2->error != PARSE_OK))
+	{
+		LM_INFO("second Via: found - outbound not used\n");
+		return 0;
+	}
+
+	/* Look for ;ob in Contact-URIs */
+	if (msg->contact
+		|| (parse_headers(msg, HDR_CONTACT_F, 0) != -1 && msg->contact))
+	{
+		if (parse_contact(msg->contact) < 0)
+		{
+			LM_ERR("parsing Contact: header body\n");
+			return 0;
+		}
+		contact = ((contact_body_t *) msg->contact->parsed)->contacts;
+		if (!contact)
+		{
+			LM_ERR("empty Contact:\n");
+			return 0;
+		}
+	
+		if (parse_uri(contact->uri.s, contact->uri.len, &puri)
+			< 0)
+		{
+			LM_ERR("parsing Contact-URI\n");
+			return 0;
+		}
+		if (parse_params(&puri.params, CLASS_CONTACT, &hooks, &params)
+			!= 0)
+		{
+			LM_ERR("parsing Contact-URI parameters\n");
+			return 0;
+		}
+		if (hooks.contact.ob)
+		{
+			LM_INFO("found ;ob parameter on Contact-URI - outbound"
+				" used\n");
 			return 1;
 			return 1;
 		}
 		}
 	}
 	}
@@ -389,6 +433,33 @@ int use_outbound(struct sip_msg *msg)
 	return 0;
 	return 0;
 }
 }
 
 
+int use_outbound(struct sip_msg *msg)
+{
+	if (msg->first_line.type != SIP_REQUEST)
+	{
+		LM_ERR("use_outbound called for something that isn't a SIP"
+			" request\n");
+		return 0;
+	}
+
+	/* If Outbound is forced return success without any further checks */
+	if (ob_force_flag != -1 && isflagset(msg, ob_force_flag) > 0)
+	{
+		LM_DBG("outbound forced\n");
+		return 1;
+	}
+
+	LM_INFO("Analysing %.*s for outbound markers\n",
+		msg->first_line.u.request.method.len,
+		msg->first_line.u.request.method.s);
+
+	if (msg->REQ_METHOD == METHOD_REGISTER)
+		return use_outbound_register(msg);
+	else
+		return use_outbound_non_reg(msg);
+
+}
+
 int bind_ob(struct ob_binds *pxb)
 int bind_ob(struct ob_binds *pxb)
 {
 {
 	if (pxb == NULL)
 	if (pxb == NULL)

+ 2 - 0
modules/p_usrloc/udomain.c

@@ -589,6 +589,7 @@ done:
  * \param _d cleaned domain
  * \param _d cleaned domain
  * \return 0 on success, -1 on failure
  * \return 0 on success, -1 on failure
  */
  */
+/*
 int db_timer_udomain(udomain_t* _d)
 int db_timer_udomain(udomain_t* _d)
 {
 {
 	db_key_t keys[2];
 	db_key_t keys[2];
@@ -611,6 +612,7 @@ int db_timer_udomain(udomain_t* _d)
 	//if (ul_db_layer_delete(_d, NULL, NULL, keys, ops, vals, 2) < 0) { //FIXME
 	//if (ul_db_layer_delete(_d, NULL, NULL, keys, ops, vals, 2) < 0) { //FIXME
 	return 0;
 	return 0;
 }
 }
+*/
 
 
 
 
 
 

+ 2 - 0
modules/p_usrloc/udomain.h

@@ -95,7 +95,9 @@ void print_udomain(FILE* _f, udomain_t* _d);
  * \param _d cleaned domain
  * \param _d cleaned domain
  * \return 0 on success, -1 on failure
  * \return 0 on success, -1 on failure
  */
  */
+/*
 int db_timer_udomain(udomain_t* _d);
 int db_timer_udomain(udomain_t* _d);
+*/
 
 
 
 
 /*!
 /*!

+ 7 - 4
modules/rr/loose.c

@@ -513,12 +513,15 @@ static inline int process_outbound(struct sip_msg *_m, str flow_token,
 	ret = rr_obb.decode_flow_token(&rcv, flow_token);
 	ret = rr_obb.decode_flow_token(&rcv, flow_token);
 
 
 	if (ret == -2) {
 	if (ret == -2) {
-		LM_INFO("no flow token found - outbound not in use\n");
+		LM_DBG("no flow token found - outbound not in use\n");
 		return 0;
 		return 0;
 	} else if (ret == -1) {
 	} else if (ret == -1) {
 		LM_ERR("failed to decode flow token\n");
 		LM_ERR("failed to decode flow token\n");
 		return -1;
 		return -1;
-	} else {
+	} else if (!ip_addr_cmp(&rcv.src_ip, &_m->rcv.src_ip)
+			|| rcv.src_port != _m->rcv.src_port) {
+		LM_DBG("\"incoming\" request found. Using flow-token for"
+			"routing\n");
 
 
 		/* First, force the local socket */
 		/* First, force the local socket */
 		si = find_si(&rcv.dst_ip, rcv.dst_port, rcv.proto);
 		si = find_si(&rcv.dst_ip, rcv.dst_port, rcv.proto);
@@ -546,9 +549,9 @@ static inline int process_outbound(struct sip_msg *_m, str flow_token,
 					rcv.src_ip.af == AF_INET6 ? "]" : "",
 					rcv.src_ip.af == AF_INET6 ? "]" : "",
 					rcv.src_port,
 					rcv.src_port,
 					get_proto_name(rcv.proto));
 					get_proto_name(rcv.proto));
-
-		return 1;
 	}
 	}
+
+	return 1;
 }
 }
 
 
 /*!
 /*!

+ 136 - 25
modules/rr/record.c

@@ -37,14 +37,18 @@
 #include "../../dprint.h"
 #include "../../dprint.h"
 #include "../../parser/parse_uri.h"
 #include "../../parser/parse_uri.h"
 #include "../../parser/parse_from.h"
 #include "../../parser/parse_from.h"
+#include "../../parser/parse_rr.h"
 #include "../../str.h"
 #include "../../str.h"
 #include "../../data_lump.h"
 #include "../../data_lump.h"
 #include "record.h"
 #include "record.h"
 #include "rr_mod.h"
 #include "rr_mod.h"
 
 
 
 
-#define RR_PREFIX "Record-Route: <sip:"
-#define RR_PREFIX_LEN (sizeof(RR_PREFIX)-1)
+#define RR_PREFIX_SIP "Record-Route: <sip:"
+#define RR_PREFIX_SIP_LEN (sizeof(RR_PREFIX_SIP)-1)
+
+#define RR_PREFIX_SIPS "Record-Route: <sip:"
+#define RR_PREFIX_SIPS_LEN (sizeof(RR_PREFIX_SIPS)-1)
 
 
 #define RR_LR ";lr"
 #define RR_LR ";lr"
 #define RR_LR_LEN (sizeof(RR_LR)-1)
 #define RR_LR_LEN (sizeof(RR_LR)-1)
@@ -78,6 +82,15 @@ static unsigned int rr_param_msg;
 static pv_spec_t *custom_user_avp;		/*!< AVP for custom_user setting */
 static pv_spec_t *custom_user_avp;		/*!< AVP for custom_user setting */
 
 
 
 
+inline static int rr_is_sips(sip_msg_t *_m)
+{
+	if(parse_sip_msg_uri(_m)<0)
+		return 0;
+	if(_m->parsed_uri.type==SIPS_URI_T)
+		return 1;
+	return 0;
+}
+
 void init_custom_user(pv_spec_t *custom_user_avp_p)
 void init_custom_user(pv_spec_t *custom_user_avp_p)
 {
 {
     custom_user_avp = custom_user_avp_p;
     custom_user_avp = custom_user_avp_p;
@@ -187,13 +200,23 @@ static inline struct lump *insert_rr_param_lump(struct lump *before,
  * \return 0 on success, negative on failure
  * \return 0 on success, negative on failure
  */
  */
 static inline int build_rr(struct lump* _l, struct lump* _l2, str* user,
 static inline int build_rr(struct lump* _l, struct lump* _l2, str* user,
-				str *tag, str *params, int _inbound, int _use_ob)
+				str *tag, str *params, int _inbound, int _use_ob, int _sips)
 {
 {
 	char* prefix, *suffix, *term, *r2;
 	char* prefix, *suffix, *term, *r2;
 	int suffix_len, prefix_len;
 	int suffix_len, prefix_len;
 	char *p;
 	char *p;
+	char *rr_prefix;
+	int rr_prefix_len;
 
 
-	prefix_len = RR_PREFIX_LEN + (user->len ? (user->len + 1) : 0);
+	if(_sips==0) {
+		rr_prefix = RR_PREFIX_SIP;
+		rr_prefix_len = RR_PREFIX_SIP_LEN;
+	} else {
+		rr_prefix = RR_PREFIX_SIPS;
+		rr_prefix_len = RR_PREFIX_SIPS_LEN;
+	}
+
+	prefix_len = rr_prefix_len + (user->len ? (user->len + 1) : 0);
 	if (enable_full_lr) {
 	if (enable_full_lr) {
 		suffix_len = RR_LR_FULL_LEN + (params?params->len:0) +
 		suffix_len = RR_LR_FULL_LEN + (params?params->len:0) +
 				((tag && tag->len) ? (RR_FROMTAG_LEN + tag->len) : 0);
 				((tag && tag->len) ? (RR_FROMTAG_LEN + tag->len) : 0);
@@ -216,21 +239,21 @@ static inline int build_rr(struct lump* _l, struct lump* _l2, str* user,
 		return -3;
 		return -3;
 	}
 	}
 	
 	
-	memcpy(prefix, RR_PREFIX, RR_PREFIX_LEN);
+	memcpy(prefix, rr_prefix, rr_prefix_len);
 	if (user->len) {
 	if (user->len) {
-		memcpy(prefix + RR_PREFIX_LEN, user->s, user->len);
+		memcpy(prefix + rr_prefix_len, user->s, user->len);
 #ifdef ENABLE_USER_CHECK
 #ifdef ENABLE_USER_CHECK
 		/* don't add the ignored user into a RR */
 		/* don't add the ignored user into a RR */
 		if(i_user.len && i_user.len == user->len && 
 		if(i_user.len && i_user.len == user->len && 
 				!strncmp(i_user.s, user->s, i_user.len))
 				!strncmp(i_user.s, user->s, i_user.len))
 		{
 		{
-			if(prefix[RR_PREFIX_LEN]=='x')
-				prefix[RR_PREFIX_LEN]='y';
+			if(prefix[rr_prefix_len]=='x')
+				prefix[rr_prefix_len]='y';
 			else
 			else
-				prefix[RR_PREFIX_LEN]='x';
+				prefix[rr_prefix_len]='x';
 		}
 		}
 #endif
 #endif
-		prefix[RR_PREFIX_LEN + user->len] = '@';
+		prefix[rr_prefix_len + user->len] = '@';
 	}
 	}
 
 
 	p = suffix;
 	p = suffix;
@@ -294,6 +317,51 @@ lump_err:
 	return -4;
 	return -4;
 }
 }
 
 
+/*!
+ * \brief Copy flow-token from top-Route: to a string
+ *
+ * Copy the user part of the top-Route: to a string (allocating private memory
+ * for this).
+ * \param token where the user-part of the top-Route: will be copied to
+ * \param _m the SIP message to extract the top-Route: from
+ * \return 0 on success, negative on failure
+ */
+static int copy_flow_token(str *token, struct sip_msg *_m)
+{
+	rr_t *rt;
+	struct sip_uri puri;
+
+	if (_m->route
+	    || (parse_headers(_m, HDR_ROUTE_F, 0) != -1 && _m->route)) {
+		if (parse_rr(_m->route) < 0) {
+			LM_ERR("parsing Route: header body\n");
+			return -1;
+		}
+		rt = (rr_t *) _m->route->parsed;
+		if (!rt) {
+			LM_ERR("empty Route:\n");
+			return -1;
+		}
+		if (parse_uri(rt->nameaddr.uri.s, rt->nameaddr.uri.len,
+				&puri) < 0) {
+			LM_ERR("parsing Route-URI\n");
+			return -1;
+		}
+
+		token->s = pkg_malloc(puri.user.len * sizeof(char));
+		if (token->s == NULL) {
+			LM_ERR("allocating memory\n");
+			return -1;
+		}
+		memcpy(token->s, puri.user.s, puri.user.len);
+		token->len = puri.user.len;
+		return 0;
+	}
+
+	LM_ERR("no Route: headers found\n");
+	return -1;
+}
+
 
 
 /*!
 /*!
  * \brief Insert a new Record-Route header field with lr parameter
  * \brief Insert a new Record-Route header field with lr parameter
@@ -311,6 +379,7 @@ int record_route(struct sip_msg* _m, str *params)
 	struct to_body* from = NULL;
 	struct to_body* from = NULL;
 	str* tag;
 	str* tag;
 	int use_ob = rr_obb.use_outbound ? rr_obb.use_outbound(_m) : 0;
 	int use_ob = rr_obb.use_outbound ? rr_obb.use_outbound(_m) : 0;
+	int sips;
 	
 	
 	user.len = 0;
 	user.len = 0;
 	
 	
@@ -322,11 +391,16 @@ int record_route(struct sip_msg* _m, str *params)
 				return -1;
 				return -1;
 			}
 			}
 		}
 		}
-	} else if (use_ob) {
+	} else if (use_ob == 1) {
 		if (rr_obb.encode_flow_token(&user, _m->rcv) != 0) {
 		if (rr_obb.encode_flow_token(&user, _m->rcv) != 0) {
 			LM_ERR("encoding outbound flow-token\n");
 			LM_ERR("encoding outbound flow-token\n");
 			return -1;
 			return -1;
 		}
 		}
+	} else if (use_ob == 2) {
+		if (copy_flow_token(&user, _m) != 0) {
+			LM_ERR("copying outbound flow-token\n");
+			return -1;
+		}
 	}
 	}
 
 
 	if (append_fromtag) {
 	if (append_fromtag) {
@@ -345,6 +419,8 @@ int record_route(struct sip_msg* _m, str *params)
 		rr_param_buf.len = 0;
 		rr_param_buf.len = 0;
 	}
 	}
 
 
+	sips = rr_is_sips(_m);
+
 	if (enable_double_rr && !use_ob) {
 	if (enable_double_rr && !use_ob) {
 		l = anchor_lump(_m, _m->headers->name.s - _m->buf,0,HDR_RECORDROUTE_T);
 		l = anchor_lump(_m, _m->headers->name.s - _m->buf,0,HDR_RECORDROUTE_T);
 		l2 = anchor_lump(_m, _m->headers->name.s - _m->buf, 0, 0);
 		l2 = anchor_lump(_m, _m->headers->name.s - _m->buf, 0, 0);
@@ -358,7 +434,7 @@ int record_route(struct sip_msg* _m, str *params)
 			LM_ERR("failed to insert conditional lump\n");
 			LM_ERR("failed to insert conditional lump\n");
 			return -6;
 			return -6;
 		}
 		}
-		if (build_rr(l, l2, &user, tag, params, OUTBOUND, 0) < 0) {
+		if (build_rr(l, l2, &user, tag, params, OUTBOUND, 0, sips) < 0) {
 			LM_ERR("failed to insert outbound Record-Route\n");
 			LM_ERR("failed to insert outbound Record-Route\n");
 			return -7;
 			return -7;
 		}
 		}
@@ -372,7 +448,7 @@ int record_route(struct sip_msg* _m, str *params)
 	}
 	}
 	
 	
 	if (build_rr(l, l2, &user, tag, params, use_ob ? OUTBOUND : INBOUND,
 	if (build_rr(l, l2, &user, tag, params, use_ob ? OUTBOUND : INBOUND,
-			use_ob) < 0) {
+			use_ob, sips) < 0) {
 		LM_ERR("failed to insert inbound Record-Route\n");
 		LM_ERR("failed to insert inbound Record-Route\n");
 		return -4;
 		return -4;
 	}
 	}
@@ -401,7 +477,19 @@ int record_route_preset(struct sip_msg* _m, str* _data)
 	char* hdr, *p;
 	char* hdr, *p;
 	int hdr_len;
 	int hdr_len;
 	int use_ob = rr_obb.use_outbound ? rr_obb.use_outbound(_m) : 0;
 	int use_ob = rr_obb.use_outbound ? rr_obb.use_outbound(_m) : 0;
-	
+	char *rr_prefix;
+	int rr_prefix_len;
+	int sips;
+
+	sips = rr_is_sips(_m);
+	if(sips==0) {
+		rr_prefix = RR_PREFIX_SIP;
+		rr_prefix_len = RR_PREFIX_SIP_LEN;
+	} else {
+		rr_prefix = RR_PREFIX_SIPS;
+		rr_prefix_len = RR_PREFIX_SIPS_LEN;
+	}
+
 	from = 0;
 	from = 0;
 	user.len = 0;
 	user.len = 0;
 	user.s = 0;
 	user.s = 0;
@@ -411,11 +499,16 @@ int record_route_preset(struct sip_msg* _m, str* _data)
 			LM_ERR("failed to extract username\n");
 			LM_ERR("failed to extract username\n");
 			return -1;
 			return -1;
 		}
 		}
-	} else if (use_ob) {
+	} else if (use_ob == 1) {
 		if (rr_obb.encode_flow_token(&user, _m->rcv) != 0) {
 		if (rr_obb.encode_flow_token(&user, _m->rcv) != 0) {
 			LM_ERR("encoding outbound flow-token\n");
 			LM_ERR("encoding outbound flow-token\n");
 			return -1;
 			return -1;
 		}
 		}
+	} else if (use_ob == 2) {
+		if (copy_flow_token(&user, _m) != 0) {
+			LM_ERR("copying outbound flow-token\n");
+			return -1;
+		}
 	}
 	}
 
 
 	if (append_fromtag) {
 	if (append_fromtag) {
@@ -432,7 +525,7 @@ int record_route_preset(struct sip_msg* _m, str* _data)
 		return -3;
 		return -3;
 	}
 	}
 
 
-	hdr_len = RR_PREFIX_LEN;
+	hdr_len = rr_prefix_len;
 	if (user.len)
 	if (user.len)
 		hdr_len += user.len + 1; /* @ */
 		hdr_len += user.len + 1; /* @ */
 	hdr_len += _data->len;
 	hdr_len += _data->len;
@@ -456,8 +549,8 @@ int record_route_preset(struct sip_msg* _m, str* _data)
 	}
 	}
 
 
 	p = hdr;
 	p = hdr;
-	memcpy(p, RR_PREFIX, RR_PREFIX_LEN);
-	p += RR_PREFIX_LEN;
+	memcpy(p, rr_prefix, rr_prefix_len);
+	p += rr_prefix_len;
 
 
 	if (user.len) {
 	if (user.len) {
 		memcpy(p, user.s, user.len);
 		memcpy(p, user.s, user.len);
@@ -508,13 +601,23 @@ int record_route_preset(struct sip_msg* _m, str* _data)
 #define RR_TRANS_LEN 11
 #define RR_TRANS_LEN 11
 #define RR_TRANS ";transport="
 #define RR_TRANS ";transport="
 static inline int build_advertised_rr(struct lump* _l, struct lump* _l2, str *_data,
 static inline int build_advertised_rr(struct lump* _l, struct lump* _l2, str *_data,
-				str* user, str *tag, int _inbound, int _use_ob)
+				str* user, str *tag, int _inbound, int _use_ob, int _sips)
 {
 {
 	char *p;
 	char *p;
 	char *hdr, *trans, *r2, *suffix, *term;
 	char *hdr, *trans, *r2, *suffix, *term;
 	int hdr_len, suffix_len;
 	int hdr_len, suffix_len;
+	char *rr_prefix;
+	int rr_prefix_len;
 
 
-	hdr_len = RR_PREFIX_LEN;
+	if(_sips==0) {
+		rr_prefix = RR_PREFIX_SIP;
+		rr_prefix_len = RR_PREFIX_SIP_LEN;
+	} else {
+		rr_prefix = RR_PREFIX_SIPS;
+		rr_prefix_len = RR_PREFIX_SIPS_LEN;
+	}
+
+	hdr_len = rr_prefix_len;
 	if (user && user->len)
 	if (user && user->len)
 		hdr_len += user->len + 1; /* @ */
 		hdr_len += user->len + 1; /* @ */
 	hdr_len += _data->len;
 	hdr_len += _data->len;
@@ -546,8 +649,8 @@ static inline int build_advertised_rr(struct lump* _l, struct lump* _l2, str *_d
 	}
 	}
 
 
 	p = hdr;
 	p = hdr;
-	memcpy(p, RR_PREFIX, RR_PREFIX_LEN);
-	p += RR_PREFIX_LEN;
+	memcpy(p, rr_prefix, rr_prefix_len);
+	p += rr_prefix_len;
 
 
 	if (user->len) {
 	if (user->len) {
 		memcpy(p, user->s, user->len);
 		memcpy(p, user->s, user->len);
@@ -625,6 +728,7 @@ int record_route_advertised_address(struct sip_msg* _m, str* _data)
 	struct lump* l;
 	struct lump* l;
 	struct lump* l2;
 	struct lump* l2;
 	int use_ob = rr_obb.use_outbound ? rr_obb.use_outbound(_m) : 0;
 	int use_ob = rr_obb.use_outbound ? rr_obb.use_outbound(_m) : 0;
+	int sips;
 	
 	
 	user.len = 0;
 	user.len = 0;
 	user.s = 0;
 	user.s = 0;
@@ -634,11 +738,16 @@ int record_route_advertised_address(struct sip_msg* _m, str* _data)
 			LM_ERR("failed to extract username\n");
 			LM_ERR("failed to extract username\n");
 			return -1;
 			return -1;
 		}
 		}
-	} else if (use_ob) {
+	} else if (use_ob == 1) {
 		if (rr_obb.encode_flow_token(&user, _m->rcv) != 0) {
 		if (rr_obb.encode_flow_token(&user, _m->rcv) != 0) {
 			LM_ERR("encoding outbound flow-token\n");
 			LM_ERR("encoding outbound flow-token\n");
 			return -1;
 			return -1;
 		}
 		}
+	} else if (use_ob == 2) {
+		if (copy_flow_token(&user, _m) != 0) {
+			LM_ERR("copying outbound flow-token\n");
+			return -1;
+		}
 	}
 	}
 
 
 	if (append_fromtag) {
 	if (append_fromtag) {
@@ -649,6 +758,8 @@ int record_route_advertised_address(struct sip_msg* _m, str* _data)
 		tag = &((struct to_body*)_m->from->parsed)->tag_value;
 		tag = &((struct to_body*)_m->from->parsed)->tag_value;
 	}
 	}
 
 
+	sips = rr_is_sips(_m);
+
 	if (enable_double_rr && !use_ob) {
 	if (enable_double_rr && !use_ob) {
 		l = anchor_lump(_m, _m->headers->name.s - _m->buf,0,HDR_RECORDROUTE_T);
 		l = anchor_lump(_m, _m->headers->name.s - _m->buf,0,HDR_RECORDROUTE_T);
 		l2 = anchor_lump(_m, _m->headers->name.s - _m->buf, 0, 0);
 		l2 = anchor_lump(_m, _m->headers->name.s - _m->buf, 0, 0);
@@ -663,7 +774,7 @@ int record_route_advertised_address(struct sip_msg* _m, str* _data)
 			return -4;
 			return -4;
 		}
 		}
 		if (build_advertised_rr(l, l2, _data, &user, tag, OUTBOUND,
 		if (build_advertised_rr(l, l2, _data, &user, tag, OUTBOUND,
-					0) < 0) {
+					0, sips) < 0) {
 			LM_ERR("failed to insert outbound Record-Route\n");
 			LM_ERR("failed to insert outbound Record-Route\n");
 			return -5;
 			return -5;
 		}
 		}
@@ -678,7 +789,7 @@ int record_route_advertised_address(struct sip_msg* _m, str* _data)
 	
 	
 	if (build_advertised_rr(l, l2, _data, &user, tag,
 	if (build_advertised_rr(l, l2, _data, &user, tag,
 				use_ob ? OUTBOUND: INBOUND,
 				use_ob ? OUTBOUND: INBOUND,
-				use_ob) < 0) {
+				use_ob, sips) < 0) {
 		LM_ERR("failed to insert outbound Record-Route\n");
 		LM_ERR("failed to insert outbound Record-Route\n");
 		return -7;
 		return -7;
 	}
 	}

+ 4 - 4
modules/rtpproxy/README

@@ -77,7 +77,7 @@ Carsten Bock
 
 
         6. Exported Pseudo Variables
         6. Exported Pseudo Variables
 
 
-              6.1. $rtpstart
+              6.1. $rtpstat
 
 
         7. MI Commands
         7. MI Commands
 
 
@@ -145,7 +145,7 @@ Chapter 1. Admin Guide
 
 
    6. Exported Pseudo Variables
    6. Exported Pseudo Variables
 
 
-        6.1. $rtpstart
+        6.1. $rtpstat
 
 
    7. MI Commands
    7. MI Commands
 
 
@@ -659,9 +659,9 @@ start_recording();
 
 
 6. Exported Pseudo Variables
 6. Exported Pseudo Variables
 
 
-   6.1. $rtpstart
+   6.1. $rtpstat
 
 
-6.1. $rtpstart
+6.1. $rtpstat
 
 
    Returns the RTP-Statistics from the RTP-Proxy. The RTP-Statistics from
    Returns the RTP-Statistics from the RTP-Proxy. The RTP-Statistics from
    the RTP-Proxy are provided as a string and it does contain several
    the RTP-Proxy are provided as a string and it does contain several

+ 1 - 1
modules/rtpproxy/doc/rtpproxy_admin.xml

@@ -761,7 +761,7 @@ start_recording();
 	<section>
 	<section>
 		<title>Exported Pseudo Variables</title>
 		<title>Exported Pseudo Variables</title>
 		<section>
 		<section>
-			<title><function moreinfo="none">$rtpstart</function></title>
+			<title><function moreinfo="none">$rtpstat</function></title>
 			<para>
 			<para>
 			Returns the RTP-Statistics from the RTP-Proxy. The RTP-Statistics from the RTP-Proxy
 			Returns the RTP-Statistics from the RTP-Proxy. The RTP-Statistics from the RTP-Proxy
 			are provided as a string and it does contain several packet-counters. The statistics
 			are provided as a string and it does contain several packet-counters. The statistics

+ 55 - 19
modules/rtpproxy/rtpproxy.c

@@ -283,7 +283,8 @@ static int alter_mediaport(struct sip_msg *, str *, str *, str *, int);
 static int alter_rtcp(struct sip_msg *msg, str *body, str *oldport, str *newport);
 static int alter_rtcp(struct sip_msg *msg, str *body, str *oldport, str *newport);
 static char *gencookie();
 static char *gencookie();
 static int rtpp_test(struct rtpp_node*, int, int);
 static int rtpp_test(struct rtpp_node*, int, int);
-static int unforce_rtp_proxy_f(struct sip_msg *, char *, char *);
+static int unforce_rtp_proxy1_f(struct sip_msg *, char *, char *);
+static int unforce_rtp_proxy(struct sip_msg *, char *);
 static int force_rtp_proxy(struct sip_msg *, char *, char *, int, int);
 static int force_rtp_proxy(struct sip_msg *, char *, char *, int, int);
 static int start_recording_f(struct sip_msg *, char *, char *);
 static int start_recording_f(struct sip_msg *, char *, char *);
 static int rtpproxy_answer1_f(struct sip_msg *, char *, char *);
 static int rtpproxy_answer1_f(struct sip_msg *, char *, char *);
@@ -359,17 +360,17 @@ static cmd_export_t cmds[] = {
 	{"set_rtp_proxy_set",  (cmd_function)set_rtp_proxy_set_f,    1,
 	{"set_rtp_proxy_set",  (cmd_function)set_rtp_proxy_set_f,    1,
 		fixup_set_id, 0,
 		fixup_set_id, 0,
 		ANY_ROUTE},
 		ANY_ROUTE},
-	{"unforce_rtp_proxy",  (cmd_function)unforce_rtp_proxy_f,    0,
+	{"unforce_rtp_proxy",  (cmd_function)unforce_rtp_proxy1_f,   0,
 		0, 0,
 		0, 0,
 		ANY_ROUTE},
 		ANY_ROUTE},
-	{"rtpproxy_destroy",   (cmd_function)unforce_rtp_proxy_f,    0,
+	{"rtpproxy_destroy",   (cmd_function)unforce_rtp_proxy1_f,   0,
 		0, 0,
 		0, 0,
 		ANY_ROUTE},
 		ANY_ROUTE},
-	{"unforce_rtp_proxy",  (cmd_function)unforce_rtp_proxy_f,    1,
-		0, 0,
+	{"unforce_rtp_proxy",  (cmd_function)unforce_rtp_proxy1_f,   1,
+		fixup_spve_null, 0,
 		ANY_ROUTE},
 		ANY_ROUTE},
-	{"rtpproxy_destroy",   (cmd_function)unforce_rtp_proxy_f,    1,
-		0, 0,
+	{"rtpproxy_destroy",   (cmd_function)unforce_rtp_proxy1_f,   1,
+		fixup_spve_null, 0,
 		ANY_ROUTE},
 		ANY_ROUTE},
 	{"start_recording",    (cmd_function)start_recording_f,      0,
 	{"start_recording",    (cmd_function)start_recording_f,      0,
 		0, 0,
 		0, 0,
@@ -378,19 +379,19 @@ static cmd_export_t cmds[] = {
 		0, 0,
 		0, 0,
 		ANY_ROUTE},
 		ANY_ROUTE},
 	{"rtpproxy_offer",	(cmd_function)rtpproxy_offer1_f,     1,
 	{"rtpproxy_offer",	(cmd_function)rtpproxy_offer1_f,     1,
-		0, 0,
+		fixup_spve_null, 0,
 		ANY_ROUTE},
 		ANY_ROUTE},
 	{"rtpproxy_offer",	(cmd_function)rtpproxy_offer2_f,     2,
 	{"rtpproxy_offer",	(cmd_function)rtpproxy_offer2_f,     2,
-		0, 0,
+		fixup_spve_spve, 0,
 		ANY_ROUTE},
 		ANY_ROUTE},
 	{"rtpproxy_answer",	(cmd_function)rtpproxy_answer1_f,    0,
 	{"rtpproxy_answer",	(cmd_function)rtpproxy_answer1_f,    0,
 		0, 0,
 		0, 0,
 		ANY_ROUTE},
 		ANY_ROUTE},
 	{"rtpproxy_answer",	(cmd_function)rtpproxy_answer1_f,    1,
 	{"rtpproxy_answer",	(cmd_function)rtpproxy_answer1_f,    1,
-		0, 0,
+		fixup_spve_null, 0,
 		ANY_ROUTE},
 		ANY_ROUTE},
 	{"rtpproxy_answer",	(cmd_function)rtpproxy_answer2_f,    2,
 	{"rtpproxy_answer",	(cmd_function)rtpproxy_answer2_f,    2,
-		0, 0,
+		fixup_spve_spve, 0,
 		ANY_ROUTE},
 		ANY_ROUTE},
 	{"rtpproxy_stream2uac",(cmd_function)rtpproxy_stream2uac2_f, 2,
 	{"rtpproxy_stream2uac",(cmd_function)rtpproxy_stream2uac2_f, 2,
 		fixup_var_str_int, 0,
 		fixup_var_str_int, 0,
@@ -1800,7 +1801,21 @@ get_extra_id(struct sip_msg* msg, str *id_str) {
 
 
 
 
 static int
 static int
-unforce_rtp_proxy_f(struct sip_msg* msg, char* flags, char* str2)
+unforce_rtp_proxy1_f(struct sip_msg* msg, char* str1, char* str2)
+{
+	str flags;
+
+	if (str1)
+		get_str_fparam(&flags, msg, (fparam_t *) str1);
+	else
+		flags.s = NULL;
+
+	return unforce_rtp_proxy(msg, flags.s);
+}
+
+
+static int
+unforce_rtp_proxy(struct sip_msg* msg, char* flags)
 {
 {
 	str callid, from_tag, to_tag, viabranch;
 	str callid, from_tag, to_tag, viabranch;
 	char *cp;
 	char *cp;
@@ -1813,6 +1828,7 @@ unforce_rtp_proxy_f(struct sip_msg* msg, char* flags, char* str2)
 	struct iovec v[1 + 4 + 3 + 2] = {{NULL, 0}, {"D", 1}, {" ", 1}, {NULL, 0}, {NULL, 0}, {NULL, 0}, {" ", 1}, {NULL, 0}, {" ", 1}, {NULL, 0}};
 	struct iovec v[1 + 4 + 3 + 2] = {{NULL, 0}, {"D", 1}, {" ", 1}, {NULL, 0}, {NULL, 0}, {NULL, 0}, {" ", 1}, {NULL, 0}, {" ", 1}, {NULL, 0}};
 	                                            /* 1 */   /* 2 */   /* 3 */    /* 4 */    /* 5 */    /* 6 */   /* 7 */    /* 8 */   /* 9 */
 	                                            /* 1 */   /* 2 */   /* 3 */    /* 4 */    /* 5 */    /* 6 */   /* 7 */    /* 8 */   /* 9 */
 
 
+
 	for (cp = flags; cp && *cp; cp++) {
 	for (cp = flags; cp && *cp; cp++) {
 		switch (*cp) {
 		switch (*cp) {
 			case '1':
 			case '1':
@@ -1976,7 +1992,7 @@ rtpproxy_manage(struct sip_msg *msg, char *flags, char *ip)
 		return -1;
 		return -1;
 
 
 	if(method==METHOD_CANCEL || method==METHOD_BYE)
 	if(method==METHOD_CANCEL || method==METHOD_BYE)
-		return unforce_rtp_proxy_f(msg, flags, 0);
+		return unforce_rtp_proxy(msg, flags);
 
 
 	if(ip==NULL)
 	if(ip==NULL)
 	{
 	{
@@ -2002,13 +2018,13 @@ rtpproxy_manage(struct sip_msg *msg, char *flags, char *ip)
 					&& tmb.t_gett()!=T_UNDEFINED)
 					&& tmb.t_gett()!=T_UNDEFINED)
 				tmb.t_gett()->uas.request->msg_flags |= FL_SDP_BODY;
 				tmb.t_gett()->uas.request->msg_flags |= FL_SDP_BODY;
 			if(route_type==FAILURE_ROUTE)
 			if(route_type==FAILURE_ROUTE)
-				return unforce_rtp_proxy_f(msg, flags, 0);
+				return unforce_rtp_proxy(msg, flags);
 			return force_rtp_proxy(msg, flags, (cp!=NULL)?newip:ip, 1,
 			return force_rtp_proxy(msg, flags, (cp!=NULL)?newip:ip, 1,
 					(ip!=NULL)?1:0);
 					(ip!=NULL)?1:0);
 		}
 		}
 	} else if(msg->first_line.type == SIP_REPLY) {
 	} else if(msg->first_line.type == SIP_REPLY) {
 		if(msg->first_line.u.reply.statuscode>=300)
 		if(msg->first_line.u.reply.statuscode>=300)
-			return unforce_rtp_proxy_f(msg, flags, 0);
+			return unforce_rtp_proxy(msg, flags);
 		if(nosdp==0) {
 		if(nosdp==0) {
 			if(method==METHOD_UPDATE)
 			if(method==METHOD_UPDATE)
 				return force_rtp_proxy(msg, flags, (cp!=NULL)?newip:ip, 0,
 				return force_rtp_proxy(msg, flags, (cp!=NULL)?newip:ip, 0,
@@ -2068,16 +2084,26 @@ rtpproxy_offer1_f(struct sip_msg *msg, char *str1, char *str2)
 {
 {
         char *cp;
         char *cp;
         char newip[IP_ADDR_MAX_STR_SIZE];
         char newip[IP_ADDR_MAX_STR_SIZE];
+	str flags;
 
 
         cp = ip_addr2a(&msg->rcv.dst_ip);
         cp = ip_addr2a(&msg->rcv.dst_ip);
         strcpy(newip, cp);
         strcpy(newip, cp);
-	return force_rtp_proxy(msg, str1, newip, 1, 0);
+
+	if (str1)
+		get_str_fparam(&flags, msg, (fparam_t *) str1);
+	else
+		flags.s = NULL;
+	return force_rtp_proxy(msg, flags.s, newip, 1, 0);
 }
 }
 
 
 static int
 static int
 rtpproxy_offer2_f(struct sip_msg *msg, char *param1, char *param2)
 rtpproxy_offer2_f(struct sip_msg *msg, char *param1, char *param2)
 {
 {
-	return force_rtp_proxy(msg, param1, param2, 1, 1);
+	str flags, new_ip;
+
+	get_str_fparam(&flags, msg, (fparam_t *) param1);
+	get_str_fparam(&new_ip, msg, (fparam_t *) param2);
+	return force_rtp_proxy(msg, flags.s, new_ip.s, 1, 1);
 }
 }
 
 
 static int
 static int
@@ -2085,6 +2111,7 @@ rtpproxy_answer1_f(struct sip_msg *msg, char *str1, char *str2)
 {
 {
         char *cp;
         char *cp;
         char newip[IP_ADDR_MAX_STR_SIZE];
         char newip[IP_ADDR_MAX_STR_SIZE];
+	str flags;
 
 
 	if (msg->first_line.type == SIP_REQUEST)
 	if (msg->first_line.type == SIP_REQUEST)
 		if (msg->first_line.u.request.method_value != METHOD_ACK)
 		if (msg->first_line.u.request.method_value != METHOD_ACK)
@@ -2092,18 +2119,27 @@ rtpproxy_answer1_f(struct sip_msg *msg, char *str1, char *str2)
 
 
         cp = ip_addr2a(&msg->rcv.dst_ip);
         cp = ip_addr2a(&msg->rcv.dst_ip);
         strcpy(newip, cp);
         strcpy(newip, cp);
-	return force_rtp_proxy(msg, str1, newip, 0, 0);
+
+	if (str1)
+		get_str_fparam(&flags, msg, (fparam_t *) str1);
+	else
+		flags.s = NULL;
+	return force_rtp_proxy(msg, flags.s, newip, 0, 0);
 }
 }
 
 
 static int
 static int
 rtpproxy_answer2_f(struct sip_msg *msg, char *param1, char *param2)
 rtpproxy_answer2_f(struct sip_msg *msg, char *param1, char *param2)
 {
 {
 
 
+	str flags, new_ip;
+
 	if (msg->first_line.type == SIP_REQUEST)
 	if (msg->first_line.type == SIP_REQUEST)
 		if (msg->first_line.u.request.method_value != METHOD_ACK)
 		if (msg->first_line.u.request.method_value != METHOD_ACK)
 			return -1;
 			return -1;
 
 
-	return force_rtp_proxy(msg, param1, param2, 0, 1);
+	get_str_fparam(&flags, msg, (fparam_t *) param1);
+	get_str_fparam(&new_ip, msg, (fparam_t *) param2);
+	return force_rtp_proxy(msg, flags.s, new_ip.s, 0, 1);
 }
 }
 
 
 
 

+ 8 - 8
modules/snmpstats/README

@@ -254,10 +254,10 @@ Chapter 1. Admin Guide
      * usrloc - all scalars and tables relating to users and contacts are
      * usrloc - all scalars and tables relating to users and contacts are
        dependent on the usrloc module. If the module is not loaded, the
        dependent on the usrloc module. If the module is not loaded, the
        respective tables will be empty.
        respective tables will be empty.
-     * dialog - all scalars relating to the number of dialogs are
-       dependent on the presence of the dialog module. Furthermore, if the
-       module is not loaded, then the kamailioDialogLimitMinorEvent, and
-       kamailioDialogLimitMajorEvent alarm will be disabled.
+     * dialog or dialog-ng - all scalars relating to the number of dialogs
+       are dependent on the presence of a dialog module. Furthermore, if
+       the module is not loaded, then the kamailioDialogLimitMinorEvent,
+       and kamailioDialogLimitMajorEvent alarm will be disabled.
 
 
    The contents of the kamailioSIPMethodSupportedTable change depending on
    The contents of the kamailioSIPMethodSupportedTable change depending on
    which modules are loaded.
    which modules are loaded.
@@ -429,7 +429,7 @@ modparam("snmpstats", "export_registrar", 1)
    There are several things that need to be done to get the SNMPStats
    There are several things that need to be done to get the SNMPStats
    module compiled and up and running.
    module compiled and up and running.
 
 
-6.1.  Compiling the SNMPStats Module
+6.1. Compiling the SNMPStats Module
 
 
    In order for the SNMPStats module to compile, you will need at least
    In order for the SNMPStats module to compile, you will need at least
    version 5.3 of the NetSNMP source code. The source can be found at:
    version 5.3 of the NetSNMP source code. The source can be found at:
@@ -451,7 +451,7 @@ modparam("snmpstats", "export_registrar", 1)
    recommended you install NetSNMP from source to avoid bringing in
    recommended you install NetSNMP from source to avoid bringing in
    excessive dependencies to the SNMPStats module.
    excessive dependencies to the SNMPStats module.
 
 
-6.2.  Configuring NetSNMP to allow connections from the SNMPStats module.
+6.2. Configuring NetSNMP to allow connections from the SNMPStats module.
 
 
    The SNMPStats module will communicate with the NetSNMP Master Agent.
    The SNMPStats module will communicate with the NetSNMP Master Agent.
    This communication happens over a protocol known as AgentX. This means
    This communication happens over a protocol known as AgentX. This means
@@ -483,7 +483,7 @@ modparam("snmpstats", "export_registrar", 1)
    This tells NetSNMP to act as a master agent, listening on the localhost
    This tells NetSNMP to act as a master agent, listening on the localhost
    UDP interface at port 705.
    UDP interface at port 705.
 
 
-6.3.  Configuring the SNMPStats module for communication with a Master Agent
+6.3. Configuring the SNMPStats module for communication with a Master Agent
 
 
    The previous section explained how to set up a NetSNMP master agent to
    The previous section explained how to set up a NetSNMP master agent to
    accept AgentX connections. We now need to tell the SNMPStats module how
    accept AgentX connections. We now need to tell the SNMPStats module how
@@ -503,7 +503,7 @@ modparam("snmpstats", "export_registrar", 1)
    be present on the same machine as Kamailio. localhost could be replaced
    be present on the same machine as Kamailio. localhost could be replaced
    with any other machine.
    with any other machine.
 
 
-6.4.  Testing for a proper Configuration
+6.4. Testing for a proper Configuration
 
 
    As a quick test to make sure that the SNMPStats module sub-agent can
    As a quick test to make sure that the SNMPStats module sub-agent can
    succesfully connect to the NetSNMP Master agent, start snmpd with the
    succesfully connect to the NetSNMP Master agent, start snmpd with the

+ 10 - 10
modules/snmpstats/doc/snmpstats_admin.xml

@@ -203,8 +203,8 @@
 		
 		
 		<listitem>
 		<listitem>
 		<para>
 		<para>
-		<emphasis>dialog</emphasis> - all scalars relating to the number of dialogs are 
-		dependent on the presence of the dialog module.  Furthermore, if the module is 
+		<emphasis>dialog or dialog-ng</emphasis> - all scalars relating to the number of dialogs are 
+		dependent on the presence of a dialog module.  Furthermore, if the module is 
 		not loaded, then the kamailioDialogLimitMinorEvent, and kamailioDialogLimitMajorEvent
 		not loaded, then the kamailioDialogLimitMinorEvent, and kamailioDialogLimitMajorEvent
 		alarm will be disabled. 
 		alarm will be disabled. 
 		</para>
 		</para>
@@ -243,7 +243,7 @@
 	</section>
 	</section>
 	<section>
 	<section>
 	<title>Parameters</title>
 	<title>Parameters</title>
-	<section>
+	<section id ="snmpstats.p.sipentitytape">
 		<title><varname>sipEntityType</varname> (String) </title>
 		<title><varname>sipEntityType</varname> (String) </title>
 
 
 		<para>
 		<para>
@@ -275,7 +275,7 @@ modparam("snmpstats", "sipEntityType", "proxyServer")
 		</para>
 		</para>
 	</section>
 	</section>
 
 
-	<section>
+	<section id ="snmpstats.p.MsqQueueMinorTreshold">
 		<title><varname>MsgQueueMinorThreshold</varname> (Integer)</title>
 		<title><varname>MsgQueueMinorThreshold</varname> (Integer)</title>
 
 
 		<para>
 		<para>
@@ -300,7 +300,7 @@ modparam("snmpstats", "MsgQueueMinorThreshold", 2000)
 		</para>
 		</para>
 	</section>
 	</section>
 	
 	
-	<section>
+	<section id ="snmpstats.p.MsqQueueMajorTreshold">
 		<title><varname>MsgQueueMajorThreshold</varname> (Integer)</title>
 		<title><varname>MsgQueueMajorThreshold</varname> (Integer)</title>
 
 
 		<para>
 		<para>
@@ -325,7 +325,7 @@ modparam("snmpstats", "MsgQueueMajorThreshold", 5000)
 		</para>
 		</para>
 	</section>
 	</section>
 
 
-	<section>
+	<section id ="snmpstats.p.dlg_minor_treshold">
 		<title><varname>dlg_minor_threshold</varname> (Integer)</title>
 		<title><varname>dlg_minor_threshold</varname> (Integer)</title>
 
 
 		<para>
 		<para>
@@ -349,7 +349,7 @@ modparam("snmpstats", "MsgQueueMajorThreshold", 5000)
 		</para>
 		</para>
 	</section>
 	</section>
 
 
-	<section>
+	<section id ="snmpstats.p.dlg_major_treshold">
 		<title><varname>dlg_major_threshold</varname> (Integer)</title>
 		<title><varname>dlg_major_threshold</varname> (Integer)</title>
 
 
 		<para>
 		<para>
@@ -373,7 +373,7 @@ modparam("snmpstats", "MsgQueueMajorThreshold", 5000)
 		</para>
 		</para>
 	</section>
 	</section>
 
 
-	<section>
+	<section id ="snmpstats.p.snmpgetPath">
 		<title><varname>snmpgetPath</varname> (String)</title>
 		<title><varname>snmpgetPath</varname> (String)</title>
 
 
 		<para>
 		<para>
@@ -399,7 +399,7 @@ modparam("snmpstats", "snmpgetPath",     "/my/custom/path/")
 		</example>
 		</example>
 	</section>
 	</section>
 	
 	
-	<section>
+	<section id ="snmpstats.p.snmpCommunity">
 		<title><varname>snmpCommunity</varname> (String)</title>
 		<title><varname>snmpCommunity</varname> (String)</title>
 
 
 		<para>
 		<para>
@@ -425,7 +425,7 @@ modparam("snmpstats", "snmpCommunity", "customCommunityString")
 		</example>
 		</example>
 	</section>
 	</section>
 
 
-	<section>
+	<section id ="snmpstats.p.export_registrar">
 		<title><varname>export_registrar</varname> (int)</title>
 		<title><varname>export_registrar</varname> (int)</title>
 
 
 		<para>
 		<para>

+ 402 - 0
modules/snmpstats/kamailioNet.c

@@ -0,0 +1,402 @@
+/*
+ * SNMPStats Module  - Network Statistics
+ *
+ * Copyright (C) 2006 SOMA Networks, INC.
+ * Written by: Jeffrey Magder ([email protected])
+ *
+ * Kamailio Server Net objects addition
+ * Copyright (C) 2013 Edvina AB, Sollentuna, Sweden
+ * Written by Olle E. Johansson
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * History:
+ * --------
+ * 2013-03-24 initial version (oej)
+ * 
+ * Note: this file originally auto-generated by mib2c 
+ *
+ */
+
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+#include "kamailioNet.h"
+
+#include "snmpstats_globals.h"
+#include "utilities.h"
+#include "../../lib/kcore/statistics.h"
+#include "../../globals.h"
+#include "../../tcp_options.h"
+
+/*
+ * Note: this file originally auto-generated by mib2c using
+ *        $
+ */
+
+
+/** Initializes the kamailioNet module */
+void
+init_kamailioNet(void)
+{
+    const oid kamailioNetTcpConnEstablished_oid[] = { 1,3,6,1,4,1,34352,3,1,3,1,4,1,2,1 };
+    const oid kamailioNetTcpConnFailed_oid[] = { 1,3,6,1,4,1,34352,3,1,3,1,4,1,2,2 };
+    const oid kamailioNetTcpConnReset_oid[] = { 1,3,6,1,4,1,34352,3,1,3,1,4,1,2,3 };
+    const oid kamailioNetTcpConnSuccess_oid[] = { 1,3,6,1,4,1,34352,3,1,3,1,4,1,2,4 };
+    const oid kamailioNetTcpConnOpened_oid[] = { 1,3,6,1,4,1,34352,3,1,3,1,4,1,2,5 };
+    const oid kamailioNetTcpConnPassiveOpen_oid[] = { 1,3,6,1,4,1,34352,3,1,3,1,4,1,2,6 };
+    const oid kamailioNetTcpConnReject_oid[] = { 1,3,6,1,4,1,34352,3,1,3,1,4,1,2,8 };
+    const oid kamailioNetTcpEnabled_oid[] = { 1,3,6,1,4,1,34352,3,1,3,1,4,1,3,1 };
+    const oid kamailioNetTcpMaxConns_oid[] = { 1,3,6,1,4,1,34352,3,1,3,1,4,1,3,2 };
+    const oid kamailioNetTcpAsync_oid[] = { 1,3,6,1,4,1,34352,3,1,3,1,4,1,3,3 };
+
+  DEBUGMSGTL(("kamailioNet", "Initializing\n"));
+
+    netsnmp_register_scalar(
+        netsnmp_create_handler_registration("kamailioNetTcpConnEstablished", handle_kamailioNetTcpConnEstablished,
+                               kamailioNetTcpConnEstablished_oid, OID_LENGTH(kamailioNetTcpConnEstablished_oid),
+                               HANDLER_CAN_RONLY
+        ));
+    netsnmp_register_scalar(
+        netsnmp_create_handler_registration("kamailioNetTcpConnFailed", handle_kamailioNetTcpConnFailed,
+                               kamailioNetTcpConnFailed_oid, OID_LENGTH(kamailioNetTcpConnFailed_oid),
+                               HANDLER_CAN_RONLY
+        ));
+    netsnmp_register_scalar(
+        netsnmp_create_handler_registration("kamailioNetTcpConnReset", handle_kamailioNetTcpConnReset,
+                               kamailioNetTcpConnReset_oid, OID_LENGTH(kamailioNetTcpConnReset_oid),
+                               HANDLER_CAN_RONLY
+        ));
+    netsnmp_register_scalar(
+        netsnmp_create_handler_registration("kamailioNetTcpConnSuccess", handle_kamailioNetTcpConnSuccess,
+                               kamailioNetTcpConnSuccess_oid, OID_LENGTH(kamailioNetTcpConnSuccess_oid),
+                               HANDLER_CAN_RONLY
+        ));
+    netsnmp_register_scalar(
+        netsnmp_create_handler_registration("kamailioNetTcpConnOpened", handle_kamailioNetTcpConnOpened,
+                               kamailioNetTcpConnOpened_oid, OID_LENGTH(kamailioNetTcpConnOpened_oid),
+                               HANDLER_CAN_RONLY
+        ));
+    netsnmp_register_scalar(
+        netsnmp_create_handler_registration("kamailioNetTcpConnPassiveOpen", handle_kamailioNetTcpConnPassiveOpen,
+                               kamailioNetTcpConnPassiveOpen_oid, OID_LENGTH(kamailioNetTcpConnPassiveOpen_oid),
+                               HANDLER_CAN_RONLY
+        ));
+    netsnmp_register_scalar(
+        netsnmp_create_handler_registration("kamailioNetTcpConnReject", handle_kamailioNetTcpConnReject,
+                               kamailioNetTcpConnReject_oid, OID_LENGTH(kamailioNetTcpConnReject_oid),
+                               HANDLER_CAN_RONLY
+        ));
+    netsnmp_register_scalar(
+        netsnmp_create_handler_registration("kamailioNetTcpEnabled", handle_kamailioNetTcpEnabled,
+                               kamailioNetTcpEnabled_oid, OID_LENGTH(kamailioNetTcpEnabled_oid),
+                               HANDLER_CAN_RONLY
+        ));
+    netsnmp_register_scalar(
+        netsnmp_create_handler_registration("kamailioNetTcpMaxConns", handle_kamailioNetTcpMaxConns,
+                               kamailioNetTcpMaxConns_oid, OID_LENGTH(kamailioNetTcpMaxConns_oid),
+                               HANDLER_CAN_RONLY
+        ));
+    netsnmp_register_scalar(
+        netsnmp_create_handler_registration("kamailioNetTcpAsync", handle_kamailioNetTcpAsync,
+                               kamailioNetTcpAsync_oid, OID_LENGTH(kamailioNetTcpAsync_oid),
+                               HANDLER_CAN_RONLY
+        ));
+}
+
+#ifdef SKREP
+	con_timeout: 0
+	connect_success: 0
+	current_write_queue_size: 0
+	local_reject: 0
+	passive_open: 0
+	send_timeout: 0
+	sendq_full: 0
+#endif
+
+int
+handle_kamailioNetTcpConnEstablished(netsnmp_mib_handler *handler,
+                          netsnmp_handler_registration *reginfo,
+                          netsnmp_agent_request_info   *reqinfo,
+                          netsnmp_request_info         *requests)
+{
+    /* We are never called for a GETNEXT if it's registered as a
+       "instance", as it's "magically" handled for us.  */
+
+    /* a instance handler also only hands us one request at a time, so
+       we don't need to loop over a list of requests; we'll only get one. */
+
+	int datafield = get_statistic("established");
+    
+    switch(reqinfo->mode) {
+
+        case MODE_GET:
+            snmp_set_var_typed_value(requests->requestvb, ASN_COUNTER,
+			(u_char *) &datafield, sizeof(int));
+            break;
+
+
+        default:
+            /* we should never get here, so this is a really bad error */
+            snmp_log(LOG_ERR, "unknown mode (%d) in handle_kamailioNetTcpConnEstablished\n", reqinfo->mode );
+            return SNMP_ERR_GENERR;
+    }
+
+    return SNMP_ERR_NOERROR;
+}
+int
+handle_kamailioNetTcpConnFailed(netsnmp_mib_handler *handler,
+                          netsnmp_handler_registration *reginfo,
+                          netsnmp_agent_request_info   *reqinfo,
+                          netsnmp_request_info         *requests)
+{
+
+	int datafield = get_statistic("connect_failed");
+    
+    switch(reqinfo->mode) {
+
+        case MODE_GET:
+            snmp_set_var_typed_value(requests->requestvb, ASN_COUNTER,
+			(u_char *) &datafield, sizeof(int));
+            break;
+
+
+        default:
+            /* we should never get here, so this is a really bad error */
+            snmp_log(LOG_ERR, "unknown mode (%d) in handle_kamailioNetTcpConnFailed\n", reqinfo->mode );
+            return SNMP_ERR_GENERR;
+    }
+
+    return SNMP_ERR_NOERROR;
+}
+int
+handle_kamailioNetTcpConnReset(netsnmp_mib_handler *handler,
+                          netsnmp_handler_registration *reginfo,
+                          netsnmp_agent_request_info   *reqinfo,
+                          netsnmp_request_info         *requests)
+{
+	int datafield = get_statistic("con_reset");
+    
+    switch(reqinfo->mode) {
+
+        case MODE_GET:
+            snmp_set_var_typed_value(requests->requestvb, ASN_COUNTER,
+			(u_char *) &datafield, sizeof(int));
+            break;
+
+
+        default:
+            /* we should never get here, so this is a really bad error */
+            snmp_log(LOG_ERR, "unknown mode (%d) in handle_kamailioNetTcpConnReset\n", reqinfo->mode );
+            return SNMP_ERR_GENERR;
+    }
+
+    return SNMP_ERR_NOERROR;
+}
+int
+handle_kamailioNetTcpConnSuccess(netsnmp_mib_handler *handler,
+                          netsnmp_handler_registration *reginfo,
+                          netsnmp_agent_request_info   *reqinfo,
+                          netsnmp_request_info         *requests)
+{
+
+	int datafield = get_statistic("connect_success");
+    
+    switch(reqinfo->mode) {
+
+        case MODE_GET:
+            snmp_set_var_typed_value(requests->requestvb, ASN_COUNTER,
+			(u_char *) &datafield, sizeof(int));
+            break;
+
+
+        default:
+            /* we should never get here, so this is a really bad error */
+            snmp_log(LOG_ERR, "unknown mode (%d) in handle_kamailioNetTcpConnSuccess\n", reqinfo->mode );
+            return SNMP_ERR_GENERR;
+    }
+
+    return SNMP_ERR_NOERROR;
+}
+int
+handle_kamailioNetTcpConnOpened(netsnmp_mib_handler *handler,
+                          netsnmp_handler_registration *reginfo,
+                          netsnmp_agent_request_info   *reqinfo,
+                          netsnmp_request_info         *requests)
+{
+	int datafield = get_statistic("current_opened_connections");
+    switch(reqinfo->mode) {
+
+        case MODE_GET:
+            snmp_set_var_typed_value(requests->requestvb, ASN_GAUGE,
+			(u_char *) &datafield, sizeof(int));
+            break;
+
+
+        default:
+            /* we should never get here, so this is a really bad error */
+            snmp_log(LOG_ERR, "unknown mode (%d) in handle_kamailioNetTcpConnOpened\n", reqinfo->mode );
+            return SNMP_ERR_GENERR;
+    }
+
+    return SNMP_ERR_NOERROR;
+}
+int
+handle_kamailioNetTcpConnPassiveOpen(netsnmp_mib_handler *handler,
+                          netsnmp_handler_registration *reginfo,
+                          netsnmp_agent_request_info   *reqinfo,
+                          netsnmp_request_info         *requests)
+{
+	int datafield = get_statistic("passive_open");
+    
+    switch(reqinfo->mode) {
+
+        case MODE_GET:
+            snmp_set_var_typed_value(requests->requestvb, ASN_COUNTER,
+			(u_char *) &datafield, sizeof(int));
+            break;
+
+
+        default:
+            /* we should never get here, so this is a really bad error */
+            snmp_log(LOG_ERR, "unknown mode (%d) in handle_kamailioNetTcpConnPassiveOpen\n", reqinfo->mode );
+            return SNMP_ERR_GENERR;
+    }
+
+    return SNMP_ERR_NOERROR;
+}
+int
+handle_kamailioNetTcpConnReject(netsnmp_mib_handler *handler,
+                          netsnmp_handler_registration *reginfo,
+                          netsnmp_agent_request_info   *reqinfo,
+                          netsnmp_request_info         *requests)
+{
+    /* We are never called for a GETNEXT if it's registered as a
+       "instance", as it's "magically" handled for us.  */
+
+    /* a instance handler also only hands us one request at a time, so
+       we don't need to loop over a list of requests; we'll only get one. */
+	int datafield = get_statistic("local_reject");
+    
+    switch(reqinfo->mode) {
+
+        case MODE_GET:
+            snmp_set_var_typed_value(requests->requestvb, ASN_COUNTER,
+			(u_char *) &datafield, sizeof(int));
+            break;
+
+
+        default:
+            /* we should never get here, so this is a really bad error */
+            snmp_log(LOG_ERR, "unknown mode (%d) in handle_kamailioNetTcpConnReject\n", reqinfo->mode );
+            return SNMP_ERR_GENERR;
+    }
+
+    return SNMP_ERR_NOERROR;
+}
+int
+handle_kamailioNetTcpEnabled(netsnmp_mib_handler *handler,
+                          netsnmp_handler_registration *reginfo,
+                          netsnmp_agent_request_info   *reqinfo,
+                          netsnmp_request_info         *requests)
+{
+    /* We are never called for a GETNEXT if it's registered as a
+       "instance", as it's "magically" handled for us.  */
+
+    /* a instance handler also only hands us one request at a time, so
+       we don't need to loop over a list of requests; we'll only get one. */
+	int enabled = (tcp_disable == 0);
+    
+    switch(reqinfo->mode) {
+
+        case MODE_GET:
+            snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
+			(u_char *) &enabled, sizeof(int));
+            break;
+
+
+        default:
+            /* we should never get here, so this is a really bad error */
+            snmp_log(LOG_ERR, "unknown mode (%d) in handle_kamailioNetTcpEnabled\n", reqinfo->mode );
+            return SNMP_ERR_GENERR;
+    }
+
+    return SNMP_ERR_NOERROR;
+}
+
+int
+handle_kamailioNetTcpMaxConns(netsnmp_mib_handler *handler,
+                          netsnmp_handler_registration *reginfo,
+                          netsnmp_agent_request_info   *reqinfo,
+                          netsnmp_request_info         *requests)
+{
+    struct cfg_group_tcp t;
+    unsigned int maxconn;
+
+    tcp_options_get(&t);
+    maxconn = t.max_connections;
+
+    switch(reqinfo->mode) {
+
+        case MODE_GET:
+            snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
+			 (u_char *) &maxconn, sizeof(int));
+            break;
+
+
+        default:
+            /* we should never get here, so this is a really bad error */
+            snmp_log(LOG_ERR, "unknown mode (%d) in handle_kamailioNetTcpMaxConns\n", reqinfo->mode );
+            return SNMP_ERR_GENERR;
+    }
+
+    return SNMP_ERR_NOERROR;
+}
+int
+handle_kamailioNetTcpAsync(netsnmp_mib_handler *handler,
+                          netsnmp_handler_registration *reginfo,
+                          netsnmp_agent_request_info   *reqinfo,
+                          netsnmp_request_info         *requests)
+{
+    struct cfg_group_tcp t;
+    unsigned int value;
+
+    tcp_options_get(&t);
+    value = t.async;
+    /* We are never called for a GETNEXT if it's registered as a
+       "instance", as it's "magically" handled for us.  */
+
+    /* a instance handler also only hands us one request at a time, so
+       we don't need to loop over a list of requests; we'll only get one. */
+    
+    switch(reqinfo->mode) {
+
+        case MODE_GET:
+            snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
+			 (u_char *) &value, sizeof(int));
+            break;
+
+
+        default:
+            /* we should never get here, so this is a really bad error */
+            snmp_log(LOG_ERR, "unknown mode (%d) in handle_kamailioNetTcpAsync\n", reqinfo->mode );
+            return SNMP_ERR_GENERR;
+    }
+
+    return SNMP_ERR_NOERROR;
+}

+ 21 - 0
modules/snmpstats/kamailioNet.h

@@ -0,0 +1,21 @@
+/*
+ * Note: this file originally auto-generated by mib2c using
+ *        $
+ */
+#ifndef KAMAILIONET_H
+#define KAMAILIONET_H
+
+/* function declarations */
+void init_kamailioNet(void);
+Netsnmp_Node_Handler handle_kamailioNetTcpConnEstablished;
+Netsnmp_Node_Handler handle_kamailioNetTcpConnFailed;
+Netsnmp_Node_Handler handle_kamailioNetTcpConnReset;
+Netsnmp_Node_Handler handle_kamailioNetTcpConnSuccess;
+Netsnmp_Node_Handler handle_kamailioNetTcpConnOpened;
+Netsnmp_Node_Handler handle_kamailioNetTcpConnPassiveOpen;
+Netsnmp_Node_Handler handle_kamailioNetTcpConnReject;
+Netsnmp_Node_Handler handle_kamailioNetTcpEnabled;
+Netsnmp_Node_Handler handle_kamailioNetTcpMaxConns;
+Netsnmp_Node_Handler handle_kamailioNetTcpAsync;
+
+#endif /* KAMAILIONET_H */

+ 440 - 0
modules/snmpstats/kamailioServer.c

@@ -0,0 +1,440 @@
+/*
+ *
+ * SNMPStats Module 
+ * Copyright (C) 2006 SOMA Networks, INC.
+ * Written by: Jeffrey Magder ([email protected])
+ *
+ * Kamailio Server core objects addition
+ * Copyright (C) 2013 Edvina AB, Sollentuna, Sweden
+ * Written by Olle E. Johansson
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * History:
+ * --------
+ * 2013-03-24 initial version (oej)
+ * 
+ * Note: this file originally auto-generated by mib2c 
+ *
+ */
+
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+#include "kamailioServer.h"
+
+#include "snmpstats_globals.h"
+#include "utilities.h"
+#include "../../lib/kcore/statistics.h"
+#include "../../ver.h"
+
+/** Initializes the kamailioServer module */
+void
+init_kamailioServer(void)
+{
+    const oid kamailioSrvMaxMemory_oid[] = { 1,3,6,1,4,1,34352,3,1,3,1,1,1,1 };
+    const oid kamailioSrvFreeMemory_oid[] = { 1,3,6,1,4,1,34352,3,1,3,1,1,1,2 };
+
+    const oid kamailioSrvCnfFullVersion_oid[] = { 1,3,6,1,4,1,34352,3,1,3,1,1,2,1 };
+    const oid kamailioSrvCnfVerName_oid[] = { 1,3,6,1,4,1,34352,3,1,3,1,1,2,2 };
+    const oid kamailioSrvCnfVerVersion_oid[] = { 1,3,6,1,4,1,34352,3,1,3,1,1,2,3 };
+    const oid kamailioSrvCnfVerArch_oid[] = { 1,3,6,1,4,1,34352,3,1,3,1,1,2,4 };
+    const oid kamailioSrvCnfVerOs_oid[] = { 1,3,6,1,4,1,34352,3,1,3,1,1,2,5 };
+    const oid kamailioSrvCnfVerId_oid[] = { 1,3,6,1,4,1,34352,3,1,3,1,1,2,6 };
+    const oid kamailioSrvCnfVerCompTime_oid[] = { 1,3,6,1,4,1,34352,3,1,3,1,1,2,7 };
+    const oid kamailioSrvCnfVerCompiler_oid[] = { 1,3,6,1,4,1,34352,3,1,3,1,1,2,8 };
+    const oid kamailioSrvCnfVerFlags_oid[] = { 1,3,6,1,4,1,34352,3,1,3,1,1,2,9 };
+
+     DEBUGMSGTL(("kamailioServer", "Initializing\n"));
+     LM_DBG("initializing Kamailio Server OID's X\n");
+
+    netsnmp_register_scalar(
+        netsnmp_create_handler_registration("kamailioSrvMaxMemory", handle_kamailioSrvMaxMemory,
+                               kamailioSrvMaxMemory_oid, OID_LENGTH(kamailioSrvMaxMemory_oid),
+                               HANDLER_CAN_RONLY
+        ));
+    netsnmp_register_scalar(
+        netsnmp_create_handler_registration("kamailioSrvFreeMemory", handle_kamailioSrvFreeMemory,
+                               kamailioSrvFreeMemory_oid, OID_LENGTH(kamailioSrvFreeMemory_oid),
+                               HANDLER_CAN_RONLY
+        ));
+    netsnmp_register_scalar(
+        netsnmp_create_handler_registration("kamailioSrvCnfFullVersion", handle_kamailioSrvCnfFullVersion,
+                               kamailioSrvCnfFullVersion_oid, OID_LENGTH(kamailioSrvCnfFullVersion_oid),
+                               HANDLER_CAN_RONLY
+        ));
+    netsnmp_register_scalar(
+        netsnmp_create_handler_registration("kamailioSrvCnfVerName", handle_kamailioSrvCnfVerName,
+                               kamailioSrvCnfVerName_oid, OID_LENGTH(kamailioSrvCnfVerName_oid),
+                               HANDLER_CAN_RONLY
+        ));
+    netsnmp_register_scalar(
+        netsnmp_create_handler_registration("kamailioSrvCnfVerVersion", handle_kamailioSrvCnfVerVersion,
+                               kamailioSrvCnfVerVersion_oid, OID_LENGTH(kamailioSrvCnfVerVersion_oid),
+                               HANDLER_CAN_RONLY
+        ));
+    netsnmp_register_scalar(
+        netsnmp_create_handler_registration("kamailioSrvCnfVerArch", handle_kamailioSrvCnfVerArch,
+                               kamailioSrvCnfVerArch_oid, OID_LENGTH(kamailioSrvCnfVerArch_oid),
+                               HANDLER_CAN_RONLY
+        ));
+    netsnmp_register_scalar(
+        netsnmp_create_handler_registration("kamailioSrvCnfVerOs", handle_kamailioSrvCnfVerOs,
+                               kamailioSrvCnfVerOs_oid, OID_LENGTH(kamailioSrvCnfVerOs_oid),
+                               HANDLER_CAN_RONLY
+        ));
+    netsnmp_register_scalar(
+        netsnmp_create_handler_registration("kamailioSrvCnfVerId", handle_kamailioSrvCnfVerId,
+                               kamailioSrvCnfVerId_oid, OID_LENGTH(kamailioSrvCnfVerId_oid),
+                               HANDLER_CAN_RONLY
+        ));
+    netsnmp_register_scalar(
+        netsnmp_create_handler_registration("kamailioSrvCnfVerCompTime", handle_kamailioSrvCnfVerCompTime,
+                               kamailioSrvCnfVerCompTime_oid, OID_LENGTH(kamailioSrvCnfVerCompTime_oid),
+                               HANDLER_CAN_RONLY
+        ));
+    netsnmp_register_scalar(
+        netsnmp_create_handler_registration("kamailioSrvCnfVerCompiler", handle_kamailioSrvCnfVerCompiler,
+                               kamailioSrvCnfVerCompiler_oid, OID_LENGTH(kamailioSrvCnfVerCompiler_oid),
+                               HANDLER_CAN_RONLY
+        ));
+    netsnmp_register_scalar(
+        netsnmp_create_handler_registration("kamailioSrvCnfVerFlags", handle_kamailioSrvCnfVerFlags,
+                               kamailioSrvCnfVerFlags_oid, OID_LENGTH(kamailioSrvCnfVerFlags_oid),
+                               HANDLER_CAN_RONLY
+        ));
+}
+
+int
+handle_kamailioSrvMaxMemory(netsnmp_mib_handler *handler,
+                          netsnmp_handler_registration *reginfo,
+                          netsnmp_agent_request_info   *reqinfo,
+                          netsnmp_request_info         *requests)
+{
+    /* We are never called for a GETNEXT if it's registered as a
+       "instance", as it's "magically" handled for us.  */
+
+    /* a instance handler also only hands us one request at a time, so
+       we don't need to loop over a list of requests; we'll only get one. */
+
+    int maxmemory = get_statistic("total_size");
+    
+    switch(reqinfo->mode) {
+
+        case MODE_GET:
+            snmp_set_var_typed_value(requests->requestvb, ASN_GAUGE,
+                        	(u_char *) &maxmemory, sizeof(int));
+            break;
+
+
+        default:
+            /* we should never get here, so this is a really bad error */
+            snmp_log(LOG_ERR, "unknown mode (%d) in handle_kamailioSrvMaxMemory\n", reqinfo->mode );
+            return SNMP_ERR_GENERR;
+    }
+
+    return SNMP_ERR_NOERROR;
+}
+
+
+int
+handle_kamailioSrvFreeMemory(netsnmp_mib_handler *handler,
+                          netsnmp_handler_registration *reginfo,
+                          netsnmp_agent_request_info   *reqinfo,
+                          netsnmp_request_info         *requests)
+{
+    int freememory = get_statistic("free_size");
+
+    /* We are never called for a GETNEXT if it's registered as a
+       "instance", as it's "magically" handled for us.  */
+
+    /* a instance handler also only hands us one request at a time, so
+       we don't need to loop over a list of requests; we'll only get one. */
+    
+    switch(reqinfo->mode) {
+
+        case MODE_GET:
+            snmp_set_var_typed_value(requests->requestvb, ASN_GAUGE,
+                        	(u_char *) &freememory, sizeof(int));
+            break;
+
+
+        default:
+            /* we should never get here, so this is a really bad error */
+            snmp_log(LOG_ERR, "unknown mode (%d) in handle_kamailioSrvFreeMemory\n", reqinfo->mode );
+            return SNMP_ERR_GENERR;
+    }
+
+    return SNMP_ERR_NOERROR;
+}
+
+
+int
+handle_kamailioSrvCnfFullVersion(netsnmp_mib_handler *handler,
+                          netsnmp_handler_registration *reginfo,
+                          netsnmp_agent_request_info   *reqinfo,
+                          netsnmp_request_info         *requests)
+{
+    /* We are never called for a GETNEXT if it's registered as a
+       "instance", as it's "magically" handled for us.  */
+
+    /* a instance handler also only hands us one request at a time, so
+       we don't need to loop over a list of requests; we'll only get one. */
+
+    
+    switch(reqinfo->mode) {
+
+        case MODE_GET:
+            snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
+					full_version, strlen(full_version));
+            break;
+
+
+        default:
+            /* we should never get here, so this is a really bad error */
+            snmp_log(LOG_ERR, "unknown mode (%d) in handle_kamailioSrvCnfFullVersion\n", reqinfo->mode );
+            return SNMP_ERR_GENERR;
+    }
+
+    return SNMP_ERR_NOERROR;
+}
+
+int
+handle_kamailioSrvCnfVerName(netsnmp_mib_handler *handler,
+                          netsnmp_handler_registration *reginfo,
+                          netsnmp_agent_request_info   *reqinfo,
+                          netsnmp_request_info         *requests)
+{
+    /* We are never called for a GETNEXT if it's registered as a
+       "instance", as it's "magically" handled for us.  */
+
+    /* a instance handler also only hands us one request at a time, so
+       we don't need to loop over a list of requests; we'll only get one. */
+    
+    switch(reqinfo->mode) {
+
+        case MODE_GET:
+            snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
+					ver_name, strlen(ver_name));
+            break;
+
+
+        default:
+            /* we should never get here, so this is a really bad error */
+            snmp_log(LOG_ERR, "unknown mode (%d) in handle_kamailioSrvCnfVerName\n", reqinfo->mode );
+            return SNMP_ERR_GENERR;
+    }
+
+    return SNMP_ERR_NOERROR;
+}
+int
+handle_kamailioSrvCnfVerVersion(netsnmp_mib_handler *handler,
+                          netsnmp_handler_registration *reginfo,
+                          netsnmp_agent_request_info   *reqinfo,
+                          netsnmp_request_info         *requests)
+{
+    /* We are never called for a GETNEXT if it's registered as a
+       "instance", as it's "magically" handled for us.  */
+
+    /* a instance handler also only hands us one request at a time, so
+       we don't need to loop over a list of requests; we'll only get one. */
+    
+    switch(reqinfo->mode) {
+
+        case MODE_GET:
+            snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
+					ver_version, strlen(ver_version));
+            break;
+
+
+        default:
+            /* we should never get here, so this is a really bad error */
+            snmp_log(LOG_ERR, "unknown mode (%d) in handle_kamailioSrvCnfVerVersion\n", reqinfo->mode );
+            return SNMP_ERR_GENERR;
+    }
+
+    return SNMP_ERR_NOERROR;
+}
+int
+handle_kamailioSrvCnfVerArch(netsnmp_mib_handler *handler,
+                          netsnmp_handler_registration *reginfo,
+                          netsnmp_agent_request_info   *reqinfo,
+                          netsnmp_request_info         *requests)
+{
+    /* We are never called for a GETNEXT if it's registered as a
+       "instance", as it's "magically" handled for us.  */
+
+    /* a instance handler also only hands us one request at a time, so
+       we don't need to loop over a list of requests; we'll only get one. */
+    
+    switch(reqinfo->mode) {
+
+        case MODE_GET:
+            snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
+					ver_arch, strlen(ver_arch));
+            break;
+
+
+        default:
+            /* we should never get here, so this is a really bad error */
+            snmp_log(LOG_ERR, "unknown mode (%d) in handle_kamailioSrvCnfVerArch\n", reqinfo->mode );
+            return SNMP_ERR_GENERR;
+    }
+
+    return SNMP_ERR_NOERROR;
+}
+int
+handle_kamailioSrvCnfVerOs(netsnmp_mib_handler *handler,
+                          netsnmp_handler_registration *reginfo,
+                          netsnmp_agent_request_info   *reqinfo,
+                          netsnmp_request_info         *requests)
+{
+    /* We are never called for a GETNEXT if it's registered as a
+       "instance", as it's "magically" handled for us.  */
+
+    /* a instance handler also only hands us one request at a time, so
+       we don't need to loop over a list of requests; we'll only get one. */
+    
+    switch(reqinfo->mode) {
+
+        case MODE_GET:
+            snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
+					ver_os, strlen(ver_os));
+            break;
+
+
+        default:
+            /* we should never get here, so this is a really bad error */
+            snmp_log(LOG_ERR, "unknown mode (%d) in handle_kamailioSrvCnfVerOs\n", reqinfo->mode );
+            return SNMP_ERR_GENERR;
+    }
+
+    return SNMP_ERR_NOERROR;
+}
+int
+handle_kamailioSrvCnfVerId(netsnmp_mib_handler *handler,
+                          netsnmp_handler_registration *reginfo,
+                          netsnmp_agent_request_info   *reqinfo,
+                          netsnmp_request_info         *requests)
+{
+    /* We are never called for a GETNEXT if it's registered as a
+       "instance", as it's "magically" handled for us.  */
+
+    /* a instance handler also only hands us one request at a time, so
+       we don't need to loop over a list of requests; we'll only get one. */
+    
+    switch(reqinfo->mode) {
+
+        case MODE_GET:
+            snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
+					ver_id, strlen(ver_id));
+            break;
+
+
+        default:
+            /* we should never get here, so this is a really bad error */
+            snmp_log(LOG_ERR, "unknown mode (%d) in handle_kamailioSrvCnfVerId\n", reqinfo->mode );
+            return SNMP_ERR_GENERR;
+    }
+
+    return SNMP_ERR_NOERROR;
+}
+int
+handle_kamailioSrvCnfVerCompTime(netsnmp_mib_handler *handler,
+                          netsnmp_handler_registration *reginfo,
+                          netsnmp_agent_request_info   *reqinfo,
+                          netsnmp_request_info         *requests)
+{
+    /* We are never called for a GETNEXT if it's registered as a
+       "instance", as it's "magically" handled for us.  */
+
+    /* a instance handler also only hands us one request at a time, so
+       we don't need to loop over a list of requests; we'll only get one. */
+    
+    switch(reqinfo->mode) {
+
+        case MODE_GET:
+            snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
+					ver_compiled_time, strlen(ver_compiled_time));
+            break;
+
+
+        default:
+            /* we should never get here, so this is a really bad error */
+            snmp_log(LOG_ERR, "unknown mode (%d) in handle_kamailioSrvCnfVerCompTime\n", reqinfo->mode );
+            return SNMP_ERR_GENERR;
+    }
+
+    return SNMP_ERR_NOERROR;
+}
+int
+handle_kamailioSrvCnfVerCompiler(netsnmp_mib_handler *handler,
+                          netsnmp_handler_registration *reginfo,
+                          netsnmp_agent_request_info   *reqinfo,
+                          netsnmp_request_info         *requests)
+{
+    /* We are never called for a GETNEXT if it's registered as a
+       "instance", as it's "magically" handled for us.  */
+
+    /* a instance handler also only hands us one request at a time, so
+       we don't need to loop over a list of requests; we'll only get one. */
+    
+    switch(reqinfo->mode) {
+
+        case MODE_GET:
+            snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
+					ver_compiler, strlen(ver_compiler));
+            break;
+
+
+        default:
+            /* we should never get here, so this is a really bad error */
+            snmp_log(LOG_ERR, "unknown mode (%d) in handle_kamailioSrvCnfVerCompiler\n", reqinfo->mode );
+            return SNMP_ERR_GENERR;
+    }
+
+    return SNMP_ERR_NOERROR;
+}
+int
+handle_kamailioSrvCnfVerFlags(netsnmp_mib_handler *handler,
+                          netsnmp_handler_registration *reginfo,
+                          netsnmp_agent_request_info   *reqinfo,
+                          netsnmp_request_info         *requests)
+{
+    /* We are never called for a GETNEXT if it's registered as a
+       "instance", as it's "magically" handled for us.  */
+
+    /* a instance handler also only hands us one request at a time, so
+       we don't need to loop over a list of requests; we'll only get one. */
+    
+    switch(reqinfo->mode) {
+
+        case MODE_GET:
+            snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
+					ver_flags, strlen(ver_flags));
+            break;
+
+
+        default:
+            /* we should never get here, so this is a really bad error */
+            snmp_log(LOG_ERR, "unknown mode (%d) in handle_kamailioSrvCnfVerFlags\n", reqinfo->mode );
+            return SNMP_ERR_GENERR;
+    }
+
+    return SNMP_ERR_NOERROR;
+}

+ 52 - 0
modules/snmpstats/kamailioServer.h

@@ -0,0 +1,52 @@
+/*
+ * SNMPStats Module 
+ * Copyright (C) 2006 SOMA Networks, INC.
+ * Written by: Jeffrey Magder ([email protected])
+ *
+ * Kamailio Server core objects addition
+ * Copyright (C) 2013 Edvina AB, Sollentuna, Sweden
+ * Written by Olle E. Johansson
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * History:
+ * --------
+ * 2013-03-24 initial version (oej)
+ * 
+ * Note: this file originally auto-generated by mib2c 
+ *
+ */
+#ifndef KAMAILIOSERVER_H
+#define KAMAILIOSERVER_H
+
+/* function declarations */
+void init_kamailioServer(void);
+Netsnmp_Node_Handler handle_kamailioSrvMaxMemory;
+Netsnmp_Node_Handler handle_kamailioSrvFreeMemory;
+
+Netsnmp_Node_Handler handle_kamailioSrvCnfFullVersion;
+Netsnmp_Node_Handler handle_kamailioSrvCnfVerName;
+Netsnmp_Node_Handler handle_kamailioSrvCnfVerVersion;
+Netsnmp_Node_Handler handle_kamailioSrvCnfVerArch;
+Netsnmp_Node_Handler handle_kamailioSrvCnfVerOs;
+Netsnmp_Node_Handler handle_kamailioSrvCnfVerId;
+Netsnmp_Node_Handler handle_kamailioSrvCnfVerCompTime;
+Netsnmp_Node_Handler handle_kamailioSrvCnfVerCompiler;
+Netsnmp_Node_Handler handle_kamailioSrvCnfVerFlags;
+
+#endif /* KAMAILIOSERVER_H */

+ 302 - 2
modules/snmpstats/mibs/KAMAILIO-MIB

@@ -7,6 +7,7 @@
 -- 
 -- 
 -- Copyright (c) The Internet Society (2006)
 -- Copyright (c) The Internet Society (2006)
 -- Ammendments (c) Soma Networks, Inc. (2006)
 -- Ammendments (c) Soma Networks, Inc. (2006)
+-- Kamailio extras (c) Edvina AB, 2013
 --
 --
 -- All rights reserved.
 -- All rights reserved.
 -- *****************************************************************
 -- *****************************************************************
@@ -25,7 +26,11 @@ KAMAILIO-MIB DEFINITIONS ::= BEGIN
         Gauge32
         Gauge32
             FROM SNMPv2-SMI
             FROM SNMPv2-SMI
             
             
-        DateAndTime
+        DateAndTime,
+	TEXTUAL-CONVENTION,
+	DisplayString,
+	RowStatus,
+	TruthValue
             FROM SNMPv2-TC
             FROM SNMPv2-TC
 
 
         SnmpAdminString
         SnmpAdminString
@@ -72,6 +77,9 @@ KAMAILIO-MIB DEFINITIONS ::= BEGIN
         REVISION   "201301081200Z"
         REVISION   "201301081200Z"
         DESCRIPTION
         DESCRIPTION
            "Renamed MIB and mib entries to new product name, Kamailio."
            "Renamed MIB and mib entries to new product name, Kamailio."
+        REVISION   "201303231200Z"
+        DESCRIPTION
+           "Added Kamailio core memory allocation data."
         ::= { kamailioModules 5 }
         ::= { kamailioModules 5 }
 
 
 --
 --
@@ -94,7 +102,6 @@ KAMAILIO-MIB DEFINITIONS ::= BEGIN
         DESCRIPTION 
         DESCRIPTION 
         "Sub-tree for Conformance specifications."
         "Sub-tree for Conformance specifications."
         ::= { kamailioMIB 3 }
         ::= { kamailioMIB 3 }
-                                
 
 
 --
 --
 -- kamailioObjects sub-components
 -- kamailioObjects sub-components
@@ -117,9 +124,302 @@ KAMAILIO-MIB DEFINITIONS ::= BEGIN
         "Sub-tree for tracking of SIP Dialogs being processed by Kamailio."
         "Sub-tree for tracking of SIP Dialogs being processed by Kamailio."
     ::= { kamailioObjects 3 }
     ::= { kamailioObjects 3 }
 
 
+   kamailioNet OBJECT-IDENTITY
+        STATUS current
+        DESCRIPTION 
+        "Sub-tree for tracking of Network Transports being enabled in Kamailio."
+    ::= { kamailioObjects 4 }
+
 -- 
 -- 
 -- Kamailio Server Objects
 -- Kamailio Server Objects
 --
 --
+    kamailioSrvMemory OBJECT-IDENTITY
+        STATUS current
+        DESCRIPTION 
+        "Sub-tree for Core Memory Statistics." 
+        ::= { kamailioServer 1 }
+
+    kamailioSrvConfig OBJECT-IDENTITY
+        STATUS current
+        DESCRIPTION 
+        "Sub-tree for Core configuration - compile flags etc." 
+        ::= { kamailioServer 2 }
+
+    kamailioSrvCounters OBJECT-IDENTITY
+        STATUS current
+        DESCRIPTION 
+        "Sub-tree for Core Counters/Statistic variables." 
+        ::= { kamailioServer 3 }
+
+    kamailioSrvProcess OBJECT-IDENTITY
+        STATUS current
+        DESCRIPTION 
+        "Sub-tree for Core Processes." 
+        ::= { kamailioServer 4 }
+
+-- 
+-- Kamailio Server Configuration
+--
+
+    kamailioSrvCnfFullVersion OBJECT-TYPE
+        SYNTAX DisplayString
+        MAX-ACCESS  read-only
+        STATUS current
+        DESCRIPTION 
+        "Full version of Kamailio"
+        ::= { kamailioSrvConfig 1 }
+
+    kamailioSrvCnfVerName OBJECT-TYPE
+        SYNTAX DisplayString
+        MAX-ACCESS  read-only
+        STATUS current
+        DESCRIPTION 
+        "Version of Kamailio"
+        ::= { kamailioSrvConfig 2 }
+
+    kamailioSrvCnfVerVersion OBJECT-TYPE
+        SYNTAX DisplayString
+        MAX-ACCESS  read-only
+        STATUS current
+        DESCRIPTION 
+        "Version of Kamailio"
+        ::= { kamailioSrvConfig 3 }
+
+    kamailioSrvCnfVerArch OBJECT-TYPE
+        SYNTAX      DisplayString
+        MAX-ACCESS  read-only
+        STATUS current
+        DESCRIPTION 
+        "Architecture Kamailio is compiled for"
+        ::= { kamailioSrvConfig 4 }
+
+    kamailioSrvCnfVerOs	OBJECT-TYPE
+        SYNTAX DisplayString
+        MAX-ACCESS  read-only
+        STATUS current
+        DESCRIPTION 
+        "Operating System Kamailio is compiled for"
+        ::= { kamailioSrvConfig 5 }
+
+    kamailioSrvCnfVerId	OBJECT-TYPE
+        SYNTAX DisplayString
+        MAX-ACCESS  read-only
+        STATUS current
+        DESCRIPTION 
+        "Kamailio Version ID"
+        ::= { kamailioSrvConfig 6 }
+
+    kamailioSrvCnfVerCompTime OBJECT-TYPE
+        SYNTAX DisplayString
+        MAX-ACCESS  read-only
+        STATUS current
+        DESCRIPTION 
+        "Kamailio Build time"
+        ::= { kamailioSrvConfig 7 }
+
+    kamailioSrvCnfVerCompiler OBJECT-TYPE
+        SYNTAX DisplayString
+        MAX-ACCESS  read-only
+        STATUS current
+        DESCRIPTION 
+        "Kamailio Build compiler used"
+        ::= { kamailioSrvConfig 8 }
+
+    kamailioSrvCnfVerFlags OBJECT-TYPE
+        SYNTAX DisplayString
+        MAX-ACCESS  read-only
+        STATUS current
+        DESCRIPTION 
+        "Kamailio Build compile time flags"
+        ::= { kamailioSrvConfig 9 }
+
+
+-- 
+-- Kamailio Server Memory data
+--
+    kamailioSrvMaxMemory OBJECT-TYPE
+        SYNTAX      Gauge32
+        MAX-ACCESS  read-only
+        STATUS current
+        DESCRIPTION 
+        "Maximum available Shared Memory"
+        ::= { kamailioSrvMemory 1 }
+
+    kamailioSrvFreeMemory OBJECT-TYPE
+        SYNTAX      Gauge32
+        MAX-ACCESS  read-only
+        STATUS current
+        DESCRIPTION 
+        "Available free memory in the Shared Memory pool"
+        ::= { kamailioSrvMemory 2 }
+
+-- 
+-- Kamailio Server Core counters
+--
+
+-- 
+-- Kamailio Network Objects
+--
+    kamailioNetTcp OBJECT-IDENTITY
+        STATUS current
+        DESCRIPTION 
+        "Sub-tree for TCP Network Transport objects (incl TLS conncetions)." 
+        ::= { kamailioNet 1 }
+
+
+    kamailioNetWebsocket OBJECT-IDENTITY
+        STATUS current
+        DESCRIPTION 
+        "Sub-tree for Websocket Network Transport objects ." 
+        ::= { kamailioNet 2 }
+
+    kamailioNetHttp OBJECT-IDENTITY
+        STATUS current
+        DESCRIPTION 
+        "Sub-tree for HTTP Server Transport objects ." 
+        ::= { kamailioNet 3 }
+
+
+--
+-- TCP connection objects
+--
+    kamailioNetTcpTls OBJECT-IDENTITY
+        STATUS current
+        DESCRIPTION 
+        "Sub-tree for TCP/TLS objects." 
+        ::= { kamailioNetTcp 1 }
+
+    kamailioNetTcpStat OBJECT-IDENTITY
+        STATUS current
+        DESCRIPTION 
+        "Sub-tree for TCP/TLS statistics." 
+        ::= { kamailioNetTcp 2 }
+
+    kamailioNetTcpConfig OBJECT-IDENTITY
+        STATUS current
+        DESCRIPTION 
+        "Sub-tree for TCP/TLS configuration data." 
+        ::= { kamailioNetTcp 3 }
+
+--
+-- TCP connection config objects
+--
+    kamailioNetTcpEnabled OBJECT-TYPE
+        SYNTAX  TruthValue
+        MAX-ACCESS  read-only
+        STATUS current
+	DEFVAL { false }
+        DESCRIPTION 
+        "True if TCP is enabled in this server."
+        ::= { kamailioNetTcpConfig 1 }
+
+    kamailioNetTcpMaxConns OBJECT-TYPE
+        SYNTAX  Integer32
+        MAX-ACCESS  read-only
+        STATUS current
+        DESCRIPTION 
+        "Maximum number of TCP connections (configurable)"
+        ::= { kamailioNetTcpConfig 2 }
+
+    kamailioNetTcpAsync OBJECT-TYPE
+        SYNTAX  TruthValue
+        MAX-ACCESS  read-only
+        STATUS current
+        DESCRIPTION 
+        "True if TCP Async is enabled in this server."
+        ::= { kamailioNetTcpConfig 3 }
+
+	---	connect_timeout: 10
+	---	send_timeout: 10
+	---	connection_lifetime: 120
+	---	max_connections(soft): 2048
+	---	max_tls_connections(soft): 2048
+	---	no_connect: 0
+	---	fd_cache: 1
+	---	async: 1
+	---	connect_wait: 1
+	---	conn_wq_max: 32768
+	---	wq_max: 10485760
+	---	defer_accept: 0
+	---	delayed_ack: 0
+	---	syncnt: 0
+	---	linger2: 0
+	---	keepalive: 1
+	---	keepidle: 0
+	---	keepintvl: 0
+	---	keepcnt: 0
+	---	crlf_ping: 1
+	---	accept_aliases: 0
+	---	alias_flags: 1
+	---	new_conn_alias_flags: 2
+
+--
+-- TCP connection statistic objects
+--
+    kamailioNetTcpConnEstablished OBJECT-TYPE
+        SYNTAX      Counter32
+        MAX-ACCESS  read-only
+        STATUS current
+        DESCRIPTION 
+        "Incremented each time a TCP connection is established."
+        ::= { kamailioNetTcpStat 1 }
+
+    kamailioNetTcpConnFailed OBJECT-TYPE
+        SYNTAX      Counter32
+        MAX-ACCESS  read-only
+        STATUS current
+        DESCRIPTION 
+        "Number of failed active TCP connection attempts."
+        ::= { kamailioNetTcpStat 2 }
+
+    kamailioNetTcpConnReset OBJECT-TYPE
+        SYNTAX      Counter32
+        MAX-ACCESS  read-only
+        STATUS current
+        DESCRIPTION 
+        "Number of reset TCP connections"
+        ::= { kamailioNetTcpStat 3 }
+
+    kamailioNetTcpConnSuccess OBJECT-TYPE
+        SYNTAX      Counter32
+        MAX-ACCESS  read-only
+        STATUS current
+        DESCRIPTION 
+        "Number of successful TCP connections"
+        ::= { kamailioNetTcpStat 4 }
+
+    kamailioNetTcpConnOpened OBJECT-TYPE
+        SYNTAX      Gauge32
+        MAX-ACCESS  read-only
+        STATUS current
+        DESCRIPTION 
+        "Number of current opened TCP connections"
+        ::= { kamailioNetTcpStat 5 }
+
+    kamailioNetTcpConnPassiveOpen OBJECT-TYPE
+        SYNTAX      Counter32
+        MAX-ACCESS  read-only
+        STATUS current
+        DESCRIPTION 
+        "Total number of accepted TCP connections (so far)."
+        ::= { kamailioNetTcpStat 6 }
+
+    kamailioNetTcpConnSuccess OBJECT-TYPE
+        SYNTAX      Counter32
+        MAX-ACCESS  read-only
+        STATUS current
+        DESCRIPTION 
+        "Total number of successfully active opened TCP connections"
+        ::= { kamailioNetTcpStat 7 }
+
+    kamailioNetTcpConnReject OBJECT-TYPE
+        SYNTAX      Counter32
+        MAX-ACCESS  read-only
+        STATUS current
+        DESCRIPTION 
+        "Number of rejected incoming TCP connections."
+        ::= { kamailioNetTcpStat 8 }
+
 
 
 
 
 --
 --

+ 0 - 1
modules/snmpstats/mibs/KAMAILIO-SIP-COMMON-MIB

@@ -113,7 +113,6 @@ KAMAILIO-SIP-COMMON-MIB DEFINITIONS ::= BEGIN
               Registrar: A registrar is a server that accepts
               Registrar: A registrar is a server that accepts
               REGISTER requests and places the information it
               REGISTER requests and places the information it
               receives in those requests into the location service
               receives in those requests into the location service
-
               for the domain it handles.
               for the domain it handles.
 
 
               Copyright (C) The Internet Society (2005). This version
               Copyright (C) The Internet Society (2005). This version

+ 5 - 15
modules/snmpstats/snmpSIPMethodSupportedTable.c

@@ -140,23 +140,16 @@ void init_kamailioSIPMethodSupportedTable(void)
 	 * NOTE: My way of checking what METHODS we support is probably wrong.
 	 * NOTE: My way of checking what METHODS we support is probably wrong.
 	 * Please feel free to correct it! */
 	 * Please feel free to correct it! */
 	
 	
-	if (module_loaded("sl")) {
-		createRow(1, "METHOD_INVITE");
-		createRow(2, "METHOD_CANCEL");
-		createRow(3, "METHOD_ACK");
-	}
-
-	if (module_loaded("tm")) {
-		createRow(4, "METHOD_BYE");
-	}
+	createRow(1, "METHOD_INVITE");
+	createRow(2, "METHOD_CANCEL");
+	createRow(3, "METHOD_ACK");
+	createRow(4, "METHOD_BYE");
 
 
 	if (module_loaded("options") || module_loaded("siputils")) {
 	if (module_loaded("options") || module_loaded("siputils")) {
 		createRow(6, "METHOD_OPTIONS");
 		createRow(6, "METHOD_OPTIONS");
 	}
 	}
 
 
-	if (module_loaded("dialog")) {
-		createRow(7, "METHOD_UPDATE");
-	}
+	createRow(7, "METHOD_UPDATE");
 
 
 	if (module_loaded("registrar")) {
 	if (module_loaded("registrar")) {
 		createRow(8, "METHOD_REGISTER");
 		createRow(8, "METHOD_REGISTER");
@@ -166,9 +159,6 @@ void init_kamailioSIPMethodSupportedTable(void)
 
 
 	createRow(5,  "METHOD_INFO");
 	createRow(5,  "METHOD_INFO");
 	createRow(9,  "METHOD_MESSAGE");
 	createRow(9,  "METHOD_MESSAGE");
-
-	/* I'm not sure what these guys are, so saying we support them by
-	 * default.  */
 	createRow(12, "METHOD_PRACK");
 	createRow(12, "METHOD_PRACK");
 	createRow(13, "METHOD_REFER");
 	createRow(13, "METHOD_REFER");
 	createRow(14, "METHOD_PUBLISH");
 	createRow(14, "METHOD_PUBLISH");

+ 1 - 1
modules/snmpstats/snmpSIPServerObjects.c

@@ -196,7 +196,7 @@ int handle_kamailioSIPProxyStatefulness(netsnmp_mib_handler *handler,
 {
 {
 	int statefullness;
 	int statefullness;
 
 
-	if (module_loaded("dialog")) 
+	if (module_loaded("dialog") || module_loaded("dialog_ng")) 
 	{
 	{
 		statefullness = PROXY_STATEFULNESS_CALL_STATEFUL;
 		statefullness = PROXY_STATEFULNESS_CALL_STATEFUL;
 	}
 	}

+ 10 - 0
modules/snmpstats/sub_agent.c

@@ -63,6 +63,8 @@
 #include "snmpSIPContactTable.h"
 #include "snmpSIPContactTable.h"
 #include "snmpSIPRegUserLookupTable.h"
 #include "snmpSIPRegUserLookupTable.h"
 #include "snmpMIBNotifications.h"
 #include "snmpMIBNotifications.h"
+#include "kamailioServer.h"
+#include "kamailioNet.h"
 
 
 #include "../../dprint.h"
 #include "../../dprint.h"
 #include "../../cfg/cfg_struct.h"
 #include "../../cfg/cfg_struct.h"
@@ -91,6 +93,8 @@ static int initialize_agentx(void)
 {
 {
 	/* We register with a master agent */
 	/* We register with a master agent */
 	register_with_master_agent(AGENT_PROCESS_NAME);
 	register_with_master_agent(AGENT_PROCESS_NAME);
+
+	LM_DBG("Initializing Kamailio OID's for SNMPD MasterX\n");
 	
 	
 	/* Initialize all scalars, and let the master agent know we want to
 	/* Initialize all scalars, and let the master agent know we want to
 	 * handle all OID's pertaining to these scalars. */
 	 * handle all OID's pertaining to these scalars. */
@@ -106,6 +110,9 @@ static int initialize_agentx(void)
 	init_kamailioSIPRegUserTable();
 	init_kamailioSIPRegUserTable();
 	init_kamailioSIPContactTable();
 	init_kamailioSIPContactTable();
 	init_kamailioSIPRegUserLookupTable();
 	init_kamailioSIPRegUserLookupTable();
+	init_kamailioServer();
+	init_kamailioNet();
+	LM_DBG("Done initializing Kamailio OID's for SNMPD MasterX\n");
 
 
 	/* In case we recevie a request to stop (kill -TERM or kill -INT) */
 	/* In case we recevie a request to stop (kill -TERM or kill -INT) */
 	keep_running = 1;
 	keep_running = 1;
@@ -117,6 +124,7 @@ static int initialize_agentx(void)
 		agent_check_and_process(1); /* 0 == don't block */
 		agent_check_and_process(1); /* 0 == don't block */
 	}
 	}
 
 
+	LM_DBG("Shutting down Kamailio SNMPD MasterX sub agent.\n");
 	snmp_shutdown(AGENT_PROCESS_NAME);
 	snmp_shutdown(AGENT_PROCESS_NAME);
 	SOCK_CLEANUP;
 	SOCK_CLEANUP;
 	exit (0);
 	exit (0);
@@ -181,10 +189,12 @@ void register_with_master_agent(char *name_to_register_under)
 	/* Initialize TCP if necessary.  (Its here for WIN32) compatibility. */
 	/* Initialize TCP if necessary.  (Its here for WIN32) compatibility. */
 	SOCK_STARTUP;
 	SOCK_STARTUP;
 
 
+	LM_DBG("Connecting to SNMPD MasterX\n");
 	/* Read in our configuration file to determine master agent ping times
 	/* Read in our configuration file to determine master agent ping times
 	 * what port communication is to take place over, etc. */
 	 * what port communication is to take place over, etc. */
 	init_agent("snmpstats");
 	init_agent("snmpstats");
 
 
 	/* Use a name we can register our agent under. */
 	/* Use a name we can register our agent under. */
 	init_snmp(name_to_register_under);
 	init_snmp(name_to_register_under);
+	LM_DBG("** Connected to SNMPD MasterX\n");
 }
 }

+ 28 - 23
modules/usrloc/README

@@ -580,6 +580,11 @@ modparam("usrloc", "db_url", "dbdriver://username:password@dbhost/dbname")
        For example NAT pinging is a killer since during each ping cycle
        For example NAT pinging is a killer since during each ping cycle
        all nated contact are loaded from the DB; The lack of memory
        all nated contact are loaded from the DB; The lack of memory
        caching also disable the statistics exports.
        caching also disable the statistics exports.
+     * 4 - This uses database to load records at startup but uses only
+       memory during the runtime. Records are not written back at all, not
+       even at shutdown. Useful for scenarios when registrations are
+       replicated to a node that does the storage in database during
+       runtime.
 
 
 Warning
 Warning
 
 
@@ -740,7 +745,7 @@ modparam("usrloc", "xavp_contact", "ulattrs")
    5.5. ul_add
    5.5. ul_add
    5.6. ul_show_contact
    5.6. ul_show_contact
 
 
-5.1.  ul_rm
+5.1. ul_rm
 
 
    Deletes an entire AOR record (including its contacts).
    Deletes an entire AOR record (including its contacts).
 
 
@@ -749,7 +754,7 @@ modparam("usrloc", "xavp_contact", "ulattrs")
      * AOR - user AOR in username[@domain] format (domain must be supplied
      * AOR - user AOR in username[@domain] format (domain must be supplied
        only if use_domain option is on).
        only if use_domain option is on).
 
 
-5.2.  ul_rm_contact
+5.2. ul_rm_contact
 
 
    Deletes a contact from an AOR record.
    Deletes a contact from an AOR record.
 
 
@@ -759,7 +764,7 @@ modparam("usrloc", "xavp_contact", "ulattrs")
        only if use_domain option is on).
        only if use_domain option is on).
      * contact - exact contact to be removed
      * contact - exact contact to be removed
 
 
-5.3.  ul_dump
+5.3. ul_dump
 
 
    Dumps the entire content of the USRLOC in memory cache
    Dumps the entire content of the USRLOC in memory cache
 
 
@@ -768,11 +773,11 @@ modparam("usrloc", "xavp_contact", "ulattrs")
        "brief", a brief dump will be done (only AOR and contacts, with no
        "brief", a brief dump will be done (only AOR and contacts, with no
        other details)
        other details)
 
 
-5.4.  ul_flush
+5.4. ul_flush
 
 
    Triggers the flush of USRLOC memory cache into DB.
    Triggers the flush of USRLOC memory cache into DB.
 
 
-5.5.  ul_add
+5.5. ul_add
 
 
    Adds a new contact for an user AOR.
    Adds a new contact for an user AOR.
 
 
@@ -788,7 +793,7 @@ modparam("usrloc", "xavp_contact", "ulattrs")
      * cflags - per branch flags of the contact
      * cflags - per branch flags of the contact
      * methods - mask with supported requests of the contact
      * methods - mask with supported requests of the contact
 
 
-5.6.  ul_show_contact
+5.6. ul_show_contact
 
 
    Dumps the contacts of an user AOR.
    Dumps the contacts of an user AOR.
 
 
@@ -802,14 +807,14 @@ modparam("usrloc", "xavp_contact", "ulattrs")
    6.1. ul.dump
    6.1. ul.dump
    6.2. ul.lookup table AOR
    6.2. ul.lookup table AOR
 
 
-6.1.  ul.dump
+6.1. ul.dump
 
 
    Dumps the content of the location table
    Dumps the content of the location table
 
 
    Parameters:
    Parameters:
      * None.
      * None.
 
 
-6.2.  ul.lookup table AOR
+6.2. ul.lookup table AOR
 
 
    Looks up the contents of an AOR entry in the location table
    Looks up the contents of an AOR entry in the location table
 
 
@@ -897,7 +902,7 @@ Chapter 2. Developer Guide
    1.14. ul_register_ulcb(type ,callback, param)
    1.14. ul_register_ulcb(type ,callback, param)
    1.15. ul_get_num_users()
    1.15. ul_get_num_users()
 
 
-1.1.  ul_register_domain(name)
+1.1. ul_register_domain(name)
 
 
    The function registers a new domain. Domain is just another name for
    The function registers a new domain. Domain is just another name for
    table used in registrar. The function is called from fixups in
    table used in registrar. The function is called from fixups in
@@ -912,7 +917,7 @@ Chapter 2. Developer Guide
      * const char* name - Name of the domain (also called table) to be
      * const char* name - Name of the domain (also called table) to be
        registered.
        registered.
 
 
-1.2.  ul_insert_urecord(domain, aor, rec)
+1.2. ul_insert_urecord(domain, aor, rec)
 
 
    The function creates a new record structure and inserts it in the
    The function creates a new record structure and inserts it in the
    specified domain. The record is structure that contains all the
    specified domain. The record is structure that contains all the
@@ -927,7 +932,7 @@ Chapter 2. Developer Guide
 
 
      * urecord_t** rec - The newly created record structure.
      * urecord_t** rec - The newly created record structure.
 
 
-1.3.  ul_delete_urecord(domain, aor)
+1.3. ul_delete_urecord(domain, aor)
 
 
    The function deletes all the contacts bound with the given Address Of
    The function deletes all the contacts bound with the given Address Of
    Record.
    Record.
@@ -939,7 +944,7 @@ Chapter 2. Developer Guide
      * str* aor - Address of record (aka username) of the record, that
      * str* aor - Address of record (aka username) of the record, that
        should be deleted.
        should be deleted.
 
 
-1.4.  ul_get_urecord(domain, aor)
+1.4. ul_get_urecord(domain, aor)
 
 
    The function returns pointer to record with given Address of Record.
    The function returns pointer to record with given Address of Record.
 
 
@@ -949,7 +954,7 @@ Chapter 2. Developer Guide
 
 
      * str* aor - Address of Record of request record.
      * str* aor - Address of Record of request record.
 
 
-1.5.  ul_lock_udomain(domain)
+1.5. ul_lock_udomain(domain)
 
 
    The function lock the specified domain, it means, that no other
    The function lock the specified domain, it means, that no other
    processes will be able to access during the time. This prevents race
    processes will be able to access during the time. This prevents race
@@ -960,14 +965,14 @@ Chapter 2. Developer Guide
    Meaning of the parameters is as follows:
    Meaning of the parameters is as follows:
      * udomain_t* domain - Domain to be locked.
      * udomain_t* domain - Domain to be locked.
 
 
-1.6.  ul_unlock_udomain(domain)
+1.6. ul_unlock_udomain(domain)
 
 
    Unlock the specified domain previously locked by ul_lock_udomain.
    Unlock the specified domain previously locked by ul_lock_udomain.
 
 
    Meaning of the parameters is as follows:
    Meaning of the parameters is as follows:
      * udomain_t* domain - Domain to be unlocked.
      * udomain_t* domain - Domain to be unlocked.
 
 
-1.7.  ul_release_urecord(record)
+1.7. ul_release_urecord(record)
 
 
    Do some sanity checks - if all contacts have been removed, delete the
    Do some sanity checks - if all contacts have been removed, delete the
    entire record structure.
    entire record structure.
@@ -975,7 +980,7 @@ Chapter 2. Developer Guide
    Meaning of the parameters is as follows:
    Meaning of the parameters is as follows:
      * urecord_t* record - Record to be released.
      * urecord_t* record - Record to be released.
 
 
-1.8.  ul_insert_ucontact(record, contact, expires, q, callid, cseq, flags,
+1.8. ul_insert_ucontact(record, contact, expires, q, callid, cseq, flags,
 cont, ua, sock)
 cont, ua, sock)
 
 
    The function inserts a new contact in the given record with specified
    The function inserts a new contact in the given record with specified
@@ -996,7 +1001,7 @@ cont, ua, sock)
      * struct socket_info *sock - socket on which the REGISTER message was
      * struct socket_info *sock - socket on which the REGISTER message was
        received on.
        received on.
 
 
-1.9.  ul_delete_ucontact (record, contact)
+1.9. ul_delete_ucontact (record, contact)
 
 
    The function deletes given contact from record.
    The function deletes given contact from record.
 
 
@@ -1006,7 +1011,7 @@ cont, ua, sock)
 
 
      * ucontact_t* contact - Contact to be deleted.
      * ucontact_t* contact - Contact to be deleted.
 
 
-1.10.  ul_get_ucontact(record, contact)
+1.10. ul_get_ucontact(record, contact)
 
 
    The function tries to find contact with given Contact URI and returns
    The function tries to find contact with given Contact URI and returns
    pointer to structure representing the contact.
    pointer to structure representing the contact.
@@ -1016,7 +1021,7 @@ cont, ua, sock)
 
 
      * str_t* contact - URI of the request contact.
      * str_t* contact - URI of the request contact.
 
 
-1.11.  ul_get_all_ucontacts (buf, len, flags)
+1.11. ul_get_all_ucontacts (buf, len, flags)
 
 
    The function retrieves all contacts of all registered users and returns
    The function retrieves all contacts of all registered users and returns
    them in the caller-supplied buffer. If the buffer is too small, the
    them in the caller-supplied buffer. If the buffer is too small, the
@@ -1037,7 +1042,7 @@ cont, ua, sock)
 
 
      * unsigned int flags - Flags that must be set.
      * unsigned int flags - Flags that must be set.
 
 
-1.12.  ul_update_ucontact(contact, expires, q, callid, cseq, set, res, ua,
+1.12. ul_update_ucontact(contact, expires, q, callid, cseq, set, res, ua,
 sock)
 sock)
 
 
    The function updates contact with new values.
    The function updates contact with new values.
@@ -1056,7 +1061,7 @@ sock)
      * struct socket_info *sock - socket on which the REGISTER message was
      * struct socket_info *sock - socket on which the REGISTER message was
        received on.
        received on.
 
 
-1.13.  ul_bind_ursloc( api )
+1.13. ul_bind_ursloc( api )
 
 
    The function imports all functions that are exported by the USRLOC
    The function imports all functions that are exported by the USRLOC
    module. Overs for other modules which want to user the internal USRLOC
    module. Overs for other modules which want to user the internal USRLOC
@@ -1065,7 +1070,7 @@ sock)
    Meaning of the parameters is as follows:
    Meaning of the parameters is as follows:
      * usrloc_api_t* api - USRLOC API
      * usrloc_api_t* api - USRLOC API
 
 
-1.14.  ul_register_ulcb(type ,callback, param)
+1.14. ul_register_ulcb(type ,callback, param)
 
 
    The function register with USRLOC a callback function to be called when
    The function register with USRLOC a callback function to be called when
    some event occures inside USRLOC.
    some event occures inside USRLOC.
@@ -1078,6 +1083,6 @@ sock)
      * void *param - some parameter to be passed to the callback each time
      * void *param - some parameter to be passed to the callback each time
        when it is called.
        when it is called.
 
 
-1.15.  ul_get_num_users()
+1.15. ul_get_num_users()
 
 
    The function loops through all domains summing up the number of users.
    The function loops through all domains summing up the number of users.

+ 9 - 0
modules/usrloc/doc/usrloc_admin.xml

@@ -588,6 +588,15 @@ modparam("usrloc", "db_url", "&exampledb;")
 			caching also disable the statistics exports.
 			caching also disable the statistics exports.
 			</para>
 			</para>
 		</listitem>
 		</listitem>
+		<listitem>
+			<para>
+			4 - This uses database to load records at startup but uses only
+			memory during the runtime. Records are not written back at all,
+			not even at shutdown. Useful for scenarios when registrations are
+			replicated to a node that does the storage in database during
+			runtime.
+			</para>
+		</listitem>
 		</itemizedlist>
 		</itemizedlist>
 		<warning>
 		<warning>
 		<para>
 		<para>

+ 9 - 5
modules/usrloc/ul_mod.c

@@ -413,15 +413,19 @@ static int child_init(int _rank)
 			return 0;
 			return 0;
 		case DB_ONLY:
 		case DB_ONLY:
 		case WRITE_THROUGH:
 		case WRITE_THROUGH:
-			/* we need connection from working SIP and TIMER and MAIN
-			 * processes only */
+			/* connect to db only from SIP workers, TIMER and MAIN processes */
 			if (_rank<=0 && _rank!=PROC_TIMER && _rank!=PROC_MAIN)
 			if (_rank<=0 && _rank!=PROC_TIMER && _rank!=PROC_MAIN)
 				return 0;
 				return 0;
 			break;
 			break;
 		case WRITE_BACK:
 		case WRITE_BACK:
-			/* connect only from TIMER (for flush), from MAIN (for
+			/* connect to db only from TIMER (for flush), from MAIN (for
 			 * final flush() and from child 1 for preload */
 			 * final flush() and from child 1 for preload */
-			if (_rank!=PROC_TIMER && _rank!=PROC_MAIN && _rank!=1)
+			if (_rank!=PROC_TIMER && _rank!=PROC_MAIN && _rank!=PROC_SIPINIT)
+				return 0;
+			break;
+		case DB_READONLY:
+			/* connect to db only from child 1 for preload */
+			if(_rank!=PROC_SIPINIT)
 				return 0;
 				return 0;
 			break;
 			break;
 	}
 	}
@@ -432,7 +436,7 @@ static int child_init(int _rank)
 		return -1;
 		return -1;
 	}
 	}
 	/* _rank==PROC_SIPINIT is used even when fork is disabled */
 	/* _rank==PROC_SIPINIT is used even when fork is disabled */
-	if (_rank==PROC_SIPINIT && db_mode!= DB_ONLY) {
+	if (_rank==PROC_SIPINIT && db_mode!=DB_ONLY) {
 		/* if cache is used, populate domains from DB */
 		/* if cache is used, populate domains from DB */
 		for( ptr=root ; ptr ; ptr=ptr->next) {
 		for( ptr=root ; ptr ; ptr=ptr->next) {
 			if (preload_udomain(ul_dbh, ptr->d) < 0) {
 			if (preload_udomain(ul_dbh, ptr->d) < 0) {

+ 1 - 0
modules/usrloc/urecord.c

@@ -382,6 +382,7 @@ static inline void wb_timer(urecord_t* _r)
 void timer_urecord(urecord_t* _r)
 void timer_urecord(urecord_t* _r)
 {
 {
 	switch(db_mode) {
 	switch(db_mode) {
+	case DB_READONLY:
 	case NO_DB:         nodb_timer(_r);
 	case NO_DB:         nodb_timer(_r);
 						break;
 						break;
 	/* use also the write_back timer routine to handle the failed
 	/* use also the write_back timer routine to handle the failed

+ 1 - 0
modules/usrloc/usrloc.h

@@ -40,6 +40,7 @@
 #define WRITE_THROUGH 1
 #define WRITE_THROUGH 1
 #define WRITE_BACK    2
 #define WRITE_BACK    2
 #define DB_ONLY       3
 #define DB_ONLY       3
+#define DB_READONLY   4
 
 
 /*forward declaration necessary for udomain*/
 /*forward declaration necessary for udomain*/
 
 

+ 4 - 0
pkg/kamailio/fedora/17/kamailio.spec

@@ -518,6 +518,7 @@ fi
 %doc %{_docdir}/kamailio/modules/README.cfg_db
 %doc %{_docdir}/kamailio/modules/README.cfg_db
 %doc %{_docdir}/kamailio/modules/README.cfg_rpc
 %doc %{_docdir}/kamailio/modules/README.cfg_rpc
 %doc %{_docdir}/kamailio/modules/README.cfgutils
 %doc %{_docdir}/kamailio/modules/README.cfgutils
+%doc %{_docdir}/kamailio/modules/README.cnxcc
 %doc %{_docdir}/kamailio/modules/README.corex
 %doc %{_docdir}/kamailio/modules/README.corex
 %doc %{_docdir}/kamailio/modules/README.counters
 %doc %{_docdir}/kamailio/modules/README.counters
 %doc %{_docdir}/kamailio/modules/README.ctl
 %doc %{_docdir}/kamailio/modules/README.ctl
@@ -657,6 +658,7 @@ fi
 %{_libdir}/kamailio/modules/cfg_db.so
 %{_libdir}/kamailio/modules/cfg_db.so
 %{_libdir}/kamailio/modules/cfg_rpc.so
 %{_libdir}/kamailio/modules/cfg_rpc.so
 %{_libdir}/kamailio/modules/cfgutils.so
 %{_libdir}/kamailio/modules/cfgutils.so
+%{_libdir}/kamailio/modules/cnxcc.so
 %{_libdir}/kamailio/modules/corex.so
 %{_libdir}/kamailio/modules/corex.so
 %{_libdir}/kamailio/modules/counters.so
 %{_libdir}/kamailio/modules/counters.so
 %{_libdir}/kamailio/modules/ctl.so
 %{_libdir}/kamailio/modules/ctl.so
@@ -1088,6 +1090,8 @@ fi
 
 
 
 
 %changelog
 %changelog
+* Wed Mar 27 2013 Peter Dunkley <[email protected]>
+  - Added cnxcc module to .spec
 * Thu Mar 7 2013 Peter Dunkley <[email protected]>
 * Thu Mar 7 2013 Peter Dunkley <[email protected]>
   - Added build requirement for docbook2X for Fedora builds
   - Added build requirement for docbook2X for Fedora builds
 * Wed Mar 6 2013 Peter Dunkley <[email protected]>
 * Wed Mar 6 2013 Peter Dunkley <[email protected]>

+ 16 - 0
sr_module.c

@@ -64,6 +64,7 @@
 #include "globals.h"
 #include "globals.h"
 #include "rpc_lookup.h"
 #include "rpc_lookup.h"
 #include "sr_compat.h"
 #include "sr_compat.h"
+#include "ppcfg.h"
 
 
 #include <sys/stat.h>
 #include <sys/stat.h>
 #include <regex.h>
 #include <regex.h>
@@ -271,6 +272,7 @@ static int register_module(unsigned ver, union module_exports_u* e,
 {
 {
 	int ret, i;
 	int ret, i;
 	struct sr_module* mod;
 	struct sr_module* mod;
+	char defmod[64];
 
 
 	ret=-1;
 	ret=-1;
 
 
@@ -366,6 +368,20 @@ static int register_module(unsigned ver, union module_exports_u* e,
 		/* i==0 => success */
 		/* i==0 => success */
 	}
 	}
 
 
+	/* add cfg define for each module: MOD_modulename */
+	if(strlen(mod->exports.name)>=60) {
+		LM_ERR("too long module name: %s\n", mod->exports.name);
+		goto error;
+	}
+	strcpy(defmod, "MOD_");
+	strcat(defmod, mod->exports.name);
+	pp_define_set_type(0);
+	if(pp_define(strlen(defmod), defmod)<0) {
+		LM_ERR("unable to set cfg define for module: %s\n",
+				mod->exports.name);
+		goto error;
+	}
+
 	/* link module in the list */
 	/* link module in the list */
 	mod->next=modules;
 	mod->next=modules;
 	modules=mod;
 	modules=mod;

+ 17 - 4
udp_server.c

@@ -321,11 +321,24 @@ int udp_init(struct socket_info* sock_info)
 	}
 	}
 	/* tos */
 	/* tos */
 	optval = tos;
 	optval = tos;
-	if (setsockopt(sock_info->socket, IPPROTO_IP, IP_TOS, (void*)&optval, 
-			sizeof(optval)) ==-1){
-		LOG(L_WARN, "WARNING: udp_init: setsockopt tos: %s\n", strerror(errno));
-		/* continue since this is not critical */
+	if (addr->s.sa_family==AF_INET){
+		if (setsockopt(sock_info->socket, IPPROTO_IP, IP_TOS, (void*)&optval, 
+				sizeof(optval)) ==-1){
+			LOG(L_WARN, "WARNING: udp_init: setsockopt tos: %s\n",
+					strerror(errno));
+			/* continue since this is not critical */
+		}
+#ifdef USE_IPV6
+	} else if (addr->s.sa_family==AF_INET6){
+		if (setsockopt(sock_info->socket, IPPROTO_IPV6, IPV6_TCLASS,
+					(void*)&optval, sizeof(optval)) ==-1) {
+			LOG(L_WARN, "WARNING: udp_init: setsockopt v6 tos: %s\n",
+					strerror(errno));
+			/* continue since this is not critical */
+		}
+#endif
 	}
 	}
+
 #if defined (__OS_linux) && defined(UDP_ERRORS)
 #if defined (__OS_linux) && defined(UDP_ERRORS)
 	optval=1;
 	optval=1;
 	/* enable error receiving on unconnected sockets */
 	/* enable error receiving on unconnected sockets */

+ 0 - 7
utils/sercmd/sercmd.c

@@ -2154,25 +2154,19 @@ int main(int argc, char** argv)
 {
 {
 	int c;
 	int c;
 	char* sock_name;
 	char* sock_name;
-	int port_no;
 	int sock_type;
 	int sock_type;
 	int s;
 	int s;
 	struct binrpc_cmd cmd;
 	struct binrpc_cmd cmd;
-	int rec;
 	struct id_list* sock_id;
 	struct id_list* sock_id;
 	char* format;
 	char* format;
-	int interactive;
 	char* line;
 	char* line;
 	char* l;
 	char* l;
 
 
 	quit=0;
 	quit=0;
 	format=0;
 	format=0;
 	line=0;
 	line=0;
-	interactive=0;
-	rec=1;
 	s=-1;
 	s=-1;
 	sock_name=0;
 	sock_name=0;
-	port_no=0;
 	sock_type=UNIXS_SOCK;
 	sock_type=UNIXS_SOCK;
 	opterr=0;
 	opterr=0;
 	while((c=getopt(argc, argv, "UVhs:D:R:vf:"))!=-1){
 	while((c=getopt(argc, argv, "UVhs:D:R:vf:"))!=-1){
@@ -2277,7 +2271,6 @@ int main(int argc, char** argv)
 	sock_id=0;
 	sock_id=0;
 	
 	
 	if (optind>=argc){
 	if (optind>=argc){
-			interactive=1;
 			/*fprintf(stderr, "ERROR: no command specified\n");
 			/*fprintf(stderr, "ERROR: no command specified\n");
 			goto error; */
 			goto error; */
 	}else{
 	}else{