Răsfoiți Sursa

Merge remote-tracking branch 'origin/master'

mojtaba 4 ani în urmă
părinte
comite
e6bad5f9b4

+ 2 - 2
etc/kamailio.cfg

@@ -851,9 +851,9 @@ route[NATMANAGE] {
 
 
 #!ifdef WITH_RTPENGINE
 #!ifdef WITH_RTPENGINE
 	if(nat_uac_test("8")) {
 	if(nat_uac_test("8")) {
-		rtpengine_manage("replace-origin replace-session-connection");
+		rtpengine_manage("SIP-source-address replace-origin replace-session-connection");
 	} else {
 	} else {
-		rtpengine_manage("trust-address replace-origin replace-session-connection");
+		rtpengine_manage("replace-origin replace-session-connection");
 	}
 	}
 #!else
 #!else
 	if(nat_uac_test("8")) {
 	if(nat_uac_test("8")) {

+ 7 - 0
src/Makefile.groups

@@ -184,6 +184,9 @@ mod_list_uuid=uuid
 # - modules depending on ev library
 # - modules depending on ev library
 mod_list_ev=evapi
 mod_list_ev=evapi
 
 
+# - modules depending on libjwt library
+mod_list_jwt=jwt
+
 # - modules depending on kazoo/rabbitmq
 # - modules depending on kazoo/rabbitmq
 mod_list_kazoo=kazoo
 mod_list_kazoo=kazoo
 
 
@@ -256,6 +259,7 @@ mod_list_all=$(sort $(mod_list_basic) $(mod_list_extra) \
 			   $(mod_list_kafka) \
 			   $(mod_list_kafka) \
 			   $(mod_list_mqtt) \
 			   $(mod_list_mqtt) \
 			   $(mod_list_secsipid) \
 			   $(mod_list_secsipid) \
+			   $(mod_list_jwt) \
 			   $(mod_list_rtp_media_server)
 			   $(mod_list_rtp_media_server)
 
 
 
 
@@ -446,6 +450,9 @@ module_group_kuuid=$(mod_list_uuid)
 # pkg libev modules
 # pkg libev modules
 module_group_kev=$(mod_list_ev)
 module_group_kev=$(mod_list_ev)
 
 
+# pkg jwt module
+module_group_kjwt=$(mod_list_jwt)
+
 # pkg kazoo module
 # pkg kazoo module
 module_group_kkazoo=$(mod_list_kazoo)
 module_group_kkazoo=$(mod_list_kazoo)
 
 

+ 3 - 3
src/core/tcp_init.h

@@ -13,8 +13,8 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  * 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 
+ * 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
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  */
  */
 
 
@@ -54,7 +54,7 @@ struct tcp_child{
 int init_tcp(void);
 int init_tcp(void);
 void destroy_tcp(void);
 void destroy_tcp(void);
 int tcp_init(struct socket_info* sock_info);
 int tcp_init(struct socket_info* sock_info);
-int tcp_init_children(void);
+int tcp_init_children(int *woneinit);
 void tcp_main_loop(void);
 void tcp_main_loop(void);
 void tcp_receive_loop(int unix_sock);
 void tcp_receive_loop(int unix_sock);
 int tcp_fix_child_sockets(int* fd);
 int tcp_fix_child_sockets(int* fd);

+ 12 - 6
src/core/tcp_main.c

@@ -5034,16 +5034,16 @@ int tcp_fix_child_sockets(int* fd)
 
 
 
 
 /* starts the tcp processes */
 /* starts the tcp processes */
-int tcp_init_children()
+int tcp_init_children(int *woneinit)
 {
 {
 	int r, i;
 	int r, i;
 	int reader_fd_1; /* for comm. with the tcp children read  */
 	int reader_fd_1; /* for comm. with the tcp children read  */
 	pid_t pid;
 	pid_t pid;
 	char si_desc[MAX_PT_DESC];
 	char si_desc[MAX_PT_DESC];
 	struct socket_info *si;
 	struct socket_info *si;
-	
+
 	/* estimate max fd. no:
 	/* estimate max fd. no:
-	 * 1 tcp send unix socket/all_proc, 
+	 * 1 tcp send unix socket/all_proc,
 	 *  + 1 udp sock/udp proc + 1 tcp_child sock/tcp child*
 	 *  + 1 udp sock/udp proc + 1 tcp_child sock/tcp child*
 	 *  + no_listen_tcp */
 	 *  + no_listen_tcp */
 	for(r=0, si=tcp_listen; si; si=si->next, r++);
 	for(r=0, si=tcp_listen; si; si=si->next, r++);
@@ -5051,12 +5051,12 @@ int tcp_init_children()
 	if (! tls_disable)
 	if (! tls_disable)
 		for (si=tls_listen; si; si=si->next, r++);
 		for (si=tls_listen; si; si=si->next, r++);
 #endif
 #endif
-	
+
 	register_fds(r+tcp_max_connections+get_max_procs()-1 /* tcp main */);
 	register_fds(r+tcp_max_connections+get_max_procs()-1 /* tcp main */);
 #if 0
 #if 0
 	tcp_max_fd_no=get_max_procs()*2 +r-1 /* timer */ +3; /* stdin/out/err*/
 	tcp_max_fd_no=get_max_procs()*2 +r-1 /* timer */ +3; /* stdin/out/err*/
 	/* max connections can be temporarily exceeded with estimated_process_count
 	/* max connections can be temporarily exceeded with estimated_process_count
-	 * - tcp_main (tcpconn_connect called simultaneously in all all the 
+	 * - tcp_main (tcpconn_connect called simultaneously in all all the
 	 *  processes) */
 	 *  processes) */
 	tcp_max_fd_no+=tcp_max_connections+get_max_procs()-1 /* tcp main */;
 	tcp_max_fd_no+=tcp_max_connections+get_max_procs()-1 /* tcp main */;
 #endif
 #endif
@@ -5095,7 +5095,7 @@ int tcp_init_children()
 
 
 	/* create the tcp sock_info structures */
 	/* create the tcp sock_info structures */
 	/* copy the sockets --moved to main_loop*/
 	/* copy the sockets --moved to main_loop*/
-	
+
 	/* fork children & create the socket pairs*/
 	/* fork children & create the socket pairs*/
 	for(r=0; r<tcp_children_no; r++){
 	for(r=0; r<tcp_children_no; r++){
 		child_rank++;
 		child_rank++;
@@ -5108,10 +5108,16 @@ int tcp_init_children()
 			goto error;
 			goto error;
 		}else if (pid>0){
 		}else if (pid>0){
 			/* parent */
 			/* parent */
+			*woneinit = 1;
 		}else{
 		}else{
 			/* child */
 			/* child */
 			bind_address=0; /* force a SEGFAULT if someone uses a non-init.
 			bind_address=0; /* force a SEGFAULT if someone uses a non-init.
 							   bind address on tcp */
 							   bind address on tcp */
+			if(*woneinit==0) {
+				if(run_child_one_init_route()<0)
+					goto error;
+			}
+
 			tcp_receive_loop(reader_fd_1);
 			tcp_receive_loop(reader_fd_1);
 		}
 		}
 	}
 	}

+ 7 - 1
src/main.c

@@ -1720,8 +1720,14 @@ int main_loop(void)
 						/* child */
 						/* child */
 						bind_address=si; /* shortcut */
 						bind_address=si; /* shortcut */
 
 
+						if(woneinit==0) {
+							if(run_child_one_init_route()<0)
+								goto error;
+						}
+
 						return sctp_core_rcv_loop();
 						return sctp_core_rcv_loop();
 					}
 					}
+					woneinit = 1;
 				}
 				}
 			/*parent*/
 			/*parent*/
 			/*close(sctp_sock)*/; /*if closed=>sendto invalid fd errors?*/
 			/*close(sctp_sock)*/; /*if closed=>sendto invalid fd errors?*/
