blob: 3d2a498a22a2c27e7e3a8311a70b747303b42f19 [file] [log] [blame]
// Copyright 2024 The Fuchsia Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/paravirtualization/lib/vsock/zxio_connected_socket.h"
#include <lib/syslog/cpp/macros.h>
#include <lib/zxio/null.h>
#include <lib/zxio/ops.h>
#include <lib/zxio/vector.h>
#include <lib/zxio/zxio.h>
#include <type_traits>
#include <fbl/inline_array.h>
namespace vsock {
static_assert(sizeof(ZxioConnectedSocket) <= sizeof(zxio_storage_t));
static_assert(std::is_standard_layout_v<ZxioConnectedSocket>);
namespace {
ZxioConnectedSocket& connected_socket(zxio_t* io) {
return *reinterpret_cast<ZxioConnectedSocket*>(io);
}
constexpr zxio_ops_t kZxioConnectedSocketOps = []() {
zxio_ops_t ops = zxio_default_ops;
ops.destroy = [](zxio_t* io) { connected_socket(io).~ZxioConnectedSocket(); };
// Mostly copied from zxio/pipe.cc
ops.wait_begin = [](zxio_t* io, zxio_signals_t zxio_signals, zx_handle_t* out_handle,
zx_signals_t* out_zx_signals) {
*out_handle = connected_socket(io).socket().get();
zx_signals_t zx_signals = ZX_SIGNAL_NONE;
if (zxio_signals & ZXIO_SIGNAL_READABLE) {
zx_signals |= ZX_SOCKET_READABLE;
}
if (zxio_signals & ZXIO_SIGNAL_WRITABLE) {
zx_signals |= ZX_SOCKET_WRITABLE;
}
if (zxio_signals & ZXIO_SIGNAL_READ_DISABLED) {
zx_signals |= ZX_SOCKET_PEER_WRITE_DISABLED;
}
if (zxio_signals & ZXIO_SIGNAL_WRITE_DISABLED) {
zx_signals |= ZX_SOCKET_WRITE_DISABLED;
}
if (zxio_signals & ZXIO_SIGNAL_READ_THRESHOLD) {
zx_signals |= ZX_SOCKET_READ_THRESHOLD;
}
if (zxio_signals & ZXIO_SIGNAL_WRITE_THRESHOLD) {
zx_signals |= ZX_SOCKET_WRITE_THRESHOLD;
}
if (zxio_signals & ZXIO_SIGNAL_PEER_CLOSED) {
zx_signals |= ZX_SOCKET_PEER_CLOSED;
}
*out_zx_signals = zx_signals;
};
// Copied from zxio/pipe.cc
ops.wait_end = [](zxio_t* io, zx_signals_t zx_signals, zxio_signals_t* out_zxio_signals) {
zxio_signals_t zxio_signals = ZXIO_SIGNAL_NONE;
if (zx_signals & ZX_SOCKET_READABLE) {
zxio_signals |= ZXIO_SIGNAL_READABLE;
}
if (zx_signals & ZX_SOCKET_WRITABLE) {
zxio_signals |= ZXIO_SIGNAL_WRITABLE;
}
if (zx_signals & ZX_SOCKET_PEER_WRITE_DISABLED) {
zxio_signals |= ZXIO_SIGNAL_READ_DISABLED;
}
if (zx_signals & ZX_SOCKET_WRITE_DISABLED) {
zxio_signals |= ZXIO_SIGNAL_WRITE_DISABLED;
}
if (zx_signals & ZX_SOCKET_READ_THRESHOLD) {
zxio_signals |= ZXIO_SIGNAL_READ_THRESHOLD;
}
if (zx_signals & ZX_SOCKET_WRITE_THRESHOLD) {
zxio_signals |= ZXIO_SIGNAL_WRITE_THRESHOLD;
}
if (zx_signals & ZX_SOCKET_PEER_CLOSED) {
zxio_signals |= ZXIO_SIGNAL_PEER_CLOSED;
}
*out_zxio_signals = zxio_signals;
};
// Mostly copied from zxio/pipe.cc
ops.get_read_buffer_available = [](zxio_t* io, size_t* out_available) {
if (out_available == nullptr) {
return ZX_ERR_INVALID_ARGS;
}
zx_info_socket_t info;
memset(&info, 0, sizeof(info));
zx_status_t status = connected_socket(io).socket().get_info(ZX_INFO_SOCKET, &info, sizeof(info),
nullptr, nullptr);
if (status != ZX_OK) {
return status;
}
*out_available = info.rx_buf_available;
return ZX_OK;
};
// Mostly copied from zxio/pipe.cc
ops.shutdown = [](zxio_t* io, zxio_shutdown_options_t options, int16_t* out_code) {
if ((options & ZXIO_SHUTDOWN_OPTIONS_MASK) != options) {
return ZX_ERR_INVALID_ARGS;
}
uint32_t disposition = 0;
if (options & ZXIO_SHUTDOWN_OPTIONS_WRITE) {
disposition = ZX_SOCKET_DISPOSITION_WRITE_DISABLED;
}
uint32_t disposition_peer = 0;
if (options & ZXIO_SHUTDOWN_OPTIONS_READ) {
disposition_peer = ZX_SOCKET_DISPOSITION_WRITE_DISABLED;
}
*out_code = 0;
return connected_socket(io).socket().set_disposition(disposition, disposition_peer);
};
ops.getpeername = [](zxio_t* io, struct sockaddr* addr, socklen_t* addrlen, int16_t* out_code) {
const auto& peer_addr = connected_socket(io).peer_addr();
if (addr != nullptr) {
std::memcpy(addr, &peer_addr, sizeof(peer_addr));
}
if (addrlen != nullptr) {
*addrlen = sizeof(peer_addr);
}
*out_code = 0;
return ZX_OK;
};
// Mostly copied from zxio/pipe.cc's zxio_stream_pipe_ops
ops.readv = [](zxio_t* io, const zx_iovec_t* vector, size_t vector_count, zxio_flags_t flags,
size_t* out_actual) {
if (flags) {
return ZX_ERR_NOT_SUPPORTED;
}
zx::socket& socket = connected_socket(io).socket();
return zxio_stream_do_vector(vector, vector_count, out_actual,
[&](void* buffer, size_t capacity, size_t* out_actual) {
return socket.read(0, buffer, capacity, out_actual);
});
};
// Copied from zxio/pipe.cc's zxio_stream_pipe_ops
ops.writev = [](zxio_t* io, const zx_iovec_t* vector, size_t vector_count, zxio_flags_t flags,
size_t* out_actual) {
if (flags) {
return ZX_ERR_NOT_SUPPORTED;
}
return zxio_stream_do_vector(
vector, vector_count, out_actual, [&](void* buffer, size_t capacity, size_t* out_actual) {
return connected_socket(io).socket().write(0, buffer, capacity, out_actual);
});
};
ops.recvmsg = [](zxio_t* io, struct msghdr* msg, int flags, size_t* out_actual,
int16_t* out_code) {
// Copied from zxio/transitional.cc
zxio_flags_t zxio_flags = 0;
if (flags & MSG_PEEK) {
zxio_flags |= ZXIO_PEEK;
flags &= ~MSG_PEEK;
}
if (flags) {
// TODO(https://fxbug.dev/42146893): support MSG_OOB
return ZX_ERR_NOT_SUPPORTED;
}
if (msg->msg_iovlen > IOV_MAX) {
return ZX_ERR_INVALID_ARGS;
}
fbl::AllocChecker ac;
fbl::InlineArray<zx_iovec_t, 1> zx_iov(&ac, msg->msg_iovlen);
ac.check();
for (int i = 0; i < msg->msg_iovlen; ++i) {
iovec const& iov = msg->msg_iov[i];
zx_iov[i] = {
.buffer = iov.iov_base,
.capacity = iov.iov_len,
};
}
zx_status_t status = zxio_readv(io, zx_iov.get(), zx_iov.size(), zxio_flags, out_actual);
if (status != ZX_OK) {
return status;
}
*out_code = 0;
return ZX_OK;
};
ops.sendmsg = [](zxio_t* io, const struct msghdr* msg, int flags, size_t* out_actual,
int16_t* out_code) {
// Copied from zxio/transitional.cc
if (flags) {
// TODO(https://fxbug.dev/42146893): support MSG_OOB
return ZX_ERR_NOT_SUPPORTED;
}
if (msg->msg_iovlen > IOV_MAX) {
return ZX_ERR_INVALID_ARGS;
}
fbl::AllocChecker ac;
fbl::InlineArray<zx_iovec_t, 1> zx_iov(&ac, msg->msg_iovlen);
ac.check();
for (int i = 0; i < msg->msg_iovlen; ++i) {
zx_iov[i] = {
.buffer = msg->msg_iov[i].iov_base,
.capacity = msg->msg_iov[i].iov_len,
};
}
zx_status_t status = zxio_writev(io, zx_iov.get(), zx_iov.size(), 0, out_actual);
if (status != ZX_OK) {
return status;
}
*out_code = 0;
return ZX_OK;
};
return ops;
}();
} // namespace
ZxioConnectedSocket::ZxioConnectedSocket(
zx::socket socket, fidl::ClientEnd<fuchsia_vsock::Connection> connection_client_end,
struct sockaddr_vm& peer_addr)
: socket_(std::move(socket)),
connection_client_end_(std::move(connection_client_end)),
peer_addr_(std::make_unique<struct sockaddr_vm>(peer_addr)) {
zxio_init(&io_, &kZxioConnectedSocketOps);
}
} // namespace vsock