Skip to main content

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
44pub(crate) mod connection;
45pub use crate::connection::{
46    Chunk, Chunks, ClosePathError, ClosedPath, ClosedStream, Connection, ConnectionError,
47    ConnectionStats, Datagrams, Event, FinishError, FrameStats, MultipathNotNegotiated, PathError,
48    PathEvent, PathId, PathStats, PathStatus, ReadError, ReadableError, RecvStream, RttEstimator,
49    SendDatagramError, SendStream, SetPathStatusError, ShouldTransmit, StreamEvent, Streams,
50    UdpStats, WriteError, Written,
51};
52#[cfg(test)]
53use test_strategy::Arbitrary;
54
55#[cfg(feature = "rustls")]
56pub use rustls;
57
58mod config;
59#[cfg(doc)]
60pub use config::DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED;
61pub use config::{
62    AckFrequencyConfig, ClientConfig, ConfigError, EndpointConfig, IdleTimeout, MtuDiscoveryConfig,
63    ServerConfig, StdSystemTime, TimeSource, TransportConfig, ValidationTokenConfig,
64};
65#[cfg(feature = "qlog")]
66pub use config::{QlogConfig, QlogFactory, QlogFileFactory};
67
68pub mod crypto;
69
70mod frame;
71pub use crate::frame::{
72    ApplicationClose, ConnectionClose, Datagram, DatagramInfo, FrameType, InvalidFrameId,
73    MaybeFrame, StreamInfo,
74};
75use crate::{
76    coding::{Decodable, Encodable},
77    frame::Frame,
78};
79
80mod endpoint;
81pub use crate::endpoint::{
82    AcceptError, ConnectError, ConnectionHandle, DatagramEvent, Endpoint, Incoming, RetryError,
83};
84
85mod packet;
86pub use packet::{
87    ConnectionIdParser, FixedLengthConnectionIdParser, LongType, PacketDecodeError, PartialDecode,
88    ProtectedHeader, ProtectedInitialHeader,
89};
90
91mod shared;
92pub use crate::shared::{ConnectionEvent, ConnectionId, EcnCodepoint, EndpointEvent};
93
94mod transport_error;
95pub use crate::transport_error::{Code as TransportErrorCode, Error as TransportError};
96
97pub mod congestion;
98
99mod cid_generator;
100pub use crate::cid_generator::{
101    ConnectionIdGenerator, HashedConnectionIdGenerator, InvalidCid, RandomConnectionIdGenerator,
102};
103
104mod token;
105use token::ResetToken;
106pub use token::{NoneTokenLog, NoneTokenStore, TokenLog, TokenReuseError, TokenStore};
107
108mod address_discovery;
109
110mod token_memory_cache;
111pub use token_memory_cache::TokenMemoryCache;
112
113pub mod iroh_hp;
114
115// Deal with time
116#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
117pub(crate) use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
118#[cfg(all(target_family = "wasm", target_os = "unknown"))]
119pub(crate) use web_time::{Duration, Instant, SystemTime, UNIX_EPOCH};
120
121#[cfg(feature = "bench")]
122pub mod bench_exports {
123    //! Exports for benchmarks
124    pub use crate::connection::send_buffer::send_buffer_benches;
125}
126
127#[cfg(fuzzing)]
128pub mod fuzzing {
129    pub use crate::connection::{Retransmits, State as ConnectionState, StreamsState};
130    pub use crate::frame::ResetStream;
131    pub use crate::packet::PartialDecode;
132    pub use crate::transport_parameters::TransportParameters;
133    pub use bytes::{BufMut, BytesMut};
134
135    #[cfg(feature = "arbitrary")]
136    use arbitrary::{Arbitrary, Result, Unstructured};
137
138    #[cfg(feature = "arbitrary")]
139    impl<'arbitrary> Arbitrary<'arbitrary> for TransportParameters {
140        fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Self> {
141            Ok(Self {
142                initial_max_streams_bidi: u.arbitrary()?,
143                initial_max_streams_uni: u.arbitrary()?,
144                ack_delay_exponent: u.arbitrary()?,
145                max_udp_payload_size: u.arbitrary()?,
146                ..Self::default()
147            })
148        }
149    }
150
151    #[derive(Debug)]
152    pub struct PacketParams {
153        pub local_cid_len: usize,
154        pub buf: BytesMut,
155        pub grease_quic_bit: bool,
156    }
157
158    #[cfg(feature = "arbitrary")]
159    impl<'arbitrary> Arbitrary<'arbitrary> for PacketParams {
160        fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Self> {
161            let local_cid_len: usize = u.int_in_range(0..=crate::MAX_CID_SIZE)?;
162            let bytes: Vec<u8> = Vec::arbitrary(u)?;
163            let mut buf = BytesMut::new();
164            buf.put_slice(&bytes[..]);
165            Ok(Self {
166                local_cid_len,
167                buf,
168                grease_quic_bit: bool::arbitrary(u)?,
169            })
170        }
171    }
172}
173
174/// The QUIC protocol version implemented.
175pub const DEFAULT_SUPPORTED_VERSIONS: &[u32] = &[
176    0x00000001,
177    0xff00_001d,
178    0xff00_001e,
179    0xff00_001f,
180    0xff00_0020,
181    0xff00_0021,
182    0xff00_0022,
183];
184
185/// Whether an endpoint was the initiator of a connection
186#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
187#[cfg_attr(test, derive(Arbitrary))]
188#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
189pub enum Side {
190    /// The initiator of a connection
191    Client = 0,
192    /// The acceptor of a connection
193    Server = 1,
194}
195
196impl Side {
197    #[inline]
198    /// Shorthand for `self == Side::Client`
199    pub fn is_client(self) -> bool {
200        self == Self::Client
201    }
202
203    #[inline]
204    /// Shorthand for `self == Side::Server`
205    pub fn is_server(self) -> bool {
206        self == Self::Server
207    }
208}
209
210impl ops::Not for Side {
211    type Output = Self;
212    fn not(self) -> Self {
213        match self {
214            Self::Client => Self::Server,
215            Self::Server => Self::Client,
216        }
217    }
218}
219
220/// Whether a stream communicates data in both directions or only from the initiator
221#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
222#[cfg_attr(test, derive(Arbitrary))]
223#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
224pub enum Dir {
225    /// Data flows in both directions
226    Bi = 0,
227    /// Data flows only from the stream's initiator
228    Uni = 1,
229}
230
231impl Dir {
232    fn iter() -> impl Iterator<Item = Self> {
233        [Self::Bi, Self::Uni].iter().cloned()
234    }
235}
236
237impl fmt::Display for Dir {
238    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239        use Dir::*;
240        f.pad(match *self {
241            Bi => "bidirectional",
242            Uni => "unidirectional",
243        })
244    }
245}
246
247/// Identifier for a stream within a particular connection
248#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
249#[cfg_attr(test, derive(Arbitrary))]
250pub struct StreamId(#[cfg_attr(test, strategy(crate::varint::varint_u64()))] u64);
251
252impl fmt::Display for StreamId {
253    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254        let initiator = match self.initiator() {
255            Side::Client => "client",
256            Side::Server => "server",
257        };
258        let dir = match self.dir() {
259            Dir::Uni => "uni",
260            Dir::Bi => "bi",
261        };
262        write!(
263            f,
264            "{} {}directional stream {}",
265            initiator,
266            dir,
267            self.index()
268        )
269    }
270}
271
272impl StreamId {
273    /// Create a new StreamId
274    pub fn new(initiator: Side, dir: Dir, index: u64) -> Self {
275        Self((index << 2) | ((dir as u64) << 1) | initiator as u64)
276    }
277    /// Which side of a connection initiated the stream
278    pub fn initiator(self) -> Side {
279        if self.0 & 0x1 == 0 {
280            Side::Client
281        } else {
282            Side::Server
283        }
284    }
285    /// Which directions data flows in
286    pub fn dir(self) -> Dir {
287        if self.0 & 0x2 == 0 { Dir::Bi } else { Dir::Uni }
288    }
289    /// Distinguishes streams of the same initiator and directionality
290    pub fn index(self) -> u64 {
291        self.0 >> 2
292    }
293}
294
295impl From<StreamId> for VarInt {
296    fn from(x: StreamId) -> Self {
297        unsafe { Self::from_u64_unchecked(x.0) }
298    }
299}
300
301impl From<VarInt> for StreamId {
302    fn from(v: VarInt) -> Self {
303        Self(v.0)
304    }
305}
306
307impl From<StreamId> for u64 {
308    fn from(x: StreamId) -> Self {
309        x.0
310    }
311}
312
313impl Decodable for StreamId {
314    fn decode<B: bytes::Buf>(buf: &mut B) -> coding::Result<Self> {
315        VarInt::decode(buf).map(|x| Self(x.into_inner()))
316    }
317}
318
319impl Encodable for StreamId {
320    fn encode<B: bytes::BufMut>(&self, buf: &mut B) {
321        VarInt::from_u64(self.0).unwrap().encode(buf);
322    }
323}
324
325#[cfg(feature = "arbitrary")]
326impl<'arbitrary> arbitrary::Arbitrary<'arbitrary> for StreamId {
327    fn arbitrary(u: &mut arbitrary::Unstructured<'arbitrary>) -> arbitrary::Result<Self> {
328        Ok(VarInt::arbitrary(u)?.into())
329    }
330}
331
332/// An outgoing packet
333#[derive(Debug)]
334#[must_use]
335pub struct Transmit {
336    /// The socket this datagram should be sent to
337    pub destination: SocketAddr,
338    /// Explicit congestion notification bits to set on the packet
339    pub ecn: Option<EcnCodepoint>,
340    /// Amount of data written to the caller-supplied buffer
341    pub size: usize,
342    /// The segment size if this transmission contains multiple datagrams.
343    /// This is `None` if the transmit only contains a single datagram
344    pub segment_size: Option<usize>,
345    /// Optional source IP address for the datagram
346    pub src_ip: Option<IpAddr>,
347}
348
349//
350// Useful internal constants
351//
352
353/// The maximum number of CIDs we bother to issue per path
354const LOCAL_CID_COUNT: u64 = 12;
355const RESET_TOKEN_SIZE: usize = 16;
356const MAX_CID_SIZE: usize = 20;
357const MIN_INITIAL_SIZE: u16 = 1200;
358/// <https://www.rfc-editor.org/rfc/rfc9000.html#name-datagram-size>
359const INITIAL_MTU: u16 = 1200;
360const MAX_UDP_PAYLOAD: u16 = 65527;
361const TIMER_GRANULARITY: Duration = Duration::from_millis(1);
362/// Maximum number of streams that can be uniquely identified by a stream ID
363const MAX_STREAM_COUNT: u64 = 1 << 60;
364
365/// Identifies a network path by the combination of remote and local addresses
366///
367/// Including the local ensures good behavior when the host has multiple IP addresses on the same
368/// subnet and zero-length connection IDs are in use or when multipath is enabled and multiple
369/// paths exist with the same remote, but different local IP interfaces.
370#[derive(Hash, Eq, PartialEq, Copy, Clone)]
371pub struct FourTuple {
372    /// The remote side of this tuple
373    pub remote: SocketAddr,
374    /// The local side of this tuple.
375    ///
376    /// The socket is irrelevant for our intents and purposes:
377    /// When we send, we can only specify the `src_ip`, not the source port.
378    /// So even if we track the port, we won't be able to make use of it.
379    pub local_ip: Option<IpAddr>,
380}
381
382impl FourTuple {
383    /// Returns whether we think the other address probably represents the same path
384    /// as ours.
385    ///
386    /// If we have a local IP set, then we're exact and only match if the 4-tuples are
387    /// exactly equal.
388    /// If we don't have a local IP set, then we only check the remote addresses for equality.
389    pub fn is_probably_same_path(&self, other: &Self) -> bool {
390        self.remote == other.remote && (self.local_ip.is_none() || self.local_ip == other.local_ip)
391    }
392
393    /// Updates this tuple's local address iff
394    /// - it was unset before,
395    /// - the other tuple has the same remote, and
396    /// - the other tuple has a local address set.
397    ///
398    /// Returns whether this and the other remote are now fully equal.
399    pub fn update_local_if_same_remote(&mut self, other: &Self) -> bool {
400        if self.remote != other.remote {
401            return false;
402        }
403        if self.local_ip.is_some() && self.local_ip != other.local_ip {
404            return false;
405        }
406        self.local_ip = other.local_ip;
407        true
408    }
409}
410
411impl fmt::Display for FourTuple {
412    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
413        f.write_str("(local: ")?;
414        if let Some(local_ip) = &self.local_ip {
415            local_ip.fmt(f)?;
416            f.write_str(", ")?;
417        } else {
418            f.write_str("<unspecified>, ")?;
419        }
420        f.write_str("remote: ")?;
421        self.remote.fmt(f)?;
422        f.write_str(")")
423    }
424}
425
426impl fmt::Debug for FourTuple {
427    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
428        fmt::Display::fmt(&self, f)
429    }
430}