From 69b3ff5118be4eb6fdd9ef645b955cac7d2fe0ba Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 7 Aug 2019 20:10:27 +0200 Subject: [PATCH] alt-svc: add protocol version selection masking So that users can mask in/out specific HTTP versions when Alt-Svc is used. - Removed "h2c" and updated test case accordingly - Changed how the altsvc struct is laid out - Added ifdefs to make the unittest run even in a quiche-tree Closes #4201 --- lib/altsvc.c | 57 ++++++++++++++++++++----------------------- lib/altsvc.h | 27 ++++++++++---------- lib/url.c | 41 +++++++++++++++++++------------ tests/data/test1654 | 6 ++--- tests/unit/unit1654.c | 2 +- 5 files changed, 69 insertions(+), 64 deletions(-) diff --git a/lib/altsvc.c b/lib/altsvc.c index b20ec13ae..abea66c69 100644 --- a/lib/altsvc.c +++ b/lib/altsvc.c @@ -54,9 +54,7 @@ static enum alpnid alpn2alpnid(char *name) return ALPN_h1; if(strcasecompare(name, "h2")) return ALPN_h2; - if(strcasecompare(name, "h2c")) - return ALPN_h2c; -#ifdef USE_QUICHE +#if defined(USE_QUICHE) && !defined(UNITTESTS) if(strcasecompare(name, "h3-22")) return ALPN_h3; #else @@ -74,10 +72,8 @@ const char *Curl_alpnid2str(enum alpnid id) return "h1"; case ALPN_h2: return "h2"; - case ALPN_h2c: - return "h2c"; case ALPN_h3: -#ifdef USE_QUICHE +#if defined(USE_QUICHE) && !defined(UNITTESTS) return "h3-22"; #else return "h3"; @@ -90,8 +86,8 @@ const char *Curl_alpnid2str(enum alpnid id) static void altsvc_free(struct altsvc *as) { - free(as->srchost); - free(as->dsthost); + free(as->src.host); + free(as->dst.host); free(as); } @@ -106,17 +102,17 @@ static struct altsvc *altsvc_createid(const char *srchost, if(!as) return NULL; - as->srchost = strdup(srchost); - if(!as->srchost) + as->src.host = strdup(srchost); + if(!as->src.host) goto error; - as->dsthost = strdup(dsthost); - if(!as->dsthost) + as->dst.host = strdup(dsthost); + if(!as->dst.host) goto error; - as->srcalpnid = srcalpnid; - as->dstalpnid = dstalpnid; - as->srcport = curlx_ultous(srcport); - as->dstport = curlx_ultous(dstport); + as->src.alpnid = srcalpnid; + as->dst.alpnid = dstalpnid; + as->src.port = curlx_ultous(srcport); + as->dst.port = curlx_ultous(dstport); return as; error: @@ -235,8 +231,8 @@ static CURLcode altsvc_out(struct altsvc *as, FILE *fp) "\"%d%02d%02d " "%02d:%02d:%02d\" " "%u %d\n", - Curl_alpnid2str(as->srcalpnid), as->srchost, as->srcport, - Curl_alpnid2str(as->dstalpnid), as->dsthost, as->dstport, + Curl_alpnid2str(as->src.alpnid), as->src.host, as->src.port, + Curl_alpnid2str(as->dst.alpnid), as->dst.host, 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); @@ -261,7 +257,7 @@ struct altsvcinfo *Curl_altsvc_init(void) #ifdef USE_NGHTTP2 | CURLALTSVC_H2 #endif -#ifdef USE_HTTP3 +#ifdef ENABLE_QUIC | CURLALTSVC_H3 #endif ; @@ -374,9 +370,9 @@ static void altsvc_flush(struct altsvcinfo *asi, enum alpnid srcalpnid, for(e = asi->list.head; e; e = n) { struct altsvc *as = e->ptr; n = e->next; - if((srcalpnid == as->srcalpnid) && - (srcport == as->srcport) && - strcasecompare(srchost, as->srchost)) { + if((srcalpnid == as->src.alpnid) && + (srcport == as->src.port) && + strcasecompare(srchost, as->src.host)) { Curl_llist_remove(&asi->list, e, NULL); altsvc_free(as); asi->num--; @@ -544,15 +540,15 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, bool Curl_altsvc_lookup(struct altsvcinfo *asi, enum alpnid srcalpnid, const char *srchost, int srcport, - enum alpnid *dstalpnid, const char **dsthost, - int *dstport) + struct altsvc **dstentry, + const int versions) /* one or more bits */ { struct curl_llist_element *e; struct curl_llist_element *n; time_t now = time(NULL); DEBUGASSERT(asi); DEBUGASSERT(srchost); - DEBUGASSERT(dsthost); + DEBUGASSERT(dstentry); for(e = asi->list.head; e; e = n) { struct altsvc *as = e->ptr; @@ -563,13 +559,12 @@ bool Curl_altsvc_lookup(struct altsvcinfo *asi, altsvc_free(as); continue; } - if((as->srcalpnid == srcalpnid) && - strcasecompare(as->srchost, srchost) && - as->srcport == srcport) { + if((as->src.alpnid == srcalpnid) && + strcasecompare(as->src.host, srchost) && + (as->src.port == srcport) && + (versions & as->dst.alpnid)) { /* match */ - *dstalpnid = as->dstalpnid; - *dsthost = as->dsthost; - *dstport = as->dstport; + *dstentry = as; return TRUE; } } diff --git a/lib/altsvc.h b/lib/altsvc.h index eefb45bf6..99d0499af 100644 --- a/lib/altsvc.h +++ b/lib/altsvc.h @@ -28,20 +28,21 @@ #include "llist.h" enum alpnid { - ALPN_none, - ALPN_h1, - ALPN_h2, - ALPN_h2c, - ALPN_h3 + ALPN_none = 0, + ALPN_h1 = CURLALTSVC_H1, + ALPN_h2 = CURLALTSVC_H2, + ALPN_h3 = CURLALTSVC_H3 +}; + +struct althost { + char *host; + unsigned short port; + enum alpnid alpnid; }; struct altsvc { - char *srchost; - char *dsthost; - unsigned short srcport; - unsigned short dstport; - enum alpnid srcalpnid; - enum alpnid dstalpnid; + struct althost src; + struct althost dst; time_t expires; bool persist; int prio; @@ -68,8 +69,8 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, bool Curl_altsvc_lookup(struct altsvcinfo *asi, enum alpnid srcalpnid, const char *srchost, int srcport, - enum alpnid *dstalpnid, const char **dsthost, - int *dstport); + struct altsvc **dstentry, + int versions); /* one or more CURLALTSVC_H* bits */ #else /* disabled */ #define Curl_altsvc_save(a,b) diff --git a/lib/url.c b/lib/url.c index 0b681803d..30263258f 100644 --- a/lib/url.c +++ b/lib/url.c @@ -3158,42 +3158,51 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, if(data->asi && !host && (port == -1) && (conn->handler->protocol == CURLPROTO_HTTPS)) { /* no connect_to match, try alt-svc! */ - const char *nhost; - int nport; - enum alpnid nalpnid; - enum alpnid salpnid; + enum alpnid srcalpnid; bool hit; + struct altsvc *as; + const int allowed_versions = ( ALPN_h1 +#ifdef USE_NGHTTP2 + | ALPN_h2 +#endif +#ifdef ENABLE_QUIC + | ALPN_h3 +#endif + ) & data->asi->flags; + host = conn->host.rawalloc; #ifdef USE_NGHTTP2 /* with h2 support, check that first */ - salpnid = ALPN_h2; + srcalpnid = ALPN_h2; hit = Curl_altsvc_lookup(data->asi, - salpnid, host, conn->remote_port, /* from */ - &nalpnid, &nhost, &nport /* to */); + srcalpnid, host, conn->remote_port, /* from */ + &as /* to */, + allowed_versions); if(!hit) #endif { - salpnid = ALPN_h1; + srcalpnid = ALPN_h1; hit = Curl_altsvc_lookup(data->asi, - salpnid, host, conn->remote_port, /* from */ - &nalpnid, &nhost, &nport /* to */); + srcalpnid, host, conn->remote_port, /* from */ + &as /* to */, + allowed_versions); } if(hit) { - char *hostd = strdup((char *)nhost); + char *hostd = strdup((char *)as->dst.host); if(!hostd) return CURLE_OUT_OF_MEMORY; conn->conn_to_host.rawalloc = hostd; conn->conn_to_host.name = hostd; conn->bits.conn_to_host = TRUE; - conn->conn_to_port = nport; + conn->conn_to_port = as->dst.port; conn->bits.conn_to_port = TRUE; conn->bits.altused = TRUE; infof(data, "Alt-svc connecting from [%s]%s:%d to [%s]%s:%d\n", - Curl_alpnid2str(salpnid), host, conn->remote_port, - Curl_alpnid2str(nalpnid), hostd, nport); - if(salpnid != nalpnid) { + Curl_alpnid2str(srcalpnid), host, conn->remote_port, + Curl_alpnid2str(as->dst.alpnid), hostd, as->dst.port); + if(srcalpnid != as->dst.alpnid) { /* protocol version switch */ - switch(nalpnid) { + switch(as->dst.alpnid) { case ALPN_h1: conn->httpversion = 11; break; diff --git a/tests/data/test1654 b/tests/data/test1654 index 175076c8a..5b32cb419 100644 --- a/tests/data/test1654 +++ b/tests/data/test1654 @@ -32,7 +32,7 @@ unit1654 h2 example.com 443 h3 shiny.example.com 8443 "20191231 00:00:00" 0 1 # a comment -h2c example.com 443 h3 shiny.example.com 8443 "20291231 23:30:00" 0 1 +h2 foo.example.com 443 h3 shiny.example.com 8443 "20291231 23:30:00" 0 1 h1 example.com 443 h3 shiny.example.com 8443 "20121231 00:00:01" 0 1 h3 example.com 443 h3 shiny.example.com 8443 "20131231 00:00:00" 0 1 # also a comment @@ -45,14 +45,14 @@ rubbish # Your alt-svc cache. https://curl.haxx.se/docs/alt-svc.html # This file was generated by libcurl! Edit at your own risk. h2 example.com 443 h3 shiny.example.com 8443 "20191231 00:00:00" 0 1 -h2c example.com 443 h3 shiny.example.com 8443 "20291231 23:30:00" 0 1 +h2 foo.example.com 443 h3 shiny.example.com 8443 "20291231 23:30:00" 0 1 h1 example.com 443 h3 shiny.example.com 8443 "20121231 00:00:01" 0 1 h3 example.com 443 h3 shiny.example.com 8443 "20131231 00:00:00" 0 1 h1 example.org 8080 h2 example.com 8080 "20190125 22:34:21" 0 0 h1 2.example.org 8080 h3 2.example.org 8080 "20190125 22:34:21" 0 0 h1 3.example.org 8080 h2 example.com 8080 "20190125 22:34:21" 0 0 h1 3.example.org 8080 h3 yesyes.com 8080 "20190125 22:34:21" 0 0 -h2c example.org 80 h2 example.com 443 "20190124 22:36:21" 0 0 +h2 example.org 80 h2 example.com 443 "20190124 22:36:21" 0 0 diff --git a/tests/unit/unit1654.c b/tests/unit/unit1654.c index 9d1a3e211..51fc5d16f 100644 --- a/tests/unit/unit1654.c +++ b/tests/unit/unit1654.c @@ -90,7 +90,7 @@ UNITTEST_START fail_unless(asi->num == 8, "wrong number of entries"); result = Curl_altsvc_parse(curl, asi, "h2=\"example.com:443\"; ma = 120;", - ALPN_h2c, "example.org", 80); + ALPN_h2, "example.org", 80); if(result) { fprintf(stderr, "Curl_altsvc_parse(4) failed!\n"); unitfail++;