summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2018-04-09 22:24:37 -0300
committerJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2018-04-17 07:33:50 -0300
commit90db45c2baec09d6577bb2dbc6f16d5b046e6859 (patch)
tree4417b5d16b5d822b747a4affd2f7ff7d1658e2e6
parent6939b5ef97b34d4052c6a55768ae18c382e8633b (diff)
downloadmullvadvpn-90db45c2baec09d6577bb2dbc6f16d5b046e6859.tar.xz
mullvadvpn-90db45c2baec09d6577bb2dbc6f16d5b046e6859.zip
Implement Linux DNS configuration with resolv.conf
-rw-r--r--Cargo.lock10
-rw-r--r--talpid-core/Cargo.toml3
-rw-r--r--talpid-core/src/firewall/linux.rs21
-rw-r--r--talpid-core/src/firewall/linux/dns.rs93
-rw-r--r--talpid-core/src/firewall/linux/mod.rs47
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(())
+ }
+}