modules_k/dispatcher/dispatch.c
b68fbcd1
 /*
31ccf6a2
  * $Id$
  *
  * dispatcher module
b68fbcd1
  *
31ccf6a2
  * Copyright (C) 2004-2006 FhG Fokus
a4ba0359
  * Copyright (C) 2005 Voice-System.ro
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
31ccf6a2
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  * History
  * -------
  * 2004-07-31  first version, by daniel
b68fbcd1
  * 2005-04-22  added ruri  & to_uri hashing (andrei)
a4ba0359
  * 2005-12-10  added failover support via avp (daniel)
081b5d4e
  * 2006-08-15  added support for authorization username hashing (carsten)
  * 2007-01-11  Added a function to check if a specific gateway is in a
  * group (carsten)
  * 2007-01-12  Added a threshhold for automatic deactivation (carsten)
  * 2007-02-09  Added active probing of failed destinations and automatic
  * re-enabling of destinations (carsten)
  * 2007-05-08  Ported the changes to SVN-Trunk, renamed ds_is_domain to
  * ds_is_from_list and modified the function to work with IPv6 adresses.
9cdc3e70
  * 2007-07-18  removed index stuff
101e9af4
  * 			   added DB support to load/reload data(ancuta)
081630ba
  * 2007-09-17  added list-file support for reload data (carstenbock)
31ccf6a2
  */
 
aecdbe9b
 /*! \file
  * \ingroup dispatcher
  * \brief Dispatcher :: Dispatch
  */
 
31ccf6a2
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
65160302
 #include <time.h>
31ccf6a2
 
ba07eb39
 #include "../../ut.h"
31ccf6a2
 #include "../../trim.h"
 #include "../../dprint.h"
44f3472f
 #include "../../action.h"
a4ba0359
 #include "../../route.h"
 #include "../../dset.h"
 #include "../../mem/shm_mem.h"
31ccf6a2
 #include "../../parser/parse_uri.h"
 #include "../../parser/parse_from.h"
65160302
 #include "../../parser/parse_param.h"
a4ba0359
 #include "../../usr_avp.h"
c497c466
 #include "../../lib/kmi/mi.h"
081b5d4e
 #include "../../parser/digest/digest.h"
 #include "../../resolve.h"
87da8778
 #include "../../lvalue.h"
eb38b3df
 #include "../../modules/tm/tm_load.h"
c1a50c9a
 #include "../../lib/srdb1/db.h"
 #include "../../lib/srdb1/db_res.h"
e2cf6343
 #include "../../str.h"
31ccf6a2
 
59c31fe7
 #include "ds_ht.h"
86e03df9
 #include "api.h"
31ccf6a2
 #include "dispatch.h"
 
101e9af4
 #define DS_TABLE_VERSION	1
c7d56382
 #define DS_TABLE_VERSION2	2
9de77c2b
 #define DS_TABLE_VERSION3	3
65160302
 #define DS_TABLE_VERSION4	4
c7d56382
 
9cdc3e70
 #define DS_ALG_RROBIN	4
 #define DS_ALG_LOAD		10
 
c7d56382
 static int _ds_table_version = DS_TABLE_VERSION;
31ccf6a2
 
59c31fe7
 static ds_ht_t *_dsht_load = NULL;
65160302
 
31ccf6a2
 
a4ba0359
 extern int ds_force_dst;
31ccf6a2
 
101e9af4
 static db_func_t ds_dbf;
c1a50c9a
 static db1_con_t* ds_db_handle=0;
65160302
 
 ds_set_t **ds_lists=NULL;
 
cd63f545
 int *ds_list_nr = NULL;
 int *crt_idx    = NULL;
 int *next_idx   = NULL;
31ccf6a2
 
101e9af4
 #define _ds_list 	(ds_lists[*crt_idx])
 #define _ds_list_nr (*ds_list_nr)
 
 void destroy_list(int);
 
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;
 }
 
 /**
  *
  */
65160302
 int ds_print_sets(void)
9de77c2b
 {
65160302
 	ds_set_t *si = NULL;
9de77c2b
 	int i;
 
 	if(_ds_list==NULL)
 		return -1;
 	
 	/* get the index of the set */
 	si = _ds_list;
 	while(si)
 	{
 		for(i=0; i<si->nr; i++)
 		{
9cdc3e70
 			LM_DBG("dst>> %d %.*s %d %d (%.*s,%d,%d)\n", si->id,
9de77c2b
 					si->dlist[i].uri.len, si->dlist[i].uri.s,
65160302
 					si->dlist[i].flags, si->dlist[i].priority,
9cdc3e70
 					si->dlist[i].attrs.duid.len, si->dlist[i].attrs.duid.s,
 					si->dlist[i].attrs.maxload,
65160302
 					si->dlist[i].attrs.weight);
9de77c2b
 		}
 		si = si->next;
 	}
 
 	return 0;
 }
 
9cdc3e70
 /**
  *
  */
351fc577
 int init_data(void)
101e9af4
 {
 	int * p;
 
65160302
 	ds_lists = (ds_set_t**)shm_malloc(2*sizeof(ds_set_t*));
101e9af4
 	if(!ds_lists)
 	{
 		LM_ERR("Out of memory\n");
 		return -1;
 	}
 	ds_lists[0] = ds_lists[1] = 0;
 
 	
 	p = (int*)shm_malloc(3*sizeof(int));
 	if(!p)
 	{
 		LM_ERR("Out of memory\n");
 		return -1;
 	}
 
 	crt_idx = p;
 	next_idx = p+1;
 	ds_list_nr = p+2;
 	*crt_idx= *next_idx = 0;
 
 	return 0;
 }
 
9cdc3e70
 /**
  *
  */
65160302
 int ds_set_attrs(ds_dest_t *dest, str *attrs)
101e9af4
 {
65160302
 	param_t* params_list = NULL;
 	param_hooks_t phooks;
 	param_t *pit=NULL;
9cdc3e70
 	str param;
65160302
 
 	if(attrs==NULL || attrs->len<=0)
 		return 0;
 	if(attrs->s[attrs->len-1]==';')
 		attrs->len--;
9cdc3e70
 	/* clone in shm */
 	dest->attrs.body.s = (char*)shm_malloc(attrs->len+1);
 	if(dest->attrs.body.s==NULL)
 	{
 		LM_ERR("no more shm\n");
 		return -1;
 	}
 	memcpy(dest->attrs.body.s, attrs->s, attrs->len);
 	dest->attrs.body.s[attrs->len] = '\0';
 	dest->attrs.body.len = attrs->len;
 
 	param = dest->attrs.body;
 	if (parse_params(&param, CLASS_ANY, &phooks, &params_list)<0)
65160302
 		return -1;
 	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;
65160302
 		} else if(pit->name.len==6
 				&& strncasecmp(pit->name.s, "weight", 4)==0) {
 			str2sint(&pit->body, &dest->attrs.weight);
 		} else if(pit->name.len==7
 				&& strncasecmp(pit->name.s, "maxload", 7)==0) {
 			str2sint(&pit->body, &dest->attrs.maxload);
 		}
 	}
 	return 0;
 }
 
9cdc3e70
 /**
  *
  */
65160302
 int add_dest2list(int id, str uri, int flags, int priority, str *attrs,
 		int list_idx, int * setn)
 {
 	ds_dest_t *dp = NULL;
 	ds_set_t  *sp = NULL;
 	ds_dest_t *dp0 = NULL;
 	ds_dest_t *dp1 = NULL;
101e9af4
 	
 	/* For DNS-Lookups */
 	static char hn[256];
 	struct hostent* he;
 	struct sip_uri puri;
 
 	/* check uri */
 	if(parse_uri(uri.s, uri.len, &puri)!=0 || puri.host.len>254)
 	{
 		LM_ERR("bad uri [%.*s]\n", uri.len, uri.s);
 		goto err;
 	}
 	
 	/* get dest set */
 	sp = ds_lists[list_idx];
 	while(sp)
 	{
 		if(sp->id == id)
 			break;
 		sp = sp->next;
 	}
 
 	if(sp==NULL)
 	{
65160302
 		sp = (ds_set_t*)shm_malloc(sizeof(ds_set_t));
101e9af4
 		if(sp==NULL)
 		{
 			LM_ERR("no more memory.\n");
 			goto err;
 		}
 		
 		memset(sp, 0, sizeof(ds_set_t));
 		sp->next = ds_lists[list_idx];
 		ds_lists[list_idx] = sp;
 		*setn = *setn+1;
 	}
 	sp->id = id;
 	sp->nr++;
 
 	/* store uri */
65160302
 	dp = (ds_dest_t*)shm_malloc(sizeof(ds_dest_t));
101e9af4
 	if(dp==NULL)
 	{
 		LM_ERR("no more memory!\n");
 		goto err;
 	}
 	memset(dp, 0, sizeof(ds_dest_t));
 
 	dp->uri.s = (char*)shm_malloc((uri.len+1)*sizeof(char));
 	if(dp->uri.s==NULL)
 	{
 		LM_ERR("no more memory!\n");
 		goto err;
 	}
 	strncpy(dp->uri.s, uri.s, uri.len);
 	dp->uri.s[uri.len]='\0';
 	dp->uri.len = uri.len;
5affd4c7
 	dp->flags = flags;
9de77c2b
 	dp->priority = priority;
101e9af4
 
65160302
 	if(ds_set_attrs(dp, attrs)<0)
 	{
 		LM_ERR("cannot set attributes!\n");
 		goto err;
 	}
 
101e9af4
 	/* The Hostname needs to be \0 terminated for resolvehost, so we
 	 * make a copy here. */
 	strncpy(hn, puri.host.s, puri.host.len);
 	hn[puri.host.len]='\0';
 		
 	/* Do a DNS-Lookup for the Host-Name: */
bf5340e1
 	he=resolvehost(hn);
101e9af4
 	if (he==0)
 	{
 		LM_ERR("could not resolve %.*s\n", puri.host.len, puri.host.s);
 		pkg_free(hn);
 		goto err;
 	}
 	/* Free the hostname */
 	hostent2ip_addr(&dp->ip_address, he, 0);
 		
 	/* Copy the Port out of the URI: */
 	dp->port = puri.port_no;		
 
9de77c2b
 	if(sp->dlist==NULL)
 	{
 		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;
 			dp0=dp0->next;
 		}
 		if(dp1==NULL)
 		{
 			dp->next = sp->dlist;
 			sp->dlist = dp;
 		} else {
 			dp->next  = dp1->next;
 			dp1->next = dp;
 		}
 	}
