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