Loading PrivateDnsConfiguration.cpp +4 −4 Original line number Diff line number Diff line Loading @@ -221,7 +221,7 @@ PrivateDnsStatus PrivateDnsConfiguration::getStatusLocked(unsigned netId) const auto it = mDohTracker.find(netId); if (it != mDohTracker.end()) { status.dohServersMap.emplace(IPSockAddr::toIPSockAddr(it->second.ipAddr, kDohPort), it->second.status); DohServerInfo(it->second.httpsTemplate, it->second.status)); } return status; Loading Loading @@ -272,7 +272,7 @@ NetworkDnsServerSupportReported PrivateDnsConfiguration::getStatusForMetrics(uns bool validated = std::any_of(status.dohServersMap.begin(), status.dohServersMap.end(), [&target](const auto& entry) { return entry.first == target && entry.second == Validation::success; entry.second.status == Validation::success; }); Server* server = event.mutable_servers()->add_server(); server->set_protocol(PROTO_DOH); Loading Loading @@ -606,8 +606,6 @@ int PrivateDnsConfiguration::setDoh(int32_t netId, uint32_t mark, // Sort the input servers to prefer IPv6. const std::vector<std::string> sortedServers = sortServers(servers); initDohLocked(); const auto& doh = makeDohIdentity(sortedServers, name, dohParams); if (!doh.ok()) { LOG(INFO) << __func__ << ": No suitable DoH server found"; Loading @@ -615,6 +613,8 @@ int PrivateDnsConfiguration::setDoh(int32_t netId, uint32_t mark, return 0; } initDohLocked(); auto it = mDohTracker.find(netId); // Skip if the same server already exists and its status == success. if (it != mDohTracker.end() && it->second == doh.value() && Loading PrivateDnsConfiguration.h +11 −3 Original line number Diff line number Diff line Loading @@ -44,13 +44,21 @@ namespace net { PrivateDnsModes convertEnumType(PrivateDnsMode mode); struct DohServerInfo { std::string httpsTemplate; Validation status; DohServerInfo(const std::string httpsTemplate, Validation status) : httpsTemplate(httpsTemplate), status(status) {} }; struct PrivateDnsStatus { PrivateDnsMode mode; // TODO: change the type to std::vector<DnsTlsServer>. std::map<DnsTlsServer, Validation, AddressComparator> dotServersMap; std::map<netdutils::IPSockAddr, Validation> dohServersMap; std::map<netdutils::IPSockAddr, DohServerInfo> dohServersMap; std::list<DnsTlsServer> validatedServers() const { std::list<DnsTlsServer> servers; Loading @@ -64,8 +72,8 @@ struct PrivateDnsStatus { } bool hasValidatedDohServers() const { for (const auto& [_, status] : dohServersMap) { if (status == Validation::success) { for (const auto& [_, info] : dohServersMap) { if (info.status == Validation::success) { return true; } } Loading ResolverController.cpp +15 −2 Original line number Diff line number Diff line Loading @@ -349,9 +349,9 @@ void ResolverController::dump(DumpWriter& dw, unsigned netId) { const auto privateDnsStatus = PrivateDnsConfiguration::getInstance().getStatus(netId); dw.println("Private DNS mode: %s", getPrivateDnsModeString(privateDnsStatus.mode)); if (privateDnsStatus.dotServersMap.size() == 0) { dw.println("No Private DNS servers configured"); dw.println("No DoT servers configured"); } else { dw.println("Private DNS configuration (%u entries)", dw.println("DoT configuration (%u entries)", static_cast<uint32_t>(privateDnsStatus.dotServersMap.size())); dw.incIndent(); for (const auto& [server, validation] : privateDnsStatus.dotServersMap) { Loading @@ -360,6 +360,19 @@ void ResolverController::dump(DumpWriter& dw, unsigned netId) { } dw.decIndent(); } if (privateDnsStatus.dohServersMap.size() == 0) { dw.println("No DoH servers configured"); } else { // TODO: print the hostname and URL as well. dw.println("DoH configuration (%u entries)", static_cast<uint32_t>(privateDnsStatus.dohServersMap.size())); dw.incIndent(); for (const auto& [server, info] : privateDnsStatus.dohServersMap) { dw.println("%s url{%s} status{%s}", server.toString().c_str(), info.httpsTemplate.c_str(), validationStatusToString(info.status)); } dw.decIndent(); } dw.println("Concurrent DNS query timeout: %d", wait_for_pending_req_timeout_count); resolv_netconfig_dump(dw, netId); } Loading tests/resolv_private_dns_test.cpp +71 −0 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ #include <poll.h> #include "NetdClient.h" using aidl::android::net::resolv::aidl::DohParamsParcel; using aidl::android::net::resolv::aidl::IDnsResolverUnsolicitedEventListener; using android::base::GetProperty; using android::base::ReadFdToString; Loading Loading @@ -1344,6 +1345,7 @@ TEST_F(PrivateDnsDohTest, ReceiveResetStream) { // Tests that, given an IP address with an allowed DoH provider name, PrivateDnsConfiguration // attempts to probe the server for DoH. // This test is representative of DoH using DDR in opportunistic mode. TEST_F(PrivateDnsDohTest, UseDohAsLongAsHostnameMatch) { // "example.com" is an allowed DoH provider name defined in // PrivateDnsConfiguration::mAvailableDoHProviders. Loading @@ -1368,5 +1370,74 @@ TEST_F(PrivateDnsDohTest, UseDohAsLongAsHostnameMatch) { .build())); EXPECT_TRUE(WaitForDotValidationFailure(someOtherIp)); EXPECT_TRUE(WaitForDohValidationFailure(someOtherIp)); // Disable DoT and DoH. This ensures that when DoT is re-enabled right afterwards, the test // observes a validation failure. ASSERT_TRUE( mDnsClient.SetResolversFromParcel(ResolverParams::Builder().setDotServers({}).build())); // If DDR is enabled and reports no results (empty DoH params), don't probe for DoH. DohParamsParcel emptyDohParams = {}; ASSERT_TRUE(mDnsClient.SetResolversFromParcel(ResolverParams::Builder() .setDotServers({someOtherIp}) .setPrivateDnsProvider(allowedDohName) .setDohParams(emptyDohParams) .build())); EXPECT_TRUE(WaitForDotValidationFailure(someOtherIp)); EXPECT_FALSE(WaitForDohValidationFailure(someOtherIp)); EXPECT_FALSE(hasUncaughtPrivateDnsValidation(someOtherIp)); } // Tests that if DDR is enabled, but returns no parameters, that DoH is not enabled. TEST_F(PrivateDnsDohTest, DdrEnabledButNoResponse) { // "example.com" is an allowed DoH provider name defined in // PrivateDnsConfiguration::mAvailableDoHProviders. constexpr char allowedDohName[] = "example.com"; constexpr char someOtherIp[] = "127.99.99.99"; // If DDR is enabled and reports no results (empty DoH params), don't probe for DoH. DohParamsParcel emptyDohParams = {}; ASSERT_TRUE(mDnsClient.SetResolversFromParcel(ResolverParams::Builder() .setDotServers({someOtherIp}) .setPrivateDnsProvider(allowedDohName) .setDohParams(emptyDohParams) .build())); EXPECT_TRUE(WaitForDotValidationFailure(someOtherIp)); EXPECT_FALSE(WaitForDohValidationFailure(someOtherIp)); EXPECT_FALSE(hasUncaughtPrivateDnsValidation(someOtherIp)); } // Tests DoH with a hostname. // This test is representative of DoH using DDR in strict mode. TEST_F(PrivateDnsDohTest, DohParamsParcel) { // Because the test doesn't support serving DoH in strict mode, it cannot check for actual DoH // queries, it can only check for validation attempts. constexpr char name[] = "example.com"; constexpr char dohIp[] = "127.99.99.99"; DohParamsParcel dohParams = { .name = name, .ips = {dohIp}, .dohpath = "/dns-query{?dns}", .port = 443, }; // Only DoH enabled. ASSERT_TRUE(mDnsClient.SetResolversFromParcel( ResolverParams::Builder().setDohParams(dohParams).build())); EXPECT_FALSE(WaitForDotValidationFailure(dohIp)); EXPECT_TRUE(WaitForDohValidationFailure(dohIp)); // Both DoT and DoH enabled. constexpr char dotIp[] = "127.88.88.88"; ASSERT_TRUE(mDnsClient.SetResolversFromParcel(ResolverParams::Builder() .setPrivateDnsProvider(name) .setDotServers({dotIp}) .setDohParams(dohParams) .build())); EXPECT_TRUE(WaitForDotValidationFailure(dotIp)); EXPECT_TRUE(WaitForDohValidationFailure(dohIp)); EXPECT_FALSE(hasUncaughtPrivateDnsValidation(dohIp)); EXPECT_FALSE(hasUncaughtPrivateDnsValidation(dotIp)); } Loading
PrivateDnsConfiguration.cpp +4 −4 Original line number Diff line number Diff line Loading @@ -221,7 +221,7 @@ PrivateDnsStatus PrivateDnsConfiguration::getStatusLocked(unsigned netId) const auto it = mDohTracker.find(netId); if (it != mDohTracker.end()) { status.dohServersMap.emplace(IPSockAddr::toIPSockAddr(it->second.ipAddr, kDohPort), it->second.status); DohServerInfo(it->second.httpsTemplate, it->second.status)); } return status; Loading Loading @@ -272,7 +272,7 @@ NetworkDnsServerSupportReported PrivateDnsConfiguration::getStatusForMetrics(uns bool validated = std::any_of(status.dohServersMap.begin(), status.dohServersMap.end(), [&target](const auto& entry) { return entry.first == target && entry.second == Validation::success; entry.second.status == Validation::success; }); Server* server = event.mutable_servers()->add_server(); server->set_protocol(PROTO_DOH); Loading Loading @@ -606,8 +606,6 @@ int PrivateDnsConfiguration::setDoh(int32_t netId, uint32_t mark, // Sort the input servers to prefer IPv6. const std::vector<std::string> sortedServers = sortServers(servers); initDohLocked(); const auto& doh = makeDohIdentity(sortedServers, name, dohParams); if (!doh.ok()) { LOG(INFO) << __func__ << ": No suitable DoH server found"; Loading @@ -615,6 +613,8 @@ int PrivateDnsConfiguration::setDoh(int32_t netId, uint32_t mark, return 0; } initDohLocked(); auto it = mDohTracker.find(netId); // Skip if the same server already exists and its status == success. if (it != mDohTracker.end() && it->second == doh.value() && Loading
PrivateDnsConfiguration.h +11 −3 Original line number Diff line number Diff line Loading @@ -44,13 +44,21 @@ namespace net { PrivateDnsModes convertEnumType(PrivateDnsMode mode); struct DohServerInfo { std::string httpsTemplate; Validation status; DohServerInfo(const std::string httpsTemplate, Validation status) : httpsTemplate(httpsTemplate), status(status) {} }; struct PrivateDnsStatus { PrivateDnsMode mode; // TODO: change the type to std::vector<DnsTlsServer>. std::map<DnsTlsServer, Validation, AddressComparator> dotServersMap; std::map<netdutils::IPSockAddr, Validation> dohServersMap; std::map<netdutils::IPSockAddr, DohServerInfo> dohServersMap; std::list<DnsTlsServer> validatedServers() const { std::list<DnsTlsServer> servers; Loading @@ -64,8 +72,8 @@ struct PrivateDnsStatus { } bool hasValidatedDohServers() const { for (const auto& [_, status] : dohServersMap) { if (status == Validation::success) { for (const auto& [_, info] : dohServersMap) { if (info.status == Validation::success) { return true; } } Loading
ResolverController.cpp +15 −2 Original line number Diff line number Diff line Loading @@ -349,9 +349,9 @@ void ResolverController::dump(DumpWriter& dw, unsigned netId) { const auto privateDnsStatus = PrivateDnsConfiguration::getInstance().getStatus(netId); dw.println("Private DNS mode: %s", getPrivateDnsModeString(privateDnsStatus.mode)); if (privateDnsStatus.dotServersMap.size() == 0) { dw.println("No Private DNS servers configured"); dw.println("No DoT servers configured"); } else { dw.println("Private DNS configuration (%u entries)", dw.println("DoT configuration (%u entries)", static_cast<uint32_t>(privateDnsStatus.dotServersMap.size())); dw.incIndent(); for (const auto& [server, validation] : privateDnsStatus.dotServersMap) { Loading @@ -360,6 +360,19 @@ void ResolverController::dump(DumpWriter& dw, unsigned netId) { } dw.decIndent(); } if (privateDnsStatus.dohServersMap.size() == 0) { dw.println("No DoH servers configured"); } else { // TODO: print the hostname and URL as well. dw.println("DoH configuration (%u entries)", static_cast<uint32_t>(privateDnsStatus.dohServersMap.size())); dw.incIndent(); for (const auto& [server, info] : privateDnsStatus.dohServersMap) { dw.println("%s url{%s} status{%s}", server.toString().c_str(), info.httpsTemplate.c_str(), validationStatusToString(info.status)); } dw.decIndent(); } dw.println("Concurrent DNS query timeout: %d", wait_for_pending_req_timeout_count); resolv_netconfig_dump(dw, netId); } Loading
tests/resolv_private_dns_test.cpp +71 −0 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ #include <poll.h> #include "NetdClient.h" using aidl::android::net::resolv::aidl::DohParamsParcel; using aidl::android::net::resolv::aidl::IDnsResolverUnsolicitedEventListener; using android::base::GetProperty; using android::base::ReadFdToString; Loading Loading @@ -1344,6 +1345,7 @@ TEST_F(PrivateDnsDohTest, ReceiveResetStream) { // Tests that, given an IP address with an allowed DoH provider name, PrivateDnsConfiguration // attempts to probe the server for DoH. // This test is representative of DoH using DDR in opportunistic mode. TEST_F(PrivateDnsDohTest, UseDohAsLongAsHostnameMatch) { // "example.com" is an allowed DoH provider name defined in // PrivateDnsConfiguration::mAvailableDoHProviders. Loading @@ -1368,5 +1370,74 @@ TEST_F(PrivateDnsDohTest, UseDohAsLongAsHostnameMatch) { .build())); EXPECT_TRUE(WaitForDotValidationFailure(someOtherIp)); EXPECT_TRUE(WaitForDohValidationFailure(someOtherIp)); // Disable DoT and DoH. This ensures that when DoT is re-enabled right afterwards, the test // observes a validation failure. ASSERT_TRUE( mDnsClient.SetResolversFromParcel(ResolverParams::Builder().setDotServers({}).build())); // If DDR is enabled and reports no results (empty DoH params), don't probe for DoH. DohParamsParcel emptyDohParams = {}; ASSERT_TRUE(mDnsClient.SetResolversFromParcel(ResolverParams::Builder() .setDotServers({someOtherIp}) .setPrivateDnsProvider(allowedDohName) .setDohParams(emptyDohParams) .build())); EXPECT_TRUE(WaitForDotValidationFailure(someOtherIp)); EXPECT_FALSE(WaitForDohValidationFailure(someOtherIp)); EXPECT_FALSE(hasUncaughtPrivateDnsValidation(someOtherIp)); } // Tests that if DDR is enabled, but returns no parameters, that DoH is not enabled. TEST_F(PrivateDnsDohTest, DdrEnabledButNoResponse) { // "example.com" is an allowed DoH provider name defined in // PrivateDnsConfiguration::mAvailableDoHProviders. constexpr char allowedDohName[] = "example.com"; constexpr char someOtherIp[] = "127.99.99.99"; // If DDR is enabled and reports no results (empty DoH params), don't probe for DoH. DohParamsParcel emptyDohParams = {}; ASSERT_TRUE(mDnsClient.SetResolversFromParcel(ResolverParams::Builder() .setDotServers({someOtherIp}) .setPrivateDnsProvider(allowedDohName) .setDohParams(emptyDohParams) .build())); EXPECT_TRUE(WaitForDotValidationFailure(someOtherIp)); EXPECT_FALSE(WaitForDohValidationFailure(someOtherIp)); EXPECT_FALSE(hasUncaughtPrivateDnsValidation(someOtherIp)); } // Tests DoH with a hostname. // This test is representative of DoH using DDR in strict mode. TEST_F(PrivateDnsDohTest, DohParamsParcel) { // Because the test doesn't support serving DoH in strict mode, it cannot check for actual DoH // queries, it can only check for validation attempts. constexpr char name[] = "example.com"; constexpr char dohIp[] = "127.99.99.99"; DohParamsParcel dohParams = { .name = name, .ips = {dohIp}, .dohpath = "/dns-query{?dns}", .port = 443, }; // Only DoH enabled. ASSERT_TRUE(mDnsClient.SetResolversFromParcel( ResolverParams::Builder().setDohParams(dohParams).build())); EXPECT_FALSE(WaitForDotValidationFailure(dohIp)); EXPECT_TRUE(WaitForDohValidationFailure(dohIp)); // Both DoT and DoH enabled. constexpr char dotIp[] = "127.88.88.88"; ASSERT_TRUE(mDnsClient.SetResolversFromParcel(ResolverParams::Builder() .setPrivateDnsProvider(name) .setDotServers({dotIp}) .setDohParams(dohParams) .build())); EXPECT_TRUE(WaitForDotValidationFailure(dotIp)); EXPECT_TRUE(WaitForDohValidationFailure(dohIp)); EXPECT_FALSE(hasUncaughtPrivateDnsValidation(dohIp)); EXPECT_FALSE(hasUncaughtPrivateDnsValidation(dotIp)); }