Asterisk - The Open Source Telephony Project  18.5.0
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 __init_curl_instance (void)
 
static void __init_thread_escapebuf (void)
 
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_TEST_DEFINE (vulnerable_url)
 
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. More...
 
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 = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "30ef0c93b36035ec78c9cfd712d36d9b" , .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
 
static struct ast_threadstorage curl_instance = { .once = PTHREAD_ONCE_INIT , .key_init = __init_curl_instance , .custom_init = curl_instance_init , }
 
struct global_curl_info global_curl_info = { .first = NULL, .last = NULL, .lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, {1, 0} } , }
 
static const char *const global_useragent = "asterisk-libcurl-agent/1.0"
 
static struct ast_threadstorage thread_escapebuf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_thread_escapebuf , .custom_init = NULL , }
 

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 212 of file func_curl.c.

Referenced by acf_curl_helper(), and parse_curlopt_key().

◆ CURLOPT_SPECIAL_HASHCOMPAT

#define CURLOPT_SPECIAL_HASHCOMPAT   ((CURLoption) -500)

◆ 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 207 of file func_curl.c.

Enumeration Type Documentation

◆ hashcompat

enum hashcompat
Enumerator
HASHCOMPAT_NO 
HASHCOMPAT_YES 
HASHCOMPAT_LEGACY 

Definition at line 251 of file func_curl.c.

◆ optiontype

enum optiontype
Enumerator
OT_BOOLEAN 
OT_INTEGER 
OT_INTEGER_MS 
OT_STRING 
OT_ENUM 

Definition at line 243 of file func_curl.c.

243  {
244  OT_BOOLEAN,
245  OT_INTEGER,
247  OT_STRING,
248  OT_ENUM,
249 };

Function Documentation

◆ __init_curl_instance()

static void __init_curl_instance ( void  )
static

Definition at line 629 of file func_curl.c.

649 {

◆ __init_thread_escapebuf()

static void __init_thread_escapebuf ( void  )
static

Definition at line 630 of file func_curl.c.

649 {

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 1011 of file func_curl.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 1011 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 824 of file func_curl.c.

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, curl_args::cb_data, LOG_WARNING, curl_args::postdata, curl_write_callback_data::str, url, and curl_args::url.

825 {
826  struct curl_args curl_params = { 0, };
827  int res;
828 
830  AST_APP_ARG(url);
832  );
833 
835 
836  if (ast_strlen_zero(info)) {
837  ast_log(LOG_WARNING, "CURL requires an argument (URL)\n");
838  return -1;
839  }
840 
841  curl_params.url = args.url;
842  curl_params.postdata = args.postdata;
843  curl_params.cb_data.str = ast_str_create(16);
844  if (!curl_params.cb_data.str) {
845  return -1;
846  }
847 
848  res = acf_curl_helper(chan, &curl_params);
849  ast_str_set(buf, len, "%s", ast_str_buffer(curl_params.cb_data.str));
850  ast_free(curl_params.cb_data.str);
851 
852  return res;
853 }
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
#define LOG_WARNING
Definition: logger.h:274
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
const char * args
const char * url
Definition: func_curl.c:658
#define ast_strlen_zero(foo)
Definition: strings.h:52
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:1065
#define ast_log
Definition: astobj2.c:42
const char * postdata
Definition: func_curl.c:659
struct curl_write_callback_data cb_data
Definition: func_curl.c:660
static int acf_curl_helper(struct ast_channel *chan, struct curl_args *args)
Definition: func_curl.c:663
def info(msg)
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
#define ast_free(a)
Definition: astmm.h:182
struct ast_str * str
If a string is being built, the string buffer.
Definition: func_curl.c:581
static char url[512]
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620
#define AST_APP_ARG(name)
Define an application argument.

◆ acf_curl_helper()

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

Definition at line 663 of file func_curl.c.

References 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_args::cb_data, curl_instance, CURLOPT_SPECIAL_FAILURE_CODE, CURLOPT_SPECIAL_HASHCOMPAT, ast_datastore::data, HASHCOMPAT_LEGACY, curl_settings::key, curl_settings::list, LOG_ERROR, LOG_NOTICE, LOG_WARNING, name, NULL, pbx_builtin_setvar_helper(), curl_args::postdata, S_OR, curl_write_callback_data::str, strsep(), thread_escapebuf, curl_args::url, url_is_vulnerable(), and curl_settings::value.

Referenced by acf_curl_exec(), and acf_curl_write().

664 {
665  struct ast_str *escapebuf = ast_str_thread_get(&thread_escapebuf, 16);
666  int ret = 0;
667  long http_code = 0; /* read curl response */
668  size_t i;
669  struct ast_vector_int hasfailurecode = { NULL };
670  char *failurecodestrings,*found;
671  CURL **curl;
672  struct curl_settings *cur;
673  struct curl_slist *headers = NULL;
674  struct ast_datastore *store = NULL;
675  int hashcompat = 0;
677  char curl_errbuf[CURL_ERROR_SIZE + 1]; /* add one to be safe */
678 
679  if (!escapebuf) {
680  return -1;
681  }
682 
683  if (!(curl = ast_threadstorage_get(&curl_instance, sizeof(*curl)))) {
684  ast_log(LOG_ERROR, "Cannot allocate curl structure\n");
685  return -1;
686  }
687 
688  if (url_is_vulnerable(args->url)) {
689  ast_log(LOG_ERROR, "URL '%s' is vulnerable to HTTP injection attacks. Aborting CURL() call.\n", args->url);
690  return -1;
691  }
692 
693  if (chan) {
694  ast_autoservice_start(chan);
695  }
696 
697  AST_VECTOR_INIT(&hasfailurecode, 0); /*Initialize vector*/
699  AST_LIST_TRAVERSE(&global_curl_info, cur, list) {
700  if (cur->key == CURLOPT_SPECIAL_HASHCOMPAT) {
701  hashcompat = (long) cur->value;
702  } else if (cur->key == CURLOPT_HTTPHEADER) {
703  headers = curl_slist_append(headers, (char*) cur->value);
704  } else if (cur->key == CURLOPT_SPECIAL_FAILURE_CODE) {
705  failurecodestrings = (char*) cur->value;
706  while( (found = strsep(&failurecodestrings, ",")) != NULL) {
707  AST_VECTOR_APPEND(&hasfailurecode, atoi(found));
708  }
709  } else {
710  curl_easy_setopt(*curl, cur->key, cur->value);
711  }
712  }
714 
715  if (chan) {
716  ast_channel_lock(chan);
717  store = ast_channel_datastore_find(chan, &curl_info, NULL);
718  ast_channel_unlock(chan);
719  if (store) {
720  list = store->data;
721  AST_LIST_LOCK(list);
722  AST_LIST_TRAVERSE(list, cur, list) {
723  if (cur->key == CURLOPT_SPECIAL_HASHCOMPAT) {
724  hashcompat = (long) cur->value;
725  } else if (cur->key == CURLOPT_HTTPHEADER) {
726  headers = curl_slist_append(headers, (char*) cur->value);
727  } else if (cur->key == CURLOPT_SPECIAL_FAILURE_CODE) {
728  failurecodestrings = (char*) cur->value;
729  while( (found = strsep(&failurecodestrings, ",")) != NULL) {
730  AST_VECTOR_APPEND(&hasfailurecode, atoi(found));
731  }
732  } else {
733  curl_easy_setopt(*curl, cur->key, cur->value);
734  }
735  }
736  }
737  }
738 
739  curl_easy_setopt(*curl, CURLOPT_URL, args->url);
740  curl_easy_setopt(*curl, CURLOPT_FILE, (void *) &args->cb_data);
741 
742  if (args->postdata) {
743  curl_easy_setopt(*curl, CURLOPT_POST, 1);
744  curl_easy_setopt(*curl, CURLOPT_POSTFIELDS, args->postdata);
745  }
746 
747  /* Always assign the headers - even when NULL - in case we had
748  * custom headers the last time we used this shared cURL
749  * instance */
750  curl_easy_setopt(*curl, CURLOPT_HTTPHEADER, headers);
751 
752  /* Temporarily assign a buffer for curl to write errors to. */
753  curl_errbuf[0] = curl_errbuf[CURL_ERROR_SIZE] = '\0';
754  curl_easy_setopt(*curl, CURLOPT_ERRORBUFFER, curl_errbuf);
755 
756  if (curl_easy_perform(*curl) != 0) {
757  ast_log(LOG_WARNING, "%s ('%s')\n", curl_errbuf, args->url);
758  }
759 
760  /* Reset buffer to NULL so curl doesn't try to write to it when the
761  * buffer is deallocated. Documentation is vague about allowing NULL
762  * here, but the source allows it. See: "typecheck: allow NULL to unset
763  * CURLOPT_ERRORBUFFER" (62bcf005f4678a93158358265ba905bace33b834). */
764  curl_easy_setopt(*curl, CURLOPT_ERRORBUFFER, (char*)NULL);
765  curl_easy_getinfo (*curl, CURLINFO_RESPONSE_CODE, &http_code);
766 
767  for (i = 0; i < AST_VECTOR_SIZE(&hasfailurecode); ++i) {
768  if (http_code == AST_VECTOR_GET(&hasfailurecode,i)){
769  ast_log(LOG_NOTICE, "%s%sCURL '%s' returned response code (%ld).\n",
770  chan ? ast_channel_name(chan) : "",
771  chan ? ast_channel_name(chan) : ": ",
772  args->url,
773  http_code);
774  ret=-1;
775  break;
776  }
777  }
778  AST_VECTOR_FREE(&hasfailurecode); /* Release the vector*/
779 
780  if (store) {
781  AST_LIST_UNLOCK(list);
782  }
783  curl_slist_free_all(headers);
784 
785  if (args->postdata) {
786  curl_easy_setopt(*curl, CURLOPT_POST, 0);
787  }
788 
789  if (args->cb_data.str && ast_str_strlen(args->cb_data.str)) {
791 
792  ast_debug(3, "CURL returned str='%s'\n", ast_str_buffer(args->cb_data.str));
793  if (hashcompat) {
794  char *remainder = ast_str_buffer(args->cb_data.str);
795  char *piece;
796  struct ast_str *fields = ast_str_create(ast_str_strlen(args->cb_data.str) / 2);
797  struct ast_str *values = ast_str_create(ast_str_strlen(args->cb_data.str) / 2);
798  int rowcount = 0;
799  while (fields && values && (piece = strsep(&remainder, "&"))) {
800  char *name = strsep(&piece, "=");
801  struct ast_flags mode = (hashcompat == HASHCOMPAT_LEGACY ? ast_uri_http_legacy : ast_uri_http);
802  if (piece) {
803  ast_uri_decode(piece, mode);
804  }
805  ast_uri_decode(name, mode);
806  ast_str_append(&fields, 0, "%s%s", rowcount ? "," : "", ast_str_set_escapecommas(&escapebuf, 0, name, INT_MAX));
807  ast_str_append(&values, 0, "%s%s", rowcount ? "," : "", ast_str_set_escapecommas(&escapebuf, 0, S_OR(piece, ""), INT_MAX));
808  rowcount++;
809  }
810  pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(fields));
811  ast_str_set(&args->cb_data.str, 0, "%s", ast_str_buffer(values));
812  ast_free(fields);
813  ast_free(values);
814  }
815  }
816 
817  if (chan) {
818  ast_autoservice_stop(chan);
819  }
820 
821  return ret;
822 }
void ast_uri_decode(char *s, struct ast_flags spec)
Decode URI, URN, URL (overwrite string)
Definition: main/utils.c:616
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:174
#define ast_channel_lock(chan)
Definition: channel.h:2945
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
#define AST_LIST_HEAD(name, type)
Defines a structure to be used to hold a list of specified type.
Definition: linkedlists.h:172
void * ast_threadstorage_get(struct ast_threadstorage *ts, size_t init_size)
Retrieve thread storage.
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:200
#define LOG_WARNING
Definition: logger.h:274
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
static struct ast_threadstorage curl_instance
Definition: func_curl.c:629
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:256
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:1091
Structure for a data store object.
Definition: datastore.h:68
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:2404
#define NULL
Definition: resample.c:96
const char * url
Definition: func_curl.c:658
hashcompat
Definition: func_curl.c:251
#define CURLOPT_SPECIAL_FAILURE_CODE
Definition: func_curl.c:212
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:1065
static int url_is_vulnerable(const char *url)
Check for potential HTTP injection risk.
Definition: func_curl.c:648
CURLoption key
Definition: func_curl.c:223
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
void * value
Definition: func_curl.c:224
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition: vector.h:113
const char * postdata
Definition: func_curl.c:659
struct curl_write_callback_data cb_data
Definition: func_curl.c:660
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:266
#define LOG_ERROR
Definition: logger.h:285
const struct ast_flags ast_uri_http_legacy
Definition: main/utils.c:574
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
static struct ast_threadstorage thread_escapebuf
Definition: func_curl.c:630
#define LOG_NOTICE
Definition: logger.h:263
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
#define ast_channel_unlock(chan)
Definition: channel.h:2946
static const char name[]
Definition: cdr_mysql.c:74
#define ast_free(a)
Definition: astmm.h:182
Structure used to handle boolean flags.
Definition: utils.h:199
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...
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:1021
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:688
void * data
Definition: datastore.h:70
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:682
char * strsep(char **str, const char *delims)
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:79
const char * ast_channel_name(const struct ast_channel *chan)
const struct ast_flags ast_uri_http
Definition: main/utils.c:573
struct ast_str * str
If a string is being built, the string buffer.
Definition: func_curl.c:581
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:861
void ast_str_trim_blanks(struct ast_str *buf)
Trims trailing whitespace characters from an ast_str string.
Definition: strings.h:678
#define CURLOPT_SPECIAL_HASHCOMPAT
Definition: func_curl.c:210
static const struct ast_datastore_info curl_info
Definition: func_curl.c:216
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:611
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620

◆ acf_curl_write()

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

Definition at line 855 of file func_curl.c.

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, and curl_args::url.

856 {
857  struct curl_args curl_params = { 0, };
858  int res;
859  char *args_value = ast_strdupa(value);
861  AST_APP_ARG(file_path);
862  );
863 
864  AST_STANDARD_APP_ARGS(args, args_value);
865 
866  if (ast_strlen_zero(name)) {
867  ast_log(LOG_WARNING, "CURL requires an argument (URL)\n");
868  return -1;
869  }
870 
871  if (ast_strlen_zero(args.file_path)) {
872  ast_log(LOG_WARNING, "CURL requires a file to write\n");
873  return -1;
874  }
875 
876  curl_params.url = name;
877  curl_params.cb_data.out_file = fopen(args.file_path, "w");
878  if (!curl_params.cb_data.out_file) {
879  ast_log(LOG_WARNING, "Failed to open file %s: %s (%d)\n",
880  args.file_path,
881  strerror(errno),
882  errno);
883  return -1;
884  }
885 
886  res = acf_curl_helper(chan, &curl_params);
887 
888  fclose(curl_params.cb_data.out_file);
889 
890  return res;
891 }
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
#define LOG_WARNING
Definition: logger.h:274
const char * args
int value
Definition: syslog.c:37
const char * url
Definition: func_curl.c:658
#define ast_strlen_zero(foo)
Definition: strings.h:52
#define ast_log
Definition: astobj2.c:42
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
FILE * out_file
If a file is being retrieved, the file to write to.
Definition: func_curl.c:585
struct curl_write_callback_data cb_data
Definition: func_curl.c:660
static int acf_curl_helper(struct ast_channel *chan, struct curl_args *args)
Definition: func_curl.c:663
int errno
static const char name[]
Definition: cdr_mysql.c:74
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
#define AST_APP_ARG(name)
Define an application argument.

