use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, UdpSocket};
const IPV4_PROBE: (Ipv4Addr, u16) = (Ipv4Addr::new(8, 8, 8, 8), 80);
const IPV6_PROBE: (Ipv6Addr, u16) = (
Ipv6Addr::new(0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8888),
80,
);
#[must_use]
pub fn local_ipv4_address() -> Option<IpAddr> {
let socket = UdpSocket::bind((Ipv4Addr::UNSPECIFIED, 0)).ok()?;
socket.connect(IPV4_PROBE).ok()?;
socket.local_addr().ok().map(|addr| addr.ip())
}
#[must_use]
pub fn local_ipv6_address() -> Option<IpAddr> {
let socket = UdpSocket::bind((Ipv6Addr::UNSPECIFIED, 0)).ok()?;
socket.connect(IPV6_PROBE).ok()?;
socket.local_addr().ok().map(|addr| addr.ip())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn local_ipv4_probe_is_the_expected_destination() {
assert_eq!(IPV4_PROBE.0, Ipv4Addr::new(8, 8, 8, 8));
assert_eq!(IPV4_PROBE.1, 80);
}
#[test]
fn local_ipv6_probe_is_the_expected_destination() {
assert_eq!(
IPV6_PROBE.0,
Ipv6Addr::new(0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8888)
);
assert_eq!(IPV6_PROBE.1, 80);
}
#[test]
fn local_ipv4_resolution_returns_some_when_route_available() {
if let Some(addr) = local_ipv4_address() {
assert!(matches!(addr, IpAddr::V4(_)), "expected IPv4, got {addr}");
}
}
#[test]
fn local_ipv6_resolution_returns_some_when_route_available() {
if let Some(addr) = local_ipv6_address() {
assert!(matches!(addr, IpAddr::V6(_)), "expected IPv6, got {addr}");
}
}
}