Skip to main content

net_wire/quic/transport_parameters/
value.rs

1//! Typed RFC 9000 transport-parameter payload views.
2
3use core::fmt;
4
5use super::super::packet::header::QuicConnectionId;
6use super::super::varint::{QuicVarInt, QuicVarIntParseError};
7use super::{QuicTransportParameter, QuicTransportParameterId};
8
9/// Identifies an RFC 9000 transport-parameter payload layout.
10#[derive(Clone, Copy, Debug, Eq, PartialEq)]
11pub enum QuicTransportParameterValueKind {
12    /// A payload containing exactly one QUIC variable integer.
13    Integer,
14    /// A payload containing an opaque connection ID.
15    ConnectionId,
16    /// A payload containing a 16-byte stateless reset token.
17    StatelessResetToken,
18    /// The empty disable-active-migration payload.
19    DisableActiveMigration,
20    /// The preferred-address payload.
21    PreferredAddress,
22}
23
24/// Failure to interpret one raw transport-parameter value as an RFC 9000 payload layout.
25///
26/// Offsets in preferred-address errors are relative to the parameter value, not to an
27/// enclosing extension payload.
28#[derive(Clone, Copy, Debug, Eq, PartialEq)]
29pub enum QuicTransportParameterValueError {
30    /// The accessor does not apply to this parameter ID.
31    WrongId {
32        /// Requested payload layout.
33        kind: QuicTransportParameterValueKind,
34        /// Actual decoded parameter ID.
35        actual: QuicTransportParameterId,
36    },
37    /// The integer payload is empty or ends before its encoded width.
38    MalformedInteger {
39        /// Exact variable-integer parsing failure.
40        error: QuicVarIntParseError,
41    },
42    /// Bytes follow an otherwise complete integer payload.
43    TrailingIntegerBytes {
44        /// Encoded integer length.
45        parsed: usize,
46        /// Complete payload length.
47        available: usize,
48    },
49    /// A fixed-size payload has the wrong length.
50    WrongLength {
51        /// Requested payload layout.
52        kind: QuicTransportParameterValueKind,
53        /// Required exact length.
54        expected: usize,
55        /// Actual payload length.
56        actual: usize,
57    },
58    /// A connection-ID payload exceeds RFC 9000's version-1 bound.
59    ConnectionIdTooLong {
60        /// Maximum permitted length.
61        maximum: usize,
62        /// Actual payload length.
63        actual: usize,
64    },
65    /// The preferred-address connection-ID length is outside `1..=20`.
66    PreferredAddressConnectionIdLength {
67        /// Encoded connection-ID length.
68        actual: u8,
69    },
70    /// A preferred-address field is incomplete.
71    PreferredAddressIncomplete {
72        /// Value-relative start offset of the incomplete field.
73        offset: usize,
74        /// Total value bytes required through this field.
75        required: usize,
76        /// Available value bytes.
77        available: usize,
78    },
79    /// Bytes follow the complete preferred-address payload.
80    PreferredAddressTrailingBytes {
81        /// Value-relative offset of the first extra byte.
82        offset: usize,
83        /// Number of extra bytes.
84        available: usize,
85    },
86}
87
88impl fmt::Display for QuicTransportParameterValueError {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        match self {
91            Self::WrongId { kind, actual } => write!(
92                f,
93                "QUIC transport parameter ID {} does not have the {kind:?} payload layout",
94                actual.raw()
95            ),
96            Self::MalformedInteger { error } => {
97                write!(
98                    f,
99                    "QUIC transport parameter integer payload cannot be parsed: {error}"
100                )
101            }
102            Self::TrailingIntegerBytes { parsed, available } => write!(
103                f,
104                "QUIC transport parameter integer payload has trailing bytes: integer uses {parsed} bytes, payload has {available}"
105            ),
106            Self::WrongLength {
107                kind,
108                expected,
109                actual,
110            } => write!(
111                f,
112                "QUIC transport parameter {kind:?} payload has wrong length: need {expected} bytes, have {actual}"
113            ),
114            Self::ConnectionIdTooLong { maximum, actual } => write!(
115                f,
116                "QUIC transport parameter connection ID is too long: maximum {maximum} bytes, have {actual}"
117            ),
118            Self::PreferredAddressConnectionIdLength { actual } => write!(
119                f,
120                "QUIC preferred-address connection-ID length is outside 1..=20: {actual}"
121            ),
122            Self::PreferredAddressIncomplete {
123                offset,
124                required,
125                available,
126            } => write!(
127                f,
128                "QUIC preferred-address value at offset {offset} is incomplete: need {required} bytes, have {available}"
129            ),
130            Self::PreferredAddressTrailingBytes { offset, available } => write!(
131                f,
132                "QUIC preferred-address value has {available} trailing bytes at offset {offset}"
133            ),
134        }
135    }
136}
137
138/// A checked borrowed RFC 9000 preferred-address payload.
139#[derive(Clone, Copy, Debug, Eq, PartialEq)]
140pub struct QuicPreferredAddress<'a> {
141    bytes: &'a [u8],
142    ipv4_address: &'a [u8; 4],
143    ipv4_port: u16,
144    ipv6_address: &'a [u8; 16],
145    ipv6_port: u16,
146    connection_id: QuicConnectionId<'a>,
147    stateless_reset_token: &'a [u8; 16],
148}
149
150impl<'a> QuicPreferredAddress<'a> {
151    /// Returns the exact complete preferred-address value bytes.
152    pub const fn as_bytes(self) -> &'a [u8] {
153        self.bytes
154    }
155
156    /// Returns the four raw IPv4 address bytes.
157    pub const fn ipv4_address(self) -> &'a [u8; 4] {
158        self.ipv4_address
159    }
160
161    /// Returns the IPv4 port decoded in network byte order.
162    pub const fn ipv4_port(self) -> u16 {
163        self.ipv4_port
164    }
165
166    /// Returns the sixteen raw IPv6 address bytes.
167    pub const fn ipv6_address(self) -> &'a [u8; 16] {
168        self.ipv6_address
169    }
170
171    /// Returns the IPv6 port decoded in network byte order.
172    pub const fn ipv6_port(self) -> u16 {
173        self.ipv6_port
174    }
175
176    /// Returns the exact preferred-address connection ID.
177    pub const fn connection_id(self) -> QuicConnectionId<'a> {
178        self.connection_id
179    }
180
181    /// Returns the exact 16-byte stateless reset token.
182    pub const fn stateless_reset_token(self) -> &'a [u8; 16] {
183        self.stateless_reset_token
184    }
185}
186
187impl<'a> QuicTransportParameter<'a> {
188    /// Interprets an RFC 9000 integer-valued parameter while preserving its exact width.
189    pub fn integer(self) -> Result<QuicVarInt<'a>, QuicTransportParameterValueError> {
190        if !is_integer_id(self.parameter_id()) {
191            return Err(wrong_id(
192                QuicTransportParameterValueKind::Integer,
193                self.parameter_id(),
194            ));
195        }
196        let value = QuicVarInt::parse(self.value)
197            .map_err(|error| QuicTransportParameterValueError::MalformedInteger { error })?;
198        if value.byte_len() != self.value.len() {
199            return Err(QuicTransportParameterValueError::TrailingIntegerBytes {
200                parsed: value.byte_len(),
201                available: self.value.len(),
202            });
203        }
204        Ok(value)
205    }
206
207    /// Interprets an RFC 9000 connection-ID parameter with its version-1 length bound.
208    pub fn connection_id(self) -> Result<QuicConnectionId<'a>, QuicTransportParameterValueError> {
209        if !is_connection_id_id(self.parameter_id()) {
210            return Err(wrong_id(
211                QuicTransportParameterValueKind::ConnectionId,
212                self.parameter_id(),
213            ));
214        }
215        if self.value.len() > 20 {
216            return Err(QuicTransportParameterValueError::ConnectionIdTooLong {
217                maximum: 20,
218                actual: self.value.len(),
219            });
220        }
221        Ok(QuicConnectionId::new(self.value))
222    }
223
224    /// Interprets the stateless-reset-token parameter's exact 16-byte payload.
225    pub fn stateless_reset_token(self) -> Result<&'a [u8; 16], QuicTransportParameterValueError> {
226        if self.parameter_id() != QuicTransportParameterId::STATELESS_RESET_TOKEN {
227            return Err(wrong_id(
228                QuicTransportParameterValueKind::StatelessResetToken,
229                self.parameter_id(),
230            ));
231        }
232        if self.value.len() != 16 {
233            return Err(QuicTransportParameterValueError::WrongLength {
234                kind: QuicTransportParameterValueKind::StatelessResetToken,
235                expected: 16,
236                actual: self.value.len(),
237            });
238        }
239        self.value
240            .try_into()
241            .map_err(|_| QuicTransportParameterValueError::WrongLength {
242                kind: QuicTransportParameterValueKind::StatelessResetToken,
243                expected: 16,
244                actual: self.value.len(),
245            })
246    }
247
248    /// Validates the empty disable-active-migration parameter payload.
249    pub fn disable_active_migration(self) -> Result<(), QuicTransportParameterValueError> {
250        if self.parameter_id() != QuicTransportParameterId::DISABLE_ACTIVE_MIGRATION {
251            return Err(wrong_id(
252                QuicTransportParameterValueKind::DisableActiveMigration,
253                self.parameter_id(),
254            ));
255        }
256        if !self.value.is_empty() {
257            return Err(QuicTransportParameterValueError::WrongLength {
258                kind: QuicTransportParameterValueKind::DisableActiveMigration,
259                expected: 0,
260                actual: self.value.len(),
261            });
262        }
263        Ok(())
264    }
265
266    /// Interprets the RFC 9000 preferred-address payload without endpoint policy.
267    pub fn preferred_address(
268        self,
269    ) -> Result<QuicPreferredAddress<'a>, QuicTransportParameterValueError> {
270        if self.parameter_id() != QuicTransportParameterId::PREFERRED_ADDRESS {
271            return Err(wrong_id(
272                QuicTransportParameterValueKind::PreferredAddress,
273                self.parameter_id(),
274            ));
275        }
276        parse_preferred_address(self.value)
277    }
278}
279
280fn wrong_id(
281    kind: QuicTransportParameterValueKind,
282    actual: QuicTransportParameterId,
283) -> QuicTransportParameterValueError {
284    QuicTransportParameterValueError::WrongId { kind, actual }
285}
286
287const fn is_integer_id(id: QuicTransportParameterId) -> bool {
288    matches!(
289        id.raw(),
290        0x01 | 0x03 | 0x04 | 0x05 | 0x06 | 0x07 | 0x08 | 0x09 | 0x0a | 0x0b | 0x0e
291    )
292}
293
294const fn is_connection_id_id(id: QuicTransportParameterId) -> bool {
295    matches!(id.raw(), 0x00 | 0x0f | 0x10)
296}
297
298fn preferred_incomplete(
299    offset: usize,
300    required: usize,
301    available: usize,
302) -> QuicTransportParameterValueError {
303    QuicTransportParameterValueError::PreferredAddressIncomplete {
304        offset,
305        required,
306        available,
307    }
308}
309
310fn parse_preferred_address<'a>(
311    bytes: &'a [u8],
312) -> Result<QuicPreferredAddress<'a>, QuicTransportParameterValueError> {
313    let available = bytes.len();
314    if available < 4 {
315        return Err(preferred_incomplete(0, 4, available));
316    }
317    if available < 6 {
318        return Err(preferred_incomplete(4, 6, available));
319    }
320    if available < 22 {
321        return Err(preferred_incomplete(6, 22, available));
322    }
323    if available < 24 {
324        return Err(preferred_incomplete(22, 24, available));
325    }
326    if available < 25 {
327        return Err(preferred_incomplete(24, 25, available));
328    }
329
330    let connection_id_len = usize::from(bytes[24]);
331    if connection_id_len == 0 || connection_id_len > 20 {
332        return Err(
333            QuicTransportParameterValueError::PreferredAddressConnectionIdLength {
334                actual: bytes[24],
335            },
336        );
337    }
338    let connection_id_end = 25 + connection_id_len;
339    if available < connection_id_end {
340        return Err(preferred_incomplete(25, connection_id_end, available));
341    }
342    let token_end = connection_id_end + 16;
343    if available < token_end {
344        return Err(preferred_incomplete(
345            connection_id_end,
346            token_end,
347            available,
348        ));
349    }
350    if available > token_end {
351        return Err(
352            QuicTransportParameterValueError::PreferredAddressTrailingBytes {
353                offset: token_end,
354                available: available - token_end,
355            },
356        );
357    }
358
359    let ipv4_address = bytes[..4]
360        .try_into()
361        .map_err(|_| preferred_incomplete(0, 4, available))?;
362    let ipv6_address = bytes[6..22]
363        .try_into()
364        .map_err(|_| preferred_incomplete(6, 22, available))?;
365    let stateless_reset_token = bytes[connection_id_end..token_end]
366        .try_into()
367        .map_err(|_| preferred_incomplete(connection_id_end, token_end, available))?;
368    Ok(QuicPreferredAddress {
369        bytes,
370        ipv4_address,
371        ipv4_port: u16::from_be_bytes([bytes[4], bytes[5]]),
372        ipv6_address,
373        ipv6_port: u16::from_be_bytes([bytes[22], bytes[23]]),
374        connection_id: QuicConnectionId::new(&bytes[25..connection_id_end]),
375        stateless_reset_token,
376    })
377}