1use cfg_if::cfg_if;
7#[cfg(any(target_os = "ios", target_os = "macos"))]
8use std::convert::TryFrom;
9use std::ffi;
10use std::iter::Iterator;
11use std::mem;
12use std::option::Option;
13
14use crate::net::if_::*;
15use crate::sys::socket::{SockaddrLike, SockaddrStorage};
16use crate::{Errno, Result};
17
18#[derive(Clone, Debug, Eq, Hash, PartialEq)]
20pub struct InterfaceAddress {
21 pub interface_name: String,
23 pub flags: InterfaceFlags,
25 pub address: Option<SockaddrStorage>,
27 pub netmask: Option<SockaddrStorage>,
29 pub broadcast: Option<SockaddrStorage>,
31 pub destination: Option<SockaddrStorage>,
33}
34
35cfg_if! {
36 if #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "fuchsia", target_os = "linux"))] {
37 fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr {
38 info.ifa_ifu
39 }
40 } else {
41 fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr {
42 info.ifa_dstaddr
43 }
44 }
45}
46
47#[cfg(any(target_os = "ios", target_os = "macos"))]
57unsafe fn workaround_xnu_bug(info: &libc::ifaddrs) -> Option<SockaddrStorage> {
58 let src_sock = info.ifa_netmask;
59 if src_sock.is_null() {
60 return None;
61 }
62
63 let mut dst_sock = mem::MaybeUninit::<libc::sockaddr_storage>::zeroed();
64
65 std::ptr::copy_nonoverlapping(
67 src_sock as *const u8,
68 dst_sock.as_mut_ptr() as *mut u8,
69 (*src_sock).sa_len.into(),
70 );
71
72 (*dst_sock.as_mut_ptr()).ss_len =
74 u8::try_from(mem::size_of::<libc::sockaddr_storage>()).unwrap();
75 let dst_sock = dst_sock.assume_init();
76
77 let dst_sock_ptr =
78 &dst_sock as *const libc::sockaddr_storage as *const libc::sockaddr;
79
80 SockaddrStorage::from_raw(dst_sock_ptr, None)
81}
82
83impl InterfaceAddress {
84 fn from_libc_ifaddrs(info: &libc::ifaddrs) -> InterfaceAddress {
86 let ifname = unsafe { ffi::CStr::from_ptr(info.ifa_name) };
87 let address = unsafe { SockaddrStorage::from_raw(info.ifa_addr, None) };
88 #[cfg(any(target_os = "ios", target_os = "macos"))]
89 let netmask = unsafe { workaround_xnu_bug(info) };
90 #[cfg(not(any(target_os = "ios", target_os = "macos")))]
91 let netmask =
92 unsafe { SockaddrStorage::from_raw(info.ifa_netmask, None) };
93 let mut addr = InterfaceAddress {
94 interface_name: ifname.to_string_lossy().to_string(),
95 flags: InterfaceFlags::from_bits_truncate(info.ifa_flags as i32),
96 address,
97 netmask,
98 broadcast: None,
99 destination: None,
100 };
101
102 let ifu = get_ifu_from_sockaddr(info);
103 if addr.flags.contains(InterfaceFlags::IFF_POINTOPOINT) {
104 addr.destination = unsafe { SockaddrStorage::from_raw(ifu, None) };
105 } else if addr.flags.contains(InterfaceFlags::IFF_BROADCAST) {
106 addr.broadcast = unsafe { SockaddrStorage::from_raw(ifu, None) };
107 }
108
109 addr
110 }
111}
112
113#[derive(Debug, Eq, Hash, PartialEq)]
119pub struct InterfaceAddressIterator {
120 base: *mut libc::ifaddrs,
121 next: *mut libc::ifaddrs,
122}
123
124impl Drop for InterfaceAddressIterator {
125 fn drop(&mut self) {
126 unsafe { libc::freeifaddrs(self.base) };
127 }
128}
129
130impl Iterator for InterfaceAddressIterator {
131 type Item = InterfaceAddress;
132 fn next(&mut self) -> Option<<Self as Iterator>::Item> {
133 match unsafe { self.next.as_ref() } {
134 Some(ifaddr) => {
135 self.next = ifaddr.ifa_next;
136 Some(InterfaceAddress::from_libc_ifaddrs(ifaddr))
137 }
138 None => None,
139 }
140 }
141}
142
143pub fn getifaddrs() -> Result<InterfaceAddressIterator> {
169 let mut addrs = mem::MaybeUninit::<*mut libc::ifaddrs>::uninit();
170 unsafe {
171 Errno::result(libc::getifaddrs(addrs.as_mut_ptr())).map(|_| {
172 InterfaceAddressIterator {
173 base: addrs.assume_init(),
174 next: addrs.assume_init(),
175 }
176 })
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183
184 #[test]
186 fn test_getifaddrs() {
187 let _ = getifaddrs();
188 }
189
190 #[test]
193 fn test_getifaddrs_netmask_correct() {
194 let addrs = getifaddrs().unwrap();
195 for iface in addrs {
196 let sock = if let Some(sock) = iface.netmask {
197 sock
198 } else {
199 continue;
200 };
201 if sock.family() == Some(crate::sys::socket::AddressFamily::Inet) {
202 let _ = sock.as_sockaddr_in().unwrap();
203 return;
204 } else if sock.family()
205 == Some(crate::sys::socket::AddressFamily::Inet6)
206 {
207 let _ = sock.as_sockaddr_in6().unwrap();
208 return;
209 }
210 }
211 panic!("No address?");
212 }
213}