tool_paramhlp.c 19 KB

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