1use std::{
10 collections::{HashMap, VecDeque},
11 net::{IpAddr, SocketAddr},
12 time::{Duration, Instant},
13};
14
15use tracing::{debug, info, warn};
16
17use crate::{
18 connection::nat_traversal::{CandidateSource, CandidateState, NatTraversalRole},
19 nat_traversal_api::{BootstrapNode, CandidateAddress, PeerId},
20};
21
22fn convert_to_nat_source(discovery_source: DiscoverySourceType) -> CandidateSource {
24 match discovery_source {
25 DiscoverySourceType::Local => CandidateSource::Local,
26 DiscoverySourceType::ServerReflexive => CandidateSource::Observed { by_node: None },
27 DiscoverySourceType::Predicted => CandidateSource::Predicted,
28 }
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub enum DiscoverySourceType {
34 Local,
35 ServerReflexive,
36 Predicted,
37}
38
39#[derive(Debug, Clone)]
41pub struct DiscoveryCandidate {
42 pub address: SocketAddr,
43 pub priority: u32,
44 pub source: DiscoverySourceType,
45 pub state: CandidateState,
46}
47
48impl DiscoveryCandidate {
49 pub fn to_candidate_address(&self) -> CandidateAddress {
51 CandidateAddress {
52 address: self.address,
53 priority: self.priority,
54 source: convert_to_nat_source(self.source),
55 state: self.state,
56 }
57 }
58}
59
60pub struct CandidateDiscoveryManager {
62 current_phase: DiscoveryPhase,
64 config: DiscoveryConfig,
66 interface_discovery: Box<dyn NetworkInterfaceDiscovery + Send>,
68 server_reflexive_discovery: ServerReflexiveDiscovery,
70 symmetric_predictor: SymmetricNatPredictor,
72 bootstrap_manager: BootstrapNodeManager,
74 cache: DiscoveryCache,
76 session_state: DiscoverySessionState,
78}
79
80#[derive(Debug, Clone)]
82pub struct DiscoveryConfig {
83 pub total_timeout: Duration,
85 pub local_scan_timeout: Duration,
87 pub bootstrap_query_timeout: Duration,
89 pub max_candidates: usize,
91 pub enable_symmetric_prediction: bool,
93 pub min_bootstrap_consensus: usize,
95 pub interface_cache_ttl: Duration,
97 pub server_reflexive_cache_ttl: Duration,
99}
100
101#[derive(Debug, Clone, PartialEq)]
103pub enum DiscoveryPhase {
104 Idle,
106 LocalInterfaceScanning {
108 started_at: Instant,
109 },
110 ServerReflexiveQuerying {
112 started_at: Instant,
113 active_queries: HashMap<BootstrapNodeId, QueryState>,
114 responses_received: Vec<ServerReflexiveResponse>,
115 },
116 SymmetricNatPrediction {
118 started_at: Instant,
119 prediction_attempts: u32,
120 pattern_analysis: PatternAnalysisState,
121 },
122 CandidateValidation {
124 started_at: Instant,
125 validation_results: HashMap<CandidateId, ValidationResult>,
126 },
127 Completed {
129 final_candidates: Vec<ValidatedCandidate>,
130 completion_time: Instant,
131 },
132 Failed {
134 error: DiscoveryError,
135 failed_at: Instant,
136 fallback_options: Vec<FallbackStrategy>,
137 },
138}
139
140#[derive(Debug, Clone)]
142pub enum DiscoveryEvent {
143 DiscoveryStarted {
145 peer_id: PeerId,
146 bootstrap_count: usize,
147 },
148 LocalScanningStarted,
150 LocalCandidateDiscovered {
152 candidate: CandidateAddress,
153 },
154 LocalScanningCompleted {
156 candidate_count: usize,
157 duration: Duration,
158 },
159 ServerReflexiveDiscoveryStarted {
161 bootstrap_count: usize,
162 },
163 ServerReflexiveCandidateDiscovered {
165 candidate: CandidateAddress,
166 bootstrap_node: SocketAddr,
167 },
168 BootstrapQueryFailed {
170 bootstrap_node: SocketAddr,
171 error: String,
172 },
173 SymmetricPredictionStarted {
175 base_address: SocketAddr,
176 },
177 PredictedCandidateGenerated {
179 candidate: CandidateAddress,
180 confidence: f64,
181 },
182 DiscoveryCompleted {
184 candidate_count: usize,
185 total_duration: Duration,
186 success_rate: f64,
187 },
188 DiscoveryFailed {
190 error: DiscoveryError,
191 partial_results: Vec<CandidateAddress>,
192 },
193}
194
195#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
197pub struct BootstrapNodeId(pub u64);
198
199#[derive(Debug, Clone, PartialEq, Eq)]
201pub struct QueryState {
202 pub started_at: Instant,
203 pub timeout: Duration,
204 pub retry_count: u32,
205 pub last_error: Option<String>,
206}
207
208#[derive(Debug, Clone, PartialEq)]
210pub struct ServerReflexiveResponse {
211 pub bootstrap_node: BootstrapNodeId,
212 pub observed_address: SocketAddr,
213 pub server_address: SocketAddr,
214 pub response_time: Duration,
215 pub reliability_score: f64,
216}
217
218#[derive(Debug, Clone, PartialEq)]
220pub struct PatternAnalysisState {
221 pub allocation_history: VecDeque<PortAllocationEvent>,
222 pub detected_pattern: Option<PortAllocationPattern>,
223 pub confidence_level: f64,
224 pub prediction_accuracy: f64,
225}
226
227#[derive(Debug, Clone, PartialEq)]
229pub struct PortAllocationEvent {
230 pub port: u16,
231 pub timestamp: Instant,
232 pub source_address: SocketAddr,
233}
234
235#[derive(Debug, Clone, PartialEq)]
237pub struct PortAllocationPattern {
238 pub pattern_type: AllocationPatternType,
239 pub base_port: u16,
240 pub stride: u16,
241 pub pool_boundaries: Option<(u16, u16)>,
242 pub confidence: f64,
243}
244
245#[derive(Debug, Clone, PartialEq, Eq)]
247pub enum AllocationPatternType {
248 Sequential,
250 FixedStride,
252 Random,
254 PoolBased,
256 TimeBased,
258 Unknown,
260}
261
262#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
264pub struct CandidateId(pub u64);
265
266#[derive(Debug, Clone, PartialEq)]
268pub enum ValidationResult {
269 Valid { rtt: Duration },
270 Invalid { reason: String },
271 Timeout,
272 Pending,
273}
274
275#[derive(Debug, Clone, PartialEq)]
277pub struct ValidatedCandidate {
278 pub id: CandidateId,
279 pub address: SocketAddr,
280 pub source: DiscoverySourceType,
281 pub priority: u32,
282 pub rtt: Option<Duration>,
283 pub reliability_score: f64,
284}
285
286impl ValidatedCandidate {
287 pub fn to_candidate_address(&self) -> CandidateAddress {
289 CandidateAddress {
290 address: self.address,
291 priority: self.priority,
292 source: convert_to_nat_source(self.source),
293 state: CandidateState::Valid,
294 }
295 }
296}
297
298#[derive(Debug)]
300pub struct DiscoverySessionState {
301 pub peer_id: PeerId,
302 pub session_id: u64,
303 pub started_at: Instant,
304 pub discovered_candidates: Vec<DiscoveryCandidate>,
305 pub statistics: DiscoveryStatistics,
306}
307
308#[derive(Debug, Default, Clone)]
310pub struct DiscoveryStatistics {
311 pub local_candidates_found: u32,
312 pub server_reflexive_candidates_found: u32,
313 pub predicted_candidates_generated: u32,
314 pub bootstrap_queries_sent: u32,
315 pub bootstrap_queries_successful: u32,
316 pub total_discovery_time: Option<Duration>,
317 pub average_bootstrap_rtt: Option<Duration>,
318}
319
320#[derive(Debug, Clone, PartialEq, Eq)]
322pub enum DiscoveryError {
323 NoLocalInterfaces,
325 AllBootstrapsFailed,
327 DiscoveryTimeout,
329 InsufficientCandidates { found: usize, required: usize },
331 NetworkError(String),
333 ConfigurationError(String),
335 InternalError(String),
337}
338
339#[derive(Debug, Clone, PartialEq, Eq)]
341pub enum FallbackStrategy {
342 UseCachedResults,
344 RetryWithRelaxedParams,
346 UseMinimalCandidates,
348 EnableRelayFallback,
350}
351
352impl Default for DiscoveryConfig {
353 fn default() -> Self {
354 Self {
355 total_timeout: Duration::from_secs(30),
356 local_scan_timeout: Duration::from_secs(2),
357 bootstrap_query_timeout: Duration::from_secs(5),
358 max_candidates: 8,
359 enable_symmetric_prediction: true,
360 min_bootstrap_consensus: 2,
361 interface_cache_ttl: Duration::from_secs(60),
362 server_reflexive_cache_ttl: Duration::from_secs(300),
363 }
364 }
365}
366
367impl CandidateDiscoveryManager {
368 pub fn new(config: DiscoveryConfig, _role: NatTraversalRole) -> Self {
370 let interface_discovery = create_platform_interface_discovery();
371 let server_reflexive_discovery = ServerReflexiveDiscovery::new(&config);
372 let symmetric_predictor = SymmetricNatPredictor::new(&config);
373 let bootstrap_manager = BootstrapNodeManager::new(&config);
374 let cache = DiscoveryCache::new(&config);
375
376 Self {
377 current_phase: DiscoveryPhase::Idle,
378 config,
379 interface_discovery,
380 server_reflexive_discovery,
381 symmetric_predictor,
382 bootstrap_manager,
383 cache,
384 session_state: DiscoverySessionState {
385 peer_id: PeerId([0; 32]), session_id: 0,
387 started_at: Instant::now(),
388 discovered_candidates: Vec::new(),
389 statistics: DiscoveryStatistics::default(),
390 },
391 }
392 }
393
394 pub fn start_discovery(&mut self, peer_id: PeerId, bootstrap_nodes: Vec<BootstrapNode>) -> Result<(), DiscoveryError> {
396 if !matches!(self.current_phase, DiscoveryPhase::Idle | DiscoveryPhase::Failed { .. } | DiscoveryPhase::Completed { .. }) {
397 return Err(DiscoveryError::InternalError("Discovery already in progress".to_string()));
398 }
399
400 info!("Starting candidate discovery for peer {:?}", peer_id);
401
402 self.session_state.peer_id = peer_id;
404 self.session_state.session_id = rand::random();
405 self.session_state.started_at = Instant::now();
406 self.session_state.discovered_candidates.clear();
407 self.session_state.statistics = DiscoveryStatistics::default();
408
409 self.bootstrap_manager.update_bootstrap_nodes(bootstrap_nodes);
411
412 self.current_phase = DiscoveryPhase::LocalInterfaceScanning {
414 started_at: Instant::now(),
415 };
416
417 Ok(())
418 }
419
420 pub fn poll(&mut self, now: Instant) -> Vec<DiscoveryEvent> {
422 let mut events = Vec::new();
423
424 if self.session_state.started_at.elapsed() > self.config.total_timeout {
426 self.handle_discovery_timeout(&mut events, now);
427 return events;
428 }
429
430 match &self.current_phase.clone() {
431 DiscoveryPhase::Idle => {
432 },
434
435 DiscoveryPhase::LocalInterfaceScanning { started_at } => {
436 self.poll_local_interface_scanning(*started_at, now, &mut events);
437 },
438
439 DiscoveryPhase::ServerReflexiveQuerying { started_at, active_queries, responses_received } => {
440 self.poll_server_reflexive_discovery(*started_at, active_queries, responses_received, now, &mut events);
441 },
442
443 DiscoveryPhase::SymmetricNatPrediction { started_at, prediction_attempts, pattern_analysis } => {
444 self.poll_symmetric_prediction(*started_at, *prediction_attempts, pattern_analysis, now, &mut events);
445 },
446
447 DiscoveryPhase::CandidateValidation { started_at, validation_results } => {
448 self.poll_candidate_validation(*started_at, validation_results, now, &mut events);
449 },
450
451 DiscoveryPhase::Completed { .. } | DiscoveryPhase::Failed { .. } => {
452 },
454 }
455
456 events
457 }
458
459 pub fn get_status(&self) -> DiscoveryStatus {
461 DiscoveryStatus {
462 phase: self.current_phase.clone(),
463 discovered_candidates: self.session_state.discovered_candidates.iter()
464 .map(|c| c.to_candidate_address())
465 .collect(),
466 statistics: self.session_state.statistics.clone(),
467 elapsed_time: self.session_state.started_at.elapsed(),
468 }
469 }
470
471 pub fn is_complete(&self) -> bool {
473 matches!(self.current_phase, DiscoveryPhase::Completed { .. } | DiscoveryPhase::Failed { .. })
474 }
475
476 pub fn get_results(&self) -> Option<DiscoveryResults> {
478 match &self.current_phase {
479 DiscoveryPhase::Completed { final_candidates, completion_time } => {
480 Some(DiscoveryResults {
481 candidates: final_candidates.clone(),
482 completion_time: *completion_time,
483 statistics: self.session_state.statistics.clone(),
484 })
485 },
486 DiscoveryPhase::Failed { .. } => {
487 Some(DiscoveryResults {
488 candidates: Vec::new(),
489 completion_time: Instant::now(),
490 statistics: self.session_state.statistics.clone(),
491 })
492 },
493 _ => None,
494 }
495 }
496
497 fn poll_local_interface_scanning(&mut self, started_at: Instant, now: Instant, events: &mut Vec<DiscoveryEvent>) {
500 if started_at.elapsed() > self.config.local_scan_timeout {
502 warn!("Local interface scanning timeout");
503 self.handle_local_scan_timeout(events, now);
504 return;
505 }
506
507 if let Some(interfaces) = self.interface_discovery.check_scan_complete() {
509 self.process_local_interfaces(interfaces, events, now);
510 }
511 }
512
513 fn process_local_interfaces(&mut self, interfaces: Vec<NetworkInterface>, events: &mut Vec<DiscoveryEvent>, now: Instant) {
514 debug!("Processing {} network interfaces", interfaces.len());
515
516 for interface in interfaces {
517 for address in &interface.addresses {
518 if self.is_valid_local_address(&address) {
519 let candidate = DiscoveryCandidate {
520 address: *address,
521 priority: self.calculate_local_priority(address, &interface),
522 source: DiscoverySourceType::Local,
523 state: CandidateState::New,
524 };
525
526 self.session_state.discovered_candidates.push(candidate.clone());
527 self.session_state.statistics.local_candidates_found += 1;
528
529 events.push(DiscoveryEvent::LocalCandidateDiscovered {
530 candidate: candidate.to_candidate_address()
531 });
532 }
533 }
534 }
535
536 events.push(DiscoveryEvent::LocalScanningCompleted {
537 candidate_count: self.session_state.statistics.local_candidates_found as usize,
538 duration: now.duration_since(self.session_state.started_at),
539 });
540
541 self.start_server_reflexive_discovery(events, now);
543 }
544
545 fn start_server_reflexive_discovery(&mut self, events: &mut Vec<DiscoveryEvent>, now: Instant) {
546 let bootstrap_nodes = self.bootstrap_manager.get_active_bootstrap_nodes();
547
548 if bootstrap_nodes.is_empty() {
549 warn!("No bootstrap nodes available for server reflexive discovery");
550 self.handle_no_bootstrap_nodes(events, now);
551 return;
552 }
553
554 let active_queries = self.server_reflexive_discovery.start_queries(&bootstrap_nodes, now);
555
556 events.push(DiscoveryEvent::ServerReflexiveDiscoveryStarted {
557 bootstrap_count: bootstrap_nodes.len(),
558 });
559
560 self.current_phase = DiscoveryPhase::ServerReflexiveQuerying {
561 started_at: now,
562 active_queries,
563 responses_received: Vec::new(),
564 };
565 }
566
567 fn poll_server_reflexive_discovery(
568 &mut self,
569 started_at: Instant,
570 active_queries: &HashMap<BootstrapNodeId, QueryState>,
571 responses_received: &Vec<ServerReflexiveResponse>,
572 now: Instant,
573 events: &mut Vec<DiscoveryEvent>
574 ) {
575 let new_responses = self.server_reflexive_discovery.poll_queries(active_queries, now);
577
578 let mut updated_responses = responses_received.clone();
579 for response in new_responses {
580 self.process_server_reflexive_response(&response, events);
581 updated_responses.push(response);
582 }
583
584 if self.should_transition_to_prediction(&updated_responses, now) {
586 self.start_symmetric_prediction(&updated_responses, events, now);
587 } else if started_at.elapsed() > self.config.bootstrap_query_timeout * 2 {
588 if updated_responses.len() >= self.config.min_bootstrap_consensus {
590 self.start_symmetric_prediction(&updated_responses, events, now);
591 } else {
592 self.handle_insufficient_bootstrap_responses(events, now);
593 }
594 } else {
595 self.current_phase = DiscoveryPhase::ServerReflexiveQuerying {
597 started_at,
598 active_queries: active_queries.clone(),
599 responses_received: updated_responses,
600 };
601 }
602 }
603
604 fn process_server_reflexive_response(&mut self, response: &ServerReflexiveResponse, events: &mut Vec<DiscoveryEvent>) {
605 debug!("Received server reflexive response: {:?}", response);
606
607 let candidate = DiscoveryCandidate {
608 address: response.observed_address,
609 priority: self.calculate_server_reflexive_priority(response),
610 source: DiscoverySourceType::ServerReflexive,
611 state: CandidateState::New,
612 };
613
614 self.session_state.discovered_candidates.push(candidate.clone());
615 self.session_state.statistics.server_reflexive_candidates_found += 1;
616
617 events.push(DiscoveryEvent::ServerReflexiveCandidateDiscovered {
618 candidate: candidate.to_candidate_address(),
619 bootstrap_node: self.bootstrap_manager.get_bootstrap_address(response.bootstrap_node).unwrap_or_else(|| "unknown".parse().unwrap()),
620 });
621 }
622
623 fn start_symmetric_prediction(&mut self, responses: &[ServerReflexiveResponse], events: &mut Vec<DiscoveryEvent>, now: Instant) {
624 if !self.config.enable_symmetric_prediction || responses.is_empty() {
625 self.start_candidate_validation(events, now);
626 return;
627 }
628
629 let base_address = self.calculate_consensus_address(responses);
631
632 events.push(DiscoveryEvent::SymmetricPredictionStarted { base_address });
633
634 self.current_phase = DiscoveryPhase::SymmetricNatPrediction {
635 started_at: now,
636 prediction_attempts: 0,
637 pattern_analysis: PatternAnalysisState {
638 allocation_history: VecDeque::new(),
639 detected_pattern: None,
640 confidence_level: 0.0,
641 prediction_accuracy: 0.0,
642 },
643 };
644 }
645
646 fn poll_symmetric_prediction(
647 &mut self,
648 _started_at: Instant,
649 _prediction_attempts: u32,
650 pattern_analysis: &PatternAnalysisState,
651 now: Instant,
652 events: &mut Vec<DiscoveryEvent>
653 ) {
654 let predicted_candidates = self.symmetric_predictor.generate_predictions(pattern_analysis, self.config.max_candidates - self.session_state.discovered_candidates.len());
656
657 for candidate in predicted_candidates {
658 self.session_state.discovered_candidates.push(candidate.clone());
659 self.session_state.statistics.predicted_candidates_generated += 1;
660
661 events.push(DiscoveryEvent::PredictedCandidateGenerated {
662 candidate: candidate.to_candidate_address(),
663 confidence: pattern_analysis.confidence_level,
664 });
665 }
666
667 self.start_candidate_validation(events, now);
669 }
670
671 fn start_candidate_validation(&mut self, _events: &mut Vec<DiscoveryEvent>, now: Instant) {
672 debug!("Starting candidate validation for {} candidates", self.session_state.discovered_candidates.len());
673
674 self.current_phase = DiscoveryPhase::CandidateValidation {
675 started_at: now,
676 validation_results: HashMap::new(),
677 };
678 }
679
680 fn poll_candidate_validation(
681 &mut self,
682 _started_at: Instant,
683 _validation_results: &HashMap<CandidateId, ValidationResult>,
684 now: Instant,
685 events: &mut Vec<DiscoveryEvent>
686 ) {
687 let validated_candidates: Vec<ValidatedCandidate> = self.session_state.discovered_candidates
689 .iter()
690 .enumerate()
691 .map(|(i, candidate)| ValidatedCandidate {
692 id: CandidateId(i as u64),
693 address: candidate.address,
694 source: candidate.source,
695 priority: candidate.priority,
696 rtt: Some(Duration::from_millis(50)), reliability_score: 0.8, })
699 .collect();
700
701 self.complete_discovery(validated_candidates, events, now);
702 }
703
704 fn complete_discovery(&mut self, candidates: Vec<ValidatedCandidate>, events: &mut Vec<DiscoveryEvent>, now: Instant) {
705 let total_duration = now.duration_since(self.session_state.started_at);
706 self.session_state.statistics.total_discovery_time = Some(total_duration);
707
708 let success_rate = if self.session_state.statistics.bootstrap_queries_sent > 0 {
709 self.session_state.statistics.bootstrap_queries_successful as f64 / self.session_state.statistics.bootstrap_queries_sent as f64
710 } else {
711 1.0
712 };
713
714 events.push(DiscoveryEvent::DiscoveryCompleted {
715 candidate_count: candidates.len(),
716 total_duration,
717 success_rate,
718 });
719
720 self.current_phase = DiscoveryPhase::Completed {
721 final_candidates: candidates,
722 completion_time: now,
723 };
724
725 info!("Candidate discovery completed successfully in {:?}", total_duration);
726 }
727
728 fn handle_discovery_timeout(&mut self, events: &mut Vec<DiscoveryEvent>, now: Instant) {
731 let error = DiscoveryError::DiscoveryTimeout;
732 events.push(DiscoveryEvent::DiscoveryFailed {
733 error: error.clone(),
734 partial_results: self.session_state.discovered_candidates.iter()
735 .map(|c| c.to_candidate_address())
736 .collect(),
737 });
738
739 self.current_phase = DiscoveryPhase::Failed {
740 error,
741 failed_at: now,
742 fallback_options: vec![FallbackStrategy::UseCachedResults, FallbackStrategy::UseMinimalCandidates],
743 };
744 }
745
746 fn handle_local_scan_timeout(&mut self, events: &mut Vec<DiscoveryEvent>, now: Instant) {
747 warn!("Local interface scan timeout, proceeding with available candidates");
748
749 events.push(DiscoveryEvent::LocalScanningCompleted {
750 candidate_count: self.session_state.statistics.local_candidates_found as usize,
751 duration: now.duration_since(self.session_state.started_at),
752 });
753
754 self.start_server_reflexive_discovery(events, now);
755 }
756
757 fn handle_no_bootstrap_nodes(&mut self, events: &mut Vec<DiscoveryEvent>, now: Instant) {
758 let error = DiscoveryError::AllBootstrapsFailed;
759 events.push(DiscoveryEvent::DiscoveryFailed {
760 error: error.clone(),
761 partial_results: self.session_state.discovered_candidates.iter()
762 .map(|c| c.to_candidate_address())
763 .collect(),
764 });
765
766 self.current_phase = DiscoveryPhase::Failed {
767 error,
768 failed_at: now,
769 fallback_options: vec![FallbackStrategy::UseMinimalCandidates],
770 };
771 }
772
773 fn handle_insufficient_bootstrap_responses(&mut self, events: &mut Vec<DiscoveryEvent>, now: Instant) {
774 warn!("Insufficient bootstrap responses, proceeding with available data");
775 self.start_candidate_validation(events, now);
776 }
777
778 fn is_valid_local_address(&self, address: &SocketAddr) -> bool {
779 match address.ip() {
780 IpAddr::V4(ipv4) => !ipv4.is_loopback() && !ipv4.is_unspecified(),
781 IpAddr::V6(ipv6) => !ipv6.is_loopback() && !ipv6.is_unspecified(),
782 }
783 }
784
785 fn calculate_local_priority(&self, address: &SocketAddr, interface: &NetworkInterface) -> u32 {
786 let mut priority = 100; match address.ip() {
789 IpAddr::V4(ipv4) => {
790 if ipv4.is_private() {
791 priority += 50; }
793 },
794 IpAddr::V6(_) => {
795 priority += 30; },
797 }
798
799 if interface.is_wireless {
800 priority -= 10; }
802
803 priority
804 }
805
806 fn calculate_server_reflexive_priority(&self, response: &ServerReflexiveResponse) -> u32 {
807 let mut priority = 200; if response.response_time < Duration::from_millis(50) {
811 priority += 20;
812 } else if response.response_time > Duration::from_millis(200) {
813 priority -= 10;
814 }
815
816 priority += (response.reliability_score * 50.0) as u32;
818
819 priority
820 }
821
822 fn should_transition_to_prediction(&self, responses: &[ServerReflexiveResponse], _now: Instant) -> bool {
823 responses.len() >= self.config.min_bootstrap_consensus.max(1)
824 }
825
826 fn calculate_consensus_address(&self, responses: &[ServerReflexiveResponse]) -> SocketAddr {
827 let mut address_counts: HashMap<SocketAddr, usize> = HashMap::new();
829
830 for response in responses {
831 *address_counts.entry(response.observed_address).or_insert(0) += 1;
832 }
833
834 address_counts
835 .into_iter()
836 .max_by_key(|(_, count)| *count)
837 .map(|(addr, _)| addr)
838 .unwrap_or_else(|| "0.0.0.0:0".parse().unwrap())
839 }
840}
841
842#[derive(Debug, Clone)]
844pub struct DiscoveryStatus {
845 pub phase: DiscoveryPhase,
846 pub discovered_candidates: Vec<CandidateAddress>,
847 pub statistics: DiscoveryStatistics,
848 pub elapsed_time: Duration,
849}
850
851#[derive(Debug, Clone)]
853pub struct DiscoveryResults {
854 pub candidates: Vec<ValidatedCandidate>,
855 pub completion_time: Instant,
856 pub statistics: DiscoveryStatistics,
857}
858
859pub trait NetworkInterfaceDiscovery {
863 fn start_scan(&mut self) -> Result<(), String>;
864 fn check_scan_complete(&mut self) -> Option<Vec<NetworkInterface>>;
865}
866
867#[derive(Debug, Clone)]
869pub struct NetworkInterface {
870 pub name: String,
871 pub addresses: Vec<SocketAddr>,
872 pub is_up: bool,
873 pub is_wireless: bool,
874 pub mtu: Option<u16>,
875}
876
877#[derive(Debug)]
879pub struct ServerReflexiveDiscovery {
880 config: DiscoveryConfig,
881}
882
883impl ServerReflexiveDiscovery {
884 pub fn new(config: &DiscoveryConfig) -> Self {
885 Self {
886 config: config.clone(),
887 }
888 }
889
890 pub fn start_queries(&mut self, _bootstrap_nodes: &[BootstrapNodeId], _now: Instant) -> HashMap<BootstrapNodeId, QueryState> {
891 HashMap::new()
893 }
894
895 pub fn poll_queries(&mut self, _active_queries: &HashMap<BootstrapNodeId, QueryState>, _now: Instant) -> Vec<ServerReflexiveResponse> {
896 Vec::new()
898 }
899}
900
901#[derive(Debug)]
903pub struct SymmetricNatPredictor {
904 config: DiscoveryConfig,
905}
906
907impl SymmetricNatPredictor {
908 pub fn new(config: &DiscoveryConfig) -> Self {
909 Self {
910 config: config.clone(),
911 }
912 }
913
914 pub fn generate_predictions(&mut self, _pattern_analysis: &PatternAnalysisState, _max_count: usize) -> Vec<DiscoveryCandidate> {
915 Vec::new()
917 }
918}
919
920#[derive(Debug)]
922pub struct BootstrapNodeManager {
923 config: DiscoveryConfig,
924 bootstrap_nodes: HashMap<BootstrapNodeId, BootstrapNode>,
925}
926
927impl BootstrapNodeManager {
928 pub fn new(config: &DiscoveryConfig) -> Self {
929 Self {
930 config: config.clone(),
931 bootstrap_nodes: HashMap::new(),
932 }
933 }
934
935 pub fn update_bootstrap_nodes(&mut self, nodes: Vec<BootstrapNode>) {
936 self.bootstrap_nodes.clear();
938 for (i, node) in nodes.into_iter().enumerate() {
939 self.bootstrap_nodes.insert(BootstrapNodeId(i as u64), node);
940 }
941 }
942
943 pub fn get_active_bootstrap_nodes(&self) -> Vec<BootstrapNodeId> {
944 self.bootstrap_nodes.keys().copied().collect()
945 }
946
947 pub fn get_bootstrap_address(&self, id: BootstrapNodeId) -> Option<SocketAddr> {
948 self.bootstrap_nodes.get(&id).map(|node| node.address)
949 }
950}
951
952#[derive(Debug)]
954pub struct DiscoveryCache {
955 config: DiscoveryConfig,
956}
957
958impl DiscoveryCache {
959 pub fn new(config: &DiscoveryConfig) -> Self {
960 Self {
961 config: config.clone(),
962 }
963 }
964}
965
966pub fn create_platform_interface_discovery() -> Box<dyn NetworkInterfaceDiscovery + Send> {
968 #[cfg(target_os = "windows")]
969 return Box::new(WindowsInterfaceDiscovery::new());
970
971 #[cfg(target_os = "linux")]
972 return Box::new(LinuxInterfaceDiscovery::new());
973
974 #[cfg(target_os = "macos")]
975 return Box::new(MacOSInterfaceDiscovery::new());
976
977 #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
978 return Box::new(GenericInterfaceDiscovery::new());
979}
980
981#[cfg(target_os = "windows")]
984pub struct WindowsInterfaceDiscovery {
985 scan_complete: bool,
986}
987
988#[cfg(target_os = "windows")]
989impl WindowsInterfaceDiscovery {
990 pub fn new() -> Self {
991 Self { scan_complete: false }
992 }
993}
994
995#[cfg(target_os = "windows")]
996impl NetworkInterfaceDiscovery for WindowsInterfaceDiscovery {
997 fn start_scan(&mut self) -> Result<(), String> {
998 self.scan_complete = true;
1000 Ok(())
1001 }
1002
1003 fn check_scan_complete(&mut self) -> Option<Vec<NetworkInterface>> {
1004 if self.scan_complete {
1005 self.scan_complete = false;
1006 Some(vec![
1007 NetworkInterface {
1008 name: "Local Area Connection".to_string(),
1009 addresses: vec!["192.168.1.100:0".parse().unwrap()],
1010 is_up: true,
1011 is_wireless: false,
1012 mtu: Some(1500),
1013 }
1014 ])
1015 } else {
1016 None
1017 }
1018 }
1019}
1020
1021#[cfg(target_os = "linux")]
1022pub struct LinuxInterfaceDiscovery {
1023 scan_complete: bool,
1024}
1025
1026#[cfg(target_os = "linux")]
1027impl LinuxInterfaceDiscovery {
1028 pub fn new() -> Self {
1029 Self { scan_complete: false }
1030 }
1031}
1032
1033#[cfg(target_os = "linux")]
1034impl NetworkInterfaceDiscovery for LinuxInterfaceDiscovery {
1035 fn start_scan(&mut self) -> Result<(), String> {
1036 self.scan_complete = true;
1038 Ok(())
1039 }
1040
1041 fn check_scan_complete(&mut self) -> Option<Vec<NetworkInterface>> {
1042 if self.scan_complete {
1043 self.scan_complete = false;
1044 Some(vec![
1045 NetworkInterface {
1046 name: "eth0".to_string(),
1047 addresses: vec!["192.168.1.100:0".parse().unwrap()],
1048 is_up: true,
1049 is_wireless: false,
1050 mtu: Some(1500),
1051 }
1052 ])
1053 } else {
1054 None
1055 }
1056 }
1057}
1058
1059#[cfg(target_os = "macos")]
1060pub struct MacOSInterfaceDiscovery {
1061 scan_complete: bool,
1062}
1063
1064#[cfg(target_os = "macos")]
1065impl MacOSInterfaceDiscovery {
1066 pub fn new() -> Self {
1067 Self { scan_complete: false }
1068 }
1069}
1070
1071#[cfg(target_os = "macos")]
1072impl NetworkInterfaceDiscovery for MacOSInterfaceDiscovery {
1073 fn start_scan(&mut self) -> Result<(), String> {
1074 self.scan_complete = true;
1076 Ok(())
1077 }
1078
1079 fn check_scan_complete(&mut self) -> Option<Vec<NetworkInterface>> {
1080 if self.scan_complete {
1081 self.scan_complete = false;
1082 Some(vec![
1083 NetworkInterface {
1084 name: "en0".to_string(),
1085 addresses: vec!["192.168.1.100:0".parse().unwrap()],
1086 is_up: true,
1087 is_wireless: true,
1088 mtu: Some(1500),
1089 }
1090 ])
1091 } else {
1092 None
1093 }
1094 }
1095}
1096
1097pub struct GenericInterfaceDiscovery {
1099 scan_complete: bool,
1100}
1101
1102impl GenericInterfaceDiscovery {
1103 pub fn new() -> Self {
1104 Self { scan_complete: false }
1105 }
1106}
1107
1108impl NetworkInterfaceDiscovery for GenericInterfaceDiscovery {
1109 fn start_scan(&mut self) -> Result<(), String> {
1110 self.scan_complete = true;
1112 Ok(())
1113 }
1114
1115 fn check_scan_complete(&mut self) -> Option<Vec<NetworkInterface>> {
1116 if self.scan_complete {
1117 self.scan_complete = false;
1118 Some(vec![
1119 NetworkInterface {
1120 name: "generic".to_string(),
1121 addresses: vec!["127.0.0.1:0".parse().unwrap()],
1122 is_up: true,
1123 is_wireless: false,
1124 mtu: Some(1500),
1125 }
1126 ])
1127 } else {
1128 None
1129 }
1130 }
1131}
1132
1133impl std::fmt::Display for DiscoveryError {
1134 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1135 match self {
1136 Self::NoLocalInterfaces => write!(f, "no local network interfaces found"),
1137 Self::AllBootstrapsFailed => write!(f, "all bootstrap node queries failed"),
1138 Self::DiscoveryTimeout => write!(f, "discovery process timed out"),
1139 Self::InsufficientCandidates { found, required } => write!(f, "insufficient candidates found: {} < {}", found, required),
1140 Self::NetworkError(msg) => write!(f, "network error: {}", msg),
1141 Self::ConfigurationError(msg) => write!(f, "configuration error: {}", msg),
1142 Self::InternalError(msg) => write!(f, "internal error: {}", msg),
1143 }
1144 }
1145}
1146
1147impl std::error::Error for DiscoveryError {}