net-wire 0.1.0

Allocation-free no_std wire views and codecs for network protocols.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
//! RFC 9000 version-1 present-value transport-parameter validation.

use core::fmt;

use super::{
    QuicTransportParameter, QuicTransportParameterId, QuicTransportParameterIter,
    QuicTransportParameterParseError, QuicTransportParameterValueError,
};

/// The endpoint that sent a QUIC transport-parameter extension.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum QuicTransportParameterSender {
    /// Parameters sent by a client.
    Client,
    /// Parameters sent by a server.
    Server,
}

/// Packet-derived connection IDs used to validate an RFC 9000 handshake transport-parameter set.
///
/// Each ID is from the first Initial packet in the direction named by RFC 9000 section 7.3.
/// For [`Self::Server`], `Some(retry_source_connection_id)` means the client received a Retry;
/// its value is that Retry packet's source connection ID.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum QuicTransportParameterHandshakeContext<'a> {
    /// Context for transport parameters sent by a client.
    Client {
        /// Source connection ID of the first Initial packet sent by the client.
        initial_source_connection_id: &'a [u8],
    },
    /// Context for transport parameters sent by a server.
    Server {
        /// Source connection ID of the first Initial packet sent by the server.
        initial_source_connection_id: &'a [u8],
        /// Destination connection ID of the first Initial packet received by the server before Retry.
        original_destination_connection_id: &'a [u8],
        /// Source connection ID of the Retry packet received by the client, when one was received.
        retry_source_connection_id: Option<&'a [u8]>,
    },
}

/// Failure to validate RFC 9000 section 7.3 handshake connection-ID consistency.
///
/// Tuple offsets are absolute offsets from the start of the transport-parameter extension payload.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum QuicTransportParameterHandshakeError {
    /// The packet-derived context does not describe the validated parameter sender.
    SenderContextMismatch {
        /// Sender validated for the transport parameters.
        sender: QuicTransportParameterSender,
        /// Sender required by the supplied context.
        context_sender: QuicTransportParameterSender,
    },
    /// A structurally validated parameter no longer parses during handshake validation.
    Structural {
        /// Exact structural parsing failure.
        error: QuicTransportParameterParseError,
    },
    /// A required connection-ID parameter was absent.
    Missing {
        /// Required parameter identifier.
        id: QuicTransportParameterId,
    },
    /// A connection-ID parameter was present when the packet context prohibits it.
    Unexpected {
        /// Unexpected parameter identifier.
        id: QuicTransportParameterId,
        /// Absolute tuple offset from the extension-payload start.
        offset: usize,
    },
    /// A connection-ID parameter differs from its corresponding packet connection ID.
    ConnectionIdMismatch {
        /// Mismatched parameter identifier.
        id: QuicTransportParameterId,
        /// Absolute tuple offset from the extension-payload start.
        offset: usize,
    },
}

impl fmt::Display for QuicTransportParameterHandshakeError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::SenderContextMismatch {
                sender,
                context_sender,
            } => write!(
                f,
                "QUIC transport-parameter sender {sender:?} does not match handshake context for {context_sender:?}"
            ),
            Self::Structural { error } => {
                write!(
                    f,
                    "QUIC handshake transport parameters are not structural: {error}"
                )
            }
            Self::Missing { id } => write!(
                f,
                "QUIC handshake transport parameter ID {} is required but missing",
                id.raw()
            ),
            Self::Unexpected { id, offset } => write!(
                f,
                "QUIC handshake transport parameter ID {} at tuple offset {offset} is unexpected",
                id.raw()
            ),
            Self::ConnectionIdMismatch { id, offset } => write!(
                f,
                "QUIC handshake transport parameter ID {} at tuple offset {offset} does not match its packet connection ID",
                id.raw()
            ),
        }
    }
}

/// Failure to validate present RFC 9000 version-1 transport parameters.
///
/// Tuple offsets are absolute offsets from the start of the transport-parameter
/// extension payload.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum QuicTransportParameterSemanticError {
    /// A structurally validated parameter no longer parses during validation.
    Structural {
        /// Exact structural parsing failure.
        error: QuicTransportParameterParseError,
    },
    /// A known parameter has an invalid RFC 9000 payload layout.
    Value {
        /// Decoded parameter identifier.
        id: QuicTransportParameterId,
        /// Absolute tuple offset from the extension-payload start.
        offset: usize,
        /// Exact typed payload failure.
        error: QuicTransportParameterValueError,
    },
    /// A client sent a parameter reserved for servers.
    ForbiddenSender {
        /// Decoded parameter identifier.
        id: QuicTransportParameterId,
        /// Absolute tuple offset from the extension-payload start.
        offset: usize,
    },
    /// An integer parameter is below its RFC 9000 minimum.
    BelowMinimum {
        /// Decoded parameter identifier.
        id: QuicTransportParameterId,
        /// Absolute tuple offset from the extension-payload start.
        offset: usize,
        /// Decoded integer value.
        value: u64,
        /// Required inclusive minimum.
        minimum: u64,
    },
    /// An integer parameter exceeds its RFC 9000 maximum.
    AboveMaximum {
        /// Decoded parameter identifier.
        id: QuicTransportParameterId,
        /// Absolute tuple offset from the extension-payload start.
        offset: usize,
        /// Decoded integer value.
        value: u64,
        /// Required inclusive maximum.
        maximum: u64,
    },
}

