1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <fidl/test/fakesocket/c/fidl.h>
6 #include <lib/async-loop/loop.h>
7 #include <lib/async/wait.h>
8 #include <lib/fidl-async/bind.h>
9 #include <lib/fidl/transport.h>
10 #include <lib/zx/socket.h>
11 #include <string.h>
12 #include <zircon/fidl.h>
13 #include <zircon/syscalls.h>
14
15 #include <unittest/unittest.h>
16
17 typedef struct fidl_socket_binding {
18 async_wait_t wait;
19 fidl_dispatch_t* dispatch;
20 async_dispatcher_t* dispatcher;
21 void* ctx;
22 const void* ops;
23 } fidl_socket_binding_t;
24
25 typedef struct fidl_socket_connection {
26 fidl_txn_t txn;
27 zx_handle_t socket;
28 } fidl_socket_connection_t;
29
fidl_socket_reply(fidl_txn_t * txn,const fidl_msg_t * msg)30 static zx_status_t fidl_socket_reply(fidl_txn_t* txn, const fidl_msg_t* msg) {
31 fidl_socket_connection_t* conn = reinterpret_cast<fidl_socket_connection_t*>(txn);
32 if (msg->num_handles > 0u) {
33 zx_handle_close_many(msg->handles, msg->num_handles);
34 }
35 return fidl_socket_write_control(conn->socket, msg->bytes, msg->num_bytes);
36 }
37
fidl_socket_binding_destroy(fidl_socket_binding_t * binding)38 static void fidl_socket_binding_destroy(fidl_socket_binding_t* binding) {
39 zx_handle_close(binding->wait.object);
40 free(binding);
41 }
42
fidl_socket_message_handler(async_dispatcher_t * dispatcher,async_wait_t * wait,zx_status_t status,const zx_packet_signal_t * signal)43 static void fidl_socket_message_handler(async_dispatcher_t* dispatcher,
44 async_wait_t* wait,
45 zx_status_t status,
46 const zx_packet_signal_t* signal) {
47 fidl_socket_binding_t* binding = reinterpret_cast<fidl_socket_binding_t*>(wait);
48 if (status != ZX_OK) {
49 goto shutdown;
50 }
51
52 if (signal->observed & ZX_SOCKET_CONTROL_READABLE) {
53 char bytes[1024];
54 for (uint64_t i = 0; i < signal->count; i++) {
55 fidl_msg_t msg = {
56 .bytes = bytes,
57 .handles = nullptr,
58 .num_bytes = 0u,
59 .num_handles = 0u,
60 };
61 size_t actual = 0u;
62 status = fidl_socket_read_control(wait->object, msg.bytes,
63 sizeof(bytes), &actual);
64 msg.num_bytes = static_cast<uint32_t>(actual);
65 if (status != ZX_OK) {
66 goto shutdown;
67 }
68 fidl_socket_connection_t conn = {
69 .txn = {.reply = fidl_socket_reply},
70 .socket = wait->object,
71 };
72 status = binding->dispatch(binding->ctx, &conn.txn, &msg, binding->ops);
73 switch (status) {
74 case ZX_OK:
75 status = async_begin_wait(dispatcher, wait);
76 if (status != ZX_OK) {
77 goto shutdown;
78 }
79 return;
80 default:
81 goto shutdown;
82 }
83 }
84 }
85
86 shutdown:
87 fidl_socket_binding_destroy(binding);
88 }
89
fidl_bind_socket(async_dispatcher_t * dispatcher,zx_handle_t socket,fidl_dispatch_t * dispatch,void * ctx,const void * ops)90 zx_status_t fidl_bind_socket(async_dispatcher_t* dispatcher, zx_handle_t socket,
91 fidl_dispatch_t* dispatch, void* ctx, const void* ops) {
92 fidl_socket_binding_t* binding = static_cast<fidl_socket_binding_t*>(
93 calloc(1, sizeof(fidl_socket_binding_t)));
94 binding->wait.handler = fidl_socket_message_handler;
95 binding->wait.object = socket;
96 binding->wait.trigger = ZX_SOCKET_CONTROL_READABLE | ZX_SOCKET_PEER_CLOSED;
97 binding->dispatch = dispatch;
98 binding->dispatcher = dispatcher;
99 binding->ctx = ctx;
100 binding->ops = ops;
101 zx_status_t status = async_begin_wait(dispatcher, &binding->wait);
102 if (status != ZX_OK) {
103 fidl_socket_binding_destroy(binding);
104 }
105 return status;
106 }
107
Control_Bind(void * ctx,const char * addr_data,size_t addr_size)108 static zx_status_t Control_Bind(void* ctx, const char* addr_data, size_t addr_size) {
109 EXPECT_EQ('x', addr_data[0]);
110 EXPECT_EQ(2u, addr_size);
111 return ZX_OK;
112 }
113
Control_GetPeerAddr(void * ctx,int32_t index,fidl_txn_t * txn)114 static zx_status_t Control_GetPeerAddr(void* ctx, int32_t index, fidl_txn_t* txn) {
115 EXPECT_EQ(5, index);
116 return fidl_test_fakesocket_ControlGetPeerAddr_reply(txn, "abc", 3);
117 }
118
119 static const fidl_test_fakesocket_Control_ops_t kOps = {
120 .Bind = Control_Bind,
121 .GetPeerAddr = Control_GetPeerAddr,
122 };
123
basic_test(void)124 static bool basic_test(void) {
125 BEGIN_TEST;
126
127 zx::socket client, server;
128 ASSERT_EQ(ZX_OK, zx::socket::create(ZX_SOCKET_HAS_CONTROL, &client, &server));
129
130 async_loop_t* loop = nullptr;
131 ASSERT_EQ(ZX_OK, async_loop_create(&kAsyncLoopConfigNoAttachToThread, &loop), "");
132 ASSERT_EQ(ZX_OK, async_loop_start_thread(loop, "spaceship-dispatcher", nullptr), "");
133
134 async_dispatcher_t* dispatcher = async_loop_get_dispatcher(loop);
135
136 ASSERT_EQ(ZX_OK, fidl_bind_socket(dispatcher, server.release(),
137 reinterpret_cast<fidl_dispatch_t*>(fidl_test_fakesocket_Control_dispatch),
138 nullptr, &kOps));
139
140 ASSERT_EQ(ZX_OK, fidl_test_fakesocket_ControlBind(client.get(), "xy", 2u));
141
142 char buffer[64];
143 memset(buffer, 0, sizeof(buffer));
144 size_t actual = 0u;
145 ASSERT_EQ(ZX_OK, fidl_test_fakesocket_ControlGetPeerAddr(client.get(), 5, buffer, sizeof(buffer), &actual));
146 ASSERT_EQ(3u, actual);
147 buffer[3] = '\0';
148 ASSERT_STR_EQ("abc", buffer);
149
150 client.reset();
151
152 async_loop_destroy(loop);
153
154 END_TEST;
155 }
156
157 BEGIN_TEST_CASE(fakesocket_tests)
158 RUN_NAMED_TEST("basic test", basic_test)
159 END_TEST_CASE(fakesocket_tests);
160