src/modules/dispatcher/dispatch.c
b68fbcd1
 /*
31ccf6a2
  * dispatcher module
b68fbcd1
  *
31ccf6a2
  * Copyright (C) 2004-2006 FhG Fokus
a4ba0359
  * Copyright (C) 2005 Voice-System.ro
fb6f8e11
  * Copyright (C) 2015 Daniel-Constantin Mierla (asipto.com)
31ccf6a2
  *
27642a08
  * This file is part of Kamailio, a free SIP server.
31ccf6a2
  *
27642a08
  * Kamailio is free software; you can redistribute it and/or modify
31ccf6a2
  * 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
  *
27642a08
  * Kamailio is distributed in the hope that it will be useful,
31ccf6a2
  * 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.
  *
9cdc3e70
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
31ccf6a2
  */
 
aecdbe9b
 /*! \file
  * \ingroup dispatcher
  * \brief Dispatcher :: Dispatch
  */
 
31ccf6a2
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
ed4757c5
 #include <stdint.h>
65160302
 #include <time.h>
12a25ed7
 #include <math.h>
31ccf6a2
 
cf83221d
 #include "../../core/ut.h"
 #include "../../core/trim.h"
 #include "../../core/dprint.h"
 #include "../../core/action.h"
 #include "../../core/route.h"
 #include "../../core/dset.h"
 #include "../../core/mem/shm_mem.h"
 #include "../../core/parser/parse_uri.h"
 #include "../../core/parser/parse_from.h"
 #include "../../core/parser/parse_param.h"
11ff4226
 #include "../../core/xavp.h"
cf83221d
 #include "../../core/parser/digest/digest.h"
 #include "../../core/resolve.h"
 #include "../../core/lvalue.h"
eb38b3df
 #include "../../modules/tm/tm_load.h"
c1a50c9a
 #include "../../lib/srdb1/db.h"
 #include "../../lib/srdb1/db_res.h"
cf83221d
 #include "../../core/str.h"
 #include "../../core/script_cb.h"
6aa21738
 #include "../../core/kemi.h"
cf83221d
 #include "../../core/fmsg.h"
31ccf6a2
 
59c31fe7
 #include "ds_ht.h"
86e03df9
 #include "api.h"
31ccf6a2
 #include "dispatch.h"
 
d081ee4e
 #define DS_TABLE_VERSION 1
 #define DS_TABLE_VERSION2 2
 #define DS_TABLE_VERSION3 3
 #define DS_TABLE_VERSION4 4
c7d56382
 
92f66e05
 #define DS_ALG_HASHCALLID 0
 #define DS_ALG_HASHFROMURI 1
 #define DS_ALG_HASHTOURI 2
 #define DS_ALG_HASHRURI 3
 #define DS_ALG_ROUNDROBIN 4
 #define DS_ALG_HASHAUTHUSER 5
 #define DS_ALG_RANDOM 6
 #define DS_ALG_HASHPV 7
 #define DS_ALG_SERIAL 8
 #define DS_ALG_WEIGHT 9
 #define DS_ALG_CALLLOAD 10
 #define DS_ALG_RELWEIGHT 11
2ce57c44
 #define DS_ALG_PARALLEL 12
90e3033c
 #define DS_ALG_LATENCY 13
9cdc3e70
 
d14833d9
 /* increment call load */
 #define DS_LOAD_INC(dgrp, didx) do { \
 		lock_get(&(dgrp)->lock); \
 		(dgrp)->dlist[didx].dload++; \
 		lock_release(&(dgrp)->lock); \
 	} while(0)
 
 /* decrement call load */
 #define DS_LOAD_DEC(dgrp, didx) do { \
 		lock_get(&(dgrp)->lock); \
 		if(likely((dgrp)->dlist[didx].dload > 0)) { \
 			(dgrp)->dlist[didx].dload--; \
 		} \
 		lock_release(&(dgrp)->lock); \
 	} while(0)
 
c7d56382
 static int _ds_table_version = DS_TABLE_VERSION;
31ccf6a2
 
59c31fe7
 static ds_ht_t *_dsht_load = NULL;
65160302
 
44c5d1c0
 static int *_ds_ping_active = NULL;
31ccf6a2
 
a4ba0359
 extern int ds_force_dst;
6aa21738
 extern str ds_event_callback;
12a25ed7
 extern int ds_ping_latency_stats;
 extern float ds_latency_estimator_alpha;
d1cb2644
 extern int ds_attrs_none;
433577dd
 extern param_t *ds_db_extra_attrs_list;
fa96c3a6
 extern int ds_load_mode;
31ccf6a2
 
101e9af4
 static db_func_t ds_dbf;
d081ee4e
 static db1_con_t *ds_db_handle = NULL;
65160302
 
0838c3e4
 static ds_set_t **ds_lists = NULL;
65160302
 
0838c3e4
 static int *ds_list_nr = NULL;
 static int *ds_crt_idx = NULL;
 static int *ds_next_idx = NULL;
31ccf6a2
 
0838c3e4
 #define _ds_list (ds_lists[*ds_crt_idx])
101e9af4
 #define _ds_list_nr (*ds_list_nr)
 
9d59f5cb
 static void ds_run_route(struct sip_msg *msg, str *uri, char *route,
 		ds_rctx_t *rctx);
08563829
 
d081ee4e
 void shuffle_uint100array(unsigned int *arr);
 int ds_reinit_rweight_on_state_change(
 		int old_state, int new_state, ds_set_t *dset);
101e9af4
 
44c5d1c0
 /**
  *
  */
 int ds_ping_active_init(void)
 {
d081ee4e
 	if(_ds_ping_active != NULL)
44c5d1c0
 		return 0;
d081ee4e
 	_ds_ping_active = (int *)shm_malloc(sizeof(int));
 	if(_ds_ping_active == NULL) {
44c5d1c0
 		LM_ERR("no more shared memory\n");
 		return -1;
 	}
 	*_ds_ping_active = 1;
 	return 0;
 }
 
 /**
  *
  */
 int ds_ping_active_get(void)
 {
d081ee4e
 	if(_ds_ping_active == NULL)
44c5d1c0
 		return -1;
 	return *_ds_ping_active;
 }
 
 /**
  *
  */
 int ds_ping_active_set(int v)
 {
d081ee4e
 	if(_ds_ping_active == NULL)
44c5d1c0
 		return -1;
 	*_ds_ping_active = v;
 	return 0;
 }
 
9cdc3e70
 /**
  *
  */
 int ds_hash_load_init(unsigned int htsize, int expire, int initexpire)
 {
 	if(_dsht_load != NULL)
 		return 0;
 	_dsht_load = ds_ht_init(htsize, expire, initexpire);
 	if(_dsht_load == NULL)
 		return -1;
 	return 0;
 }
 
 /**
  *
  */
 int ds_hash_load_destroy(void)
 {
 	if(_dsht_load == NULL)
 		return -1;
 	ds_ht_destroy(_dsht_load);
 	_dsht_load = NULL;
 	return 0;
 }
 
 /**
97a198b4
  * Recursivly iterate over ds_set and execute callback
9cdc3e70
  */
80930085
 void ds_iter_set(ds_set_t *node, void (*ds_action_cb)(ds_set_t *node, int i, void *arg),
 		void *ds_action_arg)
9de77c2b
 {
ebf80aca
 	int i;
 
d081ee4e
 	if(!node)
996f50eb
 		return;
 
d081ee4e
 	for(i = 0; i < 2; ++i)
d9a9e5f5
 		ds_iter_set(node->next[i], ds_action_cb, ds_action_arg);
cbe2e236
 
d081ee4e
 	for(i = 0; i < node->nr; i++) {
d9a9e5f5
 		ds_action_cb(node, i, ds_action_arg);
9de77c2b
 	}
 
996f50eb
 	return;
 }
 
d9a9e5f5
 void ds_log_dst_cb(ds_set_t *node, int i, void *arg)
97a198b4
 {
 	LM_DBG("dst>> %d %.*s %d %d (%.*s,%d,%d,%d)\n", node->id,
 		node->dlist[i].uri.len, node->dlist[i].uri.s,
 		node->dlist[i].flags, node->dlist[i].priority,
 		node->dlist[i].attrs.duid.len, node->dlist[i].attrs.duid.s,
 		node->dlist[i].attrs.maxload, node->dlist[i].attrs.weight,
 		node->dlist[i].attrs.rweight);
 }
 
 /**
  * Recursivly print ds_set
  */
 void ds_log_set(ds_set_t *node)
 {
d9a9e5f5
 	ds_iter_set(node, &ds_log_dst_cb, NULL);
97a198b4
 
 	return;
 }
 
996f50eb
 /**
  *
  */
 int ds_log_sets(void)
 {
d081ee4e
 	if(_ds_list == NULL)
996f50eb
 		return -1;
 
d081ee4e
 	ds_log_set(_ds_list);
996f50eb
 
9de77c2b
 	return 0;
 }
 
9cdc3e70
 /**
  *
  */
4e7e355d
 int ds_init_data(void)
101e9af4
 {
d081ee4e
 	int *p;
101e9af4
 
d081ee4e
 	ds_lists = (ds_set_t **)shm_malloc(2 * sizeof(ds_set_t *));
 	if(!ds_lists) {
101e9af4
 		LM_ERR("Out of memory\n");
 		return -1;
 	}
d081ee4e
 	memset(ds_lists, 0, 2 * sizeof(ds_set_t *));
101e9af4
 
cbe2e236
 
d081ee4e
 	p = (int *)shm_malloc(3 * sizeof(int));
 	if(!p) {
101e9af4
 		LM_ERR("Out of memory\n");
 		return -1;
 	}
d081ee4e
 	memset(p, 0, 3 * sizeof(int));
101e9af4
 
0838c3e4
 	ds_crt_idx = p;
 	ds_next_idx = p + 1;
d081ee4e
 	ds_list_nr = p + 2;
0838c3e4
 	*ds_crt_idx = *ds_next_idx = 0;
101e9af4
 
 	return 0;
 }
 
9cdc3e70
 /**
  *
  */
d1cb2644
 int ds_set_attrs(ds_dest_t *dest, str *vattrs)
101e9af4
 {
d081ee4e
 	param_t *params_list = NULL;
65160302
 	param_hooks_t phooks;
d081ee4e
 	param_t *pit = NULL;
9cdc3e70
 	str param;
e00f6102
 	int tmp_ival = 0;
d1cb2644
 	str sattrs;
 
 	if(vattrs == NULL || vattrs->len <= 0) {
 		if(ds_attrs_none==0) {
 			return 0;
 		}
 		sattrs.s = "none=yes";
 		sattrs.len = 8;
 	} else {
 		sattrs = *vattrs;
 	}
 	if(sattrs.s[sattrs.len - 1] == ';')
 		sattrs.len--;
9cdc3e70
 	/* clone in shm */
d1cb2644
 	dest->attrs.body.s = (char *)shm_malloc(sattrs.len + 1);
d081ee4e
 	if(dest->attrs.body.s == NULL) {
9cdc3e70
 		LM_ERR("no more shm\n");
 		return -1;
 	}
d1cb2644
 	memcpy(dest->attrs.body.s, sattrs.s, sattrs.len);
 	dest->attrs.body.s[sattrs.len] = '\0';
 	dest->attrs.body.len = sattrs.len;
9cdc3e70
 
 	param = dest->attrs.body;
d081ee4e
 	if(parse_params(&param, CLASS_ANY, &phooks, &params_list) < 0)
65160302
 		return -1;
d081ee4e
 	for(pit = params_list; pit; pit = pit->next) {
 		if(pit->name.len == 4 && strncasecmp(pit->name.s, "duid", 4) == 0) {
9cdc3e70
 			dest->attrs.duid = pit->body;
1d304002
 		} else if(pit->name.len == 2
 				  && strncasecmp(pit->name.s, "cc", 2) == 0) {
 			str2sint(&pit->body, &dest->attrs.congestion_control);
d081ee4e
 		} else if(pit->name.len == 6
 				  && strncasecmp(pit->name.s, "weight", 6) == 0) {
e00f6102
 			tmp_ival = 0;
 			str2sint(&pit->body, &tmp_ival);
 			if(tmp_ival >= 1 && tmp_ival <= 100) {
 				dest->attrs.weight = tmp_ival;
 			} else {
 				dest->attrs.weight = 0;
e6f4e929
 				LM_ERR("weight %d not in 1-100 range - ignoring destination\n",
e00f6102
 						tmp_ival);
 			}
8429ef38
 		} else if(pit->name.len == 7
 				  && strncasecmp(pit->name.s, "latency", 7) == 0) {
 			int initial_latency = 0;
 			if (str2sint(&pit->body, &initial_latency) == 0)
 				latency_stats_init(&dest->latency_stats, initial_latency, 10000);
d081ee4e
 		} else if(pit->name.len == 7
 				  && strncasecmp(pit->name.s, "maxload", 7) == 0) {
65160302
 			str2sint(&pit->body, &dest->attrs.maxload);
d081ee4e
 		} else if(pit->name.len == 6
 				  && strncasecmp(pit->name.s, "socket", 6) == 0) {
aae77585
 			dest->attrs.socket = pit->body;
80930085
 		} else if(pit->name.len == 8
 				  && strncasecmp(pit->name.s, "sockname", 8) == 0) {
 			dest->attrs.sockname = pit->body;
d081ee4e
 		} else if(pit->name.len == 7
 				  && strncasecmp(pit->name.s, "rweight", 7) == 0) {
e00f6102
 			tmp_ival = 0;
 			str2sint(&pit->body, &tmp_ival);
 			if(tmp_ival >= 1 && tmp_ival <= 100) {
 				dest->attrs.rweight = tmp_ival;
d081ee4e
 			} else {
e00f6102
 				dest->attrs.rweight = 0;
e6f4e929
 				LM_WARN("rweight %d not in 1-100 range - ignoring\n", tmp_ival);
1d6c1d74
 			}
3f9c38be
 		} else if(pit->name.len == 9
 				&& strncasecmp(pit->name.s, "ping_from", 9) == 0) {
 			dest->attrs.ping_from = pit->body;
ebf80aca
 		} else if(pit->name.len == 7
74ef108f
 				  && strncasecmp(pit->name.s, "obproxy", 7) == 0) {
 			dest->attrs.obproxy = pit->body;
65160302
 		}
 	}
d081ee4e
 	if(params_list)
 		free_params(params_list);
65160302
 	return 0;
 }
 
9cdc3e70
 /**
  *
  */
8d9e6578
 ds_dest_t *pack_dest(str iuri, int flags, int priority, str *attrs, int dload)
65160302
 {
 	ds_dest_t *dp = NULL;
101e9af4
 	/* For DNS-Lookups */
 	static char hn[256];
4220c41f
 	char ub[512];
d081ee4e
 	struct hostent *he;
101e9af4
 	struct sip_uri puri;
aae77585
 	str host;
 	int port, proto;
fd79adb4
 	char c = 0;
4220c41f
 	str uri;
101e9af4
 
4220c41f
 	uri = iuri;
101e9af4
 	/* check uri */
4220c41f
 	if(parse_uri(uri.s, uri.len, &puri) != 0) {
 		if(iuri.len>4 && strncmp(iuri.s, "sip:", 4)!=0 && iuri.len<500) {
4be1c60b
 			memcpy(ub, "sip:", 4);
 			memcpy(ub+4, iuri.s, iuri.len);
4220c41f
 			ub[iuri.len+4] = '\0';
 			uri.s = ub;
 			uri.len = iuri.len+4;
 			if(parse_uri(uri.s, uri.len, &puri) != 0) {
 				LM_ERR("bad uri [%.*s]\n", iuri.len, iuri.s);
 				goto err;
 			} else {
 				LM_INFO("uri without sip scheme - fixing it: %.*s\n",
 						iuri.len, iuri.s);
 			}
 		} else {
 			LM_ERR("bad uri [%.*s]\n", iuri.len, iuri.s);
 			goto err;
 		}
 	}
 
 	if(puri.host.len > 254) {
 		LM_ERR("hostname in uri is too long [%.*s]\n", uri.len, uri.s);
101e9af4
 		goto err;
 	}
cbe2e236
 
2d7f748e
 	/* skip IPv6 references if IPv6 lookups are disabled */
d081ee4e
 	if(default_core_cfg.dns_try_ipv6 == 0 && puri.host.s[0] == '['
 			&& puri.host.s[puri.host.len - 1] == ']') {
2d7f748e
 		LM_DBG("skipping IPv6 record %.*s\n", puri.host.len, puri.host.s);
996f50eb
 		return NULL;
101e9af4
 	}
 
 	/* store uri */
d081ee4e
 	dp = (ds_dest_t *)shm_malloc(sizeof(ds_dest_t));
 	if(dp == NULL) {
101e9af4
 		LM_ERR("no more memory!\n");
 		goto err;
 	}
 	memset(dp, 0, sizeof(ds_dest_t));
 
d081ee4e
 	dp->uri.s = (char *)shm_malloc((uri.len + 1) * sizeof(char));
 	if(dp->uri.s == NULL) {
101e9af4
 		LM_ERR("no more memory!\n");
 		goto err;
 	}
 	strncpy(dp->uri.s, uri.s, uri.len);
d081ee4e
 	dp->uri.s[uri.len] = '\0';
101e9af4
 	dp->uri.len = uri.len;
aae77585
 
5affd4c7
 	dp->flags = flags;
9de77c2b
 	dp->priority = priority;
8d9e6578
 	dp->dload = dload;
101e9af4
 
d081ee4e
 	if(ds_set_attrs(dp, attrs) < 0) {
65160302
 		LM_ERR("cannot set attributes!\n");
 		goto err;
 	}
 
80930085
 	/* set send socket by name or address */
 	if(dp->attrs.sockname.s && dp->attrs.sockname.len > 0) {
 		dp->sock = ksr_get_socket_by_name(&dp->attrs.sockname);
 		if(dp->sock == 0) {
 			LM_ERR("non-local socket name <%.*s>\n", dp->attrs.sockname.len,
 					dp->attrs.sockname.s);
 			goto err;
 		}
 	} else if(dp->attrs.socket.s && dp->attrs.socket.len > 0) {
fd79adb4
 		/* parse_phostport(...) expects 0-terminated string
 		 * - after socket parameter is either ';' or '\0' */
28f4cab2
 		STR_VTOZ(dp->attrs.socket.s[dp->attrs.socket.len], c);
d081ee4e
 		if(parse_phostport(
 				   dp->attrs.socket.s, &host.s, &host.len, &port, &proto)
 				!= 0) {
 			LM_ERR("bad socket <%.*s>\n", dp->attrs.socket.len,
 					dp->attrs.socket.s);
28f4cab2
 			STR_ZTOV(dp->attrs.socket.s[dp->attrs.socket.len], c);
aae77585
 			goto err;
 		}
28f4cab2
 		STR_ZTOV(dp->attrs.socket.s[dp->attrs.socket.len], c);
d081ee4e
 		dp->sock = grep_sock_info(&host, (unsigned short)port, proto);
 		if(dp->sock == 0) {
 			LM_ERR("non-local socket <%.*s>\n", dp->attrs.socket.len,
 					dp->attrs.socket.s);
aae77585
 			goto err;
 		}
d081ee4e
 	} else if(ds_default_sockinfo) {
aae77585
 		dp->sock = ds_default_sockinfo;
 	}
 
101e9af4
 	/* The Hostname needs to be \0 terminated for resolvehost, so we
 	 * make a copy here. */
 	strncpy(hn, puri.host.s, puri.host.len);
d081ee4e
 	hn[puri.host.len] = '\0';
cbe2e236
 
1c58b8e0
 	/* Do a DNS-Lookup for the Host-Name, if not disabled via dst flags */
 	if(dp->flags & DS_NODNSARES_DST) {
 		dp->irmode |= DS_IRMODE_NOIPADDR;
 	} else {
 		he = resolvehost(hn);
 		if(he == 0) {
9eba5b67
 			LM_ERR("could not resolve %.*s (missing no-probing flag?!?)\n",
 					puri.host.len, puri.host.s);
 			goto err;
1c58b8e0
 		} else {
 			/* Store hostent in the dispatcher structure */
 			hostent2ip_addr(&dp->ip_address, he, 0);
9eba5b67
 		}
101e9af4
 	}
cbe2e236
 
6016958d
 	/* Copy the port out of the URI */
848394fa
 	dp->port = puri.port_no;
6016958d
 	/* Copy the proto out of the URI */
 	dp->proto = puri.proto;
101e9af4
 
996f50eb
 	return dp;
 err:
d081ee4e
 	if(dp != NULL) {
 		if(dp->uri.s != NULL)
996f50eb
 			shm_free(dp->uri.s);
614b6b05
 		if(dp->attrs.body.s != NULL)
 			shm_free(dp->attrs.body.s);
996f50eb
 		shm_free(dp);
 	}
 
 	return NULL;
 }
 
 /**
  *
  */
 int add_dest2list(int id, str uri, int flags, int priority, str *attrs,
8d9e6578
 		int list_idx, int *setn, int dload)
