00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #include "asterisk.h"
00039
00040 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 431331 $")
00041
00042 #include <curl/curl.h>
00043
00044 #include "asterisk/lock.h"
00045 #include "asterisk/file.h"
00046 #include "asterisk/channel.h"
00047 #include "asterisk/pbx.h"
00048 #include "asterisk/cli.h"
00049 #include "asterisk/module.h"
00050 #include "asterisk/app.h"
00051 #include "asterisk/utils.h"
00052 #include "asterisk/threadstorage.h"
00053 #include "asterisk/test.h"
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162 #define CURLVERSION_ATLEAST(a,b,c) \
00163 ((LIBCURL_VERSION_MAJOR > (a)) || ((LIBCURL_VERSION_MAJOR == (a)) && (LIBCURL_VERSION_MINOR > (b))) || ((LIBCURL_VERSION_MAJOR == (a)) && (LIBCURL_VERSION_MINOR == (b)) && (LIBCURL_VERSION_PATCH >= (c))))
00164
00165 #define CURLOPT_SPECIAL_HASHCOMPAT -500
00166
00167 static void curlds_free(void *data);
00168
00169 static const struct ast_datastore_info curl_info = {
00170 .type = "CURL",
00171 .destroy = curlds_free,
00172 };
00173
00174 struct curl_settings {
00175 AST_LIST_ENTRY(curl_settings) list;
00176 CURLoption key;
00177 void *value;
00178 };
00179
00180 AST_LIST_HEAD_STATIC(global_curl_info, curl_settings);
00181
00182 static void curlds_free(void *data)
00183 {
00184 AST_LIST_HEAD(global_curl_info, curl_settings) *list = data;
00185 struct curl_settings *setting;
00186 if (!list) {
00187 return;
00188 }
00189 while ((setting = AST_LIST_REMOVE_HEAD(list, list))) {
00190 free(setting);
00191 }
00192 AST_LIST_HEAD_DESTROY(list);
00193 }
00194
00195 enum optiontype {
00196 OT_BOOLEAN,
00197 OT_INTEGER,
00198 OT_INTEGER_MS,
00199 OT_STRING,
00200 OT_ENUM,
00201 };
00202
00203 static int parse_curlopt_key(const char *name, CURLoption *key, enum optiontype *ot)
00204 {
00205 if (!strcasecmp(name, "header")) {
00206 *key = CURLOPT_HEADER;
00207 *ot = OT_BOOLEAN;
00208 } else if (!strcasecmp(name, "proxy")) {
00209 *key = CURLOPT_PROXY;
00210 *ot = OT_STRING;
00211 } else if (!strcasecmp(name, "proxyport")) {
00212 *key = CURLOPT_PROXYPORT;
00213 *ot = OT_INTEGER;
00214 } else if (!strcasecmp(name, "proxytype")) {
00215 *key = CURLOPT_PROXYTYPE;
00216 *ot = OT_ENUM;
00217 } else if (!strcasecmp(name, "dnstimeout")) {
00218 *key = CURLOPT_DNS_CACHE_TIMEOUT;
00219 *ot = OT_INTEGER;
00220 } else if (!strcasecmp(name, "userpwd")) {
00221 *key = CURLOPT_USERPWD;
00222 *ot = OT_STRING;
00223 } else if (!strcasecmp(name, "proxyuserpwd")) {
00224 *key = CURLOPT_PROXYUSERPWD;
00225 *ot = OT_STRING;
00226 } else if (!strcasecmp(name, "maxredirs")) {
00227 *key = CURLOPT_MAXREDIRS;
00228 *ot = OT_INTEGER;
00229 } else if (!strcasecmp(name, "referer")) {
00230 *key = CURLOPT_REFERER;
00231 *ot = OT_STRING;
00232 } else if (!strcasecmp(name, "useragent")) {
00233 *key = CURLOPT_USERAGENT;
00234 *ot = OT_STRING;
00235 } else if (!strcasecmp(name, "cookie")) {
00236 *key = CURLOPT_COOKIE;
00237 *ot = OT_STRING;
00238 } else if (!strcasecmp(name, "ftptimeout")) {
00239 *key = CURLOPT_FTP_RESPONSE_TIMEOUT;
00240 *ot = OT_INTEGER;
00241 } else if (!strcasecmp(name, "httptimeout")) {
00242 #if CURLVERSION_ATLEAST(7,16,2)
00243 *key = CURLOPT_TIMEOUT_MS;
00244 *ot = OT_INTEGER_MS;
00245 #else
00246 *key = CURLOPT_TIMEOUT;
00247 *ot = OT_INTEGER;
00248 #endif
00249 } else if (!strcasecmp(name, "conntimeout")) {
00250 #if CURLVERSION_ATLEAST(7,16,2)
00251 *key = CURLOPT_CONNECTTIMEOUT_MS;
00252 *ot = OT_INTEGER_MS;
00253 #else
00254 *key = CURLOPT_CONNECTTIMEOUT;
00255 *ot = OT_INTEGER;
00256 #endif
00257 } else if (!strcasecmp(name, "ftptext")) {
00258 *key = CURLOPT_TRANSFERTEXT;
00259 *ot = OT_BOOLEAN;
00260 } else if (!strcasecmp(name, "ssl_verifypeer")) {
00261 *key = CURLOPT_SSL_VERIFYPEER;
00262 *ot = OT_BOOLEAN;
00263 } else if (!strcasecmp(name, "hashcompat")) {
00264 *key = CURLOPT_SPECIAL_HASHCOMPAT;
00265 *ot = OT_BOOLEAN;
00266 } else {
00267 return -1;
00268 }
00269 return 0;
00270 }
00271
00272 static int acf_curlopt_write(struct ast_channel *chan, const char *cmd, char *name, const char *value)
00273 {
00274 struct ast_datastore *store;
00275 struct global_curl_info *list;
00276 struct curl_settings *cur, *new = NULL;
00277 CURLoption key;
00278 enum optiontype ot;
00279
00280 if (chan) {
00281 if (!(store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
00282
00283 if (!(store = ast_datastore_alloc(&curl_info, NULL))) {
00284 ast_log(LOG_ERROR, "Unable to allocate new datastore. Cannot set any CURL options\n");
00285 return -1;
00286 }
00287
00288 if (!(list = ast_calloc(1, sizeof(*list)))) {
00289 ast_log(LOG_ERROR, "Unable to allocate list head. Cannot set any CURL options\n");
00290 ast_datastore_free(store);
00291 return -1;
00292 }
00293
00294 store->data = list;
00295 AST_LIST_HEAD_INIT(list);
00296 ast_channel_datastore_add(chan, store);
00297 } else {
00298 list = store->data;
00299 }
00300 } else {
00301
00302 list = &global_curl_info;
00303 }
00304
00305 if (!parse_curlopt_key(name, &key, &ot)) {
00306 if (ot == OT_BOOLEAN) {
00307 if ((new = ast_calloc(1, sizeof(*new)))) {
00308 new->value = (void *)((long) ast_true(value));
00309 }
00310 } else if (ot == OT_INTEGER) {
00311 long tmp = atol(value);
00312 if ((new = ast_calloc(1, sizeof(*new)))) {
00313 new->value = (void *)tmp;
00314 }
00315 } else if (ot == OT_INTEGER_MS) {
00316 long tmp = atof(value) * 1000.0;
00317 if ((new = ast_calloc(1, sizeof(*new)))) {
00318 new->value = (void *)tmp;
00319 }
00320 } else if (ot == OT_STRING) {
00321 if ((new = ast_calloc(1, sizeof(*new) + strlen(value) + 1))) {
00322 new->value = (char *)new + sizeof(*new);
00323 strcpy(new->value, value);
00324 }
00325 } else if (ot == OT_ENUM) {
00326 if (key == CURLOPT_PROXYTYPE) {
00327 long ptype =
00328 #if CURLVERSION_ATLEAST(7,10,0)
00329 CURLPROXY_HTTP;
00330 #else
00331 CURLPROXY_SOCKS5;
00332 #endif
00333 if (0) {
00334 #if CURLVERSION_ATLEAST(7,15,2)
00335 } else if (!strcasecmp(value, "socks4")) {
00336 ptype = CURLPROXY_SOCKS4;
00337 #endif
00338 #if CURLVERSION_ATLEAST(7,18,0)
00339 } else if (!strcasecmp(value, "socks4a")) {
00340 ptype = CURLPROXY_SOCKS4A;
00341 #endif
00342 #if CURLVERSION_ATLEAST(7,18,0)
00343 } else if (!strcasecmp(value, "socks5")) {
00344 ptype = CURLPROXY_SOCKS5;
00345 #endif
00346 #if CURLVERSION_ATLEAST(7,18,0)
00347 } else if (!strncasecmp(value, "socks5", 6)) {
00348 ptype = CURLPROXY_SOCKS5_HOSTNAME;
00349 #endif
00350 }
00351
00352 if ((new = ast_calloc(1, sizeof(*new)))) {
00353 new->value = (void *)ptype;
00354 }
00355 } else {
00356
00357 goto yuck;
00358 }
00359 }
00360
00361
00362 if (!new) {
00363 return -1;
00364 }
00365
00366 new->key = key;
00367 } else {
00368 yuck:
00369 ast_log(LOG_ERROR, "Unrecognized option: %s\n", name);
00370 return -1;
00371 }
00372
00373
00374 AST_LIST_LOCK(list);
00375 AST_LIST_TRAVERSE_SAFE_BEGIN(list, cur, list) {
00376 if (cur->key == new->key) {
00377 AST_LIST_REMOVE_CURRENT(list);
00378 free(cur);
00379 break;
00380 }
00381 }
00382 AST_LIST_TRAVERSE_SAFE_END
00383
00384
00385 ast_debug(1, "Inserting entry %p with key %d and value %p\n", new, new->key, new->value);
00386 AST_LIST_INSERT_TAIL(list, new, list);
00387 AST_LIST_UNLOCK(list);
00388
00389 return 0;
00390 }
00391
00392 static int acf_curlopt_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, struct ast_str **bufstr, ssize_t len)
00393 {
00394 struct ast_datastore *store;
00395 struct global_curl_info *list[2] = { &global_curl_info, NULL };
00396 struct curl_settings *cur = NULL;
00397 CURLoption key;
00398 enum optiontype ot;
00399 int i;
00400
00401 if (parse_curlopt_key(data, &key, &ot)) {
00402 ast_log(LOG_ERROR, "Unrecognized option: '%s'\n", data);
00403 return -1;
00404 }
00405
00406 if (chan && (store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
00407 list[0] = store->data;
00408 list[1] = &global_curl_info;
00409 }
00410
00411 for (i = 0; i < 2; i++) {
00412 if (!list[i]) {
00413 break;
00414 }
00415 AST_LIST_LOCK(list[i]);
00416 AST_LIST_TRAVERSE(list[i], cur, list) {
00417 if (cur->key == key) {
00418 if (ot == OT_BOOLEAN || ot == OT_INTEGER) {
00419 if (buf) {
00420 snprintf(buf, len, "%ld", (long) cur->value);
00421 } else {
00422 ast_str_set(bufstr, len, "%ld", (long) cur->value);
00423 }
00424 } else if (ot == OT_INTEGER_MS) {
00425 if ((long) cur->value % 1000 == 0) {
00426 if (buf) {
00427 snprintf(buf, len, "%ld", (long)cur->value / 1000);
00428 } else {
00429 ast_str_set(bufstr, len, "%ld", (long) cur->value / 1000);
00430 }
00431 } else {
00432 if (buf) {
00433 snprintf(buf, len, "%.3f", (double) ((long) cur->value) / 1000.0);
00434 } else {
00435 ast_str_set(bufstr, len, "%.3f", (double) ((long) cur->value) / 1000.0);
00436 }
00437 }
00438 } else if (ot == OT_STRING) {
00439 ast_debug(1, "Found entry %p, with key %d and value %p\n", cur, cur->key, cur->value);
00440 if (buf) {
00441 ast_copy_string(buf, cur->value, len);
00442 } else {
00443 ast_str_set(bufstr, 0, "%s", (char *) cur->value);
00444 }
00445 } else if (key == CURLOPT_PROXYTYPE) {
00446 if (0) {
00447 #if CURLVERSION_ATLEAST(7,15,2)
00448 } else if ((long)cur->value == CURLPROXY_SOCKS4) {
00449 if (buf) {
00450 ast_copy_string(buf, "socks4", len);
00451 } else {
00452 ast_str_set(bufstr, 0, "socks4");
00453 }
00454 #endif
00455 #if CURLVERSION_ATLEAST(7,18,0)
00456 } else if ((long)cur->value == CURLPROXY_SOCKS4A) {
00457 if (buf) {
00458 ast_copy_string(buf, "socks4a", len);
00459 } else {
00460 ast_str_set(bufstr, 0, "socks4a");
00461 }
00462 #endif
00463 } else if ((long)cur->value == CURLPROXY_SOCKS5) {
00464 if (buf) {
00465 ast_copy_string(buf, "socks5", len);
00466 } else {
00467 ast_str_set(bufstr, 0, "socks5");
00468 }
00469 #if CURLVERSION_ATLEAST(7,18,0)
00470 } else if ((long)cur->value == CURLPROXY_SOCKS5_HOSTNAME) {
00471 if (buf) {
00472 ast_copy_string(buf, "socks5hostname", len);
00473 } else {
00474 ast_str_set(bufstr, 0, "socks5hostname");
00475 }
00476 #endif
00477 #if CURLVERSION_ATLEAST(7,10,0)
00478 } else if ((long)cur->value == CURLPROXY_HTTP) {
00479 if (buf) {
00480 ast_copy_string(buf, "http", len);
00481 } else {
00482 ast_str_set(bufstr, 0, "http");
00483 }
00484 #endif
00485 } else {
00486 if (buf) {
00487 ast_copy_string(buf, "unknown", len);
00488 } else {
00489 ast_str_set(bufstr, 0, "unknown");
00490 }
00491 }
00492 }
00493 break;
00494 }
00495 }
00496 AST_LIST_UNLOCK(list[i]);
00497 if (cur) {
00498 break;
00499 }
00500 }
00501
00502 return cur ? 0 : -1;
00503 }
00504
00505 static int acf_curlopt_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00506 {
00507 return acf_curlopt_helper(chan, cmd, data, buf, NULL, len);
00508 }
00509
00510 static int acf_curlopt_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
00511 {
00512 return acf_curlopt_helper(chan, cmd, data, NULL, buf, len);
00513 }
00514
00515 static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
00516 {
00517 register int realsize = size * nmemb;
00518 struct ast_str **pstr = (struct ast_str **)data;
00519
00520 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));
00521
00522 ast_str_append_substr(pstr, 0, ptr, realsize);
00523
00524 ast_debug(3, "Now, len=%zu, used=%zu\n", ast_str_size(*pstr), ast_str_strlen(*pstr));
00525
00526 return realsize;
00527 }
00528
00529 static const char * const global_useragent = "asterisk-libcurl-agent/1.0";
00530
00531 static int curl_instance_init(void *data)
00532 {
00533 CURL **curl = data;
00534
00535 if (!(*curl = curl_easy_init()))
00536 return -1;
00537
00538 curl_easy_setopt(*curl, CURLOPT_NOSIGNAL, 1);
00539 curl_easy_setopt(*curl, CURLOPT_TIMEOUT, 180);
00540 curl_easy_setopt(*curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
00541 curl_easy_setopt(*curl, CURLOPT_USERAGENT, global_useragent);
00542
00543 return 0;
00544 }
00545
00546 static void curl_instance_cleanup(void *data)
00547 {
00548 CURL **curl = data;
00549
00550 curl_easy_cleanup(*curl);
00551
00552 ast_free(data);
00553 }
00554
00555 AST_THREADSTORAGE_CUSTOM(curl_instance, curl_instance_init, curl_instance_cleanup);
00556 AST_THREADSTORAGE(thread_escapebuf);
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574 static int url_is_vulnerable(const char *url)
00575 {
00576 if (strpbrk(url, "\r\n")) {
00577 return 1;
00578 }
00579
00580 return 0;
00581 }
00582
00583 static int acf_curl_helper(struct ast_channel *chan, const char *cmd, char *info, char *buf, struct ast_str **input_str, ssize_t len)
00584 {
00585 struct ast_str *escapebuf = ast_str_thread_get(&thread_escapebuf, 16);
00586 struct ast_str *str = ast_str_create(16);
00587 int ret = -1;
00588 AST_DECLARE_APP_ARGS(args,
00589 AST_APP_ARG(url);
00590 AST_APP_ARG(postdata);
00591 );
00592 CURL **curl;
00593 struct curl_settings *cur;
00594 struct ast_datastore *store = NULL;
00595 int hashcompat = 0;
00596 AST_LIST_HEAD(global_curl_info, curl_settings) *list = NULL;
00597 char curl_errbuf[CURL_ERROR_SIZE + 1];
00598
00599 if (buf) {
00600 *buf = '\0';
00601 }
00602
00603 if (!str) {
00604 return -1;
00605 }
00606
00607 if (!escapebuf) {
00608 ast_free(str);
00609 return -1;
00610 }
00611
00612 if (ast_strlen_zero(info)) {
00613 ast_log(LOG_WARNING, "CURL requires an argument (URL)\n");
00614 ast_free(str);
00615 return -1;
00616 }
00617
00618 AST_STANDARD_APP_ARGS(args, info);
00619
00620 if (url_is_vulnerable(args.url)) {
00621 ast_log(LOG_ERROR, "URL '%s' is vulnerable to HTTP injection attacks. Aborting CURL() call.\n", args.url);
00622 return -1;
00623 }
00624
00625 if (chan) {
00626 ast_autoservice_start(chan);
00627 }
00628
00629 if (!(curl = ast_threadstorage_get(&curl_instance, sizeof(*curl)))) {
00630 ast_log(LOG_ERROR, "Cannot allocate curl structure\n");
00631 ast_free(str);
00632 return -1;
00633 }
00634
00635 AST_LIST_LOCK(&global_curl_info);
00636 AST_LIST_TRAVERSE(&global_curl_info, cur, list) {
00637 if (cur->key == CURLOPT_SPECIAL_HASHCOMPAT) {
00638 hashcompat = (cur->value != NULL) ? 1 : 0;
00639 } else {
00640 curl_easy_setopt(*curl, cur->key, cur->value);
00641 }
00642 }
00643
00644 if (chan && (store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
00645 list = store->data;
00646 AST_LIST_LOCK(list);
00647 AST_LIST_TRAVERSE(list, cur, list) {
00648 if (cur->key == CURLOPT_SPECIAL_HASHCOMPAT) {
00649 hashcompat = (cur->value != NULL) ? 1 : 0;
00650 } else {
00651 curl_easy_setopt(*curl, cur->key, cur->value);
00652 }
00653 }
00654 }
00655
00656 curl_easy_setopt(*curl, CURLOPT_URL, args.url);
00657 curl_easy_setopt(*curl, CURLOPT_FILE, (void *) &str);
00658
00659 if (args.postdata) {
00660 curl_easy_setopt(*curl, CURLOPT_POST, 1);
00661 curl_easy_setopt(*curl, CURLOPT_POSTFIELDS, args.postdata);
00662 }
00663
00664
00665 curl_errbuf[0] = curl_errbuf[CURL_ERROR_SIZE] = '\0';
00666 curl_easy_setopt(*curl, CURLOPT_ERRORBUFFER, curl_errbuf);
00667
00668 if (curl_easy_perform(*curl) != 0) {
00669 ast_log(LOG_WARNING, "%s ('%s')\n", curl_errbuf, args.url);
00670 }
00671
00672
00673
00674
00675
00676 curl_easy_setopt(*curl, CURLOPT_ERRORBUFFER, (char*)NULL);
00677
00678 if (store) {
00679 AST_LIST_UNLOCK(list);
00680 }
00681 AST_LIST_UNLOCK(&global_curl_info);
00682
00683 if (args.postdata) {
00684 curl_easy_setopt(*curl, CURLOPT_POST, 0);
00685 }
00686
00687 if (ast_str_strlen(str)) {
00688 ast_str_trim_blanks(str);
00689
00690 ast_debug(3, "str='%s'\n", ast_str_buffer(str));
00691 if (hashcompat) {
00692 char *remainder = ast_str_buffer(str);
00693 char *piece;
00694 struct ast_str *fields = ast_str_create(ast_str_strlen(str) / 2);
00695 struct ast_str *values = ast_str_create(ast_str_strlen(str) / 2);
00696 int rowcount = 0;
00697 while (fields && values && (piece = strsep(&remainder, "&"))) {
00698 char *name = strsep(&piece, "=");
00699 if (piece) {
00700 ast_uri_decode(piece);
00701 }
00702 ast_uri_decode(name);
00703 ast_str_append(&fields, 0, "%s%s", rowcount ? "," : "", ast_str_set_escapecommas(&escapebuf, 0, name, INT_MAX));
00704 ast_str_append(&values, 0, "%s%s", rowcount ? "," : "", ast_str_set_escapecommas(&escapebuf, 0, S_OR(piece, ""), INT_MAX));
00705 rowcount++;
00706 }
00707 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(fields));
00708 if (buf) {
00709 ast_copy_string(buf, ast_str_buffer(values), len);
00710 } else {
00711 ast_str_set(input_str, len, "%s", ast_str_buffer(values));
00712 }
00713 ast_free(fields);
00714 ast_free(values);
00715 } else {
00716 if (buf) {
00717 ast_copy_string(buf, ast_str_buffer(str), len);
00718 } else {
00719 ast_str_set(input_str, len, "%s", ast_str_buffer(str));
00720 }
00721 }
00722 ret = 0;
00723 }
00724 ast_free(str);
00725
00726 if (chan)
00727 ast_autoservice_stop(chan);
00728
00729 return ret;
00730 }
00731
00732 static int acf_curl_exec(struct ast_channel *chan, const char *cmd, char *info, char *buf, size_t len)
00733 {
00734 return acf_curl_helper(chan, cmd, info, buf, NULL, len);
00735 }
00736
00737 static int acf_curl2_exec(struct ast_channel *chan, const char *cmd, char *info, struct ast_str **buf, ssize_t len)
00738 {
00739 return acf_curl_helper(chan, cmd, info, NULL, buf, len);
00740 }
00741
00742 static struct ast_custom_function acf_curl = {
00743 .name = "CURL",
00744 .synopsis = "Retrieves the contents of a URL",
00745 .syntax = "CURL(url[,post-data])",
00746 .desc =
00747 " url - URL to retrieve\n"
00748 " post-data - Optional data to send as a POST (GET is default action)\n",
00749 .read = acf_curl_exec,
00750 .read2 = acf_curl2_exec,
00751 };
00752
00753 static struct ast_custom_function acf_curlopt = {
00754 .name = "CURLOPT",
00755 .synopsis = "Set options for use with the CURL() function",
00756 .syntax = "CURLOPT(<option>)",
00757 .desc =
00758 " cookie - Send cookie with request [none]\n"
00759 " conntimeout - Number of seconds to wait for connection\n"
00760 " dnstimeout - Number of seconds to wait for DNS response\n"
00761 " ftptext - For FTP, force a text transfer (boolean)\n"
00762 " ftptimeout - For FTP, the server response timeout\n"
00763 " header - Retrieve header information (boolean)\n"
00764 " httptimeout - Number of seconds to wait for HTTP response\n"
00765 " maxredirs - Maximum number of redirects to follow\n"
00766 " proxy - Hostname or IP to use as a proxy\n"
00767 " proxytype - http, socks4, or socks5\n"
00768 " proxyport - port number of the proxy\n"
00769 " proxyuserpwd - A <user>:<pass> to use for authentication\n"
00770 " referer - Referer URL to use for the request\n"
00771 " useragent - UserAgent string to use\n"
00772 " userpwd - A <user>:<pass> to use for authentication\n"
00773 " ssl_verifypeer - Whether to verify the peer certificate (boolean)\n"
00774 " hashcompat - Result data will be compatible for use with HASH()\n"
00775 "",
00776 .read = acf_curlopt_read,
00777 .read2 = acf_curlopt_read2,
00778 .write = acf_curlopt_write,
00779 };
00780
00781 AST_TEST_DEFINE(vulnerable_url)
00782 {
00783 const char *bad_urls [] = {
00784 "http://example.com\r\nDELETE http://example.com/everything",
00785 "http://example.com\rDELETE http://example.com/everything",
00786 "http://example.com\nDELETE http://example.com/everything",
00787 "\r\nhttp://example.com",
00788 "\rhttp://example.com",
00789 "\nhttp://example.com",
00790 "http://example.com\r\n",
00791 "http://example.com\r",
00792 "http://example.com\n",
00793 };
00794 const char *good_urls [] = {
00795 "http://example.com",
00796 "http://example.com/%5Cr%5Cn",
00797 };
00798 int i;
00799 enum ast_test_result_state res = AST_TEST_PASS;
00800
00801 switch (cmd) {
00802 case TEST_INIT:
00803 info->name = "vulnerable_url";
00804 info->category = "/funcs/func_curl/";
00805 info->summary = "cURL vulnerable URL test";
00806 info->description =
00807 "Ensure that any combination of '\\r' or '\\n' in a URL invalidates the URL";
00808 case TEST_EXECUTE:
00809 break;
00810 }
00811
00812 for (i = 0; i < ARRAY_LEN(bad_urls); ++i) {
00813 if (!url_is_vulnerable(bad_urls[i])) {
00814 ast_test_status_update(test, "String '%s' detected as valid when it should be invalid\n", bad_urls[i]);
00815 res = AST_TEST_FAIL;
00816 }
00817 }
00818
00819 for (i = 0; i < ARRAY_LEN(good_urls); ++i) {
00820 if (url_is_vulnerable(good_urls[i])) {
00821 ast_test_status_update(test, "String '%s' detected as invalid when it should be valid\n", good_urls[i]);
00822 res = AST_TEST_FAIL;
00823 }
00824 }
00825
00826 return res;
00827 }
00828
00829 static int unload_module(void)
00830 {
00831 int res;
00832
00833 res = ast_custom_function_unregister(&acf_curl);
00834 res |= ast_custom_function_unregister(&acf_curlopt);
00835
00836 AST_TEST_UNREGISTER(vulnerable_url);
00837
00838 return res;
00839 }
00840
00841 static int load_module(void)
00842 {
00843 int res;
00844
00845 if (!ast_module_check("res_curl.so")) {
00846 if (ast_load_resource("res_curl.so") != AST_MODULE_LOAD_SUCCESS) {
00847 ast_log(LOG_ERROR, "Cannot load res_curl, so func_curl cannot be loaded\n");
00848 return AST_MODULE_LOAD_DECLINE;
00849 }
00850 }
00851
00852 res = ast_custom_function_register(&acf_curl);
00853 res |= ast_custom_function_register(&acf_curlopt);
00854
00855 AST_TEST_REGISTER(vulnerable_url);
00856
00857 return res;
00858 }
00859
00860 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Load external URL",
00861 .load = load_module,
00862 .unload = unload_module,
00863 .load_pri = AST_MODPRI_REALTIME_DEPEND2,
00864 );
00865