diff --git a/netwerk/dns/TRR.cpp b/netwerk/dns/TRR.cpp index 6e917e8645aa..766be2307992 100644 --- a/netwerk/dns/TRR.cpp +++ b/netwerk/dns/TRR.cpp @@ -633,7 +633,8 @@ void TRR::SaveAdditionalRecords( addrRec->mTrrStart = TimeStamp::Now(); LOG(("Completing lookup for additional: %s", nsCString(iter.Key()).get())); (void)mHostResolver->CompleteLookup(hostRecord, NS_OK, ai, mPB, - mOriginSuffix, AddrHostRecord::TRR_OK); + mOriginSuffix, AddrHostRecord::TRR_OK, + this); } } @@ -672,7 +673,7 @@ void TRR::StoreIPHintAsDNSRecord(const struct SVCB& aSVCBRecord) { addrRec->mTrrStart = TimeStamp::Now(); (void)mHostResolver->CompleteLookup(hostRecord, NS_OK, ai, mPB, mOriginSuffix, - AddrHostRecord::TRR_OK); + AddrHostRecord::TRR_OK, this); } nsresult TRR::ReturnData(nsIChannel* aChannel) { @@ -705,7 +706,7 @@ nsresult TRR::ReturnData(nsIChannel* aChannel) { return NS_ERROR_FAILURE; } (void)mHostResolver->CompleteLookup(mRec, NS_OK, ai, mPB, mOriginSuffix, - mTRRSkippedReason); + mTRRSkippedReason, this); mHostResolver = nullptr; mRec = nullptr; } else { @@ -732,7 +733,7 @@ nsresult TRR::FailData(nsresult error) { RefPtr ai = new AddrInfo(mHost, mType, std::move(noAddresses)); (void)mHostResolver->CompleteLookup(mRec, error, ai, mPB, mOriginSuffix, - mTRRSkippedReason); + mTRRSkippedReason, this); } mHostResolver = nullptr; @@ -862,6 +863,8 @@ TRR::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) { nsCOMPtr channel; channel.swap(mChannel); + mChannelStatus = aStatusCode; + { // Cancel the timer since we don't need it anymore. nsCOMPtr timer; diff --git a/netwerk/dns/TRR.h b/netwerk/dns/TRR.h index 8cb501d64866..fcea932f27ac 100644 --- a/netwerk/dns/TRR.h +++ b/netwerk/dns/TRR.h @@ -103,6 +103,8 @@ class TRR : public Runnable, void SetTimeout(uint32_t aTimeoutMs) { mTimeoutMs = aTimeoutMs; } + nsresult ChannelStatus() { return mChannelStatus; } + protected: virtual ~TRR() = default; virtual DNSPacket* GetOrCreateDNSPacket(); @@ -142,6 +144,7 @@ class TRR : public Runnable, bool mFailed = false; bool mPB; DOHresp mDNS; + nsresult mChannelStatus = NS_OK; // The request timeout in milliseconds. If 0 we will use the default timeout // we get from the prefs. diff --git a/netwerk/dns/TRRQuery.cpp b/netwerk/dns/TRRQuery.cpp index 0af0c096473c..4ad89b666f55 100644 --- a/netwerk/dns/TRRQuery.cpp +++ b/netwerk/dns/TRRQuery.cpp @@ -162,10 +162,11 @@ nsresult TRRQuery::DispatchLookup(TRR* pushedTRR, bool aUseODoH) { AHostResolver::LookupStatus TRRQuery::CompleteLookup( nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb, - const nsACString& aOriginsuffix, nsHostRecord::TRRSkippedReason aReason) { + const nsACString& aOriginsuffix, nsHostRecord::TRRSkippedReason aReason, + TRR* aTRRRequest) { if (rec != mRecord) { return mHostResolver->CompleteLookup(rec, status, aNewRRSet, pb, - aOriginsuffix, aReason); + aOriginsuffix, aReason, aTRRRequest); } RefPtr newRRSet(aNewRRSet); @@ -270,7 +271,7 @@ AHostResolver::LookupStatus TRRQuery::CompleteLookup( } return mHostResolver->CompleteLookup(rec, status, newRRSet, pb, aOriginsuffix, - aReason); + aReason, aTRRRequest); } AHostResolver::LookupStatus TRRQuery::CompleteLookupByType( diff --git a/netwerk/dns/TRRQuery.h b/netwerk/dns/TRRQuery.h index 5333147d8389..a660a473a8ee 100644 --- a/netwerk/dns/TRRQuery.h +++ b/netwerk/dns/TRRQuery.h @@ -31,10 +31,11 @@ class TRRQuery : public AHostResolver { AddrHostRecord::TRRSkippedReason mTRRAAAAFailReason = AddrHostRecord::TRR_UNSET; - virtual LookupStatus CompleteLookup( - nsHostRecord*, nsresult, mozilla::net::AddrInfo*, bool pb, - const nsACString& aOriginsuffix, - nsHostRecord::TRRSkippedReason aReason) override; + virtual LookupStatus CompleteLookup(nsHostRecord*, nsresult, + mozilla::net::AddrInfo*, bool pb, + const nsACString& aOriginsuffix, + nsHostRecord::TRRSkippedReason aReason, + TRR* aTRRRequest) override; virtual LookupStatus CompleteLookupByType( nsHostRecord*, nsresult, mozilla::net::TypeRecordResultType& aResult, uint32_t aTtl, bool pb) override; diff --git a/netwerk/dns/TRRService.cpp b/netwerk/dns/TRRService.cpp index 1444186f1592..7c209a6842b6 100644 --- a/netwerk/dns/TRRService.cpp +++ b/netwerk/dns/TRRService.cpp @@ -56,10 +56,9 @@ TRRService::TRRService() mConfirmationNS("example.com"_ns), mCaptiveIsPassed(false), mTRRBLStorage("DataMutex::TRRBlocklist"), - mConfirmationState(CONFIRM_INIT), - mRetryConfirmInterval(125), - mTRRFailures(0), mParentalControlEnabled(false) { + mConfirmation.mState = CONFIRM_INIT; + mConfirmation.mTRRFailures = 0; MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); } @@ -171,11 +170,10 @@ nsresult TRRService::Init() { mParentalControlEnabled = GetParentalControlEnabledInternal(); - nsCOMPtr nls = - do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID); - if (nls) { + mLinkService = do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID); + if (mLinkService) { nsTArray suffixList; - nls->GetDnsSuffixList(suffixList); + mLinkService->GetDnsSuffixList(suffixList); RebuildSuffixList(std::move(suffixList)); } @@ -203,6 +201,8 @@ nsresult TRRService::Init() { return NS_ERROR_FAILURE; } + Telemetry::SetEventRecordingEnabled("network.dns"_ns, true); + LOG(("Initialized TRRService\n")); return NS_OK; } @@ -236,27 +236,27 @@ bool TRRService::Enabled(nsIRequest::TRRMode aRequestMode) { return false; } - if (mConfirmationState == CONFIRM_INIT && + if (mConfirmation.mState == CONFIRM_INIT && (!StaticPrefs::network_trr_wait_for_portal() || mCaptiveIsPassed || (mMode == nsIDNSService::MODE_TRRONLY || aRequestMode == nsIRequest::TRR_ONLY_MODE))) { LOG(("TRRService::Enabled => CONFIRM_TRYING\n")); - mConfirmationState = CONFIRM_TRYING; + mConfirmation.mState = CONFIRM_TRYING; } - if (mConfirmationState == CONFIRM_TRYING) { + if (mConfirmation.mState == CONFIRM_TRYING) { LOG(("TRRService::Enabled MaybeConfirm()\n")); - MaybeConfirm(); + MaybeConfirm("context-init"); if (mMode == nsIDNSService::MODE_TRRONLY) { - MOZ_ASSERT(mConfirmationState == CONFIRM_OK, + MOZ_ASSERT(mConfirmation.mState == CONFIRM_OK, "Global mode is trr-only, but confirmation failed?"); } } - LOG(("TRRService::Enabled mConfirmationState=%d mCaptiveIsPassed=%d\n", - (int)mConfirmationState, (int)mCaptiveIsPassed)); + LOG(("TRRService::Enabled mConfirmation.mState=%d mCaptiveIsPassed=%d\n", + (int)mConfirmation.mState, (int)mCaptiveIsPassed)); - if (mConfirmationState == CONFIRM_OK) { + if (mConfirmation.mState == CONFIRM_OK) { return true; } @@ -275,7 +275,7 @@ bool TRRService::Enabled(nsIRequest::TRRMode aRequestMode) { if ((aRequestMode == nsIRequest::TRR_DEFAULT_MODE && mMode == nsIDNSService::MODE_TRRFIRST) || aRequestMode == nsIRequest::TRR_FIRST_MODE) { - return mConfirmationState != CONFIRM_FAILED; + return mConfirmation.mState != CONFIRM_FAILED; } return false; @@ -352,12 +352,12 @@ nsresult TRRService::ReadPrefs(const char* name) { nsAutoCString old(mConfirmationNS); Preferences::GetCString(TRR_PREF("confirmationNS"), mConfirmationNS); if (name && !old.IsEmpty() && !mConfirmationNS.Equals(old) && - (mConfirmationState > CONFIRM_TRYING) && + (mConfirmation.mState > CONFIRM_TRYING) && (mMode == nsIDNSService::MODE_TRRFIRST || mMode == nsIDNSService::MODE_TRRONLY)) { LOG(("TRR::ReadPrefs: restart confirmationNS state\n")); - mConfirmationState = CONFIRM_TRYING; - MaybeConfirm_locked(); + mConfirmation.mState = CONFIRM_TRYING; + MaybeConfirm_locked("pref-change"); } } if (!name || !strcmp(name, TRR_PREF("bootstrapAddress"))) { @@ -572,18 +572,19 @@ TRRService::Observe(nsISupports* aSubject, const char* aTopic, if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { ReadPrefs(NS_ConvertUTF16toUTF8(aData).get()); + mConfirmation.RecordEvent("pref-change"); MutexAutoLock lock(mLock); - if (((mConfirmationState == CONFIRM_INIT) && !mBootstrapAddr.IsEmpty() && + if (((mConfirmation.mState == CONFIRM_INIT) && !mBootstrapAddr.IsEmpty() && (mMode == nsIDNSService::MODE_TRRONLY)) || - (mConfirmationState == CONFIRM_FAILED)) { - mConfirmationState = CONFIRM_TRYING; - MaybeConfirm_locked(); + (mConfirmation.mState == CONFIRM_FAILED)) { + mConfirmation.mState = CONFIRM_TRYING; + MaybeConfirm_locked("pref-change"); } - } else if (!strcmp(aTopic, kOpenCaptivePortalLoginEvent)) { // We are in a captive portal LOG(("TRRservice in captive portal\n")); mCaptiveIsPassed = false; + mConfirmation.mCaptivePortalStatus = nsICaptivePortalService::LOCKED_PORTAL; } else if (!strcmp(aTopic, NS_CAPTIVE_PORTAL_CONNECTIVITY)) { nsAutoCString data = NS_ConvertUTF16toUTF8(aData); LOG(("TRRservice captive portal was %s\n", data.get())); @@ -592,19 +593,23 @@ TRRService::Observe(nsISupports* aSubject, const char* aTopic, // unless the service is in a TRR=enabled mode. if (mMode == nsIDNSService::MODE_TRRFIRST || mMode == nsIDNSService::MODE_TRRONLY) { - if (mRetryConfirmTimer) { - mRetryConfirmTimer->Cancel(); - mRetryConfirmTimer = nullptr; + if (mConfirmation.mTimer) { + mConfirmation.mTimer->Cancel(); + mConfirmation.mTimer = nullptr; } - mRetryConfirmInterval = StaticPrefs::network_trr_retry_timeout_ms(); - if (mConfirmationState != CONFIRM_OK) { - mConfirmationState = CONFIRM_TRYING; - MaybeConfirm(); + mConfirmation.mRetryInterval = + StaticPrefs::network_trr_retry_timeout_ms(); + if (mConfirmation.mState != CONFIRM_OK) { + mConfirmation.mState = CONFIRM_TRYING; + MaybeConfirm("cp-connectivity"); } } mCaptiveIsPassed = true; - + nsCOMPtr cps = do_QueryInterface(aSubject); + if (cps) { + cps->GetState(&mConfirmation.mCaptivePortalStatus); + } } else if (!strcmp(aTopic, kClearPrivateData) || !strcmp(aTopic, kPurge)) { // flush the TRR blocklist auto bl = mTRRBLStorage.Lock(); @@ -623,12 +628,23 @@ TRRService::Observe(nsISupports* aSubject, const char* aTopic, } } - if (!strcmp(aTopic, NS_NETWORK_LINK_TOPIC) && mURISetByDetection) { - // If the URI was set via SetDetectedTrrURI we need to restore it to the - // default pref when a network link change occurs. - CheckURIPrefs(); + if (!strcmp(aTopic, NS_NETWORK_LINK_TOPIC)) { + if (NS_ConvertUTF16toUTF8(aData).EqualsLiteral( + NS_NETWORK_LINK_DATA_DOWN)) { + mConfirmation.RecordEvent("network-change"); + } + if (mURISetByDetection) { + // If the URI was set via SetDetectedTrrURI we need to restore it to the + // default pref when a network link change occurs. + CheckURIPrefs(); + } } } else if (!strcmp(aTopic, "xpcom-shutdown-threads")) { + // If a confirmation is still in progress we record the event. + // Since there should be no more confirmations after this, the shutdown + // reason would not really be recorded in telemetry. + mConfirmation.RecordEvent("shutdown"); + if (sTRRBackgroundThread) { nsCOMPtr thread; { @@ -656,32 +672,48 @@ void TRRService::RebuildSuffixList(nsTArray&& aSuffixList) { } } -void TRRService::MaybeConfirm() { +void TRRService::MaybeConfirm(const char* aReason) { MutexAutoLock lock(mLock); - MaybeConfirm_locked(); + MaybeConfirm_locked(aReason); } -void TRRService::MaybeConfirm_locked() { +void TRRService::MaybeConfirm_locked(const char* aReason) { mLock.AssertCurrentThreadOwns(); - if (mMode == nsIDNSService::MODE_TRROFF || mConfirmer || - mConfirmationState != CONFIRM_TRYING) { + + if (mMode == nsIDNSService::MODE_TRROFF || mConfirmation.mTask || + mConfirmation.mState != CONFIRM_TRYING) { LOG( - ("TRRService:MaybeConfirm mode=%d, mConfirmer=%p " - "mConfirmationState=%d\n", - (int)mMode, (void*)mConfirmer, (int)mConfirmationState)); + ("TRRService:MaybeConfirm mode=%d, mConfirmation.mTask=%p " + "mConfirmation.mState=%d\n", + (int)mMode, (void*)mConfirmation.mTask, (int)mConfirmation.mState)); return; } if (mConfirmationNS.Equals("skip") || mMode == nsIDNSService::MODE_TRRONLY) { LOG(("TRRService starting confirmation test %s SKIPPED\n", mPrivateURI.get())); - mConfirmationState = CONFIRM_OK; + mConfirmation.mState = CONFIRM_OK; } else { LOG(("TRRService starting confirmation test %s %s\n", mPrivateURI.get(), mConfirmationNS.get())); - mConfirmer = new TRR(this, mConfirmationNS, TRRTYPE_NS, ""_ns, false); - mConfirmer->SetTimeout(StaticPrefs::network_trr_confirmation_timeout_ms()); - DispatchTRRRequestInternal(mConfirmer, false); + + mConfirmation.mTask = + new TRR(this, mConfirmationNS, TRRTYPE_NS, ""_ns, false); + mConfirmation.mTask->SetTimeout( + StaticPrefs::network_trr_confirmation_timeout_ms()); + + if (mLinkService) { + mLinkService->GetNetworkID(mConfirmation.mNetworkId); + } + + if (mConfirmation.mFirstRequestTime.IsNull()) { + mConfirmation.mFirstRequestTime = TimeStamp::Now(); + } + if (mConfirmation.mTrigger.IsEmpty()) { + mConfirmation.mTrigger.Assign(aReason); + } + + DispatchTRRRequestInternal(mConfirmation.mTask, false); } } @@ -866,12 +898,12 @@ void TRRService::AddToBlocklist(const nsACString& aHost, NS_IMETHODIMP TRRService::Notify(nsITimer* aTimer) { - if (aTimer == mRetryConfirmTimer) { - mRetryConfirmTimer = nullptr; - if (mConfirmationState == CONFIRM_FAILED) { + if (aTimer == mConfirmation.mTimer) { + mConfirmation.mTimer = nullptr; + if (mConfirmation.mState == CONFIRM_FAILED) { LOG(("TRRService retry NS of %s\n", mConfirmationNS.get())); - mConfirmationState = CONFIRM_TRYING; - MaybeConfirm(); + mConfirmation.mState = CONFIRM_TRYING; + MaybeConfirm("retry"); } } else { MOZ_CRASH("Unknown timer"); @@ -892,25 +924,129 @@ void TRRService::TRRIsOkay(enum TrrOkay aReason) { ? Telemetry::LABELS_DNS_TRR_SUCCESS2::Timeout : Telemetry::LABELS_DNS_TRR_SUCCESS2::Bad)); if (aReason == OKAY_NORMAL) { - mTRRFailures = 0; + mConfirmation.mTRRFailures = 0; } else if ((mMode == nsIDNSService::MODE_TRRFIRST) && - (mConfirmationState == CONFIRM_OK)) { + (mConfirmation.mState == CONFIRM_OK)) { // only count failures while in OK state - uint32_t fails = ++mTRRFailures; + uint32_t fails = ++mConfirmation.mTRRFailures; if (fails >= StaticPrefs::network_trr_max_fails()) { LOG(("TRRService goes FAILED after %u failures in a row\n", fails)); - mConfirmationState = CONFIRM_FAILED; + mConfirmation.mState = CONFIRM_FAILED; + mConfirmation.mTrigger.Assign("failed-lookups"); // Fire off a timer and start re-trying the NS domain again - NS_NewTimerWithCallback(getter_AddRefs(mRetryConfirmTimer), this, - mRetryConfirmInterval, nsITimer::TYPE_ONE_SHOT); - mTRRFailures = 0; // clear it again + NS_NewTimerWithCallback(getter_AddRefs(mConfirmation.mTimer), this, + mConfirmation.mRetryInterval, + nsITimer::TYPE_ONE_SHOT); + mConfirmation.mTRRFailures = 0; // clear it again } } } +void TRRService::ConfirmationContext::RecordEvent(const char* aReason) { + // Reset the confirmation context attributes + // Only resets the attributes that we keep for telemetry purposes. + auto reset = [&]() { + mAttemptCount = 0; + mNetworkId.Truncate(); + mFirstRequestTime = TimeStamp(); + mContextChangeReason.Assign(aReason); + mTrigger.Truncate(); + + mRetryInterval = StaticPrefs::network_trr_retry_timeout_ms(); + }; + + if (mAttemptCount == 0) { + // XXX: resetting everything might not be the best thing here, even if the + // context changes, because there might still be a confirmation pending. + // But cancelling and retrying that confirmation might just make the whole + // confirmation longer for no reason. + reset(); + return; + } + + Telemetry::EventID eventType = + Telemetry::EventID::NetworkDns_Trrconfirmation_Context; + + nsAutoCString results; + static_assert(RESULTS_SIZE < 64); + + // mResults is a circular buffer ending at mAttemptCount + if (mAttemptCount <= RESULTS_SIZE) { + // We have fewer attempts than the size of the buffer, so all of the + // results are in the buffer. + results.Append(nsDependentCSubstring(mResults, mAttemptCount)); + } else { + // More attempts than the buffer size. + // That means past RESULTS_SIZE attempts in order are + // [posInResults .. end-of-buffer) + [start-of-buffer .. posInResults) + uint32_t posInResults = mAttemptCount % RESULTS_SIZE; + + results.Append(nsDependentCSubstring(mResults + posInResults, + RESULTS_SIZE - posInResults)); + results.Append(nsDependentCSubstring(mResults, posInResults)); + } + + auto extra = Some>({ + Telemetry::EventExtraEntry{"trigger"_ns, mTrigger}, + Telemetry::EventExtraEntry{"contextReason"_ns, mContextChangeReason}, + Telemetry::EventExtraEntry{"attemptCount"_ns, + nsPrintfCString("%u", mAttemptCount)}, + Telemetry::EventExtraEntry{"results"_ns, results}, + Telemetry::EventExtraEntry{ + "time"_ns, + nsPrintfCString( + "%f", + !mFirstRequestTime.IsNull() + ? (TimeStamp::Now() - mFirstRequestTime).ToMilliseconds() + : 0.0)}, + Telemetry::EventExtraEntry{"networkID"_ns, mNetworkId}, + Telemetry::EventExtraEntry{"captivePortal"_ns, + nsPrintfCString("%i", mCaptivePortalStatus)}, + }); + + ConfirmationState state = mState; + Telemetry::RecordEvent(eventType, mozilla::Some(nsPrintfCString("%u", state)), + extra); + + reset(); +} + +void TRRService::ConfirmationContext::RequestCompleted( + nsresult aLookupStatus, nsresult aChannelStatus) { + auto statusToChar = [aLookupStatus, aChannelStatus]() -> char { + if (aChannelStatus == NS_OK) { + // Return + if confirmation was OK, or - if confirmation failed + return aLookupStatus == NS_OK ? '+' : '-'; + } + + switch (aChannelStatus) { + case NS_ERROR_NET_TIMEOUT_EXTERNAL: + // TRR timeout expired + return 't'; + case NS_ERROR_UNKNOWN_HOST: + // TRRServiceChannel failed to due to unresolved host + return 'd'; + default: + break; + } + + // The error is a network error + if (NS_ERROR_GET_MODULE(aChannelStatus) == NS_ERROR_MODULE_NETWORK) { + return 'n'; + } + + // Some other kind of failure. + return '?'; + }; + + mResults[mAttemptCount % RESULTS_SIZE] = statusToChar(); + mAttemptCount++; +} + AHostResolver::LookupStatus TRRService::CompleteLookup( nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb, - const nsACString& aOriginSuffix, nsHostRecord::TRRSkippedReason aReason) { + const nsACString& aOriginSuffix, nsHostRecord::TRRSkippedReason aReason, + TRR* aTRRRequest) { // this is an NS check for the TRR blocklist or confirmationNS check MOZ_ASSERT_IF(XRE_IsParentProcess(), NS_IsMainThread() || IsOnTRRThread()); @@ -923,35 +1059,44 @@ AHostResolver::LookupStatus TRRService::CompleteLookup( #ifdef DEBUG { MutexAutoLock lock(mLock); - MOZ_ASSERT(!mConfirmer || (mConfirmationState == CONFIRM_TRYING)); + MOZ_ASSERT(!mConfirmation.mTask || + (mConfirmation.mState == CONFIRM_TRYING)); } #endif - if (mConfirmationState == CONFIRM_TRYING) { + if (mConfirmation.mState == CONFIRM_TRYING) { + mConfirmation.RequestCompleted(status, aTRRRequest->ChannelStatus()); + { MutexAutoLock lock(mLock); - MOZ_ASSERT(mConfirmer); - mConfirmationState = NS_SUCCEEDED(status) ? CONFIRM_OK : CONFIRM_FAILED; + MOZ_ASSERT(mConfirmation.mTask); + mConfirmation.mState = NS_SUCCEEDED(status) ? CONFIRM_OK : CONFIRM_FAILED; LOG(("TRRService finishing confirmation test %s %d %X\n", - mPrivateURI.get(), (int)mConfirmationState, (unsigned int)status)); - mConfirmer = nullptr; - - if (mConfirmationState == CONFIRM_OK) { - // A fresh confirmation means previous blocked entries might not - // be valid anymore. - auto bl = mTRRBLStorage.Lock(); - bl->Clear(); - } + mPrivateURI.get(), (int)mConfirmation.mState, (unsigned int)status)); + mConfirmation.mTask = nullptr; } - if (mConfirmationState == CONFIRM_FAILED) { - // retry failed NS confirmation - NS_NewTimerWithCallback(getter_AddRefs(mRetryConfirmTimer), this, - mRetryConfirmInterval, nsITimer::TYPE_ONE_SHOT); - if (mRetryConfirmInterval < 64000) { - // double the interval up to this point - mRetryConfirmInterval *= 2; - } + + if (mConfirmation.mState == CONFIRM_OK) { + mConfirmation.mRetryInterval = + StaticPrefs::network_trr_retry_timeout_ms(); + + // Record event and start new confirmation context + mConfirmation.RecordEvent("success"); + + // A fresh confirmation means previous blocked entries might not + // be valid anymore. + auto bl = mTRRBLStorage.Lock(); + bl->Clear(); } else { - mRetryConfirmInterval = StaticPrefs::network_trr_retry_timeout_ms(); + MOZ_ASSERT(mConfirmation.mState == CONFIRM_FAILED); + + // retry failed NS confirmation + NS_NewTimerWithCallback(getter_AddRefs(mConfirmation.mTimer), this, + mConfirmation.mRetryInterval, + nsITimer::TYPE_ONE_SHOT); + if (mConfirmation.mRetryInterval < 64000) { + // double the interval up to this point + mConfirmation.mRetryInterval *= 2; + } } if (mMode != nsIDNSService::MODE_TRRONLY) { @@ -959,7 +1104,7 @@ AHostResolver::LookupStatus TRRService::CompleteLookup( // confirmation in trr-first mode Telemetry::Accumulate(Telemetry::DNS_TRR_NS_VERFIFIED2, TRRService::AutoDetectedKey(), - (mConfirmationState == CONFIRM_OK)); + (mConfirmation.mState == CONFIRM_OK)); } return LOOKUP_OK; diff --git a/netwerk/dns/TRRService.h b/netwerk/dns/TRRService.h index 19c91403e996..2bfe20f5b559 100644 --- a/netwerk/dns/TRRService.h +++ b/netwerk/dns/TRRService.h @@ -13,6 +13,7 @@ #include "nsWeakReference.h" #include "ODoHService.h" #include "TRRServiceBase.h" +#include "nsICaptivePortalService.h" class nsDNSService; class nsIPrefBranch; @@ -39,7 +40,7 @@ class TRRService : public TRRServiceBase, nsresult Init(); nsresult Start(); bool Enabled(nsIRequest::TRRMode aRequestMode = nsIRequest::TRR_DEFAULT_MODE); - bool IsConfirmed() { return mConfirmationState == CONFIRM_OK; } + bool IsConfirmed() { return mConfirmation.mState == CONFIRM_OK; } bool DisableIPv6() { return mDisableIPv6; } nsresult GetURI(nsACString& result); @@ -48,7 +49,8 @@ class TRRService : public TRRServiceBase, LookupStatus CompleteLookup(nsHostRecord*, nsresult, mozilla::net::AddrInfo*, bool pb, const nsACString& aOriginSuffix, - nsHostRecord::TRRSkippedReason aReason) override; + nsHostRecord::TRRSkippedReason aReason, + TRR* aTrrRequest) override; LookupStatus CompleteLookupByType(nsHostRecord*, nsresult, mozilla::net::TypeRecordResultType&, uint32_t, bool pb) override; @@ -85,8 +87,8 @@ class TRRService : public TRRServiceBase, nsresult ReadPrefs(const char* name); void GetPrefBranch(nsIPrefBranch** result); - void MaybeConfirm(); - void MaybeConfirm_locked(); + void MaybeConfirm(const char* aReason); + void MaybeConfirm_locked(const char* aReason); friend class ::nsDNSService; void SetDetectedTrrURI(const nsACString& aURI); @@ -139,13 +141,53 @@ class TRRService : public TRRServiceBase, CONFIRM_OK = 2, CONFIRM_FAILED = 3 }; - Atomic mConfirmationState; - RefPtr mConfirmer; - nsCOMPtr mRetryConfirmTimer; - uint32_t mRetryConfirmInterval; // milliseconds until retry - Atomic mTRRFailures; + + class ConfirmationContext { + static const size_t RESULTS_SIZE = 32; + + public: + Atomic mState; + RefPtr mTask; + nsCOMPtr mTimer; + uint32_t mRetryInterval = 125; // milliseconds until retry + // The number of TRR requests that failed in a row. + Atomic mTRRFailures; + + // The number of confirmation retries. + uint32_t mAttemptCount = 0; + + // The results of past confirmation attempts. + // This is circular buffer ending at mAttemptCount. + char mResults[RESULTS_SIZE] = {0}; + + // Time when first confirmation started. Needed so we can + // record the time from start to confirmed. + TimeStamp mFirstRequestTime; + // The network ID at the start of the last confirmation attempt + nsCString mNetworkId; + // Captive portal status at the time of recording. + int32_t mCaptivePortalStatus = nsICaptivePortalService::UNKNOWN; + + // The reason the confirmation context changed. + nsCString mContextChangeReason; + + // What triggered the confirmation + nsCString mTrigger; + + // Called when a confirmation completes successfully or when the + // confirmation context changes. + void RecordEvent(const char* aReason); + + // Called when a confirmation request is completed. The status is recorded + // in the results. + void RequestCompleted(nsresult aLookupStatus, nsresult aChannelStatus); + }; + + ConfirmationContext mConfirmation; + bool mParentalControlEnabled; RefPtr mODoHService; + nsCOMPtr mLinkService; }; extern TRRService* gTRRService; diff --git a/netwerk/dns/nsHostResolver.cpp b/netwerk/dns/nsHostResolver.cpp index ff3bd9e89d05..1db61e586a23 100644 --- a/netwerk/dns/nsHostResolver.cpp +++ b/netwerk/dns/nsHostResolver.cpp @@ -754,7 +754,7 @@ void nsHostResolver::ClearPendingQueue( rec->Cancel(); if (rec->IsAddrRecord()) { CompleteLookup(rec, NS_ERROR_ABORT, nullptr, rec->pb, rec->originSuffix, - rec->mTRRTRRSkippedReason); + rec->mTRRTRRSkippedReason, nullptr); } else { mozilla::net::TypeRecordResultType empty(Nothing{}); CompleteLookupByType(rec, NS_ERROR_ABORT, empty, 0, rec->pb); @@ -1865,7 +1865,8 @@ void nsHostResolver::AddToEvictionQ(nsHostRecord* rec) { // returns LOOKUP_RESOLVEAGAIN, but only if 'status' is not NS_ERROR_ABORT. nsHostResolver::LookupStatus nsHostResolver::CompleteLookup( nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb, - const nsACString& aOriginsuffix, nsHostRecord::TRRSkippedReason aReason) { + const nsACString& aOriginsuffix, nsHostRecord::TRRSkippedReason aReason, + mozilla::net::TRR* aTRRRequest) { MutexAutoLock lock(mLock); MOZ_ASSERT(rec); MOZ_ASSERT(rec->pb == pb); @@ -2231,9 +2232,9 @@ void nsHostResolver::ThreadFunc() { LOG1(("DNS lookup thread - lookup completed for host [%s]: %s.\n", rec->host.get(), ai ? "success" : "failure: unknown host")); - if (LOOKUP_RESOLVEAGAIN == CompleteLookup(rec, status, ai, rec->pb, - rec->originSuffix, - rec->mTRRTRRSkippedReason)) { + if (LOOKUP_RESOLVEAGAIN == + CompleteLookup(rec, status, ai, rec->pb, rec->originSuffix, + rec->mTRRTRRSkippedReason, nullptr)) { // leave 'rec' assigned and loop to make a renewed host resolve LOG(("DNS lookup thread - Re-resolving host [%s].\n", rec->host.get())); } else { diff --git a/netwerk/dns/nsHostResolver.h b/netwerk/dns/nsHostResolver.h index 74c3a3301ba1..c255b97185af 100644 --- a/netwerk/dns/nsHostResolver.h +++ b/netwerk/dns/nsHostResolver.h @@ -421,10 +421,11 @@ class AHostResolver { LOOKUP_RESOLVEAGAIN, }; - virtual LookupStatus CompleteLookup( - nsHostRecord*, nsresult, mozilla::net::AddrInfo*, bool pb, - const nsACString& aOriginsuffix, - nsHostRecord::TRRSkippedReason aReason) = 0; + virtual LookupStatus CompleteLookup(nsHostRecord*, nsresult, + mozilla::net::AddrInfo*, bool pb, + const nsACString& aOriginsuffix, + nsHostRecord::TRRSkippedReason aReason, + mozilla::net::TRR*) = 0; virtual LookupStatus CompleteLookupByType( nsHostRecord*, nsresult, mozilla::net::TypeRecordResultType& aResult, uint32_t aTtl, bool pb) = 0; @@ -551,7 +552,8 @@ class nsHostResolver : public nsISupports, public AHostResolver { LookupStatus CompleteLookup(nsHostRecord*, nsresult, mozilla::net::AddrInfo*, bool pb, const nsACString& aOriginsuffix, - nsHostRecord::TRRSkippedReason aReason) override; + nsHostRecord::TRRSkippedReason aReason, + mozilla::net::TRR* aTRRRequest) override; LookupStatus CompleteLookupByType(nsHostRecord*, nsresult, mozilla::net::TypeRecordResultType& aResult, uint32_t aTtl, bool pb) override;