1#![allow(elided_lifetimes_in_paths)]
10#![allow(missing_debug_implementations)]
11#![allow(clippy::manual_is_multiple_of)]
12#![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#![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
108mod ack_frame;
110mod cid_queue;
111pub mod coding;
112mod constant_time;
113mod range_set;
114pub mod transport_parameters;
115mod varint;
116
117pub use varint::{VarInt, VarIntBoundsExceeded};
118
119pub mod bounded_pending_buffer;
123
124pub mod diagnostics;
126
127pub mod path_selection;
129
130pub mod shutdown;
132
133pub mod watchable;
135
136pub mod fair_polling;
138
139pub mod transport_resilience;
141
142pub mod connection_strategy;
144
145pub mod happy_eyeballs;
147
148pub mod error_handling;
150
151pub mod discovery_trait;
153
154pub mod structured_events;
156
157pub mod node;
168
169pub mod node_config;
171
172pub mod node_status;
174
175mod coordinator_control;
176pub(crate) mod coordinator_health;
178pub mod mdns;
180pub mod node_event;
182mod peer_directory;
183mod port_mapping;
184
185pub mod reachability;
187
188pub mod config;
191pub mod connection;
193pub mod endpoint;
195pub mod frame;
197pub mod packet;
199pub mod path;
201pub mod shared;
203pub mod transport_error;
205pub mod candidate_discovery;
208pub mod cid_generator;
210mod congestion;
211
212pub mod nat_traversal_api;
215mod token;
216mod token_memory_cache;
217pub mod tracing;
219
220pub mod constrained;
223pub mod crypto;
225pub mod discovery;
227
228pub use discovery::{
231 AddrFilter, AddressLookup, BootstrapCacheLookup, CompositeFilter, DedupFilter,
232 DropLoopbackFilter, DropUnspecifiedFilter, HardcodedLookup, LookupError, LookupRegistry,
233 MdnsLookup, ParallelLookupStream, PassThroughFilter, PreferIpv6Filter,
234};
235pub mod nat_traversal;
237pub mod transport;
239
240pub mod connection_router;
242
243pub mod chat;
247
248mod connection_lifecycle;
254
255pub mod p2p_endpoint;
260
261pub mod unified_config;
266
267pub mod stats_dashboard;
269pub mod terminal_ui;
271
272pub mod compliance_validator;
275
276pub mod logging;
279
280pub mod metrics;
282
283pub mod relay;
285
286pub mod masque;
288
289pub mod trust;
291
292pub mod token_v2;
294
295pub mod high_level;
297
298pub use high_level::{
300 Accept, Connecting, Connection as HighLevelConnection, Endpoint,
301 RecvStream as HighLevelRecvStream, SendStream as HighLevelSendStream, WeakConnectionHandle,
302};
303pub use path::{Path, PathId, WeakPathHandle};
304
305pub mod link_transport;
307mod link_transport_impl;
308
309pub use link_transport::{
311 BoxFuture, BoxStream, BoxedHandler, Capabilities, ConnectionStats as LinkConnectionStats,
312 DisconnectReason as LinkDisconnectReason, Incoming as LinkIncoming, LinkConn, LinkError,
313 LinkEvent, LinkRecvStream, LinkResult, LinkSendStream, LinkTransport, NatHint, ProtocolHandler,
314 ProtocolHandlerExt, ProtocolId, StreamFilter, StreamType, StreamTypeFamily,
315};
316pub use link_transport_impl::{
317 P2pLinkConn, P2pLinkTransport, P2pRecvStream, P2pSendStream, SharedTransport,
318};
319
320pub mod bootstrap_cache;
322pub use bootstrap_cache::{
323 BootstrapCache, BootstrapCacheConfig, BootstrapCacheConfigBuilder, CacheEvent, CacheStats,
324 CachedPeer, ConnectionOutcome, ConnectionStats as CacheConnectionStats,
325 NatType as CacheNatType, PeerCapabilities, PeerSource, QualityWeights, SelectionStrategy,
326};
327
328pub mod host_identity;
330pub use host_identity::{EndpointKeyPolicy, HostIdentity, HostKeyStorage, StorageError};
331
332pub use crypto::raw_public_keys::key_utils::{
334 ML_DSA_65_PUBLIC_KEY_SIZE, ML_DSA_65_SECRET_KEY_SIZE, MlDsaPublicKey, MlDsaSecretKey,
335 derive_peer_id_from_key_bytes, derive_peer_id_from_public_key, generate_ml_dsa_keypair,
336 verify_peer_id,
337};
338
339pub use candidate_discovery::{
341 CandidateDiscoveryManager, DiscoveryConfig, DiscoveryError, DiscoveryEvent, NetworkInterface,
342 ValidatedCandidate,
343};
344pub use connection::nat_traversal::{CandidateSource, CandidateState};
346pub use connection::{
347 Chunk, Chunks, ClosedStream, Connection, ConnectionError, ConnectionStats, DatagramDropStats,
348 Datagrams, Event, FinishError, PathStats, ReadError, ReadableError, RecvStream,
349 SendDatagramError, SendStream, StreamEvent, Streams, WriteError, Written,
350};
351pub use coordinator_control::RejectionReason;
352pub use endpoint::{
353 AcceptError, ConnectError, ConnectionHandle, DatagramEvent, Endpoint as LowLevelEndpoint,
354 Incoming,
355};
356pub use nat_traversal_api::{
357 BootstrapNode, CandidateAddress, NatTraversalConfig, NatTraversalEndpoint, NatTraversalError,
358 NatTraversalEvent, NatTraversalStatistics, PeerId, TraversalDeadlineKind,
359 TraversalFailureReason,
360};
361pub use reachability::{ReachabilityScope, TraversalMethod};
362
363pub use node::{Node, NodeError};
369
370pub use node_config::{NodeConfig, NodeConfigBuilder};
372
373pub use node_status::{NatType, NodeStatus};
375
376pub use node_event::{DisconnectReason as NodeDisconnectReason, NodeEvent};
378
379pub use ack_frame::ReceiveRejectReason;
385pub use connection_lifecycle::ConnectionCloseReason;
386pub use diagnostics::{GsoDiagnostics, GsoDiagnosticsSnapshot};
387pub use p2p_endpoint::{
388 AckDiagnosticsSnapshot, AckOutcomeCounters, AckPeerDiagnosticsSnapshot,
389 AckStageDiagnosticsSnapshot, AckStageLatencySnapshot, ConnectionHealth, ConnectionMetrics,
390 ConnectionTransportStats, DataChannelDiagnosticsSnapshot, DirectPathStatus,
391 DirectPathUnavailableReason, DisconnectReason, EndpointError, EndpointStats, P2pEndpoint,
392 P2pEvent, PeerConnection, PeerLifecycleEvent, TraversalPhase,
393};
394
395pub use unified_config::{
397 ConfigError, MtuConfig, NatConfig, P2pConfig, P2pConfigBuilder, PortMappingConfig,
398};
399
400pub use connection_strategy::{
402 AttemptedMethod, ConnectionAttemptError, ConnectionMethod, ConnectionStage, ConnectionStrategy,
403 StrategyConfig,
404};
405
406pub use relay::{
407 AuthToken,
408 MasqueRelayClient,
410 MasqueRelayConfig,
411 MasqueRelayServer,
412 MasqueRelayStats,
413 MigrationConfig,
414 MigrationCoordinator,
415 MigrationState,
416 RelayAuthenticator,
417 RelayError,
418 RelayManager,
419 RelayManagerConfig,
420 RelayResult,
421 RelaySession,
422 RelaySessionConfig,
423 RelaySessionState,
424 RelayStatisticsCollector,
425};
426pub use shared::{ConnectionId, EcnCodepoint, EndpointEvent};
427pub use transport_error::{Code as TransportErrorCode, Error as TransportError};
428
429pub use transport::{
431 BandwidthClass, InboundDatagram, LinkQuality, LoRaParams, ProtocolEngine, ProviderError,
432 TransportAddr, TransportCapabilities, TransportCapabilitiesBuilder, TransportDiagnostics,
433 TransportProvider, TransportRegistry, TransportStats, TransportType, UdpTransport,
434};
435
436#[cfg(feature = "ble")]
437pub use transport::BleTransport;
438
439pub use connection_router::{
441 ConnectionRouter, RoutedConnection, RouterConfig, RouterError, RouterStats,
442};
443
444pub const DEFAULT_SUPPORTED_VERSIONS: &[u32] = &[
453 0x00000001, 0xff00_001d, ];
456
457#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
459#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
460pub enum Side {
461 Client = 0,
463 Server = 1,
465}
466
467impl Side {
468 #[inline]
469 pub fn is_client(self) -> bool {
471 self == Self::Client
472 }
473
474 #[inline]
475 pub fn is_server(self) -> bool {
477 self == Self::Server
478 }
479}
480
481impl ops::Not for Side {
482 type Output = Self;
483 fn not(self) -> Self {
484 match self {
485 Self::Client => Self::Server,
486 Self::Server => Self::Client,
487 }
488 }
489}
490
491#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
493#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
494pub enum Dir {
495 Bi = 0,
497 Uni = 1,
499}
500
501impl Dir {
502 fn iter() -> impl Iterator<Item = Self> {
503 [Self::Bi, Self::Uni].iter().cloned()
504 }
505}
506
507impl fmt::Display for Dir {
508 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
509 use Dir::*;
510 f.pad(match *self {
511 Bi => "bidirectional",
512 Uni => "unidirectional",
513 })
514 }
515}
516
517#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
519#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
520pub struct StreamId(u64);
521
522impl fmt::Display for StreamId {
523 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
524 let initiator = match self.initiator() {
525 Side::Client => "client",
526 Side::Server => "server",
527 };
528 let dir = match self.dir() {
529 Dir::Uni => "uni",
530 Dir::Bi => "bi",
531 };
532 write!(
533 f,
534 "{} {}directional stream {}",
535 initiator,
536 dir,
537 self.index()
538 )
539 }
540}
541
542impl StreamId {
543 const MAX_INDEX: u64 = VarInt::MAX.into_inner() >> 2;
544
545 pub fn try_new(initiator: Side, dir: Dir, index: u64) -> Result<Self, VarIntBoundsExceeded> {
547 if index > Self::MAX_INDEX {
548 return Err(VarIntBoundsExceeded);
549 }
550 let raw = (index << 2) | ((dir as u64) << 1) | initiator as u64;
551 VarInt::from_u64(raw).map(|_| Self(raw))
552 }
553
554 pub(crate) fn new(initiator: Side, dir: Dir, index: u64) -> Self {
556 Self((index << 2) | ((dir as u64) << 1) | initiator as u64)
557 }
558 pub fn initiator(self) -> Side {
560 if self.0 & 0x1 == 0 {
561 Side::Client
562 } else {
563 Side::Server
564 }
565 }
566 pub fn dir(self) -> Dir {
568 if self.0 & 0x2 == 0 { Dir::Bi } else { Dir::Uni }
569 }
570 pub fn index(self) -> u64 {
572 self.0 >> 2
573 }
574}
575
576impl From<StreamId> for VarInt {
577 fn from(x: StreamId) -> Self {
578 unsafe { Self::from_u64_unchecked(x.0) }
581 }
582}
583
584impl From<VarInt> for StreamId {
585 fn from(v: VarInt) -> Self {
586 Self(v.0)
587 }
588}
589
590impl From<StreamId> for u64 {
591 fn from(x: StreamId) -> Self {
592 x.0
593 }
594}
595
596impl coding::Codec for StreamId {
597 fn decode<B: bytes::Buf>(buf: &mut B) -> coding::Result<Self> {
598 VarInt::decode(buf).map(|x| Self(x.into_inner()))
599 }
600 fn encode<B: bytes::BufMut>(&self, buf: &mut B) {
601 VarInt::from(*self).encode(buf);
602 }
603}
604
605#[cfg(test)]
606mod stream_id_tests {
607 use super::{Dir, Side, StreamId, VarInt};
608
609 #[test]
610 fn try_new_accepts_largest_valid_stream_index() {
611 let stream_id = StreamId::try_new(Side::Server, Dir::Uni, StreamId::MAX_INDEX);
612 assert!(stream_id.is_ok());
613 }
614
615 #[test]
616 fn try_new_rejects_stream_index_outside_varint_range() {
617 let invalid_index = StreamId::MAX_INDEX + 1;
618 let stream_id = StreamId::try_new(Side::Client, Dir::Bi, invalid_index);
619 assert!(stream_id.is_err());
620 }
621
622 #[test]
623 fn try_new_rejects_index_that_would_alias_after_shift() {
624 let stream_id = StreamId::try_new(Side::Client, Dir::Bi, 1_u64 << 63);
625 assert!(stream_id.is_err());
626 }
627
628 #[test]
629 fn valid_stream_id_converts_to_varint() {
630 let stream_id = StreamId::try_new(Side::Client, Dir::Uni, 7);
631 assert!(stream_id.is_ok());
632 let Ok(stream_id) = stream_id else {
633 return;
634 };
635 let varint = VarInt::from(stream_id);
636 assert_eq!(varint.into_inner(), u64::from(stream_id));
637 }
638}
639
640#[derive(Debug)]
642#[must_use]
643pub struct Transmit {
644 pub destination: SocketAddr,
646 pub ecn: Option<EcnCodepoint>,
648 pub size: usize,
650 pub segment_size: Option<usize>,
653 pub src_ip: Option<IpAddr>,
655}
656
657#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
659pub(crate) use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
660#[cfg(all(target_family = "wasm", target_os = "unknown"))]
661pub(crate) use web_time::{Duration, Instant, SystemTime, UNIX_EPOCH};
662
663pub const SHUTDOWN_DRAIN_TIMEOUT: Duration = Duration::from_secs(5);
671
672pub(crate) const LOC_CID_COUNT: u64 = 8;
674pub(crate) const RESET_TOKEN_SIZE: usize = 16;
675pub(crate) const MAX_CID_SIZE: usize = 20;
676pub(crate) const MIN_INITIAL_SIZE: u16 = 1200;
677pub(crate) const INITIAL_MTU: u16 = 1200;
679pub(crate) const MAX_UDP_PAYLOAD: u16 = 65527;
680pub(crate) const TIMER_GRANULARITY: Duration = Duration::from_millis(1);
681pub(crate) const MAX_STREAM_COUNT: u64 = 1 << 60;
683
684pub use cid_generator::RandomConnectionIdGenerator;
686pub use config::{
687 AckFrequencyConfig, ClientConfig, EndpointConfig, MtuDiscoveryConfig, ServerConfig,
688 TransportConfig,
689};
690
691pub use crypto::pqc::{MlDsa65, MlKem768, PqcConfig, PqcConfigBuilder, PqcError, PqcResult};
694pub(crate) use frame::Frame;
695pub use token::TokenStore;
696pub(crate) use token::{NoneTokenLog, ResetToken, TokenLog};
697pub(crate) use token_memory_cache::TokenMemoryCache;