Skip to main content

net_wire/quic/builder/
short.rs

1//! Protected QUIC v1 and v2 short-packet builder.
2
3use super::super::packet::header::{QuicConnectionId, QuicConnectionIdField, QuicShortHeader};
4use super::QuicPacketBuildError;
5
6/// Builds a complete protected QUIC short-header packet in caller-owned storage.
7///
8/// The protected low five bits and protected remainder are copied exactly without
9/// interpreting header-protection, packet-number, or payload semantics.
10pub struct QuicShortPacketBuilder<'buffer, 'input> {
11    destination: &'buffer mut [u8],
12    spin: bool,
13    protected_low_bits: u8,
14    destination_connection_id: QuicConnectionId<'input>,
15    protected_remainder: &'input [u8],
16}
17
18impl<'buffer, 'input> QuicShortPacketBuilder<'buffer, 'input> {
19    /// Creates a builder with exact opaque short-header protected bytes.
20    pub fn new(
21        destination: &'buffer mut [u8],
22        spin: bool,
23        protected_low_bits: u8,
24        destination_connection_id: QuicConnectionId<'input>,
25        protected_remainder: &'input [u8],
26    ) -> Self {
27        Self {
28            destination,
29            spin,
30            protected_low_bits,
31            destination_connection_id,
32            protected_remainder,
33        }
34    }
35
36    /// Validates all inputs and capacity before writing a complete protected short packet.
37    pub fn build(self) -> Result<QuicShortHeader<'buffer>, QuicPacketBuildError> {
38        if self.protected_low_bits > 0x1f {
39            return Err(QuicPacketBuildError::ProtectedLowBitsOutOfRange {
40                maximum: 0x1f,
41                actual: self.protected_low_bits,
42            });
43        }
44        if self.destination_connection_id.len() > 20 {
45            return Err(QuicPacketBuildError::ConnectionIdTooLong {
46                field: QuicConnectionIdField::Destination,
47                maximum: 20,
48                actual: self.destination_connection_id.len(),
49            });
50        }
51        if self.protected_remainder.len() < 2 {
52            return Err(QuicPacketBuildError::ProtectedRemainderTooShort {
53                minimum: 2,
54                actual: self.protected_remainder.len(),
55            });
56        }
57
58        let destination_end = checked_add(
59            1,
60            self.destination_connection_id.len(),
61            "destination connection ID",
62        )?;
63        let required = checked_add(
64            destination_end,
65            self.protected_remainder.len(),
66            "protected remainder",
67        )?;
68        if self.destination.len() < required {
69            return Err(QuicPacketBuildError::BufferTooShort {
70                required,
71                available: self.destination.len(),
72            });
73        }
74
75        let destination = self.destination;
76        destination[0] = 0x40 | (u8::from(self.spin) << 5) | self.protected_low_bits;
77        destination[1..destination_end].copy_from_slice(self.destination_connection_id.as_bytes());
78        destination[destination_end..required].copy_from_slice(self.protected_remainder);
79
80        let bytes = &destination[..required];
81        Ok(QuicShortHeader::from_validated(
82            bytes,
83            bytes[0],
84            QuicConnectionId::new(&bytes[1..destination_end]),
85            &bytes[destination_end..],
86        ))
87    }
88}
89
90fn checked_add(
91    offset: usize,
92    length: usize,
93    component: &'static str,
94) -> Result<usize, QuicPacketBuildError> {
95    offset
96        .checked_add(length)
97        .ok_or(QuicPacketBuildError::LengthOverflow {
98            component,
99            offset,
100            length,
101        })
102}