Skip to main content

quinn_proto/
transport_parameters.rs

1//! QUIC connection transport parameters
2//!
3//! The `TransportParameters` type is used to represent the transport parameters
4//! negotiated by peers while establishing a QUIC connection. This process
5//! happens as part of the establishment of the TLS session. As such, the types
6//! contained in this modules should generally only be referred to by custom
7//! implementations of the `crypto::Session` trait.
8
9use std::{
10    convert::TryFrom,
11    net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6},
12};
13
14use bytes::{Buf, BufMut};
15use rand::{Rng, RngExt, 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
28// Apply a given macro to a list of all the transport parameters having integer types, along with
29// their codes and default values. Using this helps us avoid error-prone duplication of the
30// contained information across decoding, encoding, and the `Default` impl. Whenever we want to do
31// something with transport parameters, we'll handle the bulk of cases by writing a macro that
32// takes a list of arguments in this form, then passing it to this macro.
33macro_rules! apply_params {
34    ($macro:ident) => {
35        $macro! {
36            // #[doc] name (id) = default,
37            /// Milliseconds, disabled if zero
38            max_idle_timeout(MaxIdleTimeout) = 0,
39            /// Limits the size of UDP payloads that the endpoint is willing to receive
40            max_udp_payload_size(MaxUdpPayloadSize) = 65527,
41
42            /// Initial value for the maximum amount of data that can be sent on the connection
43            initial_max_data(InitialMaxData) = 0,
44            /// Initial flow control limit for locally-initiated bidirectional streams
45            initial_max_stream_data_bidi_local(InitialMaxStreamDataBidiLocal) = 0,
46            /// Initial flow control limit for peer-initiated bidirectional streams
47            initial_max_stream_data_bidi_remote(InitialMaxStreamDataBidiRemote) = 0,
48            /// Initial flow control limit for unidirectional streams
49            initial_max_stream_data_uni(InitialMaxStreamDataUni) = 0,
50
51            /// Initial maximum number of bidirectional streams the peer may initiate
52            initial_max_streams_bidi(InitialMaxStreamsBidi) = 0,
53            /// Initial maximum number of unidirectional streams the peer may initiate
54            initial_max_streams_uni(InitialMaxStreamsUni) = 0,
55
56            /// Exponent used to decode the ACK Delay field in the ACK frame
57            ack_delay_exponent(AckDelayExponent) = 3,
58            /// Maximum amount of time in milliseconds by which the endpoint will delay sending
59            /// acknowledgments
60            max_ack_delay(MaxAckDelay) = 25,
61            /// Maximum number of connection IDs from the peer that an endpoint is willing to store
62            active_connection_id_limit(ActiveConnectionIdLimit) = 2,
63        }
64    };
65}
66
67macro_rules! make_struct {
68    {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
69        /// Transport parameters used to negotiate connection-level preferences between peers
70        #[derive(Debug, Copy, Clone, Eq, PartialEq)]
71        pub struct TransportParameters {
72            $($(#[$doc])* pub(crate) $name : VarInt,)*
73
74            /// Does the endpoint support active connection migration
75            pub(crate) disable_active_migration: bool,
76            /// Maximum size for datagram frames
77            pub(crate) max_datagram_frame_size: Option<VarInt>,
78            /// The value that the endpoint included in the Source Connection ID field of the first
79            /// Initial packet it sends for the connection
80            pub(crate) initial_src_cid: Option<ConnectionId>,
81            /// The endpoint is willing to receive QUIC packets containing any value for the fixed
82            /// bit
83            pub(crate) grease_quic_bit: bool,
84
85            /// Minimum amount of time in microseconds by which the endpoint is able to delay
86            /// sending acknowledgments
87            ///
88            /// If a value is provided, it implies that the endpoint supports QUIC Acknowledgement
89            /// Frequency
90            pub(crate) min_ack_delay: Option<VarInt>,
91
92            // Server-only
93            /// The value of the Destination Connection ID field from the first Initial packet sent
94            /// by the client
95            pub(crate) original_dst_cid: Option<ConnectionId>,
96            /// The value that the server included in the Source Connection ID field of a Retry
97            /// packet
98            pub(crate) retry_src_cid: Option<ConnectionId>,
99            /// Token used by the client to verify a stateless reset from the server
100            pub(crate) stateless_reset_token: Option<ResetToken>,
101            /// The server's preferred address for communication after handshake completion
102            pub(crate) preferred_address: Option<PreferredAddress>,
103            /// The randomly generated reserved transport parameter to sustain future extensibility
104            /// of transport parameter extensions.
105            /// When present, it is included during serialization but ignored during deserialization.
106            pub(crate) grease_transport_parameter: Option<ReservedTransportParameter>,
107
108            /// Defines the order in which transport parameters are serialized.
109            ///
110            /// This field is initialized only for outgoing `TransportParameters` instances and
111            /// is set to `None` for `TransportParameters` received from a peer.
112            pub(crate) write_order: Option<[u8; TransportParameterId::SUPPORTED.len()]>,
113        }
114
115        // We deliberately don't implement the `Default` trait, since that would be public, and
116        // downstream crates should never construct `TransportParameters` except by decoding those
117        // supplied by a peer.
118        impl TransportParameters {
119            /// Standard defaults, used if the peer does not supply a given parameter.
120            pub(crate) fn default() -> Self {
121                Self {
122                    $($name: VarInt::from_u32($default),)*
123
124                    disable_active_migration: false,
125                    max_datagram_frame_size: None,
126                    initial_src_cid: None,
127                    grease_quic_bit: false,
128                    min_ack_delay: None,
129
130                    original_dst_cid: None,
131                    retry_src_cid: None,
132                    stateless_reset_token: None,
133                    preferred_address: None,
134                    grease_transport_parameter: None,
135                    write_order: None,
136                }
137            }
138        }
139    }
140}
141
142apply_params!(make_struct);
143
144impl TransportParameters {
145    pub(crate) fn new(
146        config: &TransportConfig,
147        endpoint_config: &EndpointConfig,
148        cid_gen: &dyn ConnectionIdGenerator,
149        initial_src_cid: ConnectionId,
150        server_config: Option<&ServerConfig>,
151        rng: &mut impl Rng,
152    ) -> Self {
153        Self {
154            initial_src_cid: Some(initial_src_cid),
155            initial_max_streams_bidi: config.max_concurrent_bidi_streams,
156            initial_max_streams_uni: config.max_concurrent_uni_streams,
157            initial_max_data: config.receive_window,
158            initial_max_stream_data_bidi_local: config.stream_receive_window,
159            initial_max_stream_data_bidi_remote: config.stream_receive_window,
160            initial_max_stream_data_uni: config.stream_receive_window,
161            max_udp_payload_size: endpoint_config.max_udp_payload_size,
162            max_idle_timeout: config.max_idle_timeout.unwrap_or(VarInt(0)),
163            disable_active_migration: server_config.is_some_and(|c| !c.migration),
164            active_connection_id_limit: if cid_gen.cid_len() == 0 {
165                2 // i.e. default, i.e. unsent
166            } else {
167                CidQueue::LEN as u32
168            }
169            .into(),
170            max_datagram_frame_size: config
171                .datagram_receive_buffer_size
172                .map(|x| (x.min(u16::MAX.into()) as u16).into()),
173            grease_quic_bit: endpoint_config.grease_quic_bit,
174            min_ack_delay: Some(
175                VarInt::from_u64(u64::try_from(TIMER_GRANULARITY.as_micros()).unwrap()).unwrap(),
176            ),
177            grease_transport_parameter: Some(ReservedTransportParameter::random(rng)),
178            write_order: Some({
179                let mut order = std::array::from_fn(|i| i as u8);
180                order.shuffle(rng);
181                order
182            }),
183            ..Self::default()
184        }
185    }
186
187    /// Check that these parameters are legal when resuming from
188    /// certain cached parameters
189    pub(crate) fn validate_resumption_from(&self, cached: &Self) -> Result<(), TransportError> {
190        if cached.active_connection_id_limit > self.active_connection_id_limit
191            || cached.initial_max_data > self.initial_max_data
192            || cached.initial_max_stream_data_bidi_local > self.initial_max_stream_data_bidi_local
193            || cached.initial_max_stream_data_bidi_remote > self.initial_max_stream_data_bidi_remote
194            || cached.initial_max_stream_data_uni > self.initial_max_stream_data_uni
195            || cached.initial_max_streams_bidi > self.initial_max_streams_bidi
196            || cached.initial_max_streams_uni > self.initial_max_streams_uni
197            || cached.max_datagram_frame_size > self.max_datagram_frame_size
198            || cached.grease_quic_bit && !self.grease_quic_bit
199        {
200            return Err(TransportError::PROTOCOL_VIOLATION(
201                "0-RTT accepted with incompatible transport parameters",
202            ));
203        }
204        Ok(())
205    }
206
207    /// Maximum number of CIDs to issue to this peer
208    ///
209    /// Consider both a) the active_connection_id_limit from the other end; and
210    /// b) LOC_CID_COUNT used locally
211    pub(crate) fn issue_cids_limit(&self) -> u64 {
212        self.active_connection_id_limit.0.min(LOC_CID_COUNT)
213    }
214}
215
216/// A server's preferred address
217///
218/// This is communicated as a transport parameter during TLS session establishment.
219#[derive(Debug, Copy, Clone, Eq, PartialEq)]
220pub(crate) struct PreferredAddress {
221    pub(crate) address_v4: Option<SocketAddrV4>,
222    pub(crate) address_v6: Option<SocketAddrV6>,
223    pub(crate) connection_id: ConnectionId,
224    pub(crate) stateless_reset_token: ResetToken,
225}
226
227impl PreferredAddress {
228    fn wire_size(&self) -> u16 {
229        4 + 2 + 16 + 2 + 1 + self.connection_id.len() as u16 + 16
230    }
231
232    fn write<W: BufMut>(&self, w: &mut W) {
233        w.write(self.address_v4.map_or(Ipv4Addr::UNSPECIFIED, |x| *x.ip()));
234        w.write::<u16>(self.address_v4.map_or(0, |x| x.port()));
235        w.write(self.address_v6.map_or(Ipv6Addr::UNSPECIFIED, |x| *x.ip()));
236        w.write::<u16>(self.address_v6.map_or(0, |x| x.port()));
237        w.write::<u8>(self.connection_id.len() as u8);
238        w.put_slice(&self.connection_id);
239        w.put_slice(&self.stateless_reset_token);
240    }
241
242    fn read<R: Buf>(r: &mut R) -> Result<Self, Error> {
243        let ip_v4 = r.get::<Ipv4Addr>()?;
244        let port_v4 = r.get::<u16>()?;
245        let ip_v6 = r.get::<Ipv6Addr>()?;
246        let port_v6 = r.get::<u16>()?;
247        let cid_len = r.get::<u8>()?;
248        if r.remaining() < cid_len as usize || cid_len > MAX_CID_SIZE as u8 {
249            return Err(Error::Malformed);
250        }
251        let mut stage = [0; MAX_CID_SIZE];
252        r.copy_to_slice(&mut stage[0..cid_len as usize]);
253        let cid = ConnectionId::new(&stage[0..cid_len as usize]);
254        if r.remaining() < 16 {
255            return Err(Error::Malformed);
256        }
257        let mut token = [0; RESET_TOKEN_SIZE];
258        r.copy_to_slice(&mut token);
259        let address_v4 = if ip_v4.is_unspecified() && port_v4 == 0 {
260            None
261        } else {
262            Some(SocketAddrV4::new(ip_v4, port_v4))
263        };
264        let address_v6 = if ip_v6.is_unspecified() && port_v6 == 0 {
265            None
266        } else {
267            Some(SocketAddrV6::new(ip_v6, port_v6, 0, 0))
268        };
269        if address_v4.is_none() && address_v6.is_none() {
270            return Err(Error::IllegalValue);
271        }
272        Ok(Self {
273            address_v4,
274            address_v6,
275            connection_id: cid,
276            stateless_reset_token: token.into(),
277        })
278    }
279}
280
281/// Errors encountered while decoding `TransportParameters`
282#[derive(Debug, Copy, Clone, Eq, PartialEq, Error)]
283pub enum Error {
284    /// Parameters that are semantically invalid
285    #[error("parameter had illegal value")]
286    IllegalValue,
287    /// Catch-all error for problems while decoding transport parameters
288    #[error("parameters were malformed")]
289    Malformed,
290}
291
292impl From<Error> for TransportError {
293    fn from(e: Error) -> Self {
294        match e {
295            Error::IllegalValue => Self::TRANSPORT_PARAMETER_ERROR("illegal value"),
296            Error::Malformed => Self::TRANSPORT_PARAMETER_ERROR("malformed"),
297        }
298    }
299}
300
301impl From<UnexpectedEnd> for Error {
302    fn from(_: UnexpectedEnd) -> Self {
303        Self::Malformed
304    }
305}
306
307impl TransportParameters {
308    /// Encode `TransportParameters` into buffer
309    pub fn write<W: BufMut>(&self, w: &mut W) {
310        for idx in self
311            .write_order
312            .as_ref()
313            .unwrap_or(&std::array::from_fn(|i| i as u8))
314        {
315            let id = TransportParameterId::SUPPORTED[*idx as usize];
316            match id {
317                TransportParameterId::ReservedTransportParameter => {
318                    if let Some(param) = self.grease_transport_parameter {
319                        param.write(w);
320                    }
321                }
322                TransportParameterId::StatelessResetToken => {
323                    if let Some(ref x) = self.stateless_reset_token {
324                        w.write_var(id as u64);
325                        w.write_var(16);
326                        w.put_slice(x);
327                    }
328                }
329                TransportParameterId::DisableActiveMigration => {
330                    if self.disable_active_migration {
331                        w.write_var(id as u64);
332                        w.write_var(0);
333                    }
334                }
335                TransportParameterId::MaxDatagramFrameSize => {
336                    if let Some(x) = self.max_datagram_frame_size {
337                        w.write_var(id as u64);
338                        w.write_var(x.size() as u64);
339                        w.write(x);
340                    }
341                }
342                TransportParameterId::PreferredAddress => {
343                    if let Some(ref x) = self.preferred_address {
344                        w.write_var(id as u64);
345                        w.write_var(x.wire_size() as u64);
346                        x.write(w);
347                    }
348                }
349                TransportParameterId::OriginalDestinationConnectionId => {
350                    if let Some(ref cid) = self.original_dst_cid {
351                        w.write_var(id as u64);
352                        w.write_var(cid.len() as u64);
353                        w.put_slice(cid);
354                    }
355                }
356                TransportParameterId::InitialSourceConnectionId => {
357                    if let Some(ref cid) = self.initial_src_cid {
358                        w.write_var(id as u64);
359                        w.write_var(cid.len() as u64);
360                        w.put_slice(cid);
361                    }
362                }
363                TransportParameterId::RetrySourceConnectionId => {
364                    if let Some(ref cid) = self.retry_src_cid {
365                        w.write_var(id as u64);
366                        w.write_var(cid.len() as u64);
367                        w.put_slice(cid);
368                    }
369                }
370                TransportParameterId::GreaseQuicBit => {
371                    if self.grease_quic_bit {
372                        w.write_var(id as u64);
373                        w.write_var(0);
374                    }
375                }
376                TransportParameterId::MinAckDelayDraft07 => {
377                    if let Some(x) = self.min_ack_delay {
378                        w.write_var(id as u64);
379                        w.write_var(x.size() as u64);
380                        w.write(x);
381                    }
382                }
383                id => {
384                    macro_rules! write_params {
385                        {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
386                            match id {
387                                $(TransportParameterId::$id => {
388                                    if self.$name.0 != $default {
389                                        w.write_var(id as u64);
390                                        w.write(VarInt::try_from(self.$name.size()).unwrap());
391                                        w.write(self.$name);
392                                    }
393                                })*,
394                                _ => {
395                                    unimplemented!("Missing implementation of write for transport parameter with code {id:?}");
396                                }
397                            }
398                        }
399                    }
400                    apply_params!(write_params);
401                }
402            }
403        }
404    }
405
406    /// Decode `TransportParameters` from buffer
407    pub fn read<R: Buf>(side: Side, r: &mut R) -> Result<Self, Error> {
408        // Initialize to protocol-specified defaults
409        let mut params = Self::default();
410
411        // State to check for duplicate transport parameters.
412        macro_rules! param_state {
413            {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {{
414                struct ParamState {
415                    $($name: bool,)*
416                }
417
418                ParamState {
419                    $($name: false,)*
420                }
421            }}
422        }
423        let mut got = apply_params!(param_state);
424
425        while r.has_remaining() {
426            let id = r.get_var()?;
427            let len = r.get_var()?;
428            if (r.remaining() as u64) < len {
429                return Err(Error::Malformed);
430            }
431            let len = len as usize;
432            let Ok(id) = TransportParameterId::try_from(id) else {
433                // unknown transport parameters are ignored
434                r.advance(len);
435                continue;
436            };
437
438            let remaining_before = r.remaining();
439
440            match id {
441                TransportParameterId::OriginalDestinationConnectionId => {
442                    decode_cid(len, &mut params.original_dst_cid, r)?
443                }
444                TransportParameterId::StatelessResetToken => {
445                    if len != 16 || params.stateless_reset_token.is_some() {
446                        return Err(Error::Malformed);
447                    }
448                    let mut tok = [0; RESET_TOKEN_SIZE];
449                    r.copy_to_slice(&mut tok);
450                    params.stateless_reset_token = Some(tok.into());
451                }
452                TransportParameterId::DisableActiveMigration => {
453                    if len != 0 || params.disable_active_migration {
454                        return Err(Error::Malformed);
455                    }
456                    params.disable_active_migration = true;
457                }
458                TransportParameterId::PreferredAddress => {
459                    if params.preferred_address.is_some() {
460                        return Err(Error::Malformed);
461                    }
462                    params.preferred_address = Some(PreferredAddress::read(&mut r.take(len))?);
463                }
464                TransportParameterId::InitialSourceConnectionId => {
465                    decode_cid(len, &mut params.initial_src_cid, r)?
466                }
467                TransportParameterId::RetrySourceConnectionId => {
468                    decode_cid(len, &mut params.retry_src_cid, r)?
469                }
470                TransportParameterId::MaxDatagramFrameSize => {
471                    if len > 8 || params.max_datagram_frame_size.is_some() {
472                        return Err(Error::Malformed);
473                    }
474                    params.max_datagram_frame_size = Some(r.get()?);
475                }
476                TransportParameterId::GreaseQuicBit => match len {
477                    0 => params.grease_quic_bit = true,
478                    _ => return Err(Error::Malformed),
479                },
480                TransportParameterId::MinAckDelayDraft07 => params.min_ack_delay = Some(r.get()?),
481                _ => {
482                    macro_rules! parse {
483                        {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
484                            match id {
485                                $(TransportParameterId::$id => {
486                                    let value = r.get::<VarInt>()?;
487                                    if len != value.size() || got.$name { return Err(Error::Malformed); }
488                                    params.$name = value.into();
489                                    got.$name = true;
490                                })*
491                                _ => r.advance(len),
492                            }
493                        }
494                    }
495                    apply_params!(parse);
496                }
497            }
498
499            if remaining_before - r.remaining() != len {
500                return Err(Error::Malformed);
501            }
502        }
503
504        // Semantic validation
505
506        // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.26.1
507        if params.ack_delay_exponent.0 > 20
508            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.28.1
509            || params.max_ack_delay.0 >= 1 << 14
510            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-6.2.1
511            || params.active_connection_id_limit.0 < 2
512            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.10.1
513            || params.max_udp_payload_size.0 < 1200
514            // https://www.rfc-editor.org/rfc/rfc9000.html#section-4.6-2
515            || params.initial_max_streams_bidi.0 > MAX_STREAM_COUNT
516            || params.initial_max_streams_uni.0 > MAX_STREAM_COUNT
517            // https://www.ietf.org/archive/id/draft-ietf-quic-ack-frequency-08.html#section-3-4
518            || params.min_ack_delay.is_some_and(|min_ack_delay| {
519                // min_ack_delay uses microseconds, whereas max_ack_delay uses milliseconds
520                min_ack_delay.0 > params.max_ack_delay.0 * 1_000
521            })
522            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-8
523            || (side.is_server()
524                && (params.original_dst_cid.is_some()
525                    || params.preferred_address.is_some()
526                    || params.retry_src_cid.is_some()
527                    || params.stateless_reset_token.is_some()))
528            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.38.1
529            || params
530                .preferred_address.is_some_and(|x| x.connection_id.is_empty())
531        {
532            return Err(Error::IllegalValue);
533        }
534
535        Ok(params)
536    }
537}
538
539/// A reserved transport parameter.
540///
541/// It has an identifier of the form 31 * N + 27 for the integer value of N.
542/// Such identifiers are reserved to exercise the requirement that unknown transport parameters be ignored.
543/// The reserved transport parameter has no semantics and can carry arbitrary values.
544/// It may be included in transport parameters sent to the peer, and should be ignored when received.
545///
546/// See spec: <https://www.rfc-editor.org/rfc/rfc9000.html#section-18.1>
547#[derive(Debug, Copy, Clone, Eq, PartialEq)]
548pub(crate) struct ReservedTransportParameter {
549    /// The reserved identifier of the transport parameter
550    id: VarInt,
551
552    /// Buffer to store the parameter payload
553    payload: [u8; Self::MAX_PAYLOAD_LEN],
554
555    /// The number of bytes to include in the wire format from the `payload` buffer
556    payload_len: usize,
557}
558
559impl ReservedTransportParameter {
560    /// Generates a transport parameter with a random payload and a reserved ID.
561    ///
562    /// The implementation is inspired by quic-go and quiche:
563    /// 1. <https://github.com/quic-go/quic-go/blob/3e0a67b2476e1819752f04d75968de042b197b56/internal/wire/transport_parameters.go#L338-L344>
564    /// 2. <https://github.com/google/quiche/blob/cb1090b20c40e2f0815107857324e99acf6ec567/quiche/quic/core/crypto/transport_parameters.cc#L843-L860>
565    fn random(rng: &mut impl Rng) -> Self {
566        let id = Self::generate_reserved_id(rng);
567
568        let payload_len = rng.random_range(0..Self::MAX_PAYLOAD_LEN);
569
570        let payload = {
571            let mut slice = [0u8; Self::MAX_PAYLOAD_LEN];
572            rng.fill_bytes(&mut slice[..payload_len]);
573            slice
574        };
575
576        Self {
577            id,
578            payload,
579            payload_len,
580        }
581    }
582
583    fn write(&self, w: &mut impl BufMut) {
584        w.write_var(self.id.0);
585        w.write_var(self.payload_len as u64);
586        w.put_slice(&self.payload[..self.payload_len]);
587    }
588
589    /// Generates a random reserved identifier of the form `31 * N + 27`, as required by RFC 9000.
590    /// Reserved transport parameter identifiers are used to test compliance with the requirement
591    /// that unknown transport parameters must be ignored by peers.
592    /// See: <https://www.rfc-editor.org/rfc/rfc9000.html#section-18.1> and <https://www.rfc-editor.org/rfc/rfc9000.html#section-22.3>
593    fn generate_reserved_id(rng: &mut impl Rng) -> VarInt {
594        let id = {
595            let rand = rng.random_range(0u64..(1 << 62) - 27);
596            let n = rand / 31;
597            31 * n + 27
598        };
599        debug_assert!(
600            id % 31 == 27,
601            "generated id does not have the form of 31 * N + 27"
602        );
603        VarInt::from_u64(id).expect(
604            "generated id does fit into range of allowed transport parameter IDs: [0; 2^62)",
605        )
606    }
607
608    /// The maximum length of the payload to include as the parameter payload.
609    /// This value is not a specification-imposed limit but is chosen to match
610    /// the limit used by other implementations of QUIC, e.g., quic-go and quiche.
611    const MAX_PAYLOAD_LEN: usize = 16;
612}
613
614#[repr(u64)]
615#[derive(Debug, Clone, Copy, PartialEq, Eq)]
616pub(crate) enum TransportParameterId {
617    // https://www.rfc-editor.org/rfc/rfc9000.html#iana-tp-table
618    OriginalDestinationConnectionId = 0x00,
619    MaxIdleTimeout = 0x01,
620    StatelessResetToken = 0x02,
621    MaxUdpPayloadSize = 0x03,
622    InitialMaxData = 0x04,
623    InitialMaxStreamDataBidiLocal = 0x05,
624    InitialMaxStreamDataBidiRemote = 0x06,
625    InitialMaxStreamDataUni = 0x07,
626    InitialMaxStreamsBidi = 0x08,
627    InitialMaxStreamsUni = 0x09,
628    AckDelayExponent = 0x0A,
629    MaxAckDelay = 0x0B,
630    DisableActiveMigration = 0x0C,
631    PreferredAddress = 0x0D,
632    ActiveConnectionIdLimit = 0x0E,
633    InitialSourceConnectionId = 0x0F,
634    RetrySourceConnectionId = 0x10,
635
636    // Smallest possible ID of reserved transport parameter https://datatracker.ietf.org/doc/html/rfc9000#section-22.3
637    ReservedTransportParameter = 0x1B,
638
639    // https://www.rfc-editor.org/rfc/rfc9221.html#section-3
640    MaxDatagramFrameSize = 0x20,
641
642    // https://www.rfc-editor.org/rfc/rfc9287.html#section-3
643    GreaseQuicBit = 0x2AB2,
644
645    // https://datatracker.ietf.org/doc/html/draft-ietf-quic-ack-frequency#section-10.1
646    MinAckDelayDraft07 = 0xFF04DE1B,
647}
648
649impl TransportParameterId {
650    /// Array with all supported transport parameter IDs
651    const SUPPORTED: [Self; 21] = [
652        Self::MaxIdleTimeout,
653        Self::MaxUdpPayloadSize,
654        Self::InitialMaxData,
655        Self::InitialMaxStreamDataBidiLocal,
656        Self::InitialMaxStreamDataBidiRemote,
657        Self::InitialMaxStreamDataUni,
658        Self::InitialMaxStreamsBidi,
659        Self::InitialMaxStreamsUni,
660        Self::AckDelayExponent,
661        Self::MaxAckDelay,
662        Self::ActiveConnectionIdLimit,
663        Self::ReservedTransportParameter,
664        Self::StatelessResetToken,
665        Self::DisableActiveMigration,
666        Self::MaxDatagramFrameSize,
667        Self::PreferredAddress,
668        Self::OriginalDestinationConnectionId,
669        Self::InitialSourceConnectionId,
670        Self::RetrySourceConnectionId,
671        Self::GreaseQuicBit,
672        Self::MinAckDelayDraft07,
673    ];
674}
675
676impl std::cmp::PartialEq<u64> for TransportParameterId {
677    fn eq(&self, other: &u64) -> bool {
678        *other == (*self as u64)
679    }
680}
681
682impl TryFrom<u64> for TransportParameterId {
683    type Error = ();
684
685    fn try_from(value: u64) -> Result<Self, Self::Error> {
686        let param = match value {
687            id if Self::MaxIdleTimeout == id => Self::MaxIdleTimeout,
688            id if Self::MaxUdpPayloadSize == id => Self::MaxUdpPayloadSize,
689            id if Self::InitialMaxData == id => Self::InitialMaxData,
690            id if Self::InitialMaxStreamDataBidiLocal == id => Self::InitialMaxStreamDataBidiLocal,
691            id if Self::InitialMaxStreamDataBidiRemote == id => {
692                Self::InitialMaxStreamDataBidiRemote
693            }
694            id if Self::InitialMaxStreamDataUni == id => Self::InitialMaxStreamDataUni,
695            id if Self::InitialMaxStreamsBidi == id => Self::InitialMaxStreamsBidi,
696            id if Self::InitialMaxStreamsUni == id => Self::InitialMaxStreamsUni,
697            id if Self::AckDelayExponent == id => Self::AckDelayExponent,
698            id if Self::MaxAckDelay == id => Self::MaxAckDelay,
699            id if Self::ActiveConnectionIdLimit == id => Self::ActiveConnectionIdLimit,
700            id if Self::ReservedTransportParameter == id => Self::ReservedTransportParameter,
701            id if Self::StatelessResetToken == id => Self::StatelessResetToken,
702            id if Self::DisableActiveMigration == id => Self::DisableActiveMigration,
703            id if Self::MaxDatagramFrameSize == id => Self::MaxDatagramFrameSize,
704            id if Self::PreferredAddress == id => Self::PreferredAddress,
705            id if Self::OriginalDestinationConnectionId == id => {
706                Self::OriginalDestinationConnectionId
707            }
708            id if Self::InitialSourceConnectionId == id => Self::InitialSourceConnectionId,
709            id if Self::RetrySourceConnectionId == id => Self::RetrySourceConnectionId,
710            id if Self::GreaseQuicBit == id => Self::GreaseQuicBit,
711            id if Self::MinAckDelayDraft07 == id => Self::MinAckDelayDraft07,
712            _ => return Err(()),
713        };
714        Ok(param)
715    }
716}
717
718fn decode_cid(len: usize, value: &mut Option<ConnectionId>, r: &mut impl Buf) -> Result<(), Error> {
719    if len > MAX_CID_SIZE || value.is_some() || r.remaining() < len {
720        return Err(Error::Malformed);
721    }
722
723    *value = Some(ConnectionId::from_buf(r, len));
724    Ok(())
725}
726
727#[cfg(test)]
728mod test {
729    use std::convert::Infallible;
730
731    use rand::TryRng;
732
733    use super::*;
734
735    #[test]
736    fn coding() {
737        let mut buf = Vec::new();
738        let params = TransportParameters {
739            initial_src_cid: Some(ConnectionId::new(&[])),
740            original_dst_cid: Some(ConnectionId::new(&[])),
741            initial_max_streams_bidi: 16u32.into(),
742            initial_max_streams_uni: 16u32.into(),
743            ack_delay_exponent: 2u32.into(),
744            max_udp_payload_size: 1200u32.into(),
745            preferred_address: Some(PreferredAddress {
746                address_v4: Some(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 42)),
747                address_v6: Some(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 24, 0, 0)),
748                connection_id: ConnectionId::new(&[0x42]),
749                stateless_reset_token: [0xab; RESET_TOKEN_SIZE].into(),
750            }),
751            grease_quic_bit: true,
752            min_ack_delay: Some(2_000u32.into()),
753            ..TransportParameters::default()
754        };
755        params.write(&mut buf);
756        assert_eq!(
757            TransportParameters::read(Side::Client, &mut buf.as_slice()).unwrap(),
758            params
759        );
760    }
761
762    #[test]
763    fn reserved_transport_parameter_generate_reserved_id() {
764        let mut rngs = [
765            StepRng(0),
766            StepRng(1),
767            StepRng(27),
768            StepRng(31),
769            StepRng(u32::MAX as u64),
770            StepRng(u32::MAX as u64 - 1),
771            StepRng(u32::MAX as u64 + 1),
772            StepRng(u32::MAX as u64 - 27),
773            StepRng(u32::MAX as u64 + 27),
774            StepRng(u32::MAX as u64 - 31),
775            StepRng(u32::MAX as u64 + 31),
776            StepRng(u64::MAX),
777            StepRng(u64::MAX - 1),
778            StepRng(u64::MAX - 27),
779            StepRng(u64::MAX - 31),
780            StepRng(1 << 62),
781            StepRng((1 << 62) - 1),
782            StepRng((1 << 62) + 1),
783            StepRng((1 << 62) - 27),
784            StepRng((1 << 62) + 27),
785            StepRng((1 << 62) - 31),
786            StepRng((1 << 62) + 31),
787        ];
788        for rng in &mut rngs {
789            let id = ReservedTransportParameter::generate_reserved_id(rng);
790            assert!(id.0 % 31 == 27)
791        }
792    }
793
794    struct StepRng(u64);
795
796    impl TryRng for StepRng {
797        type Error = Infallible;
798
799        #[inline]
800        fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
801            Ok(self.next_u64() as u32)
802        }
803
804        #[inline]
805        fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
806            let res = self.0;
807            self.0 = self.0.wrapping_add(1);
808            Ok(res)
809        }
810
811        fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> {
812            let mut left = dst;
813            while left.len() >= 8 {
814                let (l, r) = left.split_at_mut(8);
815                left = r;
816                l.copy_from_slice(&self.next_u64().to_le_bytes());
817            }
818            let n = left.len();
819            if n > 0 {
820                left.copy_from_slice(&self.next_u32().to_le_bytes()[..n]);
821            }
822
823            Ok(())
824        }
825    }
826
827    #[test]
828    fn reserved_transport_parameter_ignored_when_read() {
829        let mut buf = Vec::new();
830        let reserved_parameter = ReservedTransportParameter::random(&mut rand::rng());
831        assert!(reserved_parameter.payload_len < ReservedTransportParameter::MAX_PAYLOAD_LEN);
832        assert!(reserved_parameter.id.0 % 31 == 27);
833
834        reserved_parameter.write(&mut buf);
835        assert!(!buf.is_empty());
836        let read_params = TransportParameters::read(Side::Server, &mut buf.as_slice()).unwrap();
837        assert_eq!(read_params, TransportParameters::default());
838    }
839
840    #[test]
841    fn read_semantic_validation() {
842        #[allow(clippy::type_complexity)]
843        let illegal_params_builders: Vec<Box<dyn FnMut(&mut TransportParameters)>> = vec![
844            Box::new(|t| {
845                // This min_ack_delay is bigger than max_ack_delay!
846                let min_ack_delay = t.max_ack_delay.0 * 1_000 + 1;
847                t.min_ack_delay = Some(VarInt::from_u64(min_ack_delay).unwrap())
848            }),
849            Box::new(|t| {
850                // Preferred address can only be sent by senders (and we are reading the transport
851                // params as a client)
852                t.preferred_address = Some(PreferredAddress {
853                    address_v4: Some(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 42)),
854                    address_v6: None,
855                    connection_id: ConnectionId::new(&[]),
856                    stateless_reset_token: [0xab; RESET_TOKEN_SIZE].into(),
857                })
858            }),
859        ];
860
861        for mut builder in illegal_params_builders {
862            let mut buf = Vec::new();
863            let mut params = TransportParameters::default();
864            builder(&mut params);
865            params.write(&mut buf);
866
867            assert_eq!(
868                TransportParameters::read(Side::Server, &mut buf.as_slice()),
869                Err(Error::IllegalValue)
870            );
871        }
872    }
873
874    #[test]
875    fn read_length_mismatch() {
876        // `max_datagram_frame_size` claims three bytes of value but encodes a one-byte `VarInt`,
877        // so a whole `disable_active_migration` parameter fits inside its declared length.
878        let mut buf = Vec::new();
879        buf.write_var(TransportParameterId::MaxDatagramFrameSize as u64);
880        buf.write_var(3);
881        buf.write(VarInt::from_u32(0));
882        buf.write_var(TransportParameterId::DisableActiveMigration as u64);
883        buf.write_var(0);
884        assert_eq!(
885            TransportParameters::read(Side::Server, &mut buf.as_slice()),
886            Err(Error::Malformed)
887        );
888    }
889
890    #[test]
891    fn read_min_ack_delay_length_mismatch() {
892        // `min_ack_delay` claims no value at all, so its `VarInt` starts on the parameter that
893        // follows it.
894        let mut buf = Vec::new();
895        buf.write_var(TransportParameterId::MinAckDelayDraft07 as u64);
896        buf.write_var(0);
897        buf.write_var(TransportParameterId::InitialMaxData as u64);
898        buf.write_var(1);
899        buf.write(VarInt::from_u32(7));
900        assert_eq!(
901            TransportParameters::read(Side::Server, &mut buf.as_slice()),
902            Err(Error::Malformed)
903        );
904    }
905
906    #[test]
907    fn read_preferred_address_length_mismatch() {
908        // `preferred_address` has a fixed size for a given connection ID length, so bytes past
909        // that size are read as a parameter of their own.
910        let address = PreferredAddress {
911            address_v4: Some(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 42)),
912            address_v6: None,
913            connection_id: ConnectionId::new(&[0x42]),
914            stateless_reset_token: [0xab; RESET_TOKEN_SIZE].into(),
915        };
916        let mut value = Vec::new();
917        address.write(&mut value);
918        value.write_var(TransportParameterId::DisableActiveMigration as u64);
919        value.write_var(0);
920        let mut buf = Vec::new();
921        buf.write_var(TransportParameterId::PreferredAddress as u64);
922        buf.write_var(value.len() as u64);
923        buf.extend_from_slice(&value);
924        assert_eq!(
925            TransportParameters::read(Side::Client, &mut buf.as_slice()),
926            Err(Error::Malformed)
927        );
928    }
929
930    #[test]
931    fn resumption_params_validation() {
932        let high_limit = TransportParameters {
933            initial_max_streams_uni: 32u32.into(),
934            ..TransportParameters::default()
935        };
936        let low_limit = TransportParameters {
937            initial_max_streams_uni: 16u32.into(),
938            ..TransportParameters::default()
939        };
940        high_limit.validate_resumption_from(&low_limit).unwrap();
941        low_limit.validate_resumption_from(&high_limit).unwrap_err();
942    }
943}