Add the DoH test in resolv_private_dns_test.
This test checks whether DoH stats shows up in dumpsys correctly.
Bug: 201735918
Test: cd packages/modules/DnsResolver && atest
Change-Id: I9609198c895e7e574c8d297f67dc8e175a42e31d
diff --git a/DnsStats.cpp b/DnsStats.cpp
index ef236f3..671732c 100644
--- a/DnsStats.cpp
+++ b/DnsStats.cpp
@@ -81,7 +81,8 @@
}
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();
@@ -91,8 +92,8 @@
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)
diff --git a/DnsStatsTest.cpp b/DnsStatsTest.cpp
index f439a55..796d73c 100644
--- a/DnsStatsTest.cpp
+++ b/DnsStatsTest.cpp
@@ -142,7 +142,8 @@
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();
@@ -237,10 +238,10 @@
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", 0), IPSockAddr::toIPSockAddr("fe80::1", 0),
+ 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.
diff --git a/PrivateDnsCommon.h b/PrivateDnsCommon.h
index 2a92d30..919fc43 100644
--- a/PrivateDnsCommon.h
+++ b/PrivateDnsCommon.h
@@ -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.
diff --git a/PrivateDnsConfiguration.cpp b/PrivateDnsConfiguration.cpp
index 54a4239..5c0894d 100644
--- a/PrivateDnsConfiguration.cpp
+++ b/PrivateDnsConfiguration.cpp
@@ -253,7 +253,7 @@
.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,
};
@@ -391,7 +391,7 @@
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);
@@ -473,11 +473,12 @@
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);
@@ -497,7 +498,7 @@
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) {
@@ -529,7 +530,7 @@
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);
}
@@ -546,7 +547,7 @@
// 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) {
@@ -562,7 +563,7 @@
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) {
diff --git a/tests/resolv_private_dns_test.cpp b/tests/resolv_private_dns_test.cpp
index c5c07a5..4c14fe8 100644
--- a/tests/resolv_private_dns_test.cpp
+++ b/tests/resolv_private_dns_test.cpp
@@ -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>
@@ -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;
@@ -46,6 +50,33 @@
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) \
@@ -140,6 +171,26 @@
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
@@ -162,6 +213,10 @@
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();
@@ -232,11 +287,11 @@
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;
@@ -432,8 +487,8 @@
// 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());
@@ -469,8 +524,8 @@
// 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());
@@ -517,3 +572,47 @@
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>", ""));
+}