use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
pub type IpFilter = dyn Fn(IpAddr) -> bool + Send + Sync;
pub fn is_public_ip(ip: IpAddr) -> bool {
match ip {
IpAddr::V4(v4) => is_public_ipv4(v4),
IpAddr::V6(v6) => is_public_ipv6(v6),
}
}
fn is_public_ipv4(ip: Ipv4Addr) -> bool {
let o = ip.octets();
!(
o[0] == 0
|| o[0] == 10
|| (o[0] == 100 && (o[1] & 0b1100_0000) == 64)
|| o[0] == 127
|| (o[0] == 169 && o[1] == 254)
|| (o[0] == 172 && (o[1] & 0xF0) == 16)
|| (o[0] == 192 && o[1] == 0 && o[2] == 0)
|| (o[0] == 192 && o[1] == 0 && o[2] == 2)
|| (o[0] == 192 && o[1] == 168)
|| (o[0] == 198 && (o[1] & 0xFE) == 18)
|| (o[0] == 198 && o[1] == 51 && o[2] == 100)
|| (o[0] == 203 && o[1] == 0 && o[2] == 113)
|| o[0] >= 224
)
}
fn is_public_ipv6(ip: Ipv6Addr) -> bool {
if let Some(mapped) = ip.to_ipv4_mapped() {
return is_public_ipv4(mapped);
}
let s = ip.segments();
!(ip.is_loopback()
|| ip.is_unspecified()
|| s[0] == 0
|| (s[0] & 0xfe00) == 0xfc00
|| (s[0] & 0xffc0) == 0xfe80
|| (s[0] & 0xff00) == 0xff00
|| s[0] == 0x0100
|| (s[0] == 0x2001 && (s[1] & 0xff00) == 0x0000)
|| (s[0] == 0x2001 && s[1] == 0x0db8))
}
#[cfg(test)]
mod tests {
use super::*;
use std::net::IpAddr;
fn allowed(s: &str) -> bool {
is_public_ip(s.parse::<IpAddr>().unwrap())
}
#[test]
fn rejects_private_and_reserved_ipv4() {
assert!(!allowed("127.0.0.1"));
assert!(!allowed("10.0.0.1"));
assert!(!allowed("172.16.0.1"));
assert!(!allowed("172.31.255.255"));
assert!(!allowed("192.168.1.1"));
assert!(!allowed("169.254.169.254")); assert!(!allowed("0.0.0.0"));
assert!(!allowed("255.255.255.255"));
assert!(!allowed("100.64.0.1")); assert!(!allowed("192.0.2.1")); assert!(!allowed("198.51.100.7")); assert!(!allowed("203.0.113.9")); assert!(!allowed("198.18.0.1")); assert!(!allowed("224.0.0.1")); assert!(!allowed("240.0.0.1")); assert!(!allowed("192.0.0.8")); }
#[test]
fn accepts_public_ipv4() {
assert!(allowed("8.8.8.8"));
assert!(allowed("1.1.1.1"));
assert!(allowed("51.75.20.104"));
assert!(allowed("172.32.0.1")); assert!(allowed("100.128.0.1")); assert!(allowed("9.9.9.9"));
}
#[test]
fn rejects_private_and_reserved_ipv6() {
assert!(!allowed("::1"));
assert!(!allowed("::"));
assert!(!allowed("fe80::1"));
assert!(!allowed("fc00::1"));
assert!(!allowed("fd12:3456:789a::1"));
assert!(!allowed("ff02::1"));
assert!(!allowed("2001:db8::1"));
assert!(!allowed("2001::1")); assert!(!allowed("::ffff:127.0.0.1")); assert!(!allowed("::ffff:10.0.0.1"));
assert!(!allowed("::ffff:169.254.169.254"));
}
#[test]
fn accepts_public_ipv6() {
assert!(allowed("2606:4700:4700::1111"));
assert!(allowed("2001:4860:4860::8888"));
assert!(allowed("::ffff:8.8.8.8")); }
#[test]
fn test_ipv4_edge_cases() {
assert!(!allowed("10.255.255.255"));
assert!(allowed("172.15.255.255")); assert!(!allowed("172.16.0.0")); assert!(!allowed("172.31.255.255")); assert!(allowed("172.32.0.0")); assert!(allowed("192.167.255.255")); assert!(allowed("192.169.0.0")); assert!(allowed("169.253.255.255")); assert!(allowed("169.255.0.0")); assert!(allowed("100.63.255.255")); assert!(!allowed("100.127.255.255")); assert!(allowed("100.128.0.0")); }
#[test]
fn test_ipv6_edge_cases() {
assert!(!allowed("::ffff:192.168.1.1"));
assert!(!allowed("2001:0:0:1::1"));
assert!(allowed("2001:200::1"));
assert!(!allowed("2001:db8:1::1"));
}
}