toolkit: is_public, the addresses the egress proxy may reach
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
//! `is_public`: the egress proxy connects only to public addresses. Everything in the M3b spec,
|
||||
//! section 5, "Refused ranges" tables is not public; everything else is.
|
||||
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
|
||||
/// True if `ip` is a public unicast address.
|
||||
pub fn is_public(ip: IpAddr) -> bool {
|
||||
match ip {
|
||||
IpAddr::V4(v4) => is_public_v4(v4),
|
||||
IpAddr::V6(v6) => is_public_v6(v6),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_public_v4(ip: Ipv4Addr) -> bool {
|
||||
let [a, b, c, _] = ip.octets();
|
||||
if a == 0 {
|
||||
return false;
|
||||
}
|
||||
if a == 10 {
|
||||
return false;
|
||||
}
|
||||
if a == 100 && (64..=127).contains(&b) {
|
||||
return false;
|
||||
}
|
||||
if a == 127 {
|
||||
return false;
|
||||
}
|
||||
if a == 169 && b == 254 {
|
||||
return false;
|
||||
}
|
||||
if a == 172 && (16..=31).contains(&b) {
|
||||
return false;
|
||||
}
|
||||
if a == 192 && b == 0 && c == 0 {
|
||||
return false;
|
||||
}
|
||||
if a == 192 && b == 0 && c == 2 {
|
||||
return false;
|
||||
}
|
||||
if a == 192 && b == 168 {
|
||||
return false;
|
||||
}
|
||||
if a == 198 && (b == 18 || b == 19) {
|
||||
return false;
|
||||
}
|
||||
if a == 198 && b == 51 && c == 100 {
|
||||
return false;
|
||||
}
|
||||
if a == 203 && b == 0 && c == 113 {
|
||||
return false;
|
||||
}
|
||||
if a >= 224 {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn is_public_v6(ip: Ipv6Addr) -> bool {
|
||||
let s = ip.segments();
|
||||
if is_ipv4_mapped(s) {
|
||||
let last32 = (u32::from(s[6]) << 16) | u32::from(s[7]);
|
||||
return is_public_v4(Ipv4Addr::from(last32));
|
||||
}
|
||||
if is_nat64(s) {
|
||||
let last32 = (u32::from(s[6]) << 16) | u32::from(s[7]);
|
||||
return is_public_v4(Ipv4Addr::from(last32));
|
||||
}
|
||||
if s[0..6].iter().all(|&x| x == 0) {
|
||||
return false;
|
||||
}
|
||||
if s[0] & 0xfe00 == 0xfc00 {
|
||||
return false;
|
||||
}
|
||||
if s[0] & 0xffc0 == 0xfe80 {
|
||||
return false;
|
||||
}
|
||||
if s[0] & 0xff00 == 0xff00 {
|
||||
return false;
|
||||
}
|
||||
if s[0] == 0x2001 && s[1] == 0x0db8 {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// `::ffff:0:0/96`: the high 80 bits are zero, then the `ffff` marker.
|
||||
fn is_ipv4_mapped(s: [u16; 8]) -> bool {
|
||||
s[0..5].iter().all(|&x| x == 0) && s[5] == 0xffff
|
||||
}
|
||||
|
||||
/// `64:ff9b::/96`: the NAT64 prefix, then 32 zero bits, then the embedded IPv4 address.
|
||||
fn is_nat64(s: [u16; 8]) -> bool {
|
||||
s[0] == 0x64 && s[1] == 0xff9b && s[2] == 0 && s[3] == 0 && s[4] == 0 && s[5] == 0
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
use proto::tools::{HttpFetchArgs, ReadFileArgs, ShellArgs, WriteFileArgs};
|
||||
|
||||
pub mod addr;
|
||||
pub mod fetch;
|
||||
pub mod files;
|
||||
pub mod input;
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
//! `is_public`: the egress proxy connects only to public addresses. Every range in the M3b spec,
|
||||
//! section 5, has a case at each end, and a public neighbour just outside it. Do not edit.
|
||||
|
||||
use std::net::IpAddr;
|
||||
|
||||
use toolkit::addr::is_public;
|
||||
|
||||
fn ip(s: &str) -> IpAddr {
|
||||
s.parse().unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn refused_ipv4() {
|
||||
for s in [
|
||||
"0.0.0.0",
|
||||
"0.255.255.255",
|
||||
"10.0.0.0",
|
||||
"10.255.255.255",
|
||||
"100.64.0.0",
|
||||
"100.100.100.100",
|
||||
"100.127.255.255",
|
||||
"127.0.0.1",
|
||||
"127.255.255.255",
|
||||
"169.254.0.1",
|
||||
"169.254.255.255",
|
||||
"172.16.0.0",
|
||||
"172.31.255.255",
|
||||
"192.0.0.0",
|
||||
"192.0.0.255",
|
||||
"192.0.2.1",
|
||||
"192.168.0.1",
|
||||
"192.168.255.255",
|
||||
"198.18.0.0",
|
||||
"198.19.255.255",
|
||||
"198.51.100.7",
|
||||
"203.0.113.9",
|
||||
"224.0.0.1",
|
||||
"239.255.255.255",
|
||||
"240.0.0.0",
|
||||
"255.255.255.255",
|
||||
] {
|
||||
assert!(!is_public(ip(s)), "{s} must be refused");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn public_ipv4() {
|
||||
for s in [
|
||||
"1.1.1.1",
|
||||
"8.8.8.8",
|
||||
"9.255.255.255",
|
||||
"11.0.0.0",
|
||||
"100.63.255.255",
|
||||
"100.128.0.0",
|
||||
"126.255.255.255",
|
||||
"128.0.0.0",
|
||||
"169.253.255.255",
|
||||
"172.15.255.255",
|
||||
"172.32.0.0",
|
||||
"192.0.1.0",
|
||||
"192.0.3.0",
|
||||
"192.167.255.255",
|
||||
"192.169.0.0",
|
||||
"198.17.255.255",
|
||||
"198.20.0.0",
|
||||
"198.51.99.255",
|
||||
"203.0.112.255",
|
||||
"223.255.255.255",
|
||||
"93.184.216.34",
|
||||
] {
|
||||
assert!(is_public(ip(s)), "{s} is public");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn refused_ipv6() {
|
||||
for s in [
|
||||
"::",
|
||||
"::1",
|
||||
"fc00::1",
|
||||
"fdff:ffff::1",
|
||||
"fd7a:115c:a1e0::1",
|
||||
"fe80::1",
|
||||
"febf::1",
|
||||
"ff02::1",
|
||||
"ff00::",
|
||||
"2001:db8::1",
|
||||
"2001:db8:ffff::1",
|
||||
"::ffff:127.0.0.1",
|
||||
"::ffff:10.1.2.3",
|
||||
"::ffff:100.100.100.100",
|
||||
"64:ff9b::7f00:1",
|
||||
"64:ff9b::a01:203",
|
||||
"::ffff:0.0.0.0",
|
||||
"::127.0.0.1",
|
||||
"::1.1.1.1",
|
||||
"::ffff",
|
||||
] {
|
||||
assert!(!is_public(ip(s)), "{s} must be refused");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn public_ipv6() {
|
||||
for s in [
|
||||
"2606:4700:4700::1111",
|
||||
"2a00:1450::1",
|
||||
"fbff::1",
|
||||
"fec0::1",
|
||||
"2001:db9::1",
|
||||
"::ffff:1.1.1.1",
|
||||
"64:ff9b::101:101",
|
||||
] {
|
||||
assert!(is_public(ip(s)), "{s} is public");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user