@@ -1777,7 +1783,7 @@ int main_loop(void)
 #ifdef USE_TCP
 #ifdef USE_TCP
 		if (!tcp_disable){
 		if (!tcp_disable){
 				/* start tcp  & tls receivers */
 				/* start tcp  & tls receivers */
-			if (tcp_init_children()<0) goto error;
+			if (tcp_init_children(&woneinit)<0) goto error;
 				/* start tcp+tls main attendant proc */
 				/* start tcp+tls main attendant proc */
 			pid = fork_process(PROC_TCP_MAIN, "tcp main process", 0);
 			pid = fork_process(PROC_TCP_MAIN, "tcp main process", 0);
 			if (pid<0){
 			if (pid<0){

+ 1 - 0
src/modules/carrierroute/cr_data.c

@@ -464,6 +464,7 @@ int add_route(struct route_data_t * rd, int carrier_id,
 		LM_ERR("could not retrieve domain data\n");
 		LM_ERR("could not retrieve domain data\n");
 		return -1;
 		return -1;
 	}
 	}
+	domain_data->sum_prob = domain_data->sum_prob + prob;
 
 
 	LM_INFO("found carrier and domain, now adding route\n");
 	LM_INFO("found carrier and domain, now adding route\n");
 	return add_route_to_tree(domain_data->tree, scan_prefix, flags, mask, scan_prefix, max_targets, prob, rewrite_hostpart,
 	return add_route_to_tree(domain_data->tree, scan_prefix, flags, mask, scan_prefix, max_targets, prob, rewrite_hostpart,

+ 13 - 34
src/modules/carrierroute/cr_db.c

@@ -31,6 +31,7 @@
 #include "carrierroute.h"
 #include "carrierroute.h"
 #include "cr_db.h"
 #include "cr_db.h"
 #include "cr_carrier.h"
 #include "cr_carrier.h"
+#include "cr_domain.h"
 #include "config.h"
 #include "config.h"
 #include <stdio.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdlib.h>
@@ -265,9 +266,8 @@ int load_user_carrier(str * user, str * domain) {
  */
  */
 int load_route_data_db(struct route_data_t * rd) {
 int load_route_data_db(struct route_data_t * rd) {
 	db1_res_t * res = NULL;
 	db1_res_t * res = NULL;
-	db1_res_t * prob_res = NULL;
 	db_row_t * row = NULL;
 	db_row_t * row = NULL;
-	int i, ret;
+	int i, j, ret;
 	struct carrier_data_t * tmp_carrier_data;
 	struct carrier_data_t * tmp_carrier_data;
 	static str query_str;
 	static str query_str;
 	str tmp_scan_prefix, tmp_rewrite_host, tmp_rewrite_prefix,
 	str tmp_scan_prefix, tmp_rewrite_host, tmp_rewrite_prefix,
@@ -353,7 +353,6 @@ int load_route_data_db(struct route_data_t * rd) {
 		}
 		}
 	}
 	}
 	int n = 0;
 	int n = 0;
-	crboolean query_done = crfalse;
 	do {
 	do {
 		LM_DBG("loading, cycle %d", n++);
 		LM_DBG("loading, cycle %d", n++);
 		for (i = 0; i < RES_ROW_N(res); ++i) {
 		for (i = 0; i < RES_ROW_N(res); ++i) {
@@ -379,6 +378,7 @@ int load_route_data_db(struct route_data_t * rd) {
 				p_tmp_comment = &tmp_comment;
 				p_tmp_comment = &tmp_comment;
 			}
 			}
 
 
+
 			if (add_route(rd,
 			if (add_route(rd,
 					row->values[COL_CARRIER].val.int_val,
 					row->values[COL_CARRIER].val.int_val,
 					row->values[COL_DOMAIN].val.int_val,
 					row->values[COL_DOMAIN].val.int_val,
@@ -398,34 +398,6 @@ int load_route_data_db(struct route_data_t * rd) {
 					p_tmp_comment) == -1) {
 					p_tmp_comment) == -1) {
 				goto errout;
 				goto errout;
 			}
 			}
-			if (row->values[COL_PROB].val.double_val == 0 && !query_done) {
-				int ret_tmp;
-				char query_tmp[QUERY_LEN];
-				str query_tmp_str;
-
-				memset(query_tmp, 0, QUERY_LEN);
-				ret_tmp = snprintf(query_tmp, QUERY_LEN, "SELECT * FROM %.*s WHERE %.*s=%d and %.*s=%d and %.*s>%d",
-						carrierroute_table.len, carrierroute_table.s, columns[COL_CARRIER]->len, columns[COL_CARRIER]->s, row->values[COL_CARRIER].val.int_val,
-						columns[COL_DOMAIN]->len, columns[COL_DOMAIN]->s, row->values[COL_DOMAIN].val.int_val, columns[COL_PROB]->len, columns[COL_PROB]->s, 0);
-
-				if (ret_tmp < 0) {
-					LM_ERR("error in snprintf while querying prob column");
-					goto errout;
-				}
-				query_tmp_str.s = query_tmp;
-				query_tmp_str.len = ret_tmp;
-
-				if (carrierroute_dbf.raw_query(carrierroute_dbh, &query_tmp_str, &prob_res) < 0) {
-					LM_ERR("Failed to query carrierroute db table based on prob column.\n");
-					goto errout;
-				}
-				if(RES_ROW_N(prob_res) == 0) {
-					LM_ERR("Carrierroute db table contains route(s) with only 0 probability.\n");
-					query_done = crtrue;
-				}
-				carrierroute_dbf.free_result(carrierroute_dbh, prob_res);
-				prob_res = NULL;
-			}
 
 
 		}
 		}
 		if (DB_CAPABILITY(carrierroute_dbf, DB_CAP_FETCH)) {
 		if (DB_CAPABILITY(carrierroute_dbf, DB_CAP_FETCH)) {
@@ -439,6 +411,16 @@ int load_route_data_db(struct route_data_t * rd) {
 		}
 		}
 	} while(RES_ROW_N(res) > 0);
 	} while(RES_ROW_N(res) > 0);
 
 
+	for (i = 0; i < rd->carrier_num; ++i) {
+		for (j = 0; j < rd->carriers[i]->domain_num; ++j) {
+			if (rd->carriers[i]->domains[j]->sum_prob == 0.0) {
+				LM_ERR("All routes with carrier id %d (%.*s) and domain id %d (%.*s) have probability 0.\n",
+						rd->carriers[i]->id, rd->carriers[i]->name->len, rd->carriers[i]->name->s,
+						rd->carriers[i]->domains[j]->id, rd->carriers[i]->domains[j]->name->len, rd->carriers[i]->domains[j]->name->s);
+			}
+		}
+	}
+
 	carrierroute_dbf.free_result(carrierroute_dbh, res);
 	carrierroute_dbf.free_result(carrierroute_dbh, res);
 	res = NULL;
 	res = NULL;
 	
 	
@@ -493,8 +475,5 @@ errout:
 	if (res) {
 	if (res) {
 		carrierroute_dbf.free_result(carrierroute_dbh, res);
 		carrierroute_dbf.free_result(carrierroute_dbh, res);
 	}
 	}
-	if (prob_res) {
-		carrierroute_dbf.free_result(carrierroute_dbh, prob_res);
-	}
 	return -1;
 	return -1;
 }
 }

+ 0 - 5
src/modules/carrierroute/cr_db.h

@@ -88,9 +88,4 @@ int load_route_data_db (struct route_data_t * rd);
 
 
 int load_user_carrier(str * user, str * domain);
 int load_user_carrier(str * user, str * domain);
 
 
-typedef enum {
-	crfalse = 0,
-	crtrue = 1
-} crboolean;
-
 #endif
 #endif

+ 1 - 0
src/modules/carrierroute/cr_domain.h

@@ -40,6 +40,7 @@
 struct domain_data_t {
 struct domain_data_t {
 	int id; /*!< the numerical id of the routing tree */
 	int id; /*!< the numerical id of the routing tree */
 	str * name; /*!< the name of the routing tree. This points to the name in domain_map to avoid duplication. */
 	str * name; /*!< the name of the routing tree. This points to the name in domain_map to avoid duplication. */
+	double sum_prob; /*!< sums the probabilities of all entries in the normal tree. Used to warn that (carrier, domain) has only routes with probability 0. */
 	struct dtrie_node_t * tree; /*!< the root node of the routing tree. Payload is of type (struct route_flags *) */
 	struct dtrie_node_t * tree; /*!< the root node of the routing tree. Payload is of type (struct route_flags *) */
 	struct dtrie_node_t * failure_tree; /*!< the root node of the failure routing tree. Payload is of type (struct failure_route_rule *) */
 	struct dtrie_node_t * failure_tree; /*!< the root node of the failure routing tree. Payload is of type (struct failure_route_rule *) */
 };
 };

+ 1 - 1
src/modules/cplc/cpl_run.c

@@ -283,7 +283,7 @@ static inline char *run_lookup( struct cpl_interpreter *intr )
 		} else {
 		} else {
 			contact = r->contacts;
 			contact = r->contacts;
 			/* skip expired contacts */
 			/* skip expired contacts */
-			while ((contact) && (contact->expires <= tc))
+			while ((contact) && (contact->expires > 0) && (contact->expires <= tc))
 				contact = contact->next;
 				contact = contact->next;
 			/* any contacts left? */
 			/* any contacts left? */
 			if (contact) {
 			if (contact) {

+ 41 - 40
src/modules/dialplan/dp_db.c

@@ -13,8 +13,8 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  * 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 
+ * 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
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  *
  *
  */
  */
@@ -46,7 +46,7 @@ str match_exp_column=   str_init(MATCH_EXP_COL);
 str match_len_column=   str_init(MATCH_LEN_COL);
 str match_len_column=   str_init(MATCH_LEN_COL);
 str subst_exp_column=   str_init(SUBST_EXP_COL);
 str subst_exp_column=   str_init(SUBST_EXP_COL);
 str repl_exp_column =   str_init(REPL_EXP_COL);
 str repl_exp_column =   str_init(REPL_EXP_COL);
-str attrs_column    =   str_init(ATTRS_COL); 
+str attrs_column    =   str_init(ATTRS_COL);
 
 
 extern int dp_fetch_rows;
 extern int dp_fetch_rows;
 extern int dp_match_dynamic;
 extern int dp_match_dynamic;
@@ -75,8 +75,9 @@ void list_rule(dpl_node_t * );
 void list_hash(int h_index);
 void list_hash(int h_index);
 
 
 
 
-dpl_id_p* rules_hash = NULL;
-int * crt_idx, *next_idx;
+static dpl_id_p* dp_rules_hash = NULL;
+static int *dp_crt_idx = NULL;
+static int *dp_next_idx = NULL;
 
 
 
 
 /**
 /**
@@ -197,21 +198,21 @@ int init_data(void)
 {
 {
 	int *p;
 	int *p;
 
 
-	rules_hash = (dpl_id_p *)shm_malloc(2*sizeof(dpl_id_p));
-	if(!rules_hash) {
+	dp_rules_hash = (dpl_id_p *)shm_malloc(2*sizeof(dpl_id_p));
+	if(!dp_rules_hash) {
 		LM_ERR("out of shm memory\n");
 		LM_ERR("out of shm memory\n");
 		return -1;
 		return -1;
 	}
 	}
-	rules_hash[0] = rules_hash[1] = 0;
+	dp_rules_hash[0] = dp_rules_hash[1] = 0;
 
 
 	p = (int *)shm_malloc(2*sizeof(int));
 	p = (int *)shm_malloc(2*sizeof(int));
 	if(!p){
 	if(!p){
 		LM_ERR("out of shm memory\n");
 		LM_ERR("out of shm memory\n");
 		return -1;
 		return -1;
 	}
 	}
-	crt_idx = p;
-	next_idx = p+1;
-	*crt_idx = *next_idx = 0;
+	dp_crt_idx = p;
+	dp_next_idx = p+1;
+	*dp_crt_idx = *dp_next_idx = 0;
 
 
 	LM_DBG("trying to initialize data from db\n");
 	LM_DBG("trying to initialize data from db\n");
 	if(init_db_data() != 0)
 	if(init_db_data() != 0)
@@ -223,15 +224,15 @@ int init_data(void)
 
 
 void destroy_data(void)
 void destroy_data(void)
 {
 {
-	if(rules_hash){
+	if(dp_rules_hash){
 		destroy_hash(0);
 		destroy_hash(0);
 		destroy_hash(1);
 		destroy_hash(1);
-		shm_free(rules_hash);
-		rules_hash = 0;
+		shm_free(dp_rules_hash);
+		dp_rules_hash = 0;
 	}
 	}
 
 
-	if(crt_idx)
-		shm_free(crt_idx);
+	if(dp_crt_idx)
+		shm_free(dp_crt_idx);
 }
 }
 
 
 
 
@@ -252,7 +253,7 @@ int dp_load_db(void)
 	dpl_node_t *rule;
 	dpl_node_t *rule;
 
 
 	LM_DBG("init\n");
 	LM_DBG("init\n");
-	if( (*crt_idx) != (*next_idx)){
+	if( (*dp_crt_idx) != (*dp_next_idx)){
 		LM_WARN("a load command already generated, aborting reload...\n");
 		LM_WARN("a load command already generated, aborting reload...\n");
 		return 0;
 		return 0;
 	}
 	}
@@ -263,7 +264,7 @@ int dp_load_db(void)
 	}
 	}
 
 
 	if (DB_CAPABILITY(dp_dbf, DB_CAP_FETCH)) {
 	if (DB_CAPABILITY(dp_dbf, DB_CAP_FETCH)) {
-		if(dp_dbf.query(dp_db_handle,0,0,0,query_cols, 0, 
+		if(dp_dbf.query(dp_db_handle,0,0,0,query_cols, 0,
 					DP_TABLE_COL_NO, order, 0) < 0){
 					DP_TABLE_COL_NO, order, 0) < 0){
 			LM_ERR("failed to query database!\n");
 			LM_ERR("failed to query database!\n");
 			return -1;
 			return -1;
@@ -285,8 +286,8 @@ int dp_load_db(void)
 
 
 	nr_rows = RES_ROW_N(res);
 	nr_rows = RES_ROW_N(res);
 
 
-	*next_idx = ((*crt_idx) == 0)? 1:0;
-	destroy_hash(*next_idx);
+	*dp_next_idx = ((*dp_crt_idx) == 0)? 1:0;
+	destroy_hash(*dp_next_idx);
 
 
 	if(nr_rows == 0){
 	if(nr_rows == 0){
 		LM_WARN("no data in the db\n");
 		LM_WARN("no data in the db\n");
@@ -302,7 +303,7 @@ int dp_load_db(void)
 			if((rule = build_rule(values)) ==0 )
 			if((rule = build_rule(values)) ==0 )
 				goto err2;
 				goto err2;
 
 
-			if(add_rule2hash(rule , *next_idx) != 0)
+			if(add_rule2hash(rule, *dp_next_idx) != 0)
 				goto err2;
 				goto err2;
 
 
 		}
 		}
@@ -321,16 +322,16 @@ int dp_load_db(void)
 
 
 end:
 end:
 	/*update data*/
 	/*update data*/
-	*crt_idx = *next_idx;
-	list_hash(*crt_idx);
+	*dp_crt_idx = *dp_next_idx;
+	list_hash(*dp_crt_idx);
 	dp_dbf.free_result(dp_db_handle, res);
 	dp_dbf.free_result(dp_db_handle, res);
 	return 0;
 	return 0;
 
 
 err2:
 err2:
 	if(rule)	destroy_rule(rule);
 	if(rule)	destroy_rule(rule);
-	destroy_hash(*next_idx);
+	destroy_hash(*dp_next_idx);
 	dp_dbf.free_result(dp_db_handle, res);
 	dp_dbf.free_result(dp_db_handle, res);
-	*next_idx = *crt_idx; 
+	*dp_next_idx = *dp_crt_idx;
 	return -1;
 	return -1;
 }
 }
 
 
@@ -550,7 +551,7 @@ int add_rule2hash(dpl_node_t * rule, int h_index)
 	dpl_index_p indexp, last_indexp, new_indexp;
 	dpl_index_p indexp, last_indexp, new_indexp;
 	int new_id;
 	int new_id;
 
 
-	if(!rules_hash){
+	if(!dp_rules_hash){
 		LM_ERR("data not allocated\n");
 		LM_ERR("data not allocated\n");
 		return -1;
 		return -1;
 	}
 	}
@@ -558,7 +559,7 @@ int add_rule2hash(dpl_node_t * rule, int h_index)
 	new_id = 0;
 	new_id = 0;
 
 
 	/*search for the corresponding dpl_id*/
 	/*search for the corresponding dpl_id*/
-	for(crt_idp = last_idp =rules_hash[h_index]; crt_idp!= NULL; 
+	for(crt_idp = last_idp =dp_rules_hash[h_index]; crt_idp!= NULL;
 			last_idp = crt_idp, crt_idp = crt_idp->next)
 			last_idp = crt_idp, crt_idp = crt_idp->next)
 		if(crt_idp->dp_id == rule->dpid)
 		if(crt_idp->dp_id == rule->dpid)
 			break;
 			break;
@@ -577,7 +578,7 @@ int add_rule2hash(dpl_node_t * rule, int h_index)
 	}
 	}
 
 
 	/*search for the corresponding dpl_index*/
 	/*search for the corresponding dpl_index*/
-	for(indexp = last_indexp =crt_idp->first_index; indexp!=NULL; 
+	for(indexp = last_indexp =crt_idp->first_index; indexp!=NULL;
 			last_indexp = indexp, indexp = indexp->next){
 			last_indexp = indexp, indexp = indexp->next){
 		if(indexp->len == rule->matchlen)
 		if(indexp->len == rule->matchlen)
 			goto add_rule;
 			goto add_rule;
@@ -617,8 +618,8 @@ add_rule:
 	indexp->last_rule = rule;
 	indexp->last_rule = rule;
 
 
 	if(new_id){
 	if(new_id){
-		crt_idp->next = rules_hash[h_index];
-		rules_hash[h_index] = crt_idp;
+		crt_idp->next = dp_rules_hash[h_index];
+		dp_rules_hash[h_index] = crt_idp;
 	}
 	}
 	LM_DBG("added the rule id %i index %i pr %i next %p to the "
 	LM_DBG("added the rule id %i index %i pr %i next %p to the "
 			"index with %i len\n", rule->dpid, rule->matchlen,
 			"index with %i len\n", rule->dpid, rule->matchlen,
@@ -639,10 +640,10 @@ void destroy_hash(int index)
 	dpl_index_p indexp;
 	dpl_index_p indexp;
 	dpl_node_p rulep;
 	dpl_node_p rulep;
 
 
-	if(!rules_hash[index])
+	if(!dp_rules_hash[index])
 		return;
 		return;
 
 
-	for(crt_idp = rules_hash[index]; crt_idp != NULL;){
+	for(crt_idp = dp_rules_hash[index]; crt_idp != NULL;){
 
 
 		for(indexp = crt_idp->first_index; indexp != NULL;){
 		for(indexp = crt_idp->first_index; indexp != NULL;){
 
 
@@ -662,13 +663,13 @@ void destroy_hash(int index)
 
 
 		}
 		}
 
 
-		rules_hash[index] = crt_idp->next;
+		dp_rules_hash[index] = crt_idp->next;
 		shm_free(crt_idp);
 		shm_free(crt_idp);
 		crt_idp = 0;
 		crt_idp = 0;
-		crt_idp = rules_hash[index];
+		crt_idp = dp_rules_hash[index];
 	}
 	}
 
 
-	rules_hash[index] = 0;
+	dp_rules_hash[index] = 0;
 }
 }
 
 
 
 
@@ -677,7 +678,7 @@ void destroy_rule(dpl_node_t * rule){
 	if(!rule)
 	if(!rule)
 		return;
 		return;
 
 
-	LM_DBG("destroying rule with priority %i\n", 
+	LM_DBG("destroying rule with priority %i\n",
 			rule->pr);
 			rule->pr);
 
 
 	if(rule->match_comp)
 	if(rule->match_comp)
@@ -708,10 +709,10 @@ dpl_id_p select_dpid(int id)
 {
 {
 	dpl_id_p idp;
 	dpl_id_p idp;
 
 
-	if(!rules_hash || !crt_idx)
+	if(!dp_rules_hash || !dp_crt_idx)
 		return NULL;
 		return NULL;
 
 
-	for(idp = rules_hash[*crt_idx]; idp!=NULL; idp = idp->next)
+	for(idp = dp_rules_hash[*dp_crt_idx]; idp!=NULL; idp = idp->next)
 		if(idp->dp_id == id)
 		if(idp->dp_id == id)
 			return idp;
 			return idp;
 
 
@@ -727,10 +728,10 @@ void list_hash(int h_index)
 	dpl_node_p rulep;
 	dpl_node_p rulep;
 
 
 
 
-	if(!rules_hash[h_index])
+	if(!dp_rules_hash[h_index])
 		return;
 		return;
 
 
-	for(crt_idp=rules_hash[h_index]; crt_idp!=NULL; crt_idp = crt_idp->next){
+	for(crt_idp=dp_rules_hash[h_index]; crt_idp!=NULL; crt_idp = crt_idp->next){
 		LM_DBG("DPID: %i, pointer %p\n", crt_idp->dp_id, crt_idp);
 		LM_DBG("DPID: %i, pointer %p\n", crt_idp->dp_id, crt_idp);
 		for(indexp=crt_idp->first_index; indexp!=NULL;indexp= indexp->next){
 		for(indexp=crt_idp->first_index; indexp!=NULL;indexp= indexp->next){
 			LM_DBG("INDEX LEN: %i\n", indexp->len);
 			LM_DBG("INDEX LEN: %i\n", indexp->len);

+ 50 - 50
src/modules/dispatcher/dispatch.c

@@ -113,13 +113,13 @@ extern int ds_load_mode;
 static db_func_t ds_dbf;
 static db_func_t ds_dbf;
 static db1_con_t *ds_db_handle = NULL;
 static db1_con_t *ds_db_handle = NULL;
 
 
-ds_set_t **ds_lists = NULL;
+static ds_set_t **ds_lists = NULL;
 
 
-int *ds_list_nr = NULL;
-int *crt_idx = NULL;
-int *next_idx = NULL;
+static int *ds_list_nr = NULL;
+static int *ds_crt_idx = NULL;
+static int *ds_next_idx = NULL;
 
 
-#define _ds_list (ds_lists[*crt_idx])
+#define _ds_list (ds_lists[*ds_crt_idx])
 #define _ds_list_nr (*ds_list_nr)
 #define _ds_list_nr (*ds_list_nr)
 
 
 static void ds_run_route(struct sip_msg *msg, str *uri, char *route,
 static void ds_run_route(struct sip_msg *msg, str *uri, char *route,
@@ -267,10 +267,10 @@ int ds_init_data(void)
 	}
 	}
 	memset(p, 0, 3 * sizeof(int));
 	memset(p, 0, 3 * sizeof(int));
 
 
-	crt_idx = p;
-	next_idx = p + 1;
+	ds_crt_idx = p;
+	ds_next_idx = p + 1;
 	ds_list_nr = p + 2;
 	ds_list_nr = p + 2;
-	*crt_idx = *next_idx = 0;
+	*ds_crt_idx = *ds_next_idx = 0;
 
 
 	return 0;
 	return 0;
 }
 }
@@ -784,7 +784,7 @@ int ds_load_list(char *lfile)
 	str uri;
 	str uri;
 	str attrs;
 	str attrs;
 
 
-	if((*crt_idx) != (*next_idx)) {
+	if((*ds_crt_idx) != (*ds_next_idx)) {
 		LM_WARN("load command already generated, aborting reload...\n");
 		LM_WARN("load command already generated, aborting reload...\n");
 		return 0;
 		return 0;
 	}
 	}
@@ -802,8 +802,8 @@ int ds_load_list(char *lfile)
 
 
 	id = setn = flags = priority = 0;
 	id = setn = flags = priority = 0;
 
 
-	*next_idx = (*crt_idx + 1) % 2;
-	ds_avl_destroy(&ds_lists[*next_idx]);
+	*ds_next_idx = (*ds_crt_idx + 1) % 2;
+	ds_avl_destroy(&ds_lists[*ds_next_idx]);
 
 
 	p = fgets(line, 1024, f);
 	p = fgets(line, 1024, f);
 	while(p) {
 	while(p) {
@@ -878,7 +878,7 @@ int ds_load_list(char *lfile)
 		attrs.len = p - attrs.s;
 		attrs.len = p - attrs.s;
 
 
 add_destination:
 add_destination:
-		if(add_dest2list(id, uri, flags, priority, &attrs, *next_idx, &setn, 0)
+		if(add_dest2list(id, uri, flags, priority, &attrs, *ds_next_idx, &setn, 0)
 				!= 0) {
 				!= 0) {
 			LM_WARN("unable to add destination %.*s to set %d -- skipping\n",
 			LM_WARN("unable to add destination %.*s to set %d -- skipping\n",
 					uri.len, uri.s, id);
 					uri.len, uri.s, id);
@@ -890,7 +890,7 @@ next_line:
 		p = fgets(line, 1024, f);
 		p = fgets(line, 1024, f);
 	}
 	}
 
 
-	if(reindex_dests(ds_lists[*next_idx]) != 0) {
+	if(reindex_dests(ds_lists[*ds_next_idx]) != 0) {
 		LM_ERR("error on reindex\n");
 		LM_ERR("error on reindex\n");
 		goto error;
 		goto error;
 	}
 	}
@@ -899,7 +899,7 @@ next_line:
 	f = NULL;
 	f = NULL;
 	/* Update list - should it be sync'ed? */
 	/* Update list - should it be sync'ed? */
 	_ds_list_nr = setn;
 	_ds_list_nr = setn;
-	*crt_idx = *next_idx;
+	*ds_crt_idx = *ds_next_idx;
 
 
 	LM_DBG("found [%d] dest sets\n", _ds_list_nr);
 	LM_DBG("found [%d] dest sets\n", _ds_list_nr);
 
 
@@ -909,8 +909,8 @@ next_line:
 error:
 error:
 	if(f != NULL)
 	if(f != NULL)
 		fclose(f);
 		fclose(f);
-	ds_avl_destroy(&ds_lists[*next_idx]);
-	*next_idx = *crt_idx;
+	ds_avl_destroy(&ds_lists[*ds_next_idx]);
+	*ds_next_idx = *ds_crt_idx;
 	return -1;
 	return -1;
 }
 }
 
 
@@ -1048,7 +1048,7 @@ int ds_load_db(void)
 		}
 		}
 	}
 	}
 
 
-	if((*crt_idx) != (*next_idx)) {
+	if((*ds_crt_idx) != (*ds_next_idx)) {
 		LM_WARN("load command already generated, aborting reload...\n");
 		LM_WARN("load command already generated, aborting reload...\n");
 		return 0;
 		return 0;
 	}
 	}
@@ -1079,8 +1079,8 @@ int ds_load_db(void)
 	}
 	}
 
 
 	setn = 0;
 	setn = 0;
-	*next_idx = (*crt_idx + 1) % 2;
-	ds_avl_destroy(&ds_lists[*next_idx]);
+	*ds_next_idx = (*ds_crt_idx + 1) % 2;
+	ds_avl_destroy(&ds_lists[*ds_next_idx]);
 
 
 	for(i = 0; i < nr_rows; i++) {
 	for(i = 0; i < nr_rows; i++) {
 		values = ROW_VALUES(rows + i);
 		values = ROW_VALUES(rows + i);
@@ -1128,7 +1128,7 @@ int ds_load_db(void)
 			}
 			}
 		}
 		}
 		LM_DBG("attributes string: [%.*s]\n", attrs.len, (attrs.s)?attrs.s:"");
 		LM_DBG("attributes string: [%.*s]\n", attrs.len, (attrs.s)?attrs.s:"");
-		if(add_dest2list(id, uri, flags, priority, &attrs, *next_idx, &setn, 0)
+		if(add_dest2list(id, uri, flags, priority, &attrs, *ds_next_idx, &setn, 0)
 				!= 0) {
 				!= 0) {
 			dest_errs++;
 			dest_errs++;
 			LM_WARN("unable to add destination %.*s to set %d -- skipping\n",
 			LM_WARN("unable to add destination %.*s to set %d -- skipping\n",
@@ -1138,7 +1138,7 @@ int ds_load_db(void)
 			}
 			}
 		}
 		}
 	}
 	}