101e9af4
 
 	LM_DBG("dest [%d/%d] <%.*s>\n", sp->id, sp->nr, dp->uri.len, dp->uri.s);
 	
 	return 0;
 err:
 	/* free allocated memory */
 	if(dp!=NULL)
 	{
 		if(dp->uri.s!=NULL)
 			shm_free(dp->uri.s);
 		shm_free(dp);
 	}
 	return -1;
 }
 
9cdc3e70
 /**
  *
  */
65160302
 int dp_init_weights(ds_set_t *dset)
 {
 	int j;
 	int k;
 	int t;
 
 	if(dset==NULL || dset->dlist==NULL)
 		return -1;
 
 	/* is weight set for dst list? (first address must have weight!=0) */
 	if(dset->dlist[0].attrs.weight==0)
 		return 0;
 
 	t = 0;
 	for(j=0; j<dset->nr; j++)
 	{
 		for(k=0; k<dset->dlist[j].attrs.weight; k++)
 		{
 			if(t>=100)
 				goto randomize;
 			dset->wlist[t] = (unsigned int)j;
 			t++;
 		}
 	}
 	j = (t-1>=0)?t-1:0;
 	for(; t<100; t++)
 		dset->wlist[t] = (unsigned int)j;
 randomize:
 	srand(time(0));
 	for (j=0; j<100; j++)
 	{
 		k = j + (rand() % (100-j));
 		t = (int)dset->wlist[j];
 		dset->wlist[j] = dset->wlist[k];
 		dset->wlist[k] = (unsigned int)t;
 	}
 
 	return 0;
 }
 
aecdbe9b
 /*! \brief  compact destinations from sets for fast access */
101e9af4
 int reindex_dests(int list_idx, int setn)
 {
 	int j;
65160302
 	ds_set_t  *sp = NULL;
 	ds_dest_t *dp = NULL, *dp0= NULL;
101e9af4
 
65160302
 	for(sp = ds_lists[list_idx]; sp!= NULL;	sp = sp->next)
101e9af4
 	{
65160302
 		dp0 = (ds_dest_t*)shm_malloc(sp->nr*sizeof(ds_dest_t));
101e9af4
 		if(dp0==NULL)
 		{
 			LM_ERR("no more memory!\n");
 			goto err1;
 		}
 		memset(dp0, 0, sp->nr*sizeof(ds_dest_t));
 
65160302
 		/* copy from the old pointer to destination, and then free it */
101e9af4
 		for(j=sp->nr-1; j>=0 && sp->dlist!= NULL; j--)
 		{
 			memcpy(&dp0[j], sp->dlist, sizeof(ds_dest_t));
 			if(j==sp->nr-1)
 				dp0[j].next = NULL;
 			else
 				dp0[j].next = &dp0[j+1];
65160302
 
 
101e9af4
 			dp = sp->dlist;
 			sp->dlist = dp->next;
 			
 			shm_free(dp);
 			dp=NULL;
 		}
65160302
 		sp->dlist = dp0;
 		dp_init_weights(sp);
101e9af4
 	}
 
 	LM_DBG("found [%d] dest sets\n", setn);
 	return 0;
 
 err1:
 	return -1;
 }
 
aecdbe9b
 /*! \brief load groups of destinations from file */
31ccf6a2
 int ds_load_list(char *lfile)
 {
 	char line[256], *p;
 	FILE *f = NULL;
9de77c2b
 	int id, setn, flags, priority;
31ccf6a2
 	str uri;
65160302
 	str attrs;
 
081630ba
 	if( (*crt_idx) != (*next_idx)) {
 		LM_WARN("load command already generated, aborting reload...\n");
 		return 0;
 	}
081b5d4e
 
31ccf6a2
 	if(lfile==NULL || strlen(lfile)<=0)
 	{
101e9af4
 		LM_ERR("bad list file\n");
31ccf6a2
 		return -1;
 	}
 
 	f = fopen(lfile, "r");
 	if(f==NULL)
 	{
101e9af4
 		LM_ERR("can't open list file [%s]\n", lfile);
31ccf6a2
 		return -1;
 		
 	}
 
9de77c2b
 	id = setn = flags = priority = 0;
081630ba
 
 	*next_idx = (*crt_idx + 1)%2;
 	destroy_list(*next_idx);
65160302
 
31ccf6a2
 	p = fgets(line, 256, f);
 	while(p)
 	{
 		/* eat all white spaces */
 		while(*p && (*p==' ' || *p=='\t' || *p=='\r' || *p=='\n'))
 			p++;
 		if(*p=='\0' || *p=='#')
 			goto next_line;
65160302
 
31ccf6a2
 		/* get set id */
 		id = 0;
 		while(*p>='0' && *p<='9')
 		{
 			id = id*10+ (*p-'0');
 			p++;
 		}
65160302
 
31ccf6a2
 		/* eat all white spaces */
 		while(*p && (*p==' ' || *p=='\t' || *p=='\r' || *p=='\n'))
 			p++;
 		if(*p=='\0' || *p=='#')
 		{
101e9af4
 			LM_ERR("bad line [%s]\n", line);
31ccf6a2
 			goto error;
 		}
 
 		/* get uri */
 		uri.s = p;
 		while(*p && *p!=' ' && *p!='\t' && *p!='\r' && *p!='\n' && *p!='#')
 			p++;
 		uri.len = p-uri.s;
 
5affd4c7
 		/* eat all white spaces */
 		while(*p && (*p==' ' || *p=='\t' || *p=='\r' || *p=='\n'))
 			p++;
65160302
 
4d0395a0
 		/* get flags */
 		flags = 0;
9de77c2b
 		priority = 0;
ec903da3
 		attrs.s = 0; attrs.len = 0;
5affd4c7
 		if(*p=='\0' || *p=='#')
9de77c2b
 			goto add_destination; /* no flags given */
 
 		while(*p>='0' && *p<='9')
5affd4c7
 		{
9de77c2b
 			flags = flags*10+ (*p-'0');
 			p++;
5affd4c7
 		}
65160302
 
9de77c2b
 		/* eat all white spaces */
 		while(*p && (*p==' ' || *p=='\t' || *p=='\r' || *p=='\n'))
 			p++;
65160302
 
9de77c2b
 		/* get priority */
 		if(*p=='\0' || *p=='#')
 			goto add_destination; /* no priority given */
5affd4c7
 
 		while(*p>='0' && *p<='9')
 		{
9de77c2b
 			priority = priority*10+ (*p-'0');
5affd4c7
 			p++;
 		}
65160302
 
 		/* eat all white spaces */
 		while(*p && (*p==' ' || *p=='\t' || *p=='\r' || *p=='\n'))
 			p++;
 		if(*p=='\0' || *p=='#')
 			goto add_destination; /* no priority given */
 
 		/* get attributes */
 		attrs.s = p;
 		while(*p && *p!=' ' && *p!='\t' && *p!='\r' && *p!='\n' && *p!='#')
 			p++;
 		attrs.len = p-attrs.s;
 
4d0395a0
 add_destination:
65160302
 		if(add_dest2list(id, uri, flags, priority, &attrs,
 					*next_idx, &setn) != 0)
31ccf6a2
 			goto error;
101e9af4
 					
31ccf6a2
 		
 next_line:
 		p = fgets(line, 256, f);
 	}
 		
101e9af4
 	if(reindex_dests(*next_idx, setn)!=0){
 		LM_ERR("error on reindex\n");
 		goto error;
 	}
 
31ccf6a2
 	fclose(f);
 	f = NULL;
081630ba
 	/* Update list */
101e9af4
 	_ds_list_nr = setn;
081630ba
 	*crt_idx = *next_idx;
9de77c2b
 	ds_print_sets();
101e9af4
 	return 0;
 
 error:
 	if(f!=NULL)
 		fclose(f);
 	destroy_list(*next_idx);
9cdc3e70
 	*next_idx = *crt_idx;
101e9af4
 	return -1;
 }
 
9cdc3e70
 /**
  *
  */
351fc577
 int ds_connect_db(void)
