use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
pub fn is_private_ip(ip: IpAddr) -> bool {
match ip {
IpAddr::V4(v4) => is_private_ipv4(v4),
IpAddr::V6(v6) => is_private_ipv6(v6),
}
}
fn is_private_ipv4(ip: Ipv4Addr) -> bool {
let octets = ip.octets();
if ip.is_unspecified() {
return true;
}
if ip.is_loopback() {
return true;
}
if octets[0] == 10 {
return true;
}
if octets[0] == 172 && (16..=31).contains(&octets[1]) {
return true;
}
if octets[0] == 192 && octets[1] == 168 {
return true;
}
if octets[0] == 169 && octets[1] == 254 {
return true;
}
if ip.is_broadcast() {
return true;
}
if (octets[0] == 192 && octets[1] == 0 && octets[2] == 2)
|| (octets[0] == 198 && octets[1] == 51 && octets[2] == 100)
|| (octets[0] == 203 && octets[1] == 0 && octets[2] == 113)
{
return true;
}
if octets[0] == 100 && (64..=127).contains(&octets[1]) {
return true;
}
if octets[0] >= 224 && octets[0] <= 239 {
return true;
}
if octets[0] >= 240 {
return true;
}
false
}
fn is_private_ipv6(ip: Ipv6Addr) -> bool {
if ip.is_loopback() {
return true;
}
if ip.is_unspecified() {
return true;
}
let segments = ip.segments();
if segments[0] & 0xffc0 == 0xfe80 {
return true;
}
if segments[0] & 0xfe00 == 0xfc00 {
return true;
}
if segments[0] & 0xff00 == 0xff00 {
return true;
}
if let Some(v4) = ip.to_ipv4_mapped() {
return is_private_ipv4(v4);
}
false
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn loopback_v4() {
assert!(is_private_ip("127.0.0.1".parse().unwrap()));
assert!(is_private_ip("127.255.255.255".parse().unwrap()));
}
#[test]
fn private_ranges_v4() {
assert!(is_private_ip("10.0.0.1".parse().unwrap()));
assert!(is_private_ip("10.255.255.255".parse().unwrap()));
assert!(is_private_ip("172.16.0.1".parse().unwrap()));
assert!(is_private_ip("172.31.255.255".parse().unwrap()));
assert!(is_private_ip("192.168.0.1".parse().unwrap()));
assert!(is_private_ip("192.168.255.255".parse().unwrap()));
}
#[test]
fn link_local_v4() {
assert!(is_private_ip("169.254.0.1".parse().unwrap()));
assert!(is_private_ip("169.254.169.254".parse().unwrap())); }
#[test]
fn unspecified_and_broadcast() {
assert!(is_private_ip("0.0.0.0".parse().unwrap()));
assert!(is_private_ip("255.255.255.255".parse().unwrap()));
}
#[test]
fn multicast_v4() {
assert!(is_private_ip("224.0.0.1".parse().unwrap()));
assert!(is_private_ip("239.255.255.255".parse().unwrap()));
}
#[test]
fn reserved_v4() {
assert!(is_private_ip("240.0.0.1".parse().unwrap()));
assert!(is_private_ip("100.64.0.1".parse().unwrap()));
}
#[test]
fn public_v4_allowed() {
assert!(!is_private_ip("8.8.8.8".parse().unwrap()));
assert!(!is_private_ip("1.1.1.1".parse().unwrap()));
assert!(!is_private_ip("93.184.216.34".parse().unwrap()));
}
#[test]
fn loopback_v6() {
assert!(is_private_ip("::1".parse().unwrap()));
}
#[test]
fn unspecified_v6() {
assert!(is_private_ip("::".parse().unwrap()));
}
#[test]
fn link_local_v6() {
assert!(is_private_ip("fe80::1".parse().unwrap()));
}
#[test]
fn unique_local_v6() {
assert!(is_private_ip("fc00::1".parse().unwrap()));
assert!(is_private_ip("fd00::1".parse().unwrap()));
}
#[test]
fn ec2_metadata_v6() {
assert!(is_private_ip("fd00:ec2::254".parse().unwrap()));
}
#[test]
fn multicast_v6() {
assert!(is_private_ip("ff02::1".parse().unwrap()));
}
#[test]
fn ipv4_mapped_v6_private() {
assert!(is_private_ip("::ffff:127.0.0.1".parse().unwrap()));
assert!(is_private_ip("::ffff:10.0.0.1".parse().unwrap()));
assert!(is_private_ip("::ffff:192.168.1.1".parse().unwrap()));
assert!(is_private_ip("::ffff:169.254.169.254".parse().unwrap()));
}
#[test]
fn ipv4_mapped_v6_public() {
assert!(!is_private_ip("::ffff:8.8.8.8".parse().unwrap()));
}
#[test]
fn public_v6_allowed() {
assert!(!is_private_ip("2607:f8b0:4004:800::200e".parse().unwrap()));
}
}