src/modules/rtpengine/rtpengine.c
0076763e
 /*
ddea262f
  * Copyright (C) 2003-2008 Sippy Software, Inc., http://www.sippysoft.com
0076763e
  * Copyright (C) 2014-2015 Sipwise GmbH, http://www.sipwise.com
ddea262f
  *
  * This file is part of Kamailio, a free SIP server.
  *
  * Kamailio is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version
  *
  * Kamailio is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
ddea262f
  */
 
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/time.h>
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
 #ifndef __USE_BSD
 #define  __USE_BSD
 #endif
 #include <netinet/ip.h>
 #ifndef __FAVOR_BSD
 #define __FAVOR_BSD
 #endif
 #include <netinet/udp.h>
 #include <arpa/inet.h>
 #include <sys/uio.h>
 #include <sys/un.h>
 #include <ctype.h>
 #include <errno.h>
 #include <netdb.h>
 #include <poll.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
3f42d6bc
 #include <ifaddrs.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
ddea262f
 
cf83221d
 #include "../../core/flags.h"
 #include "../../core/sr_module.h"
 #include "../../core/dprint.h"
 #include "../../core/data_lump.h"
 #include "../../core/data_lump_rpl.h"
 #include "../../core/error.h"
 #include "../../core/forward.h"
 #include "../../core/mem/mem.h"
 #include "../../core/parser/parse_from.h"
 #include "../../core/parser/parse_to.h"
 #include "../../core/parser/parse_uri.h"
 #include "../../core/parser/parser_f.h"
 #include "../../core/parser/sdp/sdp.h"
 #include "../../core/resolve.h"
 #include "../../core/timer.h"
 #include "../../core/trim.h"
 #include "../../core/ut.h"
 #include "../../core/pt.h"
 #include "../../core/timer_proc.h"
 #include "../../core/pvar.h"
 #include "../../core/lvalue.h"
 #include "../../core/msg_translator.h"
 #include "../../core/usr_avp.h"
 #include "../../core/socket_info.h"
 #include "../../core/mod_fix.h"
 #include "../../core/dset.h"
 #include "../../core/route.h"
9b5d2b50
 #include "../../core/rpc.h"
 #include "../../core/rpc_lookup.h"
152f46dd
 #include "../../core/kemi.h"
2baa05ad
 #include "../../core/char_msg_val.h"
697f34d4
 #include "../../core/utils/srjson.h"
ddea262f
 #include "../../modules/tm/tm_load.h"
a508f1a3
 #include "../../modules/crypto/api.h"
d37b84be
 #include "../../modules/lwsc/api.h"
7526431e
 #include "rtpengine.h"
 #include "rtpengine_funcs.h"
2625ab3c
 #include "rtpengine_hash.h"
ddea262f
 #include "bencode.h"
43451ec4
 #include "config.h"
ddea262f
 
 MODULE_VERSION
 
 #if !defined(AF_LOCAL)
 #define	AF_LOCAL AF_UNIX
 #endif
 #if !defined(PF_LOCAL)
 #define	PF_LOCAL PF_UNIX
 #endif
 
 /* NAT UAC test constants */
98877b5b
 #define	NAT_UAC_TEST_C_1918			0x01
 #define	NAT_UAC_TEST_RCVD			0x02
 #define	NAT_UAC_TEST_V_1918			0x04
 #define	NAT_UAC_TEST_S_1918			0x08
 #define	NAT_UAC_TEST_RPORT			0x10
ddea262f
 
69e57b92
 #define COOKIE_SIZE					128
 #define HOSTNAME_SIZE				100
ddea262f
 
98877b5b
 #define DEFAULT_RTPP_SET_ID			0
1190ac60
 
 enum {
 	RPC_FOUND_ALL = 2,
 	RPC_FOUND_ONE = 1,
 	RPC_FOUND_NONE = 0,
 };
ddea262f
 
98877b5b
 #define	CPORT					"22222"
ddea262f
 
7526431e
 struct ng_flags_parse {
76b1f3bf
 	int via, to, packetize, transport, directional;
e87cad92
 	bencode_item_t *dict, *flags, *direction, *replace, *rtcp_mux, *sdes,
6ab14314
 		       *t38,
8bf1935c
 		       *codec, *codec_strip, *codec_offer, *codec_transcode, *codec_mask,
 		       *codec_set, *codec_except;
e69579bc
 	str call_id, from_tag, to_tag;
7526431e
 };
 
ddea262f
 static const char *command_strings[] = {
eba7dcbe
 	[OP_OFFER]		= "offer",
 	[OP_ANSWER]		= "answer",
 	[OP_DELETE]		= "delete",
 	[OP_START_RECORDING]	= "start recording",
 	[OP_QUERY]		= "query",
a309fe46
 	[OP_PING]		= "ping",
ce5362dc
 	[OP_STOP_RECORDING]	= "stop recording",
e528fce4
 	[OP_BLOCK_DTMF]		= "block DTMF",
 	[OP_UNBLOCK_DTMF]	= "unblock DTMF",
76b1f3bf
 	[OP_BLOCK_MEDIA]	= "block media",
 	[OP_UNBLOCK_MEDIA]	= "unblock media",
12470055
 	[OP_SILENCE_MEDIA]	= "silence media",
 	[OP_UNSILENCE_MEDIA]	= "unsilence media",
558780f7
 	[OP_START_FORWARDING]	= "start forwarding",
 	[OP_STOP_FORWARDING]	= "stop forwarding",
639c29f2
 	[OP_PLAY_MEDIA]		= "play media",
 	[OP_STOP_MEDIA]		= "stop media",
120a71be
 	[OP_PLAY_DTMF]		= "play DTMF",
ddea262f
 };
 
1aa2e58f
 struct minmax_mos_stats {
 	str mos_param;
 	str at_param;
 	str packetloss_param;
 	str jitter_param;
 	str roundtrip_param;
26a7b41f
 	str roundtrip_leg_param;
1aa2e58f
 	str samples_param;
 
 	pv_elem_t *mos_pv;
 	pv_elem_t *at_pv;
 	pv_elem_t *packetloss_pv;
 	pv_elem_t *jitter_pv;
 	pv_elem_t *roundtrip_pv;
26a7b41f
 	pv_elem_t *roundtrip_leg_pv;
1aa2e58f
 	pv_elem_t *samples_pv;
 };
 struct minmax_mos_label_stats {
 	int got_any_pvs;
 
 	str label_param;
 	pv_elem_t *label_pv;
 
 	struct minmax_mos_stats min,
 				max,
 				average;
 };
 struct minmax_stats_vals {
 	long long mos;
 	long long at;
 	long long packetloss;
 	long long jitter;
 	long long roundtrip;
26a7b41f
 	long long roundtrip_leg;
1aa2e58f
 	long long samples;
 	long long avg_samples; /* our own running count to average the averages */
 };
 
785ede5b
 #define RTPE_LIST_VERSION_DELAY	10
 
 typedef struct rtpe_list_version {
 	int vernum;
 	time_t vertime;
 } rtpe_list_version_t;
 
 static rtpe_list_version_t *_rtpe_list_version = NULL;
 static int _rtpe_list_vernum_local = 0;
 
ddea262f
 static char *gencookie();
 static int rtpp_test(struct rtpp_node*, int, int);
 static int start_recording_f(struct sip_msg *, char *, char *);
ce5362dc
 static int stop_recording_f(struct sip_msg *, char *, char *);
e528fce4
 static int block_dtmf_f(struct sip_msg *, char *, char *);
 static int unblock_dtmf_f(struct sip_msg *, char *, char *);
76b1f3bf
 static int block_media_f(struct sip_msg *, char *, char *);
 static int unblock_media_f(struct sip_msg *, char *, char *);
12470055
 static int silence_media_f(struct sip_msg *, char *, char *);
 static int unsilence_media_f(struct sip_msg *, char *, char *);
558780f7
 static int start_forwarding_f(struct sip_msg *, char *, char *);
 static int stop_forwarding_f(struct sip_msg *, char *, char *);
639c29f2
 static int play_media_f(struct sip_msg *, char *, char *);
 static int stop_media_f(struct sip_msg *, char *, char *);
120a71be
 static int play_dtmf_f(struct sip_msg *, char *, char *);
7526431e
 static int rtpengine_answer1_f(struct sip_msg *, char *, char *);
 static int rtpengine_offer1_f(struct sip_msg *, char *, char *);
 static int rtpengine_delete1_f(struct sip_msg *, char *, char *);
 static int rtpengine_manage1_f(struct sip_msg *, char *, char *);
1aa2e58f
 static int rtpengine_query1_f(struct sip_msg *, char *, char *);
ffee45da
 static int rtpengine_info1_f(struct sip_msg *, char *, char *);
7526431e
 
697f34d4
 static int w_rtpengine_query_v(sip_msg_t *msg, char *pfmt, char *pvar);
 static int fixup_rtpengine_query_v(void **param, int param_no);
 static int fixup_free_rtpengine_query_v(void **param, int param_no);
 
7526431e
 static int parse_flags(struct ng_flags_parse *, struct sip_msg *, enum rtpe_operation *, const char *);
 
2baa05ad
 static int rtpengine_offer_answer(struct sip_msg *msg, const char *flags, enum rtpe_operation op, int more);
ddea262f
 static int fixup_set_id(void ** param, int param_no);
7526431e
 static int set_rtpengine_set_f(struct sip_msg * msg, char * str1, char * str2);
4b0095fb
 static struct rtpp_set * select_rtpp_set(unsigned int id_set);
131d8999
 static struct rtpp_node *select_rtpp_node_new(str, str, int, struct rtpp_node **, int);
4f4c4488
 static struct rtpp_node *select_rtpp_node_old(str, str, int, enum rtpe_operation);
 static struct rtpp_node *select_rtpp_node(str, str, int, struct rtpp_node **, int, enum rtpe_operation);
131d8999
 static int is_queried_node(struct rtpp_node *, struct rtpp_node **, int);
3d481300
 static int build_rtpp_socks(int lmode, int rtest);
ddea262f
 static char *send_rtpp_command(struct rtpp_node *, bencode_item_t *, int *);
 static int get_extra_id(struct sip_msg* msg, str *id_str);
 
7526431e
 static int rtpengine_set_store(modparam_t type, void * val);
410151eb
 static int rtpengine_add_rtpengine_set(char * rtp_proxies, unsigned int weight, int disabled, unsigned int ticks);
ddea262f
 
 static int mod_init(void);
 static int child_init(int);
 static void mod_destroy(void);
 
3f42d6bc
 static int get_ip_type(char *str_addr);
 static int get_ip_scope(char *str_addr); // useful for link-local ipv6
 static int bind_force_send_ip(int sock_idx);
 
1190ac60
 static int add_rtpp_node_info(void *ptrs, struct rtpp_node *crt_rtpp, struct rtpp_set *rtpp_list);
a309fe46
 static int rtpp_test_ping(struct rtpp_node *node);
 
ddea262f
 /* Pseudo-Variables */
cfc0ecaa
 static int pv_get_rtpestat_f(struct sip_msg *, pv_param_t *, pv_value_t *);
7ad4dadc
 static int set_rtp_inst_pvar(struct sip_msg *msg, const str * const uri);
1aa2e58f
 static int pv_parse_var(str *inp, pv_elem_t **outp, int *got_any);
 static int mos_label_stats_parse(struct minmax_mos_label_stats *mmls);
 static void parse_call_stats(bencode_item_t *, struct sip_msg *);
ddea262f
 
a9a3ec02
 static int control_cmd_tos = -1;
d75bb85c
 static int rtpengine_allow_op = 0;
131d8999
 static struct rtpp_node **queried_nodes_ptr = NULL;
ddea262f
 static pid_t mypid;
 static unsigned int myseqn = 0;
 static str extra_id_pv_param = {NULL, 0};
e005b899
 static char *setid_avp_param = NULL;
7ad4dadc
 static int hash_table_tout = 3600;
7375d0b8
 static int hash_table_size = 256;
4b0095fb
 static unsigned int setid_default = DEFAULT_RTPP_SET_ID;
ddea262f
 
 static char ** rtpp_strings=0;
7526431e
 static int rtpp_sets=0; /*used in rtpengine_set_store()*/
ddea262f
 static int rtpp_set_count = 0;
 static unsigned int current_msg_id = (unsigned int)-1;
dda07c69
 /* RTPEngine balancing list */
f049d553
 static struct rtpp_set_head * rtpp_set_list =0;
 static struct rtpp_set * active_rtpp_set =0;
 static struct rtpp_set * selected_rtpp_set_1 =0;
 static struct rtpp_set * selected_rtpp_set_2 =0;
 static struct rtpp_set * default_rtpp_set=0;
 
 static str body_intermediate;
ddea262f
 
83caaabd
 static str rtp_inst_pv_param = {NULL, 0};
 static pv_spec_t *rtp_inst_pvar = NULL;
 
ddea262f
 /* array with the sockets used by rtpporxy (per process)*/
789dec73
 static unsigned int *rtpp_no = 0;
 static gen_lock_t *rtpp_no_lock = 0;
ddea262f
 static int *rtpp_socks = 0;
789dec73
 static unsigned int rtpp_socks_size = 0;
ddea262f
 
98877b5b
 static int setid_avp_type;
e005b899
 static int_str setid_avp;
ddea262f
 
98877b5b
 static str write_sdp_pvar_str = {NULL, 0};
 static pv_spec_t *write_sdp_pvar = NULL;
e6252246
 
98877b5b
 static str read_sdp_pvar_str = {NULL, 0};
 static pv_spec_t *read_sdp_pvar = NULL;
5ee67473
 
639c29f2
 static str media_duration_pvar_str = {NULL, 0};
 static pv_spec_t *media_duration_pvar = NULL;
 
ceffd956
 #define RTPENGINE_SESS_LIMIT_MSG "Parallel session limit reached"
 #define RTPENGINE_SESS_LIMIT_MSG_LEN (sizeof(RTPENGINE_SESS_LIMIT_MSG)-1)
e6252246
 
853068a2
 #define RTPENGINE_SESS_OUT_OF_PORTS_MSG "Ran out of ports"
 #define RTPENGINE_SESS_OUT_OF_PORTS_MSG_LEN (sizeof(RTPENGINE_SESS_OUT_OF_PORTS_MSG)-1)
 
5ed51f55
 char* force_send_ip_str="";
3f42d6bc
 int force_send_ip_af = AF_UNSPEC;
e6252246
 
d37b84be
 static str _rtpe_wsapi = STR_NULL;
 lwsc_api_t _rtpe_lwscb = {0};
7b8f1489
 
 static enum hash_algo_t hash_algo = RTP_HASH_CALLID;
 
ddea262f
 typedef struct rtpp_set_link {
 	struct rtpp_set *rset;
 	pv_spec_t *rpv;
 } rtpp_set_link_t;
 
 /* tm */
 static struct tm_binds tmb;
 
 static pv_elem_t *extra_id_pv = NULL;
 
1aa2e58f
 
 static struct minmax_mos_label_stats global_mos_stats,
 				     side_A_mos_stats,
 				     side_B_mos_stats;
 int got_any_mos_pvs;
a508f1a3
 struct crypto_binds rtpengine_cb;
1aa2e58f
 
 
ddea262f
 static cmd_export_t cmds[] = {
98877b5b
 	{"set_rtpengine_set",	(cmd_function)set_rtpengine_set_f,	1,
ddea262f
 		fixup_set_id, 0,
 		ANY_ROUTE},
98877b5b
 	{"set_rtpengine_set",	(cmd_function)set_rtpengine_set_f,	2,
f049d553
 		fixup_set_id, 0,
 		ANY_ROUTE},
98877b5b
 	{"start_recording",	(cmd_function)start_recording_f,	0,
ddea262f
 		0, 0,
7526431e
 		ANY_ROUTE },
45b4d35f
 	{"start_recording",	(cmd_function)start_recording_f,	1,
 		fixup_spve_null, 0,
 		ANY_ROUTE},
ce5362dc
 	{"stop_recording",	(cmd_function)stop_recording_f, 	0,
 		0, 0,
 		ANY_ROUTE },
45b4d35f
 	{"stop_recording",	(cmd_function)stop_recording_f, 	1,
 		fixup_spve_null, 0,
 		ANY_ROUTE},
e528fce4
 	{"block_dtmf",		(cmd_function)block_dtmf_f,	 	0,
 		0, 0,
 		ANY_ROUTE },
 	{"unblock_dtmf",	(cmd_function)unblock_dtmf_f, 		0,
 		0, 0,
 		ANY_ROUTE},
76b1f3bf
 	{"block_media",		(cmd_function)block_media_f,	 	0,
 		0, 0,
 		ANY_ROUTE },
 	{"unblock_media",	(cmd_function)unblock_media_f, 		0,
 		0, 0,
 		ANY_ROUTE},
12470055
 	{"silence_media",	(cmd_function)silence_media_f,	 	0,
 		0, 0,
 		ANY_ROUTE },
 	{"unsilence_media",	(cmd_function)unsilence_media_f, 	0,
 		0, 0,
 		ANY_ROUTE},
76b1f3bf
 	{"block_dtmf",		(cmd_function)block_dtmf_f,	 	1,
 		fixup_spve_null, 0,
 		ANY_ROUTE },
 	{"unblock_dtmf",	(cmd_function)unblock_dtmf_f, 		1,
 		fixup_spve_null, 0,
 		ANY_ROUTE},
 	{"block_media",		(cmd_function)block_media_f,	 	1,
 		fixup_spve_null, 0,
 		ANY_ROUTE },
 	{"unblock_media",	(cmd_function)unblock_media_f, 		1,
 		fixup_spve_null, 0,
 		ANY_ROUTE},
12470055
 	{"silence_media",	(cmd_function)silence_media_f,	 	1,
 		fixup_spve_null, 0,
 		ANY_ROUTE },
 	{"unsilence_media",	(cmd_function)unsilence_media_f, 	1,
 		fixup_spve_null, 0,
 		ANY_ROUTE},
558780f7
 	{"start_forwarding",	(cmd_function)start_forwarding_f,	0,
 		0, 0,
 		ANY_ROUTE },
 	{"stop_forwarding",	(cmd_function)stop_forwarding_f,	0,
 		0, 0,
 		ANY_ROUTE},
 	{"start_forwarding",	(cmd_function)start_forwarding_f,	1,
 		fixup_spve_null, 0,
 		ANY_ROUTE },
 	{"stop_forwarding",	(cmd_function)stop_forwarding_f,	1,
 		fixup_spve_null, 0,
 		ANY_ROUTE},
639c29f2
 	{"play_media",		(cmd_function)play_media_f, 		1,
 		fixup_spve_null, 0,
 		ANY_ROUTE},
 	{"stop_media",		(cmd_function)stop_media_f, 		1,
 		fixup_spve_null, 0,
 		ANY_ROUTE},
 	{"stop_media",		(cmd_function)stop_media_f, 		0,
 		0, 0,
 		ANY_ROUTE},
120a71be
 	{"play_dtmf",		(cmd_function)play_dtmf_f, 		1,
 		fixup_spve_null, 0,
 		ANY_ROUTE},
98877b5b
 	{"rtpengine_offer",	(cmd_function)rtpengine_offer1_f,	0,
ddea262f
 		0, 0,
 		ANY_ROUTE},
98877b5b
 	{"rtpengine_offer",	(cmd_function)rtpengine_offer1_f,	1,
ddea262f
 		fixup_spve_null, 0,
 		ANY_ROUTE},
98877b5b
 	{"rtpengine_answer",	(cmd_function)rtpengine_answer1_f,	0,
ddea262f
 		0, 0,
 		ANY_ROUTE},
98877b5b
 	{"rtpengine_answer",	(cmd_function)rtpengine_answer1_f,	1,
ddea262f
 		fixup_spve_null, 0,
 		ANY_ROUTE},
ffee45da
 	{"rtpengine_info",	(cmd_function)rtpengine_info1_f,	0,
 		0, 0,
 		ANY_ROUTE},
 	{"rtpengine_info",	(cmd_function)rtpengine_info1_f,	1,
 		fixup_spve_null, 0,
 		ANY_ROUTE},
98877b5b
 	{"rtpengine_manage",	(cmd_function)rtpengine_manage1_f,	0,
ddea262f
 		0, 0,
 		ANY_ROUTE},
98877b5b
 	{"rtpengine_manage",	(cmd_function)rtpengine_manage1_f,	1,
ddea262f
 		fixup_spve_null, 0,
 		ANY_ROUTE},
98877b5b
 	{"rtpengine_delete",	(cmd_function)rtpengine_delete1_f,	0,
ddea262f
 		0, 0,
 		ANY_ROUTE},
98877b5b
 	{"rtpengine_delete",	(cmd_function)rtpengine_delete1_f,	1,
7526431e
 		fixup_spve_null, 0,
ddea262f
 		ANY_ROUTE},
1aa2e58f
 	{"rtpengine_query",	(cmd_function)rtpengine_query1_f,	0,
 		0, 0,
 		ANY_ROUTE},
 	{"rtpengine_query",	(cmd_function)rtpengine_query1_f,	1,
 		fixup_spve_null, 0,
 		ANY_ROUTE},
697f34d4
 	{"rtpengine_query_v",	(cmd_function)w_rtpengine_query_v,	2,
 		fixup_rtpengine_query_v, fixup_free_rtpengine_query_v,
 		ANY_ROUTE},
ddea262f
 	{0, 0, 0, 0, 0, 0}
 };
 
 static pv_export_t mod_pvs[] = {
98877b5b
 	{{"rtpstat", (sizeof("rtpstat")-1)}, /* RTP-Statistics */
cfc0ecaa
 	PVT_OTHER, pv_get_rtpestat_f, 0, 0, 0, 0, 0},
 	{{"rtpestat", (sizeof("rtpestat")-1)}, /* RTP-Statistics */
 	PVT_OTHER, pv_get_rtpestat_f, 0, 0, 0, 0, 0},
98877b5b
 	{{0, 0}, 0, 0, 0, 0, 0, 0, 0}
ddea262f
 };
 
 static param_export_t params[] = {
448202fd
 	{"rtpengine_sock",        PARAM_STRING|USE_FUNC_PARAM,
7526431e
 	                         (void*)rtpengine_set_store          },
43451ec4
 	{"rtpengine_disable_tout",INT_PARAM, &default_rtpengine_cfg.rtpengine_disable_tout },
cb6df953
 	{"aggressive_redetection",INT_PARAM, &default_rtpengine_cfg.aggressive_redetection },
43451ec4
 	{"rtpengine_retr",        INT_PARAM, &default_rtpengine_cfg.rtpengine_retr         },
 	{"queried_nodes_limit",   INT_PARAM, &default_rtpengine_cfg.queried_nodes_limit    },
 	{"rtpengine_tout_ms",     INT_PARAM, &default_rtpengine_cfg.rtpengine_tout_ms      },
d75bb85c
 	{"rtpengine_allow_op",    INT_PARAM, &rtpengine_allow_op     },
a9a3ec02
 	{"control_cmd_tos",       INT_PARAM, &control_cmd_tos        },
5192377b
 	{"db_url",                PARAM_STR, &rtpp_db_url            },
 	{"table_name",            PARAM_STR, &rtpp_table_name        },
55734eb2
 	{"setid_col",             PARAM_STR, &rtpp_setid_col         },
5192377b
 	{"url_col",               PARAM_STR, &rtpp_url_col           },
410151eb
 	{"weight_col",            PARAM_STR, &rtpp_weight_col        },
5192377b
 	{"disabled_col",          PARAM_STR, &rtpp_disabled_col      },
3e25dce1
 	{"extra_id_pv",           PARAM_STR, &extra_id_pv_param      },
 	{"setid_avp",             PARAM_STRING, &setid_avp_param     },
 	{"force_send_interface",  PARAM_STRING, &force_send_ip_str   },
 	{"rtp_inst_pvar",         PARAM_STR, &rtp_inst_pv_param      },
 	{"write_sdp_pv",          PARAM_STR, &write_sdp_pvar_str     },
 	{"read_sdp_pv",           PARAM_STR, &read_sdp_pvar_str      },
7375d0b8
 	{"hash_table_tout",       INT_PARAM, &hash_table_tout        },
 	{"hash_table_size",       INT_PARAM, &hash_table_size        },
3e25dce1
 	{"setid_default",         INT_PARAM, &setid_default          },
639c29f2
 	{"media_duration",        PARAM_STR, &media_duration_pvar_str},
7b8f1489
 	{"hash_algo",             INT_PARAM, &hash_algo},
1aa2e58f
 
 	/* MOS stats output */
 	/* global averages */
 	{"mos_min_pv",                PARAM_STR, &global_mos_stats.min.mos_param             },
 	{"mos_min_at_pv",             PARAM_STR, &global_mos_stats.min.at_param              },
 	{"mos_min_packetloss_pv",     PARAM_STR, &global_mos_stats.min.packetloss_param      },
 	{"mos_min_jitter_pv",         PARAM_STR, &global_mos_stats.min.jitter_param          },
 	{"mos_min_roundtrip_pv",      PARAM_STR, &global_mos_stats.min.roundtrip_param       },
26a7b41f
 	{"mos_min_roundtrip_leg_pv",  PARAM_STR, &global_mos_stats.min.roundtrip_leg_param   },
1aa2e58f
 	{"mos_max_pv",                PARAM_STR, &global_mos_stats.max.mos_param             },
 	{"mos_max_at_pv",             PARAM_STR, &global_mos_stats.max.at_param              },
 	{"mos_max_packetloss_pv",     PARAM_STR, &global_mos_stats.max.packetloss_param      },
 	{"mos_max_jitter_pv",         PARAM_STR, &global_mos_stats.max.jitter_param          },
 	{"mos_max_roundtrip_pv",      PARAM_STR, &global_mos_stats.max.roundtrip_param       },
26a7b41f
 	{"mos_max_roundtrip_leg_pv",  PARAM_STR, &global_mos_stats.max.roundtrip_leg_param   },
1aa2e58f
 	{"mos_average_pv",            PARAM_STR, &global_mos_stats.average.mos_param         },
 	{"mos_average_packetloss_pv", PARAM_STR, &global_mos_stats.average.packetloss_param  },
 	{"mos_average_jitter_pv",     PARAM_STR, &global_mos_stats.average.jitter_param      },
 	{"mos_average_roundtrip_pv",  PARAM_STR, &global_mos_stats.average.roundtrip_param   },
26a7b41f
 	{"mos_average_roundtrip_leg_pv", PARAM_STR, &global_mos_stats.average.roundtrip_leg_param },
1aa2e58f
 	{"mos_average_samples_pv",    PARAM_STR, &global_mos_stats.average.samples_param     },
 
 	/* designated side A */
 	{"mos_A_label_pv",              PARAM_STR, &side_A_mos_stats.label_param               },
 	{"mos_min_A_pv",                PARAM_STR, &side_A_mos_stats.min.mos_param             },
 	{"mos_min_at_A_pv",             PARAM_STR, &side_A_mos_stats.min.at_param              },
 	{"mos_min_packetloss_A_pv",     PARAM_STR, &side_A_mos_stats.min.packetloss_param      },
 	{"mos_min_jitter_A_pv",         PARAM_STR, &side_A_mos_stats.min.jitter_param          },
 	{"mos_min_roundtrip_A_pv",      PARAM_STR, &side_A_mos_stats.min.roundtrip_param       },
26a7b41f
 	{"mos_min_roundtrip_leg_A_pv",  PARAM_STR, &side_A_mos_stats.min.roundtrip_leg_param   },
1aa2e58f
 	{"mos_max_A_pv",                PARAM_STR, &side_A_mos_stats.max.mos_param             },
 	{"mos_max_at_A_pv",             PARAM_STR, &side_A_mos_stats.max.at_param              },
 	{"mos_max_packetloss_A_pv",     PARAM_STR, &side_A_mos_stats.max.packetloss_param      },
 	{"mos_max_jitter_A_pv",         PARAM_STR, &side_A_mos_stats.max.jitter_param          },
 	{"mos_max_roundtrip_A_pv",      PARAM_STR, &side_A_mos_stats.max.roundtrip_param       },
26a7b41f
 	{"mos_max_roundtrip_leg_A_pv",  PARAM_STR, &side_A_mos_stats.max.roundtrip_leg_param   },
1aa2e58f
 	{"mos_average_A_pv",            PARAM_STR, &side_A_mos_stats.average.mos_param         },
 	{"mos_average_packetloss_A_pv", PARAM_STR, &side_A_mos_stats.average.packetloss_param  },
 	{"mos_average_jitter_A_pv",     PARAM_STR, &side_A_mos_stats.average.jitter_param      },
 	{"mos_average_roundtrip_A_pv",  PARAM_STR, &side_A_mos_stats.average.roundtrip_param   },
26a7b41f
 	{"mos_average_roundtrip_leg_A_pv",  PARAM_STR, &side_A_mos_stats.average.roundtrip_leg_param },
1aa2e58f
 	{"mos_average_samples_A_pv",    PARAM_STR, &side_A_mos_stats.average.samples_param     },
 
 	/* designated side B */
 	{"mos_B_label_pv",              PARAM_STR, &side_B_mos_stats.label_param               },
 	{"mos_min_B_pv",                PARAM_STR, &side_B_mos_stats.min.mos_param             },
 	{"mos_min_at_B_pv",             PARAM_STR, &side_B_mos_stats.min.at_param              },
 	{"mos_min_packetloss_B_pv",     PARAM_STR, &side_B_mos_stats.min.packetloss_param      },
 	{"mos_min_jitter_B_pv",         PARAM_STR, &side_B_mos_stats.min.jitter_param          },
 	{"mos_min_roundtrip_B_pv",      PARAM_STR, &side_B_mos_stats.min.roundtrip_param       },
4a2c1ea8
 	{"mos_min_roundtrip_leg_B_pv",  PARAM_STR, &side_B_mos_stats.min.roundtrip_leg_param   },
1aa2e58f
 	{"mos_max_B_pv",                PARAM_STR, &side_B_mos_stats.max.mos_param             },
 	{"mos_max_at_B_pv",             PARAM_STR, &side_B_mos_stats.max.at_param              },
 	{"mos_max_packetloss_B_pv",     PARAM_STR, &side_B_mos_stats.max.packetloss_param      },
 	{"mos_max_jitter_B_pv",         PARAM_STR, &side_B_mos_stats.max.jitter_param          },
 	{"mos_max_roundtrip_B_pv",      PARAM_STR, &side_B_mos_stats.max.roundtrip_param       },
26a7b41f
 	{"mos_max_roundtrip_leg_B_pv",  PARAM_STR, &side_B_mos_stats.max.roundtrip_leg_param   },
1aa2e58f
 	{"mos_average_B_pv",            PARAM_STR, &side_B_mos_stats.average.mos_param         },
 	{"mos_average_packetloss_B_pv", PARAM_STR, &side_B_mos_stats.average.packetloss_param  },
 	{"mos_average_jitter_B_pv",     PARAM_STR, &side_B_mos_stats.average.jitter_param      },
 	{"mos_average_roundtrip_B_pv",  PARAM_STR, &side_B_mos_stats.average.roundtrip_param   },
26a7b41f
 	{"mos_average_roundtrip_leg_B_pv",  PARAM_STR, &side_B_mos_stats.average.roundtrip_leg_param },
1aa2e58f
 	{"mos_average_samples_B_pv",    PARAM_STR, &side_B_mos_stats.average.samples_param     },
 
d37b84be
 	{"wsapi",                       PARAM_STR, &_rtpe_wsapi    },
 
ddea262f
 	{0, 0, 0}
 };
 
 struct module_exports exports = {
5b6ba549
 	"rtpengine",     /* module name */
ddea262f
 	DEFAULT_DLFLAGS, /* dlopen flags */
5b6ba549
 	cmds,            /* cmd (cfg function) exports */
 	params,          /* param exports */
 	0,               /* RPC method exports */
 	mod_pvs,         /* pseudo-variables exports */
 	0,               /* response handling function */
 	mod_init,        /* module init function */
 	child_init,      /* per-child init function */
 	mod_destroy      /* module destroy function */
ddea262f
 };
 
