]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: h3: complete response status transcoding
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Mon, 16 Jun 2025 14:32:36 +0000 (16:32 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Mon, 16 Jun 2025 16:11:09 +0000 (18:11 +0200)
On the backend side, HTTP/3 request response from server is transcoded
into a HTX message. Previously, a fixed value was used for the status
code.

Improve this by extracting the value specified by the server and set it
into the HTX status line. This requires to detect :status pseudo-header
from the HTTP/3 response.

src/h3.c

index d1c798cd0c377cc95cd6405e33d53c807f54a0f6..f2799066b75f670f06aad20cf0f9b8fa1cc09953 100644 (file)
--- a/src/h3.c
+++ b/src/h3.c
@@ -1097,11 +1097,15 @@ static ssize_t h3_resp_headers_to_htx(struct qcs *qcs, const struct buffer *buf,
                                       uint64_t len, char fin)
 {
        struct h3s *h3s = qcs->ctx;
+       struct h3c *h3c = h3s->h3c;
        struct buffer *appbuf = NULL;
        struct htx *htx = NULL;
        struct htx_sl *sl;
+       struct http_hdr list[global.tune.max_http_hdr * 2];
        unsigned int flags = HTX_SL_F_NONE;
-       struct ist status = ist("200");
+       struct ist status = IST_NULL;
+       unsigned char h, t, u;
+       int hdr_idx;
 
        /* RFC 9114 4.1.2. Malformed Requests and Responses
         *
@@ -1130,6 +1134,58 @@ static ssize_t h3_resp_headers_to_htx(struct qcs *qcs, const struct buffer *buf,
        BUG_ON(!b_size(appbuf)); /* TODO */
        htx = htx_from_buf(appbuf);
 
+       /* first treat pseudo-header to build the start line */
+       hdr_idx = 0;
+       while (1) {
+               /* RFC 9114 4.3. HTTP Control Data
+                *
+                * Endpoints MUST treat a request or response that contains
+                * undefined or invalid pseudo-header fields as malformed.
+                *
+                * All pseudo-header fields MUST appear in the header section before
+                * regular header fields. Any request or response that contains a
+                * pseudo-header field that appears in a header section after a regular
+                * header field MUST be treated as malformed.
+                */
+
+               /* Stop at first non pseudo-header. */
+               if (!istmatch(list[hdr_idx].n, ist(":")))
+                       break;
+
+               /* RFC 9114 10.3 Intermediary-Encapsulation Attacks
+                *
+                * While most values that can be encoded will not alter field
+                * parsing, carriage return (ASCII 0x0d), line feed (ASCII 0x0a),
+                * and the null character (ASCII 0x00) might be exploited by an
+                * attacker if they are translated verbatim. Any request or
+                * response that contains a character not permitted in a field
+                * value MUST be treated as malformed
+                */
+
+               if (_h3_handle_phdr(qcs, &list[hdr_idx])) {
+                       h3s->err = H3_ERR_MESSAGE_ERROR;
+                       qcc_report_glitch(h3c->qcc, 1);
+                       len = -1;
+                       goto out;
+               }
+
+               /* pseudo-header. Malformed name with uppercase character or
+                * invalid token will be rejected in the else clause.
+                */
+               if (isteq(list[hdr_idx].n, ist(":status"))) {
+                       status = list[hdr_idx].v;
+               }
+               else {
+                       TRACE_ERROR("unknown pseudo-header", H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, qcs);
+                       h3s->err = H3_ERR_MESSAGE_ERROR;
+                       qcc_report_glitch(h3c->qcc, 1);
+                       len = -1;
+                       goto out;
+               }
+
+               ++hdr_idx;
+       }
+
        flags |= HTX_SL_F_VER_11;
        flags |= HTX_SL_F_XFER_LEN;
 
@@ -1144,7 +1200,16 @@ static ssize_t h3_resp_headers_to_htx(struct qcs *qcs, const struct buffer *buf,
        if (fin)
                sl->flags |= HTX_SL_F_BODYLESS;
 
-       sl->info.res.status = 200;
+       h = status.ptr[0] - '0';
+       t = status.ptr[1] - '0';
+       u = status.ptr[2] - '0';
+       if (h > 9 || t > 9 || u > 9) {
+               TRACE_ERROR("invalid status code", H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, qcs);
+               h3s->err = H3_ERR_MESSAGE_ERROR;
+               len = -1;
+               goto out;
+       }
+       sl->info.res.status = h * 100 + t * 10 + u;
 
        if (!htx_add_endof(htx, HTX_BLK_EOH)) {
                len = -1;