Skip to content

Commit

Permalink
Add support for getifaddrs. Closes: #650.
Browse files Browse the repository at this point in the history
  • Loading branch information
mwanner committed Dec 3, 2017
1 parent 86ebf7b commit 985ea01
Show file tree
Hide file tree
Showing 4 changed files with 451 additions and 0 deletions.
153 changes: 153 additions & 0 deletions src/ifaddrs.rs
Original file line number Diff line number Diff line change
@@ -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();
}
}
9 changes: 9 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ pub mod poll;

pub mod net;

#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"))]
pub mod ifaddrs;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod sched;

Expand Down
Loading

0 comments on commit 985ea01

Please sign in to comment.