diff options
Diffstat (limited to 'libs/libcurl/src/altsvc.c')
-rw-r--r-- | libs/libcurl/src/altsvc.c | 254 |
1 files changed, 156 insertions, 98 deletions
diff --git a/libs/libcurl/src/altsvc.c b/libs/libcurl/src/altsvc.c index bf869c37a5..c39d86eaff 100644 --- a/libs/libcurl/src/altsvc.c +++ b/libs/libcurl/src/altsvc.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2019, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2019 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -34,6 +34,8 @@ #include "parsedate.h" #include "sendf.h" #include "warnless.h" +#include "rand.h" +#include "rename.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -48,19 +50,20 @@ #define MAX_ALTSVC_ALPNLENSTR "10" #define MAX_ALTSVC_ALPNLEN 10 +#if (defined(USE_QUICHE) || defined(USE_NGTCP2)) && !defined(UNITTESTS) +#define H3VERSION "h3-27" +#else +#define H3VERSION "h3" +#endif + static enum alpnid alpn2alpnid(char *name) { if(strcasecompare(name, "h1")) return ALPN_h1; if(strcasecompare(name, "h2")) return ALPN_h2; -#if (defined(USE_QUICHE) || defined(USE_NGTCP2)) && !defined(UNITTESTS) - if(strcasecompare(name, "h3-24")) - return ALPN_h3; -#else - if(strcasecompare(name, "h3")) + if(strcasecompare(name, H3VERSION)) return ALPN_h3; -#endif return ALPN_none; /* unknown, probably rubbish input */ } @@ -73,11 +76,7 @@ const char *Curl_alpnid2str(enum alpnid id) case ALPN_h2: return "h2"; case ALPN_h3: -#if (defined(USE_QUICHE) || defined(USE_NGTCP2)) && !defined(UNITTESTS) - return "h3-24"; -#else - return "h3"; -#endif + return H3VERSION; default: return ""; /* bad */ } @@ -188,7 +187,16 @@ static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file) { CURLcode result = CURLE_OK; char *line = NULL; - FILE *fp = fopen(file, FOPEN_READTEXT); + FILE *fp; + + /* we need a private copy of the file name so that the altsvc cache file + name survives an easy handle reset */ + free(asi->filename); + asi->filename = strdup(file); + if(!asi->filename) + return CURLE_OUT_OF_MEMORY; + + fp = fopen(file, FOPEN_READTEXT); if(fp) { line = malloc(MAX_ALTSVC_LINE); if(!line) @@ -209,6 +217,7 @@ static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file) return result; fail: + Curl_safefree(asi->filename); free(line); fclose(fp); return CURLE_OUT_OF_MEMORY; @@ -302,6 +311,7 @@ void Curl_altsvc_cleanup(struct altsvcinfo *altsvc) n = e->next; altsvc_free(as); } + free(altsvc->filename); free(altsvc); } } @@ -309,34 +319,57 @@ void Curl_altsvc_cleanup(struct altsvcinfo *altsvc) /* * Curl_altsvc_save() writes the altsvc cache to a file. */ -CURLcode Curl_altsvc_save(struct altsvcinfo *altsvc, const char *file) +CURLcode Curl_altsvc_save(struct Curl_easy *data, + struct altsvcinfo *altsvc, const char *file) { struct curl_llist_element *e; struct curl_llist_element *n; CURLcode result = CURLE_OK; FILE *out; + char *tempstore; + unsigned char randsuffix[9]; if(!altsvc) /* no cache activated */ return CURLE_OK; + /* if not new name is given, use the one we stored from the load */ + if(!file && altsvc->filename) + file = altsvc->filename; + if((altsvc->flags & CURLALTSVC_READONLYFILE) || !file || !file[0]) /* marked as read-only, no file or zero length file name */ return CURLE_OK; - out = fopen(file, FOPEN_WRITETEXT); + + if(Curl_rand_hex(data, randsuffix, sizeof(randsuffix))) + return CURLE_FAILED_INIT; + + tempstore = aprintf("%s.%s.tmp", file, randsuffix); + if(!tempstore) + return CURLE_OUT_OF_MEMORY; + + out = fopen(tempstore, FOPEN_WRITETEXT); if(!out) - return CURLE_WRITE_ERROR; - fputs("# Your alt-svc cache. https://curl.haxx.se/docs/alt-svc.html\n" - "# This file was generated by libcurl! Edit at your own risk.\n", - out); - for(e = altsvc->list.head; e; e = n) { - struct altsvc *as = e->ptr; - n = e->next; - result = altsvc_out(as, out); + result = CURLE_WRITE_ERROR; + else { + fputs("# Your alt-svc cache. https://curl.haxx.se/docs/alt-svc.html\n" + "# This file was generated by libcurl! Edit at your own risk.\n", + out); + for(e = altsvc->list.head; e; e = n) { + struct altsvc *as = e->ptr; + n = e->next; + result = altsvc_out(as, out); + if(result) + break; + } + fclose(out); + if(!result && Curl_rename(tempstore, file)) + result = CURLE_WRITE_ERROR; + if(result) - break; + unlink(tempstore); } - fclose(out); + free(tempstore); return result; } @@ -351,12 +384,12 @@ static CURLcode getalnum(const char **ptr, char *alpnbuf, size_t buflen) while(*p && !ISBLANK(*p) && (*p != ';') && (*p != '=')) p++; len = p - protop; + *ptr = p; if(!len || (len >= buflen)) return CURLE_BAD_FUNCTION_ARGUMENT; memcpy(alpnbuf, protop, len); alpnbuf[len] = 0; - *ptr = p; return CURLE_OK; } @@ -402,6 +435,10 @@ static time_t debugtime(void *unused) * * 'value' points to the header *value*. That's contents to the right of the * header name. + * + * Currently this function rejects invalid data without returning an error. + * Invalid host name, port number will result in the specific alternative + * being rejected. Unknown protocols are skipped. */ CURLcode Curl_altsvc_parse(struct Curl_easy *data, struct altsvcinfo *asi, const char *value, @@ -415,12 +452,11 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, char alpnbuf[MAX_ALTSVC_ALPNLEN] = ""; struct altsvc *as; unsigned short dstport = srcport; /* the same by default */ - const char *semip; - time_t maxage = 24 * 3600; /* default is 24 hours */ - bool persist = FALSE; CURLcode result = getalnum(&p, alpnbuf, sizeof(alpnbuf)); - if(result) - return result; + if(result) { + infof(data, "Excessive alt-svc header, ignoring...\n"); + return CURLE_OK; + } DEBUGASSERT(asi); @@ -432,57 +468,20 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, return CURLE_OK; } - /* The 'ma' and 'persist' flags are annoyingly meant for all alternatives - but are set after the list on the line. Scan for the semicolons and get - those fields first! */ - semip = p; - do { - semip = strchr(semip, ';'); - if(semip) { - char option[32]; - unsigned long num; - char *end_ptr; - bool quoted = FALSE; - semip++; /* pass the semicolon */ - result = getalnum(&semip, option, sizeof(option)); - if(result) - break; - while(*semip && ISBLANK(*semip)) - semip++; - if(*semip != '=') - continue; - semip++; - while(*semip && ISBLANK(*semip)) - semip++; - if(*semip == '\"') { - /* quoted value */ - semip++; - quoted = TRUE; - } - num = strtoul(semip, &end_ptr, 10); - if((end_ptr != semip) && num && (num < ULONG_MAX)) { - if(strcasecompare("ma", option)) - maxage = num; - else if(strcasecompare("persist", option) && (num == 1)) - persist = TRUE; - if(quoted && (*end_ptr == '\"')) - end_ptr++; - } - semip = end_ptr; - } - } while(semip); - do { if(*p == '=') { /* [protocol]="[host][:port]" */ dstalpnid = alpn2alpnid(alpnbuf); - if(!dstalpnid) { - infof(data, "Unknown alt-svc protocol \"%s\", ignoring...\n", alpnbuf); - return CURLE_OK; - } p++; if(*p == '\"') { const char *dsthost; + const char *value_ptr; + char option[32]; + unsigned long num; + char *end_ptr; + bool quoted = FALSE; + time_t maxage = 24 * 3600; /* default is 24 hours */ + bool persist = FALSE; p++; if(*p != ':') { /* host name starts here */ @@ -490,11 +489,15 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, while(*p && (ISALNUM(*p) || (*p == '.') || (*p == '-'))) p++; len = p - hostp; - if(!len || (len >= MAX_ALTSVC_HOSTLEN)) - return CURLE_BAD_FUNCTION_ARGUMENT; - memcpy(namebuf, hostp, len); - namebuf[len] = 0; - dsthost = namebuf; + if(!len || (len >= MAX_ALTSVC_HOSTLEN)) { + infof(data, "Excessive alt-svc host name, ignoring...\n"); + dstalpnid = ALPN_none; + } + else { + memcpy(namebuf, hostp, len); + namebuf[len] = 0; + dsthost = namebuf; + } } else { /* no destination name, use source host */ @@ -502,31 +505,86 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, } if(*p == ':') { /* a port number */ - char *end_ptr; unsigned long port = strtoul(++p, &end_ptr, 10); if(port > USHRT_MAX || end_ptr == p || *end_ptr != '\"') { infof(data, "Unknown alt-svc port number, ignoring...\n"); - return CURLE_OK; + dstalpnid = ALPN_none; } p = end_ptr; dstport = curlx_ultous(port); } if(*p++ != '\"') - return CURLE_BAD_FUNCTION_ARGUMENT; - as = altsvc_createid(srchost, dsthost, - srcalpnid, dstalpnid, - srcport, dstport); - if(as) { - /* The expires time also needs to take the Age: value (if any) into - account. [See RFC 7838 section 3.1] */ - as->expires = maxage + time(NULL); - as->persist = persist; - Curl_llist_insert_next(&asi->list, asi->list.tail, as, &as->node); - asi->num++; /* one more entry */ - infof(data, "Added alt-svc: %s:%d over %s\n", dsthost, dstport, - Curl_alpnid2str(dstalpnid)); + break; + /* Handle the optional 'ma' and 'persist' flags. Unknown flags + are skipped. */ + for(;;) { + while(*p && ISBLANK(*p) && *p != ';' && *p != ',') + p++; + if(!*p || *p == ',') + break; + p++; /* pass the semicolon */ + if(!*p) + break; + result = getalnum(&p, option, sizeof(option)); + if(result) { + /* skip option if name is too long */ + option[0] = '\0'; + } + while(*p && ISBLANK(*p)) + p++; + if(*p != '=') + return CURLE_OK; + p++; + while(*p && ISBLANK(*p)) + p++; + if(!*p) + return CURLE_OK; + if(*p == '\"') { + /* quoted value */ + p++; + quoted = TRUE; + } + value_ptr = p; + if(quoted) { + while(*p && *p != '\"') + p++; + if(!*p++) + return CURLE_OK; + } + else { + while(*p && !ISBLANK(*p) && *p!= ';' && *p != ',') + p++; + } + num = strtoul(value_ptr, &end_ptr, 10); + if((end_ptr != value_ptr) && (num < ULONG_MAX)) { + if(strcasecompare("ma", option)) + maxage = num; + else if(strcasecompare("persist", option) && (num == 1)) + persist = TRUE; + } + } + if(dstalpnid) { + as = altsvc_createid(srchost, dsthost, + srcalpnid, dstalpnid, + srcport, dstport); + if(as) { + /* The expires time also needs to take the Age: value (if any) into + account. [See RFC 7838 section 3.1] */ + as->expires = maxage + time(NULL); + as->persist = persist; + Curl_llist_insert_next(&asi->list, asi->list.tail, as, &as->node); + asi->num++; /* one more entry */ + infof(data, "Added alt-svc: %s:%d over %s\n", dsthost, dstport, + Curl_alpnid2str(dstalpnid)); + } + } + else { + infof(data, "Unknown alt-svc protocol \"%s\", skipping...\n", + alpnbuf); } } + else + break; /* after the double quote there can be a comma if there's another string or a semicolon if no more */ if(*p == ',') { @@ -534,11 +592,11 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, p++; result = getalnum(&p, alpnbuf, sizeof(alpnbuf)); if(result) - /* failed to parse, but since we already did at least one host we - return OK */ - return CURLE_OK; + break; } } + else + break; } while(*p && (*p != ';') && (*p != '\n') && (*p != '\r')); return CURLE_OK; |