src/modules/dispatcher/dispatcher.c
31ccf6a2
 /**
fb6f8e11
  * dispatcher module - load balancing
31ccf6a2
  *
ba07eb39
  * Copyright (C) 2004-2005 FhG Fokus
  * Copyright (C) 2006 Voice Sistem SRL
fb6f8e11
  * Copyright (C) 2015 Daniel-Constantin Mierla (asipto.com)
31ccf6a2
  *
27642a08
  * This file is part of Kamailio, a free SIP server.
31ccf6a2
  *
27642a08
  * Kamailio is free software; you can redistribute it and/or modify
31ccf6a2
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version
  *
27642a08
  * Kamailio is distributed in the hope that it will be useful,
31ccf6a2
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
2a605383
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
31ccf6a2
  */
 
aecdbe9b
 /*! \file
  * \ingroup dispatcher
  * \brief Dispatcher :: Dispatch
  */
 
 /*! \defgroup dispatcher Dispatcher :: Load balancing and failover module
2a605383
  * 	The dispatcher module implements a set of functions for distributing SIP requests on a
aecdbe9b
  *	set of servers, but also grouping of server resources.
  *
  *	- The module has an internal API exposed to other modules.
  *	- The module implements a couple of MI functions for managing the list of server resources
  */
 
31ccf6a2
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <sys/types.h>
 #include <unistd.h>
 
cf83221d
 #include "../../core/sr_module.h"
 #include "../../core/dprint.h"
 #include "../../core/error.h"
 #include "../../core/ut.h"
 #include "../../core/route.h"
 #include "../../core/timer_proc.h"
 #include "../../core/mem/mem.h"
 #include "../../core/mod_fix.h"
 #include "../../core/rpc.h"
 #include "../../core/rpc_lookup.h"
7b691e97
 #include "../../core/kemi.h"
31ccf6a2
 
59c31fe7
 #include "ds_ht.h"
31ccf6a2
 #include "dispatch.h"
ec903da3
 #include "config.h"
86e03df9
 #include "api.h"
31ccf6a2
 
 MODULE_VERSION
 
d081ee4e
 /* clang-format off */
9de77c2b
 #define DS_SET_ID_COL			"setid"
 #define DS_DEST_URI_COL			"destination"
 #define DS_DEST_FLAGS_COL		"flags"
 #define DS_DEST_PRIORITY_COL	"priority"
65160302
 #define DS_DEST_ATTRS_COL		"attrs"
9de77c2b
 #define DS_TABLE_NAME			"dispatcher"
101e9af4
 
31ccf6a2
 /** parameters */
 char *dslistfile = CFG_DIR"dispatcher.list";
9a5bc6b1
 int  ds_force_dst   = 1;
2a605383
 int  ds_flags       = 0;
 int  ds_use_default = 0;
11ff4226
 str ds_xavp_dst = str_init("_dsdst_");
 int ds_xavp_dst_mode = 0;
 str ds_xavp_ctx = str_init("_dsctx_");
 int ds_xavp_ctx_mode = 0;
 
996f50eb
 str hash_pvar_param = STR_NULL;
095ab21d
 
11ff4226
 str ds_xavp_dst_addr = str_init("uri");
 str ds_xavp_dst_grp = str_init("grp");
 str ds_xavp_dst_dstid = str_init("dstid");
 str ds_xavp_dst_attrs = str_init("attrs");
 str ds_xavp_dst_sock = str_init("sock");
8507efe8
 str ds_xavp_dst_socket = str_init("socket");
80930085
 str ds_xavp_dst_sockname = str_init("sockname");
11ff4226
 
 str ds_xavp_ctx_cnt = str_init("cnt");
 
8cdef0e8
 
e26079d5
 pv_elem_t * hash_param_model = NULL;
095ab21d
 
f0d64282
 int probing_threshold = 1; /* number of failed requests, before a destination
1d401e0a
 							* is taken into probing */
49cca9c3
 int inactive_threshold = 1; /* number of replied requests, before a destination
1d401e0a
 							 * is taken into back in active state */
15d1022b
 str ds_ping_method = str_init("OPTIONS");
 str ds_ping_from   = str_init("sip:dispatcher@localhost");
101e9af4
 static int ds_ping_interval = 0;
12a25ed7
 int ds_ping_latency_stats = 0;
 int ds_latency_estimator_alpha_i = 900;
 float ds_latency_estimator_alpha = 0.9f;
87f170b2
 int ds_probing_mode = DS_PROBE_NONE;
ec903da3
 
996f50eb
 static str ds_ping_reply_codes_str= STR_NULL;
ec903da3
 static int** ds_ping_reply_codes = NULL;
 static int* ds_ping_reply_codes_cnt;
 
87f170b2
 str ds_default_socket = STR_NULL;
 str ds_default_sockname = STR_NULL;
aae77585
 struct socket_info * ds_default_sockinfo = NULL;
 
59c31fe7
 int ds_hash_size = 0;
 int ds_hash_expire = 7200;
9cdc3e70
 int ds_hash_initexpire = 7200;
 int ds_hash_check_interval = 30;
77a2fae7
 int ds_timer_mode = 0;
d1cb2644
 int ds_attrs_none = 0;
fa96c3a6
 int ds_load_mode = 0;
101e9af4
 
996f50eb
 str ds_outbound_proxy = STR_NULL;
a201a338
 
2e561eca
 /* tm */
 struct tm_binds tmb;
 
101e9af4
 /*db */
996f50eb
 str ds_db_url            = STR_NULL;
9de77c2b
 str ds_set_id_col        = str_init(DS_SET_ID_COL);
 str ds_dest_uri_col      = str_init(DS_DEST_URI_COL);
 str ds_dest_flags_col    = str_init(DS_DEST_FLAGS_COL);
 str ds_dest_priority_col = str_init(DS_DEST_PRIORITY_COL);
65160302
 str ds_dest_attrs_col    = str_init(DS_DEST_ATTRS_COL);
9de77c2b
 str ds_table_name        = str_init(DS_TABLE_NAME);
101e9af4
 
996f50eb
 str ds_setid_pvname   = STR_NULL;
b14bb7d7
 pv_spec_t ds_setid_pv;
996f50eb
 str ds_attrs_pvname   = STR_NULL;
09a7a4cd
 pv_spec_t ds_attrs_pv;
31ccf6a2
 
6aa21738
 str ds_event_callback = STR_NULL;
433577dd
 str ds_db_extra_attrs = STR_NULL;
 param_t *ds_db_extra_attrs_list = NULL;
6aa21738
 
2b032137
 static int ds_reload_delta = 5;
 static time_t *ds_rpc_reload_time = NULL;
 
31ccf6a2
 /** module functions */
 static int mod_init(void);
 static int child_init(int);
 
ec903da3
 static int ds_parse_reply_codes();
d997ecfc
 static int ds_init_rpc(void);
ec903da3
 
53a55967
 static int w_ds_select(sip_msg_t*, char*, char*);
 static int w_ds_select_limit(sip_msg_t*, char*, char*, char*);
31ccf6a2
 static int w_ds_select_dst(struct sip_msg*, char*, char*);
15d1022b
 static int w_ds_select_dst_limit(struct sip_msg*, char*, char*, char*);
44f3472f
 static int w_ds_select_domain(struct sip_msg*, char*, char*);
15d1022b
 static int w_ds_select_domain_limit(struct sip_msg*, char*, char*, char*);
3933a64c
 static int w_ds_select_routes(sip_msg_t*, char*, char*);
 static int w_ds_select_routes_limit(sip_msg_t*, char*, char*, char*);
a4ba0359
 static int w_ds_next_dst(struct sip_msg*, char*, char*);
 static int w_ds_next_domain(struct sip_msg*, char*, char*);
b55b6fa1
 static int w_ds_set_dst(struct sip_msg*, char*, char*);
 static int w_ds_set_domain(struct sip_msg*, char*, char*);
a4ba0359
 static int w_ds_mark_dst0(struct sip_msg*, char*, char*);
 static int w_ds_mark_dst1(struct sip_msg*, char*, char*);
9cdc3e70
 static int w_ds_load_unset(struct sip_msg*, char*, char*);
 static int w_ds_load_update(struct sip_msg*, char*, char*);
31ccf6a2
 
081b5d4e
 static int w_ds_is_from_list0(struct sip_msg*, char*, char*);
 static int w_ds_is_from_list1(struct sip_msg*, char*, char*);
121cb0f4
 static int w_ds_is_from_list2(struct sip_msg*, char*, char*);
6016958d
 static int w_ds_is_from_list3(struct sip_msg*, char*, char*, char*);
55ed25e1
 static int w_ds_list_exist(struct sip_msg*, char*, char*);
 static int w_ds_reload(struct sip_msg* msg, char*, char*);
bef88a0d
 
d574a210
 static int w_ds_is_active(sip_msg_t *msg, char *pset, char *p2);
 static int w_ds_is_active_uri(sip_msg_t *msg, char *pset, char *puri);
 
121cb0f4
 static int fixup_ds_is_from_list(void** param, int param_no);
bef88a0d
 static int fixup_ds_list_exist(void** param,int param_no);
081b5d4e
 
e2cf6343
 static void destroy(void);
31ccf6a2
 
ff4dd78e
 static int ds_warn_fixup(void** param, int param_no);
31ccf6a2
 
