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>
12 # define RES_RETRY_ON_FAILURE
17 #include "nsHostResolver.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"
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"
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
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.
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()) {}
114 // reset no more than once per second
115 if (PR_IntervalToSeconds(PR_IntervalNow() - mLastReset
) < 1) {
119 mLastReset
= PR_IntervalNow();
120 auto result
= res_ninit(&_res
);
122 LOG(("nsResState::Reset() > 'res_ninit' returned %d", result
));
123 return (result
== 0);
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!");
147 if (!strcmp(aPref
, kPrefGetTtl
)) {
148 #ifdef DNSQUERY_AVAILABLE
149 sGetTtlEnabled
= Preferences::GetBool(kPrefGetTtl
);
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));
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
));
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
;
220 // We clamp down the idle time between 0 and one hour.
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
));
230 threadPool
->SetThreadStackSize(nsIThreadManager::kThreadPoolStackSize
));
231 MOZ_ALWAYS_SUCCEEDS(threadPool
->SetName("DNS Resolver"_ns
));
232 mResolverThreads
= ToRefPtr(std::move(threadPool
));
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
) {
243 if (rec
->IsAddrRecord()) {
244 CompleteLookup(rec
, NS_ERROR_ABORT
, nullptr, rec
->pb
, rec
->originSuffix
,
245 rec
->mTRRSkippedReason
, nullptr);
247 mozilla::net::TypeRecordResultType
empty(Nothing
{});
248 CompleteLookupByType(rec
, NS_ERROR_ABORT
, empty
, rec
->mTRRSkippedReason
,
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
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
);
279 if (addrRec
->RemoveOrRefresh(aTrrToo
)) {
280 mQueue
.MaybeRemoveFromQ(record
, lock
);
281 LOG(("Removing (%s) Addr record from mRecordDB", record
->host
.get()));
284 } else if (aTrrToo
) {
285 // remove by type records
286 LOG(("Removing (%s) type record from mRecordDB", record
->host
.get()));
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
,
306 MutexAutoLock
lock(mLock
);
311 mIdleTaskCV
.NotifyAll();
315 [&](nsHostRecord
* aRec
) {
316 mLock
.AssertCurrentThreadOwns();
317 if (aRec
->IsAddrRecord()) {
318 CompleteLookupLocked(aRec
, NS_ERROR_ABORT
, nullptr, aRec
->pb
,
319 aRec
->originSuffix
, aRec
->mTRRSkippedReason
,
322 mozilla::net::TypeRecordResultType
empty(Nothing
{});
323 CompleteLookupByTypeLocked(aRec
, NS_ERROR_ABORT
, empty
,
324 aRec
->mTRRSkippedReason
, 0, aRec
->pb
,
330 for (const auto& data
: mRecordDB
.Values()) {
333 // empty host database
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
);
362 return NS_ERROR_FAILURE
;
366 if (rec
->mResolving
) {
367 return NS_ERROR_FAILURE
;
370 *result
= rec
.forget().take();
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
) {
384 MOZ_ASSERT(IS_ADDR_TYPE(key
.type
));
386 *aRv
= NS_ERROR_FAILURE
;
387 RefPtr
<nsHostRecord
> rec
= InitRecord(key
);
389 nsTArray
<NetAddr
> addresses
;
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;
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
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.
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
);
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 &&
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
)) {
492 RefPtr
<nsHostRecord
> result
= InitLoopbackRecord(key
, &rv
);
493 if (NS_WARN_IF(NS_FAILED(rv
))) {
497 aCallback
->OnResolveHostComplete(this, result
, 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
) {
535 (" Lookup queue full: dropping %s priority request for "
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
) {
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
560 LOG((" No usable record in cache for host [%s] type %d.", host
.get(),
563 if (flags
& RES_REFRESH_CACHE
) {
567 // Add callback to the list of pending callbacks.
568 rec
->mCallbacks
.insertBack(callback
);
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()) {
579 (" DNS lookup for host [%s] blocking "
580 "pending 'getaddrinfo' or trr query: "
582 host
.get(), callback
.get()));
587 (" Host [%s] is being resolved. Appending callback "
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
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
);
608 ConditionallyCreateThread(rec
);
609 } else if (IsMediumPriority(flags
) && IsLowPriority(rec
->flags
)) {
610 // Move from low to med.
611 mQueue
.MoveToAnotherPendingQ(rec
, flags
, lock
);
613 mIdleTaskCV
.Notify();
618 if (result
&& callback
->isInList()) {
624 callback
->OnResolveHostComplete(this, result
, status
);
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
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
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
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
)) {
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
754 (" No AF_INET6 in AF_UNSPEC entry: "
755 "host [%s] unknown host.",
756 nsPromiseFlatCString(aHost
).get()));
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
);
786 // walk list looking for |callback|... we cannot assume
787 // that it will be there!
789 for (nsResolveHostCallback
* c
: entry
->mCallbacks
) {
799 // complete callback with the given status code; this would only be done if
800 // the record was in the process of being resolved.
802 callback
->OnResolveHostComplete(this, rec
, status
);
806 nsresult
nsHostResolver::ConditionallyCreateThread(nsHostRecord
* rec
) {
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
);
817 mResolverThreads
->Dispatch(event
, nsIEventTarget::DISPATCH_NORMAL
);
822 LOG((" Unable to find a thread for looking up host [%s].\n",
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
);
852 // We always try custom resolvers.
853 if (!aRec
->mTrrServer
.IsEmpty()) {
857 nsIRequest::TRRMode reqMode
= aRec
->mEffectiveTRRMode
;
858 if (TRRService::Get()->Enabled(reqMode
)) {
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
);
869 auto hasConnectivity
= [this]() -> bool {
870 mLock
.AssertCurrentThreadOwns();
874 nsINetworkConnectivityService::ConnectivityState ipv4
= mNCS
->GetIPv4();
875 nsINetworkConnectivityService::ConnectivityState ipv6
= mNCS
->GetIPv6();
877 if (ipv4
== nsINetworkConnectivityService::OK
||
878 ipv6
== nsINetworkConnectivityService::OK
) {
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.
892 if (!hasConnectivity()) {
893 aRec
->RecordReason(TRRSkippedReason::TRR_NO_CONNECTIVITY
);
897 bool isConfirmed
= TRRService::Get()->IsConfirmed();
899 aRec
->RecordReason(TRRSkippedReason::TRR_NOT_CONFIRMED
);
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
);
925 typeRec
= do_QueryObject(rec
);
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
);
940 rec
->RecordReason(TRRSkippedReason::TRR_DID_NOT_MAKE_QUERY
);
945 auto lock
= rec
->mTRRQuery
.Lock();
946 MOZ_ASSERT(!lock
.ref(), "TRR already in progress");
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
);
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()));
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
);
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
;
1023 if (!aRec
->mTrrServer
.IsEmpty()) {
1024 aRec
->mEffectiveTRRMode
= nsIRequest::TRR_ONLY_MODE
;
1028 if (TRRService::Get()->IsExcludedFromTRR(aRec
->host
)) {
1029 aRec
->RecordReason(TRRSkippedReason::TRR_EXCLUDED
);
1030 aRec
->mEffectiveTRRMode
= nsIRequest::TRR_DISABLED_MODE
;
1034 if (TRRService::Get()->ParentalControlEnabled()) {
1035 aRec
->RecordReason(TRRSkippedReason::TRR_PARENTAL_CONTROL
);
1036 aRec
->mEffectiveTRRMode
= nsIRequest::TRR_DISABLED_MODE
;
1040 if (resolverMode
== nsIDNSService::MODE_TRROFF
) {
1041 aRec
->RecordReason(TRRSkippedReason::TRR_OFF_EXPLICIT
);
1042 aRec
->mEffectiveTRRMode
= nsIRequest::TRR_DISABLED_MODE
;
1046 if (requestMode
== nsIRequest::TRR_DISABLED_MODE
) {
1047 aRec
->RecordReason(TRRSkippedReason::TRR_REQ_MODE_DISABLED
);
1048 aRec
->mEffectiveTRRMode
= nsIRequest::TRR_DISABLED_MODE
;
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
;
1064 aRec
->RecordReason(TRRSkippedReason::TRR_MODE_NOT_ENABLED
);
1065 aRec
->mEffectiveTRRMode
= nsIRequest::TRR_DISABLED_MODE
;
1069 if (requestMode
== nsIRequest::TRR_DEFAULT_MODE
&&
1070 resolverMode
== nsIDNSService::MODE_TRRFIRST
) {
1071 aRec
->mEffectiveTRRMode
= nsIRequest::TRR_FIRST_MODE
;
1075 if (requestMode
== nsIRequest::TRR_DEFAULT_MODE
&&
1076 resolverMode
== nsIDNSService::MODE_TRRONLY
) {
1077 aRec
->mEffectiveTRRMode
= nsIRequest::TRR_ONLY_MODE
;
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()));
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.
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
)) &&
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
||
1139 if (!rec
->IsAddrRecord()) {
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
);
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
)) {
1165 "NameLookup: ResolveHostComplete with status NS_ERROR_UNKNOWN_HOST "
1166 "for: %s effectiveTRRmode: "
1167 "%d SkippedReason: %d",
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
);
1183 rv
= NativeLookup(rec
, aLock
);
1189 nsresult
nsHostResolver::ConditionallyRefreshRecord(
1190 nsHostRecord
* rec
, const nsACString
& host
, const MutexAutoLock
& aLock
) {
1191 if ((rec
->CheckExpiration(TimeStamp::NowLoRes()) != nsHostRecord::EXP_VALID
||
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
);
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())
1217 epoch
= TimeStamp::Now();
1219 while (!mShutdown
) {
1220 // remove next record from Q; hand over owning reference. Check high, then
1223 #define SET_GET_TTL(var, val) (var)->StoreGetTtl(sGetTtlEnabled && (val))
1225 RefPtr
<AddrHostRecord
> addrRec
= mQueue
.Dequeue(true, lock
);
1227 SET_GET_TTL(addrRec
, false);
1228 addrRec
.forget(result
);
1232 if (mActiveAnyThreadCount
< MaxResolverThreadsAnyPriority()) {
1233 addrRec
= mQueue
.Dequeue(false, lock
);
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
);
1245 // Determining timeout is racy, so allow one cycle through checking the
1246 // queues before exiting.
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
1257 mIdleTaskCV
.Wait(timeout
);
1260 now
= TimeStamp::Now();
1262 if (now
- epoch
>= timeout
) {
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
;
1274 // tell thread to exit...
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
));
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();
1302 rec
->SetExpiration(TimeStamp::NowLoRes(), lifetime
, grace
);
1303 LOG(("Caching host [%s] record for %u seconds (grace %d).", rec
->host
.get(),
1307 static bool different_rrset(AddrInfo
* rrset1
, AddrInfo
* rrset2
) {
1308 if (!rrset1
|| !rrset2
) {
1312 LOG(("different_rrset %s\n", rrset1
->Hostname().get()));
1314 if (rrset1
->ResolverType() != rrset2
->ResolverType()) {
1318 if (rrset1
->TRRType() != rrset2
->TRRType()) {
1322 if (rrset1
->Addresses().Length() != rrset2
->Addresses().Length()) {
1323 LOG(("different_rrset true due to length change\n"));
1327 nsTArray
<NetAddr
> orderedSet1
= rrset1
->Addresses().Clone();
1328 nsTArray
<NetAddr
> orderedSet2
= rrset2
->Addresses().Clone();
1332 bool eq
= orderedSet1
== orderedSet2
;
1334 LOG(("different_rrset true due to content change\n"));
1336 LOG(("different_rrset false\n"));
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
) {
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
)) {
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()) {
1395 ("nsHostResolver::MaybeRetryTRRLookup retry failed. Using "
1397 return NS_SUCCEEDED(NativeLookup(aAddrRec
, aLock
));
1400 if (aFirstAttemptSkipReason
== TRRSkippedReason::TRR_TIMEOUT
&&
1401 StaticPrefs::network_trr_strict_native_fallback_allow_timeouts()) {
1403 ("nsHostResolver::MaybeRetryTRRLookup retry timed out. Using "
1405 return NS_SUCCEEDED(NativeLookup(aAddrRec
, aLock
));
1407 LOG(("nsHostResolver::MaybeRetryTRRLookup mTrrAttempts>1, not retrying."));
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();
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
) {
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
)) {
1469 if (addrRec
->LoadResolveAgain() && (status
!= NS_ERROR_ABORT
) &&
1470 type
== DNSResolverType::Native
) {
1471 LOG(("nsHostResolver record %p resolve again due to flushcache\n",
1473 addrRec
->StoreResolveAgain(false);
1474 return LOOKUP_RESOLVEAGAIN
;
1477 MOZ_ASSERT(addrRec
->mResolving
);
1478 addrRec
->mResolving
--;
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
);
1497 // Unknown failed reason.
1498 addrRec
->RecordReason(TRRSkippedReason::TRR_FAILED
);
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
);
1511 if (!addrRec
->mTRRSuccess
) {
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.
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
++;
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()) {
1567 elem
.ToStringBuffer(buf
, sizeof(buf
));
1568 LOG(("CompleteLookup: %s has %s\n", addrRec
->host
.get(), buf
));
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()));
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.");
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
,
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
) {
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
);
1649 // Unknown failed reason.
1650 typeRec
->RecordReason(TRRSkippedReason::TRR_FAILED
);
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();
1660 ("nsHostResolver::CompleteLookupByType record %p [%s], number of "
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
);
1675 ("nsHostResolver::CompleteLookupByType record %p calling back dns "
1679 for (nsResolveHostCallback
* c
= cbs
.getFirst(); c
;
1680 c
= c
->removeAndGetNext()) {
1681 c
->OnResolveHostComplete(this, rec
, status
);
1684 OnResolveComplete(rec
, aLock
);
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
);
1725 for (RefPtr
<nsResolveHostCallback
> c
: rec
->mCallbacks
) {
1726 if (c
->EqualsAsyncListener(aListener
)) {
1728 c
->OnResolveHostComplete(this, rec
.get(), status
);
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.
1759 void nsHostResolver::ThreadFunc() {
1760 LOG(("DNS lookup thread - starting execution.\n"));
1762 #if defined(RES_RETRY_ON_FAILURE)
1765 RefPtr
<AddrHostRecord
> rec
;
1766 RefPtr
<AddrInfo
> ai
;
1770 RefPtr
<AddrHostRecord
> tmpRec
;
1771 if (!GetHostToLookup(getter_AddRefs(tmpRec
))) {
1772 break; // thread shutdown signal
1774 // GetHostToLookup() returns an owning reference
1779 LOG1(("DNS lookup thread - Calling getaddrinfo for host [%s].\n",
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
);
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
),
1796 { // obtain lock to check shutdown and manage inter-module telemetry
1797 MutexAutoLock
lock(mLock
);
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
);
1809 // Time to get TTL; categorized by expiration strategy.
1810 glean::networking::dns_renewal_time_for_ttl
.AccumulateRawDuration(
1814 glean::networking::dns_failed_lookup_time
.AccumulateRawDuration(
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()));
1833 MutexAutoLock
lock(mLock
);
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
)) {
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!");
1875 // For now we only show A/AAAA records.
1876 if (!rec
->IsAddrRecord()) {
1880 RefPtr
<AddrHostRecord
> addrRec
= do_QueryObject(rec
);
1881 MOZ_ASSERT(addrRec
);
1882 if (!addrRec
|| !addrRec
->addr_info
) {
1886 DNSCacheEntries info
;
1887 info
.hostname
= rec
->host
;
1888 info
.family
= rec
->af
;
1890 (int64_t)(rec
->mValidEnd
- TimeStamp::NowLoRes()).ToSeconds();
1891 if (info
.expiration
<= 0) {
1892 // We only need valid DNS cache entries
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
));