use std::net::{IpAddr, SocketAddr};
use std::time::Duration;
pub const FEED_FLEET_AT: &str = "fleet_at";
pub const FEED_FLEET_CA: &str = "fleet_ca";
pub const SEAM_WORD: &str = "fleet-seam-bench-only";
pub fn seam_refusal(at: Option<&str>, ca: bool) -> Option<String> {
match at.map(str::trim).filter(|a| !a.is_empty()) {
None if ca => Some(format!(
"REFUSED [{SEAM_WORD}] the installer fed `{FEED_FLEET_CA}` with no `{FEED_FLEET_AT}`: a door's own CA is a bench's, beside a bench address only; a public door is checked under the public roots"
)),
None => None,
Some(at) => match at.parse::<SocketAddr>() {
Ok(a) if bench_address(a.ip()) => None,
_ => Some(format!(
"REFUSED [{SEAM_WORD}] the installer fed `{FEED_FLEET_AT}` = {at}: the pull channel is dialled elsewhere than its door's name only on a bench (a private, loopback, link-local or shared `ip:port`)"
)),
},
}
}
pub fn bench_address(ip: IpAddr) -> bool {
match ip {
IpAddr::V4(v) => v.is_private() || v.is_loopback() || v.is_link_local() || (v.octets()[0] == 100 && (v.octets()[1] & 0xc0) == 64),
IpAddr::V6(v) => v.is_loopback() || (v.segments()[0] & 0xfe00) == 0xfc00 || (v.segments()[0] & 0xffc0) == 0xfe80,
}
}
pub const NOT_YET_KNOWN_GRACE: Duration = Duration::from_secs(45 * 60);
pub const NOT_YET_KNOWN_RETRY: Duration = Duration::from_secs(10);
pub fn after_refusal(code: &str, since_start: Duration, pause: Duration) -> (Duration, bool) {
if code == "unauthenticated" && since_start < NOT_YET_KNOWN_GRACE {
(pause.min(NOT_YET_KNOWN_RETRY), true)
} else {
(pause, false)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_fed_fleet_seam_is_refused_unless_it_is_a_bench_address() {
for at in ["10.0.2.2:40443", "127.0.0.1:443", "192.168.1.9:8443", "172.20.0.4:443", "169.254.1.1:443", "[::1]:443", "[fd00::2]:443", "[fe80::1]:443", "100.64.0.9:443", " 10.0.2.2:40443 "] {
assert_eq!(seam_refusal(Some(at), true), None, "a bench forward {at} is dialled");
assert_eq!(seam_refusal(Some(at), false), None, "a bench forward {at}, public roots");
}
for at in ["203.0.113.7:443", "8.8.8.8:443", "monetize.example:443", "[2001:db8::1]:443", "10.0.2.2", "100.128.0.1:443", "0.0.0.0:443"] {
let e = seam_refusal(Some(at), false).unwrap_or_else(|| panic!("{at} is no bench address"));
assert!(e.contains(&format!("[{SEAM_WORD}]")) && e.contains(FEED_FLEET_AT), "{at}: {e}");
}
let e = seam_refusal(None, true).expect("a CA with no bench address");
assert!(e.contains(&format!("[{SEAM_WORD}]")) && e.contains(FEED_FLEET_CA), "{e}");
assert_eq!(seam_refusal(None, false), None, "no seam: the name, under the public roots");
assert_eq!(seam_refusal(Some(" "), false), None, "a blank fleet_at is not fed");
assert!(seam_refusal(Some(""), true).is_some(), "a CA beside a blank fleet_at has no bench address");
}
#[test]
fn a_box_not_known_yet_asks_again_soon_and_quietly_inside_the_grace() {
let every = Duration::from_secs(60);
assert_eq!(after_refusal("unauthenticated", Duration::from_secs(30), every), (NOT_YET_KNOWN_RETRY, true));
assert_eq!(after_refusal("unauthenticated", NOT_YET_KNOWN_GRACE - Duration::from_secs(1), every), (NOT_YET_KNOWN_RETRY, true));
assert_eq!(after_refusal("unauthenticated", Duration::from_secs(1), Duration::from_secs(3)), (Duration::from_secs(3), true), "never longer than it would have paused");
assert_eq!(after_refusal("unauthenticated", NOT_YET_KNOWN_GRACE + Duration::from_secs(1), every), (every, false), "past the grace it is a WARN again");
assert_eq!(after_refusal("unavailable", Duration::from_secs(30), every), (every, false), "a door that is down is not 'not known yet'");
assert!(NOT_YET_KNOWN_GRACE >= Duration::from_secs(45 * 60), "covers a whole install");
assert!(NOT_YET_KNOWN_RETRY <= Duration::from_secs(10));
}
#[test]
fn a_bench_address_is_private_loopback_link_local_or_shared() {
for ip in ["10.1.2.3", "172.16.0.1", "192.168.0.1", "127.0.0.1", "169.254.0.1", "100.64.0.1", "100.127.255.255", "::1", "fc00::1", "fe80::1"] {
assert!(bench_address(ip.parse().unwrap()), "{ip}");
}
for ip in ["8.8.8.8", "100.63.255.255", "100.128.0.0", "2001:db8::1", "0.0.0.0", "::"] {
assert!(!bench_address(ip.parse().unwrap()), "{ip}");
}
}
}