Loading DnsTlsDispatcher.cpp +34 −29 Original line number Diff line number Diff line Loading @@ -67,8 +67,7 @@ std::list<DnsTlsServer> DnsTlsDispatcher::getOrderedAndUsableServerList( for (const auto& tlsServer : tlsServers) { const Key key = std::make_pair(mark, tlsServer); if (const Transport* xport = getTransport(key); xport != nullptr) { // DoT revalidation specific feature. if (Transport* xport = getTransport(key); xport != nullptr) { if (!xport->usable()) { // Don't use this xport. It will be removed after timeout // (IDLE_TIMEOUT minutes). Loading Loading @@ -112,7 +111,13 @@ DnsTlsTransport::Response DnsTlsDispatcher::query(const std::list<DnsTlsServer>& const std::list<DnsTlsServer> servers( getOrderedAndUsableServerList(tlsServers, statp->netid, statp->mark)); if (servers.empty()) LOG(WARNING) << "No usable DnsTlsServers"; if (servers.empty()) { LOG(WARNING) << "No usable DnsTlsServers"; // Call cleanup so the expired Transports can be removed as expected. std::lock_guard guard(sLock); cleanup(std::chrono::steady_clock::now()); } DnsTlsTransport::Response code = DnsTlsTransport::Response::internal_error; int serverCount = 0; Loading Loading @@ -209,9 +214,14 @@ DnsTlsTransport::Response DnsTlsDispatcher::query(const DnsTlsServer& server, un std::lock_guard guard(sLock); --xport->useCount; xport->lastUsed = now; if (code == DnsTlsTransport::Response::network_error) { xport->continuousfailureCount++; } else { xport->continuousfailureCount = 0; } // DoT revalidation specific feature. if (xport->checkRevalidationNecessary(code)) { if (xport->checkRevalidationNecessary()) { // Even if the revalidation passes, it doesn't guarantee that DoT queries // to the xport can stop failing because revalidation creates a new connection // to probe while the xport still uses an existing connection. So far, there isn't Loading Loading @@ -308,12 +318,11 @@ DnsTlsDispatcher::Transport* DnsTlsDispatcher::addTransport(const DnsTlsServer& int queryTimeout = instance->getFlag("dot_query_timeout_ms", Transport::kDotQueryTimeoutMs); // Check and adjust the parameters if they are improperly set. bool revalidationEnabled = false; const bool isForOpportunisticMode = server.name.empty(); if (triggerThr > 0 && unusableThr > 0 && isForOpportunisticMode) { revalidationEnabled = true; } else { if (triggerThr <= 0 || !isForOpportunisticMode) { triggerThr = -1; } if (unusableThr <= 0 || !isForOpportunisticMode) { unusableThr = -1; } if (queryTimeout < 0) { Loading @@ -322,9 +331,8 @@ DnsTlsDispatcher::Transport* DnsTlsDispatcher::addTransport(const DnsTlsServer& queryTimeout = 1000; } ret = new Transport(server, mark, netId, mFactory.get(), revalidationEnabled, triggerThr, unusableThr, queryTimeout); LOG(DEBUG) << "Transport is initialized with { " << triggerThr << ", " << unusableThr << ", " ret = new Transport(server, mark, netId, mFactory.get(), triggerThr, unusableThr, queryTimeout); LOG(INFO) << "Transport is initialized with { " << triggerThr << ", " << unusableThr << ", " << queryTimeout << "ms }" << " for server { " << server.toIpString() << "/" << server.name << " }"; Loading @@ -338,26 +346,23 @@ DnsTlsDispatcher::Transport* DnsTlsDispatcher::getTransport(const Key& key) { return (it == mStore.end() ? nullptr : it->second.get()); } bool DnsTlsDispatcher::Transport::checkRevalidationNecessary(DnsTlsTransport::Response code) { if (!revalidationEnabled) return false; bool DnsTlsDispatcher::Transport::checkRevalidationNecessary() { if (triggerThreshold <= 0) return false; if (continuousfailureCount < triggerThreshold) return false; if (isRevalidationThresholdReached) return false; if (code == DnsTlsTransport::Response::network_error) { continuousfailureCount++; } else { continuousfailureCount = 0; } // triggerThreshold must be greater than 0 because the value of revalidationEnabled is true. if (usable() && continuousfailureCount == triggerThreshold) { isRevalidationThresholdReached = true; return true; } return false; } bool DnsTlsDispatcher::Transport::usable() const { if (!revalidationEnabled) return true; bool DnsTlsDispatcher::Transport::usable() { if (unusableThreshold <= 0) return true; return continuousfailureCount < unusableThreshold; if (continuousfailureCount >= unusableThreshold) { // Once reach the threshold, mark this Transport as unusable. isXportUnusableThresholdReached = true; } return !isXportUnusableThresholdReached; } } // end of namespace net Loading DnsTlsDispatcher.h +20 −19 Original line number Diff line number Diff line Loading @@ -83,11 +83,10 @@ class DnsTlsDispatcher : public PrivateDnsValidationObserver { // usage monitoring so we can expire idle sessions from the cache. struct Transport { Transport(const DnsTlsServer& server, unsigned mark, unsigned netId, IDnsTlsSocketFactory* _Nonnull factory, bool revalidationEnabled, int triggerThr, int unusableThr, int timeout) IDnsTlsSocketFactory* _Nonnull factory, int triggerThr, int unusableThr, int timeout) : transport(server, mark, factory), mNetId(netId), revalidationEnabled(revalidationEnabled), triggerThreshold(triggerThr), unusableThreshold(unusableThr), mTimeout(timeout) {} Loading @@ -106,9 +105,12 @@ class DnsTlsDispatcher : public PrivateDnsValidationObserver { // If DoT revalidation is disabled, it returns true; otherwise, it returns // whether or not this Transport is usable. bool usable() const REQUIRES(sLock); bool usable() REQUIRES(sLock); bool checkRevalidationNecessary(DnsTlsTransport::Response code) REQUIRES(sLock); // Used to track if this Transport is usable. int continuousfailureCount GUARDED_BY(sLock) = 0; bool checkRevalidationNecessary() REQUIRES(sLock); std::chrono::milliseconds timeout() const { return mTimeout; } Loading @@ -117,25 +119,24 @@ class DnsTlsDispatcher : public PrivateDnsValidationObserver { static constexpr int kDotQueryTimeoutMs = -1; private: // Used to track if this Transport is usable. int continuousfailureCount GUARDED_BY(sLock) = 0; // The flag to record whether or not dot_revalidation_threshold is ever reached. bool isRevalidationThresholdReached GUARDED_BY(sLock) = false; // Used to indicate whether DoT revalidation is enabled for this Transport. // The value is set to true only if: // 1. both triggerThreshold and unusableThreshold are positive values. // 2. private DNS mode is opportunistic. const bool revalidationEnabled; // The flag to record whether or not dot_xport_unusable_threshold is ever reached. bool isXportUnusableThresholdReached GUARDED_BY(sLock) = false; // The number of continuous failures to trigger a validation. It takes effect when DoT // revalidation is on. If the value is not a positive value, DoT revalidation is disabled. // Note that it must be at least 10, or it breaks ConnectTlsServerTimeout_ConcurrentQueries // test. // If the number of continuous query timeouts reaches the threshold, mark the // server as unvalidated and trigger a validation. // If the value is not a positive value or private DNS mode is strict mode, no threshold is // set. Note that it must be at least 10, or it breaks // ConnectTlsServerTimeout_ConcurrentQueries test. const int triggerThreshold; // The threshold to determine if this Transport is considered unusable. // If continuousfailureCount reaches this value, this Transport is no longer used. It // takes effect when DoT revalidation is on. If the value is not a positive value, DoT // revalidation is disabled. // If the number of continuous query timeouts reaches the threshold, mark this // Transport as unusable. An unusable Transport won't be used anymore. // If the value is not a positive value or private DNS mode is strict mode, no threshold is // set. const int unusableThreshold; // The time to await a future (the result of a DNS request) from the DnsTlsTransport Loading tests/resolv_integration_test.cpp +107 −3 Original line number Diff line number Diff line Loading @@ -2907,6 +2907,11 @@ TEST_F(ResolverTest, BrokenEdns) { const std::string testHostName = config.asHostName(); SCOPED_TRACE(testHostName); // Don't skip unusable DoT servers and disable revalidation for this test. ScopedSystemProperties sp1(kDotXportUnusableThresholdFlag, "-1"); ScopedSystemProperties sp2(kDotRevalidationThresholdFlag, "-1"); resetNetwork(); const char* host_name = testHostName.c_str(); dns.addMapping(host_name, ns_type::ns_t_a, ADDR4); dns.setEdns(config.edns); Loading Loading @@ -4572,6 +4577,10 @@ TEST_F(ResolverTest, ConnectTlsServerTimeout) { ScopedSystemProperties sp3(kDotAsyncHandshakeFlag, config.asyncHandshake ? "1" : "0"); ScopedSystemProperties sp4(kDotMaxretriesFlag, std::to_string(config.maxRetries)); // Don't skip unusable DoT servers and disable revalidation for this test. ScopedSystemProperties sp5(kDotXportUnusableThresholdFlag, "-1"); ScopedSystemProperties sp6(kDotRevalidationThresholdFlag, "-1"); resetNetwork(); // Set up resolver to opportunistic mode. Loading Loading @@ -4662,6 +4671,10 @@ TEST_F(ResolverTest, ConnectTlsServerTimeout_ConcurrentQueries) { std::to_string(config.dotConnectTimeoutMs)); ScopedSystemProperties sp3(kDotAsyncHandshakeFlag, config.asyncHandshake ? "1" : "0"); ScopedSystemProperties sp4(kDotMaxretriesFlag, std::to_string(config.maxRetries)); // Don't skip unusable DoT servers and disable revalidation for this test. ScopedSystemProperties sp5(kDotXportUnusableThresholdFlag, "-1"); ScopedSystemProperties sp6(kDotRevalidationThresholdFlag, "-1"); resetNetwork(); for (const auto& dnsMode : {"OPPORTUNISTIC", "STRICT"}) { Loading Loading @@ -4744,6 +4757,10 @@ TEST_F(ResolverTest, QueryTlsServerTimeout) { ASSERT_TRUE(tls.startServer()); ScopedSystemProperties sp(kDotQueryTimeoutMsFlag, std::to_string(queryTimeoutMs)); // Don't skip unusable DoT servers and disable revalidation for this test. ScopedSystemProperties sp2(kDotXportUnusableThresholdFlag, "-1"); ScopedSystemProperties sp3(kDotRevalidationThresholdFlag, "-1"); resetNetwork(); auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); Loading Loading @@ -4787,8 +4804,86 @@ TEST_F(ResolverTest, QueryTlsServerTimeout) { // Also check how much time the resolver processed the query. timeTakenMs = s.timeTakenUs() / 1000; EXPECT_LE(timeTakenMs, 500); EXPECT_EQ(2, tls.queries()); EXPECT_TRUE(tls.waitForQueries(2)); } } } // Tests that the DnsResolver can skip using unusable DoT servers if dot_xport_unusable_threshold // flag is set. In this test, we make test DoT servers unresponsive during connection handshake, // so the DnsResolver will skip using a DoT server if the number of timed out queries reaches // the threshold. TEST_F(ResolverTest, SkipUnusableTlsServer) { constexpr int DOT_CONNECT_TIMEOUT_MS = 1000; static const struct TestConfig { int dotXportUnusableThreshold; int queries; int expectedQueriesSentToDot1; int expectedQueriesSentToDot2; } testConfigs[] = { // clang-format off // expectedQueriesSentToDot2 is 0 because dot_quick_fallback flag is set. {-1, 3, 3, 0}, { 1, 3, 1, 1}, { 3, 10, 3, 3}, // clang-format on }; for (const auto& config : testConfigs) { SCOPED_TRACE(fmt::format("testConfig: [{}, {}, {}, {}]", config.dotXportUnusableThreshold, config.queries, config.expectedQueriesSentToDot1, config.expectedQueriesSentToDot2)); const std::string addr1 = getUniqueIPv4Address(); const std::string addr2 = getUniqueIPv4Address(); test::DNSResponder dns1(addr1); test::DNSResponder dns2(addr2); test::DnsTlsFrontend dot1(addr1, "853", addr1, "53"); test::DnsTlsFrontend dot2(addr2, "853", addr2, "53"); dns1.addMapping(kHelloExampleCom, ns_type::ns_t_aaaa, kHelloExampleComAddrV6); dns2.addMapping(kHelloExampleCom, ns_type::ns_t_aaaa, kHelloExampleComAddrV6); ASSERT_TRUE(dns1.startServer()); ASSERT_TRUE(dns2.startServer()); ASSERT_TRUE(dot1.startServer()); ASSERT_TRUE(dot2.startServer()); ScopedSystemProperties sp1(kDotConnectTimeoutMsFlag, std::to_string(DOT_CONNECT_TIMEOUT_MS)); ScopedSystemProperties sp2(kDotXportUnusableThresholdFlag, std::to_string(config.dotXportUnusableThreshold)); ScopedSystemProperties sp3(kDotQuickFallbackFlag, "1"); ScopedSystemProperties sp4(kDotRevalidationThresholdFlag, "-1"); resetNetwork(); // Private DNS opportunistic mode. auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); parcel.servers = {addr1, addr2}; parcel.tlsServers = {addr1, addr2}; ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); EXPECT_TRUE(WaitForPrivateDnsValidation(dot1.listen_address(), true)); EXPECT_TRUE(WaitForPrivateDnsValidation(dot2.listen_address(), true)); EXPECT_TRUE(dot1.waitForQueries(1)); EXPECT_TRUE(dot2.waitForQueries(1)); dot1.clearQueries(); dot2.clearQueries(); dot1.clearConnectionsCount(); dot2.clearConnectionsCount(); // Set the DoT servers as unresponsive to connection handshake. dot1.setHangOnHandshakeForTesting(true); dot2.setHangOnHandshakeForTesting(true); // Send sequential queries for (int i = 0; i < config.queries; i++) { int fd = resNetworkQuery(TEST_NETID, kHelloExampleCom, ns_c_in, ns_t_aaaa, ANDROID_RESOLV_NO_CACHE_LOOKUP); expectAnswersValid(fd, AF_INET6, kHelloExampleComAddrV6); } EXPECT_EQ(dot1.acceptConnectionsCount(), config.expectedQueriesSentToDot1); EXPECT_EQ(dot2.acceptConnectionsCount(), config.expectedQueriesSentToDot2); } } Loading @@ -4814,7 +4909,7 @@ TEST_F(ResolverTest, TlsServerRevalidation) { } testConfigs[] = { // clang-format off {"OPPORTUNISTIC", -1, 5, false, false}, {"OPPORTUNISTIC", -1, 10, false, false}, {"OPPORTUNISTIC", -1, 10, false, true}, {"OPPORTUNISTIC", 5, 5, true, false}, {"OPPORTUNISTIC", 5, 10, true, true}, {"STRICT", -1, 5, false, false}, Loading Loading @@ -5061,6 +5156,15 @@ TEST_F(ResolverTest, DotQuickFallback) { std::to_string(DOT_CONNECT_TIMEOUT_MS)); ScopedSystemProperties sp2(kDotQuickFallbackFlag, std::to_string(config.dotQuickFallbackFlag)); // Disable revalidation because we are reusing the same IP address of DoT servers. ScopedSystemProperties sp3(kDotRevalidationThresholdFlag, "-1"); // TODO: Remove the flags and fix the test. ScopedSystemProperties sp4(kDotAsyncHandshakeFlag, "0"); ScopedSystemProperties sp5(kDotMaxretriesFlag, "3"); resetNetwork(); resetNetwork(); auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); Loading @@ -5087,7 +5191,7 @@ TEST_F(ResolverTest, DotQuickFallback) { EXPECT_EQ(dot1.acceptConnectionsCount(), 1); EXPECT_EQ(dot2.acceptConnectionsCount(), canQuickFallback ? 0 : 1); EXPECT_EQ(dot2.queries(), canQuickFallback ? 0 : 1); EXPECT_TRUE(dot2.waitForQueries(canQuickFallback ? 0 : 1)); dot1.setHangOnHandshakeForTesting(false); } Loading tests/resolv_private_dns_test.cpp +17 −0 Original line number Diff line number Diff line Loading @@ -60,6 +60,8 @@ const std::string kDohProbeTimeoutFlag("persist.device_config.netd_native.doh_pr const std::string kDohIdleTimeoutFlag("persist.device_config.netd_native.doh_idle_timeout_ms"); const std::string kDohSessionResumptionFlag( "persist.device_config.netd_native.doh_session_resumption"); const std::string kDotAsyncHandshakeFlag("persist.device_config.netd_native.dot_async_handshake"); const std::string kDotMaxretriesFlag("persist.device_config.netd_native.dot_maxtries"); constexpr int MAXPACKET = (8 * 1024); Loading Loading @@ -403,6 +405,11 @@ INSTANTIATE_TEST_SUITE_P(PrivateDns, TransportParameterizedTest, }); TEST_P(TransportParameterizedTest, GetAddrInfo) { // TODO: Remove the flags and fix the test. ScopedSystemProperties sp1(kDotAsyncHandshakeFlag, "0"); ScopedSystemProperties sp2(kDotMaxretriesFlag, "3"); resetNetwork(); const auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); Loading Loading @@ -445,6 +452,11 @@ TEST_P(TransportParameterizedTest, GetAddrInfo) { } TEST_P(TransportParameterizedTest, MdnsGetAddrInfo_fallback) { // TODO: Remove the flags and fix the test. ScopedSystemProperties sp1(kDotAsyncHandshakeFlag, "0"); ScopedSystemProperties sp2(kDotMaxretriesFlag, "3"); resetNetwork(); constexpr char host_name[] = "hello.local."; test::DNSResponder mdnsv4("127.0.0.3", test::kDefaultMdnsListenService, static_cast<ns_rcode>(-1)); Loading Loading @@ -570,6 +582,11 @@ TEST_F(PrivateDnsDohTest, ValidationFail) { // - Fallback to UDP if DoH query times out // - Fallback to DoT if DoH validation is in progress or has failed. TEST_F(PrivateDnsDohTest, QueryFailover) { // TODO: Remove the flags and fix the test. ScopedSystemProperties sp1(kDotAsyncHandshakeFlag, "0"); ScopedSystemProperties sp2(kDotMaxretriesFlag, "3"); resetNetwork(); const auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); EXPECT_TRUE(WaitForDohValidation(test::kDefaultListenAddr, true)); Loading Loading
DnsTlsDispatcher.cpp +34 −29 Original line number Diff line number Diff line Loading @@ -67,8 +67,7 @@ std::list<DnsTlsServer> DnsTlsDispatcher::getOrderedAndUsableServerList( for (const auto& tlsServer : tlsServers) { const Key key = std::make_pair(mark, tlsServer); if (const Transport* xport = getTransport(key); xport != nullptr) { // DoT revalidation specific feature. if (Transport* xport = getTransport(key); xport != nullptr) { if (!xport->usable()) { // Don't use this xport. It will be removed after timeout // (IDLE_TIMEOUT minutes). Loading Loading @@ -112,7 +111,13 @@ DnsTlsTransport::Response DnsTlsDispatcher::query(const std::list<DnsTlsServer>& const std::list<DnsTlsServer> servers( getOrderedAndUsableServerList(tlsServers, statp->netid, statp->mark)); if (servers.empty()) LOG(WARNING) << "No usable DnsTlsServers"; if (servers.empty()) { LOG(WARNING) << "No usable DnsTlsServers"; // Call cleanup so the expired Transports can be removed as expected. std::lock_guard guard(sLock); cleanup(std::chrono::steady_clock::now()); } DnsTlsTransport::Response code = DnsTlsTransport::Response::internal_error; int serverCount = 0; Loading Loading @@ -209,9 +214,14 @@ DnsTlsTransport::Response DnsTlsDispatcher::query(const DnsTlsServer& server, un std::lock_guard guard(sLock); --xport->useCount; xport->lastUsed = now; if (code == DnsTlsTransport::Response::network_error) { xport->continuousfailureCount++; } else { xport->continuousfailureCount = 0; } // DoT revalidation specific feature. if (xport->checkRevalidationNecessary(code)) { if (xport->checkRevalidationNecessary()) { // Even if the revalidation passes, it doesn't guarantee that DoT queries // to the xport can stop failing because revalidation creates a new connection // to probe while the xport still uses an existing connection. So far, there isn't Loading Loading @@ -308,12 +318,11 @@ DnsTlsDispatcher::Transport* DnsTlsDispatcher::addTransport(const DnsTlsServer& int queryTimeout = instance->getFlag("dot_query_timeout_ms", Transport::kDotQueryTimeoutMs); // Check and adjust the parameters if they are improperly set. bool revalidationEnabled = false; const bool isForOpportunisticMode = server.name.empty(); if (triggerThr > 0 && unusableThr > 0 && isForOpportunisticMode) { revalidationEnabled = true; } else { if (triggerThr <= 0 || !isForOpportunisticMode) { triggerThr = -1; } if (unusableThr <= 0 || !isForOpportunisticMode) { unusableThr = -1; } if (queryTimeout < 0) { Loading @@ -322,9 +331,8 @@ DnsTlsDispatcher::Transport* DnsTlsDispatcher::addTransport(const DnsTlsServer& queryTimeout = 1000; } ret = new Transport(server, mark, netId, mFactory.get(), revalidationEnabled, triggerThr, unusableThr, queryTimeout); LOG(DEBUG) << "Transport is initialized with { " << triggerThr << ", " << unusableThr << ", " ret = new Transport(server, mark, netId, mFactory.get(), triggerThr, unusableThr, queryTimeout); LOG(INFO) << "Transport is initialized with { " << triggerThr << ", " << unusableThr << ", " << queryTimeout << "ms }" << " for server { " << server.toIpString() << "/" << server.name << " }"; Loading @@ -338,26 +346,23 @@ DnsTlsDispatcher::Transport* DnsTlsDispatcher::getTransport(const Key& key) { return (it == mStore.end() ? nullptr : it->second.get()); } bool DnsTlsDispatcher::Transport::checkRevalidationNecessary(DnsTlsTransport::Response code) { if (!revalidationEnabled) return false; bool DnsTlsDispatcher::Transport::checkRevalidationNecessary() { if (triggerThreshold <= 0) return false; if (continuousfailureCount < triggerThreshold) return false; if (isRevalidationThresholdReached) return false; if (code == DnsTlsTransport::Response::network_error) { continuousfailureCount++; } else { continuousfailureCount = 0; } // triggerThreshold must be greater than 0 because the value of revalidationEnabled is true. if (usable() && continuousfailureCount == triggerThreshold) { isRevalidationThresholdReached = true; return true; } return false; } bool DnsTlsDispatcher::Transport::usable() const { if (!revalidationEnabled) return true; bool DnsTlsDispatcher::Transport::usable() { if (unusableThreshold <= 0) return true; return continuousfailureCount < unusableThreshold; if (continuousfailureCount >= unusableThreshold) { // Once reach the threshold, mark this Transport as unusable. isXportUnusableThresholdReached = true; } return !isXportUnusableThresholdReached; } } // end of namespace net Loading
DnsTlsDispatcher.h +20 −19 Original line number Diff line number Diff line Loading @@ -83,11 +83,10 @@ class DnsTlsDispatcher : public PrivateDnsValidationObserver { // usage monitoring so we can expire idle sessions from the cache. struct Transport { Transport(const DnsTlsServer& server, unsigned mark, unsigned netId, IDnsTlsSocketFactory* _Nonnull factory, bool revalidationEnabled, int triggerThr, int unusableThr, int timeout) IDnsTlsSocketFactory* _Nonnull factory, int triggerThr, int unusableThr, int timeout) : transport(server, mark, factory), mNetId(netId), revalidationEnabled(revalidationEnabled), triggerThreshold(triggerThr), unusableThreshold(unusableThr), mTimeout(timeout) {} Loading @@ -106,9 +105,12 @@ class DnsTlsDispatcher : public PrivateDnsValidationObserver { // If DoT revalidation is disabled, it returns true; otherwise, it returns // whether or not this Transport is usable. bool usable() const REQUIRES(sLock); bool usable() REQUIRES(sLock); bool checkRevalidationNecessary(DnsTlsTransport::Response code) REQUIRES(sLock); // Used to track if this Transport is usable. int continuousfailureCount GUARDED_BY(sLock) = 0; bool checkRevalidationNecessary() REQUIRES(sLock); std::chrono::milliseconds timeout() const { return mTimeout; } Loading @@ -117,25 +119,24 @@ class DnsTlsDispatcher : public PrivateDnsValidationObserver { static constexpr int kDotQueryTimeoutMs = -1; private: // Used to track if this Transport is usable. int continuousfailureCount GUARDED_BY(sLock) = 0; // The flag to record whether or not dot_revalidation_threshold is ever reached. bool isRevalidationThresholdReached GUARDED_BY(sLock) = false; // Used to indicate whether DoT revalidation is enabled for this Transport. // The value is set to true only if: // 1. both triggerThreshold and unusableThreshold are positive values. // 2. private DNS mode is opportunistic. const bool revalidationEnabled; // The flag to record whether or not dot_xport_unusable_threshold is ever reached. bool isXportUnusableThresholdReached GUARDED_BY(sLock) = false; // The number of continuous failures to trigger a validation. It takes effect when DoT // revalidation is on. If the value is not a positive value, DoT revalidation is disabled. // Note that it must be at least 10, or it breaks ConnectTlsServerTimeout_ConcurrentQueries // test. // If the number of continuous query timeouts reaches the threshold, mark the // server as unvalidated and trigger a validation. // If the value is not a positive value or private DNS mode is strict mode, no threshold is // set. Note that it must be at least 10, or it breaks // ConnectTlsServerTimeout_ConcurrentQueries test. const int triggerThreshold; // The threshold to determine if this Transport is considered unusable. // If continuousfailureCount reaches this value, this Transport is no longer used. It // takes effect when DoT revalidation is on. If the value is not a positive value, DoT // revalidation is disabled. // If the number of continuous query timeouts reaches the threshold, mark this // Transport as unusable. An unusable Transport won't be used anymore. // If the value is not a positive value or private DNS mode is strict mode, no threshold is // set. const int unusableThreshold; // The time to await a future (the result of a DNS request) from the DnsTlsTransport Loading
tests/resolv_integration_test.cpp +107 −3 Original line number Diff line number Diff line Loading @@ -2907,6 +2907,11 @@ TEST_F(ResolverTest, BrokenEdns) { const std::string testHostName = config.asHostName(); SCOPED_TRACE(testHostName); // Don't skip unusable DoT servers and disable revalidation for this test. ScopedSystemProperties sp1(kDotXportUnusableThresholdFlag, "-1"); ScopedSystemProperties sp2(kDotRevalidationThresholdFlag, "-1"); resetNetwork(); const char* host_name = testHostName.c_str(); dns.addMapping(host_name, ns_type::ns_t_a, ADDR4); dns.setEdns(config.edns); Loading Loading @@ -4572,6 +4577,10 @@ TEST_F(ResolverTest, ConnectTlsServerTimeout) { ScopedSystemProperties sp3(kDotAsyncHandshakeFlag, config.asyncHandshake ? "1" : "0"); ScopedSystemProperties sp4(kDotMaxretriesFlag, std::to_string(config.maxRetries)); // Don't skip unusable DoT servers and disable revalidation for this test. ScopedSystemProperties sp5(kDotXportUnusableThresholdFlag, "-1"); ScopedSystemProperties sp6(kDotRevalidationThresholdFlag, "-1"); resetNetwork(); // Set up resolver to opportunistic mode. Loading Loading @@ -4662,6 +4671,10 @@ TEST_F(ResolverTest, ConnectTlsServerTimeout_ConcurrentQueries) { std::to_string(config.dotConnectTimeoutMs)); ScopedSystemProperties sp3(kDotAsyncHandshakeFlag, config.asyncHandshake ? "1" : "0"); ScopedSystemProperties sp4(kDotMaxretriesFlag, std::to_string(config.maxRetries)); // Don't skip unusable DoT servers and disable revalidation for this test. ScopedSystemProperties sp5(kDotXportUnusableThresholdFlag, "-1"); ScopedSystemProperties sp6(kDotRevalidationThresholdFlag, "-1"); resetNetwork(); for (const auto& dnsMode : {"OPPORTUNISTIC", "STRICT"}) { Loading Loading @@ -4744,6 +4757,10 @@ TEST_F(ResolverTest, QueryTlsServerTimeout) { ASSERT_TRUE(tls.startServer()); ScopedSystemProperties sp(kDotQueryTimeoutMsFlag, std::to_string(queryTimeoutMs)); // Don't skip unusable DoT servers and disable revalidation for this test. ScopedSystemProperties sp2(kDotXportUnusableThresholdFlag, "-1"); ScopedSystemProperties sp3(kDotRevalidationThresholdFlag, "-1"); resetNetwork(); auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); Loading Loading @@ -4787,8 +4804,86 @@ TEST_F(ResolverTest, QueryTlsServerTimeout) { // Also check how much time the resolver processed the query. timeTakenMs = s.timeTakenUs() / 1000; EXPECT_LE(timeTakenMs, 500); EXPECT_EQ(2, tls.queries()); EXPECT_TRUE(tls.waitForQueries(2)); } } } // Tests that the DnsResolver can skip using unusable DoT servers if dot_xport_unusable_threshold // flag is set. In this test, we make test DoT servers unresponsive during connection handshake, // so the DnsResolver will skip using a DoT server if the number of timed out queries reaches // the threshold. TEST_F(ResolverTest, SkipUnusableTlsServer) { constexpr int DOT_CONNECT_TIMEOUT_MS = 1000; static const struct TestConfig { int dotXportUnusableThreshold; int queries; int expectedQueriesSentToDot1; int expectedQueriesSentToDot2; } testConfigs[] = { // clang-format off // expectedQueriesSentToDot2 is 0 because dot_quick_fallback flag is set. {-1, 3, 3, 0}, { 1, 3, 1, 1}, { 3, 10, 3, 3}, // clang-format on }; for (const auto& config : testConfigs) { SCOPED_TRACE(fmt::format("testConfig: [{}, {}, {}, {}]", config.dotXportUnusableThreshold, config.queries, config.expectedQueriesSentToDot1, config.expectedQueriesSentToDot2)); const std::string addr1 = getUniqueIPv4Address(); const std::string addr2 = getUniqueIPv4Address(); test::DNSResponder dns1(addr1); test::DNSResponder dns2(addr2); test::DnsTlsFrontend dot1(addr1, "853", addr1, "53"); test::DnsTlsFrontend dot2(addr2, "853", addr2, "53"); dns1.addMapping(kHelloExampleCom, ns_type::ns_t_aaaa, kHelloExampleComAddrV6); dns2.addMapping(kHelloExampleCom, ns_type::ns_t_aaaa, kHelloExampleComAddrV6); ASSERT_TRUE(dns1.startServer()); ASSERT_TRUE(dns2.startServer()); ASSERT_TRUE(dot1.startServer()); ASSERT_TRUE(dot2.startServer()); ScopedSystemProperties sp1(kDotConnectTimeoutMsFlag, std::to_string(DOT_CONNECT_TIMEOUT_MS)); ScopedSystemProperties sp2(kDotXportUnusableThresholdFlag, std::to_string(config.dotXportUnusableThreshold)); ScopedSystemProperties sp3(kDotQuickFallbackFlag, "1"); ScopedSystemProperties sp4(kDotRevalidationThresholdFlag, "-1"); resetNetwork(); // Private DNS opportunistic mode. auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); parcel.servers = {addr1, addr2}; parcel.tlsServers = {addr1, addr2}; ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); EXPECT_TRUE(WaitForPrivateDnsValidation(dot1.listen_address(), true)); EXPECT_TRUE(WaitForPrivateDnsValidation(dot2.listen_address(), true)); EXPECT_TRUE(dot1.waitForQueries(1)); EXPECT_TRUE(dot2.waitForQueries(1)); dot1.clearQueries(); dot2.clearQueries(); dot1.clearConnectionsCount(); dot2.clearConnectionsCount(); // Set the DoT servers as unresponsive to connection handshake. dot1.setHangOnHandshakeForTesting(true); dot2.setHangOnHandshakeForTesting(true); // Send sequential queries for (int i = 0; i < config.queries; i++) { int fd = resNetworkQuery(TEST_NETID, kHelloExampleCom, ns_c_in, ns_t_aaaa, ANDROID_RESOLV_NO_CACHE_LOOKUP); expectAnswersValid(fd, AF_INET6, kHelloExampleComAddrV6); } EXPECT_EQ(dot1.acceptConnectionsCount(), config.expectedQueriesSentToDot1); EXPECT_EQ(dot2.acceptConnectionsCount(), config.expectedQueriesSentToDot2); } } Loading @@ -4814,7 +4909,7 @@ TEST_F(ResolverTest, TlsServerRevalidation) { } testConfigs[] = { // clang-format off {"OPPORTUNISTIC", -1, 5, false, false}, {"OPPORTUNISTIC", -1, 10, false, false}, {"OPPORTUNISTIC", -1, 10, false, true}, {"OPPORTUNISTIC", 5, 5, true, false}, {"OPPORTUNISTIC", 5, 10, true, true}, {"STRICT", -1, 5, false, false}, Loading Loading @@ -5061,6 +5156,15 @@ TEST_F(ResolverTest, DotQuickFallback) { std::to_string(DOT_CONNECT_TIMEOUT_MS)); ScopedSystemProperties sp2(kDotQuickFallbackFlag, std::to_string(config.dotQuickFallbackFlag)); // Disable revalidation because we are reusing the same IP address of DoT servers. ScopedSystemProperties sp3(kDotRevalidationThresholdFlag, "-1"); // TODO: Remove the flags and fix the test. ScopedSystemProperties sp4(kDotAsyncHandshakeFlag, "0"); ScopedSystemProperties sp5(kDotMaxretriesFlag, "3"); resetNetwork(); resetNetwork(); auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); Loading @@ -5087,7 +5191,7 @@ TEST_F(ResolverTest, DotQuickFallback) { EXPECT_EQ(dot1.acceptConnectionsCount(), 1); EXPECT_EQ(dot2.acceptConnectionsCount(), canQuickFallback ? 0 : 1); EXPECT_EQ(dot2.queries(), canQuickFallback ? 0 : 1); EXPECT_TRUE(dot2.waitForQueries(canQuickFallback ? 0 : 1)); dot1.setHangOnHandshakeForTesting(false); } Loading
tests/resolv_private_dns_test.cpp +17 −0 Original line number Diff line number Diff line Loading @@ -60,6 +60,8 @@ const std::string kDohProbeTimeoutFlag("persist.device_config.netd_native.doh_pr const std::string kDohIdleTimeoutFlag("persist.device_config.netd_native.doh_idle_timeout_ms"); const std::string kDohSessionResumptionFlag( "persist.device_config.netd_native.doh_session_resumption"); const std::string kDotAsyncHandshakeFlag("persist.device_config.netd_native.dot_async_handshake"); const std::string kDotMaxretriesFlag("persist.device_config.netd_native.dot_maxtries"); constexpr int MAXPACKET = (8 * 1024); Loading Loading @@ -403,6 +405,11 @@ INSTANTIATE_TEST_SUITE_P(PrivateDns, TransportParameterizedTest, }); TEST_P(TransportParameterizedTest, GetAddrInfo) { // TODO: Remove the flags and fix the test. ScopedSystemProperties sp1(kDotAsyncHandshakeFlag, "0"); ScopedSystemProperties sp2(kDotMaxretriesFlag, "3"); resetNetwork(); const auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); Loading Loading @@ -445,6 +452,11 @@ TEST_P(TransportParameterizedTest, GetAddrInfo) { } TEST_P(TransportParameterizedTest, MdnsGetAddrInfo_fallback) { // TODO: Remove the flags and fix the test. ScopedSystemProperties sp1(kDotAsyncHandshakeFlag, "0"); ScopedSystemProperties sp2(kDotMaxretriesFlag, "3"); resetNetwork(); constexpr char host_name[] = "hello.local."; test::DNSResponder mdnsv4("127.0.0.3", test::kDefaultMdnsListenService, static_cast<ns_rcode>(-1)); Loading Loading @@ -570,6 +582,11 @@ TEST_F(PrivateDnsDohTest, ValidationFail) { // - Fallback to UDP if DoH query times out // - Fallback to DoT if DoH validation is in progress or has failed. TEST_F(PrivateDnsDohTest, QueryFailover) { // TODO: Remove the flags and fix the test. ScopedSystemProperties sp1(kDotAsyncHandshakeFlag, "0"); ScopedSystemProperties sp2(kDotMaxretriesFlag, "3"); resetNetwork(); const auto parcel = DnsResponderClient::GetDefaultResolverParamsParcel(); ASSERT_TRUE(mDnsClient.SetResolversFromParcel(parcel)); EXPECT_TRUE(WaitForDohValidation(test::kDefaultListenAddr, true)); Loading