mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2973 lines
62 KiB
2973 lines
62 KiB
/*
|
|
* dispatcher module
|
|
*
|
|
* Copyright (C) 2004-2006 FhG Fokus
|
|
* Copyright (C) 2005 Voice-System.ro
|
|
* Copyright (C) 2015 Daniel-Constantin Mierla (asipto.com)
|
|
*
|
|
* This file is part of Kamailio, a free SIP server.
|
|
*
|
|
* Kamailio is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version
|
|
*
|
|
* Kamailio is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
/*! \file
|
|
* \ingroup dispatcher
|
|
* \brief Dispatcher :: Dispatch
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <time.h>
|
|
|
|
#include "../../ut.h"
|
|
#include "../../trim.h"
|
|
#include "../../dprint.h"
|
|
#include "../../action.h"
|
|
#include "../../route.h"
|
|
#include "../../dset.h"
|
|
#include "../../mem/shm_mem.h"
|
|
#include "../../parser/parse_uri.h"
|
|
#include "../../parser/parse_from.h"
|
|
#include "../../parser/parse_param.h"
|
|
#include "../../usr_avp.h"
|
|
#include "../../lib/kmi/mi.h"
|
|
#include "../../parser/digest/digest.h"
|
|
#include "../../resolve.h"
|
|
#include "../../lvalue.h"
|
|
#include "../../modules/tm/tm_load.h"
|
|
#include "../../lib/srdb1/db.h"
|
|
#include "../../lib/srdb1/db_res.h"
|
|
#include "../../str.h"
|
|
#include "../../script_cb.h"
|
|
#include "../../lib/kcore/faked_msg.h"
|
|
|
|
#include "ds_ht.h"
|
|
#include "api.h"
|
|
#include "dispatch.h"
|
|
|
|
#define DS_TABLE_VERSION 1
|
|
#define DS_TABLE_VERSION2 2
|
|
#define DS_TABLE_VERSION3 3
|
|
#define DS_TABLE_VERSION4 4
|
|
|
|
#define DS_ALG_RROBIN 4
|
|
#define DS_ALG_LOAD 10
|
|
|
|
static int _ds_table_version = DS_TABLE_VERSION;
|
|
|
|
static ds_ht_t *_dsht_load = NULL;
|
|
|
|
static int *_ds_ping_active = NULL;
|
|
|
|
extern int ds_force_dst;
|
|
|
|
static db_func_t ds_dbf;
|
|
static db1_con_t* ds_db_handle=NULL;
|
|
|
|
ds_set_t **ds_lists=NULL;
|
|
|
|
int *ds_list_nr = NULL;
|
|
int *crt_idx = NULL;
|
|
int *next_idx = NULL;
|
|
|
|
#define _ds_list (ds_lists[*crt_idx])
|
|
#define _ds_list_nr (*ds_list_nr)
|
|
|
|
static void ds_run_route(struct sip_msg *msg, str *uri, char *route);
|
|
|
|
void destroy_list(int);
|
|
void shuffle_uint100array(unsigned int* arr);
|
|
int ds_reinit_rweight_on_state_change(int old_state, int new_state, ds_set_t *dset);
|
|
|
|
/**
|
|
*
|
|
*/
|
|
int ds_ping_active_init(void)
|
|
{
|
|
if(_ds_ping_active!=NULL)
|
|
return 0;
|
|
_ds_ping_active = (int*)shm_malloc(sizeof(int));
|
|
if(_ds_ping_active==NULL) {
|
|
LM_ERR("no more shared memory\n");
|
|
return -1;
|
|
}
|
|
*_ds_ping_active = 1;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
int ds_ping_active_get(void)
|
|
{
|
|
if(_ds_ping_active==NULL)
|
|
return -1;
|
|
return *_ds_ping_active;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
int ds_ping_active_set(int v)
|
|
{
|
|
if(_ds_ping_active==NULL)
|
|
return -1;
|
|
*_ds_ping_active = v;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
int ds_print_sets(void)
|
|
{
|
|
ds_set_t *si = NULL;
|
|
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++)
|
|
{
|
|
LM_DBG("dst>> %d %.*s %d %d (%.*s,%d,%d,%d)\n", si->id,
|
|
si->dlist[i].uri.len, si->dlist[i].uri.s,
|
|
si->dlist[i].flags, si->dlist[i].priority,
|
|
si->dlist[i].attrs.duid.len, si->dlist[i].attrs.duid.s,
|
|
si->dlist[i].attrs.maxload,
|
|
si->dlist[i].attrs.weight,
|
|
si->dlist[i].attrs.rweight);
|
|
}
|
|
si = si->next;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
int init_data(void)
|
|
{
|
|
int * p;
|
|
|
|
ds_lists = (ds_set_t**)shm_malloc(2*sizeof(ds_set_t*));
|
|
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;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
int ds_set_attrs(ds_dest_t *dest, str *attrs)
|
|
{
|
|
param_t* params_list = NULL;
|
|
param_hooks_t phooks;
|
|
param_t *pit=NULL;
|
|
str param;
|
|
int tmp_rweight = 0;
|
|
|
|
if(attrs==NULL || attrs->len<=0)
|
|
return 0;
|
|
if(attrs->s[attrs->len-1]==';')
|
|
attrs->len--;
|
|
/* 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(¶m, CLASS_ANY, &phooks, ¶ms_list)<0)
|
|
return -1;
|
|
for (pit = params_list; pit; pit=pit->next)
|
|
{
|
|
if (pit->name.len==4
|
|
&& strncasecmp(pit->name.s, "duid", 4)==0) {
|
|
dest->attrs.duid = pit->body;
|
|
} else if(pit->name.len==6
|
|
&& strncasecmp(pit->name.s, "weight", 6)==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);
|
|
} else if(pit->name.len==6
|
|
&& strncasecmp(pit->name.s, "socket", 6)==0) {
|
|
dest->attrs.socket = pit->body;
|
|
}else if(pit->name.len==7
|
|
&& strncasecmp(pit->name.s, "rweight", 7)==0) {
|
|
tmp_rweight = 0;
|
|
str2sint(&pit->body, &tmp_rweight);
|
|
if ( tmp_rweight>=1 && tmp_rweight<=100 ) {
|
|
dest->attrs.rweight = tmp_rweight;
|
|
}
|
|
else{
|
|
LM_ERR("rweight %d not in 1-100 range; skipped", tmp_rweight);
|
|
}
|
|
}
|
|
|
|
}
|
|
if(params_list) free_params(params_list);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
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;
|
|
|
|
/* For DNS-Lookups */
|
|
static char hn[256];
|
|
struct hostent* he;
|
|
struct sip_uri puri;
|
|
int orig_id = 0, orig_nr = 0;
|
|
str host;
|
|
int port, proto;
|
|
char c = 0;
|
|
ds_set_t *orig_ds_lists = ds_lists[list_idx];
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* skip IPv6 references if IPv6 lookups are disabled */
|
|
if (default_core_cfg.dns_try_ipv6 == 0 &&
|
|
puri.host.s[0] == '[' && puri.host.s[puri.host.len-1] == ']') {
|
|
LM_DBG("skipping IPv6 record %.*s\n", puri.host.len, puri.host.s);
|
|
return 0;
|
|
}
|
|
|
|
/* get dest set */
|
|
sp = ds_lists[list_idx];
|
|
while(sp)
|
|
{
|
|
if(sp->id == id)
|
|
break;
|
|
sp = sp->next;
|
|
}
|
|
|
|
if(sp==NULL)
|
|
{
|
|
sp = (ds_set_t*)shm_malloc(sizeof(ds_set_t));
|
|
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;
|
|
}
|
|
orig_id = sp->id;
|
|
orig_nr = sp->nr;
|
|
sp->id = id;
|
|
sp->nr++;
|
|
|
|
/* store uri */
|
|
dp = (ds_dest_t*)shm_malloc(sizeof(ds_dest_t));
|
|
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;
|
|
|
|
dp->flags = flags;
|
|
dp->priority = priority;
|
|
|
|
if(ds_set_attrs(dp, attrs)<0)
|
|
{
|
|
LM_ERR("cannot set attributes!\n");
|
|
goto err;
|
|
}
|
|
|
|
/* check socket attribute */
|
|
if (dp->attrs.socket.s && dp->attrs.socket.len > 0) {
|
|
/* parse_phostport(...) expects 0-terminated string
|
|
* - after socket parameter is either ';' or '\0' */
|
|
if(dp->attrs.socket.s[dp->attrs.socket.len]!='\0') {
|
|
c = dp->attrs.socket.s[dp->attrs.socket.len];
|
|
dp->attrs.socket.s[dp->attrs.socket.len] = '\0';
|
|
}
|
|
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);
|
|
if(c!=0) {
|
|
dp->attrs.socket.s[dp->attrs.socket.len] = c;
|
|
}
|
|
goto err;
|
|
}
|
|
if(c!=0) {
|
|
dp->attrs.socket.s[dp->attrs.socket.len] = c;
|
|
}
|
|
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);
|
|
goto err;
|
|
}
|
|
} else if (ds_default_sockinfo) {
|
|
dp->sock = ds_default_sockinfo;
|
|
}
|
|
|
|
/* 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: */
|
|
he=resolvehost(hn);
|
|
if (he==0)
|
|
{
|
|
LM_ERR("could not resolve %.*s\n", puri.host.len, puri.host.s);
|
|
goto err;
|
|
}
|
|
/* Free the hostname */
|
|
hostent2ip_addr(&dp->ip_address, he, 0);
|
|
|
|
/* Copy the port out of the URI */
|
|
dp->port = puri.port_no;
|
|
/* Copy the proto out of the URI */
|
|
dp->proto = puri.proto;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
if (sp != NULL)
|
|
{
|
|
sp->id = orig_id;
|
|
sp->nr = orig_nr;
|
|
if (sp->nr == 0)
|
|
{
|
|
shm_free(sp);
|
|
ds_lists[list_idx] = orig_ds_lists;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
/* for internal usage; arr must be arr[100] */
|
|
void shuffle_uint100array(unsigned int* arr){
|
|
if (arr == NULL)
|
|
return;
|
|
int k;
|
|
int j;
|
|
unsigned int t;
|
|
srand(time(0));
|
|
for (j=0; j<100; j++)
|
|
{
|
|
k = j + (rand() % (100-j));
|
|
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;
|
|
|
|
if(dset==NULL || dset->dlist==NULL)
|
|
return -1;
|
|
|
|
int rw_sum = 0;
|
|
/* find the sum of relative weights*/
|
|
for(j=0; j<dset->nr; j++){
|
|
if( ds_skip_dst(dset->dlist[j].flags ) )
|
|
continue;
|
|
rw_sum += dset->dlist[j].attrs.rweight;
|
|
}
|
|
|
|
if (rw_sum == 0){
|
|
return 0;
|
|
}
|
|
|
|
/* fill the array based on the relative weight of each destination */
|
|
t = 0;
|
|
for(j=0; j<dset->nr; j++)
|
|
{
|
|
if( ds_skip_dst(dset->dlist[j].flags ) )
|
|
continue;
|
|
|
|
int current_slice = dset->dlist[j].attrs.rweight*100/rw_sum; //truncate here;
|
|
for(k=0; k<current_slice; k++)
|
|
{
|
|
dset->rwlist[t] = (unsigned int)j;
|
|
t++;
|
|
}
|
|
}
|
|
/* if the array was not completely filled (i.e., the sum of rweights is
|
|
* less than 100 due to truncated), then use last address to fill the rest */
|
|
unsigned int last_insert = t>0? dset->rwlist[t-1] : (unsigned int)(dset->nr-1);
|
|
for(j=t; j<100; j++)
|
|
dset->rwlist[j] = last_insert;
|
|
|
|
/* 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);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Initialize the 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_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;
|
|
|
|
/* 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 */
|
|
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++;
|
|
}
|
|
}
|
|
/* 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 */
|
|
for(; t<100; t++)
|
|
dset->wlist[t] = (unsigned int)(dset->nr-1);
|
|
randomize:
|
|
/* 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->wlist);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*! \brief compact destinations from sets for fast access */
|
|
int reindex_dests(int list_idx, int setn)
|
|
{
|
|
int j;
|
|
ds_set_t *sp = NULL;
|
|
ds_dest_t *dp = NULL, *dp0= NULL;
|
|
|
|
for(sp = ds_lists[list_idx]; sp!= NULL; sp = sp->next)
|
|
{
|
|
dp0 = (ds_dest_t*)shm_malloc(sp->nr*sizeof(ds_dest_t));
|
|
if(dp0==NULL)
|
|
{
|
|
LM_ERR("no more memory!\n");
|
|
goto err1;
|
|
}
|
|
memset(dp0, 0, sp->nr*sizeof(ds_dest_t));
|
|
|
|
/* copy from the old pointer to destination, and then free it */
|
|
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];
|
|
|
|
|
|
dp = sp->dlist;
|
|
sp->dlist = dp->next;
|
|
|
|
shm_free(dp);
|
|
dp=NULL;
|
|
}
|
|
sp->dlist = dp0;
|
|
dp_init_weights(sp);
|
|
dp_init_relative_weights(sp);
|
|
}
|
|
|
|
LM_DBG("found [%d] dest sets\n", setn);
|
|
return 0;
|
|
|
|
err1:
|
|
return -1;
|
|
}
|
|
|
|
/*! \brief load groups of destinations from file */
|
|
int ds_load_list(char *lfile)
|
|
{
|
|
char line[256], *p;
|
|
FILE *f = NULL;
|
|
int id, setn, flags, priority;
|
|
str uri;
|
|
str attrs;
|
|
|
|
if( (*crt_idx) != (*next_idx)) {
|
|
LM_WARN("load command already generated, aborting reload...\n");
|
|
return 0;
|
|
}
|
|
|
|
if(lfile==NULL || strlen(lfile)<=0)
|
|
{
|
|
LM_ERR("bad list file\n");
|
|
return -1;
|
|
}
|
|
|
|
f = fopen(lfile, "r");
|
|
if(f==NULL)
|
|
{
|
|
LM_ERR("can't open list file [%s]\n", lfile);
|
|
return -1;
|
|
|
|
}
|
|
|
|
id = setn = flags = priority = 0;
|
|
|
|
*next_idx = (*crt_idx + 1)%2;
|
|
destroy_list(*next_idx);
|
|
|
|
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;
|
|
|
|
/* get set id */
|
|
id = 0;
|
|
while(*p>='0' && *p<='9')
|
|
{
|
|
id = id*10+ (*p-'0');
|
|
p++;
|
|
}
|
|
|
|
/* eat all white spaces */
|
|
while(*p && (*p==' ' || *p=='\t' || *p=='\r' || *p=='\n'))
|
|
p++;
|
|
if(*p=='\0' || *p=='#')
|
|
{
|
|
LM_ERR("bad line [%s]\n", line);
|
|
goto error;
|
|
}
|
|
|
|
/* get uri */
|
|
uri.s = p;
|
|
while(*p && *p!=' ' && *p!='\t' && *p!='\r' && *p!='\n' && *p!='#')
|
|
p++;
|
|
uri.len = p-uri.s;
|
|
|
|
/* eat all white spaces */
|
|
while(*p && (*p==' ' || *p=='\t' || *p=='\r' || *p=='\n'))
|
|
p++;
|
|
|
|
/* get flags */
|
|
flags = 0;
|
|
priority = 0;
|
|
attrs.s = 0; attrs.len = 0;
|
|
if(*p=='\0' || *p=='#')
|
|
goto add_destination; /* no flags given */
|
|
|
|
while(*p>='0' && *p<='9')
|
|
{
|
|
flags = flags*10+ (*p-'0');
|
|
p++;
|
|
}
|
|
|
|
/* eat all white spaces */
|
|
while(*p && (*p==' ' || *p=='\t' || *p=='\r' || *p=='\n'))
|
|
p++;
|
|
|
|
/* get priority */
|
|
if(*p=='\0' || *p=='#')
|
|
goto add_destination; /* no priority given */
|
|
|
|
while(*p>='0' && *p<='9')
|
|
{
|
|
priority = priority*10+ (*p-'0');
|
|
p++;
|
|
}
|
|
|
|
/* eat all white spaces */
|
|
while(*p && (*p==' ' || *p=='\t' || *p=='\r' || *p=='\n'))
|
|
p++;
|
|
if(*p=='\0' || *p=='#')
|
|
goto add_destination; /* no attrs given */
|
|
|
|
/* get attributes */
|
|
attrs.s = p;
|
|
while(*p && *p!=' ' && *p!='\t' && *p!='\r' && *p!='\n')
|
|
p++;
|
|
attrs.len = p-attrs.s;
|
|
|
|
add_destination:
|
|
if(add_dest2list(id, uri, flags, priority, &attrs,
|
|
*next_idx, &setn) != 0)
|
|
LM_WARN("unable to add destination %.*s to set %d -- skipping\n",
|
|
uri.len, uri.s, id);
|
|
next_line:
|
|
p = fgets(line, 256, f);
|
|
}
|
|
|
|
if(reindex_dests(*next_idx, setn)!=0){
|
|
LM_ERR("error on reindex\n");
|
|
goto error;
|
|
}
|
|
|
|
fclose(f);
|
|
f = NULL;
|
|
/* Update list - should it be sync'ed? */
|
|
_ds_list_nr = setn;
|
|
*crt_idx = *next_idx;
|
|
ds_ht_clear_slots(_dsht_load);
|
|
ds_print_sets();
|
|
return 0;
|
|
|
|
error:
|
|
if(f!=NULL)
|
|
fclose(f);
|
|
destroy_list(*next_idx);
|
|
*next_idx = *crt_idx;
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
int ds_connect_db(void)
|
|
{
|
|
if(ds_db_url.s==NULL)
|
|
return -1;
|
|
|
|
if((ds_db_handle = ds_dbf.init(&ds_db_url)) == 0) {
|
|
LM_ERR("cannot initialize db connection\n");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
void ds_disconnect_db(void)
|
|
{
|
|
if(ds_db_handle)
|
|
{
|
|
ds_dbf.close(ds_db_handle);
|
|
ds_db_handle = 0;
|
|
}
|
|
}
|
|
|
|
/*! \brief Initialize and verify DB stuff*/
|
|
int init_ds_db(void)
|
|
{
|
|
int ret;
|
|
|
|
if(ds_table_name.s == 0)
|
|
{
|
|
LM_ERR("invalid database name\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Find a database module */
|
|
if (db_bind_mod(&ds_db_url, &ds_dbf) < 0)
|
|
{
|
|
LM_ERR("Unable to bind to a database driver\n");
|
|
return -1;
|
|
}
|
|
|
|
if(ds_connect_db()!=0)
|
|
{
|
|
LM_ERR("unable to connect to the database\n");
|
|
return -1;
|
|
}
|
|
|
|
_ds_table_version = db_table_version(&ds_dbf, ds_db_handle, &ds_table_name);
|
|
if (_ds_table_version < 0)
|
|
{
|
|
LM_ERR("failed to query table version\n");
|
|
return -1;
|
|
} 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) {
|
|
LM_ERR("invalid table version (found %d , required %d, %d, %d or %d)\n"
|
|
"(use kamdbctl reinit)\n",
|
|
_ds_table_version, DS_TABLE_VERSION, DS_TABLE_VERSION2,
|
|
DS_TABLE_VERSION3, DS_TABLE_VERSION4);
|
|
return -1;
|
|
}
|
|
|
|
ret = ds_load_db();
|
|
if (ret == -2)
|
|
{
|
|
LM_WARN("failure while loading one or more dispatcher entries\n");
|
|
ret = 0;
|
|
}
|
|
|
|
ds_disconnect_db();
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*! \brief reload groups of destinations from DB*/
|
|
int ds_reload_db(void)
|
|
{
|
|
int ret;
|
|
|
|
if(ds_connect_db()!=0)
|
|
{
|
|
LM_ERR("unable to connect to the database\n");
|
|
return -1;
|
|
}
|
|
ret = ds_load_db();
|
|
if (ret == -2)
|
|
{
|
|
LM_WARN("failure while loading one or more dispatcher entries\n");
|
|
}
|
|
ds_disconnect_db();
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*! \brief load groups of destinations from DB*/
|
|
int ds_load_db(void)
|
|
{
|
|
int i, id, nr_rows, setn;
|
|
int flags;
|
|
int priority;
|
|
int nrcols;
|
|
int dest_errs = 0;
|
|
str uri;
|
|
str attrs = {0, 0};
|
|
db1_res_t * res;
|
|
db_val_t * values;
|
|
db_row_t * rows;
|
|
|
|
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};
|
|
|
|
nrcols = 2;
|
|
if(_ds_table_version == DS_TABLE_VERSION2)
|
|
nrcols = 3;
|
|
else if(_ds_table_version == DS_TABLE_VERSION3)
|
|
nrcols = 4;
|
|
else if(_ds_table_version == DS_TABLE_VERSION4)
|
|
nrcols = 5;
|
|
|
|
if( (*crt_idx) != (*next_idx))
|
|
{
|
|
LM_WARN("load command already generated, aborting reload...\n");
|
|
return 0;
|
|
}
|
|
|
|
if(ds_db_handle == NULL){
|
|
LM_ERR("invalid DB handler\n");
|
|
return -1;
|
|
}
|
|
|
|
if (ds_dbf.use_table(ds_db_handle, &ds_table_name) < 0)
|
|
{
|
|
LM_ERR("error in use_table\n");
|
|
return -1;
|
|
}
|
|
|
|
/*select the whole table and all the columns*/
|
|
if(ds_dbf.query(ds_db_handle,0,0,0,query_cols,0,nrcols,0,&res) < 0)
|
|
{
|
|
LM_ERR("error while querying database\n");
|
|
return -1;
|
|
}
|
|
|
|
nr_rows = RES_ROW_N(res);
|
|
rows = RES_ROWS(res);
|
|
if(nr_rows == 0)
|
|
LM_WARN("no dispatching data in the db -- empty destination set\n");
|
|
|
|
setn = 0;
|
|
*next_idx = (*crt_idx + 1)%2;
|
|
destroy_list(*next_idx);
|
|
|
|
for(i=0; i<nr_rows; i++)
|
|
{
|
|
values = ROW_VALUES(rows+i);
|
|
|
|
id = VAL_INT(values);
|
|
uri.s = VAL_STR(values+1).s;
|
|
uri.len = strlen(uri.s);
|
|
flags = 0;
|
|
if(nrcols>=3)
|
|
flags = VAL_INT(values+2);
|
|
priority=0;
|
|
if(nrcols>=4)
|
|
priority = VAL_INT(values+3);
|
|
|
|
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)
|
|
{
|
|
dest_errs++;
|
|
LM_WARN("unable to add destination %.*s to set %d -- skipping\n",
|
|
uri.len, uri.s, id);
|
|
}
|
|
}
|
|
if(reindex_dests(*next_idx, setn)!=0)
|
|
{
|
|
LM_ERR("error on reindex\n");
|
|
goto err2;
|
|
}
|
|
|
|
ds_dbf.free_result(ds_db_handle, res);
|
|
|
|
/* update data - should it be sync'ed? */
|
|
_ds_list_nr = setn;
|
|
*crt_idx = *next_idx;
|
|
ds_ht_clear_slots(_dsht_load);
|
|
|
|
ds_print_sets();
|
|
|
|
if (dest_errs > 0)
|
|
return -2;
|
|
return 0;
|
|
|
|
err2:
|
|
destroy_list(*next_idx);
|
|
ds_dbf.free_result(ds_db_handle, res);
|
|
*next_idx = *crt_idx;
|
|
|
|
return -1;
|
|
}
|
|
|
|
/*! \brief called from dispatcher.c: free all*/
|
|
int ds_destroy_list(void)
|
|
{
|
|
if (ds_lists) {
|
|
destroy_list(0);
|
|
destroy_list(1);
|
|
shm_free(ds_lists);
|
|
}
|
|
|
|
if (crt_idx)
|
|
shm_free(crt_idx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
void destroy_list(int list_id)
|
|
{
|
|
ds_set_t *sp = NULL;
|
|
ds_set_t *sp1 = NULL;
|
|
ds_dest_t *dest = NULL;
|
|
|
|
sp = ds_lists[list_id];
|
|
|
|
while(sp)
|
|
{
|
|
sp1 = sp->next;
|
|
for(dest = sp->dlist; dest!= NULL; dest=dest->next)
|
|
{
|
|
if(dest->uri.s!=NULL)
|
|
{
|
|
shm_free(dest->uri.s);
|
|
dest->uri.s = NULL;
|
|
}
|
|
}
|
|
if (sp->dlist != NULL)
|
|
shm_free(sp->dlist);
|
|
shm_free(sp);
|
|
sp = sp1;
|
|
}
|
|
|
|
ds_lists[list_id] = NULL;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
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)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
v=0;
|
|
for (;p<(x->s+x->len); p++)
|
|
{
|
|
v<<=8;
|
|
v+=*p;
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
|
|
v=0;
|
|
for (;p<(y->s+y->len); p++)
|
|
{
|
|
v<<=8;
|
|
v+=*p;
|
|
}
|
|
h+=v^(v>>3);
|
|
}
|
|
h=((h)+(h>>11))+((h>>13)+(h>>23));
|
|
|
|
return (h)?h:1;
|
|
}
|
|
|
|
|
|
/*! \brief
|
|
* gets the part of the uri we will use as a key for hashing
|
|
* \param key1 - will be filled with first part of the key
|
|
* (uri user or "" if no user)
|
|
* \param key2 - will be filled with the second part of the key
|
|
* (uri host:port)
|
|
* \param uri - str with the whole uri
|
|
* \param parsed_uri - struct sip_uri pointer with the parsed uri
|
|
* (it must point inside uri). It can be null
|
|
* (in this case the uri will be parsed internally).
|
|
* \param flags - if & DS_HASH_USER_ONLY, only the user part of the uri
|
|
* will be used
|
|
* \return: -1 on error, 0 on success
|
|
*/
|
|
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 */
|
|
|
|
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:"");
|
|
goto error;
|
|
}
|
|
parsed_uri=&tmp_p_uri;
|
|
}
|
|
/* uri sanity checks */
|
|
if (parsed_uri->host.s==0)
|
|
{
|
|
LM_ERR("invalid uri, no host present: %.*s\n",
|
|
uri->len, uri->len?uri->s:"");
|
|
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;
|
|
if (!(flags & DS_HASH_USER_ONLY))
|
|
{ /* key2=host */
|
|
*key2=parsed_uri->host;
|
|
/* add port if needed */
|
|
if (parsed_uri->port.s!=0)
|
|
{ /* uri has a port */
|
|
/* 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 /* ':' */;
|
|
}
|
|
}
|
|
if (key1->s==0)
|
|
{
|
|
LM_WARN("empty username in: %.*s\n", uri->len, uri->len?uri->s:"");
|
|
}
|
|
return 0;
|
|
error:
|
|
return -1;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
int ds_hash_fromuri(struct sip_msg *msg, unsigned int *hash)
|
|
{
|
|
str from;
|
|
str key1;
|
|
str key2;
|
|
|
|
if(msg==NULL || hash == NULL)
|
|
{
|
|
LM_ERR("bad parameters\n");
|
|
return -1;
|
|
}
|
|
|
|
if(parse_from_header(msg)<0)
|
|
{
|
|
LM_ERR("cannot parse From hdr\n");
|
|
return -1;
|
|
}
|
|
|
|
if(msg->from==NULL || get_from(msg)==NULL)
|
|
{
|
|
LM_ERR("cannot get From uri\n");
|
|
return -1;
|
|
}
|
|
|
|
from = get_from(msg)->uri;
|
|
trim(&from);
|
|
if (get_uri_hash_keys(&key1, &key2, &from, 0, ds_flags)<0)
|
|
return -1;
|
|
*hash = ds_get_hash(&key1, &key2);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
int ds_hash_touri(struct sip_msg *msg, unsigned int *hash)
|
|
{
|
|
str to;
|
|
str key1;
|
|
str key2;
|
|
|
|
if(msg==NULL || hash == NULL)
|
|
{
|
|
LM_ERR("bad parameters\n");
|
|
return -1;
|
|
}
|
|
if ((msg->to==0) && ((parse_headers(msg, HDR_TO_F, 0)==-1) ||
|
|
(msg->to==0)))
|
|
{
|
|
LM_ERR("cannot parse To hdr\n");
|
|
return -1;
|
|
}
|
|
|
|
|
|
to = get_to(msg)->uri;
|
|
trim(&to);
|
|
|
|
if (get_uri_hash_keys(&key1, &key2, &to, 0, ds_flags)<0)
|
|
return -1;
|
|
*hash = ds_get_hash(&key1, &key2);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
int ds_hash_callid(struct sip_msg *msg, unsigned int *hash)
|
|
{
|
|
str cid;
|
|
if(msg==NULL || hash == NULL)
|
|
{
|
|
LM_ERR("bad parameters\n");
|
|
return -1;
|
|
}
|
|
|
|
if(msg->callid==NULL && ((parse_headers(msg, HDR_CALLID_F, 0)==-1) ||
|
|
(msg->callid==NULL)) )
|
|
{
|
|
LM_ERR("cannot parse Call-Id\n");
|
|
return -1;
|
|
}
|
|
|
|
cid.s = msg->callid->body.s;
|
|
cid.len = msg->callid->body.len;
|
|
trim(&cid);
|
|
|
|
*hash = ds_get_hash(&cid, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
int ds_hash_ruri(struct sip_msg *msg, unsigned int *hash)
|
|
{
|
|
str* uri;
|
|
str key1;
|
|
str key2;
|
|
|
|
|
|
if(msg==NULL || hash == NULL)
|
|
{
|
|
LM_ERR("bad parameters\n");
|
|
return -1;
|
|
}
|
|
if (parse_sip_msg_uri(msg)<0){
|
|
LM_ERR("bad request uri\n");
|
|
return -1;
|
|
}
|
|
|
|
uri=GET_RURI(msg);
|
|
if (get_uri_hash_keys(&key1, &key2, uri, &msg->parsed_uri, ds_flags)<0)
|
|
return -1;
|
|
|
|
*hash = ds_get_hash(&key1, &key2);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
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)
|
|
{
|
|
LM_ERR("bad parameters\n");
|
|
return -1;
|
|
}
|
|
if (parse_headers(msg, HDR_PROXYAUTH_F, 0) == -1)
|
|
{
|
|
LM_ERR("error parsing headers!\n");
|
|
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)
|
|
{
|
|
LM_ERR("error parsing headers!\n");
|
|
return -1;
|
|
}
|
|
if (msg->authorization && !msg->authorization->parsed)
|
|
parse_credentials(msg->authorization);
|
|
if (msg->authorization && msg->authorization->parsed) {
|
|
h = msg->authorization;
|
|
}
|
|
}
|
|
if (!h)
|
|
{
|
|
LM_DBG("No Authorization-Header!\n");
|
|
return 1;
|
|
}
|
|
|
|
cred=(auth_body_t*)(h->parsed);
|
|
if (!cred || !cred->digest.username.user.len)
|
|
{
|
|
LM_ERR("No Authorization-Username or Credentials!\n");
|
|
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;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
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;
|
|
}
|
|
|
|
*hash = ds_get_hash(&hash_str, NULL);
|
|
LM_DBG("Hashing of '%.*s' resulted in %u !\n", hash_str.len, hash_str.s,
|
|
*hash);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* Check if a destination set exists
|
|
*/
|
|
int ds_list_exist(int set)
|
|
{
|
|
ds_set_t *si = NULL;
|
|
LM_DBG("-- Looking for set %d\n", set);
|
|
|
|
/* get the index of the set */
|
|
si = _ds_list;
|
|
while(si)
|
|
{
|
|
if(si->id == set)
|
|
{
|
|
break;
|
|
}
|
|
si = si->next;
|
|
}
|
|
|
|
if(si==NULL)
|
|
{
|
|
LM_INFO("destination set [%d] not found\n", set);
|
|
return -1; /* False */
|
|
}
|
|
LM_INFO("destination set [%d] found\n", set);
|
|
return 1; /* True */
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
int ds_get_leastloaded(ds_set_t *dset)
|
|
{
|
|
int j;
|
|
int k;
|
|
int t;
|
|
|
|
k = -1;
|
|
t = 0x7fffffff; /* high load */
|
|
for(j=0; j<dset->nr; j++)
|
|
{
|
|
if(!ds_skip_dst(dset->dlist[j].flags)
|
|
&& (dset->dlist[j].attrs.maxload == 0
|
|
|| dset->dlist[j].dload<dset->dlist[j].attrs.maxload))
|
|
{
|
|
if(dset->dlist[j].dload<t)
|
|
{
|
|
k = j;
|
|
t = dset->dlist[k].dload;
|
|
}
|
|
}
|
|
}
|
|
return k;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
int ds_load_add(struct sip_msg *msg, ds_set_t *dset, int setid, int dst)
|
|
{
|
|
if(dset->dlist[dst].attrs.duid.len==0)
|
|
{
|
|
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,
|
|
&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);
|
|
return -1;
|
|
}
|
|
dset->dlist[dst].dload++;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
int ds_load_replace(struct sip_msg *msg, str *duid)
|
|
{
|
|
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);
|
|
return -1;
|
|
}
|
|
|
|
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;
|
|
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)
|
|
{
|
|
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);
|
|
if(idx->dlist[olddst].dload>0)
|
|
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;
|
|
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;
|
|
}
|
|
|
|
ds_unlock_cell(_dsht_load, &msg->callid->body);
|
|
ds_del_cell(_dsht_load, &msg->callid->body);
|
|
if(idx->dlist[olddst].dload>0)
|
|
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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
if(idx->dlist[olddst].dload>0)
|
|
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);
|
|
return -1;
|
|
}
|
|
|
|
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);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
static inline int ds_update_dst(struct sip_msg *msg, str *uri,
|
|
struct socket_info *sock, int mode)
|
|
{
|
|
struct action act;
|
|
struct run_act_ctx ra_ctx;
|
|
switch(mode)
|
|
{
|
|
case 1:
|
|
memset(&act, '\0', sizeof(act));
|
|
act.type = SET_HOSTALL_T;
|
|
act.val[0].type = STRING_ST;
|
|
if(uri->len>4
|
|
&& strncasecmp(uri->s,"sip:",4)==0)
|
|
act.val[0].u.string = uri->s+4;
|
|
else
|
|
act.val[0].u.string = uri->s;
|
|
init_run_actions_ctx(&ra_ctx);
|
|
if (do_action(&ra_ctx, &act, msg) < 0) {
|
|
LM_ERR("error while setting host\n");
|
|
return -1;
|
|
}
|
|
break;
|
|
default:
|
|
if (set_dst_uri(msg, uri) < 0) {
|
|
LM_ERR("error while setting dst uri\n");
|
|
return -1;
|
|
}
|
|
/* dst_uri changes, so it makes sense to re-use the current uri for
|
|
* forking */
|
|
ruri_mark_new(); /* re-use uri for serial forking */
|
|
break;
|
|
}
|
|
if (sock)
|
|
msg->force_send_socket = sock;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
int ds_select_dst(struct sip_msg *msg, int set, int alg, int mode)
|
|
{
|
|
return ds_select_dst_limit(msg, set, alg, 0, mode);
|
|
}
|
|
|
|
/**
|
|
* Set destination address from group 'set' selected with alogorithm 'alg'
|
|
* - the rest of addresses in group are added as next destination in avps,
|
|
* up to the 'limit'
|
|
* - mode specify to set address in R-URI or outboud proxy
|
|
*
|
|
*/
|
|
int ds_select_dst_limit(sip_msg_t *msg, int set, int alg, unsigned int limit, int mode)
|
|
{
|
|
int i, cnt;
|
|
unsigned int hash;
|
|
int_str avp_val;
|
|
ds_set_t *idx = NULL;
|
|
char buf[2+16+1];
|
|
|
|
if(msg==NULL)
|
|
{
|
|
LM_ERR("bad parameters\n");
|
|
return -1;
|
|
}
|
|
|
|
if(_ds_list==NULL || _ds_list_nr<=0)
|
|
{
|
|
LM_ERR("no destination sets\n");
|
|
return -1;
|
|
}
|
|
|
|
if (limit==0)
|
|
{
|
|
LM_DBG("Limit set to 0 - forcing to unlimited\n");
|
|
limit = 0xffffffff;
|
|
}
|
|
--limit; /* reserving 1 slot for selected dst */
|
|
|
|
if((mode==0) && (ds_force_dst==0)
|
|
&& (msg->dst_uri.s!=NULL || msg->dst_uri.len>0))
|
|
{
|
|
LM_ERR("destination already set [%.*s]\n", msg->dst_uri.len,
|
|
msg->dst_uri.s);
|
|
return -1;
|
|
}
|
|
|
|
|
|
/* get the index of the set */
|
|
if(ds_get_index(set, &idx)!=0)
|
|
{
|
|
LM_ERR("destination set [%d] not found\n", set);
|
|
return -1;
|
|
}
|
|
|
|
LM_DBG("set [%d]\n", set);
|
|
|
|
hash = 0;
|
|
switch(alg)
|
|
{
|
|
case 0: /* hash call-id */
|
|
if(ds_hash_callid(msg, &hash)!=0)
|
|
{
|
|
LM_ERR("can't get callid hash\n");
|
|
return -1;
|
|
}
|
|
break;
|
|
case 1: /* hash from-uri */
|
|
if(ds_hash_fromuri(msg, &hash)!=0)
|
|
{
|
|
LM_ERR("can't get From uri hash\n");
|
|
return -1;
|
|
}
|
|
break;
|
|
case 2: /* hash to-uri */
|
|
if(ds_hash_touri(msg, &hash)!=0)
|
|
{
|
|
LM_ERR("can't get To uri hash\n");
|
|
return -1;
|
|
}
|
|
break;
|
|
case 3: /* hash r-uri */
|
|
if (ds_hash_ruri(msg, &hash)!=0)
|
|
{
|
|
LM_ERR("can't get ruri hash\n");
|
|
return -1;
|
|
}
|
|
break;
|
|
case DS_ALG_RROBIN: /* round robin */
|
|
hash = idx->last;
|
|
idx->last = (idx->last+1) % idx->nr;
|
|
break;
|
|
case 5: /* hash auth username */
|
|
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 */
|
|
hash = idx->last;
|
|
idx->last = (idx->last+1) % idx->nr;
|
|
break;
|
|
default:
|
|
LM_ERR("can't get authorization hash\n");
|
|
return -1;
|
|
}
|
|
break;
|
|
case 6: /* random selection */
|
|
hash = rand() % idx->nr;
|
|
break;
|
|
case 7: /* hash on PV value */
|
|
if (ds_hash_pvar(msg, &hash)!=0)
|
|
{
|
|
LM_ERR("can't get PV hash\n");
|
|
return -1;
|
|
}
|
|
break;
|
|
case 8: /* use always first entry */
|
|
hash = 0;
|
|
break;
|
|
case 9: /* weight based distribution */
|
|
hash = idx->wlist[idx->wlast];
|
|
idx->wlast = (idx->wlast+1) % 100;
|
|
break;
|
|
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 {
|
|
i = ds_get_leastloaded(idx);
|
|
if(i<0)
|
|
{
|
|
/* no address selected */
|
|
return -1;
|
|
}
|
|
hash = i;
|
|
if(ds_load_add(msg, idx, set, hash)<0)
|
|
{
|
|
LM_ERR("unable to update destination load"
|
|
" - classic dispatching\n");
|
|
alg = 0;
|
|
}
|
|
}
|
|
break;
|
|
case 11: /* relative weight based distribution */
|
|
hash = idx->rwlist[idx->rwlast];
|
|
idx->rwlast = (idx->rwlast+1) % 100;
|
|
break;
|
|
default:
|
|
LM_WARN("algo %d not implemented - using first entry...\n", alg);
|
|
hash = 0;
|
|
}
|
|
|
|
LM_DBG("alg hash [%u]\n", hash);
|
|
cnt = 0;
|
|
|
|
if(ds_use_default!=0 && idx->nr!=1)
|
|
hash = hash%(idx->nr-1);
|
|
else
|
|
hash = hash%idx->nr;
|
|
i=hash;
|
|
|
|
/* if selected address is inactive, find next active */
|
|
while (ds_skip_dst(idx->dlist[i].flags))
|
|
{
|
|
if(ds_use_default!=0 && idx->nr!=1)
|
|
i = (i+1)%(idx->nr-1);
|
|
else
|
|
i = (i+1)%idx->nr;
|
|
if(i==hash)
|
|
{
|
|
/* back to start -- looks like no active dst */
|
|
if(ds_use_default!=0)
|
|
{
|
|
i = idx->nr-1;
|
|
if(ds_skip_dst(idx->dlist[i].flags))
|
|
return -1;
|
|
break;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
hash = i;
|
|
|
|
if(ds_update_dst(msg, &idx->dlist[hash].uri, idx->dlist[hash].sock, mode)!=0)
|
|
{
|
|
LM_ERR("cannot set dst addr\n");
|
|
return -1;
|
|
}
|
|
/* if alg is round-robin then update the shortcut to next to be used */
|
|
if(alg==DS_ALG_RROBIN)
|
|
idx->last = (hash+1) % idx->nr;
|
|
|
|
LM_DBG("selected [%d-%d/%d] <%.*s>\n", alg, set, hash,
|
|
idx->dlist[hash].uri.len, idx->dlist[hash].uri.s);
|
|
|
|
if(!(ds_flags&DS_FAILOVER_ON))
|
|
return 1;
|
|
|
|
if(dst_avp_name.n!=0)
|
|
{
|
|
/* add default dst to last position in AVP list */
|
|
if(ds_use_default!=0 && hash!=idx->nr-1 && cnt<limit)
|
|
{
|
|
avp_val.s = idx->dlist[idx->nr-1].uri;
|
|
if(add_avp(AVP_VAL_STR|dst_avp_type, dst_avp_name, avp_val)!=0)
|
|
return -1;
|
|
|
|
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;
|
|
}
|
|
|
|
/* only add sock_avp if dst_avp is set */
|
|
if(sock_avp_name.n!=0 && idx->dlist[idx->nr-1].sock)
|
|
{
|
|
avp_val.s.len = 1 + sprintf(buf, "%p", idx->dlist[idx->nr-1].sock);
|
|
avp_val.s.s = buf;
|
|
if(add_avp(AVP_VAL_STR|sock_avp_type, sock_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;
|
|
}
|
|
cnt++;
|
|
}
|
|
|
|
/* add to avp */
|
|
|
|
for(i=hash-1; i>=0 && cnt<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(alg==DS_ALG_LOAD
|
|
&& idx->dlist[i].dload>=idx->dlist[i].attrs.maxload)
|
|
continue;
|
|
LM_DBG("using entry [%d/%d]\n", set, i);
|
|
avp_val.s = idx->dlist[i].uri;
|
|
if(add_avp(AVP_VAL_STR|dst_avp_type, dst_avp_name, avp_val)!=0)
|
|
return -1;
|
|
|
|
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(sock_avp_name.n!=0 && idx->dlist[i].sock)
|
|
{
|
|
avp_val.s.len = 1 + sprintf(buf, "%p", idx->dlist[i].sock);
|
|
avp_val.s.s = buf;
|
|
if(add_avp(AVP_VAL_STR|sock_avp_type, sock_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;
|
|
}
|
|
cnt++;
|
|
}
|
|
|
|
for(i=idx->nr-1; i>hash && cnt<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(alg==DS_ALG_LOAD
|
|
&& idx->dlist[i].dload>=idx->dlist[i].attrs.maxload)
|
|
LM_DBG("using entry [%d/%d]\n", set, i);
|
|
avp_val.s = idx->dlist[i].uri;
|
|
if(add_avp(AVP_VAL_STR|dst_avp_type, dst_avp_name, avp_val)!=0)
|
|
return -1;
|
|
|
|
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(sock_avp_name.n!=0 && idx->dlist[i].sock)
|
|
{
|
|
avp_val.s.len = 1 + sprintf(buf, "%p", idx->dlist[i].sock);
|
|
avp_val.s.s = buf;
|
|
if(add_avp(AVP_VAL_STR|sock_avp_type, sock_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;
|
|
}
|
|
cnt++;
|
|
}
|
|
|
|
/* add to avp the first used dst */
|
|
avp_val.s = idx->dlist[hash].uri;
|
|
if(add_avp(AVP_VAL_STR|dst_avp_type, dst_avp_name, avp_val)!=0)
|
|
return -1;
|
|
|
|
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(sock_avp_name.n!=0 && idx->dlist[hash].sock)
|
|
{
|
|
avp_val.s.len = 1 + sprintf(buf, "%p", idx->dlist[hash].sock);
|
|
avp_val.s.s = buf;
|
|
if(add_avp(AVP_VAL_STR|sock_avp_type, sock_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;
|
|
}
|
|
cnt++;
|
|
}
|
|
|
|
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)
|
|
return -1;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int ds_next_dst(struct sip_msg *msg, int mode)
|
|
{
|
|
struct search_state st;
|
|
struct usr_avp *avp;
|
|
struct usr_avp *prev_avp;
|
|
struct socket_info *sock = NULL;
|
|
int_str avp_value;
|
|
int_str sock_avp_value;
|
|
int alg = 0;
|
|
|
|
if(!(ds_flags&DS_FAILOVER_ON) || dst_avp_name.n==0)
|
|
{
|
|
LM_WARN("failover support disabled\n");
|
|
return -1;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
if(sock_avp_name.n!=0)
|
|
{
|
|
prev_avp = search_first_avp(sock_avp_type,
|
|
attrs_avp_name, &sock_avp_value, &st);
|
|
if(prev_avp!=NULL)
|
|
{
|
|
if (sscanf( sock_avp_value.s.s, "%p", (void**)&sock ) != 1)
|
|
sock = NULL;
|
|
destroy_avp(prev_avp);
|
|
}
|
|
}
|
|
|
|
prev_avp = search_first_avp(dst_avp_type, dst_avp_name, &avp_value, &st);
|
|
if(prev_avp==NULL)
|
|
return -1; /* used avp deleted -- strange */
|
|
|
|
avp = search_next_avp(&st, &avp_value);
|
|
destroy_avp(prev_avp);
|
|
if(avp==NULL || !(avp->flags&AVP_VAL_STR))
|
|
return -1; /* no more avps or value is int */
|
|
|
|
if(ds_update_dst(msg, &avp_value.s, sock, mode)!=0)
|
|
{
|
|
LM_ERR("cannot set dst addr\n");
|
|
return -1;
|
|
}
|
|
LM_DBG("using [%.*s]\n", avp_value.s.len, avp_value.s.s);
|
|
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 find uid avp for destination address\n");
|
|
return -1;
|
|
}
|
|
if(ds_load_replace(msg, &avp_value.s)<0)
|
|
{
|
|
LM_ERR("cannot update load distribution\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int ds_mark_dst(struct sip_msg *msg, int state)
|
|
{
|
|
int group, ret;
|
|
struct usr_avp *prev_avp;
|
|
int_str avp_value;
|
|
|
|
if(!(ds_flags&DS_FAILOVER_ON))
|
|
{
|
|
LM_WARN("failover support disabled\n");
|
|
return -1;
|
|
}
|
|
|
|
prev_avp = search_first_avp(grp_avp_type, grp_avp_name, &avp_value, 0);
|
|
|
|
if(prev_avp==NULL || prev_avp->flags&AVP_VAL_STR)
|
|
return -1; /* grp avp deleted -- strange */
|
|
group = avp_value.n;
|
|
|
|
prev_avp = search_first_avp(dst_avp_type, dst_avp_name, &avp_value, 0);
|
|
|
|
if(prev_avp==NULL || !(prev_avp->flags&AVP_VAL_STR))
|
|
return -1; /* dst avp deleted -- strange */
|
|
|
|
ret = ds_update_state(msg, group, &avp_value.s, state);
|
|
|
|
LM_DBG("state [%d] grp [%d] dst [%.*s]\n", state, group, avp_value.s.len,
|
|
avp_value.s.s);
|
|
|
|
return (ret==0)?1:-1;
|
|
}
|
|
|
|
/**
|
|
* Get state for given destination
|
|
*/
|
|
int ds_get_state(int group, str *address)
|
|
{
|
|
int i=0;
|
|
int state = 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;
|
|
}
|
|
|
|
while(i<idx->nr)
|
|
{
|
|
if(idx->dlist[i].uri.len==address->len
|
|
&& strncasecmp(idx->dlist[i].uri.s, address->s,
|
|
address->len)==0)
|
|
{
|
|
/* destination address found */
|
|
state = idx->dlist[i].flags;
|
|
}
|
|
i++;
|
|
}
|
|
return state;
|
|
}
|
|
|
|
/**
|
|
* Update destionation's state
|
|
*/
|
|
int ds_update_state(sip_msg_t *msg, int group, str *address, int state)
|
|
{
|
|
int i=0;
|
|
int old_state = 0;
|
|
int init_state = 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;
|
|
}
|
|
|
|
while(i<idx->nr)
|
|
{
|
|
if(idx->dlist[i].uri.len==address->len
|
|
&& strncasecmp(idx->dlist[i].uri.s, address->s,
|
|
address->len)==0)
|
|
{
|
|
/* destination address found */
|
|
old_state = idx->dlist[i].flags;
|
|
|
|
/* reset the bits used for states */
|
|
idx->dlist[i].flags &= ~(DS_STATES_ALL);
|
|
|
|
/* we need the initial state for inactive counter */
|
|
init_state = state;
|
|
|
|
if((state & DS_TRYING_DST) && (old_state & DS_INACTIVE_DST))
|
|
{
|
|
/* old state is inactive, new state is trying => keep it inactive
|
|
* - it has to go first to active state and then to trying */
|
|
state &= ~(DS_TRYING_DST);
|
|
state |= DS_INACTIVE_DST;
|
|
}
|
|
|
|
/* set the new states */
|
|
if(state & DS_DISABLED_DST)
|
|
{
|
|
idx->dlist[i].flags |= DS_DISABLED_DST;
|
|
} else {
|
|
idx->dlist[i].flags |= state;
|
|
}
|
|
|
|
if(state & DS_TRYING_DST)
|
|
{
|
|
idx->dlist[i].message_count++;
|
|
/* Destination is not replying.. Increasing failure counter */
|
|
if (idx->dlist[i].message_count >= probing_threshold)
|
|
{
|
|
/* Destionation has too much lost messages.. Bringing it to inactive state */
|
|
idx->dlist[i].flags &= ~DS_TRYING_DST;
|
|
idx->dlist[i].flags |= DS_INACTIVE_DST;
|
|
idx->dlist[i].message_count = 0;
|
|
}
|
|
} else {
|
|
if(!(init_state & DS_TRYING_DST) && (old_state & DS_INACTIVE_DST)){
|
|
idx->dlist[i].message_count++;
|
|
/* Destination was inactive but it is just replying.. Increasing successful counter */
|
|
if (idx->dlist[i].message_count < inactive_threshold)
|
|
{
|
|
/* Destination has not enough successful replies.. Leaving it into inactive state */
|
|
idx->dlist[i].flags |= DS_INACTIVE_DST;
|
|
}else{
|
|
/* Destination has enough replied messages.. Bringing it to active state */
|
|
idx->dlist[i].message_count = 0;
|
|
}
|
|
}else{
|
|
idx->dlist[i].message_count = 0;
|
|
}
|
|
}
|
|
|
|
if (!ds_skip_dst(old_state) && ds_skip_dst(idx->dlist[i].flags))
|
|
{
|
|
ds_run_route(msg, address, "dispatcher:dst-down");
|
|
|
|
} else {
|
|
if(ds_skip_dst(old_state) && !ds_skip_dst(idx->dlist[i].flags))
|
|
ds_run_route(msg, address, "dispatcher:dst-up");
|
|
}
|
|
if (idx->dlist[i].attrs.rweight > 0)
|
|
ds_reinit_rweight_on_state_change(old_state, idx->dlist[i].flags, idx);
|
|
|
|
return 0;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static void ds_run_route(sip_msg_t *msg, str *uri, char *route)
|
|
{
|
|
int rt, backup_rt;
|
|
struct run_act_ctx ctx;
|
|
sip_msg_t *fmsg;
|
|
|
|
if (route == NULL)
|
|
{
|
|
LM_ERR("bad route\n");
|
|
return;
|
|
}
|
|
|
|
LM_DBG("ds_run_route event_route[%s]\n", route);
|
|
|
|
rt = route_get(&event_rt, route);
|
|
if (rt < 0 || event_rt.rlist[rt] == NULL)
|
|
{
|
|
LM_DBG("route does not exist");
|
|
return;
|
|
}
|
|
|
|
if(msg==NULL)
|
|
{
|
|
if (faked_msg_init() < 0)
|
|
{
|
|
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;
|
|
}
|
|
|
|
backup_rt = get_route_type();
|
|
set_route_type(REQUEST_ROUTE);
|
|
init_run_actions_ctx(&ctx);
|
|
run_top_route(event_rt.rlist[rt], fmsg, 0);
|
|
set_route_type(backup_rt);
|
|
}
|
|
|
|
|
|
/**
|
|
* recalculate relative states if some destination state was changed
|
|
*/
|
|
int ds_reinit_rweight_on_state_change(int old_state, int new_state, ds_set_t *dset)
|
|
{
|
|
if (dset == NULL){
|
|
LM_ERR("destination set is null\n");
|
|
return -1;
|
|
}
|
|
if ( (!ds_skip_dst(old_state) && ds_skip_dst(new_state)) ||
|
|
(ds_skip_dst(old_state) && !ds_skip_dst(new_state)) )
|
|
{
|
|
dp_init_relative_weights(dset);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
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)
|
|
{
|
|
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 address [%d : %.*s] not found\n", group,
|
|
address->len, address->s);
|
|
return -1;
|
|
}
|
|
/**
|
|
*
|
|
*/
|
|
int ds_print_list(FILE *fout)
|
|
{
|
|
int j;
|
|
ds_set_t *list;
|
|
|
|
if(_ds_list==NULL || _ds_list_nr<=0)
|
|
{
|
|
LM_ERR("no destination sets\n");
|
|
return -1;
|
|
}
|
|
|
|
fprintf(fout, "\nnumber of destination sets: %d\n", _ds_list_nr);
|
|
|
|
for(list = _ds_list; list!= NULL; list= list->next)
|
|
{
|
|
for(j=0; j<list->nr; j++)
|
|
{
|
|
fprintf(fout, "\n set #%d\n", list->id);
|
|
|
|
if (list->dlist[j].flags&DS_DISABLED_DST)
|
|
fprintf(fout, " Disabled ");
|
|
else if (list->dlist[j].flags&DS_INACTIVE_DST)
|
|
fprintf(fout, " Inactive ");
|
|
else if (list->dlist[j].flags&DS_TRYING_DST) {
|
|
fprintf(fout, " Trying");
|
|
/* print the tries for this host. */
|
|
if (list->dlist[j].message_count > 0) {
|
|
fprintf(fout, " (Fail %d/%d)",
|
|
list->dlist[j].message_count,
|
|
probing_threshold);
|
|
} else {
|
|
fprintf(fout, " ");
|
|
}
|
|
|
|
} else {
|
|
fprintf(fout, " Active ");
|
|
}
|
|
if (list->dlist[j].flags&DS_PROBING_DST)
|
|
fprintf(fout, "(P)");
|
|
else
|
|
fprintf(fout, "(*)");
|
|
|
|
fprintf(fout, " %.*s\n",
|
|
list->dlist[j].uri.len, list->dlist[j].uri.s);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Checks, if the request (sip_msg *_m) comes from a host in a group
|
|
* (group-id or -1 for all groups)
|
|
*/
|
|
int ds_is_addr_from_list(sip_msg_t *_m, int group, str *uri, int mode)
|
|
{
|
|
pv_value_t val;
|
|
ds_set_t *list;
|
|
int j;
|
|
struct ip_addr* pipaddr;
|
|
struct ip_addr aipaddr;
|
|
unsigned short tport;
|
|
unsigned short tproto;
|
|
sip_uri_t puri;
|
|
static char hn[256];
|
|
struct hostent* he;
|
|
|
|
memset(&val, 0, sizeof(pv_value_t));
|
|
val.flags = PV_VAL_INT|PV_TYPE_INT;
|
|
|
|
if(uri==NULL || uri->len<=0) {
|
|
pipaddr = &_m->rcv.src_ip;
|
|
tport = _m->rcv.src_port;
|
|
tproto = _m->rcv.proto;
|
|
} else {
|
|
if(parse_uri(uri->s, uri->len, &puri)!=0 || puri.host.len>255) {
|
|
LM_ERR("bad uri [%.*s]\n", uri->len, uri->s);
|
|
return -1;
|
|
}
|
|
strncpy(hn, puri.host.s, puri.host.len);
|
|
hn[puri.host.len]='\0';
|
|
|
|
he=resolvehost(hn);
|
|
if (he==0) {
|
|
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;
|
|
}
|
|
|
|
for(list = _ds_list; list!= NULL; list= list->next)
|
|
{
|
|
// LM_ERR("list id: %d (n: %d)\n", list->id, list->nr);
|
|
if ((group == -1) || (group == list->id))
|
|
{
|
|
for(j=0; j<list->nr; j++)
|
|
{
|
|
// LM_ERR("port no: %d (%d)\n", list->dlist[j].port, j);
|
|
if (ip_addr_cmp(pipaddr, &list->dlist[j].ip_address)
|
|
&& ((mode&DS_MATCH_NOPORT) || list->dlist[j].port==0
|
|
|| tport == list->dlist[j].port)
|
|
&& ((mode&DS_MATCH_NOPROTO)
|
|
|| tproto == list->dlist[j].proto))
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
if(ds_attrs_pvname.s!=0 && list->dlist[j].attrs.body.len>0)
|
|
{
|
|
memset(&val, 0, sizeof(pv_value_t));
|
|
val.flags = PV_VAL_STR;
|
|
val.rs = list->dlist[j].attrs.body;
|
|
if(ds_attrs_pv.setf(_m, &ds_attrs_pv.pvp,
|
|
(int)EQ_T, &val)<0)
|
|
{
|
|
LM_ERR("setting attrs pv failed\n");
|
|
return -3;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int ds_is_from_list(struct sip_msg *_m, int group)
|
|
{
|
|
return ds_is_addr_from_list(_m, group, NULL, DS_MATCH_NOPROTO);
|
|
}
|
|
|
|
int ds_print_mi_list(struct mi_node* rpl)
|
|
{
|
|
int len, j;
|
|
char* p;
|
|
char c[3];
|
|
str data;
|
|
ds_set_t *list;
|
|
struct mi_node* node = NULL;
|
|
struct mi_node* set_node = NULL;
|
|
struct mi_attr* attr = NULL;
|
|
|
|
if(_ds_list==NULL || _ds_list_nr<=0)
|
|
{
|
|
LM_ERR("no destination sets\n");
|
|
return 0;
|
|
}
|
|
|
|
p= int2str(_ds_list_nr, &len);
|
|
node = add_mi_node_child(rpl, MI_DUP_VALUE, "SET_NO",6, p, len);
|
|
if(node== NULL)
|
|
return -1;
|
|
|
|
for(list = _ds_list; list!= NULL; list= list->next)
|
|
{
|
|
p = int2str(list->id, &len);
|
|
set_node= add_mi_node_child(rpl, MI_DUP_VALUE,"SET", 3, p, len);
|
|
if(set_node == NULL)
|
|
return -1;
|
|
|
|
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;
|
|
|
|
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 if (list->dlist[j].flags & DS_TRYING_DST)
|
|
c[0] = 'T';
|
|
else
|
|
c[0] = 'A';
|
|
|
|
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)
|
|
return -1;
|
|
|
|
data.s = int2str(list->dlist[j].priority, &data.len);
|
|
attr = add_mi_attr (node, MI_DUP_VALUE, "priority", 8,
|
|
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;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*! \brief
|
|
* 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, ...)
|
|
*/
|
|
static void ds_options_callback( struct cell *t, int type,
|
|
struct tmcb_params *ps )
|
|
{
|
|
int group = 0;
|
|
str uri = {0, 0};
|
|
sip_msg_t *fmsg;
|
|
int state;
|
|
|
|
/* The param contains the group, in which the failed host
|
|
* can be found.*/
|
|
if (ps->param==NULL)
|
|
{
|
|
LM_DBG("No parameter provided, OPTIONS-Request was finished"
|
|
" with code %d\n", ps->code);
|
|
return;
|
|
}
|
|
|
|
fmsg = NULL;
|
|
|
|
/* The param is a (void*) Pointer, so we need to dereference it and
|
|
* cast it to an int. */
|
|
group = (int)(long)(*ps->param);
|
|
/* The SIP-URI is taken from the Transaction.
|
|
* 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;
|
|
LM_DBG("OPTIONS-Request was finished with code %d (to %.*s, group %d)\n",
|
|
ps->code, uri.len, uri.s, group);
|
|
/* ps->code contains the result-code of the request.
|
|
*
|
|
* We accept both a "200 OK" or the configured reply as a valid response */
|
|
if((ps->code>=200 && ps->code<=299) || ds_ping_check_rplcode(ps->code))
|
|
{
|
|
/* Set the according entry back to "Active" */
|
|
state = 0;
|
|
if (ds_probing_mode==DS_PROBE_ALL ||
|
|
((ds_probing_mode==DS_PROBE_ONLYFLAGGED)
|
|
&& (ds_get_state(group, &uri) & DS_PROBING_DST)))
|
|
state |= DS_PROBING_DST;
|
|
|
|
/* Check if in the meantime someone disabled the target through RPC or MI */
|
|
if (!(ds_get_state(group, &uri) & DS_DISABLED_DST)
|
|
&& ds_update_state(fmsg, group, &uri, state) != 0)
|
|
{
|
|
LM_ERR("Setting the state failed (%.*s, group %d)\n", uri.len,
|
|
uri.s, group);
|
|
}
|
|
} else {
|
|
state = DS_TRYING_DST;
|
|
if (ds_probing_mode!=DS_PROBE_NONE)
|
|
state |= DS_PROBING_DST;
|
|
/* Check if in the meantime someone disabled the target through RPC or MI */
|
|
if (!(ds_get_state(group, &uri) & DS_DISABLED_DST)
|
|
&& ds_update_state(fmsg, group, &uri, state) != 0)
|
|
{
|
|
LM_ERR("Setting the probing state failed (%.*s, group %d)\n",
|
|
uri.len, uri.s, group);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*! \brief
|
|
* Timer for checking probing destinations
|
|
*
|
|
* This timer is regularly fired.
|
|
*/
|
|
void ds_check_timer(unsigned int ticks, void* param)
|
|
{
|
|
int j;
|
|
ds_set_t *list;
|
|
uac_req_t uac_r;
|
|
|
|
/* Check for the list. */
|
|
if(_ds_list==NULL || _ds_list_nr<=0)
|
|
{
|
|
LM_DBG("no destination sets\n");
|
|
return;
|
|
}
|
|
|
|
if(_ds_ping_active!=NULL && *_ds_ping_active==0)
|
|
{
|
|
LM_DBG("pinging destinations is inactive by admin\n");
|
|
return;
|
|
}
|
|
/* Iterate over the groups and the entries of each group: */
|
|
for(list = _ds_list; list!= NULL; list= list->next)
|
|
{
|
|
for(j=0; j<list->nr; j++)
|
|
{
|
|
/* skip addresses set in disabled state by admin */
|
|
if((list->dlist[j].flags&DS_DISABLED_DST) != 0)
|
|
continue;
|
|
/* If the Flag of the entry has "Probing set, send a probe: */
|
|
if (ds_probing_mode==DS_PROBE_ALL ||
|
|
(list->dlist[j].flags&DS_PROBING_DST) != 0)
|
|
{
|
|
LM_DBG("probing set #%d, URI %.*s\n", list->id,
|
|
list->dlist[j].uri.len, list->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); */
|
|
set_uac_req(&uac_r, &ds_ping_method, 0, 0, 0,
|
|
TMCB_LOCAL_COMPLETED, ds_options_callback,
|
|
(void*)(long)list->id);
|
|
if (list->dlist[j].attrs.socket.s != NULL && list->dlist[j].attrs.socket.len > 0) {
|
|
uac_r.ssock = &list->dlist[j].attrs.socket;
|
|
} else if (ds_default_socket.s != NULL && ds_default_socket.len > 0) {
|
|
uac_r.ssock = &ds_default_socket;
|
|
}
|
|
if (tmb.t_request(&uac_r,
|
|
&list->dlist[j].uri,
|
|
&list->dlist[j].uri,
|
|
&ds_ping_from,
|
|
&ds_outbound_proxy) < 0) {
|
|
LM_ERR("unable to ping [%.*s]\n",
|
|
list->dlist[j].uri.len, list->dlist[j].uri.s);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*! \brief
|
|
* Timer for checking expired items in call load dispatching
|
|
*
|
|
* This timer is regularly fired.
|
|
*/
|
|
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;
|
|
if((it->expire!=0 && it->expire<now)
|
|
|| (it->state==DS_LOAD_INIT
|
|
&& it->initexpire!=0 && it->initexpire<now))
|
|
{
|
|
/* 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--;
|
|
|
|
/* execute ds unload callback */
|
|
ds_load_remove_byid(it->dset, &it->duid);
|
|
|
|
ds_cell_free(it);
|
|
}
|
|
it = it0;
|
|
}
|
|
lock_release(&_dsht_load->entries[i].lock);
|
|
}
|
|
return;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
ds_set_t *ds_get_list(void)
|
|
{
|
|
return _ds_list;
|
|
}
|
|
|
|
int ds_get_list_nr(void)
|
|
{
|
|
return _ds_list_nr;
|
|
}
|