network_interface/target/
windows.rs

1use std::ffi::c_void;
2use std::fmt::Pointer;
3use std::mem::size_of;
4use std::net::{Ipv4Addr, Ipv6Addr};
5use std::ptr::null_mut;
6use std::slice::from_raw_parts;
7use std::iter::Iterator;
8use std::marker::PhantomData;
9
10use libc::{free, malloc, wchar_t, wcslen};
11use winapi::{
12    ctypes::c_ulong,
13    shared::{
14        ws2def::{AF_UNSPEC, SOCKADDR_IN},
15        ws2ipdef::SOCKADDR_IN6,
16        netioapi::{ConvertLengthToIpv4Mask, ConvertInterfaceLuidToIndex},
17        ntdef::ULONG,
18        ifdef::IF_LUID,
19        winerror,
20    },
21    um::{
22        iptypes::{IP_ADAPTER_ADDRESSES, IP_ADAPTER_UNICAST_ADDRESS, IP_ADAPTER_PREFIX},
23        iphlpapi::GetAdaptersAddresses,
24    },
25};
26
27use crate::utils::hex::HexSlice;
28use crate::utils::ffialloc::FFIAlloc;
29use crate::{Addr, Error, NetworkInterface, NetworkInterfaceConfig, Result, V4IfAddr, V6IfAddr};
30use crate::interface::Netmask;
31
32/// An alias for `IP_ADAPTER_ADDRESSES`
33type AdapterAddress = IP_ADAPTER_ADDRESSES;
34
35/// A constant to store `winapi::shared::ws2def::AF_INET` casted as `u16`
36const AF_INET: u16 = winapi::shared::ws2def::AF_INET as u16;
37
38/// A constant to store ` winapi::shared::ws2def::AF_INET6` casted as `u16`
39const AF_INET6: u16 = winapi::shared::ws2def::AF_INET6 as u16;
40
41/// The address family of the addresses to retrieve. This parameter must be one of the following values.
42/// The default address family is `AF_UNSPECT` in order to gather both IPv4 and IPv6 network interfaces.
43///
44/// Source: https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersaddresses#parameters
45const GET_ADAPTERS_ADDRESSES_FAMILY: u32 = AF_UNSPEC as u32;
46
47/// A constant to store `winapi::um::iptypes::GAA_FLAG_INCLUDE_PREFIX`
48const GET_ADAPTERS_ADDRESSES_FLAGS: ULONG = winapi::um::iptypes::GAA_FLAG_INCLUDE_PREFIX;
49
50type MacAddress = Option<String>;
51
52macro_rules! iterable_raw_pointer {
53    ($t: ty, $n: ident) => {
54        impl IterableRawPointer for $t {
55            type Pointer = *const $t;
56            type Value = $t;
57
58            fn next(&self) -> Self::Pointer {
59                self.$n
60            }
61        }
62    };
63}
64
65iterable_raw_pointer!(IP_ADAPTER_ADDRESSES, Next);
66iterable_raw_pointer!(IP_ADAPTER_UNICAST_ADDRESS, Next);
67iterable_raw_pointer!(IP_ADAPTER_PREFIX, Next);
68
69impl NetworkInterfaceConfig for NetworkInterface {
70    fn show() -> Result<Vec<NetworkInterface>> {
71        // Allocate a 15 KB buffer to start with.
72        let mut buffer_size: u32 = 15000;
73        // Limit retries
74        const MAX_TRIES: i32 = 10;
75        let mut try_no = 1;
76
77        let adapter_address = loop {
78            let adapter_address = FFIAlloc::alloc(buffer_size as usize).ok_or_else(|| {
79                // Memory allocation failed for IP_ADAPTER_ADDRESSES struct
80                Error::GetIfAddrsError(String::from("GetAdaptersAddresses"), 1)
81            })?;
82
83            let res = unsafe {
84                GetAdaptersAddresses(
85                    GET_ADAPTERS_ADDRESSES_FAMILY,
86                    GET_ADAPTERS_ADDRESSES_FLAGS,
87                    null_mut(),
88                    adapter_address.as_mut_ptr(),
89                    &mut buffer_size,
90                )
91            };
92            match res {
93                winerror::ERROR_SUCCESS => {
94                    break Ok(adapter_address);
95                }
96                winerror::ERROR_BUFFER_OVERFLOW => {
97                    // The buffer size indicated by the `SizePointer` parameter is too small to hold the
98                    // adapter information or the `AdapterAddresses` parameter is `NULL`. The `SizePointer`
99                    // parameter returned points to the required size of the buffer to hold the adapter
100                    // information.
101                    //
102                    // Source: https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersaddresses#return-value
103                    if try_no == MAX_TRIES {
104                        break Err(Error::GetIfAddrsError(
105                            "GetAdapterAddresses: alloc error".to_string(),
106                            res as i32,
107                        ));
108                    }
109                    try_no += 1;
110                }
111                _ => {
112                    break Err(Error::GetIfAddrsError(
113                        "GetAdapterAddresses".to_string(),
114                        res as i32,
115                    ));
116                }
117            }
118        }?;
119
120        // iterate over the contained structs
121        let mut network_interfaces = Vec::<NetworkInterface>::new();
122
123        for adapter_address in RawPointerWrapper::new(adapter_address.as_ptr()) {
124            let name = make_adapter_address_name(adapter_address)?;
125            let index = get_adapter_address_index(adapter_address)?;
126            let mac_addr = make_mac_address(adapter_address);
127            let mut network_interface = NetworkInterface {
128                name,
129                addr: Vec::new(),
130                mac_addr,
131                index,
132            };
133
134            for current_unicast_address in
135                RawPointerWrapper::new(adapter_address.FirstUnicastAddress)
136            {
137                let address = current_unicast_address.Address;
138
139                network_interface
140                    .addr
141                    .push(match unsafe { (*address.lpSockaddr).sa_family } {
142                        AF_INET => {
143                            let sockaddr = &unsafe { *(address.lpSockaddr as *const SOCKADDR_IN) };
144                            Addr::V4(V4IfAddr {
145                                ip: make_ipv4_addr(sockaddr),
146                                broadcast: lookup_ipv4_broadcast_addr(adapter_address, sockaddr),
147                                netmask: make_ipv4_netmask(current_unicast_address),
148                            })
149                        }
150                        AF_INET6 => {
151                            let sockaddr = &unsafe { *(address.lpSockaddr as *const SOCKADDR_IN6) };
152                            Addr::V6(V6IfAddr {
153                                ip: make_ipv6_addr(sockaddr)?,
154                                broadcast: None,
155                                netmask: make_ipv6_netmask(sockaddr),
156                            })
157                        }
158                        _ => continue,
159                    });
160            }
161
162            network_interfaces.push(network_interface);
163        }
164
165        Ok(network_interfaces)
166    }
167}
168
169// Find broadcast address
170//
171// see https://docs.microsoft.com/en-us/windows/win32/api/iptypes/ns-iptypes-ip_adapter_addresses_lh
172//
173// On Windows Vista and later, the linked IP_ADAPTER_PREFIX structures pointed
174// to by the FirstPrefix member include three IP adapter prefixes for each IPv4
175// address assigned to the adapter. These include
176// 0. the host IP address prefix
177// 1. the subnet IP address prefix
178// 2. and the subnet broadcast IP address prefix. << we want these
179// In addition, for each adapter with n IP adresses there are (not used)
180// 3*n + 0. multicast address prefix
181// 3*n + 1. and a broadcast address prefix.sb
182//
183// The order of addresses in prefix list and unicast list is not guaranteed to
184// be the same, so we search for the unicast address in the prefix list, and
185// then the broadcast address is next in list.
186fn lookup_ipv4_broadcast_addr(
187    adapter_address: &IP_ADAPTER_ADDRESSES,
188    unicast_ip: &SOCKADDR_IN,
189) -> Option<Ipv4Addr> {
190    let mut prefix_index_v4 = 0;
191    let mut broadcast_index: Option<i32> = None;
192
193    // Find adapter
194    for prefix_address in RawPointerWrapper::new(adapter_address.FirstPrefix) {
195        let address = prefix_address.Address;
196
197        if unsafe { (*address.lpSockaddr).sa_family } == AF_INET {
198            let sockaddr = &unsafe { *(address.lpSockaddr as *const SOCKADDR_IN) };
199
200            if let Some(broadcast_index) = broadcast_index {
201                if prefix_index_v4 == broadcast_index {
202                    return Some(make_ipv4_addr(sockaddr));
203                }
204            } else if prefix_index_v4 % 3 == 1 && ipv4_addr_equal(sockaddr, unicast_ip) {
205                broadcast_index = Some(prefix_index_v4 + 1);
206            }
207            prefix_index_v4 += 1;
208        }
209    }
210    None
211}
212
213/// Retrieves the network interface name
214fn make_adapter_address_name(adapter_address: &AdapterAddress) -> Result<String> {
215    let address_name = adapter_address.FriendlyName;
216    let address_name_length = unsafe { wcslen(address_name as *const wchar_t) };
217    let byte_slice = unsafe { from_raw_parts(address_name, address_name_length) };
218    let string = String::from_utf16(byte_slice).map_err(Error::from)?;
219
220    Ok(string)
221}
222
223/// Creates a `Ipv6Addr` from a `SOCKADDR_IN6`
224fn make_ipv6_addr(sockaddr: &SOCKADDR_IN6) -> Result<Ipv6Addr> {
225    let address_bytes = unsafe { sockaddr.sin6_addr.u.Byte() };
226    let ip = Ipv6Addr::from(*address_bytes);
227
228    Ok(ip)
229}
230
231/// Creates a `Ipv4Addr` from a `SOCKADDR_IN`
232fn make_ipv4_addr(sockaddr: &SOCKADDR_IN) -> Ipv4Addr {
233    let address = unsafe { sockaddr.sin_addr.S_un.S_addr() };
234
235    if cfg!(target_endian = "little") {
236        // due to a difference on how bytes are arranged on a
237        // single word of memory by the CPU, swap bytes based
238        // on CPU endianess to avoid having twisted IP addresses
239        //
240        // refer: https://github.com/rust-lang/rust/issues/48819
241        return Ipv4Addr::from(address.swap_bytes());
242    }
243
244    Ipv4Addr::from(*address)
245}
246
247/// Compare 2 ipv4 addresses.
248fn ipv4_addr_equal(sockaddr1: &SOCKADDR_IN, sockaddr2: &SOCKADDR_IN) -> bool {
249    let address1 = unsafe { sockaddr1.sin_addr.S_un.S_addr() };
250    let address2 = unsafe { sockaddr2.sin_addr.S_un.S_addr() };
251    address1 == address2
252}
253
254/// This function relies on the `GetAdapterAddresses` API which is available only on Windows Vista
255/// and later versions.
256///
257/// An implementation of `GetIpAddrTable` to get all available network interfaces would be required
258/// in order to support previous versions of Windows.
259fn make_ipv4_netmask(unicast_address: &IP_ADAPTER_UNICAST_ADDRESS) -> Netmask<Ipv4Addr> {
260    let mut mask: c_ulong = 0;
261    let on_link_prefix_length = unicast_address.OnLinkPrefixLength;
262    unsafe {
263        ConvertLengthToIpv4Mask(on_link_prefix_length as u32, &mut mask as *mut c_ulong);
264    }
265
266    if cfg!(target_endian = "little") {
267        // due to a difference on how bytes are arranged on a
268        // single word of memory by the CPU, swap bytes based
269        // on CPU endianess to avoid having twisted IP addresses
270        //
271        // refer: https://github.com/rust-lang/rust/issues/48819
272        return Some(Ipv4Addr::from(mask.swap_bytes()));
273    }
274
275    Some(Ipv4Addr::from(mask))
276}
277
278fn make_ipv6_netmask(_sockaddr: &SOCKADDR_IN6) -> Netmask<Ipv6Addr> {
279    None
280}
281
282/// Creates MacAddress from AdapterAddress
283fn make_mac_address(adapter_address: &AdapterAddress) -> MacAddress {
284    // see https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersaddresses#examples
285    let mac_addr_len = adapter_address.PhysicalAddressLength as usize;
286    match mac_addr_len {
287        0 => None,
288        len => Some(format!(
289            "{}",
290            HexSlice::new(&adapter_address.PhysicalAddress[..len])
291        )),
292    }
293}
294
295fn get_adapter_address_index(adapter_address: &AdapterAddress) -> Result<u32> {
296    let adapter_luid = &adapter_address.Luid as *const IF_LUID;
297
298    let index = &mut 0u32 as *mut u32;
299
300    match unsafe { ConvertInterfaceLuidToIndex(adapter_luid, index) } {
301        0 => Ok(unsafe { *index }),
302        e => Err(crate::error::Error::GetIfNameError(
303            "ConvertInterfaceLuidToIndex".to_string(),
304            e,
305        )),
306    }
307}
308
309/// Trait for linked lists in Windows API structures iteration
310trait IterableRawPointer {
311    type Pointer;
312    type Value;
313
314    ///  Returns: pointer to the next element in the linked list
315    ///           null at the end
316    fn next(&self) -> Self::Pointer;
317}
318
319/// Raw pointer container
320struct RawPointerWrapper<'a, T>(*const T, PhantomData<&'a T>)
321where
322    T: IterableRawPointer<Value = T, Pointer = *const T>;
323
324impl<'a, T> RawPointerWrapper<'a, T>
325where
326    T: IterableRawPointer<Value = T, Pointer = *const T>,
327{
328    fn new(ptr: *const T) -> RawPointerWrapper<'a, T> {
329        Self(ptr, PhantomData)
330    }
331}
332
333/// Iterator implementation for RawPointer
334impl<'a, T> Iterator for RawPointerWrapper<'a, T>
335where
336    T: IterableRawPointer<Value = T, Pointer = *const T>,
337{
338    type Item = &'a T::Value;
339
340    fn next(&mut self) -> Option<Self::Item> {
341        let ret = unsafe { self.0.as_ref() };
342        if let Some(v) = ret {
343            self.0 = v.next();
344        }
345        ret
346    }
347}
348
349#[cfg(test)]
350mod tests {
351    use std::{process::Command, cmp::min};
352
353    use crate::{NetworkInterface, NetworkInterfaceConfig, Addr};
354
355    #[test]
356    fn test_mac_addr() {
357        const MAC_ADDR_LEN: usize = "00:22:48:03:ED:76".len();
358
359        let output = Command::new("getmac").arg("/nh").output().unwrap().stdout;
360        let output_string = String::from_utf8(output).unwrap();
361        let mac_addr_list: Vec<_> = output_string
362            .lines()
363            .filter_map(|line| {
364                let line = line.trim();
365                let line = &line[..min(MAC_ADDR_LEN, line.len())];
366                match line.split('-').count() {
367                    6 => Some(line.replace('-', ":")),
368                    _ => None,
369                }
370            })
371            .collect();
372        assert!(!mac_addr_list.is_empty());
373
374        let interfaces = NetworkInterface::show().unwrap();
375        for mac_addr in mac_addr_list {
376            assert!(interfaces
377                .iter()
378                .any(|int| int.mac_addr.as_ref() == Some(&mac_addr)));
379        }
380    }
381
382    #[test]
383    // Check IP address consistency.
384    fn test_ipv4_broadcast() {
385        let interfaces = NetworkInterface::show().unwrap();
386        for ipv4 in interfaces.iter().flat_map(|i| &i.addr).filter_map(|addr| {
387            if let Addr::V4(ipv4) = addr {
388                Some(ipv4)
389            } else {
390                None
391            }
392        }) {
393            let Some(bc_addr) = ipv4.broadcast else {
394                continue;
395            };
396            let ip_bytes = ipv4.ip.octets();
397            let mask_bytes = ipv4.netmask.unwrap().octets();
398            let bc_bytes = bc_addr.octets();
399            for i in 0..4 {
400                assert_eq!(ip_bytes[i] & mask_bytes[i], bc_bytes[i] & mask_bytes[i]);
401                assert_eq!(bc_bytes[i] | mask_bytes[i], 255);
402            }
403        }
404    }
405}