/rust/registry/src/index.crates.io-6f17d22bba15001f/nix-0.26.2/src/ifaddrs.rs
Line | Count | Source (jump to first uncovered line) |
1 | | //! Query network interface addresses |
2 | | //! |
3 | | //! Uses the Linux and/or BSD specific function `getifaddrs` to query the list |
4 | | //! of interfaces and their associated addresses. |
5 | | |
6 | | use cfg_if::cfg_if; |
7 | | #[cfg(any(target_os = "ios", target_os = "macos"))] |
8 | | use std::convert::TryFrom; |
9 | | use std::ffi; |
10 | | use std::iter::Iterator; |
11 | | use std::mem; |
12 | | use std::option::Option; |
13 | | |
14 | | use crate::net::if_::*; |
15 | | use crate::sys::socket::{SockaddrLike, SockaddrStorage}; |
16 | | use crate::{Errno, Result}; |
17 | | |
18 | | /// Describes a single address for an interface as returned by `getifaddrs`. |
19 | 0 | #[derive(Clone, Debug, Eq, Hash, PartialEq)] |
20 | | pub struct InterfaceAddress { |
21 | | /// Name of the network interface |
22 | | pub interface_name: String, |
23 | | /// Flags as from `SIOCGIFFLAGS` ioctl |
24 | | pub flags: InterfaceFlags, |
25 | | /// Network address of this interface |
26 | | pub address: Option<SockaddrStorage>, |
27 | | /// Netmask of this interface |
28 | | pub netmask: Option<SockaddrStorage>, |
29 | | /// Broadcast address of this interface, if applicable |
30 | | pub broadcast: Option<SockaddrStorage>, |
31 | | /// Point-to-point destination address |
32 | | pub destination: Option<SockaddrStorage>, |
33 | | } |
34 | | |
35 | | cfg_if! { |
36 | | if #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "fuchsia", target_os = "linux"))] { |
37 | 0 | fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr { |
38 | 0 | info.ifa_ifu |
39 | 0 | } |
40 | | } else { |
41 | | fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr { |
42 | | info.ifa_dstaddr |
43 | | } |
44 | | } |
45 | | } |
46 | | |
47 | | /// Workaround a bug in XNU where netmasks will always have the wrong size in |
48 | | /// the sa_len field due to the kernel ignoring trailing zeroes in the structure |
49 | | /// when setting the field. See https://github.com/nix-rust/nix/issues/1709#issuecomment-1199304470 |
50 | | /// |
51 | | /// To fix this, we stack-allocate a new sockaddr_storage, zero it out, and |
52 | | /// memcpy sa_len of the netmask to that new storage. Finally, we reset the |
53 | | /// ss_len field to sizeof(sockaddr_storage). This is supposedly valid as all |
54 | | /// members of the sockaddr_storage are "ok" with being zeroed out (there are |
55 | | /// no pointers). |
56 | | #[cfg(any(target_os = "ios", target_os = "macos"))] |
57 | | unsafe 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 | | // memcpy only sa_len bytes, assume the rest is zero |
66 | | 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 | | // Initialize ss_len to sizeof(libc::sockaddr_storage). |
73 | | (*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 | | |
83 | | impl InterfaceAddress { |
84 | | /// Create an `InterfaceAddress` from the libc struct. |
85 | 0 | fn from_libc_ifaddrs(info: &libc::ifaddrs) -> InterfaceAddress { |
86 | 0 | let ifname = unsafe { ffi::CStr::from_ptr(info.ifa_name) }; |
87 | 0 | let address = unsafe { SockaddrStorage::from_raw(info.ifa_addr, None) }; |
88 | 0 | #[cfg(any(target_os = "ios", target_os = "macos"))] |
89 | 0 | let netmask = unsafe { workaround_xnu_bug(info) }; |
90 | 0 | #[cfg(not(any(target_os = "ios", target_os = "macos")))] |
91 | 0 | let netmask = |
92 | 0 | unsafe { SockaddrStorage::from_raw(info.ifa_netmask, None) }; |
93 | 0 | let mut addr = InterfaceAddress { |
94 | 0 | interface_name: ifname.to_string_lossy().to_string(), |
95 | 0 | flags: InterfaceFlags::from_bits_truncate(info.ifa_flags as i32), |
96 | 0 | address, |
97 | 0 | netmask, |
98 | 0 | broadcast: None, |
99 | 0 | destination: None, |
100 | 0 | }; |
101 | 0 |
|
102 | 0 | let ifu = get_ifu_from_sockaddr(info); |
103 | 0 | if addr.flags.contains(InterfaceFlags::IFF_POINTOPOINT) { |
104 | 0 | addr.destination = unsafe { SockaddrStorage::from_raw(ifu, None) }; |
105 | 0 | } else if addr.flags.contains(InterfaceFlags::IFF_BROADCAST) { |
106 | 0 | addr.broadcast = unsafe { SockaddrStorage::from_raw(ifu, None) }; |
107 | 0 | } |
108 | | |
109 | 0 | addr |
110 | 0 | } |
111 | | } |
112 | | |
113 | | /// Holds the results of `getifaddrs`. |
114 | | /// |
115 | | /// Use the function `getifaddrs` to create this Iterator. Note that the |
116 | | /// actual list of interfaces can be iterated once and will be freed as |
117 | | /// soon as the Iterator goes out of scope. |
118 | 0 | #[derive(Debug, Eq, Hash, PartialEq)] |
119 | | pub struct InterfaceAddressIterator { |
120 | | base: *mut libc::ifaddrs, |
121 | | next: *mut libc::ifaddrs, |
122 | | } |
123 | | |
124 | | impl Drop for InterfaceAddressIterator { |
125 | 0 | fn drop(&mut self) { |
126 | 0 | unsafe { libc::freeifaddrs(self.base) }; |
127 | 0 | } |
128 | | } |
129 | | |
130 | | impl Iterator for InterfaceAddressIterator { |
131 | | type Item = InterfaceAddress; |
132 | 0 | fn next(&mut self) -> Option<<Self as Iterator>::Item> { |
133 | 0 | match unsafe { self.next.as_ref() } { |
134 | 0 | Some(ifaddr) => { |
135 | 0 | self.next = ifaddr.ifa_next; |
136 | 0 | Some(InterfaceAddress::from_libc_ifaddrs(ifaddr)) |
137 | | } |
138 | 0 | None => None, |
139 | | } |
140 | 0 | } |
141 | | } |
142 | | |
143 | | /// Get interface addresses using libc's `getifaddrs` |
144 | | /// |
145 | | /// Note that the underlying implementation differs between OSes. Only the |
146 | | /// most common address families are supported by the nix crate (due to |
147 | | /// lack of time and complexity of testing). The address family is encoded |
148 | | /// in the specific variant of `SockaddrStorage` returned for the fields |
149 | | /// `address`, `netmask`, `broadcast`, and `destination`. For any entry not |
150 | | /// supported, the returned list will contain a `None` entry. |
151 | | /// |
152 | | /// # Example |
153 | | /// ``` |
154 | | /// let addrs = nix::ifaddrs::getifaddrs().unwrap(); |
155 | | /// for ifaddr in addrs { |
156 | | /// match ifaddr.address { |
157 | | /// Some(address) => { |
158 | | /// println!("interface {} address {}", |
159 | | /// ifaddr.interface_name, address); |
160 | | /// }, |
161 | | /// None => { |
162 | | /// println!("interface {} with unsupported address family", |
163 | | /// ifaddr.interface_name); |
164 | | /// } |
165 | | /// } |
166 | | /// } |
167 | | /// ``` |
168 | 0 | pub fn getifaddrs() -> Result<InterfaceAddressIterator> { |
169 | 0 | let mut addrs = mem::MaybeUninit::<*mut libc::ifaddrs>::uninit(); |
170 | 0 | unsafe { |
171 | 0 | Errno::result(libc::getifaddrs(addrs.as_mut_ptr())).map(|_| { |
172 | 0 | InterfaceAddressIterator { |
173 | 0 | base: addrs.assume_init(), |
174 | 0 | next: addrs.assume_init(), |
175 | 0 | } |
176 | 0 | }) |
177 | 0 | } |
178 | 0 | } |
179 | | |
180 | | #[cfg(test)] |
181 | | mod tests { |
182 | | use super::*; |
183 | | |
184 | | // Only checks if `getifaddrs` can be invoked without panicking. |
185 | | #[test] |
186 | | fn test_getifaddrs() { |
187 | | let _ = getifaddrs(); |
188 | | } |
189 | | |
190 | | // Ensures getting the netmask works, and in particular that |
191 | | // `workaround_xnu_bug` works properly. |
192 | | #[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 | | } |