1use crate::IpType;
24use crate::{Error, Result};
25#[cfg(windows)]
26use log::error;
27use log::{debug, trace};
28use std::convert::TryInto;
29use std::ffi::CStr;
30use std::mem;
31use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
32use std::ptr;
33#[cfg(unix)]
34use std::str::FromStr;
35#[cfg(windows)]
36use winapi::{
37 shared::{
38 minwindef::DWORD,
39 ntdef::{ULONG, VOID},
40 winerror, ws2def,
41 ws2def::{SOCKADDR, SOCKADDR_IN},
42 ws2ipdef::SOCKADDR_IN6,
43 },
44 um::{
45 heapapi::{GetProcessHeap, HeapAlloc, HeapFree},
46 iphlpapi::GetAdaptersAddresses,
47 iptypes::{
48 GAA_FLAG_INCLUDE_PREFIX, GAA_FLAG_SKIP_DNS_SERVER, GAA_FLAG_SKIP_MULTICAST,
49 IP_ADAPTER_ADDRESSES, IP_ADAPTER_ANYCAST_ADDRESS, IP_ADAPTER_UNICAST_ADDRESS,
50 },
51 },
52};
53#[cfg(windows)]
54const INITIAL_ALLOC_SIZE: ULONG = 15000;
55#[cfg(windows)]
56const MAX_TRIES: usize = 5;
57
58macro_rules! fail_os_err {
60 () => {
61 Err(Error::IoError(std::io::Error::last_os_error()))
62 };
63}
64
65#[cfg(unix)]
67fn get_addr_for_ifa_unix(addr: libc::ifaddrs, ip_type: Option<IpType>) -> Result<IpAddr> {
68 let sockaddr = addr.ifa_addr;
69 assert!(!sockaddr.is_null());
70 let family = libc::c_int::from(unsafe { *sockaddr }.sa_family);
71 if let Some(ip_type) = ip_type {
73 if (ip_type == IpType::Ipv4 && family != libc::AF_INET)
74 || (ip_type == IpType::Ipv6 && family != libc::AF_INET6)
75 {
76 trace!("Short-circuiting NoAddress: addr family does not match requested type");
77 return Err(Error::NoAddress);
78 }
79 } else if family != libc::AF_INET && family != libc::AF_INET6 {
80 trace!(
81 "Short-circuiting NoAddress: family type {:?} not address",
82 family
83 );
84 return Err(Error::NoAddress);
85 }
86 let socklen: libc::socklen_t = match family {
88 libc::AF_INET => mem::size_of::<libc::sockaddr_in>(),
89 libc::AF_INET6 => mem::size_of::<libc::sockaddr_in6>(),
90 _ => unreachable!(),
91 }
92 .try_into()
93 .unwrap_or_else(|_| unreachable!());
95 {
97 const MAXHOST: usize = libc::NI_MAXHOST as usize;
98 let mut host: [libc::c_char; MAXHOST] = [0; MAXHOST];
99 if unsafe {
100 libc::getnameinfo(
101 sockaddr,
102 socklen,
103 host.as_mut_ptr(),
104 libc::NI_MAXHOST,
105 ptr::null_mut(),
106 0,
107 libc::NI_NUMERICHOST,
108 )
109 } == 0
110 {
111 let address = unsafe { CStr::from_ptr(host.as_ptr()).to_bytes() };
112 let address = unsafe { std::str::from_utf8_unchecked(address) };
113 Ok(match family {
114 libc::AF_INET => IpAddr::V4(Ipv4Addr::from_str(address)?),
115 libc::AF_INET6 => IpAddr::V6(Ipv6Addr::from_str(address)?),
116 _ => unreachable!(),
117 })
118 } else {
119 fail_os_err!()
120 }
121 }
122}
123
124#[cfg(unix)]
135pub fn get_iface_addrs(ip_type: Option<IpType>, iface_name: Option<&str>) -> Result<Vec<IpAddr>> {
136 let mut result: Vec<IpAddr> = Vec::new();
138 let mut save_addrs: *mut libc::ifaddrs = unsafe { mem::zeroed() };
140 if unsafe { libc::getifaddrs(&mut save_addrs) } != 0 {
141 return fail_os_err!();
142 }
143 let mut addrs = save_addrs;
144 while !addrs.is_null() {
146 let addr = unsafe { *addrs };
147 let ifa_name = unsafe { CStr::from_ptr(addr.ifa_name).to_bytes() };
149 let ifa_name = unsafe { std::str::from_utf8_unchecked(ifa_name) };
150 trace!("Got interface {:?}", ifa_name);
151 let address = iface_name.map_or_else(
153 || get_addr_for_ifa_unix(addr, ip_type),
154 |expected_ifa_name| {
155 if ifa_name == expected_ifa_name {
156 get_addr_for_ifa_unix(addr, ip_type)
157 } else {
158 Err(Error::NoAddress)
159 }
160 },
161 );
162 if let Ok(address) = address {
163 trace!(
164 "Found good addresses of type {:?} for interface {:?}: {:?}",
165 ip_type,
166 iface_name,
167 address
168 );
169 result.push(address);
170 }
171 addrs = addr.ifa_next;
172 }
173 unsafe { libc::freeifaddrs(save_addrs) };
174 if result.is_empty() {
175 debug!("No address becase none of the interfaces has a matching one");
176 Err(Error::NoAddress)
177 } else {
178 Ok(result)
179 }
180}
181
182#[cfg(windows)]
185unsafe fn sockaddr_to_ipaddr(raw_addr: *mut SOCKADDR) -> IpAddr {
186 if i32::from(unsafe { *raw_addr }.sa_family) == ws2def::AF_INET {
187 #[allow(clippy::cast_ptr_alignment)]
188 let saddr_in = raw_addr.cast::<SOCKADDR_IN>();
189 let saddr_in_addr = unsafe { (*saddr_in).sin_addr.S_un.S_addr() };
190 IpAddr::V4(Ipv4Addr::from(*saddr_in_addr))
191 } else {
192 #[allow(clippy::cast_ptr_alignment)]
193 let saddr_in = raw_addr.cast::<SOCKADDR_IN6>();
194 let saddr_in_addr = unsafe { (*saddr_in).sin6_addr.u.Byte() };
195 IpAddr::V6(Ipv6Addr::from(*saddr_in_addr))
196 }
197}
198
199#[cfg(windows)]
202unsafe fn extract_addresses(adapter: *mut IP_ADAPTER_ADDRESSES) -> Vec<IpAddr> {
203 let mut addresses: Vec<IpAddr> = Vec::new();
204 let mut cur_unicast: *mut IP_ADAPTER_UNICAST_ADDRESS = unsafe { *adapter }.FirstUnicastAddress;
205 while !cur_unicast.is_null() {
206 let raw_addr = unsafe { *cur_unicast }.Address.lpSockaddr;
207 assert!(!raw_addr.is_null());
208 let ipaddr = unsafe { sockaddr_to_ipaddr(raw_addr) };
209 debug!(
210 "Found good unicast address on adapter {:?}: {:?}",
211 unsafe { *adapter }.FriendlyName,
212 ipaddr
213 );
214 addresses.push(ipaddr);
215 cur_unicast = unsafe { *cur_unicast }.Next;
216 }
217 let mut cur_anycast: *mut IP_ADAPTER_ANYCAST_ADDRESS = unsafe { *adapter }.FirstAnycastAddress;
218 while !cur_anycast.is_null() {
219 let raw_addr = unsafe { *cur_anycast }.Address.lpSockaddr;
220 assert!(!raw_addr.is_null());
221 let ipaddr = unsafe { sockaddr_to_ipaddr(raw_addr) };
222 debug!(
223 "Found good anycast address on adapter {:?}: {:?}",
224 unsafe { *adapter }.FriendlyName,
225 ipaddr
226 );
227 addresses.push(ipaddr);
228 cur_anycast = unsafe { *cur_anycast }.Next;
229 }
230 addresses
231}
232
233#[cfg(windows)]
248pub fn get_iface_addrs(ip_type: Option<IpType>, iface_name: Option<&str>) -> Result<Vec<IpAddr>> {
249 let family: u32 = match ip_type {
250 Some(IpType::Ipv4) => ws2def::AF_INET,
251 Some(IpType::Ipv6) => ws2def::AF_INET6,
252 None => ws2def::AF_UNSPEC,
253 }
254 .try_into()
255 .unwrap_or_else(|_| unreachable!());
257 let flags: ULONG = GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_MULTICAST;
258 let mut allocated_size: ULONG = INITIAL_ALLOC_SIZE;
260 let mut adapter_addresses: *mut IP_ADAPTER_ADDRESSES = unsafe { mem::zeroed() };
262 let mut return_value: DWORD = 0;
263 for trial in 0..MAX_TRIES {
265 adapter_addresses = unsafe { HeapAlloc(GetProcessHeap(), 0, allocated_size as usize) }
266 .cast::<IP_ADAPTER_ADDRESSES>();
267 if adapter_addresses.is_null() {
268 error!("Raw heap allocation failed");
269 return fail_os_err!();
270 }
271 return_value = unsafe {
272 GetAdaptersAddresses(
273 family,
274 flags,
275 ptr::null_mut(),
276 adapter_addresses,
277 &mut allocated_size,
278 )
279 };
280 debug!(
281 "GetAdaptersAddresses returned {:?} on the {}th trial",
282 return_value, trial
283 );
284 if return_value == winerror::ERROR_BUFFER_OVERFLOW {
285 unsafe { HeapFree(GetProcessHeap(), 0, adapter_addresses.cast::<VOID>()) };
286 } else {
287 break;
288 }
289 }
290 let result = if return_value == winerror::NO_ERROR {
291 let mut addresses: Vec<IpAddr> = Vec::new();
292 let mut curr_adapter = adapter_addresses;
293 while !curr_adapter.is_null() {
294 let adapter_name = unsafe { *curr_adapter }.FriendlyName as *const libc::c_char;
295 let adapter_name = unsafe { CStr::from_ptr(adapter_name).to_bytes() };
296 let adapter_name = unsafe { std::str::from_utf8_unchecked(adapter_name) };
297 trace!("Examining adpater {:?}", adapter_name);
298 if let Some(expected_adapter_name) = iface_name {
299 if adapter_name == expected_adapter_name {
300 let mut addrs = unsafe { extract_addresses(curr_adapter) };
301 addresses.append(&mut addrs);
302 }
303 } else {
304 let mut addrs = unsafe { extract_addresses(curr_adapter) };
305 addresses.append(&mut addrs);
306 }
307 curr_adapter = unsafe { *curr_adapter }.Next;
308 }
309 if addresses.is_empty() {
310 debug!("No address becase none of the adapters has a matching one");
311 Err(Error::NoAddress)
312 } else {
313 Ok(addresses)
314 }
315 } else {
316 Err(Error::IoError(std::io::Error::from_raw_os_error(
318 return_value.try_into().unwrap_or_else(|_| unreachable!()),
320 )))
321 };
322 unsafe {
323 HeapFree(GetProcessHeap(), 0, adapter_addresses.cast::<VOID>());
324 }
325 result
326}
327
328#[cfg(test)]
329mod test {
330 use super::get_iface_addrs;
331 use crate::Error;
332 use crate::IpType;
333
334 #[test]
335 fn test_get_iface_addrs_ipv4() {
336 match get_iface_addrs(Some(IpType::Ipv4), None) {
339 Ok(addresses) => {
340 assert!(!addresses.is_empty(), "Addresses should not be empty");
341 for address in &addresses {
342 assert!(address.is_ipv4(), "Address not IPv4: {:?}", address);
343 }
344 }
345 Err(error) => {
346 assert!(
347 matches!(error, Error::NoAddress),
348 "get_iface_addrs failed because of reasons other than NoAddress: {:?}",
349 error
350 );
351 }
352 }
353 }
354
355 #[test]
356 fn test_get_iface_addrs_ipv6() {
357 match get_iface_addrs(Some(IpType::Ipv6), None) {
358 Ok(addresses) => {
359 assert!(!addresses.is_empty(), "Addresses should not be empty");
360 for address in &addresses {
361 assert!(address.is_ipv6(), "Address not IPv6: {:?}", address);
362 }
363 }
364 Err(error) => {
365 assert!(
366 matches!(error, Error::NoAddress),
367 "get_iface_addrs failed because of reasons other than NoAddress: {:?}",
368 error
369 );
370 }
371 }
372 }
373
374 #[test]
375 fn test_get_iface_addrs_any() {
376 match get_iface_addrs(None, None) {
377 Ok(addresses) => {
378 assert!(!addresses.is_empty(), "Addresses should not be empty");
379 }
380 Err(error) => {
381 assert!(
382 matches!(error, Error::NoAddress),
383 "get_iface_addrs failed because of reasons other than NoAddress: {:?}",
384 error
385 );
386 assert!(
388 matches!(
389 get_iface_addrs(Some(IpType::Ipv6), None),
390 Err(Error::NoAddress)
391 ) || matches!(
392 get_iface_addrs(Some(IpType::Ipv4), None),
393 Err(Error::NoAddress)
394 ),
395 "Individual get_iface_addrs succeeded but generic one didn't"
396 );
397 }
398 }
399 }
400}