Loading DnsProxyListener.cpp +25 −123 Original line number Original line Diff line number Diff line Loading @@ -469,7 +469,7 @@ void logDnsQueryResult(const addrinfo* res) { NI_NUMERICHOST); NI_NUMERICHOST); if (!ret) { if (!ret) { LOG(DEBUG) << __func__ << ": [" << i << "] " << ai->ai_flags << " " << ai->ai_family LOG(DEBUG) << __func__ << ": [" << i << "] " << ai->ai_flags << " " << ai->ai_family << " " << ai->ai_socktype << " " << ai->ai_protocol << " " << ip_addr; << " " << ai->ai_socktype << " " << ai->ai_protocol; } else { } else { LOG(DEBUG) << __func__ << ": [" << i << "] numeric hostname translation fail " << ret; LOG(DEBUG) << __func__ << ": [" << i << "] numeric hostname translation fail " << ret; } } Loading Loading @@ -523,96 +523,30 @@ bool synthesizeNat64PrefixWithARecord(const netdutils::IPPrefix& prefix, struct return true; return true; } } bool synthesizeNat64PrefixWithARecord(const netdutils::IPPrefix& prefix, addrinfo** res, bool synthesizeNat64PrefixWithARecord(const netdutils::IPPrefix& prefix, addrinfo* result) { bool unspecWantedButNoIPv6, if (result == nullptr) return false; const android_net_context* netcontext) { if (!onlyNonSpecialUseIPv4Addresses(result)) return false; if (*res == nullptr) return false; if (!onlyNonSpecialUseIPv4Addresses(*res)) return false; if (!isValidNat64Prefix(prefix)) return false; if (!isValidNat64Prefix(prefix)) return false; const sockaddr_storage ss = netdutils::IPSockAddr(prefix.ip()); struct sockaddr_storage ss = netdutils::IPSockAddr(prefix.ip()); const sockaddr_in6* v6prefix = (sockaddr_in6*)&ss; struct sockaddr_in6* v6prefix = (struct sockaddr_in6*)&ss; addrinfo* const head4 = *res; for (addrinfo* ai = result; ai; ai = ai->ai_next) { addrinfo* head6 = nullptr; struct sockaddr_in sinOriginal = *(struct sockaddr_in*)ai->ai_addr; addrinfo* cur6 = nullptr; struct sockaddr_in6* sin6 = (struct sockaddr_in6*)ai->ai_addr; memset(sin6, 0, sizeof(sockaddr_in6)); // Build a synthesized AAAA addrinfo list from the queried A addrinfo list. Here is the diagram // for the relationship of pointers. // // head4: point to the first queried A addrinfo // | // v // +-------------+ +-------------+ // | addrinfo4#1 |-->| addrinfo4#2 |--> .. queried A addrinfo(s) for DNS64 synthesis // +-------------+ +-------------+ // ^ // | // cur4: current worked-on queried A addrinfo // // head6: point to the first synthesized AAAA addrinfo // | // v // +-------------+ +-------------+ // | addrinfo6#1 |-->| addrinfo6#2 |--> .. synthesized DNS64 AAAA addrinfo(s) // +-------------+ +-------------+ // ^ // | // cur6: current worked-on synthesized addrinfo // for (const addrinfo* cur4 = head4; cur4; cur4 = cur4->ai_next) { // Allocate a space for a synthesized AAAA addrinfo. Note that the addrinfo and sockaddr // occupy one contiguous block of memory and are allocated and freed as a single block. // See get_ai and freeaddrinfo in packages/modules/DnsResolver/getaddrinfo.cpp. addrinfo* sa = (addrinfo*)calloc(1, sizeof(addrinfo) + sizeof(sockaddr_in6)); if (sa == nullptr) { LOG(ERROR) << "allocate memory failed for synthesized result"; freeaddrinfo(head6); return false; } // Initialize the synthesized AAAA addrinfo by the queried A addrinfo. The ai_addr will be // set lately. sa->ai_flags = cur4->ai_flags; sa->ai_family = AF_INET6; sa->ai_socktype = cur4->ai_socktype; sa->ai_protocol = cur4->ai_protocol; sa->ai_addrlen = sizeof(sockaddr_in6); sa->ai_addr = (sockaddr*)(sa + 1); sa->ai_canonname = nullptr; sa->ai_next = nullptr; if (cur4->ai_canonname != nullptr) { sa->ai_canonname = strdup(cur4->ai_canonname); if (sa->ai_canonname == nullptr) { LOG(ERROR) << "allocate memory failed for canonname"; freeaddrinfo(sa); freeaddrinfo(head6); return false; } } // Synthesize /96 NAT64 prefix with the queried IPv4 address. // Synthesize /96 NAT64 prefix in place. The space has reserved by get_ai() in const sockaddr_in* sin4 = (sockaddr_in*)cur4->ai_addr; // system/netd/resolv/getaddrinfo.cpp. sockaddr_in6* sin6 = (sockaddr_in6*)sa->ai_addr; sin6->sin6_addr = v6prefix->sin6_addr; sin6->sin6_addr = v6prefix->sin6_addr; sin6->sin6_addr.s6_addr32[3] = sin4->sin_addr.s_addr; sin6->sin6_addr.s6_addr32[3] = sinOriginal.sin_addr.s_addr; sin6->sin6_family = AF_INET6; sin6->sin6_family = AF_INET6; sin6->sin6_port = sin4->sin_port; sin6->sin6_port = sinOriginal.sin_port; ai->ai_addrlen = sizeof(struct sockaddr_in6); // If the synthesized list is empty, this becomes the first element. ai->ai_family = AF_INET6; if (head6 == nullptr) { head6 = sa; } // Add this element to the end of the synthesized list. if (cur6 != nullptr) { cur6->ai_next = sa; } cur6 = sa; if (WOULD_LOG(VERBOSE)) { if (WOULD_LOG(VERBOSE)) { char buf[INET6_ADDRSTRLEN]; // big enough for either IPv4 or IPv6 char buf[INET6_ADDRSTRLEN]; // big enough for either IPv4 or IPv6 inet_ntop(AF_INET, &sin4->sin_addr.s_addr, buf, sizeof(buf)); inet_ntop(AF_INET, &sinOriginal.sin_addr.s_addr, buf, sizeof(buf)); LOG(VERBOSE) << __func__ << ": DNS A record: " << buf; LOG(VERBOSE) << __func__ << ": DNS A record: " << buf; inet_ntop(AF_INET6, &v6prefix->sin6_addr, buf, sizeof(buf)); inet_ntop(AF_INET6, &v6prefix->sin6_addr, buf, sizeof(buf)); LOG(VERBOSE) << __func__ << ": NAT64 prefix: " << buf; LOG(VERBOSE) << __func__ << ": NAT64 prefix: " << buf; Loading @@ -620,39 +554,7 @@ bool synthesizeNat64PrefixWithARecord(const netdutils::IPPrefix& prefix, addrinf LOG(VERBOSE) << __func__ << ": DNS64 Synthesized AAAA record: " << buf; LOG(VERBOSE) << __func__ << ": DNS64 Synthesized AAAA record: " << buf; } } } } logDnsQueryResult(result); // Simply concatenate the synthesized AAAA addrinfo list and the queried A addrinfo list when // AF_UNSPEC is specified. In the other words, the IPv6 addresses are listed first and then // IPv4 addresses. For example: // 64:ff9b::102:304 (socktype=2, protocol=17) -> // 64:ff9b::102:304 (socktype=1, protocol=6) -> // 1.2.3.4 (socktype=2, protocol=17) -> // 1.2.3.4 (socktype=1, protocol=6) // Note that head6 and cur6 should be non-null because there was at least one IPv4 address // synthesized. From the above example, the synthesized addrinfo list puts IPv6 and IPv4 in // groups and sort by RFC 6724 later. This ordering is different from no synthesized case // because resolv_getaddrinfo() sorts results in explore_options. resolv_getaddrinfo() calls // explore_fqdn() many times by the different items of explore_options. It means that // resolv_rfc6724_sort() only sorts the results in each explore_options and concatenates each // results into one. For example, getaddrinfo() is called with null hints for a domain name // which has both IPv4 and IPv6 addresses. The address order of the result addrinfo may be: // 2001:db8::102:304 (socktype=2, protocol=17) -> 1.2.3.4 (socktype=2, protocol=17) -> // 2001:db8::102:304 (socktype=1, protocol=6) -> 1.2.3.4 (socktype=1, protocol=6) // In above example, the first two results come from one explore option and the last two come // from another one. They are sorted first, and then concatenate together to be the result. // See also resolv_getaddrinfo in packages/modules/DnsResolver/getaddrinfo.cpp. if (unspecWantedButNoIPv6) { cur6->ai_next = head4; } else { freeaddrinfo(head4); } // Sort the concatenated addresses by RFC 6724 section 2.1. struct addrinfo sorting_head = {.ai_next = head6}; resolv_rfc6724_sort(&sorting_head, netcontext->app_mark, netcontext->uid); *res = sorting_head.ai_next; logDnsQueryResult(*res); return true; return true; } } Loading Loading @@ -811,7 +713,7 @@ void DnsProxyListener::GetAddrInfoHandler::doDns64Synthesis(int32_t* rv, addrinf } } } } if (!synthesizeNat64PrefixWithARecord(prefix, res, unspecWantedButNoIPv6, &mNetContext)) { if (!synthesizeNat64PrefixWithARecord(prefix, *res)) { if (ipv6WantedButNoData) { if (ipv6WantedButNoData) { // If caller wants IPv6 answers but no data and failed to synthesize IPv6 answers, // If caller wants IPv6 answers but no data and failed to synthesize IPv6 answers, // don't return the IPv4 answers. // don't return the IPv4 answers. Loading @@ -825,9 +727,9 @@ void DnsProxyListener::GetAddrInfoHandler::doDns64Synthesis(int32_t* rv, addrinf } } void DnsProxyListener::GetAddrInfoHandler::run() { void DnsProxyListener::GetAddrInfoHandler::run() { LOG(INFO) << "GetAddrInfoHandler::run: {" << mNetContext.app_netid << " " LOG(DEBUG) << "GetAddrInfoHandler::run: {" << mNetContext.app_netid << " " << mNetContext.app_mark << " " << mNetContext.dns_netid << " " << mNetContext.dns_mark << mNetContext.app_mark << " " << mNetContext.dns_netid << " " << " " << mNetContext.uid << " " << mNetContext.flags << "}"; << mNetContext.dns_mark << " " << mNetContext.uid << " " << mNetContext.flags << "}"; addrinfo* result = nullptr; addrinfo* result = nullptr; Stopwatch s; Stopwatch s; Loading Loading @@ -1000,9 +902,9 @@ DnsProxyListener::ResNSendHandler::ResNSendHandler(SocketClient* c, std::string : Handler(c), mMsg(std::move(msg)), mFlags(flags), mNetContext(netcontext) {} : Handler(c), mMsg(std::move(msg)), mFlags(flags), mNetContext(netcontext) {} void DnsProxyListener::ResNSendHandler::run() { void DnsProxyListener::ResNSendHandler::run() { LOG(INFO) << "ResNSendHandler::run: " << mFlags << " / {" << mNetContext.app_netid << " " LOG(DEBUG) << "ResNSendHandler::run: " << mFlags << " / {" << mNetContext.app_netid << " " << mNetContext.app_mark << " " << mNetContext.dns_netid << " " << mNetContext.dns_mark << mNetContext.app_mark << " " << mNetContext.dns_netid << " " << " " << mNetContext.uid << " " << mNetContext.flags << "}"; << mNetContext.dns_mark << " " << mNetContext.uid << " " << mNetContext.flags << "}"; Stopwatch s; Stopwatch s; maybeFixupNetContext(&mNetContext, mClient->getPid()); maybeFixupNetContext(&mNetContext, mClient->getPid()); Loading DnsResolver.cpp +2 −3 Original line number Original line Diff line number Diff line Loading @@ -31,9 +31,8 @@ bool resolv_init(const ResolverNetdCallbacks* callbacks) { bool resolv_init(const ResolverNetdCallbacks* callbacks) { android::base::InitLogging(/*argv=*/nullptr); android::base::InitLogging(/*argv=*/nullptr); LOG(INFO) << __func__ << ": Initializing resolver"; LOG(INFO) << __func__ << ": Initializing resolver"; const bool isDebug = isUserDebugBuild(); resolv_set_log_severity(android::base::WARNING); resolv_set_log_severity(isDebug ? android::base::INFO : android::base::WARNING); doh_init_logger(DOH_LOG_LEVEL_WARN); doh_init_logger(isDebug ? DOH_LOG_LEVEL_INFO : DOH_LOG_LEVEL_WARN); using android::net::gApiLevel; using android::net::gApiLevel; gApiLevel = getApiLevel(); gApiLevel = getApiLevel(); using android::net::gResNetdCallbacks; using android::net::gResNetdCallbacks; Loading DnsTlsDispatcher.cpp +50 −68 Original line number Original line Diff line number Diff line Loading @@ -26,7 +26,6 @@ #include "resolv_cache.h" #include "resolv_cache.h" #include "resolv_private.h" #include "resolv_private.h" #include "stats.pb.h" #include "stats.pb.h" #include "util.h" #include <android-base/logging.h> #include <android-base/logging.h> Loading Loading @@ -68,7 +67,8 @@ std::list<DnsTlsServer> DnsTlsDispatcher::getOrderedAndUsableServerList( for (const auto& tlsServer : tlsServers) { for (const auto& tlsServer : tlsServers) { const Key key = std::make_pair(mark, tlsServer); const Key key = std::make_pair(mark, tlsServer); if (Transport* xport = getTransport(key); xport != nullptr) { if (const Transport* xport = getTransport(key); xport != nullptr) { // DoT revalidation specific feature. if (!xport->usable()) { if (!xport->usable()) { // Don't use this xport. It will be removed after timeout // Don't use this xport. It will be removed after timeout // (IDLE_TIMEOUT minutes). // (IDLE_TIMEOUT minutes). Loading Loading @@ -112,13 +112,7 @@ DnsTlsTransport::Response DnsTlsDispatcher::query(const std::list<DnsTlsServer>& const std::list<DnsTlsServer> servers( const std::list<DnsTlsServer> servers( getOrderedAndUsableServerList(tlsServers, statp->netid, statp->mark)); getOrderedAndUsableServerList(tlsServers, statp->netid, statp->mark)); if (servers.empty()) { if (servers.empty()) LOG(WARNING) << "No usable DnsTlsServers"; LOG(WARNING) << "No usable DnsTlsServers"; // Call maybeCleanup so the expired Transports can be removed as expected. std::lock_guard guard(sLock); maybeCleanup(std::chrono::steady_clock::now()); } DnsTlsTransport::Response code = DnsTlsTransport::Response::internal_error; DnsTlsTransport::Response code = DnsTlsTransport::Response::internal_error; int serverCount = 0; int serverCount = 0; Loading Loading @@ -215,14 +209,9 @@ DnsTlsTransport::Response DnsTlsDispatcher::query(const DnsTlsServer& server, un std::lock_guard guard(sLock); std::lock_guard guard(sLock); --xport->useCount; --xport->useCount; xport->lastUsed = now; xport->lastUsed = now; if (code == DnsTlsTransport::Response::network_error) { xport->continuousfailureCount++; } else { xport->continuousfailureCount = 0; } // DoT revalidation specific feature. // DoT revalidation specific feature. if (xport->checkRevalidationNecessary()) { if (xport->checkRevalidationNecessary(code)) { // Even if the revalidation passes, it doesn't guarantee that DoT queries // 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 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 // to probe while the xport still uses an existing connection. So far, there isn't Loading @@ -237,14 +226,14 @@ DnsTlsTransport::Response DnsTlsDispatcher::query(const DnsTlsServer& server, un << (result.ok() ? "succeeded" : "failed: " + result.error().message()); << (result.ok() ? "succeeded" : "failed: " + result.error().message()); } } maybeCleanup(now); cleanup(now); } } return code; return code; } } void DnsTlsDispatcher::forceCleanup(unsigned netId) { void DnsTlsDispatcher::forceCleanup(unsigned netId) { std::lock_guard guard(sLock); std::lock_guard guard(sLock); cleanup(std::chrono::steady_clock::now(), std::chrono::seconds(-1), netId); forceCleanupLocked(netId); } } DnsTlsTransport::Result DnsTlsDispatcher::queryInternal(Transport& xport, DnsTlsTransport::Result DnsTlsDispatcher::queryInternal(Transport& xport, Loading Loading @@ -276,45 +265,33 @@ DnsTlsTransport::Result DnsTlsDispatcher::queryInternal(Transport& xport, // This timeout effectively controls how long to keep SSL session tickets. // This timeout effectively controls how long to keep SSL session tickets. static constexpr std::chrono::minutes IDLE_TIMEOUT(5); static constexpr std::chrono::minutes IDLE_TIMEOUT(5); void DnsTlsDispatcher::maybeCleanup(std::chrono::time_point<std::chrono::steady_clock> now) { void DnsTlsDispatcher::cleanup(std::chrono::time_point<std::chrono::steady_clock> now) { // Make the timeout tunable via experiment flag for testing. std::chrono::seconds unusable_xport_idle_timeout{-1}; const int value = Experiments::getInstance()->getFlag("dot_keep_unusable_xport_sec", -1); if (value > -1 && isUserDebugBuild() && std::chrono::seconds(value) < IDLE_TIMEOUT) { unusable_xport_idle_timeout = std::chrono::seconds(value); } // To avoid scanning mStore after every query, return early if a cleanup has been // To avoid scanning mStore after every query, return early if a cleanup has been // performed recently. // performed recently. const std::chrono::seconds timeout = (unusable_xport_idle_timeout < IDLE_TIMEOUT) if (now - mLastCleanup < IDLE_TIMEOUT) { ? unusable_xport_idle_timeout : IDLE_TIMEOUT; if (now - mLastCleanup < timeout) { return; return; } } cleanup(now, unusable_xport_idle_timeout, std::nullopt); for (auto it = mStore.begin(); it != mStore.end();) { auto& s = it->second; if (s->useCount == 0 && now - s->lastUsed > IDLE_TIMEOUT) { it = mStore.erase(it); } else { ++it; } } mLastCleanup = now; mLastCleanup = now; } } void DnsTlsDispatcher::cleanup(std::chrono::time_point<std::chrono::steady_clock> now, // TODO: unify forceCleanupLocked() and cleanup(). std::chrono::seconds unusable_xport_idle_timeout, void DnsTlsDispatcher::forceCleanupLocked(unsigned netId) { std::optional<unsigned> netId) { for (auto it = mStore.begin(); it != mStore.end();) { std::erase_if(mStore, [&](const auto& item) REQUIRES(sLock) { auto& s = it->second; auto const& [_, xport] = item; if (s->useCount == 0 && s->mNetId == netId) { if (xport->useCount == 0) { it = mStore.erase(it); // Remove the Transports of the associated network. } else { if (netId.has_value() && xport->mNetId == netId.value()) return true; ++it; } // Remove all expired Transports. if (now - xport->lastUsed > IDLE_TIMEOUT) return true; // Unusable Transports should be removed earlier. if (!xport->usable() && unusable_xport_idle_timeout.count() >= 0 && now - xport->lastUsed > unusable_xport_idle_timeout) return true; } } return false; }); } } DnsTlsDispatcher::Transport* DnsTlsDispatcher::addTransport(const DnsTlsServer& server, DnsTlsDispatcher::Transport* DnsTlsDispatcher::addTransport(const DnsTlsServer& server, Loading @@ -331,11 +308,12 @@ DnsTlsDispatcher::Transport* DnsTlsDispatcher::addTransport(const DnsTlsServer& int queryTimeout = instance->getFlag("dot_query_timeout_ms", Transport::kDotQueryTimeoutMs); int queryTimeout = instance->getFlag("dot_query_timeout_ms", Transport::kDotQueryTimeoutMs); // Check and adjust the parameters if they are improperly set. // Check and adjust the parameters if they are improperly set. bool revalidationEnabled = false; const bool isForOpportunisticMode = server.name.empty(); const bool isForOpportunisticMode = server.name.empty(); if (triggerThr <= 0 || !isForOpportunisticMode) { if (triggerThr > 0 && unusableThr > 0 && isForOpportunisticMode) { revalidationEnabled = true; } else { triggerThr = -1; triggerThr = -1; } if (unusableThr <= 0 || !isForOpportunisticMode) { unusableThr = -1; unusableThr = -1; } } if (queryTimeout < 0) { if (queryTimeout < 0) { Loading @@ -344,8 +322,9 @@ DnsTlsDispatcher::Transport* DnsTlsDispatcher::addTransport(const DnsTlsServer& queryTimeout = 1000; queryTimeout = 1000; } } ret = new Transport(server, mark, netId, mFactory.get(), triggerThr, unusableThr, queryTimeout); ret = new Transport(server, mark, netId, mFactory.get(), revalidationEnabled, triggerThr, LOG(INFO) << "Transport is initialized with { " << triggerThr << ", " << unusableThr << ", " unusableThr, queryTimeout); LOG(DEBUG) << "Transport is initialized with { " << triggerThr << ", " << unusableThr << ", " << queryTimeout << "ms }" << queryTimeout << "ms }" << " for server { " << server.toIpString() << "/" << server.name << " }"; << " for server { " << server.toIpString() << "/" << server.name << " }"; Loading @@ -359,23 +338,26 @@ DnsTlsDispatcher::Transport* DnsTlsDispatcher::getTransport(const Key& key) { return (it == mStore.end() ? nullptr : it->second.get()); return (it == mStore.end() ? nullptr : it->second.get()); } } bool DnsTlsDispatcher::Transport::checkRevalidationNecessary() { bool DnsTlsDispatcher::Transport::checkRevalidationNecessary(DnsTlsTransport::Response code) { if (triggerThreshold <= 0) return false; if (!revalidationEnabled) return false; if (continuousfailureCount < triggerThreshold) return false; if (isRevalidationThresholdReached) return false; isRevalidationThresholdReached = true; 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) { return true; return true; } } return false; } bool DnsTlsDispatcher::Transport::usable() { bool DnsTlsDispatcher::Transport::usable() const { if (unusableThreshold <= 0) return true; if (!revalidationEnabled) return true; if (continuousfailureCount >= unusableThreshold) { return continuousfailureCount < unusableThreshold; // Once reach the threshold, mark this Transport as unusable. isXportUnusableThresholdReached = true; } return !isXportUnusableThresholdReached; } } } // end of namespace net } // end of namespace net Loading DnsTlsDispatcher.h +23 −25 Original line number Original line Diff line number Diff line Loading @@ -83,10 +83,11 @@ class DnsTlsDispatcher : public PrivateDnsValidationObserver { // usage monitoring so we can expire idle sessions from the cache. // usage monitoring so we can expire idle sessions from the cache. struct Transport { struct Transport { Transport(const DnsTlsServer& server, unsigned mark, unsigned netId, Transport(const DnsTlsServer& server, unsigned mark, unsigned netId, IDnsTlsSocketFactory* _Nonnull factory, int triggerThr, int unusableThr, IDnsTlsSocketFactory* _Nonnull factory, bool revalidationEnabled, int triggerThr, int timeout) int unusableThr, int timeout) : transport(server, mark, factory), : transport(server, mark, factory), mNetId(netId), mNetId(netId), revalidationEnabled(revalidationEnabled), triggerThreshold(triggerThr), triggerThreshold(triggerThr), unusableThreshold(unusableThr), unusableThreshold(unusableThr), mTimeout(timeout) {} mTimeout(timeout) {} Loading @@ -105,12 +106,9 @@ class DnsTlsDispatcher : public PrivateDnsValidationObserver { // If DoT revalidation is disabled, it returns true; otherwise, it returns // If DoT revalidation is disabled, it returns true; otherwise, it returns // whether or not this Transport is usable. // whether or not this Transport is usable. bool usable() REQUIRES(sLock); bool usable() const REQUIRES(sLock); // Used to track if this Transport is usable. bool checkRevalidationNecessary(DnsTlsTransport::Response code) REQUIRES(sLock); int continuousfailureCount GUARDED_BY(sLock) = 0; bool checkRevalidationNecessary() REQUIRES(sLock); std::chrono::milliseconds timeout() const { return mTimeout; } std::chrono::milliseconds timeout() const { return mTimeout; } Loading @@ -119,24 +117,25 @@ class DnsTlsDispatcher : public PrivateDnsValidationObserver { static constexpr int kDotQueryTimeoutMs = -1; static constexpr int kDotQueryTimeoutMs = -1; private: private: // The flag to record whether or not dot_revalidation_threshold is ever reached. // Used to track if this Transport is usable. bool isRevalidationThresholdReached GUARDED_BY(sLock) = false; int continuousfailureCount GUARDED_BY(sLock) = 0; // The flag to record whether or not dot_xport_unusable_threshold is ever reached. // Used to indicate whether DoT revalidation is enabled for this Transport. bool isXportUnusableThresholdReached GUARDED_BY(sLock) = false; // 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; // If the number of continuous query timeouts reaches the threshold, mark the // The number of continuous failures to trigger a validation. It takes effect when DoT // server as unvalidated and trigger a validation. // revalidation is on. If the value is not a positive value, DoT revalidation is disabled. // If the value is not a positive value or private DNS mode is strict mode, no threshold is // Note that it must be at least 10, or it breaks ConnectTlsServerTimeout_ConcurrentQueries // set. Note that it must be at least 10, or it breaks // test. // ConnectTlsServerTimeout_ConcurrentQueries test. const int triggerThreshold; const int triggerThreshold; // The threshold to determine if this Transport is considered unusable. // The threshold to determine if this Transport is considered unusable. // If the number of continuous query timeouts reaches the threshold, mark this // If continuousfailureCount reaches this value, this Transport is no longer used. It // Transport as unusable. An unusable Transport won't be used anymore. // takes effect when DoT revalidation is on. If the value is not a positive value, DoT // If the value is not a positive value or private DNS mode is strict mode, no threshold is // revalidation is disabled. // set. const int unusableThreshold; const int unusableThreshold; // The time to await a future (the result of a DNS request) from the DnsTlsTransport // The time to await a future (the result of a DNS request) from the DnsTlsTransport Loading @@ -160,13 +159,12 @@ class DnsTlsDispatcher : public PrivateDnsValidationObserver { DnsTlsTransport::Result queryInternal(Transport& transport, const netdutils::Slice query) DnsTlsTransport::Result queryInternal(Transport& transport, const netdutils::Slice query) EXCLUDES(sLock); EXCLUDES(sLock); void maybeCleanup(std::chrono::time_point<std::chrono::steady_clock> now) REQUIRES(sLock); // Drop any cache entries whose useCount is zero and which have not been used recently. // Drop any cache entries whose useCount is zero and which have not been used recently. // This function performs a linear scan of mStore. // This function performs a linear scan of mStore. void cleanup(std::chrono::time_point<std::chrono::steady_clock> now, void cleanup(std::chrono::time_point<std::chrono::steady_clock> now) REQUIRES(sLock); std::chrono::seconds unusable_xport_idle_timeout, std::optional<unsigned> netId) REQUIRES(sLock); // Force dropping any Transports whose useCount is zero. void forceCleanupLocked(unsigned netId) REQUIRES(sLock); // Return a sorted list of usable DnsTlsServers in preference order. // Return a sorted list of usable DnsTlsServers in preference order. std::list<DnsTlsServer> getOrderedAndUsableServerList(const std::list<DnsTlsServer>& tlsServers, std::list<DnsTlsServer> getOrderedAndUsableServerList(const std::list<DnsTlsServer>& tlsServers, Loading Experiments.h +0 −1 Original line number Original line Diff line number Diff line Loading @@ -55,7 +55,6 @@ class Experiments { "sort_nameservers", "sort_nameservers", "dot_async_handshake", "dot_async_handshake", "dot_connect_timeout_ms", "dot_connect_timeout_ms", "dot_keep_unusable_xport_sec", "dot_maxtries", "dot_maxtries", "dot_revalidation_threshold", "dot_revalidation_threshold", "dot_xport_unusable_threshold", "dot_xport_unusable_threshold", Loading Loading
DnsProxyListener.cpp +25 −123 Original line number Original line Diff line number Diff line Loading @@ -469,7 +469,7 @@ void logDnsQueryResult(const addrinfo* res) { NI_NUMERICHOST); NI_NUMERICHOST); if (!ret) { if (!ret) { LOG(DEBUG) << __func__ << ": [" << i << "] " << ai->ai_flags << " " << ai->ai_family LOG(DEBUG) << __func__ << ": [" << i << "] " << ai->ai_flags << " " << ai->ai_family << " " << ai->ai_socktype << " " << ai->ai_protocol << " " << ip_addr; << " " << ai->ai_socktype << " " << ai->ai_protocol; } else { } else { LOG(DEBUG) << __func__ << ": [" << i << "] numeric hostname translation fail " << ret; LOG(DEBUG) << __func__ << ": [" << i << "] numeric hostname translation fail " << ret; } } Loading Loading @@ -523,96 +523,30 @@ bool synthesizeNat64PrefixWithARecord(const netdutils::IPPrefix& prefix, struct return true; return true; } } bool synthesizeNat64PrefixWithARecord(const netdutils::IPPrefix& prefix, addrinfo** res, bool synthesizeNat64PrefixWithARecord(const netdutils::IPPrefix& prefix, addrinfo* result) { bool unspecWantedButNoIPv6, if (result == nullptr) return false; const android_net_context* netcontext) { if (!onlyNonSpecialUseIPv4Addresses(result)) return false; if (*res == nullptr) return false; if (!onlyNonSpecialUseIPv4Addresses(*res)) return false; if (!isValidNat64Prefix(prefix)) return false; if (!isValidNat64Prefix(prefix)) return false; const sockaddr_storage ss = netdutils::IPSockAddr(prefix.ip()); struct sockaddr_storage ss = netdutils::IPSockAddr(prefix.ip()); const sockaddr_in6* v6prefix = (sockaddr_in6*)&ss; struct sockaddr_in6* v6prefix = (struct sockaddr_in6*)&ss; addrinfo* const head4 = *res; for (addrinfo* ai = result; ai; ai = ai->ai_next) { addrinfo* head6 = nullptr; struct sockaddr_in sinOriginal = *(struct sockaddr_in*)ai->ai_addr; addrinfo* cur6 = nullptr; struct sockaddr_in6* sin6 = (struct sockaddr_in6*)ai->ai_addr; memset(sin6, 0, sizeof(sockaddr_in6)); // Build a synthesized AAAA addrinfo list from the queried A addrinfo list. Here is the diagram // for the relationship of pointers. // // head4: point to the first queried A addrinfo // | // v // +-------------+ +-------------+ // | addrinfo4#1 |-->| addrinfo4#2 |--> .. queried A addrinfo(s) for DNS64 synthesis // +-------------+ +-------------+ // ^ // | // cur4: current worked-on queried A addrinfo // // head6: point to the first synthesized AAAA addrinfo // | // v // +-------------+ +-------------+ // | addrinfo6#1 |-->| addrinfo6#2 |--> .. synthesized DNS64 AAAA addrinfo(s) // +-------------+ +-------------+ // ^ // | // cur6: current worked-on synthesized addrinfo // for (const addrinfo* cur4 = head4; cur4; cur4 = cur4->ai_next) { // Allocate a space for a synthesized AAAA addrinfo. Note that the addrinfo and sockaddr // occupy one contiguous block of memory and are allocated and freed as a single block. // See get_ai and freeaddrinfo in packages/modules/DnsResolver/getaddrinfo.cpp. addrinfo* sa = (addrinfo*)calloc(1, sizeof(addrinfo) + sizeof(sockaddr_in6)); if (sa == nullptr) { LOG(ERROR) << "allocate memory failed for synthesized result"; freeaddrinfo(head6); return false; } // Initialize the synthesized AAAA addrinfo by the queried A addrinfo. The ai_addr will be // set lately. sa->ai_flags = cur4->ai_flags; sa->ai_family = AF_INET6; sa->ai_socktype = cur4->ai_socktype; sa->ai_protocol = cur4->ai_protocol; sa->ai_addrlen = sizeof(sockaddr_in6); sa->ai_addr = (sockaddr*)(sa + 1); sa->ai_canonname = nullptr; sa->ai_next = nullptr; if (cur4->ai_canonname != nullptr) { sa->ai_canonname = strdup(cur4->ai_canonname); if (sa->ai_canonname == nullptr) { LOG(ERROR) << "allocate memory failed for canonname"; freeaddrinfo(sa); freeaddrinfo(head6); return false; } } // Synthesize /96 NAT64 prefix with the queried IPv4 address. // Synthesize /96 NAT64 prefix in place. The space has reserved by get_ai() in const sockaddr_in* sin4 = (sockaddr_in*)cur4->ai_addr; // system/netd/resolv/getaddrinfo.cpp. sockaddr_in6* sin6 = (sockaddr_in6*)sa->ai_addr; sin6->sin6_addr = v6prefix->sin6_addr; sin6->sin6_addr = v6prefix->sin6_addr; sin6->sin6_addr.s6_addr32[3] = sin4->sin_addr.s_addr; sin6->sin6_addr.s6_addr32[3] = sinOriginal.sin_addr.s_addr; sin6->sin6_family = AF_INET6; sin6->sin6_family = AF_INET6; sin6->sin6_port = sin4->sin_port; sin6->sin6_port = sinOriginal.sin_port; ai->ai_addrlen = sizeof(struct sockaddr_in6); // If the synthesized list is empty, this becomes the first element. ai->ai_family = AF_INET6; if (head6 == nullptr) { head6 = sa; } // Add this element to the end of the synthesized list. if (cur6 != nullptr) { cur6->ai_next = sa; } cur6 = sa; if (WOULD_LOG(VERBOSE)) { if (WOULD_LOG(VERBOSE)) { char buf[INET6_ADDRSTRLEN]; // big enough for either IPv4 or IPv6 char buf[INET6_ADDRSTRLEN]; // big enough for either IPv4 or IPv6 inet_ntop(AF_INET, &sin4->sin_addr.s_addr, buf, sizeof(buf)); inet_ntop(AF_INET, &sinOriginal.sin_addr.s_addr, buf, sizeof(buf)); LOG(VERBOSE) << __func__ << ": DNS A record: " << buf; LOG(VERBOSE) << __func__ << ": DNS A record: " << buf; inet_ntop(AF_INET6, &v6prefix->sin6_addr, buf, sizeof(buf)); inet_ntop(AF_INET6, &v6prefix->sin6_addr, buf, sizeof(buf)); LOG(VERBOSE) << __func__ << ": NAT64 prefix: " << buf; LOG(VERBOSE) << __func__ << ": NAT64 prefix: " << buf; Loading @@ -620,39 +554,7 @@ bool synthesizeNat64PrefixWithARecord(const netdutils::IPPrefix& prefix, addrinf LOG(VERBOSE) << __func__ << ": DNS64 Synthesized AAAA record: " << buf; LOG(VERBOSE) << __func__ << ": DNS64 Synthesized AAAA record: " << buf; } } } } logDnsQueryResult(result); // Simply concatenate the synthesized AAAA addrinfo list and the queried A addrinfo list when // AF_UNSPEC is specified. In the other words, the IPv6 addresses are listed first and then // IPv4 addresses. For example: // 64:ff9b::102:304 (socktype=2, protocol=17) -> // 64:ff9b::102:304 (socktype=1, protocol=6) -> // 1.2.3.4 (socktype=2, protocol=17) -> // 1.2.3.4 (socktype=1, protocol=6) // Note that head6 and cur6 should be non-null because there was at least one IPv4 address // synthesized. From the above example, the synthesized addrinfo list puts IPv6 and IPv4 in // groups and sort by RFC 6724 later. This ordering is different from no synthesized case // because resolv_getaddrinfo() sorts results in explore_options. resolv_getaddrinfo() calls // explore_fqdn() many times by the different items of explore_options. It means that // resolv_rfc6724_sort() only sorts the results in each explore_options and concatenates each // results into one. For example, getaddrinfo() is called with null hints for a domain name // which has both IPv4 and IPv6 addresses. The address order of the result addrinfo may be: // 2001:db8::102:304 (socktype=2, protocol=17) -> 1.2.3.4 (socktype=2, protocol=17) -> // 2001:db8::102:304 (socktype=1, protocol=6) -> 1.2.3.4 (socktype=1, protocol=6) // In above example, the first two results come from one explore option and the last two come // from another one. They are sorted first, and then concatenate together to be the result. // See also resolv_getaddrinfo in packages/modules/DnsResolver/getaddrinfo.cpp. if (unspecWantedButNoIPv6) { cur6->ai_next = head4; } else { freeaddrinfo(head4); } // Sort the concatenated addresses by RFC 6724 section 2.1. struct addrinfo sorting_head = {.ai_next = head6}; resolv_rfc6724_sort(&sorting_head, netcontext->app_mark, netcontext->uid); *res = sorting_head.ai_next; logDnsQueryResult(*res); return true; return true; } } Loading Loading @@ -811,7 +713,7 @@ void DnsProxyListener::GetAddrInfoHandler::doDns64Synthesis(int32_t* rv, addrinf } } } } if (!synthesizeNat64PrefixWithARecord(prefix, res, unspecWantedButNoIPv6, &mNetContext)) { if (!synthesizeNat64PrefixWithARecord(prefix, *res)) { if (ipv6WantedButNoData) { if (ipv6WantedButNoData) { // If caller wants IPv6 answers but no data and failed to synthesize IPv6 answers, // If caller wants IPv6 answers but no data and failed to synthesize IPv6 answers, // don't return the IPv4 answers. // don't return the IPv4 answers. Loading @@ -825,9 +727,9 @@ void DnsProxyListener::GetAddrInfoHandler::doDns64Synthesis(int32_t* rv, addrinf } } void DnsProxyListener::GetAddrInfoHandler::run() { void DnsProxyListener::GetAddrInfoHandler::run() { LOG(INFO) << "GetAddrInfoHandler::run: {" << mNetContext.app_netid << " " LOG(DEBUG) << "GetAddrInfoHandler::run: {" << mNetContext.app_netid << " " << mNetContext.app_mark << " " << mNetContext.dns_netid << " " << mNetContext.dns_mark << mNetContext.app_mark << " " << mNetContext.dns_netid << " " << " " << mNetContext.uid << " " << mNetContext.flags << "}"; << mNetContext.dns_mark << " " << mNetContext.uid << " " << mNetContext.flags << "}"; addrinfo* result = nullptr; addrinfo* result = nullptr; Stopwatch s; Stopwatch s; Loading Loading @@ -1000,9 +902,9 @@ DnsProxyListener::ResNSendHandler::ResNSendHandler(SocketClient* c, std::string : Handler(c), mMsg(std::move(msg)), mFlags(flags), mNetContext(netcontext) {} : Handler(c), mMsg(std::move(msg)), mFlags(flags), mNetContext(netcontext) {} void DnsProxyListener::ResNSendHandler::run() { void DnsProxyListener::ResNSendHandler::run() { LOG(INFO) << "ResNSendHandler::run: " << mFlags << " / {" << mNetContext.app_netid << " " LOG(DEBUG) << "ResNSendHandler::run: " << mFlags << " / {" << mNetContext.app_netid << " " << mNetContext.app_mark << " " << mNetContext.dns_netid << " " << mNetContext.dns_mark << mNetContext.app_mark << " " << mNetContext.dns_netid << " " << " " << mNetContext.uid << " " << mNetContext.flags << "}"; << mNetContext.dns_mark << " " << mNetContext.uid << " " << mNetContext.flags << "}"; Stopwatch s; Stopwatch s; maybeFixupNetContext(&mNetContext, mClient->getPid()); maybeFixupNetContext(&mNetContext, mClient->getPid()); Loading
DnsResolver.cpp +2 −3 Original line number Original line Diff line number Diff line Loading @@ -31,9 +31,8 @@ bool resolv_init(const ResolverNetdCallbacks* callbacks) { bool resolv_init(const ResolverNetdCallbacks* callbacks) { android::base::InitLogging(/*argv=*/nullptr); android::base::InitLogging(/*argv=*/nullptr); LOG(INFO) << __func__ << ": Initializing resolver"; LOG(INFO) << __func__ << ": Initializing resolver"; const bool isDebug = isUserDebugBuild(); resolv_set_log_severity(android::base::WARNING); resolv_set_log_severity(isDebug ? android::base::INFO : android::base::WARNING); doh_init_logger(DOH_LOG_LEVEL_WARN); doh_init_logger(isDebug ? DOH_LOG_LEVEL_INFO : DOH_LOG_LEVEL_WARN); using android::net::gApiLevel; using android::net::gApiLevel; gApiLevel = getApiLevel(); gApiLevel = getApiLevel(); using android::net::gResNetdCallbacks; using android::net::gResNetdCallbacks; Loading
DnsTlsDispatcher.cpp +50 −68 Original line number Original line Diff line number Diff line Loading @@ -26,7 +26,6 @@ #include "resolv_cache.h" #include "resolv_cache.h" #include "resolv_private.h" #include "resolv_private.h" #include "stats.pb.h" #include "stats.pb.h" #include "util.h" #include <android-base/logging.h> #include <android-base/logging.h> Loading Loading @@ -68,7 +67,8 @@ std::list<DnsTlsServer> DnsTlsDispatcher::getOrderedAndUsableServerList( for (const auto& tlsServer : tlsServers) { for (const auto& tlsServer : tlsServers) { const Key key = std::make_pair(mark, tlsServer); const Key key = std::make_pair(mark, tlsServer); if (Transport* xport = getTransport(key); xport != nullptr) { if (const Transport* xport = getTransport(key); xport != nullptr) { // DoT revalidation specific feature. if (!xport->usable()) { if (!xport->usable()) { // Don't use this xport. It will be removed after timeout // Don't use this xport. It will be removed after timeout // (IDLE_TIMEOUT minutes). // (IDLE_TIMEOUT minutes). Loading Loading @@ -112,13 +112,7 @@ DnsTlsTransport::Response DnsTlsDispatcher::query(const std::list<DnsTlsServer>& const std::list<DnsTlsServer> servers( const std::list<DnsTlsServer> servers( getOrderedAndUsableServerList(tlsServers, statp->netid, statp->mark)); getOrderedAndUsableServerList(tlsServers, statp->netid, statp->mark)); if (servers.empty()) { if (servers.empty()) LOG(WARNING) << "No usable DnsTlsServers"; LOG(WARNING) << "No usable DnsTlsServers"; // Call maybeCleanup so the expired Transports can be removed as expected. std::lock_guard guard(sLock); maybeCleanup(std::chrono::steady_clock::now()); } DnsTlsTransport::Response code = DnsTlsTransport::Response::internal_error; DnsTlsTransport::Response code = DnsTlsTransport::Response::internal_error; int serverCount = 0; int serverCount = 0; Loading Loading @@ -215,14 +209,9 @@ DnsTlsTransport::Response DnsTlsDispatcher::query(const DnsTlsServer& server, un std::lock_guard guard(sLock); std::lock_guard guard(sLock); --xport->useCount; --xport->useCount; xport->lastUsed = now; xport->lastUsed = now; if (code == DnsTlsTransport::Response::network_error) { xport->continuousfailureCount++; } else { xport->continuousfailureCount = 0; } // DoT revalidation specific feature. // DoT revalidation specific feature. if (xport->checkRevalidationNecessary()) { if (xport->checkRevalidationNecessary(code)) { // Even if the revalidation passes, it doesn't guarantee that DoT queries // 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 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 // to probe while the xport still uses an existing connection. So far, there isn't Loading @@ -237,14 +226,14 @@ DnsTlsTransport::Response DnsTlsDispatcher::query(const DnsTlsServer& server, un << (result.ok() ? "succeeded" : "failed: " + result.error().message()); << (result.ok() ? "succeeded" : "failed: " + result.error().message()); } } maybeCleanup(now); cleanup(now); } } return code; return code; } } void DnsTlsDispatcher::forceCleanup(unsigned netId) { void DnsTlsDispatcher::forceCleanup(unsigned netId) { std::lock_guard guard(sLock); std::lock_guard guard(sLock); cleanup(std::chrono::steady_clock::now(), std::chrono::seconds(-1), netId); forceCleanupLocked(netId); } } DnsTlsTransport::Result DnsTlsDispatcher::queryInternal(Transport& xport, DnsTlsTransport::Result DnsTlsDispatcher::queryInternal(Transport& xport, Loading Loading @@ -276,45 +265,33 @@ DnsTlsTransport::Result DnsTlsDispatcher::queryInternal(Transport& xport, // This timeout effectively controls how long to keep SSL session tickets. // This timeout effectively controls how long to keep SSL session tickets. static constexpr std::chrono::minutes IDLE_TIMEOUT(5); static constexpr std::chrono::minutes IDLE_TIMEOUT(5); void DnsTlsDispatcher::maybeCleanup(std::chrono::time_point<std::chrono::steady_clock> now) { void DnsTlsDispatcher::cleanup(std::chrono::time_point<std::chrono::steady_clock> now) { // Make the timeout tunable via experiment flag for testing. std::chrono::seconds unusable_xport_idle_timeout{-1}; const int value = Experiments::getInstance()->getFlag("dot_keep_unusable_xport_sec", -1); if (value > -1 && isUserDebugBuild() && std::chrono::seconds(value) < IDLE_TIMEOUT) { unusable_xport_idle_timeout = std::chrono::seconds(value); } // To avoid scanning mStore after every query, return early if a cleanup has been // To avoid scanning mStore after every query, return early if a cleanup has been // performed recently. // performed recently. const std::chrono::seconds timeout = (unusable_xport_idle_timeout < IDLE_TIMEOUT) if (now - mLastCleanup < IDLE_TIMEOUT) { ? unusable_xport_idle_timeout : IDLE_TIMEOUT; if (now - mLastCleanup < timeout) { return; return; } } cleanup(now, unusable_xport_idle_timeout, std::nullopt); for (auto it = mStore.begin(); it != mStore.end();) { auto& s = it->second; if (s->useCount == 0 && now - s->lastUsed > IDLE_TIMEOUT) { it = mStore.erase(it); } else { ++it; } } mLastCleanup = now; mLastCleanup = now; } } void DnsTlsDispatcher::cleanup(std::chrono::time_point<std::chrono::steady_clock> now, // TODO: unify forceCleanupLocked() and cleanup(). std::chrono::seconds unusable_xport_idle_timeout, void DnsTlsDispatcher::forceCleanupLocked(unsigned netId) { std::optional<unsigned> netId) { for (auto it = mStore.begin(); it != mStore.end();) { std::erase_if(mStore, [&](const auto& item) REQUIRES(sLock) { auto& s = it->second; auto const& [_, xport] = item; if (s->useCount == 0 && s->mNetId == netId) { if (xport->useCount == 0) { it = mStore.erase(it); // Remove the Transports of the associated network. } else { if (netId.has_value() && xport->mNetId == netId.value()) return true; ++it; } // Remove all expired Transports. if (now - xport->lastUsed > IDLE_TIMEOUT) return true; // Unusable Transports should be removed earlier. if (!xport->usable() && unusable_xport_idle_timeout.count() >= 0 && now - xport->lastUsed > unusable_xport_idle_timeout) return true; } } return false; }); } } DnsTlsDispatcher::Transport* DnsTlsDispatcher::addTransport(const DnsTlsServer& server, DnsTlsDispatcher::Transport* DnsTlsDispatcher::addTransport(const DnsTlsServer& server, Loading @@ -331,11 +308,12 @@ DnsTlsDispatcher::Transport* DnsTlsDispatcher::addTransport(const DnsTlsServer& int queryTimeout = instance->getFlag("dot_query_timeout_ms", Transport::kDotQueryTimeoutMs); int queryTimeout = instance->getFlag("dot_query_timeout_ms", Transport::kDotQueryTimeoutMs); // Check and adjust the parameters if they are improperly set. // Check and adjust the parameters if they are improperly set. bool revalidationEnabled = false; const bool isForOpportunisticMode = server.name.empty(); const bool isForOpportunisticMode = server.name.empty(); if (triggerThr <= 0 || !isForOpportunisticMode) { if (triggerThr > 0 && unusableThr > 0 && isForOpportunisticMode) { revalidationEnabled = true; } else { triggerThr = -1; triggerThr = -1; } if (unusableThr <= 0 || !isForOpportunisticMode) { unusableThr = -1; unusableThr = -1; } } if (queryTimeout < 0) { if (queryTimeout < 0) { Loading @@ -344,8 +322,9 @@ DnsTlsDispatcher::Transport* DnsTlsDispatcher::addTransport(const DnsTlsServer& queryTimeout = 1000; queryTimeout = 1000; } } ret = new Transport(server, mark, netId, mFactory.get(), triggerThr, unusableThr, queryTimeout); ret = new Transport(server, mark, netId, mFactory.get(), revalidationEnabled, triggerThr, LOG(INFO) << "Transport is initialized with { " << triggerThr << ", " << unusableThr << ", " unusableThr, queryTimeout); LOG(DEBUG) << "Transport is initialized with { " << triggerThr << ", " << unusableThr << ", " << queryTimeout << "ms }" << queryTimeout << "ms }" << " for server { " << server.toIpString() << "/" << server.name << " }"; << " for server { " << server.toIpString() << "/" << server.name << " }"; Loading @@ -359,23 +338,26 @@ DnsTlsDispatcher::Transport* DnsTlsDispatcher::getTransport(const Key& key) { return (it == mStore.end() ? nullptr : it->second.get()); return (it == mStore.end() ? nullptr : it->second.get()); } } bool DnsTlsDispatcher::Transport::checkRevalidationNecessary() { bool DnsTlsDispatcher::Transport::checkRevalidationNecessary(DnsTlsTransport::Response code) { if (triggerThreshold <= 0) return false; if (!revalidationEnabled) return false; if (continuousfailureCount < triggerThreshold) return false; if (isRevalidationThresholdReached) return false; isRevalidationThresholdReached = true; 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) { return true; return true; } } return false; } bool DnsTlsDispatcher::Transport::usable() { bool DnsTlsDispatcher::Transport::usable() const { if (unusableThreshold <= 0) return true; if (!revalidationEnabled) return true; if (continuousfailureCount >= unusableThreshold) { return continuousfailureCount < unusableThreshold; // Once reach the threshold, mark this Transport as unusable. isXportUnusableThresholdReached = true; } return !isXportUnusableThresholdReached; } } } // end of namespace net } // end of namespace net Loading
DnsTlsDispatcher.h +23 −25 Original line number Original line Diff line number Diff line Loading @@ -83,10 +83,11 @@ class DnsTlsDispatcher : public PrivateDnsValidationObserver { // usage monitoring so we can expire idle sessions from the cache. // usage monitoring so we can expire idle sessions from the cache. struct Transport { struct Transport { Transport(const DnsTlsServer& server, unsigned mark, unsigned netId, Transport(const DnsTlsServer& server, unsigned mark, unsigned netId, IDnsTlsSocketFactory* _Nonnull factory, int triggerThr, int unusableThr, IDnsTlsSocketFactory* _Nonnull factory, bool revalidationEnabled, int triggerThr, int timeout) int unusableThr, int timeout) : transport(server, mark, factory), : transport(server, mark, factory), mNetId(netId), mNetId(netId), revalidationEnabled(revalidationEnabled), triggerThreshold(triggerThr), triggerThreshold(triggerThr), unusableThreshold(unusableThr), unusableThreshold(unusableThr), mTimeout(timeout) {} mTimeout(timeout) {} Loading @@ -105,12 +106,9 @@ class DnsTlsDispatcher : public PrivateDnsValidationObserver { // If DoT revalidation is disabled, it returns true; otherwise, it returns // If DoT revalidation is disabled, it returns true; otherwise, it returns // whether or not this Transport is usable. // whether or not this Transport is usable. bool usable() REQUIRES(sLock); bool usable() const REQUIRES(sLock); // Used to track if this Transport is usable. bool checkRevalidationNecessary(DnsTlsTransport::Response code) REQUIRES(sLock); int continuousfailureCount GUARDED_BY(sLock) = 0; bool checkRevalidationNecessary() REQUIRES(sLock); std::chrono::milliseconds timeout() const { return mTimeout; } std::chrono::milliseconds timeout() const { return mTimeout; } Loading @@ -119,24 +117,25 @@ class DnsTlsDispatcher : public PrivateDnsValidationObserver { static constexpr int kDotQueryTimeoutMs = -1; static constexpr int kDotQueryTimeoutMs = -1; private: private: // The flag to record whether or not dot_revalidation_threshold is ever reached. // Used to track if this Transport is usable. bool isRevalidationThresholdReached GUARDED_BY(sLock) = false; int continuousfailureCount GUARDED_BY(sLock) = 0; // The flag to record whether or not dot_xport_unusable_threshold is ever reached. // Used to indicate whether DoT revalidation is enabled for this Transport. bool isXportUnusableThresholdReached GUARDED_BY(sLock) = false; // 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; // If the number of continuous query timeouts reaches the threshold, mark the // The number of continuous failures to trigger a validation. It takes effect when DoT // server as unvalidated and trigger a validation. // revalidation is on. If the value is not a positive value, DoT revalidation is disabled. // If the value is not a positive value or private DNS mode is strict mode, no threshold is // Note that it must be at least 10, or it breaks ConnectTlsServerTimeout_ConcurrentQueries // set. Note that it must be at least 10, or it breaks // test. // ConnectTlsServerTimeout_ConcurrentQueries test. const int triggerThreshold; const int triggerThreshold; // The threshold to determine if this Transport is considered unusable. // The threshold to determine if this Transport is considered unusable. // If the number of continuous query timeouts reaches the threshold, mark this // If continuousfailureCount reaches this value, this Transport is no longer used. It // Transport as unusable. An unusable Transport won't be used anymore. // takes effect when DoT revalidation is on. If the value is not a positive value, DoT // If the value is not a positive value or private DNS mode is strict mode, no threshold is // revalidation is disabled. // set. const int unusableThreshold; const int unusableThreshold; // The time to await a future (the result of a DNS request) from the DnsTlsTransport // The time to await a future (the result of a DNS request) from the DnsTlsTransport Loading @@ -160,13 +159,12 @@ class DnsTlsDispatcher : public PrivateDnsValidationObserver { DnsTlsTransport::Result queryInternal(Transport& transport, const netdutils::Slice query) DnsTlsTransport::Result queryInternal(Transport& transport, const netdutils::Slice query) EXCLUDES(sLock); EXCLUDES(sLock); void maybeCleanup(std::chrono::time_point<std::chrono::steady_clock> now) REQUIRES(sLock); // Drop any cache entries whose useCount is zero and which have not been used recently. // Drop any cache entries whose useCount is zero and which have not been used recently. // This function performs a linear scan of mStore. // This function performs a linear scan of mStore. void cleanup(std::chrono::time_point<std::chrono::steady_clock> now, void cleanup(std::chrono::time_point<std::chrono::steady_clock> now) REQUIRES(sLock); std::chrono::seconds unusable_xport_idle_timeout, std::optional<unsigned> netId) REQUIRES(sLock); // Force dropping any Transports whose useCount is zero. void forceCleanupLocked(unsigned netId) REQUIRES(sLock); // Return a sorted list of usable DnsTlsServers in preference order. // Return a sorted list of usable DnsTlsServers in preference order. std::list<DnsTlsServer> getOrderedAndUsableServerList(const std::list<DnsTlsServer>& tlsServers, std::list<DnsTlsServer> getOrderedAndUsableServerList(const std::list<DnsTlsServer>& tlsServers, Loading
Experiments.h +0 −1 Original line number Original line Diff line number Diff line Loading @@ -55,7 +55,6 @@ class Experiments { "sort_nameservers", "sort_nameservers", "dot_async_handshake", "dot_async_handshake", "dot_connect_timeout_ms", "dot_connect_timeout_ms", "dot_keep_unusable_xport_sec", "dot_maxtries", "dot_maxtries", "dot_revalidation_threshold", "dot_revalidation_threshold", "dot_xport_unusable_threshold", "dot_xport_unusable_threshold", Loading