diff --git a/src/h3.c b/src/h3.c index 132abc1ea..2e5951f75 100644 --- a/src/h3.c +++ b/src/h3.c @@ -301,61 +301,126 @@ static inline size_t h3_decode_frm_header(uint64_t *ftype, uint64_t *flen, /* Check if H3 frame of type is valid when received on stream . * - * Returns a boolean. If false, a connection error H3_FRAME_UNEXPECTED should - * be reported. + * Returns 0 if frame valid, otherwise HTTP/3 error code. */ -static int h3_is_frame_valid(struct h3c *h3c, struct qcs *qcs, uint64_t ftype) +static int h3_check_frame_valid(struct h3c *h3c, struct qcs *qcs, uint64_t ftype) { struct h3s *h3s = qcs->ctx; + int ret = 0; /* Stream type must be known to ensure frame is valid for this stream. */ BUG_ON(h3s->type == H3S_T_UNKNOWN); switch (ftype) { case H3_FT_DATA: - return h3s->type != H3S_T_CTRL && (h3s->st_req == H3S_ST_REQ_HEADERS || - h3s->st_req == H3S_ST_REQ_DATA); + /* cf H3_FT_HEADERS case. */ + if (h3s->type == H3S_T_CTRL || + (h3s->st_req != H3S_ST_REQ_HEADERS && h3s->st_req != H3S_ST_REQ_DATA)) { + ret = H3_FRAME_UNEXPECTED; + } + + break; case H3_FT_HEADERS: - return h3s->type != H3S_T_CTRL && h3s->st_req != H3S_ST_REQ_TRAILERS; + /* RFC 9114 4.1. HTTP Message Framing + * + * + * An HTTP message (request or response) consists of: + * 1. the header section, including message control data, sent as a + * single HEADERS frame, + * 2. optionally, the content, if present, sent as a series of DATA + * frames, and + * 3. optionally, the trailer section, if present, sent as a single + * HEADERS frame. + * + * [...] + * + * Receipt of an invalid sequence of frames MUST be treated as a + * connection error of type H3_FRAME_UNEXPECTED. In particular, a DATA + * frame before any HEADERS frame, or a HEADERS or DATA frame after the + * trailing HEADERS frame, is considered invalid. Other frame types, + * especially unknown frame types, might be permitted subject to their + * own rules; see Section 9. + */ + if (h3s->type == H3S_T_CTRL || h3s->st_req == H3S_ST_REQ_TRAILERS) + ret = H3_FRAME_UNEXPECTED; + break; case H3_FT_CANCEL_PUSH: case H3_FT_GOAWAY: case H3_FT_MAX_PUSH_ID: - /* Only allowed for control stream. First frame of control - * stream MUST be SETTINGS. + /* RFC 9114 7.2.3. CANCEL_PUSH + * + * A CANCEL_PUSH frame is sent on the control stream. Receiving a + * CANCEL_PUSH frame on a stream other than the control stream MUST be + * treated as a connection error of type H3_FRAME_UNEXPECTED. */ - return h3s->type == H3S_T_CTRL && - (h3c->flags & H3_CF_SETTINGS_RECV); + + /* RFC 9114 7.2.6. GOAWAY + * + * A client MUST treat a GOAWAY frame on a stream other than the + * control stream as a connection error of type H3_FRAME_UNEXPECTED. + */ + + /* RFC 9114 7.2.7. MAX_PUSH_ID + * + * The MAX_PUSH_ID frame is always sent on the control stream. Receipt + * of a MAX_PUSH_ID frame on any other stream MUST be treated as a + * connection error of type H3_FRAME_UNEXPECTED. + */ + + if (h3s->type != H3S_T_CTRL) + ret = H3_FRAME_UNEXPECTED; + else if (!(h3c->flags & H3_CF_SETTINGS_RECV)) + ret = H3_MISSING_SETTINGS; + break; case H3_FT_SETTINGS: - /* draft-ietf-quic-http34 7.2.4. SETTINGS + /* RFC 9114 7.2.4. SETTINGS + * + * A SETTINGS frame MUST be sent as the first frame of + * each control stream (see Section 6.2.1) by each peer, and it MUST NOT + * be sent subsequently. If an endpoint receives a second SETTINGS frame + * on the control stream, the endpoint MUST respond with a connection + * error of type H3_FRAME_UNEXPECTED. * - * If an endpoint receives a second SETTINGS frame on the control + * SETTINGS frames MUST NOT be sent on any stream other than the control + * stream. If an endpoint receives a SETTINGS frame on a different * stream, the endpoint MUST respond with a connection error of type * H3_FRAME_UNEXPECTED. */ - return h3s->type == H3S_T_CTRL && - !(h3c->flags & H3_CF_SETTINGS_RECV); + if (h3s->type != H3S_T_CTRL || h3c->flags & H3_CF_SETTINGS_RECV) + ret = H3_FRAME_UNEXPECTED; + break; case H3_FT_PUSH_PROMISE: /* RFC 9114 7.2.5. PUSH_PROMISE + * * A client MUST NOT send a PUSH_PROMISE frame. A server MUST treat the * receipt of a PUSH_PROMISE frame as a connection error of type * H3_FRAME_UNEXPECTED. */ /* TODO server-side only. */ - return 0; + ret = H3_FRAME_UNEXPECTED; + break; default: - /* draft-ietf-quic-http34 9. Extensions to HTTP/3 + /* RFC 9114 9. Extensions to HTTP/3 * - * Implementations MUST discard frames [...] that have unknown - * or unsupported types. + * Implementations MUST ignore unknown or unsupported values in all + * extensible protocol elements. [...] + * However, where a known frame type is required to be in a + * specific location, such as the SETTINGS frame as the first frame of + * the control stream (see Section 6.2.1), an unknown frame type does + * not satisfy that requirement and SHOULD be treated as an error. */ - return h3s->type != H3S_T_CTRL || (h3c->flags & H3_CF_SETTINGS_RECV); + if (h3s->type == H3S_T_CTRL && !(h3c->flags & H3_CF_SETTINGS_RECV)) + ret = H3_MISSING_SETTINGS; + break; } + + return ret; } /* Check from stream that length of all DATA frames does not exceed with @@ -1235,9 +1300,9 @@ static ssize_t h3_decode_qcs(struct qcs *qcs, struct buffer *b, int fin) break; } - if (!h3_is_frame_valid(h3c, qcs, ftype)) { + if ((ret = h3_check_frame_valid(h3c, qcs, ftype))) { TRACE_ERROR("received an invalid frame", H3_EV_RX_FRAME, qcs->qcc->conn, qcs); - qcc_set_error(qcs->qcc, H3_FRAME_UNEXPECTED, 1); + qcc_set_error(qcs->qcc, ret, 1); goto err; }