blob: d3b2aff293fa01368705ea3b3a05ba79d1e1e83e [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 <fidl/test/fakesocket/c/fidl.h>
#include <lib/async-loop/loop.h>
#include <lib/async/wait.h>
#include <lib/fidl-async/bind.h>
#include <lib/fidl/transport.h>
#include <lib/zx/socket.h>
#include <string.h>
#include <zircon/fidl.h>
#include <zircon/syscalls.h>
#include <unittest/unittest.h>
typedef struct fidl_socket_binding {
async_wait_t wait;
fidl_dispatch_t* dispatch;
async_dispatcher_t* dispatcher;
void* ctx;
const void* ops;
} fidl_socket_binding_t;
typedef struct fidl_socket_connection {
fidl_txn_t txn;
zx_handle_t socket;
} fidl_socket_connection_t;
static zx_status_t fidl_socket_reply(fidl_txn_t* txn, const fidl_msg_t* msg) {
fidl_socket_connection_t* conn = reinterpret_cast<fidl_socket_connection_t*>(txn);
if (msg->num_handles > 0u) {
zx_handle_close_many(msg->handles, msg->num_handles);
}
return fidl_socket_write_control(conn->socket, msg->bytes, msg->num_bytes);
}
static void fidl_socket_binding_destroy(fidl_socket_binding_t* binding) {
zx_handle_close(binding->wait.object);
free(binding);
}
static void fidl_socket_message_handler(async_dispatcher_t* dispatcher,
async_wait_t* wait,
zx_status_t status,
const zx_packet_signal_t* signal) {
fidl_socket_binding_t* binding = reinterpret_cast<fidl_socket_binding_t*>(wait);
if (status != ZX_OK) {
goto shutdown;
}
if (signal->observed & ZX_SOCKET_CONTROL_READABLE) {
char bytes[1024];
for (uint64_t i = 0; i < signal->count; i++) {
fidl_msg_t msg = {
.bytes = bytes,
.handles = nullptr,
.num_bytes = 0u,
.num_handles = 0u,
};
size_t actual = 0u;
status = fidl_socket_read_control(wait->object, msg.bytes,
sizeof(bytes), &actual);
msg.num_bytes = static_cast<uint32_t>(actual);
if (status != ZX_OK) {
goto shutdown;
}
fidl_socket_connection_t conn = {
.txn = {.reply = fidl_socket_reply},
.socket = wait->object,
};
status = binding->dispatch(binding->ctx, &conn.txn, &msg, binding->ops);
switch (status) {
case ZX_OK:
status = async_begin_wait(dispatcher, wait);
if (status != ZX_OK) {
goto shutdown;
}
return;
default:
goto shutdown;
}
}
}
shutdown:
fidl_socket_binding_destroy(binding);
}
zx_status_t fidl_bind_socket(async_dispatcher_t* dispatcher, zx_handle_t socket,
fidl_dispatch_t* dispatch, void* ctx, const void* ops) {
fidl_socket_binding_t* binding = static_cast<fidl_socket_binding_t*>(
calloc(1, sizeof(fidl_socket_binding_t)));
binding->wait.handler = fidl_socket_message_handler;
binding->wait.object = socket;
binding->wait.trigger = ZX_SOCKET_CONTROL_READABLE | ZX_SOCKET_PEER_CLOSED;
binding->dispatch = dispatch;
binding->dispatcher = dispatcher;
binding->ctx = ctx;
binding->ops = ops;
zx_status_t status = async_begin_wait(dispatcher, &binding->wait);
if (status != ZX_OK) {
fidl_socket_binding_destroy(binding);
}
return status;
}
static zx_status_t Control_Bind(void* ctx, const char* addr_data, size_t addr_size) {
EXPECT_EQ('x', addr_data[0]);
EXPECT_EQ(2u, addr_size);
return ZX_OK;
}
static zx_status_t Control_GetPeerAddr(void* ctx, int32_t index, fidl_txn_t* txn) {
EXPECT_EQ(5, index);
return fidl_test_fakesocket_ControlGetPeerAddr_reply(txn, "abc", 3);
}
static const fidl_test_fakesocket_Control_ops_t kOps = {
.Bind = Control_Bind,
.GetPeerAddr = Control_GetPeerAddr,
};
static bool basic_test(void) {
BEGIN_TEST;
zx::socket client, server;
ASSERT_EQ(ZX_OK, zx::socket::create(ZX_SOCKET_HAS_CONTROL, &client, &server));
async_loop_t* loop = nullptr;
ASSERT_EQ(ZX_OK, async_loop_create(&kAsyncLoopConfigNoAttachToThread, &loop), "");
ASSERT_EQ(ZX_OK, async_loop_start_thread(loop, "spaceship-dispatcher", nullptr), "");
async_dispatcher_t* dispatcher = async_loop_get_dispatcher(loop);
ASSERT_EQ(ZX_OK, fidl_bind_socket(dispatcher, server.release(),
reinterpret_cast<fidl_dispatch_t*>(fidl_test_fakesocket_Control_dispatch),
nullptr, &kOps));
ASSERT_EQ(ZX_OK, fidl_test_fakesocket_ControlBind(client.get(), "xy", 2u));
char buffer[64];
memset(buffer, 0, sizeof(buffer));
size_t actual = 0u;
ASSERT_EQ(ZX_OK, fidl_test_fakesocket_ControlGetPeerAddr(client.get(), 5, buffer, sizeof(buffer), &actual));
ASSERT_EQ(3u, actual);
buffer[3] = '\0';
ASSERT_STR_EQ("abc", buffer);
client.reset();
async_loop_destroy(loop);
END_TEST;
}
BEGIN_TEST_CASE(fakesocket_tests)
RUN_NAMED_TEST("basic test", basic_test)
END_TEST_CASE(fakesocket_tests)