/rust/registry/src/index.crates.io-1949cf8c6b5b557f/nix-0.30.1/src/ifaddrs.rs
Line | Count | Source |
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(apple_targets)] |
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 | | #[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(linux_android, target_os = "emscripten", target_os = "fuchsia"))] { |
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(apple_targets)] |
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 | | let dst_sock = unsafe { |
66 | | // memcpy only sa_len bytes, assume the rest is zero |
67 | | std::ptr::copy_nonoverlapping( |
68 | | src_sock as *const u8, |
69 | | dst_sock.as_mut_ptr().cast(), |
70 | | (*src_sock).sa_len.into(), |
71 | | ); |
72 | | |
73 | | // Initialize ss_len to sizeof(libc::sockaddr_storage). |
74 | | (*dst_sock.as_mut_ptr()).ss_len = |
75 | | u8::try_from(mem::size_of::<libc::sockaddr_storage>()).unwrap(); |
76 | | dst_sock.assume_init() |
77 | | }; |
78 | | |
79 | | let dst_sock_ptr = |
80 | | &dst_sock as *const libc::sockaddr_storage as *const libc::sockaddr; |
81 | | |
82 | | unsafe { SockaddrStorage::from_raw(dst_sock_ptr, None) } |
83 | | } |
84 | | |
85 | | impl InterfaceAddress { |
86 | | /// Create an `InterfaceAddress` from the libc struct. |
87 | 0 | fn from_libc_ifaddrs(info: &libc::ifaddrs) -> InterfaceAddress { |
88 | 0 | let ifname = unsafe { ffi::CStr::from_ptr(info.ifa_name) }; |
89 | 0 | let address = unsafe { SockaddrStorage::from_raw(info.ifa_addr, None) }; |
90 | | #[cfg(apple_targets)] |
91 | | let netmask = unsafe { workaround_xnu_bug(info) }; |
92 | | #[cfg(not(apple_targets))] |
93 | 0 | let netmask = |
94 | 0 | unsafe { SockaddrStorage::from_raw(info.ifa_netmask, None) }; |
95 | 0 | let mut addr = InterfaceAddress { |
96 | 0 | interface_name: ifname.to_string_lossy().into_owned(), |
97 | 0 | flags: InterfaceFlags::from_bits_truncate( |
98 | 0 | info.ifa_flags as IflagsType, |
99 | 0 | ), |
100 | 0 | address, |
101 | 0 | netmask, |
102 | 0 | broadcast: None, |
103 | 0 | destination: None, |
104 | 0 | }; |
105 | | |
106 | 0 | let ifu = get_ifu_from_sockaddr(info); |
107 | 0 | if addr.flags.contains(InterfaceFlags::IFF_POINTOPOINT) { |
108 | 0 | addr.destination = unsafe { SockaddrStorage::from_raw(ifu, None) }; |
109 | 0 | } else if addr.flags.contains(InterfaceFlags::IFF_BROADCAST) { |
110 | 0 | addr.broadcast = unsafe { SockaddrStorage::from_raw(ifu, None) }; |
111 | 0 | } |
112 | | |
113 | 0 | addr |
114 | 0 | } |
115 | | } |
116 | | |
117 | | /// Holds the results of `getifaddrs`. |
118 | | /// |
119 | | /// Use the function `getifaddrs` to create this Iterator. Note that the |
120 | | /// actual list of interfaces can be iterated once and will be freed as |
121 | | /// soon as the Iterator goes out of scope. |
122 | | #[derive(Debug, Eq, Hash, PartialEq)] |
123 | | pub struct InterfaceAddressIterator { |
124 | | base: *mut libc::ifaddrs, |
125 | | next: *mut libc::ifaddrs, |
126 | | } |
127 | | |
128 | | impl Drop for InterfaceAddressIterator { |
129 | 0 | fn drop(&mut self) { |
130 | 0 | unsafe { libc::freeifaddrs(self.base) }; |
131 | 0 | } |
132 | | } |
133 | | |
134 | | impl Iterator for InterfaceAddressIterator { |
135 | | type Item = InterfaceAddress; |
136 | 0 | fn next(&mut self) -> Option<<Self as Iterator>::Item> { |
137 | 0 | match unsafe { self.next.as_ref() } { |
138 | 0 | Some(ifaddr) => { |
139 | 0 | self.next = ifaddr.ifa_next; |
140 | 0 | Some(InterfaceAddress::from_libc_ifaddrs(ifaddr)) |
141 | | } |
142 | 0 | None => None, |
143 | | } |
144 | 0 | } |
145 | | } |
146 | | |
147 | | /// Get interface addresses using libc's `getifaddrs` |
148 | | /// |
149 | | /// Note that the underlying implementation differs between OSes. Only the |
150 | | /// most common address families are supported by the nix crate (due to |
151 | | /// lack of time and complexity of testing). The address family is encoded |
152 | | /// in the specific variant of `SockaddrStorage` returned for the fields |
153 | | /// `address`, `netmask`, `broadcast`, and `destination`. For any entry not |
154 | | /// supported, the returned list will contain a `None` entry. |
155 | | /// |
156 | | /// # Example |
157 | | /// ``` |
158 | | /// let addrs = nix::ifaddrs::getifaddrs().unwrap(); |
159 | | /// for ifaddr in addrs { |
160 | | /// match ifaddr.address { |
161 | | /// Some(address) => { |
162 | | /// println!("interface {} address {}", |
163 | | /// ifaddr.interface_name, address); |
164 | | /// }, |
165 | | /// None => { |
166 | | /// println!("interface {} with unsupported address family", |
167 | | /// ifaddr.interface_name); |
168 | | /// } |
169 | | /// } |
170 | | /// } |
171 | | /// ``` |
172 | 0 | pub fn getifaddrs() -> Result<InterfaceAddressIterator> { |
173 | 0 | let mut addrs = mem::MaybeUninit::<*mut libc::ifaddrs>::uninit(); |
174 | | unsafe { |
175 | 0 | Errno::result(libc::getifaddrs(addrs.as_mut_ptr())).map(|_| { |
176 | 0 | InterfaceAddressIterator { |
177 | 0 | base: addrs.assume_init(), |
178 | 0 | next: addrs.assume_init(), |
179 | 0 | } |
180 | 0 | }) |
181 | | } |
182 | 0 | } |
183 | | |
184 | | #[cfg(test)] |
185 | | mod tests { |
186 | | use super::*; |
187 | | |
188 | | // Only checks if `getifaddrs` can be invoked without panicking. |
189 | | #[test] |
190 | | fn test_getifaddrs() { |
191 | | let _ = getifaddrs(); |
192 | | } |
193 | | |
194 | | // Ensures getting the netmask works, and in particular that |
195 | | // `workaround_xnu_bug` works properly. |
196 | | #[test] |
197 | | fn test_getifaddrs_netmask_correct() { |
198 | | let addrs = getifaddrs().unwrap(); |
199 | | for iface in addrs { |
200 | | let sock = if let Some(sock) = iface.netmask { |
201 | | sock |
202 | | } else { |
203 | | continue; |
204 | | }; |
205 | | if sock.family() == Some(crate::sys::socket::AddressFamily::Inet) { |
206 | | let _ = sock.as_sockaddr_in().unwrap(); |
207 | | return; |
208 | | } else if sock.family() |
209 | | == Some(crate::sys::socket::AddressFamily::Inet6) |
210 | | { |
211 | | let _ = sock.as_sockaddr_in6().unwrap(); |
212 | | return; |
213 | | } |
214 | | } |
215 | | panic!("No address?"); |
216 | | } |
217 | | } |