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 "nsHostRecord.h"
8 // Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
9 #include "DNSLogging.h"
10 #include "mozilla/StaticPrefs_network.h"
11 #include "mozilla/Telemetry.h"
12 #include "TRRService.h"
14 //----------------------------------------------------------------------------
15 // this macro filters out any flags that are not used when constructing the
16 // host key. the significant flags are those that would affect the resulting
17 // host record (i.e., the flags that are passed down to PR_GetAddrInfoByName).
18 #define RES_KEY_FLAGS(_f) \
20 (nsHostResolver::RES_CANON_NAME | nsHostResolver::RES_DISABLE_TRR | \
21 nsIDNSService::RESOLVE_TRR_MODE_MASK | nsHostResolver::RES_IP_HINT))
23 #define IS_ADDR_TYPE(_type) ((_type) == nsIDNSService::RESOLVE_TYPE_DEFAULT)
24 #define IS_OTHER_TYPE(_type) ((_type) != nsIDNSService::RESOLVE_TYPE_DEFAULT)
26 //----------------------------------------------------------------------------
28 using namespace mozilla
;
29 using namespace mozilla::net
;
31 nsHostKey::nsHostKey(const nsACString
& aHost
, const nsACString
& aTrrServer
,
32 uint16_t aType
, nsIDNSService::DNSFlags aFlags
,
33 uint16_t aAf
, bool aPb
, const nsACString
& aOriginsuffix
)
35 mTrrServer(aTrrServer
),
40 originSuffix(aOriginsuffix
) {}
42 bool nsHostKey::operator==(const nsHostKey
& other
) const {
43 return host
== other
.host
&& mTrrServer
== other
.mTrrServer
&&
45 RES_KEY_FLAGS(flags
) == RES_KEY_FLAGS(other
.flags
) && af
== other
.af
&&
46 originSuffix
== other
.originSuffix
;
49 PLDHashNumber
nsHostKey::Hash() const {
50 return AddToHash(HashString(host
.get()), HashString(mTrrServer
.get()), type
,
51 RES_KEY_FLAGS(flags
), af
, HashString(originSuffix
.get()));
54 size_t nsHostKey::SizeOfExcludingThis(
55 mozilla::MallocSizeOf mallocSizeOf
) const {
57 n
+= host
.SizeOfExcludingThisIfUnshared(mallocSizeOf
);
58 n
+= mTrrServer
.SizeOfExcludingThisIfUnshared(mallocSizeOf
);
59 n
+= originSuffix
.SizeOfExcludingThisIfUnshared(mallocSizeOf
);
63 //----------------------------------------------------------------------------
65 //----------------------------------------------------------------------------
67 NS_IMPL_ISUPPORTS0(nsHostRecord
)
69 nsHostRecord::nsHostRecord(const nsHostKey
& key
)
70 : nsHostKey(key
), mTRRQuery("nsHostRecord.mTRRQuery") {}
72 void nsHostRecord::Invalidate() { mDoomed
= true; }
74 void nsHostRecord::Cancel() {
75 RefPtr
<TRRQuery
> query
;
77 auto lock
= mTRRQuery
.Lock();
78 query
.swap(lock
.ref());
82 query
->Cancel(NS_ERROR_ABORT
);
86 nsHostRecord::ExpirationStatus
nsHostRecord::CheckExpiration(
87 const mozilla::TimeStamp
& now
) const {
88 if (!mGraceStart
.IsNull() && now
>= mGraceStart
&& !mValidEnd
.IsNull() &&
90 return nsHostRecord::EXP_GRACE
;
92 if (!mValidEnd
.IsNull() && now
< mValidEnd
) {
93 return nsHostRecord::EXP_VALID
;
96 return nsHostRecord::EXP_EXPIRED
;
99 void nsHostRecord::SetExpiration(const mozilla::TimeStamp
& now
,
100 unsigned int valid
, unsigned int grace
) {
102 if ((valid
+ grace
) < 60) {
104 LOG(("SetExpiration: artificially bumped grace to %d\n", grace
));
106 mGraceStart
= now
+ TimeDuration::FromSeconds(valid
);
107 mValidEnd
= now
+ TimeDuration::FromSeconds(valid
+ grace
);
111 void nsHostRecord::CopyExpirationTimesAndFlagsFrom(
112 const nsHostRecord
* aFromHostRecord
) {
113 // This is used to copy information from a cache entry to a record. All
114 // information necessary for HasUsableRecord needs to be copied.
115 mValidStart
= aFromHostRecord
->mValidStart
;
116 mValidEnd
= aFromHostRecord
->mValidEnd
;
117 mGraceStart
= aFromHostRecord
->mGraceStart
;
118 mDoomed
= aFromHostRecord
->mDoomed
;
119 mTtl
= uint32_t(aFromHostRecord
->mTtl
);
122 bool nsHostRecord::HasUsableResult(const mozilla::TimeStamp
& now
,
123 nsIDNSService::DNSFlags queryFlags
) const {
128 return HasUsableResultInternal(now
, queryFlags
);
131 //----------------------------------------------------------------------------
133 //----------------------------------------------------------------------------
135 static size_t SizeOfResolveHostCallbackListExcludingHead(
136 const mozilla::LinkedList
<RefPtr
<nsResolveHostCallback
>>& aCallbacks
,
137 MallocSizeOf mallocSizeOf
) {
138 size_t n
= aCallbacks
.sizeOfExcludingThis(mallocSizeOf
);
140 for (const nsResolveHostCallback
* t
= aCallbacks
.getFirst(); t
;
142 n
+= t
->SizeOfIncludingThis(mallocSizeOf
);
148 NS_IMPL_ISUPPORTS_INHERITED(AddrHostRecord
, nsHostRecord
, AddrHostRecord
)
150 AddrHostRecord::AddrHostRecord(const nsHostKey
& key
) : nsHostRecord(key
) {}
152 AddrHostRecord::~AddrHostRecord() {
154 Telemetry::Accumulate(Telemetry::DNS_BLACKLIST_COUNT
, mUnusableCount
);
157 bool AddrHostRecord::Blocklisted(const NetAddr
* aQuery
) {
158 addr_info_lock
.AssertCurrentThreadOwns();
159 LOG(("Checking unusable list for host [%s], host record [%p].\n", host
.get(),
162 // skip the string conversion for the common case of no blocklist
163 if (!mUnusableItems
.Length()) {
167 char buf
[kIPv6CStrBufSize
];
168 if (!aQuery
->ToStringBuffer(buf
, sizeof(buf
))) {
171 nsDependentCString
strQuery(buf
);
173 for (uint32_t i
= 0; i
< mUnusableItems
.Length(); i
++) {
174 if (mUnusableItems
.ElementAt(i
).Equals(strQuery
)) {
175 LOG(("Address [%s] is blocklisted for host [%s].\n", buf
, host
.get()));
183 void AddrHostRecord::ReportUnusable(const NetAddr
* aAddress
) {
184 addr_info_lock
.AssertCurrentThreadOwns();
186 ("Adding address to blocklist for host [%s], host record [%p]."
188 host
.get(), this, mTRRSuccess
));
192 char buf
[kIPv6CStrBufSize
];
193 if (aAddress
->ToStringBuffer(buf
, sizeof(buf
))) {
195 ("Successfully adding address [%s] to blocklist for host "
198 mUnusableItems
.AppendElement(nsCString(buf
));
202 void AddrHostRecord::ResetBlocklist() {
203 addr_info_lock
.AssertCurrentThreadOwns();
204 LOG(("Resetting blocklist for host [%s], host record [%p].\n", host
.get(),
206 mUnusableItems
.Clear();
209 size_t AddrHostRecord::SizeOfIncludingThis(MallocSizeOf mallocSizeOf
) const {
210 size_t n
= mallocSizeOf(this);
212 n
+= nsHostKey::SizeOfExcludingThis(mallocSizeOf
);
213 n
+= SizeOfResolveHostCallbackListExcludingHead(mCallbacks
, mallocSizeOf
);
215 n
+= addr_info
? addr_info
->SizeOfIncludingThis(mallocSizeOf
) : 0;
216 n
+= mallocSizeOf(addr
.get());
218 n
+= mUnusableItems
.ShallowSizeOfExcludingThis(mallocSizeOf
);
219 for (size_t i
= 0; i
< mUnusableItems
.Length(); i
++) {
220 n
+= mUnusableItems
[i
].SizeOfExcludingThisIfUnshared(mallocSizeOf
);
225 bool AddrHostRecord::HasUsableResultInternal(
226 const mozilla::TimeStamp
& now
, nsIDNSService::DNSFlags queryFlags
) const {
227 // don't use cached negative results for high priority queries.
228 if (negative
&& IsHighPriority(queryFlags
)) {
232 if (CheckExpiration(now
) == EXP_EXPIRED
) {
240 return addr_info
|| addr
;
243 // Returns true if the entry can be removed, or false if it should be left.
244 // Sets ResolveAgain true for entries being resolved right now.
245 bool AddrHostRecord::RemoveOrRefresh(bool aTrrToo
) {
246 // no need to flush TRRed names, they're not resolved "locally"
247 MutexAutoLock
lock(addr_info_lock
);
248 if (addr_info
&& !aTrrToo
&& addr_info
->IsTRR()) {
253 // The request has been passed to the OS resolver. The resultant DNS
254 // record should be considered stale and not trusted; set a flag to
255 // ensure it is called again.
256 StoreResolveAgain(true);
258 // if onQueue is true, the host entry is already added to the cache
259 // but is still pending to get resolved: just leave it in hash.
262 // Already resolved; not in a pending state; remove from cache
266 void AddrHostRecord::NotifyRetryingTrr() {
267 MOZ_ASSERT(mFirstTRRSkippedReason
==
268 mozilla::net::TRRSkippedReason::TRR_UNSET
);
270 // Save the skip reason of our first attempt for recording telemetry later.
271 mFirstTRRSkippedReason
= mTRRSkippedReason
;
272 mTRRSkippedReason
= mozilla::net::TRRSkippedReason::TRR_UNSET
;
275 void AddrHostRecord::ResolveComplete() {
276 if (LoadNativeUsed()) {
277 if (mNativeSuccess
) {
278 uint32_t millis
= static_cast<uint32_t>(mNativeDuration
.ToMilliseconds());
279 Telemetry::Accumulate(Telemetry::DNS_NATIVE_LOOKUP_TIME
, millis
);
281 AccumulateCategoricalKeyed(
282 TRRService::ProviderKey(),
283 mNativeSuccess
? Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::osOK
284 : Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::osFail
);
287 if (mResolverType
== DNSResolverType::TRR
) {
289 MOZ_DIAGNOSTIC_ASSERT(mTRRSkippedReason
==
290 mozilla::net::TRRSkippedReason::TRR_OK
);
291 uint32_t millis
= static_cast<uint32_t>(mTrrDuration
.ToMilliseconds());
292 Telemetry::Accumulate(Telemetry::DNS_TRR_LOOKUP_TIME3
,
293 TRRService::ProviderKey(), millis
);
295 AccumulateCategoricalKeyed(
296 TRRService::ProviderKey(),
297 mTRRSuccess
? Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::trrOK
298 : Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::trrFail
);
301 if (nsHostResolver::Mode() == nsIDNSService::MODE_TRRFIRST
||
302 nsHostResolver::Mode() == nsIDNSService::MODE_TRRONLY
) {
303 MOZ_ASSERT(mTRRSkippedReason
!= mozilla::net::TRRSkippedReason::TRR_UNSET
);
305 Telemetry::Accumulate(Telemetry::TRR_SKIP_REASON_TRR_FIRST2
,
306 TRRService::ProviderKey(),
307 static_cast<uint32_t>(mTRRSkippedReason
));
308 if (!mTRRSuccess
&& LoadNativeUsed()) {
309 Telemetry::Accumulate(
310 mNativeSuccess
? Telemetry::TRR_SKIP_REASON_NATIVE_SUCCESS
311 : Telemetry::TRR_SKIP_REASON_NATIVE_FAILED
,
312 TRRService::ProviderKey(), static_cast<uint32_t>(mTRRSkippedReason
));
315 if (IsRelevantTRRSkipReason(mTRRSkippedReason
)) {
316 Telemetry::Accumulate(Telemetry::TRR_RELEVANT_SKIP_REASON_TRR_FIRST
,
317 TRRService::ProviderKey(),
318 static_cast<uint32_t>(mTRRSkippedReason
));
320 if (!mTRRSuccess
&& LoadNativeUsed()) {
321 Telemetry::Accumulate(
322 mNativeSuccess
? Telemetry::TRR_RELEVANT_SKIP_REASON_NATIVE_SUCCESS
323 : Telemetry::TRR_RELEVANT_SKIP_REASON_NATIVE_FAILED
,
324 TRRService::ProviderKey(),
325 static_cast<uint32_t>(mTRRSkippedReason
));
329 if (StaticPrefs::network_trr_retry_on_recoverable_errors() &&
330 nsHostResolver::Mode() == nsIDNSService::MODE_TRRFIRST
) {
331 nsAutoCString
telemetryKey(TRRService::ProviderKey());
333 if (mFirstTRRSkippedReason
!= mozilla::net::TRRSkippedReason::TRR_UNSET
) {
334 telemetryKey
.AppendLiteral("|");
335 telemetryKey
.AppendInt(static_cast<uint32_t>(mFirstTRRSkippedReason
));
337 Telemetry::Accumulate(mTRRSuccess
338 ? Telemetry::TRR_SKIP_REASON_RETRY_SUCCESS
339 : Telemetry::TRR_SKIP_REASON_RETRY_FAILED
,
340 TRRService::ProviderKey(),
341 static_cast<uint32_t>(mFirstTRRSkippedReason
));
344 Telemetry::Accumulate(Telemetry::TRR_SKIP_REASON_STRICT_MODE
,
346 static_cast<uint32_t>(mTRRSkippedReason
));
349 Telemetry::Accumulate(Telemetry::TRR_ATTEMPT_COUNT
,
350 TRRService::ProviderKey(), mTrrAttempts
);
355 if (mEffectiveTRRMode
== nsIRequest::TRR_FIRST_MODE
) {
356 if (flags
& nsIDNSService::RESOLVE_DISABLE_TRR
) {
357 // TRR is disabled on request, which is a next-level back-off method.
358 Telemetry::Accumulate(Telemetry::DNS_TRR_DISABLED3
,
359 TRRService::ProviderKey(), mNativeSuccess
);
362 AccumulateCategoricalKeyed(TRRService::ProviderKey(),
363 Telemetry::LABELS_DNS_TRR_FIRST4::TRR
);
364 } else if (mNativeSuccess
) {
365 if (mResolverType
== DNSResolverType::TRR
) {
366 AccumulateCategoricalKeyed(
367 TRRService::ProviderKey(),
368 Telemetry::LABELS_DNS_TRR_FIRST4::NativeAfterTRR
);
370 AccumulateCategoricalKeyed(TRRService::ProviderKey(),
371 Telemetry::LABELS_DNS_TRR_FIRST4::Native
);
374 AccumulateCategoricalKeyed(
375 TRRService::ProviderKey(),
376 Telemetry::LABELS_DNS_TRR_FIRST4::BothFailed
);
381 switch (mEffectiveTRRMode
) {
382 case nsIRequest::TRR_DISABLED_MODE
:
383 AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::nativeOnly
);
385 case nsIRequest::TRR_FIRST_MODE
:
386 AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::trrFirst
);
388 case nsIRequest::TRR_ONLY_MODE
:
389 AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::trrOnly
);
391 case nsIRequest::TRR_DEFAULT_MODE
:
392 MOZ_ASSERT_UNREACHABLE("We should not have a default value here");
396 if (mResolverType
== DNSResolverType::TRR
&& !mTRRSuccess
&& mNativeSuccess
&&
397 !LoadGetTtl() && TRRService::Get()) {
398 TRRService::Get()->AddToBlocklist(nsCString(host
), originSuffix
, pb
, true);
402 AddrHostRecord::DnsPriority
AddrHostRecord::GetPriority(
403 nsIDNSService::DNSFlags aFlags
) {
404 if (IsHighPriority(aFlags
)) {
405 return AddrHostRecord::DNS_PRIORITY_HIGH
;
407 if (IsMediumPriority(aFlags
)) {
408 return AddrHostRecord::DNS_PRIORITY_MEDIUM
;
411 return AddrHostRecord::DNS_PRIORITY_LOW
;
414 nsresult
AddrHostRecord::GetTtl(uint32_t* aResult
) {
415 NS_ENSURE_ARG(aResult
);
420 //----------------------------------------------------------------------------
422 //----------------------------------------------------------------------------
424 NS_IMPL_ISUPPORTS_INHERITED(TypeHostRecord
, nsHostRecord
, TypeHostRecord
,
425 nsIDNSTXTRecord
, nsIDNSHTTPSSVCRecord
)
427 TypeHostRecord::TypeHostRecord(const nsHostKey
& key
)
428 : nsHostRecord(key
), DNSHTTPSSVCRecordBase(key
.host
) {}
430 TypeHostRecord::~TypeHostRecord() { mCallbacks
.clear(); }
432 bool TypeHostRecord::HasUsableResultInternal(
433 const mozilla::TimeStamp
& now
, nsIDNSService::DNSFlags queryFlags
) const {
434 if (CheckExpiration(now
) == EXP_EXPIRED
) {
442 return !mResults
.is
<Nothing
>();
445 bool TypeHostRecord::RefreshForNegativeResponse() const { return false; }
447 NS_IMETHODIMP
TypeHostRecord::GetRecords(CopyableTArray
<nsCString
>& aRecords
) {
449 MutexAutoLock
lock(mResultsLock
);
451 if (!mResults
.is
<TypeRecordTxt
>()) {
452 return NS_ERROR_NOT_AVAILABLE
;
454 aRecords
= mResults
.as
<CopyableTArray
<nsCString
>>();
458 NS_IMETHODIMP
TypeHostRecord::GetRecordsAsOneString(nsACString
& aRecords
) {
460 MutexAutoLock
lock(mResultsLock
);
462 if (!mResults
.is
<TypeRecordTxt
>()) {
463 return NS_ERROR_NOT_AVAILABLE
;
465 auto& results
= mResults
.as
<CopyableTArray
<nsCString
>>();
466 for (uint32_t i
= 0; i
< results
.Length(); i
++) {
467 aRecords
.Append(results
[i
]);
472 size_t TypeHostRecord::SizeOfIncludingThis(MallocSizeOf mallocSizeOf
) const {
473 size_t n
= mallocSizeOf(this);
475 n
+= nsHostKey::SizeOfExcludingThis(mallocSizeOf
);
476 n
+= SizeOfResolveHostCallbackListExcludingHead(mCallbacks
, mallocSizeOf
);
481 uint32_t TypeHostRecord::GetType() {
482 MutexAutoLock
lock(mResultsLock
);
484 return mResults
.match(
485 [](TypeRecordEmpty
&) {
486 MOZ_ASSERT(false, "This should never be the case");
487 return nsIDNSService::RESOLVE_TYPE_DEFAULT
;
489 [](TypeRecordTxt
&) { return nsIDNSService::RESOLVE_TYPE_TXT
; },
490 [](TypeRecordHTTPSSVC
&) { return nsIDNSService::RESOLVE_TYPE_HTTPSSVC
; });
493 TypeRecordResultType
TypeHostRecord::GetResults() {
494 MutexAutoLock
lock(mResultsLock
);
499 TypeHostRecord::GetRecords(nsTArray
<RefPtr
<nsISVCBRecord
>>& aRecords
) {
500 MutexAutoLock
lock(mResultsLock
);
501 if (!mResults
.is
<TypeRecordHTTPSSVC
>()) {
502 return NS_ERROR_NOT_AVAILABLE
;
505 auto& results
= mResults
.as
<TypeRecordHTTPSSVC
>();
507 for (const SVCB
& r
: results
) {
508 RefPtr
<nsISVCBRecord
> rec
= new mozilla::net::SVCBRecord(r
);
509 aRecords
.AppendElement(rec
);
516 TypeHostRecord::GetServiceModeRecord(bool aNoHttp2
, bool aNoHttp3
,
517 nsISVCBRecord
** aRecord
) {
518 MutexAutoLock
lock(mResultsLock
);
519 if (!mResults
.is
<TypeRecordHTTPSSVC
>()) {
520 return NS_ERROR_NOT_AVAILABLE
;
523 auto& results
= mResults
.as
<TypeRecordHTTPSSVC
>();
524 nsCOMPtr
<nsISVCBRecord
> result
= GetServiceModeRecordInternal(
525 aNoHttp2
, aNoHttp3
, results
, mAllRecordsExcluded
);
527 return NS_ERROR_NOT_AVAILABLE
;
530 result
.forget(aRecord
);
535 TypeHostRecord::GetAllRecordsWithEchConfig(
536 bool aNoHttp2
, bool aNoHttp3
, bool* aAllRecordsHaveEchConfig
,
537 bool* aAllRecordsInH3ExcludedList
,
538 nsTArray
<RefPtr
<nsISVCBRecord
>>& aResult
) {
539 MutexAutoLock
lock(mResultsLock
);
540 if (!mResults
.is
<TypeRecordHTTPSSVC
>()) {
541 return NS_ERROR_NOT_AVAILABLE
;
544 auto& records
= mResults
.as
<TypeRecordHTTPSSVC
>();
545 GetAllRecordsWithEchConfigInternal(aNoHttp2
, aNoHttp3
, records
,
546 aAllRecordsHaveEchConfig
,
547 aAllRecordsInH3ExcludedList
, aResult
);
552 TypeHostRecord::GetHasIPAddresses(bool* aResult
) {
553 NS_ENSURE_ARG(aResult
);
554 MutexAutoLock
lock(mResultsLock
);
556 if (!mResults
.is
<TypeRecordHTTPSSVC
>()) {
557 return NS_ERROR_NOT_AVAILABLE
;
560 auto& results
= mResults
.as
<TypeRecordHTTPSSVC
>();
561 *aResult
= HasIPAddressesInternal(results
);
566 TypeHostRecord::GetAllRecordsExcluded(bool* aResult
) {
567 NS_ENSURE_ARG(aResult
);
568 MutexAutoLock
lock(mResultsLock
);
570 if (!mResults
.is
<TypeRecordHTTPSSVC
>()) {
571 return NS_ERROR_NOT_AVAILABLE
;
574 *aResult
= mAllRecordsExcluded
;
579 TypeHostRecord::GetTtl(uint32_t* aResult
) {
580 NS_ENSURE_ARG(aResult
);