From f5ed0752e3f0f26139973c37b7fc74b4c4d99ea6 Mon Sep 17 00:00:00 2001 From: Igor Pissolati Date: Tue, 20 May 2025 00:56:22 -0300 Subject: [PATCH 1/6] Handle listen/accept/send/receive in afd_endpoint --- src/common/network/socket.cpp | 32 ++ src/common/network/socket.hpp | 4 + src/common/platform/status.hpp | 1 + src/windows-emulator/devices/afd_endpoint.cpp | 312 +++++++++++++++++- src/windows-emulator/devices/afd_types.hpp | 26 ++ src/windows-emulator/network/i_socket.hpp | 3 + .../network/socket_wrapper.cpp | 26 ++ .../network/socket_wrapper.hpp | 4 + .../network/static_socket_factory.cpp | 15 + 9 files changed, 412 insertions(+), 11 deletions(-) diff --git a/src/common/network/socket.cpp b/src/common/network/socket.cpp index cad5d0b1..f88f60c3 100644 --- a/src/common/network/socket.cpp +++ b/src/common/network/socket.cpp @@ -77,6 +77,33 @@ namespace network return ::bind(this->socket_, &target.get_addr(), target.get_size()) == 0; } + // NOLINTNEXTLINE(readability-make-member-function-const) + bool socket::listen(int backlog) + { + int result = ::listen(this->socket_, backlog); + if (result == 0) + { + listening_ = true; + return true; + } + return false; + } + + // NOLINTNEXTLINE(readability-make-member-function-const) + SOCKET socket::accept(address& address) + { + sockaddr addr{}; + int addrlen = sizeof(sockaddr); + const auto s = ::accept(this->socket_, &addr, &addrlen); + + if (s != INVALID_SOCKET) + { + address.set_address(&addr, addrlen); + } + + return s; + } + // NOLINTNEXTLINE(readability-make-member-function-const) bool socket::set_blocking(const bool blocking) { @@ -158,6 +185,11 @@ namespace network return this->is_valid() && is_socket_ready(this->socket_, in_poll); } + bool socket::is_listening() const + { + return this->is_valid() && listening_; + } + bool socket::sleep_sockets(const std::span& sockets, const std::chrono::milliseconds timeout, const bool in_poll) { diff --git a/src/common/network/socket.hpp b/src/common/network/socket.hpp index bd941364..602267ae 100644 --- a/src/common/network/socket.hpp +++ b/src/common/network/socket.hpp @@ -47,6 +47,8 @@ namespace network bool is_valid() const; bool bind(const address& target); + bool listen(int backlog); + SOCKET accept(address& address); bool set_blocking(bool blocking); static bool set_blocking(SOCKET s, bool blocking); @@ -62,6 +64,7 @@ namespace network int get_address_family() const; bool is_ready(bool in_poll) const; + bool is_listening() const; static bool sleep_sockets(const std::span& sockets, std::chrono::milliseconds timeout, bool in_poll); @@ -74,5 +77,6 @@ namespace network private: SOCKET socket_ = INVALID_SOCKET; + bool listening_{}; }; } diff --git a/src/common/platform/status.hpp b/src/common/platform/status.hpp index ce3b27f3..9c9b8727 100644 --- a/src/common/platform/status.hpp +++ b/src/common/platform/status.hpp @@ -43,6 +43,7 @@ using NTSTATUS = std::uint32_t; #define STATUS_FILE_IS_A_DIRECTORY ((NTSTATUS)0xC00000BAL) #define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BBL) #define STATUS_INVALID_ADDRESS ((NTSTATUS)0xC0000141L) +#define STATUS_CONNECTION_RESET ((NTSTATUS)0xC000020DL) #define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L) #define STATUS_CONNECTION_REFUSED ((NTSTATUS)0xC0000236L) #define STATUS_TIMER_RESOLUTION_NOT_SET ((NTSTATUS)0xC0000245L) diff --git a/src/windows-emulator/devices/afd_endpoint.cpp b/src/windows-emulator/devices/afd_endpoint.cpp index 5263dd82..e72f4ed2 100644 --- a/src/windows-emulator/devices/afd_endpoint.cpp +++ b/src/windows-emulator/devices/afd_endpoint.cpp @@ -259,11 +259,6 @@ namespace socket_events |= POLLRDNORM; } - if (poll_events & AFD_POLL_RECEIVE_EXPEDITED) - { - socket_events |= POLLRDNORM; - } - if (poll_events & AFD_POLL_RECEIVE_EXPEDITED) { socket_events |= POLLRDBAND; @@ -277,13 +272,13 @@ namespace return socket_events; } - ULONG map_socket_response_events_to_afd(const int16_t socket_events) + ULONG map_socket_response_events_to_afd(const int16_t socket_events, const bool is_listener) { ULONG afd_events = 0; if (socket_events & POLLRDNORM) { - afd_events |= (AFD_POLL_ACCEPT | AFD_POLL_RECEIVE); + afd_events |= is_listener ? AFD_POLL_ACCEPT : AFD_POLL_RECEIVE; } if (socket_events & POLLRDBAND) @@ -350,11 +345,10 @@ namespace } auto entry = handle_info_obj.read(i); - entry.PollEvents = map_socket_response_events_to_afd(pfd.revents); + entry.PollEvents = map_socket_response_events_to_afd(pfd.revents, pfd.s->is_listening()); entry.Status = STATUS_SUCCESS; handle_info_obj.write(entry, current_index++); - break; } assert(current_index == static_cast(count)); @@ -369,6 +363,12 @@ namespace struct afd_endpoint : io_device { + struct pending_connection + { + network::address remote_address; + std::unique_ptr accepted_socket; + }; + std::unique_ptr s_{}; bool executing_delayed_ioctl_{}; @@ -376,6 +376,8 @@ namespace std::optional require_poll_{}; std::optional delayed_ioctl_{}; std::optional timeout_{}; + std::unordered_map pending_connections_{}; + LONG next_sequence_{0}; afd_endpoint() { @@ -501,14 +503,32 @@ namespace return STATUS_NOT_SUPPORTED; } - win_emu.log.print(color::dark_gray, "--> AFD IOCTL: %X\n", c.io_control_code); + if (this->delayed_ioctl_) + { + if (auto* e = win_emu.process.events.get(c.event)) + { + e->signaled = false; + } + } const auto request = _AFD_REQUEST(c.io_control_code); + win_emu.log.print(color::dark_gray, "--> AFD IOCTL: %X (%X)\n", c.io_control_code, request); + switch (request) { case AFD_BIND: return this->ioctl_bind(win_emu, c); + case AFD_START_LISTEN: + return this->ioctl_listen(win_emu, c); + case AFD_WAIT_FOR_LISTEN: + return this->ioctl_wait_for_listen(win_emu, c); + case AFD_ACCEPT: + return this->ioctl_accept(win_emu, c); + case AFD_SEND: + return this->ioctl_send(win_emu, c); + case AFD_RECEIVE: + return this->ioctl_receive(win_emu, c); case AFD_SEND_DATAGRAM: return this->ioctl_send_datagram(win_emu, c); case AFD_RECEIVE_DATAGRAM: @@ -517,9 +537,11 @@ namespace return this->ioctl_poll(win_emu, c); case AFD_SET_CONTEXT: case AFD_GET_INFORMATION: + case AFD_SET_INFORMATION: + case AFD_QUERY_HANDLES: return STATUS_SUCCESS; default: - win_emu.log.print(color::gray, "Unsupported AFD IOCTL: %X\n", c.io_control_code); + win_emu.log.print(color::gray, "Unsupported AFD IOCTL: %X (%X)\n", c.io_control_code, request); return STATUS_NOT_SUPPORTED; } } @@ -550,6 +572,274 @@ namespace return STATUS_SUCCESS; } + NTSTATUS ioctl_listen(windows_emulator& win_emu, const io_device_context& c) const + { + if (!this->s_) + { + throw std::runtime_error("Invalid AFD endpoint socket!"); + } + + if (c.input_buffer_length < sizeof(AFD_LISTEN_INFO)) + { + return STATUS_BUFFER_TOO_SMALL; + } + + const auto listen_info = win_emu.emu().read_memory(c.input_buffer); + + if (!this->s_->listen(static_cast(listen_info.MaximumConnectionQueue))) + { + return STATUS_INVALID_PARAMETER; + } + + if (c.io_status_block) + { + IO_STATUS_BLOCK> block{}; + block.Status = STATUS_SUCCESS; + block.Information = 0; + c.io_status_block.write(block); + } + + return STATUS_SUCCESS; + } + + NTSTATUS ioctl_wait_for_listen(windows_emulator& win_emu, const io_device_context& c) + { + if (!this->s_) + { + throw std::runtime_error("Invalid AFD endpoint socket!"); + } + + if (c.output_buffer_length < sizeof(AFD_LISTEN_RESPONSE_INFO)) + { + return STATUS_BUFFER_TOO_SMALL; + } + + network::address remote_address{}; + auto accepted_socket_ptr = this->s_->accept(remote_address); + + if (!accepted_socket_ptr) + { + const auto error = this->s_->get_last_error(); + if (error == SERR(EWOULDBLOCK)) + { + this->delay_ioctrl(c, {}, true); + return STATUS_PENDING; + } + return STATUS_UNSUCCESSFUL; + } + + if (!remote_address.is_ipv4()) + { + throw std::runtime_error("Unsupported address family"); + } + + pending_connection pending{}; + pending.remote_address = remote_address; + pending.accepted_socket = std::move(accepted_socket_ptr); + + LONG sequence = next_sequence_++; + pending_connections_.try_emplace(sequence, std::move(pending)); + + AFD_LISTEN_RESPONSE_INFO response{}; + response.Sequence = sequence; + + auto transport_buffer = convert_to_win_address(win_emu, remote_address); + memcpy(&response.RemoteAddress, transport_buffer.data(), sizeof(win_sockaddr)); + + win_emu.emu().write_memory(c.output_buffer, response); + + if (c.io_status_block) + { + IO_STATUS_BLOCK> block{}; + block.Status = STATUS_SUCCESS; + block.Information = sizeof(AFD_LISTEN_RESPONSE_INFO); + c.io_status_block.write(block); + } + + return STATUS_SUCCESS; + } + + NTSTATUS ioctl_accept(windows_emulator& win_emu, const io_device_context& c) + { + if (!this->s_) + { + throw std::runtime_error("Invalid AFD endpoint socket!"); + } + + if (c.input_buffer_length < sizeof(AFD_ACCEPT_INFO)) + { + return STATUS_BUFFER_TOO_SMALL; + } + + const auto accept_info = win_emu.emu().read_memory(c.input_buffer); + + const auto it = pending_connections_.find(accept_info.Sequence); + if (it == pending_connections_.end()) + { + return STATUS_INVALID_PARAMETER; + } + + auto& accepted_socket = it->second.accepted_socket; + + auto* target_device = win_emu.process.devices.get(accept_info.AcceptHandle); + if (!target_device) + { + return STATUS_INVALID_HANDLE; + } + + auto* target_endpoint = target_device->get_internal_device(); + if (!target_endpoint) + { + return STATUS_INVALID_HANDLE; + } + + target_endpoint->s_ = std::move(accepted_socket); + + pending_connections_.erase(it); + + if (c.io_status_block) + { + IO_STATUS_BLOCK> block{}; + block.Status = STATUS_SUCCESS; + block.Information = 0; + c.io_status_block.write(block); + } + + return STATUS_SUCCESS; + } + + NTSTATUS ioctl_receive(windows_emulator& win_emu, const io_device_context& c) + { + if (!this->s_) + { + throw std::runtime_error("Invalid AFD endpoint socket!"); + } + + auto& emu = win_emu.emu(); + + if (c.input_buffer_length < sizeof(AFD_RECV_INFO>)) + { + return STATUS_BUFFER_TOO_SMALL; + } + + const auto receive_info = emu.read_memory>>(c.input_buffer); + + if (!receive_info.BufferArray || receive_info.BufferCount == 0) + { + return STATUS_INVALID_PARAMETER; + } + + if (receive_info.BufferCount > 1) + { + // TODO: Scatter/Gather + return STATUS_NOT_SUPPORTED; + } + + const auto wsabuf = emu.read_memory>>(receive_info.BufferArray); + if (!wsabuf.buf || wsabuf.len == 0) + { + return STATUS_INVALID_PARAMETER; + } + + std::vector host_buffer; + host_buffer.resize(wsabuf.len); + + const auto bytes_received = this->s_->recv(host_buffer); + + if (bytes_received < 0) + { + const auto error = this->s_->get_last_error(); + if (error == SERR(EWOULDBLOCK)) + { + this->delay_ioctrl(c, {}, true); + return STATUS_PENDING; + } + + if (error == SERR(ECONNRESET)) + return STATUS_CONNECTION_RESET; + + return STATUS_UNSUCCESSFUL; + } + + emu.write_memory(wsabuf.buf, host_buffer.data(), static_cast(bytes_received)); + + if (c.io_status_block) + { + IO_STATUS_BLOCK> block{}; + block.Status = STATUS_SUCCESS; + block.Information = static_cast(bytes_received); + c.io_status_block.write(block); + } + + return STATUS_SUCCESS; + } + + NTSTATUS ioctl_send(windows_emulator& win_emu, const io_device_context& c) + { + if (!this->s_) + { + throw std::runtime_error("Invalid AFD endpoint socket!"); + } + + auto& emu = win_emu.emu(); + + if (c.input_buffer_length < sizeof(AFD_SEND_INFO>)) + { + return STATUS_BUFFER_TOO_SMALL; + } + + const auto send_info = emu.read_memory>>(c.input_buffer); + + if (!send_info.BufferArray || send_info.BufferCount == 0) + { + return STATUS_INVALID_PARAMETER; + } + + if (send_info.BufferCount > 1) + { + // TODO: Scatter/Gather + return STATUS_NOT_SUPPORTED; + } + + const auto wsabuf = emu.read_memory>>(send_info.BufferArray); + if (!wsabuf.buf || wsabuf.len == 0) + { + return STATUS_INVALID_PARAMETER; + } + + std::vector host_buffer; + host_buffer.resize(wsabuf.len); + + emu.read_memory(wsabuf.buf, host_buffer.data(), host_buffer.size()); + + const auto bytes_sent = this->s_->send(host_buffer); + + if (bytes_sent < 0) + { + const auto error = this->s_->get_last_error(); + if (error == SERR(EWOULDBLOCK)) + { + this->delay_ioctrl(c, {}, false); + return STATUS_PENDING; + } + + if (error == SERR(ECONNRESET)) + return STATUS_CONNECTION_RESET; + + return STATUS_UNSUCCESSFUL; + } + + if (c.io_status_block) + { + IO_STATUS_BLOCK> block{}; + block.Status = STATUS_SUCCESS; + block.Information = static_cast(bytes_sent); + c.io_status_block.write(block); + } + + return STATUS_SUCCESS; + } + static std::vector resolve_endpoints(windows_emulator& win_emu, const std::span handles) { diff --git a/src/windows-emulator/devices/afd_types.hpp b/src/windows-emulator/devices/afd_types.hpp index b05ed79e..8e3d99a8 100644 --- a/src/windows-emulator/devices/afd_types.hpp +++ b/src/windows-emulator/devices/afd_types.hpp @@ -6,6 +6,32 @@ typedef LONG TDI_STATUS; +struct win_sockaddr +{ + USHORT sa_family; + CHAR sa_data[14]; +}; + +struct AFD_LISTEN_INFO +{ + BOOLEAN SanActive; + ULONG MaximumConnectionQueue; + BOOLEAN UseDelayedAcceptance; +}; + +struct AFD_LISTEN_RESPONSE_INFO +{ + LONG Sequence; + win_sockaddr RemoteAddress; +}; + +struct AFD_ACCEPT_INFO +{ + BOOLEAN SanActive; + LONG Sequence; + handle AcceptHandle; +}; + template struct TDI_CONNECTION_INFORMATION { diff --git a/src/windows-emulator/network/i_socket.hpp b/src/windows-emulator/network/i_socket.hpp index 9b8615a1..a369f40e 100644 --- a/src/windows-emulator/network/i_socket.hpp +++ b/src/windows-emulator/network/i_socket.hpp @@ -14,8 +14,11 @@ namespace network virtual int get_last_error() = 0; virtual bool is_ready(bool in_poll) = 0; + virtual bool is_listening() = 0; virtual bool bind(const address& addr) = 0; + virtual bool listen(int backlog) = 0; + virtual std::unique_ptr accept(address& address) = 0; virtual sent_size send(std::span data) = 0; virtual sent_size sendto(const address& destination, std::span data) = 0; diff --git a/src/windows-emulator/network/socket_wrapper.cpp b/src/windows-emulator/network/socket_wrapper.cpp index 60ee4828..d3702714 100644 --- a/src/windows-emulator/network/socket_wrapper.cpp +++ b/src/windows-emulator/network/socket_wrapper.cpp @@ -3,6 +3,11 @@ namespace network { + socket_wrapper::socket_wrapper(SOCKET s) + : socket_(s) + { + } + socket_wrapper::socket_wrapper(const int af, const int type, const int protocol) : socket_(af, type, protocol) { @@ -23,11 +28,32 @@ namespace network return this->socket_.is_ready(in_poll); } + bool socket_wrapper::is_listening() + { + return this->socket_.is_listening(); + } + bool socket_wrapper::bind(const address& addr) { return this->socket_.bind(addr); } + bool socket_wrapper::listen(int backlog) + { + return this->socket_.listen(backlog); + } + + std::unique_ptr socket_wrapper::accept(address& address) + { + const auto s = this->socket_.accept(address); + if (s == INVALID_SOCKET) + { + return nullptr; + } + + return std::make_unique(s); + } + sent_size socket_wrapper::send(const std::span data) { return ::send(this->socket_.get_socket(), reinterpret_cast(data.data()), diff --git a/src/windows-emulator/network/socket_wrapper.hpp b/src/windows-emulator/network/socket_wrapper.hpp index 569e170c..15da6410 100644 --- a/src/windows-emulator/network/socket_wrapper.hpp +++ b/src/windows-emulator/network/socket_wrapper.hpp @@ -7,6 +7,7 @@ namespace network class socket_wrapper : public i_socket { public: + socket_wrapper(SOCKET s); socket_wrapper(int af, int type, int protocol); ~socket_wrapper() override = default; @@ -15,8 +16,11 @@ namespace network int get_last_error() override; bool is_ready(bool in_poll) override; + bool is_listening() override; bool bind(const address& addr) override; + bool listen(int backlog) override; + std::unique_ptr accept(address& address) override; sent_size send(std::span data) override; sent_size sendto(const address& destination, std::span data) override; diff --git a/src/windows-emulator/network/static_socket_factory.cpp b/src/windows-emulator/network/static_socket_factory.cpp index c12cdd38..ea951600 100644 --- a/src/windows-emulator/network/static_socket_factory.cpp +++ b/src/windows-emulator/network/static_socket_factory.cpp @@ -65,12 +65,27 @@ namespace network return true; } + bool is_listening() override + { + return false; + } + bool bind(const address& addr) override { this->a = addr; return true; } + bool listen(int) override + { + throw std::runtime_error("Not implemented"); + } + + std::unique_ptr accept(address&) override + { + throw std::runtime_error("Not implemented"); + } + sent_size send(std::span) override { throw std::runtime_error("Not implemented"); From 4b83b20e193bcd4a252c3e1d13aa7103a10e73c7 Mon Sep 17 00:00:00 2001 From: Igor Pissolati Date: Tue, 20 May 2025 18:03:39 -0300 Subject: [PATCH 2/6] Handle connect & Fix polling --- src/common/network/socket.cpp | 26 ++-- src/common/network/socket.hpp | 3 +- src/windows-emulator/devices/afd_endpoint.cpp | 122 +++++++++++++++--- src/windows-emulator/devices/afd_types.hpp | 6 + src/windows-emulator/network/i_socket.hpp | 1 + .../network/socket_wrapper.cpp | 5 + .../network/socket_wrapper.hpp | 1 + .../network/static_socket_factory.cpp | 6 + 8 files changed, 139 insertions(+), 31 deletions(-) diff --git a/src/common/network/socket.cpp b/src/common/network/socket.cpp index f88f60c3..3bac610a 100644 --- a/src/common/network/socket.cpp +++ b/src/common/network/socket.cpp @@ -77,23 +77,23 @@ namespace network return ::bind(this->socket_, &target.get_addr(), target.get_size()) == 0; } + // NOLINTNEXTLINE(readability-make-member-function-const) + bool socket::connect(const address& target) + { + return ::connect(this->socket_, &target.get_addr(), target.get_size()) == 0; + } + // NOLINTNEXTLINE(readability-make-member-function-const) bool socket::listen(int backlog) { - int result = ::listen(this->socket_, backlog); - if (result == 0) - { - listening_ = true; - return true; - } - return false; + return ::listen(this->socket_, backlog) == 0; } // NOLINTNEXTLINE(readability-make-member-function-const) SOCKET socket::accept(address& address) { sockaddr addr{}; - int addrlen = sizeof(sockaddr); + socklen_t addrlen = sizeof(sockaddr); const auto s = ::accept(this->socket_, &addr, &addrlen); if (s != INVALID_SOCKET) @@ -187,7 +187,7 @@ namespace network bool socket::is_listening() const { - return this->is_valid() && listening_; + return this->is_valid() && is_socket_listening(this->socket_); } bool socket::sleep_sockets(const std::span& sockets, const std::chrono::milliseconds timeout, @@ -246,6 +246,14 @@ namespace network return !socket_is_ready; } + bool socket::is_socket_listening(SOCKET s) + { + int val{}; + socklen_t len = sizeof(val); + return getsockopt(s, SOL_SOCKET, SO_ACCEPTCONN, reinterpret_cast(&val), &len) != SOCKET_ERROR && + val == 1; + } + bool socket::sleep_sockets_until(const std::span& sockets, const std::chrono::high_resolution_clock::time_point time_point, const bool in_poll) diff --git a/src/common/network/socket.hpp b/src/common/network/socket.hpp index 602267ae..8b84d4a4 100644 --- a/src/common/network/socket.hpp +++ b/src/common/network/socket.hpp @@ -47,6 +47,7 @@ namespace network bool is_valid() const; bool bind(const address& target); + bool connect(const address& target); bool listen(int backlog); SOCKET accept(address& address); @@ -72,11 +73,11 @@ namespace network std::chrono::high_resolution_clock::time_point time_point, bool in_poll); static bool is_socket_ready(SOCKET s, bool in_poll); + static bool is_socket_listening(SOCKET s); void close(); private: SOCKET socket_ = INVALID_SOCKET; - bool listening_{}; }; } diff --git a/src/windows-emulator/devices/afd_endpoint.cpp b/src/windows-emulator/devices/afd_endpoint.cpp index e72f4ed2..081b408e 100644 --- a/src/windows-emulator/devices/afd_endpoint.cpp +++ b/src/windows-emulator/devices/afd_endpoint.cpp @@ -225,7 +225,7 @@ namespace const io_device_context& c) { constexpr auto info_size = offsetof(AFD_POLL_INFO64, Handles); - if (!c.input_buffer || c.input_buffer_length < info_size) + if (!c.input_buffer || c.input_buffer_length < info_size || c.input_buffer != c.output_buffer) { throw std::runtime_error("Bad AFD poll data"); } @@ -264,7 +264,7 @@ namespace socket_events |= POLLRDBAND; } - if (poll_events & (AFD_POLL_CONNECT_FAIL | AFD_POLL_SEND)) + if (poll_events & (AFD_POLL_CONNECT | AFD_POLL_CONNECT_FAIL | AFD_POLL_SEND)) { socket_events |= POLLWRNORM; } @@ -272,7 +272,8 @@ namespace return socket_events; } - ULONG map_socket_response_events_to_afd(const int16_t socket_events, const bool is_listener) + ULONG map_socket_response_events_to_afd(const int16_t socket_events, const ULONG afd_poll_events, + const bool is_listener) { ULONG afd_events = 0; @@ -288,19 +289,20 @@ namespace if (socket_events & POLLWRNORM) { - afd_events |= (AFD_POLL_CONNECT_FAIL | AFD_POLL_SEND); + afd_events |= AFD_POLL_SEND; } - if ((socket_events & (POLLHUP | POLLERR)) == (POLLHUP | POLLERR)) + if ((socket_events & (POLLHUP | POLLERR)) == (POLLHUP | POLLERR) && + afd_poll_events & (AFD_POLL_CONNECT_FAIL | AFD_POLL_ABORT)) { afd_events |= (AFD_POLL_CONNECT_FAIL | AFD_POLL_ABORT); } - else if (socket_events & POLLHUP) + else if (socket_events & POLLHUP && afd_poll_events & AFD_POLL_DISCONNECT) { afd_events |= AFD_POLL_DISCONNECT; } - if (socket_events & POLLNVAL) + if (socket_events & POLLNVAL && afd_poll_events & AFD_POLL_LOCAL_CLOSE) { afd_events |= AFD_POLL_LOCAL_CLOSE; } @@ -344,8 +346,10 @@ namespace continue; } + const auto& handle = handles[i]; + auto entry = handle_info_obj.read(i); - entry.PollEvents = map_socket_response_events_to_afd(pfd.revents, pfd.s->is_listening()); + entry.PollEvents = map_socket_response_events_to_afd(pfd.revents, handle.PollEvents, pfd.s->is_listening()); entry.Status = STATUS_SUCCESS; handle_info_obj.write(entry, current_index++); @@ -358,6 +362,14 @@ namespace info.NumberOfHandles = static_cast(current_index); // }); + if (c.io_status_block) + { + IO_STATUS_BLOCK> block{}; + block.Status = STATUS_SUCCESS; + block.Information = info_size + sizeof(AFD_POLL_HANDLE_INFO64) * current_index; + c.io_status_block.write(block); + } + return STATUS_SUCCESS; } @@ -376,6 +388,7 @@ namespace std::optional require_poll_{}; std::optional delayed_ioctl_{}; std::optional timeout_{}; + std::optional> timeout_callback_{}; std::unordered_map pending_connections_{}; LONG next_sequence_{0}; @@ -417,15 +430,16 @@ namespace this->s_->set_blocking(false); } - void delay_ioctrl(const io_device_context& c, + void delay_ioctrl(const io_device_context& c, const std::optional require_poll = {}, const std::optional timeout = {}, - const std::optional require_poll = {}) + const std::optional>& timeout_callback = {}) { if (this->executing_delayed_ioctl_) { return; } + this->timeout_callback_ = timeout_callback; this->timeout_ = timeout; this->require_poll_ = require_poll; this->delayed_ioctl_ = c; @@ -433,6 +447,7 @@ namespace void clear_pending_state() { + this->timeout_callback_ = {}; this->timeout_ = {}; this->require_poll_ = {}; this->delayed_ioctl_ = {}; @@ -466,6 +481,11 @@ namespace } write_io_status(this->delayed_ioctl_->io_status_block, STATUS_TIMEOUT); + + if (this->timeout_callback_) + { + (*this->timeout_callback_)(*this->delayed_ioctl_); + } } auto* e = win_emu.process.events.get(this->delayed_ioctl_->event); @@ -499,7 +519,7 @@ namespace { if (_AFD_BASE(c.io_control_code) != FSCTL_AFD_BASE) { - win_emu.log.print(color::cyan, "Bad AFD IOCTL: %X\n", c.io_control_code); + win_emu.log.print(color::cyan, "Bad AFD IOCTL: 0x%X\n", c.io_control_code); return STATUS_NOT_SUPPORTED; } @@ -513,12 +533,14 @@ namespace const auto request = _AFD_REQUEST(c.io_control_code); - win_emu.log.print(color::dark_gray, "--> AFD IOCTL: %X (%X)\n", c.io_control_code, request); + win_emu.log.print(color::dark_gray, "--> AFD IOCTL: 0x%X (%d)\n", c.io_control_code, request); switch (request) { case AFD_BIND: return this->ioctl_bind(win_emu, c); + case AFD_CONNECT: + return this->ioctl_connect(win_emu, c); case AFD_START_LISTEN: return this->ioctl_listen(win_emu, c); case AFD_WAIT_FOR_LISTEN: @@ -539,13 +561,54 @@ namespace case AFD_GET_INFORMATION: case AFD_SET_INFORMATION: case AFD_QUERY_HANDLES: + case AFD_TRANSPORT_IOCTL: return STATUS_SUCCESS; default: - win_emu.log.print(color::gray, "Unsupported AFD IOCTL: %X (%X)\n", c.io_control_code, request); + win_emu.log.print(color::gray, "Unsupported AFD IOCTL: 0x%X (%d)\n", c.io_control_code, request); return STATUS_NOT_SUPPORTED; } } + NTSTATUS ioctl_connect(windows_emulator& win_emu, const io_device_context& c) + { + if (!this->s_) + { + throw std::runtime_error("Invalid AFD endpoint socket!"); + } + + auto data = win_emu.emu().read_memory(c.input_buffer, c.input_buffer_length); + + constexpr auto address_offset = 12; + + if (data.size() < address_offset) + { + return STATUS_BUFFER_TOO_SMALL; + } + + const auto addr = convert_to_host_address(win_emu, std::span(data).subspan(address_offset)); + + if (!this->s_->connect(addr)) + { + const auto error = this->s_->get_last_error(); + if (error == SERR(EWOULDBLOCK)) + { + this->delay_ioctrl(c, true); + return STATUS_PENDING; + } + return STATUS_UNSUCCESSFUL; + } + + if (c.io_status_block) + { + IO_STATUS_BLOCK> block{}; + block.Status = STATUS_SUCCESS; + block.Information = 0; + c.io_status_block.write(block); + } + + return STATUS_SUCCESS; + } + NTSTATUS ioctl_bind(windows_emulator& win_emu, const io_device_context& c) const { if (!this->s_) @@ -569,6 +632,14 @@ namespace return STATUS_ADDRESS_ALREADY_ASSOCIATED; } + if (c.io_status_block) + { + IO_STATUS_BLOCK> block{}; + block.Status = STATUS_SUCCESS; + block.Information = 0; + c.io_status_block.write(block); + } + return STATUS_SUCCESS; } @@ -622,7 +693,7 @@ namespace const auto error = this->s_->get_last_error(); if (error == SERR(EWOULDBLOCK)) { - this->delay_ioctrl(c, {}, true); + this->delay_ioctrl(c, true); return STATUS_PENDING; } return STATUS_UNSUCCESSFUL; @@ -751,12 +822,14 @@ namespace const auto error = this->s_->get_last_error(); if (error == SERR(EWOULDBLOCK)) { - this->delay_ioctrl(c, {}, true); + this->delay_ioctrl(c, true); return STATUS_PENDING; } if (error == SERR(ECONNRESET)) + { return STATUS_CONNECTION_RESET; + } return STATUS_UNSUCCESSFUL; } @@ -767,7 +840,7 @@ namespace { IO_STATUS_BLOCK> block{}; block.Status = STATUS_SUCCESS; - block.Information = static_cast(bytes_received); + block.Information = static_cast(bytes_received); c.io_status_block.write(block); } @@ -819,12 +892,14 @@ namespace const auto error = this->s_->get_last_error(); if (error == SERR(EWOULDBLOCK)) { - this->delay_ioctrl(c, {}, false); + this->delay_ioctrl(c, false); return STATUS_PENDING; } if (error == SERR(ECONNRESET)) + { return STATUS_CONNECTION_RESET; + } return STATUS_UNSUCCESSFUL; } @@ -833,7 +908,7 @@ namespace { IO_STATUS_BLOCK> block{}; block.Status = STATUS_SUCCESS; - block.Information = static_cast(bytes_sent); + block.Information = static_cast(bytes_sent); c.io_status_block.write(block); } @@ -892,7 +967,12 @@ namespace timeout = utils::convert_delay_interval_to_time_point(win_emu.clock(), info.Timeout); } - this->delay_ioctrl(c, timeout); + this->delay_ioctrl(c, {}, timeout, [&](const io_device_context& dc) { + const emulator_object info_obj{win_emu.emu(), dc.input_buffer}; + info_obj.access([&](AFD_POLL_INFO64& info) { + info.NumberOfHandles = 0; // + }); + }); } return STATUS_PENDING; @@ -931,7 +1011,7 @@ namespace const auto error = this->s_->get_last_error(); if (error == SERR(EWOULDBLOCK)) { - this->delay_ioctrl(c, {}, true); + this->delay_ioctrl(c, true); return STATUS_PENDING; } @@ -991,7 +1071,7 @@ namespace const auto error = this->s_->get_last_error(); if (error == SERR(EWOULDBLOCK)) { - this->delay_ioctrl(c, {}, false); + this->delay_ioctrl(c, false); return STATUS_PENDING; } diff --git a/src/windows-emulator/devices/afd_types.hpp b/src/windows-emulator/devices/afd_types.hpp index 8e3d99a8..5c9818a6 100644 --- a/src/windows-emulator/devices/afd_types.hpp +++ b/src/windows-emulator/devices/afd_types.hpp @@ -199,5 +199,11 @@ struct AFD_POLL_INFO64 #define AFD_NO_OPERATION 39 #define AFD_VALIDATE_GROUP 40 #define AFD_GET_UNACCEPTED_CONNECT_DATA 41 +#define AFD_ROUTING_INTERFACE_QUERY 42 +#define AFD_ROUTING_INTERFACE_CHANGE 43 +#define AFD_ADDRESS_LIST_QUERY 44 +#define AFD_ADDRESS_LIST_CHANGE 45 +#define AFD_JOIN_LEAF 46 +#define AFD_TRANSPORT_IOCTL 47 // NOLINTEND(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) diff --git a/src/windows-emulator/network/i_socket.hpp b/src/windows-emulator/network/i_socket.hpp index a369f40e..946790ce 100644 --- a/src/windows-emulator/network/i_socket.hpp +++ b/src/windows-emulator/network/i_socket.hpp @@ -17,6 +17,7 @@ namespace network virtual bool is_listening() = 0; virtual bool bind(const address& addr) = 0; + virtual bool connect(const address& addr) = 0; virtual bool listen(int backlog) = 0; virtual std::unique_ptr accept(address& address) = 0; diff --git a/src/windows-emulator/network/socket_wrapper.cpp b/src/windows-emulator/network/socket_wrapper.cpp index d3702714..d7fac75f 100644 --- a/src/windows-emulator/network/socket_wrapper.cpp +++ b/src/windows-emulator/network/socket_wrapper.cpp @@ -38,6 +38,11 @@ namespace network return this->socket_.bind(addr); } + bool socket_wrapper::connect(const address& addr) + { + return this->socket_.connect(addr); + } + bool socket_wrapper::listen(int backlog) { return this->socket_.listen(backlog); diff --git a/src/windows-emulator/network/socket_wrapper.hpp b/src/windows-emulator/network/socket_wrapper.hpp index 15da6410..d3401d99 100644 --- a/src/windows-emulator/network/socket_wrapper.hpp +++ b/src/windows-emulator/network/socket_wrapper.hpp @@ -19,6 +19,7 @@ namespace network bool is_listening() override; bool bind(const address& addr) override; + bool connect(const address& addr) override; bool listen(int backlog) override; std::unique_ptr accept(address& address) override; diff --git a/src/windows-emulator/network/static_socket_factory.cpp b/src/windows-emulator/network/static_socket_factory.cpp index ea951600..5f40d49a 100644 --- a/src/windows-emulator/network/static_socket_factory.cpp +++ b/src/windows-emulator/network/static_socket_factory.cpp @@ -76,6 +76,12 @@ namespace network return true; } + bool connect(const address& addr) override + { + this->a = addr; + return true; + } + bool listen(int) override { throw std::runtime_error("Not implemented"); From d75d70e5ec8a7da08d44acfcec03166a23dfc6cc Mon Sep 17 00:00:00 2001 From: Igor Pissolati Date: Wed, 21 May 2025 11:59:56 -0300 Subject: [PATCH 3/6] Move new code out of `network::socket` --- src/common/network/socket.cpp | 40 ------------------- src/common/network/socket.hpp | 5 --- src/windows-emulator/devices/afd_endpoint.cpp | 2 +- .../network/socket_wrapper.cpp | 23 +++++++++-- 4 files changed, 20 insertions(+), 50 deletions(-) diff --git a/src/common/network/socket.cpp b/src/common/network/socket.cpp index 3bac610a..cad5d0b1 100644 --- a/src/common/network/socket.cpp +++ b/src/common/network/socket.cpp @@ -77,33 +77,6 @@ namespace network return ::bind(this->socket_, &target.get_addr(), target.get_size()) == 0; } - // NOLINTNEXTLINE(readability-make-member-function-const) - bool socket::connect(const address& target) - { - return ::connect(this->socket_, &target.get_addr(), target.get_size()) == 0; - } - - // NOLINTNEXTLINE(readability-make-member-function-const) - bool socket::listen(int backlog) - { - return ::listen(this->socket_, backlog) == 0; - } - - // NOLINTNEXTLINE(readability-make-member-function-const) - SOCKET socket::accept(address& address) - { - sockaddr addr{}; - socklen_t addrlen = sizeof(sockaddr); - const auto s = ::accept(this->socket_, &addr, &addrlen); - - if (s != INVALID_SOCKET) - { - address.set_address(&addr, addrlen); - } - - return s; - } - // NOLINTNEXTLINE(readability-make-member-function-const) bool socket::set_blocking(const bool blocking) { @@ -185,11 +158,6 @@ namespace network return this->is_valid() && is_socket_ready(this->socket_, in_poll); } - bool socket::is_listening() const - { - return this->is_valid() && is_socket_listening(this->socket_); - } - bool socket::sleep_sockets(const std::span& sockets, const std::chrono::milliseconds timeout, const bool in_poll) { @@ -246,14 +214,6 @@ namespace network return !socket_is_ready; } - bool socket::is_socket_listening(SOCKET s) - { - int val{}; - socklen_t len = sizeof(val); - return getsockopt(s, SOL_SOCKET, SO_ACCEPTCONN, reinterpret_cast(&val), &len) != SOCKET_ERROR && - val == 1; - } - bool socket::sleep_sockets_until(const std::span& sockets, const std::chrono::high_resolution_clock::time_point time_point, const bool in_poll) diff --git a/src/common/network/socket.hpp b/src/common/network/socket.hpp index 8b84d4a4..bd941364 100644 --- a/src/common/network/socket.hpp +++ b/src/common/network/socket.hpp @@ -47,9 +47,6 @@ namespace network bool is_valid() const; bool bind(const address& target); - bool connect(const address& target); - bool listen(int backlog); - SOCKET accept(address& address); bool set_blocking(bool blocking); static bool set_blocking(SOCKET s, bool blocking); @@ -65,7 +62,6 @@ namespace network int get_address_family() const; bool is_ready(bool in_poll) const; - bool is_listening() const; static bool sleep_sockets(const std::span& sockets, std::chrono::milliseconds timeout, bool in_poll); @@ -73,7 +69,6 @@ namespace network std::chrono::high_resolution_clock::time_point time_point, bool in_poll); static bool is_socket_ready(SOCKET s, bool in_poll); - static bool is_socket_listening(SOCKET s); void close(); diff --git a/src/windows-emulator/devices/afd_endpoint.cpp b/src/windows-emulator/devices/afd_endpoint.cpp index 081b408e..2eed0d1b 100644 --- a/src/windows-emulator/devices/afd_endpoint.cpp +++ b/src/windows-emulator/devices/afd_endpoint.cpp @@ -254,7 +254,7 @@ namespace { int16_t socket_events{}; - if (poll_events & (AFD_POLL_ACCEPT | AFD_POLL_RECEIVE)) + if (poll_events & (AFD_POLL_DISCONNECT | AFD_POLL_ACCEPT | AFD_POLL_RECEIVE)) { socket_events |= POLLRDNORM; } diff --git a/src/windows-emulator/network/socket_wrapper.cpp b/src/windows-emulator/network/socket_wrapper.cpp index d7fac75f..d1a0bd81 100644 --- a/src/windows-emulator/network/socket_wrapper.cpp +++ b/src/windows-emulator/network/socket_wrapper.cpp @@ -30,7 +30,17 @@ namespace network bool socket_wrapper::is_listening() { - return this->socket_.is_listening(); + if (!this->socket_.is_valid()) + { + return false; + } + + int val{}; + socklen_t len = sizeof(val); + const auto res = + getsockopt(this->socket_.get_socket(), SOL_SOCKET, SO_ACCEPTCONN, reinterpret_cast(&val), &len); + + return res != SOCKET_ERROR && val == 1; } bool socket_wrapper::bind(const address& addr) @@ -40,22 +50,27 @@ namespace network bool socket_wrapper::connect(const address& addr) { - return this->socket_.connect(addr); + return ::connect(this->socket_.get_socket(), &addr.get_addr(), addr.get_size()) == 0; } bool socket_wrapper::listen(int backlog) { - return this->socket_.listen(backlog); + return ::listen(this->socket_.get_socket(), backlog) == 0; } std::unique_ptr socket_wrapper::accept(address& address) { - const auto s = this->socket_.accept(address); + sockaddr addr{}; + socklen_t addrlen = sizeof(sockaddr); + const auto s = ::accept(this->socket_.get_socket(), &addr, &addrlen); + if (s == INVALID_SOCKET) { return nullptr; } + address.set_address(&addr, addrlen); + return std::make_unique(s); } From 4b51123cd1ef38b94f760a355c48e2c3ebc1c127 Mon Sep 17 00:00:00 2001 From: Igor Pissolati Date: Wed, 21 May 2025 12:13:24 -0300 Subject: [PATCH 4/6] Properly reset completion event --- src/windows-emulator/devices/afd_endpoint.cpp | 8 -------- src/windows-emulator/syscalls.cpp | 5 +++++ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/windows-emulator/devices/afd_endpoint.cpp b/src/windows-emulator/devices/afd_endpoint.cpp index 2eed0d1b..cb8086cb 100644 --- a/src/windows-emulator/devices/afd_endpoint.cpp +++ b/src/windows-emulator/devices/afd_endpoint.cpp @@ -523,14 +523,6 @@ namespace return STATUS_NOT_SUPPORTED; } - if (this->delayed_ioctl_) - { - if (auto* e = win_emu.process.events.get(c.event)) - { - e->signaled = false; - } - } - const auto request = _AFD_REQUEST(c.io_control_code); win_emu.log.print(color::dark_gray, "--> AFD IOCTL: 0x%X (%d)\n", c.io_control_code, request); diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 14e4d8b9..9e0e8f15 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -441,6 +441,11 @@ namespace syscalls return STATUS_INVALID_HANDLE; } + if (auto* e = c.win_emu.process.events.get(event)) + { + e->signaled = false; + } + io_device_context context{c.emu}; context.event = event; context.apc_routine = apc_routine; From f2b4d4f26d267b2021350602845f941b8ba66ef7 Mon Sep 17 00:00:00 2001 From: Igor Pissolati Date: Wed, 21 May 2025 12:53:23 -0300 Subject: [PATCH 5/6] Remove unnecessary code --- src/windows-emulator/devices/afd_endpoint.cpp | 36 ------------------- 1 file changed, 36 deletions(-) diff --git a/src/windows-emulator/devices/afd_endpoint.cpp b/src/windows-emulator/devices/afd_endpoint.cpp index cb8086cb..69e34d2b 100644 --- a/src/windows-emulator/devices/afd_endpoint.cpp +++ b/src/windows-emulator/devices/afd_endpoint.cpp @@ -365,7 +365,6 @@ namespace if (c.io_status_block) { IO_STATUS_BLOCK> block{}; - block.Status = STATUS_SUCCESS; block.Information = info_size + sizeof(AFD_POLL_HANDLE_INFO64) * current_index; c.io_status_block.write(block); } @@ -590,14 +589,6 @@ namespace return STATUS_UNSUCCESSFUL; } - if (c.io_status_block) - { - IO_STATUS_BLOCK> block{}; - block.Status = STATUS_SUCCESS; - block.Information = 0; - c.io_status_block.write(block); - } - return STATUS_SUCCESS; } @@ -624,14 +615,6 @@ namespace return STATUS_ADDRESS_ALREADY_ASSOCIATED; } - if (c.io_status_block) - { - IO_STATUS_BLOCK> block{}; - block.Status = STATUS_SUCCESS; - block.Information = 0; - c.io_status_block.write(block); - } - return STATUS_SUCCESS; } @@ -654,14 +637,6 @@ namespace return STATUS_INVALID_PARAMETER; } - if (c.io_status_block) - { - IO_STATUS_BLOCK> block{}; - block.Status = STATUS_SUCCESS; - block.Information = 0; - c.io_status_block.write(block); - } - return STATUS_SUCCESS; } @@ -714,7 +689,6 @@ namespace if (c.io_status_block) { IO_STATUS_BLOCK> block{}; - block.Status = STATUS_SUCCESS; block.Information = sizeof(AFD_LISTEN_RESPONSE_INFO); c.io_status_block.write(block); } @@ -760,14 +734,6 @@ namespace pending_connections_.erase(it); - if (c.io_status_block) - { - IO_STATUS_BLOCK> block{}; - block.Status = STATUS_SUCCESS; - block.Information = 0; - c.io_status_block.write(block); - } - return STATUS_SUCCESS; } @@ -831,7 +797,6 @@ namespace if (c.io_status_block) { IO_STATUS_BLOCK> block{}; - block.Status = STATUS_SUCCESS; block.Information = static_cast(bytes_received); c.io_status_block.write(block); } @@ -899,7 +864,6 @@ namespace if (c.io_status_block) { IO_STATUS_BLOCK> block{}; - block.Status = STATUS_SUCCESS; block.Information = static_cast(bytes_sent); c.io_status_block.write(block); } From a0a2e69381764628ec90b14a7aeb6910012752b1 Mon Sep 17 00:00:00 2001 From: Igor Pissolati Date: Wed, 21 May 2025 12:55:58 -0300 Subject: [PATCH 6/6] Explictly capture win_emu for timeout callback --- src/windows-emulator/devices/afd_endpoint.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/windows-emulator/devices/afd_endpoint.cpp b/src/windows-emulator/devices/afd_endpoint.cpp index 69e34d2b..f240857b 100644 --- a/src/windows-emulator/devices/afd_endpoint.cpp +++ b/src/windows-emulator/devices/afd_endpoint.cpp @@ -923,7 +923,7 @@ namespace timeout = utils::convert_delay_interval_to_time_point(win_emu.clock(), info.Timeout); } - this->delay_ioctrl(c, {}, timeout, [&](const io_device_context& dc) { + this->delay_ioctrl(c, {}, timeout, [&win_emu](const io_device_context& dc) { const emulator_object info_obj{win_emu.emu(), dc.input_buffer}; info_obj.access([&](AFD_POLL_INFO64& info) { info.NumberOfHandles = 0; //