ant_quic/connection/
nat_traversal.rs

1// Copyright 2024 Saorsa Labs Ltd.
2//
3// This Saorsa Network Software is licensed under the General Public License (GPL), version 3.
4// Please see the file LICENSE-GPL, or visit <http://www.gnu.org/licenses/> for the full text.
5//
6// Full details available at https://saorsalabs.com/licenses
7
8use std::{
9    collections::{HashMap, VecDeque},
10    net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
11    time::Duration,
12};
13
14use crate::shared::ConnectionId;
15use tracing::{debug, info, trace, warn};
16
17use crate::{Instant, VarInt};
18
19/// NAT traversal state for a QUIC connection
20///
21/// This manages address candidate discovery, validation, and coordination
22/// for establishing direct P2P connections through NATs.
23///
24/// v0.13.0: All nodes are symmetric P2P nodes - no role distinction.
25/// Every node can initiate, accept, and coordinate NAT traversal.
26#[derive(Debug)]
27pub(super) struct NatTraversalState {
28    // v0.13.0: role field removed - all nodes are symmetric P2P nodes
29    /// Candidate addresses we've advertised to the peer
30    pub(super) local_candidates: HashMap<VarInt, AddressCandidate>,
31    /// Candidate addresses received from the peer
32    pub(super) remote_candidates: HashMap<VarInt, AddressCandidate>,
33    /// Generated candidate pairs for connectivity testing
34    pub(super) candidate_pairs: Vec<CandidatePair>,
35    /// Index for fast pair lookup by remote address (maintained during generation)
36    pub(super) pair_index: HashMap<SocketAddr, usize>,
37    /// Currently active path validation attempts
38    pub(super) active_validations: HashMap<SocketAddr, PathValidationState>,
39    /// Coordination state for simultaneous hole punching
40    pub(super) coordination: Option<CoordinationState>,
41    /// Sequence number for address advertisements
42    pub(super) next_sequence: VarInt,
43    /// Maximum candidates we're willing to handle
44    pub(super) max_candidates: u32,
45    /// Timeout for coordination rounds
46    pub(super) coordination_timeout: Duration,
47    /// Statistics for this NAT traversal session
48    pub(super) stats: NatTraversalStats,
49    /// Security validation state
50    pub(super) security_state: SecurityValidationState,
51    /// Network condition monitoring for adaptive timeouts
52    pub(super) network_monitor: NetworkConditionMonitor,
53    /// Resource management and cleanup coordinator
54    pub(super) resource_manager: ResourceCleanupCoordinator,
55    /// Coordination support - all nodes can coordinate (v0.13.0: always enabled)
56    pub(super) bootstrap_coordinator: Option<BootstrapCoordinator>,
57}
58// v0.13.0: NatTraversalRole enum removed - all nodes are symmetric P2P nodes
59// Every node can initiate, accept, and coordinate NAT traversal without role distinction.
60/// Address candidate with metadata
61#[derive(Debug, Clone)]
62pub(super) struct AddressCandidate {
63    /// The socket address
64    pub(super) address: SocketAddr,
65    /// Priority for ICE-like selection (higher = better)
66    pub(super) priority: u32,
67    /// How this candidate was discovered
68    pub(super) source: CandidateSource,
69    /// When this candidate was first learned
70    pub(super) discovered_at: Instant,
71    /// Current state of this candidate
72    pub(super) state: CandidateState,
73    /// Number of validation attempts for this candidate
74    pub(super) attempt_count: u32,
75    /// Last validation attempt time
76    pub(super) last_attempt: Option<Instant>,
77}
78/// How an address candidate was discovered
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80pub enum CandidateSource {
81    /// Local network interface
82    Local,
83    /// Observed by a bootstrap node
84    ///
85    /// When present, `by_node` identifies the coordinator that reported the
86    /// observation using its node identifier.
87    Observed {
88        /// Identifier of the coordinator that observed our address
89        by_node: Option<VarInt>,
90    },
91    /// Received from peer via AddAddress frame
92    Peer,
93    /// Generated prediction for symmetric NAT
94    Predicted,
95}
96/// Current state of a candidate address
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98pub enum CandidateState {
99    /// Newly discovered, not yet tested
100    New,
101    /// Currently being validated
102    Validating,
103    /// Successfully validated and usable
104    Valid,
105    /// Validation failed
106    Failed,
107    /// Removed by peer or expired
108    Removed,
109}
110/// State of an individual path validation attempt
111#[derive(Debug)]
112#[allow(dead_code)]
113pub(super) struct PathValidationState {
114    /// Challenge value sent
115    pub(super) challenge: u64,
116    /// When the challenge was sent
117    pub(super) sent_at: Instant,
118    /// Number of retransmissions
119    pub(super) retry_count: u32,
120    /// Maximum retries allowed
121    pub(super) max_retries: u32,
122    /// Associated with a coordination round (if any)
123    pub(super) coordination_round: Option<VarInt>,
124    /// Adaptive timeout state
125    pub(super) timeout_state: AdaptiveTimeoutState,
126    /// Last retry attempt time
127    pub(super) last_retry_at: Option<Instant>,
128}
129/// Coordination state for simultaneous hole punching
130#[derive(Debug)]
131#[allow(dead_code)]
132pub(super) struct CoordinationState {
133    /// Current coordination round number
134    pub(super) round: VarInt,
135    /// Addresses we're punching to in this round
136    pub(super) punch_targets: Vec<PunchTarget>,
137    /// When this round started (coordination phase)
138    pub(super) round_start: Instant,
139    /// When hole punching should begin (synchronized time)
140    pub(super) punch_start: Instant,
141    /// Duration of this coordination round
142    pub(super) round_duration: Duration,
143    /// Current state of this coordination round
144    pub(super) state: CoordinationPhase,
145    /// Whether we've sent our PUNCH_ME_NOW to coordinator
146    pub(super) punch_request_sent: bool,
147    /// Whether we've received peer's PUNCH_ME_NOW via coordinator
148    pub(super) peer_punch_received: bool,
149    /// Retry count for this round
150    pub(super) retry_count: u32,
151    /// Maximum retries before giving up
152    pub(super) max_retries: u32,
153    /// Adaptive timeout state for coordination
154    pub(super) timeout_state: AdaptiveTimeoutState,
155    /// Last retry attempt time
156    pub(super) last_retry_at: Option<Instant>,
157}
158/// Phases of the coordination protocol
159#[derive(Debug, Clone, Copy, PartialEq, Eq)]
160#[allow(dead_code)]
161pub(crate) enum CoordinationPhase {
162    /// Waiting to start coordination
163    Idle,
164    /// Sending PUNCH_ME_NOW to coordinator
165    Requesting,
166    /// Waiting for peer's PUNCH_ME_NOW via coordinator
167    Coordinating,
168    /// Grace period before synchronized hole punching
169    Preparing,
170    /// Actively sending PATH_CHALLENGE packets
171    Punching,
172    /// Waiting for PATH_RESPONSE validation
173    Validating,
174    /// This round completed successfully
175    Succeeded,
176    /// This round failed, may retry
177    Failed,
178}
179/// Target for hole punching in a coordination round
180#[derive(Debug, Clone)]
181pub(super) struct PunchTarget {
182    /// Remote address to punch to
183    pub(super) remote_addr: SocketAddr,
184    /// Sequence number of the remote candidate
185    pub(super) remote_sequence: VarInt,
186    /// Challenge value for validation
187    pub(super) challenge: u64,
188}
189/// Actions to take when handling NAT traversal timeouts
190#[derive(Debug, Clone, PartialEq, Eq)]
191pub(super) enum TimeoutAction {
192    /// Retry candidate discovery
193    RetryDiscovery,
194    /// Retry coordination with bootstrap node
195    RetryCoordination,
196    /// Start path validation for discovered candidates
197    StartValidation,
198    /// NAT traversal completed successfully
199    Complete,
200    /// NAT traversal failed
201    Failed,
202}
203
204/// Candidate pair for ICE-like connectivity testing
205#[derive(Debug, Clone)]
206#[allow(dead_code)]
207pub(super) struct CandidatePair {
208    /// Sequence of remote candidate
209    pub(super) remote_sequence: VarInt,
210    /// Our local address for this pair
211    pub(super) local_addr: SocketAddr,
212    /// Remote address we're testing connectivity to
213    pub(super) remote_addr: SocketAddr,
214    /// Combined priority for pair ordering (higher = better)
215    pub(super) priority: u64,
216    /// Current state of this pair
217    pub(super) state: PairState,
218    /// Type classification for this pair
219    pub(super) pair_type: PairType,
220    /// When this pair was created
221    pub(super) created_at: Instant,
222    /// When validation was last attempted
223    pub(super) last_check: Option<Instant>,
224}
225/// State of a candidate pair during validation
226#[derive(Debug, Clone, Copy, PartialEq, Eq)]
227#[allow(dead_code)]
228pub(super) enum PairState {
229    /// Waiting to be tested
230    Waiting,
231    /// Validation succeeded - this pair works
232    Succeeded,
233    /// Validation failed
234    Failed,
235    /// Temporarily frozen (waiting for other pairs)
236    Frozen,
237}
238/// Type classification for candidate pairs (based on ICE)
239#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
240pub(super) enum PairType {
241    /// Both candidates are on local network
242    HostToHost,
243    /// Local is host, remote is server reflexive (through NAT)
244    HostToServerReflexive,
245    /// Local is server reflexive, remote is host
246    ServerReflexiveToHost,
247    /// Both are server reflexive (both behind NAT)
248    ServerReflexiveToServerReflexive,
249    /// One side is peer reflexive (learned from peer)
250    PeerReflexive,
251}
252/// Type of address candidate (following ICE terminology)
253#[derive(Debug, Clone, Copy, PartialEq, Eq)]
254pub(super) enum CandidateType {
255    /// Host candidate - directly reachable local interface
256    Host,
257    /// Server reflexive - public address observed by bootstrap node
258    ServerReflexive,
259    /// Peer reflexive - address learned from incoming packets
260    PeerReflexive,
261}
262
263/// Calculate ICE-like priority for an address candidate
264/// Based on RFC 8445 Section 5.1.2.1
265#[allow(dead_code)]
266fn calculate_candidate_priority(
267    candidate_type: CandidateType,
268    local_preference: u16,
269    component_id: u8,
270) -> u32 {
271    let type_preference = match candidate_type {
272        CandidateType::Host => 126,
273        CandidateType::PeerReflexive => 110,
274        CandidateType::ServerReflexive => 100,
275    };
276    // ICE priority formula: (2^24 * type_pref) + (2^8 * local_pref) + component_id
277    (1u32 << 24) * type_preference + (1u32 << 8) * local_preference as u32 + component_id as u32
278}
279
280/// Calculate combined priority for a candidate pair
281/// Based on RFC 8445 Section 6.1.2.3
282fn calculate_pair_priority(local_priority: u32, remote_priority: u32) -> u64 {
283    let g = local_priority as u64;
284    let d = remote_priority as u64;
285    // ICE pair priority formula: 2^32 * MIN(G,D) + 2 * MAX(G,D) + (G>D ? 1 : 0)
286    (1u64 << 32) * g.min(d) + 2 * g.max(d) + if g > d { 1 } else { 0 }
287}
288
289/// Determine candidate type from source information
290fn classify_candidate_type(source: CandidateSource) -> CandidateType {
291    match source {
292        CandidateSource::Local => CandidateType::Host,
293        CandidateSource::Observed { .. } => CandidateType::ServerReflexive,
294        CandidateSource::Peer => CandidateType::PeerReflexive,
295        CandidateSource::Predicted => CandidateType::ServerReflexive, // Symmetric NAT prediction
296    }
297}
298/// Determine pair type from individual candidate types
299fn classify_pair_type(local_type: CandidateType, remote_type: CandidateType) -> PairType {
300    match (local_type, remote_type) {
301        (CandidateType::Host, CandidateType::Host) => PairType::HostToHost,
302        (CandidateType::Host, CandidateType::ServerReflexive) => PairType::HostToServerReflexive,
303        (CandidateType::ServerReflexive, CandidateType::Host) => PairType::ServerReflexiveToHost,
304        (CandidateType::ServerReflexive, CandidateType::ServerReflexive) => {
305            PairType::ServerReflexiveToServerReflexive
306        }
307        (CandidateType::PeerReflexive, _) | (_, CandidateType::PeerReflexive) => {
308            PairType::PeerReflexive
309        }
310    }
311}
312/// Check if two candidates are compatible for pairing
313fn are_candidates_compatible(local: &AddressCandidate, remote: &AddressCandidate) -> bool {
314    // Must be same address family (IPv4 with IPv4, IPv6 with IPv6)
315    match (local.address, remote.address) {
316        (SocketAddr::V4(_), SocketAddr::V4(_)) => true,
317        (SocketAddr::V6(_), SocketAddr::V6(_)) => true,
318        _ => false, // No IPv4/IPv6 mixing for now
319    }
320}
321/// Statistics for NAT traversal attempts
322#[derive(Debug, Default, Clone)]
323#[allow(dead_code)]
324pub(crate) struct NatTraversalStats {
325    /// Total candidates received from peer
326    pub(super) remote_candidates_received: u32,
327    /// Total candidates we've advertised
328    pub(super) local_candidates_sent: u32,
329    /// Successful validations
330    pub(super) validations_succeeded: u32,
331    /// Failed validations
332    pub(super) validations_failed: u32,
333    /// Coordination rounds attempted
334    pub(super) coordination_rounds: u32,
335    /// Successful coordinations
336    pub(super) successful_coordinations: u32,
337    /// Failed coordinations
338    pub(super) failed_coordinations: u32,
339    /// Timed out coordinations
340    pub(super) timed_out_coordinations: u32,
341    /// Coordination failures due to poor network conditions
342    pub(super) coordination_failures: u32,
343    /// Successful direct connections established
344    pub(super) direct_connections: u32,
345    /// Security validation rejections
346    pub(super) security_rejections: u32,
347    /// Rate limiting violations
348    pub(super) rate_limit_violations: u32,
349    /// Invalid address rejections
350    pub(super) invalid_address_rejections: u32,
351    /// Suspicious coordination attempts
352    pub(super) suspicious_coordination_attempts: u32,
353    /// Callback probes received (TryConnectToResponse)
354    pub(super) callback_probes_received: u32,
355    /// Callback probes that succeeded
356    pub(super) callback_probes_successful: u32,
357    /// Callback probes that failed
358    pub(super) callback_probes_failed: u32,
359}
360/// Security validation state for rate limiting and attack detection
361#[derive(Debug)]
362#[allow(dead_code)]
363pub(super) struct SecurityValidationState {
364    /// Rate limiting: track candidate additions per time window
365    candidate_rate_tracker: VecDeque<Instant>,
366    /// Maximum candidates per time window
367    max_candidates_per_window: u32,
368    /// Rate limiting time window
369    rate_window: Duration,
370    /// Coordination request tracking for suspicious patterns
371    coordination_requests: VecDeque<CoordinationRequest>,
372    /// Maximum coordination requests per time window
373    max_coordination_per_window: u32,
374    /// Address validation cache to avoid repeated validation
375    address_validation_cache: HashMap<SocketAddr, AddressValidationResult>,
376    /// Cache timeout for address validation
377    validation_cache_timeout: Duration,
378}
379/// Coordination request tracking for security analysis
380#[derive(Debug, Clone)]
381struct CoordinationRequest {
382    /// When the request was made
383    timestamp: Instant,
384}
385/// Result of address validation
386#[derive(Debug, Clone, Copy, PartialEq, Eq)]
387enum AddressValidationResult {
388    /// Address is valid and safe
389    Valid,
390    /// Address is invalid (malformed, reserved, etc.)
391    Invalid,
392    /// Address is suspicious (potential attack)
393    Suspicious,
394}
395/// Adaptive timeout state for network condition awareness
396#[derive(Debug, Clone)]
397pub(super) struct AdaptiveTimeoutState {
398    /// Current timeout value
399    current_timeout: Duration,
400    /// Minimum allowed timeout
401    min_timeout: Duration,
402    /// Maximum allowed timeout
403    max_timeout: Duration,
404    /// Base timeout for exponential backoff
405    base_timeout: Duration,
406    /// Current backoff multiplier
407    backoff_multiplier: f64,
408    /// Maximum backoff multiplier
409    max_backoff_multiplier: f64,
410    /// Jitter factor for randomization
411    jitter_factor: f64,
412    /// Smoothed round-trip time estimation
413    srtt: Option<Duration>,
414    /// Round-trip time variance
415    rttvar: Option<Duration>,
416    /// Last successful round-trip time
417    last_rtt: Option<Duration>,
418    /// Number of consecutive timeouts
419    consecutive_timeouts: u32,
420    /// Number of successful responses
421    successful_responses: u32,
422}
423/// Network condition monitoring for adaptive behavior
424#[derive(Debug)]
425#[allow(dead_code)]
426pub(super) struct NetworkConditionMonitor {
427    /// Recent round-trip time measurements
428    rtt_samples: VecDeque<Duration>,
429    /// Maximum samples to keep
430    max_samples: usize,
431    /// Packet loss rate estimation
432    packet_loss_rate: f64,
433    /// Congestion window estimate
434    congestion_window: u32,
435    /// Network quality score (0.0 = poor, 1.0 = excellent)
436    quality_score: f64,
437    /// Last quality update time
438    last_quality_update: Instant,
439    /// Quality measurement interval
440    quality_update_interval: Duration,
441    /// Timeout statistics
442    timeout_stats: TimeoutStatistics,
443}
444/// Statistics for timeout behavior
445#[derive(Debug, Default)]
446struct TimeoutStatistics {
447    /// Total timeout events
448    total_timeouts: u64,
449    /// Total successful responses
450    total_responses: u64,
451    /// Average response time
452    avg_response_time: Duration,
453    /// Timeout rate (0.0 = no timeouts, 1.0 = all timeouts)
454    timeout_rate: f64,
455    /// Last update time
456    last_update: Option<Instant>,
457}
458#[allow(dead_code)]
459impl SecurityValidationState {
460    /// Create new security validation state with default settings
461    fn new() -> Self {
462        Self {
463            candidate_rate_tracker: VecDeque::new(),
464            max_candidates_per_window: 20, // Max 20 candidates per 60 seconds
465            rate_window: Duration::from_secs(60),
466            coordination_requests: VecDeque::new(),
467            max_coordination_per_window: 5, // Max 5 coordination requests per 60 seconds
468            address_validation_cache: HashMap::new(),
469            validation_cache_timeout: Duration::from_secs(300), // 5 minute cache
470        }
471    }
472    /// Create new security validation state with custom rate limits
473    fn new_with_limits(
474        max_candidates_per_window: u32,
475        max_coordination_per_window: u32,
476        rate_window: Duration,
477    ) -> Self {
478        Self {
479            candidate_rate_tracker: VecDeque::new(),
480            max_candidates_per_window,
481            rate_window,
482            coordination_requests: VecDeque::new(),
483            max_coordination_per_window,
484            address_validation_cache: HashMap::new(),
485            validation_cache_timeout: Duration::from_secs(300),
486        }
487    }
488    /// Enhanced rate limiting with adaptive thresholds
489    ///
490    /// This implements adaptive rate limiting that adjusts based on network conditions
491    /// and detected attack patterns to prevent flooding while maintaining usability.
492    fn is_adaptive_rate_limited(&mut self, peer_id: [u8; 32], now: Instant) -> bool {
493        // Clean up old entries first
494        self.cleanup_rate_tracker(now);
495        self.cleanup_coordination_tracker(now);
496        // Calculate current request rate
497        let _current_candidate_rate =
498            self.candidate_rate_tracker.len() as f64 / self.rate_window.as_secs_f64();
499        let _current_coordination_rate =
500            self.coordination_requests.len() as f64 / self.rate_window.as_secs_f64();
501
502        // Adaptive threshold based on peer behavior
503        let peer_reputation = self.calculate_peer_reputation(peer_id);
504        let adaptive_candidate_limit =
505            (self.max_candidates_per_window as f64 * peer_reputation) as u32;
506        let adaptive_coordination_limit =
507            (self.max_coordination_per_window as f64 * peer_reputation) as u32;
508
509        // Check if either limit is exceeded
510        if self.candidate_rate_tracker.len() >= adaptive_candidate_limit as usize {
511            debug!(
512                "Adaptive candidate rate limit exceeded for peer {:?}: {} >= {}",
513                hex::encode(&peer_id[..8]),
514                self.candidate_rate_tracker.len(),
515                adaptive_candidate_limit
516            );
517            return true;
518        }
519
520        if self.coordination_requests.len() >= adaptive_coordination_limit as usize {
521            debug!(
522                "Adaptive coordination rate limit exceeded for peer {:?}: {} >= {}",
523                hex::encode(&peer_id[..8]),
524                self.coordination_requests.len(),
525                adaptive_coordination_limit
526            );
527            return true;
528        }
529
530        false
531    }
532
533    /// Calculate peer reputation score (0.0 = bad, 1.0 = good)
534    ///
535    /// This implements a simple reputation system to adjust rate limits
536    /// based on peer behavior patterns.
537    fn calculate_peer_reputation(&self, _peer_id: [u8; 32]) -> f64 {
538        // Simplified reputation calculation
539        // In production, this would track:
540        // - Historical success rates
541        // - Suspicious behavior patterns
542        // - Coordination completion rates
543        // - Address validation failures
544        // For now, return a default good reputation
545        // This can be enhanced with persistent peer reputation storage
546        1.0
547    }
548
549    /// Implement amplification attack mitigation
550    ///
551    /// This prevents the bootstrap node from being used as an amplifier
552    /// in DDoS attacks by limiting server-initiated validation packets.
553    fn validate_amplification_limits(
554        &mut self,
555        source_addr: SocketAddr,
556        target_addr: SocketAddr,
557        now: Instant,
558    ) -> Result<(), NatTraversalError> {
559        // Check if we're being asked to send too many packets to the same target
560        let amplification_key = (source_addr, target_addr);
561        // Simple amplification protection: limit packets per source-target pair
562        // In production, this would be more sophisticated with:
563        // - Bandwidth tracking
564        // - Packet size ratios
565        // - Geographic analysis
566        // - Temporal pattern analysis
567
568        // For now, implement basic per-pair rate limiting
569        if self.is_amplification_suspicious(amplification_key, now) {
570            warn!(
571                "Potential amplification attack detected: {} -> {}",
572                source_addr, target_addr
573            );
574            return Err(NatTraversalError::SuspiciousCoordination);
575        }
576
577        Ok(())
578    }
579
580    /// Check for suspicious amplification patterns
581    fn is_amplification_suspicious(
582        &self,
583        _amplification_key: (SocketAddr, SocketAddr),
584        _now: Instant,
585    ) -> bool {
586        // Simplified amplification detection
587        // In production, this would track:
588        // - Request/response ratios
589        // - Bandwidth amplification factors
590        // - Temporal clustering of requests
591        // - Geographic distribution analysis
592        // For now, return false (no amplification detected)
593        // This can be enhanced with persistent amplification tracking
594        false
595    }
596
597    /// Generate cryptographically secure random values for coordination rounds
598    ///
599    /// This ensures that coordination rounds use secure random values to prevent
600    /// prediction attacks and ensure proper synchronization security.
601    fn generate_secure_coordination_round(&self) -> VarInt {
602        // Use cryptographically secure random number generation
603        let secure_random: u64 = rand::random();
604        // Ensure the value is within reasonable bounds for VarInt
605        let bounded_random = secure_random % 1000000; // Limit to reasonable range
606
607        VarInt::from_u64(bounded_random).unwrap_or(VarInt::from_u32(1))
608    }
609
610    /// Enhanced address validation with security checks
611    ///
612    /// This performs comprehensive address validation including:
613    /// - Basic format validation
614    /// - Security threat detection
615    /// - Amplification attack prevention
616    /// - Suspicious pattern recognition
617    fn enhanced_address_validation(
618        &mut self,
619        addr: SocketAddr,
620        source_addr: SocketAddr,
621        now: Instant,
622    ) -> Result<AddressValidationResult, NatTraversalError> {
623        // First, perform basic address validation
624        let basic_result = self.validate_address(addr, now);
625        match basic_result {
626            AddressValidationResult::Invalid => {
627                return Err(NatTraversalError::InvalidAddress);
628            }
629            AddressValidationResult::Suspicious => {
630                return Err(NatTraversalError::SuspiciousCoordination);
631            }
632            AddressValidationResult::Valid => {
633                // Continue with enhanced validation
634            }
635        }
636
637        // Check for amplification attack patterns
638        self.validate_amplification_limits(source_addr, addr, now)?;
639
640        // Additional security checks
641        if self.is_address_in_suspicious_range(addr) {
642            warn!("Address in suspicious range detected: {}", addr);
643            return Err(NatTraversalError::SuspiciousCoordination);
644        }
645
646        if self.is_coordination_pattern_suspicious(source_addr, addr, now) {
647            warn!(
648                "Suspicious coordination pattern detected: {} -> {}",
649                source_addr, addr
650            );
651            return Err(NatTraversalError::SuspiciousCoordination);
652        }
653
654        Ok(AddressValidationResult::Valid)
655    }
656
657    /// Check if address is in a suspicious range
658    fn is_address_in_suspicious_range(&self, addr: SocketAddr) -> bool {
659        match addr.ip() {
660            IpAddr::V4(ipv4) => {
661                // Check for addresses commonly used in attacks
662                let octets = ipv4.octets();
663                // Reject certain reserved ranges that shouldn't be used for P2P
664                if octets[0] == 0 || octets[0] == 127 {
665                    return true;
666                }
667
668                // Check for test networks (RFC 5737)
669                if octets[0] == 192 && octets[1] == 0 && octets[2] == 2 {
670                    return true;
671                }
672                if octets[0] == 198 && octets[1] == 51 && octets[2] == 100 {
673                    return true;
674                }
675                if octets[0] == 203 && octets[1] == 0 && octets[2] == 113 {
676                    return true;
677                }
678
679                false
680            }
681            IpAddr::V6(ipv6) => {
682                // Check for suspicious IPv6 ranges
683                if ipv6.is_loopback() || ipv6.is_unspecified() {
684                    return true;
685                }
686
687                // Check for documentation ranges (RFC 3849)
688                let segments = ipv6.segments();
689                if segments[0] == 0x2001 && segments[1] == 0x0db8 {
690                    return true;
691                }
692
693                false
694            }
695        }
696    }
697
698    /// Check for suspicious coordination patterns
699    fn is_coordination_pattern_suspicious(
700        &self,
701        _source_addr: SocketAddr,
702        _target_addr: SocketAddr,
703        _now: Instant,
704    ) -> bool {
705        // Simplified pattern detection
706        // In production, this would analyze:
707        // - Temporal patterns (too frequent requests)
708        // - Geographic patterns (unusual source/target combinations)
709        // - Behavioral patterns (consistent with known attack signatures)
710        // - Network topology patterns (suspicious routing)
711        // For now, return false (no suspicious patterns detected)
712        // This can be enhanced with machine learning-based pattern detection
713        false
714    }
715
716    /// Check if candidate rate limit is exceeded
717    fn is_candidate_rate_limited(&mut self, now: Instant) -> bool {
718        // Clean up old entries
719        self.cleanup_rate_tracker(now);
720        // Check if we've exceeded the rate limit
721        if self.candidate_rate_tracker.len() >= self.max_candidates_per_window as usize {
722            return true;
723        }
724
725        // Record this attempt
726        self.candidate_rate_tracker.push_back(now);
727        false
728    }
729
730    /// Check if coordination rate limit is exceeded
731    fn is_coordination_rate_limited(&mut self, now: Instant) -> bool {
732        // Clean up old entries
733        self.cleanup_coordination_tracker(now);
734        // Check if we've exceeded the rate limit
735        if self.coordination_requests.len() >= self.max_coordination_per_window as usize {
736            return true;
737        }
738
739        // Record this attempt
740        let request = CoordinationRequest { timestamp: now };
741        self.coordination_requests.push_back(request);
742        false
743    }
744
745    /// Clean up old rate tracking entries
746    fn cleanup_rate_tracker(&mut self, now: Instant) {
747        let cutoff = now - self.rate_window;
748        while let Some(&front_time) = self.candidate_rate_tracker.front() {
749            if front_time < cutoff {
750                self.candidate_rate_tracker.pop_front();
751            } else {
752                break;
753            }
754        }
755    }
756    /// Clean up old coordination tracking entries
757    fn cleanup_coordination_tracker(&mut self, now: Instant) {
758        let cutoff = now - self.rate_window;
759        while let Some(front_request) = self.coordination_requests.front() {
760            if front_request.timestamp < cutoff {
761                self.coordination_requests.pop_front();
762            } else {
763                break;
764            }
765        }
766    }
767    /// Validate an address for security concerns
768    fn validate_address(&mut self, addr: SocketAddr, now: Instant) -> AddressValidationResult {
769        // Check cache first
770        if let Some(&cached_result) = self.address_validation_cache.get(&addr) {
771            return cached_result;
772        }
773        let result = self.perform_address_validation(addr);
774
775        // Cache the result
776        self.address_validation_cache.insert(addr, result);
777
778        // Clean up old cache entries periodically
779        if self.address_validation_cache.len() > 1000 {
780            self.cleanup_address_cache(now);
781        }
782
783        result
784    }
785
786    /// Perform actual address validation
787    fn perform_address_validation(&self, addr: SocketAddr) -> AddressValidationResult {
788        match addr.ip() {
789            IpAddr::V4(ipv4) => {
790                // Check for invalid IPv4 addresses
791                if ipv4.is_unspecified() || ipv4.is_broadcast() {
792                    return AddressValidationResult::Invalid;
793                }
794                // Check for suspicious addresses
795                if ipv4.is_multicast() || ipv4.is_documentation() {
796                    return AddressValidationResult::Suspicious;
797                }
798
799                // Check for reserved ranges that shouldn't be used for P2P
800                if ipv4.octets()[0] == 0 || ipv4.octets()[0] == 127 {
801                    return AddressValidationResult::Invalid;
802                }
803
804                // Check for common attack patterns
805                if self.is_suspicious_ipv4(ipv4) {
806                    return AddressValidationResult::Suspicious;
807                }
808            }
809            IpAddr::V6(ipv6) => {
810                // Check for invalid IPv6 addresses
811                if ipv6.is_unspecified() || ipv6.is_multicast() {
812                    return AddressValidationResult::Invalid;
813                }
814
815                // Check for suspicious IPv6 addresses
816                if self.is_suspicious_ipv6(ipv6) {
817                    return AddressValidationResult::Suspicious;
818                }
819            }
820        }
821
822        // Check port range
823        if addr.port() == 0 || addr.port() < 1024 {
824            return AddressValidationResult::Suspicious;
825        }
826
827        AddressValidationResult::Valid
828    }
829
830    /// Check for suspicious IPv4 patterns
831    fn is_suspicious_ipv4(&self, ipv4: Ipv4Addr) -> bool {
832        let octets = ipv4.octets();
833        // Check for patterns that might indicate scanning or attacks
834        // Sequential or patterned addresses
835        if octets[0] == octets[1] && octets[1] == octets[2] && octets[2] == octets[3] {
836            return true;
837        }
838
839        // Check for addresses in ranges commonly used for attacks
840        // This is a simplified check - production would have more sophisticated patterns
841        false
842    }
843
844    /// Check for suspicious IPv6 patterns
845    fn is_suspicious_ipv6(&self, ipv6: Ipv6Addr) -> bool {
846        let segments = ipv6.segments();
847        // Check for obvious patterns
848        if segments.iter().all(|&s| s == segments[0]) {
849            return true;
850        }
851
852        false
853    }
854
855    /// Clean up old address validation cache entries
856    fn cleanup_address_cache(&mut self, _now: Instant) {
857        // Simple cleanup - remove random entries to keep size bounded
858        // In production, this would use LRU or timestamp-based cleanup
859        if self.address_validation_cache.len() > 500 {
860            let keys_to_remove: Vec<_> = self
861                .address_validation_cache
862                .keys()
863                .take(self.address_validation_cache.len() / 2)
864                .copied()
865                .collect();
866            for key in keys_to_remove {
867                self.address_validation_cache.remove(&key);
868            }
869        }
870    }
871
872    /// Comprehensive path validation for PUNCH_ME_NOW frames
873    ///
874    /// This performs security-critical validation to prevent various attacks:
875    /// - Address spoofing prevention
876    /// - Reflection attack mitigation
877    /// - Coordination request validation
878    /// - Rate limiting enforcement
879    fn validate_punch_me_now_frame(
880        &mut self,
881        frame: &crate::frame::PunchMeNow,
882        source_addr: SocketAddr,
883        peer_id: [u8; 32],
884        now: Instant,
885    ) -> Result<(), NatTraversalError> {
886        // 1. Rate limiting validation
887        if self.is_coordination_rate_limited(now) {
888            debug!(
889                "PUNCH_ME_NOW frame rejected: coordination rate limit exceeded for peer {:?}",
890                hex::encode(&peer_id[..8])
891            );
892            return Err(NatTraversalError::RateLimitExceeded);
893        }
894        // 2. Address validation - validate the address claimed in the frame
895        let addr_validation = self.validate_address(frame.address, now);
896        match addr_validation {
897            AddressValidationResult::Invalid => {
898                debug!(
899                    "PUNCH_ME_NOW frame rejected: invalid address {:?} from peer {:?}",
900                    frame.address,
901                    hex::encode(&peer_id[..8])
902                );
903                return Err(NatTraversalError::InvalidAddress);
904            }
905            AddressValidationResult::Suspicious => {
906                debug!(
907                    "PUNCH_ME_NOW frame rejected: suspicious address {:?} from peer {:?}",
908                    frame.address,
909                    hex::encode(&peer_id[..8])
910                );
911                return Err(NatTraversalError::SuspiciousCoordination);
912            }
913            AddressValidationResult::Valid => {
914                // Continue validation
915            }
916        }
917
918        // 3. Source address consistency validation
919        // The frame's address should reasonably relate to the actual source
920        if !self.validate_address_consistency(frame.address, source_addr) {
921            debug!(
922                "PUNCH_ME_NOW frame rejected: address consistency check failed. Frame claims {:?}, but received from {:?}",
923                frame.address, source_addr
924            );
925            return Err(NatTraversalError::SuspiciousCoordination);
926        }
927
928        // 4. Coordination parameters validation
929        if !self.validate_coordination_parameters(frame) {
930            debug!(
931                "PUNCH_ME_NOW frame rejected: invalid coordination parameters from peer {:?}",
932                hex::encode(&peer_id[..8])
933            );
934            return Err(NatTraversalError::SuspiciousCoordination);
935        }
936
937        // 5. Target peer validation (if present)
938        if let Some(target_peer_id) = frame.target_peer_id {
939            if !self.validate_target_peer_request(peer_id, target_peer_id, frame) {
940                debug!(
941                    "PUNCH_ME_NOW frame rejected: invalid target peer request from {:?} to {:?}",
942                    hex::encode(&peer_id[..8]),
943                    hex::encode(&target_peer_id[..8])
944                );
945                return Err(NatTraversalError::SuspiciousCoordination);
946            }
947        }
948
949        // 6. Resource limits validation
950        if !self.validate_resource_limits(frame) {
951            debug!(
952                "PUNCH_ME_NOW frame rejected: resource limits exceeded from peer {:?}",
953                hex::encode(&peer_id[..8])
954            );
955            return Err(NatTraversalError::ResourceLimitExceeded);
956        }
957
958        debug!(
959            "PUNCH_ME_NOW frame validation passed for peer {:?}",
960            hex::encode(&peer_id[..8])
961        );
962        Ok(())
963    }
964
965    /// Validate address consistency between claimed and observed addresses
966    ///
967    /// This prevents address spoofing by ensuring the claimed local address
968    /// is reasonably consistent with the observed source address.
969    fn validate_address_consistency(
970        &self,
971        claimed_addr: SocketAddr,
972        observed_addr: SocketAddr,
973    ) -> bool {
974        // For P2P NAT traversal, the port will typically be different due to NAT,
975        // but the IP should be consistent unless there's multi-homing or proxying
976        // Check if IPs are in the same family
977        match (claimed_addr.ip(), observed_addr.ip()) {
978            (IpAddr::V4(claimed_ip), IpAddr::V4(observed_ip)) => {
979                // For IPv4, allow same IP or addresses in same private range
980                if claimed_ip == observed_ip {
981                    return true;
982                }
983
984                // Allow within same private network (simplified check)
985                if self.are_in_same_private_network_v4(claimed_ip, observed_ip) {
986                    return true;
987                }
988
989                // Allow certain NAT scenarios where external IP differs
990                // This is a simplified check - production would be more sophisticated
991                !claimed_ip.is_private() && !observed_ip.is_private()
992            }
993            (IpAddr::V6(claimed_ip), IpAddr::V6(observed_ip)) => {
994                // For IPv6, be more lenient due to complex addressing
995                claimed_ip == observed_ip || self.are_in_same_prefix_v6(claimed_ip, observed_ip)
996            }
997            _ => {
998                // Mismatched IP families - suspicious
999                false
1000            }
1001        }
1002    }
1003
1004    /// Check if two IPv4 addresses are in the same private network
1005    fn are_in_same_private_network_v4(&self, ip1: Ipv4Addr, ip2: Ipv4Addr) -> bool {
1006        // Check common private ranges
1007        let ip1_octets = ip1.octets();
1008        let ip2_octets = ip2.octets();
1009        // 10.0.0.0/8
1010        if ip1_octets[0] == 10 && ip2_octets[0] == 10 {
1011            return true;
1012        }
1013
1014        // 172.16.0.0/12
1015        if ip1_octets[0] == 172
1016            && ip2_octets[0] == 172
1017            && (16..=31).contains(&ip1_octets[1])
1018            && (16..=31).contains(&ip2_octets[1])
1019        {
1020            return true;
1021        }
1022
1023        // 192.168.0.0/16
1024        if ip1_octets[0] == 192
1025            && ip1_octets[1] == 168
1026            && ip2_octets[0] == 192
1027            && ip2_octets[1] == 168
1028        {
1029            return true;
1030        }
1031
1032        false
1033    }
1034
1035    /// Check if two IPv6 addresses are in the same prefix
1036    fn are_in_same_prefix_v6(&self, ip1: Ipv6Addr, ip2: Ipv6Addr) -> bool {
1037        // Simplified IPv6 prefix check - compare first 64 bits
1038        let segments1 = ip1.segments();
1039        let segments2 = ip2.segments();
1040        segments1[0] == segments2[0]
1041            && segments1[1] == segments2[1]
1042            && segments1[2] == segments2[2]
1043            && segments1[3] == segments2[3]
1044    }
1045
1046    /// Validate coordination parameters for security
1047    fn validate_coordination_parameters(&self, frame: &crate::frame::PunchMeNow) -> bool {
1048        // Check round number is reasonable (not too large to prevent overflow attacks)
1049        if frame.round.into_inner() > 1000000 {
1050            return false;
1051        }
1052        // Check target sequence is reasonable
1053        if frame.paired_with_sequence_number.into_inner() > 10000 {
1054            return false;
1055        }
1056
1057        // Validate address is not obviously invalid
1058        match frame.address.ip() {
1059            IpAddr::V4(ipv4) => {
1060                // Reject obviously invalid addresses
1061                !ipv4.is_unspecified() && !ipv4.is_broadcast() && !ipv4.is_multicast()
1062            }
1063            IpAddr::V6(ipv6) => {
1064                // Reject obviously invalid addresses
1065                !ipv6.is_unspecified() && !ipv6.is_multicast()
1066            }
1067        }
1068    }
1069
1070    /// Validate target peer request for potential abuse
1071    fn validate_target_peer_request(
1072        &self,
1073        requesting_peer: [u8; 32],
1074        target_peer: [u8; 32],
1075        _frame: &crate::frame::PunchMeNow,
1076    ) -> bool {
1077        // Prevent self-coordination (peer requesting coordination with itself)
1078        if requesting_peer == target_peer {
1079            return false;
1080        }
1081        // Additional validation could include:
1082        // - Check if target peer is known/registered
1083        // - Validate target peer hasn't opted out of coordination
1084        // - Check for suspicious patterns in target peer selection
1085
1086        true
1087    }
1088
1089    /// Validate resource limits for the coordination request
1090    fn validate_resource_limits(&self, _frame: &crate::frame::PunchMeNow) -> bool {
1091        // Check current load and resource usage
1092        // This is a simplified check - production would monitor:
1093        // - Active coordination sessions
1094        // - Memory usage
1095        // - Network bandwidth
1096        // - CPU utilization
1097        // For now, just check if we have too many active coordination requests
1098        self.coordination_requests.len() < self.max_coordination_per_window as usize
1099    }
1100}
1101
1102impl AdaptiveTimeoutState {
1103    /// Create new adaptive timeout state with default values
1104    pub(crate) fn new() -> Self {
1105        let base_timeout = Duration::from_millis(1000); // 1 second base
1106        Self {
1107            current_timeout: base_timeout,
1108            min_timeout: Duration::from_millis(100),
1109            max_timeout: Duration::from_secs(30),
1110            base_timeout,
1111            backoff_multiplier: 1.0,
1112            max_backoff_multiplier: 8.0,
1113            jitter_factor: 0.1, // 10% jitter
1114            srtt: None,
1115            rttvar: None,
1116            last_rtt: None,
1117            consecutive_timeouts: 0,
1118            successful_responses: 0,
1119        }
1120    }
1121    /// Update timeout based on successful response
1122    fn update_success(&mut self, rtt: Duration) {
1123        self.last_rtt = Some(rtt);
1124        self.successful_responses += 1;
1125        self.consecutive_timeouts = 0;
1126        // Update smoothed RTT using TCP algorithm
1127        match self.srtt {
1128            None => {
1129                self.srtt = Some(rtt);
1130                self.rttvar = Some(rtt / 2);
1131            }
1132            Some(srtt) => {
1133                let rttvar = self.rttvar.unwrap_or(rtt / 2);
1134                let abs_diff = rtt.abs_diff(srtt);
1135
1136                self.rttvar = Some(rttvar * 3 / 4 + abs_diff / 4);
1137                self.srtt = Some(srtt * 7 / 8 + rtt / 8);
1138            }
1139        }
1140
1141        // Reduce backoff multiplier on success
1142        self.backoff_multiplier = (self.backoff_multiplier * 0.8).max(1.0);
1143
1144        // Update current timeout
1145        self.calculate_current_timeout();
1146    }
1147
1148    /// Update timeout based on timeout event
1149    fn update_timeout(&mut self) {
1150        self.consecutive_timeouts += 1;
1151        // Exponential backoff with bounds
1152        self.backoff_multiplier = (self.backoff_multiplier * 2.0).min(self.max_backoff_multiplier);
1153
1154        // Update current timeout
1155        self.calculate_current_timeout();
1156    }
1157
1158    /// Calculate current timeout based on conditions
1159    fn calculate_current_timeout(&mut self) {
1160        let base_timeout = if let (Some(srtt), Some(rttvar)) = (self.srtt, self.rttvar) {
1161            // Use TCP-style RTO calculation: RTO = SRTT + 4 * RTTVAR
1162            srtt + rttvar * 4
1163        } else {
1164            self.base_timeout
1165        };
1166        // Apply backoff multiplier
1167        let timeout = base_timeout.mul_f64(self.backoff_multiplier);
1168
1169        // Apply jitter to prevent thundering herd
1170        let jitter = 1.0 + (rand::random::<f64>() - 0.5) * 2.0 * self.jitter_factor;
1171        let timeout = timeout.mul_f64(jitter);
1172
1173        // Bound the timeout
1174        self.current_timeout = timeout.clamp(self.min_timeout, self.max_timeout);
1175    }
1176
1177    /// Get current timeout value
1178    fn get_timeout(&self) -> Duration {
1179        self.current_timeout
1180    }
1181    /// Check if retry should be attempted
1182    fn should_retry(&self, max_retries: u32) -> bool {
1183        self.consecutive_timeouts < max_retries
1184    }
1185    /// Get retry delay with exponential backoff
1186    fn get_retry_delay(&self) -> Duration {
1187        let delay = self.current_timeout.mul_f64(self.backoff_multiplier);
1188        delay.clamp(self.min_timeout, self.max_timeout)
1189    }
1190}
1191/// Resource management limits and cleanup configuration
1192#[derive(Debug)]
1193#[allow(dead_code)]
1194pub(super) struct ResourceManagementConfig {
1195    /// Maximum number of active validations
1196    max_active_validations: usize,
1197    /// Maximum number of local candidates
1198    max_local_candidates: usize,
1199    /// Maximum number of remote candidates
1200    max_remote_candidates: usize,
1201    /// Maximum number of candidate pairs
1202    max_candidate_pairs: usize,
1203    /// Maximum coordination rounds to keep in history
1204    max_coordination_history: usize,
1205    /// Cleanup interval for expired resources
1206    cleanup_interval: Duration,
1207    /// Timeout for stale candidates
1208    candidate_timeout: Duration,
1209    /// Timeout for path validations
1210    validation_timeout: Duration,
1211    /// Timeout for coordination rounds
1212    coordination_timeout: Duration,
1213    /// Memory pressure threshold (0.0 = no pressure, 1.0 = maximum pressure)
1214    memory_pressure_threshold: f64,
1215    /// Aggressive cleanup mode threshold
1216    aggressive_cleanup_threshold: f64,
1217}
1218/// Resource usage statistics and monitoring
1219#[derive(Debug, Default)]
1220#[allow(dead_code)]
1221pub(super) struct ResourceStats {
1222    /// Current number of active validations
1223    active_validations: usize,
1224    /// Current number of local candidates
1225    local_candidates: usize,
1226    /// Current number of remote candidates
1227    remote_candidates: usize,
1228    /// Current number of candidate pairs
1229    candidate_pairs: usize,
1230    /// Peak memory usage
1231    peak_memory_usage: usize,
1232    /// Number of cleanup operations performed
1233    cleanup_operations: u64,
1234    /// Number of resources cleaned up
1235    resources_cleaned: u64,
1236    /// Number of resource allocation failures
1237    allocation_failures: u64,
1238    /// Last cleanup time
1239    last_cleanup: Option<Instant>,
1240    /// Memory pressure level (0.0 = no pressure, 1.0 = maximum pressure)
1241    memory_pressure: f64,
1242}
1243/// Resource cleanup coordinator
1244#[derive(Debug)]
1245pub(super) struct ResourceCleanupCoordinator {
1246    /// Configuration for resource limits
1247    config: ResourceManagementConfig,
1248    /// Resource usage statistics
1249    stats: ResourceStats,
1250    /// Last cleanup time
1251    last_cleanup: Option<Instant>,
1252    /// Cleanup operation counter
1253    cleanup_counter: u64,
1254    /// Shutdown flag
1255    shutdown_requested: bool,
1256}
1257impl ResourceManagementConfig {
1258    /// Create new resource management configuration with production-ready defaults
1259    fn new() -> Self {
1260        Self {
1261            max_active_validations: 100,
1262            max_local_candidates: 50,
1263            max_remote_candidates: 100,
1264            max_candidate_pairs: 200,
1265            max_coordination_history: 10,
1266            cleanup_interval: Duration::from_secs(30),
1267            candidate_timeout: Duration::from_secs(300), // 5 minutes
1268            validation_timeout: Duration::from_secs(30),
1269            coordination_timeout: Duration::from_secs(60),
1270            memory_pressure_threshold: 0.75,
1271            aggressive_cleanup_threshold: 0.90,
1272        }
1273    }
1274    /// Create configuration optimized for low-memory environments
1275    #[cfg(feature = "low_memory")]
1276    fn low_memory() -> Self {
1277        Self {
1278            max_active_validations: 25,
1279            max_local_candidates: 10,
1280            max_remote_candidates: 25,
1281            max_candidate_pairs: 50,
1282            max_coordination_history: 3,
1283            cleanup_interval: Duration::from_secs(15),
1284            candidate_timeout: Duration::from_secs(180), // 3 minutes
1285            validation_timeout: Duration::from_secs(20),
1286            coordination_timeout: Duration::from_secs(30),
1287            memory_pressure_threshold: 0.60,
1288            aggressive_cleanup_threshold: 0.80,
1289        }
1290    }
1291}
1292#[allow(dead_code)]
1293impl ResourceCleanupCoordinator {
1294    /// Create new resource cleanup coordinator
1295    fn new() -> Self {
1296        Self {
1297            config: ResourceManagementConfig::new(),
1298            stats: ResourceStats::default(),
1299            last_cleanup: None,
1300            cleanup_counter: 0,
1301            shutdown_requested: false,
1302        }
1303    }
1304    /// Create coordinator optimized for low-memory environments
1305    #[cfg(feature = "low_memory")]
1306    fn low_memory() -> Self {
1307        Self {
1308            config: ResourceManagementConfig::low_memory(),
1309            stats: ResourceStats::default(),
1310            last_cleanup: None,
1311            cleanup_counter: 0,
1312            shutdown_requested: false,
1313        }
1314    }
1315    /// Check if resource limits are exceeded
1316    fn check_resource_limits(&self, state: &NatTraversalState) -> bool {
1317        state.active_validations.len() > self.config.max_active_validations
1318            || state.local_candidates.len() > self.config.max_local_candidates
1319            || state.remote_candidates.len() > self.config.max_remote_candidates
1320            || state.candidate_pairs.len() > self.config.max_candidate_pairs
1321    }
1322    /// Calculate current memory pressure level
1323    fn calculate_memory_pressure(
1324        &mut self,
1325        active_validations_len: usize,
1326        local_candidates_len: usize,
1327        remote_candidates_len: usize,
1328        candidate_pairs_len: usize,
1329    ) -> f64 {
1330        let total_limit = self.config.max_active_validations
1331            + self.config.max_local_candidates
1332            + self.config.max_remote_candidates
1333            + self.config.max_candidate_pairs;
1334        let current_usage = active_validations_len
1335            + local_candidates_len
1336            + remote_candidates_len
1337            + candidate_pairs_len;
1338
1339        let pressure = current_usage as f64 / total_limit as f64;
1340        self.stats.memory_pressure = pressure;
1341        pressure
1342    }
1343
1344    /// Determine if cleanup is needed
1345    fn should_cleanup(&self, now: Instant) -> bool {
1346        if self.shutdown_requested {
1347            return true;
1348        }
1349        // Check if it's time for regular cleanup
1350        if let Some(last_cleanup) = self.last_cleanup {
1351            if now.duration_since(last_cleanup) >= self.config.cleanup_interval {
1352                return true;
1353            }
1354        } else {
1355            return true; // First cleanup
1356        }
1357
1358        // Check memory pressure
1359        if self.stats.memory_pressure > self.config.memory_pressure_threshold {
1360            return true;
1361        }
1362
1363        false
1364    }
1365
1366    /// Perform cleanup of expired resources
1367    fn cleanup_expired_resources(
1368        &mut self,
1369        active_validations: &mut HashMap<SocketAddr, PathValidationState>,
1370        local_candidates: &mut HashMap<VarInt, AddressCandidate>,
1371        remote_candidates: &mut HashMap<VarInt, AddressCandidate>,
1372        candidate_pairs: &mut Vec<CandidatePair>,
1373        coordination: &mut Option<CoordinationState>,
1374        now: Instant,
1375    ) -> u64 {
1376        let mut cleaned = 0;
1377        // Clean up expired path validations
1378        cleaned += self.cleanup_expired_validations(active_validations, now);
1379
1380        // Clean up stale candidates
1381        cleaned += self.cleanup_stale_candidates(local_candidates, remote_candidates, now);
1382
1383        // Clean up failed candidate pairs
1384        cleaned += self.cleanup_failed_pairs(candidate_pairs, now);
1385
1386        // Clean up old coordination state
1387        cleaned += self.cleanup_old_coordination(coordination, now);
1388
1389        // Update statistics
1390        self.stats.cleanup_operations += 1;
1391        self.stats.resources_cleaned += cleaned;
1392        self.last_cleanup = Some(now);
1393        self.cleanup_counter += 1;
1394
1395        debug!("Cleaned up {} expired resources", cleaned);
1396        cleaned
1397    }
1398
1399    /// Clean up expired path validations
1400    fn cleanup_expired_validations(
1401        &mut self,
1402        active_validations: &mut HashMap<SocketAddr, PathValidationState>,
1403        now: Instant,
1404    ) -> u64 {
1405        let mut cleaned = 0;
1406        let validation_timeout = self.config.validation_timeout;
1407        active_validations.retain(|_addr, validation| {
1408            let is_expired = now.duration_since(validation.sent_at) > validation_timeout;
1409            if is_expired {
1410                cleaned += 1;
1411                trace!("Cleaned up expired validation for {:?}", _addr);
1412            }
1413            !is_expired
1414        });
1415
1416        cleaned
1417    }
1418
1419    /// Clean up stale candidates
1420    fn cleanup_stale_candidates(
1421        &mut self,
1422        local_candidates: &mut HashMap<VarInt, AddressCandidate>,
1423        remote_candidates: &mut HashMap<VarInt, AddressCandidate>,
1424        now: Instant,
1425    ) -> u64 {
1426        let mut cleaned = 0;
1427        let candidate_timeout = self.config.candidate_timeout;
1428        // Clean up local candidates
1429        local_candidates.retain(|_seq, candidate| {
1430            let is_stale = now.duration_since(candidate.discovered_at) > candidate_timeout
1431                || candidate.state == CandidateState::Failed
1432                || candidate.state == CandidateState::Removed;
1433            if is_stale {
1434                cleaned += 1;
1435                trace!("Cleaned up stale local candidate {:?}", candidate.address);
1436            }
1437            !is_stale
1438        });
1439
1440        // Clean up remote candidates
1441        remote_candidates.retain(|_seq, candidate| {
1442            let is_stale = now.duration_since(candidate.discovered_at) > candidate_timeout
1443                || candidate.state == CandidateState::Failed
1444                || candidate.state == CandidateState::Removed;
1445            if is_stale {
1446                cleaned += 1;
1447                trace!("Cleaned up stale remote candidate {:?}", candidate.address);
1448            }
1449            !is_stale
1450        });
1451
1452        cleaned
1453    }
1454
1455    /// Clean up failed candidate pairs
1456    fn cleanup_failed_pairs(
1457        &mut self,
1458        candidate_pairs: &mut Vec<CandidatePair>,
1459        now: Instant,
1460    ) -> u64 {
1461        let mut cleaned = 0;
1462        let pair_timeout = self.config.candidate_timeout;
1463        candidate_pairs.retain(|pair| {
1464            let is_stale = now.duration_since(pair.created_at) > pair_timeout
1465                || pair.state == PairState::Failed;
1466            if is_stale {
1467                cleaned += 1;
1468                trace!(
1469                    "Cleaned up failed candidate pair {:?} -> {:?}",
1470                    pair.local_addr, pair.remote_addr
1471                );
1472            }
1473            !is_stale
1474        });
1475
1476        cleaned
1477    }
1478
1479    /// Clean up old coordination state
1480    fn cleanup_old_coordination(
1481        &mut self,
1482        coordination: &mut Option<CoordinationState>,
1483        now: Instant,
1484    ) -> u64 {
1485        let mut cleaned = 0;
1486        if let Some(coord) = coordination {
1487            let is_expired =
1488                now.duration_since(coord.round_start) > self.config.coordination_timeout;
1489            let is_failed = coord.state == CoordinationPhase::Failed;
1490
1491            if is_expired || is_failed {
1492                let round = coord.round;
1493                *coordination = None;
1494                cleaned += 1;
1495                trace!("Cleaned up old coordination state for round {}", round);
1496            }
1497        }
1498
1499        cleaned
1500    }
1501
1502    /// Perform aggressive cleanup when under memory pressure
1503    fn aggressive_cleanup(
1504        &mut self,
1505        active_validations: &mut HashMap<SocketAddr, PathValidationState>,
1506        local_candidates: &mut HashMap<VarInt, AddressCandidate>,
1507        remote_candidates: &mut HashMap<VarInt, AddressCandidate>,
1508        candidate_pairs: &mut Vec<CandidatePair>,
1509        now: Instant,
1510    ) -> u64 {
1511        let mut cleaned = 0;
1512        // More aggressive timeout for candidates
1513        let aggressive_timeout = self.config.candidate_timeout / 2;
1514
1515        // Clean up older candidates first
1516        local_candidates.retain(|_seq, candidate| {
1517            let keep = now.duration_since(candidate.discovered_at) <= aggressive_timeout
1518                && candidate.state != CandidateState::Failed;
1519            if !keep {
1520                cleaned += 1;
1521            }
1522            keep
1523        });
1524
1525        remote_candidates.retain(|_seq, candidate| {
1526            let keep = now.duration_since(candidate.discovered_at) <= aggressive_timeout
1527                && candidate.state != CandidateState::Failed;
1528            if !keep {
1529                cleaned += 1;
1530            }
1531            keep
1532        });
1533
1534        // Clean up waiting candidate pairs
1535        candidate_pairs.retain(|pair| {
1536            let keep = pair.state != PairState::Waiting
1537                || now.duration_since(pair.created_at) <= aggressive_timeout;
1538            if !keep {
1539                cleaned += 1;
1540            }
1541            keep
1542        });
1543
1544        // Clean up old validations more aggressively
1545        active_validations.retain(|_addr, validation| {
1546            let keep = now.duration_since(validation.sent_at) <= self.config.validation_timeout / 2;
1547            if !keep {
1548                cleaned += 1;
1549            }
1550            keep
1551        });
1552
1553        warn!(
1554            "Aggressive cleanup removed {} resources due to memory pressure",
1555            cleaned
1556        );
1557        cleaned
1558    }
1559
1560    /// Request graceful shutdown and cleanup
1561    fn request_shutdown(&mut self) {
1562        self.shutdown_requested = true;
1563        debug!("Resource cleanup coordinator shutdown requested");
1564    }
1565    /// Perform final cleanup during shutdown
1566    fn shutdown_cleanup(
1567        &mut self,
1568        active_validations: &mut HashMap<SocketAddr, PathValidationState>,
1569        local_candidates: &mut HashMap<VarInt, AddressCandidate>,
1570        remote_candidates: &mut HashMap<VarInt, AddressCandidate>,
1571        candidate_pairs: &mut Vec<CandidatePair>,
1572        coordination: &mut Option<CoordinationState>,
1573    ) -> u64 {
1574        let mut cleaned = 0;
1575        // Clear all resources
1576        cleaned += active_validations.len() as u64;
1577        active_validations.clear();
1578
1579        cleaned += local_candidates.len() as u64;
1580        local_candidates.clear();
1581
1582        cleaned += remote_candidates.len() as u64;
1583        remote_candidates.clear();
1584
1585        cleaned += candidate_pairs.len() as u64;
1586        candidate_pairs.clear();
1587
1588        if coordination.is_some() {
1589            *coordination = None;
1590            cleaned += 1;
1591        }
1592
1593        info!("Shutdown cleanup removed {} resources", cleaned);
1594        cleaned
1595    }
1596
1597    /// Get current resource usage statistics
1598    fn get_resource_stats(&self) -> &ResourceStats {
1599        &self.stats
1600    }
1601    /// Update resource usage statistics
1602    fn update_stats(
1603        &mut self,
1604        active_validations_len: usize,
1605        local_candidates_len: usize,
1606        remote_candidates_len: usize,
1607        candidate_pairs_len: usize,
1608    ) {
1609        self.stats.active_validations = active_validations_len;
1610        self.stats.local_candidates = local_candidates_len;
1611        self.stats.remote_candidates = remote_candidates_len;
1612        self.stats.candidate_pairs = candidate_pairs_len;
1613        // Update peak memory usage
1614        let current_usage = self.stats.active_validations
1615            + self.stats.local_candidates
1616            + self.stats.remote_candidates
1617            + self.stats.candidate_pairs;
1618
1619        if current_usage > self.stats.peak_memory_usage {
1620            self.stats.peak_memory_usage = current_usage;
1621        }
1622    }
1623
1624    /// Perform resource cleanup based on current state
1625    pub(super) fn perform_cleanup(&mut self, now: Instant) {
1626        self.last_cleanup = Some(now);
1627        self.cleanup_counter += 1;
1628        // Update cleanup statistics
1629        self.stats.cleanup_operations += 1;
1630
1631        debug!("Performed resource cleanup #{}", self.cleanup_counter);
1632    }
1633}
1634
1635#[allow(dead_code)]
1636impl NetworkConditionMonitor {
1637    /// Create new network condition monitor
1638    fn new() -> Self {
1639        Self {
1640            rtt_samples: VecDeque::new(),
1641            max_samples: 20,
1642            packet_loss_rate: 0.0,
1643            congestion_window: 10,
1644            quality_score: 0.8, // Start with good quality assumption
1645            last_quality_update: Instant::now(),
1646            quality_update_interval: Duration::from_secs(10),
1647            timeout_stats: TimeoutStatistics::default(),
1648        }
1649    }
1650    /// Record a successful response time
1651    fn record_success(&mut self, rtt: Duration, now: Instant) {
1652        // Add RTT sample
1653        self.rtt_samples.push_back(rtt);
1654        if self.rtt_samples.len() > self.max_samples {
1655            self.rtt_samples.pop_front();
1656        }
1657        // Update timeout statistics
1658        self.timeout_stats.total_responses += 1;
1659        self.update_timeout_stats(now);
1660
1661        // Update quality score
1662        self.update_quality_score(now);
1663    }
1664
1665    /// Record a timeout event
1666    fn record_timeout(&mut self, now: Instant) {
1667        self.timeout_stats.total_timeouts += 1;
1668        self.update_timeout_stats(now);
1669        // Update quality score
1670        self.update_quality_score(now);
1671    }
1672
1673    /// Update timeout statistics
1674    fn update_timeout_stats(&mut self, now: Instant) {
1675        let total_attempts = self.timeout_stats.total_responses + self.timeout_stats.total_timeouts;
1676        if total_attempts > 0 {
1677            self.timeout_stats.timeout_rate =
1678                self.timeout_stats.total_timeouts as f64 / total_attempts as f64;
1679        }
1680
1681        // Calculate average response time
1682        if !self.rtt_samples.is_empty() {
1683            let total_rtt: Duration = self.rtt_samples.iter().sum();
1684            self.timeout_stats.avg_response_time = total_rtt / self.rtt_samples.len() as u32;
1685        }
1686
1687        self.timeout_stats.last_update = Some(now);
1688    }
1689
1690    /// Update network quality score
1691    fn update_quality_score(&mut self, now: Instant) {
1692        if now.duration_since(self.last_quality_update) < self.quality_update_interval {
1693            return;
1694        }
1695        // Quality factors
1696        let timeout_factor = 1.0 - self.timeout_stats.timeout_rate;
1697        let rtt_factor = self.calculate_rtt_factor();
1698        let consistency_factor = self.calculate_consistency_factor();
1699
1700        // Weighted quality score
1701        let new_quality = (timeout_factor * 0.4) + (rtt_factor * 0.3) + (consistency_factor * 0.3);
1702
1703        // Smooth the quality score
1704        self.quality_score = self.quality_score * 0.7 + new_quality * 0.3;
1705        self.last_quality_update = now;
1706    }
1707
1708    /// Calculate RTT factor for quality score
1709    fn calculate_rtt_factor(&self) -> f64 {
1710        if self.rtt_samples.is_empty() {
1711            return 0.5; // Neutral score
1712        }
1713        let avg_rtt = self.timeout_stats.avg_response_time;
1714
1715        // Good RTT: < 50ms = 1.0, Poor RTT: > 1000ms = 0.0
1716        let rtt_ms = avg_rtt.as_millis() as f64;
1717        let factor = 1.0 - (rtt_ms - 50.0) / 950.0;
1718        factor.clamp(0.0, 1.0)
1719    }
1720
1721    /// Calculate consistency factor for quality score
1722    fn calculate_consistency_factor(&self) -> f64 {
1723        if self.rtt_samples.len() < 3 {
1724            return 0.5; // Neutral score
1725        }
1726        // Calculate RTT variance
1727        let mean_rtt = self.timeout_stats.avg_response_time;
1728        let variance: f64 = self
1729            .rtt_samples
1730            .iter()
1731            .map(|rtt| {
1732                let diff = (*rtt).abs_diff(mean_rtt);
1733                diff.as_millis() as f64
1734            })
1735            .map(|diff| diff * diff)
1736            .sum::<f64>()
1737            / self.rtt_samples.len() as f64;
1738
1739        let std_dev = variance.sqrt();
1740
1741        // Low variance = high consistency
1742        let consistency = 1.0 - (std_dev / 1000.0).min(1.0);
1743        consistency.clamp(0.0, 1.0)
1744    }
1745
1746    /// Get current network quality score
1747    fn get_quality_score(&self) -> f64 {
1748        self.quality_score
1749    }
1750    /// Get estimated RTT based on recent samples
1751    fn get_estimated_rtt(&self) -> Option<Duration> {
1752        if self.rtt_samples.is_empty() {
1753            return None;
1754        }
1755        Some(self.timeout_stats.avg_response_time)
1756    }
1757
1758    /// Check if network conditions are suitable for coordination
1759    fn is_suitable_for_coordination(&self) -> bool {
1760        // Require reasonable quality for coordination attempts
1761        self.quality_score >= 0.3 && self.timeout_stats.timeout_rate < 0.5
1762    }
1763    /// Get estimated packet loss rate
1764    fn get_packet_loss_rate(&self) -> f64 {
1765        self.packet_loss_rate
1766    }
1767
1768    /// Get recommended timeout multiplier based on conditions
1769    fn get_timeout_multiplier(&self) -> f64 {
1770        let base_multiplier = 1.0;
1771
1772        // Adjust based on quality score
1773        let quality_multiplier = if self.quality_score < 0.3 {
1774            2.0 // Poor quality, increase timeouts
1775        } else if self.quality_score > 0.8 {
1776            0.8 // Good quality, reduce timeouts
1777        } else {
1778            1.0 // Neutral
1779        };
1780
1781        // Adjust based on packet loss
1782        let loss_multiplier = 1.0 + (self.packet_loss_rate * 2.0);
1783
1784        base_multiplier * quality_multiplier * loss_multiplier
1785    }
1786
1787    /// Clean up old samples and statistics
1788    fn cleanup(&mut self, now: Instant) {
1789        // Remove old RTT samples (keep only recent ones)
1790        let _cutoff_time = now - Duration::from_secs(60);
1791
1792        // Reset statistics if they're too old
1793        if let Some(last_update) = self.timeout_stats.last_update {
1794            if now.duration_since(last_update) > Duration::from_secs(300) {
1795                self.timeout_stats = TimeoutStatistics::default();
1796            }
1797        }
1798    }
1799}
1800
1801#[allow(dead_code)]
1802impl NatTraversalState {
1803    /// Create new NAT traversal state with given configuration
1804    ///
1805    /// v0.13.0: Role parameter removed - all nodes are symmetric P2P nodes.
1806    /// Every node can initiate, accept, and coordinate NAT traversal.
1807    pub(super) fn new(max_candidates: u32, coordination_timeout: Duration) -> Self {
1808        // v0.13.0: All nodes can coordinate - always create coordinator
1809        let bootstrap_coordinator = Some(BootstrapCoordinator::new(BootstrapConfig::default()));
1810
1811        Self {
1812            // v0.13.0: role field removed - all nodes are symmetric
1813            local_candidates: HashMap::new(),
1814            remote_candidates: HashMap::new(),
1815            candidate_pairs: Vec::new(),
1816            pair_index: HashMap::new(),
1817            active_validations: HashMap::new(),
1818            coordination: None,
1819            next_sequence: VarInt::from_u32(1),
1820            max_candidates,
1821            coordination_timeout,
1822            stats: NatTraversalStats::default(),
1823            security_state: SecurityValidationState::new(),
1824            network_monitor: NetworkConditionMonitor::new(),
1825            resource_manager: ResourceCleanupCoordinator::new(),
1826            bootstrap_coordinator,
1827        }
1828    }
1829
1830    /// Add a remote candidate from AddAddress frame with security validation
1831    pub(super) fn add_remote_candidate(
1832        &mut self,
1833        sequence: VarInt,
1834        address: SocketAddr,
1835        priority: VarInt,
1836        now: Instant,
1837    ) -> Result<(), NatTraversalError> {
1838        // Resource management: Check if we should reject new resources
1839        if self.should_reject_new_resources(now) {
1840            debug!(
1841                "Rejecting new candidate due to resource limits: {}",
1842                address
1843            );
1844            return Err(NatTraversalError::ResourceLimitExceeded);
1845        }
1846        // Security validation: Check rate limiting
1847        if self.security_state.is_candidate_rate_limited(now) {
1848            self.stats.rate_limit_violations += 1;
1849            debug!("Rate limit exceeded for candidate addition: {}", address);
1850            return Err(NatTraversalError::RateLimitExceeded);
1851        }
1852
1853        // Security validation: Validate address format and safety
1854        match self.security_state.validate_address(address, now) {
1855            AddressValidationResult::Invalid => {
1856                self.stats.invalid_address_rejections += 1;
1857                self.stats.security_rejections += 1;
1858                debug!("Invalid address rejected: {}", address);
1859                return Err(NatTraversalError::InvalidAddress);
1860            }
1861            AddressValidationResult::Suspicious => {
1862                self.stats.security_rejections += 1;
1863                debug!("Suspicious address rejected: {}", address);
1864                return Err(NatTraversalError::SecurityValidationFailed);
1865            }
1866            AddressValidationResult::Valid => {
1867                // Continue with normal processing
1868            }
1869        }
1870
1871        // Check candidate count limit
1872        if self.remote_candidates.len() >= self.max_candidates as usize {
1873            return Err(NatTraversalError::TooManyCandidates);
1874        }
1875
1876        // Check for duplicate addresses (different sequence, same address)
1877        if self
1878            .remote_candidates
1879            .values()
1880            .any(|c| c.address == address && c.state != CandidateState::Removed)
1881        {
1882            return Err(NatTraversalError::DuplicateAddress);
1883        }
1884
1885        let candidate = AddressCandidate {
1886            address,
1887            priority: priority.into_inner() as u32,
1888            source: CandidateSource::Peer,
1889            discovered_at: now,
1890            state: CandidateState::New,
1891            attempt_count: 0,
1892            last_attempt: None,
1893        };
1894
1895        self.remote_candidates.insert(sequence, candidate);
1896        self.stats.remote_candidates_received += 1;
1897
1898        trace!(
1899            "Added remote candidate: {} with priority {}",
1900            address, priority
1901        );
1902        Ok(())
1903    }
1904
1905    /// Remove a candidate by sequence number
1906    pub(super) fn remove_candidate(&mut self, sequence: VarInt) -> bool {
1907        if let Some(candidate) = self.remote_candidates.get_mut(&sequence) {
1908            candidate.state = CandidateState::Removed;
1909            // Cancel any active validation for this address
1910            self.active_validations.remove(&candidate.address);
1911            true
1912        } else {
1913            false
1914        }
1915    }
1916
1917    /// Add a local candidate that we've discovered
1918    #[allow(clippy::expect_used)]
1919    pub(super) fn add_local_candidate(
1920        &mut self,
1921        address: SocketAddr,
1922        source: CandidateSource,
1923        now: Instant,
1924    ) -> VarInt {
1925        let sequence = self.next_sequence;
1926        self.next_sequence = VarInt::from_u64(self.next_sequence.into_inner() + 1)
1927            .expect("sequence number overflow");
1928        // Calculate priority for this candidate
1929        let candidate_type = classify_candidate_type(source);
1930        let local_preference = self.calculate_local_preference(address);
1931        let priority = calculate_candidate_priority(candidate_type, local_preference, 1);
1932
1933        let candidate = AddressCandidate {
1934            address,
1935            priority,
1936            source,
1937            discovered_at: now,
1938            state: CandidateState::New,
1939            attempt_count: 0,
1940            last_attempt: None,
1941        };
1942
1943        self.local_candidates.insert(sequence, candidate);
1944        self.stats.local_candidates_sent += 1;
1945
1946        // Regenerate pairs when we add a new local candidate
1947        self.generate_candidate_pairs(now);
1948
1949        sequence
1950    }
1951
1952    /// Calculate local preference for address prioritization
1953    fn calculate_local_preference(&self, addr: SocketAddr) -> u16 {
1954        match addr {
1955            SocketAddr::V4(v4) => {
1956                if v4.ip().is_loopback() {
1957                    0 // Lowest priority
1958                } else if v4.ip().is_private() {
1959                    65000 // High priority for local network
1960                } else {
1961                    32000 // Medium priority for public addresses
1962                }
1963            }
1964            SocketAddr::V6(v6) => {
1965                if v6.ip().is_loopback() {
1966                    0
1967                } else if v6.ip().segments()[0] == 0xfe80 {
1968                    // Link-local IPv6 check
1969                    30000 // Link-local gets medium-low priority
1970                } else {
1971                    50000 // IPv6 generally gets good priority
1972                }
1973            }
1974        }
1975    }
1976    /// Generate all possible candidate pairs from local and remote candidates
1977    pub(super) fn generate_candidate_pairs(&mut self, now: Instant) {
1978        self.candidate_pairs.clear();
1979        self.pair_index.clear();
1980        // Pre-allocate capacity to avoid reallocations
1981        let estimated_capacity = self.local_candidates.len() * self.remote_candidates.len();
1982        self.candidate_pairs.reserve(estimated_capacity);
1983        self.pair_index.reserve(estimated_capacity);
1984
1985        // Cache compatibility checks to avoid repeated work
1986        let mut compatibility_cache: HashMap<(SocketAddr, SocketAddr), bool> = HashMap::new();
1987
1988        for local_candidate in self.local_candidates.values() {
1989            // Skip removed candidates early
1990            if local_candidate.state == CandidateState::Removed {
1991                continue;
1992            }
1993
1994            // Pre-classify local candidate type once
1995            let local_type = classify_candidate_type(local_candidate.source);
1996
1997            for (remote_seq, remote_candidate) in &self.remote_candidates {
1998                // Skip removed candidates
1999                if remote_candidate.state == CandidateState::Removed {
2000                    continue;
2001                }
2002
2003                // Check compatibility with caching
2004                let cache_key = (local_candidate.address, remote_candidate.address);
2005                let compatible = *compatibility_cache.entry(cache_key).or_insert_with(|| {
2006                    are_candidates_compatible(local_candidate, remote_candidate)
2007                });
2008
2009                if !compatible {
2010                    continue;
2011                }
2012
2013                // Calculate combined priority
2014                let pair_priority =
2015                    calculate_pair_priority(local_candidate.priority, remote_candidate.priority);
2016
2017                // Classify pair type (local already classified)
2018                let remote_type = classify_candidate_type(remote_candidate.source);
2019                let pair_type = classify_pair_type(local_type, remote_type);
2020
2021                let pair = CandidatePair {
2022                    remote_sequence: *remote_seq,
2023                    local_addr: local_candidate.address,
2024                    remote_addr: remote_candidate.address,
2025                    priority: pair_priority,
2026                    state: PairState::Waiting,
2027                    pair_type,
2028                    created_at: now,
2029                    last_check: None,
2030                };
2031
2032                // Store index for O(1) lookup
2033                let index = self.candidate_pairs.len();
2034                self.pair_index.insert(remote_candidate.address, index);
2035                self.candidate_pairs.push(pair);
2036            }
2037        }
2038
2039        // Sort pairs by priority (highest first) - use unstable sort for better performance
2040        self.candidate_pairs
2041            .sort_unstable_by(|a, b| b.priority.cmp(&a.priority));
2042
2043        // Rebuild index after sorting since indices changed
2044        self.pair_index.clear();
2045        for (idx, pair) in self.candidate_pairs.iter().enumerate() {
2046            self.pair_index.insert(pair.remote_addr, idx);
2047        }
2048
2049        trace!("Generated {} candidate pairs", self.candidate_pairs.len());
2050    }
2051
2052    /// Get the highest priority pairs ready for validation
2053    pub(super) fn get_next_validation_pairs(
2054        &mut self,
2055        max_concurrent: usize,
2056    ) -> Vec<&mut CandidatePair> {
2057        // Since pairs are sorted by priority (highest first), we can stop early
2058        // once we find enough waiting pairs or reach lower priority pairs
2059        let mut result = Vec::with_capacity(max_concurrent);
2060        for pair in self.candidate_pairs.iter_mut() {
2061            if pair.state == PairState::Waiting {
2062                result.push(pair);
2063                if result.len() >= max_concurrent {
2064                    break;
2065                }
2066            }
2067        }
2068
2069        result
2070    }
2071
2072    /// Find a candidate pair by remote address
2073    pub(super) fn find_pair_by_remote_addr(
2074        &mut self,
2075        addr: SocketAddr,
2076    ) -> Option<&mut CandidatePair> {
2077        // Use index for O(1) lookup instead of O(n) linear search
2078        if let Some(&index) = self.pair_index.get(&addr) {
2079            self.candidate_pairs.get_mut(index)
2080        } else {
2081            None
2082        }
2083    }
2084    /// Mark a pair as succeeded and handle promotion
2085    pub(super) fn mark_pair_succeeded(&mut self, remote_addr: SocketAddr) -> bool {
2086        // Find the pair and get its type and priority
2087        let (succeeded_type, succeeded_priority) = {
2088            if let Some(pair) = self.find_pair_by_remote_addr(remote_addr) {
2089                pair.state = PairState::Succeeded;
2090                (pair.pair_type, pair.priority)
2091            } else {
2092                return false;
2093            }
2094        };
2095        // Freeze lower priority pairs of the same type to avoid unnecessary testing
2096        for other_pair in &mut self.candidate_pairs {
2097            if other_pair.pair_type == succeeded_type
2098                && other_pair.priority < succeeded_priority
2099                && other_pair.state == PairState::Waiting
2100            {
2101                other_pair.state = PairState::Frozen;
2102            }
2103        }
2104
2105        true
2106    }
2107
2108    /// Get the best succeeded pair for each address family
2109    pub(super) fn get_best_succeeded_pairs(&self) -> Vec<&CandidatePair> {
2110        let mut best_ipv4: Option<&CandidatePair> = None;
2111        let mut best_ipv6: Option<&CandidatePair> = None;
2112        for pair in &self.candidate_pairs {
2113            if pair.state != PairState::Succeeded {
2114                continue;
2115            }
2116
2117            match pair.remote_addr {
2118                SocketAddr::V4(_) => {
2119                    if best_ipv4.is_none_or(|best| pair.priority > best.priority) {
2120                        best_ipv4 = Some(pair);
2121                    }
2122                }
2123                SocketAddr::V6(_) => {
2124                    if best_ipv6.is_none_or(|best| pair.priority > best.priority) {
2125                        best_ipv6 = Some(pair);
2126                    }
2127                }
2128            }
2129        }
2130
2131        let mut result = Vec::new();
2132        if let Some(pair) = best_ipv4 {
2133            result.push(pair);
2134        }
2135        if let Some(pair) = best_ipv6 {
2136            result.push(pair);
2137        }
2138        result
2139    }
2140
2141    /// Get candidates ready for validation, sorted by priority
2142    pub(super) fn get_validation_candidates(&self) -> Vec<(VarInt, &AddressCandidate)> {
2143        let mut candidates: Vec<_> = self
2144            .remote_candidates
2145            .iter()
2146            .filter(|(_, c)| c.state == CandidateState::New)
2147            .map(|(k, v)| (*k, v))
2148            .collect();
2149        // Sort by priority (higher priority first)
2150        candidates.sort_by(|a, b| b.1.priority.cmp(&a.1.priority));
2151        candidates
2152    }
2153
2154    /// Start validation for a candidate address with security checks
2155    pub(super) fn start_validation(
2156        &mut self,
2157        sequence: VarInt,
2158        challenge: u64,
2159        now: Instant,
2160    ) -> Result<(), NatTraversalError> {
2161        let candidate = self
2162            .remote_candidates
2163            .get_mut(&sequence)
2164            .ok_or(NatTraversalError::UnknownCandidate)?;
2165        if candidate.state != CandidateState::New {
2166            return Err(NatTraversalError::InvalidCandidateState);
2167        }
2168
2169        // Security validation: Check for validation abuse patterns
2170        if Self::is_validation_suspicious(candidate, now) {
2171            self.stats.security_rejections += 1;
2172            debug!(
2173                "Suspicious validation attempt rejected for address {}",
2174                candidate.address
2175            );
2176            return Err(NatTraversalError::SecurityValidationFailed);
2177        }
2178
2179        // Security validation: Limit concurrent validations
2180        if self.active_validations.len() >= 10 {
2181            debug!(
2182                "Too many concurrent validations, rejecting new validation for {}",
2183                candidate.address
2184            );
2185            return Err(NatTraversalError::SecurityValidationFailed);
2186        }
2187
2188        // Update candidate state
2189        candidate.state = CandidateState::Validating;
2190        candidate.attempt_count += 1;
2191        candidate.last_attempt = Some(now);
2192
2193        // Track validation state
2194        let validation = PathValidationState {
2195            challenge,
2196            sent_at: now,
2197            retry_count: 0,
2198            max_retries: 3, // TODO: Make configurable
2199            coordination_round: self.coordination.as_ref().map(|c| c.round),
2200            timeout_state: AdaptiveTimeoutState::new(),
2201            last_retry_at: None,
2202        };
2203
2204        self.active_validations
2205            .insert(candidate.address, validation);
2206        trace!(
2207            "Started validation for candidate {} with challenge {}",
2208            candidate.address, challenge
2209        );
2210        Ok(())
2211    }
2212
2213    /// Check if a validation request shows suspicious patterns
2214    fn is_validation_suspicious(candidate: &AddressCandidate, now: Instant) -> bool {
2215        // Check for excessive retry attempts
2216        if candidate.attempt_count > 10 {
2217            return true;
2218        }
2219        // Check for rapid retry patterns
2220        if let Some(last_attempt) = candidate.last_attempt {
2221            let time_since_last = now.duration_since(last_attempt);
2222            if time_since_last < Duration::from_millis(100) {
2223                return true; // Too frequent attempts
2224            }
2225        }
2226
2227        // Check if this candidate was recently failed
2228        if candidate.state == CandidateState::Failed {
2229            let time_since_discovery = now.duration_since(candidate.discovered_at);
2230            if time_since_discovery < Duration::from_secs(60) {
2231                return true; // Recently failed, shouldn't retry so soon
2232            }
2233        }
2234
2235        false
2236    }
2237
2238    /// Handle successful validation response
2239    pub(super) fn handle_validation_success(
2240        &mut self,
2241        remote_addr: SocketAddr,
2242        challenge: u64,
2243        now: Instant,
2244    ) -> Result<VarInt, NatTraversalError> {
2245        // Find the candidate with this address
2246        let sequence = self
2247            .remote_candidates
2248            .iter()
2249            .find(|(_, c)| c.address == remote_addr)
2250            .map(|(seq, _)| *seq)
2251            .ok_or(NatTraversalError::UnknownCandidate)?;
2252        // Verify challenge matches and update timeout state
2253        let validation = self
2254            .active_validations
2255            .get_mut(&remote_addr)
2256            .ok_or(NatTraversalError::NoActiveValidation)?;
2257
2258        if validation.challenge != challenge {
2259            return Err(NatTraversalError::ChallengeMismatch);
2260        }
2261
2262        // Calculate RTT and update adaptive timeout
2263        let rtt = now.duration_since(validation.sent_at);
2264        validation.timeout_state.update_success(rtt);
2265
2266        // Update network monitor
2267        self.network_monitor.record_success(rtt, now);
2268
2269        // Update candidate state
2270        let candidate = self
2271            .remote_candidates
2272            .get_mut(&sequence)
2273            .ok_or(NatTraversalError::UnknownCandidate)?;
2274
2275        candidate.state = CandidateState::Valid;
2276        self.active_validations.remove(&remote_addr);
2277        self.stats.validations_succeeded += 1;
2278
2279        trace!(
2280            "Validation successful for {} with RTT {:?}",
2281            remote_addr, rtt
2282        );
2283        Ok(sequence)
2284    }
2285
2286    /// Start a new coordination round for simultaneous hole punching with security validation
2287    pub(super) fn start_coordination_round(
2288        &mut self,
2289        targets: Vec<PunchTarget>,
2290        now: Instant,
2291    ) -> Result<VarInt, NatTraversalError> {
2292        // Security validation: Check rate limiting for coordination requests
2293        if self.security_state.is_coordination_rate_limited(now) {
2294            self.stats.rate_limit_violations += 1;
2295            debug!(
2296                "Rate limit exceeded for coordination request with {} targets",
2297                targets.len()
2298            );
2299            return Err(NatTraversalError::RateLimitExceeded);
2300        }
2301        // Security validation: Check for suspicious coordination patterns
2302        if self.is_coordination_suspicious(&targets, now) {
2303            self.stats.suspicious_coordination_attempts += 1;
2304            self.stats.security_rejections += 1;
2305            debug!(
2306                "Suspicious coordination request rejected with {} targets",
2307                targets.len()
2308            );
2309            return Err(NatTraversalError::SuspiciousCoordination);
2310        }
2311
2312        // Security validation: Validate all target addresses
2313        for target in &targets {
2314            match self
2315                .security_state
2316                .validate_address(target.remote_addr, now)
2317            {
2318                AddressValidationResult::Invalid => {
2319                    self.stats.invalid_address_rejections += 1;
2320                    self.stats.security_rejections += 1;
2321                    debug!(
2322                        "Invalid target address in coordination: {}",
2323                        target.remote_addr
2324                    );
2325                    return Err(NatTraversalError::InvalidAddress);
2326                }
2327                AddressValidationResult::Suspicious => {
2328                    self.stats.security_rejections += 1;
2329                    debug!(
2330                        "Suspicious target address in coordination: {}",
2331                        target.remote_addr
2332                    );
2333                    return Err(NatTraversalError::SecurityValidationFailed);
2334                }
2335                AddressValidationResult::Valid => {
2336                    // Continue with normal processing
2337                }
2338            }
2339        }
2340
2341        let round = self.next_sequence;
2342        self.next_sequence = VarInt::from_u64(self.next_sequence.into_inner() + 1)
2343            .expect("sequence number overflow");
2344
2345        // Calculate synchronized punch time (grace period for coordination)
2346        let coordination_grace = Duration::from_millis(500); // 500ms for coordination
2347        let punch_start = now + coordination_grace;
2348
2349        self.coordination = Some(CoordinationState {
2350            round,
2351            punch_targets: targets,
2352            round_start: now,
2353            punch_start,
2354            round_duration: self.coordination_timeout,
2355            state: CoordinationPhase::Requesting,
2356            punch_request_sent: false,
2357            peer_punch_received: false,
2358            retry_count: 0,
2359            max_retries: 3,
2360            timeout_state: AdaptiveTimeoutState::new(),
2361            last_retry_at: None,
2362        });
2363
2364        self.stats.coordination_rounds += 1;
2365        trace!(
2366            "Started coordination round {} with {} targets",
2367            round,
2368            self.coordination
2369                .as_ref()
2370                .map(|c| c.punch_targets.len())
2371                .unwrap_or(0)
2372        );
2373        Ok(round)
2374    }
2375
2376    /// Check if a coordination request shows suspicious patterns
2377    fn is_coordination_suspicious(&self, targets: &[PunchTarget], _now: Instant) -> bool {
2378        // Check for excessive number of targets
2379        if targets.len() > 20 {
2380            return true;
2381        }
2382        // Check for duplicate targets
2383        let mut seen_addresses = std::collections::HashSet::new();
2384        for target in targets {
2385            if !seen_addresses.insert(target.remote_addr) {
2386                return true; // Duplicate target
2387            }
2388        }
2389
2390        // Check for patterns that might indicate scanning
2391        if targets.len() > 5 {
2392            // Check if all targets are in sequential IP ranges (potential scan)
2393            let mut ipv4_addresses: Vec<_> = targets
2394                .iter()
2395                .filter_map(|t| match t.remote_addr.ip() {
2396                    IpAddr::V4(ipv4) => Some(u32::from(ipv4)),
2397                    _ => None,
2398                })
2399                .collect();
2400
2401            if ipv4_addresses.len() >= 3 {
2402                ipv4_addresses.sort();
2403                let mut sequential_count = 1;
2404                for i in 1..ipv4_addresses.len() {
2405                    if ipv4_addresses[i] == ipv4_addresses[i - 1] + 1 {
2406                        sequential_count += 1;
2407                        if sequential_count >= 3 {
2408                            return true; // Sequential IPs detected
2409                        }
2410                    } else {
2411                        sequential_count = 1;
2412                    }
2413                }
2414            }
2415        }
2416
2417        false
2418    }
2419
2420    /// Get the current coordination phase
2421    pub(super) fn get_coordination_phase(&self) -> Option<CoordinationPhase> {
2422        self.coordination.as_ref().map(|c| c.state)
2423    }
2424    /// Check if we need to send PUNCH_ME_NOW frame
2425    pub(super) fn should_send_punch_request(&self) -> bool {
2426        if let Some(coord) = &self.coordination {
2427            coord.state == CoordinationPhase::Requesting && !coord.punch_request_sent
2428        } else {
2429            false
2430        }
2431    }
2432    /// Mark that we've sent our PUNCH_ME_NOW request
2433    pub(super) fn mark_punch_request_sent(&mut self) {
2434        if let Some(coord) = &mut self.coordination {
2435            coord.punch_request_sent = true;
2436            coord.state = CoordinationPhase::Coordinating;
2437            trace!("PUNCH_ME_NOW sent, waiting for peer coordination");
2438        }
2439    }
2440    /// Handle receiving peer's PUNCH_ME_NOW (via coordinator) with security validation
2441    pub(super) fn handle_peer_punch_request(
2442        &mut self,
2443        peer_round: VarInt,
2444        now: Instant,
2445    ) -> Result<bool, NatTraversalError> {
2446        // Security validation: Check if this is a valid coordination request
2447        if self.is_peer_coordination_suspicious(peer_round, now) {
2448            self.stats.suspicious_coordination_attempts += 1;
2449            self.stats.security_rejections += 1;
2450            debug!(
2451                "Suspicious peer coordination request rejected for round {}",
2452                peer_round
2453            );
2454            return Err(NatTraversalError::SuspiciousCoordination);
2455        }
2456        if let Some(coord) = &mut self.coordination {
2457            if coord.round == peer_round {
2458                match coord.state {
2459                    CoordinationPhase::Coordinating | CoordinationPhase::Requesting => {
2460                        coord.peer_punch_received = true;
2461                        coord.state = CoordinationPhase::Preparing;
2462
2463                        // Calculate adaptive grace period based on network conditions
2464                        let network_rtt = self
2465                            .network_monitor
2466                            .get_estimated_rtt()
2467                            .unwrap_or(Duration::from_millis(100));
2468                        let quality_score = self.network_monitor.get_quality_score();
2469
2470                        // Scale grace period: good networks get shorter delays
2471                        let base_grace = Duration::from_millis(150);
2472                        let rtt_factor = (network_rtt.as_millis() as f64 / 100.0).clamp(0.5, 3.0);
2473                        let quality_factor = (2.0 - quality_score).clamp(1.0, 2.0);
2474
2475                        let adaptive_grace = Duration::from_millis(
2476                            (base_grace.as_millis() as f64 * rtt_factor * quality_factor) as u64,
2477                        );
2478
2479                        coord.punch_start = now + adaptive_grace;
2480
2481                        trace!(
2482                            "Peer coordination received, punch starts in {:?} (RTT: {:?}, quality: {:.2})",
2483                            adaptive_grace, network_rtt, quality_score
2484                        );
2485                        Ok(true)
2486                    }
2487                    CoordinationPhase::Preparing => {
2488                        // Already in preparation phase, just acknowledge
2489                        trace!("Peer coordination confirmed during preparation");
2490                        Ok(true)
2491                    }
2492                    _ => {
2493                        debug!(
2494                            "Received coordination in unexpected phase: {:?}",
2495                            coord.state
2496                        );
2497                        Ok(false)
2498                    }
2499                }
2500            } else {
2501                debug!(
2502                    "Received coordination for wrong round: {} vs {}",
2503                    peer_round, coord.round
2504                );
2505                Ok(false)
2506            }
2507        } else {
2508            debug!("Received peer coordination but no active round");
2509            Ok(false)
2510        }
2511    }
2512
2513    /// Check if a peer coordination request is suspicious
2514    fn is_peer_coordination_suspicious(&self, peer_round: VarInt, _now: Instant) -> bool {
2515        // Check for round number anomalies
2516        if peer_round.into_inner() == 0 {
2517            return true; // Invalid round number
2518        }
2519        // Check if round is too far in the future or past
2520        if let Some(coord) = &self.coordination {
2521            let our_round = coord.round.into_inner();
2522            let peer_round_num = peer_round.into_inner();
2523
2524            // Allow some variance but reject extreme differences
2525            if peer_round_num > our_round + 100 || peer_round_num + 100 < our_round {
2526                return true;
2527            }
2528        }
2529
2530        false
2531    }
2532
2533    /// Check if it's time to start hole punching
2534    pub(super) fn should_start_punching(&self, now: Instant) -> bool {
2535        if let Some(coord) = &self.coordination {
2536            match coord.state {
2537                CoordinationPhase::Preparing => now >= coord.punch_start,
2538                CoordinationPhase::Coordinating => {
2539                    // Check if we have peer confirmation and grace period elapsed
2540                    coord.peer_punch_received && now >= coord.punch_start
2541                }
2542                _ => false,
2543            }
2544        } else {
2545            false
2546        }
2547    }
2548    /// Start the synchronized hole punching phase
2549    pub(super) fn start_punching_phase(&mut self, now: Instant) {
2550        if let Some(coord) = &mut self.coordination {
2551            coord.state = CoordinationPhase::Punching;
2552            // Calculate precise timing for coordinated transmission
2553            let network_rtt = self
2554                .network_monitor
2555                .get_estimated_rtt()
2556                .unwrap_or(Duration::from_millis(100));
2557
2558            // Add small random jitter to avoid thundering herd
2559            let jitter_ms: u64 = rand::random::<u64>() % 11;
2560            let jitter = Duration::from_millis(jitter_ms);
2561            let transmission_time = coord.punch_start + network_rtt / 2 + jitter;
2562
2563            // Update punch start time with precise calculation
2564            coord.punch_start = transmission_time.max(now);
2565
2566            trace!(
2567                "Starting synchronized hole punching at {:?} (RTT: {:?}, jitter: {:?})",
2568                coord.punch_start, network_rtt, jitter
2569            );
2570        }
2571    }
2572
2573    /// Get punch targets for the current round
2574    pub(super) fn get_punch_targets_from_coordination(&self) -> Option<&[PunchTarget]> {
2575        self.coordination
2576            .as_ref()
2577            .map(|c| c.punch_targets.as_slice())
2578    }
2579    /// Mark coordination as validating (PATH_CHALLENGE sent)
2580    pub(super) fn mark_coordination_validating(&mut self) {
2581        if let Some(coord) = &mut self.coordination {
2582            if coord.state == CoordinationPhase::Punching {
2583                coord.state = CoordinationPhase::Validating;
2584                trace!("Coordination moved to validation phase");
2585            }
2586        }
2587    }
2588    /// Handle successful path validation during coordination
2589    pub(super) fn handle_coordination_success(
2590        &mut self,
2591        remote_addr: SocketAddr,
2592        now: Instant,
2593    ) -> bool {
2594        if let Some(coord) = &mut self.coordination {
2595            // Check if this address was one of our punch targets
2596            let was_target = coord
2597                .punch_targets
2598                .iter()
2599                .any(|target| target.remote_addr == remote_addr);
2600            if was_target && coord.state == CoordinationPhase::Validating {
2601                // Calculate RTT and update adaptive timeout
2602                let rtt = now.duration_since(coord.round_start);
2603                coord.timeout_state.update_success(rtt);
2604                self.network_monitor.record_success(rtt, now);
2605
2606                coord.state = CoordinationPhase::Succeeded;
2607                self.stats.direct_connections += 1;
2608                trace!(
2609                    "Coordination succeeded via {} with RTT {:?}",
2610                    remote_addr, rtt
2611                );
2612                true
2613            } else {
2614                false
2615            }
2616        } else {
2617            false
2618        }
2619    }
2620
2621    /// Handle coordination failure and determine if we should retry
2622    pub(super) fn handle_coordination_failure(&mut self, now: Instant) -> bool {
2623        if let Some(coord) = &mut self.coordination {
2624            coord.retry_count += 1;
2625            coord.timeout_state.update_timeout();
2626            self.network_monitor.record_timeout(now);
2627            // Check network conditions before retrying
2628            if coord.timeout_state.should_retry(coord.max_retries)
2629                && self.network_monitor.is_suitable_for_coordination()
2630            {
2631                // Retry with adaptive timeout
2632                coord.state = CoordinationPhase::Requesting;
2633                coord.punch_request_sent = false;
2634                coord.peer_punch_received = false;
2635                coord.round_start = now;
2636                coord.last_retry_at = Some(now);
2637
2638                // Use adaptive timeout for retry delay
2639                let retry_delay = coord.timeout_state.get_retry_delay();
2640
2641                // Factor in network quality for retry timing
2642                let quality_multiplier = 2.0 - self.network_monitor.get_quality_score();
2643                let adjusted_delay = Duration::from_millis(
2644                    (retry_delay.as_millis() as f64 * quality_multiplier) as u64,
2645                );
2646
2647                coord.punch_start = now + adjusted_delay;
2648
2649                trace!(
2650                    "Coordination failed, retrying round {} (attempt {}) with delay {:?} (quality: {:.2})",
2651                    coord.round,
2652                    coord.retry_count + 1,
2653                    adjusted_delay,
2654                    self.network_monitor.get_quality_score()
2655                );
2656                true
2657            } else {
2658                coord.state = CoordinationPhase::Failed;
2659                self.stats.coordination_failures += 1;
2660
2661                if !self.network_monitor.is_suitable_for_coordination() {
2662                    trace!(
2663                        "Coordination failed due to poor network conditions (quality: {:.2})",
2664                        self.network_monitor.get_quality_score()
2665                    );
2666                } else {
2667                    trace!("Coordination failed after {} attempts", coord.retry_count);
2668                }
2669                false
2670            }
2671        } else {
2672            false
2673        }
2674    }
2675
2676    /// Check if the current coordination round has timed out
2677    pub(super) fn check_coordination_timeout(&mut self, now: Instant) -> bool {
2678        if let Some(coord) = &mut self.coordination {
2679            let timeout = coord.timeout_state.get_timeout();
2680            let elapsed = now.duration_since(coord.round_start);
2681            if elapsed > timeout {
2682                trace!(
2683                    "Coordination round {} timed out after {:?} (adaptive timeout: {:?})",
2684                    coord.round, elapsed, timeout
2685                );
2686                self.handle_coordination_failure(now);
2687                true
2688            } else {
2689                false
2690            }
2691        } else {
2692            false
2693        }
2694    }
2695
2696    /// Check for validation timeouts and handle retries
2697    pub(super) fn check_validation_timeouts(&mut self, now: Instant) -> Vec<SocketAddr> {
2698        let mut expired_validations = Vec::new();
2699        let mut retry_validations = Vec::new();
2700
2701        for (addr, validation) in &mut self.active_validations {
2702            let timeout = validation.timeout_state.get_timeout();
2703            let elapsed = now.duration_since(validation.sent_at);
2704
2705            if elapsed >= timeout {
2706                if validation
2707                    .timeout_state
2708                    .should_retry(validation.max_retries)
2709                {
2710                    // Schedule retry
2711                    retry_validations.push(*addr);
2712                } else {
2713                    // Mark as expired
2714                    expired_validations.push(*addr);
2715                }
2716            }
2717        }
2718
2719        // Handle retries
2720        for addr in retry_validations {
2721            if let Some(validation) = self.active_validations.get_mut(&addr) {
2722                validation.retry_count += 1;
2723                validation.sent_at = now;
2724                validation.last_retry_at = Some(now);
2725                validation.timeout_state.update_timeout();
2726
2727                trace!(
2728                    "Retrying validation for {} (attempt {})",
2729                    addr,
2730                    validation.retry_count + 1
2731                );
2732            }
2733        }
2734
2735        // Remove expired validations
2736        for addr in &expired_validations {
2737            self.active_validations.remove(addr);
2738            self.network_monitor.record_timeout(now);
2739            trace!("Validation expired for {}", addr);
2740        }
2741
2742        expired_validations
2743    }
2744
2745    /// Schedule validation retries for active validations that need retry
2746    pub(super) fn schedule_validation_retries(&mut self, now: Instant) -> Vec<SocketAddr> {
2747        let mut retry_addresses = Vec::new();
2748
2749        // Get all active validations that need retry
2750        for (addr, validation) in &mut self.active_validations {
2751            let elapsed = now.duration_since(validation.sent_at);
2752            let timeout = validation.timeout_state.get_timeout();
2753
2754            if elapsed > timeout
2755                && validation
2756                    .timeout_state
2757                    .should_retry(validation.max_retries)
2758            {
2759                // Update retry state
2760                validation.retry_count += 1;
2761                validation.last_retry_at = Some(now);
2762                validation.sent_at = now; // Reset sent time for new attempt
2763                validation.timeout_state.update_timeout();
2764
2765                retry_addresses.push(*addr);
2766                trace!(
2767                    "Scheduled retry {} for validation to {}",
2768                    validation.retry_count, addr
2769                );
2770            }
2771        }
2772
2773        retry_addresses
2774    }
2775
2776    /// Update network conditions and cleanup
2777    pub(super) fn update_network_conditions(&mut self, now: Instant) {
2778        self.network_monitor.cleanup(now);
2779
2780        // Update timeout multiplier based on network conditions
2781        let multiplier = self.network_monitor.get_timeout_multiplier();
2782
2783        // Apply network-aware timeout adjustments to active validations
2784        for validation in self.active_validations.values_mut() {
2785            if multiplier > 1.5 {
2786                // Poor network conditions - be more patient
2787                validation.timeout_state.backoff_multiplier =
2788                    (validation.timeout_state.backoff_multiplier * 1.2)
2789                        .min(validation.timeout_state.max_backoff_multiplier);
2790            } else if multiplier < 0.8 {
2791                // Good network conditions - be more aggressive
2792                validation.timeout_state.backoff_multiplier =
2793                    (validation.timeout_state.backoff_multiplier * 0.9).max(1.0);
2794            }
2795        }
2796    }
2797
2798    /// Check if coordination should be retried now
2799    pub(super) fn should_retry_coordination(&self, now: Instant) -> bool {
2800        if let Some(coord) = &self.coordination {
2801            if coord.retry_count > 0 {
2802                if let Some(last_retry) = coord.last_retry_at {
2803                    let retry_delay = coord.timeout_state.get_retry_delay();
2804                    return now.duration_since(last_retry) >= retry_delay;
2805                }
2806            }
2807        }
2808        false
2809    }
2810
2811    /// Perform resource management and cleanup
2812    pub(super) fn perform_resource_management(&mut self, now: Instant) -> u64 {
2813        // Update resource usage statistics
2814        self.resource_manager.update_stats(
2815            self.active_validations.len(),
2816            self.local_candidates.len(),
2817            self.remote_candidates.len(),
2818            self.candidate_pairs.len(),
2819        );
2820
2821        // Calculate current memory pressure
2822        let memory_pressure = self.resource_manager.calculate_memory_pressure(
2823            self.active_validations.len(),
2824            self.local_candidates.len(),
2825            self.remote_candidates.len(),
2826            self.candidate_pairs.len(),
2827        );
2828
2829        // Perform cleanup if needed
2830        let mut cleaned = 0;
2831
2832        if self.resource_manager.should_cleanup(now) {
2833            cleaned += self.resource_manager.cleanup_expired_resources(
2834                &mut self.active_validations,
2835                &mut self.local_candidates,
2836                &mut self.remote_candidates,
2837                &mut self.candidate_pairs,
2838                &mut self.coordination,
2839                now,
2840            );
2841
2842            // If memory pressure is high, perform aggressive cleanup
2843            if memory_pressure > self.resource_manager.config.aggressive_cleanup_threshold {
2844                cleaned += self.resource_manager.aggressive_cleanup(
2845                    &mut self.active_validations,
2846                    &mut self.local_candidates,
2847                    &mut self.remote_candidates,
2848                    &mut self.candidate_pairs,
2849                    now,
2850                );
2851            }
2852        }
2853
2854        cleaned
2855    }
2856
2857    /// Check if we should reject new resources due to limits
2858    pub(super) fn should_reject_new_resources(&mut self, _now: Instant) -> bool {
2859        // Update stats and check limits
2860        self.resource_manager.update_stats(
2861            self.active_validations.len(),
2862            self.local_candidates.len(),
2863            self.remote_candidates.len(),
2864            self.candidate_pairs.len(),
2865        );
2866        let memory_pressure = self.resource_manager.calculate_memory_pressure(
2867            self.active_validations.len(),
2868            self.local_candidates.len(),
2869            self.remote_candidates.len(),
2870            self.candidate_pairs.len(),
2871        );
2872        // Reject if memory pressure is too high
2873        if memory_pressure > self.resource_manager.config.memory_pressure_threshold {
2874            self.resource_manager.stats.allocation_failures += 1;
2875            return true;
2876        }
2877
2878        // Reject if hard limits are exceeded
2879        if self.resource_manager.check_resource_limits(self) {
2880            self.resource_manager.stats.allocation_failures += 1;
2881            return true;
2882        }
2883
2884        false
2885    }
2886
2887    /// Get the next timeout instant for NAT traversal operations
2888    pub(super) fn get_next_timeout(&self, now: Instant) -> Option<Instant> {
2889        let mut next_timeout = None;
2890        // Check coordination timeout
2891        if let Some(coord) = &self.coordination {
2892            match coord.state {
2893                CoordinationPhase::Requesting | CoordinationPhase::Coordinating => {
2894                    let timeout_at = coord.round_start + self.coordination_timeout;
2895                    next_timeout =
2896                        Some(next_timeout.map_or(timeout_at, |t: Instant| t.min(timeout_at)));
2897                }
2898                CoordinationPhase::Preparing => {
2899                    // Punch start time is when we should start punching
2900                    next_timeout = Some(
2901                        next_timeout
2902                            .map_or(coord.punch_start, |t: Instant| t.min(coord.punch_start)),
2903                    );
2904                }
2905                CoordinationPhase::Punching | CoordinationPhase::Validating => {
2906                    // Check for coordination round timeout
2907                    let timeout_at = coord.round_start + coord.timeout_state.get_timeout();
2908                    next_timeout =
2909                        Some(next_timeout.map_or(timeout_at, |t: Instant| t.min(timeout_at)));
2910                }
2911                _ => {}
2912            }
2913        }
2914
2915        // Check validation timeouts
2916        for validation in self.active_validations.values() {
2917            let timeout_at = validation.sent_at + validation.timeout_state.get_timeout();
2918            next_timeout = Some(next_timeout.map_or(timeout_at, |t: Instant| t.min(timeout_at)));
2919        }
2920
2921        // Check resource cleanup interval
2922        if self.resource_manager.should_cleanup(now) {
2923            // Schedule cleanup soon
2924            let cleanup_at = now + Duration::from_secs(1);
2925            next_timeout = Some(next_timeout.map_or(cleanup_at, |t: Instant| t.min(cleanup_at)));
2926        }
2927
2928        next_timeout
2929    }
2930
2931    /// Handle timeout events and return actions to take
2932    pub(super) fn handle_timeout(
2933        &mut self,
2934        now: Instant,
2935    ) -> Result<Vec<TimeoutAction>, NatTraversalError> {
2936        let mut actions = Vec::new();
2937        // Handle coordination timeouts
2938        if let Some(coord) = &mut self.coordination {
2939            match coord.state {
2940                CoordinationPhase::Requesting | CoordinationPhase::Coordinating => {
2941                    let timeout_at = coord.round_start + self.coordination_timeout;
2942                    if now >= timeout_at {
2943                        coord.retry_count += 1;
2944                        if coord.retry_count >= coord.max_retries {
2945                            debug!("Coordination failed after {} retries", coord.retry_count);
2946                            coord.state = CoordinationPhase::Failed;
2947                            actions.push(TimeoutAction::Failed);
2948                        } else {
2949                            debug!(
2950                                "Coordination timeout, retrying ({}/{})",
2951                                coord.retry_count, coord.max_retries
2952                            );
2953                            coord.state = CoordinationPhase::Requesting;
2954                            coord.round_start = now;
2955                            actions.push(TimeoutAction::RetryCoordination);
2956                        }
2957                    }
2958                }
2959                CoordinationPhase::Preparing => {
2960                    // Check if it's time to start punching
2961                    if now >= coord.punch_start {
2962                        debug!("Starting coordinated hole punching");
2963                        coord.state = CoordinationPhase::Punching;
2964                        actions.push(TimeoutAction::StartValidation);
2965                    }
2966                }
2967                CoordinationPhase::Punching | CoordinationPhase::Validating => {
2968                    let timeout_at = coord.round_start + coord.timeout_state.get_timeout();
2969                    if now >= timeout_at {
2970                        coord.retry_count += 1;
2971                        if coord.retry_count >= coord.max_retries {
2972                            debug!("Validation failed after {} retries", coord.retry_count);
2973                            coord.state = CoordinationPhase::Failed;
2974                            actions.push(TimeoutAction::Failed);
2975                        } else {
2976                            debug!(
2977                                "Validation timeout, retrying ({}/{})",
2978                                coord.retry_count, coord.max_retries
2979                            );
2980                            coord.state = CoordinationPhase::Punching;
2981                            actions.push(TimeoutAction::StartValidation);
2982                        }
2983                    }
2984                }
2985                CoordinationPhase::Succeeded => {
2986                    actions.push(TimeoutAction::Complete);
2987                }
2988                CoordinationPhase::Failed => {
2989                    actions.push(TimeoutAction::Failed);
2990                }
2991                _ => {}
2992            }
2993        }
2994
2995        // Handle validation timeouts
2996        let mut expired_validations = Vec::new();
2997        for (addr, validation) in &mut self.active_validations {
2998            let timeout_at = validation.sent_at + validation.timeout_state.get_timeout();
2999            if now >= timeout_at {
3000                validation.retry_count += 1;
3001                if validation.retry_count >= validation.max_retries {
3002                    debug!("Path validation failed for {}: max retries exceeded", addr);
3003                    expired_validations.push(*addr);
3004                } else {
3005                    debug!(
3006                        "Path validation timeout for {}, retrying ({}/{})",
3007                        addr, validation.retry_count, validation.max_retries
3008                    );
3009                    validation.sent_at = now;
3010                    validation.last_retry_at = Some(now);
3011                    actions.push(TimeoutAction::StartValidation);
3012                }
3013            }
3014        }
3015
3016        // Remove expired validations
3017        for addr in expired_validations {
3018            self.active_validations.remove(&addr);
3019        }
3020
3021        // Handle resource cleanup
3022        if self.resource_manager.should_cleanup(now) {
3023            self.resource_manager.perform_cleanup(now);
3024        }
3025
3026        // Update network condition monitoring
3027        self.network_monitor.update_quality_score(now);
3028
3029        // If no coordination is active and we have candidates, try to start discovery
3030        if self.coordination.is_none()
3031            && !self.local_candidates.is_empty()
3032            && !self.remote_candidates.is_empty()
3033        {
3034            actions.push(TimeoutAction::RetryDiscovery);
3035        }
3036
3037        Ok(actions)
3038    }
3039
3040    /// Handle address observation for P2P nodes
3041    ///
3042    /// This method is called when a peer connects, allowing this node
3043    /// to observe the peer's public address. v0.13.0: All nodes can observe
3044    /// addresses - no bootstrap role required.
3045    pub(super) fn handle_address_observation(
3046        &mut self,
3047        peer_id: [u8; 32],
3048        observed_address: SocketAddr,
3049        connection_id: crate::shared::ConnectionId,
3050        now: Instant,
3051    ) -> Result<Option<crate::frame::AddAddress>, NatTraversalError> {
3052        if let Some(bootstrap_coordinator) = &mut self.bootstrap_coordinator {
3053            let connection_context = ConnectionContext {
3054                connection_id,
3055                original_destination: observed_address, // For now, use same as observed
3056                                                        // v0.13.0: peer_role removed - all nodes are symmetric
3057            };
3058
3059            // Observe the peer's address
3060            bootstrap_coordinator.observe_peer_address(
3061                peer_id,
3062                observed_address,
3063                connection_context,
3064                now,
3065            )?;
3066
3067            // Generate ADD_ADDRESS frame to inform peer of their observed address
3068            let sequence = self.next_sequence;
3069            self.next_sequence =
3070                VarInt::from_u32((self.next_sequence.into_inner() + 1).try_into().unwrap());
3071
3072            let priority = VarInt::from_u32(100); // Server-reflexive priority
3073            let add_address_frame =
3074                bootstrap_coordinator.generate_add_address_frame(peer_id, sequence, priority);
3075
3076            Ok(add_address_frame)
3077        } else {
3078            // Not a bootstrap node
3079            Ok(None)
3080        }
3081    }
3082
3083    /// Handle PUNCH_ME_NOW frame for bootstrap coordination
3084    ///
3085    /// This processes coordination requests from peers and facilitates
3086    /// hole punching between them.
3087    pub(super) fn handle_punch_me_now_frame(
3088        &mut self,
3089        from_peer: [u8; 32],
3090        source_addr: SocketAddr,
3091        frame: &crate::frame::PunchMeNow,
3092        now: Instant,
3093    ) -> Result<Option<crate::frame::PunchMeNow>, NatTraversalError> {
3094        if let Some(bootstrap_coordinator) = &mut self.bootstrap_coordinator {
3095            bootstrap_coordinator.process_punch_me_now_frame(from_peer, source_addr, frame, now)
3096        } else {
3097            // Not a bootstrap node - this frame should not be processed here
3098            Ok(None)
3099        }
3100    }
3101    /// Perform bootstrap cleanup operations
3102    ///
3103    /// Get observed address for a peer
3104    pub(super) fn get_observed_address(&self, peer_id: [u8; 32]) -> Option<SocketAddr> {
3105        self.bootstrap_coordinator
3106            .as_ref()
3107            .and_then(|coord| coord.peer_index.get(&peer_id).map(|p| p.observed_addr))
3108    }
3109
3110    /// Record a successful TryConnectTo callback probe
3111    ///
3112    /// Called when we receive a TryConnectToResponse indicating success.
3113    /// This confirms that the source address can reach us.
3114    pub(super) fn record_successful_callback_probe(
3115        &mut self,
3116        request_id: VarInt,
3117        source_address: SocketAddr,
3118    ) {
3119        debug!(
3120            "Recording successful callback probe: request_id={}, source={}",
3121            request_id, source_address
3122        );
3123        // Update statistics
3124        self.stats.callback_probes_received += 1;
3125        self.stats.callback_probes_successful += 1;
3126
3127        // The successful probe confirms that 'source_address' can connect to us
3128        // This is useful for understanding NAT traversal capabilities
3129    }
3130
3131    /// Record a failed TryConnectTo callback probe
3132    ///
3133    /// Called when we receive a TryConnectToResponse indicating failure.
3134    pub(super) fn record_failed_callback_probe(
3135        &mut self,
3136        request_id: VarInt,
3137        error_code: Option<crate::frame::TryConnectError>,
3138    ) {
3139        debug!(
3140            "Recording failed callback probe: request_id={}, error={:?}",
3141            request_id, error_code
3142        );
3143        // Update statistics
3144        self.stats.callback_probes_received += 1;
3145        self.stats.callback_probes_failed += 1;
3146    }
3147
3148    /// Start candidate discovery process
3149    pub(super) fn start_candidate_discovery(&mut self) -> Result<(), NatTraversalError> {
3150        debug!("Starting candidate discovery for NAT traversal");
3151        // Initialize discovery state if needed
3152        if self.local_candidates.is_empty() {
3153            // Add local interface candidates
3154            // This would be populated by the candidate discovery manager
3155            debug!("Local candidates will be populated by discovery manager");
3156        }
3157
3158        Ok(())
3159    }
3160
3161    /// Queue an ADD_ADDRESS frame for transmission
3162    pub(super) fn queue_add_address_frame(
3163        &mut self,
3164        sequence: VarInt,
3165        address: SocketAddr,
3166        priority: u32,
3167    ) -> Result<(), NatTraversalError> {
3168        debug!(
3169            "Queuing ADD_ADDRESS frame: seq={}, addr={}, priority={}",
3170            sequence, address, priority
3171        );
3172
3173        // Add to local candidates if not already present
3174        let candidate = AddressCandidate {
3175            address,
3176            priority,
3177            source: CandidateSource::Local,
3178            discovered_at: Instant::now(),
3179            state: CandidateState::New,
3180            attempt_count: 0,
3181            last_attempt: None,
3182        };
3183
3184        // Check if candidate already exists
3185        if !self.local_candidates.values().any(|c| c.address == address) {
3186            self.local_candidates.insert(sequence, candidate);
3187        }
3188
3189        Ok(())
3190    }
3191}
3192
3193/// Errors that can occur during NAT traversal
3194#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3195#[allow(dead_code)]
3196pub(crate) enum NatTraversalError {
3197    /// Too many candidates received
3198    TooManyCandidates,
3199    /// Duplicate address for different sequence
3200    DuplicateAddress,
3201    /// Unknown candidate sequence
3202    UnknownCandidate,
3203    /// Candidate in wrong state for operation
3204    InvalidCandidateState,
3205    /// No active validation for address
3206    NoActiveValidation,
3207    /// Challenge value mismatch
3208    ChallengeMismatch,
3209    /// Coordination round not active
3210    NoActiveCoordination,
3211    /// Security validation failed
3212    SecurityValidationFailed,
3213    /// Rate limit exceeded
3214    RateLimitExceeded,
3215    /// Invalid address format
3216    InvalidAddress,
3217    /// Suspicious coordination request
3218    SuspiciousCoordination,
3219    /// Resource limit exceeded
3220    ResourceLimitExceeded,
3221}
3222impl std::fmt::Display for NatTraversalError {
3223    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3224        match self {
3225            Self::TooManyCandidates => write!(f, "too many candidates"),
3226            Self::DuplicateAddress => write!(f, "duplicate address"),
3227            Self::UnknownCandidate => write!(f, "unknown candidate"),
3228            Self::InvalidCandidateState => write!(f, "invalid candidate state"),
3229            Self::NoActiveValidation => write!(f, "no active validation"),
3230            Self::ChallengeMismatch => write!(f, "challenge mismatch"),
3231            Self::NoActiveCoordination => write!(f, "no active coordination"),
3232            Self::SecurityValidationFailed => write!(f, "security validation failed"),
3233            Self::RateLimitExceeded => write!(f, "rate limit exceeded"),
3234            Self::InvalidAddress => write!(f, "invalid address"),
3235            Self::SuspiciousCoordination => write!(f, "suspicious coordination request"),
3236            Self::ResourceLimitExceeded => write!(f, "resource limit exceeded"),
3237        }
3238    }
3239}
3240
3241impl std::error::Error for NatTraversalError {}
3242
3243/// Security statistics for monitoring and debugging
3244#[derive(Debug, Clone)]
3245#[allow(dead_code)]
3246pub(crate) struct SecurityStats {
3247    /// Total security rejections
3248    pub total_security_rejections: u32,
3249    /// Rate limiting violations
3250    pub rate_limit_violations: u32,
3251    /// Invalid address rejections
3252    pub invalid_address_rejections: u32,
3253    /// Suspicious coordination attempts
3254    pub suspicious_coordination_attempts: u32,
3255    /// Number of active validations
3256    pub active_validations: usize,
3257    /// Number of cached address validations
3258    pub cached_address_validations: usize,
3259    /// Current candidate addition rate
3260    pub current_candidate_rate: usize,
3261    /// Current coordination request rate
3262    pub current_coordination_rate: usize,
3263}
3264/// Bootstrap coordinator state machine for NAT traversal coordination
3265///
3266/// This manages the bootstrap node's role in observing client addresses,
3267/// coordinating hole punching, and relaying coordination messages.
3268#[derive(Debug)]
3269pub(crate) struct BootstrapCoordinator {
3270    /// Address observation cache for quick lookups
3271    address_observations: HashMap<SocketAddr, AddressObservation>,
3272    /// Quick lookup by peer id for the last observed address
3273    peer_index: HashMap<PeerId, ObservedPeer>,
3274    /// Minimal coordination table keyed by round id
3275    coordination_table: HashMap<VarInt, CoordinationEntry>,
3276    /// Security validator for coordination requests
3277    security_validator: SecurityValidationState,
3278    /// Statistics for bootstrap operations
3279    stats: BootstrapStats,
3280}
3281// Removed legacy CoordinationSessionId type
3282/// Peer identifier for bootstrap coordination
3283type PeerId = [u8; 32];
3284/// Observed peer summary (minimal index)
3285#[derive(Debug, Clone)]
3286struct ObservedPeer {
3287    observed_addr: SocketAddr,
3288}
3289
3290/// Minimal coordination record linking two peers for a round
3291#[derive(Debug, Clone)]
3292struct CoordinationEntry {
3293    peer_b: Option<PeerId>,
3294    address_hint: SocketAddr,
3295}
3296/// Record of observed peer information
3297#[derive(Debug, Clone)]
3298#[allow(dead_code)]
3299pub(crate) struct PeerObservationRecord {
3300    /// The peer's unique identifier
3301    peer_id: PeerId,
3302    /// Last observed public address
3303    observed_address: SocketAddr,
3304    /// When this observation was made
3305    observed_at: Instant,
3306    /// Connection context for this observation
3307    connection_context: ConnectionContext,
3308    /// Whether this peer can participate in coordination
3309    can_coordinate: bool,
3310    /// Number of successful coordinations
3311    coordination_count: u32,
3312    /// Average coordination success rate
3313    success_rate: f64,
3314}
3315
3316/// Connection context for address observations
3317///
3318/// v0.13.0: peer_role field removed - all nodes are symmetric P2P nodes.
3319#[derive(Debug, Clone)]
3320#[allow(dead_code)]
3321pub(crate) struct ConnectionContext {
3322    /// Connection ID for this observation
3323    connection_id: ConnectionId,
3324    /// Original destination address (what peer thought it was connecting to)
3325    original_destination: SocketAddr,
3326    // v0.13.0: peer_role field removed - all nodes are symmetric P2P nodes
3327}
3328
3329// Transport parameters for NAT traversal removed (legacy)
3330
3331/// Address observation with validation
3332#[derive(Debug, Clone)]
3333#[allow(dead_code)]
3334struct AddressObservation {
3335    /// The observed address
3336    address: SocketAddr,
3337    /// When this address was first observed
3338    first_observed: Instant,
3339    /// How many times this address has been observed
3340    observation_count: u32,
3341    /// Validation state for this address
3342    validation_state: AddressValidationResult,
3343    /// Associated peer IDs for this address
3344    associated_peers: Vec<PeerId>,
3345}
3346
3347// Removed coordination session scaffolding
3348/// Pending coordination request awaiting peer participation (stub implementation)
3349/// Configuration for bootstrap coordinator behavior (stub implementation)
3350#[derive(Debug, Clone, Default)]
3351pub(crate) struct BootstrapConfig {
3352    _unused: (),
3353}
3354/// Statistics for bootstrap operations
3355#[derive(Debug, Clone, Default)]
3356pub(crate) struct BootstrapStats {
3357    /// Total address observations made
3358    total_observations: u64,
3359    /// Total coordination sessions facilitated
3360    total_coordinations: u64,
3361    /// Successful coordinations
3362    successful_coordinations: u64,
3363    /// Security rejections
3364    security_rejections: u64,
3365}
3366// Removed session state machine enums and recovery actions
3367impl BootstrapCoordinator {
3368    /// Create a new bootstrap coordinator
3369    pub(crate) fn new(_config: BootstrapConfig) -> Self {
3370        Self {
3371            address_observations: HashMap::new(),
3372            peer_index: HashMap::new(),
3373            coordination_table: HashMap::new(),
3374            security_validator: SecurityValidationState::new(),
3375            stats: BootstrapStats::default(),
3376        }
3377    }
3378    /// Observe a peer's address from an incoming connection
3379    ///
3380    /// This is called when a peer connects to this bootstrap node,
3381    /// allowing us to observe their public address.
3382    pub(crate) fn observe_peer_address(
3383        &mut self,
3384        peer_id: PeerId,
3385        observed_address: SocketAddr,
3386        _connection_context: ConnectionContext,
3387        now: Instant,
3388    ) -> Result<(), NatTraversalError> {
3389        // Security validation
3390        match self
3391            .security_validator
3392            .validate_address(observed_address, now)
3393        {
3394            AddressValidationResult::Valid => {}
3395            AddressValidationResult::Invalid => {
3396                self.stats.security_rejections += 1;
3397                return Err(NatTraversalError::InvalidAddress);
3398            }
3399            AddressValidationResult::Suspicious => {
3400                self.stats.security_rejections += 1;
3401                return Err(NatTraversalError::SecurityValidationFailed);
3402            }
3403        }
3404
3405        // Rate limiting check
3406        if self.security_validator.is_candidate_rate_limited(now) {
3407            self.stats.security_rejections += 1;
3408            return Err(NatTraversalError::RateLimitExceeded);
3409        }
3410
3411        // Update address observation
3412        let observation = self
3413            .address_observations
3414            .entry(observed_address)
3415            .or_insert_with(|| AddressObservation {
3416                address: observed_address,
3417                first_observed: now,
3418                observation_count: 0,
3419                validation_state: AddressValidationResult::Valid,
3420                associated_peers: Vec::new(),
3421            });
3422
3423        observation.observation_count += 1;
3424        if !observation.associated_peers.contains(&peer_id) {
3425            observation.associated_peers.push(peer_id);
3426        }
3427
3428        // Update minimal peer index for quick lookups
3429        self.peer_index.insert(
3430            peer_id,
3431            ObservedPeer {
3432                observed_addr: observed_address,
3433            },
3434        );
3435
3436        // Note: Full peer registry and session scaffolding removed; we keep only minimal caches
3437        self.stats.total_observations += 1;
3438        // active_peers removed from stats
3439
3440        debug!(
3441            "Observed peer {:?} at address {} (total observations: {})",
3442            peer_id, observed_address, self.stats.total_observations
3443        );
3444
3445        Ok(())
3446    }
3447
3448    /// Generate ADD_ADDRESS frame for a peer based on observation
3449    ///
3450    /// This creates an ADD_ADDRESS frame to inform a peer of their
3451    /// observed public address.
3452    pub(crate) fn generate_add_address_frame(
3453        &self,
3454        peer_id: PeerId,
3455        sequence: VarInt,
3456        priority: VarInt,
3457    ) -> Option<crate::frame::AddAddress> {
3458        let addr = self.peer_index.get(&peer_id)?.observed_addr;
3459        Some(crate::frame::AddAddress {
3460            sequence,
3461            address: addr,
3462            priority,
3463        })
3464    }
3465
3466    /// Process a PUNCH_ME_NOW frame from a peer
3467    ///
3468    /// This handles coordination requests from peers wanting to establish
3469    /// direct connections through NAT traversal.
3470    pub(crate) fn process_punch_me_now_frame(
3471        &mut self,
3472        from_peer: PeerId,
3473        source_addr: SocketAddr,
3474        frame: &crate::frame::PunchMeNow,
3475        now: Instant,
3476    ) -> Result<Option<crate::frame::PunchMeNow>, NatTraversalError> {
3477        // Enhanced security validation with adaptive rate limiting
3478        if self
3479            .security_validator
3480            .is_adaptive_rate_limited(from_peer, now)
3481        {
3482            self.stats.security_rejections += 1;
3483            debug!(
3484                "PUNCH_ME_NOW frame rejected: adaptive rate limit exceeded for peer {:?}",
3485                hex::encode(&from_peer[..8])
3486            );
3487            return Err(NatTraversalError::RateLimitExceeded);
3488        }
3489        // Enhanced address validation with amplification protection
3490        self.security_validator
3491            .enhanced_address_validation(frame.address, source_addr, now)
3492            .inspect_err(|&e| {
3493                self.stats.security_rejections += 1;
3494                debug!(
3495                    "PUNCH_ME_NOW frame address validation failed from peer {:?}: {:?}",
3496                    hex::encode(&from_peer[..8]),
3497                    e
3498                );
3499            })?;
3500
3501        // Comprehensive security validation
3502        self.security_validator
3503            .validate_punch_me_now_frame(frame, source_addr, from_peer, now)
3504            .inspect_err(|&e| {
3505                self.stats.security_rejections += 1;
3506                debug!(
3507                    "PUNCH_ME_NOW frame validation failed from peer {:?}: {:?}",
3508                    hex::encode(&from_peer[..8]),
3509                    e
3510                );
3511            })?;
3512
3513        // Track coordination entry minimally
3514        let _entry = self
3515            .coordination_table
3516            .entry(frame.round)
3517            .or_insert(CoordinationEntry {
3518                peer_b: frame.target_peer_id,
3519                address_hint: frame.address,
3520            });
3521        // Update target if provided later
3522        if let Some(peer_b) = frame.target_peer_id {
3523            if _entry.peer_b.is_none() {
3524                _entry.peer_b = Some(peer_b);
3525            }
3526            _entry.address_hint = frame.address;
3527        }
3528
3529        // If we have a target, echo back with swapped target to coordinate
3530        if let Some(_target_peer_id) = frame.target_peer_id {
3531            let coordination_frame = crate::frame::PunchMeNow {
3532                round: frame.round,
3533                paired_with_sequence_number: frame.paired_with_sequence_number,
3534                address: frame.address,
3535                target_peer_id: Some(from_peer),
3536            };
3537            self.stats.total_coordinations += 1;
3538            Ok(Some(coordination_frame))
3539        } else {
3540            // Response path: increment success metric
3541            self.stats.successful_coordinations += 1;
3542            Ok(None)
3543        }
3544    }
3545
3546    // Removed legacy session tracking helpers
3547    // Generate secure coordination round using cryptographically secure random values (legacy removed)
3548
3549    // Perform comprehensive security validation for coordination requests (legacy removed)
3550
3551    #[allow(dead_code)]
3552    pub(crate) fn cleanup_expired_sessions(&mut self, _now: Instant) {}
3553
3554    // Get bootstrap statistics (legacy removed)
3555
3556    // Removed peer coordination success-rate tracking and full registry
3557
3558    #[allow(dead_code)]
3559    pub(crate) fn poll_session_state_machine(&mut self, _now: Instant) -> Vec<()> {
3560        // Legacy session state machine removed
3561        Vec::new()
3562    }
3563
3564    // Check if a session should advance its state (legacy removed)
3565    // Advance session state based on event (legacy removed)
3566
3567    #[allow(dead_code)]
3568    fn cleanup_completed_sessions(&mut self, _now: Instant) {}
3569
3570    // Legacy retry mechanism removed
3571
3572    // Handle coordination errors with appropriate recovery strategies (legacy removed)
3573
3574    #[allow(dead_code)]
3575    fn estimate_peer_rtt(&self, peer_id: &PeerId) -> Option<Duration> {
3576        // Simple estimation based on peer record
3577        // In a real implementation, this would use historical RTT data
3578        let _ = peer_id;
3579        None
3580    }
3581    // Coordinate hole punching between two peers (legacy removed)
3582    // This method implemented the core coordination logic for establishing
3583    // direct P2P connections through NAT traversal.
3584
3585    // Relay coordination frame between peers (legacy removed)
3586    // This method handled the relay of coordination messages between peers
3587    // to facilitate synchronized hole punching.
3588
3589    // Implement round-based synchronization protocol (legacy removed)
3590    // This managed the timing and synchronization of hole punching rounds
3591    // to maximize the chances of successful NAT traversal.
3592
3593    // Get coordination session by ID (legacy removed)
3594
3595    // Get mutable coordination session by ID (legacy removed)
3596
3597    // Mark coordination session as successful (legacy removed)
3598
3599    // Mark coordination session as failed (legacy removed)
3600
3601    #[allow(dead_code)]
3602    pub(crate) fn get_peer_record(&self, _peer_id: PeerId) -> Option<&PeerObservationRecord> {
3603        // Legacy API kept for callers; we no longer maintain full records
3604        None
3605    }
3606}
3607
3608// Multi-destination packet transmission manager for NAT traversal
3609//
3610// This component handles simultaneous packet transmission to multiple candidate
3611// addresses during hole punching attempts, maximizing the chances of successful
3612// NAT traversal by sending packets to all viable destinations concurrently.
3613// TODO: Implement multi-path transmission infrastructure when needed
3614// This would include MultiDestinationTransmitter for sending packets to multiple
3615// destinations simultaneously for improved NAT traversal success rates.
3616// TODO: Fix nat_traversal_tests module imports
3617// #[cfg(test)]
3618// #[path = "nat_traversal_tests.rs"]
3619// mod tests;
3620
3621#[cfg(test)]
3622mod tests {
3623    use super::*;
3624
3625    // v0.13.0: Role parameter removed - all nodes are symmetric P2P nodes
3626    fn create_test_state() -> NatTraversalState {
3627        NatTraversalState::new(
3628            10,                      // max_candidates
3629            Duration::from_secs(30), // coordination_timeout
3630        )
3631    }
3632
3633    #[test]
3634    fn test_add_quic_discovered_address() {
3635        // Test that QUIC-discovered addresses are properly added as local candidates
3636        let mut state = create_test_state();
3637        let now = Instant::now();
3638
3639        // Add a QUIC-discovered address (using add_local_candidate with Observed source)
3640        let discovered_addr = SocketAddr::from(([1, 2, 3, 4], 5678));
3641        let seq = state.add_local_candidate(
3642            discovered_addr,
3643            CandidateSource::Observed { by_node: None },
3644            now,
3645        );
3646
3647        // Verify it was added correctly
3648        assert_eq!(state.local_candidates.len(), 1);
3649        let candidate = state.local_candidates.get(&seq).unwrap();
3650        assert_eq!(candidate.address, discovered_addr);
3651        assert!(matches!(candidate.source, CandidateSource::Observed { .. }));
3652        assert_eq!(candidate.state, CandidateState::New);
3653
3654        // Verify priority is set appropriately for server-reflexive
3655        assert!(candidate.priority > 0);
3656    }
3657
3658    #[test]
3659    fn test_add_multiple_quic_discovered_addresses() {
3660        // Test adding multiple QUIC-discovered addresses
3661        let mut state = create_test_state();
3662        let now = Instant::now();
3663
3664        let addrs = vec![
3665            SocketAddr::from(([1, 2, 3, 4], 5678)),
3666            SocketAddr::from(([5, 6, 7, 8], 9012)),
3667            SocketAddr::from(([2001, 0xdb8, 0, 0, 0, 0, 0, 1], 443)),
3668        ];
3669
3670        let mut sequences = Vec::new();
3671        for addr in &addrs {
3672            let seq =
3673                state.add_local_candidate(*addr, CandidateSource::Observed { by_node: None }, now);
3674            sequences.push(seq);
3675        }
3676
3677        // Verify all were added
3678        assert_eq!(state.local_candidates.len(), 3);
3679
3680        // Verify each address
3681        for (seq, addr) in sequences.iter().zip(&addrs) {
3682            let candidate = state.local_candidates.get(seq).unwrap();
3683            assert_eq!(candidate.address, *addr);
3684            assert!(matches!(candidate.source, CandidateSource::Observed { .. }));
3685        }
3686    }
3687
3688    #[test]
3689    fn test_quic_discovered_addresses_in_local_candidates() {
3690        // Test that QUIC-discovered addresses are included in local candidates
3691        let mut state = create_test_state();
3692        let now = Instant::now();
3693
3694        // Add a discovered address
3695        let addr = SocketAddr::from(([192, 168, 1, 100], 5000));
3696        let seq = state.add_local_candidate(addr, CandidateSource::Observed { by_node: None }, now);
3697
3698        // Verify it's in local candidates for advertisement
3699        assert!(state.local_candidates.contains_key(&seq));
3700        let candidate = state.local_candidates.get(&seq).unwrap();
3701        assert_eq!(candidate.address, addr);
3702
3703        // Verify it has appropriate priority for server-reflexive
3704        assert!(matches!(candidate.source, CandidateSource::Observed { .. }));
3705    }
3706
3707    #[test]
3708    fn test_quic_discovered_addresses_included_in_hole_punching() {
3709        // Test that QUIC-discovered addresses are used in hole punching
3710        let mut state = create_test_state();
3711        let now = Instant::now();
3712
3713        // Add a local discovered address
3714        let local_addr = SocketAddr::from(([192, 168, 1, 100], 5000));
3715        state.add_local_candidate(local_addr, CandidateSource::Observed { by_node: None }, now);
3716
3717        // Add a remote candidate (using valid public IP, not documentation range)
3718        let remote_addr = SocketAddr::from(([1, 2, 3, 4], 6000));
3719        let priority = VarInt::from_u32(100);
3720        state
3721            .add_remote_candidate(VarInt::from_u32(1), remote_addr, priority, now)
3722            .expect("add remote candidate should succeed");
3723
3724        // Generate candidate pairs
3725        state.generate_candidate_pairs(now);
3726
3727        // Should have one pair
3728        assert_eq!(state.candidate_pairs.len(), 1);
3729        let pair = &state.candidate_pairs[0];
3730        assert_eq!(pair.local_addr, local_addr);
3731        assert_eq!(pair.remote_addr, remote_addr);
3732    }
3733
3734    #[test]
3735    fn test_prioritize_quic_discovered_over_predicted() {
3736        // Test that QUIC-discovered addresses have higher priority than predicted
3737        let mut state = create_test_state();
3738        let now = Instant::now();
3739
3740        // Add a predicted address
3741        let predicted_addr = SocketAddr::from(([1, 2, 3, 4], 5000));
3742        let predicted_seq =
3743            state.add_local_candidate(predicted_addr, CandidateSource::Predicted, now);
3744
3745        // Add a QUIC-discovered address
3746        let discovered_addr = SocketAddr::from(([1, 2, 3, 4], 5001));
3747        let discovered_seq = state.add_local_candidate(
3748            discovered_addr,
3749            CandidateSource::Observed { by_node: None },
3750            now,
3751        );
3752
3753        // Compare priorities
3754        let predicted_priority = state.local_candidates.get(&predicted_seq).unwrap().priority;
3755        let discovered_priority = state
3756            .local_candidates
3757            .get(&discovered_seq)
3758            .unwrap()
3759            .priority;
3760
3761        // QUIC-discovered (server-reflexive) should have higher priority than predicted
3762        // Both are server-reflexive type, but observed addresses should get higher local preference
3763        assert!(discovered_priority >= predicted_priority);
3764    }
3765
3766    #[test]
3767    fn test_integration_with_nat_traversal_flow() {
3768        // Test full integration with NAT traversal flow
3769        let mut state = create_test_state();
3770        let now = Instant::now();
3771
3772        // Add both local interface and QUIC-discovered addresses
3773        let local_addr = SocketAddr::from(([192, 168, 1, 2], 5000));
3774        state.add_local_candidate(local_addr, CandidateSource::Local, now);
3775
3776        let discovered_addr = SocketAddr::from(([44, 55, 66, 77], 5000));
3777        state.add_local_candidate(
3778            discovered_addr,
3779            CandidateSource::Observed { by_node: None },
3780            now,
3781        );
3782
3783        // Add remote candidates (using valid public IPs)
3784        let remote1 = SocketAddr::from(([93, 184, 215, 123], 6000));
3785        let remote2 = SocketAddr::from(([172, 217, 16, 34], 7000));
3786        let priority = VarInt::from_u32(100);
3787        state
3788            .add_remote_candidate(VarInt::from_u32(1), remote1, priority, now)
3789            .expect("add remote candidate should succeed");
3790        state
3791            .add_remote_candidate(VarInt::from_u32(2), remote2, priority, now)
3792            .expect("add remote candidate should succeed");
3793
3794        // Generate candidate pairs
3795        state.generate_candidate_pairs(now);
3796
3797        // Should have 4 pairs (2 local × 2 remote)
3798        assert_eq!(state.candidate_pairs.len(), 4);
3799
3800        // Verify QUIC-discovered addresses are included
3801        let discovered_pairs: Vec<_> = state
3802            .candidate_pairs
3803            .iter()
3804            .filter(|p| p.local_addr == discovered_addr)
3805            .collect();
3806        assert_eq!(discovered_pairs.len(), 2);
3807    }
3808}