summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2022-07-11 17:09:20 +0200
committerDavid Lönnhager <david.l@mullvad.net>2022-08-10 14:19:04 +0200
commit97c55dc556ce9fca5039035070f3fb2af77c8801 (patch)
tree9106dd3e5abe4d6fa13c96e06bb9761ecadf7ba7
parent39486e0096a4d4d65b63fc91726afbd38e011532 (diff)
downloadmullvadvpn-97c55dc556ce9fca5039035070f3fb2af77c8801.tar.xz
mullvadvpn-97c55dc556ce9fca5039035070f3fb2af77c8801.zip
Flush DNS cache directly using dnsapi
-rw-r--r--Cargo.lock5
-rw-r--r--talpid-core/Cargo.toml1
-rw-r--r--talpid-core/src/dns/windows/dnsapi.rs55
-rw-r--r--talpid-core/src/dns/windows/mod.rs28
4 files changed, 65 insertions, 24 deletions
diff --git a/Cargo.lock b/Cargo.lock
index f9ef6eaa35..d8c57ef1c3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1906,9 +1906,9 @@ dependencies = [
[[package]]
name = "once_cell"
-version = "1.8.0"
+version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
+checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
[[package]]
name = "opaque-debug"
@@ -3061,6 +3061,7 @@ dependencies = [
"netlink-sys",
"nftnl",
"nix 0.23.1",
+ "once_cell",
"os_pipe",
"parity-tokio-ipc",
"parking_lot 0.11.2",
diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml
index 62e1fb2873..b2cfd9500f 100644
--- a/talpid-core/Cargo.toml
+++ b/talpid-core/Cargo.toml
@@ -18,6 +18,7 @@ futures = "0.3.15"
hex = "0.4"
ipnetwork = "0.16"
lazy_static = "1.0"
+once_cell = "1.13"
libc = "0.2"
log = "0.4"
os_pipe = "0.9"
diff --git a/talpid-core/src/dns/windows/dnsapi.rs b/talpid-core/src/dns/windows/dnsapi.rs
new file mode 100644
index 0000000000..1c6b2c4fbc
--- /dev/null
+++ b/talpid-core/src/dns/windows/dnsapi.rs
@@ -0,0 +1,55 @@
+use once_cell::sync::OnceCell;
+use std::{io, ptr};
+use winapi::{
+ shared::minwindef::{BOOL, FALSE},
+ um::libloaderapi::{FreeLibrary, GetProcAddress, LoadLibraryExW, LOAD_LIBRARY_SEARCH_SYSTEM32},
+};
+
+type FlushResolverCacheFn = unsafe extern "stdcall" fn() -> BOOL;
+
+static FLUSH_RESOLVER_CACHE: OnceCell<FlushResolverCacheFn> = OnceCell::new();
+
+/// Errors that can happen when configuring DNS on Windows.
+#[derive(err_derive::Error, Debug)]
+#[error(no_from)]
+pub enum Error {
+ /// Failed to load dnsapi.dll.
+ #[error(display = "Failed to load dnsapi.dll")]
+ LoadDll(#[error(source)] io::Error),
+
+ /// Failed to obtain exported function.
+ #[error(display = "Failed to obtain flush function")]
+ GetFunction(#[error(source)] io::Error),
+
+ /// Failed to flush the DNS cache.
+ #[error(display = "Call to flush DNS cache failed")]
+ FlushCache,
+}
+
+pub fn flush_resolver_cache() -> Result<(), Error> {
+ let flush_cache = FLUSH_RESOLVER_CACHE.get_or_try_init(|| {
+ let handle = unsafe {
+ LoadLibraryExW(
+ b"d\0n\0s\0a\0p\0i\0.\0d\0l\0l\0\0\0" as *const u8 as *const u16,
+ ptr::null_mut(),
+ LOAD_LIBRARY_SEARCH_SYSTEM32,
+ )
+ };
+ if handle.is_null() {
+ return Err(Error::LoadDll(io::Error::last_os_error()));
+ }
+ let function_addr =
+ unsafe { GetProcAddress(handle, b"DnsFlushResolverCache\0" as *const _ as *const i8) };
+ if function_addr.is_null() {
+ let error = io::Error::last_os_error();
+ unsafe { FreeLibrary(handle) };
+ return Err(Error::GetFunction(error));
+ }
+ Ok(unsafe { *(&function_addr as *const _ as *const _) })
+ })?;
+
+ if unsafe { flush_cache() } == FALSE {
+ return Err(Error::FlushCache);
+ }
+ Ok(())
+}
diff --git a/talpid-core/src/dns/windows/mod.rs b/talpid-core/src/dns/windows/mod.rs
index 78c167e769..2cb9b74f0b 100644
--- a/talpid-core/src/dns/windows/mod.rs
+++ b/talpid-core/src/dns/windows/mod.rs
@@ -1,9 +1,5 @@
-use crate::windows::{get_system_dir, guid_from_luid, luid_from_alias, string_from_guid};
-use std::{
- io,
- net::IpAddr,
- process::{Command, Stdio},
-};
+use crate::windows::{guid_from_luid, luid_from_alias, string_from_guid};
+use std::{io, net::IpAddr};
use talpid_types::ErrorExt;
use winapi::shared::guiddef::GUID;
use winreg::{
@@ -12,6 +8,8 @@ use winreg::{
RegKey,
};
+mod dnsapi;
+
/// Errors that can happen when configuring DNS on Windows.
#[derive(err_derive::Error, Debug)]
#[error(no_from)]
@@ -30,15 +28,11 @@ pub enum Error {
/// Failure to flush DNS cache.
#[error(display = "Failed to flush DNS resolver cache")]
- FlushResolverCacheError,
+ FlushResolverCacheError(dnsapi::Error),
/// Failed to update DNS servers for interface.
#[error(display = "Failed to update interface DNS servers")]
SetResolversError(#[error(source)] io::Error),
-
- /// Failed to locate system dir.
- #[error(display = "Failed to locate the system directory")]
- SystemDirError(#[error(source)] io::Error),
}
pub struct DnsMonitor {
@@ -152,15 +146,5 @@ fn config_interface<'a>(
}
fn flush_dns_cache() -> Result<(), Error> {
- let sysdir = get_system_dir().map_err(Error::SystemDirError)?;
- Command::new(sysdir.join("ipconfig.exe"))
- .arg("/flushdns")
- .stdout(Stdio::null())
- .stderr(Stdio::null())
- .status()
- .map_err(Error::ExecuteIpconfigError)?;
- // The exit code cannot be trusted. And the stdout messages from Windows CLI tools
- // are localized, so it can also not be checked. There is no way to verify if
- // this flush succeeded or failed.
- Ok(())
+ dnsapi::flush_resolver_cache().map_err(Error::FlushResolverCacheError)
}