Bug 1688832: part 5) Add `static` `AccessibleCaretManager::GetSelection`, `::GetFrame...
[gecko.git] / netwerk / dns / nsHostResolver.cpp
blob8073d15990f5dfd0cadececf5d77f7d8141ea1ed
1 /* vim:set ts=4 sw=2 sts=2 et cin: */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #if defined(HAVE_RES_NINIT)
7 # include <sys/types.h>
8 # include <netinet/in.h>
9 # include <arpa/inet.h>
10 # include <arpa/nameser.h>
11 # include <resolv.h>
12 # define RES_RETRY_ON_FAILURE
13 #endif
15 #include <stdlib.h>
16 #include <ctime>
17 #include "nsHostResolver.h"
18 #include "nsError.h"
19 #include "nsISupportsBase.h"
20 #include "nsISupportsUtils.h"
21 #include "nsIThreadManager.h"
22 #include "nsComponentManagerUtils.h"
23 #include "nsPrintfCString.h"
24 #include "nsXPCOMCIDInternal.h"
25 #include "prthread.h"
26 #include "prerror.h"
27 #include "prtime.h"
28 #include "mozilla/Logging.h"
29 #include "PLDHashTable.h"
30 #include "plstr.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"
37 #include "TRR.h"
38 #include "TRRQuery.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
54 // constant always.
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
80 static_assert(
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");
88 #define LOG(args) \
89 MOZ_LOG(mozilla::net::gHostResolverLog, mozilla::LogLevel::Debug, args)
90 #define LOG1(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.
107 class nsResState {
108 public:
109 nsResState()
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()) {}
119 bool Reset() {
120 // reset no more than once per second
121 if (PR_IntervalToSeconds(PR_IntervalNow() - mLastReset) < 1) {
122 return false;
125 LOG(("Calling 'res_ninit'.\n"));
127 mLastReset = PR_IntervalNow();
128 return (res_ninit(&_res) == 0);
131 private:
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) \
157 ((_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)
167 : host(aHost),
168 mTrrServer(aTrrServer),
169 type(aType),
170 flags(aFlags),
171 af(aAf),
172 pb(aPb),
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 {
189 size_t n = 0;
190 n += host.SizeOfExcludingThisIfUnshared(mallocSizeOf);
191 n += mTrrServer.SizeOfExcludingThisIfUnshared(mallocSizeOf);
192 n += originSuffix.SizeOfExcludingThisIfUnshared(mallocSizeOf);
193 return n;
196 NS_IMPL_ISUPPORTS0(nsHostRecord)
198 nsHostRecord::nsHostRecord(const nsHostKey& key)
199 : nsHostKey(key),
200 mEffectiveTRRMode(nsIRequest::TRR_DEFAULT_MODE),
201 mTRRQuery("nsHostRecord.mTRRQuery"),
202 mResolving(0),
203 negative(false),
204 mDoomed(false) {}
206 void nsHostRecord::Invalidate() { mDoomed = true; }
208 void nsHostRecord::Cancel() {
209 RefPtr<TRRQuery> query;
211 auto lock = mTRRQuery.Lock();
212 query.swap(lock.ref());
215 if (query) {
216 query->Cancel();
220 nsHostRecord::ExpirationStatus nsHostRecord::CheckExpiration(
221 const mozilla::TimeStamp& now) const {
222 if (!mGraceStart.IsNull() && now >= mGraceStart && !mValidEnd.IsNull() &&
223 now < mValidEnd) {
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) {
235 mValidStart = now;
236 if ((valid + grace) < 60) {
237 grace = 60 - valid;
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 {
256 if (mDoomed) {
257 return false;
260 // don't use cached negative results for high priority queries.
261 if (negative && IsHighPriority(queryFlags)) {
262 return false;
265 if (CheckExpiration(now) == EXP_EXPIRED) {
266 return false;
269 if (negative) {
270 return true;
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;
282 t = t->getNext()) {
283 n += t->SizeOfIncludingThis(mallocSizeOf);
286 return n;
289 NS_IMPL_ISUPPORTS_INHERITED(AddrHostRecord, nsHostRecord, AddrHostRecord)
291 AddrHostRecord::AddrHostRecord(const nsHostKey& key)
292 : nsHostRecord(key),
293 addr_info_lock("AddrHostRecord.addr_info_lock"),
294 addr_info_gencnt(0),
295 addr_info(nullptr),
296 addr(nullptr),
297 mTRRUsed(false),
298 mTRRSuccess(0),
299 mNativeSuccess(0) {}
301 AddrHostRecord::~AddrHostRecord() {
302 mCallbacks.clear();
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(),
309 this));
311 // skip the string conversion for the common case of no blocklist
312 if (!mUnusableItems.Length()) {
313 return false;
316 char buf[kIPv6CStrBufSize];
317 if (!aQuery->ToStringBuffer(buf, sizeof(buf))) {
318 return false;
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()));
325 return true;
329 return false;
332 void AddrHostRecord::ReportUnusable(const NetAddr* aAddress) {
333 addr_info_lock.AssertCurrentThreadOwns();
334 LOG(
335 ("Adding address to blocklist for host [%s], host record [%p]."
336 "used trr=%d\n",
337 host.get(), this, mTRRSuccess));
339 ++mUnusableCount;
341 char buf[kIPv6CStrBufSize];
342 if (aAddress->ToStringBuffer(buf, sizeof(buf))) {
343 LOG(
344 ("Successfully adding address [%s] to blocklist for host "
345 "[%s].\n",
346 buf, host.get()));
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(),
354 this));
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);
371 return n;
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()) {
384 return false;
386 if (LoadNative()) {
387 if (!onQueue()) {
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.
395 return false;
397 // Already resolved; not in a pending state; remove from cache
398 return true;
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);
413 if (mTRRUsed) {
414 if (mTRRSuccess) {
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);
440 } else {
441 if (mTRRSuccess) {
442 AccumulateCategoricalKeyed(TRRService::AutoDetectedKey(),
443 Telemetry::LABELS_DNS_TRR_FIRST3::TRR);
444 } else if (mNativeSuccess) {
445 if (mTRRUsed) {
446 AccumulateCategoricalKeyed(
447 TRRService::AutoDetectedKey(),
448 Telemetry::LABELS_DNS_TRR_FIRST3::NativeAfterTRR);
449 } else {
450 AccumulateCategoricalKeyed(TRRService::AutoDetectedKey(),
451 Telemetry::LABELS_DNS_TRR_FIRST3::Native);
453 } else {
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);
464 break;
465 case nsIRequest::TRR_FIRST_MODE:
466 AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::trrFirst);
467 break;
468 case nsIRequest::TRR_ONLY_MODE:
469 AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::trrOnly);
470 break;
471 case nsIRequest::TRR_DEFAULT_MODE:
472 MOZ_ASSERT_UNREACHABLE("We should not have a default value here");
473 break;
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)
496 : nsHostRecord(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) {
508 // deep copy
509 MutexAutoLock lock(mResultsLock);
511 if (!mResults.is<TypeRecordTxt>()) {
512 return NS_ERROR_NOT_AVAILABLE;
514 aRecords = mResults.as<CopyableTArray<nsCString>>();
515 return NS_OK;
518 NS_IMETHODIMP TypeHostRecord::GetRecordsAsOneString(nsACString& aRecords) {
519 // deep copy
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]);
529 return NS_OK;
532 size_t TypeHostRecord::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const {
533 size_t n = mallocSizeOf(this);
535 n += nsHostKey::SizeOfExcludingThis(mallocSizeOf);
536 n += SizeOfResolveHostCallbackListExcludingHead(mCallbacks, mallocSizeOf);
538 return n;
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);
555 return mResults;
558 NS_IMETHODIMP
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);
572 return NS_OK;
575 NS_IMETHODIMP
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);
586 if (!result) {
587 return NS_ERROR_NOT_AVAILABLE;
590 result.forget(aRecord);
591 return NS_OK;
594 NS_IMETHODIMP
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);
608 return NS_OK;
611 NS_IMETHODIMP
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);
621 return NS_OK;
624 NS_IMETHODIMP
625 TypeHostRecord::GetAllRecordsExcluded(bool* aResult) {
626 NS_ENSURE_ARG(aResult);
628 if (!mResults.is<TypeRecordHTTPSSVC>()) {
629 return NS_ERROR_NOT_AVAILABLE;
632 *aResult = mAllRecordsExcluded;
633 return NS_OK;
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!");
649 MOZ_ASSERT(aSelf);
651 if (!strcmp(aPref, kPrefGetTtl)) {
652 #ifdef DNSQUERY_AVAILABLE
653 sGetTtlEnabled = Preferences::GetBool(kPrefGetTtl);
654 #endif
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"),
670 mEvictionQSize(0),
671 mShutdown(true),
672 mNumIdleTasks(0),
673 mActiveTaskCount(0),
674 mActiveAnyThreadCount(0),
675 mPendingCount(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));
692 mShutdown = false;
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"));
719 res_ninit(&_res);
721 #endif
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;
730 } else {
731 // We clamp down the idle time between 0 and one hour.
732 poolTimeoutMs =
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));
740 MOZ_ALWAYS_SUCCEEDS(
741 threadPool->SetThreadStackSize(nsIThreadManager::kThreadPoolStackSize));
742 MOZ_ALWAYS_SUCCEEDS(threadPool->SetName("DNS Resolver"_ns));
743 mResolverThreads = ToRefPtr(std::move(threadPool));
745 return NS_OK;
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) {
753 rec->Cancel();
754 if (rec->IsAddrRecord()) {
755 CompleteLookup(rec, NS_ERROR_ABORT, nullptr, rec->pb, rec->originSuffix,
756 rec->mTRRTRRSkippedReason);
757 } else {
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
768 // differently now.
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);
777 mEvictionQSize = 0;
779 // Clear the evictionQ and remove all its corresponding entries from
780 // the cache first
781 if (!mEvictionQ.isEmpty()) {
782 for (const RefPtr<nsHostRecord>& rec : mEvictionQ) {
783 rec->Cancel();
784 mRecordDB.Remove(*static_cast<nsHostKey*>(rec));
786 mEvictionQ.clear();
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);
797 MOZ_ASSERT(addrRec);
798 if (addrRec->RemoveOrRefresh(aTrrToo)) {
799 if (record->isInList()) {
800 record->remove();
802 iter.Remove();
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,
819 evictionQ;
822 MutexAutoLock lock(mLock);
824 mShutdown = true;
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);
832 mEvictionQSize = 0;
833 mPendingCount = 0;
835 if (mNumIdleTasks) {
836 mIdleTaskCV.NotifyAll();
839 for (auto iter = mRecordDB.Iter(); !iter.Done(); iter.Next()) {
840 iter.UserData()->Cancel();
842 // empty host database
843 mRecordDB.Clear();
845 mNCS = nullptr;
848 ClearPendingQueue(pendingQHigh);
849 ClearPendingQueue(pendingQMed);
850 ClearPendingQueue(pendingQLow);
852 if (!evictionQ.isEmpty()) {
853 for (const RefPtr<nsHostRecord>& rec : evictionQ) {
854 rec->Cancel();
858 pendingQHigh.clear();
859 pendingQMed.clear();
860 pendingQLow.clear();
861 evictionQ.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);
884 if (!entry) {
885 entry = InitRecord(key);
888 RefPtr<nsHostRecord> rec = entry;
889 if (rec->IsAddrRecord()) {
890 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
891 if (addrRec->addr) {
892 return NS_ERROR_FAILURE;
896 if (rec->mResolving) {
897 return NS_ERROR_FAILURE;
900 *result = rec.forget().take();
901 return NS_OK;
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) {
913 MOZ_ASSERT(aRv);
914 MOZ_ASSERT(IS_ADDR_TYPE(key.type));
916 *aRv = NS_ERROR_FAILURE;
917 RefPtr<nsHostRecord> rec = InitRecord(key);
919 nsTArray<NetAddr> addresses;
920 PRNetAddr prAddr;
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;
940 *aRv = NS_OK;
941 return rec.forget();
944 nsresult nsHostResolver::ResolveHost(const nsACString& aHost,
945 const nsACString& aTrrServer,
946 uint16_t type,
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
966 // immediately.
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.
972 PRNetAddr tempAddr;
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);
992 if (mShutdown) {
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)) {
1019 nsresult rv;
1020 RefPtr<nsHostRecord> result = InitLoopbackRecord(key, &rv);
1021 if (NS_WARN_IF(NS_FAILED(rv))) {
1022 return rv;
1024 MOZ_ASSERT(result);
1025 aCallback->OnResolveHostComplete(this, result, NS_OK);
1026 return NS_OK;
1029 RefPtr<nsHostRecord>& entry = mRecordDB.GetOrInsert(key);
1030 if (!entry) {
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...
1048 result = rec;
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
1069 // this function.
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);
1075 result = rec;
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);
1089 result = rec;
1091 // Check if we have received too many requests.
1092 } else if (mPendingCount >= MAX_NON_PRIORITY_REQUESTS &&
1093 !IsHighPriority(flags) && !rec->mResolving) {
1094 LOG(
1095 (" Lookup queue full: dropping %s priority request for "
1096 "host [%s].\n",
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),
1122 originSuffix);
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
1138 // addr_info.
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
1151 // family).
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)) {
1171 result = rec;
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
1181 // negative.
1182 LOG(
1183 (" No AF_INET6 in AF_UNSPEC entry: "
1184 "host [%s] unknown host.",
1185 host.get()));
1186 result = rec;
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
1197 // new lookup.
1198 if (!result) {
1199 LOG((" No usable record in cache for host [%s] type %d.", host.get(),
1200 type));
1202 if (flags & RES_REFRESH_CACHE) {
1203 rec->Invalidate();
1206 // Add callback to the list of pending callbacks.
1207 rec->mCallbacks.insertBack(callback);
1208 rec->flags = flags;
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()) {
1215 callback->remove();
1216 } else {
1217 LOG(
1218 (" DNS lookup for host [%s] blocking "
1219 "pending 'getaddrinfo' or trr query: "
1220 "callback [%p]",
1221 host.get(), callback.get()));
1224 } else {
1225 LOG(
1226 (" Host [%s] is being resolved. Appending callback "
1227 "[%p].",
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
1234 // into a queue.
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.
1245 rec->remove();
1246 mHighQ.insertBack(rec);
1247 rec->flags = flags;
1248 ConditionallyCreateThread(rec);
1249 } else if (IsMediumPriority(flags) && IsLowPriority(rec->flags)) {
1250 // Move from low to med.
1251 rec->remove();
1252 mMediumQ.insertBack(rec);
1253 rec->flags = flags;
1254 mIdleTaskCV.Notify();
1259 if (result && callback->isInList()) {
1260 callback->remove();
1262 } // lock
1264 if (result) {
1265 callback->OnResolveHostComplete(this, result, status);
1268 return rv;
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);
1287 if (entry) {
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) {
1293 rec = entry;
1294 c->remove();
1295 break;
1301 // complete callback with the given status code; this would only be done if
1302 // the record was in the process of being resolved.
1303 if (rec) {
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);
1317 mActiveTaskCount++;
1318 nsresult rv =
1319 mResolverThreads->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
1320 if (NS_FAILED(rv)) {
1321 mActiveTaskCount--;
1323 } else {
1324 LOG((" Unable to find a thread for looking up host [%s].\n",
1325 rec->host.get()));
1327 return NS_OK;
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);
1346 aRec->remove();
1347 mEvictionQSize--;
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);
1369 } else {
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);
1392 return rv;
1396 auto lock = rec->mTRRQuery.Lock();
1397 MOZ_ASSERT(!lock.ref(), "TRR already in progress");
1398 lock.ref() = query;
1401 rec->mResolving++;
1402 return NS_OK;
1405 void nsHostResolver::AssertOnQ(nsHostRecord* rec,
1406 LinkedList<RefPtr<nsHostRecord>>& q) {
1407 #ifdef DEBUG
1408 MOZ_ASSERT(!q.isEmpty());
1409 MOZ_ASSERT(rec->isInList());
1410 for (const RefPtr<nsHostRecord>& r : q) {
1411 if (rec == r) {
1412 return;
1415 MOZ_ASSERT(false, "Did not find element");
1416 #endif
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);
1442 break;
1444 case AddrHostRecord::DNS_PRIORITY_MEDIUM:
1445 mMediumQ.insertBack(rec);
1446 break;
1448 case AddrHostRecord::DNS_PRIORITY_LOW:
1449 mLowQ.insertBack(rec);
1450 break;
1452 mPendingCount++;
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)));
1466 return rv;
1469 // static
1470 nsIDNSService::ResolverMode nsHostResolver::Mode() {
1471 if (gTRRService) {
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);
1484 // static
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.
1494 if (!gTRRService) {
1495 aRec->RecordReason(nsHostRecord::TRR_NO_GSERVICE);
1496 aRec->mEffectiveTRRMode = requestMode;
1497 return;
1500 if (!aRec->mTrrServer.IsEmpty()) {
1501 aRec->mEffectiveTRRMode = nsIRequest::TRR_ONLY_MODE;
1502 return;
1505 if (gTRRService->IsExcludedFromTRR(aRec->host)) {
1506 aRec->RecordReason(nsHostRecord::TRR_EXCLUDED);
1507 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1508 return;
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;
1515 return;
1518 if (resolverMode == nsIDNSService::MODE_TRROFF) {
1519 aRec->RecordReason(nsHostRecord::TRR_OFF_EXPLICIT);
1520 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1521 return;
1524 if (requestMode == nsIRequest::TRR_DISABLED_MODE) {
1525 aRec->RecordReason(nsHostRecord::TRR_REQ_MODE_DISABLED);
1526 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1527 return;
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;
1534 return;
1537 if (requestMode == nsIRequest::TRR_DEFAULT_MODE &&
1538 resolverMode == nsIDNSService::MODE_TRRFIRST) {
1539 aRec->mEffectiveTRRMode = nsIRequest::TRR_FIRST_MODE;
1540 return;
1543 if (requestMode == nsIRequest::TRR_DEFAULT_MODE &&
1544 resolverMode == nsIDNSService::MODE_TRRONLY) {
1545 aRec->mEffectiveTRRMode = nsIRequest::TRR_ONLY_MODE;
1546 return;
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()));
1564 return NS_OK;
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()) {
1613 return rv;
1616 #ifdef DEBUG
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);
1622 #endif
1624 rv = NativeLookup(rec);
1627 return rv;
1630 nsresult nsHostResolver::ConditionallyRefreshRecord(nsHostRecord* rec,
1631 const nsACString& host) {
1632 if ((rec->CheckExpiration(TimeStamp::NowLoRes()) != nsHostRecord::EXP_VALID ||
1633 rec->negative) &&
1634 !rec->mResolving) {
1635 LOG((" Using %s cache entry for host [%s] but starting async renewal.",
1636 rec->negative ? "negative" : "positive", host.BeginReading()));
1637 NameLookup(rec);
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);
1645 return NS_OK;
1648 void nsHostResolver::DeQueue(LinkedList<RefPtr<nsHostRecord>>& aQ,
1649 AddrHostRecord** aResult) {
1650 RefPtr<nsHostRecord> rec = aQ.popFirst();
1651 mPendingCount--;
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
1666 : mLongIdleTimeout;
1667 epoch = TimeStamp::Now();
1669 while (!mShutdown) {
1670 // remove next record from Q; hand over owning reference. Check high, then
1671 // med, then low
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);
1678 return true;
1681 if (mActiveAnyThreadCount < HighThreadThreshold) {
1682 if (!mMediumQ.isEmpty()) {
1683 DeQueue(mMediumQ, result);
1684 mActiveAnyThreadCount++;
1685 (*result)->StoreUsingAnyThread(true);
1686 SET_GET_TTL(*result, true);
1687 return true;
1690 if (!mLowQ.isEmpty()) {
1691 DeQueue(mLowQ, result);
1692 mActiveAnyThreadCount++;
1693 (*result)->StoreUsingAnyThread(true);
1694 SET_GET_TTL(*result, true);
1695 return true;
1699 // Determining timeout is racy, so allow one cycle through checking the
1700 // queues before exiting.
1701 if (timedOut) {
1702 break;
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
1710 mNumIdleTasks++;
1711 mIdleTaskCV.Wait(timeout);
1712 mNumIdleTasks--;
1714 now = TimeStamp::Now();
1716 if (now - epoch >= timeout) {
1717 timedOut = true;
1718 } else {
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;
1724 epoch = now;
1728 // tell thread to exit...
1729 return false;
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));
1741 return;
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();
1752 lifetime = ttl;
1753 grace = 0;
1756 rec->SetExpiration(TimeStamp::NowLoRes(), lifetime, grace);
1757 LOG(("Caching host [%s] record for %u seconds (grace %d).", rec->host.get(),
1758 lifetime, grace));
1761 static bool different_rrset(AddrInfo* rrset1, AddrInfo* rrset2) {
1762 if (!rrset1 || !rrset2) {
1763 return true;
1766 LOG(("different_rrset %s\n", rrset1->Hostname().get()));
1768 if (rrset1->IsTRR() != rrset2->IsTRR()) {
1769 return true;
1772 if (rrset1->Addresses().Length() != rrset2->Addresses().Length()) {
1773 LOG(("different_rrset true due to length change\n"));
1774 return true;
1777 nsTArray<NetAddr> orderedSet1 = rrset1->Addresses().Clone();
1778 nsTArray<NetAddr> orderedSet2 = rrset2->Addresses().Clone();
1779 orderedSet1.Sort();
1780 orderedSet2.Sort();
1782 bool eq = orderedSet1 == orderedSet2;
1783 if (!eq) {
1784 LOG(("different_rrset true due to content change\n"));
1785 } else {
1786 LOG(("different_rrset false\n"));
1788 return !eq;
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.
1802 rec->remove();
1804 mEvictionQ.insertBack(rec);
1805 if (mEvictionQSize < mMaxCacheEntries) {
1806 mEvictionQSize++;
1807 } else {
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));
1818 } else {
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));
1827 } else {
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);
1843 MOZ_ASSERT(rec);
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)) {
1855 newRRSet = nullptr;
1858 if (addrRec->LoadResolveAgain() && (status != NS_ERROR_ABORT) && !trrResult) {
1859 LOG(("nsHostResolver record %p resolve again due to flushcache\n",
1860 addrRec.get()));
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)));
1871 if (trrResult) {
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;
1878 } else {
1879 addrRec->mTRRUsed = true;
1882 if (NS_FAILED(status)) {
1883 if (aReason != nsHostRecord::TRR_UNSET) {
1884 addrRec->RecordReason(aReason);
1885 } else {
1886 // Unknown failed reason.
1887 addrRec->RecordReason(nsHostRecord::TRR_FAILED);
1889 } else {
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);
1900 return LOOKUP_OK;
1903 if (!addrRec->mTRRSuccess) {
1904 // no TRR success
1905 newRRSet = nullptr;
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.
1930 if (!mShutdown) {
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++;
1938 } else {
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()) {
1959 char buf[128];
1960 elem.ToStringBuffer(buf, sizeof(buf));
1961 LOG(("CompleteLookup: %s has %s\n", addrRec->host.get(), buf));
1963 } else {
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.");
2012 #endif
2013 return LOOKUP_OK;
2016 nsHostResolver::LookupStatus nsHostResolver::CompleteLookupByType(
2017 nsHostRecord* rec, nsresult status,
2018 mozilla::net::TypeRecordResultType& aResult, uint32_t aTtl, bool pb) {
2019 MutexAutoLock lock(mLock);
2020 MOZ_ASSERT(rec);
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);
2046 } else {
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();
2053 LOG(
2054 ("nsHostResolver::CompleteLookupByType record %p [%s], number of "
2055 "records %zu\n",
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,
2062 duration);
2065 mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs =
2066 std::move(typeRec->mCallbacks);
2068 LOG(
2069 ("nsHostResolver::CompleteLookupByType record %p calling back dns "
2070 "users\n",
2071 typeRec.get()));
2073 for (nsResolveHostCallback* c = cbs.getFirst(); c;
2074 c = c->removeAndGetNext()) {
2075 c->OnResolveHostComplete(this, rec, status);
2078 AddToEvictionQ(rec);
2079 return LOOKUP_OK;
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);
2098 if (rec) {
2099 nsHostRecord* recPtr = nullptr;
2101 for (const RefPtr<nsResolveHostCallback>& c : rec->mCallbacks) {
2102 if (c->EqualsAsyncListener(aListener)) {
2103 c->remove();
2104 recPtr = rec;
2105 c->OnResolveHostComplete(this, recPtr, status);
2106 break;
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()) {
2115 recPtr->remove();
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.
2137 return n;
2140 void nsHostResolver::ThreadFunc() {
2141 LOG(("DNS lookup thread - starting execution.\n"));
2143 #if defined(RES_RETRY_ON_FAILURE)
2144 nsResState rs;
2145 #endif
2146 RefPtr<AddrHostRecord> rec;
2147 RefPtr<AddrInfo> ai;
2149 do {
2150 if (!rec) {
2151 RefPtr<AddrHostRecord> tmpRec;
2152 if (!GetHostToLookup(getter_AddRefs(tmpRec))) {
2153 break; // thread shutdown signal
2155 // GetHostToLookup() returns an owning reference
2156 MOZ_ASSERT(tmpRec);
2157 rec.swap(tmpRec);
2160 LOG1(("DNS lookup thread - Calling getaddrinfo for host [%s].\n",
2161 rec->host.get()));
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);
2168 nsresult status =
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),
2173 getTtl);
2175 #endif
2177 { // obtain lock to check shutdown and manage inter-module telemetry
2178 MutexAutoLock lock(mLock);
2180 if (!mShutdown) {
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;
2192 } else {
2193 // Time to get TTL; categorized by expiration strategy.
2194 histogramID = Telemetry::DNS_RENEWAL_TIME_FOR_TTL;
2196 Telemetry::Accumulate(histogramID, millis);
2197 } else {
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,
2207 rec->originSuffix,
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()));
2211 } else {
2212 rec = nullptr;
2214 } while (true);
2216 mActiveTaskCount--;
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)) {
2238 return rv;
2241 res.forget(result);
2242 return NS_OK;
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!");
2253 if (!rec) {
2254 continue;
2257 // For now we only show A/AAAA records.
2258 if (!rec->IsAddrRecord()) {
2259 continue;
2262 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
2263 MOZ_ASSERT(addrRec);
2264 if (!addrRec || !addrRec->addr_info) {
2265 continue;
2268 DNSCacheEntries info;
2269 info.hostname = rec->host;
2270 info.family = rec->af;
2271 info.expiration =
2272 (int64_t)(rec->mValidEnd - TimeStamp::NowLoRes()).ToSeconds();
2273 if (info.expiration <= 0) {
2274 // We only need valid DNS cache entries
2275 continue;
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));
2295 #undef LOG
2296 #undef LOG_ENABLED