summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2018-09-18 07:00:04 -0300
committerJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2018-09-18 07:00:04 -0300
commit0dc5bd172b34f64385a9c9542c29b85413fec2bb (patch)
tree93db7f9d3d69c0c6aacc0fe603521439ff422980
parentfb80961d2c6e610aa212a8101aa9d682b98ca986 (diff)
parent5ab2ecdbc40d1810c66d81284983f3175a0e32e1 (diff)
downloadmullvadvpn-0dc5bd172b34f64385a9c9542c29b85413fec2bb.tar.xz
mullvadvpn-0dc5bd172b34f64385a9c9542c29b85413fec2bb.zip
Merge branch 'advanced-linux-dns'
-rw-r--r--CHANGELOG.md3
-rw-r--r--README.md6
-rw-r--r--talpid-core/src/lib.rs1
-rw-r--r--talpid-core/src/security/linux/dns/mod.rs67
-rw-r--r--talpid-core/src/security/linux/dns/resolvconf.rs83
-rw-r--r--talpid-core/src/security/linux/dns/static_resolv_conf.rs (renamed from talpid-core/src/security/linux/dns.rs)6
-rw-r--r--talpid-core/src/security/linux/mod.rs3
7 files changed, 165 insertions, 4 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4f2c1c79a1..777c629dc5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,6 +23,9 @@ Line wrap the file at 100 chars. Th
## [Unreleased]
+### Added
+#### Linux
+- Add support for DNS configuration using resolvconf.
## [2018.3] - 2018-09-17
diff --git a/README.md b/README.md
index e3d1d3c642..9b4065a2d6 100644
--- a/README.md
+++ b/README.md
@@ -140,6 +140,12 @@ sections.
* `TALPID_NFTABLES_COUNTERS` - Set to `"1"` to add packet counters to all firewall rules on
Linux.
+* `TALPID_DNS_MODULE` - Allows changing the method that will be used for DNS configuration on Linux.
+ By default this is automatically detected, but you can set it to one of the options below to
+ choose a specific method:
+ * `"static-file"`: change the `/etc/resolv.conf` file directly
+ * `"resolvconf"`: use the `resolvconf` program
+
## Building and running the Electron GUI app
diff --git a/talpid-core/src/lib.rs b/talpid-core/src/lib.rs
index 54e3ef08ef..596f6a6d6c 100644
--- a/talpid-core/src/lib.rs
+++ b/talpid-core/src/lib.rs
@@ -11,6 +11,7 @@
//! the License, or (at your option) any later version.
extern crate atty;
+#[macro_use]
extern crate duct;
#[macro_use]
extern crate log;
diff --git a/talpid-core/src/security/linux/dns/mod.rs b/talpid-core/src/security/linux/dns/mod.rs
new file mode 100644
index 0000000000..7cc004ce96
--- /dev/null
+++ b/talpid-core/src/security/linux/dns/mod.rs
@@ -0,0 +1,67 @@
+mod resolvconf;
+mod static_resolv_conf;
+
+use std::env;
+use std::net::IpAddr;
+
+use self::resolvconf::Resolvconf;
+use self::static_resolv_conf::StaticResolvConf;
+
+error_chain! {
+ errors {
+ NoDnsSettingsManager {
+ description("No DNS settings manager detected")
+ }
+ }
+
+ links {
+ Resolvconf(resolvconf::Error, resolvconf::ErrorKind);
+ StaticResolvConf(static_resolv_conf::Error, static_resolv_conf::ErrorKind);
+ }
+}
+
+pub enum DnsSettings {
+ Resolvconf(Resolvconf),
+ StaticResolvConf(StaticResolvConf),
+}
+
+impl DnsSettings {
+ pub fn new() -> Result<Self> {
+ let dns_module = env::var_os("TALPID_DNS_MODULE");
+
+ Ok(match dns_module.as_ref().and_then(|value| value.to_str()) {
+ Some("static-file") => DnsSettings::StaticResolvConf(StaticResolvConf::new()?),
+ Some("resolvconf") => DnsSettings::Resolvconf(Resolvconf::new()?),
+ Some(_) | None => Self::with_detected_dns_manager()?,
+ })
+ }
+
+ fn with_detected_dns_manager() -> Result<Self> {
+ Resolvconf::new()
+ .map(DnsSettings::Resolvconf)
+ .or_else(|_| StaticResolvConf::new().map(DnsSettings::StaticResolvConf))
+ .chain_err(|| ErrorKind::NoDnsSettingsManager)
+ }
+
+ pub fn set_dns(&mut self, interface: &str, servers: Vec<IpAddr>) -> Result<()> {
+ use self::DnsSettings::*;
+
+ match self {
+ Resolvconf(ref mut resolvconf) => resolvconf.set_dns(interface, servers)?,
+ StaticResolvConf(ref mut static_resolv_conf) => static_resolv_conf.set_dns(servers)?,
+ }
+
+ Ok(())
+ }
+
+ pub fn reset(&mut self) -> Result<()> {
+ use self::DnsSettings::*;
+
+ match self {
+ Resolvconf(ref mut resolvconf) => resolvconf.reset()?,
+ StaticResolvConf(ref mut static_resolv_conf) => static_resolv_conf.reset()?,
+ }
+
+ Ok(())
+ }
+}
diff --git a/talpid-core/src/security/linux/dns/resolvconf.rs b/talpid-core/src/security/linux/dns/resolvconf.rs
new file mode 100644
index 0000000000..d1ac319edb
--- /dev/null
+++ b/talpid-core/src/security/linux/dns/resolvconf.rs
@@ -0,0 +1,83 @@
+use std::collections::HashSet;
+use std::net::IpAddr;
+use std::path::PathBuf;
+
+use which::which;
+
+error_chain! {
+ errors {
+ NoResolvconf {
+ description("Failed to detect 'resolvconf' program")
+ }
+ RunResolvconf {
+ description("Failed to execute 'resolvconf' program")
+ }
+ AddRecordError(stderr: String) {
+ description("Using 'resolvconf' to add a record failed")
+ display("Using 'resolvconf' to add a record failed: {}", stderr)
+ }
+ DeleteRecordError {
+ description("Using 'resolvconf' to delete a record failed")
+ }
+ }
+}
+
+pub struct Resolvconf {
+ record_names: HashSet<String>,
+ resolvconf: PathBuf,
+}
+
+impl Resolvconf {
+ pub fn new() -> Result<Self> {
+ Ok(Resolvconf {
+ record_names: HashSet::new(),
+ resolvconf: which("resolvconf").map_err(|_| Error::from(ErrorKind::NoResolvconf))?,
+ })
+ }
+
+ pub fn set_dns(&mut self, interface: &str, servers: Vec<IpAddr>) -> Result<()> {
+ let record_name = format!("{}.mullvad", interface);
+ let mut record_contents = String::new();
+
+ for address in servers {
+ record_contents.push_str("nameserver ");
+ record_contents.push_str(&address.to_string());
+ record_contents.push('\n');
+ }
+
+ let output = cmd!(&self.resolvconf, "-a", &record_name)
+ .input(record_contents)
+ .run()
+ .chain_err(|| ErrorKind::RunResolvconf)?;
+
+ ensure!(
+ output.status.success(),
+ ErrorKind::AddRecordError(String::from_utf8_lossy(&output.stderr).to_string())
+ );
+
+ self.record_names.insert(record_name);
+
+ Ok(())
+ }
+
+ pub fn reset(&mut self) -> Result<()> {
+ let mut result = Ok(());
+
+ for record_name in self.record_names.drain() {
+ let output = cmd!(&self.resolvconf, "-d", &record_name)
+ .run()
+ .chain_err(|| ErrorKind::RunResolvconf)?;
+
+ if !output.status.success() {
+ error!(
+ "Failed to delete 'resolvconf' record '{}':\n{}",
+ record_name,
+ String::from_utf8_lossy(&output.stderr)
+ );
+ result = Err(Error::from(ErrorKind::DeleteRecordError));
+ }
+ }
+
+ result
+ }
+}
diff --git a/talpid-core/src/security/linux/dns.rs b/talpid-core/src/security/linux/dns/static_resolv_conf.rs
index a85e45cbfb..e9e81e5446 100644
--- a/talpid-core/src/security/linux/dns.rs
+++ b/talpid-core/src/security/linux/dns/static_resolv_conf.rs
@@ -34,19 +34,19 @@ error_chain! {
}
}
-pub struct DnsSettings {
+pub struct StaticResolvConf {
state: Arc<Mutex<Option<State>>>,
_watcher: DnsWatcher,
}
-impl DnsSettings {
+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(DnsSettings {
+ Ok(StaticResolvConf {
state,
_watcher: watcher,
})
diff --git a/talpid-core/src/security/linux/mod.rs b/talpid-core/src/security/linux/mod.rs
index f4de49bfa9..ecf9e671b5 100644
--- a/talpid-core/src/security/linux/mod.rs
+++ b/talpid-core/src/security/linux/mod.rs
@@ -89,7 +89,8 @@ impl NetworkSecurityT for NetworkSecurity {
fn apply_policy(&mut self, policy: SecurityPolicy) -> Result<()> {
if let SecurityPolicy::Connected { ref tunnel, .. } = policy {
- self.dns_settings.set_dns(vec![tunnel.gateway.into()])?;
+ self.dns_settings
+ .set_dns(&tunnel.interface, vec![tunnel.gateway.into()])?;
}
let table = Table::new(&self.table_name, ProtoFamily::Inet)?;