use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
pub fn local_ip_towards(target: IpAddr) -> Option<Ipv4Addr> {
let sock = UdpSocket::bind((Ipv4Addr::UNSPECIFIED, 0)).ok()?;
sock.connect(SocketAddr::new(target, 9)).ok()?;
match sock.local_addr().ok()?.ip() {
IpAddr::V4(ip) if !ip.is_unspecified() && !ip.is_loopback() => Some(ip),
_ => None,
}
}
pub fn host_ip() -> Option<Ipv4Addr> {
let mut ips = lan_ips();
let rank = |ip: &Ipv4Addr| match ip.octets() {
[192, 168, ..] => 0,
_ if ip.is_private() => 1,
_ => 2,
};
ips.sort_by_key(|ip| (rank(ip), *ip));
ips.first().copied()
}
pub fn lan_ips() -> Vec<Ipv4Addr> {
let mut ips: Vec<Ipv4Addr> = if_addrs::get_if_addrs()
.unwrap_or_default()
.into_iter()
.filter_map(|iface| match iface.ip() {
IpAddr::V4(ip) if !ip.is_loopback() && !ip.is_link_local() => Some(ip),
_ => None,
})
.collect();
ips.sort();
ips.dedup();
ips
}
pub fn local_ip_for(peer: IpAddr) -> Option<Ipv4Addr> {
local_ip_towards(peer).or_else(host_ip)
}