summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2023-01-10 11:59:40 +0100
committerDavid Lönnhager <david.l@mullvad.net>2023-01-10 11:59:40 +0100
commitdfbb241b148cf39fa6e4c73644f81b9bd665b7ff (patch)
tree6d7cb93c3e909bb3e1937ae614d0722a7796981d
parentc0b55ed2b99f9dbfd437899be4a99d1c96f55ca4 (diff)
parent569542e0db32e2eb17d644f962f121ec5cea8f52 (diff)
downloadmullvadvpn-dfbb241b148cf39fa6e4c73644f81b9bd665b7ff.tar.xz
mullvadvpn-dfbb241b148cf39fa6e4c73644f81b9bd665b7ff.zip
Merge branch 'win-revert-to-netsh'
-rw-r--r--CHANGELOG.md2
-rw-r--r--README.md10
-rw-r--r--talpid-core/src/dns/mod.rs12
-rw-r--r--talpid-core/src/dns/windows/mod.rs165
-rw-r--r--talpid-core/src/dns/windows/netsh.rs218
-rw-r--r--talpid-core/src/dns/windows/tcpip.rs156
-rw-r--r--talpid-core/src/tunnel_state_machine/connected_state.rs2
-rw-r--r--talpid-windows-net/src/net.rs15
8 files changed, 452 insertions, 128 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b82df24ca4..56f71d2774 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -51,6 +51,8 @@ Line wrap the file at 100 chars. Th
#### Windows
- Ignore adapters that have no valid GUID when removing obsolete Wintun interfaces during install.
Previously, the installer would abort.
+- Revert to using netsh for DNS config, as some Windows builds did not deal with changes correctly.
+ `TALPID_DNS_MODULE` can be used to override this.
### Changed
- Update Electron from 19.0.13 to 21.1.1.
diff --git a/README.md b/README.md
index 64e7992694..3e590dc13d 100644
--- a/README.md
+++ b/README.md
@@ -128,14 +128,20 @@ See [this](Release.md) for instructions on how to make a new release.
that will be receiving relay traffic, and `src_valid_mark` is not set to `1`, the daemon will
not be able to receive relay traffic.
-* `TALPID_DNS_MODULE` - Allows changing the method that will be used for DNS configuration on Linux.
+* `TALPID_DNS_MODULE` - Allows changing the method that will be used for DNS configuration.
By default this is automatically detected, but you can set it to one of the options below to
- choose a specific method:
+ choose a specific method.
+
+ * Linux
* `"static-file"`: change the `/etc/resolv.conf` file directly
* `"resolvconf"`: use the `resolvconf` program
* `"systemd"`: use systemd's `resolved` service through DBus
* `"network-manager"`: use `NetworkManager` service through DBus
+ * Windows
+ * `netsh`: use the `netsh` program
+ * `tcpip`: set TCP/IP parameters in the registry
+
* `TALPID_FORCE_USERSPACE_WIREGUARD` - Forces the daemon to use the userspace implementation of
WireGuard on Linux.
diff --git a/talpid-core/src/dns/mod.rs b/talpid-core/src/dns/mod.rs
index ca06251f55..39660078c3 100644
--- a/talpid-core/src/dns/mod.rs
+++ b/talpid-core/src/dns/mod.rs
@@ -79,6 +79,14 @@ impl DnsMonitor {
log::info!("Resetting DNS");
self.inner.reset()
}
+
+ /// Reset DNS settings to what they were before being set by this instance.
+ /// If the settings only affect a specific interface, this can be a no-op,
+ /// as the interface will be destroyed.
+ pub fn reset_before_interface_removal(&mut self) -> Result<(), Error> {
+ log::info!("Resetting DNS");
+ self.inner.reset_before_interface_removal()
+ }
}
trait DnsMonitorT: Sized {
@@ -93,4 +101,8 @@ trait DnsMonitorT: Sized {
fn set(&mut self, interface: &str, servers: &[IpAddr]) -> Result<(), Self::Error>;
fn reset(&mut self) -> Result<(), Self::Error>;
+
+ fn reset_before_interface_removal(&mut self) -> Result<(), Self::Error> {
+ self.reset()
+ }
}
diff --git a/talpid-core/src/dns/windows/mod.rs b/talpid-core/src/dns/windows/mod.rs
index 9b780b1f54..10ce85c055 100644
--- a/talpid-core/src/dns/windows/mod.rs
+++ b/talpid-core/src/dns/windows/mod.rs
@@ -1,157 +1,76 @@
-use std::{io, net::IpAddr};
-use talpid_types::ErrorExt;
-use talpid_windows_net::{guid_from_luid, luid_from_alias};
-use windows_sys::{core::GUID, Win32::System::Com::StringFromGUID2};
-use winreg::{
- enums::{HKEY_LOCAL_MACHINE, KEY_SET_VALUE},
- transaction::Transaction,
- RegKey,
-};
+use std::{env, fmt, net::IpAddr};
mod dnsapi;
+mod netsh;
+mod tcpip;
/// Errors that can happen when configuring DNS on Windows.
#[derive(err_derive::Error, Debug)]
-#[error(no_from)]
pub enum Error {
- /// Failure to obtain an interface LUID given an alias.
- #[error(display = "Failed to obtain LUID for the interface alias")]
- InterfaceLuidError(#[error(source)] io::Error),
+ /// Failed to set DNS config using the netsh module.
+ #[error(display = "Error in netsh module")]
+ Netsh(#[error(source)] netsh::Error),
- /// Failure to obtain an interface GUID.
- #[error(display = "Failed to obtain GUID for the interface")]
- InterfaceGuidError(#[error(source)] io::Error),
-
- /// Failure to flush DNS cache.
- #[error(display = "Failed to flush DNS resolver cache")]
- FlushResolverCacheError(#[error(source)] dnsapi::Error),
-
- /// Failed to update DNS servers for interface.
- #[error(display = "Failed to update interface DNS servers")]
- SetResolversError(#[error(source)] io::Error),
+ /// Failed to set DNS config using the tcpip module.
+ #[error(display = "Error in tcpip module")]
+ Tcpip(#[error(source)] tcpip::Error),
}
pub struct DnsMonitor {
- current_guid: Option<GUID>,
+ inner: DnsMonitorHolder,
}
impl super::DnsMonitorT for DnsMonitor {
type Error = Error;
fn new() -> Result<Self, Error> {
- Ok(DnsMonitor { current_guid: None })
+ let dns_module = env::var_os("TALPID_DNS_MODULE");
+
+ let inner = match dns_module.as_ref().and_then(|value| value.to_str()) {
+ Some("tcpip") => DnsMonitorHolder::Tcpip(tcpip::DnsMonitor::new()?),
+ Some(_) | None => DnsMonitorHolder::Netsh(netsh::DnsMonitor::new()?),
+ };
+
+ log::debug!("DNS monitor: {}", inner);
+
+ Ok(DnsMonitor { inner })
}
fn set(&mut self, interface: &str, servers: &[IpAddr]) -> Result<(), Error> {
- let guid = guid_from_luid(&luid_from_alias(interface).map_err(Error::InterfaceLuidError)?)
- .map_err(Error::InterfaceGuidError)?;
- set_dns(&guid, servers)?;
- self.current_guid = Some(guid);
- flush_dns_cache()?;
+ match self.inner {
+ DnsMonitorHolder::Netsh(ref mut inner) => inner.set(interface, servers)?,
+ DnsMonitorHolder::Tcpip(ref mut inner) => inner.set(interface, servers)?,
+ }
Ok(())
}
fn reset(&mut self) -> Result<(), Error> {
- if let Some(guid) = self.current_guid.take() {
- return set_dns(&guid, &[]).and(flush_dns_cache());
+ match self.inner {
+ DnsMonitorHolder::Netsh(ref mut inner) => inner.reset()?,
+ DnsMonitorHolder::Tcpip(ref mut inner) => inner.reset()?,
}
Ok(())
}
-}
-
-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) {
- Ok(()) => transaction.commit(),
- Err(error) => transaction.rollback().and(Err(error)),
- };
- result.map_err(Error::SetResolversError)
-}
-
-fn set_dns_inner(
- transaction: &Transaction,
- interface: &GUID,
- servers: &[IpAddr],
-) -> io::Result<()> {
- let guid_str = string_from_guid(interface);
-
- config_interface(
- transaction,
- &guid_str,
- "Tcpip",
- servers.iter().filter(|addr| addr.is_ipv4()),
- )?;
- config_interface(
- transaction,
- &guid_str,
- "Tcpip6",
- servers.iter().filter(|addr| addr.is_ipv6()),
- )?;
-
- Ok(())
-}
-
-fn config_interface<'a>(
- transaction: &Transaction,
- guid: &str,
- service: &str,
- nameservers: impl Iterator<Item = &'a IpAddr>,
-) -> io::Result<()> {
- let nameservers = nameservers
- .map(|addr| addr.to_string())
- .collect::<Vec<String>>();
-
- let reg_path =
- format!(r#"SYSTEM\CurrentControlSet\Services\{service}\Parameters\Interfaces\{guid}"#,);
- let adapter_key = match RegKey::predef(HKEY_LOCAL_MACHINE).open_subkey_transacted_with_flags(
- reg_path,
- transaction,
- KEY_SET_VALUE,
- ) {
- Ok(adapter_key) => Ok(adapter_key),
- Err(error) => {
- if nameservers.is_empty() && error.kind() == io::ErrorKind::NotFound {
- return Ok(());
- }
- Err(error)
+ fn reset_before_interface_removal(&mut self) -> Result<(), Error> {
+ match self.inner {
+ DnsMonitorHolder::Netsh(ref mut inner) => inner.reset_before_interface_removal()?,
+ DnsMonitorHolder::Tcpip(ref mut inner) => inner.reset_before_interface_removal()?,
}
- }?;
-
- if !nameservers.is_empty() {
- adapter_key.set_value("NameServer", &nameservers.join(","))?;
- } else {
- adapter_key.delete_value("NameServer").or_else(|error| {
- if error.kind() == io::ErrorKind::NotFound {
- Ok(())
- } else {
- Err(error)
- }
- })?;
- }
-
- // Try to disable LLMNR on the interface
- if let Err(error) = adapter_key.set_value("EnableMulticast", &0u32) {
- log::error!(
- "{}\nService: {service}",
- error.display_chain_with_msg("Failed to disable LLMNR on the tunnel interface")
- );
+ Ok(())
}
-
- Ok(())
}
-fn flush_dns_cache() -> Result<(), Error> {
- dnsapi::flush_resolver_cache().map_err(Error::FlushResolverCacheError)
+enum DnsMonitorHolder {
+ Netsh(netsh::DnsMonitor),
+ Tcpip(tcpip::DnsMonitor),
}
-/// Obtain a string representation for a GUID object.
-fn string_from_guid(guid: &GUID) -> String {
- let mut buffer = [0u16; 40];
- let length = unsafe { StringFromGUID2(guid, &mut buffer[0] as *mut _, buffer.len() as i32 - 1) }
- as usize;
- // cannot fail because `buffer` is large enough
- assert!(length > 0);
- let length = length - 1;
- String::from_utf16(&buffer[0..length]).unwrap()
+impl fmt::Display for DnsMonitorHolder {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ DnsMonitorHolder::Netsh(_) => f.write_str("netsh"),
+ DnsMonitorHolder::Tcpip(_) => f.write_str("TCP/IP registry parameter"),
+ }
+ }
}
diff --git a/talpid-core/src/dns/windows/netsh.rs b/talpid-core/src/dns/windows/netsh.rs
new file mode 100644
index 0000000000..7c0450f855
--- /dev/null
+++ b/talpid-core/src/dns/windows/netsh.rs
@@ -0,0 +1,218 @@
+use crate::dns::DnsMonitorT;
+use std::{
+ ffi::OsString,
+ io::{self, Write},
+ net::IpAddr,
+ os::windows::prelude::{AsRawHandle, OsStringExt},
+ path::PathBuf,
+ process::{Child, Command, ExitStatus, Stdio},
+ time::Duration,
+};
+use talpid_types::{net::IpVersion, ErrorExt};
+use talpid_windows_net::{index_from_luid, luid_from_alias};
+use windows_sys::Win32::{
+ Foundation::{MAX_PATH, WAIT_OBJECT_0, WAIT_TIMEOUT},
+ System::{
+ SystemInformation::GetSystemDirectoryW, Threading::WaitForSingleObject,
+ WindowsProgramming::INFINITE,
+ },
+};
+
+const NETSH_TIMEOUT: Duration = Duration::from_secs(10);
+
+/// Errors that can happen when configuring DNS on Windows.
+#[derive(err_derive::Error, Debug)]
+#[error(no_from)]
+pub enum Error {
+ /// Failure to obtain an interface LUID given an alias.
+ #[error(display = "Failed to obtain LUID for the interface alias")]
+ InterfaceLuidError(#[error(source)] io::Error),
+
+ /// Failure to obtain an interface index.
+ #[error(display = "Failed to obtain index of the interface")]
+ InterfaceIndexError(#[error(source)] io::Error),
+
+ /// Failure to spawn netsh subprocess.
+ #[error(display = "Failed to spawn 'netsh'")]
+ SpawnNetsh(#[error(source)] io::Error),
+
+ /// Failure to spawn netsh subprocess.
+ #[error(display = "Failed to obtain system directory")]
+ GetSystemDir(#[error(source)] io::Error),
+
+ /// Failure to write to stdin.
+ #[error(display = "Failed to write to stdin for 'netsh'")]
+ NetshInput(#[error(source)] io::Error),
+
+ /// Failure to wait for netsh result.
+ #[error(display = "Failed to wait for 'netsh'")]
+ WaitNetsh(#[error(source)] io::Error),
+
+ /// netsh returned a non-zero status.
+ #[error(display = "'netsh' returned an error: {:?}", _0)]
+ NetshError(Option<i32>),
+
+ /// netsh did not return in a timely manner.
+ #[error(display = "'netsh' took too long to complete")]
+ NetshTimeout,
+}
+
+pub struct DnsMonitor {
+ current_index: Option<u32>,
+}
+
+impl DnsMonitorT for DnsMonitor {
+ type Error = Error;
+
+ fn new() -> Result<Self, Error> {
+ Ok(DnsMonitor {
+ current_index: None,
+ })
+ }
+
+ fn set(&mut self, interface: &str, servers: &[IpAddr]) -> Result<(), Error> {
+ let interface_luid = luid_from_alias(interface).map_err(Error::InterfaceLuidError)?;
+ let interface_index =
+ index_from_luid(&interface_luid).map_err(Error::InterfaceIndexError)?;
+
+ self.current_index = Some(interface_index);
+
+ let mut added_ipv4_server = false;
+ let mut added_ipv6_server = false;
+
+ let mut netsh_input = String::new();
+
+ for server in servers {
+ let is_additional_server;
+
+ if server.is_ipv4() {
+ is_additional_server = added_ipv4_server;
+ added_ipv4_server = true;
+ } else {
+ is_additional_server = added_ipv6_server;
+ added_ipv6_server = true;
+ };
+
+ if is_additional_server {
+ netsh_input.push_str(&create_netsh_add_command(interface_index, server));
+ } else {
+ netsh_input.push_str(&create_netsh_set_command(interface_index, server));
+ }
+ }
+
+ if !added_ipv4_server {
+ netsh_input.push_str(&create_netsh_flush_command(interface_index, IpVersion::V4));
+ }
+ if !added_ipv6_server {
+ netsh_input.push_str(&create_netsh_flush_command(interface_index, IpVersion::V6));
+ }
+
+ run_netsh_with_timeout(netsh_input, NETSH_TIMEOUT)?;
+
+ Ok(())
+ }
+
+ fn reset(&mut self) -> Result<(), Error> {
+ if let Some(index) = self.current_index.take() {
+ let mut netsh_input = String::new();
+ netsh_input.push_str(&create_netsh_flush_command(index, IpVersion::V4));
+ netsh_input.push_str(&create_netsh_flush_command(index, IpVersion::V6));
+
+ if let Err(error) = run_netsh_with_timeout(netsh_input, NETSH_TIMEOUT) {
+ log::error!("{}", error.display_chain_with_msg("Failed to reset DNS"));
+ }
+ }
+ Ok(())
+ }
+
+ fn reset_before_interface_removal(&mut self) -> Result<(), Self::Error> {
+ // do nothing since the tunnel interface goes away
+ let _ = self.current_index.take();
+ Ok(())
+ }
+}
+
+fn run_netsh_with_timeout(netsh_input: String, timeout: Duration) -> Result<(), Error> {
+ log::debug!("running netsh:\n{}", netsh_input);
+
+ let sysdir = get_system_dir().map_err(Error::GetSystemDir)?;
+ let mut netsh = Command::new(sysdir.join(r"netsh.exe"));
+
+ let mut subproc = netsh
+ .stdin(Stdio::piped())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .map_err(Error::SpawnNetsh)?;
+
+ let mut stdin = subproc.stdin.take().unwrap();
+ stdin
+ .write_all(netsh_input.as_bytes())
+ .map_err(Error::NetshInput)?;
+ drop(stdin);
+
+ match wait_for_child(&mut subproc, timeout) {
+ Ok(Some(status)) => {
+ if !status.success() {
+ return Err(Error::NetshError(status.code()));
+ }
+ Ok(())
+ }
+ Ok(None) => {
+ let _ = subproc.kill();
+ Err(Error::NetshTimeout)
+ }
+ Err(error) => Err(Error::WaitNetsh(error)),
+ }
+}
+
+fn wait_for_child(subproc: &mut Child, timeout: Duration) -> io::Result<Option<ExitStatus>> {
+ let dur_millis = u32::try_from(timeout.as_millis()).unwrap_or(INFINITE);
+
+ let subproc_handle = subproc.as_raw_handle();
+ match unsafe { WaitForSingleObject(subproc_handle as isize, dur_millis) } {
+ WAIT_OBJECT_0 => subproc.try_wait(),
+ WAIT_TIMEOUT => Ok(None),
+ _error => Err(io::Error::last_os_error()),
+ }
+}
+
+fn create_netsh_set_command(interface_index: u32, server: &IpAddr) -> String {
+ // Set primary DNS server:
+ // netsh interface ipv4 set dnsservers name="Mullvad" source=static address=10.64.0.1
+ // validate=no
+
+ let interface_type = if server.is_ipv4() { "ipv4" } else { "ipv6" };
+ format!("interface {interface_type} set dnsservers name={interface_index} source=static address={server} validate=no\r\n")
+}
+
+fn create_netsh_add_command(interface_index: u32, server: &IpAddr) -> String {
+ // Add DNS server:
+ // netsh interface ipv4 add dnsservers name="Mullvad" address=10.64.0.2 validate=no
+
+ let interface_type = if server.is_ipv4() { "ipv4" } else { "ipv6" };
+ format!("interface {interface_type} add dnsservers name={interface_index} address={server} validate=no\r\n")
+}
+
+fn create_netsh_flush_command(interface_index: u32, ip_version: IpVersion) -> String {
+ // Flush DNS settings:
+ // netsh interface ipv4 set dnsservers name="Mullvad" source=static address=none validate=no
+
+ let interface_type = match ip_version {
+ IpVersion::V4 => "ipv4",
+ IpVersion::V6 => "ipv6",
+ };
+
+ format!("interface {interface_type} set dnsservers name={interface_index} source=static address=none validate=no\r\n")
+}
+
+fn get_system_dir() -> io::Result<PathBuf> {
+ let mut sysdir = [0u16; MAX_PATH as usize + 1];
+ let len = unsafe { GetSystemDirectoryW(sysdir.as_mut_ptr(), (sysdir.len() - 1) as u32) };
+ if len == 0 {
+ return Err(io::Error::last_os_error());
+ }
+ Ok(PathBuf::from(OsString::from_wide(
+ &sysdir[0..(len as usize)],
+ )))
+}
diff --git a/talpid-core/src/dns/windows/tcpip.rs b/talpid-core/src/dns/windows/tcpip.rs
new file mode 100644
index 0000000000..f7536eaed1
--- /dev/null
+++ b/talpid-core/src/dns/windows/tcpip.rs
@@ -0,0 +1,156 @@
+use crate::dns::DnsMonitorT;
+use std::{io, net::IpAddr};
+use talpid_types::ErrorExt;
+use talpid_windows_net::{guid_from_luid, luid_from_alias};
+use windows_sys::{core::GUID, Win32::System::Com::StringFromGUID2};
+use winreg::{
+ enums::{HKEY_LOCAL_MACHINE, KEY_SET_VALUE},
+ transaction::Transaction,
+ RegKey,
+};
+
+/// Errors that can happen when configuring DNS on Windows.
+#[derive(err_derive::Error, Debug)]
+#[error(no_from)]
+pub enum Error {
+ /// Failure to obtain an interface LUID given an alias.
+ #[error(display = "Failed to obtain LUID for the interface alias")]
+ InterfaceLuidError(#[error(source)] io::Error),
+
+ /// Failure to obtain an interface GUID.
+ #[error(display = "Failed to obtain GUID for the interface")]
+ InterfaceGuidError(#[error(source)] io::Error),
+
+ /// Failure to flush DNS cache.
+ #[error(display = "Failed to flush DNS resolver cache")]
+ FlushResolverCacheError(#[error(source)] super::dnsapi::Error),
+
+ /// Failed to update DNS servers for interface.
+ #[error(display = "Failed to update interface DNS servers")]
+ SetResolversError(#[error(source)] io::Error),
+}
+
+pub struct DnsMonitor {
+ current_guid: Option<GUID>,
+}
+
+impl DnsMonitorT for DnsMonitor {
+ type Error = Error;
+
+ fn new() -> Result<Self, Error> {
+ Ok(DnsMonitor { current_guid: None })
+ }
+
+ fn set(&mut self, interface: &str, servers: &[IpAddr]) -> Result<(), Error> {
+ let guid = guid_from_luid(&luid_from_alias(interface).map_err(Error::InterfaceLuidError)?)
+ .map_err(Error::InterfaceGuidError)?;
+ set_dns(&guid, servers)?;
+ self.current_guid = Some(guid);
+ 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());
+ }
+ Ok(())
+ }
+}
+
+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) {
+ Ok(()) => transaction.commit(),
+ Err(error) => transaction.rollback().and(Err(error)),
+ };
+ result.map_err(Error::SetResolversError)
+}
+
+fn set_dns_inner(
+ transaction: &Transaction,
+ interface: &GUID,
+ servers: &[IpAddr],
+) -> io::Result<()> {
+ let guid_str = string_from_guid(interface);
+
+ config_interface(
+ transaction,
+ &guid_str,
+ "Tcpip",
+ servers.iter().filter(|addr| addr.is_ipv4()),
+ )?;
+
+ config_interface(
+ transaction,
+ &guid_str,
+ "Tcpip6",
+ servers.iter().filter(|addr| addr.is_ipv6()),
+ )?;
+
+ Ok(())
+}
+
+fn config_interface<'a>(
+ transaction: &Transaction,
+ guid: &str,
+ service: &str,
+ nameservers: impl Iterator<Item = &'a IpAddr>,
+) -> io::Result<()> {
+ let nameservers = nameservers
+ .map(|addr| addr.to_string())
+ .collect::<Vec<String>>();
+
+ let reg_path =
+ format!(r#"SYSTEM\CurrentControlSet\Services\{service}\Parameters\Interfaces\{guid}"#,);
+ let adapter_key = match RegKey::predef(HKEY_LOCAL_MACHINE).open_subkey_transacted_with_flags(
+ reg_path,
+ transaction,
+ KEY_SET_VALUE,
+ ) {
+ Ok(adapter_key) => Ok(adapter_key),
+ Err(error) => {
+ if nameservers.is_empty() && error.kind() == io::ErrorKind::NotFound {
+ return Ok(());
+ }
+ Err(error)
+ }
+ }?;
+
+ if !nameservers.is_empty() {
+ adapter_key.set_value("NameServer", &nameservers.join(","))?;
+ } else {
+ adapter_key.delete_value("NameServer").or_else(|error| {
+ if error.kind() == io::ErrorKind::NotFound {
+ Ok(())
+ } else {
+ Err(error)
+ }
+ })?;
+ }
+
+ // Try to disable LLMNR on the interface
+ if let Err(error) = adapter_key.set_value("EnableMulticast", &0u32) {
+ log::error!(
+ "{}\nService: {service}",
+ error.display_chain_with_msg("Failed to disable LLMNR on the tunnel interface")
+ );
+ }
+
+ Ok(())
+}
+
+fn flush_dns_cache() -> Result<(), Error> {
+ super::dnsapi::flush_resolver_cache().map_err(Error::FlushResolverCacheError)
+}
+
+/// Obtain a string representation for a GUID object.
+fn string_from_guid(guid: &GUID) -> String {
+ let mut buffer = [0u16; 40];
+ let length = unsafe { StringFromGUID2(guid, &mut buffer[0] as *mut _, buffer.len() as i32 - 1) }
+ as usize;
+ // cannot fail because `buffer` is large enough
+ assert!(length > 0);
+ let length = length - 1;
+ String::from_utf16(&buffer[0..length]).unwrap()
+}
diff --git a/talpid-core/src/tunnel_state_machine/connected_state.rs b/talpid-core/src/tunnel_state_machine/connected_state.rs
index 5fc02d1052..de9f43335e 100644
--- a/talpid-core/src/tunnel_state_machine/connected_state.rs
+++ b/talpid-core/src/tunnel_state_machine/connected_state.rs
@@ -141,7 +141,7 @@ impl ConnectedState {
}
fn reset_dns(shared_values: &mut SharedTunnelStateValues) {
- if let Err(error) = shared_values.dns_monitor.reset() {
+ if let Err(error) = shared_values.dns_monitor.reset_before_interface_removal() {
log::error!("{}", error.display_chain_with_msg("Unable to reset DNS"));
}
}
diff --git a/talpid-windows-net/src/net.rs b/talpid-windows-net/src/net.rs
index 6a11cf7018..18a3f7748c 100644
--- a/talpid-windows-net/src/net.rs
+++ b/talpid-windows-net/src/net.rs
@@ -17,8 +17,9 @@ use windows_sys::{
NetworkManagement::{
IpHelper::{
CancelMibChangeNotify2, ConvertInterfaceAliasToLuid, ConvertInterfaceLuidToAlias,
- ConvertInterfaceLuidToGuid, CreateUnicastIpAddressEntry, FreeMibTable,
- GetIpInterfaceEntry, GetUnicastIpAddressEntry, GetUnicastIpAddressTable,
+ ConvertInterfaceLuidToGuid, ConvertInterfaceLuidToIndex,
+ CreateUnicastIpAddressEntry, FreeMibTable, GetIpInterfaceEntry,
+ GetUnicastIpAddressEntry, GetUnicastIpAddressTable,
InitializeUnicastIpAddressEntry, MibAddInstance, NotifyIpInterfaceChange,
SetIpInterfaceEntry, MIB_IPINTERFACE_ROW, MIB_UNICASTIPADDRESS_ROW,
MIB_UNICASTIPADDRESS_TABLE,
@@ -377,6 +378,16 @@ pub fn get_unicast_table(
Ok(unicast_rows)
}
+/// Returns the index of a network interface given its LUID.
+pub fn index_from_luid(luid: &NET_LUID_LH) -> io::Result<u32> {
+ let mut index = 0u32;
+ let status = unsafe { ConvertInterfaceLuidToIndex(luid, &mut index) };
+ if status != NO_ERROR as i32 {
+ return Err(io::Error::from_raw_os_error(status as i32));
+ }
+ Ok(index)
+}
+
/// Returns the GUID of a network interface given its LUID.
pub fn guid_from_luid(luid: &NET_LUID_LH) -> io::Result<GUID> {
let mut guid = MaybeUninit::zeroed();