-	if(reindex_dests(ds_lists[*next_idx]) != 0) {
+	if(reindex_dests(ds_lists[*ds_next_idx]) != 0) {
 		LM_ERR("error on reindex\n");
 		LM_ERR("error on reindex\n");
 		goto err2;
 		goto err2;
 	}
 	}
@@ -1147,7 +1147,7 @@ int ds_load_db(void)
 
 
 	/* update data - should it be sync'ed? */
 	/* update data - should it be sync'ed? */
 	_ds_list_nr = setn;
 	_ds_list_nr = setn;
-	*crt_idx = *next_idx;
+	*ds_crt_idx = *ds_next_idx;
 
 
 	LM_DBG("found [%d] dest sets\n", _ds_list_nr);
 	LM_DBG("found [%d] dest sets\n", _ds_list_nr);
 
 
@@ -1158,9 +1158,9 @@ int ds_load_db(void)
 	return 0;
 	return 0;
 
 
 err2:
 err2:
-	ds_avl_destroy(&ds_lists[*next_idx]);
+	ds_avl_destroy(&ds_lists[*ds_next_idx]);
 	ds_dbf.free_result(ds_db_handle, res);
 	ds_dbf.free_result(ds_db_handle, res);
-	*next_idx = *crt_idx;
+	*ds_next_idx = *ds_crt_idx;
 
 
 	return -1;
 	return -1;
 }
 }
@@ -1174,8 +1174,8 @@ int ds_destroy_list(void)
 		shm_free(ds_lists);
 		shm_free(ds_lists);
 	}
 	}
 
 
-	if(crt_idx)
-		shm_free(crt_idx);
+	if(ds_crt_idx)
+		shm_free(ds_crt_idx);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -1615,7 +1615,7 @@ int ds_load_replace(struct sip_msg *msg, str *duid)
 	}
 	}
 	set = it->dset;
 	set = it->dset;
 	/* get the index of the set */
 	/* get the index of the set */
-	if(ds_get_index(set, *crt_idx, &idx) != 0) {
+	if(ds_get_index(set, *ds_crt_idx, &idx) != 0) {
 		ds_unlock_cell(_dsht_load, &msg->callid->body);
 		ds_unlock_cell(_dsht_load, &msg->callid->body);
 		LM_ERR("destination set [%d] not found\n", set);
 		LM_ERR("destination set [%d] not found\n", set);
 		return -1;
 		return -1;
@@ -1676,7 +1676,7 @@ int ds_load_remove_byid(int set, str *duid)
 	int i;
 	int i;
 
 
 	/* get the index of the set */
 	/* get the index of the set */
-	if(ds_get_index(set, *crt_idx, &idx) != 0) {
+	if(ds_get_index(set, *ds_crt_idx, &idx) != 0) {
 		LM_ERR("destination set [%d] not found\n", set);
 		LM_ERR("destination set [%d] not found\n", set);
 		return -1;
 		return -1;
 	}
 	}
@@ -2276,7 +2276,7 @@ int ds_manage_routes(sip_msg_t *msg, ds_select_state_t *rstate)
 
 
 
 
 	/* get the index of the set */
 	/* get the index of the set */
-	if(ds_get_index(rstate->setid, *crt_idx, &idx) != 0) {
+	if(ds_get_index(rstate->setid, *ds_crt_idx, &idx) != 0) {
 		LM_ERR("destination set [%d] not found\n", rstate->setid);
 		LM_ERR("destination set [%d] not found\n", rstate->setid);
 		return -1;
 		return -1;
 	}
 	}
@@ -2579,7 +2579,7 @@ void ds_add_dest_cb(ds_set_t *node, int i, void *arg)
 	int setn;
 	int setn;
 
 
 	if(add_dest2list(node->id, node->dlist[i].uri, node->dlist[i].flags,
 	if(add_dest2list(node->id, node->dlist[i].uri, node->dlist[i].flags,
-			node->dlist[i].priority, &node->dlist[i].attrs.body, *next_idx,
+			node->dlist[i].priority, &node->dlist[i].attrs.body, *ds_next_idx,
 			&setn, node->dlist[i].dload) != 0) {
 			&setn, node->dlist[i].dload) != 0) {
 		LM_WARN("failed to add destination in group %d - %.*s\n",
 		LM_WARN("failed to add destination in group %d - %.*s\n",
 				node->id, node->dlist[i].uri.len, node->dlist[i].uri.s);
 				node->id, node->dlist[i].uri.len, node->dlist[i].uri.s);
@@ -2595,35 +2595,35 @@ int ds_add_dst(int group, str *address, int flags, str *attrs)
 	setn = _ds_list_nr;
 	setn = _ds_list_nr;
 	priority = 0;
 	priority = 0;
 
 
-	*next_idx = (*crt_idx + 1) % 2;
-	ds_avl_destroy(&ds_lists[*next_idx]);
+	*ds_next_idx = (*ds_crt_idx + 1) % 2;
+	ds_avl_destroy(&ds_lists[*ds_next_idx]);
 
 
 	// add all existing destinations
 	// add all existing destinations
 	ds_iter_set(_ds_list, &ds_add_dest_cb, NULL);
 	ds_iter_set(_ds_list, &ds_add_dest_cb, NULL);
 
 
 	// add new destination
 	// add new destination
 	if(add_dest2list(group, *address, flags, priority, attrs,
 	if(add_dest2list(group, *address, flags, priority, attrs,
-			*next_idx, &setn, 0) != 0) {
+			*ds_next_idx, &setn, 0) != 0) {
 		LM_WARN("unable to add destination %.*s to set %d", address->len, address->s, group);
 		LM_WARN("unable to add destination %.*s to set %d", address->len, address->s, group);
 		if(ds_load_mode==1) {
 		if(ds_load_mode==1) {
 			goto error;
 			goto error;
 		}
 		}
 	}
 	}
 
 
-	if(reindex_dests(ds_lists[*next_idx]) != 0) {
+	if(reindex_dests(ds_lists[*ds_next_idx]) != 0) {
 		LM_ERR("error on reindex\n");
 		LM_ERR("error on reindex\n");
 		goto error;
 		goto error;
 	}
 	}
 
 
 	_ds_list_nr = setn;
 	_ds_list_nr = setn;
-	*crt_idx = *next_idx;
+	*ds_crt_idx = *ds_next_idx;
 
 
 	ds_log_sets();
 	ds_log_sets();
 	return 0;
 	return 0;
 
 
 error:
 error:
-	ds_avl_destroy(&ds_lists[*next_idx]);
-	*next_idx = *crt_idx;
+	ds_avl_destroy(&ds_lists[*ds_next_idx]);
+	*ds_next_idx = *ds_crt_idx;
 	return -1;
 	return -1;
 }
 }
 
 
@@ -2637,7 +2637,7 @@ void ds_filter_dest_cb(ds_set_t *node, int i, void *arg)
 		return;
 		return;
 
 
 	if(add_dest2list(node->id, node->dlist[i].uri, node->dlist[i].flags,
 	if(add_dest2list(node->id, node->dlist[i].uri, node->dlist[i].flags,
-			node->dlist[i].priority, &node->dlist[i].attrs.body, *next_idx,
+			node->dlist[i].priority, &node->dlist[i].attrs.body, *ds_next_idx,
 			filter_arg->setn, node->dlist[i].dload) != 0) {
 			filter_arg->setn, node->dlist[i].dload) != 0) {
 		LM_WARN("failed to add destination in group %d - %.*s\n",
 		LM_WARN("failed to add destination in group %d - %.*s\n",
 				node->id, node->dlist[i].uri.len, node->dlist[i].uri.s);
 				node->id, node->dlist[i].uri.len, node->dlist[i].uri.s);
@@ -2659,26 +2659,26 @@ int ds_remove_dst(int group, str *address)
 	filter_arg.dest = dp;
 	filter_arg.dest = dp;
 	filter_arg.setn = &setn;
 	filter_arg.setn = &setn;
 
 
-	*next_idx = (*crt_idx + 1) % 2;
-	ds_avl_destroy(&ds_lists[*next_idx]);
+	*ds_next_idx = (*ds_crt_idx + 1) % 2;
+	ds_avl_destroy(&ds_lists[*ds_next_idx]);
 
 
 	// add existing destinations except destination that matches group & address
 	// add existing destinations except destination that matches group & address
 	ds_iter_set(_ds_list, &ds_filter_dest_cb, &filter_arg);
 	ds_iter_set(_ds_list, &ds_filter_dest_cb, &filter_arg);
 
 
-	if(reindex_dests(ds_lists[*next_idx]) != 0) {
+	if(reindex_dests(ds_lists[*ds_next_idx]) != 0) {
 		LM_ERR("error on reindex\n");
 		LM_ERR("error on reindex\n");
 		goto error;
 		goto error;
 	}
 	}
 
 
 	_ds_list_nr = setn;
 	_ds_list_nr = setn;
-	*crt_idx = *next_idx;
+	*ds_crt_idx = *ds_next_idx;
 
 
 	ds_log_sets();
 	ds_log_sets();
 	return 0;
 	return 0;
 
 
 error:
 error:
-	ds_avl_destroy(&ds_lists[*next_idx]);
-	*next_idx = *crt_idx;
+	ds_avl_destroy(&ds_lists[*ds_next_idx]);
+	*ds_next_idx = *ds_crt_idx;
 	return -1;
 	return -1;
 }
 }
 
 
@@ -2833,7 +2833,7 @@ int ds_update_latency(int group, str *address, int code)
 	}
 	}
 
 
 	/* get the index of the set */
 	/* get the index of the set */
-	if(ds_get_index(group, *crt_idx, &idx) != 0) {
+	if(ds_get_index(group, *ds_crt_idx, &idx) != 0) {
 		LM_ERR("destination set [%d] not found\n", group);
 		LM_ERR("destination set [%d] not found\n", group);
 		return -1;
 		return -1;
 	}
 	}
@@ -2927,7 +2927,7 @@ int ds_get_state(int group, str *address)
 	}
 	}
 
 
 	/* get the index of the set */
 	/* get the index of the set */
-	if(ds_get_index(group, *crt_idx, &idx) != 0) {
+	if(ds_get_index(group, *ds_crt_idx, &idx) != 0) {
 		LM_ERR("destination set [%d] not found\n", group);
 		LM_ERR("destination set [%d] not found\n", group);
 		return -1;
 		return -1;
 	}
 	}
@@ -2961,7 +2961,7 @@ int ds_update_state(sip_msg_t *msg, int group, str *address, int state,
 	}
 	}
 
 
 	/* get the index of the set */
 	/* get the index of the set */
-	if(ds_get_index(group, *crt_idx, &idx) != 0) {
+	if(ds_get_index(group, *ds_crt_idx, &idx) != 0) {
 		LM_ERR("destination set [%d] not found\n", group);
 		LM_ERR("destination set [%d] not found\n", group);
 		return -1;
 		return -1;
 	}
 	}
@@ -3162,7 +3162,7 @@ int ds_reinit_state(int group, str *address, int state)
 	}
 	}
 
 
 	/* get the index of the set */
 	/* get the index of the set */
-	if(ds_get_index(group, *crt_idx, &idx) != 0) {
+	if(ds_get_index(group, *ds_crt_idx, &idx) != 0) {
 		LM_ERR("destination set [%d] not found\n", group);
 		LM_ERR("destination set [%d] not found\n", group);
 		return -1;
 		return -1;
 	}
 	}
@@ -3203,7 +3203,7 @@ int ds_reinit_duid_state(int group, str *vduid, int state)
 	}
 	}
 
 
 	/* get the index of the set */
 	/* get the index of the set */
-	if(ds_get_index(group, *crt_idx, &idx) != 0) {
+	if(ds_get_index(group, *ds_crt_idx, &idx) != 0) {
 		LM_ERR("destination set [%d] not found\n", group);
 		LM_ERR("destination set [%d] not found\n", group);
 		return -1;
 		return -1;
 	}
 	}
@@ -3244,7 +3244,7 @@ int ds_reinit_state_all(int group, int state)
 	}
 	}
 
 
 	/* get the index of the set */
 	/* get the index of the set */
-	if(ds_get_index(group, *crt_idx, &idx) != 0) {
+	if(ds_get_index(group, *ds_crt_idx, &idx) != 0) {
 		LM_ERR("destination set [%d] not found\n", group);
 		LM_ERR("destination set [%d] not found\n", group);
 		return -1;
 		return -1;
 	}
 	}

+ 5 - 5
src/modules/dispatcher/dispatcher.c

@@ -1635,10 +1635,10 @@ static void dispatcher_rpc_list(rpc_t *rpc, void *ctx)
 	void *th;
 	void *th;
 	void *ih;
 	void *ih;
 
 
-	ds_set_t *ds_list = ds_get_list();
-	int ds_list_nr = ds_get_list_nr();
+	ds_set_t *dslist = ds_get_list();
+	int dslistnr = ds_get_list_nr();
 
 
-	if(ds_list == NULL || ds_list_nr <= 0) {
+	if(dslist == NULL || dslistnr <= 0) {
 		LM_DBG("no destination sets\n");
 		LM_DBG("no destination sets\n");
 		rpc->fault(ctx, 500, "No Destination Sets");
 		rpc->fault(ctx, 500, "No Destination Sets");
 		return;
 		return;
@@ -1649,12 +1649,12 @@ static void dispatcher_rpc_list(rpc_t *rpc, void *ctx)
 		rpc->fault(ctx, 500, "Internal error root reply");
 		rpc->fault(ctx, 500, "Internal error root reply");
 		return;
 		return;
 	}
 	}
-	if(rpc->struct_add(th, "d[", "NRSETS", ds_list_nr, "RECORDS", &ih) < 0) {
+	if(rpc->struct_add(th, "d[", "NRSETS", dslistnr, "RECORDS", &ih) < 0) {
 		rpc->fault(ctx, 500, "Internal error sets structure");
 		rpc->fault(ctx, 500, "Internal error sets structure");
 		return;
 		return;
 	}
 	}
 
 
-	ds_rpc_print_set(ds_list, rpc, ctx, ih);
+	ds_rpc_print_set(dslist, rpc, ctx, ih);
 
 
 	return;
 	return;
 }
 }

+ 25 - 0
src/modules/jwt/Makefile

@@ -0,0 +1,25 @@
+#
+# 
+# WARNING: do not run this directly, it should be run by the main Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=jwt.so
+
+ifeq ($(CROSS_COMPILE),)
+JWT_BUILDER=$(shell \
+	if pkg-config --exists libjwt; then \
+		echo 'pkg-config libjwt'; \
+	fi)
+endif
+
+ifneq ($(JWT_BUILDER),)
+	DEFS += $(shell $(JWT_BUILDER) --cflags)
+	LIBS += $(shell $(JWT_BUILDER) --libs)
+else
+	DEFS += -I$(LOCALBASE)/include
+	LIBS += -L$(LOCALBASE)/lib -ljwt
+endif
+
+include ../../Makefile.modules
+

+ 176 - 0
src/modules/jwt/README

