Asterisk - The Open Source Telephony Project GIT-master-27fb039
Loading...
Searching...
No Matches
Data Structures | Macros | Enumerations | Functions | Variables
func_curl.c File Reference

Curl - Load a URL. More...

#include "asterisk.h"
#include <curl/curl.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/cli.h"
#include "asterisk/module.h"
#include "asterisk/app.h"
#include "asterisk/utils.h"
#include "asterisk/threadstorage.h"
#include "asterisk/test.h"
Include dependency graph for func_curl.c:

Go to the source code of this file.

Data Structures

struct  curl_args
 
struct  curl_settings
 
struct  curl_write_callback_data
 Callback data passed to WriteMemoryCallback. More...
 
struct  global_curl_info
 

Macros

#define CURLOPT_SPECIAL_FAILURE_CODE   999
 
#define CURLOPT_SPECIAL_HASHCOMPAT   ((CURLoption) -500)
 
#define CURLVERSION_ATLEAST(a, b, c)    ((LIBCURL_VERSION_MAJOR > (a)) || ((LIBCURL_VERSION_MAJOR == (a)) && (LIBCURL_VERSION_MINOR > (b))) || ((LIBCURL_VERSION_MAJOR == (a)) && (LIBCURL_VERSION_MINOR == (b)) && (LIBCURL_VERSION_PATCH >= (c))))
 

Enumerations

enum  hashcompat { HASHCOMPAT_NO = 0 , HASHCOMPAT_YES , HASHCOMPAT_LEGACY }
 
enum  optiontype {
  OT_BOOLEAN , OT_INTEGER , OT_INTEGER_MS , OT_STRING ,
  OT_ENUM
}
 

Functions

static void __reg_module (void)
 
static void __unreg_module (void)
 
static int acf_curl_exec (struct ast_channel *chan, const char *cmd, char *info, struct ast_str **buf, ssize_t len)
 
static int acf_curl_helper (struct ast_channel *chan, struct curl_args *args)
 
static int acf_curl_write (struct ast_channel *chan, const char *cmd, char *name, const char *value)
 
static int acf_curlopt_helper (struct ast_channel *chan, const char *cmd, char *data, char *buf, struct ast_str **bufstr, ssize_t len)
 
static int acf_curlopt_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 
static int acf_curlopt_read2 (struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
 
static int acf_curlopt_write (struct ast_channel *chan, const char *cmd, char *name, const char *value)
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
 AST_THREADSTORAGE_CUSTOM_SCOPE (curl_instance, curl_instance_init, curl_instance_cleanup, static)
 
 AST_THREADSTORAGE_CUSTOM_SCOPE (thread_escapebuf, NULL, ast_free_ptr, static)
 
static void curl_instance_cleanup (void *data)
 
static int curl_instance_init (void *data)
 
static void curlds_free (void *data)
 
static int load_module (void)
 
static int parse_curlopt_key (const char *name, CURLoption *key, enum optiontype *ot)
 
static int unload_module (void)
 
static int url_is_vulnerable (const char *url)
 Check for potential HTTP injection risk.
 
static size_t WriteMemoryCallback (void *ptr, size_t size, size_t nmemb, void *data)
 

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Load external URL" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_REALTIME_DEPEND2, .requires = "res_curl", }
 
static struct ast_custom_function acf_curl
 
static struct ast_custom_function acf_curlopt
 
static const struct ast_module_infoast_module_info = &__mod_info
 
static const struct ast_datastore_info curl_info
 
struct global_curl_info global_curl_info = AST_LIST_HEAD_INIT_VALUE
 

Detailed Description

Curl - Load a URL.

Author
Tilghman Lesher curl-.nosp@m.2005.nosp@m.0919@.nosp@m.the-.nosp@m.tilgh.nosp@m.man..nosp@m.com
Note
Brian Wilkins bwilk.nosp@m.ins@.nosp@m.cfl.r.nosp@m.r.co.nosp@m.m (Added POST option)
ExtRef:
Depends on the CURL library - http://curl.haxx.se/

Definition in file func_curl.c.

Macro Definition Documentation

◆ CURLOPT_SPECIAL_FAILURE_CODE

#define CURLOPT_SPECIAL_FAILURE_CODE   999

Definition at line 267 of file func_curl.c.

◆ CURLOPT_SPECIAL_HASHCOMPAT

#define CURLOPT_SPECIAL_HASHCOMPAT   ((CURLoption) -500)

Definition at line 265 of file func_curl.c.

◆ CURLVERSION_ATLEAST

#define CURLVERSION_ATLEAST (   a,
  b,
  c 
)     ((LIBCURL_VERSION_MAJOR > (a)) || ((LIBCURL_VERSION_MAJOR == (a)) && (LIBCURL_VERSION_MINOR > (b))) || ((LIBCURL_VERSION_MAJOR == (a)) && (LIBCURL_VERSION_MINOR == (b)) && (LIBCURL_VERSION_PATCH >= (c))))

Definition at line 262 of file func_curl.c.

