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}