Backed out changeset 82c7a343a1a1 (bug 1474705) for causing linting opt failures...
[gecko.git] / netwerk / dns / nsHostResolver.cpp
blob537290afff4ce5622e64e65a130e7ec9f29dfdc5
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 #endif
15 #include <stdlib.h>
16 #include <ctime>
17 #include "nsHostResolver.h"
18 #include "nsError.h"
19 #include "nsISupports.h"
20 #include "nsISupportsUtils.h"
21 #include "nsIThreadManager.h"
22 #include "nsComponentManagerUtils.h"
23 #include "nsNetUtil.h"
24 #include "nsPrintfCString.h"
25 #include "nsXPCOMCIDInternal.h"
26 #include "prthread.h"
27 #include "prerror.h"
28 #include "prtime.h"
29 #include "mozilla/Logging.h"
30 #include "PLDHashTable.h"
31 #include "nsQueryObject.h"
32 #include "nsURLHelper.h"
33 #include "nsThreadUtils.h"
34 #include "nsThreadPool.h"
35 #include "GetAddrInfo.h"
36 #include "TRR.h"
37 #include "TRRQuery.h"
38 #include "TRRService.h"
40 #include "mozilla/Atomics.h"
41 #include "mozilla/glean/GleanMetrics.h"
42 #include "mozilla/HashFunctions.h"
43 #include "mozilla/TimeStamp.h"
44 #include "mozilla/Telemetry.h"
45 #include "mozilla/DebugOnly.h"
46 #include "mozilla/Preferences.h"
47 #include "mozilla/StaticPrefs_network.h"
48 // Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
49 #include "DNSLogging.h"
51 #ifdef XP_WIN
52 # include "mozilla/WindowsVersion.h"
53 #endif // XP_WIN
55 #ifdef MOZ_WIDGET_ANDROID
56 # include "mozilla/jni/Utils.h"
57 #endif
59 #define IS_ADDR_TYPE(_type) ((_type) == nsIDNSService::RESOLVE_TYPE_DEFAULT)
60 #define IS_OTHER_TYPE(_type) ((_type) != nsIDNSService::RESOLVE_TYPE_DEFAULT)
62 using namespace mozilla;
63 using namespace mozilla::net;
65 // None of our implementations expose a TTL for negative responses, so we use a
66 // constant always.
67 static const unsigned int NEGATIVE_RECORD_LIFETIME = 60;
69 //----------------------------------------------------------------------------
71 // Use a persistent thread pool in order to avoid spinning up new threads all
72 // the time. In particular, thread creation results in a res_init() call from
73 // libc which is quite expensive.
75 // The pool dynamically grows between 0 and MaxResolverThreads() in size. New
76 // requests go first to an idle thread. If that cannot be found and there are
77 // fewer than MaxResolverThreads() currently in the pool a new thread is created
78 // for high priority requests. If the new request is at a lower priority a new
79 // thread will only be created if there are fewer than
80 // MaxResolverThreadsAnyPriority() currently outstanding. If a thread cannot be
81 // created or an idle thread located for the request it is queued.
83 // When the pool is greater than MaxResolverThreadsAnyPriority() in size a
84 // thread will be destroyed after ShortIdleTimeoutSeconds of idle time. Smaller
85 // pools use LongIdleTimeoutSeconds for a timeout period.
87 // for threads 1 -> MaxResolverThreadsAnyPriority()
88 #define LongIdleTimeoutSeconds 300
89 // for threads MaxResolverThreadsAnyPriority() + 1 -> MaxResolverThreads()
90 #define ShortIdleTimeoutSeconds 60
92 using namespace mozilla;
94 namespace geckoprofiler::markers {
96 struct HostResolverMarker {
97 static constexpr Span<const char> MarkerTypeName() {
98 return MakeStringSpan("HostResolver");
100 static void StreamJSONMarkerData(
101 mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
102 const mozilla::ProfilerString8View& aHost,
103 const mozilla::ProfilerString8View& aOriginSuffix, uint16_t aType,
104 uint32_t aFlags) {
105 aWriter.StringProperty("host", aHost);
106 aWriter.StringProperty("originSuffix", aOriginSuffix);
107 aWriter.IntProperty("qtype", aType);
108 aWriter.StringProperty("flags", nsPrintfCString("0x%x", aFlags));
110 static MarkerSchema MarkerTypeDisplay() {
111 using MS = MarkerSchema;
112 MS schema(MS::Location::MarkerChart, MS::Location::MarkerTable);
113 schema.SetTableLabel("{marker.name} - {marker.data.host}");
114 schema.AddKeyFormatSearchable("host", MS::Format::SanitizedString,
115 MS::Searchable::Searchable);
116 schema.AddKeyFormatSearchable("originSuffix", MS::Format::SanitizedString,
117 MS::Searchable::Searchable);
118 schema.AddKeyFormat("qtype", MS::Format::Integer);
119 schema.AddKeyFormat("flags", MS::Format::String);
120 return schema;
124 } // namespace geckoprofiler::markers
126 //----------------------------------------------------------------------------
128 namespace mozilla::net {
129 LazyLogModule gHostResolverLog("nsHostResolver");
130 } // namespace mozilla::net
132 //----------------------------------------------------------------------------
134 class DnsThreadListener final : public nsIThreadPoolListener {
135 NS_DECL_THREADSAFE_ISUPPORTS
136 NS_DECL_NSITHREADPOOLLISTENER
137 private:
138 virtual ~DnsThreadListener() = default;
141 NS_IMETHODIMP
142 DnsThreadListener::OnThreadCreated() {
143 #if defined(HAVE_RES_NINIT)
144 if (!(_res.options & RES_INIT)) {
145 auto result = res_ninit(&_res);
146 LOG(("'res_ninit' returned %d ", result));
148 #endif
149 return NS_OK;
152 NS_IMETHODIMP
153 DnsThreadListener::OnThreadShuttingDown() {
154 DNSThreadShutdown();
155 #if defined(HAVE_RES_NINIT)
156 if (_res.options & RES_INIT) {
157 res_nclose(&_res);
158 memset(&_res, 0, sizeof(_res));
160 #endif
161 return NS_OK;
164 NS_IMPL_ISUPPORTS(DnsThreadListener, nsIThreadPoolListener)
166 //----------------------------------------------------------------------------
168 static const char kPrefGetTtl[] = "network.dns.get-ttl";
169 static const char kPrefNativeIsLocalhost[] = "network.dns.native-is-localhost";
170 static const char kPrefThreadIdleTime[] =
171 "network.dns.resolver-thread-extra-idle-time-seconds";
172 static bool sGetTtlEnabled = false;
173 mozilla::Atomic<bool, mozilla::Relaxed> gNativeIsLocalhost;
174 mozilla::Atomic<bool, mozilla::Relaxed> sNativeHTTPSSupported{false};
176 static void DnsPrefChanged(const char* aPref, void* aSelf) {
177 MOZ_ASSERT(NS_IsMainThread(),
178 "Should be getting pref changed notification on main thread!");
180 MOZ_ASSERT(aSelf);
182 if (!strcmp(aPref, kPrefGetTtl)) {
183 #ifdef DNSQUERY_AVAILABLE
184 sGetTtlEnabled = Preferences::GetBool(kPrefGetTtl);
185 #endif
186 } else if (!strcmp(aPref, kPrefNativeIsLocalhost)) {
187 gNativeIsLocalhost = Preferences::GetBool(kPrefNativeIsLocalhost);
191 NS_IMPL_ISUPPORTS0(nsHostResolver)
193 nsHostResolver::nsHostResolver(uint32_t maxCacheEntries,
194 uint32_t defaultCacheEntryLifetime,
195 uint32_t defaultGracePeriod)
196 : mMaxCacheEntries(maxCacheEntries),
197 mDefaultCacheLifetime(defaultCacheEntryLifetime),
198 mDefaultGracePeriod(defaultGracePeriod),
199 mIdleTaskCV(mLock, "nsHostResolver.mIdleTaskCV") {
200 mCreationTime = PR_Now();
202 mLongIdleTimeout = TimeDuration::FromSeconds(LongIdleTimeoutSeconds);
203 mShortIdleTimeout = TimeDuration::FromSeconds(ShortIdleTimeoutSeconds);
206 nsHostResolver::~nsHostResolver() = default;
208 nsresult nsHostResolver::Init() MOZ_NO_THREAD_SAFETY_ANALYSIS {
209 MOZ_ASSERT(NS_IsMainThread());
210 if (NS_FAILED(GetAddrInfoInit())) {
211 return NS_ERROR_FAILURE;
214 LOG(("nsHostResolver::Init this=%p", this));
216 mShutdown = false;
217 mNCS = NetworkConnectivityService::GetSingleton();
219 // The preferences probably haven't been loaded from the disk yet, so we
220 // need to register a callback that will set up the experiment once they
221 // are. We also need to explicitly set a value for the props otherwise the
222 // callback won't be called.
224 DebugOnly<nsresult> rv = Preferences::RegisterCallbackAndCall(
225 &DnsPrefChanged, kPrefGetTtl, this);
226 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
227 "Could not register DNS TTL pref callback.");
228 rv = Preferences::RegisterCallbackAndCall(&DnsPrefChanged,
229 kPrefNativeIsLocalhost, this);
230 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
231 "Could not register DNS pref callback.");
234 #if defined(HAVE_RES_NINIT)
235 // We want to make sure the system is using the correct resolver settings,
236 // so we force it to reload those settings whenever we startup a subsequent
237 // nsHostResolver instance. We assume that there is no reason to do this
238 // for the first nsHostResolver instance since that is usually created
239 // during application startup.
240 static int initCount = 0;
241 if (initCount++ > 0) {
242 auto result = res_ninit(&_res);
243 LOG(("nsHostResolver::Init > 'res_ninit' returned %d", result));
245 #endif
247 // We can configure the threadpool to keep threads alive for a while after
248 // the last ThreadFunc task has been executed.
249 int32_t poolTimeoutSecs = Preferences::GetInt(kPrefThreadIdleTime, 60);
250 uint32_t poolTimeoutMs;
251 if (poolTimeoutSecs < 0) {
252 // This means never shut down the idle threads
253 poolTimeoutMs = UINT32_MAX;
254 } else {
255 // We clamp down the idle time between 0 and one hour.
256 poolTimeoutMs =
257 mozilla::clamped<uint32_t>(poolTimeoutSecs * 1000, 0, 3600 * 1000);
260 #if defined(XP_WIN)
261 // For some reason, the DNSQuery_A API doesn't work on Windows 10.
262 // It returns a success code, but no records. We only allow
263 // native HTTPS records on Win 11 for now.
264 sNativeHTTPSSupported = StaticPrefs::network_dns_native_https_query_win10() ||
265 mozilla::IsWin11OrLater();
266 #elif defined(MOZ_WIDGET_ANDROID)
267 // android_res_nquery only got added in API level 29
268 sNativeHTTPSSupported = jni::GetAPIVersion() >= 29;
269 #elif defined(XP_LINUX) || defined(XP_MACOSX)
270 sNativeHTTPSSupported = true;
271 #endif
272 LOG(("Native HTTPS records supported=%d", bool(sNativeHTTPSSupported)));
274 // The ThreadFunc has its own loop and will live very long and block one
275 // thread from the thread pool's point of view, such that the timeouts are
276 // less important here. The pool is mostly used to provide an easy way to
277 // create and shutdown those threads.
278 // TODO: It seems, the ThreadFunc resembles some quite similar timeout and
279 // wait for events logic as the pool offers, maybe we could simplify this
280 // a bit, see bug 1478732 for a previous attempt.
281 nsCOMPtr<nsIThreadPool> threadPool = new nsThreadPool();
282 MOZ_ALWAYS_SUCCEEDS(threadPool->SetThreadLimit(MaxResolverThreads()));
283 MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadLimit(
284 std::max(MaxResolverThreads() / 4, (uint32_t)1)));
285 MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadMaximumTimeout(poolTimeoutMs));
286 MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadGraceTimeout(100));
287 MOZ_ALWAYS_SUCCEEDS(
288 threadPool->SetThreadStackSize(nsIThreadManager::kThreadPoolStackSize));
289 MOZ_ALWAYS_SUCCEEDS(threadPool->SetName("DNS Resolver"_ns));
290 nsCOMPtr<nsIThreadPoolListener> listener = new DnsThreadListener();
291 threadPool->SetListener(listener);
292 mResolverThreads = ToRefPtr(std::move(threadPool));
294 return NS_OK;
297 void nsHostResolver::ClearPendingQueue(
298 LinkedList<RefPtr<nsHostRecord>>& aPendingQ) {
299 // loop through pending queue, erroring out pending lookups.
300 if (!aPendingQ.isEmpty()) {
301 for (const RefPtr<nsHostRecord>& rec : aPendingQ) {
302 rec->Cancel();
303 if (rec->IsAddrRecord()) {
304 CompleteLookup(rec, NS_ERROR_ABORT, nullptr, rec->pb, rec->originSuffix,
305 rec->mTRRSkippedReason, nullptr);
306 } else {
307 mozilla::net::TypeRecordResultType empty(Nothing{});
308 CompleteLookupByType(rec, NS_ERROR_ABORT, empty, rec->mTRRSkippedReason,
309 0, rec->pb);
316 // FlushCache() is what we call when the network has changed. We must not
317 // trust names that were resolved before this change. They may resolve
318 // differently now.
320 // This function removes all existing resolved host entries from the hash.
321 // Names that are in the pending queues can be left there. Entries in the
322 // cache that have 'Resolve' set true but not 'OnQueue' are being resolved
323 // right now, so we need to mark them to get re-resolved on completion!
325 void nsHostResolver::FlushCache(bool aTrrToo) {
326 MutexAutoLock lock(mLock);
328 mQueue.FlushEvictionQ(mRecordDB, lock);
330 // Refresh the cache entries that are resolving RIGHT now, remove the rest.
331 for (auto iter = mRecordDB.Iter(); !iter.Done(); iter.Next()) {
332 nsHostRecord* record = iter.UserData();
333 // Try to remove the record, or mark it for refresh.
334 // By-type records are from TRR. We do not need to flush those entry
335 // when the network has change, because they are not local.
336 if (record->IsAddrRecord()) {
337 RefPtr<AddrHostRecord> addrRec = do_QueryObject(record);
338 MOZ_ASSERT(addrRec);
339 if (addrRec->RemoveOrRefresh(aTrrToo)) {
340 mQueue.MaybeRemoveFromQ(record, lock);
341 LOG(("Removing (%s) Addr record from mRecordDB", record->host.get()));
342 iter.Remove();
344 } else if (aTrrToo) {
345 // remove by type records
346 LOG(("Removing (%s) type record from mRecordDB", record->host.get()));
347 iter.Remove();
352 void nsHostResolver::Shutdown() {
353 LOG(("Shutting down host resolver.\n"));
356 DebugOnly<nsresult> rv =
357 Preferences::UnregisterCallback(&DnsPrefChanged, kPrefGetTtl, this);
358 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
359 "Could not unregister DNS TTL pref callback.");
362 LinkedList<RefPtr<nsHostRecord>> pendingQHigh, pendingQMed, pendingQLow,
363 evictionQ;
366 MutexAutoLock lock(mLock);
368 mShutdown = true;
370 if (mNumIdleTasks) {
371 mIdleTaskCV.NotifyAll();
374 mQueue.ClearAll(
375 [&](nsHostRecord* aRec) {
376 mLock.AssertCurrentThreadOwns();
377 if (aRec->IsAddrRecord()) {
378 CompleteLookupLocked(aRec, NS_ERROR_ABORT, nullptr, aRec->pb,
379 aRec->originSuffix, aRec->mTRRSkippedReason,
380 nullptr, lock);
381 } else {
382 mozilla::net::TypeRecordResultType empty(Nothing{});
383 CompleteLookupByTypeLocked(aRec, NS_ERROR_ABORT, empty,
384 aRec->mTRRSkippedReason, 0, aRec->pb,
385 lock);
388 lock);
390 for (const auto& data : mRecordDB.Values()) {
391 data->Cancel();
393 // empty host database
394 mRecordDB.Clear();
396 mNCS = nullptr;
399 // Shutdown the resolver threads, but with a timeout of 2 seconds (prefable).
400 // If the timeout is exceeded, any stuck threads will be leaked.
401 mResolverThreads->ShutdownWithTimeout(
402 StaticPrefs::network_dns_resolver_shutdown_timeout_ms());
405 mozilla::DebugOnly<nsresult> rv = GetAddrInfoShutdown();
406 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to shutdown GetAddrInfo");
410 nsresult nsHostResolver::GetHostRecord(
411 const nsACString& host, const nsACString& aTrrServer, uint16_t type,
412 nsIDNSService::DNSFlags flags, uint16_t af, bool pb,
413 const nsCString& originSuffix, nsHostRecord** result) {
414 MutexAutoLock lock(mLock);
415 nsHostKey key(host, aTrrServer, type, flags, af, pb, originSuffix);
417 RefPtr<nsHostRecord> rec =
418 mRecordDB.LookupOrInsertWith(key, [&] { return InitRecord(key); });
419 if (rec->IsAddrRecord()) {
420 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
421 if (addrRec->addr) {
422 return NS_ERROR_FAILURE;
426 if (rec->mResolving) {
427 return NS_ERROR_FAILURE;
430 *result = rec.forget().take();
431 return NS_OK;
434 nsHostRecord* nsHostResolver::InitRecord(const nsHostKey& key) {
435 if (IS_ADDR_TYPE(key.type)) {
436 return new AddrHostRecord(key);
438 return new TypeHostRecord(key);
441 already_AddRefed<nsHostRecord> nsHostResolver::InitLoopbackRecord(
442 const nsHostKey& key, nsresult* aRv) {
443 MOZ_ASSERT(aRv);
444 MOZ_ASSERT(IS_ADDR_TYPE(key.type));
446 *aRv = NS_ERROR_FAILURE;
447 RefPtr<nsHostRecord> rec = InitRecord(key);
449 nsTArray<NetAddr> addresses;
450 NetAddr addr;
451 if (key.af == PR_AF_INET || key.af == PR_AF_UNSPEC) {
452 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(addr.InitFromString("127.0.0.1"_ns)));
453 addresses.AppendElement(addr);
455 if (key.af == PR_AF_INET6 || key.af == PR_AF_UNSPEC) {
456 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(addr.InitFromString("::1"_ns)));
457 addresses.AppendElement(addr);
460 RefPtr<AddrInfo> ai =
461 new AddrInfo(rec->host, DNSResolverType::Native, 0, std::move(addresses));
463 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
464 MutexAutoLock lock(addrRec->addr_info_lock);
465 addrRec->addr_info = ai;
466 addrRec->SetExpiration(TimeStamp::NowLoRes(), mDefaultCacheLifetime,
467 mDefaultGracePeriod);
468 addrRec->negative = false;
470 *aRv = NS_OK;
471 return rec.forget();
474 already_AddRefed<nsHostRecord> nsHostResolver::InitMockHTTPSRecord(
475 const nsHostKey& key) {
476 MOZ_ASSERT(IS_OTHER_TYPE(key.type));
477 if (key.type != nsIDNSService::RESOLVE_TYPE_HTTPSSVC) {
478 return nullptr;
481 RefPtr<nsHostRecord> rec = InitRecord(key);
482 LOG(("InitMockHTTPSRecord host=%s\n", rec->host.get()));
484 TypeRecordResultType result = AsVariant(mozilla::Nothing());
485 uint32_t ttl = UINT32_MAX;
486 nsresult rv =
487 CreateAndResolveMockHTTPSRecord(rec->host, rec->flags, result, ttl);
488 if (NS_FAILED(rv)) {
489 return nullptr;
492 RefPtr<TypeHostRecord> typeRec = do_QueryObject(rec);
493 typeRec->mResults = result;
494 typeRec->negative = false;
495 return rec.forget();
498 // static
499 bool nsHostResolver::IsNativeHTTPSEnabled() {
500 if (!StaticPrefs::network_dns_native_https_query()) {
501 return false;
503 return sNativeHTTPSSupported;
506 nsresult nsHostResolver::ResolveHost(const nsACString& aHost,
507 const nsACString& aTrrServer,
508 int32_t aPort, uint16_t type,
509 const OriginAttributes& aOriginAttributes,
510 nsIDNSService::DNSFlags flags, uint16_t af,
511 nsResolveHostCallback* aCallback) {
512 nsAutoCString host(aHost);
513 NS_ENSURE_TRUE(!host.IsEmpty(), NS_ERROR_UNEXPECTED);
515 nsAutoCString originSuffix;
516 aOriginAttributes.CreateSuffix(originSuffix);
517 LOG(("Resolving host [%s]<%s>%s%s type %d. [this=%p]\n", host.get(),
518 originSuffix.get(), flags & RES_BYPASS_CACHE ? " - bypassing cache" : "",
519 flags & RES_REFRESH_CACHE ? " - refresh cache" : "", type, this));
521 PROFILER_MARKER("nsHostResolver::ResolveHost", NETWORK, {},
522 HostResolverMarker, host, originSuffix, type, flags);
524 // ensure that we are working with a valid hostname before proceeding. see
525 // bug 304904 for details.
526 if (!net_IsValidHostName(host)) {
527 return NS_ERROR_UNKNOWN_HOST;
530 // If TRR is disabled we can return immediately if the native API is disabled
531 if (!IsNativeHTTPSEnabled() && IS_OTHER_TYPE(type) &&
532 Mode() == nsIDNSService::MODE_TRROFF) {
533 return NS_ERROR_UNKNOWN_HOST;
536 // Used to try to parse to an IP address literal.
537 NetAddr tempAddr;
538 if (IS_OTHER_TYPE(type) && (NS_SUCCEEDED(tempAddr.InitFromString(host)))) {
539 // For by-type queries the host cannot be IP literal.
540 return NS_ERROR_UNKNOWN_HOST;
543 RefPtr<nsResolveHostCallback> callback(aCallback);
544 // if result is set inside the lock, then we need to issue the
545 // callback before returning.
546 RefPtr<nsHostRecord> result;
547 nsresult status = NS_OK, rv = NS_OK;
549 MutexAutoLock lock(mLock);
551 if (mShutdown) {
552 return NS_ERROR_NOT_INITIALIZED;
555 // check to see if there is already an entry for this |host|
556 // in the hash table. if so, then check to see if we can't
557 // just reuse the lookup result. otherwise, if there are
558 // any pending callbacks, then add to pending callbacks queue,
559 // and return. otherwise, add ourselves as first pending
560 // callback, and proceed to do the lookup.
562 Maybe<nsCString> originHost;
563 if (StaticPrefs::network_dns_port_prefixed_qname_https_rr() &&
564 type == nsIDNSService::RESOLVE_TYPE_HTTPSSVC && aPort != -1 &&
565 aPort != 443) {
566 originHost = Some(host);
567 host = nsPrintfCString("_%d._https.%s", aPort, host.get());
568 LOG((" Using port prefixed host name [%s]", host.get()));
571 bool excludedFromTRR = false;
572 if (TRRService::Get() && TRRService::Get()->IsExcludedFromTRR(host)) {
573 flags |= nsIDNSService::RESOLVE_DISABLE_TRR;
574 flags |= nsIDNSService::RESOLVE_DISABLE_NATIVE_HTTPS_QUERY;
575 excludedFromTRR = true;
577 if (!aTrrServer.IsEmpty()) {
578 return NS_ERROR_UNKNOWN_HOST;
582 nsHostKey key(host, aTrrServer, type, flags, af,
583 (aOriginAttributes.IsPrivateBrowsing()), originSuffix);
585 // Check if we have a localhost domain, if so hardcode to loopback
586 if (IS_ADDR_TYPE(type) && IsLoopbackHostname(host)) {
587 nsresult rv;
588 RefPtr<nsHostRecord> result = InitLoopbackRecord(key, &rv);
589 if (NS_WARN_IF(NS_FAILED(rv))) {
590 return rv;
592 MOZ_ASSERT(result);
593 aCallback->OnResolveHostComplete(this, result, NS_OK);
594 return NS_OK;
597 if (flags & nsIDNSService::RESOLVE_CREATE_MOCK_HTTPS_RR) {
598 RefPtr<nsHostRecord> result = InitMockHTTPSRecord(key);
599 aCallback->OnResolveHostComplete(this, result,
600 result ? NS_OK : NS_ERROR_UNKNOWN_HOST);
601 return NS_OK;
604 RefPtr<nsHostRecord> rec =
605 mRecordDB.LookupOrInsertWith(key, [&] { return InitRecord(key); });
607 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
608 MOZ_ASSERT(rec, "Record should not be null");
609 MOZ_ASSERT((IS_ADDR_TYPE(type) && rec->IsAddrRecord() && addrRec) ||
610 (IS_OTHER_TYPE(type) && !rec->IsAddrRecord()));
612 if (IS_OTHER_TYPE(type) && originHost) {
613 RefPtr<TypeHostRecord> typeRec = do_QueryObject(rec);
614 typeRec->mOriginHost = std::move(originHost);
617 if (excludedFromTRR) {
618 rec->RecordReason(TRRSkippedReason::TRR_EXCLUDED);
621 if (!(flags & RES_BYPASS_CACHE) &&
622 rec->HasUsableResult(TimeStamp::NowLoRes(), flags)) {
623 result = FromCache(rec, host, type, status, lock);
624 } else if (addrRec && addrRec->addr) {
625 // if the host name is an IP address literal and has been
626 // parsed, go ahead and use it.
627 LOG((" Using cached address for IP Literal [%s].\n", host.get()));
628 result = FromCachedIPLiteral(rec);
629 } else if (addrRec && NS_SUCCEEDED(tempAddr.InitFromString(host))) {
630 // try parsing the host name as an IP address literal to short
631 // circuit full host resolution. (this is necessary on some
632 // platforms like Win9x. see bug 219376 for more details.)
633 LOG((" Host is IP Literal [%s].\n", host.get()));
634 result = FromIPLiteral(addrRec, tempAddr);
635 } else if (mQueue.PendingCount() >= MAX_NON_PRIORITY_REQUESTS &&
636 !IsHighPriority(flags) && !rec->mResolving) {
637 LOG(
638 (" Lookup queue full: dropping %s priority request for "
639 "host [%s].\n",
640 IsMediumPriority(flags) ? "medium" : "low", host.get()));
641 if (IS_ADDR_TYPE(type)) {
642 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_OVERFLOW);
644 // This is a lower priority request and we are swamped, so refuse it.
645 rv = NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
647 // Check if the offline flag is set.
648 } else if (flags & RES_OFFLINE) {
649 LOG((" Offline request for host [%s]; ignoring.\n", host.get()));
650 rv = NS_ERROR_OFFLINE;
652 // We do not have a valid result till here.
653 // A/AAAA request can check for an alternative entry like AF_UNSPEC.
654 // Otherwise we need to start a new query.
655 } else if (!rec->mResolving) {
656 result =
657 FromUnspecEntry(rec, host, aTrrServer, originSuffix, type, flags, af,
658 aOriginAttributes.IsPrivateBrowsing(), status);
659 // If this is a by-type request or if no valid record was found
660 // in the cache or this is an AF_UNSPEC request, then start a
661 // new lookup.
662 if (!result) {
663 LOG((" No usable record in cache for host [%s] type %d.", host.get(),
664 type));
666 if (flags & RES_REFRESH_CACHE) {
667 rec->Invalidate();
670 // Add callback to the list of pending callbacks.
671 rec->mCallbacks.insertBack(callback);
672 rec->flags = flags;
673 rv = NameLookup(rec, lock);
674 if (IS_ADDR_TYPE(type)) {
675 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
676 METHOD_NETWORK_FIRST);
678 if (NS_FAILED(rv) && callback->isInList()) {
679 callback->remove();
680 } else {
681 LOG(
682 (" DNS lookup for host [%s] blocking "
683 "pending 'getaddrinfo' or trr query: "
684 "callback [%p]",
685 host.get(), callback.get()));
688 } else {
689 LOG(
690 (" Host [%s] is being resolved. Appending callback "
691 "[%p].",
692 host.get(), callback.get()));
694 rec->mCallbacks.insertBack(callback);
696 if (rec && rec->onQueue()) {
697 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
698 METHOD_NETWORK_SHARED);
700 // Consider the case where we are on a pending queue of
701 // lower priority than the request is being made at.
702 // In that case we should upgrade to the higher queue.
704 if (IsHighPriority(flags) && !IsHighPriority(rec->flags)) {
705 // Move from (low|med) to high.
706 mQueue.MoveToAnotherPendingQ(rec, flags, lock);
707 rec->flags = flags;
708 ConditionallyCreateThread(rec);
709 } else if (IsMediumPriority(flags) && IsLowPriority(rec->flags)) {
710 // Move from low to med.
711 mQueue.MoveToAnotherPendingQ(rec, flags, lock);
712 rec->flags = flags;
713 mIdleTaskCV.Notify();
718 if (result && callback->isInList()) {
719 callback->remove();
721 } // lock
723 if (result) {
724 callback->OnResolveHostComplete(this, result, status);
727 return rv;
730 already_AddRefed<nsHostRecord> nsHostResolver::FromCache(
731 nsHostRecord* aRec, const nsACString& aHost, uint16_t aType,
732 nsresult& aStatus, const MutexAutoLock& aLock) {
733 LOG((" Using cached record for host [%s].\n",
734 nsPromiseFlatCString(aHost).get()));
736 // put reference to host record on stack...
737 RefPtr<nsHostRecord> result = aRec;
738 if (IS_ADDR_TYPE(aType)) {
739 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
742 // For entries that are in the grace period
743 // or all cached negative entries, use the cache but start a new
744 // lookup in the background
745 ConditionallyRefreshRecord(aRec, aHost, aLock);
747 if (aRec->negative) {
748 LOG((" Negative cache entry for host [%s].\n",
749 nsPromiseFlatCString(aHost).get()));
750 if (IS_ADDR_TYPE(aType)) {
751 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_NEGATIVE_HIT);
753 aStatus = NS_ERROR_UNKNOWN_HOST;
756 return result.forget();
759 already_AddRefed<nsHostRecord> nsHostResolver::FromCachedIPLiteral(
760 nsHostRecord* aRec) {
761 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_LITERAL);
762 RefPtr<nsHostRecord> result = aRec;
763 return result.forget();
766 already_AddRefed<nsHostRecord> nsHostResolver::FromIPLiteral(
767 AddrHostRecord* aAddrRec, const NetAddr& aAddr) {
768 // ok, just copy the result into the host record, and be
769 // done with it! ;-)
770 aAddrRec->addr = MakeUnique<NetAddr>(aAddr);
771 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_LITERAL);
772 // put reference to host record on stack...
773 RefPtr<nsHostRecord> result = aAddrRec;
774 return result.forget();
777 already_AddRefed<nsHostRecord> nsHostResolver::FromUnspecEntry(
778 nsHostRecord* aRec, const nsACString& aHost, const nsACString& aTrrServer,
779 const nsACString& aOriginSuffix, uint16_t aType,
780 nsIDNSService::DNSFlags aFlags, uint16_t af, bool aPb, nsresult& aStatus) {
781 RefPtr<nsHostRecord> result = nullptr;
782 // If this is an IPV4 or IPV6 specific request, check if there is
783 // an AF_UNSPEC entry we can use. Otherwise, hit the resolver...
784 RefPtr<AddrHostRecord> addrRec = do_QueryObject(aRec);
785 if (addrRec && !(aFlags & RES_BYPASS_CACHE) &&
786 ((af == PR_AF_INET) || (af == PR_AF_INET6))) {
787 // Check for an AF_UNSPEC entry.
789 const nsHostKey unspecKey(aHost, aTrrServer,
790 nsIDNSService::RESOLVE_TYPE_DEFAULT, aFlags,
791 PR_AF_UNSPEC, aPb, aOriginSuffix);
792 RefPtr<nsHostRecord> unspecRec = mRecordDB.Get(unspecKey);
794 TimeStamp now = TimeStamp::NowLoRes();
795 if (unspecRec && unspecRec->HasUsableResult(now, aFlags)) {
796 MOZ_ASSERT(unspecRec->IsAddrRecord());
798 RefPtr<AddrHostRecord> addrUnspecRec = do_QueryObject(unspecRec);
799 MOZ_ASSERT(addrUnspecRec);
800 MOZ_ASSERT(addrUnspecRec->addr_info || addrUnspecRec->negative,
801 "Entry should be resolved or negative.");
803 LOG((" Trying AF_UNSPEC entry for host [%s] af: %s.\n",
804 PromiseFlatCString(aHost).get(),
805 (af == PR_AF_INET) ? "AF_INET" : "AF_INET6"));
807 // We need to lock in case any other thread is reading
808 // addr_info.
809 MutexAutoLock lock(addrRec->addr_info_lock);
811 addrRec->addr_info = nullptr;
812 addrRec->addr_info_gencnt++;
813 if (unspecRec->negative) {
814 aRec->negative = unspecRec->negative;
815 aRec->CopyExpirationTimesAndFlagsFrom(unspecRec);
816 } else if (addrUnspecRec->addr_info) {
817 MutexAutoLock lock(addrUnspecRec->addr_info_lock);
818 if (addrUnspecRec->addr_info) {
819 // Search for any valid address in the AF_UNSPEC entry
820 // in the cache (not blocklisted and from the right
821 // family).
822 nsTArray<NetAddr> addresses;
823 for (const auto& addr : addrUnspecRec->addr_info->Addresses()) {
824 if ((af == addr.inet.family) &&
825 !addrUnspecRec->Blocklisted(&addr)) {
826 addresses.AppendElement(addr);
829 if (!addresses.IsEmpty()) {
830 addrRec->addr_info = new AddrInfo(
831 addrUnspecRec->addr_info->Hostname(),
832 addrUnspecRec->addr_info->CanonicalHostname(),
833 addrUnspecRec->addr_info->ResolverType(),
834 addrUnspecRec->addr_info->TRRType(), std::move(addresses));
835 addrRec->addr_info_gencnt++;
836 aRec->CopyExpirationTimesAndFlagsFrom(unspecRec);
840 // Now check if we have a new record.
841 if (aRec->HasUsableResult(now, aFlags)) {
842 result = aRec;
843 if (aRec->negative) {
844 aStatus = NS_ERROR_UNKNOWN_HOST;
846 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
847 ConditionallyRefreshRecord(aRec, aHost, lock);
848 } else if (af == PR_AF_INET6) {
849 // For AF_INET6, a new lookup means another AF_UNSPEC
850 // lookup. We have already iterated through the
851 // AF_UNSPEC addresses, so we mark this record as
852 // negative.
853 LOG(
854 (" No AF_INET6 in AF_UNSPEC entry: "
855 "host [%s] unknown host.",
856 nsPromiseFlatCString(aHost).get()));
857 result = aRec;
858 aRec->negative = true;
859 aStatus = NS_ERROR_UNKNOWN_HOST;
860 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
861 METHOD_NEGATIVE_HIT);
866 return result.forget();
869 void nsHostResolver::DetachCallback(
870 const nsACString& host, const nsACString& aTrrServer, uint16_t aType,
871 const OriginAttributes& aOriginAttributes, nsIDNSService::DNSFlags flags,
872 uint16_t af, nsResolveHostCallback* aCallback, nsresult status) {
873 RefPtr<nsHostRecord> rec;
874 RefPtr<nsResolveHostCallback> callback(aCallback);
877 MutexAutoLock lock(mLock);
879 nsAutoCString originSuffix;
880 aOriginAttributes.CreateSuffix(originSuffix);
882 nsHostKey key(host, aTrrServer, aType, flags, af,
883 (aOriginAttributes.IsPrivateBrowsing()), originSuffix);
884 RefPtr<nsHostRecord> entry = mRecordDB.Get(key);
885 if (entry) {
886 // walk list looking for |callback|... we cannot assume
887 // that it will be there!
889 for (nsResolveHostCallback* c : entry->mCallbacks) {
890 if (c == callback) {
891 rec = entry;
892 c->remove();
893 break;
899 // complete callback with the given status code; this would only be done if
900 // the record was in the process of being resolved.
901 if (rec) {
902 callback->OnResolveHostComplete(this, rec, status);
906 nsresult nsHostResolver::ConditionallyCreateThread(nsHostRecord* rec) {
907 if (mNumIdleTasks) {
908 // wake up idle tasks to process this lookup
909 mIdleTaskCV.Notify();
910 } else if ((mActiveTaskCount < MaxResolverThreadsAnyPriority()) ||
911 (IsHighPriority(rec->flags) &&
912 mActiveTaskCount < MaxResolverThreads())) {
913 nsCOMPtr<nsIRunnable> event = mozilla::NewRunnableMethod(
914 "nsHostResolver::ThreadFunc", this, &nsHostResolver::ThreadFunc);
915 mActiveTaskCount++;
916 nsresult rv =
917 mResolverThreads->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
918 if (NS_FAILED(rv)) {
919 mActiveTaskCount--;
921 } else {
922 LOG((" Unable to find a thread for looking up host [%s].\n",
923 rec->host.get()));
925 return NS_OK;
928 nsresult nsHostResolver::TrrLookup_unlocked(nsHostRecord* rec, TRR* pushedTRR) {
929 MutexAutoLock lock(mLock);
930 return TrrLookup(rec, lock, pushedTRR);
933 void nsHostResolver::MaybeRenewHostRecord(nsHostRecord* aRec) {
934 MutexAutoLock lock(mLock);
935 MaybeRenewHostRecordLocked(aRec, lock);
938 void nsHostResolver::MaybeRenewHostRecordLocked(nsHostRecord* aRec,
939 const MutexAutoLock& aLock) {
940 mQueue.MaybeRenewHostRecord(aRec, aLock);
943 bool nsHostResolver::TRRServiceEnabledForRecord(nsHostRecord* aRec) {
944 MOZ_ASSERT(aRec, "Record must not be empty");
945 MOZ_ASSERT(aRec->mEffectiveTRRMode != nsIRequest::TRR_DEFAULT_MODE,
946 "effective TRR mode must be computed before this call");
947 if (!TRRService::Get()) {
948 aRec->RecordReason(TRRSkippedReason::TRR_NO_GSERVICE);
949 return false;
952 // We always try custom resolvers.
953 if (!aRec->mTrrServer.IsEmpty()) {
954 return true;
957 nsIRequest::TRRMode reqMode = aRec->mEffectiveTRRMode;
958 if (TRRService::Get()->Enabled(reqMode)) {
959 return true;
962 if (NS_IsOffline()) {
963 // If we are in the NOT_CONFIRMED state _because_ we lack connectivity,
964 // then we should report that the browser is offline instead.
965 aRec->RecordReason(TRRSkippedReason::TRR_BROWSER_IS_OFFLINE);
966 return false;
969 auto hasConnectivity = [this]() -> bool {
970 mLock.AssertCurrentThreadOwns();
971 if (!mNCS) {
972 return true;
974 nsINetworkConnectivityService::ConnectivityState ipv4 = mNCS->GetIPv4();
975 nsINetworkConnectivityService::ConnectivityState ipv6 = mNCS->GetIPv6();
977 if (ipv4 == nsINetworkConnectivityService::OK ||
978 ipv6 == nsINetworkConnectivityService::OK) {
979 return true;
982 if (ipv4 == nsINetworkConnectivityService::UNKNOWN ||
983 ipv6 == nsINetworkConnectivityService::UNKNOWN) {
984 // One of the checks hasn't completed yet. Optimistically assume we'll
985 // have network connectivity.
986 return true;
989 return false;
992 if (!hasConnectivity()) {
993 aRec->RecordReason(TRRSkippedReason::TRR_NO_CONNECTIVITY);
994 return false;
997 bool isConfirmed = TRRService::Get()->IsConfirmed();
998 if (!isConfirmed) {
999 aRec->RecordReason(TRRSkippedReason::TRR_NOT_CONFIRMED);
1002 return isConfirmed;
1005 // returns error if no TRR resolve is issued
1006 // it is impt this is not called while a native lookup is going on
1007 nsresult nsHostResolver::TrrLookup(nsHostRecord* aRec,
1008 const MutexAutoLock& aLock, TRR* pushedTRR) {
1009 if (Mode() == nsIDNSService::MODE_TRROFF ||
1010 StaticPrefs::network_dns_disabled()) {
1011 return NS_ERROR_UNKNOWN_HOST;
1013 LOG(("TrrLookup host:%s af:%" PRId16, aRec->host.get(), aRec->af));
1015 RefPtr<nsHostRecord> rec(aRec);
1016 mLock.AssertCurrentThreadOwns();
1018 RefPtr<AddrHostRecord> addrRec;
1019 RefPtr<TypeHostRecord> typeRec;
1021 if (rec->IsAddrRecord()) {
1022 addrRec = do_QueryObject(rec);
1023 MOZ_ASSERT(addrRec);
1024 } else {
1025 typeRec = do_QueryObject(rec);
1026 MOZ_ASSERT(typeRec);
1029 MOZ_ASSERT(!rec->mResolving);
1031 if (!TRRServiceEnabledForRecord(aRec)) {
1032 return NS_ERROR_UNKNOWN_HOST;
1035 MaybeRenewHostRecordLocked(rec, aLock);
1037 RefPtr<TRRQuery> query = new TRRQuery(this, rec);
1038 nsresult rv = query->DispatchLookup(pushedTRR);
1039 if (NS_FAILED(rv)) {
1040 rec->RecordReason(TRRSkippedReason::TRR_DID_NOT_MAKE_QUERY);
1041 return rv;
1045 auto lock = rec->mTRRQuery.Lock();
1046 MOZ_ASSERT(!lock.ref(), "TRR already in progress");
1047 lock.ref() = query;
1050 rec->mResolving++;
1051 rec->mTrrAttempts++;
1052 rec->StoreNative(false);
1053 return NS_OK;
1056 nsresult nsHostResolver::NativeLookup(nsHostRecord* aRec,
1057 const MutexAutoLock& aLock) {
1058 if (StaticPrefs::network_dns_disabled()) {
1059 return NS_ERROR_UNKNOWN_HOST;
1061 LOG(("NativeLookup host:%s af:%" PRId16, aRec->host.get(), aRec->af));
1063 // If this is not a A/AAAA request, make sure native HTTPS is enabled.
1064 MOZ_ASSERT(aRec->IsAddrRecord() || IsNativeHTTPSEnabled());
1065 mLock.AssertCurrentThreadOwns();
1067 RefPtr<nsHostRecord> rec(aRec);
1069 rec->mNativeStart = TimeStamp::Now();
1071 // Add rec to one of the pending queues, possibly removing it from mEvictionQ.
1072 MaybeRenewHostRecordLocked(aRec, aLock);
1074 mQueue.InsertRecord(rec, rec->flags, aLock);
1076 rec->StoreNative(true);
1077 rec->StoreNativeUsed(true);
1078 rec->mResolving++;
1080 nsresult rv = ConditionallyCreateThread(rec);
1082 LOG((" DNS thread counters: total=%d any-live=%d idle=%d pending=%d\n",
1083 static_cast<uint32_t>(mActiveTaskCount),
1084 static_cast<uint32_t>(mActiveAnyThreadCount),
1085 static_cast<uint32_t>(mNumIdleTasks), mQueue.PendingCount()));
1087 return rv;
1090 // static
1091 nsIDNSService::ResolverMode nsHostResolver::Mode() {
1092 if (TRRService::Get()) {
1093 return TRRService::Get()->Mode();
1096 // If we don't have a TRR service just return MODE_TRROFF so we don't make
1097 // any TRR requests by mistake.
1098 return nsIDNSService::MODE_TRROFF;
1101 nsIRequest::TRRMode nsHostRecord::TRRMode() {
1102 return nsIDNSService::GetTRRModeFromFlags(flags);
1105 // static
1106 void nsHostResolver::ComputeEffectiveTRRMode(nsHostRecord* aRec) {
1107 nsIDNSService::ResolverMode resolverMode = nsHostResolver::Mode();
1108 nsIRequest::TRRMode requestMode = aRec->TRRMode();
1110 // For domains that are excluded from TRR or when parental control is enabled,
1111 // we fallback to NativeLookup. This happens even in MODE_TRRONLY. By default
1112 // localhost and local are excluded (so we cover *.local hosts) See the
1113 // network.trr.excluded-domains pref.
1115 if (!TRRService::Get()) {
1116 aRec->RecordReason(TRRSkippedReason::TRR_NO_GSERVICE);
1117 aRec->mEffectiveTRRMode = requestMode;
1118 return;
1121 if (!aRec->mTrrServer.IsEmpty()) {
1122 aRec->mEffectiveTRRMode = nsIRequest::TRR_ONLY_MODE;
1123 return;
1126 if (TRRService::Get()->IsExcludedFromTRR(aRec->host)) {
1127 aRec->RecordReason(TRRSkippedReason::TRR_EXCLUDED);
1128 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1129 return;
1132 if (TRRService::Get()->ParentalControlEnabled()) {
1133 aRec->RecordReason(TRRSkippedReason::TRR_PARENTAL_CONTROL);
1134 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1135 return;
1138 if (resolverMode == nsIDNSService::MODE_TRROFF) {
1139 aRec->RecordReason(TRRSkippedReason::TRR_OFF_EXPLICIT);
1140 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1141 return;
1144 if (requestMode == nsIRequest::TRR_DISABLED_MODE) {
1145 aRec->RecordReason(TRRSkippedReason::TRR_REQ_MODE_DISABLED);
1146 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1147 return;
1150 if ((requestMode == nsIRequest::TRR_DEFAULT_MODE &&
1151 resolverMode == nsIDNSService::MODE_NATIVEONLY)) {
1152 if (StaticPrefs::network_trr_display_fallback_warning()) {
1153 TRRSkippedReason heuristicResult =
1154 TRRService::Get()->GetHeuristicDetectionResult();
1155 if (heuristicResult != TRRSkippedReason::TRR_UNSET &&
1156 heuristicResult != TRRSkippedReason::TRR_OK) {
1157 aRec->RecordReason(heuristicResult);
1158 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1159 return;
1162 aRec->RecordReason(TRRSkippedReason::TRR_MODE_NOT_ENABLED);
1163 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
1164 return;
1167 if (requestMode == nsIRequest::TRR_DEFAULT_MODE &&
1168 resolverMode == nsIDNSService::MODE_TRRFIRST) {
1169 aRec->mEffectiveTRRMode = nsIRequest::TRR_FIRST_MODE;
1170 return;
1173 if (requestMode == nsIRequest::TRR_DEFAULT_MODE &&
1174 resolverMode == nsIDNSService::MODE_TRRONLY) {
1175 aRec->mEffectiveTRRMode = nsIRequest::TRR_ONLY_MODE;
1176 return;
1179 aRec->mEffectiveTRRMode = requestMode;
1182 // Kick-off a name resolve operation, using native resolver and/or TRR
1183 nsresult nsHostResolver::NameLookup(nsHostRecord* rec,
1184 const mozilla::MutexAutoLock& aLock) {
1185 LOG(("NameLookup host:%s af:%" PRId16, rec->host.get(), rec->af));
1186 mLock.AssertCurrentThreadOwns();
1188 if (rec->flags & RES_IP_HINT) {
1189 LOG(("Skip lookup if RES_IP_HINT is set\n"));
1190 return NS_ERROR_UNKNOWN_HOST;
1193 nsresult rv = NS_ERROR_UNKNOWN_HOST;
1194 if (rec->mResolving) {
1195 LOG(("NameLookup %s while already resolving\n", rec->host.get()));
1196 return NS_OK;
1199 // Make sure we reset the reason each time we attempt to do a new lookup
1200 // so we don't wrongly report the reason for the previous one.
1201 rec->Reset();
1203 ComputeEffectiveTRRMode(rec);
1205 if (!rec->mTrrServer.IsEmpty()) {
1206 LOG(("NameLookup: %s use trr:%s", rec->host.get(), rec->mTrrServer.get()));
1207 if (rec->mEffectiveTRRMode != nsIRequest::TRR_ONLY_MODE) {
1208 return NS_ERROR_UNKNOWN_HOST;
1211 if (rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR) {
1212 LOG(("TRR with server and DISABLE_TRR flag. Returning error."));
1213 return NS_ERROR_UNKNOWN_HOST;
1215 return TrrLookup(rec, aLock);
1218 LOG(("NameLookup: %s effectiveTRRmode: %d flags: %X", rec->host.get(),
1219 static_cast<nsIRequest::TRRMode>(rec->mEffectiveTRRMode), rec->flags));
1221 if (rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR) {
1222 rec->RecordReason(TRRSkippedReason::TRR_DISABLED_FLAG);
1225 bool serviceNotReady = !TRRServiceEnabledForRecord(rec);
1227 if (rec->mEffectiveTRRMode != nsIRequest::TRR_DISABLED_MODE &&
1228 !((rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR)) &&
1229 !serviceNotReady) {
1230 rv = TrrLookup(rec, aLock);
1233 if (rec->mEffectiveTRRMode == nsIRequest::TRR_DISABLED_MODE ||
1234 (rec->mEffectiveTRRMode == nsIRequest::TRR_FIRST_MODE &&
1235 (rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR || serviceNotReady ||
1236 NS_FAILED(rv)))) {
1237 if (!rec->IsAddrRecord()) {
1238 if (!IsNativeHTTPSEnabled()) {
1239 return NS_ERROR_UNKNOWN_HOST;
1242 if (rec->flags & nsIDNSService::RESOLVE_DISABLE_NATIVE_HTTPS_QUERY) {
1243 return NS_ERROR_UNKNOWN_HOST;
1247 #ifdef DEBUG
1248 // If we use this branch then the mTRRUsed flag should not be set
1249 // Even if we did call TrrLookup above, the fact that it failed sync-ly
1250 // means that we didn't actually succeed in opening the channel.
1251 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
1252 MOZ_ASSERT_IF(addrRec, addrRec->mResolverType == DNSResolverType::Native);
1253 #endif
1255 // We did not lookup via TRR - don't fallback to native if the
1256 // network.trr.display_fallback_warning pref is set and either
1257 // 1. we are in TRR first mode and confirmation failed
1258 // 2. the record has trr_disabled and a heuristic skip reason
1259 if (StaticPrefs::network_trr_display_fallback_warning() &&
1260 rec->mEffectiveTRRMode != nsIRequest::TRR_ONLY_MODE) {
1261 if ((rec->mEffectiveTRRMode == nsIRequest::TRR_FIRST_MODE &&
1262 rec->mTRRSkippedReason == TRRSkippedReason::TRR_NOT_CONFIRMED) ||
1263 (rec->mEffectiveTRRMode == nsIRequest::TRR_DISABLED_MODE &&
1264 rec->mTRRSkippedReason >=
1265 nsITRRSkipReason::TRR_HEURISTIC_TRIPPED_GOOGLE_SAFESEARCH &&
1266 rec->mTRRSkippedReason <=
1267 nsITRRSkipReason::TRR_HEURISTIC_TRIPPED_NRPT)) {
1268 LOG((
1269 "NameLookup: ResolveHostComplete with status NS_ERROR_UNKNOWN_HOST "
1270 "for: %s effectiveTRRmode: "
1271 "%d SkippedReason: %d",
1272 rec->host.get(),
1273 static_cast<nsIRequest::TRRMode>(rec->mEffectiveTRRMode),
1274 static_cast<int32_t>(rec->mTRRSkippedReason)));
1276 mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs =
1277 std::move(rec->mCallbacks);
1278 for (nsResolveHostCallback* c = cbs.getFirst(); c;
1279 c = c->removeAndGetNext()) {
1280 c->OnResolveHostComplete(this, rec, NS_ERROR_UNKNOWN_HOST);
1283 return NS_OK;
1287 rv = NativeLookup(rec, aLock);
1290 return rv;
1293 nsresult nsHostResolver::ConditionallyRefreshRecord(
1294 nsHostRecord* rec, const nsACString& host, const MutexAutoLock& aLock) {
1295 if ((rec->CheckExpiration(TimeStamp::NowLoRes()) != nsHostRecord::EXP_VALID ||
1296 rec->negative) &&
1297 !rec->mResolving && rec->RefreshForNegativeResponse()) {
1298 LOG((" Using %s cache entry for host [%s] but starting async renewal.",
1299 rec->negative ? "negative" : "positive", host.BeginReading()));
1300 NameLookup(rec, aLock);
1302 if (rec->IsAddrRecord() && !rec->negative) {
1303 // negative entries are constantly being refreshed, only
1304 // track positive grace period induced renewals
1305 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_RENEWAL);
1308 return NS_OK;
1311 bool nsHostResolver::GetHostToLookup(nsHostRecord** result) {
1312 bool timedOut = false;
1313 TimeDuration timeout;
1314 TimeStamp epoch, now;
1316 MutexAutoLock lock(mLock);
1318 timeout = (mNumIdleTasks >= MaxResolverThreadsAnyPriority())
1319 ? mShortIdleTimeout
1320 : mLongIdleTimeout;
1321 epoch = TimeStamp::Now();
1323 while (!mShutdown) {
1324 // remove next record from Q; hand over owning reference. Check high, then
1325 // med, then low
1327 #define SET_GET_TTL(var, val) (var)->StoreGetTtl(sGetTtlEnabled && (val))
1329 RefPtr<nsHostRecord> rec = mQueue.Dequeue(true, lock);
1330 if (rec) {
1331 SET_GET_TTL(rec, false);
1332 rec.forget(result);
1333 return true;
1336 if (mActiveAnyThreadCount < MaxResolverThreadsAnyPriority()) {
1337 rec = mQueue.Dequeue(false, lock);
1338 if (rec) {
1339 MOZ_ASSERT(IsMediumPriority(rec->flags) || IsLowPriority(rec->flags));
1340 mActiveAnyThreadCount++;
1341 rec->StoreUsingAnyThread(true);
1342 SET_GET_TTL(rec, true);
1343 rec.forget(result);
1344 return true;
1348 // Determining timeout is racy, so allow one cycle through checking the
1349 // queues before exiting.
1350 if (timedOut) {
1351 break;
1354 // wait for one or more of the following to occur:
1355 // (1) the pending queue has a host record to process
1356 // (2) the shutdown flag has been set
1357 // (3) the thread has been idle for too long
1359 mNumIdleTasks++;
1360 mIdleTaskCV.Wait(timeout);
1361 mNumIdleTasks--;
1363 now = TimeStamp::Now();
1365 if (now - epoch >= timeout) {
1366 timedOut = true;
1367 } else {
1368 // It is possible that CondVar::Wait() was interrupted and returned
1369 // early, in which case we will loop back and re-enter it. In that
1370 // case we want to do so with the new timeout reduced to reflect
1371 // time already spent waiting.
1372 timeout -= now - epoch;
1373 epoch = now;
1377 // tell thread to exit...
1378 return false;
1381 void nsHostResolver::PrepareRecordExpirationAddrRecord(
1382 AddrHostRecord* rec) const {
1383 // NOTE: rec->addr_info_lock is already held by parent
1384 MOZ_ASSERT(((bool)rec->addr_info) != rec->negative);
1385 mLock.AssertCurrentThreadOwns();
1386 if (!rec->addr_info) {
1387 rec->SetExpiration(TimeStamp::NowLoRes(), NEGATIVE_RECORD_LIFETIME, 0);
1388 LOG(("Caching host [%s] negative record for %u seconds.\n", rec->host.get(),
1389 NEGATIVE_RECORD_LIFETIME));
1390 return;
1393 unsigned int lifetime = mDefaultCacheLifetime;
1394 unsigned int grace = mDefaultGracePeriod;
1396 unsigned int ttl = mDefaultCacheLifetime;
1397 if (sGetTtlEnabled || rec->addr_info->IsTRR()) {
1398 if (rec->addr_info && rec->addr_info->TTL() != AddrInfo::NO_TTL_DATA) {
1399 ttl = rec->addr_info->TTL();
1401 lifetime = ttl;
1402 grace = 0;
1405 rec->SetExpiration(TimeStamp::NowLoRes(), lifetime, grace);
1406 LOG(("Caching host [%s] record for %u seconds (grace %d).", rec->host.get(),
1407 lifetime, grace));
1410 static bool different_rrset(AddrInfo* rrset1, AddrInfo* rrset2) {
1411 if (!rrset1 || !rrset2) {
1412 return true;
1415 LOG(("different_rrset %s\n", rrset1->Hostname().get()));
1417 if (rrset1->ResolverType() != rrset2->ResolverType()) {
1418 return true;
1421 if (rrset1->TRRType() != rrset2->TRRType()) {
1422 return true;
1425 if (rrset1->Addresses().Length() != rrset2->Addresses().Length()) {
1426 LOG(("different_rrset true due to length change\n"));
1427 return true;
1430 nsTArray<NetAddr> orderedSet1 = rrset1->Addresses().Clone();
1431 nsTArray<NetAddr> orderedSet2 = rrset2->Addresses().Clone();
1432 orderedSet1.Sort();
1433 orderedSet2.Sort();
1435 bool eq = orderedSet1 == orderedSet2;
1436 if (!eq) {
1437 LOG(("different_rrset true due to content change\n"));
1438 } else {
1439 LOG(("different_rrset false\n"));
1441 return !eq;
1444 void nsHostResolver::AddToEvictionQ(nsHostRecord* rec,
1445 const MutexAutoLock& aLock) {
1446 mQueue.AddToEvictionQ(rec, mMaxCacheEntries, mRecordDB, aLock);
1449 // After a first lookup attempt with TRR in mode 2, we may:
1450 // - If network.trr.retry_on_recoverable_errors is false, retry with native.
1451 // - If network.trr.retry_on_recoverable_errors is true:
1452 // - Retry with native if the first attempt failed because we got NXDOMAIN, an
1453 // unreachable address (TRR_DISABLED_FLAG), or we skipped TRR because
1454 // Confirmation failed.
1455 // - Trigger a "RetryTRR" Confirmation which will start a fresh
1456 // connection for TRR, and then retry the lookup with TRR.
1457 // - If the second attempt failed, fallback to native if
1458 // network.trr.strict_native_fallback is false.
1459 // Returns true if we retried with either TRR or Native.
1460 bool nsHostResolver::MaybeRetryTRRLookup(
1461 AddrHostRecord* aAddrRec, nsresult aFirstAttemptStatus,
1462 TRRSkippedReason aFirstAttemptSkipReason, nsresult aChannelStatus,
1463 const MutexAutoLock& aLock) {
1464 if (NS_FAILED(aFirstAttemptStatus) &&
1465 (aChannelStatus == NS_ERROR_PROXY_UNAUTHORIZED ||
1466 aChannelStatus == NS_ERROR_PROXY_AUTHENTICATION_FAILED) &&
1467 aAddrRec->mEffectiveTRRMode == nsIRequest::TRR_ONLY_MODE) {
1468 LOG(("MaybeRetryTRRLookup retry because of proxy connect failed"));
1469 TRRService::Get()->DontUseTRRThread();
1470 return DoRetryTRR(aAddrRec, aLock);
1473 if (NS_SUCCEEDED(aFirstAttemptStatus) ||
1474 aAddrRec->mEffectiveTRRMode != nsIRequest::TRR_FIRST_MODE ||
1475 aFirstAttemptStatus == NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
1476 return false;
1479 MOZ_ASSERT(!aAddrRec->mResolving);
1480 if (!StaticPrefs::network_trr_retry_on_recoverable_errors()) {
1481 LOG(("nsHostResolver::MaybeRetryTRRLookup retrying with native"));
1482 return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock));
1485 if (IsFailedConfirmationOrNoConnectivity(aFirstAttemptSkipReason) ||
1486 IsNonRecoverableTRRSkipReason(aFirstAttemptSkipReason) ||
1487 IsBlockedTRRRequest(aFirstAttemptSkipReason)) {
1488 LOG(
1489 ("nsHostResolver::MaybeRetryTRRLookup retrying with native in strict "
1490 "mode, skip reason was %d",
1491 static_cast<uint32_t>(aFirstAttemptSkipReason)));
1492 return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock));
1495 if (aAddrRec->mTrrAttempts > 1) {
1496 if (!StaticPrefs::network_trr_strict_native_fallback()) {
1497 LOG(
1498 ("nsHostResolver::MaybeRetryTRRLookup retry failed. Using "
1499 "native."));
1500 return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock));
1503 if (aFirstAttemptSkipReason == TRRSkippedReason::TRR_TIMEOUT &&
1504 StaticPrefs::network_trr_strict_native_fallback_allow_timeouts()) {
1505 LOG(
1506 ("nsHostResolver::MaybeRetryTRRLookup retry timed out. Using "
1507 "native."));
1508 return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock));
1510 LOG(("nsHostResolver::MaybeRetryTRRLookup mTrrAttempts>1, not retrying."));
1511 return false;
1514 LOG(
1515 ("nsHostResolver::MaybeRetryTRRLookup triggering Confirmation and "
1516 "retrying with TRR, skip reason was %d",
1517 static_cast<uint32_t>(aFirstAttemptSkipReason)));
1518 TRRService::Get()->RetryTRRConfirm();
1520 return DoRetryTRR(aAddrRec, aLock);
1523 bool nsHostResolver::DoRetryTRR(AddrHostRecord* aAddrRec,
1524 const mozilla::MutexAutoLock& aLock) {
1526 // Clear out the old query
1527 auto trrQuery = aAddrRec->mTRRQuery.Lock();
1528 trrQuery.ref() = nullptr;
1531 if (NS_SUCCEEDED(TrrLookup(aAddrRec, aLock, nullptr /* pushedTRR */))) {
1532 aAddrRec->NotifyRetryingTrr();
1533 return true;
1536 return false;
1540 // CompleteLookup() checks if the resolving should be redone and if so it
1541 // returns LOOKUP_RESOLVEAGAIN, but only if 'status' is not NS_ERROR_ABORT.
1542 nsHostResolver::LookupStatus nsHostResolver::CompleteLookup(
1543 nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb,
1544 const nsACString& aOriginsuffix, TRRSkippedReason aReason,
1545 mozilla::net::TRR* aTRRRequest) {
1546 MutexAutoLock lock(mLock);
1547 return CompleteLookupLocked(rec, status, aNewRRSet, pb, aOriginsuffix,
1548 aReason, aTRRRequest, lock);
1551 namespace {
1552 class NetAddrIPv6FirstComparator {
1553 public:
1554 static bool Equals(const NetAddr& aLhs, const NetAddr& aRhs) {
1555 return aLhs.raw.family == aRhs.raw.family;
1557 static bool LessThan(const NetAddr& aLhs, const NetAddr& aRhs) {
1558 return aLhs.raw.family > aRhs.raw.family;
1561 } // namespace
1563 nsHostResolver::LookupStatus nsHostResolver::CompleteLookupLocked(
1564 nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb,
1565 const nsACString& aOriginsuffix, TRRSkippedReason aReason,
1566 mozilla::net::TRR* aTRRRequest, const mozilla::MutexAutoLock& aLock) {
1567 MOZ_ASSERT(rec);
1568 MOZ_ASSERT(rec->pb == pb);
1569 MOZ_ASSERT(rec->IsAddrRecord());
1571 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
1572 MOZ_ASSERT(addrRec);
1574 RefPtr<AddrInfo> newRRSet(aNewRRSet);
1575 MOZ_ASSERT(NS_FAILED(status) || newRRSet->Addresses().Length() > 0);
1577 DNSResolverType type =
1578 newRRSet ? newRRSet->ResolverType() : DNSResolverType::Native;
1580 if (NS_FAILED(status)) {
1581 newRRSet = nullptr;
1584 if (addrRec->LoadResolveAgain() && (status != NS_ERROR_ABORT) &&
1585 type == DNSResolverType::Native) {
1586 LOG(("nsHostResolver record %p resolve again due to flushcache\n",
1587 addrRec.get()));
1588 addrRec->StoreResolveAgain(false);
1589 return LOOKUP_RESOLVEAGAIN;
1592 MOZ_ASSERT(addrRec->mResolving);
1593 addrRec->mResolving--;
1594 LOG((
1595 "nsHostResolver::CompleteLookup %s %p %X resolver=%d stillResolving=%d\n",
1596 addrRec->host.get(), aNewRRSet, (unsigned int)status, (int)type,
1597 int(addrRec->mResolving)));
1599 if (type != DNSResolverType::Native) {
1600 if (NS_FAILED(status) && status != NS_ERROR_UNKNOWN_HOST &&
1601 status != NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
1602 // the errors are not failed resolves, that means
1603 // something else failed, consider this as *TRR not used*
1604 // for actually trying to resolve the host
1605 addrRec->mResolverType = DNSResolverType::Native;
1608 if (NS_FAILED(status)) {
1609 if (aReason != TRRSkippedReason::TRR_UNSET) {
1610 addrRec->RecordReason(aReason);
1611 } else {
1612 // Unknown failed reason.
1613 addrRec->RecordReason(TRRSkippedReason::TRR_FAILED);
1615 } else {
1616 addrRec->mTRRSuccess = true;
1617 addrRec->RecordReason(TRRSkippedReason::TRR_OK);
1620 nsresult channelStatus = aTRRRequest->ChannelStatus();
1621 if (MaybeRetryTRRLookup(addrRec, status, aReason, channelStatus, aLock)) {
1622 MOZ_ASSERT(addrRec->mResolving);
1623 return LOOKUP_OK;
1626 if (!addrRec->mTRRSuccess) {
1627 // no TRR success
1628 newRRSet = nullptr;
1631 if (NS_FAILED(status)) {
1632 // This is the error that consumers expect.
1633 status = NS_ERROR_UNKNOWN_HOST;
1635 } else { // native resolve completed
1636 if (addrRec->LoadUsingAnyThread()) {
1637 mActiveAnyThreadCount--;
1638 addrRec->StoreUsingAnyThread(false);
1641 addrRec->mNativeSuccess = static_cast<bool>(newRRSet);
1642 if (addrRec->mNativeSuccess) {
1643 addrRec->mNativeDuration = TimeStamp::Now() - addrRec->mNativeStart;
1647 addrRec->OnCompleteLookup();
1649 // update record fields. We might have a addrRec->addr_info already if a
1650 // previous lookup result expired and we're reresolving it or we get
1651 // a late second TRR response.
1652 if (!mShutdown) {
1653 MutexAutoLock lock(addrRec->addr_info_lock);
1654 RefPtr<AddrInfo> old_addr_info;
1655 if (different_rrset(addrRec->addr_info, newRRSet)) {
1656 LOG(("nsHostResolver record %p new gencnt\n", addrRec.get()));
1657 old_addr_info = addrRec->addr_info;
1658 addrRec->addr_info = std::move(newRRSet);
1659 addrRec->addr_info_gencnt++;
1660 } else {
1661 if (addrRec->addr_info && newRRSet) {
1662 auto builder = addrRec->addr_info->Build();
1663 builder.SetTTL(newRRSet->TTL());
1664 // Update trr timings
1665 builder.SetTrrFetchDuration(newRRSet->GetTrrFetchDuration());
1666 builder.SetTrrFetchDurationNetworkOnly(
1667 newRRSet->GetTrrFetchDurationNetworkOnly());
1669 addrRec->addr_info = builder.Finish();
1671 old_addr_info = std::move(newRRSet);
1673 addrRec->negative = !addrRec->addr_info;
1675 if (addrRec->addr_info && StaticPrefs::network_dns_preferIPv6() &&
1676 addrRec->addr_info->Addresses().Length() > 1 &&
1677 addrRec->addr_info->Addresses()[0].IsIPAddrV4()) {
1678 // Sort IPv6 addresses first.
1679 auto builder = addrRec->addr_info->Build();
1680 builder.SortAddresses(NetAddrIPv6FirstComparator());
1681 addrRec->addr_info = builder.Finish();
1684 PrepareRecordExpirationAddrRecord(addrRec);
1687 if (LOG_ENABLED()) {
1688 MutexAutoLock lock(addrRec->addr_info_lock);
1689 if (addrRec->addr_info) {
1690 for (const auto& elem : addrRec->addr_info->Addresses()) {
1691 char buf[128];
1692 elem.ToStringBuffer(buf, sizeof(buf));
1693 LOG(("CompleteLookup: %s has %s\n", addrRec->host.get(), buf));
1695 } else {
1696 LOG(("CompleteLookup: %s has NO address\n", addrRec->host.get()));
1700 PROFILER_MARKER("nsHostResolver::CompleteLookupLocked", NETWORK, {},
1701 HostResolverMarker, addrRec->host, addrRec->originSuffix,
1702 addrRec->type, addrRec->flags);
1704 // get the list of pending callbacks for this lookup, and notify
1705 // them that the lookup is complete.
1706 mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs =
1707 std::move(rec->mCallbacks);
1709 LOG(("nsHostResolver record %p calling back dns users status:%X\n",
1710 addrRec.get(), int(status)));
1712 for (nsResolveHostCallback* c = cbs.getFirst(); c;
1713 c = c->removeAndGetNext()) {
1714 c->OnResolveHostComplete(this, rec, status);
1717 OnResolveComplete(rec, aLock);
1719 #ifdef DNSQUERY_AVAILABLE
1720 // Unless the result is from TRR, resolve again to get TTL
1721 bool hasNativeResult = false;
1723 MutexAutoLock lock(addrRec->addr_info_lock);
1724 if (addrRec->addr_info && !addrRec->addr_info->IsTRR()) {
1725 hasNativeResult = true;
1728 if (hasNativeResult && !mShutdown && !addrRec->LoadGetTtl() &&
1729 !rec->mResolving && sGetTtlEnabled) {
1730 LOG(("Issuing second async lookup for TTL for host [%s].",
1731 addrRec->host.get()));
1732 addrRec->flags =
1733 (addrRec->flags & ~nsIDNSService::RESOLVE_PRIORITY_MEDIUM) |
1734 nsIDNSService::RESOLVE_PRIORITY_LOW;
1735 DebugOnly<nsresult> rv = NativeLookup(rec, aLock);
1736 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1737 "Could not issue second async lookup for TTL.");
1739 #endif
1740 return LOOKUP_OK;
1743 nsHostResolver::LookupStatus nsHostResolver::CompleteLookupByType(
1744 nsHostRecord* rec, nsresult status,
1745 mozilla::net::TypeRecordResultType& aResult, TRRSkippedReason aReason,
1746 uint32_t aTtl, bool pb) {
1747 MutexAutoLock lock(mLock);
1748 return CompleteLookupByTypeLocked(rec, status, aResult, aReason, aTtl, pb,
1749 lock);
1752 nsHostResolver::LookupStatus nsHostResolver::CompleteLookupByTypeLocked(
1753 nsHostRecord* rec, nsresult status,
1754 mozilla::net::TypeRecordResultType& aResult, TRRSkippedReason aReason,
1755 uint32_t aTtl, bool pb, const mozilla::MutexAutoLock& aLock) {
1756 MOZ_ASSERT(rec);
1757 MOZ_ASSERT(rec->pb == pb);
1758 MOZ_ASSERT(!rec->IsAddrRecord());
1760 if (rec->LoadNative()) {
1761 // If this was resolved using the native resolver
1762 // we also need to update the global count.
1763 if (rec->LoadUsingAnyThread()) {
1764 mActiveAnyThreadCount--;
1765 rec->StoreUsingAnyThread(false);
1769 RefPtr<TypeHostRecord> typeRec = do_QueryObject(rec);
1770 MOZ_ASSERT(typeRec);
1772 MOZ_ASSERT(typeRec->mResolving);
1773 typeRec->mResolving--;
1775 if (NS_FAILED(status)) {
1776 LOG(("nsHostResolver::CompleteLookupByType record %p [%s] status %x\n",
1777 typeRec.get(), typeRec->host.get(), (unsigned int)status));
1778 typeRec->SetExpiration(
1779 TimeStamp::NowLoRes(),
1780 StaticPrefs::network_dns_negative_ttl_for_type_record(), 0);
1781 MOZ_ASSERT(aResult.is<TypeRecordEmpty>());
1782 status = NS_ERROR_UNKNOWN_HOST;
1783 typeRec->negative = true;
1784 if (aReason != TRRSkippedReason::TRR_UNSET) {
1785 typeRec->RecordReason(aReason);
1786 } else {
1787 // Unknown failed reason.
1788 typeRec->RecordReason(TRRSkippedReason::TRR_FAILED);
1790 } else {
1791 size_t recordCount = 0;
1792 if (aResult.is<TypeRecordTxt>()) {
1793 recordCount = aResult.as<TypeRecordTxt>().Length();
1794 } else if (aResult.is<TypeRecordHTTPSSVC>()) {
1795 recordCount = aResult.as<TypeRecordHTTPSSVC>().Length();
1797 LOG(
1798 ("nsHostResolver::CompleteLookupByType record %p [%s], number of "
1799 "records %zu\n",
1800 typeRec.get(), typeRec->host.get(), recordCount));
1801 MutexAutoLock typeLock(typeRec->mResultsLock);
1802 typeRec->mResults = aResult;
1803 typeRec->SetExpiration(TimeStamp::NowLoRes(), aTtl, mDefaultGracePeriod);
1804 typeRec->negative = false;
1805 typeRec->mTRRSuccess = !rec->LoadNative();
1806 typeRec->mNativeSuccess = rec->LoadNative();
1807 MOZ_ASSERT(aReason != TRRSkippedReason::TRR_UNSET);
1808 typeRec->RecordReason(aReason);
1811 PROFILER_MARKER("nsHostResolver::CompleteLookupByTypeLocked", NETWORK, {},
1812 HostResolverMarker, typeRec->host, typeRec->originSuffix,
1813 typeRec->type, typeRec->flags);
1815 mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs =
1816 std::move(typeRec->mCallbacks);
1818 LOG(
1819 ("nsHostResolver::CompleteLookupByType record %p calling back dns "
1820 "users\n",
1821 typeRec.get()));
1823 for (nsResolveHostCallback* c = cbs.getFirst(); c;
1824 c = c->removeAndGetNext()) {
1825 c->OnResolveHostComplete(this, rec, status);
1828 OnResolveComplete(rec, aLock);
1830 return LOOKUP_OK;
1833 void nsHostResolver::OnResolveComplete(nsHostRecord* aRec,
1834 const mozilla::MutexAutoLock& aLock) {
1835 if (!aRec->mResolving && !mShutdown) {
1837 auto trrQuery = aRec->mTRRQuery.Lock();
1838 if (trrQuery.ref()) {
1839 aRec->mTrrDuration = trrQuery.ref()->Duration();
1841 trrQuery.ref() = nullptr;
1843 aRec->ResolveComplete();
1845 AddToEvictionQ(aRec, aLock);
1849 void nsHostResolver::CancelAsyncRequest(
1850 const nsACString& host, const nsACString& aTrrServer, uint16_t aType,
1851 const OriginAttributes& aOriginAttributes, nsIDNSService::DNSFlags flags,
1852 uint16_t af, nsIDNSListener* aListener, nsresult status)
1855 MutexAutoLock lock(mLock);
1857 nsAutoCString originSuffix;
1858 aOriginAttributes.CreateSuffix(originSuffix);
1860 // Lookup the host record associated with host, flags & address family
1862 nsHostKey key(host, aTrrServer, aType, flags, af,
1863 (aOriginAttributes.IsPrivateBrowsing()), originSuffix);
1864 RefPtr<nsHostRecord> rec = mRecordDB.Get(key);
1865 if (!rec) {
1866 return;
1869 for (RefPtr<nsResolveHostCallback> c : rec->mCallbacks) {
1870 if (c->EqualsAsyncListener(aListener)) {
1871 c->remove();
1872 c->OnResolveHostComplete(this, rec.get(), status);
1873 break;
1877 // If there are no more callbacks, remove the hash table entry
1878 if (rec->mCallbacks.isEmpty()) {
1879 mRecordDB.Remove(*static_cast<nsHostKey*>(rec.get()));
1880 // If record is on a Queue, remove it
1881 mQueue.MaybeRemoveFromQ(rec, lock);
1885 size_t nsHostResolver::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const {
1886 MutexAutoLock lock(mLock);
1888 size_t n = mallocSizeOf(this);
1890 n += mRecordDB.ShallowSizeOfExcludingThis(mallocSizeOf);
1891 for (const auto& entry : mRecordDB.Values()) {
1892 n += entry->SizeOfIncludingThis(mallocSizeOf);
1895 // The following fields aren't measured.
1896 // - mHighQ, mMediumQ, mLowQ, mEvictionQ, because they just point to
1897 // nsHostRecords that also pointed to by entries |mRecordDB|, and
1898 // measured when |mRecordDB| is measured.
1900 return n;
1903 void nsHostResolver::ThreadFunc() {
1904 LOG(("DNS lookup thread - starting execution.\n"));
1906 RefPtr<nsHostRecord> rec;
1907 RefPtr<AddrInfo> ai;
1909 do {
1910 if (!rec) {
1911 RefPtr<nsHostRecord> tmpRec;
1912 if (!GetHostToLookup(getter_AddRefs(tmpRec))) {
1913 break; // thread shutdown signal
1915 // GetHostToLookup() returns an owning reference
1916 MOZ_ASSERT(tmpRec);
1917 rec.swap(tmpRec);
1920 LOG1(("DNS lookup thread - Calling getaddrinfo for host [%s].\n",
1921 rec->host.get()));
1923 TimeStamp startTime = TimeStamp::Now();
1924 bool getTtl = rec->LoadGetTtl();
1925 TimeDuration inQueue = startTime - rec->mNativeStart;
1926 uint32_t ms = static_cast<uint32_t>(inQueue.ToMilliseconds());
1927 Telemetry::Accumulate(Telemetry::DNS_NATIVE_QUEUING, ms);
1929 if (!rec->IsAddrRecord()) {
1930 LOG(("byType on DNS thread"));
1931 TypeRecordResultType result = AsVariant(mozilla::Nothing());
1932 uint32_t ttl = UINT32_MAX;
1933 nsresult status = ResolveHTTPSRecord(rec->host, rec->flags, result, ttl);
1934 mozilla::glean::networking::dns_native_count
1935 .EnumGet(rec->pb
1936 ? glean::networking::DnsNativeCountLabel::eHttpsPrivate
1937 : glean::networking::DnsNativeCountLabel::eHttpsRegular)
1938 .Add(1);
1939 CompleteLookupByType(rec, status, result, rec->mTRRSkippedReason, ttl,
1940 rec->pb);
1941 rec = nullptr;
1942 continue;
1945 nsresult status =
1946 GetAddrInfo(rec->host, rec->af, rec->flags, getter_AddRefs(ai), getTtl);
1948 mozilla::glean::networking::dns_native_count
1949 .EnumGet(rec->pb ? glean::networking::DnsNativeCountLabel::ePrivate
1950 : glean::networking::DnsNativeCountLabel::eRegular)
1951 .Add(1);
1953 if (RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec)) {
1954 // obtain lock to check shutdown and manage inter-module telemetry
1955 MutexAutoLock lock(mLock);
1957 if (!mShutdown) {
1958 TimeDuration elapsed = TimeStamp::Now() - startTime;
1959 if (NS_SUCCEEDED(status)) {
1960 if (!addrRec->addr_info_gencnt) {
1961 // Time for initial lookup.
1962 glean::networking::dns_lookup_time.AccumulateRawDuration(elapsed);
1963 } else if (!getTtl) {
1964 // Time for renewal; categorized by expiration strategy.
1965 glean::networking::dns_renewal_time.AccumulateRawDuration(elapsed);
1966 } else {
1967 // Time to get TTL; categorized by expiration strategy.
1968 glean::networking::dns_renewal_time_for_ttl.AccumulateRawDuration(
1969 elapsed);
1971 } else {
1972 glean::networking::dns_failed_lookup_time.AccumulateRawDuration(
1973 elapsed);
1978 LOG1(("DNS lookup thread - lookup completed for host [%s]: %s.\n",
1979 rec->host.get(), ai ? "success" : "failure: unknown host"));
1981 if (LOOKUP_RESOLVEAGAIN ==
1982 CompleteLookup(rec, status, ai, rec->pb, rec->originSuffix,
1983 rec->mTRRSkippedReason, nullptr)) {
1984 // leave 'rec' assigned and loop to make a renewed host resolve
1985 LOG(("DNS lookup thread - Re-resolving host [%s].\n", rec->host.get()));
1986 } else {
1987 rec = nullptr;
1989 } while (true);
1991 MutexAutoLock lock(mLock);
1992 mActiveTaskCount--;
1993 LOG(("DNS lookup thread - queue empty, task finished.\n"));
1996 void nsHostResolver::SetCacheLimits(uint32_t aMaxCacheEntries,
1997 uint32_t aDefaultCacheEntryLifetime,
1998 uint32_t aDefaultGracePeriod) {
1999 MutexAutoLock lock(mLock);
2000 mMaxCacheEntries = aMaxCacheEntries;
2001 mDefaultCacheLifetime = aDefaultCacheEntryLifetime;
2002 mDefaultGracePeriod = aDefaultGracePeriod;
2005 nsresult nsHostResolver::Create(uint32_t maxCacheEntries,
2006 uint32_t defaultCacheEntryLifetime,
2007 uint32_t defaultGracePeriod,
2008 nsHostResolver** result) {
2009 RefPtr<nsHostResolver> res = new nsHostResolver(
2010 maxCacheEntries, defaultCacheEntryLifetime, defaultGracePeriod);
2012 nsresult rv = res->Init();
2013 if (NS_FAILED(rv)) {
2014 return rv;
2017 res.forget(result);
2018 return NS_OK;
2021 void nsHostResolver::GetDNSCacheEntries(nsTArray<DNSCacheEntries>* args) {
2022 MutexAutoLock lock(mLock);
2023 for (const auto& recordEntry : mRecordDB) {
2024 // We don't pay attention to address literals, only resolved domains.
2025 // Also require a host.
2026 nsHostRecord* rec = recordEntry.GetWeak();
2027 MOZ_ASSERT(rec, "rec should never be null here!");
2029 if (!rec) {
2030 continue;
2033 // For now we only show A/AAAA records.
2034 if (!rec->IsAddrRecord()) {
2035 continue;
2038 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
2039 MOZ_ASSERT(addrRec);
2040 if (!addrRec || !addrRec->addr_info) {
2041 continue;
2044 DNSCacheEntries info;
2045 info.hostname = rec->host;
2046 info.family = rec->af;
2047 info.expiration =
2048 (int64_t)(rec->mValidEnd - TimeStamp::NowLoRes()).ToSeconds();
2049 if (info.expiration <= 0) {
2050 // We only need valid DNS cache entries
2051 continue;
2055 MutexAutoLock lock(addrRec->addr_info_lock);
2056 for (const auto& addr : addrRec->addr_info->Addresses()) {
2057 char buf[kIPv6CStrBufSize];
2058 if (addr.ToStringBuffer(buf, sizeof(buf))) {
2059 info.hostaddr.AppendElement(buf);
2062 info.TRR = addrRec->addr_info->IsTRR();
2065 info.originAttributesSuffix = recordEntry.GetKey().originSuffix;
2066 info.flags = nsPrintfCString("%u|0x%x|%u|%d|%s", rec->type, rec->flags,
2067 rec->af, rec->pb, rec->mTrrServer.get());
2069 args->AppendElement(std::move(info));
2073 #undef LOG
2074 #undef LOG_ENABLED