diff --git a/src/linux/init/GnsEngine.cpp b/src/linux/init/GnsEngine.cpp index 37ee355a8..e0af19028 100644 --- a/src/linux/init/GnsEngine.cpp +++ b/src/linux/init/GnsEngine.cpp @@ -315,21 +315,17 @@ void GnsEngine::ProcessDNSChange(Interface& interface, const wsl::shared::hns::D content << L"nameserver " << server << L"\n"; } - if (!payload.Domain.empty()) - { - content << L"domain " << payload.Domain << L"\n"; - } - + // Use 'search' for DNS suffixes. + // Per resolv.conf(5): "The domain directive is an obsolete name for the search directive + // that handles one search list entry only." + // See: https://man7.org/linux/man-pages/man5/resolv.conf.5.html if (!payload.Search.empty()) { content << L"search " << wsl::shared::string::Join(wsl::shared::string::Split(payload.Search, L','), L' ') << L"\n"; } GNS_LOG_INFO( - "Setting DNS server domain to {}: {} on interfaceName {} ", - payload.Domain.c_str(), - content.str().c_str(), - interface.Name().c_str()); + "Setting DNS search to {}: {} on interfaceName {} ", payload.Search.c_str(), content.str().c_str(), interface.Name().c_str()); std::wofstream resolvConf; resolvConf.exceptions(std::ofstream::badbit | std::ofstream::failbit); diff --git a/src/windows/common/NatNetworking.cpp b/src/windows/common/NatNetworking.cpp index 4c39fe747..04f3db9a3 100644 --- a/src/windows/common/NatNetworking.cpp +++ b/src/windows/common/NatNetworking.cpp @@ -26,8 +26,13 @@ static wil::srwlock g_endpointsInUseLock; static std::vector g_endpointsInUse; NatNetworking::NatNetworking( - HCS_SYSTEM system, wsl::windows::common::hcs::unique_hcn_network&& network, GnsChannel&& gnsChannel, Config& config, wil::unique_socket&& dnsHvsocket) : - m_system(system), m_config(config), m_network(std::move(network)), m_gnsChannel(std::move(gnsChannel)) + HCS_SYSTEM system, + wsl::windows::common::hcs::unique_hcn_network&& network, + GnsChannel&& gnsChannel, + Config& config, + wil::unique_socket&& dnsHvsocket, + LPCWSTR dnsOptions) : + m_system(system), m_config(config), m_network(std::move(network)), m_dnsOptions(dnsOptions), m_gnsChannel(std::move(gnsChannel)) { m_connectivityTelemetryEnabled = config.EnableTelemetry && !WslTraceLoggingShouldDisableTelemetry(); @@ -48,7 +53,7 @@ NatNetworking::NatNetworking( // prioritized means: // - can only set 3 DNS servers (Linux limitation) // - when there are multiple host connected interfaces, we need to use the DNS servers from the most-likely-to-be-used interface on the host - m_mirrorDnsInfo.emplace(); + m_useMirrorDnsSettings = true; } } @@ -337,7 +342,7 @@ void NatNetworking::Initialize() UpdateDns(endpointProperties.GatewayAddress.c_str()); // if using the shared access DNS proxy, ensure that the shared access service is allowed inbound UDP access. - if (!m_mirrorDnsInfo && !m_dnsTunnelingResolver) + if (!m_useMirrorDnsSettings && !m_dnsTunnelingResolver) { // N.B. This rule works around a host OS issue that prevents the DNS proxy from working on older versions of Windows. ConfigureSharedAccessFirewallRule(); @@ -433,35 +438,22 @@ _Requires_lock_held_(m_lock) void NatNetworking::UpdateDns(std::optional gatewayAddress) noexcept try { - if (!m_dnsTunnelingResolver && !m_mirrorDnsInfo && !gatewayAddress) + if (!m_dnsTunnelingResolver && !m_useMirrorDnsSettings && !gatewayAddress) { return; } networking::DnsInfo latestDnsSettings{}; - // true if the "domain" entry of /etc/resolv.conf should be configured - // Note: the "domain" entry allows a single DNS suffix to be configured - bool configureLinuxDomain = false; - // NAT mode with DNS tunneling if (m_dnsTunnelingResolver) { latestDnsSettings = HostDnsInfo::GetDnsTunnelingSettings(m_dnsTunnelingIpAddress); } // NAT mode without Shared Access DNS proxy - else if (m_mirrorDnsInfo) + else if (m_useMirrorDnsSettings) { - m_mirrorDnsInfo->UpdateNetworkInformation(); - const auto settings = m_mirrorDnsInfo->GetDnsSettings(DnsSettingsFlags::IncludeVpn); - - latestDnsSettings.Servers = std::move(settings.Servers); - - if (!settings.Domains.empty()) - { - latestDnsSettings.Domains.emplace_back(std::move(settings.Domains.front())); - configureLinuxDomain = true; - } + latestDnsSettings = HostDnsInfo::GetDnsSettings(DnsSettingsFlags::IncludeVpn); } // NAT mode with Shared Access DNS proxy else if (gatewayAddress) @@ -472,11 +464,10 @@ try if (latestDnsSettings != m_trackedDnsSettings) { - auto dnsNotification = BuildDnsNotification(latestDnsSettings, configureLinuxDomain); + auto dnsNotification = BuildDnsNotification(latestDnsSettings, m_dnsOptions); WSL_LOG( "NatNetworking::UpdateDns", - TraceLoggingValue(dnsNotification.Domain.c_str(), "domain"), TraceLoggingValue(dnsNotification.Options.c_str(), "options"), TraceLoggingValue(dnsNotification.Search.c_str(), "search"), TraceLoggingValue(dnsNotification.ServerList.c_str(), "serverList")); diff --git a/src/windows/common/NatNetworking.h b/src/windows/common/NatNetworking.h index 14e8cf5cb..54e761502 100644 --- a/src/windows/common/NatNetworking.h +++ b/src/windows/common/NatNetworking.h @@ -18,7 +18,13 @@ namespace wsl::core { class NatNetworking final : public INetworkingEngine { public: - NatNetworking(HCS_SYSTEM system, wsl::windows::common::hcs::unique_hcn_network&& network, GnsChannel&& gnsChannel, Config& config, wil::unique_socket&& dnsHvsocket); + NatNetworking( + HCS_SYSTEM system, + wsl::windows::common::hcs::unique_hcn_network&& network, + GnsChannel&& gnsChannel, + Config& config, + wil::unique_socket&& dnsHvsocket, + LPCWSTR dnsOptions = LX_INIT_RESOLVCONF_FULL_HEADER); ~NatNetworking() override; // Note: This class cannot be moved because m_networkNotifyHandle captures a 'this' pointer. @@ -69,12 +75,18 @@ class NatNetworking final : public INetworkingEngine // The latest DNS settings configured in Linux _Guarded_by_(m_lock) networking::DnsInfo m_trackedDnsSettings {}; + // If true, DNS settings are retrieved from host adapters (mirrored mode) + // rather than using the shared access DNS proxy + bool m_useMirrorDnsSettings = false; + + // Options/header for /etc/resolv.conf + LPCWSTR m_dnsOptions = nullptr; + GnsChannel m_gnsChannel; std::shared_ptr m_networkSettings; networking::EphemeralHcnEndpoint m_endpoint; ULONG m_networkMtu = 0; - std::optional m_mirrorDnsInfo; networking::unique_notify_handle m_networkNotifyHandle{}; }; diff --git a/src/windows/common/VirtioNetworking.cpp b/src/windows/common/VirtioNetworking.cpp index 64d55ef16..a94ce9099 100644 --- a/src/windows/common/VirtioNetworking.cpp +++ b/src/windows/common/VirtioNetworking.cpp @@ -14,11 +14,12 @@ using wsl::core::VirtioNetworking; static constexpr auto c_loopbackDeviceName = TEXT(LX_INIT_LOOPBACK_DEVICE_NAME); VirtioNetworking::VirtioNetworking( - GnsChannel&& gnsChannel, bool enableLocalhostRelay, std::shared_ptr guestDeviceManager, wil::shared_handle userToken) : + GnsChannel&& gnsChannel, bool enableLocalhostRelay, LPCWSTR dnsOptions, std::shared_ptr guestDeviceManager, wil::shared_handle userToken) : m_guestDeviceManager(std::move(guestDeviceManager)), m_userToken(std::move(userToken)), m_gnsChannel(std::move(gnsChannel)), - m_enableLocalhostRelay(enableLocalhostRelay) + m_enableLocalhostRelay(enableLocalhostRelay), + m_dnsOptions(dnsOptions) { } @@ -66,7 +67,7 @@ void VirtioNetworking::Initialize() } // Get initial DNS settings for device options. - auto initialDns = m_dnsUpdateHelper.GetCurrentDnsSettings(networking::DnsSettingsFlags::IncludeVpn); + auto initialDns = networking::HostDnsInfo::GetDnsSettings(networking::DnsSettingsFlags::IncludeVpn); if (!initialDns.Servers.empty()) { if (device_options.tellp() > 0) @@ -260,7 +261,7 @@ try UpdateMtu(); // Check for DNS changes and send update if needed. - auto currentDns = m_dnsUpdateHelper.GetCurrentDnsSettings(networking::DnsSettingsFlags::IncludeVpn); + auto currentDns = networking::HostDnsInfo::GetDnsSettings(networking::DnsSettingsFlags::IncludeVpn); if (currentDns != m_trackedDnsSettings) { m_trackedDnsSettings = currentDns; @@ -274,7 +275,7 @@ void VirtioNetworking::SendDnsUpdate(const networking::DnsInfo& dnsSettings) hns::ModifyGuestEndpointSettingRequest notification{}; notification.RequestType = hns::ModifyRequestType::Update; notification.ResourceType = hns::GuestEndpointResourceType::DNS; - notification.Settings = networking::BuildDnsNotification(dnsSettings); + notification.Settings = networking::BuildDnsNotification(dnsSettings, m_dnsOptions); m_gnsChannel.SendHnsNotification(ToJsonW(notification).c_str(), m_adapterId); } diff --git a/src/windows/common/VirtioNetworking.h b/src/windows/common/VirtioNetworking.h index dc7dedf7a..b667cc7ee 100644 --- a/src/windows/common/VirtioNetworking.h +++ b/src/windows/common/VirtioNetworking.h @@ -13,7 +13,7 @@ namespace wsl::core { class VirtioNetworking : public INetworkingEngine { public: - VirtioNetworking(GnsChannel&& gnsChannel, bool enableLocalhostRelay, std::shared_ptr guestDeviceManager, wil::shared_handle userToken); + VirtioNetworking(GnsChannel&& gnsChannel, bool enableLocalhostRelay, LPCWSTR dnsOptions, std::shared_ptr guestDeviceManager, wil::shared_handle userToken); ~VirtioNetworking(); // Note: This class cannot be moved because m_networkNotifyHandle captures a 'this' pointer. @@ -49,12 +49,12 @@ class VirtioNetworking : public INetworkingEngine std::optional m_gnsPortTrackerChannel; std::shared_ptr m_networkSettings; bool m_enableLocalhostRelay; + LPCWSTR m_dnsOptions = nullptr; GUID m_localhostAdapterId; GUID m_adapterId; std::optional m_interfaceLuid; ULONG m_networkMtu = 0; - networking::DnsUpdateHelper m_dnsUpdateHelper; networking::DnsInfo m_trackedDnsSettings; // Note: this field must be destroyed first to stop the callbacks before any other field is destroyed. diff --git a/src/windows/common/WslCoreHostDnsInfo.cpp b/src/windows/common/WslCoreHostDnsInfo.cpp index 64de9a938..dfdd14ddd 100644 --- a/src/windows/common/WslCoreHostDnsInfo.cpp +++ b/src/windows/common/WslCoreHostDnsInfo.cpp @@ -105,12 +105,6 @@ wsl::core::networking::DnsInfo wsl::core::networking::HostDnsInfo::GetDnsTunneli return dnsInfo; } -std::vector wsl::core::networking::HostDnsInfo::GetAdapterAddresses() -{ - std::lock_guard lock(m_lock); - return m_addresses; -} - std::vector wsl::core::networking::HostDnsInfo::GetDnsServerStrings( _In_ const PIP_ADAPTER_DNS_SERVER_ADDRESS& FirstDnsServer, _In_ USHORT IpFamilyFilter, _In_ USHORT MaxValues) { @@ -233,7 +227,7 @@ std::vector wsl::core::networking::HostDnsInfo::GetInterfaceDnsSuff wsl::core::networking::DnsInfo wsl::core::networking::HostDnsInfo::GetDnsSettings(_In_ DnsSettingsFlags Flags) { - std::vector Addresses = GetAdapterAddresses(); + std::vector Addresses = AdapterAddresses::GetCurrent(); auto RemoveFilter = [&](const IpAdapterAddress& Address) { // Ignore interfaces that are not currently "up". @@ -326,12 +320,6 @@ wsl::core::networking::DnsInfo wsl::core::networking::HostDnsInfo::GetDnsSetting return DnsSettings; } -void wsl::core::networking::HostDnsInfo::UpdateNetworkInformation() -{ - std::lock_guard lock(m_lock); - m_addresses = AdapterAddresses::GetCurrent(); -} - std::string wsl::core::networking::GenerateResolvConf(_In_ const DnsInfo& Info) { std::string contents{}; @@ -345,7 +333,10 @@ std::string wsl::core::networking::GenerateResolvConf(_In_ const DnsInfo& Info) contents += c_asciiNewLine; } - // Add domain information if it is available. + // Add DNS suffix information using 'search' directive. + // Per resolv.conf(5): "The domain directive is an obsolete name for the search directive + // that handles one search list entry only." + // See: https://man7.org/linux/man-pages/man5/resolv.conf.5.html if (!Info.Domains.empty()) { contents += "search "; @@ -497,28 +488,21 @@ wsl::core::networking::DnsSuffixRegistryWatcher::DnsSuffixRegistryWatcher(Regist m_registryWatchers.swap(localRegistryWatchers); } -wsl::shared::hns::DNS wsl::core::networking::BuildDnsNotification(const DnsInfo& settings, bool useLinuxDomainEntry) +wsl::shared::hns::DNS wsl::core::networking::BuildDnsNotification(const DnsInfo& settings, PCWSTR options) { wsl::shared::hns::DNS dnsNotification{}; - dnsNotification.Options = LX_INIT_RESOLVCONF_FULL_HEADER; - dnsNotification.ServerList = wsl::shared::string::MultiByteToWide(wsl::shared::string::Join(settings.Servers, ',')); - - if (useLinuxDomainEntry && !settings.Domains.empty()) + if (options) { - // Use 'domain' entry for single DNS suffix (typically used when mirroring host DNS without tunneling) - dnsNotification.Domain = wsl::shared::string::MultiByteToWide(settings.Domains.front()); - } - else - { - // Use 'search' entry for DNS suffix list - dnsNotification.Search = wsl::shared::string::MultiByteToWide(wsl::shared::string::Join(settings.Domains, ',')); + dnsNotification.Options = options; } - return dnsNotification; -} + dnsNotification.ServerList = wsl::shared::string::MultiByteToWide(wsl::shared::string::Join(settings.Servers, ',')); -wsl::core::networking::DnsInfo wsl::core::networking::DnsUpdateHelper::GetCurrentDnsSettings(DnsSettingsFlags flags) -{ - m_hostDnsInfo.UpdateNetworkInformation(); - return m_hostDnsInfo.GetDnsSettings(flags); + // Use 'search' entry for DNS suffix list. + // Per resolv.conf(5): "The domain directive is an obsolete name for the search directive + // that handles one search list entry only." + // See: https://man7.org/linux/man-pages/man5/resolv.conf.5.html + dnsNotification.Search = wsl::shared::string::MultiByteToWide(wsl::shared::string::Join(settings.Domains, ',')); + + return dnsNotification; } diff --git a/src/windows/common/WslCoreHostDnsInfo.h b/src/windows/common/WslCoreHostDnsInfo.h index fe2cab071..8c0bdee76 100644 --- a/src/windows/common/WslCoreHostDnsInfo.h +++ b/src/windows/common/WslCoreHostDnsInfo.h @@ -1,7 +1,6 @@ // Copyright (C) Microsoft Corporation. All rights reserved. #pragma once -#include #include #include @@ -42,9 +41,9 @@ std::string GenerateResolvConf(_In_ const DnsInfo& Info); /// Builds an hns::DNS notification from DnsInfo settings. /// /// The DNS settings to convert -/// If true, uses 'domain' entry for single suffix; otherwise uses 'search' for all -/// suffixes The hns::DNS notification ready to send via GNS channel -wsl::shared::hns::DNS BuildDnsNotification(const DnsInfo& settings, bool useLinuxDomainEntry = false); +/// The resolv.conf header options (defaults to LX_INIT_RESOLVCONF_FULL_HEADER) +/// The hns::DNS notification ready to send via GNS channel +wsl::shared::hns::DNS BuildDnsNotification(const DnsInfo& settings, PCWSTR options = LX_INIT_RESOLVCONF_FULL_HEADER); std::vector GetAllDnsSuffixes(const std::vector& AdapterAddresses); @@ -53,27 +52,15 @@ DWORD GetBestInterface(); class HostDnsInfo { public: - DnsInfo GetDnsSettings(_In_ DnsSettingsFlags Flags); - - void UpdateNetworkInformation(); + static DnsInfo GetDnsSettings(_In_ DnsSettingsFlags Flags); static DnsInfo GetDnsTunnelingSettings(const std::wstring& dnsTunnelingNameserver); - const std::vector& CurrentAddresses() const - { - return m_addresses; - } - private: - /// - /// Internal function to retrieve the latest copy of interface information. - /// - std::vector GetAdapterAddresses(); - /// /// Internal function to retrieve interface DNS servers. /// - std::vector GetInterfaceDnsServers(const std::vector& AdapterAddresses, _In_ DnsSettingsFlags Flags); + static std::vector GetInterfaceDnsServers(const std::vector& AdapterAddresses, _In_ DnsSettingsFlags Flags); /// /// Internal function to retrieve all Windows DNS suffixes. @@ -84,30 +71,6 @@ class HostDnsInfo /// Internal function to convert DNS server addresses into strings. /// static std::vector GetDnsServerStrings(_In_ const PIP_ADAPTER_DNS_SERVER_ADDRESS& DnsServer, _In_ USHORT IpFamilyFilter, _In_ USHORT MaxValues); - - /// - /// Stores latest copy of interface information. - /// - std::mutex m_lock; - _Guarded_by_(m_lock) std::vector m_addresses; -}; - -/// -/// Helper class that fetches current DNS settings from the host. -/// Callers are responsible for tracking changes if needed. -/// -class DnsUpdateHelper -{ -public: - /// - /// Fetches current DNS settings from the host. - /// - /// Flags controlling which DNS settings to include - /// Current DNS settings - DnsInfo GetCurrentDnsSettings(DnsSettingsFlags flags); - -private: - HostDnsInfo m_hostDnsInfo; }; using RegistryChangeCallback = std::function; diff --git a/src/windows/service/exe/LxssInstance.cpp b/src/windows/service/exe/LxssInstance.cpp index 3f4a3001a..54fe3b186 100644 --- a/src/windows/service/exe/LxssInstance.cpp +++ b/src/windows/service/exe/LxssInstance.cpp @@ -784,9 +784,6 @@ try // Impersonate the service. auto runAsSelf = wil::run_as_self(); - // Update the instance's DNS information. - m_dnsInfo.UpdateNetworkInformation(); - // Update the resolv.conf file if it has changed. _UpdateNetworkConfigurationFiles(false); return; @@ -799,7 +796,7 @@ void LxssInstance::_UpdateNetworkConfigurationFiles(_In_ bool UpdateAlways) wsl::core::networking::DnsSettingsFlags flags = wsl::core::networking::DnsSettingsFlags::IncludeIpv6Servers; WI_SetFlagIf(flags, wsl::core::networking::DnsSettingsFlags::IncludeVpn, m_enableVpnDetection); - const auto dnsSettings = m_dnsInfo.GetDnsSettings(flags); + const auto dnsSettings = wsl::core::networking::HostDnsInfo::GetDnsSettings(flags); std::string fileContents = GenerateResolvConf(dnsSettings); std::lock_guard lock(m_resolvConfLock); if (!UpdateAlways && (fileContents == m_lastResolvConfContents)) diff --git a/src/windows/service/exe/LxssInstance.h b/src/windows/service/exe/LxssInstance.h index 0301d165c..3ef825b41 100644 --- a/src/windows/service/exe/LxssInstance.h +++ b/src/windows/service/exe/LxssInstance.h @@ -224,11 +224,6 @@ class LxssInstance : public LxssRunningInstance /// LxssIpTables m_ipTables; - /// - /// Class for querying host dns information. - /// - wsl::core::networking::HostDnsInfo m_dnsInfo; - /// /// Settings for updating /etc/resolv.conf. /// diff --git a/src/windows/service/exe/WslCoreVm.cpp b/src/windows/service/exe/WslCoreVm.cpp index 5ea0e809e..3d5d994ac 100644 --- a/src/windows/service/exe/WslCoreVm.cpp +++ b/src/windows/service/exe/WslCoreVm.cpp @@ -577,7 +577,7 @@ void WslCoreVm::Initialize(const GUID& VmId, const wil::shared_handle& UserToken else if (m_vmConfig.NetworkingMode == NetworkingMode::VirtioProxy) { m_networkingEngine = std::make_unique( - std::move(gnsChannel), m_vmConfig.EnableLocalhostRelay, m_guestDeviceManager, m_userToken); + std::move(gnsChannel), m_vmConfig.EnableLocalhostRelay, LX_INIT_RESOLVCONF_FULL_HEADER, m_guestDeviceManager, m_userToken); } else if (m_vmConfig.NetworkingMode == NetworkingMode::Bridged) { diff --git a/src/windows/service/exe/WslMirroredNetworking.cpp b/src/windows/service/exe/WslMirroredNetworking.cpp index 0d3bb3618..ff669c606 100644 --- a/src/windows/service/exe/WslMirroredNetworking.cpp +++ b/src/windows/service/exe/WslMirroredNetworking.cpp @@ -434,8 +434,7 @@ void wsl::core::networking::WslMirroredNetworkManager::ProcessDNSChange() } else { - m_hostDnsInfo.UpdateNetworkInformation(); - m_dnsInfo = m_hostDnsInfo.GetDnsSettings( + m_dnsInfo = wsl::core::networking::HostDnsInfo::GetDnsSettings( wsl::core::networking::DnsSettingsFlags::IncludeVpn | wsl::core::networking::DnsSettingsFlags::IncludeIpv6Servers | wsl::core::networking::DnsSettingsFlags::IncludeAllSuffixes); } diff --git a/src/windows/service/exe/WslMirroredNetworking.h b/src/windows/service/exe/WslMirroredNetworking.h index f542e8bb5..9f21e83c2 100644 --- a/src/windows/service/exe/WslMirroredNetworking.h +++ b/src/windows/service/exe/WslMirroredNetworking.h @@ -224,9 +224,6 @@ class WslMirroredNetworkManager final : public wsl::core::networking::IMirroredN _Guarded_by_(m_networkLock) DnsInfo m_trackedDnsInfo; // The current DNS info on the host _Guarded_by_(m_networkLock) DnsInfo m_dnsInfo; - // m_hostDnsInfo is an optimization used to avoid allocating a large buffer every time we call - // GetAdaptersAddresses when querying host DNS info - _Guarded_by_(m_networkLock) HostDnsInfo m_hostDnsInfo; std::wstring m_dnsTunnelingIpAddress; diff --git a/test/windows/NetworkTests.cpp b/test/windows/NetworkTests.cpp index 23f9b14ab..cd44200de 100644 --- a/test/windows/NetworkTests.cpp +++ b/test/windows/NetworkTests.cpp @@ -1037,7 +1037,6 @@ class NetworkTests wsl::shared::hns::DNS dns; dns.ServerList = L"1.1.1.1,1.1.1.2"; - dns.Domain = L"microsoft.com"; dns.Search = L"foo.microsoft.com,bar.microsoft.com"; dns.Options = LX_INIT_RESOLVCONF_FULL_HEADER; RunGns(dns, ModifyRequestType::Update, GuestEndpointResourceType::DNS); @@ -1047,7 +1046,6 @@ class NetworkTests const std::wstring expected = std::wstring(LX_INIT_RESOLVCONF_FULL_HEADER) + L"nameserver 1.1.1.1\n" L"nameserver 1.1.1.2\n" - L"domain microsoft.com\n" L"search foo.microsoft.com bar.microsoft.com\n"; VERIFY_ARE_EQUAL(expected, out.c_str()); } @@ -4612,6 +4610,9 @@ class VirtioProxyTests const std::wregex pattern(L"(.|\\n)*nameserver [0-9. ]+(.|\\n)*"); VERIFY_IS_TRUE(std::regex_match(out, pattern)); + + // Verify that /etc/resolv.conf contains a 'search' line with DNS suffixes + VERIFY_IS_TRUE(out.find(L"search ") != std::wstring::npos); } TEST_METHOD(GuestPortIsReleased)