131d8999
 /* check if the node is already queried */
 static int is_queried_node(struct rtpp_node *node, struct rtpp_node **queried_nodes_ptr, int queried_nodes)
 {
 	int i;
 
 	if (!queried_nodes_ptr) {
 		return 0;
 	}
 
 	for (i = 0; i < queried_nodes; i++) {
 		if (node == queried_nodes_ptr[i]) {
 			return 1;
 		}
 	}
 
 	return 0;
 }
 
5ce6df2d
 /* hide the node from display and disable it permanent */
 int rtpengine_delete_node(struct rtpp_node *rtpp_node)
 {
 	rtpp_node->rn_displayed = 0;
07719603
 	rtpp_node->rn_disabled = 1;
 	rtpp_node->rn_recheck_ticks = RTPENGINE_MAX_RECHECK_TICKS;
5ce6df2d
 
 	return 1;
 }
 
 
 int rtpengine_delete_node_set(struct rtpp_set *rtpp_list)
 {
 	struct rtpp_node *rtpp_node;
 
789dec73
 	lock_get(rtpp_list->rset_lock);
5ce6df2d
 	for(rtpp_node = rtpp_list->rn_first; rtpp_node != NULL;
 			rtpp_node = rtpp_node->rn_next) {
 		rtpengine_delete_node(rtpp_node);
 	}
789dec73
 	lock_release(rtpp_list->rset_lock);
5ce6df2d
 
 	return 1;
 }
 
 
 int rtpengine_delete_node_all()
 {
 	struct rtpp_set *rtpp_list;
 
 	if (!rtpp_set_list) {
 		return 1;
 	}
 
789dec73
 	lock_get(rtpp_set_list->rset_head_lock);
5ce6df2d
 	for(rtpp_list = rtpp_set_list->rset_first; rtpp_list != NULL;
 			rtpp_list = rtpp_list->rset_next) {
 		rtpengine_delete_node_set(rtpp_list);
 	}
789dec73
 	lock_release(rtpp_set_list->rset_head_lock);
5ce6df2d
 
 	return 1;
 }
 
ddea262f
 
3f42d6bc
 static int get_ip_type(char *str_addr)
 {
 	struct addrinfo hint, *info = NULL;
 	int ret;
 
 	memset(&hint, '\0', sizeof hint);
 	hint.ai_family = PF_UNSPEC;
 	hint.ai_flags = AI_NUMERICHOST;
 
 	ret = getaddrinfo(str_addr, NULL, &hint, &info);
 	if (ret) {
 		/* Invalid ip addinfos */
 		return -1;
 	}
 
 	if(info->ai_family == AF_INET) {
 		LM_DBG("%s is an ipv4 addinfos\n", str_addr);
 	} else if (info->ai_family == AF_INET6) {
 		LM_DBG("%s is an ipv6 addinfos\n", str_addr);
 	} else {
 		LM_DBG("%s is an unknown addinfos format AF=%d\n",str_addr, info->ai_family);
a311bfba
 		freeaddrinfo(info);
3f42d6bc
 		return -1;
 	}
 
 	ret = info->ai_family;
 
 	freeaddrinfo(info);
 
 	return ret;
 }
 
 
 static int get_ip_scope(char *str_addr)
 {
 	struct ifaddrs *ifaddr, *ifa;
 	struct sockaddr_in6 *in6;
 	char str_if_ip[NI_MAXHOST];
 	int ret = -1;
 
 	if (getifaddrs(&ifaddr) == -1) {
 		LM_ERR("getifaddrs() failed: %s\n", gai_strerror(ret));
 		return -1;
 	}
 
 	for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
 		in6 = (struct sockaddr_in6 *)ifa->ifa_addr;
 
 		if (ifa->ifa_addr == NULL)
 			continue;
 
 		if (ifa->ifa_addr->sa_family != AF_INET6)
 			continue;
 
 		ret = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6),
 		str_if_ip, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
 		if (ret != 0) {
 			LM_ERR("getnameinfo() failed: %s\n", gai_strerror(ret));
 			return -1;
 		}
 
 		if (strstr(str_if_ip, str_addr)) {
 			LM_INFO("dev: %-8s address: <%s> scope %d\n",
 			ifa->ifa_name, str_if_ip, in6->sin6_scope_id);
 			ret = in6->sin6_scope_id;
 			break;
 		}
 	}
 
 	freeifaddrs(ifaddr);
 
 	return ret;
 }
 
 
 static int bind_force_send_ip(int sock_idx)
 {
 	struct sockaddr_in tmp, ip4addr;
 	struct sockaddr_in6 tmp6, ip6addr;
 	char str_addr[INET_ADDRSTRLEN];
 	char str_addr6[INET6_ADDRSTRLEN];
 	socklen_t sock_len = sizeof(struct sockaddr);
 	int ret, scope;
 
 	switch (force_send_ip_af) {
 		case AF_INET:
 			memset(&ip4addr, 0, sizeof(ip4addr));
 			ip4addr.sin_family = AF_INET;
 			ip4addr.sin_port = htons(0);
15f226ae
 			if (inet_pton(AF_INET, force_send_ip_str, &ip4addr.sin_addr) != 1) {
 				LM_ERR("failed to parse IPv4 address %s\n", force_send_ip_str);
 				return -1;
 			}
3f42d6bc
 
 			if (bind(rtpp_socks[sock_idx], (struct sockaddr*)&ip4addr, sizeof(ip4addr)) < 0) {
 				LM_ERR("can't bind socket to required ipv4 interface\n");
 				return -1;
 			}
 
 			memset(&tmp, 0, sizeof(tmp));
15f226ae
 			if (getsockname(rtpp_socks[sock_idx], (struct sockaddr *) &tmp, &sock_len))
 				LM_ERR("could not determine local socket name\n");
 			else {
 				inet_ntop(AF_INET, &tmp.sin_addr, str_addr, INET_ADDRSTRLEN);
 				LM_DBG("Binding on %s:%d\n", str_addr, ntohs(tmp.sin_port));
 			}
3f42d6bc
 
 			break;
 
 		case AF_INET6:
 			if ((scope = get_ip_scope(force_send_ip_str)) < 0) {
 				LM_ERR("can't get the ipv6 interface scope\n");
 				return -1;
 			}
 			memset(&ip6addr, 0, sizeof(ip6addr));
 			ip6addr.sin6_family = AF_INET6;
 			ip6addr.sin6_port = htons(0);
 			ip6addr.sin6_scope_id = scope;
15f226ae
 			if (inet_pton(AF_INET6, force_send_ip_str, &ip6addr.sin6_addr) != 1) {
 				LM_ERR("failed to parse IPv6 address %s\n", force_send_ip_str);
 				return -1;
 			}
3f42d6bc
 
 			if ((ret = bind(rtpp_socks[sock_idx], (struct sockaddr*)&ip6addr, sizeof(ip6addr))) < 0) {
 				LM_ERR("can't bind socket to required ipv6 interface\n");
 				LM_ERR("ret=%d errno=%d\n", ret, errno);
 				return -1;
 			}
 
 			memset(&tmp6, 0, sizeof(tmp6));
15f226ae
 			if (getsockname(rtpp_socks[sock_idx], (struct sockaddr *) &tmp6, &sock_len))
 				LM_ERR("could not determine local socket name\n");
 			else {
 				inet_ntop(AF_INET6, &tmp6.sin6_addr, str_addr6, INET6_ADDRSTRLEN);
 				LM_DBG("Binding on ipv6 %s:%d\n", str_addr6, ntohs(tmp6.sin6_port));
 			}
3f42d6bc
 
 			break;
 
 		default:
7048d597
 			LM_DBG("force_send_ip_str not specified in .cfg file!\n");
3f42d6bc
 			break;
 	}
 
 	return 0;
 }
 
0def3282
 static inline int str_cmp(const str *a , const str *b) {
 	return ! (a->len == b->len && ! strncmp(a->s, b->s, a->len));
 }
7526431e
 
 static inline int str_eq(const str *p, const char *q) {
 	int l = strlen(q);
 	if (p->len != l)
 		return 0;
 	if (memcmp(p->s, q, l))
 		return 0;
 	return 1;
 }
0def3282
 
e87cad92
 static inline int str_prefix(const str *p, const char *q, str *out) {
81a1ebc7
 	int l = strlen(q);
 	if (p->len < l)
e87cad92
 		return 0;
81a1ebc7
 	if (memcmp(p->s, q, l))
e87cad92
 		return 0;
 	*out = *p;
 	out->s += l;
 	out->len -= l;
 	return 1;
 }
 /* handle either "foo-bar" or "foo=bar" from flags */
 static inline int str_key_val_prefix(const str *p, const char *q, const str *v, str *out) {
 	if (str_eq(p, q)) {
a9a3ec02
 		if(!v->s || !v->len)
 			return 0;
 
e87cad92
 		*out = *v;
 		return 1;
 	}
 	if (!str_prefix(p, q, out))
 		return 0;
 	if (out->len < 2)
 		return 0;
 	if (*out->s != '-')
 		return 0;
 	out->s++;
 	out->len--;
 	return 1;
81a1ebc7
 }
7526431e
 
 
 static int rtpengine_set_store(modparam_t type, void * val){
ddea262f
 
 	char * p;
 	int len;
 
 	p = (char* )val;
 
 	if(p==0 || *p=='\0'){
 		return 0;
 	}
 
 	if(rtpp_sets==0){
 		rtpp_strings = (char**)pkg_malloc(sizeof(char*));
 		if(!rtpp_strings){
 			LM_ERR("no pkg memory left\n");
 			return -1;
 		}
 	} else {/*realloc to make room for the current set*/
4a58b488
 		rtpp_strings = (char**)pkg_reallocxf(rtpp_strings, (rtpp_sets+1)* sizeof(char*));
ddea262f
 		if(!rtpp_strings){
 			LM_ERR("no pkg memory left\n");
 			return -1;
 		}
 	}
 
 	/*allocate for the current set of urls*/
 	len = strlen(p);
 	rtpp_strings[rtpp_sets] = (char*)pkg_malloc((len+1)*sizeof(char));
 
 	if(!rtpp_strings[rtpp_sets]){
 		LM_ERR("no pkg memory left\n");
 		return -1;
 	}
 
 	memcpy(rtpp_strings[rtpp_sets], p, len);
 	rtpp_strings[rtpp_sets][len] = '\0';
 	rtpp_sets++;
 
 	return 0;
 }
 
0def3282
 struct rtpp_node *get_rtpp_node(struct rtpp_set *rtpp_list, str *url)
 {
 	struct rtpp_node *rtpp_node;
 
 	if (rtpp_list == NULL) {
 		return NULL;
 	}
 
789dec73
 	lock_get(rtpp_list->rset_lock);
0def3282
 	rtpp_node = rtpp_list->rn_first;
 	while (rtpp_node) {
 		if (str_cmp(&rtpp_node->rn_url, url) == 0) {
789dec73
 			lock_release(rtpp_list->rset_lock);
0def3282
 			return rtpp_node;
 		}
 		rtpp_node = rtpp_node->rn_next;
 	}
789dec73
 	lock_release(rtpp_list->rset_lock);
0def3282
 
 	return NULL;
 }
 
4b0095fb
 struct rtpp_set *get_rtpp_set(unsigned int set_id)
5c6c74ab
 {
 	struct rtpp_set * rtpp_list;
 	unsigned int my_current_id = 0;
 	int new_list;
 
 	my_current_id = set_id;
 	/*search for the current_id*/
789dec73
 	lock_get(rtpp_set_list->rset_head_lock);
5c6c74ab
 	rtpp_list = rtpp_set_list ? rtpp_set_list->rset_first : 0;
789dec73
 	while (rtpp_list != 0 && rtpp_list->id_set!=my_current_id)
5c6c74ab
 		rtpp_list = rtpp_list->rset_next;
 
 	if (rtpp_list==NULL)
 	{	/*if a new id_set : add a new set of rtpp*/
 		rtpp_list = shm_malloc(sizeof(struct rtpp_set));
 		if(!rtpp_list)
 		{
789dec73
 			lock_release(rtpp_set_list->rset_head_lock);
dda07c69
 			LM_ERR("no shm memory left to create new rtpengine set %u\n", my_current_id);
5c6c74ab
 			return NULL;
 		}
 		memset(rtpp_list, 0, sizeof(struct rtpp_set));
 		rtpp_list->id_set = my_current_id;
789dec73
 		rtpp_list->rset_lock = lock_alloc();
 		if (!rtpp_list->rset_lock) {
 			lock_release(rtpp_set_list->rset_head_lock);
dda07c69
 			LM_ERR("no shm memory left to create rtpengine set lock\n");
789dec73
 			shm_free(rtpp_list);
 			rtpp_list = NULL;
 			return NULL;
 		}
 		if (lock_init(rtpp_list->rset_lock) == 0) {
 			lock_release(rtpp_set_list->rset_head_lock);
dda07c69
 			LM_ERR("could not init rtpengine set lock\n");
789dec73
 			lock_dealloc((void*)rtpp_list->rset_lock);
 			rtpp_list->rset_lock = NULL;
 			shm_free(rtpp_list);
 			rtpp_list = NULL;
 			return NULL;
 		}
5c6c74ab
 		new_list = 1;
 	}
 	else {
 		new_list = 0;
 	}
ddea262f
 
5c6c74ab
 	if (new_list)
 	{
 		/*update the list of set info*/
 		if (!rtpp_set_list->rset_first)
 		{
 			rtpp_set_list->rset_first = rtpp_list;
 		}
 		else
 		{
 			rtpp_set_list->rset_last->rset_next = rtpp_list;
 		}
 
 		rtpp_set_list->rset_last = rtpp_list;
 		rtpp_set_count++;
 
39d98127
 		if(my_current_id == setid_default){
5c6c74ab
 			default_rtpp_set = rtpp_list;
 		}
 	}
789dec73
 	lock_release(rtpp_set_list->rset_head_lock);
 
5c6c74ab
 	return rtpp_list;
 }
 
 
dda07c69
 int add_rtpengine_socks(struct rtpp_set *rtpp_list, char *rtpengine,
fd90308d
 			unsigned int weight, int disabled, unsigned int ticks, int isDB)
5c6c74ab
 {
dda07c69
 	/* Make rtpengine instances list. */
ddea262f
 	char *p, *p1, *p2, *plim;
 	struct rtpp_node *pnode;
0def3282
 	struct rtpp_node *rtpp_node;
fd90308d
 	unsigned int local_weight, port;
 	str s1;
ddea262f
 
dda07c69
 	p = rtpengine;
ddea262f
 	plim = p + strlen(p);
 
 	for(;;) {
410151eb
 		local_weight = weight;
ddea262f
 		while (*p && isspace((int)*p))
 			++p;
 		if (p >= plim)
 			break;
 		p1 = p;
 		while (*p && !isspace((int)*p))
 			++p;
 		if (p <= p1)
 			break; /* may happen??? */
fd90308d
 		p2 = p;
 
 		/* if called for database, consider simple, single char *URL */
 		/* if called for config, consider weight URL */
 		if (!isDB) {
 			/* Have weight specified? If yes, scan it */
 			p2 = memchr(p1, '=', p - p1);
 			if (p2 != NULL) {
 				local_weight = strtoul(p2 + 1, NULL, 10);
 			} else {
 				p2 = p;
 			}
ddea262f
 		}
fd90308d
 
ddea262f
 		pnode = shm_malloc(sizeof(struct rtpp_node));
 		if (pnode == NULL) {
 			LM_ERR("no shm memory left\n");
 			return -1;
 		}
 		memset(pnode, 0, sizeof(*pnode));
fd90308d
 
789dec73
 		lock_get(rtpp_no_lock);
 		pnode->idx = *rtpp_no;
 
1190ac60
 		if (ticks == RTPENGINE_MAX_RECHECK_TICKS) {
5192377b
 			pnode->rn_recheck_ticks = ticks;
 		} else {
 			pnode->rn_recheck_ticks = ticks + get_ticks();
 		}
410151eb
 		pnode->rn_weight = local_weight;
d37b84be
 		pnode->rn_umode = RNU_UNKNOWN;
5192377b
 		pnode->rn_disabled = disabled;
5ce6df2d
 		pnode->rn_displayed = 1;
ddea262f
 		pnode->rn_url.s = shm_malloc(p2 - p1 + 1);
 		if (pnode->rn_url.s == NULL) {
789dec73
 			lock_release(rtpp_no_lock);
ddea262f
 			shm_free(pnode);
 			LM_ERR("no shm memory left\n");
 			return -1;
 		}
 		memmove(pnode->rn_url.s, p1, p2 - p1);
fd90308d
 		pnode->rn_url.s[p2 - p1] = 0;
 		pnode->rn_url.len = p2-p1;
ddea262f
 
 		/* Leave only address in rn_address */
 		pnode->rn_address = pnode->rn_url.s;
 		if (strncasecmp(pnode->rn_address, "udp:", 4) == 0) {
d37b84be
 			pnode->rn_umode = RNU_UDP;
ddea262f
 			pnode->rn_address += 4;
 		} else if (strncasecmp(pnode->rn_address, "udp6:", 5) == 0) {
d37b84be
 			pnode->rn_umode = RNU_UDP6;
ddea262f
 			pnode->rn_address += 5;
 		} else if (strncasecmp(pnode->rn_address, "unix:", 5) == 0) {
d37b84be
 			pnode->rn_umode = RNU_LOCAL;
ddea262f
 			pnode->rn_address += 5;
d37b84be
 		} else if (strncasecmp(pnode->rn_address, "ws://", 5) == 0) {
 			pnode->rn_umode = RNU_WS;
 		} else if (strncasecmp(pnode->rn_address, "wss://", 6) == 0) {
 			pnode->rn_umode = RNU_WSS;
fd90308d
 		} else {
789dec73
 			lock_release(rtpp_no_lock);
d37b84be
 			LM_WARN("Node address must start with 'udp:' or 'udp6:' or 'unix:' or 'ws://' or 'wss://'. Ignore '%s'.\n", pnode->rn_address);
fd90308d
 			shm_free(pnode->rn_url.s);
 			shm_free(pnode);
b79d2343
 
 			if (!isDB) {
 				continue;
 			} else {
 				return 0;
 			}
fd90308d
 		}
 
d37b84be
 		if (pnode->rn_umode != RNU_WS && pnode->rn_umode != RNU_WSS) {
 			/* Check the rn_address is 'hostname:port' */
 			/* Check the rn_address port is valid */
 			if(pnode->rn_umode == RNU_UDP6) {
 				p1 = strstr(pnode->rn_address, "]:");
 				if(p1 != NULL) {
 					p1++;
 				}
 			} else {
 				p1 = strchr(pnode->rn_address, ':');
 			}
 			if (p1 != NULL) {
 				p1++;
 			}
 
 			if (p1 != NULL && p1[0] != '\0') {
 				s1.s = p1;
 				s1.len = strlen(p1);
 				if (str2int(&s1, &port) < 0 || port > 0xFFFF) {
 					lock_release(rtpp_no_lock);
 					LM_WARN("Node address must end with a valid port number. Ignore '%s'.\n", pnode->rn_address);
 					shm_free(pnode->rn_url.s);
 					shm_free(pnode);
 
 					if (!isDB) {
 						continue;
 					} else {
 						return 0;
 					}
 				}
 			}
 		} else {
 			/* websocket */
 			if (_rtpe_lwscb.loaded == 0) {
789dec73
 				lock_release(rtpp_no_lock);
d37b84be
 				LM_WARN("Websocket protocol requested, but no websocket API loaded. Ignore '%s'.\n", pnode->rn_address);
fd90308d
 				shm_free(pnode->rn_url.s);
 				shm_free(pnode);
b79d2343
 
 				if (!isDB) {
 					continue;
 				} else {
 					return 0;
 				}
fd90308d
 			}
 		}
 
 		/* If node found in set, update it */
0def3282
 		rtpp_node = get_rtpp_node(rtpp_list, &pnode->rn_url);
789dec73
 
 		lock_get(rtpp_list->rset_lock);
0def3282
 		if (rtpp_node) {
 			rtpp_node->rn_disabled = pnode->rn_disabled;
5ce6df2d
 			rtpp_node->rn_displayed = pnode->rn_displayed;
0def3282
 			rtpp_node->rn_recheck_ticks = pnode->rn_recheck_ticks;
 			rtpp_node->rn_weight = pnode->rn_weight;
789dec73
 			lock_release(rtpp_list->rset_lock);
 			lock_release(rtpp_no_lock);
fd90308d
 
 			shm_free(pnode->rn_url.s);
0def3282
 			shm_free(pnode);
fd90308d
 
 			if (!isDB) {
 				continue;
 			} else {
 				return 0;
 			}
0def3282
 		}
 
ddea262f
 		if (rtpp_list->rn_first == NULL) {
 			rtpp_list->rn_first = pnode;
 		} else {
 			rtpp_list->rn_last->rn_next = pnode;
 		}
 
 		rtpp_list->rn_last = pnode;
 		rtpp_list->rtpp_node_count++;
789dec73
 		lock_release(rtpp_list->rset_lock);
 
 		*rtpp_no = *rtpp_no + 1;
 		lock_release(rtpp_no_lock);
fd90308d
 
 		if (!isDB) {
 			continue;
 		} else {
 			return 0;
 		}
ddea262f
 	}
 	return 0;
 }
 
 
