summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorEmīls Piņķis <emils@mullvad.net>2018-10-01 10:20:28 +0100
committerEmīls Piņķis <emils@mullvad.net>2018-10-05 12:09:18 +0100
commit3818157d2f99b0e6c994c81695876afd88b4353b (patch)
tree34b518245d57068a25f098f0a1c8fc95c53370d8
parent5ade4229a5e69e0345ae6df41c6482fb1523bda3 (diff)
downloadmullvadvpn-3818157d2f99b0e6c994c81695876afd88b4353b.tar.xz
mullvadvpn-3818157d2f99b0e6c994c81695876afd88b4353b.zip
Implement DNS management using NetworkManager
-rw-r--r--talpid-core/src/security/linux/dns/mod.rs1
-rw-r--r--talpid-core/src/security/linux/dns/network_manager.rs110
-rw-r--r--talpid-core/src/security/linux/dns/systemd_resolved.rs6
3 files changed, 112 insertions, 5 deletions
diff --git a/talpid-core/src/security/linux/dns/mod.rs b/talpid-core/src/security/linux/dns/mod.rs
index 4205d9d957..b9ebb90b1a 100644
--- a/talpid-core/src/security/linux/dns/mod.rs
+++ b/talpid-core/src/security/linux/dns/mod.rs
@@ -1,6 +1,7 @@
mod resolvconf;
mod static_resolv_conf;
mod systemd_resolved;
+mod network_manager;
use std::env;
use std::net::IpAddr;
diff --git a/talpid-core/src/security/linux/dns/network_manager.rs b/talpid-core/src/security/linux/dns/network_manager.rs
new file mode 100644
index 0000000000..5bdddc7174
--- /dev/null
+++ b/talpid-core/src/security/linux/dns/network_manager.rs
@@ -0,0 +1,110 @@
+extern crate dbus;
+
+use std::collections::HashMap;
+use std::net::IpAddr;
+
+use self::dbus::arg::{RefArg, Variant};
+use self::dbus::stdintf::*;
+use self::dbus::BusType;
+
+error_chain! {
+ errors {
+ NoNetworkManager {
+ description("NetworkManager not detected")
+ }
+ }
+
+ foreign_links {
+ DbusError(dbus::Error);
+ }
+}
+
+const NM_BUS: &str = "org.freedesktop.NetworkManager";
+const NM_TOP_OBJECT: &str = "org.freedesktop.NetworkManager";
+const NM_OBJECT_PATH: &str = "/org/freedesktop/NetworkManager";
+const RPC_TIMEOUT_MS: i32 = 1000;
+const GLOBAL_DNS_CONF_KEY: &str = "GlobalDnsConfiguration";
+
+pub struct NetworkManager {
+ dbus_connection: dbus::Connection,
+}
+
+
+impl NetworkManager {
+ pub fn new() -> Result<Self> {
+ let dbus_connection = dbus::Connection::get_private(BusType::System)?;
+ let manager = NetworkManager { dbus_connection };
+ manager.ensure_network_manager_exists()?;
+ Ok(manager)
+ }
+
+ fn ensure_network_manager_exists(&self) -> Result<()> {
+ let _: Box<RefArg> = self
+ .as_manager()
+ .get(&NM_TOP_OBJECT, GLOBAL_DNS_CONF_KEY)
+ .chain_err(|| ErrorKind::NoNetworkManager)?;
+ Ok(())
+ }
+
+ fn as_manager<'a>(&'a self) -> dbus::ConnPath<'a, &'a dbus::Connection> {
+ self.dbus_connection
+ .with_path(NM_BUS, NM_OBJECT_PATH, RPC_TIMEOUT_MS)
+ }
+
+ pub fn set_dns(&mut self, servers: &[IpAddr]) -> Result<()> {
+ self.set_global_dns(create_global_settings(servers))
+ }
+
+ fn set_global_dns(&mut self, config: GlobalDnsConfig) -> Result<()> {
+ self.as_manager()
+ .set(NM_TOP_OBJECT, GLOBAL_DNS_CONF_KEY, config)
+ .map_err(|e| e.into())
+ }
+
+ pub fn reset(&mut self) -> Result<()> {
+ self.set_global_dns(create_empty_global_settings())
+ }
+}
+
+type GlobalDnsConfig = HashMap<&'static str, Variant<Box<RefArg>>>;
+
+// The NetworkManager GlobalDnsConfiguration schema looks something like this
+// {
+// "searches": ["example.com", "search-domain.com"],
+// "options": "this field is currently unused",
+// "domains": {
+// "*": {
+// "servers": [ "1.1.1.1" ]
+// }
+// "example.com": {
+// "servers": [ "8.8.8.8", "8.8.4.4" ]
+// }
+// }
+// }
+fn create_global_settings(server_list: &[IpAddr]) -> GlobalDnsConfig {
+ let mut global_settings = HashMap::new();
+ let mut domain_settings = HashMap::new();
+ let mut specific_domain_config = HashMap::new();
+
+ let dns_server_list = as_variant(
+ server_list
+ .iter()
+ .map(ToString::to_string)
+ .collect::<Vec<_>>(),
+ );
+ specific_domain_config.insert("servers".to_owned(), dns_server_list);
+ domain_settings.insert("*".to_owned(), as_variant(specific_domain_config));
+ global_settings.insert("domains", as_variant(domain_settings));
+ global_settings.insert("searches", as_variant(vec![] as Vec<String>));
+ global_settings.insert("options", as_variant(vec![] as Vec<String>));
+
+ global_settings
+}
+
+fn create_empty_global_settings() -> GlobalDnsConfig {
+ HashMap::new()
+}
+
+fn as_variant<T: RefArg + 'static>(t: T) -> Variant<Box<RefArg>> {
+ Variant(Box::new(t) as Box<RefArg>)
+}
diff --git a/talpid-core/src/security/linux/dns/systemd_resolved.rs b/talpid-core/src/security/linux/dns/systemd_resolved.rs
index f86ff9d7b5..e5aea9a0de 100644
--- a/talpid-core/src/security/linux/dns/systemd_resolved.rs
+++ b/talpid-core/src/security/linux/dns/systemd_resolved.rs
@@ -135,7 +135,6 @@ impl SystemdResolved {
link_object_path: &'b dbus::Path<'static>,
servers: &[IpAddr],
) -> Result<()> {
-
let server_addresses = build_addresses_argument(servers);
let mut reply = self
@@ -199,10 +198,7 @@ impl SystemdResolved {
}
fn build_addresses_argument(addresses: &[IpAddr]) -> MessageItem {
- let addresses = addresses
- .iter()
- .map(ip_address_to_message_item)
- .collect();
+ let addresses = addresses.iter().map(ip_address_to_message_item).collect();
MessageItem::Array(
MessageItemArray::new(addresses, Signature::make::<Vec<(i32, Vec<u8>)>>())