mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-11-07 00:30:59 +00:00
178 lines
5.1 KiB
C++
178 lines
5.1 KiB
C++
|
|
/*
|
||
|
|
* Copyright (c) 2024, Andrew Kaster <andrew@ladybird.org>
|
||
|
|
* Copyright (c) 2025, stasoid <stasoid@yahoo.com>
|
||
|
|
*
|
||
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include <AK/ByteReader.h>
|
||
|
|
#include <LibIPC/HandleType.h>
|
||
|
|
#include <LibIPC/TransportSocketWindows.h>
|
||
|
|
|
||
|
|
#include <AK/Windows.h>
|
||
|
|
|
||
|
|
namespace IPC {
|
||
|
|
|
||
|
|
TransportSocketWindows::TransportSocketWindows(NonnullOwnPtr<Core::LocalSocket> socket)
|
||
|
|
: m_socket(move(socket))
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
void TransportSocketWindows::set_peer_pid(int pid)
|
||
|
|
{
|
||
|
|
m_peer_pid = pid;
|
||
|
|
}
|
||
|
|
|
||
|
|
void TransportSocketWindows::set_up_read_hook(Function<void()> hook)
|
||
|
|
{
|
||
|
|
VERIFY(m_socket->is_open());
|
||
|
|
m_socket->on_ready_to_read = move(hook);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool TransportSocketWindows::is_open() const
|
||
|
|
{
|
||
|
|
return m_socket->is_open();
|
||
|
|
}
|
||
|
|
|
||
|
|
void TransportSocketWindows::close()
|
||
|
|
{
|
||
|
|
m_socket->close();
|
||
|
|
}
|
||
|
|
|
||
|
|
void TransportSocketWindows::wait_until_readable()
|
||
|
|
{
|
||
|
|
auto readable = MUST(m_socket->can_read_without_blocking(-1));
|
||
|
|
VERIFY(readable);
|
||
|
|
}
|
||
|
|
|
||
|
|
ErrorOr<void> TransportSocketWindows::duplicate_handles(Bytes bytes, Vector<size_t> const& handle_offsets)
|
||
|
|
{
|
||
|
|
if (handle_offsets.is_empty())
|
||
|
|
return {};
|
||
|
|
|
||
|
|
if (m_peer_pid == -1)
|
||
|
|
return Error::from_string_literal("Transport is not initialized");
|
||
|
|
|
||
|
|
HANDLE peer_process_handle = OpenProcess(PROCESS_DUP_HANDLE, FALSE, m_peer_pid);
|
||
|
|
if (!peer_process_handle)
|
||
|
|
return Error::from_windows_error();
|
||
|
|
ScopeGuard guard = [&] { CloseHandle(peer_process_handle); };
|
||
|
|
|
||
|
|
for (auto offset : handle_offsets) {
|
||
|
|
|
||
|
|
auto span = bytes.slice(offset);
|
||
|
|
if (span.size() < sizeof(HandleType))
|
||
|
|
return Error::from_string_literal("Not enough bytes");
|
||
|
|
|
||
|
|
UnderlyingType<HandleType> raw_type {};
|
||
|
|
ByteReader::load(span.data(), raw_type);
|
||
|
|
auto type = static_cast<HandleType>(raw_type);
|
||
|
|
if (type != HandleType::Generic && type != HandleType::Socket)
|
||
|
|
return Error::from_string_literal("Invalid handle type");
|
||
|
|
span = span.slice(sizeof(HandleType));
|
||
|
|
|
||
|
|
if (type == HandleType::Socket) {
|
||
|
|
if (span.size() < sizeof(WSAPROTOCOL_INFO))
|
||
|
|
return Error::from_string_literal("Not enough bytes for socket handle");
|
||
|
|
|
||
|
|
// We stashed the bytes of this process's version of the handle at the offset location
|
||
|
|
int handle = -1;
|
||
|
|
ByteReader::load(span.data(), handle);
|
||
|
|
|
||
|
|
auto* pi = reinterpret_cast<WSAPROTOCOL_INFO*>(span.data());
|
||
|
|
if (WSADuplicateSocket(handle, m_peer_pid, pi))
|
||
|
|
return Error::from_windows_error();
|
||
|
|
} else {
|
||
|
|
if (span.size() < sizeof(int))
|
||
|
|
return Error::from_string_literal("Not enough bytes for generic handle");
|
||
|
|
|
||
|
|
int handle = -1;
|
||
|
|
ByteReader::load(span.data(), handle);
|
||
|
|
|
||
|
|
HANDLE new_handle = INVALID_HANDLE_VALUE;
|
||
|
|
if (!DuplicateHandle(GetCurrentProcess(), to_handle(handle), peer_process_handle, &new_handle, 0, FALSE, DUPLICATE_SAME_ACCESS))
|
||
|
|
return Error::from_windows_error();
|
||
|
|
|
||
|
|
ByteReader::store(span.data(), to_fd(new_handle));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return {};
|
||
|
|
}
|
||
|
|
|
||
|
|
ErrorOr<void> TransportSocketWindows::transfer(Bytes bytes_to_write, Vector<size_t> const& handle_offsets)
|
||
|
|
{
|
||
|
|
TRY(duplicate_handles(bytes_to_write, handle_offsets));
|
||
|
|
|
||
|
|
while (!bytes_to_write.is_empty()) {
|
||
|
|
|
||
|
|
ErrorOr<size_t> maybe_nwritten = m_socket->write_some(bytes_to_write);
|
||
|
|
|
||
|
|
if (maybe_nwritten.is_error()) {
|
||
|
|
auto error = maybe_nwritten.release_error();
|
||
|
|
if (error.code() != EWOULDBLOCK)
|
||
|
|
return error;
|
||
|
|
|
||
|
|
struct pollfd pollfd = {
|
||
|
|
.fd = static_cast<SOCKET>(m_socket->fd().value()),
|
||
|
|
.events = POLLOUT,
|
||
|
|
.revents = 0
|
||
|
|
};
|
||
|
|
|
||
|
|
auto result = WSAPoll(&pollfd, 1, -1);
|
||
|
|
if (result == 1)
|
||
|
|
continue;
|
||
|
|
if (result == SOCKET_ERROR)
|
||
|
|
return Error::from_windows_error();
|
||
|
|
VERIFY_NOT_REACHED();
|
||
|
|
}
|
||
|
|
|
||
|
|
bytes_to_write = bytes_to_write.slice(maybe_nwritten.value());
|
||
|
|
}
|
||
|
|
return {};
|
||
|
|
}
|
||
|
|
|
||
|
|
TransportSocketWindows::ReadResult TransportSocketWindows::read_as_much_as_possible_without_blocking(Function<void()> schedule_shutdown)
|
||
|
|
{
|
||
|
|
ReadResult result;
|
||
|
|
|
||
|
|
while (is_open()) {
|
||
|
|
|
||
|
|
u8 buffer[4096];
|
||
|
|
auto maybe_bytes_read = m_socket->read_without_waiting({ buffer, sizeof(buffer) });
|
||
|
|
|
||
|
|
if (maybe_bytes_read.is_error()) {
|
||
|
|
auto error = maybe_bytes_read.release_error();
|
||
|
|
if (error.code() == EWOULDBLOCK)
|
||
|
|
break;
|
||
|
|
if (error.code() == ECONNRESET) {
|
||
|
|
schedule_shutdown();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
VERIFY_NOT_REACHED();
|
||
|
|
}
|
||
|
|
|
||
|
|
auto bytes_read = maybe_bytes_read.release_value();
|
||
|
|
if (bytes_read.is_empty()) {
|
||
|
|
schedule_shutdown();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
result.bytes.append(bytes_read.data(), bytes_read.size());
|
||
|
|
}
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
ErrorOr<int> TransportSocketWindows::release_underlying_transport_for_transfer()
|
||
|
|
{
|
||
|
|
return m_socket->release_fd();
|
||
|
|
}
|
||
|
|
|
||
|
|
ErrorOr<IPC::File> TransportSocketWindows::clone_for_transfer()
|
||
|
|
{
|
||
|
|
return IPC::File::clone_fd(m_socket->fd().value());
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|