Преглед изворни кода

Merge remote-tracking branch 'origin/master'

mojtaba пре 4 година
родитељ
комит
e6bad5f9b4

+ 2 - 2
etc/kamailio.cfg

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

+ 7 - 0
src/Makefile.groups

@@ -184,6 +184,9 @@ mod_list_uuid=uuid
 # - modules depending on ev library
 mod_list_ev=evapi
 
+# - modules depending on libjwt library
+mod_list_jwt=jwt
+
 # - modules depending on kazoo/rabbitmq
 mod_list_kazoo=kazoo
 
@@ -256,6 +259,7 @@ mod_list_all=$(sort $(mod_list_basic) $(mod_list_extra) \
 			   $(mod_list_kafka) \
 			   $(mod_list_mqtt) \
 			   $(mod_list_secsipid) \
+			   $(mod_list_jwt) \
 			   $(mod_list_rtp_media_server)
 
 
@@ -446,6 +450,9 @@ module_group_kuuid=$(mod_list_uuid)
 # pkg libev modules
 module_group_kev=$(mod_list_ev)
 
+# pkg jwt module
+module_group_kjwt=$(mod_list_jwt)
+
 # pkg kazoo module
 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
  * 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
  */
 
@@ -54,7 +54,7 @@ struct tcp_child{
 int init_tcp(void);
 void destroy_tcp(void);
 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_receive_loop(int unix_sock);
 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 */
-int tcp_init_children()
+int tcp_init_children(int *woneinit)
 {
 	int r, i;
 	int reader_fd_1; /* for comm. with the tcp children read  */
 	pid_t pid;
 	char si_desc[MAX_PT_DESC];
 	struct socket_info *si;
-	
+
 	/* 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*
 	 *  + no_listen_tcp */
 	for(r=0, si=tcp_listen; si; si=si->next, r++);
@@ -5051,12 +5051,12 @@ int tcp_init_children()
 	if (! tls_disable)
 		for (si=tls_listen; si; si=si->next, r++);
 #endif
-	
+
 	register_fds(r+tcp_max_connections+get_max_procs()-1 /* tcp main */);
 #if 0
 	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
-	 * - tcp_main (tcpconn_connect called simultaneously in all all the 
+	 * - tcp_main (tcpconn_connect called simultaneously in all all the
 	 *  processes) */
 	tcp_max_fd_no+=tcp_max_connections+get_max_procs()-1 /* tcp main */;
 #endif
@@ -5095,7 +5095,7 @@ int tcp_init_children()
 
 	/* create the tcp sock_info structures */
 	/* copy the sockets --moved to main_loop*/
-	
+
 	/* fork children & create the socket pairs*/
 	for(r=0; r<tcp_children_no; r++){
 		child_rank++;
@@ -5108,10 +5108,16 @@ int tcp_init_children()
 			goto error;
 		}else if (pid>0){
 			/* parent */
+			*woneinit = 1;
 		}else{
 			/* child */
 			bind_address=0; /* force a SEGFAULT if someone uses a non-init.
 							   bind address on tcp */
+			if(*woneinit==0) {
+				if(run_child_one_init_route()<0)
+					goto error;
+			}
+
 			tcp_receive_loop(reader_fd_1);
 		}
 	}

+ 7 - 1
src/main.c

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

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

@@ -31,6 +31,7 @@
 #include "carrierroute.h"
 #include "cr_db.h"
 #include "cr_carrier.h"
+#include "cr_domain.h"
 #include "config.h"
 #include <stdio.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) {
 	db1_res_t * res = NULL;
-	db1_res_t * prob_res = NULL;
 	db_row_t * row = NULL;
-	int i, ret;
+	int i, j, ret;
 	struct carrier_data_t * tmp_carrier_data;
 	static str query_str;
 	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;
-	crboolean query_done = crfalse;
 	do {
 		LM_DBG("loading, cycle %d", n++);
 		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;
 			}
 
+
 			if (add_route(rd,
 					row->values[COL_CARRIER].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) {
 				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)) {
@@ -439,6 +411,16 @@ int load_route_data_db(struct route_data_t * rd) {
 		}
 	} 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);
 	res = NULL;
 	
@@ -493,8 +475,5 @@ errout:
 	if (res) {
 		carrierroute_dbf.free_result(carrierroute_dbh, res);
 	}
-	if (prob_res) {
-		carrierroute_dbf.free_result(carrierroute_dbh, prob_res);
-	}
 	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);
 
-typedef enum {
-	crfalse = 0,
-	crtrue = 1
-} crboolean;
-
 #endif

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

@@ -40,6 +40,7 @@
 struct domain_data_t {
 	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. */
+	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 * 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 {
 			contact = r->contacts;
 			/* skip expired contacts */
-			while ((contact) && (contact->expires <= tc))
+			while ((contact) && (contact->expires > 0) && (contact->expires <= tc))
 				contact = contact->next;
 			/* any contacts left? */
 			if (contact) {

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

@@ -13,8 +13,8 @@
  * 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 
+ * 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
  *
  */
@@ -46,7 +46,7 @@ str match_exp_column=   str_init(MATCH_EXP_COL);
 str match_len_column=   str_init(MATCH_LEN_COL);
 str subst_exp_column=   str_init(SUBST_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_match_dynamic;
@@ -75,8 +75,9 @@ void list_rule(dpl_node_t * );
 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;
 
-	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");
 		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));
 	if(!p){
 		LM_ERR("out of shm memory\n");
 		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");
 	if(init_db_data() != 0)
@@ -223,15 +224,15 @@ int init_data(void)
 
 void destroy_data(void)
 {
-	if(rules_hash){
+	if(dp_rules_hash){
 		destroy_hash(0);
 		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;
 
 	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");
 		return 0;
 	}
@@ -263,7 +264,7 @@ int dp_load_db(void)
 	}
 
 	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){
 			LM_ERR("failed to query database!\n");
 			return -1;
@@ -285,8 +286,8 @@ int dp_load_db(void)
 
 	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){
 		LM_WARN("no data in the db\n");
@@ -302,7 +303,7 @@ int dp_load_db(void)
 			if((rule = build_rule(values)) ==0 )
 				goto err2;
 
-			if(add_rule2hash(rule , *next_idx) != 0)
+			if(add_rule2hash(rule, *dp_next_idx) != 0)
 				goto err2;
 
 		}
@@ -321,16 +322,16 @@ int dp_load_db(void)
 
 end:
 	/*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);
 	return 0;
 
 err2:
 	if(rule)	destroy_rule(rule);
-	destroy_hash(*next_idx);
+	destroy_hash(*dp_next_idx);
 	dp_dbf.free_result(dp_db_handle, res);
-	*next_idx = *crt_idx; 
+	*dp_next_idx = *dp_crt_idx;
 	return -1;
 }
 
@@ -550,7 +551,7 @@ int add_rule2hash(dpl_node_t * rule, int h_index)
 	dpl_index_p indexp, last_indexp, new_indexp;
 	int new_id;
 
-	if(!rules_hash){
+	if(!dp_rules_hash){
 		LM_ERR("data not allocated\n");
 		return -1;
 	}
@@ -558,7 +559,7 @@ int add_rule2hash(dpl_node_t * rule, int h_index)
 	new_id = 0;
 
 	/*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)
 		if(crt_idp->dp_id == rule->dpid)
 			break;
@@ -577,7 +578,7 @@ int add_rule2hash(dpl_node_t * rule, int h_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){
 		if(indexp->len == rule->matchlen)
 			goto add_rule;
@@ -617,8 +618,8 @@ add_rule:
 	indexp->last_rule = rule;
 
 	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 "
 			"index with %i len\n", rule->dpid, rule->matchlen,
@@ -639,10 +640,10 @@ void destroy_hash(int index)
 	dpl_index_p indexp;
 	dpl_node_p rulep;
 
-	if(!rules_hash[index])
+	if(!dp_rules_hash[index])
 		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;){
 
@@ -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);
 		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)
 		return;
 
-	LM_DBG("destroying rule with priority %i\n", 
+	LM_DBG("destroying rule with priority %i\n",
 			rule->pr);
 
 	if(rule->match_comp)
@@ -708,10 +709,10 @@ dpl_id_p select_dpid(int id)
 {
 	dpl_id_p idp;
 
-	if(!rules_hash || !crt_idx)
+	if(!dp_rules_hash || !dp_crt_idx)
 		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)
 			return idp;
 
@@ -727,10 +728,10 @@ void list_hash(int h_index)
 	dpl_node_p rulep;
 
 
-	if(!rules_hash[h_index])
+	if(!dp_rules_hash[h_index])
 		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);
 		for(indexp=crt_idp->first_index; indexp!=NULL;indexp= indexp->next){
 			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 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)
 
 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));
 
-	crt_idx = p;
-	next_idx = p + 1;
+	ds_crt_idx = p;
+	ds_next_idx = p + 1;
 	ds_list_nr = p + 2;
-	*crt_idx = *next_idx = 0;
+	*ds_crt_idx = *ds_next_idx = 0;
 
 	return 0;
 }
@@ -784,7 +784,7 @@ int ds_load_list(char *lfile)
 	str uri;
 	str attrs;
 
-	if((*crt_idx) != (*next_idx)) {
+	if((*ds_crt_idx) != (*ds_next_idx)) {
 		LM_WARN("load command already generated, aborting reload...\n");
 		return 0;
 	}
@@ -802,8 +802,8 @@ int ds_load_list(char *lfile)
 
 	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);
 	while(p) {
@@ -878,7 +878,7 @@ int ds_load_list(char *lfile)
 		attrs.len = p - attrs.s;
 
 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) {
 			LM_WARN("unable to add destination %.*s to set %d -- skipping\n",
 					uri.len, uri.s, id);
@@ -890,7 +890,7 @@ next_line:
 		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");
 		goto error;
 	}
@@ -899,7 +899,7 @@ next_line:
 	f = NULL;
 	/* Update list - should it be sync'ed? */
 	_ds_list_nr = setn;
-	*crt_idx = *next_idx;
+	*ds_crt_idx = *ds_next_idx;
 
 	LM_DBG("found [%d] dest sets\n", _ds_list_nr);
 
@@ -909,8 +909,8 @@ next_line:
 error:
 	if(f != NULL)
 		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;
 }
 
@@ -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");
 		return 0;
 	}
@@ -1079,8 +1079,8 @@ int ds_load_db(void)
 	}
 
 	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++) {
 		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:"");