c503d21d
 {
0fa5e3ac
 	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
 {
 	if(ds_db_handle)
 	{
 		ds_dbf.close(ds_db_handle);
 		ds_db_handle = 0;
 	}
 }
 
aecdbe9b
 /*! \brief Initialize and verify DB stuff*/
351fc577
 int init_ds_db(void)
101e9af4
 {
c503d21d
 	int ret;
101e9af4
 
e2cf6343
 	if(ds_table_name.s == 0)
31ccf6a2
 	{
101e9af4
 		LM_ERR("invalid database name\n");
 		return -1;
31ccf6a2
 	}
c503d21d
 	
101e9af4
 	/* Find a database module */
e2cf6343
 	if (db_bind_mod(&ds_db_url, &ds_dbf) < 0)
31ccf6a2
 	{
101e9af4
 		LM_ERR("Unable to bind to a database driver\n");
 		return -1;
31ccf6a2
 	}
c503d21d
 	
 	if(ds_connect_db()!=0){
 		
101e9af4
 		LM_ERR("unable to connect to the database\n");
 		return -1;
 	}
c503d21d
 	
c7d56382
 	_ds_table_version = db_table_version(&ds_dbf, ds_db_handle, &ds_table_name);
9cdc3e70
 	if (_ds_table_version < 0)
101e9af4
 	{
 		LM_ERR("failed to query table version\n");
 		return -1;
c7d56382
 	} else if (_ds_table_version != DS_TABLE_VERSION
9de77c2b
 			&& _ds_table_version != DS_TABLE_VERSION2
9cdc3e70
 			&& _ds_table_version != DS_TABLE_VERSION3
 			&& _ds_table_version != DS_TABLE_VERSION4) {
 		LM_ERR("invalid table version (found %d , required %d, %d, %d or %d)\n"
bb30e4a8
 			"(use kamdbctl reinit)\n",
9de77c2b
 			_ds_table_version, DS_TABLE_VERSION, DS_TABLE_VERSION2,
9cdc3e70
 			DS_TABLE_VERSION3, DS_TABLE_VERSION4);
101e9af4
 		return -1;
 	}
 
c503d21d
 	ret = ds_load_db();
 
 	ds_disconnect_db();
 
 	return ret;
101e9af4
 }
 
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;
101e9af4
 	str uri;
65160302
 	str attrs = {0, 0};
c1a50c9a
 	db1_res_t * res;
101e9af4
 	db_val_t * values;
 	db_row_t * rows;
e2cf6343
 	
65160302
 	db_key_t query_cols[5] = {&ds_set_id_col, &ds_dest_uri_col,
 				&ds_dest_flags_col, &ds_dest_priority_col,
 				&ds_dest_attrs_col};
31ccf6a2
 	
c7d56382
 	nrcols = 2;
 	if(_ds_table_version == DS_TABLE_VERSION2)
 		nrcols = 3;
9de77c2b
 	else if(_ds_table_version == DS_TABLE_VERSION3)
 		nrcols = 4;
65160302
 	else if(_ds_table_version == DS_TABLE_VERSION4)
 		nrcols = 5;
c7d56382
 
101e9af4
 	if( (*crt_idx) != (*next_idx))
31ccf6a2
 	{
101e9af4
 		LM_WARN("load command already generated, aborting reload...\n");
 		return 0;
31ccf6a2
 	}
a4ba0359
 
c503d21d
 	if(ds_db_handle == NULL){
 			LM_ERR("invalid DB handler\n");
101e9af4
 			return -1;
c503d21d
 	}
 
e2cf6343
 	if (ds_dbf.use_table(ds_db_handle, &ds_table_name) < 0)
101e9af4
 	{
 		LM_ERR("error in use_table\n");
 		return -1;
 	}
31ccf6a2
 
101e9af4
 	/*select the whole table and all the columns*/
c7d56382
 	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);
 	rows 	= RES_ROWS(res);
 	if(nr_rows == 0)
31ccf6a2
 	{
c7d56382
 		LM_WARN("no dispatching data in the db -- empty destination set\n");
101e9af4
 		ds_dbf.free_result(ds_db_handle, res);
7599a9bd
 		return 0;
31ccf6a2
 	}
 
101e9af4
 	setn = 0;
 	*next_idx = (*crt_idx + 1)%2;
 	destroy_list(*next_idx);
 	
 	for(i=0; i<nr_rows; i++)
31ccf6a2
 	{
101e9af4
 		values = ROW_VALUES(rows+i);
 
 		id = VAL_INT(values);
 		uri.s = VAL_STR(values+1).s;
 		uri.len = strlen(uri.s);
5affd4c7
 		flags = 0;
9de77c2b
 		if(nrcols>=3)
c7d56382
 			flags = VAL_INT(values+2);
9de77c2b
 		priority=0;
 		if(nrcols>=4)
 			priority = VAL_INT(values+3);
101e9af4
 
65160302
 		attrs.s = 0; attrs.len = 0;
 		if(nrcols>=5)
 		{
 			attrs.s = VAL_STR(values+4).s;
 			attrs.len = strlen(attrs.s);
 		}
 		if(add_dest2list(id, uri, flags, priority, &attrs,
 					*next_idx, &setn) != 0)
101e9af4
 			goto err2;
 
31ccf6a2
 	}
9de77c2b
 	ds_dbf.free_result(ds_db_handle, res);
101e9af4
 
 	if(reindex_dests(*next_idx, setn)!=0)
 	{
 		LM_ERR("error on reindex\n");
 		goto err2;
 	}
 
 	/*update data*/
 	_ds_list_nr = setn;
 	*crt_idx = *next_idx;
9de77c2b
 
 	ds_print_sets();
101e9af4
 
 	return 0;
 
 err2:
 	destroy_list(*next_idx);
 	ds_dbf.free_result(ds_db_handle, res);
9cdc3e70
 	*next_idx = *crt_idx;
101e9af4
 
31ccf6a2
 	return -1;
 }
 
aecdbe9b
 /*! \brief called from dispatcher.c: free all*/
351fc577
 int ds_destroy_list(void)
101e9af4
 {
cd63f545
 	if (ds_lists) {
 		destroy_list(0);
 		destroy_list(1);
 		shm_free(ds_lists);
 	}
101e9af4
 
cd63f545
 	if (crt_idx)
 		shm_free(crt_idx);
101e9af4
 
 	return 0;
 }
e2cf6343
 
9cdc3e70
 /**
  *
  */
101e9af4
 void destroy_list(int list_id)
31ccf6a2
 {
65160302
 	ds_set_t  *sp = NULL;
 	ds_dest_t *dest = NULL;
101e9af4
 
 	sp = ds_lists[list_id];
 
31ccf6a2
 	while(sp)
 	{
101e9af4
 		for(dest = sp->dlist; dest!= NULL; dest=dest->next)
31ccf6a2
 		{
101e9af4
 			if(dest->uri.s!=NULL)
    			{
    				shm_free(dest->uri.s);
    				dest->uri.s = NULL;
 	   		}
31ccf6a2
 		}
a4ba0359
 		shm_free(sp->dlist);
31ccf6a2
 		sp = sp->next;
 	}
 	
101e9af4
 	ds_lists[list_id]  = NULL;
31ccf6a2
 }
 
 /**
  *
  */
 unsigned int ds_get_hash(str *x, str *y)
 {
 	char* p;
 	register unsigned v;
 	register unsigned h;
 
 	if(!x && !y)
 		return 0;
 	h=0;
 	if(x)
 	{
ee4a9fb2
 		p=x->s;
 		if (x->len>=4)
31ccf6a2
 		{
ee4a9fb2
 			for (; p<=(x->s+x->len-4); p+=4)
 			{
 				v=(*p<<24)+(p[1]<<16)+(p[2]<<8)+p[3];
 				h+=v^(v>>3);
 			}
31ccf6a2
 		}
 		v=0;
 		for (;p<(x->s+x->len); p++)
9cdc3e70
 		{
 			v<<=8;
31ccf6a2
 			v+=*p;
 		}
 		h+=v^(v>>3);
 	}
 	if(y)
 	{
ee4a9fb2
 		p=y->s;
9cdc3e70
 		if (y->len>=4)
31ccf6a2
 		{
ee4a9fb2
 			for (; p<=(y->s+y->len-4); p+=4)
 			{
 				v=(*p<<24)+(p[1]<<16)+(p[2]<<8)+p[3];
 				h+=v^(v>>3);
 			}
31ccf6a2
 		}
 	
 		v=0;
 		for (;p<(y->s+y->len); p++)
9cdc3e70
 		{
 			v<<=8;
31ccf6a2
 			v+=*p;
 		}
 		h+=v^(v>>3);
 	}
 	h=((h)+(h>>11))+((h>>13)+(h>>23));
 
 	return (h)?h:1;
 }
 
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
  */
 static inline int get_uri_hash_keys(str* key1, str* key2,
 							str* uri, struct sip_uri* parsed_uri, int flags)
 {
 	struct sip_uri tmp_p_uri; /* used only if parsed_uri==0 */
 	
101e9af4
 	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;
 		}
 		parsed_uri=&tmp_p_uri;
 	}
 	/* uri sanity checks */
101e9af4
 	if (parsed_uri->host.s==0)
 	{
 			LM_ERR("invalid uri, no host present: %.*s\n",
 					uri->len, uri->len?uri->s:"");
ee4a9fb2
 			goto error;
 	}
 	
 	/* we want: user@host:port if port !=5060
 	 *          user@host if port==5060
 	 *          user if the user flag is set*/
 	*key1=parsed_uri->user;
 	key2->s=0;
 	key2->len=0;
101e9af4
 	if (!(flags & DS_HASH_USER_ONLY))
 	{	/* key2=host */
ee4a9fb2
 		*key2=parsed_uri->host;
 		/* add port if needed */
101e9af4
 		if (parsed_uri->port.s!=0)
 		{ /* uri has a port */
ee4a9fb2
 			/* skip port if == 5060 or sips and == 5061 */
 			if (parsed_uri->port_no !=
 					((parsed_uri->type==SIPS_URI_T)?SIPS_PORT:SIP_PORT))
 				key2->len+=parsed_uri->port.len+1 /* ':' */;
 		}
 	}
