| // 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) |