◆ 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 461 of file func_curl.c.

References ast_channel_datastore_find(), ast_copy_string(), ast_debug, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log, ast_str_set(), CURLOPT_SPECIAL_HASHCOMPAT, ast_datastore::data, global_curl_info, HASHCOMPAT_LEGACY, HASHCOMPAT_NO, HASHCOMPAT_YES, curl_settings::key, 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().

462 {
463  struct ast_datastore *store;
464  struct global_curl_info *list[2] = { &global_curl_info, NULL };
465  struct curl_settings *cur = NULL;
466  CURLoption key;
467  enum optiontype ot;
468  int i;
469 
470  if (parse_curlopt_key(data, &key, &ot)) {
471  ast_log(LOG_ERROR, "Unrecognized option: '%s'\n", data);
472  return -1;
473  }
474 
475  if (chan && (store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
476  list[0] = store->data;
477  list[1] = &global_curl_info;
478  }
479 
480  for (i = 0; i < 2; i++) {
481  if (!list[i]) {
482  break;
483  }
484  AST_LIST_LOCK(list[i]);
485  AST_LIST_TRAVERSE(list[i], cur, list) {
486  if (cur->key == key) {
487  if (ot == OT_BOOLEAN || ot == OT_INTEGER) {
488  if (buf) {
489  snprintf(buf, len, "%ld", (long) cur->value);
490  } else {
491  ast_str_set(bufstr, len, "%ld", (long) cur->value);
492  }
493  } else if (ot == OT_INTEGER_MS) {
494  if ((long) cur->value % 1000 == 0) {
495  if (buf) {
496  snprintf(buf, len, "%ld", (long)cur->value / 1000);
497  } else {
498  ast_str_set(bufstr, len, "%ld", (long) cur->value / 1000);
499  }
500  } else {
501  if (buf) {
502  snprintf(buf, len, "%.3f", (double) ((long) cur->value) / 1000.0);
503  } else {
504  ast_str_set(bufstr, len, "%.3f", (double) ((long) cur->value) / 1000.0);
505  }
506  }
507  } else if (ot == OT_STRING) {
508  ast_debug(1, "Found entry %p, with key %d and value %p\n", cur, cur->key, cur->value);
509  if (buf) {
510  ast_copy_string(buf, cur->value, len);
511  } else {
512  ast_str_set(bufstr, 0, "%s", (char *) cur->value);
513  }
514  } else if (key == CURLOPT_PROXYTYPE) {
515  const char *strval = "unknown";
516  if (0) {
517 #if CURLVERSION_ATLEAST(7,15,2)
518  } else if ((long)cur->value == CURLPROXY_SOCKS4) {
519  strval = "socks4";
520 #endif
521 #if CURLVERSION_ATLEAST(7,18,0)
522  } else if ((long)cur->value == CURLPROXY_SOCKS4A) {
523  strval = "socks4a";
524 #endif
525  } else if ((long)cur->value == CURLPROXY_SOCKS5) {
526  strval = "socks5";
527 #if CURLVERSION_ATLEAST(7,18,0)
528  } else if ((long)cur->value == CURLPROXY_SOCKS5_HOSTNAME) {
529  strval = "socks5hostname";
530 #endif
531 #if CURLVERSION_ATLEAST(7,10,0)
532  } else if ((long)cur->value == CURLPROXY_HTTP) {
533  strval = "http";
534 #endif
535  }
536  if (buf) {
537  ast_copy_string(buf, strval, len);
538  } else {
539  ast_str_set(bufstr, 0, "%s", strval);
540  }
541  } else if (key == CURLOPT_SPECIAL_HASHCOMPAT) {
542  const char *strval = "unknown";
543  if ((long) cur->value == HASHCOMPAT_LEGACY) {
544  strval = "legacy";
545  } else if ((long) cur->value == HASHCOMPAT_YES) {
546  strval = "yes";
547  } else if ((long) cur->value == HASHCOMPAT_NO) {
548  strval = "no";
549  }
550  if (buf) {
551  ast_copy_string(buf, strval, len);
552  } else {
553  ast_str_set(bufstr, 0, "%s", strval);
554  }
555  }
556  break;
557  }
558  }
559  AST_LIST_UNLOCK(list[i]);
560  if (cur) {
561  break;
562  }
563  }
564 
565  return cur ? 0 : -1;
566 }
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
static int parse_curlopt_key(const char *name, CURLoption *key, enum optiontype *ot)
Definition: func_curl.c:257
Structure for a data store object.
Definition: datastore.h:68
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:2404
#define NULL
Definition: resample.c:96
struct global_curl_info global_curl_info
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:1065
CURLoption key
Definition: func_curl.c:223
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
void * value
Definition: func_curl.c:224
#define LOG_ERROR
Definition: logger.h:285
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
void * data
Definition: datastore.h:70
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
optiontype
Definition: func_curl.c:243
#define CURLOPT_SPECIAL_HASHCOMPAT
Definition: func_curl.c:210
static const struct ast_datastore_info curl_info
Definition: func_curl.c:216

◆ 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 568 of file func_curl.c.

References acf_curlopt_helper(), and NULL.

569 {
570  return acf_curlopt_helper(chan, cmd, data, buf, NULL, len);
571 }
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
#define NULL
Definition: resample.c:96
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
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:461

◆ 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 573 of file func_curl.c.

References acf_curlopt_helper(), and NULL.

574 {
575  return acf_curlopt_helper(chan, cmd, data, NULL, buf, len);
576 }
#define NULL
Definition: resample.c:96
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
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:461

◆ acf_curlopt_write()

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

Definition at line 335 of file func_curl.c.

References ast_calloc, ast_channel_datastore_add(), ast_channel_datastore_find(), 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_true(), CURLOPT_SPECIAL_HASHCOMPAT, ast_datastore::data, global_curl_info, HASHCOMPAT_LEGACY, HASHCOMPAT_NO, HASHCOMPAT_YES, curl_settings::key, curl_settings::list, LOG_ERROR, NULL, OT_BOOLEAN, OT_ENUM, OT_INTEGER, OT_INTEGER_MS, OT_STRING, parse_curlopt_key(), and tmp().

336 {
337  struct ast_datastore *store;
338  struct global_curl_info *list;
339  struct curl_settings *cur, *new = NULL;
340  CURLoption key;
341  enum optiontype ot;
342 
343  if (chan) {
344  if (!(store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
345  /* Create a new datastore */
346  if (!(store = ast_datastore_alloc(&curl_info, NULL))) {
347  ast_log(LOG_ERROR, "Unable to allocate new datastore. Cannot set any CURL options\n");
348  return -1;
349  }
350 
351  if (!(list = ast_calloc(1, sizeof(*list)))) {
352  ast_log(LOG_ERROR, "Unable to allocate list head. Cannot set any CURL options\n");
353  ast_datastore_free(store);
354  return -1;
355  }
356 
357  store->data = list;
358  AST_LIST_HEAD_INIT(list);
359  ast_channel_datastore_add(chan, store);
360  } else {
361  list = store->data;
362  }
363  } else {
364  /* Populate the global structure */
365  list = &global_curl_info;
366  }
367 
368  if (!parse_curlopt_key(name, &key, &ot)) {
369  if (ot == OT_BOOLEAN) {
370  if ((new = ast_calloc(1, sizeof(*new)))) {
371  new->value = (void *)((long) ast_true(value));
372  }
373  } else if (ot == OT_INTEGER) {
374  long tmp = atol(value);
375  if ((new = ast_calloc(1, sizeof(*new)))) {
376  new->value = (void *)tmp;
377  }
378  } else if (ot == OT_INTEGER_MS) {
379  long tmp = atof(value) * 1000.0;
380  if ((new = ast_calloc(1, sizeof(*new)))) {
381  new->value = (void *)tmp;
382  }
383  } else if (ot == OT_STRING) {
384  if ((new = ast_calloc(1, sizeof(*new) + strlen(value) + 1))) {
385  new->value = (char *)new + sizeof(*new);
386  strcpy(new->value, value);
387  }
388  } else if (ot == OT_ENUM) {
389  if (key == CURLOPT_PROXYTYPE) {
390  long ptype =
391 #if CURLVERSION_ATLEAST(7,10,0)
392  CURLPROXY_HTTP;
393 #else
394  CURLPROXY_SOCKS5;
395 #endif
396  if (0) {
397 #if CURLVERSION_ATLEAST(7,15,2)
398  } else if (!strcasecmp(value, "socks4")) {
399  ptype = CURLPROXY_SOCKS4;
400 #endif
401 #if CURLVERSION_ATLEAST(7,18,0)
402  } else if (!strcasecmp(value, "socks4a")) {
403  ptype = CURLPROXY_SOCKS4A;
404 #endif
405 #if CURLVERSION_ATLEAST(7,18,0)
406  } else if (!strcasecmp(value, "socks5")) {
407  ptype = CURLPROXY_SOCKS5;
408 #endif
409 #if CURLVERSION_ATLEAST(7,18,0)
410  } else if (!strncasecmp(value, "socks5", 6)) {
411  ptype = CURLPROXY_SOCKS5_HOSTNAME;
412 #endif
413  }
414 
415  if ((new = ast_calloc(1, sizeof(*new)))) {
416  new->value = (void *)ptype;
417  }
418  } else if (key == CURLOPT_SPECIAL_HASHCOMPAT) {
419  if ((new = ast_calloc(1, sizeof(*new)))) {
420  new->value = (void *) (long) (!strcasecmp(value, "legacy") ? HASHCOMPAT_LEGACY : ast_true(value) ? HASHCOMPAT_YES : HASHCOMPAT_NO);
421  }
422  } else {
423  /* Highly unlikely */
424  goto yuck;
425  }
426  }
427 
428  /* Memory allocation error */
429  if (!new) {
430  return -1;
431  }
432 
433  new->key = key;
434  } else {
435 yuck:
436  ast_log(LOG_ERROR, "Unrecognized option: %s\n", name);
437  return -1;
438  }
439 
440  /* Remove any existing entry, only http headers are left */
441  AST_LIST_LOCK(list);
442  if (new->key != CURLOPT_HTTPHEADER) {
443  AST_LIST_TRAVERSE_SAFE_BEGIN(list, cur, list) {
444  if (cur->key == new->key) {
446  ast_free(cur);
447  break;
448  }
449  }
451  }
452 
453  /* Insert new entry */
454  ast_debug(1, "Inserting entry %p with key %d and value %p\n", new, new->key, new->value);
455  AST_LIST_INSERT_TAIL(list, new, list);
456  AST_LIST_UNLOCK(list);
457 
458  return 0;
459 }
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
static int tmp()
Definition: bt_open.c:389
static int parse_curlopt_key(const char *name, CURLoption *key, enum optiontype *ot)
Definition: func_curl.c:257
Structure for a data store object.
Definition: datastore.h:68
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:2404
#define NULL
Definition: resample.c:96
int value
Definition: syslog.c:37
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:614
struct global_curl_info global_curl_info
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
CURLoption key
Definition: func_curl.c:223
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:556
struct curl_settings::@205 list
#define LOG_ERROR
Definition: logger.h:285
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:730
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: main/utils.c:1951
static const char name[]
Definition: cdr_mysql.c:74
#define AST_LIST_HEAD_INIT(head)
Initializes a list head structure.
Definition: linkedlists.h:625
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
void * data
Definition: datastore.h:70
#define ast_datastore_alloc(info, uid)
Definition: datastore.h:89
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:528
optiontype
Definition: func_curl.c:243
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2390
#define CURLOPT_SPECIAL_HASHCOMPAT
Definition: func_curl.c:210
static const struct ast_datastore_info curl_info
Definition: func_curl.c:216

◆ AST_MODULE_SELF_SYM()

struct ast_module* AST_MODULE_SELF_SYM ( void  )

Definition at line 1011 of file func_curl.c.

◆ AST_TEST_DEFINE()

AST_TEST_DEFINE ( vulnerable_url  )

Definition at line 932 of file func_curl.c.

References ARRAY_LEN, AST_TEST_FAIL, AST_TEST_PASS, ast_test_status_update, sip_to_pjsip::info(), TEST_EXECUTE, TEST_INIT, and url_is_vulnerable().

933 {
934  const char *bad_urls [] = {
935  "http://example.com\r\nDELETE http://example.com/everything",
936  "http://example.com\rDELETE http://example.com/everything",
937  "http://example.com\nDELETE http://example.com/everything",
938  "\r\nhttp://example.com",
939  "\rhttp://example.com",
940  "\nhttp://example.com",
941  "http://example.com\r\n",
942  "http://example.com\r",
943  "http://example.com\n",
944  };
945  const char *good_urls [] = {
946  "http://example.com",
947  "http://example.com/%5Cr%5Cn",
948  };
949  int i;
951 
952  switch (cmd) {
953  case TEST_INIT:
954  info->name = "vulnerable_url";
955  info->category = "/funcs/func_curl/";
956  info->summary = "cURL vulnerable URL test";
957  info->description =
958  "Ensure that any combination of '\\r' or '\\n' in a URL invalidates the URL";
959  case TEST_EXECUTE:
960  break;
961  }
962 
963  for (i = 0; i < ARRAY_LEN(bad_urls); ++i) {
964  if (!url_is_vulnerable(bad_urls[i])) {
965  ast_test_status_update(test, "String '%s' detected as valid when it should be invalid\n", bad_urls[i]);
966  res = AST_TEST_FAIL;
967  }
968  }
969 
970  for (i = 0; i < ARRAY_LEN(good_urls); ++i) {
971  if (url_is_vulnerable(good_urls[i])) {
972  ast_test_status_update(test, "String '%s' detected as invalid when it should be valid\n", good_urls[i]);
973  res = AST_TEST_FAIL;
974  }
975  }
976 
977  return res;
978 }
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
static int url_is_vulnerable(const char *url)
Check for potential HTTP injection risk.
Definition: func_curl.c:648
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
def info(msg)
ast_test_result_state
Definition: test.h:200

◆ curl_instance_cleanup()

static void curl_instance_cleanup ( void *  data)
static

Definition at line 620 of file func_curl.c.

References ast_free.

621 {
622  CURL **curl = data;
623 
624  curl_easy_cleanup(*curl);
625 
626  ast_free(data);
627 }
#define ast_free(a)
Definition: astmm.h:182

◆ curl_instance_init()

static int curl_instance_init ( void *  data)
static

Definition at line 605 of file func_curl.c.

References global_useragent, and WriteMemoryCallback().

606 {
607  CURL **curl = data;
608 
609  if (!(*curl = curl_easy_init()))
610  return -1;
611 
612  curl_easy_setopt(*curl, CURLOPT_NOSIGNAL, 1);
613  curl_easy_setopt(*curl, CURLOPT_TIMEOUT, 180);
614  curl_easy_setopt(*curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
615  curl_easy_setopt(*curl, CURLOPT_USERAGENT, global_useragent);
616 
617  return 0;
618 }
static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
Definition: func_curl.c:588
static const char *const global_useragent
Definition: func_curl.c:603

◆ curlds_free()

static void curlds_free ( void *  data)
static

Definition at line 229 of file func_curl.c.

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

230 {
232  struct curl_settings *setting;
233  if (!list) {
234  return;
235  }
236  while ((setting = AST_LIST_REMOVE_HEAD(list, list))) {
237  ast_free(setting);
238  }
240  ast_free(list);
241 }
#define AST_LIST_HEAD(name, type)
Defines a structure to be used to hold a list of specified type.
Definition: linkedlists.h:172
#define AST_LIST_HEAD_DESTROY(head)
Destroys a list head structure.
Definition: linkedlists.h:652
struct curl_settings::@205 list
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:832
#define ast_free(a)
Definition: astmm.h:182

◆ load_module()

static int load_module ( void  )
static

Definition at line 993 of file func_curl.c.

References AST_CFE_WRITE, ast_custom_function_register, ast_custom_function_register_escalating, AST_MODFLAG_LOAD_ORDER, AST_MODPRI_REALTIME_DEPEND2, AST_MODULE_INFO(), AST_MODULE_SUPPORT_CORE, AST_TEST_REGISTER, ASTERISK_GPL_KEY, and unload_module().

994 {
995  int res;
996 
999 
1000  AST_TEST_REGISTER(vulnerable_url);
1001 
1002  return res;
1003 }
static struct ast_custom_function acf_curlopt
Definition: func_curl.c:899
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
#define ast_custom_function_register_escalating(acf, escalation)
Register a custom function which requires escalated privileges.
Definition: pbx.h:1517
static struct ast_custom_function acf_curl
Definition: func_curl.c:893
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1508

◆ parse_curlopt_key()

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

Definition at line 257 of file func_curl.c.

References CURLOPT_SPECIAL_FAILURE_CODE, CURLOPT_SPECIAL_HASHCOMPAT, OT_BOOLEAN, OT_ENUM, OT_INTEGER, OT_INTEGER_MS, and OT_STRING.

Referenced by acf_curlopt_helper(), and acf_curlopt_write().

258 {
259  if (!strcasecmp(name, "header")) {
260  *key = CURLOPT_HEADER;
261  *ot = OT_BOOLEAN;
262  } else if (!strcasecmp(name, "httpheader")) {
263  *key = CURLOPT_HTTPHEADER;
264  *ot = OT_STRING;
265  } else if (!strcasecmp(name, "proxy")) {
266  *key = CURLOPT_PROXY;
267  *ot = OT_STRING;
268  } else if (!strcasecmp(name, "proxyport")) {
269  *key = CURLOPT_PROXYPORT;
270  *ot = OT_INTEGER;
271  } else if (!strcasecmp(name, "proxytype")) {
272  *key = CURLOPT_PROXYTYPE;
273  *ot = OT_ENUM;
274  } else if (!strcasecmp(name, "dnstimeout")) {
275  *key = CURLOPT_DNS_CACHE_TIMEOUT;
276  *ot = OT_INTEGER;
277  } else if (!strcasecmp(name, "userpwd")) {
278  *key = CURLOPT_USERPWD;
279  *ot = OT_STRING;
280  } else if (!strcasecmp(name, "proxyuserpwd")) {
281  *key = CURLOPT_PROXYUSERPWD;
282  *ot = OT_STRING;
283  } else if (!strcasecmp(name, "followlocation")) {
284  *key = CURLOPT_FOLLOWLOCATION;
285  *ot = OT_BOOLEAN;
286  } else if (!strcasecmp(name, "maxredirs")) {
287  *key = CURLOPT_MAXREDIRS;
288  *ot = OT_INTEGER;
289  } else if (!strcasecmp(name, "referer")) {
290  *key = CURLOPT_REFERER;
291  *ot = OT_STRING;
292  } else if (!strcasecmp(name, "useragent")) {
293  *key = CURLOPT_USERAGENT;
294  *ot = OT_STRING;
295  } else if (!strcasecmp(name, "cookie")) {
296  *key = CURLOPT_COOKIE;
297  *ot = OT_STRING;
298  } else if (!strcasecmp(name, "ftptimeout")) {
299  *key = CURLOPT_FTP_RESPONSE_TIMEOUT;
300  *ot = OT_INTEGER;
301  } else if (!strcasecmp(name, "httptimeout")) {
302 #if CURLVERSION_ATLEAST(7,16,2)
303  *key = CURLOPT_TIMEOUT_MS;
304  *ot = OT_INTEGER_MS;
305 #else
306  *key = CURLOPT_TIMEOUT;
307  *ot = OT_INTEGER;
308 #endif
309  } else if (!strcasecmp(name, "conntimeout")) {
310 #if CURLVERSION_ATLEAST(7,16,2)
311  *key = CURLOPT_CONNECTTIMEOUT_MS;
312  *ot = OT_INTEGER_MS;
313 #else
314  *key = CURLOPT_CONNECTTIMEOUT;
315  *ot = OT_INTEGER;
316 #endif
317  } else if (!strcasecmp(name, "ftptext")) {
318  *key = CURLOPT_TRANSFERTEXT;
319  *ot = OT_BOOLEAN;
320  } else if (!strcasecmp(name, "ssl_verifypeer")) {
321  *key = CURLOPT_SSL_VERIFYPEER;
322  *ot = OT_BOOLEAN;
323  } else if (!strcasecmp(name, "hashcompat")) {
325  *ot = OT_ENUM;
326  } else if (!strcasecmp(name, "failurecodes")) {
328  *ot = OT_STRING;
329  } else {
330  return -1;
331  }
332  return 0;
333 }
#define CURLOPT_SPECIAL_FAILURE_CODE
Definition: func_curl.c:212
CURLoption key
Definition: func_curl.c:223
static const char name[]
Definition: cdr_mysql.c:74
#define CURLOPT_SPECIAL_HASHCOMPAT
Definition: func_curl.c:210

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 981 of file func_curl.c.

References ast_custom_function_unregister(), and AST_TEST_UNREGISTER.

Referenced by load_module().

982 {
983  int res;
984 
987 
988  AST_TEST_UNREGISTER(vulnerable_url);
989 
990  return res;
991 }
static struct ast_custom_function acf_curlopt
Definition: func_curl.c:899
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
static struct ast_custom_function acf_curl
Definition: func_curl.c:893
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128

◆ 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 648 of file func_curl.c.

Referenced by acf_curl_helper(), and AST_TEST_DEFINE().

649 {
650  if (strpbrk(url, "\r\n")) {
651  return 1;
652  }
653 
654  return 0;
655 }
static char url[512]

◆ WriteMemoryCallback()

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

Definition at line 588 of file func_curl.c.

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

Referenced by curl_instance_init().

589 {
590  register int realsize = 0;
591  struct curl_write_callback_data *cb_data = data;
592 
593  if (cb_data->str) {
594  realsize = size * nmemb;
595  ast_str_append_substr(&cb_data->str, 0, ptr, realsize);
596  } else if (cb_data->out_file) {
597  realsize = fwrite(ptr, size, nmemb, cb_data->out_file);
598  }
599 
600  return realsize;
601 }
Callback data passed to WriteMemoryCallback.
Definition: func_curl.c:579
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:1014
FILE * out_file
If a file is being retrieved, the file to write to.
Definition: func_curl.c:585
struct ast_str * str
If a string is being built, the string buffer.
Definition: func_curl.c:581

Variable Documentation

◆ __mod_info

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Load external URL" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "30ef0c93b36035ec78c9cfd712d36d9b" , .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 1011 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,
}
static int acf_curl_write(struct ast_channel *chan, const char *cmd, char *name, const char *value)
Definition: func_curl.c:855
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:824

Definition at line 893 of file func_curl.c.

◆ acf_curlopt

struct ast_custom_function acf_curlopt
static

Definition at line 899 of file func_curl.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 1011 of file func_curl.c.

◆ curl_info

const struct ast_datastore_info curl_info
static
Initial value:
= {
.type = "CURL",
.destroy = curlds_free,
}
static void curlds_free(void *data)
Definition: func_curl.c:229

Definition at line 216 of file func_curl.c.

◆ curl_instance

struct ast_threadstorage curl_instance = { .once = PTHREAD_ONCE_INIT , .key_init = __init_curl_instance , .custom_init = curl_instance_init , }
static

Definition at line 629 of file func_curl.c.

Referenced by acf_curl_helper().

◆ global_curl_info

struct global_curl_info global_curl_info = { .first = NULL, .last = NULL, .lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, {1, 0} } , }

◆ global_useragent

const char* const global_useragent = "asterisk-libcurl-agent/1.0"
static

Definition at line 603 of file func_curl.c.

Referenced by curl_instance_init().

◆ thread_escapebuf

struct ast_threadstorage thread_escapebuf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_thread_escapebuf , .custom_init = NULL , }
static

Definition at line 630 of file func_curl.c.

Referenced by acf_curl_helper().