From: Olivier Houchard Date: Wed, 7 Oct 2026 09:18:12 +0000 (+0200) Subject: BUG/MAJOR: mux-h2: split absolute URIs like the HTTP analysers do on output X-Git-Tag: v3.5-dev10~122 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=6982230035aa12260c945fe0fb5aa3e7998b33a0;p=haproxy.git BUG/MAJOR: mux-h2: split absolute URIs like the HTTP analysers do on output When deriving :authority and :path from an absolute-form URI, the authority ended at the first '/' only instead of the first '/', '?' or '#' (RFC3986#3.2), and the userinfo was stripped up to the first '@' instead of the last one. The analysers and sample fetches rely on http_parse_authority() which does it correctly, so a request could be evaluated for one host/path and delivered to the H2 server with another one, bypassing host and path based rules. Now the authority is extracted like http_parse_authority() does, and a '/' is prepended to :path when needed. This exists since commit b8ce8905c ("MEDIUM: mux-h2: do not map Host to :authority on output") in 2.1, the userinfo part since 29fa2667a ("BUG/MINOR: mux-h2: strip the userinfo when deriving :authority for a server") in 3.5-dev5. Must be backported to all stable versions (the userinfo part only where 29fa2667a was backported). --- diff --git a/src/mux_h2.c b/src/mux_h2.c index 27fe98948..ab13520b6 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -7203,17 +7203,30 @@ static size_t h2s_snd_bhdrs(struct h2s *h2s, struct htx *htx) scheme = ist2(uri.ptr, len); uri = istadv(uri, len + 3); - /* find the auth part of the URI */ + /* find the auth part of the URI. As per RFC3986#3.2, + * it ends at the first '/', '?' or '#'. + */ auth = ist2(uri.ptr, 0); - while (auth.len < uri.len && auth.ptr[auth.len] != '/') + while (auth.len < uri.len && auth.ptr[auth.len] != '/' && + auth.ptr[auth.len] != '?' && auth.ptr[auth.len] != '#') auth.len++; uri = istadv(uri, auth.len); - /* RFC9113#8.3.1: :authority must not carry the deprecated userinfo */ - at = istfind(auth, '@'); - if (istlen(at)) - auth = istadv(at, 1); + for (at = ist2(istend(auth), 0); at.ptr > auth.ptr; at.ptr--) { + if (at.ptr[-1] == '@') { + auth = ist2(at.ptr, istend(auth) - at.ptr); + break; + } + } + + if (uri.len && *uri.ptr != '/') { + struct buffer *path = get_trash_chunk(); + + if (!chunk_memcat(path, "/", 1) || !chunk_memcat(path, uri.ptr, uri.len)) + goto fail; + uri = ist2(b_orig(path), b_data(path)); + } } }