-		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) {
 			dest_errs++;
 			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");
 		goto err2;
 	}
@@ -1147,7 +1147,7 @@ int ds_load_db(void)
 
 	/* update data - should it be sync'ed? */
 	_ds_list_nr = setn;
-	*crt_idx = *next_idx;
+	*ds_crt_idx = *ds_next_idx;
 
 	LM_DBG("found [%d] dest sets\n", _ds_list_nr);
 
@@ -1158,9 +1158,9 @@ int ds_load_db(void)
 	return 0;
 
 err2:
-	ds_avl_destroy(&ds_lists[*next_idx]);
+	ds_avl_destroy(&ds_lists[*ds_next_idx]);
 	ds_dbf.free_result(ds_db_handle, res);
-	*next_idx = *crt_idx;
+	*ds_next_idx = *ds_crt_idx;
 
 	return -1;
 }
@@ -1174,8 +1174,8 @@ int ds_destroy_list(void)
 		shm_free(ds_lists);
 	}
 
-	if(crt_idx)
-		shm_free(crt_idx);
+	if(ds_crt_idx)
+		shm_free(ds_crt_idx);
 
 	return 0;
 }
@@ -1615,7 +1615,7 @@ int ds_load_replace(struct sip_msg *msg, str *duid)
 	}
 	set = it->dset;
 	/* 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);
 		LM_ERR("destination set [%d] not found\n", set);
 		return -1;
@@ -1676,7 +1676,7 @@ int ds_load_remove_byid(int set, str *duid)
 	int i;
 
 	/* 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);
 		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 */
