Skip to main content

internet/ietf/quic/encoding/
packet_headers.rs

1//! QUIC Version-Independent Properties Encoding.
2//!
3//! Codecs for protocol elements common to all versions of QUIC following [RFC 8999].
4//!
5//! This module provides:
6//!
7//! - [`HeaderForm`]: A long or short header indicator.
8//! - [`Version`]: A four-byte protocol version identifier.
9//! - [`ConnectionId`]: An opaque endpoint identifier.
10//! - [`VersionNegotiationPacket`]: A server response listing supported versions.
11//!
12//! [RFC 8999]: https://datatracker.ietf.org/doc/html/rfc8999
13
14use crate::{Buf, BufError, BufMut, BufResult, Codec, Cursor};
15
16/// A packet header form following [Section 5].
17///
18/// Indicates whether a packet uses a long or short header.
19///
20/// [Section 5]: https://datatracker.ietf.org/doc/html/rfc8999#section-5
21#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
22#[repr(u8)]
23pub enum HeaderForm {
24    /// A short header.
25    ShortHeader = 0x00,
26    /// A long header.
27    LongHeader = 0x01,
28}
29
30impl Codec for HeaderForm {
31    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
32        let current_byte = writer.peek_u8().unwrap_or(0x00);
33        let header_bit = (*self as u8) << 7;
34        let byte = (current_byte & !0x80) | header_bit;
35        writer.poke_u8(byte)
36    }
37
38    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
39        let value = reader.peek_u8()? & 0x80;
40        match value {
41            0 => Ok(Self::ShortHeader),
42            _ => Ok(Self::LongHeader),
43        }
44    }
45}
46
47/// A protocol version following [RFC 8999, Section 5.4] and [RFC 9000, Section 15].
48///
49/// A four-byte identifier for the QUIC version in use.
50///
51/// [RFC 8999, Section 5.4]: https://datatracker.ietf.org/doc/html/rfc8999#section-5.4
52/// [RFC 9000, Section 15]: https://datatracker.ietf.org/doc/html/rfc9000#section-15
53#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
54pub struct Version(pub u32);
55
56impl Version {
57    /// Reserved for version negotiation; see [`VersionNegotiationPacket`].
58    pub const VERSION_NEGOTIATION: Self = Self(0x00000000);
59
60    /// QUIC version 1.
61    pub const QUIC_V1: Self = Self(0x00000001);
62
63    /// QUIC version 2
64    pub const QUIC_V2: Self = Self(0x6b3343cf);
65
66    /// Versions that follow the pattern `0x?a?a?a?a` are reserved for GREASE.
67    pub fn is_grease(&self) -> bool {
68        (self.0 & 0x0f0f0f0f) == 0x0a0a0a0a
69    }
70}
71
72impl Codec for Version {
73    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
74        self.0.encode(writer, ())
75    }
76
77    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
78        Ok(Self(u32::decode(reader, ())?))
79    }
80}
81
82/// A connection ID following [Section 5.3].
83///
84/// An opaque connection identifier of up to 255 bytes.
85///
86/// [Section 5.3]: https://datatracker.ietf.org/doc/html/rfc8999#section-5.3
87#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
88pub struct ConnectionId(Vec<u8>);
89
90impl ConnectionId {
91    /// Length limit is 255 bytes.
92    pub const MAXIMAL_LENGTH: u8 = u8::MAX;
93
94    /// Creates connection ID from slice.
95    pub fn from_slice(connection_id: &[u8]) -> BufResult<Self> {
96        if connection_id.length() > Self::MAXIMAL_LENGTH as usize {
97            return Err(BufError::InvalidLength);
98        }
99        Ok(Self(connection_id.to_vec()))
100    }
101
102    /// Creates connection ID from Vec.
103    pub fn from_vec(connection_id: Vec<u8>) -> BufResult<Self> {
104        if connection_id.len() > Self::MAXIMAL_LENGTH as usize {
105            return Err(BufError::InvalidLength);
106        }
107        Ok(Self(connection_id))
108    }
109
110    /// Returns length.
111    pub fn length(&self) -> u8 {
112        self.0.len() as u8
113    }
114
115    /// Returns bytes clone.
116    pub fn bytes(&self) -> Vec<u8> {
117        self.0.clone()
118    }
119}
120
121impl Codec for ConnectionId {
122    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
123        (self.0.len() as u8).encode(writer, ())?;
124        self.0.encode(writer, self.0.len())
125    }
126
127    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
128        let bytes = &mut [0u8; Self::MAXIMAL_LENGTH as usize];
129        let length = u8::decode(reader, ())?;
130        reader.read_into(&mut bytes[..length as usize])?;
131        Ok(Self::from_slice(&bytes[..length as usize])?)
132    }
133}
134
135impl Codec<u8> for ConnectionId {
136    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: u8) -> BufResult<()> {
137        writer.write_slice(&self.0[..self.length() as usize])
138    }
139
140    fn decode<R: Buf>(reader: &mut Cursor<R>, cil: u8) -> BufResult<Self> {
141        let bytes = &mut [0u8; Self::MAXIMAL_LENGTH as usize];
142        reader.read_into(&mut bytes[..cil as usize])?;
143        Ok(Self::from_slice(&bytes[..cil as usize])?)
144    }
145}
146
147/// A version negotiation packet following [Section 6].
148///
149/// Sent by a server to list the [`Version`]s it supports.
150///
151/// [Section 6]: https://datatracker.ietf.org/doc/html/rfc8999#section-6
152#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
153pub struct VersionNegotiationPacket {
154    /// The destination connection id.
155    pub destination_connection_id: ConnectionId,
156    /// The source connection id.
157    pub source_connection_id: ConnectionId,
158    /// Supported protocol versions.
159    pub supported_version: Vec<Version>,
160}
161
162impl VersionNegotiationPacket {
163    /// A [HeaderForm].
164    pub const HEADER_FORM: HeaderForm = HeaderForm::LongHeader;
165    /// A [Version] set to packet specific value.
166    pub const VERSION: Version = Version::VERSION_NEGOTIATION;
167}
168
169impl Codec for VersionNegotiationPacket {
170    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
171        Self::HEADER_FORM.encode(writer, ())?;
172        writer.advance(1)?;
173        Self::VERSION.encode(writer, ())?;
174        self.destination_connection_id.encode(writer, ())?;
175        self.source_connection_id.encode(writer, ())?;
176        self.supported_version.encode(writer, ())
177    }
178
179    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
180        if HeaderForm::decode(reader, ())? != Self::HEADER_FORM {
181            return Err(BufError::UnexpectedValue);
182        }
183        reader.advance(1)?;
184        if Version::decode(reader, ())? != Self::VERSION {
185            return Err(BufError::UnexpectedValue);
186        }
187        let destination_connection_id = ConnectionId::decode(reader, ())?;
188        let source_connection_id = ConnectionId::decode(reader, ())?;
189        let supported_version = Vec::decode(reader, ())?;
190
191        Ok(Self {
192            destination_connection_id,
193            source_connection_id,
194            supported_version,
195        })
196    }
197}
198
199#[cfg(test)]
200mod tests {
201    use core::fmt::Debug;
202
203    use crate::{
204        BufError, Codec, Cursor,
205        ietf::quic::{ConnectionId, HeaderForm, Version, VersionNegotiationPacket},
206    };
207
208    fn codec_roundtrip<T: Codec<C> + Debug + Eq, C: Copy>(
209        etalon_struct: T,
210        etalon_bytes: &[u8],
211        context: C,
212    ) {
213        let mut encoded_bytes = vec![];
214        {
215            let writer = &mut Cursor::new(&mut encoded_bytes);
216            etalon_struct.encode(writer, context).unwrap();
217        }
218        assert_eq!(etalon_bytes, &encoded_bytes);
219
220        let decoded_struct = {
221            let reader = &mut Cursor::new(&mut encoded_bytes);
222            T::decode(reader, context).unwrap()
223        };
224        assert_eq!(etalon_struct, decoded_struct);
225
226        encoded_bytes.fill(0x00);
227        {
228            let writer = &mut Cursor::new(&mut encoded_bytes);
229            decoded_struct.encode(writer, context).unwrap();
230        }
231        assert_eq!(etalon_bytes, &encoded_bytes);
232    }
233
234    #[test]
235    fn header_form() {
236        let etalon_bytes = &[0b10000000];
237        let etalon_struct = HeaderForm::LongHeader;
238        codec_roundtrip(etalon_struct, etalon_bytes, ());
239
240        let etalon_bytes = &[0b00000000];
241        let etalon_struct = HeaderForm::ShortHeader;
242        codec_roundtrip(etalon_struct, etalon_bytes, ());
243    }
244
245    #[test]
246    fn version() {
247        let etalon_bytes = &[0x00, 0x00, 0x00, 0x01];
248        let etalon_struct = Version(1);
249        codec_roundtrip(etalon_struct, etalon_bytes, ());
250        assert_eq!(etalon_struct.is_grease(), false);
251
252        let etalon_bytes = &[0x1a, 0x1a, 0x1a, 0x1a];
253        let etalon_struct = Version(0x1a1a1a1a);
254        codec_roundtrip(etalon_struct, etalon_bytes, ());
255        assert_eq!(etalon_struct.is_grease(), true);
256    }
257
258    #[test]
259    fn connection_id() {
260        assert_eq!(
261            ConnectionId::from_slice(&[0x08; 256]),
262            Err(BufError::InvalidLength)
263        );
264
265        let etalon_bytes = &[0x08; 9];
266        let etalon_struct = ConnectionId::from_slice(&[0x08; 8]).unwrap();
267        codec_roundtrip(etalon_struct, etalon_bytes, ());
268
269        let etalon_bytes = &[0x014; 21];
270        let etalon_struct = ConnectionId::from_slice(&[0x014; 20]).unwrap();
271        codec_roundtrip(etalon_struct, etalon_bytes, ());
272    }
273
274    #[test]
275    fn version_negotiation_packet() {
276        let etalon_struct = VersionNegotiationPacket {
277            destination_connection_id: ConnectionId::from_slice(&[0x82; 8]).unwrap(),
278            source_connection_id: ConnectionId::from_slice(&[0x41; 8]).unwrap(),
279            supported_version: vec![Version(0)],
280        };
281
282        let etalon_bytes: &[u8] = &[
283            0b10000000, // First byte
284            0x00, 0x00, 0x00, 0x00, // Version
285            0x08, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, // Destination Connection ID
286            0x08, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, // Source Connection ID
287            0x00, 0x00, 0x00, 0x00,
288        ];
289        codec_roundtrip(etalon_struct, etalon_bytes, ());
290    }
291}