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 "nsISupportsBase.h"
20 #include "nsISupportsUtils.h"
21 #include "nsIThreadManager.h"
22 #include "nsComponentManagerUtils.h"
23 #include "nsPrintfCString.h"
24 #include "nsXPCOMCIDInternal.h"
28 #include "mozilla/Logging.h"
29 #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 "GeckoProfiler.h"
39 #include "TRRService.h"
40 #include "ODoHService.h"
42 #include "mozilla/Atomics.h"
43 #include "mozilla/HashFunctions.h"
44 #include "mozilla/TimeStamp.h"
45 #include "mozilla/Telemetry.h"
46 #include "mozilla/DebugOnly.h"
47 #include "mozilla/Preferences.h"
48 #include "mozilla/StaticPrefs_network.h"
50 using namespace mozilla
;
51 using namespace mozilla::net
;
53 // None of our implementations expose a TTL for negative responses, so we use a
55 static const unsigned int NEGATIVE_RECORD_LIFETIME
= 60;
57 //----------------------------------------------------------------------------
59 // Use a persistent thread pool in order to avoid spinning up new threads all
60 // the time. In particular, thread creation results in a res_init() call from
61 // libc which is quite expensive.
63 // The pool dynamically grows between 0 and MAX_RESOLVER_THREADS in size. New
64 // requests go first to an idle thread. If that cannot be found and there are
65 // fewer than MAX_RESOLVER_THREADS currently in the pool a new thread is created
66 // for high priority requests. If the new request is at a lower priority a new
67 // thread will only be created if there are fewer than HighThreadThreshold
68 // currently outstanding. If a thread cannot be created or an idle thread
69 // located for the request it is queued.
71 // When the pool is greater than HighThreadThreshold in size a thread will be
72 // destroyed after ShortIdleTimeoutSeconds of idle time. Smaller pools use
73 // LongIdleTimeoutSeconds for a timeout period.
75 #define HighThreadThreshold MAX_RESOLVER_THREADS_FOR_ANY_PRIORITY
76 #define LongIdleTimeoutSeconds 300 // for threads 1 -> HighThreadThreshold
77 #define ShortIdleTimeoutSeconds \
78 60 // for threads HighThreadThreshold+1 -> MAX_RESOLVER_THREADS
81 HighThreadThreshold
<= MAX_RESOLVER_THREADS
,
82 "High Thread Threshold should be less equal Maximum allowed thread");
84 //----------------------------------------------------------------------------
86 namespace mozilla::net
{
87 LazyLogModule
gHostResolverLog("nsHostResolver");
89 MOZ_LOG(mozilla::net::gHostResolverLog, mozilla::LogLevel::Debug, args)
91 MOZ_LOG(mozilla::net::gHostResolverLog, mozilla::LogLevel::Error, args)
92 #define LOG_ENABLED() \
93 MOZ_LOG_TEST(mozilla::net::gHostResolverLog, mozilla::LogLevel::Debug)
94 } // namespace mozilla::net
96 //----------------------------------------------------------------------------
98 #if defined(RES_RETRY_ON_FAILURE)
100 // this class represents the resolver state for a given thread. if we
101 // encounter a lookup failure, then we can invoke the Reset method on an
102 // instance of this class to reset the resolver (in case /etc/resolv.conf
103 // for example changed). this is mainly an issue on GNU systems since glibc
104 // only reads in /etc/resolv.conf once per thread. it may be an issue on
105 // other systems as well.
110 // initialize mLastReset to the time when this object
111 // is created. this means that a reset will not occur
112 // if a thread is too young. the alternative would be
113 // to initialize this to the beginning of time, so that
114 // the first failure would cause a reset, but since the
115 // thread would have just started up, it likely would
116 // already have current /etc/resolv.conf info.
117 : mLastReset(PR_IntervalNow()) {}
120 // reset no more than once per second
121 if (PR_IntervalToSeconds(PR_IntervalNow() - mLastReset
) < 1) {
125 LOG(("Calling 'res_ninit'.\n"));
127 mLastReset
= PR_IntervalNow();
128 return (res_ninit(&_res
) == 0);
132 PRIntervalTime mLastReset
;
135 #endif // RES_RETRY_ON_FAILURE
137 //----------------------------------------------------------------------------
139 static inline bool IsHighPriority(uint16_t flags
) {
140 return !(flags
& (nsHostResolver::RES_PRIORITY_LOW
|
141 nsHostResolver::RES_PRIORITY_MEDIUM
));
144 static inline bool IsMediumPriority(uint16_t flags
) {
145 return flags
& nsHostResolver::RES_PRIORITY_MEDIUM
;
148 static inline bool IsLowPriority(uint16_t flags
) {
149 return flags
& nsHostResolver::RES_PRIORITY_LOW
;
152 //----------------------------------------------------------------------------
153 // this macro filters out any flags that are not used when constructing the
154 // host key. the significant flags are those that would affect the resulting
155 // host record (i.e., the flags that are passed down to PR_GetAddrInfoByName).
156 #define RES_KEY_FLAGS(_f) \
158 (nsHostResolver::RES_CANON_NAME | nsHostResolver::RES_DISABLE_TRR | \
159 nsIDNSService::RESOLVE_TRR_MODE_MASK | nsHostResolver::RES_IP_HINT))
161 #define IS_ADDR_TYPE(_type) ((_type) == nsIDNSService::RESOLVE_TYPE_DEFAULT)
162 #define IS_OTHER_TYPE(_type) ((_type) != nsIDNSService::RESOLVE_TYPE_DEFAULT)
164 nsHostKey::nsHostKey(const nsACString
& aHost
, const nsACString
& aTrrServer
,
165 uint16_t aType
, uint16_t aFlags
, uint16_t aAf
, bool aPb
,
166 const nsACString
& aOriginsuffix
)
168 mTrrServer(aTrrServer
),
173 originSuffix(aOriginsuffix
) {}
175 bool nsHostKey::operator==(const nsHostKey
& other
) const {
176 return host
== other
.host
&& mTrrServer
== other
.mTrrServer
&&
177 type
== other
.type
&&
178 RES_KEY_FLAGS(flags
) == RES_KEY_FLAGS(other
.flags
) && af
== other
.af
&&
179 originSuffix
== other
.originSuffix
;
182 PLDHashNumber
nsHostKey::Hash() const {
183 return AddToHash(HashString(host
.get()), HashString(mTrrServer
.get()), type
,
184 RES_KEY_FLAGS(flags
), af
, HashString(originSuffix
.get()));
187 size_t nsHostKey::SizeOfExcludingThis(
188 mozilla::MallocSizeOf mallocSizeOf
) const {
190 n
+= host
.SizeOfExcludingThisIfUnshared(mallocSizeOf
);
191 n
+= mTrrServer
.SizeOfExcludingThisIfUnshared(mallocSizeOf
);
192 n
+= originSuffix
.SizeOfExcludingThisIfUnshared(mallocSizeOf
);
196 NS_IMPL_ISUPPORTS0(nsHostRecord
)
198 nsHostRecord::nsHostRecord(const nsHostKey
& key
)
200 mEffectiveTRRMode(nsIRequest::TRR_DEFAULT_MODE
),
201 mTRRQuery("nsHostRecord.mTRRQuery"),
206 void nsHostRecord::Invalidate() { mDoomed
= true; }
208 void nsHostRecord::Cancel() {
209 RefPtr
<TRRQuery
> query
;
211 auto lock
= mTRRQuery
.Lock();
212 query
.swap(lock
.ref());
220 nsHostRecord::ExpirationStatus
nsHostRecord::CheckExpiration(
221 const mozilla::TimeStamp
& now
) const {
222 if (!mGraceStart
.IsNull() && now
>= mGraceStart
&& !mValidEnd
.IsNull() &&
224 return nsHostRecord::EXP_GRACE
;
226 if (!mValidEnd
.IsNull() && now
< mValidEnd
) {
227 return nsHostRecord::EXP_VALID
;
230 return nsHostRecord::EXP_EXPIRED
;
233 void nsHostRecord::SetExpiration(const mozilla::TimeStamp
& now
,
234 unsigned int valid
, unsigned int grace
) {
236 if ((valid
+ grace
) < 60) {
238 LOG(("SetExpiration: artificially bumped grace to %d\n", grace
));
240 mGraceStart
= now
+ TimeDuration::FromSeconds(valid
);
241 mValidEnd
= now
+ TimeDuration::FromSeconds(valid
+ grace
);
244 void nsHostRecord::CopyExpirationTimesAndFlagsFrom(
245 const nsHostRecord
* aFromHostRecord
) {
246 // This is used to copy information from a cache entry to a record. All
247 // information necessary for HasUsableRecord needs to be copied.
248 mValidStart
= aFromHostRecord
->mValidStart
;
249 mValidEnd
= aFromHostRecord
->mValidEnd
;
250 mGraceStart
= aFromHostRecord
->mGraceStart
;
251 mDoomed
= aFromHostRecord
->mDoomed
;
254 bool nsHostRecord::HasUsableResult(const mozilla::TimeStamp
& now
,
255 uint16_t queryFlags
) const {
260 // don't use cached negative results for high priority queries.
261 if (negative
&& IsHighPriority(queryFlags
)) {
265 if (CheckExpiration(now
) == EXP_EXPIRED
) {
273 return HasUsableResultInternal();
276 static size_t SizeOfResolveHostCallbackListExcludingHead(
277 const mozilla::LinkedList
<RefPtr
<nsResolveHostCallback
>>& aCallbacks
,
278 MallocSizeOf mallocSizeOf
) {
279 size_t n
= aCallbacks
.sizeOfExcludingThis(mallocSizeOf
);
281 for (const nsResolveHostCallback
* t
= aCallbacks
.getFirst(); t
;
283 n
+= t
->SizeOfIncludingThis(mallocSizeOf
);
289 NS_IMPL_ISUPPORTS_INHERITED(AddrHostRecord
, nsHostRecord
, AddrHostRecord
)
291 AddrHostRecord::AddrHostRecord(const nsHostKey
& key
)
293 addr_info_lock("AddrHostRecord.addr_info_lock"),
301 AddrHostRecord::~AddrHostRecord() {
303 Telemetry::Accumulate(Telemetry::DNS_BLACKLIST_COUNT
, mUnusableCount
);
306 bool AddrHostRecord::Blocklisted(const NetAddr
* aQuery
) {
307 addr_info_lock
.AssertCurrentThreadOwns();
308 LOG(("Checking unusable list for host [%s], host record [%p].\n", host
.get(),
311 // skip the string conversion for the common case of no blocklist
312 if (!mUnusableItems
.Length()) {
316 char buf
[kIPv6CStrBufSize
];
317 if (!aQuery
->ToStringBuffer(buf
, sizeof(buf
))) {
320 nsDependentCString
strQuery(buf
);
322 for (uint32_t i
= 0; i
< mUnusableItems
.Length(); i
++) {
323 if (mUnusableItems
.ElementAt(i
).Equals(strQuery
)) {
324 LOG(("Address [%s] is blocklisted for host [%s].\n", buf
, host
.get()));
332 void AddrHostRecord::ReportUnusable(const NetAddr
* aAddress
) {
333 addr_info_lock
.AssertCurrentThreadOwns();
335 ("Adding address to blocklist for host [%s], host record [%p]."
337 host
.get(), this, mTRRSuccess
));
341 char buf
[kIPv6CStrBufSize
];
342 if (aAddress
->ToStringBuffer(buf
, sizeof(buf
))) {
344 ("Successfully adding address [%s] to blocklist for host "
347 mUnusableItems
.AppendElement(nsCString(buf
));
351 void AddrHostRecord::ResetBlocklist() {
352 addr_info_lock
.AssertCurrentThreadOwns();
353 LOG(("Resetting blocklist for host [%s], host record [%p].\n", host
.get(),
355 mUnusableItems
.Clear();
358 size_t AddrHostRecord::SizeOfIncludingThis(MallocSizeOf mallocSizeOf
) const {
359 size_t n
= mallocSizeOf(this);
361 n
+= nsHostKey::SizeOfExcludingThis(mallocSizeOf
);
362 n
+= SizeOfResolveHostCallbackListExcludingHead(mCallbacks
, mallocSizeOf
);
364 n
+= addr_info
? addr_info
->SizeOfIncludingThis(mallocSizeOf
) : 0;
365 n
+= mallocSizeOf(addr
.get());
367 n
+= mUnusableItems
.ShallowSizeOfExcludingThis(mallocSizeOf
);
368 for (size_t i
= 0; i
< mUnusableItems
.Length(); i
++) {
369 n
+= mUnusableItems
[i
].SizeOfExcludingThisIfUnshared(mallocSizeOf
);
374 bool AddrHostRecord::HasUsableResultInternal() const {
375 return addr_info
|| addr
;
378 // Returns true if the entry can be removed, or false if it should be left.
379 // Sets ResolveAgain true for entries being resolved right now.
380 bool AddrHostRecord::RemoveOrRefresh(bool aTrrToo
) {
381 // no need to flush TRRed names, they're not resolved "locally"
382 MutexAutoLock
lock(addr_info_lock
);
383 if (addr_info
&& !aTrrToo
&& addr_info
->IsTRR()) {
388 // The request has been passed to the OS resolver. The resultant DNS
389 // record should be considered stale and not trusted; set a flag to
390 // ensure it is called again.
391 StoreResolveAgain(true);
393 // if onQueue is true, the host entry is already added to the cache
394 // but is still pending to get resolved: just leave it in hash.
397 // Already resolved; not in a pending state; remove from cache
401 void AddrHostRecord::ResolveComplete() {
402 if (LoadNativeUsed()) {
403 if (mNativeSuccess
) {
404 uint32_t millis
= static_cast<uint32_t>(mNativeDuration
.ToMilliseconds());
405 Telemetry::Accumulate(Telemetry::DNS_NATIVE_LOOKUP_TIME
, millis
);
407 AccumulateCategoricalKeyed(
408 TRRService::AutoDetectedKey(),
409 mNativeSuccess
? Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::osOK
410 : Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::osFail
);
415 uint32_t millis
= static_cast<uint32_t>(mTrrDuration
.ToMilliseconds());
416 Telemetry::Accumulate(Telemetry::DNS_TRR_LOOKUP_TIME2
,
417 TRRService::AutoDetectedKey(), millis
);
419 AccumulateCategoricalKeyed(
420 TRRService::AutoDetectedKey(),
421 mTRRSuccess
? Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::trrOK
422 : Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::trrFail
);
425 if (nsHostResolver::Mode() == nsIDNSService::MODE_TRRFIRST
) {
426 Telemetry::Accumulate(Telemetry::TRR_SKIP_REASON_TRR_FIRST
,
427 mTRRTRRSkippedReason
);
429 if (mNativeSuccess
) {
430 Telemetry::Accumulate(Telemetry::TRR_SKIP_REASON_DNS_WORKED
,
431 mTRRTRRSkippedReason
);
435 if (mEffectiveTRRMode
== nsIRequest::TRR_FIRST_MODE
) {
436 if (flags
& nsIDNSService::RESOLVE_DISABLE_TRR
) {
437 // TRR is disabled on request, which is a next-level back-off method.
438 Telemetry::Accumulate(Telemetry::DNS_TRR_DISABLED2
,
439 TRRService::AutoDetectedKey(), mNativeSuccess
);
442 AccumulateCategoricalKeyed(TRRService::AutoDetectedKey(),
443 Telemetry::LABELS_DNS_TRR_FIRST3::TRR
);
444 } else if (mNativeSuccess
) {
446 AccumulateCategoricalKeyed(
447 TRRService::AutoDetectedKey(),
448 Telemetry::LABELS_DNS_TRR_FIRST3::NativeAfterTRR
);
450 AccumulateCategoricalKeyed(TRRService::AutoDetectedKey(),
451 Telemetry::LABELS_DNS_TRR_FIRST3::Native
);
454 AccumulateCategoricalKeyed(
455 TRRService::AutoDetectedKey(),
456 Telemetry::LABELS_DNS_TRR_FIRST3::BothFailed
);
461 switch (mEffectiveTRRMode
) {
462 case nsIRequest::TRR_DISABLED_MODE
:
463 AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::nativeOnly
);
465 case nsIRequest::TRR_FIRST_MODE
:
466 AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::trrFirst
);
468 case nsIRequest::TRR_ONLY_MODE
:
469 AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::trrOnly
);
471 case nsIRequest::TRR_DEFAULT_MODE
:
472 MOZ_ASSERT_UNREACHABLE("We should not have a default value here");
476 if (mTRRUsed
&& !mTRRSuccess
&& mNativeSuccess
&& gTRRService
) {
477 gTRRService
->AddToBlocklist(nsCString(host
), originSuffix
, pb
, true);
481 AddrHostRecord::DnsPriority
AddrHostRecord::GetPriority(uint16_t aFlags
) {
482 if (IsHighPriority(aFlags
)) {
483 return AddrHostRecord::DNS_PRIORITY_HIGH
;
485 if (IsMediumPriority(aFlags
)) {
486 return AddrHostRecord::DNS_PRIORITY_MEDIUM
;
489 return AddrHostRecord::DNS_PRIORITY_LOW
;
492 NS_IMPL_ISUPPORTS_INHERITED(TypeHostRecord
, nsHostRecord
, TypeHostRecord
,
493 nsIDNSTXTRecord
, nsIDNSHTTPSSVCRecord
)
495 TypeHostRecord::TypeHostRecord(const nsHostKey
& key
)
497 DNSHTTPSSVCRecordBase(key
.host
),
498 mResultsLock("TypeHostRecord.mResultsLock"),
499 mAllRecordsExcluded(false) {}
501 TypeHostRecord::~TypeHostRecord() { mCallbacks
.clear(); }
503 bool TypeHostRecord::HasUsableResultInternal() const {
504 return !mResults
.is
<Nothing
>();
507 NS_IMETHODIMP
TypeHostRecord::GetRecords(CopyableTArray
<nsCString
>& aRecords
) {
509 MutexAutoLock
lock(mResultsLock
);
511 if (!mResults
.is
<TypeRecordTxt
>()) {
512 return NS_ERROR_NOT_AVAILABLE
;
514 aRecords
= mResults
.as
<CopyableTArray
<nsCString
>>();
518 NS_IMETHODIMP
TypeHostRecord::GetRecordsAsOneString(nsACString
& aRecords
) {
520 MutexAutoLock
lock(mResultsLock
);
522 if (!mResults
.is
<TypeRecordTxt
>()) {
523 return NS_ERROR_NOT_AVAILABLE
;
525 auto& results
= mResults
.as
<CopyableTArray
<nsCString
>>();
526 for (uint32_t i
= 0; i
< results
.Length(); i
++) {
527 aRecords
.Append(results
[i
]);
532 size_t TypeHostRecord::SizeOfIncludingThis(MallocSizeOf mallocSizeOf
) const {
533 size_t n
= mallocSizeOf(this);
535 n
+= nsHostKey::SizeOfExcludingThis(mallocSizeOf
);
536 n
+= SizeOfResolveHostCallbackListExcludingHead(mCallbacks
, mallocSizeOf
);
541 uint32_t TypeHostRecord::GetType() {
542 MutexAutoLock
lock(mResultsLock
);
544 return mResults
.match(
545 [](TypeRecordEmpty
&) {
546 MOZ_ASSERT(false, "This should never be the case");
547 return nsIDNSService::RESOLVE_TYPE_DEFAULT
;
549 [](TypeRecordTxt
&) { return nsIDNSService::RESOLVE_TYPE_TXT
; },
550 [](TypeRecordHTTPSSVC
&) { return nsIDNSService::RESOLVE_TYPE_HTTPSSVC
; });
553 TypeRecordResultType
TypeHostRecord::GetResults() {
554 MutexAutoLock
lock(mResultsLock
);
559 TypeHostRecord::GetRecords(nsTArray
<RefPtr
<nsISVCBRecord
>>& aRecords
) {
560 MutexAutoLock
lock(mResultsLock
);
561 if (!mResults
.is
<TypeRecordHTTPSSVC
>()) {
562 return NS_ERROR_NOT_AVAILABLE
;
565 auto& results
= mResults
.as
<TypeRecordHTTPSSVC
>();
567 for (const SVCB
& r
: results
) {
568 RefPtr
<nsISVCBRecord
> rec
= new mozilla::net::SVCBRecord(r
);
569 aRecords
.AppendElement(rec
);
576 TypeHostRecord::GetServiceModeRecord(bool aNoHttp2
, bool aNoHttp3
,
577 nsISVCBRecord
** aRecord
) {
578 MutexAutoLock
lock(mResultsLock
);
579 if (!mResults
.is
<TypeRecordHTTPSSVC
>()) {
580 return NS_ERROR_NOT_AVAILABLE
;
583 auto& results
= mResults
.as
<TypeRecordHTTPSSVC
>();
584 nsCOMPtr
<nsISVCBRecord
> result
= GetServiceModeRecordInternal(
585 aNoHttp2
, aNoHttp3
, results
, mAllRecordsExcluded
);
587 return NS_ERROR_NOT_AVAILABLE
;
590 result
.forget(aRecord
);
595 TypeHostRecord::GetAllRecordsWithEchConfig(
596 bool aNoHttp2
, bool aNoHttp3
, bool* aAllRecordsHaveEchConfig
,
597 bool* aAllRecordsInH3ExcludedList
,
598 nsTArray
<RefPtr
<nsISVCBRecord
>>& aResult
) {
599 MutexAutoLock
lock(mResultsLock
);
600 if (!mResults
.is
<TypeRecordHTTPSSVC
>()) {
601 return NS_ERROR_NOT_AVAILABLE
;
604 auto& records
= mResults
.as
<TypeRecordHTTPSSVC
>();
605 GetAllRecordsWithEchConfigInternal(aNoHttp2
, aNoHttp3
, records
,
606 aAllRecordsHaveEchConfig
,
607 aAllRecordsInH3ExcludedList
, aResult
);
612 TypeHostRecord::GetHasIPAddresses(bool* aResult
) {
613 NS_ENSURE_ARG(aResult
);
615 if (!mResults
.is
<TypeRecordHTTPSSVC
>()) {
616 return NS_ERROR_NOT_AVAILABLE
;
619 auto& results
= mResults
.as
<TypeRecordHTTPSSVC
>();
620 *aResult
= HasIPAddressesInternal(results
);
625 TypeHostRecord::GetAllRecordsExcluded(bool* aResult
) {
626 NS_ENSURE_ARG(aResult
);
628 if (!mResults
.is
<TypeRecordHTTPSSVC
>()) {
629 return NS_ERROR_NOT_AVAILABLE
;
632 *aResult
= mAllRecordsExcluded
;
636 //----------------------------------------------------------------------------
638 static const char kPrefGetTtl
[] = "network.dns.get-ttl";
639 static const char kPrefNativeIsLocalhost
[] = "network.dns.native-is-localhost";
640 static const char kPrefThreadIdleTime
[] =
641 "network.dns.resolver-thread-extra-idle-time-seconds";
642 static bool sGetTtlEnabled
= false;
643 mozilla::Atomic
<bool, mozilla::Relaxed
> gNativeIsLocalhost
;
645 static void DnsPrefChanged(const char* aPref
, void* aSelf
) {
646 MOZ_ASSERT(NS_IsMainThread(),
647 "Should be getting pref changed notification on main thread!");
651 if (!strcmp(aPref
, kPrefGetTtl
)) {
652 #ifdef DNSQUERY_AVAILABLE
653 sGetTtlEnabled
= Preferences::GetBool(kPrefGetTtl
);
655 } else if (!strcmp(aPref
, kPrefNativeIsLocalhost
)) {
656 gNativeIsLocalhost
= Preferences::GetBool(kPrefNativeIsLocalhost
);
660 NS_IMPL_ISUPPORTS0(nsHostResolver
)
662 nsHostResolver::nsHostResolver(uint32_t maxCacheEntries
,
663 uint32_t defaultCacheEntryLifetime
,
664 uint32_t defaultGracePeriod
)
665 : mMaxCacheEntries(maxCacheEntries
),
666 mDefaultCacheLifetime(defaultCacheEntryLifetime
),
667 mDefaultGracePeriod(defaultGracePeriod
),
668 mLock("nsHostResolver.mLock"),
669 mIdleTaskCV(mLock
, "nsHostResolver.mIdleTaskCV"),
674 mActiveAnyThreadCount(0),
676 mCreationTime
= PR_Now();
678 mLongIdleTimeout
= TimeDuration::FromSeconds(LongIdleTimeoutSeconds
);
679 mShortIdleTimeout
= TimeDuration::FromSeconds(ShortIdleTimeoutSeconds
);
682 nsHostResolver::~nsHostResolver() = default;
684 nsresult
nsHostResolver::Init() {
685 MOZ_ASSERT(NS_IsMainThread());
686 if (NS_FAILED(GetAddrInfoInit())) {
687 return NS_ERROR_FAILURE
;
690 LOG(("nsHostResolver::Init this=%p", this));
693 mNCS
= NetworkConnectivityService::GetSingleton();
695 // The preferences probably haven't been loaded from the disk yet, so we
696 // need to register a callback that will set up the experiment once they
697 // are. We also need to explicitly set a value for the props otherwise the
698 // callback won't be called.
700 DebugOnly
<nsresult
> rv
= Preferences::RegisterCallbackAndCall(
701 &DnsPrefChanged
, kPrefGetTtl
, this);
702 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
703 "Could not register DNS TTL pref callback.");
704 rv
= Preferences::RegisterCallbackAndCall(&DnsPrefChanged
,
705 kPrefNativeIsLocalhost
, this);
706 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
707 "Could not register DNS pref callback.");
710 #if defined(HAVE_RES_NINIT)
711 // We want to make sure the system is using the correct resolver settings,
712 // so we force it to reload those settings whenever we startup a subsequent
713 // nsHostResolver instance. We assume that there is no reason to do this
714 // for the first nsHostResolver instance since that is usually created
715 // during application startup.
716 static int initCount
= 0;
717 if (initCount
++ > 0) {
718 LOG(("Calling 'res_ninit'.\n"));
723 // We can configure the threadpool to keep threads alive for a while after
724 // the last ThreadFunc task has been executed.
725 int32_t poolTimeoutSecs
= Preferences::GetInt(kPrefThreadIdleTime
, 60);
726 uint32_t poolTimeoutMs
;
727 if (poolTimeoutSecs
< 0) {
728 // This means never shut down the idle threads
729 poolTimeoutMs
= UINT32_MAX
;
731 // We clamp down the idle time between 0 and one hour.
733 mozilla::clamped
<uint32_t>(poolTimeoutSecs
* 1000, 0, 3600 * 1000);
736 nsCOMPtr
<nsIThreadPool
> threadPool
= new nsThreadPool();
737 MOZ_ALWAYS_SUCCEEDS(threadPool
->SetThreadLimit(MAX_RESOLVER_THREADS
));
738 MOZ_ALWAYS_SUCCEEDS(threadPool
->SetIdleThreadLimit(MAX_RESOLVER_THREADS
));
739 MOZ_ALWAYS_SUCCEEDS(threadPool
->SetIdleThreadTimeout(poolTimeoutMs
));
741 threadPool
->SetThreadStackSize(nsIThreadManager::kThreadPoolStackSize
));
742 MOZ_ALWAYS_SUCCEEDS(threadPool
->SetName("DNS Resolver"_ns
));
743 mResolverThreads
= ToRefPtr(std::move(threadPool
));
748 void nsHostResolver::ClearPendingQueue(
749 LinkedList
<RefPtr
<nsHostRecord
>>& aPendingQ
) {
750 // loop through pending queue, erroring out pending lookups.
751 if (!aPendingQ
.isEmpty()) {
752 for (const RefPtr
<nsHostRecord
>& rec
: aPendingQ
) {
754 if (rec
->IsAddrRecord()) {
755 CompleteLookup(rec
, NS_ERROR_ABORT
, nullptr, rec
->pb
, rec
->originSuffix
,
756 rec
->mTRRTRRSkippedReason
);
758 mozilla::net::TypeRecordResultType
empty(Nothing
{});
759 CompleteLookupByType(rec
, NS_ERROR_ABORT
, empty
, 0, rec
->pb
);
766 // FlushCache() is what we call when the network has changed. We must not
767 // trust names that were resolved before this change. They may resolve
770 // This function removes all existing resolved host entries from the hash.
771 // Names that are in the pending queues can be left there. Entries in the
772 // cache that have 'Resolve' set true but not 'OnQueue' are being resolved
773 // right now, so we need to mark them to get re-resolved on completion!
775 void nsHostResolver::FlushCache(bool aTrrToo
) {
776 MutexAutoLock
lock(mLock
);
779 // Clear the evictionQ and remove all its corresponding entries from
781 if (!mEvictionQ
.isEmpty()) {
782 for (const RefPtr
<nsHostRecord
>& rec
: mEvictionQ
) {
784 mRecordDB
.Remove(*static_cast<nsHostKey
*>(rec
));
789 // Refresh the cache entries that are resolving RIGHT now, remove the rest.
790 for (auto iter
= mRecordDB
.Iter(); !iter
.Done(); iter
.Next()) {
791 nsHostRecord
* record
= iter
.UserData();
792 // Try to remove the record, or mark it for refresh.
793 // By-type records are from TRR. We do not need to flush those entry
794 // when the network has change, because they are not local.
795 if (record
->IsAddrRecord()) {
796 RefPtr
<AddrHostRecord
> addrRec
= do_QueryObject(record
);
798 if (addrRec
->RemoveOrRefresh(aTrrToo
)) {
799 if (record
->isInList()) {
808 void nsHostResolver::Shutdown() {
809 LOG(("Shutting down host resolver.\n"));
812 DebugOnly
<nsresult
> rv
=
813 Preferences::UnregisterCallback(&DnsPrefChanged
, kPrefGetTtl
, this);
814 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
815 "Could not unregister DNS TTL pref callback.");
818 LinkedList
<RefPtr
<nsHostRecord
>> pendingQHigh
, pendingQMed
, pendingQLow
,
822 MutexAutoLock
lock(mLock
);
826 // Move queues to temporary lists.
827 pendingQHigh
= std::move(mHighQ
);
828 pendingQMed
= std::move(mMediumQ
);
829 pendingQLow
= std::move(mLowQ
);
830 evictionQ
= std::move(mEvictionQ
);
836 mIdleTaskCV
.NotifyAll();
839 for (auto iter
= mRecordDB
.Iter(); !iter
.Done(); iter
.Next()) {
840 iter
.UserData()->Cancel();
842 // empty host database
848 ClearPendingQueue(pendingQHigh
);
849 ClearPendingQueue(pendingQMed
);
850 ClearPendingQueue(pendingQLow
);
852 if (!evictionQ
.isEmpty()) {
853 for (const RefPtr
<nsHostRecord
>& rec
: evictionQ
) {
858 pendingQHigh
.clear();
863 // Shutdown the resolver threads, but with a timeout of 2 seconds (prefable).
864 // If the timeout is exceeded, any stuck threads will be leaked.
865 mResolverThreads
->ShutdownWithTimeout(
866 StaticPrefs::network_dns_resolver_shutdown_timeout_ms());
869 mozilla::DebugOnly
<nsresult
> rv
= GetAddrInfoShutdown();
870 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "Failed to shutdown GetAddrInfo");
874 nsresult
nsHostResolver::GetHostRecord(const nsACString
& host
,
875 const nsACString
& aTrrServer
,
876 uint16_t type
, uint16_t flags
,
877 uint16_t af
, bool pb
,
878 const nsCString
& originSuffix
,
879 nsHostRecord
** result
) {
880 MutexAutoLock
lock(mLock
);
881 nsHostKey
key(host
, aTrrServer
, type
, flags
, af
, pb
, originSuffix
);
883 RefPtr
<nsHostRecord
>& entry
= mRecordDB
.GetOrInsert(key
);
885 entry
= InitRecord(key
);
888 RefPtr
<nsHostRecord
> rec
= entry
;
889 if (rec
->IsAddrRecord()) {
890 RefPtr
<AddrHostRecord
> addrRec
= do_QueryObject(rec
);
892 return NS_ERROR_FAILURE
;
896 if (rec
->mResolving
) {
897 return NS_ERROR_FAILURE
;
900 *result
= rec
.forget().take();
904 nsHostRecord
* nsHostResolver::InitRecord(const nsHostKey
& key
) {
905 if (IS_ADDR_TYPE(key
.type
)) {
906 return new AddrHostRecord(key
);
908 return new TypeHostRecord(key
);
911 already_AddRefed
<nsHostRecord
> nsHostResolver::InitLoopbackRecord(
912 const nsHostKey
& key
, nsresult
* aRv
) {
914 MOZ_ASSERT(IS_ADDR_TYPE(key
.type
));
916 *aRv
= NS_ERROR_FAILURE
;
917 RefPtr
<nsHostRecord
> rec
= InitRecord(key
);
919 nsTArray
<NetAddr
> addresses
;
921 memset(&prAddr
, 0, sizeof(prAddr
));
922 if (key
.af
== PR_AF_INET
|| key
.af
== PR_AF_UNSPEC
) {
923 MOZ_RELEASE_ASSERT(PR_StringToNetAddr("127.0.0.1", &prAddr
) == PR_SUCCESS
);
924 addresses
.AppendElement(NetAddr(&prAddr
));
926 if (key
.af
== PR_AF_INET6
|| key
.af
== PR_AF_UNSPEC
) {
927 MOZ_RELEASE_ASSERT(PR_StringToNetAddr("::1", &prAddr
) == PR_SUCCESS
);
928 addresses
.AppendElement(NetAddr(&prAddr
));
931 RefPtr
<AddrInfo
> ai
= new AddrInfo(rec
->host
, 0, std::move(addresses
));
933 RefPtr
<AddrHostRecord
> addrRec
= do_QueryObject(rec
);
934 MutexAutoLock
lock(addrRec
->addr_info_lock
);
935 addrRec
->addr_info
= ai
;
936 addrRec
->SetExpiration(TimeStamp::NowLoRes(), mDefaultCacheLifetime
,
937 mDefaultGracePeriod
);
938 addrRec
->negative
= false;
944 nsresult
nsHostResolver::ResolveHost(const nsACString
& aHost
,
945 const nsACString
& aTrrServer
,
947 const OriginAttributes
& aOriginAttributes
,
948 uint16_t flags
, uint16_t af
,
949 nsResolveHostCallback
* aCallback
) {
950 nsAutoCString
host(aHost
);
951 NS_ENSURE_TRUE(!host
.IsEmpty(), NS_ERROR_UNEXPECTED
);
953 nsAutoCString originSuffix
;
954 aOriginAttributes
.CreateSuffix(originSuffix
);
955 LOG(("Resolving host [%s]<%s>%s%s type %d. [this=%p]\n", host
.get(),
956 originSuffix
.get(), flags
& RES_BYPASS_CACHE
? " - bypassing cache" : "",
957 flags
& RES_REFRESH_CACHE
? " - refresh cache" : "", type
, this));
959 // ensure that we are working with a valid hostname before proceeding. see
960 // bug 304904 for details.
961 if (!net_IsValidHostName(host
)) {
962 return NS_ERROR_UNKNOWN_HOST
;
965 // By-Type requests use only TRR. If TRR is disabled we can return
967 if (IS_OTHER_TYPE(type
) && Mode() == nsIDNSService::MODE_TRROFF
) {
968 return NS_ERROR_UNKNOWN_HOST
;
971 // Used to try to parse to an IP address literal.
973 // Unfortunately, PR_StringToNetAddr does not properly initialize
974 // the output buffer in the case of IPv6 input. See bug 223145.
975 memset(&tempAddr
, 0, sizeof(PRNetAddr
));
977 if (IS_OTHER_TYPE(type
) &&
978 (PR_StringToNetAddr(host
.get(), &tempAddr
) == PR_SUCCESS
)) {
979 // For by-type queries the host cannot be IP literal.
980 return NS_ERROR_UNKNOWN_HOST
;
982 memset(&tempAddr
, 0, sizeof(PRNetAddr
));
984 RefPtr
<nsResolveHostCallback
> callback(aCallback
);
985 // if result is set inside the lock, then we need to issue the
986 // callback before returning.
987 RefPtr
<nsHostRecord
> result
;
988 nsresult status
= NS_OK
, rv
= NS_OK
;
990 MutexAutoLock
lock(mLock
);
993 return NS_ERROR_NOT_INITIALIZED
;
996 // check to see if there is already an entry for this |host|
997 // in the hash table. if so, then check to see if we can't
998 // just reuse the lookup result. otherwise, if there are
999 // any pending callbacks, then add to pending callbacks queue,
1000 // and return. otherwise, add ourselves as first pending
1001 // callback, and proceed to do the lookup.
1003 bool excludedFromTRR
= false;
1005 if (gTRRService
&& gTRRService
->IsExcludedFromTRR(host
)) {
1006 flags
|= RES_DISABLE_TRR
;
1007 excludedFromTRR
= true;
1009 if (!aTrrServer
.IsEmpty()) {
1010 return NS_ERROR_UNKNOWN_HOST
;
1014 nsHostKey
key(host
, aTrrServer
, type
, flags
, af
,
1015 (aOriginAttributes
.mPrivateBrowsingId
> 0), originSuffix
);
1017 // Check if we have a localhost domain, if so hardcode to loopback
1018 if (IS_ADDR_TYPE(type
) && IsLoopbackHostname(host
)) {
1020 RefPtr
<nsHostRecord
> result
= InitLoopbackRecord(key
, &rv
);
1021 if (NS_WARN_IF(NS_FAILED(rv
))) {
1025 aCallback
->OnResolveHostComplete(this, result
, NS_OK
);
1029 RefPtr
<nsHostRecord
>& entry
= mRecordDB
.GetOrInsert(key
);
1031 entry
= InitRecord(key
);
1034 RefPtr
<nsHostRecord
> rec
= entry
;
1035 RefPtr
<AddrHostRecord
> addrRec
= do_QueryObject(rec
);
1036 MOZ_ASSERT(rec
, "Record should not be null");
1037 MOZ_ASSERT((IS_ADDR_TYPE(type
) && rec
->IsAddrRecord() && addrRec
) ||
1038 (IS_OTHER_TYPE(type
) && !rec
->IsAddrRecord()));
1040 if (excludedFromTRR
) {
1041 rec
->RecordReason(nsHostRecord::TRR_EXCLUDED
);
1044 if (!(flags
& RES_BYPASS_CACHE
) &&
1045 rec
->HasUsableResult(TimeStamp::NowLoRes(), flags
)) {
1046 LOG((" Using cached record for host [%s].\n", host
.get()));
1047 // put reference to host record on stack...
1049 if (IS_ADDR_TYPE(type
)) {
1050 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2
, METHOD_HIT
);
1053 // For entries that are in the grace period
1054 // or all cached negative entries, use the cache but start a new
1055 // lookup in the background
1056 ConditionallyRefreshRecord(rec
, host
);
1058 if (rec
->negative
) {
1059 LOG((" Negative cache entry for host [%s].\n", host
.get()));
1060 if (IS_ADDR_TYPE(type
)) {
1061 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2
,
1062 METHOD_NEGATIVE_HIT
);
1064 status
= NS_ERROR_UNKNOWN_HOST
;
1067 // Check whether host is a IP address for A/AAAA queries.
1068 // For by-type records we have already checked at the beginning of
1070 } else if (addrRec
&& addrRec
->addr
) {
1071 // if the host name is an IP address literal and has been
1072 // parsed, go ahead and use it.
1073 LOG((" Using cached address for IP Literal [%s].\n", host
.get()));
1074 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2
, METHOD_LITERAL
);
1076 } else if (addrRec
&&
1077 PR_StringToNetAddr(host
.get(), &tempAddr
) == PR_SUCCESS
) {
1078 // try parsing the host name as an IP address literal to short
1079 // circuit full host resolution. (this is necessary on some
1080 // platforms like Win9x. see bug 219376 for more details.)
1081 LOG((" Host is IP Literal [%s].\n", host
.get()));
1083 // ok, just copy the result into the host record, and be
1084 // done with it! ;-)
1085 addrRec
->addr
= MakeUnique
<NetAddr
>();
1086 PRNetAddrToNetAddr(&tempAddr
, addrRec
->addr
.get());
1087 // put reference to host record on stack...
1088 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2
, METHOD_LITERAL
);
1091 // Check if we have received too many requests.
1092 } else if (mPendingCount
>= MAX_NON_PRIORITY_REQUESTS
&&
1093 !IsHighPriority(flags
) && !rec
->mResolving
) {
1095 (" Lookup queue full: dropping %s priority request for "
1097 IsMediumPriority(flags
) ? "medium" : "low", host
.get()));
1098 if (IS_ADDR_TYPE(type
)) {
1099 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2
, METHOD_OVERFLOW
);
1101 // This is a lower priority request and we are swamped, so refuse it.
1102 rv
= NS_ERROR_DNS_LOOKUP_QUEUE_FULL
;
1104 // Check if the offline flag is set.
1105 } else if (flags
& RES_OFFLINE
) {
1106 LOG((" Offline request for host [%s]; ignoring.\n", host
.get()));
1107 rv
= NS_ERROR_OFFLINE
;
1109 // We do not have a valid result till here.
1110 // A/AAAA request can check for an alternative entry like AF_UNSPEC.
1111 // Otherwise we need to start a new query.
1112 } else if (!rec
->mResolving
) {
1113 // If this is an IPV4 or IPV6 specific request, check if there is
1114 // an AF_UNSPEC entry we can use. Otherwise, hit the resolver...
1115 if (addrRec
&& !(flags
& RES_BYPASS_CACHE
) &&
1116 ((af
== PR_AF_INET
) || (af
== PR_AF_INET6
))) {
1117 // Check for an AF_UNSPEC entry.
1119 const nsHostKey
unspecKey(
1120 host
, aTrrServer
, nsIDNSService::RESOLVE_TYPE_DEFAULT
, flags
,
1121 PR_AF_UNSPEC
, (aOriginAttributes
.mPrivateBrowsingId
> 0),
1123 RefPtr
<nsHostRecord
> unspecRec
= mRecordDB
.Get(unspecKey
);
1125 TimeStamp now
= TimeStamp::NowLoRes();
1126 if (unspecRec
&& unspecRec
->HasUsableResult(now
, flags
)) {
1127 MOZ_ASSERT(unspecRec
->IsAddrRecord());
1129 RefPtr
<AddrHostRecord
> addrUnspecRec
= do_QueryObject(unspecRec
);
1130 MOZ_ASSERT(addrUnspecRec
);
1131 MOZ_ASSERT(addrUnspecRec
->addr_info
|| addrUnspecRec
->negative
,
1132 "Entry should be resolved or negative.");
1134 LOG((" Trying AF_UNSPEC entry for host [%s] af: %s.\n", host
.get(),
1135 (af
== PR_AF_INET
) ? "AF_INET" : "AF_INET6"));
1137 // We need to lock in case any other thread is reading
1139 MutexAutoLock
lock(addrRec
->addr_info_lock
);
1141 addrRec
->addr_info
= nullptr;
1142 addrRec
->addr_info_gencnt
++;
1143 if (unspecRec
->negative
) {
1144 rec
->negative
= unspecRec
->negative
;
1145 rec
->CopyExpirationTimesAndFlagsFrom(unspecRec
);
1146 } else if (addrUnspecRec
->addr_info
) {
1147 MutexAutoLock
lock(addrUnspecRec
->addr_info_lock
);
1148 if (addrUnspecRec
->addr_info
) {
1149 // Search for any valid address in the AF_UNSPEC entry
1150 // in the cache (not blocklisted and from the right
1152 nsTArray
<NetAddr
> addresses
;
1153 for (const auto& addr
: addrUnspecRec
->addr_info
->Addresses()) {
1154 if ((af
== addr
.inet
.family
) &&
1155 !addrUnspecRec
->Blocklisted(&addr
)) {
1156 addresses
.AppendElement(addr
);
1159 if (!addresses
.IsEmpty()) {
1160 addrRec
->addr_info
= new AddrInfo(
1161 addrUnspecRec
->addr_info
->Hostname(),
1162 addrUnspecRec
->addr_info
->CanonicalHostname(),
1163 addrUnspecRec
->addr_info
->IsTRR(), std::move(addresses
));
1164 addrRec
->addr_info_gencnt
++;
1165 rec
->CopyExpirationTimesAndFlagsFrom(unspecRec
);
1169 // Now check if we have a new record.
1170 if (rec
->HasUsableResult(now
, flags
)) {
1172 if (rec
->negative
) {
1173 status
= NS_ERROR_UNKNOWN_HOST
;
1175 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2
, METHOD_HIT
);
1176 ConditionallyRefreshRecord(rec
, host
);
1177 } else if (af
== PR_AF_INET6
) {
1178 // For AF_INET6, a new lookup means another AF_UNSPEC
1179 // lookup. We have already iterated through the
1180 // AF_UNSPEC addresses, so we mark this record as
1183 (" No AF_INET6 in AF_UNSPEC entry: "
1184 "host [%s] unknown host.",
1187 rec
->negative
= true;
1188 status
= NS_ERROR_UNKNOWN_HOST
;
1189 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2
,
1190 METHOD_NEGATIVE_HIT
);
1195 // If this is a by-type request or if no valid record was found
1196 // in the cache or this is an AF_UNSPEC request, then start a
1199 LOG((" No usable record in cache for host [%s] type %d.", host
.get(),
1202 if (flags
& RES_REFRESH_CACHE
) {
1206 // Add callback to the list of pending callbacks.
1207 rec
->mCallbacks
.insertBack(callback
);
1209 rv
= NameLookup(rec
);
1210 if (IS_ADDR_TYPE(type
)) {
1211 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2
,
1212 METHOD_NETWORK_FIRST
);
1214 if (NS_FAILED(rv
) && callback
->isInList()) {
1218 (" DNS lookup for host [%s] blocking "
1219 "pending 'getaddrinfo' or trr query: "
1221 host
.get(), callback
.get()));
1226 (" Host [%s] is being resolved. Appending callback "
1228 host
.get(), callback
.get()));
1230 rec
->mCallbacks
.insertBack(callback
);
1232 // Only A/AAAA records are place in a queue. The queues are for
1233 // the native resolver, therefore by-type request are never put
1235 if (addrRec
&& addrRec
->onQueue()) {
1236 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2
,
1237 METHOD_NETWORK_SHARED
);
1239 // Consider the case where we are on a pending queue of
1240 // lower priority than the request is being made at.
1241 // In that case we should upgrade to the higher queue.
1243 if (IsHighPriority(flags
) && !IsHighPriority(rec
->flags
)) {
1244 // Move from (low|med) to high.
1246 mHighQ
.insertBack(rec
);
1248 ConditionallyCreateThread(rec
);
1249 } else if (IsMediumPriority(flags
) && IsLowPriority(rec
->flags
)) {
1250 // Move from low to med.
1252 mMediumQ
.insertBack(rec
);
1254 mIdleTaskCV
.Notify();
1259 if (result
&& callback
->isInList()) {
1265 callback
->OnResolveHostComplete(this, result
, status
);
1271 void nsHostResolver::DetachCallback(
1272 const nsACString
& host
, const nsACString
& aTrrServer
, uint16_t aType
,
1273 const OriginAttributes
& aOriginAttributes
, uint16_t flags
, uint16_t af
,
1274 nsResolveHostCallback
* aCallback
, nsresult status
) {
1275 RefPtr
<nsHostRecord
> rec
;
1276 RefPtr
<nsResolveHostCallback
> callback(aCallback
);
1279 MutexAutoLock
lock(mLock
);
1281 nsAutoCString originSuffix
;
1282 aOriginAttributes
.CreateSuffix(originSuffix
);
1284 nsHostKey
key(host
, aTrrServer
, aType
, flags
, af
,
1285 (aOriginAttributes
.mPrivateBrowsingId
> 0), originSuffix
);
1286 RefPtr
<nsHostRecord
> entry
= mRecordDB
.Get(key
);
1288 // walk list looking for |callback|... we cannot assume
1289 // that it will be there!
1291 for (nsResolveHostCallback
* c
: entry
->mCallbacks
) {
1292 if (c
== callback
) {
1301 // complete callback with the given status code; this would only be done if
1302 // the record was in the process of being resolved.
1304 callback
->OnResolveHostComplete(this, rec
, status
);
1308 nsresult
nsHostResolver::ConditionallyCreateThread(nsHostRecord
* rec
) {
1309 if (mNumIdleTasks
) {
1310 // wake up idle tasks to process this lookup
1311 mIdleTaskCV
.Notify();
1312 } else if ((mActiveTaskCount
< HighThreadThreshold
) ||
1313 (IsHighPriority(rec
->flags
) &&
1314 mActiveTaskCount
< MAX_RESOLVER_THREADS
)) {
1315 nsCOMPtr
<nsIRunnable
> event
= mozilla::NewRunnableMethod(
1316 "nsHostResolver::ThreadFunc", this, &nsHostResolver::ThreadFunc
);
1319 mResolverThreads
->Dispatch(event
, nsIEventTarget::DISPATCH_NORMAL
);
1320 if (NS_FAILED(rv
)) {
1324 LOG((" Unable to find a thread for looking up host [%s].\n",
1330 nsresult
nsHostResolver::TrrLookup_unlocked(nsHostRecord
* rec
, TRR
* pushedTRR
) {
1331 MutexAutoLock
lock(mLock
);
1332 return TrrLookup(rec
, pushedTRR
);
1335 void nsHostResolver::MaybeRenewHostRecord(nsHostRecord
* aRec
) {
1336 MutexAutoLock
lock(mLock
);
1337 MaybeRenewHostRecordLocked(aRec
);
1340 void nsHostResolver::MaybeRenewHostRecordLocked(nsHostRecord
* aRec
) {
1341 mLock
.AssertCurrentThreadOwns();
1342 if (aRec
->isInList()) {
1343 // we're already on the eviction queue. This is a renewal
1344 MOZ_ASSERT(mEvictionQSize
);
1345 AssertOnQ(aRec
, mEvictionQ
);
1351 // returns error if no TRR resolve is issued
1352 // it is impt this is not called while a native lookup is going on
1353 nsresult
nsHostResolver::TrrLookup(nsHostRecord
* aRec
, TRR
* pushedTRR
) {
1354 if (Mode() == nsIDNSService::MODE_TRROFF
||
1355 StaticPrefs::network_dns_disabled()) {
1356 return NS_ERROR_UNKNOWN_HOST
;
1358 LOG(("TrrLookup host:%s af:%" PRId16
, aRec
->host
.get(), aRec
->af
));
1360 RefPtr
<nsHostRecord
> rec(aRec
);
1361 mLock
.AssertCurrentThreadOwns();
1363 RefPtr
<AddrHostRecord
> addrRec
;
1364 RefPtr
<TypeHostRecord
> typeRec
;
1366 if (rec
->IsAddrRecord()) {
1367 addrRec
= do_QueryObject(rec
);
1368 MOZ_ASSERT(addrRec
);
1370 typeRec
= do_QueryObject(rec
);
1371 MOZ_ASSERT(typeRec
);
1374 MOZ_ASSERT(!rec
->mResolving
);
1376 nsIRequest::TRRMode reqMode
= rec
->mEffectiveTRRMode
;
1377 if (rec
->mTrrServer
.IsEmpty() &&
1378 (!gTRRService
|| !gTRRService
->Enabled(reqMode
))) {
1379 rec
->RecordReason(nsHostRecord::TRR_NOT_CONFIRMED
);
1380 LOG(("TrrLookup:: %s service not enabled\n", rec
->host
.get()));
1381 return NS_ERROR_UNKNOWN_HOST
;
1384 MaybeRenewHostRecordLocked(rec
);
1386 RefPtr
<TRRQuery
> query
= new TRRQuery(this, rec
);
1387 bool useODoH
= gODoHService
->Enabled() &&
1388 !((rec
->flags
& nsIDNSService::RESOLVE_DISABLE_ODOH
));
1389 nsresult rv
= query
->DispatchLookup(pushedTRR
, useODoH
);
1390 if (NS_FAILED(rv
)) {
1391 rec
->RecordReason(nsHostRecord::TRR_DID_NOT_MAKE_QUERY
);
1396 auto lock
= rec
->mTRRQuery
.Lock();
1397 MOZ_ASSERT(!lock
.ref(), "TRR already in progress");
1405 void nsHostResolver::AssertOnQ(nsHostRecord
* rec
,
1406 LinkedList
<RefPtr
<nsHostRecord
>>& q
) {
1408 MOZ_ASSERT(!q
.isEmpty());
1409 MOZ_ASSERT(rec
->isInList());
1410 for (const RefPtr
<nsHostRecord
>& r
: q
) {
1415 MOZ_ASSERT(false, "Did not find element");
1419 nsresult
nsHostResolver::NativeLookup(nsHostRecord
* aRec
) {
1420 if (StaticPrefs::network_dns_disabled()) {
1421 return NS_ERROR_UNKNOWN_HOST
;
1423 LOG(("NativeLookup host:%s af:%" PRId16
, aRec
->host
.get(), aRec
->af
));
1425 // Only A/AAAA request are resolve natively.
1426 MOZ_ASSERT(aRec
->IsAddrRecord());
1427 mLock
.AssertCurrentThreadOwns();
1429 RefPtr
<nsHostRecord
> rec(aRec
);
1430 RefPtr
<AddrHostRecord
> addrRec
;
1431 addrRec
= do_QueryObject(rec
);
1432 MOZ_ASSERT(addrRec
);
1434 addrRec
->mNativeStart
= TimeStamp::Now();
1436 // Add rec to one of the pending queues, possibly removing it from mEvictionQ.
1437 MaybeRenewHostRecordLocked(rec
);
1439 switch (AddrHostRecord::GetPriority(rec
->flags
)) {
1440 case AddrHostRecord::DNS_PRIORITY_HIGH
:
1441 mHighQ
.insertBack(rec
);
1444 case AddrHostRecord::DNS_PRIORITY_MEDIUM
:
1445 mMediumQ
.insertBack(rec
);
1448 case AddrHostRecord::DNS_PRIORITY_LOW
:
1449 mLowQ
.insertBack(rec
);
1454 addrRec
->StoreNative(true);
1455 addrRec
->StoreNativeUsed(true);
1456 addrRec
->mResolving
++;
1458 nsresult rv
= ConditionallyCreateThread(rec
);
1460 LOG((" DNS thread counters: total=%d any-live=%d idle=%d pending=%d\n",
1461 static_cast<uint32_t>(mActiveTaskCount
),
1462 static_cast<uint32_t>(mActiveAnyThreadCount
),
1463 static_cast<uint32_t>(mNumIdleTasks
),
1464 static_cast<uint32_t>(mPendingCount
)));
1470 nsIDNSService::ResolverMode
nsHostResolver::Mode() {
1472 return gTRRService
->Mode();
1475 // If we don't have a TRR service just return MODE_TRROFF so we don't make
1476 // any TRR requests by mistake.
1477 return nsIDNSService::MODE_TRROFF
;
1480 nsIRequest::TRRMode
nsHostRecord::TRRMode() {
1481 return nsIDNSService::GetTRRModeFromFlags(flags
);
1485 void nsHostResolver::ComputeEffectiveTRRMode(nsHostRecord
* aRec
) {
1486 nsIDNSService::ResolverMode resolverMode
= nsHostResolver::Mode();
1487 nsIRequest::TRRMode requestMode
= aRec
->TRRMode();
1489 // For domains that are excluded from TRR or when parental control is enabled,
1490 // we fallback to NativeLookup. This happens even in MODE_TRRONLY. By default
1491 // localhost and local are excluded (so we cover *.local hosts) See the
1492 // network.trr.excluded-domains pref.
1495 aRec
->RecordReason(nsHostRecord::TRR_NO_GSERVICE
);
1496 aRec
->mEffectiveTRRMode
= requestMode
;
1500 if (!aRec
->mTrrServer
.IsEmpty()) {
1501 aRec
->mEffectiveTRRMode
= nsIRequest::TRR_ONLY_MODE
;
1505 if (gTRRService
->IsExcludedFromTRR(aRec
->host
)) {
1506 aRec
->RecordReason(nsHostRecord::TRR_EXCLUDED
);
1507 aRec
->mEffectiveTRRMode
= nsIRequest::TRR_DISABLED_MODE
;
1511 if (StaticPrefs::network_dns_skipTRR_when_parental_control_enabled() &&
1512 gTRRService
->ParentalControlEnabled()) {
1513 aRec
->RecordReason(nsHostRecord::TRR_PARENTAL_CONTROL
);
1514 aRec
->mEffectiveTRRMode
= nsIRequest::TRR_DISABLED_MODE
;
1518 if (resolverMode
== nsIDNSService::MODE_TRROFF
) {
1519 aRec
->RecordReason(nsHostRecord::TRR_OFF_EXPLICIT
);
1520 aRec
->mEffectiveTRRMode
= nsIRequest::TRR_DISABLED_MODE
;
1524 if (requestMode
== nsIRequest::TRR_DISABLED_MODE
) {
1525 aRec
->RecordReason(nsHostRecord::TRR_REQ_MODE_DISABLED
);
1526 aRec
->mEffectiveTRRMode
= nsIRequest::TRR_DISABLED_MODE
;
1530 if ((requestMode
== nsIRequest::TRR_DEFAULT_MODE
&&
1531 resolverMode
== nsIDNSService::MODE_NATIVEONLY
)) {
1532 aRec
->RecordReason(nsHostRecord::TRR_MODE_NOT_ENABLED
);
1533 aRec
->mEffectiveTRRMode
= nsIRequest::TRR_DISABLED_MODE
;
1537 if (requestMode
== nsIRequest::TRR_DEFAULT_MODE
&&
1538 resolverMode
== nsIDNSService::MODE_TRRFIRST
) {
1539 aRec
->mEffectiveTRRMode
= nsIRequest::TRR_FIRST_MODE
;
1543 if (requestMode
== nsIRequest::TRR_DEFAULT_MODE
&&
1544 resolverMode
== nsIDNSService::MODE_TRRONLY
) {
1545 aRec
->mEffectiveTRRMode
= nsIRequest::TRR_ONLY_MODE
;
1549 aRec
->mEffectiveTRRMode
= requestMode
;
1552 // Kick-off a name resolve operation, using native resolver and/or TRR
1553 nsresult
nsHostResolver::NameLookup(nsHostRecord
* rec
) {
1554 LOG(("NameLookup host:%s af:%" PRId16
, rec
->host
.get(), rec
->af
));
1556 if (rec
->flags
& RES_IP_HINT
) {
1557 LOG(("Skip lookup if RES_IP_HINT is set\n"));
1558 return NS_ERROR_UNKNOWN_HOST
;
1561 nsresult rv
= NS_ERROR_UNKNOWN_HOST
;
1562 if (rec
->mResolving
) {
1563 LOG(("NameLookup %s while already resolving\n", rec
->host
.get()));
1567 // Make sure we reset the reason each time we attempt to do a new lookup
1568 // so we don't wronly report the reason for the previous one.
1569 rec
->mTRRTRRSkippedReason
= nsHostRecord::TRR_UNSET
;
1571 ComputeEffectiveTRRMode(rec
);
1573 if (rec
->IsAddrRecord()) {
1574 RefPtr
<AddrHostRecord
> addrRec
= do_QueryObject(rec
);
1575 MOZ_ASSERT(addrRec
);
1577 addrRec
->StoreNativeUsed(false);
1578 addrRec
->mTRRUsed
= false;
1579 addrRec
->mNativeSuccess
= false;
1582 if (!rec
->mTrrServer
.IsEmpty()) {
1583 LOG(("NameLookup: %s use trr:%s", rec
->host
.get(), rec
->mTrrServer
.get()));
1584 if (rec
->mEffectiveTRRMode
!= nsIRequest::TRR_ONLY_MODE
) {
1585 return NS_ERROR_UNKNOWN_HOST
;
1588 if (rec
->flags
& RES_DISABLE_TRR
) {
1589 LOG(("TRR with server and DISABLE_TRR flag. Returning error."));
1590 return NS_ERROR_UNKNOWN_HOST
;
1592 return TrrLookup(rec
);
1595 LOG(("NameLookup: %s effectiveTRRmode: %d flags: %X", rec
->host
.get(),
1596 rec
->mEffectiveTRRMode
, rec
->flags
));
1598 if (rec
->flags
& RES_DISABLE_TRR
) {
1599 rec
->RecordReason(nsHostRecord::TRR_DISABLED_FLAG
);
1602 if (rec
->mEffectiveTRRMode
!= nsIRequest::TRR_DISABLED_MODE
&&
1603 !((rec
->flags
& RES_DISABLE_TRR
))) {
1604 rv
= TrrLookup(rec
);
1607 bool serviceNotReady
= !gTRRService
|| !gTRRService
->IsConfirmed();
1609 if (rec
->mEffectiveTRRMode
== nsIRequest::TRR_DISABLED_MODE
||
1610 (rec
->mEffectiveTRRMode
== nsIRequest::TRR_FIRST_MODE
&&
1611 (rec
->flags
& RES_DISABLE_TRR
|| serviceNotReady
|| NS_FAILED(rv
)))) {
1612 if (!rec
->IsAddrRecord()) {
1617 // If we use this branch then the mTRRUsed flag should not be set
1618 // Even if we did call TrrLookup above, the fact that it failed sync-ly
1619 // means that we didn't actually succeed in opening the channel.
1620 RefPtr
<AddrHostRecord
> addrRec
= do_QueryObject(rec
);
1621 MOZ_ASSERT(addrRec
&& !addrRec
->mTRRUsed
);
1624 rv
= NativeLookup(rec
);
1630 nsresult
nsHostResolver::ConditionallyRefreshRecord(nsHostRecord
* rec
,
1631 const nsACString
& host
) {
1632 if ((rec
->CheckExpiration(TimeStamp::NowLoRes()) != nsHostRecord::EXP_VALID
||
1635 LOG((" Using %s cache entry for host [%s] but starting async renewal.",
1636 rec
->negative
? "negative" : "positive", host
.BeginReading()));
1639 if (rec
->IsAddrRecord() && !rec
->negative
) {
1640 // negative entries are constantly being refreshed, only
1641 // track positive grace period induced renewals
1642 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2
, METHOD_RENEWAL
);
1648 void nsHostResolver::DeQueue(LinkedList
<RefPtr
<nsHostRecord
>>& aQ
,
1649 AddrHostRecord
** aResult
) {
1650 RefPtr
<nsHostRecord
> rec
= aQ
.popFirst();
1652 MOZ_ASSERT(rec
->IsAddrRecord());
1653 RefPtr
<AddrHostRecord
> addrRec
= do_QueryObject(rec
);
1654 MOZ_ASSERT(addrRec
);
1655 addrRec
.forget(aResult
);
1658 bool nsHostResolver::GetHostToLookup(AddrHostRecord
** result
) {
1659 bool timedOut
= false;
1660 TimeDuration timeout
;
1661 TimeStamp epoch
, now
;
1663 MutexAutoLock
lock(mLock
);
1665 timeout
= (mNumIdleTasks
>= HighThreadThreshold
) ? mShortIdleTimeout
1667 epoch
= TimeStamp::Now();
1669 while (!mShutdown
) {
1670 // remove next record from Q; hand over owning reference. Check high, then
1673 #define SET_GET_TTL(var, val) (var)->StoreGetTtl(sGetTtlEnabled && (val))
1675 if (!mHighQ
.isEmpty()) {
1676 DeQueue(mHighQ
, result
);
1677 SET_GET_TTL(*result
, false);
1681 if (mActiveAnyThreadCount
< HighThreadThreshold
) {
1682 if (!mMediumQ
.isEmpty()) {
1683 DeQueue(mMediumQ
, result
);
1684 mActiveAnyThreadCount
++;
1685 (*result
)->StoreUsingAnyThread(true);
1686 SET_GET_TTL(*result
, true);
1690 if (!mLowQ
.isEmpty()) {
1691 DeQueue(mLowQ
, result
);
1692 mActiveAnyThreadCount
++;
1693 (*result
)->StoreUsingAnyThread(true);
1694 SET_GET_TTL(*result
, true);
1699 // Determining timeout is racy, so allow one cycle through checking the
1700 // queues before exiting.
1705 // wait for one or more of the following to occur:
1706 // (1) the pending queue has a host record to process
1707 // (2) the shutdown flag has been set
1708 // (3) the thread has been idle for too long
1711 mIdleTaskCV
.Wait(timeout
);
1714 now
= TimeStamp::Now();
1716 if (now
- epoch
>= timeout
) {
1719 // It is possible that CondVar::Wait() was interrupted and returned
1720 // early, in which case we will loop back and re-enter it. In that
1721 // case we want to do so with the new timeout reduced to reflect
1722 // time already spent waiting.
1723 timeout
-= now
- epoch
;
1728 // tell thread to exit...
1732 void nsHostResolver::PrepareRecordExpirationAddrRecord(
1733 AddrHostRecord
* rec
) const {
1734 // NOTE: rec->addr_info_lock is already held by parent
1735 MOZ_ASSERT(((bool)rec
->addr_info
) != rec
->negative
);
1736 mLock
.AssertCurrentThreadOwns();
1737 if (!rec
->addr_info
) {
1738 rec
->SetExpiration(TimeStamp::NowLoRes(), NEGATIVE_RECORD_LIFETIME
, 0);
1739 LOG(("Caching host [%s] negative record for %u seconds.\n", rec
->host
.get(),
1740 NEGATIVE_RECORD_LIFETIME
));
1744 unsigned int lifetime
= mDefaultCacheLifetime
;
1745 unsigned int grace
= mDefaultGracePeriod
;
1747 unsigned int ttl
= mDefaultCacheLifetime
;
1748 if (sGetTtlEnabled
|| rec
->addr_info
->IsTRR()) {
1749 if (rec
->addr_info
&& rec
->addr_info
->TTL() != AddrInfo::NO_TTL_DATA
) {
1750 ttl
= rec
->addr_info
->TTL();
1756 rec
->SetExpiration(TimeStamp::NowLoRes(), lifetime
, grace
);
1757 LOG(("Caching host [%s] record for %u seconds (grace %d).", rec
->host
.get(),
1761 static bool different_rrset(AddrInfo
* rrset1
, AddrInfo
* rrset2
) {
1762 if (!rrset1
|| !rrset2
) {
1766 LOG(("different_rrset %s\n", rrset1
->Hostname().get()));
1768 if (rrset1
->IsTRR() != rrset2
->IsTRR()) {
1772 if (rrset1
->Addresses().Length() != rrset2
->Addresses().Length()) {
1773 LOG(("different_rrset true due to length change\n"));
1777 nsTArray
<NetAddr
> orderedSet1
= rrset1
->Addresses().Clone();
1778 nsTArray
<NetAddr
> orderedSet2
= rrset2
->Addresses().Clone();
1782 bool eq
= orderedSet1
== orderedSet2
;
1784 LOG(("different_rrset true due to content change\n"));
1786 LOG(("different_rrset false\n"));
1791 void nsHostResolver::AddToEvictionQ(nsHostRecord
* rec
) {
1792 if (rec
->isInList()) {
1793 MOZ_DIAGNOSTIC_ASSERT(!mEvictionQ
.contains(rec
),
1794 "Already in eviction queue");
1795 MOZ_DIAGNOSTIC_ASSERT(!mHighQ
.contains(rec
), "Already in high queue");
1796 MOZ_DIAGNOSTIC_ASSERT(!mMediumQ
.contains(rec
), "Already in med queue");
1797 MOZ_DIAGNOSTIC_ASSERT(!mLowQ
.contains(rec
), "Already in low queue");
1798 MOZ_DIAGNOSTIC_ASSERT(false, "Already on some other queue?");
1800 // Bug 1678117 - it's not clear why this can happen, but let's fix it
1801 // for release users.
1804 mEvictionQ
.insertBack(rec
);
1805 if (mEvictionQSize
< mMaxCacheEntries
) {
1808 // remove first element on mEvictionQ
1809 RefPtr
<nsHostRecord
> head
= mEvictionQ
.popFirst();
1810 mRecordDB
.Remove(*static_cast<nsHostKey
*>(head
.get()));
1812 if (!head
->negative
) {
1813 // record the age of the entry upon eviction.
1814 TimeDuration age
= TimeStamp::NowLoRes() - head
->mValidStart
;
1815 if (rec
->IsAddrRecord()) {
1816 Telemetry::Accumulate(Telemetry::DNS_CLEANUP_AGE
,
1817 static_cast<uint32_t>(age
.ToSeconds() / 60));
1819 Telemetry::Accumulate(Telemetry::DNS_BY_TYPE_CLEANUP_AGE
,
1820 static_cast<uint32_t>(age
.ToSeconds() / 60));
1822 if (head
->CheckExpiration(TimeStamp::Now()) !=
1823 nsHostRecord::EXP_EXPIRED
) {
1824 if (rec
->IsAddrRecord()) {
1825 Telemetry::Accumulate(Telemetry::DNS_PREMATURE_EVICTION
,
1826 static_cast<uint32_t>(age
.ToSeconds() / 60));
1828 Telemetry::Accumulate(Telemetry::DNS_BY_TYPE_PREMATURE_EVICTION
,
1829 static_cast<uint32_t>(age
.ToSeconds() / 60));
1837 // CompleteLookup() checks if the resolving should be redone and if so it
1838 // returns LOOKUP_RESOLVEAGAIN, but only if 'status' is not NS_ERROR_ABORT.
1839 nsHostResolver::LookupStatus
nsHostResolver::CompleteLookup(
1840 nsHostRecord
* rec
, nsresult status
, AddrInfo
* aNewRRSet
, bool pb
,
1841 const nsACString
& aOriginsuffix
, nsHostRecord::TRRSkippedReason aReason
) {
1842 MutexAutoLock
lock(mLock
);
1844 MOZ_ASSERT(rec
->pb
== pb
);
1845 MOZ_ASSERT(rec
->IsAddrRecord());
1847 RefPtr
<AddrHostRecord
> addrRec
= do_QueryObject(rec
);
1848 MOZ_ASSERT(addrRec
);
1850 RefPtr
<AddrInfo
> newRRSet(aNewRRSet
);
1851 MOZ_ASSERT(NS_FAILED(status
) || newRRSet
->Addresses().Length() > 0);
1853 bool trrResult
= newRRSet
&& newRRSet
->IsTRR();
1854 if (NS_FAILED(status
)) {
1858 if (addrRec
->LoadResolveAgain() && (status
!= NS_ERROR_ABORT
) && !trrResult
) {
1859 LOG(("nsHostResolver record %p resolve again due to flushcache\n",
1861 addrRec
->StoreResolveAgain(false);
1862 return LOOKUP_RESOLVEAGAIN
;
1865 MOZ_ASSERT(addrRec
->mResolving
);
1866 addrRec
->mResolving
--;
1867 LOG(("nsHostResolver::CompleteLookup %s %p %X trr=%d stillResolving=%d\n",
1868 addrRec
->host
.get(), aNewRRSet
, (unsigned int)status
,
1869 aNewRRSet
? aNewRRSet
->IsTRR() : 0, int(addrRec
->mResolving
)));
1872 if (NS_FAILED(status
) && status
!= NS_ERROR_UNKNOWN_HOST
&&
1873 status
!= NS_ERROR_DEFINITIVE_UNKNOWN_HOST
) {
1874 // the errors are not failed resolves, that means
1875 // something else failed, consider this as *TRR not used*
1876 // for actually trying to resolve the host
1877 addrRec
->mTRRUsed
= false;
1879 addrRec
->mTRRUsed
= true;
1882 if (NS_FAILED(status
)) {
1883 if (aReason
!= nsHostRecord::TRR_UNSET
) {
1884 addrRec
->RecordReason(aReason
);
1886 // Unknown failed reason.
1887 addrRec
->RecordReason(nsHostRecord::TRR_FAILED
);
1890 addrRec
->mTRRSuccess
++;
1891 addrRec
->RecordReason(nsHostRecord::TRR_OK
);
1894 if (NS_FAILED(status
) &&
1895 addrRec
->mEffectiveTRRMode
== nsIRequest::TRR_FIRST_MODE
&&
1896 status
!= NS_ERROR_DEFINITIVE_UNKNOWN_HOST
) {
1897 MOZ_ASSERT(!addrRec
->mResolving
);
1898 NativeLookup(addrRec
);
1899 MOZ_ASSERT(addrRec
->mResolving
);
1903 if (!addrRec
->mTRRSuccess
) {
1908 if (NS_FAILED(status
)) {
1909 // This is the error that consumers expect.
1910 status
= NS_ERROR_UNKNOWN_HOST
;
1912 } else { // native resolve completed
1913 if (addrRec
->LoadUsingAnyThread()) {
1914 mActiveAnyThreadCount
--;
1915 addrRec
->StoreUsingAnyThread(false);
1918 addrRec
->mNativeSuccess
= static_cast<bool>(newRRSet
);
1919 if (addrRec
->mNativeSuccess
) {
1920 addrRec
->mNativeDuration
= TimeStamp::Now() - addrRec
->mNativeStart
;
1924 // This should always be cleared when a request is completed.
1925 addrRec
->StoreNative(false);
1927 // update record fields. We might have a addrRec->addr_info already if a
1928 // previous lookup result expired and we're reresolving it or we get
1929 // a late second TRR response.
1931 MutexAutoLock
lock(addrRec
->addr_info_lock
);
1932 RefPtr
<AddrInfo
> old_addr_info
;
1933 if (different_rrset(addrRec
->addr_info
, newRRSet
)) {
1934 LOG(("nsHostResolver record %p new gencnt\n", addrRec
.get()));
1935 old_addr_info
= addrRec
->addr_info
;
1936 addrRec
->addr_info
= std::move(newRRSet
);
1937 addrRec
->addr_info_gencnt
++;
1939 if (addrRec
->addr_info
&& newRRSet
) {
1940 auto builder
= addrRec
->addr_info
->Build();
1941 builder
.SetTTL(newRRSet
->TTL());
1942 // Update trr timings
1943 builder
.SetTrrFetchDuration(newRRSet
->GetTrrFetchDuration());
1944 builder
.SetTrrFetchDurationNetworkOnly(
1945 newRRSet
->GetTrrFetchDurationNetworkOnly());
1947 addrRec
->addr_info
= builder
.Finish();
1949 old_addr_info
= std::move(newRRSet
);
1951 addrRec
->negative
= !addrRec
->addr_info
;
1952 PrepareRecordExpirationAddrRecord(addrRec
);
1955 if (LOG_ENABLED()) {
1956 MutexAutoLock
lock(addrRec
->addr_info_lock
);
1957 if (addrRec
->addr_info
) {
1958 for (const auto& elem
: addrRec
->addr_info
->Addresses()) {
1960 elem
.ToStringBuffer(buf
, sizeof(buf
));
1961 LOG(("CompleteLookup: %s has %s\n", addrRec
->host
.get(), buf
));
1964 LOG(("CompleteLookup: %s has NO address\n", addrRec
->host
.get()));
1968 // get the list of pending callbacks for this lookup, and notify
1969 // them that the lookup is complete.
1970 mozilla::LinkedList
<RefPtr
<nsResolveHostCallback
>> cbs
=
1971 std::move(rec
->mCallbacks
);
1973 LOG(("nsHostResolver record %p calling back dns users status:%X\n",
1974 addrRec
.get(), int(status
)));
1976 for (nsResolveHostCallback
* c
= cbs
.getFirst(); c
;
1977 c
= c
->removeAndGetNext()) {
1978 c
->OnResolveHostComplete(this, rec
, status
);
1981 if (!addrRec
->mResolving
&& !mShutdown
) {
1983 auto trrQuery
= addrRec
->mTRRQuery
.Lock();
1984 if (trrQuery
.ref()) {
1985 addrRec
->mTrrDuration
= trrQuery
.ref()->Duration();
1987 trrQuery
.ref() = nullptr;
1989 addrRec
->ResolveComplete();
1991 AddToEvictionQ(rec
);
1994 #ifdef DNSQUERY_AVAILABLE
1995 // Unless the result is from TRR, resolve again to get TTL
1996 bool hasNativeResult
= false;
1998 MutexAutoLock
lock(addrRec
->addr_info_lock
);
1999 if (addrRec
->addr_info
&& !addrRec
->addr_info
->IsTRR()) {
2000 hasNativeResult
= true;
2003 if (hasNativeResult
&& !mShutdown
&& !addrRec
->LoadGetTtl() &&
2004 !rec
->mResolving
&& sGetTtlEnabled
) {
2005 LOG(("Issuing second async lookup for TTL for host [%s].",
2006 addrRec
->host
.get()));
2007 addrRec
->flags
= (addrRec
->flags
& ~RES_PRIORITY_MEDIUM
) | RES_PRIORITY_LOW
;
2008 DebugOnly
<nsresult
> rv
= NativeLookup(rec
);
2009 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
2010 "Could not issue second async lookup for TTL.");
2016 nsHostResolver::LookupStatus
nsHostResolver::CompleteLookupByType(
2017 nsHostRecord
* rec
, nsresult status
,
2018 mozilla::net::TypeRecordResultType
& aResult
, uint32_t aTtl
, bool pb
) {
2019 MutexAutoLock
lock(mLock
);
2021 MOZ_ASSERT(rec
->pb
== pb
);
2022 MOZ_ASSERT(!rec
->IsAddrRecord());
2024 RefPtr
<TypeHostRecord
> typeRec
= do_QueryObject(rec
);
2025 MOZ_ASSERT(typeRec
);
2027 MOZ_ASSERT(typeRec
->mResolving
);
2028 typeRec
->mResolving
--;
2031 auto lock
= rec
->mTRRQuery
.Lock();
2032 lock
.ref() = nullptr;
2035 uint32_t duration
= static_cast<uint32_t>(
2036 (TimeStamp::Now() - typeRec
->mStart
).ToMilliseconds());
2038 if (NS_FAILED(status
)) {
2039 LOG(("nsHostResolver::CompleteLookupByType record %p [%s] status %x\n",
2040 typeRec
.get(), typeRec
->host
.get(), (unsigned int)status
));
2041 typeRec
->SetExpiration(TimeStamp::NowLoRes(), NEGATIVE_RECORD_LIFETIME
, 0);
2042 MOZ_ASSERT(aResult
.is
<TypeRecordEmpty
>());
2043 status
= NS_ERROR_UNKNOWN_HOST
;
2044 typeRec
->negative
= true;
2045 Telemetry::Accumulate(Telemetry::DNS_BY_TYPE_FAILED_LOOKUP_TIME
, duration
);
2047 size_t recordCount
= 0;
2048 if (aResult
.is
<TypeRecordTxt
>()) {
2049 recordCount
= aResult
.as
<TypeRecordTxt
>().Length();
2050 } else if (aResult
.is
<TypeRecordHTTPSSVC
>()) {
2051 recordCount
= aResult
.as
<TypeRecordHTTPSSVC
>().Length();
2054 ("nsHostResolver::CompleteLookupByType record %p [%s], number of "
2056 typeRec
.get(), typeRec
->host
.get(), recordCount
));
2057 MutexAutoLock
typeLock(typeRec
->mResultsLock
);
2058 typeRec
->mResults
= aResult
;
2059 typeRec
->SetExpiration(TimeStamp::NowLoRes(), aTtl
, mDefaultGracePeriod
);
2060 typeRec
->negative
= false;
2061 Telemetry::Accumulate(Telemetry::DNS_BY_TYPE_SUCCEEDED_LOOKUP_TIME
,
2065 mozilla::LinkedList
<RefPtr
<nsResolveHostCallback
>> cbs
=
2066 std::move(typeRec
->mCallbacks
);
2069 ("nsHostResolver::CompleteLookupByType record %p calling back dns "
2073 for (nsResolveHostCallback
* c
= cbs
.getFirst(); c
;
2074 c
= c
->removeAndGetNext()) {
2075 c
->OnResolveHostComplete(this, rec
, status
);
2078 AddToEvictionQ(rec
);
2082 void nsHostResolver::CancelAsyncRequest(
2083 const nsACString
& host
, const nsACString
& aTrrServer
, uint16_t aType
,
2084 const OriginAttributes
& aOriginAttributes
, uint16_t flags
, uint16_t af
,
2085 nsIDNSListener
* aListener
, nsresult status
)
2088 MutexAutoLock
lock(mLock
);
2090 nsAutoCString originSuffix
;
2091 aOriginAttributes
.CreateSuffix(originSuffix
);
2093 // Lookup the host record associated with host, flags & address family
2095 nsHostKey
key(host
, aTrrServer
, aType
, flags
, af
,
2096 (aOriginAttributes
.mPrivateBrowsingId
> 0), originSuffix
);
2097 RefPtr
<nsHostRecord
> rec
= mRecordDB
.Get(key
);
2099 nsHostRecord
* recPtr
= nullptr;
2101 for (const RefPtr
<nsResolveHostCallback
>& c
: rec
->mCallbacks
) {
2102 if (c
->EqualsAsyncListener(aListener
)) {
2105 c
->OnResolveHostComplete(this, recPtr
, status
);
2110 // If there are no more callbacks, remove the hash table entry
2111 if (recPtr
&& recPtr
->mCallbacks
.isEmpty()) {
2112 mRecordDB
.Remove(*static_cast<nsHostKey
*>(recPtr
));
2113 // If record is on a Queue, remove it and then deref it
2114 if (recPtr
->isInList()) {
2121 size_t nsHostResolver::SizeOfIncludingThis(MallocSizeOf mallocSizeOf
) const {
2122 MutexAutoLock
lock(mLock
);
2124 size_t n
= mallocSizeOf(this);
2126 n
+= mRecordDB
.ShallowSizeOfExcludingThis(mallocSizeOf
);
2127 for (auto iter
= mRecordDB
.ConstIter(); !iter
.Done(); iter
.Next()) {
2128 auto* entry
= iter
.UserData();
2129 n
+= entry
->SizeOfIncludingThis(mallocSizeOf
);
2132 // The following fields aren't measured.
2133 // - mHighQ, mMediumQ, mLowQ, mEvictionQ, because they just point to
2134 // nsHostRecords that also pointed to by entries |mRecordDB|, and
2135 // measured when |mRecordDB| is measured.
2140 void nsHostResolver::ThreadFunc() {
2141 LOG(("DNS lookup thread - starting execution.\n"));
2143 #if defined(RES_RETRY_ON_FAILURE)
2146 RefPtr
<AddrHostRecord
> rec
;
2147 RefPtr
<AddrInfo
> ai
;
2151 RefPtr
<AddrHostRecord
> tmpRec
;
2152 if (!GetHostToLookup(getter_AddRefs(tmpRec
))) {
2153 break; // thread shutdown signal
2155 // GetHostToLookup() returns an owning reference
2160 LOG1(("DNS lookup thread - Calling getaddrinfo for host [%s].\n",
2163 TimeStamp startTime
= TimeStamp::Now();
2164 bool getTtl
= rec
->LoadGetTtl();
2165 TimeDuration inQueue
= startTime
- rec
->mNativeStart
;
2166 uint32_t ms
= static_cast<uint32_t>(inQueue
.ToMilliseconds());
2167 Telemetry::Accumulate(Telemetry::DNS_NATIVE_QUEUING
, ms
);
2169 GetAddrInfo(rec
->host
, rec
->af
, rec
->flags
, getter_AddRefs(ai
), getTtl
);
2170 #if defined(RES_RETRY_ON_FAILURE)
2171 if (NS_FAILED(status
) && rs
.Reset()) {
2172 status
= GetAddrInfo(rec
->host
, rec
->af
, rec
->flags
, getter_AddRefs(ai
),
2177 { // obtain lock to check shutdown and manage inter-module telemetry
2178 MutexAutoLock
lock(mLock
);
2181 TimeDuration elapsed
= TimeStamp::Now() - startTime
;
2182 uint32_t millis
= static_cast<uint32_t>(elapsed
.ToMilliseconds());
2184 if (NS_SUCCEEDED(status
)) {
2185 Telemetry::HistogramID histogramID
;
2186 if (!rec
->addr_info_gencnt
) {
2187 // Time for initial lookup.
2188 histogramID
= Telemetry::DNS_LOOKUP_TIME
;
2189 } else if (!getTtl
) {
2190 // Time for renewal; categorized by expiration strategy.
2191 histogramID
= Telemetry::DNS_RENEWAL_TIME
;
2193 // Time to get TTL; categorized by expiration strategy.
2194 histogramID
= Telemetry::DNS_RENEWAL_TIME_FOR_TTL
;
2196 Telemetry::Accumulate(histogramID
, millis
);
2198 Telemetry::Accumulate(Telemetry::DNS_FAILED_LOOKUP_TIME
, millis
);
2203 LOG1(("DNS lookup thread - lookup completed for host [%s]: %s.\n",
2204 rec
->host
.get(), ai
? "success" : "failure: unknown host"));
2206 if (LOOKUP_RESOLVEAGAIN
== CompleteLookup(rec
, status
, ai
, rec
->pb
,
2208 rec
->mTRRTRRSkippedReason
)) {
2209 // leave 'rec' assigned and loop to make a renewed host resolve
2210 LOG(("DNS lookup thread - Re-resolving host [%s].\n", rec
->host
.get()));
2217 LOG(("DNS lookup thread - queue empty, task finished.\n"));
2220 void nsHostResolver::SetCacheLimits(uint32_t aMaxCacheEntries
,
2221 uint32_t aDefaultCacheEntryLifetime
,
2222 uint32_t aDefaultGracePeriod
) {
2223 MutexAutoLock
lock(mLock
);
2224 mMaxCacheEntries
= aMaxCacheEntries
;
2225 mDefaultCacheLifetime
= aDefaultCacheEntryLifetime
;
2226 mDefaultGracePeriod
= aDefaultGracePeriod
;
2229 nsresult
nsHostResolver::Create(uint32_t maxCacheEntries
,
2230 uint32_t defaultCacheEntryLifetime
,
2231 uint32_t defaultGracePeriod
,
2232 nsHostResolver
** result
) {
2233 RefPtr
<nsHostResolver
> res
= new nsHostResolver(
2234 maxCacheEntries
, defaultCacheEntryLifetime
, defaultGracePeriod
);
2236 nsresult rv
= res
->Init();
2237 if (NS_FAILED(rv
)) {
2245 void nsHostResolver::GetDNSCacheEntries(nsTArray
<DNSCacheEntries
>* args
) {
2246 MutexAutoLock
lock(mLock
);
2247 for (auto iter
= mRecordDB
.Iter(); !iter
.Done(); iter
.Next()) {
2248 // We don't pay attention to address literals, only resolved domains.
2249 // Also require a host.
2250 nsHostRecord
* rec
= iter
.UserData();
2251 MOZ_ASSERT(rec
, "rec should never be null here!");
2257 // For now we only show A/AAAA records.
2258 if (!rec
->IsAddrRecord()) {
2262 RefPtr
<AddrHostRecord
> addrRec
= do_QueryObject(rec
);
2263 MOZ_ASSERT(addrRec
);
2264 if (!addrRec
|| !addrRec
->addr_info
) {
2268 DNSCacheEntries info
;
2269 info
.hostname
= rec
->host
;
2270 info
.family
= rec
->af
;
2272 (int64_t)(rec
->mValidEnd
- TimeStamp::NowLoRes()).ToSeconds();
2273 if (info
.expiration
<= 0) {
2274 // We only need valid DNS cache entries
2279 MutexAutoLock
lock(addrRec
->addr_info_lock
);
2280 for (const auto& addr
: addrRec
->addr_info
->Addresses()) {
2281 char buf
[kIPv6CStrBufSize
];
2282 if (addr
.ToStringBuffer(buf
, sizeof(buf
))) {
2283 info
.hostaddr
.AppendElement(buf
);
2286 info
.TRR
= addrRec
->addr_info
->IsTRR();
2289 info
.originAttributesSuffix
= iter
.Key().originSuffix
;
2291 args
->AppendElement(std::move(info
));