blob: 5f9ef5fdf59b343f2035ca1087dd65e7de012338 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <fuchsia/posix/socket/llcpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/fdio/fd.h>
#include <lib/fidl-async/cpp/bind.h>
#include <lib/zxs/protocol.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <array>
#include <zxtest/zxtest.h>
namespace {
class Server final : public llcpp::fuchsia::posix::socket::Control::Interface {
public:
Server(zx::socket peer) : peer_(std::move(peer)) {
// We need the FDIO to act like it's connected.
// ZXSIO_SIGNAL_CONNECTED is private, but we know the value.
ASSERT_OK(peer_.signal(0, ZX_USER_SIGNAL_3));
}
void Clone(uint32_t flags, ::zx::channel object, CloneCompleter::Sync completer) override {
return completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void Close(CloseCompleter::Sync completer) override { return completer.Reply(ZX_OK); }
void Describe(DescribeCompleter::Sync completer) override {
llcpp::fuchsia::io::Socket socket;
zx_status_t status = peer_.duplicate(ZX_RIGHT_SAME_RIGHTS, &socket.socket);
if (status != ZX_OK) {
return completer.Close(status);
}
llcpp::fuchsia::io::NodeInfo info;
info.set_socket(std::move(socket));
return completer.Reply(std::move(info));
}
void Sync(SyncCompleter::Sync completer) override {
return completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void GetAttr(GetAttrCompleter::Sync completer) override {
return completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void SetAttr(uint32_t flags, ::llcpp::fuchsia::io::NodeAttributes attributes,
SetAttrCompleter::Sync completer) override {
return completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void Ioctl(uint32_t opcode, uint64_t max_out, fidl::VectorView<::zx::handle> handles,
fidl::VectorView<uint8_t> in, IoctlCompleter::Sync completer) override {
return completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void Bind(fidl::VectorView<uint8_t> addr, BindCompleter::Sync completer) override {
return completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void Connect(fidl::VectorView<uint8_t> addr, ConnectCompleter::Sync completer) override {
return completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void Listen(int16_t backlog, ListenCompleter::Sync completer) override {
return completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void Accept(int16_t flags, AcceptCompleter::Sync completer) override {
return completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void GetSockName(GetSockNameCompleter::Sync completer) override {
return completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void GetPeerName(GetPeerNameCompleter::Sync completer) override {
return completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void SetSockOpt(int16_t level, int16_t optname, fidl::VectorView<uint8_t> optval,
SetSockOptCompleter::Sync completer) override {
return completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void GetSockOpt(int16_t level, int16_t optname, GetSockOptCompleter::Sync completer) override {
return completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void IoctlPOSIX(int16_t req, fidl::VectorView<uint8_t> in,
IoctlPOSIXCompleter::Sync completer) override {
return completer.Close(ZX_ERR_NOT_SUPPORTED);
}
private:
zx::socket peer_;
};
static void set_nonblocking_io(int fd) {
int flags;
EXPECT_GE(flags = fcntl(fd, F_GETFL), 0, "%s", strerror(errno));
EXPECT_EQ(fcntl(fd, F_SETFL, flags | O_NONBLOCK), 0, "%s", strerror(errno));
}
TEST(SocketTest, CloseZXSocketOnTransfer) {
zx::channel client_channel, server_channel;
ASSERT_OK(zx::channel::create(0, &client_channel, &server_channel));
zx::socket client_socket, server_socket;
ASSERT_OK(zx::socket::create(ZX_SOCKET_STREAM, &client_socket, &server_socket));
int fd;
{
// We need a functioning server to create the file descriptor. Since the server retains one end
// of the socket, we need to destroy the server before asserting that the socket's peer is
// closed.
Server server(std::move(client_socket));
async::Loop loop(&kAsyncLoopConfigNoAttachToThread);
ASSERT_OK(fidl::Bind(loop.dispatcher(), std::move(server_channel), &server));
ASSERT_OK(loop.StartThread("fake-socket-server"));
ASSERT_OK(fdio_fd_create(client_channel.release(), &fd));
}
zx_signals_t observed;
EXPECT_OK(server_socket.wait_one(ZX_SOCKET_WRITABLE, zx::time::infinite_past(), &observed));
zx_handle_t handle;
EXPECT_OK(fdio_fd_transfer(fd, &handle));
EXPECT_OK(server_socket.wait_one(ZX_SOCKET_PEER_CLOSED, zx::time::infinite_past(), &observed));
EXPECT_OK(zx_handle_close(handle));
}
// Verify scenario, where multi-segment recvmsg is requested, but the socket has
// just enough data to *completely* fill one segment.
// In this scenario, an attempt to read data for the next segment immediately
// fails with ZX_ERR_SHOULD_WAIT, and this may lead to bogus EAGAIN even if some
// data has actually been read.
TEST(SocketTest, RecvmsgNonblockBoundary) {
zx::channel client_channel, server_channel;
ASSERT_OK(zx::channel::create(0, &client_channel, &server_channel));
zx::socket client_socket, server_socket;
ASSERT_OK(zx::socket::create(ZX_SOCKET_STREAM, &client_socket, &server_socket));
Server server(std::move(client_socket));
async::Loop loop(&kAsyncLoopConfigNoAttachToThread);
ASSERT_OK(fidl::Bind(loop.dispatcher(), std::move(server_channel), &server));
ASSERT_OK(loop.StartThread("fake-socket-server"));
int fd;
ASSERT_OK(fdio_fd_create(client_channel.release(), &fd));
set_nonblocking_io(fd);
// Write 4 bytes of data to socket.
size_t actual;
const uint32_t data_out = 0x12345678;
EXPECT_OK(server_socket.write(0, &data_out, sizeof(data_out), &actual));
EXPECT_EQ(actual, sizeof(data_out));
uint32_t data_in1, data_in2;
// Fail at compilation stage if anyone changes types.
// This is mandatory here: we need the first chunk to be exactly the same
// length as total size of data we just wrote.
assert(sizeof(data_in1) == sizeof(data_out));
struct iovec iov[2];
iov[0].iov_base = &data_in1;
iov[0].iov_len = sizeof(data_in1);
iov[1].iov_base = &data_in2;
iov[1].iov_len = sizeof(data_in2);
struct msghdr msg = {};
msg.msg_iov = iov;
msg.msg_iovlen = sizeof(iov) / sizeof(*iov);
EXPECT_EQ(recvmsg(fd, &msg, 0), sizeof(data_out));
EXPECT_EQ(close(fd), 0, "%s", strerror(errno));
}
// Verify scenario, where multi-segment sendmsg is requested, but the socket has
// just enough spare buffer to *completely* read one segment.
// In this scenario, an attempt to send second segment should immediately fail
// with ZX_ERR_SHOULD_WAIT, but the sendmsg should report first segment length
// rather than failing with EAGAIN.
TEST(SocketTest, SendmsgNonblockBoundary) {
zx::channel client_channel, server_channel;
ASSERT_OK(zx::channel::create(0, &client_channel, &server_channel));
zx::socket client_socket, server_socket;
ASSERT_OK(zx::socket::create(ZX_SOCKET_STREAM, &client_socket, &server_socket));
Server server(std::move(client_socket));
async::Loop loop(&kAsyncLoopConfigNoAttachToThread);
ASSERT_OK(fidl::Bind(loop.dispatcher(), std::move(server_channel), &server));
ASSERT_OK(loop.StartThread("fake-socket-server"));
int fd;
ASSERT_OK(fdio_fd_create(client_channel.release(), &fd));
set_nonblocking_io(fd);
const size_t memlength = 65536;
std::unique_ptr<uint8_t[]> memchunk(new uint8_t[memlength]);
struct iovec iov[2];
iov[0].iov_base = memchunk.get();
iov[0].iov_len = memlength;
iov[1].iov_base = memchunk.get();
iov[1].iov_len = memlength;
struct msghdr msg = {};
msg.msg_iov = iov;
msg.msg_iovlen = sizeof(iov) / sizeof(*iov);
// 1. Keep sending data until socket can take no more.
for (;;) {
ssize_t count = sendmsg(fd, &msg, 0);
if (count < 0) {
EXPECT_EQ(errno, EAGAIN, "%s", strerror(errno));
break;
}
EXPECT_GE(count, 0);
}
// 2. Consume one segment of the data
size_t actual;
EXPECT_OK(server_socket.read(0, memchunk.get(), memlength, &actual));
EXPECT_EQ(memlength, actual);
// 3. Push again 2 packets of <memlength> bytes, observe only one sent.
EXPECT_EQ((ssize_t)memlength, sendmsg(fd, &msg, 0));
EXPECT_EQ(close(fd), 0, "%s", strerror(errno));
}
TEST(SocketTest, DatagramSendMsg) {
zx::channel client_channel, server_channel;
ASSERT_OK(zx::channel::create(0, &client_channel, &server_channel));
zx::socket client_socket, server_socket;
ASSERT_OK(zx::socket::create(ZX_SOCKET_DATAGRAM, &client_socket, &server_socket));
Server server(std::move(client_socket));
async::Loop loop(&kAsyncLoopConfigNoAttachToThread);
ASSERT_OK(fidl::Bind(loop.dispatcher(), std::move(server_channel), &server));
ASSERT_OK(loop.StartThread("fake-socket-server"));
int fd;
ASSERT_OK(fdio_fd_create(client_channel.release(), &fd));
struct sockaddr_in addr = {};
socklen_t addrlen = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(0);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
const char buf[] = "hello";
char rcv_buf[4096] = {0};
std::array<struct iovec, 1> iov = {{{
.iov_base = (void *)buf,
.iov_len = sizeof(buf),
}}};
struct msghdr msg = {};
msg.msg_name = &addr;
msg.msg_namelen = addrlen;
msg.msg_iov = iov.data();
msg.msg_iovlen = iov.size();
EXPECT_EQ(sendmsg(fd, &msg, 0), sizeof(buf), "%s", strerror(errno));
// Expect sendmsg() to fail when msg_namelen is greater than sizeof(struct sockaddr_storage).
msg.msg_namelen = sizeof(sockaddr_storage) + 1;
EXPECT_EQ(sendmsg(fd, &msg, 0), -1);
EXPECT_EQ(errno, EINVAL, "%s", strerror(errno));
size_t actual = 0;
EXPECT_OK(server_socket.read(0, rcv_buf, sizeof(rcv_buf), &actual));
EXPECT_EQ(actual - FDIO_SOCKET_MSG_HEADER_SIZE, sizeof(buf));
EXPECT_EQ(close(fd), 0, "%s", strerror(errno));
}
} // namespace