98877b5b
 /* 0 - succes
  * -1 - erorr
ddea262f
  * */
410151eb
 static int rtpengine_add_rtpengine_set(char * rtp_proxies, unsigned int weight, int disabled, unsigned int ticks)
ddea262f
 {
 	char *p,*p2;
 	struct rtpp_set * rtpp_list;
 	unsigned int my_current_id;
 	str id_set;
 
 	/* empty definition? */
 	p= rtp_proxies;
 	if(!p || *p=='\0'){
 		return 0;
 	}
 
 	for(;*p && isspace(*p);p++);
 	if(*p=='\0'){
 		return 0;
 	}
 
 	rtp_proxies = strstr(p, "==");
 	if(rtp_proxies){
 		if(*(rtp_proxies +2)=='\0'){
dda07c69
 			LM_ERR("script error -invalid rtpengine list!\n");
ddea262f
 			return -1;
 		}
 
 		*rtp_proxies = '\0';
 		p2 = rtp_proxies-1;
 		for(;isspace(*p2); *p2 = '\0',p2--);
 		id_set.s = p;	id_set.len = p2 - p+1;
 
 		if(id_set.len <= 0 ||str2int(&id_set, &my_current_id)<0 ){
 		LM_ERR("script error -invalid set_id value!\n");
 			return -1;
 		}
 
 		rtp_proxies+=2;
 	}else{
 		rtp_proxies = p;
39d98127
 		my_current_id = setid_default;
ddea262f
 	}
 
 	for(;*rtp_proxies && isspace(*rtp_proxies);rtp_proxies++);
 
 	if(!(*rtp_proxies)){
 		LM_ERR("script error -empty rtp_proxy list\n");
 		return -1;;
 	}
 
 	/*search for the current_id*/
5c6c74ab
 	rtpp_list = get_rtpp_set(my_current_id);
ddea262f
 
5c6c74ab
 	if (rtpp_list != NULL)
 	{
0def3282
 
fd90308d
 		if (add_rtpengine_socks(rtpp_list, rtp_proxies, weight, disabled, ticks, 0) != 0)
5c6c74ab
 			goto error;
 		else
 			return 0;
ddea262f
 	}
 
 error:
 	return -1;
 }
 
 
 static int fixup_set_id(void ** param, int param_no)
 {
4b0095fb
 	int int_val;
 	unsigned int set_id;
ddea262f
 	struct rtpp_set* rtpp_list;
 	rtpp_set_link_t *rtpl = NULL;
 	str s;
 
 	rtpl = (rtpp_set_link_t*)pkg_malloc(sizeof(rtpp_set_link_t));
 	if(rtpl==NULL) {
 		LM_ERR("no more pkg memory\n");
 		return -1;
 	}
 	memset(rtpl, 0, sizeof(rtpp_set_link_t));
 	s.s = (char*)*param;
 	s.len = strlen(s.s);
 
 	if(s.s[0] == PV_MARKER) {
 		int_val = pv_locate_name(&s);
 		if(int_val<0 || int_val!=s.len) {
 			LM_ERR("invalid parameter %s\n", s.s);
5238fe0a
 			pkg_free(rtpl);
ddea262f
 			return -1;
 		}
 		rtpl->rpv = pv_cache_get(&s);
 		if(rtpl->rpv == NULL) {
 			LM_ERR("invalid pv parameter %s\n", s.s);
15f226ae
 			pkg_free(rtpl);
ddea262f
 			return -1;
 		}
 	} else {
4b0095fb
 		int_val = str2int(&s, &set_id);
 		if (int_val == 0) {
ddea262f
 			pkg_free(*param);
4b0095fb
 			if((rtpp_list = select_rtpp_set(set_id)) ==0){
 				LM_ERR("rtpp_proxy set %u not configured\n", set_id);
15f226ae
 				pkg_free(rtpl);
ddea262f
 				return E_CFG;
 			}
 			rtpl->rset = rtpp_list;
 		} else {
 			LM_ERR("bad number <%s>\n",	(char *)(*param));
15f226ae
 			pkg_free(rtpl);
ddea262f
 			return E_CFG;
 		}
 	}
 	*param = (void*)rtpl;
 	return 0;
 }
 
a309fe46
 static int rtpp_test_ping(struct rtpp_node *node)
 {
98877b5b
 	bencode_buffer_t bencbuf;
 	bencode_item_t *dict;
 	char *cp;
 	int ret;
 
 	if (bencode_buffer_init(&bencbuf)) {
 		return -1;
 	}
 	dict = bencode_dictionary(&bencbuf);
 	bencode_dictionary_add_string(dict, "command", command_strings[OP_PING]);
 
 	if (bencbuf.error) {
 		goto error;
 	}
 
 	cp = send_rtpp_command(node, dict, &ret);
 	if (!cp) {
 		goto error;
 	}
 
 	dict = bencode_decode_expect(&bencbuf, cp, ret, BENCODE_DICTIONARY);
 	if (!dict || bencode_dictionary_get_strcmp(dict, "result", "pong")) {
 		goto error;
 	}
 
 	bencode_buffer_free(&bencbuf);
 	return 0;
a309fe46
 
 error:
98877b5b
 	bencode_buffer_free(&bencbuf);
 	return -1;
a309fe46
 }
 
ddea262f
 
1190ac60
 static void rtpengine_rpc_reload(rpc_t* rpc, void* ctx)
 {
785ede5b
 	time_t tnow;
 
1190ac60
 	if (rtpp_db_url.s == NULL) {
 		// no database
 		rpc->fault(ctx, 500, "No Database URL");
 		return;
a309fe46
 	}
785ede5b
 
3d481300
 	if(!sr_instance_ready()) {
 		rpc->fault(ctx, 500, "Initializing - try later");
 		return;
 	}
ddea262f
 
785ede5b
 	tnow = time(NULL);
 	if(tnow - _rtpe_list_version->vertime < RTPE_LIST_VERSION_DELAY) {
 		rpc->fault(ctx, 500, "Too short reload interval - try later");
 		return;
 	}
 	_rtpe_list_version->vertime = tnow;
 
dda07c69
 	if (init_rtpengine_db() < 0) {
1190ac60
 		// fail reloading from database
 		rpc->fault(ctx, 500, "Failed reloading db");
 		return;
a309fe46
 	}
ddea262f
 
3d481300
 	if (build_rtpp_socks(1, 1)) {
785ede5b
 		rpc->fault(ctx, 500, "Failed to build rtpengine sockets");
a032d400
 		return;
a309fe46
 	}
785ede5b
 
 	_rtpe_list_version->vernum += 1;
 	_rtpe_list_version->vertime = time(NULL);
 	LM_DBG("current rtpengines list version: %d (%u)\n",
 			_rtpe_list_version->vernum,
 			(unsigned int)_rtpe_list_version->vertime);
1190ac60
 }
a309fe46
 
1190ac60
 static int rtpengine_rpc_iterate(rpc_t* rpc, void* ctx, const str *rtpp_url,
 		int (*cb)(struct rtpp_node *, struct rtpp_set *, void *), void *data)
 {
 	struct rtpp_set *rtpp_list;
 	struct rtpp_node *crt_rtpp;
 	int found = RPC_FOUND_NONE, err = 0;
 	int ret;
a309fe46
 
3d481300
 	if(!sr_instance_ready()) {
 		rpc->fault(ctx, 500, "Initializing - try later");
 		return -1;
 	}
 
 	if (build_rtpp_socks(1, 1)) {
a032d400
 		rpc->fault(ctx, 500, "Out of memory");
 		return -1;
 	}
 
1190ac60
 	if (!rtpp_set_list) {
 		rpc->fault(ctx, 404, "Instance not found (no sets loaded)");
 		return -1;
a309fe46
 	}
 
98877b5b
 	/* found a matching all - show all rtpp */
1190ac60
 	if (strncmp("all", rtpp_url->s, 3) == 0) {
 		found = RPC_FOUND_ALL;
98877b5b
 	}
ddea262f
 
789dec73
 	lock_get(rtpp_set_list->rset_head_lock);
1190ac60
 	for (rtpp_list = rtpp_set_list->rset_first; rtpp_list != NULL;
98877b5b
 			rtpp_list = rtpp_list->rset_next) {
ddea262f
 
789dec73
 		lock_get(rtpp_list->rset_lock);
1190ac60
 		for (crt_rtpp = rtpp_list->rn_first; crt_rtpp != NULL;
98877b5b
 				crt_rtpp = crt_rtpp->rn_next) {
 
789dec73
 			if (!crt_rtpp->rn_displayed) {
 				continue;
 			}
 
1190ac60
 			/* found a matching rtpp - ping it */
 			if (found == RPC_FOUND_ALL ||
 			   (crt_rtpp->rn_url.len == rtpp_url->len &&
 			   strncmp(crt_rtpp->rn_url.s, rtpp_url->s, rtpp_url->len) == 0)) {
 
 				ret = cb(crt_rtpp, rtpp_list, data);
 				if (ret) {
 					err = 1;
 					break;
98877b5b
 				}
a309fe46
 
1190ac60
 				if (found == RPC_FOUND_NONE) {
 					found = RPC_FOUND_ONE;
98877b5b
 				}
 			}
a309fe46
 		}
789dec73
 		lock_release(rtpp_list->rset_lock);
ddea262f
 
1190ac60
 		if (err)
98877b5b
 			break;
 	}
1190ac60
 	lock_release(rtpp_set_list->rset_head_lock);
98877b5b
 
1190ac60
 	if (err)
 		return -1;
98877b5b
 
1190ac60
 	if (found == RPC_FOUND_NONE) {
 		rpc->fault(ctx, 404, "Instance not found");
 		return -1;
ddea262f
 	}
 
1190ac60
 	return found;
ddea262f
 }
 
1190ac60
 static int add_rtpp_node_info (void *ptrsp, struct rtpp_node *crt_rtpp, struct rtpp_set *rtpp_list)
ddea262f
 {
1190ac60
 	void *vh;
 	void **ptrs = ptrsp;
 	rpc_t *rpc;
 	void *ctx;
98877b5b
 	int rtpp_ticks;
a309fe46
 
1190ac60
 	rpc = ptrs[0];
 	ctx = ptrs[1];
a309fe46
 
1190ac60
 	if (rpc->add(ctx, "{", &vh) < 0) {
 		rpc->fault(ctx, 500, "Server error");
 		return -1;
a309fe46
 	}
 
1190ac60
 	rpc->struct_add(vh, "Sddd",
 			"url", &crt_rtpp->rn_url,
 			"set", rtpp_list->id_set,
 			"index", crt_rtpp->idx,
 			"weight", crt_rtpp->rn_weight);
a309fe46
 
1190ac60
 	if ((1 == crt_rtpp->rn_disabled ) && (crt_rtpp->rn_recheck_ticks == RTPENGINE_MAX_RECHECK_TICKS)) {
 		rpc->struct_add(vh, "s", "disabled", "1(permanent)");
98877b5b
 	} else {
1190ac60
 		rpc->struct_add(vh, "d", "disabled", crt_rtpp->rn_disabled);
a309fe46
 	}
 
1190ac60
 	if (crt_rtpp->rn_recheck_ticks == RTPENGINE_MAX_RECHECK_TICKS) {
 		rpc->struct_add(vh, "s", "recheck_ticks", "N/A");
a309fe46
 	} else {
98877b5b
 		rtpp_ticks = crt_rtpp->rn_recheck_ticks - get_ticks();
 		rtpp_ticks = rtpp_ticks < 0 ? 0 : rtpp_ticks;
1190ac60
 		rpc->struct_add(vh, "d", "recheck_ticks", rtpp_ticks);
a309fe46
 	}
 
 	return 0;
 }
 
1190ac60
 static int rtpengine_iter_cb_enable(struct rtpp_node *crt_rtpp, struct rtpp_set *rtpp_list, void *flagp)
a309fe46
 {
1190ac60
 	int *flag = flagp;
ddea262f
 
1190ac60
 	/* do ping when try to enable the rtpp */
 	if (*flag) {
ddea262f
 
1190ac60
 		/* if ping success, enable the rtpp and reset ticks */
 		if (rtpp_test_ping(crt_rtpp) == 0) {
 			crt_rtpp->rn_disabled = 0;
 			crt_rtpp->rn_recheck_ticks = RTPENGINE_MIN_RECHECK_TICKS;
ddea262f
 
1190ac60
 		/* if ping fail, disable the rtpps but _not_ permanently*/
 		} else {
 			crt_rtpp->rn_recheck_ticks = get_ticks() + cfg_get(rtpengine,rtpengine_cfg,rtpengine_disable_tout);
 			crt_rtpp->rn_disabled = 1;
 			*flag = 2; /* return value to caller */
a309fe46
 		}
ddea262f
 
1190ac60
 	/* do not ping when disable the rtpp; disable it permanenty */
 	} else {
 		crt_rtpp->rn_disabled = 1;
 		crt_rtpp->rn_recheck_ticks = RTPENGINE_MAX_RECHECK_TICKS;
a309fe46
 	}
ddea262f
 
1190ac60
 	return 0;
a309fe46
 }
 
1190ac60
 static void rtpengine_rpc_enable(rpc_t* rpc, void* ctx)
a309fe46
 {
1190ac60
 	void *vh;
a309fe46
 	str rtpp_url;
1190ac60
 	int flag, found;
a309fe46
 
1190ac60
 	if (rpc->scan(ctx, "Sd", &rtpp_url, &flag) < 2) {
 		rpc->fault(ctx, 500, "Not enough parameters");
 		return;
98877b5b
 	}
 
1190ac60
 	flag = flag ? 1 : 0; /* also used as a return value */
98877b5b
 
1190ac60
 	found = rtpengine_rpc_iterate(rpc, ctx, &rtpp_url, rtpengine_iter_cb_enable, &flag);
 	if (found == -1)
 		return;
98877b5b
 
1190ac60
 	if (rpc->add(ctx, "{", &vh) < 0) {
 		rpc->fault(ctx, 500, "Server error");
 		return;
ddea262f
 	}
 
1190ac60
 	rpc->struct_add(vh, "S", "url", &rtpp_url);
a309fe46
 
1190ac60
 	if (flag == 0)
 		rpc->struct_add(vh, "s", "status", "disable");
 	else if (flag == 1)
 		rpc->struct_add(vh, "s", "status", "enable");
 	else
 		rpc->struct_add(vh, "s", "status", "fail");
ddea262f
 }
 
1190ac60
 static int rtpengine_iter_cb_show(struct rtpp_node *crt_rtpp, struct rtpp_set *rtpp_list, void *ptrsp)
74fdbe22
 {
1190ac60
 	if (add_rtpp_node_info(ptrsp, crt_rtpp, rtpp_list) < 0)
 		return -1;
 	return 0;
 }
74fdbe22
 
1190ac60
 static void rtpengine_rpc_show(rpc_t* rpc, void* ctx)
 {
 	str rtpp_url;
 	void *ptrs[2] = {rpc, ctx};
74fdbe22
 
1190ac60
 	if (rpc->scan(ctx, "S", &rtpp_url) < 1) {
 		rpc->fault(ctx, 500, "Not enough parameters");
 		return;
74fdbe22
 	}
 
1190ac60
 	rtpengine_rpc_iterate(rpc, ctx, &rtpp_url, rtpengine_iter_cb_show, ptrs);
74fdbe22
 }
 
1190ac60
 static int rtpengine_iter_cb_ping(struct rtpp_node *crt_rtpp, struct rtpp_set *rtpp_list, void *data)
d884698c
 {
1190ac60
 	int *found_rtpp_disabled = data;
789dec73
 
1190ac60
 	/* if ping fail */
 	if (rtpp_test_ping(crt_rtpp) < 0) {
 		crt_rtpp->rn_recheck_ticks = get_ticks() + cfg_get(rtpengine,rtpengine_cfg,rtpengine_disable_tout);
 		*found_rtpp_disabled = 1;
 		crt_rtpp->rn_disabled = 1;
d884698c
 	}
 
1190ac60
 	return 0;
d884698c
 }
 
1190ac60
 static void rtpengine_rpc_ping(rpc_t* rpc, void* ctx)
9b5d2b50
 {
1190ac60
 	void *vh;
 	int found;
 	int found_rtpp_disabled = 0;
 	str rtpp_url;
9b5d2b50
 
1190ac60
 	if (rpc->scan(ctx, "S", &rtpp_url) < 1) {
 		rpc->fault(ctx, 500, "Not enough parameters");
9b5d2b50
 		return;
 	}
 
1190ac60
 	found = rtpengine_rpc_iterate(rpc, ctx, &rtpp_url, rtpengine_iter_cb_ping, &found_rtpp_disabled);
 	if (found == -1)
 		return;
 
 	if (rpc->add(ctx, "{", &vh) < 0) {
 		rpc->fault(ctx, 500, "Server error");
9b5d2b50
 		return;
 	}
 
1190ac60
 	rpc->struct_add(vh, "Ss",
 			"url", &rtpp_url,
 			"status", (found_rtpp_disabled ? "fail" : "success"));
 }
9b5d2b50
 
1190ac60
 static void rtpengine_rpc_get_hash_total(rpc_t* rpc, void* ctx)
 {
 	rpc->add(ctx, "u", rtpengine_hash_table_total());
9b5d2b50
 }
 
1190ac60
 
9b5d2b50
 static const char* rtpengine_rpc_reload_doc[2] = {
1190ac60
 	"Reload rtpengine proxies from database", 0
 };
 static const char* rtpengine_rpc_ping_doc[2] = {
 	"Ping an rtpengine instance", 0
 };
 static const char* rtpengine_rpc_show_doc[2] = {
 	"Get details about an rtpengine instance", 0
 };
 static const char* rtpengine_rpc_enable_doc[2] = {
 	"Enable or disable an rtpengine instance", 0
 };
 static const char* rtpengine_rpc_get_hash_total_doc[2] = {
 	"Get total number of entries in hash table", 0
9b5d2b50
 };
 
 rpc_export_t rtpengine_rpc[] = {
 	{"rtpengine.reload", rtpengine_rpc_reload, rtpengine_rpc_reload_doc, 0},
1190ac60
 	{"rtpengine.ping", rtpengine_rpc_ping, rtpengine_rpc_ping_doc, 0},
 	{"rtpengine.show", rtpengine_rpc_show, rtpengine_rpc_show_doc, RET_ARRAY},
 	{"rtpengine.enable", rtpengine_rpc_enable, rtpengine_rpc_enable_doc, 0},
 	{"rtpengine.get_hash_total", rtpengine_rpc_get_hash_total, rtpengine_rpc_get_hash_total_doc, 0},
9b5d2b50
 	{0, 0, 0, 0}
 };
 
 static int rtpengine_rpc_init(void)
 {
 	if (rpc_register_array(rtpengine_rpc)!=0)
 	{
 		LM_ERR("failed to register RPC commands\n");
 		return -1;
 	}
 	return 0;
 }
 
