1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24#include "tool_setup.h" 25 26#include "strcase.h" 27 28#define ENABLE_CURLX_PRINTF 29/* use our own printf() functions */ 30#include "curlx.h" 31 32#include "tool_cfgable.h" 33#include "tool_getparam.h" 34#include "tool_getpass.h" 35#include "tool_msgs.h" 36#include "tool_paramhlp.h" 37#include "tool_libinfo.h" 38#include "tool_util.h" 39#include "tool_version.h" 40#include "dynbuf.h" 41 42#include "memdebug.h" /* keep this as LAST include */ 43 44struct getout *new_getout(struct OperationConfig *config) 45{ 46 struct getout *node = calloc(1, sizeof(struct getout)); 47 struct getout *last = config->url_last; 48 if(node) { 49 static int outnum = 0; 50 51 /* append this new node last in the list */ 52 if(last) 53 last->next = node; 54 else 55 config->url_list = node; /* first node */ 56 57 /* move the last pointer */ 58 config->url_last = node; 59 60 node->flags = config->default_node_flags; 61 node->num = outnum++; 62 } 63 return node; 64} 65 66#define MAX_FILE2STRING (256*1024*1024) /* big enough ? */ 67 68ParameterError file2string(char **bufp, FILE *file) 69{ 70 struct curlx_dynbuf dyn; 71 DEBUGASSERT(MAX_FILE2STRING < INT_MAX); /* needs to fit in an int later */ 72 curlx_dyn_init(&dyn, MAX_FILE2STRING); 73 if(file) { 74 char buffer[256]; 75 76 while(fgets(buffer, sizeof(buffer), file)) { 77 char *ptr = strchr(buffer, '\r'); 78 if(ptr) 79 *ptr = '\0'; 80 ptr = strchr(buffer, '\n'); 81 if(ptr) 82 *ptr = '\0'; 83 if(curlx_dyn_add(&dyn, buffer)) 84 return PARAM_NO_MEM; 85 } 86 } 87 *bufp = curlx_dyn_ptr(&dyn); 88 return PARAM_OK; 89} 90 91ParameterError file2memory(char **bufp, size_t *size, FILE *file) 92{ 93 if(file) { 94 size_t nread; 95 struct curlx_dynbuf dyn; 96 /* The size needs to fit in an int later */ 97 DEBUGASSERT(MAX_FILE2MEMORY < INT_MAX); 98 curlx_dyn_init(&dyn, MAX_FILE2MEMORY); 99 do { 100 char buffer[4096]; 101 nread = fread(buffer, 1, sizeof(buffer), file); 102 if(ferror(file)) { 103 curlx_dyn_free(&dyn); 104 *size = 0; 105 *bufp = NULL; 106 return PARAM_READ_ERROR; 107 } 108 if(nread) 109 if(curlx_dyn_addn(&dyn, buffer, nread)) 110 return PARAM_NO_MEM; 111 } while(!feof(file)); 112 *size = curlx_dyn_len(&dyn); 113 *bufp = curlx_dyn_ptr(&dyn); 114 } 115 else { 116 *size = 0; 117 *bufp = NULL; 118 } 119 return PARAM_OK; 120} 121 122/* 123 * Parse the string and write the long in the given address. Return PARAM_OK 124 * on success, otherwise a parameter specific error enum. 125 * 126 * Since this function gets called with the 'nextarg' pointer from within the 127 * getparameter a lot, we must check it for NULL before accessing the str 128 * data. 129 */ 130static ParameterError getnum(long *val, const char *str, int base) 131{ 132 if(str) { 133 char *endptr = NULL; 134 long num; 135 if(!str[0]) 136 return PARAM_BLANK_STRING; 137 errno = 0; 138 num = strtol(str, &endptr, base); 139 if(errno == ERANGE) 140 return PARAM_NUMBER_TOO_LARGE; 141 if((endptr != str) && (*endptr == '\0')) { 142 *val = num; 143 return PARAM_OK; /* Ok */ 144 } 145 } 146 return PARAM_BAD_NUMERIC; /* badness */ 147} 148 149ParameterError str2num(long *val, const char *str) 150{ 151 return getnum(val, str, 10); 152} 153 154ParameterError oct2nummax(long *val, const char *str, long max) 155{ 156 ParameterError result = getnum(val, str, 8); 157 if(result != PARAM_OK) 158 return result; 159 else if(*val > max) 160 return PARAM_NUMBER_TOO_LARGE; 161 else if(*val < 0) 162 return PARAM_NEGATIVE_NUMERIC; 163 164 return PARAM_OK; 165} 166 167/* 168 * Parse the string and write the long in the given address. Return PARAM_OK 169 * on success, otherwise a parameter error enum. ONLY ACCEPTS POSITIVE NUMBERS! 170 * 171 * Since this function gets called with the 'nextarg' pointer from within the 172 * getparameter a lot, we must check it for NULL before accessing the str 173 * data. 174 */ 175 176ParameterError str2unum(long *val, const char *str) 177{ 178 ParameterError result = getnum(val, str, 10); 179 if(result != PARAM_OK) 180 return result; 181 if(*val < 0) 182 return PARAM_NEGATIVE_NUMERIC; 183 184 return PARAM_OK; 185} 186 187/* 188 * Parse the string and write the long in the given address if it is below the 189 * maximum allowed value. Return PARAM_OK on success, otherwise a parameter 190 * error enum. ONLY ACCEPTS POSITIVE NUMBERS! 191 * 192 * Since this function gets called with the 'nextarg' pointer from within the 193 * getparameter a lot, we must check it for NULL before accessing the str 194 * data. 195 */ 196 197ParameterError str2unummax(long *val, const char *str, long max) 198{ 199 ParameterError result = str2unum(val, str); 200 if(result != PARAM_OK) 201 return result; 202 if(*val > max) 203 return PARAM_NUMBER_TOO_LARGE; 204 205 return PARAM_OK; 206} 207 208 209/* 210 * Parse the string and write the double in the given address. Return PARAM_OK 211 * on success, otherwise a parameter specific error enum. 212 * 213 * The 'max' argument is the maximum value allowed, as the numbers are often 214 * multiplied when later used. 215 * 216 * Since this function gets called with the 'nextarg' pointer from within the 217 * getparameter a lot, we must check it for NULL before accessing the str 218 * data. 219 */ 220 221static ParameterError str2double(double *val, const char *str, double max) 222{ 223 if(str) { 224 char *endptr; 225 double num; 226 errno = 0; 227 num = strtod(str, &endptr); 228 if(errno == ERANGE) 229 return PARAM_NUMBER_TOO_LARGE; 230 if(num > max) { 231 /* too large */ 232 return PARAM_NUMBER_TOO_LARGE; 233 } 234 if((endptr != str) && (endptr == str + strlen(str))) { 235 *val = num; 236 return PARAM_OK; /* Ok */ 237 } 238 } 239 return PARAM_BAD_NUMERIC; /* badness */ 240} 241 242/* 243 * Parse the string as seconds with decimals, and write the number of 244 * milliseconds that corresponds in the given address. Return PARAM_OK on 245 * success, otherwise a parameter error enum. ONLY ACCEPTS POSITIVE NUMBERS! 246 * 247 * The 'max' argument is the maximum value allowed, as the numbers are often 248 * multiplied when later used. 249 * 250 * Since this function gets called with the 'nextarg' pointer from within the 251 * getparameter a lot, we must check it for NULL before accessing the str 252 * data. 253 */ 254 255ParameterError secs2ms(long *valp, const char *str) 256{ 257 double value; 258 ParameterError result = str2double(&value, str, (double)LONG_MAX/1000); 259 if(result != PARAM_OK) 260 return result; 261 if(value < 0) 262 return PARAM_NEGATIVE_NUMERIC; 263 264 *valp = (long)(value*1000); 265 return PARAM_OK; 266} 267 268/* 269 * Implement protocol sets in null-terminated array of protocol name pointers. 270 */ 271 272/* Return index of prototype token in set, card(set) if not found. 273 Can be called with proto == NULL to get card(set). */ 274static size_t protoset_index(const char * const *protoset, const char *proto) 275{ 276 const char * const *p = protoset; 277 278 DEBUGASSERT(proto == proto_token(proto)); /* Ensure it is tokenized. */ 279 280 for(; *p; p++) 281 if(proto == *p) 282 break; 283 return p - protoset; 284} 285 286/* Include protocol token in set. */ 287static void protoset_set(const char **protoset, const char *proto) 288{ 289 if(proto) { 290 size_t n = protoset_index(protoset, proto); 291 292 if(!protoset[n]) { 293 DEBUGASSERT(n < proto_count); 294 protoset[n] = proto; 295 protoset[n + 1] = NULL; 296 } 297 } 298} 299 300/* Exclude protocol token from set. */ 301static void protoset_clear(const char **protoset, const char *proto) 302{ 303 if(proto) { 304 size_t n = protoset_index(protoset, proto); 305 306 if(protoset[n]) { 307 size_t m = protoset_index(protoset, NULL) - 1; 308 309 protoset[n] = protoset[m]; 310 protoset[m] = NULL; 311 } 312 } 313} 314 315/* 316 * Parse the string and provide an allocated libcurl compatible protocol 317 * string output. Return non-zero on failure, zero on success. 318 * 319 * The string is a list of protocols 320 * 321 * Since this function gets called with the 'nextarg' pointer from within the 322 * getparameter a lot, we must check it for NULL before accessing the str 323 * data. 324 */ 325 326#define MAX_PROTOSTRING (64*11) /* Enough room for 64 10-chars proto names. */ 327 328ParameterError proto2num(struct OperationConfig *config, 329 const char * const *val, char **ostr, const char *str) 330{ 331 char *buffer; 332 const char *sep = ","; 333 char *token; 334 const char **protoset; 335 struct curlx_dynbuf obuf; 336 size_t proto; 337 CURLcode result; 338 339 curlx_dyn_init(&obuf, MAX_PROTOSTRING); 340 341 if(!str) 342 return PARAM_OPTION_AMBIGUOUS; 343 344 buffer = strdup(str); /* because strtok corrupts it */ 345 if(!buffer) 346 return PARAM_NO_MEM; 347 348 protoset = malloc((proto_count + 1) * sizeof(*protoset)); 349 if(!protoset) { 350 free(buffer); 351 return PARAM_NO_MEM; 352 } 353 354 /* Preset protocol set with default values. */ 355 protoset[0] = NULL; 356 for(; *val; val++) { 357 const char *p = proto_token(*val); 358 359 if(p) 360 protoset_set(protoset, p); 361 } 362 363 /* Allow strtok() here since this isn't used threaded */ 364 /* !checksrc! disable BANNEDFUNC 2 */ 365 for(token = strtok(buffer, sep); 366 token; 367 token = strtok(NULL, sep)) { 368 enum e_action { allow, deny, set } action = allow; 369 370 /* Process token modifiers */ 371 while(!ISALNUM(*token)) { /* may be NULL if token is all modifiers */ 372 switch(*token++) { 373 case '=': 374 action = set; 375 break; 376 case '-': 377 action = deny; 378 break; 379 case '+': 380 action = allow; 381 break; 382 default: /* Includes case of terminating NULL */ 383 free(buffer); 384 free((char *) protoset); 385 return PARAM_BAD_USE; 386 } 387 } 388 389 if(curl_strequal(token, "all")) { 390 switch(action) { 391 case deny: 392 protoset[0] = NULL; 393 break; 394 case allow: 395 case set: 396 memcpy((char *) protoset, 397 built_in_protos, (proto_count + 1) * sizeof(*protoset)); 398 break; 399 } 400 } 401 else { 402 const char *p = proto_token(token); 403 404 if(p) 405 switch(action) { 406 case deny: 407 protoset_clear(protoset, p); 408 break; 409 case set: 410 protoset[0] = NULL; 411 FALLTHROUGH(); 412 case allow: 413 protoset_set(protoset, p); 414 break; 415 } 416 else { /* unknown protocol */ 417 /* If they have specified only this protocol, we say treat it as 418 if no protocols are allowed */ 419 if(action == set) 420 protoset[0] = NULL; 421 warnf(config->global, "unrecognized protocol '%s'", token); 422 } 423 } 424 } 425 free(buffer); 426 427 /* We need the protocols in alphabetic order for CI tests requirements. */ 428 qsort((char *) protoset, protoset_index(protoset, NULL), sizeof(*protoset), 429 struplocompare4sort); 430 431 result = curlx_dyn_addn(&obuf, "", 0); 432 for(proto = 0; protoset[proto] && !result; proto++) 433 result = curlx_dyn_addf(&obuf, "%s,", protoset[proto]); 434 free((char *) protoset); 435 curlx_dyn_setlen(&obuf, curlx_dyn_len(&obuf) - 1); 436 free(*ostr); 437 *ostr = curlx_dyn_ptr(&obuf); 438 439 return *ostr ? PARAM_OK : PARAM_NO_MEM; 440} 441 442/** 443 * Check if the given string is a protocol supported by libcurl 444 * 445 * @param str the protocol name 446 * @return PARAM_OK protocol supported 447 * @return PARAM_LIBCURL_UNSUPPORTED_PROTOCOL protocol not supported 448 * @return PARAM_REQUIRES_PARAMETER missing parameter 449 */ 450ParameterError check_protocol(const char *str) 451{ 452 if(!str) 453 return PARAM_REQUIRES_PARAMETER; 454 455 if(proto_token(str)) 456 return PARAM_OK; 457 return PARAM_LIBCURL_UNSUPPORTED_PROTOCOL; 458} 459 460/** 461 * Parses the given string looking for an offset (which may be a 462 * larger-than-integer value). The offset CANNOT be negative! 463 * 464 * @param val the offset to populate 465 * @param str the buffer containing the offset 466 * @return PARAM_OK if successful, a parameter specific error enum if failure. 467 */ 468ParameterError str2offset(curl_off_t *val, const char *str) 469{ 470 char *endptr; 471 if(str[0] == '-') 472 /* offsets aren't negative, this indicates weird input */ 473 return PARAM_NEGATIVE_NUMERIC; 474 475#if(SIZEOF_CURL_OFF_T > SIZEOF_LONG) 476 { 477 CURLofft offt = curlx_strtoofft(str, &endptr, 10, val); 478 if(CURL_OFFT_FLOW == offt) 479 return PARAM_NUMBER_TOO_LARGE; 480 else if(CURL_OFFT_INVAL == offt) 481 return PARAM_BAD_NUMERIC; 482 } 483#else 484 errno = 0; 485 *val = strtol(str, &endptr, 0); 486 if((*val == LONG_MIN || *val == LONG_MAX) && errno == ERANGE) 487 return PARAM_NUMBER_TOO_LARGE; 488#endif 489 if((endptr != str) && (endptr == str + strlen(str))) 490 return PARAM_OK; 491 492 return PARAM_BAD_NUMERIC; 493} 494 495#define MAX_USERPWDLENGTH (100*1024) 496static CURLcode checkpasswd(const char *kind, /* for what purpose */ 497 const size_t i, /* operation index */ 498 const bool last, /* TRUE if last operation */ 499 char **userpwd) /* pointer to allocated string */ 500{ 501 char *psep; 502 char *osep; 503 504 if(!*userpwd) 505 return CURLE_OK; 506 507 /* Attempt to find the password separator */ 508 psep = strchr(*userpwd, ':'); 509 510 /* Attempt to find the options separator */ 511 osep = strchr(*userpwd, ';'); 512 513 if(!psep && **userpwd != ';') { 514 /* no password present, prompt for one */ 515 char passwd[2048] = ""; 516 char prompt[256]; 517 struct curlx_dynbuf dyn; 518 519 curlx_dyn_init(&dyn, MAX_USERPWDLENGTH); 520 if(osep) 521 *osep = '\0'; 522 523 /* build a nice-looking prompt */ 524 if(!i && last) 525 curlx_msnprintf(prompt, sizeof(prompt), 526 "Enter %s password for user '%s':", 527 kind, *userpwd); 528 else 529 curlx_msnprintf(prompt, sizeof(prompt), 530 "Enter %s password for user '%s' on URL #%zu:", 531 kind, *userpwd, i + 1); 532 533 /* get password */ 534 getpass_r(prompt, passwd, sizeof(passwd)); 535 if(osep) 536 *osep = ';'; 537 538 if(curlx_dyn_addf(&dyn, "%s:%s", *userpwd, passwd)) 539 return CURLE_OUT_OF_MEMORY; 540 541 /* return the new string */ 542 free(*userpwd); 543 *userpwd = curlx_dyn_ptr(&dyn); 544 } 545 546 return CURLE_OK; 547} 548 549ParameterError add2list(struct curl_slist **list, const char *ptr) 550{ 551 struct curl_slist *newlist = curl_slist_append(*list, ptr); 552 if(newlist) 553 *list = newlist; 554 else 555 return PARAM_NO_MEM; 556 557 return PARAM_OK; 558} 559 560int ftpfilemethod(struct OperationConfig *config, const char *str) 561{ 562 if(curl_strequal("singlecwd", str)) 563 return CURLFTPMETHOD_SINGLECWD; 564 if(curl_strequal("nocwd", str)) 565 return CURLFTPMETHOD_NOCWD; 566 if(curl_strequal("multicwd", str)) 567 return CURLFTPMETHOD_MULTICWD; 568 569 warnf(config->global, "unrecognized ftp file method '%s', using default", 570 str); 571 572 return CURLFTPMETHOD_MULTICWD; 573} 574 575int ftpcccmethod(struct OperationConfig *config, const char *str) 576{ 577 if(curl_strequal("passive", str)) 578 return CURLFTPSSL_CCC_PASSIVE; 579 if(curl_strequal("active", str)) 580 return CURLFTPSSL_CCC_ACTIVE; 581 582 warnf(config->global, "unrecognized ftp CCC method '%s', using default", 583 str); 584 585 return CURLFTPSSL_CCC_PASSIVE; 586} 587 588long delegation(struct OperationConfig *config, const char *str) 589{ 590 if(curl_strequal("none", str)) 591 return CURLGSSAPI_DELEGATION_NONE; 592 if(curl_strequal("policy", str)) 593 return CURLGSSAPI_DELEGATION_POLICY_FLAG; 594 if(curl_strequal("always", str)) 595 return CURLGSSAPI_DELEGATION_FLAG; 596 597 warnf(config->global, "unrecognized delegation method '%s', using none", 598 str); 599 600 return CURLGSSAPI_DELEGATION_NONE; 601} 602 603/* 604 * my_useragent: returns allocated string with default user agent 605 */ 606static char *my_useragent(void) 607{ 608 return strdup(CURL_NAME "/" CURL_VERSION); 609} 610 611#define isheadersep(x) ((((x)==':') || ((x)==';'))) 612 613/* 614 * inlist() returns true if the given 'checkfor' header is present in the 615 * header list. 616 */ 617static bool inlist(const struct curl_slist *head, 618 const char *checkfor) 619{ 620 size_t thislen = strlen(checkfor); 621 DEBUGASSERT(thislen); 622 DEBUGASSERT(checkfor[thislen-1] != ':'); 623 624 for(; head; head = head->next) { 625 if(curl_strnequal(head->data, checkfor, thislen) && 626 isheadersep(head->data[thislen]) ) 627 return TRUE; 628 } 629 630 return FALSE; 631} 632 633CURLcode get_args(struct OperationConfig *config, const size_t i) 634{ 635 CURLcode result = CURLE_OK; 636 bool last = (config->next ? FALSE : TRUE); 637 638 if(config->jsoned) { 639 ParameterError err = PARAM_OK; 640 /* --json also implies json Content-Type: and Accept: headers - if 641 they are not set with -H */ 642 if(!inlist(config->headers, "Content-Type")) 643 err = add2list(&config->headers, "Content-Type: application/json"); 644 if(!err && !inlist(config->headers, "Accept")) 645 err = add2list(&config->headers, "Accept: application/json"); 646 if(err) 647 return CURLE_OUT_OF_MEMORY; 648 } 649 650 /* Check we have a password for the given host user */ 651 if(config->userpwd && !config->oauth_bearer) { 652 result = checkpasswd("host", i, last, &config->userpwd); 653 if(result) 654 return result; 655 } 656 657 /* Check we have a password for the given proxy user */ 658 if(config->proxyuserpwd) { 659 result = checkpasswd("proxy", i, last, &config->proxyuserpwd); 660 if(result) 661 return result; 662 } 663 664 /* Check we have a user agent */ 665 if(!config->useragent) { 666 config->useragent = my_useragent(); 667 if(!config->useragent) { 668 errorf(config->global, "out of memory"); 669 result = CURLE_OUT_OF_MEMORY; 670 } 671 } 672 673 return result; 674} 675 676/* 677 * Parse the string and modify ssl_version in the val argument. Return PARAM_OK 678 * on success, otherwise a parameter error enum. ONLY ACCEPTS POSITIVE NUMBERS! 679 * 680 * Since this function gets called with the 'nextarg' pointer from within the 681 * getparameter a lot, we must check it for NULL before accessing the str 682 * data. 683 */ 684 685ParameterError str2tls_max(long *val, const char *str) 686{ 687 static struct s_tls_max { 688 const char *tls_max_str; 689 long tls_max; 690 } const tls_max_array[] = { 691 { "default", CURL_SSLVERSION_MAX_DEFAULT }, 692 { "1.0", CURL_SSLVERSION_MAX_TLSv1_0 }, 693 { "1.1", CURL_SSLVERSION_MAX_TLSv1_1 }, 694 { "1.2", CURL_SSLVERSION_MAX_TLSv1_2 }, 695 { "1.3", CURL_SSLVERSION_MAX_TLSv1_3 } 696 }; 697 size_t i = 0; 698 if(!str) 699 return PARAM_REQUIRES_PARAMETER; 700 for(i = 0; i < sizeof(tls_max_array)/sizeof(tls_max_array[0]); i++) { 701 if(!strcmp(str, tls_max_array[i].tls_max_str)) { 702 *val = tls_max_array[i].tls_max; 703 return PARAM_OK; 704 } 705 } 706 return PARAM_BAD_USE; 707} 708