@@ -0,0 +1,176 @@
+JWT Module
+
+Daniel-Constantin Mierla
+
+   <[email protected]>
+
+Edited by
+
+Daniel-Constantin Mierla
+
+   <[email protected]>
+
+   Copyright © 2021 asipto.com
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+        2. Dependencies
+
+              2.1. Kamailio Modules
+              2.2. External Libraries or Applications
+
+        3. Parameters
+
+              3.1. key_mode (int)
+
+        4. Functions
+
+              4.1. jwt_generate(prvkey, alg, claims)
+              4.2. jwt_verify(pubkey, alg, claims, jwtval)
+
+        5. Variables
+
+              5.1. $jwt(key)
+
+   List of Examples
+
+   1.1. Set key_mode parameter
+   1.2. jwt_generate usage
+   1.3. jwt_verify usage
+   1.4. $jwt(name) usage
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+   2. Dependencies
+
+        2.1. Kamailio Modules
+        2.2. External Libraries or Applications
+
+   3. Parameters
+
+        3.1. key_mode (int)
+
+   4. Functions
+
+        4.1. jwt_generate(prvkey, alg, claims)
+        4.2. jwt_verify(pubkey, alg, claims, jwtval)
+
+   5. Variables
+
+        5.1. $jwt(key)
+
+1. Overview
+
+   This module provides JWT (JSON Web Token) functions to be used in
+   Kamailio configuration file.
+
+   It relies on libjwt (at least v1.12.0) library
+   (https://github.com/benmcollins/libjwt).
+
+2. Dependencies
+
+   2.1. Kamailio Modules
+   2.2. External Libraries or Applications
+
+2.1. Kamailio Modules
+
+   The following modules must be loaded before this module:
+     * none.
+
+2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed before
+   running Kamailio with this module loaded:
+     * libjwt - minimum version 1.12.0.
+
+3. Parameters
+
+   3.1. key_mode (int)
+
+3.1. key_mode (int)
+
+   Mode to use the private and public keys. If set to 0, they are read
+   always from the disk. If set to 1, they are cached in memory with the
+   first use (no reload support yet).
+
+   Default value is 0.
+
+   Example 1.1. Set key_mode parameter
+...
+modparam("jwt", "key_mode", 1)
+...
+
+4. Functions
+
+   4.1. jwt_generate(prvkey, alg, claims)
+   4.2. jwt_verify(pubkey, alg, claims, jwtval)
+
+4.1.  jwt_generate(prvkey, alg, claims)
+
+   Generate the JWT, its value can be retrieved in the variable $jwt(val).
+
+   The parameters are:
+     * prvkey - path to private key
+     * alg - the algoritm to build the signature, as supported by the
+       libjwt (e.g., RS256, HS256, ES256, ...)
+     * claims - the list of claims to be added to JWT, in the format
+       "name1=value1;name2=value2;..." (same as the SIP parameters
+       format).
+
+   This function can be used from ANY_ROUTE.
+
+   Example 1.2. jwt_generate usage
+...
+  jwt_generate("/path/to/prvkey.pem", "RS256",
+        "caller=$fU;callee=$tU;callid=$ci");
+...
+
+4.2.  jwt_verify(pubkey, alg, claims, jwtval)
+
+   Verify the JWT.
+
+   The parameters are:
+     * pubkey - path to public key
+     * alg - the algoritm to build the signature, as supported by the
+       libjwt (e.g., RS256, HS256, ES256, ...)
+     * claims - the list of claims to be checked they are in the JWT, in
+       the format "name1=value1;name2=value2;..." (same as the SIP
+       parameters format).
+     * jwtval - the value of the JWT to verify
+
+   This function can be used from ANY_ROUTE.
+
+   Example 1.3. jwt_verify usage
+...
+  if(!jwt_verify("/path/to/pubkey.pem", "RS256",
+         "caller=$fU;callee=$tU;callid=$ci",
+        "$var(jwt)") {
+    xwarn("failed to verify jwt\n");
+  }
+...
+
+5. Variables
+
+   5.1. $jwt(key)
+
+5.1.  $jwt(key)
+
+   Get the values and attributes after using JWT functions.
+
+   The key can be:
+     * val - the value of JWT after a successful jwt_generate().
+     * status - the status of verification after a failed jwt_verify().
+
+   Example 1.4. $jwt(name) usage
+...
+  jwt_generate("/path/to/prvkey.pem", "RS256",
+        "caller=$fU;callee=$tU;callid=$ci");
+  xinfo("jwt is: $jwt(val)");
+...

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

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

+ 37 - 0
src/modules/jwt/doc/jwt.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../../doc/docbook/entities.xml">
+%docentities;
+
+]>
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+    <bookinfo>
+	<title>JWT Module</title>
+	<productname class="trade">kamailio.org</productname>
+	<authorgroup>
+	    <author>
+		<firstname>Daniel-Constantin</firstname>
+		<surname>Mierla</surname>
+		<email>[email protected]</email>
+	    </author>
+	    <editor>
+		<firstname>Daniel-Constantin</firstname>
+		<surname>Mierla</surname>
+		<email>[email protected]</email>
+	    </editor>
+	</authorgroup>
+	<copyright>
+	    <year>2021</year>
+	    <holder>asipto.com</holder>
+	</copyright>
+    </bookinfo>
+    <toc></toc>
+
+    <xi:include href="jwt_admin.xml"/>
+
+
+</book>

+ 217 - 0
src/modules/jwt/doc/jwt_admin.xml

@@ -0,0 +1,217 @@
+<?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 module provides JWT (JSON Web Token) functions to be used
+		in &kamailio; configuration file.
+	</para>
+	<para>
+		It relies on libjwt (at least v1.12.0) library (https://github.com/benmcollins/libjwt).
+	</para>
+	</section>
+
+	<section>
+	<title>Dependencies</title>
+	<section>
+		<title>&kamailio; Modules</title>
+		<para>
+		The following modules must be loaded before this module:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>none</emphasis>.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	<section>
+		<title>External Libraries or Applications</title>
+		<para>
+		The following libraries or applications must be installed before running
+		&kamailio; with this module loaded:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>libjwt</emphasis> - minimum version 1.12.0.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	</section>
+
+	<section>
+	<title>Parameters</title>
+	<section id="jwt.p.key_mode">
+		<title><varname>key_mode</varname> (int)</title>
+		<para>
+			Mode to use the private and public keys. If set to 0, they are read
+			always from the disk. If set to 1, they are cached in memory with
+			the first use (no reload support yet).
+		</para>
+		<para>
+		<emphasis>
+			Default value is 0.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>key_mode</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("jwt", "key_mode", 1)
+...
+</programlisting>
+		</example>
+	</section>
+
+	</section>
+
+	<section>
+	<title>Functions</title>
+	<section id="jwt.f.jwt_generate">
+	    <title>
+		<function moreinfo="none">jwt_generate(prvkey, alg, claims)</function>
+	    </title>
+	    <para>
+	    Generate the JWT, its value can be retrieved in the variable $jwt(val).
+		</para>
+		<para>
+		The parameters are:
+		</para>
+		<itemizedlist>
+			<listitem>
+			<para>
+			prvkey - path to private key
+			</para>
+			</listitem>
+			<listitem>
+			<para>
+			alg - the algoritm to build the signature, as supported by the
+			libjwt (e.g., RS256, HS256, ES256, ...)
+			</para>
+			</listitem>
+			<listitem>
+			<para>
+			claims - the list of claims to be added to JWT, in the format
+			"name1=value1;name2=value2;..." (same as the SIP parameters format).
+			</para>
+			</listitem>
+		</itemizedlist>
+		<para>
+		This function can be used from ANY_ROUTE.
+		</para>
+		<example>
+		<title><function>jwt_generate</function> usage</title>
+		<programlisting format="linespecific">
+...
+  jwt_generate("/path/to/prvkey.pem", "RS256",
+        "caller=$fU;callee=$tU;callid=$ci");
+...
+</programlisting>
+	    </example>
+	</section>
+
+	<section id="jwt.f.jwt_verify">
+	    <title>
+		<function moreinfo="none">jwt_verify(pubkey, alg, claims, jwtval)</function>
+	    </title>
+	    <para>
+	    Verify the JWT.
+		</para>
+		<para>
+		The parameters are:
+		</para>
+		<itemizedlist>
+			<listitem>
+			<para>
+			pubkey - path to public key
+			</para>
+			</listitem>
+			<listitem>
+			<para>
+			alg - the algoritm to build the signature, as supported by the
+			libjwt (e.g., RS256, HS256, ES256, ...)
+			</para>
+			</listitem>
+			<listitem>
+			<para>
+			claims - the list of claims to be checked they are in the JWT, in the format
+			"name1=value1;name2=value2;..." (same as the SIP parameters format).
+			</para>
+			</listitem>
+			<listitem>
+			<para>
+			jwtval - the value of the JWT to verify
+			</para>
+			</listitem>
+		</itemizedlist>
+		<para>
+		This function can be used from ANY_ROUTE.
+		</para>
+		<example>
+		<title><function>jwt_verify</function> usage</title>
+		<programlisting format="linespecific">
+...
+  if(!jwt_verify("/path/to/pubkey.pem", "RS256",
+         "caller=$fU;callee=$tU;callid=$ci",
+        "$var(jwt)") {
+    xwarn("failed to verify jwt\n");
+  }
+...
+</programlisting>
+	    </example>
+	</section>
+	</section>
+	<section>
+	<title>Variables</title>
+	<section id="jwt.v.jwt">
+	    <title>
+		<function moreinfo="none">$jwt(key)</function>
+	    </title>
+	    <para>
+	    Get the values and attributes after using JWT functions.
+		</para>
+		<para>
+		The key can be:
+		</para>
+		<itemizedlist>
+			<listitem>
+			<para>
+			val - the value of JWT after a successful jwt_generate().
+			</para>
+			</listitem>
+			<listitem>
+			<para>
+			status - the status of verification after a failed jwt_verify().
+			</para>
+			</listitem>
+		</itemizedlist>
+		<example>
+		<title><function>$jwt(name)</function> usage</title>
+		<programlisting format="linespecific">
+...
+  jwt_generate("/path/to/prvkey.pem", "RS256",
+        "caller=$fU;callee=$tU;callid=$ci");
+  xinfo("jwt is: $jwt(val)");
+...
+</programlisting>
+	    </example>
+	</section>
+	</section>
+
+</chapter>

+ 514 - 0
src/modules/jwt/jwt_mod.c

@@ -0,0 +1,514 @@
+/**
+ * Copyright (C) 2021 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <jwt.h>
+
+#include "../../core/sr_module.h"
+#include "../../core/dprint.h"
+#include "../../core/mod_fix.h"
+#include "../../core/lvalue.h"
+#include "../../core/kemi.h"
+#include "../../core/parser/parse_param.h"
+
+
+MODULE_VERSION
+
+static int  mod_init(void);
+static int  child_init(int);
+static void mod_destroy(void);
+
+static int w_jwt_generate(sip_msg_t* msg, char* pkey, char* palg, char* pclaims);
+static int w_jwt_verify(sip_msg_t* msg, char* pkey, char* palg, char* pclaims,
+		char *pjwtval);
+
+static int _jwt_key_mode = 0;
+
+static str _jwt_result = STR_NULL;
+static unsigned int _jwt_verify_status = 0;
+
+typedef struct jwt_fcache {
+	str fname;
+	str fdata;
+	struct jwt_fcache *next;
+} jwt_fcache_t;
+
+static jwt_fcache_t *_jwt_fcache_list = NULL;
+
+static cmd_export_t cmds[]={
+	{"jwt_generate", (cmd_function)w_jwt_generate, 3,
+		fixup_spve_all, 0, ANY_ROUTE},
+	{"jwt_verify", (cmd_function)w_jwt_verify, 4,
+		fixup_spve_all, 0, ANY_ROUTE},
+	{0, 0, 0, 0, 0, 0}
+};
+
+static param_export_t params[]={
+	{ "key_mode", PARAM_INT, &_jwt_key_mode },
+
+	{ 0, 0, 0 }
+};
+
+static int jwt_pv_get(sip_msg_t *msg, pv_param_t *param, pv_value_t *res);
+static int jwt_pv_parse_name(pv_spec_t *sp, str *in);
+static pv_export_t mod_pvs[] = {
+	{ {"jwt",  sizeof("jwt")-1}, PVT_OTHER,  jwt_pv_get,    0,
+			jwt_pv_parse_name, 0, 0, 0 },
+	{ {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
+};
+
+struct module_exports exports = {
+	"jwt",          /* module name */
+	DEFAULT_DLFLAGS, /* dlopen flags */
+	cmds,            /* cmd (cfg function) exports */
+	params,          /* param exports */
+	0,               /* RPC method exports */
+	mod_pvs,         /* pseudo-variables exports */
+	0,               /* response handling function */
+	mod_init,        /* module init function */
+	child_init,      /* per-child init function */
+	mod_destroy      /* module destroy function */
+};
+
+
+/**
+ * @brief Initialize crypto module function
+ */
+static int mod_init(void)
+{
+	return 0;
+}
+
+/**
+ * @brief Initialize crypto module children
+ */
+static int child_init(int rank)
+{
+	return 0;
+}
+
+/**
+ * destroy module function
+ */
+static void mod_destroy(void)
+{
+	return;
+}
+
+/**
+ *
+ */
+static int jwt_fcache_get(str *key, str *kdata)
+{
+	jwt_fcache_t *fc = NULL;
+
+	if(_jwt_key_mode!=1) {
+		return -1;
+	}
+	for(fc=_jwt_fcache_list; fc!=NULL; fc=fc->next) {
+		if(fc->fname.len==key->len
+				&& strncmp(fc->fname.s, key->s, key->len)==0) {
+			LM_DBG("file found in cache: %.*s\n", key->len, key->s);
+			*kdata = fc->fdata;
+			break;
+		}
+	}
+	return 0;
+}
+
+/**
+ *
+ */
+static int jwt_fcache_add(str *key, str *kdata)
+{
+	jwt_fcache_t *fc = NULL;
+
+	if(_jwt_key_mode!=1) {
+		return -1;
+	}
+	fc = (jwt_fcache_t*)pkg_malloc(sizeof(jwt_fcache_t) + key->len
+			+ kdata->len + 2);
+	if(fc==NULL) {
+		PKG_MEM_ERROR;
+		return -1;
+	}
+	memset(fc, 0, sizeof(jwt_fcache_t) + key->len + kdata->len + 2);
+	fc->fname.s = (char*)fc + sizeof(jwt_fcache_t);
+	fc->fname.len = key->len;
+	memcpy(fc->fname.s, key->s, key->len);
+	fc->fdata.s = fc->fname.s + fc->fname.len + 1;
+	fc->fdata.len = kdata->len;
+	memcpy(fc->fdata.s, kdata->s, kdata->len);
+	fc->next = _jwt_fcache_list;
+	_jwt_fcache_list = fc;
+
+	return 0;
+}
+
+/**
+ *
+ */
+static int ki_jwt_generate(sip_msg_t* msg, str *key, str *alg, str *claims)
+{
+	str dupclaims = STR_NULL;
+	str sparams = STR_NULL;
+	str kdata = STR_NULL;
+	jwt_alg_t valg = JWT_ALG_NONE;
+	time_t iat;
+	FILE *fpk = NULL;
+	unsigned char keybuf[10240];
+	size_t keybuf_len = 0;
+	param_t* params_list = NULL;
+	param_hooks_t phooks;
+	param_t *pit = NULL;
+	int ret = 0;
+	jwt_t *jwt = NULL;
+
+	if(key==NULL || key->s==NULL || alg==NULL || alg->s==NULL
+			|| claims==NULL || claims->s==NULL || claims->len<=0) {
+		LM_ERR("invalid parameters\n");
+		return -1;
+	}
+	if(_jwt_result.s != NULL) {
+		jwt_free_str(_jwt_result.s);
+		_jwt_result.s = NULL;
+		_jwt_result.len = 0;
+	}
+	valg = jwt_str_alg(alg->s);
+	if (valg == JWT_ALG_INVAL) {
+		LM_ERR("not supported algorithm: %s\n", alg->s);
+		return -1;
+	}
+	if(pkg_str_dup(&dupclaims, claims)<0) {
+		LM_ERR("failed to duplicate claims\n");
+		return -1;
+	}
+	jwt_fcache_get(key, &kdata);
+	if(kdata.s==NULL) {
+		fpk= fopen(key->s, "r");
+		if(fpk==NULL) {
+			LM_ERR("failed to read key file: %s\n", key->s);
+			goto error;
+		}
+		keybuf_len = fread(keybuf, 1, sizeof(keybuf), fpk);
+		fclose(fpk);
+		if(keybuf_len==0) {
+			LM_ERR("unable to read key file content: %s\n", key->s);
+			goto error;
+		}
+		keybuf[keybuf_len] = '\0';
+		kdata.s = (char*)keybuf;
+		kdata.len = (int)keybuf_len;
+		jwt_fcache_add(key, &kdata);
+	}
+	sparams = dupclaims;
+	if(sparams.s[sparams.len-1]==';') {
+		sparams.len--;
+	}
+	if (parse_params(&sparams, CLASS_ANY, &phooks, &params_list)<0) {
+		LM_ERR("failed to parse claims\n");
+		goto error;
+	}
+
+	ret = jwt_new(&jwt);
+	if (ret != 0 || jwt == NULL) {
+		LM_ERR("failed to initialize jwt\n");
+		goto error;
+	}
+
+	iat = time(NULL);
+
+	ret = jwt_add_grant_int(jwt, "iat", iat);
+	for (pit = params_list; pit; pit=pit->next) {
+		if(pit->name.len>0 && pit->body.len>0) {
+			pit->name.s[pit->name.len] = '\0';
+			pit->body.s[pit->body.len] = '\0';
+			jwt_add_grant(jwt, pit->name.s, pit->body.s);
+		}
+	}
+
+	ret = jwt_set_alg(jwt, valg, (unsigned char*)kdata.s, (size_t)kdata.len);
+	if (ret < 0) {
+		LM_ERR("failed to set algorithm and key\n");
+		goto error;
+	}
+
+	_jwt_result.s = jwt_encode_str(jwt);
+	_jwt_result.len = strlen(_jwt_result.s);
+
+	free_params(params_list);
+	pkg_free(dupclaims.s);
+	jwt_free(jwt);
+
+	return 1;
+
+error:
+	if(params_list!=NULL) {
+		free_params(params_list);
+	}
+	if(dupclaims.s!=NULL) {
+		pkg_free(dupclaims.s);
+	}
+	if(jwt!=NULL) {
+		jwt_free(jwt);
+	}
+	return -1;
+}
+
+/**
+ *
+ */
+static int w_jwt_generate(sip_msg_t* msg, char* pkey, char* palg, char* pclaims)
+{
+	str skey = STR_NULL;
+	str salg = STR_NULL;
+	str sclaims = STR_NULL;
+
+	if (fixup_get_svalue(msg, (gparam_t*)pkey, &skey) != 0) {
+		LM_ERR("cannot get path to the key file\n");
+		return -1;
+	}
+	if (fixup_get_svalue(msg, (gparam_t*)palg, &salg) != 0) {
+		LM_ERR("cannot get algorithm value\n");
+		return -1;
+	}
+
+	if (fixup_get_svalue(msg, (gparam_t*)pclaims, &sclaims) != 0) {
+		LM_ERR("cannot get claims value\n");
+		return -1;
+	}
+
+	return ki_jwt_generate(msg, &skey, &salg, &sclaims);
+}
+
+/**
+ *
+ */
+static int ki_jwt_verify(sip_msg_t* msg, str *key, str *alg, str *claims,
+		str *jwtval)
+{
+	str dupclaims = STR_NULL;
+	jwt_alg_t valg = JWT_ALG_NONE;
+	str kdata = STR_NULL;
+	time_t iat;
+	FILE *fpk = NULL;
+	unsigned char keybuf[10240];
+	size_t keybuf_len = 0;
+	param_t* params_list = NULL;
+	param_hooks_t phooks;
+	param_t *pit = NULL;
+	int ret = 0;
+	jwt_t *jwt = NULL;
+	jwt_valid_t *jwt_valid = NULL;
+	str sparams = STR_NULL;
+
+	if(key==NULL || key->s==NULL || alg==NULL || alg->s==NULL
+			|| claims==NULL || claims->s==NULL || claims->len<=0
+			|| jwtval==NULL || jwtval->s==NULL || jwtval->len<=0) {
+		LM_ERR("invalid parameters\n");
+		return -1;
+	}
+
+	_jwt_verify_status = 0;
+
+	valg = jwt_str_alg(alg->s);
+	if (valg == JWT_ALG_INVAL) {
+		LM_ERR("not supported algorithm: %s\n", alg->s);
+		return -1;
+	}
+	if(pkg_str_dup(&dupclaims, claims)<0) {
+		LM_ERR("failed to duplicate claims\n");
+		return -1;
+	}
+	jwt_fcache_get(key, &kdata);
+	if(kdata.s==NULL) {
+		fpk= fopen(key->s, "r");
+		if(fpk==NULL) {
+			LM_ERR("failed to read key file: %s\n", key->s);
+			goto error;
+		}
+		keybuf_len = fread(keybuf, 1, sizeof(keybuf), fpk);
+		fclose(fpk);
+		if(keybuf_len==0) {
+			LM_ERR("unable to read key file content: %s\n", key->s);
+			goto error;
+		}
+		keybuf[keybuf_len] = '\0';
+		kdata.s = (char*)keybuf;
+		kdata.len = (int)keybuf_len;
+		jwt_fcache_add(key, &kdata);
+	}
+	sparams = dupclaims;
+	if(sparams.s[sparams.len-1]==';') {
+		sparams.len--;
+	}
+	if (parse_params(&sparams, CLASS_ANY, &phooks, &params_list)<0) {
+		LM_ERR("failed to parse claims\n");
+		goto error;
+	}
+
+	ret = jwt_valid_new(&jwt_valid, valg);
+	if (ret != 0 || jwt_valid == NULL) {
+		LM_ERR("failed to initialize jwt valid\n");
+		goto error;
+	}
+
+	iat = time(NULL);
+	jwt_valid_set_headers(jwt_valid, 1);
+	jwt_valid_set_now(jwt_valid, iat);
+
+	for (pit = params_list; pit; pit=pit->next) {
+		if(pit->name.len>0 && pit->body.len>0) {
+			pit->name.s[pit->name.len] = '\0';
+			pit->body.s[pit->body.len] = '\0';
+			jwt_valid_add_grant(jwt_valid, pit->name.s, pit->body.s);
+		}
+	}
+
+	ret = jwt_decode(&jwt, jwtval->s, (unsigned char*)kdata.s, (size_t)kdata.len);
+	if (ret!=0 || jwt==NULL) {
+		LM_ERR("failed to decode jwt value\n");
+		goto error;
+	}
+	if (jwt_validate(jwt, jwt_valid) != 0) {
+		_jwt_verify_status = jwt_valid_get_status(jwt_valid);
+		LM_ERR("failed to validate jwt: %08x\n", _jwt_verify_status);
+		goto error;
+	}
+
+	free_params(params_list);
+	pkg_free(dupclaims.s);
+	jwt_free(jwt);
+	jwt_valid_free(jwt_valid);
+
+	return 1;
+
+error:
+	if(params_list!=NULL) {
+		free_params(params_list);
+	}
+	if(dupclaims.s!=NULL) {
+		pkg_free(dupclaims.s);
+	}
+	if(jwt!=NULL) {
+		jwt_free(jwt);
+	}
+	if(jwt_valid!=NULL) {
+		jwt_valid_free(jwt_valid);
+	}
+	return -1;
+}
+
+/**
+ *
+ */
+static int w_jwt_verify(sip_msg_t* msg, char* pkey, char* palg, char* pclaims,
+		char *pjwtval)
+{
+	str skey = STR_NULL;
+	str salg = STR_NULL;
+	str sclaims = STR_NULL;
+	str sjwtval = STR_NULL;
+
+	if (fixup_get_svalue(msg, (gparam_t*)pkey, &skey) != 0) {
+		LM_ERR("cannot get path to the key file\n");
+		return -1;
+	}
+	if (fixup_get_svalue(msg, (gparam_t*)palg, &salg) != 0) {
+		LM_ERR("cannot get algorithm value\n");
+		return -1;
+	}
+	if (fixup_get_svalue(msg, (gparam_t*)pclaims, &sclaims) != 0) {
+		LM_ERR("cannot get claims value\n");
+		return -1;
+	}
+	if (fixup_get_svalue(msg, (gparam_t*)pjwtval, &sjwtval) != 0) {
+		LM_ERR("cannot get jwt value\n");
+		return -1;
+	}
+
+	return ki_jwt_verify(msg, &skey, &salg, &sclaims, &sjwtval);
+}
+
+/**
+ *
+ */
+static int jwt_pv_get(sip_msg_t *msg, pv_param_t *param, pv_value_t *res)
+{
+	switch(param->pvn.u.isname.name.n)
+	{
+		case 0:
+			if(_jwt_result.s==NULL)
+				return pv_get_null(msg, param, res);
+			return pv_get_strval(msg, param, res, &_jwt_result);
+		case 1:
+			return pv_get_uintval(msg, param, res, _jwt_verify_status);
+		default:
+			return pv_get_null(msg, param, res);
+	}
+}
+
+/**
+ *
+ */
+static int jwt_pv_parse_name(pv_spec_t *sp, str *in)
+{
+	if(in->len==3 && strncmp(in->s, "val", 3)==0) {
+		sp->pvp.pvn.u.isname.name.n = 0;
+	} else if(in->len==6 && strncmp(in->s, "status", 6)==0) {
+		sp->pvp.pvn.u.isname.name.n = 1;
+	} else {
+		LM_ERR("unknown inner name [%.*s]\n", in->len, in->s);
+		return -1;
+	}
+	return 0;
+}
+
+/**
+ *
+ */
+/* clang-format off */
+static sr_kemi_t sr_kemi_jwt_exports[] = {
+	{ str_init("jwt"), str_init("jwt_generate"),
+		SR_KEMIP_INT, ki_jwt_generate,
+		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_STR,
+			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
+	},
+	{ str_init("jwt"), str_init("jwt_verify"),
+		SR_KEMIP_INT, ki_jwt_verify,
+		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_STR,
+			SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE }
+	},
+
+	{ {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } }
+};
+/* clang-format on */
+
+int mod_register(char *path, int *dlflags, void *p1, void *p2)
+{
+	sr_kemi_modules_add(sr_kemi_jwt_exports);
+	return 0;
+}

+ 6 - 1
src/modules/presence/presence_dmq.c

@@ -102,11 +102,15 @@ static int pres_dmq_init_proc()
 		}
 		}
 	}
 	}
 
 
