Skip to main content

kvarn_quinn_proto/
lib.rs

1//! Low-level protocol logic for the QUIC protoocol
2//!
3//! quinn-proto contains a fully deterministic implementation of QUIC protocol logic. It contains
4//! no networking code and does not get any relevant timestamps from the operating system. Most
5//! users may want to use the futures-based quinn API instead.
6//!
7//! The quinn-proto API might be of interest if you want to use it from a C or C++ project
8//! through C bindings or if you want to use a different event loop than the one tokio provides.
9//!
10//! The most important types are `Endpoint`, which conceptually represents the protocol state for
11//! a single socket and mostly manages configuration and dispatches incoming datagrams to the
12//! related `Connection`. `Connection` types contain the bulk of the protocol logic related to
13//! managing a single connection and all the related state (such as streams).
14
15#![cfg_attr(not(fuzzing), warn(missing_docs))]
16#![cfg_attr(test, allow(dead_code))]
17// Fixes welcome:
18#![warn(unreachable_pub)]
19#![allow(clippy::cognitive_complexity)]
20#![allow(clippy::too_many_arguments)]
21#![warn(clippy::use_self)]
22
23use std::{
24    fmt,
25    net::{IpAddr, SocketAddr},
26    ops,
27    time::Duration,
28};
29
30mod cid_queue;
31#[doc(hidden)]
32pub mod coding;
33mod constant_time;
34mod packet;
35mod range_set;
36#[cfg(all(test, feature = "rustls"))]
37mod tests;
38pub mod transport_parameters;
39mod varint;
40
41pub use varint::{VarInt, VarIntBoundsExceeded};
42
43mod connection;
44pub use crate::connection::{
45    BytesSource, Chunk, Chunks, Connection, ConnectionError, ConnectionStats, Datagrams, Event,
46    FinishError, FrameStats, PathStats, ReadError, ReadableError, RecvStream, RttEstimator,
47    SendDatagramError, SendStream, StreamEvent, Streams, UdpStats, UnknownStream, WriteError,
48    Written,
49};
50
51mod config;
52pub use config::{
53    AckFrequencyConfig, ClientConfig, ConfigError, EndpointConfig, IdleTimeout, MtuDiscoveryConfig,
54    ServerConfig, TransportConfig,
55};
56
57pub mod crypto;
58
59mod frame;
60use crate::frame::Frame;
61pub use crate::frame::{ApplicationClose, ConnectionClose, Datagram};
62
63mod endpoint;
64pub use crate::endpoint::{ConnectError, ConnectionHandle, DatagramEvent, Endpoint};
65
66mod shared;
67pub use crate::shared::{ConnectionEvent, ConnectionId, EcnCodepoint, EndpointEvent};
68
69mod transport_error;
70pub use crate::transport_error::{Code as TransportErrorCode, Error as TransportError};
71
72pub mod congestion;
73
74mod cid_generator;
75pub use crate::cid_generator::{ConnectionIdGenerator, RandomConnectionIdGenerator};
76
77mod token;
78use token::{ResetToken, RetryToken};
79
80#[cfg(feature = "arbitrary")]
81use arbitrary::Arbitrary;
82
83#[doc(hidden)]
84#[cfg(fuzzing)]
85pub mod fuzzing {
86    pub use crate::connection::{Retransmits, State as ConnectionState, StreamsState};
87    pub use crate::frame::ResetStream;
88    pub use crate::packet::PartialDecode;
89    pub use crate::transport_parameters::TransportParameters;
90    pub use bytes::{BufMut, BytesMut};
91
92    #[cfg(feature = "arbitrary")]
93    use arbitrary::{Arbitrary, Result, Unstructured};
94
95    #[cfg(feature = "arbitrary")]
96    impl<'arbitrary> Arbitrary<'arbitrary> for TransportParameters {
97        fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Self> {
98            Ok(Self {
99                initial_max_streams_bidi: u.arbitrary()?,
100                initial_max_streams_uni: u.arbitrary()?,
101                ack_delay_exponent: u.arbitrary()?,
102                max_udp_payload_size: u.arbitrary()?,
103                ..Self::default()
104            })
105        }
106    }
107
108    #[derive(Debug)]
109    pub struct PacketParams {
110        pub local_cid_len: usize,
111        pub buf: BytesMut,
112        pub grease_quic_bit: bool,
113    }
114
115    #[cfg(feature = "arbitrary")]
116    impl<'arbitrary> Arbitrary<'arbitrary> for PacketParams {
117        fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Self> {
118            let local_cid_len: usize = u.int_in_range(0..=crate::MAX_CID_SIZE)?;
119            let bytes: Vec<u8> = Vec::arbitrary(u)?;
120            let mut buf = BytesMut::new();
121            buf.put_slice(&bytes[..]);
122            Ok(Self {
123                local_cid_len,
124                buf,
125                grease_quic_bit: bool::arbitrary(u)?,
126            })
127        }
128    }
129}
130
131/// The QUIC protocol version implemented.
132pub const DEFAULT_SUPPORTED_VERSIONS: &[u32] = &[
133    0x00000001,
134    0xff00_001d,
135    0xff00_001e,
136    0xff00_001f,
137    0xff00_0020,
138    0xff00_0021,
139    0xff00_0022,
140];
141
142/// Whether an endpoint was the initiator of a connection
143#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
144#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
145pub enum Side {
146    /// The initiator of a connection
147    Client = 0,
148    /// The acceptor of a connection
149    Server = 1,
150}
151
152impl Side {
153    #[inline]
154    /// Shorthand for `self == Side::Client`
155    pub fn is_client(self) -> bool {
156        self == Self::Client
157    }
158
159    #[inline]
160    /// Shorthand for `self == Side::Server`
161    pub fn is_server(self) -> bool {
162        self == Self::Server
163    }
164}
165
166impl ops::Not for Side {
167    type Output = Self;
168    fn not(self) -> Self {
169        match self {
170            Self::Client => Self::Server,
171            Self::Server => Self::Client,
172        }
173    }
174}
175
176/// Whether a stream communicates data in both directions or only from the initiator
177#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
178#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
179pub enum Dir {
180    /// Data flows in both directions
181    Bi = 0,
182    /// Data flows only from the stream's initiator
183    Uni = 1,
184}
185
186impl Dir {
187    fn iter() -> impl Iterator<Item = Self> {
188        [Self::Bi, Self::Uni].iter().cloned()
189    }
190}
191
192impl fmt::Display for Dir {
193    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194        use self::Dir::*;
195        f.pad(match *self {
196            Bi => "bidirectional",
197            Uni => "unidirectional",
198        })
199    }
200}
201
202/// Identifier for a stream within a particular connection
203#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
204#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
205pub struct StreamId(#[doc(hidden)] pub u64);
206
207impl fmt::Display for StreamId {
208    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209        let initiator = match self.initiator() {
210            Side::Client => "client",
211            Side::Server => "server",
212        };
213        let dir = match self.dir() {
214            Dir::Uni => "uni",
215            Dir::Bi => "bi",
216        };
217        write!(
218            f,
219            "{} {}directional stream {}",
220            initiator,
221            dir,
222            self.index()
223        )
224    }
225}
226
227impl StreamId {
228    /// Create a new StreamId
229    pub fn new(initiator: Side, dir: Dir, index: u64) -> Self {
230        Self(index << 2 | (dir as u64) << 1 | initiator as u64)
231    }
232    /// Which side of a connection initiated the stream
233    pub fn initiator(self) -> Side {
234        if self.0 & 0x1 == 0 {
235            Side::Client
236        } else {
237            Side::Server
238        }
239    }
240    /// Which directions data flows in
241    pub fn dir(self) -> Dir {
242        if self.0 & 0x2 == 0 {
243            Dir::Bi
244        } else {
245            Dir::Uni
246        }
247    }
248    /// Distinguishes streams of the same initiator and directionality
249    pub fn index(self) -> u64 {
250        self.0 >> 2
251    }
252}
253
254impl From<StreamId> for VarInt {
255    fn from(x: StreamId) -> Self {
256        unsafe { Self::from_u64_unchecked(x.0) }
257    }
258}
259
260impl From<VarInt> for StreamId {
261    fn from(v: VarInt) -> Self {
262        Self(v.0)
263    }
264}
265
266impl coding::Codec for StreamId {
267    fn decode<B: bytes::Buf>(buf: &mut B) -> coding::Result<Self> {
268        VarInt::decode(buf).map(|x| Self(x.into_inner()))
269    }
270    fn encode<B: bytes::BufMut>(&self, buf: &mut B) {
271        VarInt::from_u64(self.0).unwrap().encode(buf);
272    }
273}
274
275/// An outgoing packet
276#[derive(Debug)]
277#[must_use]
278pub struct Transmit {
279    /// The socket this datagram should be sent to
280    pub destination: SocketAddr,
281    /// Explicit congestion notification bits to set on the packet
282    pub ecn: Option<EcnCodepoint>,
283    /// Amount of data written to the caller-supplied buffer
284    pub size: usize,
285    /// The segment size if this transmission contains multiple datagrams.
286    /// This is `None` if the transmit only contains a single datagram
287    pub segment_size: Option<usize>,
288    /// Optional source IP address for the datagram
289    pub src_ip: Option<IpAddr>,
290}
291
292//
293// Useful internal constants
294//
295
296/// The maximum number of CIDs we bother to issue per connection
297const LOC_CID_COUNT: u64 = 8;
298const RESET_TOKEN_SIZE: usize = 16;
299const MAX_CID_SIZE: usize = 20;
300const MIN_INITIAL_SIZE: u16 = 1200;
301/// <https://www.rfc-editor.org/rfc/rfc9000.html#name-datagram-size>
302const INITIAL_MTU: u16 = 1200;
303const MAX_UDP_PAYLOAD: u16 = 65527;
304const TIMER_GRANULARITY: Duration = Duration::from_millis(1);
305/// Maximum number of streams that can be uniquely identified by a stream ID
306const MAX_STREAM_COUNT: u64 = 1 << 60;