101e9af4
 	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;
 	
31ccf6a2
 	if(msg==NULL || hash == NULL)
 	{
101e9af4
 		LM_ERR("bad parameters\n");
31ccf6a2
 		return -1;
 	}
 	
34967758
 	if(parse_from_header(msg)<0)
31ccf6a2
 	{
101e9af4
 		LM_ERR("cannot parse From hdr\n");
31ccf6a2
 		return -1;
 	}
 	
 	if(msg->from==NULL || get_from(msg)==NULL)
 	{
101e9af4
 		LM_ERR("cannot get From uri\n");
31ccf6a2
 		return -1;
 	}
b68fbcd1
 	
ee4a9fb2
 	from   = get_from(msg)->uri;
31ccf6a2
 	trim(&from);
ee4a9fb2
 	if (get_uri_hash_keys(&key1, &key2, &from, 0, ds_flags)<0)
 		return -1;
 	*hash = ds_get_hash(&key1, &key2);
31ccf6a2
 	
 	return 0;
 }
b68fbcd1
 
 
 /**
  *
  */
 int ds_hash_touri(struct sip_msg *msg, unsigned int *hash)
 {
 	str to;
ee4a9fb2
 	str key1;
 	str key2;
 	
b68fbcd1
 	if(msg==NULL || hash == NULL)
 	{
101e9af4
 		LM_ERR("bad parameters\n");
b68fbcd1
 		return -1;
 	}
 	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;
 	}
 	
31ccf6a2
 	
ee4a9fb2
 	to   = get_to(msg)->uri;
b68fbcd1
 	trim(&to);
 	
ee4a9fb2
 	if (get_uri_hash_keys(&key1, &key2, &to, 0, ds_flags)<0)
 		return -1;
 	*hash = ds_get_hash(&key1, &key2);
b68fbcd1
 	
 	return 0;
 }
 
ee4a9fb2
 
31ccf6a2
 /**
  *
  */
 int ds_hash_callid(struct sip_msg *msg, unsigned int *hash)
 {
 	str cid;
 	if(msg==NULL || hash == NULL)
 	{
101e9af4
 		LM_ERR("bad parameters\n");
31ccf6a2
 		return -1;
 	}
 	
b68fbcd1
 	if(msg->callid==NULL && ((parse_headers(msg, HDR_CALLID_F, 0)==-1) ||
31ccf6a2
 				(msg->callid==NULL)) )
 	{
101e9af4
 		LM_ERR("cannot parse Call-Id\n");
31ccf6a2
 		return -1;
 	}
b68fbcd1
 	
31ccf6a2
 	cid.s   = msg->callid->body.s;
 	cid.len = msg->callid->body.len;
 	trim(&cid);
 	
 	*hash = ds_get_hash(&cid, NULL);
 	
 	return 0;
 }
 
b68fbcd1
 
9cdc3e70
 /**
  *
  */
b68fbcd1
 int ds_hash_ruri(struct sip_msg *msg, unsigned int *hash)
 {
 	str* uri;
ee4a9fb2
 	str key1;
 	str key2;
b68fbcd1
 	
 	
 	if(msg==NULL || hash == NULL)
 	{
101e9af4
 		LM_ERR("bad parameters\n");
b68fbcd1
 		return -1;
 	}
 	if (parse_sip_msg_uri(msg)<0){
101e9af4
 		LM_ERR("bad request uri\n");
b68fbcd1
 		return -1;
 	}
 	
 	uri=GET_RURI(msg);
ee4a9fb2
 	if (get_uri_hash_keys(&key1, &key2, uri, &msg->parsed_uri, ds_flags)<0)
b68fbcd1
 		return -1;
 	
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 */
 	struct hdr_field* h = 0;
 	/* The Username */
 	str username = {0, 0};
 	/* The Credentials from this request */
 	auth_body_t* cred;
 	
 	if(msg==NULL || hash == NULL)
 	{
101e9af4
 		LM_ERR("bad parameters\n");
081b5d4e
 		return -1;
 	}
 	if (parse_headers(msg, HDR_PROXYAUTH_F, 0) == -1)
 	{
101e9af4
 		LM_ERR("error parsing headers!\n");
081b5d4e
 		return -1;
 	}
 	if (msg->proxy_auth && !msg->proxy_auth->parsed)
 		parse_credentials(msg->proxy_auth);
 	if (msg->proxy_auth && msg->proxy_auth->parsed) {
 		h = msg->proxy_auth;
 	}
 	if (!h)
 	{
 		if (parse_headers(msg, HDR_AUTHORIZATION_F, 0) == -1)
 		{
101e9af4
 			LM_ERR("error parsing headers!\n");
081b5d4e
 			return -1;
 		}
 		if (msg->authorization && !msg->authorization->parsed)
 			parse_credentials(msg->authorization);
 		if (msg->authorization && msg->authorization->parsed) {
 			h = msg->authorization;
 		}
 	}
 	if (!h)
 	{
101e9af4
 		LM_DBG("No Authorization-Header!\n");
081b5d4e
 		return 1;
 	}
 
 	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;
 	}
 	
 	username.s = cred->digest.username.user.s;
 	username.len = cred->digest.username.user.len;
 
 	trim(&username);
 	
 	*hash = ds_get_hash(&username, NULL);
 	
 	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};
 	
 	if(msg==NULL || hash == NULL || hash_param_model == NULL)
 	{
 		LM_ERR("bad parameters\n");
 		return -1;
 	}
 	if (pv_printf_s(msg, hash_param_model, &hash_str)<0) {
 		LM_ERR("error - cannot print the format\n");
 		return -1;
 	}
 
 	/* Remove empty spaces */
 	trim(&hash_str);
 	if (hash_str.len <= 0) {
 		LM_ERR("String is empty!\n");
 		return -1;
 	}
 	LM_DBG("Hashing %.*s!\n", hash_str.len, hash_str.s);
 
 	*hash = ds_get_hash(&hash_str, NULL);
 	
 	return 0;
 }
 
9cdc3e70
 /**
  *
  */
 static inline int ds_get_index(int group, ds_set_t **index)
 {
 	ds_set_t *si = NULL;
 	
 	if(index==NULL || group<0 || _ds_list==NULL)
 		return -1;
 	
 	/* get the index of the set */
 	si = _ds_list;
 	while(si)
 	{
 		if(si->id == group)
 		{
 			*index = si;
 			break;
 		}
 		si = si->next;
 	}
 
 	if(si==NULL)
 	{
 		LM_ERR("destination set [%d] not found\n", group);
 		return -1;
 	}
 
 	return 0;
 }
 
 
 /**
  *
  */
59c31fe7
 int ds_get_leastloaded(ds_set_t *dset)
 {
 	int j;
 	int k;
 	int t;
 
 	k = 0;
 	t = dset->dlist[k].dload;
 	for(j=1; j<dset->nr; j++)
 	{
 		if(!((dset->dlist[j].flags & DS_INACTIVE_DST)
 				|| (dset->dlist[j].flags & DS_PROBING_DST)))
 		{
 			if(dset->dlist[j].dload<t)
 			{
 				k = j;
 				t = dset->dlist[k].dload;
 			}
 		}
 	}
 	return k;
 }
 
9cdc3e70
 /**
  *
  */
 int ds_load_add(struct sip_msg *msg, ds_set_t *dset, int setid, int dst)
59c31fe7
 {
9cdc3e70
 	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;
 	}
 
 	if(ds_add_cell(_dsht_load, &msg->callid->body,
9cdc3e70
 			&dset->dlist[dst].attrs.duid, setid)<0)
59c31fe7
 	{
 		LM_ERR("cannot add load to %d (%.*s)\n", setid,
 				msg->callid->body.len, msg->callid->body.s);
 		return -1;
 	}
 	dset->dlist[dst].dload++;
 	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;
 
 	if(duid->len<=0)
 	{
 		LM_ERR("invalid dst unique id not set for (%.*s)\n",
 				msg->callid->body.len, msg->callid->body.s);
a4ba0359
 		return -1;
9cdc3e70
 	}
 
 	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);
 		return -1;
 	}
 	set = it->dset;
a4ba0359
 	/* get the index of the set */
9cdc3e70
 	if(ds_get_index(set, &idx)!=0)
a4ba0359
 	{
9cdc3e70
 		ds_unlock_cell(_dsht_load, &msg->callid->body);
 		LM_ERR("destination set [%d] not found\n", set);
 		return -1;
 	}
 	olddst = -1;
 	newdst = -1;
 	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)
