diff options
| -rw-r--r-- | talpid-core/src/split_tunnel/windows/mod.rs | 16 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/connected_state.rs | 241 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/connecting_state.rs | 14 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/disconnected_state.rs | 25 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/error_state.rs | 12 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/mod.rs | 10 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/windows.rs | 209 |
7 files changed, 313 insertions, 214 deletions
diff --git a/talpid-core/src/split_tunnel/windows/mod.rs b/talpid-core/src/split_tunnel/windows/mod.rs index 6bf8dbc960..7d6ec528f3 100644 --- a/talpid-core/src/split_tunnel/windows/mod.rs +++ b/talpid-core/src/split_tunnel/windows/mod.rs @@ -240,6 +240,22 @@ impl SplitTunnel { internet_ipv4: Ipv4Addr, internet_ipv6: Option<Ipv6Addr>, ) -> Result<(), Error> { + log::debug!( + "Register IPs: {} {:?} {} {:?}", + tunnel_ipv4, + tunnel_ipv6, + internet_ipv4, + internet_ipv6 + ); + + // If there is no valid internet IPv4 address, ignore any tunnel addresses. + // This should only be the case if a reserved tunnel IP is used to keep the driver engaged. + let tunnel_ipv4 = if internet_ipv4.is_unspecified() { + Ipv4Addr::new(0, 0, 0, 0) + } else { + tunnel_ipv4 + }; + self.handle .register_ips(tunnel_ipv4, tunnel_ipv6, internet_ipv4, internet_ipv6) .map_err(Error::RegisterIps) diff --git a/talpid-core/src/tunnel_state_machine/connected_state.rs b/talpid-core/src/tunnel_state_machine/connected_state.rs index d7bf24b49e..2511713fc5 100644 --- a/talpid-core/src/tunnel_state_machine/connected_state.rs +++ b/talpid-core/src/tunnel_state_machine/connected_state.rs @@ -1,26 +1,21 @@ +#[cfg(windows)] +use super::windows; use super::{ AfterDisconnect, ConnectingState, DisconnectingState, ErrorState, EventConsequence, EventResult, SharedTunnelStateValues, TunnelCommand, TunnelCommandReceiver, TunnelState, TunnelStateTransition, TunnelStateWrapper, }; +#[cfg(windows)] +use crate::split_tunnel; use crate::{ firewall::FirewallPolicy, tunnel::{CloseHandle, TunnelEvent, TunnelMetadata}, }; -#[cfg(windows)] -use crate::{ - split_tunnel::{self, SplitTunnel}, - winnet::{self, get_best_default_route, interface_luid_to_ip, WinNetAddrFamily}, -}; use cfg_if::cfg_if; use futures::{channel::mpsc, stream::Fuse, StreamExt}; -use std::net::IpAddr; #[cfg(windows)] -use std::{ - ffi::OsStr, - net::{Ipv4Addr, Ipv6Addr}, - sync::{Arc, Mutex}, -}; +use std::ffi::OsStr; +use std::net::IpAddr; use talpid_types::{ net::TunnelParameters, tunnel::{ErrorStateCause, FirewallPolicyError}, @@ -127,137 +122,6 @@ impl ConnectedState { } } - #[cfg(target_os = "windows")] - pub 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, - ) { - // Update the "internet interface" IP when best default route changes - let ctx = &mut *(ctx as *mut SplitTunnelDefaultRouteChangeHandlerContext); - - let result = match event_type { - winnet::WinNetDefaultRouteChangeEventType::DefaultRouteChanged => { - let ip = interface_luid_to_ip(address_family.clone(), default_route.interface_luid); - - // TODO: Should we block here? - let ip = match ip { - Ok(Some(ip)) => ip, - Ok(None) => { - log::error!("Failed to obtain new default route address: none found",); - // Early return - return; - } - Err(error) => { - log::error!( - "{}", - error.display_chain_with_msg( - "Failed to obtain new default route address" - ) - ); - // Early return - return; - } - }; - - match address_family { - WinNetAddrFamily::IPV4 => { - let ip = Ipv4Addr::from(ip); - ctx.internet_ipv4 = ip; - } - WinNetAddrFamily::IPV6 => { - let ip = Ipv6Addr::from(ip); - ctx.internet_ipv6 = Some(ip); - } - } - - ctx.register_ips() - } - // no default route - winnet::WinNetDefaultRouteChangeEventType::DefaultRouteRemoved => { - match address_family { - WinNetAddrFamily::IPV4 => { - ctx.internet_ipv4 = Ipv4Addr::new(0, 0, 0, 0); - } - WinNetAddrFamily::IPV6 => { - ctx.internet_ipv6 = None; - } - } - ctx.register_ips() - } - }; - - if let Err(error) = result { - // TODO: Should we block here? - log::error!( - "{}", - error.display_chain_with_msg( - "Failed to register new addresses in split tunnel driver" - ) - ); - } - } - - #[cfg(windows)] - fn update_split_tunnel_addresses( - &self, - shared_values: &mut SharedTunnelStateValues, - ) -> Result<(), BoxedError> { - // Identify tunnel IP addresses - // TODO: Multiple IP addresses? - let mut tunnel_ipv4 = None; - let mut tunnel_ipv6 = None; - - for ip in &self.metadata.ips { - match ip { - IpAddr::V4(address) => tunnel_ipv4 = Some(address.clone()), - IpAddr::V6(address) => tunnel_ipv6 = Some(address.clone()), - } - } - - // Identify IP address that gives us Internet access - let internet_ipv4 = get_best_default_route(WinNetAddrFamily::IPV4) - .map_err(BoxedError::new)? - .map(|route| interface_luid_to_ip(WinNetAddrFamily::IPV4, route.interface_luid)) - .transpose() - .map_err(BoxedError::new)? - .flatten(); - let internet_ipv6 = get_best_default_route(WinNetAddrFamily::IPV6) - .map_err(BoxedError::new)? - .map(|route| interface_luid_to_ip(WinNetAddrFamily::IPV6, route.interface_luid)) - .transpose() - .map_err(BoxedError::new)? - .flatten(); - - let tunnel_ipv4 = tunnel_ipv4.unwrap_or(Ipv4Addr::new(0, 0, 0, 0)); - let internet_ipv4 = Ipv4Addr::from(internet_ipv4.unwrap_or_default()); - let internet_ipv6 = internet_ipv6.map(|addr| Ipv6Addr::from(addr)); - - let context = SplitTunnelDefaultRouteChangeHandlerContext::new( - shared_values.split_tunnel.clone(), - tunnel_ipv4, - tunnel_ipv6, - internet_ipv4, - internet_ipv6, - ); - - shared_values - .split_tunnel - .lock() - .expect("Thread unexpectedly panicked while holding the mutex") - .register_ips(tunnel_ipv4, tunnel_ipv6, internet_ipv4, internet_ipv6) - .map_err(BoxedError::new)?; - - #[cfg(target_os = "windows")] - shared_values.route_manager.add_default_route_callback( - Some(Self::split_tunnel_default_route_change_handler), - context, - ); - - Ok(()) - } - fn set_dns(&self, shared_values: &mut SharedTunnelStateValues) -> Result<(), BoxedError> { let dns_ips = self.get_dns_servers(shared_values); shared_values @@ -312,24 +176,6 @@ impl ConnectedState { Self::reset_dns(shared_values); Self::reset_routes(shared_values); - #[cfg(windows)] - if let Err(error) = shared_values - .split_tunnel - .lock() - .expect("Thread unexpectedly panicked while holding the mutex") - .register_ips( - Ipv4Addr::new(0, 0, 0, 0), - None, - Ipv4Addr::new(0, 0, 0, 0), - None, - ) - { - log::error!( - "{}", - error.display_chain_with_msg("Failed to unregister IP addresses") - ); - } - EventConsequence::NewState(DisconnectingState::enter( shared_values, (self.close_handle, self.tunnel_close_event, after_disconnect), @@ -483,6 +329,27 @@ impl TunnelState for ConnectedState { let connected_state = ConnectedState::from(bootstrap); let tunnel_endpoint = connected_state.tunnel_parameters.get_tunnel_endpoint(); + #[cfg(target_os = "windows")] + if let Err(error) = + windows::update_split_tunnel_addresses(Some(&connected_state.metadata), shared_values) + { + log::error!( + "{}", + error.display_chain_with_msg( + "Failed to register addresses with split tunnel driver" + ) + ); + + return DisconnectingState::enter( + shared_values, + ( + connected_state.close_handle, + connected_state.tunnel_close_event, + AfterDisconnect::Block(ErrorStateCause::StartTunnelError), + ), + ); + } + if let Err(error) = connected_state.set_firewall_policy(shared_values) { DisconnectingState::enter( shared_values, @@ -503,19 +370,6 @@ impl TunnelState for ConnectedState { ), ) } else { - #[cfg(windows)] - if let Err(error) = connected_state.update_split_tunnel_addresses(shared_values) { - log::error!("{}", error.display_chain()); - return DisconnectingState::enter( - shared_values, - ( - connected_state.close_handle, - connected_state.tunnel_close_event, - AfterDisconnect::Block(ErrorStateCause::StartTunnelError), - ), - ); - } - ( TunnelStateWrapper::from(connected_state), TunnelStateTransition::Connected(tunnel_endpoint), @@ -550,44 +404,3 @@ impl TunnelState for ConnectedState { } } } - -#[cfg(target_os = "windows")] -struct SplitTunnelDefaultRouteChangeHandlerContext { - split_tunnel: Arc<Mutex<SplitTunnel>>, - pub tunnel_ipv4: Ipv4Addr, - pub tunnel_ipv6: Option<Ipv6Addr>, - pub internet_ipv4: Ipv4Addr, - pub internet_ipv6: Option<Ipv6Addr>, -} - -#[cfg(target_os = "windows")] -impl SplitTunnelDefaultRouteChangeHandlerContext { - pub fn new( - split_tunnel: Arc<Mutex<SplitTunnel>>, - tunnel_ipv4: Ipv4Addr, - tunnel_ipv6: Option<Ipv6Addr>, - internet_ipv4: Ipv4Addr, - internet_ipv6: Option<Ipv6Addr>, - ) -> Self { - SplitTunnelDefaultRouteChangeHandlerContext { - split_tunnel, - tunnel_ipv4, - tunnel_ipv6, - internet_ipv4, - internet_ipv6, - } - } - - pub fn register_ips(&self) -> Result<(), split_tunnel::Error> { - let split_tunnel = self - .split_tunnel - .lock() - .expect("Thread unexpectedly panicked while holding the mutex"); - split_tunnel.register_ips( - self.tunnel_ipv4, - self.tunnel_ipv6, - self.internet_ipv4, - self.internet_ipv6, - ) - } -} diff --git a/talpid-core/src/tunnel_state_machine/connecting_state.rs b/talpid-core/src/tunnel_state_machine/connecting_state.rs index 34e9eeb2be..cb337c02ab 100644 --- a/talpid-core/src/tunnel_state_machine/connecting_state.rs +++ b/talpid-core/src/tunnel_state_machine/connecting_state.rs @@ -33,6 +33,8 @@ use talpid_types::{ }; #[cfg(windows)] +use super::windows; +#[cfg(windows)] use crate::{routing, winnet}; #[cfg(target_os = "android")] @@ -458,6 +460,18 @@ impl TunnelState for ConnectingState { ErrorState::enter(shared_values, ErrorStateCause::TunnelParameterError(err)) } Ok(tunnel_parameters) => { + #[cfg(windows)] + if let Err(error) = windows::update_split_tunnel_addresses(None, shared_values) { + log::error!( + "{}", + error.display_chain_with_msg( + "Failed to register addresses with split tunnel driver" + ) + ); + + return ErrorState::enter(shared_values, ErrorStateCause::StartTunnelError); + } + if let Err(error) = Self::set_firewall_policy(shared_values, &tunnel_parameters, &None) { diff --git a/talpid-core/src/tunnel_state_machine/disconnected_state.rs b/talpid-core/src/tunnel_state_machine/disconnected_state.rs index cfa7794af7..e4b03bc313 100644 --- a/talpid-core/src/tunnel_state_machine/disconnected_state.rs +++ b/talpid-core/src/tunnel_state_machine/disconnected_state.rs @@ -1,3 +1,5 @@ +#[cfg(windows)] +use super::windows; use super::{ ConnectingState, ErrorState, EventConsequence, SharedTunnelStateValues, TunnelCommand, TunnelCommandReceiver, TunnelState, TunnelStateTransition, TunnelStateWrapper, @@ -52,6 +54,25 @@ impl DisconnectedState { .expect("Thread unexpectedly panicked while holding the mutex"); split_tunnel.set_paths(paths) } + + #[cfg(windows)] + fn register_split_tunnel_addresses( + shared_values: &mut SharedTunnelStateValues, + should_reset_firewall: bool, + ) { + if should_reset_firewall && !shared_values.block_when_disconnected { + windows::clear_split_tunnel_addresses(shared_values); + } else { + if let Err(error) = windows::update_split_tunnel_addresses(None, shared_values) { + log::error!( + "{}", + error.display_chain_with_msg( + "Failed to register addresses with split tunnel driver" + ) + ); + } + } + } } impl TunnelState for DisconnectedState { @@ -61,6 +82,8 @@ impl TunnelState for DisconnectedState { shared_values: &mut SharedTunnelStateValues, should_reset_firewall: Self::Bootstrap, ) -> (TunnelStateWrapper, TunnelStateTransition) { + #[cfg(windows)] + Self::register_split_tunnel_addresses(shared_values, should_reset_firewall); Self::set_firewall_policy(shared_values, should_reset_firewall); #[cfg(target_os = "linux")] shared_values.reset_connectivity_check(); @@ -114,6 +137,8 @@ impl TunnelState for DisconnectedState { Some(TunnelCommand::BlockWhenDisconnected(block_when_disconnected)) => { if shared_values.block_when_disconnected != block_when_disconnected { shared_values.block_when_disconnected = block_when_disconnected; + #[cfg(windows)] + Self::register_split_tunnel_addresses(shared_values, true); Self::set_firewall_policy(shared_values, true); } SameState(self.into()) diff --git a/talpid-core/src/tunnel_state_machine/error_state.rs b/talpid-core/src/tunnel_state_machine/error_state.rs index 6d772a62b8..3b2368ada8 100644 --- a/talpid-core/src/tunnel_state_machine/error_state.rs +++ b/talpid-core/src/tunnel_state_machine/error_state.rs @@ -1,3 +1,5 @@ +#[cfg(windows)] +use super::windows; use super::{ ConnectingState, DisconnectedState, EventConsequence, SharedTunnelStateValues, TunnelCommand, TunnelCommandReceiver, TunnelState, TunnelStateTransition, TunnelStateWrapper, @@ -86,6 +88,16 @@ impl TunnelState for ErrorState { shared_values: &mut SharedTunnelStateValues, block_reason: Self::Bootstrap, ) -> (TunnelStateWrapper, TunnelStateTransition) { + #[cfg(windows)] + if let Err(error) = windows::update_split_tunnel_addresses(None, shared_values) { + log::error!( + "{}", + error.display_chain_with_msg( + "Failed to register addresses with split tunnel driver" + ) + ); + } + #[cfg(not(target_os = "android"))] let block_failure = Self::set_firewall_policy(shared_values).err(); #[cfg(target_os = "android")] diff --git a/talpid-core/src/tunnel_state_machine/mod.rs b/talpid-core/src/tunnel_state_machine/mod.rs index 969c2116a1..24abecf0d8 100644 --- a/talpid-core/src/tunnel_state_machine/mod.rs +++ b/talpid-core/src/tunnel_state_machine/mod.rs @@ -13,6 +13,8 @@ use self::{ }; #[cfg(windows)] use crate::split_tunnel; +#[cfg(windows)] +use crate::winnet::WinNetCallbackHandle; use crate::{ dns::DnsMonitor, firewall::{Firewall, FirewallArguments}, @@ -46,6 +48,9 @@ use talpid_types::{ tunnel::{ErrorStateCause, ParameterGenerationError, TunnelStateTransition}, }; +#[cfg(target_os = "windows")] +mod windows; + /// Errors that can happen when setting up or using the state machine. #[derive(err_derive::Error, Debug)] pub enum Error { @@ -283,6 +288,8 @@ impl TunnelStateMachine { connectivity_check_was_enabled: None, #[cfg(windows)] split_tunnel: Arc::new(Mutex::new(split_tunnel)), + #[cfg(windows)] + st_route_handler: None, }; let (initial_state, _) = DisconnectedState::enter(&mut shared_values, reset_firewall); @@ -367,6 +374,9 @@ struct SharedTunnelStateValues { /// Management of excluded apps. #[cfg(windows)] split_tunnel: Arc<Mutex<split_tunnel::SplitTunnel>>, + /// Management of excluded apps. + #[cfg(windows)] + st_route_handler: Option<WinNetCallbackHandle>, } impl SharedTunnelStateValues { diff --git a/talpid-core/src/tunnel_state_machine/windows.rs b/talpid-core/src/tunnel_state_machine/windows.rs new file mode 100644 index 0000000000..3ea26bf819 --- /dev/null +++ b/talpid-core/src/tunnel_state_machine/windows.rs @@ -0,0 +1,209 @@ +#![cfg(windows)] + +use super::SharedTunnelStateValues; +use crate::{ + split_tunnel::{self, SplitTunnel}, + tunnel::TunnelMetadata, + winnet::{self, get_best_default_route, interface_luid_to_ip, WinNetAddrFamily}, +}; +use lazy_static::lazy_static; +use std::{ + net::{IpAddr, Ipv4Addr, Ipv6Addr}, + sync::{Arc, Mutex}, +}; +use talpid_types::{BoxedError, ErrorExt}; + +lazy_static! { + static ref RESERVED_IP_V4: Ipv4Addr = "192.0.2.123".parse().unwrap(); +} + + +pub(super) fn update_split_tunnel_addresses( + metadata: Option<&TunnelMetadata>, + shared_values: &mut SharedTunnelStateValues, +) -> Result<(), BoxedError> { + let mut tunnel_ipv4 = None; + let mut tunnel_ipv6 = None; + + if let Some(metadata) = metadata { + for ip in &metadata.ips { + match ip { + IpAddr::V4(address) => tunnel_ipv4 = Some(address.clone()), + IpAddr::V6(address) => tunnel_ipv6 = Some(address.clone()), + } + } + } + + // Identify IP address that gives us Internet access + let internet_ipv4 = get_best_default_route(WinNetAddrFamily::IPV4) + .map_err(BoxedError::new)? + .map(|route| interface_luid_to_ip(WinNetAddrFamily::IPV4, route.interface_luid)) + .transpose() + .map_err(BoxedError::new)? + .flatten(); + let internet_ipv6 = get_best_default_route(WinNetAddrFamily::IPV6) + .map_err(BoxedError::new)? + .map(|route| interface_luid_to_ip(WinNetAddrFamily::IPV6, route.interface_luid)) + .transpose() + .map_err(BoxedError::new)? + .flatten(); + + let tunnel_ipv4 = tunnel_ipv4.unwrap_or(*RESERVED_IP_V4); + let internet_ipv4 = Ipv4Addr::from(internet_ipv4.unwrap_or_default()); + let internet_ipv6 = internet_ipv6.map(|addr| Ipv6Addr::from(addr)); + + let context = SplitTunnelDefaultRouteChangeHandlerContext::new( + shared_values.split_tunnel.clone(), + tunnel_ipv4, + tunnel_ipv6, + internet_ipv4, + internet_ipv6, + ); + + shared_values.st_route_handler = None; + + shared_values + .split_tunnel + .lock() + .expect("Thread unexpectedly panicked while holding the mutex") + .register_ips(tunnel_ipv4, tunnel_ipv6, internet_ipv4, internet_ipv6) + .map_err(BoxedError::new)?; + + // FIXME: Do this via the route manager + shared_values.st_route_handler = Some( + crate::winnet::add_default_route_change_callback( + Some(split_tunnel_default_route_change_handler), + context, + ) + .map_err(BoxedError::new)?, + ); + + Ok(()) +} + +pub(super) fn clear_split_tunnel_addresses(shared_values: &mut SharedTunnelStateValues) { + shared_values.st_route_handler = None; + + if let Err(error) = shared_values + .split_tunnel + .lock() + .expect("Thread unexpectedly panicked while holding the mutex") + .register_ips( + Ipv4Addr::new(0, 0, 0, 0), + None, + Ipv4Addr::new(0, 0, 0, 0), + None, + ) + { + log::error!( + "{}", + error.display_chain_with_msg("Failed to unregister IP addresses") + ); + } +} + +pub 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, +) { + // Update the "internet interface" IP when best default route changes + let ctx = &mut *(ctx as *mut SplitTunnelDefaultRouteChangeHandlerContext); + + let result = match event_type { + winnet::WinNetDefaultRouteChangeEventType::DefaultRouteChanged => { + let ip = interface_luid_to_ip(address_family.clone(), default_route.interface_luid); + + // TODO: Should we block here? + let ip = match ip { + Ok(Some(ip)) => ip, + Ok(None) => { + log::error!("Failed to obtain new default route address: none found",); + // Early return + return; + } + Err(error) => { + log::error!( + "{}", + error.display_chain_with_msg("Failed to obtain new default route address") + ); + // Early return + return; + } + }; + + match address_family { + WinNetAddrFamily::IPV4 => { + let ip = Ipv4Addr::from(ip); + ctx.internet_ipv4 = ip; + } + WinNetAddrFamily::IPV6 => { + let ip = Ipv6Addr::from(ip); + ctx.internet_ipv6 = Some(ip); + } + } + + ctx.register_ips() + } + // no default route + winnet::WinNetDefaultRouteChangeEventType::DefaultRouteRemoved => { + match address_family { + WinNetAddrFamily::IPV4 => { + ctx.internet_ipv4 = Ipv4Addr::new(0, 0, 0, 0); + } + WinNetAddrFamily::IPV6 => { + ctx.internet_ipv6 = None; + } + } + ctx.register_ips() + } + }; + + if let Err(error) = result { + // TODO: Should we block here? + log::error!( + "{}", + error.display_chain_with_msg("Failed to register new addresses in split tunnel driver") + ); + } +} + +struct SplitTunnelDefaultRouteChangeHandlerContext { + split_tunnel: Arc<Mutex<SplitTunnel>>, + pub tunnel_ipv4: Ipv4Addr, + pub tunnel_ipv6: Option<Ipv6Addr>, + pub internet_ipv4: Ipv4Addr, + pub internet_ipv6: Option<Ipv6Addr>, +} + +impl SplitTunnelDefaultRouteChangeHandlerContext { + pub fn new( + split_tunnel: Arc<Mutex<SplitTunnel>>, + tunnel_ipv4: Ipv4Addr, + tunnel_ipv6: Option<Ipv6Addr>, + internet_ipv4: Ipv4Addr, + internet_ipv6: Option<Ipv6Addr>, + ) -> Self { + SplitTunnelDefaultRouteChangeHandlerContext { + split_tunnel, + tunnel_ipv4, + tunnel_ipv6, + internet_ipv4, + internet_ipv6, + } + } + + pub fn register_ips(&self) -> Result<(), split_tunnel::Error> { + let split_tunnel = self + .split_tunnel + .lock() + .expect("Thread unexpectedly panicked while holding the mutex"); + split_tunnel.register_ips( + self.tunnel_ipv4, + self.tunnel_ipv6, + self.internet_ipv4, + self.internet_ipv6, + ) + } +} |
