summaryrefslogtreecommitdiffhomepage
path: root/talpid-core/src
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2020-12-07 20:31:40 +0100
committerDavid Lönnhager <david.l@mullvad.net>2021-01-04 16:50:18 +0100
commitd9baa6bf9d98858d9f5bae95740b9d5ecb192c0f (patch)
tree1ce85578b7bb8ca1dddad78120231a1c28113146 /talpid-core/src
parent07d363b919ee0c9e33f444475361194a29f37216 (diff)
downloadmullvadvpn-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.rs29
-rw-r--r--talpid-core/src/firewall/macos.rs24
-rw-r--r--talpid-core/src/firewall/mod.rs16
-rw-r--r--talpid-core/src/firewall/windows.rs64
-rw-r--r--talpid-core/src/tunnel/tun_provider/android/mod.rs38
-rw-r--r--talpid-core/src/tunnel_state_machine/connected_state.rs7
-rw-r--r--talpid-core/src/tunnel_state_machine/connecting_state.rs17
-rw-r--r--talpid-core/src/tunnel_state_machine/disconnected_state.rs10
-rw-r--r--talpid-core/src/tunnel_state_machine/disconnecting_state.rs21
-rw-r--r--talpid-core/src/tunnel_state_machine/error_state.rs20
-rw-r--r--talpid-core/src/tunnel_state_machine/mod.rs28
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, &params),
#[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>>,