diff options
| author | David Lönnhager <david.l@mullvad.net> | 2020-12-07 20:31:40 +0100 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2021-01-04 16:50:18 +0100 |
| commit | d9baa6bf9d98858d9f5bae95740b9d5ecb192c0f (patch) | |
| tree | 1ce85578b7bb8ca1dddad78120231a1c28113146 /talpid-core/src | |
| parent | 07d363b919ee0c9e33f444475361194a29f37216 (diff) | |
| download | mullvadvpn-d9baa6bf9d98858d9f5bae95740b9d5ecb192c0f.tar.xz mullvadvpn-d9baa6bf9d98858d9f5bae95740b9d5ecb192c0f.zip | |
Unblock API endpoint while connecting or blocked
Diffstat (limited to 'talpid-core/src')
| -rw-r--r-- | talpid-core/src/firewall/linux.rs | 29 | ||||
| -rw-r--r-- | talpid-core/src/firewall/macos.rs | 24 | ||||
| -rw-r--r-- | talpid-core/src/firewall/mod.rs | 16 | ||||
| -rw-r--r-- | talpid-core/src/firewall/windows.rs | 64 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/tun_provider/android/mod.rs | 38 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/connected_state.rs | 7 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/connecting_state.rs | 17 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/disconnected_state.rs | 10 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/disconnecting_state.rs | 21 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/error_state.rs | 20 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/mod.rs | 28 |
11 files changed, 254 insertions, 20 deletions
diff --git a/talpid-core/src/firewall/linux.rs b/talpid-core/src/firewall/linux.rs index 3c252313ce..04bd00777d 100644 --- a/talpid-core/src/firewall/linux.rs +++ b/talpid-core/src/firewall/linux.rs @@ -531,10 +531,12 @@ impl<'a> PolicyBatch<'a> { peer_endpoint, pingable_hosts, allow_lan, + allowed_endpoint, use_fwmark, } => { self.add_allow_icmp_pingable_hosts(&pingable_hosts); - self.add_allow_endpoint_rules(peer_endpoint, *use_fwmark); + self.add_allow_tunnel_endpoint_rules(peer_endpoint, *use_fwmark); + self.add_allow_endpoint_rules(allowed_endpoint); // Important to block DNS after allow relay rule (so the relay can operate // over port 53) but before allow LAN (so DNS does not leak to the LAN) @@ -548,7 +550,7 @@ impl<'a> PolicyBatch<'a> { dns_servers, use_fwmark, } => { - self.add_allow_endpoint_rules(peer_endpoint, *use_fwmark); + self.add_allow_tunnel_endpoint_rules(peer_endpoint, *use_fwmark); self.add_allow_dns_rules(tunnel, &dns_servers, TransportProtocol::Udp)?; self.add_allow_dns_rules(tunnel, &dns_servers, TransportProtocol::Tcp)?; // Important to block DNS *before* we allow the tunnel and allow LAN. So DNS @@ -560,7 +562,12 @@ impl<'a> PolicyBatch<'a> { } *allow_lan } - FirewallPolicy::Blocked { allow_lan } => { + FirewallPolicy::Blocked { + allow_lan, + allowed_endpoint, + } => { + self.add_allow_endpoint_rules(allowed_endpoint); + // Important to drop DNS before allowing LAN (to stop DNS leaking to the LAN) self.add_drop_dns_rule(); *allow_lan @@ -582,7 +589,7 @@ impl<'a> PolicyBatch<'a> { Ok(()) } - fn add_allow_endpoint_rules(&mut self, endpoint: &Endpoint, use_fwmark: bool) { + fn add_allow_tunnel_endpoint_rules(&mut self, endpoint: &Endpoint, use_fwmark: bool) { let mut in_rule = Rule::new(&self.in_chain); check_endpoint(&mut in_rule, End::Src, endpoint); @@ -608,6 +615,20 @@ impl<'a> PolicyBatch<'a> { self.batch.add(&out_rule, nftnl::MsgType::Add); } + fn add_allow_endpoint_rules(&mut self, endpoint: &Endpoint) { + let mut in_rule = Rule::new(&self.in_chain); + check_endpoint(&mut in_rule, End::Src, endpoint); + add_verdict(&mut in_rule, &Verdict::Accept); + + self.batch.add(&in_rule, nftnl::MsgType::Add); + + let mut out_rule = Rule::new(&self.out_chain); + check_endpoint(&mut out_rule, End::Dst, endpoint); + add_verdict(&mut out_rule, &Verdict::Accept); + + self.batch.add(&out_rule, nftnl::MsgType::Add); + } + fn add_allow_icmp_pingable_hosts(&mut self, pingable_hosts: &[IpAddr]) { for host in pingable_hosts { let icmp_proto = match &host { diff --git a/talpid-core/src/firewall/macos.rs b/talpid-core/src/firewall/macos.rs index dfdc1e31fc..2e23c99dd2 100644 --- a/talpid-core/src/firewall/macos.rs +++ b/talpid-core/src/firewall/macos.rs @@ -98,9 +98,11 @@ impl Firewall { FirewallPolicy::Connecting { peer_endpoint, allow_lan, + allowed_endpoint, pingable_hosts, } => { let mut rules = vec![self.get_allow_relay_rule(peer_endpoint)?]; + rules.push(self.get_allowed_endpoint_rule(allowed_endpoint)?); rules.extend(self.get_allow_pingable_hosts(&pingable_hosts)?); if allow_lan { // Important to block DNS after allow relay rule (so the relay can operate @@ -136,8 +138,12 @@ impl Firewall { Ok(rules) } - FirewallPolicy::Blocked { allow_lan } => { + FirewallPolicy::Blocked { + allow_lan, + allowed_endpoint, + } => { let mut rules = Vec::new(); + rules.push(self.get_allowed_endpoint_rule(allowed_endpoint)?); if allow_lan { // Important to block DNS before allow LAN (so DNS does not leak to the LAN) rules.append(&mut self.get_block_dns_rules()?); @@ -247,6 +253,22 @@ impl Firewall { .build()?) } + fn get_allowed_endpoint_rule( + &self, + allowed_endpoint: net::Endpoint, + ) -> Result<pfctl::FilterRule> { + let pfctl_proto = as_pfctl_proto(allowed_endpoint.protocol); + + Ok(self + .create_rule_builder(FilterRuleAction::Pass) + .direction(pfctl::Direction::Out) + .to(allowed_endpoint.address) + .proto(pfctl_proto) + .keep_state(pfctl::StatePolicy::Keep) + .quick(true) + .build()?) + } + fn get_block_dns_rules(&self) -> Result<Vec<pfctl::FilterRule>> { let block_tcp_dns_rule = self .create_rule_builder(FilterRuleAction::Drop(DropAction::Return)) diff --git a/talpid-core/src/firewall/mod.rs b/talpid-core/src/firewall/mod.rs index b467f37d98..83a112ce88 100644 --- a/talpid-core/src/firewall/mod.rs +++ b/talpid-core/src/firewall/mod.rs @@ -107,6 +107,8 @@ pub enum FirewallPolicy { pingable_hosts: Vec<IpAddr>, /// Flag setting if communication with LAN networks should be possible. allow_lan: bool, + /// Host that should be reachable by the tunnel client while connecting. + allowed_endpoint: Endpoint, /// A process that is allowed to send packets to the relay. #[cfg(windows)] relay_client: PathBuf, @@ -140,6 +142,8 @@ pub enum FirewallPolicy { Blocked { /// Flag setting if communication with LAN networks should be possible. allow_lan: bool, + /// Host that should be reachable while in the blocked state. + allowed_endpoint: Endpoint, }, } @@ -182,10 +186,14 @@ impl fmt::Display for FirewallPolicy { tunnel.ipv6_gateway, if *allow_lan { "Allowing" } else { "Blocking" } ), - FirewallPolicy::Blocked { allow_lan } => write!( + FirewallPolicy::Blocked { + allow_lan, + allowed_endpoint, + } => write!( f, - "Blocked, {} LAN", - if *allow_lan { "Allowing" } else { "Blocking" } + "Blocked. {} LAN. Allowing endpoint {}", + if *allow_lan { "Allowing" } else { "Blocking" }, + allowed_endpoint, ), } } @@ -203,6 +211,8 @@ pub struct FirewallArguments { pub initialize_blocked: bool, /// This argument is required for the blocked state to configure the firewall correctly. pub allow_lan: bool, + /// This argument is required for the blocked state to configure the firewall correctly. + pub allowed_endpoint: Option<Endpoint>, } impl Firewall { diff --git a/talpid-core/src/firewall/windows.rs b/talpid-core/src/firewall/windows.rs index d1fa08a3e6..8375eb55d6 100644 --- a/talpid-core/src/firewall/windows.rs +++ b/talpid-core/src/firewall/windows.rs @@ -57,10 +57,23 @@ impl FirewallT for Firewall { if args.initialize_blocked { let cfg = &WinFwSettings::new(args.allow_lan); + + let winfw_allowed_endpoint = if let Some(allowed_endpoint) = args.allowed_endpoint { + let allowed_endpoint_ip = Self::widestring_ip(allowed_endpoint.address.ip()); + Some(WinFwEndpoint { + ip: allowed_endpoint_ip.as_ptr(), + port: allowed_endpoint.address.port(), + protocol: WinFwProt::from(allowed_endpoint.protocol), + }) + } else { + None + }; + unsafe { WinFw_InitializeBlocked( WINFW_TIMEOUT_SECONDS, &cfg, + winfw_allowed_endpoint.as_ptr(), Some(log_sink), logging_context, ) @@ -83,6 +96,7 @@ impl FirewallT for Firewall { peer_endpoint, pingable_hosts, allow_lan, + allowed_endpoint, relay_client, } => { let cfg = &WinFwSettings::new(allow_lan); @@ -91,6 +105,7 @@ impl FirewallT for Firewall { &peer_endpoint, &cfg, "Mullvad".to_string(), + &allowed_endpoint, &pingable_hosts, &relay_client, ) @@ -105,9 +120,12 @@ impl FirewallT for Firewall { let cfg = &WinFwSettings::new(allow_lan); self.set_connected_state(&peer_endpoint, &cfg, &tunnel, &dns_servers, &relay_client) } - FirewallPolicy::Blocked { allow_lan } => { + FirewallPolicy::Blocked { + allow_lan, + allowed_endpoint, + } => { let cfg = &WinFwSettings::new(allow_lan); - self.set_blocked_state(&cfg) + self.set_blocked_state(&cfg, &allowed_endpoint) } } } @@ -138,12 +156,13 @@ impl Firewall { endpoint: &Endpoint, winfw_settings: &WinFwSettings, _tunnel_iface_alias: String, + allowed_endpoint: &Endpoint, pingable_hosts: &Vec<IpAddr>, relay_client: &Path, ) -> Result<(), Error> { trace!("Applying 'connecting' firewall policy"); let ip_str = Self::widestring_ip(endpoint.address.ip()); - let winfw_relay = WinFwRelay { + let winfw_relay = WinFwEndpoint { ip: ip_str.as_ptr(), port: endpoint.address.port(), protocol: WinFwProt::from(endpoint.protocol), @@ -171,12 +190,20 @@ impl Firewall { None }; + let allowed_endpoint_ip = Self::widestring_ip(allowed_endpoint.address.ip()); + let winfw_allowed_endpoint = Some(WinFwEndpoint { + ip: allowed_endpoint_ip.as_ptr(), + port: allowed_endpoint.address.port(), + protocol: WinFwProt::from(allowed_endpoint.protocol), + }); + unsafe { WinFw_ApplyPolicyConnecting( winfw_settings, &winfw_relay, relay_client.as_ptr(), pingable_hosts.as_ptr(), + winfw_allowed_endpoint.as_ptr(), ) .into_result() .map_err(Error::ApplyingConnectingPolicy) @@ -207,7 +234,7 @@ impl Firewall { WideCString::new(tunnel_metadata.interface.encode_utf16().collect::<Vec<_>>()).unwrap(); // ip_str, gateway_str and tunnel_alias have to outlive winfw_relay - let winfw_relay = WinFwRelay { + let winfw_relay = WinFwEndpoint { ip: ip_str.as_ptr(), port: endpoint.address.port(), protocol: WinFwProt::from(endpoint.protocol), @@ -258,10 +285,22 @@ impl Firewall { } } - fn set_blocked_state(&mut self, winfw_settings: &WinFwSettings) -> Result<(), Error> { + fn set_blocked_state( + &mut self, + winfw_settings: &WinFwSettings, + allowed_endpoint: &Endpoint, + ) -> Result<(), Error> { trace!("Applying 'blocked' firewall policy"); + + let allowed_endpoint_ip = Self::widestring_ip(allowed_endpoint.address.ip()); + let winfw_allowed_endpoint = Some(WinFwEndpoint { + ip: allowed_endpoint_ip.as_ptr(), + port: allowed_endpoint.address.port(), + protocol: WinFwProt::from(allowed_endpoint.protocol), + }); + unsafe { - WinFw_ApplyPolicyBlocked(winfw_settings) + WinFw_ApplyPolicyBlocked(winfw_settings, winfw_allowed_endpoint.as_ptr()) .into_result() .map_err(Error::ApplyingBlockedPolicy) } @@ -289,7 +328,7 @@ mod winfw { use talpid_types::net::TransportProtocol; #[repr(C)] - pub struct WinFwRelay { + pub struct WinFwEndpoint { pub ip: *const libc::wchar_t, pub port: u16, pub protocol: WinFwProt, @@ -385,6 +424,7 @@ mod winfw { pub fn WinFw_InitializeBlocked( timeout: libc::c_uint, settings: &WinFwSettings, + allowed_endpoint: *const WinFwEndpoint, sink: Option<LogSink>, sink_context: *const u8, ) -> InitializationResult; @@ -395,15 +435,16 @@ mod winfw { #[link_name = "WinFw_ApplyPolicyConnecting"] pub fn WinFw_ApplyPolicyConnecting( settings: &WinFwSettings, - relay: &WinFwRelay, + relay: &WinFwEndpoint, relayClient: *const libc::wchar_t, pingable_hosts: *const WinFwPingableHosts, + allowed_endpoint: *const WinFwEndpoint, ) -> WinFwPolicyStatus; #[link_name = "WinFw_ApplyPolicyConnected"] pub fn WinFw_ApplyPolicyConnected( settings: &WinFwSettings, - relay: &WinFwRelay, + relay: &WinFwEndpoint, relayClient: *const libc::wchar_t, tunnelIfaceAlias: *const libc::wchar_t, v4Gateway: *const libc::wchar_t, @@ -413,7 +454,10 @@ mod winfw { ) -> WinFwPolicyStatus; #[link_name = "WinFw_ApplyPolicyBlocked"] - pub fn WinFw_ApplyPolicyBlocked(settings: &WinFwSettings) -> WinFwPolicyStatus; + pub fn WinFw_ApplyPolicyBlocked( + settings: &WinFwSettings, + allowed_endpoint: *const WinFwEndpoint, + ) -> WinFwPolicyStatus; #[link_name = "WinFw_Reset"] pub fn WinFw_Reset() -> WinFwPolicyStatus; diff --git a/talpid-core/src/tunnel/tun_provider/android/mod.rs b/talpid-core/src/tunnel/tun_provider/android/mod.rs index b9385f13a7..fa48f115b9 100644 --- a/talpid-core/src/tunnel/tun_provider/android/mod.rs +++ b/talpid-core/src/tunnel/tun_provider/android/mod.rs @@ -66,6 +66,7 @@ pub struct AndroidTunProvider { object: GlobalRef, last_tun_config: TunConfig, allow_lan: bool, + allowed_endpoint: IpAddr, custom_dns_servers: Option<Vec<IpAddr>>, } @@ -74,6 +75,7 @@ impl AndroidTunProvider { pub fn new( context: AndroidContext, allow_lan: bool, + allowed_endpoint: IpAddr, custom_dns_servers: Option<Vec<IpAddr>>, ) -> Self { let env = JnixEnv::from( @@ -90,6 +92,7 @@ impl AndroidTunProvider { object: context.vpn_service, last_tun_config: TunConfig::default(), allow_lan, + allowed_endpoint, custom_dns_servers, } } @@ -103,6 +106,10 @@ impl AndroidTunProvider { Ok(()) } + pub fn set_allowed_endpoint(&mut self, endpoint: IpAddr) { + self.allowed_endpoint = endpoint; + } + pub fn set_custom_dns_servers(&mut self, servers: Option<Vec<IpAddr>>) -> Result<(), Error> { if self.custom_dns_servers != servers { self.custom_dns_servers = servers; @@ -129,6 +136,19 @@ impl AndroidTunProvider { }) } + /// Open a tunnel device that routes everything but `allowed_endpoint`, custom DNS, and (potentially) + /// LAN routes via the tunnel device. + /// + /// Will open a new tunnel if there is already an active tunnel. The previous tunnel will be + /// closed. + pub fn create_blocking_tun(&mut self) -> Result<(), Error> { + let mut config = TunConfig::default(); + self.prepare_tun_config(&mut config); + self.prepare_tun_config_for_allowed_endpoint(&mut config); + let _ = self.get_tun(config)?; + Ok(()) + } + /// Open a tunnel device using the previous or the default configuration. /// /// Will open a new tunnel if there is already an active tunnel. The previous tunnel will be @@ -231,6 +251,24 @@ impl AndroidTunProvider { } } + fn prepare_tun_config_for_allowed_endpoint(&self, config: &mut TunConfig) { + let endpoint_net = IpNetwork::from(self.allowed_endpoint); + let routes = config + .routes + .iter() + .flat_map(|&route| { + if route.is_ipv4() && endpoint_net.is_ipv4() { + route.sub(endpoint_net).collect() + } else if route.is_ipv6() && endpoint_net.is_ipv6() { + route.sub(endpoint_net).collect() + } else { + vec![route] + } + }) + .collect(); + config.routes = routes; + } + fn prepare_tun_config(&self, config: &mut TunConfig) { self.prepare_tun_config_for_allow_lan(config); self.prepare_tun_config_for_custom_dns(config); diff --git a/talpid-core/src/tunnel_state_machine/connected_state.rs b/talpid-core/src/tunnel_state_machine/connected_state.rs index 7292da0c67..0c305de9a7 100644 --- a/talpid-core/src/tunnel_state_machine/connected_state.rs +++ b/talpid-core/src/tunnel_state_machine/connected_state.rs @@ -192,6 +192,13 @@ impl ConnectedState { } } } + Some(TunnelCommand::AllowEndpoint(endpoint, tx)) => { + let _ = shared_values.set_allowed_endpoint(endpoint); + if let Err(_) = tx.send(()) { + log::error!("The AllowEndpoint receiver was dropped"); + } + SameState(self.into()) + } Some(TunnelCommand::CustomDns(servers)) => { match shared_values.set_custom_dns(servers) { Ok(true) => { diff --git a/talpid-core/src/tunnel_state_machine/connecting_state.rs b/talpid-core/src/tunnel_state_machine/connecting_state.rs index 44dcd9f153..0b03ceeca1 100644 --- a/talpid-core/src/tunnel_state_machine/connecting_state.rs +++ b/talpid-core/src/tunnel_state_machine/connecting_state.rs @@ -63,6 +63,7 @@ impl ConnectingState { peer_endpoint, pingable_hosts: gateway_list_from_params(params), allow_lan: shared_values.allow_lan, + allowed_endpoint: shared_values.allowed_endpoint.clone(), #[cfg(windows)] relay_client: TunnelMonitor::get_relay_client(&shared_values.resource_dir, ¶ms), #[cfg(target_os = "linux")] @@ -235,6 +236,22 @@ impl ConnectingState { } } } + Some(TunnelCommand::AllowEndpoint(endpoint, tx)) => { + if shared_values.set_allowed_endpoint(endpoint) { + if let Err(error) = + Self::set_firewall_policy(shared_values, &self.tunnel_parameters) + { + return self.disconnect( + shared_values, + AfterDisconnect::Block(ErrorStateCause::SetFirewallPolicyError(error)), + ); + } + } + if let Err(_) = tx.send(()) { + log::error!("The AllowEndpoint receiver was dropped"); + } + SameState(self.into()) + } Some(TunnelCommand::CustomDns(servers)) => { match shared_values.set_custom_dns(servers) { #[cfg(target_os = "android")] diff --git a/talpid-core/src/tunnel_state_machine/disconnected_state.rs b/talpid-core/src/tunnel_state_machine/disconnected_state.rs index dcc4660e9f..922eb69c88 100644 --- a/talpid-core/src/tunnel_state_machine/disconnected_state.rs +++ b/talpid-core/src/tunnel_state_machine/disconnected_state.rs @@ -17,6 +17,7 @@ impl DisconnectedState { let result = if shared_values.block_when_disconnected { let policy = FirewallPolicy::Blocked { allow_lan: shared_values.allow_lan, + allowed_endpoint: shared_values.allowed_endpoint.clone(), }; shared_values.firewall.apply_policy(policy).map_err(|e| { e.display_chain_with_msg( @@ -77,6 +78,15 @@ impl TunnelState for DisconnectedState { } SameState(self.into()) } + Some(TunnelCommand::AllowEndpoint(endpoint, tx)) => { + if shared_values.set_allowed_endpoint(endpoint) { + Self::set_firewall_policy(shared_values, true); + } + if let Err(_) = tx.send(()) { + log::error!("The AllowEndpoint receiver was dropped"); + } + SameState(self.into()) + } Some(TunnelCommand::CustomDns(servers)) => { // Same situation as allow LAN above. shared_values diff --git a/talpid-core/src/tunnel_state_machine/disconnecting_state.rs b/talpid-core/src/tunnel_state_machine/disconnecting_state.rs index 0928834d1c..48a83a6dc3 100644 --- a/talpid-core/src/tunnel_state_machine/disconnecting_state.rs +++ b/talpid-core/src/tunnel_state_machine/disconnecting_state.rs @@ -32,6 +32,13 @@ impl DisconnectingState { let _ = shared_values.set_allow_lan(allow_lan); AfterDisconnect::Nothing } + Some(TunnelCommand::AllowEndpoint(endpoint, tx)) => { + let _ = shared_values.set_allowed_endpoint(endpoint); + if let Err(_) = tx.send(()) { + log::error!("The AllowEndpoint receiver was dropped"); + } + AfterDisconnect::Nothing + } Some(TunnelCommand::CustomDns(servers)) => { let _ = shared_values.set_custom_dns(servers); AfterDisconnect::Nothing @@ -53,6 +60,13 @@ impl DisconnectingState { let _ = shared_values.set_allow_lan(allow_lan); AfterDisconnect::Block(reason) } + Some(TunnelCommand::AllowEndpoint(endpoint, tx)) => { + let _ = shared_values.set_allowed_endpoint(endpoint); + if let Err(_) = tx.send(()) { + log::error!("The AllowEndpoint receiver was dropped"); + } + AfterDisconnect::Block(reason) + } Some(TunnelCommand::CustomDns(servers)) => { let _ = shared_values.set_custom_dns(servers); AfterDisconnect::Block(reason) @@ -79,6 +93,13 @@ impl DisconnectingState { let _ = shared_values.set_allow_lan(allow_lan); AfterDisconnect::Reconnect(retry_attempt) } + Some(TunnelCommand::AllowEndpoint(endpoint, tx)) => { + let _ = shared_values.set_allowed_endpoint(endpoint); + if let Err(_) = tx.send(()) { + log::error!("The AllowEndpoint receiver was dropped"); + } + AfterDisconnect::Reconnect(retry_attempt) + } Some(TunnelCommand::CustomDns(servers)) => { let _ = shared_values.set_custom_dns(servers); AfterDisconnect::Reconnect(retry_attempt) diff --git a/talpid-core/src/tunnel_state_machine/error_state.rs b/talpid-core/src/tunnel_state_machine/error_state.rs index a87dccd5b4..51159d274f 100644 --- a/talpid-core/src/tunnel_state_machine/error_state.rs +++ b/talpid-core/src/tunnel_state_machine/error_state.rs @@ -21,6 +21,7 @@ impl ErrorState { ) -> Result<(), FirewallPolicyError> { let policy = FirewallPolicy::Blocked { allow_lan: shared_values.allow_lan, + allowed_endpoint: shared_values.allowed_endpoint.clone(), }; #[cfg(target_os = "linux")] @@ -47,7 +48,7 @@ impl ErrorState { /// Returns true if a new tunnel device was successfully created. #[cfg(target_os = "android")] fn create_blocking_tun(shared_values: &mut SharedTunnelStateValues) -> bool { - match shared_values.tun_provider.create_tun_if_closed() { + match shared_values.tun_provider.create_blocking_tun() { Ok(()) => true, Err(error) => { log::error!( @@ -105,6 +106,23 @@ impl TunnelState for ErrorState { SameState(self.into()) } } + Some(TunnelCommand::AllowEndpoint(endpoint, tx)) => { + if shared_values.set_allowed_endpoint(endpoint) { + let _ = Self::set_firewall_policy(shared_values); + + #[cfg(target_os = "android")] + if !Self::create_blocking_tun(shared_values) { + return NewState(Self::enter( + shared_values, + ErrorStateCause::SetFirewallPolicyError(FirewallPolicyError::Generic), + )); + } + } + if let Err(_) = tx.send(()) { + log::error!("The AllowEndpoint receiver was dropped"); + } + SameState(self.into()) + } Some(TunnelCommand::CustomDns(servers)) => { if let Err(error_state_cause) = shared_values.set_custom_dns(servers) { NewState(Self::enter(shared_values, error_state_cause)) diff --git a/talpid-core/src/tunnel_state_machine/mod.rs b/talpid-core/src/tunnel_state_machine/mod.rs index fbec1bf2b1..b657ec5e36 100644 --- a/talpid-core/src/tunnel_state_machine/mod.rs +++ b/talpid-core/src/tunnel_state_machine/mod.rs @@ -33,7 +33,7 @@ use std::{ #[cfg(target_os = "android")] use talpid_types::{android::AndroidContext, ErrorExt}; use talpid_types::{ - net::TunnelParameters, + net::{Endpoint, TunnelParameters}, tunnel::{ErrorStateCause, ParameterGenerationError, TunnelStateTransition}, }; @@ -75,6 +75,7 @@ pub async fn spawn( allow_lan: bool, block_when_disconnected: bool, custom_dns: Option<Vec<IpAddr>>, + allowed_endpoint: Endpoint, tunnel_parameters_generator: impl TunnelParametersGenerator, log_dir: Option<PathBuf>, resource_dir: PathBuf, @@ -101,6 +102,8 @@ pub async fn spawn( #[cfg(target_os = "android")] allow_lan, #[cfg(target_os = "android")] + allowed_endpoint.address.ip(), + #[cfg(target_os = "android")] custom_dns.clone(), ); @@ -114,6 +117,7 @@ pub async fn spawn( block_when_disconnected, is_offline, custom_dns, + allowed_endpoint, tunnel_parameters_generator, tun_provider, log_dir, @@ -152,6 +156,9 @@ pub async fn spawn( pub enum TunnelCommand { /// Enable or disable LAN access in the firewall. AllowLan(bool), + /// Endpoint that should never be blocked. + /// If an error occurs, the sender is dropped. + AllowEndpoint(Endpoint, oneshot::Sender<()>), /// Set custom DNS servers to use. CustomDns(Option<Vec<IpAddr>>), /// Enable or disable the block_when_disconnected feature. @@ -193,6 +200,7 @@ impl TunnelStateMachine { block_when_disconnected: bool, is_offline: bool, custom_dns: Option<Vec<IpAddr>>, + allowed_endpoint: Endpoint, tunnel_parameters_generator: impl TunnelParametersGenerator, tun_provider: TunProvider, log_dir: Option<PathBuf>, @@ -204,6 +212,7 @@ impl TunnelStateMachine { let args = FirewallArguments { initialize_blocked: block_when_disconnected || !reset_firewall, allow_lan, + allowed_endpoint: Some(allowed_endpoint), }; let firewall = Firewall::new(args).map_err(Error::InitFirewallError)?; @@ -218,6 +227,7 @@ impl TunnelStateMachine { block_when_disconnected, is_offline, custom_dns, + allowed_endpoint, tunnel_parameters_generator: Box::new(tunnel_parameters_generator), tun_provider, log_dir, @@ -291,6 +301,8 @@ struct SharedTunnelStateValues { is_offline: bool, /// Custom DNS servers to use. custom_dns: Option<Vec<IpAddr>>, + /// Endpoint that should not be blocked by the firewall. + allowed_endpoint: Endpoint, /// The generator of new `TunnelParameter`s tunnel_parameters_generator: Box<dyn TunnelParametersGenerator>, /// The provider of tunnel devices. @@ -328,6 +340,20 @@ impl SharedTunnelStateValues { Ok(()) } + pub fn set_allowed_endpoint(&mut self, endpoint: Endpoint) -> bool { + if self.allowed_endpoint != endpoint { + self.allowed_endpoint = endpoint; + + #[cfg(target_os = "android")] + self.tun_provider + .set_allowed_endpoint(endpoint.address.ip()); + + true + } else { + false + } + } + pub fn set_custom_dns( &mut self, custom_dns: Option<Vec<IpAddr>>, |