-	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);
 		return -1;
 	}
@@ -2579,7 +2579,7 @@ void ds_add_dest_cb(ds_set_t *node, int i, void *arg)
 	int setn;
 
 	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) {
 		LM_WARN("failed to add destination in group %d - %.*s\n",
 				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;
 	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
 	ds_iter_set(_ds_list, &ds_add_dest_cb, NULL);
 
 	// add new destination
 	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);
 		if(ds_load_mode==1) {
 			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");
 		goto error;
 	}
 
 	_ds_list_nr = setn;
-	*crt_idx = *next_idx;
+	*ds_crt_idx = *ds_next_idx;
 
 	ds_log_sets();
 	return 0;
 
 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;
 }
 
@@ -2637,7 +2637,7 @@ void ds_filter_dest_cb(ds_set_t *node, int i, void *arg)
 		return;
 
 	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) {
 		LM_WARN("failed to add destination in group %d - %.*s\n",
 				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.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
 	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");
 		goto error;
 	}
 
 	_ds_list_nr = setn;
-	*crt_idx = *next_idx;
+	*ds_crt_idx = *ds_next_idx;
 
 	ds_log_sets();
 	return 0;
 
 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;
 }
 
@@ -2833,7 +2833,7 @@ int ds_update_latency(int group, str *address, int code)
 	}
 
 	/* 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);
 		return -1;
 	}
@@ -2927,7 +2927,7 @@ int ds_get_state(int group, str *address)
 	}
 
 	/* 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);
 		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 */
-	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);
 		return -1;
 	}
