Skip to main content

if_addrs/
lib.rs

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#[cfg(not(windows))]
11mod posix;
12mod sockaddr;
13#[cfg(windows)]
14mod windows;
15
16use std::io;
17use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
18
19/// Details about an interface on this host.
20#[derive(Debug, PartialEq, Eq, Hash, Clone)]
21pub struct Interface {
22    /// The name of the interface.
23    pub name: String,
24    /// The address details of the interface.
25    pub addr: IfAddr,
26    /// The index of the interface.
27    pub index: Option<u32>,
28}
29
30impl Interface {
31    /// Check whether this is a loopback interface.
32    pub fn is_loopback(&self) -> bool {
33        self.addr.is_loopback()
34    }
35
36    /// Check whether this is a link local interface.
37    pub fn is_link_local(&self) -> bool {
38        self.addr.is_link_local()
39    }
40
41    /// Get the IP address of this interface.
42    pub fn ip(&self) -> IpAddr {
43        self.addr.ip()
44    }
45}
46
47/// Details about the address of an interface on this host.
48#[derive(Debug, PartialEq, Eq, Hash, Clone)]
49pub enum IfAddr {
50    /// This is an Ipv4 interface.
51    V4(Ifv4Addr),
52    /// This is an Ipv6 interface.
53    V6(Ifv6Addr),
54}
55
56impl IfAddr {
57    /// Check whether this is a loopback address.
58    pub fn is_loopback(&self) -> bool {
59        match *self {
60            IfAddr::V4(ref ifv4_addr) => ifv4_addr.is_loopback(),
61            IfAddr::V6(ref ifv6_addr) => ifv6_addr.is_loopback(),
62        }
63    }
64
65    /// Check whether this is a link local interface.
66    pub fn is_link_local(&self) -> bool {
67        match *self {
68            IfAddr::V4(ref ifv4_addr) => ifv4_addr.is_link_local(),
69            IfAddr::V6(ref ifv6_addr) => ifv6_addr.is_link_local(),
70        }
71    }
72
73    /// Get the IP address of this interface address.
74    pub fn ip(&self) -> IpAddr {
75        match *self {
76            IfAddr::V4(ref ifv4_addr) => IpAddr::V4(ifv4_addr.ip),
77            IfAddr::V6(ref ifv6_addr) => IpAddr::V6(ifv6_addr.ip),
78        }
79    }
80}
81
82/// Details about the ipv4 address of an interface on this host.
83#[derive(Debug, PartialEq, Eq, Hash, Clone)]
84pub struct Ifv4Addr {
85    /// The IP address of the interface.
86    pub ip: Ipv4Addr,
87    /// The netmask of the interface.
88    pub netmask: Ipv4Addr,
89    /// The broadcast address of the interface.
90    pub broadcast: Option<Ipv4Addr>,
91}
92
93impl Ifv4Addr {
94    /// Check whether this is a loopback address.
95    pub fn is_loopback(&self) -> bool {
96        self.ip.octets()[0] == 127
97    }
98
99    /// Check whether this is a link local address.
100    pub fn is_link_local(&self) -> bool {
101        self.ip.is_link_local()
102    }
103}
104
105/// Details about the ipv6 address of an interface on this host.
106#[derive(Debug, PartialEq, Eq, Hash, Clone)]
107pub struct Ifv6Addr {
108    /// The IP address of the interface.
109    pub ip: Ipv6Addr,
110    /// The netmask of the interface.
111    pub netmask: Ipv6Addr,
112    /// The broadcast address of the interface.
113    pub broadcast: Option<Ipv6Addr>,
114}
115
116impl Ifv6Addr {
117    /// Check whether this is a loopback address.
118    pub fn is_loopback(&self) -> bool {
119        self.ip.segments() == [0, 0, 0, 0, 0, 0, 0, 1]
120    }
121
122    /// Check whether this is a link local address.
123    pub fn is_link_local(&self) -> bool {
124        let bytes = self.ip.octets();
125
126        bytes[0] == 0xfe && bytes[1] == 0x80
127    }
128}
129
130#[cfg(not(windows))]
131mod getifaddrs_posix {
132    use libc::if_nametoindex;
133
134    use super::{IfAddr, Ifv4Addr, Ifv6Addr, Interface};
135    use crate::posix::{self as ifaddrs, IfAddrs};
136    use crate::sockaddr;
137    use std::ffi::CStr;
138    use std::io;
139    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
140
141    /// Return a vector of IP details for all the valid interfaces on this host.
142    #[allow(unsafe_code)]
143    pub fn get_if_addrs() -> io::Result<Vec<Interface>> {
144        let mut ret = Vec::<Interface>::new();
145        let ifaddrs = IfAddrs::new()?;
146
147        for ifaddr in ifaddrs.iter() {
148            let addr = match sockaddr::to_ipaddr(ifaddr.ifa_addr) {
149                None => continue,
150                Some(IpAddr::V4(ipv4_addr)) => {
151                    let netmask = match sockaddr::to_ipaddr(ifaddr.ifa_netmask) {
152                        Some(IpAddr::V4(netmask)) => netmask,
153                        _ => Ipv4Addr::new(0, 0, 0, 0),
154                    };
155                    let broadcast = if (ifaddr.ifa_flags & 2) != 0 {
156                        match ifaddrs::do_broadcast(&ifaddr) {
157                            Some(IpAddr::V4(broadcast)) => Some(broadcast),
158                            _ => None,
159                        }
160                    } else {
161                        None
162                    };
163
164                    IfAddr::V4(Ifv4Addr {
165                        ip: ipv4_addr,
166                        netmask,
167                        broadcast,
168                    })
169                }
170                Some(IpAddr::V6(ipv6_addr)) => {
171                    let netmask = match sockaddr::to_ipaddr(ifaddr.ifa_netmask) {
172                        Some(IpAddr::V6(netmask)) => netmask,
173                        _ => Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0),
174                    };
175                    let broadcast = if (ifaddr.ifa_flags & 2) != 0 {
176                        match ifaddrs::do_broadcast(&ifaddr) {
177                            Some(IpAddr::V6(broadcast)) => Some(broadcast),
178                            _ => None,
179                        }
180                    } else {
181                        None
182                    };
183
184                    IfAddr::V6(Ifv6Addr {
185                        ip: ipv6_addr,
186                        netmask,
187                        broadcast,
188                    })
189                }
190            };
191
192            let name = unsafe { CStr::from_ptr(ifaddr.ifa_name) }
193                .to_string_lossy()
194                .into_owned();
195            let index = {
196                let index = unsafe { if_nametoindex(ifaddr.ifa_name) };
197
198                // From `man if_nametoindex 3`:
199                // The if_nametoindex() function maps the interface name specified in ifname to its
200                // corresponding index. If the specified interface does not exist, it returns 0.
201                if index == 0 {
202                    None
203                } else {
204                    Some(index)
205                }
206            };
207            ret.push(Interface { name, addr, index });
208        }
209
210        Ok(ret)
211    }
212}
213
214/// Get a list of all the network interfaces on this machine along with their IP info.
215#[cfg(not(windows))]
216pub fn get_if_addrs() -> io::Result<Vec<Interface>> {
217    getifaddrs_posix::get_if_addrs()
218}
219
220#[cfg(windows)]
221mod getifaddrs_windows {
222    use super::{IfAddr, Ifv4Addr, Ifv6Addr, Interface};
223    use crate::sockaddr;
224    use crate::windows::IfAddrs;
225    use std::io;
226    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
227    use windows_sys::Win32::Networking::WinSock::IpDadStatePreferred;
228
229    /// Return a vector of IP details for all the valid interfaces on this host.
230    pub fn get_if_addrs() -> io::Result<Vec<Interface>> {
231        let mut ret = Vec::<Interface>::new();
232        let ifaddrs = IfAddrs::new()?;
233
234        for ifaddr in ifaddrs.iter() {
235            for addr in ifaddr.unicast_addresses() {
236                if addr.DadState != IpDadStatePreferred {
237                    continue;
238                }
239                let addr = match sockaddr::to_ipaddr(addr.Address.lpSockaddr) {
240                    None => continue,
241                    Some(IpAddr::V4(ipv4_addr)) => {
242                        let mut item_netmask = Ipv4Addr::new(0, 0, 0, 0);
243                        let mut item_broadcast = None;
244
245                        // Search prefixes for a prefix matching addr
246                        'prefixloopv4: for prefix in ifaddr.prefixes() {
247                            let ipprefix = sockaddr::to_ipaddr(prefix.Address.lpSockaddr);
248                            match ipprefix {
249                                Some(IpAddr::V4(ref a)) => {
250                                    let mut netmask: [u8; 4] = [0; 4];
251                                    for (n, netmask_elt) in netmask
252                                        .iter_mut()
253                                        .enumerate()
254                                        .take((prefix.PrefixLength as usize + 7) / 8)
255                                    {
256                                        let x_byte = ipv4_addr.octets()[n];
257                                        let y_byte = a.octets()[n];
258                                        for m in 0..8 {
259                                            if (n * 8) + m > prefix.PrefixLength as usize {
260                                                break;
261                                            }
262                                            let bit = 1 << (7 - m);
263                                            if (x_byte & bit) == (y_byte & bit) {
264                                                *netmask_elt |= bit;
265                                            } else {
266                                                continue 'prefixloopv4;
267                                            }
268                                        }
269                                    }
270                                    item_netmask = Ipv4Addr::new(
271                                        netmask[0], netmask[1], netmask[2], netmask[3],
272                                    );
273                                    let mut broadcast: [u8; 4] = ipv4_addr.octets();
274                                    for n in 0..4 {
275                                        broadcast[n] |= !netmask[n];
276                                    }
277                                    item_broadcast = Some(Ipv4Addr::new(
278                                        broadcast[0],
279                                        broadcast[1],
280                                        broadcast[2],
281                                        broadcast[3],
282                                    ));
283                                    break 'prefixloopv4;
284                                }
285                                _ => continue,
286                            };
287                        }
288                        IfAddr::V4(Ifv4Addr {
289                            ip: ipv4_addr,
290                            netmask: item_netmask,
291                            broadcast: item_broadcast,
292                        })
293                    }
294                    Some(IpAddr::V6(ipv6_addr)) => {
295                        let mut item_netmask = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
296                        // Search prefixes for a prefix matching addr
297                        'prefixloopv6: for prefix in ifaddr.prefixes() {
298                            let ipprefix = sockaddr::to_ipaddr(prefix.Address.lpSockaddr);
299                            match ipprefix {
300                                Some(IpAddr::V6(ref a)) => {
301                                    // Iterate the bits in the prefix, if they all match this prefix
302                                    // is the right one, else try the next prefix
303                                    let mut netmask: [u16; 8] = [0; 8];
304                                    for (n, netmask_elt) in netmask
305                                        .iter_mut()
306                                        .enumerate()
307                                        .take((prefix.PrefixLength as usize + 15) / 16)
308                                    {
309                                        let x_word = ipv6_addr.segments()[n];
310                                        let y_word = a.segments()[n];
311                                        for m in 0..16 {
312                                            if (n * 16) + m > prefix.PrefixLength as usize {
313                                                break;
314                                            }
315                                            let bit = 1 << (15 - m);
316                                            if (x_word & bit) == (y_word & bit) {
317                                                *netmask_elt |= bit;
318                                            } else {
319                                                continue 'prefixloopv6;
320                                            }
321                                        }
322                                    }
323                                    item_netmask = Ipv6Addr::new(
324                                        netmask[0], netmask[1], netmask[2], netmask[3], netmask[4],
325                                        netmask[5], netmask[6], netmask[7],
326                                    );
327                                    break 'prefixloopv6;
328                                }
329                                _ => continue,
330                            };
331                        }
332                        IfAddr::V6(Ifv6Addr {
333                            ip: ipv6_addr,
334                            netmask: item_netmask,
335                            broadcast: None,
336                        })
337                    }
338                };
339
340                let index = match addr {
341                    IfAddr::V4(_) => ifaddr.ipv4_index(),
342                    IfAddr::V6(_) => ifaddr.ipv6_index(),
343                };
344                ret.push(Interface {
345                    name: ifaddr.name(),
346                    addr,
347                    index,
348                });
349            }
350        }
351
352        Ok(ret)
353    }
354}
355
356#[cfg(windows)]
357/// Get address
358pub fn get_if_addrs() -> io::Result<Vec<Interface>> {
359    getifaddrs_windows::get_if_addrs()
360}
361
362#[cfg(test)]
363mod tests {
364    use super::{get_if_addrs, Interface};
365    use std::io::Read;
366    use std::net::{IpAddr, Ipv4Addr};
367    use std::process::{Command, Stdio};
368    use std::str::FromStr;
369    use std::thread;
370    use std::time::Duration;
371
372    fn list_system_interfaces(cmd: &str, arg: &str) -> String {
373        let start_cmd = if arg == "" {
374            Command::new(cmd).stdout(Stdio::piped()).spawn()
375        } else {
376            Command::new(cmd).arg(arg).stdout(Stdio::piped()).spawn()
377        };
378        let mut process = match start_cmd {
379            Err(why) => {
380                println!("couldn't start cmd {} : {}", cmd, why.to_string());
381                return "".to_string();
382            }
383            Ok(process) => process,
384        };
385        thread::sleep(Duration::from_millis(1000));
386        let _ = process.kill();
387        let result: Vec<u8> = process
388            .stdout
389            .unwrap()
390            .bytes()
391            .map(|x| x.unwrap())
392            .collect();
393        String::from_utf8(result).unwrap()
394    }
395
396    #[cfg(windows)]
397    fn list_system_addrs() -> Vec<IpAddr> {
398        use std::net::Ipv6Addr;
399        list_system_interfaces("ipconfig", "")
400            .lines()
401            .filter_map(|line| {
402                println!("{}", line);
403                if line.contains("Address") && !line.contains("Link-local") {
404                    let addr_s: Vec<&str> = line.split(" : ").collect();
405                    if line.contains("IPv6") {
406                        return Some(IpAddr::V6(Ipv6Addr::from_str(addr_s[1]).unwrap()));
407                    } else if line.contains("IPv4") {
408                        return Some(IpAddr::V4(Ipv4Addr::from_str(addr_s[1]).unwrap()));
409                    }
410                }
411                None
412            })
413            .collect()
414    }
415
416    #[cfg(any(target_os = "linux", target_os = "android", target_os = "nacl"))]
417    fn list_system_addrs() -> Vec<IpAddr> {
418        list_system_interfaces("ip", "addr")
419            .lines()
420            .filter_map(|line| {
421                println!("{}", line);
422                if line.contains("inet ") {
423                    let addr_s: Vec<&str> = line.split_whitespace().collect();
424                    let addr: Vec<&str> = addr_s[1].split('/').collect();
425                    return Some(IpAddr::V4(Ipv4Addr::from_str(addr[0]).unwrap()));
426                }
427                None
428            })
429            .collect()
430    }
431
432    #[cfg(any(
433        target_os = "freebsd",
434        target_os = "macos",
435        target_os = "ios",
436        target_os = "tvos"
437    ))]
438    fn list_system_addrs() -> Vec<IpAddr> {
439        list_system_interfaces("ifconfig", "")
440            .lines()
441            .filter_map(|line| {
442                println!("{}", line);
443                if line.contains("inet ") {
444                    let addr_s: Vec<&str> = line.split_whitespace().collect();
445                    return Some(IpAddr::V4(Ipv4Addr::from_str(addr_s[1]).unwrap()));
446                }
447                None
448            })
449            .collect()
450    }
451
452    #[test]
453    fn test_get_if_addrs() {
454        let ifaces = get_if_addrs().unwrap();
455        println!("Local interfaces:");
456        println!("{:#?}", ifaces);
457        // at least one loop back address
458        assert!(
459            1 <= ifaces
460                .iter()
461                .filter(|interface| interface.is_loopback())
462                .count()
463        );
464        // if index is set, it is non-zero
465        for interface in &ifaces {
466            if let Some(idx) = interface.index {
467                assert!(idx > 0);
468            }
469        }
470
471        // one address of IpV4(127.0.0.1)
472        let is_loopback =
473            |interface: &&Interface| interface.addr.ip() == IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
474        assert_eq!(1, ifaces.iter().filter(is_loopback).count());
475
476        // each system address shall be listed
477        let system_addrs = list_system_addrs();
478        assert!(!system_addrs.is_empty());
479        for addr in system_addrs {
480            let mut listed = false;
481            println!("\n checking whether {:?} has been properly listed \n", addr);
482            for interface in &ifaces {
483                if interface.addr.ip() == addr {
484                    listed = true;
485                }
486
487                assert!(interface.index.is_some());
488            }
489            assert!(listed);
490        }
491    }
492}