Backed out 2 changesets (bug 1156659) for causing multiple dt failures on browser_jst...
[gecko.git] / netwerk / dns / nsHostResolver.cpp
blob202eb00b34d0b70695b012f7122421b2f77b9686
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 #define IS_ADDR_TYPE(_type) ((_type) == nsIDNSService::RESOLVE_TYPE_DEFAULT)
52 #define IS_OTHER_TYPE(_type) ((_type) != nsIDNSService::RESOLVE_TYPE_DEFAULT)
54 using namespace mozilla;
55 using namespace mozilla::net;
57 // None of our implementations expose a TTL for negative responses, so we use a
58 // constant always.
59 static const unsigned int NEGATIVE_RECORD_LIFETIME = 60;
61 //----------------------------------------------------------------------------
63 // Use a persistent thread pool in order to avoid spinning up new threads all
64 // the time. In particular, thread creation results in a res_init() call from
65 // libc which is quite expensive.
67 // The pool dynamically grows between 0 and MaxResolverThreads() in size. New
68 // requests go first to an idle thread. If that cannot be found and there are
69 // fewer than MaxResolverThreads() currently in the pool a new thread is created
70 // for high priority requests. If the new request is at a lower priority a new
71 // thread will only be created if there are fewer than
72 // MaxResolverThreadsAnyPriority() currently outstanding. If a thread cannot be
73 // created or an idle thread located for the request it is queued.
75 // When the pool is greater than MaxResolverThreadsAnyPriority() in size a
76 // thread will be destroyed after ShortIdleTimeoutSeconds of idle time. Smaller
77 // pools use LongIdleTimeoutSeconds for a timeout period.
79 // for threads 1 -> MaxResolverThreadsAnyPriority()
80 #define LongIdleTimeoutSeconds 300
81 // for threads MaxResolverThreadsAnyPriority() + 1 -> MaxResolverThreads()
82 #define ShortIdleTimeoutSeconds 60
84 //----------------------------------------------------------------------------
86 namespace mozilla::net {
87 LazyLogModule gHostResolverLog("nsHostResolver");
88 } // namespace mozilla::net
90 //----------------------------------------------------------------------------
92 #if defined(RES_RETRY_ON_FAILURE)
94 // this class represents the resolver state for a given thread. if we
95 // encounter a lookup failure, then we can invoke the Reset method on an
96 // instance of this class to reset the resolver (in case /etc/resolv.conf
97 // for example changed). this is mainly an issue on GNU systems since glibc
98 // only reads in /etc/resolv.conf once per thread. it may be an issue on
99 // other systems as well.
101 class nsResState {
102 public:
103 nsResState()
104 // initialize mLastReset to the time when this object
105 // is created. this means that a reset will not occur
106 // if a thread is too young. the alternative would be
107 // to initialize this to the beginning of time, so that
108 // the first failure would cause a reset, but since the
109 // thread would have just started up, it likely would
110 // already have current /etc/resolv.conf info.
111 : mLastReset(PR_IntervalNow()) {}
113 bool Reset() {
114 // reset no more than once per second
115 if (PR_IntervalToSeconds(PR_IntervalNow() - mLastReset) < 1) {
116 return false;
119 mLastReset = PR_IntervalNow();
120 auto result = res_ninit(&_res);
122 LOG(("nsResState::Reset() > 'res_ninit' returned %d", result));
123 return (result == 0);
126 private:
127 PRIntervalTime mLastReset;
130 #endif // RES_RETRY_ON_FAILURE
132 //----------------------------------------------------------------------------
134 static const char kPrefGetTtl[] = "network.dns.get-ttl";
135 static const char kPrefNativeIsLocalhost[] = "network.dns.native-is-localhost";
136 static const char kPrefThreadIdleTime[] =
137 "network.dns.resolver-thread-extra-idle-time-seconds";
138 static bool sGetTtlEnabled = false;
139 mozilla::Atomic<bool, mozilla::Relaxed> gNativeIsLocalhost;
141 static void DnsPrefChanged(const char* aPref, void* aSelf) {
142 MOZ_ASSERT(NS_IsMainThread(),
143 "Should be getting pref changed notification on main thread!");
145 MOZ_ASSERT(aSelf);
147 if (!strcmp(aPref, kPrefGetTtl)) {
148 #ifdef DNSQUERY_AVAILABLE
149 sGetTtlEnabled = Preferences::GetBool(kPrefGetTtl);
150 #endif
151 } else if (!strcmp(aPref, kPrefNativeIsLocalhost)) {
152 gNativeIsLocalhost = Preferences::GetBool(kPrefNativeIsLocalhost);
156 NS_IMPL_ISUPPORTS0(nsHostResolver)
158 nsHostResolver::nsHostResolver(uint32_t maxCacheEntries,
159 uint32_t defaultCacheEntryLifetime,
160 uint32_t defaultGracePeriod)
161 : mMaxCacheEntries(maxCacheEntries),
162 mDefaultCacheLifetime(defaultCacheEntryLifetime),
163 mDefaultGracePeriod(defaultGracePeriod),
164 mIdleTaskCV(mLock, "nsHostResolver.mIdleTaskCV") {
165 mCreationTime = PR_Now();
167 mLongIdleTimeout = TimeDuration::FromSeconds(LongIdleTimeoutSeconds);
168 mShortIdleTimeout = TimeDuration::FromSeconds(ShortIdleTimeoutSeconds);
171 nsHostResolver::~nsHostResolver() = default;
173 nsresult nsHostResolver::Init() MOZ_NO_THREAD_SAFETY_ANALYSIS {
174 MOZ_ASSERT(NS_IsMainThread());
175 if (NS_FAILED(GetAddrInfoInit())) {
176 return NS_ERROR_FAILURE;
179 LOG(("nsHostResolver::Init this=%p", this));
181 mShutdown = false;
182 mNCS = NetworkConnectivityService::GetSingleton();
184 // The preferences probably haven't been loaded from the disk yet, so we
185 // need to register a callback that will set up the experiment once they
186 // are. We also need to explicitly set a value for the props otherwise the
187 // callback won't be called.
189 DebugOnly<nsresult> rv = Preferences::RegisterCallbackAndCall(
190 &DnsPrefChanged, kPrefGetTtl, this);
191 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
192 "Could not register DNS TTL pref callback.");
193 rv = Preferences::RegisterCallbackAndCall(&DnsPrefChanged,
194 kPrefNativeIsLocalhost, this);
195 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
196 "Could not register DNS pref callback.");
199 #if defined(HAVE_RES_NINIT)
200 // We want to make sure the system is using the correct resolver settings,
201 // so we force it to reload those settings whenever we startup a subsequent
202 // nsHostResolver instance. We assume that there is no reason to do this
203 // for the first nsHostResolver instance since that is usually created
204 // during application startup.
205 static int initCount = 0;
206 if (initCount++ > 0) {
207 auto result = res_ninit(&_res);
208 LOG(("nsHostResolver::Init > 'res_ninit' returned %d", result));
210 #endif
212 // We can configure the threadpool to keep threads alive for a while after
213 // the last ThreadFunc task has been executed.
214 int32_t poolTimeoutSecs = Preferences::GetInt(kPrefThreadIdleTime, 60);
215 uint32_t poolTimeoutMs;
216 if (poolTimeoutSecs < 0) {
217 // This means never shut down the idle threads
218 poolTimeoutMs = UINT32_MAX;
219 } else {
220 // We clamp down the idle time between 0 and one hour.
221 poolTimeoutMs =
222 mozilla::clamped<uint32_t>(poolTimeoutSecs * 1000, 0, 3600 * 1000);
225 nsCOMPtr<nsIThreadPool> threadPool = new nsThreadPool();
226 MOZ_ALWAYS_SUCCEEDS(threadPool->SetThreadLimit(MaxResolverThreads()));
227 MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadLimit(MaxResolverThreads()));
228 MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadTimeout(poolTimeoutMs));
229 MOZ_ALWAYS_SUCCEEDS(
230 threadPool->SetThreadStackSize(nsIThreadManager::kThreadPoolStackSize));
231 MOZ_ALWAYS_SUCCEEDS(threadPool->SetName("DNS Resolver"_ns));
232 mResolverThreads = ToRefPtr(std::move(threadPool));
234 return NS_OK;
237 void nsHostResolver::ClearPendingQueue(
238 LinkedList<RefPtr<nsHostRecord>>& aPendingQ) {
239 // loop through pending queue, erroring out pending lookups.
240 if (!aPendingQ.isEmpty()) {
241 for (const RefPtr<nsHostRecord>& rec : aPendingQ) {
242 rec->Cancel();
243 if (rec->IsAddrRecord()) {
244 CompleteLookup(rec, NS_ERROR_ABORT, nullptr, rec->pb, rec->originSuffix,
245 rec->mTRRSkippedReason, nullptr);
246 } else {
247 mozilla::net::TypeRecordResultType empty(Nothing{});
248 CompleteLookupByType(rec, NS_ERROR_ABORT, empty, rec->mTRRSkippedReason,
249 0, rec->pb);
256 // FlushCache() is what we call when the network has changed. We must not
257 // trust names that were resolved before this change. They may resolve
258 // differently now.
260 // This function removes all existing resolved host entries from the hash.
261 // Names that are in the pending queues can be left there. Entries in the
262 // cache that have 'Resolve' set true but not 'OnQueue' are being resolved
263 // right now, so we need to mark them to get re-resolved on completion!
265 void nsHostResolver::FlushCache(bool aTrrToo) {
266 MutexAutoLock lock(mLock);
268 mQueue.FlushEvictionQ(mRecordDB, lock);
270 // Refresh the cache entries that are resolving RIGHT now, remove the rest.
271 for (auto iter = mRecordDB.Iter(); !iter.Done(); iter.Next()) {
272 nsHostRecord* record = iter.UserData();
273 // Try to remove the record, or mark it for refresh.
274 // By-type records are from TRR. We do not need to flush those entry
275 // when the network has change, because they are not local.
276 if (record->IsAddrRecord()) {
277 RefPtr<AddrHostRecord> addrRec = do_QueryObject(record);
278 MOZ_ASSERT(addrRec);
279 if (addrRec->RemoveOrRefresh(aTrrToo)) {
280 mQueue.MaybeRemoveFromQ(record, lock);
281 LOG(("Removing (%s) Addr record from mRecordDB", record->host.get()));
282 iter.Remove();
284 } else if (aTrrToo) {
285 // remove by type records
286 LOG(("Removing (%s) type record from mRecordDB", record->host.get()));
287 iter.Remove();
292 void nsHostResolver::Shutdown() {
293 LOG(("Shutting down host resolver.\n"));
296 DebugOnly<nsresult> rv =
297 Preferences::UnregisterCallback(&DnsPrefChanged, kPrefGetTtl, this);
298 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
299 "Could not unregister DNS TTL pref callback.");
302 LinkedList<RefPtr<nsHostRecord>> pendingQHigh, pendingQMed, pendingQLow,
303 evictionQ;
306 MutexAutoLock lock(mLock);
308 mShutdown = true;
310 if (mNumIdleTasks) {
311 mIdleTaskCV.NotifyAll();
314 mQueue.ClearAll(
315 [&](nsHostRecord* aRec) {
316 mLock.AssertCurrentThreadOwns();
317 if (aRec->IsAddrRecord()) {
318 CompleteLookupLocked(aRec, NS_ERROR_ABORT, nullptr, aRec->pb,
319 aRec->originSuffix, aRec->mTRRSkippedReason,
320 nullptr, lock);
321 } else {
322 mozilla::net::TypeRecordResultType empty(Nothing{});
323 CompleteLookupByTypeLocked(aRec, NS_ERROR_ABORT, empty,
324 aRec->mTRRSkippedReason, 0, aRec->pb,
325 lock);
328 lock);
330 for (const auto& data : mRecordDB.Values()) {
331 data->Cancel();
333 // empty host database
334 mRecordDB.Clear();
336 mNCS = nullptr;
339 // Shutdown the resolver threads, but with a timeout of 2 seconds (prefable).
340 // If the timeout is exceeded, any stuck threads will be leaked.
341 mResolverThreads->ShutdownWithTimeout(
342 StaticPrefs::network_dns_resolver_shutdown_timeout_ms());
345 mozilla::DebugOnly<nsresult> rv = GetAddrInfoShutdown();
346 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to shutdown GetAddrInfo");
350 nsresult nsHostResolver::GetHostRecord(
351 const nsACString& host, const nsACString& aTrrServer, uint16_t type,
352 nsIDNSService::DNSFlags flags, uint16_t af, bool pb,
353 const nsCString& originSuffix, nsHostRecord** result) {
354 MutexAutoLock lock(mLock);
355 nsHostKey key(host, aTrrServer, type, flags, af, pb, originSuffix);
357 RefPtr<nsHostRecord> rec =
358 mRecordDB.LookupOrInsertWith(key, [&] { return InitRecord(key); });
359 if (rec->IsAddrRecord()) {
360 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
361 if (addrRec->addr) {
362 return NS_ERROR_FAILURE;
366 if (rec->mResolving) {
367 return NS_ERROR_FAILURE;
370 *result = rec.forget().take();
371 return NS_OK;
374 nsHostRecord* nsHostResolver::InitRecord(const nsHostKey& key) {
375 if (IS_ADDR_TYPE(key.type)) {
376 return new AddrHostRecord(key);
378 return new TypeHostRecord(key);
381 already_AddRefed<nsHostRecord> nsHostResolver::InitLoopbackRecord(
382 const nsHostKey& key, nsresult* aRv) {
383 MOZ_ASSERT(aRv);
384 MOZ_ASSERT(IS_ADDR_TYPE(key.type));
386 *aRv = NS_ERROR_FAILURE;
387 RefPtr<nsHostRecord> rec = InitRecord(key);
389 nsTArray<NetAddr> addresses;
390 NetAddr addr;
391 if (key.af == PR_AF_INET || key.af == PR_AF_UNSPEC) {
392 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(addr.InitFromString("127.0.0.1"_ns)));
393 addresses.AppendElement(addr);
395 if (key.af == PR_AF_INET6 || key.af == PR_AF_UNSPEC) {
396 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(addr.InitFromString("::1"_ns)));
397 addresses.AppendElement(addr);
400 RefPtr<AddrInfo> ai =
401 new AddrInfo(rec->host, DNSResolverType::Native, 0, std::move(addresses));
403 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
404 MutexAutoLock lock(addrRec->addr_info_lock);
405 addrRec->addr_info = ai;
406 addrRec->SetExpiration(TimeStamp::NowLoRes(), mDefaultCacheLifetime,
407 mDefaultGracePeriod);
408 addrRec->negative = false;
410 *aRv = NS_OK;
411 return rec.forget();
414 nsresult nsHostResolver::ResolveHost(const nsACString& aHost,
415 const nsACString& aTrrServer,
416 int32_t aPort, uint16_t type,
417 const OriginAttributes& aOriginAttributes,
418 nsIDNSService::DNSFlags flags, uint16_t af,
419 nsResolveHostCallback* aCallback) {
420 nsAutoCString host(aHost);
421 NS_ENSURE_TRUE(!host.IsEmpty(), NS_ERROR_UNEXPECTED);
423 nsAutoCString originSuffix;
424 aOriginAttributes.CreateSuffix(originSuffix);
425 LOG(("Resolving host [%s]<%s>%s%s type %d. [this=%p]\n", host.get(),
426 originSuffix.get(), flags & RES_BYPASS_CACHE ? " - bypassing cache" : "",
427 flags & RES_REFRESH_CACHE ? " - refresh cache" : "", type, this));
429 // ensure that we are working with a valid hostname before proceeding. see
430 // bug 304904 for details.
431 if (!net_IsValidHostName(host)) {
432 return NS_ERROR_UNKNOWN_HOST;
435 // By-Type requests use only TRR. If TRR is disabled we can return
436 // immediately.
437 if (IS_OTHER_TYPE(type) && Mode() == nsIDNSService::MODE_TRROFF) {
438 return NS_ERROR_UNKNOWN_HOST;
441 // Used to try to parse to an IP address literal.
442 NetAddr tempAddr;
443 if (IS_OTHER_TYPE(type) && (NS_SUCCEEDED(tempAddr.InitFromString(host)))) {
444 // For by-type queries the host cannot be IP literal.
445 return NS_ERROR_UNKNOWN_HOST;
448 RefPtr<nsResolveHostCallback> callback(aCallback);
449 // if result is set inside the lock, then we need to issue the
450 // callback before returning.
451 RefPtr<nsHostRecord> result;
452 nsresult status = NS_OK, rv = NS_OK;
454 MutexAutoLock lock(mLock);
456 if (mShutdown) {
457 return NS_ERROR_NOT_INITIALIZED;
460 // check to see if there is already an entry for this |host|
461 // in the hash table. if so, then check to see if we can't
462 // just reuse the lookup result. otherwise, if there are
463 // any pending callbacks, then add to pending callbacks queue,
464 // and return. otherwise, add ourselves as first pending
465 // callback, and proceed to do the lookup.
467 Maybe<nsCString> originHost;
468 if (StaticPrefs::network_dns_port_prefixed_qname_https_rr() &&
469 type == nsIDNSService::RESOLVE_TYPE_HTTPSSVC && aPort != -1 &&
470 aPort != 443) {
471 originHost = Some(host);
472 host = nsPrintfCString("_%d._https.%s", aPort, host.get());
473 LOG((" Using port prefixed host name [%s]", host.get()));
476 bool excludedFromTRR = false;
477 if (TRRService::Get() && TRRService::Get()->IsExcludedFromTRR(host)) {
478 flags |= nsIDNSService::RESOLVE_DISABLE_TRR;
479 excludedFromTRR = true;
481 if (!aTrrServer.IsEmpty()) {
482 return NS_ERROR_UNKNOWN_HOST;
486 nsHostKey key(host, aTrrServer, type, flags, af,
487 (aOriginAttributes.mPrivateBrowsingId > 0), originSuffix);
489 // Check if we have a localhost domain, if so hardcode to loopback
490 if (IS_ADDR_TYPE(type) && IsLoopbackHostname(host)) {
491 nsresult rv;
492 RefPtr<nsHostRecord> result = InitLoopbackRecord(key, &rv);
493 if (NS_WARN_IF(NS_FAILED(rv))) {
494 return rv;
496 MOZ_ASSERT(result);
497 aCallback->OnResolveHostComplete(this, result, NS_OK);
498 return NS_OK;
501 RefPtr<nsHostRecord> rec =
502 mRecordDB.LookupOrInsertWith(key, [&] { return InitRecord(key); });
504 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
505 MOZ_ASSERT(rec, "Record should not be null");
506 MOZ_ASSERT((IS_ADDR_TYPE(type) && rec->IsAddrRecord() && addrRec) ||
507 (IS_OTHER_TYPE(type) && !rec->IsAddrRecord()));
509 if (IS_OTHER_TYPE(type) && originHost) {
510 RefPtr<TypeHostRecord> typeRec = do_QueryObject(rec);
511 typeRec->mOriginHost = std::move(originHost);
514 if (excludedFromTRR) {
515 rec->RecordReason(TRRSkippedReason::TRR_EXCLUDED);
518 if (!(flags & RES_BYPASS_CACHE) &&
519 rec->HasUsableResult(TimeStamp::NowLoRes(), flags)) {
520 result = FromCache(rec, host, type, status, lock);
521 } else if (addrRec && addrRec->addr) {
522 // if the host name is an IP address literal and has been
523 // parsed, go ahead and use it.
524 LOG((" Using cached address for IP Literal [%s].\n", host.get()));
525 result = FromCachedIPLiteral(rec);
526 } else if (addrRec && NS_SUCCEEDED(tempAddr.InitFromString(host))) {
527 // try parsing the host name as an IP address literal to short
528 // circuit full host resolution. (this is necessary on some
529 // platforms like Win9x. see bug 219376 for more details.)
530 LOG((" Host is IP Literal [%s].\n", host.get()));
531 result = FromIPLiteral(addrRec, tempAddr);
532 } else if (mQueue.PendingCount() >= MAX_NON_PRIORITY_REQUESTS &&
533 !IsHighPriority(flags) && !rec->mResolving) {
534 LOG(
535 (" Lookup queue full: dropping %s priority request for "
536 "host [%s].\n",
537 IsMediumPriority(flags) ? "medium" : "low", host.get()));
538 if (IS_ADDR_TYPE(type)) {
539 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_OVERFLOW);
541 // This is a lower priority request and we are swamped, so refuse it.
542 rv = NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
544 // Check if the offline flag is set.
545 } else if (flags & RES_OFFLINE) {
546 LOG((" Offline request for host [%s]; ignoring.\n", host.get()));
547 rv = NS_ERROR_OFFLINE;
549 // We do not have a valid result till here.
550 // A/AAAA request can check for an alternative entry like AF_UNSPEC.
551 // Otherwise we need to start a new query.
552 } else if (!rec->mResolving) {
553 result =
554 FromUnspecEntry(rec, host, aTrrServer, originSuffix, type, flags, af,
555 aOriginAttributes.mPrivateBrowsingId > 0, status);
556 // If this is a by-type request or if no valid record was found
557 // in the cache or this is an AF_UNSPEC request, then start a
558 // new lookup.
559 if (!result) {
560 LOG((" No usable record in cache for host [%s] type %d.", host.get(),
561 type));
563 if (flags & RES_REFRESH_CACHE) {
564 rec->Invalidate();
567 // Add callback to the list of pending callbacks.
568 rec->mCallbacks.insertBack(callback);
569 rec->flags = flags;
570 rv = NameLookup(rec, lock);
571 if (IS_ADDR_TYPE(type)) {
572 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
573 METHOD_NETWORK_FIRST);
575 if (NS_FAILED(rv) && callback->isInList()) {
576 callback->remove();
577 } else {
578 LOG(
579 (" DNS lookup for host [%s] blocking "
580 "pending 'getaddrinfo' or trr query: "
581 "callback [%p]",
582 host.get(), callback.get()));
585 } else {
586 LOG(
587 (" Host [%s] is being resolved. Appending callback "
588 "[%p].",
589 host.get(), callback.get()));
591 rec->mCallbacks.insertBack(callback);
593 // Only A/AAAA records are place in a queue. The queues are for
594 // the native resolver, therefore by-type request are never put
595 // into a queue.
596 if (addrRec && addrRec->onQueue()) {
597 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
598 METHOD_NETWORK_SHARED);
600 // Consider the case where we are on a pending queue of
601 // lower priority than the request is being made at.
602 // In that case we should upgrade to the higher queue.
604 if (IsHighPriority(flags) && !IsHighPriority(rec->flags)) {
605 // Move from (low|med) to high.
606 mQueue.MoveToAnotherPendingQ(rec, flags, lock);
607 rec->flags = flags;
608 ConditionallyCreateThread(rec);
609 } else if (IsMediumPriority(flags) && IsLowPriority(rec->flags)) {
610 // Move from low to med.
611 mQueue.MoveToAnotherPendingQ(rec, flags, lock);
612 rec->flags = flags;
613 mIdleTaskCV.Notify();
618 if (result && callback->isInList()) {
619 callback->remove();
621 } // lock
623 if (result) {
624 callback->OnResolveHostComplete(this, result, status);
627 return rv;
630 already_AddRefed<nsHostRecord> nsHostResolver::FromCache(
631 nsHostRecord* aRec, const nsACString& aHost, uint16_t aType,
632 nsresult& aStatus, const MutexAutoLock& aLock) {
633 LOG((" Using cached record for host [%s].\n",
634 nsPromiseFlatCString(aHost).get()));
636 // put reference to host record on stack...
637 RefPtr<nsHostRecord> result = aRec;
638 if (IS_ADDR_TYPE(aType)) {
639 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
642 // For entries that are in the grace period
643 // or all cached negative entries, use the cache but start a new
644 // lookup in the background
645 ConditionallyRefreshRecord(aRec, aHost, aLock);
647 if (aRec->negative) {
648 LOG((" Negative cache entry for host [%s].\n",
649 nsPromiseFlatCString(aHost).get()));
650 if (IS_ADDR_TYPE(aType)) {
651 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_NEGATIVE_HIT);
653 aStatus = NS_ERROR_UNKNOWN_HOST;
656 return result.forget();
659 already_AddRefed<nsHostRecord> nsHostResolver::FromCachedIPLiteral(
660 nsHostRecord* aRec) {
661 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_LITERAL);
662 RefPtr<nsHostRecord> result = aRec;
663 return result.forget();
666 already_AddRefed<nsHostRecord> nsHostResolver::FromIPLiteral(
667 AddrHostRecord* aAddrRec, const NetAddr& aAddr) {
668 // ok, just copy the result into the host record, and be
669 // done with it! ;-)
670 aAddrRec->addr = MakeUnique<NetAddr>(aAddr);
671 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_LITERAL);
672 // put reference to host record on stack...
673 RefPtr<nsHostRecord> result = aAddrRec;
674 return result.forget();
677 already_AddRefed<nsHostRecord> nsHostResolver::FromUnspecEntry(
678 nsHostRecord* aRec, const nsACString& aHost, const nsACString& aTrrServer,
679 const nsACString& aOriginSuffix, uint16_t aType,
680 nsIDNSService::DNSFlags aFlags, uint16_t af, bool aPb, nsresult& aStatus) {
681 RefPtr<nsHostRecord> result = nullptr;
682 // If this is an IPV4 or IPV6 specific request, check if there is
683 // an AF_UNSPEC entry we can use. Otherwise, hit the resolver...
684 RefPtr<AddrHostRecord> addrRec = do_QueryObject(aRec);
685 if (addrRec && !(aFlags & RES_BYPASS_CACHE) &&
686 ((af == PR_AF_INET) || (af == PR_AF_INET6))) {
687 // Check for an AF_UNSPEC entry.
689 const nsHostKey unspecKey(aHost, aTrrServer,
690 nsIDNSService::RESOLVE_TYPE_DEFAULT, aFlags,
691 PR_AF_UNSPEC, aPb, aOriginSuffix);
692 RefPtr<nsHostRecord> unspecRec = mRecordDB.Get(unspecKey);
694 TimeStamp now = TimeStamp::NowLoRes();
695 if (unspecRec && unspecRec->HasUsableResult(now, aFlags)) {
696 MOZ_ASSERT(unspecRec->IsAddrRecord());
698 RefPtr<AddrHostRecord> addrUnspecRec = do_QueryObject(unspecRec);
699 MOZ_ASSERT(addrUnspecRec);
700 MOZ_ASSERT(addrUnspecRec->addr_info || addrUnspecRec->negative,
701 "Entry should be resolved or negative.");
703 LOG((" Trying AF_UNSPEC entry for host [%s] af: %s.\n",
704 PromiseFlatCString(aHost).get(),
705 (af == PR_AF_INET) ? "AF_INET" : "AF_INET6"));
707 // We need to lock in case any other thread is reading
708 // addr_info.
709 MutexAutoLock lock(addrRec->addr_info_lock);
711 addrRec->addr_info = nullptr;
712 addrRec->addr_info_gencnt++;
713 if (unspecRec->negative) {
714 aRec->negative = unspecRec->negative;
715 aRec->CopyExpirationTimesAndFlagsFrom(unspecRec);
716 } else if (addrUnspecRec->addr_info) {
717 MutexAutoLock lock(addrUnspecRec->addr_info_lock);
718 if (addrUnspecRec->addr_info) {
719 // Search for any valid address in the AF_UNSPEC entry
720 // in the cache (not blocklisted and from the right
721 // family).
722 nsTArray<NetAddr> addresses;
723 for (const auto& addr : addrUnspecRec->addr_info->Addresses()) {
724 if ((af == addr.inet.family) &&
725 !addrUnspecRec->Blocklisted(&addr)) {
726 addresses.AppendElement(addr);
729 if (!addresses.IsEmpty()) {
730 addrRec->addr_info = new AddrInfo(
731 addrUnspecRec->addr_info->Hostname(),
732 addrUnspecRec->addr_info->CanonicalHostname(),
733 addrUnspecRec->addr_info->ResolverType(),
734 addrUnspecRec->addr_info->TRRType(), std::move(addresses));
735 addrRec->addr_info_gencnt++;
736 aRec->CopyExpirationTimesAndFlagsFrom(unspecRec);
740 // Now check if we have a new record.
741 if (aRec->HasUsableResult(now, aFlags)) {
742 result = aRec;
743 if (aRec->negative) {
744 aStatus = NS_ERROR_UNKNOWN_HOST;
746 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
747 ConditionallyRefreshRecord(aRec, aHost, lock);
748 } else if (af == PR_AF_INET6) {
749 // For AF_INET6, a new lookup means another AF_UNSPEC
750 // lookup. We have already iterated through the
751 // AF_UNSPEC addresses, so we mark this record as
752 // negative.
753 LOG(
754 (" No AF_INET6 in AF_UNSPEC entry: "
755 "host [%s] unknown host.",
756 nsPromiseFlatCString(aHost).get()));
757 result = aRec;
758 aRec->negative = true;
759 aStatus = NS_ERROR_UNKNOWN_HOST;
760 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
761 METHOD_NEGATIVE_HIT);
766 return result.forget();
769 void nsHostResolver::DetachCallback(
770 const nsACString& host, const nsACString& aTrrServer, uint16_t aType,
771 const OriginAttributes& aOriginAttributes, nsIDNSService::DNSFlags flags,
772 uint16_t af, nsResolveHostCallback* aCallback, nsresult status) {
773 RefPtr<nsHostRecord> rec;
774 RefPtr<nsResolveHostCallback> callback(aCallback);
777 MutexAutoLock lock(mLock);
779 nsAutoCString originSuffix;
780 aOriginAttributes.CreateSuffix(originSuffix);
782 nsHostKey key(host, aTrrServer, aType, flags, af,
783 (aOriginAttributes.mPrivateBrowsingId > 0), originSuffix);
784 RefPtr<nsHostRecord> entry = mRecordDB.Get(key);
785 if (entry) {
786 // walk list looking for |callback|... we cannot assume
787 // that it will be there!
789 for (nsResolveHostCallback* c : entry->mCallbacks) {
790 if (c == callback) {
791 rec = entry;
792 c->remove();
793 break;
799 // complete callback with the given status code; this would only be done if
800 // the record was in the process of being resolved.
801 if (rec) {
802 callback->OnResolveHostComplete(this, rec, status);
806 nsresult nsHostResolver::ConditionallyCreateThread(nsHostRecord* rec) {
807 if (mNumIdleTasks) {
808 // wake up idle tasks to process this lookup
809 mIdleTaskCV.Notify();
810 } else if ((mActiveTaskCount < MaxResolverThreadsAnyPriority()) ||
811 (IsHighPriority(rec->flags) &&
812 mActiveTaskCount < MaxResolverThreads())) {
813 nsCOMPtr<nsIRunnable> event = mozilla::NewRunnableMethod(
814 "nsHostResolver::ThreadFunc", this, &nsHostResolver::ThreadFunc);
815 mActiveTaskCount++;
816 nsresult rv =
817 mResolverThreads->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
818 if (NS_FAILED(rv)) {
819 mActiveTaskCount--;
821 } else {
822 LOG((" Unable to find a thread for looking up host [%s].\n",
823 rec->host.get()));
825 return NS_OK;
828 nsresult nsHostResolver::TrrLookup_unlocked(nsHostRecord* rec, TRR* pushedTRR) {
829 MutexAutoLock lock(mLock);
830 return TrrLookup(rec, lock, pushedTRR);
833 void nsHostResolver::MaybeRenewHostRecord(nsHostRecord* aRec) {
834 MutexAutoLock lock(mLock);
835 MaybeRenewHostRecordLocked(aRec, lock);
838 void nsHostResolver::MaybeRenewHostRecordLocked(nsHostRecord* aRec,
839 const MutexAutoLock& aLock) {
840 mQueue.MaybeRenewHostRecord(aRec, aLock);
843 bool nsHostResolver::TRRServiceEnabledForRecord(nsHostRecord* aRec) {
844 MOZ_ASSERT(aRec, "Record must not be empty");
845 MOZ_ASSERT(aRec->mEffectiveTRRMode != nsIRequest::TRR_DEFAULT_MODE,
846 "effective TRR mode must be computed before this call");
847 if (!TRRService::Get()) {
848 aRec->RecordReason(TRRSkippedReason::TRR_NO_GSERVICE);
849 return false;
852 // We always try custom resolvers.
853 if (!aRec->mTrrServer.IsEmpty()) {
854 return true;
857 nsIRequest::TRRMode reqMode = aRec->mEffectiveTRRMode;
858 if (TRRService::Get()->Enabled(reqMode)) {
859 return true;
862 if (NS_IsOffline()) {
863 // If we are in the NOT_CONFIRMED state _because_ we lack connectivity,
864 // then we should report that the browser is offline instead.
865 aRec->RecordReason(TRRSkippedReason::TRR_IS_OFFLINE);
866 return false;
869 auto hasConnectivity = [this]() -> bool {
870 mLock.AssertCurrentThreadOwns();
871 if (!mNCS) {
872 return true;
874 nsINetworkConnectivityService::ConnectivityState ipv4 = mNCS->GetIPv4();
875 nsINetworkConnectivityService::ConnectivityState ipv6 = mNCS->GetIPv6();
877 if (ipv4 == nsINetworkConnectivityService::OK ||
878 ipv6 == nsINetworkConnectivityService::OK) {
879 return true;
882 if (ipv4 == nsINetworkConnectivityService::UNKNOWN ||
883 ipv6 == nsINetworkConnectivityService::UNKNOWN) {
884 // One of the checks hasn't completed yet. Optimistically assume we'll
885 // have network connectivity.
886 return true;
889 return false;
892 if (!hasConnectivity()) {
893 aRec->RecordReason(TRRSkippedReason::TRR_NO_CONNECTIVITY);
894 return false;
897 bool isConfirmed = TRRService::Get()->IsConfirmed();
898 if (!isConfirmed) {
899 aRec->RecordReason(TRRSkippedReason::TRR_NOT_CONFIRMED);
902 return isConfirmed;
905 // returns error if no TRR resolve is issued
906 // it is impt this is not called while a native lookup is going on
907 nsresult nsHostResolver::TrrLookup(nsHostRecord* aRec,
908 const MutexAutoLock& aLock, TRR* pushedTRR) {
909 if (Mode() == nsIDNSService::MODE_TRROFF ||
910 StaticPrefs::network_dns_disabled()) {
911 return NS_ERROR_UNKNOWN_HOST;
913 LOG(("TrrLookup host:%s af:%" PRId16, aRec->host.get(), aRec->af));
915 RefPtr<nsHostRecord> rec(aRec);
916 mLock.AssertCurrentThreadOwns();
918 RefPtr<AddrHostRecord> addrRec;
919 RefPtr<TypeHostRecord> typeRec;
921 if (rec->IsAddrRecord()) {
922 addrRec = do_QueryObject(rec);
923 MOZ_ASSERT(addrRec);
924 } else {
925 typeRec = do_QueryObject(rec);
926 MOZ_ASSERT(typeRec);
929 MOZ_ASSERT(!rec->mResolving);
931 if (!TRRServiceEnabledForRecord(aRec)) {
932 return NS_ERROR_UNKNOWN_HOST;
935 MaybeRenewHostRecordLocked(rec, aLock);
937 RefPtr<TRRQuery> query = new TRRQuery(this, rec);
938 nsresult rv = query->DispatchLookup(pushedTRR);
939 if (NS_FAILED(rv)) {
940 rec->RecordReason(TRRSkippedReason::TRR_DID_NOT_MAKE_QUERY);
941 return rv;
945 auto lock = rec->mTRRQuery.Lock();
946 MOZ_ASSERT(!lock.ref(), "TRR already in progress");
947 lock.ref() = query;
950 rec->mResolving++;
951 rec->mTrrAttempts++;
952 return NS_OK;
955 nsresult nsHostResolver::NativeLookup(nsHostRecord* aRec,
956 const MutexAutoLock& aLock) {
957 if (StaticPrefs::network_dns_disabled()) {
958 return NS_ERROR_UNKNOWN_HOST;
960 LOG(("NativeLookup host:%s af:%" PRId16, aRec->host.get(), aRec->af));
962 // Only A/AAAA request are resolve natively.
963 MOZ_ASSERT(aRec->IsAddrRecord());
964 mLock.AssertCurrentThreadOwns();
966 RefPtr<nsHostRecord> rec(aRec);
967 RefPtr<AddrHostRecord> addrRec;
968 addrRec = do_QueryObject(rec);
969 MOZ_ASSERT(addrRec);
971 addrRec->mNativeStart = TimeStamp::Now();
973 // Add rec to one of the pending queues, possibly removing it from mEvictionQ.
974 MaybeRenewHostRecordLocked(aRec, aLock);
976 mQueue.InsertRecord(rec, rec->flags, aLock);
978 addrRec->StoreNative(true);
979 addrRec->StoreNativeUsed(true);
980 addrRec->mResolving++;
982 nsresult rv = ConditionallyCreateThread(rec);
984 LOG((" DNS thread counters: total=%d any-live=%d idle=%d pending=%d\n",
985 static_cast<uint32_t>(mActiveTaskCount),
986 static_cast<uint32_t>(mActiveAnyThreadCount),
987 static_cast<uint32_t>(mNumIdleTasks), mQueue.PendingCount()));
989 return rv;
992 // static
993 nsIDNSService::ResolverMode nsHostResolver::Mode() {
994 if (TRRService::Get()) {
995 return TRRService::Get()->Mode();
998 // If we don't have a TRR service just return MODE_TRROFF so we don't make
999 // any TRR requests by mistake.
1000 return nsIDNSService::MODE_TRROFF;
1003 nsIRequest::TRRMode nsHostRecord::TRRMode() {
1004 return nsIDNSService::GetTRRModeFromFlags(flags);
1007 // static
1008 void nsHostResolver::ComputeEffectiveTRRMode(nsHostRecord* aRec) {
1009 nsIDNSService::ResolverMode resolverMode = nsHostResolver::Mode();
1010 nsIRequest::TRRMode requestMode = aRec->TRRMode();
1012 // For domains that are excluded from TRR or when parental control is enabled,
1013 // we fallback to NativeLookup. This happens even in MODE_TRRONLY. By default
1014 // localhost and local are excluded (so we cover *.local hosts) See the
1015 // network.trr.excluded-domains pref.
1017 if (!TRRService::Get()) {
1018 aRec->RecordReason(TRRSkippedReason::TRR_NO_GSERVICE);
1019 aRec->mEffectiveTRRMode = requestMode;
1020 return;
1023 if (!aRec->mTrrServer.IsEmpty()) {
1024 aRec->mEffectiveTRRMode = nsIRequest::TRR_ONLY_MODE;
1025 return;
1028 if (TRRService::Get()->IsExcludedFromTRR(aRec->host)) {
1029 aRec->RecordReason(TRRSkippedReason::TRR_EXCLUDED);
1030 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1031 return;
1034 if (TRRService::Get()->ParentalControlEnabled()) {
1035 aRec->RecordReason(TRRSkippedReason::TRR_PARENTAL_CONTROL);
1036 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1037 return;
1040 if (resolverMode == nsIDNSService::MODE_TRROFF) {
1041 aRec->RecordReason(TRRSkippedReason::TRR_OFF_EXPLICIT);
1042 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1043 return;
1046 if (requestMode == nsIRequest::TRR_DISABLED_MODE) {
1047 aRec->RecordReason(TRRSkippedReason::TRR_REQ_MODE_DISABLED);
1048 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1049 return;
1052 if ((requestMode == nsIRequest::TRR_DEFAULT_MODE &&
1053 resolverMode == nsIDNSService::MODE_NATIVEONLY)) {
1054 if (StaticPrefs::network_trr_display_fallback_warning()) {
1055 TRRSkippedReason heuristicResult =
1056 TRRService::Get()->GetHeuristicDetectionResult();
1057 if (heuristicResult != TRRSkippedReason::TRR_UNSET &&
1058 heuristicResult != TRRSkippedReason::TRR_OK) {
1059 aRec->RecordReason(heuristicResult);
1060 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1061 return;
1064 aRec->RecordReason(TRRSkippedReason::TRR_MODE_NOT_ENABLED);
1065 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1066 return;
1069 if (requestMode == nsIRequest::TRR_DEFAULT_MODE &&
1070 resolverMode == nsIDNSService::MODE_TRRFIRST) {
1071 aRec->mEffectiveTRRMode = nsIRequest::TRR_FIRST_MODE;
1072 return;
1075 if (requestMode == nsIRequest::TRR_DEFAULT_MODE &&
1076 resolverMode == nsIDNSService::MODE_TRRONLY) {
1077 aRec->mEffectiveTRRMode = nsIRequest::TRR_ONLY_MODE;
1078 return;
1081 aRec->mEffectiveTRRMode = requestMode;
1084 // Kick-off a name resolve operation, using native resolver and/or TRR
1085 nsresult nsHostResolver::NameLookup(nsHostRecord* rec,
1086 const mozilla::MutexAutoLock& aLock) {
1087 LOG(("NameLookup host:%s af:%" PRId16, rec->host.get(), rec->af));
1088 mLock.AssertCurrentThreadOwns();
1090 if (rec->flags & RES_IP_HINT) {
1091 LOG(("Skip lookup if RES_IP_HINT is set\n"));
1092 return NS_ERROR_UNKNOWN_HOST;
1095 nsresult rv = NS_ERROR_UNKNOWN_HOST;
1096 if (rec->mResolving) {
1097 LOG(("NameLookup %s while already resolving\n", rec->host.get()));
1098 return NS_OK;
1101 // Make sure we reset the reason each time we attempt to do a new lookup
1102 // so we don't wrongly report the reason for the previous one.
1103 rec->Reset();
1105 ComputeEffectiveTRRMode(rec);
1107 if (!rec->mTrrServer.IsEmpty()) {
1108 LOG(("NameLookup: %s use trr:%s", rec->host.get(), rec->mTrrServer.get()));
1109 if (rec->mEffectiveTRRMode != nsIRequest::TRR_ONLY_MODE) {
1110 return NS_ERROR_UNKNOWN_HOST;
1113 if (rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR) {
1114 LOG(("TRR with server and DISABLE_TRR flag. Returning error."));
1115 return NS_ERROR_UNKNOWN_HOST;
1117 return TrrLookup(rec, aLock);
1120 LOG(("NameLookup: %s effectiveTRRmode: %d flags: %X", rec->host.get(),
1121 static_cast<nsIRequest::TRRMode>(rec->mEffectiveTRRMode), rec->flags));
1123 if (rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR) {
1124 rec->RecordReason(TRRSkippedReason::TRR_DISABLED_FLAG);
1127 bool serviceNotReady = !TRRServiceEnabledForRecord(rec);
1129 if (rec->mEffectiveTRRMode != nsIRequest::TRR_DISABLED_MODE &&
1130 !((rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR)) &&
1131 !serviceNotReady) {
1132 rv = TrrLookup(rec, aLock);
1135 if (rec->mEffectiveTRRMode == nsIRequest::TRR_DISABLED_MODE ||
1136 (rec->mEffectiveTRRMode == nsIRequest::TRR_FIRST_MODE &&
1137 (rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR || serviceNotReady ||
1138 NS_FAILED(rv)))) {
1139 if (!rec->IsAddrRecord()) {
1140 return rv;
1143 #ifdef DEBUG
1144 // If we use this branch then the mTRRUsed flag should not be set
1145 // Even if we did call TrrLookup above, the fact that it failed sync-ly
1146 // means that we didn't actually succeed in opening the channel.
1147 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
1148 MOZ_ASSERT(addrRec && addrRec->mResolverType == DNSResolverType::Native);
1149 #endif
1151 // We did not lookup via TRR - don't fallback to native if the
1152 // network.trr.display_fallback_warning pref is set and either
1153 // 1. we are in TRR first mode and confirmation failed
1154 // 2. the record has trr_disabled and a heuristic skip reason
1155 if (StaticPrefs::network_trr_display_fallback_warning() &&
1156 rec->mEffectiveTRRMode != nsIRequest::TRR_ONLY_MODE) {
1157 if ((rec->mEffectiveTRRMode == nsIRequest::TRR_FIRST_MODE &&
1158 rec->mTRRSkippedReason == TRRSkippedReason::TRR_NOT_CONFIRMED) ||
1159 (rec->mEffectiveTRRMode == nsIRequest::TRR_DISABLED_MODE &&
1160 rec->mTRRSkippedReason >=
1161 nsITRRSkipReason::TRR_HEURISTIC_TRIPPED_GOOGLE_SAFESEARCH &&
1162 rec->mTRRSkippedReason <=
1163 nsITRRSkipReason::TRR_HEURISTIC_TRIPPED_NRPT)) {
1164 LOG((
1165 "NameLookup: ResolveHostComplete with status NS_ERROR_UNKNOWN_HOST "
1166 "for: %s effectiveTRRmode: "
1167 "%d SkippedReason: %d",
1168 rec->host.get(),
1169 static_cast<nsIRequest::TRRMode>(rec->mEffectiveTRRMode),
1170 static_cast<int32_t>(rec->mTRRSkippedReason)));
1172 mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs =
1173 std::move(rec->mCallbacks);
1174 for (nsResolveHostCallback* c = cbs.getFirst(); c;
1175 c = c->removeAndGetNext()) {
1176 c->OnResolveHostComplete(this, rec, NS_ERROR_UNKNOWN_HOST);
1179 return NS_OK;
1183 rv = NativeLookup(rec, aLock);
1186 return rv;
1189 nsresult nsHostResolver::ConditionallyRefreshRecord(
1190 nsHostRecord* rec, const nsACString& host, const MutexAutoLock& aLock) {
1191 if ((rec->CheckExpiration(TimeStamp::NowLoRes()) != nsHostRecord::EXP_VALID ||
1192 rec->negative) &&
1193 !rec->mResolving && rec->RefreshForNegativeResponse()) {
1194 LOG((" Using %s cache entry for host [%s] but starting async renewal.",
1195 rec->negative ? "negative" : "positive", host.BeginReading()));
1196 NameLookup(rec, aLock);
1198 if (rec->IsAddrRecord() && !rec->negative) {
1199 // negative entries are constantly being refreshed, only
1200 // track positive grace period induced renewals
1201 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_RENEWAL);
1204 return NS_OK;
1207 bool nsHostResolver::GetHostToLookup(AddrHostRecord** result) {
1208 bool timedOut = false;
1209 TimeDuration timeout;
1210 TimeStamp epoch, now;
1212 MutexAutoLock lock(mLock);
1214 timeout = (mNumIdleTasks >= MaxResolverThreadsAnyPriority())
1215 ? mShortIdleTimeout
1216 : mLongIdleTimeout;
1217 epoch = TimeStamp::Now();
1219 while (!mShutdown) {
1220 // remove next record from Q; hand over owning reference. Check high, then
1221 // med, then low
1223 #define SET_GET_TTL(var, val) (var)->StoreGetTtl(sGetTtlEnabled && (val))
1225 RefPtr<AddrHostRecord> addrRec = mQueue.Dequeue(true, lock);
1226 if (addrRec) {
1227 SET_GET_TTL(addrRec, false);
1228 addrRec.forget(result);
1229 return true;
1232 if (mActiveAnyThreadCount < MaxResolverThreadsAnyPriority()) {
1233 addrRec = mQueue.Dequeue(false, lock);
1234 if (addrRec) {
1235 MOZ_ASSERT(IsMediumPriority(addrRec->flags) ||
1236 IsLowPriority(addrRec->flags));
1237 mActiveAnyThreadCount++;
1238 addrRec->StoreUsingAnyThread(true);
1239 SET_GET_TTL(addrRec, true);
1240 addrRec.forget(result);
1241 return true;
1245 // Determining timeout is racy, so allow one cycle through checking the
1246 // queues before exiting.
1247 if (timedOut) {
1248 break;
1251 // wait for one or more of the following to occur:
1252 // (1) the pending queue has a host record to process
1253 // (2) the shutdown flag has been set
1254 // (3) the thread has been idle for too long
1256 mNumIdleTasks++;
1257 mIdleTaskCV.Wait(timeout);
1258 mNumIdleTasks--;
1260 now = TimeStamp::Now();
1262 if (now - epoch >= timeout) {
1263 timedOut = true;
1264 } else {
1265 // It is possible that CondVar::Wait() was interrupted and returned
1266 // early, in which case we will loop back and re-enter it. In that
1267 // case we want to do so with the new timeout reduced to reflect
1268 // time already spent waiting.
1269 timeout -= now - epoch;
1270 epoch = now;
1274 // tell thread to exit...
1275 return false;
1278 void nsHostResolver::PrepareRecordExpirationAddrRecord(
1279 AddrHostRecord* rec) const {
1280 // NOTE: rec->addr_info_lock is already held by parent
1281 MOZ_ASSERT(((bool)rec->addr_info) != rec->negative);
1282 mLock.AssertCurrentThreadOwns();
1283 if (!rec->addr_info) {
1284 rec->SetExpiration(TimeStamp::NowLoRes(), NEGATIVE_RECORD_LIFETIME, 0);
1285 LOG(("Caching host [%s] negative record for %u seconds.\n", rec->host.get(),
1286 NEGATIVE_RECORD_LIFETIME));
1287 return;
1290 unsigned int lifetime = mDefaultCacheLifetime;
1291 unsigned int grace = mDefaultGracePeriod;
1293 unsigned int ttl = mDefaultCacheLifetime;
1294 if (sGetTtlEnabled || rec->addr_info->IsTRR()) {
1295 if (rec->addr_info && rec->addr_info->TTL() != AddrInfo::NO_TTL_DATA) {
1296 ttl = rec->addr_info->TTL();
1298 lifetime = ttl;
1299 grace = 0;
1302 rec->SetExpiration(TimeStamp::NowLoRes(), lifetime, grace);
1303 LOG(("Caching host [%s] record for %u seconds (grace %d).", rec->host.get(),
1304 lifetime, grace));
1307 static bool different_rrset(AddrInfo* rrset1, AddrInfo* rrset2) {
1308 if (!rrset1 || !rrset2) {
1309 return true;
1312 LOG(("different_rrset %s\n", rrset1->Hostname().get()));
1314 if (rrset1->ResolverType() != rrset2->ResolverType()) {
1315 return true;
1318 if (rrset1->TRRType() != rrset2->TRRType()) {
1319 return true;
1322 if (rrset1->Addresses().Length() != rrset2->Addresses().Length()) {
1323 LOG(("different_rrset true due to length change\n"));
1324 return true;
1327 nsTArray<NetAddr> orderedSet1 = rrset1->Addresses().Clone();
1328 nsTArray<NetAddr> orderedSet2 = rrset2->Addresses().Clone();
1329 orderedSet1.Sort();
1330 orderedSet2.Sort();
1332 bool eq = orderedSet1 == orderedSet2;
1333 if (!eq) {
1334 LOG(("different_rrset true due to content change\n"));
1335 } else {
1336 LOG(("different_rrset false\n"));
1338 return !eq;
1341 void nsHostResolver::AddToEvictionQ(nsHostRecord* rec,
1342 const MutexAutoLock& aLock) {
1343 mQueue.AddToEvictionQ(rec, mMaxCacheEntries, mRecordDB, aLock);
1346 // After a first lookup attempt with TRR in mode 2, we may:
1347 // - If network.trr.retry_on_recoverable_errors is false, retry with native.
1348 // - If network.trr.retry_on_recoverable_errors is true:
1349 // - Retry with native if the first attempt failed because we got NXDOMAIN, an
1350 // unreachable address (TRR_DISABLED_FLAG), or we skipped TRR because
1351 // Confirmation failed.
1352 // - Trigger a "RetryTRR" Confirmation which will start a fresh
1353 // connection for TRR, and then retry the lookup with TRR.
1354 // - If the second attempt failed, fallback to native if
1355 // network.trr.strict_native_fallback is false.
1356 // Returns true if we retried with either TRR or Native.
1357 bool nsHostResolver::MaybeRetryTRRLookup(
1358 AddrHostRecord* aAddrRec, nsresult aFirstAttemptStatus,
1359 TRRSkippedReason aFirstAttemptSkipReason, nsresult aChannelStatus,
1360 const MutexAutoLock& aLock) {
1361 if (NS_FAILED(aFirstAttemptStatus) &&
1362 (aChannelStatus == NS_ERROR_PROXY_UNAUTHORIZED ||
1363 aChannelStatus == NS_ERROR_PROXY_AUTHENTICATION_FAILED) &&
1364 aAddrRec->mEffectiveTRRMode == nsIRequest::TRR_ONLY_MODE) {
1365 LOG(("MaybeRetryTRRLookup retry because of proxy connect failed"));
1366 TRRService::Get()->DontUseTRRThread();
1367 return DoRetryTRR(aAddrRec, aLock);
1370 if (NS_SUCCEEDED(aFirstAttemptStatus) ||
1371 aAddrRec->mEffectiveTRRMode != nsIRequest::TRR_FIRST_MODE ||
1372 aFirstAttemptStatus == NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
1373 return false;
1376 MOZ_ASSERT(!aAddrRec->mResolving);
1377 if (!StaticPrefs::network_trr_retry_on_recoverable_errors()) {
1378 LOG(("nsHostResolver::MaybeRetryTRRLookup retrying with native"));
1379 return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock));
1382 if (IsFailedConfirmationOrNoConnectivity(aFirstAttemptSkipReason) ||
1383 IsNonRecoverableTRRSkipReason(aFirstAttemptSkipReason) ||
1384 IsBlockedTRRRequest(aFirstAttemptSkipReason)) {
1385 LOG(
1386 ("nsHostResolver::MaybeRetryTRRLookup retrying with native in strict "
1387 "mode, skip reason was %d",
1388 static_cast<uint32_t>(aFirstAttemptSkipReason)));
1389 return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock));
1392 if (aAddrRec->mTrrAttempts > 1) {
1393 if (!StaticPrefs::network_trr_strict_native_fallback()) {
1394 LOG(
1395 ("nsHostResolver::MaybeRetryTRRLookup retry failed. Using "
1396 "native."));
1397 return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock));
1400 if (aFirstAttemptSkipReason == TRRSkippedReason::TRR_TIMEOUT &&
1401 StaticPrefs::network_trr_strict_native_fallback_allow_timeouts()) {
1402 LOG(
1403 ("nsHostResolver::MaybeRetryTRRLookup retry timed out. Using "
1404 "native."));
1405 return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock));
1407 LOG(("nsHostResolver::MaybeRetryTRRLookup mTrrAttempts>1, not retrying."));
1408 return false;
1411 LOG(
1412 ("nsHostResolver::MaybeRetryTRRLookup triggering Confirmation and "
1413 "retrying with TRR, skip reason was %d",
1414 static_cast<uint32_t>(aFirstAttemptSkipReason)));
1415 TRRService::Get()->RetryTRRConfirm();
1417 return DoRetryTRR(aAddrRec, aLock);
1420 bool nsHostResolver::DoRetryTRR(AddrHostRecord* aAddrRec,
1421 const mozilla::MutexAutoLock& aLock) {
1423 // Clear out the old query
1424 auto trrQuery = aAddrRec->mTRRQuery.Lock();
1425 trrQuery.ref() = nullptr;
1428 if (NS_SUCCEEDED(TrrLookup(aAddrRec, aLock, nullptr /* pushedTRR */))) {
1429 aAddrRec->NotifyRetryingTrr();
1430 return true;
1433 return false;
1437 // CompleteLookup() checks if the resolving should be redone and if so it
1438 // returns LOOKUP_RESOLVEAGAIN, but only if 'status' is not NS_ERROR_ABORT.
1439 nsHostResolver::LookupStatus nsHostResolver::CompleteLookup(
1440 nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb,
1441 const nsACString& aOriginsuffix, TRRSkippedReason aReason,
1442 mozilla::net::TRR* aTRRRequest) {
1443 MutexAutoLock lock(mLock);
1444 return CompleteLookupLocked(rec, status, aNewRRSet, pb, aOriginsuffix,
1445 aReason, aTRRRequest, lock);
1448 nsHostResolver::LookupStatus nsHostResolver::CompleteLookupLocked(
1449 nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb,
1450 const nsACString& aOriginsuffix, TRRSkippedReason aReason,
1451 mozilla::net::TRR* aTRRRequest, const mozilla::MutexAutoLock& aLock) {
1452 MOZ_ASSERT(rec);
1453 MOZ_ASSERT(rec->pb == pb);
1454 MOZ_ASSERT(rec->IsAddrRecord());
1456 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
1457 MOZ_ASSERT(addrRec);
1459 RefPtr<AddrInfo> newRRSet(aNewRRSet);
1460 MOZ_ASSERT(NS_FAILED(status) || newRRSet->Addresses().Length() > 0);
1462 DNSResolverType type =
1463 newRRSet ? newRRSet->ResolverType() : DNSResolverType::Native;
1465 if (NS_FAILED(status)) {
1466 newRRSet = nullptr;
1469 if (addrRec->LoadResolveAgain() && (status != NS_ERROR_ABORT) &&
1470 type == DNSResolverType::Native) {
1471 LOG(("nsHostResolver record %p resolve again due to flushcache\n",
1472 addrRec.get()));
1473 addrRec->StoreResolveAgain(false);
1474 return LOOKUP_RESOLVEAGAIN;
1477 MOZ_ASSERT(addrRec->mResolving);
1478 addrRec->mResolving--;
1479 LOG((
1480 "nsHostResolver::CompleteLookup %s %p %X resolver=%d stillResolving=%d\n",
1481 addrRec->host.get(), aNewRRSet, (unsigned int)status, (int)type,
1482 int(addrRec->mResolving)));
1484 if (type != DNSResolverType::Native) {
1485 if (NS_FAILED(status) && status != NS_ERROR_UNKNOWN_HOST &&
1486 status != NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
1487 // the errors are not failed resolves, that means
1488 // something else failed, consider this as *TRR not used*
1489 // for actually trying to resolve the host
1490 addrRec->mResolverType = DNSResolverType::Native;
1493 if (NS_FAILED(status)) {
1494 if (aReason != TRRSkippedReason::TRR_UNSET) {
1495 addrRec->RecordReason(aReason);
1496 } else {
1497 // Unknown failed reason.
1498 addrRec->RecordReason(TRRSkippedReason::TRR_FAILED);
1500 } else {
1501 addrRec->mTRRSuccess = true;
1502 addrRec->RecordReason(TRRSkippedReason::TRR_OK);
1505 nsresult channelStatus = aTRRRequest->ChannelStatus();
1506 if (MaybeRetryTRRLookup(addrRec, status, aReason, channelStatus, aLock)) {
1507 MOZ_ASSERT(addrRec->mResolving);
1508 return LOOKUP_OK;
1511 if (!addrRec->mTRRSuccess) {
1512 // no TRR success
1513 newRRSet = nullptr;
1516 if (NS_FAILED(status)) {
1517 // This is the error that consumers expect.
1518 status = NS_ERROR_UNKNOWN_HOST;
1520 } else { // native resolve completed
1521 if (addrRec->LoadUsingAnyThread()) {
1522 mActiveAnyThreadCount--;
1523 addrRec->StoreUsingAnyThread(false);
1526 addrRec->mNativeSuccess = static_cast<bool>(newRRSet);
1527 if (addrRec->mNativeSuccess) {
1528 addrRec->mNativeDuration = TimeStamp::Now() - addrRec->mNativeStart;
1532 addrRec->OnCompleteLookup();
1534 // update record fields. We might have a addrRec->addr_info already if a
1535 // previous lookup result expired and we're reresolving it or we get
1536 // a late second TRR response.
1537 if (!mShutdown) {
1538 MutexAutoLock lock(addrRec->addr_info_lock);
1539 RefPtr<AddrInfo> old_addr_info;
1540 if (different_rrset(addrRec->addr_info, newRRSet)) {
1541 LOG(("nsHostResolver record %p new gencnt\n", addrRec.get()));
1542 old_addr_info = addrRec->addr_info;
1543 addrRec->addr_info = std::move(newRRSet);
1544 addrRec->addr_info_gencnt++;
1545 } else {
1546 if (addrRec->addr_info && newRRSet) {
1547 auto builder = addrRec->addr_info->Build();
1548 builder.SetTTL(newRRSet->TTL());
1549 // Update trr timings
1550 builder.SetTrrFetchDuration(newRRSet->GetTrrFetchDuration());
1551 builder.SetTrrFetchDurationNetworkOnly(
1552 newRRSet->GetTrrFetchDurationNetworkOnly());
1554 addrRec->addr_info = builder.Finish();
1556 old_addr_info = std::move(newRRSet);
1558 addrRec->negative = !addrRec->addr_info;
1559 PrepareRecordExpirationAddrRecord(addrRec);
1562 if (LOG_ENABLED()) {
1563 MutexAutoLock lock(addrRec->addr_info_lock);
1564 if (addrRec->addr_info) {
1565 for (const auto& elem : addrRec->addr_info->Addresses()) {
1566 char buf[128];
1567 elem.ToStringBuffer(buf, sizeof(buf));
1568 LOG(("CompleteLookup: %s has %s\n", addrRec->host.get(), buf));
1570 } else {
1571 LOG(("CompleteLookup: %s has NO address\n", addrRec->host.get()));
1575 // get the list of pending callbacks for this lookup, and notify
1576 // them that the lookup is complete.
1577 mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs =
1578 std::move(rec->mCallbacks);
1580 LOG(("nsHostResolver record %p calling back dns users status:%X\n",
1581 addrRec.get(), int(status)));
1583 for (nsResolveHostCallback* c = cbs.getFirst(); c;
1584 c = c->removeAndGetNext()) {
1585 c->OnResolveHostComplete(this, rec, status);
1588 OnResolveComplete(rec, aLock);
1590 #ifdef DNSQUERY_AVAILABLE
1591 // Unless the result is from TRR, resolve again to get TTL
1592 bool hasNativeResult = false;
1594 MutexAutoLock lock(addrRec->addr_info_lock);
1595 if (addrRec->addr_info && !addrRec->addr_info->IsTRR()) {
1596 hasNativeResult = true;
1599 if (hasNativeResult && !mShutdown && !addrRec->LoadGetTtl() &&
1600 !rec->mResolving && sGetTtlEnabled) {
1601 LOG(("Issuing second async lookup for TTL for host [%s].",
1602 addrRec->host.get()));
1603 addrRec->flags =
1604 (addrRec->flags & ~nsIDNSService::RESOLVE_PRIORITY_MEDIUM) |
1605 nsIDNSService::RESOLVE_PRIORITY_LOW;
1606 DebugOnly<nsresult> rv = NativeLookup(rec, aLock);
1607 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1608 "Could not issue second async lookup for TTL.");
1610 #endif
1611 return LOOKUP_OK;
1614 nsHostResolver::LookupStatus nsHostResolver::CompleteLookupByType(
1615 nsHostRecord* rec, nsresult status,
1616 mozilla::net::TypeRecordResultType& aResult, TRRSkippedReason aReason,
1617 uint32_t aTtl, bool pb) {
1618 MutexAutoLock lock(mLock);
1619 return CompleteLookupByTypeLocked(rec, status, aResult, aReason, aTtl, pb,
1620 lock);
1623 nsHostResolver::LookupStatus nsHostResolver::CompleteLookupByTypeLocked(
1624 nsHostRecord* rec, nsresult status,
1625 mozilla::net::TypeRecordResultType& aResult, TRRSkippedReason aReason,
1626 uint32_t aTtl, bool pb, const mozilla::MutexAutoLock& aLock) {
1627 MOZ_ASSERT(rec);
1628 MOZ_ASSERT(rec->pb == pb);
1629 MOZ_ASSERT(!rec->IsAddrRecord());
1631 RefPtr<TypeHostRecord> typeRec = do_QueryObject(rec);
1632 MOZ_ASSERT(typeRec);
1634 MOZ_ASSERT(typeRec->mResolving);
1635 typeRec->mResolving--;
1637 if (NS_FAILED(status)) {
1638 LOG(("nsHostResolver::CompleteLookupByType record %p [%s] status %x\n",
1639 typeRec.get(), typeRec->host.get(), (unsigned int)status));
1640 typeRec->SetExpiration(
1641 TimeStamp::NowLoRes(),
1642 StaticPrefs::network_dns_negative_ttl_for_type_record(), 0);
1643 MOZ_ASSERT(aResult.is<TypeRecordEmpty>());
1644 status = NS_ERROR_UNKNOWN_HOST;
1645 typeRec->negative = true;
1646 if (aReason != TRRSkippedReason::TRR_UNSET) {
1647 typeRec->RecordReason(aReason);
1648 } else {
1649 // Unknown failed reason.
1650 typeRec->RecordReason(TRRSkippedReason::TRR_FAILED);
1652 } else {
1653 size_t recordCount = 0;
1654 if (aResult.is<TypeRecordTxt>()) {
1655 recordCount = aResult.as<TypeRecordTxt>().Length();
1656 } else if (aResult.is<TypeRecordHTTPSSVC>()) {
1657 recordCount = aResult.as<TypeRecordHTTPSSVC>().Length();
1659 LOG(
1660 ("nsHostResolver::CompleteLookupByType record %p [%s], number of "
1661 "records %zu\n",
1662 typeRec.get(), typeRec->host.get(), recordCount));
1663 MutexAutoLock typeLock(typeRec->mResultsLock);
1664 typeRec->mResults = aResult;
1665 typeRec->SetExpiration(TimeStamp::NowLoRes(), aTtl, mDefaultGracePeriod);
1666 typeRec->negative = false;
1667 typeRec->mTRRSuccess = true;
1668 typeRec->RecordReason(TRRSkippedReason::TRR_OK);
1671 mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs =
1672 std::move(typeRec->mCallbacks);
1674 LOG(
1675 ("nsHostResolver::CompleteLookupByType record %p calling back dns "
1676 "users\n",
1677 typeRec.get()));
1679 for (nsResolveHostCallback* c = cbs.getFirst(); c;
1680 c = c->removeAndGetNext()) {
1681 c->OnResolveHostComplete(this, rec, status);
1684 OnResolveComplete(rec, aLock);
1686 return LOOKUP_OK;
1689 void nsHostResolver::OnResolveComplete(nsHostRecord* aRec,
1690 const mozilla::MutexAutoLock& aLock) {
1691 if (!aRec->mResolving && !mShutdown) {
1693 auto trrQuery = aRec->mTRRQuery.Lock();
1694 if (trrQuery.ref()) {
1695 aRec->mTrrDuration = trrQuery.ref()->Duration();
1697 trrQuery.ref() = nullptr;
1699 aRec->ResolveComplete();
1701 AddToEvictionQ(aRec, aLock);
1705 void nsHostResolver::CancelAsyncRequest(
1706 const nsACString& host, const nsACString& aTrrServer, uint16_t aType,
1707 const OriginAttributes& aOriginAttributes, nsIDNSService::DNSFlags flags,
1708 uint16_t af, nsIDNSListener* aListener, nsresult status)
1711 MutexAutoLock lock(mLock);
1713 nsAutoCString originSuffix;
1714 aOriginAttributes.CreateSuffix(originSuffix);
1716 // Lookup the host record associated with host, flags & address family
1718 nsHostKey key(host, aTrrServer, aType, flags, af,
1719 (aOriginAttributes.mPrivateBrowsingId > 0), originSuffix);
1720 RefPtr<nsHostRecord> rec = mRecordDB.Get(key);
1721 if (!rec) {
1722 return;
1725 for (RefPtr<nsResolveHostCallback> c : rec->mCallbacks) {
1726 if (c->EqualsAsyncListener(aListener)) {
1727 c->remove();
1728 c->OnResolveHostComplete(this, rec.get(), status);
1729 break;
1733 // If there are no more callbacks, remove the hash table entry
1734 if (rec->mCallbacks.isEmpty()) {
1735 mRecordDB.Remove(*static_cast<nsHostKey*>(rec.get()));
1736 // If record is on a Queue, remove it
1737 mQueue.MaybeRemoveFromQ(rec, lock);
1741 size_t nsHostResolver::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const {
1742 MutexAutoLock lock(mLock);
1744 size_t n = mallocSizeOf(this);
1746 n += mRecordDB.ShallowSizeOfExcludingThis(mallocSizeOf);
1747 for (const auto& entry : mRecordDB.Values()) {
1748 n += entry->SizeOfIncludingThis(mallocSizeOf);
1751 // The following fields aren't measured.
1752 // - mHighQ, mMediumQ, mLowQ, mEvictionQ, because they just point to
1753 // nsHostRecords that also pointed to by entries |mRecordDB|, and
1754 // measured when |mRecordDB| is measured.
1756 return n;
1759 void nsHostResolver::ThreadFunc() {
1760 LOG(("DNS lookup thread - starting execution.\n"));
1762 #if defined(RES_RETRY_ON_FAILURE)
1763 nsResState rs;
1764 #endif
1765 RefPtr<AddrHostRecord> rec;
1766 RefPtr<AddrInfo> ai;
1768 do {
1769 if (!rec) {
1770 RefPtr<AddrHostRecord> tmpRec;
1771 if (!GetHostToLookup(getter_AddRefs(tmpRec))) {
1772 break; // thread shutdown signal
1774 // GetHostToLookup() returns an owning reference
1775 MOZ_ASSERT(tmpRec);
1776 rec.swap(tmpRec);
1779 LOG1(("DNS lookup thread - Calling getaddrinfo for host [%s].\n",
1780 rec->host.get()));
1782 TimeStamp startTime = TimeStamp::Now();
1783 bool getTtl = rec->LoadGetTtl();
1784 TimeDuration inQueue = startTime - rec->mNativeStart;
1785 uint32_t ms = static_cast<uint32_t>(inQueue.ToMilliseconds());
1786 Telemetry::Accumulate(Telemetry::DNS_NATIVE_QUEUING, ms);
1787 nsresult status =
1788 GetAddrInfo(rec->host, rec->af, rec->flags, getter_AddRefs(ai), getTtl);
1789 #if defined(RES_RETRY_ON_FAILURE)
1790 if (NS_FAILED(status) && rs.Reset()) {
1791 status = GetAddrInfo(rec->host, rec->af, rec->flags, getter_AddRefs(ai),
1792 getTtl);
1794 #endif
1796 { // obtain lock to check shutdown and manage inter-module telemetry
1797 MutexAutoLock lock(mLock);
1799 if (!mShutdown) {
1800 TimeDuration elapsed = TimeStamp::Now() - startTime;
1801 if (NS_SUCCEEDED(status)) {
1802 if (!rec->addr_info_gencnt) {
1803 // Time for initial lookup.
1804 glean::networking::dns_lookup_time.AccumulateRawDuration(elapsed);
1805 } else if (!getTtl) {
1806 // Time for renewal; categorized by expiration strategy.
1807 glean::networking::dns_renewal_time.AccumulateRawDuration(elapsed);
1808 } else {
1809 // Time to get TTL; categorized by expiration strategy.
1810 glean::networking::dns_renewal_time_for_ttl.AccumulateRawDuration(
1811 elapsed);
1813 } else {
1814 glean::networking::dns_failed_lookup_time.AccumulateRawDuration(
1815 elapsed);
1820 LOG1(("DNS lookup thread - lookup completed for host [%s]: %s.\n",
1821 rec->host.get(), ai ? "success" : "failure: unknown host"));
1823 if (LOOKUP_RESOLVEAGAIN ==
1824 CompleteLookup(rec, status, ai, rec->pb, rec->originSuffix,
1825 rec->mTRRSkippedReason, nullptr)) {
1826 // leave 'rec' assigned and loop to make a renewed host resolve
1827 LOG(("DNS lookup thread - Re-resolving host [%s].\n", rec->host.get()));
1828 } else {
1829 rec = nullptr;
1831 } while (true);
1833 MutexAutoLock lock(mLock);
1834 mActiveTaskCount--;
1835 LOG(("DNS lookup thread - queue empty, task finished.\n"));
1838 void nsHostResolver::SetCacheLimits(uint32_t aMaxCacheEntries,
1839 uint32_t aDefaultCacheEntryLifetime,
1840 uint32_t aDefaultGracePeriod) {
1841 MutexAutoLock lock(mLock);
1842 mMaxCacheEntries = aMaxCacheEntries;
1843 mDefaultCacheLifetime = aDefaultCacheEntryLifetime;
1844 mDefaultGracePeriod = aDefaultGracePeriod;
1847 nsresult nsHostResolver::Create(uint32_t maxCacheEntries,
1848 uint32_t defaultCacheEntryLifetime,
1849 uint32_t defaultGracePeriod,
1850 nsHostResolver** result) {
1851 RefPtr<nsHostResolver> res = new nsHostResolver(
1852 maxCacheEntries, defaultCacheEntryLifetime, defaultGracePeriod);
1854 nsresult rv = res->Init();
1855 if (NS_FAILED(rv)) {
1856 return rv;
1859 res.forget(result);
1860 return NS_OK;
1863 void nsHostResolver::GetDNSCacheEntries(nsTArray<DNSCacheEntries>* args) {
1864 MutexAutoLock lock(mLock);
1865 for (const auto& recordEntry : mRecordDB) {
1866 // We don't pay attention to address literals, only resolved domains.
1867 // Also require a host.
1868 nsHostRecord* rec = recordEntry.GetWeak();
1869 MOZ_ASSERT(rec, "rec should never be null here!");
1871 if (!rec) {
1872 continue;
1875 // For now we only show A/AAAA records.
1876 if (!rec->IsAddrRecord()) {
1877 continue;
1880 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
1881 MOZ_ASSERT(addrRec);
1882 if (!addrRec || !addrRec->addr_info) {
1883 continue;
1886 DNSCacheEntries info;
1887 info.hostname = rec->host;
1888 info.family = rec->af;
1889 info.expiration =
1890 (int64_t)(rec->mValidEnd - TimeStamp::NowLoRes()).ToSeconds();
1891 if (info.expiration <= 0) {
1892 // We only need valid DNS cache entries
1893 continue;
1897 MutexAutoLock lock(addrRec->addr_info_lock);
1898 for (const auto& addr : addrRec->addr_info->Addresses()) {
1899 char buf[kIPv6CStrBufSize];
1900 if (addr.ToStringBuffer(buf, sizeof(buf))) {
1901 info.hostaddr.AppendElement(buf);
1904 info.TRR = addrRec->addr_info->IsTRR();
1907 info.originAttributesSuffix = recordEntry.GetKey().originSuffix;
1908 info.flags = nsPrintfCString("%u|0x%x|%u|%d|%s", rec->type, rec->flags,
1909 rec->af, rec->pb, rec->mTrrServer.get());
1911 args->AppendElement(std::move(info));
1915 #undef LOG
1916 #undef LOG_ENABLED