summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2023-03-01 11:45:16 +0100
committerDavid Lönnhager <david.l@mullvad.net>2023-03-01 15:06:33 +0100
commitd30f9ef9e83c79107a59406cae5f1cb5a4eae31b (patch)
treefc12232e0643f4d29828f5dfbfa105bd5fe41694
parentc52f733be6b854ecc55de060c1b16cd1ef0ffc53 (diff)
downloadmullvadvpn-d30f9ef9e83c79107a59406cae5f1cb5a4eae31b.tar.xz
mullvadvpn-d30f9ef9e83c79107a59406cae5f1cb5a4eae31b.zip
Fall back on registry method if dnscache is disabled
-rw-r--r--talpid-core/Cargo.toml1
-rw-r--r--talpid-core/src/dns/windows/auto.rs112
-rw-r--r--talpid-core/src/dns/windows/mod.rs17
-rw-r--r--talpid-core/src/dns/windows/tcpip.rs22
4 files changed, 139 insertions, 13 deletions
diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml
index 24203d4a11..285d0c0d1b 100644
--- a/talpid-core/Cargo.toml
+++ b/talpid-core/Cargo.toml
@@ -94,6 +94,7 @@ features = [
"Win32_System_LibraryLoader",
"Win32_System_ProcessStatus",
"Win32_System_Registry",
+ "Win32_System_Rpc",
"Win32_System_Services",
"Win32_System_SystemServices",
"Win32_System_Threading",
diff --git a/talpid-core/src/dns/windows/auto.rs b/talpid-core/src/dns/windows/auto.rs
new file mode 100644
index 0000000000..a09804b700
--- /dev/null
+++ b/talpid-core/src/dns/windows/auto.rs
@@ -0,0 +1,112 @@
+use super::{iphlpapi, netsh, tcpip};
+use crate::dns::DnsMonitorT;
+use windows_sys::Win32::System::Rpc::RPC_S_SERVER_UNAVAILABLE;
+
+pub struct DnsMonitor {
+ current_monitor: InnerMonitor,
+}
+enum InnerMonitor {
+ Iphlpapi(iphlpapi::DnsMonitor),
+ Netsh(netsh::DnsMonitor),
+ Tcpip(tcpip::DnsMonitor),
+}
+
+impl InnerMonitor {
+ fn set(&mut self, interface: &str, servers: &[std::net::IpAddr]) -> Result<(), super::Error> {
+ match self {
+ InnerMonitor::Iphlpapi(monitor) => monitor.set(interface, servers)?,
+ InnerMonitor::Netsh(monitor) => monitor.set(interface, servers)?,
+ InnerMonitor::Tcpip(monitor) => monitor.set(interface, servers)?,
+ }
+ Ok(())
+ }
+
+ fn reset(&mut self) -> Result<(), super::Error> {
+ match self {
+ InnerMonitor::Iphlpapi(monitor) => monitor.reset()?,
+ InnerMonitor::Netsh(monitor) => monitor.reset()?,
+ InnerMonitor::Tcpip(monitor) => monitor.reset()?,
+ }
+ Ok(())
+ }
+
+ fn reset_before_interface_removal(&mut self) -> Result<(), super::Error> {
+ match self {
+ InnerMonitor::Iphlpapi(monitor) => monitor.reset_before_interface_removal()?,
+ InnerMonitor::Netsh(monitor) => monitor.reset_before_interface_removal()?,
+ InnerMonitor::Tcpip(monitor) => monitor.reset_before_interface_removal()?,
+ }
+ Ok(())
+ }
+}
+
+impl DnsMonitorT for DnsMonitor {
+ type Error = super::Error;
+
+ fn new() -> Result<Self, Self::Error> {
+ let current_monitor;
+
+ if iphlpapi::DnsMonitor::is_supported() {
+ current_monitor = InnerMonitor::Iphlpapi(iphlpapi::DnsMonitor::new()?);
+ } else {
+ current_monitor = InnerMonitor::Netsh(netsh::DnsMonitor::new()?);
+ }
+
+ Ok(Self { current_monitor })
+ }
+
+ fn set(&mut self, interface: &str, servers: &[std::net::IpAddr]) -> Result<(), Self::Error> {
+ let result = self.current_monitor.set(interface, servers);
+ if self.fallback_due_to_dnscache(&result) {
+ return self.set(interface, servers);
+ }
+ result
+ }
+
+ fn reset(&mut self) -> Result<(), Self::Error> {
+ let result = self.current_monitor.reset();
+ if self.fallback_due_to_dnscache(&result) {
+ return self.reset();
+ }
+ result
+ }
+
+ fn reset_before_interface_removal(&mut self) -> Result<(), Self::Error> {
+ let result = self.current_monitor.reset_before_interface_removal();
+ if self.fallback_due_to_dnscache(&result) {
+ return self.reset_before_interface_removal();
+ }
+ result
+ }
+}
+
+impl DnsMonitor {
+ fn fallback_due_to_dnscache(&mut self, result: &Result<(), super::Error>) -> bool {
+ let is_dnscache_error = match result {
+ Err(super::Error::Iphlpapi(iphlpapi::Error::SetInterfaceDnsSettings(error))) => {
+ *error == RPC_S_SERVER_UNAVAILABLE
+ }
+ Err(super::Error::Netsh(netsh::Error::NetshError(Some(1)))) => true,
+ _ => false,
+ };
+ if is_dnscache_error {
+ log::warn!("dnscache is not running? Falling back on tcpip method");
+
+ match tcpip::DnsMonitor::new() {
+ Ok(mut tcpip) => {
+ // We need to disable flushing here since it may fail.
+ // Because dnscache is disabled, there's nothing to flush anyhow.
+ tcpip.disable_flushing();
+ self.current_monitor = InnerMonitor::Tcpip(tcpip);
+ true
+ }
+ Err(error) => {
+ log::error!("Failed to init tcpip DNS module: {error}");
+ false
+ }
+ }
+ } else {
+ false
+ }
+ }
+}
diff --git a/talpid-core/src/dns/windows/mod.rs b/talpid-core/src/dns/windows/mod.rs
index baccf2c3d4..4ab9976417 100644
--- a/talpid-core/src/dns/windows/mod.rs
+++ b/talpid-core/src/dns/windows/mod.rs
@@ -2,6 +2,7 @@ use std::{env, fmt, net::IpAddr};
use super::DnsMonitorT;
+mod auto;
mod dnsapi;
mod iphlpapi;
mod netsh;
@@ -37,7 +38,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 => DnsMonitor::detect_appropriate_method()?,
+ Some(_) | None => DnsMonitorHolder::Auto(auto::DnsMonitor::new()?),
};
log::debug!("DNS monitor: {}", inner);
@@ -47,6 +48,7 @@ impl DnsMonitorT for DnsMonitor {
fn set(&mut self, interface: &str, servers: &[IpAddr]) -> Result<(), Error> {
match self.inner {
+ DnsMonitorHolder::Auto(ref mut inner) => inner.set(interface, servers)?,
DnsMonitorHolder::Iphlpapi(ref mut inner) => inner.set(interface, servers)?,
DnsMonitorHolder::Netsh(ref mut inner) => inner.set(interface, servers)?,
DnsMonitorHolder::Tcpip(ref mut inner) => inner.set(interface, servers)?,
@@ -56,6 +58,7 @@ impl DnsMonitorT for DnsMonitor {
fn reset(&mut self) -> Result<(), Error> {
match self.inner {
+ DnsMonitorHolder::Auto(ref mut inner) => inner.reset()?,
DnsMonitorHolder::Iphlpapi(ref mut inner) => inner.reset()?,
DnsMonitorHolder::Netsh(ref mut inner) => inner.reset()?,
DnsMonitorHolder::Tcpip(ref mut inner) => inner.reset()?,
@@ -65,6 +68,7 @@ impl DnsMonitorT for DnsMonitor {
fn reset_before_interface_removal(&mut self) -> Result<(), Error> {
match self.inner {
+ DnsMonitorHolder::Auto(ref mut inner) => inner.reset_before_interface_removal()?,
DnsMonitorHolder::Iphlpapi(ref mut inner) => inner.reset_before_interface_removal()?,
DnsMonitorHolder::Netsh(ref mut inner) => inner.reset_before_interface_removal()?,
DnsMonitorHolder::Tcpip(ref mut inner) => inner.reset_before_interface_removal()?,
@@ -73,16 +77,8 @@ impl DnsMonitorT for DnsMonitor {
}
}
-impl DnsMonitor {
- fn detect_appropriate_method() -> Result<DnsMonitorHolder, Error> {
- if iphlpapi::DnsMonitor::is_supported() {
- return Ok(DnsMonitorHolder::Iphlpapi(iphlpapi::DnsMonitor::new()?));
- }
- Ok(DnsMonitorHolder::Netsh(netsh::DnsMonitor::new()?))
- }
-}
-
enum DnsMonitorHolder {
+ Auto(auto::DnsMonitor),
Iphlpapi(iphlpapi::DnsMonitor),
Netsh(netsh::DnsMonitor),
Tcpip(tcpip::DnsMonitor),
@@ -91,6 +87,7 @@ enum DnsMonitorHolder {
impl fmt::Display for DnsMonitorHolder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
+ DnsMonitorHolder::Auto(_) => f.write_str("auto (iphlpapi > netsh > tcpip)"),
DnsMonitorHolder::Iphlpapi(_) => f.write_str("SetInterfaceDnsSettings (iphlpapi)"),
DnsMonitorHolder::Netsh(_) => f.write_str("netsh"),
DnsMonitorHolder::Tcpip(_) => f.write_str("TCP/IP registry parameter"),
diff --git a/talpid-core/src/dns/windows/tcpip.rs b/talpid-core/src/dns/windows/tcpip.rs
index f7536eaed1..2fdb7e3c28 100644
--- a/talpid-core/src/dns/windows/tcpip.rs
+++ b/talpid-core/src/dns/windows/tcpip.rs
@@ -32,13 +32,17 @@ pub enum Error {
pub struct DnsMonitor {
current_guid: Option<GUID>,
+ should_flush: bool,
}
impl DnsMonitorT for DnsMonitor {
type Error = Error;
fn new() -> Result<Self, Error> {
- Ok(DnsMonitor { current_guid: None })
+ Ok(DnsMonitor {
+ current_guid: None,
+ should_flush: true,
+ })
}
fn set(&mut self, interface: &str, servers: &[IpAddr]) -> Result<(), Error> {
@@ -46,18 +50,30 @@ impl DnsMonitorT for DnsMonitor {
.map_err(Error::InterfaceGuidError)?;
set_dns(&guid, servers)?;
self.current_guid = Some(guid);
- flush_dns_cache()?;
+ if self.should_flush {
+ flush_dns_cache()?;
+ }
Ok(())
}
fn reset(&mut self) -> Result<(), Error> {
if let Some(guid) = self.current_guid.take() {
- return set_dns(&guid, &[]).and(flush_dns_cache());
+ let mut result = set_dns(&guid, &[]);
+ if self.should_flush {
+ result = result.and(flush_dns_cache());
+ }
+ return result;
}
Ok(())
}
}
+impl DnsMonitor {
+ pub fn disable_flushing(&mut self) {
+ self.should_flush = false;
+ }
+}
+
fn set_dns(interface: &GUID, servers: &[IpAddr]) -> Result<(), Error> {
let transaction = Transaction::new().map_err(Error::SetResolversError)?;
let result = match set_dns_inner(&transaction, interface, servers) {