Loading DnsStats.cpp +4 −3 Original line number Diff line number Diff line Loading @@ -81,7 +81,8 @@ int StatsData::averageLatencyMs() const { } std::string StatsData::toString() const { if (total == 0) return fmt::format("{} <no data>", sockAddr.ip().toString()); if (total == 0) return fmt::format("{}:{} <no data>", sockAddr.ip().toString(), sockAddr.port()); const auto now = std::chrono::steady_clock::now(); const int lastUpdateSec = duration_cast<seconds>(now - lastUpdate).count(); Loading @@ -91,8 +92,8 @@ std::string StatsData::toString() const { buf += fmt::format("{}:{} ", rcodeToName(rcode), counts); } } return fmt::format("{} ({}, {}ms, [{}], {}s)", sockAddr.ip().toString(), total, averageLatencyMs(), buf, lastUpdateSec); return fmt::format("{}:{} ({}, {}ms, [{}], {}s)", sockAddr.ip().toString(), sockAddr.port(), total, averageLatencyMs(), buf, lastUpdateSec); } StatsRecords::StatsRecords(const IPSockAddr& ipSockAddr, size_t size) Loading DnsStatsTest.cpp +6 −5 Original line number Diff line number Diff line Loading @@ -142,7 +142,8 @@ class DnsStatsTest : public ::testing::Test { for (const auto& stats : statsData) { ASSERT_TRUE(std::regex_search(*dumpString, sm, pattern)); EXPECT_EQ(sm[1], stats.sockAddr.ip().toString()); EXPECT_EQ(sm[1], stats.sockAddr.ip().toString() + ":" + std::to_string(stats.sockAddr.port())); EXPECT_FALSE(sm[2].str().empty()); EXPECT_FALSE(sm[3].str().empty()); *dumpString = sm.suffix(); Loading Loading @@ -238,9 +239,9 @@ TEST_F(DnsStatsTest, SetAddrs) { 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), IPSockAddr::toIPSockAddr("127.0.0.1", 53), IPSockAddr::toIPSockAddr("127.0.0.1", 853), IPSockAddr::toIPSockAddr("127.0.0.1", 5353), IPSockAddr::toIPSockAddr("fe80::1", 53), IPSockAddr::toIPSockAddr("fe80::1", 853), IPSockAddr::toIPSockAddr("fe80::1", 5353), }; // Servers setup fails due to port unset. Loading PrivateDnsCommon.h +3 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,9 @@ namespace android::net { static constexpr int kDohPort = 443; static constexpr int kDotPort = 853; enum class PrivateDnsTransport : uint8_t { kDot, // DNS over TLS. kDoh, // DNS over HTTPS. Loading PrivateDnsConfiguration.cpp +9 −8 Original line number Diff line number Diff line Loading @@ -253,7 +253,7 @@ void PrivateDnsConfiguration::sendPrivateDnsValidationEvent(const ServerIdentity .hostname = identity.provider, .validation = success ? IDnsResolverUnsolicitedEventListener::VALIDATION_RESULT_SUCCESS : IDnsResolverUnsolicitedEventListener::VALIDATION_RESULT_FAILURE, .protocol = (identity.sockaddr.port() == 853) .protocol = (identity.sockaddr.port() == kDotPort) ? IDnsResolverUnsolicitedEventListener::PROTOCOL_DOT : IDnsResolverUnsolicitedEventListener::PROTOCOL_DOH, }; Loading Loading @@ -391,7 +391,7 @@ base::Result<netdutils::IPSockAddr> PrivateDnsConfiguration::getDohServer(unsign std::lock_guard guard(mPrivateDnsLock); auto it = mDohTracker.find(netId); if (it != mDohTracker.end()) { return netdutils::IPSockAddr::toIPSockAddr(it->second.ipAddr, 443); return netdutils::IPSockAddr::toIPSockAddr(it->second.ipAddr, kDohPort); } return Errorf("Failed to get DoH Server: netId {} not found", netId); Loading Loading @@ -473,11 +473,12 @@ int PrivateDnsConfiguration::setDoh(int32_t netId, uint32_t mark, const auto& [dohIt, _] = mDohTracker.insert_or_assign(netId, doh.value()); const auto& dohId = dohIt->second; RecordEntry record(netId, {netdutils::IPSockAddr::toIPSockAddr(dohId.ipAddr, 443), name}, RecordEntry record(netId, {netdutils::IPSockAddr::toIPSockAddr(dohId.ipAddr, kDohPort), name}, dohId.status); mPrivateDnsLog.push(std::move(record)); LOG(INFO) << __func__ << ": Upgrading server to DoH: " << name; resolv_stats_set_addrs(netId, PROTO_DOH, {dohId.ipAddr}, 443); resolv_stats_set_addrs(netId, PROTO_DOH, {dohId.ipAddr}, kDohPort); int probeTimeout = Experiments::getInstance()->getFlag("doh_probe_timeout_ms", kDohProbeDefaultTimeoutMs); Loading @@ -497,7 +498,7 @@ void PrivateDnsConfiguration::clearDohLocked(unsigned netId) { LOG(DEBUG) << "PrivateDnsConfiguration::clearDohLocked (" << netId << ")"; if (mDohDispatcher != nullptr) doh_net_delete(mDohDispatcher, netId); mDohTracker.erase(netId); resolv_stats_set_addrs(netId, PROTO_DOH, {}, 443); resolv_stats_set_addrs(netId, PROTO_DOH, {}, kDohPort); } void PrivateDnsConfiguration::clearDoh(unsigned netId) { Loading Loading @@ -529,7 +530,7 @@ void PrivateDnsConfiguration::onDohStatusUpdate(uint32_t netId, bool success, co Validation status = success ? Validation::success : Validation::fail; it->second.status = status; // Send the events to registered listeners. ServerIdentity identity = {netdutils::IPSockAddr::toIPSockAddr(ipAddr, 443), host}; ServerIdentity identity = {netdutils::IPSockAddr::toIPSockAddr(ipAddr, kDohPort), host}; if (needReportEvent(netId, identity, success)) { sendPrivateDnsValidationEvent(identity, netId, success); } Loading @@ -546,7 +547,7 @@ bool PrivateDnsConfiguration::needReportEvent(uint32_t netId, ServerIdentity ide // the event. switch (identity.sockaddr.port()) { // DoH case 443: { case kDohPort: { auto netPair = mPrivateDnsTransports.find(netId); if (netPair == mPrivateDnsTransports.end()) return true; for (const auto& [id, server] : netPair->second) { Loading @@ -562,7 +563,7 @@ bool PrivateDnsConfiguration::needReportEvent(uint32_t netId, ServerIdentity ide break; } // DoT case 853: { case kDotPort: { auto it = mDohTracker.find(netId); if (it == mDohTracker.end()) return true; if (it->second == identity && it->second.status == Validation::success) { Loading tests/resolv_private_dns_test.cpp +108 −9 Original line number Diff line number Diff line Loading @@ -17,7 +17,10 @@ #define LOG_TAG "resolv_private_dns_test" #include <regex> #include <aidl/android/net/IDnsResolver.h> #include <android-base/file.h> #include <android-base/logging.h> #include <android/binder_manager.h> #include <android/binder_process.h> Loading @@ -34,6 +37,7 @@ #include "tests/unsolicited_listener/unsolicited_event_listener.h" using aidl::android::net::resolv::aidl::IDnsResolverUnsolicitedEventListener; using android::base::ReadFdToString; using android::base::unique_fd; using android::net::resolv::aidl::UnsolicitedEventListener; using android::netdutils::ScopedAddrinfo; Loading @@ -46,6 +50,33 @@ const std::string kDohProbeTimeoutFlag("persist.device_config.netd_native.doh_pr namespace { std::vector<std::string> dumpService(ndk::SpAIBinder binder) { unique_fd localFd, remoteFd; bool success = Pipe(&localFd, &remoteFd); EXPECT_TRUE(success) << "Failed to open pipe for dumping: " << strerror(errno); if (!success) return {}; // dump() blocks until another thread has consumed all its output. std::thread dumpThread = std::thread([binder, remoteFd{std::move(remoteFd)}]() { EXPECT_EQ(STATUS_OK, AIBinder_dump(binder.get(), remoteFd, nullptr, 0)); }); std::string dumpContent; EXPECT_TRUE(ReadFdToString(localFd.get(), &dumpContent)) << "Error during dump: " << strerror(errno); dumpThread.join(); std::stringstream dumpStream(std::move(dumpContent)); std::vector<std::string> lines; std::string line; while (std::getline(dumpStream, line)) { lines.push_back(std::move(line)); } return lines; } // A helper which can propagate the failure to outside of the stmt to know which line // of stmt fails. The expectation fails only for the first failed stmt. #define EXPECT_NO_FAILURE(stmt) \ Loading Loading @@ -140,6 +171,26 @@ class BaseTest : public ::testing::Test { serverAddr, IDnsResolverUnsolicitedEventListener::PROTOCOL_DOH); } bool expectLog(const std::string& serverAddr, const std::string& listen_address) { ndk::SpAIBinder resolvBinder = ndk::SpAIBinder(AServiceManager_getService("dnsresolver")); assert(nullptr != resolvBinder.get()); std::vector<std::string> lines = dumpService(resolvBinder); const std::string ipAddr = listen_address.empty() ? serverAddr : serverAddr + ":" + listen_address; const std::regex pattern(R"(^\s{4,}([0-9a-fA-F:\.]*)[ ]?([<(].*[>)])[ ]?(\S*)$)"); for (const auto& line : lines) { if (line.empty()) continue; std::smatch match; if (std::regex_match(line, match, pattern)) { if (match[1] == ipAddr || match[2] == ipAddr) return true; } } return false; } DnsResponderClient mDnsClient; // Use a shared static DNS listener for all tests to avoid registering lots of listeners Loading @@ -162,6 +213,10 @@ class BaseTest : public ::testing::Test { class BasePrivateDnsTest : public BaseTest { public: static constexpr char kDnsPort[] = "53"; static constexpr char kDohPort[] = "443"; static constexpr char kDotPort[] = "853"; static void SetUpTestSuite() { BaseTest::SetUpTestSuite(); test::DohFrontend::initRustAndroidLogger(); Loading Loading @@ -232,11 +287,11 @@ class BasePrivateDnsTest : public BaseTest { static constexpr char kQueryAnswerA[] = "1.2.3.4"; static constexpr char kQueryAnswerAAAA[] = "2001:db8::100"; test::DNSResponder dns{test::kDefaultListenAddr, "53"}; test::DohFrontend doh{test::kDefaultListenAddr, "443", "127.0.1.3", "53"}; test::DnsTlsFrontend dot{test::kDefaultListenAddr, "853", "127.0.2.3", "53"}; test::DNSResponder doh_backend{"127.0.1.3", "53"}; test::DNSResponder dot_backend{"127.0.2.3", "53"}; test::DNSResponder dns{test::kDefaultListenAddr, kDnsPort}; test::DohFrontend doh{test::kDefaultListenAddr, kDohPort, "127.0.1.3", kDnsPort}; test::DnsTlsFrontend dot{test::kDefaultListenAddr, kDotPort, "127.0.2.3", kDnsPort}; test::DNSResponder doh_backend{"127.0.1.3", kDnsPort}; test::DNSResponder dot_backend{"127.0.2.3", kDnsPort}; // Used to enable DoH during the tests and set up a shorter timeout. std::unique_ptr<ScopedSystemProperties> mDohScopedProp; Loading Loading @@ -432,8 +487,8 @@ TEST_F(PrivateDnsDohTest, PreferIpv6) { // To simplify the test, set the DoT server broken. dot.stopServer(); test::DNSResponder dns_ipv6{listen_ipv6_addr, "53"}; test::DohFrontend doh_ipv6{listen_ipv6_addr, "443", listen_ipv6_addr, "53"}; test::DNSResponder dns_ipv6{listen_ipv6_addr, kDnsPort}; test::DohFrontend doh_ipv6{listen_ipv6_addr, kDohPort, listen_ipv6_addr, kDnsPort}; dns_ipv6.addMapping(kQueryHostname, ns_type::ns_t_a, kQueryAnswerA); dns_ipv6.addMapping(kQueryHostname, ns_type::ns_t_aaaa, kQueryAnswerAAAA); ASSERT_TRUE(dns_ipv6.startServer()); Loading Loading @@ -469,8 +524,8 @@ TEST_F(PrivateDnsDohTest, ChangeAndClearPrivateDnsServer) { // To simplify the test, set the DoT server broken. dot.stopServer(); test::DNSResponder dns_ipv6{listen_ipv6_addr, "53"}; test::DohFrontend doh_ipv6{listen_ipv6_addr, "443", listen_ipv6_addr, "53"}; test::DNSResponder dns_ipv6{listen_ipv6_addr, kDnsPort}; test::DohFrontend doh_ipv6{listen_ipv6_addr, kDohPort, listen_ipv6_addr, kDnsPort}; dns_ipv6.addMapping(kQueryHostname, ns_type::ns_t_a, kQueryAnswerA); dns_ipv6.addMapping(kQueryHostname, ns_type::ns_t_aaaa, kQueryAnswerAAAA); ASSERT_TRUE(dns_ipv6.startServer()); Loading Loading @@ -517,3 +572,47 @@ TEST_F(PrivateDnsDohTest, ChangeAndClearPrivateDnsServer) { EXPECT_EQ(doh_ipv6.queries(), 0); EXPECT_EQ(dns_ipv6.queries().size(), 2U); } TEST_F(PrivateDnsDohTest, ChangePrivateDnsServerAndVerifyOutput) { // To simplify the test, set the DoT server broken. dot.stopServer(); static const std::string ipv4DohServerAddr = "127.0.0.3"; static const std::string ipv6DohServerAddr = "::1"; test::DNSResponder dns_ipv6{ipv6DohServerAddr, kDnsPort}; test::DohFrontend doh_ipv6{ipv6DohServerAddr, kDohPort, ipv6DohServerAddr, kDnsPort}; dns.addMapping(kQueryHostname, ns_type::ns_t_a, kQueryAnswerA); dns.addMapping(kQueryHostname, ns_type::ns_t_aaaa, kQueryAnswerAAAA); dns_ipv6.addMapping(kQueryHostname, ns_type::ns_t_a, kQueryAnswerA); dns_ipv6.addMapping(kQueryHostname, ns_type::ns_t_aaaa, kQueryAnswerAAAA); ASSERT_TRUE(dns_ipv6.startServer()); ASSERT_TRUE(doh_ipv6.startServer()); // Start the v4 DoH server. auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); EXPECT_TRUE(WaitForDohValidation(test::kDefaultListenAddr, true)); EXPECT_TRUE(expectLog(ipv4DohServerAddr, kDohPort)); // Change to an invalid DoH server. parcel.tlsServers = {kHelloExampleComAddrV4}; ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); EXPECT_FALSE(expectLog(kHelloExampleComAddrV4, kDohPort)); EXPECT_TRUE(expectLog("<no data>", "")); // Change to the v6 DoH server. parcel.servers = {ipv6DohServerAddr}; parcel.tlsServers = {ipv6DohServerAddr}; ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); EXPECT_TRUE(WaitForDohValidation(ipv6DohServerAddr, true)); EXPECT_TRUE(expectLog(ipv6DohServerAddr, kDohPort)); EXPECT_FALSE(expectLog(ipv4DohServerAddr, kDohPort)); // Remove the private DNS server. parcel.tlsServers = {}; ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); EXPECT_FALSE(expectLog(ipv4DohServerAddr, kDohPort)); EXPECT_FALSE(expectLog(ipv6DohServerAddr, kDohPort)); EXPECT_TRUE(expectLog("<no data>", "")); } Loading
DnsStats.cpp +4 −3 Original line number Diff line number Diff line Loading @@ -81,7 +81,8 @@ int StatsData::averageLatencyMs() const { } std::string StatsData::toString() const { if (total == 0) return fmt::format("{} <no data>", sockAddr.ip().toString()); if (total == 0) return fmt::format("{}:{} <no data>", sockAddr.ip().toString(), sockAddr.port()); const auto now = std::chrono::steady_clock::now(); const int lastUpdateSec = duration_cast<seconds>(now - lastUpdate).count(); Loading @@ -91,8 +92,8 @@ std::string StatsData::toString() const { buf += fmt::format("{}:{} ", rcodeToName(rcode), counts); } } return fmt::format("{} ({}, {}ms, [{}], {}s)", sockAddr.ip().toString(), total, averageLatencyMs(), buf, lastUpdateSec); return fmt::format("{}:{} ({}, {}ms, [{}], {}s)", sockAddr.ip().toString(), sockAddr.port(), total, averageLatencyMs(), buf, lastUpdateSec); } StatsRecords::StatsRecords(const IPSockAddr& ipSockAddr, size_t size) Loading
DnsStatsTest.cpp +6 −5 Original line number Diff line number Diff line Loading @@ -142,7 +142,8 @@ class DnsStatsTest : public ::testing::Test { for (const auto& stats : statsData) { ASSERT_TRUE(std::regex_search(*dumpString, sm, pattern)); EXPECT_EQ(sm[1], stats.sockAddr.ip().toString()); EXPECT_EQ(sm[1], stats.sockAddr.ip().toString() + ":" + std::to_string(stats.sockAddr.port())); EXPECT_FALSE(sm[2].str().empty()); EXPECT_FALSE(sm[3].str().empty()); *dumpString = sm.suffix(); Loading Loading @@ -238,9 +239,9 @@ TEST_F(DnsStatsTest, SetAddrs) { 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), IPSockAddr::toIPSockAddr("127.0.0.1", 53), IPSockAddr::toIPSockAddr("127.0.0.1", 853), IPSockAddr::toIPSockAddr("127.0.0.1", 5353), IPSockAddr::toIPSockAddr("fe80::1", 53), IPSockAddr::toIPSockAddr("fe80::1", 853), IPSockAddr::toIPSockAddr("fe80::1", 5353), }; // Servers setup fails due to port unset. Loading
PrivateDnsCommon.h +3 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,9 @@ namespace android::net { static constexpr int kDohPort = 443; static constexpr int kDotPort = 853; enum class PrivateDnsTransport : uint8_t { kDot, // DNS over TLS. kDoh, // DNS over HTTPS. Loading
PrivateDnsConfiguration.cpp +9 −8 Original line number Diff line number Diff line Loading @@ -253,7 +253,7 @@ void PrivateDnsConfiguration::sendPrivateDnsValidationEvent(const ServerIdentity .hostname = identity.provider, .validation = success ? IDnsResolverUnsolicitedEventListener::VALIDATION_RESULT_SUCCESS : IDnsResolverUnsolicitedEventListener::VALIDATION_RESULT_FAILURE, .protocol = (identity.sockaddr.port() == 853) .protocol = (identity.sockaddr.port() == kDotPort) ? IDnsResolverUnsolicitedEventListener::PROTOCOL_DOT : IDnsResolverUnsolicitedEventListener::PROTOCOL_DOH, }; Loading Loading @@ -391,7 +391,7 @@ base::Result<netdutils::IPSockAddr> PrivateDnsConfiguration::getDohServer(unsign std::lock_guard guard(mPrivateDnsLock); auto it = mDohTracker.find(netId); if (it != mDohTracker.end()) { return netdutils::IPSockAddr::toIPSockAddr(it->second.ipAddr, 443); return netdutils::IPSockAddr::toIPSockAddr(it->second.ipAddr, kDohPort); } return Errorf("Failed to get DoH Server: netId {} not found", netId); Loading Loading @@ -473,11 +473,12 @@ int PrivateDnsConfiguration::setDoh(int32_t netId, uint32_t mark, const auto& [dohIt, _] = mDohTracker.insert_or_assign(netId, doh.value()); const auto& dohId = dohIt->second; RecordEntry record(netId, {netdutils::IPSockAddr::toIPSockAddr(dohId.ipAddr, 443), name}, RecordEntry record(netId, {netdutils::IPSockAddr::toIPSockAddr(dohId.ipAddr, kDohPort), name}, dohId.status); mPrivateDnsLog.push(std::move(record)); LOG(INFO) << __func__ << ": Upgrading server to DoH: " << name; resolv_stats_set_addrs(netId, PROTO_DOH, {dohId.ipAddr}, 443); resolv_stats_set_addrs(netId, PROTO_DOH, {dohId.ipAddr}, kDohPort); int probeTimeout = Experiments::getInstance()->getFlag("doh_probe_timeout_ms", kDohProbeDefaultTimeoutMs); Loading @@ -497,7 +498,7 @@ void PrivateDnsConfiguration::clearDohLocked(unsigned netId) { LOG(DEBUG) << "PrivateDnsConfiguration::clearDohLocked (" << netId << ")"; if (mDohDispatcher != nullptr) doh_net_delete(mDohDispatcher, netId); mDohTracker.erase(netId); resolv_stats_set_addrs(netId, PROTO_DOH, {}, 443); resolv_stats_set_addrs(netId, PROTO_DOH, {}, kDohPort); } void PrivateDnsConfiguration::clearDoh(unsigned netId) { Loading Loading @@ -529,7 +530,7 @@ void PrivateDnsConfiguration::onDohStatusUpdate(uint32_t netId, bool success, co Validation status = success ? Validation::success : Validation::fail; it->second.status = status; // Send the events to registered listeners. ServerIdentity identity = {netdutils::IPSockAddr::toIPSockAddr(ipAddr, 443), host}; ServerIdentity identity = {netdutils::IPSockAddr::toIPSockAddr(ipAddr, kDohPort), host}; if (needReportEvent(netId, identity, success)) { sendPrivateDnsValidationEvent(identity, netId, success); } Loading @@ -546,7 +547,7 @@ bool PrivateDnsConfiguration::needReportEvent(uint32_t netId, ServerIdentity ide // the event. switch (identity.sockaddr.port()) { // DoH case 443: { case kDohPort: { auto netPair = mPrivateDnsTransports.find(netId); if (netPair == mPrivateDnsTransports.end()) return true; for (const auto& [id, server] : netPair->second) { Loading @@ -562,7 +563,7 @@ bool PrivateDnsConfiguration::needReportEvent(uint32_t netId, ServerIdentity ide break; } // DoT case 853: { case kDotPort: { auto it = mDohTracker.find(netId); if (it == mDohTracker.end()) return true; if (it->second == identity && it->second.status == Validation::success) { Loading
tests/resolv_private_dns_test.cpp +108 −9 Original line number Diff line number Diff line Loading @@ -17,7 +17,10 @@ #define LOG_TAG "resolv_private_dns_test" #include <regex> #include <aidl/android/net/IDnsResolver.h> #include <android-base/file.h> #include <android-base/logging.h> #include <android/binder_manager.h> #include <android/binder_process.h> Loading @@ -34,6 +37,7 @@ #include "tests/unsolicited_listener/unsolicited_event_listener.h" using aidl::android::net::resolv::aidl::IDnsResolverUnsolicitedEventListener; using android::base::ReadFdToString; using android::base::unique_fd; using android::net::resolv::aidl::UnsolicitedEventListener; using android::netdutils::ScopedAddrinfo; Loading @@ -46,6 +50,33 @@ const std::string kDohProbeTimeoutFlag("persist.device_config.netd_native.doh_pr namespace { std::vector<std::string> dumpService(ndk::SpAIBinder binder) { unique_fd localFd, remoteFd; bool success = Pipe(&localFd, &remoteFd); EXPECT_TRUE(success) << "Failed to open pipe for dumping: " << strerror(errno); if (!success) return {}; // dump() blocks until another thread has consumed all its output. std::thread dumpThread = std::thread([binder, remoteFd{std::move(remoteFd)}]() { EXPECT_EQ(STATUS_OK, AIBinder_dump(binder.get(), remoteFd, nullptr, 0)); }); std::string dumpContent; EXPECT_TRUE(ReadFdToString(localFd.get(), &dumpContent)) << "Error during dump: " << strerror(errno); dumpThread.join(); std::stringstream dumpStream(std::move(dumpContent)); std::vector<std::string> lines; std::string line; while (std::getline(dumpStream, line)) { lines.push_back(std::move(line)); } return lines; } // A helper which can propagate the failure to outside of the stmt to know which line // of stmt fails. The expectation fails only for the first failed stmt. #define EXPECT_NO_FAILURE(stmt) \ Loading Loading @@ -140,6 +171,26 @@ class BaseTest : public ::testing::Test { serverAddr, IDnsResolverUnsolicitedEventListener::PROTOCOL_DOH); } bool expectLog(const std::string& serverAddr, const std::string& listen_address) { ndk::SpAIBinder resolvBinder = ndk::SpAIBinder(AServiceManager_getService("dnsresolver")); assert(nullptr != resolvBinder.get()); std::vector<std::string> lines = dumpService(resolvBinder); const std::string ipAddr = listen_address.empty() ? serverAddr : serverAddr + ":" + listen_address; const std::regex pattern(R"(^\s{4,}([0-9a-fA-F:\.]*)[ ]?([<(].*[>)])[ ]?(\S*)$)"); for (const auto& line : lines) { if (line.empty()) continue; std::smatch match; if (std::regex_match(line, match, pattern)) { if (match[1] == ipAddr || match[2] == ipAddr) return true; } } return false; } DnsResponderClient mDnsClient; // Use a shared static DNS listener for all tests to avoid registering lots of listeners Loading @@ -162,6 +213,10 @@ class BaseTest : public ::testing::Test { class BasePrivateDnsTest : public BaseTest { public: static constexpr char kDnsPort[] = "53"; static constexpr char kDohPort[] = "443"; static constexpr char kDotPort[] = "853"; static void SetUpTestSuite() { BaseTest::SetUpTestSuite(); test::DohFrontend::initRustAndroidLogger(); Loading Loading @@ -232,11 +287,11 @@ class BasePrivateDnsTest : public BaseTest { static constexpr char kQueryAnswerA[] = "1.2.3.4"; static constexpr char kQueryAnswerAAAA[] = "2001:db8::100"; test::DNSResponder dns{test::kDefaultListenAddr, "53"}; test::DohFrontend doh{test::kDefaultListenAddr, "443", "127.0.1.3", "53"}; test::DnsTlsFrontend dot{test::kDefaultListenAddr, "853", "127.0.2.3", "53"}; test::DNSResponder doh_backend{"127.0.1.3", "53"}; test::DNSResponder dot_backend{"127.0.2.3", "53"}; test::DNSResponder dns{test::kDefaultListenAddr, kDnsPort}; test::DohFrontend doh{test::kDefaultListenAddr, kDohPort, "127.0.1.3", kDnsPort}; test::DnsTlsFrontend dot{test::kDefaultListenAddr, kDotPort, "127.0.2.3", kDnsPort}; test::DNSResponder doh_backend{"127.0.1.3", kDnsPort}; test::DNSResponder dot_backend{"127.0.2.3", kDnsPort}; // Used to enable DoH during the tests and set up a shorter timeout. std::unique_ptr<ScopedSystemProperties> mDohScopedProp; Loading Loading @@ -432,8 +487,8 @@ TEST_F(PrivateDnsDohTest, PreferIpv6) { // To simplify the test, set the DoT server broken. dot.stopServer(); test::DNSResponder dns_ipv6{listen_ipv6_addr, "53"}; test::DohFrontend doh_ipv6{listen_ipv6_addr, "443", listen_ipv6_addr, "53"}; test::DNSResponder dns_ipv6{listen_ipv6_addr, kDnsPort}; test::DohFrontend doh_ipv6{listen_ipv6_addr, kDohPort, listen_ipv6_addr, kDnsPort}; dns_ipv6.addMapping(kQueryHostname, ns_type::ns_t_a, kQueryAnswerA); dns_ipv6.addMapping(kQueryHostname, ns_type::ns_t_aaaa, kQueryAnswerAAAA); ASSERT_TRUE(dns_ipv6.startServer()); Loading Loading @@ -469,8 +524,8 @@ TEST_F(PrivateDnsDohTest, ChangeAndClearPrivateDnsServer) { // To simplify the test, set the DoT server broken. dot.stopServer(); test::DNSResponder dns_ipv6{listen_ipv6_addr, "53"}; test::DohFrontend doh_ipv6{listen_ipv6_addr, "443", listen_ipv6_addr, "53"}; test::DNSResponder dns_ipv6{listen_ipv6_addr, kDnsPort}; test::DohFrontend doh_ipv6{listen_ipv6_addr, kDohPort, listen_ipv6_addr, kDnsPort}; dns_ipv6.addMapping(kQueryHostname, ns_type::ns_t_a, kQueryAnswerA); dns_ipv6.addMapping(kQueryHostname, ns_type::ns_t_aaaa, kQueryAnswerAAAA); ASSERT_TRUE(dns_ipv6.startServer()); Loading Loading @@ -517,3 +572,47 @@ TEST_F(PrivateDnsDohTest, ChangeAndClearPrivateDnsServer) { EXPECT_EQ(doh_ipv6.queries(), 0); EXPECT_EQ(dns_ipv6.queries().size(), 2U); } TEST_F(PrivateDnsDohTest, ChangePrivateDnsServerAndVerifyOutput) { // To simplify the test, set the DoT server broken. dot.stopServer(); static const std::string ipv4DohServerAddr = "127.0.0.3"; static const std::string ipv6DohServerAddr = "::1"; test::DNSResponder dns_ipv6{ipv6DohServerAddr, kDnsPort}; test::DohFrontend doh_ipv6{ipv6DohServerAddr, kDohPort, ipv6DohServerAddr, kDnsPort}; dns.addMapping(kQueryHostname, ns_type::ns_t_a, kQueryAnswerA); dns.addMapping(kQueryHostname, ns_type::ns_t_aaaa, kQueryAnswerAAAA); dns_ipv6.addMapping(kQueryHostname, ns_type::ns_t_a, kQueryAnswerA); dns_ipv6.addMapping(kQueryHostname, ns_type::ns_t_aaaa, kQueryAnswerAAAA); ASSERT_TRUE(dns_ipv6.startServer()); ASSERT_TRUE(doh_ipv6.startServer()); // Start the v4 DoH server. auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); EXPECT_TRUE(WaitForDohValidation(test::kDefaultListenAddr, true)); EXPECT_TRUE(expectLog(ipv4DohServerAddr, kDohPort)); // Change to an invalid DoH server. parcel.tlsServers = {kHelloExampleComAddrV4}; ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); EXPECT_FALSE(expectLog(kHelloExampleComAddrV4, kDohPort)); EXPECT_TRUE(expectLog("<no data>", "")); // Change to the v6 DoH server. parcel.servers = {ipv6DohServerAddr}; parcel.tlsServers = {ipv6DohServerAddr}; ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); EXPECT_TRUE(WaitForDohValidation(ipv6DohServerAddr, true)); EXPECT_TRUE(expectLog(ipv6DohServerAddr, kDohPort)); EXPECT_FALSE(expectLog(ipv4DohServerAddr, kDohPort)); // Remove the private DNS server. parcel.tlsServers = {}; ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); EXPECT_FALSE(expectLog(ipv4DohServerAddr, kDohPort)); EXPECT_FALSE(expectLog(ipv6DohServerAddr, kDohPort)); EXPECT_TRUE(expectLog("<no data>", "")); }