Bug 1866777 - Disable test_race_cache_with_network.js on windows opt for frequent...
[gecko.git] / netwerk / dns / nsHostRecord.cpp
blob159abb6e548bb9f703e8772beaa4b76a02130819
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"
7 #include "TRRQuery.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) \
19 ((_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)
34 : host(aHost),
35 mTrrServer(aTrrServer),
36 type(aType),
37 flags(aFlags),
38 af(aAf),
39 pb(aPb),
40 originSuffix(aOriginsuffix) {}
42 bool nsHostKey::operator==(const nsHostKey& other) const {
43 return host == other.host && mTrrServer == other.mTrrServer &&
44 type == other.type &&
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 {
56 size_t n = 0;
57 n += host.SizeOfExcludingThisIfUnshared(mallocSizeOf);
58 n += mTrrServer.SizeOfExcludingThisIfUnshared(mallocSizeOf);
59 n += originSuffix.SizeOfExcludingThisIfUnshared(mallocSizeOf);
60 return n;
63 //----------------------------------------------------------------------------
64 // nsHostRecord
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());
81 if (query) {
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() &&
89 now < mValidEnd) {
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) {
101 mValidStart = now;
102 if ((valid + grace) < 60) {
103 grace = 60 - valid;
104 LOG(("SetExpiration: artificially bumped grace to %d\n", grace));
106 mGraceStart = now + TimeDuration::FromSeconds(valid);
107 mValidEnd = now + TimeDuration::FromSeconds(valid + grace);
108 mTtl = valid;
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 {
124 if (mDoomed) {
125 return false;
128 return HasUsableResultInternal(now, queryFlags);
131 //----------------------------------------------------------------------------
132 // AddrHostRecord
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;
141 t = t->getNext()) {
142 n += t->SizeOfIncludingThis(mallocSizeOf);
145 return n;
148 NS_IMPL_ISUPPORTS_INHERITED(AddrHostRecord, nsHostRecord, AddrHostRecord)
150 AddrHostRecord::AddrHostRecord(const nsHostKey& key) : nsHostRecord(key) {}
152 AddrHostRecord::~AddrHostRecord() {
153 mCallbacks.clear();
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(),
160 this));
162 // skip the string conversion for the common case of no blocklist
163 if (!mUnusableItems.Length()) {
164 return false;
167 char buf[kIPv6CStrBufSize];
168 if (!aQuery->ToStringBuffer(buf, sizeof(buf))) {
169 return false;
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()));
176 return true;
180 return false;
183 void AddrHostRecord::ReportUnusable(const NetAddr* aAddress) {
184 addr_info_lock.AssertCurrentThreadOwns();
185 LOG(
186 ("Adding address to blocklist for host [%s], host record [%p]."
187 "used trr=%d\n",
188 host.get(), this, mTRRSuccess));
190 ++mUnusableCount;
192 char buf[kIPv6CStrBufSize];
193 if (aAddress->ToStringBuffer(buf, sizeof(buf))) {
194 LOG(
195 ("Successfully adding address [%s] to blocklist for host "
196 "[%s].\n",
197 buf, host.get()));
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(),
205 this));
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);
222 return n;
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)) {
229 return false;
232 if (CheckExpiration(now) == EXP_EXPIRED) {
233 return false;
236 if (negative) {
237 return true;
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()) {
249 return false;
251 if (LoadNative()) {
252 if (!onQueue()) {
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.
260 return false;
262 // Already resolved; not in a pending state; remove from cache
263 return true;
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) {
288 if (mTRRSuccess) {
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,
345 telemetryKey,
346 static_cast<uint32_t>(mTRRSkippedReason));
348 if (mTRRSuccess) {
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);
360 } else {
361 if (mTRRSuccess) {
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);
369 } else {
370 AccumulateCategoricalKeyed(TRRService::ProviderKey(),
371 Telemetry::LABELS_DNS_TRR_FIRST4::Native);
373 } else {
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);
384 break;
385 case nsIRequest::TRR_FIRST_MODE:
386 AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::trrFirst);
387 break;
388 case nsIRequest::TRR_ONLY_MODE:
389 AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::trrOnly);
390 break;
391 case nsIRequest::TRR_DEFAULT_MODE:
392 MOZ_ASSERT_UNREACHABLE("We should not have a default value here");
393 break;
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);
416 *aResult = mTtl;
417 return NS_OK;
420 //----------------------------------------------------------------------------
421 // TypeHostRecord
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) {
435 return false;
438 if (negative) {
439 return true;
442 return !mResults.is<Nothing>();
445 bool TypeHostRecord::RefreshForNegativeResponse() const { return false; }
447 NS_IMETHODIMP TypeHostRecord::GetRecords(CopyableTArray<nsCString>& aRecords) {
448 // deep copy
449 MutexAutoLock lock(mResultsLock);
451 if (!mResults.is<TypeRecordTxt>()) {
452 return NS_ERROR_NOT_AVAILABLE;
454 aRecords = mResults.as<CopyableTArray<nsCString>>();
455 return NS_OK;
458 NS_IMETHODIMP TypeHostRecord::GetRecordsAsOneString(nsACString& aRecords) {
459 // deep copy
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]);
469 return NS_OK;
472 size_t TypeHostRecord::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const {
473 size_t n = mallocSizeOf(this);
475 n += nsHostKey::SizeOfExcludingThis(mallocSizeOf);
476 n += SizeOfResolveHostCallbackListExcludingHead(mCallbacks, mallocSizeOf);
478 return n;
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);
495 return mResults;
498 NS_IMETHODIMP
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);
512 return NS_OK;
515 NS_IMETHODIMP
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);
526 if (!result) {
527 return NS_ERROR_NOT_AVAILABLE;
530 result.forget(aRecord);
531 return NS_OK;
534 NS_IMETHODIMP
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);
548 return NS_OK;
551 NS_IMETHODIMP
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);
562 return NS_OK;
565 NS_IMETHODIMP
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;
575 return NS_OK;
578 NS_IMETHODIMP
579 TypeHostRecord::GetTtl(uint32_t* aResult) {
580 NS_ENSURE_ARG(aResult);
581 *aResult = mTtl;
582 return NS_OK;
585 void TypeHostRecord::ResolveComplete() {
586 if (IsRelevantTRRSkipReason(mTRRSkippedReason)) {
587 Telemetry::Accumulate(
588 Telemetry::TRR_RELEVANT_SKIP_REASON_TRR_FIRST_TYPE_REC,
589 TRRService::ProviderKey(), static_cast<uint32_t>(mTRRSkippedReason));
592 uint32_t millis = static_cast<uint32_t>(mTrrDuration.ToMilliseconds());
593 if (mTRRSuccess) {
594 Telemetry::Accumulate(Telemetry::DNS_BY_TYPE_SUCCEEDED_LOOKUP_TIME, millis);
595 } else {
596 Telemetry::Accumulate(Telemetry::DNS_BY_TYPE_FAILED_LOOKUP_TIME, millis);