Address PR #40366 feedback: fix line endings and remove old install API test (#40369)

- Restore original CRLF line endings in DnsResolver.cpp, RingBuffer.cpp, and WslCoreHostDnsInfo.h that were inadvertently changed to LF - Remove commented-out WSLCInstall/WSLCInstallManual test methods that referenced the old install API which no longer exists Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Ben Hillis committed Apr 30, 2026 at 16:10 UTC c414300f7159ad3f4e4cc667a332d9301fa745e1
4 files changed +662 -753
src/windows/common/DnsResolver.cpp
+417 -417
@@ -1,417 +1,417 @@
1 -// Copyright (C) Microsoft Corporation. All rights reserved.
2 -
3 -#include <LxssDynamicFunction.h>
4 -#include "precomp.h"
5 -#include "DnsResolver.h"
6 -
7 -using wsl::core::networking::DnsResolver;
8 -
9 -static constexpr auto c_dnsModuleName = L"dnsapi.dll";
10 -
11 -std::optional<LxssDynamicFunction<decltype(DnsQueryRaw)>> DnsResolver::s_dnsQueryRaw;
12 -std::optional<LxssDynamicFunction<decltype(DnsCancelQueryRaw)>> DnsResolver::s_dnsCancelQueryRaw;
13 -std::optional<LxssDynamicFunction<decltype(DnsQueryRawResultFree)>> DnsResolver::s_dnsQueryRawResultFree;
14 -
15 -HRESULT DnsResolver::LoadDnsResolverMethods() noexcept
16 -{
17 - static wil::shared_hmodule dnsModule;
18 - static DWORD loadError = ERROR_SUCCESS;
19 - static std::once_flag dnsLoadFlag;
20 -
21 - // Load DNS dll only once
22 - std::call_once(dnsLoadFlag, [&]() {
23 - dnsModule.reset(LoadLibraryEx(c_dnsModuleName, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32));
24 - if (!dnsModule)
25 - {
26 - loadError = GetLastError();
27 - }
28 - });
29 -
30 - RETURN_IF_WIN32_ERROR_MSG(loadError, "LoadLibraryEx %ls", c_dnsModuleName);
31 -
32 - // Initialize dynamic functions for the DNS tunneling Windows APIs.
33 - // using the non-throwing instance of LxssDynamicFunction as to not end up in the Error telemetry
34 - LxssDynamicFunction<decltype(DnsQueryRaw)> local_dnsQueryRaw{DynamicFunctionErrorLogs::None};
35 - RETURN_IF_FAILED_EXPECTED(local_dnsQueryRaw.load(dnsModule, "DnsQueryRaw"));
36 - LxssDynamicFunction<decltype(DnsCancelQueryRaw)> local_dnsCancelQueryRaw{DynamicFunctionErrorLogs::None};
37 - RETURN_IF_FAILED_EXPECTED(local_dnsCancelQueryRaw.load(dnsModule, "DnsCancelQueryRaw"));
38 - LxssDynamicFunction<decltype(DnsQueryRawResultFree)> local_dnsQueryRawResultFree{DynamicFunctionErrorLogs::None};
39 - RETURN_IF_FAILED_EXPECTED(local_dnsQueryRawResultFree.load(dnsModule, "DnsQueryRawResultFree"));
40 -
41 - // Make a dummy call to the DNS APIs to verify if they are working. The APIs are going to be present
42 - // on older Windows versions, where they can be turned on/off. If turned off, the APIs
43 - // will be unusable and will return ERROR_CALL_NOT_IMPLEMENTED.
44 - if (local_dnsQueryRaw(nullptr, nullptr) == ERROR_CALL_NOT_IMPLEMENTED)
45 - {
46 - RETURN_IF_WIN32_ERROR_EXPECTED(ERROR_CALL_NOT_IMPLEMENTED);
47 - }
48 -
49 - s_dnsQueryRaw.emplace(std::move(local_dnsQueryRaw));
50 - s_dnsCancelQueryRaw.emplace(std::move(local_dnsCancelQueryRaw));
51 - s_dnsQueryRawResultFree.emplace(std::move(local_dnsQueryRawResultFree));
52 - return S_OK;
53 -}
54 -
55 -DnsResolver::DnsResolver(wil::unique_socket&& dnsHvsocket, DnsResolverFlags flags) :
56 - m_dnsChannel(
57 - std::move(dnsHvsocket),
58 - [this](const gsl::span<gsl::byte> dnsBuffer, const LX_GNS_DNS_CLIENT_IDENTIFIER& dnsClientIdentifier) {
59 - ProcessDnsRequest(dnsBuffer, dnsClientIdentifier);
60 - }),
61 - m_flags(flags)
62 -{
63 - // Initialize as signaled, as there are no requests yet
64 - m_allRequestsFinished.SetEvent();
65 -
66 - // Read external interface constraint regkey
67 - const auto lxssKey = windows::common::registry::OpenLxssMachineKey(KEY_READ);
68 - m_externalInterfaceConstraintName =
69 - windows::common::registry::ReadString(lxssKey.get(), nullptr, c_interfaceConstraintKey, L"");
70 -
71 - if (!m_externalInterfaceConstraintName.empty())
72 - {
73 - ResolveExternalInterfaceConstraintIndex();
74 -
75 - WSL_LOG(
76 - "DnsResolver::DnsResolver",
77 - TraceLoggingValue(m_externalInterfaceConstraintName.c_str(), "m_externalInterfaceConstraintName"),
78 - TraceLoggingValue(m_externalInterfaceConstraintIndex, "m_externalInterfaceConstraintIndex"));
79 -
80 - // Register for interface change notifications. Notifications are used to determine if the external interface constraint setting is applicable.
81 - THROW_IF_WIN32_ERROR(NotifyIpInterfaceChange(AF_UNSPEC, &DnsResolver::InterfaceChangeCallback, this, FALSE, &m_interfaceNotificationHandle));
82 - }
83 -}
84 -
85 -DnsResolver::~DnsResolver() noexcept
86 -{
87 - Stop();
88 -}
89 -
90 -void DnsResolver::GenerateTelemetry() noexcept
91 -try
92 -{
93 - // Find the 3 most common DNS API failures
94 - uint32_t mostCommonDnsStatusError = 0;
95 - uint32_t mostCommonDnsStatusErrorCount = 0;
96 - uint32_t secondCommonDnsStatusError = 0;
97 - uint32_t secondCommonDnsStatusErrorCount = 0;
98 - uint32_t thirdCommonDnsStatusError = 0;
99 - uint32_t thirdCommonDnsStatusErrorCount = 0;
100 -
101 - std::vector<std::pair<uint32_t, uint32_t>> failures(m_dnsApiFailures.size());
102 - std::copy(m_dnsApiFailures.begin(), m_dnsApiFailures.end(), failures.begin());
103 -
104 - // Sort in descending order based on failure count
105 - std::sort(failures.begin(), failures.end(), [](const auto& lhs, const auto& rhs) { return lhs.second > rhs.second; });
106 -
107 - if (failures.size() >= 1)
108 - {
109 - mostCommonDnsStatusError = failures[0].first;
110 - mostCommonDnsStatusErrorCount = failures[0].second;
111 - }
112 - if (failures.size() >= 2)
113 - {
114 - secondCommonDnsStatusError = failures[1].first;
115 - secondCommonDnsStatusErrorCount = failures[1].second;
116 - }
117 - if (failures.size() >= 3)
118 - {
119 - thirdCommonDnsStatusError = failures[2].first;
120 - thirdCommonDnsStatusErrorCount = failures[2].second;
121 - }
122 -
123 - // Add telemetry with DNS tunneling statistics, before shutting down
124 - WSL_LOG(
125 - "DnsTunnelingStatistics",
126 - TraceLoggingValue(m_totalUdpQueries.load(), "totalUdpQueries"),
127 - TraceLoggingValue(m_successfulUdpQueries.load(), "successfulUdpQueries"),
128 - TraceLoggingValue(m_totalTcpQueries.load(), "totalTcpQueries"),
129 - TraceLoggingValue(m_successfulTcpQueries.load(), "successfulTcpQueries"),
130 - TraceLoggingValue(m_queriesWithNullResult.load(), "queriesWithNullResult"),
131 - TraceLoggingValue(m_failedDnsQueryRawCalls.load(), "FailedDnsQueryRawCalls"),
132 - TraceLoggingValue(m_dnsApiFailures.size(), "totalDnsStatusErrorInstances"),
133 - TraceLoggingValue(mostCommonDnsStatusError, "mostCommonDnsStatusError"),
134 - TraceLoggingValue(mostCommonDnsStatusErrorCount, "mostCommonDnsStatusErrorCount"),
135 - TraceLoggingValue(secondCommonDnsStatusError, "secondCommonDnsStatusError"),
136 - TraceLoggingValue(secondCommonDnsStatusErrorCount, "secondCommonDnsStatusErrorCount"),
137 - TraceLoggingValue(thirdCommonDnsStatusError, "thirdCommonDnsStatusError"),
138 - TraceLoggingValue(thirdCommonDnsStatusErrorCount, "thirdCommonDnsStatusErrorCount"));
139 -}
140 -CATCH_LOG()
141 -
142 -void DnsResolver::Stop() noexcept
143 -try
144 -{
145 - WSL_LOG("DnsResolver::Stop");
146 -
147 - // Scoped m_dnsLock
148 - {
149 - const std::lock_guard lock(m_dnsLock);
150 -
151 - m_stopped = true;
152 -
153 - // Cancel existing requests. Cancel is complete when DnsQueryRawCallback is
154 - // invoked with status == ERROR_CANCELLED
155 - // N.B. Cancelling can end up calling the DnsQueryRawCallback directly on this same thread. i.e., while this
156 - // lock is held. Which is fine because m_dnsLock is a recursive mutex.
157 - // N.B. Cancelling a query will synchronously remove the query from m_dnsRequests, which invalidates iterators.
158 -
159 - std::vector<DNS_QUERY_RAW_CANCEL*> cancelHandles;
160 - cancelHandles.reserve(m_dnsRequests.size());
161 -
162 - for (auto& [_, context] : m_dnsRequests)
163 - {
164 - cancelHandles.emplace_back(&context->m_cancelHandle);
165 - }
166 -
167 - for (const auto e : cancelHandles)
168 - {
169 - LOG_IF_WIN32_ERROR(s_dnsCancelQueryRaw.value()(e));
170 - }
171 - }
172 -
173 - // Wait for all requests to complete. At this point no new requests can be started since the object is stopped.
174 - // We are only waiting for existing requests to finish.
175 - m_allRequestsFinished.wait();
176 -
177 - // Stop the response queue first as it can make calls in m_dnsChannel
178 - m_dnsResponseQueue.cancel();
179 -
180 - m_dnsChannel.Stop();
181 -
182 - // Stop interface change notifications
183 - m_interfaceNotificationHandle.reset();
184 -
185 - GenerateTelemetry();
186 -}
187 -CATCH_LOG()
188 -
189 -void DnsResolver::ProcessDnsRequest(const gsl::span<gsl::byte> dnsBuffer, const LX_GNS_DNS_CLIENT_IDENTIFIER& dnsClientIdentifier) noexcept
190 -try
191 -{
192 - const std::lock_guard lock(m_dnsLock);
193 - if (m_stopped)
194 - {
195 - return;
196 - }
197 -
198 - WSL_LOG_DEBUG(
199 - "DnsResolver::ProcessDnsRequest - received new DNS request",
200 - TraceLoggingValue(dnsBuffer.size(), "DNS buffer size"),
201 - TraceLoggingValue(dnsClientIdentifier.Protocol == IPPROTO_UDP ? "UDP" : "TCP", "Protocol"),
202 - TraceLoggingValue(dnsClientIdentifier.DnsClientId, "DNS client id"),
203 - TraceLoggingValue(!m_externalInterfaceConstraintName.empty(), "Is ExternalInterfaceConstraint configured"),
204 - TraceLoggingValue(m_externalInterfaceConstraintIndex, "m_externalInterfaceConstraintIndex"));
205 -
206 - // If the external interface constraint is configured but it is *not* present/up, WSL should be net-blind, so we avoid making DNS requests.
207 - if (!m_externalInterfaceConstraintName.empty() && m_externalInterfaceConstraintIndex == 0)
208 - {
209 - return;
210 - }
211 -
212 - dnsClientIdentifier.Protocol == IPPROTO_UDP ? m_totalUdpQueries++ : m_totalTcpQueries++;
213 -
214 - // Get next request id. If value reaches UINT_MAX + 1 it will be automatically reset to 0
215 - const auto requestId = m_currentRequestId++;
216 -
217 - // Create the DNS request context
218 - auto context = std::make_unique<DnsResolver::DnsQueryContext>(
219 - requestId, dnsClientIdentifier, [this](_Inout_ DnsResolver::DnsQueryContext* context, _Inout_opt_ DNS_QUERY_RAW_RESULT* queryResults) {
220 - HandleDnsQueryCompletion(context, queryResults);
221 - });
222 -
223 - auto [it, _] = m_dnsRequests.emplace(requestId, std::move(context));
224 - const auto localContext = it->second.get();
225 -
226 - auto removeContextOnError = wil::scope_exit([&] { WI_VERIFY(m_dnsRequests.erase(requestId) == 1); });
227 -
228 - // Fill DNS request structure
229 - DNS_QUERY_RAW_REQUEST request{};
230 -
231 - request.version = DNS_QUERY_RAW_REQUEST_VERSION1;
232 - request.resultsVersion = DNS_QUERY_RAW_RESULTS_VERSION1;
233 - request.dnsQueryRawSize = static_cast<ULONG>(dnsBuffer.size());
234 - request.dnsQueryRaw = (PBYTE)dnsBuffer.data();
235 - request.protocol = (dnsClientIdentifier.Protocol == IPPROTO_TCP) ? DNS_PROTOCOL_TCP : DNS_PROTOCOL_UDP;
236 - request.queryCompletionCallback = DnsResolver::DnsQueryRawCallback;
237 - request.queryContext = localContext;
238 - // Only unicast UDP & TCP queries are tunneled. Pass this flag to tell Windows DNS client to *not* resolve using multicast.
239 - request.queryOptions |= DNS_QUERY_NO_MULTICAST;
240 -
241 - // In a DNS request from Linux there might be DNS records that Windows DNS client does not know how to parse.
242 - // By default in this case Windows will fail the request. When the flag is enabled, Windows will extract the
243 - // question from the DNS request and attempt to resolve it, ignoring the unknown records.
244 - if (WI_IsFlagSet(m_flags, DnsResolverFlags::BestEffortDnsParsing))
245 - {
246 - request.queryRawOptions |= DNS_QUERY_RAW_OPTION_BEST_EFFORT_PARSE;
247 - }
248 -
249 - // If the external interface constraint is configured and present on the host, only send DNS requests on that interface.
250 - if (m_externalInterfaceConstraintIndex != 0)
251 - {
252 - request.interfaceIndex = m_externalInterfaceConstraintIndex;
253 - }
254 -
255 - // Start the DNS request
256 - // N.B. All DNS requests will bypass the Windows DNS cache
257 - const auto result = s_dnsQueryRaw.value()(&request, &localContext->m_cancelHandle);
258 - if (result != DNS_REQUEST_PENDING)
259 - {
260 - m_failedDnsQueryRawCalls++;
261 -
262 - WSL_LOG(
263 - "ProcessDnsRequestFailed",
264 - TraceLoggingValue(requestId, "requestId"),
265 - TraceLoggingValue(result, "result"),
266 - TraceLoggingValue("DnsQueryRaw", "executionStep"));
267 - return;
268 - }
269 -
270 - removeContextOnError.release();
271 -
272 - m_allRequestsFinished.ResetEvent();
273 -}
274 -CATCH_LOG()
275 -
276 -void DnsResolver::HandleDnsQueryCompletion(_Inout_ DnsResolver::DnsQueryContext* queryContext, _Inout_opt_ DNS_QUERY_RAW_RESULT* queryResults) noexcept
277 -try
278 -{
279 - // Always free the query result structure
280 - const auto freeQueryResults = wil::scope_exit([&] {
281 - if (queryResults != nullptr)
282 - {
283 - s_dnsQueryRawResultFree.value()(queryResults);
284 - }
285 - });
286 -
287 - const std::lock_guard lock(m_dnsLock);
288 -
289 - if (queryResults != nullptr)
290 - {
291 - WSL_LOG(
292 - "DnsResolver::HandleDnsQueryCompletion",
293 - TraceLoggingValue(queryContext->m_id, "queryContext->m_id"),
294 - TraceLoggingValue(queryResults->queryStatus, "queryResults->queryStatus"),
295 - TraceLoggingValue(queryResults->queryRawResponse != nullptr, "validResponse"));
296 -
297 - // Note: The response may be valid even if queryResults->queryStatus is not 0, for example when the DNS server returns a negative response.
298 - if (queryResults->queryRawResponse != nullptr)
299 - {
300 - queryContext->m_dnsClientIdentifier.Protocol == IPPROTO_UDP ? m_successfulUdpQueries++ : m_successfulTcpQueries++;
301 - }
302 - // the Windows DNS API returned failure
303 - else
304 - {
305 - if (m_dnsApiFailures.find(queryResults->queryStatus) == m_dnsApiFailures.end())
306 - {
307 - m_dnsApiFailures[queryResults->queryStatus] = 1;
308 - }
309 - else
310 - {
311 - m_dnsApiFailures[queryResults->queryStatus]++;
312 - }
313 - }
314 - }
315 - else
316 - {
317 - WSL_LOG(
318 - "DnsResolver::HandleDnsQueryCompletion - received a NULL queryResults",
319 - TraceLoggingValue(queryContext->m_id, "queryContext->m_id"));
320 - m_queriesWithNullResult++;
321 - }
322 -
323 - if (!m_stopped && queryResults != nullptr && queryResults->queryRawResponse != nullptr)
324 - {
325 - // Copy DNS response buffer
326 - std::vector<gsl::byte> dnsResponse(queryResults->queryRawResponseSize);
327 - CopyMemory(dnsResponse.data(), queryResults->queryRawResponse, queryResults->queryRawResponseSize);
328 -
329 - WSL_LOG_DEBUG(
330 - "DnsResolver::HandleDnsQueryCompletion - received new DNS response",
331 - TraceLoggingValue(dnsResponse.size(), "DNS buffer size"),
332 - TraceLoggingValue(queryContext->m_dnsClientIdentifier.Protocol == IPPROTO_UDP ? "UDP" : "TCP", "Protocol"),
333 - TraceLoggingValue(queryContext->m_dnsClientIdentifier.DnsClientId, "DNS client id"));
334 -
335 - // Schedule the DNS response to be sent to Linux
336 - m_dnsResponseQueue.submit([this, dnsResponse = std::move(dnsResponse), dnsClientIdentifier = queryContext->m_dnsClientIdentifier]() mutable {
337 - m_dnsChannel.SendDnsMessage(gsl::make_span(dnsResponse), dnsClientIdentifier);
338 - });
339 - }
340 -
341 - // Stop tracking this DNS request and delete the request context
342 - WI_VERIFY(m_dnsRequests.erase(queryContext->m_id) == 1);
343 -
344 - // Set event if all tracked requests have finished
345 - if (m_dnsRequests.empty())
346 - {
347 - m_allRequestsFinished.SetEvent();
348 - }
349 -}
350 -CATCH_LOG()
351 -
352 -void DnsResolver::ResolveExternalInterfaceConstraintIndex() noexcept
353 -try
354 -{
355 - const std::lock_guard lock(m_dnsLock);
356 - if (m_stopped)
357 - {
358 - return;
359 - }
360 -
361 - if (m_externalInterfaceConstraintName.empty())
362 - {
363 - return;
364 - }
365 -
366 - NET_LUID interfaceLuid{};
367 - ULONG interfaceIndex = 0;
368 -
369 - // Update the interface index on every exit path.
370 - // The calls below to convert interface name to index will fail if the interface does not exist anymore,
371 - // in which case we still need to reset the interface index to its default value of 0.
372 - const auto setInterfaceIndex = wil::scope_exit([&] {
373 - if (interfaceIndex != m_externalInterfaceConstraintIndex)
374 - {
375 - WSL_LOG(
376 - "DnsResolver::ResolveExternalInterfaceConstraintIndex - setting m_externalInterfaceConstraintIndex to new value",
377 - TraceLoggingValue(m_externalInterfaceConstraintIndex, "old interface index"),
378 - TraceLoggingValue(interfaceIndex, "new interface index"));
379 -
380 - m_externalInterfaceConstraintIndex = interfaceIndex;
381 - }
382 - });
383 -
384 - // If external interface constraint is configured, query to see if it's present on the host.
385 - auto errorCode = ConvertInterfaceAliasToLuid(m_externalInterfaceConstraintName.c_str(), &interfaceLuid);
386 - if (FAILED_WIN32_LOG(errorCode))
387 - {
388 - return;
389 - }
390 -
391 - errorCode = ConvertInterfaceLuidToIndex(&interfaceLuid, reinterpret_cast<PNET_IFINDEX>(&interfaceIndex));
392 - if (FAILED_WIN32_LOG(errorCode))
393 - {
394 - return;
395 - }
396 -}
397 -CATCH_LOG()
398 -
399 -VOID CALLBACK DnsResolver::DnsQueryRawCallback(_In_ VOID* queryContext, _Inout_opt_ DNS_QUERY_RAW_RESULT* queryResults) noexcept
400 -try
401 -{
402 - assert(queryContext != nullptr);
403 -
404 - const auto context = static_cast<DnsQueryContext*>(queryContext);
405 -
406 - // Call into DnsResolver parent object to process the query result
407 - context->m_handleQueryCompletion(context, queryResults);
408 -}
409 -CATCH_LOG()
410 -
411 -VOID CALLBACK DnsResolver::InterfaceChangeCallback(_In_ PVOID context, PMIB_IPINTERFACE_ROW, MIB_NOTIFICATION_TYPE) noexcept
412 -try
413 -{
414 - const auto dnsResolver = static_cast<DnsResolver*>(context);
415 - dnsResolver->ResolveExternalInterfaceConstraintIndex();
416 -}
417 -CATCH_LOG()
1 +// Copyright (C) Microsoft Corporation. All rights reserved.
2 +
3 +#include <LxssDynamicFunction.h>
4 +#include "precomp.h"
5 +#include "DnsResolver.h"
6 +
7 +using wsl::core::networking::DnsResolver;
8 +
9 +static constexpr auto c_dnsModuleName = L"dnsapi.dll";
10 +
11 +std::optional<LxssDynamicFunction<decltype(DnsQueryRaw)>> DnsResolver::s_dnsQueryRaw;
12 +std::optional<LxssDynamicFunction<decltype(DnsCancelQueryRaw)>> DnsResolver::s_dnsCancelQueryRaw;
13 +std::optional<LxssDynamicFunction<decltype(DnsQueryRawResultFree)>> DnsResolver::s_dnsQueryRawResultFree;
14 +
15 +HRESULT DnsResolver::LoadDnsResolverMethods() noexcept
16 +{
17 + static wil::shared_hmodule dnsModule;
18 + static DWORD loadError = ERROR_SUCCESS;
19 + static std::once_flag dnsLoadFlag;
20 +
21 + // Load DNS dll only once
22 + std::call_once(dnsLoadFlag, [&]() {
23 + dnsModule.reset(LoadLibraryEx(c_dnsModuleName, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32));
24 + if (!dnsModule)
25 + {
26 + loadError = GetLastError();
27 + }
28 + });
29 +
30 + RETURN_IF_WIN32_ERROR_MSG(loadError, "LoadLibraryEx %ls", c_dnsModuleName);
31 +
32 + // Initialize dynamic functions for the DNS tunneling Windows APIs.
33 + // using the non-throwing instance of LxssDynamicFunction as to not end up in the Error telemetry
34 + LxssDynamicFunction<decltype(DnsQueryRaw)> local_dnsQueryRaw{DynamicFunctionErrorLogs::None};
35 + RETURN_IF_FAILED_EXPECTED(local_dnsQueryRaw.load(dnsModule, "DnsQueryRaw"));
36 + LxssDynamicFunction<decltype(DnsCancelQueryRaw)> local_dnsCancelQueryRaw{DynamicFunctionErrorLogs::None};
37 + RETURN_IF_FAILED_EXPECTED(local_dnsCancelQueryRaw.load(dnsModule, "DnsCancelQueryRaw"));
38 + LxssDynamicFunction<decltype(DnsQueryRawResultFree)> local_dnsQueryRawResultFree{DynamicFunctionErrorLogs::None};
39 + RETURN_IF_FAILED_EXPECTED(local_dnsQueryRawResultFree.load(dnsModule, "DnsQueryRawResultFree"));
40 +
41 + // Make a dummy call to the DNS APIs to verify if they are working. The APIs are going to be present
42 + // on older Windows versions, where they can be turned on/off. If turned off, the APIs
43 + // will be unusable and will return ERROR_CALL_NOT_IMPLEMENTED.
44 + if (local_dnsQueryRaw(nullptr, nullptr) == ERROR_CALL_NOT_IMPLEMENTED)
45 + {
46 + RETURN_IF_WIN32_ERROR_EXPECTED(ERROR_CALL_NOT_IMPLEMENTED);
47 + }
48 +
49 + s_dnsQueryRaw.emplace(std::move(local_dnsQueryRaw));
50 + s_dnsCancelQueryRaw.emplace(std::move(local_dnsCancelQueryRaw));
51 + s_dnsQueryRawResultFree.emplace(std::move(local_dnsQueryRawResultFree));
52 + return S_OK;
53 +}
54 +
55 +DnsResolver::DnsResolver(wil::unique_socket&& dnsHvsocket, DnsResolverFlags flags) :
56 + m_dnsChannel(
57 + std::move(dnsHvsocket),
58 + [this](const gsl::span<gsl::byte> dnsBuffer, const LX_GNS_DNS_CLIENT_IDENTIFIER& dnsClientIdentifier) {
59 + ProcessDnsRequest(dnsBuffer, dnsClientIdentifier);
60 + }),
61 + m_flags(flags)
62 +{
63 + // Initialize as signaled, as there are no requests yet
64 + m_allRequestsFinished.SetEvent();
65 +
66 + // Read external interface constraint regkey
67 + const auto lxssKey = windows::common::registry::OpenLxssMachineKey(KEY_READ);
68 + m_externalInterfaceConstraintName =
69 + windows::common::registry::ReadString(lxssKey.get(), nullptr, c_interfaceConstraintKey, L"");
70 +
71 + if (!m_externalInterfaceConstraintName.empty())
72 + {
73 + ResolveExternalInterfaceConstraintIndex();
74 +
75 + WSL_LOG(
76 + "DnsResolver::DnsResolver",
77 + TraceLoggingValue(m_externalInterfaceConstraintName.c_str(), "m_externalInterfaceConstraintName"),
78 + TraceLoggingValue(m_externalInterfaceConstraintIndex, "m_externalInterfaceConstraintIndex"));
79 +
80 + // Register for interface change notifications. Notifications are used to determine if the external interface constraint setting is applicable.
81 + THROW_IF_WIN32_ERROR(NotifyIpInterfaceChange(AF_UNSPEC, &DnsResolver::InterfaceChangeCallback, this, FALSE, &m_interfaceNotificationHandle));
82 + }
83 +}
84 +
85 +DnsResolver::~DnsResolver() noexcept
86 +{
87 + Stop();
88 +}
89 +
90 +void DnsResolver::GenerateTelemetry() noexcept
91 +try
92 +{
93 + // Find the 3 most common DNS API failures
94 + uint32_t mostCommonDnsStatusError = 0;
95 + uint32_t mostCommonDnsStatusErrorCount = 0;
96 + uint32_t secondCommonDnsStatusError = 0;
97 + uint32_t secondCommonDnsStatusErrorCount = 0;
98 + uint32_t thirdCommonDnsStatusError = 0;
99 + uint32_t thirdCommonDnsStatusErrorCount = 0;
100 +
101 + std::vector<std::pair<uint32_t, uint32_t>> failures(m_dnsApiFailures.size());
102 + std::copy(m_dnsApiFailures.begin(), m_dnsApiFailures.end(), failures.begin());
103 +
104 + // Sort in descending order based on failure count
105 + std::sort(failures.begin(), failures.end(), [](const auto& lhs, const auto& rhs) { return lhs.second > rhs.second; });
106 +
107 + if (failures.size() >= 1)
108 + {
109 + mostCommonDnsStatusError = failures[0].first;
110 + mostCommonDnsStatusErrorCount = failures[0].second;
111 + }
112 + if (failures.size() >= 2)
113 + {
114 + secondCommonDnsStatusError = failures[1].first;
115 + secondCommonDnsStatusErrorCount = failures[1].second;
116 + }
117 + if (failures.size() >= 3)
118 + {
119 + thirdCommonDnsStatusError = failures[2].first;
120 + thirdCommonDnsStatusErrorCount = failures[2].second;
121 + }
122 +
123 + // Add telemetry with DNS tunneling statistics, before shutting down
124 + WSL_LOG(
125 + "DnsTunnelingStatistics",
126 + TraceLoggingValue(m_totalUdpQueries.load(), "totalUdpQueries"),
127 + TraceLoggingValue(m_successfulUdpQueries.load(), "successfulUdpQueries"),
128 + TraceLoggingValue(m_totalTcpQueries.load(), "totalTcpQueries"),
129 + TraceLoggingValue(m_successfulTcpQueries.load(), "successfulTcpQueries"),
130 + TraceLoggingValue(m_queriesWithNullResult.load(), "queriesWithNullResult"),
131 + TraceLoggingValue(m_failedDnsQueryRawCalls.load(), "FailedDnsQueryRawCalls"),
132 + TraceLoggingValue(m_dnsApiFailures.size(), "totalDnsStatusErrorInstances"),
133 + TraceLoggingValue(mostCommonDnsStatusError, "mostCommonDnsStatusError"),
134 + TraceLoggingValue(mostCommonDnsStatusErrorCount, "mostCommonDnsStatusErrorCount"),
135 + TraceLoggingValue(secondCommonDnsStatusError, "secondCommonDnsStatusError"),
136 + TraceLoggingValue(secondCommonDnsStatusErrorCount, "secondCommonDnsStatusErrorCount"),
137 + TraceLoggingValue(thirdCommonDnsStatusError, "thirdCommonDnsStatusError"),
138 + TraceLoggingValue(thirdCommonDnsStatusErrorCount, "thirdCommonDnsStatusErrorCount"));
139 +}
140 +CATCH_LOG()
141 +
142 +void DnsResolver::Stop() noexcept
143 +try
144 +{
145 + WSL_LOG("DnsResolver::Stop");
146 +
147 + // Scoped m_dnsLock
148 + {
149 + const std::lock_guard lock(m_dnsLock);
150 +
151 + m_stopped = true;
152 +
153 + // Cancel existing requests. Cancel is complete when DnsQueryRawCallback is
154 + // invoked with status == ERROR_CANCELLED
155 + // N.B. Cancelling can end up calling the DnsQueryRawCallback directly on this same thread. i.e., while this
156 + // lock is held. Which is fine because m_dnsLock is a recursive mutex.
157 + // N.B. Cancelling a query will synchronously remove the query from m_dnsRequests, which invalidates iterators.
158 +
159 + std::vector<DNS_QUERY_RAW_CANCEL*> cancelHandles;
160 + cancelHandles.reserve(m_dnsRequests.size());
161 +
162 + for (auto& [_, context] : m_dnsRequests)
163 + {
164 + cancelHandles.emplace_back(&context->m_cancelHandle);
165 + }
166 +
167 + for (const auto e : cancelHandles)
168 + {
169 + LOG_IF_WIN32_ERROR(s_dnsCancelQueryRaw.value()(e));
170 + }
171 + }
172 +
173 + // Wait for all requests to complete. At this point no new requests can be started since the object is stopped.
174 + // We are only waiting for existing requests to finish.
175 + m_allRequestsFinished.wait();
176 +
177 + // Stop the response queue first as it can make calls in m_dnsChannel
178 + m_dnsResponseQueue.cancel();
179 +
180 + m_dnsChannel.Stop();
181 +
182 + // Stop interface change notifications
183 + m_interfaceNotificationHandle.reset();
184 +
185 + GenerateTelemetry();
186 +}
187 +CATCH_LOG()
188 +
189 +void DnsResolver::ProcessDnsRequest(const gsl::span<gsl::byte> dnsBuffer, const LX_GNS_DNS_CLIENT_IDENTIFIER& dnsClientIdentifier) noexcept
190 +try
191 +{
192 + const std::lock_guard lock(m_dnsLock);
193 + if (m_stopped)
194 + {
195 + return;
196 + }
197 +
198 + WSL_LOG_DEBUG(
199 + "DnsResolver::ProcessDnsRequest - received new DNS request",
200 + TraceLoggingValue(dnsBuffer.size(), "DNS buffer size"),
201 + TraceLoggingValue(dnsClientIdentifier.Protocol == IPPROTO_UDP ? "UDP" : "TCP", "Protocol"),
202 + TraceLoggingValue(dnsClientIdentifier.DnsClientId, "DNS client id"),
203 + TraceLoggingValue(!m_externalInterfaceConstraintName.empty(), "Is ExternalInterfaceConstraint configured"),
204 + TraceLoggingValue(m_externalInterfaceConstraintIndex, "m_externalInterfaceConstraintIndex"));
205 +
206 + // If the external interface constraint is configured but it is *not* present/up, WSL should be net-blind, so we avoid making DNS requests.
207 + if (!m_externalInterfaceConstraintName.empty() && m_externalInterfaceConstraintIndex == 0)
208 + {
209 + return;
210 + }
211 +
212 + dnsClientIdentifier.Protocol == IPPROTO_UDP ? m_totalUdpQueries++ : m_totalTcpQueries++;
213 +
214 + // Get next request id. If value reaches UINT_MAX + 1 it will be automatically reset to 0
215 + const auto requestId = m_currentRequestId++;
216 +
217 + // Create the DNS request context
218 + auto context = std::make_unique<DnsResolver::DnsQueryContext>(
219 + requestId, dnsClientIdentifier, [this](_Inout_ DnsResolver::DnsQueryContext* context, _Inout_opt_ DNS_QUERY_RAW_RESULT* queryResults) {
220 + HandleDnsQueryCompletion(context, queryResults);
221 + });
222 +
223 + auto [it, _] = m_dnsRequests.emplace(requestId, std::move(context));
224 + const auto localContext = it->second.get();
225 +
226 + auto removeContextOnError = wil::scope_exit([&] { WI_VERIFY(m_dnsRequests.erase(requestId) == 1); });
227 +
228 + // Fill DNS request structure
229 + DNS_QUERY_RAW_REQUEST request{};
230 +
231 + request.version = DNS_QUERY_RAW_REQUEST_VERSION1;
232 + request.resultsVersion = DNS_QUERY_RAW_RESULTS_VERSION1;
233 + request.dnsQueryRawSize = static_cast<ULONG>(dnsBuffer.size());
234 + request.dnsQueryRaw = (PBYTE)dnsBuffer.data();
235 + request.protocol = (dnsClientIdentifier.Protocol == IPPROTO_TCP) ? DNS_PROTOCOL_TCP : DNS_PROTOCOL_UDP;
236 + request.queryCompletionCallback = DnsResolver::DnsQueryRawCallback;
237 + request.queryContext = localContext;
238 + // Only unicast UDP & TCP queries are tunneled. Pass this flag to tell Windows DNS client to *not* resolve using multicast.
239 + request.queryOptions |= DNS_QUERY_NO_MULTICAST;
240 +
241 + // In a DNS request from Linux there might be DNS records that Windows DNS client does not know how to parse.
242 + // By default in this case Windows will fail the request. When the flag is enabled, Windows will extract the
243 + // question from the DNS request and attempt to resolve it, ignoring the unknown records.
244 + if (WI_IsFlagSet(m_flags, DnsResolverFlags::BestEffortDnsParsing))
245 + {
246 + request.queryRawOptions |= DNS_QUERY_RAW_OPTION_BEST_EFFORT_PARSE;
247 + }
248 +
249 + // If the external interface constraint is configured and present on the host, only send DNS requests on that interface.
250 + if (m_externalInterfaceConstraintIndex != 0)
251 + {
252 + request.interfaceIndex = m_externalInterfaceConstraintIndex;
253 + }
254 +
255 + // Start the DNS request
256 + // N.B. All DNS requests will bypass the Windows DNS cache
257 + const auto result = s_dnsQueryRaw.value()(&request, &localContext->m_cancelHandle);
258 + if (result != DNS_REQUEST_PENDING)
259 + {
260 + m_failedDnsQueryRawCalls++;
261 +
262 + WSL_LOG(
263 + "ProcessDnsRequestFailed",
264 + TraceLoggingValue(requestId, "requestId"),
265 + TraceLoggingValue(result, "result"),
266 + TraceLoggingValue("DnsQueryRaw", "executionStep"));
267 + return;
268 + }
269 +
270 + removeContextOnError.release();
271 +
272 + m_allRequestsFinished.ResetEvent();
273 +}
274 +CATCH_LOG()
275 +
276 +void DnsResolver::HandleDnsQueryCompletion(_Inout_ DnsResolver::DnsQueryContext* queryContext, _Inout_opt_ DNS_QUERY_RAW_RESULT* queryResults) noexcept
277 +try
278 +{
279 + // Always free the query result structure
280 + const auto freeQueryResults = wil::scope_exit([&] {
281 + if (queryResults != nullptr)
282 + {
283 + s_dnsQueryRawResultFree.value()(queryResults);
284 + }
285 + });
286 +
287 + const std::lock_guard lock(m_dnsLock);
288 +
289 + if (queryResults != nullptr)
290 + {
291 + WSL_LOG(
292 + "DnsResolver::HandleDnsQueryCompletion",
293 + TraceLoggingValue(queryContext->m_id, "queryContext->m_id"),
294 + TraceLoggingValue(queryResults->queryStatus, "queryResults->queryStatus"),
295 + TraceLoggingValue(queryResults->queryRawResponse != nullptr, "validResponse"));
296 +
297 + // Note: The response may be valid even if queryResults->queryStatus is not 0, for example when the DNS server returns a negative response.
298 + if (queryResults->queryRawResponse != nullptr)
299 + {
300 + queryContext->m_dnsClientIdentifier.Protocol == IPPROTO_UDP ? m_successfulUdpQueries++ : m_successfulTcpQueries++;
301 + }
302 + // the Windows DNS API returned failure
303 + else
304 + {
305 + if (m_dnsApiFailures.find(queryResults->queryStatus) == m_dnsApiFailures.end())
306 + {
307 + m_dnsApiFailures[queryResults->queryStatus] = 1;
308 + }
309 + else
310 + {
311 + m_dnsApiFailures[queryResults->queryStatus]++;
312 + }
313 + }
314 + }
315 + else
316 + {
317 + WSL_LOG(
318 + "DnsResolver::HandleDnsQueryCompletion - received a NULL queryResults",
319 + TraceLoggingValue(queryContext->m_id, "queryContext->m_id"));
320 + m_queriesWithNullResult++;
321 + }
322 +
323 + if (!m_stopped && queryResults != nullptr && queryResults->queryRawResponse != nullptr)
324 + {
325 + // Copy DNS response buffer
326 + std::vector<gsl::byte> dnsResponse(queryResults->queryRawResponseSize);
327 + CopyMemory(dnsResponse.data(), queryResults->queryRawResponse, queryResults->queryRawResponseSize);
328 +
329 + WSL_LOG_DEBUG(
330 + "DnsResolver::HandleDnsQueryCompletion - received new DNS response",
331 + TraceLoggingValue(dnsResponse.size(), "DNS buffer size"),
332 + TraceLoggingValue(queryContext->m_dnsClientIdentifier.Protocol == IPPROTO_UDP ? "UDP" : "TCP", "Protocol"),
333 + TraceLoggingValue(queryContext->m_dnsClientIdentifier.DnsClientId, "DNS client id"));
334 +
335 + // Schedule the DNS response to be sent to Linux
336 + m_dnsResponseQueue.submit([this, dnsResponse = std::move(dnsResponse), dnsClientIdentifier = queryContext->m_dnsClientIdentifier]() mutable {
337 + m_dnsChannel.SendDnsMessage(gsl::make_span(dnsResponse), dnsClientIdentifier);
338 + });
339 + }
340 +
341 + // Stop tracking this DNS request and delete the request context
342 + WI_VERIFY(m_dnsRequests.erase(queryContext->m_id) == 1);
343 +
344 + // Set event if all tracked requests have finished
345 + if (m_dnsRequests.empty())
346 + {
347 + m_allRequestsFinished.SetEvent();
348 + }
349 +}
350 +CATCH_LOG()
351 +
352 +void DnsResolver::ResolveExternalInterfaceConstraintIndex() noexcept
353 +try
354 +{
355 + const std::lock_guard lock(m_dnsLock);
356 + if (m_stopped)
357 + {
358 + return;
359 + }
360 +
361 + if (m_externalInterfaceConstraintName.empty())
362 + {
363 + return;
364 + }
365 +
366 + NET_LUID interfaceLuid{};
367 + ULONG interfaceIndex = 0;
368 +
369 + // Update the interface index on every exit path.
370 + // The calls below to convert interface name to index will fail if the interface does not exist anymore,
371 + // in which case we still need to reset the interface index to its default value of 0.
372 + const auto setInterfaceIndex = wil::scope_exit([&] {
373 + if (interfaceIndex != m_externalInterfaceConstraintIndex)
374 + {
375 + WSL_LOG(
376 + "DnsResolver::ResolveExternalInterfaceConstraintIndex - setting m_externalInterfaceConstraintIndex to new value",
377 + TraceLoggingValue(m_externalInterfaceConstraintIndex, "old interface index"),
378 + TraceLoggingValue(interfaceIndex, "new interface index"));
379 +
380 + m_externalInterfaceConstraintIndex = interfaceIndex;
381 + }
382 + });
383 +
384 + // If external interface constraint is configured, query to see if it's present on the host.
385 + auto errorCode = ConvertInterfaceAliasToLuid(m_externalInterfaceConstraintName.c_str(), &interfaceLuid);
386 + if (FAILED_WIN32_LOG(errorCode))
387 + {
388 + return;
389 + }
390 +
391 + errorCode = ConvertInterfaceLuidToIndex(&interfaceLuid, reinterpret_cast<PNET_IFINDEX>(&interfaceIndex));
392 + if (FAILED_WIN32_LOG(errorCode))
393 + {
394 + return;
395 + }
396 +}
397 +CATCH_LOG()
398 +
399 +VOID CALLBACK DnsResolver::DnsQueryRawCallback(_In_ VOID* queryContext, _Inout_opt_ DNS_QUERY_RAW_RESULT* queryResults) noexcept
400 +try
401 +{
402 + assert(queryContext != nullptr);
403 +
404 + const auto context = static_cast<DnsQueryContext*>(queryContext);
405 +
406 + // Call into DnsResolver parent object to process the query result
407 + context->m_handleQueryCompletion(context, queryResults);
408 +}
409 +CATCH_LOG()
410 +
411 +VOID CALLBACK DnsResolver::InterfaceChangeCallback(_In_ PVOID context, PMIB_IPINTERFACE_ROW, MIB_NOTIFICATION_TYPE) noexcept
412 +try
413 +{
414 + const auto dnsResolver = static_cast<DnsResolver*>(context);
415 + dnsResolver->ResolveExternalInterfaceConstraintIndex();
416 +}
417 +CATCH_LOG()
src/windows/common/RingBuffer.cpp
+153 -153
@@ -1,154 +1,154 @@
1 -/*++
2 -
3 -Copyright (c) Microsoft. All rights reserved.
4 -
5 -Module Name:
6 -
7 - RingBuffer.cpp
8 -
9 -Abstract:
10 -
11 - This file contains definitions for the RingBuffer class.
12 -
13 ---*/
14 -
15 -#include "precomp.h"
16 -#include "RingBuffer.h"
17 -
18 -RingBuffer::RingBuffer(size_t size) : m_maxSize(size), m_offset(0)
19 -{
20 - m_buffer.reserve(size);
21 -}
22 -
23 -void RingBuffer::Insert(std::string_view data)
24 -{
25 - auto lock = m_lock.lock_exclusive();
26 - auto remainingData = gsl::make_span(data.data(), data.size());
27 - if (remainingData.size() > m_maxSize)
28 - {
29 - remainingData = remainingData.subspan(remainingData.size() - m_maxSize);
30 - }
31 -
32 - const auto bytesAtEnd = std::min(m_maxSize - m_offset, remainingData.size());
33 - if (m_offset + bytesAtEnd > m_buffer.size())
34 - {
35 - m_buffer.resize(m_offset + bytesAtEnd);
36 - WI_ASSERT(m_buffer.size() <= m_maxSize);
37 - }
38 -
39 - const auto allBuffer = gsl::make_span(m_buffer);
40 - const auto beginCopyBuffer = allBuffer.subspan(m_offset, bytesAtEnd);
41 - copy(remainingData.subspan(0, bytesAtEnd), beginCopyBuffer);
42 - remainingData = remainingData.subspan(bytesAtEnd);
43 - if (!remainingData.empty())
44 - {
45 - copy(remainingData, allBuffer);
46 - m_offset = remainingData.size();
47 - }
48 - else
49 - {
50 - m_offset += bytesAtEnd;
51 - }
52 -}
53 -
54 -std::vector<std::string> RingBuffer::GetLastDelimitedStrings(char Delimiter, size_t Count) const
55 -{
56 - auto lock = m_lock.lock_shared();
57 - auto [begin, end] = Contents();
58 - std::vector<std::string> results;
59 - std::optional<size_t> endIndex;
60 - for (size_t i = end.size(); i > 0; i--)
61 - {
62 - if (results.size() == Count)
63 - {
64 - break;
65 - }
66 -
67 - if (Delimiter == end[i - 1])
68 - {
69 - if (endIndex.has_value())
70 - {
71 - results.emplace(results.begin(), &end[i], endIndex.value() - i);
72 - endIndex.reset();
73 - }
74 - else
75 - {
76 - endIndex = i - 1;
77 - }
78 - }
79 - }
80 -
81 - if (results.size() == Count)
82 - {
83 - return results;
84 - }
85 -
86 - std::string partial;
87 - if (endIndex.has_value())
88 - {
89 - partial = std::string{&end[0], endIndex.value()};
90 - endIndex.reset();
91 - }
92 -
93 - for (size_t i = begin.size(); i > 0; i--)
94 - {
95 - if (results.size() == Count)
96 - {
97 - break;
98 - }
99 -
100 - if (Delimiter == begin[i - 1])
101 - {
102 - if (!partial.empty())
103 - {
104 - // The debug CRT will fastfail if begin[size] is accessed
105 - // But in this case it's not a problem because begin.size() - i would be == 0
106 - std::string partial_begin{&begin.data()[i], begin.size() - i};
107 - results.emplace(results.begin(), partial_begin + partial);
108 - partial.clear();
109 - }
110 - else if (endIndex.has_value())
111 - {
112 - results.emplace(results.begin(), &begin.data()[i], endIndex.value() - i);
113 - endIndex.reset();
114 - }
115 - else
116 - {
117 - endIndex = i - 1;
118 - }
119 - }
120 - }
121 -
122 - if (results.size() < Count)
123 - {
124 - // May have lost some data, or this could be the very first line logged.
125 - if (!partial.empty())
126 - {
127 - results.emplace(results.begin(), partial);
128 - }
129 - else if (endIndex.has_value())
130 - {
131 - results.emplace(results.begin(), &begin[0], endIndex.value());
132 - }
133 - }
134 -
135 - return results;
136 -}
137 -
138 -std::string RingBuffer::Get() const
139 -{
140 - auto lock = m_lock.lock_shared();
141 - auto [begin, end] = Contents();
142 - std::string data;
143 - data.reserve(begin.size() + end.size());
144 - data.append(begin.data(), begin.size());
145 - data.append(end.data(), end.size());
146 - return data;
147 -}
148 -
149 -std::pair<std::string_view, std::string_view> RingBuffer::Contents() const
150 -{
151 - std::string_view beginView(m_buffer.data() + m_offset, m_buffer.size() - m_offset);
152 - std::string_view endView(m_buffer.data(), m_offset);
153 - return {beginView, endView};
1 +/*++
2 +
3 +Copyright (c) Microsoft. All rights reserved.
4 +
5 +Module Name:
6 +
7 + RingBuffer.cpp
8 +
9 +Abstract:
10 +
11 + This file contains definitions for the RingBuffer class.
12 +
13 +--*/
14 +
15 +#include "precomp.h"
16 +#include "RingBuffer.h"
17 +
18 +RingBuffer::RingBuffer(size_t size) : m_maxSize(size), m_offset(0)
19 +{
20 + m_buffer.reserve(size);
21 +}
22 +
23 +void RingBuffer::Insert(std::string_view data)
24 +{
25 + auto lock = m_lock.lock_exclusive();
26 + auto remainingData = gsl::make_span(data.data(), data.size());
27 + if (remainingData.size() > m_maxSize)
28 + {
29 + remainingData = remainingData.subspan(remainingData.size() - m_maxSize);
30 + }
31 +
32 + const auto bytesAtEnd = std::min(m_maxSize - m_offset, remainingData.size());
33 + if (m_offset + bytesAtEnd > m_buffer.size())
34 + {
35 + m_buffer.resize(m_offset + bytesAtEnd);
36 + WI_ASSERT(m_buffer.size() <= m_maxSize);
37 + }
38 +
39 + const auto allBuffer = gsl::make_span(m_buffer);
40 + const auto beginCopyBuffer = allBuffer.subspan(m_offset, bytesAtEnd);
41 + copy(remainingData.subspan(0, bytesAtEnd), beginCopyBuffer);
42 + remainingData = remainingData.subspan(bytesAtEnd);
43 + if (!remainingData.empty())
44 + {
45 + copy(remainingData, allBuffer);
46 + m_offset = remainingData.size();
47 + }
48 + else
49 + {
50 + m_offset += bytesAtEnd;
51 + }
52 +}
53 +
54 +std::vector<std::string> RingBuffer::GetLastDelimitedStrings(char Delimiter, size_t Count) const
55 +{
56 + auto lock = m_lock.lock_shared();
57 + auto [begin, end] = Contents();
58 + std::vector<std::string> results;
59 + std::optional<size_t> endIndex;
60 + for (size_t i = end.size(); i > 0; i--)
61 + {
62 + if (results.size() == Count)
63 + {
64 + break;
65 + }
66 +
67 + if (Delimiter == end[i - 1])
68 + {
69 + if (endIndex.has_value())
70 + {
71 + results.emplace(results.begin(), &end[i], endIndex.value() - i);
72 + endIndex.reset();
73 + }
74 + else
75 + {
76 + endIndex = i - 1;
77 + }
78 + }
79 + }
80 +
81 + if (results.size() == Count)
82 + {
83 + return results;
84 + }
85 +
86 + std::string partial;
87 + if (endIndex.has_value())
88 + {
89 + partial = std::string{&end[0], endIndex.value()};
90 + endIndex.reset();
91 + }
92 +
93 + for (size_t i = begin.size(); i > 0; i--)
94 + {
95 + if (results.size() == Count)
96 + {
97 + break;
98 + }
99 +
100 + if (Delimiter == begin[i - 1])
101 + {
102 + if (!partial.empty())
103 + {
104 + // The debug CRT will fastfail if begin[size] is accessed
105 + // But in this case it's not a problem because begin.size() - i would be == 0
106 + std::string partial_begin{&begin.data()[i], begin.size() - i};
107 + results.emplace(results.begin(), partial_begin + partial);
108 + partial.clear();
109 + }
110 + else if (endIndex.has_value())
111 + {
112 + results.emplace(results.begin(), &begin.data()[i], endIndex.value() - i);
113 + endIndex.reset();
114 + }
115 + else
116 + {
117 + endIndex = i - 1;
118 + }
119 + }
120 + }
121 +
122 + if (results.size() < Count)
123 + {
124 + // May have lost some data, or this could be the very first line logged.
125 + if (!partial.empty())
126 + {
127 + results.emplace(results.begin(), partial);
128 + }
129 + else if (endIndex.has_value())
130 + {
131 + results.emplace(results.begin(), &begin[0], endIndex.value());
132 + }
133 + }
134 +
135 + return results;
136 +}
137 +
138 +std::string RingBuffer::Get() const
139 +{
140 + auto lock = m_lock.lock_shared();
141 + auto [begin, end] = Contents();
142 + std::string data;
143 + data.reserve(begin.size() + end.size());
144 + data.append(begin.data(), begin.size());
145 + data.append(end.data(), end.size());
146 + return data;
147 +}
148 +
149 +std::pair<std::string_view, std::string_view> RingBuffer::Contents() const
150 +{
151 + std::string_view beginView(m_buffer.data() + m_offset, m_buffer.size() - m_offset);
152 + std::string_view endView(m_buffer.data(), m_offset);
153 + return {beginView, endView};
154 }
\ No newline at end of file
src/windows/common/WslCoreHostDnsInfo.h
+92 -92
@@ -1,93 +1,93 @@
1 -// Copyright (C) Microsoft Corporation. All rights reserved.
2 -
3 -#pragma once
4 -#include <string>
5 -#include <vector>
6 -
7 -#include <iptypes.h>
8 -#include <wil/registry.h>
9 -
10 -#include "WslCoreNetworkingSupport.h"
11 -#include "RegistryWatcher.h"
12 -
13 -namespace wsl::core::networking {
14 -struct DnsInfo
15 -{
16 - std::vector<std::string> Servers;
17 - std::vector<std::string> Domains;
18 -};
19 -
20 -enum class DnsSettingsFlags
21 -{
22 - None = 0x0,
23 - IncludeVpn = 0x1,
24 - IncludeIpv6Servers = 0x2,
25 - IncludeAllSuffixes = 0x4
26 -};
27 -DEFINE_ENUM_FLAG_OPERATORS(DnsSettingsFlags);
28 -
29 -inline bool operator==(const DnsInfo& lhs, const DnsInfo& rhs) noexcept
30 -{
31 - return lhs.Servers == rhs.Servers && lhs.Domains == rhs.Domains;
32 -}
33 -inline bool operator!=(const DnsInfo& lhs, const DnsInfo& rhs) noexcept
34 -{
35 - return !(lhs == rhs);
36 -}
37 -
38 -std::string GenerateResolvConf(_In_ const DnsInfo& Info);
39 -
40 -/// <summary>
41 -/// Builds an hns::DNS notification from DnsInfo settings.
42 -/// </summary>
43 -/// <param name="settings">The DNS settings to convert</param>
44 -/// <param name="options">The resolv.conf header options (defaults to LX_INIT_RESOLVCONF_FULL_HEADER)</param>
45 -/// <returns>The hns::DNS notification ready to send via GNS channel</returns>
46 -wsl::shared::hns::DNS BuildDnsNotification(const DnsInfo& settings, PCWSTR options = LX_INIT_RESOLVCONF_FULL_HEADER);
47 -
48 -std::vector<std::string> GetAllDnsSuffixes(const std::vector<IpAdapterAddress>& AdapterAddresses);
49 -
50 -DWORD GetBestInterface();
51 -
52 -class HostDnsInfo
53 -{
54 -public:
55 - static DnsInfo GetDnsSettings(_In_ DnsSettingsFlags Flags);
56 -
57 - static DnsInfo GetDnsTunnelingSettings(const std::wstring& dnsTunnelingNameserver);
58 -
59 -private:
60 - /// <summary>
61 - /// Internal function to retrieve interface DNS servers.
62 - /// </summary>
63 - static std::vector<std::string> GetInterfaceDnsServers(const std::vector<IpAdapterAddress>& AdapterAddresses, _In_ DnsSettingsFlags Flags);
64 -
65 - /// <summary>
66 - /// Internal function to retrieve all Windows DNS suffixes.
67 - /// </summary>
68 - static std::vector<std::string> GetInterfaceDnsSuffixes(const std::vector<IpAdapterAddress>& AdapterAddresses);
69 -
70 - /// <summary>
71 - /// Internal function to convert DNS server addresses into strings.
72 - /// </summary>
73 - static std::vector<std::string> GetDnsServerStrings(_In_ const PIP_ADAPTER_DNS_SERVER_ADDRESS& DnsServer, _In_ USHORT IpFamilyFilter, _In_ USHORT MaxValues);
74 -};
75 -
76 -using RegistryChangeCallback = std::function<void()>;
77 -
78 -/// <summary>
79 -/// Class used to get notifications when Windows DNS suffixes are updated in registry.
80 -/// </summary>
81 -class DnsSuffixRegistryWatcher
82 -{
83 -public:
84 - DnsSuffixRegistryWatcher(RegistryChangeCallback&& reportRegistryChange);
85 - ~DnsSuffixRegistryWatcher() noexcept = default;
86 -
87 -private:
88 - RegistryChangeCallback m_reportRegistryChange;
89 -
90 - std::vector<wistd::unique_ptr<wsl::windows::common::slim_registry_watcher>> m_registryWatchers;
91 -};
92 -
1 +// Copyright (C) Microsoft Corporation. All rights reserved.
2 +
3 +#pragma once
4 +#include <string>
5 +#include <vector>
6 +
7 +#include <iptypes.h>
8 +#include <wil/registry.h>
9 +
10 +#include "WslCoreNetworkingSupport.h"
11 +#include "RegistryWatcher.h"
12 +
13 +namespace wsl::core::networking {
14 +struct DnsInfo
15 +{
16 + std::vector<std::string> Servers;
17 + std::vector<std::string> Domains;
18 +};
19 +
20 +enum class DnsSettingsFlags
21 +{
22 + None = 0x0,
23 + IncludeVpn = 0x1,
24 + IncludeIpv6Servers = 0x2,
25 + IncludeAllSuffixes = 0x4
26 +};
27 +DEFINE_ENUM_FLAG_OPERATORS(DnsSettingsFlags);
28 +
29 +inline bool operator==(const DnsInfo& lhs, const DnsInfo& rhs) noexcept
30 +{
31 + return lhs.Servers == rhs.Servers && lhs.Domains == rhs.Domains;
32 +}
33 +inline bool operator!=(const DnsInfo& lhs, const DnsInfo& rhs) noexcept
34 +{
35 + return !(lhs == rhs);
36 +}
37 +
38 +std::string GenerateResolvConf(_In_ const DnsInfo& Info);
39 +
40 +/// <summary>
41 +/// Builds an hns::DNS notification from DnsInfo settings.
42 +/// </summary>
43 +/// <param name="settings">The DNS settings to convert</param>
44 +/// <param name="options">The resolv.conf header options (defaults to LX_INIT_RESOLVCONF_FULL_HEADER)</param>
45 +/// <returns>The hns::DNS notification ready to send via GNS channel</returns>
46 +wsl::shared::hns::DNS BuildDnsNotification(const DnsInfo& settings, PCWSTR options = LX_INIT_RESOLVCONF_FULL_HEADER);
47 +
48 +std::vector<std::string> GetAllDnsSuffixes(const std::vector<IpAdapterAddress>& AdapterAddresses);
49 +
50 +DWORD GetBestInterface();
51 +
52 +class HostDnsInfo
53 +{
54 +public:
55 + static DnsInfo GetDnsSettings(_In_ DnsSettingsFlags Flags);
56 +
57 + static DnsInfo GetDnsTunnelingSettings(const std::wstring& dnsTunnelingNameserver);
58 +
59 +private:
60 + /// <summary>
61 + /// Internal function to retrieve interface DNS servers.
62 + /// </summary>
63 + static std::vector<std::string> GetInterfaceDnsServers(const std::vector<IpAdapterAddress>& AdapterAddresses, _In_ DnsSettingsFlags Flags);
64 +
65 + /// <summary>
66 + /// Internal function to retrieve all Windows DNS suffixes.
67 + /// </summary>
68 + static std::vector<std::string> GetInterfaceDnsSuffixes(const std::vector<IpAdapterAddress>& AdapterAddresses);
69 +
70 + /// <summary>
71 + /// Internal function to convert DNS server addresses into strings.
72 + /// </summary>
73 + static std::vector<std::string> GetDnsServerStrings(_In_ const PIP_ADAPTER_DNS_SERVER_ADDRESS& DnsServer, _In_ USHORT IpFamilyFilter, _In_ USHORT MaxValues);
74 +};
75 +
76 +using RegistryChangeCallback = std::function<void()>;
77 +
78 +/// <summary>
79 +/// Class used to get notifications when Windows DNS suffixes are updated in registry.
80 +/// </summary>
81 +class DnsSuffixRegistryWatcher
82 +{
83 +public:
84 + DnsSuffixRegistryWatcher(RegistryChangeCallback&& reportRegistryChange);
85 + ~DnsSuffixRegistryWatcher() noexcept = default;
86 +
87 +private:
88 + RegistryChangeCallback m_reportRegistryChange;
89 +
90 + std::vector<wistd::unique_ptr<wsl::windows::common::slim_registry_watcher>> m_registryWatchers;
91 +};
92 +
93 } // namespace wsl::core::networking
\ No newline at end of file
test/windows/InstallerTests.cpp
-91
@@ -1089,95 +1089,4 @@ class InstallerTests
1089 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);
1090 VerifyWslSettingsProtocolAssociationExistsWithRetry();
1091 }
1092 -
1093 - /*
1094 - TODO: Uncomment when the functionality is implemented in the SDK.
1095 - TEST_METHOD(WSLCInstall)
1096 - {
1097 - auto expectComponents = [](WslInstallComponent expected) {
1098 - WslInstallComponent components{};
1099 - VERIFY_SUCCEEDED(WslQueryMissingComponents(&components));
1100 -
1101 - VERIFY_ARE_EQUAL(components, expected);
1102 - };
1103 -
1104 - VERIFY_ARE_EQUAL(WslInstallComponents(WslInstallComponentWslPackage, nullptr, nullptr), E_INVALIDARG);
1105 -
1106 - // TODO: remove once 2.7.0 is released.
1107 - RegistryKeyChange<std::wstring> version(HKEY_LOCAL_MACHINE, LXSS_REGISTRY_PATH "\\MSI", L"Version", L"2.7.0");
1108 -
1109 - expectComponents(WslInstallComponentNone);
1110 -
1111 - // Validate that a package < 2.7 is handled correctly.
1112 - {
1113 - version.Set(L"2.6.0");
1114 - expectComponents(WslInstallComponentWslPackage);
1115 - }
1116 -
1117 - version.Set(L"2.7.0");
1118 -
1119 - // Validate that a missing package is detected.
1120 - expectComponents(WslInstallComponentNone);
1121 - UninstallMsi();
1122 -
1123 - expectComponents(WslInstallComponentWslPackage);
1124 -
1125 - {
1126 - UniqueWebServer fileServer(L"http://127.0.0.1:12346/", std::filesystem::path(m_msiPath));
1127 - VERIFY_SUCCEEDED(WslSetPackageUrl(L"http://127.0.0.1:12346/"));
1128 -
1129 - WslInstallComponent progressedComponents{};
1130 - auto callback = [](WslInstallComponent Component, uint64_t progress, uint64_t total, void* Context) {
1131 - *reinterpret_cast<WslInstallComponent*>(Context) |= Component;
1132 - };
1133 -
1134 - VERIFY_SUCCEEDED(WslInstallComponents(WslInstallComponentWslPackage, callback, &progressedComponents));
1135 - VERIFY_ARE_EQUAL(progressedComponents, WslInstallComponentWslPackage);
1136 -
1137 - ValidateInstalledVersion(WIDEN(WSL_PACKAGE_VERSION));
1138 - version.Set(L"2.7.0");
1139 -
1140 - expectComponents(WslInstallComponentNone);
1141 -
1142 - progressedComponents = WslInstallComponentNone;
1143 - VERIFY_ARE_EQUAL(WslInstallComponents(WslInstallComponentVMPOC, callback, &progressedComponents),
1144 - HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED)); VERIFY_ARE_EQUAL(progressedComponents, WslInstallComponentVMPOC);
1145 -
1146 - progressedComponents = WslInstallComponentNone;
1147 - VERIFY_ARE_EQUAL(WslInstallComponents(WslInstallComponentWslOC, callback, &progressedComponents),
1148 - HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED)); VERIFY_ARE_EQUAL(progressedComponents, WslInstallComponentWslOC);
1149 - }
1150 -
1151 - {
1152 - VERIFY_SUCCEEDED(WslSetPackageUrl(L"http://127.0.0.1:12346/"));
1153 - VERIFY_ARE_EQUAL(WslInstallComponents(WslInstallComponentWslPackage, nullptr, nullptr), WININET_E_CANNOT_CONNECT);
1154 - }
1155 - }
1156 -
1157 - // This test case requires a machine without the OC's enabled.
1158 - TEST_METHOD(WSLCInstallManual)
1159 - {
1160 - WslInstallComponent components{};
1161 - VERIFY_SUCCEEDED(WslQueryMissingComponents(&components));
1162 -
1163 - if (!WI_IsAnyFlagSet(components, WslInstallComponentWslOC | WslInstallComponentVMPOC))
1164 - {
1165 - LogSkipped("OC are installed, skipping test. Flags: %i", components);
1166 - return;
1167 - }
1168 -
1169 - auto expectedComponents = WslInstallComponentVMPOC;
1170 - WI_SetFlagIf(expectedComponents, WslInstallComponentWslOC, !wsl::windows::common::helpers::IsWindows11OrAbove());
1171 -
1172 - VERIFY_ARE_EQUAL(components, expectedComponents);
1173 -
1174 - WslInstallComponent progressedComponents{};
1175 - auto callback = [](WslInstallComponent Component, uint64_t progress, uint64_t total, void* Context) {
1176 - *reinterpret_cast<WslInstallComponent*>(Context) |= Component;
1177 - };
1178 -
1179 - VERIFY_ARE_EQUAL(WslInstallComponents(components, callback, &progressedComponents),
1180 - HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED)); VERIFY_ARE_EQUAL(progressedComponents, expectedComponents);
1181 - }
1182 - */
1092 };
\ No newline at end of file