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