diff options
| author | Emīls Piņķis <emils@mullvad.net> | 2018-10-01 10:20:28 +0100 |
|---|---|---|
| committer | Emīls Piņķis <emils@mullvad.net> | 2018-10-05 12:09:18 +0100 |
| commit | 3818157d2f99b0e6c994c81695876afd88b4353b (patch) | |
| tree | 34b518245d57068a25f098f0a1c8fc95c53370d8 | |
| parent | 5ade4229a5e69e0345ae6df41c6482fb1523bda3 (diff) | |
| download | mullvadvpn-3818157d2f99b0e6c994c81695876afd88b4353b.tar.xz mullvadvpn-3818157d2f99b0e6c994c81695876afd88b4353b.zip | |
Implement DNS management using NetworkManager
| -rw-r--r-- | talpid-core/src/security/linux/dns/mod.rs | 1 | ||||
| -rw-r--r-- | talpid-core/src/security/linux/dns/network_manager.rs | 110 | ||||
| -rw-r--r-- | talpid-core/src/security/linux/dns/systemd_resolved.rs | 6 |
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>)>>()) |
