summaryrefslogtreecommitdiffhomepage
path: root/talpid-core/src
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2023-02-28 17:42:42 +0100
committerDavid Lönnhager <david.l@mullvad.net>2023-03-01 15:06:32 +0100
commitc52f733be6b854ecc55de060c1b16cd1ef0ffc53 (patch)
treec32a0d5b93c3b58e0003b6075cf440df64b08e77 /talpid-core/src
parent02cf401fd27036533675a351f80c193bc00e1624 (diff)
downloadmullvadvpn-c52f733be6b854ecc55de060c1b16cd1ef0ffc53.tar.xz
mullvadvpn-c52f733be6b854ecc55de060c1b16cd1ef0ffc53.zip
Use run-time linking for iphlpapi DNS method. Fall back on netsh if necessary
Diffstat (limited to 'talpid-core/src')
-rw-r--r--talpid-core/src/dns/windows/iphlpapi.rs77
-rw-r--r--talpid-core/src/dns/windows/mod.rs7
2 files changed, 78 insertions, 6 deletions
diff --git a/talpid-core/src/dns/windows/iphlpapi.rs b/talpid-core/src/dns/windows/iphlpapi.rs
index 478599b0e9..6f33cf3cf9 100644
--- a/talpid-core/src/dns/windows/iphlpapi.rs
+++ b/talpid-core/src/dns/windows/iphlpapi.rs
@@ -1,4 +1,5 @@
use crate::dns::DnsMonitorT;
+use once_cell::sync::OnceCell;
use std::{
ffi::OsString,
io,
@@ -9,11 +10,15 @@ use std::{
use talpid_windows_net::{guid_from_luid, luid_from_alias};
use windows_sys::{
core::GUID,
+ s, w,
Win32::{
- Foundation::NO_ERROR,
+ Foundation::{ERROR_PROC_NOT_FOUND, NO_ERROR, NTSTATUS},
NetworkManagement::IpHelper::{
- SetInterfaceDnsSettings, DNS_INTERFACE_SETTINGS, DNS_INTERFACE_SETTINGS_VERSION1,
- DNS_SETTING_IPV6, DNS_SETTING_NAMESERVER,
+ DNS_INTERFACE_SETTINGS, DNS_INTERFACE_SETTINGS_VERSION1, DNS_SETTING_IPV6,
+ DNS_SETTING_NAMESERVER,
+ },
+ System::LibraryLoader::{
+ FreeLibrary, GetProcAddress, LoadLibraryExW, LOAD_LIBRARY_SEARCH_SYSTEM32,
},
},
};
@@ -37,12 +42,73 @@ pub enum Error {
/// Failure to flush DNS cache.
#[error(display = "Failed to flush DNS resolver cache")]
FlushResolverCacheError(#[error(source)] super::dnsapi::Error),
+
+ /// Failed to load iphlpapi.dll.
+ #[error(display = "Failed to load iphlpapi.dll")]
+ LoadDll(#[error(source)] io::Error),
+
+ /// Failed to obtain exported function.
+ #[error(display = "Failed to obtain DNS function")]
+ GetFunction(#[error(source)] io::Error),
+}
+
+type SetInterfaceDnsSettingsFn = unsafe extern "stdcall" fn(
+ interface: GUID,
+ settings: *const DNS_INTERFACE_SETTINGS,
+) -> NTSTATUS;
+
+struct IphlpApi {
+ set_interface_dns_settings: SetInterfaceDnsSettingsFn,
+}
+
+unsafe impl Send for IphlpApi {}
+unsafe impl Sync for IphlpApi {}
+
+static IPHLPAPI_HANDLE: OnceCell<IphlpApi> = OnceCell::new();
+
+impl IphlpApi {
+ fn new() -> Result<Self, Error> {
+ let module = unsafe { LoadLibraryExW(w!("iphlpapi.dll"), 0, LOAD_LIBRARY_SEARCH_SYSTEM32) };
+ if module == 0 {
+ log::error!("Failed to load iphlpapi.dll");
+ return Err(Error::LoadDll(io::Error::last_os_error()));
+ }
+
+ let set_interface_dns_settings =
+ unsafe { GetProcAddress(module, s!("SetInterfaceDnsSettings")) };
+ let set_interface_dns_settings = set_interface_dns_settings.ok_or_else(|| {
+ let error = io::Error::last_os_error();
+
+ if error.raw_os_error() != Some(ERROR_PROC_NOT_FOUND as i32) {
+ log::error!(
+ "Could not find SetInterfaceDnsSettings due to an unexpected error: {error}"
+ );
+ }
+
+ unsafe { FreeLibrary(module) };
+ Error::GetFunction(error)
+ })?;
+
+ // NOTE: Leaking `module` here, since we're lazily initializing it
+
+ Ok(Self {
+ set_interface_dns_settings: unsafe {
+ *(&set_interface_dns_settings as *const _ as *const _)
+ },
+ })
+ }
}
pub struct DnsMonitor {
current_guid: Option<GUID>,
}
+impl DnsMonitor {
+ pub fn is_supported() -> bool {
+ IPHLPAPI_HANDLE.get_or_try_init(|| IphlpApi::new()).is_ok()
+ }
+}
+
impl DnsMonitorT for DnsMonitor {
type Error = Error;
@@ -107,6 +173,8 @@ fn set_interface_dns_servers<T: ToString>(
servers: &[T],
flags: u32,
) -> Result<(), Error> {
+ let iphlpapi = IPHLPAPI_HANDLE.get_or_try_init(|| IphlpApi::new())?;
+
// Create comma-separated nameserver list
let nameservers = servers
.iter()
@@ -131,7 +199,8 @@ fn set_interface_dns_servers<T: ToString>(
ProfileNameServer: ptr::null_mut(),
};
- let result = unsafe { SetInterfaceDnsSettings(guid.to_owned(), &dns_interface_settings) };
+ let result =
+ unsafe { (iphlpapi.set_interface_dns_settings)(guid.to_owned(), &dns_interface_settings) };
if result != (NO_ERROR as i32) {
return Err(Error::SetInterfaceDnsSettings(result));
}
diff --git a/talpid-core/src/dns/windows/mod.rs b/talpid-core/src/dns/windows/mod.rs
index 7aed8f3258..baccf2c3d4 100644
--- a/talpid-core/src/dns/windows/mod.rs
+++ b/talpid-core/src/dns/windows/mod.rs
@@ -37,7 +37,7 @@ impl DnsMonitorT for DnsMonitor {
Some("iphlpapi") => DnsMonitorHolder::Iphlpapi(iphlpapi::DnsMonitor::new()?),
Some("tcpip") => DnsMonitorHolder::Tcpip(tcpip::DnsMonitor::new()?),
Some("netsh") => DnsMonitorHolder::Netsh(netsh::DnsMonitor::new()?),
- Some(_) | None => Self::detect_appropriate_method()?,
+ Some(_) | None => DnsMonitor::detect_appropriate_method()?,
};
log::debug!("DNS monitor: {}", inner);
@@ -75,7 +75,10 @@ impl DnsMonitorT for DnsMonitor {
impl DnsMonitor {
fn detect_appropriate_method() -> Result<DnsMonitorHolder, Error> {
- Ok(DnsMonitorHolder::Iphlpapi(iphlpapi::DnsMonitor::new()?))
+ if iphlpapi::DnsMonitor::is_supported() {
+ return Ok(DnsMonitorHolder::Iphlpapi(iphlpapi::DnsMonitor::new()?));
+ }
+ Ok(DnsMonitorHolder::Netsh(netsh::DnsMonitor::new()?))
}
}