dispatcher: init attrs value when param not given to rpc add
[kamailio] / src / modules / dispatcher / dispatcher.c
1 /**
2  * dispatcher module - load balancing
3  *
4  * Copyright (C) 2004-2005 FhG Fokus
5  * Copyright (C) 2006 Voice Sistem SRL
6  * Copyright (C) 2015 Daniel-Constantin Mierla (asipto.com)
7  *
8  * This file is part of Kamailio, a free SIP server.
9  *
10  * Kamailio is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version
14  *
15  * Kamailio is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
23  */
24
25 /*! \file
26  * \ingroup dispatcher
27  * \brief Dispatcher :: Dispatch
28  */
29
30 /*! \defgroup dispatcher Dispatcher :: Load balancing and failover module
31  *      The dispatcher module implements a set of functions for distributing SIP requests on a
32  *      set of servers, but also grouping of server resources.
33  *
34  *      - The module has an internal API exposed to other modules.
35  *      - The module implements a couple of MI functions for managing the list of server resources
36  */
37
38 #include <stdio.h>
39 #include <string.h>
40 #include <stdlib.h>
41 #include <sys/types.h>
42 #include <unistd.h>
43
44 #include "../../core/sr_module.h"
45 #include "../../core/dprint.h"
46 #include "../../core/error.h"
47 #include "../../core/ut.h"
48 #include "../../core/route.h"
49 #include "../../core/timer_proc.h"
50 #include "../../core/mem/mem.h"
51 #include "../../core/mod_fix.h"
52 #include "../../core/rpc.h"
53 #include "../../core/rpc_lookup.h"
54 #include "../../core/kemi.h"
55
56 #include "ds_ht.h"
57 #include "dispatch.h"
58 #include "config.h"
59 #include "api.h"
60
61 MODULE_VERSION
62
63 /* clang-format off */
64 #define DS_SET_ID_COL                   "setid"
65 #define DS_DEST_URI_COL                 "destination"
66 #define DS_DEST_FLAGS_COL               "flags"
67 #define DS_DEST_PRIORITY_COL    "priority"
68 #define DS_DEST_ATTRS_COL               "attrs"
69 #define DS_TABLE_NAME                   "dispatcher"
70
71 /** parameters */
72 char *dslistfile = CFG_DIR"dispatcher.list";
73 int  ds_force_dst   = 1;
74 int  ds_flags       = 0;
75 int  ds_use_default = 0;
76 str ds_xavp_dst = str_init("_dsdst_");
77 int ds_xavp_dst_mode = 0;
78 str ds_xavp_ctx = str_init("_dsctx_");
79 int ds_xavp_ctx_mode = 0;
80
81 str hash_pvar_param = STR_NULL;
82
83 str ds_xavp_dst_addr = str_init("uri");
84 str ds_xavp_dst_grp = str_init("grp");
85 str ds_xavp_dst_dstid = str_init("dstid");
86 str ds_xavp_dst_attrs = str_init("attrs");
87 str ds_xavp_dst_sock = str_init("sock");
88 str ds_xavp_dst_socket = str_init("socket");
89 str ds_xavp_dst_sockname = str_init("sockname");
90
91 str ds_xavp_ctx_cnt = str_init("cnt");
92
93
94 pv_elem_t * hash_param_model = NULL;
95
96 int probing_threshold = 1; /* number of failed requests, before a destination
97                                                         * is taken into probing */
98 int inactive_threshold = 1; /* number of replied requests, before a destination
99                                                          * is taken into back in active state */
100 str ds_ping_method = str_init("OPTIONS");
101 str ds_ping_from   = str_init("sip:dispatcher@localhost");
102 static int ds_ping_interval = 0;
103 int ds_ping_latency_stats = 0;
104 int ds_latency_estimator_alpha_i = 900;
105 float ds_latency_estimator_alpha = 0.9f;
106 int ds_probing_mode = DS_PROBE_NONE;
107
108 static str ds_ping_reply_codes_str= STR_NULL;
109 static int** ds_ping_reply_codes = NULL;
110 static int* ds_ping_reply_codes_cnt;
111
112 str ds_default_socket = STR_NULL;
113 str ds_default_sockname = STR_NULL;
114 struct socket_info * ds_default_sockinfo = NULL;
115
116 int ds_hash_size = 0;
117 int ds_hash_expire = 7200;
118 int ds_hash_initexpire = 7200;
119 int ds_hash_check_interval = 30;
120 int ds_timer_mode = 0;
121 int ds_attrs_none = 0;
122 int ds_load_mode = 0;
123
124 str ds_outbound_proxy = STR_NULL;
125
126 /* tm */
127 struct tm_binds tmb;
128
129 /*db */
130 str ds_db_url            = STR_NULL;
131 str ds_set_id_col        = str_init(DS_SET_ID_COL);
132 str ds_dest_uri_col      = str_init(DS_DEST_URI_COL);
133 str ds_dest_flags_col    = str_init(DS_DEST_FLAGS_COL);
134 str ds_dest_priority_col = str_init(DS_DEST_PRIORITY_COL);
135 str ds_dest_attrs_col    = str_init(DS_DEST_ATTRS_COL);
136 str ds_table_name        = str_init(DS_TABLE_NAME);
137
138 str ds_setid_pvname   = STR_NULL;
139 pv_spec_t ds_setid_pv;
140 str ds_attrs_pvname   = STR_NULL;
141 pv_spec_t ds_attrs_pv;
142
143 str ds_event_callback = STR_NULL;
144 str ds_db_extra_attrs = STR_NULL;
145 param_t *ds_db_extra_attrs_list = NULL;
146
147 static int ds_reload_delta = 5;
148 static time_t *ds_rpc_reload_time = NULL;
149
150 /** module functions */
151 static int mod_init(void);
152 static int child_init(int);
153
154 static int ds_parse_reply_codes();
155 static int ds_init_rpc(void);
156
157 static int w_ds_select(sip_msg_t*, char*, char*);
158 static int w_ds_select_limit(sip_msg_t*, char*, char*, char*);
159 static int w_ds_select_dst(struct sip_msg*, char*, char*);
160 static int w_ds_select_dst_limit(struct sip_msg*, char*, char*, char*);
161 static int w_ds_select_domain(struct sip_msg*, char*, char*);
162 static int w_ds_select_domain_limit(struct sip_msg*, char*, char*, char*);
163 static int w_ds_select_routes(sip_msg_t*, char*, char*);
164 static int w_ds_select_routes_limit(sip_msg_t*, char*, char*, char*);
165 static int w_ds_next_dst(struct sip_msg*, char*, char*);
166 static int w_ds_next_domain(struct sip_msg*, char*, char*);
167 static int w_ds_set_dst(struct sip_msg*, char*, char*);
168 static int w_ds_set_domain(struct sip_msg*, char*, char*);
169 static int w_ds_mark_dst0(struct sip_msg*, char*, char*);
170 static int w_ds_mark_dst1(struct sip_msg*, char*, char*);
171 static int w_ds_load_unset(struct sip_msg*, char*, char*);
172 static int w_ds_load_update(struct sip_msg*, char*, char*);
173
174 static int w_ds_is_from_list0(struct sip_msg*, char*, char*);
175 static int w_ds_is_from_list1(struct sip_msg*, char*, char*);
176 static int w_ds_is_from_list2(struct sip_msg*, char*, char*);
177 static int w_ds_is_from_list3(struct sip_msg*, char*, char*, char*);
178 static int w_ds_list_exist(struct sip_msg*, char*, char*);
179 static int w_ds_reload(struct sip_msg* msg, char*, char*);
180
181 static int w_ds_is_active(sip_msg_t *msg, char *pset, char *p2);
182 static int w_ds_is_active_uri(sip_msg_t *msg, char *pset, char *puri);
183
184 static int fixup_ds_is_from_list(void** param, int param_no);
185 static int fixup_ds_list_exist(void** param,int param_no);
186
187 static void destroy(void);
188
189 static int ds_warn_fixup(void** param, int param_no);
190
191 static int pv_get_dsv(sip_msg_t *msg, pv_param_t *param, pv_value_t *res);
192 static int pv_parse_dsv(pv_spec_p sp, str *in);
193
194 static pv_export_t mod_pvs[] = {
195         { {"dsv", (sizeof("dsv")-1)}, PVT_OTHER, pv_get_dsv, 0,
196                 pv_parse_dsv, 0, 0, 0 },
197
198         { {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
199 };
200
201 static cmd_export_t cmds[]={
202         {"ds_select",    (cmd_function)w_ds_select,            2,
203                 fixup_igp_igp, 0, ANY_ROUTE},
204         {"ds_select",    (cmd_function)w_ds_select_limit,      3,
205                 fixup_igp_all, 0, REQUEST_ROUTE|FAILURE_ROUTE},
206         {"ds_select_dst",    (cmd_function)w_ds_select_dst,    2,
207                 fixup_igp_igp, 0, REQUEST_ROUTE|FAILURE_ROUTE},
208         {"ds_select_dst",    (cmd_function)w_ds_select_dst_limit,    3,
209                 fixup_igp_all, 0, REQUEST_ROUTE|FAILURE_ROUTE},
210         {"ds_select_domain", (cmd_function)w_ds_select_domain, 2,
211                 fixup_igp_igp, 0, REQUEST_ROUTE|FAILURE_ROUTE},
212         {"ds_select_domain", (cmd_function)w_ds_select_domain_limit, 3,
213                 fixup_igp_all, 0, REQUEST_ROUTE|FAILURE_ROUTE},
214         {"ds_select_routes", (cmd_function)w_ds_select_routes, 2,
215                 fixup_spve_spve, 0, REQUEST_ROUTE|FAILURE_ROUTE},
216         {"ds_select_routes", (cmd_function)w_ds_select_routes_limit, 3,
217                 fixup_spve_spve_igp, 0, REQUEST_ROUTE|FAILURE_ROUTE},
218         {"ds_next_dst",      (cmd_function)w_ds_next_dst,      0,
219                 ds_warn_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE},
220         {"ds_next_domain",   (cmd_function)w_ds_next_domain,   0,
221                 ds_warn_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE},
222         {"ds_set_dst",       (cmd_function)w_ds_set_dst,      0,
223                 ds_warn_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE},
224         {"ds_set_domain",    (cmd_function)w_ds_set_domain,   0,
225                 ds_warn_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE},
226         {"ds_mark_dst",      (cmd_function)w_ds_mark_dst0,     0,
227                 ds_warn_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE},
228         {"ds_mark_dst",      (cmd_function)w_ds_mark_dst1,     1,
229                 ds_warn_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE},
230         {"ds_is_from_list",  (cmd_function)w_ds_is_from_list0, 0,
231                 0, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE},
232         {"ds_is_from_list",  (cmd_function)w_ds_is_from_list1, 1,
233                 fixup_igp_null, 0, ANY_ROUTE},
234         {"ds_is_from_list",  (cmd_function)w_ds_is_from_list2, 2,
235                 fixup_ds_is_from_list, 0, ANY_ROUTE},
236         {"ds_is_from_list",  (cmd_function)w_ds_is_from_list3, 3,
237                 fixup_ds_is_from_list, 0, ANY_ROUTE},
238         {"ds_list_exist",  (cmd_function)w_ds_list_exist, 1,
239                 fixup_ds_list_exist, 0, ANY_ROUTE},
240         {"ds_list_exists",  (cmd_function)w_ds_list_exist, 1,
241                 fixup_ds_list_exist, 0, ANY_ROUTE},
242         {"ds_load_unset",    (cmd_function)w_ds_load_unset,   0,
243                 0, 0, ANY_ROUTE},
244         {"ds_load_update",   (cmd_function)w_ds_load_update,  0,
245                 0, 0, ANY_ROUTE},
246         {"ds_is_active",  (cmd_function)w_ds_is_active, 1,
247                 fixup_igp_null, fixup_free_igp_null, ANY_ROUTE},
248         {"ds_is_active",  (cmd_function)w_ds_is_active_uri, 2,
249                 fixup_igp_spve, fixup_free_igp_spve, ANY_ROUTE},
250         {"bind_dispatcher",   (cmd_function)bind_dispatcher,  0,
251                 0, 0, 0},
252         {"ds_reload", (cmd_function)w_ds_reload, 0,
253                 0, 0, ANY_ROUTE},
254         {0,0,0,0,0,0}
255 };
256
257
258 static param_export_t params[]={
259         {"list_file",       PARAM_STRING, &dslistfile},
260         {"db_url",                  PARAM_STR, &ds_db_url},
261         {"table_name",      PARAM_STR, &ds_table_name},
262         {"setid_col",       PARAM_STR, &ds_set_id_col},
263         {"destination_col", PARAM_STR, &ds_dest_uri_col},
264         {"flags_col",       PARAM_STR, &ds_dest_flags_col},
265         {"priority_col",    PARAM_STR, &ds_dest_priority_col},
266         {"attrs_col",       PARAM_STR, &ds_dest_attrs_col},
267         {"force_dst",       INT_PARAM, &ds_force_dst},
268         {"flags",           INT_PARAM, &ds_flags},
269         {"use_default",     INT_PARAM, &ds_use_default},
270         {"xavp_dst",        PARAM_STR, &ds_xavp_dst},
271         {"xavp_dst_mode",   PARAM_INT, &ds_xavp_dst_mode},
272         {"xavp_ctx",        PARAM_STR, &ds_xavp_ctx},
273         {"xavp_ctx_mode",   PARAM_INT, &ds_xavp_ctx_mode},
274         {"hash_pvar",       PARAM_STR, &hash_pvar_param},
275         {"setid_pvname",    PARAM_STR, &ds_setid_pvname},
276         {"attrs_pvname",    PARAM_STR, &ds_attrs_pvname},
277         {"ds_probing_threshold", INT_PARAM, &probing_threshold},
278         {"ds_inactive_threshold", INT_PARAM, &inactive_threshold},
279         {"ds_ping_method",     PARAM_STR, &ds_ping_method},
280         {"ds_ping_from",       PARAM_STR, &ds_ping_from},
281         {"ds_ping_interval",   INT_PARAM, &ds_ping_interval},
282         {"ds_ping_latency_stats", INT_PARAM, &ds_ping_latency_stats},
283         {"ds_latency_estimator_alpha", INT_PARAM, &ds_latency_estimator_alpha_i},
284         {"ds_ping_reply_codes", PARAM_STR, &ds_ping_reply_codes_str},
285         {"ds_probing_mode",    INT_PARAM, &ds_probing_mode},
286         {"ds_hash_size",       INT_PARAM, &ds_hash_size},
287         {"ds_hash_expire",     INT_PARAM, &ds_hash_expire},
288         {"ds_hash_initexpire", INT_PARAM, &ds_hash_initexpire},
289         {"ds_hash_check_interval", INT_PARAM, &ds_hash_check_interval},
290         {"outbound_proxy",     PARAM_STR, &ds_outbound_proxy},
291         {"ds_default_socket",  PARAM_STR, &ds_default_socket},
292         {"ds_default_sockname",PARAM_STR, &ds_default_sockname},
293         {"ds_timer_mode",      PARAM_INT, &ds_timer_mode},
294         {"event_callback",     PARAM_STR, &ds_event_callback},
295         {"ds_attrs_none",      PARAM_INT, &ds_attrs_none},
296         {"ds_db_extra_attrs",  PARAM_STR, &ds_db_extra_attrs},
297         {"ds_load_mode",       PARAM_INT, &ds_load_mode},
298         {"reload_delta",       PARAM_INT, &ds_reload_delta },
299         {0,0,0}
300 };
301
302
303 /** module exports */
304 struct module_exports exports= {
305         "dispatcher",    /* module name */
306         DEFAULT_DLFLAGS, /* dlopen flags */
307         cmds,            /* cmd (cfg function) exports */
308         params,          /* param exports */
309         0,               /* exported rpc functions */
310         mod_pvs,         /* exported pseudo-variables */
311         0,               /* response handling function */
312         mod_init,        /* module init function */
313         child_init,      /* per-child init function */
314         destroy          /* module destroy function */
315 };
316 /* clang-format on */
317
318 /**
319  * init module function
320  */
321 static int mod_init(void)
322 {
323         str host;
324         int port, proto;
325         param_hooks_t phooks;
326         param_t *pit = NULL;
327
328         if(ds_ping_active_init() < 0) {
329                 return -1;
330         }
331
332         if(ds_init_rpc() < 0) {
333                 LM_ERR("failed to register RPC commands\n");
334                 return -1;
335         }
336
337         if(cfg_declare("dispatcher", dispatcher_cfg_def, &default_dispatcher_cfg,
338                            cfg_sizeof(dispatcher), &dispatcher_cfg)) {
339                 LM_ERR("Fail to declare the configuration\n");
340                 return -1;
341         }
342
343         /* Initialize the counter */
344         ds_ping_reply_codes = (int **)shm_malloc(sizeof(unsigned int *));
345         *ds_ping_reply_codes = 0;
346         ds_ping_reply_codes_cnt = (int *)shm_malloc(sizeof(int));
347         *ds_ping_reply_codes_cnt = 0;
348         if(ds_ping_reply_codes_str.s) {
349                 cfg_get(dispatcher, dispatcher_cfg, ds_ping_reply_codes_str) =
350                                 ds_ping_reply_codes_str;
351                 if(ds_parse_reply_codes() < 0) {
352                         return -1;
353                 }
354         }
355         /* copy threshholds to config */
356         cfg_get(dispatcher, dispatcher_cfg, probing_threshold) = probing_threshold;
357         cfg_get(dispatcher, dispatcher_cfg, inactive_threshold) =
358                         inactive_threshold;
359
360         if(ds_default_sockname.s && ds_default_sockname.len > 0) {
361                 ds_default_sockinfo = ksr_get_socket_by_name(&ds_default_sockname);
362                 if(ds_default_sockinfo == 0) {
363                         LM_ERR("non-local socket name <%.*s>\n", ds_default_sockname.len,
364                                         ds_default_sockname.s);
365                         return -1;
366                 }
367                 LM_INFO("default dispatcher socket set by name to <%.*s>\n",
368                                 ds_default_sockname.len, ds_default_sockname.s);
369         } else {
370                 if(ds_default_socket.s && ds_default_socket.len > 0) {
371                         if(parse_phostport(
372                                         ds_default_socket.s, &host.s, &host.len, &port, &proto)
373                                         != 0) {
374                                 LM_ERR("bad socket <%.*s>\n", ds_default_socket.len,
375                                                 ds_default_socket.s);
376                                 return -1;
377                         }
378                         ds_default_sockinfo =
379                                         grep_sock_info(&host, (unsigned short)port, proto);
380                         if(ds_default_sockinfo == 0) {
381                                 LM_ERR("non-local socket <%.*s>\n", ds_default_socket.len,
382                                                 ds_default_socket.s);
383                                 return -1;
384                         }
385                         LM_INFO("default dispatcher socket set to <%.*s>\n",
386                                         ds_default_socket.len, ds_default_socket.s);
387                 }
388         }
389
390         if(ds_init_data() != 0)
391                 return -1;
392
393         if(ds_db_url.s) {
394                 if(ds_db_extra_attrs.s!=NULL && ds_db_extra_attrs.len>2) {
395                         if(ds_db_extra_attrs.s[ds_db_extra_attrs.len-1]==';') {
396                                 ds_db_extra_attrs.len--;
397                         }
398                         if (parse_params(&ds_db_extra_attrs, CLASS_ANY, &phooks,
399                                                 &ds_db_extra_attrs_list)<0) {
400                                 LM_ERR("failed to parse extra attrs parameter\n");
401                                 return -1;
402                         }
403                         for(pit = ds_db_extra_attrs_list; pit!=NULL; pit=pit->next) {
404                                 if(pit->body.s==NULL || pit->body.len<=0) {
405                                         LM_ERR("invalid db extra attrs parameter\n");
406                                         return -1;
407                                 }
408                         }
409                 }
410                 if(ds_init_db() != 0) {
411                         LM_ERR("could not initiate a connect to the database\n");
412                         return -1;
413                 }
414         } else {
415                 if(ds_load_list(dslistfile) != 0) {
416                         LM_ERR("no dispatching list loaded from file\n");
417                         return -1;
418                 } else {
419                         LM_DBG("loaded dispatching list\n");
420                 }
421         }
422
423         if(hash_pvar_param.s && *hash_pvar_param.s) {
424                 if(pv_parse_format(&hash_pvar_param, &hash_param_model) < 0
425                                 || hash_param_model == NULL) {
426                         LM_ERR("malformed PV string: %s\n", hash_pvar_param.s);
427                         return -1;
428                 }
429         } else {
430                 hash_param_model = NULL;
431         }
432
433         if(ds_setid_pvname.s != 0) {
434                 if(pv_parse_spec(&ds_setid_pvname, &ds_setid_pv) == NULL
435                                 || !pv_is_w(&ds_setid_pv)) {
436                         LM_ERR("[%s]- invalid setid_pvname\n", ds_setid_pvname.s);
437                         return -1;
438                 }
439         }
440
441         if(ds_attrs_pvname.s != 0) {
442                 if(pv_parse_spec(&ds_attrs_pvname, &ds_attrs_pv) == NULL
443                                 || !pv_is_w(&ds_attrs_pv)) {
444                         LM_ERR("[%s]- invalid attrs_pvname\n", ds_attrs_pvname.s);
445                         return -1;
446                 }
447         }
448
449         if(ds_hash_size > 0) {
450                 if(ds_hash_load_init(
451                                         1 << ds_hash_size, ds_hash_expire, ds_hash_initexpire)
452                                 < 0)
453                         return -1;
454                 if(ds_timer_mode == 1) {
455                         if(sr_wtimer_add(ds_ht_timer, NULL, ds_hash_check_interval) < 0)
456                                 return -1;
457                 } else {
458                         if(register_timer(ds_ht_timer, NULL, ds_hash_check_interval)
459                                         < 0)
460                                 return -1;
461                 }
462         }
463
464         /* Only, if the Probing-Timer is enabled the TM-API needs to be loaded: */
465         if(ds_ping_interval > 0) {
466                 /*****************************************************
467                  * TM-Bindings
468                  *****************************************************/
469                 if(load_tm_api(&tmb) == -1) {
470                         LM_ERR("could not load the TM-functions - disable DS ping\n");
471                         return -1;
472                 }
473                 /*****************************************************
474                  * Register the PING-Timer
475                  *****************************************************/
476                 if(ds_timer_mode == 1) {
477                         if(sr_wtimer_add(ds_check_timer, NULL, ds_ping_interval) < 0)
478                                 return -1;
479                 } else {
480                         if(register_timer(ds_check_timer, NULL, ds_ping_interval) < 0)
481                                 return -1;
482                 }
483         }
484         if (ds_latency_estimator_alpha_i > 0 && ds_latency_estimator_alpha_i < 1000) {
485                 ds_latency_estimator_alpha = ds_latency_estimator_alpha_i/1000.0f;
486         } else {
487                 LM_ERR("invalid ds_latency_estimator_alpha must be between 0 and 1000,"
488                                 " using default[%.3f]\n", ds_latency_estimator_alpha);
489         }
490
491         ds_rpc_reload_time = shm_malloc(sizeof(time_t));
492         if(ds_rpc_reload_time == NULL) {
493                 SHM_MEM_ERROR;
494                 return -1;
495         }
496         *ds_rpc_reload_time = 0;
497
498         return 0;
499 }
500
501 /*! \brief
502  * Initialize children
503  */
504 static int child_init(int rank)
505 {
506         return 0;
507 }
508
509 /*! \brief
510  * destroy function
511  */
512 static void destroy(void)
513 {
514         ds_destroy_list();
515         if(ds_db_url.s)
516                 ds_disconnect_db();
517         ds_hash_load_destroy();
518         if(ds_ping_reply_codes)
519                 shm_free(ds_ping_reply_codes);
520         if(ds_ping_reply_codes_cnt)
521                 shm_free(ds_ping_reply_codes_cnt);
522         if(ds_rpc_reload_time!=NULL) {
523                 shm_free(ds_rpc_reload_time);
524                 ds_rpc_reload_time = 0;
525         }
526 }
527
528 #define GET_VALUE(param_name, param, i_value, s_value, value_flags)        \
529         do {                                                                   \
530                 if(get_is_fparam(&(i_value), &(s_value), msg, (fparam_t *)(param), \
531                                    &(value_flags))                                         \
532                                 != 0) {                                                    \
533                         LM_ERR("no %s value\n", (param_name));                         \
534                         return -1;                                                     \
535                 }                                                                  \
536         } while(0)
537
538 /*! \brief
539  * parses string to dispatcher dst flags set
540  * returns <0 on failure or int with flag on success.
541  */
542 int ds_parse_flags(char *flag_str, int flag_len)
543 {
544         int flag = 0;
545         int i;
546
547         for(i = 0; i < flag_len; i++) {
548                 if(flag_str[i] == 'a' || flag_str[i] == 'A') {
549                         flag &= ~(DS_STATES_ALL);
550                 } else if(flag_str[i] == 'i' || flag_str[i] == 'I') {
551                         flag |= DS_INACTIVE_DST;
552                 } else if(flag_str[i] == 'd' || flag_str[i] == 'D') {
553                         flag |= DS_DISABLED_DST;
554                 } else if(flag_str[i] == 't' || flag_str[i] == 'T') {
555                         flag |= DS_TRYING_DST;
556                 } else if(flag_str[i] == 'p' || flag_str[i] == 'P') {
557                         flag |= DS_PROBING_DST;
558                 } else {
559                         flag = -1;
560                         break;
561                 }
562         }
563
564         return flag;
565 }
566
567 /**
568  *
569  */
570 static int w_ds_select_addr(
571                 sip_msg_t *msg, char *set, char *alg, char *limit, int mode)
572 {
573         unsigned int algo_flags, set_flags, limit_flags;
574         str s_algo = STR_NULL;
575         str s_set = STR_NULL;
576         str s_limit = STR_NULL;
577         int a, s, l;
578         if(msg == NULL)
579                 return -1;
580
581         GET_VALUE("destination set", set, s, s_set, set_flags);
582         if(!(set_flags & PARAM_INT)) {
583                 if(set_flags & PARAM_STR)
584                         LM_ERR("unable to get destination set from [%.*s]\n", s_set.len,
585                                         s_set.s);
586                 else
587                         LM_ERR("unable to get destination set\n");
588                 return -1;
589         }
590         GET_VALUE("algorithm", alg, a, s_algo, algo_flags);
591         if(!(algo_flags & PARAM_INT)) {
592                 if(algo_flags & PARAM_STR)
593                         LM_ERR("unable to get algorithm from [%.*s]\n", s_algo.len,
594                                         s_algo.s);
595                 else
596                         LM_ERR("unable to get algorithm\n");
597                 return -1;
598         }
599
600         if(limit) {
601                 GET_VALUE("limit", limit, l, s_limit, limit_flags);
602                 if(!(limit_flags & PARAM_INT)) {
603                         if(limit_flags & PARAM_STR)
604                                 LM_ERR("unable to get dst number limit from [%.*s]\n",
605                                                 s_limit.len, s_limit.s);
606                         else
607                                 LM_ERR("unable to get dst number limit\n");
608                         return -1;
609                 }
610         } else {
611                 l = -1; /* will be casted to a rather big unsigned value */
612         }
613
614         return ds_select_dst_limit(msg, s, a, (unsigned int)l, mode);
615 }
616
617 /**
618  *
619  */
620 static int w_ds_select(struct sip_msg *msg, char *set, char *alg)
621 {
622         return w_ds_select_addr(msg, set, alg, 0 /* limit number of dst*/,
623                                         DS_SETOP_XAVP /*set no dst/uri*/);
624 }
625
626 /**
627  *
628  */
629 static int w_ds_select_limit(
630                 struct sip_msg *msg, char *set, char *alg, char *limit)
631 {
632         return w_ds_select_addr(msg, set, alg, limit /* limit number of dst*/,
633                         DS_SETOP_XAVP /*set no dst/uri*/);
634 }
635
636 /**
637  *
638  */
639 static int w_ds_select_dst(struct sip_msg *msg, char *set, char *alg)
640 {
641         return w_ds_select_addr(msg, set, alg, 0 /* limit number of dst*/,
642                                 DS_SETOP_DSTURI /*set dst uri*/);
643 }
644
645 /**
646  *
647  */
648 static int w_ds_select_dst_limit(
649                 struct sip_msg *msg, char *set, char *alg, char *limit)
650 {
651         return w_ds_select_addr(msg, set, alg, limit /* limit number of dst*/,
652                         DS_SETOP_DSTURI /*set dst uri*/);
653 }
654
655 /**
656  *
657  */
658 static int w_ds_select_domain(struct sip_msg *msg, char *set, char *alg)
659 {
660         return w_ds_select_addr(msg, set, alg, 0 /* limit number of dst*/,
661                         DS_SETOP_RURI /*set host port*/);
662 }
663
664 /**
665  *
666  */
667 static int w_ds_select_domain_limit(
668                 struct sip_msg *msg, char *set, char *alg, char *limit)
669 {
670         return w_ds_select_addr(msg, set, alg, limit /* limit number of dst*/,
671                         DS_SETOP_RURI /*set host port*/);
672 }
673
674 /**
675  *
676  */
677 static int ki_ds_select_routes_limit(sip_msg_t *msg, str *srules, str *smode,
678                 int rlimit)
679 {
680         int i;
681         int vret;
682         int gret;
683         sr_xval_t nxval;
684         ds_select_state_t vstate;
685
686         memset(&vstate, 0, sizeof(ds_select_state_t));
687         vstate.limit = (uint32_t)rlimit;
688         if(vstate.limit == 0) {
689                 LM_DBG("Limit set to 0 - forcing to unlimited\n");
690                 vstate.limit = 0xffffffff;
691         }
692         vret = -1;
693         gret = -1;
694         i = 0;
695         while(i<srules->len) {
696                 vstate.setid = 0;
697                 for(; i<srules->len; i++) {
698                         if(srules->s[i]<'0' || srules->s[i]>'9') {
699                                 if(srules->s[i]=='=') {
700                                         i++;
701                                         break;
702                                 } else {
703                                         LM_ERR("invalid character in [%.*s] at [%d]\n",
704                                                         srules->len, srules->s, i);
705                                         return -1;
706                                 }
707                         }
708                         vstate.setid = (vstate.setid * 10) + (srules->s[i] - '0');
709                 }
710                 vstate.alg = 0;
711                 for(; i<srules->len; i++) {
712                         if(srules->s[i]<'0' || srules->s[i]>'9') {
713                                 if(srules->s[i]==';') {
714                                         i++;
715                                         break;
716                                 } else {
717                                         LM_ERR("invalid character in [%.*s] at [%d]\n",
718                                                         srules->len, srules->s, i);
719                                         return -1;
720                                 }
721                         }
722                         vstate.alg = (vstate.alg * 10) + (srules->s[i] - '0');
723                 }
724                 LM_DBG("routing with setid=%d alg=%d cnt=%d limit=0x%x (%u)\n",
725                         vstate.setid, vstate.alg, vstate.cnt, vstate.limit, vstate.limit);
726
727                 vstate.umode = DS_SETOP_XAVP;
728                 /* if no r-uri/d-uri was set already, keep using the update mode
729                  * specified by the param, then just add to xavps list */
730                 if(vstate.emode==0) {
731                         switch(smode->s[0]) {
732                                 case '0':
733                                 case 'd':
734                                 case 'D':
735                                         vstate.umode = DS_SETOP_DSTURI;
736                                 break;
737                                 case '1':
738                                 case 'r':
739                                 case 'R':
740                                         vstate.umode = DS_SETOP_RURI;
741                                 break;
742                                 case '2':
743                                 case 'x':
744                                 case 'X':
745                                 break;
746                                 default:
747                                         LM_ERR("invalid routing mode parameter: %.*s\n",
748                                                         smode->len, smode->s);
749                                         return -1;
750                         }
751                 }
752                 vret = ds_manage_routes(msg, &vstate);
753                 if(vret<0) {
754                         LM_DBG("failed to select target destinations from %d=%d [%.*s]\n",
755                                         vstate.setid, vstate.alg, srules->len, srules->s);
756                         /* continue to try other target groups */
757                 } else {
758                         if(vret>0) {
759                                 gret = vret;
760                         }
761                 }
762         }
763
764         if(gret<0) {
765                 /* no selection of a target address */
766                 LM_DBG("failed to select any target destinations from [%.*s]\n",
767                                         srules->len, srules->s);
768                 /* return last failure code when trying to select target addresses */
769                 return vret;
770         }
771
772         /* add cnt value to xavp */
773         if(((ds_xavp_ctx_mode & DS_XAVP_CTX_SKIP_CNT)==0)
774                         && (ds_xavp_ctx.len >= 0)) {
775                 /* add to xavp the number of selected dst records */
776                 memset(&nxval, 0, sizeof(sr_xval_t));
777                 nxval.type = SR_XTYPE_INT;
778                 nxval.v.i = vstate.cnt;
779                 if(xavp_add_xavp_value(&ds_xavp_ctx, &ds_xavp_ctx_cnt, &nxval, NULL)==NULL) {
780                         LM_ERR("failed to add cnt value to xavp\n");
781                         return -1;
782                 }
783         }
784
785         LM_DBG("selected target destinations: %d\n", vstate.cnt);
786         return gret;
787 }
788
789 /**
790  *
791  */
792 static int ki_ds_select_routes(sip_msg_t *msg, str *srules, str *smode)
793 {
794         return ki_ds_select_routes_limit(msg, srules, smode, 0);
795 }
796
797 /**
798  *
799  */
800 static int w_ds_select_routes(sip_msg_t *msg, char *lrules, char *umode)
801 {
802         return w_ds_select_routes_limit(msg, lrules, umode, 0);
803 }
804
805 /**
806  *
807  */
808 static int w_ds_select_routes_limit(sip_msg_t *msg, char *lrules, char *umode,
809                 char *rlimit)
810 {
811         str vrules;
812         str vmode;
813         int vlimit;
814
815         if(fixup_get_svalue(msg, (gparam_t*)lrules, &vrules)<0) {
816                 LM_ERR("failed to get routing rules parameter\n");
817                 return -1;
818         }
819         if(fixup_get_svalue(msg, (gparam_t*)umode, &vmode)<0) {
820                 LM_ERR("failed to get update mode parameter\n");
821                 return -1;
822         }
823         if(rlimit!=NULL) {
824                 if(fixup_get_ivalue(msg, (gparam_t*)rlimit, &vlimit)<0) {
825                         LM_ERR("failed to get limit parameter\n");
826                         return -1;
827                 }
828         } else {
829                 vlimit = 0;
830         }
831         return ki_ds_select_routes_limit(msg, &vrules, &vmode, vlimit);
832 }
833
834 /**
835  *
836  */
837 static int w_ds_next_dst(struct sip_msg *msg, char *str1, char *str2)
838 {
839         return ds_update_dst(msg, DS_USE_NEXT, DS_SETOP_DSTURI /*set dst uri*/);
840 }
841
842 /**
843  *
844  */
845 static int w_ds_next_domain(struct sip_msg *msg, char *str1, char *str2)
846 {
847         return ds_update_dst(msg, DS_USE_NEXT, DS_SETOP_RURI /*set host port*/);
848 }
849
850 /**
851  *
852  */
853 static int w_ds_set_dst(struct sip_msg *msg, char *str1, char *str2)
854 {
855         return ds_update_dst(msg, DS_USE_CRT, DS_SETOP_DSTURI /*set dst uri*/);
856 }
857
858 /**
859  *
860  */
861 static int w_ds_set_domain(struct sip_msg *msg, char *str1, char *str2)
862 {
863         return ds_update_dst(msg, DS_USE_CRT, DS_SETOP_RURI /*set host port*/);
864 }
865
866 /**
867  *
868  */
869 static int ki_ds_mark_dst(sip_msg_t *msg)
870 {
871         int state;
872
873         state = DS_INACTIVE_DST;
874         if(ds_probing_mode == DS_PROBE_ALL)
875                 state |= DS_PROBING_DST;
876
877         return ds_mark_dst(msg, state);
878 }
879
880 /**
881  *
882  */
883 static int w_ds_mark_dst0(struct sip_msg *msg, char *str1, char *str2)
884 {
885         return ki_ds_mark_dst(msg);
886 }
887
888 /**
889  *
890  */
891 static int ki_ds_mark_dst_state(sip_msg_t *msg, str *sval)
892 {
893         int state;
894
895         if(sval->s == NULL || sval->len == 0)
896                 return ki_ds_mark_dst(msg);
897
898         state = ds_parse_flags(sval->s, sval->len);
899
900         if(state < 0) {
901                 LM_WARN("Failed to parse state flags: %.*s", sval->len, sval->s);
902                 return -1;
903         }
904
905         return ds_mark_dst(msg, state);
906 }
907
908 /**
909  *
910  */
911 static int w_ds_mark_dst1(struct sip_msg *msg, char *str1, char *str2)
912 {
913         str sval;
914
915         sval.s = str1;
916         sval.len = strlen(str1);
917
918         return ki_ds_mark_dst_state(msg, &sval);
919 }
920
921 /**
922  *
923  */
924 static int w_ds_load_unset(struct sip_msg *msg, char *str1, char *str2)
925 {
926         if(ds_load_unset(msg) < 0)
927                 return -1;
928         return 1;
929 }
930
931 /**
932  *
933  */
934 static int w_ds_load_update(struct sip_msg *msg, char *str1, char *str2)
935 {
936         if(ds_load_update(msg) < 0)
937                 return -1;
938         return 1;
939 }
940
941 /**
942  *
943  */
944 static int ds_warn_fixup(void **param, int param_no)
945 {
946         if(ds_xavp_dst.len<=0 || ds_xavp_ctx.len<=0) {
947                 LM_ERR("failover functions used, but required XAVP parameters"
948                            " are NULL -- feature disabled\n");
949         }
950         return 0;
951 }
952
953 static int ds_reload(sip_msg_t *msg)
954 {
955         if(!ds_db_url.s) {
956                 if(ds_load_list(dslistfile) != 0)
957                         LM_ERR("Error reloading from list\n");
958                 return -1;
959         } else {
960                 if(ds_reload_db() < 0)
961                         LM_ERR("Error reloading from db\n");
962                 return -1;
963         }
964         LM_DBG("reloaded dispatcher\n");
965         return 1;
966 }
967
968
969 static int w_ds_reload(struct sip_msg *msg, char *str1, char *str2)
970 {
971         return ds_reload(msg);
972 }
973
974 static int w_ds_is_from_list0(struct sip_msg *msg, char *str1, char *str2)
975 {
976         return ds_is_from_list(msg, -1);
977 }
978
979 static int ki_ds_is_from_lists(sip_msg_t *msg)
980 {
981         return ds_is_from_list(msg, -1);
982 }
983
984 static int w_ds_is_from_list1(struct sip_msg *msg, char *set, char *str2)
985 {
986         int s;
987         if(fixup_get_ivalue(msg, (gparam_p)set, &s) != 0) {
988                 LM_ERR("cannot get set id value\n");
989                 return -1;
990         }
991         return ds_is_from_list(msg, s);
992 }
993
994 static int w_ds_is_from_list2(struct sip_msg *msg, char *set, char *mode)
995 {
996         int vset;
997         int vmode;
998
999         if(fixup_get_ivalue(msg, (gparam_t *)set, &vset) != 0) {
1000                 LM_ERR("cannot get set id value\n");
1001                 return -1;
1002         }
1003         if(fixup_get_ivalue(msg, (gparam_t *)mode, &vmode) != 0) {
1004                 LM_ERR("cannot get mode value\n");
1005                 return -1;
1006         }
1007
1008         return ds_is_addr_from_list(msg, vset, NULL, vmode);
1009 }
1010
1011 static int ki_ds_is_from_list_mode(sip_msg_t *msg, int vset, int vmode)
1012 {
1013         return ds_is_addr_from_list(msg, vset, NULL, vmode);
1014 }
1015
1016 static int w_ds_is_from_list3(
1017                 struct sip_msg *msg, char *set, char *mode, char *uri)
1018 {
1019         int vset;
1020         int vmode;
1021         str suri;
1022
1023         if(fixup_get_ivalue(msg, (gparam_t *)set, &vset) != 0) {
1024                 LM_ERR("cannot get set id value\n");
1025                 return -1;
1026         }
1027         if(fixup_get_ivalue(msg, (gparam_t *)mode, &vmode) != 0) {
1028                 LM_ERR("cannot get mode value\n");
1029                 return -1;
1030         }
1031         if(fixup_get_svalue(msg, (gparam_t *)uri, &suri) != 0) {
1032                 LM_ERR("cannot get uri value\n");
1033                 return -1;
1034         }
1035
1036         return ds_is_addr_from_list(msg, vset, &suri, vmode);
1037 }
1038
1039 static int ki_ds_is_from_list_uri(sip_msg_t *msg, int vset, int vmode, str *vuri)
1040 {
1041         return ds_is_addr_from_list(msg, vset, vuri, vmode);
1042 }
1043
1044 static int fixup_ds_is_from_list(void **param, int param_no)
1045 {
1046         if(param_no == 1 || param_no == 2)
1047                 return fixup_igp_null(param, 1);
1048         if(param_no == 3)
1049                 return fixup_spve_null(param, 1);
1050         return 0;
1051 }
1052
1053 /* Check if a given set exist in memory */
1054 static int w_ds_list_exist(struct sip_msg *msg, char *param, char *p2)
1055 {
1056         int set;
1057
1058         if(fixup_get_ivalue(msg, (gparam_p)param, &set) != 0) {
1059                 LM_ERR("cannot get set id param value\n");
1060                 return -2;
1061         }
1062         return ds_list_exist(set);
1063 }
1064
1065 static int ki_ds_list_exists(struct sip_msg *msg, int set)
1066 {
1067         return ds_list_exist(set);
1068 }
1069
1070 static int fixup_ds_list_exist(void **param, int param_no)
1071 {
1072         return fixup_igp_null(param, param_no);
1073 }
1074
1075 static int ki_ds_is_active(sip_msg_t *msg, int set)
1076 {
1077         return ds_is_active_uri(msg, set, NULL);
1078 }
1079
1080 static int w_ds_is_active(sip_msg_t *msg, char *pset, char *p2)
1081 {
1082         int vset;
1083
1084         if(fixup_get_ivalue(msg, (gparam_t *)pset, &vset) != 0) {
1085                 LM_ERR("cannot get set id value\n");
1086                 return -1;
1087         }
1088
1089         return ds_is_active_uri(msg, vset, NULL);
1090 }
1091
1092 static int ki_ds_is_active_uri(sip_msg_t *msg, int set, str *uri)
1093 {
1094         return ds_is_active_uri(msg, set, uri);
1095 }
1096
1097 static int w_ds_is_active_uri(sip_msg_t *msg, char *pset, char *puri)
1098 {
1099         int vset;
1100         str suri;
1101
1102         if(fixup_get_ivalue(msg, (gparam_t *)pset, &vset) != 0) {
1103                 LM_ERR("cannot get set id value\n");
1104                 return -1;
1105         }
1106         if(fixup_get_svalue(msg, (gparam_t *)puri, &suri) != 0) {
1107                 LM_ERR("cannot get uri value\n");
1108                 return -1;
1109         }
1110
1111         return ki_ds_is_active_uri(msg, vset, &suri);
1112 }
1113
1114 static int ds_parse_reply_codes()
1115 {
1116         param_t *params_list = NULL;
1117         param_t *pit = NULL;
1118         int list_size = 0;
1119         int i = 0;
1120         int pos = 0;
1121         int code = 0;
1122         str input = {0, 0};
1123         int *ds_ping_reply_codes_new = NULL;
1124         int *ds_ping_reply_codes_old = NULL;
1125
1126         /* Validate String: */
1127         if(cfg_get(dispatcher, dispatcher_cfg, ds_ping_reply_codes_str).s == 0
1128                         || cfg_get(dispatcher, dispatcher_cfg, ds_ping_reply_codes_str).len
1129                                            <= 0)
1130                 return 0;
1131
1132         /* parse_params will modify the string pointer of .s, so we need to make a copy. */
1133         input.s = cfg_get(dispatcher, dispatcher_cfg, ds_ping_reply_codes_str).s;
1134         input.len =
1135                         cfg_get(dispatcher, dispatcher_cfg, ds_ping_reply_codes_str).len;
1136
1137         /* Parse the parameters: */
1138         if(parse_params(&input, CLASS_ANY, 0, &params_list) < 0)
1139                 return -1;
1140
1141         /* Get the number of entries in the list */
1142         for(pit = params_list; pit; pit = pit->next) {
1143                 if(pit->name.len == 4 && strncasecmp(pit->name.s, "code", 4) == 0) {
1144                         str2sint(&pit->body, &code);
1145                         if((code >= 100) && (code < 700))
1146                                 list_size += 1;
1147                 } else if(pit->name.len == 5
1148                                   && strncasecmp(pit->name.s, "class", 5) == 0) {
1149                         str2sint(&pit->body, &code);
1150                         if((code >= 1) && (code < 7))
1151                                 list_size += 100;
1152                 }
1153         }
1154         LM_DBG("Should be %d Destinations.\n", list_size);
1155
1156         if(list_size > 0) {
1157                 /* Allocate Memory for the new list: */
1158                 ds_ping_reply_codes_new = (int *)shm_malloc(list_size * sizeof(int));
1159                 if(ds_ping_reply_codes_new == NULL) {
1160                         free_params(params_list);
1161                         LM_ERR("no more memory\n");
1162                         return -1;
1163                 }
1164
1165                 /* Now create the list of valid reply-codes: */
1166                 for(pit = params_list; pit; pit = pit->next) {
1167                         if(pit->name.len == 4 && strncasecmp(pit->name.s, "code", 4) == 0) {
1168                                 str2sint(&pit->body, &code);
1169                                 if((code >= 100) && (code < 700))
1170                                         ds_ping_reply_codes_new[pos++] = code;
1171                         } else if(pit->name.len == 5
1172                                           && strncasecmp(pit->name.s, "class", 5) == 0) {
1173                                 str2sint(&pit->body, &code);
1174                                 if((code >= 1) && (code < 7)) {
1175                                         /* Add every code from this class, e.g. 100 to 199 */
1176                                         for(i = (code * 100); i <= ((code * 100) + 99); i++)
1177                                                 ds_ping_reply_codes_new[pos++] = i;
1178                                 }
1179                         }
1180                 }
1181         } else {
1182                 ds_ping_reply_codes_new = 0;
1183         }
1184         free_params(params_list);
1185
1186         /* More reply-codes? Change Pointer and then set number of codes. */
1187         if(list_size > *ds_ping_reply_codes_cnt) {
1188                 // Copy Pointer
1189                 ds_ping_reply_codes_old = *ds_ping_reply_codes;
1190                 *ds_ping_reply_codes = ds_ping_reply_codes_new;
1191                 // Done: Set new Number of entries:
1192                 *ds_ping_reply_codes_cnt = list_size;
1193                 // Free the old memory area:
1194                 if(ds_ping_reply_codes_old)
1195                         shm_free(ds_ping_reply_codes_old);
1196                 /* Less or equal? Set the number of codes first. */
1197         } else {
1198                 // Done:
1199                 *ds_ping_reply_codes_cnt = list_size;
1200                 // Copy Pointer
1201                 ds_ping_reply_codes_old = *ds_ping_reply_codes;
1202                 *ds_ping_reply_codes = ds_ping_reply_codes_new;
1203                 // Free the old memory area:
1204                 if(ds_ping_reply_codes_old)
1205                         shm_free(ds_ping_reply_codes_old);
1206         }
1207         /* Print the list as INFO: */
1208         for(i = 0; i < *ds_ping_reply_codes_cnt; i++) {
1209                 LM_DBG("Dispatcher: Now accepting Reply-Code %d (%d/%d) as valid\n",
1210                                 (*ds_ping_reply_codes)[i], (i + 1), *ds_ping_reply_codes_cnt);
1211         }
1212         return 0;
1213 }
1214
1215 int ds_ping_check_rplcode(int code)
1216 {
1217         int i;
1218
1219         for(i = 0; i < *ds_ping_reply_codes_cnt; i++) {
1220                 if((*ds_ping_reply_codes)[i] == code)
1221                         return 1;
1222         }
1223
1224         return 0;
1225 }
1226
1227 void ds_ping_reply_codes_update(str *gname, str *name)
1228 {
1229         ds_parse_reply_codes();
1230 }
1231
1232 /**
1233  *
1234  */
1235 static int pv_get_dsv(sip_msg_t *msg, pv_param_t *param, pv_value_t *res)
1236 {
1237         ds_rctx_t *rctx;
1238
1239         if(param==NULL) {
1240                 return -1;
1241         }
1242         rctx = ds_get_rctx();
1243         if(rctx==NULL) {
1244                 return pv_get_null(msg, param, res);
1245         }
1246         switch(param->pvn.u.isname.name.n)
1247         {
1248                 case 0:
1249                         return pv_get_sintval(msg, param, res, rctx->code);
1250                 case 1:
1251                         if(rctx->reason.s!=NULL && rctx->reason.len>0) {
1252                                 return pv_get_strval(msg, param, res, &rctx->reason);
1253                         }
1254                         return pv_get_null(msg, param, res);
1255                 case 2:
1256                         return pv_get_sintval(msg, param, res, rctx->flags);
1257                 default:
1258                         return pv_get_null(msg, param, res);
1259         }
1260 }
1261
1262 /**
1263  *
1264  */
1265 static int pv_parse_dsv(pv_spec_p sp, str *in)
1266 {
1267         if(sp==NULL || in==NULL || in->len<=0)
1268                 return -1;
1269
1270         switch(in->len)
1271         {
1272                 case 4:
1273                         if(strncmp(in->s, "code", 4)==0)
1274                                 sp->pvp.pvn.u.isname.name.n = 0;
1275                         else goto error;
1276                 break;
1277                 case 5:
1278                         if(strncmp(in->s, "flags", 5)==0)
1279                                 sp->pvp.pvn.u.isname.name.n = 2;
1280                         else goto error;
1281                 break;
1282                 case 6:
1283                         if(strncmp(in->s, "reason", 6)==0)
1284                                 sp->pvp.pvn.u.isname.name.n = 1;
1285                         else goto error;
1286                 break;
1287                 default:
1288                         goto error;
1289         }
1290         sp->pvp.pvn.type = PV_NAME_INTSTR;
1291         sp->pvp.pvn.u.isname.type = 0;
1292
1293         return 0;
1294
1295 error:
1296         LM_ERR("unknown PV key: %.*s\n", in->len, in->s);
1297         return -1;
1298 }
1299
1300 /* KEMI wrappers */
1301 /**
1302  *
1303  */
1304 static int ki_ds_select(sip_msg_t *msg, int set, int alg)
1305 {
1306         return ds_select_dst_limit(msg, set, alg, 0xffff /* limit number of dst*/,
1307                         2 /*set no dst/uri*/);
1308 }
1309
1310 /**
1311  *
1312  */
1313 static int ki_ds_select_limit(sip_msg_t *msg, int set, int alg, int limit)
1314 {
1315         return ds_select_dst_limit(msg, set, alg, limit /* limit number of dst*/,
1316                         2 /*set no dst/uri*/);
1317 }
1318
1319 /**
1320  *
1321  */
1322 static int ki_ds_select_dst(sip_msg_t *msg, int set, int alg)
1323 {
1324         return ds_select_dst_limit(msg, set, alg, 0xffff /* limit number of dst*/,
1325                         0 /*set dst uri*/);
1326 }
1327
1328 /**
1329  *
1330  */
1331 static int ki_ds_select_dst_limit(sip_msg_t *msg, int set, int alg, int limit)
1332 {
1333         return ds_select_dst_limit(msg, set, alg, limit /* limit number of dst*/,
1334                         0 /*set dst uri*/);
1335 }
1336
1337 /**
1338  *
1339  */
1340 static int ki_ds_select_domain(sip_msg_t *msg, int set, int alg)
1341 {
1342         return ds_select_dst_limit(msg, set, alg, 0xffff /* limit number of dst*/,
1343                         1 /*set host port*/);
1344 }
1345
1346 /**
1347  *
1348  */
1349 static int ki_ds_select_domain_limit(sip_msg_t *msg, int set, int alg, int limit)
1350 {
1351         return ds_select_dst_limit(msg, set, alg, limit /* limit number of dst*/,
1352                         1 /*set host port*/);
1353 }
1354
1355 /**
1356  *
1357  */
1358 static int ki_ds_next_dst(sip_msg_t *msg)
1359 {
1360         return ds_update_dst(msg, DS_USE_NEXT, DS_SETOP_DSTURI /*set dst uri*/);
1361 }
1362
1363 /**
1364  *
1365  */
1366 static int ki_ds_next_domain(sip_msg_t *msg)
1367 {
1368         return ds_update_dst(msg, DS_USE_NEXT, DS_SETOP_RURI /*set host port*/);
1369 }
1370
1371 /**
1372  *
1373  */
1374 static int ki_ds_set_dst(sip_msg_t *msg)
1375 {
1376         return ds_update_dst(msg, DS_USE_CRT, DS_SETOP_DSTURI /*set dst uri*/);
1377 }
1378
1379 /**
1380  *
1381  */
1382 static int ki_ds_set_domain(sip_msg_t *msg)
1383 {
1384         return ds_update_dst(msg, DS_USE_CRT, DS_SETOP_RURI /*set host port*/);
1385 }
1386
1387 /**
1388  *
1389  */
1390 /* clang-format off */
1391 static sr_kemi_t sr_kemi_dispatcher_exports[] = {
1392         { str_init("dispatcher"), str_init("ds_select"),
1393                 SR_KEMIP_INT, ki_ds_select,
1394                 { SR_KEMIP_INT, SR_KEMIP_INT, SR_KEMIP_NONE,
1395                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1396         },
1397         { str_init("dispatcher"), str_init("ds_select_limit"),
1398                 SR_KEMIP_INT, ki_ds_select_limit,
1399                 { SR_KEMIP_INT, SR_KEMIP_INT, SR_KEMIP_INT,
1400                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1401         },
1402         { str_init("dispatcher"), str_init("ds_select_domain"),
1403                 SR_KEMIP_INT, ki_ds_select_domain,
1404                 { SR_KEMIP_INT, SR_KEMIP_INT, SR_KEMIP_NONE,
1405                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1406         },
1407         { str_init("dispatcher"), str_init("ds_select_domain_limit"),
1408                 SR_KEMIP_INT, ki_ds_select_domain_limit,
1409                 { SR_KEMIP_INT, SR_KEMIP_INT, SR_KEMIP_INT,
1410                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1411         },
1412         { str_init("dispatcher"), str_init("ds_next_domain"),
1413                 SR_KEMIP_INT, ki_ds_next_domain,
1414                 { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
1415                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1416         },
1417         { str_init("dispatcher"), str_init("ds_set_domain"),
1418                 SR_KEMIP_INT, ki_ds_set_domain,
1419                 { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
1420                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1421         },
1422         { str_init("dispatcher"), str_init("ds_select_dst"),
1423                 SR_KEMIP_INT, ki_ds_select_dst,
1424                 { SR_KEMIP_INT, SR_KEMIP_INT, SR_KEMIP_NONE,
1425                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1426         },
1427         { str_init("dispatcher"), str_init("ds_select_dst_limit"),
1428                 SR_KEMIP_INT, ki_ds_select_dst_limit,
1429                 { SR_KEMIP_INT, SR_KEMIP_INT, SR_KEMIP_INT,
1430                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1431         },
1432         { str_init("dispatcher"), str_init("ds_select_routes"),
1433                 SR_KEMIP_INT, ki_ds_select_routes,
1434                 { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
1435                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1436         },
1437         { str_init("dispatcher"), str_init("ds_select_routes_limit"),
1438                 SR_KEMIP_INT, ki_ds_select_routes_limit,
1439                 { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_INT,
1440                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1441         },
1442         { str_init("dispatcher"), str_init("ds_next_dst"),
1443                 SR_KEMIP_INT, ki_ds_next_dst,
1444                 { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
1445                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1446         },
1447         { str_init("dispatcher"), str_init("ds_set_dst"),
1448                 SR_KEMIP_INT, ki_ds_set_dst,
1449                 { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
1450                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1451         },
1452         { str_init("dispatcher"), str_init("ds_mark_dst"),
1453                 SR_KEMIP_INT, ki_ds_mark_dst,
1454                 { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
1455                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1456         },
1457         { str_init("dispatcher"), str_init("ds_mark_dst_state"),
1458                 SR_KEMIP_INT, ki_ds_mark_dst_state,
1459                 { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
1460                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1461         },
1462         { str_init("dispatcher"), str_init("ds_is_from_lists"),
1463                 SR_KEMIP_INT, ki_ds_is_from_lists,
1464                 { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
1465                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1466         },
1467         { str_init("dispatcher"), str_init("ds_is_from_list"),
1468                 SR_KEMIP_INT, ds_is_from_list,
1469                 { SR_KEMIP_INT, SR_KEMIP_NONE, SR_KEMIP_NONE,
1470                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1471         },
1472         { str_init("dispatcher"), str_init("ds_is_from_list_mode"),
1473                 SR_KEMIP_INT, ki_ds_is_from_list_mode,
1474                 { SR_KEMIP_INT, SR_KEMIP_INT, SR_KEMIP_NONE,
1475                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1476         },
1477         { str_init("dispatcher"), str_init("ds_is_from_list_uri"),
1478                 SR_KEMIP_INT, ki_ds_is_from_list_uri,
1479                 { SR_KEMIP_INT, SR_KEMIP_INT, SR_KEMIP_STR,
1480                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1481         },
1482         { str_init("dispatcher"), str_init("ds_load_update"),
1483                 SR_KEMIP_INT, ds_load_update,
1484                 { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
1485                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1486         },
1487         { str_init("dispatcher"), str_init("ds_load_unset"),
1488                 SR_KEMIP_INT, ds_load_unset,
1489                 { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
1490                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1491         },
1492         { str_init("dispatcher"), str_init("ds_reload"),
1493                 SR_KEMIP_INT, ds_reload,
1494                 { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
1495                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1496         },
1497         { str_init("dispatcher"), str_init("ds_list_exists"),
1498                 SR_KEMIP_INT, ki_ds_list_exists,
1499                 { SR_KEMIP_INT, SR_KEMIP_NONE, SR_KEMIP_NONE,
1500                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1501         },
1502         { str_init("dispatcher"), str_init("ds_is_active"),
1503                 SR_KEMIP_INT, ki_ds_is_active,
1504                 { SR_KEMIP_INT, SR_KEMIP_NONE, SR_KEMIP_NONE,
1505                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1506         },
1507         { str_init("dispatcher"), str_init("ds_is_active_uri"),
1508                 SR_KEMIP_INT, ki_ds_is_active_uri,
1509                 { SR_KEMIP_INT, SR_KEMIP_STR, SR_KEMIP_NONE,
1510                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1511         },
1512         { {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } }
1513 };
1514 /* clang-format on */
1515
1516 /**
1517  *
1518  */
1519 int mod_register(char *path, int *dlflags, void *p1, void *p2)
1520 {
1521         sr_kemi_modules_add(sr_kemi_dispatcher_exports);
1522         return 0;
1523 }
1524
1525 /*** RPC implementation ***/
1526
1527 static const char *dispatcher_rpc_reload_doc[2] = {
1528                 "Reload dispatcher destination sets", 0};
1529
1530
1531 /*
1532  * RPC command to reload dispatcher destination sets
1533  */
1534 static void dispatcher_rpc_reload(rpc_t *rpc, void *ctx)
1535 {
1536
1537         if(ds_rpc_reload_time==NULL) {
1538                 LM_ERR("not ready for reload\n");
1539                 rpc->fault(ctx, 500, "Not ready for reload");
1540                 return;
1541         }
1542         if(*ds_rpc_reload_time!=0 && *ds_rpc_reload_time > time(NULL) - ds_reload_delta) {
1543                 LM_ERR("ongoing reload\n");
1544                 rpc->fault(ctx, 500, "Ongoing reload");
1545                 return;
1546         }
1547         *ds_rpc_reload_time = time(NULL);
1548
1549         if(!ds_db_url.s) {
1550                 if(ds_load_list(dslistfile) != 0) {
1551                         rpc->fault(ctx, 500, "Reload Failed");
1552                         return;
1553                 }
1554         } else {
1555                 if(ds_reload_db() < 0) {
1556                         rpc->fault(ctx, 500, "Reload Failed");
1557                         return;
1558                 }
1559         }
1560         return;
1561 }
1562
1563
1564 static const char *dispatcher_rpc_list_doc[2] = {
1565                 "Return the content of dispatcher sets", 0};
1566
1567 /**
1568  *
1569  */
1570 int ds_rpc_print_set(ds_set_t *node, rpc_t *rpc, void *ctx, void *rpc_handle)
1571 {
1572         int i = 0, rc = 0;
1573         void *rh;
1574         void *sh;
1575         void *vh;
1576         void *wh;
1577         void *lh;
1578         void *dh;
1579         int j;
1580         char c[3];
1581         str data = STR_NULL;
1582
1583         if(!node)
1584                 return 0;
1585
1586         for(; i < 2; ++i) {
1587                 rc = ds_rpc_print_set(node->next[i], rpc, ctx, rpc_handle);
1588                 if(rc != 0)
1589                         return rc;
1590         }
1591
1592         if(rpc->struct_add(rpc_handle, "{", "SET", &sh) < 0) {
1593                 rpc->fault(ctx, 500, "Internal error set structure");
1594                 return -1;
1595         }
1596         if(rpc->struct_add(sh, "d[", "ID", node->id, "TARGETS", &rh) < 0) {
1597                 rpc->fault(ctx, 500, "Internal error creating set id");
1598                 return -1;
1599         }
1600
1601         for(j = 0; j < node->nr; j++) {
1602                 if(rpc->struct_add(rh, "{", "DEST", &vh) < 0) {
1603                         rpc->fault(ctx, 500, "Internal error creating dest");
1604                         return -1;
1605                 }
1606
1607                 memset(&c, 0, sizeof(c));
1608                 if(node->dlist[j].flags & DS_INACTIVE_DST)
1609                         c[0] = 'I';
1610                 else if(node->dlist[j].flags & DS_DISABLED_DST)
1611                         c[0] = 'D';
1612                 else if(node->dlist[j].flags & DS_TRYING_DST)
1613                         c[0] = 'T';
1614                 else
1615                         c[0] = 'A';
1616
1617                 if(node->dlist[j].flags & DS_PROBING_DST)
1618                         c[1] = 'P';
1619                 else
1620                         c[1] = 'X';
1621
1622                 if(node->dlist[j].attrs.body.s) {
1623                         if(rpc->struct_add(vh, "Ssd{", "URI", &node->dlist[j].uri,
1624                                         "FLAGS", c,
1625                                         "PRIORITY", node->dlist[j].priority,
1626                                         "ATTRS", &wh) < 0) {
1627                                 rpc->fault(ctx, 500, "Internal error creating dest struct");
1628                                 return -1;
1629                         }
1630                         if(rpc->struct_add(wh, "SSdddSSS",
1631                                                 "BODY", &(node->dlist[j].attrs.body),
1632                                                 "DUID", (node->dlist[j].attrs.duid.s)
1633                                                                         ? &(node->dlist[j].attrs.duid) : &data,
1634                                                 "MAXLOAD", node->dlist[j].attrs.maxload,
1635                                                 "WEIGHT", node->dlist[j].attrs.weight,
1636                                                 "RWEIGHT", node->dlist[j].attrs.rweight,
1637                                                 "SOCKET", (node->dlist[j].attrs.socket.s)
1638                                                                         ? &(node->dlist[j].attrs.socket) : &data,
1639                                                 "SOCKNAME", (node->dlist[j].attrs.sockname.s)
1640                                                                         ? &(node->dlist[j].attrs.sockname) : &data,
1641                                                 "OBPROXY", (node->dlist[j].attrs.obproxy.s)
1642                                                                         ? &(node->dlist[j].attrs.obproxy) : &data)
1643                                         < 0) {
1644                                 rpc->fault(ctx, 500, "Internal error creating attrs struct");
1645                                 return -1;
1646                         }
1647                 } else {
1648                         if(rpc->struct_add(vh, "Ssd", "URI", &node->dlist[j].uri, "FLAGS",
1649                                            c, "PRIORITY", node->dlist[j].priority)
1650                                         < 0) {
1651                                 rpc->fault(ctx, 500, "Internal error creating dest struct");
1652                                 return -1;
1653                         }
1654                 }
1655                 if (ds_ping_latency_stats) {
1656                         if(rpc->struct_add(vh, "{", "LATENCY", &lh) < 0) {
1657                                 rpc->fault(ctx, 500, "Internal error creating dest");
1658                                 return -1;
1659                         }
1660                         if (rpc->struct_add(lh, "fffdd", "AVG", node->dlist[j].latency_stats.average,
1661                                           "STD", node->dlist[j].latency_stats.stdev,
1662                                           "EST", node->dlist[j].latency_stats.estimate,
1663                                           "MAX", node->dlist[j].latency_stats.max,
1664                                           "TIMEOUT", node->dlist[j].latency_stats.timeout)
1665                                         < 0) {
1666                                 rpc->fault(ctx, 500, "Internal error creating dest struct");
1667                                 return -1;
1668                         }
1669                 }
1670                 if (ds_hash_size>0) {
1671                         if(rpc->struct_add(vh, "{", "RUNTIME", &dh) < 0) {
1672                                 rpc->fault(ctx, 500, "Internal error creating runtime struct");
1673                                 return -1;
1674                         }
1675                         if (rpc->struct_add(dh, "d", "DLGLOAD", node->dlist[j].dload) < 0) {
1676                                 rpc->fault(ctx, 500, "Internal error creating runtime attrs");
1677                                 return -1;
1678                         }
1679                 }
1680         }
1681
1682         return 0;
1683 }
1684
1685 /*
1686  * RPC command to print dispatcher destination sets
1687  */
1688 static void dispatcher_rpc_list(rpc_t *rpc, void *ctx)
1689 {
1690         void *th;
1691         void *ih;
1692
1693         ds_set_t *dslist = ds_get_list();
1694         int dslistnr = ds_get_list_nr();
1695
1696         if(dslist == NULL || dslistnr <= 0) {
1697                 LM_DBG("no destination sets\n");
1698                 rpc->fault(ctx, 500, "No Destination Sets");
1699                 return;
1700         }
1701
1702         /* add entry node */
1703         if(rpc->add(ctx, "{", &th) < 0) {
1704                 rpc->fault(ctx, 500, "Internal error root reply");
1705                 return;
1706         }
1707         if(rpc->struct_add(th, "d[", "NRSETS", dslistnr, "RECORDS", &ih) < 0) {
1708                 rpc->fault(ctx, 500, "Internal error sets structure");
1709                 return;
1710         }
1711
1712         ds_rpc_print_set(dslist, rpc, ctx, ih);
1713
1714         return;
1715 }
1716
1717
1718 /*
1719  * RPC command to set the state of a destination address or duid
1720  */
1721 static void dispatcher_rpc_set_state_helper(rpc_t *rpc, void *ctx, int mattr)
1722 {
1723         int group;
1724         str dest;
1725         str state;
1726         int stval;
1727
1728         if(rpc->scan(ctx, ".SdS", &state, &group, &dest) < 3) {
1729                 rpc->fault(ctx, 500, "Invalid Parameters");
1730                 return;
1731         }
1732         if(state.len <= 0 || state.s == NULL) {
1733                 LM_ERR("bad state value\n");
1734                 rpc->fault(ctx, 500, "Invalid State Parameter");
1735                 return;
1736         }
1737
1738         stval = 0;
1739         if(state.s[0] == '0' || state.s[0] == 'I' || state.s[0] == 'i') {
1740                 /* set inactive */
1741                 stval |= DS_INACTIVE_DST;
1742                 if((state.len > 1) && (state.s[1] == 'P' || state.s[1] == 'p'))
1743                         stval |= DS_PROBING_DST;
1744         } else if(state.s[0] == '1' || state.s[0] == 'A' || state.s[0] == 'a') {
1745                 /* set active */
1746                 if((state.len > 1) && (state.s[1] == 'P' || state.s[1] == 'p'))
1747                         stval |= DS_PROBING_DST;
1748         } else if(state.s[0] == '2' || state.s[0] == 'D' || state.s[0] == 'd') {
1749                 /* set disabled */
1750                 stval |= DS_DISABLED_DST;
1751         } else if(state.s[0] == '3' || state.s[0] == 'T' || state.s[0] == 't') {
1752                 /* set trying */
1753                 stval |= DS_TRYING_DST;
1754                 if((state.len > 1) && (state.s[1] == 'P' || state.s[1] == 'p'))
1755                         stval |= DS_PROBING_DST;
1756         } else {
1757                 LM_ERR("unknown state value\n");
1758                 rpc->fault(ctx, 500, "Unknown State Value");
1759                 return;
1760         }
1761
1762         if(dest.len == 3 && strncmp(dest.s, "all", 3) == 0) {
1763                 ds_reinit_state_all(group, stval);
1764         } else {
1765                 if (mattr==1) {
1766                         if(ds_reinit_duid_state(group, &dest, stval) < 0) {
1767                                 rpc->fault(ctx, 500, "State Update Failed");
1768                                 return;
1769                         }
1770                 } else {
1771                         if(ds_reinit_state(group, &dest, stval) < 0) {
1772                                 rpc->fault(ctx, 500, "State Update Failed");
1773                                 return;
1774                         }
1775                 }
1776         }
1777
1778         return;
1779 }
1780
1781
1782 static const char *dispatcher_rpc_set_state_doc[2] = {
1783                 "Set the state of a destination by address", 0};
1784
1785 /*
1786  * RPC command to set the state of a destination address
1787  */
1788 static void dispatcher_rpc_set_state(rpc_t *rpc, void *ctx)
1789 {
1790         dispatcher_rpc_set_state_helper(rpc, ctx, 0);
1791 }
1792
1793 static const char *dispatcher_rpc_set_duid_state_doc[2] = {
1794                 "Set the state of a destination by duid", 0};
1795
1796 /*
1797  * RPC command to set the state of a destination duid
1798  */
1799 static void dispatcher_rpc_set_duid_state(rpc_t *rpc, void *ctx)
1800 {
1801         dispatcher_rpc_set_state_helper(rpc, ctx, 1);
1802 }
1803
1804 static const char *dispatcher_rpc_ping_active_doc[2] = {
1805                 "Manage setting on/off the pinging (keepalive) of destinations", 0};
1806
1807
1808 /*
1809  * RPC command to set the state of a destination address
1810  */
1811 static void dispatcher_rpc_ping_active(rpc_t *rpc, void *ctx)
1812 {
1813         int state;
1814         int ostate;
1815         void *th;
1816
1817         if(rpc->scan(ctx, "*d", &state) != 1) {
1818                 state = -1;
1819         }
1820         ostate = ds_ping_active_get();
1821         /* add entry node */
1822         if(rpc->add(ctx, "{", &th) < 0) {
1823                 rpc->fault(ctx, 500, "Internal error root reply");
1824                 return;
1825         }
1826         if(state == -1) {
1827                 if(rpc->struct_add(th, "d", "OldPingState", ostate) < 0) {
1828                         rpc->fault(ctx, 500, "Internal error reply structure");
1829                         return;
1830                 }
1831                 return;
1832         }
1833
1834         if(ds_ping_active_set(state) < 0) {
1835                 rpc->fault(ctx, 500, "Ping State Update Failed");
1836                 return;
1837         }
1838         if(rpc->struct_add(th, "dd", "NewPingState", state, "OldPingState", ostate)
1839                         < 0) {
1840                 rpc->fault(ctx, 500, "Internal error reply structure");
1841                 return;
1842         }
1843         return;
1844 }
1845
1846 static const char *dispatcher_rpc_add_doc[2] = {
1847                 "Add a destination address in memory", 0};
1848
1849
1850 /*
1851  * RPC command to add a destination address to memory
1852  */
1853 static void dispatcher_rpc_add(rpc_t *rpc, void *ctx)
1854 {
1855         int group, flags, nparams;
1856         str dest;
1857         str attrs = STR_NULL;
1858
1859         flags = 0;
1860
1861         nparams = rpc->scan(ctx, "dS*dS", &group, &dest, &flags, &attrs);
1862         if(nparams < 2) {
1863                 rpc->fault(ctx, 500, "Invalid Parameters");
1864                 return;
1865         } else if (nparams <= 3) {
1866                 attrs.s = 0;
1867                 attrs.len = 0;
1868         }
1869
1870         if(ds_add_dst(group, &dest, flags, &attrs) != 0) {
1871                 rpc->fault(ctx, 500, "Adding dispatcher dst failed");
1872                 return;
1873         }
1874
1875         return;
1876 }
1877
1878 static const char *dispatcher_rpc_remove_doc[2] = {
1879                 "Remove a destination address from memory", 0};
1880
1881
1882 /*
1883  * RPC command to remove a destination address from memory
1884  */
1885 static void dispatcher_rpc_remove(rpc_t *rpc, void *ctx)
1886 {
1887         int group;
1888         str dest;
1889
1890         if(rpc->scan(ctx, "dS", &group, &dest) < 2) {
1891                 rpc->fault(ctx, 500, "Invalid Parameters");
1892                 return;
1893         }
1894
1895         if(ds_remove_dst(group, &dest) != 0) {
1896                 rpc->fault(ctx, 500, "Removing dispatcher dst failed");
1897                 return;
1898         }
1899
1900         return;
1901 }
1902
1903 static const char *dispatcher_rpc_hash_doc[2] = {
1904                 "Compute the hash if the values", 0};
1905
1906
1907 /*
1908  * RPC command to compute the hash of the values
1909  */
1910 static void dispatcher_rpc_hash(rpc_t *rpc, void *ctx)
1911 {
1912         int n = 0;
1913         unsigned int hashid = 0;
1914         int nslots = 0;
1915         str val1 = STR_NULL;
1916         str val2 = STR_NULL;
1917         void *th;
1918
1919         n = rpc->scan(ctx, "dS*S", &nslots, &val1, &val2);
1920         if(n < 2) {
1921                 rpc->fault(ctx, 500, "Invalid Parameters");
1922                 return;
1923         }
1924         if(n==2) {
1925                 val2.s = NULL;
1926                 val2.s = 0;
1927         }
1928
1929         hashid = ds_get_hash(&val1, &val2);
1930
1931         /* add entry node */
1932         if(rpc->add(ctx, "{", &th) < 0) {
1933                 rpc->fault(ctx, 500, "Internal error root reply");
1934                 return;
1935         }
1936         if(rpc->struct_add(th, "uu", "hashid", hashid,
1937                                 "slot", (nslots>0)?(hashid%nslots):0)
1938                         < 0) {
1939                 rpc->fault(ctx, 500, "Internal error reply structure");
1940                 return;
1941         }
1942
1943         return;
1944 }
1945
1946 /* clang-format off */
1947 rpc_export_t dispatcher_rpc_cmds[] = {
1948         {"dispatcher.reload", dispatcher_rpc_reload,
1949                 dispatcher_rpc_reload_doc, 0},
1950         {"dispatcher.list",   dispatcher_rpc_list,
1951                 dispatcher_rpc_list_doc,   0},
1952         {"dispatcher.set_state",   dispatcher_rpc_set_state,
1953                 dispatcher_rpc_set_state_doc,   0},
1954         {"dispatcher.set_duid_state",   dispatcher_rpc_set_duid_state,
1955                 dispatcher_rpc_set_duid_state_doc,   0},
1956         {"dispatcher.ping_active",   dispatcher_rpc_ping_active,
1957                 dispatcher_rpc_ping_active_doc, 0},
1958         {"dispatcher.add",   dispatcher_rpc_add,
1959                 dispatcher_rpc_add_doc, 0},
1960         {"dispatcher.remove",   dispatcher_rpc_remove,
1961                 dispatcher_rpc_remove_doc, 0},
1962         {"dispatcher.hash",   dispatcher_rpc_hash,
1963                 dispatcher_rpc_hash_doc, 0},
1964         {0, 0, 0, 0}
1965 };
1966 /* clang-format on */
1967
1968 /**
1969  * register RPC commands
1970  */
1971 static int ds_init_rpc(void)
1972 {
1973         if(rpc_register_array(dispatcher_rpc_cmds) != 0) {
1974                 LM_ERR("failed to register RPC commands\n");
1975                 return -1;
1976         }
1977         return 0;
1978 }