270 {
271 .type = "CURL",
272 .destroy = curlds_free,
273};
274
275struct curl_settings {
277 CURLoption key;
278 void *value;
279};
280
282
283static void curlds_free(void *data)
284{
286 struct curl_settings *setting;
287 if (!list) {
288 return;
289 }
290 while ((setting = AST_LIST_REMOVE_HEAD(list, list))) {
291 ast_free(setting);
292 }
294 ast_free(list);
295}
296
297enum optiontype {
301 OT_STRING,
302 OT_ENUM,
303};
304
305enum hashcompat {
306 HASHCOMPAT_NO = 0,
309};
310
311static int parse_curlopt_key(const char *name, CURLoption *key, enum optiontype *ot)
312{
313 if (!strcasecmp(name, "header")) {
314 *key = CURLOPT_HEADER;
315 *ot = OT_BOOLEAN;
316 } else if (!strcasecmp(name, "httpheader")) {
317 *key = CURLOPT_HTTPHEADER;
318 *ot = OT_STRING;
319 } else if (!strcasecmp(name, "httpauth")) {
320 *key = CURLOPT_HTTPAUTH;
321 *ot = OT_ENUM;
322 } else if (!strcasecmp(name, "proxy")) {
323 *key = CURLOPT_PROXY;
324 *ot = OT_STRING;
325 } else if (!strcasecmp(name, "proxyport")) {
326 *key = CURLOPT_PROXYPORT;
327 *ot = OT_INTEGER;
328 } else if (!strcasecmp(name, "proxytype")) {
329 *key = CURLOPT_PROXYTYPE;
330 *ot = OT_ENUM;
331 } else if (!strcasecmp(name, "dnstimeout")) {
332 *key = CURLOPT_DNS_CACHE_TIMEOUT;
333 *ot = OT_INTEGER;
334 } else if (!strcasecmp(name, "userpwd")) {
335 *key = CURLOPT_USERPWD;
336 *ot = OT_STRING;
337 } else if (!strcasecmp(name, "proxyuserpwd")) {
338 *key = CURLOPT_PROXYUSERPWD;
339 *ot = OT_STRING;
340 } else if (!strcasecmp(name, "followlocation")) {
341 *key = CURLOPT_FOLLOWLOCATION;
342 *ot = OT_BOOLEAN;
343 } else if (!strcasecmp(name, "maxredirs")) {
344 *key = CURLOPT_MAXREDIRS;
345 *ot = OT_INTEGER;
346 } else if (!strcasecmp(name, "referer")) {
347 *key = CURLOPT_REFERER;
348 *ot = OT_STRING;
349 } else if (!strcasecmp(name, "useragent")) {
350 *key = CURLOPT_USERAGENT;
351 *ot = OT_STRING;
352 } else if (!strcasecmp(name, "cookie")) {
353 *key = CURLOPT_COOKIE;
354 *ot = OT_STRING;
355 } else if (!strcasecmp(name, "ftptimeout")) {
356 *key = CURLOPT_FTP_RESPONSE_TIMEOUT;
357 *ot = OT_INTEGER;
358 } else if (!strcasecmp(name, "httptimeout")) {
359#if CURLVERSION_ATLEAST(7,16,2)
360 *key = CURLOPT_TIMEOUT_MS;
361 *ot = OT_INTEGER_MS;
362#else
363 *key = CURLOPT_TIMEOUT;
364 *ot = OT_INTEGER;
365#endif
366 } else if (!strcasecmp(name, "conntimeout")) {
367#if CURLVERSION_ATLEAST(7,16,2)
368 *key = CURLOPT_CONNECTTIMEOUT_MS;
369 *ot = OT_INTEGER_MS;
370#else
371 *key = CURLOPT_CONNECTTIMEOUT;
372 *ot = OT_INTEGER;
373#endif
374 } else if (!strcasecmp(name, "ftptext")) {
375 *key = CURLOPT_TRANSFERTEXT;
376 *ot = OT_BOOLEAN;
377 } else if (!strcasecmp(name, "ssl_verifypeer")) {
378 *key = CURLOPT_SSL_VERIFYPEER;
379 *ot = OT_BOOLEAN;
380 } else if (!strcasecmp(name, "ssl_verifyhost")) {
381 *key = CURLOPT_SSL_VERIFYHOST;
382 *ot = OT_INTEGER;
383 } else if (!strcasecmp(name, "ssl_cainfo")) {
384 *key = CURLOPT_CAINFO;
385 *ot = OT_STRING;
386 } else if (!strcasecmp(name, "ssl_capath")) {
387 *key = CURLOPT_CAPATH;
388 *ot = OT_STRING;
389 } else if (!strcasecmp(name, "ssl_cert")) {
390 *key = CURLOPT_SSLCERT;
391 *ot = OT_STRING;
392 } else if (!strcasecmp(name, "ssl_certtype")) {
393 *key = CURLOPT_SSLCERTTYPE;
394 *ot = OT_STRING;
395 } else if (!strcasecmp(name, "ssl_key")) {
396 *key = CURLOPT_SSLKEY;
397 *ot = OT_STRING;
398 } else if (!strcasecmp(name, "ssl_keytype")) {
399 *key = CURLOPT_SSLKEYTYPE;
400 *ot = OT_STRING;
401 } else if (!strcasecmp(name, "ssl_keypasswd")) {
402 *key = CURLOPT_KEYPASSWD;
403 *ot = OT_STRING;
404 } else if (!strcasecmp(name, "hashcompat")) {
406 *ot = OT_ENUM;
407 } else if (!strcasecmp(name, "failurecodes")) {
409 *ot = OT_STRING;
410 } else {
411 return -1;
412 }
413 return 0;
414}
415
416static int acf_curlopt_write(struct ast_channel *chan, const char *cmd, char *name, const char *value)
417{
418 struct ast_datastore *store;
419 struct global_curl_info *list;
420 struct curl_settings *cur, *new = NULL;
421 CURLoption key;
422 enum optiontype ot;
423
424 if (chan) {
425 ast_channel_lock(chan);
426 if (!(store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
427 /* Create a new datastore */
428 if (!(store = ast_datastore_alloc(&curl_info, NULL))) {
429 ast_log(LOG_ERROR, "Unable to allocate new datastore. Cannot set any CURL options\n");
430 ast_channel_unlock(chan);
431 return -1;
432 }
433
434 if (!(list = ast_calloc(1, sizeof(*list)))) {
435 ast_log(LOG_ERROR, "Unable to allocate list head. Cannot set any CURL options\n");
436 ast_datastore_free(store);
437 ast_channel_unlock(chan);
438 return -1;
439 }
440
441 store->data = list;
443 ast_channel_datastore_add(chan, store);
444 } else {
445 list = store->data;
446 }
447 ast_channel_unlock(chan);
448 } else {
449 /* Populate the global structure */
451 }
452
453 if (!parse_curlopt_key(name, &key, &ot)) {
454 if (ot == OT_BOOLEAN) {
455 if ((new = ast_calloc(1, sizeof(*new)))) {
456 new->value = (void *)((long) ast_true(value));
457 }
458 } else if (ot == OT_INTEGER) {
459 long tmp = atol(value);
460 if ((new = ast_calloc(1, sizeof(*new)))) {
461 new->value = (void *)tmp;
462 }
463 } else if (ot == OT_INTEGER_MS) {
464 long tmp = atof(value) * 1000.0;
465 if ((new = ast_calloc(1, sizeof(*new)))) {
466 new->value = (void *)tmp;
467 }
468 } else if (ot == OT_STRING) {
469 if ((new = ast_calloc(1, sizeof(*new) + strlen(value) + 1))) {
470 new->value = (char *)new + sizeof(*new);
471 strcpy(new->value, value);
472 }
473 } else if (ot == OT_ENUM) {
474 if (key == CURLOPT_PROXYTYPE) {
475 long ptype =
476#if CURLVERSION_ATLEAST(7,10,0)
477 CURLPROXY_HTTP;
478#else
479 CURLPROXY_SOCKS5;
480#endif
481 if (0) {
482#if CURLVERSION_ATLEAST(7,15,2)
483 } else if (!strcasecmp(value, "socks4")) {
484 ptype = CURLPROXY_SOCKS4;
485#endif
486#if CURLVERSION_ATLEAST(7,18,0)
487 } else if (!strcasecmp(value, "socks4a")) {
488 ptype = CURLPROXY_SOCKS4A;
489#endif
490#if CURLVERSION_ATLEAST(7,18,0)
491 } else if (!strcasecmp(value, "socks5")) {
492 ptype = CURLPROXY_SOCKS5;
493#endif
494#if CURLVERSION_ATLEAST(7,18,0)
495 } else if (!strncasecmp(value, "socks5", 6)) {
496 ptype = CURLPROXY_SOCKS5_HOSTNAME;
497#endif
498 }
499
500 if ((new = ast_calloc(1, sizeof(*new)))) {
501 new->value = (void *)ptype;
502 }
503 } else if (key == CURLOPT_HTTPAUTH) {
504 long authtype = 0;
505 char *authmethod, *authstr = ast_strdupa(value);
506 while ((authmethod = strsep(&authstr, ","))) {
507 if (!strcasecmp(authmethod, "basic")) {
508 authtype |= CURLAUTH_BASIC;
509 } else if (!strcasecmp(authmethod, "digest")) {
510 authtype |= CURLAUTH_DIGEST;
511 } else {
512 ast_log(LOG_WARNING, "Auth method '%s' invalid or not supported\n", authmethod);
513 return -1;
514 }
515 }
516 if (!authmethod) {
517 ast_log(LOG_WARNING, "Auth method '%s' invalid or not supported\n", value);
518 }
519 if ((new = ast_calloc(1, sizeof(*new)))) {
520 new->value = (void *)authtype;
521 }
522 } else if (key == CURLOPT_SPECIAL_HASHCOMPAT) {
523 if ((new = ast_calloc(1, sizeof(*new)))) {
524 new->value = (void *) (long) (!strcasecmp(value, "legacy") ? HASHCOMPAT_LEGACY : ast_true(value) ? HASHCOMPAT_YES : HASHCOMPAT_NO);
525 }
526 } else {
527 /* Highly unlikely */
528 goto yuck;
529 }
530 }
531
532 /* Memory allocation error */
533 if (!new) {
534 return -1;
535 }
536
537 new->key = key;
538 } else {
539yuck:
540 ast_log(LOG_ERROR, "Unrecognized option: %s\n", name);
541 return -1;
542 }
543
544 /* Remove any existing entry, only http headers are left */
546 if (new->key != CURLOPT_HTTPHEADER) {
548 if (cur->key == new->key) {
550 ast_free(cur);
551 break;
552 }
553 }
555 }
556
557 /* Insert new entry */
558 ast_debug(1, "Inserting entry %p with key %d and value %p\n", new, new->key, new->value);
561
562 return 0;
563}
564
565static int acf_curlopt_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, struct ast_str **bufstr, ssize_t len)
566{
567 struct ast_datastore *store;
568 struct global_curl_info *list[2] = { &global_curl_info, NULL };
569 struct curl_settings *cur = NULL;
570 CURLoption key;
571 enum optiontype ot;
572 int i;
573
574 if (parse_curlopt_key(data, &key, &ot)) {
575 ast_log(LOG_ERROR, "Unrecognized option: '%s'\n", data);
576 return -1;
577 }
578
579 if (chan) {
580 /* If we have a channel, we want to read the options set there before
581 falling back to the global settings */
582 ast_channel_lock(chan);
584 ast_channel_unlock(chan);
585
586 if (store) {
587 list[0] = store->data;
589 }
590 }
591
592 for (i = 0; i < 2; i++) {
593 if (!list[i]) {
594 break;
595 }
597 AST_LIST_TRAVERSE(list[i], cur, list) {
598 if (cur->key == key) {
599 if (ot == OT_BOOLEAN || ot == OT_INTEGER) {
600 if (buf) {
601 snprintf(buf, len, "%ld", (long) cur->value);
602 } else {
603 ast_str_set(bufstr, len, "%ld", (long) cur->value);
604 }
605 } else if (ot == OT_INTEGER_MS) {
606 if ((long) cur->value % 1000 == 0) {
607 if (buf) {
608 snprintf(buf, len, "%ld", (long)cur->value / 1000);
609 } else {
610 ast_str_set(bufstr, len, "%ld", (long) cur->value / 1000);
611 }
612 } else {
613 if (buf) {
614 snprintf(buf, len, "%.3f", (double) ((long) cur->value) / 1000.0);
615 } else {
616 ast_str_set(bufstr, len, "%.3f", (double) ((long) cur->value) / 1000.0);
617 }
618 }
619 } else if (ot == OT_STRING) {
620 ast_debug(1, "Found entry %p, with key %d and value %p\n", cur, cur->key, cur->value);
621 if (buf) {
623 } else {
624 ast_str_set(bufstr, 0, "%s", (char *) cur->value);
625 }
626 } else if (key == CURLOPT_PROXYTYPE) {
627 const char *strval = "unknown";
628 if (0) {
629#if CURLVERSION_ATLEAST(7,15,2)
630 } else if ((long)cur->value == CURLPROXY_SOCKS4) {
631 strval = "socks4";
632#endif
633#if CURLVERSION_ATLEAST(7,18,0)
634 } else if ((long)cur->value == CURLPROXY_SOCKS4A) {
635 strval = "socks4a";
636#endif
637 } else if ((long)cur->value == CURLPROXY_SOCKS5) {
638 strval = "socks5";
639#if CURLVERSION_ATLEAST(7,18,0)
640 } else if ((long)cur->value == CURLPROXY_SOCKS5_HOSTNAME) {
641 strval = "socks5hostname";
642#endif
643#if CURLVERSION_ATLEAST(7,10,0)
644 } else if ((long)cur->value == CURLPROXY_HTTP) {
645 strval = "http";
646#endif
647 }
648 if (buf) {
649 ast_copy_string(buf, strval, len);
650 } else {
651 ast_str_set(bufstr, 0, "%s", strval);
652 }
653 } else if (key == CURLOPT_SPECIAL_HASHCOMPAT) {
654 const char *strval = "unknown";
655 if ((long) cur->value == HASHCOMPAT_LEGACY) {
656 strval = "legacy";
657 } else if ((long) cur->value == HASHCOMPAT_YES) {
658 strval = "yes";
659 } else if ((long) cur->value == HASHCOMPAT_NO) {
660 strval = "no";
661 }
662 if (buf) {
663 ast_copy_string(buf, strval, len);
664 } else {
665 ast_str_set(bufstr, 0, "%s", strval);
666 }
667 }
668 break;
669 }
670 }
672 if (cur) {
673 break;
674 }
675 }
676
677 return cur ? 0 : -1;
678}
679
680static int acf_curlopt_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
681{
682 return acf_curlopt_helper(chan, cmd, data, buf, NULL, len);
683}
684
685static int acf_curlopt_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
686{
687 return acf_curlopt_helper(chan, cmd, data, NULL, buf, len);
688}
689
690/*! \brief Callback data passed to \ref WriteMemoryCallback */
692 /*! \brief If a string is being built, the string buffer */
693 struct ast_str *str;
694 /*! \brief The max size of \ref str */
695 ssize_t len;
696 /*! \brief If a file is being retrieved, the file to write to */
697 FILE *out_file;
698};
699
700static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
701{
702 register int realsize = 0;
703 struct curl_write_callback_data *cb_data = data;
704
705 if (cb_data->str) {
706 realsize = size * nmemb;
707 ast_str_append_substr(&cb_data->str, 0, ptr, realsize);
708 } else if (cb_data->out_file) {
709 realsize = fwrite(ptr, size, nmemb, cb_data->out_file);
710 }
711
712 return realsize;
713}
714
715static int curl_instance_init(void *data)
716{
717 CURL **curl = data;
718
719 if (!(*curl = curl_easy_init()))
720 return -1;
721
722 curl_easy_setopt(*curl, CURLOPT_NOSIGNAL, 1);
723 curl_easy_setopt(*curl, CURLOPT_TIMEOUT, 180);
724 curl_easy_setopt(*curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
725 curl_easy_setopt(*curl, CURLOPT_USERAGENT, AST_CURL_USER_AGENT);
726
727 return 0;
728}
729
730static void curl_instance_cleanup(void *data)
731{
732 CURL **curl = data;
733
734 curl_easy_cleanup(*curl);
735
736 ast_free(data);
737}
738
740AST_THREADSTORAGE(thread_escapebuf);
741
742/*!
743 * \brief Check for potential HTTP injection risk.
744 *
745 * CVE-2014-8150 brought up the fact that HTTP proxies are subject to injection
746 * attacks. An HTTP URL sent to a proxy contains a carriage-return linefeed combination,
747 * followed by a complete HTTP request. Proxies will handle this as two separate HTTP
748 * requests rather than as a malformed URL.
749 *
750 * libcURL patched this vulnerability in version 7.40.0, but we have no guarantee that
751 * Asterisk systems will be using an up-to-date cURL library. Therefore, we implement
752 * the same fix as libcURL for determining if a URL is vulnerable to an injection attack.
753 *
754 * \param url The URL to check for vulnerability
755 * \retval 0 The URL is not vulnerable
756 * \retval 1 The URL is vulnerable.
757 */
758static int url_is_vulnerable(const char *url)
759{
760 if (strpbrk(url, "\r\n")) {
761 return 1;
762 }
763
764 return 0;
765}
766
767struct curl_args {
768 const char *url;
769 const char *postdata;
771};
772
773static int acf_curl_helper(struct ast_channel *chan, struct curl_args *args)
774{
775 struct ast_str *escapebuf = ast_str_thread_get(&thread_escapebuf, 16);
776 int ret = 0;
777 long http_code = 0; /* read curl response */
778 size_t i;
779 struct ast_vector_int hasfailurecode = { NULL };
780 char *failurecodestrings,*found;
781 CURL **curl;
782 struct curl_settings *cur;
783 struct curl_slist *headers = NULL;
784 struct ast_datastore *store = NULL;
785 int hashcompat = 0;
787 char curl_errbuf[CURL_ERROR_SIZE + 1]; /* add one to be safe */
788
789 if (!escapebuf) {
790 return -1;
791 }
792
793 if (!(curl = ast_threadstorage_get(&curl_instance, sizeof(*curl)))) {
794 ast_log(LOG_ERROR, "Cannot allocate curl structure\n");
795 return -1;
796 }
797
798 if (url_is_vulnerable(args->url)) {
799 ast_log(LOG_ERROR, "URL '%s' is vulnerable to HTTP injection attacks. Aborting CURL() call.\n", args->url);
800 return -1;
801 }
802
803 if (chan) {
805 }
806
807 AST_VECTOR_INIT(&hasfailurecode, 0); /*Initialize vector*/
810 if (cur->key == CURLOPT_SPECIAL_HASHCOMPAT) {
811 hashcompat = (long) cur->value;
812 } else if (cur->key == CURLOPT_HTTPHEADER) {
813 headers = curl_slist_append(headers, (char*) cur->value);
814 } else if (cur->key == CURLOPT_SPECIAL_FAILURE_CODE) {
815 failurecodestrings = (char*) cur->value;
816 while( (found = strsep(&failurecodestrings, ",")) != NULL) {
817 AST_VECTOR_APPEND(&hasfailurecode, atoi(found));
818 }
819 } else {
820 curl_easy_setopt(*curl, cur->key, cur->value);
821 }
822 }
824
825 if (chan) {
826 ast_channel_lock(chan);
828 ast_channel_unlock(chan);
829 if (store) {
830 list = store->data;
831 AST_LIST_LOCK(list);
832 AST_LIST_TRAVERSE(list, cur, list) {
833 if (cur->key == CURLOPT_SPECIAL_HASHCOMPAT) {
834 hashcompat = (long) cur->value;
835 } else if (cur->key == CURLOPT_HTTPHEADER) {
836 headers = curl_slist_append(headers, (char*) cur->value);
837 } else if (cur->key == CURLOPT_SPECIAL_FAILURE_CODE) {
838 failurecodestrings = (char*) cur->value;
839 while( (found = strsep(&failurecodestrings, ",")) != NULL) {
840 AST_VECTOR_APPEND(&hasfailurecode, atoi(found));
841 }
842 } else {
843 curl_easy_setopt(*curl, cur->key, cur->value);
844 }
845 }
846 }
847 }
848
849 curl_easy_setopt(*curl, CURLOPT_URL, args->url);
850 curl_easy_setopt(*curl, CURLOPT_FILE, (void *) &args->cb_data);
851
852 if (args->postdata) {
853 curl_easy_setopt(*curl, CURLOPT_POST, 1);
854 curl_easy_setopt(*curl, CURLOPT_POSTFIELDS, args->postdata);
855 }
856
857 /* Always assign the headers - even when NULL - in case we had
858 * custom headers the last time we used this shared cURL
859 * instance */
860 curl_easy_setopt(*curl, CURLOPT_HTTPHEADER, headers);
861
862 /* Temporarily assign a buffer for curl to write errors to. */
863 curl_errbuf[0] = curl_errbuf[CURL_ERROR_SIZE] = '\0';
864 curl_easy_setopt(*curl, CURLOPT_ERRORBUFFER, curl_errbuf);
865
866 if (curl_easy_perform(*curl) != 0) {
867 ast_log(LOG_WARNING, "%s ('%s')\n", curl_errbuf, args->url);
868 }
869
870 /* Reset buffer to NULL so curl doesn't try to write to it when the
871 * buffer is deallocated. Documentation is vague about allowing NULL
872 * here, but the source allows it. See: "typecheck: allow NULL to unset
873 * CURLOPT_ERRORBUFFER" (62bcf005f4678a93158358265ba905bace33b834). */
874 curl_easy_setopt(*curl, CURLOPT_ERRORBUFFER, (char*)NULL);
875 curl_easy_getinfo (*curl, CURLINFO_RESPONSE_CODE, &http_code);
876
877 for (i = 0; i < AST_VECTOR_SIZE(&hasfailurecode); ++i) {
878 if (http_code == AST_VECTOR_GET(&hasfailurecode,i)){
879 ast_log(LOG_NOTICE, "%s%sCURL '%s' returned response code (%ld).\n",
880 chan ? ast_channel_name(chan) : "",
881 chan ? ast_channel_name(chan) : ": ",
882 args->url,
883 http_code);
884 ret=-1;
885 break;
886 }
887 }
888 AST_VECTOR_FREE(&hasfailurecode); /* Release the vector*/
889
890 if (store) {
891 AST_LIST_UNLOCK(list);
892 }
893 curl_slist_free_all(headers);
894
895 if (args->postdata) {
896 curl_easy_setopt(*curl, CURLOPT_POST, 0);
897 }
898
899 if (args->cb_data.str && ast_str_strlen(args->cb_data.str)) {
900 ast_str_trim_blanks(args->cb_data.str);
901
902 ast_debug(3, "CURL returned str='%s'\n", ast_str_buffer(args->cb_data.str));
903 if (hashcompat) {
904 char *remainder = ast_str_buffer(args->cb_data.str);
905 char *piece;
906 struct ast_str *fields = ast_str_create(ast_str_strlen(args->cb_data.str) / 2);
907 struct ast_str *values = ast_str_create(ast_str_strlen(args->cb_data.str) / 2);
908 int rowcount = 0;
909 while (fields && values && (piece = strsep(&remainder, "&"))) {
910 char *name = strsep(&piece, "=");
912 if (piece) {
913 ast_uri_decode(piece, mode);
914 }
915 ast_uri_decode(name, mode);
916 ast_str_append(&fields, 0, "%s%s", rowcount ? "," : "", ast_str_set_escapecommas(&escapebuf, 0, name, INT_MAX));
917 ast_str_append(&values, 0, "%s%s", rowcount ? "," : "", ast_str_set_escapecommas(&escapebuf, 0, S_OR(piece, ""), INT_MAX));
918 rowcount++;
919 }
920 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(fields));
921 ast_str_set(&args->cb_data.str, 0, "%s", ast_str_buffer(values));
922 ast_free(fields);
924 }
925 }
926
927 if (chan) {
929 }
930
931 return ret;
932}
933
934static int acf_curl_exec(struct ast_channel *chan, const char *cmd, char *info, struct ast_str **buf, ssize_t len)
935{
936 struct curl_args curl_params = { 0, };
937 int res;
938
942 );
943
945
946 if (ast_strlen_zero(info)) {
947 ast_log(LOG_WARNING, "CURL requires an argument (URL)\n");
948 return -1;
949 }
950
951 curl_params.url = args.url;
952 curl_params.postdata = args.postdata;
953 curl_params.cb_data.str = ast_str_create(16);
954 if (!curl_params.cb_data.str) {
955 return -1;
956 }
957
958 res = acf_curl_helper(chan, &curl_params);
959 ast_str_set(buf, len, "%s", ast_str_buffer(curl_params.cb_data.str));
960 ast_free(curl_params.cb_data.str);
961
962 return res;
963}
964
965static int acf_curl_write(struct ast_channel *chan, const char *cmd, char *name, const char *value)
966{
967 struct curl_args curl_params = { 0, };
968 int res;
969 char *args_value = ast_strdupa(value);
971 AST_APP_ARG(file_path);
972 );
973
974 AST_STANDARD_APP_ARGS(args, args_value);
975
976 if (ast_strlen_zero(name)) {
977 ast_log(LOG_WARNING, "CURL requires an argument (URL)\n");
978 return -1;
979 }
980
981 if (ast_strlen_zero(args.file_path)) {
982 ast_log(LOG_WARNING, "CURL requires a file to write\n");
983 return -1;
984 }
985
986 curl_params.url = name;
987 curl_params.cb_data.out_file = fopen(args.file_path, "w");
988 if (!curl_params.cb_data.out_file) {
989 ast_log(LOG_WARNING, "Failed to open file %s: %s (%d)\n",
990 args.file_path,
991 strerror(errno),
992 errno);
993 return -1;
994 }
995
996 res = acf_curl_helper(chan, &curl_params);
997
998 fclose(curl_params.cb_data.out_file);
999
1000 return res;
1001}
1002
1003static struct ast_custom_function acf_curl = {
1004 .name = "CURL",
1005 .read2 = acf_curl_exec,
1006 .write = acf_curl_write,
1007};
1008
1009static struct ast_custom_function acf_curlopt = {
1010 .name = "CURLOPT",
1011 .read = acf_curlopt_read,
1012 .read2 = acf_curlopt_read2,
1013 .write = acf_curlopt_write,
1014};
1015
1016#ifdef TEST_FRAMEWORK
1017AST_TEST_DEFINE(vulnerable_url)
1018{
1019 const char *bad_urls [] = {
1020 "http://example.com\r\nDELETE http://example.com/everything",
1021 "http://example.com\rDELETE http://example.com/everything",
1022 "http://example.com\nDELETE http://example.com/everything",
1023 "\r\nhttp://example.com",
1024 "\rhttp://example.com",
1025 "\nhttp://example.com",
1026 "http://example.com\r\n",
1027 "http://example.com\r",
1028 "http://example.com\n",
1029 };
1030 const char *good_urls [] = {
1031 "http://example.com",
1032 "http://example.com/%5Cr%5Cn",
1033 };
1034 int i;
1036
1037 switch (cmd) {
1038 case TEST_INIT:
1039 info->name = "vulnerable_url";
1040 info->category = "/funcs/func_curl/";
1041 info->summary = "cURL vulnerable URL test";
1042 info->description =
1043 "Ensure that any combination of '\\r' or '\\n' in a URL invalidates the URL";
1044 case TEST_EXECUTE:
1045 break;
1046 }
1047
1048 for (i = 0; i < ARRAY_LEN(bad_urls); ++i) {
1049 if (!url_is_vulnerable(bad_urls[i])) {
1050 ast_test_status_update(test, "String '%s' detected as valid when it should be invalid\n", bad_urls[i]);
1051 res = AST_TEST_FAIL;
1052 }
1053 }
1054
1055 for (i = 0; i < ARRAY_LEN(good_urls); ++i) {
1056 if (url_is_vulnerable(good_urls[i])) {
1057 ast_test_status_update(test, "String '%s' detected as invalid when it should be valid\n", good_urls[i]);
1058 res = AST_TEST_FAIL;
1059 }
1060 }
1061
1062 return res;
1063}
1064#endif
1065
1066static int unload_module(void)
1067{
1068 int res;
1069
1072
1073 AST_TEST_UNREGISTER(vulnerable_url);
1074
1075 return res;
1076}
1077
1078static int load_module(void)
1079{
1080 int res;
1081
1084
1085 AST_TEST_REGISTER(vulnerable_url);
1086
1087 return res;
1088}
1089
1091 .support_level = AST_MODULE_SUPPORT_CORE,
1092 .load = load_module,
1093 .unload = unload_module,
1094 .load_pri = AST_MODPRI_REALTIME_DEPEND2,
1095 .requires = "res_curl",
1096);
char * strsep(char **str, const char *delims)
#define AST_CURL_USER_AGENT
Definition asterisk.h:44
#define ast_free(a)
Definition astmm.h:180
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition astmm.h:298
#define ast_calloc(num, len)
A wrapper for calloc()
Definition astmm.h:202
#define ast_log
Definition astobj2.c:42
const char * ast_channel_name(const struct ast_channel *chan)
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition channel.c:2375
#define ast_channel_lock(chan)
Definition channel.h:2982
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
#define ast_channel_unlock(chan)
Definition channel.h:2983
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition channel.c:2389
#define ast_datastore_alloc(info, uid)
Definition datastore.h:85
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition datastore.c:68
char buf[BUFSIZE]
Definition eagi_proxy.c:66
static const char name[]
Definition format_mp3.c:68
#define CURLOPT_SPECIAL_HASHCOMPAT
Definition func_curl.c:265
static int acf_curl_helper(struct ast_channel *chan, struct curl_args *args)
Definition func_curl.c:774
static int acf_curlopt_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
Definition func_curl.c:686
static void curl_instance_cleanup(void *data)
Definition func_curl.c:731
static int acf_curl_exec(struct ast_channel *chan, const char *cmd, char *info, struct ast_str **buf, ssize_t len)
Definition func_curl.c:935
static const struct ast_datastore_info curl_info
Definition func_curl.c:271
static int acf_curlopt_write(struct ast_channel *chan, const char *cmd, char *name, const char *value)
Definition func_curl.c:417
static int curl_instance_init(void *data)
Definition func_curl.c:716
static int acf_curlopt_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition func_curl.c:681
#define CURLOPT_SPECIAL_FAILURE_CODE
Definition func_curl.c:267
static void curlds_free(void *data)
Definition func_curl.c:284
static int url_is_vulnerable(const char *url)
Check for potential HTTP injection risk.
Definition func_curl.c:759
optiontype
Definition func_curl.c:298
@ OT_BOOLEAN
Definition func_curl.c:299
@ OT_ENUM
Definition func_curl.c:303
@ OT_STRING
Definition func_curl.c:302
@ OT_INTEGER
Definition func_curl.c:300
@ OT_INTEGER_MS
Definition func_curl.c:301
static int acf_curlopt_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, struct ast_str **bufstr, ssize_t len)
Definition func_curl.c:566
static int acf_curl_write(struct ast_channel *chan, const char *cmd, char *name, const char *value)
Definition func_curl.c:966
static struct ast_custom_function acf_curlopt
Definition func_curl.c:1010
static int load_module(void)
Definition func_curl.c:1079
static int unload_module(void)
Definition func_curl.c:1067
static struct ast_custom_function acf_curl
Definition func_curl.c:1004
static int parse_curlopt_key(const char *name, CURLoption *key, enum optiontype *ot)
Definition func_curl.c:312
hashcompat
Definition func_curl.c:306
@ HASHCOMPAT_NO
Definition func_curl.c:307
@ HASHCOMPAT_LEGACY
Definition func_curl.c:309
@ HASHCOMPAT_YES
Definition func_curl.c:308
static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
Definition func_curl.c:701
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
#define AST_APP_ARG(name)
Define an application argument.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define LOG_NOTICE
#define LOG_WARNING
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
#define AST_LIST_HEAD_DESTROY(head)
Destroys a list head structure.
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
#define AST_LIST_HEAD_INIT(head)
Initializes a list head structure.
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
#define AST_LIST_LOCK(head)
Locks a list.
Definition linkedlists.h:40
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
#define AST_LIST_HEAD(name, type)
Defines a structure to be used to hold a list of specified type.
int errno
@ AST_MODFLAG_LOAD_ORDER
Definition module.h:331
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition module.h:557
@ AST_MODPRI_REALTIME_DEPEND2
Definition module.h:336
@ AST_MODULE_SUPPORT_CORE
Definition module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition module.h:46
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name.
#define ast_custom_function_register_escalating(acf, escalation)
Register a custom function which requires escalated privileges.
Definition pbx.h:1571
#define ast_custom_function_register(acf)
Register a custom function.
Definition pbx.h:1562
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
@ AST_CFE_WRITE
Definition pbx.h:1555
static char url[512]
static struct @519 args
#define NULL
Definition resample.c:96
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition strings.h:1139
size_t attribute_pure ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition strings.h:730
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition strings.h:80
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition utils.c:2235
char * ast_str_set_escapecommas(struct ast_str **buf, ssize_t maxlen, const char *src, size_t maxsrc)
Set a dynamic string to a non-NULL terminated substring, with escaping of commas.
Definition strings.h:1069
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition strings.h:65
char * ast_str_append_substr(struct ast_str **buf, ssize_t maxlen, const char *src, size_t maxsrc)
Append a non-NULL terminated substring to the end of a dynamic string.
Definition strings.h:1062
void ast_str_trim_blanks(struct ast_str *buf)
Trims trailing whitespace characters from an ast_str string.
Definition strings.h:719
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition strings.h:659
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition strings.h:1113
char *attribute_pure ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition strings.h:761
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition strings.h:425
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition strings.h:909
Main Channel structure associated with a channel.
Data structure associated with a custom dialplan function.
Definition pbx.h:118
const char * name
Definition pbx.h:119
Structure for a data store object.
Definition datastore.h:64
void * data
Definition datastore.h:66
Structure used to handle boolean flags.
Definition utils.h:220
Support for dynamic strings.
Definition strings.h:623
Integer vector definition.
Definition vector.h:52
const char * postdata
Definition func_curl.c:770
struct curl_write_callback_data cb_data
Definition func_curl.c:771
const char * url
Definition func_curl.c:769
struct curl_settings::@178 list
CURLoption key
Definition func_curl.c:278
Callback data passed to WriteMemoryCallback.
Definition func_curl.c:692
struct ast_str * str
If a string is being built, the string buffer.
Definition func_curl.c:694
ssize_t len
The max size of str.
Definition func_curl.c:696
FILE * out_file
If a file is being retrieved, the file to write to.
Definition func_curl.c:698
int value
Definition syslog.c:37
@ TEST_INIT
Definition test.h:200
@ TEST_EXECUTE
Definition test.h:201
#define AST_TEST_REGISTER(cb)
Definition test.h:127
#define ast_test_status_update(a, b, c...)
Definition test.h:129
#define AST_TEST_UNREGISTER(cb)
Definition test.h:128
#define AST_TEST_DEFINE(hdr)
Definition test.h:126
ast_test_result_state
Definition test.h:193
@ AST_TEST_PASS
Definition test.h:195
@ AST_TEST_FAIL
Definition test.h:196
#define AST_THREADSTORAGE_CUSTOM(a, b, c)
Define a thread storage variable, with custom initialization and cleanup.
void * ast_threadstorage_get(struct ast_threadstorage *ts, size_t init_size)
Retrieve thread storage.
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
const struct ast_flags ast_uri_http
Definition utils.c:719
#define ARRAY_LEN(a)
Definition utils.h:706
void ast_uri_decode(char *s, struct ast_flags spec)
Decode URI, URN, URL (overwrite string)
Definition utils.c:762
const struct ast_flags ast_uri_http_legacy
Definition utils.c:720
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition vector.h:620
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition vector.h:185
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition vector.h:124
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition vector.h:267
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition vector.h:691

