diff options
| author | Emīls Piņķis <emils@mullvad.net> | 2021-11-02 15:09:18 +0000 |
|---|---|---|
| committer | Emīls <emils@mullvad.net> | 2021-11-03 09:54:13 +0000 |
| commit | 9e1eebb864f8e32620b1982a5604656e1bdd5658 (patch) | |
| tree | d3fe0d331074abe55703d8939582b9dd1391ae8d /talpid-core/src | |
| parent | 25abff290d1debeed06fe7098f4abd2be8eeb7fd (diff) | |
| download | mullvadvpn-9e1eebb864f8e32620b1982a5604656e1bdd5658.tar.xz mullvadvpn-9e1eebb864f8e32620b1982a5604656e1bdd5658.zip | |
Ignore local resolvers for systemd-resolved
Diffstat (limited to 'talpid-core/src')
| -rw-r--r-- | talpid-core/src/dns/linux/mod.rs | 7 | ||||
| -rw-r--r-- | talpid-core/src/dns/linux/routing.rs | 181 | ||||
| -rw-r--r-- | talpid-core/src/dns/linux/systemd_resolved.rs | 284 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/connected_state.rs | 11 |
4 files changed, 37 insertions, 446 deletions
diff --git a/talpid-core/src/dns/linux/mod.rs b/talpid-core/src/dns/linux/mod.rs index 4b046035aa..e56ca1c208 100644 --- a/talpid-core/src/dns/linux/mod.rs +++ b/talpid-core/src/dns/linux/mod.rs @@ -1,6 +1,5 @@ mod network_manager; mod resolvconf; -mod routing; mod static_resolv_conf; pub(self) mod systemd_resolved; @@ -65,8 +64,10 @@ impl super::DnsMonitorT for DnsMonitor { self.reset()?; // Creating a new DNS monitor for each set, in case the system changed how it manages DNS. let mut inner = DnsMonitorHolder::new()?; - inner.set(&self.handle, &self.route_manager, interface, servers)?; - self.inner = Some(inner); + if !servers.is_empty() { + inner.set(&self.handle, &self.route_manager, interface, servers)?; + self.inner = Some(inner); + } Ok(()) } diff --git a/talpid-core/src/dns/linux/routing.rs b/talpid-core/src/dns/linux/routing.rs deleted file mode 100644 index e04b4cc29c..0000000000 --- a/talpid-core/src/dns/linux/routing.rs +++ /dev/null @@ -1,181 +0,0 @@ -use crate::{ - linux::{iface_index, IfaceIndexLookupError}, - routing::{self, RouteManagerHandle}, -}; -use futures::{ - channel::mpsc::UnboundedSender, - stream::{abortable, AbortHandle}, - StreamExt, -}; -use rtnetlink::IpVersion; -use std::{ - collections::BTreeMap, - fmt, - net::{IpAddr, Ipv4Addr, Ipv6Addr}, -}; -use talpid_types::ErrorExt; - -pub type Result<T> = std::result::Result<T, Error>; - -const PUBLIC_INTERNET_ADDRESS_V4: IpAddr = IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)); -const PUBLIC_INTERNET_ADDRESS_V6: IpAddr = - IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)); - -#[derive(err_derive::Error, Debug)] -#[error(no_from)] -pub enum Error { - #[error(display = "The route manager returned an error")] - RouteManagerError(#[error(source)] routing::Error), - - #[error(display = "Failed to resolve interface index with error {}", _0)] - InterfaceNameError(#[error(source)] IfaceIndexLookupError), -} - -pub struct DnsRouteMonitor { - abort_handle: AbortHandle, -} - -impl Drop for DnsRouteMonitor { - fn drop(&mut self) { - self.abort_handle.abort(); - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct DnsConfig { - pub interface: u32, - pub resolvers: Vec<IpAddr>, -} - -impl fmt::Display for DnsConfig { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "interface index {}, resolvers:", self.interface)?; - for server in &self.resolvers { - write!(f, " {}", server)?; - } - Ok(()) - } -} - -pub async fn spawn_monitor( - route_manager: RouteManagerHandle, - destinations: Vec<IpAddr>, - update_tx: UnboundedSender<BTreeMap<u32, DnsConfig>>, -) -> Result<(DnsRouteMonitor, BTreeMap<u32, DnsConfig>)> { - let listener = route_manager - .change_listener() - .await - .map_err(Error::RouteManagerError)?; - let (mut listener, abort_handle) = abortable(listener); - - let monitor = DnsRouteMonitor { abort_handle }; - - let mut last_config = setup_configurations(&route_manager, &destinations).await?; - let initial_config = last_config.clone(); - - tokio::spawn(async move { - while let Some(_event) = listener.next().await { - match setup_configurations(&route_manager, &destinations).await { - Ok(new_config) => { - if last_config != new_config { - last_config = new_config.clone(); - if update_tx.unbounded_send(new_config).is_err() { - log::trace!("Stopping DNS monitor: channel is closed"); - break; - } - } - } - Err(error) => { - log::error!( - "{}", - error.display_chain_with_msg( - "Failed to determine new DNS interface settings" - ) - ); - } - } - } - }); - - Ok((monitor, initial_config)) -} - -async fn setup_configurations( - handle: &RouteManagerHandle, - destinations: &[IpAddr], -) -> Result<BTreeMap<u32, DnsConfig>> { - let mut interface_to_destinations = BTreeMap::<u32, DnsConfig>::new(); - for destination in destinations { - let interface = if destination.is_loopback() { - get_default_route_interface(handle, get_ip_version(destination), true).await? - } else { - if crate::firewall::is_local_address(destination) { - get_destination_interface(handle, *destination, true).await? - } else { - get_default_route_interface(handle, get_ip_version(destination), false).await? - } - }; - match interface { - Some(iface) => { - if let Some(config) = interface_to_destinations.get_mut(&iface) { - config.resolvers.push(*destination); - } else { - interface_to_destinations.insert( - iface, - DnsConfig { - interface: iface, - resolvers: vec![*destination], - }, - ); - } - } - None => { - log::trace!( - "Ignoring DNS server that did not match to any interface: {}", - destination - ); - } - } - } - - Ok(interface_to_destinations) -} - -async fn get_default_route_interface( - handle: &RouteManagerHandle, - ip_version: IpVersion, - set_mark: bool, -) -> Result<Option<u32>> { - match ip_version { - IpVersion::V4 => { - get_destination_interface(handle, PUBLIC_INTERNET_ADDRESS_V4, set_mark).await - } - IpVersion::V6 => { - get_destination_interface(handle, PUBLIC_INTERNET_ADDRESS_V6, set_mark).await - } - } -} - -async fn get_destination_interface( - handle: &RouteManagerHandle, - destination: IpAddr, - set_mark: bool, -) -> Result<Option<u32>> { - let route = handle - .get_destination_route(destination, set_mark) - .await - .map_err(Error::RouteManagerError)?; - route - .map(|route| route.get_node().get_device().map(iface_index)) - .flatten() - .transpose() - .map_err(Error::InterfaceNameError) -} - -fn get_ip_version(addr: &IpAddr) -> IpVersion { - if addr.is_ipv4() { - IpVersion::V4 - } else { - IpVersion::V6 - } -} diff --git a/talpid-core/src/dns/linux/systemd_resolved.rs b/talpid-core/src/dns/linux/systemd_resolved.rs index c036d82dae..9a9ae7e7f6 100644 --- a/talpid-core/src/dns/linux/systemd_resolved.rs +++ b/talpid-core/src/dns/linux/systemd_resolved.rs @@ -2,17 +2,8 @@ use crate::{ linux::{iface_index, IfaceIndexLookupError}, routing::RouteManagerHandle, }; -use futures::{channel::mpsc, StreamExt}; -use std::{ - collections::BTreeMap, - net::IpAddr, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, Mutex, - }, - thread, -}; -use talpid_dbus::systemd_resolved::{AsyncHandle, DnsState, SystemdResolved as DbusInterface}; +use std::net::IpAddr; +use talpid_dbus::systemd_resolved::{AsyncHandle, SystemdResolved as DbusInterface}; use talpid_types::ErrorExt; pub(crate) use talpid_dbus::systemd_resolved::Error as SystemdDbusError; @@ -26,20 +17,11 @@ pub enum Error { #[error(display = "Failed to resolve interface index with error {}", _0)] InterfaceNameError(#[error(source)] IfaceIndexLookupError), - - #[error(display = "Failed to spawn DNS interface monitor")] - SpawnInterfaceMonitor(#[error(source)] super::routing::Error), } -use super::routing::{DnsConfig, DnsRouteMonitor}; - pub struct SystemdResolved { pub dbus_interface: AsyncHandle, - current_config: Arc<Mutex<BTreeMap<u32, DnsConfig>>>, - initial_states: Arc<Mutex<BTreeMap<u32, DnsState>>>, tunnel_index: u32, - route_monitor: Option<(DnsRouteMonitor, tokio::task::JoinHandle<()>)>, - watcher: Option<(thread::JoinHandle<()>, Arc<AtomicBool>)>, } @@ -49,11 +31,7 @@ impl SystemdResolved { let systemd_resolved = SystemdResolved { dbus_interface, - current_config: Arc::new(Mutex::new(BTreeMap::new())), - initial_states: Arc::new(Mutex::new(BTreeMap::new())), tunnel_index: 0, - route_monitor: None, - watcher: None, }; Ok(systemd_resolved) @@ -61,266 +39,48 @@ impl SystemdResolved { pub async fn set_dns( &mut self, - route_manager: RouteManagerHandle, + _route_manager: RouteManagerHandle, interface_name: &str, servers: &[IpAddr], ) -> Result<()> { - let (update_tx, mut update_rx) = mpsc::unbounded(); - let (monitor, initial_config) = - super::routing::spawn_monitor(route_manager, servers.to_vec(), update_tx) - .await - .map_err(Error::SpawnInterfaceMonitor)?; - let tunnel_index = iface_index(interface_name)?; self.tunnel_index = tunnel_index; - let mut last_result = Ok(()); if let Err(error) = self.dbus_interface.disable_dot(self.tunnel_index).await { log::error!("Failed to disable DoT: {}", error.display_chain()); } - + if let Err(error) = self + .dbus_interface + .set_domains(tunnel_index, &[(".", true)]) + .await { - for (iface_index, iface_config) in &initial_config { - let initial_state = match self.dbus_interface.get_dns(*iface_index).await { - Ok(state) => state, - Err(error) => { - last_result = Err(Error::SystemdResolvedError(error)); - break; - } - }; - if let Err(error) = self - .dbus_interface - .set_dns(*iface_index, iface_config.resolvers.clone()) - .await - { - last_result = Err(Error::SystemdResolvedError(error)); - break; - } - { - self.initial_states - .lock() - .unwrap() - .insert(*iface_index, initial_state); - } - } + log::error!("Failed to set search domains: {}", error.display_chain()); } - if last_result.is_ok() { - if has_only_tunnel_config(&initial_config, tunnel_index) { - if let Err(error) = self - .dbus_interface - .set_domains(tunnel_index, &[(".", true)]) - .await - { - last_result = Err(Error::SystemdResolvedError(error)); - } - } else { - if let Err(error) = self.dbus_interface.set_domains(tunnel_index, &[]).await { - last_result = Err(Error::SystemdResolvedError(error)); - } - } - } - - - if let Err(error) = last_result { - let _ = self.reset(); - return Err(error); - } - - { - *self.current_config.lock().unwrap() = initial_config; - } - - let ignore_config_changes = Arc::new(AtomicBool::new(false)); - - self.watcher = Some(self.spawn_watcher_thread( - tunnel_index, - self.current_config.clone(), - ignore_config_changes.clone(), - )); - - let dbus_interface = DbusInterface::new_connection()?.async_handle(); - let initial_states = self.initial_states.clone(); - let current_config = self.current_config.clone(); - let join_handle = tokio::spawn(async move { - while let Some(new_config) = update_rx.next().await { - let mut new_initial_states = { initial_states.lock().unwrap().clone() }; - - let disable_watcher = ignore_config_changes.clone(); - disable_watcher.store(true, Ordering::Release); - - // Revert interfaces no longer in use - let keys = new_initial_states.keys().cloned().collect::<Vec<u32>>(); - for iface in keys { - if !new_config.contains_key(&iface) { - log::debug!("Reverting DNS config on interface {}", iface); - if let Err(err) = dbus_interface - .set_dns_state(new_initial_states[&iface].clone()) - .await - { - log::error!("Failed to revert interface config: {}", err); - } - new_initial_states.remove(&iface); - } - } - - for (iface, config) in &new_config { - if tunnel_index == *iface { - // All public addresses (plus the gateway) will be assigned - // to the tunnel: we can assume nothing has changed. - continue; - } - - // Store new interfaces - if !new_initial_states.contains_key(iface) { - let initial_state = match dbus_interface.get_dns(*iface).await { - Ok(state) => state, - Err(error) => { - log::error!( - "Failed to get resolvers: {}\n{}", - config, - error.display_chain() - ); - continue; - } - }; - new_initial_states.insert(*iface, initial_state); - } - - if let Err(error) = dbus_interface - .set_dns(*iface, config.resolvers.clone()) - .await - { - log::error!( - "Failed to set resolvers: {}\n{}", - config, - error.display_chain() - ); - } - } - - let tunnel_domains = if has_only_tunnel_config(&new_config, tunnel_index) { - &[(".", true)][..] - } else { - &[][..] - }; - if let Err(error) = dbus_interface - .set_domains(tunnel_index, tunnel_domains) - .await - { - log::error!( - "Failed to set DNS domains on tunnel interface\n{}", - error.display_chain() - ); - } - - { - *current_config.lock().unwrap() = new_config.clone(); - *initial_states.lock().unwrap() = new_initial_states; - } - - disable_watcher.store(false, Ordering::Release); - } - }); - self.route_monitor = Some((monitor, join_handle)); + let _ = self + .dbus_interface + .set_dns(self.tunnel_index, servers.to_vec()) + .await?; Ok(()) } - fn spawn_watcher_thread( - &mut self, - tunnel_index: u32, - current_config: Arc<Mutex<BTreeMap<u32, DnsConfig>>>, - disable_watcher: Arc<AtomicBool>, - ) -> (thread::JoinHandle<()>, Arc<AtomicBool>) { - let dbus_interface = self.dbus_interface.handle().clone(); - let should_shutdown = Arc::new(AtomicBool::new(false)); - let watch_shutdown = should_shutdown.clone(); - let callback_shutdown = should_shutdown.clone(); - let watcher_thread = std::thread::spawn(move || { - let result = dbus_interface.clone().watch_dns_changes( - move |new_servers| { - if callback_shutdown.clone().load(Ordering::Acquire) { - return; - } - if disable_watcher.clone().load(Ordering::Acquire) { - return; - } - let configs = current_config.lock().unwrap(); - let mut anything_changed = false; - for (iface, config) in &*configs { - let current_servers: Vec<IpAddr> = new_servers - .iter() - .filter(|server| server.iface_index == *iface as i32) - .map(|server| server.address) - .collect(); - if current_servers != config.resolvers { - log::trace!("DNS config for interface {} changed, currently applied servers - {:?}", iface, current_servers); - if let Err(err) = dbus_interface.set_dns(*iface, config.resolvers.clone()) - { - log::error!("Failed to re-apply DNS config - {}", err); - } - anything_changed = true; - } - } - if anything_changed { - let result = if has_only_tunnel_config(&configs, tunnel_index) { - dbus_interface.set_domains(tunnel_index, &[(".", true)]) - } else { - dbus_interface.set_domains(tunnel_index, &[]) - }; - if let Err(err) = result { - log::error!("Failed to re-apply DNS domains - {}", err); - } - } - }, - move || !watch_shutdown.load(Ordering::Acquire), - ); - if let Err(err) = result { - log::error!("Failed to watch DNS config updates: {}", err); - } - }); - (watcher_thread, should_shutdown) - } pub async fn reset(&mut self) -> Result<()> { - if let Some((watcher_thread, watcher_should_shutdown)) = self.watcher.take() { - watcher_should_shutdown.store(true, Ordering::Release); - if watcher_thread.join().is_err() { - log::error!("DNS watcher thread panicked!"); - } - } - - if let Some((monitor, join_handle)) = self.route_monitor.take() { - std::mem::drop(monitor); - let _ = join_handle.await; - } - - let initial_states = { - let mut initial_states = self.initial_states.lock().unwrap(); - std::mem::take(&mut *initial_states) - }; - for (iface, state) in &initial_states { - let result = if *iface == self.tunnel_index { - self.dbus_interface.revert_link(state.clone()).await - } else { - self.dbus_interface.set_dns_state(state.clone()).await - }; - if let Err(err) = result { - log::error!( - "{}", - err.display_chain_with_msg("Failed to revert interface config") - ); - } + if let Err(error) = self + .dbus_interface + .set_domains(self.tunnel_index, &[]) + .await + { + log::error!("Failed to set search domains: {}", error.display_chain()); } - self.current_config.lock().unwrap().clear(); + let _ = self + .dbus_interface + .set_dns(self.tunnel_index, vec![]) + .await?; Ok(()) } } - -fn has_only_tunnel_config(configs: &BTreeMap<u32, DnsConfig>, tunnel_index: u32) -> bool { - configs.len() == 1 && configs.contains_key(&tunnel_index) -} diff --git a/talpid-core/src/tunnel_state_machine/connected_state.rs b/talpid-core/src/tunnel_state_machine/connected_state.rs index 400df2695d..89a2960598 100644 --- a/talpid-core/src/tunnel_state_machine/connected_state.rs +++ b/talpid-core/src/tunnel_state_machine/connected_state.rs @@ -123,6 +123,17 @@ impl ConnectedState { fn set_dns(&self, shared_values: &mut SharedTunnelStateValues) -> Result<(), BoxedError> { let dns_ips = self.get_dns_servers(shared_values); + + #[cfg(target_os = "linux")] + let dns_ips = &dns_ips + .into_iter() + .filter(|ip| { + !crate::firewall::is_local_address(ip) + || IpAddr::V4(self.metadata.ipv4_gateway) == *ip + || self.metadata.ipv6_gateway.map(IpAddr::V6) == Some(*ip) + }) + .collect::<Vec<_>>(); + shared_values .dns_monitor .set(&self.metadata.interface, &dns_ips) |