996f50eb
 {
 	ds_dest_t *dp = NULL;
d081ee4e
 	ds_set_t *sp = NULL;
996f50eb
 	ds_dest_t *dp0 = NULL;
 	ds_dest_t *dp1 = NULL;
 
8d9e6578
 	dp = pack_dest(uri, flags, priority, attrs, dload);
d081ee4e
 	if(!dp)
996f50eb
 		goto err;
 
d081ee4e
 	sp = ds_avl_insert(&ds_lists[list_idx], id, setn);
 	if(!sp) {
996f50eb
 		LM_ERR("no more memory.\n");
 		goto err;
 	}
 	sp->nr++;
 
d081ee4e
 	if(sp->dlist == NULL) {
9de77c2b
 		sp->dlist = dp;
 	} else {
 		dp1 = NULL;
 		dp0 = sp->dlist;
 		/* highest priority last -> reindex will copy backwards */
 		while(dp0) {
 			if(dp0->priority > dp->priority)
 				break;
 			dp1 = dp0;
d081ee4e
 			dp0 = dp0->next;
9de77c2b
 		}
d081ee4e
 		if(dp1 == NULL) {
9de77c2b
 			dp->next = sp->dlist;
 			sp->dlist = dp;
 		} else {
d081ee4e
 			dp->next = dp1->next;
9de77c2b
 			dp1->next = dp;
 		}
 	}
101e9af4
 
 	LM_DBG("dest [%d/%d] <%.*s>\n", sp->id, sp->nr, dp->uri.len, dp->uri.s);
cbe2e236
 
101e9af4
 	return 0;
 err:
d081ee4e
 	if(dp != NULL) {
 		if(dp->uri.s != NULL)
101e9af4
 			shm_free(dp->uri.s);
614b6b05
 		if(dp->attrs.body.s != NULL)
 			shm_free(dp->attrs.body.s);
101e9af4
 		shm_free(dp);
 	}
92da08fd
 
101e9af4
 	return -1;
 }
 
1d6c1d74
 
 /* for internal usage; arr must be arr[100] */
d081ee4e
 void shuffle_uint100array(unsigned int *arr)
 {
1d6c1d74
 	int k;
 	int j;
 	unsigned int t;
ebf80aca
 	if(arr == NULL)
 		return;
d081ee4e
 	for(j = 0; j < 100; j++) {
 		k = j + (kam_rand() % (100 - j));
1d6c1d74
 		t = arr[j];
 		arr[j] = arr[k];
 		arr[k] = t;
 	}
 }
 
 
 /**
  * Initialize the relative weight distribution for a destination set
  * - fill the array of 0..99 elements where to keep the index of the
  *   destination address to be used. The Nth call will use
  *   the address with the index at possition N%100
  */
 int dp_init_relative_weights(ds_set_t *dset)
 {
 	int j;
 	int k;
 	int t;
2bfef639
 	int *ds_dests_flags = NULL;
 	int *ds_dests_rweights = NULL;
 	int current_slice;
 	int rw_sum;
 	unsigned int last_insert;
1d6c1d74
 
fe35aab4
 	if(dset == NULL || dset->dlist == NULL || dset->nr < 2)
1d6c1d74
 		return -1;
1d401e0a
 
fe35aab4
 	/* local copy to avoid syncronization problems */
2bfef639
 	ds_dests_flags = pkg_malloc(sizeof(int) * dset->nr);
 	if(ds_dests_flags == NULL) {
 		LM_ERR("no more pkg\n");
 		return -1;
 	}
 	ds_dests_rweights = pkg_malloc(sizeof(int) * dset->nr);
 	if(ds_dests_rweights == NULL) {
 		LM_ERR("no more pkg\n");
 		pkg_free(ds_dests_flags);
 		return -1;
 	}
 
fe35aab4
 
 	/* needed to sync the rwlist access */
1d304002
 	lock_get(&dset->lock);
2bfef639
 	rw_sum = 0;
fe35aab4
 	/* find the sum of relative weights */
d081ee4e
 	for(j = 0; j < dset->nr; j++) {
fe35aab4
 		ds_dests_flags[j] = dset->dlist[j].flags;
 		ds_dests_rweights[j] = dset->dlist[j].attrs.rweight;
 		if(ds_skip_dst(ds_dests_flags[j]))
1d6c1d74
 			continue;
fe35aab4
 		rw_sum += ds_dests_rweights[j];
1d6c1d74
 	}
 
fe35aab4
 	if(rw_sum == 0)
 		goto ret;
1d6c1d74
 
 	/* fill the array based on the relative weight of each destination */
 	t = 0;
d081ee4e
 	for(j = 0; j < dset->nr; j++) {
fe35aab4
 		if(ds_skip_dst(ds_dests_flags[j]))
1d401e0a
 			continue;
1d6c1d74
 
2bfef639
 		current_slice =
 				ds_dests_rweights[j] * 100 / rw_sum; /* truncate here */
1d304002
 		LM_DBG("rw_sum[%d][%d][%d]\n",j, rw_sum, current_slice);
d081ee4e
 		for(k = 0; k < current_slice; k++) {
1d6c1d74
 			dset->rwlist[t] = (unsigned int)j;
 			t++;
 		}
 	}
1d304002
 
abd02c40
 	/* if the array was not completely filled (i.e., the sum of rweights is
1d6c1d74
 	 * less than 100 due to truncated), then use last address to fill the rest */
2bfef639
 	last_insert = t > 0 ? dset->rwlist[t - 1] : (unsigned int)(dset->nr - 1);
ba27633c
 	if(t < 100) {
 		LM_INFO("extra rweight %d for last active destination in group %d\n",
 				(100-t), dset->id);
 	}
d081ee4e
 	for(j = t; j < 100; j++)
abd02c40
 		dset->rwlist[j] = last_insert;
1d6c1d74
 
 	/* shuffle the content of the array in order to mix the selection
 	 * of the addresses (e.g., if first address has weight=20, avoid
 	 * sending first 20 calls to it, but ensure that within a 100 calls,
 	 * 20 go to first address */
 	shuffle_uint100array(dset->rwlist);
fe35aab4
 	goto ret;
 
 ret:
1d304002
 	lock_release(&dset->lock);
fe35aab4
 	pkg_free(ds_dests_flags);
 	pkg_free(ds_dests_rweights);
1d6c1d74
 	return 0;
 }
 
 
9cdc3e70
 /**
a2b52c52
  * Initialize the weight distribution for a destination set
d035d0fe
  * - fill the array of 0..99 elements where to keep the index of the
a2b52c52
  *   destination address to be used. The Nth call will use
  *   the address with the index at possition N%100
9cdc3e70
  */
65160302
 int dp_init_weights(ds_set_t *dset)
 {
 	int j;
 	int k;
 	int t;
 
d081ee4e
 	if(dset == NULL || dset->dlist == NULL)
65160302
 		return -1;
 
 	/* is weight set for dst list? (first address must have weight!=0) */
d081ee4e
 	if(dset->dlist[0].attrs.weight == 0)
65160302
 		return 0;
 
a2b52c52
 	/* first fill the array based on the weight of each destination
 	 * - the weight is the percentage (e.g., if weight=20, the afferent
 	 *   address gets its index 20 times in the array)
 	 * - if the sum of weights is more than 100, the addresses over the
 	 *   limit are ignored */
65160302
 	t = 0;
d081ee4e
 	for(j = 0; j < dset->nr; j++) {
 		for(k = 0; k < dset->dlist[j].attrs.weight; k++) {
 			if(t >= 100)
65160302
 				goto randomize;
 			dset->wlist[t] = (unsigned int)j;
 			t++;
 		}
 	}
a2b52c52
 	/* if the array was not completely filled (i.e., the sum of weights is
 	 * less than 100), then use last address to fill the rest */
ba27633c
 	if(t < 100) {
 		LM_INFO("extra weight %d for last destination in group %d\n",
 				(100-t), dset->id);
 	}
d081ee4e
 	for(; t < 100; t++)
 		dset->wlist[t] = (unsigned int)(dset->nr - 1);
65160302
 randomize:
a2b52c52
 	/* shuffle the content of the array in order to mix the selection
 	 * of the addresses (e.g., if first address has weight=20, avoid
 	 * sending first 20 calls to it, but ensure that within a 100 calls,
 	 * 20 go to first address */
1d6c1d74
 	shuffle_uint100array(dset->wlist);
65160302
 
 	return 0;
 }
 
aecdbe9b
 /*! \brief  compact destinations from sets for fast access */
d081ee4e
 int reindex_dests(ds_set_t *node)
101e9af4
 {
d081ee4e
 	int i = 0;
 	int j = 0;
98f9cace
 
d081ee4e
 	if(!node)
996f50eb
 		return 0;
 
d081ee4e
 	for(; i < 2; ++i) {
 		int rc = reindex_dests(node->next[i]);
 		if(rc != 0)
996f50eb
 			return rc;
 	}
 
d081ee4e
 	ds_dest_t *dp = NULL, *dp0 = NULL;
101e9af4
 
d081ee4e
 	dp0 = (ds_dest_t *)shm_malloc(node->nr * sizeof(ds_dest_t));
 	if(dp0 == NULL) {
996f50eb
 		LM_ERR("no more memory!\n");
 		goto err1;
 	}
d081ee4e
 	memset(dp0, 0, node->nr * sizeof(ds_dest_t));
101e9af4
 
996f50eb
 	/* copy from the old pointer to destination, and then free it */
d081ee4e
 	for(j = node->nr - 1; j >= 0 && node->dlist != NULL; j--) {
996f50eb
 		memcpy(&dp0[j], node->dlist, sizeof(ds_dest_t));
d081ee4e
 		if(j == node->nr - 1)
996f50eb
 			dp0[j].next = NULL;
 		else
d081ee4e
 			dp0[j].next = &dp0[j + 1];
65160302
 
 
996f50eb
 		dp = node->dlist;
 		node->dlist = dp->next;
cbe2e236
 
996f50eb
 		shm_free(dp);
d081ee4e
 		dp = NULL;
101e9af4
 	}
996f50eb
 	node->dlist = dp0;
 	dp_init_weights(node);
 	dp_init_relative_weights(node);
101e9af4
 
 	return 0;
 
 err1:
 	return -1;
 }
 
aecdbe9b
 /*! \brief load groups of destinations from file */
