1 /**
2  * Copyright 2016 (C) Federico Cabiddu <federico.cabiddu@gmail.com>
3  * Copyright 2016 (C) Giacomo Vacca <giacomo.vacca@gmail.com>
4  * Copyright 2016 (C) Orange - Camille Oudot <camille.oudot@orange.com>
5  *
6  * This file is part of Kamailio, a free SIP server.
7  *
8  * This file is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version
12  *
13  *
14  * This file is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  */
24 
25 /*! \file
26  * \brief  Kamailio http_async_client :: The module interface file
27  * \ingroup http_async_client
28  */
29 
30 /*! \defgroup http_async_client Kamailio :: Async module interface to Curl/HTTP
31  *
32  * http://curl.haxx.se
33  * A generic library for many protocols
34  *
35  */
36 
37 #include <stdio.h>
38 #include <unistd.h>
39 #include <stdlib.h>
40 #include <string.h>
41 
42 #include "../../core/globals.h"
43 #include "../../core/sr_module.h"
44 #include "../../core/dprint.h"
45 #include "../../core/ut.h"
46 #include "../../core/pt.h"
47 #include "../../core/pvar.h"
48 #include "../../core/mem/shm_mem.h"
49 #include "../../core/mod_fix.h"
50 #include "../../core/pvar.h"
51 #include "../../core/cfg/cfg_struct.h"
52 #include "../../core/fmsg.h"
53 #include "../../core/kemi.h"
54 
55 #include "../../modules/tm/tm_load.h"
56 #include "../../modules/pv/pv_api.h"
57 
58 
59 #include "async_http.h"
60 
61 MODULE_VERSION
62 
63 extern int  num_workers;
64 
65 extern unsigned int q_idx;
66 extern char q_id[MAX_ID_LEN+1];
67 
68 int http_timeout = 500; /* query timeout in ms */
69 int tcp_keepalive = 0; /* TCP keepalives (default disabled) */
70 int tcp_ka_idle = 0; /* TCP keep-alive idle time wait */
71 int tcp_ka_interval = 0; /* TCP keep-alive interval */
72 int hash_size = 2048;
73 int tls_version = 0; // Use default SSL version in HTTPS requests (see curl/curl.h)
74 int tls_verify_host = 1; // By default verify host in HTTPS requests
75 int tls_verify_peer = 1; // By default verify peer in HTTPS requests
76 int curl_verbose = 0;
77 char* tls_client_cert = NULL; // client SSL certificate path, defaults to NULL
78 char* tls_client_key = NULL; // client SSL certificate key path, defaults to NULL
79 char* tls_ca_path = NULL; // certificate authority dir path, defaults to NULL
80 static char *memory_manager = "shm";
81 extern int curl_memory_manager;
82 unsigned int default_authmethod = CURLAUTH_BASIC | CURLAUTH_DIGEST;
83 
84 static int  mod_init(void);
85 static int  child_init(int);
86 static void mod_destroy(void);
87 
88 static int w_http_async_query(sip_msg_t* msg, char* query, char* rt);
89 static int set_query_param(str* param, str input);
90 
91 /* pv api binding */
92 static int ah_get_reason(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
93 static int ah_get_hdr(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
94 static int w_pv_parse_hdr_name(pv_spec_p sp, str *in);
95 static int ah_get_status(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
96 static int ah_get_msg_body(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
97 static int ah_get_body_size(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
98 static int ah_get_msg_buf(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
99 static int ah_get_msg_len(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
100 static int ah_parse_req_name(pv_spec_p sp, str *in);
101 static int ah_set_req(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val);
102 static int ah_get_id(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
103 
104 
105 static str pv_str_1 = {"1", 1};
106 static str pv_str_0 = {"0", 1};
107 
108 static int ah_get_ok(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
109 static int ah_get_err(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
110 
111 /* tm */
112 struct tm_binds tmb;
113 /* pv */
114 pv_api_t pv_api;
115 
116 stat_var *requests;
117 stat_var *replies;
118 stat_var *errors;
119 stat_var *timeouts;
120 
121 enum http_req_name_t {
122 	E_HRN_ALL = 0,
123 	E_HRN_HDR, E_HRN_METHOD, E_HRN_TIMEOUT,
124 	E_HRN_TLS_CA_PATH, E_HRN_TLS_CLIENT_KEY,
125 	E_HRN_TLS_CLIENT_CERT, E_HRN_SUSPEND,
126 	E_HRN_BODY, E_HRN_AUTHMETHOD, E_HRN_USERNAME,
127 	E_HRN_PASSWORD, E_HRN_TCP_KA, E_HRN_TCP_KA_IDLE,
128 	E_HRN_TCP_KA_INTERVAL
129 };
130 
131 static cmd_export_t cmds[]={
132 	{"http_async_query",  (cmd_function)w_http_async_query, 2, fixup_spve_spve,
133 		0, ANY_ROUTE},
134 	{0, 0, 0, 0, 0, 0}
135 };
136 
137 static param_export_t params[]={
138 	{"workers",				INT_PARAM,		&num_workers},
139 	{"connection_timeout",	INT_PARAM,		&http_timeout},
140 	{"hash_size",			INT_PARAM,		&hash_size},
141 	{"tls_version",			INT_PARAM,		&tls_version},
142 	{"tls_verify_host",		INT_PARAM,		&tls_verify_host},
143 	{"tls_verify_peer",		INT_PARAM,		&tls_verify_peer},
144 	{"curl_verbose",		INT_PARAM,		&curl_verbose},
145 	{"tls_client_cert",		PARAM_STRING,	&tls_client_cert},
146 	{"tls_client_key",		PARAM_STRING,	&tls_client_key},
147 	{"tls_ca_path",			PARAM_STRING,	&tls_ca_path},
148 	{"memory_manager",		PARAM_STRING,	&memory_manager},
149 	{"authmethod",			PARAM_INT,		&default_authmethod },
150 	{"tcp_keepalive",	    INT_PARAM,		&tcp_keepalive},
151 	{"tcp_ka_idle",	        INT_PARAM,		&tcp_ka_idle},
152 	{"tcp_ka_interval",	    INT_PARAM,		&tcp_ka_interval},
153 	{0, 0, 0}
154 };
155 
156 /*! \brief We expose internal variables via the statistic framework below.*/
157 stat_export_t mod_stats[] = {
158         {"requests",    STAT_NO_RESET, &requests        },
159         {"replies", 	STAT_NO_RESET, &replies 	},
160         {"errors",      STAT_NO_RESET, &errors       	},
161         {"timeouts",    STAT_NO_RESET, &timeouts	},
162         {0, 0, 0}
163 };
164 
165 static pv_export_t pvs[] = {
166 	{STR_STATIC_INIT("http_hdr"),
167 		PVT_HDR, ah_get_hdr, 0,
168 		w_pv_parse_hdr_name, pv_parse_index, 0, 0},
169 	{STR_STATIC_INIT("http_rr"),
170 		PVT_OTHER, ah_get_reason, 0,
171 		0, 0, 0, 0},
172 	{STR_STATIC_INIT("http_rs"),
173 		PVT_OTHER, ah_get_status, 0,
174 		0, 0, 0, 0},
175 	{STR_STATIC_INIT("http_rb"),
176 		PVT_MSG_BODY, ah_get_msg_body, 0,
177 		0, 0, 0, 0},
178 	{STR_STATIC_INIT("http_bs"),
179 		PVT_OTHER, ah_get_body_size, 0,
180 		0, 0, 0, 0},
181 	{STR_STATIC_INIT("http_mb"),
182 		PVT_OTHER, ah_get_msg_buf, 0,
183 		0, 0, 0, 0},
184 	{STR_STATIC_INIT("http_ml"),
185 		PVT_OTHER, ah_get_msg_len, 0,
186 		0, 0, 0, 0},
187 	{STR_STATIC_INIT("http_ok"),
188 		PVT_OTHER, ah_get_ok, 0,
189 		0, 0, 0, 0},
190 	{STR_STATIC_INIT("http_err"),
191 		PVT_OTHER, ah_get_err, 0,
192 		0, 0, 0, 0},
193 	{STR_STATIC_INIT("http_req"),
194 		PVT_OTHER, pv_get_null, ah_set_req,
195 		ah_parse_req_name, 0, 0, 0},
196 	{STR_STATIC_INIT("http_req_id"),
197 		PVT_OTHER, ah_get_id, 0,
198 		0, 0, 0, 0},
199 	{ {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
200 };
201 
202 struct module_exports exports = {
203 	"http_async_client",	/* module name */
204 	DEFAULT_DLFLAGS,		/* dlopen flags */
205 	cmds,					/* exported functions */
206 	params,					/* exported parameters */
207 	0,						/* RPC method exports */
208 	pvs,					/* exported pseudo-variables */
209 	0,						/* response handling function */
210 	mod_init,				/* module initialization function */
211 	child_init,				/* per-child init function */
212 	mod_destroy				/* module destroy function */
213 };
214 
215 
216 /**
217  * init module function
218  */
mod_init(void)219 static int mod_init(void)
220 {
221 	unsigned int n;
222 	LM_INFO("Initializing Http Async module\n");
223 
224 #ifdef STATISTICS
225 	/* register statistics */
226 	if (register_module_stats( exports.name, mod_stats)!=0 ) {
227 		LM_ERR("failed to register core statistics\n");
228 		return -1;
229 	}
230 #endif
231 	/* sanitize hash_size */
232 	if (hash_size < 1){
233 		LM_WARN("hash_size is smaller "
234 				"than 1  -> rounding from %d to 1\n",
235 				hash_size);
236 				hash_size = 1;
237 	}
238 	/* check that the hash table size is a power of 2 */
239 	for( n=0 ; n<(8*sizeof(n)) ; n++) {
240 		if (hash_size==(1<<n))
241 			break;
242 		if (n && hash_size<(1<<n)) {
243 			LM_WARN("hash_size is not a power "
244 				"of 2 as it should be -> rounding from %d to %d (n=%d)\n",
245 				hash_size, 1<<(n-1), n);
246 			hash_size = 1<<(n-1);
247 			break;
248 		}
249 	}
250 	/* check 'workers' param */
251 	if (num_workers < 1) {
252 		LM_ERR("the 'workers' parameter must be >= 1\n");
253 		return -1;
254 	}
255 
256 	tls_verify_host = tls_verify_host?1:0;
257 	tls_verify_peer = tls_verify_peer?1:0;
258 
259 	if (tcp_keepalive) {
260 		LM_INFO("TCP keepalives enabled\n");
261 	}
262 	/* check tcp keepalive parameters */
263 	if ((tcp_ka_idle > 0 || tcp_ka_interval > 0) && !(tcp_keepalive > 0)) {
264 		LM_WARN("either 'tcp_ka_idle' or 'tcp_ka_interval' are set but 'tcp_keepalive' is disabled: they will be ignored\n");
265 	}
266 	/* init http parameters list */
267 	init_query_params(&ah_params);
268 
269 	if (strncmp("shm", memory_manager, 3) == 0) {
270 		curl_memory_manager = 0;
271 	} else if (strncmp("sys", memory_manager, 3) == 0) {
272 		curl_memory_manager = 1;
273 	} else {
274 		LM_ERR("invalid memory_manager parameter: '%s'\n", memory_manager);
275 		return -1;
276 	}
277 
278 	set_curl_mem_callbacks();
279 
280 	/* init faked sip msg */
281 	if(faked_msg_init()<0) {
282 		LM_ERR("failed to init faked sip msg\n");
283 		return -1;
284 	}
285 
286 	if(load_tm_api( &tmb ) < 0) {
287 		LM_INFO("cannot load the TM-functions - async relay disabled\n");
288 		memset(&tmb, 0, sizeof(tm_api_t));
289 	}
290 
291 	/* allocate workers array */
292 	workers = shm_malloc(num_workers * sizeof(*workers));
293 	if(workers == NULL) {
294 		LM_ERR("error in shm_malloc\n");
295 		return -1;
296 	}
297 	memset(workers, 0, num_workers * sizeof(*workers));
298 
299 	register_procs(num_workers);
300 
301 	/* add child to update local config framework structures */
302 	cfg_register_child(num_workers);
303 
304 	return 0;
305 }
306 
307 /**
308  * @brief Initialize async module children
309  */
child_init(int rank)310 static int child_init(int rank)
311 {
312 	int pid;
313 	int i;
314 
315 	LM_DBG("child initializing async http\n");
316 
317 	if(num_workers<=0)
318 		return 0;
319 
320 	/* initialize query counter and id */
321 	q_idx = 0;
322 	q_id[0] = '\0';
323 
324 	if (rank==PROC_INIT) {
325 		for(i=0; i<num_workers; i++) {
326 			LM_DBG("initializing worker notification socket: %d\n", i);
327 			if(async_http_init_sockets(&workers[i])<0) {
328 				LM_ERR("failed to initialize tasks sockets\n");
329 				return -1;
330 			}
331 		}
332 
333 		return 0;
334 	}
335 
336 	if(rank>0) {
337 		for(i=0; i<num_workers; i++) {
338 			close(workers[i].notication_socket[0]);
339 		}
340 		return 0;
341 	}
342 	if (rank!=PROC_MAIN)
343 		return 0;
344 
345 	for(i=0; i<num_workers; i++) {
346 		if(async_http_init_worker(i+1, &workers[i])<0) {
347 			LM_ERR("failed to initialize worker process: %d\n", i);
348 			return -1;
349 		}
350 		pid=fork_process(PROC_RPC, "Http Async Worker", 1);
351 		if (pid<0)
352 			return -1; /* error */
353 		if(pid==0) {
354 			/* child */
355 			/* enforce http_reply_parse=yes */
356 			http_reply_parse = 1;
357 			/* initialize the config framework */
358 			if (cfg_child_init())
359 				return -1;
360 			/* init msg structure for http reply parsing */
361 			ah_reply = pkg_malloc(sizeof(struct sip_msg));
362 			if(!ah_reply) {
363 				LM_ERR("failed to allocate pkg memory\n");
364 				return -1;
365 			}
366 			memset(ah_reply, 0, sizeof(struct sip_msg));
367 			/* main function for workers */
368 			async_http_run_worker(&workers[i]);
369 		}
370 	}
371 
372 	return 0;
373 }
374 
375 /**
376  * destroy module function
377  */
mod_destroy(void)378 static void mod_destroy(void)
379 {
380 }
381 
382 /**
383  *
384  */
w_http_async_query(sip_msg_t * msg,char * query,char * rt)385 static int w_http_async_query(sip_msg_t *msg, char *query, char* rt)
386 {
387 	str sdata;
388 	str rn;
389 
390 	if(msg==NULL)
391 		return -1;
392 
393 	if(fixup_get_svalue(msg, (gparam_t*)query, &sdata)!=0) {
394 		LM_ERR("unable to get data\n");
395 		return -1;
396 	}
397 	if(sdata.s==NULL || sdata.len == 0) {
398 		LM_ERR("invalid data parameter\n");
399 		return -1;
400 	}
401 
402 	if(fixup_get_svalue(msg, (gparam_t*)rt, &rn)!=0)
403 	{
404 		LM_ERR("no route block name\n");
405 		return -1;
406 	}
407 	if(rn.s==NULL || rn.len == 0) {
408 		LM_ERR("invalid route name parameter\n");
409 		return -1;
410 	}
411 	return async_send_query(msg, &sdata, &rn);
412 }
413 
414 /**
415  *
416  */
ki_http_async_query(sip_msg_t * msg,str * sdata,str * rn)417 static int ki_http_async_query(sip_msg_t *msg, str *sdata, str *rn)
418 {
419 	if(msg==NULL)
420 		return -1;
421 	if(sdata==NULL || sdata->len <= 0) {
422 		LM_ERR("invalid data parameter\n");
423 		return -1;
424 	}
425 	if(rn->s==NULL || rn->len <= 0) {
426 		LM_ERR("invalid route name parameter\n");
427 		return -1;
428 	}
429 	return async_send_query(msg, sdata, rn);
430 }
431 
432 #define _IVALUE_ERROR(NAME) LM_ERR("invalid parameter '" #NAME "' (must be a number)\n")
433 #define _IVALUE(NAME)\
434 int i_##NAME ;\
435 if(fixup_get_ivalue(msg, (gparam_t*)NAME, &( i_##NAME))!=0)\
436 { \
437 	_IVALUE_ERROR(NAME);\
438 	return -1;\
439 }
440 /*
441  * Helper to copy input string parameter into a query parameter
442  */
set_query_param(str * param,str input)443 static int set_query_param(str* param, str input)
444 {
445 	if (param->s) {
446 		shm_free(param->s);
447 		param->s = NULL;
448 		param->len = 0;
449 	}
450 
451 	if (input.s && input.len > 0) {
452 		if (shm_str_dup(param, &input) < 0) {
453 			LM_ERR("Error allocating parameter\n");
454 			return -1;
455 		}
456 	}
457 
458 	return 1;
459 }
460 
461 /*
462  * Helper to copy input string parameter into a query char* parameter
463  */
set_query_cparam(char ** param,str input)464 static int set_query_cparam(char** param, str input)
465 {
466 	if (*param) {
467 		shm_free(*param);
468 		*param = NULL;
469 	}
470 
471 	if (input.s && input.len > 0) {
472 		*param = shm_malloc(input.len+1);
473 
474 		if(*param == NULL) {
475 			LM_ERR("error in shm_malloc\n");
476 			return -1;
477 		}
478 
479 		strncpy(*param, input.s, input.len);
480 		(*param)[input.len] = '\0';
481 
482 		LM_DBG("param set to '%s'\n", *param);
483 	}
484 
485 	return 1;
486 }
487 
488 /* module PVs */
489 
490 #define AH_WRAP_GET_PV(AH_F, PV_F) static int AH_F(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) \
491 	{ \
492 		if (ah_reply) { \
493 			if (ah_error.s) { \
494 				LM_WARN("an async variable was read after http error, use $http_ok to check the request's status\n"); \
495 				return pv_get_null(msg, param, res); \
496 			} else { \
497 				return pv_api.PV_F(ah_reply, param, res); \
498 			} \
499 		} else { \
500 			LM_ERR("the async variables can only be read from an async http worker\n"); \
501 			return pv_get_null(msg, param, res); \
502 		} \
503 	}
504 
AH_WRAP_GET_PV(ah_get_reason,get_reason)505 AH_WRAP_GET_PV(ah_get_reason,      get_reason)
506 AH_WRAP_GET_PV(ah_get_hdr,         get_hdr)
507 AH_WRAP_GET_PV(ah_get_status,      get_status)
508 AH_WRAP_GET_PV(ah_get_msg_body,    get_msg_body)
509 AH_WRAP_GET_PV(ah_get_body_size,   get_body_size)
510 AH_WRAP_GET_PV(ah_get_msg_buf,     get_msg_buf)
511 AH_WRAP_GET_PV(ah_get_msg_len,     get_msg_len)
512 
513 static int w_pv_parse_hdr_name(pv_spec_p sp, str *in) {
514 	return pv_api.parse_hdr_name(sp, in);
515 }
516 
ah_get_id(struct sip_msg * msg,pv_param_t * param,pv_value_t * res)517 static int ah_get_id(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) {
518 	return pv_get_strlval(msg, param, res, q_id, strlen(q_id));
519 }
520 
ah_get_ok(struct sip_msg * msg,pv_param_t * param,pv_value_t * res)521 static int ah_get_ok(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) {
522 	if (ah_reply) {
523 		if (ah_error.s) {
524 			return pv_get_intstrval(msg, param, res, 0, &pv_str_0);
525 		} else {
526 			return pv_get_intstrval(msg, param, res, 1, &pv_str_1);
527 		}
528 	} else {
529 		LM_ERR("the async variables can only be read from an async http worker\n");
530 		return pv_get_null(msg, param, res);
531 	}
532 }
533 
ah_get_err(struct sip_msg * msg,pv_param_t * param,pv_value_t * res)534 static int ah_get_err(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) {
535 	if (ah_reply) {
536 		if (ah_error.s) {
537 			return pv_get_strval(msg, param, res, &ah_error);
538 		} else {
539 			return pv_get_null(msg, param, res);
540 		}
541 	} else {
542 		LM_ERR("the async variables can only be read from an async http worker\n");
543 		return pv_get_null(msg, param, res);
544 	}
545 }
546 
ah_parse_req_name(pv_spec_p sp,str * in)547 static int ah_parse_req_name(pv_spec_p sp, str *in) {
548 	if(sp==NULL || in==NULL || in->len<=0)
549 		return -1;
550 
551 	switch(in->len)
552 	{
553 		case 3:
554 			if(strncmp(in->s, "all", 3)==0)
555 				sp->pvp.pvn.u.isname.name.n = E_HRN_ALL;
556 			else if(strncmp(in->s, "hdr", 3)==0)
557 				sp->pvp.pvn.u.isname.name.n = E_HRN_HDR;
558 			else goto error;
559 			break;
560 		case 4:
561 			if(strncmp(in->s, "body", 4)==0)
562 				sp->pvp.pvn.u.isname.name.n = E_HRN_BODY;
563 			else goto error;
564 			break;
565 		case 6:
566 			if(strncmp(in->s, "method", 6)==0)
567 				sp->pvp.pvn.u.isname.name.n = E_HRN_METHOD;
568 			else goto error;
569 			break;
570 		case 7:
571 			if(strncmp(in->s, "timeout", 7)==0)
572 				sp->pvp.pvn.u.isname.name.n = E_HRN_TIMEOUT;
573 			else if(strncmp(in->s, "suspend", 7)==0)
574 				sp->pvp.pvn.u.isname.name.n = E_HRN_SUSPEND;
575 			else if(strncmp(in->s, "ka-idle", 7)==0)
576 				sp->pvp.pvn.u.isname.name.n = E_HRN_TCP_KA_IDLE;
577 			else goto error;
578 			break;
579 		case 8:
580 			if(strncmp(in->s, "username", 8)==0)
581 				sp->pvp.pvn.u.isname.name.n = E_HRN_USERNAME;
582 			else if(strncmp(in->s, "password", 8)==0)
583 				sp->pvp.pvn.u.isname.name.n = E_HRN_PASSWORD;
584 			else goto error;
585 			break;
586 		case 9:
587 			if(strncmp(in->s, "keepalive", 9)==0)
588 				sp->pvp.pvn.u.isname.name.n = E_HRN_TCP_KA;
589 			else goto error;
590 			break;
591 		case 10:
592 			if(strncmp(in->s, "authmethod", 10)==0)
593 				sp->pvp.pvn.u.isname.name.n = E_HRN_AUTHMETHOD;
594 			else goto error;
595 			break;
596 		case 11:
597 			if(strncmp(in->s, "tls_ca_path", 11)==0)
598 				sp->pvp.pvn.u.isname.name.n = E_HRN_TLS_CA_PATH;
599 			else if(strncmp(in->s, "ka-interval", 11)==0)
600 				sp->pvp.pvn.u.isname.name.n = E_HRN_TCP_KA_INTERVAL;
601 			else goto error;
602 			break;
603 		case 14:
604 			if(strncmp(in->s, "tls_client_key", 14)==0)
605 				sp->pvp.pvn.u.isname.name.n = E_HRN_TLS_CLIENT_KEY;
606 			else goto error;
607 			break;
608 		case 15:
609 			if(strncmp(in->s, "tls_client_cert", 15)==0)
610 				sp->pvp.pvn.u.isname.name.n = E_HRN_TLS_CLIENT_CERT;
611 			else goto error;
612 			break;
613 		default:
614 			goto error;
615 	}
616 	sp->pvp.pvn.type = PV_NAME_INTSTR;
617 	sp->pvp.pvn.u.isname.type = 0;
618 
619 	return 0;
620 
621 error:
622 	LM_ERR("unknown http_req name %.*s\n", in->len, in->s);
623 	return -1;
624 }
625 
ah_set_req(struct sip_msg * msg,pv_param_t * param,int op,pv_value_t * val)626 static int ah_set_req(struct sip_msg* msg, pv_param_t *param,
627 		int op, pv_value_t *val)
628 {
629 	pv_value_t *tval;
630 
631 	if(param==NULL || tmb.t_request==NULL)
632 		return -1;
633 
634 	tval = val;
635 	if((tval!=NULL) && (tval->flags&PV_VAL_NULL)) {
636 		tval = NULL;
637 	}
638 
639 	switch((enum http_req_name_t) param->pvn.u.isname.name.n) {
640 	case E_HRN_ALL:
641 		if (tval == NULL)
642 			set_query_params(&ah_params);
643 		break;
644 	case E_HRN_HDR:
645 		if (tval) {
646 			if (!(tval->flags & PV_VAL_STR)) {
647 				LM_ERR("invalid value type for $http_req(hdr)\n");
648 				return -1;
649 			}
650 			header_list_add(&ah_params.headers, &tval->rs);
651 		}
652 		break;
653 	case E_HRN_METHOD:
654 		if (tval) {
655 			if (!(tval->flags & PV_VAL_STR)) {
656 				LM_ERR("invalid value type for $http_req(method)\n");
657 				return -1;
658 			}
659 			query_params_set_method(&ah_params, &tval->rs);
660 		} else {
661 			ah_params.method = AH_METH_DEFAULT;
662 		}
663 		break;
664 	case E_HRN_TIMEOUT:
665 		if (tval) {
666 			if (!(tval->flags & PV_VAL_INT)) {
667 				LM_ERR("invalid value type for $http_req(timeout)\n");
668 				return -1;
669 			}
670 			ah_params.timeout = tval->ri;
671 		} else {
672 			ah_params.timeout = http_timeout;
673 		}
674 		break;
675 	case E_HRN_TLS_CA_PATH:
676 		if (tval) {
677 			if (!(tval->flags & PV_VAL_STR)) {
678 				LM_ERR("invalid value type for $http_req(tls_ca_path)\n");
679 				return -1;
680 			}
681 			set_query_cparam(&ah_params.tls_ca_path, tval->rs);
682 		}
683 		break;
684 	case E_HRN_TLS_CLIENT_KEY:
685 		if (tval) {
686 			if (!(tval->flags & PV_VAL_STR)) {
687 				LM_ERR("invalid value type for $http_req(tls_client_key)\n");
688 				return -1;
689 			}
690 			set_query_cparam(&ah_params.tls_client_key, tval->rs);
691 		}
692 		break;
693 	case E_HRN_TLS_CLIENT_CERT:
694 		if (tval) {
695 			if (!(tval->flags & PV_VAL_STR)) {
696 				LM_ERR("invalid value type for $http_req(tls_client_cert)\n");
697 				return -1;
698 			}
699 			set_query_cparam(&ah_params.tls_client_cert, tval->rs);
700 		}
701 		break;
702 	case E_HRN_SUSPEND:
703 		if (tval) {
704 			if (!(tval->flags & PV_VAL_INT)) {
705 				LM_ERR("invalid value type for $http_req(suspend)\n");
706 				return -1;
707 			}
708 			ah_params.suspend_transaction = tval->ri?1:0;
709 		} else {
710 			ah_params.suspend_transaction = 1;
711 		}
712 		break;
713 	case E_HRN_BODY:
714 		if (tval) {
715 			if (!(tval->flags & PV_VAL_STR)) {
716 				LM_ERR("invalid value type for $http_req(body)\n");
717 				return -1;
718 			}
719 			set_query_param(&ah_params.body, tval->rs);
720 		}
721 		break;
722 	case E_HRN_AUTHMETHOD:
723 		if (tval) {
724 			if (!(tval->flags & PV_VAL_INT)) {
725 				LM_ERR("invalid value type for $http_req(authmethod)\n");
726 				return -1;
727 			}
728 			ah_params.authmethod = tval->ri;
729 		} else {
730 			ah_params.authmethod = default_authmethod;
731 		}
732 		break;
733 	case E_HRN_USERNAME:
734 		if (tval) {
735 			if (!(tval->flags & PV_VAL_STR)) {
736 				LM_ERR("invalid value type for $http_req(username)\n");
737 				return -1;
738 			}
739 			set_query_cparam(&ah_params.username, tval->rs);
740 		}
741 		break;
742 	case E_HRN_PASSWORD:
743 		if (tval) {
744 			if (!(tval->flags & PV_VAL_STR)) {
745 				LM_ERR("invalid value type for $http_req(password)\n");
746 				return -1;
747 			}
748 			set_query_cparam(&ah_params.password, tval->rs);
749 		}
750 		break;
751 	case E_HRN_TCP_KA:
752 		if (tval) {
753 			if (!(tval->flags & PV_VAL_INT)) {
754 				LM_ERR("invalid value type for $http_req(keepalive)\n");
755 				return -1;
756 			}
757 			ah_params.tcp_keepalive = tval->ri;
758 		} else {
759 			ah_params.tcp_keepalive = tcp_keepalive;
760 		}
761 		break;
762 
763 	case E_HRN_TCP_KA_IDLE:
764 		if (tval) {
765 			if (!(tval->flags & PV_VAL_INT)) {
766 				LM_ERR("invalid value type for $http_req(ka-idle)\n");
767 				return -1;
768 			}
769 			ah_params.tcp_ka_idle = tval->ri;
770 		} else {
771 			ah_params.tcp_ka_idle = tcp_ka_idle;
772 		}
773 		break;
774 
775 	case E_HRN_TCP_KA_INTERVAL:
776 		if (tval) {
777 			if (!(tval->flags & PV_VAL_INT)) {
778 				LM_ERR("invalid value type for $http_req(ka-interval)\n");
779 				return -1;
780 			}
781 			ah_params.tcp_ka_interval = tval->ri;
782 		} else {
783 			ah_params.tcp_ka_interval = tcp_ka_interval;
784 		}
785 		break;
786 	}
787 
788 	return 1;
789 }
790 
791 /**
792  *
793  */
794 /* clang-format off */
795 static sr_kemi_t sr_kemi_http_async_client_exports[] = {
796 	{ str_init("http_async_client"), str_init("query"),
797 		SR_KEMIP_INT, ki_http_async_query,
798 		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
799 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
800 	},
801 
802 	{ {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } }
803 };
804 /* clang-format on */
805 
mod_register(char * path,int * dlflags,void * p1,void * p2)806 int mod_register(char *path, int *dlflags, void *p1, void *p2)
807 {
808 	pv_register_api_t pvra;
809 
810 	pvra = (pv_register_api_t)find_export("pv_register_api", NO_SCRIPT, 0);
811 	if (!pvra) {
812 		LM_ERR("Cannot import pv functions (pv module must be loaded before this module)\n");
813 		return -1;
814 	}
815 	pvra(&pv_api);
816 	sr_kemi_modules_add(sr_kemi_http_async_client_exports);
817 	return 0;
818 }
819