ddea262f
 static int
 mod_init(void)
 {
 	int i;
e005b899
 	pv_spec_t *avp_spec;
 	unsigned short avp_flags;
 	str s;
ddea262f
 
785ede5b
 	_rtpe_list_version = (rtpe_list_version_t*)shm_mallocxz(sizeof(rtpe_list_version_t));
 	if(_rtpe_list_version==NULL) {
 		LM_ERR("no more shm memory for rtpe list version\n");
 		return -1;
 	}
 	_rtpe_list_version->vernum = 1;
 	_rtpe_list_version->vertime = time(NULL);
 
9b5d2b50
 	if(rtpengine_rpc_init()<0)
 	{
 		LM_ERR("failed to register RPC commands\n");
 		return -1;
 	}
ddea262f
 
789dec73
 	rtpp_no = (unsigned int*)shm_malloc(sizeof(unsigned int));
 	if (!rtpp_no) {
 		LM_ERR("no more shm memory for rtpp_no\n");
 		return -1;
 	}
 	*rtpp_no = 0;
 
 	rtpp_no_lock = lock_alloc();
 	if (!rtpp_no_lock) {
 		LM_ERR("no more shm memory for rtpp_no_lock\n");
 		return -1;
 	}
 
 	if (lock_init(rtpp_no_lock) == 0) {
 		LM_ERR("could not init rtpp_no_lock\n");
 		return -1;
 	}
 
d37b84be
 	if(_rtpe_wsapi.s!=NULL && _rtpe_wsapi.len==4
 			&& strncasecmp(_rtpe_wsapi.s, "lwsc", 4)==0) {
 		if(lwsc_load_api(&_rtpe_lwscb)) {
 			LM_ERR("failed to load WS API: %s\n", _rtpe_wsapi.s);
 			return -1;
 		}
 	} else {
 		if(_rtpe_wsapi.s!=NULL && _rtpe_wsapi.len>0) {
 			LM_ERR("unsupported WS API: %s\n", _rtpe_wsapi.s);
 			return -1;
 		}
 	}
 
789dec73
 	/* initialize the list of set; mod_destroy does shm_free() if fail */
 	if (!rtpp_set_list) {
 		rtpp_set_list = shm_malloc(sizeof(struct rtpp_set_head));
 		if(!rtpp_set_list){
 			LM_ERR("no shm memory left to create list of proxysets\n");
 			return -1;
 		}
 		memset(rtpp_set_list, 0, sizeof(struct rtpp_set_head));
 
 		rtpp_set_list->rset_head_lock = lock_alloc();
 		if (!rtpp_set_list->rset_head_lock) {
 			LM_ERR("no shm memory left to create list of proxysets lock\n");
 			return -1;
 		}
 
 		if (lock_init(rtpp_set_list->rset_head_lock) == 0) {
dda07c69
 			LM_ERR("could not init lock sets for rtpengine list\n");
789dec73
 			return -1;
 		}
 	}
ddea262f
 
5c6c74ab
 	if (rtpp_db_url.s == NULL)
 	{
dda07c69
 		/* storing the list of rtpengine sets in shared memory*/
5c6c74ab
 		for(i=0;i<rtpp_sets;i++){
410151eb
 			if(rtpengine_add_rtpengine_set(rtpp_strings[i], 1, 0, 0) !=0){
5c6c74ab
 				for(;i<rtpp_sets;i++)
 					if(rtpp_strings[i])
 						pkg_free(rtpp_strings[i]);
 				pkg_free(rtpp_strings);
 				return -1;
 			}
 			if(rtpp_strings[i])
 				pkg_free(rtpp_strings[i]);
 		}
 	}
 	else
 	{
dda07c69
 		LM_INFO("Loading rtpengine definitions from DB\n");
 		if ( init_rtpengine_db() < 0)
5c6c74ab
 		{
dda07c69
 			LM_ERR("error while loading rtpengine instances from database\n");
ddea262f
 			return -1;
 		}
 	}
 
83caaabd
 	if (rtp_inst_pv_param.s) {
 		rtp_inst_pv_param.len = strlen(rtp_inst_pv_param.s);
 		rtp_inst_pvar = pv_cache_get(&rtp_inst_pv_param);
 		if ((rtp_inst_pvar == NULL) ||
98877b5b
 		   ((rtp_inst_pvar->type != PVT_AVP) &&
 		   (rtp_inst_pvar->type != PVT_XAVP) &&
 		   (rtp_inst_pvar->type != PVT_SCRIPTVAR))) {
 			LM_ERR("Invalid pvar name <%.*s>\n", rtp_inst_pv_param.len, rtp_inst_pv_param.s);
 			return -1;
83caaabd
 		}
 	}
 
1aa2e58f
 	if (pv_parse_var(&extra_id_pv_param, &extra_id_pv, NULL))
 		return -1;
 
 	if (mos_label_stats_parse(&global_mos_stats))
 		return -1;
 	if (mos_label_stats_parse(&side_A_mos_stats))
 		return -1;
 	if (mos_label_stats_parse(&side_B_mos_stats))
 		return -1;
ddea262f
 
e005b899
 	if (setid_avp_param) {
98877b5b
 		s.s = setid_avp_param; s.len = strlen(s.s);
 		avp_spec = pv_cache_get(&s);
 		if (avp_spec==NULL || (avp_spec->type != PVT_AVP)) {
 			LM_ERR("malformed or non AVP definition <%s>\n", setid_avp_param);
 			return -1;
 		}
 		if (pv_get_avp_name(0, &(avp_spec->pvp), &setid_avp, &avp_flags) != 0) {
 			LM_ERR("invalid AVP definition <%s>\n", setid_avp_param);
 			return -1;
 		}
 		setid_avp_type = avp_flags;
e005b899
 	}
 
e6252246
 	if (write_sdp_pvar_str.len > 0) {
 		write_sdp_pvar = pv_cache_get(&write_sdp_pvar_str);
 		if (write_sdp_pvar == NULL
98877b5b
 			|| (write_sdp_pvar->type != PVT_AVP &&  write_sdp_pvar->type != PVT_SCRIPTVAR) ) {
e6252246
 			LM_ERR("write_sdp_pv: not a valid AVP or VAR definition <%.*s>\n",
98877b5b
 				write_sdp_pvar_str.len, write_sdp_pvar_str.s);
e6252246
 			return -1;
 		}
 	}
 
5ee67473
 	if (read_sdp_pvar_str.len > 0) {
 		read_sdp_pvar = pv_cache_get(&read_sdp_pvar_str);
 		if (read_sdp_pvar == NULL
98877b5b
 			|| (read_sdp_pvar->type != PVT_AVP &&  read_sdp_pvar->type != PVT_SCRIPTVAR) ) {
5ee67473
 			LM_ERR("read_sdp_pv: not a valid AVP or VAR definition <%.*s>\n",
98877b5b
 				read_sdp_pvar_str.len, read_sdp_pvar_str.s);
5ee67473
 			return -1;
 		}
 	}
 
639c29f2
 	if (media_duration_pvar_str.len > 0) {
 		media_duration_pvar = pv_cache_get(&media_duration_pvar_str);
 		if (media_duration_pvar == NULL
 			|| (media_duration_pvar->type != PVT_AVP && media_duration_pvar->type != PVT_SCRIPTVAR) ) {
 			LM_ERR("media_duration_pv: not a valid AVP or VAR definition <%.*s>\n",
 				media_duration_pvar_str.len, media_duration_pvar_str.s);
 			return -1;
 		}
 	}
 
ddea262f
 	if (rtpp_strings)
 		pkg_free(rtpp_strings);
 
 	if (load_tm_api( &tmb ) < 0)
 	{
 		LM_DBG("could not load the TM-functions - answer-offer model"
98877b5b
 			" auto-detection is disabled\n");
ddea262f
 		memset(&tmb, 0, sizeof(struct tm_binds));
 	}
 
3f42d6bc
 	/* Determine IP addr type (IPv4 or IPv6 allowed) */
 	force_send_ip_af = get_ip_type(force_send_ip_str);
 	if (force_send_ip_af != AF_INET && force_send_ip_af != AF_INET6 &&
98877b5b
 	   strlen(force_send_ip_str) > 0) {
3f42d6bc
 		LM_ERR("%s is an unknown address\n", force_send_ip_str);
 		return -1;
5ed51f55
 	}
3f42d6bc
 
2625ab3c
 	/* init the hastable which keeps the call-id <-> selected_node relation */
7375d0b8
 	if (!rtpengine_hash_table_init(hash_table_size)) {
 		LM_ERR("rtpengine_hash_table_init(%d) failed!\n", hash_table_size);
2625ab3c
 		return -1;
 	} else {
7375d0b8
 		LM_DBG("rtpengine_hash_table_init(%d) success!\n", hash_table_size);
2625ab3c
 	}
 
3e25dce1
 	/* select the default set */
 	default_rtpp_set = select_rtpp_set(setid_default);
2a31c547
 	active_rtpp_set = default_rtpp_set;
3e25dce1
 	if (!default_rtpp_set) {
4b0095fb
 		LM_NOTICE("Default rtpp set %u NOT found\n", setid_default);
3e25dce1
 	} else {
4b0095fb
 		LM_DBG("Default rtpp set %u found\n", setid_default);
3e25dce1
 	}
 
43451ec4
     if(cfg_declare("rtpengine", rtpengine_cfg_def, &default_rtpengine_cfg, cfg_sizeof(rtpengine), &rtpengine_cfg)){
         LM_ERR("Failed to declare the configuration\n");
         return -1;
     }
 
7b8f1489
 	if (hash_algo == RTP_HASH_SHA1_CALLID) {
 		if (load_crypto_api(&rtpengine_cb) != 0) {
 			LM_ERR("Crypto module required in order to have SHA1 hashing!\n");
 			return -1;
 		}
a508f1a3
 	}
43451ec4
 
ddea262f
 	return 0;
 }
 
99250f75
 #define rtpe_reload_lock_get(plock) do { \
3d481300
 	if (rtpp_db_url.s != NULL && lmode != 0) lock_get(plock); \
99250f75
 } while(0)
 
 #define rtpe_reload_lock_release(plock) do { \
3d481300
 	if (rtpp_db_url.s != NULL && lmode != 0) lock_release(plock); \
99250f75
 } while(0)
 
3d481300
 /**
  * build rtp enigne sockets
  * - lmode: locking mode (1 - lock if needed; 0 - no locking)
  * - rtest: rtpengine testing (1 - test if active; 0 - no test done)
  */
 static int build_rtpp_socks(int lmode, int rtest) {
789dec73
 	int n, i;
ddea262f
 	char *cp;
 	struct addrinfo hints, *res;
 	struct rtpp_set  *rtpp_list;
 	struct rtpp_node *pnode;
a032d400
 	unsigned int current_rtpp_no;
5de00fa4
 #ifdef IP_MTU_DISCOVER
 	int ip_mtu_discover = IP_PMTUDISC_DONT;
 #endif
ddea262f
 
785ede5b
 	if(_rtpe_list_vernum_local == _rtpe_list_version->vernum) {
 		/* same version for the list of rtpengines */
c88e65ed
 		LM_DBG("same rtpengines list version: %d (%u)\n",
 			_rtpe_list_version->vernum,
 			(unsigned int)_rtpe_list_version->vertime);
785ede5b
 		return 0;
 	}
 
99250f75
 	rtpe_reload_lock_get(rtpp_no_lock);
a032d400
 	current_rtpp_no = *rtpp_no;
99250f75
 	rtpe_reload_lock_release(rtpp_no_lock);
a032d400
 
789dec73
 	// close current sockets
 	for (i = 0; i < rtpp_socks_size; i++) {
 		if (rtpp_socks[i] >= 0) {
 			close(rtpp_socks[i]);
d76761bf
 			rtpp_socks[i] = -1;
789dec73
 		}
 	}
ddea262f
 
789dec73
 	rtpp_socks_size = current_rtpp_no;
c836f8ba
 	/* allocate one more to have a safety end place holder */
 	rtpp_socks = (int*)pkg_reallocxf(rtpp_socks, sizeof(int)*(rtpp_socks_size+1));
789dec73
 	if (!rtpp_socks) {
 		LM_ERR("no more pkg memory for rtpp_socks\n");
ddea262f
 		return -1;
 	}
c836f8ba
 	memset(rtpp_socks, -1, sizeof(int)*(rtpp_socks_size+1));
ddea262f
 
99250f75
 	rtpe_reload_lock_get(rtpp_set_list->rset_head_lock);
785ede5b
 	_rtpe_list_vernum_local = _rtpe_list_version->vernum;
789dec73
 	for (rtpp_list = rtpp_set_list->rset_first; rtpp_list != 0;
 		rtpp_list = rtpp_list->rset_next) {
ddea262f
 
99250f75
 		rtpe_reload_lock_get(rtpp_list->rset_lock);
789dec73
 		for (pnode=rtpp_list->rn_first; pnode!=0; pnode = pnode->rn_next) {
ddea262f
 			char *hostname;
491d1261
 			char *hp;
ddea262f
 
d37b84be
 			if (pnode->rn_umode == RNU_LOCAL || pnode->rn_umode == RNU_WS
 					|| pnode->rn_umode == RNU_WSS) {
ddea262f
 				rtpp_socks[pnode->idx] = -1;
 				goto rptest;
 			}
 
 			/*
 			 * This is UDP or UDP6. Detect host and port; lookup host;
 			 * do connect() in order to specify peer address
 			 */
 			hostname = (char*)pkg_malloc(sizeof(char) * (strlen(pnode->rn_address) + 1));
 			if (hostname==NULL) {
 				LM_ERR("no more pkg memory\n");
789dec73
 				rtpp_socks[pnode->idx] = -1;
c88e65ed
 
 				/* retry later */
 				_rtpe_list_version->vernum += 1;
 				_rtpe_list_version->vertime = time(NULL);
 
789dec73
 				continue;
ddea262f
 			}
 			strcpy(hostname, pnode->rn_address);
 
 			cp = strrchr(hostname, ':');
 			if (cp != NULL) {
 				*cp = '\0';
 				cp++;
 			}
 			if (cp == NULL || *cp == '\0')
 				cp = CPORT;
 
d37b84be
 			if(pnode->rn_umode == RNU_UDP6) {
491d1261
 				hp = strrchr(hostname, ']');
 				if(hp != NULL)
 					*hp = '\0';
 
 				hp = hostname;
 				if(*hp == '[')
 					hp++;
 			} else {
 				hp = hostname;
 			}
 
ddea262f
 			memset(&hints, 0, sizeof(hints));
 			hints.ai_flags = 0;
d37b84be
 			hints.ai_family = (pnode->rn_umode == RNU_UDP6) ? AF_INET6 : AF_INET;
ddea262f
 			hints.ai_socktype = SOCK_DGRAM;
491d1261
 			if ((n = getaddrinfo(hp, cp, &hints, &res)) != 0) {
ddea262f
 				LM_ERR("%s\n", gai_strerror(n));
 				pkg_free(hostname);
789dec73
 				rtpp_socks[pnode->idx] = -1;
c88e65ed
 
 				/* retry later */
 				_rtpe_list_version->vernum += 1;
 				_rtpe_list_version->vertime = time(NULL);
 
789dec73
 				continue;
ddea262f
 			}
 			pkg_free(hostname);
 
d37b84be
 			rtpp_socks[pnode->idx] = socket((pnode->rn_umode == RNU_UDP6)
98877b5b
 				? AF_INET6 : AF_INET, SOCK_DGRAM, 0);
3f42d6bc
 			if (rtpp_socks[pnode->idx] == -1) {
ddea262f
 				LM_ERR("can't create socket\n");
 				freeaddrinfo(res);
c88e65ed
 
 				/* retry later */
 				_rtpe_list_version->vernum += 1;
 				_rtpe_list_version->vertime = time(NULL);
 
789dec73
 				continue;
ddea262f
 			}
 
5de00fa4
 #ifdef IP_MTU_DISCOVER
15f226ae
 			if (setsockopt(rtpp_socks[pnode->idx], IPPROTO_IP,
 					IP_MTU_DISCOVER, &ip_mtu_discover,
 					sizeof(ip_mtu_discover)))
 				LM_WARN("Failed enable set MTU discovery socket option\n");
5de00fa4
 #endif
 
5fef3a6d
 			if((0 <= control_cmd_tos) && (control_cmd_tos < 256)) {
 				unsigned char tos = control_cmd_tos;
d37b84be
 				if (pnode->rn_umode == RNU_UDP6) {
31deafda
 					if(setsockopt(rtpp_socks[pnode->idx], IPPROTO_IPV6,
5fef3a6d
 							IPV6_TCLASS, &control_cmd_tos,
31deafda
 							sizeof(control_cmd_tos)))
 						LM_WARN("Failed to set IPv6 TOS socket option\n");
5fef3a6d
 
 				} else {
31deafda
 					if(setsockopt(rtpp_socks[pnode->idx], IPPROTO_IP,
 							IP_TOS, &tos, sizeof(tos)))
 						LM_WARN("Failed to set IPv4 TOS socket option\n");
5fef3a6d
 				}
 			}
 
3f42d6bc
 			if (bind_force_send_ip(pnode->idx) == -1) {
 				LM_ERR("can't bind socket\n");
 				close(rtpp_socks[pnode->idx]);
 				rtpp_socks[pnode->idx] = -1;
 				freeaddrinfo(res);
c88e65ed
 
 				/* retry later */
 				_rtpe_list_version->vernum += 1;
 				_rtpe_list_version->vertime = time(NULL);
 
789dec73
 				continue;
5ed51f55
 			}
 
3f42d6bc
 			if (connect(rtpp_socks[pnode->idx], res->ai_addr, res->ai_addrlen) == -1) {
dda07c69
 				LM_ERR("can't connect to a RTPEngine instance\n");
3f42d6bc
 				close(rtpp_socks[pnode->idx]);
ddea262f
 				rtpp_socks[pnode->idx] = -1;
 				freeaddrinfo(res);
c88e65ed
 
 				/* retry later */
 				_rtpe_list_version->vernum += 1;
 				_rtpe_list_version->vertime = time(NULL);
 
789dec73
 				continue;
ddea262f
 			}
3f42d6bc
 
ddea262f
 			freeaddrinfo(res);
 rptest:
3d481300
 			if(rtest) pnode->rn_disabled = rtpp_test(pnode, 0, 1);
ddea262f
 		}
99250f75
 		rtpe_reload_lock_release(rtpp_list->rset_lock);
789dec73
 	}
99250f75
 	rtpe_reload_lock_release(rtpp_set_list->rset_head_lock);
789dec73
 
 	return 0;
 }
 
1aa2e58f
 static int pv_parse_var(str *inp, pv_elem_t **outp, int *got_any) {
 	if (inp->s && *inp->s) {
 		inp->len = strlen(inp->s);
 		if(pv_parse_format(inp, outp) < 0) {
 			LM_ERR("malformed PV string: %s\n", inp->s);
 			return -1;
 		}
 		if (got_any)
 			*got_any = 1;
 	} else {
 		*outp = NULL;
 	}
 	return 0;
 }
 
 static int minmax_pv_parse(struct minmax_mos_stats *s, int *got_any) {
 	if (pv_parse_var(&s->mos_param, &s->mos_pv, got_any))
 		return -1;
 	if (pv_parse_var(&s->at_param, &s->at_pv, got_any))
 		return -1;
 	if (pv_parse_var(&s->packetloss_param, &s->packetloss_pv, got_any))
 		return -1;
 	if (pv_parse_var(&s->jitter_param, &s->jitter_pv, got_any))
 		return -1;
 	if (pv_parse_var(&s->roundtrip_param, &s->roundtrip_pv, got_any))
 		return -1;
26a7b41f
 	if (pv_parse_var(&s->roundtrip_leg_param, &s->roundtrip_leg_pv, got_any))
 		return -1;
1aa2e58f
 	if (pv_parse_var(&s->samples_param, &s->samples_pv, got_any))
 		return -1;
 	return 0;
 }
 
 static int mos_label_stats_parse(struct minmax_mos_label_stats *mmls) {
 	if (pv_parse_var(&mmls->label_param, &mmls->label_pv, &mmls->got_any_pvs))
 		return -1;
 
 	if (minmax_pv_parse(&mmls->min, &mmls->got_any_pvs))
 		return -1;
 	if (minmax_pv_parse(&mmls->max, &mmls->got_any_pvs))
 		return -1;
 	if (minmax_pv_parse(&mmls->average, &mmls->got_any_pvs))
 		return -1;
 
 	if (mmls->got_any_pvs)
 		got_any_mos_pvs = 1;
 
 	return 0;
 }
 
 
789dec73
 static int
 child_init(int rank)
 {
 	if(!rtpp_set_list)
 		return 0;
 
d76761bf
 	/* do not init sockets for PROC_INIT and main process when fork=yes */
 	if(rank==PROC_INIT || (rank==PROC_MAIN && dont_fork==0)) {
 		return 0;
 	}
 
789dec73
 	mypid = getpid();
 
131d8999
 	// vector of pointers to queried nodes
43451ec4
 	queried_nodes_ptr = (struct rtpp_node**)pkg_malloc(MAX_RTPP_TRIED_NODES * sizeof(struct rtpp_node*));
131d8999
 	if (!queried_nodes_ptr) {
 		LM_ERR("no more pkg memory for queried_nodes_ptr\n");
 		return -1;
 	}
43451ec4
 	memset(queried_nodes_ptr, 0, MAX_RTPP_TRIED_NODES * sizeof(struct rtpp_node*));
131d8999
 
dda07c69
 	/* Iterate known RTPEngine instances - create sockets */
3d481300
 	if(rank==PROC_SIPINIT) {
 		/* probe rtpengines only in first worker */
 		if (build_rtpp_socks(0, 1))
 			return -1;
 	} else {
 		if (build_rtpp_socks(0, 0))
 			return -1;
 	}
ddea262f
 
 	return 0;
 }
 
 
 static void mod_destroy(void)
 {
 	struct rtpp_set * crt_list, * last_list;
 	struct rtpp_node * crt_rtpp, *last_rtpp;
 
 	/*free the shared memory*/
789dec73
 	if (rtpp_no) {
 		shm_free(rtpp_no);
 		rtpp_no = NULL;
 	}
 
 	if (rtpp_no_lock) {
 		lock_destroy(rtpp_no_lock);
 		lock_dealloc(rtpp_no_lock);
 		rtpp_no_lock = NULL;
 	}
 
 	if (!rtpp_set_list) {
 		return;
 	}
 
 	if (!rtpp_set_list->rset_head_lock) {
 		shm_free(rtpp_set_list);
 		rtpp_set_list = NULL;
ddea262f
 		return;
789dec73
 	}
ddea262f
 
789dec73
 	lock_get(rtpp_set_list->rset_head_lock);
ddea262f
 	for(crt_list = rtpp_set_list->rset_first; crt_list != NULL; ){
6711306d
 		last_list = crt_list;
ddea262f
 
789dec73
 		if (!crt_list->rset_lock) {
 			crt_list = last_list->rset_next;
 			shm_free(last_list);
 			last_list = NULL;
 			continue;
 		}
 
6711306d
 		lock_get(last_list->rset_lock);
ddea262f
 		for(crt_rtpp = crt_list->rn_first; crt_rtpp != NULL;  ){
 
 			if(crt_rtpp->rn_url.s)
 				shm_free(crt_rtpp->rn_url.s);
 
 			last_rtpp = crt_rtpp;
 			crt_rtpp = last_rtpp->rn_next;
 			shm_free(last_rtpp);
 		}
 		crt_list = last_list->rset_next;
6711306d
 		lock_release(last_list->rset_lock);
789dec73
 
 		lock_destroy(last_list->rset_lock);
 		lock_dealloc((void*)last_list->rset_lock);
 		last_list->rset_lock = NULL;
 
ddea262f
 		shm_free(last_list);
789dec73
 		last_list = NULL;
ddea262f
 	}
789dec73
 	lock_release(rtpp_set_list->rset_head_lock);
 
 	lock_destroy(rtpp_set_list->rset_head_lock);
 	lock_dealloc((void*)rtpp_set_list->rset_head_lock);
 	rtpp_set_list->rset_head_lock = NULL;
ddea262f
 
 	shm_free(rtpp_set_list);
789dec73
 	rtpp_set_list = NULL;
2625ab3c
 
 	/* destroy the hastable which keeps the call-id <-> selected_node relation */
 	if (!rtpengine_hash_table_destroy()) {
 		LM_ERR("rtpengine_hash_table_destroy() failed!\n");
 	} else {
 		LM_DBG("rtpengine_hash_table_destroy() success!\n");
 	}
785ede5b
 	if(_rtpe_list_version!=NULL) {
 		shm_free(_rtpe_list_version);
 		_rtpe_list_version = NULL;
 	}
ddea262f
 }
 
 
 static char * gencookie(void)
 {
3e92a47f
 	static char cook[34];
ddea262f
 
3e92a47f
 	snprintf(cook, 34, "%d_%d_%u ", server_id, (int)mypid, myseqn);
ddea262f
 	myseqn++;
 	return cook;
 }
 
 
 
 static const char *transports[] = {
 	[0x00]	= "RTP/AVP",
 	[0x01]	= "RTP/SAVP",
 	[0x02]	= "RTP/AVPF",
 	[0x03]	= "RTP/SAVPF",
99695c28
 	[0x04]	= "UDP/TLS/RTP/SAVP",
 	[0x06]	= "UDP/TLS/RTP/SAVPF",
ddea262f
 };
 
8bf1935c
 static int parse_codec_flag(struct ng_flags_parse *ng_flags, const str *key, const str *val,
134c6b4b
 		const char *cmp1, const char *cmp2, const char *dictstr,
8bf1935c
 		bencode_item_t **dictp)
 {
 	str s;
 
 	if (!str_key_val_prefix(key, cmp1, val, &s)) {
 		if (!cmp2)
 			return 0;
 		if (!str_key_val_prefix(key, cmp2, val, &s))
 			return 0;
 	}
 
 	if (!*dictp) {
 		*dictp = bencode_list(ng_flags->dict->buffer);
134c6b4b
 		bencode_dictionary_add(ng_flags->codec, dictstr,
8bf1935c
 			*dictp);
 	}
 	bencode_list_add_str(*dictp, &s);
 
 	return 1;
 }
 
