tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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