diff options
| author | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2018-09-18 07:00:04 -0300 |
|---|---|---|
| committer | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2018-09-18 07:00:04 -0300 |
| commit | 0dc5bd172b34f64385a9c9542c29b85413fec2bb (patch) | |
| tree | 93db7f9d3d69c0c6aacc0fe603521439ff422980 | |
| parent | fb80961d2c6e610aa212a8101aa9d682b98ca986 (diff) | |
| parent | 5ab2ecdbc40d1810c66d81284983f3175a0e32e1 (diff) | |
| download | mullvadvpn-0dc5bd172b34f64385a9c9542c29b85413fec2bb.tar.xz mullvadvpn-0dc5bd172b34f64385a9c9542c29b85413fec2bb.zip | |
Merge branch 'advanced-linux-dns'
| -rw-r--r-- | CHANGELOG.md | 3 | ||||
| -rw-r--r-- | README.md | 6 | ||||
| -rw-r--r-- | talpid-core/src/lib.rs | 1 | ||||
| -rw-r--r-- | talpid-core/src/security/linux/dns/mod.rs | 67 | ||||
| -rw-r--r-- | talpid-core/src/security/linux/dns/resolvconf.rs | 83 | ||||
| -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.rs | 3 |
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 @@ -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)?; |
