use std::net::IpAddr;
pub fn is_local_network(addr: IpAddr) -> bool {
let addr = match addr {
IpAddr::V6(ip) => ip.to_ipv4_mapped().map_or(IpAddr::V6(ip), IpAddr::V4),
IpAddr::V4(_) => addr,
};
match addr {
IpAddr::V4(ip) => {
ip.is_loopback() || ip.is_private() || ip.is_link_local() || ip.is_unspecified()
}
IpAddr::V6(ip) => {
if ip.is_loopback() || ip.is_unspecified() {
return true;
}
let octets = ip.octets();
if octets[0] == 0xfe && (octets[1] & 0xc0) == 0x80 {
return true;
}
if (octets[0] & 0xfe) == 0xfc {
return true;
}
false
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn local_network_detection() {
assert!(is_local_network("127.0.0.1".parse().unwrap()));
assert!(is_local_network("192.168.1.1".parse().unwrap()));
assert!(is_local_network("10.0.0.1".parse().unwrap()));
assert!(is_local_network("172.16.0.1".parse().unwrap()));
assert!(is_local_network("169.254.1.1".parse().unwrap()));
assert!(is_local_network("::1".parse().unwrap()));
assert!(!is_local_network("8.8.8.8".parse().unwrap()));
assert!(!is_local_network("1.2.3.4".parse().unwrap()));
}
#[test]
fn ipv6_local_network_detection() {
assert!(is_local_network("::1".parse().unwrap()));
assert!(is_local_network("fe80::1".parse().unwrap()));
assert!(is_local_network("fe80::abcd:1234".parse().unwrap()));
assert!(is_local_network("fc00::1".parse().unwrap()));
assert!(is_local_network("fd00::1".parse().unwrap()));
assert!(is_local_network("fd12:3456:789a::1".parse().unwrap()));
assert!(!is_local_network("2001:db8::1".parse().unwrap()));
assert!(!is_local_network(
"2607:f8b0:4004:800::200e".parse().unwrap()
));
}
#[test]
fn unspecified_v4_is_local() {
assert!(is_local_network("0.0.0.0".parse().unwrap()));
}
#[test]
fn unspecified_v6_is_local() {
assert!(is_local_network("::".parse().unwrap()));
}
#[test]
fn ipv4_mapped_v6_loopback_is_local() {
assert!(is_local_network("::ffff:127.0.0.1".parse().unwrap()));
assert!(is_local_network("::ffff:7f00:1".parse().unwrap()));
}
#[test]
fn ipv4_mapped_v6_private_is_local() {
assert!(is_local_network("::ffff:192.168.1.1".parse().unwrap()));
assert!(is_local_network("::ffff:10.0.0.1".parse().unwrap()));
assert!(is_local_network("::ffff:172.16.0.1".parse().unwrap()));
assert!(!is_local_network("::ffff:8.8.8.8".parse().unwrap()));
}
}