Pārlūkot izejas kodu

Merge pull request #3 from kamailio/master

Update my kamailio repository master after merging lrkproxy
mojtabaesfandiari 4 gadi atpakaļ
vecāks
revīzija
0cecab4a74
34 mainītis faili ar 4376 papildinājumiem un 349 dzēšanām
  1. 1 1
      src/Makefile.groups
  2. 2 2
      src/modules/ctl/io_listener.c
  3. 2 2
      src/modules/htable/htable.c
  4. 12 3
      src/modules/ims_diameter_server/ims_diameter_server.c
  5. 14 0
      src/modules/lrkproxy/Makefile
  6. 316 0
      src/modules/lrkproxy/README
  7. 4 0
      src/modules/lrkproxy/doc/Makefile
  8. 33 0
      src/modules/lrkproxy/doc/lrkproxy.xml
  9. 364 0
      src/modules/lrkproxy/doc/lrkproxy_admin.xml
  10. 1732 0
      src/modules/lrkproxy/lrkproxy.c
  11. 107 0
      src/modules/lrkproxy/lrkproxy.h
  12. 479 0
      src/modules/lrkproxy/lrkproxy_funcs.c
  13. 41 0
      src/modules/lrkproxy/lrkproxy_funcs.h
  14. 522 0
      src/modules/lrkproxy/lrkproxy_hash.c
  15. 75 0
      src/modules/lrkproxy/lrkproxy_hash.h
  16. 63 22
      src/modules/pv/pv_branch.c
  17. 0 1
      src/modules/rtp_media_server/Makefile
  18. 302 278
      src/modules/textops/README
  19. 40 0
      src/modules/textops/doc/textops_admin.xml
  20. 64 0
      src/modules/textops/textops.c
  21. 12 3
      src/modules/topos/README
  22. 9 0
      src/modules/topos/doc/topos.xml
  23. 5 2
      src/modules/topos/doc/topos_admin.xml
  24. 5 0
      src/modules/topos/tps_msg.c
  25. 33 8
      src/modules/topos/tps_storage.c
  26. 2 0
      src/modules/topos/tps_storage.h
  27. 6 0
      src/modules/topos_redis/README
  28. 9 0
      src/modules/topos_redis/doc/topos_redis.xml
  29. 63 12
      src/modules/topos_redis/topos_redis_storage.c
  30. 27 9
      src/modules/uac_redirect/README
  31. 22 0
      src/modules/uac_redirect/doc/uac_redirect_admin.xml
  32. 2 2
      src/modules/uac_redirect/rd_funcs.c
  33. 4 0
      src/modules/uac_redirect/uac_redirect.c
  34. 4 4
      src/modules/usrloc/ul_rpc.c

+ 1 - 1
src/Makefile.groups

@@ -23,7 +23,7 @@ mod_list_extra=avp auth_diameter call_control call_obj dmq domainpolicy msrp \
 			carrierroute pdb qos sca seas sms sst timer tmrec uac_redirect \
 			carrierroute pdb qos sca seas sms sst timer tmrec uac_redirect \
 			xhttp xhttp_rpc xprint jsonrpcs nosip dmq_usrloc statsd rtjson \
 			xhttp xhttp_rpc xprint jsonrpcs nosip dmq_usrloc statsd rtjson \
 			log_custom keepalive ss7ops app_sqlang acc_diameter evrexec \
 			log_custom keepalive ss7ops app_sqlang acc_diameter evrexec \
-			sipjson xhttp_prom
+			sipjson lrkproxy xhttp_prom
 
 
 # - 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 \

+ 2 - 2
src/modules/ctl/io_listener.c

@@ -495,7 +495,7 @@ again:
 		DBG("handle_stream read: eof on %s\n", s_c->parent->name);
 		DBG("handle_stream read: eof on %s\n", s_c->parent->name);
 		goto close_connection;
 		goto close_connection;
 	}
 	}
-	LM_INFO("bytes read: %d\n", bytes_read);
+	LM_DBG("bytes read: %d\n", bytes_read);
 	r->end+=bytes_read;
 	r->end+=bytes_read;
 	if (bytes_read && (bytes_read<r->bytes_to_go)){
 	if (bytes_read && (bytes_read<r->bytes_to_go)){
 		r->bytes_to_go-=bytes_read;
 		r->bytes_to_go-=bytes_read;
@@ -515,7 +515,7 @@ again:
 			/* error while processing the packet => close the connection */
 			/* error while processing the packet => close the connection */
 			goto close_connection;
 			goto close_connection;
 		}
 		}
