noq_proto/
lib.rs

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