summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock20
-rw-r--r--talpid-core/Cargo.toml1
-rw-r--r--talpid-core/src/security/linux/dns/mod.rs12
-rw-r--r--talpid-core/src/security/linux/dns/systemd_resolved.rs234
4 files changed, 265 insertions, 2 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 67dc2b6811..cd852af42b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -214,6 +214,15 @@ dependencies = [
]
[[package]]
+name = "dbus"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libdbus-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "derive_builder"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -700,6 +709,14 @@ version = "0.2.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "libdbus-sys"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "pkg-config 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "linked-hash-map"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1625,6 +1642,7 @@ version = "0.1.0"
dependencies = [
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dbus 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"duct 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2187,6 +2205,7 @@ dependencies = [
"checksum crossbeam-epoch 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2af0e75710d6181e234c8ecc79f14a97907850a541b13b0be1dd10992f2e4620"
"checksum crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d636a8b3bcc1b409d7ffd3facef8f21dcb4009626adbd0c5e6c4305c07253c7b"
"checksum ctrlc 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "630391922b1b893692c6334369ff528dcc3a9d8061ccf4c803aa8f83cb13db5e"
+"checksum dbus 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e34c238dfb3f5881d46ad301403cd8f8ecf946e2a4e89bdd1166728b68b5008"
"checksum derive_builder 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c998e6ab02a828dd9735c18f154e14100e674ed08cb4e1938f0e4177543f439"
"checksum derive_builder_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "735e24ee9e5fa8e16b86da5007856e97d592e11867e45d76e0c0d0a164a0b757"
"checksum dirs 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "37a76dd8b997af7107d0bb69d43903cf37153a18266f8b3fdb9911f28efb5444"
@@ -2238,6 +2257,7 @@ dependencies = [
"checksum lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca488b89a5657b0a2ecd45b95609b3e848cf1755da332a0da46e2b2b1cb371a7"
"checksum lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a6f08839bc70ef4a3fe1d566d5350f519c5912ea86be0df1740a7d247c7fc0ef"
"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d"
+"checksum libdbus-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8720f9274907052cb50313f91201597868da9d625f8dd125f2aca5bddb7e83a1"
"checksum linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e"
"checksum linked_hash_set 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3c7c91c4c7bbeb4f2f7c4e5be11e6a05bd6830bc37249c47ce1ad86ad453ff9c"
"checksum lock_api 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "949826a5ccf18c1b3a7c3d57692778d21768b79e46eb9dd07bfc4c2160036c54"
diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml
index 0221d6db4f..b0f047ef52 100644
--- a/talpid-core/Cargo.toml
+++ b/talpid-core/Cargo.toml
@@ -29,6 +29,7 @@ ipnetwork = "0.13"
lazy_static = "1.0"
[target.'cfg(target_os = "linux")'.dependencies]
+dbus = "0.6"
failure = "0.1"
notify = "4.0"
resolv-conf = "0.6.1"
diff --git a/talpid-core/src/security/linux/dns/mod.rs b/talpid-core/src/security/linux/dns/mod.rs
index 7cc004ce96..e854f4a3e1 100644
--- a/talpid-core/src/security/linux/dns/mod.rs
+++ b/talpid-core/src/security/linux/dns/mod.rs
@@ -1,11 +1,13 @@
mod resolvconf;
mod static_resolv_conf;
+mod systemd_resolved;
use std::env;
use std::net::IpAddr;
use self::resolvconf::Resolvconf;
use self::static_resolv_conf::StaticResolvConf;
+use self::systemd_resolved::SystemdResolved;
error_chain! {
errors {
@@ -17,12 +19,14 @@ error_chain! {
links {
Resolvconf(resolvconf::Error, resolvconf::ErrorKind);
StaticResolvConf(static_resolv_conf::Error, static_resolv_conf::ErrorKind);
+ SystemdResolved(systemd_resolved::Error, systemd_resolved::ErrorKind);
}
}
pub enum DnsSettings {
Resolvconf(Resolvconf),
StaticResolvConf(StaticResolvConf),
+ SystemdResolved(SystemdResolved),
}
impl DnsSettings {
@@ -37,8 +41,8 @@ impl DnsSettings {
}
fn with_detected_dns_manager() -> Result<Self> {
- Resolvconf::new()
- .map(DnsSettings::Resolvconf)
+ SystemdResolved::new()
+ .map(DnsSettings::SystemdResolved)
.or_else(|_| StaticResolvConf::new().map(DnsSettings::StaticResolvConf))
.chain_err(|| ErrorKind::NoDnsSettingsManager)
}
@@ -49,6 +53,9 @@ impl DnsSettings {
match self {
Resolvconf(ref mut resolvconf) => resolvconf.set_dns(interface, servers)?,
StaticResolvConf(ref mut static_resolv_conf) => static_resolv_conf.set_dns(servers)?,
+ SystemdResolved(ref mut systemd_resolved) => {
+ systemd_resolved.set_dns(interface, servers)?
+ }
}
Ok(())
@@ -60,6 +67,7 @@ impl DnsSettings {
match self {
Resolvconf(ref mut resolvconf) => resolvconf.reset()?,
StaticResolvConf(ref mut static_resolv_conf) => static_resolv_conf.reset()?,
+ SystemdResolved(ref mut systemd_resolved) => systemd_resolved.reset()?,
}
Ok(())
diff --git a/talpid-core/src/security/linux/dns/systemd_resolved.rs b/talpid-core/src/security/linux/dns/systemd_resolved.rs
new file mode 100644
index 0000000000..f86ff9d7b5
--- /dev/null
+++ b/talpid-core/src/security/linux/dns/systemd_resolved.rs
@@ -0,0 +1,234 @@
+extern crate dbus;
+
+use std::collections::HashMap;
+use std::net::IpAddr;
+
+use error_chain::ChainedError;
+use libc::{AF_INET, AF_INET6};
+
+use self::dbus::arg::RefArg;
+use self::dbus::stdintf::*;
+use self::dbus::{BusType, Interface, Member, MessageItem, MessageItemArray, Signature};
+
+use super::super::iface_index;
+
+error_chain! {
+ errors {
+ NoSystemdResolved {
+ description("Systemd resolved not detected")
+ }
+ InvalidInterfaceName {
+ description("Invalid network interface name")
+ }
+ DbusRpcError {
+ description("Failed to perform RPC call on DBus")
+ }
+ GetLinkError {
+ description("Failed to find link interface in resolved manager")
+ }
+ SetDnsError {
+ description("Failed to configure DNS servers")
+ }
+ RevertDnsError {
+ description("Failed to revert DNS configuration")
+ }
+ DBusError {
+ description("Failed to initialize a connection to dbus")
+ }
+ }
+
+}
+
+const RESOLVED_BUS: &str = "org.freedesktop.resolve1";
+const RPC_TIMEOUT_MS: i32 = 1000;
+
+lazy_static! {
+ static ref LINK_INTERFACE: Interface<'static> =
+ Interface::from_slice("org.freedesktop.resolve1.Link".as_bytes()).unwrap();
+ static ref MANAGER_INTERFACE: Interface<'static> =
+ Interface::from_slice("org.freedesktop.resolve1.Manager".as_bytes()).unwrap();
+ static ref GET_LINK_METHOD: Member<'static> = Member::from_slice("GetLink".as_bytes()).unwrap();
+ static ref SET_DNS_METHOD: Member<'static> = Member::from_slice("SetDNS".as_bytes()).unwrap();
+ static ref REVERT_METHOD: Member<'static> = Member::from_slice("Revert".as_bytes()).unwrap();
+}
+
+pub struct SystemdResolved {
+ dbus_connection: dbus::Connection,
+ interface_links: HashMap<String, dbus::Path<'static>>,
+}
+
+impl SystemdResolved {
+ pub fn new() -> Result<Self> {
+ let dbus_connection =
+ dbus::Connection::get_private(BusType::System).chain_err(|| ErrorKind::DBusError)?;
+ let systemd_resolved = SystemdResolved {
+ dbus_connection,
+ interface_links: HashMap::new(),
+ };
+
+ systemd_resolved.ensure_resolved_exists()?;
+
+ Ok(systemd_resolved)
+ }
+
+ fn ensure_resolved_exists(&self) -> Result<()> {
+ let _: Box<RefArg> = self
+ .as_manager_object()
+ .get(&MANAGER_INTERFACE, "DNS")
+ .chain_err(|| ErrorKind::NoSystemdResolved)?;
+
+ Ok(())
+ }
+
+ fn as_manager_object<'a>(&'a self) -> dbus::ConnPath<'a, &'a dbus::Connection> {
+ self.dbus_connection
+ .with_path(RESOLVED_BUS, "/org/freedesktop/resolve1", RPC_TIMEOUT_MS)
+ }
+
+ fn as_link_object<'a>(
+ &'a self,
+ link_object_path: dbus::Path<'a>,
+ ) -> dbus::ConnPath<'a, &'a dbus::Connection> {
+ self.dbus_connection
+ .with_path(RESOLVED_BUS, link_object_path, RPC_TIMEOUT_MS)
+ }
+
+ pub fn set_dns(&mut self, interface_name: &str, servers: &[IpAddr]) -> Result<()> {
+ let new_entry = if let Some(link_object_path) = self.interface_links.get(interface_name) {
+ self.set_link_dns(&link_object_path, servers)?;
+
+ None
+ } else {
+ let link_object_path = self.fetch_link(interface_name)?;
+
+ self.set_link_dns(&link_object_path, servers)?;
+
+ Some((interface_name.to_owned(), link_object_path))
+ };
+
+ if let Some((interface_name, link_object_path)) = new_entry {
+ self.interface_links
+ .insert(interface_name, link_object_path);
+ }
+
+ Ok(())
+ }
+
+ fn fetch_link(&self, interface_name: &str) -> Result<dbus::Path<'static>> {
+ let interface_index =
+ iface_index(interface_name).chain_err(|| ErrorKind::InvalidInterfaceName)?;
+
+ let mut reply = self
+ .as_manager_object()
+ .method_call_with_args(&MANAGER_INTERFACE, &GET_LINK_METHOD, |message| {
+ message.append_items(&[MessageItem::Int32(interface_index as i32)]);
+ })
+ .chain_err(|| ErrorKind::DbusRpcError)?;
+
+ let result = reply.as_result().chain_err(|| ErrorKind::GetLinkError)?;
+
+ result.read1().chain_err(|| ErrorKind::GetLinkError)
+ }
+
+ fn set_link_dns<'a, 'b: 'a>(
+ &'a self,
+ link_object_path: &'b dbus::Path<'static>,
+ servers: &[IpAddr],
+ ) -> Result<()> {
+
+ let server_addresses = build_addresses_argument(servers);
+
+ let mut reply = self
+ .as_link_object(link_object_path.clone())
+ .method_call_with_args(&LINK_INTERFACE, &SET_DNS_METHOD, |message| {
+ message.append_items(&[server_addresses]);
+ })
+ .chain_err(|| ErrorKind::DbusRpcError)?;
+
+ reply
+ .as_result()
+ .map(|_| ())
+ .chain_err(|| ErrorKind::SetDnsError)
+ }
+
+ pub fn reset(&mut self) -> Result<()> {
+ let mut result = Ok(());
+ let interface_links: Vec<_> = self.interface_links.drain().collect();
+
+ for (interface_name, link_object_path) in interface_links {
+ if let Err(error) = self.revert_link(link_object_path, &interface_name) {
+ let chained_error = error.chain_err(|| {
+ format!(
+ "Failed to revert DNS settings of interface: {}",
+ interface_name
+ )
+ });
+ error!("{}", chained_error.display_chain());
+ result = Err(Error::from(ErrorKind::RevertDnsError));
+ }
+ }
+
+ result
+ }
+
+ fn revert_link(
+ &mut self,
+ link_object_path: dbus::Path<'static>,
+ interface_name: &str,
+ ) -> Result<()> {
+ let link = self.as_link_object(link_object_path);
+
+ match link.method_call_with_args(&LINK_INTERFACE, &REVERT_METHOD, |_| {}) {
+ Ok(mut reply) => reply
+ .as_result()
+ .map(|_| ())
+ .chain_err(|| ErrorKind::RevertDnsError),
+ Err(error) => {
+ if error.name() == Some("org.freedesktop.DBus.Error.UnknownObject") {
+ info!(
+ "Not reseting DNS of interface {} because it no longer exists",
+ interface_name
+ );
+ Ok(())
+ } else {
+ Err(error).chain_err(|| ErrorKind::DbusRpcError)
+ }
+ }
+ }
+ }
+}
+
+fn build_addresses_argument(addresses: &[IpAddr]) -> MessageItem {
+ let addresses = addresses
+ .iter()
+ .map(ip_address_to_message_item)
+ .collect();
+
+ MessageItem::Array(
+ MessageItemArray::new(addresses, Signature::make::<Vec<(i32, Vec<u8>)>>())
+ .expect("Invalid construction of DBus array of IP addresses argument"),
+ )
+}
+
+fn ip_address_to_message_item(address: &IpAddr) -> MessageItem {
+ let (protocol, octets) = match address {
+ IpAddr::V4(ipv4_address) => (AF_INET, bytes_to_message_item_array(&ipv4_address.octets())),
+ IpAddr::V6(ipv6_address) => (
+ AF_INET6,
+ bytes_to_message_item_array(&ipv6_address.octets()),
+ ),
+ };
+
+ MessageItem::Struct(vec![
+ MessageItem::Int32(protocol),
+ MessageItem::Array(octets),
+ ])
+}
+
+fn bytes_to_message_item_array(bytes: &[u8]) -> MessageItemArray {
+ MessageItemArray::new(
+ bytes.into_iter().cloned().map(MessageItem::Byte).collect(),
+ Signature::make::<Vec<u8>>(),
+ )
+ .expect("Invalid construction of DBus array of bytes argument")
+}