impl fmt::Display for QuicTransportParameterSemanticError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Structural { error } => {
                write!(f, "QUIC transport parameters are not structural: {error}")
            }
            Self::Value { id, offset, error } => write!(
                f,
                "QUIC transport parameter ID {} at tuple offset {offset} has an invalid value: {error}",
                id.raw()
            ),
            Self::ForbiddenSender { id, offset } => write!(
                f,
                "QUIC client transport parameter ID {} at tuple offset {offset} is server-only",
                id.raw()
            ),
            Self::BelowMinimum {
                id,
                offset,
                value,
                minimum,
            } => write!(
                f,
                "QUIC transport parameter ID {} at tuple offset {offset} has value {value}, below minimum {minimum}",
                id.raw()
            ),
            Self::AboveMaximum {
                id,
                offset,
                value,
                maximum,
            } => write!(
                f,
                "QUIC transport parameter ID {} at tuple offset {offset} has value {value}, above maximum {maximum}",
                id.raw()
            ),
        }
    }
}

/// A checked borrowed view of present RFC 9000 version-1 transport parameters.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct QuicTransportParametersV1<'a> {
    bytes: &'a [u8],
    sender: QuicTransportParameterSender,
}

impl<'a> QuicTransportParametersV1<'a> {
    /// Returns the exact complete extension-payload bytes.
    pub const fn as_bytes(self) -> &'a [u8] {
        self.bytes
    }

    /// Returns the endpoint role used for validation.
    pub const fn sender(self) -> QuicTransportParameterSender {
        self.sender
    }

    /// Returns a fresh iterator over the original raw parameter tuples.
    pub const fn iter(self) -> QuicTransportParameterIter<'a> {
        QuicTransportParameterIter::new(self.bytes)
    }

    /// Validates RFC 9000 section 7.3 connection-ID parameters against packet-derived context.
    ///
    /// This checks only the packet consistency rules that require handshake context; construction
    /// of this type has already validated sender role and present parameter value layouts.
    pub fn validate_handshake(
        self,
        context: QuicTransportParameterHandshakeContext<'_>,
    ) -> Result<(), QuicTransportParameterHandshakeError> {
        let context_sender = match context {
            QuicTransportParameterHandshakeContext::Client { .. } => {
                QuicTransportParameterSender::Client
            }
            QuicTransportParameterHandshakeContext::Server { .. } => {
                QuicTransportParameterSender::Server
            }
        };
        if self.sender != context_sender {
            return Err(
                QuicTransportParameterHandshakeError::SenderContextMismatch {
                    sender: self.sender,
                    context_sender,
                },
            );
        }

        match context {
            QuicTransportParameterHandshakeContext::Client {
                initial_source_connection_id,
            } => validate_client_handshake(self, initial_source_connection_id),
            QuicTransportParameterHandshakeContext::Server {
                initial_source_connection_id,
                original_destination_connection_id,
                retry_source_connection_id,
            } => validate_server_handshake(
                self,
                initial_source_connection_id,
                original_destination_connection_id,
                retry_source_connection_id,
            ),
        }
    }
}

fn validate_client_handshake(
    parameters: QuicTransportParametersV1<'_>,
    initial_source_connection_id: &[u8],
) -> Result<(), QuicTransportParameterHandshakeError> {
    let mut initial_present = false;
    let mut offset = 0;
    for parameter in parameters.iter() {
        let parameter = parameter
            .map_err(|error| QuicTransportParameterHandshakeError::Structural { error })?;
        let id = parameter.parameter_id();
        if id == QuicTransportParameterId::INITIAL_SOURCE_CONNECTION_ID {
            initial_present = true;
            if parameter.value() != initial_source_connection_id {
                return Err(QuicTransportParameterHandshakeError::ConnectionIdMismatch {
                    id,
                    offset,
                });
            }
        }
        offset += parameter.as_bytes().len();
    }
    if !initial_present {
        return Err(QuicTransportParameterHandshakeError::Missing {
            id: QuicTransportParameterId::INITIAL_SOURCE_CONNECTION_ID,
        });
    }
    Ok(())
}

