diff options
59 files changed, 1992 insertions, 5360 deletions
diff --git a/Cargo.lock b/Cargo.lock index 77a447ec8b..a437183a59 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3161,8 +3161,9 @@ dependencies = [ "tunnel-obfuscation", "uuid", "which", - "widestring 0.5.1", + "widestring 1.0.2", "winapi", + "windows", "windows-service", "windows-sys 0.42.0", "winreg", @@ -3973,6 +3974,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] +name = "windows" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0286ba339aa753e70765d521bb0242cc48e1194562bfa2a2ad7ac8a6de28f5d5" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.0", + "windows_i686_gnu 0.42.0", + "windows_i686_msvc 0.42.0", + "windows_x86_64_gnu 0.42.0", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.0", +] + +[[package]] name = "windows-service" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/build-windows-modules.sh b/build-windows-modules.sh index 41433da069..849b80b2d6 100755 --- a/build-windows-modules.sh +++ b/build-windows-modules.sh @@ -92,7 +92,6 @@ function main { clean_libraries build_solution "./windows/winfw" "winfw.sln" - build_solution "./windows/winnet" "winnet.sln" build_solution "./windows/driverlogic" "driverlogic.sln" @@ -284,7 +284,6 @@ if [[ "$(uname -s)" == "MINGW"* ]]; then if [[ "$SIGN" == "true" ]]; then CPP_BINARIES=( "windows/winfw/bin/x64-$CPP_BUILD_MODE/winfw.dll" - "windows/winnet/bin/x64-$CPP_BUILD_MODE/winnet.dll" "windows/driverlogic/bin/x64-$CPP_BUILD_MODE/driverlogic.exe" # The nsis plugin is always built in 32 bit release mode windows/nsis-plugins/bin/Win32-Release/*.dll diff --git a/gui/tasks/distribution.js b/gui/tasks/distribution.js index deb4342116..5ced2ed563 100644 --- a/gui/tasks/distribution.js +++ b/gui/tasks/distribution.js @@ -141,12 +141,6 @@ const config = { from: root(path.join('windows', 'winfw', 'bin', 'x64-${env.CPP_BUILD_MODE}', 'winfw.dll')), to: '.', }, - { - from: root( - path.join('windows', 'winnet', 'bin', 'x64-${env.CPP_BUILD_MODE}', 'winnet.dll'), - ), - to: '.', - }, { from: distAssets('binaries/x86_64-pc-windows-msvc/openvpn.exe'), to: '.' }, { from: root('build/lib/x86_64-pc-windows-msvc/libwg.dll'), to: '.' }, { from: distAssets('binaries/x86_64-pc-windows-msvc/wintun/wintun.dll'), to: '.' }, diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml index d1167e16fc..fbd231a6a8 100644 --- a/talpid-core/Cargo.toml +++ b/talpid-core/Cargo.toml @@ -79,13 +79,28 @@ subslice = "0.2" [target.'cfg(windows)'.dependencies] -widestring = "0.5" +widestring = "1.0" winreg = { version = "0.7", features = ["transactions"] } winapi = { version = "0.3.6", features = ["ws2def"] } talpid-platform-metadata = { path = "../talpid-platform-metadata" } memoffset = "0.6" windows-service = "0.5.0" +[target.'cfg(windows)'.dependencies.windows] +version = "0.42.0" +features = [ + "Data_Xml_Dom", + "Win32_Foundation", + "Win32_Security", + "Win32_System_Threading", + "Win32_UI_WindowsAndMessaging", + "Win32_NetworkManagement", + "Win32_NetworkManagement_IpHelper", + "Win32_NetworkManagement_Ndis", + "Win32_Foundation", + "Win32_Networking_WinSock", +] + [target.'cfg(windows)'.dependencies.windows-sys] version = "0.42.0" features = [ diff --git a/talpid-core/build.rs b/talpid-core/build.rs index b46304d5a0..70a1fb6d53 100644 --- a/talpid-core/build.rs +++ b/talpid-core/build.rs @@ -6,7 +6,6 @@ mod win { use std::{env, path::PathBuf}; pub static WINFW_BUILD_DIR: &'static str = "..\\windows\\winfw\\bin"; - pub static WINNET_BUILD_DIR: &'static str = "..\\windows\\winnet\\bin"; pub fn default_windows_build_artifact_dir(build_dir: &str) -> PathBuf { manifest_dir().join(build_dir).join(&target_platform_dir()) @@ -49,9 +48,7 @@ fn main() { use crate::win::*; const WINFW_DIR_VAR: &str = "WINFW_LIB_DIR"; - const WINNET_DIR_VAR: &str = "WINNET_LIB_DIR"; declare_library(WINFW_DIR_VAR, WINFW_BUILD_DIR, "winfw"); - declare_library(WINNET_DIR_VAR, WINNET_BUILD_DIR, "winnet"); let lib_dir = manifest_dir().join("../build/lib/x86_64-pc-windows-msvc"); println!("cargo:rustc-link-search={}", &lib_dir.display()); println!("cargo:rustc-link-lib=dylib=libwg"); diff --git a/talpid-core/src/lib.rs b/talpid-core/src/lib.rs index 73c3293fb4..46bb4c1169 100644 --- a/talpid-core/src/lib.rs +++ b/talpid-core/src/lib.rs @@ -9,10 +9,6 @@ #[macro_use] mod ffi; -/// Misc networking functions for Windows. -#[cfg(windows)] -mod winnet; - /// Windows API wrappers and utilities #[cfg(target_os = "windows")] pub mod windows; diff --git a/talpid-core/src/offline/mod.rs b/talpid-core/src/offline/mod.rs index b07fb3d8c9..3c5448762b 100644 --- a/talpid-core/src/offline/mod.rs +++ b/talpid-core/src/offline/mod.rs @@ -1,4 +1,4 @@ -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "windows"))] use crate::routing::RouteManagerHandle; #[cfg(target_os = "windows")] use crate::windows::window::PowerManagementListener; @@ -46,6 +46,7 @@ pub async fn spawn_monitor( sender: UnboundedSender<bool>, #[cfg(target_os = "linux")] route_manager: RouteManagerHandle, #[cfg(target_os = "android")] android_context: AndroidContext, + #[cfg(target_os = "windows")] route_manager: RouteManagerHandle, #[cfg(target_os = "windows")] power_mgmt_rx: PowerManagementListener, ) -> Result<MonitorHandle, Error> { let monitor = if !*FORCE_DISABLE_OFFLINE_MONITOR { @@ -57,6 +58,8 @@ pub async fn spawn_monitor( #[cfg(target_os = "android")] android_context, #[cfg(target_os = "windows")] + route_manager, + #[cfg(target_os = "windows")] power_mgmt_rx, ) .await?, diff --git a/talpid-core/src/offline/windows.rs b/talpid-core/src/offline/windows.rs index bbe9d951a9..9dc341d03a 100644 --- a/talpid-core/src/offline/windows.rs +++ b/talpid-core/src/offline/windows.rs @@ -1,11 +1,13 @@ use crate::{ - windows::window::{PowerManagementEvent, PowerManagementListener}, - winnet, + routing::{get_best_default_route, CallbackHandle, EventType, RouteManagerHandle}, + windows::{ + window::{PowerManagementEvent, PowerManagementListener}, + AddressFamily, + }, }; use futures::channel::mpsc::UnboundedSender; use parking_lot::Mutex; use std::{ - ffi::c_void, io, sync::{Arc, Weak}, time::Duration, @@ -17,20 +19,21 @@ pub enum Error { #[error(display = "Unable to create listener thread")] ThreadCreationError(#[error(source)] io::Error), #[error(display = "Failed to start connectivity monitor")] - ConnectivityMonitorError(#[error(source)] winnet::DefaultRouteCallbackError), + ConnectivityMonitorError(#[error(source)] crate::routing::Error), } pub struct BroadcastListener { system_state: Arc<Mutex<SystemState>>, - _callback_handle: winnet::WinNetCallbackHandle, + _callback_handle: CallbackHandle, _notify_tx: Arc<UnboundedSender<bool>>, } unsafe impl Send for BroadcastListener {} impl BroadcastListener { - pub fn start( + pub async fn start( notify_tx: UnboundedSender<bool>, + route_manager_handle: RouteManagerHandle, mut power_mgmt_rx: PowerManagementListener, ) -> Result<Self, Error> { let notify_tx = Arc::new(notify_tx); @@ -66,7 +69,8 @@ impl BroadcastListener { }); let callback_handle = - unsafe { Self::setup_network_connectivity_listener(system_state.clone())? }; + Self::setup_network_connectivity_listener(system_state.clone(), route_manager_handle) + .await?; Ok(BroadcastListener { system_state, @@ -76,7 +80,7 @@ impl BroadcastListener { } fn check_initial_connectivity() -> (bool, bool) { - let v4_connectivity = winnet::get_best_default_route(winnet::WinNetAddrFamily::IPV4) + let v4_connectivity = get_best_default_route(AddressFamily::Ipv4) .map(|route| route.is_some()) .unwrap_or_else(|error| { log::error!( @@ -85,7 +89,7 @@ impl BroadcastListener { ); true }); - let v6_connectivity = winnet::get_best_default_route(winnet::WinNetAddrFamily::IPV6) + let v6_connectivity = get_best_default_route(AddressFamily::Ipv6) .map(|route| route.is_some()) .unwrap_or_else(|error| { log::error!( @@ -103,34 +107,35 @@ impl BroadcastListener { /// The caller must make sure the `system_state` reference is valid /// until after `WinNet_DeactivateConnectivityMonitor` has been called. - unsafe fn setup_network_connectivity_listener( + async fn setup_network_connectivity_listener( system_state: Arc<Mutex<SystemState>>, - ) -> Result<winnet::WinNetCallbackHandle, Error> { - let change_handle = winnet::add_default_route_change_callback( - Some(Self::connectivity_callback), - system_state, - )?; + route_manager_handle: RouteManagerHandle, + ) -> Result<CallbackHandle, Error> { + let change_handle = route_manager_handle + .add_default_route_change_callback(Box::new(move |event, addr_family| { + Self::connectivity_callback(event, addr_family, &system_state) + })) + .await + .map_err(|e| Error::ConnectivityMonitorError(e))?; Ok(change_handle) } - unsafe extern "system" fn connectivity_callback( - event_type: winnet::WinNetDefaultRouteChangeEventType, - family: winnet::WinNetAddrFamily, - _default_route: winnet::WinNetDefaultRoute, - ctx: *mut c_void, + fn connectivity_callback<'a>( + event_type: EventType<'a>, + family: AddressFamily, + state_lock: &Arc<Mutex<SystemState>>, ) { - use winnet::WinNetDefaultRouteChangeEventType::*; + use crate::routing::EventType::*; - if event_type == DefaultRouteUpdatedDetails { + if matches!(event_type, UpdatedDetails(_)) { // ignore changes that don't affect the route return; } - let state_lock: &mut Arc<Mutex<SystemState>> = &mut *(ctx as *mut _); - let connectivity = event_type != DefaultRouteRemoved; + let connectivity = event_type != Removed; let change = match family { - winnet::WinNetAddrFamily::IPV4 => StateChange::NetworkV4Connectivity(connectivity), - winnet::WinNetAddrFamily::IPV6 => StateChange::NetworkV6Connectivity(connectivity), + AddressFamily::Ipv4 => StateChange::NetworkV4Connectivity(connectivity), + AddressFamily::Ipv6 => StateChange::NetworkV6Connectivity(connectivity), }; let mut state = state_lock.lock(); state.apply_change(change); @@ -202,9 +207,10 @@ pub type MonitorHandle = BroadcastListener; pub async fn spawn_monitor( sender: UnboundedSender<bool>, + route_manager_handle: RouteManagerHandle, power_mgmt_rx: PowerManagementListener, ) -> Result<MonitorHandle, Error> { - BroadcastListener::start(sender, power_mgmt_rx) + BroadcastListener::start(sender, route_manager_handle, power_mgmt_rx).await } fn apply_system_state_change(state: Arc<Mutex<SystemState>>, change: StateChange) { diff --git a/talpid-core/src/routing/mod.rs b/talpid-core/src/routing/mod.rs index 1eb02a206b..5d1247618e 100644 --- a/talpid-core/src/routing/mod.rs +++ b/talpid-core/src/routing/mod.rs @@ -5,8 +5,10 @@ use ipnetwork::IpNetwork; use std::{fmt, net::IpAddr}; #[cfg(target_os = "windows")] -#[path = "windows.rs"] +#[path = "windows/mod.rs"] mod imp; +#[cfg(target_os = "windows")] +pub use imp::{get_best_default_route, CallbackHandle, EventType, InterfaceAndGateway}; #[cfg(not(target_os = "windows"))] #[path = "unix.rs"] diff --git a/talpid-core/src/routing/windows.rs b/talpid-core/src/routing/windows.rs deleted file mode 100644 index fad8540ecd..0000000000 --- a/talpid-core/src/routing/windows.rs +++ /dev/null @@ -1,221 +0,0 @@ -use super::NetNode; -use crate::{routing::RequiredRoute, winnet}; -use futures::{ - channel::{ - mpsc::{self, UnboundedReceiver, UnboundedSender}, - oneshot, - }, - StreamExt, -}; -use std::{collections::HashSet, net::IpAddr}; -use windows_sys::Win32::NetworkManagement::Ndis::NET_LUID_LH; -use winnet::WinNetAddrFamily; - -/// Windows routing errors. -#[derive(err_derive::Error, Debug)] -pub enum Error { - /// The sender was dropped unexpectedly -- possible panic - #[error(display = "The channel sender was dropped")] - ManagerChannelDown, - /// Failure to initialize route manager - #[error(display = "Failed to start route manager")] - FailedToStartManager, - /// Failure to add routes - #[error(display = "Failed to add routes")] - AddRoutesFailed(#[error(source)] winnet::Error), - /// Failure to clear routes - #[error(display = "Failed to clear applied routes")] - ClearRoutesFailed, - /// WinNet returned an error while adding default route callback - #[error(display = "Failed to set callback for default route")] - FailedToAddDefaultRouteCallback, - /// Attempt to use route manager that has been dropped - #[error(display = "Cannot send message to route manager since it is down")] - RouteManagerDown, - /// Something went wrong when getting the mtu of the interface - #[error(display = "Could not get the mtu of the interface")] - GetMtu, -} - -pub type Result<T> = std::result::Result<T, Error>; - -/// Manages routes by calling into WinNet -pub struct RouteManager { - manage_tx: Option<UnboundedSender<RouteManagerCommand>>, -} - -/// Handle to a route manager. -#[derive(Clone)] -pub struct RouteManagerHandle { - tx: UnboundedSender<RouteManagerCommand>, -} - -impl RouteManagerHandle { - /// Applies the given routes while the route manager is running. - pub async fn add_routes(&self, routes: HashSet<RequiredRoute>) -> Result<()> { - let (response_tx, response_rx) = oneshot::channel(); - self.tx - .unbounded_send(RouteManagerCommand::AddRoutes(routes, response_tx)) - .map_err(|_| Error::RouteManagerDown)?; - response_rx.await.map_err(|_| Error::ManagerChannelDown)? - } - - /// Applies the given routes while the route manager is running. - pub async fn get_mtu_for_route(&self, ip: IpAddr) -> Result<u16> { - let (response_tx, response_rx) = oneshot::channel(); - self.tx - .unbounded_send(RouteManagerCommand::GetMtuForRoute(ip, response_tx)) - .map_err(|_| Error::RouteManagerDown)?; - response_rx.await.map_err(|_| Error::ManagerChannelDown)? - } -} - -#[derive(Debug)] -pub enum RouteManagerCommand { - AddRoutes(HashSet<RequiredRoute>, oneshot::Sender<Result<()>>), - GetMtuForRoute(IpAddr, oneshot::Sender<Result<u16>>), - Shutdown, -} - -impl RouteManager { - /// Creates a new route manager that will apply the provided routes and ensure they exist until - /// it's stopped. - pub async fn new(required_routes: HashSet<RequiredRoute>) -> Result<Self> { - if !winnet::activate_routing_manager() { - return Err(Error::FailedToStartManager); - } - let (manage_tx, manage_rx) = mpsc::unbounded(); - let manager = Self { - manage_tx: Some(manage_tx), - }; - tokio::spawn(RouteManager::listen(manage_rx)); - manager.add_routes(required_routes).await?; - - Ok(manager) - } - - /// Retrieve a sender directly to the command channel. - pub fn handle(&self) -> Result<RouteManagerHandle> { - if let Some(tx) = &self.manage_tx { - Ok(RouteManagerHandle { tx: tx.clone() }) - } else { - Err(Error::RouteManagerDown) - } - } - - async fn listen(mut manage_rx: UnboundedReceiver<RouteManagerCommand>) { - while let Some(command) = manage_rx.next().await { - match command { - RouteManagerCommand::AddRoutes(routes, tx) => { - let routes: Vec<_> = routes - .iter() - .map(|route| { - let destination = winnet::WinNetIpNetwork::from(route.prefix); - match &route.node { - NetNode::DefaultNode => { - winnet::WinNetRoute::through_default_node(destination) - } - NetNode::RealNode(node) => winnet::WinNetRoute::new( - winnet::WinNetNode::from(node), - destination, - ), - } - }) - .collect(); - - let _ = tx.send( - winnet::routing_manager_add_routes(&routes).map_err(Error::AddRoutesFailed), - ); - } - RouteManagerCommand::GetMtuForRoute(ip, tx) => { - let addr_family = if ip.is_ipv4() { - winnet::WinNetAddrFamily::IPV4 - } else { - winnet::WinNetAddrFamily::IPV6 - }; - let res = match get_mtu_for_route(addr_family) { - Ok(Some(mtu)) => Ok(mtu), - Ok(None) => Err(Error::GetMtu), - Err(e) => Err(e), - }; - let _ = tx.send(res); - } - RouteManagerCommand::Shutdown => { - break; - } - } - } - } - - /// Stops the routing manager and invalidates the route manager - no new default route callbacks - /// can be added - pub fn stop(&mut self) { - if let Some(tx) = self.manage_tx.take() { - if tx.unbounded_send(RouteManagerCommand::Shutdown).is_err() { - log::error!("RouteManager channel already down or thread panicked"); - } - - winnet::deactivate_routing_manager(); - } - } - - /// Applies the given routes until [`RouteManager::stop`] is called. - pub async fn add_routes(&self, routes: HashSet<RequiredRoute>) -> Result<()> { - if let Some(tx) = &self.manage_tx { - let (result_tx, result_rx) = oneshot::channel(); - if tx - .unbounded_send(RouteManagerCommand::AddRoutes(routes, result_tx)) - .is_err() - { - return Err(Error::RouteManagerDown); - } - result_rx.await.map_err(|_| Error::ManagerChannelDown)? - } else { - Err(Error::RouteManagerDown) - } - } - - /// Removes all routes previously applied in [`RouteManager::new`] or - /// [`RouteManager::add_routes`]. - pub fn clear_routes(&self) -> Result<()> { - if winnet::routing_manager_delete_applied_routes() { - Ok(()) - } else { - Err(Error::ClearRoutesFailed) - } - } -} - -fn get_mtu_for_route(addr_family: WinNetAddrFamily) -> Result<Option<u16>> { - use crate::windows::AddressFamily; - match winnet::get_best_default_route(addr_family) { - Ok(Some(route)) => { - let addr_family = match addr_family { - WinNetAddrFamily::IPV4 => AddressFamily::Ipv4, - WinNetAddrFamily::IPV6 => AddressFamily::Ipv6, - }; - let luid = NET_LUID_LH { - Value: route.interface_luid, - }; - let interface_row = crate::windows::get_ip_interface_entry(addr_family, &luid) - .map_err(|e| { - log::error!("Could not get ip interface entry: {}", e); - Error::GetMtu - })?; - let mtu = interface_row.NlMtu; - let mtu = u16::try_from(mtu).map_err(|_| Error::GetMtu)?; - Ok(Some(mtu)) - } - Ok(None) => Ok(None), - Err(e) => { - log::error!("Could not get best default route: {}", e); - Err(Error::GetMtu) - } - } -} - -impl Drop for RouteManager { - fn drop(&mut self) { - self.stop(); - } -} diff --git a/talpid-core/src/routing/windows/default_route_monitor.rs b/talpid-core/src/routing/windows/default_route_monitor.rs new file mode 100644 index 0000000000..3976903f11 --- /dev/null +++ b/talpid-core/src/routing/windows/default_route_monitor.rs @@ -0,0 +1,451 @@ +use super::{ + get_best_default_route, get_best_default_route::route_has_gateway, AddressFamily, Error, + InterfaceAndGateway, Result, +}; + +use std::{ + ffi::c_void, + io, + sync::{ + mpsc::{channel, RecvTimeoutError, Sender}, + Arc, Mutex, + }, + time::{Duration, Instant}, +}; +use windows_sys::Win32::{ + Foundation::{BOOLEAN, HANDLE, NO_ERROR}, + NetworkManagement::{ + IpHelper::{ + CancelMibChangeNotify2, ConvertInterfaceLuidToIndex, NotifyIpInterfaceChange, + NotifyRouteChange2, NotifyUnicastIpAddressChange, MIB_IPFORWARD_ROW2, + MIB_IPINTERFACE_ROW, MIB_NOTIFICATION_TYPE, MIB_UNICASTIPADDRESS_ROW, + }, + Ndis::NET_LUID_LH, + }, +}; + +const WIN_FALSE: BOOLEAN = 0; + +struct DefaultRouteMonitorContext { + callback: Box<dyn for<'a> Fn(EventType<'a>) + Send + 'static>, + refresh_current_route: bool, + family: AddressFamily, + best_route: Option<InterfaceAndGateway>, +} + +impl DefaultRouteMonitorContext { + fn new( + callback: Box<dyn for<'a> Fn(EventType<'a>) + Send + 'static>, + family: AddressFamily, + ) -> Self { + Self { + callback, + best_route: None, + refresh_current_route: false, + family, + } + } + + fn update_refresh_flag(&mut self, luid: &NET_LUID_LH, index: u32) { + if let Some(best_route) = &self.best_route { + // SAFETY: luid is a union but both fields are finally represented by u64, as such any + // access is valid + if unsafe { luid.Value } == unsafe { best_route.iface.Value } { + self.refresh_current_route = true; + return; + } + // SAFETY: luid is a union but both fields are finally represented by u64, as such any + // access is valid + if unsafe { luid.Value } != 0 { + return; + } + + let mut default_interface_index = 0; + let route_luid = best_route.iface; + // SAFETY: No clear safety specifications + if NO_ERROR as i32 + == unsafe { ConvertInterfaceLuidToIndex(&route_luid, &mut default_interface_index) } + { + self.refresh_current_route = index == default_interface_index; + } else { + self.refresh_current_route = true; + } + } + } + + fn evaluate_routes(&mut self) { + let refresh_current = self.refresh_current_route; + self.refresh_current_route = false; + + let current_best_route = get_best_default_route(self.family).ok().flatten(); + + match (&self.best_route, current_best_route) { + (None, None) => (), + (None, Some(current_best_route)) => { + self.best_route = Some(current_best_route); + (self.callback)(EventType::Updated(&self.best_route.as_ref().unwrap())); + } + (Some(_), None) => { + self.best_route = None; + (self.callback)(EventType::Removed); + } + (Some(best_route), Some(current_best_route)) => { + if best_route != ¤t_best_route { + self.best_route = Some(current_best_route); + (self.callback)(EventType::Updated(&self.best_route.as_ref().unwrap())); + } else if refresh_current { + (self.callback)(EventType::UpdatedDetails( + &self.best_route.as_ref().unwrap(), + )); + } + } + } + } +} + +pub struct DefaultRouteMonitor { + // SAFETY: These handles must be dropped before the context. This will happen automatically if + // it is handled by DefaultRouteMonitors drop implementation + notify_change_handles: Option<(NotifyChangeHandle, NotifyChangeHandle, NotifyChangeHandle)>, + // SAFETY: Context must be dropped after all of the notifier handles have been dropped in order + // to guarantee none of them use its pointer. This will be dropped by DefaultRouteMonitors + // drop implementation. SAFETY: The content of this pointer is not allowed to be mutated at + // any point except for in the drop implementation + context: *const ContextAndBurstGuard, +} + +/// SAFETY: DefaultRouteMonitor is `Send` since `NotifyChangeHandle` is `Send` and +/// `ContextAndBurstGuard` is `Sync` as it holds Mutex<T> and Arc<Mutex<T>> fields. +unsafe impl Send for DefaultRouteMonitor {} + +impl Drop for DefaultRouteMonitor { + fn drop(&mut self) { + drop(self.notify_change_handles.take()); + // SAFETY: This pointer was created by Box::into_raw and is not modified since then. + // This drop function is also only called once + let context = unsafe { Box::from_raw(self.context as *mut ContextAndBurstGuard) }; + + // Stop the burst guard + context.burst_guard.lock().unwrap().stop(); + + // Drop the context now that we are guaranteed nothing might try to access the context + drop(context); + } +} + +struct NotifyChangeHandle(HANDLE); + +/// SAFETY: NotifyChangeHandle is `Send` since it holds sole ownership of a pointer provided by C +unsafe impl Send for NotifyChangeHandle {} + +impl Drop for NotifyChangeHandle { + fn drop(&mut self) { + // SAFETY: There is no clear safety specification on this function. However self.0 should + // point to a handle that has been allocated by windows and should be non-null. Even + // if it would be null that would cause a panic rather than UB. + unsafe { + if NO_ERROR as i32 != CancelMibChangeNotify2(self.0) { + // If this callback is called after we free the context that could result in UB, in + // order to avoid that we panic. + panic!("Could not cancel change notification callback") + } + } + } +} + +#[derive(PartialEq, Clone, Copy)] +/// The type of route update passed to the callback +pub enum EventType<'a> { + /// New route + Updated(&'a InterfaceAndGateway), + /// Updated details of the same old route + UpdatedDetails(&'a InterfaceAndGateway), + /// Route removed + Removed, +} + +// SAFETY: This struct must be `Sync` otherwise it is not allowed to be sent between threads. +// Having only `Mutex<T>` or `Arc<Mutex<T>>` fields guarantees that it is `Sync` +struct ContextAndBurstGuard { + context: Arc<Mutex<DefaultRouteMonitorContext>>, + burst_guard: Mutex<BurstGuard>, +} + +impl DefaultRouteMonitor { + pub fn new<F: for<'a> Fn(EventType<'a>) + Send + 'static>( + family: AddressFamily, + callback: F, + ) -> Result<Self> { + let context = Arc::new(Mutex::new(DefaultRouteMonitorContext::new( + Box::new(callback), + family, + ))); + + let moved_context = context.clone(); + let burst_guard = Mutex::new(BurstGuard::new(move || { + moved_context.lock().unwrap().evaluate_routes(); + })); + + // SAFETY: We need to send the ContextAndBurstGuard to the windows notification functions as + // a raw pointer. This imposes the requirement it is not mutated or dropped until + // after those notifications are guaranteed to not run. This happens when the + // DefaultRouteMonitor is dropped and not before then. It also imposes the requirement that + // ContextAndBurstGuard is `Sync` since we will send references to it to other + // threads. This requirement is fullfilled since all fields of `ContextAndBurstGuard` are + // wrapped in either a Arc<Mutex> or Mutex. + let context_and_burst = Box::into_raw(Box::new(ContextAndBurstGuard { + context, + burst_guard, + })) as *const _; + + let handles = match Self::register_callbacks(family, context_and_burst) { + Ok(handles) => handles, + Err(e) => { + // Clean up the memory leak in case of error + // SAFETY: We created context_and_burst from `Box::into_raw()` and it has not been + // modified since. All of the handles have been freed at this point + // so there will be no risk of UAF. + drop(unsafe { Box::from_raw(context_and_burst as *mut ContextAndBurstGuard) }); + return Err(e); + } + }; + + let monitor = Self { + context: context_and_burst, + notify_change_handles: Some(handles), + }; + + // We must set the best default route after we have registered listeners in order to avoid + // race conditions. + { + // SAFETY: `monitor.context` will be valid since monitor will handle dropping it. No + // mutation happens here since we are using a Mutex. + let context = &unsafe { &*(monitor.context) }.context; + let mut context = context.lock().unwrap(); + context.best_route = get_best_default_route(context.family)?; + } + + Ok(monitor) + } + + fn register_callbacks( + family: AddressFamily, + context_and_burst: *const ContextAndBurstGuard, + ) -> Result<(NotifyChangeHandle, NotifyChangeHandle, NotifyChangeHandle)> { + let family = family.to_af_family(); + + // We must provide a raw pointer that points to the context that will be used in the + // callbacks. We provide a Mutex for the state turned into a Weak pointer turned + // into a raw pointer in order to not have to manually deallocate the memory after + // we cancel the callbacks. This will leak the weak pointer but the context state itself + // will be correctly dropped when DefaultRouteManager is dropped. + let context_ptr = context_and_burst; + let mut handle_ptr = 0; + // SAFETY: No clear safety specifications, context_ptr must be valid for as long as handle + // has not been dropped. + let status = unsafe { + NotifyRouteChange2( + family, + Some(route_change_callback), + context_ptr as *const _, + WIN_FALSE, + &mut handle_ptr, + ) + }; + + if NO_ERROR as i32 != status { + return Err(Error::RegisterNotifyRouteCallback( + io::Error::from_raw_os_error(status), + )); + } + let notify_route_change_handle = NotifyChangeHandle(handle_ptr); + + let mut handle_ptr = 0; + // SAFETY: No clear safety specifications, context_ptr must be valid for as long as handle + // has not been dropped. + let status = unsafe { + NotifyIpInterfaceChange( + family, + Some(interface_change_callback), + context_ptr as *const _, + WIN_FALSE, + &mut handle_ptr, + ) + }; + if NO_ERROR as i32 != status { + return Err(Error::RegisterNotifyIpInterfaceCallback( + io::Error::from_raw_os_error(status), + )); + } + let notify_interface_change_handle = NotifyChangeHandle(handle_ptr); + + let mut handle_ptr = 0; + // SAFETY: No clear safety specifications, context_ptr must be valid for as long as handle + // has not been dropped. + let status = unsafe { + NotifyUnicastIpAddressChange( + family, + Some(ip_address_change_callback), + context_ptr as *const _, + WIN_FALSE, + &mut handle_ptr, + ) + }; + if NO_ERROR as i32 != status { + return Err(Error::RegisterNotifyUnicastIpAddressCallback( + io::Error::from_raw_os_error(status), + )); + } + let notify_address_change_handle = NotifyChangeHandle(handle_ptr); + + Ok(( + notify_route_change_handle, + notify_interface_change_handle, + notify_address_change_handle, + )) + } +} + +// SAFETY: `context` is a Box::into_raw() pointer which may only be used as a non-mutable reference. +// It is guaranteed by the DefaultRouteMonitor to not be dropped before this function is guaranteed +// to not be called again. +unsafe extern "system" fn route_change_callback( + context: *const c_void, + row: *const MIB_IPFORWARD_ROW2, + _notification_type: MIB_NOTIFICATION_TYPE, +) { + // SAFETY: We assume Windows provides this pointer correctly + let row = &*row; + + if row.DestinationPrefix.PrefixLength != 0 || !route_has_gateway(row) { + return; + } + + // SAFETY: context must not be dropped or modified until this callback has been cancelled. + let context_and_burst: &ContextAndBurstGuard = &*(context as *const ContextAndBurstGuard); + let mut context = context_and_burst.context.lock().unwrap(); + + context.update_refresh_flag(&row.InterfaceLuid, row.InterfaceIndex); + context_and_burst.burst_guard.lock().unwrap().trigger(); +} + +// SAFETY: `context` is a Box::into_raw() pointer which may only be used as a non-mutable reference. +// It is guaranteed by the DefaultRouteMonitor to not be dropped before this function is guaranteed +// to not be called again. +unsafe extern "system" fn interface_change_callback( + context: *const c_void, + row: *const MIB_IPINTERFACE_ROW, + _notification_type: MIB_NOTIFICATION_TYPE, +) { + // SAFETY: We assume Windows provides this pointer correctly + let row = &*row; + + // SAFETY: context must not be dropped or modified until this callback has been cancelled. + let context_and_burst: &ContextAndBurstGuard = &*(context as *const ContextAndBurstGuard); + let mut context = context_and_burst.context.lock().unwrap(); + + context.update_refresh_flag(&row.InterfaceLuid, row.InterfaceIndex); + context_and_burst.burst_guard.lock().unwrap().trigger(); +} + +// SAFETY: `context` is a Box::into_raw() pointer which may only be used as a non-mutable reference. +// It is guaranteed by the DefaultRouteMonitor to not be dropped before this function is guaranteed +// to not be called again. +unsafe extern "system" fn ip_address_change_callback( + context: *const c_void, + row: *const MIB_UNICASTIPADDRESS_ROW, + _notification_type: MIB_NOTIFICATION_TYPE, +) { + // SAFETY: We assume Windows provides this pointer correctly + let row = &*row; + + // SAFETY: context must not be dropped or modified until this callback has been cancelled. + let context_and_burst: &ContextAndBurstGuard = &*(context as *const ContextAndBurstGuard); + let mut context = context_and_burst.context.lock().unwrap(); + + context.update_refresh_flag(&row.InterfaceLuid, row.InterfaceIndex); + context_and_burst.burst_guard.lock().unwrap().trigger(); +} + +/// BurstGuard is a wrapper for a function that protects that function from being called too many +/// times in a short amount of time. To call the function use `burst_guard.trigger()`, at that point +/// `BurstGuard` will wait for `buffer_period` and if no more calls to `trigger` are made then it +/// will call the wrapped function. If another call to `trigger` is made during this wait then it +/// will wait another `buffer_period`, this happens over and over until either +/// `longest_buffer_period` time has elapsed or until no call to `trigger` has been made in +/// `buffer_period`. At which point the wrapped function will be called. +struct BurstGuard { + sender: Sender<BurstGuardEvent>, +} + +enum BurstGuardEvent { + Trigger, + Shutdown(Sender<()>), +} + +impl BurstGuard { + fn new<F: Fn() + Send + 'static>(callback: F) -> Self { + /// This is the period of time the `BurstGuard` will wait for a new trigger to be sent + /// before it calls the callback. + const BURST_BUFFER_PERIOD: Duration = Duration::from_millis(200); + /// This is the longest period that the `BurstGuard` will wait from the first trigger till + /// it calls the callback. + const BURST_LONGEST_BUFFER_PERIOD: Duration = Duration::from_secs(2); + + let (sender, listener) = channel(); + std::thread::spawn(move || { + // The `stop` implementation assumes that this thread will not call `callback` again + // if the listener has been dropped. + while let Ok(message) = listener.recv() { + match message { + BurstGuardEvent::Trigger => { + let start = Instant::now(); + loop { + match listener.recv_timeout(BURST_BUFFER_PERIOD) { + Ok(BurstGuardEvent::Trigger) => { + if start.elapsed() >= BURST_LONGEST_BUFFER_PERIOD { + callback(); + break; + } + } + Ok(BurstGuardEvent::Shutdown(tx)) => { + let _ = tx.send(()); + return; + } + Err(RecvTimeoutError::Timeout) => { + callback(); + break; + } + Err(RecvTimeoutError::Disconnected) => { + break; + } + } + } + } + BurstGuardEvent::Shutdown(tx) => { + let _ = tx.send(()); + return; + } + } + } + }); + Self { sender } + } + + /// When `stop` returns an then the `BurstGuard` thread is guaranteed to not make any further + /// calls to `callback`. + fn stop(&self) { + let (sender, listener) = channel(); + // If we could not send then it means the thread has already shut down and we can return + if self.sender.send(BurstGuardEvent::Shutdown(sender)).is_ok() { + // We do not care what the result is, if it is OK it means the thread shut down, if + // it is Err it also means it shut down. + let _ = listener.recv(); + } + } + + /// Non-blocking + fn trigger(&self) { + self.sender.send(BurstGuardEvent::Trigger).unwrap(); + } +} diff --git a/talpid-core/src/routing/windows/get_best_default_route.rs b/talpid-core/src/routing/windows/get_best_default_route.rs new file mode 100644 index 0000000000..4ec7395fff --- /dev/null +++ b/talpid-core/src/routing/windows/get_best_default_route.rs @@ -0,0 +1,190 @@ +use super::{Error, Result}; +use crate::windows::{get_ip_interface_entry, try_socketaddr_from_inet_sockaddr, AddressFamily}; +use std::{convert::TryInto, io, net::SocketAddr}; +use widestring::{widecstr, WideCStr}; +use windows_sys::Win32::{ + Foundation::NO_ERROR, + NetworkManagement::{ + IpHelper::{ + FreeMibTable, GetIfEntry2, GetIpForwardTable2, IF_TYPE_SOFTWARE_LOOPBACK, + IF_TYPE_TUNNEL, MIB_IF_ROW2, MIB_IPFORWARD_ROW2, + }, + Ndis::NET_LUID_LH, + }, +}; + +// Interface description substrings found for virtual adapters. +const TUNNEL_INTERFACE_DESCS: [&WideCStr; 3] = [ + widecstr!("WireGuard"), + widecstr!("Wintun"), + widecstr!("Tunnel"), +]; + +fn get_ipforward_rows(family: AddressFamily) -> Result<Vec<MIB_IPFORWARD_ROW2>> { + let family = family.to_af_family(); + let mut table_ptr = std::ptr::null_mut(); + + // SAFETY: GetIpForwardTable2 does not have clear safety specifications however what it does is + // heap allocate a IpForwardTable2 and then change table_ptr to point to that allocation. + let status = unsafe { GetIpForwardTable2(family, &mut table_ptr) }; + if NO_ERROR as i32 != status { + return Err(Error::GetIpForwardTableFailed( + io::Error::from_raw_os_error(status), + )); + } + + // SAFETY: table_ptr is valid since GetIpForwardTable2 did not return an error + let num_entries = unsafe { *table_ptr }.NumEntries; + let mut vec = Vec::with_capacity(num_entries.try_into().unwrap_or_default()); + + for i in 0..num_entries { + assert!( + usize::try_from(i).unwrap() * std::mem::size_of::<MIB_IPFORWARD_ROW2>() + < usize::try_from(isize::MAX).unwrap() + ); + + // SAFETY: table_ptr is valid since GetIpForwardTable2 did not return an error nor have we + // or will we modify the table + let ptr: *const MIB_IPFORWARD_ROW2 = unsafe { (*table_ptr).Table.as_ptr() }; + + // SAFETY: The assert guarantees that the amount of bytes we are jumping is not larger than + // isize::MAX. Win32 guarantees that the resulting pointer is aligned, non-null, + // init. + let row: &MIB_IPFORWARD_ROW2 = + unsafe { ptr.offset(i.try_into().unwrap()).as_ref() }.unwrap(); + vec.push(row.clone()); + } + // SAFETY: FreeMibTable does not have clear safety rules but it deallocates the + // MIB_IPFORWARD_TABLE2 This pointer is ONLY deallocated here so it is guaranteed to not + // have been already deallocated. We have cloned all MIB_IPFORWARD_ROW2s and the rows do not + // contain pointers to the table so they will not be dangling after this free. + unsafe { FreeMibTable(table_ptr as *const _) } + Ok(vec) +} + +/// General type for passing interface and gateway +pub struct InterfaceAndGateway { + /// Interface + pub iface: NET_LUID_LH, + /// Gateway + pub gateway: SocketAddr, +} + +impl PartialEq for InterfaceAndGateway { + fn eq(&self, other: &InterfaceAndGateway) -> bool { + // SAFETY: Accessing Value is always valid in this union as both fields are the same type + (unsafe { self.iface.Value == other.iface.Value } && self.gateway == other.gateway) + } +} + +/// Get the best default route for the given address family or None if none exists. +pub fn get_best_default_route(family: AddressFamily) -> Result<Option<InterfaceAndGateway>> { + let table = get_ipforward_rows(family)?; + + // Remove all candidates without a gateway and which are not on a physical interface. + // Then get the annotated routes which are active. + let mut annotated: Vec<AnnotatedRoute<'_>> = table + .iter() + .filter(|row| { + 0 == row.DestinationPrefix.PrefixLength + && route_has_gateway(row) + && is_route_on_physical_interface(row).unwrap_or(false) + }) + .filter_map(|row| annotate_route(row)) + .collect(); + + if annotated.is_empty() { + return Ok(None); + } + + // We previously filtered out all inactive routes so we only need to sort by acending + // effective_metric + annotated.sort_by(|lhs, rhs| lhs.effective_metric.cmp(&rhs.effective_metric)); + + Ok(Some(InterfaceAndGateway { + iface: annotated[0].route.InterfaceLuid, + gateway: try_socketaddr_from_inet_sockaddr(annotated[0].route.NextHop) + .map_err(|_| Error::InvalidSiFamily)?, + })) +} + +pub fn route_has_gateway(route: &MIB_IPFORWARD_ROW2) -> bool { + match try_socketaddr_from_inet_sockaddr(route.NextHop) { + Ok(sock) => !sock.ip().is_unspecified(), + Err(_) => false, + } +} + +// TODO(Jon): It would be more correct to filter for devices that match the known LUID of the tunnel +// interface +fn is_route_on_physical_interface(route: &MIB_IPFORWARD_ROW2) -> Result<bool> { + // The last 16 bits of _bitfield represent the interface type. For that reason we mask it with + // 0xFFFF. SAFETY: route.InterfaceLuid is a union. Both variants of this union are always + // valid since one is a u64 and the other is a wrapped u64. Access to the _bitfield as such + // is safe since it does not reinterpret the u64 as anything it is not. + let if_type = u32::try_from(unsafe { route.InterfaceLuid.Info._bitfield } & 0xFFFF).unwrap(); + if if_type == IF_TYPE_SOFTWARE_LOOPBACK || if_type == IF_TYPE_TUNNEL { + return Ok(false); + } + + // OpenVPN uses interface type IF_TYPE_PROP_VIRTUAL, + // but tethering etc. may rely on virtual adapters too, + // so we have to filter out the TAP adapter specifically. + + // SAFETY: We are allowed to initialize MIB_IF_ROW2 with zeroed because it is made up entirely + // of types for which the zero pattern (all zeros) is valid. + let mut row: MIB_IF_ROW2 = unsafe { std::mem::zeroed() }; + row.InterfaceLuid = route.InterfaceLuid; + row.InterfaceIndex = route.InterfaceIndex; + + // SAFETY: GetIfEntry2 does not have clear safety rules however it will read the + // row.InterfaceLuid or row.InterfaceIndex and use that information to populate the struct. + // We guarantee here that these fields are valid since they are set. + let status = unsafe { GetIfEntry2(&mut row) }; + if NO_ERROR as i32 != status { + return Err(Error::GetIfEntryFailed(io::Error::from_raw_os_error( + status, + ))); + } + + let row_description = WideCStr::from_slice_truncate(&row.Description) + .expect("Windows provided incorrectly formatted utf16 string"); + + for tunnel_interface_desc in TUNNEL_INTERFACE_DESCS { + if contains_subslice(row_description.as_slice(), tunnel_interface_desc.as_slice()) { + return Ok(false); + } + } + + return Ok(true); +} + +fn contains_subslice<T: PartialEq>(slice: &[T], subslice: &[T]) -> bool { + slice + .windows(subslice.len()) + .any(|window| window == subslice) +} + +struct AnnotatedRoute<'a> { + route: &'a MIB_IPFORWARD_ROW2, + effective_metric: u32, +} + +fn annotate_route<'a>(route: &'a MIB_IPFORWARD_ROW2) -> Option<AnnotatedRoute<'a>> { + // SAFETY: `si_family` is valid in both `Ipv4` and `Ipv6` so we can safely access `si_family`. + let iface = get_ip_interface_entry( + AddressFamily::try_from_af_family(unsafe { route.DestinationPrefix.Prefix.si_family }) + .ok()?, + &route.InterfaceLuid, + ) + .ok()?; + + if iface.Connected == 0 { + None + } else { + Some(AnnotatedRoute { + route, + effective_metric: route.Metric + iface.Metric, + }) + } +} diff --git a/talpid-core/src/routing/windows/mod.rs b/talpid-core/src/routing/windows/mod.rs new file mode 100644 index 0000000000..06d23368ca --- /dev/null +++ b/talpid-core/src/routing/windows/mod.rs @@ -0,0 +1,303 @@ +use crate::{routing::RequiredRoute, windows::AddressFamily}; +use futures::channel::oneshot; +use std::{collections::HashSet, io, net::IpAddr}; +use talpid_types::ErrorExt; +use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; + +pub use default_route_monitor::EventType; +pub use get_best_default_route::{get_best_default_route, route_has_gateway, InterfaceAndGateway}; +pub use route_manager::{Callback, CallbackHandle, Route, RouteManagerInternal}; + +mod default_route_monitor; +mod get_best_default_route; +mod route_manager; + +/// Windows routing errors. +#[derive(err_derive::Error, Debug)] +pub enum Error { + /// The sender was dropped unexpectedly -- possible panic + #[error(display = "The channel sender was dropped")] + ManagerChannelDown, + /// Failure to initialize route manager + #[error(display = "Failed to start route manager")] + FailedToStartManager, + /// Attempt to use route manager that has been dropped + #[error(display = "Cannot send message to route manager since it is down")] + RouteManagerDown, + /// Low level error caused by a failure to add to route table + #[error(display = "Could not add route to route table")] + AddToRouteTable(io::Error), + /// Low level error caused by failure to delete route from route table + #[error(display = "Failed to delete applied routes")] + DeleteFromRouteTable(io::Error), + /// GetIpForwardTable2 windows API call failed + #[error(display = "Failed to retrieve the routing table")] + GetIpForwardTableFailed(io::Error), + /// GetIfEntry2 windows API call failed + #[error(display = "Failed to retrieve network interface entry")] + GetIfEntryFailed(io::Error), + /// Low level error caused by failing to register the route callback + #[error(display = "Attempt to register notify route change callback failed")] + RegisterNotifyRouteCallback(io::Error), + /// Low level error caused by failing to register the ip interface callback + #[error(display = "Attempt to register notify ip interface change callback failed")] + RegisterNotifyIpInterfaceCallback(io::Error), + /// Low level error caused by failing to register the unicast ip address callback + #[error(display = "Attempt to register notify unicast ip address change callback failed")] + RegisterNotifyUnicastIpAddressCallback(io::Error), + /// Low level error caused by windows Adapters API + #[error(display = "Windows adapter error")] + Adapter(io::Error), + /// High level error caused by a failure to clear the routes in the route manager. + /// Contains the lower error + #[error(display = "Failed to clear applied routes")] + ClearRoutesFailed(Box<Error>), + /// High level error caused by a failure to add routes in the route manager. + /// Contains the lower error + #[error(display = "Failed to add routes")] + AddRoutesFailed(Box<Error>), + /// Something went wrong when getting the mtu of the interface + #[error(display = "Could not get the mtu of the interface")] + GetMtu, + /// The SI family was of an unexpected value + #[error(display = "The SI family was of an unexpected value")] + InvalidSiFamily, + /// Device name not found + #[error(display = "The device name was not found")] + DeviceNameNotFound, + /// No default route + #[error(display = "No default route found")] + NoDefaultRoute, + /// Conversion error between types + #[error(display = "Conversion error")] + Conversion, + /// Could not find device gateway + #[error(display = "Could not find device gateway")] + DeviceGatewayNotFound, + /// Could not get default route + #[error(display = "Could not get default route")] + GetDefaultRoute, + /// Could not find device by name + #[error(display = "Could not find device by name")] + GetDeviceByName, + /// Could not find device by gateway + #[error(display = "Could not find device by gateway")] + GetDeviceByGateway, +} + +pub type Result<T> = std::result::Result<T, Error>; + +/// Manages routes by calling into WinNet +pub struct RouteManager { + manage_tx: Option<UnboundedSender<RouteManagerCommand>>, +} + +/// Handle to a route manager. +#[derive(Clone)] +pub struct RouteManagerHandle { + tx: UnboundedSender<RouteManagerCommand>, +} + +impl RouteManagerHandle { + /// Add a callback which will be called if the default route changes. + pub async fn add_default_route_change_callback( + &self, + callback: Callback, + ) -> Result<CallbackHandle> { + let (response_tx, response_rx) = oneshot::channel(); + self.tx + .send(RouteManagerCommand::RegisterDefaultRouteChangeCallback( + callback, + response_tx, + )) + .map_err(|_| Error::RouteManagerDown)?; + response_rx.await.map_err(|_| Error::ManagerChannelDown)? + } + + /// Applies the given routes while the route manager is running. + pub async fn add_routes(&self, routes: HashSet<RequiredRoute>) -> Result<()> { + let (response_tx, response_rx) = oneshot::channel(); + self.tx + .send(RouteManagerCommand::AddRoutes(routes, response_tx)) + .map_err(|_| Error::RouteManagerDown)?; + response_rx.await.map_err(|_| Error::ManagerChannelDown)? + } + + /// Applies the given routes while the route manager is running. + pub async fn get_mtu_for_route(&self, ip: IpAddr) -> Result<u16> { + let (response_tx, response_rx) = oneshot::channel(); + self.tx + .send(RouteManagerCommand::GetMtuForRoute(ip, response_tx)) + .map_err(|_| Error::RouteManagerDown)?; + response_rx.await.map_err(|_| Error::ManagerChannelDown)? + } +} + +pub enum RouteManagerCommand { + AddRoutes(HashSet<RequiredRoute>, oneshot::Sender<Result<()>>), + GetMtuForRoute(IpAddr, oneshot::Sender<Result<u16>>), + ClearRoutes, + RegisterDefaultRouteChangeCallback(Callback, oneshot::Sender<Result<CallbackHandle>>), + Shutdown, +} + +impl RouteManager { + /// Creates a new route manager that will apply the provided routes and ensure they exist until + /// it's stopped. + pub async fn new(required_routes: HashSet<RequiredRoute>) -> Result<Self> { + let internal = match RouteManagerInternal::new() { + Ok(internal) => internal, + Err(_) => return Err(Error::FailedToStartManager), + }; + let (manage_tx, manage_rx) = mpsc::unbounded_channel(); + let manager = Self { + manage_tx: Some(manage_tx), + }; + tokio::spawn(RouteManager::listen(manage_rx, internal)); + manager.add_routes(required_routes).await?; + + Ok(manager) + } + + /// Add a callback which will be called if the default route changes. + pub async fn add_default_route_change_callback( + &self, + callback: Callback, + ) -> Result<CallbackHandle> { + if let Some(tx) = &self.manage_tx { + let (result_tx, result_rx) = oneshot::channel(); + if tx + .send(RouteManagerCommand::RegisterDefaultRouteChangeCallback( + callback, result_tx, + )) + .is_err() + { + return Err(Error::RouteManagerDown); + } + result_rx.await.map_err(|_| Error::ManagerChannelDown)? + } else { + Err(Error::RouteManagerDown) + } + } + + /// Retrieve a sender directly to the command channel. + pub fn handle(&self) -> Result<RouteManagerHandle> { + if let Some(tx) = &self.manage_tx { + Ok(RouteManagerHandle { tx: tx.clone() }) + } else { + Err(Error::RouteManagerDown) + } + } + + async fn listen( + mut manage_rx: UnboundedReceiver<RouteManagerCommand>, + mut internal: RouteManagerInternal, + ) { + while let Some(command) = manage_rx.recv().await { + match command { + RouteManagerCommand::AddRoutes(routes, tx) => { + let routes: Vec<_> = routes + .into_iter() + .map(|route| Route { + network: route.prefix, + node: route.node, + }) + .collect(); + + let _ = tx.send( + internal + .add_routes(routes) + .map_err(|e| Error::AddRoutesFailed(Box::new(e))), + ); + } + RouteManagerCommand::GetMtuForRoute(ip, tx) => { + let addr_family = if ip.is_ipv4() { + AddressFamily::Ipv4 + } else { + AddressFamily::Ipv6 + }; + let res = match get_mtu_for_route(addr_family) { + Ok(Some(mtu)) => Ok(mtu), + Ok(None) => Err(Error::GetMtu), + Err(e) => Err(e), + }; + let _ = tx.send(res); + } + RouteManagerCommand::ClearRoutes => { + if let Err(e) = internal.delete_applied_routes() { + log::error!("{}", e.display_chain_with_msg("Could not clear routes")); + } + } + RouteManagerCommand::RegisterDefaultRouteChangeCallback(callback, tx) => { + let _ = tx.send(internal.register_default_route_changed_callback(callback)); + } + RouteManagerCommand::Shutdown => { + break; + } + } + } + } + + /// Stops the routing manager and invalidates the route manager - no new default route callbacks + /// can be added + pub fn stop(&mut self) { + if let Some(tx) = self.manage_tx.take() { + if tx.send(RouteManagerCommand::Shutdown).is_err() { + log::error!("RouteManager channel already down or thread panicked"); + } + } + } + + /// Applies the given routes until [`RouteManager::stop`] is called. + pub async fn add_routes(&self, routes: HashSet<RequiredRoute>) -> Result<()> { + if let Some(tx) = &self.manage_tx { + let (result_tx, result_rx) = oneshot::channel(); + if tx + .send(RouteManagerCommand::AddRoutes(routes, result_tx)) + .is_err() + { + return Err(Error::RouteManagerDown); + } + result_rx.await.map_err(|_| Error::ManagerChannelDown)? + } else { + Err(Error::RouteManagerDown) + } + } + + /// Removes all routes previously applied in [`RouteManager::new`] or + /// [`RouteManager::add_routes`]. + pub fn clear_routes(&self) -> Result<()> { + if let Some(tx) = &self.manage_tx { + tx.send(RouteManagerCommand::ClearRoutes) + .map_err(|_| Error::RouteManagerDown) + } else { + Err(Error::RouteManagerDown) + } + } +} + +fn get_mtu_for_route(addr_family: AddressFamily) -> Result<Option<u16>> { + match get_best_default_route(addr_family) { + Ok(Some(route)) => { + let interface_row = crate::windows::get_ip_interface_entry(addr_family, &route.iface) + .map_err(|e| { + log::error!("Could not get ip interface entry: {}", e); + Error::GetMtu + })?; + let mtu = interface_row.NlMtu; + let mtu = u16::try_from(mtu).map_err(|_| Error::GetMtu)?; + Ok(Some(mtu)) + } + Ok(None) => Ok(None), + Err(e) => { + log::error!("Could not get best default route: {}", e); + Err(Error::GetMtu) + } + } +} + +impl Drop for RouteManager { + fn drop(&mut self) { + self.stop(); + } +} diff --git a/talpid-core/src/routing/windows/route_manager.rs b/talpid-core/src/routing/windows/route_manager.rs new file mode 100644 index 0000000000..f1d878dd28 --- /dev/null +++ b/talpid-core/src/routing/windows/route_manager.rs @@ -0,0 +1,885 @@ +use super::{ + default_route_monitor::{DefaultRouteMonitor, EventType as RouteMonitorEventType}, + get_best_default_route, Error, InterfaceAndGateway, Result, +}; +use crate::{ + routing::NetNode, + windows::{inet_sockaddr_from_socketaddr, try_socketaddr_from_inet_sockaddr, AddressFamily}, +}; +use ipnetwork::IpNetwork; +use std::{ + collections::HashMap, + io, + net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, + sync::{Arc, Mutex}, +}; +use widestring::{WideCStr, WideCString}; +use windows_sys::Win32::{ + Foundation::{ + ERROR_BUFFER_OVERFLOW, ERROR_NOT_FOUND, ERROR_NO_DATA, ERROR_OBJECT_ALREADY_EXISTS, + ERROR_SUCCESS, NO_ERROR, + }, + NetworkManagement::{ + IpHelper::{ + ConvertInterfaceAliasToLuid, CreateIpForwardEntry2, DeleteIpForwardEntry2, + GetAdaptersAddresses, InitializeIpForwardEntry, SetIpForwardEntry2, + GAA_FLAG_INCLUDE_GATEWAYS, GAA_FLAG_SKIP_ANYCAST, GAA_FLAG_SKIP_DNS_SERVER, + GAA_FLAG_SKIP_FRIENDLY_NAME, GAA_FLAG_SKIP_MULTICAST, GET_ADAPTERS_ADDRESSES_FLAGS, + IP_ADAPTER_ADDRESSES_LH, IP_ADAPTER_GATEWAY_ADDRESS_LH, IP_ADAPTER_IPV4_ENABLED, + IP_ADAPTER_IPV6_ENABLED, IP_ADDRESS_PREFIX, MIB_IPFORWARD_ROW2, + }, + Ndis::NET_LUID_LH, + }, + Networking::WinSock::{ + NlroManual, ADDRESS_FAMILY, AF_INET, AF_INET6, MIB_IPPROTO_NETMGMT, SOCKADDR_IN, + SOCKADDR_IN6, SOCKADDR_INET, SOCKET_ADDRESS, + }, +}; + +type Network = IpNetwork; +type NodeAddress = SOCKADDR_INET; + +/// Callback handle for the default route changed callback. Produced by the RouteManager. +pub struct CallbackHandle { + nonce: i32, + callbacks: Arc<Mutex<(i32, HashMap<i32, Callback>)>>, +} + +impl Drop for CallbackHandle { + fn drop(&mut self) { + let (_, callbacks) = &mut *self.callbacks.lock().unwrap(); + match callbacks.remove(&self.nonce) { + Some(_) => (), + None => { + log::warn!("Could not un-register route manager callback due to it already being de-registered"); + } + } + } +} + +#[derive(Clone)] +struct RegisteredRoute { + network: Network, + luid: NET_LUID_LH, + next_hop: SocketAddr, +} + +impl std::fmt::Display for RegisteredRoute { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // SAFETY: luid.Value is always valid as the underlying type of both union fields is an u64 + formatter.write_fmt(format_args!("RegisteredRoute {{ luid: {} }}", unsafe { + self.luid.Value + })) + } +} + +impl PartialEq for RegisteredRoute { + fn eq(&self, other: &Self) -> bool { + // SAFETY: luid.Value is always valid as the underlying type of both union fields is an u64 + (unsafe { self.luid.Value == other.luid.Value }) + && (self.next_hop == other.next_hop) + && (self.network == other.network) + } +} + +#[derive(Clone)] +pub struct Node { + pub device_name: Option<widestring::U16CString>, + pub gateway: Option<NodeAddress>, +} + +#[derive(Clone)] +pub struct Route { + pub network: Network, + pub node: NetNode, +} + +#[derive(Clone)] +struct RouteRecord { + route: Route, + registered_route: RegisteredRoute, +} + +struct EventEntry { + record: RouteRecord, + event_type: RecordEventType, +} + +enum RecordEventType { + AddRoute, + DeleteRoute, +} + +pub type Callback = Box<dyn for<'a> Fn(RouteMonitorEventType<'a>, AddressFamily) + Send>; + +pub struct RouteManagerInternal { + route_monitor_v4: Option<DefaultRouteMonitor>, + route_monitor_v6: Option<DefaultRouteMonitor>, + routes: Arc<Mutex<Vec<RouteRecord>>>, + /// Lock for a nonce and a HashMap of callbacks and their id which is used as a handle to + /// unregister them. The nonce is used to create new ids and then incrementing. + callbacks: Arc<Mutex<(i32, HashMap<i32, Callback>)>>, +} + +impl RouteManagerInternal { + pub fn new() -> Result<Self> { + let routes = Arc::new(Mutex::new(Vec::new())); + let callbacks = Arc::new(Mutex::new((0, HashMap::new()))); + + let callbacks_ipv4 = callbacks.clone(); + let routes_ipv4 = routes.clone(); + let callbacks_ipv6 = callbacks.clone(); + let routes_ipv6 = routes.clone(); + + Ok(Self { + route_monitor_v4: Some(DefaultRouteMonitor::new( + AddressFamily::Ipv4, + move |event_type| { + Self::default_route_change(&callbacks_ipv4, &routes_ipv4, AF_INET, event_type); + }, + )?), + route_monitor_v6: Some(DefaultRouteMonitor::new( + AddressFamily::Ipv6, + move |event_type| { + Self::default_route_change(&callbacks_ipv6, &routes_ipv6, AF_INET6, event_type); + }, + )?), + routes, + callbacks, + }) + } + + pub fn add_routes(&self, new_routes: Vec<Route>) -> Result<()> { + let mut route_manager_routes = self.routes.lock().unwrap(); + + let mut event_log = vec![]; + + for route in new_routes { + let registered_route = Self::add_into_routing_table(&route).map_err(|error| { + if let Err(error) = Self::undo_events(&event_log, &mut route_manager_routes) { + error + } else { + error + } + })?; + + let new_record = RouteRecord { + route, + registered_route, + }; + + event_log.push(EventEntry { + event_type: RecordEventType::AddRoute, + record: new_record.clone(), + }); + + let existing_record_idx = + Self::find_route_record(&mut route_manager_routes, &new_record.registered_route); + + match existing_record_idx { + None => route_manager_routes.push(new_record), + Some(idx) => route_manager_routes[idx] = new_record, + } + } + Ok(()) + } + + fn add_into_routing_table(route: &Route) -> Result<RegisteredRoute> { + let node = Self::resolve_node(ipnetwork_to_address_family(route.network), &route.node)?; + + // SAFETY: MIB_IPFORWARD_ROW2 contains no references or pointers only number primitives and + // as such it is safe to zero it. + let mut spec: MIB_IPFORWARD_ROW2 = unsafe { std::mem::zeroed() }; + + // SAFETY: This function must be used to initialize MIB_IPFORWARD_ROW2 structs if it is to + // be used later by CreateIpForwardEntry2. + unsafe { InitializeIpForwardEntry(&mut spec) }; + + spec.InterfaceLuid = node.iface; + spec.DestinationPrefix = win_ip_address_prefix_from_ipnetwork_port_zero(route.network); + spec.NextHop = inet_sockaddr_from_socketaddr(node.gateway); + spec.Metric = 0; + spec.Protocol = MIB_IPPROTO_NETMGMT; + spec.Origin = NlroManual; + + // SAFETY: DestinationPrefix must be initialized to a valid prefix. NextHop must have a + // valid IP address and family. At least one of InterfaceLuid and InterfaceIndex must be set + // to the interface. + let mut status = unsafe { CreateIpForwardEntry2(&spec) }; + + // The return code ERROR_OBJECT_ALREADY_EXISTS means there is already an existing route + // on the same interface, with the same DestinationPrefix and NextHop. + // + // However, all the other properties of the route may be different. And the properties may + // not have the exact same values as when the route was registered, because windows + // will adjust route properties at time of route insertion as well as later. + // + // The simplest thing in this case is to just overwrite the route. + // + + if ERROR_OBJECT_ALREADY_EXISTS as i32 == status { + // SAFETY: DestinationPrefix must be initialzed to a valid prefix. NextHop must have + // a valid IP address and family. At least one of InterfaceLuid and InterfaceIndex must + // be set to the interface. + status = unsafe { SetIpForwardEntry2(&spec) }; + } + + if NO_ERROR as i32 != status { + log::error!("Could not register route in routing table"); + return Err(Error::AddToRouteTable(io::Error::from_raw_os_error(status))); + } + + Ok(RegisteredRoute { + network: route.network, + luid: node.iface, + next_hop: node.gateway, + }) + } + + fn resolve_node(family: AddressFamily, optional_node: &NetNode) -> Result<InterfaceAndGateway> { + // There are four cases: + // + // Unspecified node (use interface and gateway of default route). + // Node is specified by name. + // Node is specified by name and gateway. + // Node is specified by gateway. + // + + match optional_node { + NetNode::DefaultNode => { + let default_route = get_best_default_route(family)?; + match default_route { + None => { + log::error!("Unable to determine details of default route"); + return Err(Error::NoDefaultRoute); + } + Some(default_route) => return Ok(default_route), + } + } + NetNode::RealNode(node) => { + if let Some(device_name) = &node.get_device() { + let device_name = WideCString::from_str(device_name) + .expect("Failed to convert UTF-8 string to null terminated UCS string"); + let luid = match Self::parse_string_encoded_luid(device_name.as_ucstr())? { + None => { + let mut luid = NET_LUID_LH { Value: 0 }; + // SAFETY: No specific safety requirement + if NO_ERROR as i32 + != unsafe { + ConvertInterfaceAliasToLuid(device_name.as_ptr(), &mut luid) + } + { + log::error!( + "Unable to derive interface LUID from interface alias: {:?}", + device_name + ); + return Err(Error::DeviceNameNotFound); + } else { + luid + } + } + Some(luid) => luid, + }; + + return Ok(InterfaceAndGateway { + iface: luid, + gateway: match node.get_address() { + Some(ip) => SocketAddr::new(ip, 0), + None => match family { + AddressFamily::Ipv4 => { + SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0) + } + AddressFamily::Ipv6 => { + SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 0) + } + }, + }, + }); + } + + // The node is specified only by gateway. + // + + // Unwrapping is fine because the node must have an address since no device name was + // found. + let gateway = node.get_address().map(inet_sockaddr_from_ipaddr).unwrap(); + Ok(InterfaceAndGateway { + iface: interface_luid_from_gateway(&gateway)?, + gateway: try_socketaddr_from_inet_sockaddr(gateway) + .map_err(|_| Error::InvalidSiFamily)?, + }) + } + } + } + + fn find_route_record(records: &mut Vec<RouteRecord>, route: &RegisteredRoute) -> Option<usize> { + records + .iter() + .position(|record| route == &record.registered_route) + } + + fn undo_events(event_log: &Vec<EventEntry>, records: &mut Vec<RouteRecord>) -> Result<()> { + // Rewind state by processing events in the reverse order. + // + + let mut result = Ok(()); + + for event in event_log.iter().rev() { + match event.event_type { + RecordEventType::AddRoute => { + let record_idx = Self::find_route_record(records, &event.record.registered_route) + .expect("Internal state inconsistency in route manager, could not find route record"); + let record = records.get(record_idx) + .expect("Internal state inconsistency in route manager, route record index pointing at nothing"); + + if let Err(e) = Self::delete_from_routing_table(&record.registered_route) { + result = result.and(Err(e)); + continue; + } + records.remove(record_idx); + } + RecordEventType::DeleteRoute => { + if let Err(e) = Self::restore_into_routing_table(&event.record.registered_route) + { + result = result.and(Err(e)); + continue; + } + records.push(event.record.clone()); + } + } + } + + result + } + + fn delete_from_routing_table(route: &RegisteredRoute) -> Result<()> { + // SAFETY: There are no pointers or references inside of MIB_IPFORWARD_ROW2, only primitive + // numbers as such it is safe to zero it. + let mut r: MIB_IPFORWARD_ROW2 = unsafe { std::mem::zeroed() }; + + r.InterfaceLuid = route.luid; + r.DestinationPrefix = win_ip_address_prefix_from_ipnetwork_port_zero(route.network); + r.NextHop = inet_sockaddr_from_socketaddr(route.next_hop); + + // SAFETY: DestinationPrefix must be initialzed to a valid prefix. NextHop must have + // a valid IP address and family. At least one of InterfaceLuid and InterfaceIndex must be + // set to the interface. + let status = unsafe { DeleteIpForwardEntry2(&r) }; + + match u32::try_from(status) { + Ok(ERROR_NOT_FOUND) => { + log::warn!("Attempting to delete route which was not present in routing table, ignoring and proceeding. Route: {}", route); + } + Ok(NO_ERROR) => (), + _ => { + log::error!( + "Failed to delete route in routing table. Route: {}, Status: {}", + route, + status + ); + return Err(Error::DeleteFromRouteTable(io::Error::from_raw_os_error( + status, + ))); + } + } + + Ok(()) + } + + fn restore_into_routing_table(route: &RegisteredRoute) -> Result<()> { + // SAFETY: There are no pointers or references inside of MIB_IPFORWARD_ROW2, only primitive + // numbers as such it is safe to zero it. + let mut spec: MIB_IPFORWARD_ROW2 = unsafe { std::mem::zeroed() }; + + // SAFETY: This function must be used to initialize MIB_IPFORWARD_ROW2 structs if it is to + // be used later by CreateIpForwardEntry2. + unsafe { InitializeIpForwardEntry(&mut spec) }; + + spec.InterfaceLuid = route.luid; + spec.DestinationPrefix = win_ip_address_prefix_from_ipnetwork_port_zero(route.network); + spec.NextHop = inet_sockaddr_from_socketaddr(route.next_hop); + spec.Metric = 0; + spec.Protocol = MIB_IPPROTO_NETMGMT; + spec.Origin = NlroManual; + + // SAFETY: DestinationPrefix must be initialized to a valid prefix. NextHop must have a + // valid IP address and family. At least one of InterfaceLuid and InterfaceIndex must be set + // to the interface. + let status = unsafe { CreateIpForwardEntry2(&spec) }; + + if NO_ERROR as i32 != status { + log::error!( + "Could not register route in routing table. Route: {}, Status: {}", + route, + status + ); + return Err(Error::AddToRouteTable(io::Error::from_raw_os_error(status))); + } + Ok(()) + } + + fn parse_string_encoded_luid(encoded_luid: &WideCStr) -> Result<Option<NET_LUID_LH>> { + // The `#` is a valid character in adapter names so we use `?` instead. + // The LUID is thus prefixed with `?` and hex encoded and left-padded with zeroes. + // E.g. `?deadbeefcafebabe` or `?000dbeefcafebabe`. + // + + const STRING_ENCODED_LUID_LENGTH: usize = 17; + + if encoded_luid.len() != STRING_ENCODED_LUID_LENGTH + || Some(Ok('?')) != encoded_luid.chars().next() + { + return Ok(None); + } + + let luid = NET_LUID_LH { + Value: u64::from_str_radix( + &encoded_luid.to_string().map_err(|_| { + log::error!("Failed to parse string encoded LUID: {:?}", encoded_luid); + Error::Conversion + })?[1..], + 16, + ) + .map_err(|_| { + log::error!("Failed to parse string encoded LUID: {:?}", encoded_luid); + Error::Conversion + })?, + }; + + return Ok(Some(luid)); + } + + pub fn delete_applied_routes(&mut self) -> Result<()> { + let mut routes = self.routes.lock().unwrap(); + // Delete all routes owned by us. + // + + for record in (*routes).iter() { + if let Err(_) = Self::delete_from_routing_table(&record.registered_route) { + log::error!( + "Failed to delete route while clearing applied routes, {}", + record.registered_route + ); + } + } + + routes.clear(); + Ok(()) + } + + pub fn register_default_route_changed_callback( + &self, + callback: Callback, + ) -> Result<CallbackHandle> { + let (nonce, callbacks) = &mut *self.callbacks.lock().unwrap(); + let old_nonce = *nonce; + callbacks.insert(old_nonce, callback); + *nonce = nonce.wrapping_add(1); + Ok(CallbackHandle { + nonce: old_nonce, + callbacks: self.callbacks.clone(), + }) + } + + fn default_route_change<'a>( + callbacks: &Arc<Mutex<(i32, HashMap<i32, Callback>)>>, + records: &Arc<Mutex<Vec<RouteRecord>>>, + family: ADDRESS_FAMILY, + event_type: RouteMonitorEventType<'a>, + ) { + // Forward event to all registered listeners. + // + + { + let (_, callbacks) = &mut *callbacks.lock().unwrap(); + for callback in callbacks.values() { + let family = + AddressFamily::try_from_af_family(u16::try_from(family).unwrap()).unwrap(); + callback(event_type, family); + } + } + + // Examine event to determine if best default route has changed. + // + + let route = if let RouteMonitorEventType::Updated(route) = event_type { + route + } else { + return; + }; + + // Examine our routes to see if any of them are policy bound to the best default route. + // + + let mut records = records.lock().unwrap(); + let mut affected_routes: Vec<&mut RouteRecord> = vec![]; + + for record in (*records).iter_mut() { + if matches!(record.route.node, NetNode::DefaultNode) + && family + == u32::from(ipnetwork_to_address_family(record.route.network).to_af_family()) + { + affected_routes.push(record); + } + } + + if affected_routes.is_empty() { + return; + } + + // Update all affected routes. + // + + log::info!("Best default route has changed. Refreshing dependent routes"); + + for affected_route in affected_routes { + // We can't update the existing route because defining characteristics are being + // changed. So removing and adding again is the only option. + // + + match Self::delete_from_routing_table(&affected_route.registered_route) { + Ok(()) => (), + Err(e) => { + log::error!( + "Failed to delete route when refreshing existing routes: {}", + e + ); + continue; + } + } + + affected_route.registered_route.luid = route.iface; + affected_route.registered_route.next_hop = route.gateway; + + match Self::restore_into_routing_table(&affected_route.registered_route) { + Ok(()) => (), + Err(e) => { + log::error!("Failed to add route when refreshing existing routes: {}", e); + continue; + } + } + } + } +} + +impl Drop for RouteManagerInternal { + fn drop(&mut self) { + drop(self.route_monitor_v4.take()); + drop(self.route_monitor_v6.take()); + + match self.delete_applied_routes() { + Ok(()) => (), + Err(e) => { + log::error!("Failed to correctly drop RouteManagerInternal {}", e) + } + } + } +} + +fn interface_luid_from_gateway(gateway: &SOCKADDR_INET) -> Result<NET_LUID_LH> { + const ADAPTER_FLAGS: GET_ADAPTERS_ADDRESSES_FLAGS = GAA_FLAG_SKIP_ANYCAST + | GAA_FLAG_SKIP_MULTICAST + | GAA_FLAG_SKIP_DNS_SERVER + | GAA_FLAG_SKIP_FRIENDLY_NAME + | GAA_FLAG_INCLUDE_GATEWAYS; + + // SAFETY: The si_family field is always valid to access. + let family: u32 = u32::from(unsafe { gateway.si_family }); + let adapters = Adapters::new(family, ADAPTER_FLAGS)?; + + // Process adapters to find matching ones. + // + + let mut matches: Vec<_> = adapters + // SAFETY: We are not allowed to dereference adapter.Head if it has been aquired in a previous iteration of the iterator + // we ensure this is upheld by not saving any references to adapter.Head between iterations. + .iter() + .filter(|adapter| { + if !adapter_interface_enabled(adapter, family).unwrap_or(false) { + return false; + } + let gateways = if adapter.FirstGatewayAddress.is_null() { + vec![] + } else { + // SAFETY: adapter.FirstGatewayAddress is not null and all elements in the linked list live + // in the same buffer and as such have the same lifetime. + unsafe { isolate_gateway_address(get_first_gateway_address_reference(adapter), family) } + }; + + address_present(gateways, &gateway).unwrap_or(false) + }) + .collect(); + + if matches.is_empty() { + log::error!("Unable to find network adapter with specified gateway"); + return Err(Error::DeviceGatewayNotFound); + } + + // Sort matching interfaces ascending by metric. + // + + let target_v4 = AF_INET == family; + + matches.sort_by(|lhs, rhs| { + if target_v4 { + lhs.Ipv4Metric.cmp(&rhs.Ipv4Metric) + } else { + lhs.Ipv6Metric.cmp(&rhs.Ipv6Metric) + } + }); + + // Select the interface with the best (lowest) metric. + // + + Ok(matches[0].Luid) +} + +/// SAFETY: adapter.FirstGatewayAddress must be dereferencable and must live as long as adapter +unsafe fn get_first_gateway_address_reference( + adapter: &IP_ADAPTER_ADDRESSES_LH, +) -> &IP_ADAPTER_GATEWAY_ADDRESS_LH { + &*adapter.FirstGatewayAddress +} + +fn adapter_interface_enabled( + adapter: &IP_ADAPTER_ADDRESSES_LH, + family: ADDRESS_FAMILY, +) -> Result<bool> { + match family { + // SAFETY: All fields in the Anonymous2 union are at represented by a u32 so dereferencing + // them is safe + AF_INET => Ok(0 != unsafe { adapter.Anonymous2.Flags } & IP_ADAPTER_IPV4_ENABLED), + AF_INET6 => Ok(0 != unsafe { adapter.Anonymous2.Flags } & IP_ADAPTER_IPV6_ENABLED), + _ => Err(Error::InvalidSiFamily), + } +} + +/// SAFETY: `head` must be a linked list where each `head.Next` is either null or +/// the it and all of its fields has lifetime 'a and are dereferencable. +unsafe fn isolate_gateway_address<'a>( + head: &'a IP_ADAPTER_GATEWAY_ADDRESS_LH, + family: ADDRESS_FAMILY, +) -> Vec<&'a SOCKET_ADDRESS> { + let mut matches = vec![]; + + let mut gateway = head; + loop { + // SAFETY: The contract states that Address.lpSockaddr is dereferencable if the element is + // non-null + if family == u32::from((*gateway.Address.lpSockaddr).sa_family) { + // SAFETY: The contract states that this field must have lifetime 'a + matches.push(&gateway.Address); + } + + if gateway.Next.is_null() { + break; + } + + // SAFETY: Gateway.Next is not null here and the contract states it must be dereferencable + // if non-null + gateway = &*gateway.Next; + } + + matches +} + +fn address_present(hay: Vec<&'_ SOCKET_ADDRESS>, needle: &'_ SOCKADDR_INET) -> Result<bool> { + for candidate in hay { + // SAFETY: Contract states that needle is dereferencable + if equal_address(needle, candidate)? { + return Ok(true); + } + } + + Ok(false) +} + +fn equal_address(lhs: &'_ SOCKADDR_INET, rhs: &'_ SOCKET_ADDRESS) -> Result<bool> { + let rhs = &*rhs; + // SAFETY: The si_family field is always valid + if unsafe { lhs.si_family != (*rhs.lpSockaddr).sa_family } { + return Ok(false); + } + + match unsafe { lhs.si_family } as u32 { + AF_INET => { + let typed_rhs = rhs.lpSockaddr as *mut SOCKADDR_IN; + // SAFETY: If rhs.lpSockaddr.sa_family is IPv4 then lpSockaddr is a SOCKADDR_IN + Ok(unsafe { lhs.Ipv4.sin_addr.S_un.S_addr == (*typed_rhs).sin_addr.S_un.S_addr }) + } + AF_INET6 => { + let typed_rhs = rhs.lpSockaddr as *mut SOCKADDR_IN6; + // SAFETY: If rhs.lpSockaddr.sa_family is IPv6 then lpSockaddr is a SOCKADDR_IN6 + Ok(unsafe { lhs.Ipv6.sin6_addr.u.Byte == (*typed_rhs).sin6_addr.u.Byte }) + } + _ => { + log::error!("Missing case handler in match"); + Err(Error::InvalidSiFamily) + } + } +} + +/// Linked list containing `IP_ADAPTER_ADDRESSES_LH` queried from the windows API. +/// Consume by using the iterator produced by `iter_mut()` +struct Adapters { + // SAFETY: This vector is not allowed to be resized since all of the data inside of it would be + // dangling + buffer: Vec<u8>, +} + +impl Adapters { + /// Create a new linked list of adapters from the windows API + fn new(family: ADDRESS_FAMILY, flags: GET_ADAPTERS_ADDRESSES_FLAGS) -> Result<Self> { + const MSDN_RECOMMENDED_STARTING_BUFFER_SIZE: usize = 1024 * 15; + let mut buffer: Vec<u8> = Vec::with_capacity(MSDN_RECOMMENDED_STARTING_BUFFER_SIZE); + buffer.resize(MSDN_RECOMMENDED_STARTING_BUFFER_SIZE, 0); + + let mut buffer_size = u32::try_from(buffer.len()).unwrap(); + let mut buffer_pointer = buffer.as_mut_ptr(); + + // Acquire interfaces. + // + + loop { + // SAFETY: buffer_size must point to the correct amount of bytes in the buffer which it + // does. buffer_pointer must point to the start of a mutable buffer which it + // does. After this call buffer_size might have changed and as such the + // buffer must be resized to reflect this if this function is going to be + // called again. + let status = unsafe { + GetAdaptersAddresses( + family, + flags, + std::ptr::null_mut() as *mut _, + buffer_pointer as *mut IP_ADAPTER_ADDRESSES_LH, + &mut buffer_size, + ) + }; + + if ERROR_SUCCESS == status { + // SAFETY: We truncate the buffer to avoid having a bunch of zero:ed objects at the + // end of it truncate will not change capacity and will therefore + // never reallocate the vector which means it can not cause the + // pointers in the buffer to dangle. + buffer.truncate(usize::try_from(buffer_size).unwrap()); + break; + } + + if ERROR_NO_DATA == status { + return Ok(Self { buffer: Vec::new() }); + } + + if ERROR_BUFFER_OVERFLOW != status { + log::error!("Probe required buffer size for GetAdaptersAddresses"); + return Err(Error::Adapter(io::Error::from_raw_os_error( + i32::try_from(status).unwrap(), + ))); + } + + // The needed length is returned in the buffer_size pointer + buffer.resize(usize::try_from(buffer_size).unwrap(), 0); + buffer_pointer = buffer.as_mut_ptr(); + } + + // Verify structure compatibility. + // The structure has been extended many times. + // + + // Unwrapping is fine because we previously would return if we got a ERROR_NO_DATA status. + // As such the buffer is not empty. SAFETY: Casting the buffers first element to an + // IP_ADAPTER_ADDRESSES_LH is safe as that is the underlying data structure. SAFETY: + // This union field is always valid to read from + let system_size = unsafe { + (*(buffer.get(0).unwrap() as *const u8 as *const IP_ADAPTER_ADDRESSES_LH)) + .Anonymous1 + .Anonymous + .Length + }; + let code_size = u32::try_from(std::mem::size_of::<IP_ADAPTER_ADDRESSES_LH>()).unwrap(); + + if system_size < code_size { + log::error!("Expecting IP_ADAPTER_ADDRESSES to have size {code_size} bytes. Found structure with size {system_size} bytes."); + return Err(Error::Adapter(io::Error::new(io::ErrorKind::Other, + format!("Expecting IP_ADAPTER_ADDRESSES to have size {code_size} bytes. Found structure with size {system_size} bytes.")))); + } + + // Initialize members. + // + + Ok(Self { buffer }) + } + + /// Produces a iterator for the linked list in `Adapters` see + /// [AdaptersIterator](struct.AdaptersIterator.html) SAFETY: See the documentation on + /// `AdaptersIterator` + fn iter<'a>(&'a self) -> AdaptersIterator<'a> { + let cur = if self.buffer.is_empty() { + std::ptr::null() + } else { + &self.buffer[0] as *const u8 as *const IP_ADAPTER_ADDRESSES_LH + }; + AdaptersIterator { + _adapters: self, + cur, + } + } +} + +/// SAFETY: You are only allowed to dereference `IP_ADAPTER_ADDRESSES_LH.Next` or any following +/// `Next` items in the linked list if they were produced by the latest call to `next()`. Any raw +/// pointers that were aquired before the call to `next()` are not valid to dereference. +struct AdaptersIterator<'a> { + _adapters: &'a Adapters, + cur: *const IP_ADAPTER_ADDRESSES_LH, +} + +impl<'a> Iterator for AdaptersIterator<'a> { + type Item = &'a IP_ADAPTER_ADDRESSES_LH; + fn next(&mut self) -> Option<Self::Item> { + if self.cur.is_null() { + None + } else { + let ret = self.cur; + // SAFETY: self.cur is guaranteed to not be null, we are also holding a &Adapters which + // guarantees no other reference of self could be held right now which has + // mutably dereferenced the same address that self.cur is pointing to. + // + // It is possible that someone has copied the previous returned items `Next` pointer + // which points to the same as address as self.cur, however dereferencing + // that is unsafe and that code is responsible for not dereferencing + // `Next` on a reference returned by this function after that reference has been + // dropped. + self.cur = unsafe { (*self.cur).Next }; + // SAFETY: ret is guaranteed to be non-null and valid since self.adapters owns the + // memory. + Some(unsafe { &*ret }) + } + } +} + +/// Convert to a windows defined `IP_ADDRESS_PREFIX` from a `ipnetwork::IpNetwork` but set the port +/// to 0 +pub fn win_ip_address_prefix_from_ipnetwork_port_zero(from: IpNetwork) -> IP_ADDRESS_PREFIX { + // Port should not matter so we set it to 0 + let prefix = + crate::windows::inet_sockaddr_from_socketaddr(std::net::SocketAddr::new(from.ip(), 0)); + IP_ADDRESS_PREFIX { + Prefix: prefix, + PrefixLength: from.prefix(), + } +} + +/// Convert to a windows defined `SOCKADDR_INET` from a `IpAddr` but set the port to 0 +pub fn inet_sockaddr_from_ipaddr(from: IpAddr) -> SOCKADDR_INET { + // Port should not matter so we set it to 0 + crate::windows::inet_sockaddr_from_socketaddr(std::net::SocketAddr::new(from, 0)) +} + +/// Convert to a `AddressFamily` from a `ipnetwork::IpNetwork` +pub fn ipnetwork_to_address_family(from: IpNetwork) -> AddressFamily { + if from.is_ipv4() { + AddressFamily::Ipv4 + } else { + AddressFamily::Ipv6 + } +} diff --git a/talpid-core/src/split_tunnel/windows/mod.rs b/talpid-core/src/split_tunnel/windows/mod.rs index 0bde6ac435..49028319a0 100644 --- a/talpid-core/src/split_tunnel/windows/mod.rs +++ b/talpid-core/src/split_tunnel/windows/mod.rs @@ -5,6 +5,7 @@ mod volume_monitor; mod windows; use crate::{ + routing::{self, get_best_default_route, CallbackHandle, EventType, RouteManagerHandle}, tunnel::TunnelMetadata, tunnel_state_machine::TunnelCommand, windows::{ @@ -12,7 +13,6 @@ use crate::{ window::{PowerManagementEvent, PowerManagementListener}, AddressFamily, }, - winnet::{self, get_best_default_route, WinNetAddrFamily, WinNetCallbackHandle}, }; use futures::channel::{mpsc, oneshot}; use std::{ @@ -29,9 +29,7 @@ use std::{ time::Duration, }; use talpid_types::{tunnel::ErrorStateCause, ErrorExt}; -use windows_sys::Win32::{ - Foundation::ERROR_OPERATION_ABORTED, NetworkManagement::Ndis::NET_LUID_LH, -}; +use windows_sys::Win32::Foundation::ERROR_OPERATION_ABORTED; const DRIVER_EVENT_BUFFER_SIZE: usize = 2048; const RESERVED_IP_V4: Ipv4Addr = Ipv4Addr::new(192, 0, 2, 123); @@ -74,7 +72,7 @@ pub enum Error { /// Failed to obtain default route #[error(display = "Failed to obtain the default route")] - ObtainDefaultRoute(#[error(source)] winnet::Error), + ObtainDefaultRoute(#[error(source)] routing::Error), /// Failed to obtain an IP address given a network interface LUID #[error(display = "Failed to obtain IP address for interface LUID")] @@ -116,10 +114,11 @@ pub struct SplitTunnel { event_thread: Option<std::thread::JoinHandle<()>>, quit_event: Arc<windows::Event>, excluded_processes: Arc<RwLock<HashMap<usize, ExcludedProcess>>>, - _route_change_callback: Option<WinNetCallbackHandle>, + _route_change_callback: Option<CallbackHandle>, daemon_tx: Weak<mpsc::UnboundedSender<TunnelCommand>>, async_path_update_in_progress: Arc<AtomicBool>, power_mgmt_handle: tokio::task::JoinHandle<()>, + route_manager: RouteManagerHandle, } enum Request { @@ -187,6 +186,7 @@ impl SplitTunnel { daemon_tx: Weak<mpsc::UnboundedSender<TunnelCommand>>, volume_update_rx: mpsc::UnboundedReceiver<()>, power_mgmt_rx: PowerManagementListener, + route_manager: RouteManagerHandle, ) -> Result<Self, Error> { let excluded_processes = Arc::new(RwLock::new(HashMap::new())); @@ -209,6 +209,7 @@ impl SplitTunnel { async_path_update_in_progress: Arc::new(AtomicBool::new(false)), excluded_processes, power_mgmt_handle, + route_manager, }) } @@ -715,13 +716,22 @@ impl SplitTunnel { )); self._route_change_callback = None; + let moved_context_mutex = context_mutex.clone(); let mut context = context_mutex.lock().unwrap(); - let callback = winnet::add_default_route_change_callback( - Some(split_tunnel_default_route_change_handler), - context_mutex.clone(), - ) - .map(Some) - .map_err(|_| Error::RegisterRouteChangeCallback)?; + let callback = self + .runtime + .block_on( + self.route_manager + .add_default_route_change_callback(Box::new(move |event, addr_family| { + split_tunnel_default_route_change_handler( + event, + addr_family, + &moved_context_mutex, + ) + })), + ) + .map(Some) + .map_err(|_| Error::RegisterRouteChangeCallback)?; context.initialize_internet_addresses()?; context.register_ips()?; @@ -801,16 +811,10 @@ impl SplitTunnelDefaultRouteChangeHandlerContext { pub fn initialize_internet_addresses(&mut self) -> Result<(), Error> { // Identify IP address that gives us Internet access - let internet_ipv4 = get_best_default_route(WinNetAddrFamily::IPV4) + let internet_ipv4 = get_best_default_route(AddressFamily::Ipv4) .map_err(Error::ObtainDefaultRoute)? .map(|route| { - get_ip_address_for_interface( - AddressFamily::Ipv4, - NET_LUID_LH { - Value: route.interface_luid, - }, - ) - .map(|ip| match ip { + get_ip_address_for_interface(AddressFamily::Ipv4, route.iface).map(|ip| match ip { Some(IpAddr::V4(addr)) => Some(addr), Some(_) => unreachable!("wrong address family (expected IPv4)"), None => { @@ -822,16 +826,10 @@ impl SplitTunnelDefaultRouteChangeHandlerContext { .transpose() .map_err(Error::LuidToIp)? .flatten(); - let internet_ipv6 = get_best_default_route(WinNetAddrFamily::IPV6) + let internet_ipv6 = get_best_default_route(AddressFamily::Ipv6) .map_err(Error::ObtainDefaultRoute)? .map(|route| { - get_ip_address_for_interface( - AddressFamily::Ipv6, - NET_LUID_LH { - Value: route.interface_luid, - }, - ) - .map(|ip| match ip { + get_ip_address_for_interface(AddressFamily::Ipv6, route.iface).map(|ip| match ip { Some(IpAddr::V6(addr)) => Some(addr), Some(_) => unreachable!("wrong address family (expected IPv6)"), None => { @@ -851,16 +849,14 @@ impl SplitTunnelDefaultRouteChangeHandlerContext { } } -unsafe extern "system" fn split_tunnel_default_route_change_handler( - event_type: winnet::WinNetDefaultRouteChangeEventType, - address_family: WinNetAddrFamily, - default_route: winnet::WinNetDefaultRoute, - ctx: *mut libc::c_void, +fn split_tunnel_default_route_change_handler<'a>( + event_type: EventType<'a>, + address_family: AddressFamily, + ctx_mutex: &Arc<Mutex<SplitTunnelDefaultRouteChangeHandlerContext>>, ) { - use winnet::WinNetDefaultRouteChangeEventType::*; + use crate::routing::EventType::*; // Update the "internet interface" IP when best default route changes - let ctx_mutex = &mut *(ctx as *mut Arc<Mutex<SplitTunnelDefaultRouteChangeHandlerContext>>); let mut ctx = ctx_mutex.lock().expect("ST route handler mutex poisoned"); let daemon_tx = ctx.daemon_tx.upgrade(); @@ -870,16 +866,9 @@ unsafe extern "system" fn split_tunnel_default_route_change_handler( } }; - let translated_family = winnet_to_talpid_family(address_family); - let result = match event_type { - DefaultRouteChanged | DefaultRouteUpdatedDetails => { - match get_ip_address_for_interface( - translated_family, - NET_LUID_LH { - Value: default_route.interface_luid, - }, - ) { + Updated(default_route) | UpdatedDetails(default_route) => { + match get_ip_address_for_interface(address_family, default_route.iface) { Ok(Some(ip)) => match IpAddr::from(ip) { IpAddr::V4(addr) => ctx.addresses.internet_ipv4 = Some(addr), IpAddr::V6(addr) => ctx.addresses.internet_ipv6 = Some(addr), @@ -887,10 +876,10 @@ unsafe extern "system" fn split_tunnel_default_route_change_handler( Ok(None) => { log::warn!("Failed to obtain default route interface address"); match address_family { - WinNetAddrFamily::IPV4 => { + AddressFamily::Ipv4 => { ctx.addresses.internet_ipv4 = None; } - WinNetAddrFamily::IPV6 => { + AddressFamily::Ipv6 => { ctx.addresses.internet_ipv6 = None; } } @@ -910,12 +899,12 @@ unsafe extern "system" fn split_tunnel_default_route_change_handler( ctx.register_ips() } // no default route - DefaultRouteRemoved => { + Removed => { match address_family { - WinNetAddrFamily::IPV4 => { + AddressFamily::Ipv4 => { ctx.addresses.internet_ipv4 = None; } - WinNetAddrFamily::IPV6 => { + AddressFamily::Ipv6 => { ctx.addresses.internet_ipv6 = None; } } @@ -931,10 +920,3 @@ unsafe extern "system" fn split_tunnel_default_route_change_handler( maybe_send(TunnelCommand::Block(ErrorStateCause::SplitTunnelError)); } } - -fn winnet_to_talpid_family(address_family: WinNetAddrFamily) -> AddressFamily { - match address_family { - WinNetAddrFamily::IPV4 => AddressFamily::Ipv4, - WinNetAddrFamily::IPV6 => AddressFamily::Ipv6, - } -} diff --git a/talpid-core/src/tunnel/mod.rs b/talpid-core/src/tunnel/mod.rs index 302c8003c9..4425c3c69d 100644 --- a/talpid-core/src/tunnel/mod.rs +++ b/talpid-core/src/tunnel/mod.rs @@ -42,7 +42,7 @@ pub enum Error { /// Failure in Windows syscall. #[cfg(windows)] #[error(display = "Failure in Windows syscall")] - WinnetError(#[error(source)] crate::winnet::Error), + WinnetError(#[error(source)] crate::routing::Error), /// Running on an operating system which is not supported yet. #[error(display = "Tunnel type not supported on this operating system")] diff --git a/talpid-core/src/tunnel/wireguard/mod.rs b/talpid-core/src/tunnel/wireguard/mod.rs index 5e8c0ede49..b982dc148d 100644 --- a/talpid-core/src/tunnel/wireguard/mod.rs +++ b/talpid-core/src/tunnel/wireguard/mod.rs @@ -226,6 +226,8 @@ impl WireguardMonitor { args.resource_dir, args.tun_provider, #[cfg(target_os = "windows")] + args.route_manager.clone(), + #[cfg(target_os = "windows")] setup_done_tx, )?; let iface_name = tunnel.get_interface_name(); @@ -507,6 +509,7 @@ impl WireguardMonitor { log_path: Option<&Path>, resource_dir: &Path, tun_provider: Arc<Mutex<TunProvider>>, + #[cfg(windows)] route_manager_handle: crate::routing::RouteManagerHandle, #[cfg(windows)] setup_done_tx: mpsc::Sender<std::result::Result<(), BoxedError>>, ) -> Result<Box<dyn Tunnel>> { #[cfg(target_os = "linux")] @@ -576,7 +579,11 @@ impl WireguardMonitor { #[cfg(not(windows))] Self::get_tunnel_destinations(config).flat_map(Self::replace_default_prefixes), #[cfg(windows)] + route_manager_handle, + #[cfg(windows)] setup_done_tx, + #[cfg(windows)] + &runtime, ) .map_err(Error::TunnelError)?, )) diff --git a/talpid-core/src/tunnel/wireguard/wireguard_go.rs b/talpid-core/src/tunnel/wireguard/wireguard_go.rs index 60705d324f..a1ca8be6ba 100644 --- a/talpid-core/src/tunnel/wireguard/wireguard_go.rs +++ b/talpid-core/src/tunnel/wireguard/wireguard_go.rs @@ -39,9 +39,6 @@ use { type Result<T> = std::result::Result<T, TunnelError>; -#[cfg(target_os = "windows")] -use crate::winnet; - #[cfg(not(target_os = "windows"))] use std::sync::{Arc, Mutex}; @@ -66,7 +63,7 @@ pub struct WgGoTunnel { // context that maps to fs::File instance, used with logging callback _logging_context: LoggingContext, #[cfg(target_os = "windows")] - _route_callback_handle: Option<crate::winnet::WinNetCallbackHandle>, + _route_callback_handle: Option<crate::routing::CallbackHandle>, #[cfg(target_os = "windows")] setup_handle: tokio::task::JoinHandle<()>, } @@ -117,15 +114,19 @@ impl WgGoTunnel { pub fn start_tunnel( config: &Config, log_path: Option<&Path>, + route_manager_handle: crate::tunnel::RouteManagerHandle, mut done_tx: futures::channel::mpsc::Sender<std::result::Result<(), BoxedError>>, + runtime: &tokio::runtime::Handle, ) -> Result<Self> { use talpid_types::ErrorExt; - let route_callback_handle = winnet::add_default_route_change_callback( - Some(WgGoTunnel::default_route_changed_callback), - (), - ) - .ok(); + let route_callback_handle = runtime + .block_on( + route_manager_handle.add_default_route_change_callback(Box::new( + WgGoTunnel::default_route_changed_callback, + )), + ) + .ok(); if route_callback_handle.is_none() { log::warn!("Failed to register default route callback"); } @@ -208,25 +209,21 @@ impl WgGoTunnel { // Callback to be used to rebind the tunnel sockets when the default route changes #[cfg(target_os = "windows")] - pub unsafe extern "system" fn default_route_changed_callback( - event_type: winnet::WinNetDefaultRouteChangeEventType, - address_family: winnet::WinNetAddrFamily, - default_route: winnet::WinNetDefaultRoute, - _ctx: *mut libc::c_void, + pub fn default_route_changed_callback<'a>( + event_type: crate::routing::EventType<'a>, + address_family: crate::windows::AddressFamily, ) { - use windows_sys::Win32::NetworkManagement::{ - IpHelper::ConvertInterfaceLuidToIndex, Ndis::NET_LUID_LH, - }; - use winnet::WinNetDefaultRouteChangeEventType::*; + use crate::routing::EventType::*; + use windows_sys::Win32::NetworkManagement::IpHelper::ConvertInterfaceLuidToIndex; let iface_idx: u32 = match event_type { - DefaultRouteChanged => { + Updated(default_route) => { let mut iface_idx = 0u32; - let iface_luid = NET_LUID_LH { - Value: default_route.interface_luid, + // TODO: Make sure unwrap is fine + let iface_luid = default_route.iface; + let status = unsafe { + ConvertInterfaceLuidToIndex(&iface_luid as *const _, &mut iface_idx as *mut _) }; - let status = - ConvertInterfaceLuidToIndex(&iface_luid as *const _, &mut iface_idx as *mut _); if status != 0 { log::error!( "Failed to convert interface LUID to interface index: {}: {}", @@ -238,12 +235,12 @@ impl WgGoTunnel { iface_idx } // if there is no new default route, specify 0 as the interface index - DefaultRouteRemoved => 0, + Removed => 0, // ignore interface updates that don't affect the interface to use - DefaultRouteUpdatedDetails => return, + UpdatedDetails(_) => return, }; - wgRebindTunnelSocket(address_family.to_windows_proto_enum(), iface_idx); + unsafe { wgRebindTunnelSocket(address_family.to_af_family(), iface_idx) }; } #[cfg(not(target_os = "windows"))] diff --git a/talpid-core/src/tunnel_state_machine/connecting_state.rs b/talpid-core/src/tunnel_state_machine/connecting_state.rs index 5a83bd6b76..964963d46e 100644 --- a/talpid-core/src/tunnel_state_machine/connecting_state.rs +++ b/talpid-core/src/tunnel_state_machine/connecting_state.rs @@ -29,7 +29,7 @@ use talpid_types::{ }; #[cfg(windows)] -use crate::{routing, winnet}; +use crate::routing; #[cfg(target_os = "android")] use crate::tunnel::tun_provider; @@ -524,12 +524,7 @@ fn should_retry(error: &tunnel::Error, retry_attempt: u32) -> bool { #[cfg(windows)] fn is_recoverable_routing_error(error: &crate::routing::Error) -> bool { match error { - routing::Error::AddRoutesFailed(route_error) => match route_error { - winnet::Error::GetDefaultRoute - | winnet::Error::GetDeviceByName - | winnet::Error::GetDeviceByGateway => true, - _ => false, - }, + routing::Error::AddRoutesFailed(_) => true, _ => false, } } diff --git a/talpid-core/src/tunnel_state_machine/mod.rs b/talpid-core/src/tunnel_state_machine/mod.rs index c1b52278f0..5d13b7d1f2 100644 --- a/talpid-core/src/tunnel_state_machine/mod.rs +++ b/talpid-core/src/tunnel_state_machine/mod.rs @@ -258,6 +258,10 @@ impl TunnelStateMachine { #[cfg(target_os = "windows")] let power_mgmt_rx = crate::windows::window::PowerManagementListener::new(); + let route_manager = RouteManager::new(HashSet::new()) + .await + .map_err(Error::InitRouteManagerError)?; + #[cfg(windows)] let split_tunnel = split_tunnel::SplitTunnel::new( runtime.clone(), @@ -265,6 +269,9 @@ impl TunnelStateMachine { args.command_tx.clone(), volume_update_rx, power_mgmt_rx.clone(), + route_manager + .handle() + .map_err(Error::InitRouteManagerError)?, ) .map_err(Error::InitSplitTunneling)?; @@ -279,9 +286,6 @@ impl TunnelStateMachine { }; let firewall = Firewall::from_args(fw_args).map_err(Error::InitFirewallError)?; - let route_manager = RouteManager::new(HashSet::new()) - .await - .map_err(Error::InitRouteManagerError)?; let dns_monitor = DnsMonitor::new( #[cfg(target_os = "linux")] runtime.clone(), @@ -315,6 +319,8 @@ impl TunnelStateMachine { #[cfg(target_os = "android")] android_context, #[cfg(target_os = "windows")] + route_manager.handle()?, + #[cfg(target_os = "windows")] power_mgmt_rx, ) .await diff --git a/talpid-core/src/windows/mod.rs b/talpid-core/src/windows/mod.rs index d853991707..5504e11d93 100644 --- a/talpid-core/src/windows/mod.rs +++ b/talpid-core/src/windows/mod.rs @@ -109,7 +109,7 @@ impl fmt::Display for AddressFamily { } impl AddressFamily { - /// Convert an [`AddressFamily`] to one of the `AF_*` constants. + /// Convert one of the `AF_*` constants to an [`AddressFamily`]. pub fn try_from_af_family(family: u16) -> Result<AddressFamily> { match u32::from(family) { AF_INET => Ok(AddressFamily::Ipv4), @@ -117,6 +117,15 @@ impl AddressFamily { family => Err(Error::UnknownAddressFamily(family)), } } + + /// Convert an [`AddressFamily`] to one of the `AF_*` constants. + pub fn to_af_family(&self) -> u16 { + match self { + // These values are both small enough to fit in a u16 + Self::Ipv4 => u16::try_from(AF_INET).unwrap(), + Self::Ipv6 => u16::try_from(AF_INET6).unwrap(), + } + } } /// Context for [`notify_ip_interface_change`]. When it is dropped, diff --git a/talpid-core/src/winnet.rs b/talpid-core/src/winnet.rs deleted file mode 100644 index 9843d873aa..0000000000 --- a/talpid-core/src/winnet.rs +++ /dev/null @@ -1,416 +0,0 @@ -use self::api::*; -use crate::{logging::windows::log_sink, routing::Node}; -use ipnetwork::IpNetwork; -use libc::c_void; -use std::{ - convert::TryFrom, - net::{IpAddr, Ipv4Addr, Ipv6Addr}, - ptr, -}; -use widestring::WideCString; - -/// Errors that this module may produce. -#[derive(err_derive::Error, Debug)] -pub enum Error { - /// Supplied interface alias is invalid. - #[error(display = "Supplied interface alias is invalid")] - InvalidInterfaceAlias(#[error(source)] widestring::NulError<u16>), - - /// Failed to enable IPv6 on the network interface. - #[error(display = "Failed to enable IPv6 on the network interface")] - EnableIpv6, - - /// Failed to get the current default route. - #[error(display = "Failed to obtain default route")] - GetDefaultRoute, - - /// Failed to get a network device. - #[error(display = "Failed to obtain network interface by name")] - GetDeviceByName, - - /// Failed to get a network device. - #[error(display = "Failed to obtain network interface by gateway")] - GetDeviceByGateway, - - /// Unexpected error while adding routes - #[error(display = "Winnet returned an error while adding routes")] - GeneralAddRoutesError, - - /// Failed to obtain an IP address given a LUID. - #[error(display = "Failed to obtain IP address for the given interface")] - GetIpAddressFromLuid, - - /// Failed to read IPv6 status on the TAP network interface. - #[error(display = "Failed to read IPv6 status on the TAP network interface")] - GetIpv6Status, -} - -fn logging_context() -> *const u8 { - b"WinNet\0".as_ptr() -} - -#[derive(Debug, Default, Clone, Copy)] -#[allow(dead_code)] -#[repr(u32)] -pub enum WinNetAddrFamily { - #[default] - IPV4 = 0, - IPV6 = 1, -} - -impl WinNetAddrFamily { - pub fn to_windows_proto_enum(&self) -> u16 { - match self { - Self::IPV4 => 2, - Self::IPV6 => 23, - } - } -} - -#[repr(C)] -#[derive(Default)] -pub struct WinNetIp { - pub addr_family: WinNetAddrFamily, - pub ip_bytes: [u8; 16], -} - -#[repr(C)] -#[derive(Default)] -pub struct WinNetDefaultRoute { - pub interface_luid: u64, - pub gateway: WinNetIp, -} - -#[derive(Debug)] -pub struct WrongIpFamilyError; - -impl TryFrom<WinNetIp> for Ipv4Addr { - type Error = WrongIpFamilyError; - - fn try_from(addr: WinNetIp) -> Result<Ipv4Addr, WrongIpFamilyError> { - match addr.addr_family { - WinNetAddrFamily::IPV4 => { - let mut bytes: [u8; 4] = Default::default(); - bytes.clone_from_slice(&addr.ip_bytes[..4]); - Ok(Ipv4Addr::from(bytes)) - } - WinNetAddrFamily::IPV6 => Err(WrongIpFamilyError), - } - } -} - -impl TryFrom<WinNetIp> for Ipv6Addr { - type Error = WrongIpFamilyError; - - fn try_from(addr: WinNetIp) -> Result<Ipv6Addr, WrongIpFamilyError> { - match addr.addr_family { - WinNetAddrFamily::IPV4 => Err(WrongIpFamilyError), - WinNetAddrFamily::IPV6 => Ok(Ipv6Addr::from(addr.ip_bytes)), - } - } -} - -impl From<WinNetIp> for IpAddr { - fn from(addr: WinNetIp) -> IpAddr { - match addr.addr_family { - WinNetAddrFamily::IPV4 => IpAddr::V4(Ipv4Addr::try_from(addr).unwrap()), - WinNetAddrFamily::IPV6 => IpAddr::V6(Ipv6Addr::try_from(addr).unwrap()), - } - } -} - -impl From<IpAddr> for WinNetIp { - fn from(addr: IpAddr) -> WinNetIp { - let mut bytes = [0u8; 16]; - match addr { - IpAddr::V4(v4_addr) => { - bytes[..4].copy_from_slice(&v4_addr.octets()); - WinNetIp { - addr_family: WinNetAddrFamily::IPV4, - ip_bytes: bytes, - } - } - IpAddr::V6(v6_addr) => { - bytes.copy_from_slice(&v6_addr.octets()); - - WinNetIp { - addr_family: WinNetAddrFamily::IPV6, - ip_bytes: bytes, - } - } - } - } -} - -#[repr(C)] -pub struct WinNetIpNetwork { - prefix: u8, - ip: WinNetIp, -} - -impl From<IpNetwork> for WinNetIpNetwork { - fn from(network: IpNetwork) -> WinNetIpNetwork { - WinNetIpNetwork { - prefix: network.prefix(), - ip: WinNetIp::from(network.ip()), - } - } -} - -#[repr(C)] -pub struct WinNetNode { - gateway: *mut WinNetIp, - device_name: *mut u16, -} - -impl WinNetNode { - fn new(name: &str, ip: WinNetIp) -> Self { - let device_name = WideCString::from_str(name) - .expect("Failed to convert UTF-8 string to null terminated UCS string") - .into_raw(); - let gateway = Box::into_raw(Box::new(ip)); - Self { - gateway, - device_name, - } - } - - fn from_gateway(ip: WinNetIp) -> Self { - let gateway = Box::into_raw(Box::new(ip)); - Self { - gateway, - device_name: ptr::null_mut(), - } - } - - fn from_device(name: &str) -> Self { - let device_name = WideCString::from_str(name) - .expect("Failed to convert UTF-8 string to null terminated UCS string") - .into_raw(); - Self { - gateway: ptr::null_mut(), - device_name, - } - } -} - -impl From<&Node> for WinNetNode { - fn from(node: &Node) -> Self { - match (node.get_address(), node.get_device()) { - (Some(gateway), None) => WinNetNode::from_gateway(gateway.into()), - (None, Some(device)) => WinNetNode::from_device(device), - (Some(gateway), Some(device)) => WinNetNode::new(device, gateway.into()), - _ => unreachable!(), - } - } -} - -impl Drop for WinNetNode { - fn drop(&mut self) { - if !self.gateway.is_null() { - unsafe { - let _ = Box::from_raw(self.gateway); - } - } - if !self.device_name.is_null() { - unsafe { - let _ = WideCString::from_ptr_str(self.device_name); - } - } - } -} - -#[repr(C)] -pub struct WinNetRoute { - gateway: WinNetIpNetwork, - node: *mut WinNetNode, -} - -impl WinNetRoute { - pub fn through_default_node(gateway: WinNetIpNetwork) -> Self { - Self { - gateway, - node: ptr::null_mut(), - } - } - - pub fn new(node: WinNetNode, gateway: WinNetIpNetwork) -> Self { - let node = Box::into_raw(Box::new(node)); - Self { gateway, node } - } -} - -impl Drop for WinNetRoute { - fn drop(&mut self) { - if !self.node.is_null() { - unsafe { - let _ = Box::from_raw(self.node); - } - self.node = ptr::null_mut(); - } - } -} - -pub fn activate_routing_manager() -> bool { - unsafe { WinNet_ActivateRouteManager(Some(log_sink), logging_context()) } -} - -pub struct WinNetCallbackHandle { - handle: *mut libc::c_void, - // Allows us to keep the context pointer alive. - _context: Box<dyn std::any::Any>, -} - -unsafe impl Send for WinNetCallbackHandle {} - -impl Drop for WinNetCallbackHandle { - fn drop(&mut self) { - unsafe { WinNet_UnregisterDefaultRouteChangedCallback(self.handle) }; - } -} - -#[derive(Debug, Clone, Copy, PartialEq)] -#[allow(dead_code)] -#[repr(u16)] -pub enum WinNetDefaultRouteChangeEventType { - DefaultRouteChanged = 0, - DefaultRouteUpdatedDetails = 1, - DefaultRouteRemoved = 2, -} - -pub type DefaultRouteChangedCallback = unsafe extern "system" fn( - event_type: WinNetDefaultRouteChangeEventType, - family: WinNetAddrFamily, - default_route: WinNetDefaultRoute, - ctx: *mut c_void, -); - -#[derive(err_derive::Error, Debug)] -#[error(display = "Failed to set callback for default route")] -pub struct DefaultRouteCallbackError; - -pub fn add_default_route_change_callback<T: 'static>( - callback: Option<DefaultRouteChangedCallback>, - context: T, -) -> std::result::Result<WinNetCallbackHandle, DefaultRouteCallbackError> { - let mut handle_ptr = ptr::null_mut(); - let mut context = Box::new(context); - let ctx_ptr = &mut *context as *mut T as *mut libc::c_void; - unsafe { - if !WinNet_RegisterDefaultRouteChangedCallback(callback, ctx_ptr, &mut handle_ptr as *mut _) - { - return Err(DefaultRouteCallbackError); - } - - Ok(WinNetCallbackHandle { - handle: handle_ptr, - _context: context, - }) - } -} - -pub fn routing_manager_add_routes(routes: &[WinNetRoute]) -> Result<(), Error> { - let ptr = routes.as_ptr(); - let length: u32 = routes.len() as u32; - match unsafe { WinNet_AddRoutes(ptr, length) } { - WinNetAddRouteStatus::Success => Ok(()), - WinNetAddRouteStatus::GeneralError => Err(Error::GeneralAddRoutesError), - WinNetAddRouteStatus::NoDefaultRoute => Err(Error::GetDefaultRoute), - WinNetAddRouteStatus::NameNotFound => Err(Error::GetDeviceByName), - WinNetAddRouteStatus::GatewayNotFound => Err(Error::GetDeviceByGateway), - } -} - -pub fn routing_manager_delete_applied_routes() -> bool { - unsafe { WinNet_DeleteAppliedRoutes() } -} - -pub fn deactivate_routing_manager() { - unsafe { WinNet_DeactivateRouteManager() } -} - -pub fn get_best_default_route( - family: WinNetAddrFamily, -) -> Result<Option<WinNetDefaultRoute>, Error> { - let mut default_route = WinNetDefaultRoute::default(); - match unsafe { - WinNet_GetBestDefaultRoute( - family, - &mut default_route as *mut _, - Some(log_sink), - logging_context(), - ) - } { - WinNetStatus::Success => Ok(Some(default_route)), - WinNetStatus::NotFound => Ok(None), - WinNetStatus::Failure => Err(Error::GetDefaultRoute), - } -} - -#[allow(non_snake_case)] -mod api { - use super::DefaultRouteChangedCallback; - use crate::logging::windows::LogSink; - - #[allow(dead_code)] - #[repr(u32)] - pub enum WinNetStatus { - Success = 0, - NotFound = 1, - Failure = 2, - } - - #[allow(dead_code)] - #[repr(u32)] - pub enum WinNetAddRouteStatus { - Success = 0, - GeneralError = 1, - NoDefaultRoute = 2, - NameNotFound = 3, - GatewayNotFound = 4, - } - - extern "system" { - #[link_name = "WinNet_ActivateRouteManager"] - pub fn WinNet_ActivateRouteManager(sink: Option<LogSink>, sink_context: *const u8) -> bool; - - #[link_name = "WinNet_AddRoutes"] - pub fn WinNet_AddRoutes( - routes: *const super::WinNetRoute, - num_routes: u32, - ) -> WinNetAddRouteStatus; - - // #[link_name = "WinNet_AddRoute"] - // pub fn WinNet_AddRoute(route: *const super::WinNetRoute) -> WinNetAddRouteStatus; - - // #[link_name = "WinNet_DeleteRoutes"] - // pub fn WinNet_DeleteRoutes(routes: *const super::WinNetRoute, num_routes: u32) -> bool; - - // #[link_name = "WinNet_DeleteRoute"] - // pub fn WinNet_DeleteRoute(route: *const super::WinNetRoute) -> bool; - - #[link_name = "WinNet_DeleteAppliedRoutes"] - pub fn WinNet_DeleteAppliedRoutes() -> bool; - - #[link_name = "WinNet_DeactivateRouteManager"] - pub fn WinNet_DeactivateRouteManager(); - - #[link_name = "WinNet_GetBestDefaultRoute"] - pub fn WinNet_GetBestDefaultRoute( - family: super::WinNetAddrFamily, - default_route: *mut super::WinNetDefaultRoute, - sink: Option<LogSink>, - sink_context: *const u8, - ) -> WinNetStatus; - - #[link_name = "WinNet_RegisterDefaultRouteChangedCallback"] - pub fn WinNet_RegisterDefaultRouteChangedCallback( - callback: Option<DefaultRouteChangedCallback>, - callbackContext: *mut libc::c_void, - registrationHandle: *mut *mut libc::c_void, - ) -> bool; - - #[link_name = "WinNet_UnregisterDefaultRouteChangedCallback"] - pub fn WinNet_UnregisterDefaultRouteChangedCallback(registrationHandle: *mut libc::c_void); - } -} diff --git a/windows/winnet/extras.sln b/windows/winnet/extras.sln deleted file mode 100644 index 0e85bd1879..0000000000 --- a/windows/winnet/extras.sln +++ /dev/null @@ -1,102 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.32014.148 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "loader", "src\extras\loader\loader.vcxproj", "{227C50F4-D9F6-4D9A-84A0-33CE84432D0D}" - ProjectSection(ProjectDependencies) = postProject - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D} = {EE69EA4A-CF71-4B88-866B-957F60C4CE0D} - {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4} = {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winnet", "src\winnet\winnet.vcxproj", "{89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}" - ProjectSection(ProjectDependencies) = postProject - {B52E2D10-A94A-4605-914A-2DCEF6A757EF} = {B52E2D10-A94A-4605-914A-2DCEF6A757EF} - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D} = {EE69EA4A-CF71-4B88-866B-957F60C4CE0D} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcommon", "..\windows-libraries\src\libcommon\libcommon.vcxproj", "{B52E2D10-A94A-4605-914A-2DCEF6A757EF}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tests", "src\extras\tests\tests.vcxproj", "{01A4E766-CC61-40B7-A3D6-7A37F6BF5CCB}" - ProjectSection(ProjectDependencies) = postProject - {B52E2D10-A94A-4605-914A-2DCEF6A757EF} = {B52E2D10-A94A-4605-914A-2DCEF6A757EF} - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D} = {EE69EA4A-CF71-4B88-866B-957F60C4CE0D} - {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4} = {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libshared", "..\libshared\src\libshared\libshared.vcxproj", "{EE69EA4A-CF71-4B88-866B-957F60C4CE0D}" - ProjectSection(ProjectDependencies) = postProject - {B52E2D10-A94A-4605-914A-2DCEF6A757EF} = {B52E2D10-A94A-4605-914A-2DCEF6A757EF} - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Debug-AutoTests|x64 = Debug-AutoTests|x64 - Debug-AutoTests|x86 = Debug-AutoTests|x86 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {227C50F4-D9F6-4D9A-84A0-33CE84432D0D}.Debug|x64.ActiveCfg = Debug|x64 - {227C50F4-D9F6-4D9A-84A0-33CE84432D0D}.Debug|x64.Build.0 = Debug|x64 - {227C50F4-D9F6-4D9A-84A0-33CE84432D0D}.Debug|x86.ActiveCfg = Debug|Win32 - {227C50F4-D9F6-4D9A-84A0-33CE84432D0D}.Debug|x86.Build.0 = Debug|Win32 - {227C50F4-D9F6-4D9A-84A0-33CE84432D0D}.Debug-AutoTests|x64.ActiveCfg = Debug|x64 - {227C50F4-D9F6-4D9A-84A0-33CE84432D0D}.Debug-AutoTests|x86.ActiveCfg = Debug|Win32 - {227C50F4-D9F6-4D9A-84A0-33CE84432D0D}.Release|x64.ActiveCfg = Release|x64 - {227C50F4-D9F6-4D9A-84A0-33CE84432D0D}.Release|x64.Build.0 = Release|x64 - {227C50F4-D9F6-4D9A-84A0-33CE84432D0D}.Release|x86.ActiveCfg = Release|Win32 - {227C50F4-D9F6-4D9A-84A0-33CE84432D0D}.Release|x86.Build.0 = Release|Win32 - {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug|x64.ActiveCfg = Debug|x64 - {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug|x64.Build.0 = Debug|x64 - {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug|x86.ActiveCfg = Debug|Win32 - {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug|x86.Build.0 = Debug|Win32 - {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug-AutoTests|x64.ActiveCfg = Debug Static|x64 - {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug-AutoTests|x64.Build.0 = Debug Static|x64 - {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug-AutoTests|x86.ActiveCfg = Debug Static|Win32 - {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug-AutoTests|x86.Build.0 = Debug Static|Win32 - {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Release|x64.ActiveCfg = Release|x64 - {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Release|x64.Build.0 = Release|x64 - {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Release|x86.ActiveCfg = Release|Win32 - {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Release|x86.Build.0 = Release|Win32 - {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug|x64.ActiveCfg = Debug|x64 - {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug|x64.Build.0 = Debug|x64 - {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug|x86.ActiveCfg = Debug|Win32 - {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug|x86.Build.0 = Debug|Win32 - {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug-AutoTests|x64.ActiveCfg = Debug|x64 - {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug-AutoTests|x64.Build.0 = Debug|x64 - {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug-AutoTests|x86.ActiveCfg = Debug|Win32 - {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug-AutoTests|x86.Build.0 = Debug|Win32 - {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Release|x64.ActiveCfg = Release|x64 - {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Release|x64.Build.0 = Release|x64 - {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Release|x86.ActiveCfg = Release|Win32 - {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Release|x86.Build.0 = Release|Win32 - {01A4E766-CC61-40B7-A3D6-7A37F6BF5CCB}.Debug|x64.ActiveCfg = Debug|x64 - {01A4E766-CC61-40B7-A3D6-7A37F6BF5CCB}.Debug|x86.ActiveCfg = Debug|Win32 - {01A4E766-CC61-40B7-A3D6-7A37F6BF5CCB}.Debug-AutoTests|x64.ActiveCfg = Debug|x64 - {01A4E766-CC61-40B7-A3D6-7A37F6BF5CCB}.Debug-AutoTests|x64.Build.0 = Debug|x64 - {01A4E766-CC61-40B7-A3D6-7A37F6BF5CCB}.Debug-AutoTests|x86.ActiveCfg = Debug|Win32 - {01A4E766-CC61-40B7-A3D6-7A37F6BF5CCB}.Debug-AutoTests|x86.Build.0 = Debug|Win32 - {01A4E766-CC61-40B7-A3D6-7A37F6BF5CCB}.Release|x64.ActiveCfg = Debug|x64 - {01A4E766-CC61-40B7-A3D6-7A37F6BF5CCB}.Release|x86.ActiveCfg = Debug|Win32 - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D}.Debug|x64.ActiveCfg = Debug|x64 - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D}.Debug|x64.Build.0 = Debug|x64 - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D}.Debug|x86.ActiveCfg = Debug|Win32 - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D}.Debug|x86.Build.0 = Debug|Win32 - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D}.Debug-AutoTests|x64.ActiveCfg = Debug|x64 - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D}.Debug-AutoTests|x64.Build.0 = Debug|x64 - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D}.Debug-AutoTests|x86.ActiveCfg = Debug|Win32 - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D}.Debug-AutoTests|x86.Build.0 = Debug|Win32 - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D}.Release|x64.ActiveCfg = Release|x64 - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D}.Release|x64.Build.0 = Release|x64 - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D}.Release|x86.ActiveCfg = Release|Win32 - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {B11B65B3-3DA6-495C-B0F5-5ACBB2F1743D} - EndGlobalSection -EndGlobal diff --git a/windows/winnet/src/extras/loader/loader.cpp b/windows/winnet/src/extras/loader/loader.cpp deleted file mode 100644 index acb5083b36..0000000000 --- a/windows/winnet/src/extras/loader/loader.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "stdafx.h" -#include <winnet/winnet.h> -#include <libshared/logging/stdoutlogger.h> -#include <iostream> - -int main() -{ - //wchar_t *alias = nullptr; - - //const auto status = WinNet_GetTapInterfaceAlias(&alias, nullptr, nullptr); - - //switch (status) - //{ - // case WINNET_GTIA_STATUS::FAILURE: - // { - // std::wcout << L"Could not determine alias" << std::endl; - // break; - // } - // case WINNET_GTIA_STATUS::SUCCESS: - // { - // std::wcout << L"Interface alias: " << alias << std::endl; - // WinNet_ReleaseString(alias); - // } - //}; - - _getwch(); - - return 0; -} diff --git a/windows/winnet/src/extras/loader/loader.vcxproj b/windows/winnet/src/extras/loader/loader.vcxproj deleted file mode 100644 index 665d5046a3..0000000000 --- a/windows/winnet/src/extras/loader/loader.vcxproj +++ /dev/null @@ -1,188 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Debug|x64"> - <Configuration>Debug</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|x64"> - <Configuration>Release</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - </ItemGroup> - <ItemGroup> - <ClCompile Include="loader.cpp" /> - <ClCompile Include="stdafx.cpp" /> - </ItemGroup> - <ItemGroup> - <ClInclude Include="stdafx.h" /> - <ClInclude Include="targetver.h" /> - </ItemGroup> - <PropertyGroup Label="Globals"> - <VCProjectVersion>16.0</VCProjectVersion> - <ProjectGuid>{227C50F4-D9F6-4D9A-84A0-33CE84432D0D}</ProjectGuid> - <Keyword>Win32Proj</Keyword> - <RootNamespace>loader</RootNamespace> - <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v143</PlatformToolset> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>false</UseDebugLibraries> - <PlatformToolset>v143</PlatformToolset> - <WholeProgramOptimization>true</WholeProgramOptimization> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v143</PlatformToolset> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>false</UseDebugLibraries> - <PlatformToolset>v143</PlatformToolset> - <WholeProgramOptimization>true</WholeProgramOptimization> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - </ImportGroup> - <ImportGroup Label="Shared"> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <LinkIncremental>true</LinkIncremental> - <IntDir>$(SolutionDir)bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir> - <OutDir>$(SolutionDir)bin\$(Platform)-$(Configuration)\</OutDir> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <LinkIncremental>true</LinkIncremental> - <IntDir>$(SolutionDir)bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir> - <OutDir>$(SolutionDir)bin\$(Platform)-$(Configuration)\</OutDir> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <LinkIncremental>false</LinkIncremental> - <IntDir>$(SolutionDir)bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir> - <OutDir>$(SolutionDir)bin\$(Platform)-$(Configuration)\</OutDir> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <LinkIncremental>false</LinkIncremental> - <IntDir>$(SolutionDir)bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir> - <OutDir>$(SolutionDir)bin\$(Platform)-$(Configuration)\</OutDir> - </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <ClCompile> - <PrecompiledHeader>Create</PrecompiledHeader> - <WarningLevel>Level4</WarningLevel> - <Optimization>Disabled</Optimization> - <SDLCheck>true</SDLCheck> - <PreprocessorDefinitions>WINNET_STATIC;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <ConformanceMode>true</ConformanceMode> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - <AdditionalIncludeDirectories>$(ProjectDir)../../;$(ProjectDir)../../../../libshared/src/</AdditionalIncludeDirectories> - <LanguageStandard>stdcpplatest</LanguageStandard> - </ClCompile> - <Link> - <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories> - <AdditionalDependencies>libshared.lib;winnet.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> - </Link> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <ClCompile> - <PrecompiledHeader>Create</PrecompiledHeader> - <WarningLevel>Level4</WarningLevel> - <Optimization>Disabled</Optimization> - <SDLCheck>true</SDLCheck> - <PreprocessorDefinitions>WINNET_STATIC;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <ConformanceMode>true</ConformanceMode> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - <AdditionalIncludeDirectories>$(ProjectDir)../../;$(ProjectDir)../../../../libshared/src/</AdditionalIncludeDirectories> - <LanguageStandard>stdcpplatest</LanguageStandard> - </ClCompile> - <Link> - <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories> - <AdditionalDependencies>libshared.lib;winnet.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> - </Link> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <ClCompile> - <PrecompiledHeader>Create</PrecompiledHeader> - <WarningLevel>Level4</WarningLevel> - <Optimization>MaxSpeed</Optimization> - <FunctionLevelLinking>true</FunctionLevelLinking> - <IntrinsicFunctions>true</IntrinsicFunctions> - <SDLCheck>true</SDLCheck> - <PreprocessorDefinitions>WINNET_STATIC;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <ConformanceMode>true</ConformanceMode> - <RuntimeLibrary>MultiThreaded</RuntimeLibrary> - <AdditionalIncludeDirectories>$(ProjectDir)../../;$(ProjectDir)../../../../libshared/src/</AdditionalIncludeDirectories> - <LanguageStandard>stdcpplatest</LanguageStandard> - </ClCompile> - <Link> - <SubSystem>Console</SubSystem> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <OptimizeReferences>true</OptimizeReferences> - <GenerateDebugInformation>true</GenerateDebugInformation> - <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories> - <AdditionalDependencies>libshared.lib;winnet.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> - </Link> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <ClCompile> - <PrecompiledHeader>Create</PrecompiledHeader> - <WarningLevel>Level4</WarningLevel> - <Optimization>MaxSpeed</Optimization> - <FunctionLevelLinking>true</FunctionLevelLinking> - <IntrinsicFunctions>true</IntrinsicFunctions> - <SDLCheck>true</SDLCheck> - <PreprocessorDefinitions>WINNET_STATIC;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <ConformanceMode>true</ConformanceMode> - <RuntimeLibrary>MultiThreaded</RuntimeLibrary> - <AdditionalIncludeDirectories>$(ProjectDir)../../;$(ProjectDir)../../../../libshared/src/</AdditionalIncludeDirectories> - <LanguageStandard>stdcpplatest</LanguageStandard> - </ClCompile> - <Link> - <SubSystem>Console</SubSystem> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <OptimizeReferences>true</OptimizeReferences> - <GenerateDebugInformation>true</GenerateDebugInformation> - <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories> - <AdditionalDependencies>libshared.lib;winnet.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> - </Link> - </ItemDefinitionGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - </ImportGroup> -</Project>
\ No newline at end of file diff --git a/windows/winnet/src/extras/loader/loader.vcxproj.filters b/windows/winnet/src/extras/loader/loader.vcxproj.filters deleted file mode 100644 index cd0f4643c7..0000000000 --- a/windows/winnet/src/extras/loader/loader.vcxproj.filters +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup> - <ClCompile Include="loader.cpp" /> - <ClCompile Include="stdafx.cpp" /> - </ItemGroup> - <ItemGroup> - <ClInclude Include="stdafx.h" /> - <ClInclude Include="targetver.h" /> - </ItemGroup> -</Project>
\ No newline at end of file diff --git a/windows/winnet/src/extras/loader/stdafx.cpp b/windows/winnet/src/extras/loader/stdafx.cpp deleted file mode 100644 index 8d6fa45555..0000000000 --- a/windows/winnet/src/extras/loader/stdafx.cpp +++ /dev/null @@ -1,8 +0,0 @@ -// stdafx.cpp : source file that includes just the standard includes -// loader.pch will be the pre-compiled header -// stdafx.obj will contain the pre-compiled type information - -#include "stdafx.h" - -// TODO: reference any additional headers you need in STDAFX.H -// and not in this file diff --git a/windows/winnet/src/extras/loader/stdafx.h b/windows/winnet/src/extras/loader/stdafx.h deleted file mode 100644 index b005a839de..0000000000 --- a/windows/winnet/src/extras/loader/stdafx.h +++ /dev/null @@ -1,15 +0,0 @@ -// stdafx.h : include file for standard system include files, -// or project specific include files that are used frequently, but -// are changed infrequently -// - -#pragma once - -#include "targetver.h" - -#include <stdio.h> -#include <tchar.h> - - - -// TODO: reference additional headers your program requires here diff --git a/windows/winnet/src/extras/loader/targetver.h b/windows/winnet/src/extras/loader/targetver.h deleted file mode 100644 index 87c0086de7..0000000000 --- a/windows/winnet/src/extras/loader/targetver.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -// Including SDKDDKVer.h defines the highest available Windows platform. - -// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and -// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. - -#include <SDKDDKVer.h> diff --git a/windows/winnet/src/extras/tests/adaptermonitor.cpp b/windows/winnet/src/extras/tests/adaptermonitor.cpp deleted file mode 100644 index 465bb0b7cd..0000000000 --- a/windows/winnet/src/extras/tests/adaptermonitor.cpp +++ /dev/null @@ -1,798 +0,0 @@ -#include "stdafx.h" -#include "testadapterutil.h" -#include <libshared/logging/stdoutlogger.h> -#include <libshared/logging/logsinkadapter.h> -#include <iostream> -#include <CppUnitTest.h> - -using namespace Microsoft::VisualStudio::CppUnitTestFramework; - - -namespace -{ - -auto MakeStdoutLogger() -{ - return std::make_shared<shared::logging::LogSinkAdapter>(shared::logging::StdoutLogger, nullptr); -} - -enum class LastEvent -{ - NoEvent, - Add, - Delete, - Update -}; - -} - -namespace Microsoft::VisualStudio::CppUnitTestFramework -{ - -template<> -std::wstring ToString<LastEvent>(const enum class LastEvent& t) -{ - switch (t) - { - case LastEvent::NoEvent: - return L"LastEvent::NoEvent"; - case LastEvent::Add: - return L"LastEvent::Add"; - case LastEvent::Delete: - return L"LastEvent::Delete"; - case LastEvent::Update: - return L"LastEvent::Update"; - } - return L"LastEvent::<Unknown value>"; -} - -} - -TEST_CLASS(NetworkAdapterMonitorTests) -{ -public: - - TEST_METHOD(addAdapter) - { - auto logSink = MakeStdoutLogger(); - - const auto filter = [](const MIB_IF_ROW2 &) -> bool - { - return true; - }; - - const auto testProvider = std::make_shared<TestDataProvider>(); - size_t adapterCount = 0; - - NetworkAdapterMonitor inst( - logSink, - [&adapterCount](const std::vector<MIB_IF_ROW2> &adapters, const MIB_IF_ROW2 *, UpdateType) -> void - { - adapterCount = adapters.size(); - }, - filter, - testProvider - ); - - Assert::AreEqual( - size_t(0), - adapterCount, - L"Expected 0 adapters initially" - ); - - // - // Add new adapter - // - - constexpr size_t currentLuid = 100; - - MIB_IF_ROW2 adapter = { 0 }; - adapter.InterfaceLuid.Value = currentLuid; - adapter.AdminStatus = NET_IF_ADMIN_STATUS_UP; - - MIB_IPINTERFACE_ROW iface = { 0 }; - iface.InterfaceLuid.Value = currentLuid; - iface.Family = AF_INET; - - testProvider->addIpInterface(adapter, iface); - - testProvider->sendEvent(&iface, MibAddInstance); - - Assert::AreEqual( - size_t(1), - adapterCount, - L"Expected new adapter" - ); - } - - TEST_METHOD(addAdapter_Duplicate) - { - auto logSink = MakeStdoutLogger(); - - const auto filter = [](const MIB_IF_ROW2 &) -> bool - { - return true; - }; - - const auto testProvider = std::make_shared<TestDataProvider>(); - size_t adapterCount = 0; - - NetworkAdapterMonitor inst( - logSink, - [&adapterCount](const std::vector<MIB_IF_ROW2> &adapters, const MIB_IF_ROW2 *, UpdateType) -> void - { - adapterCount = adapters.size(); - }, - filter, - testProvider - ); - - // - // Create a fake adapter - // - - MIB_IF_ROW2 adapter = { 0 }; - adapter.InterfaceLuid.Value = 1; - adapter.AdminStatus = NET_IF_ADMIN_STATUS_UP; - - MIB_IPINTERFACE_ROW iface = { 0 }; - iface.InterfaceLuid.Value = 1; - iface.Family = AF_INET; - - testProvider->addIpInterface(adapter, iface); - - testProvider->sendEvent(&iface, MibAddInstance); - - Assert::AreEqual( - size_t(1), - adapterCount, - L"Expected new adapter" - ); - - // - // Register the same interface twice - // - testProvider->sendEvent(&iface, MibAddInstance); - - Assert::AreEqual( - size_t(1), - adapterCount, - L"Expected ignored duplicate interface event" - ); - } - - TEST_METHOD(removeAdapter_AdminStatus) - { - auto logSink = MakeStdoutLogger(); - - const auto filter = [](const MIB_IF_ROW2 &) -> bool - { - return true; - }; - - const auto testProvider = std::make_shared<TestDataProvider>(); - size_t adapterCount = 0; - - NetworkAdapterMonitor inst( - logSink, - [&adapterCount](const std::vector<MIB_IF_ROW2> &adapters, const MIB_IF_ROW2 *, UpdateType) -> void - { - adapterCount = adapters.size(); - }, - filter, - testProvider - ); - - // - // Create fake adapter - // - constexpr size_t luidValue = 1; - - MIB_IF_ROW2 adapter = { 0 }; - adapter.InterfaceLuid.Value = luidValue; - adapter.AdminStatus = NET_IF_ADMIN_STATUS_UP; - - MIB_IPINTERFACE_ROW iface = { 0 }; - iface.InterfaceLuid.Value = luidValue; - iface.Family = AF_INET; - - testProvider->addIpInterface(adapter, iface); - - testProvider->sendEvent(&iface, MibAddInstance); - - Assert::AreEqual( - luidValue, - adapterCount, - L"Expected new adapter" - ); - - // - // Delete adapter (AdminStatus) - // - adapter.AdminStatus = NET_IF_ADMIN_STATUS_DOWN; - testProvider->addAdapter(adapter); - - testProvider->sendEvent(&iface, MibDeleteInstance); - - testProvider->removeAdapter(adapter); - - Assert::AreEqual( - size_t(0), - adapterCount, - L"Expected removed adapter" - ); - } - - TEST_METHOD(removeAdapter_NoInterfaces) - { - auto logSink = MakeStdoutLogger(); - - const auto filter = [](const MIB_IF_ROW2 &) -> bool - { - return true; - }; - - const auto testProvider = std::make_shared<TestDataProvider>(); - size_t adapterCount = 0; - - NetworkAdapterMonitor inst( - logSink, - [&adapterCount](const std::vector<MIB_IF_ROW2> &adapters, const MIB_IF_ROW2 *, UpdateType) -> void - { - adapterCount = adapters.size(); - }, - filter, - testProvider - ); - - // - // Create fake adapter - // - - constexpr size_t luidValue = 1; - - MIB_IF_ROW2 adapter = { 0 }; - adapter.InterfaceLuid.Value = luidValue; - adapter.AdminStatus = NET_IF_ADMIN_STATUS_UP; - - MIB_IPINTERFACE_ROW iface = { 0 }; - iface.InterfaceLuid.Value = luidValue; - iface.Family = AF_INET; - - testProvider->addIpInterface(adapter, iface); - - testProvider->sendEvent(&iface, MibAddInstance); - - Assert::AreEqual( - luidValue, - adapterCount, - L"Expected new adapter (with IPv4 interface)" - ); - - // - // Delete IP interfaces on the adapter (should report Delete) - // - - testProvider->removeIpInterface(iface); - testProvider->sendEvent(&iface, MibDeleteInstance); - - adapter = { 0 }; - adapter.InterfaceLuid.Value = luidValue; - testProvider->removeAdapter(adapter); - - Assert::AreEqual( - size_t(0), - adapterCount, - L"Expected removed adapter" - ); - } - - TEST_METHOD(removeAdapter_Duplicate) - { - auto logSink = MakeStdoutLogger(); - - const auto filter = [](const MIB_IF_ROW2 &) -> bool - { - return true; - }; - - const auto testProvider = std::make_shared<TestDataProvider>(); - size_t adapterCount = 0; - - NetworkAdapterMonitor inst( - logSink, - [&adapterCount](const std::vector<MIB_IF_ROW2> &adapters, const MIB_IF_ROW2 *, UpdateType) -> void - { - adapterCount = adapters.size(); - }, - filter, - testProvider - ); - - // - // Create fake adapter - // - MIB_IF_ROW2 adapter = { 0 }; - adapter.InterfaceLuid.Value = 1; - adapter.AdminStatus = NET_IF_ADMIN_STATUS_UP; - - MIB_IPINTERFACE_ROW iface = { 0 }; - iface.InterfaceLuid.Value = 1; - iface.Family = AF_INET; - - testProvider->addIpInterface(adapter, iface); - - testProvider->sendEvent(&iface, MibAddInstance); - - Assert::AreEqual( - size_t(1), - adapterCount, - L"Expected new adapter" - ); - - // - // Duplicate deletion events - // - - adapter.AdminStatus = NET_IF_ADMIN_STATUS_DOWN; - testProvider->addAdapter(adapter); // update status - - testProvider->removeIpInterface(iface); - - testProvider->sendEvent(&iface, MibDeleteInstance); - testProvider->sendEvent(&iface, MibDeleteInstance); - testProvider->sendEvent(&iface, MibDeleteInstance); - testProvider->sendEvent(&iface, MibDeleteInstance); - - Assert::AreEqual( - size_t(0), - adapterCount, - L"State inconsistent after duplicate Delete event" - ); - } - - TEST_METHOD(addIPv6Interface) - { - auto logSink = MakeStdoutLogger(); - - const auto filter = [](const MIB_IF_ROW2 &) -> bool - { - return true; - }; - - const auto testProvider = std::make_shared<TestDataProvider>(); - size_t adapterCount = 0; - - NetworkAdapterMonitor inst( - logSink, - [&adapterCount](const std::vector<MIB_IF_ROW2> &adapters, const MIB_IF_ROW2 *, UpdateType) -> void - { - adapterCount = adapters.size(); - }, - filter, - testProvider - ); - - // - // Add IPv6 interface - // - - constexpr size_t currentLuid = 100; - - MIB_IF_ROW2 adapter = { 0 }; - adapter.InterfaceLuid.Value = currentLuid; - adapter.AdminStatus = NET_IF_ADMIN_STATUS_UP; - - MIB_IPINTERFACE_ROW iface = { 0 }; - iface.InterfaceLuid.Value = currentLuid; - iface.Family = AF_INET6; - - testProvider->addIpInterface(adapter, iface); - - testProvider->sendEvent(&iface, MibAddInstance); - - Assert::AreEqual( - size_t(1), - adapterCount, - L"Expected new adapter" - ); - } - - TEST_METHOD(addIPv4And6Interface) - { - auto logSink = MakeStdoutLogger(); - - const auto filter = [](const MIB_IF_ROW2 &) -> bool - { - return true; - }; - - const auto testProvider = std::make_shared<TestDataProvider>(); - size_t adapterCount = 0; - - NetworkAdapterMonitor inst( - logSink, - [&adapterCount](const std::vector<MIB_IF_ROW2> &adapters, const MIB_IF_ROW2 *, UpdateType) -> void - { - adapterCount = adapters.size(); - }, - filter, - testProvider - ); - - constexpr size_t currentLuid = 1; - constexpr size_t expectedCount = 1; - - MIB_IF_ROW2 adapter = { 0 }; - adapter.InterfaceLuid.Value = currentLuid; - adapter.AdminStatus = NET_IF_ADMIN_STATUS_UP; - - // - // Add IPv4 interface - // - - MIB_IPINTERFACE_ROW iface4 = { 0 }; - iface4.InterfaceLuid.Value = currentLuid; - iface4.Family = AF_INET; - testProvider->addIpInterface(adapter, iface4); - testProvider->sendEvent(&iface4, MibAddInstance); - - // - // Add IPv6 interface - // - - MIB_IPINTERFACE_ROW iface6 = { 0 }; - iface6.InterfaceLuid.Value = currentLuid; - iface6.Family = AF_INET6; - testProvider->addIpInterface(adapter, iface6); - testProvider->sendEvent(&iface6, MibAddInstance); - - Assert::AreEqual( - expectedCount, - adapterCount, - L"Expected single adapter with two IP interfaces" - ); - } - - TEST_METHOD(addIPv4And6Interface_RemoveIPv4) - { - auto logSink = MakeStdoutLogger(); - - const auto filter = [](const MIB_IF_ROW2 &) -> bool - { - return true; - }; - - const auto testProvider = std::make_shared<TestDataProvider>(); - size_t adapterCount = 0; - - NetworkAdapterMonitor inst( - logSink, - [&adapterCount](const std::vector<MIB_IF_ROW2> &adapters, const MIB_IF_ROW2 *, UpdateType) -> void - { - adapterCount = adapters.size(); - }, - filter, - testProvider - ); - - constexpr size_t currentLuid = 1; - - MIB_IF_ROW2 adapter = { 0 }; - adapter.InterfaceLuid.Value = currentLuid; - adapter.AdminStatus = NET_IF_ADMIN_STATUS_UP; - - // - // Add IPv4 interface - // - - MIB_IPINTERFACE_ROW iface4 = { 0 }; - iface4.InterfaceLuid.Value = currentLuid; - iface4.Family = AF_INET; - testProvider->addIpInterface(adapter, iface4); - testProvider->sendEvent(&iface4, MibAddInstance); - - // - // Add IPv6 interface - // - - MIB_IPINTERFACE_ROW iface6 = { 0 }; - iface6.InterfaceLuid.Value = currentLuid; - iface6.Family = AF_INET6; - testProvider->addIpInterface(adapter, iface6); - testProvider->sendEvent(&iface6, MibAddInstance); - - // - // Remove IPv4 interface - // - testProvider->removeIpInterface(iface4); - testProvider->sendEvent(&iface4, MibDeleteInstance); - - constexpr size_t expectedCount = 1; - - Assert::AreEqual( - expectedCount, - adapterCount, - L"Expected single adapter (with IPv6 interface)" - ); - } - - TEST_METHOD(addIPv4And6Interface_RemoveIPv6) - { - auto logSink = MakeStdoutLogger(); - - const auto filter = [](const MIB_IF_ROW2 &) -> bool - { - return true; - }; - - const auto testProvider = std::make_shared<TestDataProvider>(); - size_t adapterCount = 0; - - NetworkAdapterMonitor inst( - logSink, - [&adapterCount](const std::vector<MIB_IF_ROW2> &adapters, const MIB_IF_ROW2 *, UpdateType) -> void - { - adapterCount = adapters.size(); - }, - filter, - testProvider - ); - - constexpr size_t currentLuid = 1; - - MIB_IF_ROW2 adapter = { 0 }; - adapter.InterfaceLuid.Value = currentLuid; - adapter.AdminStatus = NET_IF_ADMIN_STATUS_UP; - - // - // Add IPv4 interface - // - - MIB_IPINTERFACE_ROW iface4 = { 0 }; - iface4.InterfaceLuid.Value = currentLuid; - iface4.Family = AF_INET; - testProvider->addIpInterface(adapter, iface4); - testProvider->sendEvent(&iface4, MibAddInstance); - - // - // Add IPv6 interface - // - - MIB_IPINTERFACE_ROW iface6 = { 0 }; - iface6.InterfaceLuid.Value = currentLuid; - iface6.Family = AF_INET6; - testProvider->addIpInterface(adapter, iface6); - testProvider->sendEvent(&iface6, MibAddInstance); - - // - // Remove IPv6 interface - // - testProvider->removeIpInterface(iface6); - testProvider->sendEvent(&iface6, MibDeleteInstance); - - constexpr size_t expectedCount = 1; - - Assert::AreEqual( - expectedCount, - adapterCount, - L"Expected single adapter (with IPv4 interface)" - ); - } - - TEST_METHOD(addIPv4And6Interface_RemoveBoth) - { - auto logSink = MakeStdoutLogger(); - - const auto filter = [](const MIB_IF_ROW2 &) -> bool - { - return true; - }; - - const auto testProvider = std::make_shared<TestDataProvider>(); - size_t adapterCount = 0; - - NetworkAdapterMonitor inst( - logSink, - [&adapterCount](const std::vector<MIB_IF_ROW2> &adapters, const MIB_IF_ROW2 *, UpdateType) -> void - { - adapterCount = adapters.size(); - }, - filter, - testProvider - ); - - constexpr size_t currentLuid = 1; - - MIB_IF_ROW2 adapter = { 0 }; - adapter.InterfaceLuid.Value = currentLuid; - adapter.AdminStatus = NET_IF_ADMIN_STATUS_UP; - - // - // Add IPv4 interface - // - - MIB_IPINTERFACE_ROW iface4 = { 0 }; - iface4.InterfaceLuid.Value = currentLuid; - iface4.Family = AF_INET; - testProvider->addIpInterface(adapter, iface4); - testProvider->sendEvent(&iface4, MibAddInstance); - - // - // Add IPv6 interface - // - - MIB_IPINTERFACE_ROW iface6 = { 0 }; - iface6.InterfaceLuid.Value = currentLuid; - iface6.Family = AF_INET6; - testProvider->addIpInterface(adapter, iface6); - testProvider->sendEvent(&iface6, MibAddInstance); - - // - // Remove IP interfaces - // - testProvider->removeIpInterface(iface4); - testProvider->sendEvent(&iface4, MibDeleteInstance); - testProvider->removeIpInterface(iface6); - testProvider->sendEvent(&iface6, MibDeleteInstance); - - constexpr size_t expectedCount = 0; - - Assert::AreEqual( - expectedCount, - adapterCount, - L"Expected no adapter (0 IP interfaces)" - ); - } - - TEST_METHOD(filter) - { - auto logSink = MakeStdoutLogger(); - - const auto testProvider = std::make_shared<TestDataProvider>(); - - // - // Exclude adapters not connected to the internet, - // loopback devices, and software adapters - // - - const auto filter = [](const MIB_IF_ROW2 &row) -> bool - { - switch (row.InterfaceLuid.Info.IfType) - { - case IF_TYPE_SOFTWARE_LOOPBACK: - { - return false; - } - } - - if (FALSE == row.InterfaceAndOperStatusFlags.HardwareInterface) - { - return false; - } - return IfOperStatusUp == row.OperStatus - && MediaConnectStateConnected == row.MediaConnectState; - }; - - size_t adapterCount = 0; - LastEvent lastEvent = LastEvent::NoEvent; - - NetworkAdapterMonitor inst( - logSink, - [&adapterCount, &lastEvent](const std::vector<MIB_IF_ROW2> &adapters, const MIB_IF_ROW2 *, UpdateType updateType) -> void - { - switch (updateType) - { - case UpdateType::Add: - lastEvent = LastEvent::Add; - break; - case UpdateType::Delete: - lastEvent = LastEvent::Delete; - break; - case UpdateType::Update: - lastEvent = LastEvent::Update; - break; - default: - Assert::Fail(L"Unhandled update type"); - } - - adapterCount = adapters.size(); - }, - filter, - testProvider - ); - - // - // Our filter should ignore loopback devices - // - - constexpr size_t loopbackLuid = 1; - - MIB_IF_ROW2 adapter = { 0 }; - adapter.AdminStatus = NET_IF_ADMIN_STATUS_UP; - adapter.InterfaceLuid.Value = loopbackLuid; - adapter.InterfaceLuid.Info.IfType = IF_TYPE_SOFTWARE_LOOPBACK; - adapter.MediaConnectState = MediaConnectStateConnected; - adapter.InterfaceAndOperStatusFlags.HardwareInterface = TRUE; - adapter.OperStatus = IfOperStatusUp; - - MIB_IPINTERFACE_ROW iface4 = { 0 }; - iface4.InterfaceLuid.Value = loopbackLuid; - iface4.Family = AF_INET; - - lastEvent = LastEvent::NoEvent; - - testProvider->addIpInterface(adapter, iface4); - testProvider->sendEvent(&iface4, MibAddInstance); - - Assert::AreEqual(LastEvent::NoEvent, lastEvent, L"Unexpectedly received event for loopback adapter"); - - Assert::AreEqual( - size_t(0), - adapterCount, - L"Loopback adapter was not filtered correctly" - ); - - testProvider->removeIpInterface(iface4); - testProvider->sendEvent(&iface4, MibDeleteInstance); - testProvider->removeAdapter(adapter); - - Assert::AreEqual(LastEvent::NoEvent, lastEvent, L"Unexpectedly received event for loopback adapter"); - - // - // Our filter should ignore devices not connected to the internet - // - - constexpr size_t disconnectedLuid = 2; - - adapter = { 0 }; - adapter.AdminStatus = NET_IF_ADMIN_STATUS_UP; - adapter.InterfaceLuid.Value = disconnectedLuid; - adapter.MediaConnectState = MediaConnectStateDisconnected; - adapter.InterfaceAndOperStatusFlags.HardwareInterface = TRUE; - adapter.OperStatus = IfOperStatusUp; - - iface4 = { 0 }; - iface4.InterfaceLuid.Value = disconnectedLuid; - iface4.Family = AF_INET; - testProvider->addIpInterface(adapter, iface4); - testProvider->sendEvent(&iface4, MibAddInstance); - - Assert::AreEqual(LastEvent::NoEvent, lastEvent, L"Unexpectedly received event for disconnected adapter"); - - testProvider->removeIpInterface(iface4); - testProvider->sendEvent(&iface4, MibDeleteInstance); - testProvider->removeAdapter(adapter); - - Assert::AreEqual(LastEvent::NoEvent, lastEvent, L"Unexpectedly received event for disconnected adapter"); - - // - // Report events for hardware devices - // - - constexpr size_t onlineHardwareLuid = 3; - - adapter = { 0 }; - adapter.AdminStatus = NET_IF_ADMIN_STATUS_UP; - adapter.InterfaceLuid.Value = onlineHardwareLuid; - adapter.MediaConnectState = MediaConnectStateConnected; - adapter.InterfaceAndOperStatusFlags.HardwareInterface = TRUE; - adapter.OperStatus = IfOperStatusUp; - - iface4 = { 0 }; - iface4.InterfaceLuid.Value = onlineHardwareLuid; - iface4.Family = AF_INET; - testProvider->addIpInterface(adapter, iface4); - testProvider->sendEvent(&iface4, MibAddInstance); - - Assert::AreEqual(LastEvent::Add, lastEvent, L"Expected event for connected adapter was not received"); - - lastEvent = LastEvent::NoEvent; - - testProvider->removeIpInterface(iface4); - testProvider->sendEvent(&iface4, MibDeleteInstance); - testProvider->removeAdapter(adapter); - - Assert::AreEqual(LastEvent::Delete, lastEvent, L"Expected event for connected adapter was not received"); - } -}; diff --git a/windows/winnet/src/extras/tests/networkadaptermonitor.vcxproj.filters b/windows/winnet/src/extras/tests/networkadaptermonitor.vcxproj.filters deleted file mode 100644 index 08573b397e..0000000000 --- a/windows/winnet/src/extras/tests/networkadaptermonitor.vcxproj.filters +++ /dev/null @@ -1,42 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup> - <Filter Include="Source Files"> - <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> - <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> - </Filter> - <Filter Include="Header Files"> - <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> - <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions> - </Filter> - <Filter Include="Resource Files"> - <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> - <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> - </Filter> - </ItemGroup> - <ItemGroup> - <ClInclude Include="stdafx.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="targetver.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="testadapterutil.h"> - <Filter>Header Files</Filter> - </ClInclude> - </ItemGroup> - <ItemGroup> - <ClCompile Include="stdafx.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="adaptermonitor.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="offlinemonitor.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="testadapterutil.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - </ItemGroup> -</Project>
\ No newline at end of file diff --git a/windows/winnet/src/extras/tests/stdafx.cpp b/windows/winnet/src/extras/tests/stdafx.cpp deleted file mode 100644 index c5bc6dac72..0000000000 --- a/windows/winnet/src/extras/tests/stdafx.cpp +++ /dev/null @@ -1,8 +0,0 @@ -// stdafx.cpp : source file that includes just the standard includes -// networkadaptermonitor.pch will be the pre-compiled header -// stdafx.obj will contain the pre-compiled type information - -#include "stdafx.h" - -// TODO: reference any additional headers you need in STDAFX.H -// and not in this file diff --git a/windows/winnet/src/extras/tests/stdafx.h b/windows/winnet/src/extras/tests/stdafx.h deleted file mode 100644 index 6505f3e020..0000000000 --- a/windows/winnet/src/extras/tests/stdafx.h +++ /dev/null @@ -1,13 +0,0 @@ -// stdafx.h : include file for standard system include files, -// or project specific include files that are used frequently, but -// are changed infrequently -// - -#pragma once - -#include "targetver.h" - -// Headers for CppUnitTest -#include <CppUnitTest.h> - -// TODO: reference additional headers your program requires here diff --git a/windows/winnet/src/extras/tests/targetver.h b/windows/winnet/src/extras/tests/targetver.h deleted file mode 100644 index 87c0086de7..0000000000 --- a/windows/winnet/src/extras/tests/targetver.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -// Including SDKDDKVer.h defines the highest available Windows platform. - -// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and -// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. - -#include <SDKDDKVer.h> diff --git a/windows/winnet/src/extras/tests/testadapterutil.cpp b/windows/winnet/src/extras/tests/testadapterutil.cpp deleted file mode 100644 index 128cbee681..0000000000 --- a/windows/winnet/src/extras/tests/testadapterutil.cpp +++ /dev/null @@ -1,252 +0,0 @@ -#include "stdafx.h" -#include "testadapterutil.h" -#include <CppUnitTest.h> - -using namespace Microsoft::VisualStudio::CppUnitTestFramework; - -#include <libcommon/logging/logsink.h> -#include <winnet/networkadaptermonitor.h> - -using FilterType = NetworkAdapterMonitor::FilterType; -using UpdateSinkType = NetworkAdapterMonitor::UpdateSinkType; -using UpdateType = NetworkAdapterMonitor::UpdateType; - -// -// MibIfTable -// - -#include <algorithm> - -void MibIfTable::add(const MIB_IF_ROW2 &row) -{ - const auto it = std::find_if( - m_table.begin(), - m_table.end(), - [&row](const MIB_IF_ROW2 &elem) - { - return elem.InterfaceLuid.Value == row.InterfaceLuid.Value; - } - ); - - if (m_table.end() == it) - { - m_table.push_back(row); - } - else - { - *it = row; - } -} - -void MibIfTable::remove(const MIB_IF_ROW2 &row) -{ - const auto it = std::find_if( - m_table.begin(), - m_table.end(), - [&row](const MIB_IF_ROW2 &elem) - { - return elem.InterfaceLuid.Value == row.InterfaceLuid.Value; - } - ); - - if (m_table.end() != it) - { - m_table.erase(it); - } -} - - -// -// TestDataProvider -// - -DWORD TestDataProvider::notifyIpInterfaceChange( - ADDRESS_FAMILY, - PIPINTERFACE_CHANGE_CALLBACK Callback, - PVOID CallerContext, - BOOLEAN, - HANDLE * -) -{ - // TODO: assert: m_callback == nullptr - // TODO: multiple callbacks? - m_callback = Callback; - m_context = CallerContext; - - return NO_ERROR; -} - -DWORD TestDataProvider::cancelMibChangeNotify2(HANDLE) -{ - // TODO: assert: m_callback != nullptr - // TODO: multiple callbacks? - m_callback = nullptr; - m_context = nullptr; - - return NO_ERROR; -} - -DWORD TestDataProvider::getIfTable2(PMIB_IF_TABLE2 *tableOut) -{ - MIB_IF_TABLE2 *tableCopy = reinterpret_cast<MIB_IF_TABLE2*>(new uint8_t[ - sizeof(MIB_IF_TABLE2) - + sizeof(MIB_IF_ROW2) * m_adapterTable.entries().size() - ]); - - tableCopy->NumEntries = static_cast<ULONG>(m_adapterTable.entries().size()); - - std::copy( - m_adapterTable.entries().begin(), - m_adapterTable.entries().end(), - static_cast<MIB_IF_ROW2*>(tableCopy->Table) - ); - - *tableOut = tableCopy; - return NO_ERROR; -} - -void TestDataProvider::freeMibTable(PVOID Memory) -{ - delete[] reinterpret_cast<uint8_t*>(Memory); -} - -DWORD TestDataProvider::getIfEntry2(PMIB_IF_ROW2 Row) -{ - // TODO: accept InterfaceIndex as well - // FIXME: should ERROR_INVALID_PARAMETER be returned if LUID = 0? - - if (nullptr == Row) - { - return ERROR_INVALID_PARAMETER; - } - - const auto it = std::find_if( - m_adapterTable.entries().begin(), - m_adapterTable.entries().end(), - [&Row](const MIB_IF_ROW2 &entry) - { - return entry.InterfaceLuid.Value == Row->InterfaceLuid.Value; - } - ); - - if (m_adapterTable.entries().end() == it) - { - return ERROR_FILE_NOT_FOUND; - } - - *Row = *it; - - return NO_ERROR; -} - -DWORD TestDataProvider::getIpInterfaceEntry(PMIB_IPINTERFACE_ROW Row) -{ - // TODO: accept InterfaceIndex as well - // FIXME: should ERROR_INVALID_PARAMETER be returned if LUID = 0? - - if (nullptr == Row) - { - return ERROR_INVALID_PARAMETER; - } - if (AF_INET != Row->Family && AF_INET6 != Row->Family) - { - return ERROR_INVALID_PARAMETER; - } - - bool foundMatchingLuid = false; - - for (const auto &candidate : m_ipInterfaces) - { - if (candidate.InterfaceLuid.Value != Row->InterfaceLuid.Value) - { - continue; - } - - foundMatchingLuid = true; - - if (Row->Family == candidate.Family) - { - *Row = candidate; - return NO_ERROR; - } - } - - if (foundMatchingLuid) - { - // "the Row parameter does not match the IP address family - // specified in the Family member in the - // MIB_IPINTERFACE_ROW structure." - return ERROR_NOT_FOUND; - } - - // - // The LUID is also valid if it exists among adapters - // without an IP interface - // - const auto it = std::find_if( - m_adapterTable.entries().begin(), - m_adapterTable.entries().end(), - [Row](const MIB_IF_ROW2 &elem) - { - return Row->InterfaceLuid.Value == elem.InterfaceLuid.Value; - } - ); - - if (m_adapterTable.entries().end() != it) - { - return ERROR_NOT_FOUND; - } - - return ERROR_FILE_NOT_FOUND; -} - -void TestDataProvider::addAdapter(const MIB_IF_ROW2& adapter) -{ - m_adapterTable.add(adapter); -} - -void TestDataProvider::addIpInterface(const MIB_IF_ROW2& adapter, const MIB_IPINTERFACE_ROW& iface) -{ - addAdapter(adapter); - m_ipInterfaces.push_back(iface); -} - -void TestDataProvider::removeAdapter(const MIB_IF_ROW2& adapter) -{ - for (auto it = m_ipInterfaces.begin(); m_ipInterfaces.end() != it; ) - { - if (it->InterfaceLuid.Value == adapter.InterfaceLuid.Value) - { - it = m_ipInterfaces.erase(it); - } - else - { - ++it; - } - } - - m_adapterTable.remove(adapter); -} - -void TestDataProvider::removeIpInterface(const MIB_IPINTERFACE_ROW& iface) -{ - const auto it = std::find_if( - m_ipInterfaces.begin(), - m_ipInterfaces.end(), - [&iface](const MIB_IPINTERFACE_ROW &elem) - { - return iface.InterfaceLuid.Value == elem.InterfaceLuid.Value - && iface.Family == elem.Family; - } - ); - - if (m_ipInterfaces.end() != it) - { - m_ipInterfaces.erase(it); - } -} - -void TestDataProvider::sendEvent(MIB_IPINTERFACE_ROW *hint, MIB_NOTIFICATION_TYPE updateType) -{ - m_callback(m_context, hint, updateType); -} diff --git a/windows/winnet/src/extras/tests/testadapterutil.h b/windows/winnet/src/extras/tests/testadapterutil.h deleted file mode 100644 index 5ae8cc32c2..0000000000 --- a/windows/winnet/src/extras/tests/testadapterutil.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include <functional> -#include <winSock2.h> -#include <winnet/networkadaptermonitor.h> - -using FilterType = NetworkAdapterMonitor::FilterType; -using UpdateSinkType = NetworkAdapterMonitor::UpdateSinkType; -using UpdateType = NetworkAdapterMonitor::UpdateType; - - -class MibIfTable -{ - std::vector<MIB_IF_ROW2> m_table; - -public: - - void add(const MIB_IF_ROW2 &row); - void remove(const MIB_IF_ROW2 &row); - - const std::vector<MIB_IF_ROW2>& entries() const - { - return m_table; - } -}; - -class TestDataProvider : public NetworkAdapterMonitor::IDataProvider -{ - - PIPINTERFACE_CHANGE_CALLBACK m_callback; - void *m_context; - - MibIfTable m_adapterTable; - std::vector<MIB_IPINTERFACE_ROW> m_ipInterfaces; - -public: - - TestDataProvider() : m_callback(nullptr), m_context(nullptr) - { - } - - TestDataProvider(const TestDataProvider&) = delete; - TestDataProvider(TestDataProvider&&) = delete; - TestDataProvider& operator=(const TestDataProvider&) = delete; - TestDataProvider& operator=(const TestDataProvider&&) = delete; - - DWORD notifyIpInterfaceChange( - ADDRESS_FAMILY Family, - PIPINTERFACE_CHANGE_CALLBACK Callback, - PVOID CallerContext, - BOOLEAN InitialNotification, - HANDLE *NotificationHandle - ) override; - DWORD cancelMibChangeNotify2(HANDLE NotificationHandle) override; - - DWORD getIfTable2(PMIB_IF_TABLE2 *Table) override; - void freeMibTable(PVOID Memory) override; - - DWORD getIfEntry2(PMIB_IF_ROW2 Row) override; - DWORD getIpInterfaceEntry(PMIB_IPINTERFACE_ROW Row) override; - - // - // Test utilities - // - - void addAdapter(const MIB_IF_ROW2 &adapter); - void addIpInterface(const MIB_IF_ROW2 &adapter, const MIB_IPINTERFACE_ROW &iface); - - void removeAdapter(const MIB_IF_ROW2 &adapter); - void removeIpInterface(const MIB_IPINTERFACE_ROW &iface); - - void sendEvent(MIB_IPINTERFACE_ROW *hint, MIB_NOTIFICATION_TYPE updateType); -}; diff --git a/windows/winnet/src/extras/tests/tests.vcxproj b/windows/winnet/src/extras/tests/tests.vcxproj deleted file mode 100644 index b131033d53..0000000000 --- a/windows/winnet/src/extras/tests/tests.vcxproj +++ /dev/null @@ -1,109 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Debug|x64"> - <Configuration>Debug</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - </ItemGroup> - <PropertyGroup Label="Globals"> - <VCProjectVersion>16.0</VCProjectVersion> - <ProjectGuid>{01A4E766-CC61-40B7-A3D6-7A37F6BF5CCB}</ProjectGuid> - <Keyword>Win32Proj</Keyword> - <RootNamespace>tests</RootNamespace> - <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> - <ProjectSubType>NativeUnitTestProject</ProjectSubType> - <ProjectName>tests</ProjectName> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>DynamicLibrary</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v143</PlatformToolset> - <CharacterSet>Unicode</CharacterSet> - <UseOfMfc>false</UseOfMfc> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> - <ConfigurationType>DynamicLibrary</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v143</PlatformToolset> - <CharacterSet>Unicode</CharacterSet> - <UseOfMfc>false</UseOfMfc> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - </ImportGroup> - <ImportGroup Label="Shared"> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <LinkIncremental>true</LinkIncremental> - <OutDir>$(SolutionDir)bin\$(Platform)-$(Configuration)\</OutDir> - <IntDir>$(SolutionDir)bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <LinkIncremental>true</LinkIncremental> - <OutDir>$(SolutionDir)bin\$(Platform)-$(Configuration)\</OutDir> - <IntDir>$(SolutionDir)bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir> - </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <ClCompile> - <PrecompiledHeader>Use</PrecompiledHeader> - <WarningLevel>Level4</WarningLevel> - <Optimization>Disabled</Optimization> - <AdditionalIncludeDirectories>$(ProjectDir)../../;$(ProjectDir)../../../../libshared/src;$(ProjectDir)../../../../windows-libraries/src;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <UseFullPaths>true</UseFullPaths> - <LanguageStandard>stdcpplatest</LanguageStandard> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - </ClCompile> - <Link> - <SubSystem>Windows</SubSystem> - <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration);$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <AdditionalDependencies>libshared.lib;libcommon.lib;winnet-static.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> - </Link> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <ClCompile> - <PrecompiledHeader>Use</PrecompiledHeader> - <WarningLevel>Level4</WarningLevel> - <Optimization>Disabled</Optimization> - <AdditionalIncludeDirectories>$(ProjectDir)../../;$(ProjectDir)../../../../libshared/src;$(ProjectDir)../../../../windows-libraries/src;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>WIN32;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <UseFullPaths>true</UseFullPaths> - <LanguageStandard>stdcpplatest</LanguageStandard> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - </ClCompile> - <Link> - <SubSystem>Windows</SubSystem> - <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration);$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <AdditionalDependencies>libshared.lib;libcommon.lib;winnet-static.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> - </Link> - </ItemDefinitionGroup> - <ItemGroup> - <ClInclude Include="stdafx.h" /> - <ClInclude Include="targetver.h" /> - <ClInclude Include="testadapterutil.h" /> - </ItemGroup> - <ItemGroup> - <ClCompile Include="stdafx.cpp"> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader> - </ClCompile> - <ClCompile Include="adaptermonitor.cpp" /> - <ClCompile Include="testadapterutil.cpp" /> - </ItemGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - </ImportGroup> -</Project>
\ No newline at end of file diff --git a/windows/winnet/src/winnet/converters.cpp b/windows/winnet/src/winnet/converters.cpp deleted file mode 100644 index c09ae6339d..0000000000 --- a/windows/winnet/src/winnet/converters.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include <stdafx.h> -#include "converters.h" -#include <libcommon/error.h> -#include <cstdint> - -using namespace winnet::routing; - -namespace -{ - -SOCKADDR_INET IpToNative(const WINNET_IP &from) -{ - SOCKADDR_INET to = { 0 }; - - switch (from.family) - { - case WINNET_ADDR_FAMILY_IPV4: - { - to.Ipv4.sin_family = AF_INET; - to.Ipv4.sin_addr.s_addr = *reinterpret_cast<const uint32_t*>(from.bytes); - - break; - } - case WINNET_ADDR_FAMILY_IPV6: - { - to.Ipv6.sin6_family = AF_INET6; - memcpy(to.Ipv6.sin6_addr.u.Byte, from.bytes, 16); - - break; - } - default: - { - THROW_ERROR("Invalid network address family"); - } - } - - return to; -} - -WINNET_IP IpFromNative(const SOCKADDR_INET &from) -{ - WINNET_IP to = { 0 }; - - switch (from.si_family) - { - case AF_INET: - { - *reinterpret_cast<uint32_t*>(to.bytes) = static_cast<uint32_t>(from.Ipv4.sin_addr.s_addr); - to.family = WINNET_ADDR_FAMILY_IPV4; - break; - } - case AF_INET6: - { - memcpy(to.bytes, from.Ipv6.sin6_addr.u.Byte, 16); - to.family = WINNET_ADDR_FAMILY_IPV6; - break; - } - default: - { - THROW_ERROR("Invalid network address family"); - } - } - - return to; -} - -} // anonymous namespace - -namespace winnet -{ - -Network ConvertNetwork(const WINNET_IP_NETWORK &in) -{ - // - // Convert WINNET_IPNETWORK into Network aka IP_ADDRESS_PREFIX - // - - Network out = { 0 }; - - out.PrefixLength = in.prefix; - out.Prefix = IpToNative(in.addr); - - return out; -} - -std::optional<Node> ConvertNode(const WINNET_NODE *in) -{ - if (nullptr == in) - { - return std::nullopt; - } - - if (nullptr == in->deviceName && nullptr == in->gateway) - { - THROW_ERROR("Invalid 'WINNET_NODE' definition"); - } - - std::optional<std::wstring> deviceName; - std::optional<NodeAddress> gateway; - - if (nullptr != in->deviceName) - { - deviceName = in->deviceName; - } - - if (nullptr != in->gateway) - { - gateway = IpToNative(*in->gateway); - } - - return Node(deviceName, gateway); -} - -std::vector<Route> ConvertRoutes(const WINNET_ROUTE *routes, uint32_t numRoutes) -{ - std::vector<Route> out; - - out.reserve(numRoutes); - - for (size_t i = 0; i < numRoutes; ++i) - { - out.emplace_back(Route - { - ConvertNetwork(routes[i].network), - ConvertNode(routes[i].node) - }); - } - - return out; -} - -std::vector<WINNET_IP> ConvertNativeAddresses(const SOCKADDR_INET *addresses, uint32_t numAddresses) -{ - std::vector<WINNET_IP> out; - out.reserve(numAddresses); - - for (uint32_t i = 0; i < numAddresses; ++i) - { - out.emplace_back(IpFromNative(addresses[i])); - } - - return out; -} - -} diff --git a/windows/winnet/src/winnet/converters.h b/windows/winnet/src/winnet/converters.h deleted file mode 100644 index a608958cee..0000000000 --- a/windows/winnet/src/winnet/converters.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "winnet.h" -#include "routing/types.h" -#include <optional> -#include <vector> - -namespace winnet -{ - -routing::Network ConvertNetwork(const WINNET_IP_NETWORK &in); -std::optional<routing::Node> ConvertNode(const WINNET_NODE *in); -std::vector<routing::Route> ConvertRoutes(const WINNET_ROUTE *routes, uint32_t numRoutes); -std::vector<WINNET_IP> ConvertNativeAddresses(const SOCKADDR_INET *addresses, uint32_t numAddresses); - -} diff --git a/windows/winnet/src/winnet/dllmain.cpp b/windows/winnet/src/winnet/dllmain.cpp deleted file mode 100644 index e66eb0495d..0000000000 --- a/windows/winnet/src/winnet/dllmain.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "stdafx.h"
-#include <windows.h>
-
-BOOL APIENTRY DllMain(HMODULE, DWORD, LPVOID)
-{
- //
- // Avoid doing work in DllMain since the loader lock is held
- //
-
- return TRUE;
-}
diff --git a/windows/winnet/src/winnet/routing/defaultroutemonitor.cpp b/windows/winnet/src/winnet/routing/defaultroutemonitor.cpp deleted file mode 100644 index 8e1e2599ad..0000000000 --- a/windows/winnet/src/winnet/routing/defaultroutemonitor.cpp +++ /dev/null @@ -1,248 +0,0 @@ -#include "stdafx.h" -#include <libcommon/error.h> -#include "defaultroutemonitor.h" -#include "helpers.h" - -namespace winnet::routing -{ - -namespace -{ - -const uint32_t POINT_TWO_SECOND_BURST = 200; -const uint32_t TWO_SECOND_INTERFERENCE = 2000; - -} // anonymous namespace - -DefaultRouteMonitor::DefaultRouteMonitor -( - ADDRESS_FAMILY family, - Callback callback, - std::shared_ptr<common::logging::ILogSink> logSink -) - : m_family(family) - , m_callback(callback) - , m_logSink(logSink) - , m_refreshCurrentRoute(false) - , m_evaluateRoutesGuard(std::make_unique<common::BurstGuard>( - std::bind(&DefaultRouteMonitor::evaluateRoutes, this), - POINT_TWO_SECOND_BURST, - TWO_SECOND_INTERFERENCE - )) -{ - std::scoped_lock<std::mutex> lock(m_evaluationLock); - - auto status = NotifyRouteChange2(family, RouteChangeCallback, this, FALSE, &m_routeNotificationHandle); - - if (NO_ERROR != status) - { - THROW_WINDOWS_ERROR(status, "Register for route table change notifications"); - } - - status = NotifyIpInterfaceChange(family, InterfaceChangeCallback, this, - FALSE, &m_interfaceNotificationHandle); - - if (NO_ERROR != status) - { - CancelMibChangeNotify2(m_routeNotificationHandle); - THROW_WINDOWS_ERROR(status, "Register for network interface change notifications"); - } - - status = NotifyUnicastIpAddressChange(family, AddressChangeCallback, this, - FALSE, &m_addressNotificationHandle); - - if (NO_ERROR != status) - { - CancelMibChangeNotify2(m_routeNotificationHandle); - CancelMibChangeNotify2(m_interfaceNotificationHandle); - THROW_WINDOWS_ERROR(status, "Register for unicast address change notifications"); - } - - try - { - m_bestRoute = GetBestDefaultRoute(m_family); - } - catch (...) - { - } -} - -DefaultRouteMonitor::~DefaultRouteMonitor() -{ - // - // Cancel notifications to stop triggering the BurstGuard. - // - - CancelMibChangeNotify2(m_addressNotificationHandle); - CancelMibChangeNotify2(m_interfaceNotificationHandle); - CancelMibChangeNotify2(m_routeNotificationHandle); - - // - // Controlled destruction of BurstGuard to prevent it from calling here - // after other member variables have been destructed. - // - - m_evaluateRoutesGuard.reset(); -} - -//static -void NETIOAPI_API_ DefaultRouteMonitor::RouteChangeCallback -( - void *context, - MIB_IPFORWARD_ROW2 *row, - MIB_NOTIFICATION_TYPE -) -{ - // - // We're only interested in changes that add/remove/update a default route. - // - - if (0 != row->DestinationPrefix.PrefixLength - || false == RouteHasGateway(*row)) - { - return; - } - - const auto monitor = reinterpret_cast<DefaultRouteMonitor*>(context); - monitor->updateRefreshFlag(row->InterfaceLuid, row->InterfaceIndex); - monitor->m_evaluateRoutesGuard->trigger(); -} - -//static -void NETIOAPI_API_ DefaultRouteMonitor::InterfaceChangeCallback -( - void *context, - MIB_IPINTERFACE_ROW *row, - MIB_NOTIFICATION_TYPE -) -{ - const auto monitor = reinterpret_cast<DefaultRouteMonitor*>(context); - monitor->updateRefreshFlag(row->InterfaceLuid, row->InterfaceIndex); - monitor->m_evaluateRoutesGuard->trigger(); -} - -//static -void NETIOAPI_API_ DefaultRouteMonitor::AddressChangeCallback -( - void *context, - MIB_UNICASTIPADDRESS_ROW *row, - MIB_NOTIFICATION_TYPE -) -{ - const auto monitor = reinterpret_cast<DefaultRouteMonitor*>(context); - monitor->updateRefreshFlag(row->InterfaceLuid, row->InterfaceIndex); - monitor->m_evaluateRoutesGuard->trigger(); -} - -void DefaultRouteMonitor::updateRefreshFlag(const NET_LUID &luid, const NET_IFINDEX &index) -{ - std::scoped_lock<std::mutex> lock(m_evaluationLock); - - if (!m_bestRoute.has_value()) - { - return; - } - - if (luid.Value == m_bestRoute->iface.Value) - { - m_refreshCurrentRoute = true; - return; - } - - if (luid.Value != 0) - { - return; - } - - NET_IFINDEX defaultInterfaceIndex = 0; - const auto routeLuid = &m_bestRoute->iface; - ConvertInterfaceLuidToIndex(routeLuid, &defaultInterfaceIndex); - m_refreshCurrentRoute = index == defaultInterfaceIndex || - (defaultInterfaceIndex == NET_IFINDEX_UNSPECIFIED); -} - -void DefaultRouteMonitor::evaluateRoutes() -{ - std::scoped_lock<std::mutex> lock(m_evaluationLock); - - try - { - evaluateRoutesInner(); - } - catch (const std::exception &ex) - { - const auto msg = std::string("Failure while evaluating route table: ").append(ex.what()); - m_logSink->error(msg.c_str()); - } - catch (...) - { - m_logSink->error("Unspecified failure while evaluating route table"); - } -} - -void DefaultRouteMonitor::evaluateRoutesInner() -{ - std::optional<InterfaceAndGateway> currentBestRoute; - - bool refreshCurrent = m_refreshCurrentRoute; - m_refreshCurrentRoute = false; - - try - { - currentBestRoute = GetBestDefaultRoute(m_family); - } - catch (...) - { - } - - // - // If there was no default route previously. - // - - if (false == m_bestRoute.has_value()) - { - if (currentBestRoute.has_value()) - { - m_bestRoute = currentBestRoute; - m_callback(EventType::Updated, m_bestRoute); - } - - return; - } - - // - // There used to be a default route. - // If there is not currently a default route. - // - - if (false == currentBestRoute.has_value()) - { - m_bestRoute.reset(); - m_callback(EventType::Removed, std::nullopt); - - return; - } - - // - // The current best route may have changed. - // - - if (m_bestRoute.value() != currentBestRoute.value()) - { - m_bestRoute = currentBestRoute; - m_callback(EventType::Updated, m_bestRoute); - - return; - } - - // - // Interface details may have changed. - // - - if (refreshCurrent) - { - m_callback(EventType::UpdatedDetails, m_bestRoute); - } -} - -} diff --git a/windows/winnet/src/winnet/routing/defaultroutemonitor.h b/windows/winnet/src/winnet/routing/defaultroutemonitor.h deleted file mode 100644 index ce2a3ce3f6..0000000000 --- a/windows/winnet/src/winnet/routing/defaultroutemonitor.h +++ /dev/null @@ -1,78 +0,0 @@ -#pragma once - -#include <ifdef.h> -#include <ws2def.h> -#include <functional> -#include <optional> -#include <memory> -#include <mutex> -#include <libcommon/logging/ilogsink.h> -#include <libcommon/burstguard.h> -#include "types.h" - -namespace winnet::routing -{ - -class DefaultRouteMonitor -{ -public: - - enum class EventType - { - // The best default route changed. - Updated, - - // Interface details changed; the associated interface and - // gateway did not. - UpdatedDetails, - - // No default routes exist. - Removed, - }; - - using Callback = std::function<void - ( - EventType eventType, - - // For update events, data associated with the new best default route. - const std::optional<InterfaceAndGateway> &route - )>; - - DefaultRouteMonitor(ADDRESS_FAMILY family, Callback callback, std::shared_ptr<common::logging::ILogSink> logSink); - ~DefaultRouteMonitor(); - - DefaultRouteMonitor(const DefaultRouteMonitor &) = delete; - DefaultRouteMonitor(DefaultRouteMonitor &&) = delete; - DefaultRouteMonitor &operator=(const DefaultRouteMonitor &) = delete; - DefaultRouteMonitor &operator=(DefaultRouteMonitor &&) = delete; - -private: - - ADDRESS_FAMILY m_family; - Callback m_callback; - std::shared_ptr<common::logging::ILogSink> m_logSink; - - // This can't be a plain member variable. - // We need to be able to delete it explicitly in order to have a controlled tear down. - std::unique_ptr<common::BurstGuard> m_evaluateRoutesGuard; - - std::optional<InterfaceAndGateway> m_bestRoute; - bool m_refreshCurrentRoute; - - HANDLE m_routeNotificationHandle; - HANDLE m_interfaceNotificationHandle; - HANDLE m_addressNotificationHandle; - - std::mutex m_evaluationLock; - - static void NETIOAPI_API_ RouteChangeCallback(void *context, MIB_IPFORWARD_ROW2 *row, MIB_NOTIFICATION_TYPE notificationType); - static void NETIOAPI_API_ InterfaceChangeCallback(void *context, MIB_IPINTERFACE_ROW *row, MIB_NOTIFICATION_TYPE notificationType); - static void NETIOAPI_API_ AddressChangeCallback(void *context, MIB_UNICASTIPADDRESS_ROW *row, MIB_NOTIFICATION_TYPE notificationType); - - void updateRefreshFlag(const NET_LUID &luid, const NET_IFINDEX &index); - - void evaluateRoutes(); - void evaluateRoutesInner(); -}; - -} diff --git a/windows/winnet/src/winnet/routing/helpers.cpp b/windows/winnet/src/winnet/routing/helpers.cpp deleted file mode 100644 index 4a85fda1fb..0000000000 --- a/windows/winnet/src/winnet/routing/helpers.cpp +++ /dev/null @@ -1,295 +0,0 @@ -#include "stdafx.h" -#include "helpers.h" -#include <ws2def.h> -#include <in6addr.h> -#include <numeric> -#include <libcommon/error.h> -#include <libcommon/memory.h> - -namespace -{ - -// Interface description substrings found for virtual adapters. -const wchar_t *TUNNEL_INTERFACE_DESCS[] = { - L"WireGuard", - L"Wintun", - L"Tunnel" -}; - -bool IsRouteOnPhysicalInterface(const MIB_IPFORWARD_ROW2 &route) -{ - switch (route.InterfaceLuid.Info.IfType) - { - case IF_TYPE_SOFTWARE_LOOPBACK: - case IF_TYPE_TUNNEL: - { - return false; - } - } - - // OpenVPN uses interface type IF_TYPE_PROP_VIRTUAL, - // but tethering etc. may rely on virtual adapters too, - // so we have to filter out the TAP adapter specifically. - - MIB_IF_ROW2 row = { 0 }; - row.InterfaceLuid = route.InterfaceLuid; - - if (NO_ERROR != GetIfEntry2(&row)) - { - THROW_ERROR("Cannot obtain interface information for the given route"); - } - - for (size_t i = 0; i < ARRAYSIZE(TUNNEL_INTERFACE_DESCS); i++) - { - if (nullptr != wcsstr(row.Description, TUNNEL_INTERFACE_DESCS[i])) - { - return false; - } - } - - return true; -} - -} // anonymous namespace - -namespace winnet::routing -{ - -bool EqualAddress(const Network &lhs, const Network &rhs) -{ - if (lhs.PrefixLength != rhs.PrefixLength) - { - return false; - } - - return EqualAddress(lhs.Prefix, rhs.Prefix); -} - -bool EqualAddress(const NodeAddress &lhs, const NodeAddress &rhs) -{ - if (lhs.si_family != rhs.si_family) - { - return false; - } - - switch (lhs.si_family) - { - case AF_INET: - { - return lhs.Ipv4.sin_addr.s_addr == rhs.Ipv4.sin_addr.s_addr; - } - case AF_INET6: - { - return 0 == memcmp(&lhs.Ipv6.sin6_addr, &rhs.Ipv6.sin6_addr, sizeof(IN6_ADDR)); - } - default: - { - THROW_ERROR("Invalid address family for network address"); - } - } -} - -bool EqualAddress(const SOCKADDR_INET *lhs, const SOCKET_ADDRESS *rhs) -{ - if (lhs->si_family != rhs->lpSockaddr->sa_family) - { - return false; - } - - switch (lhs->si_family) - { - case AF_INET: - { - auto typedRhs = reinterpret_cast<const SOCKADDR_IN *>(rhs->lpSockaddr); - return lhs->Ipv4.sin_addr.s_addr == typedRhs->sin_addr.s_addr; - } - case AF_INET6: - { - auto typedRhs = reinterpret_cast<const SOCKADDR_IN6 *>(rhs->lpSockaddr); - return 0 == memcmp(lhs->Ipv6.sin6_addr.u.Byte, typedRhs->sin6_addr.u.Byte, 16); - } - default: - { - THROW_ERROR("Missing case handler in switch clause"); - } - } -} - -bool GetAdapterInterface(NET_LUID adapter, ADDRESS_FAMILY addressFamily, MIB_IPINTERFACE_ROW *iface) -{ - memset(iface, 0, sizeof(MIB_IPINTERFACE_ROW)); - - iface->Family = addressFamily; - iface->InterfaceLuid = adapter; - - return NO_ERROR == GetIpInterfaceEntry(iface); -} - -std::vector<AnnotatedRoute> AnnotateRoutes(const std::vector<const MIB_IPFORWARD_ROW2 *> &routes) -{ - std::vector<AnnotatedRoute> annotated; - annotated.reserve(routes.size()); - - for (auto route : routes) - { - MIB_IPINTERFACE_ROW iface; - - if (false == GetAdapterInterface(route->InterfaceLuid, route->DestinationPrefix.Prefix.si_family, &iface)) - { - continue; - } - - annotated.emplace_back - ( - AnnotatedRoute{ route, bool_cast(iface.Connected), route->Metric + iface.Metric } - ); - } - - return annotated; -} - -bool RouteHasGateway(const MIB_IPFORWARD_ROW2 &route) -{ - switch (route.NextHop.si_family) - { - case AF_INET: - { - return 0 != route.NextHop.Ipv4.sin_addr.s_addr; - } - case AF_INET6: - { - const uint8_t *begin = &route.NextHop.Ipv6.sin6_addr.u.Byte[0]; - const uint8_t *end = begin + 16; - - return 0 != std::accumulate(begin, end, 0); - } - default: - { - return false; - } - }; -} - -std::optional<InterfaceAndGateway> GetBestDefaultRoute(ADDRESS_FAMILY family) -{ - PMIB_IPFORWARD_TABLE2 table; - - auto status = GetIpForwardTable2(family, &table); - - if (NO_ERROR != status) - { - THROW_WINDOWS_ERROR(status, "Acquire route table"); - } - - common::memory::ScopeDestructor sd; - - sd += [table] - { - FreeMibTable(table); - }; - - std::vector<const MIB_IPFORWARD_ROW2 *> candidates; - candidates.reserve(table->NumEntries); - - // - // Enumerate routes looking for: route 0/0 - // The WireGuard interface route has no gateway. - // - - for (ULONG i = 0; i < table->NumEntries; ++i) - { - const MIB_IPFORWARD_ROW2 &candidate = table->Table[i]; - - if (0 == candidate.DestinationPrefix.PrefixLength - && RouteHasGateway(candidate) - && IsRouteOnPhysicalInterface(candidate)) - { - candidates.emplace_back(&candidate); - } - } - - auto annotated = AnnotateRoutes(candidates); - - if (annotated.empty()) - { - return std::nullopt; - } - - // - // Sort on (active, effectiveMetric) ascending by metric. - // - - std::sort(annotated.begin(), annotated.end(), [](const AnnotatedRoute &lhs, const AnnotatedRoute &rhs) - { - if (lhs.active == rhs.active) - { - return lhs.effectiveMetric < rhs.effectiveMetric; - } - - return lhs.active && false == rhs.active; - }); - - // - // Ensure the top rated route is active. - // - - if (false == annotated[0].active) - { - return std::nullopt; - } - - return std::make_optional(InterfaceAndGateway { annotated[0].route->InterfaceLuid, annotated[0].route->NextHop }); -} - -bool AdapterInterfaceEnabled(const IP_ADAPTER_ADDRESSES *adapter, ADDRESS_FAMILY family) -{ - switch (family) - { - case AF_INET: - { - return 0 != adapter->Ipv4Enabled; - } - case AF_INET6: - { - return 0 != adapter->Ipv6Enabled; - } - default: - { - THROW_ERROR("Missing case handler in switch clause"); - } - } -} - -std::vector<const SOCKET_ADDRESS *> IsolateGatewayAddresses -( - PIP_ADAPTER_GATEWAY_ADDRESS_LH head, - ADDRESS_FAMILY family -) -{ - std::vector<const SOCKET_ADDRESS *> matches; - - for (auto gateway = head; nullptr != gateway; gateway = gateway->Next) - { - if (family == gateway->Address.lpSockaddr->sa_family) - { - matches.emplace_back(&gateway->Address); - } - } - - return matches; -} - -bool AddressPresent(const std::vector<const SOCKET_ADDRESS *> &hay, const SOCKADDR_INET *needle) -{ - for (const auto candidate : hay) - { - if (EqualAddress(needle, candidate)) - { - return true; - } - } - - return false; -} - -} diff --git a/windows/winnet/src/winnet/routing/helpers.h b/windows/winnet/src/winnet/routing/helpers.h deleted file mode 100644 index 9d73585da9..0000000000 --- a/windows/winnet/src/winnet/routing/helpers.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include "types.h" -#include <vector> -#include <optional> - -namespace winnet::routing -{ - -bool EqualAddress(const Network &lhs, const Network &rhs); -bool EqualAddress(const NodeAddress &lhs, const NodeAddress &rhs); -bool EqualAddress(const SOCKADDR_INET *lhs, const SOCKET_ADDRESS *rhs); - -bool GetAdapterInterface(NET_LUID adapter, ADDRESS_FAMILY addressFamily, MIB_IPINTERFACE_ROW *iface); - -struct AnnotatedRoute -{ - const MIB_IPFORWARD_ROW2 *route; - bool active; - uint32_t effectiveMetric; -}; - -template<typename T> -bool bool_cast(const T &value) -{ - return 0 != value; -} - -std::vector<AnnotatedRoute> AnnotateRoutes(const std::vector<const MIB_IPFORWARD_ROW2 *> &routes); - -bool RouteHasGateway(const MIB_IPFORWARD_ROW2 &route); - -std::optional<InterfaceAndGateway> GetBestDefaultRoute(ADDRESS_FAMILY family); - -bool AdapterInterfaceEnabled(const IP_ADAPTER_ADDRESSES *adapter, ADDRESS_FAMILY family); - -std::vector<const SOCKET_ADDRESS *> IsolateGatewayAddresses -( - PIP_ADAPTER_GATEWAY_ADDRESS_LH head, - ADDRESS_FAMILY family -); - -bool AddressPresent(const std::vector<const SOCKET_ADDRESS *> &hay, const SOCKADDR_INET *needle); - - -} diff --git a/windows/winnet/src/winnet/routing/routemanager.cpp b/windows/winnet/src/winnet/routing/routemanager.cpp deleted file mode 100644 index 2c2257e916..0000000000 --- a/windows/winnet/src/winnet/routing/routemanager.cpp +++ /dev/null @@ -1,647 +0,0 @@ -#include "stdafx.h" -#include "routemanager.h" -#include "helpers.h" -#include <libcommon/error.h> -#include <libcommon/memory.h> -#include <libcommon/string.h> -#include <libcommon/network/adapters.h> -#include <vector> -#include <algorithm> -#include <numeric> -#include <sstream> - -using AutoLockType = std::scoped_lock<std::mutex>; -using AutoRecursiveLockType = std::scoped_lock<std::recursive_mutex>; -using namespace std::placeholders; - -namespace winnet::routing -{ - -namespace -{ - -using Adapters = common::network::Adapters; - -NET_LUID InterfaceLuidFromGateway(const NodeAddress &gateway) -{ - const DWORD adapterFlags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER - | GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_INCLUDE_GATEWAYS; - - Adapters adapters(gateway.si_family, adapterFlags); - - // - // Process adapters to find matching ones. - // - - std::vector<const IP_ADAPTER_ADDRESSES *> matches; - - for (auto adapter = adapters.next(); nullptr != adapter; adapter = adapters.next()) - { - if (false == AdapterInterfaceEnabled(adapter, gateway.si_family)) - { - continue; - } - - auto gateways = IsolateGatewayAddresses(adapter->FirstGatewayAddress, gateway.si_family); - - if (AddressPresent(gateways, &gateway)) - { - matches.emplace_back(adapter); - } - } - - if (matches.empty()) - { - THROW_ERROR_TYPE(error::DeviceGatewayNotFound, "Unable to find network adapter with specified gateway"); - } - - // - // Sort matching interfaces ascending by metric. - // - - const bool targetV4 = (AF_INET == gateway.si_family); - - std::sort(matches.begin(), matches.end(), [&targetV4](const IP_ADAPTER_ADDRESSES *lhs, const IP_ADAPTER_ADDRESSES *rhs) - { - if (targetV4) - { - return lhs->Ipv4Metric < rhs->Ipv4Metric; - } - - return lhs->Ipv6Metric < rhs->Ipv6Metric; - }); - - // - // Select the interface with the best (lowest) metric. - // - - return matches[0]->Luid; -} - -bool ParseStringEncodedLuid(const std::wstring &encodedLuid, NET_LUID &luid) -{ - // - // The `#` is a valid character in adapter names so we use `?` instead. - // The LUID is thus prefixed with `?` and hex encoded and left-padded with zeroes. - // E.g. `?deadbeefcafebabe` or `?000dbeefcafebabe`. - // - - static const size_t StringEncodedLuidLength = 17; - - if (encodedLuid.size() != StringEncodedLuidLength - || L'?' != encodedLuid[0]) - { - return false; - } - - try - { - std::wstringstream ss; - - ss << std::hex << &encodedLuid[1]; - ss >> luid.Value; - } - catch (...) - { - const auto msg = std::string("Failed to parse string encoded LUID: ") - .append(common::string::ToAnsi(encodedLuid)); - - THROW_ERROR(msg.c_str()); - } - - return true; -} - -InterfaceAndGateway ResolveNode(ADDRESS_FAMILY family, const std::optional<Node> &optionalNode) -{ - // - // There are four cases: - // - // Unspecified node (use interface and gateway of default route). - // Node is specified by name. - // Node is specified by name and gateway. - // Node is specified by gateway. - // - - if (false == optionalNode.has_value()) - { - const auto default_route = GetBestDefaultRoute(family); - if (!default_route.has_value()) - { - THROW_ERROR_TYPE(error::NoDefaultRoute, "Unable to determine details of default route"); - } - return default_route.value(); - } - - const auto &node = optionalNode.value(); - - if (node.deviceName().has_value()) - { - const auto &deviceName = node.deviceName().value(); - NET_LUID luid; - - if (false == ParseStringEncodedLuid(deviceName, luid) - && 0 != ConvertInterfaceAliasToLuid(deviceName.c_str(), &luid)) - { - const auto msg = std::string("Unable to derive interface LUID from interface alias: ") - .append(common::string::ToAnsi(deviceName)); - THROW_ERROR_TYPE(error::DeviceNameNotFound, msg.c_str()); - } - - auto onLinkProvider = [&family]() - { - NodeAddress onLink = { 0 }; - onLink.si_family = family; - - return onLink; - }; - - return InterfaceAndGateway{ luid, node.gateway().value_or(onLinkProvider()) }; - } - - // - // The node is specified only by gateway. - // - - return InterfaceAndGateway{ InterfaceLuidFromGateway(node.gateway().value()), node.gateway().value() }; -} - -std::wstring FormatNetwork(const Network &network) -{ - using namespace common::string; - - switch (network.Prefix.si_family) - { - case AF_INET: - { - return FormatIpv4<AddressOrder::NetworkByteOrder>(network.Prefix.Ipv4.sin_addr.s_addr, network.PrefixLength); - } - case AF_INET6: - { - return FormatIpv6(network.Prefix.Ipv6.sin6_addr.u.Byte, network.PrefixLength); - } - default: - { - return L"Failed to format network details"; - } - } -} - -} // anonymous namespace - -RouteManager::RouteManager(std::shared_ptr<common::logging::ILogSink> logSink) - : m_logSink(logSink) - , m_routeMonitorV4(std::make_unique<DefaultRouteMonitor>( - static_cast<ADDRESS_FAMILY>(AF_INET), - std::bind(&RouteManager::defaultRouteChanged, this, static_cast<ADDRESS_FAMILY>(AF_INET), _1, _2), - logSink - )) - , m_routeMonitorV6(std::make_unique<DefaultRouteMonitor>( - static_cast<ADDRESS_FAMILY>(AF_INET6), - std::bind(&RouteManager::defaultRouteChanged, this, static_cast<ADDRESS_FAMILY>(AF_INET6), _1, _2), - logSink - )) -{ -} - -RouteManager::~RouteManager() -{ - // - // Stop callbacks that are triggered by events in Windows from coming in. - // - - m_routeMonitorV4.reset(); - m_routeMonitorV6.reset(); - - deleteAppliedRoutes(); -} - -void RouteManager::addRoutes(const std::vector<Route> &routes) -{ - AutoLockType lock(m_routesLock); - - std::vector<EventEntry> eventLog; - - for (const auto &route : routes) - { - try - { - RouteRecord newRecord{ route, addIntoRoutingTable(route) }; - - eventLog.emplace_back(EventEntry{ EventType::ADD_ROUTE, newRecord }); - - auto existingRecord = findRouteRecord(newRecord.registeredRoute); - - if (m_routes.end() == existingRecord) - { - m_routes.emplace_back(std::move(newRecord)); - } - else - { - *existingRecord = std::move(newRecord); - } - } - catch (const error::RouteManagerError&) - { - undoEvents(eventLog); - throw; - } - catch (...) - { - undoEvents(eventLog); - THROW_ERROR("Unexpected error during batch insertion of routes"); - } - } -} - -void RouteManager::deleteRoutes(const std::vector<Route> &routes) -{ - AutoLockType lock(m_routesLock); - - std::vector<EventEntry> eventLog; - - for (const auto &route : routes) - { - try - { - const auto record = findRouteRecordFromSpec(route); - - if (m_routes.end() == record) - { - const auto err = std::wstring(L"Request to delete unknown route: ") - .append(FormatNetwork(route.network())); - - m_logSink->warning(common::string::ToAnsi(err).c_str()); - - continue; - } - - deleteFromRoutingTable(record->registeredRoute); - - eventLog.emplace_back(EventEntry{ EventType::DELETE_ROUTE, *record }); - m_routes.erase(record); - } - catch (...) - { - undoEvents(eventLog); - - THROW_ERROR("Failed during batch removal of routes"); - } - } -} - -void RouteManager::deleteAppliedRoutes() -{ - // - // Delete all routes owned by us. - // - - for (const auto &record : m_routes) - { - try - { - deleteFromRoutingTable(record.registeredRoute); - } - catch (const std::exception & ex) - { - std::wstringstream ss; - - ss << L"Failed to delete route while clearing applied routes, Route: " - << FormatRegisteredRoute(record.registeredRoute); - - m_logSink->error(common::string::ToAnsi(ss.str()).c_str()); - m_logSink->error(ex.what()); - } - } - - m_routes.clear(); -} - -RouteManager::CallbackHandle RouteManager::registerDefaultRouteChangedCallback(DefaultRouteChangedCallback callback) -{ - AutoRecursiveLockType lock(m_defaultRouteCallbacksLock); - - m_defaultRouteCallbacks.emplace_back(callback); - - // Return raw address of record in list. - return &m_defaultRouteCallbacks.back(); -} - -void RouteManager::unregisterDefaultRouteChangedCallback(CallbackHandle handle) -{ - AutoRecursiveLockType lock(m_defaultRouteCallbacksLock); - - for (auto it = m_defaultRouteCallbacks.begin(); it != m_defaultRouteCallbacks.end(); ++it) - { - // Match on raw address of record. - if (&*it == handle) - { - m_defaultRouteCallbacks.erase(it); - return; - } - } -} - -std::list<RouteManager::RouteRecord>::iterator RouteManager::findRouteRecord(const RegisteredRoute &route) -{ - return std::find_if(m_routes.begin(), m_routes.end(), [&route](const auto &record) - { - return route == record.registeredRoute; - }); -} - -std::list<RouteManager::RouteRecord>::iterator RouteManager::findRouteRecordFromSpec(const Route &route) -{ - return std::find_if(m_routes.begin(), m_routes.end(), [&route](const auto &record) - { - return route == record.route; - }); -} - -RouteManager::RegisteredRoute RouteManager::addIntoRoutingTable(const Route &route) -{ - const auto node = ResolveNode(route.network().Prefix.si_family, route.node()); - - MIB_IPFORWARD_ROW2 spec; - - InitializeIpForwardEntry(&spec); - - spec.InterfaceLuid = node.iface; - spec.DestinationPrefix = route.network(); - spec.NextHop = node.gateway; - spec.Metric = 0; - spec.Protocol = MIB_IPPROTO_NETMGMT; - spec.Origin = NlroManual; - - auto status = CreateIpForwardEntry2(&spec); - - // - // The return code ERROR_OBJECT_ALREADY_EXISTS means there is already an existing route - // on the same interface, with the same DestinationPrefix and NextHop. - // - // However, all the other properties of the route may be different. And the properties may - // not have the exact same values as when the route was registered, because windows - // will adjust route properties at time of route insertion as well as later. - // - // The simplest thing in this case is to just overwrite the route. - // - - if (status == ERROR_OBJECT_ALREADY_EXISTS) - { - status = SetIpForwardEntry2(&spec); - } - - if (NO_ERROR != status) - { - THROW_WINDOWS_ERROR(status, "Register route in routing table"); - } - - return RegisteredRoute { route.network(), node.iface, node.gateway }; -} - -void RouteManager::restoreIntoRoutingTable(const RegisteredRoute &route) -{ - MIB_IPFORWARD_ROW2 spec; - - InitializeIpForwardEntry(&spec); - - spec.InterfaceLuid = route.luid; - spec.DestinationPrefix = route.network; - spec.NextHop = route.nextHop; - spec.Metric = 0; - spec.Protocol = MIB_IPPROTO_NETMGMT; - spec.Origin = NlroManual; - - const auto status = CreateIpForwardEntry2(&spec); - - if (NO_ERROR != status) - { - THROW_WINDOWS_ERROR(status, "Register route in routing table"); - } -} - -void RouteManager::deleteFromRoutingTable(const RegisteredRoute &route) -{ - MIB_IPFORWARD_ROW2 r = { 0}; - - r.InterfaceLuid = route.luid; - r.DestinationPrefix = route.network; - r.NextHop = route.nextHop; - - auto status = DeleteIpForwardEntry2(&r); - - if (ERROR_NOT_FOUND == status) - { - status = NO_ERROR; - - const auto err = std::wstring(L"Attempting to delete route which was not present in routing table, " \ - "ignoring and proceeding. Route: ").append(FormatRegisteredRoute(route)); - - m_logSink->warning(common::string::ToAnsi(err).c_str()); - } - - if (NO_ERROR != status) - { - THROW_WINDOWS_ERROR(status, "Delete route in routing table"); - } -} - -void RouteManager::undoEvents(const std::vector<EventEntry> &eventLog) -{ - // - // Rewind state by processing events in the reverse order. - // - - for (auto it = eventLog.rbegin(); it != eventLog.rend(); ++it) - { - try - { - switch (it->type) - { - case EventType::ADD_ROUTE: - { - const auto record = findRouteRecord(it->record.registeredRoute); - - if (m_routes.end() == record) - { - THROW_ERROR("Internal state inconsistency in route manager"); - } - - deleteFromRoutingTable(record->registeredRoute); - m_routes.erase(record); - - break; - } - case EventType::DELETE_ROUTE: - { - restoreIntoRoutingTable(it->record.registeredRoute); - m_routes.emplace_back(it->record); - - break; - } - default: - { - THROW_ERROR("Missing case handler in switch clause"); - } - } - } - catch (const std::exception &ex) - { - const auto err = std::string("Attempting to rollback state: ").append(ex.what()); - m_logSink->error(err.c_str()); - } - } -} - -// static -std::wstring RouteManager::FormatRegisteredRoute(const RegisteredRoute &route) -{ - using namespace common::string; - - std::wstringstream ss; - - if (AF_INET == route.network.Prefix.si_family) - { - std::wstring gateway(L"\"On-link\""); - - if (0 != route.nextHop.Ipv4.sin_addr.s_addr) - { - gateway = FormatIpv4<AddressOrder::NetworkByteOrder>(route.nextHop.Ipv4.sin_addr.s_addr); - } - - ss << FormatIpv4<AddressOrder::NetworkByteOrder>(route.network.Prefix.Ipv4.sin_addr.s_addr, route.network.PrefixLength) - << L" with gateway " << gateway - << L" on interface with LUID 0x" << std::hex << route.luid.Value; - } - else if (AF_INET6 == route.network.Prefix.si_family) - { - std::wstring gateway(L"\"On-link\""); - - const uint8_t *begin = &route.nextHop.Ipv6.sin6_addr.u.Byte[0]; - const uint8_t *end = begin + 16; - - if (0 != std::accumulate(begin, end, 0)) - { - gateway = FormatIpv6(route.nextHop.Ipv6.sin6_addr.u.Byte); - } - - ss << FormatIpv6(route.network.Prefix.Ipv6.sin6_addr.u.Byte, route.network.PrefixLength) - << L" with gateway " << gateway - << L" on interface with LUID 0x" << std::hex << route.luid.Value; - } - else - { - ss << L"Failed to format route details"; - } - - return ss.str(); -} - -void RouteManager::defaultRouteChanged(ADDRESS_FAMILY family, DefaultRouteMonitor::EventType eventType, - const std::optional<InterfaceAndGateway> &route) -{ - // - // Forward event to all registered listeners. - // - - m_defaultRouteCallbacksLock.lock(); - - for (const auto &callback : m_defaultRouteCallbacks) - { - try - { - callback(eventType, family, route); - } - catch (const std::exception &ex) - { - const auto msg = std::string("Failure in default-route-changed callback: ").append(ex.what()); - m_logSink->error(msg.c_str()); - } - catch (...) - { - m_logSink->error("Unspecified failure in default-route-changed callback"); - } - } - - m_defaultRouteCallbacksLock.unlock(); - - // - // Examine event to determine if best default route has changed. - // - - if (DefaultRouteMonitor::EventType::Updated != eventType) - { - return; - } - - // - // Examine our routes to see if any of them are policy bound to the best default route. - // - - AutoLockType routesLock(m_routesLock); - - using RecordIterator = std::list<RouteRecord>::iterator; - - std::list<RecordIterator> affectedRoutes; - - for (RecordIterator it = m_routes.begin(); it != m_routes.end(); ++it) - { - if (false == it->route.node().has_value() - && family == it->route.network().Prefix.si_family) - { - affectedRoutes.emplace_back(it); - } - } - - if (affectedRoutes.empty()) - { - return; - } - - // - // Update all affected routes. - // - - m_logSink->info("Best default route has changed. Refreshing dependent routes"); - - for (auto &it : affectedRoutes) - { - // - // We can't update the existing route because defining characteristics are being changed. - // So removing and adding again is the only option. - // - - try - { - deleteFromRoutingTable(it->registeredRoute); - } - catch (const std::exception &ex) - { - const auto msg = std::string("Failed to delete route when refreshing " \ - "existing routes: ").append(ex.what()); - - m_logSink->error(msg.c_str()); - - continue; - } - - it->registeredRoute.luid = route.value().iface; - it->registeredRoute.nextHop = route.value().gateway; - - try - { - restoreIntoRoutingTable(it->registeredRoute); - } - catch (const std::exception &ex) - { - const auto msg = std::string("Failed to add route when refreshing " \ - "existing routes: ").append(ex.what()); - - m_logSink->error(msg.c_str()); - - continue; - } - } -} - -} diff --git a/windows/winnet/src/winnet/routing/routemanager.h b/windows/winnet/src/winnet/routing/routemanager.h deleted file mode 100644 index 42949a657b..0000000000 --- a/windows/winnet/src/winnet/routing/routemanager.h +++ /dev/null @@ -1,176 +0,0 @@ -#pragma once - -#include <string> -#include <memory> -#include <vector> -#include <list> -#include <optional> -#include <mutex> -#include <functional> -#include <windows.h> -#include <ws2def.h> -#include <ifdef.h> -#include <libcommon/string.h> -#include <libcommon/logging/ilogsink.h> -#include "defaultroutemonitor.h" -#include "helpers.h" - -namespace winnet::routing -{ - -namespace error -{ - -class RouteManagerError : public std::runtime_error -{ -public: - - RouteManagerError(const char* message) - : std::runtime_error(message) - { - } -}; - -class NoDefaultRoute : public RouteManagerError -{ -public: - - NoDefaultRoute(const char* message) - : RouteManagerError(message) - { - } -}; - -class DeviceNameNotFound : public RouteManagerError -{ -public: - - DeviceNameNotFound(const char* message) - : RouteManagerError(message) - { - } -}; - -class DeviceGatewayNotFound : public RouteManagerError -{ -public: - - DeviceGatewayNotFound(const char* message) - : RouteManagerError(message) - { - } -}; - -} - -class RouteManager -{ -public: - - RouteManager(std::shared_ptr<common::logging::ILogSink> logSink); - ~RouteManager(); - - RouteManager(const RouteManager &) = delete; - RouteManager(RouteManager &&) = default; - RouteManager &operator=(const RouteManager &) = delete; - RouteManager &operator=(RouteManager &&) = delete; - - void addRoutes(const std::vector<Route> &routes); - void deleteRoutes(const std::vector<Route> &routes); - void deleteAppliedRoutes(); - - using DefaultRouteChangedEventType = DefaultRouteMonitor::EventType; - - using DefaultRouteChangedCallback = std::function<void - ( - DefaultRouteChangedEventType eventType, - ADDRESS_FAMILY family, - - // For update events, data associated with the new best default route. - const std::optional<InterfaceAndGateway> &route - )>; - - using CallbackHandle = void*; - - CallbackHandle registerDefaultRouteChangedCallback(DefaultRouteChangedCallback callback); - void unregisterDefaultRouteChangedCallback(CallbackHandle handle); - -private: - - std::shared_ptr<common::logging::ILogSink> m_logSink; - - std::unique_ptr<DefaultRouteMonitor> m_routeMonitorV4; - std::unique_ptr<DefaultRouteMonitor> m_routeMonitorV6; - - // These are the exact details derived from the route specification (`Route`). - // They are used when registering and deleting a route in the system. - struct RegisteredRoute - { - Network network; - NET_LUID luid; - NodeAddress nextHop; - - bool operator==(const RegisteredRoute &rhs) const - { - return luid.Value == rhs.luid.Value - && EqualAddress(nextHop, rhs.nextHop) - && EqualAddress(network, rhs.network); - } - }; - - struct RouteRecord - { - Route route; - RegisteredRoute registeredRoute; - }; - - std::list<RouteRecord> m_routes; - std::mutex m_routesLock; - - std::list<DefaultRouteChangedCallback> m_defaultRouteCallbacks; - std::recursive_mutex m_defaultRouteCallbacksLock; - - // - // Find record based on route registration data. - // - // Note: Searching the records and matching on route specification is - // unreliable because of the node attribute on the route. Different node - // specifications can resolve to the same physical node. - // - // (node = exit node = interface) - // - std::list<RouteRecord>::iterator findRouteRecord(const RegisteredRoute &route); - - // - // Find record based on route specification. - // - // Note: Only ever use this to find the registration data for a route - // that was successfully registered previously. - // - std::list<RouteRecord>::iterator findRouteRecordFromSpec(const Route &route); - - RegisteredRoute addIntoRoutingTable(const Route &route); - void restoreIntoRoutingTable(const RegisteredRoute &route); - void deleteFromRoutingTable(const RegisteredRoute &route); - - enum class EventType - { - ADD_ROUTE, - DELETE_ROUTE, - }; - - struct EventEntry - { - EventType type; - RouteRecord record; - }; - - void undoEvents(const std::vector<EventEntry> &eventLog); - - static std::wstring FormatRegisteredRoute(const RegisteredRoute &route); - - void defaultRouteChanged(ADDRESS_FAMILY family, DefaultRouteMonitor::EventType eventType, - const std::optional<InterfaceAndGateway> &route); -}; - -} diff --git a/windows/winnet/src/winnet/routing/types.cpp b/windows/winnet/src/winnet/routing/types.cpp deleted file mode 100644 index 9a7c755feb..0000000000 --- a/windows/winnet/src/winnet/routing/types.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include "stdafx.h" -#include "types.h" -#include "helpers.h" -#include <libcommon/string.h> -#include <libcommon/error.h> - -namespace winnet::routing -{ - -Node::Node(const std::optional<std::wstring> &deviceName, const std::optional<NodeAddress> &gateway) - : m_deviceName(deviceName) - , m_gateway(gateway) -{ - if (false == m_deviceName.has_value() && false == m_gateway.has_value()) - { - THROW_ERROR("Invalid node definition"); - } - - if (m_deviceName.has_value()) - { - auto trimmed = common::string::Trim<>(m_deviceName.value()); - - if (trimmed.empty()) - { - THROW_ERROR("Invalid device name in node definition"); - } - - m_deviceName = std::move(trimmed); - } -} - -bool Node::operator==(const Node &rhs) const -{ - if (m_deviceName.has_value()) - { - if (false == rhs.m_deviceName.has_value() - || 0 != _wcsicmp(m_deviceName.value().c_str(), rhs.deviceName().value().c_str())) - { - return false; - } - } - else if (rhs.m_deviceName.has_value()) - { - return false; - } - - if (m_gateway.has_value()) - { - if (false == rhs.m_gateway.has_value() - || false == EqualAddress(m_gateway.value(), rhs.gateway().value())) - { - return false; - } - } - else if (rhs.m_gateway.has_value()) - { - return false; - } - - return true; -} - -Route::Route(const Network &network, const std::optional<Node> &node) - : m_network(network) - , m_node(node) -{ -} - -bool Route::operator==(const Route &rhs) const -{ - if (m_node.has_value()) - { - return rhs.node().has_value() - && EqualAddress(m_network, rhs.network()) - && m_node.value() == rhs.node().value(); - } - - return false == rhs.node().has_value() - && EqualAddress(m_network, rhs.network()); -} - -bool InterfaceAndGateway::operator==(const InterfaceAndGateway &rhs) -{ - return iface.Value == rhs.iface.Value - && EqualAddress(gateway, rhs.gateway); -} - -bool InterfaceAndGateway::operator!=(const InterfaceAndGateway &rhs) -{ - return !(*this == rhs); -} - -} diff --git a/windows/winnet/src/winnet/routing/types.h b/windows/winnet/src/winnet/routing/types.h deleted file mode 100644 index 4b70a5739d..0000000000 --- a/windows/winnet/src/winnet/routing/types.h +++ /dev/null @@ -1,74 +0,0 @@ -#pragma once - -#include <string> -#include <optional> -#include <winsock2.h> -#include <windows.h> -#include <ws2def.h> -#include <ws2ipdef.h> -#include <iphlpapi.h> - -namespace winnet::routing -{ - -using Network = IP_ADDRESS_PREFIX; -using NodeAddress = SOCKADDR_INET; - -class Node -{ -public: - - Node(const std::optional<std::wstring> &deviceName, const std::optional<NodeAddress> &gateway); - - const std::optional<std::wstring> &deviceName() const - { - return m_deviceName; - } - - const std::optional<NodeAddress> &gateway() const - { - return m_gateway; - } - - bool operator==(const Node &rhs) const; - -private: - - std::optional<std::wstring> m_deviceName; - std::optional<NodeAddress> m_gateway; -}; - -class Route -{ -public: - - Route(const Network &network, const std::optional<Node> &node); - - const Network &network() const - { - return m_network; - } - - const std::optional<Node> &node() const - { - return m_node; - } - - bool operator==(const Route &rhs) const; - -private: - - Network m_network; - std::optional<Node> m_node; -}; - -struct InterfaceAndGateway -{ - NET_LUID iface; - NodeAddress gateway; - - bool operator==(const InterfaceAndGateway &rhs); - bool operator!=(const InterfaceAndGateway &rhs); -}; - -} diff --git a/windows/winnet/src/winnet/stdafx.cpp b/windows/winnet/src/winnet/stdafx.cpp deleted file mode 100644 index b29c52afc9..0000000000 --- a/windows/winnet/src/winnet/stdafx.cpp +++ /dev/null @@ -1,8 +0,0 @@ -// stdafx.cpp : source file that includes just the standard includes
-// winroute.pch will be the pre-compiled header
-// stdafx.obj will contain the pre-compiled type information
-
-#include "stdafx.h"
-
-// TODO: reference any additional headers you need in STDAFX.H
-// and not in this file
diff --git a/windows/winnet/src/winnet/stdafx.h b/windows/winnet/src/winnet/stdafx.h deleted file mode 100644 index 3115e02cd4..0000000000 --- a/windows/winnet/src/winnet/stdafx.h +++ /dev/null @@ -1,20 +0,0 @@ -// stdafx.h : include file for standard system include files,
-// or project specific include files that are used frequently, but
-// are changed infrequently
-//
-
-#pragma once
-
-// wcscpy
-#define _CRT_SECURE_NO_WARNINGS 1
-
-#include "targetver.h"
-
-#define NOMINMAX
-#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
-// Windows Header Files:
-#include <windows.h>
-
-
-
-// TODO: reference additional headers your program requires here
diff --git a/windows/winnet/src/winnet/targetver.h b/windows/winnet/src/winnet/targetver.h deleted file mode 100644 index b8b7263c9e..0000000000 --- a/windows/winnet/src/winnet/targetver.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once
-
-// Including SDKDDKVer.h defines the highest available Windows platform.
-
-// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
-// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
-
-#include <WinSDKVer.h>
-
-#define _WIN32_WINNT _WIN32_WINNT_WIN7
-
-#include <SDKDDKVer.h>
diff --git a/windows/winnet/src/winnet/winnet.cpp b/windows/winnet/src/winnet/winnet.cpp deleted file mode 100644 index 99ff24a351..0000000000 --- a/windows/winnet/src/winnet/winnet.cpp +++ /dev/null @@ -1,398 +0,0 @@ -#include "stdafx.h"
-#include "winnet.h"
-#include "routing/routemanager.h"
-#include "converters.h"
-#include <libshared/logging/logsinkadapter.h>
-#include <libshared/logging/unwind.h>
-#include <libcommon/error.h>
-#include <libcommon/memory.h>
-#include <libcommon/valuemapper.h>
-#include <cstdint>
-#include <memory>
-#include <optional>
-#include <mutex>
-
-using namespace winnet::routing;
-using AutoLockType = std::scoped_lock<std::mutex>;
-
-namespace
-{
-
-std::mutex g_RouteManagerLock;
-RouteManager *g_RouteManager = nullptr;
-std::shared_ptr<shared::logging::LogSinkAdapter> g_RouteManagerLogSink;
-
-} //anonymous namespace
-
-extern "C"
-WINNET_LINKAGE
-WINNET_STATUS
-WINNET_API
-WinNet_GetBestDefaultRoute(
- WINNET_ADDR_FAMILY family,
- WINNET_DEFAULT_ROUTE *route,
- MullvadLogSink logSink,
- void *logSinkContext
-)
-{
- try
- {
- if (nullptr == route)
- {
- THROW_ERROR("Invalid argument: route");
- }
-
- static const std::pair<WINNET_ADDR_FAMILY, ADDRESS_FAMILY> familyMap[] =
- {
- { WINNET_ADDR_FAMILY_IPV4, static_cast<ADDRESS_FAMILY>(AF_INET) },
- { WINNET_ADDR_FAMILY_IPV6, static_cast<ADDRESS_FAMILY>(AF_INET6) }
- };
- const auto win_family = common::ValueMapper::Map<>(family, familyMap);
-
- const auto ifaceAndGateway = GetBestDefaultRoute(win_family);
-
- if (!ifaceAndGateway.has_value())
- {
- return WINNET_STATUS_NOT_FOUND;
- }
-
- route->interfaceLuid = ifaceAndGateway->iface.Value;
- const auto ips = winnet::ConvertNativeAddresses(&ifaceAndGateway->gateway, 1);
- route->gateway = ips[0];
-
- return WINNET_STATUS_SUCCESS;
- }
- catch (const std::exception & err)
- {
- shared::logging::UnwindAndLog(logSink, logSinkContext, err);
- return WINNET_STATUS_FAILURE;
- }
- catch (...)
- {
- return WINNET_STATUS_FAILURE;
- }
-}
-
-extern "C"
-WINNET_LINKAGE
-bool
-WINNET_API
-WinNet_ActivateRouteManager(
- MullvadLogSink logSink,
- void *logSinkContext
-)
-{
- AutoLockType lock(g_RouteManagerLock);
-
- try
- {
- if (nullptr != g_RouteManager)
- {
- THROW_ERROR("Cannot activate route manager twice");
- }
-
- g_RouteManagerLogSink = std::make_shared<shared::logging::LogSinkAdapter>(logSink, logSinkContext);
- g_RouteManager = new RouteManager(g_RouteManagerLogSink);
-
- return true;
- }
- catch (const std::exception &err)
- {
- shared::logging::UnwindAndLog(logSink, logSinkContext, err);
- return false;
- }
- catch (...)
- {
- return false;
- }
-}
-
-extern "C"
-WINNET_LINKAGE
-WINNET_AR_STATUS
-WINNET_API
-WinNet_AddRoutes(
- const WINNET_ROUTE *routes,
- uint32_t numRoutes
-)
-{
- AutoLockType lock(g_RouteManagerLock);
-
- if (nullptr == g_RouteManager)
- {
- return WINNET_AR_STATUS_GENERAL_ERROR;
- }
-
- try
- {
- if (nullptr == routes)
- {
- THROW_ERROR("Invalid argument: routes");
- }
-
- g_RouteManager->addRoutes(winnet::ConvertRoutes(routes, numRoutes));
- return WINNET_AR_STATUS_SUCCESS;
- }
- catch (const winnet::routing::error::NoDefaultRoute &err)
- {
- common::error::UnwindException(err, g_RouteManagerLogSink);
- return WINNET_AR_STATUS_NO_DEFAULT_ROUTE;
- }
- catch (const winnet::routing::error::DeviceNameNotFound &err)
- {
- common::error::UnwindException(err, g_RouteManagerLogSink);
- return WINNET_AR_STATUS_NAME_NOT_FOUND;
- }
- catch (const winnet::routing::error::DeviceGatewayNotFound &err)
- {
- common::error::UnwindException(err, g_RouteManagerLogSink);
- return WINNET_AR_STATUS_GATEWAY_NOT_FOUND;
- }
- catch (const std::exception &err)
- {
- common::error::UnwindException(err, g_RouteManagerLogSink);
- return WINNET_AR_STATUS_GENERAL_ERROR;
- }
- catch (...)
- {
- return WINNET_AR_STATUS_GENERAL_ERROR;
- }
-}
-
-extern "C"
-WINNET_LINKAGE
-WINNET_AR_STATUS
-WINNET_API
-WinNet_AddRoute(
- const WINNET_ROUTE *route
-)
-{
- return WinNet_AddRoutes(route, 1);
-}
-
-extern "C"
-WINNET_LINKAGE
-bool
-WINNET_API
-WinNet_DeleteRoutes(
- const WINNET_ROUTE *routes,
- uint32_t numRoutes
-)
-{
- AutoLockType lock(g_RouteManagerLock);
-
- if (nullptr == g_RouteManager)
- {
- return false;
- }
-
- try
- {
- if (nullptr == routes)
- {
- THROW_ERROR("Invalid argument: routes");
- }
-
- g_RouteManager->deleteRoutes(winnet::ConvertRoutes(routes, numRoutes));
- return true;
- }
- catch (const std::exception &err)
- {
- common::error::UnwindException(err, g_RouteManagerLogSink);
- return false;
- }
- catch (...)
- {
- return false;
- }
-}
-
-extern "C"
-WINNET_LINKAGE
-bool
-WINNET_API
-WinNet_DeleteAppliedRoutes()
-{
- AutoLockType lock(g_RouteManagerLock);
-
- if (nullptr == g_RouteManager)
- {
- return false;
- }
-
- try
- {
- g_RouteManager->deleteAppliedRoutes();
- return true;
- }
- catch (const std::exception & err)
- {
- common::error::UnwindException(err, g_RouteManagerLogSink);
- return false;
- }
- catch (...)
- {
- return false;
- }
-}
-
-extern "C"
-WINNET_LINKAGE
-bool
-WINNET_API
-WinNet_DeleteRoute(
- const WINNET_ROUTE *route
-)
-{
- return WinNet_DeleteRoutes(route, 1);
-}
-
-extern "C"
-WINNET_LINKAGE
-bool
-WINNET_API
-WinNet_RegisterDefaultRouteChangedCallback(
- WinNetDefaultRouteChangedCallback callback,
- void *context,
- void **registrationHandle
-)
-{
- AutoLockType lock(g_RouteManagerLock);
-
- if (nullptr == g_RouteManager)
- {
- return false;
- }
-
- try
- {
- if (nullptr == callback)
- {
- THROW_ERROR("Invalid argument: callback");
- }
-
- if (nullptr == registrationHandle)
- {
- THROW_ERROR("Invalid argument: registrationHandle");
- }
-
- auto forwarder = [callback, context](RouteManager::DefaultRouteChangedEventType eventType,
- ADDRESS_FAMILY family, const std::optional<InterfaceAndGateway> &route)
- {
- //
- // Translate the event type.
- //
-
- using from_t = RouteManager::DefaultRouteChangedEventType;
- using to_t = WINNET_DEFAULT_ROUTE_CHANGED_EVENT_TYPE;
-
- static const std::pair<from_t, to_t> eventTypeMap[] =
- {
- { from_t::Updated, WINNET_DEFAULT_ROUTE_CHANGED_EVENT_TYPE_UPDATED },
- { from_t::UpdatedDetails, WINNET_DEFAULT_ROUTE_CHANGED_EVENT_TYPE_UPDATED_DETAILS },
- { from_t::Removed, WINNET_DEFAULT_ROUTE_CHANGED_EVENT_TYPE_REMOVED }
- };
-
- const auto translatedEventType = common::ValueMapper::Map<>(eventType, eventTypeMap);
-
- //
- // Translate the family type.
- //
-
- static const std::pair<ADDRESS_FAMILY, WINNET_ADDR_FAMILY> familyMap[] =
- {
- { static_cast<ADDRESS_FAMILY>(AF_INET), WINNET_ADDR_FAMILY_IPV4 },
- { static_cast<ADDRESS_FAMILY>(AF_INET6), WINNET_ADDR_FAMILY_IPV6 }
- };
-
- const auto translatedFamily = common::ValueMapper::Map<>(family, familyMap);
-
- WINNET_DEFAULT_ROUTE defaultRoute = { 0 };
-
- //
- // Determine which LUID and gateway to forward.
- //
-
- switch (eventType)
- {
- case RouteManager::DefaultRouteChangedEventType::Updated:
- case RouteManager::DefaultRouteChangedEventType::UpdatedDetails:
- {
- const auto ips = winnet::ConvertNativeAddresses(&route.value().gateway, 1);
- defaultRoute.gateway = ips[0];
- defaultRoute.interfaceLuid = route.value().iface.Value;
- break;
- }
- }
-
- //
- // Forward to client.
- //
-
- callback(translatedEventType, translatedFamily, defaultRoute, context);
- };
-
- *registrationHandle = g_RouteManager->registerDefaultRouteChangedCallback(forwarder);
-
- return true;
- }
- catch (const std::exception &err)
- {
- common::error::UnwindException(err, g_RouteManagerLogSink);
- return false;
- }
- catch (...)
- {
- return false;
- }
-}
-
-extern "C"
-WINNET_LINKAGE
-void
-WINNET_API
-WinNet_UnregisterDefaultRouteChangedCallback(
- void *registrationHandle
-)
-{
- AutoLockType lock(g_RouteManagerLock);
-
- if (nullptr == g_RouteManager)
- {
- return;
- }
-
- try
- {
- g_RouteManager->unregisterDefaultRouteChangedCallback(registrationHandle);
- }
- catch (const std::exception &err)
- {
- g_RouteManagerLogSink->error("Failed to unregister default-route-changed callback");
- common::error::UnwindException(err, g_RouteManagerLogSink);
- }
- catch (...)
- {
- }
-}
-
-extern "C"
-WINNET_LINKAGE
-void
-WINNET_API
-WinNet_DeactivateRouteManager(
-)
-{
- AutoLockType lock(g_RouteManagerLock);
-
- try
- {
- delete g_RouteManager;
- g_RouteManager = nullptr;
-
- g_RouteManagerLogSink.reset();
- }
- catch (...)
- {
- }
-}
diff --git a/windows/winnet/src/winnet/winnet.def b/windows/winnet/src/winnet/winnet.def deleted file mode 100644 index 0bc759d8bc..0000000000 --- a/windows/winnet/src/winnet/winnet.def +++ /dev/null @@ -1,5 +0,0 @@ -LIBRARY winnet -EXPORTS - WinNet_ActivateRouteManager - WinNet_DeactivateRouteManager - WinNet_GetBestDefaultRoute diff --git a/windows/winnet/src/winnet/winnet.h b/windows/winnet/src/winnet/winnet.h deleted file mode 100644 index 38d1386a58..0000000000 --- a/windows/winnet/src/winnet/winnet.h +++ /dev/null @@ -1,186 +0,0 @@ -#pragma once - -#include <libshared/logging/logsink.h> -#include <stdint.h> -#include <stdbool.h> - -#ifndef WINNET_STATIC -#ifdef WINNET_EXPORTS -#define WINNET_LINKAGE __declspec(dllexport) -#else -#define WINNET_LINKAGE __declspec(dllimport) -#endif -#else -#define WINNET_LINKAGE -#endif - -#define WINNET_API __stdcall - -enum WINNET_ADDR_FAMILY -{ - WINNET_ADDR_FAMILY_IPV4 = 0, - WINNET_ADDR_FAMILY_IPV6 = 1, -}; - -typedef struct tag_WINNET_IP -{ - WINNET_ADDR_FAMILY family; - uint8_t bytes[16]; // Network byte order. -} -WINNET_IP; - -typedef struct tag_WINNET_IP_NETWORK -{ - uint8_t prefix; - WINNET_IP addr; -} -WINNET_IP_NETWORK; - -typedef struct tag_WINNET_NODE -{ - const WINNET_IP *gateway; - const wchar_t *deviceName; -} -WINNET_NODE; - -typedef struct tag_WINNET_ROUTE -{ - WINNET_IP_NETWORK network; - const WINNET_NODE *node; -} -WINNET_ROUTE; - -extern "C" -WINNET_LINKAGE -bool -WINNET_API -WinNet_ActivateRouteManager( - MullvadLogSink logSink, - void *logSinkContext -); - -enum WINNET_AR_STATUS -{ - WINNET_AR_STATUS_SUCCESS = 0, - WINNET_AR_STATUS_GENERAL_ERROR = 1, - WINNET_AR_STATUS_NO_DEFAULT_ROUTE = 2, - WINNET_AR_STATUS_NAME_NOT_FOUND = 3, - WINNET_AR_STATUS_GATEWAY_NOT_FOUND = 4, -}; - -extern "C" -WINNET_LINKAGE -WINNET_AR_STATUS -WINNET_API -WinNet_AddRoutes( - const WINNET_ROUTE *routes, - uint32_t numRoutes -); - -extern "C" -WINNET_LINKAGE -WINNET_AR_STATUS -WINNET_API -WinNet_AddRoute( - const WINNET_ROUTE *route -); - -extern "C" -WINNET_LINKAGE -bool -WINNET_API -WinNet_DeleteRoutes( - const WINNET_ROUTE *routes, - uint32_t numRoutes -); - -extern "C" -WINNET_LINKAGE -bool -WINNET_API -WinNet_DeleteRoute( - const WINNET_ROUTE *route -); - -extern "C" -WINNET_LINKAGE -bool -WINNET_API -WinNet_DeleteAppliedRoutes( -); - -typedef struct tag_WINNET_DEFAULT_ROUTE -{ - uint64_t interfaceLuid; - WINNET_IP gateway; -} -WINNET_DEFAULT_ROUTE; - -enum WINNET_STATUS -{ - WINNET_STATUS_SUCCESS = 0, - WINNET_STATUS_NOT_FOUND = 1, - WINNET_STATUS_FAILURE = 2, -}; - -extern "C" -WINNET_LINKAGE -WINNET_STATUS -WINNET_API -WinNet_GetBestDefaultRoute( - WINNET_ADDR_FAMILY family, - WINNET_DEFAULT_ROUTE *route, - MullvadLogSink logSink, - void *logSinkContext -); - -enum WINNET_DEFAULT_ROUTE_CHANGED_EVENT_TYPE -{ - // Best default route changed. - WINNET_DEFAULT_ROUTE_CHANGED_EVENT_TYPE_UPDATED = 0, - - // The route (gateway or interface) did not change, but - // interface details may have changed. - WINNET_DEFAULT_ROUTE_CHANGED_EVENT_TYPE_UPDATED_DETAILS = 1, - - // No default routes exist. - WINNET_DEFAULT_ROUTE_CHANGED_EVENT_TYPE_REMOVED = 2, -}; - -typedef void (WINNET_API *WinNetDefaultRouteChangedCallback) -( - WINNET_DEFAULT_ROUTE_CHANGED_EVENT_TYPE eventType, - - // Indicates which IP family the event relates to. - WINNET_ADDR_FAMILY family, - - // For update events, indicates the interface associated with the new best default route. - WINNET_DEFAULT_ROUTE route, - - void *context -); - -extern "C" -WINNET_LINKAGE -bool -WINNET_API -WinNet_RegisterDefaultRouteChangedCallback( - WinNetDefaultRouteChangedCallback callback, - void *context, - void **registrationHandle -); - -extern "C" -WINNET_LINKAGE -void -WINNET_API -WinNet_UnregisterDefaultRouteChangedCallback( - void *registrationHandle -); - -extern "C" -WINNET_LINKAGE -void -WINNET_API -WinNet_DeactivateRouteManager( -); diff --git a/windows/winnet/src/winnet/winnet.rc b/windows/winnet/src/winnet/winnet.rc deleted file mode 100644 index 0509b4a59e..0000000000 --- a/windows/winnet/src/winnet/winnet.rc +++ /dev/null @@ -1,25 +0,0 @@ -#include "../../../version.h" - -1 VERSIONINFO -FILEVERSION MAJOR_VERSION,MINOR_VERSION,PATCH_VERSION,0 -PRODUCTVERSION MAJOR_VERSION,MINOR_VERSION,PATCH_VERSION,0 -BEGIN -BLOCK "StringFileInfo" -BEGIN - BLOCK "040904E4" - BEGIN - VALUE "CompanyName", "Mullvad VPN AB" - VALUE "FileDescription", "Mullvad VPN networking module" - VALUE "FileVersion", PRODUCT_VERSION - VALUE "InternalName", "winnet" - VALUE "LegalCopyright", "(c) 2022 Mullvad VPN AB" - VALUE "OriginalFilename", "winnet.dll" - VALUE "ProductName", "Mullvad VPN" - VALUE "ProductVersion", PRODUCT_VERSION - END -END -BLOCK "VarFileInfo" -BEGIN - VALUE "Translation", 0x409, 1252 -END -END diff --git a/windows/winnet/src/winnet/winnet.vcxproj b/windows/winnet/src/winnet/winnet.vcxproj deleted file mode 100644 index e38fd0e4b1..0000000000 --- a/windows/winnet/src/winnet/winnet.vcxproj +++ /dev/null @@ -1,314 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug Static|Win32"> - <Configuration>Debug Static</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Debug Static|x64"> - <Configuration>Debug Static</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Debug|x64"> - <Configuration>Debug</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|x64"> - <Configuration>Release</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - </ItemGroup> - <ItemGroup> - <ClCompile Include="converters.cpp" /> - <ClCompile Include="dllmain.cpp" /> - <ClCompile Include="routing\defaultroutemonitor.cpp" /> - <ClCompile Include="routing\helpers.cpp" /> - <ClCompile Include="routing\routemanager.cpp" /> - <ClCompile Include="routing\types.cpp" /> - <ClCompile Include="stdafx.cpp" /> - <ClCompile Include="winnet.cpp" /> - </ItemGroup> - <ItemGroup> - <ClInclude Include="converters.h" /> - <ClInclude Include="routing\defaultroutemonitor.h" /> - <ClInclude Include="routing\helpers.h" /> - <ClInclude Include="routing\routemanager.h" /> - <ClInclude Include="routing\types.h" /> - <ClInclude Include="stdafx.h" /> - <ClInclude Include="targetver.h" /> - <ClInclude Include="winnet.h" /> - </ItemGroup> - <ItemGroup> - <None Include="winnet.def" /> - </ItemGroup> - <ItemGroup> - <ResourceCompile Include="winnet.rc" /> - </ItemGroup> - <PropertyGroup Label="Globals"> - <VCProjectVersion>16.0</VCProjectVersion> - <ProjectGuid>{89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}</ProjectGuid> - <Keyword>Win32Proj</Keyword> - <RootNamespace>winnet</RootNamespace> - <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>DynamicLibrary</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v143</PlatformToolset> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Static|Win32'" Label="Configuration"> - <ConfigurationType>StaticLibrary</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v143</PlatformToolset> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <ConfigurationType>DynamicLibrary</ConfigurationType> - <UseDebugLibraries>false</UseDebugLibraries> - <PlatformToolset>v143</PlatformToolset> - <WholeProgramOptimization>true</WholeProgramOptimization> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> - <ConfigurationType>DynamicLibrary</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v143</PlatformToolset> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Static|x64'" Label="Configuration"> - <ConfigurationType>StaticLibrary</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v143</PlatformToolset> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> - <ConfigurationType>DynamicLibrary</ConfigurationType> - <UseDebugLibraries>false</UseDebugLibraries> - <PlatformToolset>v143</PlatformToolset> - <WholeProgramOptimization>true</WholeProgramOptimization> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - </ImportGroup> - <ImportGroup Label="Shared"> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug Static|Win32'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug Static|x64'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <LinkIncremental>true</LinkIncremental> - <OutDir>$(SolutionDir)\bin\$(Platform)-$(Configuration)\</OutDir> - <IntDir>$(SolutionDir)\bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Static|Win32'"> - <LinkIncremental>true</LinkIncremental> - <OutDir>$(SolutionDir)\bin\$(Platform)-Debug\</OutDir> - <IntDir>$(SolutionDir)\bin\temp\$(Platform)-Debug\$(ProjectName)\</IntDir> - <TargetName>$(ProjectName)-static</TargetName> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <LinkIncremental>true</LinkIncremental> - <OutDir>$(SolutionDir)\bin\$(Platform)-$(Configuration)\</OutDir> - <IntDir>$(SolutionDir)\bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Static|x64'"> - <LinkIncremental>true</LinkIncremental> - <OutDir>$(SolutionDir)\bin\$(Platform)-Debug\</OutDir> - <IntDir>$(SolutionDir)\bin\temp\$(Platform)-Debug\$(ProjectName)\</IntDir> - <TargetName>$(ProjectName)-static</TargetName> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <LinkIncremental>false</LinkIncremental> - <OutDir>$(SolutionDir)\bin\$(Platform)-$(Configuration)\</OutDir> - <IntDir>$(SolutionDir)\bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <LinkIncremental>false</LinkIncremental> - <OutDir>$(SolutionDir)\bin\$(Platform)-$(Configuration)\</OutDir> - <IntDir>$(SolutionDir)\bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir> - </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <ClCompile> - <PrecompiledHeader>Create</PrecompiledHeader> - <WarningLevel>Level4</WarningLevel> - <Optimization>Disabled</Optimization> - <SDLCheck>true</SDLCheck> - <PreprocessorDefinitions>WIN32;_DEBUG;WINNET_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <ConformanceMode>true</ConformanceMode> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - <LanguageStandard>stdcpplatest</LanguageStandard> - <AdditionalIncludeDirectories>$(ProjectDir)..\..\..\libshared\src\;$(ProjectDir)..\..\..\windows-libraries\src\;$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - </ClCompile> - <Link> - <SubSystem>Windows</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <ModuleDefinitionFile>winnet.def</ModuleDefinitionFile> - <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories> - <AdditionalDependencies>libshared.lib;libcommon.lib;Iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies> - </Link> - <PreBuildEvent> - <Command>cargo run -q --bin mullvad-version version.h > $(ProjectDir)..\..\..\version.h</Command> - </PreBuildEvent> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug Static|Win32'"> - <ClCompile> - <PrecompiledHeader>Create</PrecompiledHeader> - <WarningLevel>Level4</WarningLevel> - <Optimization>Disabled</Optimization> - <SDLCheck>true</SDLCheck> - <PreprocessorDefinitions>WINNET_STATIC;WIN32;_DEBUG;WINNET_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <ConformanceMode>true</ConformanceMode> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - <LanguageStandard>stdcpplatest</LanguageStandard> - <AdditionalIncludeDirectories>$(ProjectDir)..\..\..\libshared\src\;$(ProjectDir)..\..\..\windows-libraries\src\;$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - </ClCompile> - <Link> - <SubSystem>Windows</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <ModuleDefinitionFile>winnet.def</ModuleDefinitionFile> - <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories> - <AdditionalDependencies>libcommon.lib;Iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies> - </Link> - <Lib> - <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-Debug</AdditionalLibraryDirectories> - <AdditionalDependencies>libshared.lib;libcommon.lib;Iphlpapi.lib</AdditionalDependencies> - </Lib> - <PreBuildEvent> - <Command>cargo run -q --bin mullvad-version version.h > $(ProjectDir)..\..\..\version.h</Command> - </PreBuildEvent> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <ClCompile> - <PrecompiledHeader>Create</PrecompiledHeader> - <WarningLevel>Level4</WarningLevel> - <Optimization>Disabled</Optimization> - <SDLCheck>true</SDLCheck> - <PreprocessorDefinitions>_DEBUG;WINNET_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <ConformanceMode>true</ConformanceMode> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - <LanguageStandard>stdcpplatest</LanguageStandard> - <AdditionalIncludeDirectories>$(ProjectDir)..\..\..\libshared\src\;$(ProjectDir)..\..\..\windows-libraries\src\;$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - </ClCompile> - <Link> - <SubSystem>Windows</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <AdditionalDependencies>libshared.lib;libcommon.lib;Iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies> - <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories> - <ModuleDefinitionFile>winnet.def</ModuleDefinitionFile> - </Link> - <PreBuildEvent> - <Command>cargo run -q --bin mullvad-version version.h > $(ProjectDir)..\..\..\version.h</Command> - </PreBuildEvent> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug Static|x64'"> - <ClCompile> - <PrecompiledHeader>Create</PrecompiledHeader> - <WarningLevel>Level4</WarningLevel> - <Optimization>Disabled</Optimization> - <SDLCheck>true</SDLCheck> - <PreprocessorDefinitions>WINNET_STATIC;_DEBUG;WINNET_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <ConformanceMode>true</ConformanceMode> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - <LanguageStandard>stdcpplatest</LanguageStandard> - <AdditionalIncludeDirectories>$(ProjectDir)..\..\..\libshared\src\;$(ProjectDir)..\..\..\windows-libraries\src\;$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - </ClCompile> - <Link> - <SubSystem>Windows</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <AdditionalDependencies>libcommon.lib;Iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies> - <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories> - <ModuleDefinitionFile>winnet.def</ModuleDefinitionFile> - </Link> - <Lib> - <AdditionalDependencies>libshared.lib;libcommon.lib;Iphlpapi.lib</AdditionalDependencies> - <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-Debug</AdditionalLibraryDirectories> - </Lib> - <PreBuildEvent> - <Command>cargo run -q --bin mullvad-version version.h > $(ProjectDir)..\..\..\version.h</Command> - </PreBuildEvent> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <ClCompile> - <PrecompiledHeader>Create</PrecompiledHeader> - <WarningLevel>Level4</WarningLevel> - <Optimization>MaxSpeed</Optimization> - <FunctionLevelLinking>true</FunctionLevelLinking> - <IntrinsicFunctions>true</IntrinsicFunctions> - <SDLCheck>true</SDLCheck> - <PreprocessorDefinitions>WIN32;NDEBUG;WINNET_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <ConformanceMode>true</ConformanceMode> - <RuntimeLibrary>MultiThreaded</RuntimeLibrary> - <LanguageStandard>stdcpplatest</LanguageStandard> - <AdditionalIncludeDirectories>$(ProjectDir)..\..\..\libshared\src\;$(ProjectDir)..\..\..\windows-libraries\src\;$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - </ClCompile> - <Link> - <SubSystem>Windows</SubSystem> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <OptimizeReferences>true</OptimizeReferences> - <GenerateDebugInformation>true</GenerateDebugInformation> - <ModuleDefinitionFile>winnet.def</ModuleDefinitionFile> - <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories> - <AdditionalDependencies>libshared.lib;libcommon.lib;Iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies> - </Link> - <PreBuildEvent> - <Command>cargo run -q --bin mullvad-version version.h > $(ProjectDir)..\..\..\version.h</Command> - </PreBuildEvent> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <ClCompile> - <PrecompiledHeader>Create</PrecompiledHeader> - <WarningLevel>Level4</WarningLevel> - <Optimization>MaxSpeed</Optimization> - <FunctionLevelLinking>true</FunctionLevelLinking> - <IntrinsicFunctions>true</IntrinsicFunctions> - <SDLCheck>true</SDLCheck> - <PreprocessorDefinitions>NDEBUG;WINNET_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <ConformanceMode>true</ConformanceMode> - <RuntimeLibrary>MultiThreaded</RuntimeLibrary> - <LanguageStandard>stdcpplatest</LanguageStandard> - <AdditionalIncludeDirectories>$(ProjectDir)..\..\..\libshared\src\;$(ProjectDir)..\..\..\windows-libraries\src\;$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - </ClCompile> - <Link> - <SubSystem>Windows</SubSystem> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <OptimizeReferences>true</OptimizeReferences> - <GenerateDebugInformation>true</GenerateDebugInformation> - <ModuleDefinitionFile>winnet.def</ModuleDefinitionFile> - <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories> - <AdditionalDependencies>libshared.lib;libcommon.lib;Iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies> - </Link> - <PreBuildEvent> - <Command>cargo run -q --bin mullvad-version version.h > $(ProjectDir)..\..\..\version.h</Command> - </PreBuildEvent> - </ItemDefinitionGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - </ImportGroup> -</Project>
\ No newline at end of file diff --git a/windows/winnet/src/winnet/winnet.vcxproj.filters b/windows/winnet/src/winnet/winnet.vcxproj.filters deleted file mode 100644 index b9a9053165..0000000000 --- a/windows/winnet/src/winnet/winnet.vcxproj.filters +++ /dev/null @@ -1,50 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup> - <ClCompile Include="dllmain.cpp" /> - <ClCompile Include="stdafx.cpp" /> - <ClCompile Include="winnet.cpp" /> - <ClCompile Include="routing\types.cpp"> - <Filter>routing</Filter> - </ClCompile> - <ClCompile Include="routing\helpers.cpp"> - <Filter>routing</Filter> - </ClCompile> - <ClCompile Include="routing\defaultroutemonitor.cpp"> - <Filter>routing</Filter> - </ClCompile> - <ClCompile Include="routing\routemanager.cpp"> - <Filter>routing</Filter> - </ClCompile> - <ClCompile Include="converters.cpp" /> - </ItemGroup> - <ItemGroup> - <ClInclude Include="stdafx.h" /> - <ClInclude Include="targetver.h" /> - <ClInclude Include="winnet.h" /> - <ClInclude Include="routing\types.h"> - <Filter>routing</Filter> - </ClInclude> - <ClInclude Include="routing\helpers.h"> - <Filter>routing</Filter> - </ClInclude> - <ClInclude Include="routing\defaultroutemonitor.h"> - <Filter>routing</Filter> - </ClInclude> - <ClInclude Include="routing\routemanager.h"> - <Filter>routing</Filter> - </ClInclude> - <ClInclude Include="converters.h" /> - </ItemGroup> - <ItemGroup> - <None Include="winnet.def" /> - </ItemGroup> - <ItemGroup> - <ResourceCompile Include="winnet.rc" /> - </ItemGroup> - <ItemGroup> - <Filter Include="routing"> - <UniqueIdentifier>{8df22cc6-597f-4342-bc57-7647393084be}</UniqueIdentifier> - </Filter> - </ItemGroup> -</Project>
\ No newline at end of file diff --git a/windows/winnet/winnet.sln b/windows/winnet/winnet.sln deleted file mode 100644 index d77c5de04e..0000000000 --- a/windows/winnet/winnet.sln +++ /dev/null @@ -1,72 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.32014.148 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winnet", "src\winnet\winnet.vcxproj", "{89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}" - ProjectSection(ProjectDependencies) = postProject - {B52E2D10-A94A-4605-914A-2DCEF6A757EF} = {B52E2D10-A94A-4605-914A-2DCEF6A757EF} - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D} = {EE69EA4A-CF71-4B88-866B-957F60C4CE0D} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcommon", "..\windows-libraries\src\libcommon\libcommon.vcxproj", "{B52E2D10-A94A-4605-914A-2DCEF6A757EF}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libshared", "..\libshared\src\libshared\libshared.vcxproj", "{EE69EA4A-CF71-4B88-866B-957F60C4CE0D}" - ProjectSection(ProjectDependencies) = postProject - {B52E2D10-A94A-4605-914A-2DCEF6A757EF} = {B52E2D10-A94A-4605-914A-2DCEF6A757EF} - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug Static|x64 = Debug Static|x64 - Debug Static|x86 = Debug Static|x86 - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug Static|x64.ActiveCfg = Debug Static|x64 - {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug Static|x64.Build.0 = Debug Static|x64 - {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug Static|x86.ActiveCfg = Debug Static|Win32 - {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug Static|x86.Build.0 = Debug Static|Win32 - {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug|x64.ActiveCfg = Debug|x64 - {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug|x64.Build.0 = Debug|x64 - {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug|x86.ActiveCfg = Debug|Win32 - {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug|x86.Build.0 = Debug|Win32 - {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Release|x64.ActiveCfg = Release|x64 - {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Release|x64.Build.0 = Release|x64 - {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Release|x86.ActiveCfg = Release|Win32 - {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Release|x86.Build.0 = Release|Win32 - {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug Static|x64.ActiveCfg = Debug|x64 - {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug Static|x64.Build.0 = Debug|x64 - {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug Static|x86.ActiveCfg = Debug|Win32 - {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug Static|x86.Build.0 = Debug|Win32 - {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug|x64.ActiveCfg = Debug|x64 - {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug|x64.Build.0 = Debug|x64 - {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug|x86.ActiveCfg = Debug|Win32 - {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug|x86.Build.0 = Debug|Win32 - {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Release|x64.ActiveCfg = Release|x64 - {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Release|x64.Build.0 = Release|x64 - {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Release|x86.ActiveCfg = Release|Win32 - {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Release|x86.Build.0 = Release|Win32 - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D}.Debug Static|x64.ActiveCfg = Debug|x64 - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D}.Debug Static|x64.Build.0 = Debug|x64 - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D}.Debug Static|x86.ActiveCfg = Debug|Win32 - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D}.Debug Static|x86.Build.0 = Debug|Win32 - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D}.Debug|x64.ActiveCfg = Debug|x64 - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D}.Debug|x64.Build.0 = Debug|x64 - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D}.Debug|x86.ActiveCfg = Debug|Win32 - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D}.Debug|x86.Build.0 = Debug|Win32 - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D}.Release|x64.ActiveCfg = Release|x64 - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D}.Release|x64.Build.0 = Release|x64 - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D}.Release|x86.ActiveCfg = Release|Win32 - {EE69EA4A-CF71-4B88-866B-957F60C4CE0D}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {B019612F-9475-4A56-A836-B74F90CE2E78} - EndGlobalSection -EndGlobal |
