summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md2
-rw-r--r--gui/src/main/daemon-rpc.ts21
-rw-r--r--mullvad-cli/src/cmds/custom_dns.rs156
-rw-r--r--mullvad-cli/src/cmds/dns.rs136
-rw-r--r--mullvad-cli/src/cmds/mod.rs9
-rw-r--r--mullvad-daemon/src/lib.rs44
-rw-r--r--mullvad-daemon/src/management_interface.rs28
-rw-r--r--mullvad-management-interface/proto/management_interface.proto18
-rw-r--r--mullvad-management-interface/src/types.rs88
-rw-r--r--mullvad-types/src/settings/migrations/mod.rs12
-rw-r--r--mullvad-types/src/settings/migrations/v1.rs2
-rw-r--r--mullvad-types/src/settings/migrations/v2.rs2
-rw-r--r--mullvad-types/src/settings/migrations/v3.rs189
-rw-r--r--mullvad-types/src/settings/mod.rs82
-rw-r--r--talpid-core/src/tunnel/tun_provider/android/mod.rs2
-rw-r--r--talpid-core/src/tunnel_state_machine/connected_state.rs57
-rw-r--r--talpid-core/src/tunnel_state_machine/connecting_state.rs14
-rw-r--r--talpid-core/src/tunnel_state_machine/disconnected_state.rs4
-rw-r--r--talpid-core/src/tunnel_state_machine/disconnecting_state.rs12
-rw-r--r--talpid-core/src/tunnel_state_machine/error_state.rs4
-rw-r--r--talpid-core/src/tunnel_state_machine/mod.rs30
21 files changed, 624 insertions, 288 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8595a44f6f..cee3e30c77 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -28,9 +28,11 @@ Line wrap the file at 100 chars. Th
management interface UDS socket. This means that only users in that group can use the CLI and GUI.
- Support WireGuard over TCP for custom VPN relays in the CLI.
- Make app native on Apple Silicon.
+- Add DNS options for ad and tracker blocking to the CLI.
### Changed
- Upgrade OpenVPN from 2.5.0 to 2.5.1.
+- Replace CLI command `mullvad custom-dns` with the new command `mullvad dns`.
#### Linux
- Only allow packets with the mark set to `0x6d6f6c65` to communicate with the relay server.
diff --git a/gui/src/main/daemon-rpc.ts b/gui/src/main/daemon-rpc.ts
index 8eef8848e2..5e28be9380 100644
--- a/gui/src/main/daemon-rpc.ts
+++ b/gui/src/main/daemon-rpc.ts
@@ -447,8 +447,21 @@ export class DaemonRpc {
public async setDnsOptions(dns: IDnsOptions): Promise<void> {
const dnsOptions = new grpcTypes.DnsOptions();
- dnsOptions.setCustom(dns.custom);
- dnsOptions.setAddressesList(dns.addresses);
+
+ const defaultOptions = new grpcTypes.DefaultDnsOptions();
+ defaultOptions.setBlockAds(false);
+ defaultOptions.setBlockTrackers(false);
+ dnsOptions.setDefaultOptions(defaultOptions);
+
+ const customOptions = new grpcTypes.CustomDnsOptions();
+ customOptions.setAddressesList(dns.addresses);
+ dnsOptions.setCustomOptions(customOptions);
+
+ if (dns.custom) {
+ dnsOptions.setState(grpcTypes.DnsOptions.DnsState.CUSTOM);
+ } else {
+ dnsOptions.setState(grpcTypes.DnsOptions.DnsState.DEFAULT);
+ }
await this.call<grpcTypes.DnsOptions, Empty>(this.client.setDnsOptions, dnsOptions);
}
@@ -1029,8 +1042,8 @@ function convertFromTunnelOptions(tunnelOptions: grpcTypes.TunnelOptions.AsObjec
enableIpv6: tunnelOptions.generic!.enableIpv6,
},
dns: {
- custom: tunnelOptions.dnsOptions?.custom ?? false,
- addresses: tunnelOptions.dnsOptions?.addressesList ?? [],
+ custom: tunnelOptions.dnsOptions!.state! === grpcTypes.DnsOptions.DnsState.CUSTOM,
+ addresses: tunnelOptions.dnsOptions?.customOptions?.addressesList ?? [],
},
};
}
diff --git a/mullvad-cli/src/cmds/custom_dns.rs b/mullvad-cli/src/cmds/custom_dns.rs
deleted file mode 100644
index 6213a8f6ea..0000000000
--- a/mullvad-cli/src/cmds/custom_dns.rs
+++ /dev/null
@@ -1,156 +0,0 @@
-use crate::{new_rpc_client, Command, Result};
-use clap::value_t_or_exit;
-use mullvad_management_interface::types;
-
-pub struct CustomDns;
-
-#[mullvad_management_interface::async_trait]
-impl Command for CustomDns {
- fn name(&self) -> &'static str {
- "custom-dns"
- }
-
- fn clap_subcommand(&self) -> clap::App<'static, 'static> {
- clap::SubCommand::with_name(self.name())
- .about("Configure custom DNS servers to use when connected")
- .setting(clap::AppSettings::SubcommandRequiredElseHelp)
- .subcommand(
- clap::SubCommand::with_name("servers")
- .about("Set custom DNS servers to use")
- .setting(clap::AppSettings::SubcommandRequiredElseHelp)
- .subcommand(
- clap::SubCommand::with_name("set")
- .about("Set custom DNS servers to use")
- .arg(
- clap::Arg::with_name("servers")
- .multiple(true)
- .help("One or more IP addresses pointing to DNS resolvers.")
- .required(true),
- ),
- )
- .subcommand(
- clap::SubCommand::with_name("clear").about("Remove all custom DNS servers"),
- ),
- )
- .subcommand(
- clap::SubCommand::with_name("get").about("Display the current custom DNS settings"),
- )
- .subcommand(
- clap::SubCommand::with_name("set")
- .about("Enable or disable custom DNS")
- .arg(
- clap::Arg::with_name("policy")
- .required(true)
- .possible_values(&["on", "off"]),
- ),
- )
- }
-
- async fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
- match matches.subcommand() {
- ("servers", Some(matches)) => match matches.subcommand() {
- ("set", Some(matches)) => {
- self.set_servers(matches.values_of_lossy("servers")).await
- }
- ("clear", _) => self.clear_servers().await,
- _ => unreachable!("No custom-dns server command given"),
- },
- ("set", Some(matches)) => {
- let policy = value_t_or_exit!(matches.value_of("policy"), String);
- self.set_state(policy == "on").await
- }
- ("get", _) => self.get().await,
- _ => unreachable!("No custom-dns command given"),
- }
- }
-}
-
-impl CustomDns {
- async fn set_state(&self, enabled: bool) -> Result<()> {
- let mut rpc = new_rpc_client().await?;
- let options = rpc
- .get_settings(())
- .await?
- .into_inner()
- .tunnel_options
- .unwrap()
- .dns_options
- .unwrap();
- rpc.set_dns_options(types::DnsOptions {
- custom: enabled,
- addresses: options.addresses,
- })
- .await?;
- println!("Updated custom DNS settings");
- Ok(())
- }
-
- async fn set_servers(&self, servers: Option<Vec<String>>) -> Result<()> {
- let mut rpc = new_rpc_client().await?;
- let options = rpc
- .get_settings(())
- .await?
- .into_inner()
- .tunnel_options
- .unwrap()
- .dns_options
- .unwrap();
- rpc.set_dns_options(types::DnsOptions {
- custom: options.custom,
- addresses: servers.unwrap_or_default(),
- })
- .await?;
- println!("Updated custom DNS settings");
- Ok(())
- }
-
- async fn clear_servers(&self) -> Result<()> {
- let mut rpc = new_rpc_client().await?;
- let options = rpc
- .get_settings(())
- .await?
- .into_inner()
- .tunnel_options
- .unwrap()
- .dns_options
- .unwrap();
- rpc.set_dns_options(types::DnsOptions {
- custom: options.custom,
- addresses: vec![],
- })
- .await?;
- println!("Cleared list of custom DNS servers");
- Ok(())
- }
-
- async fn get(&self) -> Result<()> {
- let mut rpc = new_rpc_client().await?;
- let options = rpc
- .get_settings(())
- .await?
- .into_inner()
- .tunnel_options
- .unwrap()
- .dns_options
- .unwrap();
-
- let state = if options.custom {
- "enabled"
- } else {
- "disabled"
- };
- println!("Custom DNS: {}", state);
-
- match options.addresses.len() {
- 0 => println!("No DNS servers are configured"),
- _ => {
- println!("Servers:");
- for server in &options.addresses {
- println!("\t{}", server);
- }
- }
- }
-
- Ok(())
- }
-}
diff --git a/mullvad-cli/src/cmds/dns.rs b/mullvad-cli/src/cmds/dns.rs
new file mode 100644
index 0000000000..9ffa0ec09c
--- /dev/null
+++ b/mullvad-cli/src/cmds/dns.rs
@@ -0,0 +1,136 @@
+use crate::{new_rpc_client, Command, Result};
+use mullvad_management_interface::types;
+use mullvad_types::settings::{DnsOptions, DnsState};
+use std::convert::TryInto;
+
+pub struct Dns;
+
+#[mullvad_management_interface::async_trait]
+impl Command for Dns {
+ fn name(&self) -> &'static str {
+ "dns"
+ }
+
+ fn clap_subcommand(&self) -> clap::App<'static, 'static> {
+ clap::SubCommand::with_name(self.name())
+ .about("Configure DNS servers to use when connected")
+ .setting(clap::AppSettings::SubcommandRequiredElseHelp)
+ .subcommand(
+ clap::SubCommand::with_name("get").about("Display the current DNS settings"),
+ )
+ .subcommand(
+ clap::SubCommand::with_name("set")
+ .about("Set DNS servers to use")
+ .setting(clap::AppSettings::SubcommandRequiredElseHelp)
+ .subcommand(
+ clap::SubCommand::with_name("default")
+ .about("Use default DNS servers")
+ .arg(
+ clap::Arg::with_name("block ads")
+ .long("block-ads")
+ .takes_value(false)
+ .help("Block domain names used for ads"),
+ )
+ .arg(
+ clap::Arg::with_name("block trackers")
+ .long("block-trackers")
+ .takes_value(false)
+ .help("Block domain names used for tracking"),
+ ),
+ )
+ .subcommand(
+ clap::SubCommand::with_name("custom")
+ .about("Set a list of custom DNS servers")
+ .arg(
+ clap::Arg::with_name("servers")
+ .multiple(true)
+ .help("One or more IP addresses pointing to DNS resolvers.")
+ .required(true),
+ ),
+ ),
+ )
+ }
+
+ async fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
+ match matches.subcommand() {
+ ("set", Some(matches)) => match matches.subcommand() {
+ ("default", Some(matches)) => {
+ self.set_default(
+ matches.is_present("block ads"),
+ matches.is_present("block trackers"),
+ )
+ .await
+ }
+ ("custom", Some(matches)) => {
+ self.set_custom(matches.values_of_lossy("servers")).await
+ }
+ _ => unreachable!("No custom-dns server command given"),
+ },
+ ("get", _) => self.get().await,
+ _ => unreachable!("No custom-dns command given"),
+ }
+ }
+}
+
+impl Dns {
+ async fn set_default(&self, block_ads: bool, block_trackers: bool) -> Result<()> {
+ let mut rpc = new_rpc_client().await?;
+ let settings = rpc.get_settings(()).await?.into_inner();
+ rpc.set_dns_options(types::DnsOptions {
+ state: types::dns_options::DnsState::Default as i32,
+ default_options: Some(types::DefaultDnsOptions {
+ block_ads,
+ block_trackers,
+ }),
+ ..settings.tunnel_options.unwrap().dns_options.unwrap()
+ })
+ .await?;
+ println!("Updated DNS settings");
+ Ok(())
+ }
+
+ async fn set_custom(&self, servers: Option<Vec<String>>) -> Result<()> {
+ let mut rpc = new_rpc_client().await?;
+ let settings = rpc.get_settings(()).await?.into_inner();
+ rpc.set_dns_options(types::DnsOptions {
+ state: types::dns_options::DnsState::Custom as i32,
+ custom_options: Some(types::CustomDnsOptions {
+ addresses: servers.unwrap_or_default(),
+ }),
+ ..settings.tunnel_options.unwrap().dns_options.unwrap()
+ })
+ .await?;
+ println!("Updated DNS settings");
+ Ok(())
+ }
+
+ async fn get(&self) -> Result<()> {
+ let mut rpc = new_rpc_client().await?;
+ let options: DnsOptions = rpc
+ .get_settings(())
+ .await?
+ .into_inner()
+ .tunnel_options
+ .unwrap()
+ .dns_options
+ .unwrap()
+ .try_into()
+ .unwrap();
+
+ match options.state {
+ DnsState::Default => {
+ println!("Custom DNS: no");
+ println!("Block ads: {}", options.default_options.block_ads);
+ println!("Block trackers: {}", options.default_options.block_trackers);
+ }
+ DnsState::Custom => {
+ println!("Custom DNS: yes\nServers:");
+ for server in &options.custom_options.addresses {
+ println!("{}", server);
+ }
+ }
+ }
+
+ Ok(())
+ }
+}
diff --git a/mullvad-cli/src/cmds/mod.rs b/mullvad-cli/src/cmds/mod.rs
index eb999a2936..bd7ca97372 100644
--- a/mullvad-cli/src/cmds/mod.rs
+++ b/mullvad-cli/src/cmds/mod.rs
@@ -22,12 +22,12 @@ pub use self::connect::Connect;
mod disconnect;
pub use self::disconnect::Disconnect;
+mod dns;
+pub use self::dns::Dns;
+
mod lan;
pub use self::lan::Lan;
-mod custom_dns;
-pub use self::custom_dns::CustomDns;
-
mod reconnect;
pub use self::reconnect::Reconnect;
@@ -61,10 +61,9 @@ pub fn get_commands() -> HashMap<&'static str, Box<dyn Command>> {
Box::new(Bridge),
Box::new(Connect),
Box::new(Disconnect),
+ Box::new(Dns),
Box::new(Reconnect),
Box::new(Lan),
- #[cfg(not(target_os = "android"))]
- Box::new(CustomDns),
Box::new(Relay),
Box::new(Reset),
#[cfg(target_os = "linux")]
diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs
index e5f57373a8..d44ce55153 100644
--- a/mullvad-daemon/src/lib.rs
+++ b/mullvad-daemon/src/lib.rs
@@ -35,7 +35,7 @@ use mullvad_types::{
RelaySettingsUpdate,
},
relay_list::{Relay, RelayList},
- settings::{DnsOptions, Settings},
+ settings::{DnsOptions, DnsState, Settings},
states::{TargetState, TunnelState},
version::{AppVersion, AppVersionInfo},
wireguard::{KeygenEvent, RotationInterval},
@@ -80,6 +80,12 @@ const FIRST_KEY_PUSH_TIMEOUT: Duration = Duration::from_secs(5);
/// Delay between generating a new WireGuard key and reconnecting
const WG_RECONNECT_DELAY: Duration = Duration::from_secs(4 * 60);
+lazy_static::lazy_static! {
+ static ref DNS_AD_BLOCKING_SERVERS: [IpAddr; 1] = ["100.64.0.1".parse().unwrap()];
+ static ref DNS_TRACKER_BLOCKING_SERVERS: [IpAddr; 1] = ["100.64.0.2".parse().unwrap()];
+ static ref DNS_AD_TRACKER_BLOCKING_SERVERS: [IpAddr; 1] = ["100.64.0.3".parse().unwrap()];
+}
+
pub type ResponseTx<T, E> = oneshot::Sender<Result<T, E>>;
#[derive(err_derive::Error, Debug)]
@@ -216,7 +222,7 @@ pub enum DaemonCommand {
SetBridgeState(ResponseTx<(), settings::Error>, BridgeState),
/// Set if IPv6 should be enabled in the tunnel
SetEnableIpv6(ResponseTx<(), settings::Error>, bool),
- /// Set custom DNS servers to use instead of passing requests to the gateway
+ /// Set DNS options or servers to use
SetDnsOptions(ResponseTx<(), settings::Error>, DnsOptions),
/// Set MTU for wireguard tunnels
SetWireguardMtu(ResponseTx<(), settings::Error>, Option<u16>),
@@ -629,7 +635,7 @@ where
let tunnel_command_tx = tunnel_state_machine::spawn(
settings.allow_lan,
settings.block_when_disconnected,
- Self::get_custom_resolvers(&settings.tunnel_options.dns_options),
+ Self::get_dns_resolvers(&settings.tunnel_options.dns_options),
initial_api_endpoint,
tunnel_parameters_generator,
log_dir,
@@ -694,11 +700,28 @@ where
Ok(daemon)
}
- fn get_custom_resolvers(dns_options: &DnsOptions) -> Option<Vec<IpAddr>> {
- if dns_options.custom && !dns_options.addresses.is_empty() {
- Some(dns_options.addresses.clone())
- } else {
- None
+ fn get_dns_resolvers(options: &DnsOptions) -> Option<Vec<IpAddr>> {
+ match options.state {
+ DnsState::Default => {
+ if options.default_options.block_ads {
+ if options.default_options.block_trackers {
+ Some(DNS_AD_TRACKER_BLOCKING_SERVERS.to_vec())
+ } else {
+ Some(DNS_AD_BLOCKING_SERVERS.to_vec())
+ }
+ } else if options.default_options.block_trackers {
+ Some(DNS_TRACKER_BLOCKING_SERVERS.to_vec())
+ } else {
+ None
+ }
+ }
+ DnsState::Custom => {
+ if options.custom_options.addresses.is_empty() {
+ None
+ } else {
+ Some(options.custom_options.addresses.clone())
+ }
+ }
}
}
@@ -1854,10 +1877,9 @@ where
Self::oneshot_send(tx, Ok(()), "set_dns_options response");
if settings_changed {
let settings = self.settings.to_settings();
- let resolvers =
- Self::get_custom_resolvers(&settings.tunnel_options.dns_options);
+ let resolvers = Self::get_dns_resolvers(&settings.tunnel_options.dns_options);
self.event_listener.notify_settings(settings);
- self.send_tunnel_command(TunnelCommand::CustomDns(resolvers));
+ self.send_tunnel_command(TunnelCommand::Dns(resolvers));
}
}
Err(e) => {
diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs
index a9b07a1214..2e1d8cbc82 100644
--- a/mullvad-daemon/src/management_interface.rs
+++ b/mullvad-daemon/src/management_interface.rs
@@ -361,31 +361,13 @@ impl ManagementService for ManagementServiceImpl {
#[cfg(not(target_os = "android"))]
async fn set_dns_options(&self, request: Request<types::DnsOptions>) -> ServiceResult<()> {
- let options = request.into_inner();
- log::debug!(
- "set_dns_options({}, {:?})",
- options.custom,
- options.addresses
- );
-
- let mut servers_ip = vec![];
- for server in options.addresses.into_iter() {
- if let Ok(addr) = server.parse() {
- servers_ip.push(addr);
- } else {
- let err_msg = format!("failed to parse IP address: {}", server);
- return Err(Status::invalid_argument(err_msg));
- }
- }
+ let options = DnsOptions::try_from(request.into_inner()).map_err(|error| match error {
+ types::FromProtobufTypeError::InvalidArgument(error) => Status::invalid_argument(error),
+ })?;
+ log::debug!("set_dns_options({:?})", options);
let (tx, rx) = oneshot::channel();
- self.send_command_to_daemon(DaemonCommand::SetDnsOptions(
- tx,
- DnsOptions {
- custom: options.custom,
- addresses: servers_ip,
- },
- ))?;
+ self.send_command_to_daemon(DaemonCommand::SetDnsOptions(tx, options))?;
self.wait_for_result(rx)
.await?
.map(Response::new)
diff --git a/mullvad-management-interface/proto/management_interface.proto b/mullvad-management-interface/proto/management_interface.proto
index ba36deeea4..41ba8c96a9 100644
--- a/mullvad-management-interface/proto/management_interface.proto
+++ b/mullvad-management-interface/proto/management_interface.proto
@@ -376,9 +376,23 @@ message TunnelOptions {
DnsOptions dns_options = 4;
}
+message DefaultDnsOptions {
+ bool block_ads = 1;
+ bool block_trackers = 2;
+}
+
+message CustomDnsOptions {
+ repeated string addresses = 1;
+}
+
message DnsOptions {
- bool custom = 1;
- repeated string addresses = 2;
+ enum DnsState {
+ DEFAULT = 0;
+ CUSTOM = 1;
+ }
+ DnsState state = 1;
+ DefaultDnsOptions default_options = 2;
+ CustomDnsOptions custom_options = 3;
}
message PublicKey {
diff --git a/mullvad-management-interface/src/types.rs b/mullvad-management-interface/src/types.rs
index 753149f35e..aff3a30789 100644
--- a/mullvad-management-interface/src/types.rs
+++ b/mullvad-management-interface/src/types.rs
@@ -472,6 +472,29 @@ impl From<mullvad_types::relay_constraints::RelaySettings> for RelaySettings {
}
}
+impl From<&mullvad_types::settings::DnsOptions> for DnsOptions {
+ fn from(options: &mullvad_types::settings::DnsOptions) -> Self {
+ DnsOptions {
+ state: match options.state {
+ mullvad_types::settings::DnsState::Default => dns_options::DnsState::Default as i32,
+ mullvad_types::settings::DnsState::Custom => dns_options::DnsState::Custom as i32,
+ },
+ default_options: Some(DefaultDnsOptions {
+ block_ads: options.default_options.block_ads,
+ block_trackers: options.default_options.block_trackers,
+ }),
+ custom_options: Some(CustomDnsOptions {
+ addresses: options
+ .custom_options
+ .addresses
+ .iter()
+ .map(|addr| addr.to_string())
+ .collect(),
+ }),
+ }
+ }
+}
+
impl From<&mullvad_types::settings::TunnelOptions> for TunnelOptions {
fn from(options: &mullvad_types::settings::TunnelOptions) -> Self {
Self {
@@ -489,15 +512,7 @@ impl From<&mullvad_types::settings::TunnelOptions> for TunnelOptions {
enable_ipv6: options.generic.enable_ipv6,
}),
#[cfg(not(target_os = "android"))]
- dns_options: Some(DnsOptions {
- custom: options.dns_options.custom,
- addresses: options
- .dns_options
- .addresses
- .iter()
- .map(|addr| addr.to_string())
- .collect(),
- }),
+ dns_options: Some(DnsOptions::from(&options.dns_options)),
#[cfg(target_os = "android")]
dns_options: None,
}
@@ -606,6 +621,7 @@ impl From<TransportProtocol> for talpid_types::net::TransportProtocol {
}
}
+#[derive(Debug)]
pub enum FromProtobufTypeError {
InvalidArgument(&'static str),
}
@@ -1008,6 +1024,60 @@ impl TryFrom<BridgeState> for mullvad_types::relay_constraints::BridgeState {
}
}
+impl TryFrom<DnsOptions> for mullvad_types::settings::DnsOptions {
+ type Error = FromProtobufTypeError;
+
+ fn try_from(options: DnsOptions) -> Result<Self, Self::Error> {
+ use mullvad_types::settings::{
+ CustomDnsOptions as MullvadCustomDnsOptions,
+ DefaultDnsOptions as MullvadDefaultDnsOptions, DnsOptions as MullvadDnsOptions,
+ DnsState as MullvadDnsState,
+ };
+
+ let state = match dns_options::DnsState::from_i32(options.state) {
+ Some(dns_options::DnsState::Default) => MullvadDnsState::Default,
+ Some(dns_options::DnsState::Custom) => MullvadDnsState::Custom,
+ None => {
+ return Err(FromProtobufTypeError::InvalidArgument(
+ "invalid DNS options state",
+ ))
+ }
+ };
+
+ let default_options =
+ options
+ .default_options
+ .ok_or(FromProtobufTypeError::InvalidArgument(
+ "missing default DNS options",
+ ))?;
+ let custom_options =
+ options
+ .custom_options
+ .ok_or(FromProtobufTypeError::InvalidArgument(
+ "missing default DNS options",
+ ))?;
+
+ Ok(MullvadDnsOptions {
+ state,
+ default_options: MullvadDefaultDnsOptions {
+ block_ads: default_options.block_ads,
+ block_trackers: default_options.block_trackers,
+ },
+ custom_options: MullvadCustomDnsOptions {
+ addresses: custom_options
+ .addresses
+ .into_iter()
+ .map(|addr| {
+ addr.parse().map_err(|_| {
+ FromProtobufTypeError::InvalidArgument("invalid IP address")
+ })
+ })
+ .collect::<Result<Vec<_>, _>>()?,
+ },
+ })
+ }
+}
+
fn convert_providers_constraint(
providers: &Constraint<mullvad_types::relay_constraints::Providers>,
) -> Vec<String> {
diff --git a/mullvad-types/src/settings/migrations/mod.rs b/mullvad-types/src/settings/migrations/mod.rs
index 0d40e1e564..433b7ba5bc 100644
--- a/mullvad-types/src/settings/migrations/mod.rs
+++ b/mullvad-types/src/settings/migrations/mod.rs
@@ -2,6 +2,7 @@ use super::{Error, Result};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
mod v1;
mod v2;
+mod v3;
#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
@@ -9,6 +10,7 @@ mod v2;
pub enum SettingsVersion {
V2 = 2,
V3 = 3,
+ V4 = 4,
}
pub const CURRENT_SETTINGS_VERSION: SettingsVersion = SettingsVersion::V3;
@@ -21,6 +23,7 @@ impl<'de> Deserialize<'de> for SettingsVersion {
match <u32>::deserialize(deserializer)? {
v if v == SettingsVersion::V2 as u32 => Ok(SettingsVersion::V2),
v if v == SettingsVersion::V3 as u32 => Ok(SettingsVersion::V3),
+ v if v == SettingsVersion::V4 as u32 => Ok(SettingsVersion::V4),
v => Err(serde::de::Error::custom(format!(
"{} is not a valid SettingsVersion",
v
@@ -52,8 +55,11 @@ pub fn try_migrate_settings(mut settings_file: &[u8]) -> Result<crate::settings:
return Err(Error::NoMatchingVersion);
}
- let migrations: Vec<Box<dyn SettingsMigration>> =
- vec![Box::new(v1::Migration), Box::new(v2::Migration)];
+ let migrations: Vec<Box<dyn SettingsMigration>> = vec![
+ Box::new(v1::Migration),
+ Box::new(v2::Migration),
+ Box::new(v3::Migration),
+ ];
for migration in &migrations {
if !migration.version_matches(&mut settings) {
@@ -79,7 +85,7 @@ mod test {
#[test]
#[should_panic]
fn test_deserialization_failure_version_too_big() {
- let _version: SettingsVersion = serde_json::from_str("4").expect("Version too big");
+ let _version: SettingsVersion = serde_json::from_str("100").expect("Version too big");
}
#[test]
diff --git a/mullvad-types/src/settings/migrations/v1.rs b/mullvad-types/src/settings/migrations/v1.rs
index 15ce57f0f5..870b9b9d09 100644
--- a/mullvad-types/src/settings/migrations/v1.rs
+++ b/mullvad-types/src/settings/migrations/v1.rs
@@ -128,7 +128,7 @@ mod test {
"enable_ipv6": false
}
},
- "settings_version": 3
+ "settings_version": 4
}
"#;
diff --git a/mullvad-types/src/settings/migrations/v2.rs b/mullvad-types/src/settings/migrations/v2.rs
index 393da1827c..6e74f60ccb 100644
--- a/mullvad-types/src/settings/migrations/v2.rs
+++ b/mullvad-types/src/settings/migrations/v2.rs
@@ -160,7 +160,7 @@ mod test {
"enable_ipv6": false
}
},
- "settings_version": 3
+ "settings_version": 4
}
"#;
diff --git a/mullvad-types/src/settings/migrations/v3.rs b/mullvad-types/src/settings/migrations/v3.rs
new file mode 100644
index 0000000000..fc56745f06
--- /dev/null
+++ b/mullvad-types/src/settings/migrations/v3.rs
@@ -0,0 +1,189 @@
+use super::{Error, Result, SettingsVersion};
+use crate::settings::{CustomDnsOptions, DefaultDnsOptions, DnsOptions, DnsState};
+
+
+pub(super) struct Migration;
+
+impl super::SettingsMigration for Migration {
+ fn version_matches(&self, settings: &mut serde_json::Value) -> bool {
+ settings
+ .get("settings_version")
+ .map(|version| version == SettingsVersion::V3 as u64)
+ .unwrap_or(false)
+ }
+
+ fn migrate(&self, settings: &mut serde_json::Value) -> Result<()> {
+ log::info!("Migrating settings format to V4");
+
+ let dns_options = || -> Option<&serde_json::Value> {
+ settings.get("tunnel_options")?.get("dns_options")
+ }();
+
+ if let Some(options) = dns_options {
+ let new_state = if options
+ .get("custom")
+ .map(|custom| custom.as_bool().unwrap_or(false))
+ .unwrap_or(false)
+ {
+ DnsState::Custom
+ } else {
+ DnsState::Default
+ };
+ let addresses = if let Some(addrs) = options.get("addresses") {
+ serde_json::from_value(addrs.clone()).map_err(Error::ParseError)?
+ } else {
+ vec![]
+ };
+
+ settings["tunnel_options"]["dns_options"] = serde_json::json!(DnsOptions {
+ state: new_state,
+ default_options: DefaultDnsOptions::default(),
+ custom_options: CustomDnsOptions { addresses },
+ });
+ }
+
+ settings["settings_version"] = serde_json::json!(SettingsVersion::V4);
+
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::super::try_migrate_settings;
+ use serde_json;
+
+ pub const V3_SETTINGS: &str = r#"
+{
+ "account_token": "1234",
+ "relay_settings": {
+ "normal": {
+ "location": {
+ "only": {
+ "country": "se"
+ }
+ },
+ "tunnel_protocol": "any",
+ "wireguard_constraints": {
+ "port": "any"
+ },
+ "openvpn_constraints": {
+ "port": {
+ "only": 53
+ },
+ "protocol": {
+ "only": "udp"
+ }
+ }
+ }
+ },
+ "bridge_settings": {
+ "normal": {
+ "location": "any"
+ }
+ },
+ "bridge_state": "auto",
+ "allow_lan": true,
+ "block_when_disconnected": false,
+ "auto_connect": false,
+ "tunnel_options": {
+ "openvpn": {
+ "mssfix": null
+ },
+ "wireguard": {
+ "mtu": null,
+ "rotation_interval": {
+ "secs": 86400,
+ "nanos": 0
+ }
+ },
+ "generic": {
+ "enable_ipv6": false
+ },
+ "dns_options": {
+ "custom": false,
+ "addresses": [
+ "1.1.1.1",
+ "1.2.3.4"
+ ]
+ }
+ },
+ "settings_version": 3
+}
+"#;
+
+ pub const NEW_SETTINGS: &str = r#"
+{
+ "account_token": "1234",
+ "relay_settings": {
+ "normal": {
+ "location": {
+ "only": {
+ "country": "se"
+ }
+ },
+ "tunnel_protocol": "any",
+ "wireguard_constraints": {
+ "port": "any"
+ },
+ "openvpn_constraints": {
+ "port": {
+ "only": 53
+ },
+ "protocol": {
+ "only": "udp"
+ }
+ }
+ }
+ },
+ "bridge_settings": {
+ "normal": {
+ "location": "any"
+ }
+ },
+ "bridge_state": "auto",
+ "allow_lan": true,
+ "block_when_disconnected": false,
+ "auto_connect": false,
+ "tunnel_options": {
+ "openvpn": {
+ "mssfix": null
+ },
+ "wireguard": {
+ "mtu": null,
+ "rotation_interval": {
+ "secs": 86400,
+ "nanos": 0
+ }
+ },
+ "generic": {
+ "enable_ipv6": false
+ },
+ "dns_options": {
+ "state": "default",
+ "default_options": {
+ "block_ads": false,
+ "block_trackers": false
+ },
+ "custom_options": {
+ "addresses": [
+ "1.1.1.1",
+ "1.2.3.4"
+ ]
+ }
+ }
+ },
+ "settings_version": 4
+}
+"#;
+
+
+ #[test]
+ fn test_v3_migration() {
+ let migrated_settings =
+ try_migrate_settings(V3_SETTINGS.as_bytes()).expect("Migration failed");
+ let new_settings = serde_json::from_str(NEW_SETTINGS).unwrap();
+
+ assert_eq!(&migrated_settings, &new_settings);
+ }
+}
diff --git a/mullvad-types/src/settings/mod.rs b/mullvad-types/src/settings/mod.rs
index 1cd900edfc..0430f530ea 100644
--- a/mullvad-types/src/settings/mod.rs
+++ b/mullvad-types/src/settings/mod.rs
@@ -6,7 +6,7 @@ use crate::{
wireguard,
};
#[cfg(target_os = "android")]
-use jnix::{FromJava, IntoJava};
+use jnix::{jni::objects::JObject, FromJava, IntoJava, JnixEnv};
use log::{debug, info};
use serde::{Deserialize, Serialize};
use serde_json;
@@ -174,19 +174,87 @@ pub struct TunnelOptions {
/// Contains generic tunnel options that may apply to more than a single tunnel type.
#[cfg_attr(target_os = "android", jnix(skip))]
pub generic: GenericTunnelOptions,
- /// Custom DNS options.
+ /// DNS options.
pub dns_options: DnsOptions,
}
-/// Custom DNS config
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
+#[serde(rename_all = "snake_case")]
+pub enum DnsState {
+ Default,
+ Custom,
+}
+
+impl Default for DnsState {
+ fn default() -> Self {
+ Self::Default
+ }
+}
+
+/// DNS config
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
-#[serde(default)]
-#[cfg_attr(target_os = "android", derive(FromJava, IntoJava))]
+#[cfg_attr(target_os = "android", derive(IntoJava))]
#[cfg_attr(target_os = "android", jnix(package = "net.mullvad.mullvadvpn.model"))]
pub struct DnsOptions {
- /// Whether to use the addresses in `custom_dns`.
+ #[cfg_attr(target_os = "android", jnix(map = "|state| state == DnsState::Custom"))]
+ pub state: DnsState,
+ #[cfg_attr(target_os = "android", jnix(skip))]
+ pub default_options: DefaultDnsOptions,
+ #[cfg_attr(target_os = "android", jnix(map = "|opts| opts.addresses"))]
+ pub custom_options: CustomDnsOptions,
+}
+
+#[cfg(target_os = "android")]
+#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
+#[cfg_attr(target_os = "android", derive(FromJava))]
+#[cfg_attr(
+ target_os = "android",
+ jnix(class_name = "net.mullvad.mullvadvpn.model.DnsOptions")
+)]
+pub struct AndroidDnsOptions {
pub custom: bool,
- /// Custom DNS servers to use.
+ pub addresses: Vec<IpAddr>,
+}
+
+#[cfg(target_os = "android")]
+impl From<AndroidDnsOptions> for DnsOptions {
+ fn from(options: AndroidDnsOptions) -> Self {
+ Self {
+ state: if options.custom {
+ DnsState::Custom
+ } else {
+ DnsState::Default
+ },
+ default_options: DefaultDnsOptions::default(),
+ custom_options: CustomDnsOptions {
+ addresses: options.addresses,
+ },
+ }
+ }
+}
+
+#[cfg(target_os = "android")]
+impl<'env, 'sub_env> FromJava<'env, JObject<'sub_env>> for DnsOptions
+where
+ 'env: 'sub_env,
+{
+ const JNI_SIGNATURE: &'static str = "Lnet/mullvad/mullvadvpn/model/DnsOptions";
+
+ fn from_java(env: &JnixEnv<'env>, object: JObject<'sub_env>) -> Self {
+ AndroidDnsOptions::from_java(env, object).into()
+ }
+}
+
+/// Default DNS config
+#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
+pub struct DefaultDnsOptions {
+ pub block_ads: bool,
+ pub block_trackers: bool,
+}
+
+/// Custom DNS config
+#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
+pub struct CustomDnsOptions {
pub addresses: Vec<IpAddr>,
}
diff --git a/talpid-core/src/tunnel/tun_provider/android/mod.rs b/talpid-core/src/tunnel/tun_provider/android/mod.rs
index 575f05f4c2..3febda73d4 100644
--- a/talpid-core/src/tunnel/tun_provider/android/mod.rs
+++ b/talpid-core/src/tunnel/tun_provider/android/mod.rs
@@ -110,7 +110,7 @@ impl AndroidTunProvider {
self.allowed_endpoint = endpoint;
}
- pub fn set_custom_dns_servers(&mut self, servers: Option<Vec<IpAddr>>) -> Result<(), Error> {
+ pub fn set_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()?;
diff --git a/talpid-core/src/tunnel_state_machine/connected_state.rs b/talpid-core/src/tunnel_state_machine/connected_state.rs
index dd2102127e..ea8ce8e992 100644
--- a/talpid-core/src/tunnel_state_machine/connected_state.rs
+++ b/talpid-core/src/tunnel_state_machine/connected_state.rs
@@ -80,7 +80,7 @@ impl ConnectedState {
#[allow(unused_variables)]
fn get_dns_servers(&self, shared_values: &SharedTunnelStateValues) -> Vec<IpAddr> {
#[cfg(not(target_os = "android"))]
- if let Some(ref servers) = shared_values.custom_dns {
+ if let Some(ref servers) = shared_values.dns_servers {
servers.clone()
} else {
let mut dns_ips = vec![];
@@ -197,41 +197,34 @@ impl ConnectedState {
}
SameState(self.into())
}
- Some(TunnelCommand::CustomDns(servers)) => {
- 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::SetFirewallPolicyError(
- error,
- )),
- );
- }
+ Some(TunnelCommand::Dns(servers)) => match shared_values.set_dns_servers(servers) {
+ Ok(true) => {
+ 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) {
- #[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),
- )
- }
+ 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),
+ )
}
}
- Ok(false) => SameState(self.into()),
- Err(error_cause) => {
- self.disconnect(shared_values, AfterDisconnect::Block(error_cause))
- }
}
- }
+ Ok(false) => SameState(self.into()),
+ Err(error_cause) => {
+ self.disconnect(shared_values, AfterDisconnect::Block(error_cause))
+ }
+ },
Some(TunnelCommand::BlockWhenDisconnected(block_when_disconnected)) => {
shared_values.block_when_disconnected = block_when_disconnected;
SameState(self.into())
diff --git a/talpid-core/src/tunnel_state_machine/connecting_state.rs b/talpid-core/src/tunnel_state_machine/connecting_state.rs
index 29c3bb0a90..553cf9377d 100644
--- a/talpid-core/src/tunnel_state_machine/connecting_state.rs
+++ b/talpid-core/src/tunnel_state_machine/connecting_state.rs
@@ -267,14 +267,12 @@ impl ConnectingState {
}
SameState(self.into())
}
- Some(TunnelCommand::CustomDns(servers)) => {
- 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::Dns(servers)) => match shared_values.set_dns_servers(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;
SameState(self.into())
diff --git a/talpid-core/src/tunnel_state_machine/disconnected_state.rs b/talpid-core/src/tunnel_state_machine/disconnected_state.rs
index 7a02a9e550..8d2c9bc0fa 100644
--- a/talpid-core/src/tunnel_state_machine/disconnected_state.rs
+++ b/talpid-core/src/tunnel_state_machine/disconnected_state.rs
@@ -87,10 +87,10 @@ impl TunnelState for DisconnectedState {
}
SameState(self.into())
}
- Some(TunnelCommand::CustomDns(servers)) => {
+ Some(TunnelCommand::Dns(servers)) => {
// Same situation as allow LAN above.
shared_values
- .set_custom_dns(servers)
+ .set_dns_servers(servers)
.expect("Failed to reconnect after changing custom DNS servers");
SameState(self.into())
diff --git a/talpid-core/src/tunnel_state_machine/disconnecting_state.rs b/talpid-core/src/tunnel_state_machine/disconnecting_state.rs
index 7ddbdcf5da..7d308d5971 100644
--- a/talpid-core/src/tunnel_state_machine/disconnecting_state.rs
+++ b/talpid-core/src/tunnel_state_machine/disconnecting_state.rs
@@ -39,8 +39,8 @@ impl DisconnectingState {
}
AfterDisconnect::Nothing
}
- Some(TunnelCommand::CustomDns(servers)) => {
- let _ = shared_values.set_custom_dns(servers);
+ Some(TunnelCommand::Dns(servers)) => {
+ let _ = shared_values.set_dns_servers(servers);
AfterDisconnect::Nothing
}
Some(TunnelCommand::BlockWhenDisconnected(block_when_disconnected)) => {
@@ -72,8 +72,8 @@ impl DisconnectingState {
}
AfterDisconnect::Block(reason)
}
- Some(TunnelCommand::CustomDns(servers)) => {
- let _ = shared_values.set_custom_dns(servers);
+ Some(TunnelCommand::Dns(servers)) => {
+ let _ = shared_values.set_dns_servers(servers);
AfterDisconnect::Block(reason)
}
Some(TunnelCommand::BlockWhenDisconnected(block_when_disconnected)) => {
@@ -110,8 +110,8 @@ impl DisconnectingState {
}
AfterDisconnect::Reconnect(retry_attempt)
}
- Some(TunnelCommand::CustomDns(servers)) => {
- let _ = shared_values.set_custom_dns(servers);
+ Some(TunnelCommand::Dns(servers)) => {
+ let _ = shared_values.set_dns_servers(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 6674cca13e..5e647c8201 100644
--- a/talpid-core/src/tunnel_state_machine/error_state.rs
+++ b/talpid-core/src/tunnel_state_machine/error_state.rs
@@ -123,8 +123,8 @@ impl TunnelState for ErrorState {
}
SameState(self.into())
}
- Some(TunnelCommand::CustomDns(servers)) => {
- if let Err(error_state_cause) = shared_values.set_custom_dns(servers) {
+ Some(TunnelCommand::Dns(servers)) => {
+ if let Err(error_state_cause) = shared_values.set_dns_servers(servers) {
NewState(Self::enter(shared_values, error_state_cause))
} else {
SameState(self.into())
diff --git a/talpid-core/src/tunnel_state_machine/mod.rs b/talpid-core/src/tunnel_state_machine/mod.rs
index 509a1a0e43..1f26f2fa04 100644
--- a/talpid-core/src/tunnel_state_machine/mod.rs
+++ b/talpid-core/src/tunnel_state_machine/mod.rs
@@ -76,7 +76,7 @@ pub enum Error {
pub async fn spawn(
allow_lan: bool,
block_when_disconnected: bool,
- custom_dns: Option<Vec<IpAddr>>,
+ dns_servers: Option<Vec<IpAddr>>,
allowed_endpoint: Endpoint,
tunnel_parameters_generator: impl TunnelParametersGenerator,
log_dir: Option<PathBuf>,
@@ -106,7 +106,7 @@ pub async fn spawn(
#[cfg(target_os = "android")]
allowed_endpoint.address.ip(),
#[cfg(target_os = "android")]
- custom_dns.clone(),
+ dns_servers.clone(),
);
let runtime = tokio::runtime::Handle::current();
@@ -118,7 +118,7 @@ pub async fn spawn(
allow_lan,
block_when_disconnected,
is_offline,
- custom_dns,
+ dns_servers,
allowed_endpoint,
tunnel_parameters_generator,
tun_provider,
@@ -161,8 +161,8 @@ pub enum TunnelCommand {
/// 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>>),
+ /// Set DNS servers to use.
+ Dns(Option<Vec<IpAddr>>),
/// Enable or disable the block_when_disconnected feature.
BlockWhenDisconnected(bool),
/// Notify the state machine of the connectivity of the device.
@@ -204,7 +204,7 @@ impl TunnelStateMachine {
allow_lan: bool,
block_when_disconnected: bool,
is_offline: bool,
- custom_dns: Option<Vec<IpAddr>>,
+ dns_servers: Option<Vec<IpAddr>>,
allowed_endpoint: Endpoint,
tunnel_parameters_generator: impl TunnelParametersGenerator,
tun_provider: TunProvider,
@@ -232,7 +232,7 @@ impl TunnelStateMachine {
allow_lan,
block_when_disconnected,
is_offline,
- custom_dns,
+ dns_servers,
allowed_endpoint,
tunnel_parameters_generator: Box::new(tunnel_parameters_generator),
tun_provider,
@@ -304,8 +304,8 @@ struct SharedTunnelStateValues {
block_when_disconnected: bool,
/// True when the computer is known to be offline.
is_offline: bool,
- /// Custom DNS servers to use.
- custom_dns: Option<Vec<IpAddr>>,
+ /// DNS servers to use (overriding default).
+ dns_servers: Option<Vec<IpAddr>>,
/// Endpoint that should not be blocked by the firewall.
allowed_endpoint: Endpoint,
/// The generator of new `TunnelParameter`s
@@ -359,20 +359,20 @@ impl SharedTunnelStateValues {
}
}
- pub fn set_custom_dns(
+ pub fn set_dns_servers(
&mut self,
- custom_dns: Option<Vec<IpAddr>>,
+ dns_servers: Option<Vec<IpAddr>>,
) -> Result<bool, ErrorStateCause> {
- if self.custom_dns != custom_dns {
- self.custom_dns = custom_dns.clone();
+ if self.dns_servers != dns_servers {
+ self.dns_servers = dns_servers.clone();
#[cfg(target_os = "android")]
{
- if let Err(error) = self.tun_provider.set_custom_dns_servers(custom_dns) {
+ if let Err(error) = self.tun_provider.set_dns_servers(dns_servers) {
log::error!(
"{}",
error.display_chain_with_msg(
- "Failed to restart tunnel after changing custom DNS servers",
+ "Failed to restart tunnel after changing DNS servers",
)
);
return Err(ErrorStateCause::StartTunnelError);