fn validate_server_handshake(
    parameters: QuicTransportParametersV1<'_>,
    initial_source_connection_id: &[u8],
    original_destination_connection_id: &[u8],
    retry_source_connection_id: Option<&[u8]>,
) -> Result<(), QuicTransportParameterHandshakeError> {
    let mut initial_present = false;
    let mut original_present = false;
    let mut retry_present = false;
    let mut offset = 0;
    for parameter in parameters.iter() {
        let parameter = parameter
            .map_err(|error| QuicTransportParameterHandshakeError::Structural { error })?;
        let id = parameter.parameter_id();
        match id {
            QuicTransportParameterId::INITIAL_SOURCE_CONNECTION_ID => {
                initial_present = true;
                if parameter.value() != initial_source_connection_id {
                    return Err(QuicTransportParameterHandshakeError::ConnectionIdMismatch {
                        id,
                        offset,
                    });
                }
            }
            QuicTransportParameterId::ORIGINAL_DESTINATION_CONNECTION_ID => {
                original_present = true;
                if parameter.value() != original_destination_connection_id {
                    return Err(QuicTransportParameterHandshakeError::ConnectionIdMismatch {
                        id,
                        offset,
                    });
                }
            }
            QuicTransportParameterId::RETRY_SOURCE_CONNECTION_ID => {
                let Some(expected) = retry_source_connection_id else {
                    return Err(QuicTransportParameterHandshakeError::Unexpected { id, offset });
                };
                retry_present = true;
                if parameter.value() != expected {
                    return Err(QuicTransportParameterHandshakeError::ConnectionIdMismatch {
                        id,
                        offset,
                    });
                }
            }
            _ => {}
        }
        offset += parameter.as_bytes().len();
    }
    if !initial_present {
        return Err(QuicTransportParameterHandshakeError::Missing {
            id: QuicTransportParameterId::INITIAL_SOURCE_CONNECTION_ID,
        });
    }
    if !original_present {
        return Err(QuicTransportParameterHandshakeError::Missing {
            id: QuicTransportParameterId::ORIGINAL_DESTINATION_CONNECTION_ID,
        });
    }
    if retry_source_connection_id.is_some() && !retry_present {
        return Err(QuicTransportParameterHandshakeError::Missing {
            id: QuicTransportParameterId::RETRY_SOURCE_CONNECTION_ID,
        });
    }
    Ok(())
}

pub(super) fn validate<'a>(
    bytes: &'a [u8],
    sender: QuicTransportParameterSender,
) -> Result<QuicTransportParametersV1<'a>, QuicTransportParameterSemanticError> {
    let mut offset = 0;
    let parameters = QuicTransportParameterIter::new(bytes);
    for parameter in parameters {
        let parameter =
            parameter.map_err(|error| QuicTransportParameterSemanticError::Structural { error })?;
        validate_parameter(parameter, sender, offset)?;
        offset += parameter.as_bytes().len();
    }
    Ok(QuicTransportParametersV1 { bytes, sender })
}

fn validate_parameter(
    parameter: QuicTransportParameter<'_>,
    sender: QuicTransportParameterSender,
    offset: usize,
) -> Result<(), QuicTransportParameterSemanticError> {
    let id = parameter.parameter_id();
    if sender == QuicTransportParameterSender::Client && is_server_only(id) {
        return Err(QuicTransportParameterSemanticError::ForbiddenSender { id, offset });
    }

    match id.raw() {
        0x00 | 0x0f | 0x10 => parameter
            .connection_id()
            .map(|_| ())
            .map_err(|error| value_error(id, offset, error)),
        0x01 | 0x04 | 0x05 | 0x06 | 0x07 => parameter
            .integer()
            .map(|_| ())
            .map_err(|error| value_error(id, offset, error)),
        0x02 => parameter
            .stateless_reset_token()
            .map(|_| ())
            .map_err(|error| value_error(id, offset, error)),
        0x03 => validate_minimum(parameter, offset, 1200),
        0x08 | 0x09 => validate_maximum(parameter, offset, 1u64 << 60),
        0x0a => validate_maximum(parameter, offset, 20),
        0x0b => validate_maximum(parameter, offset, (1u64 << 14) - 1),
        0x0c => parameter
            .disable_active_migration()
            .map_err(|error| value_error(id, offset, error)),
        0x0d => parameter
            .preferred_address()
            .map(|_| ())
            .map_err(|error| value_error(id, offset, error)),
        0x0e => validate_minimum(parameter, offset, 2),
        _ => Ok(()),
    }
}

fn validate_minimum(
    parameter: QuicTransportParameter<'_>,
    offset: usize,
    minimum: u64,
) -> Result<(), QuicTransportParameterSemanticError> {
    let id = parameter.parameter_id();
    let value = parameter
        .integer()
        .map_err(|error| value_error(id, offset, error))?
        .value();
    if value < minimum {
        return Err(QuicTransportParameterSemanticError::BelowMinimum {
            id,
            offset,
            value,
            minimum,
        });
    }
    Ok(())
}

fn validate_maximum(
    parameter: QuicTransportParameter<'_>,
    offset: usize,
    maximum: u64,
) -> Result<(), QuicTransportParameterSemanticError> {
    let id = parameter.parameter_id();
    let value = parameter
        .integer()
        .map_err(|error| value_error(id, offset, error))?
        .value();
    if value > maximum {
        return Err(QuicTransportParameterSemanticError::AboveMaximum {
            id,
            offset,
            value,
            maximum,
        });
    }
    Ok(())
}

fn value_error(
    id: QuicTransportParameterId,
    offset: usize,
    error: QuicTransportParameterValueError,
) -> QuicTransportParameterSemanticError {
    QuicTransportParameterSemanticError::Value { id, offset, error }
}

const fn is_server_only(id: QuicTransportParameterId) -> bool {
    matches!(id.raw(), 0x00 | 0x02 | 0x0d | 0x10)
}