TRRService.cpp (45999B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 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/. */ 5 6 #include "nsCharSeparatedTokenizer.h" 7 #include "nsComponentManagerUtils.h" 8 #include "nsDirectoryServiceUtils.h" 9 #include "nsHttpConnectionInfo.h" 10 #include "nsHttpHandler.h" 11 #include "nsICaptivePortalService.h" 12 #include "nsIFile.h" 13 #include "nsINetworkLinkService.h" 14 #include "nsIObserverService.h" 15 #include "nsIOService.h" 16 #include "nsNetUtil.h" 17 #include "nsStandardURL.h" 18 #include "TRR.h" 19 #include "TRRService.h" 20 21 #include "mozilla/Preferences.h" 22 #include "mozilla/StaticPrefs_network.h" 23 #include "mozilla/glean/NetwerkDnsMetrics.h" 24 #include "mozilla/Telemetry.h" 25 #include "mozilla/Tokenizer.h" 26 #include "mozilla/dom/ContentParent.h" 27 #include "mozilla/glean/NetwerkMetrics.h" 28 #include "mozilla/net/NeckoParent.h" 29 #include "mozilla/net/TRRServiceChild.h" 30 #include "mozilla/ProfilerMarkers.h" 31 // Put DNSLogging.h at the end to avoid LOG being overwritten by other headers. 32 #include "DNSLogging.h" 33 34 static const char kOpenCaptivePortalLoginEvent[] = "captive-portal-login"; 35 static const char kClearPrivateData[] = "clear-private-data"; 36 static const char kPurge[] = "browser:purge-session-history"; 37 38 #define TRR_PREF_PREFIX "network.trr." 39 #define TRR_PREF(x) TRR_PREF_PREFIX x 40 41 namespace mozilla::net { 42 43 StaticRefPtr<nsIThread> sTRRBackgroundThread; 44 static Atomic<TRRService*> sTRRServicePtr; 45 46 static Atomic<size_t, Relaxed> sDomainIndex(0); 47 static Atomic<size_t, Relaxed> sCurrentTRRModeIndex(0); 48 49 constexpr nsLiteralCString kTRRDomains[3][7] = { 50 // clang-format off 51 { 52 // When mode is 0, the provider key has no postfix. 53 "(other)"_ns, 54 "mozilla.cloudflare-dns.com"_ns, 55 "firefox.dns.nextdns.io"_ns, 56 "private.canadianshield.cira.ca"_ns, 57 "doh.xfinity.com"_ns, // Steered clients 58 "dns.shaw.ca"_ns, // Steered clients 59 "dooh.cloudflare-dns.com"_ns, // DNS over Oblivious HTTP 60 }, 61 { 62 "(other)_2"_ns, 63 "mozilla.cloudflare-dns.com_2"_ns, 64 "firefox.dns.nextdns.io_2"_ns, 65 "private.canadianshield.cira.ca_2"_ns, 66 "doh.xfinity.com_2"_ns, // Steered clients 67 "dns.shaw.ca_2"_ns, // Steered clients 68 "dooh.cloudflare-dns.com_2"_ns, // DNS over Oblivious HTTP 69 }, 70 { 71 "(other)_3"_ns, 72 "mozilla.cloudflare-dns.com_3"_ns, 73 "firefox.dns.nextdns.io_3"_ns, 74 "private.canadianshield.cira.ca_3"_ns, 75 "doh.xfinity.com_3"_ns, // Steered clients 76 "dns.shaw.ca_3"_ns, // Steered clients 77 "dooh.cloudflare-dns.com_3"_ns, // DNS over Oblivious HTTP 78 }, 79 // clang-format on 80 }; 81 82 // static 83 void TRRService::SetCurrentTRRMode(nsIDNSService::ResolverMode aMode) { 84 // A table to map ResolverMode to the row of kTRRDomains. 85 // When the aMode is 2, we use kTRRDomains[1] as provider keys. When aMode is 86 // 3, we use kTRRDomains[2]. Otherwise, we kTRRDomains[0] is used. 87 static const uint32_t index[] = {0, 0, 1, 2, 0, 0}; 88 if (aMode > nsIDNSService::MODE_TRROFF) { 89 aMode = nsIDNSService::MODE_TRROFF; 90 } 91 sCurrentTRRModeIndex = index[static_cast<size_t>(aMode)]; 92 } 93 94 // static 95 void TRRService::SetProviderDomain(const nsACString& aTRRDomain) { 96 sDomainIndex = 0; 97 for (size_t i = 1; i < std::size(kTRRDomains[0]); i++) { 98 if (aTRRDomain.Equals(kTRRDomains[0][i])) { 99 sDomainIndex = i; 100 break; 101 } 102 } 103 } 104 105 const nsCString& TRRProviderKey() { return TRRService::ProviderKey(); } 106 107 // static 108 const nsCString& TRRService::ProviderKey() { 109 return kTRRDomains[sCurrentTRRModeIndex][sDomainIndex]; 110 } 111 112 NS_IMPL_ISUPPORTS_INHERITED(TRRService, TRRServiceBase, nsIObserver, 113 nsISupportsWeakReference) 114 115 NS_IMPL_ADDREF_USING_AGGREGATOR(TRRService::ConfirmationContext, OwningObject()) 116 NS_IMPL_RELEASE_USING_AGGREGATOR(TRRService::ConfirmationContext, 117 OwningObject()) 118 NS_IMPL_QUERY_INTERFACE(TRRService::ConfirmationContext, nsITimerCallback, 119 nsINamed) 120 121 TRRService::TRRService() : mLock("TRRService") { 122 MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); 123 } 124 125 // static 126 TRRService* TRRService::Get() { return sTRRServicePtr; } 127 128 // static 129 void TRRService::AddObserver(nsIObserver* aObserver, 130 nsIObserverService* aObserverService) { 131 nsCOMPtr<nsIObserverService> observerService; 132 if (aObserverService) { 133 observerService = aObserverService; 134 } else { 135 observerService = mozilla::services::GetObserverService(); 136 } 137 138 if (observerService) { 139 observerService->AddObserver(aObserver, NS_CAPTIVE_PORTAL_CONNECTIVITY, 140 true); 141 observerService->AddObserver(aObserver, kOpenCaptivePortalLoginEvent, true); 142 observerService->AddObserver(aObserver, kClearPrivateData, true); 143 observerService->AddObserver(aObserver, kPurge, true); 144 observerService->AddObserver(aObserver, NS_NETWORK_LINK_TOPIC, true); 145 observerService->AddObserver(aObserver, NS_DNS_SUFFIX_LIST_UPDATED_TOPIC, 146 true); 147 observerService->AddObserver(aObserver, "xpcom-shutdown-threads", true); 148 } 149 } 150 151 // static 152 bool TRRService::CheckCaptivePortalIsPassed() { 153 bool result = false; 154 nsCOMPtr<nsICaptivePortalService> captivePortalService = 155 do_GetService(NS_CAPTIVEPORTAL_CID); 156 if (captivePortalService) { 157 int32_t captiveState; 158 MOZ_ALWAYS_SUCCEEDS(captivePortalService->GetState(&captiveState)); 159 160 if ((captiveState == nsICaptivePortalService::UNLOCKED_PORTAL) || 161 (captiveState == nsICaptivePortalService::NOT_CAPTIVE)) { 162 result = true; 163 } 164 LOG(("TRRService::Init mCaptiveState=%d mCaptiveIsPassed=%d\n", 165 captiveState, (int)result)); 166 } 167 168 return result; 169 } 170 171 nsresult TRRService::Init(bool aNativeHTTPSQueryEnabled) { 172 MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); 173 if (mInitialized) { 174 return NS_OK; 175 } 176 mInitialized = true; 177 178 AddObserver(this); 179 180 nsCOMPtr<nsIPrefBranch> prefBranch; 181 GetPrefBranch(getter_AddRefs(prefBranch)); 182 if (prefBranch) { 183 prefBranch->AddObserver(TRR_PREF_PREFIX, this, true); 184 prefBranch->AddObserver(kRolloutURIPref, this, true); 185 prefBranch->AddObserver(kRolloutModePref, this, true); 186 } 187 188 sTRRServicePtr = this; 189 190 mNativeHTTPSQueryEnabled = aNativeHTTPSQueryEnabled; 191 ReadPrefs(nullptr); 192 mConfirmation.HandleEvent(ConfirmationEvent::Init); 193 194 if (XRE_IsParentProcess()) { 195 mCaptiveIsPassed = CheckCaptivePortalIsPassed(); 196 197 mParentalControlEnabled = GetParentalControlsEnabledInternal(); 198 199 mLinkService = do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID); 200 if (mLinkService) { 201 nsTArray<nsCString> suffixList; 202 mLinkService->GetDnsSuffixList(suffixList); 203 RebuildSuffixList(std::move(suffixList)); 204 } 205 206 nsCOMPtr<nsIThread> thread; 207 if (NS_FAILED( 208 NS_NewNamedThread("TRR Background", getter_AddRefs(thread)))) { 209 NS_WARNING("NS_NewNamedThread failed!"); 210 return NS_ERROR_FAILURE; 211 } 212 213 sTRRBackgroundThread = thread; 214 } 215 216 LOG(("Initialized TRRService\n")); 217 return NS_OK; 218 } 219 220 // static 221 bool TRRService::GetParentalControlsEnabledInternal() { 222 return nsHttpHandler::GetParentalControlsEnabled(); 223 } 224 225 // static, for testing purposes only 226 bool TRRService::ReloadParentalControlsEnabled() { 227 nsHttpHandler::UpdateParentalControlsEnabled(true /* wait for completion */); 228 return nsHttpHandler::GetParentalControlsEnabled(); 229 } 230 231 void TRRService::SetDetectedTrrURI(const nsACString& aURI) { 232 LOG(("SetDetectedTrrURI(%s", nsPromiseFlatCString(aURI).get())); 233 // If the user has set a custom URI then we don't want to override that. 234 // If the URI is set via doh-rollout.uri, mURIPref will be empty 235 // (see TRRServiceBase::OnTRRURIChange) 236 if (!mURIPref.IsEmpty()) { 237 LOG(("Already has user value. Not setting URI")); 238 return; 239 } 240 241 if (StaticPrefs::network_trr_use_ohttp()) { 242 LOG(("No autodetection when using OHTTP")); 243 return; 244 } 245 246 mURISetByDetection = MaybeSetPrivateURI(aURI); 247 } 248 249 bool TRRService::Enabled(nsIRequest::TRRMode aRequestMode) { 250 if (mMode == nsIDNSService::MODE_TRROFF || 251 aRequestMode == nsIRequest::TRR_DISABLED_MODE) { 252 LOG(("TRR service not enabled - off or disabled")); 253 return false; 254 } 255 256 // If already confirmed, service is enabled. 257 if (mConfirmation.State() == CONFIRM_OK || 258 aRequestMode == nsIRequest::TRR_ONLY_MODE) { 259 LOG(("TRR service enabled - confirmed or trr_only request")); 260 return true; 261 } 262 263 // If this is a TRR_FIRST request but the resolver has a different mode, 264 // just go ahead and let it try to use TRR. 265 if (aRequestMode == nsIRequest::TRR_FIRST_MODE && 266 mMode != nsIDNSService::MODE_TRRFIRST) { 267 LOG(("TRR service enabled - trr_first request")); 268 return true; 269 } 270 271 // In TRR_ONLY_MODE / confirmationNS == "skip" we don't try to confirm. 272 if (mConfirmation.State() == CONFIRM_DISABLED) { 273 LOG(("TRRService service enabled - confirmation is disabled")); 274 return true; 275 } 276 277 LOG(("TRRService::Enabled mConfirmation.mState=%d mCaptiveIsPassed=%d\n", 278 mConfirmation.State(), (int)mCaptiveIsPassed)); 279 280 if (StaticPrefs::network_trr_wait_for_confirmation()) { 281 return mConfirmation.State() == CONFIRM_OK; 282 } 283 284 if (StaticPrefs::network_trr_attempt_when_retrying_confirmation()) { 285 return mConfirmation.State() == CONFIRM_OK || 286 mConfirmation.State() == CONFIRM_TRYING_OK || 287 mConfirmation.State() == CONFIRM_TRYING_FAILED; 288 } 289 290 return mConfirmation.State() == CONFIRM_OK || 291 mConfirmation.State() == CONFIRM_TRYING_OK; 292 } 293 294 void TRRService::GetPrefBranch(nsIPrefBranch** result) { 295 MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); 296 *result = nullptr; 297 CallGetService(NS_PREFSERVICE_CONTRACTID, result); 298 } 299 300 bool TRRService::MaybeSetPrivateURI(const nsACString& aURI) { 301 bool clearCache = false; 302 nsAutoCString newURI(aURI); 303 LOG(("MaybeSetPrivateURI(%s)", newURI.get())); 304 305 ProcessURITemplate(newURI); 306 { 307 MutexAutoLock lock(mLock); 308 if (mPrivateURI.Equals(newURI)) { 309 return false; 310 } 311 312 if (!mPrivateURI.IsEmpty()) { 313 LOG(("TRRService clearing blocklist because of change in uri service\n")); 314 auto bl = mTRRBLStorage.Lock(); 315 bl->Clear(); 316 clearCache = true; 317 } 318 319 nsAutoCString host; 320 321 nsCOMPtr<nsIURI> url; 322 if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(url), newURI))) { 323 url->GetHost(host); 324 } 325 326 SetProviderDomain(host); 327 328 mPrivateURI = newURI; 329 330 // Notify the content processes of the new TRR 331 for (auto* cp : 332 dom::ContentParent::AllProcesses(dom::ContentParent::eLive)) { 333 PNeckoParent* neckoParent = 334 SingleManagedOrNull(cp->ManagedPNeckoParent()); 335 if (!neckoParent) { 336 continue; 337 } 338 (void)neckoParent->SendSetTRRDomain(host); 339 } 340 341 AsyncCreateTRRConnectionInfo(mPrivateURI); 342 343 // The URI has changed. We should trigger a new confirmation immediately. 344 // We must do this here because the URI could also change because of 345 // steering. 346 mConfirmationTriggered = 347 mConfirmation.HandleEvent(ConfirmationEvent::URIChange, lock); 348 } 349 350 // Clear the cache because we changed the URI 351 if (clearCache) { 352 ClearEntireCache(); 353 } 354 355 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 356 if (obs) { 357 obs->NotifyObservers(nullptr, NS_NETWORK_TRR_URI_CHANGED_TOPIC, nullptr); 358 } 359 return true; 360 } 361 362 nsresult TRRService::ReadPrefs(const char* name) { 363 MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); 364 365 // Whenever a pref change occurs that would cause us to clear the cache 366 // we set this to true then do it at the end of the method. 367 bool clearEntireCache = false; 368 369 if (!name || !strcmp(name, TRR_PREF("mode")) || 370 !strcmp(name, kRolloutModePref)) { 371 nsIDNSService::ResolverMode prevMode = Mode(); 372 373 OnTRRModeChange(); 374 // When the TRR service gets disabled we should purge the TRR cache to 375 // make sure we don't use any of the cached entries on a network where 376 // they are invalid - for example after turning on a VPN. 377 if (TRR_DISABLED(Mode()) && !TRR_DISABLED(prevMode)) { 378 clearEntireCache = true; 379 } 380 } 381 if (!name || !strcmp(name, TRR_PREF("uri")) || 382 !strcmp(name, TRR_PREF("default_provider_uri")) || 383 !strcmp(name, kRolloutURIPref) || !strcmp(name, TRR_PREF("ohttp.uri")) || 384 !strcmp(name, TRR_PREF("use_ohttp"))) { 385 OnTRRURIChange(); 386 } 387 if (!name || !strcmp(name, TRR_PREF("credentials"))) { 388 MutexAutoLock lock(mLock); 389 Preferences::GetCString(TRR_PREF("credentials"), mPrivateCred); 390 } 391 if (!name || !strcmp(name, TRR_PREF("confirmationNS"))) { 392 MutexAutoLock lock(mLock); 393 Preferences::GetCString(TRR_PREF("confirmationNS"), mConfirmationNS); 394 LOG(("confirmationNS = %s", mConfirmationNS.get())); 395 } 396 if (!name || !strcmp(name, TRR_PREF("bootstrapAddr"))) { 397 MutexAutoLock lock(mLock); 398 Preferences::GetCString(TRR_PREF("bootstrapAddr"), mBootstrapAddr); 399 clearEntireCache = true; 400 } 401 if (!name || !strcmp(name, TRR_PREF("excluded-domains")) || 402 !strcmp(name, TRR_PREF("builtin-excluded-domains"))) { 403 MutexAutoLock lock(mLock); 404 405 mExcludedDomains.Clear(); 406 407 auto parseExcludedDomains = [this](const char* aPrefName) MOZ_REQUIRES( 408 mLock) { 409 nsAutoCString excludedDomains; 410 Preferences::GetCString(aPrefName, excludedDomains); 411 if (excludedDomains.IsEmpty()) { 412 return; 413 } 414 415 for (const nsACString& tokenSubstring : 416 nsCCharSeparatedTokenizerTemplate< 417 NS_IsAsciiWhitespace, nsTokenizerFlags::SeparatorOptional>( 418 excludedDomains, ',') 419 .ToRange()) { 420 nsCString token{tokenSubstring}; 421 LOG(("TRRService::ReadPrefs %s host:[%s]\n", aPrefName, token.get())); 422 mExcludedDomains.Insert(token); 423 } 424 }; 425 426 parseExcludedDomains(TRR_PREF("excluded-domains")); 427 parseExcludedDomains(TRR_PREF("builtin-excluded-domains")); 428 clearEntireCache = true; 429 } 430 431 // if name is null, then we're just now initializing. In that case we don't 432 // need to clear the cache. 433 if (name && clearEntireCache) { 434 ClearEntireCache(); 435 } 436 437 return NS_OK; 438 } 439 440 void TRRService::ClearEntireCache() { 441 if (!StaticPrefs::network_trr_clear_cache_on_pref_change()) { 442 return; 443 } 444 nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID); 445 if (!dns) { 446 return; 447 } 448 dns->ClearCache(true); 449 } 450 451 void TRRService::AddEtcHosts(const nsTArray<nsCString>& aArray) { 452 MutexAutoLock lock(mLock); 453 for (const auto& item : aArray) { 454 LOG(("Adding %s from /etc/hosts to excluded domains", item.get())); 455 mEtcHostsDomains.Insert(item); 456 } 457 } 458 459 void TRRService::ReadEtcHostsFile() { 460 if (!XRE_IsParentProcess()) { 461 return; 462 } 463 464 DoReadEtcHostsFile([](const nsTArray<nsCString>* aArray) -> bool { 465 RefPtr<TRRService> service(sTRRServicePtr); 466 if (service && aArray) { 467 service->AddEtcHosts(*aArray); 468 } 469 return !!service; 470 }); 471 } 472 473 void TRRService::GetURI(nsACString& result) { 474 MutexAutoLock lock(mLock); 475 result = mPrivateURI; 476 } 477 478 nsresult TRRService::GetCredentials(nsCString& result) { 479 MutexAutoLock lock(mLock); 480 result = mPrivateCred; 481 return NS_OK; 482 } 483 484 uint32_t TRRService::GetRequestTimeout() { 485 if (mMode == nsIDNSService::MODE_TRRONLY) { 486 return StaticPrefs::network_trr_request_timeout_mode_trronly_ms(); 487 } 488 489 if (StaticPrefs::network_trr_strict_native_fallback()) { 490 return StaticPrefs::network_trr_strict_fallback_request_timeout_ms(); 491 } 492 493 return StaticPrefs::network_trr_request_timeout_ms(); 494 } 495 496 nsresult TRRService::Start() { 497 MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); 498 if (!mInitialized) { 499 return NS_ERROR_NOT_INITIALIZED; 500 } 501 return NS_OK; 502 } 503 504 TRRService::~TRRService() { 505 MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); 506 LOG(("Exiting TRRService\n")); 507 } 508 509 nsresult TRRService::DispatchTRRRequest(TRR* aTrrRequest) { 510 return DispatchTRRRequestInternal(aTrrRequest, true); 511 } 512 513 nsresult TRRService::DispatchTRRRequestInternal(TRR* aTrrRequest, 514 bool aWithLock) { 515 NS_ENSURE_ARG_POINTER(aTrrRequest); 516 517 nsCOMPtr<nsIThread> thread = MainThreadOrTRRThread(aWithLock); 518 if (!thread) { 519 return NS_ERROR_FAILURE; 520 } 521 522 RefPtr<TRR> trr = aTrrRequest; 523 return thread->Dispatch(trr.forget()); 524 } 525 526 already_AddRefed<nsIThread> TRRService::MainThreadOrTRRThread(bool aWithLock) { 527 if (XRE_IsSocketProcess() || mDontUseTRRThread) { 528 return do_GetMainThread(); 529 } 530 531 nsCOMPtr<nsIThread> thread = aWithLock ? TRRThread() : TRRThread_locked(); 532 return thread.forget(); 533 } 534 535 already_AddRefed<nsIThread> TRRService::TRRThread() { 536 MutexAutoLock lock(mLock); 537 return TRRThread_locked(); 538 } 539 540 already_AddRefed<nsIThread> TRRService::TRRThread_locked() { 541 RefPtr<nsIThread> thread = sTRRBackgroundThread; 542 return thread.forget(); 543 } 544 545 bool TRRService::IsOnTRRThread() { 546 nsCOMPtr<nsIThread> thread; 547 { 548 MutexAutoLock lock(mLock); 549 thread = sTRRBackgroundThread; 550 } 551 if (!thread) { 552 return false; 553 } 554 555 return thread->IsOnCurrentThread(); 556 } 557 558 NS_IMETHODIMP 559 TRRService::Observe(nsISupports* aSubject, const char* aTopic, 560 const char16_t* aData) { 561 MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); 562 LOG(("TRR::Observe() topic=%s\n", aTopic)); 563 if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { 564 // Reset the state of whether a confirmation is triggered, so we can check 565 // if we create a new one after ReadPrefs(). 566 mConfirmationTriggered = false; 567 ReadPrefs(NS_ConvertUTF16toUTF8(aData).get()); 568 { 569 MutexAutoLock lock(mLock); 570 mConfirmation.RecordEvent("pref-change", lock); 571 } 572 573 // We should only trigger a new confirmation if reading the prefs didn't 574 // already trigger one. 575 if (!mConfirmationTriggered) { 576 mConfirmation.HandleEvent(ConfirmationEvent::PrefChange); 577 } 578 } else if (!strcmp(aTopic, kOpenCaptivePortalLoginEvent)) { 579 // We are in a captive portal 580 LOG(("TRRservice in captive portal\n")); 581 mCaptiveIsPassed = false; 582 mConfirmation.SetCaptivePortalStatus( 583 nsICaptivePortalService::LOCKED_PORTAL); 584 } else if (!strcmp(aTopic, NS_CAPTIVE_PORTAL_CONNECTIVITY)) { 585 nsAutoCString data = NS_ConvertUTF16toUTF8(aData); 586 LOG(("TRRservice captive portal was %s\n", data.get())); 587 nsCOMPtr<nsICaptivePortalService> cps = do_QueryInterface(aSubject); 588 if (cps) { 589 mConfirmation.SetCaptivePortalStatus(cps->State()); 590 } 591 592 // If we were previously in a captive portal, this event means we will 593 // need to trigger confirmation again. Otherwise it's just a periodical 594 // captive-portal check that completed and we don't need to react to it. 595 if (!mCaptiveIsPassed) { 596 mConfirmation.HandleEvent(ConfirmationEvent::CaptivePortalConnectivity); 597 } 598 599 mCaptiveIsPassed = true; 600 } else if (!strcmp(aTopic, kClearPrivateData) || !strcmp(aTopic, kPurge)) { 601 // flush the TRR blocklist 602 auto bl = mTRRBLStorage.Lock(); 603 bl->Clear(); 604 } else if (!strcmp(aTopic, NS_DNS_SUFFIX_LIST_UPDATED_TOPIC) || 605 !strcmp(aTopic, NS_NETWORK_LINK_TOPIC)) { 606 // nsINetworkLinkService is only available on parent process. 607 if (XRE_IsParentProcess()) { 608 nsCOMPtr<nsINetworkLinkService> link = do_QueryInterface(aSubject); 609 // The network link service notification normally passes itself as the 610 // subject, but some unit tests will sometimes pass a null subject. 611 if (link) { 612 nsTArray<nsCString> suffixList; 613 link->GetDnsSuffixList(suffixList); 614 RebuildSuffixList(std::move(suffixList)); 615 } 616 } 617 618 if (!strcmp(aTopic, NS_NETWORK_LINK_TOPIC)) { 619 nsAutoCString converted = NS_ConvertUTF16toUTF8(aData); 620 if (converted.EqualsLiteral(NS_NETWORK_LINK_DATA_DOWN)) { 621 MutexAutoLock lock(mLock); 622 mConfirmation.RecordEvent("network-down", lock); 623 } else if (converted.EqualsLiteral(NS_NETWORK_LINK_DATA_CHANGED)) { 624 MutexAutoLock lock(mLock); 625 mConfirmation.RecordEvent("network-change", lock); 626 } 627 628 if (mURISetByDetection) { 629 // If the URI was set via SetDetectedTrrURI we need to restore it to the 630 // default pref when a network link change occurs. 631 CheckURIPrefs(); 632 } 633 634 if (converted.EqualsLiteral(NS_NETWORK_LINK_DATA_UP)) { 635 mConfirmation.HandleEvent(ConfirmationEvent::NetworkUp); 636 } 637 } 638 } else if (!strcmp(aTopic, "xpcom-shutdown-threads")) { 639 mShutdown = true; 640 // If a confirmation is still in progress we record the event. 641 // Since there should be no more confirmations after this, the shutdown 642 // reason would not really be recorded in telemetry. 643 { 644 MutexAutoLock lock(mLock); 645 mConfirmation.RecordEvent("shutdown", lock); 646 } 647 648 if (sTRRBackgroundThread) { 649 nsCOMPtr<nsIThread> thread; 650 thread = sTRRBackgroundThread.get(); 651 sTRRBackgroundThread = nullptr; 652 MOZ_ALWAYS_SUCCEEDS(thread->Shutdown()); 653 sTRRServicePtr = nullptr; 654 } 655 } 656 return NS_OK; 657 } 658 659 void TRRService::RebuildSuffixList(nsTArray<nsCString>&& aSuffixList) { 660 if (!StaticPrefs::network_trr_split_horizon_mitigations() || mShutdown) { 661 return; 662 } 663 664 MutexAutoLock lock(mLock); 665 mDNSSuffixDomains.Clear(); 666 for (const auto& item : aSuffixList) { 667 LOG(("TRRService adding %s to suffix list", item.get())); 668 mDNSSuffixDomains.Insert(item); 669 } 670 } 671 672 void TRRService::ConfirmationContext::SetState( 673 enum ConfirmationState aNewState) { 674 LOG(("ConfirmationContext::SetState %u", uint32_t(aNewState))); 675 mState = aNewState; 676 677 enum ConfirmationState state = mState; 678 if (XRE_IsParentProcess()) { 679 NS_DispatchToMainThread(NS_NewRunnableFunction( 680 "TRRService::ConfirmationContextNotify", [state] { 681 if (nsCOMPtr<nsIObserverService> obs = 682 mozilla::services::GetObserverService()) { 683 auto stateString = 684 [](enum ConfirmationState aState) -> const char16_t* { 685 switch (aState) { 686 case CONFIRM_OFF: 687 return u"CONFIRM_OFF"; 688 case CONFIRM_TRYING_OK: 689 return u"CONFIRM_TRYING_OK"; 690 case CONFIRM_OK: 691 return u"CONFIRM_OK"; 692 case CONFIRM_FAILED: 693 return u"CONFIRM_FAILED"; 694 case CONFIRM_TRYING_FAILED: 695 return u"CONFIRM_TRYING_FAILED"; 696 case CONFIRM_DISABLED: 697 return u"CONFIRM_DISABLED"; 698 } 699 MOZ_ASSERT_UNREACHABLE(); 700 return u""; 701 }; 702 703 obs->NotifyObservers(nullptr, "network:trr-confirmation", 704 stateString(state)); 705 } 706 })); 707 } 708 709 if (XRE_IsParentProcess()) { 710 return; 711 } 712 713 MOZ_ASSERT(XRE_IsSocketProcess()); 714 MOZ_ASSERT(NS_IsMainThread()); 715 716 TRRServiceChild* child = TRRServiceChild::GetSingleton(); 717 if (child && child->CanSend()) { 718 LOG(("TRRService::SendSetConfirmationState")); 719 (void)child->SendSetConfirmationState(mState); 720 } 721 } 722 723 bool TRRService::ConfirmationContext::HandleEvent(ConfirmationEvent aEvent) { 724 MutexAutoLock lock(OwningObject()->mLock); 725 return HandleEvent(aEvent, lock); 726 } 727 728 // We're protected by service->mLock 729 bool TRRService::ConfirmationContext::HandleEvent(ConfirmationEvent aEvent, 730 const MutexAutoLock&) { 731 auto prevAddr = TaskAddr(); 732 TRRService* service = OwningObject(); 733 service->mLock.AssertCurrentThreadOwns(); 734 nsIDNSService::ResolverMode mode = service->Mode(); 735 736 auto resetConfirmation = [&]() { 737 service->mLock.AssertCurrentThreadOwns(); 738 mTask = nullptr; 739 nsCOMPtr<nsITimer> timer = std::move(mTimer); 740 if (timer) { 741 timer->Cancel(); 742 } 743 744 mRetryInterval = StaticPrefs::network_trr_retry_timeout_ms(); 745 mTRRFailures = 0; 746 747 if (TRR_DISABLED(mode)) { 748 LOG(("TRR is disabled. mConfirmation.mState -> CONFIRM_OFF")); 749 SetState(CONFIRM_OFF); 750 return; 751 } 752 753 if (mode == nsIDNSService::MODE_TRRONLY) { 754 LOG(("TRR_ONLY_MODE. mConfirmation.mState -> CONFIRM_DISABLED")); 755 SetState(CONFIRM_DISABLED); 756 return; 757 } 758 759 if (service->mConfirmationNS.Equals("skip"_ns)) { 760 LOG(( 761 "mConfirmationNS == skip. mConfirmation.mState -> CONFIRM_DISABLED")); 762 SetState(CONFIRM_DISABLED); 763 return; 764 } 765 766 // The next call to maybeConfirm will transition to CONFIRM_TRYING_OK 767 LOG(("mConfirmation.mState -> CONFIRM_OK")); 768 SetState(CONFIRM_OK); 769 }; 770 771 auto maybeConfirm = [&](const char* aReason) { 772 service->mLock.AssertCurrentThreadOwns(); 773 if (TRR_DISABLED(mode) || mState == CONFIRM_DISABLED || mTask) { 774 LOG( 775 ("TRRService:MaybeConfirm(%s) mode=%d, mTask=%p " 776 "mState=%d\n", 777 aReason, (int)mode, (void*)mTask, (int)mState)); 778 return; 779 } 780 781 MOZ_ASSERT(mode != nsIDNSService::MODE_TRRONLY, 782 "Confirmation should be disabled"); 783 MOZ_ASSERT(!service->mConfirmationNS.Equals("skip"), 784 "Confirmation should be disabled"); 785 786 LOG(("maybeConfirm(%s) starting confirmation test %s %s\n", aReason, 787 service->mPrivateURI.get(), service->mConfirmationNS.get())); 788 789 MOZ_ASSERT(mState == CONFIRM_OK || mState == CONFIRM_FAILED); 790 791 if (mState == CONFIRM_FAILED) { 792 LOG(("mConfirmation.mState -> CONFIRM_TRYING_FAILED")); 793 SetState(CONFIRM_TRYING_FAILED); 794 } else { 795 LOG(("mConfirmation.mState -> CONFIRM_TRYING_OK")); 796 SetState(CONFIRM_TRYING_OK); 797 } 798 799 nsCOMPtr<nsITimer> timer = std::move(mTimer); 800 if (timer) { 801 timer->Cancel(); 802 } 803 804 MOZ_ASSERT(mode == nsIDNSService::MODE_TRRFIRST, 805 "Should only confirm in TRR first mode"); 806 // Set aUseFreshConnection if TRR lookups are retried 807 // or if confirmation already failed. 808 mTask = new TRR(service, service->mConfirmationNS, TRRTYPE_NS, ""_ns, false, 809 mState == CONFIRM_TRYING_FAILED || 810 StaticPrefs::network_trr_retry_on_recoverable_errors()); 811 mTask->SetTimeout(StaticPrefs::network_trr_confirmation_timeout_ms()); 812 mTask->SetPurpose(TRR::Confirmation); 813 814 if (service->mLinkService) { 815 service->mLinkService->GetNetworkID(mNetworkId); 816 } 817 818 if (mFirstRequestTime.IsNull()) { 819 mFirstRequestTime = TimeStamp::Now(); 820 } 821 if (mTrigger.IsEmpty()) { 822 mTrigger.Assign(aReason); 823 } 824 825 LOG(("Dispatching confirmation task: %p", mTask.get())); 826 service->DispatchTRRRequestInternal(mTask, false); 827 }; 828 829 switch (aEvent) { 830 case ConfirmationEvent::Init: 831 resetConfirmation(); 832 maybeConfirm("context-init"); 833 break; 834 case ConfirmationEvent::PrefChange: 835 resetConfirmation(); 836 maybeConfirm("pref-change"); 837 break; 838 case ConfirmationEvent::ConfirmationRetry: 839 MOZ_ASSERT(mState == CONFIRM_FAILED); 840 if (mState == CONFIRM_FAILED) { 841 maybeConfirm("confirmation-retry"); 842 } 843 break; 844 case ConfirmationEvent::FailedLookups: 845 MOZ_ASSERT(mState == CONFIRM_OK); 846 mTrigger.Assign("failed-lookups"); 847 mFailedLookups = nsDependentCSubstring( 848 mFailureReasons, mTRRFailures % ConfirmationContext::RESULTS_SIZE); 849 maybeConfirm("failed-lookups"); 850 break; 851 case ConfirmationEvent::RetryTRR: 852 MOZ_ASSERT(mState == CONFIRM_OK); 853 maybeConfirm("retry-trr"); 854 break; 855 case ConfirmationEvent::URIChange: 856 resetConfirmation(); 857 maybeConfirm("uri-change"); 858 break; 859 case ConfirmationEvent::CaptivePortalConnectivity: 860 // If we area already confirmed then we're fine. 861 // If there is a confirmation in progress, likely it started before 862 // we had full connectivity, so it may be hanging. We reset and try again. 863 if (mState == CONFIRM_FAILED || mState == CONFIRM_TRYING_FAILED || 864 mState == CONFIRM_TRYING_OK) { 865 resetConfirmation(); 866 maybeConfirm("cp-connectivity"); 867 } 868 break; 869 case ConfirmationEvent::NetworkUp: 870 if (mState != CONFIRM_OK) { 871 resetConfirmation(); 872 maybeConfirm("network-up"); 873 } 874 break; 875 case ConfirmationEvent::ConfirmOK: 876 // Reset confirmation retry timeout to default 877 mRetryInterval = StaticPrefs::network_trr_retry_timeout_ms(); 878 SetState(CONFIRM_OK); 879 mTask = nullptr; 880 break; 881 case ConfirmationEvent::ConfirmFail: 882 MOZ_ASSERT(mState == CONFIRM_TRYING_OK || 883 mState == CONFIRM_TRYING_FAILED); 884 SetState(CONFIRM_FAILED); 885 mTask = nullptr; 886 // retry failed NS confirmation 887 LOG(("Setting timer to reconfirm %u", uint32_t(mRetryInterval))); 888 NS_NewTimerWithCallback(getter_AddRefs(mTimer), this, mRetryInterval, 889 nsITimer::TYPE_ONE_SHOT); 890 // double the interval up to this point 891 mRetryInterval *= 2; 892 if (mRetryInterval > StaticPrefs::network_trr_max_retry_timeout_ms()) { 893 mRetryInterval = StaticPrefs::network_trr_max_retry_timeout_ms(); 894 } 895 break; 896 default: 897 MOZ_ASSERT_UNREACHABLE("Unexpected ConfirmationEvent"); 898 } 899 900 return prevAddr != TaskAddr(); 901 } 902 903 bool TRRService::MaybeBootstrap(const nsACString& aPossible, 904 nsACString& aResult) { 905 MutexAutoLock lock(mLock); 906 if (mMode == nsIDNSService::MODE_TRROFF || mBootstrapAddr.IsEmpty()) { 907 return false; 908 } 909 910 nsCOMPtr<nsIURI> url; 911 nsresult rv = 912 NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID) 913 .Apply(&nsIStandardURLMutator::Init, nsIStandardURL::URLTYPE_STANDARD, 914 443, mPrivateURI, nullptr, nullptr, nullptr) 915 .Finalize(url); 916 if (NS_FAILED(rv)) { 917 LOG(("TRRService::MaybeBootstrap failed to create URI!\n")); 918 return false; 919 } 920 921 nsAutoCString host; 922 url->GetHost(host); 923 if (!aPossible.Equals(host)) { 924 return false; 925 } 926 LOG(("TRRService::MaybeBootstrap: use %s instead of %s\n", 927 mBootstrapAddr.get(), host.get())); 928 aResult = mBootstrapAddr; 929 return true; 930 } 931 932 bool TRRService::IsDomainBlocked(const nsACString& aHost, 933 const nsACString& aOriginSuffix, 934 bool aPrivateBrowsing) { 935 auto bl = mTRRBLStorage.Lock(); 936 if (bl->IsEmpty()) { 937 return false; 938 } 939 940 // use a unified casing for the hashkey 941 nsAutoCString hashkey(aHost + aOriginSuffix); 942 if (auto val = bl->Lookup(hashkey)) { 943 int32_t until = 944 *val + int32_t(StaticPrefs::network_trr_temp_blocklist_duration_sec()); 945 int32_t expire = NowInSeconds(); 946 if (until > expire) { 947 LOG(("Host [%s] is TRR blocklisted\n", nsCString(aHost).get())); 948 return true; 949 } 950 951 // the blocklisted entry has expired 952 val.Remove(); 953 } 954 return false; 955 } 956 957 // When running in TRR-only mode, the blocklist is not used and it will also 958 // try resolving the localhost / .local names. 959 bool TRRService::IsTemporarilyBlocked(const nsACString& aHost, 960 const nsACString& aOriginSuffix, 961 bool aPrivateBrowsing, 962 bool aParentsToo) // false if domain 963 { 964 if (!StaticPrefs::network_trr_temp_blocklist()) { 965 LOG(("TRRService::IsTemporarilyBlocked temp blocklist disabled by pref")); 966 return false; 967 } 968 969 if (mMode == nsIDNSService::MODE_TRRONLY) { 970 return false; // might as well try 971 } 972 973 LOG(("Checking if host [%s] is blocklisted", aHost.BeginReading())); 974 975 int32_t dot = aHost.FindChar('.'); 976 if ((dot == kNotFound) && aParentsToo) { 977 // Only if a full host name. Domains can be dotless to be able to 978 // blocklist entire TLDs 979 return true; 980 } 981 982 if (IsDomainBlocked(aHost, aOriginSuffix, aPrivateBrowsing)) { 983 return true; 984 } 985 986 nsDependentCSubstring domain = Substring(aHost, 0); 987 while (dot != kNotFound) { 988 dot++; 989 domain.Rebind(domain, dot, domain.Length() - dot); 990 991 if (IsDomainBlocked(domain, aOriginSuffix, aPrivateBrowsing)) { 992 return true; 993 } 994 995 dot = domain.FindChar('.'); 996 } 997 998 return false; 999 } 1000 1001 bool TRRService::IsExcludedFromTRR(const nsACString& aHost) { 1002 // This method may be called off the main thread. We need to lock so 1003 // mExcludedDomains and mDNSSuffixDomains don't change while this code 1004 // is running. 1005 MutexAutoLock lock(mLock); 1006 1007 return IsExcludedFromTRR_unlocked(aHost); 1008 } 1009 1010 bool TRRService::IsExcludedFromTRR_unlocked(const nsACString& aHost) { 1011 int32_t dot = 0; 1012 // iteratively check the sub-domain of |aHost| 1013 while (dot < static_cast<int32_t>(aHost.Length())) { 1014 nsDependentCSubstring subdomain = 1015 Substring(aHost, dot, aHost.Length() - dot); 1016 1017 if (mExcludedDomains.Contains(subdomain)) { 1018 LOG(("Subdomain [%s] of host [%s] Is Excluded From TRR via pref\n", 1019 subdomain.BeginReading(), aHost.BeginReading())); 1020 return true; 1021 } 1022 if (mDNSSuffixDomains.Contains(subdomain)) { 1023 LOG( 1024 ("Subdomain [%s] of host [%s] Is Excluded From TRR via DNSSuffix " 1025 "domains\n", 1026 subdomain.BeginReading(), aHost.BeginReading())); 1027 return true; 1028 } 1029 if (mEtcHostsDomains.Contains(subdomain)) { 1030 LOG(("Subdomain [%s] of host [%s] Is Excluded From TRR by /etc/hosts\n", 1031 subdomain.BeginReading(), aHost.BeginReading())); 1032 return true; 1033 } 1034 1035 dot = aHost.FindChar('.', dot + 1); 1036 if (dot == kNotFound) { 1037 break; 1038 } 1039 dot++; 1040 } 1041 1042 return false; 1043 } 1044 1045 void TRRService::AddToBlocklist(const nsACString& aHost, 1046 const nsACString& aOriginSuffix, 1047 bool privateBrowsing, bool aParentsToo) { 1048 if (!StaticPrefs::network_trr_temp_blocklist()) { 1049 LOG(("TRRService::AddToBlocklist temp blocklist disabled by pref")); 1050 return; 1051 } 1052 1053 LOG(("TRR blocklist %s\n", nsCString(aHost).get())); 1054 nsAutoCString hashkey(aHost + aOriginSuffix); 1055 1056 // this overwrites any existing entry 1057 { 1058 auto bl = mTRRBLStorage.Lock(); 1059 bl->InsertOrUpdate(hashkey, NowInSeconds()); 1060 } 1061 1062 // See bug 1700405. Some test expects 15 trr consecutive failures, but the NS 1063 // check against the base domain is successful. So, we skip this NS check when 1064 // the pref said so in order to pass the test reliably. 1065 if (aParentsToo && !StaticPrefs::network_trr_skip_check_for_blocked_host()) { 1066 // when given a full host name, verify its domain as well 1067 int32_t dot = aHost.FindChar('.'); 1068 if (dot != kNotFound) { 1069 // this has a domain to be checked 1070 dot++; 1071 nsDependentCSubstring domain = 1072 Substring(aHost, dot, aHost.Length() - dot); 1073 nsAutoCString check(domain); 1074 if (IsTemporarilyBlocked(check, aOriginSuffix, privateBrowsing, false)) { 1075 // the domain part is already blocklisted, no need to add this entry 1076 return; 1077 } 1078 // verify 'check' over TRR 1079 LOG(("TRR: verify if '%s' resolves as NS\n", check.get())); 1080 1081 // check if there's an NS entry for this name 1082 RefPtr<TRR> trr = new TRR(this, check, TRRTYPE_NS, aOriginSuffix, 1083 privateBrowsing, false); 1084 trr->SetPurpose(TRR::Blocklist); 1085 DispatchTRRRequest(trr); 1086 } 1087 } 1088 } 1089 1090 NS_IMETHODIMP 1091 TRRService::ConfirmationContext::Notify(nsITimer* aTimer) { 1092 MutexAutoLock lock(OwningObject()->mLock); 1093 if (aTimer == mTimer) { 1094 HandleEvent(ConfirmationEvent::ConfirmationRetry, lock); 1095 } 1096 1097 return NS_OK; 1098 } 1099 1100 NS_IMETHODIMP 1101 TRRService::ConfirmationContext::GetName(nsACString& aName) { 1102 aName.AssignLiteral("TRRService::ConfirmationContext"); 1103 return NS_OK; 1104 } 1105 1106 static char StatusToChar(nsresult aLookupStatus, nsresult aChannelStatus) { 1107 // If the resolution fails in the TRR channel then we'll have a failed 1108 // aChannelStatus. Otherwise, we parse the response - if it's not a valid DNS 1109 // packet or doesn't contain the correct responses aLookupStatus will be a 1110 // failure code. 1111 if (aChannelStatus == NS_OK) { 1112 // Return + if confirmation was OK, or - if confirmation failed 1113 return aLookupStatus == NS_OK ? '+' : '-'; 1114 } 1115 1116 if (nsCOMPtr<nsIIOService> ios = do_GetIOService()) { 1117 bool hasConnectiviy = true; 1118 ios->GetConnectivity(&hasConnectiviy); 1119 if (!hasConnectiviy) { 1120 // Browser has no active network interfaces = is offline. 1121 return 'o'; 1122 } 1123 } 1124 1125 switch (aChannelStatus) { 1126 case NS_ERROR_NET_TIMEOUT_EXTERNAL: 1127 // TRR timeout expired 1128 return 't'; 1129 case NS_ERROR_UNKNOWN_HOST: 1130 // TRRServiceChannel failed to due to unresolved host 1131 return 'd'; 1132 default: 1133 break; 1134 } 1135 1136 // The error is a network error 1137 if (NS_ERROR_GET_MODULE(aChannelStatus) == NS_ERROR_MODULE_NETWORK) { 1138 return 'n'; 1139 } 1140 1141 // Some other kind of failure. 1142 return '?'; 1143 } 1144 1145 void TRRService::RetryTRRConfirm() { 1146 if (mConfirmation.State() == CONFIRM_OK) { 1147 LOG(("TRRService::RetryTRRConfirm triggering confirmation")); 1148 mConfirmation.HandleEvent(ConfirmationEvent::RetryTRR); 1149 } 1150 } 1151 1152 void TRRService::RecordTRRStatus(TRR* aTrrRequest) { 1153 MOZ_ASSERT_IF(XRE_IsParentProcess(), NS_IsMainThread() || IsOnTRRThread()); 1154 MOZ_ASSERT_IF(XRE_IsSocketProcess(), NS_IsMainThread()); 1155 1156 nsresult channelStatus = aTrrRequest->ChannelStatus(); 1157 1158 glean::dns::trr_success.Get( 1159 ProviderKey(), 1160 NS_SUCCEEDED(channelStatus) 1161 ? "Fine"_ns 1162 : (channelStatus == NS_ERROR_NET_TIMEOUT_EXTERNAL ? "Timeout"_ns 1163 : "Bad"_ns)); 1164 mConfirmation.RecordTRRStatus(aTrrRequest); 1165 } 1166 1167 void TRRService::ConfirmationContext::RecordTRRStatus(TRR* aTrrRequest) { 1168 nsresult channelStatus = aTrrRequest->ChannelStatus(); 1169 1170 if (OwningObject()->Mode() == nsIDNSService::MODE_TRRONLY) { 1171 mLastConfirmationSkipReason = aTrrRequest->SkipReason(); 1172 mLastConfirmationStatus = channelStatus; 1173 } 1174 1175 if (NS_SUCCEEDED(channelStatus)) { 1176 LOG(("TRRService::RecordTRRStatus channel success")); 1177 mTRRFailures = 0; 1178 return; 1179 } 1180 1181 if (OwningObject()->Mode() != nsIDNSService::MODE_TRRFIRST) { 1182 return; 1183 } 1184 1185 // only count failures while in OK state 1186 if (State() != CONFIRM_OK) { 1187 return; 1188 } 1189 1190 // When TRR retry is enabled, nsHostResolver will trigger Confirmation 1191 // immediately upon a lookup failure, so nothing to be done here. 1192 // nsHostResolver can assess the success of the lookup considering all the 1193 // involved results (A, AAAA) so we let it tell us when to re-Confirm. 1194 if (StaticPrefs::network_trr_retry_on_recoverable_errors()) { 1195 LOG(("TRRService not counting failures when retry is enabled")); 1196 return; 1197 } 1198 1199 mFailureReasons[mTRRFailures % ConfirmationContext::RESULTS_SIZE] = 1200 StatusToChar(NS_OK, channelStatus); 1201 uint32_t fails = ++mTRRFailures; 1202 LOG(("TRRService::RecordTRRStatus fails=%u", fails)); 1203 1204 if (fails >= StaticPrefs::network_trr_max_fails()) { 1205 LOG(("TRRService had %u failures in a row\n", fails)); 1206 // When several failures occur we trigger a confirmation causing 1207 // us to transition into the CONFIRM_TRYING_OK state. 1208 // Only after the confirmation fails do we finally go into CONFIRM_FAILED 1209 // and start skipping TRR. 1210 1211 // Trigger a confirmation immediately. 1212 // If it fails, it will fire off a timer to start retrying again. 1213 HandleEvent(ConfirmationEvent::FailedLookups); 1214 } 1215 } 1216 1217 void TRRService::ConfirmationContext::RecordEvent(const char* aReason, 1218 const MutexAutoLock&) { 1219 // Reset the confirmation context attributes 1220 // Only resets the attributes that we keep for telemetry purposes. 1221 auto reset = [&]() { 1222 mAttemptCount = 0; 1223 mNetworkId.Truncate(); 1224 mFirstRequestTime = TimeStamp(); 1225 mContextChangeReason.Assign(aReason); 1226 mTrigger.Truncate(); 1227 mFailedLookups.Truncate(); 1228 1229 mRetryInterval = StaticPrefs::network_trr_retry_timeout_ms(); 1230 }; 1231 1232 if (mAttemptCount == 0) { 1233 // XXX: resetting everything might not be the best thing here, even if the 1234 // context changes, because there might still be a confirmation pending. 1235 // But cancelling and retrying that confirmation might just make the whole 1236 // confirmation longer for no reason. 1237 reset(); 1238 return; 1239 } 1240 1241 nsAutoCString results; 1242 static_assert(RESULTS_SIZE < 64); 1243 1244 // mResults is a circular buffer ending at mAttemptCount 1245 if (mAttemptCount <= RESULTS_SIZE) { 1246 // We have fewer attempts than the size of the buffer, so all of the 1247 // results are in the buffer. 1248 results.Append(nsDependentCSubstring(mResults, mAttemptCount)); 1249 } else { 1250 // More attempts than the buffer size. 1251 // That means past RESULTS_SIZE attempts in order are 1252 // [posInResults .. end-of-buffer) + [start-of-buffer .. posInResults) 1253 uint32_t posInResults = mAttemptCount % RESULTS_SIZE; 1254 1255 results.Append(nsDependentCSubstring(mResults + posInResults, 1256 RESULTS_SIZE - posInResults)); 1257 results.Append(nsDependentCSubstring(mResults, posInResults)); 1258 } 1259 1260 glean::network_dns::TrrConfirmationContextExtra extra = { 1261 .attemptcount = Some(mAttemptCount), 1262 .captiveportal = Some(nsPrintfCString("%i", mCaptivePortalStatus)), 1263 .contextreason = Some(mContextChangeReason), 1264 .failedlookups = mTrigger.Equals("failed-lookups"_ns) 1265 ? Some(mFailedLookups) 1266 : Nothing(), 1267 .networkid = Some(mNetworkId), 1268 .results = Some(results), 1269 .time = Some(nsPrintfCString( 1270 "%f", !mFirstRequestTime.IsNull() 1271 ? (TimeStamp::Now() - mFirstRequestTime).ToMilliseconds() 1272 : 0.0)), 1273 .trigger = Some(mTrigger), 1274 .value = Some(mState), 1275 }; 1276 glean::network_dns::trr_confirmation_context.Record(Some(extra)); 1277 1278 reset(); 1279 } 1280 1281 void TRRService::ConfirmationContext::RequestCompleted( 1282 nsresult aLookupStatus, nsresult aChannelStatus) { 1283 mResults[mAttemptCount % RESULTS_SIZE] = 1284 StatusToChar(aLookupStatus, aChannelStatus); 1285 mAttemptCount++; 1286 } 1287 1288 void TRRService::ConfirmationContext::CompleteConfirmation(nsresult aStatus, 1289 TRR* aTRRRequest) { 1290 { 1291 MutexAutoLock lock(OwningObject()->mLock); 1292 // Ignore confirmations that dont match the pending task. 1293 if (mTask != aTRRRequest) { 1294 return; 1295 } 1296 MOZ_ASSERT(State() == CONFIRM_TRYING_OK || 1297 State() == CONFIRM_TRYING_FAILED); 1298 if (State() != CONFIRM_TRYING_OK && State() != CONFIRM_TRYING_FAILED) { 1299 return; 1300 } 1301 1302 RequestCompleted(aStatus, aTRRRequest->ChannelStatus()); 1303 mLastConfirmationSkipReason = aTRRRequest->SkipReason(); 1304 mLastConfirmationStatus = aTRRRequest->ChannelStatus(); 1305 1306 MOZ_ASSERT(mTask); 1307 if (NS_SUCCEEDED(aStatus)) { 1308 profiler_add_marker("TRR Confirmation Success", 1309 geckoprofiler::category::NETWORK); 1310 HandleEvent(ConfirmationEvent::ConfirmOK, lock); 1311 } else { 1312 profiler_add_marker("TRR Confirmation Failure", 1313 geckoprofiler::category::NETWORK); 1314 HandleEvent(ConfirmationEvent::ConfirmFail, lock); 1315 } 1316 1317 if (State() == CONFIRM_OK) { 1318 // Record event and start new confirmation context 1319 RecordEvent("success", lock); 1320 } 1321 LOG(("TRRService finishing confirmation test %s %d %X\n", 1322 OwningObject()->mPrivateURI.get(), State(), (unsigned int)aStatus)); 1323 } 1324 1325 if (State() == CONFIRM_OK) { 1326 // A fresh confirmation means previous blocked entries might not 1327 // be valid anymore. 1328 auto bl = OwningObject()->mTRRBLStorage.Lock(); 1329 bl->Clear(); 1330 } else { 1331 MOZ_ASSERT(State() == CONFIRM_FAILED); 1332 } 1333 1334 glean::dns::trr_ns_verfified 1335 .Get(TRRService::ProviderKey(), 1336 (State() == CONFIRM_OK) ? "true"_ns : "false"_ns) 1337 .Add(); 1338 } 1339 1340 AHostResolver::LookupStatus TRRService::CompleteLookup( 1341 nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb, 1342 const nsACString& aOriginSuffix, TRRSkippedReason aReason, 1343 TRR* aTRRRequest) { 1344 // this is an NS check for the TRR blocklist or confirmationNS check 1345 1346 MOZ_ASSERT_IF(XRE_IsParentProcess(), NS_IsMainThread() || IsOnTRRThread()); 1347 MOZ_ASSERT_IF(XRE_IsSocketProcess(), NS_IsMainThread()); 1348 MOZ_ASSERT(!rec); 1349 1350 RefPtr<AddrInfo> newRRSet(aNewRRSet); 1351 MOZ_ASSERT(newRRSet && newRRSet->TRRType() == TRRTYPE_NS); 1352 1353 if (aTRRRequest->Purpose() == TRR::Confirmation) { 1354 mConfirmation.CompleteConfirmation(status, aTRRRequest); 1355 return LOOKUP_OK; 1356 } 1357 1358 if (aTRRRequest->Purpose() == TRR::Blocklist) { 1359 if (NS_SUCCEEDED(status)) { 1360 LOG(("TRR verified %s to be fine!\n", newRRSet->Hostname().get())); 1361 } else { 1362 LOG(("TRR says %s doesn't resolve as NS!\n", newRRSet->Hostname().get())); 1363 AddToBlocklist(newRRSet->Hostname(), aOriginSuffix, pb, false); 1364 } 1365 return LOOKUP_OK; 1366 } 1367 1368 MOZ_ASSERT_UNREACHABLE( 1369 "TRRService::CompleteLookup called for unexpected request"); 1370 return LOOKUP_OK; 1371 } 1372 1373 AHostResolver::LookupStatus TRRService::CompleteLookupByType( 1374 nsHostRecord*, nsresult, mozilla::net::TypeRecordResultType& aResult, 1375 mozilla::net::TRRSkippedReason aReason, uint32_t aTtl, bool aPb) { 1376 return LOOKUP_OK; 1377 } 1378 1379 NS_IMETHODIMP TRRService::OnProxyConfigChanged() { 1380 LOG(("TRRService::OnProxyConfigChanged")); 1381 1382 nsAutoCString uri; 1383 GetURI(uri); 1384 AsyncCreateTRRConnectionInfo(uri); 1385 1386 return NS_OK; 1387 } 1388 1389 void TRRService::InitTRRConnectionInfo(bool aForceReinit) { 1390 if (XRE_IsParentProcess()) { 1391 TRRServiceBase::InitTRRConnectionInfo(aForceReinit); 1392 return; 1393 } 1394 1395 MOZ_ASSERT(XRE_IsSocketProcess()); 1396 MOZ_ASSERT(NS_IsMainThread()); 1397 1398 TRRServiceChild* child = TRRServiceChild::GetSingleton(); 1399 if (child && child->CanSend()) { 1400 LOG(("TRRService::SendInitTRRConnectionInfo")); 1401 (void)child->SendInitTRRConnectionInfo(aForceReinit); 1402 } 1403 } 1404 1405 } // namespace mozilla::net