summaryrefslogtreecommitdiffhomepage
path: root/talpid-core/src
diff options
context:
space:
mode:
Diffstat (limited to 'talpid-core/src')
-rw-r--r--talpid-core/src/dns/linux/mod.rs20
-rw-r--r--talpid-core/src/dns/linux/static_resolv_conf.rs93
2 files changed, 69 insertions, 44 deletions
diff --git a/talpid-core/src/dns/linux/mod.rs b/talpid-core/src/dns/linux/mod.rs
index 5a4bfdb5da..3eb11df51a 100644
--- a/talpid-core/src/dns/linux/mod.rs
+++ b/talpid-core/src/dns/linux/mod.rs
@@ -10,8 +10,6 @@ use self::{
use crate::routing::RouteManagerHandle;
use std::{env, fmt, net::IpAddr};
-const RESOLV_CONF_PATH: &str = "/etc/resolv.conf";
-
pub type Result<T> = std::result::Result<T, Error>;
/// Errors that can happen in the Linux DNS monitor
@@ -58,7 +56,7 @@ impl super::DnsMonitorT for DnsMonitor {
fn set(&mut self, interface: &str, servers: &[IpAddr]) -> Result<()> {
self.reset()?;
// Creating a new DNS monitor for each set, in case the system changed how it manages DNS.
- let mut inner = DnsMonitorHolder::new()?;
+ let mut inner = DnsMonitorHolder::new(&self.handle)?;
if !servers.is_empty() {
inner.set(&self.handle, &self.route_manager, interface, servers)?;
self.inner = Some(inner);
@@ -95,21 +93,23 @@ impl fmt::Display for DnsMonitorHolder {
}
impl DnsMonitorHolder {
- fn new() -> Result<Self> {
+ fn new(handle: &tokio::runtime::Handle) -> Result<Self> {
let dns_module = env::var_os("TALPID_DNS_MODULE");
let manager = match dns_module.as_ref().and_then(|value| value.to_str()) {
- Some("static-file") => DnsMonitorHolder::StaticResolvConf(StaticResolvConf::new()?),
+ Some("static-file") => {
+ DnsMonitorHolder::StaticResolvConf(handle.block_on(StaticResolvConf::new())?)
+ }
Some("resolvconf") => DnsMonitorHolder::Resolvconf(Resolvconf::new()?),
Some("systemd") => DnsMonitorHolder::SystemdResolved(SystemdResolved::new()?),
Some("network-manager") => DnsMonitorHolder::NetworkManager(NetworkManager::new()?),
- Some(_) | None => Self::with_detected_dns_manager()?,
+ Some(_) | None => Self::with_detected_dns_manager(handle)?,
};
log::debug!("Managing DNS via {}", manager);
Ok(manager)
}
- fn with_detected_dns_manager() -> Result<Self> {
+ fn with_detected_dns_manager(handle: &tokio::runtime::Handle) -> Result<Self> {
SystemdResolved::new()
.map(DnsMonitorHolder::SystemdResolved)
.or_else(|err| {
@@ -124,7 +124,11 @@ impl DnsMonitorHolder {
NetworkManager::new().map(DnsMonitorHolder::NetworkManager)
})
.or_else(|_| Resolvconf::new().map(DnsMonitorHolder::Resolvconf))
- .or_else(|_| StaticResolvConf::new().map(DnsMonitorHolder::StaticResolvConf))
+ .or_else(|_| {
+ handle
+ .block_on(StaticResolvConf::new())
+ .map(DnsMonitorHolder::StaticResolvConf)
+ })
.map_err(|_| Error::NoDnsMonitor)
}
diff --git a/talpid-core/src/dns/linux/static_resolv_conf.rs b/talpid-core/src/dns/linux/static_resolv_conf.rs
index 691d7b468b..613a995db2 100644
--- a/talpid-core/src/dns/linux/static_resolv_conf.rs
+++ b/talpid-core/src/dns/linux/static_resolv_conf.rs
@@ -1,25 +1,20 @@
-use super::RESOLV_CONF_PATH;
-use notify::{RecommendedWatcher, RecursiveMode, Watcher};
+use futures::StreamExt;
+use inotify::{Inotify, WatchMask};
use parking_lot::Mutex;
use resolv_conf::{Config, ScopedIp};
-use std::{
- fs, io,
- net::IpAddr,
- path::Path,
- sync::{mpsc, Arc},
- thread,
-};
+use std::{fs, io, net::IpAddr, sync::Arc};
use talpid_types::ErrorExt;
+use triggered::{trigger, Listener, Trigger};
const RESOLV_CONF_BACKUP_PATH: &str = "/etc/resolv.conf.mullvadbackup";
-const RESOLV_CONF_DIR: &str = "/etc/";
+const RESOLV_CONF_PATH: &str = "/etc/resolv.conf";
pub type Result<T> = std::result::Result<T, Error>;
#[derive(err_derive::Error, Debug)]
pub enum Error {
#[error(display = "Failed to watch /etc/resolv.conf for changes")]
- WatchResolvConf(#[error(source)] notify::Error),
+ WatchResolvConf(#[error(source)] std::io::Error),
#[error(display = "Failed to write to {}", _0)]
WriteResolvConf(&'static str, #[error(source)] io::Error),
@@ -40,11 +35,11 @@ pub struct StaticResolvConf {
}
impl StaticResolvConf {
- pub fn new() -> Result<Self> {
+ pub async fn new() -> Result<Self> {
restore_from_backup()?;
let state = Arc::new(Mutex::new(None));
- let watcher = DnsWatcher::start(state.clone())?;
+ let watcher = DnsWatcher::start(state.clone()).await?;
Ok(StaticResolvConf {
state,
@@ -107,39 +102,65 @@ impl State {
}
struct DnsWatcher {
- _watcher: RecommendedWatcher,
+ cancel_trigger: Trigger,
+}
+
+impl Drop for DnsWatcher {
+ fn drop(&mut self) {
+ self.cancel_trigger.trigger();
+ }
}
impl DnsWatcher {
- fn start(state: Arc<Mutex<Option<State>>>) -> Result<Self> {
- let (event_tx, event_rx) = mpsc::channel();
- let mut watcher = notify::raw_watcher(event_tx).map_err(Error::WatchResolvConf)?;
+ async fn start(state: Arc<Mutex<Option<State>>>) -> Result<Self> {
+ let mut watcher = Inotify::init().map_err(Error::WatchResolvConf)?;
+ let mut mask = WatchMask::empty();
+ // Documentation for the meaning of these masks can be found in `man inotify`
+ //
+ // We do not watch for writes but instead for when a file opened for writing is closed.
+ // This way we don't have collisions.
+ mask.insert(WatchMask::CLOSE_WRITE);
+ // DELETE_SELF is generated if the file watched is itself deleted
+ mask.insert(WatchMask::DELETE_SELF);
+ mask.insert(WatchMask::MOVE_SELF);
watcher
- .watch(&RESOLV_CONF_DIR, RecursiveMode::NonRecursive)
+ .add_watch(&RESOLV_CONF_PATH, mask)
.map_err(Error::WatchResolvConf)?;
- thread::spawn(move || Self::event_loop(event_rx, &state));
+ let (cancel_trigger, cancel_listener) = trigger();
- Ok(DnsWatcher { _watcher: watcher })
+ tokio::spawn(async move { Self::event_loop(watcher, cancel_listener, &state).await });
+
+ Ok(DnsWatcher { cancel_trigger })
}
- fn event_loop(events: mpsc::Receiver<notify::RawEvent>, state: &Arc<Mutex<Option<State>>>) {
- for event in events {
- if event
- .path
- .as_ref()
- .map(|p| p.as_path() == AsRef::<Path>::as_ref(RESOLV_CONF_PATH))
- .unwrap_or(false)
- {
- let mut locked_state = state.lock();
- if let Err(error) = Self::update(locked_state.as_mut()) {
- log::error!(
- "{}",
- error.display_chain_with_msg(
- "Failed to update DNS state after DNS settings changed"
- )
- );
+ async fn event_loop(
+ mut watcher: Inotify,
+ mut cancel_listener: Listener,
+ state: &Arc<Mutex<Option<State>>>,
+ ) {
+ const EVENT_BUFFER_SIZE: usize = 1024;
+ let mut buffer = [0; EVENT_BUFFER_SIZE];
+ let mut events = watcher
+ .event_stream(&mut buffer)
+ .expect("Could not read events for resolv.conf");
+
+ loop {
+ tokio::select! {
+ _ = &mut cancel_listener => {
+ break;
+ },
+ Some(_) = events.next() => {
+ let mut locked_state = state.lock();
+ if let Err(error) = Self::update(locked_state.as_mut()) {
+ log::error!(
+ "{}",
+ error.display_chain_with_msg(
+ "Failed to update DNS state after DNS settings changed"
+ )
+ );
+ }
}
}
}