diff options
| author | David Lönnhager <david.l@mullvad.net> | 2023-01-10 11:59:40 +0100 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2023-01-10 11:59:40 +0100 |
| commit | dfbb241b148cf39fa6e4c73644f81b9bd665b7ff (patch) | |
| tree | 6d7cb93c3e909bb3e1937ae614d0722a7796981d | |
| parent | c0b55ed2b99f9dbfd437899be4a99d1c96f55ca4 (diff) | |
| parent | 569542e0db32e2eb17d644f962f121ec5cea8f52 (diff) | |
| download | mullvadvpn-dfbb241b148cf39fa6e4c73644f81b9bd665b7ff.tar.xz mullvadvpn-dfbb241b148cf39fa6e4c73644f81b9bd665b7ff.zip | |
Merge branch 'win-revert-to-netsh'
| -rw-r--r-- | CHANGELOG.md | 2 | ||||
| -rw-r--r-- | README.md | 10 | ||||
| -rw-r--r-- | talpid-core/src/dns/mod.rs | 12 | ||||
| -rw-r--r-- | talpid-core/src/dns/windows/mod.rs | 165 | ||||
| -rw-r--r-- | talpid-core/src/dns/windows/netsh.rs | 218 | ||||
| -rw-r--r-- | talpid-core/src/dns/windows/tcpip.rs | 156 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/connected_state.rs | 2 | ||||
| -rw-r--r-- | talpid-windows-net/src/net.rs | 15 |
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. @@ -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(); |
