summaryrefslogtreecommitdiffhomepage
path: root/talpid-core
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 /talpid-core
parent6939b5ef97b34d4052c6a55768ae18c382e8633b (diff)
downloadmullvadvpn-90db45c2baec09d6577bb2dbc6f16d5b046e6859.tar.xz
mullvadvpn-90db45c2baec09d6577bb2dbc6f16d5b046e6859.zip
Implement Linux DNS configuration with resolv.conf
Diffstat (limited to 'talpid-core')
-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
4 files changed, 143 insertions, 21 deletions
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(())
+ }
+}