DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
quic_tp.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file quic_tp.cpp
6 * @brief QUIC transport parameters codec (see quic_tp.h).
7 */
8
10
11#if DETWS_ENABLE_HTTP3
12
14#include <string.h>
15
16void quic_tp_defaults(QuicTransportParams *tp)
17{
18 memset(tp, 0, sizeof(*tp));
19 tp->max_udp_payload_size = 65527;
20 tp->ack_delay_exponent = 3;
21 tp->max_ack_delay = 25;
22 tp->active_connection_id_limit = 2;
23}
24
25namespace
26{
27// Append one parameter: ID (varint) || Length (varint) || raw value bytes.
28bool put_param(uint8_t *out, size_t cap, size_t *p, uint64_t id, const uint8_t *val, size_t val_len)
29{
30 size_t n = quic_varint_encode(out + *p, cap - *p, id);
31 if (!n)
32 return false;
33 *p += n;
34 n = quic_varint_encode(out + *p, cap - *p, val_len);
35 if (!n)
36 return false;
37 *p += n;
38 if (val_len)
39 {
40 // Overflow-safe check (*p <= cap holds after the varint encodes): avoids the *p + val_len
41 // size_t wraparound an analyzer flags as a possible destination overflow (cpp:S3519).
42 if (val_len > cap - *p)
43 return false;
44 memcpy(out + *p, val, val_len);
45 *p += val_len;
46 }
47 return true;
48}
49
50// Append a varint-valued parameter (Value is itself a varint).
51bool put_varint_param(uint8_t *out, size_t cap, size_t *p, uint64_t id, uint64_t value)
52{
53 uint8_t v[8];
54 size_t vlen = quic_varint_encode(v, sizeof(v), value);
55 if (!vlen)
56 return false;
57 return put_param(out, cap, p, id, v, vlen);
58}
59} // namespace
60
61size_t quic_tp_encode(const QuicTransportParams *tp, uint8_t *out, size_t cap)
62{
63 size_t p = 0;
64 bool ok = true;
65 if (tp->has_original_dcid)
66 ok = ok && put_param(out, cap, &p, QuicTp::QUIC_TP_ORIGINAL_DCID, tp->original_dcid, tp->original_dcid_len);
67 if (tp->has_initial_scid)
68 ok = ok && put_param(out, cap, &p, QuicTp::QUIC_TP_INITIAL_SCID, tp->initial_scid, tp->initial_scid_len);
69 if (tp->has_retry_scid)
70 ok = ok && put_param(out, cap, &p, QuicTp::QUIC_TP_RETRY_SCID, tp->retry_scid, tp->retry_scid_len);
71
72 ok = ok && put_varint_param(out, cap, &p, QuicTp::QUIC_TP_INITIAL_MAX_DATA, tp->initial_max_data);
73 ok = ok && put_varint_param(out, cap, &p, QuicTp::QUIC_TP_INITIAL_MAX_SD_BIDI_LOCAL, tp->initial_max_sd_bidi_local);
74 ok = ok &&
75 put_varint_param(out, cap, &p, QuicTp::QUIC_TP_INITIAL_MAX_SD_BIDI_REMOTE, tp->initial_max_sd_bidi_remote);
76 ok = ok && put_varint_param(out, cap, &p, QuicTp::QUIC_TP_INITIAL_MAX_SD_UNI, tp->initial_max_sd_uni);
77 ok = ok && put_varint_param(out, cap, &p, QuicTp::QUIC_TP_INITIAL_MAX_STREAMS_BIDI, tp->initial_max_streams_bidi);
78 ok = ok && put_varint_param(out, cap, &p, QuicTp::QUIC_TP_INITIAL_MAX_STREAMS_UNI, tp->initial_max_streams_uni);
79 ok = ok && put_varint_param(out, cap, &p, QuicTp::QUIC_TP_MAX_IDLE_TIMEOUT, tp->max_idle_timeout);
80 ok = ok && put_varint_param(out, cap, &p, QuicTp::QUIC_TP_MAX_UDP_PAYLOAD_SIZE, tp->max_udp_payload_size);
81 ok = ok && put_varint_param(out, cap, &p, QuicTp::QUIC_TP_ACTIVE_CID_LIMIT, tp->active_connection_id_limit);
82 if (tp->disable_active_migration)
83 ok = ok && put_param(out, cap, &p, QuicTp::QUIC_TP_DISABLE_ACTIVE_MIGRATION, nullptr, 0);
84
85 return ok ? p : 0;
86}
87
88namespace
89{
90// Decode the varint that IS the whole value of a varint-valued parameter (must consume exactly len).
91bool value_varint(const uint8_t *val, size_t len, uint64_t *out)
92{
93 size_t consumed = 0;
94 if (!quic_varint_decode(val, len, out, &consumed))
95 return false;
96 return consumed == len;
97}
98
99// Copy a connection-ID value (<= QUIC_MAX_CID_LEN) into a fixed field.
100bool copy_cid(const uint8_t *val, size_t len, uint8_t *dst, uint8_t *dst_len, bool *has)
101{
102 if (len > QUIC_MAX_CID_LEN)
103 return false;
104 memcpy(dst, val, len);
105 *dst_len = (uint8_t)len;
106 *has = true;
107 return true;
108}
109
110// Apply a connection-ID transport parameter. *handled is set true if id names a CID param (whether or
111// not the copy succeeded); false leaves it for another category. Returns false only on a bad value.
112bool quic_tp_apply_cid(uint64_t id, const uint8_t *val, size_t vlen, QuicTransportParams *tp, bool *handled)
113{
114 *handled = true;
115 switch (id)
116 {
117 case QuicTp::QUIC_TP_ORIGINAL_DCID:
118 return copy_cid(val, vlen, tp->original_dcid, &tp->original_dcid_len, &tp->has_original_dcid);
119 case QuicTp::QUIC_TP_INITIAL_SCID:
120 return copy_cid(val, vlen, tp->initial_scid, &tp->initial_scid_len, &tp->has_initial_scid);
121 case QuicTp::QUIC_TP_RETRY_SCID:
122 return copy_cid(val, vlen, tp->retry_scid, &tp->retry_scid_len, &tp->has_retry_scid);
123 default:
124 *handled = false;
125 return true;
126 }
127}
128
129// Apply a varint-valued transport parameter with its RFC 9000 range checks. *handled is set as above.
130bool quic_tp_apply_varint(uint64_t id, const uint8_t *val, size_t vlen, QuicTransportParams *tp, bool *handled)
131{
132 *handled = true;
133 switch (id)
134 {
135 case QuicTp::QUIC_TP_MAX_IDLE_TIMEOUT:
136 return value_varint(val, vlen, &tp->max_idle_timeout);
137 case QuicTp::QUIC_TP_MAX_UDP_PAYLOAD_SIZE:
138 return value_varint(val, vlen, &tp->max_udp_payload_size) && tp->max_udp_payload_size >= 1200;
139 case QuicTp::QUIC_TP_INITIAL_MAX_DATA:
140 return value_varint(val, vlen, &tp->initial_max_data);
141 case QuicTp::QUIC_TP_INITIAL_MAX_SD_BIDI_LOCAL:
142 return value_varint(val, vlen, &tp->initial_max_sd_bidi_local);
143 case QuicTp::QUIC_TP_INITIAL_MAX_SD_BIDI_REMOTE:
144 return value_varint(val, vlen, &tp->initial_max_sd_bidi_remote);
145 case QuicTp::QUIC_TP_INITIAL_MAX_SD_UNI:
146 return value_varint(val, vlen, &tp->initial_max_sd_uni);
147 case QuicTp::QUIC_TP_INITIAL_MAX_STREAMS_BIDI:
148 return value_varint(val, vlen, &tp->initial_max_streams_bidi);
149 case QuicTp::QUIC_TP_INITIAL_MAX_STREAMS_UNI:
150 return value_varint(val, vlen, &tp->initial_max_streams_uni);
151 case QuicTp::QUIC_TP_ACK_DELAY_EXPONENT:
152 return value_varint(val, vlen, &tp->ack_delay_exponent) && tp->ack_delay_exponent <= 20;
153 case QuicTp::QUIC_TP_MAX_ACK_DELAY:
154 return value_varint(val, vlen, &tp->max_ack_delay) && tp->max_ack_delay < (1u << 14);
155 case QuicTp::QUIC_TP_ACTIVE_CID_LIMIT:
156 return value_varint(val, vlen, &tp->active_connection_id_limit) && tp->active_connection_id_limit >= 2;
157 default:
158 *handled = false;
159 return true;
160 }
161}
162
163// Dispatch one parsed transport parameter to tp; false on a malformed / out-of-range value. Unknown
164// (GREASE) IDs are silently ignored, matching RFC 9000 §7.4.1.
165bool quic_tp_apply(uint64_t id, const uint8_t *val, size_t vlen, QuicTransportParams *tp)
166{
167 bool handled = false;
168 if (!quic_tp_apply_cid(id, val, vlen, tp, &handled))
169 return false;
170 if (handled)
171 return true;
172 if (!quic_tp_apply_varint(id, val, vlen, tp, &handled))
173 return false;
174 if (handled)
175 return true;
176 if (id == QuicTp::QUIC_TP_DISABLE_ACTIVE_MIGRATION)
177 {
178 if (vlen != 0)
179 return false;
180 tp->disable_active_migration = true;
181 }
182 return true; // unknown / GREASE: skip
183}
184} // namespace
185
186bool quic_tp_parse(const uint8_t *buf, size_t len, QuicTransportParams *tp)
187{
188 quic_tp_defaults(tp);
189 uint32_t seen = 0; // dup-guard bitmask over the known IDs (all < 32)
190
191 size_t off = 0;
192 while (off < len)
193 {
194 uint64_t id = 0;
195 uint64_t vlen = 0;
196 size_t c = 0;
197 if (!quic_varint_decode(buf + off, len - off, &id, &c))
198 return false;
199 off += c;
200 if (!quic_varint_decode(buf + off, len - off, &vlen, &c))
201 return false;
202 off += c;
203 if (off + vlen > len)
204 return false;
205 const uint8_t *val = buf + off;
206 off += vlen;
207
208 if (id < 32)
209 {
210 uint32_t bit = 1u << id;
211 if (seen & bit)
212 return false; // a known parameter must not appear twice
213 seen |= bit;
214 }
215
216 if (!quic_tp_apply(id, val, vlen, tp))
217 return false;
218 }
219 return true;
220}
221
222#endif // DETWS_ENABLE_HTTP3
QUIC transport parameters (RFC 9000 sec 18) carried in the TLS quic_transport_parameters extension (R...
QUIC variable-length integer coding (RFC 9000 sec 16).