a4ba0359
 		{
9cdc3e70
 			olddst = i;
 			if(newdst!=-1)
 				break;
 		}
 		if(idx->dlist[i].attrs.duid.len==duid->len
 				&& strncasecmp(idx->dlist[i].attrs.duid.s, duid->s,
 					duid->len)==0)
 		{
 			newdst = i;
 			if(olddst!=-1)
 				break;
 		}
 	}
 	if(olddst==-1)
 	{
 		ds_unlock_cell(_dsht_load, &msg->callid->body);
 		LM_ERR("old destination address not found for [%d, %.*s]\n", set,
 				it->duid.len, it->duid.s);
 		return -1;
 	}
 	if(newdst==-1)
 	{
 		ds_unlock_cell(_dsht_load, &msg->callid->body);
 		LM_ERR("new destination address not found for [%d, %.*s]\n", set,
 				duid->len, duid->s);
 		return -1;
 	}
 
 	ds_unlock_cell(_dsht_load, &msg->callid->body);
 	ds_del_cell(_dsht_load, &msg->callid->body);
 	idx->dlist[olddst].dload--;
 
 	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);
 		return -1;
 	}
 	return 0;
 }
 
 /**
  *
  */
 int ds_load_remove(struct sip_msg *msg)
 {
 	ds_cell_t *it;
 	int set;
 	int olddst;
 	ds_set_t *idx = NULL;
 	int i;
 
 	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);
 		return -1;
 	}
 	set = it->dset;
 	/* get the index of the set */
 	if(ds_get_index(set, &idx)!=0)
 	{
 		ds_unlock_cell(_dsht_load, &msg->callid->body);
 		LM_ERR("destination set [%d] not found\n", set);
 		return -1;
 	}
 	olddst = -1;
 	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)
 		{
 			olddst = i;
a4ba0359
 			break;
 		}
9cdc3e70
 	}
 	if(olddst==-1)
 	{
 		ds_unlock_cell(_dsht_load, &msg->callid->body);
 		LM_ERR("old destination address not found for [%d, %.*s]\n", set,
 				it->duid.len, it->duid.s);
 		return -1;
a4ba0359
 	}
 
9cdc3e70
 	ds_unlock_cell(_dsht_load, &msg->callid->body);
 	ds_del_cell(_dsht_load, &msg->callid->body);
 	idx->dlist[olddst].dload--;
 
 	return 0;
 }
 
 
 /**
  *
  */
 int ds_load_remove_byid(int set, str *duid)
 {
 	int olddst;
 	ds_set_t *idx = NULL;
 	int i;
 
 	/* get the index of the set */
 	if(ds_get_index(set, &idx)!=0)
a4ba0359
 	{
9cdc3e70
 		LM_ERR("destination set [%d] not found\n", set);
 		return -1;
 	}
 	olddst = -1;
 	for(i=0; i<idx->nr; i++)
 	{
 		if(idx->dlist[i].attrs.duid.len==duid->len
 				&& strncasecmp(idx->dlist[i].attrs.duid.s, duid->s,
 					duid->len)==0)
 		{
 			olddst = i;
 			break;
 		}
 	}
 	if(olddst==-1)
 	{
 		LM_ERR("old destination address not found for [%d, %.*s]\n", set,
 				duid->len, duid->s);
 		return -1;
 	}
 
 	idx->dlist[olddst].dload--;
 
 	return 0;
 }
 
 /**
  *
  */
 int ds_load_state(struct sip_msg *msg, int state)
 {
 	ds_cell_t *it;
 
 	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)
 {
     if(parse_headers(msg, HDR_CSEQ_F|HDR_CALLID_F, 0)!=0
 			|| msg->cseq==NULL || msg->callid==NULL)
     {
         LM_ERR("cannot parse cseq and callid headers\n");
         return -1;
     }
 	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)
 		{
 			/* off-load call */
 			ds_load_remove(msg);
 		}
 		return 0;
 	}
 
 	if(get_cseq(msg)->method_id==METHOD_INVITE)
 	{
 		/* if status is 2xx then set state to confirmed */
 		if(REPLY_CLASS(msg)==2)
 			ds_load_state(msg, DS_LOAD_CONFIRMED);
 	}
a4ba0359
 	return 0;
 }
 
9cdc3e70
 /**
  *
  */
 int ds_load_unset(struct sip_msg *msg)
 {
 	struct search_state st;
 	struct usr_avp *prev_avp;
 	int_str avp_value;
 	
 	if(dstid_avp_name.n==0)
 		return 0;
 
 	/* for INVITE requests should be called after dst list is built */
 	if(msg->first_line.type==SIP_REQUEST
 			&&  msg->first_line.u.request.method_value==METHOD_INVITE)
     {
 		prev_avp = search_first_avp(dstid_avp_type, dstid_avp_name,
 				&avp_value, &st);
 		if(prev_avp==NULL)
 			return 0;
 	}
 	return ds_load_remove(msg);
 }
 
 /**
  *
  */
a4ba0359
 static inline int ds_update_dst(struct sip_msg *msg, str *uri, int mode)
 {
 	struct action act;
dfea35f6
 	struct run_act_ctx ra_ctx;
4dbb452a
 	str *duri = NULL;
a4ba0359
 	switch(mode)
 	{
 		case 1:
dfea35f6
 			memset(&act, '\0', sizeof(act));
8381c032
 			act.type = SET_HOSTALL_T;
dfea35f6
 			act.val[0].type = STRING_ST;
9cdc3e70
 			if(uri->len>4
a4ba0359
 					&& strncasecmp(uri->s,"sip:",4)==0)
dfea35f6
 				act.val[0].u.string = uri->s+4;
a4ba0359
 			else
dfea35f6
 				act.val[0].u.string = uri->s;
 			init_run_actions_ctx(&ra_ctx);
 			if (do_action(&ra_ctx, &act, msg) < 0) {
101e9af4
 				LM_ERR("error while setting host\n");
a4ba0359
 				return -1;
 			}
 		break;
 		default:
4dbb452a
 			duri = uri;
8381c032
 			if (set_dst_uri(msg, uri) < 0) {
 				LM_ERR("error while setting dst uri\n");
 				return -1;
039b182e
 			}
 			/* dst_uri changes, so it makes sense to re-use the current uri for
 				forking */
 			ruri_mark_new(); /* re-use uri for serial forking */
a4ba0359
 		break;
 	}
 	return 0;
 }
ee4a9fb2
 
31ccf6a2
 /**
  *
  */
a4ba0359
 int ds_select_dst(struct sip_msg *msg, int set, int alg, int mode)
