@@ -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+
4573impl 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