1 /*
2 * http_client Module
3 * Copyright (C) 2015-2016 Edvina AB, Olle E. Johansson
4 *
5 * Based on part of the utils module and part
6 * of the json-rpc-c module
7 *
8 * Copyright (C) 2008 Juha Heinanen
9 * Copyright (C) 2009 1&1 Internet AG
10 * Copyright (C) 2013 Carsten Bock, ng-voice GmbH
11 *
12 * This file is part of Kamailio, a free SIP server.
13 *
14 * Kamailio is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version
18 *
19 * Kamailio is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27 *
28 */
29
30 /*! \file
31 * \brief Kamailio http_client :: The module interface file
32 * \ingroup http_client
33 */
34
35 /*! \defgroup http_client Kamailio :: Module interface to Curl
36 *
37 * http://curl.haxx.se
38 * A generic library for many protocols
39 *
40 * http_connect(connection, url, $avp)
41 * http_connect(connection, url, content-type, data, $avp)
42 *
43 * $var(res) = http_connect("anders", "/postlåda", "application/json", "{ ok, {200, ok}}", "$avp(gurka)");
44 * $var(res) = http_connect("anders", "/postlåda", "application/json", "$var(tomat)", "$avp(gurka)");
45 *
46 */
47
48
49 #include <curl/curl.h>
50
51 #include "../../core/mod_fix.h"
52 #include "../../core/sr_module.h"
53 #include "../../core/ut.h"
54 #include "../../core/resolve.h"
55 #include "../../core/locking.h"
56 #include "../../core/script_cb.h"
57 #include "../../core/mem/shm_mem.h"
58 #include "../../lib/srdb1/db.h"
59 #include "../../core/rpc.h"
60 #include "../../core/rpc_lookup.h"
61 #include "../../core/config.h"
62 #include "../../core/lvalue.h"
63 #include "../../core/pt.h" /* Process table */
64 #include "../../core/kemi.h"
65
66 #include "functions.h"
67 #include "curlcon.h"
68 #include "curlrpc.h"
69 #include "curl_api.h"
70
71 MODULE_VERSION
72
73 #define CURL_USER_AGENT NAME " (" VERSION " (" ARCH "/" OS_QUOTED "))"
74 #define CURL_USER_AGENT_LEN (sizeof(CURL_USER_AGENT) - 1)
75
76 /* Module parameter variables */
77 unsigned int default_connection_timeout = 4;
78 char *default_tls_cacert =
79 NULL; /*!< File name: Default CA cert to use for curl TLS connection */
80 str default_tls_clientcert =
81 STR_NULL; /*!< File name: Default client certificate to use for curl TLS connection */
82 str default_tls_clientkey =
83 STR_NULL; /*!< File name: Key in PEM format that belongs to client cert */
84 str default_cipher_suite_list = STR_NULL; /*!< List of allowed cipher suites */
85 unsigned int default_tls_version = 0; /*!< 0 = Use libcurl default */
86 unsigned int default_tls_verify_peer =
87 1; /*!< 0 = Do not verify TLS server cert. 1 = Verify TLS cert (default) */
88 unsigned int default_tls_verify_host =
89 2; /*!< 0 = Do not verify TLS server CN/SAN 2 = Verify TLS server CN/SAN (default) */
90 str default_http_proxy = STR_NULL; /*!< Default HTTP proxy to use */
91 unsigned int default_http_proxy_port = 0; /*!< Default HTTP proxy port to use */
92 unsigned int default_http_follow_redirect =
93 0; /*!< Follow HTTP redirects CURLOPT_FOLLOWLOCATION */
94 unsigned int default_keep_connections =
95 0; /*!< Keep http connections open for reuse */
96 str default_useragent = {CURL_USER_AGENT,
97 CURL_USER_AGENT_LEN}; /*!< Default CURL useragent. Default "Kamailio Curl " */
98 unsigned int default_maxdatasize = 0; /*!< Default download size. 0=disabled */
99 unsigned int default_authmethod =
100 CURLAUTH_BASIC
101 | CURLAUTH_DIGEST; /*!< authentication method - Basic, Digest or both */
102
103 char *default_netinterface = 0; /*!< local network interface */
104
105 /*!< Default http query result mode
106 * - 0: return full result
107 * - 1: return first line only */
108 unsigned int default_query_result = 1;
109 /*!< Default download size for result of query function. 0=disabled (no limit) */
110 unsigned int default_query_maxdatasize = 0;
111
112 str http_client_config_file = STR_NULL;
113
114 static curl_version_info_data *curl_info;
115
116 /* Module management function prototypes */
117 static int mod_init(void);
118 static int child_init(int);
119 static void destroy(void);
120
121 /* Fixup functions to be defined later */
122 static int fixup_http_query_get(void **param, int param_no);
123 static int fixup_free_http_query_get(void **param, int param_no);
124 static int fixup_http_query_post(void **param, int param_no);
125 static int fixup_free_http_query_post(void **param, int param_no);
126 static int fixup_http_query_post_hdr(void **param, int param_no);
127 static int fixup_free_http_query_post_hdr(void **param, int param_no);
128
129 static int fixup_curl_connect(void **param, int param_no);
130 static int fixup_free_curl_connect(void **param, int param_no);
131 static int fixup_curl_connect_post(void **param, int param_no);
132 static int fixup_curl_connect_post_raw(void **param, int param_no);
133 static int fixup_free_curl_connect_post(void **param, int param_no);
134 static int fixup_free_curl_connect_post_raw(void **param, int param_no);
135 static int w_curl_connect_post(struct sip_msg *_m, char *_con, char *_url,
136 char *_result, char *_ctype, char *_data);
137 static int w_curl_connect_post_raw(struct sip_msg *_m, char *_con, char *_url,
138 char *_result, char *_ctype, char *_data);
139
140 static int fixup_curl_get_redirect(void **param, int param_no);
141 static int fixup_free_curl_get_redirect(void **param, int param_no);
142 static int w_curl_get_redirect(struct sip_msg *_m, char *_con, char *_result);
143
144 /* Wrappers for http_query to be defined later */
145 static int w_http_query(struct sip_msg *_m, char *_url, char *_result);
146 static int w_http_query_post(
147 struct sip_msg *_m, char *_url, char *_post, char *_result);
148 static int w_http_query_post_hdr(struct sip_msg *_m, char *_url, char *_post,
149 char *_hdrs, char *_result);
150 static int w_curl_connect(
151 struct sip_msg *_m, char *_con, char *_url, char *_result);
152
153 /* forward function */
154 static int curl_con_param(modparam_t type, void *val);
155 static int pv_parse_curlerror(pv_spec_p sp, str *in);
156 static int pv_get_curlerror(
157 struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
158
159 /* clang-format off */
160 /* Exported functions */
161 static cmd_export_t cmds[] = {
162 {"http_client_query", (cmd_function)w_http_query, 2, fixup_http_query_get,
163 fixup_free_http_query_get,
164 REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
165 {"http_client_query", (cmd_function)w_http_query_post, 3, fixup_http_query_post,
166 fixup_free_http_query_post,
167 REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
168 {"http_client_query", (cmd_function)w_http_query_post_hdr, 4, fixup_http_query_post_hdr,
169 fixup_free_http_query_post_hdr,
170 REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
171 {"http_connect", (cmd_function)w_curl_connect, 3, fixup_curl_connect,
172 fixup_free_curl_connect,
173 REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
174 {"http_connect", (cmd_function)w_curl_connect_post, 5, fixup_curl_connect_post,
175 fixup_free_curl_connect_post,
176 REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
177 {"http_connect_raw", (cmd_function)w_curl_connect_post_raw, 5, fixup_curl_connect_post_raw,
178 fixup_free_curl_connect_post_raw,
179 REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
180 {"http_get_redirect", (cmd_function)w_curl_get_redirect, 2, fixup_curl_get_redirect,
181 fixup_free_curl_get_redirect,
182 REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
183 {"bind_http_client", (cmd_function)bind_httpc_api, 0, 0, 0, 0},
184 {0,0,0,0,0,0}
185 };
186
187
188 /* Exported parameters */
189 static param_export_t params[] = {
190 {"connection_timeout", PARAM_INT, &default_connection_timeout},
191 {"cacert", PARAM_STRING, &default_tls_cacert },
192 {"client_cert", PARAM_STR, &default_tls_clientcert },
193 {"client_key", PARAM_STR, &default_tls_clientkey },
194 {"cipher_suites", PARAM_STR, &default_cipher_suite_list },
195 {"tlsversion", PARAM_INT, &default_tls_version },
196 {"verify_peer", PARAM_INT, &default_tls_verify_peer },
197 {"verify_host", PARAM_INT, &default_tls_verify_host },
198 {"httpproxyport", PARAM_INT, &default_http_proxy_port },
199 {"httpproxy", PARAM_STR, &default_http_proxy},
200 {"httpredirect", PARAM_INT, &default_http_follow_redirect },
201 {"useragent", PARAM_STR, &default_useragent },
202 {"maxdatasize", PARAM_INT, &default_maxdatasize },
203 {"config_file", PARAM_STR, &http_client_config_file },
204 {"httpcon", PARAM_STRING|USE_FUNC_PARAM, (void*)curl_con_param},
205 {"authmethod", PARAM_INT, &default_authmethod },
206 {"keep_connections", PARAM_INT, &default_keep_connections },
207 {"query_result", PARAM_INT, &default_query_result },
208 {"query_maxdatasize", PARAM_INT, &default_query_maxdatasize },
209 {"netinterface", PARAM_STRING, &default_netinterface },
210 {0, 0, 0}
211 };
212
213
214 /*!
215 * \brief Exported Pseudo variables
216 */
217 static pv_export_t mod_pvs[] = {
218 {{"curlerror", (sizeof("curlerror")-1)}, /* Curl error codes */
219 PVT_OTHER, pv_get_curlerror, 0, pv_parse_curlerror, 0, 0, 0},
220
221 {{0, 0}, 0, 0, 0, 0, 0, 0, 0}
222 };
223
224 /* Module interface */
225 struct module_exports exports = {
226 "http_client", /* module name */
227 DEFAULT_DLFLAGS, /* dlopen flags */
228 cmds, /* exported functions */
229 params, /* exported parameters */
230 0, /* RPC method exports */
231 mod_pvs, /* exported pseudo-variables */
232 0, /* response handling function */
233 mod_init, /* module initialization function */
234 child_init, /* per-child init function */
235 destroy /* module destroy function */
236 };
237 /* clang-format on */
238
239 counter_handle_t connections; /* Number of connection definitions */
240 counter_handle_t connok; /* Successful Connection attempts */
241 counter_handle_t connfail; /* Failed Connection attempts */
242
243
init_shmlock(void)244 static int init_shmlock(void)
245 {
246 return 0;
247 }
248
249
destroy_shmlock(void)250 static void destroy_shmlock(void)
251 {
252 ;
253 }
254
255 /* Init counters */
curl_counter_init()256 static void curl_counter_init()
257 {
258 counter_register(&connections, "httpclient", "connections", 0, 0, 0,
259 "Counter of connection definitions (httpcon)", 0);
260 counter_register(&connok, "httpclient", "connok", 0, 0, 0,
261 "Counter of successful connections (200 OK)", 0);
262 counter_register(&connfail, "httpclient", "connfail", 0, 0, 0,
263 "Counter of failed connections (not 200 OK)", 0);
264 }
265
266
267 /* Module initialization function */
mod_init(void)268 static int mod_init(void)
269 {
270
271 LM_DBG("init curl module\n");
272
273 /* Initialize curl */
274 if(curl_global_init(CURL_GLOBAL_ALL)) {
275 LM_ERR("curl_global_init failed\n");
276 return -1;
277 }
278 curl_info = curl_version_info(CURLVERSION_NOW);
279
280 if(curl_init_rpc() < 0) {
281 LM_ERR("failed to register RPC commands\n");
282 return -1;
283 }
284
285 if(init_shmlock() != 0) {
286 LM_CRIT("cannot initialize shared memory lock.\n");
287 return -1;
288 }
289
290 curl_counter_init();
291 counter_add(connections, curl_connection_count());
292
293 if(default_tls_version >= CURL_SSLVERSION_LAST) {
294 LM_WARN("tlsversion %d unsupported value. Using libcurl default\n",
295 default_tls_version);
296 default_tls_version = CURL_SSLVERSION_DEFAULT;
297 }
298 if(http_client_config_file.s != NULL) {
299 if(http_client_load_config(&http_client_config_file) < 0) {
300 LM_ERR("Failed to load http_client connections from [%.*s]\n",
301 http_client_config_file.len, http_client_config_file.s);
302 return -1;
303 }
304 }
305
306 if(default_connection_timeout == 0) {
307 LM_ERR("CURL connection timeout set to zero. Using default 4 secs\n");
308 default_connection_timeout = 4;
309 }
310 if(default_http_proxy_port == 0) {
311 LM_INFO("HTTP proxy port set to 0. Disabling HTTP proxy\n");
312 }
313
314 LM_DBG("**** init http_client module done. Curl version: %s SSL %s\n",
315 curl_info->version, curl_info->ssl_version);
316 LM_DBG("**** init http_client: Number of connection objects: %d \n",
317 curl_connection_count());
318 LM_DBG("**** init http_client: User Agent: %.*s \n", default_useragent.len,
319 default_useragent.s);
320 LM_DBG("**** init http_client: HTTPredirect: %d \n",
321 default_http_follow_redirect);
322 LM_DBG("**** init http_client: Client Cert: %.*s Key %.*s\n",
323 default_tls_clientcert.len, default_tls_clientcert.s,
324 default_tls_clientkey.len, default_tls_clientkey.s);
325 LM_DBG("**** init http_client: CA Cert: %s \n", default_tls_cacert);
326 LM_DBG("**** init http_client: Cipher Suites: %.*s \n",
327 default_cipher_suite_list.len, default_cipher_suite_list.s);
328 LM_DBG("**** init http_client: SSL Version: %d \n", default_tls_version);
329 LM_DBG("**** init http_client: verifypeer: %d verifyhost: %d\n",
330 default_tls_verify_peer, default_tls_verify_host);
331 LM_DBG("**** init http_client: HTTP Proxy: %.*s Port %d\n",
332 default_http_proxy.len, default_http_proxy.s,
333 default_http_proxy_port);
334 LM_DBG("**** init http_client: Auth method: %d \n", default_authmethod);
335 LM_DBG("**** init http_client: Keep Connections open: %d \n",
336 default_keep_connections);
337
338 LM_DBG("**** Extra: Curl supports %s %s %s \n",
339 (curl_info->features & CURL_VERSION_SSL ? "TLS" : ""),
340 (curl_info->features & CURL_VERSION_IPV6 ? "IPv6" : ""),
341 (curl_info->features & CURL_VERSION_IDN ? "IDN" : ""));
342 return 0;
343 }
344
345 /*! Returns TRUE if curl supports TLS */
curl_support_tls()346 int curl_support_tls()
347 {
348 return (curl_info->features & CURL_VERSION_SSL);
349 }
350
351 /*! Returns TRUE if curl supports IPv6 */
curl_support_ipv6()352 int curl_support_ipv6()
353 {
354 return (curl_info->features & CURL_VERSION_IPV6);
355 }
356
357
358 /* Child initialization function */
child_init(int rank)359 static int child_init(int rank)
360 {
361 int i = my_pid();
362
363 if(rank == PROC_INIT || rank == PROC_MAIN || rank == PROC_TCP_MAIN) {
364 return 0; /* do nothing for the main process */
365 }
366 LM_DBG("*** http_client module initializing process %d\n", i);
367
368 return 0;
369 }
370
371
destroy(void)372 static void destroy(void)
373 {
374 /* Cleanup curl */
375 curl_global_cleanup();
376 destroy_shmlock();
377 }
378
379
380 /**
381 * parse httpcon module parameter
382 */
curl_con_param(modparam_t type,void * val)383 int curl_con_param(modparam_t type, void *val)
384 {
385 if(val == NULL) {
386 goto error;
387 }
388
389 LM_DBG("**** HTTP_CLIENT got modparam httpcon \n");
390 return curl_parse_param((char *)val);
391 error:
392 return -1;
393 }
394
395 /* Fixup functions */
396
397 /*
398 * Fix http_query params: url (string that may contain pvars) and
399 * result (writable pvar).
400 */
fixup_http_query_get(void ** param,int param_no)401 static int fixup_http_query_get(void **param, int param_no)
402 {
403 if(param_no == 1) {
404 return fixup_spve_null(param, 1);
405 }
406
407 if(param_no == 2) {
408 if(fixup_pvar_null(param, 1) != 0) {
409 LM_ERR("http_query: failed to fixup result pvar\n");
410 return -1;
411 }
412 if(((pv_spec_t *)(*param))->setf == NULL) {
413 LM_ERR("http_query: result pvar is not writeble\n");
414 return -1;
415 }
416 return 0;
417 }
418
419 LM_ERR("invalid parameter number <%d>\n", param_no);
420 return -1;
421 }
422
423 /*
424 * Free http_query params.
425 */
fixup_free_http_query_get(void ** param,int param_no)426 static int fixup_free_http_query_get(void **param, int param_no)
427 {
428 if(param_no == 1) {
429 return fixup_free_spve_null(param, 1);
430 }
431
432 if(param_no == 2) {
433 return fixup_free_pvar_null(param, 1);
434 }
435
436 LM_ERR("http_query: invalid parameter number <%d>\n", param_no);
437 return -1;
438 }
439
440
441 /*
442 * Fix curl_connect params: connection(string/pvar) url (string that may contain pvars) and
443 * result (writable pvar).
444 */
fixup_curl_connect(void ** param,int param_no)445 static int fixup_curl_connect(void **param, int param_no)
446 {
447
448 if(param_no == 1) {
449 /* We want char * strings */
450 return 0;
451 }
452 /* URL and data may contain pvar */
453 if(param_no == 2) {
454 return fixup_spve_null(param, 1);
455 }
456 if(param_no == 3) {
457 if(fixup_pvar_null(param, 1) != 0) {
458 LM_ERR("failed to fixup result pvar\n");
459 return -1;
460 }
461 if(((pv_spec_t *)(*param))->setf == NULL) {
462 LM_ERR("result pvar is not writeble\n");
463 return -1;
464 }
465 return 0;
466 }
467
468 LM_ERR("invalid parameter number <%d>\n", param_no);
469 return -1;
470 }
471
472 /*
473 * Fix curl_connect params when posting (5 parameters):
474 * connection (string/pvar), url (string with pvars), content-type,
475 * data (string/pvar, pvar)
476 */
fixup_curl_connect_post(void ** param,int param_no)477 static int fixup_curl_connect_post(void **param, int param_no)
478 {
479
480 if(param_no == 1 || param_no == 3) {
481 /* We want char * strings */
482 return 0;
483 }
484 /* URL and data may contain pvar */
485 if(param_no == 2 || param_no == 4) {
486 return fixup_spve_null(param, 1);
487 }
488 if(param_no == 5) {
489 if(fixup_pvar_null(param, 1) != 0) {
490 LM_ERR("failed to fixup result pseudo variable\n");
491 return -1;
492 }
493 if(((pv_spec_t *)(*param))->setf == NULL) {
494 LM_ERR("result pvar is not writeable\n");
495 return -1;
496 }
497 return 0;
498 }
499
500 LM_ERR("invalid parameter number <%d>\n", param_no);
501 return -1;
502 }
503
504 /*
505 * Fix curl_connect params when posting (5 parameters):
506 * connection (string/pvar), url (string with pvars), content-type,
507 * data (string(with no pvar parsing), pvar)
508 */
fixup_curl_connect_post_raw(void ** param,int param_no)509 static int fixup_curl_connect_post_raw(void **param, int param_no)
510 {
511
512 if(param_no == 1 || param_no == 3 || param_no == 4) {
513 /* We want char * strings */
514 return 0;
515 }
516 /* URL and data may contain pvar */
517 if(param_no == 2) {
518 return fixup_spve_null(param, 1);
519 }
520 if(param_no == 5) {
521 if(fixup_pvar_null(param, 1) != 0) {
522 LM_ERR("failed to fixup result pseudo variable\n");
523 return -1;
524 }
525 if(((pv_spec_t *)(*param))->setf == NULL) {
526 LM_ERR("result pvar is not writeable\n");
527 return -1;
528 }
529 return 0;
530 }
531
532 LM_ERR("invalid parameter number <%d>\n", param_no);
533 return -1;
534 }
535
536 /*
537 * Free curl_connect params.
538 */
fixup_free_curl_connect_post(void ** param,int param_no)539 static int fixup_free_curl_connect_post(void **param, int param_no)
540 {
541 if(param_no == 1 || param_no == 3) {
542 /* Char strings don't need freeing */
543 return 0;
544 }
545 if(param_no == 2 || param_no == 4) {
546 return fixup_free_spve_null(param, 1);
547 }
548
549 if(param_no == 5) {
550 return fixup_free_pvar_null(param, 1);
551 }
552
553 LM_ERR("invalid parameter number <%d>\n", param_no);
554 return -1;
555 }
556
557 /*
558 * Free curl_connect params.
559 */
fixup_free_curl_connect_post_raw(void ** param,int param_no)560 static int fixup_free_curl_connect_post_raw(void **param, int param_no)
561 {
562 if(param_no == 1 || param_no == 3 || param_no == 4) {
563 /* Char strings don't need freeing */
564 return 0;
565 }
566 if(param_no == 2) {
567 return fixup_free_spve_null(param, 1);
568 }
569
570 if(param_no == 5) {
571 return fixup_free_pvar_null(param, 1);
572 }
573
574 LM_ERR("invalid parameter number <%d>\n", param_no);
575 return -1;
576 }
577
578 /*
579 * Free curl_connect params.
580 */
fixup_free_curl_connect(void ** param,int param_no)581 static int fixup_free_curl_connect(void **param, int param_no)
582 {
583 if(param_no == 1) {
584 /* Char strings don't need freeing */
585 return 0;
586 }
587 if(param_no == 2) {
588 return fixup_free_spve_null(param, 1);
589 }
590
591 if(param_no == 3) {
592 return fixup_free_pvar_null(param, 1);
593 }
594
595 LM_ERR("invalid parameter number <%d>\n", param_no);
596 return -1;
597 }
598
599 /*
600 * Wrapper for Curl_connect (GET)
601 */
ki_curl_connect_helper(sip_msg_t * _m,str * con,str * url,pv_spec_t * dst)602 static int ki_curl_connect_helper(sip_msg_t *_m, str *con, str *url,
603 pv_spec_t *dst)
604 {
605 str result = {NULL, 0};
606 pv_value_t val;
607 int ret = 0;
608
609 ret = curl_con_query_url(_m, con, url, &result, NULL, NULL);
610
611 val.rs = result;
612 val.flags = PV_VAL_STR;
613 if(dst->setf) {
614 dst->setf(_m, &dst->pvp, (int)EQ_T, &val);
615 } else {
616 LM_WARN("target pv is not writable\n");
617 }
618
619 if(result.s != NULL)
620 pkg_free(result.s);
621
622 return (ret == 0) ? -1 : ret;
623 }
624
625 /*
626 * Kemi wrapper for Curl_connect (GET)
627 */
ki_curl_connect(sip_msg_t * _m,str * con,str * url,str * dpv)628 static int ki_curl_connect(sip_msg_t *_m, str *con, str *url, str *dpv)
629 {
630 pv_spec_t *dst;
631
632 dst = pv_cache_get(dpv);
633 if(dst==NULL) {
634 LM_ERR("failed to get pv spec for: %.*s\n", dpv->len, dpv->s);
635 return -1;
636 }
637 if(dst->setf==NULL) {
638 LM_ERR("target pv is not writable: %.*s\n", dpv->len, dpv->s);
639 return -1;
640 }
641
642 return ki_curl_connect_helper(_m, con, url, dst);
643 }
644
645 /*
646 * Cfg wrapper for Curl_connect (GET)
647 */
w_curl_connect(sip_msg_t * _m,char * _con,char * _url,char * _result)648 static int w_curl_connect(sip_msg_t *_m, char *_con, char *_url, char *_result)
649 {
650 str con = {NULL, 0};
651 str url = {NULL, 0};
652 pv_spec_t *dst;
653
654 if(_con == NULL || _url == NULL || _result == NULL) {
655 LM_ERR("http_connect: Invalid parameter\n");
656 return -1;
657 }
658 con.s = _con;
659 con.len = strlen(con.s);
660 if(get_str_fparam(&url, _m, (gparam_p)_url) != 0) {
661 LM_ERR("http_connect: url has no value\n");
662 return -1;
663 }
664
665 LM_DBG("**** HTTP_CONNECT Connection %s URL %s Result var %s\n", _con, _url,
666 _result);
667 dst = (pv_spec_t *)_result;
668
669 return ki_curl_connect_helper(_m, &con, &url, dst);
670 }
671
672 /*
673 * Wrapper for Curl_connect (POST)
674 */
ki_curl_connect_post_helper(sip_msg_t * _m,str * con,str * url,str * ctype,str * data,pv_spec_t * dst)675 static int ki_curl_connect_post_helper(sip_msg_t *_m, str *con, str *url,
676 str *ctype, str *data, pv_spec_t *dst)
677 {
678 str result = {NULL, 0};
679 pv_value_t val;
680 int ret = 0;
681
682 ret = curl_con_query_url(_m, con, url, &result, ctype->s, data);
683
684 val.rs = result;
685 val.flags = PV_VAL_STR;
686 if(dst->setf) {
687 dst->setf(_m, &dst->pvp, (int)EQ_T, &val);
688 } else {
689 LM_WARN("target pv is not writtable\n");
690 }
691
692 if(result.s != NULL)
693 pkg_free(result.s);
694
695 return (ret == 0) ? -1 : ret;
696 }
697
698 /*
699 * Kemi wrapper for Curl_connect (POST)
700 */
ki_curl_connect_post(sip_msg_t * _m,str * con,str * url,str * ctype,str * data,str * dpv)701 static int ki_curl_connect_post(sip_msg_t *_m, str *con, str *url,
702 str *ctype, str *data, str *dpv)
703 {
704 pv_spec_t *dst;
705
706 dst = pv_cache_get(dpv);
707 if(dst==NULL) {
708 LM_ERR("failed to get pv spec for: %.*s\n", dpv->len, dpv->s);
709 return -1;
710 }
711 if(dst->setf==NULL) {
712 LM_ERR("target pv is not writable: %.*s\n", dpv->len, dpv->s);
713 return -1;
714 }
715
716 return ki_curl_connect_post_helper(_m, con, url, ctype, data, dst);
717 }
718
719 /*
720 * Wrapper for Curl_connect (POST) raw data (no pvar parsing inside the data)
721 */
w_curl_connect_post_raw(struct sip_msg * _m,char * _con,char * _url,char * _ctype,char * _data,char * _result)722 static int w_curl_connect_post_raw(struct sip_msg *_m, char *_con, char *_url,
723 char *_ctype, char *_data, char *_result)
724 {
725 str con = {NULL, 0};
726 str url = {NULL, 0};
727 str ctype = {NULL, 0};
728 str data = {NULL, 0};
729 pv_spec_t *dst;
730
731 if(_con == NULL || _url == NULL || _ctype==NULL || _data == NULL
732 || _result == NULL) {
733 LM_ERR("http_connect: Invalid parameters\n");
734 return -1;
735 }
736 con.s = _con;
737 con.len = strlen(con.s);
738
739 if(get_str_fparam(&url, _m, (gparam_p)_url) != 0) {
740 LM_ERR("http_connect: URL has no value\n");
741 return -1;
742 }
743
744 ctype.s = _ctype;
745 ctype.len = strlen(ctype.s);
746
747 data.s = _data;
748 data.len = strlen(data.s);
749
750 LM_DBG("**** HTTP_CONNECT: Connection %s URL %s Result var %s\n", _con,
751 _url, _result);
752 dst = (pv_spec_t *)_result;
753
754 return ki_curl_connect_post_helper(_m, &con, &url, &ctype, &data, dst);
755 }
756
757 /*
758 * Wrapper for Curl_connect (POST)
759 */
w_curl_connect_post(struct sip_msg * _m,char * _con,char * _url,char * _ctype,char * _data,char * _result)760 static int w_curl_connect_post(struct sip_msg *_m, char *_con, char *_url,
761 char *_ctype, char *_data, char *_result)
762 {
763 str con = {NULL, 0};
764 str url = {NULL, 0};
765 str ctype = {NULL, 0};
766 str data = {NULL, 0};
767 pv_spec_t *dst;
768
769 if(_con == NULL || _url == NULL || _ctype==NULL || _data == NULL
770 || _result == NULL) {
771 LM_ERR("http_connect: Invalid parameters\n");
772 return -1;
773 }
774 con.s = _con;
775 con.len = strlen(con.s);
776
777 if(get_str_fparam(&url, _m, (gparam_p)_url) != 0) {
778 LM_ERR("http_connect: URL has no value\n");
779 return -1;
780 }
781
782 ctype.s = _ctype;
783 ctype.len = strlen(ctype.s);
784
785 if(get_str_fparam(&data, _m, (gparam_p)_data) != 0) {
786 LM_ERR("http_connect: No post data given\n");
787 return -1;
788 }
789
790 LM_DBG("**** HTTP_CONNECT: Connection %s URL %s Result var %s\n", _con,
791 _url, _result);
792 dst = (pv_spec_t *)_result;
793
794 return ki_curl_connect_post_helper(_m, &con, &url, &ctype, &data, dst);
795 }
796
797 /*!
798 * Fix http_query params: url (string that may contain pvars) and
799 * result (writable pvar).
800 */
fixup_http_query_post(void ** param,int param_no)801 static int fixup_http_query_post(void **param, int param_no)
802 {
803 if((param_no == 1) || (param_no == 2)) {
804 return fixup_spve_null(param, 1);
805 }
806
807 if(param_no == 3) {
808 if(fixup_pvar_null(param, 1) != 0) {
809 LM_ERR("failed to fixup result pvar\n");
810 return -1;
811 }
812 if(((pv_spec_t *)(*param))->setf == NULL) {
813 LM_ERR("result pvar is not writeble\n");
814 return -1;
815 }
816 return 0;
817 }
818
819 LM_ERR("invalid parameter number <%d>\n", param_no);
820 return -1;
821 }
822
823 /*!
824 * Free http_query params.
825 */
fixup_free_http_query_post(void ** param,int param_no)826 static int fixup_free_http_query_post(void **param, int param_no)
827 {
828 if((param_no == 1) || (param_no == 2)) {
829 return fixup_free_spve_null(param, 1);
830 }
831
832 if(param_no == 3) {
833 return fixup_free_pvar_null(param, 1);
834 }
835
836 LM_ERR("invalid parameter number <%d>\n", param_no);
837 return -1;
838 }
839
840 /*
841 * Fix http_query params: url (string that may contain pvars) and
842 * result (writable pvar).
843 */
fixup_http_query_post_hdr(void ** param,int param_no)844 static int fixup_http_query_post_hdr(void **param, int param_no)
845 {
846 if((param_no >= 1) && (param_no <= 3)) {
847 return fixup_spve_null(param, 1);
848 }
849
850 if(param_no == 4) {
851 if(fixup_pvar_null(param, 1) != 0) {
852 LM_ERR("failed to fixup result pvar\n");
853 return -1;
854 }
855 if(((pv_spec_t *)(*param))->setf == NULL) {
856 LM_ERR("result pvar is not writeble\n");
857 return -1;
858 }
859 return 0;
860 }
861
862 LM_ERR("invalid parameter number <%d>\n", param_no);
863 return -1;
864 }
865
866 /*
867 * Free http_query params.
868 */
fixup_free_http_query_post_hdr(void ** param,int param_no)869 static int fixup_free_http_query_post_hdr(void **param, int param_no)
870 {
871 if((param_no >= 1) && (param_no <= 3)) {
872 return fixup_free_spve_null(param, 1);
873 }
874
875 if(param_no == 4) {
876 return fixup_free_pvar_null(param, 1);
877 }
878
879 LM_ERR("invalid parameter number <%d>\n", param_no);
880 return -1;
881 }
882
883 /*!
884 * helper for HTTP-Query function
885 */
ki_http_query_helper(sip_msg_t * _m,str * url,str * post,str * hdrs,pv_spec_t * dst)886 static int ki_http_query_helper(sip_msg_t *_m, str *url, str *post, str *hdrs,
887 pv_spec_t *dst)
888 {
889 int ret = 0;
890 str result = {NULL, 0};
891 pv_value_t val;
892
893 if(url==NULL || url->s==NULL) {
894 LM_ERR("invalid url parameter\n");
895 return -1;
896 }
897 ret = http_client_query(_m, url->s, &result,
898 (post && post->s && post->len>0)?post->s:NULL,
899 (hdrs && hdrs->s && hdrs->len>0)?hdrs->s:NULL);
900
901 val.rs = result;
902 val.flags = PV_VAL_STR;
903 if(dst->setf) {
904 dst->setf(_m, &dst->pvp, (int)EQ_T, &val);
905 } else {
906 LM_WARN("target pv is not writable\n");
907 }
908
909 if(result.s != NULL)
910 pkg_free(result.s);
911
912 return (ret == 0) ? -1 : ret;
913 }
914
ki_http_query_post_hdrs(sip_msg_t * _m,str * url,str * post,str * hdrs,str * dpv)915 static int ki_http_query_post_hdrs(sip_msg_t *_m, str *url, str *post, str *hdrs,
916 str *dpv)
917 {
918 pv_spec_t *dst;
919
920 dst = pv_cache_get(dpv);
921 if(dst==NULL) {
922 LM_ERR("failed to get pv spec for: %.*s\n", dpv->len, dpv->s);
923 return -1;
924 }
925 if(dst->setf==NULL) {
926 LM_ERR("target pv is not writable: %.*s\n", dpv->len, dpv->s);
927 return -1;
928 }
929
930 return ki_http_query_helper(_m, url, post, hdrs, dst);
931 }
932
ki_http_query_post(sip_msg_t * _m,str * url,str * post,str * dpv)933 static int ki_http_query_post(sip_msg_t *_m, str *url, str *post, str *dpv)
934 {
935 return ki_http_query_post_hdrs(_m, url, post, NULL, dpv);
936 }
937
ki_http_query(sip_msg_t * _m,str * url,str * dpv)938 static int ki_http_query(sip_msg_t *_m, str *url, str *dpv)
939 {
940 return ki_http_query_post_hdrs(_m, url, NULL, NULL, dpv);
941 }
942
943 /*!
944 * Wrapper for HTTP-Query function for cfg script
945 */
w_http_query_script(sip_msg_t * _m,char * _url,char * _post,char * _hdrs,char * _result)946 static int w_http_query_script(sip_msg_t *_m, char *_url, char *_post,
947 char *_hdrs, char *_result)
948 {
949 str url = {NULL, 0};
950 str post = {NULL, 0};
951 str hdrs = {NULL, 0};
952 pv_spec_t *dst;
953
954 if(get_str_fparam(&url, _m, (gparam_p)_url) != 0 || url.len <= 0) {
955 LM_ERR("URL has no value\n");
956 return -1;
957 }
958 if(_post && get_str_fparam(&post, _m, (gparam_p)_post) != 0) {
959 LM_ERR("DATA has no value\n");
960 return -1;
961 } else {
962 if(post.len == 0) {
963 post.s = NULL;
964 }
965 }
966 if(_hdrs && get_str_fparam(&hdrs, _m, (gparam_p)_hdrs) != 0) {
967 LM_ERR("HDRS has no value\n");
968 return -1;
969 } else {
970 if(hdrs.len == 0) {
971 hdrs.s = NULL;
972 }
973 }
974 dst = (pv_spec_t *)_result;
975
976 return ki_http_query_helper(_m, &url, &post, &hdrs, dst);
977 }
978
979 /*!
980 * Wrapper for HTTP-Query (GET)
981 */
w_http_query(struct sip_msg * _m,char * _url,char * _result)982 static int w_http_query(struct sip_msg *_m, char *_url, char *_result)
983 {
984 return w_http_query_script(_m, _url, NULL, NULL, _result);
985 }
986
987 /*!
988 * Wrapper for HTTP-Query (POST-Variant)
989 */
w_http_query_post(struct sip_msg * _m,char * _url,char * _post,char * _result)990 static int w_http_query_post(
991 struct sip_msg *_m, char *_url, char *_post, char *_result)
992 {
993 return w_http_query_script(_m, _url, _post, NULL, _result);
994 }
995
996 /*!
997 * Wrapper for HTTP-Query (HDRS-Variant)
998 */
w_http_query_post_hdr(struct sip_msg * _m,char * _url,char * _post,char * _hdrs,char * _result)999 static int w_http_query_post_hdr(
1000 struct sip_msg *_m, char *_url, char *_post, char *_hdrs, char *_result)
1001 {
1002 return w_http_query_script(_m, _url, _post, _hdrs, _result);
1003 }
1004
1005 /*!
1006 * Parse arguments to pv $curlerror
1007 */
pv_parse_curlerror(pv_spec_p sp,str * in)1008 static int pv_parse_curlerror(pv_spec_p sp, str *in)
1009 {
1010 int cerr = 0;
1011 if(sp == NULL || in == NULL || in->len <= 0)
1012 return -1;
1013
1014 cerr = atoi(in->s);
1015 LM_DBG(" =====> CURL ERROR %d \n", cerr);
1016 sp->pvp.pvn.u.isname.name.n = cerr;
1017
1018 sp->pvp.pvn.type = PV_NAME_INTSTR;
1019 sp->pvp.pvn.u.isname.type = 0;
1020
1021 return 0;
1022 }
1023
1024 /*
1025 * PV - return curl error explanation as string
1026 */
pv_get_curlerror(struct sip_msg * msg,pv_param_t * param,pv_value_t * res)1027 static int pv_get_curlerror(
1028 struct sip_msg *msg, pv_param_t *param, pv_value_t *res)
1029 {
1030 str curlerr;
1031 char *err = NULL;
1032
1033 if(param == NULL) {
1034 return -1;
1035 }
1036
1037 /* cURL error codes does not collide with HTTP codes */
1038 if(param->pvn.u.isname.name.n < 0 || param->pvn.u.isname.name.n > 999) {
1039 err = "Bad CURL error code";
1040 }
1041 if(param->pvn.u.isname.name.n > 99) {
1042 err = "HTTP result code";
1043 }
1044 if(err == NULL) {
1045 err = (char *)curl_easy_strerror(param->pvn.u.isname.name.n);
1046 }
1047 curlerr.s = err;
1048 curlerr.len = strlen(err);
1049
1050 return pv_get_strval(msg, param, res, &curlerr);
1051 }
1052
1053
1054 /*
1055 * Fix curl_get_redirect params: connection(string/pvar) url (string that may contain pvars) and
1056 * result (writable pvar).
1057 */
fixup_curl_get_redirect(void ** param,int param_no)1058 static int fixup_curl_get_redirect(void **param, int param_no)
1059 {
1060 if(param_no == 1) { /* Connection name */
1061 /* We want char * strings */
1062 return 0;
1063 }
1064 if(param_no == 2) { /* PVAR to store result in */
1065 if(fixup_pvar_null(param, 1) != 0) {
1066 LM_ERR("failed to fixup result pseudo variable\n");
1067 return -1;
1068 }
1069 if(((pv_spec_t *)(*param))->setf == NULL) {
1070 LM_ERR("result pseudovariable is not writeable\n");
1071 return -1;
1072 }
1073 return 0;
1074 }
1075
1076 LM_ERR("invalid parameter number <%d>\n", param_no);
1077 return -1;
1078 }
1079
1080 /*
1081 * Free curl_get_redirect params.
1082 */
fixup_free_curl_get_redirect(void ** param,int param_no)1083 static int fixup_free_curl_get_redirect(void **param, int param_no)
1084 {
1085 if(param_no == 1) {
1086 /* Char strings don't need freeing */
1087 return 0;
1088 }
1089 if(param_no == 2) {
1090 return fixup_free_spve_null(param, 1);
1091 }
1092
1093 LM_ERR("invalid parameter number <%d>\n", param_no);
1094 return -1;
1095 }
1096
1097 /*
1098 * Wrapper for Curl_redirect
1099 */
w_curl_get_redirect(struct sip_msg * _m,char * _con,char * _result)1100 static int w_curl_get_redirect(struct sip_msg *_m, char *_con, char *_result)
1101 {
1102
1103 str con = {NULL, 0};
1104 str result = {NULL, 0};
1105 pv_spec_t *dst;
1106 pv_value_t val;
1107 int ret = 0;
1108
1109 if(_con == NULL || _result == NULL) {
1110 LM_ERR("Invalid or missing parameter\n");
1111 return -1;
1112 }
1113 con.s = _con;
1114 con.len = strlen(con.s);
1115
1116 LM_DBG("**** http_client get_redirect Connection %s Result var %s\n", _con,
1117 _result);
1118
1119 ret = curl_get_redirect(_m, &con, &result);
1120
1121 val.rs = result;
1122 val.flags = PV_VAL_STR;
1123 dst = (pv_spec_t *)_result;
1124 dst->setf(_m, &dst->pvp, (int)EQ_T, &val);
1125
1126 if(result.s != NULL)
1127 pkg_free(result.s);
1128
1129 return ret;
1130 }
1131
1132 /**
1133 *
1134 */
1135 /* clang-format off */
1136 static sr_kemi_t sr_kemi_http_client_exports[] = {
1137 { str_init("http_client"), str_init("query"),
1138 SR_KEMIP_INT, ki_http_query,
1139 { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
1140 SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1141 },
1142 { str_init("http_client"), str_init("query_post"),
1143 SR_KEMIP_INT, ki_http_query_post,
1144 { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_STR,
1145 SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1146 },
1147 { str_init("http_client"), str_init("query_post_hdrs"),
1148 SR_KEMIP_INT, ki_http_query_post_hdrs,
1149 { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_STR,
1150 SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE }
1151 },
1152 { str_init("http_client"), str_init("curl_connect"),
1153 SR_KEMIP_INT, ki_curl_connect,
1154 { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_STR,
1155 SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1156 },
1157 { str_init("http_client"), str_init("curl_connect_post"),
1158 SR_KEMIP_INT, ki_curl_connect_post,
1159 { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_STR,
1160 SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE }
1161 },
1162
1163 { {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } }
1164 };
1165 /* clang-format on */
1166
mod_register(char * path,int * dlflags,void * p1,void * p2)1167 int mod_register(char *path, int *dlflags, void *p1, void *p2)
1168 {
1169 sr_kemi_modules_add(sr_kemi_http_client_exports);
1170 return 0;
1171 }
1172