diff options
| -rw-r--r-- | mullvad-cli/src/cmds/custom_dns.rs | 132 | ||||
| -rw-r--r-- | mullvad-daemon/src/lib.rs | 31 | ||||
| -rw-r--r-- | mullvad-daemon/src/management_interface.rs | 52 | ||||
| -rw-r--r-- | mullvad-daemon/src/settings.rs | 12 | ||||
| -rw-r--r-- | mullvad-management-interface/proto/management_interface.proto | 9 | ||||
| -rw-r--r-- | mullvad-types/src/settings/mod.rs | 20 | ||||
| -rw-r--r-- | talpid-types/src/net/mod.rs | 3 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/winfw.cpp | 2 |
8 files changed, 182 insertions, 79 deletions
diff --git a/mullvad-cli/src/cmds/custom_dns.rs b/mullvad-cli/src/cmds/custom_dns.rs index daba881f36..c573abeca3 100644 --- a/mullvad-cli/src/cmds/custom_dns.rs +++ b/mullvad-cli/src/cmds/custom_dns.rs @@ -1,4 +1,5 @@ use crate::{new_rpc_client, Command, Result}; +use clap::value_t_or_exit; use mullvad_management_interface::types; pub struct CustomDns; @@ -14,38 +15,88 @@ impl Command for CustomDns { .about("Configure custom DNS servers to use when connected") .setting(clap::AppSettings::SubcommandRequiredElseHelp) .subcommand( - clap::SubCommand::with_name("set") - .about("Change custom DNS setting") - .arg( - clap::Arg::with_name("servers") - .multiple(true) - .help("One or more IP addresses pointing to DNS resolvers.") - .required(true), + 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("reset").about("Remove all custom DNS servers")) .subcommand( - clap::SubCommand::with_name("get").about("Display the current custom DNS setting"), + 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("enabled") + .required(true) + .possible_values(&["on", "off"]), + ), ) } async fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()> { - if let Some(set_matches) = matches.subcommand_matches("set") { - self.set(set_matches.values_of_lossy("servers")).await - } else if let Some(_matches) = matches.subcommand_matches("reset") { - self.reset().await - } else if let Some(_matches) = matches.subcommand_matches("get") { - self.get().await - } else { - unreachable!("No custom-dns command given"); + 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 enabled = value_t_or_exit!(matches.value_of("enabled"), String); + self.set_state(enabled == "on").await + } + ("get", _) => self.get().await, + _ => unreachable!("No custom-dns command given"), } } } impl CustomDns { - async fn set(&self, servers: Option<Vec<String>>) -> Result<()> { + async fn set_state(&self, enabled: bool) -> Result<()> { let mut rpc = new_rpc_client().await?; - rpc.set_custom_dns(types::CustomDns { + 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?; @@ -53,34 +104,53 @@ impl CustomDns { Ok(()) } - async fn reset(&self) -> Result<()> { + async fn clear_servers(&self) -> Result<()> { let mut rpc = new_rpc_client().await?; - rpc.set_custom_dns(types::CustomDns { addresses: vec![] }) - .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 custom_dns = rpc + let options = rpc .get_settings(()) .await? .into_inner() .tunnel_options .unwrap() - .generic - .unwrap() - .custom_dns; - match custom_dns { - None => println!("No DNS servers are configured"), - Some(types::CustomDns { addresses }) => { - println!("Custom DNS servers:"); - for server in &addresses { + .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-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index 7f5b85f7e0..320e839a0a 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -26,6 +26,8 @@ use futures::{ }; use log::{debug, error, info, warn}; use mullvad_rpc::AccountsProxy; +#[cfg(windows)] +use mullvad_types::settings::DnsOptions; use mullvad_types::{ account::{AccountData, AccountToken, VoucherSubmission}, endpoint::MullvadEndpoint, @@ -196,7 +198,7 @@ pub enum DaemonCommand { SetEnableIpv6(oneshot::Sender<()>, bool), /// Set custom DNS servers to use instead of passing requests to the gateway #[cfg(windows)] - SetCustomDns(oneshot::Sender<()>, Option<Vec<IpAddr>>), + SetDnsOptions(oneshot::Sender<()>, DnsOptions), /// Set MTU for wireguard tunnels SetWireguardMtu(oneshot::Sender<()>, Option<u16>), /// Set automatic key rotation interval for wireguard tunnels @@ -581,7 +583,7 @@ where settings.allow_lan, settings.block_when_disconnected, #[cfg(windows)] - settings.tunnel_options.generic.custom_dns.clone(), + Self::get_custom_resolvers(&settings.tunnel_options.dns_options), tunnel_parameters_generator, log_dir, resource_dir, @@ -634,6 +636,15 @@ where Ok(daemon) } + #[cfg(windows)] + fn get_custom_resolvers(dns_options: &DnsOptions) -> Option<Vec<IpAddr>> { + if dns_options.custom { + Some(dns_options.addresses.clone()) + } else { + None + } + } + /// Consume the `Daemon` and run the main event loop. Blocks until an error happens or a /// shutdown event is received. pub async fn run(mut self) -> Result<(), Error> { @@ -1046,7 +1057,7 @@ 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(windows)] - SetCustomDns(tx, dns_servers) => self.on_set_custom_dns(tx, dns_servers), + 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) => { self.on_set_wireguard_rotation_interval(tx, interval).await @@ -1686,15 +1697,17 @@ where } #[cfg(windows)] - fn on_set_custom_dns(&mut self, tx: oneshot::Sender<()>, servers: Option<Vec<IpAddr>>) { - let save_result = self.settings.set_custom_dns(servers.clone()); + 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 { Ok(settings_changed) => { - Self::oneshot_send(tx, (), "set_custom_dns response"); + Self::oneshot_send(tx, (), "set_dns_options response"); if settings_changed { - self.event_listener - .notify_settings(self.settings.to_settings()); - self.send_tunnel_command(TunnelCommand::CustomDns(servers)); + let settings = self.settings.to_settings(); + let resolvers = + Self::get_custom_resolvers(&settings.tunnel_options.dns_options); + self.event_listener.notify_settings(settings); + self.send_tunnel_command(TunnelCommand::CustomDns(resolvers)); } } Err(e) => error!("{}", e.display_chain_with_msg("Unable to save settings")), diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs index a5cfebedc3..ca324308cb 100644 --- a/mullvad-daemon/src/management_interface.rs +++ b/mullvad-daemon/src/management_interface.rs @@ -6,6 +6,8 @@ use mullvad_management_interface::{ }; use mullvad_paths; use mullvad_rpc::{rest::Error as RestError, StatusCode}; +#[cfg(windows)] +use mullvad_types::settings::DnsOptions; use mullvad_types::{ account::AccountToken, location::GeoIpLocation, @@ -409,12 +411,16 @@ impl ManagementService for ManagementServiceImpl { } #[cfg(windows)] - async fn set_custom_dns(&self, request: Request<types::CustomDns>) -> ServiceResult<()> { - let servers = request.into_inner(); - log::debug!("set_custom_dns({:?})", servers.addresses); + 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 servers.addresses.into_iter() { + for server in options.addresses.into_iter() { if let Ok(addr) = server.parse() { servers_ip.push(addr); } else { @@ -423,20 +429,20 @@ impl ManagementService for ManagementServiceImpl { } } - let servers_ip = if !servers_ip.is_empty() { - Some(servers_ip) - } else { - None - }; - let (tx, rx) = oneshot::channel(); - self.send_command_to_daemon(DaemonCommand::SetCustomDns(tx, servers_ip))?; + self.send_command_to_daemon(DaemonCommand::SetDnsOptions( + tx, + DnsOptions { + custom: options.custom, + addresses: servers_ip, + }, + ))?; rx.await .map(Response::new) .map_err(|_| Status::internal("internal error")) } #[cfg(not(windows))] - async fn set_custom_dns(&self, _: Request<types::CustomDns>) -> ServiceResult<()> { + async fn set_dns_options(&self, _: Request<types::DnsOptions>) -> ServiceResult<()> { Ok(Response::new(())) } @@ -1172,17 +1178,19 @@ fn convert_tunnel_options(options: &TunnelOptions) -> types::TunnelOptions { }), generic: Some(types::tunnel_options::GenericOptions { enable_ipv6: options.generic.enable_ipv6, - #[cfg(windows)] - custom_dns: options - .generic - .custom_dns - .as_ref() - .map(|addresses| types::CustomDns { - addresses: addresses.iter().map(|addr| addr.to_string()).collect(), - }), - #[cfg(not(windows))] - custom_dns: None, }), + #[cfg(windows)] + dns_options: Some(types::DnsOptions { + custom: options.dns_options.custom, + addresses: options + .dns_options + .addresses + .iter() + .map(|addr| addr.to_string()) + .collect(), + }), + #[cfg(not(windows))] + dns_options: None, } } diff --git a/mullvad-daemon/src/settings.rs b/mullvad-daemon/src/settings.rs index 1f6f6a447c..bcc07f84ae 100644 --- a/mullvad-daemon/src/settings.rs +++ b/mullvad-daemon/src/settings.rs @@ -1,10 +1,10 @@ use log::{debug, error, info}; +#[cfg(windows)] +use mullvad_types::settings::DnsOptions; use mullvad_types::{ relay_constraints::{BridgeSettings, BridgeState, RelaySettingsUpdate}, settings::Settings, }; -#[cfg(windows)] -use std::net::IpAddr; use std::{ fs::{self, File}, io, @@ -213,11 +213,9 @@ impl SettingsPersister { } #[cfg(windows)] - pub fn set_custom_dns(&mut self, servers: Option<Vec<IpAddr>>) -> Result<bool, Error> { - let should_save = Self::update_field( - &mut self.settings.tunnel_options.generic.custom_dns, - servers, - ); + 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); self.update(should_save) } diff --git a/mullvad-management-interface/proto/management_interface.proto b/mullvad-management-interface/proto/management_interface.proto index b66bc7439f..6d40e50d78 100644 --- a/mullvad-management-interface/proto/management_interface.proto +++ b/mullvad-management-interface/proto/management_interface.proto @@ -39,7 +39,7 @@ service ManagementService { rpc SetOpenvpnMssfix(google.protobuf.UInt32Value) returns (google.protobuf.Empty) {} rpc SetWireguardMtu(google.protobuf.UInt32Value) returns (google.protobuf.Empty) {} rpc SetEnableIpv6(google.protobuf.BoolValue) returns (google.protobuf.Empty) {} - rpc SetCustomDns(CustomDns) returns (google.protobuf.Empty) {} + rpc SetDnsOptions(DnsOptions) returns (google.protobuf.Empty) {} // Account management rpc CreateNewAccount(google.protobuf.Empty) returns (google.protobuf.StringValue) {} @@ -360,16 +360,17 @@ message TunnelOptions { } message GenericOptions { bool enable_ipv6 = 1; - CustomDns custom_dns = 2; } OpenvpnOptions openvpn = 1; WireguardOptions wireguard = 2; GenericOptions generic = 3; + DnsOptions dns_options = 4; } -message CustomDns { - repeated string addresses = 1; +message DnsOptions { + bool custom = 1; + repeated string addresses = 2; } message PublicKey { diff --git a/mullvad-types/src/settings/mod.rs b/mullvad-types/src/settings/mod.rs index 2b8ce221b8..0c8810b1db 100644 --- a/mullvad-types/src/settings/mod.rs +++ b/mullvad-types/src/settings/mod.rs @@ -7,6 +7,8 @@ use jnix::IntoJava; use log::{debug, info}; use serde::{Deserialize, Serialize}; use serde_json; +#[cfg(windows)] +use std::net::IpAddr; use talpid_types::net::{openvpn, wireguard, GenericTunnelOptions}; mod migrations; @@ -164,6 +166,20 @@ 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. + #[cfg(windows)] + pub dns_options: DnsOptions, +} + +/// Custom DNS config +#[cfg(windows)] +#[serde(default)] +#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)] +pub struct DnsOptions { + /// Whether to use the addresses in `custom_dns`. + pub custom: bool, + /// Custom DNS servers to use. + pub addresses: Vec<IpAddr>, } impl Default for TunnelOptions { @@ -177,9 +193,9 @@ impl Default for TunnelOptions { generic: GenericTunnelOptions { // Enable IPv6 be default on Android enable_ipv6: cfg!(target_os = "android"), - #[cfg(windows)] - custom_dns: None, }, + #[cfg(windows)] + dns_options: DnsOptions::default(), } } } diff --git a/talpid-types/src/net/mod.rs b/talpid-types/src/net/mod.rs index 36528e9744..15bf33a5bb 100644 --- a/talpid-types/src/net/mod.rs +++ b/talpid-types/src/net/mod.rs @@ -203,9 +203,6 @@ pub struct GenericTunnelOptions { /// Enable configuration of IPv6 on the tunnel interface, allowing IPv6 communication to be /// forwarded through the tunnel. pub enable_ipv6: bool, - /// Custom DNS servers to use. - #[cfg(windows)] - pub custom_dns: Option<Vec<IpAddr>>, } /// Returns a vector of IP networks representing all of the internet, 0.0.0.0/0. diff --git a/windows/winfw/src/winfw/winfw.cpp b/windows/winfw/src/winfw/winfw.cpp index 080713b742..0ea46c2ce2 100644 --- a/windows/winfw/src/winfw/winfw.cpp +++ b/windows/winfw/src/winfw/winfw.cpp @@ -346,7 +346,7 @@ WinFw_ApplyPolicyConnected( THROW_ERROR("Invalid argument: v4Gateway"); } - if (nullptr == dnsServers || 0 == numDnsServers) + if (nullptr == dnsServers) { THROW_ERROR("Invalid argument: dnsServers"); } |
