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