1use std::{
10 convert::TryFrom,
11 net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6},
12};
13
14use bytes::{Buf, BufMut};
15use rand::{Rng as _, RngCore, seq::SliceRandom as _};
16use thiserror::Error;
17
18use crate::{
19 LOC_CID_COUNT, MAX_CID_SIZE, MAX_STREAM_COUNT, RESET_TOKEN_SIZE, ResetToken, Side,
20 TIMER_GRANULARITY, TransportError, VarInt,
21 cid_generator::ConnectionIdGenerator,
22 cid_queue::CidQueue,
23 coding::{BufExt, BufMutExt, UnexpectedEnd},
24 config::{EndpointConfig, ServerConfig, TransportConfig},
25 shared::ConnectionId,
26};
27
28macro_rules! apply_params {
34 ($macro:ident) => {
35 $macro! {
36 max_idle_timeout(MaxIdleTimeout) = 0,
39 max_udp_payload_size(MaxUdpPayloadSize) = 65527,
41
42 initial_max_data(InitialMaxData) = 0,
44 initial_max_stream_data_bidi_local(InitialMaxStreamDataBidiLocal) = 0,
46 initial_max_stream_data_bidi_remote(InitialMaxStreamDataBidiRemote) = 0,
48 initial_max_stream_data_uni(InitialMaxStreamDataUni) = 0,
50
51 initial_max_streams_bidi(InitialMaxStreamsBidi) = 0,
53 initial_max_streams_uni(InitialMaxStreamsUni) = 0,
55
56 ack_delay_exponent(AckDelayExponent) = 3,
58 max_ack_delay(MaxAckDelay) = 25,
61 active_connection_id_limit(ActiveConnectionIdLimit) = 2,
63 }
64 };
65}
66
67macro_rules! make_struct {
68 {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
69 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
71 pub struct TransportParameters {
72 $($(#[$doc])* pub(crate) $name : VarInt,)*
73
74 pub(crate) disable_active_migration: bool,
76 pub(crate) max_datagram_frame_size: Option<VarInt>,
78 pub(crate) initial_src_cid: Option<ConnectionId>,
81 pub(crate) grease_quic_bit: bool,
84
85 pub(crate) min_ack_delay: Option<VarInt>,
91
92 pub(crate) nat_traversal: Option<NatTraversalConfig>,
96
97 pub(crate) original_dst_cid: Option<ConnectionId>,
101 pub(crate) retry_src_cid: Option<ConnectionId>,
104 pub(crate) stateless_reset_token: Option<ResetToken>,
106 pub(crate) preferred_address: Option<PreferredAddress>,
108 pub(crate) grease_transport_parameter: Option<ReservedTransportParameter>,
112
113 pub(crate) write_order: Option<[u8; TransportParameterId::SUPPORTED.len()]>,
118 }
119
120 impl TransportParameters {
124 pub(crate) fn default() -> Self {
126 Self {
127 $($name: VarInt::from_u32($default),)*
128
129 disable_active_migration: false,
130 max_datagram_frame_size: None,
131 initial_src_cid: None,
132 grease_quic_bit: false,
133 min_ack_delay: None,
134 nat_traversal: None,
135
136 original_dst_cid: None,
137 retry_src_cid: None,
138 stateless_reset_token: None,
139 preferred_address: None,
140 grease_transport_parameter: None,
141 write_order: None,
142 }
143 }
144 }
145 }
146}
147
148apply_params!(make_struct);
149
150impl TransportParameters {
151 pub(crate) fn new(
152 config: &TransportConfig,
153 endpoint_config: &EndpointConfig,
154 cid_gen: &dyn ConnectionIdGenerator,
155 initial_src_cid: ConnectionId,
156 server_config: Option<&ServerConfig>,
157 rng: &mut impl RngCore,
158 ) -> Self {
159 Self {
160 initial_src_cid: Some(initial_src_cid),
161 initial_max_streams_bidi: config.max_concurrent_bidi_streams,
162 initial_max_streams_uni: config.max_concurrent_uni_streams,
163 initial_max_data: config.receive_window,
164 initial_max_stream_data_bidi_local: config.stream_receive_window,
165 initial_max_stream_data_bidi_remote: config.stream_receive_window,
166 initial_max_stream_data_uni: config.stream_receive_window,
167 max_udp_payload_size: endpoint_config.max_udp_payload_size,
168 max_idle_timeout: config.max_idle_timeout.unwrap_or(VarInt(0)),
169 disable_active_migration: server_config.is_some_and(|c| !c.migration),
170 active_connection_id_limit: if cid_gen.cid_len() == 0 {
171 2 } else {
173 CidQueue::LEN as u32
174 }
175 .into(),
176 max_datagram_frame_size: config
177 .datagram_receive_buffer_size
178 .map(|x| (x.min(u16::MAX.into()) as u16).into()),
179 grease_quic_bit: endpoint_config.grease_quic_bit,
180 min_ack_delay: Some(
181 VarInt::from_u64(u64::try_from(TIMER_GRANULARITY.as_micros()).unwrap()).unwrap(),
182 ),
183 grease_transport_parameter: Some(ReservedTransportParameter::random(rng)),
184 write_order: Some({
185 let mut order = std::array::from_fn(|i| i as u8);
186 order.shuffle(rng);
187 order
188 }),
189 ..Self::default()
190 }
191 }
192
193 pub(crate) fn validate_resumption_from(&self, cached: &Self) -> Result<(), TransportError> {
196 if cached.active_connection_id_limit > self.active_connection_id_limit
197 || cached.initial_max_data > self.initial_max_data
198 || cached.initial_max_stream_data_bidi_local > self.initial_max_stream_data_bidi_local
199 || cached.initial_max_stream_data_bidi_remote > self.initial_max_stream_data_bidi_remote
200 || cached.initial_max_stream_data_uni > self.initial_max_stream_data_uni
201 || cached.initial_max_streams_bidi > self.initial_max_streams_bidi
202 || cached.initial_max_streams_uni > self.initial_max_streams_uni
203 || cached.max_datagram_frame_size > self.max_datagram_frame_size
204 || cached.grease_quic_bit && !self.grease_quic_bit
205 {
206 return Err(TransportError::PROTOCOL_VIOLATION(
207 "0-RTT accepted with incompatible transport parameters",
208 ));
209 }
210 Ok(())
211 }
212
213 pub(crate) fn issue_cids_limit(&self) -> u64 {
218 self.active_connection_id_limit.0.min(LOC_CID_COUNT)
219 }
220}
221
222#[derive(Debug, Copy, Clone, Eq, PartialEq)]
227pub struct NatTraversalConfig {
228 pub(crate) role: NatTraversalRole,
230 pub(crate) max_candidates: VarInt,
232 pub(crate) coordination_timeout: VarInt,
234 pub(crate) max_concurrent_attempts: VarInt,
236}
237
238impl NatTraversalConfig {
239 fn wire_size(&self) -> u16 {
240 1 + self.max_candidates.size() as u16 +
242 self.coordination_timeout.size() as u16 +
243 self.max_concurrent_attempts.size() as u16 +
244 match self.role {
245 NatTraversalRole::Server { .. } => 1, _ => 0,
247 }
248 }
249
250 fn write<W: BufMut>(&self, w: &mut W) {
251 match self.role {
252 NatTraversalRole::Client => w.put_u8(0),
253 NatTraversalRole::Server { can_relay } => {
254 w.put_u8(1);
255 w.put_u8(if can_relay { 1 } else { 0 });
256 }
257 NatTraversalRole::Bootstrap => w.put_u8(2),
258 }
259 w.write(self.max_candidates);
260 w.write(self.coordination_timeout);
261 w.write(self.max_concurrent_attempts);
262 }
263
264 fn read<R: Buf>(r: &mut R) -> Result<Self, Error> {
265 let role_byte = r.get::<u8>()?;
266 let role = match role_byte {
267 0 => NatTraversalRole::Client,
268 1 => {
269 let can_relay = r.get::<u8>()? != 0;
270 NatTraversalRole::Server { can_relay }
271 }
272 2 => NatTraversalRole::Bootstrap,
273 _ => return Err(Error::IllegalValue),
274 };
275
276 let max_candidates = r.get()?;
277 let coordination_timeout = r.get()?;
278 let max_concurrent_attempts = r.get()?;
279
280 Ok(Self {
281 role,
282 max_candidates,
283 coordination_timeout,
284 max_concurrent_attempts,
285 })
286 }
287}
288
289#[derive(Debug, Copy, Clone, Eq, PartialEq)]
291pub(crate) enum NatTraversalRole {
292 Client,
294 Server {
296 can_relay: bool,
298 },
299 Bootstrap,
301}
302
303#[derive(Debug, Copy, Clone, Eq, PartialEq)]
307pub(crate) struct PreferredAddress {
308 pub(crate) address_v4: Option<SocketAddrV4>,
309 pub(crate) address_v6: Option<SocketAddrV6>,
310 pub(crate) connection_id: ConnectionId,
311 pub(crate) stateless_reset_token: ResetToken,
312}
313
314impl PreferredAddress {
315 fn wire_size(&self) -> u16 {
316 4 + 2 + 16 + 2 + 1 + self.connection_id.len() as u16 + 16
317 }
318
319 fn write<W: BufMut>(&self, w: &mut W) {
320 w.write(self.address_v4.map_or(Ipv4Addr::UNSPECIFIED, |x| *x.ip()));
321 w.write::<u16>(self.address_v4.map_or(0, |x| x.port()));
322 w.write(self.address_v6.map_or(Ipv6Addr::UNSPECIFIED, |x| *x.ip()));
323 w.write::<u16>(self.address_v6.map_or(0, |x| x.port()));
324 w.write::<u8>(self.connection_id.len() as u8);
325 w.put_slice(&self.connection_id);
326 w.put_slice(&self.stateless_reset_token);
327 }
328
329 fn read<R: Buf>(r: &mut R) -> Result<Self, Error> {
330 let ip_v4 = r.get::<Ipv4Addr>()?;
331 let port_v4 = r.get::<u16>()?;
332 let ip_v6 = r.get::<Ipv6Addr>()?;
333 let port_v6 = r.get::<u16>()?;
334 let cid_len = r.get::<u8>()?;
335 if r.remaining() < cid_len as usize || cid_len > MAX_CID_SIZE as u8 {
336 return Err(Error::Malformed);
337 }
338 let mut stage = [0; MAX_CID_SIZE];
339 r.copy_to_slice(&mut stage[0..cid_len as usize]);
340 let cid = ConnectionId::new(&stage[0..cid_len as usize]);
341 if r.remaining() < 16 {
342 return Err(Error::Malformed);
343 }
344 let mut token = [0; RESET_TOKEN_SIZE];
345 r.copy_to_slice(&mut token);
346 let address_v4 = if ip_v4.is_unspecified() && port_v4 == 0 {
347 None
348 } else {
349 Some(SocketAddrV4::new(ip_v4, port_v4))
350 };
351 let address_v6 = if ip_v6.is_unspecified() && port_v6 == 0 {
352 None
353 } else {
354 Some(SocketAddrV6::new(ip_v6, port_v6, 0, 0))
355 };
356 if address_v4.is_none() && address_v6.is_none() {
357 return Err(Error::IllegalValue);
358 }
359 Ok(Self {
360 address_v4,
361 address_v6,
362 connection_id: cid,
363 stateless_reset_token: token.into(),
364 })
365 }
366}
367
368#[derive(Debug, Copy, Clone, Eq, PartialEq, Error)]
370pub enum Error {
371 #[error("parameter had illegal value")]
373 IllegalValue,
374 #[error("parameters were malformed")]
376 Malformed,
377}
378
379impl From<Error> for TransportError {
380 fn from(e: Error) -> Self {
381 match e {
382 Error::IllegalValue => Self::TRANSPORT_PARAMETER_ERROR("illegal value"),
383 Error::Malformed => Self::TRANSPORT_PARAMETER_ERROR("malformed"),
384 }
385 }
386}
387
388impl From<UnexpectedEnd> for Error {
389 fn from(_: UnexpectedEnd) -> Self {
390 Self::Malformed
391 }
392}
393
394impl TransportParameters {
395 pub fn write<W: BufMut>(&self, w: &mut W) {
397 for idx in self
398 .write_order
399 .as_ref()
400 .unwrap_or(&std::array::from_fn(|i| i as u8))
401 {
402 let id = TransportParameterId::SUPPORTED[*idx as usize];
403 match id {
404 TransportParameterId::ReservedTransportParameter => {
405 if let Some(param) = self.grease_transport_parameter {
406 param.write(w);
407 }
408 }
409 TransportParameterId::StatelessResetToken => {
410 if let Some(ref x) = self.stateless_reset_token {
411 w.write_var(id as u64);
412 w.write_var(16);
413 w.put_slice(x);
414 }
415 }
416 TransportParameterId::DisableActiveMigration => {
417 if self.disable_active_migration {
418 w.write_var(id as u64);
419 w.write_var(0);
420 }
421 }
422 TransportParameterId::MaxDatagramFrameSize => {
423 if let Some(x) = self.max_datagram_frame_size {
424 w.write_var(id as u64);
425 w.write_var(x.size() as u64);
426 w.write(x);
427 }
428 }
429 TransportParameterId::PreferredAddress => {
430 if let Some(ref x) = self.preferred_address {
431 w.write_var(id as u64);
432 w.write_var(x.wire_size() as u64);
433 x.write(w);
434 }
435 }
436 TransportParameterId::OriginalDestinationConnectionId => {
437 if let Some(ref cid) = self.original_dst_cid {
438 w.write_var(id as u64);
439 w.write_var(cid.len() as u64);
440 w.put_slice(cid);
441 }
442 }
443 TransportParameterId::InitialSourceConnectionId => {
444 if let Some(ref cid) = self.initial_src_cid {
445 w.write_var(id as u64);
446 w.write_var(cid.len() as u64);
447 w.put_slice(cid);
448 }
449 }
450 TransportParameterId::RetrySourceConnectionId => {
451 if let Some(ref cid) = self.retry_src_cid {
452 w.write_var(id as u64);
453 w.write_var(cid.len() as u64);
454 w.put_slice(cid);
455 }
456 }
457 TransportParameterId::GreaseQuicBit => {
458 if self.grease_quic_bit {
459 w.write_var(id as u64);
460 w.write_var(0);
461 }
462 }
463 TransportParameterId::MinAckDelayDraft07 => {
464 if let Some(x) = self.min_ack_delay {
465 w.write_var(id as u64);
466 w.write_var(x.size() as u64);
467 w.write(x);
468 }
469 }
470 TransportParameterId::NatTraversal => {
471 if let Some(ref x) = self.nat_traversal {
472 w.write_var(id as u64);
473 w.write_var(x.wire_size() as u64);
474 x.write(w);
475 }
476 }
477 id => {
478 macro_rules! write_params {
479 {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
480 match id {
481 $(TransportParameterId::$id => {
482 if self.$name.0 != $default {
483 w.write_var(id as u64);
484 w.write(VarInt::try_from(self.$name.size()).unwrap());
485 w.write(self.$name);
486 }
487 })*,
488 _ => {
489 unimplemented!("Missing implementation of write for transport parameter with code {id:?}");
490 }
491 }
492 }
493 }
494 apply_params!(write_params);
495 }
496 }
497 }
498 }
499
500 pub fn read<R: Buf>(side: Side, r: &mut R) -> Result<Self, Error> {
502 let mut params = Self::default();
504
505 macro_rules! param_state {
507 {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {{
508 struct ParamState {
509 $($name: bool,)*
510 }
511
512 ParamState {
513 $($name: false,)*
514 }
515 }}
516 }
517 let mut got = apply_params!(param_state);
518
519 while r.has_remaining() {
520 let id = r.get_var()?;
521 let len = r.get_var()?;
522 if (r.remaining() as u64) < len {
523 return Err(Error::Malformed);
524 }
525 let len = len as usize;
526 let Ok(id) = TransportParameterId::try_from(id) else {
527 r.advance(len);
529 continue;
530 };
531
532 match id {
533 TransportParameterId::OriginalDestinationConnectionId => {
534 decode_cid(len, &mut params.original_dst_cid, r)?
535 }
536 TransportParameterId::StatelessResetToken => {
537 if len != 16 || params.stateless_reset_token.is_some() {
538 return Err(Error::Malformed);
539 }
540 let mut tok = [0; RESET_TOKEN_SIZE];
541 r.copy_to_slice(&mut tok);
542 params.stateless_reset_token = Some(tok.into());
543 }
544 TransportParameterId::DisableActiveMigration => {
545 if len != 0 || params.disable_active_migration {
546 return Err(Error::Malformed);
547 }
548 params.disable_active_migration = true;
549 }
550 TransportParameterId::PreferredAddress => {
551 if params.preferred_address.is_some() {
552 return Err(Error::Malformed);
553 }
554 params.preferred_address = Some(PreferredAddress::read(&mut r.take(len))?);
555 }
556 TransportParameterId::InitialSourceConnectionId => {
557 decode_cid(len, &mut params.initial_src_cid, r)?
558 }
559 TransportParameterId::RetrySourceConnectionId => {
560 decode_cid(len, &mut params.retry_src_cid, r)?
561 }
562 TransportParameterId::MaxDatagramFrameSize => {
563 if len > 8 || params.max_datagram_frame_size.is_some() {
564 return Err(Error::Malformed);
565 }
566 params.max_datagram_frame_size = Some(r.get().unwrap());
567 }
568 TransportParameterId::GreaseQuicBit => match len {
569 0 => params.grease_quic_bit = true,
570 _ => return Err(Error::Malformed),
571 },
572 TransportParameterId::MinAckDelayDraft07 => {
573 params.min_ack_delay = Some(r.get().unwrap())
574 }
575 TransportParameterId::NatTraversal => {
576 if params.nat_traversal.is_some() {
577 return Err(Error::Malformed);
578 }
579 params.nat_traversal = Some(NatTraversalConfig::read(&mut r.take(len))?);
580 }
581 _ => {
582 macro_rules! parse {
583 {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
584 match id {
585 $(TransportParameterId::$id => {
586 let value = r.get::<VarInt>()?;
587 if len != value.size() || got.$name { return Err(Error::Malformed); }
588 params.$name = value.into();
589 got.$name = true;
590 })*
591 _ => r.advance(len),
592 }
593 }
594 }
595 apply_params!(parse);
596 }
597 }
598 }
599
600 if params.ack_delay_exponent.0 > 20
604 || params.max_ack_delay.0 >= 1 << 14
606 || params.active_connection_id_limit.0 < 2
608 || params.max_udp_payload_size.0 < 1200
610 || params.initial_max_streams_bidi.0 > MAX_STREAM_COUNT
612 || params.initial_max_streams_uni.0 > MAX_STREAM_COUNT
613 || params.min_ack_delay.is_some_and(|min_ack_delay| {
615 min_ack_delay.0 > params.max_ack_delay.0 * 1_000
617 })
618 || (side.is_server()
620 && (params.original_dst_cid.is_some()
621 || params.preferred_address.is_some()
622 || params.retry_src_cid.is_some()
623 || params.stateless_reset_token.is_some()))
624 || params
626 .preferred_address.is_some_and(|x| x.connection_id.is_empty())
627 {
628 return Err(Error::IllegalValue);
629 }
630
631 Ok(params)
632 }
633}
634
635#[derive(Debug, Copy, Clone, Eq, PartialEq)]
644pub(crate) struct ReservedTransportParameter {
645 id: VarInt,
647
648 payload: [u8; Self::MAX_PAYLOAD_LEN],
650
651 payload_len: usize,
653}
654
655impl ReservedTransportParameter {
656 fn random(rng: &mut impl RngCore) -> Self {
662 let id = Self::generate_reserved_id(rng);
663
664 let payload_len = rng.random_range(0..Self::MAX_PAYLOAD_LEN);
665
666 let payload = {
667 let mut slice = [0u8; Self::MAX_PAYLOAD_LEN];
668 rng.fill_bytes(&mut slice[..payload_len]);
669 slice
670 };
671
672 Self {
673 id,
674 payload,
675 payload_len,
676 }
677 }
678
679 fn write(&self, w: &mut impl BufMut) {
680 w.write_var(self.id.0);
681 w.write_var(self.payload_len as u64);
682 w.put_slice(&self.payload[..self.payload_len]);
683 }
684
685 fn generate_reserved_id(rng: &mut impl RngCore) -> VarInt {
690 let id = {
691 let rand = rng.random_range(0u64..(1 << 62) - 27);
692 let n = rand / 31;
693 31 * n + 27
694 };
695 debug_assert!(
696 id % 31 == 27,
697 "generated id does not have the form of 31 * N + 27"
698 );
699 VarInt::from_u64(id).expect(
700 "generated id does fit into range of allowed transport parameter IDs: [0; 2^62)",
701 )
702 }
703
704 const MAX_PAYLOAD_LEN: usize = 16;
708}
709
710#[repr(u64)]
711#[derive(Debug, Clone, Copy, PartialEq, Eq)]
712pub(crate) enum TransportParameterId {
713 OriginalDestinationConnectionId = 0x00,
715 MaxIdleTimeout = 0x01,
716 StatelessResetToken = 0x02,
717 MaxUdpPayloadSize = 0x03,
718 InitialMaxData = 0x04,
719 InitialMaxStreamDataBidiLocal = 0x05,
720 InitialMaxStreamDataBidiRemote = 0x06,
721 InitialMaxStreamDataUni = 0x07,
722 InitialMaxStreamsBidi = 0x08,
723 InitialMaxStreamsUni = 0x09,
724 AckDelayExponent = 0x0A,
725 MaxAckDelay = 0x0B,
726 DisableActiveMigration = 0x0C,
727 PreferredAddress = 0x0D,
728 ActiveConnectionIdLimit = 0x0E,
729 InitialSourceConnectionId = 0x0F,
730 RetrySourceConnectionId = 0x10,
731
732 ReservedTransportParameter = 0x1B,
734
735 MaxDatagramFrameSize = 0x20,
737
738 GreaseQuicBit = 0x2AB2,
740
741 MinAckDelayDraft07 = 0xFF04DE1B,
743
744 NatTraversal = 0x58, }
748
749impl TransportParameterId {
750 const SUPPORTED: [Self; 22] = [
752 Self::MaxIdleTimeout,
753 Self::MaxUdpPayloadSize,
754 Self::InitialMaxData,
755 Self::InitialMaxStreamDataBidiLocal,
756 Self::InitialMaxStreamDataBidiRemote,
757 Self::InitialMaxStreamDataUni,
758 Self::InitialMaxStreamsBidi,
759 Self::InitialMaxStreamsUni,
760 Self::AckDelayExponent,
761 Self::MaxAckDelay,
762 Self::ActiveConnectionIdLimit,
763 Self::ReservedTransportParameter,
764 Self::StatelessResetToken,
765 Self::DisableActiveMigration,
766 Self::MaxDatagramFrameSize,
767 Self::PreferredAddress,
768 Self::OriginalDestinationConnectionId,
769 Self::InitialSourceConnectionId,
770 Self::RetrySourceConnectionId,
771 Self::GreaseQuicBit,
772 Self::MinAckDelayDraft07,
773 Self::NatTraversal,
774 ];
775}
776
777impl std::cmp::PartialEq<u64> for TransportParameterId {
778 fn eq(&self, other: &u64) -> bool {
779 *other == (*self as u64)
780 }
781}
782
783impl TryFrom<u64> for TransportParameterId {
784 type Error = ();
785
786 fn try_from(value: u64) -> Result<Self, Self::Error> {
787 let param = match value {
788 id if Self::MaxIdleTimeout == id => Self::MaxIdleTimeout,
789 id if Self::MaxUdpPayloadSize == id => Self::MaxUdpPayloadSize,
790 id if Self::InitialMaxData == id => Self::InitialMaxData,
791 id if Self::InitialMaxStreamDataBidiLocal == id => Self::InitialMaxStreamDataBidiLocal,
792 id if Self::InitialMaxStreamDataBidiRemote == id => {
793 Self::InitialMaxStreamDataBidiRemote
794 }
795 id if Self::InitialMaxStreamDataUni == id => Self::InitialMaxStreamDataUni,
796 id if Self::InitialMaxStreamsBidi == id => Self::InitialMaxStreamsBidi,
797 id if Self::InitialMaxStreamsUni == id => Self::InitialMaxStreamsUni,
798 id if Self::AckDelayExponent == id => Self::AckDelayExponent,
799 id if Self::MaxAckDelay == id => Self::MaxAckDelay,
800 id if Self::ActiveConnectionIdLimit == id => Self::ActiveConnectionIdLimit,
801 id if Self::ReservedTransportParameter == id => Self::ReservedTransportParameter,
802 id if Self::StatelessResetToken == id => Self::StatelessResetToken,
803 id if Self::DisableActiveMigration == id => Self::DisableActiveMigration,
804 id if Self::MaxDatagramFrameSize == id => Self::MaxDatagramFrameSize,
805 id if Self::PreferredAddress == id => Self::PreferredAddress,
806 id if Self::OriginalDestinationConnectionId == id => {
807 Self::OriginalDestinationConnectionId
808 }
809 id if Self::InitialSourceConnectionId == id => Self::InitialSourceConnectionId,
810 id if Self::RetrySourceConnectionId == id => Self::RetrySourceConnectionId,
811 id if Self::GreaseQuicBit == id => Self::GreaseQuicBit,
812 id if Self::MinAckDelayDraft07 == id => Self::MinAckDelayDraft07,
813 id if Self::NatTraversal == id => Self::NatTraversal,
814 _ => return Err(()),
815 };
816 Ok(param)
817 }
818}
819
820fn decode_cid(len: usize, value: &mut Option<ConnectionId>, r: &mut impl Buf) -> Result<(), Error> {
821 if len > MAX_CID_SIZE || value.is_some() || r.remaining() < len {
822 return Err(Error::Malformed);
823 }
824
825 *value = Some(ConnectionId::from_buf(r, len));
826 Ok(())
827}
828
829#[cfg(test)]
830mod test {
831 use super::*;
832
833 #[test]
834 fn coding() {
835 let mut buf = Vec::new();
836 let params = TransportParameters {
837 initial_src_cid: Some(ConnectionId::new(&[])),
838 original_dst_cid: Some(ConnectionId::new(&[])),
839 initial_max_streams_bidi: 16u32.into(),
840 initial_max_streams_uni: 16u32.into(),
841 ack_delay_exponent: 2u32.into(),
842 max_udp_payload_size: 1200u32.into(),
843 preferred_address: Some(PreferredAddress {
844 address_v4: Some(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 42)),
845 address_v6: Some(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 24, 0, 0)),
846 connection_id: ConnectionId::new(&[0x42]),
847 stateless_reset_token: [0xab; RESET_TOKEN_SIZE].into(),
848 }),
849 grease_quic_bit: true,
850 min_ack_delay: Some(2_000u32.into()),
851 ..TransportParameters::default()
852 };
853 params.write(&mut buf);
854 assert_eq!(
855 TransportParameters::read(Side::Client, &mut buf.as_slice()).unwrap(),
856 params
857 );
858 }
859
860 #[test]
861 fn reserved_transport_parameter_generate_reserved_id() {
862 use rand::rngs::mock::StepRng;
863 let mut rngs = [
864 StepRng::new(0, 1),
865 StepRng::new(1, 1),
866 StepRng::new(27, 1),
867 StepRng::new(31, 1),
868 StepRng::new(u32::MAX as u64, 1),
869 StepRng::new(u32::MAX as u64 - 1, 1),
870 StepRng::new(u32::MAX as u64 + 1, 1),
871 StepRng::new(u32::MAX as u64 - 27, 1),
872 StepRng::new(u32::MAX as u64 + 27, 1),
873 StepRng::new(u32::MAX as u64 - 31, 1),
874 StepRng::new(u32::MAX as u64 + 31, 1),
875 StepRng::new(u64::MAX, 1),
876 StepRng::new(u64::MAX - 1, 1),
877 StepRng::new(u64::MAX - 27, 1),
878 StepRng::new(u64::MAX - 31, 1),
879 StepRng::new(1 << 62, 1),
880 StepRng::new((1 << 62) - 1, 1),
881 StepRng::new((1 << 62) + 1, 1),
882 StepRng::new((1 << 62) - 27, 1),
883 StepRng::new((1 << 62) + 27, 1),
884 StepRng::new((1 << 62) - 31, 1),
885 StepRng::new((1 << 62) + 31, 1),
886 ];
887 for rng in &mut rngs {
888 let id = ReservedTransportParameter::generate_reserved_id(rng);
889 assert!(id.0 % 31 == 27)
890 }
891 }
892
893 #[test]
894 fn reserved_transport_parameter_ignored_when_read() {
895 let mut buf = Vec::new();
896 let reserved_parameter = ReservedTransportParameter::random(&mut rand::rng());
897 assert!(reserved_parameter.payload_len < ReservedTransportParameter::MAX_PAYLOAD_LEN);
898 assert!(reserved_parameter.id.0 % 31 == 27);
899
900 reserved_parameter.write(&mut buf);
901 assert!(!buf.is_empty());
902 let read_params = TransportParameters::read(Side::Server, &mut buf.as_slice()).unwrap();
903 assert_eq!(read_params, TransportParameters::default());
904 }
905
906 #[test]
907 fn read_semantic_validation() {
908 #[allow(clippy::type_complexity)]
909 let illegal_params_builders: Vec<Box<dyn FnMut(&mut TransportParameters)>> = vec![
910 Box::new(|t| {
911 let min_ack_delay = t.max_ack_delay.0 * 1_000 + 1;
913 t.min_ack_delay = Some(VarInt::from_u64(min_ack_delay).unwrap())
914 }),
915 Box::new(|t| {
916 t.preferred_address = Some(PreferredAddress {
919 address_v4: Some(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 42)),
920 address_v6: None,
921 connection_id: ConnectionId::new(&[]),
922 stateless_reset_token: [0xab; RESET_TOKEN_SIZE].into(),
923 })
924 }),
925 ];
926
927 for mut builder in illegal_params_builders {
928 let mut buf = Vec::new();
929 let mut params = TransportParameters::default();
930 builder(&mut params);
931 params.write(&mut buf);
932
933 assert_eq!(
934 TransportParameters::read(Side::Server, &mut buf.as_slice()),
935 Err(Error::IllegalValue)
936 );
937 }
938 }
939
940 #[test]
941 fn resumption_params_validation() {
942 let high_limit = TransportParameters {
943 initial_max_streams_uni: 32u32.into(),
944 ..TransportParameters::default()
945 };
946 let low_limit = TransportParameters {
947 initial_max_streams_uni: 16u32.into(),
948 ..TransportParameters::default()
949 };
950 high_limit.validate_resumption_from(&low_limit).unwrap();
951 low_limit.validate_resumption_from(&high_limit).unwrap_err();
952 }
953}