1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "src/traced_relay/relay_service.h"
18
19 #include <memory>
20
21 #include "perfetto/ext/base/unix_socket.h"
22 #include "protos/perfetto/ipc/wire_protocol.gen.h"
23 #include "src/base/test/test_task_runner.h"
24 #include "src/ipc/buffered_frame_deserializer.h"
25 #include "test/gtest_and_gmock.h"
26
27 // Disable tests on MacOS and Windows as neither abstract sockets nor pseudo
28 // boot ID are supported.
29 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
30 PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
31
32 namespace perfetto {
33 namespace {
34
35 using ::testing::_;
36 using ::testing::Invoke;
37
38 class TestEventListener : public base::UnixSocket::EventListener {
39 public:
40 MOCK_METHOD(void, OnDataAvailable, (base::UnixSocket*), (override));
41 MOCK_METHOD(void, OnConnect, (base::UnixSocket*, bool), (override));
42 MOCK_METHOD(void, OnNewIncomingConnection, (base::UnixSocket*));
43
OnNewIncomingConnection(base::UnixSocket *,std::unique_ptr<base::UnixSocket> new_connection)44 void OnNewIncomingConnection(
45 base::UnixSocket*,
46 std::unique_ptr<base::UnixSocket> new_connection) override {
47 // Need to keep |new_connection| alive.
48 client_connection_ = std::move(new_connection);
49 OnNewIncomingConnection(client_connection_.get());
50 }
51
52 private:
53 std::unique_ptr<base::UnixSocket> client_connection_;
54 };
55
56 // Exercises the relay service and also validates that the relay service injects
57 // a SetPeerIdentity message:
58 //
59 // producer (client UnixSocket) <- @producer.sock -> relay service
60 // <- 127.0.0.1.* -> tcp_server (listening UnixSocet).
TEST(RelayServiceTest,SetPeerIdentity)61 TEST(RelayServiceTest, SetPeerIdentity) {
62 base::TestTaskRunner task_runner;
63 auto relay_service = std::make_unique<RelayService>(&task_runner);
64 // Disable the extra socket connection created by RelayClient.
65 relay_service->SetRelayClientDisabledForTesting(true);
66
67 // Set up a server UnixSocket to find an unused TCP port.
68 // The TCP connection emulates the socket to the host traced.
69 TestEventListener tcp_listener;
70 auto tcp_server = base::UnixSocket::Listen(
71 "127.0.0.1:0", &tcp_listener, &task_runner, base::SockFamily::kInet,
72 base::SockType::kStream);
73 ASSERT_TRUE(tcp_server->is_listening());
74 auto tcp_sock_name = tcp_server->GetSockAddr();
75 auto* unix_sock_name = "@producer.sock"; // Use abstract socket for server.
76
77 // Start the relay service.
78 relay_service->Start(unix_sock_name, tcp_sock_name.c_str());
79
80 // Emulates the producer connection.
81 TestEventListener producer_listener;
82 auto producer = base::UnixSocket::Connect(
83 unix_sock_name, &producer_listener, &task_runner, base::SockFamily::kUnix,
84 base::SockType::kStream);
85 auto producer_connected = task_runner.CreateCheckpoint("producer_connected");
86 EXPECT_CALL(producer_listener, OnConnect(_, _))
87 .WillOnce(Invoke([&](base::UnixSocket* s, bool conn) {
88 EXPECT_TRUE(conn);
89 EXPECT_EQ(s, producer.get());
90 producer_connected();
91 }));
92 task_runner.RunUntilCheckpoint("producer_connected");
93
94 // Add some producer data.
95 ipc::Frame test_frame;
96 test_frame.add_data_for_testing("test_data");
97 auto test_data = ipc::BufferedFrameDeserializer::Serialize(test_frame);
98 producer->SendStr(test_data);
99
100 base::UnixSocket* tcp_client_connection = nullptr;
101 auto tcp_client_connected =
102 task_runner.CreateCheckpoint("tcp_client_connected");
103 EXPECT_CALL(tcp_listener, OnNewIncomingConnection(_))
104 .WillOnce(Invoke([&](base::UnixSocket* client) {
105 tcp_client_connection = client;
106 tcp_client_connected();
107 }));
108 task_runner.RunUntilCheckpoint("tcp_client_connected");
109
110 // Asserts that we can receive the SetPeerIdentity message.
111 auto peer_identity_recv = task_runner.CreateCheckpoint("peer_identity_recv");
112 ipc::BufferedFrameDeserializer deserializer;
113 EXPECT_CALL(tcp_listener, OnDataAvailable(_))
114 .WillRepeatedly(Invoke([&](base::UnixSocket* tcp_conn) {
115 auto buf = deserializer.BeginReceive();
116 auto rsize = tcp_conn->Receive(buf.data, buf.size);
117 EXPECT_TRUE(deserializer.EndReceive(rsize));
118
119 auto frame = deserializer.PopNextFrame();
120 EXPECT_TRUE(frame->has_set_peer_identity());
121
122 const auto& set_peer_identity = frame->set_peer_identity();
123 EXPECT_EQ(set_peer_identity.pid(), getpid());
124 EXPECT_EQ(set_peer_identity.uid(), static_cast<int32_t>(geteuid()));
125 EXPECT_TRUE(set_peer_identity.has_machine_id_hint());
126
127 frame = deserializer.PopNextFrame();
128 EXPECT_EQ(1u, frame->data_for_testing().size());
129 EXPECT_EQ(std::string("test_data"), frame->data_for_testing()[0]);
130
131 peer_identity_recv();
132 }));
133 task_runner.RunUntilCheckpoint("peer_identity_recv");
134 }
135
TEST(RelayServiceTest,MachineIDHint)136 TEST(RelayServiceTest, MachineIDHint) {
137 base::TestTaskRunner task_runner;
138 auto relay_service = std::make_unique<RelayService>(&task_runner);
139
140 auto hint1 = relay_service->GetMachineIdHint();
141 auto hint2 =
142 relay_service->GetMachineIdHint(/*use_pseudo_boot_id_for_testing=*/true);
143 EXPECT_NE(hint1, hint2);
144
145 // Add a short sleep to verify that pseudo boot ID isn't affected.
146 std::this_thread::sleep_for(std::chrono::milliseconds(1));
147
148 relay_service = std::make_unique<RelayService>(&task_runner);
149 auto hint3 = relay_service->GetMachineIdHint();
150 auto hint4 =
151 relay_service->GetMachineIdHint(/*use_pseudo_boot_id_for_testing=*/true);
152 EXPECT_NE(hint3, hint4);
153
154 EXPECT_FALSE(hint1.empty());
155 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
156 // This test can run on Android kernel 3.x, but pseudo boot ID uses statx(2)
157 // that requires kernel 4.11.
158 EXPECT_FALSE(hint2.empty());
159 #endif
160
161 EXPECT_EQ(hint1, hint3);
162 EXPECT_EQ(hint2, hint4);
163 }
164
165 // Test that the RelayClient notifies its usr with the callback on
166 // connection errors.
TEST(RelayClientTest,OnErrorCallback)167 TEST(RelayClientTest, OnErrorCallback) {
168 base::TestTaskRunner task_runner;
169
170 // Set up a server UnixSocket to find an unused TCP port.
171 // The TCP connection emulates the socket to the host traced.
172 TestEventListener tcp_listener;
173 auto tcp_server = base::UnixSocket::Listen(
174 "127.0.0.1:0", &tcp_listener, &task_runner, base::SockFamily::kInet,
175 base::SockType::kStream);
176 ASSERT_TRUE(tcp_server->is_listening());
177 auto tcp_sock_name = tcp_server->GetSockAddr();
178
179 auto on_relay_client_error =
180 task_runner.CreateCheckpoint("on_relay_client_error");
181 auto on_error_callback = [&]() { on_relay_client_error(); };
182 auto relay_client = std::make_unique<RelayClient>(
183 tcp_sock_name, "fake_machine_id_hint", &task_runner, on_error_callback);
184
185 base::UnixSocket* tcp_client_connection = nullptr;
186 auto tcp_client_connected =
187 task_runner.CreateCheckpoint("tcp_client_connected");
188 EXPECT_CALL(tcp_listener, OnNewIncomingConnection(_))
189 .WillOnce(Invoke([&](base::UnixSocket* client) {
190 tcp_client_connection = client;
191 tcp_client_connected();
192 }));
193 task_runner.RunUntilCheckpoint("tcp_client_connected");
194
195 // Just drain the data passed over the socket.
196 EXPECT_CALL(tcp_listener, OnDataAvailable(_))
197 .WillRepeatedly(Invoke([&](base::UnixSocket* tcp_conn) {
198 ::testing::IgnoreResult(tcp_conn->ReceiveString());
199 }));
200
201 EXPECT_FALSE(relay_client->clock_synced_with_service_for_testing());
202 // Shutdown the connected connection. The RelayClient should notice this
203 // error.
204 tcp_client_connection->Shutdown(true);
205 task_runner.RunUntilCheckpoint("on_relay_client_error");
206
207 // Shutdown the server. The RelayClient should notice that the connection is
208 // refused.
209 tcp_server->Shutdown(true);
210 on_relay_client_error =
211 task_runner.CreateCheckpoint("on_relay_client_error_2");
212 relay_client = std::make_unique<RelayClient>(
213 tcp_sock_name, "fake_machine_id_hint", &task_runner,
214 [&]() { on_relay_client_error(); });
215 task_runner.RunUntilCheckpoint("on_relay_client_error_2");
216 }
217
TEST(RelayClientTest,SetPeerIdentity)218 TEST(RelayClientTest, SetPeerIdentity) {
219 base::TestTaskRunner task_runner;
220 // Set up a server UnixSocket to find an unused TCP port.
221 // The TCP connection emulates the socket to the host traced.
222 TestEventListener tcp_listener;
223 auto tcp_server = base::UnixSocket::Listen(
224 "127.0.0.1:0", &tcp_listener, &task_runner, base::SockFamily::kInet,
225 base::SockType::kStream);
226 ASSERT_TRUE(tcp_server->is_listening());
227 auto tcp_sock_name = tcp_server->GetSockAddr();
228 auto on_error_callback = [&]() { FAIL() << "Should not be called"; };
229 auto relay_service = std::make_unique<RelayClient>(
230 tcp_sock_name, "fake_machine_id_hint", &task_runner, on_error_callback);
231
232 base::UnixSocket* tcp_client_connection = nullptr;
233 auto tcp_client_connected =
234 task_runner.CreateCheckpoint("tcp_client_connected");
235 EXPECT_CALL(tcp_listener, OnNewIncomingConnection(_))
236 .WillOnce(Invoke([&](base::UnixSocket* client) {
237 tcp_client_connection = client;
238 tcp_client_connected();
239 }));
240 task_runner.RunUntilCheckpoint("tcp_client_connected");
241
242 // Asserts that we can receive the SetPeerIdentity message.
243 auto peer_identity_recv = task_runner.CreateCheckpoint("peer_identity_recv");
244 ipc::BufferedFrameDeserializer deserializer;
245 EXPECT_CALL(tcp_listener, OnDataAvailable(_))
246 .WillRepeatedly(Invoke([&](base::UnixSocket* tcp_conn) {
247 auto buf = deserializer.BeginReceive();
248 auto rsize = tcp_conn->Receive(buf.data, buf.size);
249 EXPECT_TRUE(deserializer.EndReceive(rsize));
250
251 auto frame = deserializer.PopNextFrame();
252 EXPECT_TRUE(frame->has_set_peer_identity());
253
254 const auto& set_peer_identity = frame->set_peer_identity();
255 EXPECT_EQ(set_peer_identity.pid(), getpid());
256 EXPECT_EQ(set_peer_identity.uid(), static_cast<int32_t>(geteuid()));
257 EXPECT_EQ(set_peer_identity.machine_id_hint(), "fake_machine_id_hint");
258
259 peer_identity_recv();
260 }));
261 task_runner.RunUntilCheckpoint("peer_identity_recv");
262 }
263
264 } // namespace
265 } // namespace perfetto
266
267 #endif
268