Enumeration Type Documentation

◆ hashcompat

enum hashcompat
Enumerator
HASHCOMPAT_NO 
HASHCOMPAT_YES 
HASHCOMPAT_LEGACY 

Definition at line 306 of file func_curl.c.

306 {
307 HASHCOMPAT_NO = 0,
310};

◆ optiontype

enum optiontype
Enumerator
OT_BOOLEAN 
OT_INTEGER 
OT_INTEGER_MS 
OT_STRING 
OT_ENUM 

Definition at line 298 of file func_curl.c.

298 {
302 OT_STRING,
303 OT_ENUM,
304};

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 1097 of file func_curl.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 1097 of file func_curl.c.

◆ acf_curl_exec()

static int acf_curl_exec ( struct ast_channel chan,
const char *  cmd,
char *  info,
struct ast_str **  buf,
ssize_t  len 
)
static

Definition at line 935 of file func_curl.c.

936{
937 struct curl_args curl_params = { 0, };
938 int res;
939
943 );
944
946
947 if (ast_strlen_zero(info)) {
948 ast_log(LOG_WARNING, "CURL requires an argument (URL)\n");
949 return -1;
950 }
951
952 curl_params.url = args.url;
953 curl_params.postdata = args.postdata;
954 curl_params.cb_data.str = ast_str_create(16);
955 if (!curl_params.cb_data.str) {
956 return -1;
957 }
958
959 res = acf_curl_helper(chan, &curl_params);
960 ast_str_set(buf, len, "%s", ast_str_buffer(curl_params.cb_data.str));
961 ast_free(curl_params.cb_data.str);
962
963 return res;
964}

