From 25ca79df1ee35a5866fb13f8bdd601d44c907bfc Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 27 Aug 2023 00:06:02 +0200 Subject: [PATCH] altsvc: accept and parse IPv6 addresses in response headers Store numerical IPv6 addresses in the alt-svc file with the brackets present. Verify with test 437 and 438 Fixes #11737 Reported-by: oliverpool on github Closes #11743 --- docs/ALTSVC.md | 3 ++ lib/altsvc.c | 82 +++++++++++++++++++++++++++++++------ tests/data/Makefile.inc | 2 +- tests/data/test437 | 68 +++++++++++++++++++++++++++++++ tests/data/test438 | 90 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 231 insertions(+), 14 deletions(-) create mode 100644 tests/data/test437 create mode 100644 tests/data/test438 diff --git a/docs/ALTSVC.md b/docs/ALTSVC.md index 560b43748a..b9117e4d46 100644 --- a/docs/ALTSVC.md +++ b/docs/ALTSVC.md @@ -33,6 +33,9 @@ space separated fields. 8. Boolean (1 or 0) if "persist" was set for this entry 9. Integer priority value (not currently used) +If the host name is an IPv6 numerical address, it is stored with brackets such +as `[::1]`. + # TODO - handle multiple response headers, when one of them says `clear` (should diff --git a/lib/altsvc.c b/lib/altsvc.c index 11009d5ac8..22b0b69c77 100644 --- a/lib/altsvc.c +++ b/lib/altsvc.c @@ -38,6 +38,8 @@ #include "warnless.h" #include "fopen.h" #include "rename.h" +#include "strdup.h" +#include "inet_pton.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -97,19 +99,39 @@ static struct altsvc *altsvc_createid(const char *srchost, { struct altsvc *as = calloc(sizeof(struct altsvc), 1); size_t hlen; + size_t dlen; if(!as) return NULL; hlen = strlen(srchost); + dlen = strlen(dsthost); DEBUGASSERT(hlen); - as->src.host = strdup(srchost); + DEBUGASSERT(dlen); + if(!hlen || !dlen) + /* bad input */ + return NULL; + if((hlen > 2) && srchost[0] == '[') { + /* IPv6 address, strip off brackets */ + srchost++; + hlen -= 2; + } + else if(srchost[hlen - 1] == '.') + /* strip off trailing dot */ + hlen--; + if((dlen > 2) && dsthost[0] == '[') { + /* IPv6 address, strip off brackets */ + dsthost++; + dlen -= 2; + } + + as->src.host = Curl_memdup(srchost, hlen + 1); if(!as->src.host) goto error; - if(hlen && (srchost[hlen - 1] == '.')) - /* strip off trailing any dot */ - as->src.host[--hlen] = 0; - as->dst.host = strdup(dsthost); + as->src.host[hlen] = 0; + + as->dst.host = Curl_memdup(dsthost, dlen + 1); if(!as->dst.host) goto error; + as->dst.host[dlen] = 0; as->src.alpnid = srcalpnid; as->dst.alpnid = dstalpnid; @@ -231,18 +253,40 @@ fail: static CURLcode altsvc_out(struct altsvc *as, FILE *fp) { struct tm stamp; + const char *dst6_pre = ""; + const char *dst6_post = ""; + const char *src6_pre = ""; + const char *src6_post = ""; CURLcode result = Curl_gmtime(as->expires, &stamp); if(result) return result; - +#ifdef ENABLE_IPV6 + else { + char ipv6_unused[16]; + if(1 == Curl_inet_pton(AF_INET6, as->dst.host, ipv6_unused)) { + dst6_pre = "["; + dst6_post = "]"; + } + if(1 == Curl_inet_pton(AF_INET6, as->src.host, ipv6_unused)) { + src6_pre = "["; + src6_post = "]"; + } + } +#endif fprintf(fp, - "%s %s %u " - "%s %s %u " + "%s %s%s%s %u " + "%s %s%s%s %u " "\"%d%02d%02d " "%02d:%02d:%02d\" " "%u %d\n", - Curl_alpnid2str(as->src.alpnid), as->src.host, as->src.port, - Curl_alpnid2str(as->dst.alpnid), as->dst.host, as->dst.port, + Curl_alpnid2str(as->src.alpnid), + src6_pre, as->src.host, src6_post, + as->src.port, + + Curl_alpnid2str(as->dst.alpnid), + dst6_pre, as->dst.host, dst6_post, + as->dst.port, + stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday, stamp.tm_hour, stamp.tm_min, stamp.tm_sec, as->persist, as->prio); @@ -500,9 +544,21 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, if(*p != ':') { /* host name starts here */ const char *hostp = p; - while(*p && (ISALNUM(*p) || (*p == '.') || (*p == '-'))) - p++; - len = p - hostp; + if(*p == '[') { + /* pass all valid IPv6 letters - does not handle zone id */ + len = strspn(++p, "0123456789abcdefABCDEF:."); + if(p[len] != ']') + /* invalid host syntax, bail out */ + break; + /* we store the IPv6 numerical address *with* brackets */ + len += 2; + p = &p[len-1]; + } + else { + while(*p && (ISALNUM(*p) || (*p == '.') || (*p == '-'))) + p++; + len = p - hostp; + } if(!len || (len >= MAX_ALTSVC_HOSTLEN)) { infof(data, "Excessive alt-svc host name, ignoring."); valid = FALSE; diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc index 78edff66c4..a0015b3b20 100644 --- a/tests/data/Makefile.inc +++ b/tests/data/Makefile.inc @@ -70,7 +70,7 @@ test399 test400 test401 test402 test403 test404 test405 test406 test407 \ test408 test409 test410 test411 test412 test413 test414 test415 test416 \ test417 test418 test419 test420 test421 test422 test423 test424 test425 \ test426 test427 test428 test429 test430 test431 test432 test433 test434 \ -test435 test436 \ +test435 test436 test437 test438 \ \ test440 test441 test442 test443 test444 test445 test446 test447 test448 \ test449 test450 test451 test452 test453 test454 test455 test456 \ diff --git a/tests/data/test437 b/tests/data/test437 new file mode 100644 index 0000000000..a49fb8c48b --- /dev/null +++ b/tests/data/test437 @@ -0,0 +1,68 @@ + + + +HTTP +Alt-Svc + + + +# +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes +Alt-Svc: h1="[ffff::1]:8181" + +-foo- + + + +# +# Client-side + + +debug +alt-svc + + +http + + +Alt-Svc to numerical IPv6 address + + +# make debug-curl accept Alt-Svc over plain HTTP +CURL_ALTSVC_HTTP="yeah" + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER --alt-svc "%LOGDIR/altsvc-%TESTNUMBER" + + + +# +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + +# strip out the (dynamic) expire date from the file so that the rest +# matches +s/\"([^\"]*)\"/TIMESTAMP/ + + +# Your alt-svc cache. https://curl.se/docs/alt-svc.html +# This file was generated by libcurl! Edit at your own risk. +h1 %HOSTIP %HTTPPORT h1 [ffff::1] 8181 TIMESTAMP 0 0 + + + diff --git a/tests/data/test438 b/tests/data/test438 new file mode 100644 index 0000000000..9015e927e8 --- /dev/null +++ b/tests/data/test438 @@ -0,0 +1,90 @@ + + + +HTTP +HTTP GET +Alt-Svc +HTTP/2 + + + +# +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-Head: yesyes +Alt-Svc: h1="%HOST6IP:%HTTP6PORT", ma=315360000; persist=0 + +-foo- + + + +# +# Client-side + + +alt-svc +debug +ipv6 + + +http +http-ipv6 + + +HTTPS IPv4 GET translated by alt-svc to IPv6 address + + +# make debug-curl accept Alt-Svc over plain HTTP +CURL_ALTSVC_HTTP="yeah" + + +--alt-svc "%LOGDIR/altsvc-%TESTNUMBER" "http://%HOSTIP:%HTTPPORT/%TESTNUMBER" "http://%HOSTIP:%HTTPPORT/%TESTNUMBER" + + +h1 %HOSTIP %HTTPPORT h1 %HOST6IP %HTTP6PORT "20290222 22:19:28" 0 0 + + + + +# +# Verify data after the test has been "shot" + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-Head: yesyes +Alt-Svc: h1="%HOST6IP:%HTTP6PORT", ma=315360000; persist=0 + +-foo- +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-Head: yesyes +Alt-Svc: h1="%HOST6IP:%HTTP6PORT", ma=315360000; persist=0 + +-foo- + + +s/^server: nghttpx.*\r?\n// +# strip out the (dynamic) expire date from the file so that the rest +# matches +s/\"2([^\"]*)\"/TIMESTAMP/ + + +# Your alt-svc cache. https://curl.se/docs/alt-svc.html +# This file was generated by libcurl! Edit at your own risk. +h1 %HOSTIP %HTTPPORT h1 %HOST6IP %HTTP6PORT TIMESTAMP 0 0 + + +