7526431e
 static int parse_flags(struct ng_flags_parse *ng_flags, struct sip_msg *msg, enum rtpe_operation *op,
 		const char *flags_str)
 {
 	char *e;
 	const char *err;
81a1ebc7
 	str key, val, s;
7526431e
 
 	if (!flags_str)
 		return 0;
 
 	while (1) {
 		while (*flags_str == ' ')
 			flags_str++;
 
 		key.s = (void *) flags_str;
 		val.len = key.len = -1;
 		val.s = NULL;
 
 		e = strpbrk(key.s, " =");
 		if (!e)
 			e = key.s + strlen(key.s);
 		else if (*e == '=') {
 			key.len = e - key.s;
 			val.s = e + 1;
 			e = strchr(val.s, ' ');
 			if (!e)
 				e = val.s + strlen(val.s);
 			val.len = e - val.s;
 		}
 
 		if (key.len == -1)
 			key.len = e - key.s;
 		if (!key.len)
 			break;
 
81a1ebc7
 		/* check for items which have their own sub-list */
e87cad92
 		if (str_key_val_prefix(&key, "replace", &val, &s)) {
81a1ebc7
 			bencode_list_add_str(ng_flags->replace, &s);
 			goto next;
 		}
e87cad92
 		if (str_key_val_prefix(&key, "SDES", &val, &s)) {
 			bencode_list_add_str(ng_flags->sdes, &s);
 			goto next;
 		}
6ab14314
 		if (str_key_val_prefix(&key, "T38", &val, &s) || str_key_val_prefix(&key, "T.38", &val, &s)) {
 			bencode_list_add_str(ng_flags->t38, &s);
 			goto next;
 		}
e87cad92
 		if (str_key_val_prefix(&key, "rtcp-mux", &val, &s)) {
81a1ebc7
 			bencode_list_add_str(ng_flags->rtcp_mux, &s);
 			goto next;
 		}
 
134c6b4b
 		if (parse_codec_flag(ng_flags, &key, &val, "transcode", "codec-transcode", "transcode", &ng_flags->codec_transcode))
e87cad92
 			goto next;
134c6b4b
 		if (parse_codec_flag(ng_flags, &key, &val, "codec-strip", NULL, "strip", &ng_flags->codec_strip))
e87cad92
 			goto next;
134c6b4b
 		if (parse_codec_flag(ng_flags, &key, &val, "codec-offer", NULL, "offer", &ng_flags->codec_offer))
e87cad92
 			goto next;
134c6b4b
 		if (parse_codec_flag(ng_flags, &key, &val, "codec-mask", NULL, "mask", &ng_flags->codec_mask))
8bf1935c
 			goto next;
134c6b4b
 		if (parse_codec_flag(ng_flags, &key, &val, "codec-set", NULL, "set", &ng_flags->codec_set))
8bf1935c
 			goto next;
134c6b4b
 		if (parse_codec_flag(ng_flags, &key, &val, "codec-except", NULL, "except", &ng_flags->codec_except))
e87cad92
 			goto next;
 
81a1ebc7
 		/* check for specially handled items */
7526431e
 		switch (key.len) {
 			case 3:
0d89dc5e
 				if (str_eq(&key, "RTP") && !val.s) {
7526431e
 					ng_flags->transport |= 0x100;
 					ng_flags->transport &= ~0x001;
 				}
0d89dc5e
 				else if (str_eq(&key, "AVP") && !val.s) {
7526431e
 					ng_flags->transport |= 0x100;
 					ng_flags->transport &= ~0x002;
 				}
639fce79
 				else if (str_eq(&key, "TOS") && val.s)
 					bencode_dictionary_add_integer(ng_flags->dict, "TOS", atoi(val.s));
7526431e
 				else
81a1ebc7
 					goto generic;
 				goto next;
7526431e
 				break;
 
 			case 4:
0d89dc5e
 				if (str_eq(&key, "SRTP") && !val.s)
7526431e
 					ng_flags->transport |= 0x101;
0d89dc5e
 				else if (str_eq(&key, "AVPF") && !val.s)
7526431e
 					ng_flags->transport |= 0x102;
0d89dc5e
 				else if (str_eq(&key, "DTLS") && !val.s)
99695c28
 					ng_flags->transport |= 0x104;
7526431e
 				else
81a1ebc7
 					goto generic;
 				goto next;
7526431e
 				break;
 
 			case 6:
81a1ebc7
 				if (str_eq(&key, "to-tag")) {
e69579bc
 					if (val.s)
 						ng_flags->to_tag = val;
7526431e
 					ng_flags->to = 1;
81a1ebc7
 					goto next;
 				}
7526431e
 				break;
 
 			case 7:
0d89dc5e
 				if (str_eq(&key, "RTP/AVP") && !val.s)
7526431e
 					ng_flags->transport = 0x100;
e69579bc
 				else if (str_eq(&key, "call-id")) {
 					err = "missing value";
 					if (!val.s)
 						goto error;
 					ng_flags->call_id = val;
81a1ebc7
 				}
e69579bc
 				else
 					goto generic;
 				goto next;
7526431e
 				break;
 
 			case 8:
81a1ebc7
 				if (str_eq(&key, "internal") || str_eq(&key, "external"))
 					bencode_list_add_str(ng_flags->direction, &key);
0d89dc5e
 				else if (str_eq(&key, "RTP/AVPF") && !val.s)
7526431e
 					ng_flags->transport = 0x102;
0d89dc5e
 				else if (str_eq(&key, "RTP/SAVP") && !val.s)
7526431e
 					ng_flags->transport = 0x101;
e69579bc
 				else if (str_eq(&key, "from-tag")) {
 					err = "missing value";
 					if (!val.s)
 						goto error;
 					ng_flags->from_tag = val;
76b1f3bf
 					ng_flags->directional = 1;
e69579bc
 				}
7526431e
 				else
81a1ebc7
 					goto generic;
 				goto next;
7526431e
 				break;
 
 			case 9:
0d89dc5e
 				if (str_eq(&key, "RTP/SAVPF") && !val.s)
7526431e
 					ng_flags->transport = 0x103;
d54bcfad
 				else if (str_eq(&key, "direction"))
 					bencode_list_add_str(ng_flags->direction, &val);
640206f0
 				else
 					goto generic;
 				goto next;
7526431e
 				break;
 
 			case 10:
 				if (str_eq(&key, "via-branch")) {
 					err = "missing value";
 					if (!val.s)
 						goto error;
 					err = "invalid value";
 					if (*val.s == '1' || *val.s == '2')
 						ng_flags->via = *val.s - '0';
 					else if (str_eq(&val, "auto"))
 						ng_flags->via = 3;
 					else if (str_eq(&val, "extra"))
 						ng_flags->via = -1;
2baa05ad
 					else if (str_eq(&val, "next"))
 						ng_flags->via = -2;
ad3ae013
 					else if (str_eq(&val, "auto-next") || str_eq(&val, "next-auto"))
 						ng_flags->via = -3;
 					else if (str_eq(&val, "auto-extra") || str_eq(&val, "extra-auto"))
 						ng_flags->via = -4;
7526431e
 					else
 						goto error;
81a1ebc7
 					goto next;
7526431e
 				}
 				break;
 
 			case 11:
81a1ebc7
 				if (str_eq(&key, "repacketize")) {
7526431e
 					err = "missing value";
 					if (!val.s)
 						goto error;
 					ng_flags->packetize = 0;
 					while (isdigit(*val.s)) {
 						ng_flags->packetize *= 10;
 						ng_flags->packetize += *val.s - '0';
 						val.s++;
 					}
 					err = "invalid value";
 					if (!ng_flags->packetize)
 						goto error;
 					bencode_dictionary_add_integer(ng_flags->dict, "repacketize", ng_flags->packetize);
 				}
76b1f3bf
 				else if (str_eq(&key, "directional"))
 					ng_flags->directional = 1;
 				else
 					goto generic;
 				goto next;
7526431e
 				break;
 
 			case 12:
 				if (str_eq(&key, "force-answer")) {
 					err = "cannot force answer in non-offer command";
 					if (*op != OP_OFFER)
 						goto error;
 					*op = OP_ANSWER;
81a1ebc7
 					goto next;
7526431e
 				}
0d89dc5e
 				else if (str_eq(&key, "delete-delay") && val.s)
 					bencode_dictionary_add_integer(ng_flags->dict, "delete delay", atoi(val.s));
7526431e
 				break;
99695c28
 
 			case 16:
0d89dc5e
 				if (str_eq(&key, "UDP/TLS/RTP/SAVP") && !val.s)
99695c28
 					ng_flags->transport = 0x104;
 				else
 					goto generic;
 				goto next;
 				break;
 
 			case 17:
0d89dc5e
 				if (str_eq(&key, "UDP/TLS/RTP/SAVPF") && !val.s)
99695c28
 					ng_flags->transport = 0x106;
 				else
 					goto generic;
 				goto next;
 				break;
 
7526431e
 		}
 
81a1ebc7
 generic:
 		if (!val.s)
 			bencode_list_add_str(ng_flags->flags, &key);
 		else
 			bencode_dictionary_str_add_str(ng_flags->dict, &key, &val);
 		goto next;
 
 next:
7526431e
 		flags_str = e;
 	}
 
 	return 0;
 
 error:
 	if (val.s)
 		LM_ERR("error processing flag `%.*s' (value '%.*s'): %s\n", key.len, key.s,
 				val.len, val.s, err);
 	else
 		LM_ERR("error processing flag `%.*s': %s\n", key.len, key.s, err);
 	return -1;
 }
 
ddea262f
 static bencode_item_t *rtpp_function_call(bencode_buffer_t *bencbuf, struct sip_msg *msg,
7526431e
 	enum rtpe_operation op, const char *flags_str, str *body_out)
ddea262f
 {
7526431e
 	struct ng_flags_parse ng_flags;
 	bencode_item_t *item, *resp;
e69579bc
 	str viabranch = STR_NULL;
5f936a38
 	str body = STR_NULL, error = STR_NULL;
ffee45da
 	int ret, queried_nodes = 0, cont_type = 0;
ddea262f
 	struct rtpp_node *node;
 	char *cp;
5ee67473
 	pv_value_t pv_val;
2baa05ad
 	char md5[MD5_LEN];
 	char branch_buf[MAX_BRANCH_PARAM_LEN];
9178da02
 	bencode_item_t *result;
ad3ae013
 	tm_cell_t *t;
 	unsigned int branch_idx;
ddea262f
 
 	/*** get & init basic stuff needed ***/
 
7526431e
 	memset(&ng_flags, 0, sizeof(ng_flags));
 
0a2450e6
 	if(IS_SIP(msg) || IS_SIP_REPLY(msg)) {
 		if (get_callid(msg, &ng_flags.call_id) == -1 || ng_flags.call_id.len == 0) {
 			LM_ERR("can't get Call-Id field\n");
 			return NULL;
 		}
 		if (get_to_tag(msg, &ng_flags.to_tag) == -1) {
 			LM_ERR("can't get To tag\n");
 			return NULL;
 		}
 		if (get_from_tag(msg, &ng_flags.from_tag) == -1 || ng_flags.from_tag.len == 0) {
 			LM_ERR("can't get From tag\n");
 			return NULL;
 		}
ddea262f
 	}
 	if (bencode_buffer_init(bencbuf)) {
7526431e
 		LM_ERR("could not initialize bencode_buffer_t\n");
ddea262f
 		return NULL;
 	}
7526431e
 	ng_flags.dict = bencode_dictionary(bencbuf);
ddea262f
 
9178da02
 	item = bencode_dictionary_add_list(ng_flags.dict, "supports");
 	bencode_list_add_string(item, "load limit");
 
f049d553
 	body.s = NULL;
498d7649
 	ng_flags.flags = bencode_list(bencbuf);
 
ddea262f
 	if (op == OP_OFFER || op == OP_ANSWER) {
7526431e
 		ng_flags.direction = bencode_list(bencbuf);
 		ng_flags.replace = bencode_list(bencbuf);
 		ng_flags.rtcp_mux = bencode_list(bencbuf);
e87cad92
 		ng_flags.sdes = bencode_list(bencbuf);
6ab14314
 		ng_flags.t38 = bencode_list(bencbuf);
e87cad92
 		ng_flags.codec = bencode_dictionary(bencbuf);
ddea262f
 
5ee67473
 		if (read_sdp_pvar!= NULL) {
 			if (read_sdp_pvar->getf(msg,&read_sdp_pvar->pvp, &pv_val) < 0)
 			{
 				LM_ERR("error getting pvar value <%.*s>\n", read_sdp_pvar_str.len, read_sdp_pvar_str.s);
 				goto error;
 			} else {
 				body = pv_val.rs;
 			}
 
ffee45da
 		} else if ((cont_type = extract_body(msg, &body)) == -1) {
ddea262f
 			LM_ERR("can't extract body from the message\n");
 			goto error;
 		}
f049d553
 		if (body_intermediate.s)
 			bencode_dictionary_add_str(ng_flags.dict, "sdp", &body_intermediate);
 		else
 			bencode_dictionary_add_str(ng_flags.dict, "sdp", &body);
ddea262f
 	}
 
 	/*** parse flags & build dictionary ***/
 
7526431e
 	ng_flags.to = (op == OP_DELETE) ? 0 : 1;
ddea262f
 
7526431e
 	if (parse_flags(&ng_flags, msg, &op, flags_str))
 		goto error;
ddea262f
 
0a2450e6
 	if(!IS_SIP(msg) && !IS_SIP_REPLY(msg)) {
 		/* check required values */
 		if (ng_flags.call_id.len == 0) {
 			LM_ERR("can't get Call-Id field\n");
 			return NULL;
 		}
 		if (ng_flags.from_tag.len == 0) {
 			LM_ERR("can't get From tag\n");
 			return NULL;
 		}
 	}
 
ffee45da
 	/* trickle ice sdp fragment? */
 	if (cont_type == 3)
 		bencode_list_add_string(ng_flags.flags, "fragment");
 
ddea262f
 	/* only add those if any flags were given at all */
7526431e
 	if (ng_flags.direction && ng_flags.direction->child)
 		bencode_dictionary_add(ng_flags.dict, "direction", ng_flags.direction);
 	if (ng_flags.flags && ng_flags.flags->child)
 		bencode_dictionary_add(ng_flags.dict, "flags", ng_flags.flags);
 	if (ng_flags.replace && ng_flags.replace->child)
 		bencode_dictionary_add(ng_flags.dict, "replace", ng_flags.replace);
e87cad92
 	if (ng_flags.codec && ng_flags.codec->child)
 		bencode_dictionary_add(ng_flags.dict, "codec", ng_flags.codec);
7526431e
 	if ((ng_flags.transport & 0x100))
 		bencode_dictionary_add_string(ng_flags.dict, "transport-protocol",
99695c28
 				transports[ng_flags.transport & 0x007]);
7526431e
 	if (ng_flags.rtcp_mux && ng_flags.rtcp_mux->child)
 		bencode_dictionary_add(ng_flags.dict, "rtcp-mux", ng_flags.rtcp_mux);
e87cad92
 	if (ng_flags.sdes && ng_flags.sdes->child)
 		bencode_dictionary_add(ng_flags.dict, "SDES", ng_flags.sdes);
6ab14314
 	if (ng_flags.t38 && ng_flags.t38->child)
 		bencode_dictionary_add(ng_flags.dict, "T.38", ng_flags.t38);
7526431e
 
e69579bc
 	bencode_dictionary_add_str(ng_flags.dict, "call-id", &ng_flags.call_id);
7526431e
 
 	if (ng_flags.via) {
ad3ae013
 		/* pre-process */
2baa05ad
 		switch (ng_flags.via) {
 			case 3:
 				ng_flags.via = (msg->first_line.type == SIP_REPLY) ? 2 : 1;
ad3ae013
 				break;
 			case -3:
 				ng_flags.via = (msg->first_line.type == SIP_REPLY) ? 1 : -2;
 				break;
 			case -4:
 				ng_flags.via = (msg->first_line.type == SIP_REPLY) ? 1 : -1;
 				break;
 		}
 
 		ret = -1;
 		switch (ng_flags.via) {
2baa05ad
 			case 1:
 			case 2:
 				ret = get_via_branch(msg, ng_flags.via, &viabranch);
 				break;
 			case -1:
 				if (extra_id_pv)
 					ret = get_extra_id(msg, &viabranch);
 				break;
 			case -2:
 				if (!char_msg_val(msg, md5))
 					break;
ad3ae013
 				branch_idx = 0;
 				if (tmb.t_gett) {
 					t = tmb.t_gett();
 					if (t && t != T_UNDEFINED)
 						branch_idx = t->nr_of_outgoings;
 				}
2baa05ad
 				msg->hash_index = hash(msg->callid->body, get_cseq(msg)->number);
 
 				viabranch.s = branch_buf;
ad3ae013
 				if (branch_builder(msg->hash_index, 0, md5, branch_idx, branch_buf, &viabranch.len))
2baa05ad
 					ret = 0;
 				break;
 		}
 
ddea262f
 		if (ret == -1 || viabranch.len == 0) {
 			LM_ERR("can't get Via branch/extra ID\n");
 			goto error;
 		}
7526431e
 		bencode_dictionary_add_str(ng_flags.dict, "via-branch", &viabranch);
ddea262f
 	}
 
 	item = bencode_list(bencbuf);
7526431e
 	bencode_dictionary_add(ng_flags.dict, "received-from", item);
ddea262f
 	bencode_list_add_string(item, (msg->rcv.src_ip.af == AF_INET) ? "IP4" : (
 		(msg->rcv.src_ip.af == AF_INET6) ? "IP6" :
 		"?"
 	) );
 	bencode_list_add_string(item, ip_addr2a(&msg->rcv.src_ip));
 
76b1f3bf
 	if (op == OP_BLOCK_DTMF || op == OP_BLOCK_MEDIA || op == OP_UNBLOCK_DTMF
12470055
 			|| op == OP_UNBLOCK_MEDIA || op == OP_START_FORWARDING || op == OP_STOP_FORWARDING
 			|| op == OP_SILENCE_MEDIA || op == OP_UNSILENCE_MEDIA)
76b1f3bf
 	{
 		if (ng_flags.directional)
 			bencode_dictionary_add_str(ng_flags.dict, "from-tag", &ng_flags.from_tag);
 	}
 	else if ((msg->first_line.type == SIP_REQUEST && op != OP_ANSWER)
e69579bc
 		|| (msg->first_line.type == SIP_REPLY && op == OP_DELETE)
639c29f2
 		|| (msg->first_line.type == SIP_REPLY && op == OP_ANSWER)
 		|| ng_flags.directional) /* set if from-tag was set manually */
ddea262f
 	{
e69579bc
 		bencode_dictionary_add_str(ng_flags.dict, "from-tag", &ng_flags.from_tag);
 		if (ng_flags.to && ng_flags.to_tag.s && ng_flags.to_tag.len)
 			bencode_dictionary_add_str(ng_flags.dict, "to-tag", &ng_flags.to_tag);
ddea262f
 	}
 	else {
e69579bc
 		if (!ng_flags.to_tag.s || !ng_flags.to_tag.len) {
ddea262f
 			LM_ERR("No to-tag present\n");
 			goto error;
 		}
e69579bc
 		bencode_dictionary_add_str(ng_flags.dict, "from-tag", &ng_flags.to_tag);
 		bencode_dictionary_add_str(ng_flags.dict, "to-tag", &ng_flags.from_tag);
ddea262f
 	}
 
7526431e
 	bencode_dictionary_add_string(ng_flags.dict, "command", command_strings[op]);
ddea262f
 
 	/*** send it out ***/
 
 	if (bencbuf->error) {
 		LM_ERR("out of memory - bencode failed\n");
 		goto error;
 	}
 
ceffd956
 select_node:
ddea262f
 	do {
43451ec4
 		if (queried_nodes >= cfg_get(rtpengine,rtpengine_cfg,queried_nodes_limit)) {
dac57a30
 			LM_ERR("queried nodes limit reached\n");
 			goto error;
 		}
131d8999
 
e69579bc
 		node = select_rtpp_node(ng_flags.call_id, viabranch, 1, queried_nodes_ptr, queried_nodes, op);
ddea262f
 		if (!node) {
 			LM_ERR("no available proxies\n");
 			goto error;
 		}
2625ab3c
 
7526431e
 		cp = send_rtpp_command(node, ng_flags.dict, &ret);
d85096f5
 		/* if node is disabled permanent, don't recheck it later */
 		if (cp == NULL && node->rn_recheck_ticks != RTPENGINE_MAX_RECHECK_TICKS) {
2625ab3c
 			node->rn_disabled = 1;
43451ec4
 			node->rn_recheck_ticks = get_ticks() + cfg_get(rtpengine,rtpengine_cfg,rtpengine_disable_tout);
2625ab3c
 		}
131d8999
 
 		queried_nodes_ptr[queried_nodes++] = node;
ddea262f
 	} while (cp == NULL);
131d8999
 
ddea262f
 	LM_DBG("proxy reply: %.*s\n", ret, cp);
 
a902ae16
 	set_rtp_inst_pvar(msg, &node->rn_url);
ddea262f
 	/*** process reply ***/
 
7526431e
 	resp = bencode_decode_expect(bencbuf, cp, ret, BENCODE_DICTIONARY);
 	if (!resp) {
ddea262f
 		LM_ERR("failed to decode bencoded reply from proxy: %.*s\n", ret, cp);
 		goto error;
 	}
2625ab3c
 
9178da02
 	result = bencode_dictionary_get_expect(resp, "result", BENCODE_STRING);
 	if (!result) {
 		LM_ERR("No 'result' dictionary entry in response from proxy %.*s",
 				node->rn_url.len, node->rn_url.s);
 		goto error;
 	}
 
 	if (!bencode_strcmp(result, "load limit")) {
 		item = bencode_dictionary_get_expect(resp, "message", BENCODE_STRING);
 		if (!item)
 			LM_INFO("proxy %.*s has reached its load limit - trying next one",
 					node->rn_url.len, node->rn_url.s);
 		else
 			LM_INFO("proxy %.*s has reached its load limit (%.*s) - trying next one",
 					node->rn_url.len, node->rn_url.s,
ffee45da
 					(int) item->iov[1].iov_len, (char *) item->iov[1].iov_base);
9178da02
 		goto select_node;
 	}
 
 	if (!bencode_strcmp(result, "error")) {
ceffd956
 		if (!bencode_dictionary_get_str(resp, "error-reason", &error)) {
98877b5b
 			LM_ERR("proxy return error but didn't give an error reason: %.*s\n", ret, cp);
131d8999
 		} else {
2625ab3c
 			if ((RTPENGINE_SESS_LIMIT_MSG_LEN == error.len) &&
98877b5b
 				(strncmp(error.s, RTPENGINE_SESS_LIMIT_MSG, RTPENGINE_SESS_LIMIT_MSG_LEN) == 0))
ceffd956
 			{
 				LM_WARN("proxy %.*s: %.*s", node->rn_url.len, node->rn_url.s , error.len, error.s);
 				goto select_node;
 			}
853068a2
 			if ((RTPENGINE_SESS_OUT_OF_PORTS_MSG_LEN == error.len) &&
 				(strncmp(error.s, RTPENGINE_SESS_OUT_OF_PORTS_MSG, RTPENGINE_SESS_OUT_OF_PORTS_MSG_LEN) == 0))
 			{
 				LM_WARN("proxy %.*s: %.*s", node->rn_url.len, node->rn_url.s , error.len, error.s);
 				goto select_node;
 			}
 
ddea262f
 			LM_ERR("proxy replied with error: %.*s\n", error.len, error.s);
ceffd956
 		}
2625ab3c
 		goto error;
ddea262f
 	}
 
b531e175
 	/* add hastable entry with the node => */
e69579bc
 	if (!rtpengine_hash_table_lookup(ng_flags.call_id, viabranch, op)) {
b531e175
 		// build the entry
 		struct rtpengine_hash_entry *entry = shm_malloc(sizeof(struct rtpengine_hash_entry));
 		if (!entry) {
 			LM_ERR("rtpengine hash table fail to create entry for calllen=%d callid=%.*s viabranch=%.*s\n",
e69579bc
 				ng_flags.call_id.len, ng_flags.call_id.len, ng_flags.call_id.s,
 				viabranch.len, viabranch.s);
b531e175
 			goto skip_hash_table_insert;
 		}
 		memset(entry, 0, sizeof(struct rtpengine_hash_entry));
 
 		// fill the entry
e69579bc
 		if (ng_flags.call_id.s && ng_flags.call_id.len > 0) {
 			if (shm_str_dup(&entry->callid, &ng_flags.call_id) < 0) {
b531e175
 				LM_ERR("rtpengine hash table fail to duplicate calllen=%d callid=%.*s\n",
e69579bc
 					ng_flags.call_id.len, ng_flags.call_id.len, ng_flags.call_id.s);
b531e175
 				rtpengine_hash_table_free_entry(entry);
 				goto skip_hash_table_insert;
 			}
 		}
 		if (viabranch.s && viabranch.len > 0) {
 			if (shm_str_dup(&entry->viabranch, &viabranch) < 0) {
 				LM_ERR("rtpengine hash table fail to duplicate calllen=%d viabranch=%.*s\n",
e69579bc
 					ng_flags.call_id.len, viabranch.len, viabranch.s);
b531e175
 				rtpengine_hash_table_free_entry(entry);
 				goto skip_hash_table_insert;
 			}
 		}
 		entry->node = node;
 		entry->next = NULL;
 		entry->tout = get_ticks() + hash_table_tout;
 
 		// insert the key<->entry from the hashtable
e69579bc
 		if (!rtpengine_hash_table_insert(ng_flags.call_id, viabranch, entry)) {
b531e175
 			LM_ERR("rtpengine hash table fail to insert node=%.*s for calllen=%d callid=%.*s viabranch=%.*s\n",
e69579bc
 				node->rn_url.len, node->rn_url.s, ng_flags.call_id.len,
 				ng_flags.call_id.len, ng_flags.call_id.s, viabranch.len, viabranch.s);
b531e175
 			rtpengine_hash_table_free_entry(entry);
 			goto skip_hash_table_insert;
 		} else {
 			LM_DBG("rtpengine hash table insert node=%.*s for calllen=%d callid=%.*s viabranch=%.*s\n",
e69579bc
 				node->rn_url.len, node->rn_url.s, ng_flags.call_id.len,
 				ng_flags.call_id.len, ng_flags.call_id.s, viabranch.len, viabranch.s);
b531e175
 		}
 	}
 
 skip_hash_table_insert:
ddea262f
 	if (body_out)
 		*body_out = body;
 
2625ab3c
 	if (op == OP_DELETE) {
 		/* Delete the key<->value from the hashtable */
e69579bc
 		if (!rtpengine_hash_table_remove(ng_flags.call_id, viabranch, op)) {
5f936a38
 			LM_ERR("rtpengine hash table failed to remove entry for callen=%d callid=%.*s viabranch=%.*s\n",
e69579bc
 				ng_flags.call_id.len, ng_flags.call_id.len, ng_flags.call_id.s,
 				viabranch.len, viabranch.s);
2625ab3c
 		} else {
5f936a38
 			LM_DBG("rtpengine hash table remove entry for callen=%d callid=%.*s viabranch=%.*s\n",
e69579bc
 				ng_flags.call_id.len, ng_flags.call_id.len, ng_flags.call_id.s,
 				viabranch.len, viabranch.s);
2625ab3c
 		}
 	}
 
7526431e
 	return resp;
ddea262f
 
 error:
 	bencode_buffer_free(bencbuf);
 	return NULL;
 }
 
7526431e
 static int rtpp_function_call_simple(struct sip_msg *msg, enum rtpe_operation op, const char *flags_str)
 {
ddea262f
 	bencode_buffer_t bencbuf;
639c29f2
 	bencode_item_t *ret;
ddea262f
 
639c29f2
 	ret = rtpp_function_call(&bencbuf, msg, op, flags_str, NULL);
 	if (!ret)
ddea262f
 		return -1;
 
639c29f2
 	if (bencode_dictionary_get_strcmp(ret, "result", "ok")) {
 		LM_ERR("proxy didn't return \"ok\" result\n");
 		bencode_buffer_free(&bencbuf);
 		return -1;
 	}
 
ddea262f
 	bencode_buffer_free(&bencbuf);
 	return 1;
 }
 
639c29f2
 static int rtpengine_simple_wrap(struct sip_msg *msg, void *d, int more, enum rtpe_operation op) {
 	return rtpp_function_call_simple(msg, op, d);
 }
 
 
eba7dcbe
 static bencode_item_t *rtpp_function_call_ok(bencode_buffer_t *bencbuf, struct sip_msg *msg,
7526431e
 		enum rtpe_operation op, const char *flags_str, str *body)
 {
eba7dcbe
 	bencode_item_t *ret;
 
7526431e
 	ret = rtpp_function_call(bencbuf, msg, op, flags_str, body);
eba7dcbe
 	if (!ret)
 		return NULL;
 
 	if (bencode_dictionary_get_strcmp(ret, "result", "ok")) {
 		LM_ERR("proxy didn't return \"ok\" result\n");
 		bencode_buffer_free(bencbuf);
 		return NULL;
 	}
 
 	return ret;
 }
 
ddea262f
 
 
 static int
 rtpp_test(struct rtpp_node *node, int isdisabled, int force)
 {
 	bencode_buffer_t bencbuf;
 	bencode_item_t *dict;
 	char *cp;
 	int ret;
 
1190ac60
 	if(node->rn_recheck_ticks == RTPENGINE_MAX_RECHECK_TICKS){
98877b5b
 		LM_DBG("rtpp %s disabled for ever\n", node->rn_url.s);
ddea262f
 		return 1;
 	}
 	if (force == 0) {
 		if (isdisabled == 0)
 			return 0;
 		if (node->rn_recheck_ticks > get_ticks())
 			return 1;
 	}
 
 	if (bencode_buffer_init(&bencbuf)) {
 		LM_ERR("could not initialized bencode_buffer_t\n");
 		return 1;
 	}
 	dict = bencode_dictionary(&bencbuf);
 	bencode_dictionary_add_string(dict, "command", "ping");
 	if (bencbuf.error)
 		goto benc_error;
 
 	cp = send_rtpp_command(node, dict, &ret);
 	if (!cp) {
5192377b
 		node->rn_disabled = 1;
43451ec4
 		node->rn_recheck_ticks = get_ticks() + cfg_get(rtpengine,rtpengine_cfg,rtpengine_disable_tout);
ddea262f
 		LM_ERR("proxy did not respond to ping\n");
 		goto error;
 	}
 
 	dict = bencode_decode_expect(&bencbuf, cp, ret, BENCODE_DICTIONARY);
 	if (!dict || bencode_dictionary_get_strcmp(dict, "result", "pong")) {
 		LM_ERR("proxy responded with invalid response\n");
 		goto error;
 	}
 
dda07c69
 	LM_INFO("rtpengine instance <%s> found, support for it %senabled\n",
98877b5b
 		node->rn_url.s, force == 0 ? "re-" : "");
ddea262f
 
 	bencode_buffer_free(&bencbuf);
 	return 0;
 
 benc_error:
98877b5b
 	LM_ERR("out of memory - bencode failed\n");
ddea262f
 error:
 	bencode_buffer_free(&bencbuf);
 	return 1;
 }
 
 static char *
 send_rtpp_command(struct rtpp_node *node, bencode_item_t *dict, int *outlen)
 {
 	struct sockaddr_un addr;
 	int fd, len, i, vcnt;
43451ec4
 	int rtpengine_retr, rtpengine_tout_ms = 1000;
ddea262f
 	char *cp;
d37b84be
 	static char buf[0x40000];
ddea262f
 	struct pollfd fds[1];
 	struct iovec *v;
26206894
 	str cmd = STR_NULL;
d37b84be
 	const static str rtpe_proto = { "ng.rtpengine.com", 16 };
 	str request, response;
ddea262f
 
 	v = bencode_iovec(dict, &vcnt, 1, 0);
 	if (!v) {
 		LM_ERR("error converting bencode to iovec\n");
 		return NULL;
 	}
 
 	len = 0;
 	cp = buf;
d37b84be
 	rtpengine_tout_ms = cfg_get(rtpengine,rtpengine_cfg,rtpengine_tout_ms);
 
 	if (node->rn_umode == RNU_LOCAL) {
ddea262f
 		memset(&addr, 0, sizeof(addr));
 		addr.sun_family = AF_LOCAL;
 		strncpy(addr.sun_path, node->rn_address,
98877b5b
 			sizeof(addr.sun_path) - 1);
ddea262f
 #ifdef HAVE_SOCKADDR_SA_LEN
 		addr.sun_len = strlen(addr.sun_path);
 #endif
 
 		fd = socket(AF_LOCAL, SOCK_STREAM, 0);
 		if (fd < 0) {
 			LM_ERR("can't create socket\n");
 			goto badproxy;
 		}
 		if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
 			close(fd);
dda07c69
 			LM_ERR("can't connect to RTPEngine <%s>\n", node->rn_url.s);
ddea262f
 			goto badproxy;
 		}
 
 		do {
 			len = writev(fd, v + 1, vcnt);
 		} while (len == -1 && errno == EINTR);
 		if (len <= 0) {
 			close(fd);
dda07c69
 			LM_ERR("can't send command to RTPEngine <%s>\n", node->rn_url.s);
ddea262f
 			goto badproxy;
 		}
 		do {
 			len = read(fd, buf, sizeof(buf) - 1);
 		} while (len == -1 && errno == EINTR);
 		close(fd);
 		if (len <= 0) {
dda07c69
 			LM_ERR("can't read reply from RTPEngine <%s>\n", node->rn_url.s);
ddea262f
 			goto badproxy;
 		}
d37b84be
 	} else if (node->rn_umode == RNU_WS || node->rn_umode == RNU_WSS) {
 		/* assemble full request string, flatten iovec */
 		v[0].iov_base = gencookie();
 		v[0].iov_len = strlen(v[0].iov_base);
 		len = 0;
 		for (i = 0; i <= vcnt; i++)
 			len += v[i].iov_len;
 		request.s = pkg_malloc(len + 1);
 		if (!request.s) {
 			LM_ERR("out of memory\n");
 			goto badproxy;
 		}
 		len = 0;
 		for (i = 0; i <= vcnt; i++) {
 			memcpy(request.s + len, v[i].iov_base, v[i].iov_len);
 			len += v[i].iov_len;
 		}
 		request.s[len] = '\0';
 		request.len = len;
 
 		len = _rtpe_lwscb.request(&node->rn_url, (str *) &rtpe_proto, &request, &response,
 				rtpengine_tout_ms * 1000);
 
 		if (len < 0) {
 			LM_ERR("failed to do websocket request\n");
 			goto badproxy;
 		}
 
 		/* process/copy response; verify cookie */
 		if (response.len < v[0].iov_len) {
 			LM_ERR("empty or short websocket response\n");
 			pkg_free(response.s);
 			goto badproxy;
 		}
 		if (memcmp(response.s, v[0].iov_base, v[0].iov_len)) {
 			LM_ERR("mismatched cookie in websocket response\n");
 			pkg_free(response.s);
 			goto badproxy;
 		}
 		len = response.len - v[0].iov_len;
 		if (len >= sizeof(buf)) {
 			LM_ERR("websocket response too large\n");
 			pkg_free(response.s);
 			goto badproxy;
 		}
 		memcpy(buf, response.s + v[0].iov_len, len);
 		pkg_free(response.s);
ddea262f
 	} else {
d37b84be
 		/* UDP or UDP6 */
ddea262f
 		fds[0].fd = rtpp_socks[node->idx];
 		fds[0].events = POLLIN;
 		fds[0].revents = 0;
 		/* Drain input buffer */
 		while ((poll(fds, 1, 0) == 1) &&
98877b5b
 			((fds[0].revents & POLLIN) != 0)) {
15f226ae
 			/* coverity[check_return : FALSE] */
ddea262f
 			recv(rtpp_socks[node->idx], buf, sizeof(buf) - 1, 0);
 			fds[0].revents = 0;
 		}
 		v[0].iov_base = gencookie();
 		v[0].iov_len = strlen(v[0].iov_base);
9d1e8e25
 		rtpengine_retr = cfg_get(rtpengine,rtpengine_cfg,rtpengine_retr);
7526431e
 		for (i = 0; i < rtpengine_retr; i++) {
ddea262f
 			do {
 				len = writev(rtpp_socks[node->idx], v, vcnt + 1);
 			} while (len == -1 && (errno == EINTR || errno == ENOBUFS));
 			if (len <= 0) {
26206894
 				bencode_get_str(bencode_dictionary_get(dict, "command"), &cmd);
dda07c69
 				LM_ERR("can't send command \"%.*s\" to RTPEngine <%s>\n",
26206894
 					cmd.len, cmd.s, node->rn_url.s);
ddea262f
 				goto badproxy;
 			}
82192d06
 			while ((poll(fds, 1, rtpengine_tout_ms) == 1) &&
98877b5b
 				(fds[0].revents & POLLIN) != 0) {
ddea262f
 				do {
 					len = recv(rtpp_socks[node->idx], buf, sizeof(buf)-1, 0);
 				} while (len == -1 && errno == EINTR);
 				if (len <= 0) {
26206894
 					bencode_get_str(bencode_dictionary_get(dict, "command"), &cmd);
dda07c69
 					LM_ERR("can't read reply for command \"%.*s\" from RTPEngine <%s>\n",
26206894
 						cmd.len, cmd.s, node->rn_url.s);
ddea262f
 					goto badproxy;
 				}
 				if (len >= (v[0].iov_len - 1) &&
98877b5b
 					memcmp(buf, v[0].iov_base, (v[0].iov_len - 1)) == 0) {
ddea262f
 					len -= (v[0].iov_len - 1);
 					cp += (v[0].iov_len - 1);
 					if (len != 0) {
 						len--;
 						cp++;
 					}
 					goto out;
 				}
 				fds[0].revents = 0;
 			}
 		}
7526431e
 		if (i == rtpengine_retr) {
26206894
 			bencode_get_str(bencode_dictionary_get(dict, "command"), &cmd);
dda07c69
 			LM_ERR("timeout waiting reply for command \"%.*s\" from RTPEngine <%s>\n",
26206894
 				cmd.len, cmd.s, node->rn_url.s);
ddea262f
 			goto badproxy;
 		}
 	}
 
 out:
 	cp[len] = '\0';
 	*outlen = len;
 	return cp;
 
a309fe46
 badproxy:
ddea262f
 	return NULL;
 }
 
 /*
  * select the set with the id_set id
  */
 
4b0095fb
 static struct rtpp_set * select_rtpp_set(unsigned int id_set ){
ddea262f
 
 	struct rtpp_set * rtpp_list;
 	/*is it a valid set_id?*/
 
789dec73
 	if (!rtpp_set_list) {
 		LM_ERR("no rtpp_set_list\n");
 		return 0;
 	}
 
 	lock_get(rtpp_set_list->rset_head_lock);
 	if (!rtpp_set_list->rset_first) {
 		LM_ERR("no rtpp_set_list->rset_first\n");
 		lock_release(rtpp_set_list->rset_head_lock);
ddea262f
 		return 0;
 	}
 
98877b5b
 	for (rtpp_list=rtpp_set_list->rset_first; rtpp_list!=0 &&
 			rtpp_list->id_set!=id_set; rtpp_list=rtpp_list->rset_next);
 	if (!rtpp_list) {
ddea262f
 		LM_ERR(" script error-invalid id_set to be selected\n");
 	}
789dec73
 	lock_release(rtpp_set_list->rset_head_lock);
ddea262f
 
 	return rtpp_list;
 }
2625ab3c
 
ddea262f
 /*
2625ab3c
  * run the selection algorithm and return the new selected node
ddea262f
  */
 static struct rtpp_node *
131d8999
 select_rtpp_node_new(str callid, str viabranch, int do_test, struct rtpp_node **queried_nodes_ptr, int queried_nodes)
ddea262f
 {
 	struct rtpp_node* node;
2625ab3c
 	unsigned i, sum, sumcut, weight_sum;
 	int was_forced = 0;
ddea262f
 
a508f1a3
 	str hash_data;
 
7b8f1489
 	switch (hash_algo) {
 		case RTP_HASH_CALLID:
 			hash_data = callid;
 
 			break;
 		case RTP_HASH_SHA1_CALLID:
 			if (rtpengine_cb.SHA1 == NULL) {
 				/* don't throw warning here; there is already a warni*/
 				LM_BUG("SHA1 algo set but crypto not loaded! Program shouldn't have started!");
 				return NULL;
 			}
 
 			if (rtpengine_cb.SHA1(&callid, &hash_data) < 0) {
 				LM_ERR("SHA1 hash in crypto module failed!\n");
 				return NULL;
 			}
 
 			break;
10349080
 		case RTP_HASH_CRC32_CALLID:
 			crc32_uint(&callid, &sum);
 			goto retry;
7b8f1489
 		default:
 			LM_ERR("unknown hashing algo %d\n", hash_algo);
a508f1a3
 			return NULL;
 	}
 
ddea262f
 	/* XXX Use quick-and-dirty hashing algo */
7ad4dadc
 	sum = 0;
a508f1a3
 	for(i = 0; i < hash_data.len; i++)
 		sum += hash_data.s[i];
 
7b8f1489
 	/* FIXME this seems to affect the algorithm in a negative way
 	 * legacy code uses it; disable it for other algos */
 	if (hash_algo == RTP_HASH_CALLID) {
 		sum &= 0xff;
 	}
ddea262f
 
 retry:
10349080
 	LM_DBG("sum is = %u\n", sum);
ddea262f
 	weight_sum = 0;
 
789dec73
 	lock_get(active_rtpp_set->rset_lock);
2625ab3c
 	for (node=active_rtpp_set->rn_first; node!=NULL; node=node->rn_next) {
789dec73
 		/* Select only between displayed machines */
 		if (!node->rn_displayed) {
 			continue;
 		}
 
2625ab3c
 		/* Try to enable if it's time to try. */
ddea262f
 		if (node->rn_disabled && node->rn_recheck_ticks <= get_ticks()){
 			node->rn_disabled = rtpp_test(node, 1, 0);
 		}
2625ab3c
 
 		/* Select only between enabled machines */
131d8999
 		if (!node->rn_disabled && !is_queried_node(node, queried_nodes_ptr, queried_nodes)) {
ddea262f
 			weight_sum += node->rn_weight;
2625ab3c
 		}
ddea262f
 	}
789dec73
 	lock_release(active_rtpp_set->rset_lock);
2625ab3c
 
 	/* No proxies? Force all to be redetected, if not yet */
ddea262f
 	if (weight_sum == 0) {
cb6df953
 		if (!cfg_get(rtpengine,rtpengine_cfg,aggressive_redetection)) {
 			return NULL;
 		}
 
2625ab3c
 		if (was_forced) {
ddea262f
 			return NULL;
2625ab3c
 		}
 
ddea262f
 		was_forced = 1;
2625ab3c
 
789dec73
 		lock_get(active_rtpp_set->rset_lock);
f049d553
 		for(node=active_rtpp_set->rn_first; node!=NULL; node=node->rn_next) {
789dec73
 			/* Select only between displayed machines */
 			if (!node->rn_displayed) {
 				continue;
 			}
 
ddea262f
 			node->rn_disabled = rtpp_test(node, 1, 1);
 		}
789dec73
 		lock_release(active_rtpp_set->rset_lock);
2625ab3c
 
ddea262f
 		goto retry;
 	}
2625ab3c
 
 	/* sumcut here lays from 0 to weight_sum-1 */
ddea262f
 	sumcut = sum % weight_sum;
2625ab3c
 
ddea262f
 	/*
 	 * Scan proxy list and decrease until appropriate proxy is found.
 	 */
789dec73
 	lock_get(active_rtpp_set->rset_lock);
f049d553
 	for (node=active_rtpp_set->rn_first; node!=NULL; node=node->rn_next) {
789dec73
 		/* Select only between displayed machines */
 		if (!node->rn_displayed) {
 			continue;
 		}
 
2625ab3c
 		/* Select only between enabled machines */
ddea262f
 		if (node->rn_disabled)
 			continue;
2625ab3c
 
131d8999
 		/* Select only between not already queried machines */
 		if (is_queried_node(node, queried_nodes_ptr, queried_nodes))
 			continue;
 
 		/* Found machine */
789dec73
 		if (sumcut < node->rn_weight) {
 			lock_release(active_rtpp_set->rset_lock);
ddea262f
 			goto found;
789dec73
 		}
2625ab3c
 
 		/* Update sumcut if enabled machine */
ddea262f
 		sumcut -= node->rn_weight;
 	}
789dec73
 	lock_release(active_rtpp_set->rset_lock);
2625ab3c
 
ddea262f
 	/* No node list */
 	return NULL;
2625ab3c
 
ddea262f
 found:
 	if (do_test) {
789dec73
 		lock_get(active_rtpp_set->rset_lock);
ddea262f
 		node->rn_disabled = rtpp_test(node, node->rn_disabled, 0);
789dec73
 		if (node->rn_disabled) {
 			lock_release(active_rtpp_set->rset_lock);
ddea262f
 			goto retry;
789dec73
 		}
 		lock_release(active_rtpp_set->rset_lock);
ddea262f
 	}
2625ab3c
 
7ad4dadc
 	/* return selected node */
ddea262f
 	return node;
 }
 
2625ab3c
 /*
d75bb85c
  * lookup the hastable (key=callid value=node) and get the old node (e.g. for answer/delete)
2625ab3c
  */
 static struct rtpp_node *
4f4c4488
 select_rtpp_node_old(str callid, str viabranch, int do_test, enum rtpe_operation op)
2625ab3c
 {
 	struct rtpp_node *node = NULL;
 
4f4c4488
 	node = rtpengine_hash_table_lookup(callid, viabranch, op);
2625ab3c
 
 	if (!node) {
a7b1016a
 		LM_DBG("rtpengine hash table lookup failed to find node for calllen=%d callid=%.*s viabranch=%.*s\n",
5f936a38
 			callid.len, callid.len, callid.s, viabranch.len, viabranch.s);
2625ab3c
 		return NULL;
 	} else {
5f936a38
 		LM_DBG("rtpengine hash table lookup find node=%.*s for calllen=%d callid=%.*s viabranch=%.*s\n",
 			node->rn_url.len, node->rn_url.s, callid.len, callid.len, callid.s, viabranch.len, viabranch.s);
2625ab3c
 	}
 
7ad4dadc
 	return node;
 }
 
fe6614fe
 unsigned int node_in_set(struct rtpp_node *node, struct rtpp_set *set) {
 	struct rtpp_node *current = set->rn_first;
 	while (current) {
 		if (current->idx == node->idx) return 1;
 		current = current->rn_next;
 	}
 	return 0;
 }
 
7ad4dadc
 /*
  * Main balancing routine. This DO try to keep the same proxy for
  * the call if some proxies were disabled or enabled (e.g. kamctl command)
  */
 static struct rtpp_node *
4f4c4488
 select_rtpp_node(str callid, str viabranch, int do_test, struct rtpp_node **queried_nodes_ptr, int queried_nodes, enum rtpe_operation op)
7ad4dadc
 {
 	struct rtpp_node *node = NULL;
789dec73
 
3d481300
 	if (build_rtpp_socks(1, 0)) {
a032d400
 		LM_ERR("out of memory\n");
 		return NULL;
789dec73
 	}
 
 	if (!active_rtpp_set) {
 		default_rtpp_set = select_rtpp_set(setid_default);
 		active_rtpp_set = default_rtpp_set;
 	}
7ad4dadc
 
789dec73
 	if (!active_rtpp_set) {
7ad4dadc
 		LM_ERR("script error - no valid set selected\n");
 		return NULL;
 	}
 
 	// lookup node
4f4c4488
 	node = select_rtpp_node_old(callid, viabranch, do_test, op);
7ad4dadc
 
 	// check node
fe6614fe
 	if (!node || (node_in_set(node, active_rtpp_set) == 0)) {
7ad4dadc
 		// run the selection algorithm
131d8999
 		node = select_rtpp_node_new(callid, viabranch, do_test, queried_nodes_ptr, queried_nodes);
7ad4dadc
 
 		// check node
 		if (!node) {
 			LM_ERR("rtpengine failed to select new for calllen=%d callid=%.*s\n",
 				callid.len, callid.len, callid.s);
 			return NULL;
 		}
 	}
 
d75bb85c
 	// if node enabled, return it
2625ab3c
 	if (!node->rn_disabled) {
 		return node;
d75bb85c
 	}
 
5a537506
 	// if proper configuration and node manually or timeout disabled, return it
 	if (rtpengine_allow_op) {
1190ac60
 		if (node->rn_recheck_ticks == RTPENGINE_MAX_RECHECK_TICKS) {
d75bb85c
 			LM_DBG("node=%.*s for calllen=%d callid=%.*s is disabled(permanent) (probably still UP)! Return it\n",
 				node->rn_url.len, node->rn_url.s, callid.len, callid.len, callid.s);
97620f9e
 			return node;
5a537506
 		} else {
3f7a6240
 			LM_DBG("node=%.*s for calllen=%d callid=%.*s is disabled, either broke or timeout disabled!\n",
5a537506
 				node->rn_url.len, node->rn_url.s, callid.len, callid.len, callid.s);
f653ea8c
 			if (rtpengine_allow_op == 1) {
3f7a6240
 			        LM_DBG("Return it\n");
f653ea8c
 				return node;
 			}
d75bb85c
 		}
2625ab3c
 	}
 
 	return NULL;
 }
 
ddea262f
 static int
 get_extra_id(struct sip_msg* msg, str *id_str) {
98877b5b
 	if (msg == NULL || extra_id_pv == NULL || id_str == NULL) {
ddea262f
 		LM_ERR("bad parameters\n");
 		return -1;
 	}
98877b5b
 	if (pv_printf_s(msg, extra_id_pv, id_str) < 0) {
ddea262f
 		LM_ERR("cannot print the additional id\n");
 		return -1;
 	}
 
 	return 1;
 
 }
 
e005b899
 static int
f049d553
 set_rtpengine_set_from_avp(struct sip_msg *msg, int direction)
e005b899
 {
98877b5b
 	struct usr_avp *avp;
 	int_str setid_val;
5c6c74ab
 
98877b5b
 	if ((setid_avp_param == NULL) ||
 	   (avp = search_first_avp(setid_avp_type, setid_avp, &setid_val, 0)) == NULL) {
 		if (direction == 1 || !selected_rtpp_set_2)
 			active_rtpp_set = selected_rtpp_set_1;
 		else
 			active_rtpp_set = selected_rtpp_set_2;
 		return 1;
 	}
e005b899
 
98877b5b
 	if (avp->flags&AVP_VAL_STR) {
 		LM_ERR("setid_avp must hold an integer value\n");
 		return -1;
 	}
e005b899
 
98877b5b
 	active_rtpp_set = select_rtpp_set(setid_val.n);
 	if(active_rtpp_set == NULL) {
dda07c69
 		LM_ERR("could not locate engine set %u\n", setid_val.n);
98877b5b
 		return -1;
 	}
5c6c74ab
 
4b0095fb
 	LM_DBG("using rtpengine set %u\n", setid_val.n);
98877b5b
 
 	current_msg_id = msg->id;
 
 	return 1;
e005b899
 }
ddea262f
 
