Skip to main content

saorsa_transport/
lib.rs

1// Copyright 2024 Saorsa Labs Ltd.
2//
3// This Saorsa Network Software is licensed under the General Public License (GPL), version 3.
4// Please see the file LICENSE-GPL, or visit <http://www.gnu.org/licenses/> for the full text.
5//
6// Full details available at https://saorsalabs.com/licenses
7
8//! saorsa-transport: QUIC transport protocol with advanced NAT traversal for P2P networks
9#![allow(elided_lifetimes_in_paths)]
10#![allow(missing_debug_implementations)]
11#![allow(clippy::manual_is_multiple_of)]
12//!
13//! This library provides a clean, modular implementation of QUIC-native NAT traversal
14//! using raw public keys for authentication. It is designed to be minimal, focused,
15//! and highly testable, with exceptional cross-platform support.
16//!
17//! The library is organized into the following main modules:
18//! - `transport`: Core QUIC transport functionality
19//! - `nat_traversal`: QUIC-native NAT traversal protocol
20//! - `discovery`: Platform-specific network interface discovery
21//! - `crypto`: Raw public key authentication
22//! - `trust`: Trust management with TOFU pinning and channel binding
23
24// Documentation warnings enabled - all public APIs must be documented
25#![cfg_attr(not(fuzzing), warn(missing_docs))]
26#![allow(unreachable_pub)]
27#![allow(clippy::cognitive_complexity)]
28#![allow(clippy::too_many_arguments)]
29#![allow(clippy::use_self)]
30// Dead code warnings enabled - remove unused code
31#![warn(dead_code)]
32#![allow(clippy::field_reassign_with_default)]
33#![allow(clippy::module_inception)]
34#![allow(clippy::useless_vec)]
35#![allow(private_interfaces)]
36#![allow(clippy::upper_case_acronyms)]
37#![allow(clippy::type_complexity)]
38#![allow(clippy::manual_clamp)]
39#![allow(clippy::needless_range_loop)]
40#![allow(clippy::borrowed_box)]
41#![allow(clippy::manual_strip)]
42#![allow(clippy::if_same_then_else)]
43#![allow(clippy::ptr_arg)]
44#![allow(clippy::incompatible_msrv)]
45#![allow(clippy::await_holding_lock)]
46#![allow(clippy::single_match)]
47#![allow(clippy::must_use_candidate)]
48#![allow(clippy::let_underscore_must_use)]
49#![allow(clippy::let_underscore_untyped)]
50#![allow(clippy::large_enum_variant)]
51#![allow(clippy::too_many_lines)]
52#![allow(clippy::result_large_err)]
53#![allow(clippy::enum_glob_use)]
54#![allow(clippy::match_like_matches_macro)]
55#![allow(clippy::struct_field_names)]
56#![allow(clippy::cast_precision_loss)]
57#![allow(clippy::cast_sign_loss)]
58#![allow(clippy::cast_possible_wrap)]
59#![allow(clippy::cast_possible_truncation)]
60#![allow(clippy::unnecessary_wraps)]
61#![allow(clippy::doc_markdown)]
62#![allow(clippy::module_name_repetitions)]
63#![allow(clippy::items_after_statements)]
64#![allow(clippy::missing_panics_doc)]
65#![allow(clippy::missing_errors_doc)]
66#![allow(clippy::similar_names)]
67#![allow(clippy::new_without_default)]
68#![allow(clippy::unwrap_or_default)]
69#![allow(clippy::uninlined_format_args)]
70#![allow(clippy::redundant_field_names)]
71#![allow(clippy::redundant_closure_for_method_calls)]
72#![allow(clippy::redundant_pattern_matching)]
73#![allow(clippy::option_if_let_else)]
74#![allow(clippy::trivially_copy_pass_by_ref)]
75#![allow(clippy::len_without_is_empty)]
76#![allow(clippy::explicit_auto_deref)]
77#![allow(clippy::blocks_in_conditions)]
78#![allow(clippy::collapsible_else_if)]
79#![allow(clippy::collapsible_if)]
80#![allow(clippy::unnecessary_cast)]
81#![allow(clippy::needless_bool)]
82#![allow(clippy::needless_borrow)]
83#![allow(clippy::redundant_static_lifetimes)]
84#![allow(clippy::match_ref_pats)]
85#![allow(clippy::should_implement_trait)]
86#![allow(clippy::wildcard_imports)]
87#![warn(unused_must_use)]
88#![allow(improper_ctypes)]
89#![allow(improper_ctypes_definitions)]
90#![allow(non_upper_case_globals)]
91#![allow(clippy::wrong_self_convention)]
92#![allow(clippy::vec_init_then_push)]
93#![allow(clippy::format_in_format_args)]
94#![allow(clippy::from_over_into)]
95#![allow(clippy::useless_conversion)]
96#![allow(clippy::never_loop)]
97#![allow(dropping_references)]
98#![allow(non_snake_case)]
99#![allow(clippy::unnecessary_literal_unwrap)]
100#![allow(clippy::assertions_on_constants)]
101
102use std::{
103    fmt,
104    net::{IpAddr, SocketAddr},
105    ops,
106};
107
108// Core modules
109mod cid_queue;
110pub mod coding;
111mod constant_time;
112mod range_set;
113pub mod transport_parameters;
114mod varint;
115
116pub use varint::{VarInt, VarIntBoundsExceeded};
117
118// Removed optional bloom module
119
120/// Bounded pending data buffer with TTL expiration
121pub mod bounded_pending_buffer;
122
123/// RTT-based path selection with hysteresis
124pub mod path_selection;
125
126/// Coordinated shutdown for endpoints
127pub mod shutdown;
128
129/// Watchable state pattern for reactive observation
130pub mod watchable;
131
132/// Fair polling for multiple transports
133pub mod fair_polling;
134
135/// Graceful transport degradation
136pub mod transport_resilience;
137
138/// Connection strategy state machine for progressive NAT traversal fallback
139pub mod connection_strategy;
140
141/// RFC 8305 Happy Eyeballs v2 for parallel IPv4/IPv6 connection racing
142pub mod happy_eyeballs;
143
144/// Discovery trait for stream composition
145pub mod discovery_trait;
146
147/// Structured event logging for observability
148pub mod structured_events;
149
150// ============================================================================
151// SIMPLE API - Zero Configuration P2P
152// ============================================================================
153
154/// Zero-configuration P2P node - THE PRIMARY API
155///
156/// Use [`Node`] for the simplest possible P2P experience:
157/// ```rust,ignore
158/// let node = Node::new().await?;
159/// ```
160pub mod node;
161
162/// Minimal configuration for zero-config P2P nodes
163pub mod node_config;
164
165/// Consolidated node status for observability
166pub mod node_status;
167
168/// Unified events for P2P nodes
169pub mod node_event;
170
171// Core implementation modules
172/// Configuration structures and validation
173pub mod config;
174/// QUIC connection state machine and management
175pub mod connection;
176/// QUIC endpoint for accepting and initiating connections
177pub mod endpoint;
178/// QUIC frame types and encoding/decoding
179pub mod frame;
180/// QUIC packet structures and processing
181pub mod packet;
182/// Shared types and utilities
183pub mod shared;
184/// Transport error types and codes
185pub mod transport_error;
186// Simplified congestion control
187/// Network candidate discovery and management
188pub mod candidate_discovery;
189/// Connection ID generation strategies
190pub mod cid_generator;
191mod congestion;
192
193// Zero-cost tracing system
194/// High-level NAT traversal API
195pub mod nat_traversal_api;
196mod token;
197mod token_memory_cache;
198/// Zero-cost tracing and event logging system
199pub mod tracing;
200
201// Public modules with new structure
202/// Constrained protocol engine for low-bandwidth transports (BLE, LoRa)
203pub mod constrained;
204/// Cryptographic operations and raw public key support
205pub mod crypto;
206/// Platform-specific network interface discovery
207pub mod discovery;
208/// NAT traversal protocol implementation
209pub mod nat_traversal;
210/// Transport-level protocol implementation
211pub mod transport;
212
213/// Connection router for automatic protocol engine selection (QUIC vs Constrained)
214pub mod connection_router;
215
216// Additional modules
217// v0.2: auth module removed - TLS handles peer authentication via ML-DSA-65
218/// Secure chat protocol implementation
219pub mod chat;
220
221// ============================================================================
222// P2P API
223// ============================================================================
224
225/// P2P endpoint - the primary API for saorsa-transport
226///
227/// This module provides the main API for P2P networking with NAT traversal,
228/// connection management, and secure communication.
229pub mod p2p_endpoint;
230
231/// P2P configuration system
232///
233/// This module provides `P2pConfig` with builder pattern support for
234/// configuring endpoints, NAT traversal, MTU, PQC, and other settings.
235pub mod unified_config;
236
237/// Real-time statistics dashboard
238pub mod stats_dashboard;
239/// Terminal user interface components
240pub mod terminal_ui;
241
242// Compliance validation framework
243/// IETF compliance validation tools
244pub mod compliance_validator;
245
246// Comprehensive logging system
247/// Structured logging and diagnostics
248pub mod logging;
249
250/// Metrics collection and export system (basic metrics always available)
251pub mod metrics;
252
253/// TURN-style relay protocol for NAT traversal fallback
254pub mod relay;
255
256/// MASQUE CONNECT-UDP Bind protocol for fully connectable P2P nodes
257pub mod masque;
258
259/// Transport trust module (TOFU, rotations, channel binding surfaces)
260pub mod trust;
261
262/// Address-validation tokens bound to (PeerId||CID||nonce)
263pub mod token_v2;
264
265// High-level async API modules (ported from quinn crate)
266pub mod high_level;
267
268// Re-export high-level API types for easier usage
269pub use high_level::{
270    Accept, Connecting, Connection as HighLevelConnection, Endpoint,
271    RecvStream as HighLevelRecvStream, SendStream as HighLevelSendStream,
272};
273
274// Link transport abstraction layer for overlay networks
275pub mod link_transport;
276mod link_transport_impl;
277
278// Re-export link transport types
279pub use link_transport::{
280    BoxFuture, BoxStream, BoxedHandler, Capabilities, ConnectionStats as LinkConnectionStats,
281    DisconnectReason as LinkDisconnectReason, Incoming as LinkIncoming, LinkConn, LinkError,
282    LinkEvent, LinkRecvStream, LinkResult, LinkSendStream, LinkTransport, NatHint, ProtocolHandler,
283    ProtocolHandlerExt, ProtocolId, StreamFilter, StreamType, StreamTypeFamily,
284};
285pub use link_transport_impl::{
286    P2pLinkConn, P2pLinkTransport, P2pRecvStream, P2pSendStream, SharedTransport,
287};
288
289// Bootstrap cache for peer persistence and quality-based selection
290pub mod bootstrap_cache;
291pub use bootstrap_cache::{
292    BootstrapCache, BootstrapCacheConfig, BootstrapCacheConfigBuilder, CacheEvent, CacheStats,
293    CachedPeer, ConnectionOutcome, ConnectionStats as CacheConnectionStats,
294    NatType as CacheNatType, PeerCapabilities, PeerSource, QualityWeights, SelectionStrategy,
295};
296
297// Host identity for local-only HostKey management (ADR-007)
298pub mod host_identity;
299pub use host_identity::{EndpointKeyPolicy, HostIdentity, HostKeyStorage, StorageError};
300
301// Re-export crypto utilities (v0.2: Pure PQC with ML-DSA-65)
302pub use crypto::raw_public_keys::key_utils::{
303    ML_DSA_65_PUBLIC_KEY_SIZE, ML_DSA_65_SECRET_KEY_SIZE, MlDsaPublicKey, MlDsaSecretKey,
304    fingerprint_public_key, fingerprint_public_key_bytes, generate_ml_dsa_keypair,
305};
306
307// Re-export key types for backward compatibility
308pub use candidate_discovery::{
309    CandidateDiscoveryManager, DiscoveryConfig, DiscoveryError, DiscoveryEvent, NetworkInterface,
310    ValidatedCandidate,
311};
312// v0.13.0: NatTraversalRole removed - all nodes are symmetric P2P nodes
313pub use connection::nat_traversal::{CandidateSource, CandidateState};
314pub use connection::{
315    Chunk, Chunks, ClosedStream, Connection, ConnectionError, ConnectionStats, DatagramDropStats,
316    Datagrams, Event, FinishError, ReadError, ReadableError, RecvStream, SendDatagramError,
317    SendStream, StreamEvent, Streams, WriteError, Written,
318};
319pub use endpoint::{
320    AcceptError, ConnectError, ConnectionHandle, DatagramEvent, Endpoint as LowLevelEndpoint,
321    Incoming,
322};
323pub use nat_traversal_api::{
324    BootstrapNode, CandidateAddress, NatTraversalConfig, NatTraversalEndpoint, NatTraversalError,
325    NatTraversalEvent, NatTraversalStatistics,
326};
327
328// ============================================================================
329// SIMPLE API EXPORTS - Zero Configuration P2P (RECOMMENDED)
330// ============================================================================
331
332/// Zero-configuration P2P node - THE PRIMARY API
333pub use node::{Node, NodeError};
334
335/// Minimal configuration for zero-config P2P nodes
336pub use node_config::{NodeConfig, NodeConfigBuilder};
337
338/// Consolidated node status for observability
339pub use node_status::{NatType, NodeStatus};
340
341/// Unified events for P2P nodes
342pub use node_event::{DisconnectReason as NodeDisconnectReason, NodeEvent, TraversalMethod};
343
344// ============================================================================
345// P2P API EXPORTS (for advanced use)
346// ============================================================================
347
348/// P2P endpoint - for advanced use, prefer Node for most applications
349pub use p2p_endpoint::{
350    ConnectionMetrics, DisconnectReason, EndpointError, EndpointStats, P2pEndpoint, P2pEvent,
351    PeerConnection, TraversalPhase,
352};
353
354/// P2P configuration with builder pattern
355pub use unified_config::{ConfigError, MtuConfig, NatConfig, P2pConfig, P2pConfigBuilder};
356
357/// Connection strategy for progressive NAT traversal fallback
358pub use connection_strategy::{
359    AttemptedMethod, ConnectionAttemptError, ConnectionMethod, ConnectionStage, ConnectionStrategy,
360    StrategyConfig,
361};
362
363pub use relay::{
364    AuthToken,
365    // MASQUE types re-exported from relay module
366    MasqueRelayClient,
367    MasqueRelayConfig,
368    MasqueRelayServer,
369    MasqueRelayStats,
370    MigrationConfig,
371    MigrationCoordinator,
372    MigrationState,
373    RelayAuthenticator,
374    RelayError,
375    RelayManager,
376    RelayManagerConfig,
377    RelayResult,
378    RelaySession,
379    RelaySessionConfig,
380    RelaySessionState,
381    RelayStatisticsCollector,
382};
383pub use shared::{ConnectionId, EcnCodepoint, EndpointEvent};
384pub use transport_error::{Code as TransportErrorCode, Error as TransportError};
385
386// Re-export transport abstraction types
387pub use transport::{
388    BandwidthClass, InboundDatagram, LinkQuality, LoRaParams, ProtocolEngine, ProviderError,
389    TransportAddr, TransportCapabilities, TransportCapabilitiesBuilder, TransportDiagnostics,
390    TransportProvider, TransportRegistry, TransportStats, TransportType, UdpTransport,
391};
392
393#[cfg(feature = "ble")]
394pub use transport::BleTransport;
395
396// Re-export connection router types for automatic protocol engine selection
397pub use connection_router::{
398    ConnectionRouter, RoutedConnection, RouterConfig, RouterError, RouterStats,
399};
400
401// #[cfg(fuzzing)]
402// pub mod fuzzing; // Module not implemented yet
403
404/// The QUIC protocol version implemented.
405///
406/// Simplified to include only the essential versions:
407/// - 0x00000001: QUIC v1 (RFC 9000)
408/// - 0xff00_001d: Draft 29
409pub const DEFAULT_SUPPORTED_VERSIONS: &[u32] = &[
410    0x00000001,  // QUIC v1 (RFC 9000)
411    0xff00_001d, // Draft 29
412];
413
414/// Whether an endpoint was the initiator of a connection
415#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
416#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
417pub enum Side {
418    /// The initiator of a connection
419    Client = 0,
420    /// The acceptor of a connection
421    Server = 1,
422}
423
424impl Side {
425    #[inline]
426    /// Shorthand for `self == Side::Client`
427    pub fn is_client(self) -> bool {
428        self == Self::Client
429    }
430
431    #[inline]
432    /// Shorthand for `self == Side::Server`
433    pub fn is_server(self) -> bool {
434        self == Self::Server
435    }
436}
437
438impl ops::Not for Side {
439    type Output = Self;
440    fn not(self) -> Self {
441        match self {
442            Self::Client => Self::Server,
443            Self::Server => Self::Client,
444        }
445    }
446}
447
448/// Whether a stream communicates data in both directions or only from the initiator
449#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
450#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
451pub enum Dir {
452    /// Data flows in both directions
453    Bi = 0,
454    /// Data flows only from the stream's initiator
455    Uni = 1,
456}
457
458impl Dir {
459    fn iter() -> impl Iterator<Item = Self> {
460        [Self::Bi, Self::Uni].iter().cloned()
461    }
462}
463
464impl fmt::Display for Dir {
465    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
466        use Dir::*;
467        f.pad(match *self {
468            Bi => "bidirectional",
469            Uni => "unidirectional",
470        })
471    }
472}
473
474/// Identifier for a stream within a particular connection
475#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
476#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
477pub struct StreamId(u64);
478
479impl fmt::Display for StreamId {
480    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
481        let initiator = match self.initiator() {
482            Side::Client => "client",
483            Side::Server => "server",
484        };
485        let dir = match self.dir() {
486            Dir::Uni => "uni",
487            Dir::Bi => "bi",
488        };
489        write!(
490            f,
491            "{} {}directional stream {}",
492            initiator,
493            dir,
494            self.index()
495        )
496    }
497}
498
499impl StreamId {
500    /// Create a new StreamId
501    pub fn new(initiator: Side, dir: Dir, index: u64) -> Self {
502        Self((index << 2) | ((dir as u64) << 1) | initiator as u64)
503    }
504    /// Which side of a connection initiated the stream
505    pub fn initiator(self) -> Side {
506        if self.0 & 0x1 == 0 {
507            Side::Client
508        } else {
509            Side::Server
510        }
511    }
512    /// Which directions data flows in
513    pub fn dir(self) -> Dir {
514        if self.0 & 0x2 == 0 { Dir::Bi } else { Dir::Uni }
515    }
516    /// Distinguishes streams of the same initiator and directionality
517    pub fn index(self) -> u64 {
518        self.0 >> 2
519    }
520}
521
522impl From<StreamId> for VarInt {
523    fn from(x: StreamId) -> Self {
524        unsafe { Self::from_u64_unchecked(x.0) }
525    }
526}
527
528impl From<VarInt> for StreamId {
529    fn from(v: VarInt) -> Self {
530        Self(v.0)
531    }
532}
533
534impl From<StreamId> for u64 {
535    fn from(x: StreamId) -> Self {
536        x.0
537    }
538}
539
540impl coding::Codec for StreamId {
541    fn decode<B: bytes::Buf>(buf: &mut B) -> coding::Result<Self> {
542        VarInt::decode(buf).map(|x| Self(x.into_inner()))
543    }
544    fn encode<B: bytes::BufMut>(&self, buf: &mut B) {
545        // StreamId values should always be valid VarInt values, but handle the error case
546        match VarInt::from_u64(self.0) {
547            Ok(varint) => varint.encode(buf),
548            Err(_) => {
549                // This should never happen for valid StreamIds, but use a safe fallback
550                VarInt::MAX.encode(buf);
551            }
552        }
553    }
554}
555
556/// An outgoing packet
557#[derive(Debug)]
558#[must_use]
559pub struct Transmit {
560    /// The socket this datagram should be sent to
561    pub destination: SocketAddr,
562    /// Explicit congestion notification bits to set on the packet
563    pub ecn: Option<EcnCodepoint>,
564    /// Amount of data written to the caller-supplied buffer
565    pub size: usize,
566    /// The segment size if this transmission contains multiple datagrams.
567    /// This is `None` if the transmit only contains a single datagram
568    pub segment_size: Option<usize>,
569    /// Optional source IP address for the datagram
570    pub src_ip: Option<IpAddr>,
571}
572
573// Deal with time
574#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
575pub(crate) use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
576#[cfg(all(target_family = "wasm", target_os = "unknown"))]
577pub(crate) use web_time::{Duration, Instant, SystemTime, UNIX_EPOCH};
578
579//
580// Useful internal constants
581//
582
583/// Maximum time to wait for QUIC connections and tasks to drain during shutdown.
584///
585/// Used by both `P2pEndpoint` and `NatTraversalEndpoint` to bound graceful-shutdown waits.
586pub const SHUTDOWN_DRAIN_TIMEOUT: Duration = Duration::from_secs(5);
587
588/// The maximum number of CIDs we bother to issue per connection
589pub(crate) const LOC_CID_COUNT: u64 = 8;
590pub(crate) const RESET_TOKEN_SIZE: usize = 16;
591pub(crate) const MAX_CID_SIZE: usize = 20;
592pub(crate) const MIN_INITIAL_SIZE: u16 = 1200;
593/// <https://www.rfc-editor.org/rfc/rfc9000.html#name-datagram-size>
594pub(crate) const INITIAL_MTU: u16 = 1200;
595pub(crate) const MAX_UDP_PAYLOAD: u16 = 65527;
596pub(crate) const TIMER_GRANULARITY: Duration = Duration::from_millis(1);
597/// Maximum number of streams that can be tracked per connection
598pub(crate) const MAX_STREAM_COUNT: u64 = 1 << 60;
599
600// Internal type re-exports for crate modules
601pub use cid_generator::RandomConnectionIdGenerator;
602pub use config::{
603    AckFrequencyConfig, ClientConfig, EndpointConfig, MtuDiscoveryConfig, ServerConfig,
604    TransportConfig,
605};
606
607// Post-Quantum Cryptography (PQC) re-exports - always available
608// v0.2: Pure PQC only - HybridKem and HybridSignature removed
609pub use crypto::pqc::{MlDsa65, MlKem768, PqcConfig, PqcConfigBuilder, PqcError, PqcResult};
610pub(crate) use frame::Frame;
611pub use token::TokenStore;
612pub(crate) use token::{NoneTokenLog, ResetToken, TokenLog};
613pub(crate) use token_memory_cache::TokenMemoryCache;