summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2018-09-09 14:14:18 -0300
committerJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2018-09-17 10:55:33 -0300
commite7ecb193af50f78cb4f267dff90a75072333e11f (patch)
treef81fa56e6e77558810af360e53ad023f4d847c18
parent107de7db3914d935185e7e2cb94aadd80ea7fcc6 (diff)
downloadmullvadvpn-e7ecb193af50f78cb4f267dff90a75072333e11f.tar.xz
mullvadvpn-e7ecb193af50f78cb4f267dff90a75072333e11f.zip
Separate `DnsSettings` and `StaticResolvConf`
-rw-r--r--talpid-core/src/security/linux/dns/mod.rs203
-rw-r--r--talpid-core/src/security/linux/dns/static_resolv_conf.rs214
2 files changed, 229 insertions, 188 deletions
diff --git a/talpid-core/src/security/linux/dns/mod.rs b/talpid-core/src/security/linux/dns/mod.rs
index a85e45cbfb..5758104594 100644
--- a/talpid-core/src/security/linux/dns/mod.rs
+++ b/talpid-core/src/security/linux/dns/mod.rs
@@ -1,214 +1,41 @@
-extern crate notify;
-extern crate resolv_conf;
+mod static_resolv_conf;
use std::net::IpAddr;
-use std::ops::DerefMut;
-use std::sync::{mpsc, Arc, Mutex, MutexGuard};
-use std::{fs, io, thread};
-use error_chain::ChainedError;
-
-use self::notify::{RecommendedWatcher, RecursiveMode, Watcher};
-use self::resolv_conf::{Config, ScopedIp};
-
-const RESOLV_CONF_PATH: &str = "/etc/resolv.conf";
-const RESOLV_CONF_BACKUP_PATH: &str = "/etc/resolv.conf.mullvadbackup";
+use self::static_resolv_conf::StaticResolvConf;
error_chain! {
- errors {
- WatchResolvConf {
- description("Failed to watch /etc/resolv.conf for changes")
- }
-
- WriteResolvConf {
- description("Failed to write to /etc/resolv.conf")
- }
-
- BackupResolvConf {
- description("Failed to create backup of /etc/resolv.conf")
- }
-
- RestoreResolvConf {
- description("Failed to restore /etc/resolv.conf from backup")
- }
+ links {
+ StaticResolvConf(static_resolv_conf::Error, static_resolv_conf::ErrorKind);
}
}
-pub struct DnsSettings {
- state: Arc<Mutex<Option<State>>>,
- _watcher: DnsWatcher,
+pub enum DnsSettings {
+ StaticResolvConf(StaticResolvConf),
}
impl DnsSettings {
pub fn new() -> Result<Self> {
- restore_from_backup().chain_err(|| ErrorKind::RestoreResolvConf)?;
-
- let state = Arc::new(Mutex::new(None));
- let watcher = DnsWatcher::start(state.clone())?;
-
- Ok(DnsSettings {
- state,
- _watcher: watcher,
- })
+ Ok(DnsSettings::StaticResolvConf(StaticResolvConf::new()?))
}
pub fn set_dns(&mut self, servers: Vec<IpAddr>) -> Result<()> {
- let mut state = self.lock_state();
- let new_state = match state.take() {
- None => {
- let backup = read_config().chain_err(|| ErrorKind::BackupResolvConf)?;
- write_backup(&backup).chain_err(|| ErrorKind::BackupResolvConf)?;
-
- State {
- backup,
- desired_dns: servers,
- }
- }
- Some(previous_state) => State {
- backup: previous_state.backup,
- desired_dns: servers,
- },
- };
-
- let new_config = new_state.desired_config();
-
- *state = Some(new_state);
+ use self::DnsSettings::*;
- write_config(&new_config)
- }
-
- pub fn reset(&mut self) -> Result<()> {
- if let Some(state) = self.lock_state().take() {
- write_config(&state.backup)?;
- let _ = fs::remove_file(RESOLV_CONF_BACKUP_PATH);
+ match self {
+ StaticResolvConf(ref mut static_resolv_conf) => static_resolv_conf.set_dns(servers)?,
}
Ok(())
}
- fn lock_state(&self) -> MutexGuard<Option<State>> {
- self.state
- .lock()
- .expect("a thread panicked while using the DNS configuration state")
- }
-}
-
-struct State {
- backup: Config,
- desired_dns: Vec<IpAddr>,
-}
-
-impl State {
- fn desired_config(&self) -> Config {
- let mut config = self.backup.clone();
-
- config.nameservers = self
- .desired_dns
- .iter()
- .map(|&address| ScopedIp::from(address))
- .collect();
-
- config
- }
-}
-
-struct DnsWatcher {
- _watcher: RecommendedWatcher,
-}
-
-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).chain_err(|| ErrorKind::WatchResolvConf)?;
-
- watcher
- .watch(RESOLV_CONF_PATH, RecursiveMode::NonRecursive)
- .chain_err(|| ErrorKind::WatchResolvConf)?;
-
- thread::spawn(move || Self::event_loop(event_rx, state));
-
- Ok(DnsWatcher { _watcher: watcher })
- }
-
- fn event_loop(events: mpsc::Receiver<notify::RawEvent>, state: Arc<Mutex<Option<State>>>) {
- for _ in events {
- let locked_state = state
- .lock()
- .expect("a thread panicked while using the DNS configuration state");
+ pub fn reset(&mut self) -> Result<()> {
+ use self::DnsSettings::*;
- if let Err(error) = Self::update(locked_state) {
- let chained_error = error
- .chain_err(|| "Failed to update DNS state after DNS settings have changed.");
- error!("{}", chained_error.display_chain());
- }
+ match self {
+ StaticResolvConf(ref mut static_resolv_conf) => static_resolv_conf.reset()?,
}
- }
-
- fn update(mut locked_state: MutexGuard<Option<State>>) -> Result<()> {
- if let &mut Some(ref mut state) = locked_state.deref_mut() {
- let mut new_config = read_config()?;
- let desired_nameservers = state
- .desired_dns
- .iter()
- .map(|&address| ScopedIp::from(address))
- .collect();
-
- if new_config.nameservers != desired_nameservers {
- state.backup = new_config.clone();
- new_config.nameservers = desired_nameservers;
-
- write_config(&new_config)
- } else {
- new_config.nameservers.clear();
- new_config.nameservers.append(&mut state.backup.nameservers);
- state.backup = new_config;
- write_backup(&state.backup).chain_err(|| "Failed to update /etc/resolv.conf backup")
- }
- } else {
- Ok(())
- }
- }
-}
-
-fn read_config() -> Result<Config> {
- let contents =
- fs::read_to_string(RESOLV_CONF_PATH).chain_err(|| "Failed to read /etc/resolv.conf")?;
- let config =
- Config::parse(&contents).chain_err(|| "Failed to parse contents of /etc/resolv.conf")?;
-
- Ok(config)
-}
-
-fn write_config(config: &Config) -> Result<()> {
- fs::write(RESOLV_CONF_PATH, config.to_string().as_bytes())
- .chain_err(|| ErrorKind::WriteResolvConf)
-}
-
-fn write_backup(backup: &Config) -> Result<()> {
- fs::write(RESOLV_CONF_BACKUP_PATH, backup.to_string().as_bytes())
- .chain_err(|| "Failed to write to /etc/resolv.conf backup file")
-}
-
-fn restore_from_backup() -> Result<()> {
- match fs::read_to_string(RESOLV_CONF_BACKUP_PATH) {
- Ok(backup) => {
- info!("Restoring DNS state from backup");
- let config = Config::parse(&backup)
- .chain_err(|| "Backup of /etc/resolv.conf could not be parsed")?;
-
- write_config(&config)?;
-
- fs::remove_file(RESOLV_CONF_BACKUP_PATH)
- .chain_err(|| "Failed to remove stale backup of /etc/resolv.conf")
- }
- Err(ref error) if error.kind() == io::ErrorKind::NotFound => {
- debug!("No DNS state backup to restore");
- Ok(())
- }
- Err(error) => Err(Error::with_chain(
- error,
- "Failed to read /etc/resolv.conf backup",
- )),
+ Ok(())
}
}
diff --git a/talpid-core/src/security/linux/dns/static_resolv_conf.rs b/talpid-core/src/security/linux/dns/static_resolv_conf.rs
new file mode 100644
index 0000000000..e9e81e5446
--- /dev/null
+++ b/talpid-core/src/security/linux/dns/static_resolv_conf.rs
@@ -0,0 +1,214 @@
+extern crate notify;
+extern crate resolv_conf;
+
+use std::net::IpAddr;
+use std::ops::DerefMut;
+use std::sync::{mpsc, Arc, Mutex, MutexGuard};
+use std::{fs, io, thread};
+
+use error_chain::ChainedError;
+
+use self::notify::{RecommendedWatcher, RecursiveMode, Watcher};
+use self::resolv_conf::{Config, ScopedIp};
+
+const RESOLV_CONF_PATH: &str = "/etc/resolv.conf";
+const RESOLV_CONF_BACKUP_PATH: &str = "/etc/resolv.conf.mullvadbackup";
+
+error_chain! {
+ errors {
+ WatchResolvConf {
+ description("Failed to watch /etc/resolv.conf for changes")
+ }
+
+ WriteResolvConf {
+ description("Failed to write to /etc/resolv.conf")
+ }
+
+ BackupResolvConf {
+ description("Failed to create backup of /etc/resolv.conf")
+ }
+
+ RestoreResolvConf {
+ description("Failed to restore /etc/resolv.conf from backup")
+ }
+ }
+}
+
+pub struct StaticResolvConf {
+ state: Arc<Mutex<Option<State>>>,
+ _watcher: DnsWatcher,
+}
+
+impl StaticResolvConf {
+ pub fn new() -> Result<Self> {
+ restore_from_backup().chain_err(|| ErrorKind::RestoreResolvConf)?;
+
+ let state = Arc::new(Mutex::new(None));
+ let watcher = DnsWatcher::start(state.clone())?;
+
+ Ok(StaticResolvConf {
+ state,
+ _watcher: watcher,
+ })
+ }
+
+ pub fn set_dns(&mut self, servers: Vec<IpAddr>) -> Result<()> {
+ let mut state = self.lock_state();
+ let new_state = match state.take() {
+ None => {
+ let backup = read_config().chain_err(|| ErrorKind::BackupResolvConf)?;
+ write_backup(&backup).chain_err(|| ErrorKind::BackupResolvConf)?;
+
+ State {
+ backup,
+ desired_dns: servers,
+ }
+ }
+ Some(previous_state) => State {
+ backup: previous_state.backup,
+ desired_dns: servers,
+ },
+ };
+
+ let new_config = new_state.desired_config();
+
+ *state = Some(new_state);
+
+ write_config(&new_config)
+ }
+
+ pub fn reset(&mut self) -> Result<()> {
+ if let Some(state) = self.lock_state().take() {
+ write_config(&state.backup)?;
+ let _ = fs::remove_file(RESOLV_CONF_BACKUP_PATH);
+ }
+
+ Ok(())
+ }
+
+ fn lock_state(&self) -> MutexGuard<Option<State>> {
+ self.state
+ .lock()
+ .expect("a thread panicked while using the DNS configuration state")
+ }
+}
+
+struct State {
+ backup: Config,
+ desired_dns: Vec<IpAddr>,
+}
+
+impl State {
+ fn desired_config(&self) -> Config {
+ let mut config = self.backup.clone();
+
+ config.nameservers = self
+ .desired_dns
+ .iter()
+ .map(|&address| ScopedIp::from(address))
+ .collect();
+
+ config
+ }
+}
+
+struct DnsWatcher {
+ _watcher: RecommendedWatcher,
+}
+
+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).chain_err(|| ErrorKind::WatchResolvConf)?;
+
+ watcher
+ .watch(RESOLV_CONF_PATH, RecursiveMode::NonRecursive)
+ .chain_err(|| ErrorKind::WatchResolvConf)?;
+
+ thread::spawn(move || Self::event_loop(event_rx, state));
+
+ Ok(DnsWatcher { _watcher: watcher })
+ }
+
+ fn event_loop(events: mpsc::Receiver<notify::RawEvent>, state: Arc<Mutex<Option<State>>>) {
+ for _ in events {
+ let locked_state = state
+ .lock()
+ .expect("a thread panicked while using the DNS configuration state");
+
+ if let Err(error) = Self::update(locked_state) {
+ let chained_error = error
+ .chain_err(|| "Failed to update DNS state after DNS settings have changed.");
+ error!("{}", chained_error.display_chain());
+ }
+ }
+ }
+
+ fn update(mut locked_state: MutexGuard<Option<State>>) -> Result<()> {
+ if let &mut Some(ref mut state) = locked_state.deref_mut() {
+ let mut new_config = read_config()?;
+ let desired_nameservers = state
+ .desired_dns
+ .iter()
+ .map(|&address| ScopedIp::from(address))
+ .collect();
+
+ if new_config.nameservers != desired_nameservers {
+ state.backup = new_config.clone();
+ new_config.nameservers = desired_nameservers;
+
+ write_config(&new_config)
+ } else {
+ new_config.nameservers.clear();
+ new_config.nameservers.append(&mut state.backup.nameservers);
+ state.backup = new_config;
+
+ write_backup(&state.backup).chain_err(|| "Failed to update /etc/resolv.conf backup")
+ }
+ } else {
+ Ok(())
+ }
+ }
+}
+
+fn read_config() -> Result<Config> {
+ let contents =
+ fs::read_to_string(RESOLV_CONF_PATH).chain_err(|| "Failed to read /etc/resolv.conf")?;
+ let config =
+ Config::parse(&contents).chain_err(|| "Failed to parse contents of /etc/resolv.conf")?;
+
+ Ok(config)
+}
+
+fn write_config(config: &Config) -> Result<()> {
+ fs::write(RESOLV_CONF_PATH, config.to_string().as_bytes())
+ .chain_err(|| ErrorKind::WriteResolvConf)
+}
+
+fn write_backup(backup: &Config) -> Result<()> {
+ fs::write(RESOLV_CONF_BACKUP_PATH, backup.to_string().as_bytes())
+ .chain_err(|| "Failed to write to /etc/resolv.conf backup file")
+}
+
+fn restore_from_backup() -> Result<()> {
+ match fs::read_to_string(RESOLV_CONF_BACKUP_PATH) {
+ Ok(backup) => {
+ info!("Restoring DNS state from backup");
+ let config = Config::parse(&backup)
+ .chain_err(|| "Backup of /etc/resolv.conf could not be parsed")?;
+
+ write_config(&config)?;
+
+ fs::remove_file(RESOLV_CONF_BACKUP_PATH)
+ .chain_err(|| "Failed to remove stale backup of /etc/resolv.conf")
+ }
+ Err(ref error) if error.kind() == io::ErrorKind::NotFound => {
+ debug!("No DNS state backup to restore");
+ Ok(())
+ }
+ Err(error) => Err(Error::with_chain(
+ error,
+ "Failed to read /etc/resolv.conf backup",
+ )),
+ }
+}