Skip to main content

soma_mcp_client/
net.rs

1use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
2
3#[must_use]
4pub fn is_denied_loopback_or_wildcard(ip: IpAddr) -> bool {
5    ip.is_loopback() || ip.is_unspecified()
6}
7
8#[must_use]
9pub fn is_link_local(ip: IpAddr) -> bool {
10    match ip {
11        IpAddr::V4(ip) => ip.is_link_local(),
12        IpAddr::V6(ip) => ip.is_unicast_link_local(),
13    }
14}
15
16#[must_use]
17pub fn is_private_or_cgnat(ip: IpAddr) -> bool {
18    match ip {
19        IpAddr::V4(ip) => {
20            ip.is_private() || ip.octets()[0] == 100 && (64..=127).contains(&ip.octets()[1])
21        }
22        IpAddr::V6(ip) => ip.is_unique_local(),
23    }
24}
25
26#[must_use]
27pub fn is_metadata_ip(ip: IpAddr) -> bool {
28    match ip {
29        IpAddr::V4(ip) => ip == Ipv4Addr::new(169, 254, 169, 254),
30        IpAddr::V6(ip) => ip == Ipv6Addr::LOCALHOST,
31    }
32}
33
34#[must_use]
35pub fn host_is_private_tld_surprise(host: &str) -> bool {
36    let normalized = host.trim_end_matches('.').to_ascii_lowercase();
37    normalized == "localhost"
38        || !normalized.contains('.')
39        || normalized.ends_with(".local")
40        || normalized.ends_with(".internal")
41        || normalized.ends_with(".lan")
42}
43
44#[cfg(test)]
45#[path = "net_tests.rs"]
46mod tests;