1 /*
2 * Copyright (C) 2019 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 <array>
18
19 #include <android-base/test_utils.h>
20 #include <gmock/gmock.h>
21 #include <gtest/gtest.h>
22
23 #include "DnsStats.h"
24
25 namespace android::net {
26
27 using namespace std::chrono_literals;
28 using android::netdutils::IPSockAddr;
29 using std::chrono::milliseconds;
30 using ::testing::IsEmpty;
31 using ::testing::UnorderedElementsAreArray;
32
33 namespace {
34
makeDnsQueryEvent(const Protocol protocol,const NsRcode rcode,const milliseconds & latency)35 DnsQueryEvent makeDnsQueryEvent(const Protocol protocol, const NsRcode rcode,
36 const milliseconds& latency) {
37 DnsQueryEvent event;
38 event.set_protocol(protocol);
39 event.set_rcode(rcode);
40 event.set_latency_micros(latency.count() * 1000);
41 return event;
42 }
43
makeStatsData(const IPSockAddr & server,const int total,const milliseconds & latencyMs,const std::map<int,int> & rcodeCounts)44 StatsData makeStatsData(const IPSockAddr& server, const int total, const milliseconds& latencyMs,
45 const std::map<int, int>& rcodeCounts) {
46 StatsData ret(server);
47 ret.total = total;
48 ret.latencyUs = latencyMs;
49 ret.rcodeCounts = rcodeCounts;
50 return ret;
51 }
52
53 } // namespace
54
55 class StatsRecordsTest : public ::testing::Test {};
56
TEST_F(StatsRecordsTest,PushRecord)57 TEST_F(StatsRecordsTest, PushRecord) {
58 const IPSockAddr server = IPSockAddr::toIPSockAddr("127.0.0.2", 53);
59 constexpr size_t size = 3;
60 const StatsRecords::Record recordNoError = {NS_R_NO_ERROR, 10ms};
61 const StatsRecords::Record recordTimeout = {NS_R_TIMEOUT, 250ms};
62
63 StatsRecords sr(server, size);
64 EXPECT_EQ(sr.getStatsData(), makeStatsData(server, 0, 0ms, {}));
65
66 sr.push(recordNoError);
67 EXPECT_EQ(sr.getStatsData(), makeStatsData(server, 1, 10ms, {{NS_R_NO_ERROR, 1}}));
68
69 sr.push(recordNoError);
70 EXPECT_EQ(sr.getStatsData(), makeStatsData(server, 2, 20ms, {{NS_R_NO_ERROR, 2}}));
71
72 sr.push(recordTimeout);
73 EXPECT_EQ(sr.getStatsData(),
74 makeStatsData(server, 3, 270ms, {{NS_R_NO_ERROR, 2}, {NS_R_TIMEOUT, 1}}));
75
76 sr.push(recordTimeout);
77 EXPECT_EQ(sr.getStatsData(),
78 makeStatsData(server, 3, 510ms, {{NS_R_NO_ERROR, 1}, {NS_R_TIMEOUT, 2}}));
79
80 sr.push(recordTimeout);
81 EXPECT_EQ(sr.getStatsData(),
82 makeStatsData(server, 3, 750ms, {{NS_R_NO_ERROR, 0}, {NS_R_TIMEOUT, 3}}));
83 }
84
85 class DnsStatsTest : public ::testing::Test {
86 protected:
captureDumpOutput()87 std::string captureDumpOutput() {
88 netdutils::DumpWriter dw(STDOUT_FILENO);
89 CapturedStdout captured;
90 mDnsStats.dump(dw);
91 return captured.str();
92 }
93
94 // Get the output string from dump() and check the content.
verifyDumpOutput(const std::vector<StatsData> & tcpData,const std::vector<StatsData> & udpData,const std::vector<StatsData> & dotData)95 void verifyDumpOutput(const std::vector<StatsData>& tcpData,
96 const std::vector<StatsData>& udpData,
97 const std::vector<StatsData>& dotData) {
98 // A simple pattern to capture two matches:
99 // server address (empty allowed) and its statistics.
100 const std::regex pattern(R"(\s{4,}([0-9a-fA-F:\.]*) ([<(].*[>)]))");
101 std::string dumpString = captureDumpOutput();
102
103 const auto check = [&](const std::vector<StatsData>& statsData, const std::string& protocol,
104 std::string* dumpString) {
105 SCOPED_TRACE(protocol);
106 ASSERT_NE(dumpString->find(protocol), std::string::npos);
107 std::smatch sm;
108
109 // Expect to show something even if none of servers is set.
110 if (statsData.empty()) {
111 ASSERT_TRUE(std::regex_search(*dumpString, sm, pattern));
112 EXPECT_TRUE(sm[1].str().empty());
113 EXPECT_EQ(sm[2], "<no server>");
114 *dumpString = sm.suffix();
115 return;
116 }
117
118 for (const auto& stats : statsData) {
119 ASSERT_TRUE(std::regex_search(*dumpString, sm, pattern));
120 EXPECT_EQ(sm[1], stats.serverSockAddr.ip().toString());
121 EXPECT_FALSE(sm[2].str().empty());
122 *dumpString = sm.suffix();
123 }
124 };
125
126 check(udpData, "UDP", &dumpString);
127 check(dotData, "TLS", &dumpString);
128 check(tcpData, "TCP", &dumpString);
129
130 // Ensure the whole string has been checked.
131 EXPECT_EQ(dumpString, "\n");
132 }
133
134 DnsStats mDnsStats;
135 };
136
TEST_F(DnsStatsTest,SetServers)137 TEST_F(DnsStatsTest, SetServers) {
138 // Check before any operation to mDnsStats.
139 verifyDumpOutput({}, {}, {});
140
141 static const struct {
142 std::vector<std::string> servers;
143 std::vector<std::string> expectation;
144 bool isSuccess;
145 } tests[] = {
146 // Normal case.
147 {
148 {"127.0.0.1", "127.0.0.2", "fe80::1%22", "2001:db8::2", "::1"},
149 {"127.0.0.1", "127.0.0.2", "fe80::1%22", "2001:db8::2", "::1"},
150 true,
151 },
152 // Duplicate servers.
153 {
154 {"127.0.0.1", "2001:db8::2", "127.0.0.1", "2001:db8::2"},
155 {"127.0.0.1", "2001:db8::2"},
156 true,
157 },
158 // Invalid server addresses. The state remains in previous state.
159 {
160 {"not_an_ip", "127.0.0.3", "127.a.b.2"},
161 {"127.0.0.1", "2001:db8::2"},
162 false,
163 },
164 // Clean up the old servers 127.0.0.1 and 127.0.0.2.
165 {
166 {"127.0.0.4", "2001:db8::5"},
167 {"127.0.0.4", "2001:db8::5"},
168 true,
169 },
170 // Empty list.
171 {{}, {}, true},
172 };
173
174 for (const auto& [servers, expectation, isSuccess] : tests) {
175 std::vector<IPSockAddr> ipSockAddrs;
176 ipSockAddrs.reserve(servers.size());
177 for (const auto& server : servers) {
178 ipSockAddrs.push_back(IPSockAddr::toIPSockAddr(server, 53));
179 }
180
181 EXPECT_TRUE(mDnsStats.setServers(ipSockAddrs, PROTO_TCP) == isSuccess);
182 EXPECT_TRUE(mDnsStats.setServers(ipSockAddrs, PROTO_UDP) == isSuccess);
183 EXPECT_TRUE(mDnsStats.setServers(ipSockAddrs, PROTO_DOT) == isSuccess);
184
185 std::vector<StatsData> expectedStats;
186 expectedStats.reserve(expectation.size());
187 for (const auto& exp : expectation) {
188 expectedStats.push_back(makeStatsData(IPSockAddr::toIPSockAddr(exp, 53), 0, 0ms, {}));
189 }
190
191 EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), UnorderedElementsAreArray(expectedStats));
192 EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats));
193 EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), UnorderedElementsAreArray(expectedStats));
194 }
195
196 verifyDumpOutput({}, {}, {});
197 }
198
TEST_F(DnsStatsTest,SetServersDifferentPorts)199 TEST_F(DnsStatsTest, SetServersDifferentPorts) {
200 const std::vector<IPSockAddr> servers = {
201 IPSockAddr::toIPSockAddr("127.0.0.1", 0), IPSockAddr::toIPSockAddr("fe80::1", 0),
202 IPSockAddr::toIPSockAddr("127.0.0.1", 53), IPSockAddr::toIPSockAddr("127.0.0.1", 5353),
203 IPSockAddr::toIPSockAddr("127.0.0.1", 853), IPSockAddr::toIPSockAddr("fe80::1", 53),
204 IPSockAddr::toIPSockAddr("fe80::1", 5353), IPSockAddr::toIPSockAddr("fe80::1", 853),
205 };
206
207 // Servers setup fails due to port unset.
208 EXPECT_FALSE(mDnsStats.setServers(servers, PROTO_TCP));
209 EXPECT_FALSE(mDnsStats.setServers(servers, PROTO_UDP));
210 EXPECT_FALSE(mDnsStats.setServers(servers, PROTO_DOT));
211
212 EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), IsEmpty());
213 EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), IsEmpty());
214 EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), IsEmpty());
215 verifyDumpOutput({}, {}, {});
216
217 EXPECT_TRUE(mDnsStats.setServers(std::vector(servers.begin() + 2, servers.end()), PROTO_TCP));
218 EXPECT_TRUE(mDnsStats.setServers(std::vector(servers.begin() + 2, servers.end()), PROTO_UDP));
219 EXPECT_TRUE(mDnsStats.setServers(std::vector(servers.begin() + 2, servers.end()), PROTO_DOT));
220
221 const std::vector<StatsData> expectedStats = {
222 makeStatsData(servers[2], 0, 0ms, {}), makeStatsData(servers[3], 0, 0ms, {}),
223 makeStatsData(servers[4], 0, 0ms, {}), makeStatsData(servers[5], 0, 0ms, {}),
224 makeStatsData(servers[6], 0, 0ms, {}), makeStatsData(servers[7], 0, 0ms, {}),
225 };
226
227 EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), UnorderedElementsAreArray(expectedStats));
228 EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats));
229 EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), UnorderedElementsAreArray(expectedStats));
230 verifyDumpOutput(expectedStats, expectedStats, expectedStats);
231 }
232
TEST_F(DnsStatsTest,AddStatsAndClear)233 TEST_F(DnsStatsTest, AddStatsAndClear) {
234 const std::vector<IPSockAddr> servers = {
235 IPSockAddr::toIPSockAddr("127.0.0.1", 53),
236 IPSockAddr::toIPSockAddr("127.0.0.2", 53),
237 };
238 const DnsQueryEvent record = makeDnsQueryEvent(PROTO_UDP, NS_R_NO_ERROR, 10ms);
239
240 EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_TCP));
241 EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_UDP));
242
243 // Fail to add stats because of incorrect arguments.
244 EXPECT_FALSE(mDnsStats.addStats(IPSockAddr::toIPSockAddr("127.0.0.4", 53), record));
245 EXPECT_FALSE(mDnsStats.addStats(IPSockAddr::toIPSockAddr("127.a.b.4", 53), record));
246
247 EXPECT_TRUE(mDnsStats.addStats(servers[0], record));
248 EXPECT_TRUE(mDnsStats.addStats(servers[0], record));
249 EXPECT_TRUE(mDnsStats.addStats(servers[1], record));
250
251 const std::vector<StatsData> expectedStatsForTcp = {
252 makeStatsData(servers[0], 0, 0ms, {}),
253 makeStatsData(servers[1], 0, 0ms, {}),
254 };
255 const std::vector<StatsData> expectedStatsForUdp = {
256 makeStatsData(servers[0], 2, 20ms, {{NS_R_NO_ERROR, 2}}),
257 makeStatsData(servers[1], 1, 10ms, {{NS_R_NO_ERROR, 1}}),
258 };
259
260 EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), UnorderedElementsAreArray(expectedStatsForTcp));
261 EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStatsForUdp));
262 EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), IsEmpty());
263 verifyDumpOutput(expectedStatsForTcp, expectedStatsForUdp, {});
264
265 // Clear stats.
266 EXPECT_TRUE(mDnsStats.setServers({}, PROTO_TCP));
267 EXPECT_TRUE(mDnsStats.setServers({}, PROTO_UDP));
268 EXPECT_TRUE(mDnsStats.setServers({}, PROTO_DOT));
269 EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), IsEmpty());
270 EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), IsEmpty());
271 EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), IsEmpty());
272 verifyDumpOutput({}, {}, {});
273 }
274
TEST_F(DnsStatsTest,StatsRemainsInExistentServer)275 TEST_F(DnsStatsTest, StatsRemainsInExistentServer) {
276 std::vector<IPSockAddr> servers = {
277 IPSockAddr::toIPSockAddr("127.0.0.1", 53),
278 IPSockAddr::toIPSockAddr("127.0.0.2", 53),
279 };
280 const DnsQueryEvent recordNoError = makeDnsQueryEvent(PROTO_UDP, NS_R_NO_ERROR, 10ms);
281 const DnsQueryEvent recordTimeout = makeDnsQueryEvent(PROTO_UDP, NS_R_TIMEOUT, 250ms);
282
283 EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_UDP));
284
285 // Add a record to 127.0.0.1.
286 EXPECT_TRUE(mDnsStats.addStats(servers[0], recordNoError));
287
288 // Add four records to 127.0.0.2.
289 EXPECT_TRUE(mDnsStats.addStats(servers[1], recordNoError));
290 EXPECT_TRUE(mDnsStats.addStats(servers[1], recordNoError));
291 EXPECT_TRUE(mDnsStats.addStats(servers[1], recordTimeout));
292 EXPECT_TRUE(mDnsStats.addStats(servers[1], recordTimeout));
293
294 std::vector<StatsData> expectedStats = {
295 makeStatsData(servers[0], 1, 10ms, {{NS_R_NO_ERROR, 1}}),
296 makeStatsData(servers[1], 4, 520ms, {{NS_R_NO_ERROR, 2}, {NS_R_TIMEOUT, 2}}),
297 };
298 EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats));
299 verifyDumpOutput({}, expectedStats, {});
300
301 // Update the server list, the stats of 127.0.0.2 will remain.
302 servers = {
303 IPSockAddr::toIPSockAddr("127.0.0.2", 53),
304 IPSockAddr::toIPSockAddr("127.0.0.3", 53),
305 IPSockAddr::toIPSockAddr("127.0.0.4", 53),
306 };
307 EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_UDP));
308 expectedStats = {
309 makeStatsData(servers[0], 4, 520ms, {{NS_R_NO_ERROR, 2}, {NS_R_TIMEOUT, 2}}),
310 makeStatsData(servers[1], 0, 0ms, {}),
311 makeStatsData(servers[2], 0, 0ms, {}),
312 };
313 EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats));
314 verifyDumpOutput({}, expectedStats, {});
315
316 // Let's add a record to 127.0.0.2 again.
317 EXPECT_TRUE(mDnsStats.addStats(servers[0], recordNoError));
318 expectedStats = {
319 makeStatsData(servers[0], 5, 530ms, {{NS_R_NO_ERROR, 3}, {NS_R_TIMEOUT, 2}}),
320 makeStatsData(servers[1], 0, 0ms, {}),
321 makeStatsData(servers[2], 0, 0ms, {}),
322 };
323 EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats));
324 verifyDumpOutput({}, expectedStats, {});
325 }
326
TEST_F(DnsStatsTest,AddStatsRecords_100000)327 TEST_F(DnsStatsTest, AddStatsRecords_100000) {
328 constexpr size_t operations = 100000;
329 constexpr size_t logSize = DnsStats::kLogSize;
330 constexpr size_t rcodeNum = 4; // A value by which kLogSize is divisible.
331 ASSERT_EQ(logSize % rcodeNum, 0U);
332
333 const std::vector<IPSockAddr> servers = {
334 IPSockAddr::toIPSockAddr("127.0.0.1", 53),
335 IPSockAddr::toIPSockAddr("127.0.0.2", 53),
336 IPSockAddr::toIPSockAddr("127.0.0.3", 53),
337 IPSockAddr::toIPSockAddr("127.0.0.4", 53),
338 };
339
340 // To test unknown rcode in rcodeToName(), store the elements as type int.
341 const std::array<int, rcodeNum> rcodes = {
342 NS_R_NO_ERROR, // NOERROR
343 NS_R_NXDOMAIN, // NXDOMAIN
344 99, // UNKNOWN(99)
345 NS_R_INTERNAL_ERROR, // INTERNAL_ERROR
346 };
347
348 EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_TCP));
349 EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_UDP));
350 EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_DOT));
351
352 for (size_t i = 0; i < operations; i++) {
353 const NsRcode rcode = static_cast<NsRcode>(rcodes[i % rcodeNum]);
354 const auto eventTcp = makeDnsQueryEvent(PROTO_TCP, rcode, 10ms);
355 const auto eventUdp = makeDnsQueryEvent(PROTO_UDP, rcode, 10ms);
356 const auto eventDot = makeDnsQueryEvent(PROTO_DOT, rcode, 10ms);
357 for (const auto& server : servers) {
358 SCOPED_TRACE(server.toString() + "-" + std::to_string(i));
359 ASSERT_TRUE(mDnsStats.addStats(server, eventTcp));
360 ASSERT_TRUE(mDnsStats.addStats(server, eventUdp));
361 ASSERT_TRUE(mDnsStats.addStats(server, eventDot));
362 }
363 }
364
365 std::map<int, int> expectedRcodeCounts;
366 for (const auto& rcode : rcodes) {
367 expectedRcodeCounts.try_emplace(rcode, 32);
368 }
369 const std::vector<StatsData> expectedStats = {
370 makeStatsData(servers[0], logSize, logSize * 10ms, expectedRcodeCounts),
371 makeStatsData(servers[1], logSize, logSize * 10ms, expectedRcodeCounts),
372 makeStatsData(servers[2], logSize, logSize * 10ms, expectedRcodeCounts),
373 makeStatsData(servers[3], logSize, logSize * 10ms, expectedRcodeCounts),
374 };
375
376 EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), UnorderedElementsAreArray(expectedStats));
377 EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats));
378 EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), UnorderedElementsAreArray(expectedStats));
379 verifyDumpOutput(expectedStats, expectedStats, expectedStats);
380 }
381
382 } // namespace android::net
383