+	if(publ_cache_mode==PS_PCACHE_RECORD && pres_subs_dbmode==NO_DB) {
+		goto finish;
+	}
+
 	if(!pa_db) {
 	if(!pa_db) {
 		LM_DBG("Initializing presence DB connection for pid (%d)\n", my_pid());
 		LM_DBG("Initializing presence DB connection for pid (%d)\n", my_pid());
 
 
 		if(pa_dbf.init == 0) {
 		if(pa_dbf.init == 0) {
-			LM_ERR("dmq_worker_init: database not bound\n");
+			LM_ERR("database not bound\n");
 			return -1;
 			return -1;
 		}
 		}
 
 
@@ -123,6 +127,7 @@ static int pres_dmq_init_proc()
 		}
 		}
 	}
 	}
 
 
+finish:
 	*pres_dmq_proc_init = 1;
 	*pres_dmq_proc_init = 1;
 
 
 	LM_DBG("process initialization complete\n");
 	LM_DBG("process initialization complete\n");

+ 3 - 2
src/modules/pv_headers/pvh_func.c

@@ -62,6 +62,7 @@ int pvh_collect_headers(struct sip_msg *msg)
 	char hvals[header_name_size][header_value_size];
 	char hvals[header_name_size][header_value_size];
 	int idx = 0, d_size = 0;
 	int idx = 0, d_size = 0;
 	str val_part = STR_NULL;
 	str val_part = STR_NULL;
