use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use anyhow::Result;
use claps::common::log::LogResult;
pub fn addrs() -> Result<Vec<IpAddr>> {
Ok(local_ip_address::list_afinet_netifas()
.log()?
.iter()
.filter_map(|(_name, addr)| {
if addr.is_global_() {
Some(addr.to_owned())
} else {
None
}
})
.collect())
}
trait IsGlobal {
fn is_global_(&self) -> bool;
}
trait V4IsGlobal {
fn is_global_(&self) -> bool;
}
trait V6IsGlobal {
fn is_global_(&self) -> bool;
fn is_unicast_link_local_(&self) -> bool;
}
impl IsGlobal for IpAddr {
fn is_global_(&self) -> bool {
match self {
IpAddr::V4(addr) => addr.is_global_(),
IpAddr::V6(addr) => addr.is_global_(),
}
}
}
impl V4IsGlobal for Ipv4Addr {
fn is_global_(&self) -> bool {
!(self.octets()[0] == 0 || self.is_private()
|| self.is_loopback()
|| self.is_link_local()
||(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0)
|| self.is_documentation()
|| self.is_broadcast())
}
}
impl V6IsGlobal for Ipv6Addr {
fn is_global_(&self) -> bool {
!(self.is_unspecified()
|| self.is_loopback()
|| matches!(self.segments(), [0, 0, 0, 0, 0, 0xffff, _, _])
|| matches!(self.segments(), [0x64, 0xff9b, 1, _, _, _, _, _])
|| matches!(self.segments(), [0x100, 0, 0, 0, _, _, _, _])
|| (matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200)
&& !(
u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001
|| u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002
|| matches!(self.segments(), [0x2001, 3, _, _, _, _, _, _])
|| matches!(self.segments(), [0x2001, 4, 0x112, _, _, _, _, _])
|| matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if (0x20..=0x2F).contains(&b))
))
|| self.is_unicast_link_local_())
}
fn is_unicast_link_local_(&self) -> bool {
(self.segments()[0] & 0xffc0) == 0xfe80
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use super::*;
#[test]
fn test_ipv4_is_global() {
let addr = IpAddr::from_str("127.0.0.1").unwrap();
assert!(!addr.is_global_());
let addr = IpAddr::from_str("183.173.142.216").unwrap();
assert!(addr.is_global_());
let addr = IpAddr::from_str("172.17.0.1").unwrap();
assert!(!addr.is_global_());
}
#[test]
fn test_ipv6_is_global() {
let addr = IpAddr::from_str("::1").unwrap();
assert!(!addr.is_global_());
let addr = IpAddr::from_str("2402:f000:3:8801:fd1:9ac2:8087:fae8").unwrap();
assert!(addr.is_global_());
let addr = IpAddr::from_str("fe80::85f0:7ccd:2aff:70f5").unwrap();
assert!(!addr.is_global_());
let addr = IpAddr::from_str("fe80::42:5bff:fed7:d2bf").unwrap();
assert!(!addr.is_global_());
}
}