use std::net::{IpAddr, SocketAddr};
use std::time::Duration;
use serde::{Deserialize, Serialize};
use crate::connection::{ConnectionId, Family};
use crate::diag::Finding;
use crate::dns::{DnsError, DnsResolution};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Path {
pub target: Target,
pub resolution: Option<DnsResolution>,
pub egress: Egress,
pub probes: ProbeResults,
pub verdict: Verdict,
pub findings: Vec<Finding>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Target {
Ip {
ip: IpAddr,
port: Option<u16>,
},
Host {
name: String,
port: Option<u16>,
},
Url {
url: String,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ProbeStrategy {
LanIp,
IcmpOnly,
UnspecifiedTcp,
SpecificPort,
HttpUrl,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Egress {
pub connection_id: ConnectionId,
pub iface: String,
pub src: IpAddr,
pub gateway: Option<IpAddr>,
pub family_used: Family,
pub family_unreachable: Vec<Family>,
pub uid_scoped: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProbeResults {
pub strategy: ProbeStrategy,
pub gateway_ping: Option<PingResult>,
pub target_ping: Option<PingResult>,
pub tcp_connect: Option<TcpProbeResult>,
pub tls_handshake: Option<TlsProbeResult>,
pub http_head: Option<HttpProbeResult>,
pub trace: Option<Vec<Hop>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PingResult {
pub sent: u32,
pub received: u32,
pub rtt_min: Option<Duration>,
pub rtt_avg: Option<Duration>,
pub rtt_max: Option<Duration>,
}
impl PingResult {
pub fn loss_pct(&self) -> f32 {
if self.sent == 0 {
return 0.0;
}
let lost = self.sent.saturating_sub(self.received);
(lost as f32 / self.sent as f32) * 100.0
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TcpProbeResult {
pub addr: SocketAddr,
pub connected: bool,
pub took: Duration,
pub error: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TlsProbeResult {
pub peer: SocketAddr,
pub sni: String,
pub negotiated: bool,
pub took: Duration,
pub error: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct HttpProbeResult {
pub url: String,
pub status: Option<u16>,
pub took: Duration,
pub error: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Hop {
pub ttl: u8,
pub ip: Option<IpAddr>,
pub rtt: Option<Duration>,
pub hostname: Option<String>,
}
impl Eq for Hop {}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Verdict {
Reachable {
latency_ms: u64,
family_used: Family,
},
PartiallyReachable {
working: Family,
broken: Family,
},
DnsFailed {
error: DnsError,
},
NoEgress {
reason: String,
},
GatewayDown {
gateway: IpAddr,
},
PacketLoss {
loss_pct: f32,
},
TcpRefused {
addr: SocketAddr,
},
TcpTimeout {
addr: SocketAddr,
},
TlsFailed {
err: String,
},
HttpFailed {
status: u16,
},
}
impl Eq for Verdict {}