31ccf6a2
 {
101e9af4
 	int i, cnt;
31ccf6a2
 	unsigned int hash;
8cdef0e8
 	int_str avp_val;
65160302
 	ds_set_t *idx = NULL;
31ccf6a2
 
 	if(msg==NULL)
 	{
101e9af4
 		LM_ERR("bad parameters\n");
31ccf6a2
 		return -1;
 	}
 	
101e9af4
 	if(_ds_list==NULL || _ds_list_nr<=0)
31ccf6a2
 	{
101e9af4
 		LM_ERR("no destination sets\n");
31ccf6a2
 		return -1;
 	}
 
a4ba0359
 	if((mode==0) && (ds_force_dst==0)
a082d858
 			&& (msg->dst_uri.s!=NULL || msg->dst_uri.len>0))
31ccf6a2
 	{
101e9af4
 		LM_ERR("destination already set [%.*s]\n", msg->dst_uri.len,
 				msg->dst_uri.s);
31ccf6a2
 		return -1;
 	}
 	
 
 	/* get the index of the set */
a4ba0359
 	if(ds_get_index(set, &idx)!=0)
31ccf6a2
 	{
101e9af4
 		LM_ERR("destination set [%d] not found\n", set);
31ccf6a2
 		return -1;
 	}
a4ba0359
 	
101e9af4
 	LM_DBG("set [%d]\n", set);
31ccf6a2
 
 	hash = 0;
a4ba0359
 	switch(alg)
31ccf6a2
 	{
65160302
 		case 0: /* hash call-id */
31ccf6a2
 			if(ds_hash_callid(msg, &hash)!=0)
 			{
101e9af4
 				LM_ERR("can't get callid hash\n");
31ccf6a2
 				return -1;
 			}
 		break;
65160302
 		case 1: /* hash from-uri */
31ccf6a2
 			if(ds_hash_fromuri(msg, &hash)!=0)
 			{
101e9af4
 				LM_ERR("can't get From uri hash\n");
31ccf6a2
 				return -1;
 			}
 		break;
65160302
 		case 2: /* hash to-uri */
b68fbcd1
 			if(ds_hash_touri(msg, &hash)!=0)
 			{
101e9af4
 				LM_ERR("can't get To uri hash\n");
b68fbcd1
 				return -1;
 			}
 		break;
65160302
 		case 3: /* hash r-uri */
b68fbcd1
 			if (ds_hash_ruri(msg, &hash)!=0)
 			{
101e9af4
 				LM_ERR("can't get ruri hash\n");
b68fbcd1
 				return -1;
 			}
 		break;
9cdc3e70
 		case DS_ALG_RROBIN: /* round robin */
101e9af4
 			hash = idx->last;
 			idx->last = (idx->last+1) % idx->nr;
a082d858
 		break;
65160302
 		case 5: /* hash auth username */
081b5d4e
 			i = ds_hash_authusername(msg, &hash);
 			switch (i)
 			{
 				case 0:
 					/* Authorization-Header found: Nothing to be done here */
 				break;
 				case 1:
 					/* No Authorization found: Use round robin */
101e9af4
 					hash = idx->last;
 					idx->last = (idx->last+1) % idx->nr;
081b5d4e
 				break;
 				default:
101e9af4
 					LM_ERR("can't get authorization hash\n");
081b5d4e
 					return -1;
 				break;
 			}
 		break;
65160302
 		case 6: /* random selection */
101e9af4
 			hash = rand() % idx->nr;
 		break;
65160302
 		case 7: /* hash on PV value */
095ab21d
 			if (ds_hash_pvar(msg, &hash)!=0)
 			{
 				LM_ERR("can't get PV hash\n");
 				return -1;
 			}
 		break;		
65160302
 		case 8: /* use always first entry */
bd1f4427
 			hash = 0;
 		break;
59c31fe7
 		case 9: /* weight based distribution */
65160302
 			hash = idx->wlist[idx->wlast];
 			idx->wlast = (idx->wlast+1) % 100;
 		break;
9cdc3e70
 		case DS_ALG_LOAD: /* call load based distribution */
 			/* only INVITE can start a call */
 			if(msg->first_line.u.request.method_value!=METHOD_INVITE)
 			{
 				/* use first entry */
 				hash = 0;
 				alg = 0;
 				break;
 			}
 			if(dstid_avp_name.n==0)
 			{
 				LM_ERR("no dst ID avp for load distribution"
 						" - using first entry...\n");
 				hash = 0;
 				alg = 0;
 			} else {
 				hash = ds_get_leastloaded(idx);
 				if(ds_load_add(msg, idx, set, hash)<0)
 				{
 					LM_ERR("unable to update destination load"
 							" - classic dispatching\n");
 					alg = 0;
 				}
 			}
59c31fe7
 		break;
31ccf6a2
 		default:
101e9af4
 			LM_WARN("algo %d not implemented - using first entry...\n", alg);
31ccf6a2
 			hash = 0;
 	}
 
101e9af4
 	LM_DBG("alg hash [%u]\n", hash);
a4ba0359
 	cnt = 0;
bb857f52
 
101e9af4
 	if(ds_use_default!=0 && idx->nr!=1)
 		hash = hash%(idx->nr-1);
a4ba0359
 	else
101e9af4
 		hash = hash%idx->nr;
a4ba0359
 	i=hash;
101e9af4
 	while ((idx->dlist[i].flags & DS_INACTIVE_DST)
 			|| (idx->dlist[i].flags & DS_PROBING_DST))
44f3472f
 	{
0f5e21c6
 		if(ds_use_default!=0 && idx->nr!=1)
101e9af4
 			i = (i+1)%(idx->nr-1);
a4ba0359
 		else
101e9af4
 			i = (i+1)%idx->nr;
a4ba0359
 		if(i==hash)
 		{
0f5e21c6
 			/* back to start -- looks like no active dst */
a4ba0359
 			if(ds_use_default!=0)
 			{
101e9af4
 				i = idx->nr-1;
0f5e21c6
 				if((idx->dlist[i].flags & DS_INACTIVE_DST)
 						|| (idx->dlist[i].flags & DS_PROBING_DST))
 					return -1;
 				break;
a4ba0359
 			} else {
44f3472f
 				return -1;
 			}
a4ba0359
 		}
 	}
 
 	hash = i;
 
101e9af4
 	if(ds_update_dst(msg, &idx->dlist[hash].uri, mode)!=0)
a4ba0359
 	{
101e9af4
 		LM_ERR("cannot set dst addr\n");
a4ba0359
 		return -1;
 	}
00aa2d8a
 	/* if alg is round-robin then update the shortcut to next to be used */
9cdc3e70
 	if(alg==DS_ALG_RROBIN)
00aa2d8a
 		idx->last = (hash+1) % idx->nr;
44f3472f
 	
101e9af4
 	LM_DBG("selected [%d-%d/%d] <%.*s>\n", alg, set, hash,
 			idx->dlist[hash].uri.len, idx->dlist[hash].uri.s);
a4ba0359
 
 	if(!(ds_flags&DS_FAILOVER_ON))
 		return 1;
 
8cdef0e8
 	if(dst_avp_name.n!=0)
a4ba0359
 	{
0f5e21c6
 		/* add default dst to last position in AVP list */
101e9af4
 		if(ds_use_default!=0 && hash!=idx->nr-1)
8cdef0e8
 		{
101e9af4
 			avp_val.s = idx->dlist[idx->nr-1].uri;
8cdef0e8
 			if(add_avp(AVP_VAL_STR|dst_avp_type, dst_avp_name, avp_val)!=0)
 				return -1;
9cdc3e70
 
 			if(attrs_avp_name.n!=0 && idx->dlist[idx->nr-1].attrs.body.len>0)
 			{
 				avp_val.s = idx->dlist[idx->nr-1].attrs.body;
 				if(add_avp(AVP_VAL_STR|attrs_avp_type, attrs_avp_name,
 							avp_val)!=0)
 					return -1;
 			}
 			if(alg==DS_ALG_LOAD)
 			{
 				if(idx->dlist[idx->nr-1].attrs.duid.len<=0)
 				{
 					LM_ERR("no uid for destination: %d %.*s\n", set,
 							idx->dlist[idx->nr-1].uri.len,
 							idx->dlist[idx->nr-1].uri.s);
 					return -1;
 				}
 				avp_val.s = idx->dlist[idx->nr-1].attrs.duid;
 				if(add_avp(AVP_VAL_STR|dstid_avp_type, dstid_avp_name,
 							avp_val)!=0)
 					return -1;
 			}
8cdef0e8
 			cnt++;
 		}
a4ba0359
 	
8cdef0e8
 		/* add to avp */
 
 		for(i=hash-1; i>=0; i--)
 		{	
101e9af4
 			if((idx->dlist[i].flags & DS_INACTIVE_DST)
 					|| (ds_use_default!=0 && i==(idx->nr-1)))
8cdef0e8
 				continue;
101e9af4
 			LM_DBG("using entry [%d/%d]\n", set, i);
 			avp_val.s = idx->dlist[i].uri;
8cdef0e8
 			if(add_avp(AVP_VAL_STR|dst_avp_type, dst_avp_name, avp_val)!=0)
 				return -1;
9cdc3e70
 
 			if(attrs_avp_name.n!=0 && idx->dlist[i].attrs.body.len>0)
 			{
 				avp_val.s = idx->dlist[i].attrs.body;
 				if(add_avp(AVP_VAL_STR|attrs_avp_type, attrs_avp_name,
 							avp_val)!=0)
 					return -1;
 			}
 			if(alg==DS_ALG_LOAD)
 			{
 				if(idx->dlist[i].attrs.duid.len<=0)
 				{
 					LM_ERR("no uid for destination: %d %.*s\n", set,
 							idx->dlist[i].uri.len,
 							idx->dlist[i].uri.s);
 					return -1;
 				}
 				avp_val.s = idx->dlist[i].attrs.duid;
 				if(add_avp(AVP_VAL_STR|dstid_avp_type, dstid_avp_name,
 							avp_val)!=0)
 					return -1;
 			}
8cdef0e8
 			cnt++;
 		}
a4ba0359
 
101e9af4
 		for(i=idx->nr-1; i>hash; i--)
8cdef0e8
 		{	
101e9af4
 			if((idx->dlist[i].flags & DS_INACTIVE_DST)
 					|| (ds_use_default!=0 && i==(idx->nr-1)))
8cdef0e8
 				continue;
101e9af4
 			LM_DBG("using entry [%d/%d]\n", set, i);
 			avp_val.s = idx->dlist[i].uri;
8cdef0e8
 			if(add_avp(AVP_VAL_STR|dst_avp_type, dst_avp_name, avp_val)!=0)
 				return -1;
9cdc3e70
 
 			if(attrs_avp_name.n!=0 && idx->dlist[i].attrs.body.len>0)
 			{
 				avp_val.s = idx->dlist[i].attrs.body;
 				if(add_avp(AVP_VAL_STR|attrs_avp_type, attrs_avp_name,
 							avp_val)!=0)
 					return -1;
 			}
 			if(alg==DS_ALG_LOAD)
 			{
 				if(idx->dlist[i].attrs.duid.len<=0)
 				{
 					LM_ERR("no uid for destination: %d %.*s\n", set,
 							idx->dlist[i].uri.len,
 							idx->dlist[i].uri.s);
 					return -1;
 				}
 				avp_val.s = idx->dlist[i].attrs.duid;
 				if(add_avp(AVP_VAL_STR|dstid_avp_type, dstid_avp_name,
 							avp_val)!=0)
 					return -1;
 			}
8cdef0e8
 			cnt++;
 		}
 	
 		/* add to avp the first used dst */
101e9af4
 		avp_val.s = idx->dlist[hash].uri;
8cdef0e8
 		if(add_avp(AVP_VAL_STR|dst_avp_type, dst_avp_name, avp_val)!=0)
a4ba0359
 			return -1;
9cdc3e70
 
 		if(attrs_avp_name.n!=0 && idx->dlist[hash].attrs.body.len>0)
 		{
 			avp_val.s = idx->dlist[hash].attrs.body;
 			if(add_avp(AVP_VAL_STR|attrs_avp_type, attrs_avp_name,
 						avp_val)!=0)
 				return -1;
 		}
 		if(alg==DS_ALG_LOAD)
 		{
 			if(idx->dlist[hash].attrs.duid.len<=0)
 			{
 				LM_ERR("no uid for destination: %d %.*s\n", set,
 							idx->dlist[hash].uri.len,
 							idx->dlist[hash].uri.s);
 				return -1;
 			}
 			avp_val.s = idx->dlist[hash].attrs.duid;
 			if(add_avp(AVP_VAL_STR|dstid_avp_type, dstid_avp_name,
 						avp_val)!=0)
 				return -1;
 		}
a4ba0359
 		cnt++;
 	}
 
8cdef0e8
 	if(grp_avp_name.n!=0)
 	{
 		/* add to avp the group id */
 		avp_val.n = set;
 		if(add_avp(grp_avp_type, grp_avp_name, avp_val)!=0)
a4ba0359
 			return -1;
 	}
 
8cdef0e8
 	if(cnt_avp_name.n!=0)
 	{
 		/* add to avp the number of dst */
 		avp_val.n = cnt;
 		if(add_avp(cnt_avp_type, cnt_avp_name, avp_val)!=0)
 			return -1;
 	}
a4ba0359
 	
 	return 1;
 }
 
 int ds_next_dst(struct sip_msg *msg, int mode)
 {
76830266
 	struct search_state st;
a4ba0359
 	struct usr_avp *avp;
 	struct usr_avp *prev_avp;
 	int_str avp_value;
9cdc3e70
 	int alg = 0;
a4ba0359
 	
8cdef0e8
 	if(!(ds_flags&DS_FAILOVER_ON) || dst_avp_name.n==0)
a4ba0359
 	{
101e9af4
 		LM_WARN("failover support disabled\n");
a4ba0359
 		return -1;
31ccf6a2
 	}
 
9cdc3e70
 	if(dstid_avp_name.n!=0)
 	{
 		prev_avp = search_first_avp(dstid_avp_type, dstid_avp_name,
 				&avp_value, &st);
 		if(prev_avp!=NULL)
 		{
 			/* load based dispatching */
 			alg = DS_ALG_LOAD;
 			/* off-load destination id */
 			destroy_avp(prev_avp);
 		}
 	}
 
 	if(attrs_avp_name.n!=0)
 	{
 		prev_avp = search_first_avp(attrs_avp_type,
 					attrs_avp_name, &avp_value, &st);
 		if(prev_avp!=NULL)
 		{
 			destroy_avp(prev_avp);
 		}
 	}
a4ba0359
 
76830266
 	prev_avp = search_first_avp(dst_avp_type, dst_avp_name, &avp_value, &st);
a4ba0359
 	if(prev_avp==NULL)
 		return -1; /* used avp deleted -- strange */
 
76830266
 	avp = search_next_avp(&st, &avp_value);
a4ba0359
 	destroy_avp(prev_avp);
 	if(avp==NULL || !(avp->flags&AVP_VAL_STR))
 		return -1; /* no more avps or value is int */
 	
2e017606
 	if(ds_update_dst(msg, &avp_value.s, mode)!=0)
a4ba0359
 	{
101e9af4
 		LM_ERR("cannot set dst addr\n");
a4ba0359
 		return -1;
 	}
101e9af4
 	LM_DBG("using [%.*s]\n", avp_value.s.len, avp_value.s.s);
9cdc3e70
 	if(alg==DS_ALG_LOAD)
 	{
 		prev_avp = search_first_avp(dstid_avp_type, dstid_avp_name,
 				&avp_value, &st);
 		if(prev_avp!=NULL)
 		{
 			LM_ERR("cannot uid for dst addr\n");
 			return -1;
 		}
 		if(ds_load_replace(msg, &avp_value.s)<0)
 		{
 			LM_ERR("cannot update load distribution\n");
 			return -1;
 		}
 	}
a4ba0359
 	
31ccf6a2
 	return 1;
 }
 
a4ba0359
 int ds_mark_dst(struct sip_msg *msg, int mode)
 {
 	int group, ret;
 	struct usr_avp *prev_avp;
 	int_str avp_value;
 	
 	if(!(ds_flags&DS_FAILOVER_ON))
 	{
101e9af4
 		LM_WARN("failover support disabled\n");
a4ba0359
 		return -1;
 	}
 
8cdef0e8
 	prev_avp = search_first_avp(grp_avp_type, grp_avp_name, &avp_value, 0);
a4ba0359
 	
 	if(prev_avp==NULL || prev_avp->flags&AVP_VAL_STR)
 		return -1; /* grp avp deleted -- strange */
 	group = avp_value.n;
 	
8cdef0e8
 	prev_avp = search_first_avp(dst_avp_type, dst_avp_name, &avp_value, 0);
a4ba0359
 	
 	if(prev_avp==NULL || !(prev_avp->flags&AVP_VAL_STR))
 		return -1; /* dst avp deleted -- strange */
 	
081b5d4e
 	if(mode==1) {
9cdc3e70
 		ret = ds_set_state(group, &avp_value.s,
e68230dd
 				DS_INACTIVE_DST|DS_PROBING_DST|DS_RESET_FAIL_DST, 0);
081b5d4e
 	} else if(mode==2) {
 		ret = ds_set_state(group, &avp_value.s, DS_PROBING_DST, 1);
 		if (ret == 0) ret = ds_set_state(group, &avp_value.s,
 				DS_INACTIVE_DST, 0);
 	} else {
2e017606
 		ret = ds_set_state(group, &avp_value.s, DS_INACTIVE_DST, 1);
081b5d4e
 		if (ret == 0) ret = ds_set_state(group, &avp_value.s,
 				DS_PROBING_DST, 0);
 	}
 	
101e9af4
 	LM_DBG("mode [%d] grp [%d] dst [%.*s]\n", mode, group, avp_value.s.len,
 			avp_value.s.s);
a4ba0359
 	
 	return (ret==0)?1:-1;
 }
 
241a05bc
 /**
  *
  */
a4ba0359
 int ds_set_state(int group, str *address, int state, int type)
 {
101e9af4
 	int i=0;
65160302
 	ds_set_t *idx = NULL;
a4ba0359
 
101e9af4
 	if(_ds_list==NULL || _ds_list_nr<=0)
a4ba0359
 	{
101e9af4
 		LM_ERR("the list is null\n");
a4ba0359
 		return -1;
 	}
 	
 	/* get the index of the set */
 	if(ds_get_index(group, &idx)!=0)
 	{
101e9af4
 		LM_ERR("destination set [%d] not found\n", group);
a4ba0359
 		return -1;
 	}
 
101e9af4
 	while(i<idx->nr)
a4ba0359
 	{
9cdc3e70
 		if(idx->dlist[i].uri.len==address->len
101e9af4
 				&& strncasecmp(idx->dlist[i].uri.s, address->s,
a4ba0359
 					address->len)==0)
 		{
081b5d4e
 			/* remove the Probing/Inactive-State? Set the fail-count to 0. */
 			if (state == DS_PROBING_DST) {
 				if (type) {
ad4e490c
 					if (idx->dlist[i].flags & DS_INACTIVE_DST) {
 						LM_INFO("Ignoring the request to set this destination"
 								" to probing: It is already inactive!\n");
 						return 0;
 					}
 					
101e9af4
 					idx->dlist[i].failure_count++;
081b5d4e
 					/* Fire only, if the Threshold is reached. */
9cdc3e70
 					if (idx->dlist[i].failure_count
081b5d4e
 							< probing_threshhold) return 0;
101e9af4
 					if (idx->dlist[i].failure_count
9cdc3e70
 							> probing_threshhold)
101e9af4
 						idx->dlist[i].failure_count
081b5d4e
 							= probing_threshhold;				
 				}
 			}
 			/* Reset the Failure-Counter */
 			if ((state & DS_RESET_FAIL_DST) > 0) {
101e9af4
 				idx->dlist[i].failure_count = 0;
081b5d4e
 				state &= ~DS_RESET_FAIL_DST;
 			}
e8cd119a
 	
 			/*  Type 2 means reply from OPTIONS-Ping */
 			if (type == 2) {
 				if (idx->dlist[i].flags & DS_INACTIVE_DST) {
 					LM_INFO("Ignoring the request to set this destination"
 							" to active: It is already administratively deactivated!\n");
 					return 0;
 				}
5bb9a711
 				type = 0;
e8cd119a
 			}
081b5d4e
 			
a4ba0359
 			if(type)
101e9af4
 				idx->dlist[i].flags |= state;
a4ba0359
 			else
101e9af4
 				idx->dlist[i].flags &= ~state;
a4ba0359
 				
 			return 0;
 		}
 		i++;
 	}
 
 	return -1;
 }
 
241a05bc
 /**
  *
  */
 int ds_reinit_state(int group, str *address, 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 */
 	if(ds_get_index(group, &idx)!=0)
 	{
 		LM_ERR("destination set [%d] not found\n", group);
 		return -1;
 	}
 
 	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)
 		{
 			idx->dlist[i].flags |= state;
 			return 0;
 		}
 	}
 	LM_ERR("destination address [%d : %.*s] not found\n", group,
 			address->len, address->s);
 	return -1;
 }
 /**
  *
  */
a4ba0359
 int ds_print_list(FILE *fout)
 {
101e9af4
 	int j;
65160302
 	ds_set_t *list;
101e9af4
 		
a4ba0359
 	if(_ds_list==NULL || _ds_list_nr<=0)
 	{
101e9af4
 		LM_ERR("no destination sets\n");
a4ba0359
 		return -1;
 	}
 	
 	fprintf(fout, "\nnumber of destination sets: %d\n", _ds_list_nr);
101e9af4
 	
 	for(list = _ds_list; list!= NULL; list= list->next)
a4ba0359
 	{
101e9af4
 		for(j=0; j<list->nr; j++)
a4ba0359
 		{
101e9af4
 			fprintf(fout, "\n set #%d\n", list->id);
 		
 			if (list->dlist[j].flags&DS_INACTIVE_DST)
   				fprintf(fout, "    Disabled         ");
   			else if (list->dlist[j].flags&DS_PROBING_DST)
   				fprintf(fout, "    Probing          ");
   			else {
   				fprintf(fout, "    Active");
   				/* Optional: Print the tries for this host. */
   				if (list->dlist[j].failure_count > 0) {
   					fprintf(fout, " (Fail %d/%d)",
   							list->dlist[j].failure_count,
  							probing_threshhold);
   				} else {
   					fprintf(fout, "           ");
   				}
   			}
9cdc3e70
 
101e9af4
   			fprintf(fout, "   %.*s\n",
   				list->dlist[j].uri.len, list->dlist[j].uri.s);		
a4ba0359
 		}
 	}
 	return 0;
 }
 
