Skip to content

Commit 3871586

Browse files
committed
Merge #667
667: Add support for getifaddrs. r=asomers a=mwanner Here's a first attempt at a rustian interface for `getifaddrs`, please review. Changes for the changelog: - Added `nix::ifaddrs::{getifaddrs, InterfaceAddressIterator, InterfaceAddress and InterfaceFlags}` Closes #650. Closes #764.
2 parents 86ebf7b + 985ea01 commit 3871586

File tree

4 files changed

+451
-0
lines changed

4 files changed

+451
-0
lines changed

src/ifaddrs.rs

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
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+
}

src/lib.rs

+9
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@ pub mod poll;
4343

4444
pub mod net;
4545

46+
#[cfg(any(target_os = "dragonfly",
47+
target_os = "freebsd",
48+
target_os = "ios",
49+
target_os = "linux",
50+
target_os = "macos",
51+
target_os = "netbsd",
52+
target_os = "openbsd"))]
53+
pub mod ifaddrs;
54+
4655
#[cfg(any(target_os = "linux", target_os = "android"))]
4756
pub mod sched;
4857

0 commit comments

Comments
 (0)