Skip to main content

net_wire/quic/builder/
long.rs

1//! Protected QUIC v1 and v2 length-delimited long-packet builders.
2
3use super::super::packet::header::{
4    QuicConnectionId, QuicConnectionIdField, QuicLongHeader, QuicLongPacketType, QuicVersion,
5};
6use super::super::packet::long::QuicProtectedLongPacket;
7use super::super::varint::{QuicVarInt, QuicVarIntBuildError, QuicVarIntLen};
8use super::{QuicPacketBuildError, QuicPacketBuildField};
9
10/// Builds a protected QUIC v1 or v2 Initial packet in caller-owned storage.
11pub struct QuicInitialPacketBuilder<'buffer, 'input> {
12    destination: &'buffer mut [u8],
13    version: QuicVersion,
14    protected_low_bits: u8,
15    destination_connection_id: QuicConnectionId<'input>,
16    source_connection_id: QuicConnectionId<'input>,
17    token: &'input [u8],
18    protected_remainder: &'input [u8],
19    token_length_len: Option<QuicVarIntLen>,
20    length_len: Option<QuicVarIntLen>,
21}
22
23impl<'buffer, 'input> QuicInitialPacketBuilder<'buffer, 'input> {
24    /// Creates a builder with exact opaque Initial token and protected-remainder bytes.
25    pub fn new(
26        destination: &'buffer mut [u8],
27        version: QuicVersion,
28        protected_low_bits: u8,
29        destination_connection_id: QuicConnectionId<'input>,
30        source_connection_id: QuicConnectionId<'input>,
31        token: &'input [u8],
32        protected_remainder: &'input [u8],
33    ) -> Self {
34        Self {
35            destination,
36            version,
37            protected_low_bits,
38            destination_connection_id,
39            source_connection_id,
40            token,
41            protected_remainder,
42            token_length_len: None,
43            length_len: None,
44        }
45    }
46
47    /// Requests an explicit legal encoded width for the Initial token-length field.
48    pub fn with_token_length_len(mut self, length: QuicVarIntLen) -> Self {
49        self.token_length_len = Some(length);
50        self
51    }
52
53    /// Requests an explicit legal encoded width for the protected packet-length field.
54    pub fn with_length_len(mut self, length: QuicVarIntLen) -> Self {
55        self.length_len = Some(length);
56        self
57    }
58
59    /// Validates all inputs and capacity before writing a complete protected Initial packet.
60    pub fn build(self) -> Result<QuicProtectedLongPacket<'buffer>, QuicPacketBuildError> {
61        build_long(BuildInput {
62            destination: self.destination,
63            version: self.version,
64            protected_low_bits: self.protected_low_bits,
65            destination_connection_id: self.destination_connection_id,
66            source_connection_id: self.source_connection_id,
67            packet_type: QuicLongPacketType::Initial,
68            token: Some(self.token),
69            token_length_len: self.token_length_len,
70            length_len: self.length_len,
71            protected_remainder: self.protected_remainder,
72        })
73    }
74}
75
76/// Builds a protected QUIC v1 or v2 0-RTT packet in caller-owned storage.
77pub struct QuicZeroRttPacketBuilder<'buffer, 'input> {
78    destination: &'buffer mut [u8],
79    version: QuicVersion,
80    protected_low_bits: u8,
81    destination_connection_id: QuicConnectionId<'input>,
82    source_connection_id: QuicConnectionId<'input>,
83    protected_remainder: &'input [u8],
84    length_len: Option<QuicVarIntLen>,
85}
86
87impl<'buffer, 'input> QuicZeroRttPacketBuilder<'buffer, 'input> {
88    /// Creates a builder with exact opaque protected-remainder bytes.
89    pub fn new(
90        destination: &'buffer mut [u8],
91        version: QuicVersion,
92        protected_low_bits: u8,
93        destination_connection_id: QuicConnectionId<'input>,
94        source_connection_id: QuicConnectionId<'input>,
95        protected_remainder: &'input [u8],
96    ) -> Self {
97        Self {
98            destination,
99            version,
100            protected_low_bits,
101            destination_connection_id,
102            source_connection_id,
103            protected_remainder,
104            length_len: None,
105        }
106    }
107
108    /// Requests an explicit legal encoded width for the protected packet-length field.
109    pub fn with_length_len(mut self, length: QuicVarIntLen) -> Self {
110        self.length_len = Some(length);
111        self
112    }
113
114    /// Validates all inputs and capacity before writing a complete protected 0-RTT packet.
115    pub fn build(self) -> Result<QuicProtectedLongPacket<'buffer>, QuicPacketBuildError> {
116        build_long(BuildInput {
117            destination: self.destination,
118            version: self.version,
119            protected_low_bits: self.protected_low_bits,
120            destination_connection_id: self.destination_connection_id,
121            source_connection_id: self.source_connection_id,
122            packet_type: QuicLongPacketType::ZeroRtt,
123            token: None,
124            token_length_len: None,
125            length_len: self.length_len,
126            protected_remainder: self.protected_remainder,
127        })
128    }
129}
130
131/// Builds a protected QUIC v1 or v2 Handshake packet in caller-owned storage.
132pub struct QuicHandshakePacketBuilder<'buffer, 'input> {
133    destination: &'buffer mut [u8],
134    version: QuicVersion,
135    protected_low_bits: u8,
136    destination_connection_id: QuicConnectionId<'input>,
137    source_connection_id: QuicConnectionId<'input>,
138    protected_remainder: &'input [u8],
139    length_len: Option<QuicVarIntLen>,
140}
141
142impl<'buffer, 'input> QuicHandshakePacketBuilder<'buffer, 'input> {
143    /// Creates a builder with exact opaque protected-remainder bytes.
144    pub fn new(
145        destination: &'buffer mut [u8],
146        version: QuicVersion,
147        protected_low_bits: u8,
148        destination_connection_id: QuicConnectionId<'input>,
149        source_connection_id: QuicConnectionId<'input>,
150        protected_remainder: &'input [u8],
151    ) -> Self {
152        Self {
153            destination,
154            version,
155            protected_low_bits,
156            destination_connection_id,
157            source_connection_id,
158            protected_remainder,
159            length_len: None,
160        }
161    }
162
163    /// Requests an explicit legal encoded width for the protected packet-length field.
164    pub fn with_length_len(mut self, length: QuicVarIntLen) -> Self {
165        self.length_len = Some(length);
166        self
167    }
168
169    /// Validates all inputs and capacity before writing a complete protected Handshake packet.
170    pub fn build(self) -> Result<QuicProtectedLongPacket<'buffer>, QuicPacketBuildError> {
171        build_long(BuildInput {
172            destination: self.destination,
173            version: self.version,
174            protected_low_bits: self.protected_low_bits,
175            destination_connection_id: self.destination_connection_id,
176            source_connection_id: self.source_connection_id,
177            packet_type: QuicLongPacketType::Handshake,
178            token: None,
179            token_length_len: None,
180            length_len: self.length_len,
181            protected_remainder: self.protected_remainder,
182        })
183    }
184}
185
186struct BuildInput<'buffer, 'input> {
187    destination: &'buffer mut [u8],
188    version: QuicVersion,
189    protected_low_bits: u8,
190    destination_connection_id: QuicConnectionId<'input>,
191    source_connection_id: QuicConnectionId<'input>,
192    packet_type: QuicLongPacketType,
193    token: Option<&'input [u8]>,
194    token_length_len: Option<QuicVarIntLen>,
195    length_len: Option<QuicVarIntLen>,
196    protected_remainder: &'input [u8],
197}
198
199fn build_long<'buffer>(
200    input: BuildInput<'buffer, '_>,
201) -> Result<QuicProtectedLongPacket<'buffer>, QuicPacketBuildError> {
202    let raw_type = input.packet_type.raw_type(input.version).ok_or(
203        QuicPacketBuildError::UnsupportedVersion {
204            version: input.version,
205        },
206    )?;
207    if input.protected_low_bits > 0x0f {
208        return Err(QuicPacketBuildError::ProtectedLowBitsOutOfRange {
209            maximum: 0x0f,
210            actual: input.protected_low_bits,
211        });
212    }
213    validate_connection_id(
214        QuicConnectionIdField::Destination,
215        input.destination_connection_id.len(),
216    )?;
217    validate_connection_id(
218        QuicConnectionIdField::Source,
219        input.source_connection_id.len(),
220    )?;
221    if input.protected_remainder.len() < 2 {
222        return Err(QuicPacketBuildError::ProtectedRemainderTooShort {
223            minimum: 2,
224            actual: input.protected_remainder.len(),
225        });
226    }
227
228    let protected_length = u64::try_from(input.protected_remainder.len()).map_err(|_| {
229        QuicPacketBuildError::VarInt {
230            field: QuicPacketBuildField::ProtectedLength,
231            error: QuicVarIntBuildError::ValueTooLarge { value: u64::MAX },
232        }
233    })?;
234    let length_len = validate_varint(
235        QuicPacketBuildField::ProtectedLength,
236        protected_length,
237        input.length_len,
238    )?;
239    let token_length = input.token.map(|token| token.len());
240    let token_length_value = match token_length {
241        Some(length) => u64::try_from(length).map_err(|_| QuicPacketBuildError::VarInt {
242            field: QuicPacketBuildField::TokenLength,
243            error: QuicVarIntBuildError::ValueTooLarge { value: u64::MAX },
244        })?,
245        None => 0,
246    };
247    let token_length_len = match token_length {
248        Some(_) => Some(validate_varint(
249            QuicPacketBuildField::TokenLength,
250            token_length_value,
251            input.token_length_len,
252        )?),
253        None => None,
254    };
255
256    let destination_start = 6;
257    let destination_end = checked_add(
258        destination_start,
259        input.destination_connection_id.len(),
260        "destination connection ID",
261    )?;
262    let source_length_offset = destination_end;
263    let source_start = checked_add(source_length_offset, 1, "source connection ID length")?;
264    let source_end = checked_add(
265        source_start,
266        input.source_connection_id.len(),
267        "source connection ID",
268    )?;
269    let token_length_offset = source_end;
270    let token_start = match (input.token, token_length_len) {
271        (Some(_), Some(length)) => {
272            checked_add(token_length_offset, length.byte_len(), "token length")?
273        }
274        (None, None) => source_end,
275        _ => {
276            return Err(QuicPacketBuildError::LengthOverflow {
277                component: "token fields",
278                offset: source_end,
279                length: 0,
280            });
281        }
282    };
283    let token_end = match input.token {
284        Some(token) => checked_add(token_start, token.len(), "Initial token")?,
285        None => token_start,
286    };
287    let length_offset = token_end;
288    let packet_number_offset =
289        checked_add(length_offset, length_len.byte_len(), "protected length")?;
290    let required = checked_add(
291        packet_number_offset,
292        input.protected_remainder.len(),
293        "protected remainder",
294    )?;
295    if input.destination.len() < required {
296        return Err(QuicPacketBuildError::BufferTooShort {
297            required,
298            available: input.destination.len(),
299        });
300    }
301
302    let destination = input.destination;
303    destination[0] = 0xc0 | (raw_type << 4) | input.protected_low_bits;
304    destination[1..5].copy_from_slice(&input.version.raw().to_be_bytes());
305    destination[5] = input.destination_connection_id.len() as u8;
306    destination[destination_start..destination_end]
307        .copy_from_slice(input.destination_connection_id.as_bytes());
308    destination[source_length_offset] = input.source_connection_id.len() as u8;
309    destination[source_start..source_end].copy_from_slice(input.source_connection_id.as_bytes());
310    if let (Some(token), Some(token_length_len)) = (input.token, token_length_len) {
311        write_varint(
312            &mut destination[token_length_offset..token_start],
313            token_length_value,
314            token_length_len,
315        );
316        destination[token_start..token_end].copy_from_slice(token);
317    }
318    write_varint(
319        &mut destination[length_offset..packet_number_offset],
320        protected_length,
321        length_len,
322    );
323    destination[packet_number_offset..required].copy_from_slice(input.protected_remainder);
324
325    let bytes = &destination[..required];
326    let header = QuicLongHeader::from_validated(
327        &bytes[..source_end],
328        bytes[0],
329        input.version,
330        QuicConnectionId::new(&bytes[destination_start..destination_end]),
331        QuicConnectionId::new(&bytes[source_start..source_end]),
332        &bytes[source_end..],
333    );
334    let token_length = token_length_len.map(|length| {
335        QuicVarInt::from_validated(
336            &bytes[token_length_offset..token_start],
337            token_length_value,
338            length,
339        )
340    });
341    let token_fields =
342        token_length.map(|token_length| (token_length, &bytes[token_start..token_end]));
343    let length = QuicVarInt::from_validated(
344        &bytes[length_offset..packet_number_offset],
345        protected_length,
346        length_len,
347    );
348    Ok(QuicProtectedLongPacket::from_validated(
349        bytes,
350        header,
351        input.packet_type,
352        token_fields,
353        length,
354        packet_number_offset,
355        &bytes[packet_number_offset..required],
356    ))
357}
358
359fn validate_connection_id(
360    field: QuicConnectionIdField,
361    actual: usize,
362) -> Result<(), QuicPacketBuildError> {
363    if actual > 20 {
364        return Err(QuicPacketBuildError::ConnectionIdTooLong {
365            field,
366            maximum: 20,
367            actual,
368        });
369    }
370    Ok(())
371}
372
373fn validate_varint(
374    field: QuicPacketBuildField,
375    value: u64,
376    requested: Option<QuicVarIntLen>,
377) -> Result<QuicVarIntLen, QuicPacketBuildError> {
378    if value > QuicVarIntLen::Eight.max_value() {
379        return Err(QuicPacketBuildError::VarInt {
380            field,
381            error: QuicVarIntBuildError::ValueTooLarge { value },
382        });
383    }
384    let length = requested.unwrap_or_else(|| canonical_len(value));
385    if value > length.max_value() {
386        return Err(QuicPacketBuildError::VarInt {
387            field,
388            error: QuicVarIntBuildError::WidthTooSmall { length, value },
389        });
390    }
391    Ok(length)
392}
393
394const fn canonical_len(value: u64) -> QuicVarIntLen {
395    if value <= QuicVarIntLen::One.max_value() {
396        QuicVarIntLen::One
397    } else if value <= QuicVarIntLen::Two.max_value() {
398        QuicVarIntLen::Two
399    } else if value <= QuicVarIntLen::Four.max_value() {
400        QuicVarIntLen::Four
401    } else {
402        QuicVarIntLen::Eight
403    }
404}
405
406fn checked_add(
407    offset: usize,
408    length: usize,
409    component: &'static str,
410) -> Result<usize, QuicPacketBuildError> {
411    offset
412        .checked_add(length)
413        .ok_or(QuicPacketBuildError::LengthOverflow {
414            component,
415            offset,
416            length,
417        })
418}
419
420fn write_varint(destination: &mut [u8], value: u64, length: QuicVarIntLen) {
421    let value_bytes = value.to_be_bytes();
422    let length_bytes = length.byte_len();
423    destination.copy_from_slice(&value_bytes[value_bytes.len() - length_bytes..]);
424    destination[0] |= match length {
425        QuicVarIntLen::One => 0,
426        QuicVarIntLen::Two => 0x40,
427        QuicVarIntLen::Four => 0x80,
428        QuicVarIntLen::Eight => 0xc0,
429    };
430}