1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
use std::alloc::{alloc, dealloc, Layout};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

use libc::{
    getifaddrs, strlen, c_char, ifaddrs, sockaddr_in, sockaddr_in6, AF_INET, AF_INET6, IFF_LOOPBACK,
};

use crate::Error;

/// `ifaddrs` struct raw pointer alias
type IfAddrsPtr = *mut *mut ifaddrs;

/// Perform a search over the system's network interfaces using `getifaddrs`,
/// retrieved network interfaces belonging to both socket address families
/// `AF_INET` and `AF_INET6` are retrieved along with the interface address name.
///
/// # Example
///
/// ```
/// use std::net::IpAddr;
/// use local_ip_address::list_afinet_netifas;
///
/// let ifas = list_afinet_netifas().unwrap();
///
/// if let Some((_, ipaddr)) = ifas
/// .iter()
/// .find(|(name, ipaddr)| (*name == "en0" || *name == "epair0b") && matches!(ipaddr, IpAddr::V4(_))) {
///     // This is your local IP address: 192.168.1.111
///     println!("This is your local IP address: {:?}", ipaddr);
/// }
/// ```
pub fn list_afinet_netifas() -> Result<Vec<(String, IpAddr)>, Error> {
    match list_afinet_netifas_info() {
        Ok(interfaces) => Ok(interfaces
            .iter()
            .map(|i| (i.iname.clone(), i.addr))
            .collect()),
        Err(e) => Err(e),
    }
}

pub(crate) struct AfInetInfo {
    pub addr: IpAddr,
    pub iname: String,
    pub is_loopback: bool,
}

impl AfInetInfo {
    /// Determines if an interface is used for mobile_data
    pub(crate) fn is_mobile_data(&self) -> bool {
        self.iname.contains("rmnet_data")
    }
}

// Internal method to list AF_INET info in a struct.  This method is used by
// list_afiinet_netifas and local_ip,
pub(crate) fn list_afinet_netifas_info() -> Result<Vec<AfInetInfo>, Error> {
    unsafe {
        let layout = Layout::new::<IfAddrsPtr>();
        let ptr = alloc(layout);
        let myaddr = ptr as IfAddrsPtr;
        let getifaddrs_result = getifaddrs(myaddr);

        if getifaddrs_result != 0 {
            // an error occurred on getifaddrs
            return Err(Error::StrategyError(format!(
                "GetIfAddrs returned error: {}",
                getifaddrs_result
            )));
        }

        let mut interfaces: Vec<AfInetInfo> = Vec::new();
        let ifa = myaddr;

        // An instance of `ifaddrs` is build on top of a linked list where
        // `ifaddrs.ifa_next` represent the next node in the list.
        //
        // To find the relevant interface address walk over the nodes of the
        // linked list looking for interface address which belong to the socket
        // address families AF_INET (IPv4) and AF_INET6 (IPv6)
        loop {
            let ifa_addr = (**ifa).ifa_addr;

            match (*ifa_addr).sa_family as i32 {
                // AF_INET IPv4 protocol implementation
                AF_INET => {
                    let interface_address = ifa_addr;
                    let socket_addr_v4: *mut sockaddr_in = interface_address as *mut sockaddr_in;
                    let in_addr = (*socket_addr_v4).sin_addr;
                    let mut ip_addr = Ipv4Addr::from(in_addr.s_addr);

                    if cfg!(target_endian = "little") {
                        // due to a difference on how bytes are arranged on a
                        // single word of memory by the CPU, swap bytes based
                        // on CPU endianness to avoid having twisted IP addresses
                        //
                        // refer: https://github.com/rust-lang/rust/issues/48819
                        ip_addr = Ipv4Addr::from(in_addr.s_addr.swap_bytes());
                    }

                    interfaces.push(AfInetInfo {
                        addr: IpAddr::V4(ip_addr),
                        iname: get_ifa_name(ifa)?,
                        is_loopback: is_loopback_addr(ifa),
                    });
                }
                // AF_INET6 IPv6 protocol implementation
                AF_INET6 => {
                    let interface_address = ifa_addr;
                    let socket_addr_v6: *mut sockaddr_in6 = interface_address as *mut sockaddr_in6;
                    let in6_addr = (*socket_addr_v6).sin6_addr;
                    let ip_addr = Ipv6Addr::from(in6_addr.s6_addr);

                    interfaces.push(AfInetInfo {
                        addr: IpAddr::V6(ip_addr),
                        iname: get_ifa_name(ifa)?,
                        is_loopback: is_loopback_addr(ifa),
                    });
                }
                _ => {}
            }

            // Check if we are at the end of our network interface list
            *ifa = (**ifa).ifa_next;
            if (*ifa).is_null() {
                break;
            }
        }

        dealloc(ptr, layout);
        Ok(interfaces)
    }
}

/// Retrieves the name of a interface address
unsafe fn get_ifa_name(ifa: *mut *mut ifaddrs) -> Result<String, Error> {
    let str = (*(*ifa)).ifa_name;
    let len = strlen(str as *const c_char);
    let slice = std::slice::from_raw_parts(str as *mut u8, len);
    match String::from_utf8(slice.to_vec()) {
        Ok(s) => Ok(s),
        Err(e) => Err(Error::StrategyError(format!(
            "Failed to retrieve interface name. The name is not a valid UTF-8 string. {}",
            e
        ))),
    }
}

/// Determines if an interface address is a loopback address
unsafe fn is_loopback_addr(ifa: *mut *mut ifaddrs) -> bool {
    let iflags = (*(*ifa)).ifa_flags as i32;
    (iflags & IFF_LOOPBACK) != 0
}