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