Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New socket API #2199

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
175 changes: 98 additions & 77 deletions src/ifaddrs.rs
Expand Up @@ -2,34 +2,32 @@
//!
//! Uses the Linux and/or BSD specific function `getifaddrs` to query the list
//! of interfaces and their associated addresses.

use cfg_if::cfg_if;
#[cfg(apple_targets)]
use std::convert::TryFrom;
use std::ffi;
use std::iter::Iterator;
use std::marker::PhantomData;
use std::mem;
use std::option::Option;

use crate::net::if_::*;
use crate::sys::socket::{SockaddrLike, SockaddrStorage};
use crate::sys::socket::RawAddr;
use crate::{Errno, Result};

/// Describes a single address for an interface as returned by `getifaddrs`.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct InterfaceAddress {
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct InterfaceAddress<'a> {
/// 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<SockaddrStorage>,
pub address: Option<RawAddr<'a>>,
/// Netmask of this interface
pub netmask: Option<SockaddrStorage>,
pub netmask: Option<RawAddr<'a>>,
/// Broadcast address of this interface, if applicable
pub broadcast: Option<SockaddrStorage>,
pub broadcast: Option<RawAddr<'a>>,
/// Point-to-point destination address
pub destination: Option<SockaddrStorage>,
pub destination: Option<RawAddr<'a>>,
}

cfg_if! {
Expand All @@ -44,54 +42,12 @@ cfg_if! {
}
}

/// Workaround a bug in XNU where netmasks will always have the wrong size in
/// the sa_len field due to the kernel ignoring trailing zeroes in the structure
/// when setting the field. See https://github.com/nix-rust/nix/issues/1709#issuecomment-1199304470
///
/// To fix this, we stack-allocate a new sockaddr_storage, zero it out, and
/// memcpy sa_len of the netmask to that new storage. Finally, we reset the
/// ss_len field to sizeof(sockaddr_storage). This is supposedly valid as all
/// members of the sockaddr_storage are "ok" with being zeroed out (there are
/// no pointers).
#[cfg(apple_targets)]
unsafe fn workaround_xnu_bug(info: &libc::ifaddrs) -> Option<SockaddrStorage> {
let src_sock = info.ifa_netmask;
if src_sock.is_null() {
return None;
}

let mut dst_sock = mem::MaybeUninit::<libc::sockaddr_storage>::zeroed();

let dst_sock = unsafe {
// memcpy only sa_len bytes, assume the rest is zero
std::ptr::copy_nonoverlapping(
src_sock as *const u8,
dst_sock.as_mut_ptr().cast(),
(*src_sock).sa_len.into(),
);

// Initialize ss_len to sizeof(libc::sockaddr_storage).
(*dst_sock.as_mut_ptr()).ss_len =
u8::try_from(mem::size_of::<libc::sockaddr_storage>()).unwrap();
dst_sock.assume_init()
};

let dst_sock_ptr =
&dst_sock as *const libc::sockaddr_storage as *const libc::sockaddr;

unsafe { SockaddrStorage::from_raw(dst_sock_ptr, None) }
}