References acf_curl_helper(), args, AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_free, ast_log, AST_STANDARD_APP_ARGS, ast_str_buffer(), ast_str_create, ast_str_set(), ast_strlen_zero(), buf, curl_args::cb_data, len(), LOG_WARNING, curl_args::postdata, curl_write_callback_data::str, curl_args::url, and url.

◆ acf_curl_helper()

static int acf_curl_helper ( struct ast_channel chan,
struct curl_args args 
)
static

Definition at line 774 of file func_curl.c.

775{
776 struct ast_str *escapebuf = ast_str_thread_get(&thread_escapebuf, 16);
777 int ret = 0;
778 long http_code = 0; /* read curl response */
779 size_t i;
780 struct ast_vector_int hasfailurecode = { NULL };
781 char *failurecodestrings,*found;
782 CURL **curl;
783 struct curl_settings *cur;
784 struct curl_slist *headers = NULL;
785 struct ast_datastore *store = NULL;
786 int hashcompat = 0;
788 char curl_errbuf[CURL_ERROR_SIZE + 1]; /* add one to be safe */
789
790 if (!escapebuf) {
791 return -1;
792 }
793
794 if (!(curl = ast_threadstorage_get(&curl_instance, sizeof(*curl)))) {
795 ast_log(LOG_ERROR, "Cannot allocate curl structure\n");
796 return -1;
797 }
798
799 if (url_is_vulnerable(args->url)) {
800 ast_log(LOG_ERROR, "URL '%s' is vulnerable to HTTP injection attacks. Aborting CURL() call.\n", args->url);
801 return -1;
802 }
803
804 if (chan) {
806 }
807
808 AST_VECTOR_INIT(&hasfailurecode, 0); /*Initialize vector*/
811 if (cur->key == CURLOPT_SPECIAL_HASHCOMPAT) {
812 hashcompat = (long) cur->value;
813 } else if (cur->key == CURLOPT_HTTPHEADER) {
814 headers = curl_slist_append(headers, (char*) cur->value);
815 } else if (cur->key == CURLOPT_SPECIAL_FAILURE_CODE) {
816 failurecodestrings = (char*) cur->value;
817 while( (found = strsep(&failurecodestrings, ",")) != NULL) {
818 AST_VECTOR_APPEND(&hasfailurecode, atoi(found));
819 }
820 } else {
821 curl_easy_setopt(*curl, cur->key, cur->value);
822 }
823 }
825
826 if (chan) {
827 ast_channel_lock(chan);
829 ast_channel_unlock(chan);
830 if (store) {
831 list = store->data;
832 AST_LIST_LOCK(list);
833 AST_LIST_TRAVERSE(list, cur, list) {
834 if (cur->key == CURLOPT_SPECIAL_HASHCOMPAT) {
835 hashcompat = (long) cur->value;
836 } else if (cur->key == CURLOPT_HTTPHEADER) {
837 headers = curl_slist_append(headers, (char*) cur->value);
838 } else if (cur->key == CURLOPT_SPECIAL_FAILURE_CODE) {
839 failurecodestrings = (char*) cur->value;
840 while( (found = strsep(&failurecodestrings, ",")) != NULL) {
841 AST_VECTOR_APPEND(&hasfailurecode, atoi(found));
842 }
843 } else {
844 curl_easy_setopt(*curl, cur->key, cur->value);
845 }
846 }
847 }
848 }
849
850 curl_easy_setopt(*curl, CURLOPT_URL, args->url);
851 curl_easy_setopt(*curl, CURLOPT_FILE, (void *) &args->cb_data);
852
853 if (args->postdata) {
854 curl_easy_setopt(*curl, CURLOPT_POST, 1);
855 curl_easy_setopt(*curl, CURLOPT_POSTFIELDS, args->postdata);
856 }
857
858 /* Always assign the headers - even when NULL - in case we had
859 * custom headers the last time we used this shared cURL
860 * instance */
861 curl_easy_setopt(*curl, CURLOPT_HTTPHEADER, headers);
862
863 /* Temporarily assign a buffer for curl to write errors to. */
864 curl_errbuf[0] = curl_errbuf[CURL_ERROR_SIZE] = '\0';
865 curl_easy_setopt(*curl, CURLOPT_ERRORBUFFER, curl_errbuf);
866
867 if (curl_easy_perform(*curl) != 0) {
868 ast_log(LOG_WARNING, "%s ('%s')\n", curl_errbuf, args->url);
869 }
870
871 /* Reset buffer to NULL so curl doesn't try to write to it when the
872 * buffer is deallocated. Documentation is vague about allowing NULL
873 * here, but the source allows it. See: "typecheck: allow NULL to unset
874 * CURLOPT_ERRORBUFFER" (62bcf005f4678a93158358265ba905bace33b834). */
875 curl_easy_setopt(*curl, CURLOPT_ERRORBUFFER, (char*)NULL);
876 curl_easy_getinfo (*curl, CURLINFO_RESPONSE_CODE, &http_code);
877
878 for (i = 0; i < AST_VECTOR_SIZE(&hasfailurecode); ++i) {
879 if (http_code == AST_VECTOR_GET(&hasfailurecode,i)){
880 ast_log(LOG_NOTICE, "%s%sCURL '%s' returned response code (%ld).\n",
881 chan ? ast_channel_name(chan) : "",
882 chan ? ast_channel_name(chan) : ": ",
883 args->url,
884 http_code);
885 ret=-1;
886 break;
887 }
888 }
889 AST_VECTOR_FREE(&hasfailurecode); /* Release the vector*/
890
891 if (store) {
892 AST_LIST_UNLOCK(list);
893 }
894 curl_slist_free_all(headers);
895
896 if (args->postdata) {
897 curl_easy_setopt(*curl, CURLOPT_POST, 0);
898 }
899
900 if (args->cb_data.str && ast_str_strlen(args->cb_data.str)) {
901 ast_str_trim_blanks(args->cb_data.str);
902
903 ast_debug(3, "CURL returned str='%s'\n", ast_str_buffer(args->cb_data.str));
904 if (hashcompat) {
905 char *remainder = ast_str_buffer(args->cb_data.str);
906 char *piece;
907 struct ast_str *fields = ast_str_create(ast_str_strlen(args->cb_data.str) / 2);
908 struct ast_str *values = ast_str_create(ast_str_strlen(args->cb_data.str) / 2);
909 int rowcount = 0;
910 while (fields && values && (piece = strsep(&remainder, "&"))) {
911 char *name = strsep(&piece, "=");
913 if (piece) {
914 ast_uri_decode(piece, mode);
915 }
916 ast_uri_decode(name, mode);
917 ast_str_append(&fields, 0, "%s%s", rowcount ? "," : "", ast_str_set_escapecommas(&escapebuf, 0, name, INT_MAX));
918 ast_str_append(&values, 0, "%s%s", rowcount ? "," : "", ast_str_set_escapecommas(&escapebuf, 0, S_OR(piece, ""), INT_MAX));
919 rowcount++;
920 }
921 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(fields));
922 ast_str_set(&args->cb_data.str, 0, "%s", ast_str_buffer(values));
923 ast_free(fields);
925 }
926 }
927
928 if (chan) {
930 }
931
932 return ret;
933}

