altsvc.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674
  1. /***************************************************************************
  2. * _ _ ____ _
  3. * Project ___| | | | _ \| |
  4. * / __| | | | |_) | |
  5. * | (__| |_| | _ <| |___
  6. * \___|\___/|_| \_\_____|
  7. *
  8. * Copyright (C) Daniel Stenberg, <[email protected]>, 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. /*
  25. * The Alt-Svc: header is defined in RFC 7838:
  26. * https://datatracker.ietf.org/doc/html/rfc7838
  27. */
  28. #include "curl_setup.h"
  29. #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_ALTSVC)
  30. #include <curl/curl.h>
  31. #include "urldata.h"
  32. #include "altsvc.h"
  33. #include "curl_fopen.h"
  34. #include "curl_get_line.h"
  35. #include "parsedate.h"
  36. #include "sendf.h"
  37. #include "curlx/warnless.h"
  38. #include "rename.h"
  39. #include "strdup.h"
  40. #include "curlx/inet_pton.h"
  41. #include "curlx/strparse.h"
  42. #include "connect.h"
  43. /* The last 2 #include files should be in this order */
  44. #include "curl_memory.h"
  45. #include "memdebug.h"
  46. #define MAX_ALTSVC_LINE 4095
  47. #define MAX_ALTSVC_DATELEN 256
  48. #define MAX_ALTSVC_HOSTLEN 2048
  49. #define MAX_ALTSVC_ALPNLEN 10
  50. #define H3VERSION "h3"
  51. /* Given the ALPN ID, return the name */
  52. const char *Curl_alpnid2str(enum alpnid id)
  53. {
  54. switch(id) {
  55. case ALPN_h1:
  56. return "h1";
  57. case ALPN_h2:
  58. return "h2";
  59. case ALPN_h3:
  60. return H3VERSION;
  61. default:
  62. return ""; /* bad */
  63. }
  64. }
  65. static void altsvc_free(struct altsvc *as)
  66. {
  67. free(as->src.host);
  68. free(as->dst.host);
  69. free(as);
  70. }
  71. static struct altsvc *altsvc_createid(const char *srchost,
  72. size_t hlen,
  73. const char *dsthost,
  74. size_t dlen, /* dsthost length */
  75. enum alpnid srcalpnid,
  76. enum alpnid dstalpnid,
  77. size_t srcport,
  78. size_t dstport)
  79. {
  80. struct altsvc *as = calloc(1, sizeof(struct altsvc));
  81. if(!as)
  82. return NULL;
  83. DEBUGASSERT(hlen);
  84. DEBUGASSERT(dlen);
  85. if(!hlen || !dlen)
  86. /* bad input */
  87. goto error;
  88. if((hlen > 2) && srchost[0] == '[') {
  89. /* IPv6 address, strip off brackets */
  90. srchost++;
  91. hlen -= 2;
  92. }
  93. else if(srchost[hlen - 1] == '.') {
  94. /* strip off trailing dot */
  95. hlen--;
  96. if(!hlen)
  97. goto error;
  98. }
  99. if((dlen > 2) && dsthost[0] == '[') {
  100. /* IPv6 address, strip off brackets */
  101. dsthost++;
  102. dlen -= 2;
  103. }
  104. as->src.host = Curl_memdup0(srchost, hlen);
  105. if(!as->src.host)
  106. goto error;
  107. as->dst.host = Curl_memdup0(dsthost, dlen);
  108. if(!as->dst.host)
  109. goto error;
  110. as->src.alpnid = srcalpnid;
  111. as->dst.alpnid = dstalpnid;
  112. as->src.port = (unsigned short)srcport;
  113. as->dst.port = (unsigned short)dstport;
  114. return as;
  115. error:
  116. altsvc_free(as);
  117. return NULL;
  118. }
  119. static struct altsvc *altsvc_create(struct Curl_str *srchost,
  120. struct Curl_str *dsthost,
  121. struct Curl_str *srcalpn,
  122. struct Curl_str *dstalpn,
  123. size_t srcport,
  124. size_t dstport)
  125. {
  126. enum alpnid dstalpnid =
  127. Curl_alpn2alpnid(curlx_str(dstalpn), curlx_strlen(dstalpn));
  128. enum alpnid srcalpnid =
  129. Curl_alpn2alpnid(curlx_str(srcalpn), curlx_strlen(srcalpn));
  130. if(!srcalpnid || !dstalpnid)
  131. return NULL;
  132. return altsvc_createid(curlx_str(srchost), curlx_strlen(srchost),
  133. curlx_str(dsthost), curlx_strlen(dsthost),
  134. srcalpnid, dstalpnid,
  135. srcport, dstport);
  136. }
  137. /* only returns SERIOUS errors */
  138. static CURLcode altsvc_add(struct altsvcinfo *asi, const char *line)
  139. {
  140. /* Example line:
  141. h2 example.com 443 h3 shiny.example.com 8443 "20191231 10:00:00" 1
  142. */
  143. struct Curl_str srchost;
  144. struct Curl_str dsthost;
  145. struct Curl_str srcalpn;
  146. struct Curl_str dstalpn;
  147. struct Curl_str date;
  148. curl_off_t srcport;
  149. curl_off_t dstport;
  150. curl_off_t persist;
  151. curl_off_t prio;
  152. if(curlx_str_word(&line, &srcalpn, MAX_ALTSVC_ALPNLEN) ||
  153. curlx_str_singlespace(&line) ||
  154. curlx_str_word(&line, &srchost, MAX_ALTSVC_HOSTLEN) ||
  155. curlx_str_singlespace(&line) ||
  156. curlx_str_number(&line, &srcport, 65535) ||
  157. curlx_str_singlespace(&line) ||
  158. curlx_str_word(&line, &dstalpn, MAX_ALTSVC_ALPNLEN) ||
  159. curlx_str_singlespace(&line) ||
  160. curlx_str_word(&line, &dsthost, MAX_ALTSVC_HOSTLEN) ||
  161. curlx_str_singlespace(&line) ||
  162. curlx_str_number(&line, &dstport, 65535) ||
  163. curlx_str_singlespace(&line) ||
  164. curlx_str_quotedword(&line, &date, MAX_ALTSVC_DATELEN) ||
  165. curlx_str_singlespace(&line) ||
  166. curlx_str_number(&line, &persist, 1) ||
  167. curlx_str_singlespace(&line) ||
  168. curlx_str_number(&line, &prio, 0) ||
  169. curlx_str_newline(&line))
  170. ;
  171. else {
  172. struct altsvc *as;
  173. char dbuf[MAX_ALTSVC_DATELEN + 1];
  174. time_t expires = 0;
  175. /* The date parser works on a null-terminated string. The maximum length
  176. is upheld by curlx_str_quotedword(). */
  177. memcpy(dbuf, curlx_str(&date), curlx_strlen(&date));
  178. dbuf[curlx_strlen(&date)] = 0;
  179. Curl_getdate_capped(dbuf, &expires);
  180. as = altsvc_create(&srchost, &dsthost, &srcalpn, &dstalpn,
  181. (size_t)srcport, (size_t)dstport);
  182. if(as) {
  183. as->expires = expires;
  184. as->prio = 0; /* not supported to just set zero */
  185. as->persist = persist ? 1 : 0;
  186. Curl_llist_append(&asi->list, as, &as->node);
  187. }
  188. }
  189. return CURLE_OK;
  190. }
  191. /*
  192. * Load alt-svc entries from the given file. The text based line-oriented file
  193. * format is documented here: https://curl.se/docs/alt-svc.html
  194. *
  195. * This function only returns error on major problems that prevent alt-svc
  196. * handling to work completely. It will ignore individual syntactical errors
  197. * etc.
  198. */
  199. static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file)
  200. {
  201. CURLcode result = CURLE_OK;
  202. FILE *fp;
  203. /* we need a private copy of the filename so that the altsvc cache file
  204. name survives an easy handle reset */
  205. free(asi->filename);
  206. asi->filename = strdup(file);
  207. if(!asi->filename)
  208. return CURLE_OUT_OF_MEMORY;
  209. fp = curlx_fopen(file, FOPEN_READTEXT);
  210. if(fp) {
  211. bool eof = FALSE;
  212. struct dynbuf buf;
  213. curlx_dyn_init(&buf, MAX_ALTSVC_LINE);
  214. do {
  215. result = Curl_get_line(&buf, fp, &eof);
  216. if(!result) {
  217. const char *lineptr = curlx_dyn_ptr(&buf);
  218. curlx_str_passblanks(&lineptr);
  219. if(curlx_str_single(&lineptr, '#'))
  220. altsvc_add(asi, lineptr);
  221. }
  222. } while(!result && !eof);
  223. curlx_dyn_free(&buf); /* free the line buffer */
  224. curlx_fclose(fp);
  225. }
  226. return result;
  227. }
  228. /*
  229. * Write this single altsvc entry to a single output line
  230. */
  231. static CURLcode altsvc_out(struct altsvc *as, FILE *fp)
  232. {
  233. struct tm stamp;
  234. const char *dst6_pre = "";
  235. const char *dst6_post = "";
  236. const char *src6_pre = "";
  237. const char *src6_post = "";
  238. CURLcode result = Curl_gmtime(as->expires, &stamp);
  239. if(result)
  240. return result;
  241. #ifdef USE_IPV6
  242. else {
  243. char ipv6_unused[16];
  244. if(curlx_inet_pton(AF_INET6, as->dst.host, ipv6_unused) == 1) {
  245. dst6_pre = "[";
  246. dst6_post = "]";
  247. }
  248. if(curlx_inet_pton(AF_INET6, as->src.host, ipv6_unused) == 1) {
  249. src6_pre = "[";
  250. src6_post = "]";
  251. }
  252. }
  253. #endif
  254. curl_mfprintf(fp,
  255. "%s %s%s%s %u "
  256. "%s %s%s%s %u "
  257. "\"%d%02d%02d "
  258. "%02d:%02d:%02d\" "
  259. "%u %u\n",
  260. Curl_alpnid2str(as->src.alpnid),
  261. src6_pre, as->src.host, src6_post,
  262. as->src.port,
  263. Curl_alpnid2str(as->dst.alpnid),
  264. dst6_pre, as->dst.host, dst6_post,
  265. as->dst.port,
  266. stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
  267. stamp.tm_hour, stamp.tm_min, stamp.tm_sec,
  268. as->persist, as->prio);
  269. return CURLE_OK;
  270. }
  271. /* ---- library-wide functions below ---- */
  272. /*
  273. * Curl_altsvc_init() creates a new altsvc cache.
  274. * It returns the new instance or NULL if something goes wrong.
  275. */
  276. struct altsvcinfo *Curl_altsvc_init(void)
  277. {
  278. struct altsvcinfo *asi = calloc(1, sizeof(struct altsvcinfo));
  279. if(!asi)
  280. return NULL;
  281. Curl_llist_init(&asi->list, NULL);
  282. /* set default behavior */
  283. asi->flags = CURLALTSVC_H1
  284. #ifdef USE_HTTP2
  285. | CURLALTSVC_H2
  286. #endif
  287. #ifdef USE_HTTP3
  288. | CURLALTSVC_H3
  289. #endif
  290. ;
  291. return asi;
  292. }
  293. /*
  294. * Curl_altsvc_load() loads alt-svc from file.
  295. */
  296. CURLcode Curl_altsvc_load(struct altsvcinfo *asi, const char *file)
  297. {
  298. DEBUGASSERT(asi);
  299. return altsvc_load(asi, file);
  300. }
  301. /*
  302. * Curl_altsvc_ctrl() passes on the external bitmask.
  303. */
  304. CURLcode Curl_altsvc_ctrl(struct altsvcinfo *asi, const long ctrl)
  305. {
  306. DEBUGASSERT(asi);
  307. asi->flags = ctrl;
  308. return CURLE_OK;
  309. }
  310. /*
  311. * Curl_altsvc_cleanup() frees an altsvc cache instance and all associated
  312. * resources.
  313. */
  314. void Curl_altsvc_cleanup(struct altsvcinfo **altsvcp)
  315. {
  316. if(*altsvcp) {
  317. struct Curl_llist_node *e;
  318. struct Curl_llist_node *n;
  319. struct altsvcinfo *altsvc = *altsvcp;
  320. for(e = Curl_llist_head(&altsvc->list); e; e = n) {
  321. struct altsvc *as = Curl_node_elem(e);
  322. n = Curl_node_next(e);
  323. altsvc_free(as);
  324. }
  325. free(altsvc->filename);
  326. free(altsvc);
  327. *altsvcp = NULL; /* clear the pointer */
  328. }
  329. }
  330. /*
  331. * Curl_altsvc_save() writes the altsvc cache to a file.
  332. */
  333. CURLcode Curl_altsvc_save(struct Curl_easy *data,
  334. struct altsvcinfo *altsvc, const char *file)
  335. {
  336. CURLcode result = CURLE_OK;
  337. FILE *out;
  338. char *tempstore = NULL;
  339. if(!altsvc)
  340. /* no cache activated */
  341. return CURLE_OK;
  342. /* if not new name is given, use the one we stored from the load */
  343. if(!file && altsvc->filename)
  344. file = altsvc->filename;
  345. if((altsvc->flags & CURLALTSVC_READONLYFILE) || !file || !file[0])
  346. /* marked as read-only, no file or zero length filename */
  347. return CURLE_OK;
  348. result = Curl_fopen(data, file, &out, &tempstore);
  349. if(!result) {
  350. struct Curl_llist_node *e;
  351. struct Curl_llist_node *n;
  352. fputs("# Your alt-svc cache. https://curl.se/docs/alt-svc.html\n"
  353. "# This file was generated by libcurl! Edit at your own risk.\n",
  354. out);
  355. for(e = Curl_llist_head(&altsvc->list); e; e = n) {
  356. struct altsvc *as = Curl_node_elem(e);
  357. n = Curl_node_next(e);
  358. result = altsvc_out(as, out);
  359. if(result)
  360. break;
  361. }
  362. curlx_fclose(out);
  363. if(!result && tempstore && Curl_rename(tempstore, file))
  364. result = CURLE_WRITE_ERROR;
  365. if(result && tempstore)
  366. unlink(tempstore);
  367. }
  368. free(tempstore);
  369. return result;
  370. }
  371. /* hostcompare() returns true if 'host' matches 'check'. The first host
  372. * argument may have a trailing dot present that will be ignored.
  373. */
  374. static bool hostcompare(const char *host, const char *check)
  375. {
  376. size_t hlen = strlen(host);
  377. size_t clen = strlen(check);
  378. if(hlen && (host[hlen - 1] == '.'))
  379. hlen--;
  380. if(hlen != clen)
  381. /* they cannot match if they have different lengths */
  382. return FALSE;
  383. return curl_strnequal(host, check, hlen);
  384. }
  385. /* altsvc_flush() removes all alternatives for this source origin from the
  386. list */
  387. static void altsvc_flush(struct altsvcinfo *asi, enum alpnid srcalpnid,
  388. const char *srchost, unsigned short srcport)
  389. {
  390. struct Curl_llist_node *e;
  391. struct Curl_llist_node *n;
  392. for(e = Curl_llist_head(&asi->list); e; e = n) {
  393. struct altsvc *as = Curl_node_elem(e);
  394. n = Curl_node_next(e);
  395. if((srcalpnid == as->src.alpnid) &&
  396. (srcport == as->src.port) &&
  397. hostcompare(srchost, as->src.host)) {
  398. Curl_node_remove(e);
  399. altsvc_free(as);
  400. }
  401. }
  402. }
  403. #if defined(DEBUGBUILD) || defined(UNITTESTS)
  404. /* to play well with debug builds, we can *set* a fixed time this will
  405. return */
  406. static time_t altsvc_debugtime(void *unused)
  407. {
  408. const char *timestr = getenv("CURL_TIME");
  409. (void)unused;
  410. if(timestr) {
  411. curl_off_t val;
  412. curlx_str_number(&timestr, &val, TIME_T_MAX);
  413. return (time_t)val;
  414. }
  415. return time(NULL);
  416. }
  417. #undef time
  418. #define time(x) altsvc_debugtime(x)
  419. #endif
  420. /*
  421. * Curl_altsvc_parse() takes an incoming alt-svc response header and stores
  422. * the data correctly in the cache.
  423. *
  424. * 'value' points to the header *value*. That is contents to the right of the
  425. * header name.
  426. *
  427. * Currently this function rejects invalid data without returning an error.
  428. * Invalid hostname, port number will result in the specific alternative
  429. * being rejected. Unknown protocols are skipped.
  430. */
  431. CURLcode Curl_altsvc_parse(struct Curl_easy *data,
  432. struct altsvcinfo *asi, const char *value,
  433. enum alpnid srcalpnid, const char *srchost,
  434. unsigned short srcport)
  435. {
  436. const char *p = value;
  437. struct altsvc *as;
  438. unsigned short dstport = srcport; /* the same by default */
  439. size_t entries = 0;
  440. struct Curl_str alpn;
  441. const char *sp;
  442. time_t maxage = 24 * 3600; /* default is 24 hours */
  443. bool persist = FALSE;
  444. #ifdef CURL_DISABLE_VERBOSE_STRINGS
  445. (void)data;
  446. #endif
  447. DEBUGASSERT(asi);
  448. /* initial check for "clear" */
  449. if(!curlx_str_cspn(&p, &alpn, ";\n\r")) {
  450. curlx_str_trimblanks(&alpn);
  451. /* "clear" is a magic keyword */
  452. if(curlx_str_casecompare(&alpn, "clear")) {
  453. /* Flush cached alternatives for this source origin */
  454. altsvc_flush(asi, srcalpnid, srchost, srcport);
  455. return CURLE_OK;
  456. }
  457. }
  458. p = value;
  459. if(curlx_str_until(&p, &alpn, MAX_ALTSVC_LINE, '='))
  460. return CURLE_OK; /* strange line */
  461. curlx_str_trimblanks(&alpn);
  462. /* Handle the optional 'ma' and 'persist' flags once first, as they need to
  463. be known for each alternative service. Unknown flags are skipped. */
  464. sp = strchr(p, ';');
  465. if(sp) {
  466. sp++; /* pass the semicolon */
  467. for(;;) {
  468. struct Curl_str name;
  469. struct Curl_str val;
  470. const char *vp;
  471. curl_off_t num;
  472. bool quoted;
  473. /* allow some extra whitespaces around name and value */
  474. if(curlx_str_until(&sp, &name, 20, '=') ||
  475. curlx_str_single(&sp, '=') ||
  476. curlx_str_until(&sp, &val, 80, ';'))
  477. break;
  478. curlx_str_trimblanks(&name);
  479. curlx_str_trimblanks(&val);
  480. /* the value might be quoted */
  481. vp = curlx_str(&val);
  482. quoted = (*vp == '\"');
  483. if(quoted)
  484. vp++;
  485. if(!curlx_str_number(&vp, &num, TIME_T_MAX)) {
  486. if(curlx_str_casecompare(&name, "ma"))
  487. maxage = (time_t)num;
  488. else if(curlx_str_casecompare(&name, "persist") && (num == 1))
  489. persist = TRUE;
  490. }
  491. if(quoted && curlx_str_single(&sp, '\"'))
  492. break;
  493. if(curlx_str_single(&sp, ';'))
  494. break;
  495. }
  496. }
  497. do {
  498. if(!curlx_str_single(&p, '=')) {
  499. /* [protocol]="[host][:port], [protocol]="[host][:port]" */
  500. enum alpnid dstalpnid =
  501. Curl_alpn2alpnid(curlx_str(&alpn), curlx_strlen(&alpn));
  502. if(!curlx_str_single(&p, '\"')) {
  503. struct Curl_str dsthost;
  504. curl_off_t port = 0;
  505. if(curlx_str_single(&p, ':')) {
  506. /* hostname starts here */
  507. if(curlx_str_single(&p, '[')) {
  508. if(curlx_str_until(&p, &dsthost, MAX_ALTSVC_HOSTLEN, ':')) {
  509. infof(data, "Bad alt-svc hostname, ignoring.");
  510. break;
  511. }
  512. }
  513. else {
  514. /* IPv6 host name */
  515. if(curlx_str_until(&p, &dsthost, MAX_IPADR_LEN, ']') ||
  516. curlx_str_single(&p, ']')) {
  517. infof(data, "Bad alt-svc IPv6 hostname, ignoring.");
  518. break;
  519. }
  520. }
  521. if(curlx_str_single(&p, ':'))
  522. break;
  523. }
  524. else
  525. /* no destination name, use source host */
  526. curlx_str_assign(&dsthost, srchost, strlen(srchost));
  527. if(curlx_str_number(&p, &port, 0xffff)) {
  528. infof(data, "Unknown alt-svc port number, ignoring.");
  529. break;
  530. }
  531. dstport = (unsigned short)port;
  532. if(curlx_str_single(&p, '\"'))
  533. break;
  534. if(dstalpnid) {
  535. if(!entries++)
  536. /* Flush cached alternatives for this source origin, if any - when
  537. this is the first entry of the line. */
  538. altsvc_flush(asi, srcalpnid, srchost, srcport);
  539. as = altsvc_createid(srchost, strlen(srchost),
  540. curlx_str(&dsthost),
  541. curlx_strlen(&dsthost),
  542. srcalpnid, dstalpnid,
  543. srcport, dstport);
  544. if(as) {
  545. time_t secs = time(NULL);
  546. /* The expires time also needs to take the Age: value (if any)
  547. into account. [See RFC 7838 section 3.1] */
  548. if(maxage > (TIME_T_MAX - secs))
  549. as->expires = TIME_T_MAX;
  550. else
  551. as->expires = maxage + secs;
  552. as->persist = persist;
  553. Curl_llist_append(&asi->list, as, &as->node);
  554. infof(data, "Added alt-svc: %.*s:%d over %s",
  555. (int)curlx_strlen(&dsthost), curlx_str(&dsthost),
  556. dstport, Curl_alpnid2str(dstalpnid));
  557. }
  558. }
  559. }
  560. else
  561. break;
  562. /* after the double quote there can be a comma if there is another
  563. string or a semicolon if no more */
  564. if(curlx_str_single(&p, ','))
  565. break;
  566. /* comma means another alternative is present */
  567. if(curlx_str_until(&p, &alpn, MAX_ALTSVC_LINE, '='))
  568. break;
  569. curlx_str_trimblanks(&alpn);
  570. }
  571. else
  572. break;
  573. } while(1);
  574. return CURLE_OK;
  575. }
  576. /*
  577. * Return TRUE on a match
  578. */
  579. bool Curl_altsvc_lookup(struct altsvcinfo *asi,
  580. enum alpnid srcalpnid, const char *srchost,
  581. int srcport,
  582. struct altsvc **dstentry,
  583. const int versions) /* one or more bits */
  584. {
  585. struct Curl_llist_node *e;
  586. struct Curl_llist_node *n;
  587. time_t now = time(NULL);
  588. DEBUGASSERT(asi);
  589. DEBUGASSERT(srchost);
  590. DEBUGASSERT(dstentry);
  591. for(e = Curl_llist_head(&asi->list); e; e = n) {
  592. struct altsvc *as = Curl_node_elem(e);
  593. n = Curl_node_next(e);
  594. if(as->expires < now) {
  595. /* an expired entry, remove */
  596. Curl_node_remove(e);
  597. altsvc_free(as);
  598. continue;
  599. }
  600. if((as->src.alpnid == srcalpnid) &&
  601. hostcompare(srchost, as->src.host) &&
  602. (as->src.port == srcport) &&
  603. (versions & (int)as->dst.alpnid)) {
  604. /* match */
  605. *dstentry = as;
  606. return TRUE;
  607. }
  608. }
  609. return FALSE;
  610. }
  611. #if defined(DEBUGBUILD) || defined(UNITTESTS)
  612. #undef time
  613. #endif
  614. #endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_ALTSVC */