diff options
| author | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2018-04-09 22:24:37 -0300 |
|---|---|---|
| committer | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2018-04-17 07:33:50 -0300 |
| commit | 90db45c2baec09d6577bb2dbc6f16d5b046e6859 (patch) | |
| tree | 4417b5d16b5d822b747a4affd2f7ff7d1658e2e6 | |
| parent | 6939b5ef97b34d4052c6a55768ae18c382e8633b (diff) | |
| download | mullvadvpn-90db45c2baec09d6577bb2dbc6f16d5b046e6859.tar.xz mullvadvpn-90db45c2baec09d6577bb2dbc6f16d5b046e6859.zip | |
Implement Linux DNS configuration with resolv.conf
| -rw-r--r-- | Cargo.lock | 10 | ||||
| -rw-r--r-- | talpid-core/Cargo.toml | 3 | ||||
| -rw-r--r-- | talpid-core/src/firewall/linux.rs | 21 | ||||
| -rw-r--r-- | talpid-core/src/firewall/linux/dns.rs | 93 | ||||
| -rw-r--r-- | talpid-core/src/firewall/linux/mod.rs | 47 |
5 files changed, 153 insertions, 21 deletions
diff --git a/Cargo.lock b/Cargo.lock index c0043253e7..634ddf679d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -957,6 +957,14 @@ dependencies = [ ] [[package]] +name = "resolv-conf" +version = "0.6.0" +source = "git+https://github.com/tailhook/resolv-conf.git#95d629bdd333916ac4659571ddf03b495329f191" +dependencies = [ + "quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "rustc-demangle" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1160,6 +1168,7 @@ dependencies = [ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "openvpn-plugin 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "pfctl 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "resolv-conf 0.6.0 (git+https://github.com/tailhook/resolv-conf.git)", "shell-escape 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "system-configuration 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "talpid-ipc 0.1.0", @@ -1584,6 +1593,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8e931c58b93d86f080c734bfd2bce7dd0079ae2331235818133c8be7f422e20e" "checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a" "checksum remove_dir_all 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b5d2f806b0fcdabd98acd380dc8daef485e22bcb7cddc811d1337967f2528cf5" +"checksum resolv-conf 0.6.0 (git+https://github.com/tailhook/resolv-conf.git)" = "<none>" "checksum rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11fb43a206a04116ffd7cfcf9bcb941f8eb6cc7ff667272246b0a1c74259a3cb" "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" "checksum schannel 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "fbaffce35eb61c5b00846e73128b0cd62717e7c0ec46abbec132370d013975b4" diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml index a95d46eb9e..f9c3aedee5 100644 --- a/talpid-core/Cargo.toml +++ b/talpid-core/Cargo.toml @@ -22,6 +22,9 @@ talpid-types = { path = "../talpid-types" } [target.'cfg(unix)'.dependencies] libc = "0.2.20" +[target.'cfg(target_os = "linux")'.dependencies] +resolv-conf = { git = "https://github.com/tailhook/resolv-conf.git" } + [target.'cfg(target_os = "macos")'.dependencies] pfctl = "0.1" system-configuration = "0.1" diff --git a/talpid-core/src/firewall/linux.rs b/talpid-core/src/firewall/linux.rs deleted file mode 100644 index 53c3bfb12f..0000000000 --- a/talpid-core/src/firewall/linux.rs +++ /dev/null @@ -1,21 +0,0 @@ -use super::{Firewall, SecurityPolicy}; - -error_chain!{} - -/// The Linux implementation for the `Firewall` trait. -pub struct Netfilter; -impl Firewall for Netfilter { - type Error = Error; - - fn new() -> Result<Self> { - Ok(Netfilter) - } - - fn apply_policy(&mut self, _policy: SecurityPolicy) -> Result<()> { - Ok(()) - } - - fn reset_policy(&mut self) -> Result<()> { - Ok(()) - } -} diff --git a/talpid-core/src/firewall/linux/dns.rs b/talpid-core/src/firewall/linux/dns.rs new file mode 100644 index 0000000000..d2d1627dd0 --- /dev/null +++ b/talpid-core/src/firewall/linux/dns.rs @@ -0,0 +1,93 @@ +extern crate resolv_conf; + +use std::fs::File; +use std::io::{self, Read, Write}; +use std::net::IpAddr; + +use self::resolv_conf::{Config, ScopedIp}; + +error_chain!{ + errors { + ParseResolvConf { + description("failed to parse contents of /etc/resolv.conf") + } + + ReadResolvConf { + description("failed to read /etc/resolv.conf") + } + + WriteResolvConf { + description("failed to write to /etc/resolv.conf") + } + } +} + +pub struct DnsSettings { + backup: Option<String>, + desired_dns: Option<Vec<IpAddr>>, +} + +impl DnsSettings { + pub fn new() -> Result<Self> { + Ok(DnsSettings { + backup: None, + desired_dns: None, + }) + } + + pub fn set_dns(&mut self, servers: Vec<IpAddr>) -> Result<()> { + if self.backup.is_none() { + self.backup = Some(Self::read_resolv_conf().chain_err(|| ErrorKind::ReadResolvConf)?); + } + + self.desired_dns = Some(servers); + self.configure_dns()?; + + Ok(()) + } + + pub fn reset(&mut self) -> Result<()> { + self.desired_dns = None; + + if let Some(backup) = self.backup.take() { + Self::write_resolv_conf(&backup).chain_err(|| ErrorKind::WriteResolvConf)?; + } + + Ok(()) + } + + fn configure_dns(&self) -> Result<()> { + let mut config = match self.backup { + Some(ref previous_config) => { + Config::parse(previous_config).chain_err(|| ErrorKind::ParseResolvConf)? + } + None => Config::new(), + }; + + if let Some(ref nameservers) = self.desired_dns { + config.nameservers = nameservers + .iter() + .map(|&address| ScopedIp::from(address)) + .collect(); + } else { + config.nameservers.clear(); + } + + Self::write_resolv_conf(&config.to_string()).chain_err(|| ErrorKind::WriteResolvConf) + } + + fn read_resolv_conf() -> io::Result<String> { + let mut file = File::open("/etc/resolv.conf")?; + let mut contents = String::new(); + + file.read_to_string(&mut contents)?; + + Ok(contents) + } + + fn write_resolv_conf(contents: &str) -> io::Result<()> { + let mut file = File::create("/etc/resolv.conf")?; + + file.write_all(contents.as_bytes()) + } +} diff --git a/talpid-core/src/firewall/linux/mod.rs b/talpid-core/src/firewall/linux/mod.rs new file mode 100644 index 0000000000..6b8e213f54 --- /dev/null +++ b/talpid-core/src/firewall/linux/mod.rs @@ -0,0 +1,47 @@ +use error_chain::ChainedError; + +use super::{Firewall, SecurityPolicy}; + +mod dns; + +use self::dns::DnsSettings; + +error_chain! { + links { + DnsSettings(self::dns::Error, self::dns::ErrorKind) #[doc = "DNS error"]; + } +} + +/// The Linux implementation for the `Firewall` trait. +pub struct Netfilter { + dns_settings: DnsSettings, +} + +impl Firewall for Netfilter { + type Error = Error; + + fn new() -> Result<Self> { + Ok(Netfilter { + dns_settings: DnsSettings::new()?, + }) + } + + fn apply_policy(&mut self, policy: SecurityPolicy) -> Result<()> { + match policy { + SecurityPolicy::Connected { tunnel, .. } => { + self.dns_settings.set_dns(vec![tunnel.gateway.into()])?; + } + _ => (), + } + + Ok(()) + } + + fn reset_policy(&mut self) -> Result<()> { + if let Err(error) = self.dns_settings.reset() { + warn!("Failed to reset DNS settings: {}", error.display_chain()); + } + + Ok(()) + } +} |