References args, ast_autoservice_start(), ast_autoservice_stop(), ast_channel_datastore_find(), ast_channel_lock, ast_channel_name(), ast_channel_unlock, ast_debug, ast_free, AST_LIST_HEAD, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log, ast_str_append(), ast_str_buffer(), ast_str_create, ast_str_set(), ast_str_set_escapecommas(), ast_str_strlen(), ast_str_thread_get(), ast_str_trim_blanks(), ast_threadstorage_get(), ast_uri_decode(), ast_uri_http, ast_uri_http_legacy, AST_VECTOR_APPEND, AST_VECTOR_FREE, AST_VECTOR_GET, AST_VECTOR_INIT, AST_VECTOR_SIZE, curl_info, CURLOPT_SPECIAL_FAILURE_CODE, CURLOPT_SPECIAL_HASHCOMPAT, ast_datastore::data, HASHCOMPAT_LEGACY, curl_settings::key, LOG_ERROR, LOG_NOTICE, LOG_WARNING, name, NULL, pbx_builtin_setvar_helper(), S_OR, strsep(), url_is_vulnerable(), and curl_settings::value.

Referenced by acf_curl_exec(), and acf_curl_write().

◆ acf_curl_write()

static int acf_curl_write ( struct ast_channel chan,
const char *  cmd,
char *  name,
const char *  value 
)
static

Definition at line 966 of file func_curl.c.

967{
968 struct curl_args curl_params = { 0, };
969 int res;
970 char *args_value = ast_strdupa(value);
972 AST_APP_ARG(file_path);
973 );
974
975 AST_STANDARD_APP_ARGS(args, args_value);
976
977 if (ast_strlen_zero(name)) {
978 ast_log(LOG_WARNING, "CURL requires an argument (URL)\n");
979 return -1;
980 }
981
982 if (ast_strlen_zero(args.file_path)) {
983 ast_log(LOG_WARNING, "CURL requires a file to write\n");
984 return -1;
985 }
986
987 curl_params.url = name;
988 curl_params.cb_data.out_file = fopen(args.file_path, "w");
989 if (!curl_params.cb_data.out_file) {
990 ast_log(LOG_WARNING, "Failed to open file %s: %s (%d)\n",
991 args.file_path,
992 strerror(errno),
993 errno);
994 return -1;
995 }
996
997 res = acf_curl_helper(chan, &curl_params);
998
999 fclose(curl_params.cb_data.out_file);
1000
1001 return res;
1002}

References acf_curl_helper(), args, AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log, AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), curl_args::cb_data, errno, LOG_WARNING, name, curl_write_callback_data::out_file, curl_args::url, and value.

◆ acf_curlopt_helper()

static int acf_curlopt_helper ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
struct ast_str **  bufstr,
ssize_t  len 
)
static

Definition at line 566 of file func_curl.c.

567{
568 struct ast_datastore *store;
569 struct global_curl_info *list[2] = { &global_curl_info, NULL };
570 struct curl_settings *cur = NULL;
571 CURLoption key;
572 enum optiontype ot;
573 int i;
574
575 if (parse_curlopt_key(data, &key, &ot)) {
576 ast_log(LOG_ERROR, "Unrecognized option: '%s'\n", data);
577 return -1;
578 }
579
580 if (chan) {
581 /* If we have a channel, we want to read the options set there before
582 falling back to the global settings */
583 ast_channel_lock(chan);
585 ast_channel_unlock(chan);
586
587 if (store) {
588 list[0] = store->data;
590 }
591 }
592
593 for (i = 0; i < 2; i++) {
594 if (!list[i]) {
595 break;
596 }
598 AST_LIST_TRAVERSE(list[i], cur, list) {
599 if (cur->key == key) {
600 if (ot == OT_BOOLEAN || ot == OT_INTEGER) {
601 if (buf) {
602 snprintf(buf, len, "%ld", (long) cur->value);
603 } else {
604 ast_str_set(bufstr, len, "%ld", (long) cur->value);
605 }
606 } else if (ot == OT_INTEGER_MS) {
607 if ((long) cur->value % 1000 == 0) {
608 if (buf) {
609 snprintf(buf, len, "%ld", (long)cur->value / 1000);
610 } else {
611 ast_str_set(bufstr, len, "%ld", (long) cur->value / 1000);
612 }
613 } else {
614 if (buf) {
615 snprintf(buf, len, "%.3f", (double) ((long) cur->value) / 1000.0);
616 } else {
617 ast_str_set(bufstr, len, "%.3f", (double) ((long) cur->value) / 1000.0);
618 }
619 }
620 } else if (ot == OT_STRING) {
621 ast_debug(1, "Found entry %p, with key %d and value %p\n", cur, cur->key, cur->value);
622 if (buf) {
624 } else {
625 ast_str_set(bufstr, 0, "%s", (char *) cur->value);
626 }
627 } else if (key == CURLOPT_PROXYTYPE) {
628 const char *strval = "unknown";
629 if (0) {
630#if CURLVERSION_ATLEAST(7,15,2)
631 } else if ((long)cur->value == CURLPROXY_SOCKS4) {
632 strval = "socks4";
633#endif
634#if CURLVERSION_ATLEAST(7,18,0)
635 } else if ((long)cur->value == CURLPROXY_SOCKS4A) {
636 strval = "socks4a";
637#endif
638 } else if ((long)cur->value == CURLPROXY_SOCKS5) {
639 strval = "socks5";
640#if CURLVERSION_ATLEAST(7,18,0)
641 } else if ((long)cur->value == CURLPROXY_SOCKS5_HOSTNAME) {
642 strval = "socks5hostname";
643#endif
644#if CURLVERSION_ATLEAST(7,10,0)
645 } else if ((long)cur->value == CURLPROXY_HTTP) {
646 strval = "http";
647#endif
648 }
649 if (buf) {
650 ast_copy_string(buf, strval, len);
651 } else {
652 ast_str_set(bufstr, 0, "%s", strval);
653 }
654 } else if (key == CURLOPT_SPECIAL_HASHCOMPAT) {
655 const char *strval = "unknown";
656 if ((long) cur->value == HASHCOMPAT_LEGACY) {
657 strval = "legacy";
658 } else if ((long) cur->value == HASHCOMPAT_YES) {
659 strval = "yes";
660 } else if ((long) cur->value == HASHCOMPAT_NO) {
661 strval = "no";
662 }
663 if (buf) {
664 ast_copy_string(buf, strval, len);
665 } else {
666 ast_str_set(bufstr, 0, "%s", strval);
667 }
668 }
669 break;
670 }
671 }
673 if (cur) {
674 break;
675 }
676 }
677
678 return cur ? 0 : -1;
679}

References ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, ast_copy_string(), ast_debug, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log, ast_str_set(), buf, curl_info, CURLOPT_SPECIAL_HASHCOMPAT, ast_datastore::data, HASHCOMPAT_LEGACY, HASHCOMPAT_NO, HASHCOMPAT_YES, curl_settings::key, len(), curl_settings::list, LOG_ERROR, NULL, OT_BOOLEAN, OT_INTEGER, OT_INTEGER_MS, OT_STRING, parse_curlopt_key(), and curl_settings::value.

Referenced by acf_curlopt_read(), and acf_curlopt_read2().

◆ acf_curlopt_read()

static int acf_curlopt_read ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
)
static

Definition at line 681 of file func_curl.c.

682{
683 return acf_curlopt_helper(chan, cmd, data, buf, NULL, len);
684}

References acf_curlopt_helper(), buf, len(), and NULL.

◆ acf_curlopt_read2()

static int acf_curlopt_read2 ( struct ast_channel chan,
const char *  cmd,
char *  data,
struct ast_str **  buf,
ssize_t  len 
)
static

Definition at line 686 of file func_curl.c.

687{
688 return acf_curlopt_helper(chan, cmd, data, NULL, buf, len);
689}

References acf_curlopt_helper(), buf, len(), and NULL.

◆ acf_curlopt_write()

static int acf_curlopt_write ( struct ast_channel chan,
const char *  cmd,
char *  name,
const char *  value 
)
static

Definition at line 417 of file func_curl.c.

418{
419 struct ast_datastore *store;
420 struct global_curl_info *list;
421 struct curl_settings *cur, *new = NULL;
422 CURLoption key;
423 enum optiontype ot;
424
425 if (chan) {
426 ast_channel_lock(chan);
427 if (!(store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
428 /* Create a new datastore */
429 if (!(store = ast_datastore_alloc(&curl_info, NULL))) {
430 ast_log(LOG_ERROR, "Unable to allocate new datastore. Cannot set any CURL options\n");
431 ast_channel_unlock(chan);
432 return -1;
433 }
434
435 if (!(list = ast_calloc(1, sizeof(*list)))) {
436 ast_log(LOG_ERROR, "Unable to allocate list head. Cannot set any CURL options\n");
437 ast_datastore_free(store);
438 ast_channel_unlock(chan);
439 return -1;
440 }
441
442 store->data = list;
444 ast_channel_datastore_add(chan, store);
445 } else {
446 list = store->data;
447 }
448 ast_channel_unlock(chan);
449 } else {
450 /* Populate the global structure */
452 }
453
454 if (!parse_curlopt_key(name, &key, &ot)) {
455 if (ot == OT_BOOLEAN) {
456 if ((new = ast_calloc(1, sizeof(*new)))) {
457 new->value = (void *)((long) ast_true(value));
458 }
459 } else if (ot == OT_INTEGER) {
460 long tmp = atol(value);
461 if ((new = ast_calloc(1, sizeof(*new)))) {
462 new->value = (void *)tmp;
463 }
464 } else if (ot == OT_INTEGER_MS) {
465 long tmp = atof(value) * 1000.0;
466 if ((new = ast_calloc(1, sizeof(*new)))) {
467 new->value = (void *)tmp;
468 }
469 } else if (ot == OT_STRING) {
470 if ((new = ast_calloc(1, sizeof(*new) + strlen(value) + 1))) {
471 new->value = (char *)new + sizeof(*new);
472 strcpy(new->value, value);
473 }
474 } else if (ot == OT_ENUM) {
475 if (key == CURLOPT_PROXYTYPE) {
476 long ptype =
477#if CURLVERSION_ATLEAST(7,10,0)
478 CURLPROXY_HTTP;
479#else
480 CURLPROXY_SOCKS5;
481#endif
482 if (0) {
483#if CURLVERSION_ATLEAST(7,15,2)
484 } else if (!strcasecmp(value, "socks4")) {
485 ptype = CURLPROXY_SOCKS4;
486#endif
487#if CURLVERSION_ATLEAST(7,18,0)
488 } else if (!strcasecmp(value, "socks4a")) {
489 ptype = CURLPROXY_SOCKS4A;
490#endif
491#if CURLVERSION_ATLEAST(7,18,0)
492 } else if (!strcasecmp(value, "socks5")) {
493 ptype = CURLPROXY_SOCKS5;
494#endif
495#if CURLVERSION_ATLEAST(7,18,0)
496 } else if (!strncasecmp(value, "socks5", 6)) {
497 ptype = CURLPROXY_SOCKS5_HOSTNAME;
498#endif
499 }
500
501 if ((new = ast_calloc(1, sizeof(*new)))) {
502 new->value = (void *)ptype;
503 }
504 } else if (key == CURLOPT_HTTPAUTH) {
505 long authtype = 0;
506 char *authmethod, *authstr = ast_strdupa(value);
507 while ((authmethod = strsep(&authstr, ","))) {
508 if (!strcasecmp(authmethod, "basic")) {
509 authtype |= CURLAUTH_BASIC;
510 } else if (!strcasecmp(authmethod, "digest")) {
511 authtype |= CURLAUTH_DIGEST;
512 } else {
513 ast_log(LOG_WARNING, "Auth method '%s' invalid or not supported\n", authmethod);
514 return -1;
515 }
516 }
517 if (!authmethod) {
518 ast_log(LOG_WARNING, "Auth method '%s' invalid or not supported\n", value);
519 }
520 if ((new = ast_calloc(1, sizeof(*new)))) {
521 new->value = (void *)authtype;
522 }
523 } else if (key == CURLOPT_SPECIAL_HASHCOMPAT) {
524 if ((new = ast_calloc(1, sizeof(*new)))) {
525 new->value = (void *) (long) (!strcasecmp(value, "legacy") ? HASHCOMPAT_LEGACY : ast_true(value) ? HASHCOMPAT_YES : HASHCOMPAT_NO);
526 }
527 } else {
528 /* Highly unlikely */
529 goto yuck;
530 }
531 }
532
533 /* Memory allocation error */
534 if (!new) {
535 return -1;
536 }
537
538 new->key = key;
539 } else {
540yuck:
541 ast_log(LOG_ERROR, "Unrecognized option: %s\n", name);
542 return -1;
543 }
544
545 /* Remove any existing entry, only http headers are left */
547 if (new->key != CURLOPT_HTTPHEADER) {
549 if (cur->key == new->key) {
551 ast_free(cur);
552 break;
553 }
554 }
556 }
557
558 /* Insert new entry */
559 ast_debug(1, "Inserting entry %p with key %d and value %p\n", new, new->key, new->value);
562
563 return 0;
564}

References ast_calloc, ast_channel_datastore_add(), ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, ast_datastore_alloc, ast_datastore_free(), ast_debug, ast_free, AST_LIST_HEAD_INIT, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, AST_LIST_UNLOCK, ast_log, ast_strdupa, ast_true(), curl_info, CURLOPT_SPECIAL_HASHCOMPAT, ast_datastore::data, HASHCOMPAT_LEGACY, HASHCOMPAT_NO, HASHCOMPAT_YES, curl_settings::key, curl_settings::list, LOG_ERROR, LOG_WARNING, name, NULL, OT_BOOLEAN, OT_ENUM, OT_INTEGER, OT_INTEGER_MS, OT_STRING, parse_curlopt_key(), strsep(), and value.

◆ AST_MODULE_SELF_SYM()

struct ast_module * AST_MODULE_SELF_SYM ( void  )

Definition at line 1097 of file func_curl.c.

◆ AST_THREADSTORAGE_CUSTOM_SCOPE() [1/2]

AST_THREADSTORAGE_CUSTOM_SCOPE ( curl_instance  ,
curl_instance_init  ,
curl_instance_cleanup  ,
static   
)

◆ AST_THREADSTORAGE_CUSTOM_SCOPE() [2/2]

AST_THREADSTORAGE_CUSTOM_SCOPE ( thread_escapebuf  ,
NULL  ,
ast_free_ptr  ,
static   
)

◆ curl_instance_cleanup()

static void curl_instance_cleanup ( void *  data)
static

Definition at line 731 of file func_curl.c.

732{
733 CURL **curl = data;
734
735 curl_easy_cleanup(*curl);
736
737 ast_free(data);
738}

References ast_free.

◆ curl_instance_init()

static int curl_instance_init ( void *  data)
static

Definition at line 716 of file func_curl.c.

717{
718 CURL **curl = data;
719
720 if (!(*curl = curl_easy_init()))
721 return -1;
722
723 curl_easy_setopt(*curl, CURLOPT_NOSIGNAL, 1);
724 curl_easy_setopt(*curl, CURLOPT_TIMEOUT, 180);
725 curl_easy_setopt(*curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
726 curl_easy_setopt(*curl, CURLOPT_USERAGENT, AST_CURL_USER_AGENT);
727
728 return 0;
729}

References AST_CURL_USER_AGENT, and WriteMemoryCallback().

◆ curlds_free()

static void curlds_free ( void *  data)
static

Definition at line 284 of file func_curl.c.

285{
287 struct curl_settings *setting;
288 if (!list) {
289 return;
290 }
291 while ((setting = AST_LIST_REMOVE_HEAD(list, list))) {
292 ast_free(setting);
293 }
295 ast_free(list);
296}

References ast_free, AST_LIST_HEAD, AST_LIST_HEAD_DESTROY, AST_LIST_REMOVE_HEAD, and curl_settings::list.

◆ load_module()

static int load_module ( void  )
static

Definition at line 1079 of file func_curl.c.

1080{
1081 int res;
1082
1085
1086 AST_TEST_REGISTER(vulnerable_url);
1087
1088 return res;
1089}

References acf_curl, acf_curlopt, AST_CFE_WRITE, ast_custom_function_register, ast_custom_function_register_escalating, and AST_TEST_REGISTER.

◆ parse_curlopt_key()

static int parse_curlopt_key ( const char *  name,
CURLoption *  key,
enum optiontype ot 
)
static

Definition at line 312 of file func_curl.c.

313{
314 if (!strcasecmp(name, "header")) {
315 *key = CURLOPT_HEADER;
316 *ot = OT_BOOLEAN;
317 } else if (!strcasecmp(name, "httpheader")) {
318 *key = CURLOPT_HTTPHEADER;
319 *ot = OT_STRING;
320 } else if (!strcasecmp(name, "httpauth")) {
321 *key = CURLOPT_HTTPAUTH;
322 *ot = OT_ENUM;
323 } else if (!strcasecmp(name, "proxy")) {
324 *key = CURLOPT_PROXY;
325 *ot = OT_STRING;
326 } else if (!strcasecmp(name, "proxyport")) {
327 *key = CURLOPT_PROXYPORT;
328 *ot = OT_INTEGER;
329 } else if (!strcasecmp(name, "proxytype")) {
330 *key = CURLOPT_PROXYTYPE;
331 *ot = OT_ENUM;
332 } else if (!strcasecmp(name, "dnstimeout")) {
333 *key = CURLOPT_DNS_CACHE_TIMEOUT;
334 *ot = OT_INTEGER;
335 } else if (!strcasecmp(name, "userpwd")) {
336 *key = CURLOPT_USERPWD;
337 *ot = OT_STRING;
338 } else if (!strcasecmp(name, "proxyuserpwd")) {
339 *key = CURLOPT_PROXYUSERPWD;
340 *ot = OT_STRING;
341 } else if (!strcasecmp(name, "followlocation")) {
342 *key = CURLOPT_FOLLOWLOCATION;
343 *ot = OT_BOOLEAN;
344 } else if (!strcasecmp(name, "maxredirs")) {
345 *key = CURLOPT_MAXREDIRS;
346 *ot = OT_INTEGER;
347 } else if (!strcasecmp(name, "referer")) {
348 *key = CURLOPT_REFERER;
349 *ot = OT_STRING;
350 } else if (!strcasecmp(name, "useragent")) {
351 *key = CURLOPT_USERAGENT;
352 *ot = OT_STRING;
353 } else if (!strcasecmp(name, "cookie")) {
354 *key = CURLOPT_COOKIE;
355 *ot = OT_STRING;
356 } else if (!strcasecmp(name, "ftptimeout")) {
357 *key = CURLOPT_FTP_RESPONSE_TIMEOUT;
358 *ot = OT_INTEGER;
359 } else if (!strcasecmp(name, "httptimeout")) {
360#if CURLVERSION_ATLEAST(7,16,2)
361 *key = CURLOPT_TIMEOUT_MS;
362 *ot = OT_INTEGER_MS;
363#else
364 *key = CURLOPT_TIMEOUT;
365 *ot = OT_INTEGER;
366#endif
367 } else if (!strcasecmp(name, "conntimeout")) {
368#if CURLVERSION_ATLEAST(7,16,2)
369 *key = CURLOPT_CONNECTTIMEOUT_MS;
370 *ot = OT_INTEGER_MS;
371#else
372 *key = CURLOPT_CONNECTTIMEOUT;
373 *ot = OT_INTEGER;
374#endif
375 } else if (!strcasecmp(name, "ftptext")) {
376 *key = CURLOPT_TRANSFERTEXT;
377 *ot = OT_BOOLEAN;
378 } else if (!strcasecmp(name, "ssl_verifypeer")) {
379 *key = CURLOPT_SSL_VERIFYPEER;
380 *ot = OT_BOOLEAN;
381 } else if (!strcasecmp(name, "ssl_verifyhost")) {
382 *key = CURLOPT_SSL_VERIFYHOST;
383 *ot = OT_INTEGER;
384 } else if (!strcasecmp(name, "ssl_cainfo")) {
385 *key = CURLOPT_CAINFO;
386 *ot = OT_STRING;
387 } else if (!strcasecmp(name, "ssl_capath")) {
388 *key = CURLOPT_CAPATH;
389 *ot = OT_STRING;
390 } else if (!strcasecmp(name, "ssl_cert")) {
391 *key = CURLOPT_SSLCERT;
392 *ot = OT_STRING;
393 } else if (!strcasecmp(name, "ssl_certtype")) {
394 *key = CURLOPT_SSLCERTTYPE;
395 *ot = OT_STRING;
396 } else if (!strcasecmp(name, "ssl_key")) {
397 *key = CURLOPT_SSLKEY;
398 *ot = OT_STRING;
399 } else if (!strcasecmp(name, "ssl_keytype")) {
400 *key = CURLOPT_SSLKEYTYPE;
401 *ot = OT_STRING;
402 } else if (!strcasecmp(name, "ssl_keypasswd")) {
403 *key = CURLOPT_KEYPASSWD;
404 *ot = OT_STRING;
405 } else if (!strcasecmp(name, "hashcompat")) {
407 *ot = OT_ENUM;
408 } else if (!strcasecmp(name, "failurecodes")) {
410 *ot = OT_STRING;
411 } else {
412 return -1;
413 }
414 return 0;
415}

References CURLOPT_SPECIAL_FAILURE_CODE, CURLOPT_SPECIAL_HASHCOMPAT, curl_settings::key, name, OT_BOOLEAN, OT_ENUM, OT_INTEGER, OT_INTEGER_MS, and OT_STRING.

Referenced by acf_curlopt_helper(), and acf_curlopt_write().

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 1067 of file func_curl.c.

1068{
1069 int res;
1070
1073
1074 AST_TEST_UNREGISTER(vulnerable_url);
1075
1076 return res;
1077}

References acf_curl, acf_curlopt, ast_custom_function_unregister(), and AST_TEST_UNREGISTER.

◆ url_is_vulnerable()

static int url_is_vulnerable ( const char *  url)
static

Check for potential HTTP injection risk.

CVE-2014-8150 brought up the fact that HTTP proxies are subject to injection attacks. An HTTP URL sent to a proxy contains a carriage-return linefeed combination, followed by a complete HTTP request. Proxies will handle this as two separate HTTP requests rather than as a malformed URL.

libcURL patched this vulnerability in version 7.40.0, but we have no guarantee that Asterisk systems will be using an up-to-date cURL library. Therefore, we implement the same fix as libcURL for determining if a URL is vulnerable to an injection attack.

Parameters
urlThe URL to check for vulnerability
Return values
0The URL is not vulnerable
1The URL is vulnerable.

Definition at line 759 of file func_curl.c.

760{
761 if (strpbrk(url, "\r\n")) {
762 return 1;
763 }
764
765 return 0;
766}

References url.

Referenced by acf_curl_helper().

◆ WriteMemoryCallback()

static size_t WriteMemoryCallback ( void *  ptr,
size_t  size,
size_t  nmemb,
void *  data 
)
static

Definition at line 701 of file func_curl.c.

702{
703 register int realsize = 0;
704 struct curl_write_callback_data *cb_data = data;
705
706 if (cb_data->str) {
707 realsize = size * nmemb;
708 ast_str_append_substr(&cb_data->str, 0, ptr, realsize);
709 } else if (cb_data->out_file) {
710 realsize = fwrite(ptr, size, nmemb, cb_data->out_file);
711 }
712
713 return realsize;
714}

References ast_str_append_substr(), curl_write_callback_data::out_file, and curl_write_callback_data::str.

Referenced by curl_instance_init().

Variable Documentation

◆ __mod_info

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Load external URL" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_REALTIME_DEPEND2, .requires = "res_curl", }
static

Definition at line 1097 of file func_curl.c.

◆ acf_curl

struct ast_custom_function acf_curl
static
Initial value:
= {
.name = "CURL",
.read2 = acf_curl_exec,
.write = acf_curl_write,
}

Definition at line 1004 of file func_curl.c.

1004 {
1005 .name = "CURL",
1006 .read2 = acf_curl_exec,
1007 .write = acf_curl_write,
1008};

Referenced by load_module(), and unload_module().

◆ acf_curlopt

struct ast_custom_function acf_curlopt
static

Definition at line 1010 of file func_curl.c.

1010 {
1011 .name = "CURLOPT",
1012 .read = acf_curlopt_read,
1013 .read2 = acf_curlopt_read2,
1014 .write = acf_curlopt_write,
1015};

Referenced by load_module(), and unload_module().

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 1097 of file func_curl.c.

◆ curl_info

const struct ast_datastore_info curl_info
static
Initial value:
= {
.type = "CURL",
.destroy = curlds_free,
}

Definition at line 271 of file func_curl.c.

271 {
272 .type = "CURL",
273 .destroy = curlds_free,
274};

Referenced by acf_curl_helper(), acf_curlopt_helper(), and acf_curlopt_write().

◆ global_curl_info