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 <regex> 18 #include <thread> 19 20 #include <android-base/strings.h> 21 #include <android-base/test_utils.h> 22 #include <gtest/gtest.h> 23 24 #include "DnsQueryLog.h" 25 26 using namespace std::chrono_literals; 27 28 namespace android::net { 29 30 namespace { 31 32 // Dump the log to STDOUT and capture it. 33 std::string captureDumpOutput(const DnsQueryLog& queryLog) { 34 netdutils::DumpWriter dw(STDOUT_FILENO); 35 CapturedStdout captured; 36 queryLog.dump(dw); 37 return captured.str(); 38 } 39 40 // A simple check for the dump result by checking the netIds one by one. 41 void verifyDumpOutput(const std::string& dumpLog, const std::vector<int>& expectedNetIds) { 42 // Capture three matches: netId, hostname, and answer (empty allowed). 43 static const std::regex pattern( 44 R"(netId=(\d+).* hostname=([\w\*]+) answer=\[([\w:,\.\*\s]*)\])"); 45 46 std::string str(dumpLog); 47 std::smatch sm; 48 for (const auto& netId : expectedNetIds) { 49 SCOPED_TRACE(netId); 50 EXPECT_TRUE(std::regex_search(str, sm, pattern)); 51 EXPECT_EQ(sm[1], std::to_string(netId)); 52 str = sm.suffix(); 53 } 54 55 // Ensure the dumpLog is exactly as expected. 56 EXPECT_FALSE(std::regex_search(str, sm, pattern)); 57 } 58 59 } // namespace 60 61 class DnsQueryLogTest : public ::testing::Test { 62 protected: 63 const std::vector<std::string> serversV4 = {"127.0.0.1", "1.2.3.4"}; 64 const std::vector<std::string> serversV4V6 = {"127.0.0.1", "1.2.3.4", "2001:db8::1", 65 "fe80:1::2%testnet"}; 66 }; 67 68 TEST_F(DnsQueryLogTest, Push) { 69 std::vector<DnsQueryLog::Record> records = { 70 DnsQueryLog::Record(30, 1000, 1000, "example.com", serversV4, 10), 71 DnsQueryLog::Record(31, 1000, 1000, "", serversV4, 10), // Empty hostname. 72 DnsQueryLog::Record(32, 1000, 1000, "example.com", {}, 10), // No answer. 73 DnsQueryLog::Record(33, 1000, 1000, "example.com", serversV4V6, 10), 74 }; 75 DnsQueryLog queryLog; 76 for (auto& r : records) { 77 queryLog.push(std::move(r)); 78 } 79 80 std::string output = captureDumpOutput(queryLog); 81 verifyDumpOutput(output, {30, 31, 32, 33}); 82 } 83 84 TEST_F(DnsQueryLogTest, PushStressTest) { 85 const int threadNum = 100; 86 const int pushNum = 1000; 87 const size_t size = 500; 88 DnsQueryLog queryLog(size); 89 std::vector<std::thread> threads(threadNum); 90 91 // Launch 'threadNum' threads to push the same queryLog 'pushNum' times. 92 for (auto& thread : threads) { 93 thread = std::thread([&]() { 94 for (int i = 0; i < pushNum; i++) { 95 DnsQueryLog::Record record(30, 1000, 1000, "www.example.com", serversV4, 10); 96 queryLog.push(std::move(record)); 97 } 98 }); 99 } 100 for (auto& thread : threads) { 101 thread.join(); 102 } 103 104 // Verify there are exact 'size' records in queryLog. 105 std::string output = captureDumpOutput(queryLog); 106 verifyDumpOutput(output, std::vector(size, 30)); 107 } 108 109 TEST_F(DnsQueryLogTest, ZeroSize) { 110 const size_t size = 0; 111 DnsQueryLog::Record r1(30, 1000, 1000, "www.example1.com", serversV4V6, 10); 112 DnsQueryLog::Record r2(31, 1000, 1000, "www.example2.com", serversV4V6, 10); 113 DnsQueryLog::Record r3(32, 1000, 1000, "www.example3.com", serversV4V6, 10); 114 115 DnsQueryLog queryLog(size); 116 queryLog.push(std::move(r1)); 117 queryLog.push(std::move(r2)); 118 queryLog.push(std::move(r3)); 119 120 std::string output = captureDumpOutput(queryLog); 121 verifyDumpOutput(output, {}); 122 } 123 124 TEST_F(DnsQueryLogTest, CapacityFull) { 125 const size_t size = 3; 126 DnsQueryLog::Record r1(30, 1000, 1000, "www.example1.com", serversV4V6, 10); 127 DnsQueryLog::Record r2(31, 1000, 1000, "www.example2.com", serversV4V6, 10); 128 DnsQueryLog::Record r3(32, 1000, 1000, "www.example3.com", serversV4V6, 10); 129 DnsQueryLog::Record r4(33, 1000, 1000, "www.example4.com", serversV4V6, 10); 130 const std::vector<int> expectedNetIds = {31, 32, 33}; 131 132 DnsQueryLog queryLog(size); 133 queryLog.push(std::move(r1)); 134 queryLog.push(std::move(r2)); 135 queryLog.push(std::move(r3)); 136 queryLog.push(std::move(r4)); 137 138 std::string output = captureDumpOutput(queryLog); 139 verifyDumpOutput(output, expectedNetIds); 140 } 141 142 TEST_F(DnsQueryLogTest, ValidityTime) { 143 DnsQueryLog::Record r1(30, 1000, 1000, "www.example.com", serversV4, 10); 144 DnsQueryLog queryLog(3, 100ms); 145 queryLog.push(std::move(r1)); 146 147 // Dump the output and verify the correctness by checking netId. 148 std::string output = captureDumpOutput(queryLog); 149 verifyDumpOutput(output, {30}); 150 151 std::this_thread::sleep_for(150ms); 152 153 // The record is expired thus not shown in the output. 154 output = captureDumpOutput(queryLog); 155 verifyDumpOutput(output, {}); 156 157 // Push another record to ensure it still works. 158 DnsQueryLog::Record r2(31, 1000, 1000, "example.com", serversV4V6, 10); 159 queryLog.push(std::move(r2)); 160 output = captureDumpOutput(queryLog); 161 verifyDumpOutput(output, {31}); 162 } 163 164 } // namespace android::net 165