ndisapi/netlib/ip_helper/network_adapter_info/
statics.rs

1use crate::{IfLuid, IphlpNetworkAdapterInfo, MacAddress};
2use windows::Win32::Foundation::{SetLastError, ERROR_BUFFER_OVERFLOW, NO_ERROR};
3use windows::Win32::NetworkManagement::IpHelper::{
4    FreeMibTable, GetAdaptersAddresses, GetIfTable2, GAA_FLAG_INCLUDE_ALL_INTERFACES,
5    GAA_FLAG_INCLUDE_GATEWAYS, GAA_FLAG_SKIP_ANYCAST, GAA_FLAG_SKIP_MULTICAST,
6    IF_TYPE_SOFTWARE_LOOPBACK,
7};
8use windows::Win32::NetworkManagement::Ndis::IfOperStatusUp;
9use windows::Win32::Networking::WinSock::AF_UNSPEC;
10use windows::Win32::{
11    Foundation::WIN32_ERROR,
12    NetworkManagement::IpHelper::{IP_ADAPTER_ADDRESSES_LH, MIB_IF_TABLE2},
13};
14
15impl IphlpNetworkAdapterInfo {
16    /// Returns a list of network interfaces which:
17    /// 1. Have at least one unicast address assigned
18    /// 2. Operational (IfOperStatusUp)
19    /// 3. Not software loopback
20    ///
21    /// # Returns
22    ///
23    /// * `Vec<IphlpNetworkAdapterInfo>`: A vector of `IphlpNetworkAdapterInfo` objects.
24    ///
25    /// # Safety
26    ///
27    /// This function uses unsafe Windows API calls to get the network interface information.
28    /// The caller should ensure that the returned `IphlpNetworkAdapterInfo` objects are not used
29    /// after the corresponding network interfaces have been removed or disabled.
30    pub fn get_external_network_connections() -> Vec<IphlpNetworkAdapterInfo> {
31        let mut ret_val = Vec::new();
32        let mut dw_size = 0;
33        let mut mib_table: *mut MIB_IF_TABLE2 = std::ptr::null_mut();
34
35        // Query detailed information on available network interfaces
36        let error_code = unsafe { GetIfTable2(&mut mib_table) };
37        if error_code.is_err() {
38            return ret_val;
39        }
40
41        let error_code = unsafe {
42            GetAdaptersAddresses(
43                AF_UNSPEC.0 as u32,
44                GAA_FLAG_SKIP_ANYCAST
45                    | GAA_FLAG_SKIP_MULTICAST
46                    | GAA_FLAG_INCLUDE_GATEWAYS
47                    | GAA_FLAG_INCLUDE_ALL_INTERFACES,
48                None,
49                None,
50                &mut dw_size,
51            )
52        };
53
54        // Get available unicast addresses
55        if WIN32_ERROR(error_code) == ERROR_BUFFER_OVERFLOW && dw_size != 0 {
56            loop {
57                let mut ip_address_info = vec![0u8; dw_size as usize];
58
59                let error_code = unsafe {
60                    GetAdaptersAddresses(
61                        AF_UNSPEC.0 as u32,
62                        GAA_FLAG_SKIP_ANYCAST
63                            | GAA_FLAG_SKIP_MULTICAST
64                            | GAA_FLAG_INCLUDE_GATEWAYS
65                            | GAA_FLAG_INCLUDE_ALL_INTERFACES,
66                        None,
67                        Some(ip_address_info.as_mut_ptr() as *mut IP_ADAPTER_ADDRESSES_LH),
68                        &mut dw_size,
69                    )
70                };
71
72                if WIN32_ERROR(error_code) == NO_ERROR {
73                    let mut current_address =
74                        ip_address_info.as_ptr() as *mut IP_ADAPTER_ADDRESSES_LH;
75
76                    while !current_address.is_null() {
77                        let current = unsafe { &*current_address };
78
79                        if current.FirstUnicastAddress.is_null()
80                            || current.OperStatus != IfOperStatusUp
81                            || current.IfType == IF_TYPE_SOFTWARE_LOOPBACK
82                        {
83                            current_address = current.Next;
84                            continue;
85                        }
86
87                        // Lookup advanced information on the network interface
88                        for i in 0..unsafe { (*mib_table).NumEntries } {
89                            let table_ptr = unsafe { (*mib_table).Table.as_ptr() };
90                            let entry_ptr = unsafe { table_ptr.add(i as usize) };
91                            if IfLuid::from(unsafe { (*entry_ptr).InterfaceLuid })
92                                == IfLuid::from(current.Luid)
93                            {
94                                ret_val.push(unsafe {
95                                    IphlpNetworkAdapterInfo::new(current, entry_ptr)
96                                });
97                                break;
98                            }
99                        }
100
101                        current_address = current.Next;
102                    }
103
104                    break;
105                }
106                // In case of insufficient buffer size we try to recover by reallocating buffer
107                if WIN32_ERROR(error_code) != ERROR_BUFFER_OVERFLOW {
108                    unsafe { SetLastError(WIN32_ERROR(error_code)) };
109                    break;
110                }
111            }
112        } else {
113            // GetAdaptersAddresses has failed with status different from ERROR_BUFFER_OVERFLOW when obtaining required buffer size
114            if WIN32_ERROR(error_code) != NO_ERROR {
115                unsafe { SetLastError(WIN32_ERROR(error_code)) };
116            }
117        }
118
119        // Free interface table
120        unsafe { FreeMibTable(mib_table as *const core::ffi::c_void) };
121
122        ret_val
123    }
124
125    /// Finds a network interface by the provided LUID.
126    ///
127    /// # Arguments
128    ///
129    /// * `luid` - An `IfLuid` to look up.
130    ///
131    /// # Returns
132    ///
133    /// * `Option<IphlpNetworkAdapterInfo>` - An optional `IphlpNetworkAdapterInfo` class instance.
134    ///
135    /// # Safety
136    ///
137    /// This function uses unsafe Windows API calls to get the network interface information.
138    /// The caller should ensure that the returned `IphlpNetworkAdapterInfo` objects are not used
139    /// after the corresponding network interfaces have been removed or disabled.
140    pub fn get_connection_by_luid(luid: IfLuid) -> Option<IphlpNetworkAdapterInfo> {
141        let mut dw_size = 0;
142        let mut mib_table: *mut MIB_IF_TABLE2 = std::ptr::null_mut();
143
144        // Query detailed information on available network interfaces
145        if unsafe { GetIfTable2(&mut mib_table) }.is_err() {
146            return None;
147        }
148
149        // Get available unicast addresses
150        let error_code = unsafe {
151            GetAdaptersAddresses(
152                AF_UNSPEC.0 as u32,
153                GAA_FLAG_SKIP_ANYCAST
154                    | GAA_FLAG_SKIP_MULTICAST
155                    | GAA_FLAG_INCLUDE_GATEWAYS
156                    | GAA_FLAG_INCLUDE_ALL_INTERFACES,
157                None,
158                None,
159                &mut dw_size,
160            )
161        };
162
163        if WIN32_ERROR(error_code) == ERROR_BUFFER_OVERFLOW && dw_size != 0 {
164            loop {
165                let mut ip_address_info = vec![0u8; dw_size as usize];
166
167                let error_code = unsafe {
168                    GetAdaptersAddresses(
169                        AF_UNSPEC.0 as u32,
170                        GAA_FLAG_SKIP_ANYCAST
171                            | GAA_FLAG_SKIP_MULTICAST
172                            | GAA_FLAG_INCLUDE_GATEWAYS
173                            | GAA_FLAG_INCLUDE_ALL_INTERFACES,
174                        None,
175                        Some(ip_address_info.as_mut_ptr() as *mut IP_ADAPTER_ADDRESSES_LH),
176                        &mut dw_size,
177                    )
178                };
179
180                if WIN32_ERROR(error_code) == NO_ERROR {
181                    let mut current_address =
182                        ip_address_info.as_ptr() as *mut IP_ADAPTER_ADDRESSES_LH;
183
184                    while !current_address.is_null() {
185                        let current = unsafe { &*current_address };
186
187                        if IfLuid::from(current.Luid) != luid {
188                            current_address = current.Next;
189                            continue;
190                        }
191
192                        // Lookup advanced information on the network interface
193                        for i in 0..unsafe { (*mib_table).NumEntries } {
194                            let table_ptr = unsafe { (*mib_table).Table.as_ptr() };
195                            let entry_ptr = unsafe { table_ptr.add(i as usize) };
196                            if IfLuid::from(unsafe { (*entry_ptr).InterfaceLuid }) == luid {
197                                let result =
198                                    unsafe { IphlpNetworkAdapterInfo::new(current, entry_ptr) };
199                                unsafe { FreeMibTable(mib_table as *const core::ffi::c_void) };
200                                return Some(result);
201                            }
202                        }
203
204                        current_address = current.Next;
205                    }
206
207                    break;
208                }
209                // In case of insufficient buffer size, we try to recover by reallocating the buffer
210                if WIN32_ERROR(error_code) != ERROR_BUFFER_OVERFLOW {
211                    unsafe { SetLastError(WIN32_ERROR(error_code)) };
212                    break;
213                }
214            }
215        } else {
216            // GetAdaptersAddresses has failed with a status different from ERROR_BUFFER_OVERFLOW when obtaining the required buffer size
217            if WIN32_ERROR(error_code) != NO_ERROR {
218                unsafe { SetLastError(WIN32_ERROR(error_code)) };
219            }
220        }
221
222        // Free interface table
223        unsafe { FreeMibTable(mib_table as *const core::ffi::c_void) };
224
225        None
226    }
227
228    /// Finds network interface by provided hardware address.
229    ///
230    /// # Arguments
231    ///
232    /// * `address` - A `MacAddress` to lookup.
233    ///
234    /// # Returns
235    ///
236    /// * `Option<IphlpNetworkAdapterInfo>` - An optional `IphlpNetworkAdapterInfo` object.
237    ///
238    /// # Safety
239    ///
240    /// This function uses unsafe Windows API calls to get the network interface information.
241    /// The caller should ensure that the returned `IphlpNetworkAdapterInfo` objects are not used
242    /// after the corresponding network interfaces have been removed or disabled.
243    pub fn get_connection_by_hw_address(address: &MacAddress) -> Option<IphlpNetworkAdapterInfo> {
244        let mut dw_size = 0;
245        let mut mib_table: *mut MIB_IF_TABLE2 = std::ptr::null_mut();
246
247        // Query detailed information on available network interfaces
248        if unsafe { GetIfTable2(&mut mib_table) }.is_err() {
249            return None;
250        }
251
252        // Get available unicast addresses
253        let error_code = unsafe {
254            GetAdaptersAddresses(
255                AF_UNSPEC.0 as u32,
256                GAA_FLAG_SKIP_ANYCAST
257                    | GAA_FLAG_SKIP_MULTICAST
258                    | GAA_FLAG_INCLUDE_GATEWAYS
259                    | GAA_FLAG_INCLUDE_ALL_INTERFACES,
260                None,
261                None,
262                &mut dw_size,
263            )
264        };
265
266        if WIN32_ERROR(error_code) == ERROR_BUFFER_OVERFLOW && dw_size != 0 {
267            loop {
268                let mut ip_address_info = vec![0u8; dw_size as usize];
269
270                let error_code = unsafe {
271                    GetAdaptersAddresses(
272                        AF_UNSPEC.0 as u32,
273                        GAA_FLAG_SKIP_ANYCAST
274                            | GAA_FLAG_SKIP_MULTICAST
275                            | GAA_FLAG_INCLUDE_GATEWAYS
276                            | GAA_FLAG_INCLUDE_ALL_INTERFACES,
277                        None,
278                        Some(ip_address_info.as_mut_ptr() as *mut IP_ADAPTER_ADDRESSES_LH),
279                        &mut dw_size,
280                    )
281                };
282
283                if WIN32_ERROR(error_code) == NO_ERROR {
284                    let mut current_address =
285                        ip_address_info.as_ptr() as *mut IP_ADAPTER_ADDRESSES_LH;
286
287                    while !current_address.is_null() {
288                        let current = unsafe { &*current_address };
289
290                        if MacAddress::from_slice(&current.PhysicalAddress).unwrap_or_default()
291                            != *address
292                        {
293                            current_address = current.Next;
294                            continue;
295                        }
296
297                        // Lookup advanced information on the network interface
298                        for i in 0..unsafe { (*mib_table).NumEntries } {
299                            let table_ptr = unsafe { (*mib_table).Table.as_ptr() };
300                            let entry_ptr = unsafe { table_ptr.add(i as usize) };
301                            if IfLuid::from(unsafe { (*mib_table).Table[i as usize].InterfaceLuid })
302                                == IfLuid::from(current.Luid)
303                            {
304                                let result =
305                                    unsafe { IphlpNetworkAdapterInfo::new(current, entry_ptr) };
306                                unsafe { FreeMibTable(mib_table as *const core::ffi::c_void) };
307                                return Some(result);
308                            }
309                        }
310
311                        current_address = current.Next;
312                    }
313
314                    break;
315                }
316                // In case of insufficient buffer size we try to recover by reallocating buffer
317                if WIN32_ERROR(error_code) != ERROR_BUFFER_OVERFLOW {
318                    unsafe { SetLastError(WIN32_ERROR(error_code)) };
319                    break;
320                }
321            }
322        } else {
323            // GetAdaptersAddresses has failed with status different from ERROR_BUFFER_OVERFLOW when obtaining required buffer size
324            if WIN32_ERROR(error_code) != NO_ERROR {
325                unsafe { SetLastError(WIN32_ERROR(error_code)) };
326            }
327        }
328
329        // Free interface table
330        unsafe { FreeMibTable(mib_table as *const core::ffi::c_void) };
331
332        None
333    }
334
335    /// Finds network interface by provided GUID.
336    ///
337    /// # Arguments
338    ///
339    /// * `guid` - A `&str` containing the GUID to lookup.
340    ///
341    /// # Returns
342    ///
343    /// * `Option<IphlpNetworkAdapterInfo>` - An optional `IphlpNetworkAdapterInfo` object.
344    ///
345    /// # Safety
346    ///
347    /// This function uses unsafe Windows API calls to get the network interface information.
348    /// The caller should ensure that the returned `IphlpNetworkAdapterInfo` objects are not used
349    /// after the corresponding network interfaces have been removed or disabled.
350    pub fn get_connection_by_guid(guid: &str) -> Option<IphlpNetworkAdapterInfo> {
351        let mut dw_size = 0;
352        let mut mib_table: *mut MIB_IF_TABLE2 = std::ptr::null_mut();
353
354        // Query detailed information on available network interfaces
355        if unsafe { GetIfTable2(&mut mib_table) }.is_err() {
356            return None;
357        }
358
359        // Get available unicast addresses
360        let error_code = unsafe {
361            GetAdaptersAddresses(
362                AF_UNSPEC.0 as u32,
363                GAA_FLAG_SKIP_ANYCAST
364                    | GAA_FLAG_SKIP_MULTICAST
365                    | GAA_FLAG_INCLUDE_GATEWAYS
366                    | GAA_FLAG_INCLUDE_ALL_INTERFACES,
367                None,
368                None,
369                &mut dw_size,
370            )
371        };
372
373        if WIN32_ERROR(error_code) == ERROR_BUFFER_OVERFLOW && dw_size != 0 {
374            loop {
375                let mut ip_address_info = vec![0u8; dw_size as usize];
376
377                let error_code = unsafe {
378                    GetAdaptersAddresses(
379                        AF_UNSPEC.0 as u32,
380                        GAA_FLAG_SKIP_ANYCAST
381                            | GAA_FLAG_SKIP_MULTICAST
382                            | GAA_FLAG_INCLUDE_GATEWAYS
383                            | GAA_FLAG_INCLUDE_ALL_INTERFACES,
384                        None,
385                        Some(ip_address_info.as_mut_ptr() as *mut IP_ADAPTER_ADDRESSES_LH),
386                        &mut dw_size,
387                    )
388                };
389
390                if WIN32_ERROR(error_code) == NO_ERROR {
391                    let mut current_address =
392                        ip_address_info.as_ptr() as *mut IP_ADAPTER_ADDRESSES_LH;
393
394                    while !current_address.is_null() {
395                        let current = unsafe { &*current_address };
396
397                        let adapter_name = unsafe { current.AdapterName.to_string() }
398                            .unwrap_or_default()
399                            .to_uppercase();
400
401                        if !adapter_name.contains(guid) {
402                            current_address = current.Next;
403                            continue;
404                        }
405
406                        // Lookup advanced information on the network interface
407                        for i in 0..unsafe { (*mib_table).NumEntries } {
408                            let table_ptr = unsafe { (*mib_table).Table.as_ptr() };
409                            let entry_ptr = unsafe { table_ptr.add(i as usize) };
410                            if IfLuid::from(unsafe { (*mib_table).Table[i as usize].InterfaceLuid })
411                                == IfLuid::from(current.Luid)
412                            {
413                                let result =
414                                    unsafe { IphlpNetworkAdapterInfo::new(current, entry_ptr) };
415                                unsafe { FreeMibTable(mib_table as *const core::ffi::c_void) };
416                                return Some(result);
417                            }
418                        }
419
420                        current_address = current.Next;
421                    }
422
423                    break;
424                }
425                // In case of insufficient buffer size we try to recover by reallocating buffer
426                if WIN32_ERROR(error_code) != ERROR_BUFFER_OVERFLOW {
427                    unsafe { SetLastError(WIN32_ERROR(error_code)) };
428                    break;
429                }
430            }
431        } else {
432            // GetAdaptersAddresses has failed with status different from ERROR_BUFFER_OVERFLOW when obtaining required buffer size
433            if WIN32_ERROR(error_code) != NO_ERROR {
434                unsafe { SetLastError(WIN32_ERROR(error_code)) };
435            }
436        }
437
438        // Free interface table
439        unsafe { FreeMibTable(mib_table as *const core::ffi::c_void) };
440
441        None
442    }
443}