-		LM_INFO("bytes processed: %d\n", bytes_processed);
+		LM_DBG("bytes processed: %d\n", bytes_processed);
 		r->proc+=bytes_processed;
 		r->proc+=bytes_processed;
 		r->bytes_to_go=bytes_needed;
 		r->bytes_to_go=bytes_needed;
 		if (bytes_needed>0){
 		if (bytes_needed>0){

+ 2 - 2
src/modules/htable/htable.c

@@ -484,7 +484,7 @@ static int ht_rm_items(sip_msg_t* msg, str* hname, str* op, str *val,
 		case 2:
 		case 2:
 			if(strncmp(op->s, "re", 2)==0) {
 			if(strncmp(op->s, "re", 2)==0) {
 				isval.s = *val;
 				isval.s = *val;
-				if (ht_dmq_replicate_action(HT_DMQ_RM_CELL_RE, &ht->name, NULL,
+				if ((ht->dmqreplicate > 0) && ht_dmq_replicate_action(HT_DMQ_RM_CELL_RE, &ht->name, NULL,
 							AVP_VAL_STR, &isval, mkey)!=0) {
 							AVP_VAL_STR, &isval, mkey)!=0) {
 					LM_ERR("dmq relication failed (op %d)\n", mkey);
 					LM_ERR("dmq relication failed (op %d)\n", mkey);
 				}
 				}
@@ -494,7 +494,7 @@ static int ht_rm_items(sip_msg_t* msg, str* hname, str* op, str *val,
 				return 1;
 				return 1;
 			} else if(strncmp(op->s, "sw", 2)==0) {
 			} else if(strncmp(op->s, "sw", 2)==0) {
 				isval.s = *val;
 				isval.s = *val;
-				if (ht_dmq_replicate_action(HT_DMQ_RM_CELL_SW, &ht->name, NULL,
+				if ((ht->dmqreplicate > 0) &&ht_dmq_replicate_action(HT_DMQ_RM_CELL_SW, &ht->name, NULL,
 							AVP_VAL_STR, &isval, mkey)!=0) {
 							AVP_VAL_STR, &isval, mkey)!=0) {
 					LM_ERR("dmq relication failed (op %d)\n", mkey);
 					LM_ERR("dmq relication failed (op %d)\n", mkey);
 				}
 				}

+ 12 - 3
src/modules/ims_diameter_server/ims_diameter_server.c

@@ -280,6 +280,10 @@ int diameter_request(struct sip_msg * msg, char* peer, char* appid, char* comman
 		}
 		}
 		LM_DBG("Peer %.*s\n", s_peer.len, s_peer.s);
 		LM_DBG("Peer %.*s\n", s_peer.len, s_peer.s);
 	}
 	}
+	if (get_str_fparam(&s_message, msg, (fparam_t*)message) < 0) {
+		LM_ERR("failed to get Message\n");
+		return -1;
+	}
 	if (get_str_fparam(&s_appid, msg, (fparam_t*)appid) < 0) {
 	if (get_str_fparam(&s_appid, msg, (fparam_t*)appid) < 0) {
 		LM_ERR("failed to get App-ID\n");
 		LM_ERR("failed to get App-ID\n");
 		return -1;
 		return -1;
@@ -306,9 +310,14 @@ int diameter_request(struct sip_msg * msg, char* peer, char* appid, char* comman
 	session = cdpb.AAACreateSession(0);
 	session = cdpb.AAACreateSession(0);
 
 
 	req = cdpb.AAACreateRequest(i_appid, i_commandcode, Flag_Proxyable, session);
 	req = cdpb.AAACreateRequest(i_appid, i_commandcode, Flag_Proxyable, session);
+        if (session) {
+	        cdpb.AAADropSession(session);
+                session = 0;
+        }
+
 	if (!req) goto error1;
 	if (!req) goto error1;
 
 
-	if (addAVPsfromJSON(req, &s_message)) {
+	if (!addAVPsfromJSON(req, &s_message)) {
 		LM_ERR("Failed to parse JSON Request\n");
 		LM_ERR("Failed to parse JSON Request\n");
 		return -1;
 		return -1;
 	}
 	}
@@ -322,7 +331,7 @@ int diameter_request(struct sip_msg * msg, char* peer, char* appid, char* comman
 		} else {
 		} else {
 			resp = cdpb.AAASendRecvMessageToPeer(req, &s_peer);
 			resp = cdpb.AAASendRecvMessageToPeer(req, &s_peer);
 			LM_DBG("Successfully sent diameter\n");
 			LM_DBG("Successfully sent diameter\n");
-			if (AAAmsg2json(resp, &responsejson) == 1) {
+			if (resp && AAAmsg2json(resp, &responsejson) == 1) {
 				return 1;
 				return 1;
 			} else {
 			} else {
 				LM_ERR("Failed to convert response to JSON\n");
 				LM_ERR("Failed to convert response to JSON\n");
@@ -337,7 +346,7 @@ int diameter_request(struct sip_msg * msg, char* peer, char* appid, char* comman
 		} else {
 		} else {
 			resp = cdpb.AAASendRecvMessage(req);
 			resp = cdpb.AAASendRecvMessage(req);
 			LM_DBG("Successfully sent diameter\n");
 			LM_DBG("Successfully sent diameter\n");
-			if (AAAmsg2json(resp, &responsejson) == 1) {
+			if (resp && AAAmsg2json(resp, &responsejson) == 1) {
 				return 1;
 				return 1;
 			} else {
 			} else {
 				LM_ERR("Failed to convert response to JSON\n");
 				LM_ERR("Failed to convert response to JSON\n");

+ 14 - 0
src/modules/lrkproxy/Makefile

@@ -0,0 +1,14 @@
+#
+# lrkproxy module makefile
+#
+#
+# WARNING: do not run this directly, it should be run by the main Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=lrkproxy.so
+LIBS=
+
+SERLIBPATH=../../lib
+include ../../Makefile.modules
+

+ 316 - 0
src/modules/lrkproxy/README

@@ -0,0 +1,316 @@
+lrkproxy Module
+
+Mojtaba Esfandiari.S
+
+   Nasim Telecom
+
+   Copyright © 2020 Nasim Telecom Inc.
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+        2. LRKProxy Architecture
+
+              2.1. LRKP_Controlling Layer (LRKP_CL)
+              2.2. LRKP_Transport Stateful Layer (LRKP_TSL)
+
+        3. Multiple LRKProxy usage
+        4. Dependencies
+
+              4.1. Kamailio Modules
+              4.2. External Libraries or Applications
+              4.3. Parameters
+
+                    4.3.1. lrkproxy_sock (string)
+                    4.3.2. lrkproxy_disable_tout (integer)
+                    4.3.3. lrkproxy_tout (integer)
+                    4.3.4. lrkproxy_retr (integer)
+                    4.3.5. lrkp_alg (integer)
+                    4.3.6. hash_table_tout (integer)
+                    4.3.7. hash_table_size (integer)
+
+              4.4. Functions
+
+                    4.4.1. set_lrkproxy_set(setid)
+                    4.4.2. lrkproxy_manage([flags [, ip_address]])
+
+   List of Examples
+
+   1.1. Set lrkproxy_sock parameter
+   1.2. Set lrkproxy_disable_tout parameter
+   1.3. Set lrkproxy_tout parameter
+   1.4. Set lrkproxy_retr parameter
+   1.5. Set lrkp_alg parameter
+   1.6. Set hash_table_tout parameter
+   1.7. Set hash_table_size parameter
+   1.8. set_lrkproxy_set usage
+   1.9. lrkproxy_manage usage
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+   2. LRKProxy Architecture
+
+        2.1. LRKP_Controlling Layer (LRKP_CL)
+        2.2. LRKP_Transport Stateful Layer (LRKP_TSL)
+
+   3. Multiple LRKProxy usage
+   4. Dependencies
+
+        4.1. Kamailio Modules
+        4.2. External Libraries or Applications
+        4.3. Parameters
+
+              4.3.1. lrkproxy_sock (string)
+              4.3.2. lrkproxy_disable_tout (integer)
+              4.3.3. lrkproxy_tout (integer)
+              4.3.4. lrkproxy_retr (integer)
+              4.3.5. lrkp_alg (integer)
+              4.3.6. hash_table_tout (integer)
+              4.3.7. hash_table_size (integer)
+
+        4.4. Functions
+
+              4.4.1. set_lrkproxy_set(setid)
+              4.4.2. lrkproxy_manage([flags [, ip_address]])
+
+1. Overview
+
+   This is a module that enables media streams to be relayed via an
+   lrkproxy. This module works with py_lrkproxy engine in:
+   https://github.com/mojtabaesfandiari/pylrkproxy This module does
+   relaying audio streams between peers in PREROUTING netfilter-hooking
+   section in kernel-space linux. The LRKProxy architecture is composed of
+   two different layers. These layers are independent of each other.
+
+2. LRKProxy Architecture
+
+   2.1. LRKP_Controlling Layer (LRKP_CL)
+   2.2. LRKP_Transport Stateful Layer (LRKP_TSL)
+
+2.1. LRKP_Controlling Layer (LRKP_CL)
+
+   The first layer is developed as User-Space application that allows
+   User-Space to directly access and manipulate cache data buffer and
+   packet buffer in Kernel-Space. This layer gets all information about
+   creating new sessions, active sessions and tear-down sessions which is
+   gotten from SDP body during signaling plan and relay them to the
+   LRKP-Transport Stateful Layer (LRKP- TSL).
+
+2.2. LRKP_Transport Stateful Layer (LRKP_TSL)
+
+   The second layer is developed in Kernel-Space as a main decision point
+   for RTP admission controller and Quickpath selector to where a received
+   packet should be forwarded with power of packet mangling framework in
+   the network stack.
+
+   The LRKP_CL and LRKP-TSL could be run as independence functions on
+   different machines. We could have one LRKP_CL with multiple LRKP-TSL on
+   different machines. The LRKP_CL could works with all LRKP-TSL with
+   different strategies(lrkp_alg parameter).
+
+3. Multiple LRKProxy usage
+
+   The LRKP_CL Layer can support multiple LRKP_TSL Layer for
+   balancing/distribution and control/selection purposes.
+
+   The module allows definition of several sets of LRKP_TSL.
+   Load-balancing will be performed over predefine algorithm by setting
+   lrkp_alg parameter.
+
+   IMPORTANT: This module does not support balancing inside a set like as
+   is done RTPProxy module based on the weight of each rtpproxy from the
+   set. The balancing would be run on different machine
+
+4. Dependencies
+
+   4.1. Kamailio Modules
+   4.2. External Libraries or Applications
+   4.3. Parameters
+
+        4.3.1. lrkproxy_sock (string)
+        4.3.2. lrkproxy_disable_tout (integer)
+        4.3.3. lrkproxy_tout (integer)
+        4.3.4. lrkproxy_retr (integer)
+        4.3.5. lrkp_alg (integer)
+        4.3.6. hash_table_tout (integer)
+        4.3.7. hash_table_size (integer)
+
+   4.4. Functions
+
+        4.4.1. set_lrkproxy_set(setid)
+        4.4.2. lrkproxy_manage([flags [, ip_address]])
+
+4.1. Kamailio Modules
+
+   The following modules must be loaded before this module:
+     * tm module - (optional) if you want to have lrkproxy_manage() fully
+       functional
+
+4.2. External Libraries or Applications
+
+   The following libraries or applications must be installed before
+   running Kamailio with this module loaded:
+     * None.
+
+4.3. Parameters
+
+4.3.1. lrkproxy_sock (string)
+
+   Used to define the list of LRKP_TSL instances to connect to. These can
+   be UNIX sockets or IPv4/IPv6 UDP sockets. Each modparam entry will
+   insert sockets into a single set with default value set ID '0'. To
+   define multiple LRKP_TSL, just add the instances in each modparam.
+
+   Default value is "NONE" (disabled).
+
+   Example 1.1. Set lrkproxy_sock parameter
+...
+# single lrkproxy
+modparam("lrkproxy", "lrkproxy_sock", "udp:192.168.122.108:8080")
+
+# multiple lrkproxies for LB in diffenrent machine
+modparam("lrkproxy", "lrkproxy_sock", "udp:192.168.122.108:8080")
+modparam("lrkproxy", "lrkproxy_sock", "udp:192.168.122.109:8080")
+
+...
+
+4.3.2. lrkproxy_disable_tout (integer)
+
+   Once LRKP_TSL was found unreachable and marked as disabled, the LRKP_CL
+   module will not attempt to establish communication to LRKP_TSL for
+   lrkproxy_disable_tout seconds.
+
+   Default value is "60".
+
+   Example 1.2. Set lrkproxy_disable_tout parameter
+...
+modparam("lrkproxy", "lrkproxy_disable_tout", 20)
+...
+
+4.3.3. lrkproxy_tout (integer)
+
+   Timeout value in waiting for reply from LRKP_TSL.
+
+   Default value is "1".
+
+   Example 1.3. Set lrkproxy_tout parameter
+...
+modparam("lrkproxy", "lrkproxy_tout", 2)
+...
+
+4.3.4. lrkproxy_retr (integer)
+
+   How many times the LRKP_CL should retry to send and receive after
+   timeout was generated.
+
+   Default value is "5".
+
+   Example 1.4. Set lrkproxy_retr parameter
+...
+modparam("lrkproxy", "lrkproxy_retr", 2)
+...
+
+4.3.5. lrkp_alg (integer)
+
+   This parameter set the algorithm of LRKP_TSL selection. lrk_LINER=0,
+   lrk_RR=1
+
+   Default value is "0".
+
+   Example 1.5. Set lrkp_alg parameter
+...
+modparam("lrkproxy", "lrkp_alg", 1)
+...
+
+4.3.6. hash_table_tout (integer)
+
+   Number of seconds after an lrkproxy hash table entry is marked for
+   deletion. By default, this parameter is set to 3600 (seconds).
+
+   To maintain information about a selected rtp machine node, for a given
+   call, entries are added in a hashtable of (callid, viabranch) pairs.
+   When command comes, lookup callid, viabranch pairs. If found, return
+   chosen node. If not found, choose a new node, insert it in the hastable
+   and return the chosen node.
+
+   NOTE: In the current implementation, the actual deletion happens on the
+   fly, while insert/remove/lookup the hastable, only for the entries in
+   the insert/remove/lookup path.
+
+   NOTE: When configuring this parameter, one should consider maximum call
+   time VS share memory for unfinished calls.
+
+   Default value is "3600".
+
+   Example 1.6. Set hash_table_tout parameter
+...
+modparam("lrkproxy", "hash_table_tout", "3600")
+...
+
+4.3.7. hash_table_size (integer)
+
+   Size of the hash table. Default value is 128.
+
+   Default value is "128".
+
+   Example 1.7. Set hash_table_size parameter
+...
+modparam("lrkproxy", "hash_table_size", 256)
+...
+
+4.4. Functions
+
+4.4.1. set_lrkproxy_set(setid)
+
+   Sets the Id of the lrkproxy set to be used for the next
+   lrkproxy_manage() command. The parameter can be an integer or a config
+   variable holding an integer.
+
+   This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
+   BRANCH_ROUTE.
+
+   Example 1.8. set_lrkproxy_set usage
+...
+set_lrkproxy_set("0");
+lrkproxy_manage();
+...
+
+4.4.2. lrkproxy_manage([flags [, ip_address]])
+
+   Manage the LRKProxy session - it combines the functionality of
+   lrkproxy_offer(), lrkproxy_answer() and unforce_lrkproxy(), detecting
+   internally based on message type and method which one to execute.
+
+   IMPORTANT:The LRKProxy just has one function relating rtp packets. It
+   does not support combination of functionality of lrkproxy_offer(),
+   lrkproxy_answer() and unforce_lrkproxy() and other etc. So you have to
+   just use lrkproxy_manage.
+
+   Meaning of the parameters is as follows:
+     * flags - flags to turn on some features.
+          + internal,external - The shorthand of this flag is "ie". This
+            can be used to relay media sessions between two different NIC
+            from internal to external path.
+          + external,internal - The shorthand of this flag is "ei". This
+            can be used to relay media sessions between two different NIC
+            from external to internal path.
+     * ip_address - new SDP IP address.This optional parameter is under
+       development.
+
+   This function can be used from ANY_ROUTE.
+
+   Example 1.9. lrkproxy_manage usage
+...
+lrkproxy_manage();
+//or
+lrkproxy_manage("ie");
+//or
+lrkproxy_manage("ei");
+
+...

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

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

+ 33 - 0
src/modules/lrkproxy/doc/lrkproxy.xml

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../../doc/docbook/entities.xml">
+%docentities;
+
+]>
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+	<bookinfo>
+	<title>lrkproxy Module</title>
+	<productname class="trade">&kamailioname;</productname>
+	<authorgroup>
+		<author>
+		<firstname>Mojtaba</firstname>
+		<surname>Esfandiari.S</surname>
+		<affiliation><orgname>Nasim Telecom</orgname></affiliation>
+		<address>
+			<email>[email protected]</email>
+		</address>
+		</author>
+	</authorgroup>
+	<copyright>
+		<year>2020</year>
+		<holder><ulink url='https://www.nasimtelecom.com/en/'>Nasim Telecom Inc.</ulink></holder>
+	</copyright>
+	</bookinfo>
+	<toc></toc>
+	
+	<xi:include href="lrkproxy_admin.xml"/>
+</book>

+ 364 - 0
src/modules/lrkproxy/doc/lrkproxy_admin.xml

@@ -0,0 +1,364 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../../doc/docbook/entities.xml">
+%docentities;
+
+]>
+
+<!-- Module User's Guide -->
+
+<chapter>
+
+	<title>&adminguide;</title>
+
+	<section>
+	<title>Overview</title>
+	<para>
+		This is a module that enables media streams to be relayed via an
+ 		lrkproxy. This module works with py_lrkproxy engine in:
+		https://github.com/mojtabaesfandiari/pylrkproxy
+		This module does relaying audio streams between peers in
+		PREROUTING netfilter-hooking section in kernel-space linux.
+		The LRKProxy architecture is composed of two
+		different layers. These layers are independent of each
+		other.
+	</para>
+	</section>
+
+	<section>
+	<title>LRKProxy Architecture</title>
+	<section>
+		<title>LRKP_Controlling Layer (LRKP_CL)</title>
+		<para>
+			The first layer is developed as User-Space
+			application that allows User-Space to directly
+			access and manipulate cache data
+			buffer and packet buffer in Kernel-Space. This layer
+			gets all information about creating new sessions,
+			active sessions and tear-down sessions which is
+	       		gotten from SDP body during signaling plan and relay
+			them to the LRKP-Transport Stateful Layer (LRKP-
+			TSL).
+		</para>
+	</section>
+	<section>
+		<title>LRKP_Transport Stateful Layer (LRKP_TSL)</title>
+		<para>
+			The second layer is developed in Kernel-Space as
+        		a main decision point for RTP admission controller
+        		and Quickpath selector to where a received packet
+        		should be forwarded with power of packet mangling
+        		framework in the network stack.
+		</para>
+	</section>
+	<para>
+		The LRKP_CL and LRKP-TSL could be run as
+		independence functions on different machines. We
+		could have one LRKP_CL with multiple LRKP-TSL
+		on different machines. The LRKP_CL could works
+		with all LRKP-TSL with different strategies(lrkp_alg parameter).
+	</para>
+	</section>
+	<section>
+	<title>Multiple LRKProxy usage</title>
+	<para>
+		The LRKP_CL Layer can support multiple LRKP_TSL Layer
+    		for balancing/distribution and control/selection purposes.
+	</para>
+	<para>
+		The module allows definition of several sets of LRKP_TSL.
+		Load-balancing will be performed over predefine algorithm by setting lrkp_alg parameter.
+
+	</para>
+	<para>
+		IMPORTANT: This module does not support balancing inside a set like as is done RTPProxy module based on
+		the weight of each rtpproxy from the set. The balancing would be run on different machine
+	</para>
+	</section>
+	
+	<section>
+	<title>Dependencies</title>
+	<section>
+		<title>&kamailio; Modules</title>
+		<para>
+		The following modules must be loaded before this module:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>tm module</emphasis> - (optional) if you want to
+				have lrkproxy_manage() fully functional
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	<section>
+		<title>External Libraries or Applications</title>
+		<para>
+		The following libraries or applications must be installed before
+		running &kamailio; with this module loaded:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>None</emphasis>.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	<section>
+	<title>Parameters</title>
+	<section id="lrkproxy.p.lrkproxy_sock">
+	<title><varname>lrkproxy_sock</varname> (string)</title>
+			<para>
+			Used to define the list of LRKP_TSL instances to connect to. These can
+			be UNIX sockets or IPv4/IPv6 UDP sockets. Each modparam entry will
+   			insert sockets into a single set with default value set ID '0'.
+   			To define multiple LRKP_TSL, just add the instances in each modparam.
+			</para>
+			<para>
+			<emphasis>
+				Default value is <quote>NONE</quote> (disabled).
+			</emphasis>
+			</para>
+			<example>
+			<title>Set <varname>lrkproxy_sock</varname> parameter</title>
+			<programlisting format="linespecific">
+...
+# single lrkproxy
+modparam("lrkproxy", "lrkproxy_sock", "udp:192.168.122.108:8080")
+
+# multiple lrkproxies for LB in diffenrent machine
+modparam("lrkproxy", "lrkproxy_sock", "udp:192.168.122.108:8080")
+modparam("lrkproxy", "lrkproxy_sock", "udp:192.168.122.109:8080")
+
+...
+</programlisting>
+		</example>
+	</section>
+	<section id="lrkproxy.p.lrkproxy_disable_tout">
+		<title><varname>lrkproxy_disable_tout</varname> (integer)</title>
+		<para>
+		Once LRKP_TSL was found unreachable and marked as disabled, the
+   		LRKP_CL module will not attempt to establish communication to LRKP_TSL
+   		for lrkproxy_disable_tout seconds.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>60</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>lrkproxy_disable_tout</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("lrkproxy", "lrkproxy_disable_tout", 20)
+...
+</programlisting>
+		</example>
+	</section>
+	<section id="lrkproxy.p.lrkproxy_tout">
+		<title><varname>lrkproxy_tout</varname> (integer)</title>
+		<para>
+		Timeout value in waiting for reply from LRKP_TSL.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>1</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>lrkproxy_tout</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("lrkproxy", "lrkproxy_tout", 2)
+...
+</programlisting>
+		</example>
+	</section>
+	<section id="lrkproxy.p.lrkproxy_retr">
+		<title><varname>lrkproxy_retr</varname> (integer)</title>
+		<para>
+		How many times the LRKP_CL should retry to send and receive after
+		timeout was generated.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>5</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>lrkproxy_retr</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("lrkproxy", "lrkproxy_retr", 2)
+...
+</programlisting>
+		</example>
+	</section>
+	<section id="lrkproxy.p.lrkp_alg">
+		<title><varname>lrkp_alg</varname> (integer)</title>
+		<para>
+		This parameter set the algorithm of LRKP_TSL selection.
+    		lrk_LINER=0,
+    		lrk_RR=1
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>0</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>lrkp_alg</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("lrkproxy", "lrkp_alg", 1)
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section id="lrkproxy.p.hash_table_tout">
+		<title><varname>hash_table_tout</varname> (integer)</title>
+		<para>
+		Number of seconds after an lrkproxy hash table entry is marked for
+   		deletion. By default, this parameter is set to 3600 (seconds).
+		</para>
+		<para>
+		To maintain information about a selected rtp machine node, for a given
+		call, entries are added in a hashtable of (callid, viabranch) pairs. When
+   		command comes, lookup callid, viabranch pairs. If found, return chosen node. If not
+   		found, choose a new node, insert it in the hastable and return the
+   		chosen node.
+		</para>
+		<para>
+		NOTE: In the current implementation, the actual deletion happens on the
+		fly, while insert/remove/lookup the hastable, only for the entries in
+		the insert/remove/lookup path.
+		</para>
+		<para>
+		NOTE: When configuring this parameter, one should consider maximum call
+		time VS share memory for unfinished calls.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>3600</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>hash_table_tout</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("lrkproxy", "hash_table_tout", "3600")
+...
+</programlisting>
+		</example>
+	</section>
+	<section id="lrkproxy.p.hash_table_size">
+		<title><varname>hash_table_size</varname> (integer)</title>
+		<para>
+		Size of the hash table. Default value is 128.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>128</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>hash_table_size</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("lrkproxy", "hash_table_size", 256)
+...
+</programlisting>
+		</example>
+	</section>
+	</section>
+	<section>
+	<title>Functions</title>
+	<section id="lrkproxy.f.set_lrkproxy_set">
+		<title>
+		<function moreinfo="none">set_lrkproxy_set(setid)</function>
+		</title>
+		<para>
+		Sets the Id of the lrkproxy set to be used for the next
+   		lrkproxy_manage() command. The parameter can be an integer or a config
+   		variable holding an integer.
+		</para>
+		<para>
+		This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
+		BRANCH_ROUTE.
+		</para>
+		<example>
+		<title><function>set_lrkproxy_set</function> usage</title>
+		<programlisting format="linespecific">
+...
+set_lrkproxy_set("0");
+lrkproxy_manage();
+...
+</programlisting>
+		</example>
+	</section>
+        <section id="lrkproxy.f.lrkproxy_manage">
+                <title>
+                <function moreinfo="none">lrkproxy_manage([flags [, ip_address]])</function>
+                </title>
+                <para>
+                Manage the LRKProxy session - it combines the functionality of
+   		lrkproxy_offer(), lrkproxy_answer() and unforce_lrkproxy(), detecting
+   		internally based on message type and method which one to execute.
+                </para>
+		<para>
+		   IMPORTANT:The LRKProxy just has one function relating rtp packets. 
+		   It does not support combination of functionality of lrkproxy_offer(),
+		   lrkproxy_answer() and unforce_lrkproxy() and other etc.
+		   So you have to just use lrkproxy_manage.
+		</para>
+		<para>Meaning of the parameters is as follows:</para>
+		<itemizedlist>
+		<listitem>
+			<para>
+			<emphasis>flags</emphasis> - flags to turn on some features.
+			</para>
+			<itemizedlist>
+				<listitem><para>
+				<emphasis>internal,external</emphasis> - The shorthand of this flag is "ie".
+				This can be used to relay media sessions between two different NIC from internal to external path.
+				</para></listitem>
+			</itemizedlist>
+			<itemizedlist>
+				<listitem><para>
+				<emphasis>external,internal</emphasis> - The shorthand of this flag is "ei".
+				This can be used to relay media sessions between two different NIC from external to internal path.
+				</para></listitem>
+			</itemizedlist>
+		</listitem>
+		<listitem><para>
+		<emphasis>ip_address</emphasis> - new SDP IP address.This optional parameter is under development.
+		</para></listitem>
+		</itemizedlist>
+		<para>
+		This function can be used from ANY_ROUTE.
+                </para>
+		<example>
+		<title><function>lrkproxy_manage</function> usage</title>
+		<programlisting format="linespecific">
+...
+lrkproxy_manage();
+//or
+lrkproxy_manage("ie");
+//or
+lrkproxy_manage("ei");
+
+...
+</programlisting>
+                </example>
+	</section>
+
+
+	</section>
+	</section>
+</chapter>

+ 1732 - 0
src/modules/lrkproxy/lrkproxy.c

@@ -0,0 +1,1732 @@
+/*
+ * Copyright (C) 2003-2008 Sippy Software, Inc., http://www.sippysoft.com
+ * Copyright (C) 2020 Mojtaba Esfandiari.S, Nasim-Telecom
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#ifndef __USE_BSD
+#define  __USE_BSD
+#endif
+#include <netinet/ip.h>
+#ifndef __FAVOR_BSD
+#define __FAVOR_BSD
+#endif
+#include <netinet/udp.h>
+#include <arpa/inet.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../../core/flags.h"
+#include "../../core/sr_module.h"
+#include "../../core/dprint.h"
+#include "../../core/data_lump.h"
+#include "../../core/data_lump_rpl.h"
+#include "../../core/error.h"
+#include "../../core/forward.h"
+#include "../../core/mem/mem.h"
+#include "../../core/parser/parse_from.h"
+#include "../../core/parser/parse_to.h"
+#include "../../core/parser/parse_uri.h"
+#include "../../core/parser/parser_f.h"
+#include "../../core/parser/sdp/sdp.h"
+#include "../../core/resolve.h"
+#include "../../core/timer.h"
+#include "../../core/trim.h"
+#include "../../core/ut.h"
+#include "../../core/pt.h"
+#include "../../core/timer_proc.h"
+#include "../../core/rpc.h"
+#include "../../core/rpc_lookup.h"
+#include "../../core/pvar.h"
+#include "../../core/lvalue.h"
+#include "../../core/msg_translator.h"
+#include "../../core/usr_avp.h"
+#include "../../core/socket_info.h"
+#include "../../core/mod_fix.h"
+#include "../../core/dset.h"
+#include "../../core/route.h"
+#include "../../core/kemi.h"
+#include "../../modules/tm/tm_load.h"
+#include "lrkproxy.h"
+#include "lrkproxy_hash.h"
+#include "lrkproxy_funcs.h"
+
+MODULE_VERSION
+
+
+#if !defined(AF_LOCAL)
+#define	AF_LOCAL AF_UNIX
+#endif
+#if !defined(PF_LOCAL)
+#define	PF_LOCAL PF_UNIX
+#endif
+
+///* NAT UAC test constants */
+//#define	NAT_UAC_TEST_C_1918	0x01
+//#define	NAT_UAC_TEST_RCVD	0x02
+//#define	NAT_UAC_TEST_V_1918	0x04
+//#define	NAT_UAC_TEST_S_1918	0x08
+//#define	NAT_UAC_TEST_RPORT	0x10
+
+#define DEFAULT_LRKP_SET_ID		0
+static str DEFAULT_LRKP_SET_ID_STR = str_init("0");
+
+//#define RPC_DEFAULT_NATPING_STATE	1
+
+#define RPC_MIN_RECHECK_TICKS		0
+#define RPC_MAX_RECHECK_TICKS		(unsigned int)-1
+
+
+/* Supported version of the LRK proxy command protocol */
+#define	SUP_CPROTOVER	"20190708"
+/* Required additional version of the LRK proxy command protocol */
+#define	REQ_CPROTOVER	"20190709"
+/* Additional version necessary for re-packetization support */
+#define	REP_CPROTOVER	"20190708"
+#define	PTL_CPROTOVER	"20190708"
+
+#define	CPORT		"22333"
+#define HASH_SIZE   128
+
+static char *gencookie();
+static int lrkp_test(struct lrkp_node*);
+static int lrkp_get_config(struct lrkp_node *node);
+static int lrkp_set_conntrack_rule(struct lrkproxy_hash_entry *e);
+
+
+static int lrkproxy_force(struct sip_msg *msg, const char *flags, enum lrk_operation op, int more);
+static int lrkproxy_unforce(struct sip_msg *msg, const char *flags, enum lrk_operation op, int more);
+
+static int lrkproxy_manage0(struct sip_msg *msg, char *flags, char *ip);
+static int lrkproxy_manage1(struct sip_msg *msg, char *flags, char *ip);
+static int lrkproxy_manage2(struct sip_msg *msg, char *flags, char *ip);
+
+static int change_media_sdp(sip_msg_t *msg, struct lrkproxy_hash_entry *e, const char *flags, enum lrk_operation op);
+
+static int add_lrkproxy_socks(struct lrkp_set * lrkp_list, char * lrkproxy);
+static int fixup_set_id(void ** param, int param_no);
+static int set_lrkproxy_set_f(struct sip_msg * msg, char * str1, char * str2);
+
+static struct lrkp_set * select_lrkp_set(int id_set);
+
+static int lrkproxy_set_store(modparam_t type, void * val);
+static int lrkproxy_add_lrkproxy_set( char * lrk_proxies);
+
+static int mod_init(void);
+static int child_init(int);
+static void mod_destroy(void);
+
+/* Pseudo-Variables */
+//static int pv_get_lrkstat_f(struct sip_msg *, pv_param_t *, pv_value_t *);
+
+static int lrkproxy_disable_tout = 60;
+static int lrkproxy_retr = 5;
+static int lrkproxy_tout = 1;
+static pid_t mypid;
+static unsigned int myseqn = 0;
+//static str nolrkproxy_str = str_init("a=nolrkproxy:yes");
+//static str extra_id_pv_param = {NULL, 0};
+
+static char ** lrkp_strings=0;
+static int lrkp_sets=0; /*used in lrkproxy_set_store()*/
+static int lrkp_set_count = 0;
+static unsigned int current_msg_id = (unsigned int)-1;
+/* LRK proxy balancing list */
+struct lrkp_set_head * lrkp_set_list =0;
+struct lrkp_set * selected_lrkp_set =0;
+struct lrkp_set * default_lrkp_set=0;
+struct lrkp_node *selected_lrkp_node = 0;
+int lrkp_algorithm = LRK_LINER;
+static int hash_table_size = 0;
+static int hash_table_tout = 3600;
+
+
+
+//static char *ice_candidate_priority_avp_param = NULL;
+//static int ice_candidate_priority_avp_type;
+//static int_str ice_candidate_priority_avp;
+//static str lrk_inst_pv_param = {NULL, 0};
+//static pv_spec_t *lrk_inst_pvar = NULL;
+
+/* array with the sockets used by lrkproxy (per process)*/
+static unsigned int lrkp_no = 0;
+static int *lrkp_socks = 0;
+
+
+typedef struct lrkp_set_link {
+	struct lrkp_set *rset;
+	pv_spec_t *rpv;
+} lrkp_set_link_t;
+
+/* tm */
+static struct tm_binds tmb;
+
+/*0-> disabled, 1 ->enabled*/
+//unsigned int *natping_state=0;
+
+static cmd_export_t cmds[] = {
+
+        {"set_lrkproxy_set",  (cmd_function)set_lrkproxy_set_f,    1,
+                fixup_set_id, 0,
+                ANY_ROUTE},
+        {"lrkproxy_manage",	(cmd_function)lrkproxy_manage0,     0,
+                                                       0, 0,
+                   ANY_ROUTE},
+        {"lrkproxy_manage",	(cmd_function)lrkproxy_manage1,     1,
+                                                       fixup_spve_null, fixup_free_spve_null,
+                   ANY_ROUTE},
+        {"lrkproxy_manage",	(cmd_function)lrkproxy_manage2,     2,
+                                                       fixup_spve_spve, fixup_free_spve_spve,
+                   ANY_ROUTE},
+
+        {0, 0, 0, 0, 0, 0}
+};
+
+static param_export_t params[] = {
+        {"lrkproxy_sock",         PARAM_STRING|USE_FUNC_PARAM,
+                (void*)lrkproxy_set_store          },
+        {"lrkproxy_disable_tout", INT_PARAM, &lrkproxy_disable_tout },
+        {"lrkproxy_retr",         INT_PARAM, &lrkproxy_retr         },
+        {"lrkproxy_tout",         INT_PARAM, &lrkproxy_tout         },
+        {"lrkp_alg",         INT_PARAM, &lrkp_algorithm         },
+        {"hash_table_tout",       INT_PARAM, &hash_table_tout        },
+        {"hash_table_size",       INT_PARAM, &hash_table_size        },
+
+        {0, 0, 0}
+};
+
+/** module exports */
+struct module_exports exports= {
+        "lrkproxy",        /* module name */
+        DEFAULT_DLFLAGS, /* dlopen flags */
+        cmds,            /* cmd exports */
+        params,          /* param exports */
+        0,               /* RPC method exports */
+        0,         /* exported pseudo-variables */
+        0,               /* response handling function */
+        mod_init,        /* module initialization function */
+        child_init,               /* per-child init function */
+        mod_destroy                /* module destroy function */
+};
+
+
+static int lrkproxy_set_store(modparam_t type, void * val){
+
+    char * p;
+    int len;
+
+    p = (char* )val;
+
+    if(p==0 || *p=='\0'){
+        return 0;
+    }
+
+    if(lrkp_sets==0){
+        lrkp_strings = (char**)pkg_malloc(sizeof(char*));
+        if(!lrkp_strings){
+                    LM_ERR("no pkg memory left\n");
+            return -1;
+        }
+    } else {/*realloc to make room for the current set*/
+        lrkp_strings = (char**)pkg_reallocxf(lrkp_strings,
+                                             (lrkp_sets+1)* sizeof(char*));
+        if(!lrkp_strings){
+                    LM_ERR("no pkg memory left\n");
+            return -1;
+        }
+    }
+
+    /*allocate for the current set of urls*/
+    len = strlen(p);
+    lrkp_strings[lrkp_sets] = (char*)pkg_malloc((len+1)*sizeof(char));
+
+    if(!lrkp_strings[lrkp_sets]){
+                LM_ERR("no pkg memory left\n");
+        return -1;
+    }
+
+    memcpy(lrkp_strings[lrkp_sets], p, len);
+    lrkp_strings[lrkp_sets][len] = '\0';
+    lrkp_sets++;
+
+    return 0;
+}
+
+struct lrkp_set *get_lrkp_set(str *const set_name)
+{
+    unsigned int this_set_id;
+    struct lrkp_set *lrkp_list;
+    if (lrkp_set_list == NULL)
+    {
+                LM_ERR("lrkp set list not configured\n");
+        return NULL;
+    }
+    /* Only integer set_names are valid at the moment */
+    if ((set_name->s == NULL) || (set_name->len == 0))
+    {
+                LM_ERR("Invalid set name '%.*s'\n", set_name->len, set_name->s);
+        return NULL;
+    }
+    if (str2int(set_name, &this_set_id) < 0)
+    {
+                LM_ERR("Invalid set name '%.*s' - must be integer\n", set_name->len, set_name->s);
+        return NULL;
+    }
+
+    lrkp_list = select_lrkp_set(this_set_id);
+
+    if(lrkp_list==NULL){	/*if a new id_set : add a new set of lrkp*/
+        lrkp_list = shm_malloc(sizeof(struct lrkp_set));
+        if(!lrkp_list){
+                    LM_ERR("no shm memory left\n");
+            return NULL;
+        }
+        memset(lrkp_list, 0, sizeof(struct lrkp_set));
+        lrkp_list->id_set = this_set_id;
+        if (lrkp_set_list->lset_first == NULL)
+        {
+            lrkp_set_list->lset_first = lrkp_list;
+        } else {
+            lrkp_set_list->lset_last->lset_next = lrkp_list;
+        }
+        lrkp_set_list->lset_last = lrkp_list;
+        lrkp_set_count++;
+
+        if (this_set_id == DEFAULT_LRKP_SET_ID)
+        {
+            default_lrkp_set = lrkp_list;
+        }
+    }
+    return lrkp_list;
+}
+
+int insert_lrkp_node(struct lrkp_set *const lrkp_list, const str *const url, const int weight, const int enable)
+{
+    struct lrkp_node *pnode;
+
+    if ((pnode = shm_malloc(sizeof(struct lrkp_node) + url->len + 1)) == NULL)
+    {
+                LM_ERR("out of shm memory\n");
+        return -1;
+    }
+
+    memset(pnode, 0, sizeof(struct lrkp_node) + url->len + 1);
+
+
+    struct lrkp_node_conf *node_conf;
+    node_conf = shm_malloc(sizeof(struct lrkp_node_conf));
+    if (!node_conf)
+    {
+                LM_ERR("out of shm memory\n");
+        return -1;
+    }
+
+    memset(node_conf, 0, sizeof(struct lrkp_node_conf));
+    pnode->lrkp_n_c = node_conf;
+
+    pnode->idx = lrkp_no++;
+    pnode->ln_weight = weight;
+    pnode->ln_umode = 0;
+    pnode->ln_enable = enable;
+    /* Permanently disable if marked as disabled */
+//    pnode->ln_recheck_ticks = disabled ? RPC_MAX_RECHECK_TICKS : 0;
+    pnode->ln_url.s = (char*)(pnode + 1);
+    memcpy(pnode->ln_url.s, url->s, url->len);
+    pnode->ln_url.len = url->len;
+
+            LM_DBG("url is '%.*s'\n", pnode->ln_url.len, pnode->ln_url.s);
+
+    /* Find protocol and store address */
+    pnode->ln_address = pnode->ln_url.s;
+    if (strncasecmp(pnode->ln_address, "udp:", 4) == 0) {
+        pnode->ln_umode = 1;
+        pnode->ln_address += 4;
+    } else if (strncasecmp(pnode->ln_address, "udp6:", 5) == 0) {
+        pnode->ln_umode = 6;
+        pnode->ln_address += 5;
+    } else if (strncasecmp(pnode->ln_address, "unix:", 5) == 0) {
+        pnode->ln_umode = 0;
+        pnode->ln_address += 5;
+    }
+
+    if (lrkp_list->ln_first == NULL)
+    {
+        lrkp_list->ln_first = pnode;
+    } else {
+        lrkp_list->ln_last->ln_next = pnode;
+    }
+    lrkp_list->ln_last = pnode;
+    lrkp_list->lrkp_node_count++;
+
+    return 0;
+}
+
+static int add_lrkproxy_socks(struct lrkp_set * lrkp_list,
+                              char * lrkproxy){
+    /* Make lrk proxies list. */
+    char *p, *p1, *p2, *plim;
+    int weight;
+    str url;
+
+    p = lrkproxy;
+    plim = p + strlen(p);
+
+    for(;;) {
+        weight = 1;
+        while (*p && isspace((int)*p))
+            ++p;
+        if (p >= plim)
+            break;
+        p1 = p;
+        while (*p && !isspace((int)*p))
+            ++p;
+        if (p <= p1)
+            break; /* may happen??? */
+        /* Have weight specified? If yes, scan it */
+        p2 = memchr(p1, '=', p - p1);
+        if (p2 != NULL) {
+            weight = strtoul(p2 + 1, NULL, 10);
+        } else {
+            p2 = p;
+        }
+
+        url.s = p1;
+        url.len = (p2-p1);
+        insert_lrkp_node(lrkp_list, &url, weight, 0);
+    }
+    return 0;
+}
+
+/*	0-succes
+ *  -1 - erorr
+ * */
+static int lrkproxy_add_lrkproxy_set( char * lrk_proxies)
+{
+    char *p,*p2;
+    struct lrkp_set * lrkp_list;
+    str id_set;
+
+    /* empty definition? */
+    p= lrk_proxies;
+    if(!p || *p=='\0'){
+        return 0;
+    }
+
+    for(;*p && isspace(*p);p++);
+    if(*p=='\0'){
+        return 0;
+    }
+
+    lrk_proxies = strstr(p, "==");
+    if(lrk_proxies){
+        if(*(lrk_proxies +2)=='\0'){
+                    LM_ERR("script error -invalid lrk proxy list!\n");
+            return -1;
+        }
+
+        *lrk_proxies = '\0';
+        p2 = lrk_proxies-1;
+        for(;isspace(*p2); *p2 = '\0',p2--);
+        id_set.s = p;	id_set.len = p2 - p+1;
+
+        if(id_set.len <= 0){
+                    LM_ERR("script error -invalid set_id value!\n");
+            return -1;
+        }
+
+        lrk_proxies+=2;
+    }else{
+        lrk_proxies = p;
+        id_set = DEFAULT_LRKP_SET_ID_STR;
+    }
+
+    for(;*lrk_proxies && isspace(*lrk_proxies);lrk_proxies++);
+
+    if(!(*lrk_proxies)){
+                LM_ERR("script error -empty lrkproxy list\n");
+        return -1;;
+    }
+
+    lrkp_list = get_lrkp_set(&id_set);
+    if (lrkp_list == NULL)
+    {
+                LM_ERR("Failed to get or create lrkp_list for '%.*s'\n", id_set.len, id_set.s);
+        return -1;
+    }
+
+    if(add_lrkproxy_socks(lrkp_list, lrk_proxies)!= 0){
+        return -1;
+    }
+
+    return 0;
+}
+
+
+static int fixup_set_id(void ** param, int param_no)
+{
+	int int_val, err;
+	struct lrkp_set* lrkp_list;
+	lrkp_set_link_t *lrkl = NULL;
+	str s;
+
+	lrkl = (lrkp_set_link_t*)pkg_malloc(sizeof(lrkp_set_link_t));
+	if(lrkl==NULL) {
+		LM_ERR("no more pkg memory\n");
+		return -1;
+	}
+	memset(lrkl, 0, sizeof(lrkp_set_link_t));
+	s.s = (char*)*param;
+	s.len = strlen(s.s);
+
+	if(s.s[0] == PV_MARKER) {
+		int_val = pv_locate_name(&s);
+		if(int_val<0 || int_val!=s.len) {
+			LM_ERR("invalid parameter %s\n", s.s);
+			pkg_free(lrkl);
+			return -1;
+		}
+		lrkl->rpv = pv_cache_get(&s);
+		if(lrkl->rpv == NULL) {
+			LM_ERR("invalid pv parameter %s\n", s.s);
+			pkg_free(lrkl);
+			return -1;
+		}
+	} else {
+		int_val = str2s(*param, strlen(*param), &err);
+		if (err == 0) {
+			pkg_free(*param);
+			if((lrkp_list = select_lrkp_set(int_val)) ==0){
+				LM_ERR("lrkp_proxy set %i not configured\n", int_val);
+				pkg_free(lrkl);
+				return E_CFG;
+			}
+			lrkl->rset = lrkp_list;
+		} else {
+			LM_ERR("bad number <%s>\n",	(char *)(*param));
+			pkg_free(lrkl);
+			return E_CFG;
+		}
+	}
+	*param = (void*)lrkl;
+	return 0;
+}
+
+
+static int
+mod_init(void)
+{
+    int i;
+//	pv_spec_t avp_spec;
+//	str s;
+//	unsigned short avp_flags;
+
+//	if(lrkproxy_rpc_init()<0)
+//	{
+//		LM_ERR("failed to register RPC commands\n");
+//		return -1;
+//	}
+
+    /* Configure the head of the lrkp_set_list */
+    lrkp_set_list = shm_malloc(sizeof(struct lrkp_set_head));
+    if (lrkp_set_list == NULL)
+    {
+                LM_ERR("no shm memory for lrkp_set_list\n");
+        return -1;
+    }
+    memset(lrkp_set_list, 0, sizeof(struct lrkp_set_head));
+
+
+    /* storing the list of lrk proxy sets in shared memory*/
+    for(i=0;i<lrkp_sets;i++){
+                LM_DBG("Adding LRK-Proxy set %d/%d: %s\n", i, lrkp_sets, lrkp_strings[i]);
+//        if ((lrkp_db_url.s == NULL) &&
+        if (lrkproxy_add_lrkproxy_set(lrkp_strings[i]) != 0) {
+            for(;i<lrkp_sets;i++)
+                if(lrkp_strings[i])
+                    pkg_free(lrkp_strings[i]);
+            pkg_free(lrkp_strings);
+                    LM_ERR("Failed to add LRK-Proxy from Config!\n");
+            return -1;
+        }
+        if(lrkp_strings[i])
+            pkg_free(lrkp_strings[i]);
+    }
+
+
+    if (lrkp_strings)
+        pkg_free(lrkp_strings);
+
+
+    /* init the hastable which keeps the all media address for both party and also the elected_node <--> callid& via-branch relation */
+    if (hash_table_size < 1){
+        hash_table_size = HASH_SIZE;    //the default size 128 entry.
+    }
+
+    if (!lrkproxy_hash_table_init(hash_table_size)) {
+                LM_ERR("lrkproxy_hash_table_init(%d) failed!\n", hash_table_size);
+        return -1;
+    } else {
+//                LM_DBG("lrkproxy_hash_table_init(%d) success!\n", hash_table_size);
+                LM_INFO(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>lrkproxy_hash_table_init(%d) success!\n", hash_table_size);
+    }
+
+
+
+    /* load tm module*/
+    if (load_tm_api( &tmb ) < 0)
+    {
+                LM_DBG("could not load the TM-functions - answer-offer model"
+                               " auto-detection is disabled\n");
+        memset(&tmb, 0, sizeof(struct tm_binds));
+    }
+
+    return 0;
+}
+
+
+static int
+child_init(int rank)
+{
+    int n;
+    char *cp;
+    struct addrinfo hints, *res;
+    struct lrkp_set  *lrkp_list;
+    struct lrkp_node *pnode;
+
+    if(lrkp_set_list==NULL )
+        return 0;
+
+    /* do not init sockets for PROC_INIT and main process when fork=yes */
+    if(rank==PROC_INIT || (rank==PROC_MAIN && dont_fork==0)) {
+        return 0;
+    }
+
+    /* Iterate known LRK proxies - create sockets */
+    mypid = getpid();
+
+    lrkp_socks = (int*)pkg_malloc( sizeof(int)*lrkp_no );
+    if (lrkp_socks==NULL) {
+                LM_ERR("no more pkg memory\n");
+        return -1;
+    }
+    memset(lrkp_socks, -1, sizeof(int)*lrkp_no);
+
+    for(lrkp_list = lrkp_set_list->lset_first; lrkp_list != 0;
+        lrkp_list = lrkp_list->lset_next){
+
+        for (pnode=lrkp_list->ln_first; pnode!=0; pnode = pnode->ln_next){
+            char *hostname;
+
+            if (pnode->ln_umode == 0) {
+                lrkp_socks[pnode->idx] = -1;
+                goto rptest;
+            }
+
+            /*
+             * This is UDP or UDP6. Detect host and port; lookup host;
+             * do connect() in order to specify peer address
+             */
+            hostname = (char*)pkg_malloc(sizeof(char) * (strlen(pnode->ln_address) + 1));
+            if (hostname==NULL) {
+                        LM_ERR("no more pkg memory\n");
+                return -1;
+            }
+            strcpy(hostname, pnode->ln_address);
+
+            cp = strrchr(hostname, ':');
+            if (cp != NULL) {
+                *cp = '\0';
+                cp++;
+            }
+            if (cp == NULL || *cp == '\0')
+                cp = CPORT;
+
+            memset(&hints, 0, sizeof(hints));
+            hints.ai_flags = 0;
+            hints.ai_family = (pnode->ln_umode == 6) ? AF_INET6 : AF_INET;
+            hints.ai_socktype = SOCK_DGRAM;
+            if ((n = getaddrinfo(hostname, cp, &hints, &res)) != 0) {
+                        LM_ERR("%s\n", gai_strerror(n));
+                pkg_free(hostname);
+                return -1;
+            }
+            pkg_free(hostname);
+
+            lrkp_socks[pnode->idx] = socket((pnode->ln_umode == 6)
+                                            ? AF_INET6 : AF_INET, SOCK_DGRAM, 0);
+            if ( lrkp_socks[pnode->idx] == -1) {
+                        LM_ERR("can't create socket\n");
+                freeaddrinfo(res);
+                return -1;
+            }
+
+            if (connect( lrkp_socks[pnode->idx], res->ai_addr, res->ai_addrlen) == -1) {
+                        LM_ERR("can't connect to a LRK proxy\n");
+                close( lrkp_socks[pnode->idx] );
+                lrkp_socks[pnode->idx] = -1;
+                freeaddrinfo(res);
+                return -1;
+            }
+            freeaddrinfo(res);
+rptest:
+            pnode->ln_enable = lrkp_test(pnode);
+            if (pnode->ln_enable) {       //get lrk proxy config if it is enable.
+//                LM_INFO("lrkp_test test is calling here\n"); //enable next line.
+                lrkp_get_config(pnode);
+            }
+        }
+    }
+
+    return 0;
+}
+
+
+static void mod_destroy(void)
+{
+    struct lrkp_set * crt_list, * last_list;
+    struct lrkp_node * crt_lrkp, *last_lrkp;
+
+    /*free the shared memory*/
+//	if (natping_state)
+//		shm_free(natping_state);
+
+    if(lrkp_set_list == NULL)
+        return;
+
+    for(crt_list = lrkp_set_list->lset_first; crt_list != NULL; ){
+
+        for(crt_lrkp = crt_list->ln_first; crt_lrkp != NULL;  ){
+
+            last_lrkp = crt_lrkp;
+            crt_lrkp = last_lrkp->ln_next;
+            shm_free(last_lrkp);
+        }
+
+        last_list = crt_list;
+        crt_list = last_list->lset_next;
+        shm_free(last_list);
+    }
+
+    shm_free(lrkp_set_list);
+
+    /* destroy the hash table */
+    if (!lrkproxy_hash_table_destroy()) {
+                LM_ERR("lrkproxy_hash_table_destroy() failed!\n");
+    } else {
+                LM_DBG("lrkproxy_hash_table_destroy() success!\n");
+    }
+
+}
+
+
+static char * gencookie(void)
+{
+    static char cook[34];
+
+    sprintf(cook, "%d_%u ", (int)mypid, myseqn);
+    myseqn++;
+    return cook;
+}
+
+static int lrkp_test(struct lrkp_node *node)
+{
+    int buflen = 256;
+    char buf[buflen];
+    struct iovec v[2] = {{NULL, 0}, {"P", 1}};
+
+    memset(buf, 0, buflen);
+    memcpy(buf, send_lrkp_command(node, v, 2, 0), buflen);
+
+//    if (buf == NULL) {
+    if (!buf[0]) {
+        LM_ERR("can't ping the lrk proxy %s, Disable it right now.\n", node->ln_url.s);
+        return 0;
+    }
+
+    char *resp = buf + v[0].iov_len + v[1].iov_len + 1;
+    if (memcmp(resp, "PONG", 4) == 0)
+//                LM_DBG("Recieve PONG response from lrk proxy server %s, Enable it right now.\n", node->ln_url.s);
+            LM_INFO("Recieve PONG response from lrk proxy server %s, Enable it right now.\n", node->ln_url.s);
+
+    return 1;
+
+}
+
+static int lrkp_get_config(struct lrkp_node *node){
+
+    int buflen = 256;
+    char buf[buflen];
+    struct iovec v[2] = {{NULL, 0}, {"G", 1}};
+    struct lrkp_node_conf *lnconf = NULL;
+
+    memset(buf, 0, buflen);
+    memcpy(buf, send_lrkp_command(node, v, 2, 0), buflen);
+
+//    if (buf == NULL) {
+    if (!buf[0]) {
+        LM_ERR("can't get config of the lrk proxy %s, Disable it right now.\n", node->ln_url.s);
+        return 0;
+    }
+
+    lnconf = (struct lrkp_node_conf *)(buf + v[0].iov_len + v[1].iov_len + 1);
+
+    if (lnconf == NULL){
+        LM_ERR("can't get config of the lrk proxy %s, Disable it right now.\n", node->ln_url.s);
+        return 0;
+    }
+
+
+    memcpy(node->lrkp_n_c, lnconf, sizeof(struct lrkp_node_conf));
+
+//    node->lrkp_n_c = lnconf;
+    LM_INFO("the lrk proxy %s is configured successfully right now.\n", node->ln_url.s);
+    LM_INFO("buffer internal:%s\n", node->lrkp_n_c->internal_ip);
+    LM_INFO("buffer external:%s\n", node->lrkp_n_c->external_ip);
+    LM_INFO("buffer start_port:%d\n", node->lrkp_n_c->start_port);
+    LM_INFO("buffer end_port:%d\n", node->lrkp_n_c->end_port);
+    LM_INFO("buffer current_port:%d\n", node->lrkp_n_c->current_port);
+
+    return 1;
+
+
+}
+
+static int lrkp_set_conntrack_rule(struct lrkproxy_hash_entry *e) {
+    int buflen = 254;
+    char buf[buflen];
+    int v_len = 0;
+
+    char src_ipv4[20];
+    char src_port[20];
+    char dst_ipv4[20];
+    char dst_port[20];
+    char snat_ipv4[20];
+    char snat_port[20];
+    char dnat_ipv4[20];
+    char dnat_port[20];
+    char timeout[20];
+    char callid[50];
+
+    struct iovec v[] = {
+            {NULL, 0},  /* reserved (cookie) */
+            {"S",  1},   /* command & common options */
+            {NULL, 0},  /* src_ipv4 */
+            {NULL, 0},  /* dst_ipnv4 */
+            {NULL, 0},  /* snat_ipv4 */
+            {NULL, 0},  /* dnat_ipv4 */
+            {NULL, 0},  /* src_port */
+            {NULL, 0},  /* dst_port*/
+            {NULL, 0},  /* snat_port */
+            {NULL, 0},  /* dnat_port*/
+            {NULL, 0},  /* timeout to clear conntrack entry*/
+            {NULL, 0},  /* callid of session */
+    };
+
+    v_len += v[1].iov_len;
+
+    //set src_ipv4 to buffer.
+    sprintf(src_ipv4, " %.*s ", e->src_ipv4.len, e->src_ipv4.s);
+    v[2].iov_base = src_ipv4;
+    v[2].iov_len = strlen(v[2].iov_base);
+    v_len += v[2].iov_len;
+
+    //set dst_ipv4 to buffer.
+    sprintf(dst_ipv4, "%.*s ", e->dst_ipv4.len, e->dst_ipv4.s);
+    v[3].iov_base = dst_ipv4;
+    v[3].iov_len = strlen(v[3].iov_base);
+    v_len += v[3].iov_len;
+
+    //set snat_ipv4 to buffer.
+    sprintf(snat_ipv4, "%.*s ", e->snat_ipv4.len, e->snat_ipv4.s);
+    v[4].iov_base = snat_ipv4;
+    v[4].iov_len = strlen(v[4].iov_base);
+    v_len += v[4].iov_len;
+
+    //set dnat_ipv4 to buffer.
+    sprintf(dnat_ipv4, "%.*s ", e->dnat_ipv4.len, e->dnat_ipv4.s);
+    v[5].iov_base = dnat_ipv4;
+    v[5].iov_len = strlen(v[5].iov_base);
+    v_len += v[5].iov_len;
+
+    //set src_port to buffer.
+    sprintf(src_port, "%.*s ", e->src_port.len, e->src_port.s);
+    v[6].iov_base = src_port;
+    v[6].iov_len = strlen(v[6].iov_base);
+    v_len += v[6].iov_len;
+
+    //set dst_port to buffer.
+    sprintf(dst_port, "%.*s ", e->dst_port.len, e->dst_port.s);
+    v[7].iov_base = dst_port;
+    v[7].iov_len = strlen(v[7].iov_base);
+    v_len += v[7].iov_len;
+
+    //set snat_port to buffer.
+    sprintf(snat_port, "%.*s ", e->snat_port.len, e->snat_port.s);
+    v[8].iov_base = snat_port;
+    v[8].iov_len = strlen(v[8].iov_base);
+    v_len += v[8].iov_len;
+
+    //set dnat_port to buffer.
+    sprintf(dnat_port, "%.*s ", e->dnat_port.len, e->dnat_port.s);
+    v[9].iov_base = dnat_port;
+    v[9].iov_len = strlen(v[9].iov_base);
+    v_len += v[9].iov_len;
+
+    //set timeout to buffer. Set to 60 sec for default.
+    sprintf(timeout, "%d ", 60);
+    v[10].iov_base = timeout;
+    v[10].iov_len = strlen(v[10].iov_base);
+    v_len += v[10].iov_len;
+
+    //set callid to buffer.
+    sprintf(callid, "%.*s ", e->callid.len, e->callid.s);
+    v[11].iov_base = callid;
+    v[11].iov_len = strlen(v[11].iov_base);
+    v_len += v[11].iov_len;
+//    LM_ERR("e->callid.len is:%d right now.\n\n", e->callid.len);
+
+    memset(buf, 0, buflen);
+    memcpy(buf, send_lrkp_command(e->node, v, 12, v_len), buflen);
+//
+
+//    if (buf == NULL) {
+    if (!buf[0]) {
+                LM_ERR("can't ping the lrk proxy %s, Disable it right now.\n", e->node->ln_url.s);
+        return 0;
+    }
+
+    v_len += v[0].iov_len;
+
+
+//    char *resp = buf + v[0].iov_len + v[1].iov_len + v[2].iov_len;
+    char *resp = buf + v_len;
+    if (memcmp(resp, "OK", 2) == 0) {
+                LM_INFO("Recieve OK response from lrk proxy server %s, Rule set successfully.\n", e->node->ln_url.s);
+                LM_DBG("Recieve OK response from lrk proxy server %s, Rule set successfully.\n", e->node->ln_url.s);
+    }
+    return 1;
+
+}
+
+
+char *send_lrkp_command(struct lrkp_node *node, struct iovec *v, int vcnt, int more)
+{
+    struct sockaddr_un addr;
+    int fd, len, i;
+//    char *cp;
+    static char buf[256];
+    struct pollfd fds[1];
+
+    memset(buf, 0, 256);
+    len = 0;
+//    cp = buf;
+    if (node->ln_umode == 0) {
+        memset(&addr, 0, sizeof(addr));
+        addr.sun_family = AF_LOCAL;
+        strncpy(addr.sun_path, node->ln_address,
+                sizeof(addr.sun_path) - 1);
+#ifdef HAVE_SOCKADDR_SA_LEN
+        addr.sun_len = strlen(addr.sun_path);
+#endif
+
+        fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+        if (fd < 0) {
+                    LM_ERR("can't create socket\n");
+            goto badproxy;
+        }
+        if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+            close(fd);
+                    LM_ERR("can't connect to lrk proxy\n");
+            goto badproxy;
+        }
+
+        do {
+            len = writev(fd, v + 1, vcnt - 1);
+        } while (len == -1 && errno == EINTR);
+        if (len <= 0) {
+            close(fd);
+                    LM_ERR("can't send command to a lrk proxy %s\n", node->ln_url.s);
+            goto badproxy;
+        }
+        do {
+            len = read(fd, buf, sizeof(buf) - 1);
+        } while (len == -1 && errno == EINTR);
+        close(fd);
+        if (len <= 0) {
+                    LM_ERR("can't read reply from a lrk proxy %s\n", node->ln_url.s);
+            goto badproxy;
+        }
+    } else {
+        fds[0].fd = lrkp_socks[node->idx];
+        fds[0].events = POLLIN;
+        fds[0].revents = 0;
+        /* Drain input buffer */
+        while ((poll(fds, 1, 0) == 1) &&
+               ((fds[0].revents & POLLIN) != 0)) {
+            recv(lrkp_socks[node->idx], buf, sizeof(buf) - 1, 0);
+            fds[0].revents = 0;
+        }
+        v[0].iov_base = gencookie();
+        v[0].iov_len = strlen(v[0].iov_base);
+        for (i = 0; i < lrkproxy_retr; i++) {
+            do {
+                len = writev(lrkp_socks[node->idx], v, vcnt);
+            } while (len == -1 && (errno == EINTR || errno == ENOBUFS));
+            if (len <= 0) {
+                        LM_ERR("can't send command to a lrk proxy %s\n", node->ln_url.s);
+                goto badproxy;
+            }
+            while ((poll(fds, 1, lrkproxy_tout * 1000) == 1) &&
+                   (fds[0].revents & POLLIN) != 0) {
+                do {
+                    len = recv(lrkp_socks[node->idx], buf, sizeof(buf) - 1, 0);
+                } while (len == -1 && errno == EINTR);
+                if (len <= 0) {
+                            LM_ERR("can't read reply from a lrk proxy %s\n", node->ln_url.s);
+                    goto badproxy;
+                }
+                if (len >= (v[0].iov_len - 1) &&
+                    memcmp(buf, v[0].iov_base, (v[0].iov_len - 1)) == 0) {      //check coocke validation.
+                    char *command = buf + v[0].iov_len;
+                    switch (*command) {
+                        case 'P':
+                            if (len == v[0].iov_len + v[1].iov_len + 4 + 1)
+                                goto out;
+//                            break;
+                        case 'G':
+                            if (len == v[0].iov_len + v[1].iov_len + sizeof(struct lrkp_node_conf) + 1)
+                                goto out;
+//                            break;
+                        case 'S':
+                            if (len == more + v[0].iov_len + 2)
+                                goto out;
+//                            break;
+                    }
+
+                }
+                fds[0].revents = 0;
+            }
+        }
+        if (i == lrkproxy_tout) {
+                    LM_ERR("timeout waiting reply from a lrk proxy server %s\n", node->ln_url.s);
+            goto badproxy;
+
+        }
+    }
+    out:
+    return buf;
+    badproxy:
+            LM_ERR("lrk proxy <%s> does not respond, disable it\n", node->ln_url.s);
+    node->ln_enable = 0;
+//    node->ln_recheck_ticks = get_ticks() + lrkproxy_disable_tout;
+    return buf;
+}
+
+/*
+ * select the set with the id_set id
+ */
+static struct lrkp_set * select_lrkp_set(int id_set ){
+
+    struct lrkp_set * lrkp_list;
+    /*is it a valid set_id?*/
+
+    if(!lrkp_set_list)
+    {
+                LM_ERR("lrkproxy set list not initialised\n");
+        return NULL;
+    }
+
+    for(lrkp_list=lrkp_set_list->lset_first; lrkp_list!=NULL &&
+                                             lrkp_list->id_set!=id_set; lrkp_list=lrkp_list->lset_next);
+
+    return lrkp_list;
+}
+
+
+struct lrkp_node *select_lrkp_node(int do_test)
+{
+//    unsigned sum, sumcut, weight_sum;
+    unsigned weight_sum;
+    struct lrkp_node* node;
+    int was_forced;
+    int was_forced2;
+    int was_forced3;
+
+    if(!selected_lrkp_set){
+        LM_ERR("script error -no valid set selected\n");
+        return NULL;
+    }
+    /* Most popular case: 1 proxy, nothing to calculate */
+    if (selected_lrkp_set->lrkp_node_count == 1) {
+        node = selected_lrkp_set->ln_first;
+//        if (node->rn_disabled && node->rn_recheck_ticks <= get_ticks())
+        if (!node->ln_enable) {
+            node->ln_enable = lrkp_test(node);
+            if (node->ln_enable) {       //get lrk proxy config if it is enable.
+                lrkp_get_config(node);
+                return node;
+            }
+        }
+        return node->ln_enable ? node : NULL;
+//        return NULL;
+    }
+
+
+    /* Check node is enable and test it again*/
+    was_forced = 0;
+retry:
+    weight_sum = 0;
+    for (node=selected_lrkp_set->ln_first; node!=NULL; node=node->ln_next) {
+
+        if (!node->ln_enable) {
+            /* Try to enable if it's time to try. */
+            node->ln_enable = lrkp_test(node);
+            if (node->ln_enable)       //get lrk proxy config if it is enable.
+                lrkp_get_config(node);
+        }
+
+//        if (!node->rn_disabled)
+//            weight_sum += node->rn_weight;
+        if (node->ln_enable)
+            weight_sum += node->ln_weight;
+    }
+
+    if (weight_sum == 0) {
+        /* No proxies? Force all to be redetected, if not yet */
+        if (was_forced)
+            return NULL;
+        was_forced = 1;
+//        for(node=selected_lrkp_set->ln_first; node!=NULL; node=node->ln_next) {
+//            node->ln_enable = lrkp_test(node);
+//        }
+        goto retry;
+    }
+
+    if (lrkp_algorithm == LRK_LINER) {
+        was_forced2 = 0;
+retry2:
+        for (node=selected_lrkp_set->ln_first; node != NULL; node = node->ln_next)
+            if (node->ln_enable)
+                goto found;
+        was_forced2 = 1;
+        if (was_forced2)
+            return NULL;
+
+        goto retry2;
+    }
+    else if(lrkp_algorithm == LRK_RR) {
+        was_forced3 = 0;
+retry3:
+        if (!selected_lrkp_node) {
+            selected_lrkp_node = selected_lrkp_set->ln_first;
+            was_forced3 = 1;
+        }
+        for (node = selected_lrkp_node; node != NULL; node = node->ln_next) {
+            if (!node->ln_enable)
+                continue;
+            selected_lrkp_node = node->ln_next;
+//        if (sumcut < node->ln_weight)
+            goto found;
+//        sumcut -= node->ln_weight;
+        }
+
+        if (was_forced3)
+            return NULL;
+
+        selected_lrkp_node = NULL;
+        goto retry3;
+    }
+
+    found:
+    if (do_test) {
+//    //todo...
+        node->ln_enable = lrkp_test(node);
+        if (!node->ln_enable)
+            goto retry;
+    }
+    return node;
+}
+
+//static int change_media_sdp(sip_msg_t *msg, struct lrkp_node *n, const char *flags, int type) {
+static int change_media_sdp(sip_msg_t *msg, struct lrkproxy_hash_entry *e, const char *flags, enum lrk_operation op) {
+    struct lump *l;
+    str body;
+    str newbody;
+
+    int len;
+    char *start_sdp_o = NULL; //"o=";
+    char *start_sdp_s = NULL; //"s=";
+    char *start_sdp_c = NULL; //"c=IN IP4";
+    char *start_sdp_m = NULL; //"m=audio";
+    char *ip_selected = NULL;
+    char *sdp_param_start = NULL;
+    char *sdp_param_end = NULL;
+    char *off=NULL;
+    char sdp_new_o[128];
+    char sdp_new_s[128];
+    char sdp_new_c[128];
+    char sdp_new_m[128];
+
+    body.s = get_body(msg);
+    if (body.s == 0) {
+                LM_ERR("failed to get the message body\n");
+        return -1;
+    }
+
+    body.len = msg->len - (int) (body.s - msg->buf);
+    if (body.len == 0) {
+                LM_DBG("message body has zero length\n");
+        return -1;
+    }
+//            LM_INFO("body:<%.*s>\n", body.len, body.s);
+
+    //allocate new buffer to new sdp buffer.
+    newbody.len = 1024;
+    newbody.s = pkg_malloc(newbody.len);
+    if (newbody.s == NULL) {
+                LM_ERR("out of pkg memory\n");
+        return -1;
+    }
+    memset(newbody.s, 0, 1024);
+
+    off = body.s;
+    start_sdp_o = strstr(off, "o=");
+    start_sdp_s = strstr(off, "s=");
+    start_sdp_c = strstr(off, "c=IN IP4");
+    start_sdp_m = strstr(off, "m=audio");
+
+    //if enabled then set direction,
+    if (e->node->lrkp_n_c->internal_ip && flags) {
+        if (strstr(flags, "ei")) {
+            ip_selected = e->node->lrkp_n_c->internal_ip;// lrk_node->internal_ip;
+        } else if (strstr(flags, "ie")) {
+            ip_selected = e->node->lrkp_n_c->external_ip; //lrk_node->external_ip;
+        } else {
+                    LM_INFO("no flags found\n");
+            return 0;
+        }
+    } else {
+        ip_selected = e->node->lrkp_n_c->external_ip; //lrk_node->external_ip;
+    }
+
+    if (op == OP_OFFER) {
+        e->dst_ipv4.s = ip_selected;
+        e->dst_ipv4.len = strlen(e->dst_ipv4.s);
+
+        str current_port;
+        current_port.s = int2str(e->node->lrkp_n_c->current_port, &current_port.len);
+
+        if (shm_str_dup(&e->dst_port, &current_port) < 0) {
+                    LM_ERR("lrkproxy fail to insert dst_port, calllen=%d dst_port=%.*s\n",
+                           e->callid.len, current_port.len, current_port.s);
+            lrkproxy_hash_table_free_entry(e);
+            return 0;
+        }
+
+//        e->dst_port = e->node->lrkp_n_c->current_port;
+    }
+    else if (op == OP_ANSWER){
+        e->snat_ipv4.s = ip_selected;
+        e->snat_ipv4.len = strlen(e->snat_ipv4.s);
+
+        str current_port;
+        unsigned int snat_port;
+
+        str2int(&e->dst_port, &snat_port);
+        snat_port += 2;
+
+        current_port.s = int2str(snat_port, &current_port.len);
+
+        if (shm_str_dup(&e->snat_port, &current_port) < 0) {
+                    LM_ERR("lrkproxy fail to insert snat_port, calllen=%d snat_port=%.*s\n",
+                           e->callid.len, current_port.len, current_port.s);
+            lrkproxy_hash_table_free_entry(e);
+            return 0;
+        }
+
+//        e->snat_port = e->dst_port + 2;
+    }
+
+
+    while (*off != EOB)    //while end of body.
+    {
+        sdp_param_start = off;
+        sdp_param_end = sdp_param_start;
+        while (*sdp_param_end != CR && *sdp_param_end != LF && *sdp_param_end != EOB) sdp_param_end++;
+        len = (int) (sdp_param_end - sdp_param_start);
+        if ((int) (start_sdp_o - off) == 0) {
+            memset(sdp_new_o, 0, 128);
+            snprintf(sdp_new_o, 128, "o=lrkproxy %s %s IN IP4 %s\r", SUP_CPROTOVER, REQ_CPROTOVER, ip_selected);
+            strncat(newbody.s, sdp_new_o, strlen(sdp_new_o));
+            off += len + 1;
+            continue;
+        }
+        if ((int) (start_sdp_s - off) == 0) {
+            memset(sdp_new_s, 0, 128);
+            snprintf(sdp_new_s, 128, "s=lrkproxy Support only Audio Call\r");
+            strncat(newbody.s, sdp_new_s, strlen(sdp_new_s));
+            off += len + 1;
+            continue;
+        }
+        if ((int) (start_sdp_c - off) == 0) {
+            memset(sdp_new_c, 0, 128);
+            snprintf(sdp_new_c, 128, "c=IN IP4 %s\r", ip_selected);
+            strncat(newbody.s, sdp_new_c, strlen(sdp_new_c));
+            off += len + 1;
+            continue;
+        }
+        if ((int)(start_sdp_m - off) == 0){
+            memset(sdp_new_m, 0, 128);
+            char *avp_flags = off;
+//            int occure = 0;
+            for (;*avp_flags && !isspace(*avp_flags); avp_flags++);
+            for (avp_flags++;*avp_flags && !isspace(*avp_flags); avp_flags++);
+            avp_flags++;
+            if (op == OP_OFFER)
+                snprintf(sdp_new_m, 128, "m=audio %.*s %.*s\r",e->dst_port.len, e->dst_port.s, (int)(len - (avp_flags-off)), avp_flags);
+//                snprintf(sdp_new_m, 128, "m=audio %d %.*s\r",e->node->lrkp_n_c->current_port, (int)(len - (avp_flags-off)), avp_flags);
+            else if (op == OP_ANSWER)
+                snprintf(sdp_new_m, 128, "m=audio %.*s %.*s\r",e->snat_port.len, e->snat_port.s, (int)(len - (avp_flags-off)), avp_flags);
+//               snprintf(sdp_new_m, 128, "m=audio %d %.*s\r",e->node->lrkp_n_c->current_port, (int)(len - (avp_flags-off)), avp_flags);
+//            printf("%.*s\n\n", len - (avp_flags-off), avp_flags);
+            strncat(newbody.s,sdp_new_m, strlen(sdp_new_m));
+            off += len+1;
+            continue;
+        }
+
+        strncat(newbody.s, off, len + 1);
+        off += len + 1;
+    }
+
+
+//    LM_INFO("%.*s", (int)strlen(newbody.s), newbody.s);
+    l = del_lump(msg, body.s - msg->buf, body.len, 0);
+    if (!l) {
+                LM_ERR("del_lump failed\n");
+        return -1;
+    }
+
+
+    if (insert_new_lump_after(l, newbody.s, strlen(newbody.s), 0) == 0) {
+                LM_ERR("could not insert new lump\n");
+        pkg_free(newbody.s);
+        return -1;
+    }
+
+            LM_BUG("Insert_new_lump successfully\n");
+
+    return 1;
+}
+
+/* This function assumes p points to a line of requested type. */
+
+	static int
+set_lrkproxy_set_f(struct sip_msg * msg, char * str1, char * str2)
+{
+	lrkp_set_link_t *lrkl;
+	pv_value_t val;
+
+	lrkl = (lrkp_set_link_t*)str1;
+
+	current_msg_id = 0;
+	selected_lrkp_set = 0;
+
+	if(lrkl->rset != NULL) {
+		current_msg_id = msg->id;
+		selected_lrkp_set = lrkl->rset;
+	} else {
+		if(pv_get_spec_value(msg, lrkl->rpv, &val)<0) {
+			LM_ERR("cannot evaluate pv param\n");
+			return -1;
+		}
+		if(!(val.flags & PV_VAL_INT)) {
+			LM_ERR("pv param must hold an integer value\n");
+			return -1;
+		}
+		selected_lrkp_set = select_lrkp_set(val.ri);
+		if(selected_lrkp_set==NULL) {
+			LM_ERR("could not locate lrkproxy set %d\n", val.ri);
+			return -1;
+		}
+		current_msg_id = msg->id;
+	}
+	return 1;
+}
+
+static int
+lrkproxy_manage(struct sip_msg *msg, char *flags, char *ip)
+{
+    int method;
+    int nosdp;
+    tm_cell_t *t = NULL;
+
+    if (msg->cseq==NULL && ((parse_headers(msg, HDR_CSEQ_F, 0)==-1) ||
+                            (msg->cseq==NULL)))
+    {
+                LM_ERR("no CSEQ header\n");
+        return -1;
+    }
+
+    method = get_cseq(msg)->method_id;
+
+    if (!(method==METHOD_INVITE || method==METHOD_ACK || method==METHOD_CANCEL
+          || method==METHOD_BYE || method==METHOD_UPDATE))
+        return -1;
+
+    if (method==METHOD_CANCEL || method==METHOD_BYE)
+        return lrkproxy_unforce(msg, flags, OP_DELETE, 1);
+
+    if (msg->msg_flags & FL_SDP_BODY)
+        nosdp = 0;
+    else
+        nosdp = parse_sdp(msg);
+
+    if (msg->first_line.type == SIP_REQUEST) {
+        if(method==METHOD_ACK && nosdp==0)
+            return lrkproxy_force(msg, flags, OP_ANSWER, 1);
+        if(method==METHOD_UPDATE && nosdp==0)
+            return lrkproxy_force(msg, flags, OP_OFFER, 1);
+        if(method==METHOD_INVITE && nosdp==0) {
+            msg->msg_flags |= FL_SDP_BODY;
+            if(tmb.t_gett!=NULL) {
+                t = tmb.t_gett();
+                if(t!=NULL && t!=T_UNDEFINED && t->uas.request!=NULL) {
+                    t->uas.request->msg_flags |= FL_SDP_BODY;
+                }
+            }
+            if(route_type==FAILURE_ROUTE)
+                return lrkproxy_unforce(msg, flags, OP_DELETE, 1);
+            return lrkproxy_force(msg, flags, OP_OFFER, 1);
+        }
+    } else if (msg->first_line.type == SIP_REPLY) {
+        if (msg->first_line.u.reply.statuscode>=300)
+            return lrkproxy_unforce(msg, flags, OP_DELETE, 2);
+        if (nosdp==0) {
+            if (method==METHOD_UPDATE)
+                return lrkproxy_force(msg, flags, OP_ANSWER, 2);
+            if (tmb.t_gett==NULL || tmb.t_gett()==NULL
+                || tmb.t_gett()==T_UNDEFINED)
+                return lrkproxy_force(msg, flags, OP_ANSWER, 2);
+            if (tmb.t_gett()->uas.request->msg_flags & FL_SDP_BODY)
+                return lrkproxy_force(msg, flags, OP_ANSWER, 2);
+            return lrkproxy_force(msg, flags, OP_OFFER, 2);
+        }
+    }
+
+    return -1;
+}
+
+static int
+lrkproxy_manage0(struct sip_msg *msg, char *flags, char *ip)
+{
+    return lrkproxy_manage(msg, 0, 0);
+}
+
+static int
+lrkproxy_manage1(struct sip_msg *msg, char *flags, char *ip)
+{
+    str flag_str;
+    if(fixup_get_svalue(msg, (gparam_p)flags, &flag_str)<0)
+    {
+                LM_ERR("invalid flags parameter\n");
+        return -1;
+    }
+    return lrkproxy_manage(msg, flag_str.s, 0);
+}
+
+static int
+lrkproxy_manage2(struct sip_msg *msg, char *flags, char *ip)
+{
+    str flag_str;
+    str ip_str;
+    if(fixup_get_svalue(msg, (gparam_p)flags, &flag_str)<0)
+    {
+                LM_ERR("invalid flags parameter\n");
+        return -1;
+    }
+    if(fixup_get_svalue(msg, (gparam_p)ip, &ip_str)<0)
+    {
+                LM_ERR("invalid IP parameter\n");
+        return -1;
+    }
+    return lrkproxy_manage(msg, flag_str.s, ip_str.s);
+}
+
+
+static int lrkproxy_force(struct sip_msg *msg, const char *flags, enum lrk_operation op, int more) {
+
+//    lrk_sdp_info_t lrk_sdp_info;
+    struct lrkproxy_hash_entry *entry = NULL;
+    str viabranch = STR_NULL;
+    str call_id;
+    int via_id;
+
+    if (get_callid(msg, &call_id) == -1) {
+                LM_ERR("can't get Call-Id field\n");
+        return -1;
+    }
+
+    /*We have to choice VIA id,
+     * for SIP_REQUEST we use VIA1 and for SIP_REPLY we use VIA2 */
+    via_id = more;
+
+    if (get_via_branch(msg, via_id, &viabranch) == -1) {
+                LM_ERR("can't get Call-Id field\n");
+        return -1;
+    }
+
+    if (op == OP_OFFER) {
+                LM_INFO ("Here is SIP_REQUEST &  METHOD_INVITE\n");
+        int more_test = 1;
+
+        //select new node based on lrkp_algorithm param.
+        struct lrkp_node *node = select_lrkp_node(more_test);
+        if (!node) {
+                    LM_ERR("can't ping any lrk proxy right now.\n");
+            return -1;
+        }
+
+        LM_DBG("selected lrk proxy node: %s\n", node->ln_url.s);
+
+        //check if entry not exist.
+        if (!lrkproxy_hash_table_lookup(call_id, viabranch)) {
+
+//        lrk_get_sdp_info(msg, &lrk_sdp_info);
+
+            //build new entry for hash table.
+//            struct lrkproxy_hash_entry *entry = shm_malloc(sizeof(struct lrkproxy_hash_entry));
+            entry = shm_malloc(sizeof(struct lrkproxy_hash_entry));
+            if (!entry) {
+                        LM_ERR("lrkproxy hash table fail to create entry for calllen=%d callid=%.*s viabranch=%.*s\n",
+                               call_id.len, call_id.len, call_id.s,
+                               viabranch.len, viabranch.s);
+                return 0;
+            }
+            memset(entry, 0, sizeof(struct lrkproxy_hash_entry));
+
+            // fill the entry
+            if (call_id.s && call_id.len > 0) {
+                if (shm_str_dup(&entry->callid, &call_id) < 0) {
+                            LM_ERR("lrkproxy hash table fail to instert call_id, calllen=%d callid=%.*s\n",
+                                   call_id.len, call_id.len, call_id.s);
+                    lrkproxy_hash_table_free_entry(entry);
+                    return 0;
+                }
+            }
+
+            if (viabranch.s && viabranch.len > 0) {
+                if (shm_str_dup(&entry->viabranch, &viabranch) < 0) {
+                            LM_ERR("lrkproxy hash table fail to insert viabranch, calllen=%d viabranch=%.*s\n",
+                                   call_id.len, viabranch.len, viabranch.s);
+                    lrkproxy_hash_table_free_entry(entry);
+                    return 0;
+                }
+            }
+
+            //fill src_ipv4 and src_port for entry.
+            str src_ipv4;
+            if (get_sdp_ipaddr_media(msg, &src_ipv4) == -1) {
+                        LM_ERR("can't get media src_ipv4 from sdp field\n");
+                return -1;
+            }
+
+            if(src_ipv4.s && src_ipv4.len > 0) {
+                        LM_DBG("src_ipv4 from sdp:%.*s\n", src_ipv4.len, src_ipv4.s);
+                if (shm_str_dup(&entry->src_ipv4, &src_ipv4) < 0) {
+                            LM_ERR("lrkproxy hash table fail to insert src_ipv4, calllen=%d src_ipv4=%.*s\n",
+                                   call_id.len, src_ipv4.len, src_ipv4.s);
+                    lrkproxy_hash_table_free_entry(entry);
+                    return 0;
+                }
+            }
+
+            str src_port;
+            if (get_sdp_port_media(msg, &src_port) == -1) {
+                        LM_ERR("can't get media src_port from sdp field\n");
+                return -1;
+            }
+
+
+            if(src_port.s && src_port.len > 0) {
+                        LM_DBG("src_port from sdp:%.*s\n", src_port.len, src_port.s);
+                if (shm_str_dup(&entry->src_port, &src_port) < 0) {
+                            LM_ERR("lrkproxy hash table fail to insert src_port, calllen=%d src_port=%.*s\n",
+                                   call_id.len, src_port.len, src_port.s);
+                    lrkproxy_hash_table_free_entry(entry);
+                    return 0;
+                }
+            }
+
+//            entry->
+            entry->node = node;
+            entry->next = NULL;
+            entry->tout = get_ticks() + hash_table_tout;
+
+            // insert the key<->entry from the hashtable
+            if (!lrkproxy_hash_table_insert(call_id, viabranch, entry)) {
+                        LM_ERR(
+                        "lrkproxy hash table fail to insert node=%.*s for calllen=%d callid=%.*s viabranch=%.*s\n",
+                        node->ln_url.len, node->ln_url.s, call_id.len,
+                        call_id.len, call_id.s, viabranch.len, viabranch.s);
+                lrkproxy_hash_table_free_entry(entry);
+                return 0;
+            } else {
+                        LM_INFO("lrkproxy hash table insert node=%.*s for calllen=%d callid=%.*s viabranch=%.*s\n",
+                                node->ln_url.len, node->ln_url.s, call_id.len,
+                                call_id.len, call_id.s, viabranch.len, viabranch.s);
+
+                        LM_DBG("lrkproxy hash table insert node=%.*s for calllen=%d callid=%.*s viabranch=%.*s\n",
+                               node->ln_url.len, node->ln_url.s, call_id.len,
+                               call_id.len, call_id.s, viabranch.len, viabranch.s);
+            }
+        }
+
+        if (flags)
+            change_media_sdp(msg, entry, flags, op);
+        else
+            change_media_sdp(msg, entry, NULL, op);
+
+        if (node->lrkp_n_c->current_port >= node->lrkp_n_c->end_port)
+            node->lrkp_n_c->current_port = node->lrkp_n_c->start_port;
+        else
+            node->lrkp_n_c->current_port += 4;
+
+    } else if (op == OP_ANSWER) {
+                LM_INFO ("Here is SIP_REPLY of METHOD_INVITE\n");
+
+
+        entry = lrkproxy_hash_table_lookup(call_id, viabranch);
+        if (!entry){
+                    LM_ERR("No found entry in hash table\n");
+                    //todo...
+            return 0;
+        }
+
+        //fill other data for entry
+        str dnat_ipv4;
+        if (get_sdp_ipaddr_media(msg, &dnat_ipv4) == -1) {
+                    LM_ERR("can't get media dnat_ipv4 from sdp field\n");
+            return -1;
+        }
+
+        if(dnat_ipv4.s && dnat_ipv4.len > 0) {
+                    LM_DBG("dnat_ipv4 from sdp:%.*s\n", dnat_ipv4.len, dnat_ipv4.s);
+            if (shm_str_dup(&entry->dnat_ipv4, &dnat_ipv4) < 0) {
+                        LM_ERR("lrkproxy hash table fail to insert dnat_ipv4, calllen=%d dnat_ipv4=%.*s\n",
+                               call_id.len, dnat_ipv4.len, dnat_ipv4.s);
+                lrkproxy_hash_table_free_entry(entry);
+                return 0;
+            }
+        }
+
+        str dnat_port;
+        if (get_sdp_port_media(msg, &dnat_port) == -1) {
+                    LM_ERR("can't get media port from sdp field\n");
+            return -1;
+        }
+
+
+        if(dnat_port.s && dnat_port.len > 0) {
+                    LM_DBG("port from sdp:%.*s\n", dnat_port.len, dnat_port.s);
+            if (shm_str_dup(&entry->dnat_port, &dnat_port) < 0) {
+                        LM_ERR("lrkproxy hash table fail to insert dnat_port, calllen=%d dnat_port=%.*s\n",
+                               call_id.len, dnat_port.len, dnat_port.s);
+                lrkproxy_hash_table_free_entry(entry);
+                return 0;
+            }
+        }
+
+
+        if (flags)
+            change_media_sdp(msg, entry, flags, op);
+        else
+            change_media_sdp(msg, entry, NULL, op);
+
+
+        LM_INFO("selected node: %s\n",entry->node->ln_url.s);
+        LM_INFO("call_is: %.*s\n",entry->callid.len, entry->callid.s);
+        LM_INFO("viabranch: %.*s\n",entry->viabranch.len, entry->viabranch.s);
+        LM_INFO("src_ipv4: %.*s\n",entry->src_ipv4.len, entry->src_ipv4.s);
+        LM_INFO("src_port: %.*s\n",entry->src_port.len, entry->src_port.s);
+        LM_INFO("dst_ipv4: %.*s\n",entry->dst_ipv4.len, entry->dst_ipv4.s);
+        LM_INFO("dst_port: %.*s\n",entry->dst_port.len, entry->dst_port.s);
+
+        LM_INFO("dnat_ipv4: %.*s\n",entry->dnat_ipv4.len, entry->dnat_ipv4.s);
+        LM_INFO("dnat_port: %.*s\n",entry->dnat_port.len, entry->dnat_port.s);
+        LM_INFO("snat_ipv4: %.*s\n",entry->snat_ipv4.len, entry->snat_ipv4.s);
+        LM_INFO("snat_port: %.*s\n",entry->snat_port.len, entry->snat_port.s);
+
+
+        lrkp_set_conntrack_rule(entry);
+
+    }
+    return 1;
+}
+
+static int lrkproxy_unforce(struct sip_msg *msg, const char *flags, enum lrk_operation op, int more){
+//            LM_INFO ("Here is lrkproxy_unforce\n");
+//    struct lrkproxy_hash_entry *entry = NULL;
+    str viabranch = STR_NULL;
+    str call_id;
+    int via_id;
+
+    if (get_callid(msg, &call_id) == -1) {
+                LM_ERR("can't get Call-Id field\n");
+        return -1;
+    }
+
+    /*We have to choice VIA id,
+     * for SIP_REQUEST we use VIA1 and for SIP_REPLY we use VIA2 */
+    via_id = more;
+
+    if (get_via_branch(msg, via_id, &viabranch) == -1) {
+                LM_ERR("can't get Call-Id field\n");
+        return -1;
+    }
+
+    if (op == OP_DELETE) {
+        /* Delete the key<->value from the hashtable */
+        if (!lrkproxy_hash_table_remove(call_id, viabranch, op)) {
+                    LM_ERR("lrkproxy hash table failed to remove entry for callen=%d callid=%.*s viabranch=%.*s\n",
+                           call_id.len, call_id.len, call_id.s,
+                           viabranch.len, viabranch.s);
+        } else {
+                    LM_DBG("lrkproxy hash table remove entry for callen=%d callid=%.*s viabranch=%.*s\n",
+                           call_id.len, call_id.len, call_id.s,
+                           viabranch.len, viabranch.s);
+        }
+    }
+    LM_INFO("lrkproxy hash table remove entry for callen=%d callid=%.*s viabranch=%.*s successfully\n",
+            call_id.len, call_id.len, call_id.s,
+            viabranch.len, viabranch.s);
+    return 1;
+}

+ 107 - 0
src/modules/lrkproxy/lrkproxy.h

@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2003-2008 Sippy Software, Inc., http://www.sippysoft.com
+ * Copyright (C) 2020 Mojtaba Esfandiari.S, Nasim-Telecom
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef _LRKPROXY_H
+#define _LRKPROXY_H
+
+#include <sys/uio.h>
+#include "../../core/str.h"
+
+/* Handy macros */
+#define STR2IOVEC(sx, ix)       do {(ix).iov_base = (sx).s; (ix).iov_len = (sx).len;} while(0)
+#define SZ2IOVEC(sx, ix)        do {(ix).iov_base = (sx); (ix).iov_len = strlen(sx);} while(0)
+
+#define CR '\r'
+#define LF '\n'
+#define EOB '\0'
+
+enum lrk_operation {
+    OP_OFFER = 1,
+    OP_ANSWER,
+    OP_DELETE,
+    OP_PING,
+    OP_GETINFO,
+    OP_SETCONNT,
+
+    OP_ANY,
+};
+
+
+enum lrk_alg{
+    LRK_LINER=0,
+    LRK_RR
+};
+
+struct lrkp_node_conf
+{
+    int start_port;
+    int end_port;
+    int current_port;
+    char internal_ip[20];
+    char external_ip[20];
+};
+
+struct lrkp_node {
+    unsigned int		idx;			/* overall index */
+    str					ln_url;			/* unparsed, deletable */
+    int					ln_umode;
+    char				*ln_address;	/* substring of rn_url */
+    int					ln_enable;	/* found unaccessible? */
+    unsigned			ln_weight;		/* for load balancing */
+//    unsigned int		ln_recheck_ticks;
+//    int                 ln_rep_supported;
+//    int                 ln_ptl_supported;
+    struct lrkp_node_conf     *lrkp_n_c;
+    struct lrkp_node	*ln_next;
+};
+
+
+
+struct lrkp_set{
+    unsigned int 		id_set;
+    unsigned			weight_sum;
+    unsigned int		lrkp_node_count;
+    int 				set_disabled;
+    unsigned int		set_recheck_ticks;
+    struct lrkp_node	*ln_first;
+    struct lrkp_node	*ln_last;
+    struct lrkp_set     *lset_next;
+};
+
+
+
+struct lrkp_set_head{
+    struct lrkp_set		*lset_first;
+    struct lrkp_set		*lset_last;
+};
+/* Functions from nathelper */
+//struct lrkp_node *lrkp_node(str, int);
+struct lrkp_node *select_lrkp_node(int);
+char *send_lrkp_command(struct lrkp_node *, struct iovec *, int, int);
+
+struct lrkp_set *get_lrkp_set(str *set_name);
+int insert_lrkp_node(struct lrkp_set *const lrkp_list, const str *const url,
+		const int weight, const int enable);
+
+
+#endif  //_LRKPROXY_H

+ 479 - 0
src/modules/lrkproxy/lrkproxy_funcs.c

@@ -0,0 +1,479 @@
+/*
+ * Copyright (C) 2001-2003 FhG Fokus
+ * Copyright (C) 2014-2015 Sipwise GmbH, http://www.sipwise.com
+ * Copyright (C) 2020 Mojtaba Esfandiari.S, Nasim-Telecom
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "lrkproxy_funcs.h"
+#include "../../core/dprint.h"
+#include "../../core/config.h"
+#include "../../core/ut.h"
+#include "../../core/forward.h"
+#include "../../core/resolve.h"
+#include "../../core/globals.h"
+#include "../../core/udp_server.h"
+#include "../../core/pt.h"
+#include "../../core/parser/msg_parser.h"
+#include "../../core/trim.h"
+#include "../../core/parser/parse_from.h"
+#include "../../core/parser/contact/parse_contact.h"
+#include "../../core/parser/parse_uri.h"
+#include "../../core/parser/parse_content.h"
+#include "../../core/parser/parser_f.h"
+#include "../../core/parser/sdp/sdp_helpr_funcs.h"
+
+#define READ(val) \
+	(*(val + 0) + (*(val + 1) << 8) + (*(val + 2) << 16) + (*(val + 3) << 24))
+#define advance(_ptr,_n,_str,_error) \
+	do{\
+		if ((_ptr)+(_n)>(_str).s+(_str).len)\
+			goto _error;\
+		(_ptr) = (_ptr) + (_n);\
+	}while(0);
+#define one_of_16( _x , _t ) \
+	(_x==_t[0]||_x==_t[15]||_x==_t[8]||_x==_t[2]||_x==_t[3]||_x==_t[4]\
+	||_x==_t[5]||_x==_t[6]||_x==_t[7]||_x==_t[1]||_x==_t[9]||_x==_t[10]\
+	||_x==_t[11]||_x==_t[12]||_x==_t[13]||_x==_t[14])
+#define one_of_8( _x , _t ) \
+	(_x==_t[0]||_x==_t[7]||_x==_t[1]||_x==_t[2]||_x==_t[3]||_x==_t[4]\
+	||_x==_t[5]||_x==_t[6])
+
+
+
+/**
+ * return:
+ * -1: error
+ *  1: text or sdp
+ *  2: multipart
+ *  3: trickle ice sdp fragment
+ */
+int check_content_type(struct sip_msg *msg)
+{
+    static unsigned int appl[16] = {
+            0x6c707061/*appl*/,0x6c707041/*Appl*/,0x6c705061/*aPpl*/,
+            0x6c705041/*APpl*/,0x6c507061/*apPl*/,0x6c507041/*ApPl*/,
+            0x6c505061/*aPPl*/,0x6c505041/*APPl*/,0x4c707061/*appL*/,
+            0x4c707041/*AppL*/,0x4c705061/*aPpL*/,0x4c705041/*APpL*/,
+            0x4c507061/*apPL*/,0x4c507041/*ApPL*/,0x4c505061/*aPPL*/,
+            0x4c505041/*APPL*/};
+    static unsigned int icat[16] = {
+            0x74616369/*icat*/,0x74616349/*Icat*/,0x74614369/*iCat*/,
+            0x74614349/*ICat*/,0x74416369/*icAt*/,0x74416349/*IcAt*/,
+            0x74414369/*iCAt*/,0x74414349/*ICAt*/,0x54616369/*icaT*/,
+            0x54616349/*IcaT*/,0x54614369/*iCaT*/,0x54614349/*ICaT*/,
+            0x54416369/*icAT*/,0x54416349/*IcAT*/,0x54414369/*iCAT*/,
+            0x54414349/*ICAT*/};
+    static unsigned int ion_[8] = {
+            0x006e6f69/*ion_*/,0x006e6f49/*Ion_*/,0x006e4f69/*iOn_*/,
+            0x006e4f49/*IOn_*/,0x004e6f69/*ioN_*/,0x004e6f49/*IoN_*/,
+            0x004e4f69/*iON_*/,0x004e4f49/*ION_*/};
+    static unsigned int sdp_[8] = {
+            0x00706473/*sdp_*/,0x00706453/*Sdp_*/,0x00704473/*sDp_*/,
+            0x00704453/*SDp_*/,0x00506473/*sdP_*/,0x00506453/*SdP_*/,
+            0x00504473/*sDP_*/,0x00504453/*SDP_*/};
+    str           str_type;
+    unsigned int  x;
+    char          *p;
+
+    if (!msg->content_type)
+    {
+                LM_WARN("the header Content-TYPE is absent!"
+                                "let's assume the content is text/plain ;-)\n");
+        return 1;
+    }
+
+    trim_len(str_type.len,str_type.s,msg->content_type->body);
+    if (str_type.len>=15 && (*str_type.s=='m' || *str_type.s=='M')
+        && strncasecmp(str_type.s, "multipart/mixed", 15) == 0) {
+        return 2;
+    }
+    p = str_type.s;
+    advance(p,4,str_type,error_1);
+    x = READ(p-4);
+    if (!one_of_16(x,appl))
+        goto other;
+    advance(p,4,str_type,error_1);
+    x = READ(p-4);
+    if (!one_of_16(x,icat))
+        goto other;
+    advance(p,3,str_type,error_1);
+    x = READ(p-3) & 0x00ffffff;
+    if (!one_of_8(x,ion_))
+        goto other;
+
+    /* skip spaces and tabs if any */
+    while (*p==' ' || *p=='\t')
+        advance(p,1,str_type,error_1);
+    if (*p!='/')
+    {
+                LM_ERR("no / found after primary type\n");
+        goto error;
+    }
+    advance(p,1,str_type,error_1);
+    while ((*p==' ' || *p=='\t') && p+1<str_type.s+str_type.len)
+        advance(p,1,str_type,error_1);
+
+    advance(p,3,str_type,error_1);
+    x = READ(p-3) & 0x00ffffff;
+    if (!one_of_8(x,sdp_)) {
+        if (strncasecmp(p-3, "trickle-ice-sdpfrag", 19) == 0)
+            return 3;
+        goto other;
+    }
+
+    if (*p==';'||*p==' '||*p=='\t'||*p=='\n'||*p=='\r'||*p==0) {
+                LM_DBG("type <%.*s> found valid\n", (int)(p-str_type.s), str_type.s);
+        return 1;
+    } else {
+                LM_ERR("bad end for type!\n");
+        return -1;
+    }
+
+    error_1:
+            LM_ERR("body ended :-(!\n");
+    error:
+    return -1;
+    other:
+            LM_ERR("invalid type for a message\n");
+    return -1;
+}
+
+
+/*
+ * Get message body and check Content-Type header field
+ */
+int extract_body(struct sip_msg *msg, str *body )
+{
+    char c;
+    int ret;
+    str mpdel;
+    char *rest, *p1, *p2;
+    struct hdr_field hf;
+    unsigned int mime;
+
+    body->s = get_body(msg);
+    if (body->s==0) {
+                LM_ERR("failed to get the message body\n");
+        goto error;
+    }
+
+    /*
+     * Better use the content-len value - no need of any explicit
+     * parcing as get_body() parsed all headers and Conten-Length
+     * body header is automaticaly parsed when found.
+     */
+    if (msg->content_length==0) {
+                LM_ERR("failed to get the content length in message\n");
+        goto error;
+    }
+
+    body->len = get_content_length(msg);
+    if (body->len==0) {
+                LM_ERR("message body has length zero\n");
+        goto error;
+    }
+
+    if (body->len + body->s > msg->buf + msg->len) {
+                LM_ERR("content-length exceeds packet-length by %d\n",
+                       (int)((body->len + body->s) - (msg->buf + msg->len)));
+        goto error;
+    }
+
+    /* no need for parse_headers(msg, EOH), get_body will
+     * parse everything */
+    /*is the content type correct?*/
+    if((ret = check_content_type(msg))==-1)
+    {
+                LM_ERR("content type mismatching\n");
+        goto error;
+    }
+
+    if(ret!=2)
+        goto done;
+
+    /* multipart body */
+    if(get_mixed_part_delimiter(&msg->content_type->body,&mpdel) < 0) {
+        goto error;
+    }
+    p1 = find_sdp_line_delimiter(body->s, body->s+body->len, mpdel);
+    if (p1 == NULL) {
+                LM_ERR("empty multipart content\n");
+        return -1;
+    }
+    p2=p1;
+    c = 0;
+    for(;;)
+    {
+        p1 = p2;
+        if (p1 == NULL || p1 >= body->s+body->len)
+            break; /* No parts left */
+        p2 = find_next_sdp_line_delimiter(p1, body->s+body->len,
+                                          mpdel, body->s+body->len);
+        /* p2 is text limit for application parsing */
+        rest = eat_line(p1 + mpdel.len + 2, p2 - p1 - mpdel.len - 2);
+        if ( rest > p2 ) {
+                    LM_ERR("Unparsable <%.*s>\n", (int)(p1-p1), p1);
+            return -1;
+        }
+        while( rest<p2 ) {
+            memset(&hf,0, sizeof(struct hdr_field));
+            rest = get_sdp_hdr_field(rest, p2, &hf);
+            if(hf.type==HDR_EOH_T)
+                break;
+            if(hf.type==HDR_ERROR_T)
+                return -1;
+            if(hf.type==HDR_CONTENTTYPE_T) {
+                if(decode_mime_type(hf.body.s, hf.body.s + hf.body.len,
+                                    &mime)==NULL)
+                    return -1;
+                if (((((unsigned int)mime)>>16) == TYPE_APPLICATION)
+                    && ((mime&0x00ff) == SUBTYPE_SDP)) {
+                    c = 1;
+                }
+            }
+        } /* end of while */
+        if(c==1)
+        {
+            if (rest < p2 && *rest == '\r') rest++;
+            if (rest < p2 && *rest == '\n') rest++;
+            if (rest < p2 && p2[-1] == '\n') p2--;
+            if (rest < p2 && p2[-1] == '\r') p2--;
+            body->s = rest;
+            body->len = p2-rest;
+            goto done;
+        }
+    }
+
+    error:
+    return -1;
+
+    done:
+    /*LM_DBG("DEBUG:extract_body:=|%.*s|\n",body->len,body->s);*/
+    return ret; /* mirrors return type of check_content_type */
+}
+
+/*
+ * Some helper functions taken verbatim from tm module.
+ */
+
+/*
+ * Extract Call-ID value
+ * assumes the callid header is already parsed
+ * (so make sure it is, before calling this function or
+ *  it might fail even if the message _has_ a callid)
+ */
+int
+get_callid(struct sip_msg* _m, str* _cid)
+{
+
+    if ((parse_headers(_m, HDR_CALLID_F, 0) == -1)) {
+                LM_ERR("failed to parse call-id header\n");
+        return -1;
+    }
+
+    if (_m->callid == NULL) {
+                LM_ERR("call-id not found\n");
+        return -1;
+    }
+
+    _cid->s = _m->callid->body.s;
+    _cid->len = _m->callid->body.len;
+    trim(_cid);
+    return 0;
+}
+
+/*
+ * Extract tag from To header field of a response
+ */
+int
+get_to_tag(struct sip_msg* _m, str* _tag)
+{
+
+    if (parse_to_header(_m) < 0) {
+                LM_ERR("To header field missing\n");
+        return -1;
+    }
+
+    if (get_to(_m)->tag_value.len) {
+        _tag->s = get_to(_m)->tag_value.s;
+        _tag->len = get_to(_m)->tag_value.len;
+    } else {
+        _tag->s = NULL; /* fixes gcc 4.0 warnings */
+        _tag->len = 0;
+    }
+
+    return 0;
+}
+
+/*
+ * Extract tag from From header field of a request
+ */
+int
+get_from_tag(struct sip_msg* _m, str* _tag)
+{
+
+    if (parse_from_header(_m)<0) {
+                LM_ERR("failed to parse From header\n");
+        return -1;
+    }
+
+    if (get_from(_m)->tag_value.len) {
+        _tag->s = get_from(_m)->tag_value.s;
+        _tag->len = get_from(_m)->tag_value.len;
+    } else {
+        _tag->s = NULL; /* fixes gcc 4.0 warnings */
+        _tag->len = 0;
+    }
+
+    return 0;
+}
+
+/*
+ * Extract URI from the Contact header field
+ */
+int
+get_contact_uri(struct sip_msg* _m, struct sip_uri *uri, contact_t** _c)
+{
+
+    if ((parse_headers(_m, HDR_CONTACT_F, 0) == -1) || !_m->contact)
+        return -1;
+    if (!_m->contact->parsed && parse_contact(_m->contact) < 0) {
+                LM_ERR("failed to parse Contact body\n");
+        return -1;
+    }
+    *_c = ((contact_body_t*)_m->contact->parsed)->contacts;
+    if (*_c == NULL)
+        /* no contacts found */
+        return -1;
+
+    if (parse_uri((*_c)->uri.s, (*_c)->uri.len, uri) < 0 || uri->host.len <= 0) {
+                LM_ERR("failed to parse Contact URI [%.*s]\n",
+                       (*_c)->uri.len, ((*_c)->uri.s)?(*_c)->uri.s:"");
+        return -1;
+    }
+    return 0;
+}
+
+/*
+ * Extract branch from Via header
+ */
+int
+get_via_branch(struct sip_msg* msg, int vianum, str* _branch)
+{
+    struct via_body *via;
+    struct via_param *p;
+
+    if (parse_via_header(msg, vianum, &via) < 0)
+        return -1;
+
+    for (p = via->param_lst; p; p = p->next)
+    {
+        if (p->name.len == strlen("branch")
+            && strncasecmp(p->name.s, "branch", strlen("branch")) == 0) {
+            _branch->s = p->value.s;
+            _branch->len = p->value.len;
+            return 0;
+        }
+    }
+    return -1;
+}
+
+int get_sdp_ipaddr_media(struct sip_msg *msg, str *ip_addr) {
+    sdp_session_cell_t *sdp_session;
+    sdp_stream_cell_t *sdp_stream;
+    sdp_info_t *sdp = (sdp_info_t *) msg->body;
+    if (!sdp) {
+                LM_INFO("sdp null\n");
+        return -1;
+    }
+
+
+    int sdp_session_num = 0;
+    sdp_session = get_sdp_session(msg, sdp_session_num);
+
+    if (!sdp_session) {
+                LM_INFO("can not get the sdp session\n");
+        return 0;
+    }
+
+    if (sdp_session->ip_addr.s && sdp_session->ip_addr.len > 0) {
+                LM_INFO("sdp_session->ip_addr:%.*s\n", sdp_session->ip_addr.len, sdp_session->ip_addr.s);
+        ip_addr->s = sdp_session->ip_addr.s;
+        ip_addr->len = sdp_session->ip_addr.len;
+        trim(ip_addr);
+    }
+    else {
+        int sdp_stream_num = 0;
+        sdp_stream = get_sdp_stream(msg, sdp_session_num, sdp_stream_num);
+        if (!sdp_stream) {
+                    LM_INFO("can not get the sdp stream\n");
+            return 0;
+        }
+        if (sdp_stream->ip_addr.s && sdp_stream->ip_addr.len > 0) {
+            LM_INFO("sdp_stream->ip_addr:%.*s\n", sdp_stream->ip_addr.len, sdp_stream->ip_addr.s);
+            ip_addr->s = sdp_stream->ip_addr.s;
+            ip_addr->len = sdp_stream->ip_addr.len;
+            trim(ip_addr);
+        }
+    }
+
+    
+    return 0;
+}
+
+
+int get_sdp_port_media(struct sip_msg *msg, str *port){
+//    sdp_session_cell_t *sdp_session;
+    sdp_stream_cell_t *sdp_stream;
+    int sdp_session_num = 0;
+
+    sdp_info_t *sdp = (sdp_info_t *)msg->body;
+    if(!sdp) {
+                LM_INFO("sdp null\n");
+        return -1;
+    }
+
+//    sdp_session = get_sdp_session(msg, sdp_session_num);
+//    if(!sdp_session) {
+//                LM_INFO("can not get the sdp session\n");
+//        return 0;
+//    } else {
+//                LM_INFO("NEW_IP_ADDRESS:>%.*s>\n", sdp_session->ip_addr.len, sdp_session->ip_addr.s);
+//        lrk_sdp_info->ip_addr.s = sdp_session->ip_addr;
+        int sdp_stream_num = 0;
+        sdp_stream = get_sdp_stream(msg, sdp_session_num, sdp_stream_num);
+        if (!sdp_stream) {
+                    LM_INFO("can not get the sdp stream\n");
+            return -1;
+        } else {
+//                    LM_INFO ("PORT:<%.*s>\n", sdp_stream->port.len, sdp_stream->port.s);
+//            str2int(&sdp_stream->port, lrk_sdp_info->port)
+            port->s = sdp_stream->port.s;
+            port->len = sdp_stream->port.len;
+            trim(port);
+        }
+//    }
+    return 0;
+
+}
+

+ 41 - 0
src/modules/lrkproxy/lrkproxy_funcs.h

@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2001-2003 FhG Fokus
+ * Copyright (C) 2014-2015 Sipwise GmbH, http://www.sipwise.com
+ * Copyright (C) 2020 Mojtaba Esfandiari.S, Nasim-Telecom
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#ifndef _LRKPROXY_FUNCS_H
+#define _LRKPROXY_FUNCS_H
+
+
+#include "../../core/str.h"
+#include "../../core/parser/msg_parser.h"
+#include "../../core/parser/contact/contact.h"
+
+int extract_body(struct sip_msg * , str *);
+int check_content_type(struct sip_msg * );
+int get_callid(struct sip_msg *, str *);
+int get_to_tag(struct sip_msg *, str *);
+int get_from_tag(struct sip_msg *, str *);
+int get_contact_uri(struct sip_msg *, struct sip_uri *, contact_t **);
+int get_via_branch(struct sip_msg *, int, str *);
+int get_sdp_ipaddr_media(struct sip_msg *msg, str *ip_addr);
+int get_sdp_port_media(struct sip_msg *msg, str *port);
+
+
+#endif //_LRKPROXY_FUNCS_H

+ 522 - 0
src/modules/lrkproxy/lrkproxy_hash.c

@@ -0,0 +1,522 @@
+/*
+ * Copyright (C) 2003-2008 Sippy Software, Inc., http://www.sippysoft.com
+ * Copyright (C) 2014-2015 Sipwise GmbH, http://www.sipwise.com
+ * Copyright (C) 2020 Mojtaba Esfandiari.S, Nasim-Telecom
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "lrkproxy.h"
+#include "lrkproxy_hash.h"
+
+#include "../../core/str.h"
+#include "../../core/dprint.h"
+#include "../../core/mem/shm_mem.h"
+#include "../../core/timer.h"
+
+static void lrkproxy_hash_table_free_row_lock(gen_lock_t *row_lock);
+
+static struct lrkproxy_hash_table *lrkproxy_hash_table;
+
+/* get from sipwise rtpengine */
+static int str_cmp_str(const str a, const str b) {
+    if (a.len < b.len)
+        return -1;
+    if (a.len > b.len)
+        return 1;
+    if (a.len == 0 && b.len == 0)
+        return 0;
+    return memcmp(a.s, b.s, a.len);
+}
+
+/* get from sipwise rtpengine */
+static int str_equal(str a, str b) {
+    return (str_cmp_str(a, b) == 0);
+}
+
+/* get from sipwise rtpengine */
+static unsigned int str_hash(str s) {
+    unsigned int ret = 5381;
+    str it = s;
+
+    while (it.len > 0) {
+        ret = (ret << 5) + ret + *it.s;
+        it.s++;
+        it.len--;
+    }
+
+    return ret % lrkproxy_hash_table->size;
+}
+
+/* lrkproxy hash API */
+int lrkproxy_hash_table_init(int size) {
+    int i;
+    int hash_table_size;
+
+
+    hash_table_size = size;
+
+
+//            LM_DBG(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>lrkproxy_hash_table size = %d\n", hash_table_size);
+            LM_INFO(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>lrkproxy_hash_table size = %d\n", hash_table_size);
+
+    // init hashtable
+    lrkproxy_hash_table = shm_malloc(sizeof(struct lrkproxy_hash_table));
+    if (!lrkproxy_hash_table) {
+                LM_ERR("no shm left to create lrkproxy_hash_table\n");
+        return 0;
+    }
+    memset(lrkproxy_hash_table, 0, sizeof(struct lrkproxy_hash_table));
+    lrkproxy_hash_table->size = hash_table_size;
+
+    // init hashtable row_locks
+    lrkproxy_hash_table->row_locks = shm_malloc(hash_table_size * sizeof(gen_lock_t*));
+    if (!lrkproxy_hash_table->row_locks) {
+                LM_ERR("no shm left to create lrkproxy_hash_table->row_locks\n");
+        lrkproxy_hash_table_destroy();
+        return 0;
+    }
+    memset(lrkproxy_hash_table->row_locks, 0, hash_table_size * sizeof(gen_lock_t*));
+
+    // init hashtable row_entry_list
+    lrkproxy_hash_table->row_entry_list = shm_malloc(lrkproxy_hash_table->size * sizeof(struct lrkproxy_hash_entry*));
+    if (!lrkproxy_hash_table->row_entry_list) {
+                LM_ERR("no shm left to create lrkproxy_hash_table->row_entry_list\n");
+        lrkproxy_hash_table_destroy();
+        return 0;
+    }
+    memset(lrkproxy_hash_table->row_entry_list, 0, lrkproxy_hash_table->size * sizeof(struct lrkproxy_hash_entry*));
+
+    // init hashtable row_totals
+    lrkproxy_hash_table->row_totals = shm_malloc(hash_table_size * sizeof(unsigned int));
+    if (!lrkproxy_hash_table->row_totals) {
+                LM_ERR("no shm left to create lrkproxy_hash_table->row_totals\n");
+        lrkproxy_hash_table_destroy();
+        return 0;
+    }
+    memset(lrkproxy_hash_table->row_totals, 0, hash_table_size * sizeof(unsigned int));
+
+    // init hashtable  row_locks[i], row_entry_list[i] and row_totals[i]
+    for (i = 0; i < hash_table_size; i++) {
+        // alloc hashtable row_locks[i]
+        lrkproxy_hash_table->row_locks[i] = lock_alloc();
+        if (!lrkproxy_hash_table->row_locks[i]) {
+                    LM_ERR("no shm left to create lrkproxy_hash_table->row_locks[%d]\n", i);
+            lrkproxy_hash_table_destroy();
+            return 0;
+        }
+
+        // init hashtable row_locks[i]
+        if (!lock_init(lrkproxy_hash_table->row_locks[i])) {
+                    LM_ERR("fail to init lrkproxy_hash_table->row_locks[%d]\n", i);
+            lrkproxy_hash_table_destroy();
+            return 0;
+        }
+
+        // init hashtable row_entry_list[i]
+        lrkproxy_hash_table->row_entry_list[i] = shm_malloc(sizeof(struct lrkproxy_hash_entry));
+        if (!lrkproxy_hash_table->row_entry_list[i]) {
+                    LM_ERR("no shm left to create lrkproxy_hash_table->row_entry_list[%d]\n", i);
+            lrkproxy_hash_table_destroy();
+            return 0;
+        }
+        memset(lrkproxy_hash_table->row_entry_list[i], 0, sizeof(struct lrkproxy_hash_entry));
+
+        lrkproxy_hash_table->row_entry_list[i]->tout = -1;
+        lrkproxy_hash_table->row_entry_list[i]->next = NULL;
+
+        // init hashtable row_totals[i]
+        lrkproxy_hash_table->row_totals[i] = 0;
+    }
+
+    return 1;
+}
+
+int lrkproxy_hash_table_destroy() {
+    int i;
+
+    // check lrkproxy hashtable
+    if (!lrkproxy_hash_table) {
+                LM_ERR("NULL lrkproxy_hash_table\n");
+        return 1;
+    }
+
+    // check lrkproxy hashtable->row_locks
+    if (!lrkproxy_hash_table->row_locks) {
+                LM_ERR("NULL lrkproxy_hash_table->row_locks\n");
+        shm_free(lrkproxy_hash_table);
+        lrkproxy_hash_table = NULL;
+        return 1;
+    }
+
+    // destroy hashtable content
+    for (i = 0; i < lrkproxy_hash_table->size; i++) {
+        // lock
+        if (!lrkproxy_hash_table->row_locks[i]) {
+                    LM_ERR("NULL lrkproxy_hash_table->row_locks[%d]\n", i);
+            continue;
+        } else {
+            lock_get(lrkproxy_hash_table->row_locks[i]);
+        }
+
+        // check lrkproxy hashtable->row_entry_list
+        if (!lrkproxy_hash_table->row_entry_list) {
+                    LM_ERR("NULL lrkproxy_hash_table->row_entry_list\n");
+        } else {
+            // destroy hashtable row_entry_list[i]
+            lrkproxy_hash_table_free_row_entry_list(lrkproxy_hash_table->row_entry_list[i]);
+            lrkproxy_hash_table->row_entry_list[i] = NULL;
+        }
+
+        // unlock
+        lock_release(lrkproxy_hash_table->row_locks[i]);
+
+        // destroy hashtable row_locks[i]
+        lrkproxy_hash_table_free_row_lock(lrkproxy_hash_table->row_locks[i]);
+        lrkproxy_hash_table->row_locks[i] = NULL;
+    }
+
+    // destroy hashtable row_entry_list
+    if (!lrkproxy_hash_table->row_entry_list) {
+                LM_ERR("NULL lrkproxy_hash_table->row_entry_list\n");
+    } else {
+        shm_free(lrkproxy_hash_table->row_entry_list);
+        lrkproxy_hash_table->row_entry_list = NULL;
+    }
+
+    // destroy hashtable row_totals
+    if (!lrkproxy_hash_table->row_totals) {
+                LM_ERR("NULL lrkproxy_hash_table->row_totals\n");
+    } else {
+        shm_free(lrkproxy_hash_table->row_totals);
+        lrkproxy_hash_table->row_totals = NULL;
+    }
+
+    // destroy hashtable row_locks
+    if (!lrkproxy_hash_table->row_locks) {
+        // should not be the case; just for code symmetry
+                LM_ERR("NULL lrkproxy_hash_table->row_locks\n");
+    } else {
+        shm_free(lrkproxy_hash_table->row_locks);
+        lrkproxy_hash_table->row_locks = NULL;
+    }
+
+    // destroy hashtable
+    if (!lrkproxy_hash_table) {
+        // should not be the case; just for code symmetry
+                LM_ERR("NULL lrkproxy_hash_table\n");
+    } else {
+        shm_free(lrkproxy_hash_table);
+        lrkproxy_hash_table = NULL;
+    }
+
+    return 1;
+}
+
+
+void lrkproxy_hash_table_free_entry(struct lrkproxy_hash_entry *entry) {
+    if (!entry) {
+                LM_ERR("try to free a NULL entry\n");
+        return ;
+    }
+
+    // free callid
+    if (entry->callid.s) {
+        shm_free(entry->callid.s);
+    }
+
+    // free viabranch
+    if (entry->viabranch.s) {
+        shm_free(entry->viabranch.s);
+    }
+
+    // free entry
+    shm_free(entry);
+
+    return ;
+}
+
+void lrkproxy_hash_table_free_row_entry_list(struct lrkproxy_hash_entry *row_entry_list) {
+    struct lrkproxy_hash_entry *entry, *last_entry;
+
+    if (!row_entry_list) {
+                LM_ERR("try to free a NULL row_entry_list\n");
+        return ;
+    }
+
+    entry = row_entry_list;
+    while (entry) {
+        last_entry = entry;
+        entry = entry->next;
+        lrkproxy_hash_table_free_entry(last_entry);
+        last_entry = NULL;
+    }
+
+    return ;
+}
+
+int lrkproxy_hash_table_insert(str callid, str viabranch, struct lrkproxy_hash_entry *value) {
+    struct lrkproxy_hash_entry *entry, *last_entry;
+    struct lrkproxy_hash_entry *new_entry = (struct lrkproxy_hash_entry *) value;
+    unsigned int hash_index;
+
+    // sanity checks
+    if (!lrkproxy_hash_table_sanity_checks()) {
+                LM_ERR("sanity checks failed\n");
+        return 0;
+    }
+
+    // get entry list
+    hash_index = str_hash(callid);
+    entry = lrkproxy_hash_table->row_entry_list[hash_index];
+    last_entry = entry;
+
+    // lock
+    if (lrkproxy_hash_table->row_locks[hash_index]) {
+        lock_get(lrkproxy_hash_table->row_locks[hash_index]);
+    } else {
+                LM_ERR("NULL lrkproxy_hash_table->row_locks[%d]\n", hash_index);
+        return 0;
+    }
+
+    while (entry) {
+        // if found, don't add new entry
+        if (str_equal(entry->callid, new_entry->callid) &&
+            str_equal(entry->viabranch, new_entry->viabranch)) {
+            // unlock
+            lock_release(lrkproxy_hash_table->row_locks[hash_index]);
+                    LM_NOTICE("callid=%.*s, viabranch=%.*s already in hashtable, ignore new value\n",
+                              entry->callid.len, entry->callid.s,
+                              entry->viabranch.len, entry->viabranch.s);
+            return 0;
+        }
+
+        // if expired entry discovered, delete it
+        if (entry->tout < get_ticks()) {
+            // set pointers; exclude entry
+            last_entry->next = entry->next;
+
+            // free current entry; entry points to unknown
+            lrkproxy_hash_table_free_entry(entry);
+
+            // set pointers
+            entry = last_entry;
+
+            // update total
+            lrkproxy_hash_table->row_totals[hash_index]--;
+        }
+
+        // next entry in the list
+        last_entry = entry;
+        entry = entry->next;
+    }
+
+    last_entry->next = new_entry;
+
+    // update total
+    lrkproxy_hash_table->row_totals[hash_index]++;
+
+    // unlock
+    lock_release(lrkproxy_hash_table->row_locks[hash_index]);
+
+    return 1;
+}
+
+int lrkproxy_hash_table_remove(str callid, str viabranch, enum lrk_operation op) {
+    struct lrkproxy_hash_entry *entry, *last_entry;
+    unsigned int hash_index;
+    int found = 0;
+
+    // sanity checks
+    if (!lrkproxy_hash_table_sanity_checks()) {
+                LM_ERR("sanity checks failed\n");
+        return 0;
+    }
+
+    // get first entry from entry list; jump over unused list head
+    hash_index = str_hash(callid);
+    entry = lrkproxy_hash_table->row_entry_list[hash_index];
+    last_entry = entry;
+
+    // lock
+    if (lrkproxy_hash_table->row_locks[hash_index]) {
+        lock_get(lrkproxy_hash_table->row_locks[hash_index]);
+    } else {
+                LM_ERR("NULL lrkproxy_hash_table->row_locks[%d]\n", hash_index);
+        return 0;
+    }
+
+    while (entry) {
+        // if callid found, delete entry
+        if ((str_equal(entry->callid, callid) && str_equal(entry->viabranch, viabranch)) ||
+            (str_equal(entry->callid, callid) && viabranch.len == 0 && op == OP_DELETE)) {
+            // set pointers; exclude entry
+            last_entry->next = entry->next;
+
+            // free current entry; entry points to unknown
+            lrkproxy_hash_table_free_entry(entry);
+
+            // set pointers
+            entry = last_entry;
+
+            // update total
+            lrkproxy_hash_table->row_totals[hash_index]--;
+
+            found = 1;
+
+            if (!(viabranch.len == 0 && op == OP_DELETE)) {
+                // unlock
+                lock_release(lrkproxy_hash_table->row_locks[hash_index]);
+                return found;
+            }
+
+            // try to also delete other viabranch entries for callid
+            last_entry = entry;
+            entry = entry->next;
+            continue;
+        }
+
+        // if expired entry discovered, delete it
+        if (entry->tout < get_ticks()) {
+            // set pointers; exclude entry
+            last_entry->next = entry->next;
+
+            // free current entry; entry points to unknown
+            lrkproxy_hash_table_free_entry(entry);
+
+            // set pointers
+            entry = last_entry;
+
+            // update total
+            lrkproxy_hash_table->row_totals[hash_index]--;
+        }
+
+        last_entry = entry;
+        entry = entry->next;
+    }
+
+    // unlock
+    lock_release(lrkproxy_hash_table->row_locks[hash_index]);
+
+    return found;
+}
+//struct lrkp_node *lrkproxy_hash_table_lookup(str callid, str viabranch, enum lrk_operation op) {
+//struct lrkproxy_hash_entry *lrkproxy_hash_table_lookup(str callid, str viabranch, enum lrk_operation op) {
+struct lrkproxy_hash_entry *lrkproxy_hash_table_lookup(str callid, str viabranch) {
+    struct lrkproxy_hash_entry *entry, *last_entry;
+    unsigned int hash_index;
+//    struct lrkp_node *node;
+
+    // sanity checks
+    if (!lrkproxy_hash_table_sanity_checks()) {
+                LM_ERR("sanity checks failed\n");
+        return 0;
+    }
+
+    // get first entry from entry list; jump over unused list head
+    hash_index = str_hash(callid);
+    entry = lrkproxy_hash_table->row_entry_list[hash_index];
+    last_entry = entry;
+
+    // lock
+    if (lrkproxy_hash_table->row_locks[hash_index]) {
+        lock_get(lrkproxy_hash_table->row_locks[hash_index]);
+    } else {
+                LM_ERR("NULL lrkproxy_hash_table->row_locks[%d]\n", hash_index);
+        return 0;
+    }
+
+    while (entry) {
+        // if callid found, return entry
+//        if ((str_equal(entry->callid, callid) && str_equal(entry->viabranch, viabranch)) ||
+//            (str_equal(entry->callid, callid) && viabranch.len == 0 && op == OP_DELETE)) {
+        if (str_equal(entry->callid, callid) && str_equal(entry->viabranch, viabranch)) {
+//            node = entry->node;
+            // unlock
+            lock_release(lrkproxy_hash_table->row_locks[hash_index]);
+            return entry;
+//            return node;
+        }
+
+        // if expired entry discovered, delete it
+        if (entry->tout < get_ticks()) {
+            // set pointers; exclude entry
+            last_entry->next = entry->next;
+
+            // free current entry; entry points to unknown
+            lrkproxy_hash_table_free_entry(entry);
+
+            // set pointers
+            entry = last_entry;
+
+            // update total
+            lrkproxy_hash_table->row_totals[hash_index]--;
+        }
+
+        last_entry = entry;
+        entry = entry->next;
+    }
+
+    // unlock
+    lock_release(lrkproxy_hash_table->row_locks[hash_index]);
+
+    return NULL;
+}
+
+
+static void lrkproxy_hash_table_free_row_lock(gen_lock_t *row_lock) {
+    if (!row_lock) {
+                LM_ERR("try to free a NULL lock\n");
+        return ;
+    }
+
+    lock_destroy(row_lock);
+    lock_dealloc(row_lock);
+
+    return ;
+}
+
+int lrkproxy_hash_table_sanity_checks() {
+    // check lrkproxy hashtable
+    if (!lrkproxy_hash_table) {
+                LM_ERR("NULL lrkproxy_hash_table\n");
+        return 0;
+    }
+
+    // check lrkproxy hashtable->row_locks
+    if (!lrkproxy_hash_table->row_locks) {
+                LM_ERR("NULL lrkproxy_hash_table->row_locks\n");
+        return 0;
+    }
+
+    // check lrkproxy hashtable->row_entry_list
+    if (!lrkproxy_hash_table->row_entry_list) {
+                LM_ERR("NULL lrkproxy_hash_table->row_entry_list\n");
+        return 0;
+    }
+
+    // check lrkproxy hashtable->row_totals
+    if (!lrkproxy_hash_table->row_totals) {
+                LM_ERR("NULL lrkproxy_hash_table->row_totals\n");
+        return 0;
+    }
+
+    return 1;
+}
+

+ 75 - 0
src/modules/lrkproxy/lrkproxy_hash.h

@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2003-2008 Sippy Software, Inc., http://www.sippysoft.com
+ * Copyright (C) 2014-2015 Sipwise GmbH, http://www.sipwise.com
+ * Copyright (C) 2020 Mojtaba Esfandiari.S, Nasim-Telecom
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef LRKPROXY_HASH_H
+#define LRKPROXY_HASH_H
+
+#include "../../core/str.h"
+#include "../../core/locking.h"
+
+
+/* table entry */
+struct lrkproxy_hash_entry {
+    str src_ipv4;   //media ip address of initiator call in INVITE SIP message.
+    str dst_ipv4;   //media ip address of selected node in 200Ok SIP message.
+    str snat_ipv4;  //change media ip address to selected node.
+    str dnat_ipv4;  //change media ip address to orgin destination party.
+    str src_port;   //media port of initiator call in INVITE SIP message
+    str dst_port;   //media port of selected node in 200Ok SIP message.
+    str snat_port;  //change media port to selected node.
+    str dnat_port;  //change media port to orgin destination party.
+
+    str callid;				// call callid
+    str viabranch;				// call viabranch
+    struct lrkp_node *node;			// call selected node
+
+    unsigned int tout;			// call timeout
+    struct lrkproxy_hash_entry *next;	// call next
+};
+
+/* table */
+struct lrkproxy_hash_table {
+    struct lrkproxy_hash_entry **row_entry_list;	// vector of size pointers to entry
+    gen_lock_t **row_locks;				// vector of size pointers to locks
+    unsigned int *row_totals;			// vector of size numbers of entries in the hashtable rows
+    unsigned int size;				// hash table size
+};
+
+
+
+int lrkproxy_hash_table_init(int hsize);
+int lrkproxy_hash_table_destroy();
+int lrkproxy_hash_table_insert(str callid, str viabranch, struct lrkproxy_hash_entry *value);
+int lrkproxy_hash_table_remove(str callid, str viabranch, enum lrk_operation);
+struct lrkproxy_hash_entry *lrkproxy_hash_table_lookup(str callid, str viabranch);
+//struct lrkproxy_hash_entry *lrkproxy_hash_table_lookup(str callid, str viabranch, enum lrk_operation);
+//struct lrkp_node *lrkproxy_hash_table_lookup(str callid, str viabranch, enum lrk_operation);
+//void lrkproxy_hash_table_print();
+//unsigned int lrkproxy_hash_table_total();
+
+void lrkproxy_hash_table_free_entry(struct lrkproxy_hash_entry *entry);
+void lrkproxy_hash_table_free_row_entry_list(struct lrkproxy_hash_entry *row_entry_list);
+
+int lrkproxy_hash_table_sanity_checks();
+
+#endif //LRKPROXY_HASH_H

+ 63 - 22
src/modules/pv/pv_branch.c

@@ -93,6 +93,12 @@ int pv_get_branchx_helper(sip_msg_t *msg, pv_param_t *param,
 			if(br->location_ua_len==0)
 			if(br->location_ua_len==0)
 				return pv_get_null(msg, param, res);
 				return pv_get_null(msg, param, res);
 			return pv_get_strlval(msg, param, res, br->location_ua, br->location_ua_len);
 			return pv_get_strlval(msg, param, res, br->location_ua, br->location_ua_len);
+		case 9: /* otcpid */
+			return pv_get_uintval(msg, param, res, br->otcpid);
+		case 10: /* instance */
+			if(br->instance_len==0)
+				return pv_get_null(msg, param, res);
+			return pv_get_strlval(msg, param, res, br->instance, br->instance_len);
 		default:
 		default:
 			/* 0 - uri */
 			/* 0 - uri */
 			return pv_get_strlval(msg, param, res, br->uri, br->len);
 			return pv_get_strlval(msg, param, res, br->uri, br->len);
@@ -283,6 +289,22 @@ int pv_set_branchx_helper(sip_msg_t *msg, pv_param_t *param,
 		case 8: /* location_ua */
 		case 8: /* location_ua */
 			/* do nothing - cannot set the location_ua */
 			/* do nothing - cannot set the location_ua */
 		break;
 		break;
+		case 9: /* otcpid */
+			if(val==NULL || (val->flags&PV_VAL_NULL))
+			{
+				br->otcpid = 0;
+				break;
+			}
+			if(!(val->flags&PV_VAL_INT))
+			{
+				LM_ERR("int value required to set branch flags\n");
+				return -1;
+			}
+			br->otcpid = val->ri;
+		break;
+		case 10: /* instance */
+			/* do nothing - cannot set the instance */
+		break;
 		default:
 		default:
 			/* 0 - uri */
 			/* 0 - uri */
 			if(val==NULL || (val->flags&PV_VAL_NULL))
 			if(val==NULL || (val->flags&PV_VAL_NULL))
@@ -335,42 +357,53 @@ int pv_parse_branchx_name(pv_spec_p sp, str *in)
 
 
 	switch(in->len)
 	switch(in->len)
 	{
 	{
-		case 3: 
-			if(strncmp(in->s, "uri", 3)==0)
-				sp->pvp.pvn.u.isname.name.n = 0;
+		case 1:
+			if(*in->s=='q' || *in->s=='Q')
+				sp->pvp.pvn.u.isname.name.n = 3;
 			else goto error;
 			else goto error;
 		break;
 		break;
-		case 7: 
-			if(strncmp(in->s, "dst_uri", 7)==0)
-				sp->pvp.pvn.u.isname.name.n = 1;
+		case 3:
+			if(strncmp(in->s, "uri", 3)==0)
+				sp->pvp.pvn.u.isname.name.n = 0;
 			else goto error;
 			else goto error;
 		break;
 		break;
-		case 4: 
+		case 4:
 			if(strncmp(in->s, "path", 4)==0)
 			if(strncmp(in->s, "path", 4)==0)
 				sp->pvp.pvn.u.isname.name.n = 2;
 				sp->pvp.pvn.u.isname.name.n = 2;
 			else if (strncmp(in->s, "ruid", 4)==0)
 			else if (strncmp(in->s, "ruid", 4)==0)
 				sp->pvp.pvn.u.isname.name.n = 7;
 				sp->pvp.pvn.u.isname.name.n = 7;
 			else goto error;
 			else goto error;
 		break;
 		break;
-		case 1: 
-			if(*in->s=='q' || *in->s=='Q')
-				sp->pvp.pvn.u.isname.name.n = 3;
+		case 5:
+			if(strncmp(in->s, "count", 5)==0)
+				sp->pvp.pvn.u.isname.name.n = 5;
+			else if(strncmp(in->s, "flags", 5)==0)
+				sp->pvp.pvn.u.isname.name.n = 6;
+			else goto error;
+		break;
+		case 6:
+			if(strncmp(in->s, "otcpid", 6)==0)
+				sp->pvp.pvn.u.isname.name.n = 9;
+			else goto error;
+		break;
+		case 7:
+			if(strncmp(in->s, "dst_uri", 7)==0)
+				sp->pvp.pvn.u.isname.name.n = 1;
+			else goto error;
+		break;
+		case 8:
+			if(strncmp(in->s, "instance", 8)==0)
+				sp->pvp.pvn.u.isname.name.n = 10;
 			else goto error;
 			else goto error;
 		break;
 		break;
-		case 11: 
+		case 11:
 			if(strncmp(in->s, "send_socket", 11)==0)
 			if(strncmp(in->s, "send_socket", 11)==0)
 				sp->pvp.pvn.u.isname.name.n = 4;
 				sp->pvp.pvn.u.isname.name.n = 4;
 			else if(strncmp(in->s, "location_ua", 11)==0)
 			else if(strncmp(in->s, "location_ua", 11)==0)
 				sp->pvp.pvn.u.isname.name.n = 8;
 				sp->pvp.pvn.u.isname.name.n = 8;
 			else goto error;
 			else goto error;
 		break;
 		break;
-		case 5: 
-			if(strncmp(in->s, "count", 5)==0)
-				sp->pvp.pvn.u.isname.name.n = 5;
-			else if(strncmp(in->s, "flags", 5)==0)
-				sp->pvp.pvn.u.isname.name.n = 6;
-			else goto error;
-		break;
+
 		default:
 		default:
 			goto error;
 			goto error;
 	}
 	}
@@ -804,6 +837,7 @@ int sbranch_set_ruri(sip_msg_t *msg)
 		set_force_socket(msg, br->force_send_socket);
 		set_force_socket(msg, br->force_send_socket);
 
 
 	msg->reg_id = br->reg_id;
 	msg->reg_id = br->reg_id;
+	msg->otcpid = br->otcpid;
 	set_ruri_q(br->q);
 	set_ruri_q(br->q);
 	old_bflags = 0;
 	old_bflags = 0;
 	getbflagsval(0, &old_bflags);
 	getbflagsval(0, &old_bflags);
@@ -824,7 +858,9 @@ int sbranch_append(sip_msg_t *msg)
 	str path = {0};
 	str path = {0};
 	str ruid = {0};
 	str ruid = {0};
 	str location_ua = {0};
 	str location_ua = {0};
+	str instance = {0};
 	branch_t *br;
 	branch_t *br;
+	branch_t *newbr;
 
 
 	br = &_pv_sbranch;
 	br = &_pv_sbranch;
 	if(br->len==0)
 	if(br->len==0)
@@ -849,14 +885,19 @@ int sbranch_append(sip_msg_t *msg)
 		location_ua.s = br->location_ua;
 		location_ua.s = br->location_ua;
 		location_ua.len = br->location_ua_len;
 		location_ua.len = br->location_ua_len;
 	}
 	}
+	if(br->instance_len) {
+		instance.s = br->instance;
+		instance.len = br->instance_len;
+	}
 
 
-	if (append_branch(msg, &uri, &duri, &path, br->q, br->flags,
-					  br->force_send_socket, 0 /*instance*/, br->reg_id,
-					  &ruid, &location_ua)
-			    == -1) {
+	newbr = ksr_push_branch(msg, &uri, &duri, &path, br->q, br->flags,
+					  br->force_send_socket, &instance, br->reg_id,
+					  &ruid, &location_ua);
+	if(newbr==NULL) {
 		LM_ERR("failed to append static branch\n");
 		LM_ERR("failed to append static branch\n");
 		return -1;
 		return -1;
 	}
 	}
+	newbr->otcpid = br->otcpid;
 	return 0;
 	return 0;
 }
 }
 
 

+ 0 - 1
src/modules/rtp_media_server/Makefile

@@ -9,5 +9,4 @@ BCUNITLIBS=-lbcunit
 MS2LIBS=-lmediastreamer
 MS2LIBS=-lmediastreamer
 
 
 LIBS=$(ORTPLIBS) $(BCUNITLIBS) $(MS2LIBS)
 LIBS=$(ORTPLIBS) $(BCUNITLIBS) $(MS2LIBS)
-DEFS+=-DKAMAILIO_MOD_INTERFACE
 include ../../Makefile.modules
 include ../../Makefile.modules

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 302 - 278
src/modules/textops/README


+ 40 - 0
src/modules/textops/doc/textops_admin.xml

@@ -90,6 +90,10 @@ From: medabeda
 			</para>
 			</para>
 		</listitem>
 		</listitem>
 		</itemizedlist>
 		</itemizedlist>
+		<para>
+			Note: it performs Posix regex matching and the 're' parameter
+			is compiled with the flags REG_EXTENDED|REG_ICASE|REG_NEWLINE.
+		</para>
 		<para>
 		<para>
 		This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
 		This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
 		FAILURE_ROUTE, BRANCH_ROUTE.
 		FAILURE_ROUTE, BRANCH_ROUTE.
@@ -128,6 +132,42 @@ if ( search("[Ss][Ii][Pp]") ) { /*....*/ };
 ...
 ...
 if ( search_body("[Ss][Ii][Pp]") ) { /*....*/ };
 if ( search_body("[Ss][Ii][Pp]") ) { /*....*/ };
 ...
 ...
+</programlisting>
+		</example>
+	</section>
+
+	<section id="textops.f.search_str">
+		<title>
+		<function moreinfo="none">search_str(text. re)</function>
+		</title>
+		<para>
+		Searches for the re in the body of the message.
+		</para>
+		<para>Meaning of the parameters is as follows:</para>
+		<itemizedlist>
+		<listitem>
+			<para><emphasis>text</emphasis> - text to perform regex searching
+			over it.
+			</para>
+		</listitem>
+		<listitem>
+			<para><emphasis>re</emphasis> - regular expression to match over
+			the 'text' parameter.
+			</para>
+		</listitem>
+		</itemizedlist>
+		<para>
+			Both parameters can contain variables.
+		</para>
+		<para>
+		This function can be used from ANY_ROUTE.
+		</para>
+		<example>
+		<title><function>search_str</function> usage</title>
+		<programlisting format="linespecific">
+...
+if ( search_str("$ru", ";transport=tcp") ) { /*....*/ };
+...
 </programlisting>
 </programlisting>
 		</example>
 		</example>
 	</section>
 	</section>

+ 64 - 0
src/modules/textops/textops.c

@@ -148,6 +148,7 @@ static int is_present_hf_re_pv_f(sip_msg_t* msg, char* key, char* foo);
 static int is_audio_on_hold_f(struct sip_msg *msg, char *str1, char *str2 );
 static int is_audio_on_hold_f(struct sip_msg *msg, char *str1, char *str2 );
 static int regex_substring_f(struct sip_msg *msg,  char *input, char *regex,
 static int regex_substring_f(struct sip_msg *msg,  char *input, char *regex,
 		char *matched_index, char *match_count, char *dst);
 		char *matched_index, char *match_count, char *dst);
+static int w_search_str(sip_msg_t *msg, char *ptext, char *pre);
 static int fixup_substre(void**, int);
 static int fixup_substre(void**, int);
 static int hname_fixup(void** param, int param_no);
 static int hname_fixup(void** param, int param_no);
 static int free_hname_fixup(void** param, int param_no);
 static int free_hname_fixup(void** param, int param_no);
@@ -314,6 +315,9 @@ static cmd_export_t cmds[]={
 	{"cmp_istr",  (cmd_function)cmp_istr_f, 2,
 	{"cmp_istr",  (cmd_function)cmp_istr_f, 2,
 		fixup_spve_spve, 0,
 		fixup_spve_spve, 0,
 		ANY_ROUTE},
 		ANY_ROUTE},
+	{"search_str",  (cmd_function)w_search_str, 2,
+		fixup_spve_spve, 0,
+		ANY_ROUTE},
 	{"starts_with",  (cmd_function)starts_with_f, 2,
 	{"starts_with",  (cmd_function)starts_with_f, 2,
 		fixup_spve_spve, 0,
 		fixup_spve_spve, 0,
 		ANY_ROUTE},
 		ANY_ROUTE},
@@ -4593,6 +4597,61 @@ static int fixup_subst_hf(void** param, int param_no)
 	return 0;
 	return 0;
 }
 }
 
 
+/**
+ *
+ */
+static int ki_search_str(sip_msg_t *msg, str *stext, str *sre)
+{
+	int ret;
+	regex_t re;
+	regmatch_t pmatch;
+
+
+	if(sre==NULL || sre->len<=0) {
+		return 2;
+	}
+
+	if(stext==NULL || stext->len<=0) {
+		return -2;
+	}
+
+	memset(&re, 0, sizeof(regex_t));
+	if (regcomp(&re, sre->s, REG_EXTENDED|REG_ICASE|REG_NEWLINE)!=0) {
+		LM_ERR("failed to compile regex: %.*s\n", sre->len, sre->s);
+		return -2;
+	}
+
+	if (regexec(&re, stext->s, 1, &pmatch, 0)!=0) {
+		ret = -1;
+	} else {
+		ret = 1;
+	}
+
+	regfree(&re);
+
+	return ret;
+}
+
+/**
+ *
+ */
+static int w_search_str(sip_msg_t *msg, char *ptext, char *pre)
+{
+	str stext;
+	str sre;
+
+	if(fixup_get_svalue(msg, (gparam_t*)ptext, &stext)!=0) {
+		LM_ERR("cannot get first parameter\n");
+		return -2;
+	}
+	if(fixup_get_svalue(msg, (gparam_t*)pre, &sre)!=0) {
+		LM_ERR("cannot get second parameter\n");
+		return -2;
+	}
+
+	return ki_search_str(msg, &stext, &sre);
+}
+
 /**
 /**
  *
  *
  */
  */
@@ -4945,6 +5004,11 @@ static sr_kemi_t sr_kemi_textops_exports[] = {
 		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
 		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	},
+	{ str_init("textops"), str_init("search_str"),
+		SR_KEMIP_INT, ki_search_str,
+		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
+			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
+	},
 	{ str_init("textops"), str_init("starts_with"),
 	{ str_init("textops"), str_init("starts_with"),
 		SR_KEMIP_INT, ki_starts_with,
 		SR_KEMIP_INT, ki_starts_with,
 		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
 		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,

+ 12 - 3
src/modules/topos/README

@@ -10,7 +10,13 @@ Daniel-Constantin Mierla
 
 
    <[email protected]>
    <[email protected]>
 
 
+Frederic Gaisnon
+
+   <[email protected]>
+
    Copyright © 2016 FhG FOKUS
    Copyright © 2016 FhG FOKUS
+
+   Copyright © 2021 MomentTech
      __________________________________________________________________
      __________________________________________________________________
 
 
    Table of Contents
    Table of Contents
@@ -112,8 +118,8 @@ Chapter 1. Admin Guide
    It also works for SIP MESSAGE or other requests that do not create a
    It also works for SIP MESSAGE or other requests that do not create a
    dialog -- record_route() must be used for them as well, the headers are
    dialog -- record_route() must be used for them as well, the headers are
    not going to be in the messages sent to the network, they are needed to
    not going to be in the messages sent to the network, they are needed to
-   know local addresses used to communicate with each side. At this moment
-   it is not designed to work for presence (SUBSCRIBE-based) dialogs. The
+   know local addresses used to communicate with each side. This module is
+   designed to work for presence (SUBSCRIBE-based) dialogs too. The
    REGISTER and PUBLISH requests are skipped from processing by this
    REGISTER and PUBLISH requests are skipped from processing by this
    module, expected to be terminated on a local SIP server.
    module, expected to be terminated on a local SIP server.
 
 
@@ -225,7 +231,10 @@ modparam("topos", "branch_expire", 300)
    mind that the module does not update the dialog timestamp after the
    mind that the module does not update the dialog timestamp after the
    initial call setup on re-INVITEs or other in-dialog messages. So set a
    initial call setup on re-INVITEs or other in-dialog messages. So set a
    large enough value (according your longest call duration) to prevent
    large enough value (according your longest call duration) to prevent
-   problems in re-writing messages.
+   problems in re-writing messages. This key is only relevant for INVITE
+   dialog. SUBSCRIBE dialog records lifetime are based on value set in
+   expires header. Moreover each re-SUBSCRIBEs update the dialog
+   timestamp.
 
 
    Default value is 10800 (3 hours).
    Default value is 10800 (3 hours).
 
 

+ 9 - 0
src/modules/topos/doc/topos.xml

@@ -23,11 +23,20 @@
 		<surname>Mierla</surname>
 		<surname>Mierla</surname>
 		<email>[email protected]</email>
 		<email>[email protected]</email>
 	    </editor>
 	    </editor>
+            <editor>
+                <firstname>Frederic</firstname>
+                <surname>Gaisnon</surname>
+                <email>[email protected]</email>
+            </editor>
 	</authorgroup>
 	</authorgroup>
 	<copyright>
 	<copyright>
 	    <year>2016</year>
 	    <year>2016</year>
 	    <holder>&fhg;</holder>
 	    <holder>&fhg;</holder>
 	</copyright>
 	</copyright>
+        <copyright>
+            <year>2021</year>
+            <holder>MomentTech</holder>
+        </copyright>
     </bookinfo>
     </bookinfo>
     <toc></toc>
     <toc></toc>
     
     

+ 5 - 2
src/modules/topos/doc/topos_admin.xml

@@ -30,8 +30,8 @@
 		a dialog -- record_route() must be used for them as well, the
 		a dialog -- record_route() must be used for them as well, the
 		headers are not going to be in the messages sent to the network, they
 		headers are not going to be in the messages sent to the network, they
 		are needed to know local addresses used to communicate with each side.
 		are needed to know local addresses used to communicate with each side.
-		At this moment it is not designed to work for presence (SUBSCRIBE-based)
-		dialogs. The REGISTER and PUBLISH requests are skipped from processing
+                This module is designed to work for presence (SUBSCRIBE-based) dialogs too.
+		The REGISTER and PUBLISH requests are skipped from processing
 		by this module, expected to be terminated on a local SIP server.
 		by this module, expected to be terminated on a local SIP server.
 	</para>
 	</para>
 	</section>
 	</section>
@@ -199,6 +199,9 @@ modparam("topos", "branch_expire", 300)
 			after the initial call setup on re-INVITEs or other in-dialog
 			after the initial call setup on re-INVITEs or other in-dialog
 			messages. So set a large enough value (according your longest call
 			messages. So set a large enough value (according your longest call
 			duration) to prevent problems in re-writing messages.
 			duration) to prevent problems in re-writing messages.
+			This key is only relevant for INVITE dialog. 
+                        SUBSCRIBE dialog records lifetime are based on value set in expires
+                        header. Moreover each re-SUBSCRIBEs update the dialog timestamp.
 		</para>
 		</para>
 		<para>
 		<para>
 		<emphasis>
 		<emphasis>

+ 5 - 0
src/modules/topos/tps_msg.c

@@ -905,6 +905,11 @@ int tps_request_received(sip_msg_t *msg, int dialog)
 				goto error;
 				goto error;
 			}
 			}
 		}
 		}
+		if((get_cseq(msg)->method_id)&(METHOD_SUBSCRIBE)) {
+			if(tps_storage_update_dialog(msg, &mtsd, &stsd, TPS_DBU_CONTACT|TPS_DBU_TIME)<0) {
+				goto error;
+			}
+		}
 	}
 	}
 	return 0;
 	return 0;
 
 

+ 33 - 8
src/modules/topos/tps_storage.c

@@ -41,6 +41,7 @@
 #include "../../core/parser/contact/parse_contact.h"
 #include "../../core/parser/contact/parse_contact.h"
 #include "../../core/parser/parse_from.h"
 #include "../../core/parser/parse_from.h"
 #include "../../core/parser/parse_to.h"
 #include "../../core/parser/parse_to.h"
+#include "../../core/parser/parse_expires.h"
 
 
 #include "../../lib/srdb1/db.h"
 #include "../../lib/srdb1/db.h"
 #include "../../core/utils/sruid.h"
 #include "../../core/utils/sruid.h"
@@ -336,8 +337,11 @@ int tps_storage_fill_contact(sip_msg_t *msg, tps_data_t *td, str *uuid, int dir,
 				td->cp += pv_val.rs.len;
 				td->cp += pv_val.rs.len;
 			}
 			}
 		}
 		}
-		*td->cp = '@';
-		td->cp++;
+
+		if (!((ctmode == 1) && (dir==TPS_DIR_DOWNSTREAM) && (curi.user.len <= 0))) {
+			*td->cp = '@';
+			td->cp++;
+		}
 
 
 		if (_tps_contact_host.len) { // using configured hostname in the contact header
 		if (_tps_contact_host.len) { // using configured hostname in the contact header
 			memcpy(td->cp, _tps_contact_host.s, _tps_contact_host.len);
 			memcpy(td->cp, _tps_contact_host.s, _tps_contact_host.len);
@@ -465,8 +469,8 @@ int tps_storage_link_msg(sip_msg_t *msg, tps_data_t *td, int dir)
 
 
 	/* extract the contact address */
 	/* extract the contact address */
 	if(parse_headers(msg, HDR_CONTACT_F, 0)<0 || msg->contact==NULL) {
 	if(parse_headers(msg, HDR_CONTACT_F, 0)<0 || msg->contact==NULL) {
-		if(td->s_method_id != METHOD_INVITE) {
-			/* no mandatory contact unless is INVITE - done */
+		if((td->s_method_id != METHOD_INVITE) && (td->s_method_id != METHOD_SUBSCRIBE)){
+			/* no mandatory contact unless is INVITE or SUBSCRIBE - done */
 			return 0;
 			return 0;
 		}
 		}
 		if(msg->first_line.type==SIP_REPLY) {
 		if(msg->first_line.type==SIP_REPLY) {
@@ -504,6 +508,13 @@ int tps_storage_link_msg(sip_msg_t *msg, tps_data_t *td, int dir)
 		}
 		}
 	}
 	}
 
 
+	if  (td->s_method_id == METHOD_SUBSCRIBE) {
+		if(msg->expires && (msg->expires->body.len > 0) && (msg->expires->parsed || (parse_expires(msg->expires) >= 0))) {
+			td->expires = ((exp_body_t *)msg->expires->parsed)->val;
+		}
+	}
+
+
 	LM_DBG("downstream: %s - acontact: [%.*s] - bcontact: [%.*s]\n",
 	LM_DBG("downstream: %s - acontact: [%.*s] - bcontact: [%.*s]\n",
 			(dir==TPS_DIR_DOWNSTREAM)?"yes":"no",
 			(dir==TPS_DIR_DOWNSTREAM)?"yes":"no",
 			td->a_contact.len, (td->a_contact.len>0)?td->a_contact.s:"",
 			td->a_contact.len, (td->a_contact.len>0)?td->a_contact.s:"",
@@ -1036,6 +1047,8 @@ int tps_db_load_branch(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd,
 	db_key_t db_cols[TPS_NR_KEYS];
 	db_key_t db_cols[TPS_NR_KEYS];
 	db1_res_t* db_res = NULL;
 	db1_res_t* db_res = NULL;
 	str sinv = str_init("INVITE");
 	str sinv = str_init("INVITE");
+	str ssub = str_init("SUBSCRIBE");
+	int bInviteDlg = 1;
 	int nr_keys;
 	int nr_keys;
 	int nr_cols;
 	int nr_cols;
 	int n;
 	int n;
@@ -1047,6 +1060,10 @@ int tps_db_load_branch(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd,
 	nr_keys = 0;
 	nr_keys = 0;
 	nr_cols = 0;
 	nr_cols = 0;
 
 
+	if((get_cseq(msg)->method_id == METHOD_SUBSCRIBE) || ((get_cseq(msg)->method_id == METHOD_NOTIFY) && (msg->event->len > 0))) {
+		bInviteDlg = 0;
+	}
+
 	if(mode==0) {
 	if(mode==0) {
 		/* load same transaction using Via branch */
 		/* load same transaction using Via branch */
 		db_keys[nr_keys]=&tt_col_x_vbranch;
 		db_keys[nr_keys]=&tt_col_x_vbranch;
@@ -1075,7 +1092,7 @@ int tps_db_load_branch(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd,
 		db_ops[nr_keys]=OP_EQ;
 		db_ops[nr_keys]=OP_EQ;
 		db_vals[nr_keys].type = DB1_STR;
 		db_vals[nr_keys].type = DB1_STR;
 		db_vals[nr_keys].nul = 0;
 		db_vals[nr_keys].nul = 0;
-		db_vals[nr_keys].val.str_val = sinv;
+		db_vals[nr_keys].val.str_val = bInviteDlg ? sinv : ssub;
 		nr_keys++;
 		nr_keys++;
 	}
 	}
 
 
@@ -1407,7 +1424,7 @@ int tps_storage_update_branch(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd,
 	if(msg==NULL || md==NULL || sd==NULL)
 	if(msg==NULL || md==NULL || sd==NULL)
 		return -1;
 		return -1;
 
 
-	if(md->s_method_id != METHOD_INVITE) {
+	if((md->s_method_id != METHOD_INVITE) && (md->s_method_id != METHOD_SUBSCRIBE)) {
 		return 0;
 		return 0;
 	}
 	}
 
 
@@ -1514,6 +1531,14 @@ int tps_db_update_dialog(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd,
 			}
 			}
 		}
 		}
 	}
 	}
+	if ((mode & TPS_DBU_TIME) && ((sd->b_tag.len > 0)
+			&& ((md->direction == TPS_DIR_UPSTREAM) && (msg->first_line.type==SIP_REQUEST))
+			&& (msg->first_line.u.request.method_value == METHOD_SUBSCRIBE))) {
+		db_ucols[nr_ucols] = &td_col_rectime;
+		db_uvals[nr_ucols].type = DB1_DATETIME;
+		db_uvals[nr_ucols].val.time_val = time(NULL);
+		nr_ucols++;
+	}
 
 
 	if(nr_ucols==0) {
 	if(nr_ucols==0) {
 		return 0;
 		return 0;
@@ -1543,7 +1568,7 @@ int tps_storage_update_dialog(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd,
 	if(msg==NULL || md==NULL || sd==NULL)
 	if(msg==NULL || md==NULL || sd==NULL)
 		return -1;
 		return -1;
 
 
-	if(md->s_method_id != METHOD_INVITE) {
+	if((md->s_method_id != METHOD_INVITE) && (md->s_method_id != METHOD_SUBSCRIBE)) {
 		return 0;
 		return 0;
 	}
 	}
 	if(msg->first_line.type==SIP_REPLY) {
 	if(msg->first_line.type==SIP_REPLY) {
@@ -1575,7 +1600,7 @@ int tps_db_end_dialog(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd)
 	if(msg==NULL || md==NULL || sd==NULL || _tps_db_handle==NULL)
 	if(msg==NULL || md==NULL || sd==NULL || _tps_db_handle==NULL)
 		return -1;
 		return -1;
 
 
-	if(md->s_method_id != METHOD_BYE) {
+	if((md->s_method_id != METHOD_BYE) && !((md->s_method_id == METHOD_SUBSCRIBE) && (md->expires == 0))) {
 		return 0;
 		return 0;
 	}
 	}
 
 

+ 2 - 0
src/modules/topos/tps_storage.h

@@ -41,6 +41,7 @@
 #define TPS_DBU_RPLATTRS	(1<<1)
 #define TPS_DBU_RPLATTRS	(1<<1)
 #define TPS_DBU_ARR		(1<<2)
 #define TPS_DBU_ARR		(1<<2)
 #define TPS_DBU_BRR		(1<<3)
 #define TPS_DBU_BRR		(1<<3)
+#define TPS_DBU_TIME		(1<<4)
 #define TPS_DBU_ALL		(0xffffffff)
 #define TPS_DBU_ALL		(0xffffffff)
 
 
 #define TPS_DATA_SIZE	8192
 #define TPS_DATA_SIZE	8192
@@ -79,6 +80,7 @@ typedef struct tps_data {
 	int32_t iflags;
 	int32_t iflags;
 	int32_t direction;
 	int32_t direction;
 	uint32_t s_method_id;
 	uint32_t s_method_id;
+	int32_t expires;
 } tps_data_t;
 } tps_data_t;
 
 
 int tps_storage_dialog_find(sip_msg_t *msg, tps_data_t *td);
 int tps_storage_dialog_find(sip_msg_t *msg, tps_data_t *td);

+ 6 - 0
src/modules/topos_redis/README

@@ -10,9 +10,15 @@ Daniel-Constantin Mierla
 
 
    <[email protected]>
    <[email protected]>
 
 
+Frederic Gaisnon
+
+   <[email protected]>
+
    Copyright © 2017 kamailio.org
    Copyright © 2017 kamailio.org
 
 
    Copyright © 2017 flowroute.com
    Copyright © 2017 flowroute.com
+
+   Copyright © 2021 MomentTech
      __________________________________________________________________
      __________________________________________________________________
 
 
    Table of Contents
    Table of Contents

+ 9 - 0
src/modules/topos_redis/doc/topos_redis.xml

@@ -23,6 +23,11 @@
 		<surname>Mierla</surname>
 		<surname>Mierla</surname>
 		<email>[email protected]</email>
 		<email>[email protected]</email>
 	    </editor>
 	    </editor>
+	    <editor>
+		<firstname>Frederic</firstname>
+		<surname>Gaisnon</surname>
+		<email>[email protected]</email>
+	    </editor>
 	</authorgroup>
 	</authorgroup>
 	<copyright>
 	<copyright>
 	    <year>2017</year>
 	    <year>2017</year>
@@ -32,6 +37,10 @@
 	    <year>2017</year>
 	    <year>2017</year>
 	    <holder>flowroute.com</holder>
 	    <holder>flowroute.com</holder>
 	</copyright>
 	</copyright>
+	<copyright>
+	    <year>2021</year>
+	    <holder>MomentTech</holder>
+	</copyright>
     </bookinfo>
     </bookinfo>
     <toc></toc>
     <toc></toc>
 
 

+ 63 - 12
src/modules/topos_redis/topos_redis_storage.c

@@ -265,7 +265,12 @@ int tps_redis_insert_dialog(tps_data_t *td)
 	argvlen[argc] = rkey.len;
 	argvlen[argc] = rkey.len;
 	argc++;
 	argc++;
 
 
-	lval = (unsigned long)_tps_api.get_dialog_expire();
+	if(td->s_method.len==9 && strncmp(td->s_method.s, "SUBSCRIBE", 9)==0) {
+		lval = (unsigned long)td->expires;
+	} else {
+		lval = (unsigned long)_tps_api.get_dialog_expire();
+  }
+
 	if(lval==0) {
 	if(lval==0) {
 		return 0;
 		return 0;
 	}
 	}
@@ -297,7 +302,7 @@ int tps_redis_clean_dialogs(void)
 /**
 /**
  *
  *
  */
  */
-int tps_redis_insert_invite_branch(tps_data_t *td)
+int tps_redis_insert_initial_method_branch(tps_data_t *td)
 {
 {
 	char* argv[TPS_REDIS_NR_KEYS];
 	char* argv[TPS_REDIS_NR_KEYS];
 	size_t argvlen[TPS_REDIS_NR_KEYS];
 	size_t argvlen[TPS_REDIS_NR_KEYS];
@@ -328,8 +333,9 @@ int tps_redis_insert_invite_branch(tps_data_t *td)
 
 
 	rp = _tps_redis_cbuf;
 	rp = _tps_redis_cbuf;
 	rkey.len = snprintf(rp, TPS_REDIS_DATA_SIZE-128,
 	rkey.len = snprintf(rp, TPS_REDIS_DATA_SIZE-128,
-					"%.*sINVITE:%.*s:%.*s",
+					"%.*s%.*s:%.*s:%.*s",
 					_tps_redis_bprefix.len, _tps_redis_bprefix.s,
 					_tps_redis_bprefix.len, _tps_redis_bprefix.s,
+					td->s_method.len, td->s_method.s,
 					td->a_callid.len, td->a_callid.s,
 					td->a_callid.len, td->a_callid.s,
 					td->b_tag.len, td->b_tag.s);
 					td->b_tag.len, td->b_tag.s);
 	if(rkey.len<0 || rkey.len>=TPS_REDIS_DATA_SIZE-128) {
 	if(rkey.len<0 || rkey.len>=TPS_REDIS_DATA_SIZE-128) {
@@ -360,8 +366,8 @@ int tps_redis_insert_invite_branch(tps_data_t *td)
 		}
 		}
 		return -1;
 		return -1;
 	}
 	}
-	LM_DBG("inserting invite branch record for [%.*s] with argc %d\n",
-			rkey.len, rkey.s, argc);
+	LM_DBG("inserting %.*s branch record for [%.*s] with argc %d\n",
+			td->s_method.len, td->s_method.s,rkey.len, rkey.s, argc);
 
 
 	freeReplyObject(rrpl);
 	freeReplyObject(rrpl);
 
 
@@ -552,7 +558,7 @@ int tps_redis_clean_branches(void)
 /**
 /**
  *
  *
  */
  */
-int tps_redis_load_invite_branch(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd)
+int tps_redis_load_initial_method_branch(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd)
 {
 {
 	char* argv[TPS_REDIS_NR_KEYS];
 	char* argv[TPS_REDIS_NR_KEYS];
 	size_t argvlen[TPS_REDIS_NR_KEYS];
 	size_t argvlen[TPS_REDIS_NR_KEYS];
@@ -588,8 +594,9 @@ int tps_redis_load_invite_branch(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd)
 	rp = _tps_redis_cbuf;
 	rp = _tps_redis_cbuf;
 
 
 	rkey.len = snprintf(rp, TPS_REDIS_DATA_SIZE,
 	rkey.len = snprintf(rp, TPS_REDIS_DATA_SIZE,
-					"%.*sINVITE:%.*s:%.*s",
+					"%.*s%.*s:%.*s:%.*s",
 					_tps_redis_bprefix.len, _tps_redis_bprefix.s,
 					_tps_redis_bprefix.len, _tps_redis_bprefix.s,
+					md->s_method.len, md->s_method.s,
 					md->a_callid.len, md->a_callid.s,
 					md->a_callid.len, md->a_callid.s,
 					md->b_tag.len, md->b_tag.s);
 					md->b_tag.len, md->b_tag.s);
 	if(rkey.len<0 || rkey.len>=TPS_REDIS_DATA_SIZE) {
 	if(rkey.len<0 || rkey.len>=TPS_REDIS_DATA_SIZE) {
@@ -733,9 +740,9 @@ int tps_redis_load_branch(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd,
 		/* load same transaction using Via branch */
 		/* load same transaction using Via branch */
 		xvbranch1 = &md->x_vbranch1;
 		xvbranch1 = &md->x_vbranch1;
 	} else {
 	} else {
-		/* load corresponding INVITE transaction using call-id + to-tag */
-		if(tps_redis_load_invite_branch(msg, md, &id)<0) {
-			LM_ERR("failed to load the INVITE branch value\n");
+		/* load corresponding INVITE or SUBSCRIBE transaction using call-id + to-tag */
+		if(tps_redis_load_initial_method_branch(msg, md, &id)<0) {
+			LM_ERR("failed to load the %.*s branch value\n", md->s_method.len, md->s_method.s);
 			return -1;
 			return -1;
 		}
 		}
 		xvbranch1 = &id.x_vbranch1;
 		xvbranch1 = &id.x_vbranch1;
@@ -1122,11 +1129,18 @@ int tps_redis_update_branch(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd,
 	}
 	}
 
 
 	if(md->s_method.len==6 && strncmp(md->s_method.s, "INVITE", 6)==0) {
 	if(md->s_method.len==6 && strncmp(md->s_method.s, "INVITE", 6)==0) {
-		if(tps_redis_insert_invite_branch(md)<0) {
+		if(tps_redis_insert_initial_method_branch(md)<0) {
 			LM_ERR("failed to insert INVITE extra branch data\n");
 			LM_ERR("failed to insert INVITE extra branch data\n");
 			return -1;
 			return -1;
 		}
 		}
 	}
 	}
+	if(md->s_method.len==9 && strncmp(md->s_method.s, "SUBSCRIBE", 9)==0) {
+		if(tps_redis_insert_initial_method_branch(md)<0) {
+			LM_ERR("failed to insert SUBSCRIBE extra branch data\n");
+			return -1;
+		}
+	}
+
 	rsrv = _tps_redis_api.get_server(&_topos_redis_serverid);
 	rsrv = _tps_redis_api.get_server(&_topos_redis_serverid);
 	if(rsrv==NULL) {
 	if(rsrv==NULL) {
 		LM_ERR("cannot find redis server [%.*s]\n",
 		LM_ERR("cannot find redis server [%.*s]\n",
@@ -1209,6 +1223,7 @@ int tps_redis_update_dialog(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd,
 	redisc_server_t *rsrv = NULL;
 	redisc_server_t *rsrv = NULL;
 	redisReply *rrpl = NULL;
 	redisReply *rrpl = NULL;
 	int32_t liflags;
 	int32_t liflags;
+	unsigned long lval = 0;
 
 
 	if(sd->a_uuid.len<=0 && sd->b_uuid.len<=0) {
 	if(sd->a_uuid.len<=0 && sd->b_uuid.len<=0) {
 		LM_INFO("no uuid for this message\n");
 		LM_INFO("no uuid for this message\n");
@@ -1297,6 +1312,11 @@ int tps_redis_update_dialog(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd,
 		}
 		}
 	}
 	}
 
 
+	if (mode & TPS_DBU_TIME) {
+		lval = (unsigned long)time(NULL);
+		TPS_REDIS_SET_ARGN(lval, rp, &rval, argc, &td_key_rectime, argv, argvlen);
+	}
+
 	if(argc<=2) {
 	if(argc<=2) {
 		return 0;
 		return 0;
 	}
 	}
@@ -1313,6 +1333,37 @@ int tps_redis_update_dialog(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd,
 			rkey.len, rkey.s, argc);
 			rkey.len, rkey.s, argc);
 	freeReplyObject(rrpl);
 	freeReplyObject(rrpl);
 
 
+	if (mode & TPS_DBU_TIME) {
+		/* reset expire for the key */
+		argc = 0;
+
+		argv[argc]    = "EXPIRE";
+		argvlen[argc] = 6;
+		argc++;
+
+		argv[argc]    = rkey.s;
+		argvlen[argc] = rkey.len;
+		argc++;
+
+		lval = (unsigned long)md->expires;
+		if(lval==0) {
+			return 0;
+		}
+		TPS_REDIS_SET_ARGNV(lval, rp, &rval, argc, argv, argvlen);
+
+		rrpl = _tps_redis_api.exec_argv(rsrv, argc, (const char **)argv, argvlen);
+		if(rrpl==NULL) {
+			LM_ERR("failed to execute expire redis command\n");
+			if(rsrv->ctxRedis->err) {
+				LM_ERR("redis error: %s\n", rsrv->ctxRedis->errstr);
+			}
+			return -1;
+		}
+		LM_DBG("expire %lu set on dialog record for [%.*s] with argc %d\n", lval,
+			rkey.len, rkey.s, argc);
+		freeReplyObject(rrpl);
+	}
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1333,7 +1384,7 @@ int tps_redis_end_dialog(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd)
 	int32_t liflags;
 	int32_t liflags;
 	unsigned long lval = 0;
 	unsigned long lval = 0;
 
 
-	if(md->s_method_id != METHOD_BYE) {
+	if((md->s_method_id != METHOD_BYE) && !((md->s_method_id == METHOD_SUBSCRIBE) && (md->expires == 0))) {
 		return 0;
 		return 0;
 	}
 	}
 
 

+ 27 - 9
src/modules/uac_redirect/README

@@ -31,6 +31,7 @@ Bogdan-Andrei Iancu
               4.5. acc_db_table (string)
               4.5. acc_db_table (string)
               4.6. bflags (int)
               4.6. bflags (int)
               4.7. flags_hdr_mode (int)
               4.7. flags_hdr_mode (int)
+              4.8. q_value (int)
 
 
         5. Functions
         5. Functions
 
 
@@ -50,11 +51,12 @@ Bogdan-Andrei Iancu
    1.5. Set acc_db_table parameter
    1.5. Set acc_db_table parameter
    1.6. Set bflags module parameter
    1.6. Set bflags module parameter
    1.7. Set flags_hdr_mode parameter
    1.7. Set flags_hdr_mode parameter
-   1.8. set_deny_filter usage
-   1.9. set_accept_filter usage
-   1.10. get_redirects usage
+   1.8. Set q_value parameter
+   1.9. set_deny_filter usage
+   1.10. set_accept_filter usage
    1.11. get_redirects usage
    1.11. get_redirects usage
-   1.12. Redirection script example
+   1.12. get_redirects usage
+   1.13. Redirection script example
 
 
 Chapter 1. Admin Guide
 Chapter 1. Admin Guide
 
 
@@ -76,6 +78,7 @@ Chapter 1. Admin Guide
         4.5. acc_db_table (string)
         4.5. acc_db_table (string)
         4.6. bflags (int)
         4.6. bflags (int)
         4.7. flags_hdr_mode (int)
         4.7. flags_hdr_mode (int)
+        4.8. q_value (int)
 
 
    5. Functions
    5. Functions
 
 
@@ -149,6 +152,7 @@ Chapter 1. Admin Guide
    4.5. acc_db_table (string)
    4.5. acc_db_table (string)
    4.6. bflags (int)
    4.6. bflags (int)
    4.7. flags_hdr_mode (int)
    4.7. flags_hdr_mode (int)
+   4.8. q_value (int)
 
 
 4.1. default_filter (string)
 4.1. default_filter (string)
 
 
@@ -273,6 +277,20 @@ branch_route[1] {
 modparam("uac_redirect","flags_hdr_mode",2)
 modparam("uac_redirect","flags_hdr_mode",2)
 ...
 ...
 
 
+4.8. q_value (int)
+
+   Specifies the q-value to asign to contacts without one. Because
+   Kamailio doesn't support float parameter types, the value in the
+   parameter is divided by 1000 and stored as float. For example, if you
+   want q value to be 0.38, use value 380 here.
+
+   The default value is 10 (0.01).
+
+   Example 1.8. Set q_value parameter
+...
+modparam("uac_redirect","q_value",0)
+...
+
 5. Functions
 5. Functions
 
 
    5.1. set_deny_filter(filter,flags)
    5.1. set_deny_filter(filter,flags)
@@ -295,7 +313,7 @@ modparam("uac_redirect","flags_hdr_mode",2)
 
 
    This function can be used from FAILURE_ROUTE.
    This function can be used from FAILURE_ROUTE.
 
 
-   Example 1.8. set_deny_filter usage
+   Example 1.9. set_deny_filter usage
 ...
 ...
 set_deny_filter(".*@domain2.net","reset_all");
 set_deny_filter(".*@domain2.net","reset_all");
 set_deny_filter(".*@domain1.net","");
 set_deny_filter(".*@domain1.net","");
@@ -316,7 +334,7 @@ set_deny_filter(".*@domain1.net","");
 
 
    This function can be used from FAILURE_ROUTE.
    This function can be used from FAILURE_ROUTE.
 
 
-   Example 1.9. set_accept_filter usage
+   Example 1.10. set_accept_filter usage
 ...
 ...
 set_accept_filter(".*@domain2.net","reset_added");
 set_accept_filter(".*@domain2.net","reset_added");
 set_accept_filter(".*@domain1.net","");
 set_accept_filter(".*@domain1.net","");
@@ -345,7 +363,7 @@ set_accept_filter(".*@domain1.net","");
 
 
    This function can be used from FAILURE_ROUTE.
    This function can be used from FAILURE_ROUTE.
 
 
-   Example 1.10. get_redirects usage
+   Example 1.11. get_redirects usage
 ...
 ...
 # max 2 contacts per branch, but no overall limit
 # max 2 contacts per branch, but no overall limit
 get_redirects("*:2");
 get_redirects("*:2");
@@ -369,14 +387,14 @@ get_redirects("*");
 
 
    This function can be used from FAILURE_ROUTE.
    This function can be used from FAILURE_ROUTE.
 
 
-   Example 1.11. get_redirects usage
+   Example 1.12. get_redirects usage
 ...
 ...
 get_redirects("4:1","Redirected");
 get_redirects("4:1","Redirected");
 ...
 ...
 
 
 6. Script Example
 6. Script Example
 
 
-   Example 1.12. Redirection script example
+   Example 1.13. Redirection script example
 loadmodule "modules/sl/sl.so"
 loadmodule "modules/sl/sl.so"
 loadmodule "modules/usrloc/usrloc.so"
 loadmodule "modules/usrloc/usrloc.so"
 loadmodule "modules/registrar/registrar.so"
 loadmodule "modules/registrar/registrar.so"

+ 22 - 0
src/modules/uac_redirect/doc/uac_redirect_admin.xml

@@ -337,6 +337,28 @@ branch_route[1] {
 				<programlisting format="linespecific">
 				<programlisting format="linespecific">
 ...
 ...
 modparam("uac_redirect","flags_hdr_mode",2)
 modparam("uac_redirect","flags_hdr_mode",2)
+...
+				</programlisting>
+			</example>
+		</section>
+		<section>
+			<title><varname>q_value</varname> (int)</title>
+			<para>
+			Specifies the q-value to asign to contacts without one. Because
+			Kamailio doesn't support float parameter types, the value in the
+			parameter is divided by 1000 and stored as float. For example, if
+			you want q value to be 0.38, use value 380 here.
+			</para>
+			<para>
+				<emphasis>
+					The default value is 10 (0.01).
+				</emphasis>
+			</para>
+			<example>
+				<title>Set <varname>q_value</varname> parameter</title>
+				<programlisting format="linespecific">
+...
+modparam("uac_redirect","q_value",0)
 ...
 ...
 				</programlisting>
 				</programlisting>
 			</example>
 			</example>

+ 2 - 2
src/modules/uac_redirect/rd_funcs.c

@@ -33,9 +33,9 @@
 
 
 
 
 extern sruid_t _redirect_sruid;
 extern sruid_t _redirect_sruid;
+extern int _redirect_q_value;
 
 
 #define MAX_CONTACTS_PER_REPLY   16
 #define MAX_CONTACTS_PER_REPLY   16
-#define DEFAULT_Q_VALUE          10
 
 
 static int shmcontact2dset(struct sip_msg *req, struct sip_msg *shrpl,
 static int shmcontact2dset(struct sip_msg *req, struct sip_msg *shrpl,
 		long max, struct acc_param *reason, unsigned int bflags);
 		long max, struct acc_param *reason, unsigned int bflags);
@@ -147,7 +147,7 @@ static int sort_contacts(hdr_field_t *chdr, contact_t **ct_array,
 			/* does the contact has a q val? */
 			/* does the contact has a q val? */
 			q_para = ct_list->q;
 			q_para = ct_list->q;
 			if (q_para==0 || q_para->body.len==0) {
 			if (q_para==0 || q_para->body.len==0) {
-				q = DEFAULT_Q_VALUE;
+				q = _redirect_q_value;
 			} else {
 			} else {
 				if (str2q( &q, q_para->body.s, q_para->body.len)!=0) {
 				if (str2q( &q, q_para->body.s, q_para->body.len)!=0) {
 					LM_ERR("invalid q param\n");
 					LM_ERR("invalid q param\n");

+ 4 - 0
src/modules/uac_redirect/uac_redirect.c

@@ -52,6 +52,9 @@ int flags_hdr_mode = 0;
 
 
 #define ACCEPT_RULE_STR "accept"
 #define ACCEPT_RULE_STR "accept"
 #define DENY_RULE_STR   "deny"
 #define DENY_RULE_STR   "deny"
+#define DEFAULT_Q_VALUE 10
+
+int _redirect_q_value = DEFAULT_Q_VALUE;
 
 
 /* sruid to get internal uid */
 /* sruid to get internal uid */
 sruid_t _redirect_sruid;
 sruid_t _redirect_sruid;
@@ -88,6 +91,7 @@ static param_export_t params[] = {
 	{"acc_db_table",    PARAM_STRING,  &acc_db_table     },
 	{"acc_db_table",    PARAM_STRING,  &acc_db_table     },
 	{"bflags",    		INT_PARAM,  &bflags			  },
 	{"bflags",    		INT_PARAM,  &bflags			  },
 	{"flags_hdr_mode",	INT_PARAM,  &flags_hdr_mode	  },
 	{"flags_hdr_mode",	INT_PARAM,  &flags_hdr_mode	  },
+	{"q_value",         INT_PARAM,  &_redirect_q_value   },
 	{0, 0, 0}
 	{0, 0, 0}
 };
 };
 
 

+ 4 - 4
src/modules/usrloc/ul_rpc.c

@@ -402,7 +402,7 @@ static void ul_rpc_lookup(rpc_t* rpc, void* ctx)
 	/* look for table */
 	/* look for table */
 	dom = rpc_find_domain( &table );
 	dom = rpc_find_domain( &table );
 	if (dom == NULL) {
 	if (dom == NULL) {
-		rpc->fault(ctx, 500, "Domain not found");
+		rpc->fault(ctx, 500, "Domain table not found");
 		return;
 		return;
 	}
 	}
 
 
@@ -476,7 +476,7 @@ static void ul_rpc_rm_aor(rpc_t* rpc, void* ctx)
 	/* look for table */
 	/* look for table */
 	dom = rpc_find_domain( &table );
 	dom = rpc_find_domain( &table );
 	if (dom == NULL) {
 	if (dom == NULL) {
-		rpc->fault(ctx, 500, "Domain not found");
+		rpc->fault(ctx, 500, "Domain table not found");
 		return;
 		return;
 	}
 	}
 
 
@@ -520,7 +520,7 @@ static void ul_rpc_rm_contact(rpc_t* rpc, void* ctx)
 	/* look for table */
 	/* look for table */
 	dom = rpc_find_domain( &table );
 	dom = rpc_find_domain( &table );
 	if (dom == NULL) {
 	if (dom == NULL) {
-		rpc->fault(ctx, 500, "Domain not found");
+		rpc->fault(ctx, 500, "Domain table not found");
 		return;
 		return;
 	}
 	}
 
 
@@ -661,7 +661,7 @@ static void ul_rpc_add(rpc_t* rpc, void* ctx)
 	/* look for table */
 	/* look for table */
 	dom = rpc_find_domain( &table );
 	dom = rpc_find_domain( &table );
 	if (dom == NULL) {
 	if (dom == NULL) {
-		rpc->fault(ctx, 500, "Domain not found");
+		rpc->fault(ctx, 500, "Domain table not found");
 		return;
 		return;
 	}
 	}
 
 

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels