42 #include <curl/curl.h>
162 #define CURLVERSION_ATLEAST(a,b,c) \
163 ((LIBCURL_VERSION_MAJOR > (a)) || ((LIBCURL_VERSION_MAJOR == (a)) && (LIBCURL_VERSION_MINOR > (b))) || ((LIBCURL_VERSION_MAJOR == (a)) && (LIBCURL_VERSION_MINOR == (b)) && (LIBCURL_VERSION_PATCH >= (c))))
165 #define CURLOPT_SPECIAL_HASHCOMPAT -500
185 struct curl_settings *setting;
205 if (!strcasecmp(name,
"header")) {
206 *key = CURLOPT_HEADER;
208 }
else if (!strcasecmp(name,
"proxy")) {
209 *key = CURLOPT_PROXY;
211 }
else if (!strcasecmp(name,
"proxyport")) {
212 *key = CURLOPT_PROXYPORT;
214 }
else if (!strcasecmp(name,
"proxytype")) {
215 *key = CURLOPT_PROXYTYPE;
217 }
else if (!strcasecmp(name,
"dnstimeout")) {
218 *key = CURLOPT_DNS_CACHE_TIMEOUT;
220 }
else if (!strcasecmp(name,
"userpwd")) {
221 *key = CURLOPT_USERPWD;
223 }
else if (!strcasecmp(name,
"proxyuserpwd")) {
224 *key = CURLOPT_PROXYUSERPWD;
226 }
else if (!strcasecmp(name,
"maxredirs")) {
227 *key = CURLOPT_MAXREDIRS;
229 }
else if (!strcasecmp(name,
"referer")) {
230 *key = CURLOPT_REFERER;
232 }
else if (!strcasecmp(name,
"useragent")) {
233 *key = CURLOPT_USERAGENT;
235 }
else if (!strcasecmp(name,
"cookie")) {
236 *key = CURLOPT_COOKIE;
238 }
else if (!strcasecmp(name,
"ftptimeout")) {
239 *key = CURLOPT_FTP_RESPONSE_TIMEOUT;
241 }
else if (!strcasecmp(name,
"httptimeout")) {
242 #if CURLVERSION_ATLEAST(7,16,2)
243 *key = CURLOPT_TIMEOUT_MS;
246 *key = CURLOPT_TIMEOUT;
249 }
else if (!strcasecmp(name,
"conntimeout")) {
250 #if CURLVERSION_ATLEAST(7,16,2)
251 *key = CURLOPT_CONNECTTIMEOUT_MS;
254 *key = CURLOPT_CONNECTTIMEOUT;
257 }
else if (!strcasecmp(name,
"ftptext")) {
258 *key = CURLOPT_TRANSFERTEXT;
260 }
else if (!strcasecmp(name,
"ssl_verifypeer")) {
261 *key = CURLOPT_SSL_VERIFYPEER;
263 }
else if (!strcasecmp(name,
"hashcompat")) {
284 ast_log(
LOG_ERROR,
"Unable to allocate new datastore. Cannot set any CURL options\n");
289 ast_log(
LOG_ERROR,
"Unable to allocate list head. Cannot set any CURL options\n");
308 new->value = (
void *)((
long)
ast_true(value));
311 long tmp = atol(value);
313 new->value = (
void *)tmp;
316 long tmp = atof(value) * 1000.0;
318 new->value = (
void *)tmp;
321 if ((
new =
ast_calloc(1,
sizeof(*
new) + strlen(value) + 1))) {
322 new->value = (
char *)
new +
sizeof(*
new);
323 strcpy(new->value, value);
326 if (key == CURLOPT_PROXYTYPE) {
328 #if CURLVERSION_ATLEAST(7,10,0)
334 #if CURLVERSION_ATLEAST(7,15,2)
335 }
else if (!strcasecmp(value,
"socks4")) {
336 ptype = CURLPROXY_SOCKS4;
338 #if CURLVERSION_ATLEAST(7,18,0)
339 }
else if (!strcasecmp(value,
"socks4a")) {
340 ptype = CURLPROXY_SOCKS4A;
342 #if CURLVERSION_ATLEAST(7,18,0)
343 }
else if (!strcasecmp(value,
"socks5")) {
344 ptype = CURLPROXY_SOCKS5;
346 #if CURLVERSION_ATLEAST(7,18,0)
347 }
else if (!strncasecmp(value,
"socks5", 6)) {
348 ptype = CURLPROXY_SOCKS5_HOSTNAME;
353 new->value = (
void *)ptype;
376 if (cur->
key == new->key) {
385 ast_debug(1,
"Inserting entry %p with key %d and value %p\n",
new, new->key, new->value);
407 list[0] = store->
data;
411 for (i = 0; i < 2; i++) {
417 if (cur->
key == key) {
420 snprintf(buf, len,
"%ld", (
long) cur->
value);
425 if ((
long) cur->
value % 1000 == 0) {
427 snprintf(buf, len,
"%ld", (
long)cur->
value / 1000);
433 snprintf(buf, len,
"%.3f", (
double) ((
long) cur->
value) / 1000.0);
439 ast_debug(1,
"Found entry %p, with key %d and value %p\n", cur, cur->
key, cur->
value);
445 }
else if (key == CURLOPT_PROXYTYPE) {
447 #if CURLVERSION_ATLEAST(7,15,2)
448 }
else if ((
long)cur->
value == CURLPROXY_SOCKS4) {
455 #if CURLVERSION_ATLEAST(7,18,0)
456 }
else if ((
long)cur->
value == CURLPROXY_SOCKS4A) {
463 }
else if ((
long)cur->
value == CURLPROXY_SOCKS5) {
469 #if CURLVERSION_ATLEAST(7,18,0)
470 }
else if ((
long)cur->
value == CURLPROXY_SOCKS5_HOSTNAME) {
477 #if CURLVERSION_ATLEAST(7,10,0)
478 }
else if ((
long)cur->
value == CURLPROXY_HTTP) {
517 register int realsize = size * nmemb;
520 ast_debug(3,
"Called with data=%p, str=%p, realsize=%d, len=%zu, used=%zu\n", data, *pstr, realsize,
ast_str_size(*pstr),
ast_str_strlen(*pstr));
535 if (!(*curl = curl_easy_init()))
538 curl_easy_setopt(*curl, CURLOPT_NOSIGNAL, 1);
539 curl_easy_setopt(*curl, CURLOPT_TIMEOUT, 180);
550 curl_easy_cleanup(*curl);
576 if (strpbrk(url,
"\r\n")) {
597 char curl_errbuf[CURL_ERROR_SIZE + 1];
621 ast_log(
LOG_ERROR,
"URL '%s' is vulnerable to HTTP injection attacks. Aborting CURL() call.\n",
args.url);
638 hashcompat = (cur->
value != NULL) ? 1 : 0;
640 curl_easy_setopt(*curl, cur->
key, cur->
value);
649 hashcompat = (cur->
value != NULL) ? 1 : 0;
651 curl_easy_setopt(*curl, cur->
key, cur->
value);
656 curl_easy_setopt(*curl, CURLOPT_URL,
args.url);
657 curl_easy_setopt(*curl, CURLOPT_FILE, (
void *) &str);
660 curl_easy_setopt(*curl, CURLOPT_POST, 1);
661 curl_easy_setopt(*curl, CURLOPT_POSTFIELDS,
args.postdata);
665 curl_errbuf[0] = curl_errbuf[CURL_ERROR_SIZE] =
'\0';
666 curl_easy_setopt(*curl, CURLOPT_ERRORBUFFER, curl_errbuf);
668 if (curl_easy_perform(*curl) != 0) {
676 curl_easy_setopt(*curl, CURLOPT_ERRORBUFFER, (
char*)NULL);
684 curl_easy_setopt(*curl, CURLOPT_POST, 0);
697 while (fields && values && (piece =
strsep(&remainder,
"&"))) {
744 .synopsis =
"Retrieves the contents of a URL",
745 .syntax =
"CURL(url[,post-data])",
747 " url - URL to retrieve\n"
748 " post-data - Optional data to send as a POST (GET is default action)\n",
755 .synopsis =
"Set options for use with the CURL() function",
756 .syntax =
"CURLOPT(<option>)",
758 " cookie - Send cookie with request [none]\n"
759 " conntimeout - Number of seconds to wait for connection\n"
760 " dnstimeout - Number of seconds to wait for DNS response\n"
761 " ftptext - For FTP, force a text transfer (boolean)\n"
762 " ftptimeout - For FTP, the server response timeout\n"
763 " header - Retrieve header information (boolean)\n"
764 " httptimeout - Number of seconds to wait for HTTP response\n"
765 " maxredirs - Maximum number of redirects to follow\n"
766 " proxy - Hostname or IP to use as a proxy\n"
767 " proxytype - http, socks4, or socks5\n"
768 " proxyport - port number of the proxy\n"
769 " proxyuserpwd - A <user>:<pass> to use for authentication\n"
770 " referer - Referer URL to use for the request\n"
771 " useragent - UserAgent string to use\n"
772 " userpwd - A <user>:<pass> to use for authentication\n"
773 " ssl_verifypeer - Whether to verify the peer certificate (boolean)\n"
774 " hashcompat - Result data will be compatible for use with HASH()\n"
783 const char *bad_urls [] = {
784 "http://example.com\r\nDELETE http://example.com/everything",
785 "http://example.com\rDELETE http://example.com/everything",
786 "http://example.com\nDELETE http://example.com/everything",
787 "\r\nhttp://example.com",
788 "\rhttp://example.com",
789 "\nhttp://example.com",
790 "http://example.com\r\n",
791 "http://example.com\r",
792 "http://example.com\n",
794 const char *good_urls [] = {
795 "http://example.com",
796 "http://example.com/%5Cr%5Cn",
803 info->name =
"vulnerable_url";
804 info->category =
"/funcs/func_curl/";
805 info->summary =
"cURL vulnerable URL test";
807 "Ensure that any combination of '\\r' or '\\n' in a URL invalidates the URL";
812 for (i = 0; i <
ARRAY_LEN(bad_urls); ++i) {
819 for (i = 0; i <
ARRAY_LEN(good_urls); ++i) {
847 ast_log(
LOG_ERROR,
"Cannot load res_curl, so func_curl cannot be loaded\n");
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Main Channel structure associated with a channel.
static int acf_curlopt_write(struct ast_channel *chan, const char *cmd, char *name, const char *value)
#define AST_LIST_LOCK(head)
Locks a list.
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
#define AST_LIST_HEAD(name, type)
Defines a structure to be used to hold a list of specified type.
static int acf_curl2_exec(struct ast_channel *chan, const char *cmd, char *info, struct ast_str **buf, ssize_t len)
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...
char * strsep(char **str, const char *delims)
enum ast_module_load_result ast_load_resource(const char *resource_name)
Load a module.
static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
static struct ast_custom_function acf_curlopt
size_t ast_str_size(const struct ast_str *buf)
Returns the current maximum length (without reallocation) of the current buffer.
void ast_uri_decode(char *s)
Decode URI, URN, URL (overwrite string)
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
static struct ast_threadstorage curl_instance
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
static int parse_curlopt_key(const char *name, CURLoption *key, enum optiontype *ot)
#define AST_TEST_REGISTER(cb)
Structure for a data store type.
static int acf_curlopt_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
struct ast_str * ast_str_create(size_t init_len)
Create a malloc'ed dynamic length string.
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Structure for a data store object.
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.
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
Definitions to aid in the use of thread local storage.
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
struct global_curl_info global_curl_info
static const char *const global_useragent
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
int ast_module_check(const char *name)
Check if module with the name given is loaded.
#define AST_LIST_HEAD_DESTROY(head)
Destroys a list head structure.
struct curl_settings::@131 list
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
static int url_is_vulnerable(const char *url)
Check for potential HTTP injection risk.
#define ast_debug(level,...)
Log a DEBUG message.
General Asterisk PBX channel definitions.
#define ast_test_status_update(a, b, c...)
static force_inline int attribute_pure ast_strlen_zero(const char *s)
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.
Data structure associated with a custom dialplan function.
#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.
Core PBX routines and definitions.
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
static int curl_instance_init(void *data)
static int acf_curl_helper(struct ast_channel *chan, const char *cmd, char *info, char *buf, struct ast_str **input_str, ssize_t len)
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
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".
static struct ast_custom_function acf_curl
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
#define AST_TEST_UNREGISTER(cb)
static int load_module(void)
static struct ast_threadstorage thread_escapebuf
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
static int unload_module(void)
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
struct ast_datastore * ast_datastore_alloc(const struct ast_datastore_info *info, const char *uid)
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in 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.
static int acf_curlopt_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, struct ast_str **bufstr, ssize_t len)
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_THREADSTORAGE_CUSTOM(a, b, c)
Define a thread storage variable, with custom initialization and cleanup.
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. ...
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
static void curlds_free(void *data)
Standard Command Line Interface.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
#define AST_TEST_DEFINE(hdr)
static int acf_curlopt_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
#define AST_APP_ARG(name)
Define an application argument.
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
void ast_str_trim_blanks(struct ast_str *buf)
Trims trailing whitespace characters from an ast_str string.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
static struct ast_datastore_info curl_info
#define ASTERISK_GPL_KEY
The text the key() function should return.
Asterisk module definitions.
static int acf_curl_exec(struct ast_channel *chan, const char *cmd, char *info, char *buf, size_t len)
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
#define CURLOPT_SPECIAL_HASHCOMPAT
#define ast_custom_function_register(acf)
Register a custom function.
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
static void curl_instance_cleanup(void *data)