Loading Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ cc_library { "DnsProxyListener.cpp", "DnsResolver.cpp", "DnsResolverService.cpp", "DnsStats.cpp", "DnsTlsDispatcher.cpp", "DnsTlsQueryMap.cpp", "DnsTlsTransport.cpp", Loading Loading @@ -202,6 +203,7 @@ cc_test { "resolv_cache_unit_test.cpp", "resolv_tls_unit_test.cpp", "resolv_unit_test.cpp", "DnsStatsTest.cpp", ], shared_libs: [ "libbase", Loading DnsStats.cpp 0 → 100644 +199 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #define LOG_TAG "resolv" #include "DnsStats.h" #include <android-base/logging.h> #include <android-base/stringprintf.h> namespace android::net { using base::StringPrintf; using netdutils::DumpWriter; using netdutils::IPAddress; using netdutils::IPSockAddr; using netdutils::ScopedIndent; using std::chrono::duration_cast; using std::chrono::microseconds; using std::chrono::milliseconds; using std::chrono::seconds; namespace { static constexpr IPAddress INVALID_IPADDRESS = IPAddress(); std::string rcodeToName(int rcode) { // clang-format off switch (rcode) { case NS_R_NO_ERROR: return "NOERROR"; case NS_R_FORMERR: return "FORMERR"; case NS_R_SERVFAIL: return "SERVFAIL"; case NS_R_NXDOMAIN: return "NXDOMAIN"; case NS_R_NOTIMPL: return "NOTIMP"; case NS_R_REFUSED: return "REFUSED"; case NS_R_YXDOMAIN: return "YXDOMAIN"; case NS_R_YXRRSET: return "YXRRSET"; case NS_R_NXRRSET: return "NXRRSET"; case NS_R_NOTAUTH: return "NOTAUTH"; case NS_R_NOTZONE: return "NOTZONE"; case NS_R_INTERNAL_ERROR: return "INTERNAL_ERROR"; case NS_R_TIMEOUT: return "TIMEOUT"; default: return StringPrintf("UNKNOWN(%d)", rcode); } // clang-format on } bool ensureNoInvalidIp(const std::vector<IPSockAddr>& servers) { for (const auto& server : servers) { if (server.ip() == INVALID_IPADDRESS || server.port() == 0) { LOG(WARNING) << "Invalid server: " << server; return false; } } return true; } } // namespace // The comparison ignores the last update time. bool StatsData::operator==(const StatsData& o) const { return std::tie(serverSockAddr, total, rcodeCounts, latencyUs) == std::tie(o.serverSockAddr, o.total, o.rcodeCounts, o.latencyUs); } std::string StatsData::toString() const { if (total == 0) return StringPrintf("%s <no data>", serverSockAddr.ip().toString().c_str()); const auto now = std::chrono::steady_clock::now(); const int meanLatencyMs = duration_cast<milliseconds>(latencyUs).count() / total; const int lastUpdateSec = duration_cast<seconds>(now - lastUpdate).count(); std::string buf; for (const auto& [rcode, counts] : rcodeCounts) { if (counts != 0) { buf += StringPrintf("%s:%d ", rcodeToName(rcode).c_str(), counts); } } return StringPrintf("%s (%d, %dms, [%s], %ds)", serverSockAddr.ip().toString().c_str(), total, meanLatencyMs, buf.c_str(), lastUpdateSec); } StatsRecords::StatsRecords(const IPSockAddr& ipSockAddr, size_t size) : mCapacity(size), mStatsData(ipSockAddr) {} void StatsRecords::push(const Record& record) { updateStatsData(record, true); mRecords.push_back(record); if (mRecords.size() > mCapacity) { updateStatsData(mRecords.front(), false); mRecords.pop_front(); } } void StatsRecords::updateStatsData(const Record& record, const bool add) { const int rcode = record.rcode; if (add) { mStatsData.total += 1; mStatsData.rcodeCounts[rcode] += 1; mStatsData.latencyUs += record.latencyUs; } else { mStatsData.total -= 1; mStatsData.rcodeCounts[rcode] -= 1; mStatsData.latencyUs -= record.latencyUs; } mStatsData.lastUpdate = std::chrono::steady_clock::now(); } bool DnsStats::setServers(const std::vector<netdutils::IPSockAddr>& servers, Protocol protocol) { if (!ensureNoInvalidIp(servers)) return false; ServerStatsMap& statsMap = mStats[protocol]; for (const auto& server : servers) { statsMap.try_emplace(server, StatsRecords(server, kLogSize)); } // Clean up the map to eliminate the nodes not belonging to the given list of servers. const auto cleanup = [&](ServerStatsMap* statsMap) { ServerStatsMap tmp; for (const auto& server : servers) { if (statsMap->find(server) != statsMap->end()) { tmp.insert(statsMap->extract(server)); } } statsMap->swap(tmp); }; cleanup(&statsMap); return true; } bool DnsStats::addStats(const IPSockAddr& ipSockAddr, const DnsQueryEvent& record) { if (ipSockAddr.ip() == INVALID_IPADDRESS) return false; for (auto& [serverSockAddr, statsRecords] : mStats[record.protocol()]) { if (serverSockAddr == ipSockAddr) { const StatsRecords::Record rec = { .rcode = record.rcode(), .latencyUs = microseconds(record.latency_micros()), }; statsRecords.push(rec); return true; } } return false; } std::vector<StatsData> DnsStats::getStats(Protocol protocol) const { std::vector<StatsData> ret; if (mStats.find(protocol) != mStats.end()) { for (const auto& [_, statsRecords] : mStats.at(protocol)) { ret.push_back(statsRecords.getStatsData()); } } return ret; } void DnsStats::dump(DumpWriter& dw) { const auto dumpStatsMap = [&](ServerStatsMap& statsMap) { ScopedIndent indentLog(dw); if (statsMap.size() == 0) { dw.println("<no server>"); return; } for (const auto& [_, statsRecords] : statsMap) { dw.println("%s", statsRecords.getStatsData().toString().c_str()); } }; dw.println("Server statistics: (total, RTT avg, {rcode:counts}, last update)"); ScopedIndent indentStats(dw); dw.println("over UDP"); dumpStatsMap(mStats[PROTO_UDP]); dw.println("over TLS"); dumpStatsMap(mStats[PROTO_DOT]); dw.println("over TCP"); dumpStatsMap(mStats[PROTO_TCP]); } } // namespace android::net DnsStats.h 0 → 100644 +115 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #pragma once #include <chrono> #include <deque> #include <map> #include <vector> #include <android-base/thread_annotations.h> #include <netdutils/DumpWriter.h> #include <netdutils/InternetAddresses.h> #include "ResolverStats.h" #include "stats.pb.h" namespace android::net { // The overall information of a StatsRecords. struct StatsData { StatsData(const netdutils::IPSockAddr& ipSockAddr) : serverSockAddr(ipSockAddr) { lastUpdate = std::chrono::steady_clock::now(); }; // Server socket address. netdutils::IPSockAddr serverSockAddr; // The most recent number of records being accumulated. int total = 0; // The map used to store the number of each rcode. std::map<int, int> rcodeCounts; // The aggregated RTT in microseconds. // For DNS-over-TCP, it includes TCP handshake. // For DNS-over-TLS, it might include TCP handshake plus SSL handshake. std::chrono::microseconds latencyUs = {}; // The last update timestamp. std::chrono::time_point<std::chrono::steady_clock> lastUpdate; std::string toString() const; // For testing. bool operator==(const StatsData& o) const; friend std::ostream& operator<<(std::ostream& os, const StatsData& data) { return os << data.toString(); } }; // A circular buffer based class used to store the statistics for a server with a protocol. class StatsRecords { public: struct Record { int rcode; std::chrono::microseconds latencyUs; }; StatsRecords(const netdutils::IPSockAddr& ipSockAddr, size_t size); void push(const Record& record); const StatsData& getStatsData() const { return mStatsData; } private: void updateStatsData(const Record& record, const bool add); std::deque<Record> mRecords; size_t mCapacity; StatsData mStatsData; }; // DnsStats class manages the statistics of DNS servers per netId. // The class itself is not thread-safe. class DnsStats { public: using ServerStatsMap = std::map<netdutils::IPSockAddr, StatsRecords>; // Add |servers| to the map, and remove no-longer-used servers. // Return true if they are successfully added; otherwise, return false. bool setServers(const std::vector<netdutils::IPSockAddr>& servers, Protocol protocol); // Return true if |record| is successfully added into |server|'s stats; otherwise, return false. bool addStats(const netdutils::IPSockAddr& server, const DnsQueryEvent& record); void dump(netdutils::DumpWriter& dw); // For testing. std::vector<StatsData> getStats(Protocol protocol) const; // TODO: Compatible support for getResolverInfo(). // TODO: Support getSortedServers(). private: std::map<Protocol, ServerStatsMap> mStats; static constexpr size_t kLogSize = 128; }; } // namespace android::net DnsStatsTest.cpp 0 → 100644 +292 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <gmock/gmock.h> #include <gtest/gtest.h> #include "DnsStats.h" namespace android::net { using namespace std::chrono_literals; using android::netdutils::IPSockAddr; using std::chrono::milliseconds; using ::testing::IsEmpty; using ::testing::UnorderedElementsAreArray; namespace { DnsQueryEvent makeDnsQueryEvent(const Protocol protocol, const NsRcode rcode, const milliseconds& latency) { DnsQueryEvent event; event.set_protocol(protocol); event.set_rcode(rcode); event.set_latency_micros(latency.count() * 1000); return event; } StatsData makeStatsData(const IPSockAddr& server, const int total, const milliseconds& latencyMs, const std::map<int, int>& rcodeCounts) { StatsData ret(server); ret.total = total; ret.latencyUs = latencyMs; ret.rcodeCounts = rcodeCounts; return ret; } } // namespace class StatsRecordsTest : public ::testing::Test {}; TEST_F(StatsRecordsTest, PushRecord) { const IPSockAddr server = IPSockAddr::toIPSockAddr("127.0.0.2", 53); constexpr size_t size = 3; const StatsRecords::Record recordNoError = {NS_R_NO_ERROR, 10ms}; const StatsRecords::Record recordTimeout = {NS_R_TIMEOUT, 250ms}; StatsRecords sr(server, size); EXPECT_EQ(sr.getStatsData(), makeStatsData(server, 0, 0ms, {})); sr.push(recordNoError); EXPECT_EQ(sr.getStatsData(), makeStatsData(server, 1, 10ms, {{NS_R_NO_ERROR, 1}})); sr.push(recordNoError); EXPECT_EQ(sr.getStatsData(), makeStatsData(server, 2, 20ms, {{NS_R_NO_ERROR, 2}})); sr.push(recordTimeout); EXPECT_EQ(sr.getStatsData(), makeStatsData(server, 3, 270ms, {{NS_R_NO_ERROR, 2}, {NS_R_TIMEOUT, 1}})); sr.push(recordTimeout); EXPECT_EQ(sr.getStatsData(), makeStatsData(server, 3, 510ms, {{NS_R_NO_ERROR, 1}, {NS_R_TIMEOUT, 2}})); sr.push(recordTimeout); EXPECT_EQ(sr.getStatsData(), makeStatsData(server, 3, 750ms, {{NS_R_NO_ERROR, 0}, {NS_R_TIMEOUT, 3}})); } class DnsStatsTest : public ::testing::Test { protected: DnsStats mDnsStats; }; TEST_F(DnsStatsTest, SetServers) { static const struct { std::vector<std::string> servers; std::vector<std::string> expectation; bool isSuccess; } tests[] = { // Normal case. { {"127.0.0.1", "127.0.0.2", "fe80::1%22", "2001:db8::2", "::1"}, {"127.0.0.1", "127.0.0.2", "fe80::1%22", "2001:db8::2", "::1"}, true, }, // Duplicate servers. { {"127.0.0.1", "2001:db8::2", "127.0.0.1", "2001:db8::2"}, {"127.0.0.1", "2001:db8::2"}, true, }, // Invalid server addresses. The state remains in previous state. { {"not_an_ip", "127.0.0.3", "127.a.b.2"}, {"127.0.0.1", "2001:db8::2"}, false, }, // Clean up the old servers 127.0.0.1 and 127.0.0.2. { {"127.0.0.4", "2001:db8::5"}, {"127.0.0.4", "2001:db8::5"}, true, }, // Empty list. {{}, {}, true}, }; for (const auto& [servers, expectation, isSuccess] : tests) { std::vector<IPSockAddr> ipSockAddrs; ipSockAddrs.reserve(servers.size()); for (const auto& server : servers) { ipSockAddrs.push_back(IPSockAddr::toIPSockAddr(server, 53)); } EXPECT_TRUE(mDnsStats.setServers(ipSockAddrs, PROTO_TCP) == isSuccess); EXPECT_TRUE(mDnsStats.setServers(ipSockAddrs, PROTO_UDP) == isSuccess); EXPECT_TRUE(mDnsStats.setServers(ipSockAddrs, PROTO_DOT) == isSuccess); std::vector<StatsData> expectedStats; expectedStats.reserve(expectation.size()); for (const auto& exp : expectation) { expectedStats.push_back(makeStatsData(IPSockAddr::toIPSockAddr(exp, 53), 0, 0ms, {})); } EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), UnorderedElementsAreArray(expectedStats)); EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats)); EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), UnorderedElementsAreArray(expectedStats)); } } TEST_F(DnsStatsTest, SetServersDifferentPorts) { const std::vector<IPSockAddr> servers = { IPSockAddr::toIPSockAddr("127.0.0.1", 0), IPSockAddr::toIPSockAddr("fe80::1", 0), IPSockAddr::toIPSockAddr("127.0.0.1", 53), IPSockAddr::toIPSockAddr("127.0.0.1", 5353), IPSockAddr::toIPSockAddr("127.0.0.1", 853), IPSockAddr::toIPSockAddr("fe80::1", 53), IPSockAddr::toIPSockAddr("fe80::1", 5353), IPSockAddr::toIPSockAddr("fe80::1", 853), }; // Servers setup fails due to port unset. EXPECT_FALSE(mDnsStats.setServers(servers, PROTO_TCP)); EXPECT_FALSE(mDnsStats.setServers(servers, PROTO_UDP)); EXPECT_FALSE(mDnsStats.setServers(servers, PROTO_DOT)); EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), IsEmpty()); EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), IsEmpty()); EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), IsEmpty()); EXPECT_TRUE(mDnsStats.setServers(std::vector(servers.begin() + 2, servers.end()), PROTO_TCP)); EXPECT_TRUE(mDnsStats.setServers(std::vector(servers.begin() + 2, servers.end()), PROTO_UDP)); EXPECT_TRUE(mDnsStats.setServers(std::vector(servers.begin() + 2, servers.end()), PROTO_DOT)); const std::vector<StatsData> expectedStats = { makeStatsData(servers[2], 0, 0ms, {}), makeStatsData(servers[3], 0, 0ms, {}), makeStatsData(servers[4], 0, 0ms, {}), makeStatsData(servers[5], 0, 0ms, {}), makeStatsData(servers[6], 0, 0ms, {}), makeStatsData(servers[7], 0, 0ms, {}), }; EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), UnorderedElementsAreArray(expectedStats)); EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats)); EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), UnorderedElementsAreArray(expectedStats)); } TEST_F(DnsStatsTest, AddStatsAndClear) { const std::vector<IPSockAddr> servers = { IPSockAddr::toIPSockAddr("127.0.0.1", 53), IPSockAddr::toIPSockAddr("127.0.0.2", 53), }; const DnsQueryEvent record = makeDnsQueryEvent(PROTO_UDP, NS_R_NO_ERROR, 10ms); EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_TCP)); EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_UDP)); // Fail to add stats because of incorrect arguments. EXPECT_FALSE(mDnsStats.addStats(IPSockAddr::toIPSockAddr("127.0.0.4", 53), record)); EXPECT_FALSE(mDnsStats.addStats(IPSockAddr::toIPSockAddr("127.a.b.4", 53), record)); EXPECT_TRUE(mDnsStats.addStats(servers[0], record)); EXPECT_TRUE(mDnsStats.addStats(servers[0], record)); EXPECT_TRUE(mDnsStats.addStats(servers[1], record)); const std::vector<StatsData> expectedStatsForTcp = { makeStatsData(servers[0], 0, 0ms, {}), makeStatsData(servers[1], 0, 0ms, {}), }; const std::vector<StatsData> expectedStatsForUdp = { makeStatsData(servers[0], 2, 20ms, {{NS_R_NO_ERROR, 2}}), makeStatsData(servers[1], 1, 10ms, {{NS_R_NO_ERROR, 1}}), }; EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), UnorderedElementsAreArray(expectedStatsForTcp)); EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStatsForUdp)); EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), IsEmpty()); // Clear stats. EXPECT_TRUE(mDnsStats.setServers({}, PROTO_TCP)); EXPECT_TRUE(mDnsStats.setServers({}, PROTO_UDP)); EXPECT_TRUE(mDnsStats.setServers({}, PROTO_DOT)); EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), IsEmpty()); EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), IsEmpty()); EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), IsEmpty()); } TEST_F(DnsStatsTest, StatsRemainsInExistentServer) { std::vector<IPSockAddr> servers = { IPSockAddr::toIPSockAddr("127.0.0.1", 53), IPSockAddr::toIPSockAddr("127.0.0.2", 53), }; const DnsQueryEvent recordNoError = makeDnsQueryEvent(PROTO_UDP, NS_R_NO_ERROR, 10ms); const DnsQueryEvent recordTimeout = makeDnsQueryEvent(PROTO_UDP, NS_R_TIMEOUT, 250ms); EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_UDP)); // Add a record to 127.0.0.1. EXPECT_TRUE(mDnsStats.addStats(servers[0], recordNoError)); // Add four records to 127.0.0.2. EXPECT_TRUE(mDnsStats.addStats(servers[1], recordNoError)); EXPECT_TRUE(mDnsStats.addStats(servers[1], recordNoError)); EXPECT_TRUE(mDnsStats.addStats(servers[1], recordTimeout)); EXPECT_TRUE(mDnsStats.addStats(servers[1], recordTimeout)); std::vector<StatsData> expectedStats = { makeStatsData(servers[0], 1, 10ms, {{NS_R_NO_ERROR, 1}}), makeStatsData(servers[1], 4, 520ms, {{NS_R_NO_ERROR, 2}, {NS_R_TIMEOUT, 2}}), }; EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats)); // Update the server list, the stats of 127.0.0.2 will remain. servers = { IPSockAddr::toIPSockAddr("127.0.0.2", 53), IPSockAddr::toIPSockAddr("127.0.0.3", 53), IPSockAddr::toIPSockAddr("127.0.0.4", 53), }; EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_UDP)); expectedStats = { makeStatsData(servers[0], 4, 520ms, {{NS_R_NO_ERROR, 2}, {NS_R_TIMEOUT, 2}}), makeStatsData(servers[1], 0, 0ms, {}), makeStatsData(servers[2], 0, 0ms, {}), }; EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats)); // Let's add a record to 127.0.0.2 again. EXPECT_TRUE(mDnsStats.addStats(servers[0], recordNoError)); expectedStats = { makeStatsData(servers[0], 5, 530ms, {{NS_R_NO_ERROR, 3}, {NS_R_TIMEOUT, 2}}), makeStatsData(servers[1], 0, 0ms, {}), makeStatsData(servers[2], 0, 0ms, {}), }; EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats)); } TEST_F(DnsStatsTest, AddStatsRecords_100000) { constexpr int num = 100000; const std::vector<IPSockAddr> servers = { IPSockAddr::toIPSockAddr("127.0.0.1", 53), IPSockAddr::toIPSockAddr("127.0.0.2", 53), IPSockAddr::toIPSockAddr("127.0.0.3", 53), IPSockAddr::toIPSockAddr("127.0.0.4", 53), }; EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_TCP)); EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_UDP)); EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_DOT)); for (int i = 0; i < num; i++) { const auto eventTcp = makeDnsQueryEvent(PROTO_TCP, static_cast<NsRcode>(i % 10), 10ms); const auto eventUdp = makeDnsQueryEvent(PROTO_UDP, static_cast<NsRcode>(i % 10), 10ms); const auto eventDot = makeDnsQueryEvent(PROTO_DOT, static_cast<NsRcode>(i % 10), 10ms); for (const auto& server : servers) { const std::string trace = server.toString() + "-" + std::to_string(i); SCOPED_TRACE(trace); EXPECT_TRUE(mDnsStats.addStats(server, eventTcp)); EXPECT_TRUE(mDnsStats.addStats(server, eventUdp)); EXPECT_TRUE(mDnsStats.addStats(server, eventDot)); } } } } // namespace android::net DnsTlsDispatcher.cpp +10 −2 Original line number Diff line number Diff line Loading @@ -17,8 +17,11 @@ #define LOG_TAG "resolv" #include "DnsTlsDispatcher.h" #include <netdutils/Stopwatch.h> #include "DnsTlsSocketFactory.h" #include "resolv_cache.h" #include "resolv_private.h" #include "stats.pb.h" Loading @@ -27,6 +30,7 @@ namespace android { namespace net { using android::netdutils::IPSockAddr; using android::netdutils::Stopwatch; using netdutils::Slice; Loading Loading @@ -112,19 +116,23 @@ DnsTlsTransport::Response DnsTlsDispatcher::query(const std::list<DnsTlsServer>& case DnsTlsTransport::Response::success: dnsQueryEvent->set_rcode( static_cast<NsRcode>(reinterpret_cast<HEADER*>(ans.base())->rcode)); resolv_stats_add(statp->netid, IPSockAddr::toIPSockAddr(server.ss), dnsQueryEvent); return code; case DnsTlsTransport::Response::limit_error: dnsQueryEvent->set_rcode(NS_R_INTERNAL_ERROR); resolv_stats_add(statp->netid, IPSockAddr::toIPSockAddr(server.ss), dnsQueryEvent); return code; // These response codes might differ when trying other servers, so // keep iterating to see if we can get a different (better) result. case DnsTlsTransport::Response::network_error: // Sync from res_tls_send in res_send.cpp dnsQueryEvent->set_rcode(NS_R_TIMEOUT); continue; resolv_stats_add(statp->netid, IPSockAddr::toIPSockAddr(server.ss), dnsQueryEvent); break; case DnsTlsTransport::Response::internal_error: dnsQueryEvent->set_rcode(NS_R_INTERNAL_ERROR); continue; resolv_stats_add(statp->netid, IPSockAddr::toIPSockAddr(server.ss), dnsQueryEvent); break; // No "default" statement. } } Loading Loading
Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ cc_library { "DnsProxyListener.cpp", "DnsResolver.cpp", "DnsResolverService.cpp", "DnsStats.cpp", "DnsTlsDispatcher.cpp", "DnsTlsQueryMap.cpp", "DnsTlsTransport.cpp", Loading Loading @@ -202,6 +203,7 @@ cc_test { "resolv_cache_unit_test.cpp", "resolv_tls_unit_test.cpp", "resolv_unit_test.cpp", "DnsStatsTest.cpp", ], shared_libs: [ "libbase", Loading
DnsStats.cpp 0 → 100644 +199 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #define LOG_TAG "resolv" #include "DnsStats.h" #include <android-base/logging.h> #include <android-base/stringprintf.h> namespace android::net { using base::StringPrintf; using netdutils::DumpWriter; using netdutils::IPAddress; using netdutils::IPSockAddr; using netdutils::ScopedIndent; using std::chrono::duration_cast; using std::chrono::microseconds; using std::chrono::milliseconds; using std::chrono::seconds; namespace { static constexpr IPAddress INVALID_IPADDRESS = IPAddress(); std::string rcodeToName(int rcode) { // clang-format off switch (rcode) { case NS_R_NO_ERROR: return "NOERROR"; case NS_R_FORMERR: return "FORMERR"; case NS_R_SERVFAIL: return "SERVFAIL"; case NS_R_NXDOMAIN: return "NXDOMAIN"; case NS_R_NOTIMPL: return "NOTIMP"; case NS_R_REFUSED: return "REFUSED"; case NS_R_YXDOMAIN: return "YXDOMAIN"; case NS_R_YXRRSET: return "YXRRSET"; case NS_R_NXRRSET: return "NXRRSET"; case NS_R_NOTAUTH: return "NOTAUTH"; case NS_R_NOTZONE: return "NOTZONE"; case NS_R_INTERNAL_ERROR: return "INTERNAL_ERROR"; case NS_R_TIMEOUT: return "TIMEOUT"; default: return StringPrintf("UNKNOWN(%d)", rcode); } // clang-format on } bool ensureNoInvalidIp(const std::vector<IPSockAddr>& servers) { for (const auto& server : servers) { if (server.ip() == INVALID_IPADDRESS || server.port() == 0) { LOG(WARNING) << "Invalid server: " << server; return false; } } return true; } } // namespace // The comparison ignores the last update time. bool StatsData::operator==(const StatsData& o) const { return std::tie(serverSockAddr, total, rcodeCounts, latencyUs) == std::tie(o.serverSockAddr, o.total, o.rcodeCounts, o.latencyUs); } std::string StatsData::toString() const { if (total == 0) return StringPrintf("%s <no data>", serverSockAddr.ip().toString().c_str()); const auto now = std::chrono::steady_clock::now(); const int meanLatencyMs = duration_cast<milliseconds>(latencyUs).count() / total; const int lastUpdateSec = duration_cast<seconds>(now - lastUpdate).count(); std::string buf; for (const auto& [rcode, counts] : rcodeCounts) { if (counts != 0) { buf += StringPrintf("%s:%d ", rcodeToName(rcode).c_str(), counts); } } return StringPrintf("%s (%d, %dms, [%s], %ds)", serverSockAddr.ip().toString().c_str(), total, meanLatencyMs, buf.c_str(), lastUpdateSec); } StatsRecords::StatsRecords(const IPSockAddr& ipSockAddr, size_t size) : mCapacity(size), mStatsData(ipSockAddr) {} void StatsRecords::push(const Record& record) { updateStatsData(record, true); mRecords.push_back(record); if (mRecords.size() > mCapacity) { updateStatsData(mRecords.front(), false); mRecords.pop_front(); } } void StatsRecords::updateStatsData(const Record& record, const bool add) { const int rcode = record.rcode; if (add) { mStatsData.total += 1; mStatsData.rcodeCounts[rcode] += 1; mStatsData.latencyUs += record.latencyUs; } else { mStatsData.total -= 1; mStatsData.rcodeCounts[rcode] -= 1; mStatsData.latencyUs -= record.latencyUs; } mStatsData.lastUpdate = std::chrono::steady_clock::now(); } bool DnsStats::setServers(const std::vector<netdutils::IPSockAddr>& servers, Protocol protocol) { if (!ensureNoInvalidIp(servers)) return false; ServerStatsMap& statsMap = mStats[protocol]; for (const auto& server : servers) { statsMap.try_emplace(server, StatsRecords(server, kLogSize)); } // Clean up the map to eliminate the nodes not belonging to the given list of servers. const auto cleanup = [&](ServerStatsMap* statsMap) { ServerStatsMap tmp; for (const auto& server : servers) { if (statsMap->find(server) != statsMap->end()) { tmp.insert(statsMap->extract(server)); } } statsMap->swap(tmp); }; cleanup(&statsMap); return true; } bool DnsStats::addStats(const IPSockAddr& ipSockAddr, const DnsQueryEvent& record) { if (ipSockAddr.ip() == INVALID_IPADDRESS) return false; for (auto& [serverSockAddr, statsRecords] : mStats[record.protocol()]) { if (serverSockAddr == ipSockAddr) { const StatsRecords::Record rec = { .rcode = record.rcode(), .latencyUs = microseconds(record.latency_micros()), }; statsRecords.push(rec); return true; } } return false; } std::vector<StatsData> DnsStats::getStats(Protocol protocol) const { std::vector<StatsData> ret; if (mStats.find(protocol) != mStats.end()) { for (const auto& [_, statsRecords] : mStats.at(protocol)) { ret.push_back(statsRecords.getStatsData()); } } return ret; } void DnsStats::dump(DumpWriter& dw) { const auto dumpStatsMap = [&](ServerStatsMap& statsMap) { ScopedIndent indentLog(dw); if (statsMap.size() == 0) { dw.println("<no server>"); return; } for (const auto& [_, statsRecords] : statsMap) { dw.println("%s", statsRecords.getStatsData().toString().c_str()); } }; dw.println("Server statistics: (total, RTT avg, {rcode:counts}, last update)"); ScopedIndent indentStats(dw); dw.println("over UDP"); dumpStatsMap(mStats[PROTO_UDP]); dw.println("over TLS"); dumpStatsMap(mStats[PROTO_DOT]); dw.println("over TCP"); dumpStatsMap(mStats[PROTO_TCP]); } } // namespace android::net
DnsStats.h 0 → 100644 +115 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #pragma once #include <chrono> #include <deque> #include <map> #include <vector> #include <android-base/thread_annotations.h> #include <netdutils/DumpWriter.h> #include <netdutils/InternetAddresses.h> #include "ResolverStats.h" #include "stats.pb.h" namespace android::net { // The overall information of a StatsRecords. struct StatsData { StatsData(const netdutils::IPSockAddr& ipSockAddr) : serverSockAddr(ipSockAddr) { lastUpdate = std::chrono::steady_clock::now(); }; // Server socket address. netdutils::IPSockAddr serverSockAddr; // The most recent number of records being accumulated. int total = 0; // The map used to store the number of each rcode. std::map<int, int> rcodeCounts; // The aggregated RTT in microseconds. // For DNS-over-TCP, it includes TCP handshake. // For DNS-over-TLS, it might include TCP handshake plus SSL handshake. std::chrono::microseconds latencyUs = {}; // The last update timestamp. std::chrono::time_point<std::chrono::steady_clock> lastUpdate; std::string toString() const; // For testing. bool operator==(const StatsData& o) const; friend std::ostream& operator<<(std::ostream& os, const StatsData& data) { return os << data.toString(); } }; // A circular buffer based class used to store the statistics for a server with a protocol. class StatsRecords { public: struct Record { int rcode; std::chrono::microseconds latencyUs; }; StatsRecords(const netdutils::IPSockAddr& ipSockAddr, size_t size); void push(const Record& record); const StatsData& getStatsData() const { return mStatsData; } private: void updateStatsData(const Record& record, const bool add); std::deque<Record> mRecords; size_t mCapacity; StatsData mStatsData; }; // DnsStats class manages the statistics of DNS servers per netId. // The class itself is not thread-safe. class DnsStats { public: using ServerStatsMap = std::map<netdutils::IPSockAddr, StatsRecords>; // Add |servers| to the map, and remove no-longer-used servers. // Return true if they are successfully added; otherwise, return false. bool setServers(const std::vector<netdutils::IPSockAddr>& servers, Protocol protocol); // Return true if |record| is successfully added into |server|'s stats; otherwise, return false. bool addStats(const netdutils::IPSockAddr& server, const DnsQueryEvent& record); void dump(netdutils::DumpWriter& dw); // For testing. std::vector<StatsData> getStats(Protocol protocol) const; // TODO: Compatible support for getResolverInfo(). // TODO: Support getSortedServers(). private: std::map<Protocol, ServerStatsMap> mStats; static constexpr size_t kLogSize = 128; }; } // namespace android::net
DnsStatsTest.cpp 0 → 100644 +292 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <gmock/gmock.h> #include <gtest/gtest.h> #include "DnsStats.h" namespace android::net { using namespace std::chrono_literals; using android::netdutils::IPSockAddr; using std::chrono::milliseconds; using ::testing::IsEmpty; using ::testing::UnorderedElementsAreArray; namespace { DnsQueryEvent makeDnsQueryEvent(const Protocol protocol, const NsRcode rcode, const milliseconds& latency) { DnsQueryEvent event; event.set_protocol(protocol); event.set_rcode(rcode); event.set_latency_micros(latency.count() * 1000); return event; } StatsData makeStatsData(const IPSockAddr& server, const int total, const milliseconds& latencyMs, const std::map<int, int>& rcodeCounts) { StatsData ret(server); ret.total = total; ret.latencyUs = latencyMs; ret.rcodeCounts = rcodeCounts; return ret; } } // namespace class StatsRecordsTest : public ::testing::Test {}; TEST_F(StatsRecordsTest, PushRecord) { const IPSockAddr server = IPSockAddr::toIPSockAddr("127.0.0.2", 53); constexpr size_t size = 3; const StatsRecords::Record recordNoError = {NS_R_NO_ERROR, 10ms}; const StatsRecords::Record recordTimeout = {NS_R_TIMEOUT, 250ms}; StatsRecords sr(server, size); EXPECT_EQ(sr.getStatsData(), makeStatsData(server, 0, 0ms, {})); sr.push(recordNoError); EXPECT_EQ(sr.getStatsData(), makeStatsData(server, 1, 10ms, {{NS_R_NO_ERROR, 1}})); sr.push(recordNoError); EXPECT_EQ(sr.getStatsData(), makeStatsData(server, 2, 20ms, {{NS_R_NO_ERROR, 2}})); sr.push(recordTimeout); EXPECT_EQ(sr.getStatsData(), makeStatsData(server, 3, 270ms, {{NS_R_NO_ERROR, 2}, {NS_R_TIMEOUT, 1}})); sr.push(recordTimeout); EXPECT_EQ(sr.getStatsData(), makeStatsData(server, 3, 510ms, {{NS_R_NO_ERROR, 1}, {NS_R_TIMEOUT, 2}})); sr.push(recordTimeout); EXPECT_EQ(sr.getStatsData(), makeStatsData(server, 3, 750ms, {{NS_R_NO_ERROR, 0}, {NS_R_TIMEOUT, 3}})); } class DnsStatsTest : public ::testing::Test { protected: DnsStats mDnsStats; }; TEST_F(DnsStatsTest, SetServers) { static const struct { std::vector<std::string> servers; std::vector<std::string> expectation; bool isSuccess; } tests[] = { // Normal case. { {"127.0.0.1", "127.0.0.2", "fe80::1%22", "2001:db8::2", "::1"}, {"127.0.0.1", "127.0.0.2", "fe80::1%22", "2001:db8::2", "::1"}, true, }, // Duplicate servers. { {"127.0.0.1", "2001:db8::2", "127.0.0.1", "2001:db8::2"}, {"127.0.0.1", "2001:db8::2"}, true, }, // Invalid server addresses. The state remains in previous state. { {"not_an_ip", "127.0.0.3", "127.a.b.2"}, {"127.0.0.1", "2001:db8::2"}, false, }, // Clean up the old servers 127.0.0.1 and 127.0.0.2. { {"127.0.0.4", "2001:db8::5"}, {"127.0.0.4", "2001:db8::5"}, true, }, // Empty list. {{}, {}, true}, }; for (const auto& [servers, expectation, isSuccess] : tests) { std::vector<IPSockAddr> ipSockAddrs; ipSockAddrs.reserve(servers.size()); for (const auto& server : servers) { ipSockAddrs.push_back(IPSockAddr::toIPSockAddr(server, 53)); } EXPECT_TRUE(mDnsStats.setServers(ipSockAddrs, PROTO_TCP) == isSuccess); EXPECT_TRUE(mDnsStats.setServers(ipSockAddrs, PROTO_UDP) == isSuccess); EXPECT_TRUE(mDnsStats.setServers(ipSockAddrs, PROTO_DOT) == isSuccess); std::vector<StatsData> expectedStats; expectedStats.reserve(expectation.size()); for (const auto& exp : expectation) { expectedStats.push_back(makeStatsData(IPSockAddr::toIPSockAddr(exp, 53), 0, 0ms, {})); } EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), UnorderedElementsAreArray(expectedStats)); EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats)); EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), UnorderedElementsAreArray(expectedStats)); } } TEST_F(DnsStatsTest, SetServersDifferentPorts) { const std::vector<IPSockAddr> servers = { IPSockAddr::toIPSockAddr("127.0.0.1", 0), IPSockAddr::toIPSockAddr("fe80::1", 0), IPSockAddr::toIPSockAddr("127.0.0.1", 53), IPSockAddr::toIPSockAddr("127.0.0.1", 5353), IPSockAddr::toIPSockAddr("127.0.0.1", 853), IPSockAddr::toIPSockAddr("fe80::1", 53), IPSockAddr::toIPSockAddr("fe80::1", 5353), IPSockAddr::toIPSockAddr("fe80::1", 853), }; // Servers setup fails due to port unset. EXPECT_FALSE(mDnsStats.setServers(servers, PROTO_TCP)); EXPECT_FALSE(mDnsStats.setServers(servers, PROTO_UDP)); EXPECT_FALSE(mDnsStats.setServers(servers, PROTO_DOT)); EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), IsEmpty()); EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), IsEmpty()); EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), IsEmpty()); EXPECT_TRUE(mDnsStats.setServers(std::vector(servers.begin() + 2, servers.end()), PROTO_TCP)); EXPECT_TRUE(mDnsStats.setServers(std::vector(servers.begin() + 2, servers.end()), PROTO_UDP)); EXPECT_TRUE(mDnsStats.setServers(std::vector(servers.begin() + 2, servers.end()), PROTO_DOT)); const std::vector<StatsData> expectedStats = { makeStatsData(servers[2], 0, 0ms, {}), makeStatsData(servers[3], 0, 0ms, {}), makeStatsData(servers[4], 0, 0ms, {}), makeStatsData(servers[5], 0, 0ms, {}), makeStatsData(servers[6], 0, 0ms, {}), makeStatsData(servers[7], 0, 0ms, {}), }; EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), UnorderedElementsAreArray(expectedStats)); EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats)); EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), UnorderedElementsAreArray(expectedStats)); } TEST_F(DnsStatsTest, AddStatsAndClear) { const std::vector<IPSockAddr> servers = { IPSockAddr::toIPSockAddr("127.0.0.1", 53), IPSockAddr::toIPSockAddr("127.0.0.2", 53), }; const DnsQueryEvent record = makeDnsQueryEvent(PROTO_UDP, NS_R_NO_ERROR, 10ms); EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_TCP)); EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_UDP)); // Fail to add stats because of incorrect arguments. EXPECT_FALSE(mDnsStats.addStats(IPSockAddr::toIPSockAddr("127.0.0.4", 53), record)); EXPECT_FALSE(mDnsStats.addStats(IPSockAddr::toIPSockAddr("127.a.b.4", 53), record)); EXPECT_TRUE(mDnsStats.addStats(servers[0], record)); EXPECT_TRUE(mDnsStats.addStats(servers[0], record)); EXPECT_TRUE(mDnsStats.addStats(servers[1], record)); const std::vector<StatsData> expectedStatsForTcp = { makeStatsData(servers[0], 0, 0ms, {}), makeStatsData(servers[1], 0, 0ms, {}), }; const std::vector<StatsData> expectedStatsForUdp = { makeStatsData(servers[0], 2, 20ms, {{NS_R_NO_ERROR, 2}}), makeStatsData(servers[1], 1, 10ms, {{NS_R_NO_ERROR, 1}}), }; EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), UnorderedElementsAreArray(expectedStatsForTcp)); EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStatsForUdp)); EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), IsEmpty()); // Clear stats. EXPECT_TRUE(mDnsStats.setServers({}, PROTO_TCP)); EXPECT_TRUE(mDnsStats.setServers({}, PROTO_UDP)); EXPECT_TRUE(mDnsStats.setServers({}, PROTO_DOT)); EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), IsEmpty()); EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), IsEmpty()); EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), IsEmpty()); } TEST_F(DnsStatsTest, StatsRemainsInExistentServer) { std::vector<IPSockAddr> servers = { IPSockAddr::toIPSockAddr("127.0.0.1", 53), IPSockAddr::toIPSockAddr("127.0.0.2", 53), }; const DnsQueryEvent recordNoError = makeDnsQueryEvent(PROTO_UDP, NS_R_NO_ERROR, 10ms); const DnsQueryEvent recordTimeout = makeDnsQueryEvent(PROTO_UDP, NS_R_TIMEOUT, 250ms); EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_UDP)); // Add a record to 127.0.0.1. EXPECT_TRUE(mDnsStats.addStats(servers[0], recordNoError)); // Add four records to 127.0.0.2. EXPECT_TRUE(mDnsStats.addStats(servers[1], recordNoError)); EXPECT_TRUE(mDnsStats.addStats(servers[1], recordNoError)); EXPECT_TRUE(mDnsStats.addStats(servers[1], recordTimeout)); EXPECT_TRUE(mDnsStats.addStats(servers[1], recordTimeout)); std::vector<StatsData> expectedStats = { makeStatsData(servers[0], 1, 10ms, {{NS_R_NO_ERROR, 1}}), makeStatsData(servers[1], 4, 520ms, {{NS_R_NO_ERROR, 2}, {NS_R_TIMEOUT, 2}}), }; EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats)); // Update the server list, the stats of 127.0.0.2 will remain. servers = { IPSockAddr::toIPSockAddr("127.0.0.2", 53), IPSockAddr::toIPSockAddr("127.0.0.3", 53), IPSockAddr::toIPSockAddr("127.0.0.4", 53), }; EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_UDP)); expectedStats = { makeStatsData(servers[0], 4, 520ms, {{NS_R_NO_ERROR, 2}, {NS_R_TIMEOUT, 2}}), makeStatsData(servers[1], 0, 0ms, {}), makeStatsData(servers[2], 0, 0ms, {}), }; EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats)); // Let's add a record to 127.0.0.2 again. EXPECT_TRUE(mDnsStats.addStats(servers[0], recordNoError)); expectedStats = { makeStatsData(servers[0], 5, 530ms, {{NS_R_NO_ERROR, 3}, {NS_R_TIMEOUT, 2}}), makeStatsData(servers[1], 0, 0ms, {}), makeStatsData(servers[2], 0, 0ms, {}), }; EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats)); } TEST_F(DnsStatsTest, AddStatsRecords_100000) { constexpr int num = 100000; const std::vector<IPSockAddr> servers = { IPSockAddr::toIPSockAddr("127.0.0.1", 53), IPSockAddr::toIPSockAddr("127.0.0.2", 53), IPSockAddr::toIPSockAddr("127.0.0.3", 53), IPSockAddr::toIPSockAddr("127.0.0.4", 53), }; EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_TCP)); EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_UDP)); EXPECT_TRUE(mDnsStats.setServers(servers, PROTO_DOT)); for (int i = 0; i < num; i++) { const auto eventTcp = makeDnsQueryEvent(PROTO_TCP, static_cast<NsRcode>(i % 10), 10ms); const auto eventUdp = makeDnsQueryEvent(PROTO_UDP, static_cast<NsRcode>(i % 10), 10ms); const auto eventDot = makeDnsQueryEvent(PROTO_DOT, static_cast<NsRcode>(i % 10), 10ms); for (const auto& server : servers) { const std::string trace = server.toString() + "-" + std::to_string(i); SCOPED_TRACE(trace); EXPECT_TRUE(mDnsStats.addStats(server, eventTcp)); EXPECT_TRUE(mDnsStats.addStats(server, eventUdp)); EXPECT_TRUE(mDnsStats.addStats(server, eventDot)); } } } } // namespace android::net
DnsTlsDispatcher.cpp +10 −2 Original line number Diff line number Diff line Loading @@ -17,8 +17,11 @@ #define LOG_TAG "resolv" #include "DnsTlsDispatcher.h" #include <netdutils/Stopwatch.h> #include "DnsTlsSocketFactory.h" #include "resolv_cache.h" #include "resolv_private.h" #include "stats.pb.h" Loading @@ -27,6 +30,7 @@ namespace android { namespace net { using android::netdutils::IPSockAddr; using android::netdutils::Stopwatch; using netdutils::Slice; Loading Loading @@ -112,19 +116,23 @@ DnsTlsTransport::Response DnsTlsDispatcher::query(const std::list<DnsTlsServer>& case DnsTlsTransport::Response::success: dnsQueryEvent->set_rcode( static_cast<NsRcode>(reinterpret_cast<HEADER*>(ans.base())->rcode)); resolv_stats_add(statp->netid, IPSockAddr::toIPSockAddr(server.ss), dnsQueryEvent); return code; case DnsTlsTransport::Response::limit_error: dnsQueryEvent->set_rcode(NS_R_INTERNAL_ERROR); resolv_stats_add(statp->netid, IPSockAddr::toIPSockAddr(server.ss), dnsQueryEvent); return code; // These response codes might differ when trying other servers, so // keep iterating to see if we can get a different (better) result. case DnsTlsTransport::Response::network_error: // Sync from res_tls_send in res_send.cpp dnsQueryEvent->set_rcode(NS_R_TIMEOUT); continue; resolv_stats_add(statp->netid, IPSockAddr::toIPSockAddr(server.ss), dnsQueryEvent); break; case DnsTlsTransport::Response::internal_error: dnsQueryEvent->set_rcode(NS_R_INTERNAL_ERROR); continue; resolv_stats_add(statp->netid, IPSockAddr::toIPSockAddr(server.ss), dnsQueryEvent); break; // No "default" statement. } } Loading