31ccf6a2
 int ds_load_list(char *lfile)
 {
1391db0f
 	char line[1024], *p;
31ccf6a2
 	FILE *f = NULL;
9de77c2b
 	int id, setn, flags, priority;
31ccf6a2
 	str uri;
65160302
 	str attrs;
 
0838c3e4
 	if((*ds_crt_idx) != (*ds_next_idx)) {
081630ba
 		LM_WARN("load command already generated, aborting reload...\n");
 		return 0;
 	}
081b5d4e
 
d081ee4e
 	if(lfile == NULL || strlen(lfile) <= 0) {
101e9af4
 		LM_ERR("bad list file\n");
31ccf6a2
 		return -1;
 	}
 
 	f = fopen(lfile, "r");
d081ee4e
 	if(f == NULL) {
101e9af4
 		LM_ERR("can't open list file [%s]\n", lfile);
31ccf6a2
 		return -1;
 	}
 
9de77c2b
 	id = setn = flags = priority = 0;
081630ba
 
0838c3e4
 	*ds_next_idx = (*ds_crt_idx + 1) % 2;
 	ds_avl_destroy(&ds_lists[*ds_next_idx]);
65160302
 
1391db0f
 	p = fgets(line, 1024, f);
d081ee4e
 	while(p) {
31ccf6a2
 		/* eat all white spaces */
d081ee4e
 		while(*p && (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n'))
31ccf6a2
 			p++;
d081ee4e
 		if(*p == '\0' || *p == '#')
31ccf6a2
 			goto next_line;
65160302
 
31ccf6a2
 		/* get set id */
 		id = 0;
d081ee4e
 		while(*p >= '0' && *p <= '9') {
 			id = id * 10 + (*p - '0');
31ccf6a2
 			p++;
 		}
65160302
 
31ccf6a2
 		/* eat all white spaces */
d081ee4e
 		while(*p && (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n'))
31ccf6a2
 			p++;
d081ee4e
 		if(*p == '\0' || *p == '#') {
101e9af4
 			LM_ERR("bad line [%s]\n", line);
31ccf6a2
 			goto error;
 		}
 
 		/* get uri */
 		uri.s = p;
d081ee4e
 		while(*p && *p != ' ' && *p != '\t' && *p != '\r' && *p != '\n'
 				&& *p != '#')
31ccf6a2
 			p++;
d081ee4e
 		uri.len = p - uri.s;
31ccf6a2
 
5affd4c7
 		/* eat all white spaces */
d081ee4e
 		while(*p && (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n'))
5affd4c7
 			p++;
65160302
 
4d0395a0
 		/* get flags */
 		flags = 0;
9de77c2b
 		priority = 0;
d081ee4e
 		attrs.s = 0;
 		attrs.len = 0;
 		if(*p == '\0' || *p == '#')
9de77c2b
 			goto add_destination; /* no flags given */
 
d081ee4e
 		while(*p >= '0' && *p <= '9') {
 			flags = flags * 10 + (*p - '0');
9de77c2b
 			p++;
5affd4c7
 		}
65160302
 
9de77c2b
 		/* eat all white spaces */
d081ee4e
 		while(*p && (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n'))
9de77c2b
 			p++;
65160302
 
9de77c2b
 		/* get priority */
d081ee4e
 		if(*p == '\0' || *p == '#')
9de77c2b
 			goto add_destination; /* no priority given */
5affd4c7
 
d081ee4e
 		while(*p >= '0' && *p <= '9') {
 			priority = priority * 10 + (*p - '0');
5affd4c7
 			p++;
 		}
65160302
 
 		/* eat all white spaces */
d081ee4e
 		while(*p && (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n'))
65160302
 			p++;
d081ee4e
 		if(*p == '\0' || *p == '#')
aae77585
 			goto add_destination; /* no attrs given */
65160302
 
 		/* get attributes */
 		attrs.s = p;
d081ee4e
 		while(*p && *p != ' ' && *p != '\t' && *p != '\r' && *p != '\n')
65160302
 			p++;
d081ee4e
 		attrs.len = p - attrs.s;
65160302
 
fa96c3a6
 add_destination:
0838c3e4
 		if(add_dest2list(id, uri, flags, priority, &attrs, *ds_next_idx, &setn, 0)
fa96c3a6
 				!= 0) {
92da08fd
 			LM_WARN("unable to add destination %.*s to set %d -- skipping\n",
 					uri.len, uri.s, id);
fa96c3a6
 			if(ds_load_mode==1) {
 				goto error;
 			}
 		}
 next_line:
1391db0f
 		p = fgets(line, 1024, f);
31ccf6a2
 	}
cbe2e236
 
0838c3e4
 	if(reindex_dests(ds_lists[*ds_next_idx]) != 0) {
101e9af4
 		LM_ERR("error on reindex\n");
 		goto error;
 	}
 
31ccf6a2
 	fclose(f);
 	f = NULL;
86646d0d
 	/* Update list - should it be sync'ed? */
101e9af4
 	_ds_list_nr = setn;
0838c3e4
 	*ds_crt_idx = *ds_next_idx;
ebf80aca
 
f4b8bede
 	LM_DBG("found [%d] dest sets\n", _ds_list_nr);
 
996f50eb
 	ds_log_sets();
101e9af4
 	return 0;
 
 error:
d081ee4e
 	if(f != NULL)
101e9af4
 		fclose(f);
0838c3e4
 	ds_avl_destroy(&ds_lists[*ds_next_idx]);
 	*ds_next_idx = *ds_crt_idx;
101e9af4
 	return -1;
 }
 
9cdc3e70
 /**
  *
  */
351fc577
 int ds_connect_db(void)
c503d21d
 {
d081ee4e
 	if(ds_db_url.s == NULL)
2af93c2f
 		return -1;
 
0fa5e3ac
 	if((ds_db_handle = ds_dbf.init(&ds_db_url)) == 0) {
 		LM_ERR("cannot initialize db connection\n");
c503d21d
 		return -1;
 	}
 	return 0;
 }
 
9cdc3e70
 /**
  *
  */
351fc577
 void ds_disconnect_db(void)
c503d21d
 {
d081ee4e
 	if(ds_db_handle) {
c503d21d
 		ds_dbf.close(ds_db_handle);
 		ds_db_handle = 0;
 	}
 }
 
aecdbe9b
 /*! \brief Initialize and verify DB stuff*/
4e7e355d
 int ds_init_db(void)
101e9af4
 {
c503d21d
 	int ret;
101e9af4
 
d081ee4e
 	if(ds_table_name.s == 0) {
101e9af4
 		LM_ERR("invalid database name\n");
 		return -1;
31ccf6a2
 	}
cbe2e236
 
101e9af4
 	/* Find a database module */
d081ee4e
 	if(db_bind_mod(&ds_db_url, &ds_dbf) < 0) {
101e9af4
 		LM_ERR("Unable to bind to a database driver\n");
 		return -1;
31ccf6a2
 	}
cbe2e236
 
d081ee4e
 	if(ds_connect_db() != 0) {
101e9af4
 		LM_ERR("unable to connect to the database\n");
 		return -1;
 	}
cbe2e236
 
c7d56382
 	_ds_table_version = db_table_version(&ds_dbf, ds_db_handle, &ds_table_name);
d081ee4e
 	if(_ds_table_version < 0) {
101e9af4
 		LM_ERR("failed to query table version\n");
 		return -1;
d081ee4e
 	} else if(_ds_table_version != DS_TABLE_VERSION
 			  && _ds_table_version != DS_TABLE_VERSION2
 			  && _ds_table_version != DS_TABLE_VERSION3
 			  && _ds_table_version != DS_TABLE_VERSION4) {
9cdc3e70
 		LM_ERR("invalid table version (found %d , required %d, %d, %d or %d)\n"
d081ee4e
 			   "(use kamdbctl reinit)\n",
cbe2e236
 				_ds_table_version, DS_TABLE_VERSION, DS_TABLE_VERSION2,
 				DS_TABLE_VERSION3, DS_TABLE_VERSION4);
101e9af4
 		return -1;
 	}
 
c503d21d
 	ret = ds_load_db();
d081ee4e
 	if(ret == -2) {
5c834924
 		LM_WARN("failure while loading one or more dispatcher entries\n");
 		ret = 0;
 	}
c503d21d
 
 	ds_disconnect_db();
 
 	return ret;
101e9af4
 }
 
96e76014
 /*! \brief reload groups of destinations from DB*/
 int ds_reload_db(void)
 {
 	int ret;
 
d081ee4e
 	if(ds_connect_db() != 0) {
96e76014
 		LM_ERR("unable to connect to the database\n");
 		return -1;
 	}
 	ret = ds_load_db();
d081ee4e
 	if(ret == -2) {
96e76014
 		LM_WARN("failure while loading one or more dispatcher entries\n");
 	}
 	ds_disconnect_db();
 
 	return ret;
 }
 
aecdbe9b
 /*! \brief load groups of destinations from DB*/
351fc577
 int ds_load_db(void)
101e9af4
 {
 	int i, id, nr_rows, setn;
5affd4c7
 	int flags;
9de77c2b
 	int priority;
c7d56382
 	int nrcols;
5c834924
 	int dest_errs = 0;
101e9af4
 	str uri;
65160302
 	str attrs = {0, 0};
d081ee4e
 	db1_res_t *res;
 	db_val_t *values;
 	db_row_t *rows;
433577dd
 #define DS_DB_MAX_COLS	32
 	db_key_t query_cols[DS_DB_MAX_COLS];
 	param_t *pit=NULL;
 	int nc;
 	int plen;
 #define DS_ATTRS_MAXSIZE	1024
 	char ds_attrs_buf[DS_ATTRS_MAXSIZE];
 
 	query_cols[0] = &ds_set_id_col;
 	query_cols[1] = &ds_dest_uri_col;
 	query_cols[2] = &ds_dest_flags_col;
 	query_cols[3] = &ds_dest_priority_col;
 	query_cols[4] = &ds_dest_attrs_col;
cbe2e236
 
c7d56382
 	nrcols = 2;
433577dd
 	if(_ds_table_version == DS_TABLE_VERSION2) {
c7d56382
 		nrcols = 3;
433577dd
 	} else if(_ds_table_version == DS_TABLE_VERSION3) {
9de77c2b
 		nrcols = 4;
433577dd
 	} else if(_ds_table_version == DS_TABLE_VERSION4) {
65160302
 		nrcols = 5;
433577dd
 		for(pit = ds_db_extra_attrs_list; pit!=NULL; pit=pit->next) {
 			if(nrcols>=DS_DB_MAX_COLS) {
 				LM_ERR("too many db columns: %d\n", nrcols);
 				return -1;
 			}
ebf80aca
 			query_cols[nrcols++] = &pit->body;
433577dd
 		}
 	}
c7d56382
 
0838c3e4
 	if((*ds_crt_idx) != (*ds_next_idx)) {
101e9af4
 		LM_WARN("load command already generated, aborting reload...\n");
 		return 0;
31ccf6a2
 	}
a4ba0359
 
d081ee4e
 	if(ds_db_handle == NULL) {
cbe2e236
 		LM_ERR("invalid DB handler\n");
 		return -1;
c503d21d
 	}
 
d081ee4e
 	if(ds_dbf.use_table(ds_db_handle, &ds_table_name) < 0) {
101e9af4
 		LM_ERR("error in use_table\n");
 		return -1;
 	}
31ccf6a2
 
433577dd
 	LM_DBG("loading dispatcher db records - nrcols: %d\n", nrcols);
 
101e9af4
 	/*select the whole table and all the columns*/
d081ee4e
 	if(ds_dbf.query(ds_db_handle, 0, 0, 0, query_cols, 0, nrcols, 0, &res)
 			< 0) {
101e9af4
 		LM_ERR("error while querying database\n");
 		return -1;
 	}
31ccf6a2
 
101e9af4
 	nr_rows = RES_ROW_N(res);
d081ee4e
 	rows = RES_ROWS(res);
433577dd
 	if(nr_rows == 0) {
c7d56382
 		LM_WARN("no dispatching data in the db -- empty destination set\n");
433577dd
 	}
31ccf6a2
 
101e9af4
 	setn = 0;
0838c3e4
 	*ds_next_idx = (*ds_crt_idx + 1) % 2;
 	ds_avl_destroy(&ds_lists[*ds_next_idx]);
cbe2e236
 
d081ee4e
 	for(i = 0; i < nr_rows; i++) {
 		values = ROW_VALUES(rows + i);
101e9af4
 
 		id = VAL_INT(values);
d081ee4e
 		uri.s = VAL_STR(values + 1).s;
101e9af4
 		uri.len = strlen(uri.s);
5affd4c7
 		flags = 0;
d081ee4e
 		if(nrcols >= 3)
 			flags = VAL_INT(values + 2);
 		priority = 0;
 		if(nrcols >= 4)
 			priority = VAL_INT(values + 3);
 
 		attrs.s = 0;
 		attrs.len = 0;
433577dd
 		if(nrcols >= 5) {
 			if(!VAL_NULL(values + 4)) {
 				attrs.s = VAL_STR(values + 4).s;
 				if(attrs.s) attrs.len = strlen(attrs.s);
 			}
 			if(ds_db_extra_attrs_list!=NULL && nrcols > 5) {
 				if(attrs.len>0) {
 					memcpy(ds_attrs_buf, attrs.s, attrs.len);
 					if(ds_attrs_buf[attrs.len-1]!=';') {
 						ds_attrs_buf[attrs.len++] = ';';
 					}
 				}
 				attrs.s = ds_attrs_buf;
 				pit = ds_db_extra_attrs_list;
 				for(nc = 5; nc<nrcols && pit!=NULL; nc++) {
 					if(!VAL_NULL(values + nc) && strlen(VAL_STRING(values + nc))>0) {
 						plen = snprintf(attrs.s + attrs.len,
 								DS_ATTRS_MAXSIZE - attrs.len - 1,
 								"%.*s=%s;", pit->name.len, pit->name.s,
 								VAL_STRING(values + nc));
 						if(plen<=0 || plen>=DS_ATTRS_MAXSIZE - attrs.len - 1) {
 							LM_ERR("cannot build attrs buffer\n");
 							goto err2;
 						}
 						attrs.len+=plen;
 					}
 					pit = pit->next;
 				}
 			}
65160302
 		}
433577dd
 		LM_DBG("attributes string: [%.*s]\n", attrs.len, (attrs.s)?attrs.s:"");
0838c3e4
 		if(add_dest2list(id, uri, flags, priority, &attrs, *ds_next_idx, &setn, 0)
d081ee4e
 				!= 0) {
5c834924
 			dest_errs++;
92da08fd
 			LM_WARN("unable to add destination %.*s to set %d -- skipping\n",
 					uri.len, uri.s, id);
fa96c3a6
 			if(ds_load_mode==1) {
 				goto err2;
 			}
5c834924
 		}
31ccf6a2
 	}
0838c3e4
 	if(reindex_dests(ds_lists[*ds_next_idx]) != 0) {
101e9af4
 		LM_ERR("error on reindex\n");
 		goto err2;
 	}
 
5c834924
 	ds_dbf.free_result(ds_db_handle, res);
 
86646d0d
 	/* update data - should it be sync'ed? */
101e9af4
 	_ds_list_nr = setn;
0838c3e4
 	*ds_crt_idx = *ds_next_idx;
9de77c2b
 
f4b8bede
 	LM_DBG("found [%d] dest sets\n", _ds_list_nr);
 
996f50eb
 	ds_log_sets();
101e9af4
 
d081ee4e
 	if(dest_errs > 0)
5c834924
 		return -2;
101e9af4
 	return 0;
 
 err2:
0838c3e4
 	ds_avl_destroy(&ds_lists[*ds_next_idx]);
101e9af4
 	ds_dbf.free_result(ds_db_handle, res);
0838c3e4
 	*ds_next_idx = *ds_crt_idx;
101e9af4
 
31ccf6a2
 	return -1;
 }
 
aecdbe9b
 /*! \brief called from dispatcher.c: free all*/
351fc577
 int ds_destroy_list(void)
101e9af4
 {
d081ee4e
 	if(ds_lists) {
 		ds_avl_destroy(&ds_lists[0]);
 		ds_avl_destroy(&ds_lists[1]);
cd63f545
 		shm_free(ds_lists);
 	}
101e9af4
 
0838c3e4
 	if(ds_crt_idx)
 		shm_free(ds_crt_idx);
101e9af4
 
 	return 0;
 }
e2cf6343
 
31ccf6a2
 /**
  *
  */
 unsigned int ds_get_hash(str *x, str *y)
 {
d081ee4e
 	char *p;
31ccf6a2
 	register unsigned v;
 	register unsigned h;
 
 	if(!x && !y)
 		return 0;
d081ee4e
 	h = 0;
 	if(x) {
 		p = x->s;
 		if(x->len >= 4) {
 			for(; p <= (x->s + x->len - 4); p += 4) {
 				v = (*p << 24) + (p[1] << 16) + (p[2] << 8) + p[3];
 				h += v ^ (v >> 3);
ee4a9fb2
 			}
31ccf6a2
 		}
d081ee4e
 		v = 0;
 		for(; p < (x->s + x->len); p++) {
 			v <<= 8;
 			v += *p;
31ccf6a2
 		}
d081ee4e
 		h += v ^ (v >> 3);
 	}
 	if(y) {
 		p = y->s;
 		if(y->len >= 4) {
 			for(; p <= (y->s + y->len - 4); p += 4) {
 				v = (*p << 24) + (p[1] << 16) + (p[2] << 8) + p[3];
 				h += v ^ (v >> 3);
ee4a9fb2
 			}
31ccf6a2
 		}
cbe2e236
 
d081ee4e
 		v = 0;
 		for(; p < (y->s + y->len); p++) {
 			v <<= 8;
 			v += *p;
31ccf6a2
 		}
d081ee4e
 		h += v ^ (v >> 3);
31ccf6a2
 	}
d081ee4e
 	h = ((h) + (h >> 11)) + ((h >> 13) + (h >> 23));
31ccf6a2
 
d081ee4e
 	return (h) ? h : 1;
31ccf6a2
 }
 
ee4a9fb2
 
aecdbe9b
 /*! \brief
ee4a9fb2
  * gets the part of the uri we will use as a key for hashing
aecdbe9b
  * \param  key1       - will be filled with first part of the key
ee4a9fb2
  *                       (uri user or "" if no user)
aecdbe9b
  * \param  key2       - will be filled with the second part of the key
ee4a9fb2
  *                       (uri host:port)
aecdbe9b
  * \param  uri        - str with the whole uri
  * \param  parsed_uri - struct sip_uri pointer with the parsed uri
ee4a9fb2
  *                       (it must point inside uri). It can be null
  *                       (in this case the uri will be parsed internally).
aecdbe9b
  * \param  flags  -    if & DS_HASH_USER_ONLY, only the user part of the uri
ee4a9fb2
  *                      will be used
aecdbe9b
  * \return: -1 on error, 0 on success
ee4a9fb2
  */
d081ee4e
 static inline int get_uri_hash_keys(
 		str *key1, str *key2, str *uri, struct sip_uri *parsed_uri, int flags)
ee4a9fb2
 {
 	struct sip_uri tmp_p_uri; /* used only if parsed_uri==0 */
cbe2e236
 
d081ee4e
 	if(parsed_uri == 0) {
 		if(parse_uri(uri->s, uri->len, &tmp_p_uri) < 0) {
 			LM_ERR("invalid uri %.*s\n", uri->len, uri->len ? uri->s : "");
ee4a9fb2
 			goto error;
 		}
d081ee4e
 		parsed_uri = &tmp_p_uri;
ee4a9fb2
 	}
 	/* uri sanity checks */
d081ee4e
 	if(parsed_uri->host.s == 0) {
 		LM_ERR("invalid uri, no host present: %.*s\n", uri->len,
 				uri->len ? uri->s : "");
cbe2e236
 		goto error;
ee4a9fb2
 	}
cbe2e236
 
ee4a9fb2
 	/* we want: user@host:port if port !=5060
 	 *          user@host if port==5060
 	 *          user if the user flag is set*/
d081ee4e
 	*key1 = parsed_uri->user;
 	key2->s = 0;
 	key2->len = 0;
 	if(!(flags & DS_HASH_USER_ONLY)) { /* key2=host */
 		*key2 = parsed_uri->host;
ee4a9fb2
 		/* add port if needed */
d081ee4e
 		if(parsed_uri->port.s != 0) { /* uri has a port */
ee4a9fb2
 			/* skip port if == 5060 or sips and == 5061 */
d081ee4e
 			if(parsed_uri->port_no != ((parsed_uri->type == SIPS_URI_T)
 													  ? SIPS_PORT
 													  : SIP_PORT))
 				key2->len += parsed_uri->port.len + 1 /* ':' */;
ee4a9fb2
 		}
 	}
d081ee4e
 	if(key1->s == 0) {
 		LM_WARN("empty username in: %.*s\n", uri->len, uri->len ? uri->s : "");
ee4a9fb2
 	}
 	return 0;
 error:
 	return -1;
 }
 
 
31ccf6a2
 /**
  *
  */
 int ds_hash_fromuri(struct sip_msg *msg, unsigned int *hash)
 {
 	str from;
ee4a9fb2
 	str key1;
 	str key2;
cbe2e236
 
d081ee4e
 	if(msg == NULL || hash == NULL) {
101e9af4
 		LM_ERR("bad parameters\n");
31ccf6a2
 		return -1;
 	}
cbe2e236
 
d081ee4e
 	if(parse_from_header(msg) < 0) {
101e9af4
 		LM_ERR("cannot parse From hdr\n");
31ccf6a2
 		return -1;
 	}
cbe2e236
 
d081ee4e
 	if(msg->from == NULL || get_from(msg) == NULL) {
101e9af4
 		LM_ERR("cannot get From uri\n");
31ccf6a2
 		return -1;
 	}
cbe2e236
 
d081ee4e
 	from = get_from(msg)->uri;
31ccf6a2
 	trim(&from);
d081ee4e
 	if(get_uri_hash_keys(&key1, &key2, &from, 0, ds_flags) < 0)
ee4a9fb2
 		return -1;
 	*hash = ds_get_hash(&key1, &key2);
cbe2e236
 
31ccf6a2
 	return 0;
 }
b68fbcd1
 
 
 /**
  *
  */
 int ds_hash_touri(struct sip_msg *msg, unsigned int *hash)
 {
 	str to;
ee4a9fb2
 	str key1;
 	str key2;
cbe2e236
 
d081ee4e
 	if(msg == NULL || hash == NULL) {
101e9af4
 		LM_ERR("bad parameters\n");
b68fbcd1
 		return -1;
 	}
d081ee4e
 	if((msg->to == 0)
 			&& ((parse_headers(msg, HDR_TO_F, 0) == -1) || (msg->to == 0))) {
101e9af4
 		LM_ERR("cannot parse To hdr\n");
b68fbcd1
 		return -1;
 	}
cbe2e236
 
 
d081ee4e
 	to = get_to(msg)->uri;
b68fbcd1
 	trim(&to);
cbe2e236
 
d081ee4e
 	if(get_uri_hash_keys(&key1, &key2, &to, 0, ds_flags) < 0)
ee4a9fb2
 		return -1;
 	*hash = ds_get_hash(&key1, &key2);
cbe2e236
 
b68fbcd1
 	return 0;
 }
 
ee4a9fb2
 
31ccf6a2
 /**
  *
  */
 int ds_hash_callid(struct sip_msg *msg, unsigned int *hash)
 {
 	str cid;
d081ee4e
 	if(msg == NULL || hash == NULL) {
101e9af4
 		LM_ERR("bad parameters\n");
31ccf6a2
 		return -1;
 	}
cbe2e236
 
d081ee4e
 	if(msg->callid == NULL && ((parse_headers(msg, HDR_CALLID_F, 0) == -1)
 									  || (msg->callid == NULL))) {
101e9af4
 		LM_ERR("cannot parse Call-Id\n");
31ccf6a2
 		return -1;
 	}
cbe2e236
 
d081ee4e
 	cid.s = msg->callid->body.s;
31ccf6a2
 	cid.len = msg->callid->body.len;
 	trim(&cid);
cbe2e236
 
31ccf6a2
 	*hash = ds_get_hash(&cid, NULL);
cbe2e236
 
31ccf6a2
 	return 0;
 }
 
b68fbcd1
 
9cdc3e70
 /**
  *
  */
b68fbcd1
 int ds_hash_ruri(struct sip_msg *msg, unsigned int *hash)
 {
d081ee4e
 	str *uri;
ee4a9fb2
 	str key1;
 	str key2;
cbe2e236
 
d081ee4e
 	if(msg == NULL || hash == NULL) {
101e9af4
 		LM_ERR("bad parameters\n");
b68fbcd1
 		return -1;
 	}
d081ee4e
 	if(parse_sip_msg_uri(msg) < 0) {
101e9af4
 		LM_ERR("bad request uri\n");
b68fbcd1
 		return -1;
 	}
cbe2e236
 
d081ee4e
 	uri = GET_RURI(msg);
 	if(get_uri_hash_keys(&key1, &key2, uri, &msg->parsed_uri, ds_flags) < 0)
b68fbcd1
 		return -1;
cbe2e236
 
ee4a9fb2
 	*hash = ds_get_hash(&key1, &key2);
b68fbcd1
 	return 0;
 }
 
9cdc3e70
 /**
  *
  */
081b5d4e
 int ds_hash_authusername(struct sip_msg *msg, unsigned int *hash)
 {
 	/* Header, which contains the authorization */
d081ee4e
 	struct hdr_field *h = 0;
081b5d4e
 	/* The Username */
 	str username = {0, 0};
 	/* The Credentials from this request */
d081ee4e
 	auth_body_t *cred;
cbe2e236
 
d081ee4e
 	if(msg == NULL || hash == NULL) {
101e9af4
 		LM_ERR("bad parameters\n");
081b5d4e
 		return -1;
 	}
e9cfb2bf
 	*hash = 0;
d081ee4e
 	if(parse_headers(msg, HDR_PROXYAUTH_F, 0) == -1) {
101e9af4
 		LM_ERR("error parsing headers!\n");
081b5d4e
 		return -1;
 	}
e9cfb2bf
 	if(msg->proxy_auth && !msg->proxy_auth->parsed) {
 		if(parse_credentials(msg->proxy_auth)!=0) {
 			LM_DBG("no parsing for proxy-auth header\n");
 		}
 	}
d081ee4e
 	if(msg->proxy_auth && msg->proxy_auth->parsed) {
081b5d4e
 		h = msg->proxy_auth;
 	}
d081ee4e
 	if(!h) {
 		if(parse_headers(msg, HDR_AUTHORIZATION_F, 0) == -1) {
101e9af4
 			LM_ERR("error parsing headers!\n");
081b5d4e
 			return -1;
 		}
e9cfb2bf
 		if(msg->authorization && !msg->authorization->parsed) {
 			if(parse_credentials(msg->authorization)!=0) {
 				LM_DBG("no parsing for auth header\n");
 			}
 		}
d081ee4e
 		if(msg->authorization && msg->authorization->parsed) {
081b5d4e
 			h = msg->authorization;
 		}
 	}
d081ee4e
 	if(!h) {
101e9af4
 		LM_DBG("No Authorization-Header!\n");
081b5d4e
 		return 1;
 	}
 
d081ee4e
 	cred = (auth_body_t *)(h->parsed);
 	if(!cred || !cred->digest.username.user.len) {
101e9af4
 		LM_ERR("No Authorization-Username or Credentials!\n");
081b5d4e
 		return 1;
 	}
cbe2e236
 
081b5d4e
 	username.s = cred->digest.username.user.s;
 	username.len = cred->digest.username.user.len;
 
 	trim(&username);
cbe2e236
 
081b5d4e
 	*hash = ds_get_hash(&username, NULL);
cbe2e236
 
081b5d4e
 	return 0;
 }
b68fbcd1
 
095ab21d
 
9cdc3e70
 /**
  *
  */
095ab21d
 int ds_hash_pvar(struct sip_msg *msg, unsigned int *hash)
 {
 	/* The String to create the hash */
 	str hash_str = {0, 0};
cbe2e236
 
d081ee4e
 	if(msg == NULL || hash == NULL || hash_param_model == NULL) {
095ab21d
 		LM_ERR("bad parameters\n");
 		return -1;
 	}
d081ee4e
 	if(pv_printf_s(msg, hash_param_model, &hash_str) < 0) {
095ab21d
 		LM_ERR("error - cannot print the format\n");
 		return -1;
 	}
 
 	/* Remove empty spaces */
 	trim(&hash_str);
d081ee4e
 	if(hash_str.len <= 0) {
095ab21d
 		LM_ERR("String is empty!\n");
 		return -1;
 	}
 
 	*hash = ds_get_hash(&hash_str, NULL);
83a558fb
 	LM_DBG("Hashing of '%.*s' resulted in %u !\n", hash_str.len, hash_str.s,
 			*hash);
cbe2e236
 
095ab21d
 	return 0;
 }
 
9cdc3e70
 /**
  *
  */
996f50eb
 static inline int ds_get_index(int group, int ds_list_idx, ds_set_t **index)
9cdc3e70
 {
 	ds_set_t *si = NULL;
cbe2e236
 
d081ee4e
 	if(index == NULL || group < 0 || ds_lists[ds_list_idx] == NULL)
9cdc3e70
 		return -1;
cbe2e236
 
9cdc3e70
 	/* get the index of the set */
d081ee4e
 	si = ds_avl_find(ds_lists[ds_list_idx], group);
9cdc3e70
 
d081ee4e
 	if(si == NULL)
9cdc3e70
 		return -1;
 
996f50eb
 	*index = si;
9cdc3e70
 	return 0;
 }
 
bef88a0d
 /*
  * Check if a destination set exists
  */
 int ds_list_exist(int set)
 {
 	ds_set_t *si = NULL;
fcabdc01
 	LM_DBG("looking for destination set [%d]\n", set);
bef88a0d
 
 	/* get the index of the set */
d081ee4e
 	si = ds_avl_find(_ds_list, set);
bef88a0d
 
d081ee4e
 	if(si == NULL) {
fcabdc01
 		LM_DBG("destination set [%d] not found\n", set);
d081ee4e
 		return -1; /* False */
bef88a0d
 	}
fcabdc01
 	LM_DBG("destination set [%d] found\n", set);
d081ee4e
 	return 1; /* True */
bef88a0d
 }
9cdc3e70
 
 /**
  *
  */
59c31fe7
 int ds_get_leastloaded(ds_set_t *dset)
 {
 	int j;
 	int k;
 	int t;
 
ecd5c584
 	k = -1;
 	t = 0x7fffffff; /* high load */
d14833d9
 	lock_get(&dset->lock); \
d081ee4e
 	for(j = 0; j < dset->nr; j++) {
ecd5c584
 		if(!ds_skip_dst(dset->dlist[j].flags)
a0521f71
 				&& (dset->dlist[j].attrs.maxload == 0
d081ee4e
 						   || dset->dlist[j].dload
 									  < dset->dlist[j].attrs.maxload)) {
 			if(dset->dlist[j].dload < t) {
59c31fe7
 				k = j;
 				t = dset->dlist[k].dload;
 			}
 		}
 	}
d14833d9
 	lock_release(&dset->lock); \
59c31fe7
 	return k;
 }
 
9cdc3e70
 /**
  *
  */
 int ds_load_add(struct sip_msg *msg, ds_set_t *dset, int setid, int dst)
59c31fe7
 {
d081ee4e
 	if(dset->dlist[dst].attrs.duid.len == 0) {
59c31fe7
 		LM_ERR("dst unique id not set for %d (%.*s)\n", setid,
 				msg->callid->body.len, msg->callid->body.s);
 		return -1;
 	}
 
d081ee4e
 	if(ds_add_cell(_dsht_load, &msg->callid->body, &dset->dlist[dst].attrs.duid,
 			   setid)
 			< 0) {
 		LM_ERR("cannot add load to %d (%.*s)\n", setid, msg->callid->body.len,
 				msg->callid->body.s);
59c31fe7
 		return -1;
 	}
d14833d9
 	DS_LOAD_INC(dset, dst);
59c31fe7
 	return 0;
 }
 
9cdc3e70
 /**
  *
  */
 int ds_load_replace(struct sip_msg *msg, str *duid)
a4ba0359
 {
9cdc3e70
 	ds_cell_t *it;
 	int set;
 	int olddst;
 	int newdst;
 	ds_set_t *idx = NULL;
 	int i;
 
d081ee4e
 	if(duid->len <= 0) {
9cdc3e70
 		LM_ERR("invalid dst unique id not set for (%.*s)\n",
 				msg->callid->body.len, msg->callid->body.s);
a4ba0359
 		return -1;
9cdc3e70
 	}
 
d081ee4e
 	if((it = ds_get_cell(_dsht_load, &msg->callid->body)) == NULL) {
 		LM_ERR("cannot find load for (%.*s)\n", msg->callid->body.len,
 				msg->callid->body.s);
9cdc3e70
 		return -1;
 	}
 	set = it->dset;
a4ba0359
 	/* get the index of the set */
0838c3e4
 	if(ds_get_index(set, *ds_crt_idx, &idx) != 0) {
9cdc3e70
 		ds_unlock_cell(_dsht_load, &msg->callid->body);
 		LM_ERR("destination set [%d] not found\n", set);
 		return -1;
 	}
 	olddst = -1;
 	newdst = -1;
d081ee4e
 	for(i = 0; i < idx->nr; i++) {
 		if(idx->dlist[i].attrs.duid.len == it->duid.len
 				&& strncasecmp(
 						   idx->dlist[i].attrs.duid.s, it->duid.s, it->duid.len)
 						   == 0) {
9cdc3e70
 			olddst = i;
d081ee4e
 			if(newdst != -1)
9cdc3e70
 				break;
 		}
d081ee4e
 		if(idx->dlist[i].attrs.duid.len == duid->len
 				&& strncasecmp(idx->dlist[i].attrs.duid.s, duid->s, duid->len)
 						   == 0) {
9cdc3e70
 			newdst = i;
d081ee4e
 			if(olddst != -1)
9cdc3e70
 				break;
 		}
 	}
4099a6ca
 	/* old destination has not been found: has been removed meanwhile? */
d081ee4e
 	if(olddst == -1) {
4099a6ca
 		LM_WARN("old destination address not found for [%d, %.*s]\n", set,
9cdc3e70
 				it->duid.len, it->duid.s);
ebf80aca
 	}
d081ee4e
 	if(newdst == -1) {
4099a6ca
 		/* new destination has not been found: has been removed meanwhile? */
9cdc3e70
 		ds_unlock_cell(_dsht_load, &msg->callid->body);
 		LM_ERR("new destination address not found for [%d, %.*s]\n", set,
 				duid->len, duid->s);
4099a6ca
 		return -2;
9cdc3e70
 	}
 
 	ds_unlock_cell(_dsht_load, &msg->callid->body);
 	ds_del_cell(_dsht_load, &msg->callid->body);
ebf80aca
 
4099a6ca
 	if(olddst != -1)
 		DS_LOAD_DEC(idx, olddst);
9cdc3e70
 
d081ee4e
 	if(ds_load_add(msg, idx, set, newdst) < 0) {
 		LM_ERR("unable to replace destination load [%.*s / %.*s]\n", duid->len,
 				duid->s, msg->callid->body.len, msg->callid->body.s);
9cdc3e70
 		return -1;
 	}
 	return 0;
 }
 
 /**
  *
  */
684da612
 int ds_load_remove_byid(int set, str *duid)
9cdc3e70
 {
 	int olddst;
 	ds_set_t *idx = NULL;
 	int i;
 
 	/* get the index of the set */
0838c3e4
 	if(ds_get_index(set, *ds_crt_idx, &idx) != 0) {
9cdc3e70
 		LM_ERR("destination set [%d] not found\n", set);
 		return -1;
 	}
 	olddst = -1;
d081ee4e
 	for(i = 0; i < idx->nr; i++) {
684da612
 		if(idx->dlist[i].attrs.duid.len == duid->len
 				&& strncasecmp(idx->dlist[i].attrs.duid.s, duid->s, duid->len)
d081ee4e
 						   == 0) {
9cdc3e70
 			olddst = i;
a4ba0359
 			break;
 		}
9cdc3e70
 	}
d081ee4e
 	if(olddst == -1) {
9cdc3e70
 		LM_ERR("old destination address not found for [%d, %.*s]\n", set,
684da612
 				duid->len, duid->s);
9cdc3e70
 		return -1;
a4ba0359
 	}
 
d14833d9
 	DS_LOAD_DEC(idx, olddst);
9cdc3e70
 
 	return 0;
 }
 
 /**
  *
  */
684da612
 int ds_load_remove(struct sip_msg *msg)
9cdc3e70
 {
684da612
 	ds_cell_t *it;
9cdc3e70
 
684da612
 	if((it = ds_get_cell(_dsht_load, &msg->callid->body)) == NULL) {
 		LM_ERR("cannot find load for (%.*s)\n", msg->callid->body.len,
 				msg->callid->body.s);
9cdc3e70
 		return -1;
 	}
684da612
 
 	if (ds_load_remove_byid(it->dset, &it->duid) < 0) {
 		ds_unlock_cell(_dsht_load, &msg->callid->body);
9cdc3e70
 		return -1;
 	}
684da612
 	ds_unlock_cell(_dsht_load, &msg->callid->body);
 	ds_del_cell(_dsht_load, &msg->callid->body);
9cdc3e70
 
 	return 0;
 }
 
 /**
  *
  */
 int ds_load_state(struct sip_msg *msg, int state)
 {
 	ds_cell_t *it;
 
d081ee4e
 	if((it = ds_get_cell(_dsht_load, &msg->callid->body)) == NULL) {
 		LM_DBG("cannot find load for (%.*s)\n", msg->callid->body.len,
 				msg->callid->body.s);
a4ba0359
 		return -1;
 	}
 
9cdc3e70
 	it->state = state;
 	ds_unlock_cell(_dsht_load, &msg->callid->body);
 
 	return 0;
 }
 
 
 /**
  *
  */
 int ds_load_update(struct sip_msg *msg)
 {
d081ee4e
 	if(parse_headers(msg, HDR_CSEQ_F | HDR_CALLID_F, 0) != 0
 			|| msg->cseq == NULL || msg->callid == NULL) {
cbe2e236
 		LM_ERR("cannot parse cseq and callid headers\n");
 		return -1;
 	}
d081ee4e
 	if(msg->first_line.type == SIP_REQUEST) {
 		if(msg->first_line.u.request.method_value == METHOD_BYE
 				|| msg->first_line.u.request.method_value == METHOD_CANCEL) {
9cdc3e70
 			/* off-load call */
 			ds_load_remove(msg);
 		}
 		return 0;
 	}
 
d081ee4e
 	if(get_cseq(msg)->method_id == METHOD_INVITE) {
9cdc3e70
 		/* if status is 2xx then set state to confirmed */
d081ee4e
 		if(REPLY_CLASS(msg) == 2)
9cdc3e70
 			ds_load_state(msg, DS_LOAD_CONFIRMED);
 	}
a4ba0359
 	return 0;
 }
 
9cdc3e70
 /**
  *
  */
 int ds_load_unset(struct sip_msg *msg)
 {
11ff4226
 	sr_xavp_t *rxavp = NULL;
cbe2e236
 
11ff4226
 	if(ds_xavp_dst.len <= 0)
9cdc3e70
 		return 0;
 
 	/* for INVITE requests should be called after dst list is built */
d081ee4e
 	if(msg->first_line.type == SIP_REQUEST
 			&& msg->first_line.u.request.method_value == METHOD_INVITE) {
11ff4226
 		rxavp = xavp_get_child_with_sval(&ds_xavp_dst, &ds_xavp_dst_dstid);
 		if(rxavp == NULL)
9cdc3e70
 			return 0;
 	}
 	return ds_load_remove(msg);
 }
 
 /**
  *
  */
80930085
 static inline int ds_push_dst(sip_msg_t *msg, str *uri, socket_info_t *sock,
 		int mode)
a4ba0359
 {
 	struct action act;
dfea35f6
 	struct run_act_ctx ra_ctx;
d081ee4e
 	switch(mode) {
f6960f87
 		case DS_SETOP_RURI:
dfea35f6
 			memset(&act, '\0', sizeof(act));
8381c032
 			act.type = SET_HOSTALL_T;
dfea35f6
 			act.val[0].type = STRING_ST;
d081ee4e
 			if(uri->len > 4 && strncasecmp(uri->s, "sip:", 4) == 0) {
 				act.val[0].u.string = uri->s + 4;
 			} else if(uri->len > 5 && strncasecmp(uri->s, "sips:", 5) == 0) {
 				act.val[0].u.string = uri->s + 5;
53a55967
 			} else {
dfea35f6
 				act.val[0].u.string = uri->s;
53a55967
 			}
dfea35f6
 			init_run_actions_ctx(&ra_ctx);
d081ee4e
 			if(do_action(&ra_ctx, &act, msg) < 0) {
c26ff896
 				LM_ERR("error while setting r-uri domain with: %.*s\n",
 						uri->len, uri->s);
a4ba0359
 				return -1;
 			}
cbe2e236
 			break;
53a55967
 
f6960f87
 		case DS_SETOP_XAVP:
53a55967
 			/* no update to d-uri/r-uri */
 			return 0;
 
a4ba0359
 		default:
d081ee4e
 			if(set_dst_uri(msg, uri) < 0) {
c26ff896
 				LM_ERR("error while setting dst uri with: %.*s\n",
 						uri->len, uri->s);
8381c032
 				return -1;
039b182e
 			}
 			/* dst_uri changes, so it makes sense to re-use the current uri for
1d401e0a
 			 * forking */
039b182e
 			ruri_mark_new(); /* re-use uri for serial forking */
cbe2e236
 			break;
a4ba0359
 	}
f6960f87
 	if(sock) {
aae77585
 		msg->force_send_socket = sock;
f6960f87
 	}
a4ba0359
 	return 0;
 }
ee4a9fb2
 
2ce57c44
 /**
  *
  */
 int ds_add_branches(sip_msg_t *msg, ds_set_t *idx, unsigned int hash, int mode)
 {
 	unsigned int i = 0;
 	str ruri = STR_NULL;
 	sip_uri_t *puri = NULL;
 	char buri[MAX_URI_SIZE];
 
f6960f87
 	if(mode!=DS_SETOP_XAVP && hash+1>=idx->nr) {
2ce57c44
 		/* nothing to add */
 		return 0;
 	}
 
f6960f87
 	if(mode==DS_SETOP_RURI) {
2ce57c44
 		/* ruri updates */
 		LM_DBG("adding branches with ruri\n");
 		if(parse_sip_msg_uri(msg)<0) {
 			LM_ERR("failed to parse sip msg uri\n");
 			return -1;
 		}
 		puri = &msg->parsed_uri;
 	} else {
 		/* duri updates */
 		LM_DBG("adding branches with duri\n");
 	}
f6960f87
 	if(mode!=DS_SETOP_XAVP) {
 		i = hash + 1;
 	} else {
 		i = hash;
 	}
 	for(; i<idx->nr; i++) {
 		if(mode==DS_SETOP_RURI) {
2ce57c44
 			/* ruri updates */
 			if(puri->user.len<=0) {
 				/* no username to preserve */
 				if(append_branch(msg, &idx->dlist[i].uri, NULL, NULL,
 						Q_UNSPECIFIED, 0, idx->dlist[i].sock, NULL, 0,
 						NULL, NULL)<0) {
 					LM_ERR("failed to add branch with ruri\n");
 					return -1;
 				}
 			} else {
 				/* new uri from ruri username and dispatcher uri */
 				if(idx->dlist[i].uri.len<6) {
 					LM_WARN("invalid dispatcher uri - skipping (%u)\n", i);
 					continue;
 				}
 				if(strncmp(idx->dlist[i].uri.s, "sips:", 5)==0) {
 					ruri.len = snprintf(buri, MAX_URI_SIZE, "sips:%.*s@%.*s",
 							puri->user.len, puri->user.s,
 							idx->dlist[i].uri.len-5, idx->dlist[i].uri.s+5);
 				} else {
 					if(strncmp(idx->dlist[i].uri.s, "sip:", 4)==0) {
 						ruri.len = snprintf(buri, MAX_URI_SIZE, "sip:%.*s@%.*s",
 								puri->user.len, puri->user.s,
 								idx->dlist[i].uri.len-4, idx->dlist[i].uri.s+4);
 					} else {
 						LM_WARN("unsupported protocol schema - ignoring\n");
 						continue;
 					}
 				}
 				ruri.s = buri;
 				if(append_branch(msg, &ruri, NULL, NULL,
 						Q_UNSPECIFIED, 0, idx->dlist[i].sock, NULL, 0,
 						NULL, NULL)<0) {
 					LM_ERR("failed to add branch with user ruri\n");
 					return -1;
 				}
 			}
 		} else {
 			/* duri updates */
 			if(append_branch(msg, GET_RURI(msg), &idx->dlist[i].uri, NULL,
 					Q_UNSPECIFIED, 0, idx->dlist[i].sock, NULL, 0,
 					NULL, NULL)<0) {
 				LM_ERR("failed to add branch with duri\n");
 				return -1;
 			}
 		}
 
 	}
 	return 0;
 }
 
11ff4226
 /**
  *
  */
c9e5b863
 int ds_add_xavp_record(ds_set_t *dsidx, int pos, int set, int alg,
 		sr_xavp_t **pxavp)
11ff4226
 {
 	sr_xavp_t *nxavp=NULL;
 	sr_xval_t nxval;
 
 	/* add destination uri field */
 	memset(&nxval, 0, sizeof(sr_xval_t));
 	nxval.type = SR_XTYPE_STR;
 	nxval.v.s = dsidx->dlist[pos].uri;
 	if(xavp_add_value(&ds_xavp_dst_addr, &nxval, &nxavp)==NULL) {
 		LM_ERR("failed to add destination uri xavp field\n");
 		return -1;
 	}
 
 	/* add setid field */
 	memset(&nxval, 0, sizeof(sr_xval_t));
 	nxval.type = SR_XTYPE_INT;
 	nxval.v.i = set;
 	if(xavp_add_value(&ds_xavp_dst_grp, &nxval, &nxavp)==NULL) {
 		xavp_destroy_list(&nxavp);
 		LM_ERR("failed to add destination setid xavp field\n");
 		return -1;
 	}
 
 	if(((ds_xavp_dst_mode & DS_XAVP_DST_SKIP_ATTRS) == 0)
 			&& (dsidx->dlist[pos].attrs.body.len > 0)) {
 		memset(&nxval, 0, sizeof(sr_xval_t));
 		nxval.type = SR_XTYPE_STR;
 		nxval.v.s = dsidx->dlist[pos].attrs.body;
 		if(xavp_add_value(&ds_xavp_dst_attrs, &nxval, &nxavp)==NULL) {
 			xavp_destroy_list(&nxavp);
 			LM_ERR("failed to add destination attrs xavp field\n");
 			return -1;
 		}
 	}
 
 	if(dsidx->dlist[pos].sock) {
 		memset(&nxval, 0, sizeof(sr_xval_t));
21addddd
 		nxval.type = SR_XTYPE_VPTR;
 		nxval.v.vptr = dsidx->dlist[pos].sock;
11ff4226
 		if(xavp_add_value(&ds_xavp_dst_sock, &nxval, &nxavp)==NULL) {
 			xavp_destroy_list(&nxavp);
 			LM_ERR("failed to add destination sock xavp field\n");
 			return -1;
 		}
8507efe8
 		if((ds_xavp_dst_mode & DS_XAVP_DST_ADD_SOCKSTR)
 				&& (dsidx->dlist[pos].attrs.socket.len > 0)) {
 			memset(&nxval, 0, sizeof(sr_xval_t));
 			nxval.type = SR_XTYPE_STR;
 			nxval.v.s = dsidx->dlist[pos].attrs.socket;
 			if(xavp_add_value(&ds_xavp_dst_socket, &nxval, &nxavp)==NULL) {
 				xavp_destroy_list(&nxavp);
80930085
 				LM_ERR("failed to add socket address attrs xavp field\n");
 				return -1;
 			}
 		}
 		if((ds_xavp_dst_mode & DS_XAVP_DST_ADD_SOCKNAME)
 				&& (dsidx->dlist[pos].attrs.sockname.len > 0)) {
 			memset(&nxval, 0, sizeof(sr_xval_t));
 			nxval.type = SR_XTYPE_STR;
 			nxval.v.s = dsidx->dlist[pos].attrs.sockname;
 			if(xavp_add_value(&ds_xavp_dst_sockname, &nxval, &nxavp)==NULL) {
 				xavp_destroy_list(&nxavp);
 				LM_ERR("failed to add socket name attrs xavp field\n");
8507efe8
 				return -1;
 			}
 		}
11ff4226
 	}
 
 	if(alg == DS_ALG_CALLLOAD) {
 		if(dsidx->dlist[pos].attrs.duid.len <= 0) {
 			LM_ERR("no uid for destination: %d %.*s\n", set,
 					dsidx->dlist[pos].uri.len,
 					dsidx->dlist[pos].uri.s);
 			xavp_destroy_list(&nxavp);
 			return -1;
 		}
 		memset(&nxval, 0, sizeof(sr_xval_t));
 		nxval.type = SR_XTYPE_STR;
 		nxval.v.s = dsidx->dlist[pos].attrs.duid;
 		if(xavp_add_value(&ds_xavp_dst_dstid, &nxval, &nxavp)==NULL) {
 			xavp_destroy_list(&nxavp);
 			LM_ERR("failed to add destination dst uid xavp field\n");
 			return -1;
 		}
 	}
 
 	/* add xavp in root list */
 	memset(&nxval, 0, sizeof(sr_xval_t));
 	nxval.type = SR_XTYPE_XAVP;
 	nxval.v.xavp = nxavp;
c9e5b863
 	if((*pxavp = xavp_add_value_after(&ds_xavp_dst, &nxval, *pxavp))==NULL) {
11ff4226
 		LM_ERR("cannot add dst xavp to root list\n");
 		xavp_destroy_list(&nxavp);
 		return -1;
 	}
 
 	return 0;
 }
 
31ccf6a2
 /**
  *
  */
a4ba0359
 int ds_select_dst(struct sip_msg *msg, int set, int alg, int mode)
15d1022b
 {
3ea4bc0b
 	return ds_select_dst_limit(msg, set, alg, 0, mode);
15d1022b
 }
 
ecd5c584
 /**
  * Set destination address from group 'set' selected with alogorithm 'alg'
3933a64c
  * - the rest of addresses in group are added as next destination in xavps,
ecd5c584
  *   up to the 'limit'
  * - mode specify to set address in R-URI or outboud proxy
  *
  */
3933a64c
 int ds_select_dst_limit(sip_msg_t *msg, int set, int alg, uint32_t limit,
 		int mode)
31ccf6a2
 {
3933a64c
 	int ret;
 	sr_xval_t nxval;
 	ds_select_state_t vstate;
 
 	memset(&vstate, 0, sizeof(ds_select_state_t));
 	vstate.setid = set;
 	vstate.alg = alg;
 	vstate.umode = mode;
 	vstate.limit = limit;
 
 	if(vstate.limit == 0) {
 		LM_DBG("Limit set to 0 - forcing to unlimited\n");
 		vstate.limit = 0xffffffff;
 	}
 
 	ret = ds_manage_routes(msg, &vstate);
 	if(ret<0) {
 		return ret;
 	}
 
 	/* add cnt value to xavp */
 	if(((ds_xavp_ctx_mode & DS_XAVP_CTX_SKIP_CNT)==0)
 			&& (ds_xavp_ctx.len >= 0)) {
 		/* add to xavp the number of selected dst records */
 		memset(&nxval, 0, sizeof(sr_xval_t));
 		nxval.type = SR_XTYPE_INT;
 		nxval.v.i = vstate.cnt;
 		if(xavp_add_xavp_value(&ds_xavp_ctx, &ds_xavp_ctx_cnt, &nxval, NULL)==NULL) {
 			LM_ERR("failed to add cnt value to xavp\n");
 			return -1;
 		}
 	}
 
 	LM_DBG("selected target destinations: %d\n", vstate.cnt);
 
 	return ret;
 }
 
90e3033c
 typedef struct sorted_ds {
 	int idx;
 	int priority;
 } sorted_ds_t;
 
 int ds_manage_routes_fill_reodered_xavp(sorted_ds_t *ds_sorted, ds_set_t *idx, ds_select_state_t *rstate)
 {
 	int i;
 	if(!(ds_flags & DS_FAILOVER_ON))
 		return 1;
 	for(i=0; i < idx->nr && rstate->cnt < rstate->limit; i++) {
 		if(ds_sorted[i].idx < 0 || ds_skip_dst(idx->dlist[i].flags)
 				|| (ds_use_default != 0 && ds_sorted[i].idx == (idx->nr - 1))) {
 			continue;
 		}
 		if(ds_add_xavp_record(idx, ds_sorted[i].idx, rstate->setid, rstate->alg,
 					&rstate->lxavp)<0) {
 			LM_ERR("failed to add destination in the xavp (%d/%d)\n",
 					ds_sorted[i].idx, rstate->setid);
 			return -1;
 		}
 		LM_DBG("destination added in the xavp (%d/%d)\n",
 					ds_sorted[i].idx, rstate->setid);
 		rstate->cnt++;
 	}
 	return 0;
 }
 
12efb982
 int ds_manage_routes_fill_xavp(unsigned int hash, ds_set_t *idx, ds_select_state_t *rstate)
 {
 	int i;
 
 	LM_DBG("using first entry [%d/%d]\n", rstate->setid, hash);
 	if(ds_add_xavp_record(idx, hash, rstate->setid, rstate->alg,
 				&rstate->lxavp)<0) {
 		LM_ERR("failed to add destination in the xavp (%d/%d)\n",
 				hash, rstate->setid);
 		return -1;
 	}
 	rstate->cnt++;
 
 	/* add to xavp the destinations after the selected one */
 	for(i = hash + 1; i < idx->nr && rstate->cnt < rstate->limit; i++) {
 		if(ds_skip_dst(idx->dlist[i].flags)
 				|| (ds_use_default != 0 && i == (idx->nr - 1))) {
 			continue;
 		}
 		/* max load exceeded per destination */
 		if(rstate->alg == DS_ALG_CALLLOAD
 				&& idx->dlist[i].attrs.maxload != 0
 				&& idx->dlist[i].dload >= idx->dlist[i].attrs.maxload) {
 			continue;
 		}
 		LM_DBG("using entry [%d/%d]\n", rstate->setid, i);
 		if(ds_add_xavp_record(idx, i, rstate->setid, rstate->alg,
 					&rstate->lxavp)<0) {
 			LM_ERR("failed to add destination in the xavp (%d/%d)\n",
 					i, rstate->setid);
 			return -1;
 		}
 		rstate->cnt++;
 	}
 
 	/* add to xavp the destinations before the selected one */
 	for(i = 0; i < hash && rstate->cnt < rstate->limit; i++) {
 		if(ds_skip_dst(idx->dlist[i].flags)
 				|| (ds_use_default != 0 && i == (idx->nr - 1))) {
 			continue;
 		}
 		/* max load exceeded per destination */
 		if(rstate->alg == DS_ALG_CALLLOAD
 				&& idx->dlist[i].attrs.maxload != 0
 				&& idx->dlist[i].dload >= idx->dlist[i].attrs.maxload) {
 			continue;
 		}
 		LM_DBG("using entry [%d/%d]\n", rstate->setid, i);
 		if(ds_add_xavp_record(idx, i, rstate->setid, rstate->alg,
 					&rstate->lxavp)<0) {
 			LM_ERR("failed to add destination in the xavp (%d/%d)\n",
 					i, rstate->setid);
 			return -1;
 		}
 		rstate->cnt++;
 	}
 	return 0;
 }
 
90e3033c
 
 void ds_sorted_by_priority(sorted_ds_t * sorted_ds, int size) {
 	int i,ii;
 	for(i=0;i<size;++i) {
 		for(ii=1;ii<size;++ii) {
 			sorted_ds_t temp;
 			if(sorted_ds[ii-1].priority < sorted_ds[ii].priority) {
 				temp.idx = sorted_ds[ii].idx;
 				temp.priority = sorted_ds[ii].priority;
 				sorted_ds[ii].idx = sorted_ds[ii-1].idx;
 				sorted_ds[ii].priority = sorted_ds[ii-1].priority;
 				sorted_ds[ii-1].idx = temp.idx;
 				sorted_ds[ii-1].priority = temp.priority;
 			}
 		}
 	}
 }
 
 int ds_manage_route_algo13(ds_set_t *idx, ds_select_state_t *rstate) {
 	int hash = idx->last;
 	int y = 0;
 	int z = hash;
 	int active_priority = 0;
 	sorted_ds_t *ds_sorted = pkg_malloc(sizeof(sorted_ds_t) * idx->nr);
 	if(ds_sorted == NULL) {
 		LM_ERR("no more pkg\n");
 		return -1;
 	}
 
 	for(y=0; y<idx->nr ;y++) {
 		int latency_proirity_handicap = 0;
 		ds_dest_t * ds_dest = &idx->dlist[z];
 		int gw_priority = ds_dest->priority;
 		int gw_latency = ds_dest->latency_stats.estimate;
 		int gw_inactive = ds_skip_dst(ds_dest->flags);
 		// if cc is enabled, the latency is the congestion ms instead of the estimated latency.
 		if (ds_dest->attrs.congestion_control)
 			gw_latency = ds_dest->latency_stats.estimate - ds_dest->latency_stats.average;
 		if(!gw_inactive) {
 			if(gw_latency > gw_priority && gw_priority > 0)
 				latency_proirity_handicap = gw_latency / gw_priority;
 			ds_dest->attrs.rpriority = gw_priority - latency_proirity_handicap;
 			if(ds_dest->attrs.rpriority < 1 && gw_priority > 0)
 				ds_dest->attrs.rpriority = 1;
 			if(ds_dest->attrs.rpriority > active_priority) {
 				hash = z;
 				active_priority = ds_dest->attrs.rpriority;
 			}
 			ds_sorted[y].idx = z;
 			ds_sorted[y].priority = ds_dest->attrs.rpriority;
 			LM_DBG("[active]idx[%d]uri[%.*s]priority[%d-%d=%d]latency[%dms]flag[%d]\n",
 				z, ds_dest->uri.len, ds_dest->uri.s,
 				gw_priority, latency_proirity_handicap,
 				ds_dest->attrs.rpriority, gw_latency, ds_dest->flags);
 		} else {
 			ds_sorted[y].idx = -1;
 			ds_sorted[y].priority = -1;
 			LM_DBG("[inactive]idx[%d]uri[%.*s]priority[%d]latency[%dms]flag[%d]",
 				z, ds_dest->uri.len, ds_dest->uri.s,
 				gw_priority, gw_latency, ds_dest->flags);
 		}
 		if(ds_use_default != 0 && idx->nr != 1)
 			z = (z + 1) % (idx->nr - 1);
 		else
 			z = (z + 1) % idx->nr;
 	}
 	idx->last = (hash + 1) % idx->nr;
 	LM_DBG("priority[%d]gateway_selected[%d]next_index[%d]\n", active_priority, hash, idx->last);
 	ds_sorted_by_priority(ds_sorted, idx->nr);
 	ds_manage_routes_fill_reodered_xavp(ds_sorted, idx, rstate);
 	pkg_free(ds_sorted);
 	return hash;
 }
 
3933a64c
 /**
  *
  */
 int ds_manage_routes(sip_msg_t *msg, ds_select_state_t *rstate)
 {
 	int i;
11ff4226
 	unsigned int hash;
65160302
 	ds_set_t *idx = NULL;
18eeda01
 	int ulast = 0;
e307432e
 	int vlast = 0;
90e3033c
 	int xavp_filled = 0;
31ccf6a2
 
d081ee4e
 	if(msg == NULL) {
101e9af4
 		LM_ERR("bad parameters\n");
31ccf6a2
 		return -1;
 	}
cbe2e236
 
d081ee4e
 	if(_ds_list == NULL || _ds_list_nr <= 0) {
101e9af4
 		LM_ERR("no destination sets\n");
31ccf6a2
 		return -1;
 	}
 
3933a64c
 	if((rstate->umode == DS_SETOP_DSTURI) && (ds_force_dst == 0)
d081ee4e
 			&& (msg->dst_uri.s != NULL || msg->dst_uri.len > 0)) {
101e9af4
 		LM_ERR("destination already set [%.*s]\n", msg->dst_uri.len,
 				msg->dst_uri.s);
31ccf6a2
 		return -1;
 	}
cbe2e236
 
31ccf6a2
 
 	/* get the index of the set */
0838c3e4
 	if(ds_get_index(rstate->setid, *ds_crt_idx, &idx) != 0) {
3933a64c
 		LM_ERR("destination set [%d] not found\n", rstate->setid);
31ccf6a2
 		return -1;
 	}
cbe2e236
 
3933a64c
 	LM_DBG("set [%d]\n", rstate->setid);
31ccf6a2
 
 	hash = 0;
3933a64c
 	switch(rstate->alg) {
92f66e05
 		case DS_ALG_HASHCALLID: /* 0 - hash call-id */
d081ee4e
 			if(ds_hash_callid(msg, &hash) != 0) {
101e9af4
 				LM_ERR("can't get callid hash\n");
31ccf6a2
 				return -1;
 			}
cbe2e236
 			break;
92f66e05
 		case DS_ALG_HASHFROMURI: /* 1 - hash from-uri */
d081ee4e
 			if(ds_hash_fromuri(msg, &hash) != 0) {
101e9af4
 				LM_ERR("can't get From uri hash\n");
31ccf6a2
 				return -1;
 			}
cbe2e236
 			break;
92f66e05
 		case DS_ALG_HASHTOURI: /* 2 - hash to-uri */
d081ee4e
 			if(ds_hash_touri(msg, &hash) != 0) {
101e9af4
 				LM_ERR("can't get To uri hash\n");
b68fbcd1
 				return -1;
 			}
cbe2e236
 			break;
92f66e05
 		case DS_ALG_HASHRURI: /* 3 - hash r-uri */
d081ee4e
 			if(ds_hash_ruri(msg, &hash) != 0) {
101e9af4
 				LM_ERR("can't get ruri hash\n");
b68fbcd1
 				return -1;
 			}
cbe2e236
 			break;
92f66e05
 		case DS_ALG_ROUNDROBIN: /* 4 - round robin */
e307432e
 			lock_get(&idx->lock);
101e9af4
 			hash = idx->last;
d081ee4e
 			idx->last = (idx->last + 1) % idx->nr;
e307432e
 			vlast = idx->last;
 			lock_release(&idx->lock);
18eeda01
 			ulast = 1;
cbe2e236
 			break;
92f66e05
 		case DS_ALG_HASHAUTHUSER: /* 5 - hash auth username */
081b5d4e
 			i = ds_hash_authusername(msg, &hash);
d081ee4e
 			switch(i) {
081b5d4e
 				case 0:
 					/* Authorization-Header found: Nothing to be done here */
cbe2e236
 					break;
081b5d4e
 				case 1:
 					/* No Authorization found: Use round robin */
e307432e
 					lock_get(&idx->lock);
101e9af4
 					hash = idx->last;
d081ee4e
 					idx->last = (idx->last + 1) % idx->nr;
e307432e
 					vlast = idx->last;
 					lock_release(&idx->lock);
18eeda01
 					ulast = 1;
cbe2e236
 					break;
081b5d4e
 				default:
101e9af4
 					LM_ERR("can't get authorization hash\n");
081b5d4e
 					return -1;
 			}
cbe2e236
 			break;
92f66e05
 		case DS_ALG_RANDOM: /* 6 - random selection */
5b23ce26
 			hash = kam_rand();
cbe2e236
 			break;
92f66e05
 		case DS_ALG_HASHPV: /* 7 - hash on PV value */
d081ee4e
 			if(ds_hash_pvar(msg, &hash) != 0) {
095ab21d
 				LM_ERR("can't get PV hash\n");
 				return -1;
 			}
cbe2e236
 			break;
92f66e05
 		case DS_ALG_SERIAL: /* 8 - use always first entry */
bd1f4427
 			hash = 0;
cbe2e236
 			break;
92f66e05
 		case DS_ALG_WEIGHT: /* 9 - weight based distribution */
e307432e
 			lock_get(&idx->lock);
65160302
 			hash = idx->wlist[idx->wlast];
d081ee4e
 			idx->wlast = (idx->wlast + 1) % 100;
e307432e
 			lock_release(&idx->lock);
cbe2e236
 			break;
92f66e05
 		case DS_ALG_CALLLOAD: /* 10 - call load based distribution */
9cdc3e70
 			/* only INVITE can start a call */
d081ee4e
 			if(msg->first_line.u.request.method_value != METHOD_INVITE) {
9cdc3e70
 				/* use first entry */
 				hash = 0;
3933a64c
 				rstate->alg = 0;
9cdc3e70
 				break;
 			}
11ff4226
 			if(ds_xavp_dst.len <= 0) {
 				LM_ERR("no dst xavp for load distribution"
d081ee4e
 					   " - using first entry...\n");
9cdc3e70
 				hash = 0;
3933a64c
 				rstate->alg = 0;
9cdc3e70
 			} else {
ecd5c584
 				i = ds_get_leastloaded(idx);
d081ee4e
 				if(i < 0) {
ecd5c584
 					/* no address selected */
 					return -1;
 				}
 				hash = i;
3933a64c
 				if(ds_load_add(msg, idx, rstate->setid, hash) < 0) {
9cdc3e70
 					LM_ERR("unable to update destination load"
d081ee4e
 						   " - classic dispatching\n");
3933a64c
 					rstate->alg = 0;
9cdc3e70
 				}
 			}
cbe2e236
 			break;
92f66e05
 		case DS_ALG_RELWEIGHT: /* 11 - relative weight based distribution */
e307432e
 			lock_get(&idx->lock);
1d6c1d74
 			hash = idx->rwlist[idx->rwlast];
d081ee4e
 			idx->rwlast = (idx->rwlast + 1) % 100;
e307432e
 			lock_release(&idx->lock);
1d6c1d74
 			break;
2ce57c44
 		case DS_ALG_PARALLEL: /* 12 - parallel dispatching */
 			hash = 0;
 			break;
90e3033c
 		case DS_ALG_LATENCY: /* 13 - latency optimized round-robin with failover */
 			lock_get(&idx->lock);
 			hash = ds_manage_route_algo13(idx, rstate);
 			lock_release(&idx->lock);
 			if (hash == -1)
 				return -1;
 			xavp_filled = 1;
 			break;
31ccf6a2
 		default:
3933a64c
 			LM_WARN("algo %d not implemented - using first entry...\n",
 					rstate->alg);
31ccf6a2
 			hash = 0;
 	}
 
3933a64c
 	LM_DBG("using alg [%d] hash [%u]\n", rstate->alg, hash);
bb857f52
 
d081ee4e
 	if(ds_use_default != 0 && idx->nr != 1)
 		hash = hash % (idx->nr - 1);
a4ba0359
 	else
d081ee4e
 		hash = hash % idx->nr;
 	i = hash;
ecd5c584
 
 	/* if selected address is inactive, find next active */
90e3033c
 	while(!xavp_filled && ds_skip_dst(idx->dlist[i].flags)) {
d081ee4e
 		if(ds_use_default != 0 && idx->nr != 1)
 			i = (i + 1) % (idx->nr - 1);
a4ba0359
 		else
d081ee4e
 			i = (i + 1) % idx->nr;
 		if(i == hash) {
0f5e21c6
 			/* back to start -- looks like no active dst */
d081ee4e
 			if(ds_use_default != 0) {
 				i = idx->nr - 1;
25bedcd9
 				if(ds_skip_dst(idx->dlist[i].flags))
0f5e21c6
 					return -1;
 				break;
a4ba0359
 			} else {
44f3472f
 				return -1;
 			}
a4ba0359
 		}
 	}
 
 	hash = i;
 
3933a64c
 	if(rstate->umode!=DS_SETOP_XAVP) {
94385af9
 		if(ds_push_dst(msg, &idx->dlist[hash].uri, idx->dlist[hash].sock,
3933a64c
 					rstate->umode) != 0) {
c9e5b863
 			LM_ERR("cannot set next hop address with: %.*s\n",
 					idx->dlist[hash].uri.len, idx->dlist[hash].uri.s);
 			return -1;
 		}
074d3c67
 		rstate->emode = 1;
a4ba0359
 	}
cbe2e236
 
e307432e
 	/* update last field for next select to point after the current active used,
 	 * if not updated meanwhile */
 	lock_get(&idx->lock);
 	if(ulast && (vlast == idx->last)) {
18eeda01
 		idx->last = (hash + 1) % idx->nr;
 	}
e307432e
 	lock_release(&idx->lock);
18eeda01
 
3933a64c
 	LM_DBG("selected [%d-%d-%d/%d] <%.*s>\n", rstate->alg, rstate->setid,
 			rstate->umode, hash,
101e9af4
 			idx->dlist[hash].uri.len, idx->dlist[hash].uri.s);
a4ba0359
 
3933a64c
 	if(rstate->alg == DS_ALG_PARALLEL) {
 		if(ds_add_branches(msg, idx, hash, rstate->umode)<0) {
2ce57c44
 			LM_ERR("failed to add additional branches\n");
 			/* one destination was already set - return success anyhow */
 			return 2;
 		}
 		return 1;
 	}
 
d081ee4e
 	if(!(ds_flags & DS_FAILOVER_ON))
a4ba0359
 		return 1;
 
11ff4226
 	if(ds_xavp_dst.len<=0) {
 		/* no xavp name to store the rest of the records */
 		return 1;
 	}
aae77585
 
90e3033c
 	if(!xavp_filled) {
 		if(ds_manage_routes_fill_xavp(hash, idx, rstate) == -1){
 			return -1;
 		}
 	}
a4ba0359
 
c9e5b863
 	/* add default dst to last position in XAVP list */
3933a64c
 	if(ds_use_default != 0 && hash != idx->nr - 1
 				&& rstate->cnt < rstate->limit) {
 		LM_DBG("using default entry [%d/%d]\n", rstate->setid, idx->nr - 1);
 		if(ds_add_xavp_record(idx, idx->nr - 1, rstate->setid, rstate->alg,
 					&rstate->lxavp)<0) {
c9e5b863
 			LM_ERR("failed to add default destination in the xavp\n");
a4ba0359
 			return -1;
11ff4226
 		}
3933a64c
 		rstate->cnt++;
8cdef0e8
 	}
cbe2e236
 
a4ba0359
 	return 1;
 }
 
b55b6fa1
 int ds_update_dst(struct sip_msg *msg, int upos, int mode)
a4ba0359
 {
11ff4226
 
4099a6ca
 	int ret;
11ff4226
 	socket_info_t *sock = NULL;
 	sr_xavp_t *rxavp = NULL;
 	sr_xavp_t *lxavp = NULL;
cbe2e236
 
f8723954
 	LM_DBG("updating dst\n");
b55b6fa1
 	if(upos == DS_USE_NEXT) {
 		if(!(ds_flags & DS_FAILOVER_ON) || ds_xavp_dst.len <= 0) {
 			LM_WARN("failover support disabled\n");
 			return -1;
 		}
31ccf6a2
 	}
 
4099a6ca
 next_dst:
11ff4226
 	rxavp = xavp_get(&ds_xavp_dst, NULL);
f465d241
 	if(rxavp == NULL || rxavp->val.type != SR_XTYPE_XAVP) {
11ff4226
 		LM_DBG("no xavp with previous destination record\n");
 		return -1;
9cdc3e70
 	}
 
b55b6fa1
 	if(upos == DS_USE_NEXT) {
f8723954
 		LM_DBG("updating dst with next record\n");
b55b6fa1
 		/* use next destination - delete the current one and search the next */
 		xavp_rm(rxavp, NULL);
11ff4226
 
b55b6fa1
 		rxavp = xavp_get(&ds_xavp_dst, NULL);
f465d241
 		if(rxavp == NULL || rxavp->val.type != SR_XTYPE_XAVP) {
b55b6fa1
 			LM_DBG("no xavp with next destination record\n");
 			return -1;
 		}
aae77585
 	}
 
f465d241
 	/* retrieve attributes from sub list */
 	rxavp = rxavp->val.v.xavp;
11ff4226
 	lxavp = xavp_get(&ds_xavp_dst_sock, rxavp);
21addddd
 	if(lxavp!=NULL && lxavp->val.type==SR_XTYPE_VPTR) {
11ff4226
 		LM_DBG("socket enforced in next destination record\n");
21addddd
 		sock = lxavp->val.v.vptr;
11ff4226
 	}
a4ba0359
 
11ff4226
 	lxavp = xavp_get(&ds_xavp_dst_addr, rxavp);
 	if(lxavp==NULL || lxavp->val.type!=SR_XTYPE_STR) {
f8723954
 		LM_WARN("no xavp uri field in next destination record (%p)\n", lxavp);
11ff4226
 		return -1;
 	}
cbe2e236
 
94385af9
 	if(ds_push_dst(msg, &lxavp->val.v.s, sock, mode) != 0) {
11ff4226
 		LM_ERR("cannot set dst addr: %.*s\n", lxavp->val.v.s.len,
 				lxavp->val.v.s.s);
a4ba0359
 		return -1;
 	}
11ff4226
 	LM_DBG("using next dst uri [%.*s]\n", lxavp->val.v.s.len,
 				lxavp->val.v.s.s);
b55b6fa1
 
 	/* call load update if dstid field is set */
 	lxavp = xavp_get(&ds_xavp_dst_dstid, rxavp);
 	if(lxavp==NULL || lxavp->val.type!=SR_XTYPE_STR) {
 		/* no dstid field - done */
 		return 1;
 	}
 	if(upos == DS_USE_NEXT) {
4099a6ca
 		ret = ds_load_replace(msg, &lxavp->val.v.s);
 		switch(ret) {
 			case 0:
 				break;
 			case -2:
 				LM_ERR("cannot update load with %.*s, skipping dst.\n", lxavp->val.v.s.len, lxavp->val.v.s.s);
 				goto next_dst;
 			default:
 				LM_ERR("cannot update load distribution\n");
 				return -1;
9cdc3e70
 		}
 	}
31ccf6a2
 	return 1;
 }
 
97a198b4
 /* callback for adding nodes based on index */
d9a9e5f5
 void ds_add_dest_cb(ds_set_t *node, int i, void *arg)
97a198b4
 {
 	int setn;
fa96c3a6
 
 	if(add_dest2list(node->id, node->dlist[i].uri, node->dlist[i].flags,
0838c3e4
 			node->dlist[i].priority, &node->dlist[i].attrs.body, *ds_next_idx,
8d9e6578
 			&setn, node->dlist[i].dload) != 0) {
fa96c3a6
 		LM_WARN("failed to add destination in group %d - %.*s\n",
 				node->id, node->dlist[i].uri.len, node->dlist[i].uri.s);
 	}
97a198b4
 	return;
 }
 
 /* add dispatcher entry to in-memory dispatcher list */
2e842e9c
 int ds_add_dst(int group, str *address, int flags, str *attrs)
97a198b4
 {
 	int setn, priority;
 
 	setn = _ds_list_nr;
 	priority = 0;
2e842e9c
 
0838c3e4
 	*ds_next_idx = (*ds_crt_idx + 1) % 2;
 	ds_avl_destroy(&ds_lists[*ds_next_idx]);
97a198b4
 
 	// add all existing destinations
d9a9e5f5
 	ds_iter_set(_ds_list, &ds_add_dest_cb, NULL);
97a198b4
 
 	// add new destination
2e842e9c
 	if(add_dest2list(group, *address, flags, priority, attrs,
0838c3e4
 			*ds_next_idx, &setn, 0) != 0) {
97a198b4
 		LM_WARN("unable to add destination %.*s to set %d", address->len, address->s, group);
fa96c3a6
 		if(ds_load_mode==1) {
 			goto error;
 		}
 	}
97a198b4
 
0838c3e4
 	if(reindex_dests(ds_lists[*ds_next_idx]) != 0) {
97a198b4
 		LM_ERR("error on reindex\n");
 		goto error;
 	}
 
 	_ds_list_nr = setn;
0838c3e4
 	*ds_crt_idx = *ds_next_idx;
ebf80aca
 
97a198b4
 	ds_log_sets();
 	return 0;
 
 error:
0838c3e4
 	ds_avl_destroy(&ds_lists[*ds_next_idx]);
 	*ds_next_idx = *ds_crt_idx;
97a198b4
 	return -1;
 }
 
d9a9e5f5
 /* callback for removing nodes based on setid & address */
 void ds_filter_dest_cb(ds_set_t *node, int i, void *arg)
 {
 	struct ds_filter_dest_cb_arg *filter_arg = (typeof(filter_arg)) arg;
 
 	if(node->id == filter_arg->setid && node->dlist[i].uri.len == filter_arg->dest->uri.len &&
 		strncmp(node->dlist[i].uri.s, filter_arg->dest->uri.s, filter_arg->dest->uri.len) == 0)
 		return;
 
 	if(add_dest2list(node->id, node->dlist[i].uri, node->dlist[i].flags,
0838c3e4
 			node->dlist[i].priority, &node->dlist[i].attrs.body, *ds_next_idx,
8d9e6578
 			filter_arg->setn, node->dlist[i].dload) != 0) {
d9a9e5f5
 		LM_WARN("failed to add destination in group %d - %.*s\n",
 				node->id, node->dlist[i].uri.len, node->dlist[i].uri.s);
 	}
 	return;
 }
 
 /* remove dispatcher entry from in-memory dispatcher list */
 int ds_remove_dst(int group, str *address)
 {
 	int setn;
 	struct ds_filter_dest_cb_arg filter_arg;
 	ds_dest_t *dp = NULL;
 
 	setn = 0;
 
8d9e6578
 	dp = pack_dest(*address, 0, 0, NULL, 0);
d9a9e5f5
 	filter_arg.setid = group;
 	filter_arg.dest = dp;
 	filter_arg.setn = &setn;
 
0838c3e4
 	*ds_next_idx = (*ds_crt_idx + 1) % 2;
 	ds_avl_destroy(&ds_lists[*ds_next_idx]);
d9a9e5f5
 
 	// add existing destinations except destination that matches group & address
 	ds_iter_set(_ds_list, &ds_filter_dest_cb, &filter_arg);
 
0838c3e4
 	if(reindex_dests(ds_lists[*ds_next_idx]) != 0) {
d9a9e5f5
 		LM_ERR("error on reindex\n");
 		goto error;
 	}
 
 	_ds_list_nr = setn;
0838c3e4
 	*ds_crt_idx = *ds_next_idx;
ebf80aca
 
d9a9e5f5
 	ds_log_sets();
 	return 0;
 
 error:
0838c3e4
 	ds_avl_destroy(&ds_lists[*ds_next_idx]);
 	*ds_next_idx = *ds_crt_idx;
d9a9e5f5
 	return -1;
 }
 
cbe2e236
 int ds_mark_dst(struct sip_msg *msg, int state)
a4ba0359
 {
11ff4226
 	sr_xavp_t *rxavp = NULL;
 	int group;
 	int ret;
9d59f5cb
 	ds_rctx_t rctx;
cbe2e236
 
d081ee4e
 	if(!(ds_flags & DS_FAILOVER_ON)) {
101e9af4
 		LM_WARN("failover support disabled\n");
a4ba0359
 		return -1;
 	}
 
11ff4226
 	if(ds_xavp_dst.len<=0) {
 		LM_WARN("no xavp name to store dst records\n");
 		return -1;
 	}
 	rxavp = xavp_get_child_with_ival(&ds_xavp_dst, &ds_xavp_dst_grp);
cbe2e236
 
11ff4226
 	if(rxavp == NULL)
 		return -1; /* grp xavp not available */
 	group = rxavp->val.v.i;
cbe2e236
 
11ff4226
 	rxavp = xavp_get_child_with_sval(&ds_xavp_dst, &ds_xavp_dst_addr);
cbe2e236
 
11ff4226
 	if(rxavp == NULL )
 		return -1; /* dst addr uri not available */
cbe2e236
 
9d59f5cb
 	memset(&rctx, 0, sizeof(ds_rctx_t));
 	if(msg!=NULL) {
 		if(msg!=FAKED_REPLY) {
 			if(msg->first_line.type == SIP_REPLY) {
 				rctx.flags |= 1;
 				rctx.code = (int)msg->first_line.u.reply.statuscode;
 				rctx.reason = msg->first_line.u.reply.reason;
 			} else {
 				rctx.code = 820;
 			}
 		} else {
 			rctx.code = 810;
 		}
 	} else {
 		rctx.code = 800;
 	}
 	ret = ds_update_state(msg, group, &rxavp->val.v.s, state, &rctx);
cbe2e236
 
11ff4226
 	LM_DBG("state [%d] grp [%d] dst [%.*s]\n", state, group, rxavp->val.v.s.len,
 			rxavp->val.v.s.s);
cbe2e236
 
d081ee4e
 	return (ret == 0) ? 1 : -1;
a4ba0359
 }
 
cf105d5a
 void latency_stats_init(ds_latency_stats_t *latency_stats, int latency, int count)
 {
8429ef38
 	latency_stats->stdev = 0.0f;
 	latency_stats->m2 = 0.0f;
 	latency_stats->max = latency;
 	latency_stats->min = latency;
 	latency_stats->average = latency;
 	latency_stats->estimate = latency;
 	latency_stats->count = count;
 }
 
cf105d5a
 #define _VOR1(v) ((v)?(v):1)
 
 static inline void latency_stats_update(ds_latency_stats_t *latency_stats, int latency)
 {
b728cb30
 	int training_count = 10000;
 
b103fefd
 	/* after 2^21 ~24 days at 1s interval, the average becomes a weighted average */
 	if (latency_stats->count < 2097152) {
12a25ed7
 		latency_stats->count++;
b103fefd
 	} else { /* We adjust the sum of squares used by the oneline algorithm proportionally */
cf105d5a
 		latency_stats->m2 -= latency_stats->m2/_VOR1(latency_stats->count);
b103fefd
 	}
8429ef38
 
 	if (latency_stats->count == 1)
 		latency_stats_init(latency_stats, latency, 1);
b728cb30
 	/* stabilize-train the estimator if the average is stable after 10 samples */
 	if (latency_stats->count > 10 && latency_stats->count < training_count
 	        && latency_stats->stdev < 0.5)
 		latency_stats->count = training_count;
8429ef38
 
12a25ed7
 	if (latency_stats->min > latency)
 		latency_stats->min = latency;
 	if (latency_stats->max < latency)
 		latency_stats->max = latency;
 
b103fefd
 	/* standard deviation using oneline algorithm */
 	/* https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Online_algorithm */
12a25ed7
 	if (latency_stats->count > 1) {
ebf80aca
 		float delta;
 		float delta2;
 		delta = latency - latency_stats->average;
cf105d5a
 		latency_stats->average += delta/_VOR1(latency_stats->count);
ebf80aca
 		delta2 = latency - latency_stats->average;
 		latency_stats->m2 += ((double)delta)*delta2;
cf105d5a
 		latency_stats->stdev = sqrt(latency_stats->m2 / _VOR1(latency_stats->count-1));
12a25ed7
 	}
 	/* exponentialy weighted moving average */
 	if (latency_stats->count < 10) {
 		latency_stats->estimate = latency_stats->average;
 	} else {
 		latency_stats->estimate = latency_stats->estimate*ds_latency_estimator_alpha
 		                          + latency*(1-ds_latency_estimator_alpha);
 	}
 }
 
b2cd7018
 typedef struct congestion_control_state {
 	int gw_congested_count;
 	int gw_normal_count;
 	int total_congestion_ms;
 	int enabled;
 	int apply_rweights;
 } congestion_control_state_t;
 
cf105d5a
 int ds_update_weighted_congestion_control(congestion_control_state_t *cc,
 		int weight, ds_latency_stats_t *latency_stats)
b2cd7018
 {
 	int active_weight = 0;
 	int congestion_ms = latency_stats->estimate - latency_stats->average;
 	if (weight <= 0) return 0;
 	if (congestion_ms < 0) congestion_ms = 0;
 	cc->total_congestion_ms += congestion_ms;
 	active_weight = weight - congestion_ms;
 	if (active_weight < 0) active_weight = 0;
 	if (active_weight == 0) {
 		cc->gw_congested_count++;
 	} else {
 		cc->gw_normal_count++;
 	}
 	return active_weight;
 }
 
 void ds_init_congestion_control_state(congestion_control_state_t *cc)
 {
 	cc->gw_congested_count = 0;
 	cc->gw_normal_count = 0;
 	cc->total_congestion_ms = 0;
 	cc->enabled = 1;
 	cc->apply_rweights = 0;
 }
 
12a25ed7
 int ds_update_latency(int group, str *address, int code)
 {
 	int i = 0;
 	int state = 0;
 	ds_set_t *idx = NULL;
b2cd7018
 	congestion_control_state_t cc;
 	ds_init_congestion_control_state(&cc);
12a25ed7
 
 	if(_ds_list == NULL || _ds_list_nr <= 0) {
 		LM_ERR("the list is null\n");
 		return -1;
 	}
 
 	/* get the index of the set */
0838c3e4
 	if(ds_get_index(group, *ds_crt_idx, &idx) != 0) {
12a25ed7
 		LM_ERR("destination set [%d] not found\n", group);
 		return -1;
 	}
1d304002
 	lock_get(&idx->lock);
 	while (i < idx->nr) {
 		ds_dest_t *ds_dest = &idx->dlist[i];
 		ds_latency_stats_t *latency_stats = &ds_dest->latency_stats;
 		if (ds_dest->uri.len == address->len
 				&& strncasecmp(ds_dest->uri.s, address->s, address->len) == 0) {
ebf80aca
 			struct timeval now;
 			int latency_ms;
1d304002
 			/* Destination address found, this is the gateway that was pinged. */
 			state = ds_dest->flags;
90e3033c
 			if (!(state & DS_PROBING_DST)) {
 				i++;
 				continue;
 			}
1d304002
 			if (code == 408 && latency_stats->timeout < UINT32_MAX)
12a25ed7
 				latency_stats->timeout++;
1d304002
 			gettimeofday(&now, NULL);
ebf80aca
 			latency_ms = (now.tv_sec - latency_stats->start.tv_sec)*1000
1d304002
 		            + (now.tv_usec - latency_stats->start.tv_usec)/1000;
90e3033c
 			if (code != 408)
 				latency_stats_update(latency_stats, latency_ms);
 
 			LM_DBG("[%d]latency[%d]avg[%.2f][%.*s]code[%d]rweight[%d]\n",
 					latency_stats->count, latency_ms,
 					latency_stats->average, address->len, address->s,
 					code, ds_dest->attrs.rweight);
1d304002
 
 			/* Adjusting weight using congestion detection based on latency estimator. */
b2cd7018
 			if (ds_dest->attrs.congestion_control && ds_dest->attrs.weight > 0) {
 				int active_weight = ds_update_weighted_congestion_control(&cc, ds_dest->attrs.weight, latency_stats);
1d304002
 				if (ds_dest->attrs.rweight != active_weight) {
b2cd7018
 					cc.apply_rweights = 1;
1d304002
 					ds_dest->attrs.rweight = active_weight;
 				}
 				LM_DBG("[%d]latency[%d]avg[%.2f][%.*s]code[%d]rweight[%d]cms[%d]\n",
 					latency_stats->count, latency_ms,
 					latency_stats->average, address->len, address->s,
b2cd7018
 					code, ds_dest->attrs.rweight, ds_dest->attrs.weight - active_weight);
12a25ed7
 			}
b2cd7018
 		} else if (ds_dest->attrs.congestion_control && ds_dest->attrs.weight > 0) {
 			/* This is another gateway in the set, we verify if it is congested. */
 			ds_update_weighted_congestion_control(&cc, ds_dest->attrs.weight, latency_stats);
1d304002
 		}
b2cd7018
 		if (!ds_dest->attrs.congestion_control) cc.enabled = 0;
12a25ed7
 		i++;
 	}
1d304002
 	/* All the GWs are above their congestion threshold, load distribution will now be based on
 	 * the ratio of congestion_ms each GW is facing. */
b2cd7018
 	if (cc.enabled && cc.gw_congested_count > 1 && cc.gw_normal_count == 0) {
1d304002
 		i = 0;
 		while (i < idx->nr) {
ebf80aca
 			int congestion_ms;
 			int active_weight;
1d304002
 			ds_dest_t *ds_dest = &idx->dlist[i];
 			ds_latency_stats_t *latency_stats = &ds_dest->latency_stats;
ebf80aca
 			congestion_ms = latency_stats->estimate - latency_stats->average;
1d304002
 			/* We multiply by 2^4 to keep enough precision */
cf105d5a
 			active_weight = (cc.total_congestion_ms << 4) / _VOR1(congestion_ms);
1d304002
 			if (ds_dest->attrs.rweight != active_weight) {
b2cd7018
 				cc.apply_rweights = 1;
1d304002
 				ds_dest->attrs.rweight = active_weight;
 			}
 			LM_DBG("all gw congested[%d][%d]latency_avg[%.2f][%.*s]code[%d]rweight[%d/%d:%d]cms[%d]\n",
b2cd7018
 				        cc.total_congestion_ms, latency_stats->count, latency_stats->average,
 				        ds_dest->uri.len, ds_dest->uri.s, code, cc.total_congestion_ms, congestion_ms,
1d304002
 				        ds_dest->attrs.rweight, congestion_ms);
 		i++;
 		}
 	}
 
 	lock_release(&idx->lock);
b2cd7018
 	if (cc.enabled && cc.apply_rweights) dp_init_relative_weights(idx);
12a25ed7
 	return state;
 }
 
 
241a05bc
 /**
2e567ce1
  * Get state for given destination
  */
 int ds_get_state(int group, str *address)
 {
d081ee4e
 	int i = 0;
2e567ce1
 	ds_set_t *idx = NULL;
 
d081ee4e
 	if(_ds_list == NULL || _ds_list_nr <= 0) {
2e567ce1
 		LM_ERR("the list is null\n");
 		return -1;
 	}
 
 	/* get the index of the set */
0838c3e4
 	if(ds_get_index(group, *ds_crt_idx, &idx) != 0) {
2e567ce1
 		LM_ERR("destination set [%d] not found\n", group);
 		return -1;
 	}
 
d081ee4e
 	while(i < idx->nr) {
 		if(idx->dlist[i].uri.len == address->len
 				&& strncasecmp(idx->dlist[i].uri.s, address->s, address->len)
 						   == 0) {
2e567ce1
 			/* destination address found */
7a66f445
 			return idx->dlist[i].flags;
2e567ce1
 		}
 		i++;
 	}
7a66f445
 	return 0;
2e567ce1
 }
 
 /**
  * Update destionation's state
241a05bc
  */
9d59f5cb
 int ds_update_state(sip_msg_t *msg, int group, str *address, int state,
 		ds_rctx_t *rctx)
a4ba0359
 {
d081ee4e
 	int i = 0;
cbe2e236
 	int old_state = 0;
92db7b10
 	int init_state = 0;
65160302
 	ds_set_t *idx = NULL;
a4ba0359
 
d081ee4e
 	if(_ds_list == NULL || _ds_list_nr <= 0) {
101e9af4
 		LM_ERR("the list is null\n");
a4ba0359
 		return -1;
 	}
cbe2e236
 
a4ba0359
 	/* get the index of the set */
0838c3e4
 	if(ds_get_index(group, *ds_crt_idx, &idx) != 0) {
101e9af4
 		LM_ERR("destination set [%d] not found\n", group);
a4ba0359
 		return -1;
 	}
8ee71161
 	LM_DBG("update state for %.*s in group %d to %d\n", address->len, address->s, group, state);
a4ba0359
 
d081ee4e
 	while(i < idx->nr) {
 		if(idx->dlist[i].uri.len == address->len
 				&& strncasecmp(idx->dlist[i].uri.s, address->s, address->len)
 						   == 0) {
cbe2e236
 			/* destination address found */
 			old_state = idx->dlist[i].flags;
 
 			/* reset the bits used for states */
 			idx->dlist[i].flags &= ~(DS_STATES_ALL);
848394fa
 
2e567ce1
 			/* we need the initial state for inactive counter */
92db7b10
 			init_state = state;
848394fa
 
d081ee4e
 			if((state & DS_TRYING_DST) && (old_state & DS_INACTIVE_DST)) {
d6654584
 				/* old state is inactive, new state is trying => keep it inactive
 				 * - it has to go first to active state and then to trying */
1f63d8d3
 				state &= ~(DS_TRYING_DST);
 				state |= DS_INACTIVE_DST;
d6654584
 			}
 
cbe2e236
 			/* set the new states */
d081ee4e
 			if(state & DS_DISABLED_DST) {
cbe2e236
 				idx->dlist[i].flags |= DS_DISABLED_DST;
d6654584
 			} else {
cbe2e236
 				idx->dlist[i].flags |= state;
d6654584
 			}
848394fa
 
d081ee4e
 			if(state & DS_TRYING_DST) {
2e567ce1
 				idx->dlist[i].message_count++;
8ee71161
 				LM_DBG("destination did not replied %d times, threshold %d\n",
 						 idx->dlist[i].message_count, probing_threshold);
2e567ce1
 				/* Destination is not replying.. Increasing failure counter */
d081ee4e
 				if(idx->dlist[i].message_count >= probing_threshold) {
8ee71161
 					/* Destination has too much lost messages.. Bringing it to inactive state */
cbe2e236
 					idx->dlist[i].flags &= ~DS_TRYING_DST;
 					idx->dlist[i].flags |= DS_INACTIVE_DST;
2e567ce1
 					idx->dlist[i].message_count = 0;
8ee71161
 					LM_DBG("deactivate destination, threshold %d reached\n", probing_threshold);
081b5d4e
 				}
cbe2e236
 			} else {
d081ee4e
 				if(!(init_state & DS_TRYING_DST)
 						&& (old_state & DS_INACTIVE_DST)) {
2e567ce1
 					idx->dlist[i].message_count++;
 					/* Destination was inactive but it is just replying.. Increasing successful counter */
d081ee4e
 					if(idx->dlist[i].message_count < inactive_threshold) {
2e567ce1
 						/* Destination has not enough successful replies.. Leaving it into inactive state */
 						idx->dlist[i].flags |= DS_INACTIVE_DST;
ee45c2a0
 						/* if destination was in probing state, we stay there for now */
 						if((old_state & DS_PROBING_DST) != 0) {
 							idx->dlist[i].flags |= DS_PROBING_DST;
 						}
8ee71161
 						LM_DBG("destination replied successful %d times, threshold %d\n",
 								 idx->dlist[i].message_count, inactive_threshold);
d081ee4e
 					} else {
2e567ce1
 						/* Destination has enough replied messages.. Bringing it to active state */
 						idx->dlist[i].message_count = 0;
8ee71161
 						LM_DBG("activate destination, threshold %d reached\n", inactive_threshold);
2e567ce1
 					}
d081ee4e
 				} else {
2e567ce1
 					idx->dlist[i].message_count = 0;
 				}
081b5d4e
 			}
08563829
 
d081ee4e
 			if(!ds_skip_dst(old_state) && ds_skip_dst(idx->dlist[i].flags)) {
9d59f5cb
 				ds_run_route(msg, address, "dispatcher:dst-down", rctx);
08563829
 
cbe2e236
 			} else {
 				if(ds_skip_dst(old_state) && !ds_skip_dst(idx->dlist[i].flags))
9d59f5cb
 					ds_run_route(msg, address, "dispatcher:dst-up", rctx);
08563829
 			}
d081ee4e
 			if(idx->dlist[i].attrs.rweight > 0)
 				ds_reinit_rweight_on_state_change(
 						old_state, idx->dlist[i].flags, idx);
cbe2e236
 
8ee71161
 			LM_DBG("old state was %d, set new state to %d\n", old_state, idx->dlist[i].flags);
a4ba0359
 			return 0;
 		}
 		i++;
 	}
 
 	return -1;
 }
 
9d59f5cb
 /**
  *
  */
 static ds_rctx_t *_ds_rctx = NULL;
 
 /**
  *
  */
 ds_rctx_t* ds_get_rctx(void)
 {
 	return _ds_rctx;
 }
 
 static void ds_run_route(sip_msg_t *msg, str *uri, char *route, ds_rctx_t *rctx)
08563829
 {
1f63d8d3
 	int rt, backup_rt;
390ec435
 	struct run_act_ctx ctx;
1f63d8d3
 	sip_msg_t *fmsg;
6aa21738
 	sr_kemi_eng_t *keng = NULL;
 	str evname;
08563829
 
d081ee4e
 	if(route == NULL) {
390ec435
 		LM_ERR("bad route\n");
 		return;
08563829
 	}
390ec435
 
9d59f5cb
 	LM_DBG("executing event_route[%s]\n", route);
1f63d8d3
 
6aa21738
 	rt = -1;
 	if(ds_event_callback.s==NULL || ds_event_callback.len<=0) {
 		rt = route_lookup(&event_rt, route);
 		if(rt < 0 || event_rt.rlist[rt] == NULL) {
 			LM_DBG("route does not exist");
 			return;
 		}
 	} else {
 		keng = sr_kemi_eng_get();
 		if(keng==NULL) {
 			LM_DBG("event callback (%s) set, but no cfg engine\n",
 					ds_event_callback.s);
 			return;
 		}
08563829
 	}
 
d081ee4e
 	if(msg == NULL) {
 		if(faked_msg_init() < 0) {
1f63d8d3
 			LM_ERR("faked_msg_init() failed\n");
 			return;
 		}
 		fmsg = faked_msg_next();
 		fmsg->parsed_orig_ruri_ok = 0;
 		fmsg->new_uri = *uri;
 	} else {
 		fmsg = msg;
 	}
 
6aa21738
 	if(rt>=0 || ds_event_callback.len>0) {
9d59f5cb
 		_ds_rctx = rctx;
6aa21738
 		backup_rt = get_route_type();
 		set_route_type(REQUEST_ROUTE);
 		init_run_actions_ctx(&ctx);
 		if(rt>=0) {
 			run_top_route(event_rt.rlist[rt], fmsg, 0);
 		} else {
 			if(keng!=NULL) {
 				evname.s = route;
 				evname.len = strlen(evname.s);
ea9c1f5d
 				if(sr_kemi_route(keng, fmsg, EVENT_ROUTE,
6aa21738
 							&ds_event_callback, &evname)<0) {
 					LM_ERR("error running event route kemi callback\n");
 				}
 			}
 		}
 		set_route_type(backup_rt);
9d59f5cb
 		_ds_rctx = NULL;
6aa21738
 	}
08563829
 }
 
1d6c1d74
 
 /**
1d401e0a
  * recalculate relative states if some destination state was changed
1d6c1d74
  */
d081ee4e
 int ds_reinit_rweight_on_state_change(
 		int old_state, int new_state, ds_set_t *dset)
1d6c1d74
 {
d081ee4e
 	if(dset == NULL) {
1d6c1d74
 		LM_ERR("destination set is null\n");
 		return -1;
 	}
d081ee4e
 	if((!ds_skip_dst(old_state) && ds_skip_dst(new_state))
 			|| (ds_skip_dst(old_state) && !ds_skip_dst(new_state))) {
1d6c1d74
 		dp_init_relative_weights(dset);
 	}
 
 	return 0;
 }
 
 
241a05bc
 /**
  *
  */
 int ds_reinit_state(int group, str *address, int state)
 {
d081ee4e
 	int i = 0;
241a05bc
 	ds_set_t *idx = NULL;
 
d081ee4e
 	if(_ds_list == NULL || _ds_list_nr <= 0) {
241a05bc
 		LM_ERR("the list is null\n");
 		return -1;
 	}
 
 	/* get the index of the set */
0838c3e4
 	if(ds_get_index(group, *ds_crt_idx, &idx) != 0) {
241a05bc
 		LM_ERR("destination set [%d] not found\n", group);
 		return -1;
 	}
 
d081ee4e
 	for(i = 0; i < idx->nr; i++) {
 		if(idx->dlist[i].uri.len == address->len
 				&& strncasecmp(idx->dlist[i].uri.s, address->s, address->len)
 						   == 0) {
1d6c1d74
 			int old_state = idx->dlist[i].flags;
25bedcd9
 			/* reset the bits used for states */
 			idx->dlist[i].flags &= ~(DS_STATES_ALL);
 			/* set the new states */
241a05bc
 			idx->dlist[i].flags |= state;
d081ee4e
 			if(idx->dlist[i].attrs.rweight > 0) {
 				ds_reinit_rweight_on_state_change(
 						old_state, idx->dlist[i].flags, idx);
1d6c1d74
 			}
 
241a05bc
 			return 0;
 		}
 	}
d081ee4e
 	LM_ERR("destination address [%d : %.*s] not found\n", group, address->len,
 			address->s);
241a05bc
 	return -1;
 }
996f50eb
 
b7b0f258
 /**
  *
  */
 int ds_reinit_duid_state(int group, str *vduid, int state)
 {
 	int i = 0;
 	ds_set_t *idx = NULL;
 
 	if(_ds_list == NULL || _ds_list_nr <= 0) {
 		LM_ERR("the list is null\n");
 		return -1;
 	}
 
 	/* get the index of the set */
0838c3e4
 	if(ds_get_index(group, *ds_crt_idx, &idx) != 0) {
b7b0f258
 		LM_ERR("destination set [%d] not found\n", group);
 		return -1;
 	}
 
 	for(i = 0; i < idx->nr; i++) {
 		if(idx->dlist[i].attrs.duid.len == vduid->len
 				&& strncasecmp(idx->dlist[i].attrs.duid.s, vduid->s, vduid->len)
 						   == 0) {
 			int old_state = idx->dlist[i].flags;
 			/* reset the bits used for states */
 			idx->dlist[i].flags &= ~(DS_STATES_ALL);
 			/* set the new states */
 			idx->dlist[i].flags |= state;
 			if(idx->dlist[i].attrs.rweight > 0) {
 				ds_reinit_rweight_on_state_change(
 						old_state, idx->dlist[i].flags, idx);
 			}
 
 			return 0;
 		}
 	}
 	LM_ERR("destination duid [%d : %.*s] not found\n", group, vduid->len,
 			vduid->s);
 	return -1;
 }
 
d8cc8626
 /**
  *
  */
 int ds_reinit_state_all(int group, int state)
 {
 	int i = 0;
 	ds_set_t *idx = NULL;
 
 	if(_ds_list == NULL || _ds_list_nr <= 0) {
 		LM_ERR("the list is null\n");
 		return -1;
 	}
 
 	/* get the index of the set */
0838c3e4
 	if(ds_get_index(group, *ds_crt_idx, &idx) != 0) {
d8cc8626
 		LM_ERR("destination set [%d] not found\n", group);
 		return -1;
 	}
 
 	for(i = 0; i < idx->nr; i++) {
 		int old_state = idx->dlist[i].flags;
 		/* reset the bits used for states */
 		idx->dlist[i].flags &= ~(DS_STATES_ALL);
 		/* set the new states */
 		idx->dlist[i].flags |= state;
 		if(idx->dlist[i].attrs.rweight > 0) {
 			ds_reinit_rweight_on_state_change(
 					old_state, idx->dlist[i].flags, idx);
 		}
 	}
 	return 0;
 }
 
241a05bc
 /**
  *
  */
d081ee4e
 void ds_fprint_set(FILE *fout, ds_set_t *node)
a4ba0359
 {
f0f6269a
 	int i, j;
 
d081ee4e
 	if(!node)
996f50eb
 		return;
 
d081ee4e
 	for(i = 0; i < 2; ++i)
 		ds_fprint_set(fout, node->next[i]);
996f50eb
 
d081ee4e
 	for(j = 0; j < node->nr; j++) {
996f50eb
 		fprintf(fout, "\n set #%d\n", node->id);
 
d081ee4e
 		if(node->dlist[j].flags & DS_DISABLED_DST)
996f50eb
 			fprintf(fout, "    Disabled         ");
d081ee4e
 		else if(node->dlist[j].flags & DS_INACTIVE_DST)
996f50eb
 			fprintf(fout, "    Inactive         ");
d081ee4e
 		else if(node->dlist[j].flags & DS_TRYING_DST) {
996f50eb
 			fprintf(fout, "    Trying");
 			/* print the tries for this host. */
d081ee4e
 			if(node->dlist[j].message_count > 0) {
 				fprintf(fout, " (Fail %d/%d)", node->dlist[j].message_count,
996f50eb
 						probing_threshold);
 			} else {
 				fprintf(fout, "           ");
 			}
cbe2e236
 
996f50eb
 		} else {
 			fprintf(fout, "    Active           ");
 		}
d081ee4e
 		if(node->dlist[j].flags & DS_PROBING_DST)
996f50eb
 			fprintf(fout, "(P)");
 		else
 			fprintf(fout, "(*)");
 
d081ee4e
 		fprintf(fout, "   %.*s\n", node->dlist[j].uri.len,
 				node->dlist[j].uri.s);
996f50eb
 	}
 }
 
 /**
  *
  */
 int ds_fprint_list(FILE *fout)
 {
d081ee4e
 	if(_ds_list == NULL || _ds_list_nr <= 0) {
101e9af4
 		LM_ERR("no destination sets\n");
a4ba0359
 		return -1;
 	}
cbe2e236
 
a4ba0359
 	fprintf(fout, "\nnumber of destination sets: %d\n", _ds_list_nr);
cbe2e236
 
d081ee4e
 	ds_fprint_set(fout, _ds_list);
996f50eb
 
 	return 0;
 }
 
 
d081ee4e
 int ds_is_addr_from_set(sip_msg_t *_m, struct ip_addr *pipaddr,
 		unsigned short tport, unsigned short tproto, ds_set_t *node, int mode,
 		int export_set_pv)
996f50eb
 {
 	pv_value_t val;
 	int j;
d081ee4e
 	for(j = 0; j < node->nr; j++) {
1c58b8e0
 		if(node->dlist[j].irmode & DS_IRMODE_NOIPADDR) {
 			/* dst record using hotname with dns not done - no ip to match */
 			continue;
 		}
d081ee4e
 		if(ip_addr_cmp(pipaddr, &node->dlist[j].ip_address)
 				&& ((mode & DS_MATCH_NOPORT) || node->dlist[j].port == 0
 						   || tport == node->dlist[j].port)
 				&& ((mode & DS_MATCH_NOPROTO)
2945ba44
 						   || tproto == node->dlist[j].proto)
 				&& (((mode & DS_MATCH_ACTIVE) && !ds_skip_dst(node->dlist[j].flags))
 						   || !(mode & DS_MATCH_ACTIVE))) {
d081ee4e
 			if(export_set_pv && ds_setid_pvname.s != 0) {
996f50eb
 				memset(&val, 0, sizeof(pv_value_t));
d081ee4e
 				val.flags = PV_VAL_INT | PV_TYPE_INT;
cbe2e236
 
996f50eb
 				val.ri = node->id;
d081ee4e
 				if(ds_setid_pv.setf(_m, &ds_setid_pv.pvp, (int)EQ_T, &val)
 						< 0) {
996f50eb
 					LM_ERR("setting PV failed\n");
 					return -2;
 				}
cbe2e236
 			}
d081ee4e
 			if(ds_attrs_pvname.s != 0 && node->dlist[j].attrs.body.len > 0) {
996f50eb
 				memset(&val, 0, sizeof(pv_value_t));
 				val.flags = PV_VAL_STR;
 				val.rs = node->dlist[j].attrs.body;
d081ee4e
 				if(ds_attrs_pv.setf(_m, &ds_attrs_pv.pvp, (int)EQ_T, &val)
 						< 0) {
996f50eb
 					LM_ERR("setting attrs pv failed\n");
 					return -3;
 				}
 			}
 			return 1;
a4ba0359
 		}
 	}
996f50eb
 	return -1;
a4ba0359
 }
 
996f50eb
 /**
  *
  */
d081ee4e
 int ds_is_addr_from_set_r(sip_msg_t *_m, struct ip_addr *pipaddr,
 		unsigned short tport, unsigned short tproto, ds_set_t *node, int mode,
 		int export_set_pv)
996f50eb
 {
f0f6269a
 	int i, rc;
 
d081ee4e
 	if(!node)
996f50eb
 		return -1;
 
d081ee4e
 	for(i = 0; i < 2; ++i) {
 		rc = ds_is_addr_from_set_r(
 				_m, pipaddr, tport, tproto, node->next[i], mode, export_set_pv);
 		if(rc != -1)
996f50eb
 			return rc;
 	}
 
d081ee4e
 	return ds_is_addr_from_set(
 			_m, pipaddr, tport, tproto, node, mode, export_set_pv);
996f50eb
 }
ba07eb39
 
081b5d4e
 /* Checks, if the request (sip_msg *_m) comes from a host in a group
  * (group-id or -1 for all groups)
  */
6016958d
 int ds_is_addr_from_list(sip_msg_t *_m, int group, str *uri, int mode)
b14bb7d7
 {
65160302
 	ds_set_t *list;
996f50eb
 
d081ee4e
 	struct ip_addr *pipaddr;
 	struct ip_addr aipaddr;
6016958d
 	unsigned short tport;
 	unsigned short tproto;
 	sip_uri_t puri;
 	static char hn[256];
d081ee4e
 	struct hostent *he;
f0f6269a
 	int rc = -1;
101e9af4
 
d081ee4e
 	if(uri == NULL || uri->len <= 0) {
6016958d
 		pipaddr = &_m->rcv.src_ip;
 		tport = _m->rcv.src_port;
 		tproto = _m->rcv.proto;
 	} else {
d081ee4e
 		if(parse_uri(uri->s, uri->len, &puri) != 0 || puri.host.len > 255) {
6016958d
 			LM_ERR("bad uri [%.*s]\n", uri->len, uri->s);
 			return -1;
 		}
 		strncpy(hn, puri.host.s, puri.host.len);
d081ee4e
 		hn[puri.host.len] = '\0';
6016958d
 
d081ee4e
 		he = resolvehost(hn);
 		if(he == 0) {
6016958d
 			LM_ERR("could not resolve %.*s\n", puri.host.len, puri.host.s);
 			return -1;
 		}
 		hostent2ip_addr(&aipaddr, he, 0);
 		pipaddr = &aipaddr;
 		tport = puri.port_no;
 		tproto = puri.proto;
 	}
 
996f50eb
 
d081ee4e
 	if(group == -1) {
9eba5b67
 		rc = ds_is_addr_from_set_r(_m, pipaddr, tport, tproto, _ds_list,
 				mode, 1);
f0f6269a
 	} else {
d081ee4e
 		list = ds_avl_find(_ds_list, group);
 		if(list) {
 			rc = ds_is_addr_from_set(_m, pipaddr, tport, tproto, list, mode, 0);
f0f6269a
 		}
996f50eb
 	}
 
 	return rc;
081b5d4e
 }
 
6016958d
 int ds_is_from_list(struct sip_msg *_m, int group)
 {
 	return ds_is_addr_from_list(_m, group, NULL, DS_MATCH_NOPROTO);
 }
b14bb7d7
 
d574a210
 /**
  * Check if the a group has any or a specific active uri
  */
 int ds_is_active_uri(sip_msg_t *msg, int group, str *uri)
 {
 	ds_set_t *list;
 	int j;
 
 	list = ds_avl_find(_ds_list, group);
 	if(list) {
 		for(j = 0; j < list->nr; j++) {
 			if(!ds_skip_dst(list->dlist[j].flags)) {
 				if(uri==NULL || uri->s==NULL || uri->len<=0) {
 					return 1;
 				}
 				if((list->dlist[j].uri.len==uri->len)
 						&& (memcmp(list->dlist[j].uri.s, uri->s, uri->len)==0)) {
 					return 1;
 				}
 			}
 		}
 	}
 
 	return -1;
 }
 
aecdbe9b
 /*! \brief
081b5d4e
  * Callback-Function for the OPTIONS-Request
  * This Function is called, as soon as the Transaction is finished
  * (e. g. a Response came in, the timeout was hit, ...)
9cdc3e70
  */
d081ee4e
 static void ds_options_callback(
 		struct cell *t, int type, struct tmcb_params *ps)
081b5d4e
 {
 	int group = 0;
 	str uri = {0, 0};
1f63d8d3
 	sip_msg_t *fmsg;
cbe2e236
 	int state;
9d59f5cb
 	ds_rctx_t rctx;
390ec435
 
2664cb9a
 	/* The param contains the group, in which the failed host
081b5d4e
 	 * can be found.*/
d081ee4e
 	if(ps->param == NULL) {
101e9af4
 		LM_DBG("No parameter provided, OPTIONS-Request was finished"
d081ee4e
 			   " with code %d\n",
 				ps->code);
081b5d4e
 		return;
 	}
1f63d8d3
 
 	fmsg = NULL;
 
081b5d4e
 	/* The param is a (void*) Pointer, so we need to dereference it and
 	 *  cast it to an int. */
cfc67d90
 	group = (int)(long)(*ps->param);
081b5d4e
 	/* The SIP-URI is taken from the Transaction.
f22dcd55
 	 * Remove the "To: <" (s+5) and the trailing >+new-line (s - 5 (To: <)
 	 * - 3 (>\r\n)). */
 	uri.s = t->to.s + 5;
 	uri.len = t->to.len - 8;
101e9af4
 	LM_DBG("OPTIONS-Request was finished with code %d (to %.*s, group %d)\n",
081b5d4e
 			ps->code, uri.len, uri.s, group);
9d59f5cb
 	if (ds_ping_latency_stats) {
12a25ed7
 		ds_update_latency(group, &uri, ps->code);
9d59f5cb
 	}
 
 	memset(&rctx, 0, sizeof(ds_rctx_t));
 	rctx.code = ps->code;
 	if(ps->rpl!=NULL) {
 		if(ps->rpl!=FAKED_REPLY) {
 			rctx.flags |= 1;
 			rctx.reason = ps->rpl->first_line.u.reply.reason;
 		}
 	}
 
081b5d4e
 	/* ps->code contains the result-code of the request.
9cdc3e70
 	 *
ec903da3
 	 * We accept both a "200 OK" or the configured reply as a valid response */
d081ee4e
 	if((ps->code >= 200 && ps->code <= 299)
 			|| ds_ping_check_rplcode(ps->code)) {
cbe2e236
 		/* Set the according entry back to "Active" */
 		state = 0;
d081ee4e
 		if(ds_probing_mode == DS_PROBE_ALL
 				|| ((ds_probing_mode == DS_PROBE_ONLYFLAGGED)
 						   && (ds_get_state(group, &uri) & DS_PROBING_DST)))
cbe2e236
 			state |= DS_PROBING_DST;
65a442a2
 
2e567ce1
 		/* Check if in the meantime someone disabled the target through RPC or MI */
d081ee4e
 		if(!(ds_get_state(group, &uri) & DS_DISABLED_DST)
9d59f5cb
 				&& ds_update_state(fmsg, group, &uri, state, &rctx) != 0) {
101e9af4
 			LM_ERR("Setting the state failed (%.*s, group %d)\n", uri.len,
 					uri.s, group);
081b5d4e
 		}
cbe2e236
 	} else {
 		state = DS_TRYING_DST;
d081ee4e
 		if(ds_probing_mode != DS_PROBE_NONE)
cbe2e236
 			state |= DS_PROBING_DST;
2e567ce1
 		/* Check if in the meantime someone disabled the target through RPC or MI */
d081ee4e
 		if(!(ds_get_state(group, &uri) & DS_DISABLED_DST)
9d59f5cb
 				&& ds_update_state(fmsg, group, &uri, state, &rctx) != 0) {
d1de0cc5
 			LM_ERR("Setting the probing state failed (%.*s, group %d)\n",
 					uri.len, uri.s, group);
 		}
 	}
081b5d4e
 
 	return;
 }
 
ee45c2a0
 /*
  * Small helper to decide to ping a gateway or not
  */
 static inline int ds_ping_result_helper(ds_set_t *node, int j)
 {
 	/* probe all */
 	if(ds_probing_mode == DS_PROBE_ALL) {
 		LM_DBG("probe all, mode DS_PROBE_ALL\n");
 		return 1;
 	}
 	/* probe if probing is set, but not in mode DS_PROBE_INACTIVE */
 	if (ds_probing_mode != DS_PROBE_INACTIVE
 			&& (node->dlist[j].flags & DS_PROBING_DST) != 0) {
 		LM_DBG("probing set, but not mode DS_PROBE_INACTIVE\n");
 		return 1;
 	}
 	/* probe for mode DS_PROBE_INACTIVE only for inactive and probing gw */
 	if (ds_probing_mode == DS_PROBE_INACTIVE
 			&& (node->dlist[j].flags & DS_PROBING_DST) != 0
 			&& (node->dlist[j].flags & DS_INACTIVE_DST) != 0) {
 		LM_DBG("probing and inactive set, mode DS_PROBE_INACTIVE\n");
 		return 1;
 	}
 	return 0;
 }
 
996f50eb
 /**
  *
  */
d081ee4e
 void ds_ping_set(ds_set_t *node)
996f50eb
 {
 	uac_req_t uac_r;
 	int i, j;
3f9c38be
 	str ping_from;
74ef108f
 	str obproxy;
6c77c86a
 	int state;
 	ds_rctx_t rctx;
996f50eb
 
d081ee4e
 	if(!node)
f0f6269a
 		return;
 
d081ee4e
 	for(i = 0; i < 2; ++i)
 		ds_ping_set(node->next[i]);
996f50eb
 
d081ee4e
 	for(j = 0; j < node->nr; j++) {
996f50eb
 		/* skip addresses set in disabled state by admin */
d081ee4e
 		if((node->dlist[j].flags & DS_DISABLED_DST) != 0)
996f50eb
 			continue;
 		/* If the Flag of the entry has "Probing set, send a probe:	*/
ee45c2a0
 		if(ds_ping_result_helper(node, j)) {
996f50eb
 			LM_DBG("probing set #%d, URI %.*s\n", node->id,
 					node->dlist[j].uri.len, node->dlist[j].uri.s);
 
 			/* Send ping using TM-Module.
 			 * int request(str* m, str* ruri, str* to, str* from, str* h,
 			 *		str* b, str *oburi,
 			 *		transaction_cb cb, void* cbp); */
d081ee4e
 			set_uac_req(&uac_r, &ds_ping_method, 0, 0, 0, TMCB_LOCAL_COMPLETED,
 					ds_options_callback, (void *)(long)node->id);
80930085
 			if(node->dlist[j].attrs.sockname.s != NULL
 					&& node->dlist[j].attrs.sockname.len > 0) {
 				uac_r.ssockname = &node->dlist[j].attrs.sockname;
 			} else if(node->dlist[j].attrs.socket.s != NULL
d081ee4e
 					&& node->dlist[j].attrs.socket.len > 0) {
996f50eb
 				uac_r.ssock = &node->dlist[j].attrs.socket;
80930085
 			} else if(ds_default_sockname.s != NULL
 					  && ds_default_sockname.len > 0) {
 				uac_r.ssockname = &ds_default_sockname;
d081ee4e
 			} else if(ds_default_socket.s != NULL
 					  && ds_default_socket.len > 0) {
996f50eb
 				uac_r.ssock = &ds_default_socket;
 			}
12a25ed7
 
3f9c38be
 			/* Overwrite default ping From URI with attribute */
 			if(node->dlist[j].attrs.ping_from.s != NULL
 					&& node->dlist[j].attrs.ping_from.len > 0) {
 				ping_from = node->dlist[j].attrs.ping_from;
 				LM_DBG("ping_from: %.*s\n", ping_from.len, ping_from.s);
 			}
 			else {
 				ping_from = ds_ping_from;
 				LM_DBG("Default ping_from: %.*s\n", ping_from.len, ping_from.s);
 			}
 
74ef108f
 			if(node->dlist[j].attrs.obproxy.s != NULL
 					&& node->dlist[j].attrs.obproxy.len > 0) {
 				obproxy = node->dlist[j].attrs.obproxy;
 				LM_DBG("outbound proxy: %.*s\n", obproxy.len, obproxy.s);
 			}
 			else {
 				obproxy = ds_outbound_proxy;
 				LM_DBG("Default outbound proxy: %.*s\n", ds_outbound_proxy.len, ds_outbound_proxy.s);
 			}
 
12a25ed7
 			gettimeofday(&node->dlist[j].latency_stats.start, NULL);
 
d081ee4e
 			if(tmb.t_request(&uac_r, &node->dlist[j].uri, &node->dlist[j].uri,
74ef108f
 					   &ping_from, &obproxy)
d081ee4e
 					< 0) {
6c77c86a
 				LM_ERR("unable to ping [%.*s] in group [%d]\n",
 						node->dlist[j].uri.len, node->dlist[j].uri.s,
 						node->id);
 				state = DS_TRYING_DST;
 				if(ds_probing_mode != DS_PROBE_NONE) {
 					state |= DS_PROBING_DST;
 				}
 				memset(&rctx, 0, sizeof(ds_rctx_t));
 				rctx.code = 500;
 				rctx.reason.s = "Sending keepalive failed";
 				rctx.reason.len = 24;
 				/* check if meantime someone disabled the target via RPC */
 				if(!(node->dlist[j].flags & DS_DISABLED_DST)
 						&& ds_update_state(NULL, node->id, &node->dlist[j].uri,
 								state, &rctx) != 0) {
 					LM_ERR("Setting the probing state failed (%.*s, group %d)\n",
 							node->dlist[j].uri.len, node->dlist[j].uri.s,
 							node->id);
 				}
996f50eb
 			}
 		}
 	}
 }
 
aecdbe9b
 /*! \brief
cbe2e236
  * Timer for checking probing destinations
9cdc3e70
  *
081b5d4e
  * This timer is regularly fired.
  */
d081ee4e
 void ds_check_timer(unsigned int ticks, void *param)
101e9af4
 {
996f50eb
 
cbe2e236
 
081b5d4e
 	/* Check for the list. */
d081ee4e
 	if(_ds_list == NULL || _ds_list_nr <= 0) {
ec755f34
 		LM_DBG("no destination sets\n");
081b5d4e
 		return;
 	}
101e9af4
 
d081ee4e
 	if(_ds_ping_active != NULL && *_ds_ping_active == 0) {
44c5d1c0
 		LM_DBG("pinging destinations is inactive by admin\n");
 		return;
 	}
996f50eb
 
d081ee4e
 	ds_ping_set(_ds_list);
081b5d4e
 }
59c31fe7
 
9cdc3e70
 /*! \brief
  * Timer for checking expired items in call load dispatching
  *
  * This timer is regularly fired.
  */
59c31fe7
 void ds_ht_timer(unsigned int ticks, void *param)
 {
 	ds_cell_t *it;
 	ds_cell_t *it0;
 	time_t now;
 	int i;
 
d081ee4e
 	if(_dsht_load == NULL)
59c31fe7
 		return;
 
 	now = time(NULL);
cbe2e236
 
d081ee4e
 	for(i = 0; i < _dsht_load->htsize; i++) {
59c31fe7
 		/* free entries */
 		lock_get(&_dsht_load->entries[i].lock);
 		it = _dsht_load->entries[i].first;
f0f6269a
 		while(it) {
59c31fe7
 			it0 = it->next;
d081ee4e
 			if((it->expire != 0 && it->expire < now)
 					|| (it->state == DS_LOAD_INIT && it->initexpire != 0
 							   && it->initexpire < now)) {
59c31fe7
 				/* expired */
d081ee4e
 				if(it->prev == NULL)
59c31fe7
 					_dsht_load->entries[i].first = it->next;
 				else
 					it->prev->next = it->next;
 				if(it->next)
 					it->next->prev = it->prev;
 				_dsht_load->entries[i].esize--;
9cdc3e70
 
59c31fe7
 				/* execute ds unload callback */
9cdc3e70
 				ds_load_remove_byid(it->dset, &it->duid);
 
59c31fe7
 				ds_cell_free(it);
 			}
 			it = it0;
 		}
 		lock_release(&_dsht_load->entries[i].lock);
 	}
 	return;
 }
 
94385af9
 int ds_next_dst_api(sip_msg_t *msg, int mode)
 {
 	return ds_update_dst(msg, DS_USE_NEXT, mode);
 }
 
d081ee4e
 int bind_dispatcher(dispatcher_api_t *api)
86e03df9
 {
d081ee4e
 	if(!api) {
86e03df9
 		ERR("Invalid parameter value\n");
 		return -1;
 	}
d081ee4e
 	api->select = ds_select_dst;
94385af9
 	api->next = ds_next_dst_api;
d081ee4e
 	api->mark = ds_mark_dst;
86e03df9
 	api->is_from = ds_is_from_list;
 	return 0;
 }
d997ecfc
 
 
 ds_set_t *ds_get_list(void)
 {
 	return _ds_list;
 }
 
 int ds_get_list_nr(void)
 {
 	return _ds_list_nr;
 }
996f50eb
 
d081ee4e
 ds_set_t *ds_avl_find(ds_set_t *node, int id)
996f50eb
 {
f0f6269a
 	int next_step;
 
d081ee4e
 	while(node && id != node->id) {
f0f6269a
 		next_step = (id > node->id);
0369e5ab
 		node = node->next[next_step];
 	}
 	return node;
996f50eb
 }
 
 /**
  *
  */
d081ee4e
 void ds_avl_destroy(ds_set_t **node_ptr)
996f50eb
 {
d081ee4e
 	ds_set_t *node = NULL;
 	ds_dest_t *dest = NULL;
 	int i = 0;
996f50eb
 
d081ee4e
 	if(!node_ptr || !(*node_ptr))
996f50eb
 		return;
 
98f9cace
 	node = *node_ptr;
996f50eb
 
d081ee4e
 	for(i = 0; i < 2; ++i)
 		ds_avl_destroy(&node->next[i]);
996f50eb
 
d081ee4e
 	for(dest = node->dlist; dest != NULL; dest = dest->next) {
 		if(dest->uri.s != NULL) {
996f50eb
 			shm_free(dest->uri.s);
 			dest->uri.s = NULL;
 		}
614b6b05
 		if (dest->attrs.body.s != NULL) {
 			shm_free(dest->attrs.body.s);
 			dest->attrs.body.s = NULL;
 		}
996f50eb
 	}
d081ee4e
 	if(node->dlist != NULL)
996f50eb
 		shm_free(node->dlist);
 	shm_free(node);
 
 	*node_ptr = NULL;
 
 	return;
 }
 
d081ee4e
 static void avl_rebalance(ds_set_t **path_top, int target);
996f50eb
 
d081ee4e
 ds_set_t *ds_avl_insert(ds_set_t **root, int id, int *setn)
996f50eb
 {
d081ee4e
 	ds_set_t **rotation_top;
 	ds_set_t *node;
f0f6269a
 	int next_step;
98f9cace
 
 	rotation_top = root;
 	node = *root;
 
d081ee4e
 	while(node && id != node->id) {
f0f6269a
 		next_step = (id > node->id);
d081ee4e
 		if(!AVL_BALANCED(node))
 			rotation_top = root;
996f50eb
 		root = &node->next[next_step];
 		node = *root;
0369e5ab
 	}
d081ee4e
 	if(!node) {
98f9cace
 		node = shm_malloc(sizeof(ds_set_t));
 		memset(node, 0, sizeof(ds_set_t));
0369e5ab
 		node->id = id;
 		node->longer = AVL_NEITHER;
 		*root = node;
1d304002
 		lock_init(&node->lock);
d081ee4e
 		avl_rebalance(rotation_top, id);
996f50eb
 
 		(*setn)++;
0369e5ab
 	}
 	return node;
996f50eb
 }
 
d081ee4e
 static void avl_rebalance_path(ds_set_t *path, int id)
996f50eb
 {
f0f6269a
 	int next_step;
996f50eb
 	/* Each node in path is currently balanced.
0369e5ab
 	 * Until we find target, mark each node as longer
 	 * in the direction of target because we know we have
 	 * inserted target there
 	 */
d081ee4e
 	while(path && id != path->id) {
f0f6269a
 		next_step = (id > path->id);
996f50eb
 		path->longer = next_step;
 		path = path->next[next_step];
 	}
 }
 
d081ee4e
 static ds_set_t *avl_rotate_2(ds_set_t **path_top, int dir)
996f50eb
 {
 	ds_set_t *B, *C, *D, *E;
98f9cace
 
996f50eb
 	B = *path_top;
 	D = B->next[dir];
d081ee4e
 	C = D->next[1 - dir];
996f50eb
 	E = D->next[dir];
 	*path_top = D;
d081ee4e
 	D->next[1 - dir] = B;
996f50eb
 	B->next[dir] = C;
 	B->longer = AVL_NEITHER;
 	D->longer = AVL_NEITHER;
98f9cace
 
996f50eb
 	return E;
 }
 
d081ee4e
 static ds_set_t *avl_rotate_3(ds_set_t **path_top, int dir, int third)
996f50eb
 {
 	ds_set_t *B, *F, *D, *C, *E;
98f9cace
 
996f50eb
 	B = *path_top;
 	F = B->next[dir];
d081ee4e
 	D = F->next[1 - dir];
996f50eb
 	/* node: C and E can be NULL */
d081ee4e
 	C = D->next[1 - dir];
996f50eb
 	E = D->next[dir];
 	*path_top = D;
d081ee4e
 	D->next[1 - dir] = B;
996f50eb
 	D->next[dir] = F;
 	B->next[dir] = C;
d081ee4e
 	F->next[1 - dir] = E;
996f50eb
 	D->longer = AVL_NEITHER;
 
 	/* assume both trees are balanced */
 	B->longer = F->longer = AVL_NEITHER;
 
d081ee4e
 	if(third == AVL_NEITHER)
996f50eb
 		return NULL;
 
d081ee4e
 	if(third == dir) {
996f50eb
 		/* E holds the insertion so B is unbalanced */
d081ee4e
 		B->longer = 1 - dir;
996f50eb
 		return E;
 	} else {
 		/* C holds the insertion so F is unbalanced */
 		F->longer = dir;
 		return C;
 	}
 }
 
d081ee4e
 static void avl_rebalance(ds_set_t **path_top, int id)
996f50eb
 {
d081ee4e
 	ds_set_t *path;
996f50eb
 	int first, second, third;
98f9cace
 
 	path = *path_top;
 
d081ee4e
 	if(AVL_BALANCED(path)) {
0369e5ab
 		avl_rebalance_path(path, id);
 		return;
996f50eb
 	}
 	first = (id > path->id);
d081ee4e
 	if(path->longer != first) {
0369e5ab
 		/* took the shorter path */
 		path->longer = AVL_NEITHER;
 		avl_rebalance_path(path->next[first], id);
 		return;
996f50eb
 	}
 	/* took the longer path, need to rotate */
 	second = (id > path->next[first]->id);
d081ee4e
 	if(first == second) {
0369e5ab
 		/* just a two-point rotate */
 		path = avl_rotate_2(path_top, first);
 		avl_rebalance_path(path, id);
 		return;
996f50eb
 	}
 	/* fine details of the 3 point rotate depend on the third step.
 	 * However there may not be a third step, if the third point of the
 	 * rotation is the newly inserted point.  In that case we record
 	 * the third step as NEITHER
 	 */
 	path = path->next[first]->next[second];
d081ee4e
 	if(id == path->id)
 		third = AVL_NEITHER;
 	else
 		third = (id > path->id);
996f50eb
 	path = avl_rotate_3(path_top, first, third);
 	avl_rebalance_path(path, id);
 }