VirtualBox

source: vbox/trunk/src/libs/curl-8.7.1/lib/altsvc.c@ 106165

最後變更 在這個檔案從106165是 104083,由 vboxsync 提交於 8 月 前

curl-8.7.1: Applied and adjusted our curl changes to 8.4.0. bugref:10639

  • 屬性 svn:eol-style 設為 native
檔案大小: 19.2 KB
 
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
30#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_ALTSVC)
31#include <curl/curl.h>
32#include "urldata.h"
33#include "altsvc.h"
34#include "curl_get_line.h"
35#include "strcase.h"
36#include "parsedate.h"
37#include "sendf.h"
38#include "warnless.h"
39#include "fopen.h"
40#include "rename.h"
41#include "strdup.h"
42#include "inet_pton.h"
43
44/* The last 3 #include files should be in this order */
45#include "curl_printf.h"
46#include "curl_memory.h"
47#include "memdebug.h"
48
49#define MAX_ALTSVC_LINE 4095
50#define MAX_ALTSVC_DATELENSTR "64"
51#define MAX_ALTSVC_DATELEN 64
52#define MAX_ALTSVC_HOSTLENSTR "512"
53#define MAX_ALTSVC_HOSTLEN 512
54#define MAX_ALTSVC_ALPNLENSTR "10"
55#define MAX_ALTSVC_ALPNLEN 10
56
57#define H3VERSION "h3"
58
59static enum alpnid alpn2alpnid(char *name)
60{
61 if(strcasecompare(name, "h1"))
62 return ALPN_h1;
63 if(strcasecompare(name, "h2"))
64 return ALPN_h2;
65 if(strcasecompare(name, H3VERSION))
66 return ALPN_h3;
67 return ALPN_none; /* unknown, probably rubbish input */
68}
69
70/* Given the ALPN ID, return the name */
71const char *Curl_alpnid2str(enum alpnid id)
72{
73 switch(id) {
74 case ALPN_h1:
75 return "h1";
76 case ALPN_h2:
77 return "h2";
78 case ALPN_h3:
79 return H3VERSION;
80 default:
81 return ""; /* bad */
82 }
83}
84
85
86static void altsvc_free(struct altsvc *as)
87{
88 free(as->src.host);
89 free(as->dst.host);
90 free(as);
91}
92
93static struct altsvc *altsvc_createid(const char *srchost,
94 const char *dsthost,
95 enum alpnid srcalpnid,
96 enum alpnid dstalpnid,
97 unsigned int srcport,
98 unsigned int dstport)
99{
100 struct altsvc *as = calloc(1, sizeof(struct altsvc));
101 size_t hlen;
102 size_t dlen;
103 if(!as)
104 return NULL;
105 hlen = strlen(srchost);
106 dlen = strlen(dsthost);
107 DEBUGASSERT(hlen);
108 DEBUGASSERT(dlen);
109 if(!hlen || !dlen) {
110 /* bad input */
111 free(as);
112 return NULL;
113 }
114 if((hlen > 2) && srchost[0] == '[') {
115 /* IPv6 address, strip off brackets */
116 srchost++;
117 hlen -= 2;
118 }
119 else if(srchost[hlen - 1] == '.')
120 /* strip off trailing dot */
121 hlen--;
122 if((dlen > 2) && dsthost[0] == '[') {
123 /* IPv6 address, strip off brackets */
124 dsthost++;
125 dlen -= 2;
126 }
127
128 as->src.host = Curl_memdup0(srchost, hlen);
129 if(!as->src.host)
130 goto error;
131
132 as->dst.host = Curl_memdup0(dsthost, dlen);
133 if(!as->dst.host)
134 goto error;
135
136 as->src.alpnid = srcalpnid;
137 as->dst.alpnid = dstalpnid;
138 as->src.port = curlx_ultous(srcport);
139 as->dst.port = curlx_ultous(dstport);
140
141 return as;
142error:
143 altsvc_free(as);
144 return NULL;
145}
146
147static struct altsvc *altsvc_create(char *srchost,
148 char *dsthost,
149 char *srcalpn,
150 char *dstalpn,
151 unsigned int srcport,
152 unsigned int dstport)
153{
154 enum alpnid dstalpnid = alpn2alpnid(dstalpn);
155 enum alpnid srcalpnid = alpn2alpnid(srcalpn);
156 if(!srcalpnid || !dstalpnid)
157 return NULL;
158 return altsvc_createid(srchost, dsthost, srcalpnid, dstalpnid,
159 srcport, dstport);
160}
161
162/* only returns SERIOUS errors */
163static CURLcode altsvc_add(struct altsvcinfo *asi, char *line)
164{
165 /* Example line:
166 h2 example.com 443 h3 shiny.example.com 8443 "20191231 10:00:00" 1
167 */
168 char srchost[MAX_ALTSVC_HOSTLEN + 1];
169 char dsthost[MAX_ALTSVC_HOSTLEN + 1];
170 char srcalpn[MAX_ALTSVC_ALPNLEN + 1];
171 char dstalpn[MAX_ALTSVC_ALPNLEN + 1];
172 char date[MAX_ALTSVC_DATELEN + 1];
173 unsigned int srcport;
174 unsigned int dstport;
175 unsigned int prio;
176 unsigned int persist;
177 int rc;
178
179 rc = sscanf(line,
180 "%" MAX_ALTSVC_ALPNLENSTR "s %" MAX_ALTSVC_HOSTLENSTR "s %u "
181 "%" MAX_ALTSVC_ALPNLENSTR "s %" MAX_ALTSVC_HOSTLENSTR "s %u "
182 "\"%" MAX_ALTSVC_DATELENSTR "[^\"]\" %u %u",
183 srcalpn, srchost, &srcport,
184 dstalpn, dsthost, &dstport,
185 date, &persist, &prio);
186 if(9 == rc) {
187 struct altsvc *as;
188 time_t expires = Curl_getdate_capped(date);
189 as = altsvc_create(srchost, dsthost, srcalpn, dstalpn, srcport, dstport);
190 if(as) {
191 as->expires = expires;
192 as->prio = prio;
193 as->persist = persist ? 1 : 0;
194 Curl_llist_insert_next(&asi->list, asi->list.tail, as, &as->node);
195 }
196 }
197
198 return CURLE_OK;
199}
200
201/*
202 * Load alt-svc entries from the given file. The text based line-oriented file
203 * format is documented here: https://curl.se/docs/alt-svc.html
204 *
205 * This function only returns error on major problems that prevent alt-svc
206 * handling to work completely. It will ignore individual syntactical errors
207 * etc.
208 */
209static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file)
210{
211 CURLcode result = CURLE_OK;
212 FILE *fp;
213
214 /* we need a private copy of the file name so that the altsvc cache file
215 name survives an easy handle reset */
216 free(asi->filename);
217 asi->filename = strdup(file);
218 if(!asi->filename)
219 return CURLE_OUT_OF_MEMORY;
220
221 fp = fopen(file, FOPEN_READTEXT);
222 if(fp) {
223 struct dynbuf buf;
224 Curl_dyn_init(&buf, MAX_ALTSVC_LINE);
225 while(Curl_get_line(&buf, fp)) {
226 char *lineptr = Curl_dyn_ptr(&buf);
227 while(*lineptr && ISBLANK(*lineptr))
228 lineptr++;
229 if(*lineptr == '#')
230 /* skip commented lines */
231 continue;
232
233 altsvc_add(asi, lineptr);
234 }
235 Curl_dyn_free(&buf); /* free the line buffer */
236 fclose(fp);
237 }
238 return result;
239}
240
241/*
242 * Write this single altsvc entry to a single output line
243 */
244
245static CURLcode altsvc_out(struct altsvc *as, FILE *fp)
246{
247 struct tm stamp;
248 const char *dst6_pre = "";
249 const char *dst6_post = "";
250 const char *src6_pre = "";
251 const char *src6_post = "";
252 CURLcode result = Curl_gmtime(as->expires, &stamp);
253 if(result)
254 return result;
255#ifdef ENABLE_IPV6
256 else {
257 char ipv6_unused[16];
258 if(1 == Curl_inet_pton(AF_INET6, as->dst.host, ipv6_unused)) {
259 dst6_pre = "[";
260 dst6_post = "]";
261 }
262 if(1 == Curl_inet_pton(AF_INET6, as->src.host, ipv6_unused)) {
263 src6_pre = "[";
264 src6_post = "]";
265 }
266 }
267#endif
268 fprintf(fp,
269 "%s %s%s%s %u "
270 "%s %s%s%s %u "
271 "\"%d%02d%02d "
272 "%02d:%02d:%02d\" "
273 "%u %d\n",
274 Curl_alpnid2str(as->src.alpnid),
275 src6_pre, as->src.host, src6_post,
276 as->src.port,
277
278 Curl_alpnid2str(as->dst.alpnid),
279 dst6_pre, as->dst.host, dst6_post,
280 as->dst.port,
281
282 stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
283 stamp.tm_hour, stamp.tm_min, stamp.tm_sec,
284 as->persist, as->prio);
285 return CURLE_OK;
286}
287
288/* ---- library-wide functions below ---- */
289
290/*
291 * Curl_altsvc_init() creates a new altsvc cache.
292 * It returns the new instance or NULL if something goes wrong.
293 */
294struct altsvcinfo *Curl_altsvc_init(void)
295{
296 struct altsvcinfo *asi = calloc(1, sizeof(struct altsvcinfo));
297 if(!asi)
298 return NULL;
299 Curl_llist_init(&asi->list, NULL);
300
301 /* set default behavior */
302 asi->flags = CURLALTSVC_H1
303#ifdef USE_HTTP2
304 | CURLALTSVC_H2
305#endif
306#ifdef ENABLE_QUIC
307 | CURLALTSVC_H3
308#endif
309 ;
310 return asi;
311}
312
313/*
314 * Curl_altsvc_load() loads alt-svc from file.
315 */
316CURLcode Curl_altsvc_load(struct altsvcinfo *asi, const char *file)
317{
318 CURLcode result;
319 DEBUGASSERT(asi);
320 result = altsvc_load(asi, file);
321 return result;
322}
323
324/*
325 * Curl_altsvc_ctrl() passes on the external bitmask.
326 */
327CURLcode Curl_altsvc_ctrl(struct altsvcinfo *asi, const long ctrl)
328{
329 DEBUGASSERT(asi);
330 asi->flags = ctrl;
331 return CURLE_OK;
332}
333
334/*
335 * Curl_altsvc_cleanup() frees an altsvc cache instance and all associated
336 * resources.
337 */
338void Curl_altsvc_cleanup(struct altsvcinfo **altsvcp)
339{
340 struct Curl_llist_element *e;
341 struct Curl_llist_element *n;
342 if(*altsvcp) {
343 struct altsvcinfo *altsvc = *altsvcp;
344 for(e = altsvc->list.head; e; e = n) {
345 struct altsvc *as = e->ptr;
346 n = e->next;
347 altsvc_free(as);
348 }
349 free(altsvc->filename);
350 free(altsvc);
351 *altsvcp = NULL; /* clear the pointer */
352 }
353}
354
355/*
356 * Curl_altsvc_save() writes the altsvc cache to a file.
357 */
358CURLcode Curl_altsvc_save(struct Curl_easy *data,
359 struct altsvcinfo *altsvc, const char *file)
360{
361 struct Curl_llist_element *e;
362 struct Curl_llist_element *n;
363 CURLcode result = CURLE_OK;
364 FILE *out;
365 char *tempstore = NULL;
366
367 if(!altsvc)
368 /* no cache activated */
369 return CURLE_OK;
370
371 /* if not new name is given, use the one we stored from the load */
372 if(!file && altsvc->filename)
373 file = altsvc->filename;
374
375 if((altsvc->flags & CURLALTSVC_READONLYFILE) || !file || !file[0])
376 /* marked as read-only, no file or zero length file name */
377 return CURLE_OK;
378
379 result = Curl_fopen(data, file, &out, &tempstore);
380 if(!result) {
381 fputs("# Your alt-svc cache. https://curl.se/docs/alt-svc.html\n"
382 "# This file was generated by libcurl! Edit at your own risk.\n",
383 out);
384 for(e = altsvc->list.head; e; e = n) {
385 struct altsvc *as = e->ptr;
386 n = e->next;
387 result = altsvc_out(as, out);
388 if(result)
389 break;
390 }
391 fclose(out);
392 if(!result && tempstore && Curl_rename(tempstore, file))
393 result = CURLE_WRITE_ERROR;
394
395 if(result && tempstore)
396 unlink(tempstore);
397 }
398 free(tempstore);
399 return result;
400}
401
402static CURLcode getalnum(const char **ptr, char *alpnbuf, size_t buflen)
403{
404 size_t len;
405 const char *protop;
406 const char *p = *ptr;
407 while(*p && ISBLANK(*p))
408 p++;
409 protop = p;
410 while(*p && !ISBLANK(*p) && (*p != ';') && (*p != '='))
411 p++;
412 len = p - protop;
413 *ptr = p;
414
415 if(!len || (len >= buflen))
416 return CURLE_BAD_FUNCTION_ARGUMENT;
417 memcpy(alpnbuf, protop, len);
418 alpnbuf[len] = 0;
419 return CURLE_OK;
420}
421
422/* hostcompare() returns true if 'host' matches 'check'. The first host
423 * argument may have a trailing dot present that will be ignored.
424 */
425static bool hostcompare(const char *host, const char *check)
426{
427 size_t hlen = strlen(host);
428 size_t clen = strlen(check);
429
430 if(hlen && (host[hlen - 1] == '.'))
431 hlen--;
432 if(hlen != clen)
433 /* they can't match if they have different lengths */
434 return FALSE;
435 return strncasecompare(host, check, hlen);
436}
437
438/* altsvc_flush() removes all alternatives for this source origin from the
439 list */
440static void altsvc_flush(struct altsvcinfo *asi, enum alpnid srcalpnid,
441 const char *srchost, unsigned short srcport)
442{
443 struct Curl_llist_element *e;
444 struct Curl_llist_element *n;
445 for(e = asi->list.head; e; e = n) {
446 struct altsvc *as = e->ptr;
447 n = e->next;
448 if((srcalpnid == as->src.alpnid) &&
449 (srcport == as->src.port) &&
450 hostcompare(srchost, as->src.host)) {
451 Curl_llist_remove(&asi->list, e, NULL);
452 altsvc_free(as);
453 }
454 }
455}
456
457#ifdef DEBUGBUILD
458/* to play well with debug builds, we can *set* a fixed time this will
459 return */
460static time_t altsvc_debugtime(void *unused)
461{
462 char *timestr = getenv("CURL_TIME");
463 (void)unused;
464 if(timestr) {
465 unsigned long val = strtol(timestr, NULL, 10);
466 return (time_t)val;
467 }
468 return time(NULL);
469}
470#undef time
471#define time(x) altsvc_debugtime(x)
472#endif
473
474#define ISNEWLINE(x) (((x) == '\n') || (x) == '\r')
475
476/*
477 * Curl_altsvc_parse() takes an incoming alt-svc response header and stores
478 * the data correctly in the cache.
479 *
480 * 'value' points to the header *value*. That's contents to the right of the
481 * header name.
482 *
483 * Currently this function rejects invalid data without returning an error.
484 * Invalid host name, port number will result in the specific alternative
485 * being rejected. Unknown protocols are skipped.
486 */
487CURLcode Curl_altsvc_parse(struct Curl_easy *data,
488 struct altsvcinfo *asi, const char *value,
489 enum alpnid srcalpnid, const char *srchost,
490 unsigned short srcport)
491{
492 const char *p = value;
493 size_t len;
494 char namebuf[MAX_ALTSVC_HOSTLEN] = "";
495 char alpnbuf[MAX_ALTSVC_ALPNLEN] = "";
496 struct altsvc *as;
497 unsigned short dstport = srcport; /* the same by default */
498 CURLcode result = getalnum(&p, alpnbuf, sizeof(alpnbuf));
499 size_t entries = 0;
500#ifdef CURL_DISABLE_VERBOSE_STRINGS
501 (void)data;
502#endif
503 if(result) {
504 infof(data, "Excessive alt-svc header, ignoring.");
505 return CURLE_OK;
506 }
507
508 DEBUGASSERT(asi);
509
510 /* "clear" is a magic keyword */
511 if(strcasecompare(alpnbuf, "clear")) {
512 /* Flush cached alternatives for this source origin */
513 altsvc_flush(asi, srcalpnid, srchost, srcport);
514 return CURLE_OK;
515 }
516
517 do {
518 if(*p == '=') {
519 /* [protocol]="[host][:port]" */
520 enum alpnid dstalpnid = alpn2alpnid(alpnbuf); /* the same by default */
521 p++;
522 if(*p == '\"') {
523 const char *dsthost = "";
524 const char *value_ptr;
525 char option[32];
526 unsigned long num;
527 char *end_ptr;
528 bool quoted = FALSE;
529 time_t maxage = 24 * 3600; /* default is 24 hours */
530 bool persist = FALSE;
531 bool valid = TRUE;
532 p++;
533 if(*p != ':') {
534 /* host name starts here */
535 const char *hostp = p;
536 if(*p == '[') {
537 /* pass all valid IPv6 letters - does not handle zone id */
538 len = strspn(++p, "0123456789abcdefABCDEF:.");
539 if(p[len] != ']')
540 /* invalid host syntax, bail out */
541 break;
542 /* we store the IPv6 numerical address *with* brackets */
543 len += 2;
544 p = &p[len-1];
545 }
546 else {
547 while(*p && (ISALNUM(*p) || (*p == '.') || (*p == '-')))
548 p++;
549 len = p - hostp;
550 }
551 if(!len || (len >= MAX_ALTSVC_HOSTLEN)) {
552 infof(data, "Excessive alt-svc host name, ignoring.");
553 valid = FALSE;
554 }
555 else {
556 memcpy(namebuf, hostp, len);
557 namebuf[len] = 0;
558 dsthost = namebuf;
559 }
560 }
561 else {
562 /* no destination name, use source host */
563 dsthost = srchost;
564 }
565 if(*p == ':') {
566 unsigned long port = 0;
567 p++;
568 if(ISDIGIT(*p))
569 /* a port number */
570 port = strtoul(p, &end_ptr, 10);
571 else
572 end_ptr = (char *)p; /* not left uninitialized */
573 if(!port || port > USHRT_MAX || end_ptr == p || *end_ptr != '\"') {
574 infof(data, "Unknown alt-svc port number, ignoring.");
575 valid = FALSE;
576 }
577 else {
578 dstport = curlx_ultous(port);
579 p = end_ptr;
580 }
581 }
582 if(*p++ != '\"')
583 break;
584 /* Handle the optional 'ma' and 'persist' flags. Unknown flags
585 are skipped. */
586 for(;;) {
587 while(ISBLANK(*p))
588 p++;
589 if(*p != ';')
590 break;
591 p++; /* pass the semicolon */
592 if(!*p || ISNEWLINE(*p))
593 break;
594 result = getalnum(&p, option, sizeof(option));
595 if(result) {
596 /* skip option if name is too long */
597 option[0] = '\0';
598 }
599 while(*p && ISBLANK(*p))
600 p++;
601 if(*p != '=')
602 return CURLE_OK;
603 p++;
604 while(*p && ISBLANK(*p))
605 p++;
606 if(!*p)
607 return CURLE_OK;
608 if(*p == '\"') {
609 /* quoted value */
610 p++;
611 quoted = TRUE;
612 }
613 value_ptr = p;
614 if(quoted) {
615 while(*p && *p != '\"')
616 p++;
617 if(!*p++)
618 return CURLE_OK;
619 }
620 else {
621 while(*p && !ISBLANK(*p) && *p!= ';' && *p != ',')
622 p++;
623 }
624 num = strtoul(value_ptr, &end_ptr, 10);
625 if((end_ptr != value_ptr) && (num < ULONG_MAX)) {
626 if(strcasecompare("ma", option))
627 maxage = num;
628 else if(strcasecompare("persist", option) && (num == 1))
629 persist = TRUE;
630 }
631 }
632 if(dstalpnid && valid) {
633 if(!entries++)
634 /* Flush cached alternatives for this source origin, if any - when
635 this is the first entry of the line. */
636 altsvc_flush(asi, srcalpnid, srchost, srcport);
637
638 as = altsvc_createid(srchost, dsthost,
639 srcalpnid, dstalpnid,
640 srcport, dstport);
641 if(as) {
642 /* The expires time also needs to take the Age: value (if any) into
643 account. [See RFC 7838 section 3.1] */
644 as->expires = maxage + time(NULL);
645 as->persist = persist;
646 Curl_llist_insert_next(&asi->list, asi->list.tail, as, &as->node);
647 infof(data, "Added alt-svc: %s:%d over %s", dsthost, dstport,
648 Curl_alpnid2str(dstalpnid));
649 }
650 }
651 }
652 else
653 break;
654 /* after the double quote there can be a comma if there's another
655 string or a semicolon if no more */
656 if(*p == ',') {
657 /* comma means another alternative is presented */
658 p++;
659 result = getalnum(&p, alpnbuf, sizeof(alpnbuf));
660 if(result)
661 break;
662 }
663 }
664 else
665 break;
666 } while(*p && (*p != ';') && (*p != '\n') && (*p != '\r'));
667
668 return CURLE_OK;
669}
670
671/*
672 * Return TRUE on a match
673 */
674bool Curl_altsvc_lookup(struct altsvcinfo *asi,
675 enum alpnid srcalpnid, const char *srchost,
676 int srcport,
677 struct altsvc **dstentry,
678 const int versions) /* one or more bits */
679{
680 struct Curl_llist_element *e;
681 struct Curl_llist_element *n;
682 time_t now = time(NULL);
683 DEBUGASSERT(asi);
684 DEBUGASSERT(srchost);
685 DEBUGASSERT(dstentry);
686
687 for(e = asi->list.head; e; e = n) {
688 struct altsvc *as = e->ptr;
689 n = e->next;
690 if(as->expires < now) {
691 /* an expired entry, remove */
692 Curl_llist_remove(&asi->list, e, NULL);
693 altsvc_free(as);
694 continue;
695 }
696 if((as->src.alpnid == srcalpnid) &&
697 hostcompare(srchost, as->src.host) &&
698 (as->src.port == srcport) &&
699 (versions & as->dst.alpnid)) {
700 /* match */
701 *dstentry = as;
702 return TRUE;
703 }
704 }
705 return FALSE;
706}
707
708#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_ALTSVC */
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette