1 // Copyright 2018 MaidSafe.net limited.
2 //
3 // This SAFE Network Software is licensed to you under the MIT license <LICENSE-MIT
4 // http://opensource.org/licenses/MIT> or the Modified BSD license <LICENSE-BSD
5 // https://opensource.org/licenses/BSD-3-Clause>, at your option. This file may not be copied,
6 // modified, or distributed except according to those terms. Please review the Licences for the
7 // specific language governing permissions and limitations relating to use of the SAFE Network
8 // Software.
9
10 //! get_if_addrs
11
12 #![doc(
13 html_logo_url = "https://raw.githubusercontent.com/maidsafe/QA/master/Images/
14 maidsafe_logo.png",
15 html_favicon_url = "http://maidsafe.net/img/favicon.ico",
16 html_root_url = "http://maidsafe.github.io/get_if_addrs"
17 )]
18 // For explanation of lint checks, run `rustc -W help` or see
19 // https://github.com/maidsafe/QA/blob/master/Documentation/Rust%20Lint%20Checks.md
20 #![forbid(
21 exceeding_bitshifts, mutable_transmutes, no_mangle_const_items, unknown_crate_types, warnings
22 )]
23 #![deny(
24 bad_style, deprecated, improper_ctypes, missing_docs, non_shorthand_field_patterns,
25 overflowing_literals, plugin_as_library, private_no_mangle_fns, private_no_mangle_statics,
26 stable_features, unconditional_recursion, unknown_lints, unsafe_code, unused, unused_allocation,
27 unused_attributes, unused_comparisons, unused_features, unused_parens, while_true
28 )]
29 #![warn(
30 trivial_casts, trivial_numeric_casts, unused_extern_crates, unused_import_braces,
31 unused_qualifications, unused_results
32 )]
33 #![allow(
34 box_pointers, missing_copy_implementations, missing_debug_implementations,
35 variant_size_differences
36 )]
37 #![cfg_attr(
38 feature = "cargo-clippy",
39 deny(clippy, unicode_not_nfc, wrong_pub_self_convention, option_unwrap_used)
40 )]
41 #![cfg_attr(feature = "cargo-clippy", allow(use_debug, too_many_arguments))]
42
43 #[cfg(windows)]
44 extern crate winapi;
45
46 use std::io;
47 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
48 #[cfg(test)]
49 #[macro_use]
50 extern crate unwrap;
51 extern crate c_linked_list;
52 #[cfg(target_os = "android")]
53 extern crate get_if_addrs_sys;
54 extern crate libc;
55
56 /// Details about an interface on this host
57 #[derive(Debug, PartialEq, Eq, Hash, Clone)]
58 pub struct Interface {
59 /// The name of the interface.
60 pub name: String,
61 /// The address details of the interface.
62 pub addr: IfAddr,
63 }
64
65 /// Details about the address of an interface on this host
66 #[derive(Debug, PartialEq, Eq, Hash, Clone)]
67 pub enum IfAddr {
68 /// This is an Ipv4 interface.
69 V4(Ifv4Addr),
70 /// This is an Ipv6 interface.
71 V6(Ifv6Addr),
72 }
73
74 /// Details about the ipv4 address of an interface on this host
75 #[derive(Debug, PartialEq, Eq, Hash, Clone)]
76 pub struct Ifv4Addr {
77 /// The IP address of the interface.
78 pub ip: Ipv4Addr,
79 /// The netmask of the interface.
80 pub netmask: Ipv4Addr,
81 /// The broadcast address of the interface.
82 pub broadcast: Option<Ipv4Addr>,
83 }
84
85 /// Details about the ipv6 address of an interface on this host
86 #[derive(Debug, PartialEq, Eq, Hash, Clone)]
87 pub struct Ifv6Addr {
88 /// The IP address of the interface.
89 pub ip: Ipv6Addr,
90 /// The netmask of the interface.
91 pub netmask: Ipv6Addr,
92 /// The broadcast address of the interface.
93 pub broadcast: Option<Ipv6Addr>,
94 }
95
96 impl Interface {
97 /// Check whether this is a loopback interface.
is_loopback(&self) -> bool98 pub fn is_loopback(&self) -> bool {
99 self.addr.is_loopback()
100 }
101
102 /// Get the IP address of this interface.
ip(&self) -> IpAddr103 pub fn ip(&self) -> IpAddr {
104 self.addr.ip()
105 }
106 }
107
108 impl IfAddr {
109 /// Check whether this is a loopback address.
is_loopback(&self) -> bool110 pub fn is_loopback(&self) -> bool {
111 match *self {
112 IfAddr::V4(ref ifv4_addr) => ifv4_addr.is_loopback(),
113 IfAddr::V6(ref ifv6_addr) => ifv6_addr.is_loopback(),
114 }
115 }
116
117 /// Get the IP address of this interface address.
ip(&self) -> IpAddr118 pub fn ip(&self) -> IpAddr {
119 match *self {
120 IfAddr::V4(ref ifv4_addr) => IpAddr::V4(ifv4_addr.ip),
121 IfAddr::V6(ref ifv6_addr) => IpAddr::V6(ifv6_addr.ip),
122 }
123 }
124 }
125
126 impl Ifv4Addr {
127 /// Check whether this is a loopback address.
is_loopback(&self) -> bool128 pub fn is_loopback(&self) -> bool {
129 self.ip.octets()[0] == 127
130 }
131 }
132
133 impl Ifv6Addr {
134 /// Check whether this is a loopback address.
is_loopback(&self) -> bool135 pub fn is_loopback(&self) -> bool {
136 self.ip.segments() == [0, 0, 0, 0, 0, 0, 0, 1]
137 }
138 }
139
140 #[cfg(not(windows))]
141 mod getifaddrs_posix {
142 use super::{IfAddr, Ifv4Addr, Ifv6Addr, Interface};
143 use c_linked_list::CLinkedListMut;
144 #[cfg(target_os = "android")]
145 use get_if_addrs_sys::freeifaddrs as posix_freeifaddrs;
146 #[cfg(target_os = "android")]
147 use get_if_addrs_sys::getifaddrs as posix_getifaddrs;
148 #[cfg(target_os = "android")]
149 use get_if_addrs_sys::ifaddrs as posix_ifaddrs;
150 #[cfg(not(target_os = "android"))]
151 use libc::freeifaddrs as posix_freeifaddrs;
152 #[cfg(not(target_os = "android"))]
153 use libc::getifaddrs as posix_getifaddrs;
154 #[cfg(not(target_os = "android"))]
155 use libc::ifaddrs as posix_ifaddrs;
156 use libc::sockaddr as posix_sockaddr;
157 use libc::sockaddr_in as posix_sockaddr_in;
158 use libc::sockaddr_in6 as posix_sockaddr_in6;
159 use libc::{AF_INET6, AF_INET};
160 use std::ffi::CStr;
161 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
162 use std::{io, mem};
163
164 #[allow(unsafe_code)]
sockaddr_to_ipaddr(sockaddr: *const posix_sockaddr) -> Option<IpAddr>165 fn sockaddr_to_ipaddr(sockaddr: *const posix_sockaddr) -> Option<IpAddr> {
166 if sockaddr.is_null() {
167 return None;
168 }
169
170 let sa_family = u32::from(unsafe { *sockaddr }.sa_family);
171
172 if sa_family == AF_INET as u32 {
173 #[cfg_attr(feature = "cargo-clippy", allow(cast_ptr_alignment))]
174 let sa = &unsafe { *(sockaddr as *const posix_sockaddr_in) };
175 Some(IpAddr::V4(Ipv4Addr::new(
176 ((sa.sin_addr.s_addr) & 255) as u8,
177 ((sa.sin_addr.s_addr >> 8) & 255) as u8,
178 ((sa.sin_addr.s_addr >> 16) & 255) as u8,
179 ((sa.sin_addr.s_addr >> 24) & 255) as u8,
180 )))
181 } else if sa_family == AF_INET6 as u32 {
182 #[cfg_attr(feature = "cargo-clippy", allow(cast_ptr_alignment))]
183 let sa = &unsafe { *(sockaddr as *const posix_sockaddr_in6) };
184 // Ignore all fe80:: addresses as these are link locals
185 if sa.sin6_addr.s6_addr[0] == 0xfe && sa.sin6_addr.s6_addr[1] == 0x80 {
186 return None;
187 }
188 Some(IpAddr::V6(Ipv6Addr::from(sa.sin6_addr.s6_addr)))
189 } else {
190 None
191 }
192 }
193
194 #[cfg(any(target_os = "linux", target_os = "android", target_os = "nacl"))]
do_broadcast(ifaddr: &posix_ifaddrs) -> Option<IpAddr>195 fn do_broadcast(ifaddr: &posix_ifaddrs) -> Option<IpAddr> {
196 sockaddr_to_ipaddr(ifaddr.ifa_ifu)
197 }
198
199 #[cfg(
200 any(target_os = "freebsd", target_os = "ios", target_os = "macos", target_os = "openbsd", target_os = "dragonfly")
201 )]
do_broadcast(ifaddr: &posix_ifaddrs) -> Option<IpAddr>202 fn do_broadcast(ifaddr: &posix_ifaddrs) -> Option<IpAddr> {
203 sockaddr_to_ipaddr(ifaddr.ifa_dstaddr)
204 }
205
206 /// Return a vector of IP details for all the valid interfaces on this host
207 #[allow(unsafe_code)]
208 #[allow(trivial_casts)]
get_if_addrs() -> io::Result<Vec<Interface>>209 pub fn get_if_addrs() -> io::Result<Vec<Interface>> {
210 let mut ret = Vec::<Interface>::new();
211 let mut ifaddrs: *mut posix_ifaddrs;
212 unsafe {
213 ifaddrs = mem::uninitialized();
214 if -1 == posix_getifaddrs(&mut ifaddrs) {
215 return Err(io::Error::last_os_error());
216 }
217 }
218
219 for ifaddr in unsafe { CLinkedListMut::from_ptr(ifaddrs, |a| a.ifa_next) }.iter() {
220 if ifaddr.ifa_addr.is_null() {
221 continue;
222 }
223 let name = unsafe { CStr::from_ptr(ifaddr.ifa_name as *const _) }
224 .to_string_lossy()
225 .into_owned();
226 let addr = match sockaddr_to_ipaddr(ifaddr.ifa_addr) {
227 None => continue,
228 Some(IpAddr::V4(ipv4_addr)) => {
229 let netmask = match sockaddr_to_ipaddr(ifaddr.ifa_netmask) {
230 Some(IpAddr::V4(netmask)) => netmask,
231 _ => Ipv4Addr::new(0, 0, 0, 0),
232 };
233 let broadcast = if (ifaddr.ifa_flags & 2) != 0 {
234 match do_broadcast(ifaddr) {
235 Some(IpAddr::V4(broadcast)) => Some(broadcast),
236 _ => None,
237 }
238 } else {
239 None
240 };
241 IfAddr::V4(Ifv4Addr {
242 ip: ipv4_addr,
243 netmask,
244 broadcast,
245 })
246 }
247 Some(IpAddr::V6(ipv6_addr)) => {
248 let netmask = match sockaddr_to_ipaddr(ifaddr.ifa_netmask) {
249 Some(IpAddr::V6(netmask)) => netmask,
250 _ => Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0),
251 };
252 let broadcast = if (ifaddr.ifa_flags & 2) != 0 {
253 match do_broadcast(ifaddr) {
254 Some(IpAddr::V6(broadcast)) => Some(broadcast),
255 _ => None,
256 }
257 } else {
258 None
259 };
260 IfAddr::V6(Ifv6Addr {
261 ip: ipv6_addr,
262 netmask,
263 broadcast,
264 })
265 }
266 };
267 ret.push(Interface { name, addr });
268 }
269 unsafe {
270 posix_freeifaddrs(ifaddrs);
271 }
272 Ok(ret)
273 }
274 }
275
276 /// Get a list of all the network interfaces on this machine along with their IP info.
277 #[cfg(not(windows))]
get_if_addrs() -> io::Result<Vec<Interface>>278 pub fn get_if_addrs() -> io::Result<Vec<Interface>> {
279 getifaddrs_posix::get_if_addrs()
280 }
281
282 #[cfg(windows)]
283 mod getifaddrs_windows {
284 use super::{IfAddr, Ifv4Addr, Ifv6Addr, Interface};
285 use c_linked_list::CLinkedListConst;
286 use libc;
287 use libc::{c_char, c_int, c_ulong, c_void, size_t};
288 use std::ffi::CStr;
289 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
290 use std::{io, ptr};
291 use winapi::SOCKADDR as sockaddr;
292 use winapi::SOCKADDR_IN as sockaddr_in;
293 use winapi::{sockaddr_in6, AF_INET6, AF_INET, DWORD, ERROR_SUCCESS};
294
295 #[repr(C)]
296 struct SocketAddress {
297 pub lp_socket_address: *const sockaddr,
298 pub i_socket_address_length: c_int,
299 }
300 #[repr(C)]
301 struct IpAdapterUnicastAddress {
302 pub length: c_ulong,
303 pub flags: DWORD,
304 pub next: *const IpAdapterUnicastAddress,
305 // Loads more follows, but I'm not bothering to map these for now
306 pub address: SocketAddress,
307 }
308 #[repr(C)]
309 struct IpAdapterPrefix {
310 pub length: c_ulong,
311 pub flags: DWORD,
312 pub next: *const IpAdapterPrefix,
313 pub address: SocketAddress,
314 pub prefix_length: c_ulong,
315 }
316 #[repr(C)]
317 struct IpAdapterAddresses {
318 pub length: c_ulong,
319 pub if_index: DWORD,
320 pub next: *const IpAdapterAddresses,
321 pub adapter_name: *const c_char,
322 pub first_unicast_address: *const IpAdapterUnicastAddress,
323 first_anycast_address: *const c_void,
324 first_multicast_address: *const c_void,
325 first_dns_server_address: *const c_void,
326 dns_suffix: *const c_void,
327 description: *const c_void,
328 friendly_name: *const c_void,
329 physical_address: [c_char; 8],
330 physical_address_length: DWORD,
331 flags: DWORD,
332 mtu: DWORD,
333 if_type: DWORD,
334 oper_status: c_int,
335 ipv6_if_index: DWORD,
336 zone_indices: [DWORD; 16],
337 // Loads more follows, but I'm not bothering to map these for now
338 pub first_prefix: *const IpAdapterPrefix,
339 }
340 #[link(name = "Iphlpapi")]
341 extern "system" {
342 /// get adapter's addresses
GetAdaptersAddresses( family: c_ulong, flags: c_ulong, reserved: *const c_void, addresses: *const IpAdapterAddresses, size: *mut c_ulong, ) -> c_ulong343 fn GetAdaptersAddresses(
344 family: c_ulong,
345 flags: c_ulong,
346 reserved: *const c_void,
347 addresses: *const IpAdapterAddresses,
348 size: *mut c_ulong,
349 ) -> c_ulong;
350 }
351
352 #[allow(unsafe_code)]
sockaddr_to_ipaddr(sockaddr: *const sockaddr) -> Option<IpAddr>353 fn sockaddr_to_ipaddr(sockaddr: *const sockaddr) -> Option<IpAddr> {
354 if sockaddr.is_null() {
355 return None;
356 }
357 if unsafe { *sockaddr }.sa_family as u32 == AF_INET as u32 {
358 let sa = &unsafe { *(sockaddr as *const sockaddr_in) };
359 // Ignore all 169.254.x.x addresses as these are not active interfaces
360 if sa.sin_addr.S_un & 65535 == 0xfea9 {
361 return None;
362 }
363 Some(IpAddr::V4(Ipv4Addr::new(
364 ((sa.sin_addr.S_un >> 0) & 255) as u8,
365 ((sa.sin_addr.S_un >> 8) & 255) as u8,
366 ((sa.sin_addr.S_un >> 16) & 255) as u8,
367 ((sa.sin_addr.S_un >> 24) & 255) as u8,
368 )))
369 } else if unsafe { *sockaddr }.sa_family as u32 == AF_INET6 as u32 {
370 let sa = &unsafe { *(sockaddr as *const sockaddr_in6) };
371 // Ignore all fe80:: addresses as these are link locals
372 if sa.sin6_addr.s6_addr[0] == 0xfe && sa.sin6_addr.s6_addr[1] == 0x80 {
373 return None;
374 }
375 Some(IpAddr::V6(Ipv6Addr::from(sa.sin6_addr.s6_addr)))
376 } else {
377 None
378 }
379 }
380
381 // trivial_numeric_casts lint may become allow by default.
382 // Refer: https://github.com/rust-lang/rfcs/issues/1020
383 /// Return a vector of IP details for all the valid interfaces on this host
384 #[allow(unsafe_code, trivial_numeric_casts)]
get_if_addrs() -> io::Result<Vec<Interface>>385 pub fn get_if_addrs() -> io::Result<Vec<Interface>> {
386 let mut ret = Vec::<Interface>::new();
387 let mut ifaddrs: *const IpAdapterAddresses;
388 let mut buffersize: c_ulong = 15000;
389 loop {
390 unsafe {
391 ifaddrs = libc::malloc(buffersize as size_t) as *mut IpAdapterAddresses;
392 if ifaddrs.is_null() {
393 panic!("Failed to allocate buffer in get_if_addrs()");
394 }
395 let retcode = GetAdaptersAddresses(
396 0,
397 // GAA_FLAG_SKIP_ANYCAST |
398 // GAA_FLAG_SKIP_MULTICAST |
399 // GAA_FLAG_SKIP_DNS_SERVER |
400 // GAA_FLAG_INCLUDE_PREFIX |
401 // GAA_FLAG_SKIP_FRIENDLY_NAME
402 0x3e,
403 ptr::null(),
404 ifaddrs,
405 &mut buffersize,
406 );
407 match retcode {
408 ERROR_SUCCESS => break,
409 111 => {
410 libc::free(ifaddrs as *mut c_void);
411 buffersize *= 2;
412 continue;
413 }
414 _ => return Err(io::Error::last_os_error()),
415 }
416 }
417 }
418
419 for ifaddr in unsafe { CLinkedListConst::from_ptr(ifaddrs, |a| a.next) }.iter() {
420 for addr in unsafe {
421 CLinkedListConst::from_ptr(ifaddr.first_unicast_address, |a| a.next)
422 }.iter()
423 {
424 let name = unsafe { CStr::from_ptr(ifaddr.adapter_name) }
425 .to_string_lossy()
426 .into_owned();
427
428 let addr = match sockaddr_to_ipaddr(addr.address.lp_socket_address) {
429 None => continue,
430 Some(IpAddr::V4(ipv4_addr)) => {
431 let mut item_netmask = Ipv4Addr::new(0, 0, 0, 0);
432 let mut item_broadcast = None;
433 // Search prefixes for a prefix matching addr
434 'prefixloopv4: for prefix in unsafe {
435 CLinkedListConst::from_ptr(ifaddr.first_prefix, |p| p.next)
436 }.iter()
437 {
438 let ipprefix = sockaddr_to_ipaddr(prefix.address.lp_socket_address);
439 match ipprefix {
440 Some(IpAddr::V4(ref a)) => {
441 let mut netmask: [u8; 4] = [0; 4];
442 for (n, netmask_elt) in netmask
443 .iter_mut()
444 .enumerate()
445 .take((prefix.prefix_length as usize + 7) / 8)
446 {
447 let x_byte = ipv4_addr.octets()[n];
448 let y_byte = a.octets()[n];
449 // Clippy 0.0.128 doesn't handle the label on the `continue`
450 #[cfg_attr(
451 feature = "cargo-clippy", allow(needless_continue)
452 )]
453 for m in 0..8 {
454 if (n * 8) + m > prefix.prefix_length as usize {
455 break;
456 }
457 let bit = 1 << m;
458 if (x_byte & bit) == (y_byte & bit) {
459 *netmask_elt |= bit;
460 } else {
461 continue 'prefixloopv4;
462 }
463 }
464 }
465 item_netmask = Ipv4Addr::new(
466 netmask[0], netmask[1], netmask[2], netmask[3],
467 );
468 let mut broadcast: [u8; 4] = ipv4_addr.octets();
469 for n in 0..4 {
470 broadcast[n] |= !netmask[n];
471 }
472 item_broadcast = Some(Ipv4Addr::new(
473 broadcast[0],
474 broadcast[1],
475 broadcast[2],
476 broadcast[3],
477 ));
478 break 'prefixloopv4;
479 }
480 _ => continue,
481 };
482 }
483 IfAddr::V4(Ifv4Addr {
484 ip: ipv4_addr,
485 netmask: item_netmask,
486 broadcast: item_broadcast,
487 })
488 }
489 Some(IpAddr::V6(ipv6_addr)) => {
490 let mut item_netmask = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
491 // Search prefixes for a prefix matching addr
492 'prefixloopv6: for prefix in unsafe {
493 CLinkedListConst::from_ptr(ifaddr.first_prefix, |p| p.next)
494 }.iter()
495 {
496 let ipprefix = sockaddr_to_ipaddr(prefix.address.lp_socket_address);
497 match ipprefix {
498 Some(IpAddr::V6(ref a)) => {
499 // Iterate the bits in the prefix, if they all match this prefix
500 // is the right one, else try the next prefix
501 let mut netmask: [u16; 8] = [0; 8];
502 for (n, netmask_elt) in netmask
503 .iter_mut()
504 .enumerate()
505 .take((prefix.prefix_length as usize + 15) / 16)
506 {
507 let x_word = ipv6_addr.segments()[n];
508 let y_word = a.segments()[n];
509 // Clippy 0.0.128 doesn't handle the label on the `continue`
510 #[cfg_attr(
511 feature = "cargo-clippy", allow(needless_continue)
512 )]
513 for m in 0..16 {
514 if (n * 16) + m > prefix.prefix_length as usize {
515 break;
516 }
517 let bit = 1 << m;
518 if (x_word & bit) == (y_word & bit) {
519 *netmask_elt |= bit;
520 } else {
521 continue 'prefixloopv6;
522 }
523 }
524 }
525 item_netmask = Ipv6Addr::new(
526 netmask[0], netmask[1], netmask[2], netmask[3], netmask[4],
527 netmask[5], netmask[6], netmask[7],
528 );
529 break 'prefixloopv6;
530 }
531 _ => continue,
532 };
533 }
534 IfAddr::V6(Ifv6Addr {
535 ip: ipv6_addr,
536 netmask: item_netmask,
537 broadcast: None,
538 })
539 }
540 };
541 ret.push(Interface {
542 name: name,
543 addr: addr,
544 });
545 }
546 }
547 unsafe {
548 libc::free(ifaddrs as *mut c_void);
549 }
550 Ok(ret)
551 }
552 }
553
554 #[cfg(windows)]
555 /// Get address
get_if_addrs() -> io::Result<Vec<Interface>>556 pub fn get_if_addrs() -> io::Result<Vec<Interface>> {
557 getifaddrs_windows::get_if_addrs()
558 }
559
560 #[cfg(test)]
561 mod tests {
562 use super::{get_if_addrs, Interface};
563 use std::error::Error;
564 use std::io::Read;
565 use std::net::{IpAddr, Ipv4Addr};
566 use std::process::{Command, Stdio};
567 use std::str::FromStr;
568 use std::thread;
569 use std::time::Duration;
570
list_system_interfaces(cmd: &str, arg: &str) -> String571 fn list_system_interfaces(cmd: &str, arg: &str) -> String {
572 let start_cmd = if arg == "" {
573 Command::new(cmd).stdout(Stdio::piped()).spawn()
574 } else {
575 Command::new(cmd).arg(arg).stdout(Stdio::piped()).spawn()
576 };
577 let mut process = match start_cmd {
578 Err(why) => {
579 println!("couldn't start cmd {} : {}", cmd, why.description());
580 return "".to_string();
581 }
582 Ok(process) => process,
583 };
584 thread::sleep(Duration::from_millis(1000));
585 let _ = process.kill();
586 let result: Vec<u8> = unwrap!(process.stdout)
587 .bytes()
588 .map(|x| unwrap!(x))
589 .collect();
590 unwrap!(String::from_utf8(result))
591 }
592
593 #[cfg(windows)]
list_system_addrs() -> Vec<IpAddr>594 fn list_system_addrs() -> Vec<IpAddr> {
595 use std::net::Ipv6Addr;
596 list_system_interfaces("ipconfig", "")
597 .lines()
598 .filter_map(|line| {
599 println!("{}", line);
600 if line.contains("Address") && !line.contains("Link-local") {
601 let addr_s: Vec<&str> = line.split(" : ").collect();
602 if line.contains("IPv6") {
603 return Some(IpAddr::V6(unwrap!(Ipv6Addr::from_str(addr_s[1]))));
604 } else if line.contains("IPv4") {
605 return Some(IpAddr::V4(unwrap!(Ipv4Addr::from_str(addr_s[1]))));
606 }
607 }
608 None
609 })
610 .collect()
611 }
612
613 #[cfg(any(target_os = "linux", target_os = "android", target_os = "nacl"))]
list_system_addrs() -> Vec<IpAddr>614 fn list_system_addrs() -> Vec<IpAddr> {
615 list_system_interfaces("ip", "addr")
616 .lines()
617 .filter_map(|line| {
618 println!("{}", line);
619 if line.contains("inet ") {
620 let addr_s: Vec<&str> = line.split_whitespace().collect();
621 let addr: Vec<&str> = addr_s[1].split('/').collect();
622 return Some(IpAddr::V4(unwrap!(Ipv4Addr::from_str(addr[0]))));
623 }
624 None
625 })
626 .collect()
627 }
628
629 #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "macos", target_os = "ios"))]
list_system_addrs() -> Vec<IpAddr>630 fn list_system_addrs() -> Vec<IpAddr> {
631 list_system_interfaces("ifconfig", "")
632 .lines()
633 .filter_map(|line| {
634 println!("{}", line);
635 if line.contains("inet ") {
636 let addr_s: Vec<&str> = line.split_whitespace().collect();
637 return Some(IpAddr::V4(unwrap!(Ipv4Addr::from_str(addr_s[1]))));
638 }
639 None
640 })
641 .collect()
642 }
643
644 #[test]
test_get_if_addrs()645 fn test_get_if_addrs() {
646 let ifaces = unwrap!(get_if_addrs());
647 println!("Local interfaces:");
648 println!("{:#?}", ifaces);
649 // at least one loop back address
650 assert!(
651 1 <= ifaces
652 .iter()
653 .filter(|interface| interface.is_loopback())
654 .count()
655 );
656 // one address of IpV4(127.0.0.1)
657 let is_loopback =
658 |interface: &&Interface| interface.addr.ip() == IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
659 assert_eq!(1, ifaces.iter().filter(is_loopback).count());
660
661 // each system address shall be listed
662 let system_addrs = list_system_addrs();
663 assert!(!system_addrs.is_empty());
664 for addr in system_addrs {
665 let mut listed = false;
666 println!("\n checking whether {:?} has been properly listed \n", addr);
667 for interface in &ifaces {
668 if interface.addr.ip() == addr {
669 listed = true;
670 }
671 }
672 assert!(listed);
673 }
674 }
675 }
676