diff options
| author | David Lönnhager <david.l@mullvad.net> | 2021-05-21 10:53:18 +0200 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2021-06-09 15:55:07 +0200 |
| commit | f8c4a9879afb26482108ae02f3e65682d7d7ab83 (patch) | |
| tree | 654461afe3da1014b547c0cfe2a7f93f1aff6ccc /talpid-dbus | |
| parent | 6d9a1ea3a2502b0d4b3145231b7922984d77632a (diff) | |
| download | mullvadvpn-f8c4a9879afb26482108ae02f3e65682d7d7ab83.tar.xz mullvadvpn-f8c4a9879afb26482108ae02f3e65682d7d7ab83.zip | |
Infer and monitor interfaces for DNS config
Diffstat (limited to 'talpid-dbus')
| -rw-r--r-- | talpid-dbus/Cargo.toml | 1 | ||||
| -rw-r--r-- | talpid-dbus/src/systemd_resolved.rs | 156 |
2 files changed, 140 insertions, 17 deletions
diff --git a/talpid-dbus/Cargo.toml b/talpid-dbus/Cargo.toml index 5685daf922..7ae6403254 100644 --- a/talpid-dbus/Cargo.toml +++ b/talpid-dbus/Cargo.toml @@ -12,3 +12,4 @@ lazy_static = "1.0" log = "0.4" libc = "0.2" talpid-types = { path = "../talpid-types" } +tokio = { version = "0.2", features = [ "blocking" ] } diff --git a/talpid-dbus/src/systemd_resolved.rs b/talpid-dbus/src/systemd_resolved.rs index 2fd2f14956..2a5b7b2b02 100644 --- a/talpid-dbus/src/systemd_resolved.rs +++ b/talpid-dbus/src/systemd_resolved.rs @@ -52,6 +52,9 @@ pub enum Error { #[error(display = "Failed to remove a match for DNS config updates")] DnsUpdateRemoveMatchError(#[error(source)] dbus::Error), + + #[error(display = "Async D-Bus task failed")] + AsyncTaskError(#[error(source)] tokio::task::JoinError), } lazy_static! { @@ -73,6 +76,7 @@ const RPC_TIMEOUT: Duration = Duration::from_secs(1); const LINK_INTERFACE: &str = "org.freedesktop.resolve1.Link"; const MANAGER_INTERFACE: &str = "org.freedesktop.resolve1.Manager"; +const DNS_DOMAINS: &str = "Domains"; const DNS_SERVERS: &str = "DNS"; const GET_LINK_METHOD: &str = "GetLink"; const SET_DNS_METHOD: &str = "SetDNS"; @@ -84,12 +88,18 @@ pub struct SystemdResolved { pub dbus_connection: Arc<SyncConnection>, } +#[derive(Clone)] pub struct DnsState { pub interface_path: dbus::Path<'static>, pub interface_index: u32, pub set_servers: Vec<IpAddr>, } +#[derive(Clone)] +pub struct AsyncHandle { + dbus_interface: SystemdResolved, +} + impl SystemdResolved { pub fn new() -> Result<Self> { let dbus_connection = crate::get_connection().map_err(Error::ConnectDBus)?; @@ -218,14 +228,29 @@ impl SystemdResolved { ) } - pub fn set_dns(&self, interface_index: u32, servers: &[IpAddr]) -> Result<DnsState> { + pub fn get_dns(&self, interface_index: u32) -> Result<DnsState> { let link_object_path = self .fetch_link(interface_index) .map_err(|e| Error::GetLinkError(Box::new(e)))?; + let set_servers = self.get_link_dns(&link_object_path)?; + + Ok(DnsState { + interface_path: link_object_path, + interface_index, + set_servers, + }) + } - let mut set_servers = servers.to_vec(); - set_servers.sort(); - self.set_link_dns(&link_object_path, servers)?; + pub fn set_dns_state(&self, state: DnsState) -> Result<()> { + self.set_link_dns(&state.interface_path, &state.set_servers) + } + + pub fn set_dns(&self, interface_index: u32, servers: Vec<IpAddr>) -> Result<DnsState> { + let set_servers = servers.to_vec(); + let link_object_path = self + .fetch_link(interface_index) + .map_err(|e| Error::GetLinkError(Box::new(e)))?; + self.set_link_dns(&link_object_path, &servers)?; Ok(DnsState { interface_path: link_object_path, interface_index, @@ -233,6 +258,20 @@ impl SystemdResolved { }) } + pub fn get_domains(&self, interface_index: u32) -> Result<Vec<(String, bool)>> { + let link_object_path = self + .fetch_link(interface_index) + .map_err(|e| Error::GetLinkError(Box::new(e)))?; + self.get_link_dns_domains(&link_object_path) + } + + pub fn set_domains(&self, interface_index: u32, domains: &[(&str, bool)]) -> Result<()> { + let link_object_path = self + .fetch_link(interface_index) + .map_err(|e| Error::GetLinkError(Box::new(e)))?; + self.set_link_dns_domains(&link_object_path, domains) + } + fn fetch_link(&self, interface_index: u32) -> Result<dbus::Path<'static>> { self.as_manager_object() .method_call( @@ -244,6 +283,21 @@ impl SystemdResolved { .map(|result: (dbus::Path<'static>,)| result.0) } + fn get_link_dns<'a, 'b: 'a>( + &'a self, + link_object_path: &'b dbus::Path<'static>, + ) -> Result<Vec<IpAddr>> { + let servers: Vec<(i32, Vec<u8>)> = self + .as_link_object(link_object_path.clone()) + .get(LINK_INTERFACE, DNS_SERVERS) + .map_err(Error::DBusRpcError)?; + + Ok(servers + .into_iter() + .filter_map(|(_family, addr)| ip_from_bytes(&addr)) + .collect()) + } + fn set_link_dns<'a, 'b: 'a>( &'a self, link_object_path: &'b dbus::Path<'static>, @@ -255,20 +309,18 @@ impl SystemdResolved { .collect::<Vec<_>>(); self.as_link_object(link_object_path.clone()) .method_call(LINK_INTERFACE, SET_DNS_METHOD, (servers,)) - .map_err(Error::DBusRpcError)?; - - // set the search domain to catch all DNS requests, forces the link to be the prefered - // resolver, otherwise systemd-resolved will use other interfaces to do DNS lookups - let dns_domains: &[_] = &[(&".", true)]; + .map_err(Error::DBusRpcError) + } - Proxy::new( - RESOLVED_BUS, - link_object_path, - RPC_TIMEOUT, - &*self.dbus_connection, - ) - .method_call(LINK_INTERFACE, SET_DOMAINS_METHOD, (dns_domains,)) - .map_err(Error::SetDomainsError) + fn get_link_dns_domains<'a, 'b: 'a>( + &'a self, + link_object_path: &'b dbus::Path<'static>, + ) -> Result<Vec<(String, bool)>> { + let domains: Vec<(String, bool)> = self + .as_link_object(link_object_path.clone()) + .get(LINK_INTERFACE, DNS_DOMAINS) + .map_err(Error::DBusRpcError)?; + Ok(domains) } pub fn revert_link(&mut self, dns_state: &DnsState) -> std::result::Result<(), dbus::Error> { @@ -289,6 +341,21 @@ impl SystemdResolved { } } + fn set_link_dns_domains<'a, 'b: 'a>( + &'a self, + link_object_path: &'b dbus::Path<'static>, + domains: &[(&str, bool)], + ) -> Result<()> { + Proxy::new( + RESOLVED_BUS, + link_object_path, + RPC_TIMEOUT, + &*self.dbus_connection, + ) + .method_call(LINK_INTERFACE, SET_DOMAINS_METHOD, (domains,)) + .map_err(Error::SetDomainsError) + } + pub fn watch_dns_changes< F: FnMut(Vec<DnsServer>) + Send + Sync + 'static, S: Fn() -> bool + Clone + Send + Sync + 'static, @@ -338,6 +405,10 @@ impl SystemdResolved { .remove_match(dns_matcher) .map_err(Error::DnsUpdateRemoveMatchError) } + + pub fn async_handle(&self) -> AsyncHandle { + AsyncHandle::new(self.clone()) + } } #[derive(Debug)] @@ -398,3 +469,54 @@ fn ip_from_bytes(bytes: &[u8]) -> Option<IpAddr> { _ => None, } } + +impl AsyncHandle { + fn new(dbus_interface: SystemdResolved) -> Self { + Self { dbus_interface } + } + + pub async fn get_dns(&self, interface_index: u32) -> Result<DnsState> { + let interface = self.dbus_interface.clone(); + tokio::task::spawn_blocking(move || interface.get_dns(interface_index)) + .await + .map_err(Error::AsyncTaskError)? + } + + pub async fn set_dns_state(&self, state: DnsState) -> Result<()> { + let interface = self.dbus_interface.clone(); + tokio::task::spawn_blocking(move || interface.set_dns_state(state)) + .await + .map_err(Error::AsyncTaskError)? + } + + pub async fn set_dns(&self, interface_index: u32, servers: Vec<IpAddr>) -> Result<DnsState> { + let interface = self.dbus_interface.clone(); + tokio::task::spawn_blocking(move || interface.set_dns(interface_index, servers)) + .await + .map_err(Error::AsyncTaskError)? + } + + pub async fn set_domains( + &self, + interface_index: u32, + domains: &[(&'static str, bool)], + ) -> Result<()> { + let interface = self.dbus_interface.clone(); + let domains = domains.to_vec(); + tokio::task::spawn_blocking(move || interface.set_domains(interface_index, &domains)) + .await + .map_err(Error::AsyncTaskError)? + } + + pub async fn revert_link(&self, state: DnsState) -> Result<()> { + let mut interface = self.dbus_interface.clone(); + tokio::task::spawn_blocking(move || interface.revert_link(&state)) + .await + .map_err(Error::AsyncTaskError)? + .map_err(Error::DBusRpcError) + } + + pub fn handle(&self) -> &SystemdResolved { + &self.dbus_interface + } +} |
