Skip to main content

net_wire/quic/transport_parameters/
mod.rs

1//! RFC 9000 section 18 QUIC transport-parameter sequence parsing.
2
3mod semantic;
4mod value;
5
6use core::{fmt, iter::FusedIterator};
7
8use super::varint::{QuicVarInt, QuicVarIntParseError};
9#[cfg(feature = "tls")]
10use crate::tls::extensions::extension::TlsExtension;
11#[cfg(feature = "tls")]
12use crate::tls::types::TlsExtensionType;
13
14pub use semantic::{
15    QuicTransportParameterHandshakeContext, QuicTransportParameterHandshakeError,
16    QuicTransportParameterSemanticError, QuicTransportParameterSender, QuicTransportParametersV1,
17};
18pub use value::{
19    QuicPreferredAddress, QuicTransportParameterValueError, QuicTransportParameterValueKind,
20};
21
22/// A raw QUIC transport-parameter identifier, preserving unknown and reserved values.
23#[repr(transparent)]
24#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
25pub struct QuicTransportParameterId(u64);
26
27impl QuicTransportParameterId {
28    /// The original-destination-connection-ID parameter (`0x00`).
29    pub const ORIGINAL_DESTINATION_CONNECTION_ID: Self = Self(0x00);
30    /// The maximum-idle-timeout parameter (`0x01`).
31    pub const MAX_IDLE_TIMEOUT: Self = Self(0x01);
32    /// The stateless-reset-token parameter (`0x02`).
33    pub const STATELESS_RESET_TOKEN: Self = Self(0x02);
34    /// The maximum-UDP-payload-size parameter (`0x03`).
35    pub const MAX_UDP_PAYLOAD_SIZE: Self = Self(0x03);
36    /// The initial-maximum-data parameter (`0x04`).
37    pub const INITIAL_MAX_DATA: Self = Self(0x04);
38    /// The initial-maximum-stream-data-bidi-local parameter (`0x05`).
39    pub const INITIAL_MAX_STREAM_DATA_BIDI_LOCAL: Self = Self(0x05);
40    /// The initial-maximum-stream-data-bidi-remote parameter (`0x06`).
41    pub const INITIAL_MAX_STREAM_DATA_BIDI_REMOTE: Self = Self(0x06);
42    /// The initial-maximum-stream-data-uni parameter (`0x07`).
43    pub const INITIAL_MAX_STREAM_DATA_UNI: Self = Self(0x07);
44    /// The initial-maximum-streams-bidi parameter (`0x08`).
45    pub const INITIAL_MAX_STREAMS_BIDI: Self = Self(0x08);
46    /// The initial-maximum-streams-uni parameter (`0x09`).
47    pub const INITIAL_MAX_STREAMS_UNI: Self = Self(0x09);
48    /// The acknowledgment-delay-exponent parameter (`0x0a`).
49    pub const ACK_DELAY_EXPONENT: Self = Self(0x0a);
50    /// The maximum-acknowledgment-delay parameter (`0x0b`).
51    pub const MAX_ACK_DELAY: Self = Self(0x0b);
52    /// The disable-active-migration parameter (`0x0c`).
53    pub const DISABLE_ACTIVE_MIGRATION: Self = Self(0x0c);
54    /// The preferred-address parameter (`0x0d`).
55    pub const PREFERRED_ADDRESS: Self = Self(0x0d);
56    /// The active-connection-ID-limit parameter (`0x0e`).
57    pub const ACTIVE_CONNECTION_ID_LIMIT: Self = Self(0x0e);
58    /// The initial-source-connection-ID parameter (`0x0f`).
59    pub const INITIAL_SOURCE_CONNECTION_ID: Self = Self(0x0f);
60    /// The retry-source-connection-ID parameter (`0x10`).
61    pub const RETRY_SOURCE_CONNECTION_ID: Self = Self(0x10);
62
63    /// Creates an identifier from its decoded wire value.
64    pub const fn new(raw: u64) -> Self {
65        Self(raw)
66    }
67
68    /// Returns the decoded wire value.
69    pub const fn raw(self) -> u64 {
70        self.0
71    }
72
73    /// Returns whether this identifier has RFC 9000's reserved `31 * N + 27` form.
74    pub const fn is_reserved(self) -> bool {
75        self.0 % 31 == 27
76    }
77}
78
79/// Identifies a transport-parameter tuple field.
80#[derive(Clone, Copy, Debug, Eq, PartialEq)]
81pub enum QuicTransportParameterField {
82    /// The tuple's parameter-ID variable integer.
83    Id,
84    /// The tuple's parameter-length variable integer.
85    Length,
86    /// The tuple's opaque parameter-value bytes.
87    Value,
88}
89
90/// Failure to parse a QUIC transport-parameter extension payload.
91#[derive(Clone, Copy, Debug, Eq, PartialEq)]
92pub enum QuicTransportParameterParseError {
93    /// A tuple variable integer is incomplete.
94    IncompleteVarInt {
95        /// Tuple field whose variable integer is incomplete.
96        field: QuicTransportParameterField,
97        /// Absolute field offset from the extension-payload start.
98        offset: usize,
99        /// Exact variable-integer parsing failure.
100        error: QuicVarIntParseError,
101    },
102    /// A decoded parameter length cannot be represented as `usize` on this target.
103    LengthNotRepresentable {
104        /// Absolute length-field offset from the extension-payload start.
105        offset: usize,
106        /// Decoded wire length.
107        value: u64,
108    },
109    /// Computing a parameter-value end offset overflows `usize`.
110    LengthOverflow {
111        /// Absolute value offset from the extension-payload start.
112        offset: usize,
113        /// Representable decoded parameter length.
114        length: usize,
115    },
116    /// A parameter value is incomplete.
117    IncompleteValue {
118        /// Absolute value offset from the extension-payload start.
119        offset: usize,
120        /// Bytes required by the value.
121        required: usize,
122        /// Bytes available for the value.
123        available: usize,
124    },
125    /// A parameter identifier occurs more than once.
126    DuplicateId {
127        /// Repeated decoded parameter identifier.
128        id: QuicTransportParameterId,
129        /// Absolute tuple offset of the first occurrence.
130        first_offset: usize,
131        /// Absolute tuple offset of the duplicate occurrence.
132        duplicate_offset: usize,
133    },
134}
135
136impl fmt::Display for QuicTransportParameterParseError {
137    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138        match self {
139            Self::IncompleteVarInt {
140                field,
141                offset,
142                error,
143            } => write!(
144                f,
145                "QUIC transport parameter {field:?} at offset {offset} cannot be parsed: {error}"
146            ),
147            Self::LengthNotRepresentable { offset, value } => write!(
148                f,
149                "QUIC transport parameter length at offset {offset} has unrepresentable value {value}"
150            ),
151            Self::LengthOverflow { offset, length } => write!(
152                f,
153                "QUIC transport parameter value at offset {offset} overflows with length {length}"
154            ),
155            Self::IncompleteValue {
156                offset,
157                required,
158                available,
159            } => write!(
160                f,
161                "QUIC transport parameter value at offset {offset} is incomplete: need {required} bytes, have {available}"
162            ),
163            Self::DuplicateId {
164                id,
165                first_offset,
166                duplicate_offset,
167            } => write!(
168                f,
169                "QUIC transport parameter ID {} is duplicated at offset {duplicate_offset}; first occurred at offset {first_offset}",
170                id.raw()
171            ),
172        }
173    }
174}
175
176/// Failure to interpret a TLS extension as RFC 9001 QUIC transport parameters.
177#[cfg(feature = "tls")]
178#[derive(Clone, Copy, Debug, Eq, PartialEq)]
179pub enum QuicTransportParametersTlsExtensionError {
180    /// The TLS extension has a type other than `quic_transport_parameters`.
181    WrongExtensionType {
182        /// Exact type carried by the TLS extension.
183        actual: TlsExtensionType,
184    },
185    /// The extension payload is not a valid QUIC transport-parameter sequence.
186    Parse(QuicTransportParameterParseError),
187}
188
189#[cfg(feature = "tls")]
190impl fmt::Display for QuicTransportParametersTlsExtensionError {
191    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192        match self {
193            Self::WrongExtensionType { actual } => write!(
194                f,
195                "TLS extension type {} is not QUIC transport parameters",
196                actual.raw()
197            ),
198            Self::Parse(error) => {
199                write!(f, "TLS QUIC transport parameters cannot be parsed: {error}")
200            }
201        }
202    }
203}
204
205/// A checked borrowed QUIC transport-parameter tuple.
206#[derive(Clone, Copy, Debug, Eq, PartialEq)]
207pub struct QuicTransportParameter<'a> {
208    bytes: &'a [u8],
209    id: QuicVarInt<'a>,
210    length: QuicVarInt<'a>,
211    value: &'a [u8],
212}
213
214impl<'a> QuicTransportParameter<'a> {
215    /// Returns the exact encoded tuple bytes.
216    pub const fn as_bytes(self) -> &'a [u8] {
217        self.bytes
218    }
219
220    /// Returns the exact encoded parameter-ID variable integer.
221    pub const fn id(self) -> QuicVarInt<'a> {
222        self.id
223    }
224
225    /// Returns the decoded parameter identifier.
226    pub const fn parameter_id(self) -> QuicTransportParameterId {
227        QuicTransportParameterId::new(self.id.value())
228    }
229
230    /// Returns the exact encoded parameter-length variable integer.
231    pub const fn length(self) -> QuicVarInt<'a> {
232        self.length
233    }
234
235    /// Returns the exact opaque parameter-value bytes, which may be empty.
236    pub const fn value(self) -> &'a [u8] {
237        self.value
238    }
239}
240
241/// A complete validated borrowed QUIC transport-parameter extension payload.
242#[derive(Clone, Copy, Debug, Eq, PartialEq)]
243pub struct QuicTransportParameters<'a> {
244    bytes: &'a [u8],
245}
246
247impl<'a> QuicTransportParameters<'a> {
248    /// Parses and validates a complete transport-parameter extension payload.
249    pub fn parse(bytes: &'a [u8]) -> Result<Self, QuicTransportParameterParseError> {
250        let mut remaining = bytes;
251        let mut offset = 0;
252        while !remaining.is_empty() {
253            let (parameter, suffix) = parse_at(remaining, offset)?;
254            if let Some(first_offset) = first_id_offset(bytes, offset, parameter.parameter_id()) {
255                return Err(QuicTransportParameterParseError::DuplicateId {
256                    id: parameter.parameter_id(),
257                    first_offset,
258                    duplicate_offset: offset,
259                });
260            }
261            let consumed = parameter.as_bytes().len();
262            remaining = suffix;
263            offset += consumed;
264        }
265        Ok(Self { bytes })
266    }
267
268    /// Validates all present known parameters against RFC 9000 version-1 role and value rules.
269    pub fn validate_v1(
270        self,
271        sender: QuicTransportParameterSender,
272    ) -> Result<QuicTransportParametersV1<'a>, QuicTransportParameterSemanticError> {
273        semantic::validate(self.bytes, sender)
274    }
275
276    /// Returns the exact complete extension-payload bytes.
277    pub const fn as_bytes(self) -> &'a [u8] {
278        self.bytes
279    }
280
281    /// Returns a fresh iterator over this already validated extension payload.
282    pub const fn iter(self) -> QuicTransportParameterIter<'a> {
283        QuicTransportParameterIter::new(self.bytes)
284    }
285}
286
287#[cfg(feature = "tls")]
288impl<'a> TlsExtension<'a> {
289    /// Parses this RFC 9001 QUIC transport-parameters extension payload.
290    ///
291    /// RFC 9001 permits this extension in ClientHello and EncryptedExtensions and requires it
292    /// in both. Callers that own the parent handshake-message context enforce its placement and
293    /// presence; this accessor parses only the extension payload.
294    pub fn quic_transport_parameters(
295        &self,
296    ) -> Result<QuicTransportParameters<'a>, QuicTransportParametersTlsExtensionError> {
297        if self.extension_type() != TlsExtensionType::QUIC_TRANSPORT_PARAMETERS {
298            return Err(
299                QuicTransportParametersTlsExtensionError::WrongExtensionType {
300                    actual: self.extension_type(),
301                },
302            );
303        }
304        QuicTransportParameters::parse(self.data())
305            .map_err(QuicTransportParametersTlsExtensionError::Parse)
306    }
307}
308
309/// A fallible fused iterator over a QUIC transport-parameter extension payload.
310#[derive(Clone, Debug)]
311pub struct QuicTransportParameterIter<'a> {
312    remaining: &'a [u8],
313    offset: usize,
314    failed: bool,
315}
316
317impl<'a> QuicTransportParameterIter<'a> {
318    pub(super) const fn new(bytes: &'a [u8]) -> Self {
319        Self {
320            remaining: bytes,
321            offset: 0,
322            failed: false,
323        }
324    }
325}
326
327impl<'a> Iterator for QuicTransportParameterIter<'a> {
328    type Item = Result<QuicTransportParameter<'a>, QuicTransportParameterParseError>;
329
330    fn next(&mut self) -> Option<Self::Item> {
331        if self.failed || self.remaining.is_empty() {
332            return None;
333        }
334
335        match parse_at(self.remaining, self.offset) {
336            Ok((parameter, suffix)) => {
337                let consumed = parameter.as_bytes().len();
338                self.remaining = suffix;
339                self.offset += consumed;
340                Some(Ok(parameter))
341            }
342            Err(error) => {
343                self.failed = true;
344                self.remaining = &[];
345                Some(Err(error))
346            }
347        }
348    }
349}
350
351impl FusedIterator for QuicTransportParameterIter<'_> {}
352
353fn parse_at<'a>(
354    bytes: &'a [u8],
355    offset: usize,
356) -> Result<(QuicTransportParameter<'a>, &'a [u8]), QuicTransportParameterParseError> {
357    let id = QuicVarInt::parse(bytes).map_err(|error| {
358        QuicTransportParameterParseError::IncompleteVarInt {
359            field: QuicTransportParameterField::Id,
360            offset,
361            error,
362        }
363    })?;
364    let length_start = id.byte_len();
365    let length_offset = offset + length_start;
366    let length = QuicVarInt::parse(&bytes[length_start..]).map_err(|error| {
367        QuicTransportParameterParseError::IncompleteVarInt {
368            field: QuicTransportParameterField::Length,
369            offset: length_offset,
370            error,
371        }
372    })?;
373    let value_start = length_start + length.byte_len();
374    let value_offset = offset + value_start;
375    let value_length = usize::try_from(length.value()).map_err(|_| {
376        QuicTransportParameterParseError::LengthNotRepresentable {
377            offset: length_offset,
378            value: length.value(),
379        }
380    })?;
381    let value_end = value_start.checked_add(value_length).ok_or(
382        QuicTransportParameterParseError::LengthOverflow {
383            offset: value_offset,
384            length: value_length,
385        },
386    )?;
387    let available = bytes.len() - value_start;
388    if available < value_length {
389        return Err(QuicTransportParameterParseError::IncompleteValue {
390            offset: value_offset,
391            required: value_length,
392            available,
393        });
394    }
395
396    Ok((
397        QuicTransportParameter {
398            bytes: &bytes[..value_end],
399            id,
400            length,
401            value: &bytes[value_start..value_end],
402        },
403        &bytes[value_end..],
404    ))
405}
406
407fn first_id_offset(bytes: &[u8], end: usize, id: QuicTransportParameterId) -> Option<usize> {
408    let mut remaining = &bytes[..end];
409    let mut offset = 0;
410    while !remaining.is_empty() {
411        let (parameter, suffix) = match parse_at(remaining, offset) {
412            Ok(parsed) => parsed,
413            Err(_) => return None,
414        };
415        if parameter.parameter_id() == id {
416            return Some(offset);
417        }
418        offset += parameter.as_bytes().len();
419        remaining = suffix;
420    }
421    None
422}