static ssize_t h3_parse_uni_stream_no_h3(struct qcs *qcs, struct buffer *b, int fin)
{
struct h3s *h3s = qcs->ctx;
+ int ret = -1;
/* Function reserved to non-HTTP/3 unidirectional streams. */
BUG_ON(!quic_stream_is_uni(qcs->id) || !(h3s->flags & H3_SF_UNI_NO_H3));
switch (h3s->type) {
case H3S_T_QPACK_DEC:
- if (qpack_decode_dec(b, fin, qcs))
- return -1;
+ ret = qpack_decode_dec(b, fin, qcs);
break;
case H3S_T_QPACK_ENC:
- if (qpack_decode_enc(b, fin, qcs))
- return -1;
+ ret = qpack_decode_enc(b, fin, qcs);
break;
case H3S_T_UNKNOWN:
default:
ABORT_NOW();
}
- /* TODO adjust return code */
- return 0;
+ return ret;
}
/* Decode a H3 frame header from <rxbuf> buffer. The frame type is stored in
return 0;
}
+/* Return the length in bytes of the QPACK prefix integer at the head of <buf>,
+ * whose first byte carries <prefix_bits> significant bits. Returns 0 if the
+ * integer is not complete yet, or a negative value if it is too long to be a
+ * valid one.
+ */
+static int qpack_prefix_int_len(const struct buffer *buf, int prefix_bits)
+{
+ /* One prefix byte plus the nine continuations of a 62-bit value. */
+ unsigned char tmp[10];
+ const unsigned char *raw = tmp;
+ uint64_t len = MIN(sizeof tmp, b_data(buf));
+
+ b_getblk(buf, (char *)tmp, len, 0);
+ qpack_get_varint(&raw, &len, prefix_bits);
+
+ /* A truncated and a too large integer are reported the same way. */
+ if (len == (uint64_t)-1)
+ return b_data(buf) < sizeof tmp ? 0 : -1;
+
+ return raw - tmp;
+}
+
/* Decode an encoder stream.
- *
- * Returns 0 on success else non-zero.
+ * Returns the number of consumed bytes or a negative error code.
*/
int qpack_decode_enc(struct buffer *buf, int fin, void *ctx)
{
}
inst = (unsigned char)*b_head(buf) & QPACK_ENC_INST_BITMASK;
- if (inst == QPACK_ENC_INST_DUP) {
- /* Duplicate */
- }
- else if (inst & QPACK_ENC_INST_IWNR_BIT) {
- /* Insert With Name Reference */
- }
- else if (inst & QPACK_ENC_INST_IWLN_BIT) {
- /* Insert with literal name */
- }
- else if (inst & QPACK_ENC_INST_SDTC_BIT) {
+ if (inst & QPACK_ENC_INST_SDTC_BIT) {
/* Set dynamic table capacity */
int capacity = *b_head(buf) & 0x1f;
return -1;
}
+ return 1;
}
- return 0;
+ /* haproxy advertises a null dynamic table capacity, so the peer must not
+ * insert anything.
+ */
+ qcc_set_error(qcs->qcc, QPACK_ERR_ENCODER_STREAM_ERROR, 1,
+ muxc_tevt_type_proto_err);
+ return -1;
}
/* Decode an decoder stream.
- *
- * Returns 0 on success else non-zero.
+ * Returns the number of consumed bytes or a negative error code.
*/
int qpack_decode_dec(struct buffer *buf, int fin, void *ctx)
{
struct qcs *qcs = ctx;
size_t len;
unsigned char inst;
+ int rdlen;
/* RFC 9204 4.2. Encoder and Decoder Streams
*
return 0;
}
- inst = (unsigned char)*b_head(buf) & QPACK_DEC_INST_BITMASK;
- if (inst == QPACK_DEC_INST_ICINC) {
+ inst = (unsigned char)*b_head(buf);
+ if (!(inst & (QPACK_DEC_INST_SACK | QPACK_DEC_INST_SCCL))) {
/* Insert count increment */
/* RFC 9204 4.4.3. Insert Count Increment
muxc_tevt_type_proto_err);
return -1;
}
- else if (inst & QPACK_DEC_INST_SACK) {
- /* Section Acknowledgment */
- }
- else if (inst & QPACK_DEC_INST_SCCL) {
- /* Stream cancellation */
+
+ /* Section Acknowledgment and Stream Cancellation both carry a stream ID
+ * which is of no use here, as haproxy encodes every field section with a
+ * null Required Insert Count. Skip them.
+ */
+ if (inst & QPACK_DEC_INST_SACK)
+ rdlen = qpack_prefix_int_len(buf, 7);
+ else
+ rdlen = qpack_prefix_int_len(buf, 6);
+
+ if (rdlen < 0) {
+ qcc_set_error(qcs->qcc, QPACK_ERR_DECODER_STREAM_ERROR, 1,
+ muxc_tevt_type_proto_err);
+ return -1;
}
- return 0;
+ return rdlen;
}
/* Decode a field section prefix made of <enc_ric> and <db> two varints.