diff options
| author | Emīls Piņķis <emils@mullvad.net> | 2018-10-29 13:27:40 +0000 |
|---|---|---|
| committer | Emīls Piņķis <emils@mullvad.net> | 2018-10-29 13:27:40 +0000 |
| commit | bf92da29c67e457a4edf542922a0bd33ebe11203 (patch) | |
| tree | c55cef9ad34304b093ce011cb54e00bf2ba6d98f | |
| parent | 6550301700b954a96a561fd2c25e7a1201080ca4 (diff) | |
| parent | 55329a4fcd8d16cb2a94997c592c9518efced211 (diff) | |
| download | mullvadvpn-bf92da29c67e457a4edf542922a0bd33ebe11203.tar.xz mullvadvpn-bf92da29c67e457a4edf542922a0bd33ebe11203.zip | |
Merge branch 'linux-improve-nm-heuristics'
| -rw-r--r-- | talpid-core/src/security/linux/dns/mod.rs | 4 | ||||
| -rw-r--r-- | talpid-core/src/security/linux/dns/network_manager.rs | 71 |
2 files changed, 75 insertions, 0 deletions
diff --git a/talpid-core/src/security/linux/dns/mod.rs b/talpid-core/src/security/linux/dns/mod.rs index 25a9a5926d..4013ec1cab 100644 --- a/talpid-core/src/security/linux/dns/mod.rs +++ b/talpid-core/src/security/linux/dns/mod.rs @@ -74,6 +74,10 @@ impl DnsSettings { pub fn set_dns(&mut self, interface: &str, servers: Vec<IpAddr>) -> Result<()> { use self::DnsSettings::*; + self.reset()?; + // Resetting the DNS managemer in case the previously selected one isn't valid + *self = DnsSettings::new()?; + match self { Resolvconf(ref mut resolvconf) => resolvconf.set_dns(interface, servers)?, diff --git a/talpid-core/src/security/linux/dns/network_manager.rs b/talpid-core/src/security/linux/dns/network_manager.rs index 6db4893336..775ac283b3 100644 --- a/talpid-core/src/security/linux/dns/network_manager.rs +++ b/talpid-core/src/security/linux/dns/network_manager.rs @@ -7,11 +7,23 @@ use self::dbus::arg::{RefArg, Variant}; use self::dbus::stdintf::*; use self::dbus::BusType; +use std::fs::File; +use std::io::{BufRead, BufReader}; +use std::path::Path; + +use error_chain::ChainedError; + error_chain! { errors { NoNetworkManager { description("NetworkManager not detected") } + NmTooOld { + description("NetworkManager is too old") + } + NmNotManagingDns{ + description("NetworkManager is not managing DNS") + } } foreign_links { @@ -21,9 +33,12 @@ error_chain! { const NM_BUS: &str = "org.freedesktop.NetworkManager"; const NM_TOP_OBJECT: &str = "org.freedesktop.NetworkManager"; +const NM_DNS_MANAGER: &str = "org.freedesktop.NetworkManager.DnsManager"; +const NM_DNS_MANAGER_PATH: &str = "/org/freedesktop/NetworkManager/DnsManager"; const NM_OBJECT_PATH: &str = "/org/freedesktop/NetworkManager"; const RPC_TIMEOUT_MS: i32 = 1000; const GLOBAL_DNS_CONF_KEY: &str = "GlobalDnsConfiguration"; +const RC_MANAGEMENT_MODE_KEY: &str = "RcManager"; pub struct NetworkManager { dbus_connection: dbus::Connection, @@ -35,6 +50,7 @@ impl NetworkManager { let dbus_connection = dbus::Connection::get_private(BusType::System)?; let manager = NetworkManager { dbus_connection }; manager.ensure_network_manager_exists()?; + manager.ensure_resolv_conf_is_managed()?; Ok(manager) } @@ -46,6 +62,36 @@ impl NetworkManager { Ok(()) } + fn ensure_resolv_conf_is_managed(&self) -> Result<()> { + // check if NM is set to manage resolv.conf + let management_mode: Result<String> = self + .dbus_connection + .with_path(NM_BUS, NM_DNS_MANAGER_PATH, RPC_TIMEOUT_MS) + .get(NM_DNS_MANAGER, RC_MANAGEMENT_MODE_KEY) + .chain_err(|| ErrorKind::NmTooOld); + + match management_mode { + Err(e) => { + debug!("Failed to get NM management mode - {}", e.display_chain()); + return Err(e); + } + Ok(management_mode) => { + if management_mode == "unmanaged" { + return Err(Error::from(ErrorKind::NmNotManagingDns)); + } + } + } + + let expected_resolv_conf = "/var/run/NetworkManager/resolv.conf"; + let actual_resolv_conf = "/etc/resolv.conf"; + if !eq_file_content(&expected_resolv_conf, &actual_resolv_conf) { + debug!("/etc/resolv.conf differs from reference resolv.conf, therefore NM is not manaing DNS"); + bail!(ErrorKind::NmNotManagingDns); + } + + Ok(()) + } + fn as_manager(&self) -> dbus::ConnPath<&dbus::Connection> { self.dbus_connection .with_path(NM_BUS, NM_OBJECT_PATH, RPC_TIMEOUT_MS) @@ -108,3 +154,28 @@ fn create_empty_global_settings() -> GlobalDnsConfig { fn as_variant<T: RefArg + 'static>(t: T) -> Variant<Box<RefArg>> { Variant(Box::new(t) as Box<RefArg>) } + +fn eq_file_content<P: AsRef<Path>>(a: &P, b: &P) -> bool { + let file_a = match File::open(a).map(BufReader::new) { + Ok(file) => file, + Err(e) => { + debug!("Failed top open file {}: {}", a.as_ref().display(), e); + return false; + } + }; + let file_b = match File::open(b).map(BufReader::new) { + Ok(file) => file, + Err(e) => { + debug!("Failed top open file {}: {}", b.as_ref().display(), e); + return false; + } + }; + + file_a + .lines() + .zip(file_b.lines()) + .all(|(a, b)| match (a, b) { + (Ok(a), Ok(b)) => a == b, + _ => false, + }) +} |
