diff options
| -rw-r--r-- | talpid-core/Cargo.toml | 1 | ||||
| -rw-r--r-- | talpid-core/src/dns/windows/auto.rs | 112 | ||||
| -rw-r--r-- | talpid-core/src/dns/windows/mod.rs | 17 | ||||
| -rw-r--r-- | talpid-core/src/dns/windows/tcpip.rs | 22 |
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) { |
