Skip to main content

net_wire/quic/packet/
datagram.rs

1//! Allocation-free parsing and partitioning of complete QUIC UDP datagrams.
2
3use core::iter::FusedIterator;
4
5use super::header::{
6    QuicLongHeader, QuicLongPacketType, QuicShortHeader, QuicShortHeaderContext, QuicVersion,
7};
8use super::long::QuicProtectedLongPacket;
9use super::parse::QuicPacketParseError;
10use super::terminal::{QuicRetryPacket, QuicVersionNegotiationPacket};
11
12/// A borrowed unknown-version QUIC long packet occupying the complete datagram remainder.
13///
14/// QUIC does not define a version-specific packet boundary for an unknown version. This view
15/// therefore owns the complete datagram remainder and makes no version-specific claims.
16#[derive(Clone, Copy, Debug, Eq, PartialEq)]
17pub struct QuicUnknownLongPacket<'a> {
18    header: QuicLongHeader<'a>,
19    bytes: &'a [u8],
20}
21
22impl<'a> QuicUnknownLongPacket<'a> {
23    /// Returns the exact parsed invariant long-header prefix.
24    pub const fn header(self) -> QuicLongHeader<'a> {
25        self.header
26    }
27
28    /// Returns the complete datagram remainder, including the invariant header.
29    pub const fn as_bytes(self) -> &'a [u8] {
30        self.bytes
31    }
32}
33
34/// One borrowed QUIC packet partitioned from a supplied UDP datagram.
35#[derive(Clone, Copy, Debug, Eq, PartialEq)]
36pub enum QuicPacket<'a> {
37    /// A length-delimited v1 or v2 protected Initial, 0-RTT, or Handshake packet.
38    ProtectedLong(QuicProtectedLongPacket<'a>),
39    /// A Version Negotiation packet, which consumes the datagram remainder.
40    VersionNegotiation(QuicVersionNegotiationPacket<'a>),
41    /// A Retry packet, which consumes the datagram remainder.
42    Retry(QuicRetryPacket<'a>),
43    /// A short-header packet, which consumes the datagram remainder.
44    Short(QuicShortHeader<'a>),
45    /// An unknown nonzero-version long packet occupying the complete datagram remainder.
46    ///
47    /// QUIC does not define a version-specific packet boundary for an unknown version, so
48    /// iteration stops here rather than guessing a suffix boundary. [`Self::as_bytes`] returns
49    /// the complete remaining datagram for this variant.
50    UnknownLong(QuicUnknownLongPacket<'a>),
51}
52
53impl<'a> QuicPacket<'a> {
54    /// Returns the exact complete packet bytes represented by this variant.
55    pub const fn as_bytes(self) -> &'a [u8] {
56        match self {
57            Self::ProtectedLong(packet) => packet.as_bytes(),
58            Self::VersionNegotiation(packet) => packet.as_bytes(),
59            Self::Retry(packet) => packet.as_bytes(),
60            Self::Short(packet) => packet.as_bytes(),
61            Self::UnknownLong(packet) => packet.as_bytes(),
62        }
63    }
64
65    /// Returns the raw first byte.
66    pub const fn first_byte(self) -> u8 {
67        match self {
68            Self::ProtectedLong(packet) => packet.header().first_byte(),
69            Self::VersionNegotiation(packet) => packet.header().first_byte(),
70            Self::Retry(packet) => packet.header().first_byte(),
71            Self::Short(packet) => packet.first_byte(),
72            Self::UnknownLong(packet) => packet.header().first_byte(),
73        }
74    }
75
76    /// Returns whether this packet uses the long-header form.
77    pub const fn is_long_header(self) -> bool {
78        !matches!(self, Self::Short(_))
79    }
80}
81
82/// A validated borrowed QUIC UDP datagram.
83///
84/// Construction performs a complete partitioning pass. Consequently, successful construction
85/// means every byte belongs to a structurally recognized packet or to a terminal opaque
86/// unknown-version long packet.
87#[derive(Clone, Copy, Debug, Eq, PartialEq)]
88pub struct QuicDatagram<'a> {
89    bytes: &'a [u8],
90    short_context: Option<QuicShortHeaderContext>,
91}
92
93impl<'a> QuicDatagram<'a> {
94    /// Validates and borrows one complete QUIC UDP datagram.
95    pub fn parse(
96        bytes: &'a [u8],
97        short_context: Option<QuicShortHeaderContext>,
98    ) -> Result<Self, QuicPacketParseError> {
99        if bytes.is_empty() {
100            return Err(QuicPacketParseError::EmptyDatagram);
101        }
102
103        for packet in QuicPackets::new(bytes, short_context) {
104            packet?;
105        }
106        Ok(Self {
107            bytes,
108            short_context,
109        })
110    }
111
112    /// Returns the exact supplied UDP datagram bytes.
113    pub const fn as_bytes(self) -> &'a [u8] {
114        self.bytes
115    }
116
117    /// Returns a new iterator over this datagram's validated packet partitions.
118    pub const fn packets(self) -> QuicPackets<'a> {
119        QuicPackets::new(self.bytes, self.short_context)
120    }
121}
122
123/// A fallible, fused iterator that partitions one QUIC UDP datagram.
124///
125/// On an error, this iterator permanently exhausts itself to avoid resuming after an uncertain
126/// packet boundary.
127#[derive(Clone, Debug)]
128pub struct QuicPackets<'a> {
129    remaining: &'a [u8],
130    short_context: Option<QuicShortHeaderContext>,
131    exhausted: bool,
132}
133
134impl<'a> QuicPackets<'a> {
135    /// Creates an iterator that partitions one supplied UDP datagram as it is consumed.
136    ///
137    /// Unlike [`QuicDatagram::parse`], this constructor does not validate before iteration.
138    pub const fn new(bytes: &'a [u8], short_context: Option<QuicShortHeaderContext>) -> Self {
139        Self {
140            remaining: bytes,
141            short_context,
142            exhausted: false,
143        }
144    }
145}
146
147impl<'a> Iterator for QuicPackets<'a> {
148    type Item = Result<QuicPacket<'a>, QuicPacketParseError>;
149
150    fn next(&mut self) -> Option<Self::Item> {
151        if self.exhausted || self.remaining.is_empty() {
152            self.exhausted = true;
153            return None;
154        }
155
156        let bytes = self.remaining;
157        let result = if bytes[0] & 0x80 == 0 {
158            let context = match self.short_context {
159                Some(context) => context,
160                None => return self.fail(QuicPacketParseError::ShortHeaderContextRequired),
161            };
162            QuicShortHeader::parse(bytes, context).map(QuicPacket::Short)
163        } else {
164            match QuicLongHeader::parse(bytes) {
165                Err(error) => Err(error),
166                Ok(header) if header.version() == QuicVersion::NEGOTIATION => {
167                    QuicVersionNegotiationPacket::parse(bytes).map(QuicPacket::VersionNegotiation)
168                }
169                Ok(header) => match header.long_packet_type() {
170                    Some(QuicLongPacketType::Retry) => {
171                        QuicRetryPacket::parse(bytes).map(QuicPacket::Retry)
172                    }
173                    Some(_) => QuicProtectedLongPacket::parse(bytes).map(|(packet, suffix)| {
174                        self.remaining = suffix;
175                        QuicPacket::ProtectedLong(packet)
176                    }),
177                    None => Ok(QuicPacket::UnknownLong(QuicUnknownLongPacket {
178                        header,
179                        bytes,
180                    })),
181                },
182            }
183        };
184
185        match result {
186            Ok(
187                packet @ (QuicPacket::Short(_)
188                | QuicPacket::VersionNegotiation(_)
189                | QuicPacket::Retry(_)
190                | QuicPacket::UnknownLong(_)),
191            ) => {
192                self.remaining = &[];
193                self.exhausted = true;
194                Some(Ok(packet))
195            }
196            Ok(packet) => Some(Ok(packet)),
197            Err(error) => self.fail(error),
198        }
199    }
200}
201
202impl FusedIterator for QuicPackets<'_> {}
203
204impl<'a> QuicPackets<'a> {
205    fn fail(
206        &mut self,
207        error: QuicPacketParseError,
208    ) -> Option<Result<QuicPacket<'a>, QuicPacketParseError>> {
209        self.remaining = &[];
210        self.exhausted = true;
211        Some(Err(error))
212    }
213}