diff options
| author | David Lönnhager <david.l@mullvad.net> | 2022-07-11 17:09:20 +0200 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2022-08-10 14:19:04 +0200 |
| commit | 97c55dc556ce9fca5039035070f3fb2af77c8801 (patch) | |
| tree | 9106dd3e5abe4d6fa13c96e06bb9761ecadf7ba7 | |
| parent | 39486e0096a4d4d65b63fc91726afbd38e011532 (diff) | |
| download | mullvadvpn-97c55dc556ce9fca5039035070f3fb2af77c8801.tar.xz mullvadvpn-97c55dc556ce9fca5039035070f3fb2af77c8801.zip | |
Flush DNS cache directly using dnsapi
| -rw-r--r-- | Cargo.lock | 5 | ||||
| -rw-r--r-- | talpid-core/Cargo.toml | 1 | ||||
| -rw-r--r-- | talpid-core/src/dns/windows/dnsapi.rs | 55 | ||||
| -rw-r--r-- | talpid-core/src/dns/windows/mod.rs | 28 |
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) } |
