2
2
//!
3
3
//! Uses the Linux and/or BSD specific function `getifaddrs` to query the list
4
4
//! of interfaces and their associated addresses.
5
-
6
5
use cfg_if:: cfg_if;
7
- #[ cfg( apple_targets) ]
8
- use std:: convert:: TryFrom ;
9
6
use std:: ffi;
10
7
use std:: iter:: Iterator ;
8
+ use std:: marker:: PhantomData ;
11
9
use std:: mem;
12
10
use std:: option:: Option ;
13
11
14
12
use crate :: net:: if_:: * ;
15
- use crate :: sys:: socket:: { SockaddrLike , SockaddrStorage } ;
13
+ use crate :: sys:: socket:: RawAddr ;
16
14
use crate :: { Errno , Result } ;
17
15
18
16
/// Describes a single address for an interface as returned by `getifaddrs`.
19
- #[ derive( Clone , Debug , Eq , Hash , PartialEq ) ]
20
- pub struct InterfaceAddress {
17
+ #[ derive( Clone , Debug , Eq , PartialEq ) ]
18
+ pub struct InterfaceAddress < ' a > {
21
19
/// Name of the network interface
22
20
pub interface_name : String ,
23
21
/// Flags as from `SIOCGIFFLAGS` ioctl
24
22
pub flags : InterfaceFlags ,
25
23
/// Network address of this interface
26
- pub address : Option < SockaddrStorage > ,
24
+ pub address : Option < RawAddr < ' a > > ,
27
25
/// Netmask of this interface
28
- pub netmask : Option < SockaddrStorage > ,
26
+ pub netmask : Option < RawAddr < ' a > > ,
29
27
/// Broadcast address of this interface, if applicable
30
- pub broadcast : Option < SockaddrStorage > ,
28
+ pub broadcast : Option < RawAddr < ' a > > ,
31
29
/// Point-to-point destination address
32
- pub destination : Option < SockaddrStorage > ,
30
+ pub destination : Option < RawAddr < ' a > > ,
33
31
}
34
32
35
33
cfg_if ! {
@@ -44,54 +42,12 @@ cfg_if! {
44
42
}
45
43
}
46
44
47
- /// Workaround a bug in XNU where netmasks will always have the wrong size in
48
- /// the sa_len field due to the kernel ignoring trailing zeroes in the structure
49
- /// when setting the field. See https://github.com/nix-rust/nix/issues/1709#issuecomment-1199304470
50
- ///
51
- /// To fix this, we stack-allocate a new sockaddr_storage, zero it out, and
52
- /// memcpy sa_len of the netmask to that new storage. Finally, we reset the
53
- /// ss_len field to sizeof(sockaddr_storage). This is supposedly valid as all
54
- /// members of the sockaddr_storage are "ok" with being zeroed out (there are
55
- /// no pointers).
56
- #[ cfg( apple_targets) ]
57
- unsafe fn workaround_xnu_bug ( info : & libc:: ifaddrs ) -> Option < SockaddrStorage > {
58
- let src_sock = info. ifa_netmask ;
59
- if src_sock. is_null ( ) {
60
- return None ;
61
- }
62
-
63
- let mut dst_sock = mem:: MaybeUninit :: < libc:: sockaddr_storage > :: zeroed ( ) ;
64
-
65
- let dst_sock = unsafe {
66
- // memcpy only sa_len bytes, assume the rest is zero
67
- std:: ptr:: copy_nonoverlapping (
68
- src_sock as * const u8 ,
69
- dst_sock. as_mut_ptr ( ) . cast ( ) ,
70
- ( * src_sock) . sa_len . into ( ) ,
71
- ) ;
72
-
73
- // Initialize ss_len to sizeof(libc::sockaddr_storage).
74
- ( * dst_sock. as_mut_ptr ( ) ) . ss_len =
75
- u8:: try_from ( mem:: size_of :: < libc:: sockaddr_storage > ( ) ) . unwrap ( ) ;
76
- dst_sock. assume_init ( )
77
- } ;
78
-
79
- let dst_sock_ptr =
80
- & dst_sock as * const libc:: sockaddr_storage as * const libc:: sockaddr ;
81
-
82
- unsafe { SockaddrStorage :: from_raw ( dst_sock_ptr, None ) }
83
- }
84
-
85
- impl InterfaceAddress {
45
+ impl < ' a > InterfaceAddress < ' a > {
86
46
/// Create an `InterfaceAddress` from the libc struct.
87
47
fn from_libc_ifaddrs ( info : & libc:: ifaddrs ) -> InterfaceAddress {
88
48
let ifname = unsafe { ffi:: CStr :: from_ptr ( info. ifa_name ) } ;
89
- let address = unsafe { SockaddrStorage :: from_raw ( info. ifa_addr , None ) } ;
90
- #[ cfg( apple_targets) ]
91
- let netmask = unsafe { workaround_xnu_bug ( info) } ;
92
- #[ cfg( not( apple_targets) ) ]
93
- let netmask =
94
- unsafe { SockaddrStorage :: from_raw ( info. ifa_netmask , None ) } ;
49
+ let address = unsafe { RawAddr :: new ( & * info. ifa_addr ) } ;
50
+ let netmask = unsafe { RawAddr :: new ( & * info. ifa_netmask ) } ;
95
51
let mut addr = InterfaceAddress {
96
52
interface_name : ifname. to_string_lossy ( ) . to_string ( ) ,
97
53
flags : InterfaceFlags :: from_bits_truncate ( info. ifa_flags as i32 ) ,
@@ -103,9 +59,9 @@ impl InterfaceAddress {
103
59
104
60
let ifu = get_ifu_from_sockaddr ( info) ;
105
61
if addr. flags . contains ( InterfaceFlags :: IFF_POINTOPOINT ) {
106
- addr. destination = unsafe { SockaddrStorage :: from_raw ( ifu, None ) } ;
62
+ addr. destination = unsafe { RawAddr :: new ( & * ifu) } ;
107
63
} else if addr. flags . contains ( InterfaceFlags :: IFF_BROADCAST ) {
108
- addr. broadcast = unsafe { SockaddrStorage :: from_raw ( ifu, None ) } ;
64
+ addr. broadcast = unsafe { RawAddr :: new ( & * ifu) } ;
109
65
}
110
66
111
67
addr
@@ -114,23 +70,43 @@ impl InterfaceAddress {
114
70
115
71
/// Holds the results of `getifaddrs`.
116
72
///
117
- /// Use the function `getifaddrs` to create this Iterator. Note that the
118
- /// actual list of interfaces can be iterated once and will be freed as
119
- /// soon as the Iterator goes out of scope.
120
- #[ derive( Debug , Eq , Hash , PartialEq ) ]
121
- pub struct InterfaceAddressIterator {
73
+ /// Use the function `getifaddrs` to create this struct and [`Self::iter`]
74
+ /// to create the iterator. Note that the actual list of interfaces can be
75
+ /// iterated once and will be freed as soon as the Iterator goes out of scope.
76
+ #[ derive( Debug ) ]
77
+ pub struct InterfaceAddresses {
122
78
base : * mut libc:: ifaddrs ,
123
- next : * mut libc:: ifaddrs ,
124
79
}
125
80
126
- impl Drop for InterfaceAddressIterator {
81
+ impl InterfaceAddresses {
82
+ /// Create an iterator over the list of interfaces.
83
+ pub fn iter ( & self ) -> InterfaceAddressIterator < ' _ > {
84
+ InterfaceAddressIterator {
85
+ next : self . base ,
86
+ _a : PhantomData ,
87
+ }
88
+ }
89
+ }
90
+
91
+ impl Drop for InterfaceAddresses {
127
92
fn drop ( & mut self ) {
128
93
unsafe { libc:: freeifaddrs ( self . base ) } ;
129
94
}
130
95
}
131
96
132
- impl Iterator for InterfaceAddressIterator {
133
- type Item = InterfaceAddress ;
97
+ /// Holds the results of `getifaddrs`.
98
+ ///
99
+ /// Use the function `getifaddrs` to create this Iterator. Note that the
100
+ /// actual list of interfaces can be iterated once and will be freed as
101
+ /// soon as the Iterator goes out of scope.
102
+ #[ derive( Debug , Eq , Hash , PartialEq ) ]
103
+ pub struct InterfaceAddressIterator < ' a > {
104
+ next : * mut libc:: ifaddrs ,
105
+ _a : PhantomData < & ' a ( ) > ,
106
+ }
107
+
108
+ impl < ' a > Iterator for InterfaceAddressIterator < ' a > {
109
+ type Item = InterfaceAddress < ' a > ;
134
110
fn next ( & mut self ) -> Option < <Self as Iterator >:: Item > {
135
111
match unsafe { self . next . as_ref ( ) } {
136
112
Some ( ifaddr) => {
@@ -154,7 +130,7 @@ impl Iterator for InterfaceAddressIterator {
154
130
/// # Example
155
131
/// ```
156
132
/// let addrs = nix::ifaddrs::getifaddrs().unwrap();
157
- /// for ifaddr in addrs {
133
+ /// for ifaddr in addrs.iter() {
158
134
/// match ifaddr.address {
159
135
/// Some(address) => {
160
136
/// println!("interface {} address {}",
@@ -167,21 +143,23 @@ impl Iterator for InterfaceAddressIterator {
167
143
/// }
168
144
/// }
169
145
/// ```
170
- pub fn getifaddrs ( ) -> Result < InterfaceAddressIterator > {
146
+ pub fn getifaddrs ( ) -> Result < InterfaceAddresses > {
171
147
let mut addrs = mem:: MaybeUninit :: < * mut libc:: ifaddrs > :: uninit ( ) ;
172
148
unsafe {
173
149
Errno :: result ( libc:: getifaddrs ( addrs. as_mut_ptr ( ) ) ) . map ( |_| {
174
- InterfaceAddressIterator {
150
+ InterfaceAddresses {
175
151
base : addrs. assume_init ( ) ,
176
- next : addrs. assume_init ( ) ,
177
152
}
178
153
} )
179
154
}
180
155
}
181
156
182
157
#[ cfg( test) ]
183
158
mod tests {
159
+ use std:: collections:: HashMap ;
160
+
184
161
use super :: * ;
162
+ use crate :: sys:: socket:: { AddressFamily , Ipv4Address } ;
185
163
186
164
// Only checks if `getifaddrs` can be invoked without panicking.
187
165
#[ test]
@@ -194,22 +172,65 @@ mod tests {
194
172
#[ test]
195
173
fn test_getifaddrs_netmask_correct ( ) {
196
174
let addrs = getifaddrs ( ) . unwrap ( ) ;
197
- for iface in addrs {
175
+ for iface in addrs. iter ( ) {
198
176
let sock = if let Some ( sock) = iface. netmask {
199
177
sock
200
178
} else {
201
179
continue ;
202
180
} ;
203
- if sock. family ( ) == Some ( crate :: sys :: socket :: AddressFamily :: Inet ) {
204
- let _ = sock. as_sockaddr_in ( ) . unwrap ( ) ;
181
+ if sock. family ( ) == AddressFamily :: INET {
182
+ let _ = sock. to_ipv4 ( ) . unwrap ( ) ;
205
183
return ;
206
- } else if sock. family ( )
207
- == Some ( crate :: sys:: socket:: AddressFamily :: Inet6 )
208
- {
209
- let _ = sock. as_sockaddr_in6 ( ) . unwrap ( ) ;
184
+ } else if sock. family ( ) == AddressFamily :: INET6 {
185
+ let _ = sock. to_ipv6 ( ) . unwrap ( ) ;
210
186
return ;
211
187
}
212
188
}
213
189
panic ! ( "No address?" ) ;
214
190
}
191
+
192
+ #[ test]
193
+ fn test_get_ifaddrs_netmasks_eq ( ) {
194
+ let mut netmasks = HashMap :: new ( ) ;
195
+
196
+ let ifs = getifaddrs ( ) . unwrap ( ) ;
197
+
198
+ for ifa in ifs. iter ( ) {
199
+ let Some ( netmask) =
200
+ ifa. netmask . filter ( |n| n. family ( ) == AddressFamily :: INET )
201
+ else {
202
+ continue ;
203
+ } ;
204
+
205
+ let ipv4 = * netmask. to_ipv4 ( ) . unwrap ( ) ;
206
+
207
+ let [ a, b, c, d] = ipv4. ip ( ) . to_be_bytes ( ) ;
208
+
209
+ let x = Ipv4Address :: new ( a, b, c, d, ipv4. port ( ) ) ;
210
+
211
+ assert_eq ! ( ipv4, x) ;
212
+
213
+ netmasks. insert (
214
+ ifa. interface_name . clone ( ) ,
215
+ * netmask. to_ipv4 ( ) . unwrap ( ) ,
216
+ ) ;
217
+ }
218
+
219
+ drop ( ifs) ;
220
+
221
+ let ifs = getifaddrs ( ) . unwrap ( ) ;
222
+
223
+ for ifa in ifs. iter ( ) {
224
+ let Some ( netmask) =
225
+ ifa. netmask . filter ( |n| n. family ( ) == AddressFamily :: INET )
226
+ else {
227
+ continue ;
228
+ } ;
229
+
230
+ assert_eq ! (
231
+ netmasks. get( & ifa. interface_name) . unwrap( ) ,
232
+ netmask. to_ipv4( ) . unwrap( ) ,
233
+ ) ;
234
+ }
235
+ }
215
236
}
0 commit comments