@@ -3162,7 +3162,7 @@ int ds_reinit_state(int group, str *address, int state)
 	}
 
 	/* 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);
 		return -1;
 	}
@@ -3203,7 +3203,7 @@ int ds_reinit_duid_state(int group, str *vduid, int state)
 	}
 
 	/* 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);
 		return -1;
 	}
@@ -3244,7 +3244,7 @@ int ds_reinit_state_all(int group, int state)
 	}
 
 	/* 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);
 		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 *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");
 		rpc->fault(ctx, 500, "No Destination Sets");
 		return;
@@ -1649,12 +1649,12 @@ static void dispatcher_rpc_list(rpc_t *rpc, void *ctx)
 		rpc->fault(ctx, 500, "Internal error root reply");
 		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");
 		return;
 	}
 
-	ds_rpc_print_set(ds_list, rpc, ctx, ih);
+	ds_rpc_print_set(dslist, rpc, ctx, ih);
 
 	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) {
 		LM_DBG("Initializing presence DB connection for pid (%d)\n", my_pid());
 
 		if(pa_dbf.init == 0) {
-			LM_ERR("dmq_worker_init: database not bound\n");
+			LM_ERR("database not bound\n");
 			return -1;
 		}
 
@@ -123,6 +127,7 @@ static int pres_dmq_init_proc()
 		}
 	}
 
+finish:
 	*pres_dmq_proc_init = 1;
 
 	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];
 	int idx = 0, d_size = 0;
 	str val_part = STR_NULL;
+	char *marker = NULL;
 
 	if(pvh_hdrs_collected(msg)) {
 		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.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)) {
 
-			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 "
 					   "value",
 						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;
 	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);
 		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;
 }
 
-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;
 
 	*d_size = -1;
@@ -115,12 +144,17 @@ int pvh_split_values(
 		*d_size = 0;
 		return 1;
 	}
-
+	if(!marker)
+		marker = pvh_detect_split_char(s->s);
 	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;
-		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)
 				continue;
 			if(c_idx + 1 < header_value_size)
@@ -131,7 +165,7 @@ int pvh_split_values(
 		}
 		if(c_idx == 0)
 			(*d_size)++;
-		strncpy(&d[*d_size][c_idx++], &p, 1);
+		strncpy(&d[*d_size][c_idx++], p, 1);
 	}
 
 	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_copy(str *dst, str *src, unsigned int max_size);
 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 */

+ 66 - 47
src/modules/registrar/README

@@ -85,15 +85,16 @@ Bogdan-Andre Iancu
 
               4.1. save(domain, [, flags [, 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]]])
 
-              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
 
@@ -151,15 +152,16 @@ Bogdan-Andre Iancu
    1.34. Set use_expired_contacts parameter
    1.35. save 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
 
