master
cpp 655 lines 21.9 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 helpers.cpp
8
9 Abstract:
10
11 This file contains helper function definitions.
12
13 --*/
14
15 #include "precomp.h"
16 #include "helpers.hpp"
17 #include "svccomm.hpp"
18 #include "socket.hpp"
19 #include "hvsocket.hpp"
20 #include "relay.hpp"
21 #include "localhost.h"
22 #include <gsl/algorithm>
23 #include <gslhelpers.h>
24
25 #define LOCALHOST_RELAY_BUFFER_SIZE (0x20000)
26
27 struct in6_addr_linux
28 {
29 union
30 {
31 uint8_t addr[16];
32 uint32_t addr32[4];
33 } u;
34 };
35
36 const uint32_t ADDR6_MASK3 = ~in6_addr_linux(IN6ADDR_LOOPBACK_INIT).u.addr32[3];
37 const uint32_t N_ADDR_LOOPBACK = ntohl(INADDR_LOOPBACK);
38 const uint32_t N_ADDR_ANY = ntohl(INADDR_ANY);
39
40 static VOID PortListenerAsync(_Inout_ std::shared_ptr<LX_PORT_LISTENER_THREAD_CONTEXT> Arguments);
41 static wil::unique_socket BindRelayListener(ADDRESS_FAMILY const Family, USHORT const Port);
42
43 static int GetPortListener(_In_ wsl::shared::SocketChannel& Channel)
44 {
45 const auto& GuestAgentInfo = Channel.ReceiveMessage<LX_GNS_SET_PORT_LISTENER>();
46 return GuestAgentInfo.HvSocketPort;
47 }
48
49 static int WindowsAddressFamily(int LinuxAddressFamily)
50 {
51 switch (LinuxAddressFamily)
52 {
53 case LX_AF_INET:
54 return AF_INET;
55
56 case LX_AF_INET6:
57 return AF_INET6;
58 }
59 WSL_LOG("PortRelayBindFamily", TraceLoggingValue(LinuxAddressFamily, "LinuxAddressFamily"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
60
61 THROW_HR(E_INVALIDARG);
62 }
63
64 bool BindsLocalhost(int Af, uint32_t const* Address)
65 {
66 switch (Af)
67 {
68 case LX_AF_INET:
69 return Address[0] == N_ADDR_ANY || Address[0] == N_ADDR_LOOPBACK;
70 case LX_AF_INET6:
71 return (Address[0] | Address[1] | Address[2] | (Address[3] & ADDR6_MASK3)) == 0;
72 }
73 return false;
74 }
75
76 void wsl::windows::wslrelay::localhost::RelayWorker(_In_ wsl::shared::SocketChannel& Channel, _In_ const GUID& VmId)
77 {
78 std::vector<gsl::byte> Buffer;
79 Relay Relay;
80 const int HvSocketPort = GetPortListener(Channel);
81
82 for (;;)
83 {
84 auto Transaction = Channel.ReceiveTransaction();
85 auto [Message, Span] = Transaction.ReceiveOrClosed<MESSAGE_HEADER>();
86 if (Message == nullptr)
87 {
88 break;
89 }
90
91 switch (Message->MessageType)
92 {
93 case LxGnsMessagePortListenerRelayStart:
94 case LxGnsMessagePortListenerRelayStop:
95 {
96 const auto* RelayOperation = gslhelpers::try_get_struct<LX_GNS_PORT_LISTENER_RELAY>(Span);
97 THROW_HR_IF(E_INVALIDARG, !RelayOperation);
98 // Ignore non-localhost addresses.
99 if (WindowsAddressFamily(RelayOperation->Family) == AF_INET)
100 {
101 if (RelayOperation->Address[0] != INADDR_ANY && RelayOperation->Address[0] != htonl(INADDR_LOOPBACK))
102 {
103 continue;
104 }
105 }
106 else
107 {
108 WI_ASSERT(WindowsAddressFamily(RelayOperation->Family) == AF_INET6);
109 bool IgnorePort = false;
110 for (int Part = 0; Part < 3; ++Part)
111 {
112 if (RelayOperation->Address[Part] != 0)
113 {
114 IgnorePort = true;
115 break;
116 }
117 }
118 // Create relays for unspecified (any) or loopback Ipv6 addresses.
119 if (IgnorePort || ntohl(RelayOperation->Address[3]) > 1)
120 {
121 continue;
122 }
123 }
124 if (Message->MessageType == LxGnsMessagePortListenerRelayStart)
125 {
126 Relay.StartPortListener(VmId, RelayOperation->Family, RelayOperation->Port, HvSocketPort);
127 }
128 else
129 {
130 Relay.StopPortListener(RelayOperation->Family, RelayOperation->Port);
131 }
132 break;
133 }
134 case LxGnsMessagePortMappingRequest:
135 {
136 // TODO: handle UDP binds
137 const auto* RelayOperation = gslhelpers::try_get_struct<LX_GNS_PORT_ALLOCATION_REQUEST>(Span);
138 THROW_HR_IF(E_INVALIDARG, !RelayOperation);
139 RESULT_MESSAGE<int32_t> Response = {};
140 Response.Header.MessageType = decltype(Response)::Type;
141 Response.Header.MessageSize = sizeof(Response);
142
143 if (RelayOperation->Protocol == IPPROTO_TCP && BindsLocalhost(RelayOperation->Af, RelayOperation->Address32))
144 {
145 if (RelayOperation->Allocate)
146 {
147 Response.Result = Relay.StartPortListener(VmId, RelayOperation->Af, RelayOperation->Port, HvSocketPort);
148 }
149 else
150 {
151 Relay.StopPortListener(RelayOperation->Af, RelayOperation->Port);
152 }
153 }
154
155 Transaction.Send(Response);
156 break;
157 }
158
159 default:
160 THROW_HR_MSG(E_UNEXPECTED, "Unexpected message %d", Message->MessageType);
161 }
162 }
163 }
164
165 wsl::windows::wslrelay::localhost::Relay::~Relay()
166 {
167 // Iterate through each relay and set the exit event and wait for all worker threads to finish.
168 auto lock = m_lock.lock_exclusive();
169 for (auto const& entry : m_RelayThreads)
170 {
171 entry.second->ThreadContext->ExitEvent.SetEvent();
172 }
173
174 m_RelayThreads.clear();
175 }
176
177 int wsl::windows::wslrelay::localhost::Relay::StartPortListener(_In_ const GUID& VmId, _In_ unsigned short Family, _In_ unsigned short Port, _In_ int HvSocketPort)
178 try
179 {
180 auto lock = m_lock.lock_exclusive();
181 if (const auto iter = m_RelayThreads.find({Family, Port}); iter != m_RelayThreads.end())
182 {
183 iter->second->ThreadContext->Count++;
184 return 0;
185 }
186
187 // Create a worker thread to service the port relay.
188 //
189 // N.B. The worker thread takes ownership of the arguments pointer.
190 const auto Arguments = std::make_shared<LX_PORT_LISTENER_CONTEXT>();
191 Arguments->ThreadContext = std::make_shared<LX_PORT_LISTENER_THREAD_CONTEXT>();
192 Arguments->ThreadContext->VmId = VmId;
193 Arguments->ThreadContext->Family = Family;
194 Arguments->ThreadContext->Port = Port;
195 Arguments->ThreadContext->HvSocketPort = HvSocketPort;
196 Arguments->ThreadContext->ExitEvent.create(wil::EventOptions::ManualReset);
197 Arguments->ThreadContext->ListenSocket = BindRelayListener(Family, Port);
198 Arguments->Worker = std::thread(PortListenerAsync, Arguments->ThreadContext);
199
200 m_RelayThreads[{Family, Port}] = Arguments;
201
202 return 0;
203 }
204 catch (...)
205 {
206 LOG_CAUGHT_EXCEPTION();
207 switch (wil::ResultFromCaughtException())
208 {
209 case HRESULT_FROM_WIN32(WSAEADDRINUSE):
210 case HRESULT_FROM_WIN32(WSAEACCES): // TODO: Remap and handle this next to the bind call.
211 return -LX_EADDRINUSE;
212 default:
213 return -LX_EINVAL;
214 }
215 }
216
217 void wsl::windows::wslrelay::localhost::Relay::StopPortListener(_In_ unsigned short Family, _In_ unsigned short Port)
218 {
219 try
220 {
221 // Search through the worker threads and terminate any that match the address family and port.
222 auto lock = m_lock.lock_exclusive();
223 const auto iter = m_RelayThreads.find({Family, Port});
224 if (iter != m_RelayThreads.end())
225 {
226 iter->second->ThreadContext->Count--;
227 if (iter->second->ThreadContext->Count <= 0)
228 {
229 iter->second->ThreadContext->ExitEvent.SetEvent();
230 iter->second->Worker.join();
231 m_RelayThreads.erase(iter);
232 WSL_LOG("PortRelayUnBind", TraceLoggingValue(Family, "family"), TraceLoggingValue(Port, "port"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
233 }
234 }
235 }
236 CATCH_LOG()
237 }
238
239 static wil::unique_socket BindRelayListener(ADDRESS_FAMILY const Family, USHORT const Port)
240 {
241
242 // Perform a mapping from Linux address family to Windows.
243 const int AddressFamily = WindowsAddressFamily(Family);
244
245 // Create a listening tcp socket on the specified port.
246
247 wil::unique_socket ListenSocket(WSASocket(AddressFamily, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED));
248
249 THROW_LAST_ERROR_IF(!ListenSocket);
250
251 // Set the SO_REUSEADDR socket option.
252
253 constexpr BOOLEAN On = true;
254 THROW_LAST_ERROR_IF(setsockopt(ListenSocket.get(), SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char*>(&On), sizeof(On)) == SOCKET_ERROR);
255
256 sockaddr* Address;
257 sockaddr_in InetAddress{};
258 sockaddr_in6 Inet6Address{};
259 DWORD AddressSize;
260 if (AddressFamily == AF_INET)
261 {
262 InetAddress.sin_family = AF_INET;
263 InetAddress.sin_port = htons(Port);
264 InetAddress.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
265 Address = reinterpret_cast<sockaddr*>(&InetAddress);
266 AddressSize = sizeof(InetAddress);
267 }
268 else
269 {
270 Inet6Address.sin6_family = AF_INET6;
271 Inet6Address.sin6_port = htons(Port);
272 Inet6Address.sin6_addr = IN6ADDR_LOOPBACK_INIT;
273 Address = reinterpret_cast<sockaddr*>(&Inet6Address);
274 AddressSize = sizeof(Inet6Address);
275 }
276
277 // Start listening on the specified port.
278 // TODO: Catch relevant bind errors WSAEACCES, WSAEADDRINUSE and throw as a new hr that will
279 // end up being emitted by seccomp as EADDRINUSE.
280 THROW_LAST_ERROR_IF(bind(ListenSocket.get(), Address, AddressSize) == SOCKET_ERROR);
281
282 THROW_LAST_ERROR_IF(listen(ListenSocket.get(), -1) == SOCKET_ERROR);
283
284 WSL_LOG("PortRelayBind", TraceLoggingValue(Family, "family"), TraceLoggingValue(Port, "port"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
285
286 return ListenSocket;
287 }
288
289 static VOID PortListenerAsync(_Inout_ std::shared_ptr<LX_PORT_LISTENER_THREAD_CONTEXT> Arguments)
290 try
291 {
292 // Begin accepting connections until the relay is stopped.
293
294 for (;;)
295 {
296 auto InetSocket =
297 wsl::windows::common::socket::CancellableAccept(Arguments->ListenSocket.get(), INFINITE, Arguments->ExitEvent.get());
298 if (!InetSocket)
299 {
300 break; // Exit event was signaled, exit.
301 }
302
303 // Establish a relay thread.
304
305 WSL_LOG("PortRelayUsage", TraceLoggingValue(Arguments->Family, "family"), TraceLoggingValue(Arguments->Port, "port"), TraceLoggingLevel(WINEVENT_LEVEL_INFO));
306
307 auto RelayThread = std::thread([Arguments, InetSocket = std::move(*InetSocket)]() {
308 try
309 {
310 wsl::windows::common::wslutil::SetThreadDescription(L"Port relay");
311 const auto HvSocket = wsl::windows::common::hvsocket::Connect(Arguments->VmId, Arguments->HvSocketPort);
312 LX_INIT_START_SOCKET_RELAY Message{};
313 Message.Header.MessageType = LxInitMessageStartSocketRelay;
314 Message.Header.MessageSize = sizeof(Message);
315 Message.Family = Arguments->Family;
316 Message.Port = Arguments->Port;
317 Message.BufferSize = LOCALHOST_RELAY_BUFFER_SIZE;
318 wsl::windows::common::socket::Send(HvSocket.get(), gslhelpers::struct_as_bytes(Message));
319 wsl::windows::common::relay::SocketRelay(InetSocket.get(), HvSocket.get(), Message.BufferSize);
320 }
321 CATCH_LOG()
322 });
323
324 RelayThread.detach();
325 }
326 }
327 CATCH_LOG()
328
329 struct PortRelay
330 {
331 wil::unique_socket ListenSocket;
332 uint32_t LinuxPort;
333 uint32_t RelayPort;
334 wil::unique_event AcceptEvent{wil::EventOptions::None};
335 OVERLAPPED Overlapped{};
336 bool Pending = false;
337 wil::unique_socket PendingSocket;
338 int Family;
339 CHAR AcceptBuffer[2 * sizeof(SOCKADDR_STORAGE)]{};
340
341 PortRelay(wil::unique_socket&& ListenSocket, uint32_t LinuxPort, uint32_t RelayPort, int Family) :
342 ListenSocket(std::move(ListenSocket)), LinuxPort(LinuxPort), RelayPort(RelayPort), Family(Family)
343 {
344 Overlapped.hEvent = AcceptEvent.get();
345 }
346
347 ~PortRelay()
348 {
349 if (Pending) // Cancel pending accept(), if any.
350 {
351 DWORD bytesProcessed;
352 DWORD flagsReturned;
353 CancelIoEx(reinterpret_cast<HANDLE>(ListenSocket.get()), &Overlapped);
354 WSAGetOverlappedResult(ListenSocket.get(), &Overlapped, &bytesProcessed, TRUE, &flagsReturned);
355 }
356 }
357
358 void LaunchRelay(const GUID& VmId)
359 {
360 WI_VERIFY(PendingSocket);
361
362 std::thread thread{
363 [WindowsSocket = std::move(PendingSocket), LinuxPort = LinuxPort, RelayPort = RelayPort, Family = Family, VmId = VmId]() {
364 try
365 {
366 WSL_LOG(
367 "StartPortRelay",
368 TraceLoggingValue(LinuxPort, "LinuxPort"),
369 TraceLoggingValue(WindowsSocket.get(), "Socket"),
370 TraceLoggingValue(Family, "Family"));
371
372 RunRelay(WindowsSocket.get(), VmId, LinuxPort, RelayPort, Family);
373 }
374 CATCH_LOG();
375
376 WSL_LOG(
377 "StopPortRelay",
378 TraceLoggingValue(LinuxPort, "LinuxPort"),
379 TraceLoggingValue(WindowsSocket.get(), "Socket"),
380 TraceLoggingValue(Family, "Family"));
381 }};
382
383 thread.detach();
384 }
385
386 static void RunRelay(SOCKET WindowsSocket, const GUID& VmId, uint32_t LinuxPort, uint32_t RelayPort, uint32_t Family)
387 {
388 wsl::shared::SocketChannel channel(wsl::windows::common::hvsocket::Connect(VmId, RelayPort), "SocketRelay");
389
390 WI_VERIFY(Family == AF_INET || Family == AF_INET6);
391 LX_INIT_START_SOCKET_RELAY message{};
392 message.Port = LinuxPort;
393 message.Family = Family == AF_INET ? LX_AF_INET : LX_AF_INET6;
394 message.BufferSize = LOCALHOST_RELAY_BUFFER_SIZE;
395 channel.SendMessage(message);
396
397 wsl::windows::common::relay::SocketRelay(WindowsSocket, channel.Socket(), message.BufferSize);
398 }
399
400 // Completes the overlapped AcceptEx initiated by ScheduleAccept, and sets accept context on the accepted socket.
401 void CompleteAccept()
402 {
403 Pending = false;
404
405 DWORD bytes{};
406 DWORD flags{};
407 if (!WSAGetOverlappedResult(ListenSocket.get(), &Overlapped, &bytes, false, &flags))
408 {
409 THROW_WIN32(WSAGetLastError());
410 }
411
412 // Set the accept context to mark the socket as connected.
413 wsl::windows::common::socket::SetAcceptContext(PendingSocket.get(), ListenSocket.get());
414 }
415
416 // If the accept completes immediately, accept context will be set on the accepted socket.
417 bool ScheduleAccept()
418 {
419 WI_VERIFY(!Pending);
420
421 PendingSocket.reset(WSASocket(Family, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED));
422 memset(AcceptBuffer, 0, sizeof(AcceptBuffer));
423 DWORD BytesReturned{};
424 if (!AcceptEx(ListenSocket.get(), PendingSocket.get(), AcceptBuffer, 0, sizeof(SOCKADDR_STORAGE), sizeof(SOCKADDR_STORAGE), &BytesReturned, &Overlapped))
425 {
426 const int error = WSAGetLastError();
427 THROW_HR_IF(HRESULT_FROM_WIN32(error), error != WSA_IO_PENDING);
428
429 Pending = true;
430 return false;
431 }
432
433 // Set the accept context to mark the socket as connected.
434 wsl::windows::common::socket::SetAcceptContext(PendingSocket.get(), ListenSocket.get());
435 return true;
436 }
437 };
438
439 std::shared_ptr<PortRelay> CreatePortListener(uint16_t WindowsPort, uint16_t LinuxPort, uint32_t RelayPort, int Family)
440 {
441 wil::unique_socket ListenSocket(WSASocket(Family, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED));
442
443 THROW_LAST_ERROR_IF(!ListenSocket);
444
445 constexpr BOOLEAN On = true;
446 THROW_LAST_ERROR_IF(setsockopt(ListenSocket.get(), SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char*>(&On), sizeof(On)) == SOCKET_ERROR);
447
448 sockaddr* Address{};
449 sockaddr_in InetAddress{};
450 sockaddr_in6 Inet6Address{};
451 DWORD AddressSize{};
452 if (Family == AF_INET)
453 {
454 InetAddress.sin_family = AF_INET;
455 InetAddress.sin_port = htons(WindowsPort);
456 InetAddress.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
457 Address = reinterpret_cast<sockaddr*>(&InetAddress);
458 AddressSize = sizeof(InetAddress);
459 }
460 else
461 {
462 Inet6Address.sin6_family = AF_INET6;
463 Inet6Address.sin6_port = htons(WindowsPort);
464 Inet6Address.sin6_addr = IN6ADDR_LOOPBACK_INIT;
465 Address = reinterpret_cast<sockaddr*>(&Inet6Address);
466 AddressSize = sizeof(Inet6Address);
467 }
468
469 THROW_LAST_ERROR_IF(bind(ListenSocket.get(), Address, AddressSize) == SOCKET_ERROR);
470 THROW_LAST_ERROR_IF(listen(ListenSocket.get(), -1) == SOCKET_ERROR);
471
472 return std::make_shared<PortRelay>(std::move(ListenSocket), LinuxPort, RelayPort, Family);
473 }
474
475 void AcceptThread(std::vector<std::shared_ptr<PortRelay>>& ports, const GUID& VmId, HANDLE ExitEvent)
476 {
477 while (true)
478 {
479 // First make sure that all the accept() are scheduled
480 std::vector<HANDLE> events{ExitEvent};
481 for (auto& e : ports)
482 {
483 if (!e->Pending)
484 {
485 while (e->ScheduleAccept())
486 {
487 e->LaunchRelay(VmId); // Start the relay if accept completes immediately.
488 }
489 }
490
491 events.push_back(e->AcceptEvent.get());
492 }
493
494 // WaitForMultipleObjects supports at most MAXIMUM_WAIT_OBJECTS (64) handles.
495 auto result = WaitForMultipleObjects(static_cast<DWORD>(events.size()), events.data(), false, INFINITE);
496 THROW_LAST_ERROR_IF(result == WAIT_FAILED);
497
498 if (result == 0) // If the exit event is signaled, leave the loop
499 {
500 break;
501 }
502
503 // Otherwise complete the accept and start a relay
504 try
505 {
506 ports[result - 1]->CompleteAccept();
507 ports[result - 1]->LaunchRelay(VmId);
508 }
509 CATCH_LOG();
510 }
511 }
512
513 std::optional<WSLC_MAP_PORT> ReceiveServiceMessage()
514 {
515 WSLC_MAP_PORT message{};
516
517 DWORD bytesRead{};
518 if (!ReadFile(GetStdHandle(STD_INPUT_HANDLE), &message, sizeof(message), &bytesRead, nullptr))
519 {
520 LOG_LAST_ERROR();
521 return {};
522 }
523 else if (bytesRead == 0)
524 {
525 return {};
526 }
527
528 WI_ASSERT(message.Header.MessageSize == sizeof(message));
529 WI_ASSERT(message.Header.MessageType == LxMessageWSLCMapPort);
530 return message;
531 }
532
533 void wsl::windows::wslrelay::localhost::RunWSLCPortRelay(const GUID& VmId, uint32_t RelayPort, HANDLE ExitEvent)
534 {
535 std::map<std::tuple<uint16_t, uint32_t>, std::shared_ptr<PortRelay>> ports;
536
537 std::thread acceptThread;
538 wil::unique_event acceptThreadEvent{wil::EventOptions::ManualReset};
539
540 auto stopAcceptThread = [&]() {
541 if (acceptThread.joinable())
542 {
543 acceptThreadEvent.SetEvent();
544 acceptThread.join();
545 acceptThread = {};
546 acceptThreadEvent.ResetEvent();
547 }
548 };
549
550 auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { stopAcceptThread(); });
551
552 while (true)
553 {
554 // Receive a message
555 auto message = ReceiveServiceMessage();
556 if (!message.has_value())
557 {
558 return;
559 }
560
561 std::tuple<uint16_t, uint16_t> key{message->WindowsPort, message->AddressFamily};
562
563 HRESULT result = E_UNEXPECTED;
564 auto sendResponse = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {
565 WSL_LOG(
566 "PortMapping",
567 TraceLoggingValue(result, "Result"),
568 TraceLoggingValue(message->AddressFamily, "Family"),
569 TraceLoggingValue(message->WindowsPort, "WindowsPort"),
570 TraceLoggingValue(message->LinuxPort, "LinuxPort"),
571 TraceLoggingValue(message->Stop, "Remove"));
572
573 THROW_LAST_ERROR_IF(!WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), &result, sizeof(result), nullptr, nullptr));
574 });
575
576 // Check if the binding is valid.
577 bool update = false;
578 auto it = ports.find(key);
579 if (message->Stop)
580 {
581 if (it == ports.end())
582 {
583 result = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
584 continue;
585 }
586 else
587 {
588 ports.erase(it);
589 update = true;
590 }
591 }
592 else
593 {
594 if (it != ports.end())
595 {
596 result = HRESULT_FROM_WIN32(WSAEADDRINUSE);
597 continue;
598 }
599 else
600 {
601 // WaitForMultipleObjects supports at most MAXIMUM_WAIT_OBJECTS (64) handles.
602 // Reject the mapping if adding it would exceed the limit (1 handle reserved for the exit event).
603 constexpr size_t c_maxPorts = MAXIMUM_WAIT_OBJECTS - 1;
604 if (ports.size() >= c_maxPorts)
605 {
606 result = HRESULT_FROM_WIN32(ERROR_TOO_MANY_OPEN_FILES);
607 continue;
608 }
609
610 try
611 {
612 ports.emplace(key, CreatePortListener(message->WindowsPort, message->LinuxPort, RelayPort, message->AddressFamily));
613 update = true;
614 }
615 catch (...)
616 {
617 result = wil::ResultFromCaughtException();
618 if (result == HRESULT_FROM_WIN32(WSAEACCES))
619 {
620 // Translate WSAEACCES to WSAEADDRINUSE to match the virtionet behavior.
621 result = HRESULT_FROM_WIN32(WSAEADDRINUSE);
622 }
623
624 continue;
625 }
626 }
627 }
628
629 // Update the ports list
630 if (update)
631 {
632 stopAcceptThread();
633 }
634
635 // Start the accept thread, if needed
636 if (!acceptThread.joinable())
637 {
638 std::vector<std::shared_ptr<PortRelay>> relays;
639 for (auto& e : ports)
640 {
641 relays.emplace_back(e.second);
642 }
643
644 acceptThread = std::thread([&, relays = std::move(relays)]() mutable {
645 try
646 {
647 AcceptThread(relays, VmId, acceptThreadEvent.get());
648 }
649 CATCH_LOG();
650 });
651 }
652
653 result = S_OK;
654 }
655 }