tor-browser

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

TRR.cpp (32154B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=4 sw=2 sts=2 et cin: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "DNS.h"
      8 #include "DNSUtils.h"
      9 #include "nsCharSeparatedTokenizer.h"
     10 #include "nsContentUtils.h"
     11 #include "nsHttpHandler.h"
     12 #include "nsHttpChannel.h"
     13 #include "nsHostResolver.h"
     14 #include "nsIHttpChannel.h"
     15 #include "nsIHttpChannelInternal.h"
     16 #include "nsIIOService.h"
     17 #include "nsIInputStream.h"
     18 #include "nsIObliviousHttp.h"
     19 #include "nsIOService.h"
     20 #include "nsISupports.h"
     21 #include "nsISupportsUtils.h"
     22 #include "nsITimedChannel.h"
     23 #include "nsIUploadChannel2.h"
     24 #include "nsIURIMutator.h"
     25 #include "nsNetUtil.h"
     26 #include "nsQueryObject.h"
     27 #include "nsStringStream.h"
     28 #include "nsThreadUtils.h"
     29 #include "nsURLHelper.h"
     30 #include "ObliviousHttpChannel.h"
     31 #include "TRR.h"
     32 #include "TRRService.h"
     33 #include "TRRServiceChannel.h"
     34 #include "TRRLoadInfo.h"
     35 
     36 #include "mozilla/Base64.h"
     37 #include "mozilla/Logging.h"
     38 #include "mozilla/Maybe.h"
     39 #include "mozilla/Preferences.h"
     40 #include "mozilla/StaticPrefs_network.h"
     41 #include "mozilla/glean/NetwerkDnsMetrics.h"
     42 #include "mozilla/TimeStamp.h"
     43 #include "mozilla/Tokenizer.h"
     44 #include "mozilla/UniquePtr.h"
     45 // Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
     46 #include "DNSLogging.h"
     47 #include "mozilla/glean/NetwerkMetrics.h"
     48 
     49 namespace mozilla {
     50 namespace net {
     51 
     52 NS_IMPL_ISUPPORTS_INHERITED(TRR, Runnable, nsIStreamListener, nsITimerCallback)
     53 
     54 // when firing off a normal A or AAAA query
     55 TRR::TRR(AHostResolver* aResolver, nsHostRecord* aRec, enum TrrType aType)
     56    : mozilla::Runnable("TRR"),
     57      mRec(aRec),
     58      mHostResolver(aResolver),
     59      mType(aType),
     60      mOriginSuffix(aRec->originSuffix) {
     61  mHost = aRec->host;
     62  mPB = aRec->pb;
     63  MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(),
     64                        "TRR must be in parent or socket process");
     65 }
     66 
     67 // when following CNAMEs
     68 TRR::TRR(AHostResolver* aResolver, nsHostRecord* aRec, nsCString& aHost,
     69         enum TrrType& aType, unsigned int aLoopCount, bool aPB)
     70    : mozilla::Runnable("TRR"),
     71      mHost(aHost),
     72      mRec(aRec),
     73      mHostResolver(aResolver),
     74      mType(aType),
     75      mPB(aPB),
     76      mCnameLoop(aLoopCount),
     77      mOriginSuffix(aRec ? aRec->originSuffix : ""_ns) {
     78  MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(),
     79                        "TRR must be in parent or socket process");
     80 }
     81 
     82 // to verify a domain
     83 TRR::TRR(AHostResolver* aResolver, nsACString& aHost, enum TrrType aType,
     84         const nsACString& aOriginSuffix, bool aPB, bool aUseFreshConnection)
     85    : mozilla::Runnable("TRR"),
     86      mHost(aHost),
     87      mRec(nullptr),
     88      mHostResolver(aResolver),
     89      mType(aType),
     90      mPB(aPB),
     91      mOriginSuffix(aOriginSuffix),
     92      mUseFreshConnection(aUseFreshConnection) {
     93  MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(),
     94                        "TRR must be in parent or socket process");
     95 }
     96 
     97 void TRR::HandleTimeout() {
     98  mTimeout = nullptr;
     99  RecordReason(TRRSkippedReason::TRR_TIMEOUT);
    100  Cancel(NS_ERROR_NET_TIMEOUT_EXTERNAL);
    101 }
    102 
    103 NS_IMETHODIMP
    104 TRR::Notify(nsITimer* aTimer) {
    105  if (aTimer == mTimeout) {
    106    HandleTimeout();
    107  } else {
    108    MOZ_CRASH("Unknown timer");
    109  }
    110 
    111  return NS_OK;
    112 }
    113 
    114 NS_IMETHODIMP
    115 TRR::Run() {
    116  MOZ_ASSERT_IF(XRE_IsParentProcess() && TRRService::Get(),
    117                NS_IsMainThread() || TRRService::Get()->IsOnTRRThread());
    118  MOZ_ASSERT_IF(XRE_IsSocketProcess(), NS_IsMainThread());
    119 
    120  if ((TRRService::Get() == nullptr) || NS_FAILED(SendHTTPRequest())) {
    121    RecordReason(TRRSkippedReason::TRR_SEND_FAILED);
    122    FailData(NS_ERROR_FAILURE);
    123    // The dtor will now be run
    124  }
    125  return NS_OK;
    126 }
    127 
    128 DNSPacket* TRR::GetOrCreateDNSPacket() {
    129  if (!mPacket) {
    130    mPacket = MakeUnique<DNSPacket>();
    131  }
    132 
    133  return mPacket.get();
    134 }
    135 
    136 nsresult TRR::CreateQueryURI(nsIURI** aOutURI) {
    137  nsAutoCString uri;
    138  nsCOMPtr<nsIURI> dnsURI;
    139  if (UseDefaultServer()) {
    140    TRRService::Get()->GetURI(uri);
    141  } else {
    142    uri = mRec->mTrrServer;
    143  }
    144 
    145  nsresult rv = NS_NewURI(getter_AddRefs(dnsURI), uri);
    146  if (NS_FAILED(rv)) {
    147    RecordReason(TRRSkippedReason::TRR_BAD_URL);
    148    return rv;
    149  }
    150 
    151  dnsURI.forget(aOutURI);
    152  return NS_OK;
    153 }
    154 
    155 bool TRR::MaybeBlockRequest() {
    156  if (((mType == TRRTYPE_A) || (mType == TRRTYPE_AAAA)) &&
    157      mRec->mEffectiveTRRMode != nsIRequest::TRR_ONLY_MODE) {
    158    // let NS resolves skip the blocklist check
    159    // we also don't check the blocklist for TRR only requests
    160    MOZ_ASSERT(mRec);
    161 
    162    // If TRRService isn't enabled anymore for the req, don't do TRR.
    163    if (!TRRService::Get()->Enabled(mRec->mEffectiveTRRMode)) {
    164      RecordReason(TRRSkippedReason::TRR_MODE_NOT_ENABLED);
    165      return true;
    166    }
    167 
    168    if (!StaticPrefs::network_trr_strict_native_fallback() &&
    169        UseDefaultServer() &&
    170        TRRService::Get()->IsTemporarilyBlocked(mHost, mOriginSuffix, mPB,
    171                                                true)) {
    172      if (mType == TRRTYPE_A) {
    173        // count only blocklist for A records to avoid double counts
    174        glean::dns::trr_blacklisted.Get(TRRService::ProviderKey(), "true"_ns)
    175            .Add();
    176      }
    177 
    178      RecordReason(TRRSkippedReason::TRR_HOST_BLOCKED_TEMPORARY);
    179      // not really an error but no TRR is issued
    180      return true;
    181    }
    182 
    183    if (TRRService::Get()->IsExcludedFromTRR(mHost)) {
    184      RecordReason(TRRSkippedReason::TRR_EXCLUDED);
    185      return true;
    186    }
    187 
    188    if (UseDefaultServer() && (mType == TRRTYPE_A)) {
    189      glean::dns::trr_blacklisted.Get(TRRService::ProviderKey(), "false"_ns)
    190          .Add();
    191    }
    192  }
    193 
    194  return false;
    195 }
    196 
    197 nsresult TRR::SendHTTPRequest() {
    198  // This is essentially the "run" method - created from nsHostResolver
    199  if (mCancelled) {
    200    return NS_ERROR_FAILURE;
    201  }
    202 
    203  if ((mType != TRRTYPE_A) && (mType != TRRTYPE_AAAA) &&
    204      (mType != TRRTYPE_NS) && (mType != TRRTYPE_TXT) &&
    205      (mType != TRRTYPE_HTTPSSVC)) {
    206    // limit the calling interface because nsHostResolver has explicit slots for
    207    // these types
    208    return NS_ERROR_FAILURE;
    209  }
    210 
    211  if (MaybeBlockRequest()) {
    212    return NS_ERROR_UNKNOWN_HOST;
    213  }
    214 
    215  LOG(("TRR::SendHTTPRequest resolve %s type %u\n", mHost.get(), mType));
    216 
    217  nsAutoCString body;
    218  bool disableECS = StaticPrefs::network_trr_disable_ECS();
    219  nsresult rv =
    220      GetOrCreateDNSPacket()->EncodeRequest(body, mHost, mType, disableECS);
    221  if (NS_FAILED(rv)) {
    222    HandleEncodeError(rv);
    223    return rv;
    224  }
    225 
    226  bool useGet = StaticPrefs::network_trr_useGET();
    227  nsCOMPtr<nsIURI> dnsURI;
    228  rv = CreateQueryURI(getter_AddRefs(dnsURI));
    229  if (NS_FAILED(rv)) {
    230    LOG(("TRR:SendHTTPRequest: NewURI failed!\n"));
    231    return rv;
    232  }
    233 
    234  if (useGet) {
    235    /* For GET requests, the outgoing packet needs to be Base64url-encoded and
    236       then appended to the end of the URI. */
    237    nsAutoCString encoded;
    238    rv = Base64URLEncode(body.Length(),
    239                         reinterpret_cast<const unsigned char*>(body.get()),
    240                         Base64URLEncodePaddingPolicy::Omit, encoded);
    241    NS_ENSURE_SUCCESS(rv, rv);
    242 
    243    nsAutoCString query;
    244    rv = dnsURI->GetQuery(query);
    245    if (NS_FAILED(rv)) {
    246      return rv;
    247    }
    248 
    249    if (query.IsEmpty()) {
    250      query.Assign("?dns="_ns);
    251    } else {
    252      query.Append("&dns="_ns);
    253    }
    254    query.Append(encoded);
    255 
    256    rv = NS_MutateURI(dnsURI).SetQuery(query).Finalize(dnsURI);
    257    LOG(("TRR::SendHTTPRequest GET dns=%s\n", body.get()));
    258  }
    259 
    260  nsCOMPtr<nsIChannel> channel;
    261  bool useOHTTP = StaticPrefs::network_trr_use_ohttp();
    262  if (useOHTTP) {
    263    nsCOMPtr<nsIObliviousHttpService> ohttpService(
    264        do_GetService("@mozilla.org/network/oblivious-http-service;1"));
    265    if (!ohttpService) {
    266      return NS_ERROR_FAILURE;
    267    }
    268    nsCOMPtr<nsIURI> relayURI;
    269    nsTArray<uint8_t> encodedConfig;
    270    rv = ohttpService->GetTRRSettings(getter_AddRefs(relayURI), encodedConfig);
    271    if (NS_FAILED(rv)) {
    272      return rv;
    273    }
    274    if (!relayURI) {
    275      return NS_ERROR_FAILURE;
    276    }
    277    rv = ohttpService->NewChannel(relayURI, dnsURI, encodedConfig,
    278                                  getter_AddRefs(channel));
    279  } else {
    280    rv = DNSUtils::CreateChannelHelper(dnsURI, getter_AddRefs(channel));
    281  }
    282  if (NS_FAILED(rv) || !channel) {
    283    LOG(("TRR:SendHTTPRequest: NewChannel failed!\n"));
    284    return rv;
    285  }
    286 
    287  auto loadFlags = nsIRequest::LOAD_ANONYMOUS | nsIRequest::INHIBIT_CACHING |
    288                   nsIRequest::LOAD_BYPASS_CACHE |
    289                   nsIChannel::LOAD_BYPASS_URL_CLASSIFIER;
    290  if (mUseFreshConnection) {
    291    // Causes TRRServiceChannel to tell the connection manager
    292    // to clear out any connection with the current conn info.
    293    loadFlags |= nsIRequest::LOAD_FRESH_CONNECTION;
    294  }
    295  channel->SetLoadFlags(loadFlags);
    296  NS_ENSURE_SUCCESS(rv, rv);
    297 
    298  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
    299  if (!httpChannel) {
    300    return NS_ERROR_UNEXPECTED;
    301  }
    302 
    303  // This connection should not use TRR
    304  rv = httpChannel->SetTRRMode(nsIRequest::TRR_DISABLED_MODE);
    305  NS_ENSURE_SUCCESS(rv, rv);
    306 
    307  nsCString contentType(ContentType());
    308  rv = httpChannel->SetRequestHeader("Accept"_ns, contentType, false);
    309  NS_ENSURE_SUCCESS(rv, rv);
    310 
    311  nsAutoCString cred;
    312  if (UseDefaultServer()) {
    313    TRRService::Get()->GetCredentials(cred);
    314  }
    315  if (!cred.IsEmpty()) {
    316    rv = httpChannel->SetRequestHeader("Authorization"_ns, cred, false);
    317    NS_ENSURE_SUCCESS(rv, rv);
    318  }
    319 
    320  nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(channel);
    321  if (!internalChannel) {
    322    return NS_ERROR_UNEXPECTED;
    323  }
    324 
    325  // setting a small stream window means the h2 stack won't pipeline a window
    326  // update with each HEADERS or reply to a DATA with a WINDOW UPDATE
    327  rv = internalChannel->SetInitialRwin(127 * 1024);
    328  NS_ENSURE_SUCCESS(rv, rv);
    329  rv = internalChannel->SetIsTRRServiceChannel(true);
    330  NS_ENSURE_SUCCESS(rv, rv);
    331 
    332  // When using OHTTP, the we can't use cached connection info, since we
    333  // need to connect to the relay, not the TRR server.
    334  if (UseDefaultServer() && !useOHTTP &&
    335      StaticPrefs::network_trr_async_connInfo()) {
    336    RefPtr<nsHttpConnectionInfo> trrConnInfo =
    337        TRRService::Get()->TRRConnectionInfo();
    338    if (trrConnInfo) {
    339      nsAutoCString host;
    340      dnsURI->GetHost(host);
    341      if (host.Equals(trrConnInfo->GetOrigin())) {
    342        internalChannel->SetConnectionInfo(trrConnInfo);
    343        LOG(("TRR::SendHTTPRequest use conn info:%s\n",
    344             trrConnInfo->HashKey().get()));
    345      } else {
    346        // The connection info is inconsistent. Avoid using it and generate a
    347        // new one.
    348        TRRService::Get()->SetDefaultTRRConnectionInfo(nullptr);
    349        TRRService::Get()->InitTRRConnectionInfo(true);
    350      }
    351    } else {
    352      TRRService::Get()->InitTRRConnectionInfo();
    353    }
    354  }
    355 
    356  if (useGet) {
    357    rv = httpChannel->SetRequestMethod("GET"_ns);
    358    NS_ENSURE_SUCCESS(rv, rv);
    359  } else {
    360    nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(httpChannel);
    361    if (!uploadChannel) {
    362      return NS_ERROR_UNEXPECTED;
    363    }
    364    uint32_t streamLength = body.Length();
    365    nsCOMPtr<nsIInputStream> uploadStream;
    366    rv =
    367        NS_NewCStringInputStream(getter_AddRefs(uploadStream), std::move(body));
    368    NS_ENSURE_SUCCESS(rv, rv);
    369 
    370    rv = uploadChannel->ExplicitSetUploadStream(uploadStream, contentType,
    371                                                streamLength, "POST"_ns, false);
    372    NS_ENSURE_SUCCESS(rv, rv);
    373  }
    374 
    375  rv = SetupTRRServiceChannelInternal(httpChannel, useGet, contentType);
    376  if (NS_FAILED(rv)) {
    377    return rv;
    378  }
    379 
    380  rv = httpChannel->AsyncOpen(this);
    381  if (NS_FAILED(rv)) {
    382    return rv;
    383  }
    384 
    385  // If the asyncOpen succeeded we can say that we actually attempted to
    386  // use the TRR connection.
    387  if (mRec) {
    388    mRec->mResolverType = ResolverType();
    389  }
    390 
    391  NS_NewTimerWithCallback(
    392      getter_AddRefs(mTimeout), this,
    393      mTimeoutMs ? mTimeoutMs : TRRService::Get()->GetRequestTimeout(),
    394      nsITimer::TYPE_ONE_SHOT);
    395 
    396  mChannel = channel;
    397  return NS_OK;
    398 }
    399 
    400 // static
    401 nsresult TRR::SetupTRRServiceChannelInternal(nsIHttpChannel* aChannel,
    402                                             bool aUseGet,
    403                                             const nsACString& aContentType) {
    404  nsCOMPtr<nsIHttpChannel> httpChannel = aChannel;
    405  MOZ_ASSERT(httpChannel);
    406 
    407  nsresult rv = NS_OK;
    408  if (!aUseGet) {
    409    rv =
    410        httpChannel->SetRequestHeader("Cache-Control"_ns, "no-store"_ns, false);
    411    NS_ENSURE_SUCCESS(rv, rv);
    412  }
    413 
    414  // Sanitize the request by removing the Accept-Language header so we minimize
    415  // the amount of fingerprintable information we send to the server.
    416  if (!StaticPrefs::network_trr_send_accept_language_headers()) {
    417    rv = httpChannel->SetRequestHeader("Accept-Language"_ns, ""_ns, false);
    418    NS_ENSURE_SUCCESS(rv, rv);
    419  }
    420 
    421  // Sanitize the request by removing the User-Agent
    422  if (!StaticPrefs::network_trr_send_user_agent_headers()) {
    423    rv = httpChannel->SetRequestHeader("User-Agent"_ns, ""_ns, false);
    424    NS_ENSURE_SUCCESS(rv, rv);
    425  }
    426 
    427  if (StaticPrefs::network_trr_send_empty_accept_encoding_headers()) {
    428    rv = httpChannel->SetEmptyRequestHeader("Accept-Encoding"_ns);
    429    NS_ENSURE_SUCCESS(rv, rv);
    430  }
    431 
    432  // set the *default* response content type
    433  if (NS_FAILED(httpChannel->SetContentType(aContentType))) {
    434    LOG(("TRR::SetupTRRServiceChannelInternal: couldn't set content-type!\n"));
    435  }
    436 
    437  return NS_OK;
    438 }
    439 
    440 NS_IMETHODIMP
    441 TRR::OnStartRequest(nsIRequest* aRequest) {
    442  LOG(("TRR::OnStartRequest %p %s %d\n", this, mHost.get(), mType));
    443 
    444  nsresult status = NS_OK;
    445  aRequest->GetStatus(&status);
    446 
    447  if (NS_FAILED(status)) {
    448    if (gIOService->InSleepMode()) {
    449      RecordReason(TRRSkippedReason::TRR_SYSTEM_SLEEP_MODE);
    450    } else if (NS_IsOffline()) {
    451      RecordReason(TRRSkippedReason::TRR_BROWSER_IS_OFFLINE);
    452    }
    453 
    454    switch (status) {
    455      case NS_ERROR_UNKNOWN_HOST:
    456        RecordReason(TRRSkippedReason::TRR_CHANNEL_DNS_FAIL);
    457        break;
    458      case NS_ERROR_OFFLINE:
    459        RecordReason(TRRSkippedReason::TRR_BROWSER_IS_OFFLINE);
    460        break;
    461      case NS_ERROR_NET_RESET:
    462        RecordReason(TRRSkippedReason::TRR_NET_RESET);
    463        break;
    464      case NS_ERROR_NET_TIMEOUT:
    465      case NS_ERROR_NET_TIMEOUT_EXTERNAL:
    466        RecordReason(TRRSkippedReason::TRR_NET_TIMEOUT);
    467        break;
    468      case NS_ERROR_PROXY_CONNECTION_REFUSED:
    469        RecordReason(TRRSkippedReason::TRR_NET_REFUSED);
    470        break;
    471      case NS_ERROR_NET_INTERRUPT:
    472        RecordReason(TRRSkippedReason::TRR_NET_INTERRUPT);
    473        break;
    474      case NS_ERROR_NET_INADEQUATE_SECURITY:
    475        RecordReason(TRRSkippedReason::TRR_NET_INADEQ_SEQURITY);
    476        break;
    477      default:
    478        RecordReason(TRRSkippedReason::TRR_UNKNOWN_CHANNEL_FAILURE);
    479    }
    480  }
    481 
    482  return NS_OK;
    483 }
    484 
    485 void TRR::SaveAdditionalRecords(
    486    const nsClassHashtable<nsCStringHashKey, DOHresp>& aRecords) {
    487  if (!mRec) {
    488    return;
    489  }
    490  nsresult rv;
    491  for (const auto& recordEntry : aRecords) {
    492    if (!recordEntry.GetData() || recordEntry.GetData()->mAddresses.IsEmpty()) {
    493      // no point in adding empty records.
    494      continue;
    495    }
    496    // If IPv6 is disabled don't add anything else than IPv4.
    497    if (StaticPrefs::network_dns_disableIPv6() &&
    498        std::find_if(recordEntry.GetData()->mAddresses.begin(),
    499                     recordEntry.GetData()->mAddresses.end(),
    500                     [](const NetAddr& addr) { return !addr.IsIPAddrV4(); }) !=
    501            recordEntry.GetData()->mAddresses.end()) {
    502      continue;
    503    }
    504    RefPtr<nsHostRecord> hostRecord;
    505    rv = mHostResolver->GetHostRecord(
    506        recordEntry.GetKey(), EmptyCString(),
    507        nsIDNSService::RESOLVE_TYPE_DEFAULT, mRec->flags, AF_UNSPEC, mRec->pb,
    508        mRec->originSuffix, getter_AddRefs(hostRecord));
    509    if (NS_FAILED(rv)) {
    510      LOG(("Failed to get host record for additional record %s",
    511           nsCString(recordEntry.GetKey()).get()));
    512      continue;
    513    }
    514    RefPtr<AddrInfo> ai(
    515        new AddrInfo(recordEntry.GetKey(), ResolverType(), TRRTYPE_A,
    516                     std::move(recordEntry.GetData()->mAddresses),
    517                     recordEntry.GetData()->mTtl));
    518    mHostResolver->MaybeRenewHostRecord(hostRecord);
    519 
    520    // Since we're not actually calling NameLookup for this record, we need
    521    // to set these fields to avoid assertions in CompleteLookup.
    522    // This is quite hacky, and should be fixed.
    523    hostRecord->Reset();
    524    hostRecord->mResolving++;
    525    hostRecord->mEffectiveTRRMode =
    526        static_cast<nsIRequest::TRRMode>(mRec->mEffectiveTRRMode);
    527    LOG(("Completing lookup for additional: %s",
    528         nsCString(recordEntry.GetKey()).get()));
    529    (void)mHostResolver->CompleteLookup(hostRecord, NS_OK, ai, mPB,
    530                                        mOriginSuffix, TRRSkippedReason::TRR_OK,
    531                                        this);
    532  }
    533 }
    534 
    535 void TRR::StoreIPHintAsDNSRecord(const struct SVCB& aSVCBRecord) {
    536  LOG(("TRR::StoreIPHintAsDNSRecord [%p] [%s]", this,
    537       aSVCBRecord.mSvcDomainName.get()));
    538  CopyableTArray<NetAddr> addresses;
    539  aSVCBRecord.GetIPHints(addresses);
    540 
    541  if (StaticPrefs::network_dns_disableIPv6()) {
    542    addresses.RemoveElementsBy(
    543        [](const NetAddr& addr) { return !addr.IsIPAddrV4(); });
    544  }
    545 
    546  if (addresses.IsEmpty()) {
    547    return;
    548  }
    549 
    550  RefPtr<nsHostRecord> hostRecord;
    551  nsresult rv = mHostResolver->GetHostRecord(
    552      aSVCBRecord.mSvcDomainName, EmptyCString(),
    553      nsIDNSService::RESOLVE_TYPE_DEFAULT,
    554      mRec->flags | nsIDNSService::RESOLVE_IP_HINT, AF_UNSPEC, mRec->pb,
    555      mRec->originSuffix, getter_AddRefs(hostRecord));
    556  if (NS_FAILED(rv)) {
    557    LOG(("Failed to get host record"));
    558    return;
    559  }
    560 
    561  mHostResolver->MaybeRenewHostRecord(hostRecord);
    562 
    563  RefPtr<AddrInfo> ai(new AddrInfo(aSVCBRecord.mSvcDomainName, ResolverType(),
    564                                   TRRTYPE_A, std::move(addresses), mTTL));
    565 
    566  // Since we're not actually calling NameLookup for this record, we need
    567  // to set these fields to avoid assertions in CompleteLookup.
    568  // This is quite hacky, and should be fixed.
    569  hostRecord->mResolving++;
    570  hostRecord->mEffectiveTRRMode =
    571      static_cast<nsIRequest::TRRMode>(mRec->mEffectiveTRRMode);
    572  (void)mHostResolver->CompleteLookup(hostRecord, NS_OK, ai, mPB, mOriginSuffix,
    573                                      TRRSkippedReason::TRR_OK, this);
    574 }
    575 
    576 nsresult TRR::ReturnData(nsIChannel* aChannel) {
    577  Maybe<TimeDuration> trrFetchDuration;
    578  Maybe<TimeDuration> trrFetchDurationNetworkOnly;
    579  // Set timings.
    580  nsCOMPtr<nsITimedChannel> timedChan = do_QueryInterface(aChannel);
    581  if (timedChan) {
    582    TimeStamp asyncOpen, start, end;
    583    if (NS_SUCCEEDED(timedChan->GetAsyncOpen(&asyncOpen)) &&
    584        !asyncOpen.IsNull()) {
    585      trrFetchDuration = Some(TimeStamp::Now() - asyncOpen);
    586    }
    587    if (NS_SUCCEEDED(timedChan->GetRequestStart(&start)) &&
    588        NS_SUCCEEDED(timedChan->GetResponseEnd(&end)) && !start.IsNull() &&
    589        !end.IsNull()) {
    590      trrFetchDurationNetworkOnly = Some(end - start);
    591    }
    592  }
    593 
    594  if (mType != TRRTYPE_TXT && mType != TRRTYPE_HTTPSSVC) {
    595    // create and populate an AddrInfo instance to pass on
    596    RefPtr<AddrInfo> ai(new AddrInfo(mHost, ResolverType(), mType,
    597                                     nsTArray<NetAddr>(), mDNS.mTtl));
    598    auto builder = ai->Build();
    599    builder.SetAddresses(std::move(mDNS.mAddresses));
    600    builder.SetCanonicalHostname(mCname);
    601    if (trrFetchDuration) {
    602      builder.SetTrrFetchDuration((*trrFetchDuration).ToMilliseconds());
    603    }
    604    if (trrFetchDurationNetworkOnly) {
    605      builder.SetTrrFetchDurationNetworkOnly(
    606          (*trrFetchDurationNetworkOnly).ToMilliseconds());
    607    }
    608    ai = builder.Finish();
    609 
    610    if (!mHostResolver) {
    611      return NS_ERROR_FAILURE;
    612    }
    613    RecordReason(TRRSkippedReason::TRR_OK);
    614    (void)mHostResolver->CompleteLookup(mRec, NS_OK, ai, mPB, mOriginSuffix,
    615                                        mTRRSkippedReason, this);
    616    mHostResolver = nullptr;
    617    mRec = nullptr;
    618  } else {
    619    RecordReason(TRRSkippedReason::TRR_OK);
    620    (void)mHostResolver->CompleteLookupByType(mRec, NS_OK, mResult,
    621                                              mTRRSkippedReason, mTTL, mPB);
    622  }
    623 
    624  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
    625  if (httpChannel) {
    626    nsAutoCString version;
    627    if (NS_SUCCEEDED(httpChannel->GetProtocolVersion(version))) {
    628      nsAutoCString key("h1"_ns);
    629      if (version.Equals("h3"_ns)) {
    630        key.Assign("h3"_ns);
    631      } else if (version.Equals("h2"_ns)) {
    632        key.Assign("h2"_ns);
    633      }
    634 
    635      if (trrFetchDuration) {
    636        glean::networking::trr_fetch_duration.Get(key).AccumulateRawDuration(
    637            *trrFetchDuration);
    638      }
    639      if (trrFetchDurationNetworkOnly) {
    640        key.Append("_network_only"_ns);
    641        glean::networking::trr_fetch_duration.Get(key).AccumulateRawDuration(
    642            *trrFetchDurationNetworkOnly);
    643      }
    644    }
    645  }
    646  return NS_OK;
    647 }
    648 
    649 nsresult TRR::FailData(nsresult error) {
    650  if (!mHostResolver) {
    651    return NS_ERROR_FAILURE;
    652  }
    653 
    654  // If we didn't record a reason until now, record a default one.
    655  RecordReason(TRRSkippedReason::TRR_FAILED);
    656 
    657  if (mType == TRRTYPE_TXT || mType == TRRTYPE_HTTPSSVC) {
    658    TypeRecordResultType empty(Nothing{});
    659    (void)mHostResolver->CompleteLookupByType(mRec, error, empty,
    660                                              mTRRSkippedReason, 0, mPB);
    661  } else {
    662    // create and populate an TRR AddrInfo instance to pass on to signal that
    663    // this comes from TRR
    664    nsTArray<NetAddr> noAddresses;
    665    RefPtr<AddrInfo> ai =
    666        new AddrInfo(mHost, ResolverType(), mType, std::move(noAddresses));
    667 
    668    (void)mHostResolver->CompleteLookup(mRec, error, ai, mPB, mOriginSuffix,
    669                                        mTRRSkippedReason, this);
    670  }
    671 
    672  mHostResolver = nullptr;
    673  mRec = nullptr;
    674  return NS_OK;
    675 }
    676 
    677 void TRR::HandleDecodeError(nsresult aStatusCode) {
    678  auto rcode = mPacket->GetRCode();
    679  if (rcode.isOk() && rcode.unwrap() != 0) {
    680    if (rcode.unwrap() == 0x03) {
    681      RecordReason(TRRSkippedReason::TRR_NXDOMAIN);
    682    } else {
    683      RecordReason(TRRSkippedReason::TRR_RCODE_FAIL);
    684    }
    685  } else if (aStatusCode == NS_ERROR_UNKNOWN_HOST ||
    686             aStatusCode == NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
    687    RecordReason(TRRSkippedReason::TRR_NO_ANSWERS);
    688  } else {
    689    RecordReason(TRRSkippedReason::TRR_DECODE_FAILED);
    690  }
    691 }
    692 
    693 bool TRR::HasUsableResponse() {
    694  if (mType == TRRTYPE_A || mType == TRRTYPE_AAAA) {
    695    return !mDNS.mAddresses.IsEmpty();
    696  }
    697  if (mType == TRRTYPE_TXT) {
    698    return mResult.is<TypeRecordTxt>();
    699  }
    700  if (mType == TRRTYPE_HTTPSSVC) {
    701    return mResult.is<TypeRecordHTTPSSVC>();
    702  }
    703  return false;
    704 }
    705 
    706 nsresult TRR::FollowCname(nsIChannel* aChannel) {
    707  nsresult rv = NS_OK;
    708  nsAutoCString cname;
    709  while (NS_SUCCEEDED(rv) && mDNS.mAddresses.IsEmpty() && !mCname.IsEmpty() &&
    710         mCnameLoop > 0) {
    711    mCnameLoop--;
    712    LOG(("TRR::On200Response CNAME %s => %s (%u)\n", mHost.get(), mCname.get(),
    713         mCnameLoop));
    714    cname = mCname;
    715    mCname.Truncate();
    716 
    717    LOG(("TRR: check for CNAME record for %s within previous response\n",
    718         cname.get()));
    719    nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords;
    720    rv = GetOrCreateDNSPacket()->Decode(
    721        cname, mType, mCname, StaticPrefs::network_trr_allow_rfc1918(), mDNS,
    722        mResult, additionalRecords, mTTL);
    723    if (NS_FAILED(rv)) {
    724      LOG(("TRR::FollowCname DohDecode %x\n", (int)rv));
    725      HandleDecodeError(rv);
    726    }
    727  }
    728 
    729  // restore mCname as DohDecode() change it
    730  mCname = cname;
    731  if (NS_SUCCEEDED(rv) && HasUsableResponse()) {
    732    ReturnData(aChannel);
    733    return NS_OK;
    734  }
    735 
    736  bool ra = mPacket && mPacket->RecursionAvailable().unwrapOr(false);
    737  LOG(("ra = %d", ra));
    738  if (rv == NS_ERROR_UNKNOWN_HOST && ra) {
    739    // If recursion is available, but no addresses have been returned,
    740    // we can just return a failure here.
    741    LOG(("TRR::FollowCname not sending another request as RA flag is set."));
    742    FailData(NS_ERROR_UNKNOWN_HOST);
    743    return NS_OK;
    744  }
    745 
    746  if (!mCnameLoop) {
    747    LOG(("TRR::On200Response CNAME loop, eject!\n"));
    748    return NS_ERROR_REDIRECT_LOOP;
    749  }
    750 
    751  LOG(("TRR::On200Response CNAME %s => %s (%u)\n", mHost.get(), mCname.get(),
    752       mCnameLoop));
    753  RefPtr<TRR> trr =
    754      new TRR(mHostResolver, mRec, mCname, mType, mCnameLoop, mPB);
    755  trr->SetPurpose(mPurpose);
    756  if (!TRRService::Get()) {
    757    return NS_ERROR_FAILURE;
    758  }
    759  return TRRService::Get()->DispatchTRRRequest(trr);
    760 }
    761 
    762 nsresult TRR::On200Response(nsIChannel* aChannel) {
    763  // decode body and create an AddrInfo struct for the response
    764  nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords;
    765  if (RefPtr<TypeHostRecord> typeRec = do_QueryObject(mRec)) {
    766    MutexAutoLock lock(typeRec->mResultsLock);
    767    if (typeRec->mOriginHost) {
    768      GetOrCreateDNSPacket()->SetOriginHost(typeRec->mOriginHost);
    769    }
    770  }
    771  nsresult rv = GetOrCreateDNSPacket()->Decode(
    772      mHost, mType, mCname, StaticPrefs::network_trr_allow_rfc1918(), mDNS,
    773      mResult, additionalRecords, mTTL);
    774  if (NS_FAILED(rv)) {
    775    LOG(("TRR::On200Response DohDecode %x\n", (int)rv));
    776    HandleDecodeError(rv);
    777    return rv;
    778  }
    779  if (StaticPrefs::network_trr_add_additional_records()) {
    780    SaveAdditionalRecords(additionalRecords);
    781  }
    782 
    783  if (mResult.is<TypeRecordHTTPSSVC>()) {
    784    auto& results = mResult.as<TypeRecordHTTPSSVC>();
    785    for (const auto& rec : results) {
    786      StoreIPHintAsDNSRecord(rec);
    787    }
    788  }
    789 
    790  if (!mDNS.mAddresses.IsEmpty() || mType == TRRTYPE_TXT || mCname.IsEmpty()) {
    791    // pass back the response data
    792    ReturnData(aChannel);
    793    return NS_OK;
    794  }
    795 
    796  LOG(("TRR::On200Response trying CNAME %s", mCname.get()));
    797  return FollowCname(aChannel);
    798 }
    799 
    800 void TRR::RecordProcessingTime(nsIChannel* aChannel) {
    801  // This method records the time it took from the last received byte of the
    802  // DoH response until we've notified the consumer with a host record.
    803  nsCOMPtr<nsITimedChannel> timedChan = do_QueryInterface(aChannel);
    804  if (!timedChan) {
    805    return;
    806  }
    807  TimeStamp end;
    808  if (NS_FAILED(timedChan->GetResponseEnd(&end))) {
    809    return;
    810  }
    811 
    812  if (end.IsNull()) {
    813    return;
    814  }
    815 
    816  glean::dns::trr_processing_time.AccumulateRawDuration(TimeStamp::Now() - end);
    817 
    818  LOG(("Processing DoH response took %f ms",
    819       (TimeStamp::Now() - end).ToMilliseconds()));
    820 }
    821 
    822 void TRR::ReportStatus(nsresult aStatusCode) {
    823  // If the TRR was cancelled by nsHostResolver, then we don't need to report
    824  // it as failed; otherwise it can cause the confirmation to fail.
    825  if (UseDefaultServer() && aStatusCode != NS_ERROR_ABORT) {
    826    // Bad content is still considered "okay" if the HTTP response is okay
    827    TRRService::Get()->RecordTRRStatus(this);
    828  }
    829 }
    830 
    831 static void RecordHttpVersion(nsIHttpChannel* aHttpChannel) {
    832  nsCOMPtr<nsIHttpChannelInternal> internalChannel =
    833      do_QueryInterface(aHttpChannel);
    834  if (!internalChannel) {
    835    LOG(("RecordHttpVersion: Failed to QI nsIHttpChannelInternal"));
    836    return;
    837  }
    838 
    839  uint32_t major, minor;
    840  if (NS_FAILED(internalChannel->GetResponseVersion(&major, &minor))) {
    841    LOG(("RecordHttpVersion: Failed to get protocol version"));
    842    return;
    843  }
    844 
    845  if (major == 2) {
    846    glean::dns::trr_http_version.Get(TRRService::ProviderKey(), "h_2"_ns).Add();
    847  } else if (major == 3) {
    848    glean::dns::trr_http_version.Get(TRRService::ProviderKey(), "h_3"_ns).Add();
    849  } else {
    850    glean::dns::trr_http_version.Get(TRRService::ProviderKey(), "h_1"_ns).Add();
    851  }
    852 
    853  LOG(("RecordHttpVersion: Provider responded using HTTP version: %d", major));
    854 }
    855 
    856 NS_IMETHODIMP
    857 TRR::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
    858  // The dtor will be run after the function returns
    859  LOG(("TRR:OnStopRequest %p %s %d failed=%d code=%X\n", this, mHost.get(),
    860       mType, mFailed, (unsigned int)aStatusCode));
    861  nsCOMPtr<nsIChannel> channel;
    862  channel.swap(mChannel);
    863 
    864  mChannelStatus = aStatusCode;
    865  if (NS_SUCCEEDED(aStatusCode)) {
    866    nsCString label = "regular"_ns;
    867    if (mPB) {
    868      label = "private"_ns;
    869    }
    870    mozilla::glean::networking::trr_request_count.Get(label).Add(1);
    871  }
    872 
    873  {
    874    // Cancel the timer since we don't need it anymore.
    875    nsCOMPtr<nsITimer> timer;
    876    mTimeout.swap(timer);
    877    if (timer) {
    878      timer->Cancel();
    879    }
    880  }
    881 
    882  auto scopeExit = MakeScopeExit([&] { ReportStatus(aStatusCode); });
    883 
    884  nsresult rv = NS_OK;
    885  // if status was "fine", parse the response and pass on the answer
    886  if (!mFailed && NS_SUCCEEDED(aStatusCode)) {
    887    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
    888    if (!httpChannel) {
    889      return NS_ERROR_UNEXPECTED;
    890    }
    891    nsAutoCString contentType;
    892    httpChannel->GetContentType(contentType);
    893    if (contentType.Length() &&
    894        !contentType.LowerCaseEqualsASCII(ContentType())) {
    895      LOG(("TRR:OnStopRequest %p %s %d wrong content type %s\n", this,
    896           mHost.get(), mType, contentType.get()));
    897      FailData(NS_ERROR_UNEXPECTED);
    898      return NS_OK;
    899    }
    900 
    901    uint32_t httpStatus;
    902    rv = httpChannel->GetResponseStatus(&httpStatus);
    903    if (NS_SUCCEEDED(rv) && httpStatus == 200) {
    904      rv = On200Response(channel);
    905      if (NS_SUCCEEDED(rv) && UseDefaultServer()) {
    906        RecordReason(TRRSkippedReason::TRR_OK);
    907        RecordProcessingTime(channel);
    908        RecordHttpVersion(httpChannel);
    909        return rv;
    910      }
    911    } else {
    912      RecordReason(TRRSkippedReason::TRR_SERVER_RESPONSE_ERR);
    913      LOG(("TRR:OnStopRequest:%d %p rv %x httpStatus %d\n", __LINE__, this,
    914           (int)rv, httpStatus));
    915    }
    916  }
    917 
    918  LOG(("TRR:OnStopRequest %p status %x mFailed %d\n", this, (int)aStatusCode,
    919       mFailed));
    920  FailData(NS_SUCCEEDED(rv) ? NS_ERROR_UNKNOWN_HOST : rv);
    921  return NS_OK;
    922 }
    923 
    924 NS_IMETHODIMP
    925 TRR::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream,
    926                     uint64_t aOffset, const uint32_t aCount) {
    927  LOG(("TRR:OnDataAvailable %p %s %d failed=%d aCount=%u\n", this, mHost.get(),
    928       mType, mFailed, (unsigned int)aCount));
    929  // receive DNS response into the local buffer
    930  if (mFailed) {
    931    return NS_ERROR_FAILURE;
    932  }
    933 
    934  nsresult rv = GetOrCreateDNSPacket()->OnDataAvailable(aRequest, aInputStream,
    935                                                        aOffset, aCount);
    936  if (NS_FAILED(rv)) {
    937    LOG(("TRR::OnDataAvailable:%d fail\n", __LINE__));
    938    mFailed = true;
    939    return rv;
    940  }
    941  return NS_OK;
    942 }
    943 
    944 void TRR::Cancel(nsresult aStatus) {
    945  bool isTRRServiceChannel = false;
    946  nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(
    947      do_QueryInterface(mChannel));
    948  if (httpChannelInternal) {
    949    nsresult rv =
    950        httpChannelInternal->GetIsTRRServiceChannel(&isTRRServiceChannel);
    951    if (NS_FAILED(rv)) {
    952      isTRRServiceChannel = false;
    953    }
    954  }
    955  // nsHttpChannel can be only canceled on the main thread.
    956  RefPtr<nsHttpChannel> httpChannel = do_QueryObject(mChannel);
    957  if (isTRRServiceChannel && !XRE_IsSocketProcess() && !httpChannel) {
    958    if (TRRService::Get()) {
    959      nsCOMPtr<nsIThread> thread = TRRService::Get()->TRRThread();
    960      if (thread && !thread->IsOnCurrentThread()) {
    961        thread->Dispatch(NS_NewRunnableFunction(
    962            "TRR::Cancel",
    963            [self = RefPtr(this), aStatus]() { self->Cancel(aStatus); }));
    964        return;
    965      }
    966    }
    967  } else {
    968    if (!NS_IsMainThread()) {
    969      NS_DispatchToMainThread(NS_NewRunnableFunction(
    970          "TRR::Cancel",
    971          [self = RefPtr(this), aStatus]() { self->Cancel(aStatus); }));
    972      return;
    973    }
    974  }
    975 
    976  if (mCancelled) {
    977    return;
    978  }
    979  mCancelled = true;
    980 
    981  if (mChannel) {
    982    RecordReason(TRRSkippedReason::TRR_REQ_CANCELLED);
    983    LOG(("TRR: %p canceling Channel %p %s %d status=%" PRIx32 "\n", this,
    984         mChannel.get(), mHost.get(), mType, static_cast<uint32_t>(aStatus)));
    985    mChannel->Cancel(aStatus);
    986  }
    987 }
    988 
    989 bool TRR::UseDefaultServer() { return !mRec || mRec->mTrrServer.IsEmpty(); }
    990 
    991 }  // namespace net
    992 }  // namespace mozilla