+	char *marker = NULL;
 
 
 	if(pvh_hdrs_collected(msg)) {
 	if(pvh_hdrs_collected(msg)) {
 		LM_ERR("headers are already collected\n");
 		LM_ERR("headers are already collected\n");
@@ -95,10 +96,10 @@ int pvh_collect_headers(struct sip_msg *msg)
 		val.len = hf->body.len;
 		val.len = hf->body.len;
 		val.s = hf->body.s;
 		val.s = hf->body.s;
 
 
-		if(strchr(val.s, ',') != NULL
+		if(( marker = pvh_detect_split_char(val.s)) != NULL
 				&& str_hash_case_get(&split_headers, name.s, name.len)) {
 				&& str_hash_case_get(&split_headers, name.s, name.len)) {
 
 
-			if(pvh_split_values(&val, hvals, &d_size, 1) < 0) {
+			if(pvh_split_values(&val, hvals, &d_size, 1, marker) < 0) {
 				LM_ERR("could not parse %.*s header comma separated "
 				LM_ERR("could not parse %.*s header comma separated "
 					   "value",
 					   "value",
 						name.len, name.s);
 						name.len, name.s);

+ 1 - 1
src/modules/pv_headers/pvh_hash.c

@@ -37,7 +37,7 @@ int pvh_str_hash_init(struct str_hash_table *ht, str *keys, char *desc)
 	int idx = 0, d_size = 0;
 	int idx = 0, d_size = 0;
 	str val = STR_NULL;
 	str val = STR_NULL;
 
 
-	if(pvh_split_values(keys, split, &d_size, 0) < 0) {
+	if(pvh_split_values(keys, split, &d_size, 0, NULL) < 0) {
 		LM_ERR("could not parse %s param\n", desc);
 		LM_ERR("could not parse %s param\n", desc);
 		return -1;
 		return -1;
 	}
 	}

+ 42 - 8
src/modules/pv_headers/pvh_str.c

@@ -103,10 +103,39 @@ int pvh_extract_display_uri(char *suri, str *display, str *duri)
 	return 1;
 	return 1;
 }
 }
 
 
-int pvh_split_values(
-		str *s, char d[][header_value_size], int *d_size, int keep_spaces)
+char *pvh_detect_split_char(char *val)
 {
 {
-	char p;
+	char *quote_a = NULL, *quote_b = NULL;
+	char *split = NULL;
+
+	if(val == NULL)
+		return NULL;
+
+	split = strchr(val, ',');
+	if(split == NULL) {
+		LM_DBG("no split marker detected\n");
+		return NULL;
+	}
+
+	quote_a = strchr(val, '"');
+	if(quote_a == NULL || split < quote_a) {
+		LM_DBG("split marker detected[%ld], not between quotes\n", split - val);
+		return split;
+	}
+
+	quote_b = strchr(val + (split - quote_a + 1), '"');
+	if(quote_b == NULL) {
+		LM_DBG("split marker detected[%ld], quote occurrence unbalanced[%ld]\n",
+				split - val, quote_b - val);
+		return split;
+	}
+	return pvh_detect_split_char(val + (quote_b - val + 1));
+}
+
+int pvh_split_values(str *s, char d[][header_value_size], int *d_size,
+		int keep_spaces, char *marker)
+{
+	char *p = NULL;
 	int idx = 0, c_idx = 0;
 	int idx = 0, c_idx = 0;
 
 
 	*d_size = -1;
 	*d_size = -1;
@@ -115,12 +144,17 @@ int pvh_split_values(
 		*d_size = 0;
 		*d_size = 0;
 		return 1;
 		return 1;
 	}
 	}
-
+	if(!marker)
+		marker = pvh_detect_split_char(s->s);
 	while(idx < s->len) {
 	while(idx < s->len) {
-		strncpy(&p, s->s + idx++, 1);
-		if(keep_spaces == 0 && strncmp(&p, " ", 1) == 0)
+		p = s->s + idx++;
+		if(keep_spaces == 0 && strncmp(p, " ", 1) == 0)
 			continue;
 			continue;
-		if(strncmp(&p, ",", 1) == 0) {
+		if(p == marker) {
+			if(marker && idx < s->len) {
+				LM_DBG("search next split marker[%d]\n", idx);
+				marker = pvh_detect_split_char(p + 1);
+			}
 			if(c_idx == 0)
 			if(c_idx == 0)
 				continue;
 				continue;
 			if(c_idx + 1 < header_value_size)
 			if(c_idx + 1 < header_value_size)
@@ -131,7 +165,7 @@ int pvh_split_values(
 		}
 		}
 		if(c_idx == 0)
 		if(c_idx == 0)
 			(*d_size)++;
 			(*d_size)++;
-		strncpy(&d[*d_size][c_idx++], &p, 1);
+		strncpy(&d[*d_size][c_idx++], p, 1);
 	}
 	}
 
 
 	if(c_idx > 0) {
 	if(c_idx > 0) {

+ 3 - 2
src/modules/pv_headers/pvh_str.h

@@ -34,7 +34,8 @@ int pvh_str_new(str *s, int size);
 int pvh_str_free(str *s);
 int pvh_str_free(str *s);
 int pvh_str_copy(str *dst, str *src, unsigned int max_size);
 int pvh_str_copy(str *dst, str *src, unsigned int max_size);
 int pvh_extract_display_uri(char *suri, str *display, str *duri);
 int pvh_extract_display_uri(char *suri, str *display, str *duri);
-int pvh_split_values(
-		str *s, char d[][header_value_size], int *d_size, int keep_spaces);
+char *pvh_detect_split_char(char *s);
+int pvh_split_values(str *s, char d[][header_value_size], int *d_size,
+		int keep_spaces, char *marker);
 
 
 #endif /* PV_STR_H */
 #endif /* PV_STR_H */

+ 66 - 47
src/modules/registrar/README

@@ -85,15 +85,16 @@ Bogdan-Andre Iancu
 
 
               4.1. save(domain, [, flags [, uri]])
               4.1. save(domain, [, flags [, uri]])
               4.2. lookup(domain [, uri])
               4.2. lookup(domain [, uri])
-              4.3. lookup_branches(domain)
-              4.4. registered(domain [, uri [, match_option [,
+              4.3. lookup_to_dset(domain [, uri])
+              4.4. lookup_branches(domain)
+              4.5. registered(domain [, uri [, match_option [,
                       match_action]]])
                       match_action]]])
 
 
-              4.5. add_sock_hdr(hdr_name)
-              4.6. unregister(domain, uri[, ruid])
-              4.7. reg_fetch_contacts(domain, uri, profile)
-              4.8. reg_free_contacts(profile)
-              4.9. reg_send_reply()
+              4.6. add_sock_hdr(hdr_name)
+              4.7. unregister(domain, uri[, ruid])
+              4.8. reg_fetch_contacts(domain, uri, profile)
+              4.9. reg_free_contacts(profile)
+              4.10. reg_send_reply()
 
 
         5. Event Routes
         5. Event Routes
 
 
@@ -151,15 +152,16 @@ Bogdan-Andre Iancu
    1.34. Set use_expired_contacts parameter
    1.34. Set use_expired_contacts parameter
    1.35. save usage
    1.35. save usage
    1.36. lookup usage
    1.36. lookup usage
-   1.37. lookup_branches usage
-   1.38. registered usage
-   1.39. add_sock_hdr usage
-   1.40. unregister usage
-   1.41. reg_fetch_contacts usage
-   1.42. reg_free_contacts usage
-   1.43. reg_send_reply usage
-   1.44. event_route[usrloc:contact-expired] usage
-   1.45. $ulc(name) usage
+   1.37. lookup_to_dset usage
+   1.38. lookup_branches usage
+   1.39. registered usage
+   1.40. add_sock_hdr usage
+   1.41. unregister usage
+   1.42. reg_fetch_contacts usage
+   1.43. reg_free_contacts usage
+   1.44. reg_send_reply usage
+   1.45. event_route[usrloc:contact-expired] usage
+   1.46. $ulc(name) usage
 
 
 Chapter 1. Admin Guide
 Chapter 1. Admin Guide
 
 
@@ -217,13 +219,14 @@ Chapter 1. Admin Guide
 
 
         4.1. save(domain, [, flags [, uri]])
         4.1. save(domain, [, flags [, uri]])
         4.2. lookup(domain [, uri])
         4.2. lookup(domain [, uri])
-        4.3. lookup_branches(domain)
-        4.4. registered(domain [, uri [, match_option [, match_action]]])
-        4.5. add_sock_hdr(hdr_name)
-        4.6. unregister(domain, uri[, ruid])
-        4.7. reg_fetch_contacts(domain, uri, profile)
-        4.8. reg_free_contacts(profile)
-        4.9. reg_send_reply()
+        4.3. lookup_to_dset(domain [, uri])
+        4.4. lookup_branches(domain)
+        4.5. registered(domain [, uri [, match_option [, match_action]]])
+        4.6. add_sock_hdr(hdr_name)
+        4.7. unregister(domain, uri[, ruid])
+        4.8. reg_fetch_contacts(domain, uri, profile)
+        4.9. reg_free_contacts(profile)
+        4.10. reg_send_reply()
 
 
    5. Event Routes
    5. Event Routes
 
 
@@ -968,13 +971,14 @@ kamcmd cfg.set_now_int registrar use_expired_contacts 0
 
 
    4.1. save(domain, [, flags [, uri]])
    4.1. save(domain, [, flags [, uri]])
    4.2. lookup(domain [, uri])
    4.2. lookup(domain [, uri])
-   4.3. lookup_branches(domain)
-   4.4. registered(domain [, uri [, match_option [, match_action]]])
-   4.5. add_sock_hdr(hdr_name)
-   4.6. unregister(domain, uri[, ruid])
-   4.7. reg_fetch_contacts(domain, uri, profile)
-   4.8. reg_free_contacts(profile)
-   4.9. reg_send_reply()
+   4.3. lookup_to_dset(domain [, uri])
+   4.4. lookup_branches(domain)
+   4.5. registered(domain [, uri [, match_option [, match_action]]])
+   4.6. add_sock_hdr(hdr_name)
+   4.7. unregister(domain, uri[, ruid])
+   4.8. reg_fetch_contacts(domain, uri, profile)
+   4.9. reg_free_contacts(profile)
+   4.10. reg_send_reply()
 
 
 4.1.  save(domain, [, flags [, uri]])
 4.1.  save(domain, [, flags [, uri]])
 
 
@@ -1055,7 +1059,8 @@ save("location", "0x00", "sip:[email protected]");
    Example 1.36. lookup usage
    Example 1.36. lookup usage
 ...
 ...
 lookup("location");
 lookup("location");
-switch ($retcode) {
+                        switch ($retcode) {:1
+
     case -1:
     case -1:
     case -3:
     case -3:
         sl_send_reply("404", "Not Found");
         sl_send_reply("404", "Not Found");
@@ -1066,7 +1071,21 @@ switch ($retcode) {
 };
 };
 ...
 ...
 
 
-4.3.  lookup_branches(domain)
+4.3.  lookup_to_dset(domain [, uri])
+
+   Similar to lookup(...), but push the location contacts to destination
+   set, without changing the R-URI (first branch not changed, it creates
+   additional branches). For the meaning of the parameters and the return
+   codes, see the documentation for lookup(...) function.
+
+   This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
+
+   Example 1.37. lookup_to_dset usage
+...
+lookup_to_dset("location");
+...
+
+4.4.  lookup_branches(domain)
 
 
    The function performs lookup(domain) on r-uri and additional branches
    The function performs lookup(domain) on r-uri and additional branches
    (only branches that have no other attributes set than uri).
    (only branches that have no other attributes set than uri).
@@ -1078,12 +1097,12 @@ switch ($retcode) {
 
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
 
-   Example 1.37. lookup_branches usage
+   Example 1.38. lookup_branches usage
 ...
 ...
 lookup_branches("location");
 lookup_branches("location");
 ...
 ...
 
 
-4.4.  registered(domain [, uri [, match_option [, match_action]]])
+4.5.  registered(domain [, uri [, match_option [, match_action]]])
 
 
    The function returns true if the AOR in the URI is registered, false
    The function returns true if the AOR in the URI is registered, false
    otherwise. The function does not modify the message being process, it
    otherwise. The function does not modify the message being process, it
@@ -1110,7 +1129,7 @@ lookup_branches("location");
 
 
    This function can be used from ANY_ROUTE.
    This function can be used from ANY_ROUTE.
 
 
-   Example 1.38. registered usage
+   Example 1.39. registered usage
 ...
 ...
 if (registered("location")) {
 if (registered("location")) {
         sl_send_reply("100", "Trying");
         sl_send_reply("100", "Trying");
@@ -1124,7 +1143,7 @@ if (registered("location","$rz:$Au", 2)) {
 };
 };
 ...
 ...
 
 
-4.5.  add_sock_hdr(hdr_name)
+4.6.  add_sock_hdr(hdr_name)
 
 
    Adds a new header to the current REGISTER request with “hdr_name” which
    Adds a new header to the current REGISTER request with “hdr_name” which
    contains the description of the received socket (proto:ip:port)
    contains the description of the received socket (proto:ip:port)
@@ -1137,12 +1156,12 @@ if (registered("location","$rz:$Au", 2)) {
 
 
    This function can be used from REQUEST_ROUTE.
    This function can be used from REQUEST_ROUTE.
 
 
-   Example 1.39. add_sock_hdr usage
+   Example 1.40. add_sock_hdr usage
 ...
 ...
 add_sock_hdr("Sock-Info");
 add_sock_hdr("Sock-Info");
 ...
 ...
 
 
-4.6.  unregister(domain, uri[, ruid])
+4.7.  unregister(domain, uri[, ruid])
 
 
    The function removes contacts associated with 'uri' from the location
    The function removes contacts associated with 'uri' from the location
    database. If 'ruid' is provided a specific contact is removed, if
    database. If 'ruid' is provided a specific contact is removed, if
@@ -1167,7 +1186,7 @@ add_sock_hdr("Sock-Info");
      * -2 - Error in unregistering user
      * -2 - Error in unregistering user
      * -3 - Contacts for AOR not found
      * -3 - Contacts for AOR not found
 
 
-   Example 1.40. unregister usage
+   Example 1.41. unregister usage
 ...
 ...
 unregister("location", "$ru");
 unregister("location", "$ru");
 unregister("location", "sip:[email protected]");
 unregister("location", "sip:[email protected]");
@@ -1175,7 +1194,7 @@ unregister("location", "$ru", "$ulc(caller=>ruid)");
 unregister("location", "", "$ruid");
 unregister("location", "", "$ruid");
 ...
 ...
 
 
-4.7.  reg_fetch_contacts(domain, uri, profile)
+4.8.  reg_fetch_contacts(domain, uri, profile)
 
 
    The function fetches the contacts for 'uri' from table 'domain' to
    The function fetches the contacts for 'uri' from table 'domain' to
    pseudo-variable $ulc(profile).
    pseudo-variable $ulc(profile).
@@ -1191,13 +1210,13 @@ unregister("location", "", "$ruid");
 
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
 
-   Example 1.41. reg_fetch_contacts usage
+   Example 1.42. reg_fetch_contacts usage
 ...
 ...
 reg_fetch_contacts("location", "$ru", "callee");
 reg_fetch_contacts("location", "$ru", "callee");
 reg_fetch_contacts("location", "sip:[email protected]", "caller");
 reg_fetch_contacts("location", "sip:[email protected]", "caller");
 ...
 ...
 
 
-4.8.  reg_free_contacts(profile)
+4.9.  reg_free_contacts(profile)
 
 
    The function frees the contacts from pseudo-variable $ulc(profile).
    The function frees the contacts from pseudo-variable $ulc(profile).
    Should be called to release the content of a profile. Anyhow, fetching
    Should be called to release the content of a profile. Anyhow, fetching
@@ -1210,12 +1229,12 @@ reg_fetch_contacts("location", "sip:[email protected]", "caller");
 
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
 
-   Example 1.42. reg_free_contacts usage
+   Example 1.43. reg_free_contacts usage
 ...
 ...
 reg_free_contacts("callee");
 reg_free_contacts("callee");
 ...
 ...
 
 
-4.9.  reg_send_reply()
+4.10.  reg_send_reply()
 
 
    The function sends the SIP reply that is normally sent by save(...),
    The function sends the SIP reply that is normally sent by save(...),
    but that was skipped due to flag 0x2. It must be used after save(...,
    but that was skipped due to flag 0x2. It must be used after save(...,
@@ -1224,7 +1243,7 @@ reg_free_contacts("callee");
 
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
 
-   Example 1.43. reg_send_reply usage
+   Example 1.44. reg_send_reply usage
 ...
 ...
 save("location", "0x2");
 save("location", "0x2");
 ...
 ...
@@ -1240,7 +1259,7 @@ reg_send_reply();
    Executed when a contact in location table has expired. The variable
    Executed when a contact in location table has expired. The variable
    $ulc(exp=>...) is filled with the attributes of the expired contact.
    $ulc(exp=>...) is filled with the attributes of the expired contact.
 
 
-   Example 1.44. event_route[usrloc:contact-expired] usage
+   Example 1.45. event_route[usrloc:contact-expired] usage
 ...
 ...
 event_route[usrloc:contact-expired] {
 event_route[usrloc:contact-expired] {
     xlog("expired contact for $ulc(exp=>aor)\n");
     xlog("expired contact for $ulc(exp=>aor)\n");
@@ -1314,7 +1333,7 @@ event_route[usrloc:contact-expired] {
    The pseudo-variable accepts positive index value to access a specific
    The pseudo-variable accepts positive index value to access a specific
    contact record.
    contact record.
 
 
-   Example 1.45. $ulc(name) usage
+   Example 1.46. $ulc(name) usage
 ...
 ...
 if(reg_fetch_contacts("location", "$fu", "caller"))
 if(reg_fetch_contacts("location", "$fu", "caller"))
 {
 {

+ 25 - 1
src/modules/registrar/doc/registrar_admin.xml

@@ -1338,7 +1338,8 @@ save("location", "0x00", "sip:[email protected]");
 		<programlisting format="linespecific">
 		<programlisting format="linespecific">
 ...
 ...
 lookup("location");
 lookup("location");
-switch ($retcode) {
+			switch ($retcode) {:1
+
     case -1:
     case -1:
     case -3:
     case -3:
         sl_send_reply("404", "Not Found");
         sl_send_reply("404", "Not Found");
@@ -1352,6 +1353,29 @@ switch ($retcode) {
 		</example>
 		</example>
 	</section>
 	</section>
 
 
+	<section id="registrar.f.lookup_to_dset">
+		<title>
+		<function moreinfo="none">lookup_to_dset(domain [, uri])</function>
+		</title>
+		<para>
+			Similar to lookup(...), but push the location contacts to destination
+			set, without changing the R-URI (first branch not changed, it creates
+			additional branches). For the meaning of the parameters and the return
+			codes, see the documentation for lookup(...) function.
+		</para>
+		<para>
+		This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
+		</para>
+		<example>
+		<title><function>lookup_to_dset</function> usage</title>
+		<programlisting format="linespecific">
+...
+lookup_to_dset("location");
+...
+</programlisting>
+		</example>
+	</section>
+
 	<section id="registrar.f.lookup_branches">
 	<section id="registrar.f.lookup_branches">
 		<title>
 		<title>
 		<function moreinfo="none">lookup_branches(domain)</function>
 		<function moreinfo="none">lookup_branches(domain)</function>

+ 3 - 1
src/modules/tm/t_cancel.c

@@ -527,7 +527,9 @@ unsigned int t_uac_cancel( str *headers, str *body,
 		LM_ERR("send failed\n");
 		LM_ERR("send failed\n");
 		goto error1;
 		goto error1;
 	}
 	}
-	start_retr(cancel);
+	if(start_retr(cancel)!=0) {
+		LM_CRIT("failed to start retransmission for cancel %p\n", cancel);
+	}
 	/* </start_sending> */
 	/* </start_sending> */
 
 
 	return ret;
 	return ret;

+ 113 - 66
src/modules/uac/README

@@ -63,13 +63,14 @@ Ramona-Elena Modroiu
               4.5. uac_replace_to(uri)
               4.5. uac_replace_to(uri)
               4.6. uac_restore_to()
               4.6. uac_restore_to()
               4.7. uac_auth([mode])
               4.7. uac_auth([mode])
-              4.8. uac_req_send()
-              4.9. uac_reg_lookup(uuid, dst)
-              4.10. uac_reg_status(uuid)
-              4.11. uac_reg_request_to(user, mode)
-              4.12. uac_reg_enable(attr, val)
-              4.13. uac_reg_disable(attr, val)
-              4.14. uac_reg_refresh(luuid)
+              4.8. uac_auth_mode(vmode)
+              4.9. uac_req_send()
+              4.10. uac_reg_lookup(uuid, dst)
+              4.11. uac_reg_status(uuid)
+              4.12. uac_reg_request_to(user, mode)
+              4.13. uac_reg_enable(attr, val)
+              4.14. uac_reg_disable(attr, val)
+              4.15. uac_reg_refresh(luuid)
 
 
         5. Pseudo Variables
         5. Pseudo Variables
         6. Event Routes
         6. Event Routes
@@ -123,25 +124,26 @@ Ramona-Elena Modroiu
    1.27. uac_replace_to usage
    1.27. uac_replace_to usage
    1.28. uac_restore_to usage
    1.28. uac_restore_to usage
    1.29. uac_auth usage
    1.29. uac_auth usage
-   1.30. uac_req_send usage
-   1.31. uac_reg_lookup usage
-   1.32. uac_reg_status usage
-   1.33. uac_reg_request_to usage
-   1.34. uac_reg_enable usage
-   1.35. uac_reg_disable usage
-   1.36. uac_reg_refresh usage
-   1.37. event_route[uac:reply] usage
-   1.38. uac.reg_dump usage
-   1.39. uac.reg_info usage
-   1.40. uac.reg_enable usage
-   1.41. uac.reg_disable usage
-   1.42. uac.reg_unregister usage
-   1.43. uac.reg_reload usage
-   1.44. uac.reg_refresh usage
-   1.45. uac.reg_active usage
-   1.46. uac.reg_add usage
-   1.47. uac.reg_remove usage
-   1.48. lookup remote registrations usage
+   1.30. uac_auth_mode usage
+   1.31. uac_req_send usage
+   1.32. uac_reg_lookup usage
+   1.33. uac_reg_status usage
+   1.34. uac_reg_request_to usage
+   1.35. uac_reg_enable usage
+   1.36. uac_reg_disable usage
+   1.37. uac_reg_refresh usage
+   1.38. event_route[uac:reply] usage
+   1.39. uac.reg_dump usage
+   1.40. uac.reg_info usage
+   1.41. uac.reg_enable usage
+   1.42. uac.reg_disable usage
+   1.43. uac.reg_unregister usage
+   1.44. uac.reg_reload usage
+   1.45. uac.reg_refresh usage
+   1.46. uac.reg_active usage
+   1.47. uac.reg_add usage
+   1.48. uac.reg_remove usage
+   1.49. lookup remote registrations usage
 
 
 Chapter 1. Admin Guide
 Chapter 1. Admin Guide
 
 
@@ -187,13 +189,14 @@ Chapter 1. Admin Guide
         4.5. uac_replace_to(uri)
         4.5. uac_replace_to(uri)
         4.6. uac_restore_to()
         4.6. uac_restore_to()
         4.7. uac_auth([mode])
         4.7. uac_auth([mode])
-        4.8. uac_req_send()
-        4.9. uac_reg_lookup(uuid, dst)
-        4.10. uac_reg_status(uuid)
-        4.11. uac_reg_request_to(user, mode)
-        4.12. uac_reg_enable(attr, val)
-        4.13. uac_reg_disable(attr, val)
-        4.14. uac_reg_refresh(luuid)
+        4.8. uac_auth_mode(vmode)
+        4.9. uac_req_send()
+        4.10. uac_reg_lookup(uuid, dst)
+        4.11. uac_reg_status(uuid)
+        4.12. uac_reg_request_to(user, mode)
+        4.13. uac_reg_enable(attr, val)
+        4.14. uac_reg_disable(attr, val)
+        4.15. uac_reg_refresh(luuid)
 
 
    5. Pseudo Variables
    5. Pseudo Variables
    6. Event Routes
    6. Event Routes
@@ -631,13 +634,14 @@ end
    4.5. uac_replace_to(uri)
    4.5. uac_replace_to(uri)
    4.6. uac_restore_to()
    4.6. uac_restore_to()
    4.7. uac_auth([mode])
    4.7. uac_auth([mode])
-   4.8. uac_req_send()
-   4.9. uac_reg_lookup(uuid, dst)
-   4.10. uac_reg_status(uuid)
-   4.11. uac_reg_request_to(user, mode)
-   4.12. uac_reg_enable(attr, val)
-   4.13. uac_reg_disable(attr, val)
-   4.14. uac_reg_refresh(luuid)
+   4.8. uac_auth_mode(vmode)
+   4.9. uac_req_send()
+   4.10. uac_reg_lookup(uuid, dst)
+   4.11. uac_reg_status(uuid)
+   4.12. uac_reg_request_to(user, mode)
+   4.13. uac_reg_enable(attr, val)
+   4.14. uac_reg_disable(attr, val)
+   4.15. uac_reg_refresh(luuid)
 
 
 4.1.  uac_replace_from(display,uri)
 4.1.  uac_replace_from(display,uri)
 
 
@@ -821,7 +825,50 @@ failure_route[TRUNKAUTH] {
 }
 }
 ...
 ...
 
 
-4.8.  uac_req_send()
+4.8.  uac_auth_mode(vmode)
+
+   This function can be called only from failure route and will build the
+   authentication response header and insert it into the request without
+   sending anything.
+
+   If mode is set to 1, then the password has to be provided in HA1
+   format. The parameter can be a static integer or a variable holding an
+   integer value.
+
+   This function can be used from FAILURE_ROUTE.
+
+   Example 1.30. uac_auth_mode usage
+...
+modparam("uac","auth_username_avp","$avp(auser)")
+modparam("uac","auth_password_avp","$avp(apass)")
+modparam("uac","auth_realm_avp","$avp(arealm)")
+
+request_route {
+   ...
+   if(is_method("INVITE")) {
+      t_on_failure("TRUNKAUTH");
+   }
+   ...
+}
+
+failure_route[TRUNKAUTH] {
+
+    if (t_is_canceled()) {
+        exit;
+    }
+    if(t_check_status("401|407")) {
+        $avp(auser) = "test";
+        $avp(apass) = "test";
+        # $avp(apass) = "36d0a02793542b4961e8348347236dbf";
+        if (uac_auth_mode("1")) {
+            t_relay();
+        }
+        exit;
+    }
+}
+...
+
+4.9.  uac_req_send()
 
 
    This function sends a SIP message from the configuration file. The
    This function sends a SIP message from the configuration file. The
    message is built out of $uac_req(...) pseudo-variable.
    message is built out of $uac_req(...) pseudo-variable.
@@ -829,7 +876,7 @@ failure_route[TRUNKAUTH] {
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    BRANCH_ROUTE, ONREPLY_ROUTE, LOCAL_ROUTE.
    BRANCH_ROUTE, ONREPLY_ROUTE, LOCAL_ROUTE.
 
 
-   Example 1.30. uac_req_send usage
+   Example 1.31. uac_req_send usage
 ...
 ...
 $uac_req(method)="OPTIONS";
 $uac_req(method)="OPTIONS";
 $uac_req(ruri)="sip:kamailio.org";
 $uac_req(ruri)="sip:kamailio.org";
@@ -839,14 +886,14 @@ $uac_req(callid)=$(mb{s.md5});
 uac_req_send();
 uac_req_send();
 ...
 ...
 
 
-4.9.  uac_reg_lookup(uuid, dst)
+4.10.  uac_reg_lookup(uuid, dst)
 
 
    This function sets the PV dst to SIP URI that correspond to uuid in uac
    This function sets the PV dst to SIP URI that correspond to uuid in uac
    registrations table. uuid and dst must be pseudo-variables.
    registrations table. uuid and dst must be pseudo-variables.
 
 
    This function can be used from ANY_ROUTE.
    This function can be used from ANY_ROUTE.
 
 
-   Example 1.31. uac_reg_lookup usage
+   Example 1.32. uac_reg_lookup usage
 ...
 ...
 
 
 if(uac_reg_lookup("$rU", "$ru"))
 if(uac_reg_lookup("$rU", "$ru"))
@@ -855,7 +902,7 @@ if(uac_reg_lookup("$rU", "$ru"))
 }
 }
 ...
 ...
 
 
-4.10.  uac_reg_status(uuid)
+4.11.  uac_reg_status(uuid)
 
 
    This function returns the current registration status for the uuid.
    This function returns the current registration status for the uuid.
 
 
@@ -870,12 +917,12 @@ if(uac_reg_lookup("$rU", "$ru"))
 
 
    This function can be used from ANY_ROUTE.
    This function can be used from ANY_ROUTE.
 
 
-   Example 1.32. uac_reg_status usage
+   Example 1.33. uac_reg_status usage
 ...
 ...
 $var(status) = uac_reg_status("$rU");
 $var(status) = uac_reg_status("$rU");
 ...
 ...
 
 
-4.11.  uac_reg_request_to(user, mode)
+4.12.  uac_reg_request_to(user, mode)
 
 
    This function can be used to send an authenticated request to a remote
    This function can be used to send an authenticated request to a remote
    user in the uac registrations table. It sets the request-uri, dst-uri
    user in the uac registrations table. It sets the request-uri, dst-uri
@@ -895,7 +942,7 @@ $var(status) = uac_reg_status("$rU");
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    BRANCH_ROUTE.
    BRANCH_ROUTE.
 
 
-   Example 1.33. uac_reg_request_to usage
+   Example 1.34. uac_reg_request_to usage
 ...
 ...
 
 
 if(uac_reg_request_to("$fU", 0))
 if(uac_reg_request_to("$fU", 0))
@@ -914,7 +961,7 @@ failure_route[REMOTE_AUTH] {
 }
 }
 ...
 ...
 
 
-4.12.  uac_reg_enable(attr, val)
+4.13.  uac_reg_enable(attr, val)
 
 
    Enable a remote registration record based on a filter specified by
    Enable a remote registration record based on a filter specified by
    attribute and value. The attribute can be: l_uuid, l_username,
    attribute and value. The attribute can be: l_uuid, l_username,
@@ -923,12 +970,12 @@ failure_route[REMOTE_AUTH] {
 
 
    The SIP processing is done on the next timer routine.
    The SIP processing is done on the next timer routine.
 
 
-   Example 1.34. uac_reg_enable usage
+   Example 1.35. uac_reg_enable usage
 ...
 ...
    uac_reg_enable("l_uuid", "account123");
    uac_reg_enable("l_uuid", "account123");
 ...
 ...
 
 
-4.13.  uac_reg_disable(attr, val)
+4.14.  uac_reg_disable(attr, val)
 
 
    Disable a remote registration record based on a filter specified by
    Disable a remote registration record based on a filter specified by
    attribute and value. The attribute can be: l_uuid, l_username,
    attribute and value. The attribute can be: l_uuid, l_username,
@@ -937,18 +984,18 @@ failure_route[REMOTE_AUTH] {
 
 
    The SIP processing is done on the next timer routine.
    The SIP processing is done on the next timer routine.
 
 
-   Example 1.35. uac_reg_disable usage
+   Example 1.36. uac_reg_disable usage
 ...
 ...
    uac_reg_disable("l_uuid", "account123");
    uac_reg_disable("l_uuid", "account123");
 ...
 ...
 
 
-4.14.  uac_reg_refresh(luuid)
+4.15.  uac_reg_refresh(luuid)
 
 
    Refresh the uac remote registration record based on local uuid. If the
    Refresh the uac remote registration record based on local uuid. If the
    record was already loaded, new values are taken from database,
    record was already loaded, new values are taken from database,
    otherwise a new record is created.
    otherwise a new record is created.
 
 
-   Example 1.36. uac_reg_refresh usage
+   Example 1.37. uac_reg_refresh usage
 ...
 ...
    uac_reg_refresh("account123");
    uac_reg_refresh("account123");
 ...
 ...
@@ -972,7 +1019,7 @@ failure_route[REMOTE_AUTH] {
    then the event_route is executed twice, first for 401/407 and second
    then the event_route is executed twice, first for 401/407 and second
    for final reply of the transaction.
    for final reply of the transaction.
 
 
-   Example 1.37. event_route[uac:reply] usage
+   Example 1.38. event_route[uac:reply] usage
 ...
 ...
 $uac_req(method)="OPTIONS";
 $uac_req(method)="OPTIONS";
 $uac_req(ruri)="sip:kamailio.org";
 $uac_req(ruri)="sip:kamailio.org";
@@ -1012,7 +1059,7 @@ event_route[uac:reply] {
 
 
    Dump the content of remote registration table from memory.
    Dump the content of remote registration table from memory.
 
 
-   Example 1.38. uac.reg_dump usage
+   Example 1.39. uac.reg_dump usage
 ...
 ...
    kamcmd uac.reg_dump
    kamcmd uac.reg_dump
 ...
 ...
@@ -1033,7 +1080,7 @@ event_route[uac:reply] {
      * 16 (2^4) - registration initialized (after loading from database,
      * 16 (2^4) - registration initialized (after loading from database,
        the registration process was initialized)
        the registration process was initialized)
 
 
-   Example 1.39. uac.reg_info usage
+   Example 1.40. uac.reg_info usage
 ...
 ...
    kamcmd uac.reg_info l_uuid account123
    kamcmd uac.reg_info l_uuid account123
    kamcmd uac.reg_info l_uuid s:12345678
    kamcmd uac.reg_info l_uuid s:12345678
@@ -1047,7 +1094,7 @@ event_route[uac:reply] {
    matched against the value of the attribute in the remote registration
    matched against the value of the attribute in the remote registration
    record.
    record.
 
 
-   Example 1.40. uac.reg_enable usage
+   Example 1.41. uac.reg_enable usage
 ...
 ...
    kamcmd uac.reg_enable l_uuid account123
    kamcmd uac.reg_enable l_uuid account123
    kamcmd uac.reg_enable l_uuid s:12345678
    kamcmd uac.reg_enable l_uuid s:12345678
@@ -1061,7 +1108,7 @@ event_route[uac:reply] {
    matched against the value of the attribute in the remote registration
    matched against the value of the attribute in the remote registration
    record.
    record.
 
 
-   Example 1.41. uac.reg_disable usage
+   Example 1.42. uac.reg_disable usage
 ...
 ...
    kamcmd uac.reg_disable l_uuid account123
    kamcmd uac.reg_disable l_uuid account123
    kamcmd uac.reg_disable l_uuid s:12345678
    kamcmd uac.reg_disable l_uuid s:12345678
@@ -1075,7 +1122,7 @@ event_route[uac:reply] {
    should be matched against the value of the attribute in the remote
    should be matched against the value of the attribute in the remote
    registration record.
    registration record.
 
 
-   Example 1.42. uac.reg_unregister usage
+   Example 1.43. uac.reg_unregister usage
 ...
 ...
    kamcmd uac.reg_unregister l_uuid account123
    kamcmd uac.reg_unregister l_uuid account123
    kamcmd uac.reg_unregister l_uuid s:12345678
    kamcmd uac.reg_unregister l_uuid s:12345678
@@ -1088,7 +1135,7 @@ event_route[uac:reply] {
    150 seconds between reloads -- see the reg_gc_interval parameter for
    150 seconds between reloads -- see the reg_gc_interval parameter for
    more details.
    more details.
 
 
-   Example 1.43. uac.reg_reload usage
+   Example 1.44. uac.reg_reload usage
 ...
 ...
    kamcmd uac.reg_reload
    kamcmd uac.reg_reload
 ...
 ...
@@ -1099,7 +1146,7 @@ event_route[uac:reply] {
    the record exists in memory, it will be replaced with the new values
    the record exists in memory, it will be replaced with the new values
    loaded from database.
    loaded from database.
 
 
-   Example 1.44. uac.reg_refresh usage
+   Example 1.45. uac.reg_refresh usage
 ...
 ...
    kamcmd uac.reg_refresh account123
    kamcmd uac.reg_refresh account123
    kamcmd uac.reg_refresh s:12345678
    kamcmd uac.reg_refresh s:12345678
@@ -1111,7 +1158,7 @@ event_route[uac:reply] {
    1 enables remote registrations for all records and 0 disables doing
    1 enables remote registrations for all records and 0 disables doing
    them.
    them.
 
 
-   Example 1.45. uac.reg_active usage
+   Example 1.46. uac.reg_active usage
 ...
 ...
    kamctl rpc uac.reg_active 0
    kamctl rpc uac.reg_active 0
    kamctl rpc uac.reg_active 1
    kamctl rpc uac.reg_active 1
@@ -1142,7 +1189,7 @@ event_route[uac:reply] {
    Use a dot (.) if no value should be set for auth_password, auth_ha1, or
    Use a dot (.) if no value should be set for auth_password, auth_ha1, or
    contact_addr.
    contact_addr.
 
 
-   Example 1.46. uac.reg_add usage
+   Example 1.47. uac.reg_add usage
 ...
 ...
    kamcmd uac.reg_add ...
    kamcmd uac.reg_add ...
 ...
 ...
@@ -1151,7 +1198,7 @@ event_route[uac:reply] {
 
 
    Remove a UAC remote registration record by l_uuid.
    Remove a UAC remote registration record by l_uuid.
 
 
-   Example 1.47. uac.reg_remove usage
+   Example 1.48. uac.reg_remove usage
 ...
 ...
    kamcmd uac.reg_remove my_l_uuid
    kamcmd uac.reg_remove my_l_uuid
 ...
 ...
@@ -1207,7 +1254,7 @@ event_route[uac:reply] {
    if the call is coming from a remote SIP provider and can change the
    if the call is coming from a remote SIP provider and can change the
    R-URI to local username@domain. Afterwards you can run location lookup.
    R-URI to local username@domain. Afterwards you can run location lookup.
 
 
-   Example 1.48. lookup remote registrations usage
+   Example 1.49. lookup remote registrations usage
 ...
 ...
     if(uac_reg_lookup("$rU", "$ru")) {
     if(uac_reg_lookup("$rU", "$ru")) {
         xlog("request from a remote SIP provider [$ou => $ru]\n");
         xlog("request from a remote SIP provider [$ou => $ru]\n");

+ 51 - 0
src/modules/uac/doc/uac_admin.xml

@@ -884,6 +884,57 @@ failure_route[TRUNKAUTH] {
         exit;
         exit;
     }
     }
 }
 }
+...
+				</programlisting>
+			</example>
+		</section>
+		<section id="uac.f.uac_auth_mode">
+			<title>
+				<function moreinfo="none">uac_auth_mode(vmode)</function>
+			</title>
+			<para>
+			This function can be called only from failure route and will
+			build the authentication response header and insert it into the
+			request without sending anything.
+			</para>
+			<para>
+			If mode is set to 1, then the password has to be provided in HA1 format.
+			The parameter can be a static integer or a variable holding an integer value.
+			</para>
+			<para>
+			This function can be used from FAILURE_ROUTE.
+			</para>
+			<example>
+				<title><function>uac_auth_mode</function> usage</title>
+				<programlisting format="linespecific">
+...
+modparam("uac","auth_username_avp","$avp(auser)")
+modparam("uac","auth_password_avp","$avp(apass)")
+modparam("uac","auth_realm_avp","$avp(arealm)")
+
+request_route {
+   ...
+   if(is_method("INVITE")) {
+      t_on_failure("TRUNKAUTH");
+   }
+   ...
+}
+
+failure_route[TRUNKAUTH] {
+
+    if (t_is_canceled()) {
+        exit;
+    }
+    if(t_check_status("401|407")) {
+        $avp(auser) = "test";
+        $avp(apass) = "test";
+        # $avp(apass) = "36d0a02793542b4961e8348347236dbf";
+        if (uac_auth_mode("1")) {
+            t_relay();
+        }
+        exit;
+    }
+}
 ...
 ...
 				</programlisting>
 				</programlisting>
 			</example>
 			</example>

+ 2 - 0
src/modules/uac/uac.c

@@ -135,6 +135,8 @@ static cmd_export_t cmds[]={
 		REQUEST_ROUTE | BRANCH_ROUTE },
 		REQUEST_ROUTE | BRANCH_ROUTE },
 	{"uac_restore_to",  (cmd_function)w_restore_to,  0, 0, 0, REQUEST_ROUTE },
 	{"uac_restore_to",  (cmd_function)w_restore_to,  0, 0, 0, REQUEST_ROUTE },
 	{"uac_auth",	  (cmd_function)w_uac_auth,       0, 0, 0, FAILURE_ROUTE },
 	{"uac_auth",	  (cmd_function)w_uac_auth,       0, 0, 0, FAILURE_ROUTE },
+	{"uac_auth",      (cmd_function)w_uac_auth_mode,  1,
+			fixup_igp_null, fixup_free_igp_null, FAILURE_ROUTE },
 	{"uac_auth_mode", (cmd_function)w_uac_auth_mode,  1,
 	{"uac_auth_mode", (cmd_function)w_uac_auth_mode,  1,
 			fixup_igp_null, fixup_free_igp_null, FAILURE_ROUTE },
 			fixup_igp_null, fixup_free_igp_null, FAILURE_ROUTE },
 	{"uac_req_send",  (cmd_function)w_uac_req_send,   0, 0, 0, ANY_ROUTE},
 	{"uac_req_send",  (cmd_function)w_uac_req_send,   0, 0, 0, ANY_ROUTE},