|
| 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 std::ffi; |
| 7 | +use std::fmt; |
| 8 | +use std::iter::Iterator; |
| 9 | +use std::mem; |
| 10 | +use std::option::Option; |
| 11 | + |
| 12 | +use libc; |
| 13 | + |
| 14 | +use {Result, Errno}; |
| 15 | +use sys::socket::SockAddr; |
| 16 | +use net::if_::*; |
| 17 | + |
| 18 | +/// Describes a single address for an interface as returned by `getifaddrs`. |
| 19 | +#[derive(Clone, 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<SockAddr>, |
| 27 | + /// Netmask of this interface |
| 28 | + pub netmask: Option<SockAddr>, |
| 29 | + /// Broadcast address of this interface, if applicable |
| 30 | + pub broadcast: Option<SockAddr>, |
| 31 | + /// Point-to-point destination address |
| 32 | + pub destination: Option<SockAddr>, |
| 33 | +} |
| 34 | + |
| 35 | +impl fmt::Debug for InterfaceAddress { |
| 36 | + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 37 | + write!(f, "InterfaceAddress ({:?})", self.interface_name) |
| 38 | + } |
| 39 | +} |
| 40 | + |
| 41 | +cfg_if! { |
| 42 | + if #[cfg(any(target_os = "emscripten", target_os = "fuchsia", target_os = "linux"))] { |
| 43 | + fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr { |
| 44 | + info.ifa_ifu |
| 45 | + } |
| 46 | + } else { |
| 47 | + fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr { |
| 48 | + info.ifa_dstaddr |
| 49 | + } |
| 50 | + } |
| 51 | +} |
| 52 | + |
| 53 | +impl InterfaceAddress { |
| 54 | + /// Create an `InterfaceAddress` from the libc struct. |
| 55 | + fn from_libc_ifaddrs(info: &libc::ifaddrs) -> InterfaceAddress { |
| 56 | + let ifname = unsafe { ffi::CStr::from_ptr(info.ifa_name) }; |
| 57 | + let address = unsafe { SockAddr::from_libc_sockaddr(info.ifa_addr) }; |
| 58 | + let netmask = unsafe { SockAddr::from_libc_sockaddr(info.ifa_netmask) }; |
| 59 | + let mut addr = InterfaceAddress { |
| 60 | + interface_name: ifname.to_string_lossy().to_string(), |
| 61 | + flags: InterfaceFlags::from_bits_truncate(info.ifa_flags as i32), |
| 62 | + address: address, |
| 63 | + netmask: netmask, |
| 64 | + broadcast: None, |
| 65 | + destination: None, |
| 66 | + }; |
| 67 | + |
| 68 | + let ifu = get_ifu_from_sockaddr(info); |
| 69 | + if addr.flags.contains(InterfaceFlags::IFF_POINTOPOINT) { |
| 70 | + addr.destination = unsafe { SockAddr::from_libc_sockaddr(ifu) }; |
| 71 | + } else if addr.flags.contains(InterfaceFlags::IFF_BROADCAST) { |
| 72 | + addr.broadcast = unsafe { SockAddr::from_libc_sockaddr(ifu) }; |
| 73 | + } |
| 74 | + |
| 75 | + addr |
| 76 | + } |
| 77 | +} |
| 78 | + |
| 79 | +/// Holds the results of `getifaddrs`. |
| 80 | +/// |
| 81 | +/// Use the function `getifaddrs` to create this Iterator. Note that the |
| 82 | +/// actual list of interfaces can be iterated once and will be freed as |
| 83 | +/// soon as the Iterator goes out of scope. |
| 84 | +#[derive(Debug, Eq, Hash, PartialEq)] |
| 85 | +pub struct InterfaceAddressIterator { |
| 86 | + base: *mut libc::ifaddrs, |
| 87 | + next: *mut libc::ifaddrs, |
| 88 | +} |
| 89 | + |
| 90 | +impl Drop for InterfaceAddressIterator { |
| 91 | + fn drop(&mut self) { |
| 92 | + unsafe { libc::freeifaddrs(self.base) }; |
| 93 | + } |
| 94 | +} |
| 95 | + |
| 96 | +impl Iterator for InterfaceAddressIterator { |
| 97 | + type Item = InterfaceAddress; |
| 98 | + fn next(&mut self) -> Option<<Self as Iterator>::Item> { |
| 99 | + match unsafe { self.next.as_ref() } { |
| 100 | + Some(ifaddr) => { |
| 101 | + self.next = ifaddr.ifa_next; |
| 102 | + Some(InterfaceAddress::from_libc_ifaddrs(ifaddr)) |
| 103 | + } |
| 104 | + None => None, |
| 105 | + } |
| 106 | + } |
| 107 | +} |
| 108 | + |
| 109 | +/// Get interface addresses using libc's `getifaddrs` |
| 110 | +/// |
| 111 | +/// Note that the underlying implementation differs between OSes. Only the |
| 112 | +/// most common address families are supported by the nix crate (due to |
| 113 | +/// lack of time and complexity of testing). The address family is encoded |
| 114 | +/// in the specific variant of `SockAddr` returned for the fields `address`, |
| 115 | +/// `netmask`, `broadcast`, and `destination`. For any entry not supported, |
| 116 | +/// the returned list will contain a `None` entry. |
| 117 | +/// |
| 118 | +/// # Example |
| 119 | +/// ``` |
| 120 | +/// let addrs = nix::ifaddrs::getifaddrs().unwrap(); |
| 121 | +/// for ifaddr in addrs { |
| 122 | +/// match ifaddr.address { |
| 123 | +/// Some(address) => { |
| 124 | +/// println!("interface {} address {}", |
| 125 | +/// ifaddr.interface_name, address); |
| 126 | +/// }, |
| 127 | +/// None => { |
| 128 | +/// println!("interface {} with unsupported address family", |
| 129 | +/// ifaddr.interface_name); |
| 130 | +/// } |
| 131 | +/// } |
| 132 | +/// } |
| 133 | +/// ``` |
| 134 | +pub fn getifaddrs() -> Result<InterfaceAddressIterator> { |
| 135 | + let mut addrs: *mut libc::ifaddrs = unsafe { mem::uninitialized() }; |
| 136 | + Errno::result(unsafe { libc::getifaddrs(&mut addrs) }).map(|_| { |
| 137 | + InterfaceAddressIterator { |
| 138 | + base: addrs, |
| 139 | + next: addrs, |
| 140 | + } |
| 141 | + }) |
| 142 | +} |
| 143 | + |
| 144 | +#[cfg(test)] |
| 145 | +mod tests { |
| 146 | + use super::*; |
| 147 | + |
| 148 | + // Only checks if `getifaddrs` can be invoked without panicking. |
| 149 | + #[test] |
| 150 | + fn test_getifaddrs() { |
| 151 | + let _ = getifaddrs(); |
| 152 | + } |
| 153 | +} |
0 commit comments