#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"
{
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;
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);
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;
--- /dev/null
+<testcase>
+<info>
+<keywords>
+HTTP
+Alt-Svc
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<data>
+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-
+</data>
+</reply>
+
+#
+# Client-side
+<client>
+<features>
+debug
+alt-svc
+</features>
+<server>
+http
+</server>
+ <name>
+Alt-Svc to numerical IPv6 address
+ </name>
+<setenv>
+# make debug-curl accept Alt-Svc over plain HTTP
+CURL_ALTSVC_HTTP="yeah"
+</setenv>
+ <command>
+http://%HOSTIP:%HTTPPORT/%TESTNUMBER --alt-svc "%LOGDIR/altsvc-%TESTNUMBER"
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<protocol>
+GET /%TESTNUMBER HTTP/1.1\r
+Host: %HOSTIP:%HTTPPORT\r
+User-Agent: curl/%VERSION\r
+Accept: */*\r
+\r
+</protocol>
+<stripfile>
+# strip out the (dynamic) expire date from the file so that the rest
+# matches
+s/\"([^\"]*)\"/TIMESTAMP/
+</stripfile>
+<file name="%LOGDIR/altsvc-%TESTNUMBER" mode="text">
+# 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
+</file>
+</verify>
+</testcase>
--- /dev/null
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+Alt-Svc
+HTTP/2
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<data nocheck="yes">
+HTTP/1.1 200 OK\r
+Date: Tue, 09 Nov 2010 14:49:00 GMT\r
+Content-Length: 6\r
+Connection: close\r
+Content-Type: text/html\r
+Funny-Head: yesyes\r
+Alt-Svc: h1="%HOST6IP:%HTTP6PORT", ma=315360000; persist=0\r
+\r
+-foo-
+</data>
+</reply>
+
+#
+# Client-side
+<client>
+<features>
+alt-svc
+debug
+ipv6
+</features>
+<server>
+http
+http-ipv6
+</server>
+ <name>
+HTTPS IPv4 GET translated by alt-svc to IPv6 address
+ </name>
+<setenv>
+# make debug-curl accept Alt-Svc over plain HTTP
+CURL_ALTSVC_HTTP="yeah"
+</setenv>
+ <command>
+--alt-svc "%LOGDIR/altsvc-%TESTNUMBER" "http://%HOSTIP:%HTTPPORT/%TESTNUMBER" "http://%HOSTIP:%HTTPPORT/%TESTNUMBER"
+</command>
+<file name="%LOGDIR/altsvc-%TESTNUMBER">
+h1 %HOSTIP %HTTPPORT h1 %HOST6IP %HTTP6PORT "20290222 22:19:28" 0 0
+</file>
+
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<stdout>
+HTTP/1.1 200 OK\r
+Date: Tue, 09 Nov 2010 14:49:00 GMT\r
+Content-Length: 6\r
+Connection: close\r
+Content-Type: text/html\r
+Funny-Head: yesyes\r
+Alt-Svc: h1="%HOST6IP:%HTTP6PORT", ma=315360000; persist=0\r
+\r
+-foo-
+HTTP/1.1 200 OK\r
+Date: Tue, 09 Nov 2010 14:49:00 GMT\r
+Content-Length: 6\r
+Connection: close\r
+Content-Type: text/html\r
+Funny-Head: yesyes\r
+Alt-Svc: h1="%HOST6IP:%HTTP6PORT", ma=315360000; persist=0\r
+\r
+-foo-
+</stdout>
+<stripfile>
+s/^server: nghttpx.*\r?\n//
+# strip out the (dynamic) expire date from the file so that the rest
+# matches
+s/\"2([^\"]*)\"/TIMESTAMP/
+</stripfile>
+<file name="%LOGDIR/altsvc-%TESTNUMBER" mode="text">
+# 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
+</file>
+</verify>
+</testcase>