Skip to content

Commit fd0a878

Browse files
committed
Workaround XNU bug in getifaddrs netmasks
1 parent a10078f commit fd0a878

File tree

1 file changed

+49
-0
lines changed

1 file changed

+49
-0
lines changed

src/ifaddrs.rs

+49
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,43 @@ cfg_if! {
4242
}
4343
}
4444

45+
/// Workaround a bug in XNU where netmasks will always have the wrong size in
46+
/// the sa_len field due to the kernel ignoring trailing zeroes in the structure
47+
/// when setting the field. See https://github.com/nix-rust/nix/issues/1709#issuecomment-1199304470
48+
///
49+
/// To fix this, we overwrite `sa_len` to have the known (static) value of the
50+
/// structure.
51+
#[cfg(target_os = "macos")]
52+
fn workaround_xnu_bug(info: &libc::ifaddrs) {
53+
let sock = info.ifa_netmask;
54+
if sock.is_null() {
55+
return
56+
}
57+
let family = unsafe {
58+
(*sock).sa_family
59+
};
60+
61+
let fixup_size = match i32::from(family) {
62+
libc::AF_INET => std::mem::size_of::<libc::sockaddr_in>(),
63+
libc::AF_INET6 => std::mem::size_of::<libc::sockaddr_in6>(),
64+
_ => return
65+
};
66+
67+
assert!(fixup_size < u8::MAX.into());
68+
unsafe {
69+
(*sock).sa_len = (*sock).sa_len.max(fixup_size as u8);
70+
}
71+
}
72+
4573
impl InterfaceAddress {
4674
/// Create an `InterfaceAddress` from the libc struct.
4775
fn from_libc_ifaddrs(info: &libc::ifaddrs) -> InterfaceAddress {
4876
let ifname = unsafe { ffi::CStr::from_ptr(info.ifa_name) };
4977
let address = unsafe { SockaddrStorage::from_raw(info.ifa_addr, None) };
78+
#[cfg(target_os = "macos")]
79+
{
80+
workaround_xnu_bug(info)
81+
}
5082
let netmask = unsafe { SockaddrStorage::from_raw(info.ifa_netmask, None) };
5183
let mut addr = InterfaceAddress {
5284
interface_name: ifname.to_string_lossy().to_string(),
@@ -144,4 +176,21 @@ mod tests {
144176
fn test_getifaddrs() {
145177
let _ = getifaddrs();
146178
}
179+
180+
#[test]
181+
fn test_getifaddrs_netmask_correct() {
182+
let addrs = getifaddrs().unwrap();
183+
for iface in addrs {
184+
let sock = if let Some(sock) = iface.netmask {
185+
sock
186+
} else {
187+
continue
188+
};
189+
if sock.family() == Some(crate::sys::socket::AddressFamily::Inet) {
190+
let _ = sock.as_sockaddr_in().unwrap();
191+
return;
192+
}
193+
}
194+
panic!("No address?");
195+
}
147196
}

0 commit comments

Comments
 (0)