• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 == str + strlen(str))) {
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'\n", 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\n",
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\n",
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\n",
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\n");
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