Network System 0.1.1
High-performance modular networking library for scalable client-server applications
Loading...
Searching...
No Matches
transport_params.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2024, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
7
8#include <algorithm>
9#include <set>
10
12{
13
14namespace
15{
16
17void append_varint(std::vector<uint8_t>& buffer, uint64_t value)
18{
19 auto encoded = varint::encode(value);
20 buffer.insert(buffer.end(), encoded.begin(), encoded.end());
21}
22
23void append_bytes(std::vector<uint8_t>& buffer, std::span<const uint8_t> data)
24{
25 buffer.insert(buffer.end(), data.begin(), data.end());
26}
27
28void append_parameter(std::vector<uint8_t>& buffer, uint64_t param_id,
29 std::span<const uint8_t> value)
30{
31 append_varint(buffer, param_id);
32 append_varint(buffer, value.size());
33 append_bytes(buffer, value);
34}
35
36void append_varint_parameter(std::vector<uint8_t>& buffer, uint64_t param_id,
37 uint64_t value)
38{
39 auto encoded_value = varint::encode(value);
40 append_parameter(buffer, param_id,
41 std::span<const uint8_t>(encoded_value.data(), encoded_value.size()));
42}
43
44void append_empty_parameter(std::vector<uint8_t>& buffer, uint64_t param_id)
45{
46 append_varint(buffer, param_id);
47 append_varint(buffer, 0);
48}
49
50auto read_varint_from_span(std::span<const uint8_t>& data)
51 -> Result<uint64_t>
52{
53 auto result = varint::decode(data);
54 if (result.is_err())
55 {
57 "Failed to decode varint",
58 "transport_params");
59 }
60 auto [value, consumed] = result.value();
61 data = data.subspan(consumed);
62 return ok(std::move(value));
63}
64
65} // namespace
66
67auto transport_parameters::encode() const -> std::vector<uint8_t>
68{
69 std::vector<uint8_t> buffer;
70 buffer.reserve(256);
71
72 // Original Destination Connection ID (server only)
74 {
77 }
78
79 // Initial Source Connection ID
81 {
84 }
85
86 // Retry Source Connection ID (server only)
88 {
91 }
92
93 // Stateless Reset Token (server only)
95 {
96 append_parameter(buffer, transport_param_id::stateless_reset_token,
97 std::span<const uint8_t>(stateless_reset_token->data(),
98 stateless_reset_token->size()));
99 }
100
101 // Timing parameters
102 if (max_idle_timeout != 0)
103 {
104 append_varint_parameter(buffer, transport_param_id::max_idle_timeout,
106 }
107
108 if (ack_delay_exponent != 3)
109 {
110 append_varint_parameter(buffer, transport_param_id::ack_delay_exponent,
112 }
113
114 if (max_ack_delay != 25)
115 {
116 append_varint_parameter(buffer, transport_param_id::max_ack_delay,
118 }
119
120 // Flow control parameters
121 if (max_udp_payload_size != 65527)
122 {
123 append_varint_parameter(buffer, transport_param_id::max_udp_payload_size,
125 }
126
127 if (initial_max_data != 0)
128 {
129 append_varint_parameter(buffer, transport_param_id::initial_max_data,
131 }
132
134 {
135 append_varint_parameter(buffer,
138 }
139
141 {
142 append_varint_parameter(buffer,
145 }
146
148 {
149 append_varint_parameter(buffer, transport_param_id::initial_max_stream_data_uni,
151 }
152
153 // Stream limits
155 {
156 append_varint_parameter(buffer, transport_param_id::initial_max_streams_bidi,
158 }
159
161 {
162 append_varint_parameter(buffer, transport_param_id::initial_max_streams_uni,
164 }
165
166 // Connection options
168 {
169 append_empty_parameter(buffer, transport_param_id::disable_active_migration);
170 }
171
173 {
174 append_varint_parameter(buffer, transport_param_id::active_connection_id_limit,
176 }
177
178 // Preferred address (server only, complex encoding)
180 {
181 std::vector<uint8_t> addr_data;
182 addr_data.reserve(64);
183
184 // IPv4 address (4 bytes) + port (2 bytes)
185 append_bytes(addr_data,
186 std::span<const uint8_t>(preferred_address->ipv4_address.data(), 4));
187 addr_data.push_back(static_cast<uint8_t>(preferred_address->ipv4_port >> 8));
188 addr_data.push_back(static_cast<uint8_t>(preferred_address->ipv4_port & 0xFF));
189
190 // IPv6 address (16 bytes) + port (2 bytes)
191 append_bytes(addr_data,
192 std::span<const uint8_t>(preferred_address->ipv6_address.data(), 16));
193 addr_data.push_back(static_cast<uint8_t>(preferred_address->ipv6_port >> 8));
194 addr_data.push_back(static_cast<uint8_t>(preferred_address->ipv6_port & 0xFF));
195
196 // Connection ID length + Connection ID
197 addr_data.push_back(static_cast<uint8_t>(preferred_address->connection_id.length()));
198 append_bytes(addr_data, preferred_address->connection_id.data());
199
200 // Stateless reset token (16 bytes)
201 append_bytes(addr_data,
202 std::span<const uint8_t>(preferred_address->stateless_reset_token.data(),
203 16));
204
205 append_parameter(buffer, transport_param_id::preferred_address,
206 std::span<const uint8_t>(addr_data.data(), addr_data.size()));
207 }
208
209 return buffer;
210}
211
212auto transport_parameters::decode(std::span<const uint8_t> data)
214{
216 std::set<uint64_t> seen_params;
217
218 while (!data.empty())
219 {
220 // Read parameter ID
221 auto id_result = read_varint_from_span(data);
222 if (id_result.is_err())
223 {
225 "Failed to decode parameter ID",
226 "transport_params");
227 }
228 uint64_t param_id = id_result.value();
229
230 // Check for duplicates
231 if (seen_params.contains(param_id))
232 {
234 "Duplicate transport parameter",
235 "transport_params");
236 }
237 seen_params.insert(param_id);
238
239 // Read parameter length
240 auto len_result = read_varint_from_span(data);
241 if (len_result.is_err())
242 {
244 "Failed to decode parameter length",
245 "transport_params");
246 }
247 uint64_t param_len = len_result.value();
248
249 if (param_len > data.size())
250 {
252 "Parameter length exceeds buffer",
253 "transport_params");
254 }
255
256 std::span<const uint8_t> param_data = data.subspan(0, param_len);
257 data = data.subspan(param_len);
258
259 // Parse parameter value
260 switch (param_id)
261 {
263 if (param_len > 20)
264 {
266 "Connection ID too long",
267 "transport_params");
268 }
270 connection_id(param_data);
271 break;
272
274 if (param_len > 20)
275 {
277 "Connection ID too long",
278 "transport_params");
279 }
281 connection_id(param_data);
282 break;
283
285 if (param_len > 20)
286 {
288 "Connection ID too long",
289 "transport_params");
290 }
292 connection_id(param_data);
293 break;
294
296 if (param_len != 16)
297 {
299 "Stateless reset token must be 16 bytes",
300 "transport_params");
301 }
302 {
303 std::array<uint8_t, 16> token{};
304 std::copy_n(param_data.data(), 16, token.data());
305 params.stateless_reset_token = token;
306 }
307 break;
308
310 {
311 auto result = varint::decode(param_data);
312 if (result.is_err())
313 {
315 "Failed to decode max_idle_timeout",
316 "transport_params");
317 }
318 params.max_idle_timeout = result.value().first;
319 }
320 break;
321
323 {
324 auto result = varint::decode(param_data);
325 if (result.is_err())
326 {
328 "Failed to decode ack_delay_exponent",
329 "transport_params");
330 }
331 if (result.value().first > 20)
332 {
334 "ack_delay_exponent exceeds maximum (20)",
335 "transport_params");
336 }
337 params.ack_delay_exponent = result.value().first;
338 }
339 break;
340
342 {
343 auto result = varint::decode(param_data);
344 if (result.is_err())
345 {
347 "Failed to decode max_ack_delay",
348 "transport_params");
349 }
350 if (result.value().first > 16383)
351 {
353 "max_ack_delay exceeds maximum (16383)",
354 "transport_params");
355 }
356 params.max_ack_delay = result.value().first;
357 }
358 break;
359
361 {
362 auto result = varint::decode(param_data);
363 if (result.is_err())
364 {
366 "Failed to decode max_udp_payload_size",
367 "transport_params");
368 }
369 if (result.value().first < 1200)
370 {
372 "max_udp_payload_size below minimum (1200)",
373 "transport_params");
374 }
375 params.max_udp_payload_size = result.value().first;
376 }
377 break;
378
380 {
381 auto result = varint::decode(param_data);
382 if (result.is_err())
383 {
385 "Failed to decode initial_max_data",
386 "transport_params");
387 }
388 params.initial_max_data = result.value().first;
389 }
390 break;
391
393 {
394 auto result = varint::decode(param_data);
395 if (result.is_err())
396 {
399 "Failed to decode initial_max_stream_data_bidi_local",
400 "transport_params");
401 }
402 params.initial_max_stream_data_bidi_local = result.value().first;
403 }
404 break;
405
407 {
408 auto result = varint::decode(param_data);
409 if (result.is_err())
410 {
413 "Failed to decode initial_max_stream_data_bidi_remote",
414 "transport_params");
415 }
416 params.initial_max_stream_data_bidi_remote = result.value().first;
417 }
418 break;
419
421 {
422 auto result = varint::decode(param_data);
423 if (result.is_err())
424 {
427 "Failed to decode initial_max_stream_data_uni",
428 "transport_params");
429 }
430 params.initial_max_stream_data_uni = result.value().first;
431 }
432 break;
433
435 {
436 auto result = varint::decode(param_data);
437 if (result.is_err())
438 {
441 "Failed to decode initial_max_streams_bidi",
442 "transport_params");
443 }
444 params.initial_max_streams_bidi = result.value().first;
445 }
446 break;
447
449 {
450 auto result = varint::decode(param_data);
451 if (result.is_err())
452 {
455 "Failed to decode initial_max_streams_uni",
456 "transport_params");
457 }
458 params.initial_max_streams_uni = result.value().first;
459 }
460 break;
461
463 if (param_len != 0)
464 {
467 "disable_active_migration must have zero-length value",
468 "transport_params");
469 }
470 params.disable_active_migration = true;
471 break;
472
474 {
475 auto result = varint::decode(param_data);
476 if (result.is_err())
477 {
480 "Failed to decode active_connection_id_limit",
481 "transport_params");
482 }
483 if (result.value().first < 2)
484 {
487 "active_connection_id_limit must be at least 2",
488 "transport_params");
489 }
490 params.active_connection_id_limit = result.value().first;
491 }
492 break;
493
495 {
496 // Minimum: 4 + 2 + 16 + 2 + 1 + 0 + 16 = 41 bytes
497 if (param_len < 41)
498 {
500 "Preferred address too short",
501 "transport_params");
502 }
504 size_t offset = 0;
505
506 // IPv4 address
507 std::copy_n(param_data.data() + offset, 4, addr.ipv4_address.data());
508 offset += 4;
509
510 // IPv4 port
511 addr.ipv4_port = static_cast<uint16_t>(
512 (param_data[offset] << 8) | param_data[offset + 1]);
513 offset += 2;
514
515 // IPv6 address
516 std::copy_n(param_data.data() + offset, 16, addr.ipv6_address.data());
517 offset += 16;
518
519 // IPv6 port
520 addr.ipv6_port = static_cast<uint16_t>(
521 (param_data[offset] << 8) | param_data[offset + 1]);
522 offset += 2;
523
524 // Connection ID
525 uint8_t cid_len = param_data[offset++];
526 if (cid_len > 20 || offset + cid_len + 16 > param_len)
527 {
529 "Invalid preferred address connection ID",
530 "transport_params");
531 }
533 std::span<const uint8_t>(param_data.data() + offset, cid_len));
534 offset += cid_len;
535
536 // Stateless reset token
537 std::copy_n(param_data.data() + offset, 16, addr.stateless_reset_token.data());
538
539 params.preferred_address = addr;
540 }
541 break;
542
543 default:
544 // Unknown parameters are ignored (RFC 9000 Section 18.1)
545 break;
546 }
547 }
548
549 return ok(std::move(params));
550}
551
552auto transport_parameters::validate(bool is_server) const -> VoidResult
553{
554 // Validate ack_delay_exponent
555 if (ack_delay_exponent > 20)
556 {
558 "ack_delay_exponent must not exceed 20",
559 "transport_params");
560 }
561
562 // Validate max_ack_delay
563 if (max_ack_delay > 16383)
564 {
566 "max_ack_delay must not exceed 16383",
567 "transport_params");
568 }
569
570 // Validate max_udp_payload_size
571 if (max_udp_payload_size < 1200)
572 {
574 "max_udp_payload_size must be at least 1200",
575 "transport_params");
576 }
577
578 // Validate active_connection_id_limit
579 if (active_connection_id_limit < 2)
580 {
582 "active_connection_id_limit must be at least 2",
583 "transport_params");
584 }
585
586 // Server-only parameters should not be set by client
587 if (!is_server)
588 {
589 if (original_destination_connection_id)
590 {
592 "Client must not send original_destination_connection_id",
593 "transport_params");
594 }
595 if (retry_source_connection_id)
596 {
598 "Client must not send retry_source_connection_id",
599 "transport_params");
600 }
601 if (stateless_reset_token)
602 {
604 "Client must not send stateless_reset_token",
605 "transport_params");
606 }
607 if (preferred_address)
608 {
610 "Client must not send preferred_address",
611 "transport_params");
612 }
613 }
614
615 return ok();
616}
617
619{
620 // Most defaults are already set in the struct definition
621 // This method can be used to apply context-specific defaults
622 if (max_udp_payload_size == 0)
623 {
624 max_udp_payload_size = 65527;
625 }
626 if (ack_delay_exponent == 0)
627 {
629 }
630 if (max_ack_delay == 0)
631 {
632 max_ack_delay = 25;
633 }
635 {
637 }
638}
639
641{
643
644 // Set reasonable defaults for a client
645 params.max_idle_timeout = 30000; // 30 seconds
646 params.max_udp_payload_size = 65527;
647 params.initial_max_data = 1048576; // 1 MB
648 params.initial_max_stream_data_bidi_local = 262144; // 256 KB
650 params.initial_max_stream_data_uni = 262144;
651 params.initial_max_streams_bidi = 100;
652 params.initial_max_streams_uni = 100;
653 params.ack_delay_exponent = 3;
654 params.max_ack_delay = 25;
656
657 return params;
658}
659
661{
663
664 // Set reasonable defaults for a server
665 params.max_idle_timeout = 30000; // 30 seconds
666 params.max_udp_payload_size = 65527;
667 params.initial_max_data = 1048576; // 1 MB
668 params.initial_max_stream_data_bidi_local = 262144; // 256 KB
670 params.initial_max_stream_data_uni = 262144;
671 params.initial_max_streams_bidi = 100;
672 params.initial_max_streams_uni = 100;
673 params.ack_delay_exponent = 3;
674 params.max_ack_delay = 25;
676
677 return params;
678}
679
680} // namespace kcenon::network::protocols::quic
QUIC Connection ID (RFC 9000 Section 5.1)
static auto encode(uint64_t value) -> std::vector< uint8_t >
Encode a value to variable-length format.
Definition varint.cpp:10
static auto decode(std::span< const uint8_t > data) -> Result< std::pair< uint64_t, size_t > >
Decode variable-length integer from buffer.
Definition varint.cpp:146
@ error
Black hole detected, reset to base.
auto make_default_client_params() -> transport_parameters
Create default client transport parameters.
auto make_default_server_params() -> transport_parameters
Create default server transport parameters.
VoidResult error_void(int code, const std::string &message, const std::string &source="network_system", const std::string &details="")
VoidResult ok()
QUIC preferred address transport parameter (RFC 9000 Section 18.2)
QUIC transport parameters (RFC 9000 Section 18)
std::optional< connection_id > retry_source_connection_id
Retry Source Connection ID (server only, after Retry)
uint64_t max_idle_timeout
Maximum idle timeout in milliseconds (0 = disabled)
uint64_t initial_max_data
Initial maximum data for connection (default: 0)
uint64_t ack_delay_exponent
ACK delay exponent (default: 3, meaning 8 microseconds)
uint64_t initial_max_streams_uni
Initial maximum unidirectional streams.
std::optional< std::array< uint8_t, 16 > > stateless_reset_token
Stateless reset token (server only, 16 bytes)
void apply_defaults()
Apply default values for unset parameters.
auto encode() const -> std::vector< uint8_t >
Encode transport parameters to binary format.
bool disable_active_migration
Whether active connection migration is disabled.
auto validate(bool is_server) const -> VoidResult
Validate transport parameters.
uint64_t initial_max_streams_bidi
Initial maximum bidirectional streams.
uint64_t max_udp_payload_size
Maximum UDP payload size (default: 65527)
uint64_t initial_max_stream_data_bidi_remote
Initial maximum data for remotely-initiated bidirectional streams.
std::optional< preferred_address_info > preferred_address
Preferred address for migration (server only)
static auto decode(std::span< const uint8_t > data) -> Result< transport_parameters >
Decode transport parameters from binary format.
std::optional< connection_id > initial_source_connection_id
Initial Source Connection ID.
uint64_t max_ack_delay
Maximum ACK delay in milliseconds (default: 25)
uint64_t active_connection_id_limit
Maximum number of connection IDs from the peer.
std::optional< connection_id > original_destination_connection_id
Original Destination Connection ID (server only)
uint64_t initial_max_stream_data_bidi_local
Initial maximum data for locally-initiated bidirectional streams.
uint64_t initial_max_stream_data_uni
Initial maximum data for unidirectional streams.