Skip to content

Commit c680f93

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

File tree

1 file changed

+53
-0
lines changed

1 file changed

+53
-0
lines changed

src/ifaddrs.rs

+53
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,47 @@ 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 future
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+
if info.ifa_netmask.is_null() {
54+
return
55+
}
56+
let family = unsafe {
57+
(*info.ifa_netmask).sa_family
58+
};
59+
60+
let fixup_size = match i32::from(family) {
61+
libc::AF_INET => Some(std::mem::size_of::<libc::sockaddr_in>()),
62+
libc::AF_INET6 => Some(std::mem::size_of::<libc::sockaddr_in6>()),
63+
_ => None
64+
};
65+
66+
if let Some(fixup_size) = fixup_size {
67+
assert!(fixup_size < u8::MAX.into());
68+
let sock = info.ifa_netmask as *mut libc::sockaddr;
69+
unsafe {
70+
if (*sock).sa_len < fixup_size as u8 {
71+
(*sock).sa_len = fixup_size as u8;
72+
}
73+
}
74+
}
75+
}
76+
4577
impl InterfaceAddress {
4678
/// Create an `InterfaceAddress` from the libc struct.
4779
fn from_libc_ifaddrs(info: &libc::ifaddrs) -> InterfaceAddress {
4880
let ifname = unsafe { ffi::CStr::from_ptr(info.ifa_name) };
4981
let address = unsafe { SockaddrStorage::from_raw(info.ifa_addr, None) };
82+
#[cfg(target_os = "macos")]
83+
{
84+
workaround_xnu_bug(info)
85+
}
5086
let netmask = unsafe { SockaddrStorage::from_raw(info.ifa_netmask, None) };
5187
let mut addr = InterfaceAddress {
5288
interface_name: ifname.to_string_lossy().to_string(),
@@ -144,4 +180,21 @@ mod tests {
144180
fn test_getifaddrs() {
145181
let _ = getifaddrs();
146182
}
183+
184+
#[test]
185+
fn test_getifaddrs_netmask_correct() {
186+
let addrs = getifaddrs().unwrap();
187+
for iface in addrs {
188+
let sock = if let Some(sock) = iface.netmask {
189+
sock
190+
} else {
191+
continue
192+
};
193+
if sock.family() == Some(crate::sys::socket::AddressFamily::Inet) {
194+
let _ = sock.as_sockaddr_in().unwrap();
195+
return;
196+
}
197+
}
198+
panic!("No address?");
199+
}
147200
}

0 commit comments

Comments
 (0)