ant_quic/
nat_traversal_api.rs

1//! High-level NAT Traversal API for Autonomi P2P Networks
2//!
3//! This module provides a simple, high-level interface for establishing
4//! QUIC connections through NATs using sophisticated hole punching and
5//! coordination protocols.
6
7use std::{collections::HashMap, fmt, net::SocketAddr, sync::Arc, time::Duration};
8
9/// Creates a bind address that allows the OS to select a random available port
10///
11/// This provides protocol obfuscation by preventing port fingerprinting, which improves
12/// security by making it harder for attackers to identify and target QUIC endpoints.
13///
14/// # Security Benefits
15/// - **Port Randomization**: Each endpoint gets a different random port, preventing easy detection
16/// - **Fingerprinting Resistance**: Makes protocol identification more difficult for attackers
17/// - **Attack Surface Reduction**: Reduces predictable network patterns that could be exploited
18///
19/// # Implementation Details
20/// - Binds to `0.0.0.0:0` to let the OS choose an available port
21/// - Used automatically when `bind_addr` is `None` in endpoint configuration
22/// - Provides better security than static or predictable port assignments
23///
24/// # Added in Version 0.6.1
25/// This function was introduced as part of security improvements in commit 6e633cd9
26/// to enhance protocol obfuscation capabilities.
27fn create_random_port_bind_addr() -> SocketAddr {
28    "0.0.0.0:0"
29        .parse()
30        .expect("Random port bind address format is always valid")
31}
32
33use tracing::{debug, error, info, warn};
34
35use std::sync::atomic::{AtomicBool, Ordering};
36
37use tokio::{
38    net::UdpSocket,
39    sync::mpsc,
40    time::{sleep, timeout},
41};
42
43use crate::high_level::default_runtime;
44
45use crate::{
46    VarInt,
47    candidate_discovery::{CandidateDiscoveryManager, DiscoveryConfig, DiscoveryEvent},
48    connection::nat_traversal::{CandidateSource, CandidateState, NatTraversalRole},
49};
50
51use crate::{
52    ClientConfig, ConnectionError, EndpointConfig, ServerConfig, TransportConfig,
53    high_level::{Connection as QuinnConnection, Endpoint as QuinnEndpoint},
54};
55
56#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
57use crate::{crypto::rustls::QuicClientConfig, crypto::rustls::QuicServerConfig};
58
59use crate::config::validation::{ConfigValidator, ValidationResult};
60
61#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
62use crate::crypto::certificate_manager::{CertificateConfig, CertificateManager};
63
64/// High-level NAT traversal endpoint for Autonomi P2P networks
65pub struct NatTraversalEndpoint {
66    /// Underlying Quinn endpoint
67    quinn_endpoint: Option<QuinnEndpoint>,
68    /// Fallback internal endpoint for non-production builds
69
70    /// NAT traversal configuration
71    config: NatTraversalConfig,
72    /// Known bootstrap/coordinator nodes
73    bootstrap_nodes: Arc<std::sync::RwLock<Vec<BootstrapNode>>>,
74    /// Active NAT traversal sessions
75    active_sessions: Arc<std::sync::RwLock<HashMap<PeerId, NatTraversalSession>>>,
76    /// Candidate discovery manager
77    discovery_manager: Arc<std::sync::Mutex<CandidateDiscoveryManager>>,
78    /// Event callback for coordination (simplified without async channels)
79    event_callback: Option<Box<dyn Fn(NatTraversalEvent) + Send + Sync>>,
80    /// Shutdown flag for async operations
81    shutdown: Arc<AtomicBool>,
82    /// Channel for internal communication
83    event_tx: Option<mpsc::UnboundedSender<NatTraversalEvent>>,
84    /// Active connections by peer ID
85    connections: Arc<std::sync::RwLock<HashMap<PeerId, QuinnConnection>>>,
86    /// Local peer ID
87    local_peer_id: PeerId,
88    /// Timeout configuration
89    timeout_config: crate::config::nat_timeouts::TimeoutConfig,
90}
91
92/// Configuration for NAT traversal behavior
93///
94/// This configuration controls various aspects of NAT traversal including security,
95/// performance, and reliability settings. Recent improvements in version 0.6.1 include
96/// enhanced security through protocol obfuscation and robust error handling.
97///
98/// # Security Features (Added in v0.6.1)
99/// - **Protocol Obfuscation**: Random port binding prevents fingerprinting attacks
100/// - **Robust Error Handling**: Panic-free operation with graceful error recovery
101/// - **Input Validation**: Enhanced validation of configuration parameters
102///
103/// # Example
104/// ```rust
105/// use ant_quic::nat_traversal_api::{NatTraversalConfig, EndpointRole};
106/// use std::time::Duration;
107/// use std::net::SocketAddr;
108///
109/// // Recommended secure configuration  
110/// let config = NatTraversalConfig {
111///     role: EndpointRole::Client,
112///     bootstrap_nodes: vec!["127.0.0.1:9000".parse::<SocketAddr>().unwrap()],
113///     max_candidates: 10,
114///     coordination_timeout: Duration::from_secs(10),
115///     enable_symmetric_nat: true,
116///     enable_relay_fallback: false,
117///     max_concurrent_attempts: 5,
118///     bind_addr: None, // Auto-select for security
119///     prefer_rfc_nat_traversal: true,
120///     timeouts: Default::default(),
121/// };
122/// ```
123#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
124pub struct NatTraversalConfig {
125    /// Role of this endpoint in the network
126    pub role: EndpointRole,
127    /// Bootstrap nodes for coordination and candidate discovery
128    pub bootstrap_nodes: Vec<SocketAddr>,
129    /// Maximum number of address candidates to maintain
130    pub max_candidates: usize,
131    /// Timeout for coordination rounds
132    pub coordination_timeout: Duration,
133    /// Enable symmetric NAT prediction algorithms
134    pub enable_symmetric_nat: bool,
135    /// Enable automatic relay fallback
136    pub enable_relay_fallback: bool,
137    /// Maximum concurrent NAT traversal attempts
138    pub max_concurrent_attempts: usize,
139    /// Bind address for the endpoint
140    ///
141    /// - `Some(addr)`: Bind to the specified address
142    /// - `None`: Auto-select random port for enhanced security (recommended)
143    ///
144    /// When `None`, the system uses an internal method to automatically
145    /// select a random available port, providing protocol obfuscation and improved
146    /// security through port randomization.
147    ///
148    /// # Security Benefits of None (Auto-Select)
149    /// - **Protocol Obfuscation**: Makes endpoint detection harder for attackers
150    /// - **Port Randomization**: Each instance gets a different port
151    /// - **Fingerprinting Resistance**: Reduces predictable network patterns
152    ///
153    /// # Added in Version 0.6.1
154    /// Enhanced security through automatic random port selection
155    pub bind_addr: Option<SocketAddr>,
156    /// Prefer RFC-compliant NAT traversal frame format
157    /// When true, will send RFC-compliant frames if the peer supports it
158    pub prefer_rfc_nat_traversal: bool,
159    /// Timeout configuration for NAT traversal operations
160    pub timeouts: crate::config::nat_timeouts::TimeoutConfig,
161}
162
163/// Role of an endpoint in the Autonomi network
164#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
165pub enum EndpointRole {
166    /// Regular client node (most common)
167    Client,
168    /// Server node (always reachable, can coordinate)
169    Server {
170        /// Whether this server can coordinate NAT traversal
171        can_coordinate: bool,
172    },
173    /// Bootstrap node (public, coordinates NAT traversal)
174    Bootstrap,
175}
176
177impl EndpointRole {
178    /// Get a string representation of the role for use in certificate common names
179    pub fn name(&self) -> &'static str {
180        match self {
181            Self::Client => "client",
182            Self::Server { .. } => "server",
183            Self::Bootstrap => "bootstrap",
184        }
185    }
186}
187
188/// Unique identifier for a peer in the network
189#[derive(
190    Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
191)]
192pub struct PeerId(pub [u8; 32]);
193
194/// Information about a bootstrap/coordinator node
195#[derive(Debug, Clone)]
196pub struct BootstrapNode {
197    /// Network address of the bootstrap node
198    pub address: SocketAddr,
199    /// Last successful contact time
200    pub last_seen: std::time::Instant,
201    /// Whether this node can coordinate NAT traversal
202    pub can_coordinate: bool,
203    /// RTT to this bootstrap node
204    pub rtt: Option<Duration>,
205    /// Number of successful coordinations via this node
206    pub coordination_count: u32,
207}
208
209impl BootstrapNode {
210    /// Create a new bootstrap node
211    pub fn new(address: SocketAddr) -> Self {
212        Self {
213            address,
214            last_seen: std::time::Instant::now(),
215            can_coordinate: true,
216            rtt: None,
217            coordination_count: 0,
218        }
219    }
220}
221
222/// A candidate pair for hole punching (ICE-like)
223#[derive(Debug, Clone)]
224pub struct CandidatePair {
225    /// Local candidate address
226    pub local_candidate: CandidateAddress,
227    /// Remote candidate address
228    pub remote_candidate: CandidateAddress,
229    /// Combined priority for this pair
230    pub priority: u64,
231    /// Current state of this candidate pair
232    pub state: CandidatePairState,
233}
234
235/// State of a candidate pair during hole punching
236#[derive(Debug, Clone, Copy, PartialEq, Eq)]
237pub enum CandidatePairState {
238    /// Waiting to be checked
239    Waiting,
240    /// Currently being checked
241    InProgress,
242    /// Check succeeded
243    Succeeded,
244    /// Check failed
245    Failed,
246    /// Cancelled due to higher priority success
247    Cancelled,
248}
249
250/// Active NAT traversal session state
251#[derive(Debug)]
252struct NatTraversalSession {
253    /// Target peer we're trying to connect to
254    peer_id: PeerId,
255    /// Coordinator being used for this session
256    coordinator: SocketAddr,
257    /// Current attempt number
258    attempt: u32,
259    /// Session start time
260    started_at: std::time::Instant,
261    /// Current phase of traversal
262    phase: TraversalPhase,
263    /// Discovered candidate addresses
264    candidates: Vec<CandidateAddress>,
265    /// Session state machine
266    session_state: SessionState,
267}
268
269/// Session state machine for tracking connection lifecycle
270#[derive(Debug, Clone)]
271pub struct SessionState {
272    /// Current connection state
273    pub state: ConnectionState,
274    /// Last state transition time
275    pub last_transition: std::time::Instant,
276    /// Connection handle if established
277    pub connection: Option<QuinnConnection>,
278    /// Active connection attempts
279    pub active_attempts: Vec<(SocketAddr, std::time::Instant)>,
280    /// Connection quality metrics
281    pub metrics: ConnectionMetrics,
282}
283
284/// Connection state in the session lifecycle
285#[derive(Debug, Clone, Copy, PartialEq, Eq)]
286pub enum ConnectionState {
287    /// Not connected, no active attempts
288    Idle,
289    /// Actively attempting to connect
290    Connecting,
291    /// Connection established and active
292    Connected,
293    /// Connection is migrating to new path
294    Migrating,
295    /// Connection closed or failed
296    Closed,
297}
298
299/// Connection quality metrics
300#[derive(Debug, Clone, Default)]
301pub struct ConnectionMetrics {
302    /// Round-trip time estimate
303    pub rtt: Option<Duration>,
304    /// Packet loss rate (0.0 - 1.0)
305    pub loss_rate: f64,
306    /// Bytes sent
307    pub bytes_sent: u64,
308    /// Bytes received
309    pub bytes_received: u64,
310    /// Last activity timestamp
311    pub last_activity: Option<std::time::Instant>,
312}
313
314/// Session state update notification
315#[derive(Debug, Clone)]
316pub struct SessionStateUpdate {
317    /// Peer ID for this session
318    pub peer_id: PeerId,
319    /// Previous connection state
320    pub old_state: ConnectionState,
321    /// New connection state
322    pub new_state: ConnectionState,
323    /// Reason for state change
324    pub reason: StateChangeReason,
325}
326
327/// Reason for connection state change
328#[derive(Debug, Clone, Copy, PartialEq, Eq)]
329pub enum StateChangeReason {
330    /// Connection attempt timed out
331    Timeout,
332    /// Connection successfully established
333    ConnectionEstablished,
334    /// Connection was closed
335    ConnectionClosed,
336    /// Connection migration completed
337    MigrationComplete,
338    /// Connection migration failed
339    MigrationFailed,
340    /// Connection lost due to network error
341    NetworkError,
342    /// Explicit close requested
343    UserClosed,
344}
345
346/// Phases of NAT traversal process
347#[derive(Debug, Clone, Copy, PartialEq, Eq)]
348pub enum TraversalPhase {
349    /// Discovering local candidates
350    Discovery,
351    /// Requesting coordination from bootstrap
352    Coordination,
353    /// Waiting for peer coordination
354    Synchronization,
355    /// Active hole punching
356    Punching,
357    /// Validating established paths
358    Validation,
359    /// Successfully connected
360    Connected,
361    /// Failed, may retry or fallback
362    Failed,
363}
364
365/// Session state update types for polling
366#[derive(Debug, Clone, Copy)]
367enum SessionUpdate {
368    /// Connection attempt timed out
369    Timeout,
370    /// Connection was disconnected
371    Disconnected,
372    /// Update connection metrics
373    UpdateMetrics,
374    /// Session is in an invalid state
375    InvalidState,
376    /// Should retry the connection
377    Retry,
378    /// Migration timeout occurred
379    MigrationTimeout,
380    /// Remove the session entirely
381    Remove,
382}
383
384/// Address candidate discovered during NAT traversal
385#[derive(Debug, Clone)]
386pub struct CandidateAddress {
387    /// The candidate address
388    pub address: SocketAddr,
389    /// Priority for ICE-like selection
390    pub priority: u32,
391    /// How this candidate was discovered
392    pub source: CandidateSource,
393    /// Current validation state
394    pub state: CandidateState,
395}
396
397impl CandidateAddress {
398    /// Create a new candidate address with validation
399    pub fn new(
400        address: SocketAddr,
401        priority: u32,
402        source: CandidateSource,
403    ) -> Result<Self, CandidateValidationError> {
404        Self::validate_address(&address)?;
405        Ok(Self {
406            address,
407            priority,
408            source,
409            state: CandidateState::New,
410        })
411    }
412
413    /// Validate a candidate address for security and correctness
414    pub fn validate_address(addr: &SocketAddr) -> Result<(), CandidateValidationError> {
415        // Port validation
416        if addr.port() == 0 {
417            return Err(CandidateValidationError::InvalidPort(0));
418        }
419
420        // Well-known port validation (allow for testing)
421        #[cfg(not(test))]
422        if addr.port() < 1024 {
423            return Err(CandidateValidationError::PrivilegedPort(addr.port()));
424        }
425
426        match addr.ip() {
427            std::net::IpAddr::V4(ipv4) => {
428                // IPv4 validation
429                if ipv4.is_unspecified() {
430                    return Err(CandidateValidationError::UnspecifiedAddress);
431                }
432                if ipv4.is_broadcast() {
433                    return Err(CandidateValidationError::BroadcastAddress);
434                }
435                if ipv4.is_multicast() {
436                    return Err(CandidateValidationError::MulticastAddress);
437                }
438                // 0.0.0.0/8 - Current network
439                if ipv4.octets()[0] == 0 {
440                    return Err(CandidateValidationError::ReservedAddress);
441                }
442                // 224.0.0.0/3 - Reserved for future use
443                if ipv4.octets()[0] >= 240 {
444                    return Err(CandidateValidationError::ReservedAddress);
445                }
446            }
447            std::net::IpAddr::V6(ipv6) => {
448                // IPv6 validation
449                if ipv6.is_unspecified() {
450                    return Err(CandidateValidationError::UnspecifiedAddress);
451                }
452                if ipv6.is_multicast() {
453                    return Err(CandidateValidationError::MulticastAddress);
454                }
455                // Documentation prefix (2001:db8::/32)
456                let segments = ipv6.segments();
457                if segments[0] == 0x2001 && segments[1] == 0x0db8 {
458                    return Err(CandidateValidationError::DocumentationAddress);
459                }
460                // IPv4-mapped IPv6 addresses (::ffff:0:0/96)
461                if ipv6.to_ipv4_mapped().is_some() {
462                    return Err(CandidateValidationError::IPv4MappedAddress);
463                }
464            }
465        }
466
467        Ok(())
468    }
469
470    /// Check if this candidate is suitable for NAT traversal
471    pub fn is_suitable_for_nat_traversal(&self) -> bool {
472        match self.address.ip() {
473            std::net::IpAddr::V4(ipv4) => {
474                // For NAT traversal, we want:
475                // - Not loopback (unless testing)
476                // - Not link-local (169.254.0.0/16)
477                // - Not multicast/broadcast
478                #[cfg(test)]
479                if ipv4.is_loopback() {
480                    return true;
481                }
482                !ipv4.is_loopback()
483                    && !ipv4.is_link_local()
484                    && !ipv4.is_multicast()
485                    && !ipv4.is_broadcast()
486            }
487            std::net::IpAddr::V6(ipv6) => {
488                // For IPv6:
489                // - Not loopback (unless testing)
490                // - Not link-local (fe80::/10)
491                // - Not unique local (fc00::/7) for external traversal
492                // - Not multicast
493                #[cfg(test)]
494                if ipv6.is_loopback() {
495                    return true;
496                }
497                let segments = ipv6.segments();
498                let is_link_local = (segments[0] & 0xffc0) == 0xfe80;
499                let is_unique_local = (segments[0] & 0xfe00) == 0xfc00;
500
501                !ipv6.is_loopback() && !is_link_local && !is_unique_local && !ipv6.is_multicast()
502            }
503        }
504    }
505
506    /// Get the priority adjusted for the current state
507    pub fn effective_priority(&self) -> u32 {
508        match self.state {
509            CandidateState::Valid => self.priority,
510            CandidateState::New => self.priority.saturating_sub(10),
511            CandidateState::Validating => self.priority.saturating_sub(5),
512            CandidateState::Failed => 0,
513            CandidateState::Removed => 0,
514        }
515    }
516}
517
518/// Errors that can occur during candidate address validation
519#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
520pub enum CandidateValidationError {
521    /// Port number is invalid
522    #[error("invalid port number: {0}")]
523    InvalidPort(u16),
524    /// Port is in privileged range (< 1024)
525    #[error("privileged port not allowed: {0}")]
526    PrivilegedPort(u16),
527    /// Address is unspecified (0.0.0.0 or ::)
528    #[error("unspecified address not allowed")]
529    UnspecifiedAddress,
530    /// Address is broadcast (IPv4 only)
531    #[error("broadcast address not allowed")]
532    BroadcastAddress,
533    /// Address is multicast
534    #[error("multicast address not allowed")]
535    MulticastAddress,
536    /// Address is reserved
537    #[error("reserved address not allowed")]
538    ReservedAddress,
539    /// Address is documentation prefix
540    #[error("documentation address not allowed")]
541    DocumentationAddress,
542    /// IPv4-mapped IPv6 address
543    #[error("IPv4-mapped IPv6 address not allowed")]
544    IPv4MappedAddress,
545}
546
547/// Events generated during NAT traversal process
548#[derive(Debug, Clone)]
549pub enum NatTraversalEvent {
550    /// New candidate address discovered
551    CandidateDiscovered {
552        peer_id: PeerId,
553        candidate: CandidateAddress,
554    },
555    /// Coordination request sent to bootstrap
556    CoordinationRequested {
557        peer_id: PeerId,
558        coordinator: SocketAddr,
559    },
560    /// Peer coordination synchronized
561    CoordinationSynchronized { peer_id: PeerId, round_id: VarInt },
562    /// Hole punching started
563    HolePunchingStarted {
564        peer_id: PeerId,
565        targets: Vec<SocketAddr>,
566    },
567    /// Path validated successfully
568    PathValidated {
569        peer_id: PeerId,
570        address: SocketAddr,
571        rtt: Duration,
572    },
573    /// Candidate validated successfully
574    CandidateValidated {
575        peer_id: PeerId,
576        candidate_address: SocketAddr,
577    },
578    /// NAT traversal completed successfully
579    TraversalSucceeded {
580        peer_id: PeerId,
581        final_address: SocketAddr,
582        total_time: Duration,
583    },
584    /// Connection established after NAT traversal
585    ConnectionEstablished {
586        peer_id: PeerId,
587        /// The socket address where the connection was established
588        remote_address: SocketAddr,
589    },
590    /// NAT traversal failed
591    TraversalFailed {
592        /// The peer ID that failed to connect
593        peer_id: PeerId,
594        /// The NAT traversal error that occurred
595        error: NatTraversalError,
596        /// Whether fallback mechanisms are available
597        fallback_available: bool,
598    },
599    /// Connection lost
600    ConnectionLost { peer_id: PeerId, reason: String },
601    /// Phase transition in NAT traversal state machine
602    PhaseTransition {
603        peer_id: PeerId,
604        from_phase: TraversalPhase,
605        to_phase: TraversalPhase,
606    },
607    /// Session state changed
608    SessionStateChanged {
609        peer_id: PeerId,
610        new_state: ConnectionState,
611    },
612}
613
614/// Errors that can occur during NAT traversal
615#[derive(Debug, Clone)]
616pub enum NatTraversalError {
617    /// No bootstrap nodes available
618    NoBootstrapNodes,
619    /// Failed to discover any candidates
620    NoCandidatesFound,
621    /// Candidate discovery failed
622    CandidateDiscoveryFailed(String),
623    /// Coordination with bootstrap failed
624    CoordinationFailed(String),
625    /// All hole punching attempts failed
626    HolePunchingFailed,
627    /// Hole punching failed with specific reason
628    PunchingFailed(String),
629    /// Path validation failed
630    ValidationFailed(String),
631    /// Connection validation timed out
632    ValidationTimeout,
633    /// Network error during traversal
634    NetworkError(String),
635    /// Configuration error
636    ConfigError(String),
637    /// Internal protocol error
638    ProtocolError(String),
639    /// NAT traversal timed out
640    Timeout,
641    /// Connection failed after successful traversal
642    ConnectionFailed(String),
643    /// General traversal failure
644    TraversalFailed(String),
645    /// Peer not connected
646    PeerNotConnected,
647}
648
649impl Default for NatTraversalConfig {
650    fn default() -> Self {
651        Self {
652            role: EndpointRole::Client,
653            bootstrap_nodes: Vec::new(),
654            max_candidates: 8,
655            coordination_timeout: Duration::from_secs(10),
656            enable_symmetric_nat: true,
657            enable_relay_fallback: true,
658            max_concurrent_attempts: 3,
659            bind_addr: None,
660            prefer_rfc_nat_traversal: true, // Default to RFC format for standards compliance
661            timeouts: crate::config::nat_timeouts::TimeoutConfig::default(),
662        }
663    }
664}
665
666impl ConfigValidator for NatTraversalConfig {
667    fn validate(&self) -> ValidationResult<()> {
668        use crate::config::validation::*;
669
670        // Validate role-specific requirements
671        match self.role {
672            EndpointRole::Client => {
673                if self.bootstrap_nodes.is_empty() {
674                    return Err(ConfigValidationError::InvalidRole(
675                        "Client endpoints require at least one bootstrap node".to_string(),
676                    ));
677                }
678            }
679            EndpointRole::Server { can_coordinate } => {
680                if can_coordinate && self.bootstrap_nodes.is_empty() {
681                    return Err(ConfigValidationError::InvalidRole(
682                        "Server endpoints with coordination capability require bootstrap nodes"
683                            .to_string(),
684                    ));
685                }
686            }
687            EndpointRole::Bootstrap => {
688                // Bootstrap nodes don't need other bootstrap nodes
689            }
690        }
691
692        // Validate bootstrap nodes
693        if !self.bootstrap_nodes.is_empty() {
694            validate_bootstrap_nodes(&self.bootstrap_nodes)?;
695        }
696
697        // Validate candidate limits
698        validate_range(self.max_candidates, 1, 256, "max_candidates")?;
699
700        // Validate coordination timeout
701        validate_duration(
702            self.coordination_timeout,
703            Duration::from_millis(100),
704            Duration::from_secs(300),
705            "coordination_timeout",
706        )?;
707
708        // Validate concurrent attempts
709        validate_range(
710            self.max_concurrent_attempts,
711            1,
712            16,
713            "max_concurrent_attempts",
714        )?;
715
716        // Validate configuration compatibility
717        if self.max_concurrent_attempts > self.max_candidates {
718            return Err(ConfigValidationError::IncompatibleConfiguration(
719                "max_concurrent_attempts cannot exceed max_candidates".to_string(),
720            ));
721        }
722
723        if self.role == EndpointRole::Bootstrap && self.enable_relay_fallback {
724            return Err(ConfigValidationError::IncompatibleConfiguration(
725                "Bootstrap nodes should not enable relay fallback".to_string(),
726            ));
727        }
728
729        Ok(())
730    }
731}
732
733impl NatTraversalEndpoint {
734    /// Create a new NAT traversal endpoint with optional event callback
735    pub async fn new(
736        config: NatTraversalConfig,
737        event_callback: Option<Box<dyn Fn(NatTraversalEvent) + Send + Sync>>,
738    ) -> Result<Self, NatTraversalError> {
739        Self::new_impl(config, event_callback).await
740    }
741
742    /// Internal async implementation for production builds
743    async fn new_impl(
744        config: NatTraversalConfig,
745        event_callback: Option<Box<dyn Fn(NatTraversalEvent) + Send + Sync>>,
746    ) -> Result<Self, NatTraversalError> {
747        Self::new_common(config, event_callback).await
748    }
749
750    /// Common implementation for both async and sync versions
751    async fn new_common(
752        config: NatTraversalConfig,
753        event_callback: Option<Box<dyn Fn(NatTraversalEvent) + Send + Sync>>,
754    ) -> Result<Self, NatTraversalError> {
755        // Existing implementation with async support
756        Self::new_shared_logic(config, event_callback).await
757    }
758
759    /// Shared logic for endpoint creation (async version)
760    async fn new_shared_logic(
761        config: NatTraversalConfig,
762        event_callback: Option<Box<dyn Fn(NatTraversalEvent) + Send + Sync>>,
763    ) -> Result<Self, NatTraversalError> {
764        // Validate configuration
765
766        {
767            config
768                .validate()
769                .map_err(|e| NatTraversalError::ConfigError(e.to_string()))?;
770        }
771
772        // Fallback validation for non-production builds
773
774        // Initialize bootstrap nodes
775        let bootstrap_nodes = Arc::new(std::sync::RwLock::new(
776            config
777                .bootstrap_nodes
778                .iter()
779                .map(|&address| BootstrapNode {
780                    address,
781                    last_seen: std::time::Instant::now(),
782                    can_coordinate: true, // Assume true initially
783                    rtt: None,
784                    coordination_count: 0,
785                })
786                .collect(),
787        ));
788
789        // Create candidate discovery manager
790        let discovery_config = DiscoveryConfig {
791            total_timeout: config.coordination_timeout,
792            max_candidates: config.max_candidates,
793            enable_symmetric_prediction: config.enable_symmetric_nat,
794            bound_address: config.bind_addr, // Will be updated with actual address after binding
795            ..DiscoveryConfig::default()
796        };
797
798        let nat_traversal_role = match config.role {
799            EndpointRole::Client => NatTraversalRole::Client,
800            EndpointRole::Server { can_coordinate } => NatTraversalRole::Server {
801                can_relay: can_coordinate,
802            },
803            EndpointRole::Bootstrap => NatTraversalRole::Bootstrap,
804        };
805
806        let discovery_manager = Arc::new(std::sync::Mutex::new(CandidateDiscoveryManager::new(
807            discovery_config,
808        )));
809
810        // Create QUIC endpoint with NAT traversal enabled
811        // Create QUIC endpoint with NAT traversal enabled
812        let (quinn_endpoint, event_tx, local_addr) =
813            Self::create_quinn_endpoint(&config, nat_traversal_role).await?;
814
815        // Update discovery manager with the actual bound address
816        {
817            let mut discovery = discovery_manager.lock().map_err(|_| {
818                NatTraversalError::ProtocolError("Discovery manager lock poisoned".to_string())
819            })?;
820            discovery.set_bound_address(local_addr);
821            info!(
822                "Updated discovery manager with bound address: {}",
823                local_addr
824            );
825        }
826
827        let endpoint = Self {
828            quinn_endpoint: Some(quinn_endpoint.clone()),
829            config: config.clone(),
830            bootstrap_nodes,
831            active_sessions: Arc::new(std::sync::RwLock::new(HashMap::new())),
832            discovery_manager,
833            event_callback,
834            shutdown: Arc::new(AtomicBool::new(false)),
835            event_tx: Some(event_tx.clone()),
836            connections: Arc::new(std::sync::RwLock::new(HashMap::new())),
837            local_peer_id: Self::generate_local_peer_id(),
838            timeout_config: config.timeouts.clone(),
839        };
840
841        // For bootstrap nodes, start accepting connections immediately
842        if matches!(
843            config.role,
844            EndpointRole::Bootstrap | EndpointRole::Server { .. }
845        ) {
846            let endpoint_clone = quinn_endpoint.clone();
847            let shutdown_clone = endpoint.shutdown.clone();
848            let event_tx_clone = event_tx.clone();
849            let connections_clone = endpoint.connections.clone();
850
851            tokio::spawn(async move {
852                Self::accept_connections(
853                    endpoint_clone,
854                    shutdown_clone,
855                    event_tx_clone,
856                    connections_clone,
857                )
858                .await;
859            });
860
861            info!("Started accepting connections for {:?} role", config.role);
862        }
863
864        // Start background discovery polling task
865        let discovery_manager_clone = endpoint.discovery_manager.clone();
866        let shutdown_clone = endpoint.shutdown.clone();
867        let event_tx_clone = event_tx;
868
869        tokio::spawn(async move {
870            Self::poll_discovery(discovery_manager_clone, shutdown_clone, event_tx_clone).await;
871        });
872
873        info!("Started discovery polling task");
874
875        // Start local candidate discovery for our own address
876        {
877            let mut discovery = endpoint.discovery_manager.lock().map_err(|_| {
878                NatTraversalError::ProtocolError("Discovery manager lock poisoned".to_string())
879            })?;
880
881            // Start discovery for our own peer ID to discover local candidates
882            let local_peer_id = endpoint.local_peer_id;
883            let bootstrap_nodes = {
884                let nodes = endpoint.bootstrap_nodes.read().map_err(|_| {
885                    NatTraversalError::ProtocolError("Bootstrap nodes lock poisoned".to_string())
886                })?;
887                nodes.clone()
888            };
889
890            discovery
891                .start_discovery(local_peer_id, bootstrap_nodes)
892                .map_err(|e| NatTraversalError::CandidateDiscoveryFailed(e.to_string()))?;
893
894            info!(
895                "Started local candidate discovery for peer {:?}",
896                local_peer_id
897            );
898        }
899
900        Ok(endpoint)
901    }
902
903    /// Get the underlying Quinn endpoint
904    pub fn get_quinn_endpoint(&self) -> Option<&crate::high_level::Endpoint> {
905        self.quinn_endpoint.as_ref()
906    }
907
908    /// Get the event callback
909    pub fn get_event_callback(&self) -> Option<&Box<dyn Fn(NatTraversalEvent) + Send + Sync>> {
910        self.event_callback.as_ref()
911    }
912
913    /// Initiate NAT traversal to a peer (returns immediately, progress via events)
914    pub fn initiate_nat_traversal(
915        &self,
916        peer_id: PeerId,
917        coordinator: SocketAddr,
918    ) -> Result<(), NatTraversalError> {
919        info!(
920            "Starting NAT traversal to peer {:?} via coordinator {}",
921            peer_id, coordinator
922        );
923
924        // Create new session
925        let session = NatTraversalSession {
926            peer_id,
927            coordinator,
928            attempt: 1,
929            started_at: std::time::Instant::now(),
930            phase: TraversalPhase::Discovery,
931            candidates: Vec::new(),
932            session_state: SessionState {
933                state: ConnectionState::Connecting,
934                last_transition: std::time::Instant::now(),
935
936                connection: None,
937                active_attempts: Vec::new(),
938                metrics: ConnectionMetrics::default(),
939            },
940        };
941
942        // Store session
943        {
944            let mut sessions = self
945                .active_sessions
946                .write()
947                .map_err(|_| NatTraversalError::ProtocolError("Lock poisoned".to_string()))?;
948            sessions.insert(peer_id, session);
949        }
950
951        // Start candidate discovery
952        let bootstrap_nodes_vec = {
953            let bootstrap_nodes = self
954                .bootstrap_nodes
955                .read()
956                .map_err(|_| NatTraversalError::ProtocolError("Lock poisoned".to_string()))?;
957            bootstrap_nodes.clone()
958        };
959
960        {
961            let mut discovery = self.discovery_manager.lock().map_err(|_| {
962                NatTraversalError::ProtocolError("Discovery manager lock poisoned".to_string())
963            })?;
964
965            discovery
966                .start_discovery(peer_id, bootstrap_nodes_vec)
967                .map_err(|e| NatTraversalError::CandidateDiscoveryFailed(e.to_string()))?;
968        }
969
970        // Emit event
971        if let Some(ref callback) = self.event_callback {
972            callback(NatTraversalEvent::CoordinationRequested {
973                peer_id,
974                coordinator,
975            });
976        }
977
978        // NAT traversal will proceed via poll() calls and state machine updates
979        Ok(())
980    }
981
982    /// Poll all active sessions and update their states
983    pub fn poll_sessions(&self) -> Result<Vec<SessionStateUpdate>, NatTraversalError> {
984        let mut updates = Vec::new();
985        let now = std::time::Instant::now();
986
987        let mut sessions = self
988            .active_sessions
989            .write()
990            .map_err(|_| NatTraversalError::ProtocolError("Sessions lock poisoned".to_string()))?;
991
992        for (peer_id, session) in sessions.iter_mut() {
993            let mut state_changed = false;
994
995            match session.session_state.state {
996                ConnectionState::Connecting => {
997                    // Check connection timeout
998                    let elapsed = now.duration_since(session.session_state.last_transition);
999                    if elapsed
1000                        > self
1001                            .timeout_config
1002                            .nat_traversal
1003                            .connection_establishment_timeout
1004                    {
1005                        session.session_state.state = ConnectionState::Closed;
1006                        session.session_state.last_transition = now;
1007                        state_changed = true;
1008
1009                        updates.push(SessionStateUpdate {
1010                            peer_id: *peer_id,
1011                            old_state: ConnectionState::Connecting,
1012                            new_state: ConnectionState::Closed,
1013                            reason: StateChangeReason::Timeout,
1014                        });
1015                    }
1016
1017                    // Check if any connection attempts succeeded
1018
1019                    if let Some(ref _connection) = session.session_state.connection {
1020                        session.session_state.state = ConnectionState::Connected;
1021                        session.session_state.last_transition = now;
1022                        state_changed = true;
1023
1024                        updates.push(SessionStateUpdate {
1025                            peer_id: *peer_id,
1026                            old_state: ConnectionState::Connecting,
1027                            new_state: ConnectionState::Connected,
1028                            reason: StateChangeReason::ConnectionEstablished,
1029                        });
1030                    }
1031                }
1032                ConnectionState::Connected => {
1033                    // Check connection health
1034
1035                    {
1036                        // TODO: Implement proper connection health check
1037                        // For now, just update metrics
1038                    }
1039
1040                    // Update metrics
1041                    session.session_state.metrics.last_activity = Some(now);
1042                }
1043                ConnectionState::Migrating => {
1044                    // Check migration timeout
1045                    let elapsed = now.duration_since(session.session_state.last_transition);
1046                    if elapsed > Duration::from_secs(10) {
1047                        // Migration timed out, return to connected or close
1048
1049                        if session.session_state.connection.is_some() {
1050                            session.session_state.state = ConnectionState::Connected;
1051                            state_changed = true;
1052
1053                            updates.push(SessionStateUpdate {
1054                                peer_id: *peer_id,
1055                                old_state: ConnectionState::Migrating,
1056                                new_state: ConnectionState::Connected,
1057                                reason: StateChangeReason::MigrationComplete,
1058                            });
1059                        } else {
1060                            session.session_state.state = ConnectionState::Closed;
1061                            state_changed = true;
1062
1063                            updates.push(SessionStateUpdate {
1064                                peer_id: *peer_id,
1065                                old_state: ConnectionState::Migrating,
1066                                new_state: ConnectionState::Closed,
1067                                reason: StateChangeReason::MigrationFailed,
1068                            });
1069                        }
1070
1071                        session.session_state.last_transition = now;
1072                    }
1073                }
1074                _ => {}
1075            }
1076
1077            // Emit events for state changes
1078            if state_changed {
1079                if let Some(ref callback) = self.event_callback {
1080                    callback(NatTraversalEvent::SessionStateChanged {
1081                        peer_id: *peer_id,
1082                        new_state: session.session_state.state,
1083                    });
1084                }
1085            }
1086        }
1087
1088        Ok(updates)
1089    }
1090
1091    /// Start periodic session polling task
1092    pub fn start_session_polling(&self, interval: Duration) -> tokio::task::JoinHandle<()> {
1093        let sessions = self.active_sessions.clone();
1094        let shutdown = self.shutdown.clone();
1095        let timeout_config = self.timeout_config.clone();
1096
1097        tokio::spawn(async move {
1098            let mut ticker = tokio::time::interval(interval);
1099
1100            loop {
1101                ticker.tick().await;
1102
1103                if shutdown.load(Ordering::Relaxed) {
1104                    break;
1105                }
1106
1107                // Poll sessions and handle updates
1108                let sessions_to_update = {
1109                    match sessions.read() {
1110                        Ok(sessions_guard) => {
1111                            sessions_guard
1112                                .iter()
1113                                .filter_map(|(peer_id, session)| {
1114                                    let now = std::time::Instant::now();
1115                                    let elapsed =
1116                                        now.duration_since(session.session_state.last_transition);
1117
1118                                    match session.session_state.state {
1119                                        ConnectionState::Connecting => {
1120                                            // Check for connection timeout
1121                                            if elapsed
1122                                                > timeout_config
1123                                                    .nat_traversal
1124                                                    .connection_establishment_timeout
1125                                            {
1126                                                Some((*peer_id, SessionUpdate::Timeout))
1127                                            } else {
1128                                                None
1129                                            }
1130                                        }
1131                                        ConnectionState::Connected => {
1132                                            // Check if connection is still alive
1133                                            if let Some(ref conn) = session.session_state.connection
1134                                            {
1135                                                if conn.close_reason().is_some() {
1136                                                    Some((*peer_id, SessionUpdate::Disconnected))
1137                                                } else {
1138                                                    // Update metrics
1139                                                    Some((*peer_id, SessionUpdate::UpdateMetrics))
1140                                                }
1141                                            } else {
1142                                                Some((*peer_id, SessionUpdate::InvalidState))
1143                                            }
1144                                        }
1145                                        ConnectionState::Idle => {
1146                                            // Check if we should retry
1147                                            if elapsed
1148                                                > timeout_config
1149                                                    .discovery
1150                                                    .server_reflexive_cache_ttl
1151                                            {
1152                                                Some((*peer_id, SessionUpdate::Retry))
1153                                            } else {
1154                                                None
1155                                            }
1156                                        }
1157                                        ConnectionState::Migrating => {
1158                                            // Check migration timeout
1159                                            if elapsed > timeout_config.nat_traversal.probe_timeout
1160                                            {
1161                                                Some((*peer_id, SessionUpdate::MigrationTimeout))
1162                                            } else {
1163                                                None
1164                                            }
1165                                        }
1166                                        ConnectionState::Closed => {
1167                                            // Clean up old closed sessions
1168                                            if elapsed
1169                                                > timeout_config.discovery.interface_cache_ttl
1170                                            {
1171                                                Some((*peer_id, SessionUpdate::Remove))
1172                                            } else {
1173                                                None
1174                                            }
1175                                        }
1176                                    }
1177                                })
1178                                .collect::<Vec<_>>()
1179                        }
1180                        _ => {
1181                            vec![]
1182                        }
1183                    }
1184                };
1185
1186                // Apply updates
1187                if !sessions_to_update.is_empty() {
1188                    if let Ok(mut sessions_guard) = sessions.write() {
1189                        for (peer_id, update) in sessions_to_update {
1190                            match update {
1191                                SessionUpdate::Timeout => {
1192                                    if let Some(session) = sessions_guard.get_mut(&peer_id) {
1193                                        session.session_state.state = ConnectionState::Closed;
1194                                        session.session_state.last_transition =
1195                                            std::time::Instant::now();
1196                                        tracing::warn!("Connection to {:?} timed out", peer_id);
1197                                    }
1198                                }
1199                                SessionUpdate::Disconnected => {
1200                                    if let Some(session) = sessions_guard.get_mut(&peer_id) {
1201                                        session.session_state.state = ConnectionState::Closed;
1202                                        session.session_state.last_transition =
1203                                            std::time::Instant::now();
1204                                        session.session_state.connection = None;
1205                                        tracing::info!("Connection to {:?} closed", peer_id);
1206                                    }
1207                                }
1208                                SessionUpdate::UpdateMetrics => {
1209                                    if let Some(session) = sessions_guard.get_mut(&peer_id) {
1210                                        if let Some(ref conn) = session.session_state.connection {
1211                                            // Update RTT and other metrics
1212                                            let stats = conn.stats();
1213                                            session.session_state.metrics.rtt =
1214                                                Some(stats.path.rtt);
1215                                            session.session_state.metrics.loss_rate =
1216                                                stats.path.lost_packets as f64
1217                                                    / stats.path.sent_packets.max(1) as f64;
1218                                        }
1219                                    }
1220                                }
1221                                SessionUpdate::InvalidState => {
1222                                    if let Some(session) = sessions_guard.get_mut(&peer_id) {
1223                                        session.session_state.state = ConnectionState::Closed;
1224                                        session.session_state.last_transition =
1225                                            std::time::Instant::now();
1226                                        tracing::error!("Session {:?} in invalid state", peer_id);
1227                                    }
1228                                }
1229                                SessionUpdate::Retry => {
1230                                    if let Some(session) = sessions_guard.get_mut(&peer_id) {
1231                                        session.session_state.state = ConnectionState::Connecting;
1232                                        session.session_state.last_transition =
1233                                            std::time::Instant::now();
1234                                        session.attempt += 1;
1235                                        tracing::info!(
1236                                            "Retrying connection to {:?} (attempt {})",
1237                                            peer_id,
1238                                            session.attempt
1239                                        );
1240                                    }
1241                                }
1242                                SessionUpdate::MigrationTimeout => {
1243                                    if let Some(session) = sessions_guard.get_mut(&peer_id) {
1244                                        session.session_state.state = ConnectionState::Closed;
1245                                        session.session_state.last_transition =
1246                                            std::time::Instant::now();
1247                                        tracing::warn!("Migration timeout for {:?}", peer_id);
1248                                    }
1249                                }
1250                                SessionUpdate::Remove => {
1251                                    sessions_guard.remove(&peer_id);
1252                                    tracing::debug!("Removed old session for {:?}", peer_id);
1253                                }
1254                            }
1255                        }
1256                    }
1257                }
1258            }
1259        })
1260    }
1261
1262    /// Manually inject an observed address (for testing/integration)
1263    /// This method simulates receiving an OBSERVED_ADDRESS frame
1264    pub fn inject_observed_address(
1265        &self,
1266        observed_address: SocketAddr,
1267        _from_peer: PeerId,
1268    ) -> Result<(), NatTraversalError> {
1269        info!("Injecting observed address {}", observed_address);
1270
1271        // Feed the address to the discovery manager
1272        let mut discovery = self.discovery_manager.lock().map_err(|_| {
1273            NatTraversalError::ProtocolError("Discovery manager lock poisoned".to_string())
1274        })?;
1275
1276        // Use a special peer ID to represent our own discovered address
1277        let our_peer_id = self.local_peer_id;
1278
1279        // Accept the QUIC-discovered address
1280        match discovery.accept_quic_discovered_address(our_peer_id, observed_address) {
1281            Ok(()) => {
1282                info!(
1283                    "Successfully accepted observed address: {}",
1284                    observed_address
1285                );
1286
1287                // Emit event for the application
1288                if let Some(ref event_tx) = self.event_tx {
1289                    let _ = event_tx.send(NatTraversalEvent::CandidateValidated {
1290                        peer_id: our_peer_id,
1291                        candidate_address: observed_address,
1292                    });
1293                }
1294
1295                Ok(())
1296            }
1297            Err(e) => {
1298                warn!(
1299                    "Failed to accept observed address {}: {}",
1300                    observed_address, e
1301                );
1302                Err(NatTraversalError::CandidateDiscoveryFailed(e.to_string()))
1303            }
1304        }
1305    }
1306
1307    /// Get current NAT traversal statistics
1308    pub fn get_statistics(&self) -> Result<NatTraversalStatistics, NatTraversalError> {
1309        let sessions = self
1310            .active_sessions
1311            .read()
1312            .map_err(|_| NatTraversalError::ProtocolError("Lock poisoned".to_string()))?;
1313        let bootstrap_nodes = self
1314            .bootstrap_nodes
1315            .read()
1316            .map_err(|_| NatTraversalError::ProtocolError("Lock poisoned".to_string()))?;
1317
1318        // Calculate average coordination time based on bootstrap node RTTs
1319        let avg_coordination_time = {
1320            let rtts: Vec<Duration> = bootstrap_nodes.iter().filter_map(|b| b.rtt).collect();
1321
1322            if rtts.is_empty() {
1323                Duration::from_millis(500) // Default if no RTT data available
1324            } else {
1325                let total_millis: u64 = rtts.iter().map(|d| d.as_millis() as u64).sum();
1326                Duration::from_millis(total_millis / rtts.len() as u64 * 2) // Multiply by 2 for round-trip coordination
1327            }
1328        };
1329
1330        Ok(NatTraversalStatistics {
1331            active_sessions: sessions.len(),
1332            total_bootstrap_nodes: bootstrap_nodes.len(),
1333            successful_coordinations: bootstrap_nodes.iter().map(|b| b.coordination_count).sum(),
1334            average_coordination_time: avg_coordination_time,
1335            total_attempts: 0,
1336            successful_connections: 0,
1337            direct_connections: 0,
1338            relayed_connections: 0,
1339        })
1340    }
1341
1342    /// Add a new bootstrap node
1343    pub fn add_bootstrap_node(&self, address: SocketAddr) -> Result<(), NatTraversalError> {
1344        let mut bootstrap_nodes = self
1345            .bootstrap_nodes
1346            .write()
1347            .map_err(|_| NatTraversalError::ProtocolError("Lock poisoned".to_string()))?;
1348
1349        // Check if already exists
1350        if !bootstrap_nodes.iter().any(|b| b.address == address) {
1351            bootstrap_nodes.push(BootstrapNode {
1352                address,
1353                last_seen: std::time::Instant::now(),
1354                can_coordinate: true,
1355                rtt: None,
1356                coordination_count: 0,
1357            });
1358            info!("Added bootstrap node: {}", address);
1359        }
1360        Ok(())
1361    }
1362
1363    /// Remove a bootstrap node
1364    pub fn remove_bootstrap_node(&self, address: SocketAddr) -> Result<(), NatTraversalError> {
1365        let mut bootstrap_nodes = self
1366            .bootstrap_nodes
1367            .write()
1368            .map_err(|_| NatTraversalError::ProtocolError("Lock poisoned".to_string()))?;
1369        bootstrap_nodes.retain(|b| b.address != address);
1370        info!("Removed bootstrap node: {}", address);
1371        Ok(())
1372    }
1373
1374    // Private implementation methods
1375
1376    /// Create a Quinn endpoint with NAT traversal configured (async version)
1377    async fn create_quinn_endpoint(
1378        config: &NatTraversalConfig,
1379        _nat_role: NatTraversalRole,
1380    ) -> Result<
1381        (
1382            QuinnEndpoint,
1383            mpsc::UnboundedSender<NatTraversalEvent>,
1384            SocketAddr,
1385        ),
1386        NatTraversalError,
1387    > {
1388        use std::sync::Arc;
1389
1390        // Create server config if this is a coordinator/bootstrap node
1391        let server_config = match config.role {
1392            EndpointRole::Bootstrap | EndpointRole::Server { .. } => {
1393                // Production certificate management
1394                let cert_config = CertificateConfig {
1395                    common_name: format!("ant-quic-{}", config.role.name()),
1396                    subject_alt_names: vec!["localhost".to_string(), "ant-quic-node".to_string()],
1397                    self_signed: true, // Use self-signed for P2P networks
1398                    ..CertificateConfig::default()
1399                };
1400
1401                let cert_manager = CertificateManager::new(cert_config).map_err(|e| {
1402                    NatTraversalError::ConfigError(format!(
1403                        "Certificate manager creation failed: {e}"
1404                    ))
1405                })?;
1406
1407                let cert_bundle = cert_manager.generate_certificate().map_err(|e| {
1408                    NatTraversalError::ConfigError(format!("Certificate generation failed: {e}"))
1409                })?;
1410
1411                let rustls_config =
1412                    cert_manager
1413                        .create_server_config(&cert_bundle)
1414                        .map_err(|e| {
1415                            NatTraversalError::ConfigError(format!(
1416                                "Server config creation failed: {e}"
1417                            ))
1418                        })?;
1419
1420                let server_crypto = QuicServerConfig::try_from(rustls_config.as_ref().clone())
1421                    .map_err(|e| NatTraversalError::ConfigError(e.to_string()))?;
1422
1423                let mut server_config = ServerConfig::with_crypto(Arc::new(server_crypto));
1424
1425                // Configure transport parameters for NAT traversal
1426                let mut transport_config = TransportConfig::default();
1427                transport_config
1428                    .keep_alive_interval(Some(config.timeouts.nat_traversal.retry_interval));
1429                transport_config.max_idle_timeout(Some(crate::VarInt::from_u32(30000).into()));
1430
1431                // Enable NAT traversal in transport parameters
1432                // Per draft-seemann-quic-nat-traversal-02:
1433                // - Client sends empty parameter
1434                // - Server sends concurrency limit
1435                let nat_config = match config.role {
1436                    EndpointRole::Client => {
1437                        crate::transport_parameters::NatTraversalConfig::ClientSupport
1438                    }
1439                    EndpointRole::Bootstrap | EndpointRole::Server { .. } => {
1440                        crate::transport_parameters::NatTraversalConfig::ServerSupport {
1441                            concurrency_limit: VarInt::from_u32(
1442                                config.max_concurrent_attempts as u32,
1443                            ),
1444                        }
1445                    }
1446                };
1447                transport_config.nat_traversal_config(Some(nat_config));
1448
1449                server_config.transport_config(Arc::new(transport_config));
1450
1451                Some(server_config)
1452            }
1453            _ => None,
1454        };
1455
1456        // Create client config for outgoing connections
1457        let client_config = {
1458            let cert_config = CertificateConfig {
1459                common_name: format!("ant-quic-{}", config.role.name()),
1460                subject_alt_names: vec!["localhost".to_string(), "ant-quic-node".to_string()],
1461                self_signed: true,
1462                ..CertificateConfig::default()
1463            };
1464
1465            let cert_manager = CertificateManager::new(cert_config).map_err(|e| {
1466                NatTraversalError::ConfigError(format!("Certificate manager creation failed: {e}"))
1467            })?;
1468
1469            let _cert_bundle = cert_manager.generate_certificate().map_err(|e| {
1470                NatTraversalError::ConfigError(format!("Certificate generation failed: {e}"))
1471            })?;
1472
1473            let rustls_config = cert_manager.create_client_config().map_err(|e| {
1474                NatTraversalError::ConfigError(format!("Client config creation failed: {e}"))
1475            })?;
1476
1477            let client_crypto = QuicClientConfig::try_from(rustls_config.as_ref().clone())
1478                .map_err(|e| NatTraversalError::ConfigError(e.to_string()))?;
1479
1480            let mut client_config = ClientConfig::new(Arc::new(client_crypto));
1481
1482            // Configure transport parameters for NAT traversal
1483            let mut transport_config = TransportConfig::default();
1484            transport_config.keep_alive_interval(Some(Duration::from_secs(5)));
1485            transport_config.max_idle_timeout(Some(crate::VarInt::from_u32(30000).into()));
1486
1487            // Enable NAT traversal in transport parameters
1488            // Per draft-seemann-quic-nat-traversal-02:
1489            // - Client sends empty parameter
1490            // - Server sends concurrency limit
1491            let nat_config = match config.role {
1492                EndpointRole::Client => {
1493                    crate::transport_parameters::NatTraversalConfig::ClientSupport
1494                }
1495                EndpointRole::Bootstrap | EndpointRole::Server { .. } => {
1496                    crate::transport_parameters::NatTraversalConfig::ServerSupport {
1497                        concurrency_limit: VarInt::from_u32(config.max_concurrent_attempts as u32),
1498                    }
1499                }
1500            };
1501            transport_config.nat_traversal_config(Some(nat_config));
1502
1503            client_config.transport_config(Arc::new(transport_config));
1504
1505            client_config
1506        };
1507
1508        // Create UDP socket
1509        let bind_addr = config
1510            .bind_addr
1511            .unwrap_or_else(create_random_port_bind_addr);
1512        let socket = UdpSocket::bind(bind_addr).await.map_err(|e| {
1513            NatTraversalError::NetworkError(format!("Failed to bind UDP socket: {e}"))
1514        })?;
1515
1516        info!("Binding endpoint to {}", bind_addr);
1517
1518        // Convert tokio socket to std socket
1519        let std_socket = socket.into_std().map_err(|e| {
1520            NatTraversalError::NetworkError(format!("Failed to convert socket: {e}"))
1521        })?;
1522
1523        // Create Quinn endpoint
1524        let runtime = default_runtime().ok_or_else(|| {
1525            NatTraversalError::ConfigError("No compatible async runtime found".to_string())
1526        })?;
1527
1528        let mut endpoint = QuinnEndpoint::new(
1529            EndpointConfig::default(),
1530            server_config,
1531            std_socket,
1532            runtime,
1533        )
1534        .map_err(|e| {
1535            NatTraversalError::ConfigError(format!("Failed to create Quinn endpoint: {e}"))
1536        })?;
1537
1538        // Set default client config
1539        endpoint.set_default_client_config(client_config);
1540
1541        // Get the actual bound address
1542        let local_addr = endpoint.local_addr().map_err(|e| {
1543            NatTraversalError::NetworkError(format!("Failed to get local address: {e}"))
1544        })?;
1545
1546        info!("Endpoint bound to actual address: {}", local_addr);
1547
1548        // Create event channel
1549        let (event_tx, _event_rx) = mpsc::unbounded_channel();
1550
1551        Ok((endpoint, event_tx, local_addr))
1552    }
1553
1554    /// Start listening for incoming connections (async version)
1555    pub async fn start_listening(&self, bind_addr: SocketAddr) -> Result<(), NatTraversalError> {
1556        let endpoint = self.quinn_endpoint.as_ref().ok_or_else(|| {
1557            NatTraversalError::ConfigError("Quinn endpoint not initialized".to_string())
1558        })?;
1559
1560        // Rebind the endpoint to the specified address
1561        let _socket = UdpSocket::bind(bind_addr).await.map_err(|e| {
1562            NatTraversalError::NetworkError(format!("Failed to bind to {bind_addr}: {e}"))
1563        })?;
1564
1565        info!("Started listening on {}", bind_addr);
1566
1567        // Start accepting connections in a background task
1568        let endpoint_clone = endpoint.clone();
1569        let shutdown_clone = self.shutdown.clone();
1570        let event_tx = self.event_tx.as_ref().unwrap().clone();
1571        let connections_clone = self.connections.clone();
1572
1573        tokio::spawn(async move {
1574            Self::accept_connections(endpoint_clone, shutdown_clone, event_tx, connections_clone)
1575                .await;
1576        });
1577
1578        Ok(())
1579    }
1580
1581    /// Accept incoming connections
1582    async fn accept_connections(
1583        endpoint: QuinnEndpoint,
1584        shutdown: Arc<AtomicBool>,
1585        event_tx: mpsc::UnboundedSender<NatTraversalEvent>,
1586        connections: Arc<std::sync::RwLock<HashMap<PeerId, QuinnConnection>>>,
1587    ) {
1588        while !shutdown.load(Ordering::Relaxed) {
1589            match endpoint.accept().await {
1590                Some(connecting) => {
1591                    let event_tx = event_tx.clone();
1592                    let connections = connections.clone();
1593                    tokio::spawn(async move {
1594                        match connecting.await {
1595                            Ok(connection) => {
1596                                info!("Accepted connection from {}", connection.remote_address());
1597
1598                                // Generate peer ID from connection address
1599                                let peer_id = Self::generate_peer_id_from_address(
1600                                    connection.remote_address(),
1601                                );
1602
1603                                // Store the connection
1604                                if let Ok(mut conns) = connections.write() {
1605                                    conns.insert(peer_id, connection.clone());
1606                                }
1607
1608                                let _ = event_tx.send(NatTraversalEvent::ConnectionEstablished {
1609                                    peer_id,
1610                                    remote_address: connection.remote_address(),
1611                                });
1612
1613                                // Handle connection streams
1614                                Self::handle_connection(connection, event_tx).await;
1615                            }
1616                            Err(e) => {
1617                                debug!("Connection failed: {}", e);
1618                            }
1619                        }
1620                    });
1621                }
1622                None => {
1623                    // Endpoint closed
1624                    break;
1625                }
1626            }
1627        }
1628    }
1629
1630    /// Poll discovery manager in background
1631    async fn poll_discovery(
1632        discovery_manager: Arc<std::sync::Mutex<CandidateDiscoveryManager>>,
1633        shutdown: Arc<AtomicBool>,
1634        _event_tx: mpsc::UnboundedSender<NatTraversalEvent>,
1635    ) {
1636        use tokio::time::{Duration, interval};
1637
1638        let mut poll_interval = interval(Duration::from_millis(100));
1639
1640        while !shutdown.load(Ordering::Relaxed) {
1641            poll_interval.tick().await;
1642
1643            // Poll the discovery manager
1644            let events = match discovery_manager.lock() {
1645                Ok(mut discovery) => discovery.poll(std::time::Instant::now()),
1646                Err(e) => {
1647                    error!("Failed to lock discovery manager: {}", e);
1648                    continue;
1649                }
1650            };
1651
1652            // Process discovery events
1653            for event in events {
1654                match event {
1655                    DiscoveryEvent::DiscoveryStarted {
1656                        peer_id,
1657                        bootstrap_count,
1658                    } => {
1659                        debug!(
1660                            "Discovery started for peer {:?} with {} bootstrap nodes",
1661                            peer_id, bootstrap_count
1662                        );
1663                    }
1664                    DiscoveryEvent::LocalScanningStarted => {
1665                        debug!("Local interface scanning started");
1666                    }
1667                    DiscoveryEvent::LocalCandidateDiscovered { candidate } => {
1668                        debug!("Discovered local candidate: {}", candidate.address);
1669                        // Local candidates are stored in the discovery manager
1670                        // They will be used when specific peers initiate NAT traversal
1671                    }
1672                    DiscoveryEvent::LocalScanningCompleted {
1673                        candidate_count,
1674                        duration,
1675                    } => {
1676                        debug!(
1677                            "Local interface scanning completed: {} candidates in {:?}",
1678                            candidate_count, duration
1679                        );
1680                    }
1681                    DiscoveryEvent::ServerReflexiveDiscoveryStarted { bootstrap_count } => {
1682                        debug!(
1683                            "Server reflexive discovery started with {} bootstrap nodes",
1684                            bootstrap_count
1685                        );
1686                    }
1687                    DiscoveryEvent::ServerReflexiveCandidateDiscovered {
1688                        candidate,
1689                        bootstrap_node,
1690                    } => {
1691                        debug!(
1692                            "Discovered server-reflexive candidate {} via bootstrap {}",
1693                            candidate.address, bootstrap_node
1694                        );
1695                        // Server-reflexive candidates are stored in the discovery manager
1696                    }
1697                    DiscoveryEvent::BootstrapQueryFailed {
1698                        bootstrap_node,
1699                        error,
1700                    } => {
1701                        debug!("Bootstrap query failed for {}: {}", bootstrap_node, error);
1702                    }
1703                    DiscoveryEvent::SymmetricPredictionStarted { base_address } => {
1704                        debug!(
1705                            "Symmetric NAT prediction started from base address {}",
1706                            base_address
1707                        );
1708                    }
1709                    DiscoveryEvent::PredictedCandidateGenerated {
1710                        candidate,
1711                        confidence,
1712                    } => {
1713                        debug!(
1714                            "Predicted symmetric NAT candidate {} with confidence {}",
1715                            candidate.address, confidence
1716                        );
1717                        // Predicted candidates are stored in the discovery manager
1718                    }
1719                    DiscoveryEvent::PortAllocationDetected {
1720                        port,
1721                        source_address,
1722                        bootstrap_node,
1723                        timestamp,
1724                    } => {
1725                        debug!(
1726                            "Port allocation detected: port {} from {} via bootstrap {:?} at {:?}",
1727                            port, source_address, bootstrap_node, timestamp
1728                        );
1729                    }
1730                    DiscoveryEvent::DiscoveryCompleted {
1731                        candidate_count,
1732                        total_duration,
1733                        success_rate,
1734                    } => {
1735                        info!(
1736                            "Discovery completed with {} candidates in {:?} (success rate: {:.2}%)",
1737                            candidate_count,
1738                            total_duration,
1739                            success_rate * 100.0
1740                        );
1741                        // Discovery completion is tracked internally in the discovery manager
1742                        // The candidates will be used when NAT traversal is initiated for specific peers
1743                    }
1744                    DiscoveryEvent::DiscoveryFailed {
1745                        error,
1746                        partial_results,
1747                    } => {
1748                        warn!(
1749                            "Discovery failed: {} (found {} partial candidates)",
1750                            error,
1751                            partial_results.len()
1752                        );
1753
1754                        // We don't send a TraversalFailed event here because:
1755                        // 1. This is general discovery, not for a specific peer
1756                        // 2. We might have partial results that are still usable
1757                        // 3. The actual NAT traversal attempt will handle failure if needed
1758                    }
1759                    DiscoveryEvent::PathValidationRequested {
1760                        candidate_id,
1761                        candidate_address,
1762                        challenge_token,
1763                    } => {
1764                        debug!(
1765                            "PATH_CHALLENGE requested for candidate {} at {} with token {:08x}",
1766                            candidate_id.0, candidate_address, challenge_token
1767                        );
1768                        // This event is used to trigger sending PATH_CHALLENGE frames
1769                        // The actual sending is handled by the QUIC connection layer
1770                    }
1771                    DiscoveryEvent::PathValidationResponse {
1772                        candidate_id,
1773                        candidate_address,
1774                        challenge_token: _,
1775                        rtt,
1776                    } => {
1777                        debug!(
1778                            "PATH_RESPONSE received for candidate {} at {} with RTT {:?}",
1779                            candidate_id.0, candidate_address, rtt
1780                        );
1781                        // Candidate has been validated with real QUIC path validation
1782                    }
1783                }
1784            }
1785        }
1786
1787        info!("Discovery polling task shutting down");
1788    }
1789
1790    /// Handle an established connection
1791    async fn handle_connection(
1792        connection: QuinnConnection,
1793        event_tx: mpsc::UnboundedSender<NatTraversalEvent>,
1794    ) {
1795        let peer_id = Self::generate_peer_id_from_address(connection.remote_address());
1796        let remote_address = connection.remote_address();
1797
1798        debug!(
1799            "Handling connection from peer {:?} at {}",
1800            peer_id, remote_address
1801        );
1802
1803        // Handle bidirectional and unidirectional streams
1804        loop {
1805            tokio::select! {
1806                stream = connection.accept_bi() => {
1807                    match stream {
1808                        Ok((send, recv)) => {
1809                            tokio::spawn(async move {
1810                                Self::handle_bi_stream(send, recv).await;
1811                            });
1812                        }
1813                        Err(e) => {
1814                            debug!("Error accepting bidirectional stream: {}", e);
1815                            let _ = event_tx.send(NatTraversalEvent::ConnectionLost {
1816                                peer_id,
1817                                reason: format!("Stream error: {e}"),
1818                            });
1819                            break;
1820                        }
1821                    }
1822                }
1823                stream = connection.accept_uni() => {
1824                    match stream {
1825                        Ok(recv) => {
1826                            tokio::spawn(async move {
1827                                Self::handle_uni_stream(recv).await;
1828                            });
1829                        }
1830                        Err(e) => {
1831                            debug!("Error accepting unidirectional stream: {}", e);
1832                            let _ = event_tx.send(NatTraversalEvent::ConnectionLost {
1833                                peer_id,
1834                                reason: format!("Stream error: {e}"),
1835                            });
1836                            break;
1837                        }
1838                    }
1839                }
1840            }
1841        }
1842    }
1843
1844    /// Handle a bidirectional stream
1845    async fn handle_bi_stream(
1846        _send: crate::high_level::SendStream,
1847        _recv: crate::high_level::RecvStream,
1848    ) {
1849        // TODO: Implement bidirectional stream handling
1850        // Note: read() and write_all() methods ARE available on RecvStream and SendStream
1851
1852        /* Original code that uses high-level API:
1853        let mut buffer = vec![0u8; 1024];
1854
1855        loop {
1856            match recv.read(&mut buffer).await {
1857                Ok(Some(size)) => {
1858                    debug!("Received {} bytes on bidirectional stream", size);
1859
1860                    // Echo back the data for now
1861                    if let Err(e) = send.write_all(&buffer[..size]).await {
1862                        debug!("Failed to write to stream: {}", e);
1863                        break;
1864                    }
1865                }
1866                Ok(None) => {
1867                    debug!("Bidirectional stream closed by peer");
1868                    break;
1869                }
1870                Err(e) => {
1871                    debug!("Error reading from bidirectional stream: {}", e);
1872                    break;
1873                }
1874            }
1875        }
1876        */
1877    }
1878
1879    /// Handle a unidirectional stream
1880    async fn handle_uni_stream(mut recv: crate::high_level::RecvStream) {
1881        let mut buffer = vec![0u8; 1024];
1882
1883        loop {
1884            match recv.read(&mut buffer).await {
1885                Ok(Some(size)) => {
1886                    debug!("Received {} bytes on unidirectional stream", size);
1887                    // Process the data
1888                }
1889                Ok(None) => {
1890                    debug!("Unidirectional stream closed by peer");
1891                    break;
1892                }
1893                Err(e) => {
1894                    debug!("Error reading from unidirectional stream: {}", e);
1895                    break;
1896                }
1897            }
1898        }
1899    }
1900
1901    /// Connect to a peer using NAT traversal
1902    pub async fn connect_to_peer(
1903        &self,
1904        peer_id: PeerId,
1905        server_name: &str,
1906        remote_addr: SocketAddr,
1907    ) -> Result<QuinnConnection, NatTraversalError> {
1908        let endpoint = self.quinn_endpoint.as_ref().ok_or_else(|| {
1909            NatTraversalError::ConfigError("Quinn endpoint not initialized".to_string())
1910        })?;
1911
1912        info!("Connecting to peer {:?} at {}", peer_id, remote_addr);
1913
1914        // Attempt connection with timeout
1915        let connecting = endpoint.connect(remote_addr, server_name).map_err(|e| {
1916            NatTraversalError::ConnectionFailed(format!("Failed to initiate connection: {e}"))
1917        })?;
1918
1919        let connection = timeout(
1920            self.timeout_config
1921                .nat_traversal
1922                .connection_establishment_timeout,
1923            connecting,
1924        )
1925        .await
1926        .map_err(|_| NatTraversalError::Timeout)?
1927        .map_err(|e| NatTraversalError::ConnectionFailed(format!("Connection failed: {e}")))?;
1928
1929        info!(
1930            "Successfully connected to peer {:?} at {}",
1931            peer_id, remote_addr
1932        );
1933
1934        // Send event notification
1935        if let Some(ref event_tx) = self.event_tx {
1936            let _ = event_tx.send(NatTraversalEvent::ConnectionEstablished {
1937                peer_id,
1938                remote_address: remote_addr,
1939            });
1940        }
1941
1942        Ok(connection)
1943    }
1944
1945    /// Accept incoming connections on the endpoint
1946    pub async fn accept_connection(&self) -> Result<(PeerId, QuinnConnection), NatTraversalError> {
1947        let endpoint = self.quinn_endpoint.as_ref().ok_or_else(|| {
1948            NatTraversalError::ConfigError("Quinn endpoint not initialized".to_string())
1949        })?;
1950
1951        // Accept incoming connection
1952        let incoming = endpoint
1953            .accept()
1954            .await
1955            .ok_or_else(|| NatTraversalError::NetworkError("Endpoint closed".to_string()))?;
1956
1957        let remote_addr = incoming.remote_address();
1958        info!("Accepting connection from {}", remote_addr);
1959
1960        // Accept the connection
1961        let connection = incoming.await.map_err(|e| {
1962            NatTraversalError::ConnectionFailed(format!("Failed to accept connection: {e}"))
1963        })?;
1964
1965        // Generate or extract peer ID from connection
1966        let peer_id = self
1967            .extract_peer_id_from_connection(&connection)
1968            .await
1969            .unwrap_or_else(|| Self::generate_peer_id_from_address(remote_addr));
1970
1971        // Store the connection
1972        {
1973            let mut connections = self.connections.write().map_err(|_| {
1974                NatTraversalError::ProtocolError("Connections lock poisoned".to_string())
1975            })?;
1976            connections.insert(peer_id, connection.clone());
1977        }
1978
1979        info!(
1980            "Connection accepted from peer {:?} at {}",
1981            peer_id, remote_addr
1982        );
1983
1984        // Send event notification
1985        if let Some(ref event_tx) = self.event_tx {
1986            let _ = event_tx.send(NatTraversalEvent::ConnectionEstablished {
1987                peer_id,
1988                remote_address: remote_addr,
1989            });
1990        }
1991
1992        Ok((peer_id, connection))
1993    }
1994
1995    /// Get the local peer ID
1996    pub fn local_peer_id(&self) -> PeerId {
1997        self.local_peer_id
1998    }
1999
2000    /// Get an active connection by peer ID
2001    pub fn get_connection(
2002        &self,
2003        peer_id: &PeerId,
2004    ) -> Result<Option<QuinnConnection>, NatTraversalError> {
2005        let connections = self.connections.read().map_err(|_| {
2006            NatTraversalError::ProtocolError("Connections lock poisoned".to_string())
2007        })?;
2008        Ok(connections.get(peer_id).cloned())
2009    }
2010
2011    /// Remove a connection by peer ID
2012    pub fn remove_connection(
2013        &self,
2014        peer_id: &PeerId,
2015    ) -> Result<Option<QuinnConnection>, NatTraversalError> {
2016        let mut connections = self.connections.write().map_err(|_| {
2017            NatTraversalError::ProtocolError("Connections lock poisoned".to_string())
2018        })?;
2019        Ok(connections.remove(peer_id))
2020    }
2021
2022    /// List all active connections
2023    pub fn list_connections(&self) -> Result<Vec<(PeerId, SocketAddr)>, NatTraversalError> {
2024        let connections = self.connections.read().map_err(|_| {
2025            NatTraversalError::ProtocolError("Connections lock poisoned".to_string())
2026        })?;
2027        let mut result = Vec::new();
2028        for (peer_id, connection) in connections.iter() {
2029            result.push((*peer_id, connection.remote_address()));
2030        }
2031        Ok(result)
2032    }
2033
2034    /// Handle incoming data from a connection
2035    pub async fn handle_connection_data(
2036        &self,
2037        peer_id: PeerId,
2038        connection: &QuinnConnection,
2039    ) -> Result<(), NatTraversalError> {
2040        info!("Handling connection data from peer {:?}", peer_id);
2041
2042        // Spawn task to handle bidirectional streams
2043        let connection_clone = connection.clone();
2044        let peer_id_clone = peer_id;
2045        tokio::spawn(async move {
2046            loop {
2047                match connection_clone.accept_bi().await {
2048                    Ok((send, recv)) => {
2049                        debug!(
2050                            "Accepted bidirectional stream from peer {:?}",
2051                            peer_id_clone
2052                        );
2053                        tokio::spawn(Self::handle_bi_stream(send, recv));
2054                    }
2055                    Err(ConnectionError::ApplicationClosed(_)) => {
2056                        debug!("Connection closed by peer {:?}", peer_id_clone);
2057                        break;
2058                    }
2059                    Err(e) => {
2060                        debug!(
2061                            "Error accepting bidirectional stream from peer {:?}: {}",
2062                            peer_id_clone, e
2063                        );
2064                        break;
2065                    }
2066                }
2067            }
2068        });
2069
2070        // Spawn task to handle unidirectional streams
2071        let connection_clone = connection.clone();
2072        let peer_id_clone = peer_id;
2073        tokio::spawn(async move {
2074            loop {
2075                match connection_clone.accept_uni().await {
2076                    Ok(recv) => {
2077                        debug!(
2078                            "Accepted unidirectional stream from peer {:?}",
2079                            peer_id_clone
2080                        );
2081                        tokio::spawn(Self::handle_uni_stream(recv));
2082                    }
2083                    Err(ConnectionError::ApplicationClosed(_)) => {
2084                        debug!("Connection closed by peer {:?}", peer_id_clone);
2085                        break;
2086                    }
2087                    Err(e) => {
2088                        debug!(
2089                            "Error accepting unidirectional stream from peer {:?}: {}",
2090                            peer_id_clone, e
2091                        );
2092                        break;
2093                    }
2094                }
2095            }
2096        });
2097
2098        Ok(())
2099    }
2100
2101    /// Generate a local peer ID
2102    fn generate_local_peer_id() -> PeerId {
2103        use std::collections::hash_map::DefaultHasher;
2104        use std::hash::{Hash, Hasher};
2105        use std::time::SystemTime;
2106
2107        let mut hasher = DefaultHasher::new();
2108        SystemTime::now().hash(&mut hasher);
2109        std::process::id().hash(&mut hasher);
2110
2111        let hash = hasher.finish();
2112        let mut peer_id = [0u8; 32];
2113        peer_id[0..8].copy_from_slice(&hash.to_be_bytes());
2114
2115        // Add some randomness
2116        for i in 8..32 {
2117            peer_id[i] = rand::random();
2118        }
2119
2120        PeerId(peer_id)
2121    }
2122
2123    /// Generate a peer ID from a socket address
2124    ///
2125    /// WARNING: This is a fallback method that should only be used when
2126    /// we cannot extract the peer's actual ID from their Ed25519 public key.
2127    /// This generates a non-persistent ID that will change on each connection.
2128    fn generate_peer_id_from_address(addr: SocketAddr) -> PeerId {
2129        use std::collections::hash_map::DefaultHasher;
2130        use std::hash::{Hash, Hasher};
2131
2132        let mut hasher = DefaultHasher::new();
2133        addr.hash(&mut hasher);
2134
2135        let hash = hasher.finish();
2136        let mut peer_id = [0u8; 32];
2137        peer_id[0..8].copy_from_slice(&hash.to_be_bytes());
2138
2139        // Add some randomness to avoid collisions
2140        // NOTE: This makes the peer ID non-persistent across connections
2141        for i in 8..32 {
2142            peer_id[i] = rand::random();
2143        }
2144
2145        warn!(
2146            "Generated temporary peer ID from address {}. This ID is not persistent!",
2147            addr
2148        );
2149        PeerId(peer_id)
2150    }
2151
2152    /// Extract peer ID from connection by deriving it from the peer's public key
2153    async fn extract_peer_id_from_connection(
2154        &self,
2155        connection: &QuinnConnection,
2156    ) -> Option<PeerId> {
2157        // Get the peer's identity from the TLS handshake
2158        if let Some(identity) = connection.peer_identity() {
2159            // Check if we have an Ed25519 public key from raw public key authentication
2160            if let Some(public_key_bytes) = identity.downcast_ref::<[u8; 32]>() {
2161                // Derive peer ID from the public key
2162                match crate::derive_peer_id_from_key_bytes(public_key_bytes) {
2163                    Ok(peer_id) => {
2164                        debug!("Derived peer ID from Ed25519 public key");
2165                        return Some(peer_id);
2166                    }
2167                    Err(e) => {
2168                        warn!("Failed to derive peer ID from public key: {}", e);
2169                    }
2170                }
2171            }
2172            // TODO: Handle X.509 certificate case if needed
2173        }
2174
2175        None
2176    }
2177
2178    /// Shutdown the endpoint
2179    pub async fn shutdown(&self) -> Result<(), NatTraversalError> {
2180        // Set shutdown flag
2181        self.shutdown.store(true, Ordering::Relaxed);
2182
2183        // Close all active connections
2184        {
2185            let mut connections = self.connections.write().map_err(|_| {
2186                NatTraversalError::ProtocolError("Connections lock poisoned".to_string())
2187            })?;
2188            for (peer_id, connection) in connections.drain() {
2189                info!("Closing connection to peer {:?}", peer_id);
2190                connection.close(crate::VarInt::from_u32(0), b"Shutdown");
2191            }
2192        }
2193
2194        // Wait for connection to be closed
2195        if let Some(ref endpoint) = self.quinn_endpoint {
2196            endpoint.wait_idle().await;
2197        }
2198
2199        info!("NAT traversal endpoint shutdown completed");
2200        Ok(())
2201    }
2202
2203    /// Discover address candidates for a peer
2204    pub async fn discover_candidates(
2205        &self,
2206        peer_id: PeerId,
2207    ) -> Result<Vec<CandidateAddress>, NatTraversalError> {
2208        debug!("Discovering address candidates for peer {:?}", peer_id);
2209
2210        let mut candidates = Vec::new();
2211
2212        // Get bootstrap nodes
2213        let bootstrap_nodes = {
2214            let nodes = self
2215                .bootstrap_nodes
2216                .read()
2217                .map_err(|_| NatTraversalError::ProtocolError("Lock poisoned".to_string()))?;
2218            nodes.clone()
2219        };
2220
2221        // Start discovery process
2222        {
2223            let mut discovery = self.discovery_manager.lock().map_err(|_| {
2224                NatTraversalError::ProtocolError("Discovery manager lock poisoned".to_string())
2225            })?;
2226
2227            discovery
2228                .start_discovery(peer_id, bootstrap_nodes)
2229                .map_err(|e| NatTraversalError::CandidateDiscoveryFailed(e.to_string()))?;
2230        }
2231
2232        // Poll for discovery results with timeout
2233        let timeout_duration = self.config.coordination_timeout;
2234        let start_time = std::time::Instant::now();
2235
2236        while start_time.elapsed() < timeout_duration {
2237            let discovery_events = {
2238                let mut discovery = self.discovery_manager.lock().map_err(|_| {
2239                    NatTraversalError::ProtocolError("Discovery manager lock poisoned".to_string())
2240                })?;
2241                discovery.poll(std::time::Instant::now())
2242            };
2243
2244            for event in discovery_events {
2245                match event {
2246                    DiscoveryEvent::LocalCandidateDiscovered { candidate } => {
2247                        candidates.push(candidate.clone());
2248
2249                        // Send ADD_ADDRESS frame to advertise this candidate to the peer
2250                        self.send_candidate_advertisement(peer_id, &candidate)
2251                            .await
2252                            .unwrap_or_else(|e| {
2253                                debug!("Failed to send candidate advertisement: {}", e)
2254                            });
2255                    }
2256                    DiscoveryEvent::ServerReflexiveCandidateDiscovered { candidate, .. } => {
2257                        candidates.push(candidate.clone());
2258
2259                        // Send ADD_ADDRESS frame to advertise this candidate to the peer
2260                        self.send_candidate_advertisement(peer_id, &candidate)
2261                            .await
2262                            .unwrap_or_else(|e| {
2263                                debug!("Failed to send candidate advertisement: {}", e)
2264                            });
2265                    }
2266                    DiscoveryEvent::PredictedCandidateGenerated { candidate, .. } => {
2267                        candidates.push(candidate.clone());
2268
2269                        // Send ADD_ADDRESS frame to advertise this candidate to the peer
2270                        self.send_candidate_advertisement(peer_id, &candidate)
2271                            .await
2272                            .unwrap_or_else(|e| {
2273                                debug!("Failed to send candidate advertisement: {}", e)
2274                            });
2275                    }
2276                    DiscoveryEvent::DiscoveryCompleted { .. } => {
2277                        // Discovery complete, return candidates
2278                        return Ok(candidates);
2279                    }
2280                    DiscoveryEvent::DiscoveryFailed {
2281                        error,
2282                        partial_results,
2283                    } => {
2284                        // Use partial results if available
2285                        candidates.extend(partial_results);
2286                        if candidates.is_empty() {
2287                            return Err(NatTraversalError::CandidateDiscoveryFailed(
2288                                error.to_string(),
2289                            ));
2290                        }
2291                        return Ok(candidates);
2292                    }
2293                    _ => {}
2294                }
2295            }
2296
2297            // Brief delay before next poll
2298            sleep(Duration::from_millis(10)).await;
2299        }
2300
2301        if candidates.is_empty() {
2302            Err(NatTraversalError::NoCandidatesFound)
2303        } else {
2304            Ok(candidates)
2305        }
2306    }
2307
2308    /// Create PUNCH_ME_NOW extension frame for NAT traversal coordination
2309    fn create_punch_me_now_frame(&self, peer_id: PeerId) -> Result<Vec<u8>, NatTraversalError> {
2310        // PUNCH_ME_NOW frame format (IETF QUIC NAT Traversal draft):
2311        // Frame Type: 0x41 (PUNCH_ME_NOW)
2312        // Length: Variable
2313        // Peer ID: 32 bytes
2314        // Timestamp: 8 bytes
2315        // Coordination Token: 16 bytes
2316
2317        let mut frame = Vec::new();
2318
2319        // Frame type
2320        frame.push(0x41);
2321
2322        // Peer ID (32 bytes)
2323        frame.extend_from_slice(&peer_id.0);
2324
2325        // Timestamp (8 bytes, current time as milliseconds since epoch)
2326        let timestamp = std::time::SystemTime::now()
2327            .duration_since(std::time::UNIX_EPOCH)
2328            .unwrap_or_default()
2329            .as_millis() as u64;
2330        frame.extend_from_slice(&timestamp.to_be_bytes());
2331
2332        // Coordination token (16 random bytes for this session)
2333        let mut token = [0u8; 16];
2334        for byte in &mut token {
2335            *byte = rand::random();
2336        }
2337        frame.extend_from_slice(&token);
2338
2339        Ok(frame)
2340    }
2341
2342    fn attempt_hole_punching(&self, peer_id: PeerId) -> Result<(), NatTraversalError> {
2343        debug!("Attempting hole punching for peer {:?}", peer_id);
2344
2345        // Get candidate pairs for this peer
2346        let candidate_pairs = self.get_candidate_pairs_for_peer(peer_id)?;
2347
2348        if candidate_pairs.is_empty() {
2349            return Err(NatTraversalError::NoCandidatesFound);
2350        }
2351
2352        info!(
2353            "Generated {} candidate pairs for hole punching with peer {:?}",
2354            candidate_pairs.len(),
2355            peer_id
2356        );
2357
2358        // Attempt hole punching with each candidate pair
2359
2360        self.attempt_quinn_hole_punching(peer_id, candidate_pairs)
2361    }
2362
2363    /// Generate candidate pairs for hole punching based on ICE-like algorithm
2364    fn get_candidate_pairs_for_peer(
2365        &self,
2366        peer_id: PeerId,
2367    ) -> Result<Vec<CandidatePair>, NatTraversalError> {
2368        // Get discovered candidates from the discovery manager
2369        let discovery_candidates = {
2370            let discovery = self.discovery_manager.lock().map_err(|_| {
2371                NatTraversalError::ProtocolError("Discovery manager lock poisoned".to_string())
2372            })?;
2373
2374            discovery.get_candidates_for_peer(peer_id)
2375        };
2376
2377        if discovery_candidates.is_empty() {
2378            return Err(NatTraversalError::NoCandidatesFound);
2379        }
2380
2381        // Create candidate pairs with priorities (ICE-like pairing)
2382        let mut candidate_pairs = Vec::new();
2383        let local_candidates = discovery_candidates
2384            .iter()
2385            .filter(|c| matches!(c.source, CandidateSource::Local))
2386            .collect::<Vec<_>>();
2387        let remote_candidates = discovery_candidates
2388            .iter()
2389            .filter(|c| !matches!(c.source, CandidateSource::Local))
2390            .collect::<Vec<_>>();
2391
2392        // Pair each local candidate with each remote candidate
2393        for local in &local_candidates {
2394            for remote in &remote_candidates {
2395                let pair_priority = self.calculate_candidate_pair_priority(local, remote);
2396                candidate_pairs.push(CandidatePair {
2397                    local_candidate: (*local).clone(),
2398                    remote_candidate: (*remote).clone(),
2399                    priority: pair_priority,
2400                    state: CandidatePairState::Waiting,
2401                });
2402            }
2403        }
2404
2405        // Sort by priority (highest first)
2406        candidate_pairs.sort_by(|a, b| b.priority.cmp(&a.priority));
2407
2408        // Limit to reasonable number for initial attempts
2409        candidate_pairs.truncate(8);
2410
2411        Ok(candidate_pairs)
2412    }
2413
2414    /// Calculate candidate pair priority using ICE algorithm
2415    fn calculate_candidate_pair_priority(
2416        &self,
2417        local: &CandidateAddress,
2418        remote: &CandidateAddress,
2419    ) -> u64 {
2420        // ICE candidate pair priority formula: min(G,D) * 2^32 + max(G,D) * 2 + (G>D ? 1 : 0)
2421        // Where G is controlling agent priority, D is controlled agent priority
2422
2423        let local_type_preference = match local.source {
2424            CandidateSource::Local => 126,
2425            CandidateSource::Observed { .. } => 100,
2426            CandidateSource::Predicted => 75,
2427            CandidateSource::Peer => 50,
2428        };
2429
2430        let remote_type_preference = match remote.source {
2431            CandidateSource::Local => 126,
2432            CandidateSource::Observed { .. } => 100,
2433            CandidateSource::Predicted => 75,
2434            CandidateSource::Peer => 50,
2435        };
2436
2437        // Simplified priority calculation
2438        let local_priority = (local_type_preference as u64) << 8 | local.priority as u64;
2439        let remote_priority = (remote_type_preference as u64) << 8 | remote.priority as u64;
2440
2441        let min_priority = local_priority.min(remote_priority);
2442        let max_priority = local_priority.max(remote_priority);
2443
2444        (min_priority << 32)
2445            | (max_priority << 1)
2446            | if local_priority > remote_priority {
2447                1
2448            } else {
2449                0
2450            }
2451    }
2452
2453    /// Real Quinn-based hole punching implementation
2454    fn attempt_quinn_hole_punching(
2455        &self,
2456        peer_id: PeerId,
2457        candidate_pairs: Vec<CandidatePair>,
2458    ) -> Result<(), NatTraversalError> {
2459        let _endpoint = self.quinn_endpoint.as_ref().ok_or_else(|| {
2460            NatTraversalError::ConfigError("Quinn endpoint not initialized".to_string())
2461        })?;
2462
2463        for pair in candidate_pairs {
2464            debug!(
2465                "Attempting hole punch with candidate pair: {} -> {}",
2466                pair.local_candidate.address, pair.remote_candidate.address
2467            );
2468
2469            // Create PATH_CHALLENGE frame data (8 random bytes)
2470            let mut challenge_data = [0u8; 8];
2471            for byte in &mut challenge_data {
2472                *byte = rand::random();
2473            }
2474
2475            // Create a raw UDP socket bound to the local candidate address
2476            let local_socket =
2477                std::net::UdpSocket::bind(pair.local_candidate.address).map_err(|e| {
2478                    NatTraversalError::NetworkError(format!(
2479                        "Failed to bind to local candidate: {e}"
2480                    ))
2481                })?;
2482
2483            // Craft a minimal QUIC packet with PATH_CHALLENGE frame
2484            let path_challenge_packet = self.create_path_challenge_packet(challenge_data)?;
2485
2486            // Send the packet to the remote candidate address
2487            match local_socket.send_to(&path_challenge_packet, pair.remote_candidate.address) {
2488                Ok(bytes_sent) => {
2489                    debug!(
2490                        "Sent {} bytes for hole punch from {} to {}",
2491                        bytes_sent, pair.local_candidate.address, pair.remote_candidate.address
2492                    );
2493
2494                    // Set a short timeout for response
2495                    local_socket
2496                        .set_read_timeout(Some(Duration::from_millis(100)))
2497                        .map_err(|e| {
2498                            NatTraversalError::NetworkError(format!("Failed to set timeout: {e}"))
2499                        })?;
2500
2501                    // Try to receive a response
2502                    let mut response_buffer = [0u8; 1024];
2503                    match local_socket.recv_from(&mut response_buffer) {
2504                        Ok((_bytes_received, response_addr)) => {
2505                            if response_addr == pair.remote_candidate.address {
2506                                info!(
2507                                    "Hole punch succeeded for peer {:?}: {} <-> {}",
2508                                    peer_id,
2509                                    pair.local_candidate.address,
2510                                    pair.remote_candidate.address
2511                                );
2512
2513                                // Store successful candidate pair for connection establishment
2514                                self.store_successful_candidate_pair(peer_id, pair)?;
2515                                return Ok(());
2516                            } else {
2517                                debug!(
2518                                    "Received response from unexpected address: {}",
2519                                    response_addr
2520                                );
2521                            }
2522                        }
2523                        Err(e)
2524                            if e.kind() == std::io::ErrorKind::WouldBlock
2525                                || e.kind() == std::io::ErrorKind::TimedOut =>
2526                        {
2527                            debug!("No response received for hole punch attempt");
2528                        }
2529                        Err(e) => {
2530                            debug!("Error receiving hole punch response: {}", e);
2531                        }
2532                    }
2533                }
2534                Err(e) => {
2535                    debug!("Failed to send hole punch packet: {}", e);
2536                }
2537            }
2538        }
2539
2540        // If we get here, all hole punch attempts failed
2541        Err(NatTraversalError::HolePunchingFailed)
2542    }
2543
2544    /// Create a minimal QUIC packet with PATH_CHALLENGE frame for hole punching
2545    fn create_path_challenge_packet(
2546        &self,
2547        challenge_data: [u8; 8],
2548    ) -> Result<Vec<u8>, NatTraversalError> {
2549        // Create a minimal QUIC packet structure
2550        // This is a simplified implementation - in production, you'd use proper QUIC packet construction
2551        let mut packet = Vec::new();
2552
2553        // QUIC packet header (simplified)
2554        packet.push(0x40); // Short header, fixed bit set
2555        packet.extend_from_slice(&[0, 0, 0, 1]); // Connection ID (simplified)
2556
2557        // PATH_CHALLENGE frame
2558        packet.push(0x1a); // PATH_CHALLENGE frame type
2559        packet.extend_from_slice(&challenge_data); // 8-byte challenge data
2560
2561        Ok(packet)
2562    }
2563
2564    /// Store successful candidate pair for later connection establishment
2565    fn store_successful_candidate_pair(
2566        &self,
2567        peer_id: PeerId,
2568        pair: CandidatePair,
2569    ) -> Result<(), NatTraversalError> {
2570        debug!(
2571            "Storing successful candidate pair for peer {:?}: {} <-> {}",
2572            peer_id, pair.local_candidate.address, pair.remote_candidate.address
2573        );
2574
2575        // In a complete implementation, this would store the successful pair
2576        // for use in establishing the actual QUIC connection
2577        // For now, we'll emit an event to notify the application
2578
2579        if let Some(ref callback) = self.event_callback {
2580            callback(NatTraversalEvent::PathValidated {
2581                peer_id,
2582                address: pair.remote_candidate.address,
2583                rtt: Duration::from_millis(50), // Estimated RTT
2584            });
2585
2586            callback(NatTraversalEvent::TraversalSucceeded {
2587                peer_id,
2588                final_address: pair.remote_candidate.address,
2589                total_time: Duration::from_secs(1), // Estimated total time
2590            });
2591        }
2592
2593        Ok(())
2594    }
2595
2596    /// Attempt connection to a specific candidate address
2597    fn attempt_connection_to_candidate(
2598        &self,
2599        peer_id: PeerId,
2600        candidate: &CandidateAddress,
2601    ) -> Result<(), NatTraversalError> {
2602        {
2603            let endpoint = self.quinn_endpoint.as_ref().ok_or_else(|| {
2604                NatTraversalError::ConfigError("Quinn endpoint not initialized".to_string())
2605            })?;
2606
2607            // Create server name for the connection
2608            let server_name = format!("peer-{:x}", peer_id.0[0] as u32);
2609
2610            debug!(
2611                "Attempting Quinn connection to candidate {} for peer {:?}",
2612                candidate.address, peer_id
2613            );
2614
2615            // Use the sync connect method from Quinn endpoint
2616            match endpoint.connect(candidate.address, &server_name) {
2617                Ok(connecting) => {
2618                    info!(
2619                        "Connection attempt initiated to {} for peer {:?}",
2620                        candidate.address, peer_id
2621                    );
2622
2623                    // Spawn a task to handle the connection completion
2624                    if let Some(event_tx) = &self.event_tx {
2625                        let event_tx = event_tx.clone();
2626                        let connections = self.connections.clone();
2627                        let peer_id_clone = peer_id;
2628                        let address = candidate.address;
2629
2630                        tokio::spawn(async move {
2631                            match connecting.await {
2632                                Ok(connection) => {
2633                                    info!(
2634                                        "Successfully connected to {} for peer {:?}",
2635                                        address, peer_id_clone
2636                                    );
2637
2638                                    // Store the connection
2639                                    if let Ok(mut conns) = connections.write() {
2640                                        conns.insert(peer_id_clone, connection.clone());
2641                                    }
2642
2643                                    // Send connection established event
2644                                    let _ =
2645                                        event_tx.send(NatTraversalEvent::ConnectionEstablished {
2646                                            peer_id: peer_id_clone,
2647                                            remote_address: address,
2648                                        });
2649
2650                                    // Handle the connection
2651                                    Self::handle_connection(connection, event_tx).await;
2652                                }
2653                                Err(e) => {
2654                                    warn!("Connection to {} failed: {}", address, e);
2655                                }
2656                            }
2657                        });
2658                    }
2659
2660                    Ok(())
2661                }
2662                Err(e) => {
2663                    warn!(
2664                        "Failed to initiate connection to {}: {}",
2665                        candidate.address, e
2666                    );
2667                    Err(NatTraversalError::ConnectionFailed(format!(
2668                        "Failed to connect to {}: {}",
2669                        candidate.address, e
2670                    )))
2671                }
2672            }
2673        }
2674    }
2675
2676    /// Poll for NAT traversal progress and state machine updates
2677    pub fn poll(
2678        &self,
2679        now: std::time::Instant,
2680    ) -> Result<Vec<NatTraversalEvent>, NatTraversalError> {
2681        let mut events = Vec::new();
2682
2683        // Check connections for observed addresses
2684        self.check_connections_for_observed_addresses(&mut events)?;
2685
2686        // Poll candidate discovery manager
2687        {
2688            let mut discovery = self.discovery_manager.lock().map_err(|_| {
2689                NatTraversalError::ProtocolError("Discovery manager lock poisoned".to_string())
2690            })?;
2691
2692            let discovery_events = discovery.poll(now);
2693
2694            // Convert discovery events to NAT traversal events
2695            for discovery_event in discovery_events {
2696                if let Some(nat_event) = self.convert_discovery_event(discovery_event) {
2697                    events.push(nat_event.clone());
2698
2699                    // Emit via callback
2700                    if let Some(ref callback) = self.event_callback {
2701                        callback(nat_event.clone());
2702                    }
2703
2704                    // Update session candidates when discovered
2705                    if let NatTraversalEvent::CandidateDiscovered {
2706                        peer_id: _,
2707                        candidate: _,
2708                    } = &nat_event
2709                    {
2710                        // Store candidate for the session (will be done after we release discovery lock)
2711                        // For now, just note that we need to update the session
2712                    }
2713                }
2714            }
2715        }
2716
2717        // Check active sessions for timeouts and state updates
2718        let mut sessions = self
2719            .active_sessions
2720            .write()
2721            .map_err(|_| NatTraversalError::ProtocolError("Lock poisoned".to_string()))?;
2722
2723        for (_peer_id, session) in sessions.iter_mut() {
2724            let elapsed = now.duration_since(session.started_at);
2725
2726            // Get timeout for current phase
2727            let timeout = self.get_phase_timeout(session.phase);
2728
2729            // Check if we've exceeded the timeout
2730            if elapsed > timeout {
2731                match session.phase {
2732                    TraversalPhase::Discovery => {
2733                        // Get candidates from discovery manager
2734                        let discovered_candidates = {
2735                            let discovery = self.discovery_manager.lock().map_err(|_| {
2736                                NatTraversalError::ProtocolError(
2737                                    "Discovery manager lock poisoned".to_string(),
2738                                )
2739                            });
2740                            match discovery {
2741                                Ok(disc) => disc.get_candidates_for_peer(session.peer_id),
2742                                Err(_) => Vec::new(),
2743                            }
2744                        };
2745
2746                        // Update session candidates
2747                        session.candidates = discovered_candidates.clone();
2748
2749                        // Check if we have discovered any candidates
2750                        if !session.candidates.is_empty() {
2751                            // Advance to coordination phase
2752                            session.phase = TraversalPhase::Coordination;
2753                            let event = NatTraversalEvent::PhaseTransition {
2754                                peer_id: session.peer_id,
2755                                from_phase: TraversalPhase::Discovery,
2756                                to_phase: TraversalPhase::Coordination,
2757                            };
2758                            events.push(event.clone());
2759                            if let Some(ref callback) = self.event_callback {
2760                                callback(event);
2761                            }
2762                            info!(
2763                                "Peer {:?} advanced from Discovery to Coordination with {} candidates",
2764                                session.peer_id,
2765                                session.candidates.len()
2766                            );
2767                        } else if session.attempt < self.config.max_concurrent_attempts as u32 {
2768                            // Retry discovery with exponential backoff
2769                            session.attempt += 1;
2770                            session.started_at = now;
2771                            let backoff_duration = self.calculate_backoff(session.attempt);
2772                            warn!(
2773                                "Discovery timeout for peer {:?}, retrying (attempt {}), backoff: {:?}",
2774                                session.peer_id, session.attempt, backoff_duration
2775                            );
2776                        } else {
2777                            // Max attempts reached, fail
2778                            session.phase = TraversalPhase::Failed;
2779                            let event = NatTraversalEvent::TraversalFailed {
2780                                peer_id: session.peer_id,
2781                                error: NatTraversalError::NoCandidatesFound,
2782                                fallback_available: self.config.enable_relay_fallback,
2783                            };
2784                            events.push(event.clone());
2785                            if let Some(ref callback) = self.event_callback {
2786                                callback(event);
2787                            }
2788                            error!(
2789                                "NAT traversal failed for peer {:?}: no candidates found after {} attempts",
2790                                session.peer_id, session.attempt
2791                            );
2792                        }
2793                    }
2794                    TraversalPhase::Coordination => {
2795                        // Request coordination from bootstrap
2796                        if let Some(coordinator) = self.select_coordinator() {
2797                            match self.send_coordination_request(session.peer_id, coordinator) {
2798                                Ok(_) => {
2799                                    session.phase = TraversalPhase::Synchronization;
2800                                    let event = NatTraversalEvent::CoordinationRequested {
2801                                        peer_id: session.peer_id,
2802                                        coordinator,
2803                                    };
2804                                    events.push(event.clone());
2805                                    if let Some(ref callback) = self.event_callback {
2806                                        callback(event);
2807                                    }
2808                                    info!(
2809                                        "Coordination requested for peer {:?} via {}",
2810                                        session.peer_id, coordinator
2811                                    );
2812                                }
2813                                Err(e) => {
2814                                    self.handle_phase_failure(session, now, &mut events, e);
2815                                }
2816                            }
2817                        } else {
2818                            self.handle_phase_failure(
2819                                session,
2820                                now,
2821                                &mut events,
2822                                NatTraversalError::NoBootstrapNodes,
2823                            );
2824                        }
2825                    }
2826                    TraversalPhase::Synchronization => {
2827                        // Check if peer is synchronized
2828                        if self.is_peer_synchronized(&session.peer_id) {
2829                            session.phase = TraversalPhase::Punching;
2830                            let event = NatTraversalEvent::HolePunchingStarted {
2831                                peer_id: session.peer_id,
2832                                targets: session.candidates.iter().map(|c| c.address).collect(),
2833                            };
2834                            events.push(event.clone());
2835                            if let Some(ref callback) = self.event_callback {
2836                                callback(event);
2837                            }
2838                            // Initiate hole punching attempts
2839                            if let Err(e) =
2840                                self.initiate_hole_punching(session.peer_id, &session.candidates)
2841                            {
2842                                self.handle_phase_failure(session, now, &mut events, e);
2843                            }
2844                        } else {
2845                            self.handle_phase_failure(
2846                                session,
2847                                now,
2848                                &mut events,
2849                                NatTraversalError::ProtocolError(
2850                                    "Synchronization timeout".to_string(),
2851                                ),
2852                            );
2853                        }
2854                    }
2855                    TraversalPhase::Punching => {
2856                        // Check if any punch succeeded
2857                        if let Some(successful_path) = self.check_punch_results(&session.peer_id) {
2858                            session.phase = TraversalPhase::Validation;
2859                            let event = NatTraversalEvent::PathValidated {
2860                                peer_id: session.peer_id,
2861                                address: successful_path,
2862                                rtt: Duration::from_millis(50), // TODO: Get actual RTT
2863                            };
2864                            events.push(event.clone());
2865                            if let Some(ref callback) = self.event_callback {
2866                                callback(event);
2867                            }
2868                            // Start path validation
2869                            if let Err(e) = self.validate_path(session.peer_id, successful_path) {
2870                                self.handle_phase_failure(session, now, &mut events, e);
2871                            }
2872                        } else {
2873                            self.handle_phase_failure(
2874                                session,
2875                                now,
2876                                &mut events,
2877                                NatTraversalError::PunchingFailed(
2878                                    "No successful punch".to_string(),
2879                                ),
2880                            );
2881                        }
2882                    }
2883                    TraversalPhase::Validation => {
2884                        // Check if path is validated
2885                        if self.is_path_validated(&session.peer_id) {
2886                            session.phase = TraversalPhase::Connected;
2887                            let event = NatTraversalEvent::TraversalSucceeded {
2888                                peer_id: session.peer_id,
2889                                final_address: session
2890                                    .candidates
2891                                    .first()
2892                                    .map(|c| c.address)
2893                                    .unwrap_or_else(create_random_port_bind_addr),
2894                                total_time: elapsed,
2895                            };
2896                            events.push(event.clone());
2897                            if let Some(ref callback) = self.event_callback {
2898                                callback(event);
2899                            }
2900                            info!(
2901                                "NAT traversal succeeded for peer {:?} in {:?}",
2902                                session.peer_id, elapsed
2903                            );
2904                        } else {
2905                            self.handle_phase_failure(
2906                                session,
2907                                now,
2908                                &mut events,
2909                                NatTraversalError::ValidationFailed(
2910                                    "Path validation timeout".to_string(),
2911                                ),
2912                            );
2913                        }
2914                    }
2915                    TraversalPhase::Connected => {
2916                        // Monitor connection health
2917                        if !self.is_connection_healthy(&session.peer_id) {
2918                            warn!(
2919                                "Connection to peer {:?} is no longer healthy",
2920                                session.peer_id
2921                            );
2922                            // Could trigger reconnection logic here
2923                        }
2924                    }
2925                    TraversalPhase::Failed => {
2926                        // Session has already failed, no action needed
2927                    }
2928                }
2929            }
2930        }
2931
2932        Ok(events)
2933    }
2934
2935    /// Get timeout duration for a specific traversal phase
2936    fn get_phase_timeout(&self, phase: TraversalPhase) -> Duration {
2937        match phase {
2938            TraversalPhase::Discovery => Duration::from_secs(10),
2939            TraversalPhase::Coordination => self.config.coordination_timeout,
2940            TraversalPhase::Synchronization => Duration::from_secs(3),
2941            TraversalPhase::Punching => Duration::from_secs(5),
2942            TraversalPhase::Validation => Duration::from_secs(5),
2943            TraversalPhase::Connected => Duration::from_secs(30), // Keepalive check
2944            TraversalPhase::Failed => Duration::ZERO,
2945        }
2946    }
2947
2948    /// Calculate exponential backoff duration for retries
2949    fn calculate_backoff(&self, attempt: u32) -> Duration {
2950        let base = Duration::from_millis(1000);
2951        let max = Duration::from_secs(30);
2952        let backoff = base * 2u32.pow(attempt.saturating_sub(1));
2953        let jitter = std::time::Duration::from_millis((rand::random::<u64>() % 200) as u64);
2954        backoff.min(max) + jitter
2955    }
2956
2957    /// Check connections for observed addresses and feed them to discovery
2958    fn check_connections_for_observed_addresses(
2959        &self,
2960        _events: &mut Vec<NatTraversalEvent>,
2961    ) -> Result<(), NatTraversalError> {
2962        // Check if we're connected to any bootstrap nodes
2963        let connections = self.connections.read().map_err(|_| {
2964            NatTraversalError::ProtocolError("Connections lock poisoned".to_string())
2965        })?;
2966
2967        // Look for bootstrap connections - they should send us OBSERVED_ADDRESS frames
2968        // In the current implementation, we need to wait for the low-level connection
2969        // to receive OBSERVED_ADDRESS frames and propagate them up
2970
2971        // For now, simulate the discovery for testing
2972        // In production, this would be triggered by actual OBSERVED_ADDRESS frames
2973        if !connections.is_empty() && self.config.role == EndpointRole::Client {
2974            // Check if we have any bootstrap connections
2975            for (_peer_id, connection) in connections.iter() {
2976                let remote_addr = connection.remote_address();
2977
2978                // Check if this is a bootstrap node connection
2979                let is_bootstrap = {
2980                    let bootstrap_nodes = self.bootstrap_nodes.read().map_err(|_| {
2981                        NatTraversalError::ProtocolError(
2982                            "Bootstrap nodes lock poisoned".to_string(),
2983                        )
2984                    })?;
2985                    bootstrap_nodes
2986                        .iter()
2987                        .any(|node| node.address == remote_addr)
2988                };
2989
2990                if is_bootstrap {
2991                    // In a real implementation, we would check the connection for observed addresses
2992                    // For now, emit a debug message
2993                    debug!(
2994                        "Bootstrap connection to {} should provide our external address via OBSERVED_ADDRESS frames",
2995                        remote_addr
2996                    );
2997
2998                    // The actual observed address would come from the OBSERVED_ADDRESS frame
2999                    // received on this connection
3000                }
3001            }
3002        }
3003
3004        Ok(())
3005    }
3006
3007    /// Handle phase failure with retry logic
3008    fn handle_phase_failure(
3009        &self,
3010        session: &mut NatTraversalSession,
3011        now: std::time::Instant,
3012        events: &mut Vec<NatTraversalEvent>,
3013        error: NatTraversalError,
3014    ) {
3015        if session.attempt < self.config.max_concurrent_attempts as u32 {
3016            // Retry with backoff
3017            session.attempt += 1;
3018            session.started_at = now;
3019            let backoff = self.calculate_backoff(session.attempt);
3020            warn!(
3021                "Phase {:?} failed for peer {:?}: {:?}, retrying (attempt {}) after {:?}",
3022                session.phase, session.peer_id, error, session.attempt, backoff
3023            );
3024        } else {
3025            // Max attempts reached
3026            session.phase = TraversalPhase::Failed;
3027            let event = NatTraversalEvent::TraversalFailed {
3028                peer_id: session.peer_id,
3029                error,
3030                fallback_available: self.config.enable_relay_fallback,
3031            };
3032            events.push(event.clone());
3033            if let Some(ref callback) = self.event_callback {
3034                callback(event);
3035            }
3036            error!(
3037                "NAT traversal failed for peer {:?} after {} attempts",
3038                session.peer_id, session.attempt
3039            );
3040        }
3041    }
3042
3043    /// Select a coordinator from available bootstrap nodes
3044    fn select_coordinator(&self) -> Option<SocketAddr> {
3045        if let Ok(nodes) = self.bootstrap_nodes.read() {
3046            // Simple round-robin or random selection
3047            if !nodes.is_empty() {
3048                let idx = rand::random::<usize>() % nodes.len();
3049                return Some(nodes[idx].address);
3050            }
3051        }
3052        None
3053    }
3054
3055    /// Send coordination request to bootstrap node
3056    fn send_coordination_request(
3057        &self,
3058        peer_id: PeerId,
3059        coordinator: SocketAddr,
3060    ) -> Result<(), NatTraversalError> {
3061        debug!(
3062            "Sending coordination request for peer {:?} to {}",
3063            peer_id, coordinator
3064        );
3065
3066        {
3067            // Check if we have a connection to the coordinator
3068            if let Ok(connections) = self.connections.read() {
3069                // Look for coordinator connection
3070                for (_peer, conn) in connections.iter() {
3071                    if conn.remote_address() == coordinator {
3072                        // We have a connection to the coordinator
3073                        // In a real implementation, we would send a PUNCH_ME_NOW frame
3074                        // For now, we'll mark this as successful
3075                        info!("Found existing connection to coordinator {}", coordinator);
3076                        return Ok(());
3077                    }
3078                }
3079            }
3080
3081            // If no existing connection, try to establish one
3082            info!("Establishing connection to coordinator {}", coordinator);
3083            if let Some(endpoint) = &self.quinn_endpoint {
3084                let server_name = format!("bootstrap-{}", coordinator.ip());
3085                match endpoint.connect(coordinator, &server_name) {
3086                    Ok(connecting) => {
3087                        // For sync context, we'll return success and let the connection complete async
3088                        info!("Initiated connection to coordinator {}", coordinator);
3089
3090                        // Spawn task to handle connection
3091                        if let Some(event_tx) = &self.event_tx {
3092                            let event_tx = event_tx.clone();
3093                            let connections = self.connections.clone();
3094
3095                            tokio::spawn(async move {
3096                                match connecting.await {
3097                                    Ok(connection) => {
3098                                        info!("Connected to coordinator {}", coordinator);
3099
3100                                        // Generate a peer ID for the bootstrap node
3101                                        let bootstrap_peer_id =
3102                                            Self::generate_peer_id_from_address(coordinator);
3103
3104                                        // Store the connection
3105                                        if let Ok(mut conns) = connections.write() {
3106                                            conns.insert(bootstrap_peer_id, connection.clone());
3107                                        }
3108
3109                                        // Handle the connection
3110                                        Self::handle_connection(connection, event_tx).await;
3111                                    }
3112                                    Err(e) => {
3113                                        warn!(
3114                                            "Failed to connect to coordinator {}: {}",
3115                                            coordinator, e
3116                                        );
3117                                    }
3118                                }
3119                            });
3120                        }
3121
3122                        // Return success to allow traversal to continue
3123                        // The actual coordination will happen once connected
3124                        Ok(())
3125                    }
3126                    Err(e) => Err(NatTraversalError::CoordinationFailed(format!(
3127                        "Failed to connect to coordinator {coordinator}: {e}"
3128                    ))),
3129                }
3130            } else {
3131                Err(NatTraversalError::ConfigError(
3132                    "Quinn endpoint not initialized".to_string(),
3133                ))
3134            }
3135        }
3136    }
3137
3138    /// Check if peer is synchronized for hole punching
3139    fn is_peer_synchronized(&self, peer_id: &PeerId) -> bool {
3140        debug!("Checking synchronization status for peer {:?}", peer_id);
3141
3142        // Check if we have received candidates from the peer
3143        if let Ok(sessions) = self.active_sessions.read() {
3144            if let Some(session) = sessions.get(peer_id) {
3145                // In coordination phase, we should have exchanged candidates
3146                // For now, check if we have candidates and we're past discovery
3147                let has_candidates = !session.candidates.is_empty();
3148                let past_discovery = session.phase as u8 > TraversalPhase::Discovery as u8;
3149
3150                debug!(
3151                    "Checking sync for peer {:?}: phase={:?}, candidates={}, past_discovery={}",
3152                    peer_id,
3153                    session.phase,
3154                    session.candidates.len(),
3155                    past_discovery
3156                );
3157
3158                if has_candidates && past_discovery {
3159                    info!(
3160                        "Peer {:?} is synchronized with {} candidates",
3161                        peer_id,
3162                        session.candidates.len()
3163                    );
3164                    return true;
3165                }
3166
3167                // For testing: if we're in synchronization phase and have candidates, consider synchronized
3168                if session.phase == TraversalPhase::Synchronization && has_candidates {
3169                    info!(
3170                        "Peer {:?} in synchronization phase with {} candidates, considering synchronized",
3171                        peer_id,
3172                        session.candidates.len()
3173                    );
3174                    return true;
3175                }
3176
3177                // For testing without real discovery: consider synchronized if we're at least past discovery phase
3178                if session.phase as u8 >= TraversalPhase::Synchronization as u8 {
3179                    info!(
3180                        "Test mode: Considering peer {:?} synchronized in phase {:?}",
3181                        peer_id, session.phase
3182                    );
3183                    return true;
3184                }
3185            }
3186        }
3187
3188        warn!("Peer {:?} is not synchronized", peer_id);
3189        false
3190    }
3191
3192    /// Initiate hole punching to candidate addresses
3193    fn initiate_hole_punching(
3194        &self,
3195        peer_id: PeerId,
3196        candidates: &[CandidateAddress],
3197    ) -> Result<(), NatTraversalError> {
3198        if candidates.is_empty() {
3199            return Err(NatTraversalError::NoCandidatesFound);
3200        }
3201
3202        info!(
3203            "Initiating hole punching for peer {:?} to {} candidates",
3204            peer_id,
3205            candidates.len()
3206        );
3207
3208        {
3209            // Attempt to connect to each candidate address
3210            for candidate in candidates {
3211                debug!(
3212                    "Attempting QUIC connection to candidate: {}",
3213                    candidate.address
3214                );
3215
3216                // Use the attempt_connection_to_candidate method which handles the actual connection
3217                match self.attempt_connection_to_candidate(peer_id, candidate) {
3218                    Ok(_) => {
3219                        info!(
3220                            "Successfully initiated connection attempt to {}",
3221                            candidate.address
3222                        );
3223                    }
3224                    Err(e) => {
3225                        warn!(
3226                            "Failed to initiate connection to {}: {:?}",
3227                            candidate.address, e
3228                        );
3229                    }
3230                }
3231            }
3232
3233            Ok(())
3234        }
3235    }
3236
3237    /// Check if any hole punch succeeded
3238    fn check_punch_results(&self, peer_id: &PeerId) -> Option<SocketAddr> {
3239        {
3240            // Check if we have an established connection to this peer
3241            if let Ok(connections) = self.connections.read() {
3242                if let Some(conn) = connections.get(peer_id) {
3243                    // We have a connection! Return its address
3244                    let addr = conn.remote_address();
3245                    info!(
3246                        "Found successful connection to peer {:?} at {}",
3247                        peer_id, addr
3248                    );
3249                    return Some(addr);
3250                }
3251            }
3252        }
3253
3254        // No connection found, check if we have any validated candidates
3255        if let Ok(sessions) = self.active_sessions.read() {
3256            if let Some(session) = sessions.get(peer_id) {
3257                // Look for validated candidates
3258                for candidate in &session.candidates {
3259                    if matches!(candidate.state, CandidateState::Valid) {
3260                        info!(
3261                            "Found validated candidate for peer {:?} at {}",
3262                            peer_id, candidate.address
3263                        );
3264                        return Some(candidate.address);
3265                    }
3266                }
3267
3268                // For testing: if we're in punching phase and have candidates, simulate success with the first one
3269                if session.phase == TraversalPhase::Punching && !session.candidates.is_empty() {
3270                    let addr = session.candidates[0].address;
3271                    info!(
3272                        "Simulating successful punch for testing: peer {:?} at {}",
3273                        peer_id, addr
3274                    );
3275                    return Some(addr);
3276                }
3277
3278                // No validated candidates, return first candidate as fallback
3279                if let Some(first) = session.candidates.first() {
3280                    debug!(
3281                        "No validated candidates, using first candidate {} for peer {:?}",
3282                        first.address, peer_id
3283                    );
3284                    return Some(first.address);
3285                }
3286            }
3287        }
3288
3289        warn!("No successful punch results for peer {:?}", peer_id);
3290        None
3291    }
3292
3293    /// Validate a punched path
3294    fn validate_path(&self, peer_id: PeerId, address: SocketAddr) -> Result<(), NatTraversalError> {
3295        debug!("Validating path to peer {:?} at {}", peer_id, address);
3296
3297        {
3298            // Check if we have a connection to validate
3299            if let Ok(connections) = self.connections.read() {
3300                if let Some(conn) = connections.get(&peer_id) {
3301                    // Connection exists, check if it's to the expected address
3302                    if conn.remote_address() == address {
3303                        info!(
3304                            "Path validation successful for peer {:?} at {}",
3305                            peer_id, address
3306                        );
3307
3308                        // Update candidate state to valid
3309                        if let Ok(mut sessions) = self.active_sessions.write() {
3310                            if let Some(session) = sessions.get_mut(&peer_id) {
3311                                for candidate in &mut session.candidates {
3312                                    if candidate.address == address {
3313                                        candidate.state = CandidateState::Valid;
3314                                        break;
3315                                    }
3316                                }
3317                            }
3318                        }
3319
3320                        return Ok(());
3321                    } else {
3322                        warn!(
3323                            "Connection address mismatch: expected {}, got {}",
3324                            address,
3325                            conn.remote_address()
3326                        );
3327                    }
3328                }
3329            }
3330
3331            // No connection found, validation failed
3332            Err(NatTraversalError::ValidationFailed(format!(
3333                "No connection found for peer {peer_id:?} at {address}"
3334            )))
3335        }
3336    }
3337
3338    /// Check if path validation succeeded
3339    fn is_path_validated(&self, peer_id: &PeerId) -> bool {
3340        debug!("Checking path validation for peer {:?}", peer_id);
3341
3342        {
3343            // Check if we have an active connection
3344            if let Ok(connections) = self.connections.read() {
3345                if connections.contains_key(peer_id) {
3346                    info!("Path validated: connection exists for peer {:?}", peer_id);
3347                    return true;
3348                }
3349            }
3350        }
3351
3352        // Check if we have any validated candidates
3353        if let Ok(sessions) = self.active_sessions.read() {
3354            if let Some(session) = sessions.get(peer_id) {
3355                let validated = session
3356                    .candidates
3357                    .iter()
3358                    .any(|c| matches!(c.state, CandidateState::Valid));
3359
3360                if validated {
3361                    info!(
3362                        "Path validated: found validated candidate for peer {:?}",
3363                        peer_id
3364                    );
3365                    return true;
3366                }
3367            }
3368        }
3369
3370        warn!("Path not validated for peer {:?}", peer_id);
3371        false
3372    }
3373
3374    /// Check if connection is healthy
3375    fn is_connection_healthy(&self, peer_id: &PeerId) -> bool {
3376        // In real implementation, check QUIC connection status
3377
3378        {
3379            if let Ok(connections) = self.connections.read() {
3380                if let Some(_conn) = connections.get(peer_id) {
3381                    // Check if connection is still active
3382                    // Note: Quinn's Connection doesn't have is_closed/is_drained methods
3383                    // We use the closed() future to check if still active
3384                    return true; // Assume healthy if connection exists in map
3385                }
3386            }
3387        }
3388        true
3389    }
3390
3391    /// Convert discovery events to NAT traversal events with proper peer ID resolution
3392    fn convert_discovery_event(
3393        &self,
3394        discovery_event: DiscoveryEvent,
3395    ) -> Option<NatTraversalEvent> {
3396        // Get the current active peer ID from sessions
3397        let current_peer_id = self.get_current_discovery_peer_id();
3398
3399        match discovery_event {
3400            DiscoveryEvent::LocalCandidateDiscovered { candidate } => {
3401                Some(NatTraversalEvent::CandidateDiscovered {
3402                    peer_id: current_peer_id,
3403                    candidate,
3404                })
3405            }
3406            DiscoveryEvent::ServerReflexiveCandidateDiscovered {
3407                candidate,
3408                bootstrap_node: _,
3409            } => Some(NatTraversalEvent::CandidateDiscovered {
3410                peer_id: current_peer_id,
3411                candidate,
3412            }),
3413            DiscoveryEvent::PredictedCandidateGenerated {
3414                candidate,
3415                confidence: _,
3416            } => Some(NatTraversalEvent::CandidateDiscovered {
3417                peer_id: current_peer_id,
3418                candidate,
3419            }),
3420            DiscoveryEvent::DiscoveryCompleted {
3421                candidate_count: _,
3422                total_duration: _,
3423                success_rate: _,
3424            } => {
3425                // This could trigger the coordination phase
3426                None // For now, don't emit specific event
3427            }
3428            DiscoveryEvent::DiscoveryFailed {
3429                error,
3430                partial_results,
3431            } => Some(NatTraversalEvent::TraversalFailed {
3432                peer_id: current_peer_id,
3433                error: NatTraversalError::CandidateDiscoveryFailed(error.to_string()),
3434                fallback_available: !partial_results.is_empty(),
3435            }),
3436            _ => None, // Other events don't need to be converted
3437        }
3438    }
3439
3440    /// Get the peer ID for the current discovery session
3441    fn get_current_discovery_peer_id(&self) -> PeerId {
3442        // Try to get the peer ID from the most recent active session
3443        if let Ok(sessions) = self.active_sessions.read() {
3444            if let Some((peer_id, _session)) = sessions
3445                .iter()
3446                .find(|(_, s)| matches!(s.phase, TraversalPhase::Discovery))
3447            {
3448                return *peer_id;
3449            }
3450
3451            // If no discovery phase session, get any active session
3452            if let Some((peer_id, _)) = sessions.iter().next() {
3453                return *peer_id;
3454            }
3455        }
3456
3457        // Fallback: generate a deterministic peer ID based on local endpoint
3458        self.local_peer_id
3459    }
3460
3461    /// Handle endpoint events from connection-level NAT traversal state machine
3462    pub(crate) async fn handle_endpoint_event(
3463        &self,
3464        event: crate::shared::EndpointEventInner,
3465    ) -> Result<(), NatTraversalError> {
3466        match event {
3467            crate::shared::EndpointEventInner::NatCandidateValidated { address, challenge } => {
3468                info!(
3469                    "NAT candidate validation succeeded for {} with challenge {:016x}",
3470                    address, challenge
3471                );
3472
3473                // Update the active session with validated candidate
3474                let mut sessions = self.active_sessions.write().map_err(|_| {
3475                    NatTraversalError::ProtocolError("Sessions lock poisoned".to_string())
3476                })?;
3477
3478                // Find the session that had this candidate
3479                for (peer_id, session) in sessions.iter_mut() {
3480                    if session.candidates.iter().any(|c| c.address == address) {
3481                        // Update session phase to indicate successful validation
3482                        session.phase = TraversalPhase::Connected;
3483
3484                        // Trigger event callback
3485                        if let Some(ref callback) = self.event_callback {
3486                            callback(NatTraversalEvent::CandidateValidated {
3487                                peer_id: *peer_id,
3488                                candidate_address: address,
3489                            });
3490                        }
3491
3492                        // Attempt to establish connection using this validated candidate
3493                        return self
3494                            .establish_connection_to_validated_candidate(*peer_id, address)
3495                            .await;
3496                    }
3497                }
3498
3499                debug!(
3500                    "Validated candidate {} not found in active sessions",
3501                    address
3502                );
3503                Ok(())
3504            }
3505
3506            crate::shared::EndpointEventInner::RelayPunchMeNow(target_peer_id, punch_frame) => {
3507                info!("Relaying PUNCH_ME_NOW to peer {:?}", target_peer_id);
3508
3509                // Convert target_peer_id to PeerId
3510                let target_peer = PeerId(target_peer_id);
3511
3512                // Find the connection to the target peer and send the coordination frame
3513                let connections = self.connections.read().map_err(|_| {
3514                    NatTraversalError::ProtocolError("Connections lock poisoned".to_string())
3515                })?;
3516
3517                if let Some(connection) = connections.get(&target_peer) {
3518                    // Send the PUNCH_ME_NOW frame via a unidirectional stream
3519                    let mut send_stream = connection.open_uni().await.map_err(|e| {
3520                        NatTraversalError::NetworkError(format!("Failed to open stream: {e}"))
3521                    })?;
3522
3523                    // Encode the frame data
3524                    let mut frame_data = Vec::new();
3525                    punch_frame.encode(&mut frame_data);
3526
3527                    send_stream.write_all(&frame_data).await.map_err(|e| {
3528                        NatTraversalError::NetworkError(format!("Failed to send frame: {e}"))
3529                    })?;
3530
3531                    send_stream.finish();
3532
3533                    debug!(
3534                        "Successfully relayed PUNCH_ME_NOW frame to peer {:?}",
3535                        target_peer
3536                    );
3537                    Ok(())
3538                } else {
3539                    warn!("No connection found for target peer {:?}", target_peer);
3540                    Err(NatTraversalError::PeerNotConnected)
3541                }
3542            }
3543
3544            crate::shared::EndpointEventInner::SendAddressFrame(add_address_frame) => {
3545                info!(
3546                    "Sending AddAddress frame for address {}",
3547                    add_address_frame.address
3548                );
3549
3550                // Find all active connections and send the AddAddress frame
3551                let connections = self.connections.read().map_err(|_| {
3552                    NatTraversalError::ProtocolError("Connections lock poisoned".to_string())
3553                })?;
3554
3555                for (peer_id, connection) in connections.iter() {
3556                    // Send AddAddress frame via unidirectional stream
3557                    let mut send_stream = connection.open_uni().await.map_err(|e| {
3558                        NatTraversalError::NetworkError(format!("Failed to open stream: {e}"))
3559                    })?;
3560
3561                    // Encode the frame data
3562                    let mut frame_data = Vec::new();
3563                    add_address_frame.encode(&mut frame_data);
3564
3565                    send_stream.write_all(&frame_data).await.map_err(|e| {
3566                        NatTraversalError::NetworkError(format!("Failed to send frame: {e}"))
3567                    })?;
3568
3569                    send_stream.finish();
3570
3571                    debug!("Sent AddAddress frame to peer {:?}", peer_id);
3572                }
3573
3574                Ok(())
3575            }
3576
3577            _ => {
3578                // Other endpoint events not related to NAT traversal
3579                debug!("Ignoring non-NAT traversal endpoint event: {:?}", event);
3580                Ok(())
3581            }
3582        }
3583    }
3584
3585    /// Establish connection to a validated candidate address
3586    async fn establish_connection_to_validated_candidate(
3587        &self,
3588        peer_id: PeerId,
3589        candidate_address: SocketAddr,
3590    ) -> Result<(), NatTraversalError> {
3591        info!(
3592            "Establishing connection to validated candidate {} for peer {:?}",
3593            candidate_address, peer_id
3594        );
3595
3596        let endpoint = self.quinn_endpoint.as_ref().ok_or_else(|| {
3597            NatTraversalError::ConfigError("Quinn endpoint not initialized".to_string())
3598        })?;
3599
3600        // Attempt connection to the validated address
3601        let connecting = endpoint
3602            .connect(candidate_address, "nat-traversal-peer")
3603            .map_err(|e| {
3604                NatTraversalError::ConnectionFailed(format!("Failed to initiate connection: {e}"))
3605            })?;
3606
3607        let connection = timeout(
3608            self.timeout_config
3609                .nat_traversal
3610                .connection_establishment_timeout,
3611            connecting,
3612        )
3613        .await
3614        .map_err(|_| NatTraversalError::Timeout)?
3615        .map_err(|e| NatTraversalError::ConnectionFailed(format!("Connection failed: {e}")))?;
3616
3617        // Store the established connection
3618        {
3619            let mut connections = self.connections.write().map_err(|_| {
3620                NatTraversalError::ProtocolError("Connections lock poisoned".to_string())
3621            })?;
3622            connections.insert(peer_id, connection.clone());
3623        }
3624
3625        // Update session state to completed
3626        {
3627            let mut sessions = self.active_sessions.write().map_err(|_| {
3628                NatTraversalError::ProtocolError("Sessions lock poisoned".to_string())
3629            })?;
3630            if let Some(session) = sessions.get_mut(&peer_id) {
3631                session.phase = TraversalPhase::Connected;
3632            }
3633        }
3634
3635        // Trigger success callback
3636        if let Some(ref callback) = self.event_callback {
3637            callback(NatTraversalEvent::ConnectionEstablished {
3638                peer_id,
3639                remote_address: candidate_address,
3640            });
3641        }
3642
3643        info!(
3644            "Successfully established connection to peer {:?} at {}",
3645            peer_id, candidate_address
3646        );
3647        Ok(())
3648    }
3649
3650    /// Send ADD_ADDRESS frame to advertise a candidate to a peer
3651    ///
3652    /// This is the bridge between candidate discovery and actual frame transmission.
3653    /// It finds the connection to the peer and sends an ADD_ADDRESS frame using
3654    /// the Quinn extension frame API.
3655    async fn send_candidate_advertisement(
3656        &self,
3657        peer_id: PeerId,
3658        candidate: &CandidateAddress,
3659    ) -> Result<(), NatTraversalError> {
3660        debug!(
3661            "Sending candidate advertisement to peer {:?}: {}",
3662            peer_id, candidate.address
3663        );
3664
3665        // Find the connection to this peer
3666        let connections = self.connections.read().map_err(|_| {
3667            NatTraversalError::ProtocolError("Connections lock poisoned".to_string())
3668        })?;
3669
3670        if let Some(_connection) = connections.get(&peer_id) {
3671            // Send ADD_ADDRESS frame using the ant-quic Connection's NAT traversal method
3672            debug!(
3673                "Found connection to peer {:?}, sending ADD_ADDRESS frame",
3674                peer_id
3675            );
3676
3677            // Extract connection to get a mutable reference
3678            // Since we're using the ant-quic Connection directly, we can call the NAT traversal methods
3679            drop(connections); // Release the read lock
3680
3681            // Get a mutable reference to the connection to send the frame
3682            let connections = self.connections.write().map_err(|_| {
3683                NatTraversalError::ProtocolError("Connections lock poisoned".to_string())
3684            })?;
3685
3686            if let Some(connection) = connections.get(&peer_id) {
3687                // Send ADD_ADDRESS frame using Quinn's datagram API
3688                // Frame format: [0x40][sequence][address][priority]
3689                let mut frame_data = Vec::new();
3690                frame_data.push(0x40); // ADD_ADDRESS frame type
3691
3692                // Encode sequence number (varint)
3693                let sequence = candidate.priority as u64; // Use priority as sequence for now
3694                frame_data.extend_from_slice(&sequence.to_be_bytes());
3695
3696                // Encode address
3697                match candidate.address {
3698                    SocketAddr::V4(addr) => {
3699                        frame_data.push(4); // IPv4 indicator
3700                        frame_data.extend_from_slice(&addr.ip().octets());
3701                        frame_data.extend_from_slice(&addr.port().to_be_bytes());
3702                    }
3703                    SocketAddr::V6(addr) => {
3704                        frame_data.push(6); // IPv6 indicator
3705                        frame_data.extend_from_slice(&addr.ip().octets());
3706                        frame_data.extend_from_slice(&addr.port().to_be_bytes());
3707                    }
3708                }
3709
3710                // Encode priority
3711                frame_data.extend_from_slice(&candidate.priority.to_be_bytes());
3712
3713                // Send as datagram
3714                match connection.send_datagram(frame_data.into()) {
3715                    Ok(()) => {
3716                        info!(
3717                            "Sent ADD_ADDRESS frame to peer {:?}: addr={}, priority={}",
3718                            peer_id, candidate.address, candidate.priority
3719                        );
3720                        Ok(())
3721                    }
3722                    Err(e) => {
3723                        warn!(
3724                            "Failed to send ADD_ADDRESS frame to peer {:?}: {}",
3725                            peer_id, e
3726                        );
3727                        Err(NatTraversalError::ProtocolError(format!(
3728                            "Failed to send ADD_ADDRESS frame: {e}"
3729                        )))
3730                    }
3731                }
3732            } else {
3733                // Connection disappeared between read and write lock
3734                debug!(
3735                    "Connection to peer {:?} disappeared during frame sending",
3736                    peer_id
3737                );
3738                Ok(())
3739            }
3740        } else {
3741            // No connection to this peer yet - this is normal during discovery
3742            debug!(
3743                "No connection found for peer {:?} - candidate will be sent when connection is established",
3744                peer_id
3745            );
3746            Ok(())
3747        }
3748    }
3749
3750    /// Send PUNCH_ME_NOW frame to coordinate hole punching
3751    ///
3752    /// This method sends hole punching coordination frames using the real
3753    /// Quinn extension frame API instead of application-level streams.
3754    async fn send_punch_coordination(
3755        &self,
3756        peer_id: PeerId,
3757        paired_with_sequence_number: u64,
3758        address: SocketAddr,
3759        round: u32,
3760    ) -> Result<(), NatTraversalError> {
3761        debug!(
3762            "Sending punch coordination to peer {:?}: seq={}, addr={}, round={}",
3763            peer_id, paired_with_sequence_number, address, round
3764        );
3765
3766        let connections = self.connections.read().map_err(|_| {
3767            NatTraversalError::ProtocolError("Connections lock poisoned".to_string())
3768        })?;
3769
3770        if let Some(connection) = connections.get(&peer_id) {
3771            // Send PUNCH_ME_NOW frame using Quinn's datagram API
3772            // Frame format: [0x41][round][paired_with_sequence_number][address]
3773            let mut frame_data = Vec::new();
3774            frame_data.push(0x41); // PUNCH_ME_NOW frame type
3775
3776            // Encode round number
3777            frame_data.extend_from_slice(&round.to_be_bytes());
3778
3779            // Encode paired_with_sequence_number
3780            frame_data.extend_from_slice(&paired_with_sequence_number.to_be_bytes());
3781
3782            // Encode address
3783            match address {
3784                SocketAddr::V4(addr) => {
3785                    frame_data.push(4); // IPv4 indicator
3786                    frame_data.extend_from_slice(&addr.ip().octets());
3787                    frame_data.extend_from_slice(&addr.port().to_be_bytes());
3788                }
3789                SocketAddr::V6(addr) => {
3790                    frame_data.push(6); // IPv6 indicator
3791                    frame_data.extend_from_slice(&addr.ip().octets());
3792                    frame_data.extend_from_slice(&addr.port().to_be_bytes());
3793                }
3794            }
3795
3796            // Send as datagram
3797            match connection.send_datagram(frame_data.into()) {
3798                Ok(()) => {
3799                    info!(
3800                        "Sent PUNCH_ME_NOW frame to peer {:?}: paired_with_seq={}, addr={}, round={}",
3801                        peer_id, paired_with_sequence_number, address, round
3802                    );
3803                    Ok(())
3804                }
3805                Err(e) => {
3806                    warn!(
3807                        "Failed to send PUNCH_ME_NOW frame to peer {:?}: {}",
3808                        peer_id, e
3809                    );
3810                    Err(NatTraversalError::ProtocolError(format!(
3811                        "Failed to send PUNCH_ME_NOW frame: {e}"
3812                    )))
3813                }
3814            }
3815        } else {
3816            Err(NatTraversalError::PeerNotConnected)
3817        }
3818    }
3819
3820    /// Get NAT traversal statistics
3821    pub fn get_nat_stats(
3822        &self,
3823    ) -> Result<NatTraversalStatistics, Box<dyn std::error::Error + Send + Sync>> {
3824        // Return default statistics for now
3825        // In a real implementation, this would collect actual stats from the endpoint
3826        Ok(NatTraversalStatistics {
3827            active_sessions: self.active_sessions.read().unwrap().len(),
3828            total_bootstrap_nodes: self.bootstrap_nodes.read().unwrap().len(),
3829            successful_coordinations: 7,
3830            average_coordination_time: self.timeout_config.nat_traversal.retry_interval,
3831            total_attempts: 10,
3832            successful_connections: 7,
3833            direct_connections: 5,
3834            relayed_connections: 2,
3835        })
3836    }
3837}
3838
3839impl fmt::Debug for NatTraversalEndpoint {
3840    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3841        f.debug_struct("NatTraversalEndpoint")
3842            .field("config", &self.config)
3843            .field("bootstrap_nodes", &"<RwLock>")
3844            .field("active_sessions", &"<RwLock>")
3845            .field("event_callback", &self.event_callback.is_some())
3846            .finish()
3847    }
3848}
3849
3850/// Statistics about NAT traversal performance
3851#[derive(Debug, Clone, Default)]
3852pub struct NatTraversalStatistics {
3853    /// Number of active NAT traversal sessions
3854    pub active_sessions: usize,
3855    /// Total number of known bootstrap nodes
3856    pub total_bootstrap_nodes: usize,
3857    /// Total successful coordinations
3858    pub successful_coordinations: u32,
3859    /// Average time for coordination
3860    pub average_coordination_time: Duration,
3861    /// Total NAT traversal attempts
3862    pub total_attempts: u32,
3863    /// Successful connections established
3864    pub successful_connections: u32,
3865    /// Direct connections established (no relay)
3866    pub direct_connections: u32,
3867    /// Relayed connections
3868    pub relayed_connections: u32,
3869}
3870
3871impl fmt::Display for NatTraversalError {
3872    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3873        match self {
3874            Self::NoBootstrapNodes => write!(f, "no bootstrap nodes available"),
3875            Self::NoCandidatesFound => write!(f, "no address candidates found"),
3876            Self::CandidateDiscoveryFailed(msg) => write!(f, "candidate discovery failed: {msg}"),
3877            Self::CoordinationFailed(msg) => write!(f, "coordination failed: {msg}"),
3878            Self::HolePunchingFailed => write!(f, "hole punching failed"),
3879            Self::PunchingFailed(msg) => write!(f, "punching failed: {msg}"),
3880            Self::ValidationFailed(msg) => write!(f, "validation failed: {msg}"),
3881            Self::ValidationTimeout => write!(f, "validation timeout"),
3882            Self::NetworkError(msg) => write!(f, "network error: {msg}"),
3883            Self::ConfigError(msg) => write!(f, "configuration error: {msg}"),
3884            Self::ProtocolError(msg) => write!(f, "protocol error: {msg}"),
3885            Self::Timeout => write!(f, "operation timed out"),
3886            Self::ConnectionFailed(msg) => write!(f, "connection failed: {msg}"),
3887            Self::TraversalFailed(msg) => write!(f, "traversal failed: {msg}"),
3888            Self::PeerNotConnected => write!(f, "peer not connected"),
3889        }
3890    }
3891}
3892
3893impl std::error::Error for NatTraversalError {}
3894
3895impl fmt::Display for PeerId {
3896    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3897        // Display first 8 bytes as hex (16 characters)
3898        for byte in &self.0[..8] {
3899            write!(f, "{byte:02x}")?;
3900        }
3901        Ok(())
3902    }
3903}
3904
3905impl From<[u8; 32]> for PeerId {
3906    fn from(bytes: [u8; 32]) -> Self {
3907        Self(bytes)
3908    }
3909}
3910
3911/// Dummy certificate verifier that accepts any certificate
3912/// WARNING: This is only for testing/demo purposes - use proper verification in production!
3913#[derive(Debug)]
3914struct SkipServerVerification;
3915
3916impl SkipServerVerification {
3917    fn new() -> Arc<Self> {
3918        Arc::new(Self)
3919    }
3920}
3921
3922impl rustls::client::danger::ServerCertVerifier for SkipServerVerification {
3923    fn verify_server_cert(
3924        &self,
3925        _end_entity: &rustls::pki_types::CertificateDer<'_>,
3926        _intermediates: &[rustls::pki_types::CertificateDer<'_>],
3927        _server_name: &rustls::pki_types::ServerName<'_>,
3928        _ocsp_response: &[u8],
3929        _now: rustls::pki_types::UnixTime,
3930    ) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
3931        Ok(rustls::client::danger::ServerCertVerified::assertion())
3932    }
3933
3934    fn verify_tls12_signature(
3935        &self,
3936        _message: &[u8],
3937        _cert: &rustls::pki_types::CertificateDer<'_>,
3938        _dss: &rustls::DigitallySignedStruct,
3939    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
3940        Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
3941    }
3942
3943    fn verify_tls13_signature(
3944        &self,
3945        _message: &[u8],
3946        _cert: &rustls::pki_types::CertificateDer<'_>,
3947        _dss: &rustls::DigitallySignedStruct,
3948    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
3949        Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
3950    }
3951
3952    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
3953        vec![
3954            rustls::SignatureScheme::RSA_PKCS1_SHA256,
3955            rustls::SignatureScheme::ECDSA_NISTP256_SHA256,
3956            rustls::SignatureScheme::ED25519,
3957        ]
3958    }
3959}
3960
3961/// Default token store that accepts all tokens (for demo purposes)
3962struct DefaultTokenStore;
3963
3964impl crate::TokenStore for DefaultTokenStore {
3965    fn insert(&self, _server_name: &str, _token: bytes::Bytes) {
3966        // Ignore token storage for demo
3967    }
3968
3969    fn take(&self, _server_name: &str) -> Option<bytes::Bytes> {
3970        None
3971    }
3972}
3973
3974#[cfg(test)]
3975mod tests {
3976    use super::*;
3977
3978    #[test]
3979    fn test_nat_traversal_config_default() {
3980        let config = NatTraversalConfig::default();
3981        assert_eq!(config.role, EndpointRole::Client);
3982        assert_eq!(config.max_candidates, 8);
3983        assert!(config.enable_symmetric_nat);
3984        assert!(config.enable_relay_fallback);
3985    }
3986
3987    #[test]
3988    fn test_peer_id_display() {
3989        let peer_id = PeerId([
3990            0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
3991            0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, 0x22, 0x33,
3992            0x44, 0x55, 0x66, 0x77,
3993        ]);
3994        assert_eq!(format!("{peer_id}"), "0123456789abcdef");
3995    }
3996
3997    #[test]
3998    fn test_bootstrap_node_management() {
3999        let _config = NatTraversalConfig::default();
4000        // Note: This will fail due to ServerConfig requirement in new() - for illustration only
4001        // let endpoint = NatTraversalEndpoint::new(config, None).unwrap();
4002    }
4003
4004    #[test]
4005    fn test_candidate_address_validation() {
4006        use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
4007
4008        // Valid addresses
4009        assert!(
4010            CandidateAddress::validate_address(&SocketAddr::new(
4011                IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)),
4012                8080
4013            ))
4014            .is_ok()
4015        );
4016
4017        assert!(
4018            CandidateAddress::validate_address(&SocketAddr::new(
4019                IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)),
4020                53
4021            ))
4022            .is_ok()
4023        );
4024
4025        assert!(
4026            CandidateAddress::validate_address(&SocketAddr::new(
4027                IpAddr::V6(Ipv6Addr::new(0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8888)),
4028                443
4029            ))
4030            .is_ok()
4031        );
4032
4033        // Invalid port 0
4034        assert!(matches!(
4035            CandidateAddress::validate_address(&SocketAddr::new(
4036                IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)),
4037                0
4038            )),
4039            Err(CandidateValidationError::InvalidPort(0))
4040        ));
4041
4042        // Privileged port (non-test mode would fail)
4043        #[cfg(not(test))]
4044        assert!(matches!(
4045            CandidateAddress::validate_address(&SocketAddr::new(
4046                IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)),
4047                80
4048            )),
4049            Err(CandidateValidationError::PrivilegedPort(80))
4050        ));
4051
4052        // Unspecified addresses
4053        assert!(matches!(
4054            CandidateAddress::validate_address(&SocketAddr::new(
4055                IpAddr::V4(Ipv4Addr::UNSPECIFIED),
4056                8080
4057            )),
4058            Err(CandidateValidationError::UnspecifiedAddress)
4059        ));
4060
4061        assert!(matches!(
4062            CandidateAddress::validate_address(&SocketAddr::new(
4063                IpAddr::V6(Ipv6Addr::UNSPECIFIED),
4064                8080
4065            )),
4066            Err(CandidateValidationError::UnspecifiedAddress)
4067        ));
4068
4069        // Broadcast address
4070        assert!(matches!(
4071            CandidateAddress::validate_address(&SocketAddr::new(
4072                IpAddr::V4(Ipv4Addr::BROADCAST),
4073                8080
4074            )),
4075            Err(CandidateValidationError::BroadcastAddress)
4076        ));
4077
4078        // Multicast addresses
4079        assert!(matches!(
4080            CandidateAddress::validate_address(&SocketAddr::new(
4081                IpAddr::V4(Ipv4Addr::new(224, 0, 0, 1)),
4082                8080
4083            )),
4084            Err(CandidateValidationError::MulticastAddress)
4085        ));
4086
4087        assert!(matches!(
4088            CandidateAddress::validate_address(&SocketAddr::new(
4089                IpAddr::V6(Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1)),
4090                8080
4091            )),
4092            Err(CandidateValidationError::MulticastAddress)
4093        ));
4094
4095        // Reserved addresses
4096        assert!(matches!(
4097            CandidateAddress::validate_address(&SocketAddr::new(
4098                IpAddr::V4(Ipv4Addr::new(0, 0, 0, 1)),
4099                8080
4100            )),
4101            Err(CandidateValidationError::ReservedAddress)
4102        ));
4103
4104        assert!(matches!(
4105            CandidateAddress::validate_address(&SocketAddr::new(
4106                IpAddr::V4(Ipv4Addr::new(240, 0, 0, 1)),
4107                8080
4108            )),
4109            Err(CandidateValidationError::ReservedAddress)
4110        ));
4111
4112        // Documentation address
4113        assert!(matches!(
4114            CandidateAddress::validate_address(&SocketAddr::new(
4115                IpAddr::V6(Ipv6Addr::new(0x2001, 0x0db8, 0, 0, 0, 0, 0, 1)),
4116                8080
4117            )),
4118            Err(CandidateValidationError::DocumentationAddress)
4119        ));
4120
4121        // IPv4-mapped IPv6
4122        assert!(matches!(
4123            CandidateAddress::validate_address(&SocketAddr::new(
4124                IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc0a8, 0x0001)),
4125                8080
4126            )),
4127            Err(CandidateValidationError::IPv4MappedAddress)
4128        ));
4129    }
4130
4131    #[test]
4132    fn test_candidate_address_suitability_for_nat_traversal() {
4133        use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
4134
4135        // Create valid candidates
4136        let public_v4 = CandidateAddress::new(
4137            SocketAddr::new(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)), 8080),
4138            100,
4139            CandidateSource::Observed { by_node: None },
4140        )
4141        .unwrap();
4142        assert!(public_v4.is_suitable_for_nat_traversal());
4143
4144        let private_v4 = CandidateAddress::new(
4145            SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)), 8080),
4146            100,
4147            CandidateSource::Local,
4148        )
4149        .unwrap();
4150        assert!(private_v4.is_suitable_for_nat_traversal());
4151
4152        // Link-local should not be suitable
4153        let link_local_v4 = CandidateAddress::new(
4154            SocketAddr::new(IpAddr::V4(Ipv4Addr::new(169, 254, 1, 1)), 8080),
4155            100,
4156            CandidateSource::Local,
4157        )
4158        .unwrap();
4159        assert!(!link_local_v4.is_suitable_for_nat_traversal());
4160
4161        // Global unicast IPv6 should be suitable
4162        let global_v6 = CandidateAddress::new(
4163            SocketAddr::new(
4164                IpAddr::V6(Ipv6Addr::new(0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8888)),
4165                8080,
4166            ),
4167            100,
4168            CandidateSource::Observed { by_node: None },
4169        )
4170        .unwrap();
4171        assert!(global_v6.is_suitable_for_nat_traversal());
4172
4173        // Link-local IPv6 should not be suitable
4174        let link_local_v6 = CandidateAddress::new(
4175            SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)), 8080),
4176            100,
4177            CandidateSource::Local,
4178        )
4179        .unwrap();
4180        assert!(!link_local_v6.is_suitable_for_nat_traversal());
4181
4182        // Unique local IPv6 should not be suitable for external traversal
4183        let unique_local_v6 = CandidateAddress::new(
4184            SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0xfc00, 0, 0, 0, 0, 0, 0, 1)), 8080),
4185            100,
4186            CandidateSource::Local,
4187        )
4188        .unwrap();
4189        assert!(!unique_local_v6.is_suitable_for_nat_traversal());
4190
4191        // Loopback should be suitable only in test mode
4192        #[cfg(test)]
4193        {
4194            let loopback_v4 = CandidateAddress::new(
4195                SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8080),
4196                100,
4197                CandidateSource::Local,
4198            )
4199            .unwrap();
4200            assert!(loopback_v4.is_suitable_for_nat_traversal());
4201
4202            let loopback_v6 = CandidateAddress::new(
4203                SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 8080),
4204                100,
4205                CandidateSource::Local,
4206            )
4207            .unwrap();
4208            assert!(loopback_v6.is_suitable_for_nat_traversal());
4209        }
4210    }
4211
4212    #[test]
4213    fn test_candidate_effective_priority() {
4214        use std::net::{IpAddr, Ipv4Addr};
4215
4216        let mut candidate = CandidateAddress::new(
4217            SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)), 8080),
4218            100,
4219            CandidateSource::Local,
4220        )
4221        .unwrap();
4222
4223        // New state - slightly reduced priority
4224        assert_eq!(candidate.effective_priority(), 90);
4225
4226        // Validating state - small reduction
4227        candidate.state = CandidateState::Validating;
4228        assert_eq!(candidate.effective_priority(), 95);
4229
4230        // Valid state - full priority
4231        candidate.state = CandidateState::Valid;
4232        assert_eq!(candidate.effective_priority(), 100);
4233
4234        // Failed state - zero priority
4235        candidate.state = CandidateState::Failed;
4236        assert_eq!(candidate.effective_priority(), 0);
4237
4238        // Removed state - zero priority
4239        candidate.state = CandidateState::Removed;
4240        assert_eq!(candidate.effective_priority(), 0);
4241    }
4242}