impl InterfaceAddress {
impl<'a> InterfaceAddress<'a> {
/// 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 { SockaddrStorage::from_raw(info.ifa_addr, None) };
#[cfg(apple_targets)]
let netmask = unsafe { workaround_xnu_bug(info) };
#[cfg(not(apple_targets))]
let netmask =
unsafe { SockaddrStorage::from_raw(info.ifa_netmask, None) };
let address = unsafe { RawAddr::new(&*info.ifa_addr) };
let netmask = unsafe { RawAddr::new(&*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),
Expand All @@ -103,9 +59,9 @@ impl InterfaceAddress {

let ifu = get_ifu_from_sockaddr(info);
if addr.flags.contains(InterfaceFlags::IFF_POINTOPOINT) {
addr.destination = unsafe { SockaddrStorage::from_raw(ifu, None) };
addr.destination = unsafe { RawAddr::new(&*ifu) };
} else if addr.flags.contains(InterfaceFlags::IFF_BROADCAST) {
addr.broadcast = unsafe { SockaddrStorage::from_raw(ifu, None) };
addr.broadcast = unsafe { RawAddr::new(&*ifu) };
}

addr
Expand All @@ -114,23 +70,43 @@ impl InterfaceAddress {

/// 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 {
/// Use the function `getifaddrs` to create this struct and [`Self::iter`]
/// to create the 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)]
pub struct InterfaceAddresses {
base: *mut libc::ifaddrs,
next: *mut libc::ifaddrs,
}

impl Drop for InterfaceAddressIterator {
impl InterfaceAddresses {
/// Create an iterator over the list of interfaces.
pub fn iter(&self) -> InterfaceAddressIterator<'_> {
InterfaceAddressIterator {
next: self.base,
_a: PhantomData,
}
}
}

impl Drop for InterfaceAddresses {
fn drop(&mut self) {
unsafe { libc::freeifaddrs(self.base) };
}
}

impl Iterator for InterfaceAddressIterator {
type Item = InterfaceAddress;
/// 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<'a> {
next: *mut libc::ifaddrs,
_a: PhantomData<&'a ()>,
}

impl<'a> Iterator for InterfaceAddressIterator<'a> {
type Item = InterfaceAddress<'a>;
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
match unsafe { self.next.as_ref() } {
Some(ifaddr) => {
Expand All @@ -154,7 +130,7 @@ impl Iterator for InterfaceAddressIterator {
/// # Example
/// ```
/// let addrs = nix::ifaddrs::getifaddrs().unwrap();
/// for ifaddr in addrs {
/// for ifaddr in addrs.iter() {
/// match ifaddr.address {
/// Some(address) => {
/// println!("interface {} address {}",
Expand All @@ -167,21 +143,23 @@ impl Iterator for InterfaceAddressIterator {
/// }
/// }
/// ```
pub fn getifaddrs() -> Result<InterfaceAddressIterator> {
pub fn getifaddrs() -> Result<InterfaceAddresses> {
let mut addrs = mem::MaybeUninit::<*mut libc::ifaddrs>::uninit();
unsafe {
Errno::result(libc::getifaddrs(addrs.as_mut_ptr())).map(|_| {
InterfaceAddressIterator {
InterfaceAddresses {
base: addrs.assume_init(),
next: addrs.assume_init(),
}
})
}
}

#[cfg(test)]
mod tests {
use std::collections::HashMap;

use super::*;
use crate::sys::socket::{AddressFamily, Ipv4Address};

// Only checks if `getifaddrs` can be invoked without panicking.
#[test]
Expand All @@ -194,22 +172,65 @@ mod tests {
#[test]
fn test_getifaddrs_netmask_correct() {
let addrs = getifaddrs().unwrap();
for iface in addrs {
for iface in addrs.iter() {
let sock = if let Some(sock) = iface.netmask {
sock
} else {
continue;
};
if sock.family() == Some(crate::sys::socket::AddressFamily::Inet) {
let _ = sock.as_sockaddr_in().unwrap();
if sock.family() == AddressFamily::INET {
let _ = sock.to_ipv4().unwrap();
return;
} else if sock.family()
== Some(crate::sys::socket::AddressFamily::Inet6)
{
let _ = sock.as_sockaddr_in6().unwrap();
} else if sock.family() == AddressFamily::INET6 {
let _ = sock.to_ipv6().unwrap();
return;
}
}
panic!("No address?");
}

#[test]
fn test_get_ifaddrs_netmasks_eq() {
let mut netmasks = HashMap::new();

let ifs = getifaddrs().unwrap();

for ifa in ifs.iter() {
let Some(netmask) =
ifa.netmask.filter(|n| n.family() == AddressFamily::INET)
else {
continue;
};

let ipv4 = *netmask.to_ipv4().unwrap();

let [a, b, c, d] = ipv4.ip().to_be_bytes();

let x = Ipv4Address::new(a, b, c, d, ipv4.port());

assert_eq!(ipv4, x);

netmasks.insert(
ifa.interface_name.clone(),
*netmask.to_ipv4().unwrap(),
);
}

drop(ifs);

let ifs = getifaddrs().unwrap();

for ifa in ifs.iter() {
let Some(netmask) =
ifa.netmask.filter(|n| n.family() == AddressFamily::INET)
else {
continue;
};

assert_eq!(
netmasks.get(&ifa.interface_name).unwrap(),
netmask.to_ipv4().unwrap(),
);
}
}
}
2 changes: 1 addition & 1 deletion src/sys/ptrace/bsd.rs
Expand Up @@ -68,7 +68,7 @@ unsafe fn ptrace_other(
addr,
data,
))
.map(|_| 0)
.map(|_| 0)
}
}

Expand Down