diff options
Diffstat (limited to 'src/ifaddrs.rs')
-rw-r--r-- | src/ifaddrs.rs | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/src/ifaddrs.rs b/src/ifaddrs.rs new file mode 100644 index 00000000..2b3024b5 --- /dev/null +++ b/src/ifaddrs.rs @@ -0,0 +1,153 @@ +//! Query network interface addresses +//! +//! Uses the Linux and/or BSD specific function `getifaddrs` to query the list +//! of interfaces and their associated addresses. + +use std::ffi; +use std::fmt; +use std::iter::Iterator; +use std::mem; +use std::option::Option; + +use libc; + +use {Result, Errno}; +use sys::socket::SockAddr; +use net::if_::*; + +/// Describes a single address for an interface as returned by `getifaddrs`. +#[derive(Clone, Eq, Hash, PartialEq)] +pub struct InterfaceAddress { + /// Name of the network interface + pub interface_name: String, + /// Flags as from `SIOCGIFFLAGS` ioctl + pub flags: InterfaceFlags, + /// Network address of this interface + pub address: Option<SockAddr>, + /// Netmask of this interface + pub netmask: Option<SockAddr>, + /// Broadcast address of this interface, if applicable + pub broadcast: Option<SockAddr>, + /// Point-to-point destination address + pub destination: Option<SockAddr>, +} + +impl fmt::Debug for InterfaceAddress { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "InterfaceAddress ({:?})", self.interface_name) + } +} + +cfg_if! { + if #[cfg(any(target_os = "emscripten", target_os = "fuchsia", target_os = "linux"))] { + fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr { + info.ifa_ifu + } + } else { + fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr { + info.ifa_dstaddr + } + } +} + +impl InterfaceAddress { + /// Create an `InterfaceAddress` from the libc struct. + fn from_libc_ifaddrs(info: &libc::ifaddrs) -> InterfaceAddress { + let ifname = unsafe { ffi::CStr::from_ptr(info.ifa_name) }; + let address = unsafe { SockAddr::from_libc_sockaddr(info.ifa_addr) }; + let netmask = unsafe { SockAddr::from_libc_sockaddr(info.ifa_netmask) }; + let mut addr = InterfaceAddress { + interface_name: ifname.to_string_lossy().to_string(), + flags: InterfaceFlags::from_bits_truncate(info.ifa_flags as i32), + address: address, + netmask: netmask, + broadcast: None, + destination: None, + }; + + let ifu = get_ifu_from_sockaddr(info); + if addr.flags.contains(InterfaceFlags::IFF_POINTOPOINT) { + addr.destination = unsafe { SockAddr::from_libc_sockaddr(ifu) }; + } else if addr.flags.contains(InterfaceFlags::IFF_BROADCAST) { + addr.broadcast = unsafe { SockAddr::from_libc_sockaddr(ifu) }; + } + + addr + } +} + +/// Holds the results of `getifaddrs`. +/// +/// Use the function `getifaddrs` to create this Iterator. Note that the +/// actual list of interfaces can be iterated once and will be freed as +/// soon as the Iterator goes out of scope. +#[derive(Debug, Eq, Hash, PartialEq)] +pub struct InterfaceAddressIterator { + base: *mut libc::ifaddrs, + next: *mut libc::ifaddrs, +} + +impl Drop for InterfaceAddressIterator { + fn drop(&mut self) { + unsafe { libc::freeifaddrs(self.base) }; + } +} + +impl Iterator for InterfaceAddressIterator { + type Item = InterfaceAddress; + fn next(&mut self) -> Option<<Self as Iterator>::Item> { + match unsafe { self.next.as_ref() } { + Some(ifaddr) => { + self.next = ifaddr.ifa_next; + Some(InterfaceAddress::from_libc_ifaddrs(ifaddr)) + } + None => None, + } + } +} + +/// Get interface addresses using libc's `getifaddrs` +/// +/// Note that the underlying implementation differs between OSes. Only the +/// most common address families are supported by the nix crate (due to +/// lack of time and complexity of testing). The address family is encoded +/// in the specific variant of `SockAddr` returned for the fields `address`, +/// `netmask`, `broadcast`, and `destination`. For any entry not supported, +/// the returned list will contain a `None` entry. +/// +/// # Example +/// ``` +/// let addrs = nix::ifaddrs::getifaddrs().unwrap(); +/// for ifaddr in addrs { +/// match ifaddr.address { +/// Some(address) => { +/// println!("interface {} address {}", +/// ifaddr.interface_name, address); +/// }, +/// None => { +/// println!("interface {} with unsupported address family", +/// ifaddr.interface_name); +/// } +/// } +/// } +/// ``` +pub fn getifaddrs() -> Result<InterfaceAddressIterator> { + let mut addrs: *mut libc::ifaddrs = unsafe { mem::uninitialized() }; + Errno::result(unsafe { libc::getifaddrs(&mut addrs) }).map(|_| { + InterfaceAddressIterator { + base: addrs, + next: addrs, + } + }) +} + +#[cfg(test)] +mod tests { + use super::*; + + // Only checks if `getifaddrs` can be invoked without panicking. + #[test] + fn test_getifaddrs() { + let _ = getifaddrs(); + } +} |