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>
17 #include "nsHostResolver.h"
19 #include "nsISupports.h"
20 #include "nsISupportsUtils.h"
21 #include "nsIThreadManager.h"
22 #include "nsComponentManagerUtils.h"
23 #include "nsNetUtil.h"
24 #include "nsPrintfCString.h"
25 #include "nsXPCOMCIDInternal.h"
29 #include "mozilla/Logging.h"
30 #include "PLDHashTable.h"
31 #include "nsQueryObject.h"
32 #include "nsURLHelper.h"
33 #include "nsThreadUtils.h"
34 #include "nsThreadPool.h"
35 #include "GetAddrInfo.h"
38 #include "TRRService.h"
40 #include "mozilla/Atomics.h"
41 #include "mozilla/glean/GleanMetrics.h"
42 #include "mozilla/HashFunctions.h"
43 #include "mozilla/TimeStamp.h"
44 #include "mozilla/Telemetry.h"
45 #include "mozilla/DebugOnly.h"
46 #include "mozilla/Preferences.h"
47 #include "mozilla/StaticPrefs_network.h"
48 // Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
49 #include "DNSLogging.h"
52 # include "mozilla/WindowsVersion.h"
55 #ifdef MOZ_WIDGET_ANDROID
56 # include "mozilla/jni/Utils.h"
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
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
,
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
);
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
138 virtual ~DnsThreadListener() = default;
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
));
153 DnsThreadListener::OnThreadShuttingDown() {
155 #if defined(HAVE_RES_NINIT)
156 if (_res
.options
& RES_INIT
) {
158 memset(&_res
, 0, sizeof(_res
));
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!");
182 if (!strcmp(aPref
, kPrefGetTtl
)) {
183 #ifdef DNSQUERY_AVAILABLE
184 sGetTtlEnabled
= Preferences::GetBool(kPrefGetTtl
);
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));
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
));
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
;
255 // We clamp down the idle time between 0 and one hour.
257 mozilla::clamped
<uint32_t>(poolTimeoutSecs
* 1000, 0, 3600 * 1000);
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;
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));
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
));
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
) {
303 if (rec
->IsAddrRecord()) {
304 CompleteLookup(rec
, NS_ERROR_ABORT
, nullptr, rec
->pb
, rec
->originSuffix
,
305 rec
->mTRRSkippedReason
, nullptr);
307 mozilla::net::TypeRecordResultType
empty(Nothing
{});
308 CompleteLookupByType(rec
, NS_ERROR_ABORT
, empty
, rec
->mTRRSkippedReason
,
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
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
);
339 if (addrRec
->RemoveOrRefresh(aTrrToo
)) {
340 mQueue
.MaybeRemoveFromQ(record
, lock
);
341 LOG(("Removing (%s) Addr record from mRecordDB", record
->host
.get()));
344 } else if (aTrrToo
) {
345 // remove by type records
346 LOG(("Removing (%s) type record from mRecordDB", record
->host
.get()));
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
,
366 MutexAutoLock
lock(mLock
);
371 mIdleTaskCV
.NotifyAll();
375 [&](nsHostRecord
* aRec
) {
376 mLock
.AssertCurrentThreadOwns();
377 if (aRec
->IsAddrRecord()) {
378 CompleteLookupLocked(aRec
, NS_ERROR_ABORT
, nullptr, aRec
->pb
,
379 aRec
->originSuffix
, aRec
->mTRRSkippedReason
,
382 mozilla::net::TypeRecordResultType
empty(Nothing
{});
383 CompleteLookupByTypeLocked(aRec
, NS_ERROR_ABORT
, empty
,
384 aRec
->mTRRSkippedReason
, 0, aRec
->pb
,
390 for (const auto& data
: mRecordDB
.Values()) {
393 // empty host database
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
);
422 return NS_ERROR_FAILURE
;
426 if (rec
->mResolving
) {
427 return NS_ERROR_FAILURE
;
430 *result
= rec
.forget().take();
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
) {
444 MOZ_ASSERT(IS_ADDR_TYPE(key
.type
));
446 *aRv
= NS_ERROR_FAILURE
;
447 RefPtr
<nsHostRecord
> rec
= InitRecord(key
);
449 nsTArray
<NetAddr
> addresses
;
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;
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
) {
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
;
487 CreateAndResolveMockHTTPSRecord(rec
->host
, rec
->flags
, result
, ttl
);
492 RefPtr
<TypeHostRecord
> typeRec
= do_QueryObject(rec
);
493 typeRec
->mResults
= result
;
494 typeRec
->negative
= false;
499 bool nsHostResolver::IsNativeHTTPSEnabled() {
500 if (!StaticPrefs::network_dns_native_https_query()) {
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.
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
);
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 &&
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
)) {
588 RefPtr
<nsHostRecord
> result
= InitLoopbackRecord(key
, &rv
);
589 if (NS_WARN_IF(NS_FAILED(rv
))) {
593 aCallback
->OnResolveHostComplete(this, result
, 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
);
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
) {
638 (" Lookup queue full: dropping %s priority request for "
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
) {
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
663 LOG((" No usable record in cache for host [%s] type %d.", host
.get(),
666 if (flags
& RES_REFRESH_CACHE
) {
670 // Add callback to the list of pending callbacks.
671 rec
->mCallbacks
.insertBack(callback
);
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()) {
682 (" DNS lookup for host [%s] blocking "
683 "pending 'getaddrinfo' or trr query: "
685 host
.get(), callback
.get()));
690 (" Host [%s] is being resolved. Appending callback "
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
);
708 ConditionallyCreateThread(rec
);
709 } else if (IsMediumPriority(flags
) && IsLowPriority(rec
->flags
)) {
710 // Move from low to med.
711 mQueue
.MoveToAnotherPendingQ(rec
, flags
, lock
);
713 mIdleTaskCV
.Notify();
718 if (result
&& callback
->isInList()) {
724 callback
->OnResolveHostComplete(this, result
, status
);
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
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
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
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
)) {
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
854 (" No AF_INET6 in AF_UNSPEC entry: "
855 "host [%s] unknown host.",
856 nsPromiseFlatCString(aHost
).get()));
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
);
886 // walk list looking for |callback|... we cannot assume
887 // that it will be there!
889 for (nsResolveHostCallback
* c
: entry
->mCallbacks
) {
899 // complete callback with the given status code; this would only be done if
900 // the record was in the process of being resolved.
902 callback
->OnResolveHostComplete(this, rec
, status
);
906 nsresult
nsHostResolver::ConditionallyCreateThread(nsHostRecord
* rec
) {
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
);
917 mResolverThreads
->Dispatch(event
, nsIEventTarget::DISPATCH_NORMAL
);
922 LOG((" Unable to find a thread for looking up host [%s].\n",
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
);
952 // We always try custom resolvers.
953 if (!aRec
->mTrrServer
.IsEmpty()) {
957 nsIRequest::TRRMode reqMode
= aRec
->mEffectiveTRRMode
;
958 if (TRRService::Get()->Enabled(reqMode
)) {
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
);
969 auto hasConnectivity
= [this]() -> bool {
970 mLock
.AssertCurrentThreadOwns();
974 nsINetworkConnectivityService::ConnectivityState ipv4
= mNCS
->GetIPv4();
975 nsINetworkConnectivityService::ConnectivityState ipv6
= mNCS
->GetIPv6();
977 if (ipv4
== nsINetworkConnectivityService::OK
||
978 ipv6
== nsINetworkConnectivityService::OK
) {
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.
992 if (!hasConnectivity()) {
993 aRec
->RecordReason(TRRSkippedReason::TRR_NO_CONNECTIVITY
);
997 bool isConfirmed
= TRRService::Get()->IsConfirmed();
999 aRec
->RecordReason(TRRSkippedReason::TRR_NOT_CONFIRMED
);
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
);
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
);
1045 auto lock
= rec
->mTRRQuery
.Lock();
1046 MOZ_ASSERT(!lock
.ref(), "TRR already in progress");
1051 rec
->mTrrAttempts
++;
1052 rec
->StoreNative(false);
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);
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()));
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
);
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
;
1121 if (!aRec
->mTrrServer
.IsEmpty()) {
1122 aRec
->mEffectiveTRRMode
= nsIRequest::TRR_ONLY_MODE
;
1126 if (TRRService::Get()->IsExcludedFromTRR(aRec
->host
)) {
1127 aRec
->RecordReason(TRRSkippedReason::TRR_EXCLUDED
);
1128 aRec
->mEffectiveTRRMode
= nsIRequest::TRR_DISABLED_MODE
;
1132 if (TRRService::Get()->ParentalControlEnabled()) {
1133 aRec
->RecordReason(TRRSkippedReason::TRR_PARENTAL_CONTROL
);
1134 aRec
->mEffectiveTRRMode
= nsIRequest::TRR_DISABLED_MODE
;
1138 if (resolverMode
== nsIDNSService::MODE_TRROFF
) {
1139 aRec
->RecordReason(TRRSkippedReason::TRR_OFF_EXPLICIT
);
1140 aRec
->mEffectiveTRRMode
= nsIRequest::TRR_DISABLED_MODE
;
1144 if (requestMode
== nsIRequest::TRR_DISABLED_MODE
) {
1145 aRec
->RecordReason(TRRSkippedReason::TRR_REQ_MODE_DISABLED
);
1146 aRec
->mEffectiveTRRMode
= nsIRequest::TRR_DISABLED_MODE
;
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
;
1162 aRec
->RecordReason(TRRSkippedReason::TRR_MODE_NOT_ENABLED
);
1163 aRec
->mEffectiveTRRMode
= nsIRequest::TRR_DISABLED_MODE
;
1167 if (requestMode
== nsIRequest::TRR_DEFAULT_MODE
&&
1168 resolverMode
== nsIDNSService::MODE_TRRFIRST
) {
1169 aRec
->mEffectiveTRRMode
= nsIRequest::TRR_FIRST_MODE
;
1173 if (requestMode
== nsIRequest::TRR_DEFAULT_MODE
&&
1174 resolverMode
== nsIDNSService::MODE_TRRONLY
) {
1175 aRec
->mEffectiveTRRMode
= nsIRequest::TRR_ONLY_MODE
;
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()));
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.
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
)) &&
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
||
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
;
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
);
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
)) {
1269 "NameLookup: ResolveHostComplete with status NS_ERROR_UNKNOWN_HOST "
1270 "for: %s effectiveTRRmode: "
1271 "%d SkippedReason: %d",
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
);
1287 rv
= NativeLookup(rec
, aLock
);
1293 nsresult
nsHostResolver::ConditionallyRefreshRecord(
1294 nsHostRecord
* rec
, const nsACString
& host
, const MutexAutoLock
& aLock
) {
1295 if ((rec
->CheckExpiration(TimeStamp::NowLoRes()) != nsHostRecord::EXP_VALID
||
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
);
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())
1321 epoch
= TimeStamp::Now();
1323 while (!mShutdown
) {
1324 // remove next record from Q; hand over owning reference. Check high, then
1327 #define SET_GET_TTL(var, val) (var)->StoreGetTtl(sGetTtlEnabled && (val))
1329 RefPtr
<nsHostRecord
> rec
= mQueue
.Dequeue(true, lock
);
1331 SET_GET_TTL(rec
, false);
1336 if (mActiveAnyThreadCount
< MaxResolverThreadsAnyPriority()) {
1337 rec
= mQueue
.Dequeue(false, lock
);
1339 MOZ_ASSERT(IsMediumPriority(rec
->flags
) || IsLowPriority(rec
->flags
));
1340 mActiveAnyThreadCount
++;
1341 rec
->StoreUsingAnyThread(true);
1342 SET_GET_TTL(rec
, true);
1348 // Determining timeout is racy, so allow one cycle through checking the
1349 // queues before exiting.
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
1360 mIdleTaskCV
.Wait(timeout
);
1363 now
= TimeStamp::Now();
1365 if (now
- epoch
>= timeout
) {
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
;
1377 // tell thread to exit...
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
));
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();
1405 rec
->SetExpiration(TimeStamp::NowLoRes(), lifetime
, grace
);
1406 LOG(("Caching host [%s] record for %u seconds (grace %d).", rec
->host
.get(),
1410 static bool different_rrset(AddrInfo
* rrset1
, AddrInfo
* rrset2
) {
1411 if (!rrset1
|| !rrset2
) {
1415 LOG(("different_rrset %s\n", rrset1
->Hostname().get()));
1417 if (rrset1
->ResolverType() != rrset2
->ResolverType()) {
1421 if (rrset1
->TRRType() != rrset2
->TRRType()) {
1425 if (rrset1
->Addresses().Length() != rrset2
->Addresses().Length()) {
1426 LOG(("different_rrset true due to length change\n"));
1430 nsTArray
<NetAddr
> orderedSet1
= rrset1
->Addresses().Clone();
1431 nsTArray
<NetAddr
> orderedSet2
= rrset2
->Addresses().Clone();
1435 bool eq
= orderedSet1
== orderedSet2
;
1437 LOG(("different_rrset true due to content change\n"));
1439 LOG(("different_rrset false\n"));
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
) {
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
)) {
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()) {
1498 ("nsHostResolver::MaybeRetryTRRLookup retry failed. Using "
1500 return NS_SUCCEEDED(NativeLookup(aAddrRec
, aLock
));
1503 if (aFirstAttemptSkipReason
== TRRSkippedReason::TRR_TIMEOUT
&&
1504 StaticPrefs::network_trr_strict_native_fallback_allow_timeouts()) {
1506 ("nsHostResolver::MaybeRetryTRRLookup retry timed out. Using "
1508 return NS_SUCCEEDED(NativeLookup(aAddrRec
, aLock
));
1510 LOG(("nsHostResolver::MaybeRetryTRRLookup mTrrAttempts>1, not retrying."));
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();
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
);
1552 class NetAddrIPv6FirstComparator
{
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
;
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
) {
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
)) {
1584 if (addrRec
->LoadResolveAgain() && (status
!= NS_ERROR_ABORT
) &&
1585 type
== DNSResolverType::Native
) {
1586 LOG(("nsHostResolver record %p resolve again due to flushcache\n",
1588 addrRec
->StoreResolveAgain(false);
1589 return LOOKUP_RESOLVEAGAIN
;
1592 MOZ_ASSERT(addrRec
->mResolving
);
1593 addrRec
->mResolving
--;
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
);
1612 // Unknown failed reason.
1613 addrRec
->RecordReason(TRRSkippedReason::TRR_FAILED
);
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
);
1626 if (!addrRec
->mTRRSuccess
) {
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.
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
++;
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()) {
1692 elem
.ToStringBuffer(buf
, sizeof(buf
));
1693 LOG(("CompleteLookup: %s has %s\n", addrRec
->host
.get(), buf
));
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()));
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.");
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
,
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
) {
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
);
1787 // Unknown failed reason.
1788 typeRec
->RecordReason(TRRSkippedReason::TRR_FAILED
);
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();
1798 ("nsHostResolver::CompleteLookupByType record %p [%s], number of "
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
);
1819 ("nsHostResolver::CompleteLookupByType record %p calling back dns "
1823 for (nsResolveHostCallback
* c
= cbs
.getFirst(); c
;
1824 c
= c
->removeAndGetNext()) {
1825 c
->OnResolveHostComplete(this, rec
, status
);
1828 OnResolveComplete(rec
, aLock
);
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
);
1869 for (RefPtr
<nsResolveHostCallback
> c
: rec
->mCallbacks
) {
1870 if (c
->EqualsAsyncListener(aListener
)) {
1872 c
->OnResolveHostComplete(this, rec
.get(), status
);
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.
1903 void nsHostResolver::ThreadFunc() {
1904 LOG(("DNS lookup thread - starting execution.\n"));
1906 RefPtr
<nsHostRecord
> rec
;
1907 RefPtr
<AddrInfo
> ai
;
1911 RefPtr
<nsHostRecord
> tmpRec
;
1912 if (!GetHostToLookup(getter_AddRefs(tmpRec
))) {
1913 break; // thread shutdown signal
1915 // GetHostToLookup() returns an owning reference
1920 LOG1(("DNS lookup thread - Calling getaddrinfo for host [%s].\n",
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
1936 ? glean::networking::DnsNativeCountLabel::eHttpsPrivate
1937 : glean::networking::DnsNativeCountLabel::eHttpsRegular
)
1939 CompleteLookupByType(rec
, status
, result
, rec
->mTRRSkippedReason
, ttl
,
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
)
1953 if (RefPtr
<AddrHostRecord
> addrRec
= do_QueryObject(rec
)) {
1954 // obtain lock to check shutdown and manage inter-module telemetry
1955 MutexAutoLock
lock(mLock
);
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
);
1967 // Time to get TTL; categorized by expiration strategy.
1968 glean::networking::dns_renewal_time_for_ttl
.AccumulateRawDuration(
1972 glean::networking::dns_failed_lookup_time
.AccumulateRawDuration(
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()));
1991 MutexAutoLock
lock(mLock
);
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
)) {
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!");
2033 // For now we only show A/AAAA records.
2034 if (!rec
->IsAddrRecord()) {
2038 RefPtr
<AddrHostRecord
> addrRec
= do_QueryObject(rec
);
2039 MOZ_ASSERT(addrRec
);
2040 if (!addrRec
|| !addrRec
->addr_info
) {
2044 DNSCacheEntries info
;
2045 info
.hostname
= rec
->host
;
2046 info
.family
= rec
->af
;
2048 (int64_t)(rec
->mValidEnd
- TimeStamp::NowLoRes()).ToSeconds();
2049 if (info
.expiration
<= 0) {
2050 // We only need valid DNS cache entries
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
));