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