From cc3cabc76893282e86485f75d69c635d03f28564 Mon Sep 17 00:00:00 2001 From: ayeteadoe Date: Mon, 14 Jul 2025 01:16:11 -0700 Subject: [PATCH] LibCore: Implement TCPServer on Windows --- Libraries/LibCore/CMakeLists.txt | 5 +- .../EventLoopImplementationWindows.cpp | 3 +- Libraries/LibCore/SocketWindows.cpp | 12 ++ Libraries/LibCore/SystemWindows.cpp | 48 +++++++- Libraries/LibCore/TCPServerWindows.cpp | 112 ++++++++++++++++++ 5 files changed, 174 insertions(+), 6 deletions(-) create mode 100644 Libraries/LibCore/TCPServerWindows.cpp diff --git a/Libraries/LibCore/CMakeLists.txt b/Libraries/LibCore/CMakeLists.txt index 9fe447471e4..a7a4f581721 100644 --- a/Libraries/LibCore/CMakeLists.txt +++ b/Libraries/LibCore/CMakeLists.txt @@ -58,11 +58,12 @@ set(SOURCES ) if (WIN32) - # FIXME: Support UDPServer and TCPServer on Windows + # FIXME: Support UDPServer on Windows list(APPEND SOURCES SocketWindows.cpp AnonymousBufferWindows.cpp - EventLoopImplementationWindows.cpp) + EventLoopImplementationWindows.cpp + TCPServerWindows.cpp) else() list(APPEND SOURCES Command.cpp diff --git a/Libraries/LibCore/EventLoopImplementationWindows.cpp b/Libraries/LibCore/EventLoopImplementationWindows.cpp index 2e38479c518..56bf97a26d7 100644 --- a/Libraries/LibCore/EventLoopImplementationWindows.cpp +++ b/Libraries/LibCore/EventLoopImplementationWindows.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2023, Andreas Kling * Copyright (c) 2024-2025, stasoid + * Copyright (c) 2025, ayeteadoe * * SPDX-License-Identifier: BSD-2-Clause */ @@ -166,7 +167,7 @@ static int notifier_type_to_network_event(NotificationType type) { switch (type) { case NotificationType::Read: - return FD_READ | FD_CLOSE; + return FD_READ | FD_CLOSE | FD_ACCEPT; case NotificationType::Write: return FD_WRITE; default: diff --git a/Libraries/LibCore/SocketWindows.cpp b/Libraries/LibCore/SocketWindows.cpp index cb193dce8ca..7a61633be5c 100644 --- a/Libraries/LibCore/SocketWindows.cpp +++ b/Libraries/LibCore/SocketWindows.cpp @@ -2,6 +2,7 @@ * Copyright (c) 2018-2021, Andreas Kling * Copyright (c) 2021, sin-ack * Copyright (c) 2025, stasoid + * Copyright (c) 2025, ayeteadoe * * SPDX-License-Identifier: BSD-2-Clause */ @@ -279,4 +280,15 @@ ErrorOr> TCPSocket::connect(SocketAddress const& addres return socket; } +ErrorOr> TCPSocket::adopt_fd(int fd) +{ + if (static_cast(fd) == INVALID_SOCKET) + return Error::from_windows_error(); + + auto socket = TRY(adopt_nonnull_own_or_enomem(new (nothrow) TCPSocket())); + socket->m_helper.set_fd(fd); + socket->setup_notifier(); + return socket; +} + } diff --git a/Libraries/LibCore/SystemWindows.cpp b/Libraries/LibCore/SystemWindows.cpp index b3b524b48e9..271887f8a12 100644 --- a/Libraries/LibCore/SystemWindows.cpp +++ b/Libraries/LibCore/SystemWindows.cpp @@ -5,6 +5,7 @@ * Copyright (c) 2022, Matthias Zimmerman * Copyright (c) 2023, Cameron Youell * Copyright (c) 2024-2025, stasoid + * Copyright (c) 2025, ayeteadoe * * SPDX-License-Identifier: BSD-2-Clause */ @@ -109,10 +110,15 @@ ErrorOr fstat(int handle) return st; } -ErrorOr ioctl(int, unsigned, ...) +ErrorOr ioctl(int fd, unsigned request, ...) { - dbgln("Core::System::ioctl() is not implemented"); - VERIFY_NOT_REACHED(); + va_list ap; + va_start(ap, request); + u_long arg = va_arg(ap, FlatPtr); + va_end(ap); + if (::ioctlsocket(fd, request, &arg) == SOCKET_ERROR) + return Error::from_windows_error(); + return {}; } ErrorOr getcwd() @@ -242,6 +248,42 @@ bool is_socket(int handle) return GetFileType(to_handle(handle)) == FILE_TYPE_PIPE; } +ErrorOr bind(int sockfd, struct sockaddr const* name, socklen_t name_size) +{ + if (::bind(sockfd, name, name_size) == SOCKET_ERROR) + return Error::from_windows_error(); + return {}; +} + +ErrorOr listen(int sockfd, int backlog) +{ + if (::listen(sockfd, backlog) == SOCKET_ERROR) + return Error::from_windows_error(); + return {}; +} + +ErrorOr accept(int sockfd, struct sockaddr* addr, socklen_t* addr_size) +{ + auto fd = ::accept(sockfd, addr, addr_size); + if (fd == INVALID_SOCKET) + return Error::from_windows_error(); + return fd; +} + +ErrorOr getsockname(int sockfd, struct sockaddr* name, socklen_t* name_size) +{ + if (::getsockname(sockfd, name, name_size) == SOCKET_ERROR) + return Error::from_windows_error(); + return {}; +} + +ErrorOr setsockopt(int sockfd, int level, int option, void const* value, socklen_t value_size) +{ + if (::setsockopt(sockfd, level, option, static_cast(value), value_size) == SOCKET_ERROR) + return Error::from_windows_error(); + return {}; +} + ErrorOr socketpair(int domain, int type, int protocol, int sv[2]) { if (domain != AF_LOCAL || type != SOCK_STREAM || protocol != 0) diff --git a/Libraries/LibCore/TCPServerWindows.cpp b/Libraries/LibCore/TCPServerWindows.cpp new file mode 100644 index 00000000000..41d941fa3ce --- /dev/null +++ b/Libraries/LibCore/TCPServerWindows.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2025, ayeteadoe + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include + +#include + +namespace Core { + +ErrorOr> TCPServer::try_create(EventReceiver* parent) +{ + int fd = TRY(Core::System::socket(AF_INET, SOCK_STREAM, 0)); + ArmedScopeGuard close_fd { [fd]() { + MUST(Core::System::close(fd)); + } }; + + int option = 1; + TRY(Core::System::ioctl(fd, FIONBIO, option)); + TRY(Core::System::setsockopt(fd, SOL_SOCKET, SO_OOBINLINE, &option, sizeof(option))); + if (SetHandleInformation(to_handle(fd), HANDLE_FLAG_INHERIT, 0) == 0) + return Error::from_windows_error(); + close_fd.disarm(); + return adopt_nonnull_ref_or_enomem(new (nothrow) TCPServer(fd, parent)); +} + +TCPServer::TCPServer(int fd, EventReceiver* parent) + : EventReceiver(parent) + , m_fd(fd) +{ + VERIFY(m_fd >= 0); +} + +TCPServer::~TCPServer() +{ + MUST(Core::System::close(m_fd)); +} + +ErrorOr TCPServer::listen(IPv4Address const& address, u16 port, AllowAddressReuse allow_address_reuse) +{ + if (m_listening) + return Error::from_errno(EADDRINUSE); + + auto socket_address = SocketAddress(address, port); + auto in = socket_address.to_sockaddr_in(); + + if (allow_address_reuse == AllowAddressReuse::Yes) { + int option = 1; + TRY(Core::System::setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option))); + } + + TRY(Core::System::bind(m_fd, (sockaddr const*)&in, sizeof(in))); + TRY(Core::System::listen(m_fd, 5)); + m_listening = true; + + m_notifier = Notifier::construct(m_fd, Notifier::Type::Read, this); + m_notifier->on_activation = [this] { + if (on_ready_to_accept) + on_ready_to_accept(); + }; + return {}; +} + +ErrorOr TCPServer::set_blocking(bool blocking) +{ + TRY(Core::System::ioctl(m_fd, FIONBIO, blocking ? 0 : 1)); + return {}; +} + +ErrorOr> TCPServer::accept() +{ + VERIFY(m_listening); + sockaddr_in in; + socklen_t in_size = sizeof(in); + int accepted_fd = TRY(Core::System::accept(m_fd, (sockaddr*)&in, &in_size)); + return TRY(TCPSocket::adopt_fd(accepted_fd)); +} + +Optional TCPServer::local_address() const +{ + if (m_fd == -1) + return {}; + + sockaddr_in address; + socklen_t len = sizeof(address); + if (Core::System::getsockname(m_fd, (sockaddr*)&address, &len).is_error()) + return {}; + + return IPv4Address(address.sin_addr.s_addr); +} + +Optional TCPServer::local_port() const +{ + if (m_fd == -1) + return {}; + + sockaddr_in address; + socklen_t len = sizeof(address); + if (Core::System::getsockname(m_fd, (sockaddr*)&address, &len).is_error()) + return {}; + + return ntohs(address.sin_port); +} + +}