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};
28
29mod cid_queue;
30pub mod coding;
31mod constant_time;
32mod range_set;
33#[cfg(all(test, any(feature = "rustls-aws-lc-rs", feature = "rustls-ring")))]
34mod tests;
35pub mod transport_parameters;
36mod varint;
37
38pub use varint::{VarInt, VarIntBoundsExceeded};
39
40#[cfg(feature = "bloom")]
41mod bloom_token_log;
42#[cfg(feature = "bloom")]
43pub use bloom_token_log::BloomTokenLog;
44
45mod connection;
46pub use crate::connection::{
47    Chunk, Chunks, ClosedStream, Connection, ConnectionError, ConnectionStats, Datagrams, Event,
48    FinishError, FrameStats, PathStats, ReadError, ReadableError, RecvStream, RttEstimator,
49    SendDatagramError, SendStream, ShouldTransmit, StreamEvent, Streams, UdpStats, WriteError,
50    Written,
51};
52#[cfg(feature = "qlog")]
53pub use connection::qlog::QlogStream;
54
55#[cfg(feature = "rustls")]
56pub use rustls;
57
58mod config;
59#[cfg(feature = "qlog")]
60pub use config::QlogConfig;
61pub use config::{
62    AckFrequencyConfig, ClientConfig, ConfigError, EndpointConfig, IdleTimeout, MtuDiscoveryConfig,
63    ServerConfig, StdSystemTime, TimeSource, TransportConfig, ValidationTokenConfig,
64};
65
66pub mod crypto;
67
68mod frame;
69use crate::frame::Frame;
70pub use crate::frame::{ApplicationClose, ConnectionClose, Datagram, FrameType};
71
72mod endpoint;
73pub use crate::endpoint::{
74    AcceptError, ConnectError, ConnectionHandle, DatagramEvent, Endpoint, Incoming, RetryError,
75};
76
77mod packet;
78pub use packet::{
79    ConnectionIdParser, FixedLengthConnectionIdParser, LongType, PacketDecodeError, PartialDecode,
80    ProtectedHeader, ProtectedInitialHeader,
81};
82
83mod shared;
84pub use crate::shared::{ConnectionEvent, ConnectionId, EcnCodepoint, EndpointEvent};
85
86mod transport_error;
87pub use crate::transport_error::{Code as TransportErrorCode, Error as TransportError};
88
89pub mod congestion;
90
91mod cid_generator;
92pub use crate::cid_generator::{
93    ConnectionIdGenerator, HashedConnectionIdGenerator, InvalidCid, RandomConnectionIdGenerator,
94};
95
96mod token;
97use token::ResetToken;
98pub use token::{NoneTokenLog, NoneTokenStore, TokenLog, TokenReuseError, TokenStore};
99
100mod token_memory_cache;
101pub use token_memory_cache::TokenMemoryCache;
102
103#[cfg(feature = "arbitrary")]
104use arbitrary::Arbitrary;
105
106// Deal with time
107#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
108pub(crate) use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
109#[cfg(all(target_family = "wasm", target_os = "unknown"))]
110pub(crate) use web_time::{Duration, Instant, SystemTime, UNIX_EPOCH};
111
112#[cfg(fuzzing)]
113pub mod fuzzing {
114    pub use crate::connection::{Retransmits, State as ConnectionState, StreamsState};
115    pub use crate::frame::ResetStream;
116    pub use crate::packet::PartialDecode;
117    pub use crate::transport_parameters::TransportParameters;
118    pub use bytes::{BufMut, BytesMut};
119
120    #[cfg(feature = "arbitrary")]
121    use arbitrary::{Arbitrary, Result, Unstructured};
122
123    #[cfg(feature = "arbitrary")]
124    impl<'arbitrary> Arbitrary<'arbitrary> for TransportParameters {
125        fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Self> {
126            Ok(Self {
127                initial_max_streams_bidi: u.arbitrary()?,
128                initial_max_streams_uni: u.arbitrary()?,
129                ack_delay_exponent: u.arbitrary()?,
130                max_udp_payload_size: u.arbitrary()?,
131                ..Self::default()
132            })
133        }
134    }
135
136    #[derive(Debug)]
137    pub struct PacketParams {
138        pub local_cid_len: usize,
139        pub buf: BytesMut,
140        pub grease_quic_bit: bool,
141    }
142
143    #[cfg(feature = "arbitrary")]
144    impl<'arbitrary> Arbitrary<'arbitrary> for PacketParams {
145        fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Self> {
146            let local_cid_len: usize = u.int_in_range(0..=crate::MAX_CID_SIZE)?;
147            let bytes: Vec<u8> = Vec::arbitrary(u)?;
148            let mut buf = BytesMut::new();
149            buf.put_slice(&bytes[..]);
150            Ok(Self {
151                local_cid_len,
152                buf,
153                grease_quic_bit: bool::arbitrary(u)?,
154            })
155        }
156    }
157}
158
159/// The QUIC protocol version implemented.
160pub const DEFAULT_SUPPORTED_VERSIONS: &[u32] = &[
161    0x00000001,
162    0xff00_001d,
163    0xff00_001e,
164    0xff00_001f,
165    0xff00_0020,
166    0xff00_0021,
167    0xff00_0022,
168];
169
170/// Whether an endpoint was the initiator of a connection
171#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
172#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
173pub enum Side {
174    /// The initiator of a connection
175    Client = 0,
176    /// The acceptor of a connection
177    Server = 1,
178}
179
180impl Side {
181    #[inline]
182    /// Shorthand for `self == Side::Client`
183    pub fn is_client(self) -> bool {
184        self == Self::Client
185    }
186
187    #[inline]
188    /// Shorthand for `self == Side::Server`
189    pub fn is_server(self) -> bool {
190        self == Self::Server
191    }
192}
193
194impl ops::Not for Side {
195    type Output = Self;
196    fn not(self) -> Self {
197        match self {
198            Self::Client => Self::Server,
199            Self::Server => Self::Client,
200        }
201    }
202}
203
204/// Whether a stream communicates data in both directions or only from the initiator
205#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
206#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
207pub enum Dir {
208    /// Data flows in both directions
209    Bi = 0,
210    /// Data flows only from the stream's initiator
211    Uni = 1,
212}
213
214impl Dir {
215    fn iter() -> impl Iterator<Item = Self> {
216        [Self::Bi, Self::Uni].iter().cloned()
217    }
218}
219
220impl fmt::Display for Dir {
221    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222        use Dir::*;
223        f.pad(match *self {
224            Bi => "bidirectional",
225            Uni => "unidirectional",
226        })
227    }
228}
229
230/// Identifier for a stream within a particular connection
231#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
232#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
233pub struct StreamId(u64);
234
235impl fmt::Display for StreamId {
236    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237        let initiator = match self.initiator() {
238            Side::Client => "client",
239            Side::Server => "server",
240        };
241        let dir = match self.dir() {
242            Dir::Uni => "uni",
243            Dir::Bi => "bi",
244        };
245        write!(
246            f,
247            "{} {}directional stream {}",
248            initiator,
249            dir,
250            self.index()
251        )
252    }
253}
254
255impl StreamId {
256    /// Create a new StreamId
257    pub fn new(initiator: Side, dir: Dir, index: u64) -> Self {
258        Self((index << 2) | ((dir as u64) << 1) | initiator as u64)
259    }
260    /// Which side of a connection initiated the stream
261    pub fn initiator(self) -> Side {
262        if self.0 & 0x1 == 0 {
263            Side::Client
264        } else {
265            Side::Server
266        }
267    }
268    /// Which directions data flows in
269    pub fn dir(self) -> Dir {
270        if self.0 & 0x2 == 0 { Dir::Bi } else { Dir::Uni }
271    }
272    /// Distinguishes streams of the same initiator and directionality
273    pub fn index(self) -> u64 {
274        self.0 >> 2
275    }
276}
277
278impl From<StreamId> for VarInt {
279    fn from(x: StreamId) -> Self {
280        unsafe { Self::from_u64_unchecked(x.0) }
281    }
282}
283
284impl From<VarInt> for StreamId {
285    fn from(v: VarInt) -> Self {
286        Self(v.0)
287    }
288}
289
290impl From<StreamId> for u64 {
291    fn from(x: StreamId) -> Self {
292        x.0
293    }
294}
295
296impl coding::Codec for StreamId {
297    fn decode<B: bytes::Buf>(buf: &mut B) -> coding::Result<Self> {
298        VarInt::decode(buf).map(|x| Self(x.into_inner()))
299    }
300    fn encode<B: bytes::BufMut>(&self, buf: &mut B) {
301        VarInt::from_u64(self.0).unwrap().encode(buf);
302    }
303}
304
305/// An outgoing packet
306#[derive(Debug)]
307#[must_use]
308pub struct Transmit {
309    /// The socket this datagram should be sent to
310    pub destination: SocketAddr,
311    /// Explicit congestion notification bits to set on the packet
312    pub ecn: Option<EcnCodepoint>,
313    /// Amount of data written to the caller-supplied buffer
314    pub size: usize,
315    /// The segment size if this transmission contains multiple datagrams.
316    /// This is `None` if the transmit only contains a single datagram
317    pub segment_size: Option<usize>,
318    /// Optional source IP address for the datagram
319    pub src_ip: Option<IpAddr>,
320}
321
322//
323// Useful internal constants
324//
325
326/// The maximum number of CIDs we bother to issue per connection
327const LOC_CID_COUNT: u64 = 8;
328const RESET_TOKEN_SIZE: usize = 16;
329const MAX_CID_SIZE: usize = 20;
330const MIN_INITIAL_SIZE: u16 = 1200;
331/// <https://www.rfc-editor.org/rfc/rfc9000.html#name-datagram-size>
332const INITIAL_MTU: u16 = 1200;
333const MAX_UDP_PAYLOAD: u16 = 65527;
334const TIMER_GRANULARITY: Duration = Duration::from_millis(1);
335/// Maximum number of streams that can be uniquely identified by a stream ID
336const MAX_STREAM_COUNT: u64 = 1 << 60;