1aa2e58f
 static void avp_print_s(pv_elem_t *pv, char *str, int len, struct sip_msg *msg) {
 	pv_value_t val;
 
 	if (!pv)
 		return;
 
 	memset(&val, 0, sizeof(val));
 	val.flags = PV_VAL_STR;
 	val.rs.s = str;
 	val.rs.len = len;
 	pv->spec->setf(msg, &pv->spec->pvp, EQ_T, &val);
 }
 
 static void avp_print_decimal(pv_elem_t *pv, int num, struct sip_msg *msg) {
 	int len;
 	char buf[8];
 
 	len = snprintf(buf, sizeof(buf), "%i.%i", num / 10, abs(num % 10));
 	avp_print_s(pv, buf, len, msg);
 }
 static void avp_print_int(pv_elem_t *pv, int num, struct sip_msg *msg) {
 	int len;
 	char buf[8];
 
 	len = snprintf(buf, sizeof(buf), "%i", num);
 	avp_print_s(pv, buf, len, msg);
 }
 static void avp_print_time(pv_elem_t *pv, int num, struct sip_msg *msg) {
 	int len;
 	char buf[8];
 
 	len = snprintf(buf, sizeof(buf), "%i:%02i", num / 60, abs(num % 60));
 	avp_print_s(pv, buf, len, msg);
 }
 
 static void avp_print_mos(struct minmax_mos_stats *s, struct minmax_stats_vals *vals, long long created,
 		struct sip_msg *msg)
 {
 	if (!vals->avg_samples)
 		return;
 
 	avp_print_decimal(s->mos_pv, vals->mos / vals->avg_samples, msg);
 	avp_print_time(s->at_pv, vals->at - created, msg);
 	avp_print_int(s->packetloss_pv, vals->packetloss / vals->avg_samples, msg);
 	avp_print_int(s->jitter_pv, vals->jitter / vals->avg_samples, msg);
 	avp_print_int(s->roundtrip_pv, vals->roundtrip / vals->avg_samples, msg);
26a7b41f
 	avp_print_int(s->roundtrip_leg_pv, vals->roundtrip_leg / vals->avg_samples, msg);
1aa2e58f
 	avp_print_int(s->samples_pv, vals->samples / vals->avg_samples, msg);
 }
 
 static int decode_mos_vals_dict(struct minmax_stats_vals *vals, bencode_item_t *dict, const char *key) {
 	bencode_item_t *mos_ent;
 
 	mos_ent = bencode_dictionary_get_expect(dict, key, BENCODE_DICTIONARY);
 	if (!mos_ent)
 		return 0;
 
 	vals->mos = bencode_dictionary_get_integer(mos_ent, "MOS", -1);
 	vals->at = bencode_dictionary_get_integer(mos_ent, "reported at", -1);
 	vals->packetloss = bencode_dictionary_get_integer(mos_ent, "packet loss", -1);
 	vals->jitter = bencode_dictionary_get_integer(mos_ent, "jitter", -1);
 	vals->roundtrip = bencode_dictionary_get_integer(mos_ent, "round-trip time", -1);
26a7b41f
 	vals->roundtrip_leg = bencode_dictionary_get_integer(mos_ent, "round-trip time leg", -1);
1aa2e58f
 	vals->samples = bencode_dictionary_get_integer(mos_ent, "samples", -1);
 	vals->avg_samples = 1;
 
 	return 1;
 }
 
 static void parse_call_stats_1(struct minmax_mos_label_stats *mmls, bencode_item_t *dict,
 		struct sip_msg *msg)
 {
 	long long created;
 	str label, check;
 	long long ssrcs[4];
 	unsigned int num_ssrcs = 0, i;
 	long long ssrc;
 	char *endp;
 	bencode_item_t *ssrc_list,
 		       *ssrc_key,
 		       *ssrc_dict,
 		       *tags,
 		       *tag_key,
 		       *tag_dict,
 		       *medias,
 		       *media,
 		       *streams,
 		       *stream;
 	struct minmax_stats_vals min_vals = { .mos = 100 },
 				 max_vals = { .mos = -1  },
 				 average_vals = { .avg_samples = 0 },
 				 vals_decoded;
 
 	if (!mmls->got_any_pvs)
 		return;
 
 	/* check if only a subset of info is requested */
 	if (!mmls->label_pv)
 		goto ssrcs_done;
 
 	if (pv_printf_s(msg, mmls->label_pv, &label)) {
 		LM_ERR("error printing label PV\n");
 		return;
 	}
 	LM_DBG("rtpengine: looking for label '%.*s'\n", label.len, label.s);
 
 	/* walk through tags to find the label we're looking for */
 	tags = bencode_dictionary_get_expect(dict, "tags", BENCODE_DICTIONARY);
 	if (!tags)
 		return; /* label wanted but no tags found - return nothing */
 	LM_DBG("rtpengine: XXX got tags\n");
 
 	for (tag_key = tags->child; tag_key; tag_key = tag_key->sibling->sibling) {
 		LM_DBG("rtpengine: XXX got tag\n");
 		tag_dict = tag_key->sibling;
 		/* compare label */
 		if (!bencode_dictionary_get_str(tag_dict, "label", &check))
 			continue;
 		LM_DBG("rtpengine: XXX got label %.*s\n", check.len, check.s);
 		if (str_cmp(&check, &label))
 			continue;
 		LM_DBG("rtpengine: XXX label match\n");
 		medias = bencode_dictionary_get_expect(tag_dict, "medias", BENCODE_LIST);
 		if (!medias)
 			continue;
 		LM_DBG("rtpengine: XXX got medias\n");
 		for (media = medias->child; media; media = media->sibling) {
 			LM_DBG("rtpengine: XXX got media\n");
 			streams = bencode_dictionary_get_expect(media, "streams", BENCODE_LIST);
 			if (!streams)
 				continue;
 			LM_DBG("rtpengine: XXX got streams\n");
 			/* only check the first stream (RTP) */
 			stream = streams->child;
 			if (!stream)
 				continue;
 			LM_DBG("rtpengine: XXX got stream type %i\n", stream->type);
 			LM_DBG("rtpengine: XXX stream child '%.*s'\n", (int) stream->child->iov[1].iov_len, (char *) stream->child->iov[1].iov_base);
 			LM_DBG("rtpengine: XXX stream child val type %i\n", stream->child->sibling->type);
 			if ((ssrc = bencode_dictionary_get_integer(stream, "SSRC", -1)) == -1)
 				continue;
 			/* got a valid SSRC to watch for */
 			ssrcs[num_ssrcs] = ssrc;
 			LM_DBG("rtpengine: found SSRC '%lli' for label '%.*s'\n",
 					ssrc,
 					label.len, label.s);
 			num_ssrcs++;
 			/* see if we can do more */
 			if (num_ssrcs >= (sizeof(ssrcs) / sizeof(*ssrcs)))
 				goto ssrcs_done;
 		}
 	}
 	/* if we get here, we were looking for label. see if we found one. if not, return nothing */
 	if (num_ssrcs == 0)
 		return;
 
 ssrcs_done:
 	/* now look for the stats values */
 	created = bencode_dictionary_get_integer(dict, "created", 0);
 	ssrc_list = bencode_dictionary_get_expect(dict, "SSRC", BENCODE_DICTIONARY);
 	if (!ssrc_list)
 		return;
 
 	for (ssrc_key = ssrc_list->child; ssrc_key; ssrc_key = ssrc_key->sibling->sibling) {
 		/* see if this is a SSRC we're interested in */
 		if (num_ssrcs == 0)
 			goto ssrc_ok;
 		if (!bencode_get_str(ssrc_key, &check))
 			continue;
 		ssrc = strtoll(check.s, &endp, 10);
 		for (i = 0; i < num_ssrcs; i++) {
 			if (ssrcs[i] != ssrc)
 				continue;
 			/* it's a match */
 			LM_DBG("rtpengine: considering SSRC '%.*s'\n",
 					check.len, check.s);
 			goto ssrc_ok;
 		}
 		/* no match */
 		continue;
 
 ssrc_ok:
 		ssrc_dict = ssrc_key->sibling;
 		if (!ssrc_dict)
 			continue;
 
 		if (decode_mos_vals_dict(&vals_decoded, ssrc_dict, "average MOS")) {
a378f97a
 			if (vals_decoded.mos > 0) {
 				average_vals.avg_samples++;
 				average_vals.mos += vals_decoded.mos;
 				average_vals.packetloss += vals_decoded.packetloss;
 				average_vals.jitter += vals_decoded.jitter;
 				average_vals.roundtrip += vals_decoded.roundtrip;
 				average_vals.roundtrip_leg += vals_decoded.roundtrip_leg;
 				average_vals.samples += vals_decoded.samples;
 			}
1aa2e58f
 		}
 
 		if (decode_mos_vals_dict(&vals_decoded, ssrc_dict, "highest MOS")) {
 			if (vals_decoded.mos > max_vals.mos)
 				max_vals = vals_decoded;
 		}
 		if (decode_mos_vals_dict(&vals_decoded, ssrc_dict, "lowest MOS")) {
a378f97a
 			if (vals_decoded.mos > 0 && vals_decoded.mos < min_vals.mos)
1aa2e58f
 				min_vals = vals_decoded;
 		}
 	}
 
 	avp_print_mos(&mmls->max, &max_vals, created, msg);
 	avp_print_mos(&mmls->min, &min_vals, created, msg);
 	avp_print_mos(&mmls->average, &average_vals, created, msg);
 }
 
 static void parse_call_stats(bencode_item_t *dict, struct sip_msg *msg) {
 	if (!got_any_mos_pvs)
 		return;
 
 	parse_call_stats_1(&global_mos_stats, dict, msg);
 	parse_call_stats_1(&side_A_mos_stats, dict, msg);
 	parse_call_stats_1(&side_B_mos_stats, dict, msg);
 }
 
7526431e
 static int rtpengine_delete(struct sip_msg *msg, const char *flags) {
1aa2e58f
 	bencode_buffer_t bencbuf;
 	bencode_item_t *ret = rtpp_function_call_ok(&bencbuf, msg, OP_DELETE, flags, NULL);
 	if (!ret)
 		return -1;
 	parse_call_stats(ret, msg);
 	bencode_buffer_free(&bencbuf);
 	return 1;
 }
 
 static int rtpengine_query(struct sip_msg *msg, const char *flags) {
 	bencode_buffer_t bencbuf;
 	bencode_item_t *ret = rtpp_function_call_ok(&bencbuf, msg, OP_QUERY, flags, NULL);
 	if (!ret)
 		return -1;
 	parse_call_stats(ret, msg);
 	bencode_buffer_free(&bencbuf);
 	return 1;
ddea262f
 }
 
639c29f2
 static int rtpengine_rtpp_set_wrap(struct sip_msg *msg, int (*func)(struct sip_msg *msg, void *, int,
 			enum rtpe_operation),
 		void *data, int direction, enum rtpe_operation op)
f049d553
 {
 	int ret, more;
 
 	body_intermediate.s = NULL;
 
 	if (set_rtpengine_set_from_avp(msg, direction) == -1)
98877b5b
 		return -1;
f049d553
 
 	more = 1;
 	if (!selected_rtpp_set_2 || selected_rtpp_set_2 == selected_rtpp_set_1)
 		more = 0;
 
639c29f2
 	ret = func(msg, data, more, op);
f049d553
 	if (ret < 0)
 		return ret;
 
 	if (!more)
 		return ret;
 
 	direction = (direction == 1) ? 2 : 1;
 	if (set_rtpengine_set_from_avp(msg, direction) == -1)
98877b5b
 		return -1;
f049d553
 
639c29f2
 	ret = func(msg, data, 0, op);
f049d553
 	body_intermediate.s = NULL;
 	return ret;
 }
 
639c29f2
 static int rtpengine_delete_wrap(struct sip_msg *msg, void *d, int more, enum rtpe_operation op) {
f049d553
 	return rtpengine_delete(msg, d);
 }
 
639c29f2
 static int rtpengine_rtpp_set_wrap_fparam(struct sip_msg *msg, int (*func)(struct sip_msg *msg, void *, int,
 			enum rtpe_operation),
 		char *str1, int direction, enum rtpe_operation op)
ddea262f
 {
 	str flags;
e005b899
 
7526431e
 	flags.s = NULL;
15f226ae
 	if (str1) {
 		if (get_str_fparam(&flags, msg, (fparam_t *) str1)) {
 			LM_ERR("Error getting string parameter\n");
 			return -1;
 		}
 	}
7526431e
 
639c29f2
 	return rtpengine_rtpp_set_wrap(msg, func, flags.s, direction, op);
 }
 
 static int
 rtpengine_delete1_f(struct sip_msg* msg, char* str1, char* str2)
 {
 	return rtpengine_rtpp_set_wrap_fparam(msg, rtpengine_delete_wrap, str1, 1, OP_DELETE);
ddea262f
 }
 
639c29f2
 static int rtpengine_query_wrap(struct sip_msg *msg, void *d, int more, enum rtpe_operation op) {
1aa2e58f
 	return rtpengine_query(msg, d);
 }
 
 static int
 rtpengine_query1_f(struct sip_msg* msg, char* str1, char* str2)
 {
639c29f2
 	return rtpengine_rtpp_set_wrap_fparam(msg, rtpengine_query_wrap, str1, 1, OP_QUERY);
1aa2e58f
 }
 
 
ddea262f
 /* This function assumes p points to a line of requested type. */
 
 static int
f049d553
 set_rtpengine_set_n(struct sip_msg *msg, rtpp_set_link_t *rtpl, struct rtpp_set **out)
ddea262f
 {
 	pv_value_t val;
5c6c74ab
 	struct rtpp_node *node;
f049d553
 	int nb_active_nodes = 0;
ddea262f
 
 	if(rtpl->rset != NULL) {
 		current_msg_id = msg->id;
f049d553
 		*out = rtpl->rset;
 		return 1;
 	}
5c6c74ab
 
f049d553
 	if(pv_get_spec_value(msg, rtpl->rpv, &val)<0) {
 		LM_ERR("cannot evaluate pv param\n");
 		return -1;
 	}
 	if(!(val.flags & PV_VAL_INT)) {
 		LM_ERR("pv param must hold an integer value\n");
 		return -1;
 	}
 	*out = select_rtpp_set(val.ri);
 	if(*out==NULL) {
4b0095fb
 		LM_ERR("could not locate rtpengine set %u\n", val.ri);
f049d553
 		return -1;
 	}
 	current_msg_id = msg->id;
5c6c74ab
 
789dec73
 	lock_get((*out)->rset_lock);
f049d553
 	node = (*out)->rn_first;
 	while (node != NULL)
 	{
98877b5b
 		if (node->rn_disabled == 0) nb_active_nodes++;
 		node = node->rn_next;
f049d553
 	}
789dec73
 	lock_release((*out)->rset_lock);
f049d553
 
 	if ( nb_active_nodes > 0 )
 	{
 		LM_DBG("rtpp: selected proxy set ID %d with %d active nodes.\n",
98877b5b
 			current_msg_id, nb_active_nodes);
f049d553
 		return nb_active_nodes;
 	}
 	else
 	{
 		LM_WARN("rtpp: selected proxy set ID %d but it has no active node.\n",
98877b5b
 			current_msg_id);
f049d553
 		return -2;
 	}
 }
 
 static int
 set_rtpengine_set_f(struct sip_msg * msg, char * str1, char * str2)
 {
 	rtpp_set_link_t *rtpl1, *rtpl2;
 	int ret;
 
 	rtpl1 = (rtpp_set_link_t*)str1;
 	rtpl2 = (rtpp_set_link_t*)str2;
 
 	current_msg_id = 0;
 	active_rtpp_set = 0;
 	selected_rtpp_set_1 = 0;
 	selected_rtpp_set_2 = 0;
 
 	ret = set_rtpengine_set_n(msg, rtpl1, &selected_rtpp_set_1);
 	if (ret < 0)
 		return ret;
 
 	if (rtpl2) {
 		ret = set_rtpengine_set_n(msg, rtpl2, &selected_rtpp_set_2);
 		if (ret < 0)
 			return ret;
ddea262f
 	}
f049d553
 
ddea262f
 	return 1;
 }
 
 static int
7526431e
 rtpengine_manage(struct sip_msg *msg, const char *flags)
ddea262f
 {
 	int method;
 	int nosdp;
75040f62
 	tm_cell_t *t = NULL;
ddea262f
 
387de23a
 	if(route_type==BRANCH_FAILURE_ROUTE) {
 		/* do nothing in branch failure event route
 		 * - delete done on transaction failure route */
 		return 1;
 	}
 
98877b5b
 	if (msg->cseq==NULL && ((parse_headers(msg, HDR_CSEQ_F, 0)==-1) ||
387de23a
 	   (msg->cseq==NULL))) {
ddea262f
 		LM_ERR("no CSEQ header\n");
 		return -1;
 	}
 
 	method = get_cseq(msg)->method_id;
 
de77beff
 	if (!(method & (METHOD_INVITE|METHOD_ACK|METHOD_CANCEL|METHOD_BYE
 					|METHOD_UPDATE|METHOD_PRACK)))
ddea262f
 		return -1;
 
de77beff
 	if (method & (METHOD_CANCEL|METHOD_BYE))
7526431e
 		return rtpengine_delete(msg, flags);
ddea262f
 
98877b5b
 	if (msg->msg_flags & FL_SDP_BODY)
ddea262f
 		nosdp = 0;
 	else
 		nosdp = parse_sdp(msg);
 
98877b5b
 	if (msg->first_line.type == SIP_REQUEST) {
de77beff
 		if((method & (METHOD_ACK|METHOD_PRACK)) && nosdp==0)
f049d553
 			return rtpengine_offer_answer(msg, flags, OP_ANSWER, 0);
ddea262f
 		if(method==METHOD_UPDATE && nosdp==0)
f049d553
 			return rtpengine_offer_answer(msg, flags, OP_OFFER, 0);
ddea262f
 		if(method==METHOD_INVITE && nosdp==0) {
 			msg->msg_flags |= FL_SDP_BODY;
75040f62
 			if(tmb.t_gett!=NULL) {
 				t = tmb.t_gett();
 				if(t!=NULL && t!=T_UNDEFINED && t->uas.request!=NULL) {
 					t->uas.request->msg_flags |= FL_SDP_BODY;
 				}
 			}
ddea262f
 			if(route_type==FAILURE_ROUTE)
7526431e
 				return rtpengine_delete(msg, flags);
f049d553
 			return rtpengine_offer_answer(msg, flags, OP_OFFER, 0);
ddea262f
 		}
98877b5b
 	} else if (msg->first_line.type == SIP_REPLY) {
 		if (msg->first_line.u.reply.statuscode>=300)
7526431e
 			return rtpengine_delete(msg, flags);
98877b5b
 		if (nosdp==0) {
 			if (method==METHOD_UPDATE)
f049d553
 				return rtpengine_offer_answer(msg, flags, OP_ANSWER, 0);
98877b5b
 			if (tmb.t_gett==NULL || tmb.t_gett()==NULL
ddea262f
 					|| tmb.t_gett()==T_UNDEFINED)
f049d553
 				return rtpengine_offer_answer(msg, flags, OP_ANSWER, 0);
98877b5b
 			if (tmb.t_gett()->uas.request->msg_flags & FL_SDP_BODY)
f049d553
 				return rtpengine_offer_answer(msg, flags, OP_ANSWER, 0);
 			return rtpengine_offer_answer(msg, flags, OP_OFFER, 0);
ddea262f
 		}
 	}
 	return -1;
 }
 
639c29f2
 static int rtpengine_manage_wrap(struct sip_msg *msg, void *d, int more, enum rtpe_operation op) {
f049d553
 	return rtpengine_manage(msg, d);
 }
 
ddea262f
 static int
7526431e
 rtpengine_manage1_f(struct sip_msg *msg, char *str1, char *str2)
ddea262f
 {
639c29f2
 	return rtpengine_rtpp_set_wrap_fparam(msg, rtpengine_manage_wrap, str1, 1, OP_ANY);
ffee45da
 }
 
 static int
 rtpengine_info1_f(struct sip_msg *msg, char *str1, char *str2)
 {
639c29f2
 	return rtpengine_rtpp_set_wrap_fparam(msg, rtpengine_simple_wrap, str1, 1, OP_OFFER);
ffee45da
 }
 
639c29f2
 static int rtpengine_offer_wrap(struct sip_msg *msg, void *d, int more, enum rtpe_operation op) {
f049d553
 	return rtpengine_offer_answer(msg, d, OP_OFFER, more);
ddea262f
 }
 
 static int
7526431e
 rtpengine_offer1_f(struct sip_msg *msg, char *str1, char *str2)
ddea262f
 {
639c29f2
 	return rtpengine_rtpp_set_wrap_fparam(msg, rtpengine_offer_wrap, str1, 1, OP_OFFER);
f049d553
 }
 
639c29f2
 static int rtpengine_answer_wrap(struct sip_msg *msg, void *d, int more, enum rtpe_operation op) {
f049d553
 	return rtpengine_offer_answer(msg, d, OP_ANSWER, more);
ddea262f
 }
 
 static int
7526431e
 rtpengine_answer1_f(struct sip_msg *msg, char *str1, char *str2)
ddea262f
 {
 
 	if (msg->first_line.type == SIP_REQUEST)
de77beff
 		if (!(msg->first_line.u.request.method_value
 					& (METHOD_ACK | METHOD_PRACK)))
ddea262f
 			return -1;
 
639c29f2
 	return rtpengine_rtpp_set_wrap_fparam(msg, rtpengine_answer_wrap, str1, 2, OP_ANSWER);
ddea262f
 }
 
 static int
2baa05ad
 rtpengine_offer_answer(struct sip_msg *msg, const char *flags, enum rtpe_operation op, int more)
ddea262f
 {
 	bencode_buffer_t bencbuf;
 	bencode_item_t *dict;
 	str body, newbody;
 	struct lump *anchor;
e6252246
 	pv_value_t pv_val;
36cb7534
 	str cur_body = {0, 0};
ddea262f
 
7526431e
 	dict = rtpp_function_call_ok(&bencbuf, msg, op, flags, &body);
ddea262f
 	if (!dict)
 		return -1;
 
 	if (!bencode_dictionary_get_str_dup(dict, "sdp", &newbody)) {
 		LM_ERR("failed to extract sdp body from proxy reply\n");
 		goto error;
 	}
 
f049d553
 	if (body_intermediate.s)
 		pkg_free(body_intermediate.s);
 
 	if (more)
 		body_intermediate = newbody;
 	else {
e6252246
 		if (write_sdp_pvar!= NULL) {
 			pv_val.rs = newbody;
 			pv_val.flags = PV_VAL_STR;
 
 			if (write_sdp_pvar->setf(msg,&write_sdp_pvar->pvp, (int)EQ_T, &pv_val) < 0)
 			{
 				LM_ERR("error setting pvar <%.*s>\n", write_sdp_pvar_str.len, write_sdp_pvar_str.s);
 				goto error_free;
 			}
 
 			pkg_free(newbody.s);
 
 		} else {
75c4ca0b
 			if (read_sdp_pvar_str.len > 0) {
 				/* get the body from the message as body ptr may have changed
 				 * when using read_sdp_pv */
 				cur_body.len = 0;
 				cur_body.s = get_body(msg);
 				cur_body.len = msg->buf + msg->len - cur_body.s;
 
 				anchor = del_lump(msg, cur_body.s - msg->buf, cur_body.len, 0);
 			} else {
 				anchor = del_lump(msg, body.s - msg->buf, body.len, 0);
 			}
e6252246
 			if (!anchor) {
 				LM_ERR("del_lump failed\n");
 				goto error_free;
 			}
 			if (!insert_new_lump_after(anchor, newbody.s, newbody.len, 0)) {
 				LM_ERR("insert_new_lump_after failed\n");
 				goto error_free;
 			}
f049d553
 		}
ddea262f
 	}
 
 	bencode_buffer_free(&bencbuf);
 	return 1;
 
 error_free:
 	pkg_free(newbody.s);
 error:
 	bencode_buffer_free(&bencbuf);
 	return -1;
 }
 
 
639c29f2
 static int
 rtpengine_generic_f(struct sip_msg* msg, char *str1, enum rtpe_operation op)
 {
 	return rtpengine_rtpp_set_wrap_fparam(msg, rtpengine_simple_wrap, str1, 1, op);
ce5362dc
 }
 
ddea262f
 static int
45b4d35f
 start_recording_f(struct sip_msg* msg, char *str1, char *str2)
ddea262f
 {
639c29f2
 	return rtpengine_generic_f(msg, str1, OP_START_RECORDING);
ddea262f
 }
 
ce5362dc
 static int
45b4d35f
 stop_recording_f(struct sip_msg* msg, char *str1, char *str2)
ce5362dc
 {
639c29f2
 	return rtpengine_generic_f(msg, str1, OP_STOP_RECORDING);
e528fce4
 }
 
 static int
 block_dtmf_f(struct sip_msg* msg, char *str1, char *str2)
 {
639c29f2
 	return rtpengine_generic_f(msg, str1, OP_BLOCK_DTMF);
e528fce4
 }
 
 static int
 unblock_dtmf_f(struct sip_msg* msg, char *str1, char *str2)
 {
639c29f2
 	return rtpengine_generic_f(msg, str1, OP_UNBLOCK_DTMF);
76b1f3bf
 }
 
 static int
 block_media_f(struct sip_msg* msg, char *str1, char *str2)
 {
639c29f2
 	return rtpengine_generic_f(msg, str1, OP_BLOCK_MEDIA);
76b1f3bf
 }
 
 static int
 unblock_media_f(struct sip_msg* msg, char *str1, char *str2)
 {
639c29f2
 	return rtpengine_generic_f(msg, str1, OP_UNBLOCK_MEDIA);
 }
 
