mirror of
https://github.com/telemt/telemt.git
synced 2026-09-15 15:04:15 +03:00
Split oversized runtime modules + Async tests hardened
Co-Authored-By: brekotis <93345790+brekotis@users.noreply.github.com>
This commit is contained in:
+10
-184
@@ -70,9 +70,7 @@ pub async fn run_probe(
|
||||
) -> Result<NetworkProbe> {
|
||||
let mut probe = NetworkProbe::default();
|
||||
let dns_resolver = Arc::new(
|
||||
crate::network::dns_overrides::GenerationDnsResolver::from_entries(
|
||||
&config.dns_overrides,
|
||||
)?,
|
||||
crate::network::dns_overrides::GenerationDnsResolver::from_entries(&config.dns_overrides)?,
|
||||
);
|
||||
let servers = collect_stun_servers(config);
|
||||
let mut detected_ipv4 = detect_local_ip_v4();
|
||||
@@ -426,185 +424,13 @@ pub fn decide_network_capabilities(
|
||||
}
|
||||
}
|
||||
|
||||
// Local interface discovery and bogon classification.
|
||||
mod local;
|
||||
pub use local::{
|
||||
detect_interface_ipv4, detect_interface_ipv6, is_bogon, is_bogon_v4, is_bogon_v6,
|
||||
log_probe_result,
|
||||
};
|
||||
use local::{detect_local_ip_v4, detect_local_ip_v6};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::config::NetworkConfig;
|
||||
|
||||
#[test]
|
||||
fn manual_nat_ip_enables_ipv4_me_without_reflection() {
|
||||
let config = NetworkConfig {
|
||||
ipv4: true,
|
||||
..Default::default()
|
||||
};
|
||||
let probe = NetworkProbe {
|
||||
detected_ipv4: Some(Ipv4Addr::new(10, 0, 0, 10)),
|
||||
ipv4_is_bogon: true,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let decision = decide_network_capabilities(
|
||||
&config,
|
||||
&probe,
|
||||
Some(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4))),
|
||||
);
|
||||
|
||||
assert!(decision.ipv4_me);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manual_nat_ip_does_not_enable_other_family() {
|
||||
let config = NetworkConfig {
|
||||
ipv4: true,
|
||||
ipv6: Some(true),
|
||||
..Default::default()
|
||||
};
|
||||
let probe = NetworkProbe {
|
||||
detected_ipv4: Some(Ipv4Addr::new(10, 0, 0, 10)),
|
||||
detected_ipv6: Some(Ipv6Addr::LOCALHOST),
|
||||
ipv4_is_bogon: true,
|
||||
ipv6_is_bogon: true,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let decision = decide_network_capabilities(
|
||||
&config,
|
||||
&probe,
|
||||
Some(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4))),
|
||||
);
|
||||
|
||||
assert!(decision.ipv4_me);
|
||||
assert!(!decision.ipv6_me);
|
||||
}
|
||||
}
|
||||
|
||||
fn detect_local_ip_v4() -> Option<Ipv4Addr> {
|
||||
let socket = UdpSocket::bind("0.0.0.0:0").ok()?;
|
||||
socket.connect("8.8.8.8:80").ok()?;
|
||||
match socket.local_addr().ok()?.ip() {
|
||||
IpAddr::V4(v4) => Some(v4),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn detect_local_ip_v6() -> Option<Ipv6Addr> {
|
||||
let socket = UdpSocket::bind("[::]:0").ok()?;
|
||||
socket.connect("[2001:4860:4860::8888]:80").ok()?;
|
||||
match socket.local_addr().ok()?.ip() {
|
||||
IpAddr::V6(v6) => Some(v6),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn detect_interface_ipv4() -> Option<Ipv4Addr> {
|
||||
detect_local_ip_v4()
|
||||
}
|
||||
|
||||
pub fn detect_interface_ipv6() -> Option<Ipv6Addr> {
|
||||
detect_local_ip_v6()
|
||||
}
|
||||
|
||||
pub fn is_bogon(ip: IpAddr) -> bool {
|
||||
match ip {
|
||||
IpAddr::V4(v4) => is_bogon_v4(v4),
|
||||
IpAddr::V6(v6) => is_bogon_v6(v6),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_bogon_v4(ip: Ipv4Addr) -> bool {
|
||||
let octets = ip.octets();
|
||||
if ip.is_private() || ip.is_loopback() || ip.is_link_local() {
|
||||
return true;
|
||||
}
|
||||
if octets[0] == 0 {
|
||||
return true;
|
||||
}
|
||||
if octets[0] == 100 && (octets[1] & 0xC0) == 64 {
|
||||
return true;
|
||||
}
|
||||
if octets[0] == 192 && octets[1] == 0 && octets[2] == 0 {
|
||||
return true;
|
||||
}
|
||||
if octets[0] == 192 && octets[1] == 0 && octets[2] == 2 {
|
||||
return true;
|
||||
}
|
||||
if octets[0] == 198 && (octets[1] & 0xFE) == 18 {
|
||||
return true;
|
||||
}
|
||||
if octets[0] == 198 && octets[1] == 51 && octets[2] == 100 {
|
||||
return true;
|
||||
}
|
||||
if octets[0] == 203 && octets[1] == 0 && octets[2] == 113 {
|
||||
return true;
|
||||
}
|
||||
if ip.is_multicast() {
|
||||
return true;
|
||||
}
|
||||
if octets[0] >= 240 {
|
||||
return true;
|
||||
}
|
||||
if ip.is_broadcast() {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn is_bogon_v6(ip: Ipv6Addr) -> bool {
|
||||
if ip.is_unspecified() || ip.is_loopback() || ip.is_unique_local() {
|
||||
return true;
|
||||
}
|
||||
let segs = ip.segments();
|
||||
if (segs[0] & 0xFFC0) == 0xFE80 {
|
||||
return true;
|
||||
}
|
||||
if segs[0..5] == [0, 0, 0, 0, 0] && segs[5] == 0xFFFF {
|
||||
return true;
|
||||
}
|
||||
if segs[0] == 0x0100 && segs[1..4] == [0, 0, 0] {
|
||||
return true;
|
||||
}
|
||||
if segs[0] == 0x2001 && segs[1] == 0x0db8 {
|
||||
return true;
|
||||
}
|
||||
if segs[0] == 0x2002 {
|
||||
return true;
|
||||
}
|
||||
if ip.is_multicast() {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn log_probe_result(probe: &NetworkProbe, decision: &NetworkDecision) {
|
||||
info!(
|
||||
ipv4 = probe
|
||||
.detected_ipv4
|
||||
.as_ref()
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_else(|| "-".into()),
|
||||
ipv6 = probe
|
||||
.detected_ipv6
|
||||
.as_ref()
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_else(|| "-".into()),
|
||||
reflected_v4 = probe
|
||||
.reflected_ipv4
|
||||
.as_ref()
|
||||
.map(|v| v.ip().to_string())
|
||||
.unwrap_or_else(|| "-".into()),
|
||||
reflected_v6 = probe
|
||||
.reflected_ipv6
|
||||
.as_ref()
|
||||
.map(|v| v.ip().to_string())
|
||||
.unwrap_or_else(|| "-".into()),
|
||||
ipv4_bogon = probe.ipv4_is_bogon,
|
||||
ipv6_bogon = probe.ipv6_is_bogon,
|
||||
ipv4_me = decision.ipv4_me,
|
||||
ipv6_me = decision.ipv6_me,
|
||||
ipv4_dc = decision.ipv4_dc,
|
||||
ipv6_dc = decision.ipv6_dc,
|
||||
prefer = decision.effective_prefer,
|
||||
multipath = decision.effective_multipath,
|
||||
"Network capabilities resolved"
|
||||
);
|
||||
}
|
||||
mod tests;
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
use super::*;
|
||||
|
||||
pub(super) fn detect_local_ip_v4() -> Option<Ipv4Addr> {
|
||||
let socket = UdpSocket::bind("0.0.0.0:0").ok()?;
|
||||
socket.connect("8.8.8.8:80").ok()?;
|
||||
match socket.local_addr().ok()?.ip() {
|
||||
IpAddr::V4(v4) => Some(v4),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn detect_local_ip_v6() -> Option<Ipv6Addr> {
|
||||
let socket = UdpSocket::bind("[::]:0").ok()?;
|
||||
socket.connect("[2001:4860:4860::8888]:80").ok()?;
|
||||
match socket.local_addr().ok()?.ip() {
|
||||
IpAddr::V6(v6) => Some(v6),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn detect_interface_ipv4() -> Option<Ipv4Addr> {
|
||||
detect_local_ip_v4()
|
||||
}
|
||||
|
||||
pub fn detect_interface_ipv6() -> Option<Ipv6Addr> {
|
||||
detect_local_ip_v6()
|
||||
}
|
||||
|
||||
pub fn is_bogon(ip: IpAddr) -> bool {
|
||||
match ip {
|
||||
IpAddr::V4(v4) => is_bogon_v4(v4),
|
||||
IpAddr::V6(v6) => is_bogon_v6(v6),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_bogon_v4(ip: Ipv4Addr) -> bool {
|
||||
let octets = ip.octets();
|
||||
if ip.is_private() || ip.is_loopback() || ip.is_link_local() {
|
||||
return true;
|
||||
}
|
||||
if octets[0] == 0 {
|
||||
return true;
|
||||
}
|
||||
if octets[0] == 100 && (octets[1] & 0xC0) == 64 {
|
||||
return true;
|
||||
}
|
||||
if octets[0] == 192 && octets[1] == 0 && octets[2] == 0 {
|
||||
return true;
|
||||
}
|
||||
if octets[0] == 192 && octets[1] == 0 && octets[2] == 2 {
|
||||
return true;
|
||||
}
|
||||
if octets[0] == 198 && (octets[1] & 0xFE) == 18 {
|
||||
return true;
|
||||
}
|
||||
if octets[0] == 198 && octets[1] == 51 && octets[2] == 100 {
|
||||
return true;
|
||||
}
|
||||
if octets[0] == 203 && octets[1] == 0 && octets[2] == 113 {
|
||||
return true;
|
||||
}
|
||||
if ip.is_multicast() {
|
||||
return true;
|
||||
}
|
||||
if octets[0] >= 240 {
|
||||
return true;
|
||||
}
|
||||
if ip.is_broadcast() {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn is_bogon_v6(ip: Ipv6Addr) -> bool {
|
||||
if ip.is_unspecified() || ip.is_loopback() || ip.is_unique_local() {
|
||||
return true;
|
||||
}
|
||||
let segs = ip.segments();
|
||||
if (segs[0] & 0xFFC0) == 0xFE80 {
|
||||
return true;
|
||||
}
|
||||
if segs[0..5] == [0, 0, 0, 0, 0] && segs[5] == 0xFFFF {
|
||||
return true;
|
||||
}
|
||||
if segs[0] == 0x0100 && segs[1..4] == [0, 0, 0] {
|
||||
return true;
|
||||
}
|
||||
if segs[0] == 0x2001 && segs[1] == 0x0db8 {
|
||||
return true;
|
||||
}
|
||||
if segs[0] == 0x2002 {
|
||||
return true;
|
||||
}
|
||||
if ip.is_multicast() {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn log_probe_result(probe: &NetworkProbe, decision: &NetworkDecision) {
|
||||
info!(
|
||||
ipv4 = probe
|
||||
.detected_ipv4
|
||||
.as_ref()
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_else(|| "-".into()),
|
||||
ipv6 = probe
|
||||
.detected_ipv6
|
||||
.as_ref()
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_else(|| "-".into()),
|
||||
reflected_v4 = probe
|
||||
.reflected_ipv4
|
||||
.as_ref()
|
||||
.map(|v| v.ip().to_string())
|
||||
.unwrap_or_else(|| "-".into()),
|
||||
reflected_v6 = probe
|
||||
.reflected_ipv6
|
||||
.as_ref()
|
||||
.map(|v| v.ip().to_string())
|
||||
.unwrap_or_else(|| "-".into()),
|
||||
ipv4_bogon = probe.ipv4_is_bogon,
|
||||
ipv6_bogon = probe.ipv6_is_bogon,
|
||||
ipv4_me = decision.ipv4_me,
|
||||
ipv6_me = decision.ipv6_me,
|
||||
ipv4_dc = decision.ipv4_dc,
|
||||
ipv6_dc = decision.ipv6_dc,
|
||||
prefer = decision.effective_prefer,
|
||||
multipath = decision.effective_multipath,
|
||||
"Network capabilities resolved"
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
use super::*;
|
||||
use crate::config::NetworkConfig;
|
||||
|
||||
#[test]
|
||||
fn manual_nat_ip_enables_ipv4_me_without_reflection() {
|
||||
let config = NetworkConfig {
|
||||
ipv4: true,
|
||||
..Default::default()
|
||||
};
|
||||
let probe = NetworkProbe {
|
||||
detected_ipv4: Some(Ipv4Addr::new(10, 0, 0, 10)),
|
||||
ipv4_is_bogon: true,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let decision =
|
||||
decide_network_capabilities(&config, &probe, Some(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4))));
|
||||
|
||||
assert!(decision.ipv4_me);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manual_nat_ip_does_not_enable_other_family() {
|
||||
let config = NetworkConfig {
|
||||
ipv4: true,
|
||||
ipv6: Some(true),
|
||||
..Default::default()
|
||||
};
|
||||
let probe = NetworkProbe {
|
||||
detected_ipv4: Some(Ipv4Addr::new(10, 0, 0, 10)),
|
||||
detected_ipv6: Some(Ipv6Addr::LOCALHOST),
|
||||
ipv4_is_bogon: true,
|
||||
ipv6_is_bogon: true,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let decision =
|
||||
decide_network_capabilities(&config, &probe, Some(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4))));
|
||||
|
||||
assert!(decision.ipv4_me);
|
||||
assert!(!decision.ipv6_me);
|
||||
}
|
||||
+2
-2
@@ -394,8 +394,8 @@ async fn resolve_stun_addr(
|
||||
}
|
||||
|
||||
if let Some((host, port)) = split_host_port(stun_addr)
|
||||
&& let Some(addr) = dns_resolver
|
||||
.and_then(|resolver| resolver.resolve_socket_addr(&host, port))
|
||||
&& let Some(addr) =
|
||||
dns_resolver.and_then(|resolver| resolver.resolve_socket_addr(&host, port))
|
||||
{
|
||||
return Ok(match (addr.is_ipv4(), family) {
|
||||
(true, IpFamily::V4) | (false, IpFamily::V6) => Some(addr),
|
||||
|
||||
Reference in New Issue
Block a user