@@ -217,13 +219,14 @@ Chapter 1. Admin Guide
 
         4.1. save(domain, [, flags [, 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
 
@@ -968,13 +971,14 @@ kamcmd cfg.set_now_int registrar use_expired_contacts 0
 
    4.1. save(domain, [, flags [, 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]])
 
@@ -1055,7 +1059,8 @@ save("location", "0x00", "sip:[email protected]");
    Example 1.36. lookup usage
 ...
 lookup("location");
-switch ($retcode) {
+                        switch ($retcode) {:1
+
     case -1:
     case -3:
         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
    (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.
 
-   Example 1.37. lookup_branches usage
+   Example 1.38. lookup_branches usage
 ...
 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
    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.
 
-   Example 1.38. registered usage
+   Example 1.39. registered usage
 ...
 if (registered("location")) {
         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
    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.
 
-   Example 1.39. add_sock_hdr usage
+   Example 1.40. add_sock_hdr usage
 ...
 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
    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
      * -3 - Contacts for AOR not found
 
-   Example 1.40. unregister usage
+   Example 1.41. unregister usage
 ...
 unregister("location", "$ru");
 unregister("location", "sip:[email protected]");
@@ -1175,7 +1194,7 @@ unregister("location", "$ru", "$ulc(caller=>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
    pseudo-variable $ulc(profile).
@@ -1191,13 +1210,13 @@ unregister("location", "", "$ruid");
 
    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", "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).
    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.
 
-   Example 1.42. reg_free_contacts usage
+   Example 1.43. reg_free_contacts usage
 ...
 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(...),
    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.
 
-   Example 1.43. reg_send_reply usage
+   Example 1.44. reg_send_reply usage
 ...
 save("location", "0x2");
 ...
@@ -1240,7 +1259,7 @@ reg_send_reply();
    Executed when a contact in location table has expired. The variable
    $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] {
     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
    contact record.
 
-   Example 1.45. $ulc(name) usage
+   Example 1.46. $ulc(name) usage
 ...
 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">
 ...
 lookup("location");
-switch ($retcode) {
+			switch ($retcode) {:1
+
     case -1:
     case -3:
         sl_send_reply("404", "Not Found");
@@ -1352,6 +1353,29 @@ switch ($retcode) {
 		</example>
 	</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">
 		<title>
 		<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");
 		goto error1;
 	}
-	start_retr(cancel);
+	if(start_retr(cancel)!=0) {
+		LM_CRIT("failed to start retransmission for cancel %p\n", cancel);
+	}
 	/* </start_sending> */
 
 	return ret;

+ 113 - 66
src/modules/uac/README

@@ -63,13 +63,14 @@ Ramona-Elena Modroiu
               4.5. uac_replace_to(uri)
               4.6. uac_restore_to()
               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
         6. Event Routes
@@ -123,25 +124,26 @@ Ramona-Elena Modroiu
    1.27. uac_replace_to usage
    1.28. uac_restore_to 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
 
@@ -187,13 +189,14 @@ Chapter 1. Admin Guide
         4.5. uac_replace_to(uri)
         4.6. uac_restore_to()
         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
    6. Event Routes
@@ -631,13 +634,14 @@ end
    4.5. uac_replace_to(uri)
    4.6. uac_restore_to()
    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)
 
@@ -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
    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,
    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(ruri)="sip:kamailio.org";
@@ -839,14 +886,14 @@ $uac_req(callid)=$(mb{s.md5});
 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
    registrations table. uuid and dst must be pseudo-variables.
 
    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"))
@@ -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.
 
@@ -870,12 +917,12 @@ if(uac_reg_lookup("$rU", "$ru"))
 
    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");
 ...
 
-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
    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,
    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))
@@ -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
    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.
 
-   Example 1.34. uac_reg_enable usage
+   Example 1.35. uac_reg_enable usage
 ...
    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
    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.
 
-   Example 1.35. uac_reg_disable usage
+   Example 1.36. uac_reg_disable usage
 ...
    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
    record was already loaded, new values are taken from database,
    otherwise a new record is created.
 
-   Example 1.36. uac_reg_refresh usage
+   Example 1.37. uac_reg_refresh usage
 ...
    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
    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(ruri)="sip:kamailio.org";
@@ -1012,7 +1059,7 @@ event_route[uac:reply] {
 
    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
 ...
@@ -1033,7 +1080,7 @@ event_route[uac:reply] {
      * 16 (2^4) - registration initialized (after loading from database,
        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 s:12345678
@@ -1047,7 +1094,7 @@ event_route[uac:reply] {
    matched against the value of the attribute in the remote registration
    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 s:12345678
@@ -1061,7 +1108,7 @@ event_route[uac:reply] {
    matched against the value of the attribute in the remote registration
    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 s:12345678
@@ -1075,7 +1122,7 @@ event_route[uac:reply] {
    should be matched against the value of the attribute in the remote
    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 s:12345678
@@ -1088,7 +1135,7 @@ event_route[uac:reply] {
    150 seconds between reloads -- see the reg_gc_interval parameter for
    more details.
 
-   Example 1.43. uac.reg_reload usage
+   Example 1.44. uac.reg_reload usage
 ...
    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
    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 s:12345678
@@ -1111,7 +1158,7 @@ event_route[uac:reply] {
    1 enables remote registrations for all records and 0 disables doing
    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 1
@@ -1142,7 +1189,7 @@ event_route[uac:reply] {
    Use a dot (.) if no value should be set for auth_password, auth_ha1, or
    contact_addr.
 
-   Example 1.46. uac.reg_add usage
+   Example 1.47. uac.reg_add usage
 ...
    kamcmd uac.reg_add ...
 ...
@@ -1151,7 +1198,7 @@ event_route[uac:reply] {
 
    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
 ...
@@ -1207,7 +1254,7 @@ event_route[uac:reply] {
    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.
 
-   Example 1.48. lookup remote registrations usage
+   Example 1.49. lookup remote registrations usage
 ...
     if(uac_reg_lookup("$rU", "$ru")) {
         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;
     }
 }
+...
+				</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>
 			</example>

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

@@ -135,6 +135,8 @@ static cmd_export_t cmds[]={
 		REQUEST_ROUTE | BRANCH_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_mode,  1,
+			fixup_igp_null, fixup_free_igp_null, FAILURE_ROUTE },
 	{"uac_auth_mode", (cmd_function)w_uac_auth_mode,  1,
 			fixup_igp_null, fixup_free_igp_null, FAILURE_ROUTE },
 	{"uac_req_send",  (cmd_function)w_uac_req_send,   0, 0, 0, ANY_ROUTE},