async_traceroute/traceroute/
probe.rs

1use std::fmt::Display;
2use std::io;
3use std::net::Ipv4Addr;
4use std::time::{Duration, Instant};
5
6pub use parser::ProbeResponseParser;
7
8pub mod parser;
9pub mod task;
10pub mod generator;
11pub mod sniffer;
12
13pub type ProbeId = String;
14
15#[derive(Clone, Debug)]
16pub struct ProbeResponse {
17    id: ProbeId,
18    from_address: Ipv4Addr,
19}
20
21impl ProbeResponse {
22    pub fn probe_id(&self) -> ProbeId {
23        self.id.clone()
24    }
25    
26    pub fn from_address(&self) -> Ipv4Addr {
27        self.from_address
28    }
29}
30
31#[derive(Clone, Debug)]
32pub struct ProbeResult {
33    id: ProbeId,
34    ttl: u8,
35    from_address: Ipv4Addr,
36    rtt: Duration,
37    hostname: Option<String>,
38}
39
40impl ProbeResult {
41    pub fn probe_id(&self) -> ProbeId {
42        self.id.clone()
43    }
44
45    pub fn from_address(&self) -> Ipv4Addr {
46        self.from_address
47    }
48    
49    pub fn rtt(&self) -> Duration {
50        self.rtt
51    }
52    
53    pub fn ttl(&self) -> u8 {
54        self.ttl
55    }
56    
57    pub fn set_hostname(&mut self, hostname: &str) {
58        self.hostname = Some(hostname.to_string());
59    }
60    pub fn get_hostname(&self) -> Option<String> {
61        self.hostname.clone()
62    }
63}
64
65impl Display for ProbeResult {
66    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67        let mut string = String::with_capacity(300);
68        let from_address = self.from_address;
69        string.push_str(&self.hostname.as_ref().unwrap_or(&from_address.to_string()));
70        string.push(' ');
71        string.push_str(&format!("({from_address})"));
72        string.push_str("  ");
73        
74        let mut rtt_micros = self.rtt.as_micros().to_string();
75        rtt_micros.insert(2, '.');
76        string.push_str(&format!("{:2} ms", rtt_micros));
77        write!(f, "{}", string)
78    }
79}
80
81#[derive(Debug)]
82pub enum ProbeError {
83    Timeout { ttl: u8 },
84    IoError { ttl: u8, io_error: Option<io::Error> },
85}
86
87impl ProbeError {
88    pub fn get_ttl(&self) -> u8 {
89        match self {
90            ProbeError::Timeout { ttl } => *ttl,
91            ProbeError::IoError { ttl, .. } => *ttl
92        }
93    }
94}
95
96struct CompletableProbe {
97    id: ProbeId,
98    ttl: u8,
99    sent_at: Instant,
100    probe_result: Option<ProbeResult>,
101}
102
103impl CompletableProbe {
104    pub fn new(id: &str, ttl: u8) -> Self {
105        Self {
106            id: id.to_string(),
107            ttl,
108            sent_at: Instant::now(),
109            probe_result: None,
110        }
111    }
112
113    pub fn complete(&mut self, probe_response: ProbeResponse) -> Option<ProbeResult> {
114        if probe_response.id != self.id {
115            return None;
116        }
117
118        if let Some(probe_result) = &self.probe_result {
119            return Some(probe_result.clone());
120        }
121
122        Some(ProbeResult {
123            id: probe_response.id,
124            ttl: self.ttl,
125            from_address: probe_response.from_address,
126            rtt: self.sent_at.elapsed(),
127            hostname: None,
128        })
129    }
130}
131
132#[derive(Debug, clap::ValueEnum, Clone, Default)]
133pub enum ProbeMethod {
134    #[default]
135    UDP,
136    TCP,
137    ICMP,
138}