// 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 #include #include #include #include #include #include #include #include #include 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(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(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(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( 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_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);