local_ip_address/
unix.rs

1use std::alloc::{alloc, dealloc, Layout};
2use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
3
4use libc::{
5    getifaddrs, strlen, c_char, ifaddrs, sockaddr_in, sockaddr_in6, AF_INET, AF_INET6, IFF_LOOPBACK,
6};
7
8use crate::Error;
9
10/// `ifaddrs` struct raw pointer alias
11type IfAddrsPtr = *mut *mut ifaddrs;
12
13/// Perform a search over the system's network interfaces using `getifaddrs`,
14/// retrieved network interfaces belonging to both socket address families
15/// `AF_INET` and `AF_INET6` are retrieved along with the interface address name.
16///
17/// # Example
18///
19/// ```
20/// use std::net::IpAddr;
21/// use local_ip_address::list_afinet_netifas;
22///
23/// let ifas = list_afinet_netifas().unwrap();
24///
25/// if let Some((_, ipaddr)) = ifas
26/// .iter()
27/// .find(|(name, ipaddr)| (*name == "en0" || *name == "epair0b") && matches!(ipaddr, IpAddr::V4(_))) {
28///     // This is your local IP address: 192.168.1.111
29///     println!("This is your local IP address: {:?}", ipaddr);
30/// }
31/// ```
32pub fn list_afinet_netifas() -> Result<Vec<(String, IpAddr)>, Error> {
33    match list_afinet_netifas_info() {
34        Ok(interfaces) => Ok(interfaces
35            .iter()
36            .map(|i| (i.iname.clone(), i.addr))
37            .collect()),
38        Err(e) => Err(e),
39    }
40}
41
42pub(crate) struct AfInetInfo {
43    pub addr: IpAddr,
44    pub iname: String,
45    pub is_loopback: bool,
46}
47
48impl AfInetInfo {
49    /// Determines if an interface is used for mobile_data
50    pub(crate) fn is_mobile_data(&self) -> bool {
51        self.iname.contains("rmnet_data")
52    }
53}
54
55// Internal method to list AF_INET info in a struct.  This method is used by
56// list_afiinet_netifas and local_ip,
57pub(crate) fn list_afinet_netifas_info() -> Result<Vec<AfInetInfo>, Error> {
58    unsafe {
59        let layout = Layout::new::<IfAddrsPtr>();
60        let ptr = alloc(layout);
61        let myaddr = ptr as IfAddrsPtr;
62        let getifaddrs_result = getifaddrs(myaddr);
63
64        if getifaddrs_result != 0 {
65            // an error occurred on getifaddrs
66            return Err(Error::StrategyError(format!(
67                "GetIfAddrs returned error: {}",
68                getifaddrs_result
69            )));
70        }
71
72        let mut interfaces: Vec<AfInetInfo> = Vec::new();
73        let ifa = myaddr;
74
75        // An instance of `ifaddrs` is build on top of a linked list where
76        // `ifaddrs.ifa_next` represent the next node in the list.
77        //
78        // To find the relevant interface address walk over the nodes of the
79        // linked list looking for interface address which belong to the socket
80        // address families AF_INET (IPv4) and AF_INET6 (IPv6)
81        loop {
82            let ifa_addr = (**ifa).ifa_addr;
83
84            match (*ifa_addr).sa_family as i32 {
85                // AF_INET IPv4 protocol implementation
86                AF_INET => {
87                    let interface_address = ifa_addr;
88                    let socket_addr_v4: *mut sockaddr_in = interface_address as *mut sockaddr_in;
89                    let in_addr = (*socket_addr_v4).sin_addr;
90                    let mut ip_addr = Ipv4Addr::from(in_addr.s_addr);
91
92                    if cfg!(target_endian = "little") {
93                        // due to a difference on how bytes are arranged on a
94                        // single word of memory by the CPU, swap bytes based
95                        // on CPU endianness to avoid having twisted IP addresses
96                        //
97                        // refer: https://github.com/rust-lang/rust/issues/48819
98                        ip_addr = Ipv4Addr::from(in_addr.s_addr.swap_bytes());
99                    }
100
101                    interfaces.push(AfInetInfo {
102                        addr: IpAddr::V4(ip_addr),
103                        iname: get_ifa_name(ifa)?,
104                        is_loopback: is_loopback_addr(ifa),
105                    });
106                }
107                // AF_INET6 IPv6 protocol implementation
108                AF_INET6 => {
109                    let interface_address = ifa_addr;
110                    let socket_addr_v6: *mut sockaddr_in6 = interface_address as *mut sockaddr_in6;
111                    let in6_addr = (*socket_addr_v6).sin6_addr;
112                    let ip_addr = Ipv6Addr::from(in6_addr.s6_addr);
113
114                    interfaces.push(AfInetInfo {
115                        addr: IpAddr::V6(ip_addr),
116                        iname: get_ifa_name(ifa)?,
117                        is_loopback: is_loopback_addr(ifa),
118                    });
119                }
120                _ => {}
121            }
122
123            // Check if we are at the end of our network interface list
124            *ifa = (**ifa).ifa_next;
125            if (*ifa).is_null() {
126                break;
127            }
128        }
129
130        dealloc(ptr, layout);
131        Ok(interfaces)
132    }
133}
134
135/// Retrieves the name of a interface address
136unsafe fn get_ifa_name(ifa: *mut *mut ifaddrs) -> Result<String, Error> {
137    let str = (*(*ifa)).ifa_name;
138    let len = strlen(str as *const c_char);
139    let slice = std::slice::from_raw_parts(str as *mut u8, len);
140    match String::from_utf8(slice.to_vec()) {
141        Ok(s) => Ok(s),
142        Err(e) => Err(Error::StrategyError(format!(
143            "Failed to retrieve interface name. The name is not a valid UTF-8 string. {}",
144            e
145        ))),
146    }
147}
148
149/// Determines if an interface address is a loopback address
150unsafe fn is_loopback_addr(ifa: *mut *mut ifaddrs) -> bool {
151    let iflags = (*(*ifa)).ifa_flags as i32;
152    (iflags & IFF_LOOPBACK) != 0
153}