Skip to main content

tiny_dlna/
net.rs

1//! Picking the local IP address other devices should use to reach us.
2
3use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
4
5/// The local IPv4 address used to route packets to `target`.
6/// No packet is sent: connecting a UDP socket only selects a route.
7pub fn local_ip_towards(target: IpAddr) -> Option<Ipv4Addr> {
8    let sock = UdpSocket::bind((Ipv4Addr::UNSPECIFIED, 0)).ok()?;
9    sock.connect(SocketAddr::new(target, 9)).ok()?;
10    match sock.local_addr().ok()?.ip() {
11        IpAddr::V4(ip) if !ip.is_unspecified() && !ip.is_loopback() => Some(ip),
12        _ => None,
13    }
14}
15
16/// A best-guess LAN address of this host, used when no route-based answer
17/// is available. Prefers 192.168.x.x, then other private ranges, then any
18/// non-loopback address; within a class the smallest address wins, which
19/// usually skips VM bridges like 192.168.65.1.
20pub fn host_ip() -> Option<Ipv4Addr> {
21    let mut ips = lan_ips();
22    let rank = |ip: &Ipv4Addr| match ip.octets() {
23        [192, 168, ..] => 0,
24        _ if ip.is_private() => 1,
25        _ => 2,
26    };
27    ips.sort_by_key(|ip| (rank(ip), *ip));
28    ips.first().copied()
29}
30
31/// All non-loopback, non-link-local IPv4 addresses of this host, sorted.
32pub fn lan_ips() -> Vec<Ipv4Addr> {
33    let mut ips: Vec<Ipv4Addr> = if_addrs::get_if_addrs()
34        .unwrap_or_default()
35        .into_iter()
36        .filter_map(|iface| match iface.ip() {
37            IpAddr::V4(ip) if !ip.is_loopback() && !ip.is_link_local() => Some(ip),
38            _ => None,
39        })
40        .collect();
41    ips.sort();
42    ips.dedup();
43    ips
44}
45
46/// The address `peer` should use to reach this host.
47pub fn local_ip_for(peer: IpAddr) -> Option<Ipv4Addr> {
48    local_ip_towards(peer).or_else(host_ip)
49}