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