Skip to content

Commit

Permalink
Workaround XNU bug in getifaddrs netmasks
Browse files Browse the repository at this point in the history
  • Loading branch information
roblabla committed Aug 10, 2022
1 parent a10078f commit c785f03
Showing 1 changed file with 51 additions and 0 deletions.
51 changes: 51 additions & 0 deletions src/ifaddrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,45 @@ 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 future
/// when setting the field. See https://github.com/nix-rust/nix/issues/1709#issuecomment-1199304470
///
/// To fix this, we overwrite sa_len to have the known (static) value of the
/// structure.
fn workaround_xnu_bug(info: &libc::ifaddrs) {
if info.ifa_netmask.is_null() {
return
}
let family = unsafe {
(*info.ifa_netmask).sa_family
};

let fixup_size = match i32::from(family) {
libc::AF_INET => Some(std::mem::size_of::<libc::sockaddr_in>()),
libc::AF_INET6 => Some(std::mem::size_of::<libc::sockaddr_in6>()),
_ => None
};

if let Some(fixup_size) = fixup_size {
assert!(fixup_size < u8::MAX.into());
let sock = info.ifa_netmask as *mut libc::sockaddr;
unsafe {
if (*sock).sa_len < fixup_size as u8 {
(*sock).sa_len = fixup_size as u8;
}
}
}
}

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 { SockaddrStorage::from_raw(info.ifa_addr, None) };
if cfg!(target_os = "macos") {
workaround_xnu_bug(info)
}
let netmask = unsafe { SockaddrStorage::from_raw(info.ifa_netmask, None) };
let mut addr = InterfaceAddress {
interface_name: ifname.to_string_lossy().to_string(),
Expand Down Expand Up @@ -144,4 +178,21 @@ mod tests {
fn test_getifaddrs() {
let _ = getifaddrs();
}

#[test]
fn test_getifaddrs_netmask_correct() {
let addrs = getifaddrs().unwrap();
for iface in addrs {
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();
return;
}
}
panic!("No address?");
}
}

0 comments on commit c785f03

Please sign in to comment.