ba07eb39
 
081b5d4e
 /* Checks, if the request (sip_msg *_m) comes from a host in a group
  * (group-id or -1 for all groups)
  */
b14bb7d7
 int ds_is_from_list(struct sip_msg *_m, int group)
 {
 	pv_value_t val;
65160302
 	ds_set_t *list;
101e9af4
 	int j;
 
b14bb7d7
 	memset(&val, 0, sizeof(pv_value_t));
 	val.flags = PV_VAL_INT|PV_TYPE_INT;
 
101e9af4
 	for(list = _ds_list; list!= NULL; list= list->next)
 	{
b14bb7d7
 		// LM_ERR("list id: %d (n: %d)\n", list->id, list->nr);
 		if ((group == -1) || (group == list->id))
101e9af4
 		{
 			for(j=0; j<list->nr; j++)
 			{
b14bb7d7
 				// LM_ERR("port no: %d (%d)\n", list->dlist[j].port, j);
 				if (ip_addr_cmp(&_m->rcv.src_ip, &list->dlist[j].ip_address)
 						&& (list->dlist[j].port==0
 						|| _m->rcv.src_port == list->dlist[j].port))
 				{
 					if(group==-1 && ds_setid_pvname.s!=0)
 					{
 						val.ri = list->id;
 						if(ds_setid_pv.setf(_m, &ds_setid_pv.pvp,
 								(int)EQ_T, &val)<0)
 						{
 							LM_ERR("setting PV failed\n");
 							return -2;
 						}
 					}
 					return 1;
 				}
 			}
081b5d4e
 		}
 	}
 	return -1;
 }
 
b14bb7d7
 
ba07eb39
 int ds_print_mi_list(struct mi_node* rpl)
 {
101e9af4
 	int len, j;
ba07eb39
 	char* p;
5319d08d
 	char c[3];
9cdc3e70
 	str data;
65160302
 	ds_set_t *list;
ba07eb39
 	struct mi_node* node = NULL;
 	struct mi_node* set_node = NULL;
 	struct mi_attr* attr = NULL;
101e9af4
 	
ba07eb39
 	if(_ds_list==NULL || _ds_list_nr<=0)
 	{
101e9af4
 		LM_ERR("no destination sets\n");
ba07eb39
 		return  0;
 	}
 
9cdc3e70
 	p= int2str(_ds_list_nr, &len);
101e9af4
 	node = add_mi_node_child(rpl, MI_DUP_VALUE, "SET_NO",6, p, len);
ba07eb39
 	if(node== NULL)
 		return -1;
 
101e9af4
 	for(list = _ds_list; list!= NULL; list= list->next)
ba07eb39
 	{
101e9af4
 		p = int2str(list->id, &len);
ba07eb39
 		set_node= add_mi_node_child(rpl, MI_DUP_VALUE,"SET", 3, p, len);
 		if(set_node == NULL)
 			return -1;
 
101e9af4
 		for(j=0; j<list->nr; j++)
   		{
   			node= add_mi_node_child(set_node, 0, "URI", 3,
   					list->dlist[j].uri.s, list->dlist[j].uri.len);
   			if(node == NULL)
   				return -1;
9cdc3e70
 
5319d08d
 			memset(&c, 0, sizeof(c));
 			if (list->dlist[j].flags & DS_INACTIVE_DST)
 				c[0] = 'I';
 			else if (list->dlist[j].flags & DS_DISABLED_DST)
 				c[0] = 'D';
 			else
 				c[0] = 'A';
9cdc3e70
 
5319d08d
 			if (list->dlist[j].flags & DS_PROBING_DST)
 				c[1] = 'P';
 			else
 				c[1] = 'X';
 
 			attr = add_mi_attr (node, MI_DUP_VALUE, "flags", 5, c, 2);
 			if(attr == 0)
101e9af4
   				return -1;
9cdc3e70
 
 			data.s = int2str(list->dlist[j].priority, &data.len);
5319d08d
 			attr = add_mi_attr (node, MI_DUP_VALUE, "priority", 8,
9cdc3e70
 					data.s, data.len);
   			if(attr == 0)
   				return -1;
    			attr = add_mi_attr (node, MI_DUP_VALUE, "attrs", 5,
 				(list->dlist[j].attrs.body.s)?list->dlist[j].attrs.body.s:"",
 				list->dlist[j].attrs.body.len);
   			if(attr == 0)
   				return -1;
 		}
ba07eb39
 	}
 
 	return 0;
 }
081b5d4e
 
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
  */
101e9af4
 static void ds_options_callback( struct cell *t, int type,
 		struct tmcb_params *ps )
081b5d4e
 {
 	int group = 0;
 	str uri = {0, 0};
 	/* The Param does contain the group, in which the failed host
 	 * can be found.*/
101e9af4
 	if (!*ps->param)
 	{
 		LM_DBG("No parameter provided, OPTIONS-Request was finished"
081b5d4e
 				" with code %d\n", ps->code);
 		return;
 	}
 	/* 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.
 	 * Remove the "To: " (s+4) and the trailing new-line (s - 4 (To: )
 	 * - 2 (\r\n)). */
 	uri.s = t->to.s + 4;
 	uri.len = t->to.len - 6;
101e9af4
 	LM_DBG("OPTIONS-Request was finished with code %d (to %.*s, group %d)\n",
081b5d4e
 			ps->code, uri.len, uri.s, group);
 	/* 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 */
 	if ((ps->code == 200) || ds_ping_check_rplcode(ps->code))
101e9af4
 	{
081b5d4e
 		/* Set the according entry back to "Active":
 		 *  remove the Probing/Inactive Flag and reset the failure counter. */
 		if (ds_set_state(group, &uri,
e8cd119a
 					DS_INACTIVE_DST|DS_PROBING_DST|DS_RESET_FAIL_DST, 2) != 0)
101e9af4
 		{
 			LM_ERR("Setting the state failed (%.*s, group %d)\n", uri.len,
 					uri.s, group);
081b5d4e
 		}
 	}
5be6fa20
 	if(ds_probing_mode==1 && ps->code == 408)
d1de0cc5
 	{
 		if (ds_set_state(group, &uri, DS_PROBING_DST, 1) != 0)
 		{
 			LM_ERR("Setting the probing state failed (%.*s, group %d)\n",
 					uri.len, uri.s, group);
 		}
 	}
081b5d4e
 
 	return;
 }
 
aecdbe9b
 /*! \brief
081b5d4e
  * Timer for checking inactive destinations
9cdc3e70
  *
081b5d4e
  * This timer is regularly fired.
  */
101e9af4
 void ds_check_timer(unsigned int ticks, void* param)
 {
 	int j;
65160302
 	ds_set_t *list;
4ac3f941
 	uac_req_t uac_r;
081b5d4e
 	
 	/* Check for the list. */
 	if(_ds_list==NULL || _ds_list_nr<=0)
 	{
101e9af4
 		LM_ERR("no destination sets\n");
081b5d4e
 		return;
 	}
101e9af4
 
081b5d4e
 	/* Iterate over the groups and the entries of each group: */
101e9af4
 	for(list = _ds_list; list!= NULL; list= list->next)
 	{
9cdc3e70
 		for(j=0; j<list->nr; j++)
101e9af4
 		{
241a05bc
 			/* skip addresses set in disabled state by admin */
 			if((list->dlist[j].flags&DS_DISABLED_DST) != 0)
 				continue;
081b5d4e
 			/* If the Flag of the entry has "Probing set, send a probe:	*/
d1de0cc5
 			if (ds_probing_mode==1 ||
 					(list->dlist[j].flags&DS_PROBING_DST) != 0)
101e9af4
 			{
 				LM_DBG("probing set #%d, URI %.*s\n", list->id,
 						list->dlist[j].uri.len, list->dlist[j].uri.s);
081b5d4e
 				
8381c032
 				/* 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); */
69fdda75
 				set_uac_req(&uac_r, &ds_ping_method, 0, 0, 0,
 						TMCB_LOCAL_COMPLETED, ds_options_callback,
4ac3f941
 							(void*)(long)list->id);
 				if (tmb.t_request(&uac_r,
8381c032
 							&list->dlist[j].uri,
101e9af4
 							&list->dlist[j].uri,
 							&ds_ping_from,
4ac3f941
 							0) < 0) {
8381c032
 					LM_ERR("unable to ping [%.*s]\n",
 							list->dlist[j].uri.len, list->dlist[j].uri.s);
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;
 
 	if(_dsht_load==NULL)
 		return;
 
 	now = time(NULL);
 	
 	for(i=0; i<_dsht_load->htsize; i++)
 	{
 		/* free entries */
 		lock_get(&_dsht_load->entries[i].lock);
 		it = _dsht_load->entries[i].first;
 		while(it)
 		{
 			it0 = it->next;
9cdc3e70
 			if((it->expire!=0 && it->expire<now)
 				|| (it->state==DS_LOAD_INIT
 						&& it->initexpire!=0 && it->initexpire<now))
59c31fe7
 			{
 				/* expired */
 				if(it->prev==NULL)
 					_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;
 }
 
86e03df9
 int bind_dispatcher(dispatcher_api_t* api)
 {
 	if (!api) {
 		ERR("Invalid parameter value\n");
 		return -1;
 	}
 	api->select  = ds_select_dst;
 	api->next    = ds_next_dst;
 	api->mark    = ds_mark_dst;
 	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;
 }