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