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
new_getout(struct OperationConfig * config)44 struct 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
file2string(char ** bufp,FILE * file)68 ParameterError 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
91 #define MAX_FILE2MEMORY (1024*1024*1024) /* big enough ? */
92
file2memory(char ** bufp,size_t * size,FILE * file)93 ParameterError file2memory(char **bufp, size_t *size, FILE *file)
94 {
95 if(file) {
96 size_t nread;
97 struct curlx_dynbuf dyn;
98 /* The size needs to fit in an int later */
99 DEBUGASSERT(MAX_FILE2MEMORY < INT_MAX);
100 curlx_dyn_init(&dyn, MAX_FILE2MEMORY);
101 do {
102 char buffer[4096];
103 nread = fread(buffer, 1, sizeof(buffer), file);
104 if(ferror(file)) {
105 curlx_dyn_free(&dyn);
106 *size = 0;
107 *bufp = NULL;
108 return PARAM_READ_ERROR;
109 }
110 if(nread)
111 if(curlx_dyn_addn(&dyn, buffer, nread))
112 return PARAM_NO_MEM;
113 } while(!feof(file));
114 *size = curlx_dyn_len(&dyn);
115 *bufp = curlx_dyn_ptr(&dyn);
116 }
117 else {
118 *size = 0;
119 *bufp = NULL;
120 }
121 return PARAM_OK;
122 }
123
124 /*
125 * Parse the string and write the long in the given address. Return PARAM_OK
126 * on success, otherwise a parameter specific error enum.
127 *
128 * Since this function gets called with the 'nextarg' pointer from within the
129 * getparameter a lot, we must check it for NULL before accessing the str
130 * data.
131 */
getnum(long * val,const char * str,int base)132 static ParameterError getnum(long *val, const char *str, int base)
133 {
134 if(str) {
135 char *endptr = NULL;
136 long num;
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
str2num(long * val,const char * str)149 ParameterError str2num(long *val, const char *str)
150 {
151 return getnum(val, str, 10);
152 }
153
oct2nummax(long * val,const char * str,long max)154 ParameterError 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
str2unum(long * val,const char * str)176 ParameterError 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
str2unummax(long * val,const char * str,long max)197 ParameterError 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
str2double(double * val,const char * str,double max)221 static 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
secs2ms(long * valp,const char * str)255 ParameterError 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). */
protoset_index(const char * const * protoset,const char * proto)274 static 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. */
protoset_set(const char ** protoset,const char * proto)287 static 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. */
protoset_clear(const char ** protoset,const char * proto)301 static 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
proto2num(struct OperationConfig * config,const char * const * val,char ** ostr,const char * str)328 ParameterError 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 */
check_protocol(const char * str)450 ParameterError 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 */
str2offset(curl_off_t * val,const char * str)468 ParameterError 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)
checkpasswd(const char * kind,const size_t i,const bool last,char ** userpwd)496 static 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
add2list(struct curl_slist ** list,const char * ptr)549 ParameterError 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
ftpfilemethod(struct OperationConfig * config,const char * str)560 int 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
ftpcccmethod(struct OperationConfig * config,const char * str)575 int 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
delegation(struct OperationConfig * config,const char * str)588 long 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 */
my_useragent(void)606 static 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 */
inlist(const struct curl_slist * head,const char * checkfor)617 static 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
get_args(struct OperationConfig * config,const size_t i)633 CURLcode 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
str2tls_max(long * val,const char * str)685 ParameterError 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