From 423b4dce82e1b54ab9c22572f76610ea2604f5aa Mon Sep 17 00:00:00 2001 From: Asmir Avdicevic Date: Mon, 24 Aug 2026 20:39:16 +0200 Subject: [PATCH] fix(windows): make GetIpNetEntry2 optional --- Cargo.toml | 1 + src/os/windows/interface.rs | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c8aa77f..afc26e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ features = [ "Win32_NetworkManagement_IpHelper", "Win32_Networking_WinSock", "Win32_NetworkManagement_Ndis", + "Win32_System_LibraryLoader", ] [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] diff --git a/src/os/windows/interface.rs b/src/os/windows/interface.rs index eecf215..4041c5a 100644 --- a/src/os/windows/interface.rs +++ b/src/os/windows/interface.rs @@ -30,9 +30,15 @@ use std::mem::MaybeUninit; #[cfg(feature = "gateway")] use std::net::Ipv4Addr; #[cfg(feature = "gateway")] -use windows_sys::Win32::NetworkManagement::IpHelper::{GetIpNetEntry2, MIB_IPNET_ROW2, SendARP}; +use std::sync::OnceLock; +#[cfg(feature = "gateway")] +use windows_sys::Win32::Foundation::WIN32_ERROR; +#[cfg(feature = "gateway")] +use windows_sys::Win32::NetworkManagement::IpHelper::{MIB_IPNET_ROW2, SendARP}; #[cfg(feature = "gateway")] use windows_sys::Win32::NetworkManagement::Ndis::NET_LUID_LH; +#[cfg(feature = "gateway")] +use windows_sys::Win32::System::LibraryLoader::{GetModuleHandleA, GetProcAddress}; fn sanitize_u64(val: u64) -> Option { if val == u64::MAX { None } else { Some(val) } @@ -120,14 +126,36 @@ fn physical_address_to_mac(address: &[u8], length: u32) -> Option { Some(MacAddr::from_octets(address[..6].try_into().unwrap())) } +#[cfg(feature = "gateway")] +type GetIpNetEntry2 = unsafe extern "system" fn(*mut MIB_IPNET_ROW2) -> WIN32_ERROR; + +/// Resolve `GetIpNetEntry2`, which some `iphlpapi.dll` implementations do not export. +#[cfg(feature = "gateway")] +fn get_ip_net_entry2() -> Option { + static ENTRY: OnceLock> = OnceLock::new(); + *ENTRY.get_or_init(|| { + // `iphlpapi.dll` is already loaded: this module statically imports `GetAdaptersAddresses`. + let module = unsafe { GetModuleHandleA(c"iphlpapi.dll".as_ptr().cast()) }; + if module.is_null() { + return None; + } + let proc = unsafe { GetProcAddress(module, c"GetIpNetEntry2".as_ptr().cast()) }?; + // SAFETY: the export matches the `GetIpNetEntry2` signature. + Some(unsafe { + std::mem::transmute:: isize, GetIpNetEntry2>(proc) + }) + }) +} + #[cfg(feature = "gateway")] fn get_neighbor_mac(address: SOCKADDR_INET, interface_luid: NET_LUID_LH) -> Option { + let get_ip_net_entry2 = get_ip_net_entry2()?; let mut row = MIB_IPNET_ROW2 { Address: address, InterfaceLuid: interface_luid, ..Default::default() }; - let result = unsafe { GetIpNetEntry2(&mut row) }; + let result = unsafe { get_ip_net_entry2(&mut row) }; if result != NO_ERROR { return None; }