Bug 1890277: part 4) Add CSPParser support for the `trusted-types` directive, guarded...
[gecko.git] / netwerk / dns / nsHostResolver.cpp
blobb74b974041bce648763e1a4a85007373a55e20e5
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 #include "nsIThreadPool.h"
7 #if defined(HAVE_RES_NINIT)
8 # include <sys/types.h>
9 # include <netinet/in.h>
10 # include <arpa/inet.h>
11 # include <arpa/nameser.h>
12 # include <resolv.h>
13 # define RES_RETRY_ON_FAILURE
14 #endif
16 #include <stdlib.h>
17 #include <ctime>
18 #include "nsHostResolver.h"
19 #include "nsError.h"
20 #include "nsISupports.h"
21 #include "nsISupportsUtils.h"
22 #include "nsIThreadManager.h"
23 #include "nsComponentManagerUtils.h"
24 #include "nsNetUtil.h"
25 #include "nsPrintfCString.h"
26 #include "nsXPCOMCIDInternal.h"
27 #include "prthread.h"
28 #include "prerror.h"
29 #include "prtime.h"
30 #include "mozilla/Logging.h"
31 #include "PLDHashTable.h"
32 #include "nsQueryObject.h"
33 #include "nsURLHelper.h"
34 #include "nsThreadUtils.h"
35 #include "nsThreadPool.h"
36 #include "GetAddrInfo.h"
37 #include "TRR.h"
38 #include "TRRQuery.h"
39 #include "TRRService.h"
41 #include "mozilla/Atomics.h"
42 #include "mozilla/glean/GleanMetrics.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"
49 // Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
50 #include "DNSLogging.h"
52 #ifdef XP_WIN
53 # include "mozilla/WindowsVersion.h"
54 #endif // XP_WIN
56 #ifdef MOZ_WIDGET_ANDROID
57 # include "mozilla/jni/Utils.h"
58 #endif
60 #define IS_ADDR_TYPE(_type) ((_type) == nsIDNSService::RESOLVE_TYPE_DEFAULT)
61 #define IS_OTHER_TYPE(_type) ((_type) != nsIDNSService::RESOLVE_TYPE_DEFAULT)
63 using namespace mozilla;
64 using namespace mozilla::net;
66 // None of our implementations expose a TTL for negative responses, so we use a
67 // constant always.
68 static const unsigned int NEGATIVE_RECORD_LIFETIME = 60;
70 //----------------------------------------------------------------------------
72 // Use a persistent thread pool in order to avoid spinning up new threads all
73 // the time. In particular, thread creation results in a res_init() call from
74 // libc which is quite expensive.
76 // The pool dynamically grows between 0 and MaxResolverThreads() in size. New
77 // requests go first to an idle thread. If that cannot be found and there are
78 // fewer than MaxResolverThreads() currently in the pool a new thread is created
79 // for high priority requests. If the new request is at a lower priority a new
80 // thread will only be created if there are fewer than
81 // MaxResolverThreadsAnyPriority() currently outstanding. If a thread cannot be
82 // created or an idle thread located for the request it is queued.
84 // When the pool is greater than MaxResolverThreadsAnyPriority() in size a
85 // thread will be destroyed after ShortIdleTimeoutSeconds of idle time. Smaller
86 // pools use LongIdleTimeoutSeconds for a timeout period.
88 // for threads 1 -> MaxResolverThreadsAnyPriority()
89 #define LongIdleTimeoutSeconds 300
90 // for threads MaxResolverThreadsAnyPriority() + 1 -> MaxResolverThreads()
91 #define ShortIdleTimeoutSeconds 60
93 //----------------------------------------------------------------------------
95 namespace mozilla::net {
96 LazyLogModule gHostResolverLog("nsHostResolver");
97 } // namespace mozilla::net
99 //----------------------------------------------------------------------------
101 #if defined(RES_RETRY_ON_FAILURE)
103 // this class represents the resolver state for a given thread. if we
104 // encounter a lookup failure, then we can invoke the Reset method on an
105 // instance of this class to reset the resolver (in case /etc/resolv.conf
106 // for example changed). this is mainly an issue on GNU systems since glibc
107 // only reads in /etc/resolv.conf once per thread. it may be an issue on
108 // other systems as well.
110 class nsResState {
111 public:
112 nsResState()
113 // initialize mLastReset to the time when this object
114 // is created. this means that a reset will not occur
115 // if a thread is too young. the alternative would be
116 // to initialize this to the beginning of time, so that
117 // the first failure would cause a reset, but since the
118 // thread would have just started up, it likely would
119 // already have current /etc/resolv.conf info.
120 : mLastReset(PR_IntervalNow()) {}
122 bool Reset() {
123 // reset no more than once per second
124 if (PR_IntervalToSeconds(PR_IntervalNow() - mLastReset) < 1) {
125 return false;
128 mLastReset = PR_IntervalNow();
129 auto result = res_ninit(&_res);
131 LOG(("nsResState::Reset() > 'res_ninit' returned %d", result));
132 return (result == 0);
135 private:
136 PRIntervalTime mLastReset;
139 #endif // RES_RETRY_ON_FAILURE
141 class DnsThreadListener final : public nsIThreadPoolListener {
142 NS_DECL_THREADSAFE_ISUPPORTS
143 NS_DECL_NSITHREADPOOLLISTENER
144 private:
145 virtual ~DnsThreadListener() = default;
148 NS_IMETHODIMP
149 DnsThreadListener::OnThreadCreated() { return NS_OK; }
151 NS_IMETHODIMP
152 DnsThreadListener::OnThreadShuttingDown() {
153 DNSThreadShutdown();
154 return NS_OK;
157 NS_IMPL_ISUPPORTS(DnsThreadListener, nsIThreadPoolListener)
159 //----------------------------------------------------------------------------
161 static const char kPrefGetTtl[] = "network.dns.get-ttl";
162 static const char kPrefNativeIsLocalhost[] = "network.dns.native-is-localhost";
163 static const char kPrefThreadIdleTime[] =
164 "network.dns.resolver-thread-extra-idle-time-seconds";
165 static bool sGetTtlEnabled = false;
166 mozilla::Atomic<bool, mozilla::Relaxed> gNativeIsLocalhost;
167 mozilla::Atomic<bool, mozilla::Relaxed> sNativeHTTPSSupported{false};
169 static void DnsPrefChanged(const char* aPref, void* aSelf) {
170 MOZ_ASSERT(NS_IsMainThread(),
171 "Should be getting pref changed notification on main thread!");
173 MOZ_ASSERT(aSelf);
175 if (!strcmp(aPref, kPrefGetTtl)) {
176 #ifdef DNSQUERY_AVAILABLE
177 sGetTtlEnabled = Preferences::GetBool(kPrefGetTtl);
178 #endif
179 } else if (!strcmp(aPref, kPrefNativeIsLocalhost)) {
180 gNativeIsLocalhost = Preferences::GetBool(kPrefNativeIsLocalhost);
184 NS_IMPL_ISUPPORTS0(nsHostResolver)
186 nsHostResolver::nsHostResolver(uint32_t maxCacheEntries,
187 uint32_t defaultCacheEntryLifetime,
188 uint32_t defaultGracePeriod)
189 : mMaxCacheEntries(maxCacheEntries),
190 mDefaultCacheLifetime(defaultCacheEntryLifetime),
191 mDefaultGracePeriod(defaultGracePeriod),
192 mIdleTaskCV(mLock, "nsHostResolver.mIdleTaskCV") {
193 mCreationTime = PR_Now();
195 mLongIdleTimeout = TimeDuration::FromSeconds(LongIdleTimeoutSeconds);
196 mShortIdleTimeout = TimeDuration::FromSeconds(ShortIdleTimeoutSeconds);
199 nsHostResolver::~nsHostResolver() = default;
201 nsresult nsHostResolver::Init() MOZ_NO_THREAD_SAFETY_ANALYSIS {
202 MOZ_ASSERT(NS_IsMainThread());
203 if (NS_FAILED(GetAddrInfoInit())) {
204 return NS_ERROR_FAILURE;
207 LOG(("nsHostResolver::Init this=%p", this));
209 mShutdown = false;
210 mNCS = NetworkConnectivityService::GetSingleton();
212 // The preferences probably haven't been loaded from the disk yet, so we
213 // need to register a callback that will set up the experiment once they
214 // are. We also need to explicitly set a value for the props otherwise the
215 // callback won't be called.
217 DebugOnly<nsresult> rv = Preferences::RegisterCallbackAndCall(
218 &DnsPrefChanged, kPrefGetTtl, this);
219 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
220 "Could not register DNS TTL pref callback.");
221 rv = Preferences::RegisterCallbackAndCall(&DnsPrefChanged,
222 kPrefNativeIsLocalhost, this);
223 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
224 "Could not register DNS pref callback.");
227 #if defined(HAVE_RES_NINIT)
228 // We want to make sure the system is using the correct resolver settings,
229 // so we force it to reload those settings whenever we startup a subsequent
230 // nsHostResolver instance. We assume that there is no reason to do this
231 // for the first nsHostResolver instance since that is usually created
232 // during application startup.
233 static int initCount = 0;
234 if (initCount++ > 0) {
235 auto result = res_ninit(&_res);
236 LOG(("nsHostResolver::Init > 'res_ninit' returned %d", result));
238 #endif
240 // We can configure the threadpool to keep threads alive for a while after
241 // the last ThreadFunc task has been executed.
242 int32_t poolTimeoutSecs = Preferences::GetInt(kPrefThreadIdleTime, 60);
243 uint32_t poolTimeoutMs;
244 if (poolTimeoutSecs < 0) {
245 // This means never shut down the idle threads
246 poolTimeoutMs = UINT32_MAX;
247 } else {
248 // We clamp down the idle time between 0 and one hour.
249 poolTimeoutMs =
250 mozilla::clamped<uint32_t>(poolTimeoutSecs * 1000, 0, 3600 * 1000);
253 #if defined(XP_WIN)
254 // For some reason, the DNSQuery_A API doesn't work on Windows 10.
255 // It returns a success code, but no records. We only allow
256 // native HTTPS records on Win 11 for now.
257 sNativeHTTPSSupported = StaticPrefs::network_dns_native_https_query_win10() ||
258 mozilla::IsWin11OrLater();
259 #elif defined(MOZ_WIDGET_ANDROID)
260 // android_res_nquery only got added in API level 29
261 sNativeHTTPSSupported = jni::GetAPIVersion() >= 29;
262 #elif defined(XP_LINUX) || defined(XP_MACOSX)
263 sNativeHTTPSSupported = true;
264 #endif
265 LOG(("Native HTTPS records supported=%d", bool(sNativeHTTPSSupported)));
267 nsCOMPtr<nsIThreadPool> threadPool = new nsThreadPool();
268 MOZ_ALWAYS_SUCCEEDS(threadPool->SetThreadLimit(MaxResolverThreads()));
269 MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadLimit(MaxResolverThreads()));
270 MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadTimeout(poolTimeoutMs));
271 MOZ_ALWAYS_SUCCEEDS(
272 threadPool->SetThreadStackSize(nsIThreadManager::kThreadPoolStackSize));
273 MOZ_ALWAYS_SUCCEEDS(threadPool->SetName("DNS Resolver"_ns));
274 nsCOMPtr<nsIThreadPoolListener> listener = new DnsThreadListener();
275 threadPool->SetListener(listener);
276 mResolverThreads = ToRefPtr(std::move(threadPool));
278 return NS_OK;
281 void nsHostResolver::ClearPendingQueue(
282 LinkedList<RefPtr<nsHostRecord>>& aPendingQ) {
283 // loop through pending queue, erroring out pending lookups.
284 if (!aPendingQ.isEmpty()) {
285 for (const RefPtr<nsHostRecord>& rec : aPendingQ) {
286 rec->Cancel();
287 if (rec->IsAddrRecord()) {
288 CompleteLookup(rec, NS_ERROR_ABORT, nullptr, rec->pb, rec->originSuffix,
289 rec->mTRRSkippedReason, nullptr);
290 } else {
291 mozilla::net::TypeRecordResultType empty(Nothing{});
292 CompleteLookupByType(rec, NS_ERROR_ABORT, empty, rec->mTRRSkippedReason,
293 0, rec->pb);
300 // FlushCache() is what we call when the network has changed. We must not
301 // trust names that were resolved before this change. They may resolve
302 // differently now.
304 // This function removes all existing resolved host entries from the hash.
305 // Names that are in the pending queues can be left there. Entries in the
306 // cache that have 'Resolve' set true but not 'OnQueue' are being resolved
307 // right now, so we need to mark them to get re-resolved on completion!
309 void nsHostResolver::FlushCache(bool aTrrToo) {
310 MutexAutoLock lock(mLock);
312 mQueue.FlushEvictionQ(mRecordDB, lock);
314 // Refresh the cache entries that are resolving RIGHT now, remove the rest.
315 for (auto iter = mRecordDB.Iter(); !iter.Done(); iter.Next()) {
316 nsHostRecord* record = iter.UserData();
317 // Try to remove the record, or mark it for refresh.
318 // By-type records are from TRR. We do not need to flush those entry
319 // when the network has change, because they are not local.
320 if (record->IsAddrRecord()) {
321 RefPtr<AddrHostRecord> addrRec = do_QueryObject(record);
322 MOZ_ASSERT(addrRec);
323 if (addrRec->RemoveOrRefresh(aTrrToo)) {
324 mQueue.MaybeRemoveFromQ(record, lock);
325 LOG(("Removing (%s) Addr record from mRecordDB", record->host.get()));
326 iter.Remove();
328 } else if (aTrrToo) {
329 // remove by type records
330 LOG(("Removing (%s) type record from mRecordDB", record->host.get()));
331 iter.Remove();
336 void nsHostResolver::Shutdown() {
337 LOG(("Shutting down host resolver.\n"));
340 DebugOnly<nsresult> rv =
341 Preferences::UnregisterCallback(&DnsPrefChanged, kPrefGetTtl, this);
342 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
343 "Could not unregister DNS TTL pref callback.");
346 LinkedList<RefPtr<nsHostRecord>> pendingQHigh, pendingQMed, pendingQLow,
347 evictionQ;
350 MutexAutoLock lock(mLock);
352 mShutdown = true;
354 if (mNumIdleTasks) {
355 mIdleTaskCV.NotifyAll();
358 mQueue.ClearAll(
359 [&](nsHostRecord* aRec) {
360 mLock.AssertCurrentThreadOwns();
361 if (aRec->IsAddrRecord()) {
362 CompleteLookupLocked(aRec, NS_ERROR_ABORT, nullptr, aRec->pb,
363 aRec->originSuffix, aRec->mTRRSkippedReason,
364 nullptr, lock);
365 } else {
366 mozilla::net::TypeRecordResultType empty(Nothing{});
367 CompleteLookupByTypeLocked(aRec, NS_ERROR_ABORT, empty,
368 aRec->mTRRSkippedReason, 0, aRec->pb,
369 lock);
372 lock);
374 for (const auto& data : mRecordDB.Values()) {
375 data->Cancel();
377 // empty host database
378 mRecordDB.Clear();
380 mNCS = nullptr;
383 // Shutdown the resolver threads, but with a timeout of 2 seconds (prefable).
384 // If the timeout is exceeded, any stuck threads will be leaked.
385 mResolverThreads->ShutdownWithTimeout(
386 StaticPrefs::network_dns_resolver_shutdown_timeout_ms());
389 mozilla::DebugOnly<nsresult> rv = GetAddrInfoShutdown();
390 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to shutdown GetAddrInfo");
394 nsresult nsHostResolver::GetHostRecord(
395 const nsACString& host, const nsACString& aTrrServer, uint16_t type,
396 nsIDNSService::DNSFlags flags, uint16_t af, bool pb,
397 const nsCString& originSuffix, nsHostRecord** result) {
398 MutexAutoLock lock(mLock);
399 nsHostKey key(host, aTrrServer, type, flags, af, pb, originSuffix);
401 RefPtr<nsHostRecord> rec =
402 mRecordDB.LookupOrInsertWith(key, [&] { return InitRecord(key); });
403 if (rec->IsAddrRecord()) {
404 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
405 if (addrRec->addr) {
406 return NS_ERROR_FAILURE;
410 if (rec->mResolving) {
411 return NS_ERROR_FAILURE;
414 *result = rec.forget().take();
415 return NS_OK;
418 nsHostRecord* nsHostResolver::InitRecord(const nsHostKey& key) {
419 if (IS_ADDR_TYPE(key.type)) {
420 return new AddrHostRecord(key);
422 return new TypeHostRecord(key);
425 already_AddRefed<nsHostRecord> nsHostResolver::InitLoopbackRecord(
426 const nsHostKey& key, nsresult* aRv) {
427 MOZ_ASSERT(aRv);
428 MOZ_ASSERT(IS_ADDR_TYPE(key.type));
430 *aRv = NS_ERROR_FAILURE;
431 RefPtr<nsHostRecord> rec = InitRecord(key);
433 nsTArray<NetAddr> addresses;
434 NetAddr addr;
435 if (key.af == PR_AF_INET || key.af == PR_AF_UNSPEC) {
436 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(addr.InitFromString("127.0.0.1"_ns)));
437 addresses.AppendElement(addr);
439 if (key.af == PR_AF_INET6 || key.af == PR_AF_UNSPEC) {
440 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(addr.InitFromString("::1"_ns)));
441 addresses.AppendElement(addr);
444 RefPtr<AddrInfo> ai =
445 new AddrInfo(rec->host, DNSResolverType::Native, 0, std::move(addresses));
447 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
448 MutexAutoLock lock(addrRec->addr_info_lock);
449 addrRec->addr_info = ai;
450 addrRec->SetExpiration(TimeStamp::NowLoRes(), mDefaultCacheLifetime,
451 mDefaultGracePeriod);
452 addrRec->negative = false;
454 *aRv = NS_OK;
455 return rec.forget();
458 static bool IsNativeHTTPSEnabled() {
459 if (!StaticPrefs::network_dns_native_https_query()) {
460 return false;
462 return sNativeHTTPSSupported;
465 nsresult nsHostResolver::ResolveHost(const nsACString& aHost,
466 const nsACString& aTrrServer,
467 int32_t aPort, uint16_t type,
468 const OriginAttributes& aOriginAttributes,
469 nsIDNSService::DNSFlags flags, uint16_t af,
470 nsResolveHostCallback* aCallback) {
471 nsAutoCString host(aHost);
472 NS_ENSURE_TRUE(!host.IsEmpty(), NS_ERROR_UNEXPECTED);
474 nsAutoCString originSuffix;
475 aOriginAttributes.CreateSuffix(originSuffix);
476 LOG(("Resolving host [%s]<%s>%s%s type %d. [this=%p]\n", host.get(),
477 originSuffix.get(), flags & RES_BYPASS_CACHE ? " - bypassing cache" : "",
478 flags & RES_REFRESH_CACHE ? " - refresh cache" : "", type, this));
480 // ensure that we are working with a valid hostname before proceeding. see
481 // bug 304904 for details.
482 if (!net_IsValidHostName(host)) {
483 return NS_ERROR_UNKNOWN_HOST;
486 // If TRR is disabled we can return immediately if the native API is disabled
487 if (!IsNativeHTTPSEnabled() && IS_OTHER_TYPE(type) &&
488 Mode() == nsIDNSService::MODE_TRROFF) {
489 return NS_ERROR_UNKNOWN_HOST;
492 // Used to try to parse to an IP address literal.
493 NetAddr tempAddr;
494 if (IS_OTHER_TYPE(type) && (NS_SUCCEEDED(tempAddr.InitFromString(host)))) {
495 // For by-type queries the host cannot be IP literal.
496 return NS_ERROR_UNKNOWN_HOST;
499 RefPtr<nsResolveHostCallback> callback(aCallback);
500 // if result is set inside the lock, then we need to issue the
501 // callback before returning.
502 RefPtr<nsHostRecord> result;
503 nsresult status = NS_OK, rv = NS_OK;
505 MutexAutoLock lock(mLock);
507 if (mShutdown) {
508 return NS_ERROR_NOT_INITIALIZED;
511 // check to see if there is already an entry for this |host|
512 // in the hash table. if so, then check to see if we can't
513 // just reuse the lookup result. otherwise, if there are
514 // any pending callbacks, then add to pending callbacks queue,
515 // and return. otherwise, add ourselves as first pending
516 // callback, and proceed to do the lookup.
518 Maybe<nsCString> originHost;
519 if (StaticPrefs::network_dns_port_prefixed_qname_https_rr() &&
520 type == nsIDNSService::RESOLVE_TYPE_HTTPSSVC && aPort != -1 &&
521 aPort != 443) {
522 originHost = Some(host);
523 host = nsPrintfCString("_%d._https.%s", aPort, host.get());
524 LOG((" Using port prefixed host name [%s]", host.get()));
527 bool excludedFromTRR = false;
528 if (TRRService::Get() && TRRService::Get()->IsExcludedFromTRR(host)) {
529 flags |= nsIDNSService::RESOLVE_DISABLE_TRR;
530 excludedFromTRR = true;
532 if (!aTrrServer.IsEmpty()) {
533 return NS_ERROR_UNKNOWN_HOST;
537 nsHostKey key(host, aTrrServer, type, flags, af,
538 (aOriginAttributes.mPrivateBrowsingId > 0), originSuffix);
540 // Check if we have a localhost domain, if so hardcode to loopback
541 if (IS_ADDR_TYPE(type) && IsLoopbackHostname(host)) {
542 nsresult rv;
543 RefPtr<nsHostRecord> result = InitLoopbackRecord(key, &rv);
544 if (NS_WARN_IF(NS_FAILED(rv))) {
545 return rv;
547 MOZ_ASSERT(result);
548 aCallback->OnResolveHostComplete(this, result, NS_OK);
549 return NS_OK;
552 RefPtr<nsHostRecord> rec =
553 mRecordDB.LookupOrInsertWith(key, [&] { return InitRecord(key); });
555 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
556 MOZ_ASSERT(rec, "Record should not be null");
557 MOZ_ASSERT((IS_ADDR_TYPE(type) && rec->IsAddrRecord() && addrRec) ||
558 (IS_OTHER_TYPE(type) && !rec->IsAddrRecord()));
560 if (IS_OTHER_TYPE(type) && originHost) {
561 RefPtr<TypeHostRecord> typeRec = do_QueryObject(rec);
562 typeRec->mOriginHost = std::move(originHost);
565 if (excludedFromTRR) {
566 rec->RecordReason(TRRSkippedReason::TRR_EXCLUDED);
569 if (!(flags & RES_BYPASS_CACHE) &&
570 rec->HasUsableResult(TimeStamp::NowLoRes(), flags)) {
571 result = FromCache(rec, host, type, status, lock);
572 } else if (addrRec && addrRec->addr) {
573 // if the host name is an IP address literal and has been
574 // parsed, go ahead and use it.
575 LOG((" Using cached address for IP Literal [%s].\n", host.get()));
576 result = FromCachedIPLiteral(rec);
577 } else if (addrRec && NS_SUCCEEDED(tempAddr.InitFromString(host))) {
578 // try parsing the host name as an IP address literal to short
579 // circuit full host resolution. (this is necessary on some
580 // platforms like Win9x. see bug 219376 for more details.)
581 LOG((" Host is IP Literal [%s].\n", host.get()));
582 result = FromIPLiteral(addrRec, tempAddr);
583 } else if (mQueue.PendingCount() >= MAX_NON_PRIORITY_REQUESTS &&
584 !IsHighPriority(flags) && !rec->mResolving) {
585 LOG(
586 (" Lookup queue full: dropping %s priority request for "
587 "host [%s].\n",
588 IsMediumPriority(flags) ? "medium" : "low", host.get()));
589 if (IS_ADDR_TYPE(type)) {
590 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_OVERFLOW);
592 // This is a lower priority request and we are swamped, so refuse it.
593 rv = NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
595 // Check if the offline flag is set.
596 } else if (flags & RES_OFFLINE) {
597 LOG((" Offline request for host [%s]; ignoring.\n", host.get()));
598 rv = NS_ERROR_OFFLINE;
600 // We do not have a valid result till here.
601 // A/AAAA request can check for an alternative entry like AF_UNSPEC.
602 // Otherwise we need to start a new query.
603 } else if (!rec->mResolving) {
604 result =
605 FromUnspecEntry(rec, host, aTrrServer, originSuffix, type, flags, af,
606 aOriginAttributes.mPrivateBrowsingId > 0, status);
607 // If this is a by-type request or if no valid record was found
608 // in the cache or this is an AF_UNSPEC request, then start a
609 // new lookup.
610 if (!result) {
611 LOG((" No usable record in cache for host [%s] type %d.", host.get(),
612 type));
614 if (flags & RES_REFRESH_CACHE) {
615 rec->Invalidate();
618 // Add callback to the list of pending callbacks.
619 rec->mCallbacks.insertBack(callback);
620 rec->flags = flags;
621 rv = NameLookup(rec, lock);
622 if (IS_ADDR_TYPE(type)) {
623 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
624 METHOD_NETWORK_FIRST);
626 if (NS_FAILED(rv) && callback->isInList()) {
627 callback->remove();
628 } else {
629 LOG(
630 (" DNS lookup for host [%s] blocking "
631 "pending 'getaddrinfo' or trr query: "
632 "callback [%p]",
633 host.get(), callback.get()));
636 } else {
637 LOG(
638 (" Host [%s] is being resolved. Appending callback "
639 "[%p].",
640 host.get(), callback.get()));
642 rec->mCallbacks.insertBack(callback);
644 if (rec && rec->onQueue()) {
645 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
646 METHOD_NETWORK_SHARED);
648 // Consider the case where we are on a pending queue of
649 // lower priority than the request is being made at.
650 // In that case we should upgrade to the higher queue.
652 if (IsHighPriority(flags) && !IsHighPriority(rec->flags)) {
653 // Move from (low|med) to high.
654 mQueue.MoveToAnotherPendingQ(rec, flags, lock);
655 rec->flags = flags;
656 ConditionallyCreateThread(rec);
657 } else if (IsMediumPriority(flags) && IsLowPriority(rec->flags)) {
658 // Move from low to med.
659 mQueue.MoveToAnotherPendingQ(rec, flags, lock);
660 rec->flags = flags;
661 mIdleTaskCV.Notify();
666 if (result && callback->isInList()) {
667 callback->remove();
669 } // lock
671 if (result) {
672 callback->OnResolveHostComplete(this, result, status);
675 return rv;
678 already_AddRefed<nsHostRecord> nsHostResolver::FromCache(
679 nsHostRecord* aRec, const nsACString& aHost, uint16_t aType,
680 nsresult& aStatus, const MutexAutoLock& aLock) {
681 LOG((" Using cached record for host [%s].\n",
682 nsPromiseFlatCString(aHost).get()));
684 // put reference to host record on stack...
685 RefPtr<nsHostRecord> result = aRec;
686 if (IS_ADDR_TYPE(aType)) {
687 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
690 // For entries that are in the grace period
691 // or all cached negative entries, use the cache but start a new
692 // lookup in the background
693 ConditionallyRefreshRecord(aRec, aHost, aLock);
695 if (aRec->negative) {
696 LOG((" Negative cache entry for host [%s].\n",
697 nsPromiseFlatCString(aHost).get()));
698 if (IS_ADDR_TYPE(aType)) {
699 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_NEGATIVE_HIT);
701 aStatus = NS_ERROR_UNKNOWN_HOST;
704 return result.forget();
707 already_AddRefed<nsHostRecord> nsHostResolver::FromCachedIPLiteral(
708 nsHostRecord* aRec) {
709 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_LITERAL);
710 RefPtr<nsHostRecord> result = aRec;
711 return result.forget();
714 already_AddRefed<nsHostRecord> nsHostResolver::FromIPLiteral(
715 AddrHostRecord* aAddrRec, const NetAddr& aAddr) {
716 // ok, just copy the result into the host record, and be
717 // done with it! ;-)
718 aAddrRec->addr = MakeUnique<NetAddr>(aAddr);
719 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_LITERAL);
720 // put reference to host record on stack...
721 RefPtr<nsHostRecord> result = aAddrRec;
722 return result.forget();
725 already_AddRefed<nsHostRecord> nsHostResolver::FromUnspecEntry(
726 nsHostRecord* aRec, const nsACString& aHost, const nsACString& aTrrServer,
727 const nsACString& aOriginSuffix, uint16_t aType,
728 nsIDNSService::DNSFlags aFlags, uint16_t af, bool aPb, nsresult& aStatus) {
729 RefPtr<nsHostRecord> result = nullptr;
730 // If this is an IPV4 or IPV6 specific request, check if there is
731 // an AF_UNSPEC entry we can use. Otherwise, hit the resolver...
732 RefPtr<AddrHostRecord> addrRec = do_QueryObject(aRec);
733 if (addrRec && !(aFlags & RES_BYPASS_CACHE) &&
734 ((af == PR_AF_INET) || (af == PR_AF_INET6))) {
735 // Check for an AF_UNSPEC entry.
737 const nsHostKey unspecKey(aHost, aTrrServer,
738 nsIDNSService::RESOLVE_TYPE_DEFAULT, aFlags,
739 PR_AF_UNSPEC, aPb, aOriginSuffix);
740 RefPtr<nsHostRecord> unspecRec = mRecordDB.Get(unspecKey);
742 TimeStamp now = TimeStamp::NowLoRes();
743 if (unspecRec && unspecRec->HasUsableResult(now, aFlags)) {
744 MOZ_ASSERT(unspecRec->IsAddrRecord());
746 RefPtr<AddrHostRecord> addrUnspecRec = do_QueryObject(unspecRec);
747 MOZ_ASSERT(addrUnspecRec);
748 MOZ_ASSERT(addrUnspecRec->addr_info || addrUnspecRec->negative,
749 "Entry should be resolved or negative.");
751 LOG((" Trying AF_UNSPEC entry for host [%s] af: %s.\n",
752 PromiseFlatCString(aHost).get(),
753 (af == PR_AF_INET) ? "AF_INET" : "AF_INET6"));
755 // We need to lock in case any other thread is reading
756 // addr_info.
757 MutexAutoLock lock(addrRec->addr_info_lock);
759 addrRec->addr_info = nullptr;
760 addrRec->addr_info_gencnt++;
761 if (unspecRec->negative) {
762 aRec->negative = unspecRec->negative;
763 aRec->CopyExpirationTimesAndFlagsFrom(unspecRec);
764 } else if (addrUnspecRec->addr_info) {
765 MutexAutoLock lock(addrUnspecRec->addr_info_lock);
766 if (addrUnspecRec->addr_info) {
767 // Search for any valid address in the AF_UNSPEC entry
768 // in the cache (not blocklisted and from the right
769 // family).
770 nsTArray<NetAddr> addresses;
771 for (const auto& addr : addrUnspecRec->addr_info->Addresses()) {
772 if ((af == addr.inet.family) &&
773 !addrUnspecRec->Blocklisted(&addr)) {
774 addresses.AppendElement(addr);
777 if (!addresses.IsEmpty()) {
778 addrRec->addr_info = new AddrInfo(
779 addrUnspecRec->addr_info->Hostname(),
780 addrUnspecRec->addr_info->CanonicalHostname(),
781 addrUnspecRec->addr_info->ResolverType(),
782 addrUnspecRec->addr_info->TRRType(), std::move(addresses));
783 addrRec->addr_info_gencnt++;
784 aRec->CopyExpirationTimesAndFlagsFrom(unspecRec);
788 // Now check if we have a new record.
789 if (aRec->HasUsableResult(now, aFlags)) {
790 result = aRec;
791 if (aRec->negative) {
792 aStatus = NS_ERROR_UNKNOWN_HOST;
794 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
795 ConditionallyRefreshRecord(aRec, aHost, lock);
796 } else if (af == PR_AF_INET6) {
797 // For AF_INET6, a new lookup means another AF_UNSPEC
798 // lookup. We have already iterated through the
799 // AF_UNSPEC addresses, so we mark this record as
800 // negative.
801 LOG(
802 (" No AF_INET6 in AF_UNSPEC entry: "
803 "host [%s] unknown host.",
804 nsPromiseFlatCString(aHost).get()));
805 result = aRec;
806 aRec->negative = true;
807 aStatus = NS_ERROR_UNKNOWN_HOST;
808 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
809 METHOD_NEGATIVE_HIT);
814 return result.forget();
817 void nsHostResolver::DetachCallback(
818 const nsACString& host, const nsACString& aTrrServer, uint16_t aType,
819 const OriginAttributes& aOriginAttributes, nsIDNSService::DNSFlags flags,
820 uint16_t af, nsResolveHostCallback* aCallback, nsresult status) {
821 RefPtr<nsHostRecord> rec;
822 RefPtr<nsResolveHostCallback> callback(aCallback);
825 MutexAutoLock lock(mLock);
827 nsAutoCString originSuffix;
828 aOriginAttributes.CreateSuffix(originSuffix);
830 nsHostKey key(host, aTrrServer, aType, flags, af,
831 (aOriginAttributes.mPrivateBrowsingId > 0), originSuffix);
832 RefPtr<nsHostRecord> entry = mRecordDB.Get(key);
833 if (entry) {
834 // walk list looking for |callback|... we cannot assume
835 // that it will be there!
837 for (nsResolveHostCallback* c : entry->mCallbacks) {
838 if (c == callback) {
839 rec = entry;
840 c->remove();
841 break;
847 // complete callback with the given status code; this would only be done if
848 // the record was in the process of being resolved.
849 if (rec) {
850 callback->OnResolveHostComplete(this, rec, status);
854 nsresult nsHostResolver::ConditionallyCreateThread(nsHostRecord* rec) {
855 if (mNumIdleTasks) {
856 // wake up idle tasks to process this lookup
857 mIdleTaskCV.Notify();
858 } else if ((mActiveTaskCount < MaxResolverThreadsAnyPriority()) ||
859 (IsHighPriority(rec->flags) &&
860 mActiveTaskCount < MaxResolverThreads())) {
861 nsCOMPtr<nsIRunnable> event = mozilla::NewRunnableMethod(
862 "nsHostResolver::ThreadFunc", this, &nsHostResolver::ThreadFunc);
863 mActiveTaskCount++;
864 nsresult rv =
865 mResolverThreads->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
866 if (NS_FAILED(rv)) {
867 mActiveTaskCount--;
869 } else {
870 LOG((" Unable to find a thread for looking up host [%s].\n",
871 rec->host.get()));
873 return NS_OK;
876 nsresult nsHostResolver::TrrLookup_unlocked(nsHostRecord* rec, TRR* pushedTRR) {
877 MutexAutoLock lock(mLock);
878 return TrrLookup(rec, lock, pushedTRR);
881 void nsHostResolver::MaybeRenewHostRecord(nsHostRecord* aRec) {
882 MutexAutoLock lock(mLock);
883 MaybeRenewHostRecordLocked(aRec, lock);
886 void nsHostResolver::MaybeRenewHostRecordLocked(nsHostRecord* aRec,
887 const MutexAutoLock& aLock) {
888 mQueue.MaybeRenewHostRecord(aRec, aLock);
891 bool nsHostResolver::TRRServiceEnabledForRecord(nsHostRecord* aRec) {
892 MOZ_ASSERT(aRec, "Record must not be empty");
893 MOZ_ASSERT(aRec->mEffectiveTRRMode != nsIRequest::TRR_DEFAULT_MODE,
894 "effective TRR mode must be computed before this call");
895 if (!TRRService::Get()) {
896 aRec->RecordReason(TRRSkippedReason::TRR_NO_GSERVICE);
897 return false;
900 // We always try custom resolvers.
901 if (!aRec->mTrrServer.IsEmpty()) {
902 return true;
905 nsIRequest::TRRMode reqMode = aRec->mEffectiveTRRMode;
906 if (TRRService::Get()->Enabled(reqMode)) {
907 return true;
910 if (NS_IsOffline()) {
911 // If we are in the NOT_CONFIRMED state _because_ we lack connectivity,
912 // then we should report that the browser is offline instead.
913 aRec->RecordReason(TRRSkippedReason::TRR_IS_OFFLINE);
914 return false;
917 auto hasConnectivity = [this]() -> bool {
918 mLock.AssertCurrentThreadOwns();
919 if (!mNCS) {
920 return true;
922 nsINetworkConnectivityService::ConnectivityState ipv4 = mNCS->GetIPv4();
923 nsINetworkConnectivityService::ConnectivityState ipv6 = mNCS->GetIPv6();
925 if (ipv4 == nsINetworkConnectivityService::OK ||
926 ipv6 == nsINetworkConnectivityService::OK) {
927 return true;
930 if (ipv4 == nsINetworkConnectivityService::UNKNOWN ||
931 ipv6 == nsINetworkConnectivityService::UNKNOWN) {
932 // One of the checks hasn't completed yet. Optimistically assume we'll
933 // have network connectivity.
934 return true;
937 return false;
940 if (!hasConnectivity()) {
941 aRec->RecordReason(TRRSkippedReason::TRR_NO_CONNECTIVITY);
942 return false;
945 bool isConfirmed = TRRService::Get()->IsConfirmed();
946 if (!isConfirmed) {
947 aRec->RecordReason(TRRSkippedReason::TRR_NOT_CONFIRMED);
950 return isConfirmed;
953 // returns error if no TRR resolve is issued
954 // it is impt this is not called while a native lookup is going on
955 nsresult nsHostResolver::TrrLookup(nsHostRecord* aRec,
956 const MutexAutoLock& aLock, TRR* pushedTRR) {
957 if (Mode() == nsIDNSService::MODE_TRROFF ||
958 StaticPrefs::network_dns_disabled()) {
959 return NS_ERROR_UNKNOWN_HOST;
961 LOG(("TrrLookup host:%s af:%" PRId16, aRec->host.get(), aRec->af));
963 RefPtr<nsHostRecord> rec(aRec);
964 mLock.AssertCurrentThreadOwns();
966 RefPtr<AddrHostRecord> addrRec;
967 RefPtr<TypeHostRecord> typeRec;
969 if (rec->IsAddrRecord()) {
970 addrRec = do_QueryObject(rec);
971 MOZ_ASSERT(addrRec);
972 } else {
973 typeRec = do_QueryObject(rec);
974 MOZ_ASSERT(typeRec);
977 MOZ_ASSERT(!rec->mResolving);
979 if (!TRRServiceEnabledForRecord(aRec)) {
980 return NS_ERROR_UNKNOWN_HOST;
983 MaybeRenewHostRecordLocked(rec, aLock);
985 RefPtr<TRRQuery> query = new TRRQuery(this, rec);
986 nsresult rv = query->DispatchLookup(pushedTRR);
987 if (NS_FAILED(rv)) {
988 rec->RecordReason(TRRSkippedReason::TRR_DID_NOT_MAKE_QUERY);
989 return rv;
993 auto lock = rec->mTRRQuery.Lock();
994 MOZ_ASSERT(!lock.ref(), "TRR already in progress");
995 lock.ref() = query;
998 rec->mResolving++;
999 rec->mTrrAttempts++;
1000 rec->StoreNative(false);
1001 return NS_OK;
1004 nsresult nsHostResolver::NativeLookup(nsHostRecord* aRec,
1005 const MutexAutoLock& aLock) {
1006 if (StaticPrefs::network_dns_disabled()) {
1007 return NS_ERROR_UNKNOWN_HOST;
1009 LOG(("NativeLookup host:%s af:%" PRId16, aRec->host.get(), aRec->af));
1011 // If this is not a A/AAAA request, make sure native HTTPS is enabled.
1012 MOZ_ASSERT(aRec->IsAddrRecord() || IsNativeHTTPSEnabled());
1013 mLock.AssertCurrentThreadOwns();
1015 RefPtr<nsHostRecord> rec(aRec);
1017 rec->mNativeStart = TimeStamp::Now();
1019 // Add rec to one of the pending queues, possibly removing it from mEvictionQ.
1020 MaybeRenewHostRecordLocked(aRec, aLock);
1022 mQueue.InsertRecord(rec, rec->flags, aLock);
1024 rec->StoreNative(true);
1025 rec->StoreNativeUsed(true);
1026 rec->mResolving++;
1028 nsresult rv = ConditionallyCreateThread(rec);
1030 LOG((" DNS thread counters: total=%d any-live=%d idle=%d pending=%d\n",
1031 static_cast<uint32_t>(mActiveTaskCount),
1032 static_cast<uint32_t>(mActiveAnyThreadCount),
1033 static_cast<uint32_t>(mNumIdleTasks), mQueue.PendingCount()));
1035 return rv;
1038 // static
1039 nsIDNSService::ResolverMode nsHostResolver::Mode() {
1040 if (TRRService::Get()) {
1041 return TRRService::Get()->Mode();
1044 // If we don't have a TRR service just return MODE_TRROFF so we don't make
1045 // any TRR requests by mistake.
1046 return nsIDNSService::MODE_TRROFF;
1049 nsIRequest::TRRMode nsHostRecord::TRRMode() {
1050 return nsIDNSService::GetTRRModeFromFlags(flags);
1053 // static
1054 void nsHostResolver::ComputeEffectiveTRRMode(nsHostRecord* aRec) {
1055 nsIDNSService::ResolverMode resolverMode = nsHostResolver::Mode();
1056 nsIRequest::TRRMode requestMode = aRec->TRRMode();
1058 // For domains that are excluded from TRR or when parental control is enabled,
1059 // we fallback to NativeLookup. This happens even in MODE_TRRONLY. By default
1060 // localhost and local are excluded (so we cover *.local hosts) See the
1061 // network.trr.excluded-domains pref.
1063 if (!TRRService::Get()) {
1064 aRec->RecordReason(TRRSkippedReason::TRR_NO_GSERVICE);
1065 aRec->mEffectiveTRRMode = requestMode;
1066 return;
1069 if (!aRec->mTrrServer.IsEmpty()) {
1070 aRec->mEffectiveTRRMode = nsIRequest::TRR_ONLY_MODE;
1071 return;
1074 if (TRRService::Get()->IsExcludedFromTRR(aRec->host)) {
1075 aRec->RecordReason(TRRSkippedReason::TRR_EXCLUDED);
1076 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1077 return;
1080 if (TRRService::Get()->ParentalControlEnabled()) {
1081 aRec->RecordReason(TRRSkippedReason::TRR_PARENTAL_CONTROL);
1082 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1083 return;
1086 if (resolverMode == nsIDNSService::MODE_TRROFF) {
1087 aRec->RecordReason(TRRSkippedReason::TRR_OFF_EXPLICIT);
1088 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1089 return;
1092 if (requestMode == nsIRequest::TRR_DISABLED_MODE) {
1093 aRec->RecordReason(TRRSkippedReason::TRR_REQ_MODE_DISABLED);
1094 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1095 return;
1098 if ((requestMode == nsIRequest::TRR_DEFAULT_MODE &&
1099 resolverMode == nsIDNSService::MODE_NATIVEONLY)) {
1100 if (StaticPrefs::network_trr_display_fallback_warning()) {
1101 TRRSkippedReason heuristicResult =
1102 TRRService::Get()->GetHeuristicDetectionResult();
1103 if (heuristicResult != TRRSkippedReason::TRR_UNSET &&
1104 heuristicResult != TRRSkippedReason::TRR_OK) {
1105 aRec->RecordReason(heuristicResult);
1106 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1107 return;
1110 aRec->RecordReason(TRRSkippedReason::TRR_MODE_NOT_ENABLED);
1111 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1112 return;
1115 if (requestMode == nsIRequest::TRR_DEFAULT_MODE &&
1116 resolverMode == nsIDNSService::MODE_TRRFIRST) {
1117 aRec->mEffectiveTRRMode = nsIRequest::TRR_FIRST_MODE;
1118 return;
1121 if (requestMode == nsIRequest::TRR_DEFAULT_MODE &&
1122 resolverMode == nsIDNSService::MODE_TRRONLY) {
1123 aRec->mEffectiveTRRMode = nsIRequest::TRR_ONLY_MODE;
1124 return;
1127 aRec->mEffectiveTRRMode = requestMode;
1130 // Kick-off a name resolve operation, using native resolver and/or TRR
1131 nsresult nsHostResolver::NameLookup(nsHostRecord* rec,
1132 const mozilla::MutexAutoLock& aLock) {
1133 LOG(("NameLookup host:%s af:%" PRId16, rec->host.get(), rec->af));
1134 mLock.AssertCurrentThreadOwns();
1136 if (rec->flags & RES_IP_HINT) {
1137 LOG(("Skip lookup if RES_IP_HINT is set\n"));
1138 return NS_ERROR_UNKNOWN_HOST;
1141 nsresult rv = NS_ERROR_UNKNOWN_HOST;
1142 if (rec->mResolving) {
1143 LOG(("NameLookup %s while already resolving\n", rec->host.get()));
1144 return NS_OK;
1147 // Make sure we reset the reason each time we attempt to do a new lookup
1148 // so we don't wrongly report the reason for the previous one.
1149 rec->Reset();
1151 ComputeEffectiveTRRMode(rec);
1153 if (!rec->mTrrServer.IsEmpty()) {
1154 LOG(("NameLookup: %s use trr:%s", rec->host.get(), rec->mTrrServer.get()));
1155 if (rec->mEffectiveTRRMode != nsIRequest::TRR_ONLY_MODE) {
1156 return NS_ERROR_UNKNOWN_HOST;
1159 if (rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR) {
1160 LOG(("TRR with server and DISABLE_TRR flag. Returning error."));
1161 return NS_ERROR_UNKNOWN_HOST;
1163 return TrrLookup(rec, aLock);
1166 LOG(("NameLookup: %s effectiveTRRmode: %d flags: %X", rec->host.get(),
1167 static_cast<nsIRequest::TRRMode>(rec->mEffectiveTRRMode), rec->flags));
1169 if (rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR) {
1170 rec->RecordReason(TRRSkippedReason::TRR_DISABLED_FLAG);
1173 bool serviceNotReady = !TRRServiceEnabledForRecord(rec);
1175 if (rec->mEffectiveTRRMode != nsIRequest::TRR_DISABLED_MODE &&
1176 !((rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR)) &&
1177 !serviceNotReady) {
1178 rv = TrrLookup(rec, aLock);
1181 if (rec->mEffectiveTRRMode == nsIRequest::TRR_DISABLED_MODE ||
1182 (rec->mEffectiveTRRMode == nsIRequest::TRR_FIRST_MODE &&
1183 (rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR || serviceNotReady ||
1184 NS_FAILED(rv)))) {
1185 if (!IsNativeHTTPSEnabled() && !rec->IsAddrRecord()) {
1186 return rv;
1189 #ifdef DEBUG
1190 // If we use this branch then the mTRRUsed flag should not be set
1191 // Even if we did call TrrLookup above, the fact that it failed sync-ly
1192 // means that we didn't actually succeed in opening the channel.
1193 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
1194 MOZ_ASSERT_IF(addrRec, addrRec->mResolverType == DNSResolverType::Native);
1195 #endif
1197 // We did not lookup via TRR - don't fallback to native if the
1198 // network.trr.display_fallback_warning pref is set and either
1199 // 1. we are in TRR first mode and confirmation failed
1200 // 2. the record has trr_disabled and a heuristic skip reason
1201 if (StaticPrefs::network_trr_display_fallback_warning() &&
1202 rec->mEffectiveTRRMode != nsIRequest::TRR_ONLY_MODE) {
1203 if ((rec->mEffectiveTRRMode == nsIRequest::TRR_FIRST_MODE &&
1204 rec->mTRRSkippedReason == TRRSkippedReason::TRR_NOT_CONFIRMED) ||
1205 (rec->mEffectiveTRRMode == nsIRequest::TRR_DISABLED_MODE &&
1206 rec->mTRRSkippedReason >=
1207 nsITRRSkipReason::TRR_HEURISTIC_TRIPPED_GOOGLE_SAFESEARCH &&
1208 rec->mTRRSkippedReason <=
1209 nsITRRSkipReason::TRR_HEURISTIC_TRIPPED_NRPT)) {
1210 LOG((
1211 "NameLookup: ResolveHostComplete with status NS_ERROR_UNKNOWN_HOST "
1212 "for: %s effectiveTRRmode: "
1213 "%d SkippedReason: %d",
1214 rec->host.get(),
1215 static_cast<nsIRequest::TRRMode>(rec->mEffectiveTRRMode),
1216 static_cast<int32_t>(rec->mTRRSkippedReason)));
1218 mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs =
1219 std::move(rec->mCallbacks);
1220 for (nsResolveHostCallback* c = cbs.getFirst(); c;
1221 c = c->removeAndGetNext()) {
1222 c->OnResolveHostComplete(this, rec, NS_ERROR_UNKNOWN_HOST);
1225 return NS_OK;
1229 rv = NativeLookup(rec, aLock);
1232 return rv;
1235 nsresult nsHostResolver::ConditionallyRefreshRecord(
1236 nsHostRecord* rec, const nsACString& host, const MutexAutoLock& aLock) {
1237 if ((rec->CheckExpiration(TimeStamp::NowLoRes()) != nsHostRecord::EXP_VALID ||
1238 rec->negative) &&
1239 !rec->mResolving && rec->RefreshForNegativeResponse()) {
1240 LOG((" Using %s cache entry for host [%s] but starting async renewal.",
1241 rec->negative ? "negative" : "positive", host.BeginReading()));
1242 NameLookup(rec, aLock);
1244 if (rec->IsAddrRecord() && !rec->negative) {
1245 // negative entries are constantly being refreshed, only
1246 // track positive grace period induced renewals
1247 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_RENEWAL);
1250 return NS_OK;
1253 bool nsHostResolver::GetHostToLookup(nsHostRecord** result) {
1254 bool timedOut = false;
1255 TimeDuration timeout;
1256 TimeStamp epoch, now;
1258 MutexAutoLock lock(mLock);
1260 timeout = (mNumIdleTasks >= MaxResolverThreadsAnyPriority())
1261 ? mShortIdleTimeout
1262 : mLongIdleTimeout;
1263 epoch = TimeStamp::Now();
1265 while (!mShutdown) {
1266 // remove next record from Q; hand over owning reference. Check high, then
1267 // med, then low
1269 #define SET_GET_TTL(var, val) (var)->StoreGetTtl(sGetTtlEnabled && (val))
1271 RefPtr<nsHostRecord> rec = mQueue.Dequeue(true, lock);
1272 if (rec) {
1273 SET_GET_TTL(rec, false);
1274 rec.forget(result);
1275 return true;
1278 if (mActiveAnyThreadCount < MaxResolverThreadsAnyPriority()) {
1279 rec = mQueue.Dequeue(false, lock);
1280 if (rec) {
1281 MOZ_ASSERT(IsMediumPriority(rec->flags) || IsLowPriority(rec->flags));
1282 mActiveAnyThreadCount++;
1283 rec->StoreUsingAnyThread(true);
1284 SET_GET_TTL(rec, true);
1285 rec.forget(result);
1286 return true;
1290 // Determining timeout is racy, so allow one cycle through checking the
1291 // queues before exiting.
1292 if (timedOut) {
1293 break;
1296 // wait for one or more of the following to occur:
1297 // (1) the pending queue has a host record to process
1298 // (2) the shutdown flag has been set
1299 // (3) the thread has been idle for too long
1301 mNumIdleTasks++;
1302 mIdleTaskCV.Wait(timeout);
1303 mNumIdleTasks--;
1305 now = TimeStamp::Now();
1307 if (now - epoch >= timeout) {
1308 timedOut = true;
1309 } else {
1310 // It is possible that CondVar::Wait() was interrupted and returned
1311 // early, in which case we will loop back and re-enter it. In that
1312 // case we want to do so with the new timeout reduced to reflect
1313 // time already spent waiting.
1314 timeout -= now - epoch;
1315 epoch = now;
1319 // tell thread to exit...
1320 return false;
1323 void nsHostResolver::PrepareRecordExpirationAddrRecord(
1324 AddrHostRecord* rec) const {
1325 // NOTE: rec->addr_info_lock is already held by parent
1326 MOZ_ASSERT(((bool)rec->addr_info) != rec->negative);
1327 mLock.AssertCurrentThreadOwns();
1328 if (!rec->addr_info) {
1329 rec->SetExpiration(TimeStamp::NowLoRes(), NEGATIVE_RECORD_LIFETIME, 0);
1330 LOG(("Caching host [%s] negative record for %u seconds.\n", rec->host.get(),
1331 NEGATIVE_RECORD_LIFETIME));
1332 return;
1335 unsigned int lifetime = mDefaultCacheLifetime;
1336 unsigned int grace = mDefaultGracePeriod;
1338 unsigned int ttl = mDefaultCacheLifetime;
1339 if (sGetTtlEnabled || rec->addr_info->IsTRR()) {
1340 if (rec->addr_info && rec->addr_info->TTL() != AddrInfo::NO_TTL_DATA) {
1341 ttl = rec->addr_info->TTL();
1343 lifetime = ttl;
1344 grace = 0;
1347 rec->SetExpiration(TimeStamp::NowLoRes(), lifetime, grace);
1348 LOG(("Caching host [%s] record for %u seconds (grace %d).", rec->host.get(),
1349 lifetime, grace));
1352 static bool different_rrset(AddrInfo* rrset1, AddrInfo* rrset2) {
1353 if (!rrset1 || !rrset2) {
1354 return true;
1357 LOG(("different_rrset %s\n", rrset1->Hostname().get()));
1359 if (rrset1->ResolverType() != rrset2->ResolverType()) {
1360 return true;
1363 if (rrset1->TRRType() != rrset2->TRRType()) {
1364 return true;
1367 if (rrset1->Addresses().Length() != rrset2->Addresses().Length()) {
1368 LOG(("different_rrset true due to length change\n"));
1369 return true;
1372 nsTArray<NetAddr> orderedSet1 = rrset1->Addresses().Clone();
1373 nsTArray<NetAddr> orderedSet2 = rrset2->Addresses().Clone();
1374 orderedSet1.Sort();
1375 orderedSet2.Sort();
1377 bool eq = orderedSet1 == orderedSet2;
1378 if (!eq) {
1379 LOG(("different_rrset true due to content change\n"));
1380 } else {
1381 LOG(("different_rrset false\n"));
1383 return !eq;
1386 void nsHostResolver::AddToEvictionQ(nsHostRecord* rec,
1387 const MutexAutoLock& aLock) {
1388 mQueue.AddToEvictionQ(rec, mMaxCacheEntries, mRecordDB, aLock);
1391 // After a first lookup attempt with TRR in mode 2, we may:
1392 // - If network.trr.retry_on_recoverable_errors is false, retry with native.
1393 // - If network.trr.retry_on_recoverable_errors is true:
1394 // - Retry with native if the first attempt failed because we got NXDOMAIN, an
1395 // unreachable address (TRR_DISABLED_FLAG), or we skipped TRR because
1396 // Confirmation failed.
1397 // - Trigger a "RetryTRR" Confirmation which will start a fresh
1398 // connection for TRR, and then retry the lookup with TRR.
1399 // - If the second attempt failed, fallback to native if
1400 // network.trr.strict_native_fallback is false.
1401 // Returns true if we retried with either TRR or Native.
1402 bool nsHostResolver::MaybeRetryTRRLookup(
1403 AddrHostRecord* aAddrRec, nsresult aFirstAttemptStatus,
1404 TRRSkippedReason aFirstAttemptSkipReason, nsresult aChannelStatus,
1405 const MutexAutoLock& aLock) {
1406 if (NS_FAILED(aFirstAttemptStatus) &&
1407 (aChannelStatus == NS_ERROR_PROXY_UNAUTHORIZED ||
1408 aChannelStatus == NS_ERROR_PROXY_AUTHENTICATION_FAILED) &&
1409 aAddrRec->mEffectiveTRRMode == nsIRequest::TRR_ONLY_MODE) {
1410 LOG(("MaybeRetryTRRLookup retry because of proxy connect failed"));
1411 TRRService::Get()->DontUseTRRThread();
1412 return DoRetryTRR(aAddrRec, aLock);
1415 if (NS_SUCCEEDED(aFirstAttemptStatus) ||
1416 aAddrRec->mEffectiveTRRMode != nsIRequest::TRR_FIRST_MODE ||
1417 aFirstAttemptStatus == NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
1418 return false;
1421 MOZ_ASSERT(!aAddrRec->mResolving);
1422 if (!StaticPrefs::network_trr_retry_on_recoverable_errors()) {
1423 LOG(("nsHostResolver::MaybeRetryTRRLookup retrying with native"));
1424 return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock));
1427 if (IsFailedConfirmationOrNoConnectivity(aFirstAttemptSkipReason) ||
1428 IsNonRecoverableTRRSkipReason(aFirstAttemptSkipReason) ||
1429 IsBlockedTRRRequest(aFirstAttemptSkipReason)) {
1430 LOG(
1431 ("nsHostResolver::MaybeRetryTRRLookup retrying with native in strict "
1432 "mode, skip reason was %d",
1433 static_cast<uint32_t>(aFirstAttemptSkipReason)));
1434 return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock));
1437 if (aAddrRec->mTrrAttempts > 1) {
1438 if (!StaticPrefs::network_trr_strict_native_fallback()) {
1439 LOG(
1440 ("nsHostResolver::MaybeRetryTRRLookup retry failed. Using "
1441 "native."));
1442 return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock));
1445 if (aFirstAttemptSkipReason == TRRSkippedReason::TRR_TIMEOUT &&
1446 StaticPrefs::network_trr_strict_native_fallback_allow_timeouts()) {
1447 LOG(
1448 ("nsHostResolver::MaybeRetryTRRLookup retry timed out. Using "
1449 "native."));
1450 return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock));
1452 LOG(("nsHostResolver::MaybeRetryTRRLookup mTrrAttempts>1, not retrying."));
1453 return false;
1456 LOG(
1457 ("nsHostResolver::MaybeRetryTRRLookup triggering Confirmation and "
1458 "retrying with TRR, skip reason was %d",
1459 static_cast<uint32_t>(aFirstAttemptSkipReason)));
1460 TRRService::Get()->RetryTRRConfirm();
1462 return DoRetryTRR(aAddrRec, aLock);
1465 bool nsHostResolver::DoRetryTRR(AddrHostRecord* aAddrRec,
1466 const mozilla::MutexAutoLock& aLock) {
1468 // Clear out the old query
1469 auto trrQuery = aAddrRec->mTRRQuery.Lock();
1470 trrQuery.ref() = nullptr;
1473 if (NS_SUCCEEDED(TrrLookup(aAddrRec, aLock, nullptr /* pushedTRR */))) {
1474 aAddrRec->NotifyRetryingTrr();
1475 return true;
1478 return false;
1482 // CompleteLookup() checks if the resolving should be redone and if so it
1483 // returns LOOKUP_RESOLVEAGAIN, but only if 'status' is not NS_ERROR_ABORT.
1484 nsHostResolver::LookupStatus nsHostResolver::CompleteLookup(
1485 nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb,
1486 const nsACString& aOriginsuffix, TRRSkippedReason aReason,
1487 mozilla::net::TRR* aTRRRequest) {
1488 MutexAutoLock lock(mLock);
1489 return CompleteLookupLocked(rec, status, aNewRRSet, pb, aOriginsuffix,
1490 aReason, aTRRRequest, lock);
1493 namespace {
1494 class NetAddrIPv6FirstComparator {
1495 public:
1496 static bool Equals(const NetAddr& aLhs, const NetAddr& aRhs) {
1497 return aLhs.raw.family == aRhs.raw.family;
1499 static bool LessThan(const NetAddr& aLhs, const NetAddr& aRhs) {
1500 return aLhs.raw.family > aRhs.raw.family;
1503 } // namespace
1505 nsHostResolver::LookupStatus nsHostResolver::CompleteLookupLocked(
1506 nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb,
1507 const nsACString& aOriginsuffix, TRRSkippedReason aReason,
1508 mozilla::net::TRR* aTRRRequest, const mozilla::MutexAutoLock& aLock) {
1509 MOZ_ASSERT(rec);
1510 MOZ_ASSERT(rec->pb == pb);
1511 MOZ_ASSERT(rec->IsAddrRecord());
1513 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
1514 MOZ_ASSERT(addrRec);
1516 RefPtr<AddrInfo> newRRSet(aNewRRSet);
1517 MOZ_ASSERT(NS_FAILED(status) || newRRSet->Addresses().Length() > 0);
1519 DNSResolverType type =
1520 newRRSet ? newRRSet->ResolverType() : DNSResolverType::Native;
1522 if (NS_FAILED(status)) {
1523 newRRSet = nullptr;
1526 if (addrRec->LoadResolveAgain() && (status != NS_ERROR_ABORT) &&
1527 type == DNSResolverType::Native) {
1528 LOG(("nsHostResolver record %p resolve again due to flushcache\n",
1529 addrRec.get()));
1530 addrRec->StoreResolveAgain(false);
1531 return LOOKUP_RESOLVEAGAIN;
1534 MOZ_ASSERT(addrRec->mResolving);
1535 addrRec->mResolving--;
1536 LOG((
1537 "nsHostResolver::CompleteLookup %s %p %X resolver=%d stillResolving=%d\n",
1538 addrRec->host.get(), aNewRRSet, (unsigned int)status, (int)type,
1539 int(addrRec->mResolving)));
1541 if (type != DNSResolverType::Native) {
1542 if (NS_FAILED(status) && status != NS_ERROR_UNKNOWN_HOST &&
1543 status != NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
1544 // the errors are not failed resolves, that means
1545 // something else failed, consider this as *TRR not used*
1546 // for actually trying to resolve the host
1547 addrRec->mResolverType = DNSResolverType::Native;
1550 if (NS_FAILED(status)) {
1551 if (aReason != TRRSkippedReason::TRR_UNSET) {
1552 addrRec->RecordReason(aReason);
1553 } else {
1554 // Unknown failed reason.
1555 addrRec->RecordReason(TRRSkippedReason::TRR_FAILED);
1557 } else {
1558 addrRec->mTRRSuccess = true;
1559 addrRec->RecordReason(TRRSkippedReason::TRR_OK);
1562 nsresult channelStatus = aTRRRequest->ChannelStatus();
1563 if (MaybeRetryTRRLookup(addrRec, status, aReason, channelStatus, aLock)) {
1564 MOZ_ASSERT(addrRec->mResolving);
1565 return LOOKUP_OK;
1568 if (!addrRec->mTRRSuccess) {
1569 // no TRR success
1570 newRRSet = nullptr;
1573 if (NS_FAILED(status)) {
1574 // This is the error that consumers expect.
1575 status = NS_ERROR_UNKNOWN_HOST;
1577 } else { // native resolve completed
1578 if (addrRec->LoadUsingAnyThread()) {
1579 mActiveAnyThreadCount--;
1580 addrRec->StoreUsingAnyThread(false);
1583 addrRec->mNativeSuccess = static_cast<bool>(newRRSet);
1584 if (addrRec->mNativeSuccess) {
1585 addrRec->mNativeDuration = TimeStamp::Now() - addrRec->mNativeStart;
1589 addrRec->OnCompleteLookup();
1591 // update record fields. We might have a addrRec->addr_info already if a
1592 // previous lookup result expired and we're reresolving it or we get
1593 // a late second TRR response.
1594 if (!mShutdown) {
1595 MutexAutoLock lock(addrRec->addr_info_lock);
1596 RefPtr<AddrInfo> old_addr_info;
1597 if (different_rrset(addrRec->addr_info, newRRSet)) {
1598 LOG(("nsHostResolver record %p new gencnt\n", addrRec.get()));
1599 old_addr_info = addrRec->addr_info;
1600 addrRec->addr_info = std::move(newRRSet);
1601 addrRec->addr_info_gencnt++;
1602 } else {
1603 if (addrRec->addr_info && newRRSet) {
1604 auto builder = addrRec->addr_info->Build();
1605 builder.SetTTL(newRRSet->TTL());
1606 // Update trr timings
1607 builder.SetTrrFetchDuration(newRRSet->GetTrrFetchDuration());
1608 builder.SetTrrFetchDurationNetworkOnly(
1609 newRRSet->GetTrrFetchDurationNetworkOnly());
1611 addrRec->addr_info = builder.Finish();
1613 old_addr_info = std::move(newRRSet);
1615 addrRec->negative = !addrRec->addr_info;
1617 if (addrRec->addr_info && StaticPrefs::network_dns_preferIPv6() &&
1618 addrRec->addr_info->Addresses().Length() > 1 &&
1619 addrRec->addr_info->Addresses()[0].IsIPAddrV4()) {
1620 // Sort IPv6 addresses first.
1621 auto builder = addrRec->addr_info->Build();
1622 builder.SortAddresses(NetAddrIPv6FirstComparator());
1623 addrRec->addr_info = builder.Finish();
1626 PrepareRecordExpirationAddrRecord(addrRec);
1629 if (LOG_ENABLED()) {
1630 MutexAutoLock lock(addrRec->addr_info_lock);
1631 if (addrRec->addr_info) {
1632 for (const auto& elem : addrRec->addr_info->Addresses()) {
1633 char buf[128];
1634 elem.ToStringBuffer(buf, sizeof(buf));
1635 LOG(("CompleteLookup: %s has %s\n", addrRec->host.get(), buf));
1637 } else {
1638 LOG(("CompleteLookup: %s has NO address\n", addrRec->host.get()));
1642 // get the list of pending callbacks for this lookup, and notify
1643 // them that the lookup is complete.
1644 mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs =
1645 std::move(rec->mCallbacks);
1647 LOG(("nsHostResolver record %p calling back dns users status:%X\n",
1648 addrRec.get(), int(status)));
1650 for (nsResolveHostCallback* c = cbs.getFirst(); c;
1651 c = c->removeAndGetNext()) {
1652 c->OnResolveHostComplete(this, rec, status);
1655 OnResolveComplete(rec, aLock);
1657 #ifdef DNSQUERY_AVAILABLE
1658 // Unless the result is from TRR, resolve again to get TTL
1659 bool hasNativeResult = false;
1661 MutexAutoLock lock(addrRec->addr_info_lock);
1662 if (addrRec->addr_info && !addrRec->addr_info->IsTRR()) {
1663 hasNativeResult = true;
1666 if (hasNativeResult && !mShutdown && !addrRec->LoadGetTtl() &&
1667 !rec->mResolving && sGetTtlEnabled) {
1668 LOG(("Issuing second async lookup for TTL for host [%s].",
1669 addrRec->host.get()));
1670 addrRec->flags =
1671 (addrRec->flags & ~nsIDNSService::RESOLVE_PRIORITY_MEDIUM) |
1672 nsIDNSService::RESOLVE_PRIORITY_LOW;
1673 DebugOnly<nsresult> rv = NativeLookup(rec, aLock);
1674 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1675 "Could not issue second async lookup for TTL.");
1677 #endif
1678 return LOOKUP_OK;
1681 nsHostResolver::LookupStatus nsHostResolver::CompleteLookupByType(
1682 nsHostRecord* rec, nsresult status,
1683 mozilla::net::TypeRecordResultType& aResult, TRRSkippedReason aReason,
1684 uint32_t aTtl, bool pb) {
1685 MutexAutoLock lock(mLock);
1686 return CompleteLookupByTypeLocked(rec, status, aResult, aReason, aTtl, pb,
1687 lock);
1690 nsHostResolver::LookupStatus nsHostResolver::CompleteLookupByTypeLocked(
1691 nsHostRecord* rec, nsresult status,
1692 mozilla::net::TypeRecordResultType& aResult, TRRSkippedReason aReason,
1693 uint32_t aTtl, bool pb, const mozilla::MutexAutoLock& aLock) {
1694 MOZ_ASSERT(rec);
1695 MOZ_ASSERT(rec->pb == pb);
1696 MOZ_ASSERT(!rec->IsAddrRecord());
1698 if (rec->LoadNative()) {
1699 // If this was resolved using the native resolver
1700 // we also need to update the global count.
1701 if (rec->LoadUsingAnyThread()) {
1702 mActiveAnyThreadCount--;
1703 rec->StoreUsingAnyThread(false);
1707 RefPtr<TypeHostRecord> typeRec = do_QueryObject(rec);
1708 MOZ_ASSERT(typeRec);
1710 MOZ_ASSERT(typeRec->mResolving);
1711 typeRec->mResolving--;
1713 if (NS_FAILED(status)) {
1714 LOG(("nsHostResolver::CompleteLookupByType record %p [%s] status %x\n",
1715 typeRec.get(), typeRec->host.get(), (unsigned int)status));
1716 typeRec->SetExpiration(
1717 TimeStamp::NowLoRes(),
1718 StaticPrefs::network_dns_negative_ttl_for_type_record(), 0);
1719 MOZ_ASSERT(aResult.is<TypeRecordEmpty>());
1720 status = NS_ERROR_UNKNOWN_HOST;
1721 typeRec->negative = true;
1722 if (aReason != TRRSkippedReason::TRR_UNSET) {
1723 typeRec->RecordReason(aReason);
1724 } else {
1725 // Unknown failed reason.
1726 typeRec->RecordReason(TRRSkippedReason::TRR_FAILED);
1728 } else {
1729 size_t recordCount = 0;
1730 if (aResult.is<TypeRecordTxt>()) {
1731 recordCount = aResult.as<TypeRecordTxt>().Length();
1732 } else if (aResult.is<TypeRecordHTTPSSVC>()) {
1733 recordCount = aResult.as<TypeRecordHTTPSSVC>().Length();
1735 LOG(
1736 ("nsHostResolver::CompleteLookupByType record %p [%s], number of "
1737 "records %zu\n",
1738 typeRec.get(), typeRec->host.get(), recordCount));
1739 MutexAutoLock typeLock(typeRec->mResultsLock);
1740 typeRec->mResults = aResult;
1741 typeRec->SetExpiration(TimeStamp::NowLoRes(), aTtl, mDefaultGracePeriod);
1742 typeRec->negative = false;
1743 typeRec->mTRRSuccess = !rec->LoadNative();
1744 typeRec->mNativeSuccess = rec->LoadNative();
1745 MOZ_ASSERT(aReason != TRRSkippedReason::TRR_UNSET);
1746 typeRec->RecordReason(aReason);
1749 mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs =
1750 std::move(typeRec->mCallbacks);
1752 LOG(
1753 ("nsHostResolver::CompleteLookupByType record %p calling back dns "
1754 "users\n",
1755 typeRec.get()));
1757 for (nsResolveHostCallback* c = cbs.getFirst(); c;
1758 c = c->removeAndGetNext()) {
1759 c->OnResolveHostComplete(this, rec, status);
1762 OnResolveComplete(rec, aLock);
1764 return LOOKUP_OK;
1767 void nsHostResolver::OnResolveComplete(nsHostRecord* aRec,
1768 const mozilla::MutexAutoLock& aLock) {
1769 if (!aRec->mResolving && !mShutdown) {
1771 auto trrQuery = aRec->mTRRQuery.Lock();
1772 if (trrQuery.ref()) {
1773 aRec->mTrrDuration = trrQuery.ref()->Duration();
1775 trrQuery.ref() = nullptr;
1777 aRec->ResolveComplete();
1779 AddToEvictionQ(aRec, aLock);
1783 void nsHostResolver::CancelAsyncRequest(
1784 const nsACString& host, const nsACString& aTrrServer, uint16_t aType,
1785 const OriginAttributes& aOriginAttributes, nsIDNSService::DNSFlags flags,
1786 uint16_t af, nsIDNSListener* aListener, nsresult status)
1789 MutexAutoLock lock(mLock);
1791 nsAutoCString originSuffix;
1792 aOriginAttributes.CreateSuffix(originSuffix);
1794 // Lookup the host record associated with host, flags & address family
1796 nsHostKey key(host, aTrrServer, aType, flags, af,
1797 (aOriginAttributes.mPrivateBrowsingId > 0), originSuffix);
1798 RefPtr<nsHostRecord> rec = mRecordDB.Get(key);
1799 if (!rec) {
1800 return;
1803 for (RefPtr<nsResolveHostCallback> c : rec->mCallbacks) {
1804 if (c->EqualsAsyncListener(aListener)) {
1805 c->remove();
1806 c->OnResolveHostComplete(this, rec.get(), status);
1807 break;
1811 // If there are no more callbacks, remove the hash table entry
1812 if (rec->mCallbacks.isEmpty()) {
1813 mRecordDB.Remove(*static_cast<nsHostKey*>(rec.get()));
1814 // If record is on a Queue, remove it
1815 mQueue.MaybeRemoveFromQ(rec, lock);
1819 size_t nsHostResolver::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const {
1820 MutexAutoLock lock(mLock);
1822 size_t n = mallocSizeOf(this);
1824 n += mRecordDB.ShallowSizeOfExcludingThis(mallocSizeOf);
1825 for (const auto& entry : mRecordDB.Values()) {
1826 n += entry->SizeOfIncludingThis(mallocSizeOf);
1829 // The following fields aren't measured.
1830 // - mHighQ, mMediumQ, mLowQ, mEvictionQ, because they just point to
1831 // nsHostRecords that also pointed to by entries |mRecordDB|, and
1832 // measured when |mRecordDB| is measured.
1834 return n;
1837 void nsHostResolver::ThreadFunc() {
1838 LOG(("DNS lookup thread - starting execution.\n"));
1840 #if defined(RES_RETRY_ON_FAILURE)
1841 nsResState rs;
1842 #endif
1843 RefPtr<nsHostRecord> rec;
1844 RefPtr<AddrInfo> ai;
1846 do {
1847 if (!rec) {
1848 RefPtr<nsHostRecord> tmpRec;
1849 if (!GetHostToLookup(getter_AddRefs(tmpRec))) {
1850 break; // thread shutdown signal
1852 // GetHostToLookup() returns an owning reference
1853 MOZ_ASSERT(tmpRec);
1854 rec.swap(tmpRec);
1857 LOG1(("DNS lookup thread - Calling getaddrinfo for host [%s].\n",
1858 rec->host.get()));
1860 TimeStamp startTime = TimeStamp::Now();
1861 bool getTtl = rec->LoadGetTtl();
1862 TimeDuration inQueue = startTime - rec->mNativeStart;
1863 uint32_t ms = static_cast<uint32_t>(inQueue.ToMilliseconds());
1864 Telemetry::Accumulate(Telemetry::DNS_NATIVE_QUEUING, ms);
1866 if (!rec->IsAddrRecord()) {
1867 LOG(("byType on DNS thread"));
1868 TypeRecordResultType result = AsVariant(mozilla::Nothing());
1869 uint32_t ttl = UINT32_MAX;
1870 nsresult status = ResolveHTTPSRecord(rec->host, rec->flags, result, ttl);
1871 mozilla::glean::networking::dns_native_count
1872 .EnumGet(rec->pb
1873 ? glean::networking::DnsNativeCountLabel::eHttpsPrivate
1874 : glean::networking::DnsNativeCountLabel::eHttpsRegular)
1875 .Add(1);
1876 CompleteLookupByType(rec, status, result, rec->mTRRSkippedReason, ttl,
1877 rec->pb);
1878 rec = nullptr;
1879 continue;
1882 nsresult status =
1883 GetAddrInfo(rec->host, rec->af, rec->flags, getter_AddRefs(ai), getTtl);
1884 #if defined(RES_RETRY_ON_FAILURE)
1885 if (NS_FAILED(status) && rs.Reset()) {
1886 status = GetAddrInfo(rec->host, rec->af, rec->flags, getter_AddRefs(ai),
1887 getTtl);
1889 #endif
1891 mozilla::glean::networking::dns_native_count
1892 .EnumGet(rec->pb ? glean::networking::DnsNativeCountLabel::ePrivate
1893 : glean::networking::DnsNativeCountLabel::eRegular)
1894 .Add(1);
1896 if (RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec)) {
1897 // obtain lock to check shutdown and manage inter-module telemetry
1898 MutexAutoLock lock(mLock);
1900 if (!mShutdown) {
1901 TimeDuration elapsed = TimeStamp::Now() - startTime;
1902 if (NS_SUCCEEDED(status)) {
1903 if (!addrRec->addr_info_gencnt) {
1904 // Time for initial lookup.
1905 glean::networking::dns_lookup_time.AccumulateRawDuration(elapsed);
1906 } else if (!getTtl) {
1907 // Time for renewal; categorized by expiration strategy.
1908 glean::networking::dns_renewal_time.AccumulateRawDuration(elapsed);
1909 } else {
1910 // Time to get TTL; categorized by expiration strategy.
1911 glean::networking::dns_renewal_time_for_ttl.AccumulateRawDuration(
1912 elapsed);
1914 } else {
1915 glean::networking::dns_failed_lookup_time.AccumulateRawDuration(
1916 elapsed);
1921 LOG1(("DNS lookup thread - lookup completed for host [%s]: %s.\n",
1922 rec->host.get(), ai ? "success" : "failure: unknown host"));
1924 if (LOOKUP_RESOLVEAGAIN ==
1925 CompleteLookup(rec, status, ai, rec->pb, rec->originSuffix,
1926 rec->mTRRSkippedReason, nullptr)) {
1927 // leave 'rec' assigned and loop to make a renewed host resolve
1928 LOG(("DNS lookup thread - Re-resolving host [%s].\n", rec->host.get()));
1929 } else {
1930 rec = nullptr;
1932 } while (true);
1934 MutexAutoLock lock(mLock);
1935 mActiveTaskCount--;
1936 LOG(("DNS lookup thread - queue empty, task finished.\n"));
1939 void nsHostResolver::SetCacheLimits(uint32_t aMaxCacheEntries,
1940 uint32_t aDefaultCacheEntryLifetime,
1941 uint32_t aDefaultGracePeriod) {
1942 MutexAutoLock lock(mLock);
1943 mMaxCacheEntries = aMaxCacheEntries;
1944 mDefaultCacheLifetime = aDefaultCacheEntryLifetime;
1945 mDefaultGracePeriod = aDefaultGracePeriod;
1948 nsresult nsHostResolver::Create(uint32_t maxCacheEntries,
1949 uint32_t defaultCacheEntryLifetime,
1950 uint32_t defaultGracePeriod,
1951 nsHostResolver** result) {
1952 RefPtr<nsHostResolver> res = new nsHostResolver(
1953 maxCacheEntries, defaultCacheEntryLifetime, defaultGracePeriod);
1955 nsresult rv = res->Init();
1956 if (NS_FAILED(rv)) {
1957 return rv;
1960 res.forget(result);
1961 return NS_OK;
1964 void nsHostResolver::GetDNSCacheEntries(nsTArray<DNSCacheEntries>* args) {
1965 MutexAutoLock lock(mLock);
1966 for (const auto& recordEntry : mRecordDB) {
1967 // We don't pay attention to address literals, only resolved domains.
1968 // Also require a host.
1969 nsHostRecord* rec = recordEntry.GetWeak();
1970 MOZ_ASSERT(rec, "rec should never be null here!");
1972 if (!rec) {
1973 continue;
1976 // For now we only show A/AAAA records.
1977 if (!rec->IsAddrRecord()) {
1978 continue;
1981 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
1982 MOZ_ASSERT(addrRec);
1983 if (!addrRec || !addrRec->addr_info) {
1984 continue;
1987 DNSCacheEntries info;
1988 info.hostname = rec->host;
1989 info.family = rec->af;
1990 info.expiration =
1991 (int64_t)(rec->mValidEnd - TimeStamp::NowLoRes()).ToSeconds();
1992 if (info.expiration <= 0) {
1993 // We only need valid DNS cache entries
1994 continue;
1998 MutexAutoLock lock(addrRec->addr_info_lock);
1999 for (const auto& addr : addrRec->addr_info->Addresses()) {
2000 char buf[kIPv6CStrBufSize];
2001 if (addr.ToStringBuffer(buf, sizeof(buf))) {
2002 info.hostaddr.AppendElement(buf);
2005 info.TRR = addrRec->addr_info->IsTRR();
2008 info.originAttributesSuffix = recordEntry.GetKey().originSuffix;
2009 info.flags = nsPrintfCString("%u|0x%x|%u|%d|%s", rec->type, rec->flags,
2010 rec->af, rec->pb, rec->mTrrServer.get());
2012 args->AppendElement(std::move(info));
2016 #undef LOG
2017 #undef LOG_ENABLED