Bug 1866777 - Disable test_race_cache_with_network.js on windows opt for frequent...
[gecko.git] / netwerk / dns / nsHostResolver.cpp
blob179d15c965775b05f0651cf1770e5c5c4edd051a
1 /* vim:set ts=4 sw=2 sts=2 et cin: */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #if defined(HAVE_RES_NINIT)
7 # include <sys/types.h>
8 # include <netinet/in.h>
9 # include <arpa/inet.h>
10 # include <arpa/nameser.h>
11 # include <resolv.h>
12 # define RES_RETRY_ON_FAILURE
13 #endif
15 #include <stdlib.h>
16 #include <ctime>
17 #include "nsHostResolver.h"
18 #include "nsError.h"
19 #include "nsISupports.h"
20 #include "nsISupportsUtils.h"
21 #include "nsIThreadManager.h"
22 #include "nsComponentManagerUtils.h"
23 #include "nsNetUtil.h"
24 #include "nsPrintfCString.h"
25 #include "nsXPCOMCIDInternal.h"
26 #include "prthread.h"
27 #include "prerror.h"
28 #include "prtime.h"
29 #include "mozilla/Logging.h"
30 #include "PLDHashTable.h"
31 #include "nsQueryObject.h"
32 #include "nsURLHelper.h"
33 #include "nsThreadUtils.h"
34 #include "nsThreadPool.h"
35 #include "GetAddrInfo.h"
36 #include "TRR.h"
37 #include "TRRQuery.h"
38 #include "TRRService.h"
40 #include "mozilla/Atomics.h"
41 #include "mozilla/glean/GleanMetrics.h"
42 #include "mozilla/HashFunctions.h"
43 #include "mozilla/TimeStamp.h"
44 #include "mozilla/Telemetry.h"
45 #include "mozilla/DebugOnly.h"
46 #include "mozilla/Preferences.h"
47 #include "mozilla/StaticPrefs_network.h"
48 // Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
49 #include "DNSLogging.h"
51 #ifdef XP_WIN
52 # include "mozilla/WindowsVersion.h"
53 #endif // XP_WIN
55 #ifdef MOZ_WIDGET_ANDROID
56 # include "mozilla/jni/Utils.h"
57 #endif
59 #define IS_ADDR_TYPE(_type) ((_type) == nsIDNSService::RESOLVE_TYPE_DEFAULT)
60 #define IS_OTHER_TYPE(_type) ((_type) != nsIDNSService::RESOLVE_TYPE_DEFAULT)
62 using namespace mozilla;
63 using namespace mozilla::net;
65 // None of our implementations expose a TTL for negative responses, so we use a
66 // constant always.
67 static const unsigned int NEGATIVE_RECORD_LIFETIME = 60;
69 //----------------------------------------------------------------------------
71 // Use a persistent thread pool in order to avoid spinning up new threads all
72 // the time. In particular, thread creation results in a res_init() call from
73 // libc which is quite expensive.
75 // The pool dynamically grows between 0 and MaxResolverThreads() in size. New
76 // requests go first to an idle thread. If that cannot be found and there are
77 // fewer than MaxResolverThreads() currently in the pool a new thread is created
78 // for high priority requests. If the new request is at a lower priority a new
79 // thread will only be created if there are fewer than
80 // MaxResolverThreadsAnyPriority() currently outstanding. If a thread cannot be
81 // created or an idle thread located for the request it is queued.
83 // When the pool is greater than MaxResolverThreadsAnyPriority() in size a
84 // thread will be destroyed after ShortIdleTimeoutSeconds of idle time. Smaller
85 // pools use LongIdleTimeoutSeconds for a timeout period.
87 // for threads 1 -> MaxResolverThreadsAnyPriority()
88 #define LongIdleTimeoutSeconds 300
89 // for threads MaxResolverThreadsAnyPriority() + 1 -> MaxResolverThreads()
90 #define ShortIdleTimeoutSeconds 60
92 //----------------------------------------------------------------------------
94 namespace mozilla::net {
95 LazyLogModule gHostResolverLog("nsHostResolver");
96 } // namespace mozilla::net
98 //----------------------------------------------------------------------------
100 #if defined(RES_RETRY_ON_FAILURE)
102 // this class represents the resolver state for a given thread. if we
103 // encounter a lookup failure, then we can invoke the Reset method on an
104 // instance of this class to reset the resolver (in case /etc/resolv.conf
105 // for example changed). this is mainly an issue on GNU systems since glibc
106 // only reads in /etc/resolv.conf once per thread. it may be an issue on
107 // other systems as well.
109 class nsResState {
110 public:
111 nsResState()
112 // initialize mLastReset to the time when this object
113 // is created. this means that a reset will not occur
114 // if a thread is too young. the alternative would be
115 // to initialize this to the beginning of time, so that
116 // the first failure would cause a reset, but since the
117 // thread would have just started up, it likely would
118 // already have current /etc/resolv.conf info.
119 : mLastReset(PR_IntervalNow()) {}
121 bool Reset() {
122 // reset no more than once per second
123 if (PR_IntervalToSeconds(PR_IntervalNow() - mLastReset) < 1) {
124 return false;
127 mLastReset = PR_IntervalNow();
128 auto result = res_ninit(&_res);
130 LOG(("nsResState::Reset() > 'res_ninit' returned %d", result));
131 return (result == 0);
134 private:
135 PRIntervalTime mLastReset;
138 #endif // RES_RETRY_ON_FAILURE
140 //----------------------------------------------------------------------------
142 static const char kPrefGetTtl[] = "network.dns.get-ttl";
143 static const char kPrefNativeIsLocalhost[] = "network.dns.native-is-localhost";
144 static const char kPrefThreadIdleTime[] =
145 "network.dns.resolver-thread-extra-idle-time-seconds";
146 static bool sGetTtlEnabled = false;
147 mozilla::Atomic<bool, mozilla::Relaxed> gNativeIsLocalhost;
148 mozilla::Atomic<bool, mozilla::Relaxed> sNativeHTTPSSupported{false};
150 static void DnsPrefChanged(const char* aPref, void* aSelf) {
151 MOZ_ASSERT(NS_IsMainThread(),
152 "Should be getting pref changed notification on main thread!");
154 MOZ_ASSERT(aSelf);
156 if (!strcmp(aPref, kPrefGetTtl)) {
157 #ifdef DNSQUERY_AVAILABLE
158 sGetTtlEnabled = Preferences::GetBool(kPrefGetTtl);
159 #endif
160 } else if (!strcmp(aPref, kPrefNativeIsLocalhost)) {
161 gNativeIsLocalhost = Preferences::GetBool(kPrefNativeIsLocalhost);
165 NS_IMPL_ISUPPORTS0(nsHostResolver)
167 nsHostResolver::nsHostResolver(uint32_t maxCacheEntries,
168 uint32_t defaultCacheEntryLifetime,
169 uint32_t defaultGracePeriod)
170 : mMaxCacheEntries(maxCacheEntries),
171 mDefaultCacheLifetime(defaultCacheEntryLifetime),
172 mDefaultGracePeriod(defaultGracePeriod),
173 mIdleTaskCV(mLock, "nsHostResolver.mIdleTaskCV") {
174 mCreationTime = PR_Now();
176 mLongIdleTimeout = TimeDuration::FromSeconds(LongIdleTimeoutSeconds);
177 mShortIdleTimeout = TimeDuration::FromSeconds(ShortIdleTimeoutSeconds);
180 nsHostResolver::~nsHostResolver() = default;
182 nsresult nsHostResolver::Init() MOZ_NO_THREAD_SAFETY_ANALYSIS {
183 MOZ_ASSERT(NS_IsMainThread());
184 if (NS_FAILED(GetAddrInfoInit())) {
185 return NS_ERROR_FAILURE;
188 LOG(("nsHostResolver::Init this=%p", this));
190 mShutdown = false;
191 mNCS = NetworkConnectivityService::GetSingleton();
193 // The preferences probably haven't been loaded from the disk yet, so we
194 // need to register a callback that will set up the experiment once they
195 // are. We also need to explicitly set a value for the props otherwise the
196 // callback won't be called.
198 DebugOnly<nsresult> rv = Preferences::RegisterCallbackAndCall(
199 &DnsPrefChanged, kPrefGetTtl, this);
200 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
201 "Could not register DNS TTL pref callback.");
202 rv = Preferences::RegisterCallbackAndCall(&DnsPrefChanged,
203 kPrefNativeIsLocalhost, this);
204 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
205 "Could not register DNS pref callback.");
208 #if defined(HAVE_RES_NINIT)
209 // We want to make sure the system is using the correct resolver settings,
210 // so we force it to reload those settings whenever we startup a subsequent
211 // nsHostResolver instance. We assume that there is no reason to do this
212 // for the first nsHostResolver instance since that is usually created
213 // during application startup.
214 static int initCount = 0;
215 if (initCount++ > 0) {
216 auto result = res_ninit(&_res);
217 LOG(("nsHostResolver::Init > 'res_ninit' returned %d", result));
219 #endif
221 // We can configure the threadpool to keep threads alive for a while after
222 // the last ThreadFunc task has been executed.
223 int32_t poolTimeoutSecs = Preferences::GetInt(kPrefThreadIdleTime, 60);
224 uint32_t poolTimeoutMs;
225 if (poolTimeoutSecs < 0) {
226 // This means never shut down the idle threads
227 poolTimeoutMs = UINT32_MAX;
228 } else {
229 // We clamp down the idle time between 0 and one hour.
230 poolTimeoutMs =
231 mozilla::clamped<uint32_t>(poolTimeoutSecs * 1000, 0, 3600 * 1000);
234 #if defined(XP_WIN)
235 // For some reason, the DNSQuery_A API doesn't work on Windows 10.
236 // It returns a success code, but no records. We only allow
237 // native HTTPS records on Win 11 for now.
238 sNativeHTTPSSupported = mozilla::IsWin11OrLater();
239 #elif defined(MOZ_WIDGET_ANDROID)
240 // android_res_nquery only got added in API level 29
241 sNativeHTTPSSupported = jni::GetAPIVersion() >= 29;
242 #elif defined(XP_LINUX) || defined(XP_MACOSX)
243 sNativeHTTPSSupported = true;
244 #endif
245 LOG(("Native HTTPS records supported=%d", bool(sNativeHTTPSSupported)));
247 nsCOMPtr<nsIThreadPool> threadPool = new nsThreadPool();
248 MOZ_ALWAYS_SUCCEEDS(threadPool->SetThreadLimit(MaxResolverThreads()));
249 MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadLimit(MaxResolverThreads()));
250 MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadTimeout(poolTimeoutMs));
251 MOZ_ALWAYS_SUCCEEDS(
252 threadPool->SetThreadStackSize(nsIThreadManager::kThreadPoolStackSize));
253 MOZ_ALWAYS_SUCCEEDS(threadPool->SetName("DNS Resolver"_ns));
254 mResolverThreads = ToRefPtr(std::move(threadPool));
256 return NS_OK;
259 void nsHostResolver::ClearPendingQueue(
260 LinkedList<RefPtr<nsHostRecord>>& aPendingQ) {
261 // loop through pending queue, erroring out pending lookups.
262 if (!aPendingQ.isEmpty()) {
263 for (const RefPtr<nsHostRecord>& rec : aPendingQ) {
264 rec->Cancel();
265 if (rec->IsAddrRecord()) {
266 CompleteLookup(rec, NS_ERROR_ABORT, nullptr, rec->pb, rec->originSuffix,
267 rec->mTRRSkippedReason, nullptr);
268 } else {
269 mozilla::net::TypeRecordResultType empty(Nothing{});
270 CompleteLookupByType(rec, NS_ERROR_ABORT, empty, rec->mTRRSkippedReason,
271 0, rec->pb);
278 // FlushCache() is what we call when the network has changed. We must not
279 // trust names that were resolved before this change. They may resolve
280 // differently now.
282 // This function removes all existing resolved host entries from the hash.
283 // Names that are in the pending queues can be left there. Entries in the
284 // cache that have 'Resolve' set true but not 'OnQueue' are being resolved
285 // right now, so we need to mark them to get re-resolved on completion!
287 void nsHostResolver::FlushCache(bool aTrrToo) {
288 MutexAutoLock lock(mLock);
290 mQueue.FlushEvictionQ(mRecordDB, lock);
292 // Refresh the cache entries that are resolving RIGHT now, remove the rest.
293 for (auto iter = mRecordDB.Iter(); !iter.Done(); iter.Next()) {
294 nsHostRecord* record = iter.UserData();
295 // Try to remove the record, or mark it for refresh.
296 // By-type records are from TRR. We do not need to flush those entry
297 // when the network has change, because they are not local.
298 if (record->IsAddrRecord()) {
299 RefPtr<AddrHostRecord> addrRec = do_QueryObject(record);
300 MOZ_ASSERT(addrRec);
301 if (addrRec->RemoveOrRefresh(aTrrToo)) {
302 mQueue.MaybeRemoveFromQ(record, lock);
303 LOG(("Removing (%s) Addr record from mRecordDB", record->host.get()));
304 iter.Remove();
306 } else if (aTrrToo) {
307 // remove by type records
308 LOG(("Removing (%s) type record from mRecordDB", record->host.get()));
309 iter.Remove();
314 void nsHostResolver::Shutdown() {
315 LOG(("Shutting down host resolver.\n"));
318 DebugOnly<nsresult> rv =
319 Preferences::UnregisterCallback(&DnsPrefChanged, kPrefGetTtl, this);
320 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
321 "Could not unregister DNS TTL pref callback.");
324 LinkedList<RefPtr<nsHostRecord>> pendingQHigh, pendingQMed, pendingQLow,
325 evictionQ;
328 MutexAutoLock lock(mLock);
330 mShutdown = true;
332 if (mNumIdleTasks) {
333 mIdleTaskCV.NotifyAll();
336 mQueue.ClearAll(
337 [&](nsHostRecord* aRec) {
338 mLock.AssertCurrentThreadOwns();
339 if (aRec->IsAddrRecord()) {
340 CompleteLookupLocked(aRec, NS_ERROR_ABORT, nullptr, aRec->pb,
341 aRec->originSuffix, aRec->mTRRSkippedReason,
342 nullptr, lock);
343 } else {
344 mozilla::net::TypeRecordResultType empty(Nothing{});
345 CompleteLookupByTypeLocked(aRec, NS_ERROR_ABORT, empty,
346 aRec->mTRRSkippedReason, 0, aRec->pb,
347 lock);
350 lock);
352 for (const auto& data : mRecordDB.Values()) {
353 data->Cancel();
355 // empty host database
356 mRecordDB.Clear();
358 mNCS = nullptr;
361 // Shutdown the resolver threads, but with a timeout of 2 seconds (prefable).
362 // If the timeout is exceeded, any stuck threads will be leaked.
363 mResolverThreads->ShutdownWithTimeout(
364 StaticPrefs::network_dns_resolver_shutdown_timeout_ms());
367 mozilla::DebugOnly<nsresult> rv = GetAddrInfoShutdown();
368 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to shutdown GetAddrInfo");
372 nsresult nsHostResolver::GetHostRecord(
373 const nsACString& host, const nsACString& aTrrServer, uint16_t type,
374 nsIDNSService::DNSFlags flags, uint16_t af, bool pb,
375 const nsCString& originSuffix, nsHostRecord** result) {
376 MutexAutoLock lock(mLock);
377 nsHostKey key(host, aTrrServer, type, flags, af, pb, originSuffix);
379 RefPtr<nsHostRecord> rec =
380 mRecordDB.LookupOrInsertWith(key, [&] { return InitRecord(key); });
381 if (rec->IsAddrRecord()) {
382 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
383 if (addrRec->addr) {
384 return NS_ERROR_FAILURE;
388 if (rec->mResolving) {
389 return NS_ERROR_FAILURE;
392 *result = rec.forget().take();
393 return NS_OK;
396 nsHostRecord* nsHostResolver::InitRecord(const nsHostKey& key) {
397 if (IS_ADDR_TYPE(key.type)) {
398 return new AddrHostRecord(key);
400 return new TypeHostRecord(key);
403 already_AddRefed<nsHostRecord> nsHostResolver::InitLoopbackRecord(
404 const nsHostKey& key, nsresult* aRv) {
405 MOZ_ASSERT(aRv);
406 MOZ_ASSERT(IS_ADDR_TYPE(key.type));
408 *aRv = NS_ERROR_FAILURE;
409 RefPtr<nsHostRecord> rec = InitRecord(key);
411 nsTArray<NetAddr> addresses;
412 NetAddr addr;
413 if (key.af == PR_AF_INET || key.af == PR_AF_UNSPEC) {
414 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(addr.InitFromString("127.0.0.1"_ns)));
415 addresses.AppendElement(addr);
417 if (key.af == PR_AF_INET6 || key.af == PR_AF_UNSPEC) {
418 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(addr.InitFromString("::1"_ns)));
419 addresses.AppendElement(addr);
422 RefPtr<AddrInfo> ai =
423 new AddrInfo(rec->host, DNSResolverType::Native, 0, std::move(addresses));
425 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
426 MutexAutoLock lock(addrRec->addr_info_lock);
427 addrRec->addr_info = ai;
428 addrRec->SetExpiration(TimeStamp::NowLoRes(), mDefaultCacheLifetime,
429 mDefaultGracePeriod);
430 addrRec->negative = false;
432 *aRv = NS_OK;
433 return rec.forget();
436 static bool IsNativeHTTPSEnabled() {
437 if (!StaticPrefs::network_dns_native_https_query()) {
438 return false;
440 return sNativeHTTPSSupported;
443 nsresult nsHostResolver::ResolveHost(const nsACString& aHost,
444 const nsACString& aTrrServer,
445 int32_t aPort, uint16_t type,
446 const OriginAttributes& aOriginAttributes,
447 nsIDNSService::DNSFlags flags, uint16_t af,
448 nsResolveHostCallback* aCallback) {
449 nsAutoCString host(aHost);
450 NS_ENSURE_TRUE(!host.IsEmpty(), NS_ERROR_UNEXPECTED);
452 nsAutoCString originSuffix;
453 aOriginAttributes.CreateSuffix(originSuffix);
454 LOG(("Resolving host [%s]<%s>%s%s type %d. [this=%p]\n", host.get(),
455 originSuffix.get(), flags & RES_BYPASS_CACHE ? " - bypassing cache" : "",
456 flags & RES_REFRESH_CACHE ? " - refresh cache" : "", type, this));
458 // ensure that we are working with a valid hostname before proceeding. see
459 // bug 304904 for details.
460 if (!net_IsValidHostName(host)) {
461 return NS_ERROR_UNKNOWN_HOST;
464 // If TRR is disabled we can return immediately if the native API is disabled
465 if (!IsNativeHTTPSEnabled() && IS_OTHER_TYPE(type) &&
466 Mode() == nsIDNSService::MODE_TRROFF) {
467 return NS_ERROR_UNKNOWN_HOST;
470 // Used to try to parse to an IP address literal.
471 NetAddr tempAddr;
472 if (IS_OTHER_TYPE(type) && (NS_SUCCEEDED(tempAddr.InitFromString(host)))) {
473 // For by-type queries the host cannot be IP literal.
474 return NS_ERROR_UNKNOWN_HOST;
477 RefPtr<nsResolveHostCallback> callback(aCallback);
478 // if result is set inside the lock, then we need to issue the
479 // callback before returning.
480 RefPtr<nsHostRecord> result;
481 nsresult status = NS_OK, rv = NS_OK;
483 MutexAutoLock lock(mLock);
485 if (mShutdown) {
486 return NS_ERROR_NOT_INITIALIZED;
489 // check to see if there is already an entry for this |host|
490 // in the hash table. if so, then check to see if we can't
491 // just reuse the lookup result. otherwise, if there are
492 // any pending callbacks, then add to pending callbacks queue,
493 // and return. otherwise, add ourselves as first pending
494 // callback, and proceed to do the lookup.
496 Maybe<nsCString> originHost;
497 if (StaticPrefs::network_dns_port_prefixed_qname_https_rr() &&
498 type == nsIDNSService::RESOLVE_TYPE_HTTPSSVC && aPort != -1 &&
499 aPort != 443) {
500 originHost = Some(host);
501 host = nsPrintfCString("_%d._https.%s", aPort, host.get());
502 LOG((" Using port prefixed host name [%s]", host.get()));
505 bool excludedFromTRR = false;
506 if (TRRService::Get() && TRRService::Get()->IsExcludedFromTRR(host)) {
507 flags |= nsIDNSService::RESOLVE_DISABLE_TRR;
508 excludedFromTRR = true;
510 if (!aTrrServer.IsEmpty()) {
511 return NS_ERROR_UNKNOWN_HOST;
515 nsHostKey key(host, aTrrServer, type, flags, af,
516 (aOriginAttributes.mPrivateBrowsingId > 0), originSuffix);
518 // Check if we have a localhost domain, if so hardcode to loopback
519 if (IS_ADDR_TYPE(type) && IsLoopbackHostname(host)) {
520 nsresult rv;
521 RefPtr<nsHostRecord> result = InitLoopbackRecord(key, &rv);
522 if (NS_WARN_IF(NS_FAILED(rv))) {
523 return rv;
525 MOZ_ASSERT(result);
526 aCallback->OnResolveHostComplete(this, result, NS_OK);
527 return NS_OK;
530 RefPtr<nsHostRecord> rec =
531 mRecordDB.LookupOrInsertWith(key, [&] { return InitRecord(key); });
533 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
534 MOZ_ASSERT(rec, "Record should not be null");
535 MOZ_ASSERT((IS_ADDR_TYPE(type) && rec->IsAddrRecord() && addrRec) ||
536 (IS_OTHER_TYPE(type) && !rec->IsAddrRecord()));
538 if (IS_OTHER_TYPE(type) && originHost) {
539 RefPtr<TypeHostRecord> typeRec = do_QueryObject(rec);
540 typeRec->mOriginHost = std::move(originHost);
543 if (excludedFromTRR) {
544 rec->RecordReason(TRRSkippedReason::TRR_EXCLUDED);
547 if (!(flags & RES_BYPASS_CACHE) &&
548 rec->HasUsableResult(TimeStamp::NowLoRes(), flags)) {
549 result = FromCache(rec, host, type, status, lock);
550 } else if (addrRec && addrRec->addr) {
551 // if the host name is an IP address literal and has been
552 // parsed, go ahead and use it.
553 LOG((" Using cached address for IP Literal [%s].\n", host.get()));
554 result = FromCachedIPLiteral(rec);
555 } else if (addrRec && NS_SUCCEEDED(tempAddr.InitFromString(host))) {
556 // try parsing the host name as an IP address literal to short
557 // circuit full host resolution. (this is necessary on some
558 // platforms like Win9x. see bug 219376 for more details.)
559 LOG((" Host is IP Literal [%s].\n", host.get()));
560 result = FromIPLiteral(addrRec, tempAddr);
561 } else if (mQueue.PendingCount() >= MAX_NON_PRIORITY_REQUESTS &&
562 !IsHighPriority(flags) && !rec->mResolving) {
563 LOG(
564 (" Lookup queue full: dropping %s priority request for "
565 "host [%s].\n",
566 IsMediumPriority(flags) ? "medium" : "low", host.get()));
567 if (IS_ADDR_TYPE(type)) {
568 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_OVERFLOW);
570 // This is a lower priority request and we are swamped, so refuse it.
571 rv = NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
573 // Check if the offline flag is set.
574 } else if (flags & RES_OFFLINE) {
575 LOG((" Offline request for host [%s]; ignoring.\n", host.get()));
576 rv = NS_ERROR_OFFLINE;
578 // We do not have a valid result till here.
579 // A/AAAA request can check for an alternative entry like AF_UNSPEC.
580 // Otherwise we need to start a new query.
581 } else if (!rec->mResolving) {
582 result =
583 FromUnspecEntry(rec, host, aTrrServer, originSuffix, type, flags, af,
584 aOriginAttributes.mPrivateBrowsingId > 0, status);
585 // If this is a by-type request or if no valid record was found
586 // in the cache or this is an AF_UNSPEC request, then start a
587 // new lookup.
588 if (!result) {
589 LOG((" No usable record in cache for host [%s] type %d.", host.get(),
590 type));
592 if (flags & RES_REFRESH_CACHE) {
593 rec->Invalidate();
596 // Add callback to the list of pending callbacks.
597 rec->mCallbacks.insertBack(callback);
598 rec->flags = flags;
599 rv = NameLookup(rec, lock);
600 if (IS_ADDR_TYPE(type)) {
601 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
602 METHOD_NETWORK_FIRST);
604 if (NS_FAILED(rv) && callback->isInList()) {
605 callback->remove();
606 } else {
607 LOG(
608 (" DNS lookup for host [%s] blocking "
609 "pending 'getaddrinfo' or trr query: "
610 "callback [%p]",
611 host.get(), callback.get()));
614 } else {
615 LOG(
616 (" Host [%s] is being resolved. Appending callback "
617 "[%p].",
618 host.get(), callback.get()));
620 rec->mCallbacks.insertBack(callback);
622 // Only A/AAAA records are place in a queue. The queues are for
623 // the native resolver, therefore by-type request are never put
624 // into a queue.
625 if (addrRec && addrRec->onQueue()) {
626 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
627 METHOD_NETWORK_SHARED);
629 // Consider the case where we are on a pending queue of
630 // lower priority than the request is being made at.
631 // In that case we should upgrade to the higher queue.
633 if (IsHighPriority(flags) && !IsHighPriority(rec->flags)) {
634 // Move from (low|med) to high.
635 mQueue.MoveToAnotherPendingQ(rec, flags, lock);
636 rec->flags = flags;
637 ConditionallyCreateThread(rec);
638 } else if (IsMediumPriority(flags) && IsLowPriority(rec->flags)) {
639 // Move from low to med.
640 mQueue.MoveToAnotherPendingQ(rec, flags, lock);
641 rec->flags = flags;
642 mIdleTaskCV.Notify();
647 if (result && callback->isInList()) {
648 callback->remove();
650 } // lock
652 if (result) {
653 callback->OnResolveHostComplete(this, result, status);
656 return rv;
659 already_AddRefed<nsHostRecord> nsHostResolver::FromCache(
660 nsHostRecord* aRec, const nsACString& aHost, uint16_t aType,
661 nsresult& aStatus, const MutexAutoLock& aLock) {
662 LOG((" Using cached record for host [%s].\n",
663 nsPromiseFlatCString(aHost).get()));
665 // put reference to host record on stack...
666 RefPtr<nsHostRecord> result = aRec;
667 if (IS_ADDR_TYPE(aType)) {
668 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
671 // For entries that are in the grace period
672 // or all cached negative entries, use the cache but start a new
673 // lookup in the background
674 ConditionallyRefreshRecord(aRec, aHost, aLock);
676 if (aRec->negative) {
677 LOG((" Negative cache entry for host [%s].\n",
678 nsPromiseFlatCString(aHost).get()));
679 if (IS_ADDR_TYPE(aType)) {
680 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_NEGATIVE_HIT);
682 aStatus = NS_ERROR_UNKNOWN_HOST;
685 return result.forget();
688 already_AddRefed<nsHostRecord> nsHostResolver::FromCachedIPLiteral(
689 nsHostRecord* aRec) {
690 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_LITERAL);
691 RefPtr<nsHostRecord> result = aRec;
692 return result.forget();
695 already_AddRefed<nsHostRecord> nsHostResolver::FromIPLiteral(
696 AddrHostRecord* aAddrRec, const NetAddr& aAddr) {
697 // ok, just copy the result into the host record, and be
698 // done with it! ;-)
699 aAddrRec->addr = MakeUnique<NetAddr>(aAddr);
700 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_LITERAL);
701 // put reference to host record on stack...
702 RefPtr<nsHostRecord> result = aAddrRec;
703 return result.forget();
706 already_AddRefed<nsHostRecord> nsHostResolver::FromUnspecEntry(
707 nsHostRecord* aRec, const nsACString& aHost, const nsACString& aTrrServer,
708 const nsACString& aOriginSuffix, uint16_t aType,
709 nsIDNSService::DNSFlags aFlags, uint16_t af, bool aPb, nsresult& aStatus) {
710 RefPtr<nsHostRecord> result = nullptr;
711 // If this is an IPV4 or IPV6 specific request, check if there is
712 // an AF_UNSPEC entry we can use. Otherwise, hit the resolver...
713 RefPtr<AddrHostRecord> addrRec = do_QueryObject(aRec);
714 if (addrRec && !(aFlags & RES_BYPASS_CACHE) &&
715 ((af == PR_AF_INET) || (af == PR_AF_INET6))) {
716 // Check for an AF_UNSPEC entry.
718 const nsHostKey unspecKey(aHost, aTrrServer,
719 nsIDNSService::RESOLVE_TYPE_DEFAULT, aFlags,
720 PR_AF_UNSPEC, aPb, aOriginSuffix);
721 RefPtr<nsHostRecord> unspecRec = mRecordDB.Get(unspecKey);
723 TimeStamp now = TimeStamp::NowLoRes();
724 if (unspecRec && unspecRec->HasUsableResult(now, aFlags)) {
725 MOZ_ASSERT(unspecRec->IsAddrRecord());
727 RefPtr<AddrHostRecord> addrUnspecRec = do_QueryObject(unspecRec);
728 MOZ_ASSERT(addrUnspecRec);
729 MOZ_ASSERT(addrUnspecRec->addr_info || addrUnspecRec->negative,
730 "Entry should be resolved or negative.");
732 LOG((" Trying AF_UNSPEC entry for host [%s] af: %s.\n",
733 PromiseFlatCString(aHost).get(),
734 (af == PR_AF_INET) ? "AF_INET" : "AF_INET6"));
736 // We need to lock in case any other thread is reading
737 // addr_info.
738 MutexAutoLock lock(addrRec->addr_info_lock);
740 addrRec->addr_info = nullptr;
741 addrRec->addr_info_gencnt++;
742 if (unspecRec->negative) {
743 aRec->negative = unspecRec->negative;
744 aRec->CopyExpirationTimesAndFlagsFrom(unspecRec);
745 } else if (addrUnspecRec->addr_info) {
746 MutexAutoLock lock(addrUnspecRec->addr_info_lock);
747 if (addrUnspecRec->addr_info) {
748 // Search for any valid address in the AF_UNSPEC entry
749 // in the cache (not blocklisted and from the right
750 // family).
751 nsTArray<NetAddr> addresses;
752 for (const auto& addr : addrUnspecRec->addr_info->Addresses()) {
753 if ((af == addr.inet.family) &&
754 !addrUnspecRec->Blocklisted(&addr)) {
755 addresses.AppendElement(addr);
758 if (!addresses.IsEmpty()) {
759 addrRec->addr_info = new AddrInfo(
760 addrUnspecRec->addr_info->Hostname(),
761 addrUnspecRec->addr_info->CanonicalHostname(),
762 addrUnspecRec->addr_info->ResolverType(),
763 addrUnspecRec->addr_info->TRRType(), std::move(addresses));
764 addrRec->addr_info_gencnt++;
765 aRec->CopyExpirationTimesAndFlagsFrom(unspecRec);
769 // Now check if we have a new record.
770 if (aRec->HasUsableResult(now, aFlags)) {
771 result = aRec;
772 if (aRec->negative) {
773 aStatus = NS_ERROR_UNKNOWN_HOST;
775 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
776 ConditionallyRefreshRecord(aRec, aHost, lock);
777 } else if (af == PR_AF_INET6) {
778 // For AF_INET6, a new lookup means another AF_UNSPEC
779 // lookup. We have already iterated through the
780 // AF_UNSPEC addresses, so we mark this record as
781 // negative.
782 LOG(
783 (" No AF_INET6 in AF_UNSPEC entry: "
784 "host [%s] unknown host.",
785 nsPromiseFlatCString(aHost).get()));
786 result = aRec;
787 aRec->negative = true;
788 aStatus = NS_ERROR_UNKNOWN_HOST;
789 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
790 METHOD_NEGATIVE_HIT);
795 return result.forget();
798 void nsHostResolver::DetachCallback(
799 const nsACString& host, const nsACString& aTrrServer, uint16_t aType,
800 const OriginAttributes& aOriginAttributes, nsIDNSService::DNSFlags flags,
801 uint16_t af, nsResolveHostCallback* aCallback, nsresult status) {
802 RefPtr<nsHostRecord> rec;
803 RefPtr<nsResolveHostCallback> callback(aCallback);
806 MutexAutoLock lock(mLock);
808 nsAutoCString originSuffix;
809 aOriginAttributes.CreateSuffix(originSuffix);
811 nsHostKey key(host, aTrrServer, aType, flags, af,
812 (aOriginAttributes.mPrivateBrowsingId > 0), originSuffix);
813 RefPtr<nsHostRecord> entry = mRecordDB.Get(key);
814 if (entry) {
815 // walk list looking for |callback|... we cannot assume
816 // that it will be there!
818 for (nsResolveHostCallback* c : entry->mCallbacks) {
819 if (c == callback) {
820 rec = entry;
821 c->remove();
822 break;
828 // complete callback with the given status code; this would only be done if
829 // the record was in the process of being resolved.
830 if (rec) {
831 callback->OnResolveHostComplete(this, rec, status);
835 nsresult nsHostResolver::ConditionallyCreateThread(nsHostRecord* rec) {
836 if (mNumIdleTasks) {
837 // wake up idle tasks to process this lookup
838 mIdleTaskCV.Notify();
839 } else if ((mActiveTaskCount < MaxResolverThreadsAnyPriority()) ||
840 (IsHighPriority(rec->flags) &&
841 mActiveTaskCount < MaxResolverThreads())) {
842 nsCOMPtr<nsIRunnable> event = mozilla::NewRunnableMethod(
843 "nsHostResolver::ThreadFunc", this, &nsHostResolver::ThreadFunc);
844 mActiveTaskCount++;
845 nsresult rv =
846 mResolverThreads->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
847 if (NS_FAILED(rv)) {
848 mActiveTaskCount--;
850 } else {
851 LOG((" Unable to find a thread for looking up host [%s].\n",
852 rec->host.get()));
854 return NS_OK;
857 nsresult nsHostResolver::TrrLookup_unlocked(nsHostRecord* rec, TRR* pushedTRR) {
858 MutexAutoLock lock(mLock);
859 return TrrLookup(rec, lock, pushedTRR);
862 void nsHostResolver::MaybeRenewHostRecord(nsHostRecord* aRec) {
863 MutexAutoLock lock(mLock);
864 MaybeRenewHostRecordLocked(aRec, lock);
867 void nsHostResolver::MaybeRenewHostRecordLocked(nsHostRecord* aRec,
868 const MutexAutoLock& aLock) {
869 mQueue.MaybeRenewHostRecord(aRec, aLock);
872 bool nsHostResolver::TRRServiceEnabledForRecord(nsHostRecord* aRec) {
873 MOZ_ASSERT(aRec, "Record must not be empty");
874 MOZ_ASSERT(aRec->mEffectiveTRRMode != nsIRequest::TRR_DEFAULT_MODE,
875 "effective TRR mode must be computed before this call");
876 if (!TRRService::Get()) {
877 aRec->RecordReason(TRRSkippedReason::TRR_NO_GSERVICE);
878 return false;
881 // We always try custom resolvers.
882 if (!aRec->mTrrServer.IsEmpty()) {
883 return true;
886 nsIRequest::TRRMode reqMode = aRec->mEffectiveTRRMode;
887 if (TRRService::Get()->Enabled(reqMode)) {
888 return true;
891 if (NS_IsOffline()) {
892 // If we are in the NOT_CONFIRMED state _because_ we lack connectivity,
893 // then we should report that the browser is offline instead.
894 aRec->RecordReason(TRRSkippedReason::TRR_IS_OFFLINE);
895 return false;
898 auto hasConnectivity = [this]() -> bool {
899 mLock.AssertCurrentThreadOwns();
900 if (!mNCS) {
901 return true;
903 nsINetworkConnectivityService::ConnectivityState ipv4 = mNCS->GetIPv4();
904 nsINetworkConnectivityService::ConnectivityState ipv6 = mNCS->GetIPv6();
906 if (ipv4 == nsINetworkConnectivityService::OK ||
907 ipv6 == nsINetworkConnectivityService::OK) {
908 return true;
911 if (ipv4 == nsINetworkConnectivityService::UNKNOWN ||
912 ipv6 == nsINetworkConnectivityService::UNKNOWN) {
913 // One of the checks hasn't completed yet. Optimistically assume we'll
914 // have network connectivity.
915 return true;
918 return false;
921 if (!hasConnectivity()) {
922 aRec->RecordReason(TRRSkippedReason::TRR_NO_CONNECTIVITY);
923 return false;
926 bool isConfirmed = TRRService::Get()->IsConfirmed();
927 if (!isConfirmed) {
928 aRec->RecordReason(TRRSkippedReason::TRR_NOT_CONFIRMED);
931 return isConfirmed;
934 // returns error if no TRR resolve is issued
935 // it is impt this is not called while a native lookup is going on
936 nsresult nsHostResolver::TrrLookup(nsHostRecord* aRec,
937 const MutexAutoLock& aLock, TRR* pushedTRR) {
938 if (Mode() == nsIDNSService::MODE_TRROFF ||
939 StaticPrefs::network_dns_disabled()) {
940 return NS_ERROR_UNKNOWN_HOST;
942 LOG(("TrrLookup host:%s af:%" PRId16, aRec->host.get(), aRec->af));
944 RefPtr<nsHostRecord> rec(aRec);
945 mLock.AssertCurrentThreadOwns();
947 RefPtr<AddrHostRecord> addrRec;
948 RefPtr<TypeHostRecord> typeRec;
950 if (rec->IsAddrRecord()) {
951 addrRec = do_QueryObject(rec);
952 MOZ_ASSERT(addrRec);
953 } else {
954 typeRec = do_QueryObject(rec);
955 MOZ_ASSERT(typeRec);
958 MOZ_ASSERT(!rec->mResolving);
960 if (!TRRServiceEnabledForRecord(aRec)) {
961 return NS_ERROR_UNKNOWN_HOST;
964 MaybeRenewHostRecordLocked(rec, aLock);
966 RefPtr<TRRQuery> query = new TRRQuery(this, rec);
967 nsresult rv = query->DispatchLookup(pushedTRR);
968 if (NS_FAILED(rv)) {
969 rec->RecordReason(TRRSkippedReason::TRR_DID_NOT_MAKE_QUERY);
970 return rv;
974 auto lock = rec->mTRRQuery.Lock();
975 MOZ_ASSERT(!lock.ref(), "TRR already in progress");
976 lock.ref() = query;
979 rec->mResolving++;
980 rec->mTrrAttempts++;
981 rec->StoreNative(false);
982 return NS_OK;
985 nsresult nsHostResolver::NativeLookup(nsHostRecord* aRec,
986 const MutexAutoLock& aLock) {
987 if (StaticPrefs::network_dns_disabled()) {
988 return NS_ERROR_UNKNOWN_HOST;
990 LOG(("NativeLookup host:%s af:%" PRId16, aRec->host.get(), aRec->af));
992 // If this is not a A/AAAA request, make sure native HTTPS is enabled.
993 MOZ_ASSERT(aRec->IsAddrRecord() || IsNativeHTTPSEnabled());
994 mLock.AssertCurrentThreadOwns();
996 RefPtr<nsHostRecord> rec(aRec);
998 rec->mNativeStart = TimeStamp::Now();
1000 // Add rec to one of the pending queues, possibly removing it from mEvictionQ.
1001 MaybeRenewHostRecordLocked(aRec, aLock);
1003 mQueue.InsertRecord(rec, rec->flags, aLock);
1005 rec->StoreNative(true);
1006 rec->StoreNativeUsed(true);
1007 rec->mResolving++;
1009 nsresult rv = ConditionallyCreateThread(rec);
1011 LOG((" DNS thread counters: total=%d any-live=%d idle=%d pending=%d\n",
1012 static_cast<uint32_t>(mActiveTaskCount),
1013 static_cast<uint32_t>(mActiveAnyThreadCount),
1014 static_cast<uint32_t>(mNumIdleTasks), mQueue.PendingCount()));
1016 return rv;
1019 // static
1020 nsIDNSService::ResolverMode nsHostResolver::Mode() {
1021 if (TRRService::Get()) {
1022 return TRRService::Get()->Mode();
1025 // If we don't have a TRR service just return MODE_TRROFF so we don't make
1026 // any TRR requests by mistake.
1027 return nsIDNSService::MODE_TRROFF;
1030 nsIRequest::TRRMode nsHostRecord::TRRMode() {
1031 return nsIDNSService::GetTRRModeFromFlags(flags);
1034 // static
1035 void nsHostResolver::ComputeEffectiveTRRMode(nsHostRecord* aRec) {
1036 nsIDNSService::ResolverMode resolverMode = nsHostResolver::Mode();
1037 nsIRequest::TRRMode requestMode = aRec->TRRMode();
1039 // For domains that are excluded from TRR or when parental control is enabled,
1040 // we fallback to NativeLookup. This happens even in MODE_TRRONLY. By default
1041 // localhost and local are excluded (so we cover *.local hosts) See the
1042 // network.trr.excluded-domains pref.
1044 if (!TRRService::Get()) {
1045 aRec->RecordReason(TRRSkippedReason::TRR_NO_GSERVICE);
1046 aRec->mEffectiveTRRMode = requestMode;
1047 return;
1050 if (!aRec->mTrrServer.IsEmpty()) {
1051 aRec->mEffectiveTRRMode = nsIRequest::TRR_ONLY_MODE;
1052 return;
1055 if (TRRService::Get()->IsExcludedFromTRR(aRec->host)) {
1056 aRec->RecordReason(TRRSkippedReason::TRR_EXCLUDED);
1057 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1058 return;
1061 if (TRRService::Get()->ParentalControlEnabled()) {
1062 aRec->RecordReason(TRRSkippedReason::TRR_PARENTAL_CONTROL);
1063 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1064 return;
1067 if (resolverMode == nsIDNSService::MODE_TRROFF) {
1068 aRec->RecordReason(TRRSkippedReason::TRR_OFF_EXPLICIT);
1069 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1070 return;
1073 if (requestMode == nsIRequest::TRR_DISABLED_MODE) {
1074 aRec->RecordReason(TRRSkippedReason::TRR_REQ_MODE_DISABLED);
1075 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1076 return;
1079 if ((requestMode == nsIRequest::TRR_DEFAULT_MODE &&
1080 resolverMode == nsIDNSService::MODE_NATIVEONLY)) {
1081 if (StaticPrefs::network_trr_display_fallback_warning()) {
1082 TRRSkippedReason heuristicResult =
1083 TRRService::Get()->GetHeuristicDetectionResult();
1084 if (heuristicResult != TRRSkippedReason::TRR_UNSET &&
1085 heuristicResult != TRRSkippedReason::TRR_OK) {
1086 aRec->RecordReason(heuristicResult);
1087 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1088 return;
1091 aRec->RecordReason(TRRSkippedReason::TRR_MODE_NOT_ENABLED);
1092 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1093 return;
1096 if (requestMode == nsIRequest::TRR_DEFAULT_MODE &&
1097 resolverMode == nsIDNSService::MODE_TRRFIRST) {
1098 aRec->mEffectiveTRRMode = nsIRequest::TRR_FIRST_MODE;
1099 return;
1102 if (requestMode == nsIRequest::TRR_DEFAULT_MODE &&
1103 resolverMode == nsIDNSService::MODE_TRRONLY) {
1104 aRec->mEffectiveTRRMode = nsIRequest::TRR_ONLY_MODE;
1105 return;
1108 aRec->mEffectiveTRRMode = requestMode;
1111 // Kick-off a name resolve operation, using native resolver and/or TRR
1112 nsresult nsHostResolver::NameLookup(nsHostRecord* rec,
1113 const mozilla::MutexAutoLock& aLock) {
1114 LOG(("NameLookup host:%s af:%" PRId16, rec->host.get(), rec->af));
1115 mLock.AssertCurrentThreadOwns();
1117 if (rec->flags & RES_IP_HINT) {
1118 LOG(("Skip lookup if RES_IP_HINT is set\n"));
1119 return NS_ERROR_UNKNOWN_HOST;
1122 nsresult rv = NS_ERROR_UNKNOWN_HOST;
1123 if (rec->mResolving) {
1124 LOG(("NameLookup %s while already resolving\n", rec->host.get()));
1125 return NS_OK;
1128 // Make sure we reset the reason each time we attempt to do a new lookup
1129 // so we don't wrongly report the reason for the previous one.
1130 rec->Reset();
1132 ComputeEffectiveTRRMode(rec);
1134 if (!rec->mTrrServer.IsEmpty()) {
1135 LOG(("NameLookup: %s use trr:%s", rec->host.get(), rec->mTrrServer.get()));
1136 if (rec->mEffectiveTRRMode != nsIRequest::TRR_ONLY_MODE) {
1137 return NS_ERROR_UNKNOWN_HOST;
1140 if (rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR) {
1141 LOG(("TRR with server and DISABLE_TRR flag. Returning error."));
1142 return NS_ERROR_UNKNOWN_HOST;
1144 return TrrLookup(rec, aLock);
1147 LOG(("NameLookup: %s effectiveTRRmode: %d flags: %X", rec->host.get(),
1148 static_cast<nsIRequest::TRRMode>(rec->mEffectiveTRRMode), rec->flags));
1150 if (rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR) {
1151 rec->RecordReason(TRRSkippedReason::TRR_DISABLED_FLAG);
1154 bool serviceNotReady = !TRRServiceEnabledForRecord(rec);
1156 if (rec->mEffectiveTRRMode != nsIRequest::TRR_DISABLED_MODE &&
1157 !((rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR)) &&
1158 !serviceNotReady) {
1159 rv = TrrLookup(rec, aLock);
1162 if (rec->mEffectiveTRRMode == nsIRequest::TRR_DISABLED_MODE ||
1163 (rec->mEffectiveTRRMode == nsIRequest::TRR_FIRST_MODE &&
1164 (rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR || serviceNotReady ||
1165 NS_FAILED(rv)))) {
1166 if (!IsNativeHTTPSEnabled() && !rec->IsAddrRecord()) {
1167 return rv;
1170 #ifdef DEBUG
1171 // If we use this branch then the mTRRUsed flag should not be set
1172 // Even if we did call TrrLookup above, the fact that it failed sync-ly
1173 // means that we didn't actually succeed in opening the channel.
1174 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
1175 MOZ_ASSERT_IF(addrRec, addrRec->mResolverType == DNSResolverType::Native);
1176 #endif
1178 // We did not lookup via TRR - don't fallback to native if the
1179 // network.trr.display_fallback_warning pref is set and either
1180 // 1. we are in TRR first mode and confirmation failed
1181 // 2. the record has trr_disabled and a heuristic skip reason
1182 if (StaticPrefs::network_trr_display_fallback_warning() &&
1183 rec->mEffectiveTRRMode != nsIRequest::TRR_ONLY_MODE) {
1184 if ((rec->mEffectiveTRRMode == nsIRequest::TRR_FIRST_MODE &&
1185 rec->mTRRSkippedReason == TRRSkippedReason::TRR_NOT_CONFIRMED) ||
1186 (rec->mEffectiveTRRMode == nsIRequest::TRR_DISABLED_MODE &&
1187 rec->mTRRSkippedReason >=
1188 nsITRRSkipReason::TRR_HEURISTIC_TRIPPED_GOOGLE_SAFESEARCH &&
1189 rec->mTRRSkippedReason <=
1190 nsITRRSkipReason::TRR_HEURISTIC_TRIPPED_NRPT)) {
1191 LOG((
1192 "NameLookup: ResolveHostComplete with status NS_ERROR_UNKNOWN_HOST "
1193 "for: %s effectiveTRRmode: "
1194 "%d SkippedReason: %d",
1195 rec->host.get(),
1196 static_cast<nsIRequest::TRRMode>(rec->mEffectiveTRRMode),
1197 static_cast<int32_t>(rec->mTRRSkippedReason)));
1199 mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs =
1200 std::move(rec->mCallbacks);
1201 for (nsResolveHostCallback* c = cbs.getFirst(); c;
1202 c = c->removeAndGetNext()) {
1203 c->OnResolveHostComplete(this, rec, NS_ERROR_UNKNOWN_HOST);
1206 return NS_OK;
1210 rv = NativeLookup(rec, aLock);
1213 return rv;
1216 nsresult nsHostResolver::ConditionallyRefreshRecord(
1217 nsHostRecord* rec, const nsACString& host, const MutexAutoLock& aLock) {
1218 if ((rec->CheckExpiration(TimeStamp::NowLoRes()) != nsHostRecord::EXP_VALID ||
1219 rec->negative) &&
1220 !rec->mResolving && rec->RefreshForNegativeResponse()) {
1221 LOG((" Using %s cache entry for host [%s] but starting async renewal.",
1222 rec->negative ? "negative" : "positive", host.BeginReading()));
1223 NameLookup(rec, aLock);
1225 if (rec->IsAddrRecord() && !rec->negative) {
1226 // negative entries are constantly being refreshed, only
1227 // track positive grace period induced renewals
1228 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_RENEWAL);
1231 return NS_OK;
1234 bool nsHostResolver::GetHostToLookup(nsHostRecord** result) {
1235 bool timedOut = false;
1236 TimeDuration timeout;
1237 TimeStamp epoch, now;
1239 MutexAutoLock lock(mLock);
1241 timeout = (mNumIdleTasks >= MaxResolverThreadsAnyPriority())
1242 ? mShortIdleTimeout
1243 : mLongIdleTimeout;
1244 epoch = TimeStamp::Now();
1246 while (!mShutdown) {
1247 // remove next record from Q; hand over owning reference. Check high, then
1248 // med, then low
1250 #define SET_GET_TTL(var, val) (var)->StoreGetTtl(sGetTtlEnabled && (val))
1252 RefPtr<nsHostRecord> rec = mQueue.Dequeue(true, lock);
1253 if (rec) {
1254 SET_GET_TTL(rec, false);
1255 rec.forget(result);
1256 return true;
1259 if (mActiveAnyThreadCount < MaxResolverThreadsAnyPriority()) {
1260 rec = mQueue.Dequeue(false, lock);
1261 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
1262 if (addrRec) {
1263 MOZ_ASSERT(IsMediumPriority(addrRec->flags) ||
1264 IsLowPriority(addrRec->flags));
1265 mActiveAnyThreadCount++;
1266 addrRec->StoreUsingAnyThread(true);
1267 SET_GET_TTL(addrRec, true);
1268 addrRec.forget(result);
1269 return true;
1273 // Determining timeout is racy, so allow one cycle through checking the
1274 // queues before exiting.
1275 if (timedOut) {
1276 break;
1279 // wait for one or more of the following to occur:
1280 // (1) the pending queue has a host record to process
1281 // (2) the shutdown flag has been set
1282 // (3) the thread has been idle for too long
1284 mNumIdleTasks++;
1285 mIdleTaskCV.Wait(timeout);
1286 mNumIdleTasks--;
1288 now = TimeStamp::Now();
1290 if (now - epoch >= timeout) {
1291 timedOut = true;
1292 } else {
1293 // It is possible that CondVar::Wait() was interrupted and returned
1294 // early, in which case we will loop back and re-enter it. In that
1295 // case we want to do so with the new timeout reduced to reflect
1296 // time already spent waiting.
1297 timeout -= now - epoch;
1298 epoch = now;
1302 // tell thread to exit...
1303 return false;
1306 void nsHostResolver::PrepareRecordExpirationAddrRecord(
1307 AddrHostRecord* rec) const {
1308 // NOTE: rec->addr_info_lock is already held by parent
1309 MOZ_ASSERT(((bool)rec->addr_info) != rec->negative);
1310 mLock.AssertCurrentThreadOwns();
1311 if (!rec->addr_info) {
1312 rec->SetExpiration(TimeStamp::NowLoRes(), NEGATIVE_RECORD_LIFETIME, 0);
1313 LOG(("Caching host [%s] negative record for %u seconds.\n", rec->host.get(),
1314 NEGATIVE_RECORD_LIFETIME));
1315 return;
1318 unsigned int lifetime = mDefaultCacheLifetime;
1319 unsigned int grace = mDefaultGracePeriod;
1321 unsigned int ttl = mDefaultCacheLifetime;
1322 if (sGetTtlEnabled || rec->addr_info->IsTRR()) {
1323 if (rec->addr_info && rec->addr_info->TTL() != AddrInfo::NO_TTL_DATA) {
1324 ttl = rec->addr_info->TTL();
1326 lifetime = ttl;
1327 grace = 0;
1330 rec->SetExpiration(TimeStamp::NowLoRes(), lifetime, grace);
1331 LOG(("Caching host [%s] record for %u seconds (grace %d).", rec->host.get(),
1332 lifetime, grace));
1335 static bool different_rrset(AddrInfo* rrset1, AddrInfo* rrset2) {
1336 if (!rrset1 || !rrset2) {
1337 return true;
1340 LOG(("different_rrset %s\n", rrset1->Hostname().get()));
1342 if (rrset1->ResolverType() != rrset2->ResolverType()) {
1343 return true;
1346 if (rrset1->TRRType() != rrset2->TRRType()) {
1347 return true;
1350 if (rrset1->Addresses().Length() != rrset2->Addresses().Length()) {
1351 LOG(("different_rrset true due to length change\n"));
1352 return true;
1355 nsTArray<NetAddr> orderedSet1 = rrset1->Addresses().Clone();
1356 nsTArray<NetAddr> orderedSet2 = rrset2->Addresses().Clone();
1357 orderedSet1.Sort();
1358 orderedSet2.Sort();
1360 bool eq = orderedSet1 == orderedSet2;
1361 if (!eq) {
1362 LOG(("different_rrset true due to content change\n"));
1363 } else {
1364 LOG(("different_rrset false\n"));
1366 return !eq;
1369 void nsHostResolver::AddToEvictionQ(nsHostRecord* rec,
1370 const MutexAutoLock& aLock) {
1371 mQueue.AddToEvictionQ(rec, mMaxCacheEntries, mRecordDB, aLock);
1374 // After a first lookup attempt with TRR in mode 2, we may:
1375 // - If network.trr.retry_on_recoverable_errors is false, retry with native.
1376 // - If network.trr.retry_on_recoverable_errors is true:
1377 // - Retry with native if the first attempt failed because we got NXDOMAIN, an
1378 // unreachable address (TRR_DISABLED_FLAG), or we skipped TRR because
1379 // Confirmation failed.
1380 // - Trigger a "RetryTRR" Confirmation which will start a fresh
1381 // connection for TRR, and then retry the lookup with TRR.
1382 // - If the second attempt failed, fallback to native if
1383 // network.trr.strict_native_fallback is false.
1384 // Returns true if we retried with either TRR or Native.
1385 bool nsHostResolver::MaybeRetryTRRLookup(
1386 AddrHostRecord* aAddrRec, nsresult aFirstAttemptStatus,
1387 TRRSkippedReason aFirstAttemptSkipReason, nsresult aChannelStatus,
1388 const MutexAutoLock& aLock) {
1389 if (NS_FAILED(aFirstAttemptStatus) &&
1390 (aChannelStatus == NS_ERROR_PROXY_UNAUTHORIZED ||
1391 aChannelStatus == NS_ERROR_PROXY_AUTHENTICATION_FAILED) &&
1392 aAddrRec->mEffectiveTRRMode == nsIRequest::TRR_ONLY_MODE) {
1393 LOG(("MaybeRetryTRRLookup retry because of proxy connect failed"));
1394 TRRService::Get()->DontUseTRRThread();
1395 return DoRetryTRR(aAddrRec, aLock);
1398 if (NS_SUCCEEDED(aFirstAttemptStatus) ||
1399 aAddrRec->mEffectiveTRRMode != nsIRequest::TRR_FIRST_MODE ||
1400 aFirstAttemptStatus == NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
1401 return false;
1404 MOZ_ASSERT(!aAddrRec->mResolving);
1405 if (!StaticPrefs::network_trr_retry_on_recoverable_errors()) {
1406 LOG(("nsHostResolver::MaybeRetryTRRLookup retrying with native"));
1407 return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock));
1410 if (IsFailedConfirmationOrNoConnectivity(aFirstAttemptSkipReason) ||
1411 IsNonRecoverableTRRSkipReason(aFirstAttemptSkipReason) ||
1412 IsBlockedTRRRequest(aFirstAttemptSkipReason)) {
1413 LOG(
1414 ("nsHostResolver::MaybeRetryTRRLookup retrying with native in strict "
1415 "mode, skip reason was %d",
1416 static_cast<uint32_t>(aFirstAttemptSkipReason)));
1417 return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock));
1420 if (aAddrRec->mTrrAttempts > 1) {
1421 if (!StaticPrefs::network_trr_strict_native_fallback()) {
1422 LOG(
1423 ("nsHostResolver::MaybeRetryTRRLookup retry failed. Using "
1424 "native."));
1425 return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock));
1428 if (aFirstAttemptSkipReason == TRRSkippedReason::TRR_TIMEOUT &&
1429 StaticPrefs::network_trr_strict_native_fallback_allow_timeouts()) {
1430 LOG(
1431 ("nsHostResolver::MaybeRetryTRRLookup retry timed out. Using "
1432 "native."));
1433 return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock));
1435 LOG(("nsHostResolver::MaybeRetryTRRLookup mTrrAttempts>1, not retrying."));
1436 return false;
1439 LOG(
1440 ("nsHostResolver::MaybeRetryTRRLookup triggering Confirmation and "
1441 "retrying with TRR, skip reason was %d",
1442 static_cast<uint32_t>(aFirstAttemptSkipReason)));
1443 TRRService::Get()->RetryTRRConfirm();
1445 return DoRetryTRR(aAddrRec, aLock);
1448 bool nsHostResolver::DoRetryTRR(AddrHostRecord* aAddrRec,
1449 const mozilla::MutexAutoLock& aLock) {
1451 // Clear out the old query
1452 auto trrQuery = aAddrRec->mTRRQuery.Lock();
1453 trrQuery.ref() = nullptr;
1456 if (NS_SUCCEEDED(TrrLookup(aAddrRec, aLock, nullptr /* pushedTRR */))) {
1457 aAddrRec->NotifyRetryingTrr();
1458 return true;
1461 return false;
1465 // CompleteLookup() checks if the resolving should be redone and if so it
1466 // returns LOOKUP_RESOLVEAGAIN, but only if 'status' is not NS_ERROR_ABORT.
1467 nsHostResolver::LookupStatus nsHostResolver::CompleteLookup(
1468 nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb,
1469 const nsACString& aOriginsuffix, TRRSkippedReason aReason,
1470 mozilla::net::TRR* aTRRRequest) {
1471 MutexAutoLock lock(mLock);
1472 return CompleteLookupLocked(rec, status, aNewRRSet, pb, aOriginsuffix,
1473 aReason, aTRRRequest, lock);
1476 nsHostResolver::LookupStatus nsHostResolver::CompleteLookupLocked(
1477 nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb,
1478 const nsACString& aOriginsuffix, TRRSkippedReason aReason,
1479 mozilla::net::TRR* aTRRRequest, const mozilla::MutexAutoLock& aLock) {
1480 MOZ_ASSERT(rec);
1481 MOZ_ASSERT(rec->pb == pb);
1482 MOZ_ASSERT(rec->IsAddrRecord());
1484 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
1485 MOZ_ASSERT(addrRec);
1487 RefPtr<AddrInfo> newRRSet(aNewRRSet);
1488 MOZ_ASSERT(NS_FAILED(status) || newRRSet->Addresses().Length() > 0);
1490 DNSResolverType type =
1491 newRRSet ? newRRSet->ResolverType() : DNSResolverType::Native;
1493 if (NS_FAILED(status)) {
1494 newRRSet = nullptr;
1497 if (addrRec->LoadResolveAgain() && (status != NS_ERROR_ABORT) &&
1498 type == DNSResolverType::Native) {
1499 LOG(("nsHostResolver record %p resolve again due to flushcache\n",
1500 addrRec.get()));
1501 addrRec->StoreResolveAgain(false);
1502 return LOOKUP_RESOLVEAGAIN;
1505 MOZ_ASSERT(addrRec->mResolving);
1506 addrRec->mResolving--;
1507 LOG((
1508 "nsHostResolver::CompleteLookup %s %p %X resolver=%d stillResolving=%d\n",
1509 addrRec->host.get(), aNewRRSet, (unsigned int)status, (int)type,
1510 int(addrRec->mResolving)));
1512 if (type != DNSResolverType::Native) {
1513 if (NS_FAILED(status) && status != NS_ERROR_UNKNOWN_HOST &&
1514 status != NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
1515 // the errors are not failed resolves, that means
1516 // something else failed, consider this as *TRR not used*
1517 // for actually trying to resolve the host
1518 addrRec->mResolverType = DNSResolverType::Native;
1521 if (NS_FAILED(status)) {
1522 if (aReason != TRRSkippedReason::TRR_UNSET) {
1523 addrRec->RecordReason(aReason);
1524 } else {
1525 // Unknown failed reason.
1526 addrRec->RecordReason(TRRSkippedReason::TRR_FAILED);
1528 } else {
1529 addrRec->mTRRSuccess = true;
1530 addrRec->RecordReason(TRRSkippedReason::TRR_OK);
1533 nsresult channelStatus = aTRRRequest->ChannelStatus();
1534 if (MaybeRetryTRRLookup(addrRec, status, aReason, channelStatus, aLock)) {
1535 MOZ_ASSERT(addrRec->mResolving);
1536 return LOOKUP_OK;
1539 if (!addrRec->mTRRSuccess) {
1540 // no TRR success
1541 newRRSet = nullptr;
1544 if (NS_FAILED(status)) {
1545 // This is the error that consumers expect.
1546 status = NS_ERROR_UNKNOWN_HOST;
1548 } else { // native resolve completed
1549 if (addrRec->LoadUsingAnyThread()) {
1550 mActiveAnyThreadCount--;
1551 addrRec->StoreUsingAnyThread(false);
1554 addrRec->mNativeSuccess = static_cast<bool>(newRRSet);
1555 if (addrRec->mNativeSuccess) {
1556 addrRec->mNativeDuration = TimeStamp::Now() - addrRec->mNativeStart;
1560 addrRec->OnCompleteLookup();
1562 // update record fields. We might have a addrRec->addr_info already if a
1563 // previous lookup result expired and we're reresolving it or we get
1564 // a late second TRR response.
1565 if (!mShutdown) {
1566 MutexAutoLock lock(addrRec->addr_info_lock);
1567 RefPtr<AddrInfo> old_addr_info;
1568 if (different_rrset(addrRec->addr_info, newRRSet)) {
1569 LOG(("nsHostResolver record %p new gencnt\n", addrRec.get()));
1570 old_addr_info = addrRec->addr_info;
1571 addrRec->addr_info = std::move(newRRSet);
1572 addrRec->addr_info_gencnt++;
1573 } else {
1574 if (addrRec->addr_info && newRRSet) {
1575 auto builder = addrRec->addr_info->Build();
1576 builder.SetTTL(newRRSet->TTL());
1577 // Update trr timings
1578 builder.SetTrrFetchDuration(newRRSet->GetTrrFetchDuration());
1579 builder.SetTrrFetchDurationNetworkOnly(
1580 newRRSet->GetTrrFetchDurationNetworkOnly());
1582 addrRec->addr_info = builder.Finish();
1584 old_addr_info = std::move(newRRSet);
1586 addrRec->negative = !addrRec->addr_info;
1587 PrepareRecordExpirationAddrRecord(addrRec);
1590 if (LOG_ENABLED()) {
1591 MutexAutoLock lock(addrRec->addr_info_lock);
1592 if (addrRec->addr_info) {
1593 for (const auto& elem : addrRec->addr_info->Addresses()) {
1594 char buf[128];
1595 elem.ToStringBuffer(buf, sizeof(buf));
1596 LOG(("CompleteLookup: %s has %s\n", addrRec->host.get(), buf));
1598 } else {
1599 LOG(("CompleteLookup: %s has NO address\n", addrRec->host.get()));
1603 // get the list of pending callbacks for this lookup, and notify
1604 // them that the lookup is complete.
1605 mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs =
1606 std::move(rec->mCallbacks);
1608 LOG(("nsHostResolver record %p calling back dns users status:%X\n",
1609 addrRec.get(), int(status)));
1611 for (nsResolveHostCallback* c = cbs.getFirst(); c;
1612 c = c->removeAndGetNext()) {
1613 c->OnResolveHostComplete(this, rec, status);
1616 OnResolveComplete(rec, aLock);
1618 #ifdef DNSQUERY_AVAILABLE
1619 // Unless the result is from TRR, resolve again to get TTL
1620 bool hasNativeResult = false;
1622 MutexAutoLock lock(addrRec->addr_info_lock);
1623 if (addrRec->addr_info && !addrRec->addr_info->IsTRR()) {
1624 hasNativeResult = true;
1627 if (hasNativeResult && !mShutdown && !addrRec->LoadGetTtl() &&
1628 !rec->mResolving && sGetTtlEnabled) {
1629 LOG(("Issuing second async lookup for TTL for host [%s].",
1630 addrRec->host.get()));
1631 addrRec->flags =
1632 (addrRec->flags & ~nsIDNSService::RESOLVE_PRIORITY_MEDIUM) |
1633 nsIDNSService::RESOLVE_PRIORITY_LOW;
1634 DebugOnly<nsresult> rv = NativeLookup(rec, aLock);
1635 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1636 "Could not issue second async lookup for TTL.");
1638 #endif
1639 return LOOKUP_OK;
1642 nsHostResolver::LookupStatus nsHostResolver::CompleteLookupByType(
1643 nsHostRecord* rec, nsresult status,
1644 mozilla::net::TypeRecordResultType& aResult, TRRSkippedReason aReason,
1645 uint32_t aTtl, bool pb) {
1646 MutexAutoLock lock(mLock);
1647 return CompleteLookupByTypeLocked(rec, status, aResult, aReason, aTtl, pb,
1648 lock);
1651 nsHostResolver::LookupStatus nsHostResolver::CompleteLookupByTypeLocked(
1652 nsHostRecord* rec, nsresult status,
1653 mozilla::net::TypeRecordResultType& aResult, TRRSkippedReason aReason,
1654 uint32_t aTtl, bool pb, const mozilla::MutexAutoLock& aLock) {
1655 MOZ_ASSERT(rec);
1656 MOZ_ASSERT(rec->pb == pb);
1657 MOZ_ASSERT(!rec->IsAddrRecord());
1659 RefPtr<TypeHostRecord> typeRec = do_QueryObject(rec);
1660 MOZ_ASSERT(typeRec);
1662 MOZ_ASSERT(typeRec->mResolving);
1663 typeRec->mResolving--;
1665 if (NS_FAILED(status)) {
1666 LOG(("nsHostResolver::CompleteLookupByType record %p [%s] status %x\n",
1667 typeRec.get(), typeRec->host.get(), (unsigned int)status));
1668 typeRec->SetExpiration(
1669 TimeStamp::NowLoRes(),
1670 StaticPrefs::network_dns_negative_ttl_for_type_record(), 0);
1671 MOZ_ASSERT(aResult.is<TypeRecordEmpty>());
1672 status = NS_ERROR_UNKNOWN_HOST;
1673 typeRec->negative = true;
1674 if (aReason != TRRSkippedReason::TRR_UNSET) {
1675 typeRec->RecordReason(aReason);
1676 } else {
1677 // Unknown failed reason.
1678 typeRec->RecordReason(TRRSkippedReason::TRR_FAILED);
1680 } else {
1681 size_t recordCount = 0;
1682 if (aResult.is<TypeRecordTxt>()) {
1683 recordCount = aResult.as<TypeRecordTxt>().Length();
1684 } else if (aResult.is<TypeRecordHTTPSSVC>()) {
1685 recordCount = aResult.as<TypeRecordHTTPSSVC>().Length();
1687 LOG(
1688 ("nsHostResolver::CompleteLookupByType record %p [%s], number of "
1689 "records %zu\n",
1690 typeRec.get(), typeRec->host.get(), recordCount));
1691 MutexAutoLock typeLock(typeRec->mResultsLock);
1692 typeRec->mResults = aResult;
1693 typeRec->SetExpiration(TimeStamp::NowLoRes(), aTtl, mDefaultGracePeriod);
1694 typeRec->negative = false;
1695 typeRec->mTRRSuccess = !rec->LoadNative();
1696 typeRec->mNativeSuccess = rec->LoadNative();
1697 MOZ_ASSERT(aReason != TRRSkippedReason::TRR_UNSET);
1698 typeRec->RecordReason(aReason);
1701 mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs =
1702 std::move(typeRec->mCallbacks);
1704 LOG(
1705 ("nsHostResolver::CompleteLookupByType record %p calling back dns "
1706 "users\n",
1707 typeRec.get()));
1709 for (nsResolveHostCallback* c = cbs.getFirst(); c;
1710 c = c->removeAndGetNext()) {
1711 c->OnResolveHostComplete(this, rec, status);
1714 OnResolveComplete(rec, aLock);
1716 return LOOKUP_OK;
1719 void nsHostResolver::OnResolveComplete(nsHostRecord* aRec,
1720 const mozilla::MutexAutoLock& aLock) {
1721 if (!aRec->mResolving && !mShutdown) {
1723 auto trrQuery = aRec->mTRRQuery.Lock();
1724 if (trrQuery.ref()) {
1725 aRec->mTrrDuration = trrQuery.ref()->Duration();
1727 trrQuery.ref() = nullptr;
1729 aRec->ResolveComplete();
1731 AddToEvictionQ(aRec, aLock);
1735 void nsHostResolver::CancelAsyncRequest(
1736 const nsACString& host, const nsACString& aTrrServer, uint16_t aType,
1737 const OriginAttributes& aOriginAttributes, nsIDNSService::DNSFlags flags,
1738 uint16_t af, nsIDNSListener* aListener, nsresult status)
1741 MutexAutoLock lock(mLock);
1743 nsAutoCString originSuffix;
1744 aOriginAttributes.CreateSuffix(originSuffix);
1746 // Lookup the host record associated with host, flags & address family
1748 nsHostKey key(host, aTrrServer, aType, flags, af,
1749 (aOriginAttributes.mPrivateBrowsingId > 0), originSuffix);
1750 RefPtr<nsHostRecord> rec = mRecordDB.Get(key);
1751 if (!rec) {
1752 return;
1755 for (RefPtr<nsResolveHostCallback> c : rec->mCallbacks) {
1756 if (c->EqualsAsyncListener(aListener)) {
1757 c->remove();
1758 c->OnResolveHostComplete(this, rec.get(), status);
1759 break;
1763 // If there are no more callbacks, remove the hash table entry
1764 if (rec->mCallbacks.isEmpty()) {
1765 mRecordDB.Remove(*static_cast<nsHostKey*>(rec.get()));
1766 // If record is on a Queue, remove it
1767 mQueue.MaybeRemoveFromQ(rec, lock);
1771 size_t nsHostResolver::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const {
1772 MutexAutoLock lock(mLock);
1774 size_t n = mallocSizeOf(this);
1776 n += mRecordDB.ShallowSizeOfExcludingThis(mallocSizeOf);
1777 for (const auto& entry : mRecordDB.Values()) {
1778 n += entry->SizeOfIncludingThis(mallocSizeOf);
1781 // The following fields aren't measured.
1782 // - mHighQ, mMediumQ, mLowQ, mEvictionQ, because they just point to
1783 // nsHostRecords that also pointed to by entries |mRecordDB|, and
1784 // measured when |mRecordDB| is measured.
1786 return n;
1789 void nsHostResolver::ThreadFunc() {
1790 LOG(("DNS lookup thread - starting execution.\n"));
1792 #if defined(RES_RETRY_ON_FAILURE)
1793 nsResState rs;
1794 #endif
1795 RefPtr<nsHostRecord> rec;
1796 RefPtr<AddrInfo> ai;
1798 do {
1799 if (!rec) {
1800 RefPtr<nsHostRecord> tmpRec;
1801 if (!GetHostToLookup(getter_AddRefs(tmpRec))) {
1802 break; // thread shutdown signal
1804 // GetHostToLookup() returns an owning reference
1805 MOZ_ASSERT(tmpRec);
1806 rec.swap(tmpRec);
1809 LOG1(("DNS lookup thread - Calling getaddrinfo for host [%s].\n",
1810 rec->host.get()));
1812 TimeStamp startTime = TimeStamp::Now();
1813 bool getTtl = rec->LoadGetTtl();
1814 TimeDuration inQueue = startTime - rec->mNativeStart;
1815 uint32_t ms = static_cast<uint32_t>(inQueue.ToMilliseconds());
1816 Telemetry::Accumulate(Telemetry::DNS_NATIVE_QUEUING, ms);
1818 if (!rec->IsAddrRecord()) {
1819 LOG(("byType on DNS thread"));
1820 TypeRecordResultType result = AsVariant(mozilla::Nothing());
1821 uint32_t ttl = UINT32_MAX;
1822 nsresult status = ResolveHTTPSRecord(rec->host, rec->flags, result, ttl);
1823 CompleteLookupByType(rec, status, result, rec->mTRRSkippedReason, ttl,
1824 rec->pb);
1825 rec = nullptr;
1826 continue;
1829 nsresult status =
1830 GetAddrInfo(rec->host, rec->af, rec->flags, getter_AddRefs(ai), getTtl);
1831 #if defined(RES_RETRY_ON_FAILURE)
1832 if (NS_FAILED(status) && rs.Reset()) {
1833 status = GetAddrInfo(rec->host, rec->af, rec->flags, getter_AddRefs(ai),
1834 getTtl);
1836 #endif
1838 if (RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec)) {
1839 // obtain lock to check shutdown and manage inter-module telemetry
1840 MutexAutoLock lock(mLock);
1842 if (!mShutdown) {
1843 TimeDuration elapsed = TimeStamp::Now() - startTime;
1844 if (NS_SUCCEEDED(status)) {
1845 if (!addrRec->addr_info_gencnt) {
1846 // Time for initial lookup.
1847 glean::networking::dns_lookup_time.AccumulateRawDuration(elapsed);
1848 } else if (!getTtl) {
1849 // Time for renewal; categorized by expiration strategy.
1850 glean::networking::dns_renewal_time.AccumulateRawDuration(elapsed);
1851 } else {
1852 // Time to get TTL; categorized by expiration strategy.
1853 glean::networking::dns_renewal_time_for_ttl.AccumulateRawDuration(
1854 elapsed);
1856 } else {
1857 glean::networking::dns_failed_lookup_time.AccumulateRawDuration(
1858 elapsed);
1863 LOG1(("DNS lookup thread - lookup completed for host [%s]: %s.\n",
1864 rec->host.get(), ai ? "success" : "failure: unknown host"));
1866 if (LOOKUP_RESOLVEAGAIN ==
1867 CompleteLookup(rec, status, ai, rec->pb, rec->originSuffix,
1868 rec->mTRRSkippedReason, nullptr)) {
1869 // leave 'rec' assigned and loop to make a renewed host resolve
1870 LOG(("DNS lookup thread - Re-resolving host [%s].\n", rec->host.get()));
1871 } else {
1872 rec = nullptr;
1874 } while (true);
1876 MutexAutoLock lock(mLock);
1877 mActiveTaskCount--;
1878 LOG(("DNS lookup thread - queue empty, task finished.\n"));
1881 void nsHostResolver::SetCacheLimits(uint32_t aMaxCacheEntries,
1882 uint32_t aDefaultCacheEntryLifetime,
1883 uint32_t aDefaultGracePeriod) {
1884 MutexAutoLock lock(mLock);
1885 mMaxCacheEntries = aMaxCacheEntries;
1886 mDefaultCacheLifetime = aDefaultCacheEntryLifetime;
1887 mDefaultGracePeriod = aDefaultGracePeriod;
1890 nsresult nsHostResolver::Create(uint32_t maxCacheEntries,
1891 uint32_t defaultCacheEntryLifetime,
1892 uint32_t defaultGracePeriod,
1893 nsHostResolver** result) {
1894 RefPtr<nsHostResolver> res = new nsHostResolver(
1895 maxCacheEntries, defaultCacheEntryLifetime, defaultGracePeriod);
1897 nsresult rv = res->Init();
1898 if (NS_FAILED(rv)) {
1899 return rv;
1902 res.forget(result);
1903 return NS_OK;
1906 void nsHostResolver::GetDNSCacheEntries(nsTArray<DNSCacheEntries>* args) {
1907 MutexAutoLock lock(mLock);
1908 for (const auto& recordEntry : mRecordDB) {
1909 // We don't pay attention to address literals, only resolved domains.
1910 // Also require a host.
1911 nsHostRecord* rec = recordEntry.GetWeak();
1912 MOZ_ASSERT(rec, "rec should never be null here!");
1914 if (!rec) {
1915 continue;
1918 // For now we only show A/AAAA records.
1919 if (!rec->IsAddrRecord()) {
1920 continue;
1923 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
1924 MOZ_ASSERT(addrRec);
1925 if (!addrRec || !addrRec->addr_info) {
1926 continue;
1929 DNSCacheEntries info;
1930 info.hostname = rec->host;
1931 info.family = rec->af;
1932 info.expiration =
1933 (int64_t)(rec->mValidEnd - TimeStamp::NowLoRes()).ToSeconds();
1934 if (info.expiration <= 0) {
1935 // We only need valid DNS cache entries
1936 continue;
1940 MutexAutoLock lock(addrRec->addr_info_lock);
1941 for (const auto& addr : addrRec->addr_info->Addresses()) {
1942 char buf[kIPv6CStrBufSize];
1943 if (addr.ToStringBuffer(buf, sizeof(buf))) {
1944 info.hostaddr.AppendElement(buf);
1947 info.TRR = addrRec->addr_info->IsTRR();
1950 info.originAttributesSuffix = recordEntry.GetKey().originSuffix;
1951 info.flags = nsPrintfCString("%u|0x%x|%u|%d|%s", rec->type, rec->flags,
1952 rec->af, rec->pb, rec->mTrrServer.get());
1954 args->AppendElement(std::move(info));
1958 #undef LOG
1959 #undef LOG_ENABLED