12470055
 static int
 silence_media_f(struct sip_msg* msg, char *str1, char *str2)
 {
 	return rtpengine_generic_f(msg, str1, OP_SILENCE_MEDIA);
 }
 
 static int
 unsilence_media_f(struct sip_msg* msg, char *str1, char *str2)
 {
 	return rtpengine_generic_f(msg, str1, OP_UNSILENCE_MEDIA);
 }
 
639c29f2
 static int rtpengine_play_media(struct sip_msg *msg, void *d, int more, enum rtpe_operation op) {
 	bencode_buffer_t bencbuf;
 	long long duration;
 	bencode_item_t *ret;
 	char intbuf[32];
 	pv_value_t val;
 	int retval = 1;
 
 	ret = rtpp_function_call_ok(&bencbuf, msg, OP_PLAY_MEDIA, d, NULL);
 	if (!ret)
 		return -1;
 	if (media_duration_pvar) {
 		duration = bencode_dictionary_get_integer(ret, "duration", -1);
 		snprintf(intbuf, sizeof(intbuf), "%lli", duration);
 		memset(&val, 0, sizeof(val));
 		val.flags = PV_VAL_STR;
 		val.rs.s = intbuf;
 		val.rs.len = strlen(intbuf);
 		if (media_duration_pvar->setf(msg, &media_duration_pvar->pvp, (int)EQ_T, &val) < 0)
 		{
 			LM_ERR("error setting pvar <%.*s>\n", media_duration_pvar_str.len, media_duration_pvar_str.s);
 			retval = -1;
76b1f3bf
 		}
 	}
 
639c29f2
 	bencode_buffer_free(&bencbuf);
 	return retval;
e528fce4
 }
 
639c29f2
 static int
 play_media_f(struct sip_msg* msg, char *str1, char *str2)
 {
 	return rtpengine_rtpp_set_wrap_fparam(msg, rtpengine_play_media, str1, 1, OP_PLAY_MEDIA);
558780f7
 }
 
639c29f2
 static int
 stop_media_f(struct sip_msg* msg, char *str1, char *str2)
 {
 	return rtpengine_generic_f(msg, str1, OP_STOP_MEDIA);
558780f7
 }
 
120a71be
 static int
 play_dtmf_f(struct sip_msg* msg, char *str1, char *str2)
 {
 	return rtpengine_generic_f(msg, str1, OP_PLAY_DTMF);
 }
 
558780f7
 static int
 start_forwarding_f(struct sip_msg* msg, char *str1, char *str2)
 {
639c29f2
 	return rtpengine_generic_f(msg, str1, OP_START_FORWARDING);
558780f7
 }
 
 static int
 stop_forwarding_f(struct sip_msg* msg, char *str1, char *str2)
 {
639c29f2
 	return rtpengine_generic_f(msg, str1, OP_STOP_FORWARDING);
558780f7
 }
 
639c29f2
 static int rtpengine_rtpstat_wrap(struct sip_msg *msg, void *d, int more, enum rtpe_operation op) {
e7c27cbb
 	void **parms;
 	pv_param_t *param;
 	pv_value_t *res;
eba7dcbe
 	bencode_buffer_t bencbuf;
e7c27cbb
 	bencode_item_t *dict, *tot, *rtp, *rtcp;
eba7dcbe
 	static char buf[256];
 	str ret;
 
e7c27cbb
 	parms = d;
 	param = parms[0];
 	res = parms[1];
 
7526431e
 	dict = rtpp_function_call_ok(&bencbuf, msg, OP_QUERY, NULL, NULL);
eba7dcbe
 	if (!dict)
 		return -1;
 
 	tot = bencode_dictionary_get_expect(dict, "totals", BENCODE_DICTIONARY);
e7c27cbb
 	rtp = bencode_dictionary_get_expect(tot, "RTP", BENCODE_DICTIONARY);
 	rtcp = bencode_dictionary_get_expect(tot, "RTCP", BENCODE_DICTIONARY);
eba7dcbe
 
e7c27cbb
 	if (!rtp || !rtcp)
eba7dcbe
 		goto error;
 
 	ret.s = buf;
 	ret.len = snprintf(buf, sizeof(buf),
e7c27cbb
 			"RTP: %lli bytes, %lli packets, %lli errors; "
 			"RTCP: %lli bytes, %lli packets, %lli errors",
 			bencode_dictionary_get_integer(rtp, "bytes", -1),
 			bencode_dictionary_get_integer(rtp, "packets", -1),
 			bencode_dictionary_get_integer(rtp, "errors", -1),
 			bencode_dictionary_get_integer(rtcp, "bytes", -1),
 			bencode_dictionary_get_integer(rtcp, "packets", -1),
 			bencode_dictionary_get_integer(rtcp, "errors", -1));
eba7dcbe
 
 	bencode_buffer_free(&bencbuf);
 	return pv_get_strval(msg, param, res, &ret);
 
 error:
 	bencode_buffer_free(&bencbuf);
 	return -1;
ddea262f
 }
 
e7c27cbb
 /*
  * Returns the current RTP-Statistics from the RTP-Proxy
  */
 static int
cfc0ecaa
 pv_get_rtpestat_f(struct sip_msg *msg, pv_param_t *param, pv_value_t *res)
e7c27cbb
 {
 	void *parms[2];
 
 	parms[0] = param;
 	parms[1] = res;
 
639c29f2
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_rtpstat_wrap, parms, 1, OP_ANY);
e7c27cbb
 }
 
697f34d4
 /**
  *
  */
 static srjson_t *rtpengine_query_v_build_json(srjson_doc_t *jdoc,
 		bencode_item_t *dict)
 {
 	srjson_t *vnode;
 	srjson_t *tnode;
 	bencode_item_t *it;
 	str sval;
 
 	if(jdoc==NULL || dict==NULL) {
 		LM_ERR("invalid parameters\n");
 		return NULL;
 	}
 
 	switch (dict->type) {
 		case BENCODE_STRING:
 			return srjson_CreateStr(jdoc, dict->iov[1].iov_base, dict->iov[1].iov_len);
 
 		case BENCODE_INTEGER:
 			return srjson_CreateNumber(jdoc, dict->value);
 
 		case BENCODE_LIST:
 			vnode = srjson_CreateArray(jdoc);
 			if(vnode==NULL) {
 				LM_ERR("failed to create the array node\n");
 				return NULL;
 			}
 			for (it = dict->child; it; it = it->sibling) {
 				tnode = rtpengine_query_v_build_json(jdoc, it);
 				if (!tnode) {
 					srjson_Delete(jdoc, vnode);
 					return NULL;
 				}
 				srjson_AddItemToArray(jdoc, vnode, tnode);
 			}
 			return vnode;
 
 		case BENCODE_DICTIONARY:
 			vnode = srjson_CreateObject(jdoc);
 			if(vnode==NULL) {
 				LM_ERR("failed to create the object node\n");
 				return NULL;
 			}
 			for (it = dict->child; it; it = it->sibling) {
 				/* name of the item */
 				sval.s = it->iov[1].iov_base;
 				sval.len = it->iov[1].iov_len;
 				/* value of the item */
 				it = it->sibling;
 				tnode = rtpengine_query_v_build_json(jdoc, it);
 				if (!tnode) {
 					srjson_Delete(jdoc, vnode);
 					return NULL;
 				}
 				srjson_AddStrItemToObject(jdoc, vnode, sval.s, sval.len, tnode);
 			}
 			return vnode;
 
 		default:
 			LM_ERR("unsupported bencode item type %d\n", dict->type);
 			return NULL;
 	}
 }
 
 /**
  *
  */
 static int rtpengine_query_v_print(bencode_item_t *dict, str *fmt,
 		str *bout)
 {
 	srjson_doc_t jdoc;
 
 	if(fmt==NULL || fmt->s==NULL || fmt->len<=0) {
 		LM_ERR("invalid format parameter\n");
 		return -1;
 	}
 	if(fmt->s[0]!='j' && fmt->s[0]!='J') {
 		LM_ERR("invalid format parameter value: %.*s\n", fmt->len, fmt->s);
 		return -1;
 	}
 
 	srjson_InitDoc(&jdoc, NULL);
 	jdoc.root = rtpengine_query_v_build_json(&jdoc, dict);
 
 	if(jdoc.root==NULL) {
 		LM_ERR("failed to build json document\n");
 		return -1;
 	}
 
 	if(fmt->len>1 && (fmt->s[1]=='p' || fmt->s[1]=='P')) {
 		bout->s = srjson_Print(&jdoc, jdoc.root);
 	} else {
 		bout->s = srjson_PrintUnformatted(&jdoc, jdoc.root);
 	}
 	if(bout->s==NULL) {
 		LM_ERR("unable to serialize json document\n");
 		srjson_DestroyDoc(&jdoc);
 		return -1;
 	}
 	bout->len = strlen(bout->s);
 	srjson_DestroyDoc(&jdoc);
 
 	return 0;
 }
 
 /**
  *
  */
 static int rtpengine_query_v_wrap(struct sip_msg *msg, void *d, int more,
 		enum rtpe_operation op)
 {
 	void **parms;
 	str *fmt = NULL;
 	pv_spec_t *dst = NULL;
 	pv_value_t val = {0};
 	bencode_buffer_t bencbuf;
 	bencode_item_t *dict;
 
 	parms = d;
 	fmt = parms[0];
 	dst = parms[1];
 
 	dict = rtpp_function_call_ok(&bencbuf, msg, OP_QUERY, NULL, NULL);
 	if (!dict) {
 		return -1;
 	}
 	if(rtpengine_query_v_print(dict, fmt, &val.rs)<0) {
 		goto error;
 	}
 
 	val.flags = PV_VAL_STR;
 	if(dst->setf) {
 		dst->setf(msg, &dst->pvp, (int)EQ_T, &val);
 	} else {
 		LM_WARN("target pv is not writable\n");
 	}
 
 	/* val.rs.s is allocated by srjson print */
 	free(val.rs.s);
 
 	bencode_buffer_free(&bencbuf);
 	return 1;
 
 error:
 	bencode_buffer_free(&bencbuf);
 	return -1;
 }
 
 /**
  *
  */
 static int ki_rtpengine_query_v(sip_msg_t *msg, str *fmt, str *dpv)
 {
 	void *parms[2];
 	pv_spec_t *dst;
 
 	dst = pv_cache_get(dpv);
 	if(dst==NULL) {
 		LM_ERR("failed to get pv spec for: %.*s\n", dpv->len, dpv->s);
 		return -1;
 	}
 	if(dst->setf==NULL) {
 		LM_ERR("target pv is not writable: %.*s\n", dpv->len, dpv->s);
 		return -1;
 	}
 	parms[0] = fmt;
 	parms[1] = dst;
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_query_v_wrap, parms, 1, OP_ANY);
 }
 
 /*
  * Store the cmd QUERY result to variable
  */
 static int w_rtpengine_query_v(sip_msg_t *msg, char *pfmt, char *pvar)
 {
 	void *parms[2];
 	str fmt = {NULL, 0};
 	pv_spec_t *dst;
 
 	if(fixup_get_svalue(msg, (gparam_t*)pfmt, &fmt) < 0 || fmt.len <= 0) {
 		LM_ERR("fmt has no value\n");
 		return -1;
 	}
 	dst = (pv_spec_t *)pvar;
 
 	parms[0] = &fmt;
 	parms[1] = dst;
 
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_query_v_wrap, parms, 1, OP_ANY);
 }
 
 /**
  *
  */
 static int fixup_rtpengine_query_v(void **param, int param_no)
 {
 	if(param_no == 1) {
 		return fixup_spve_null(param, 1);
 	}
 
 	if(param_no == 2) {
 		if(fixup_pvar_null(param, 1) != 0) {
 			LM_ERR("failed to fixup result pvar\n");
 			return -1;
 		}
 		if(((pv_spec_t *)(*param))->setf == NULL) {
 			LM_ERR("result pvar is not writeble\n");
 			return -1;
 		}
 		return 0;
 	}
 
 	LM_ERR("invalid parameter number <%d>\n", param_no);
 	return -1;
 }
 
 /**
  *
  */
 static int fixup_free_rtpengine_query_v(void **param, int param_no)
 {
 	if(param_no == 1) {
 		return fixup_free_spve_null(param, 1);
 	}
 
 	if(param_no == 2) {
 		return fixup_free_pvar_null(param, 1);
 	}
 
 	LM_ERR("invalid parameter number <%d>\n", param_no);
 	return -1;
 }
 
7ad4dadc
 static int
 set_rtp_inst_pvar(struct sip_msg *msg, const str * const uri) {
83caaabd
 	pv_value_t val;
 
 	if (rtp_inst_pvar == NULL)
 		return 0;
 
 	memset(&val, 0, sizeof(pv_value_t));
 	val.flags = PV_VAL_STR;
 	val.rs = *uri;
 
98877b5b
 	if (rtp_inst_pvar->setf(msg, &rtp_inst_pvar->pvp, (int)EQ_T, &val) < 0) {
83caaabd
 		LM_ERR("Failed to add RTP Engine URI to pvar\n");
 		return -1;
 	}
 	return 0;
 }
152f46dd
 
 /**
  *
  */
 static int ki_rtpengine_manage0(sip_msg_t *msg) {
639c29f2
     return rtpengine_rtpp_set_wrap(msg, rtpengine_manage_wrap, NULL, 1, OP_ANY);
152f46dd
 }
 
 /**
  *
  */
3ad3438a
 static int ki_rtpengine_manage(sip_msg_t *msg, str *flags) {
639c29f2
     return rtpengine_rtpp_set_wrap(msg, rtpengine_manage_wrap, ((flags && flags->len > 0) ? flags->s : NULL), 1,
 		    OP_ANY);
152f46dd
 }
 
39d23767
 static int ki_rtpengine_offer0(sip_msg_t *msg)
 {
639c29f2
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_offer_wrap, 0, 1, OP_ANY);
39d23767
 }
 
 static int ki_rtpengine_offer(sip_msg_t *msg, str *flags)
 {
639c29f2
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_offer_wrap, flags->s, 1, OP_ANY);
39d23767
 }
 
 static int ki_rtpengine_answer0(sip_msg_t *msg)
 {
639c29f2
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_answer_wrap, NULL, 2, OP_ANY);
39d23767
 }
 
 static int ki_rtpengine_answer(sip_msg_t *msg, str *flags)
 {
639c29f2
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_answer_wrap, flags->s, 2, OP_ANY);
39d23767
 }
 
 static int ki_rtpengine_delete0(sip_msg_t *msg)
 {
639c29f2
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_delete_wrap, NULL, 1, OP_ANY);
39d23767
 }
 
 static int ki_rtpengine_delete(sip_msg_t *msg, str *flags)
 {
639c29f2
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_delete_wrap, flags->s, 1, OP_ANY);
39d23767
 }
 
026475aa
 static int ki_rtpengine_query0(sip_msg_t *msg)
a508f1a3
 {
639c29f2
         return rtpengine_rtpp_set_wrap(msg, rtpengine_query_wrap, NULL, 1, OP_ANY);
026475aa
 }
 
 static int ki_rtpengine_query(sip_msg_t *msg, str *flags)
a508f1a3
 {
639c29f2
         return rtpengine_rtpp_set_wrap(msg, rtpengine_query_wrap, flags->s, 1, OP_ANY);
026475aa
 }
 
39d23767
 static int ki_start_recording(sip_msg_t *msg)
 {
639c29f2
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_simple_wrap, NULL, 1, OP_START_RECORDING);
39d23767
 }
 
ce5362dc
 static int ki_stop_recording(sip_msg_t *msg)
 {
639c29f2
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_simple_wrap, NULL, 1, OP_STOP_RECORDING);
ce5362dc
 }
 
1bc33f1a
 
 static int ki_block_media0(sip_msg_t *msg)
 {
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_simple_wrap, NULL, 1, OP_BLOCK_MEDIA);
 }
 static int ki_block_media(sip_msg_t *msg, str *flags)
 {
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_simple_wrap, flags->s, 1, OP_BLOCK_MEDIA);
 }
 static int ki_unblock_media0(sip_msg_t *msg)
 {
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_simple_wrap, NULL, 1, OP_UNBLOCK_MEDIA);
 }
 static int ki_unblock_media(sip_msg_t *msg , str *flags)
 {
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_simple_wrap, flags->s, 1, OP_UNBLOCK_MEDIA);
 }
 
12470055
 static int ki_silence_media0(sip_msg_t *msg)
 {
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_simple_wrap, NULL, 1, OP_SILENCE_MEDIA);
 }
 static int ki_silence_media(sip_msg_t *msg, str *flags)
 {
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_simple_wrap, flags->s, 1, OP_SILENCE_MEDIA);
 }
 static int ki_unsilence_media0(sip_msg_t *msg)
 {
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_simple_wrap, NULL, 1, OP_UNSILENCE_MEDIA);
 }
 static int ki_unsilence_media(sip_msg_t *msg , str *flags)
 {
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_simple_wrap, flags->s, 1, OP_UNSILENCE_MEDIA);
 }
 
1bc33f1a
 static int ki_block_dtmf0(sip_msg_t *msg)
 {
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_simple_wrap, NULL, 1, OP_BLOCK_DTMF);
 }
 static int ki_block_dtmf(sip_msg_t *msg, str *flags)
 {
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_simple_wrap, flags->s, 1, OP_BLOCK_DTMF);
 }
 static int ki_unblock_dtmf0(sip_msg_t *msg)
 {
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_simple_wrap, NULL, 1, OP_UNBLOCK_DTMF);
 }
 static int ki_unblock_dtmf(sip_msg_t *msg, str *flags)
 {
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_simple_wrap, flags->s, 1, OP_UNBLOCK_DTMF);
 }
 
 static int ki_play_media(sip_msg_t *msg, str *flags)
 {
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_simple_wrap, flags->s, 1, OP_PLAY_MEDIA);
 }
 static int ki_stop_media0(sip_msg_t *msg)
 {
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_simple_wrap, NULL, 1, OP_STOP_MEDIA);
 }
 static int ki_stop_media(sip_msg_t *msg, str *flags)
 {
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_simple_wrap, flags->s, 1, OP_STOP_MEDIA);
 }
 
 
39d23767
 static int ki_set_rtpengine_set(sip_msg_t *msg, int r1)
 {
 	rtpp_set_link_t rtpl1;
 	rtpp_set_link_t rtpl2;
 	int ret;
 
 	memset(&rtpl1, 0, sizeof(rtpp_set_link_t));
 	memset(&rtpl2, 0, sizeof(rtpp_set_link_t));
 
 	if((rtpl1.rset = select_rtpp_set((unsigned int)r1)) ==0){
 		LM_ERR("rtpp_proxy set %d not configured\n", r1);
 		return -1;
 	}
 
 	current_msg_id = 0;
 	active_rtpp_set = 0;
 	selected_rtpp_set_1 = 0;
 	selected_rtpp_set_2 = 0;
 
 	ret = set_rtpengine_set_n(msg, &rtpl1, &selected_rtpp_set_1);
 	if (ret < 0)
 		return ret;
 
 	return 1;
 }
 
 static int ki_set_rtpengine_set2(sip_msg_t *msg, int r1, int r2)
 {
 	rtpp_set_link_t rtpl1;
 	rtpp_set_link_t rtpl2;
 	int ret;
 
 	memset(&rtpl1, 0, sizeof(rtpp_set_link_t));
 	memset(&rtpl2, 0, sizeof(rtpp_set_link_t));
 
 	if((rtpl1.rset = select_rtpp_set((unsigned int)r1)) ==0){
 		LM_ERR("rtpp_proxy set %d not configured\n", r1);
 		return -1;
 	}
 	if((rtpl2.rset = select_rtpp_set((unsigned int)r2)) ==0){
 		LM_ERR("rtpp_proxy set %d not configured\n", r2);
 		return -1;
 	}
 
 	current_msg_id = 0;
 	active_rtpp_set = 0;
 	selected_rtpp_set_1 = 0;
 	selected_rtpp_set_2 = 0;
 
 	ret = set_rtpengine_set_n(msg, &rtpl1, &selected_rtpp_set_1);
 	if (ret < 0)
 		return ret;
 
 	ret = set_rtpengine_set_n(msg, &rtpl2, &selected_rtpp_set_2);
 	if (ret < 0)
 		return ret;
 
 	return 1;
 }
 
152f46dd
 /**
  *
  */
401240be
 /* clang-format off */
152f46dd
 static sr_kemi_t sr_kemi_rtpengine_exports[] = {
     { str_init("rtpengine"), str_init("rtpengine_manage0"),
         SR_KEMIP_INT, ki_rtpengine_manage0,
         { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
3ad3438a
     { str_init("rtpengine"), str_init("rtpengine_manage"),
         SR_KEMIP_INT, ki_rtpengine_manage,
152f46dd
         { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
39d23767
     { str_init("rtpengine"), str_init("rtpengine_offer0"),
         SR_KEMIP_INT, ki_rtpengine_offer0,
         { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
     { str_init("rtpengine"), str_init("rtpengine_offer"),
         SR_KEMIP_INT, ki_rtpengine_offer,
         { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
     { str_init("rtpengine"), str_init("rtpengine_answer0"),
         SR_KEMIP_INT, ki_rtpengine_answer0,
         { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
     { str_init("rtpengine"), str_init("rtpengine_answer"),
         SR_KEMIP_INT, ki_rtpengine_answer,
         { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
     { str_init("rtpengine"), str_init("rtpengine_delete0"),
         SR_KEMIP_INT, ki_rtpengine_delete0,
         { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
     { str_init("rtpengine"), str_init("rtpengine_delete"),
         SR_KEMIP_INT, ki_rtpengine_delete,
         { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
     { str_init("rtpengine"), str_init("start_recording"),
         SR_KEMIP_INT, ki_start_recording,
         { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
ce5362dc
     { str_init("rtpengine"), str_init("stop_recording"),
         SR_KEMIP_INT, ki_stop_recording,
         { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
1bc33f1a
 
 	{ str_init("rtpengine"), str_init("block_media0"),
         SR_KEMIP_INT, ki_block_media0,
         { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
     { str_init("rtpengine"), str_init("block_media"),
         SR_KEMIP_INT, ki_block_media,
         { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
 	{ str_init("rtpengine"), str_init("unblock_media0"),
         SR_KEMIP_INT, ki_unblock_media0,
         { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
     { str_init("rtpengine"), str_init("unblock_media"),
         SR_KEMIP_INT, ki_unblock_media,
         { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
 
12470055
 	{ str_init("rtpengine"), str_init("silence_media0"),
         SR_KEMIP_INT, ki_silence_media0,
         { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
     { str_init("rtpengine"), str_init("silence_media"),
         SR_KEMIP_INT, ki_silence_media,
         { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
 	{ str_init("rtpengine"), str_init("unsilence_media0"),
         SR_KEMIP_INT, ki_unsilence_media0,
         { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
     { str_init("rtpengine"), str_init("unsilence_media"),
         SR_KEMIP_INT, ki_unsilence_media,
         { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
 
1bc33f1a
 	{ str_init("rtpengine"), str_init("block_dtmf0"),
         SR_KEMIP_INT, ki_block_dtmf0,
         { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
     { str_init("rtpengine"), str_init("block_dtmf"),
         SR_KEMIP_INT, ki_block_dtmf,
         { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
 	{ str_init("rtpengine"), str_init("unblock_dtmf0"),
         SR_KEMIP_INT, ki_unblock_dtmf0,
         { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
     { str_init("rtpengine"), str_init("unblock_dtmf"),
         SR_KEMIP_INT, ki_unblock_dtmf,
         { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
 
     { str_init("rtpengine"), str_init("play_media"),
         SR_KEMIP_INT, ki_play_media,
         { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
 		{ str_init("rtpengine"), str_init("stop_media0"),
         SR_KEMIP_INT, ki_stop_media0,
         { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
     { str_init("rtpengine"), str_init("stop_media"),
         SR_KEMIP_INT, ki_stop_media,
         { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
 
39d23767
     { str_init("rtpengine"), str_init("set_rtpengine_set"),
         SR_KEMIP_INT, ki_set_rtpengine_set,
         { SR_KEMIP_INT, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
     { str_init("rtpengine"), str_init("set_rtpengine_set2"),
         SR_KEMIP_INT, ki_set_rtpengine_set2,
ede35768
         { SR_KEMIP_INT, SR_KEMIP_INT, SR_KEMIP_NONE,
39d23767
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
026475aa
     { str_init("rtpengine"), str_init("rtpengine_query0"),
         SR_KEMIP_INT, ki_rtpengine_query0,
         { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
     { str_init("rtpengine"), str_init("rtpengine_query"),
         SR_KEMIP_INT, ki_rtpengine_query,
         { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
697f34d4
    { str_init("rtpengine"), str_init("rtpengine_query_v"),
         SR_KEMIP_INT, ki_rtpengine_query_v,
         { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
             SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
     },
152f46dd
 
     { {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } }
 };
401240be
 /* clang-format on */
152f46dd
 
 int mod_register(char *path, int *dlflags, void *p1, void *p2) {
     sr_kemi_modules_add(sr_kemi_rtpengine_exports);
     return 0;
e69579bc
 }