summaryrefslogtreecommitdiffhomepage
path: root/talpid-core/src
diff options
context:
space:
mode:
authorEmīls Piņķis <emils@mullvad.net>2021-11-02 15:09:18 +0000
committerEmīls <emils@mullvad.net>2021-11-03 09:54:13 +0000
commit9e1eebb864f8e32620b1982a5604656e1bdd5658 (patch)
treed3fe0d331074abe55703d8939582b9dd1391ae8d /talpid-core/src
parent25abff290d1debeed06fe7098f4abd2be8eeb7fd (diff)
downloadmullvadvpn-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.rs7
-rw-r--r--talpid-core/src/dns/linux/routing.rs181
-rw-r--r--talpid-core/src/dns/linux/systemd_resolved.rs284
-rw-r--r--talpid-core/src/tunnel_state_machine/connected_state.rs11
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)