From 81ade541200869e07880c59183a578d7102517d2 Mon Sep 17 00:00:00 2001 From: Arham Wani Date: Sun, 23 Aug 2026 14:42:03 +0530 Subject: [PATCH] altsvc: continue after unknown parameters Parse extension names and values before deciding whether a parameter is known. This prevents long names and token or quoted-string values from hiding later ma or persist parameters, as required by RFC 7838 section 3. Add unit coverage for long extension names and both value forms, including an escaped quote and semicolon inside a quoted string. Fixes #22644 Closes #22676 --- lib/altsvc.c | 50 ++++++++++++++++++++++++++----------------- tests/unit/unit1654.c | 24 +++++++++++++++++++++ 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/lib/altsvc.c b/lib/altsvc.c index 11c75d45df..c57d3f6b40 100644 --- a/lib/altsvc.c +++ b/lib/altsvc.c @@ -471,6 +471,8 @@ static void altsvc_flush(struct altsvcinfo *asi, } } +#define ALTSVC_MA 1 +#define ALTSVC_PERSIST 2 static void altsvc_parse_params(const char **pp, time_t *pmaxage, bool *ppersist) @@ -482,36 +484,44 @@ static void altsvc_parse_params(const char **pp, for(;;) { struct Curl_str name; struct Curl_str val; - const char *vp; curl_off_t num; - bool quoted; + int keyword = 0; /* allow some extra whitespaces around name and value */ - if(curlx_str_until(pp, &name, 20, '=') || - curlx_str_single(pp, '=') || - curlx_str_cspn(pp, &val, ",;")) + if(curlx_str_until(pp, &name, MAX_ALTSVC_LINE, '=') || + curlx_str_single(pp, '=')) break; /* skip further parameter parsing */ curlx_str_trimblanks(&name); + curlx_str_passblanks(pp); + if(**pp == '\"') { + if(curlx_str_quotedword(pp, &val, MAX_ALTSVC_LINE)) + break; + } + else { + if(curlx_str_cspn(pp, &val, ",;\r\n")) + break; + } curlx_str_trimblanks(&val); - /* the value might be quoted */ - vp = curlx_str(&val); - quoted = (*vp == '\"'); - if(quoted) - vp++; - /* we process 2 number value parameters: 'ma' and 'persist' */ - if(curlx_str_number(&vp, &num, TIME_T_MAX)) - break; /* not a number, skip further parameter parsing */ if(curlx_str_casecompare(&name, "ma")) - *pmaxage = (time_t)num; - else if(curlx_str_casecompare(&name, "persist") && (num == 1)) - *ppersist = TRUE; + keyword = ALTSVC_MA; + else if(curlx_str_casecompare(&name, "persist")) + keyword = ALTSVC_PERSIST; + + if(keyword) { + const char *vp = curlx_str(&val); + const char *vend = vp + curlx_strlen(&val); + if(curlx_str_number(&vp, &num, TIME_T_MAX)) + break; /* not a number, skip further parameter parsing */ + if(vp != vend) + break; /* not entirely a number, skip further parameter parsing */ + if(keyword == ALTSVC_MA) + *pmaxage = (time_t)num; + else if(num == 1) + *ppersist = TRUE; + } - *pp = vp; /* point to the byte ending the value */ - curlx_str_passblanks(pp); - if(quoted && curlx_str_single(pp, '\"')) - break; /* was quoted but not ended in quote, skip */ curlx_str_passblanks(pp); if(curlx_str_single(pp, ';')) break; /* no further parameters */ diff --git a/tests/unit/unit1654.c b/tests/unit/unit1654.c index 3f851d1bc7..706f9a0169 100644 --- a/tests/unit/unit1654.c +++ b/tests/unit/unit1654.c @@ -34,8 +34,10 @@ static CURLcode test_unit1654(const char *arg) CURL *curl = NULL; CURLcode result; struct altsvcinfo *asi = Curl_altsvc_init(); + struct altsvc *dstentry = NULL; struct Curl_peer *origin = NULL; const struct Curl_scheme *scheme = &Curl_scheme_https; + bool same_destination = FALSE; abort_if(!asi, "Curl_altsvc_i"); result = Curl_altsvc_load(asi, arg); @@ -160,6 +162,28 @@ static CURLcode test_unit1654(const char *arg) origin, ALPN_h2); fail_if(result, "Curl_altsvc_parse(12) failed!"); + /* Unknown extension parameters must not hide later known parameters. */ + if(Curl_peer_create(curl, scheme, "params.example", 443, &origin)) + goto fail; + result = Curl_altsvc_parse(curl, asi, + "h2=\":443\"; long-extension-parameter=token; " + "ext=token; " + "quoted=\"a;\\\"b\"; ma=120; persist=1\r\n", + origin, ALPN_h1); + fail_if(result, "Curl_altsvc_parse(13) failed!"); + if(!Curl_altsvc_lookup(asi, origin, ALPN_h1, &dstentry, + CURLALTSVC_H2, &same_destination)) { + fail_unless(FALSE, "alt-svc entry not found"); + goto fail; + } + fail_unless(dstentry->expires == 1548369261 + 120, + "unknown parameters hid ma"); + fail_unless(dstentry->persist, "unknown parameters hid persist"); + fail_unless(same_destination, "wrong alt-svc destination"); + + result = Curl_altsvc_parse(curl, asi, "clear\r\n", origin, ALPN_h1); + fail_if(result, "Curl_altsvc_parse(14) failed!"); + Curl_altsvc_save(curl, asi, outname); fail: