From 1351c23c1abd9c662f5eda46a5132b79faa81398 Mon Sep 17 00:00:00 2001 From: Kees Verruijt Date: Sat, 1 Feb 2025 14:36:12 +0100 Subject: [PATCH] Split network layer --- Cargo.lock | 138 +++++++++++++++++++++++++++++++++++-- Cargo.toml | 3 + src/network/mod.rs | 3 + src/network/windows/mod.rs | 59 ++++++++++++++++ src/util.rs | 56 +++++++++++++-- 5 files changed, 249 insertions(+), 10 deletions(-) create mode 100644 src/network/windows/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 67c0e62..3a4e1f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1084,7 +1084,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.52.0", ] [[package]] @@ -1413,6 +1413,7 @@ dependencies = [ "tokio-graceful-shutdown", "tokio-shutdown", "tokio-util", + "windows", ] [[package]] @@ -3036,6 +3037,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f919aee0a93304be7f62e8e5027811bbba96bcb1de84d6618be56e43f8a32a1" +dependencies = [ + "windows-core 0.59.0", + "windows-targets 0.53.0", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -3045,14 +3056,49 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "810ce18ed2112484b0d4e15d022e5f598113e220c53e373fb31e67e21670c1ce" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result 0.3.0", + "windows-strings 0.3.0", + "windows-targets 0.53.0", +] + +[[package]] +name = "windows-implement" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26fd936d991781ea39e87c3a27285081e3c0da5ca0fcbc02d368cc6f52ff01" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-registry" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" dependencies = [ - "windows-result", - "windows-strings", + "windows-result 0.2.0", + "windows-strings 0.1.0", "windows-targets 0.52.6", ] @@ -3065,16 +3111,34 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-result" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d08106ce80268c4067c0571ca55a9b4e9516518eaa1a1fe9b37ca403ae1d1a34" +dependencies = [ + "windows-targets 0.53.0", +] + [[package]] name = "windows-strings" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-result", + "windows-result 0.2.0", "windows-targets 0.52.6", ] +[[package]] +name = "windows-strings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b888f919960b42ea4e11c2f408fadb55f78a9f236d5eef084103c8ce52893491" +dependencies = [ + "windows-targets 0.53.0", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -3126,13 +3190,29 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -3145,6 +3225,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -3157,6 +3243,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -3169,12 +3261,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -3187,6 +3291,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -3199,6 +3309,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -3211,6 +3327,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -3223,6 +3345,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "write16" version = "1.0.0" diff --git a/Cargo.toml b/Cargo.toml index f17fddd..01d1dcc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,6 +59,9 @@ system-configuration = "0.6.1" netlink-sys = { version = "0.8.4", features = ["tokio", "tokio_socket"] } rtnetlink = "0.14.1" +[target.'cfg(target_os = "windows")'.dependencies] +windows = { version = "0.59.0", features = ["Win32_NetworkManagement_IpHelper", "Win32_NetworkManagement_WiFi", "Win32_Networking_WinSock", "Win32_System", "Win32_System_Threading", "Win32_Security", "Win32_System_Diagnostics", "Win32_System_Diagnostics_Debug", "Win32_System_IO"] } + [build-dependencies] protobuf-codegen = "3.5.1" openssl = { version = "0.10", features = ["vendored"] } diff --git a/src/network/mod.rs b/src/network/mod.rs index 64100e4..7640a1c 100644 --- a/src/network/mod.rs +++ b/src/network/mod.rs @@ -2,3 +2,6 @@ pub(crate) mod linux; #[cfg(target_os = "macos")] pub(crate) mod macos; + +#[cfg(target_os = "windows")] +pub(crate) mod windows; diff --git a/src/network/windows/mod.rs b/src/network/windows/mod.rs new file mode 100644 index 0000000..47911ac --- /dev/null +++ b/src/network/windows/mod.rs @@ -0,0 +1,59 @@ +use std::ptr::null_mut; +use tokio::sync::watch; +use tokio_util::sync::CancellationToken; +use windows::Win32::Foundation::CloseHandle; +use windows::Win32::NetworkManagement::IpHelper::NotifyAddrChange; +use windows::Win32::System::Threading::{CreateEventW, WaitForSingleObject, INFINITE}; +use windows::Win32::System::IO::OVERLAPPED; + +use crate::radar::RadarError; + +pub async fn wait_for_ip_addr_change(cancel_token: CancellationToken) -> Result<(), RadarError> { + let (tx, mut rx) = watch::channel(false); + + let handle = unsafe { + // Create a manual-reset event + let event = CreateEventW(None, true, false, None) + .map_err(|_| RadarError::Io(std::io::Error::last_os_error()))?; + if event.is_invalid() { + return Err(RadarError::Io(std::io::Error::last_os_error())); + } + + // Prepare an OVERLAPPED structure with the event handle + let mut overlapped = OVERLAPPED { + hEvent: event, + ..Default::default() + }; + + // Register for address change notifications + let notify_result = NotifyAddrChange(null_mut(), &mut overlapped); + if notify_result != 0 { + CloseHandle(event); + return Err(RadarError::Io(std::io::Error::last_os_error())); + } + + // Spawn a blocking task to wait for the event + let handle = tokio::task::spawn_blocking(move || { + let result = WaitForSingleObject(event, INFINITE); + let _ = tx.send(result.0 == 0); + CloseHandle(event); + }); + handle + }; + + // Wait asynchronously for a change or cancellation + tokio::select! { + _ = cancel_token.cancelled() => { + // Cancel the operation + handle.abort(); + return Err(RadarError::Shutdown); + } + change_result = rx.changed() => { + if change_result.is_ok() && *rx.borrow() { + return Ok(()); + } else { + return Err(RadarError::EnumerationFailed); + } + } + } +} diff --git a/src/util.rs b/src/util.rs index 4f5e945..62cf7f3 100644 --- a/src/util.rs +++ b/src/util.rs @@ -268,17 +268,15 @@ pub fn match_ipv4(addr: &Ipv4Addr, bcast: &Ipv4Addr, netmask: &Ipv4Addr) -> bool r == b } -#[cfg(target_os = "windows")] -pub async fn wait_for_ip_addr_change() -> io::Result<()> { - tokio::time::sleep(std::time::Duration::from_secs(30)).await; -} - #[cfg(target_os = "macos")] pub(crate) use crate::network::macos::wait_for_ip_addr_change; #[cfg(target_os = "linux")] pub(crate) use crate::network::linux::wait_for_ip_addr_change; +#[cfg(target_os = "windows")] +pub(crate) use crate::network::windows::wait_for_ip_addr_change; + #[cfg(target_os = "macos")] pub fn is_wireless_interface(interface_name: &str) -> bool { use system_configuration::dynamic_store::*; @@ -330,5 +328,53 @@ pub fn is_wireless_interface(interface_name: &str) -> bool { #[cfg(target_os = "windows")] pub fn is_wireless_interface(_interface_name: &str) -> bool { + use std::ptr::null_mut; + use windows::Win32::NetworkManagement::IpHelper::IP_ADAPTER_ADDRESSES_LH; + use windows::Win32::NetworkManagement::IpHelper::{ + GetAdaptersAddresses, GAA_FLAG_INCLUDE_ALL_INTERFACES, + }; + use windows::Win32::NetworkManagement::Wifi::{ + WlanCloseHandle, WlanEnumInterfaces, WlanFreeMemory, WlanOpenHandle, + WLAN_INTERFACE_INFO_LIST, + }; + use windows::Win32::System::Diagnostics::Debug::ERROR_BUFFER_OVERFLOW; + + unsafe { + // Open WLAN handle + let mut client_handle = null_mut(); + let mut negotiated_version = 0; + let wlan_result = + WlanOpenHandle(2, null_mut(), &mut negotiated_version, &mut client_handle); + + if wlan_result != 0 { + panic!("WlanOpenHandle failed with error: {}", wlan_result); + } + + let mut interface_list: *mut WLAN_INTERFACE_INFO_LIST = null_mut(); + let wlan_enum_result = WlanEnumInterfaces(client_handle, null_mut(), &mut interface_list); + + if wlan_enum_result != 0 { + WlanCloseHandle(client_handle, null_mut()); + panic!("WlanEnumInterfaces failed with error: {}", wlan_enum_result); + } + + let interfaces = &*interface_list; + + // Check each WLAN interface + for i in 0..interfaces.dwNumberOfItems { + let wlan_interface = &interfaces.InterfaceInfo[i as usize]; + let wlan_interface_name = + String::from_utf16_lossy(&wlan_interface.strInterfaceDescription); + if wlan_interface_name.trim() == interface_name.trim() { + WlanFreeMemory(interface_list as _); + WlanCloseHandle(client_handle, null_mut()); + return true; + } + } + + WlanFreeMemory(interface_list as _); + WlanCloseHandle(client_handle, null_mut()); + } + false }