summaryrefslogtreecommitdiff
path: root/funcs
diff options
context:
space:
mode:
authorMatthew Jordan <mjordan@digium.com>2014-10-26 01:21:18 +0000
committerJoshua Colp <jcolp@digium.com>2016-03-23 11:46:32 -0300
commit6bbcfb34bd411ec88a02e6c655b0d5b9821495a4 (patch)
tree32ec12b55c8a2bf682c10bccb4cd7699ec5e80bd /funcs
parentc21cee80cc21fd48f29f23eabb7206417c4a7ef6 (diff)
funcs/func_curl: Add the ability for CURL to download and store files
This patch adds a write option to the CURL dialplan function, allowing it to CURL files and store them locally. The value 'written' to the CURL URL specifies the location on disk to store the file. As an example: same => n,Set(CURL(http://1.1.1.1/foo.wav)=/tmp/foo.wav) Would retrieve the file foo.wav from the remote server and store it in the /tmp directory. Due to the potentially dangerous nature of this function call, APIs are forbidden from using the write functionality unless live_dangerously is set to True in asterisk.conf. ASTERISK-25652 #close Change-Id: I44f4ad823d7d20f04ceaad3698c5c7f653c41b0d
Diffstat (limited to 'funcs')
-rw-r--r--funcs/func_curl.c212
1 files changed, 137 insertions, 75 deletions
diff --git a/funcs/func_curl.c b/funcs/func_curl.c
index fd03fc375..6a8c36767 100644
--- a/funcs/func_curl.c
+++ b/funcs/func_curl.c
@@ -58,15 +58,39 @@ ASTERISK_REGISTER_FILE()
Retrieve content from a remote web or ftp server
</synopsis>
<syntax>
- <parameter name="url" required="true" />
+ <parameter name="url" required="true">
+ <para>The full URL for the resource to retrieve.</para>
+ </parameter>
<parameter name="post-data">
+ <para><emphasis>Read Only</emphasis></para>
<para>If specified, an <literal>HTTP POST</literal> will be
performed with the content of
<replaceable>post-data</replaceable>, instead of an
<literal>HTTP GET</literal> (default).</para>
</parameter>
</syntax>
- <description />
+ <description>
+ <para>When this function is read, a <literal>HTTP GET</literal>
+ (by default) will be used to retrieve the contents of the provided
+ <replaceable>url</replaceable>. The contents are returned as the
+ result of the function.</para>
+ <example title="Displaying contents of a page" language="text">
+ exten => s,1,Verbose(0, ${CURL(http://localhost:8088/static/astman.css)})
+ </example>
+ <para>When this function is written to, a <literal>HTTP GET</literal>
+ will be used to retrieve the contents of the provided
+ <replaceable>url</replaceable>. The value written to the function
+ specifies the destination file of the cURL'd resource.</para>
+ <example title="Retrieving a file" language="text">
+ exten => s,1,Set(CURL(http://localhost:8088/static/astman.css)=/var/spool/asterisk/tmp/astman.css))
+ </example>
+ <note>
+ <para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
+ is set to <literal>no</literal>, this function can only be written to from the
+ dialplan, and not directly from external protocols. Read operations are
+ unaffected.</para>
+ </note>
+ </description>
<see-also>
<ref type="function">CURLOPT</ref>
</see-also>
@@ -526,16 +550,27 @@ static int acf_curlopt_read2(struct ast_channel *chan, const char *cmd, char *da
return acf_curlopt_helper(chan, cmd, data, NULL, buf, len);
}
+/*! \brief Callback data passed to \ref WriteMemoryCallback */
+struct curl_write_callback_data {
+ /*! \brief If a string is being built, the string buffer */
+ struct ast_str *str;
+ /*! \brief The max size of \ref str */
+ ssize_t len;
+ /*! \brief If a file is being retrieved, the file to write to */
+ FILE *out_file;
+};
+
static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
{
- register int realsize = size * nmemb;
- struct ast_str **pstr = (struct ast_str **)data;
-
- 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));
-
- ast_str_append_substr(pstr, 0, ptr, realsize);
-
- ast_debug(3, "Now, len=%zu, used=%zu\n", ast_str_size(*pstr), ast_str_strlen(*pstr));
+ register int realsize = 0;
+ struct curl_write_callback_data *cb_data = data;
+
+ if (cb_data->str) {
+ realsize = size * nmemb;
+ ast_str_append_substr(&cb_data->str, 0, ptr, realsize);
+ } else if (cb_data->out_file) {
+ realsize = fwrite(ptr, size, nmemb, cb_data->out_file);
+ }
return realsize;
}
@@ -594,15 +629,16 @@ static int url_is_vulnerable(const char *url)
return 0;
}
-static int acf_curl_helper(struct ast_channel *chan, const char *cmd, char *info, char *buf, struct ast_str **input_str, ssize_t len)
+struct curl_args {
+ const char *url;
+ const char *postdata;
+ struct curl_write_callback_data cb_data;
+};
+
+static int acf_curl_helper(struct ast_channel *chan, struct curl_args *args)
{
struct ast_str *escapebuf = ast_str_thread_get(&thread_escapebuf, 16);
- struct ast_str *str = ast_str_create(16);
int ret = -1;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(url);
- AST_APP_ARG(postdata);
- );
CURL **curl;
struct curl_settings *cur;
struct ast_datastore *store = NULL;
@@ -610,29 +646,17 @@ static int acf_curl_helper(struct ast_channel *chan, const char *cmd, char *info
AST_LIST_HEAD(global_curl_info, curl_settings) *list = NULL;
char curl_errbuf[CURL_ERROR_SIZE + 1]; /* add one to be safe */
- if (buf) {
- *buf = '\0';
- }
-
- if (!str) {
- return -1;
- }
-
if (!escapebuf) {
- ast_free(str);
return -1;
}
- if (ast_strlen_zero(info)) {
- ast_log(LOG_WARNING, "CURL requires an argument (URL)\n");
- ast_free(str);
+ if (!(curl = ast_threadstorage_get(&curl_instance, sizeof(*curl)))) {
+ ast_log(LOG_ERROR, "Cannot allocate curl structure\n");
return -1;
}
- AST_STANDARD_APP_ARGS(args, info);
-
- if (url_is_vulnerable(args.url)) {
- ast_log(LOG_ERROR, "URL '%s' is vulnerable to HTTP injection attacks. Aborting CURL() call.\n", args.url);
+ if (url_is_vulnerable(args->url)) {
+ ast_log(LOG_ERROR, "URL '%s' is vulnerable to HTTP injection attacks. Aborting CURL() call.\n", args->url);
return -1;
}
@@ -640,12 +664,6 @@ static int acf_curl_helper(struct ast_channel *chan, const char *cmd, char *info
ast_autoservice_start(chan);
}
- if (!(curl = ast_threadstorage_get(&curl_instance, sizeof(*curl)))) {
- ast_log(LOG_ERROR, "Cannot allocate curl structure\n");
- ast_free(str);
- return -1;
- }
-
AST_LIST_LOCK(&global_curl_info);
AST_LIST_TRAVERSE(&global_curl_info, cur, list) {
if (cur->key == CURLOPT_SPECIAL_HASHCOMPAT) {
@@ -668,12 +686,12 @@ static int acf_curl_helper(struct ast_channel *chan, const char *cmd, char *info
}
}
- curl_easy_setopt(*curl, CURLOPT_URL, args.url);
- curl_easy_setopt(*curl, CURLOPT_FILE, (void *) &str);
+ curl_easy_setopt(*curl, CURLOPT_URL, args->url);
+ curl_easy_setopt(*curl, CURLOPT_FILE, (void *) &args->cb_data);
- if (args.postdata) {
+ if (args->postdata) {
curl_easy_setopt(*curl, CURLOPT_POST, 1);
- curl_easy_setopt(*curl, CURLOPT_POSTFIELDS, args.postdata);
+ curl_easy_setopt(*curl, CURLOPT_POSTFIELDS, args->postdata);
}
/* Temporarily assign a buffer for curl to write errors to. */
@@ -681,7 +699,7 @@ static int acf_curl_helper(struct ast_channel *chan, const char *cmd, char *info
curl_easy_setopt(*curl, CURLOPT_ERRORBUFFER, curl_errbuf);
if (curl_easy_perform(*curl) != 0) {
- ast_log(LOG_WARNING, "%s ('%s')\n", curl_errbuf, args.url);
+ ast_log(LOG_WARNING, "%s ('%s')\n", curl_errbuf, args->url);
}
/* Reset buffer to NULL so curl doesn't try to write to it when the
@@ -694,19 +712,19 @@ static int acf_curl_helper(struct ast_channel *chan, const char *cmd, char *info
AST_LIST_UNLOCK(list);
}
- if (args.postdata) {
+ if (args->postdata) {
curl_easy_setopt(*curl, CURLOPT_POST, 0);
}
- if (ast_str_strlen(str)) {
- ast_str_trim_blanks(str);
+ if (args->cb_data.str && ast_str_strlen(args->cb_data.str)) {
+ ast_str_trim_blanks(args->cb_data.str);
- ast_debug(3, "str='%s'\n", ast_str_buffer(str));
+ ast_debug(3, "CURL returned str='%s'\n", ast_str_buffer(args->cb_data.str));
if (hashcompat) {
- char *remainder = ast_str_buffer(str);
+ char *remainder = ast_str_buffer(args->cb_data.str);
char *piece;
- struct ast_str *fields = ast_str_create(ast_str_strlen(str) / 2);
- struct ast_str *values = ast_str_create(ast_str_strlen(str) / 2);
+ struct ast_str *fields = ast_str_create(ast_str_strlen(args->cb_data.str) / 2);
+ struct ast_str *values = ast_str_create(ast_str_strlen(args->cb_data.str) / 2);
int rowcount = 0;
while (fields && values && (piece = strsep(&remainder, "&"))) {
char *name = strsep(&piece, "=");
@@ -720,49 +738,93 @@ static int acf_curl_helper(struct ast_channel *chan, const char *cmd, char *info
rowcount++;
}
pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(fields));
- if (buf) {
- ast_copy_string(buf, ast_str_buffer(values), len);
- } else {
- ast_str_set(input_str, len, "%s", ast_str_buffer(values));
- }
+ ast_str_set(&args->cb_data.str, 0, "%s", ast_str_buffer(values));
ast_free(fields);
ast_free(values);
- } else {
- if (buf) {
- ast_copy_string(buf, ast_str_buffer(str), len);
- } else {
- ast_str_set(input_str, len, "%s", ast_str_buffer(str));
- }
}
ret = 0;
}
- ast_free(str);
- if (chan)
+ if (chan) {
ast_autoservice_stop(chan);
+ }
return ret;
}
-static int acf_curl_exec(struct ast_channel *chan, const char *cmd, char *info, char *buf, size_t len)
+static int acf_curl_exec(struct ast_channel *chan, const char *cmd, char *info, struct ast_str **buf, ssize_t len)
{
- return acf_curl_helper(chan, cmd, info, buf, NULL, len);
+ struct curl_args curl_params = { 0, };
+ int res;
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(url);
+ AST_APP_ARG(postdata);
+ );
+
+ AST_STANDARD_APP_ARGS(args, info);
+
+ if (ast_strlen_zero(info)) {
+ ast_log(LOG_WARNING, "CURL requires an argument (URL)\n");
+ return -1;
+ }
+
+ curl_params.url = args.url;
+ curl_params.postdata = args.postdata;
+ curl_params.cb_data.str = ast_str_create(16);
+ if (!curl_params.cb_data.str) {
+ return -1;
+ }
+
+ res = acf_curl_helper(chan, &curl_params);
+ ast_str_set(buf, len, "%s", ast_str_buffer(curl_params.cb_data.str));
+ ast_free(curl_params.cb_data.str);
+
+ return res;
}
-static int acf_curl2_exec(struct ast_channel *chan, const char *cmd, char *info, struct ast_str **buf, ssize_t len)
+static int acf_curl_write(struct ast_channel *chan, const char *cmd, char *name, const char *value)
{
- return acf_curl_helper(chan, cmd, info, NULL, buf, len);
+ struct curl_args curl_params = { 0, };
+ int res;
+ char *args_value = ast_strdupa(value);
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(file_path);
+ );
+
+ AST_STANDARD_APP_ARGS(args, args_value);
+
+ if (ast_strlen_zero(name)) {
+ ast_log(LOG_WARNING, "CURL requires an argument (URL)\n");
+ return -1;
+ }
+
+ if (ast_strlen_zero(args.file_path)) {
+ ast_log(LOG_WARNING, "CURL requires a file to write\n");
+ return -1;
+ }
+
+ curl_params.url = name;
+ curl_params.cb_data.out_file = fopen(args.file_path, "w");
+ if (!curl_params.cb_data.out_file) {
+ ast_log(LOG_WARNING, "Failed to open file %s: %s (%d)\n",
+ args.file_path,
+ strerror(errno),
+ errno);
+ return -1;
+ }
+
+ res = acf_curl_helper(chan, &curl_params);
+
+ fclose(curl_params.cb_data.out_file);
+
+ return res;
}
static struct ast_custom_function acf_curl = {
.name = "CURL",
- .synopsis = "Retrieves the contents of a URL",
- .syntax = "CURL(url[,post-data])",
- .desc =
- " url - URL to retrieve\n"
- " post-data - Optional data to send as a POST (GET is default action)\n",
- .read = acf_curl_exec,
- .read2 = acf_curl2_exec,
+ .read2 = acf_curl_exec,
+ .write = acf_curl_write,
};
static struct ast_custom_function acf_curlopt = {
@@ -865,7 +927,7 @@ static int load_module(void)
}
}
- res = ast_custom_function_register(&acf_curl);
+ res = ast_custom_function_register_escalating(&acf_curl, AST_CFE_WRITE);
res |= ast_custom_function_register(&acf_curlopt);
AST_TEST_REGISTER(vulnerable_url);