diff options
| author | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2020-12-04 10:28:41 -0300 |
|---|---|---|
| committer | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2020-12-04 10:28:41 -0300 |
| commit | 26981772fb092e9865d14b9a0dc70d7a52481cf4 (patch) | |
| tree | b64ff66d00ced9fe0bb3c3e9afbf5cb136767a09 | |
| parent | 0ce5d9d70ab5e7779be8cd201526689231eb1ea5 (diff) | |
| parent | 9b263846b9a7774b4a98404d2419f363b98e2586 (diff) | |
| download | mullvadvpn-26981772fb092e9865d14b9a0dc70d7a52481cf4.tar.xz mullvadvpn-26981772fb092e9865d14b9a0dc70d7a52481cf4.zip | |
Merge branch 'custom-dns-on-android'
27 files changed, 262 insertions, 81 deletions
diff --git a/Cargo.lock b/Cargo.lock index de0f19a4aa..6c4168a3a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -999,9 +999,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jnix" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6354ae923ca4df982181ae2cd77eb4214f8c11d11d0c0cd8606c9347ac2abc57" +checksum = "1c610361550ad147bf2d7ddf71b67035706759ebf6f503cc6cbe4a581004de8a" dependencies = [ "jni", "jnix-macros", @@ -1011,9 +1011,9 @@ dependencies = [ [[package]] name = "jnix-macros" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c78132fe420156f13b30518fcda9449b0ab8ae3b5584e8a1c53ce390fe770b44" +checksum = "13e87ff0edc1c5199f084e3175e9a3f5ee2bb56035403b685d0ec8edd5157250" dependencies = [ "heck", "proc-macro2", diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/model/DnsOptions.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/DnsOptions.kt new file mode 100644 index 0000000000..e4d7b75481 --- /dev/null +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/DnsOptions.kt @@ -0,0 +1,6 @@ +package net.mullvad.mullvadvpn.model + +import java.net.InetAddress +import java.util.ArrayList + +data class DnsOptions(val custom: Boolean, val addresses: ArrayList<InetAddress>) diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelOptions.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelOptions.kt index 7579a469ca..7e3bdef2b3 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelOptions.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelOptions.kt @@ -2,4 +2,4 @@ package net.mullvad.mullvadvpn.model import net.mullvad.talpid.net.wireguard.TunnelOptions as WireguardTunnelOptions -data class TunnelOptions(val wireguard: WireguardTunnelOptions) +data class TunnelOptions(val wireguard: WireguardTunnelOptions, val dnsOptions: DnsOptions) diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/CustomDns.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/CustomDns.kt new file mode 100644 index 0000000000..95a4be3cb9 --- /dev/null +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/CustomDns.kt @@ -0,0 +1,68 @@ +package net.mullvad.mullvadvpn.service + +import java.net.InetAddress +import java.util.ArrayList +import kotlin.properties.Delegates.observable +import net.mullvad.mullvadvpn.model.DnsOptions +import net.mullvad.talpid.util.EventNotifier + +class CustomDns(val daemon: MullvadDaemon, val settingsListener: SettingsListener) { + private var enabled by observable(false) { _, oldValue, newValue -> + if (oldValue != newValue) { + onEnabledChanged.notify(newValue) + } + } + + private var dnsServers by observable<ArrayList<InetAddress>>(ArrayList()) { _, _, servers -> + onDnsServersChanged.notify(servers.toList()) + } + + val onEnabledChanged = EventNotifier(false) + val onDnsServersChanged = EventNotifier<List<InetAddress>>(emptyList()) + + init { + settingsListener.dnsOptionsNotifier.subscribe(this) { dnsOptions -> + enabled = dnsOptions.custom + dnsServers = ArrayList(dnsOptions.addresses) + } + } + + fun onDestroy() { + settingsListener.dnsOptionsNotifier.unsubscribe(this) + } + + fun enable() { + synchronized(this) { + changeDnsOptions(true, dnsServers) + } + } + + fun disable() { + synchronized(this) { + changeDnsOptions(false, dnsServers) + } + } + + fun addDnsServer(server: InetAddress) { + synchronized(this) { + if (!dnsServers.contains(server)) { + dnsServers.add(server) + changeDnsOptions(enabled, dnsServers) + } + } + } + + fun removeDnsServer(server: InetAddress) { + synchronized(this) { + if (dnsServers.remove(server)) { + changeDnsOptions(enabled, dnsServers) + } + } + } + + private fun changeDnsOptions(enable: Boolean, dnsServers: ArrayList<InetAddress>) { + val options = DnsOptions(enable, dnsServers) + + daemon.setDnsOptions(options) + } +} diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt index 838fe80c31..083568f726 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt @@ -1,6 +1,7 @@ package net.mullvad.mullvadvpn.service import net.mullvad.mullvadvpn.model.AppVersionInfo +import net.mullvad.mullvadvpn.model.DnsOptions import net.mullvad.mullvadvpn.model.GeoIpLocation import net.mullvad.mullvadvpn.model.GetAccountDataResult import net.mullvad.mullvadvpn.model.KeygenEvent @@ -106,6 +107,10 @@ class MullvadDaemon(val vpnService: MullvadVpnService) { setAutoConnect(daemonInterfaceAddress, autoConnect) } + fun setDnsOptions(dnsOptions: DnsOptions) { + setDnsOptions(daemonInterfaceAddress, dnsOptions) + } + fun setWireguardMtu(wireguardMtu: Int?) { setWireguardMtu(daemonInterfaceAddress, wireguardMtu) } @@ -158,6 +163,7 @@ class MullvadDaemon(val vpnService: MullvadVpnService) { private external fun setAccount(daemonInterfaceAddress: Long, accountToken: String?) private external fun setAllowLan(daemonInterfaceAddress: Long, allowLan: Boolean) private external fun setAutoConnect(daemonInterfaceAddress: Long, alwaysOn: Boolean) + private external fun setDnsOptions(daemonInterfaceAddress: Long, dnsOptions: DnsOptions) private external fun setWireguardMtu(daemonInterfaceAddress: Long, wireguardMtu: Int?) private external fun shutdown(daemonInterfaceAddress: Long) private external fun submitVoucher( diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt index be49d3723c..1b31605adc 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt @@ -226,6 +226,7 @@ class MullvadVpnService : TalpidVpnService() { private suspend fun setUpInstance(daemon: MullvadDaemon, settings: Settings) { val settingsListener = SettingsListener(daemon, settings) val connectionProxy = ConnectionProxy(this, daemon) + val customDns = CustomDns(daemon, settingsListener) val splitTunneling = splitTunneling.await() splitTunneling.onChange = { excludedApps -> @@ -241,6 +242,7 @@ class MullvadVpnService : TalpidVpnService() { daemon, connectionProxy, connectivityListener, + customDns, settingsListener, splitTunneling ) diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/ServiceInstance.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/ServiceInstance.kt index 5ef94a4f5c..135ea9ef28 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/ServiceInstance.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/ServiceInstance.kt @@ -6,6 +6,7 @@ class ServiceInstance( val daemon: MullvadDaemon, val connectionProxy: ConnectionProxy, val connectivityListener: ConnectivityListener, + val customDns: CustomDns, val settingsListener: SettingsListener, val splitTunneling: SplitTunneling ) { @@ -16,6 +17,7 @@ class ServiceInstance( fun onDestroy() { accountCache.onDestroy() connectionProxy.onDestroy() + customDns.onDestroy() keyStatusListener.onDestroy() locationInfoCache.onDestroy() settingsListener.onDestroy() diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/SettingsListener.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/SettingsListener.kt index 17af7022a8..5ffe9a1a2f 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/SettingsListener.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/SettingsListener.kt @@ -14,6 +14,7 @@ class SettingsListener(val daemon: MullvadDaemon, val initialSettings: Settings) private val settingsNotifier: EventNotifier<Settings> = EventNotifier(settings) val accountNumberNotifier = EventNotifier(initialSettings.accountToken) + val dnsOptionsNotifier = EventNotifier(initialSettings.tunnelOptions.dnsOptions) var onRelaySettingsChange: ((RelaySettings?) -> Unit)? = null set(value) { @@ -50,6 +51,10 @@ class SettingsListener(val daemon: MullvadDaemon, val initialSettings: Settings) accountNumberNotifier.notify(newSettings.accountToken) } + if (settings.tunnelOptions.dnsOptions != newSettings.tunnelOptions.dnsOptions) { + dnsOptionsNotifier.notify(newSettings.tunnelOptions.dnsOptions) + } + if (settings.relaySettings != newSettings.relaySettings) { onRelaySettingsChange?.invoke(newSettings.relaySettings) } diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceConnection.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceConnection.kt index 88de95d04a..33e1698b15 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceConnection.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceConnection.kt @@ -9,6 +9,7 @@ class ServiceConnection(private val service: ServiceInstance, val mainActivity: val accountCache = service.accountCache val connectionProxy = service.connectionProxy val connectivityListener = service.connectivityListener + val customDns = service.customDns val keyStatusListener = service.keyStatusListener val locationInfoCache = service.locationInfoCache val settingsListener = service.settingsListener diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceDependentFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceDependentFragment.kt index 7f84f09008..1e1ba969ed 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceDependentFragment.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceDependentFragment.kt @@ -9,6 +9,7 @@ import net.mullvad.mullvadvpn.dataproxy.AppVersionInfoCache import net.mullvad.mullvadvpn.dataproxy.RelayListListener import net.mullvad.mullvadvpn.service.AccountCache import net.mullvad.mullvadvpn.service.ConnectionProxy +import net.mullvad.mullvadvpn.service.CustomDns import net.mullvad.mullvadvpn.service.KeyStatusListener import net.mullvad.mullvadvpn.service.LocationInfoCache import net.mullvad.mullvadvpn.service.MullvadDaemon @@ -44,6 +45,9 @@ abstract class ServiceDependentFragment(val onNoService: OnNoService) : ServiceA lateinit var connectivityListener: ConnectivityListener private set + lateinit var customDns: CustomDns + private set + lateinit var daemon: MullvadDaemon private set @@ -69,6 +73,7 @@ abstract class ServiceDependentFragment(val onNoService: OnNoService) : ServiceA appVersionInfoCache = serviceConnection.appVersionInfoCache connectionProxy = serviceConnection.connectionProxy connectivityListener = serviceConnection.connectivityListener + customDns = serviceConnection.customDns daemon = serviceConnection.daemon keyStatusListener = serviceConnection.keyStatusListener locationInfoCache = serviceConnection.locationInfoCache diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index b4e5dd3279..290fdaa9ee 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -26,8 +26,6 @@ use futures::{ }; use log::{debug, error, info, warn}; use mullvad_rpc::AccountsProxy; -#[cfg(not(target_os = "android"))] -use mullvad_types::settings::DnsOptions; use mullvad_types::{ account::{AccountData, AccountToken, VoucherSubmission}, endpoint::MullvadEndpoint, @@ -37,21 +35,20 @@ use mullvad_types::{ RelaySettingsUpdate, }, relay_list::{Relay, RelayList}, - settings::Settings, + settings::{DnsOptions, Settings}, states::{TargetState, TunnelState}, version::{AppVersion, AppVersionInfo}, wireguard::KeygenEvent, }; use settings::SettingsPersister; #[cfg(not(target_os = "android"))] -use std::net::IpAddr; -#[cfg(not(target_os = "android"))] use std::path::Path; use std::{ fs::{self, File}, io, marker::PhantomData, mem, + net::IpAddr, path::PathBuf, sync::{mpsc as sync_mpsc, Arc, Weak}, time::Duration, @@ -197,7 +194,6 @@ pub enum DaemonCommand { /// Set if IPv6 should be enabled in the tunnel SetEnableIpv6(oneshot::Sender<()>, bool), /// Set custom DNS servers to use instead of passing requests to the gateway - #[cfg(not(target_os = "android"))] SetDnsOptions(oneshot::Sender<()>, DnsOptions), /// Set MTU for wireguard tunnels SetWireguardMtu(oneshot::Sender<()>, Option<u16>), @@ -583,7 +579,6 @@ where let tunnel_command_tx = tunnel_state_machine::spawn( settings.allow_lan, settings.block_when_disconnected, - #[cfg(not(target_os = "android"))] Self::get_custom_resolvers(&settings.tunnel_options.dns_options), tunnel_parameters_generator, log_dir, @@ -637,7 +632,6 @@ where Ok(daemon) } - #[cfg(not(target_os = "android"))] fn get_custom_resolvers(dns_options: &DnsOptions) -> Option<Vec<IpAddr>> { if dns_options.custom { Some(dns_options.addresses.clone()) @@ -1057,7 +1051,6 @@ where } SetBridgeState(tx, bridge_state) => self.on_set_bridge_state(tx, bridge_state), SetEnableIpv6(tx, enable_ipv6) => self.on_set_enable_ipv6(tx, enable_ipv6), - #[cfg(not(target_os = "android"))] SetDnsOptions(tx, dns_servers) => self.on_set_dns_options(tx, dns_servers), SetWireguardMtu(tx, mtu) => self.on_set_wireguard_mtu(tx, mtu), SetWireguardRotationInterval(tx, interval) => { @@ -1697,7 +1690,6 @@ where } } - #[cfg(not(target_os = "android"))] fn on_set_dns_options(&mut self, tx: oneshot::Sender<()>, dns_options: DnsOptions) { let save_result = self.settings.set_dns_options(dns_options.clone()); match save_result { diff --git a/mullvad-daemon/src/settings.rs b/mullvad-daemon/src/settings.rs index d67702c575..fc177c1da1 100644 --- a/mullvad-daemon/src/settings.rs +++ b/mullvad-daemon/src/settings.rs @@ -1,9 +1,7 @@ use log::{debug, error, info}; -#[cfg(not(target_os = "android"))] -use mullvad_types::settings::DnsOptions; use mullvad_types::{ relay_constraints::{BridgeSettings, BridgeState, RelaySettingsUpdate}, - settings::Settings, + settings::{DnsOptions, Settings}, }; use std::{ fs::{self, File}, @@ -212,7 +210,6 @@ impl SettingsPersister { self.update(should_save) } - #[cfg(not(target_os = "android"))] pub fn set_dns_options(&mut self, options: DnsOptions) -> Result<bool, Error> { let should_save = Self::update_field(&mut self.settings.tunnel_options.dns_options, options); diff --git a/mullvad-jni/Cargo.toml b/mullvad-jni/Cargo.toml index 9f61403249..21cc9612bb 100644 --- a/mullvad-jni/Cargo.toml +++ b/mullvad-jni/Cargo.toml @@ -14,7 +14,7 @@ crate_type = ["cdylib"] err-derive = "0.2.1" futures = "0.3" ipnetwork = "0.16" -jnix = { version = "0.2.3", features = ["derive"] } +jnix = { version = "0.3", features = ["derive"] } lazy_static = "1" log = "0.4" log-panics = "2" diff --git a/mullvad-jni/src/classes.rs b/mullvad-jni/src/classes.rs index 068e75f63c..d402d1a8a3 100644 --- a/mullvad-jni/src/classes.rs +++ b/mullvad-jni/src/classes.rs @@ -7,6 +7,7 @@ pub const CLASSES: &[&str] = &[ "net/mullvad/mullvadvpn/model/AppVersionInfo", "net/mullvad/mullvadvpn/model/Constraint$Any", "net/mullvad/mullvadvpn/model/Constraint$Only", + "net/mullvad/mullvadvpn/model/DnsOptions", "net/mullvad/mullvadvpn/model/GeoIpLocation", "net/mullvad/mullvadvpn/model/GetAccountDataResult$Ok", "net/mullvad/mullvadvpn/model/GetAccountDataResult$InvalidAccount", diff --git a/mullvad-jni/src/daemon_interface.rs b/mullvad-jni/src/daemon_interface.rs index b00a1b6b50..d3ab959b33 100644 --- a/mullvad-jni/src/daemon_interface.rs +++ b/mullvad-jni/src/daemon_interface.rs @@ -5,7 +5,7 @@ use mullvad_types::{ location::GeoIpLocation, relay_constraints::RelaySettingsUpdate, relay_list::RelayList, - settings::Settings, + settings::{DnsOptions, Settings}, states::{TargetState, TunnelState}, version::AppVersionInfo, wireguard::{self, KeygenEvent}, @@ -202,6 +202,14 @@ impl DaemonInterface { block_on(rx).map_err(|_| Error::NoResponse) } + pub fn set_dns_options(&self, dns_options: DnsOptions) -> Result<()> { + let (tx, rx) = oneshot::channel(); + + self.send_command(DaemonCommand::SetDnsOptions(tx, dns_options))?; + + block_on(rx).map_err(|_| Error::NoResponse) + } + pub fn set_wireguard_mtu(&self, wireguard_mtu: Option<u16>) -> Result<()> { let (tx, rx) = oneshot::channel(); diff --git a/mullvad-jni/src/lib.rs b/mullvad-jni/src/lib.rs index 48be979ac1..94d5d77313 100644 --- a/mullvad-jni/src/lib.rs +++ b/mullvad-jni/src/lib.rs @@ -21,7 +21,10 @@ use mullvad_daemon::{ exception_logging, logging, runtime::new_runtime_builder, version, Daemon, DaemonCommandChannel, }; use mullvad_rpc::{rest::Error as RestError, StatusCode}; -use mullvad_types::account::{AccountData, VoucherSubmission}; +use mullvad_types::{ + account::{AccountData, VoucherSubmission}, + settings::DnsOptions, +}; use std::{ io, path::{Path, PathBuf}, @@ -825,6 +828,28 @@ pub extern "system" fn Java_net_mullvad_mullvadvpn_service_MullvadDaemon_setAuto #[no_mangle] #[allow(non_snake_case)] +pub extern "system" fn Java_net_mullvad_mullvadvpn_service_MullvadDaemon_setDnsOptions( + env: JNIEnv<'_>, + _: JObject<'_>, + daemon_interface_address: jlong, + dnsOptions: JObject<'_>, +) { + let env = JnixEnv::from(env); + + if let Some(daemon_interface) = get_daemon_interface(daemon_interface_address) { + let dns_options = DnsOptions::from_java(&env, dnsOptions); + + if let Err(error) = daemon_interface.set_dns_options(dns_options) { + log::error!( + "{}", + error.display_chain_with_msg("Failed to set custom DNS options") + ); + } + } +} + +#[no_mangle] +#[allow(non_snake_case)] pub extern "system" fn Java_net_mullvad_mullvadvpn_service_MullvadDaemon_setWireguardMtu( env: JNIEnv<'_>, _: JObject<'_>, diff --git a/mullvad-types/Cargo.toml b/mullvad-types/Cargo.toml index 548712d7d8..ab0989c948 100644 --- a/mullvad-types/Cargo.toml +++ b/mullvad-types/Cargo.toml @@ -20,4 +20,4 @@ serde_json = "1.0" talpid-types = { path = "../talpid-types" } [target.'cfg(target_os = "android")'.dependencies] -jnix = { version = "0.2.3", features = ["derive"] } +jnix = { version = "0.3", features = ["derive"] } diff --git a/mullvad-types/src/settings/mod.rs b/mullvad-types/src/settings/mod.rs index 4f09fa480b..867f1c311e 100644 --- a/mullvad-types/src/settings/mod.rs +++ b/mullvad-types/src/settings/mod.rs @@ -3,11 +3,10 @@ use crate::relay_constraints::{ RelayConstraints, RelaySettings, RelaySettingsUpdate, }; #[cfg(target_os = "android")] -use jnix::IntoJava; +use jnix::{FromJava, IntoJava}; use log::{debug, info}; use serde::{Deserialize, Serialize}; use serde_json; -#[cfg(not(target_os = "android"))] use std::net::IpAddr; use talpid_types::net::{openvpn, wireguard, GenericTunnelOptions}; @@ -167,14 +166,14 @@ pub struct TunnelOptions { #[cfg_attr(target_os = "android", jnix(skip))] pub generic: GenericTunnelOptions, /// Custom DNS options. - #[cfg(not(target_os = "android"))] pub dns_options: DnsOptions, } /// Custom DNS config -#[cfg(not(target_os = "android"))] #[serde(default)] #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)] +#[cfg_attr(target_os = "android", derive(FromJava, IntoJava))] +#[cfg_attr(target_os = "android", jnix(package = "net.mullvad.mullvadvpn.model"))] pub struct DnsOptions { /// Whether to use the addresses in `custom_dns`. pub custom: bool, @@ -194,7 +193,6 @@ impl Default for TunnelOptions { // Enable IPv6 be default on Android enable_ipv6: cfg!(target_os = "android"), }, - #[cfg(not(target_os = "android"))] dns_options: DnsOptions::default(), } } diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml index d9e9b544a9..dd1367e770 100644 --- a/talpid-core/Cargo.toml +++ b/talpid-core/Cargo.toml @@ -42,7 +42,7 @@ nix = "0.19" [target.'cfg(target_os = "android")'.dependencies] -jnix = { version = "0.2.3", features = ["derive"] } +jnix = { version = "0.3", features = ["derive"] } [target.'cfg(target_os = "linux")'.dependencies] diff --git a/talpid-core/src/tunnel/tun_provider/android/mod.rs b/talpid-core/src/tunnel/tun_provider/android/mod.rs index c744ca96fa..4c136ef189 100644 --- a/talpid-core/src/tunnel/tun_provider/android/mod.rs +++ b/talpid-core/src/tunnel/tun_provider/android/mod.rs @@ -60,11 +60,16 @@ pub struct AndroidTunProvider { object: GlobalRef, last_tun_config: TunConfig, allow_lan: bool, + custom_dns_servers: Option<Vec<IpAddr>>, } impl AndroidTunProvider { /// Create a new AndroidTunProvider interfacing with Android's VpnService. - pub fn new(context: AndroidContext, allow_lan: bool) -> Self { + pub fn new( + context: AndroidContext, + allow_lan: bool, + custom_dns_servers: Option<Vec<IpAddr>>, + ) -> Self { let env = JnixEnv::from( context .jvm @@ -79,6 +84,7 @@ impl AndroidTunProvider { object: context.vpn_service, last_tun_config: TunConfig::default(), allow_lan, + custom_dns_servers, } } @@ -91,6 +97,15 @@ impl AndroidTunProvider { Ok(()) } + 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; + self.recreate_tun_if_open()?; + } + + Ok(()) + } + /// Retrieve a tunnel device with the provided configuration. pub fn get_tun(&mut self, config: TunConfig) -> Result<VpnServiceTun, Error> { let tun_fd = self.get_tun_fd(config.clone())?; @@ -170,10 +185,11 @@ impl AndroidTunProvider { } } - fn get_tun_fd(&mut self, config: TunConfig) -> Result<RawFd, Error> { + fn get_tun_fd(&mut self, mut config: TunConfig) -> Result<RawFd, Error> { + self.prepare_tun_config(&mut config); + let env = self.env()?; - let actual_config = self.prepare_tun_config(config); - let java_config = actual_config.into_java(&env); + let java_config = config.into_java(&env); let result = self.call_method( "getTun", @@ -191,8 +207,11 @@ impl AndroidTunProvider { } fn recreate_tun_if_open(&mut self) -> Result<(), Error> { + let mut actual_config = self.last_tun_config.clone(); + + self.prepare_tun_config(&mut actual_config); + let env = self.env()?; - let actual_config = self.prepare_tun_config(self.last_tun_config.clone()); let java_config = actual_config.into_java(&env); let result = self.call_method( @@ -208,7 +227,12 @@ impl AndroidTunProvider { } } - fn prepare_tun_config(&self, config: TunConfig) -> TunConfig { + fn prepare_tun_config(&self, config: &mut TunConfig) { + self.prepare_tun_config_for_allow_lan(config); + self.prepare_tun_config_for_custom_dns(config); + } + + fn prepare_tun_config_for_allow_lan(&self, config: &mut TunConfig) { if self.allow_lan { let (required_ipv4_routes, required_ipv6_routes) = config .required_routes @@ -245,9 +269,13 @@ impl AndroidTunProvider { }) .collect(); - TunConfig { routes, ..config } - } else { - config + config.routes = routes; + } + } + + fn prepare_tun_config_for_custom_dns(&self, config: &mut TunConfig) { + if let Some(custom_dns_servers) = self.custom_dns_servers.clone() { + config.dns_servers = custom_dns_servers; } } diff --git a/talpid-core/src/tunnel_state_machine/connected_state.rs b/talpid-core/src/tunnel_state_machine/connected_state.rs index a5a8861016..7292da0c67 100644 --- a/talpid-core/src/tunnel_state_machine/connected_state.rs +++ b/talpid-core/src/tunnel_state_machine/connected_state.rs @@ -192,30 +192,39 @@ impl ConnectedState { } } } - #[cfg(not(target_os = "android"))] Some(TunnelCommand::CustomDns(servers)) => { - if shared_values.custom_dns != servers { - shared_values.custom_dns = servers; - - if let Err(error) = self.set_firewall_policy(shared_values) { - return self.disconnect( - shared_values, - AfterDisconnect::Block(ErrorStateCause::SetFirewallPolicyError(error)), - ); - } - - match self.set_dns(shared_values) { - Ok(()) => SameState(self.into()), - Err(error) => { - log::error!("{}", error.display_chain_with_msg("Failed to set DNS")); - self.disconnect( + match shared_values.set_custom_dns(servers) { + Ok(true) => { + if let Err(error) = self.set_firewall_policy(shared_values) { + return self.disconnect( shared_values, - AfterDisconnect::Block(ErrorStateCause::SetDnsError), - ) + AfterDisconnect::Block(ErrorStateCause::SetFirewallPolicyError( + error, + )), + ); + } + + match self.set_dns(shared_values) { + #[cfg(target_os = "android")] + Ok(()) => self.disconnect(shared_values, AfterDisconnect::Reconnect(0)), + #[cfg(not(target_os = "android"))] + Ok(()) => SameState(self.into()), + Err(error) => { + log::error!( + "{}", + error.display_chain_with_msg("Failed to set DNS") + ); + self.disconnect( + shared_values, + AfterDisconnect::Block(ErrorStateCause::SetDnsError), + ) + } } } - } else { - SameState(self.into()) + Ok(false) => SameState(self.into()), + Err(error_cause) => { + self.disconnect(shared_values, AfterDisconnect::Block(error_cause)) + } } } Some(TunnelCommand::BlockWhenDisconnected(block_when_disconnected)) => { diff --git a/talpid-core/src/tunnel_state_machine/connecting_state.rs b/talpid-core/src/tunnel_state_machine/connecting_state.rs index 146d7c293a..6e561acc6b 100644 --- a/talpid-core/src/tunnel_state_machine/connecting_state.rs +++ b/talpid-core/src/tunnel_state_machine/connecting_state.rs @@ -235,10 +235,13 @@ impl ConnectingState { } } } - #[cfg(not(target_os = "android"))] Some(TunnelCommand::CustomDns(servers)) => { - shared_values.custom_dns = servers; - SameState(self.into()) + match shared_values.set_custom_dns(servers) { + #[cfg(target_os = "android")] + Ok(true) => self.disconnect(shared_values, AfterDisconnect::Reconnect(0)), + Ok(_) => SameState(self.into()), + Err(cause) => self.disconnect(shared_values, AfterDisconnect::Block(cause)), + } } Some(TunnelCommand::BlockWhenDisconnected(block_when_disconnected)) => { shared_values.block_when_disconnected = block_when_disconnected; diff --git a/talpid-core/src/tunnel_state_machine/disconnected_state.rs b/talpid-core/src/tunnel_state_machine/disconnected_state.rs index fb55000a9a..dcc4660e9f 100644 --- a/talpid-core/src/tunnel_state_machine/disconnected_state.rs +++ b/talpid-core/src/tunnel_state_machine/disconnected_state.rs @@ -77,9 +77,12 @@ impl TunnelState for DisconnectedState { } SameState(self.into()) } - #[cfg(not(target_os = "android"))] Some(TunnelCommand::CustomDns(servers)) => { - shared_values.custom_dns = servers; + // Same situation as allow LAN above. + shared_values + .set_custom_dns(servers) + .expect("Failed to reconnect after changing custom DNS servers"); + SameState(self.into()) } Some(TunnelCommand::BlockWhenDisconnected(block_when_disconnected)) => { diff --git a/talpid-core/src/tunnel_state_machine/disconnecting_state.rs b/talpid-core/src/tunnel_state_machine/disconnecting_state.rs index 35278901e1..0928834d1c 100644 --- a/talpid-core/src/tunnel_state_machine/disconnecting_state.rs +++ b/talpid-core/src/tunnel_state_machine/disconnecting_state.rs @@ -32,9 +32,8 @@ impl DisconnectingState { let _ = shared_values.set_allow_lan(allow_lan); AfterDisconnect::Nothing } - #[cfg(not(target_os = "android"))] Some(TunnelCommand::CustomDns(servers)) => { - shared_values.custom_dns = servers; + let _ = shared_values.set_custom_dns(servers); AfterDisconnect::Nothing } Some(TunnelCommand::BlockWhenDisconnected(block_when_disconnected)) => { @@ -54,9 +53,8 @@ impl DisconnectingState { let _ = shared_values.set_allow_lan(allow_lan); AfterDisconnect::Block(reason) } - #[cfg(not(target_os = "android"))] Some(TunnelCommand::CustomDns(servers)) => { - shared_values.custom_dns = servers; + let _ = shared_values.set_custom_dns(servers); AfterDisconnect::Block(reason) } Some(TunnelCommand::BlockWhenDisconnected(block_when_disconnected)) => { @@ -81,9 +79,8 @@ impl DisconnectingState { let _ = shared_values.set_allow_lan(allow_lan); AfterDisconnect::Reconnect(retry_attempt) } - #[cfg(not(target_os = "android"))] Some(TunnelCommand::CustomDns(servers)) => { - shared_values.custom_dns = servers; + let _ = shared_values.set_custom_dns(servers); AfterDisconnect::Reconnect(retry_attempt) } Some(TunnelCommand::BlockWhenDisconnected(block_when_disconnected)) => { diff --git a/talpid-core/src/tunnel_state_machine/error_state.rs b/talpid-core/src/tunnel_state_machine/error_state.rs index 7c01969ade..a87dccd5b4 100644 --- a/talpid-core/src/tunnel_state_machine/error_state.rs +++ b/talpid-core/src/tunnel_state_machine/error_state.rs @@ -105,10 +105,12 @@ impl TunnelState for ErrorState { SameState(self.into()) } } - #[cfg(not(target_os = "android"))] Some(TunnelCommand::CustomDns(servers)) => { - shared_values.custom_dns = servers; - SameState(self.into()) + if let Err(error_state_cause) = shared_values.set_custom_dns(servers) { + NewState(Self::enter(shared_values, error_state_cause)) + } else { + SameState(self.into()) + } } Some(TunnelCommand::BlockWhenDisconnected(block_when_disconnected)) => { shared_values.block_when_disconnected = block_when_disconnected; diff --git a/talpid-core/src/tunnel_state_machine/mod.rs b/talpid-core/src/tunnel_state_machine/mod.rs index 3e07bde0f4..adafe9f16e 100644 --- a/talpid-core/src/tunnel_state_machine/mod.rs +++ b/talpid-core/src/tunnel_state_machine/mod.rs @@ -23,11 +23,10 @@ use futures::{ channel::{mpsc, oneshot}, stream, StreamExt, }; -#[cfg(not(target_os = "android"))] -use std::net::IpAddr; use std::{ collections::HashSet, io, + net::IpAddr, path::{Path, PathBuf}, sync::{mpsc as sync_mpsc, Arc}, }; @@ -75,7 +74,7 @@ pub enum Error { pub async fn spawn( allow_lan: bool, block_when_disconnected: bool, - #[cfg(not(target_os = "android"))] custom_dns: Option<Vec<IpAddr>>, + custom_dns: Option<Vec<IpAddr>>, tunnel_parameters_generator: impl TunnelParametersGenerator, log_dir: Option<PathBuf>, resource_dir: PathBuf, @@ -101,6 +100,8 @@ pub async fn spawn( android_context, #[cfg(target_os = "android")] allow_lan, + #[cfg(target_os = "android")] + custom_dns.clone(), ); let runtime = tokio::runtime::Handle::current(); @@ -112,7 +113,6 @@ pub async fn spawn( allow_lan, block_when_disconnected, is_offline, - #[cfg(not(target_os = "android"))] custom_dns, tunnel_parameters_generator, tun_provider, @@ -153,7 +153,6 @@ pub enum TunnelCommand { /// Enable or disable LAN access in the firewall. AllowLan(bool), /// Set custom DNS servers to use. - #[cfg(not(target_os = "android"))] CustomDns(Option<Vec<IpAddr>>), /// Enable or disable the block_when_disconnected feature. BlockWhenDisconnected(bool), @@ -193,7 +192,7 @@ impl TunnelStateMachine { allow_lan: bool, block_when_disconnected: bool, is_offline: bool, - #[cfg(not(target_os = "android"))] custom_dns: Option<Vec<IpAddr>>, + custom_dns: Option<Vec<IpAddr>>, tunnel_parameters_generator: impl TunnelParametersGenerator, tun_provider: TunProvider, log_dir: Option<PathBuf>, @@ -218,7 +217,6 @@ impl TunnelStateMachine { allow_lan, block_when_disconnected, is_offline, - #[cfg(not(target_os = "android"))] custom_dns, tunnel_parameters_generator: Box::new(tunnel_parameters_generator), tun_provider, @@ -292,7 +290,6 @@ struct SharedTunnelStateValues { /// True when the computer is known to be offline. is_offline: bool, /// Custom DNS servers to use. - #[cfg(not(target_os = "android"))] custom_dns: Option<Vec<IpAddr>>, /// The generator of new `TunnelParameter`s tunnel_parameters_generator: Box<dyn TunnelParametersGenerator>, @@ -331,6 +328,32 @@ impl SharedTunnelStateValues { Ok(()) } + pub fn set_custom_dns( + &mut self, + custom_dns: Option<Vec<IpAddr>>, + ) -> Result<bool, ErrorStateCause> { + if self.custom_dns != custom_dns { + self.custom_dns = custom_dns.clone(); + + #[cfg(target_os = "android")] + { + if let Err(error) = self.tun_provider.set_custom_dns_servers(custom_dns) { + log::error!( + "{}", + error.display_chain_with_msg( + "Failed to restart tunnel after changing custom DNS servers", + ) + ); + return Err(ErrorStateCause::StartTunnelError); + } + } + + Ok(true) + } else { + Ok(false) + } + } + /// NetworkManager's connectivity check can get hung when DNS requests fail, thus the TSM /// should always disable it before applying firewall rules. The connectivity check should be /// reset whenever the firewall is cleared. diff --git a/talpid-types/Cargo.toml b/talpid-types/Cargo.toml index bde982e50c..2b40fca5a8 100644 --- a/talpid-types/Cargo.toml +++ b/talpid-types/Cargo.toml @@ -16,4 +16,4 @@ rand = "0.7" err-derive = "0.2.1" [target.'cfg(target_os = "android")'.dependencies] -jnix = { version = "0.2.3", features = ["derive"] } +jnix = { version = "0.3", features = ["derive"] } |