9d59f5cb
 static int pv_get_dsv(sip_msg_t *msg, pv_param_t *param, pv_value_t *res);
 static int pv_parse_dsv(pv_spec_p sp, str *in);
 
 static pv_export_t mod_pvs[] = {
 	{ {"dsv", (sizeof("dsv")-1)}, PVT_OTHER, pv_get_dsv, 0,
 		pv_parse_dsv, 0, 0, 0 },
 
 	{ {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
 };
 
31ccf6a2
 static cmd_export_t cmds[]={
53a55967
 	{"ds_select",    (cmd_function)w_ds_select,            2,
 		fixup_igp_igp, 0, ANY_ROUTE},
 	{"ds_select",    (cmd_function)w_ds_select_limit,      3,
 		fixup_igp_all, 0, REQUEST_ROUTE|FAILURE_ROUTE},
9cdc3e70
 	{"ds_select_dst",    (cmd_function)w_ds_select_dst,    2,
 		fixup_igp_igp, 0, REQUEST_ROUTE|FAILURE_ROUTE},
76473abd
 	{"ds_select_dst",    (cmd_function)w_ds_select_dst_limit,    3,
53a55967
 		fixup_igp_all, 0, REQUEST_ROUTE|FAILURE_ROUTE},
9cdc3e70
 	{"ds_select_domain", (cmd_function)w_ds_select_domain, 2,
 		fixup_igp_igp, 0, REQUEST_ROUTE|FAILURE_ROUTE},
76473abd
 	{"ds_select_domain", (cmd_function)w_ds_select_domain_limit, 3,
53a55967
 		fixup_igp_all, 0, REQUEST_ROUTE|FAILURE_ROUTE},
3933a64c
 	{"ds_select_routes", (cmd_function)w_ds_select_routes, 2,
 		fixup_spve_spve, 0, REQUEST_ROUTE|FAILURE_ROUTE},
 	{"ds_select_routes", (cmd_function)w_ds_select_routes_limit, 3,
 		fixup_spve_spve_igp, 0, REQUEST_ROUTE|FAILURE_ROUTE},
9cdc3e70
 	{"ds_next_dst",      (cmd_function)w_ds_next_dst,      0,
 		ds_warn_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE},
 	{"ds_next_domain",   (cmd_function)w_ds_next_domain,   0,
 		ds_warn_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE},
b55b6fa1
 	{"ds_set_dst",       (cmd_function)w_ds_set_dst,      0,
 		ds_warn_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE},
 	{"ds_set_domain",    (cmd_function)w_ds_set_domain,   0,
 		ds_warn_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE},
9cdc3e70
 	{"ds_mark_dst",      (cmd_function)w_ds_mark_dst0,     0,
e68230dd
 		ds_warn_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE},
9cdc3e70
 	{"ds_mark_dst",      (cmd_function)w_ds_mark_dst1,     1,
e68230dd
 		ds_warn_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE},
9cdc3e70
 	{"ds_is_from_list",  (cmd_function)w_ds_is_from_list0, 0,
 		0, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE},
 	{"ds_is_from_list",  (cmd_function)w_ds_is_from_list1, 1,
9a25e712
 		fixup_igp_null, 0, ANY_ROUTE},
121cb0f4
 	{"ds_is_from_list",  (cmd_function)w_ds_is_from_list2, 2,
 		fixup_ds_is_from_list, 0, ANY_ROUTE},
6016958d
 	{"ds_is_from_list",  (cmd_function)w_ds_is_from_list3, 3,
121cb0f4
 		fixup_ds_is_from_list, 0, ANY_ROUTE},
bef88a0d
 	{"ds_list_exist",  (cmd_function)w_ds_list_exist, 1,
 		fixup_ds_list_exist, 0, ANY_ROUTE},
39295cbb
 	{"ds_list_exists",  (cmd_function)w_ds_list_exist, 1,
 		fixup_ds_list_exist, 0, ANY_ROUTE},
9cdc3e70
 	{"ds_load_unset",    (cmd_function)w_ds_load_unset,   0,
 		0, 0, ANY_ROUTE},
 	{"ds_load_update",   (cmd_function)w_ds_load_update,  0,
 		0, 0, ANY_ROUTE},
d574a210
 	{"ds_is_active",  (cmd_function)w_ds_is_active, 1,
 		fixup_igp_null, fixup_free_igp_null, ANY_ROUTE},
 	{"ds_is_active",  (cmd_function)w_ds_is_active_uri, 2,
 		fixup_igp_spve, fixup_free_igp_spve, ANY_ROUTE},
86e03df9
 	{"bind_dispatcher",   (cmd_function)bind_dispatcher,  0,
 		0, 0, 0},
7cf474fd
 	{"ds_reload", (cmd_function)w_ds_reload, 0,
 		0, 0, ANY_ROUTE},
80998a7f
 	{0,0,0,0,0,0}
31ccf6a2
 };
 
 
 static param_export_t params[]={
397364ed
 	{"list_file",       PARAM_STRING, &dslistfile},
 	{"db_url",		    PARAM_STR, &ds_db_url},
 	{"table_name", 	    PARAM_STR, &ds_table_name},
 	{"setid_col",       PARAM_STR, &ds_set_id_col},
 	{"destination_col", PARAM_STR, &ds_dest_uri_col},
 	{"flags_col",       PARAM_STR, &ds_dest_flags_col},
 	{"priority_col",    PARAM_STR, &ds_dest_priority_col},
 	{"attrs_col",       PARAM_STR, &ds_dest_attrs_col},
101e9af4
 	{"force_dst",       INT_PARAM, &ds_force_dst},
 	{"flags",           INT_PARAM, &ds_flags},
 	{"use_default",     INT_PARAM, &ds_use_default},
11ff4226
 	{"xavp_dst",        PARAM_STR, &ds_xavp_dst},
 	{"xavp_dst_mode",   PARAM_INT, &ds_xavp_dst_mode},
 	{"xavp_ctx",        PARAM_STR, &ds_xavp_ctx},
 	{"xavp_ctx_mode",   PARAM_INT, &ds_xavp_ctx_mode},
397364ed
 	{"hash_pvar",       PARAM_STR, &hash_pvar_param},
 	{"setid_pvname",    PARAM_STR, &ds_setid_pvname},
 	{"attrs_pvname",    PARAM_STR, &ds_attrs_pvname},
f0d64282
 	{"ds_probing_threshold", INT_PARAM, &probing_threshold},
49cca9c3
 	{"ds_inactive_threshold", INT_PARAM, &inactive_threshold},
397364ed
 	{"ds_ping_method",     PARAM_STR, &ds_ping_method},
 	{"ds_ping_from",       PARAM_STR, &ds_ping_from},
081b5d4e
 	{"ds_ping_interval",   INT_PARAM, &ds_ping_interval},
12a25ed7
 	{"ds_ping_latency_stats", INT_PARAM, &ds_ping_latency_stats},
 	{"ds_latency_estimator_alpha", INT_PARAM, &ds_latency_estimator_alpha_i},
397364ed
 	{"ds_ping_reply_codes", PARAM_STR, &ds_ping_reply_codes_str},
d1de0cc5
 	{"ds_probing_mode",    INT_PARAM, &ds_probing_mode},
9cdc3e70
 	{"ds_hash_size",       INT_PARAM, &ds_hash_size},
 	{"ds_hash_expire",     INT_PARAM, &ds_hash_expire},
 	{"ds_hash_initexpire", INT_PARAM, &ds_hash_initexpire},
 	{"ds_hash_check_interval", INT_PARAM, &ds_hash_check_interval},
433577dd
 	{"outbound_proxy",     PARAM_STR, &ds_outbound_proxy},
aae77585
 	{"ds_default_socket",  PARAM_STR, &ds_default_socket},
87f170b2
 	{"ds_default_sockname",PARAM_STR, &ds_default_sockname},
77a2fae7
 	{"ds_timer_mode",      PARAM_INT, &ds_timer_mode},
6aa21738
 	{"event_callback",     PARAM_STR, &ds_event_callback},
d1cb2644
 	{"ds_attrs_none",      PARAM_INT, &ds_attrs_none},
433577dd
 	{"ds_db_extra_attrs",  PARAM_STR, &ds_db_extra_attrs},
fa96c3a6
 	{"ds_load_mode",       PARAM_INT, &ds_load_mode},
2b032137
 	{"reload_delta",       PARAM_INT, &ds_reload_delta },
31ccf6a2
 	{0,0,0}
 };
 
 
 /** module exports */
 struct module_exports exports= {
118f5840
 	"dispatcher",    /* module name */
51716422
 	DEFAULT_DLFLAGS, /* dlopen flags */
118f5840
 	cmds,            /* cmd (cfg function) exports */
 	params,          /* param exports */
 	0,               /* exported rpc functions */
9d59f5cb
 	mod_pvs,         /* exported pseudo-variables */
118f5840
 	0,               /* response handling function */
 	mod_init,        /* module init function */
 	child_init,      /* per-child init function */
 	destroy          /* module destroy function */
31ccf6a2
 };
d081ee4e
 /* clang-format on */
31ccf6a2
 
 /**
  * init module function
  */
 static int mod_init(void)
 {
aae77585
 	str host;
 	int port, proto;
433577dd
 	param_hooks_t phooks;
 	param_t *pit = NULL;
c7d56382
 
d081ee4e
 	if(ds_ping_active_init() < 0) {
44c5d1c0
 		return -1;
 	}
 
d081ee4e
 	if(ds_init_rpc() < 0) {
d997ecfc
 		LM_ERR("failed to register RPC commands\n");
 		return -1;
 	}
9bdaf1a8
 
d081ee4e
 	if(cfg_declare("dispatcher", dispatcher_cfg_def, &default_dispatcher_cfg,
 			   cfg_sizeof(dispatcher), &dispatcher_cfg)) {
ed55e6f5
 		LM_ERR("Fail to declare the configuration\n");
 		return -1;
 	}
 
ec903da3
 	/* Initialize the counter */
d081ee4e
 	ds_ping_reply_codes = (int **)shm_malloc(sizeof(unsigned int *));
ec903da3
 	*ds_ping_reply_codes = 0;
d081ee4e
 	ds_ping_reply_codes_cnt = (int *)shm_malloc(sizeof(int));
ec903da3
 	*ds_ping_reply_codes_cnt = 0;
 	if(ds_ping_reply_codes_str.s) {
d081ee4e
 		cfg_get(dispatcher, dispatcher_cfg, ds_ping_reply_codes_str) =
 				ds_ping_reply_codes_str;
 		if(ds_parse_reply_codes() < 0) {
ec903da3
 			return -1;
 		}
2a605383
 	}
49cca9c3
 	/* copy threshholds to config */
d081ee4e
 	cfg_get(dispatcher, dispatcher_cfg, probing_threshold) = probing_threshold;
 	cfg_get(dispatcher, dispatcher_cfg, inactive_threshold) =
 			inactive_threshold;
 
87f170b2
 	if(ds_default_sockname.s && ds_default_sockname.len > 0) {
 		ds_default_sockinfo = ksr_get_socket_by_name(&ds_default_sockname);
d081ee4e
 		if(ds_default_sockinfo == 0) {
87f170b2
 			LM_ERR("non-local socket name <%.*s>\n", ds_default_sockname.len,
 					ds_default_sockname.s);
2a605383
 			return -1;
 		}
87f170b2
 		LM_INFO("default dispatcher socket set by name to <%.*s>\n",
 				ds_default_sockname.len, ds_default_sockname.s);
 	} else {
 		if(ds_default_socket.s && ds_default_socket.len > 0) {
 			if(parse_phostport(
 					ds_default_socket.s, &host.s, &host.len, &port, &proto)
 					!= 0) {
 				LM_ERR("bad socket <%.*s>\n", ds_default_socket.len,
 						ds_default_socket.s);
 				return -1;
 			}
 			ds_default_sockinfo =
 					grep_sock_info(&host, (unsigned short)port, proto);
 			if(ds_default_sockinfo == 0) {
 				LM_ERR("non-local socket <%.*s>\n", ds_default_socket.len,
 						ds_default_socket.s);
 				return -1;
 			}
 			LM_INFO("default dispatcher socket set to <%.*s>\n",
 					ds_default_socket.len, ds_default_socket.s);
 		}
2a605383
 	}
ec903da3
 
4e7e355d
 	if(ds_init_data() != 0)
101e9af4
 		return -1;
 
d081ee4e
 	if(ds_db_url.s) {
433577dd
 		if(ds_db_extra_attrs.s!=NULL && ds_db_extra_attrs.len>2) {
 			if(ds_db_extra_attrs.s[ds_db_extra_attrs.len-1]==';') {
 				ds_db_extra_attrs.len--;
 			}
 			if (parse_params(&ds_db_extra_attrs, CLASS_ANY, &phooks,
 						&ds_db_extra_attrs_list)<0) {
 				LM_ERR("failed to parse extra attrs parameter\n");
 				return -1;
 			}
 			for(pit = ds_db_extra_attrs_list; pit!=NULL; pit=pit->next) {
 				if(pit->body.s==NULL || pit->body.len<=0) {
 					LM_ERR("invalid db extra attrs parameter\n");
 					return -1;
 				}
 			}
 		}
4e7e355d
 		if(ds_init_db() != 0) {
c503d21d
 			LM_ERR("could not initiate a connect to the database\n");
101e9af4
 			return -1;
 		}
 	} else {
d081ee4e
 		if(ds_load_list(dslistfile) != 0) {
ac082a22
 			LM_ERR("no dispatching list loaded from file\n");
 			return -1;
 		} else {
 			LM_DBG("loaded dispatching list\n");
 		}
31ccf6a2
 	}
65160302
 
d081ee4e
 	if(hash_pvar_param.s && *hash_pvar_param.s) {
095ab21d
 		if(pv_parse_format(&hash_pvar_param, &hash_param_model) < 0
d081ee4e
 				|| hash_param_model == NULL) {
095ab21d
 			LM_ERR("malformed PV string: %s\n", hash_pvar_param.s);
 			return -1;
2a605383
 		}
095ab21d
 	} else {
 		hash_param_model = NULL;
 	}
cbe2e236
 
d081ee4e
 	if(ds_setid_pvname.s != 0) {
 		if(pv_parse_spec(&ds_setid_pvname, &ds_setid_pv) == NULL
 				|| !pv_is_w(&ds_setid_pv)) {
b14bb7d7
 			LM_ERR("[%s]- invalid setid_pvname\n", ds_setid_pvname.s);
 			return -1;
 		}
 	}
09a7a4cd
 
d081ee4e
 	if(ds_attrs_pvname.s != 0) {
 		if(pv_parse_spec(&ds_attrs_pvname, &ds_attrs_pv) == NULL
 				|| !pv_is_w(&ds_attrs_pv)) {
09a7a4cd
 			LM_ERR("[%s]- invalid attrs_pvname\n", ds_attrs_pvname.s);
 			return -1;
 		}
 	}
 
11ff4226
 	if(ds_hash_size > 0) {
 		if(ds_hash_load_init(
 					1 << ds_hash_size, ds_hash_expire, ds_hash_initexpire)
 				< 0)
 			return -1;
 		if(ds_timer_mode == 1) {
 			if(sr_wtimer_add(ds_ht_timer, NULL, ds_hash_check_interval) < 0)
9cdc3e70
 				return -1;
 		} else {
11ff4226
 			if(register_timer(ds_ht_timer, NULL, ds_hash_check_interval)
 					< 0)
 				return -1;
9cdc3e70
 		}
 	}
11ff4226
 
081b5d4e
 	/* Only, if the Probing-Timer is enabled the TM-API needs to be loaded: */
d081ee4e
 	if(ds_ping_interval > 0) {
081b5d4e
 		/*****************************************************
 		 * TM-Bindings
cbe2e236
 		 *****************************************************/
d081ee4e
 		if(load_tm_api(&tmb) == -1) {
fd5ddfd1
 			LM_ERR("could not load the TM-functions - disable DS ping\n");
 			return -1;
081b5d4e
 		}
fd5ddfd1
 		/*****************************************************
 		 * Register the PING-Timer
 		 *****************************************************/
d081ee4e
 		if(ds_timer_mode == 1) {
 			if(sr_wtimer_add(ds_check_timer, NULL, ds_ping_interval) < 0)
77a2fae7
 				return -1;
 		} else {
d081ee4e
 			if(register_timer(ds_check_timer, NULL, ds_ping_interval) < 0)
77a2fae7
 				return -1;
 		}
081b5d4e
 	}
12a25ed7
 	if (ds_latency_estimator_alpha_i > 0 && ds_latency_estimator_alpha_i < 1000) {
 		ds_latency_estimator_alpha = ds_latency_estimator_alpha_i/1000.0f;
 	} else {
 		LM_ERR("invalid ds_latency_estimator_alpha must be between 0 and 1000,"
 				" using default[%.3f]\n", ds_latency_estimator_alpha);
 	}
2b032137
 
 	ds_rpc_reload_time = shm_malloc(sizeof(time_t));
 	if(ds_rpc_reload_time == NULL) {
 		SHM_MEM_ERROR;
 		return -1;
 	}
 	*ds_rpc_reload_time = 0;
 
31ccf6a2
 	return 0;
 }
 
aecdbe9b
 /*! \brief
f0d64282
  * Initialize children
31ccf6a2
  */
 static int child_init(int rank)
 {
 	return 0;
 }
 
aecdbe9b
 /*! \brief
2af93c2f
  * destroy function
  */
e2cf6343
 static void destroy(void)
2af93c2f
 {
 	ds_destroy_list();
e2cf6343
 	if(ds_db_url.s)
2af93c2f
 		ds_disconnect_db();
9cdc3e70
 	ds_hash_load_destroy();
ec903da3
 	if(ds_ping_reply_codes)
2a605383
 		shm_free(ds_ping_reply_codes);
db15dbfd
 	if(ds_ping_reply_codes_cnt)
 		shm_free(ds_ping_reply_codes_cnt);
2b032137
 	if(ds_rpc_reload_time!=NULL) {
 		shm_free(ds_rpc_reload_time);
 		ds_rpc_reload_time = 0;
 	}
2af93c2f
 }
 
d081ee4e
 #define GET_VALUE(param_name, param, i_value, s_value, value_flags)        \
 	do {                                                                   \
 		if(get_is_fparam(&(i_value), &(s_value), msg, (fparam_t *)(param), \
 				   &(value_flags))                                         \
 				!= 0) {                                                    \
 			LM_ERR("no %s value\n", (param_name));                         \
 			return -1;                                                     \
 		}                                                                  \
 	} while(0)
196c65fd
 
499fb7b7
 /*! \brief
  * parses string to dispatcher dst flags set
  * returns <0 on failure or int with flag on success.
  */
d081ee4e
 int ds_parse_flags(char *flag_str, int flag_len)
499fb7b7
 {
76473abd
 	int flag = 0;
 	int i;
 
d081ee4e
 	for(i = 0; i < flag_len; i++) {
 		if(flag_str[i] == 'a' || flag_str[i] == 'A') {
76473abd
 			flag &= ~(DS_STATES_ALL);
d081ee4e
 		} else if(flag_str[i] == 'i' || flag_str[i] == 'I') {
76473abd
 			flag |= DS_INACTIVE_DST;
d081ee4e
 		} else if(flag_str[i] == 'd' || flag_str[i] == 'D') {
76473abd
 			flag |= DS_DISABLED_DST;
d081ee4e
 		} else if(flag_str[i] == 't' || flag_str[i] == 'T') {
76473abd
 			flag |= DS_TRYING_DST;
d081ee4e
 		} else if(flag_str[i] == 'p' || flag_str[i] == 'P') {
76473abd
 			flag |= DS_PROBING_DST;
 		} else {
 			flag = -1;
 			break;
 		}
 	}
 
 	return flag;
499fb7b7
 }
 
31ccf6a2
 /**
  *
  */
d081ee4e
 static int w_ds_select_addr(
 		sip_msg_t *msg, char *set, char *alg, char *limit, int mode)
31ccf6a2
 {
15d1022b
 	unsigned int algo_flags, set_flags, limit_flags;
 	str s_algo = STR_NULL;
 	str s_set = STR_NULL;
 	str s_limit = STR_NULL;
 	int a, s, l;
d081ee4e
 	if(msg == NULL)
31ccf6a2
 		return -1;
058b9857
 
196c65fd
 	GET_VALUE("destination set", set, s, s_set, set_flags);
d081ee4e
 	if(!(set_flags & PARAM_INT)) {
 		if(set_flags & PARAM_STR)
 			LM_ERR("unable to get destination set from [%.*s]\n", s_set.len,
 					s_set.s);
196c65fd
 		else
 			LM_ERR("unable to get destination set\n");
a4ba0359
 		return -1;
 	}
196c65fd
 	GET_VALUE("algorithm", alg, a, s_algo, algo_flags);
d081ee4e
 	if(!(algo_flags & PARAM_INT)) {
 		if(algo_flags & PARAM_STR)
 			LM_ERR("unable to get algorithm from [%.*s]\n", s_algo.len,
 					s_algo.s);
196c65fd
 		else
 			LM_ERR("unable to get algorithm\n");
a4ba0359
 		return -1;
 	}
31ccf6a2
 
d081ee4e
 	if(limit) {
76473abd
 		GET_VALUE("limit", limit, l, s_limit, limit_flags);
d081ee4e
 		if(!(limit_flags & PARAM_INT)) {
 			if(limit_flags & PARAM_STR)
 				LM_ERR("unable to get dst number limit from [%.*s]\n",
 						s_limit.len, s_limit.s);
76473abd
 			else
 				LM_ERR("unable to get dst number limit\n");
 			return -1;
 		}
 	} else {
 		l = -1; /* will be casted to a rather big unsigned value */
 	}
15d1022b
 
 	return ds_select_dst_limit(msg, s, a, (unsigned int)l, mode);
058b9857
 }
53a55967
 
 /**
  *
  */
d081ee4e
 static int w_ds_select(struct sip_msg *msg, char *set, char *alg)
53a55967
 {
f6960f87
 	return w_ds_select_addr(msg, set, alg, 0 /* limit number of dst*/,
 					DS_SETOP_XAVP /*set no dst/uri*/);
53a55967
 }
 
 /**
  *
  */
d081ee4e
 static int w_ds_select_limit(
 		struct sip_msg *msg, char *set, char *alg, char *limit)
53a55967
 {
 	return w_ds_select_addr(msg, set, alg, limit /* limit number of dst*/,
f6960f87
 			DS_SETOP_XAVP /*set no dst/uri*/);
53a55967
 }
 
058b9857
 /**
  *
  */
d081ee4e
 static int w_ds_select_dst(struct sip_msg *msg, char *set, char *alg)
058b9857
 {
f6960f87
 	return w_ds_select_addr(msg, set, alg, 0 /* limit number of dst*/,
 				DS_SETOP_DSTURI /*set dst uri*/);
15d1022b
 }
 
 /**
  *
  */
d081ee4e
 static int w_ds_select_dst_limit(
 		struct sip_msg *msg, char *set, char *alg, char *limit)
15d1022b
 {
f6960f87
 	return w_ds_select_addr(msg, set, alg, limit /* limit number of dst*/,
 			DS_SETOP_DSTURI /*set dst uri*/);
44f3472f
 }
 
 /**
  *
  */
d081ee4e
 static int w_ds_select_domain(struct sip_msg *msg, char *set, char *alg)
44f3472f
 {
f6960f87
 	return w_ds_select_addr(msg, set, alg, 0 /* limit number of dst*/,
 			DS_SETOP_RURI /*set host port*/);
15d1022b
 }
 
 /**
  *
  */
d081ee4e
 static int w_ds_select_domain_limit(
 		struct sip_msg *msg, char *set, char *alg, char *limit)
15d1022b
 {
f6960f87
 	return w_ds_select_addr(msg, set, alg, limit /* limit number of dst*/,
 			DS_SETOP_RURI /*set host port*/);
a4ba0359
 }
 
3933a64c
 /**
  *
  */
 static int ki_ds_select_routes_limit(sip_msg_t *msg, str *srules, str *smode,
 		int rlimit)
 {
 	int i;
 	int vret;
 	int gret;
 	sr_xval_t nxval;
 	ds_select_state_t vstate;
 
 	memset(&vstate, 0, sizeof(ds_select_state_t));
 	vstate.limit = (uint32_t)rlimit;
 	if(vstate.limit == 0) {
 		LM_DBG("Limit set to 0 - forcing to unlimited\n");
 		vstate.limit = 0xffffffff;
 	}
 	vret = -1;
 	gret = -1;
 	i = 0;
 	while(i<srules->len) {
 		vstate.setid = 0;
 		for(; i<srules->len; i++) {
 			if(srules->s[i]<'0' || srules->s[i]>'9') {
 				if(srules->s[i]=='=') {
 					i++;
 					break;
 				} else {
 					LM_ERR("invalid character in [%.*s] at [%d]\n",
 							srules->len, srules->s, i);
 					return -1;
 				}
 			}
 			vstate.setid = (vstate.setid * 10) + (srules->s[i] - '0');
 		}
 		vstate.alg = 0;
 		for(; i<srules->len; i++) {
 			if(srules->s[i]<'0' || srules->s[i]>'9') {
 				if(srules->s[i]==';') {
 					i++;
 					break;
 				} else {
 					LM_ERR("invalid character in [%.*s] at [%d]\n",
 							srules->len, srules->s, i);
 					return -1;
 				}
 			}
 			vstate.alg = (vstate.alg * 10) + (srules->s[i] - '0');
 		}
 		LM_DBG("routing with setid=%d alg=%d cnt=%d limit=0x%x (%u)\n",
 			vstate.setid, vstate.alg, vstate.cnt, vstate.limit, vstate.limit);
074d3c67
 
3933a64c
 		vstate.umode = DS_SETOP_XAVP;
074d3c67
 		/* if no r-uri/d-uri was set already, keep using the update mode
 		 * specified by the param, then just add to xavps list */
 		if(vstate.emode==0) {
3933a64c
 			switch(smode->s[0]) {
 				case '0':
 				case 'd':
 				case 'D':
 					vstate.umode = DS_SETOP_DSTURI;
 				break;
 				case '1':
 				case 'r':
 				case 'R':
 					vstate.umode = DS_SETOP_RURI;
 				break;
 				case '2':
 				case 'x':
 				case 'X':
 				break;
 				default:
 					LM_ERR("invalid routing mode parameter: %.*s\n",
 							smode->len, smode->s);
 					return -1;
 			}
 		}
 		vret = ds_manage_routes(msg, &vstate);
 		if(vret<0) {
 			LM_DBG("failed to select target destinations from %d=%d [%.*s]\n",
 					vstate.setid, vstate.alg, srules->len, srules->s);
 			/* continue to try other target groups */
 		} else {
 			if(vret>0) {
 				gret = vret;
 			}
 		}
 	}
 
 	if(gret<0) {
 		/* no selection of a target address */
 		LM_DBG("failed to select any target destinations from [%.*s]\n",
 					srules->len, srules->s);
 		/* return last failure code when trying to select target addresses */
 		return vret;
 	}
 
 	/* add cnt value to xavp */
 	if(((ds_xavp_ctx_mode & DS_XAVP_CTX_SKIP_CNT)==0)
 			&& (ds_xavp_ctx.len >= 0)) {
 		/* add to xavp the number of selected dst records */
 		memset(&nxval, 0, sizeof(sr_xval_t));
 		nxval.type = SR_XTYPE_INT;
 		nxval.v.i = vstate.cnt;
 		if(xavp_add_xavp_value(&ds_xavp_ctx, &ds_xavp_ctx_cnt, &nxval, NULL)==NULL) {
 			LM_ERR("failed to add cnt value to xavp\n");
 			return -1;
 		}
 	}
 
 	LM_DBG("selected target destinations: %d\n", vstate.cnt);
 	return gret;
 }
 
 /**
  *
  */
 static int ki_ds_select_routes(sip_msg_t *msg, str *srules, str *smode)
 {
 	return ki_ds_select_routes_limit(msg, srules, smode, 0);
 }
 
 /**
  *
  */
 static int w_ds_select_routes(sip_msg_t *msg, char *lrules, char *umode)
 {
 	return w_ds_select_routes_limit(msg, lrules, umode, 0);
 }
 
 /**
  *
  */
 static int w_ds_select_routes_limit(sip_msg_t *msg, char *lrules, char *umode,
 		char *rlimit)
 {
 	str vrules;
 	str vmode;
 	int vlimit;
 
 	if(fixup_get_svalue(msg, (gparam_t*)lrules, &vrules)<0) {
 		LM_ERR("failed to get routing rules parameter\n");
 		return -1;
 	}
 	if(fixup_get_svalue(msg, (gparam_t*)umode, &vmode)<0) {
 		LM_ERR("failed to get update mode parameter\n");
 		return -1;
 	}
 	if(rlimit!=NULL) {
 		if(fixup_get_ivalue(msg, (gparam_t*)rlimit, &vlimit)<0) {
 			LM_ERR("failed to get limit parameter\n");
 			return -1;
 		}
 	} else {
 		vlimit = 0;
 	}
 	return ki_ds_select_routes_limit(msg, &vrules, &vmode, vlimit);
 }
 
a4ba0359
 /**
  *
  */
 static int w_ds_next_dst(struct sip_msg *msg, char *str1, char *str2)
 {
b55b6fa1
 	return ds_update_dst(msg, DS_USE_NEXT, DS_SETOP_DSTURI /*set dst uri*/);
a4ba0359
 }
 
 /**
  *
  */
 static int w_ds_next_domain(struct sip_msg *msg, char *str1, char *str2)
 {
b55b6fa1
 	return ds_update_dst(msg, DS_USE_NEXT, DS_SETOP_RURI /*set host port*/);
 }
 
 /**
  *
  */
 static int w_ds_set_dst(struct sip_msg *msg, char *str1, char *str2)
 {
 	return ds_update_dst(msg, DS_USE_CRT, DS_SETOP_DSTURI /*set dst uri*/);
 }
 
 /**
  *
  */
 static int w_ds_set_domain(struct sip_msg *msg, char *str1, char *str2)
 {
 	return ds_update_dst(msg, DS_USE_CRT, DS_SETOP_RURI /*set host port*/);
a4ba0359
 }
 
 /**
  *
  */
55ed25e1
 static int ki_ds_mark_dst(sip_msg_t *msg)
a4ba0359
 {
cbe2e236
 	int state;
 
 	state = DS_INACTIVE_DST;
d081ee4e
 	if(ds_probing_mode == DS_PROBE_ALL)
cbe2e236
 		state |= DS_PROBING_DST;
 
 	return ds_mark_dst(msg, state);
a4ba0359
 }
 
 /**
  *
  */
55ed25e1
 static int w_ds_mark_dst0(struct sip_msg *msg, char *str1, char *str2)
 {
 	return ki_ds_mark_dst(msg);
 }
 
 /**
  *
  */
 static int ki_ds_mark_dst_state(sip_msg_t *msg, str *sval)
a4ba0359
 {
cbe2e236
 	int state;
 
55ed25e1
 	if(sval->s == NULL || sval->len == 0)
 		return ki_ds_mark_dst(msg);
cbe2e236
 
55ed25e1
 	state = ds_parse_flags(sval->s, sval->len);
499fb7b7
 
d081ee4e
 	if(state < 0) {
55ed25e1
 		LM_WARN("Failed to parse state flags: %.*s", sval->len, sval->s);
76473abd
 		return -1;
499fb7b7
 	}
cbe2e236
 
 	return ds_mark_dst(msg, state);
31ccf6a2
 }
 
55ed25e1
 /**
  *
  */
 static int w_ds_mark_dst1(struct sip_msg *msg, char *str1, char *str2)
 {
 	str sval;
 
 	sval.s = str1;
 	sval.len = strlen(str1);
 
 	return ki_ds_mark_dst_state(msg, &sval);
 }
9cdc3e70
 
 /**
  *
  */
 static int w_ds_load_unset(struct sip_msg *msg, char *str1, char *str2)
 {
d081ee4e
 	if(ds_load_unset(msg) < 0)
9cdc3e70
 		return -1;
 	return 1;
 }
 
 /**
  *
  */
 static int w_ds_load_update(struct sip_msg *msg, char *str1, char *str2)
 {
d081ee4e
 	if(ds_load_update(msg) < 0)
9cdc3e70
 		return -1;
 	return 1;
 }
 
 /**
  *
  */
d081ee4e
 static int ds_warn_fixup(void **param, int param_no)
ff4dd78e
 {
11ff4226
 	if(ds_xavp_dst.len<=0 || ds_xavp_ctx.len<=0) {
 		LM_ERR("failover functions used, but required XAVP parameters"
d081ee4e
 			   " are NULL -- feature disabled\n");
ff4dd78e
 	}
 	return 0;
 }
ba07eb39
 
55ed25e1
 static int ds_reload(sip_msg_t *msg)
7cf474fd
 {
 	if(!ds_db_url.s) {
d081ee4e
 		if(ds_load_list(dslistfile) != 0)
7cf474fd
 			LM_ERR("Error reloading from list\n");
d081ee4e
 		return -1;
7cf474fd
 	} else {
d081ee4e
 		if(ds_reload_db() < 0)
7cf474fd
 			LM_ERR("Error reloading from db\n");
d081ee4e
 		return -1;
7cf474fd
 	}
7128ffd7
 	LM_DBG("reloaded dispatcher\n");
7cf474fd
 	return 1;
 }
 
081b5d4e
 
55ed25e1
 static int w_ds_reload(struct sip_msg *msg, char *str1, char *str2)
 {
 	return ds_reload(msg);
 }
 
081b5d4e
 static int w_ds_is_from_list0(struct sip_msg *msg, char *str1, char *str2)
 {
 	return ds_is_from_list(msg, -1);
 }
 
55ed25e1
 static int ki_ds_is_from_lists(sip_msg_t *msg)
 {
 	return ds_is_from_list(msg, -1);
 }
081b5d4e
 
 static int w_ds_is_from_list1(struct sip_msg *msg, char *set, char *str2)
 {
9a25e712
 	int s;
d081ee4e
 	if(fixup_get_ivalue(msg, (gparam_p)set, &s) != 0) {
9a25e712
 		LM_ERR("cannot get set id value\n");
 		return -1;
 	}
 	return ds_is_from_list(msg, s);
081b5d4e
 }
ec903da3
 
121cb0f4
 static int w_ds_is_from_list2(struct sip_msg *msg, char *set, char *mode)
6016958d
 {
 	int vset;
 	int vmode;
 
d081ee4e
 	if(fixup_get_ivalue(msg, (gparam_t *)set, &vset) != 0) {
6016958d
 		LM_ERR("cannot get set id value\n");
 		return -1;
 	}
d081ee4e
 	if(fixup_get_ivalue(msg, (gparam_t *)mode, &vmode) != 0) {
121cb0f4
 		LM_ERR("cannot get mode value\n");
 		return -1;
 	}
 
 	return ds_is_addr_from_list(msg, vset, NULL, vmode);
 }
 
55ed25e1
 static int ki_ds_is_from_list_mode(sip_msg_t *msg, int vset, int vmode)
 {
 	return ds_is_addr_from_list(msg, vset, NULL, vmode);
 }
 
d081ee4e
 static int w_ds_is_from_list3(
 		struct sip_msg *msg, char *set, char *mode, char *uri)
121cb0f4
 {
 	int vset;
 	int vmode;
 	str suri;
 
d081ee4e
 	if(fixup_get_ivalue(msg, (gparam_t *)set, &vset) != 0) {
121cb0f4
 		LM_ERR("cannot get set id value\n");
6016958d
 		return -1;
 	}
d081ee4e
 	if(fixup_get_ivalue(msg, (gparam_t *)mode, &vmode) != 0) {
6016958d
 		LM_ERR("cannot get mode value\n");
 		return -1;
 	}
d081ee4e
 	if(fixup_get_svalue(msg, (gparam_t *)uri, &suri) != 0) {
121cb0f4
 		LM_ERR("cannot get uri value\n");
 		return -1;
 	}
6016958d
 
 	return ds_is_addr_from_list(msg, vset, &suri, vmode);
 }
 
55ed25e1
 static int ki_ds_is_from_list_uri(sip_msg_t *msg, int vset, int vmode, str *vuri)
 {
 	return ds_is_addr_from_list(msg, vset, vuri, vmode);
 }
121cb0f4
 
d081ee4e
 static int fixup_ds_is_from_list(void **param, int param_no)
6016958d
 {
d081ee4e
 	if(param_no == 1 || param_no == 2)
6016958d
 		return fixup_igp_null(param, 1);
d081ee4e
 	if(param_no == 3)
6016958d
 		return fixup_spve_null(param, 1);
 	return 0;
 }
 
bef88a0d
 /* Check if a given set exist in memory */
55ed25e1
 static int w_ds_list_exist(struct sip_msg *msg, char *param, char *p2)
bef88a0d
 {
 	int set;
 
d081ee4e
 	if(fixup_get_ivalue(msg, (gparam_p)param, &set) != 0) {
bef88a0d
 		LM_ERR("cannot get set id param value\n");
fcabdc01
 		return -2;
bef88a0d
 	}
 	return ds_list_exist(set);
 }
 
55ed25e1
 static int ki_ds_list_exists(struct sip_msg *msg, int set)
 {
 	return ds_list_exist(set);
 }
 
d081ee4e
 static int fixup_ds_list_exist(void **param, int param_no)
bef88a0d
 {
 	return fixup_igp_null(param, param_no);
 }
 
d574a210
 static int ki_ds_is_active(sip_msg_t *msg, int set)
 {
 	return ds_is_active_uri(msg, set, NULL);
 }
 
 static int w_ds_is_active(sip_msg_t *msg, char *pset, char *p2)
 {
 	int vset;
 
 	if(fixup_get_ivalue(msg, (gparam_t *)pset, &vset) != 0) {
 		LM_ERR("cannot get set id value\n");
 		return -1;
 	}
 
 	return ds_is_active_uri(msg, vset, NULL);
 }
 
 static int ki_ds_is_active_uri(sip_msg_t *msg, int set, str *uri)
 {
 	return ds_is_active_uri(msg, set, uri);
 }
 
 static int w_ds_is_active_uri(sip_msg_t *msg, char *pset, char *puri)
 {
 	int vset;
 	str suri;
 
 	if(fixup_get_ivalue(msg, (gparam_t *)pset, &vset) != 0) {
 		LM_ERR("cannot get set id value\n");
 		return -1;
 	}
 	if(fixup_get_svalue(msg, (gparam_t *)puri, &suri) != 0) {
 		LM_ERR("cannot get uri value\n");
 		return -1;
 	}
 
 	return ki_ds_is_active_uri(msg, vset, &suri);
 }
 
d081ee4e
 static int ds_parse_reply_codes()
 {
 	param_t *params_list = NULL;
 	param_t *pit = NULL;
ec903da3
 	int list_size = 0;
 	int i = 0;
 	int pos = 0;
 	int code = 0;
 	str input = {0, 0};
d081ee4e
 	int *ds_ping_reply_codes_new = NULL;
 	int *ds_ping_reply_codes_old = NULL;
ec903da3
 
 	/* Validate String: */
d081ee4e
 	if(cfg_get(dispatcher, dispatcher_cfg, ds_ping_reply_codes_str).s == 0
 			|| cfg_get(dispatcher, dispatcher_cfg, ds_ping_reply_codes_str).len
 					   <= 0)
ec903da3
 		return 0;
 
 	/* parse_params will modify the string pointer of .s, so we need to make a copy. */
 	input.s = cfg_get(dispatcher, dispatcher_cfg, ds_ping_reply_codes_str).s;
d081ee4e
 	input.len =
 			cfg_get(dispatcher, dispatcher_cfg, ds_ping_reply_codes_str).len;
cbe2e236
 
ec903da3
 	/* Parse the parameters: */
d081ee4e
 	if(parse_params(&input, CLASS_ANY, 0, &params_list) < 0)
ec903da3
 		return -1;
 
 	/* Get the number of entries in the list */
d081ee4e
 	for(pit = params_list; pit; pit = pit->next) {
 		if(pit->name.len == 4 && strncasecmp(pit->name.s, "code", 4) == 0) {
ec903da3
 			str2sint(&pit->body, &code);
d081ee4e
 			if((code >= 100) && (code < 700))
ec903da3
 				list_size += 1;
d081ee4e
 		} else if(pit->name.len == 5
 				  && strncasecmp(pit->name.s, "class", 5) == 0) {
ec903da3
 			str2sint(&pit->body, &code);
d081ee4e
 			if((code >= 1) && (code < 7))
ec903da3
 				list_size += 100;
 		}
 	}
29ebde9c
 	LM_DBG("expecting %d reply codes\n", list_size);
ec903da3
 
d081ee4e
 	if(list_size > 0) {
ec903da3
 		/* Allocate Memory for the new list: */
d081ee4e
 		ds_ping_reply_codes_new = (int *)shm_malloc(list_size * sizeof(int));
 		if(ds_ping_reply_codes_new == NULL) {
ec903da3
 			free_params(params_list);
 			LM_ERR("no more memory\n");
 			return -1;
 		}
cbe2e236
 
ec903da3
 		/* Now create the list of valid reply-codes: */
d081ee4e
 		for(pit = params_list; pit; pit = pit->next) {
 			if(pit->name.len == 4 && strncasecmp(pit->name.s, "code", 4) == 0) {
ec903da3
 				str2sint(&pit->body, &code);
d081ee4e
 				if((code >= 100) && (code < 700))
ec903da3
 					ds_ping_reply_codes_new[pos++] = code;
d081ee4e
 			} else if(pit->name.len == 5
 					  && strncasecmp(pit->name.s, "class", 5) == 0) {
ec903da3
 				str2sint(&pit->body, &code);
d081ee4e
 				if((code >= 1) && (code < 7)) {
ec903da3
 					/* Add every code from this class, e.g. 100 to 199 */
d081ee4e
 					for(i = (code * 100); i <= ((code * 100) + 99); i++)
ec903da3
 						ds_ping_reply_codes_new[pos++] = i;
 				}
 			}
 		}
 	} else {
 		ds_ping_reply_codes_new = 0;
 	}
 	free_params(params_list);
 
 	/* More reply-codes? Change Pointer and then set number of codes. */
d081ee4e
 	if(list_size > *ds_ping_reply_codes_cnt) {
ec903da3
 		// Copy Pointer
 		ds_ping_reply_codes_old = *ds_ping_reply_codes;
 		*ds_ping_reply_codes = ds_ping_reply_codes_new;
 		// Done: Set new Number of entries:
 		*ds_ping_reply_codes_cnt = list_size;
 		// Free the old memory area:
 		if(ds_ping_reply_codes_old)
2a605383
 			shm_free(ds_ping_reply_codes_old);
cbe2e236
 		/* Less or equal? Set the number of codes first. */
ec903da3
 	} else {
 		// Done:
 		*ds_ping_reply_codes_cnt = list_size;
 		// Copy Pointer
 		ds_ping_reply_codes_old = *ds_ping_reply_codes;
 		*ds_ping_reply_codes = ds_ping_reply_codes_new;
 		// Free the old memory area:
 		if(ds_ping_reply_codes_old)
2a605383
 			shm_free(ds_ping_reply_codes_old);
ec903da3
 	}
 	/* Print the list as INFO: */
d081ee4e
 	for(i = 0; i < *ds_ping_reply_codes_cnt; i++) {
29ebde9c
 		LM_DBG("now accepting reply code %d (%d/%d) as valid\n",
d081ee4e
 				(*ds_ping_reply_codes)[i], (i + 1), *ds_ping_reply_codes_cnt);
ec903da3
 	}
 	return 0;
 }
 
 int ds_ping_check_rplcode(int code)
 {
 	int i;
cbe2e236
 
d081ee4e
 	for(i = 0; i < *ds_ping_reply_codes_cnt; i++) {
ec903da3
 		if((*ds_ping_reply_codes)[i] == code)
 			return 1;
 	}
 
 	return 0;
 }
 
d081ee4e
 void ds_ping_reply_codes_update(str *gname, str *name)
 {
ec903da3
 	ds_parse_reply_codes();
 }
d997ecfc
 
9d59f5cb
 /**
  *
  */
 static int pv_get_dsv(sip_msg_t *msg, pv_param_t *param, pv_value_t *res)
 {
 	ds_rctx_t *rctx;
 
 	if(param==NULL) {
 		return -1;
 	}
 	rctx = ds_get_rctx();
 	if(rctx==NULL) {
 		return pv_get_null(msg, param, res);
 	}
 	switch(param->pvn.u.isname.name.n)
 	{
 		case 0:
 			return pv_get_sintval(msg, param, res, rctx->code);
 		case 1:
 			if(rctx->reason.s!=NULL && rctx->reason.len>0) {
 				return pv_get_strval(msg, param, res, &rctx->reason);
 			}
 			return pv_get_null(msg, param, res);
 		case 2:
 			return pv_get_sintval(msg, param, res, rctx->flags);
 		default:
 			return pv_get_null(msg, param, res);
 	}
 }
 
 /**
  *
  */
 static int pv_parse_dsv(pv_spec_p sp, str *in)
 {
 	if(sp==NULL || in==NULL || in->len<=0)
 		return -1;
 
 	switch(in->len)
 	{
 		case 4:
 			if(strncmp(in->s, "code", 4)==0)
 				sp->pvp.pvn.u.isname.name.n = 0;
 			else goto error;
 		break;
 		case 5:
 			if(strncmp(in->s, "flags", 5)==0)
 				sp->pvp.pvn.u.isname.name.n = 2;
 			else goto error;
 		break;
 		case 6:
 			if(strncmp(in->s, "reason", 6)==0)
0ad7fb46
 				sp->pvp.pvn.u.isname.name.n = 1;
9d59f5cb
 			else goto error;
 		break;
 		default:
 			goto error;
 	}
 	sp->pvp.pvn.type = PV_NAME_INTSTR;
 	sp->pvp.pvn.u.isname.type = 0;
 
 	return 0;
 
 error:
 	LM_ERR("unknown PV key: %.*s\n", in->len, in->s);
 	return -1;
 }
 
7b691e97
 /* KEMI wrappers */
 /**
  *
  */
 static int ki_ds_select(sip_msg_t *msg, int set, int alg)
 {
 	return ds_select_dst_limit(msg, set, alg, 0xffff /* limit number of dst*/,
 			2 /*set no dst/uri*/);
 }
 
 /**
  *
  */
 static int ki_ds_select_limit(sip_msg_t *msg, int set, int alg, int limit)
 {
 	return ds_select_dst_limit(msg, set, alg, limit /* limit number of dst*/,
 			2 /*set no dst/uri*/);
 }
 
 /**
  *
  */
 static int ki_ds_select_dst(sip_msg_t *msg, int set, int alg)
 {
 	return ds_select_dst_limit(msg, set, alg, 0xffff /* limit number of dst*/,
 			0 /*set dst uri*/);
 }
 
 /**
  *
  */
 static int ki_ds_select_dst_limit(sip_msg_t *msg, int set, int alg, int limit)
 {
 	return ds_select_dst_limit(msg, set, alg, limit /* limit number of dst*/,
 			0 /*set dst uri*/);
 }
 
 /**
  *
  */
 static int ki_ds_select_domain(sip_msg_t *msg, int set, int alg)
 {
 	return ds_select_dst_limit(msg, set, alg, 0xffff /* limit number of dst*/,
 			1 /*set host port*/);
 }
 
 /**
  *
  */
 static int ki_ds_select_domain_limit(sip_msg_t *msg, int set, int alg, int limit)
 {
 	return ds_select_dst_limit(msg, set, alg, limit /* limit number of dst*/,
 			1 /*set host port*/);
 }
 
 /**
  *
  */
 static int ki_ds_next_dst(sip_msg_t *msg)
 {
b55b6fa1
 	return ds_update_dst(msg, DS_USE_NEXT, DS_SETOP_DSTURI /*set dst uri*/);
7b691e97
 }
 
 /**
  *
  */
 static int ki_ds_next_domain(sip_msg_t *msg)
 {
b55b6fa1
 	return ds_update_dst(msg, DS_USE_NEXT, DS_SETOP_RURI /*set host port*/);
7b691e97
 }
 
b55b6fa1
 /**
  *
  */
 static int ki_ds_set_dst(sip_msg_t *msg)
 {
 	return ds_update_dst(msg, DS_USE_CRT, DS_SETOP_DSTURI /*set dst uri*/);
 }
 
 /**
  *
  */
 static int ki_ds_set_domain(sip_msg_t *msg)
 {
 	return ds_update_dst(msg, DS_USE_CRT, DS_SETOP_RURI /*set host port*/);
 }
7b691e97
 
 /**
  *
  */
 /* clang-format off */
 static sr_kemi_t sr_kemi_dispatcher_exports[] = {
 	{ str_init("dispatcher"), str_init("ds_select"),
 		SR_KEMIP_INT, ki_ds_select,
 		{ SR_KEMIP_INT, SR_KEMIP_INT, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("dispatcher"), str_init("ds_select_limit"),
 		SR_KEMIP_INT, ki_ds_select_limit,
 		{ SR_KEMIP_INT, SR_KEMIP_INT, SR_KEMIP_INT,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("dispatcher"), str_init("ds_select_domain"),
 		SR_KEMIP_INT, ki_ds_select_domain,
 		{ SR_KEMIP_INT, SR_KEMIP_INT, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("dispatcher"), str_init("ds_select_domain_limit"),
 		SR_KEMIP_INT, ki_ds_select_domain_limit,
 		{ SR_KEMIP_INT, SR_KEMIP_INT, SR_KEMIP_INT,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("dispatcher"), str_init("ds_next_domain"),
 		SR_KEMIP_INT, ki_ds_next_domain,
 		{ SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
b55b6fa1
 	{ str_init("dispatcher"), str_init("ds_set_domain"),
 		SR_KEMIP_INT, ki_ds_set_domain,
 		{ SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
7b691e97
 	{ str_init("dispatcher"), str_init("ds_select_dst"),
 		SR_KEMIP_INT, ki_ds_select_dst,
 		{ SR_KEMIP_INT, SR_KEMIP_INT, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("dispatcher"), str_init("ds_select_dst_limit"),
 		SR_KEMIP_INT, ki_ds_select_dst_limit,
 		{ SR_KEMIP_INT, SR_KEMIP_INT, SR_KEMIP_INT,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
3933a64c
 	{ str_init("dispatcher"), str_init("ds_select_routes"),
 		SR_KEMIP_INT, ki_ds_select_routes,
 		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("dispatcher"), str_init("ds_select_routes_limit"),
 		SR_KEMIP_INT, ki_ds_select_routes_limit,
 		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_INT,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
7b691e97
 	{ str_init("dispatcher"), str_init("ds_next_dst"),
 		SR_KEMIP_INT, ki_ds_next_dst,
 		{ SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
b55b6fa1
 	{ str_init("dispatcher"), str_init("ds_set_dst"),
 		SR_KEMIP_INT, ki_ds_set_dst,
 		{ SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
55ed25e1
 	{ str_init("dispatcher"), str_init("ds_mark_dst"),
 		SR_KEMIP_INT, ki_ds_mark_dst,
 		{ SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("dispatcher"), str_init("ds_mark_dst_state"),
 		SR_KEMIP_INT, ki_ds_mark_dst_state,
1d1eeafc
 		{ SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
55ed25e1
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("dispatcher"), str_init("ds_is_from_lists"),
 		SR_KEMIP_INT, ki_ds_is_from_lists,
 		{ SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("dispatcher"), str_init("ds_is_from_list"),
 		SR_KEMIP_INT, ds_is_from_list,
 		{ SR_KEMIP_INT, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("dispatcher"), str_init("ds_is_from_list_mode"),
 		SR_KEMIP_INT, ki_ds_is_from_list_mode,
 		{ SR_KEMIP_INT, SR_KEMIP_INT, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("dispatcher"), str_init("ds_is_from_list_uri"),
 		SR_KEMIP_INT, ki_ds_is_from_list_uri,
 		{ SR_KEMIP_INT, SR_KEMIP_INT, SR_KEMIP_STR,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("dispatcher"), str_init("ds_load_update"),
 		SR_KEMIP_INT, ds_load_update,
 		{ SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("dispatcher"), str_init("ds_load_unset"),
 		SR_KEMIP_INT, ds_load_unset,
 		{ SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("dispatcher"), str_init("ds_reload"),
 		SR_KEMIP_INT, ds_reload,
 		{ SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("dispatcher"), str_init("ds_list_exists"),
 		SR_KEMIP_INT, ki_ds_list_exists,
1d1eeafc
 		{ SR_KEMIP_INT, SR_KEMIP_NONE, SR_KEMIP_NONE,
55ed25e1
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
d574a210
 	{ str_init("dispatcher"), str_init("ds_is_active"),
 		SR_KEMIP_INT, ki_ds_is_active,
 		{ SR_KEMIP_INT, SR_KEMIP_NONE, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
 	{ str_init("dispatcher"), str_init("ds_is_active_uri"),
 		SR_KEMIP_INT, ki_ds_is_active_uri,
 		{ SR_KEMIP_INT, SR_KEMIP_STR, SR_KEMIP_NONE,
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
 	},
7b691e97
 	{ {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } }
 };
 /* clang-format on */
 
 /**
  *
  */
 int mod_register(char *path, int *dlflags, void *p1, void *p2)
 {
 	sr_kemi_modules_add(sr_kemi_dispatcher_exports);
 	return 0;
 }
 
d997ecfc
 /*** RPC implementation ***/
 
d081ee4e
 static const char *dispatcher_rpc_reload_doc[2] = {
 		"Reload dispatcher destination sets", 0};
d997ecfc
 
 
 /*
  * RPC command to reload dispatcher destination sets
  */
d081ee4e
 static void dispatcher_rpc_reload(rpc_t *rpc, void *ctx)
d997ecfc
 {
2b032137
 
 	if(ds_rpc_reload_time==NULL) {
 		LM_ERR("not ready for reload\n");
 		rpc->fault(ctx, 500, "Not ready for reload");
 		return;
 	}
 	if(*ds_rpc_reload_time!=0 && *ds_rpc_reload_time > time(NULL) - ds_reload_delta) {
 		LM_ERR("ongoing reload\n");
 		rpc->fault(ctx, 500, "Ongoing reload");
 		return;
 	}
 	*ds_rpc_reload_time = time(NULL);
 
d997ecfc
 	if(!ds_db_url.s) {
d081ee4e
 		if(ds_load_list(dslistfile) != 0) {
d997ecfc
 			rpc->fault(ctx, 500, "Reload Failed");
 			return;
 		}
 	} else {
d081ee4e
 		if(ds_reload_db() < 0) {
d997ecfc
 			rpc->fault(ctx, 500, "Reload Failed");
 			return;
 		}
 	}
9f8de525
 	rpc->rpl_printf(ctx, "Ok. Dispatcher successfully reloaded.");
d997ecfc
 	return;
 }
 
 
d081ee4e
 static const char *dispatcher_rpc_list_doc[2] = {
 		"Return the content of dispatcher sets", 0};
d997ecfc
 
996f50eb
 /**
  *
d997ecfc
  */
d081ee4e
 int ds_rpc_print_set(ds_set_t *node, rpc_t *rpc, void *ctx, void *rpc_handle)
d997ecfc
 {
d081ee4e
 	int i = 0, rc = 0;
 	void *rh;
 	void *sh;
 	void *vh;
 	void *wh;
513a1763
 	void *lh;
 	void *dh;
d997ecfc
 	int j;
 	char c[3];
996f50eb
 	str data = STR_NULL;
 
c73a22ab
 	if(!node)
 		return 0;
 
 	for(; i < 2; ++i) {
 		rc = ds_rpc_print_set(node->next[i], rpc, ctx, rpc_handle);
 		if(rc != 0)
 			return rc;
 	}
 
d081ee4e
 	if(rpc->struct_add(rpc_handle, "{", "SET", &sh) < 0) {
996f50eb
 		rpc->fault(ctx, 500, "Internal error set structure");
 		return -1;
 	}
d081ee4e
 	if(rpc->struct_add(sh, "d[", "ID", node->id, "TARGETS", &rh) < 0) {
996f50eb
 		rpc->fault(ctx, 500, "Internal error creating set id");
 		return -1;
 	}
 
d081ee4e
 	for(j = 0; j < node->nr; j++) {
 		if(rpc->struct_add(rh, "{", "DEST", &vh) < 0) {
996f50eb
 			rpc->fault(ctx, 500, "Internal error creating dest");
 			return -1;
 		}
 
 		memset(&c, 0, sizeof(c));
d081ee4e
 		if(node->dlist[j].flags & DS_INACTIVE_DST)
996f50eb
 			c[0] = 'I';
d081ee4e
 		else if(node->dlist[j].flags & DS_DISABLED_DST)
996f50eb
 			c[0] = 'D';
d081ee4e
 		else if(node->dlist[j].flags & DS_TRYING_DST)
996f50eb
 			c[0] = 'T';
 		else
 			c[0] = 'A';
 
d081ee4e
 		if(node->dlist[j].flags & DS_PROBING_DST)
996f50eb
 			c[1] = 'P';
 		else
 			c[1] = 'X';
 
d081ee4e
 		if(node->dlist[j].attrs.body.s) {
513a1763
 			if(rpc->struct_add(vh, "Ssd{", "URI", &node->dlist[j].uri,
 					"FLAGS", c,
 					"PRIORITY", node->dlist[j].priority,
 					"ATTRS", &wh) < 0) {
996f50eb
 				rpc->fault(ctx, 500, "Internal error creating dest struct");
 				return -1;
 			}
74ef108f
 			if(rpc->struct_add(wh, "SSdddSSS",
513a1763
 						"BODY", &(node->dlist[j].attrs.body),
 						"DUID", (node->dlist[j].attrs.duid.s)
 									? &(node->dlist[j].attrs.duid) : &data,
 						"MAXLOAD", node->dlist[j].attrs.maxload,
 						"WEIGHT", node->dlist[j].attrs.weight,
 						"RWEIGHT", node->dlist[j].attrs.rweight,
 						"SOCKET", (node->dlist[j].attrs.socket.s)
87f170b2
 									? &(node->dlist[j].attrs.socket) : &data,
 						"SOCKNAME", (node->dlist[j].attrs.sockname.s)
74ef108f
 									? &(node->dlist[j].attrs.sockname) : &data,
 						"OBPROXY", (node->dlist[j].attrs.obproxy.s)
 									? &(node->dlist[j].attrs.obproxy) : &data)
d081ee4e
 					< 0) {
996f50eb
 				rpc->fault(ctx, 500, "Internal error creating attrs struct");
 				return -1;
 			}
 		} else {
d081ee4e
 			if(rpc->struct_add(vh, "Ssd", "URI", &node->dlist[j].uri, "FLAGS",
 					   c, "PRIORITY", node->dlist[j].priority)
 					< 0) {
996f50eb
 				rpc->fault(ctx, 500, "Internal error creating dest struct");
 				return -1;
 			}
 		}
12a25ed7
 		if (ds_ping_latency_stats) {
 			if(rpc->struct_add(vh, "{", "LATENCY", &lh) < 0) {
 				rpc->fault(ctx, 500, "Internal error creating dest");
 				return -1;
 			}
 			if (rpc->struct_add(lh, "fffdd", "AVG", node->dlist[j].latency_stats.average,
 					  "STD", node->dlist[j].latency_stats.stdev,
 					  "EST", node->dlist[j].latency_stats.estimate,
 					  "MAX", node->dlist[j].latency_stats.max,
 					  "TIMEOUT", node->dlist[j].latency_stats.timeout)
 					< 0) {
 				rpc->fault(ctx, 500, "Internal error creating dest struct");
 				return -1;
 			}
 		}
513a1763
 		if (ds_hash_size>0) {
 			if(rpc->struct_add(vh, "{", "RUNTIME", &dh) < 0) {
 				rpc->fault(ctx, 500, "Internal error creating runtime struct");
 				return -1;
 			}
 			if (rpc->struct_add(dh, "d", "DLGLOAD", node->dlist[j].dload) < 0) {
 				rpc->fault(ctx, 500, "Internal error creating runtime attrs");
 				return -1;
 			}
 		}
996f50eb
 	}
 
 	return 0;
 }
 
 /*
  * RPC command to print dispatcher destination sets
  */
d081ee4e
 static void dispatcher_rpc_list(rpc_t *rpc, void *ctx)
996f50eb
 {
d081ee4e
 	void *th;
 	void *ih;
d997ecfc
 
0838c3e4
 	ds_set_t *dslist = ds_get_list();
 	int dslistnr = ds_get_list_nr();
d997ecfc
 
0838c3e4
 	if(dslist == NULL || dslistnr <= 0) {
ca230986
 		LM_DBG("no destination sets\n");
d997ecfc
 		rpc->fault(ctx, 500, "No Destination Sets");
 		return;
 	}
 
 	/* add entry node */
d081ee4e
 	if(rpc->add(ctx, "{", &th) < 0) {
6904d8c8
 		rpc->fault(ctx, 500, "Internal error root reply");
d997ecfc
 		return;
 	}
0838c3e4
 	if(rpc->struct_add(th, "d[", "NRSETS", dslistnr, "RECORDS", &ih) < 0) {
d1b3dd1a
 		rpc->fault(ctx, 500, "Internal error sets structure");
d997ecfc
 		return;
 	}
 
0838c3e4
 	ds_rpc_print_set(dslist, rpc, ctx, ih);
d997ecfc
 
 	return;
 }
 
 
 /*
b7b0f258
  * RPC command to set the state of a destination address or duid
d997ecfc
  */
b7b0f258
 static void dispatcher_rpc_set_state_helper(rpc_t *rpc, void *ctx, int mattr)
d997ecfc
 {
 	int group;
 	str dest;
 	str state;
 	int stval;
 
d081ee4e
 	if(rpc->scan(ctx, ".SdS", &state, &group, &dest) < 3) {
d997ecfc
 		rpc->fault(ctx, 500, "Invalid Parameters");
 		return;
 	}
d081ee4e
 	if(state.len <= 0 || state.s == NULL) {
d997ecfc
 		LM_ERR("bad state value\n");
 		rpc->fault(ctx, 500, "Invalid State Parameter");
 		return;
 	}
 
 	stval = 0;
d081ee4e
 	if(state.s[0] == '0' || state.s[0] == 'I' || state.s[0] == 'i') {
d997ecfc
 		/* set inactive */
 		stval |= DS_INACTIVE_DST;
d081ee4e
 		if((state.len > 1) && (state.s[1] == 'P' || state.s[1] == 'p'))
d997ecfc
 			stval |= DS_PROBING_DST;
d081ee4e
 	} else if(state.s[0] == '1' || state.s[0] == 'A' || state.s[0] == 'a') {
d997ecfc
 		/* set active */
d081ee4e
 		if((state.len > 1) && (state.s[1] == 'P' || state.s[1] == 'p'))
d997ecfc
 			stval |= DS_PROBING_DST;
d081ee4e
 	} else if(state.s[0] == '2' || state.s[0] == 'D' || state.s[0] == 'd') {
d997ecfc
 		/* set disabled */
 		stval |= DS_DISABLED_DST;
d081ee4e
 	} else if(state.s[0] == '3' || state.s[0] == 'T' || state.s[0] == 't') {
cbe2e236
 		/* set trying */
 		stval |= DS_TRYING_DST;
d081ee4e
 		if((state.len > 1) && (state.s[1] == 'P' || state.s[1] == 'p'))
cbe2e236
 			stval |= DS_PROBING_DST;
d997ecfc
 	} else {
7c13f41a
 		LM_ERR("unknown state value\n");
d997ecfc
 		rpc->fault(ctx, 500, "Unknown State Value");
 		return;
 	}
 
27fdee6b
 	if(dest.len == 3 && strncmp(dest.s, "all", 3) == 0) {
d4003b91
 		ds_reinit_state_all(group, stval);
 	} else {
b7b0f258
 		if (mattr==1) {
 			if(ds_reinit_duid_state(group, &dest, stval) < 0) {
 				rpc->fault(ctx, 500, "State Update Failed");
 				return;
 			}
 		} else {
 			if(ds_reinit_state(group, &dest, stval) < 0) {
 				rpc->fault(ctx, 500, "State Update Failed");
 				return;
 			}
d4003b91
 		}
d997ecfc
 	}
af8b605b
 	rpc->rpl_printf(ctx, "Ok. Dispatcher state updated.");
d997ecfc
 	return;
 }
 
 
b7b0f258
 static const char *dispatcher_rpc_set_state_doc[2] = {
 		"Set the state of a destination by address", 0};
 
 /*
  * RPC command to set the state of a destination address
  */
 static void dispatcher_rpc_set_state(rpc_t *rpc, void *ctx)
 {
 	dispatcher_rpc_set_state_helper(rpc, ctx, 0);
 }
 
 static const char *dispatcher_rpc_set_duid_state_doc[2] = {
 		"Set the state of a destination by duid", 0};
 
 /*
  * RPC command to set the state of a destination duid
  */
 static void dispatcher_rpc_set_duid_state(rpc_t *rpc, void *ctx)
 {
 	dispatcher_rpc_set_state_helper(rpc, ctx, 1);
 }
 
d081ee4e
 static const char *dispatcher_rpc_ping_active_doc[2] = {
 		"Manage setting on/off the pinging (keepalive) of destinations", 0};
44c5d1c0
 
 
 /*
  * RPC command to set the state of a destination address
  */
d081ee4e
 static void dispatcher_rpc_ping_active(rpc_t *rpc, void *ctx)
44c5d1c0
 {
 	int state;
 	int ostate;
 	void *th;
 
d081ee4e
 	if(rpc->scan(ctx, "*d", &state) != 1) {
44c5d1c0
 		state = -1;
 	}
 	ostate = ds_ping_active_get();
 	/* add entry node */
d081ee4e
 	if(rpc->add(ctx, "{", &th) < 0) {
44c5d1c0
 		rpc->fault(ctx, 500, "Internal error root reply");
 		return;
 	}
d081ee4e
 	if(state == -1) {
 		if(rpc->struct_add(th, "d", "OldPingState", ostate) < 0) {
44c5d1c0
 			rpc->fault(ctx, 500, "Internal error reply structure");
 			return;
 		}
 		return;
 	}
 
d081ee4e
 	if(ds_ping_active_set(state) < 0) {
44c5d1c0
 		rpc->fault(ctx, 500, "Ping State Update Failed");
 		return;
 	}
d081ee4e
 	if(rpc->struct_add(th, "dd", "NewPingState", state, "OldPingState", ostate)
 			< 0) {
44c5d1c0
 		rpc->fault(ctx, 500, "Internal error reply structure");
 		return;
 	}
 	return;
 }
 
97a198b4
 static const char *dispatcher_rpc_add_doc[2] = {
 		"Add a destination address in memory", 0};
 
 
 /*
  * RPC command to add a destination address to memory
  */
 static void dispatcher_rpc_add(rpc_t *rpc, void *ctx)
 {
41714a49
 	int group, flags, nparams;
97a198b4
 	str dest;
7894fb47
 	str attrs = STR_NULL;
97a198b4
 
 	flags = 0;
 
41714a49
 	nparams = rpc->scan(ctx, "dS*dS", &group, &dest, &flags, &attrs);
 	if(nparams < 2) {
97a198b4
 		rpc->fault(ctx, 500, "Invalid Parameters");
 		return;
7894fb47
 	} else if (nparams <= 3) {
41714a49
 		attrs.s = 0;
 		attrs.len = 0;
97a198b4
 	}
 
2e842e9c
 	if(ds_add_dst(group, &dest, flags, &attrs) != 0) {
97a198b4
 		rpc->fault(ctx, 500, "Adding dispatcher dst failed");
 		return;
 	}
70a7f92b
 	rpc->rpl_printf(ctx, "Ok. Dispatcher destination added.");
97a198b4
 	return;
 }
 
d9a9e5f5
 static const char *dispatcher_rpc_remove_doc[2] = {
 		"Remove a destination address from memory", 0};
 
 
 /*
  * RPC command to remove a destination address from memory
  */
 static void dispatcher_rpc_remove(rpc_t *rpc, void *ctx)
 {
 	int group;
 	str dest;
 
 	if(rpc->scan(ctx, "dS", &group, &dest) < 2) {
 		rpc->fault(ctx, 500, "Invalid Parameters");
 		return;
 	}
 
 	if(ds_remove_dst(group, &dest) != 0) {
 		rpc->fault(ctx, 500, "Removing dispatcher dst failed");
 		return;
 	}
101ddb5e
 	rpc->rpl_printf(ctx, "Ok. Dispatcher destination removed.");
d9a9e5f5
 	return;
 }
 
aac6c77c
 static const char *dispatcher_rpc_hash_doc[2] = {
 		"Compute the hash if the values", 0};
 
 
 /*
  * RPC command to compute the hash of the values
  */
 static void dispatcher_rpc_hash(rpc_t *rpc, void *ctx)
 {
 	int n = 0;
 	unsigned int hashid = 0;
 	int nslots = 0;
 	str val1 = STR_NULL;
 	str val2 = STR_NULL;
 	void *th;
 
 	n = rpc->scan(ctx, "dS*S", &nslots, &val1, &val2);
 	if(n < 2) {
 		rpc->fault(ctx, 500, "Invalid Parameters");
 		return;
 	}
 	if(n==2) {
 		val2.s = NULL;
 		val2.s = 0;
 	}
 
 	hashid = ds_get_hash(&val1, &val2);
 
 	/* add entry node */
 	if(rpc->add(ctx, "{", &th) < 0) {
 		rpc->fault(ctx, 500, "Internal error root reply");
 		return;
 	}
 	if(rpc->struct_add(th, "uu", "hashid", hashid,
 				"slot", (nslots>0)?(hashid%nslots):0)
 			< 0) {
 		rpc->fault(ctx, 500, "Internal error reply structure");
 		return;
 	}
 
 	return;
 }
 
d081ee4e
 /* clang-format off */
d997ecfc
 rpc_export_t dispatcher_rpc_cmds[] = {
 	{"dispatcher.reload", dispatcher_rpc_reload,
 		dispatcher_rpc_reload_doc, 0},
 	{"dispatcher.list",   dispatcher_rpc_list,
 		dispatcher_rpc_list_doc,   0},
 	{"dispatcher.set_state",   dispatcher_rpc_set_state,
 		dispatcher_rpc_set_state_doc,   0},
b7b0f258
 	{"dispatcher.set_duid_state",   dispatcher_rpc_set_duid_state,
 		dispatcher_rpc_set_duid_state_doc,   0},
44c5d1c0
 	{"dispatcher.ping_active",   dispatcher_rpc_ping_active,
 		dispatcher_rpc_ping_active_doc, 0},
97a198b4
 	{"dispatcher.add",   dispatcher_rpc_add,
 		dispatcher_rpc_add_doc, 0},
d9a9e5f5
 	{"dispatcher.remove",   dispatcher_rpc_remove,
 		dispatcher_rpc_remove_doc, 0},
aac6c77c
 	{"dispatcher.hash",   dispatcher_rpc_hash,
 		dispatcher_rpc_hash_doc, 0},
d997ecfc
 	{0, 0, 0, 0}
 };
d081ee4e
 /* clang-format on */
d997ecfc
 
 /**
  * register RPC commands
  */
 static int ds_init_rpc(void)
 {
d081ee4e
 	if(rpc_register_array(dispatcher_rpc_cmds) != 0) {
d997ecfc
 		LM_ERR("failed to register RPC commands\n");
 		return -1;
 	}
 	return 0;
 }