tor-browser

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

nsHostResolver.cpp (66882B)


      1 /* vim:set ts=4 sw=2 sts=2 et cin: */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "nsIThreadPool.h"
      7 #if defined(HAVE_RES_NINIT)
      8 #  include <sys/types.h>
      9 #  include <netinet/in.h>
     10 #  include <arpa/inet.h>
     11 #  include <arpa/nameser.h>
     12 #  include <resolv.h>
     13 #endif
     14 
     15 #include <stdlib.h>
     16 #include <ctime>
     17 #include "nsHostResolver.h"
     18 #include "nsError.h"
     19 #include "nsIOService.h"
     20 #include "nsISupports.h"
     21 #include "nsISupportsUtils.h"
     22 #include "nsIThreadManager.h"
     23 #include "nsComponentManagerUtils.h"
     24 #include "nsNetUtil.h"
     25 #include "nsPrintfCString.h"
     26 #include "nsXPCOMCIDInternal.h"
     27 #include "prthread.h"
     28 #include "prerror.h"
     29 #include "prtime.h"
     30 #include "mozilla/Logging.h"
     31 #include "PLDHashTable.h"
     32 #include "nsQueryObject.h"
     33 #include "nsURLHelper.h"
     34 #include "nsThreadUtils.h"
     35 #include "nsThreadPool.h"
     36 #include "GetAddrInfo.h"
     37 #include "TRR.h"
     38 #include "TRRQuery.h"
     39 #include "TRRService.h"
     40 
     41 #include "mozilla/Atomics.h"
     42 #include "mozilla/glean/NetwerkMetrics.h"
     43 #include "mozilla/TimeStamp.h"
     44 #include "mozilla/glean/NetwerkDnsMetrics.h"
     45 #include "mozilla/DebugOnly.h"
     46 #include "mozilla/Preferences.h"
     47 #include "mozilla/StaticPrefs_network.h"
     48 // Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
     49 #include "DNSLogging.h"
     50 
     51 #ifdef XP_WIN
     52 #  include "mozilla/WindowsVersion.h"
     53 #endif  // XP_WIN
     54 
     55 #ifdef MOZ_WIDGET_ANDROID
     56 #  include "mozilla/jni/Utils.h"
     57 #endif
     58 
     59 #define IS_ADDR_TYPE(_type) ((_type) == nsIDNSService::RESOLVE_TYPE_DEFAULT)
     60 #define IS_OTHER_TYPE(_type) ((_type) != nsIDNSService::RESOLVE_TYPE_DEFAULT)
     61 
     62 using namespace mozilla;
     63 using namespace mozilla::net;
     64 
     65 // None of our implementations expose a TTL for negative responses, so we use a
     66 // constant always.
     67 static const unsigned int NEGATIVE_RECORD_LIFETIME = 60;
     68 
     69 //----------------------------------------------------------------------------
     70 
     71 // Use a persistent thread pool in order to avoid spinning up new threads all
     72 // the time. In particular, thread creation results in a res_init() call from
     73 // libc which is quite expensive.
     74 //
     75 // The pool dynamically grows between 0 and MaxResolverThreads() in size. New
     76 // requests go first to an idle thread. If that cannot be found and there are
     77 // fewer than MaxResolverThreads() currently in the pool a new thread is created
     78 // for high priority requests. If the new request is at a lower priority a new
     79 // thread will only be created if there are fewer than
     80 // MaxResolverThreadsAnyPriority() currently outstanding. If a thread cannot be
     81 // created or an idle thread located for the request it is queued.
     82 //
     83 // When the pool is greater than MaxResolverThreadsAnyPriority() in size a
     84 // thread will be destroyed after ShortIdleTimeoutSeconds of idle time. Smaller
     85 // pools use LongIdleTimeoutSeconds for a timeout period.
     86 
     87 // for threads 1 -> MaxResolverThreadsAnyPriority()
     88 #define LongIdleTimeoutSeconds 300
     89 // for threads MaxResolverThreadsAnyPriority() + 1 -> MaxResolverThreads()
     90 #define ShortIdleTimeoutSeconds 60
     91 
     92 using namespace mozilla;
     93 
     94 namespace mozilla::net {
     95 LazyLogModule gHostResolverLog("nsHostResolver");
     96 }  // namespace mozilla::net
     97 
     98 //----------------------------------------------------------------------------
     99 
    100 class DnsThreadListener final : public nsIThreadPoolListener {
    101  NS_DECL_THREADSAFE_ISUPPORTS
    102  NS_DECL_NSITHREADPOOLLISTENER
    103 private:
    104  virtual ~DnsThreadListener() = default;
    105 };
    106 
    107 NS_IMETHODIMP
    108 DnsThreadListener::OnThreadCreated() { return NS_OK; }
    109 
    110 NS_IMETHODIMP
    111 DnsThreadListener::OnThreadShuttingDown() {
    112  DNSThreadShutdown();
    113  return NS_OK;
    114 }
    115 
    116 NS_IMPL_ISUPPORTS(DnsThreadListener, nsIThreadPoolListener)
    117 
    118 //----------------------------------------------------------------------------
    119 
    120 mozilla::Atomic<bool, mozilla::Relaxed> sNativeHTTPSSupported{false};
    121 
    122 NS_IMPL_ISUPPORTS0(nsHostResolver)
    123 
    124 nsHostResolver::nsHostResolver()
    125    : mIdleTaskCV(mLock, "nsHostResolver.mIdleTaskCV") {
    126  mCreationTime = PR_Now();
    127 
    128  mLongIdleTimeout = TimeDuration::FromSeconds(LongIdleTimeoutSeconds);
    129  mShortIdleTimeout = TimeDuration::FromSeconds(ShortIdleTimeoutSeconds);
    130 }
    131 
    132 nsHostResolver::~nsHostResolver() = default;
    133 
    134 nsresult nsHostResolver::Init() MOZ_NO_THREAD_SAFETY_ANALYSIS {
    135  MOZ_ASSERT(NS_IsMainThread());
    136  if (NS_FAILED(GetAddrInfoInit())) {
    137    return NS_ERROR_FAILURE;
    138  }
    139 
    140  LOG(("nsHostResolver::Init this=%p", this));
    141 
    142  mShutdown = false;
    143  mNCS = NetworkConnectivityService::GetSingleton();
    144 
    145 #if defined(HAVE_RES_NINIT)
    146  // We want to make sure the system is using the correct resolver settings,
    147  // so we force it to reload those settings whenever we startup a subsequent
    148  // nsHostResolver instance.  We assume that there is no reason to do this
    149  // for the first nsHostResolver instance since that is usually created
    150  // during application startup.
    151  static int initCount = 0;
    152  if (initCount++ > 0) {
    153    auto result = res_ninit(&_res);
    154    LOG(("nsHostResolver::Init > 'res_ninit' returned %d", result));
    155  }
    156 #endif
    157 
    158  // We can configure the threadpool to keep threads alive for a while after
    159  // the last ThreadFunc task has been executed.
    160  int32_t poolTimeoutSecs =
    161      StaticPrefs::network_dns_resolver_thread_extra_idle_time_seconds();
    162  uint32_t poolTimeoutMs;
    163  if (poolTimeoutSecs < 0) {
    164    // This means never shut down the idle threads
    165    poolTimeoutMs = UINT32_MAX;
    166  } else {
    167    // We clamp down the idle time between 0 and one hour.
    168    poolTimeoutMs =
    169        std::clamp<uint32_t>(poolTimeoutSecs * 1000, 0, 3600 * 1000);
    170  }
    171 
    172 #if defined(XP_WIN)
    173  // For some reason, the DNSQuery_A API doesn't work on Windows 10.
    174  // It returns a success code, but no records. We only allow
    175  // native HTTPS records on Win 11 for now.
    176  sNativeHTTPSSupported = mozilla::IsWin11OrLater();
    177 #elif defined(MOZ_WIDGET_ANDROID)
    178  // android_res_nquery only got added in API level 29
    179  sNativeHTTPSSupported = jni::GetAPIVersion() >= 29;
    180 #elif defined(XP_LINUX) || defined(XP_MACOSX)
    181  sNativeHTTPSSupported = true;
    182 #endif
    183  LOG(("Native HTTPS records supported=%d", bool(sNativeHTTPSSupported)));
    184 
    185  // The ThreadFunc has its own loop and will live very long and block one
    186  // thread from the thread pool's point of view, such that the timeouts are
    187  // less important here. The pool is mostly used to provide an easy way to
    188  // create and shutdown those threads.
    189  // TODO: It seems, the ThreadFunc resembles some quite similar timeout and
    190  // wait for events logic as the pool offers, maybe we could simplify this
    191  // a bit, see bug 1478732 for a previous attempt.
    192  nsCOMPtr<nsIThreadPool> threadPool = new nsThreadPool();
    193  MOZ_ALWAYS_SUCCEEDS(threadPool->SetThreadLimit(MaxResolverThreads()));
    194  MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadLimit(
    195      std::max(MaxResolverThreads() / 4, (uint32_t)1)));
    196  MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadMaximumTimeout(poolTimeoutMs));
    197  MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadGraceTimeout(100));
    198  MOZ_ALWAYS_SUCCEEDS(
    199      threadPool->SetThreadStackSize(nsIThreadManager::kThreadPoolStackSize));
    200  MOZ_ALWAYS_SUCCEEDS(threadPool->SetName("DNS Resolver"_ns));
    201  nsCOMPtr<nsIThreadPoolListener> listener = new DnsThreadListener();
    202  threadPool->SetListener(listener);
    203  mResolverThreads = ToRefPtr(std::move(threadPool));
    204 
    205  return NS_OK;
    206 }
    207 
    208 void nsHostResolver::ClearPendingQueue(
    209    LinkedList<RefPtr<nsHostRecord>>& aPendingQ) {
    210  // loop through pending queue, erroring out pending lookups.
    211  if (!aPendingQ.isEmpty()) {
    212    for (const RefPtr<nsHostRecord>& rec : aPendingQ) {
    213      rec->Cancel();
    214      if (rec->IsAddrRecord()) {
    215        CompleteLookup(rec, NS_ERROR_ABORT, nullptr, rec->pb, rec->originSuffix,
    216                       rec->mTRRSkippedReason, nullptr);
    217      } else {
    218        mozilla::net::TypeRecordResultType empty(Nothing{});
    219        CompleteLookupByType(rec, NS_ERROR_ABORT, empty, rec->mTRRSkippedReason,
    220                             0, rec->pb);
    221      }
    222    }
    223  }
    224 }
    225 
    226 //
    227 // FlushCache() is what we call when the network has changed. We must not
    228 // trust names that were resolved before this change. They may resolve
    229 // differently now.
    230 //
    231 // This function removes all existing resolved host entries from the hash.
    232 // Names that are in the pending queues can be left there. Entries in the
    233 // cache that have 'Resolve' set true but not 'OnQueue' are being resolved
    234 // right now, so we need to mark them to get re-resolved on completion!
    235 
    236 void nsHostResolver::FlushCache(bool aTrrToo, bool aFlushEvictionQueue) {
    237  MutexAutoLock lock(mLock);
    238 
    239  if (aFlushEvictionQueue) {
    240    mQueue.FlushEvictionQ(mRecordDB, lock);
    241  }
    242 
    243  // Refresh the cache entries that are resolving RIGHT now, remove the rest.
    244  for (auto iter = mRecordDB.Iter(); !iter.Done(); iter.Next()) {
    245    nsHostRecord* record = iter.UserData();
    246    // Try to remove the record, or mark it for refresh.
    247    // By-type records are from TRR. We do not need to flush those entry
    248    // when the network has change, because they are not local.
    249    if (record->IsAddrRecord()) {
    250      RefPtr<AddrHostRecord> addrRec = do_QueryObject(record);
    251      MOZ_ASSERT(addrRec);
    252      if (addrRec->RemoveOrRefresh(aTrrToo)) {
    253        mQueue.MaybeRemoveFromQ(record, lock);
    254        LOG(("Removing (%s) Addr record from mRecordDB", record->host.get()));
    255        iter.Remove();
    256      }
    257    } else if (aTrrToo) {
    258      // remove by type records
    259      LOG(("Removing (%s) type record from mRecordDB", record->host.get()));
    260      iter.Remove();
    261    }
    262  }
    263 }
    264 
    265 void nsHostResolver::Shutdown() {
    266  LOG(("Shutting down host resolver.\n"));
    267 
    268  LinkedList<RefPtr<nsHostRecord>> pendingQHigh, pendingQMed, pendingQLow,
    269      evictionQ;
    270 
    271  {
    272    MutexAutoLock lock(mLock);
    273 
    274    mShutdown = true;
    275 
    276    if (mNumIdleTasks) {
    277      mIdleTaskCV.NotifyAll();
    278    }
    279 
    280    mQueue.ClearAll(
    281        [&](nsHostRecord* aRec) {
    282          mLock.AssertCurrentThreadOwns();
    283          if (aRec->IsAddrRecord()) {
    284            CompleteLookupLocked(aRec, NS_ERROR_ABORT, nullptr, aRec->pb,
    285                                 aRec->originSuffix, aRec->mTRRSkippedReason,
    286                                 nullptr, lock);
    287          } else {
    288            mozilla::net::TypeRecordResultType empty(Nothing{});
    289            CompleteLookupByTypeLocked(aRec, NS_ERROR_ABORT, empty,
    290                                       aRec->mTRRSkippedReason, 0, aRec->pb,
    291                                       lock);
    292          }
    293        },
    294        lock);
    295 
    296    for (const auto& data : mRecordDB.Values()) {
    297      data->Cancel();
    298    }
    299    // empty host database
    300    mRecordDB.Clear();
    301 
    302    mNCS = nullptr;
    303  }
    304 
    305  // Shutdown the resolver threads, but with a timeout of 5 seconds (prefable).
    306  // If the timeout is exceeded, any stuck threads will be leaked.
    307  mResolverThreads->ShutdownWithTimeout(
    308      StaticPrefs::network_dns_resolver_shutdown_timeout_ms());
    309 
    310  {
    311    mozilla::DebugOnly<nsresult> rv = GetAddrInfoShutdown();
    312    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to shutdown GetAddrInfo");
    313  }
    314 }
    315 
    316 nsresult nsHostResolver::GetHostRecord(
    317    const nsACString& host, const nsACString& aTrrServer, uint16_t type,
    318    nsIDNSService::DNSFlags flags, uint16_t af, bool pb,
    319    const nsCString& originSuffix, nsHostRecord** result) {
    320  MutexAutoLock lock(mLock);
    321  nsHostKey key(host, aTrrServer, type, flags, af, pb, originSuffix);
    322 
    323  RefPtr<nsHostRecord> rec =
    324      mRecordDB.LookupOrInsertWith(key, [&] { return InitRecord(key); });
    325  if (rec->IsAddrRecord()) {
    326    RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
    327    if (addrRec->addr) {
    328      return NS_ERROR_FAILURE;
    329    }
    330  }
    331 
    332  if (rec->mResolving) {
    333    return NS_ERROR_FAILURE;
    334  }
    335 
    336  *result = rec.forget().take();
    337  return NS_OK;
    338 }
    339 
    340 nsHostRecord* nsHostResolver::InitRecord(const nsHostKey& key) {
    341  if (IS_ADDR_TYPE(key.type)) {
    342    return new AddrHostRecord(key);
    343  }
    344  return new TypeHostRecord(key);
    345 }
    346 
    347 namespace {
    348 class NetAddrIPv6FirstComparator {
    349 public:
    350  static bool Equals(const NetAddr& aLhs, const NetAddr& aRhs) {
    351    return aLhs.raw.family == aRhs.raw.family;
    352  }
    353  static bool LessThan(const NetAddr& aLhs, const NetAddr& aRhs) {
    354    return aLhs.raw.family > aRhs.raw.family;
    355  }
    356 };
    357 }  // namespace
    358 
    359 already_AddRefed<nsHostRecord> nsHostResolver::InitLoopbackRecord(
    360    const nsHostKey& key, nsresult* aRv) {
    361  MOZ_ASSERT(aRv);
    362  MOZ_ASSERT(IS_ADDR_TYPE(key.type));
    363 
    364  *aRv = NS_ERROR_FAILURE;
    365  RefPtr<nsHostRecord> rec = InitRecord(key);
    366 
    367  nsTArray<NetAddr> addresses;
    368  NetAddr addr;
    369  if (key.af == PR_AF_INET || key.af == PR_AF_UNSPEC) {
    370    MOZ_RELEASE_ASSERT(NS_SUCCEEDED(addr.InitFromString("127.0.0.1"_ns)));
    371    addresses.AppendElement(addr);
    372  }
    373  if (key.af == PR_AF_INET6 || key.af == PR_AF_UNSPEC) {
    374    MOZ_RELEASE_ASSERT(NS_SUCCEEDED(addr.InitFromString("::1"_ns)));
    375    addresses.AppendElement(addr);
    376  }
    377 
    378  if (StaticPrefs::network_dns_preferIPv6() && addresses.Length() > 1 &&
    379      addresses[0].IsIPAddrV4()) {
    380    // Sort IPv6 addresses first.
    381    addresses.Sort(NetAddrIPv6FirstComparator());
    382  }
    383 
    384  RefPtr<AddrInfo> ai =
    385      new AddrInfo(rec->host, DNSResolverType::Native, 0, std::move(addresses));
    386 
    387  RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
    388  MutexAutoLock lock(addrRec->addr_info_lock);
    389  addrRec->addr_info = ai;
    390  addrRec->SetExpiration(TimeStamp::NowLoRes(),
    391                         StaticPrefs::network_dnsCacheExpiration(),
    392                         StaticPrefs::network_dnsCacheExpirationGracePeriod());
    393  addrRec->negative = false;
    394  // Use the oldest possible timestamp, since the contents of this record never
    395  // change.
    396  addrRec->mLastUpdate = TimeStamp::ProcessCreation();
    397 
    398  *aRv = NS_OK;
    399  return rec.forget();
    400 }
    401 
    402 already_AddRefed<nsHostRecord> nsHostResolver::InitMockHTTPSRecord(
    403    const nsHostKey& key) {
    404  MOZ_ASSERT(IS_OTHER_TYPE(key.type));
    405  if (key.type != nsIDNSService::RESOLVE_TYPE_HTTPSSVC) {
    406    return nullptr;
    407  }
    408 
    409  RefPtr<nsHostRecord> rec = InitRecord(key);
    410  LOG(("InitMockHTTPSRecord host=%s\n", rec->host.get()));
    411 
    412  TypeRecordResultType result = AsVariant(mozilla::Nothing());
    413  uint32_t ttl = UINT32_MAX;
    414  nsresult rv =
    415      CreateAndResolveMockHTTPSRecord(rec->host, rec->flags, result, ttl);
    416  if (NS_FAILED(rv)) {
    417    return nullptr;
    418  }
    419 
    420  RefPtr<TypeHostRecord> typeRec = do_QueryObject(rec);
    421  MutexAutoLock lock(typeRec->mResultsLock);
    422  typeRec->mResults = result;
    423  typeRec->negative = false;
    424  return rec.forget();
    425 }
    426 
    427 // static
    428 bool nsHostResolver::IsNativeHTTPSEnabled() {
    429  if (!StaticPrefs::network_dns_native_https_query()) {
    430    return false;
    431  }
    432 #ifdef XP_WIN
    433  if (StaticPrefs::network_dns_native_https_query_win10()) {
    434    // If this pref is true, we allow resolving HTTPS records.
    435    // It might not work, or we might use the HTTPS override records.
    436    return true;
    437  }
    438 #endif
    439  return sNativeHTTPSSupported;
    440 }
    441 
    442 nsresult nsHostResolver::ResolveHost(const nsACString& aHost,
    443                                     const nsACString& aTrrServer,
    444                                     int32_t aPort, uint16_t type,
    445                                     const OriginAttributes& aOriginAttributes,
    446                                     nsIDNSService::DNSFlags flags, uint16_t af,
    447                                     nsResolveHostCallback* aCallback) {
    448  nsAutoCString host(aHost);
    449  NS_ENSURE_TRUE(!host.IsEmpty(), NS_ERROR_UNEXPECTED);
    450 
    451  nsAutoCString originSuffix;
    452  aOriginAttributes.CreateSuffix(originSuffix);
    453  LOG(("Resolving host [%s]<%s>%s%s type %d. [this=%p]\n", host.get(),
    454       originSuffix.get(),
    455       flags & nsIDNSService::RESOLVE_BYPASS_CACHE ? " - bypassing cache" : "",
    456       flags & nsIDNSService::RESOLVE_REFRESH_CACHE ? " - refresh cache" : "",
    457       type, this));
    458 
    459  // When this pref is set, we always set the flag, to make sure consumers
    460  // that forget to set the flag don't end up being a cache miss.
    461  if (StaticPrefs::network_dns_always_ai_canonname()) {
    462    flags |= nsIDNSService::RESOLVE_CANONICAL_NAME;
    463  }
    464 
    465  // ensure that we are working with a valid hostname before proceeding.  see
    466  // bug 304904 for details.
    467  if (!net_IsValidDNSHost(host)) {
    468    return NS_ERROR_UNKNOWN_HOST;
    469  }
    470 
    471  // If TRR is disabled we can return immediately if the native API is disabled
    472  if (!IsNativeHTTPSEnabled() && IS_OTHER_TYPE(type) &&
    473      Mode() == nsIDNSService::MODE_TRROFF) {
    474    return NS_ERROR_UNKNOWN_HOST;
    475  }
    476 
    477  // Used to try to parse to an IP address literal.
    478  NetAddr tempAddr;
    479  if (IS_OTHER_TYPE(type) && (NS_SUCCEEDED(tempAddr.InitFromString(host)))) {
    480    // For by-type queries the host cannot be IP literal.
    481    return NS_ERROR_UNKNOWN_HOST;
    482  }
    483 
    484  RefPtr<nsResolveHostCallback> callback(aCallback);
    485  // if result is set inside the lock, then we need to issue the
    486  // callback before returning.
    487  RefPtr<nsHostRecord> result;
    488  nsresult status = NS_OK, rv = NS_OK;
    489  {
    490    MutexAutoLock lock(mLock);
    491 
    492    if (mShutdown) {
    493      return NS_ERROR_NOT_INITIALIZED;
    494    }
    495 
    496    // check to see if there is already an entry for this |host|
    497    // in the hash table.  if so, then check to see if we can't
    498    // just reuse the lookup result.  otherwise, if there are
    499    // any pending callbacks, then add to pending callbacks queue,
    500    // and return.  otherwise, add ourselves as first pending
    501    // callback, and proceed to do the lookup.
    502 
    503    Maybe<nsCString> originHost;
    504    if (StaticPrefs::network_dns_port_prefixed_qname_https_rr() &&
    505        type == nsIDNSService::RESOLVE_TYPE_HTTPSSVC && aPort != -1 &&
    506        aPort != 443) {
    507      originHost = Some(host);
    508      host = nsPrintfCString("_%d._https.%s", aPort, host.get());
    509      LOG(("  Using port prefixed host name [%s]", host.get()));
    510    }
    511 
    512    bool excludedFromTRR = false;
    513    if (TRRService::Get() && TRRService::Get()->IsExcludedFromTRR(host)) {
    514      flags |= nsIDNSService::RESOLVE_DISABLE_TRR;
    515      flags |= nsIDNSService::RESOLVE_DISABLE_NATIVE_HTTPS_QUERY;
    516      excludedFromTRR = true;
    517 
    518      if (!aTrrServer.IsEmpty()) {
    519        return NS_ERROR_UNKNOWN_HOST;
    520      }
    521    }
    522 
    523    nsHostKey key(host, aTrrServer, type, flags, af,
    524                  (aOriginAttributes.IsPrivateBrowsing()), originSuffix);
    525 
    526    // Check if we have a localhost domain, if so hardcode to loopback
    527    if (IS_ADDR_TYPE(type) && IsLoopbackHostname(host)) {
    528      nsresult rv;
    529      RefPtr<nsHostRecord> result = InitLoopbackRecord(key, &rv);
    530      if (NS_WARN_IF(NS_FAILED(rv))) {
    531        return rv;
    532      }
    533      MOZ_ASSERT(result);
    534      aCallback->OnResolveHostComplete(this, result, NS_OK);
    535      return NS_OK;
    536    }
    537 
    538    if (flags & nsIDNSService::RESOLVE_CREATE_MOCK_HTTPS_RR) {
    539      RefPtr<nsHostRecord> result = InitMockHTTPSRecord(key);
    540      aCallback->OnResolveHostComplete(this, result,
    541                                       result ? NS_OK : NS_ERROR_UNKNOWN_HOST);
    542      return NS_OK;
    543    }
    544 
    545    RefPtr<nsHostRecord> rec =
    546        mRecordDB.LookupOrInsertWith(key, [&] { return InitRecord(key); });
    547 
    548    RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
    549    MOZ_ASSERT(rec, "Record should not be null");
    550    MOZ_ASSERT((IS_ADDR_TYPE(type) && rec->IsAddrRecord() && addrRec) ||
    551               (IS_OTHER_TYPE(type) && !rec->IsAddrRecord()));
    552 
    553    if (IS_OTHER_TYPE(type) && originHost) {
    554      RefPtr<TypeHostRecord> typeRec = do_QueryObject(rec);
    555      MutexAutoLock lock(typeRec->mResultsLock);
    556      typeRec->mOriginHost = std::move(originHost);
    557    }
    558 
    559    if (excludedFromTRR) {
    560      rec->RecordReason(TRRSkippedReason::TRR_EXCLUDED);
    561    }
    562 
    563    if (!(flags & nsIDNSService::RESOLVE_BYPASS_CACHE) &&
    564        rec->HasUsableResult(TimeStamp::NowLoRes(), flags)) {
    565      result = FromCache(rec, host, type, status, lock);
    566    } else if (addrRec && addrRec->addr) {
    567      // if the host name is an IP address literal and has been
    568      // parsed, go ahead and use it.
    569      LOG(("  Using cached address for IP Literal [%s].\n", host.get()));
    570      result = FromCachedIPLiteral(rec);
    571    } else if (addrRec && NS_SUCCEEDED(tempAddr.InitFromString(host))) {
    572      // try parsing the host name as an IP address literal to short
    573      // circuit full host resolution.  (this is necessary on some
    574      // platforms like Win9x.  see bug 219376 for more details.)
    575      LOG(("  Host is IP Literal [%s].\n", host.get()));
    576      result = FromIPLiteral(addrRec, tempAddr);
    577    } else if (mQueue.PendingCount() >= MAX_NON_PRIORITY_REQUESTS &&
    578               !IsHighPriority(flags) && !rec->mResolving) {
    579      LOG(
    580          ("  Lookup queue full: dropping %s priority request for "
    581           "host [%s].\n",
    582           IsMediumPriority(flags) ? "medium" : "low", host.get()));
    583      if (IS_ADDR_TYPE(type)) {
    584        glean::dns::lookup_method.AccumulateSingleSample(METHOD_OVERFLOW);
    585      }
    586      // This is a lower priority request and we are swamped, so refuse it.
    587      rv = NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
    588 
    589      // Check if the offline flag is set.
    590    } else if (flags & nsIDNSService::RESOLVE_OFFLINE) {
    591      LOG(("  Offline request for host [%s]; ignoring.\n", host.get()));
    592      rv = NS_ERROR_OFFLINE;
    593 
    594      // We do not have a valid result till here.
    595      // A/AAAA request can check for an alternative entry like AF_UNSPEC.
    596      // Otherwise we need to start a new query.
    597    } else if (!rec->mResolving) {
    598      result =
    599          FromUnspecEntry(rec, host, aTrrServer, originSuffix, type, flags, af,
    600                          aOriginAttributes.IsPrivateBrowsing(), status, lock);
    601      // If this is a by-type request or if no valid record was found
    602      // in the cache or this is an AF_UNSPEC request, then start a
    603      // new lookup.
    604      if (!result) {
    605        LOG(("  No usable record in cache for host [%s] type %d.", host.get(),
    606             type));
    607 
    608        if (flags & nsIDNSService::RESOLVE_REFRESH_CACHE) {
    609          rec->Invalidate();
    610        }
    611 
    612        // Add callback to the list of pending callbacks.
    613        rec->mCallbacks.insertBack(callback);
    614        rec->flags = flags;
    615        rv = NameLookup(rec, lock);
    616        if (IS_ADDR_TYPE(type)) {
    617          glean::dns::lookup_method.AccumulateSingleSample(
    618              METHOD_NETWORK_FIRST);
    619        }
    620        if (NS_FAILED(rv) && callback->isInList()) {
    621          callback->remove();
    622        } else {
    623          LOG(
    624              ("  DNS lookup for host [%s] blocking "
    625               "pending 'getaddrinfo' or trr query: "
    626               "callback [%p]",
    627               host.get(), callback.get()));
    628        }
    629      }
    630    } else {
    631      LOG(
    632          ("  Host [%s] is being resolved. Appending callback "
    633           "[%p].",
    634           host.get(), callback.get()));
    635 
    636      rec->mCallbacks.insertBack(callback);
    637 
    638      if (rec && rec->onQueue()) {
    639        glean::dns::lookup_method.AccumulateSingleSample(METHOD_NETWORK_SHARED);
    640 
    641        // Consider the case where we are on a pending queue of
    642        // lower priority than the request is being made at.
    643        // In that case we should upgrade to the higher queue.
    644 
    645        if (IsHighPriority(flags) && !IsHighPriority(rec->flags)) {
    646          // Move from (low|med) to high.
    647          mQueue.MoveToAnotherPendingQ(rec, flags, lock);
    648          rec->flags = flags;
    649          ConditionallyCreateThread(rec);
    650        } else if (IsMediumPriority(flags) && IsLowPriority(rec->flags)) {
    651          // Move from low to med.
    652          mQueue.MoveToAnotherPendingQ(rec, flags, lock);
    653          rec->flags = flags;
    654          mIdleTaskCV.Notify();
    655        }
    656      }
    657    }
    658 
    659    if (result && callback->isInList()) {
    660      callback->remove();
    661    }
    662  }  // lock
    663 
    664  if (result) {
    665    callback->OnResolveHostComplete(this, result, status);
    666  }
    667 
    668  return rv;
    669 }
    670 
    671 already_AddRefed<nsHostRecord> nsHostResolver::FromCache(
    672    nsHostRecord* aRec, const nsACString& aHost, uint16_t aType,
    673    nsresult& aStatus, const MutexAutoLock& aLock) {
    674  LOG(("  Using cached record for host [%s].\n",
    675       nsPromiseFlatCString(aHost).get()));
    676 
    677  // put reference to host record on stack...
    678  RefPtr<nsHostRecord> result = aRec;
    679 
    680  // For cached entries that are in the grace period or negative, use the cache
    681  // but start a new lookup in the background.
    682  //
    683  // Also records telemetry for type of cache hit (HIT/NEGATIVE_HIT/RENEWAL).
    684  ConditionallyRefreshRecord(aRec, aHost, aLock);
    685 
    686  if (aRec->negative) {
    687    LOG(("  Negative cache entry for host [%s].\n",
    688         nsPromiseFlatCString(aHost).get()));
    689    aStatus = NS_ERROR_UNKNOWN_HOST;
    690  } else if (StaticPrefs::network_dns_mru_to_tail()) {
    691    mQueue.MoveToEvictionQueueTail(aRec, aLock);
    692  }
    693 
    694  return result.forget();
    695 }
    696 
    697 already_AddRefed<nsHostRecord> nsHostResolver::FromCachedIPLiteral(
    698    nsHostRecord* aRec) {
    699  glean::dns::lookup_method.AccumulateSingleSample(METHOD_LITERAL);
    700  RefPtr<nsHostRecord> result = aRec;
    701  return result.forget();
    702 }
    703 
    704 already_AddRefed<nsHostRecord> nsHostResolver::FromIPLiteral(
    705    AddrHostRecord* aAddrRec, const NetAddr& aAddr) {
    706  // ok, just copy the result into the host record, and be
    707  // done with it! ;-)
    708  aAddrRec->addr = MakeUnique<NetAddr>(aAddr);
    709  glean::dns::lookup_method.AccumulateSingleSample(METHOD_LITERAL);
    710  // put reference to host record on stack...
    711  RefPtr<nsHostRecord> result = aAddrRec;
    712  return result.forget();
    713 }
    714 
    715 already_AddRefed<nsHostRecord> nsHostResolver::FromUnspecEntry(
    716    nsHostRecord* aRec, const nsACString& aHost, const nsACString& aTrrServer,
    717    const nsACString& aOriginSuffix, uint16_t aType,
    718    nsIDNSService::DNSFlags aFlags, uint16_t af, bool aPb, nsresult& aStatus,
    719    const MutexAutoLock& aLock) {
    720  RefPtr<nsHostRecord> result = nullptr;
    721  // If this is an IPV4 or IPV6 specific request, check if there is
    722  // an AF_UNSPEC entry we can use. Otherwise, hit the resolver...
    723  RefPtr<AddrHostRecord> addrRec = do_QueryObject(aRec);
    724  if (addrRec && !(aFlags & nsIDNSService::RESOLVE_BYPASS_CACHE) &&
    725      ((af == PR_AF_INET) || (af == PR_AF_INET6))) {
    726    // Check for an AF_UNSPEC entry.
    727 
    728    const nsHostKey unspecKey(aHost, aTrrServer,
    729                              nsIDNSService::RESOLVE_TYPE_DEFAULT, aFlags,
    730                              PR_AF_UNSPEC, aPb, aOriginSuffix);
    731    RefPtr<nsHostRecord> unspecRec = mRecordDB.Get(unspecKey);
    732 
    733    TimeStamp now = TimeStamp::NowLoRes();
    734    if (unspecRec && unspecRec->HasUsableResult(now, aFlags)) {
    735      MOZ_ASSERT(unspecRec->IsAddrRecord());
    736 
    737      RefPtr<AddrHostRecord> addrUnspecRec = do_QueryObject(unspecRec);
    738      MOZ_ASSERT(addrUnspecRec);
    739      MOZ_ASSERT(addrUnspecRec->addr_info || addrUnspecRec->negative,
    740                 "Entry should be resolved or negative.");
    741 
    742      LOG(("  Trying AF_UNSPEC entry for host [%s] af: %s.\n",
    743           PromiseFlatCString(aHost).get(),
    744           (af == PR_AF_INET) ? "AF_INET" : "AF_INET6"));
    745 
    746      // We need to lock in case any other thread is reading
    747      // addr_info.
    748      MutexAutoLock lock(addrRec->addr_info_lock);
    749 
    750      addrRec->addr_info = nullptr;
    751      addrRec->addr_info_gencnt++;
    752      if (unspecRec->negative) {
    753        aRec->negative = unspecRec->negative;
    754        aRec->CopyExpirationTimesAndFlagsFrom(unspecRec);
    755      } else if (addrUnspecRec->addr_info) {
    756        MutexAutoLock lock(addrUnspecRec->addr_info_lock);
    757        if (addrUnspecRec->addr_info) {
    758          // Search for any valid address in the AF_UNSPEC entry
    759          // in the cache (not blocklisted and from the right
    760          // family).
    761          nsTArray<NetAddr> addresses;
    762          for (const auto& addr : addrUnspecRec->addr_info->Addresses()) {
    763            if ((af == addr.inet.family) &&
    764                !addrUnspecRec->Blocklisted(&addr)) {
    765              addresses.AppendElement(addr);
    766            }
    767          }
    768          if (!addresses.IsEmpty()) {
    769            addrRec->addr_info = new AddrInfo(
    770                addrUnspecRec->addr_info->Hostname(),
    771                addrUnspecRec->addr_info->CanonicalHostname(),
    772                addrUnspecRec->addr_info->ResolverType(),
    773                addrUnspecRec->addr_info->TRRType(), std::move(addresses));
    774            addrRec->addr_info_gencnt++;
    775            aRec->CopyExpirationTimesAndFlagsFrom(unspecRec);
    776          }
    777        }
    778      }
    779      // Now check if we have a new record.
    780      if (aRec->HasUsableResult(now, aFlags)) {
    781        result = aRec;
    782        if (aRec->negative) {
    783          aStatus = NS_ERROR_UNKNOWN_HOST;
    784        }
    785        ConditionallyRefreshRecord(aRec, aHost, lock);
    786      } else if (af == PR_AF_INET6) {
    787        // For AF_INET6, a new lookup means another AF_UNSPEC
    788        // lookup. We have already iterated through the
    789        // AF_UNSPEC addresses, so we mark this record as
    790        // negative.
    791        LOG(
    792            ("  No AF_INET6 in AF_UNSPEC entry: "
    793             "host [%s] unknown host.",
    794             nsPromiseFlatCString(aHost).get()));
    795        result = aRec;
    796        aRec->negative = true;
    797        aStatus = NS_ERROR_UNKNOWN_HOST;
    798        // this record has just been marked as negative so we record the
    799        // telemetry for it.
    800        glean::dns::lookup_method.AccumulateSingleSample(METHOD_NEGATIVE_HIT);
    801      }
    802    }
    803  }
    804 
    805  return result.forget();
    806 }
    807 
    808 void nsHostResolver::DetachCallback(
    809    const nsACString& host, const nsACString& aTrrServer, uint16_t aType,
    810    const OriginAttributes& aOriginAttributes, nsIDNSService::DNSFlags flags,
    811    uint16_t af, nsResolveHostCallback* aCallback, nsresult status) {
    812  RefPtr<nsHostRecord> rec;
    813  RefPtr<nsResolveHostCallback> callback(aCallback);
    814 
    815  {
    816    MutexAutoLock lock(mLock);
    817 
    818    nsAutoCString originSuffix;
    819    aOriginAttributes.CreateSuffix(originSuffix);
    820 
    821    nsHostKey key(host, aTrrServer, aType, flags, af,
    822                  (aOriginAttributes.IsPrivateBrowsing()), originSuffix);
    823    RefPtr<nsHostRecord> entry = mRecordDB.Get(key);
    824    if (entry) {
    825      // walk list looking for |callback|... we cannot assume
    826      // that it will be there!
    827 
    828      for (nsResolveHostCallback* c : entry->mCallbacks) {
    829        if (c == callback) {
    830          rec = entry;
    831          c->remove();
    832          break;
    833        }
    834      }
    835    }
    836  }
    837 
    838  // complete callback with the given status code; this would only be done if
    839  // the record was in the process of being resolved.
    840  if (rec) {
    841    callback->OnResolveHostComplete(this, rec, status);
    842  }
    843 }
    844 
    845 nsresult nsHostResolver::ConditionallyCreateThread(nsHostRecord* rec) {
    846  if (mNumIdleTasks) {
    847    // wake up idle tasks to process this lookup
    848    mIdleTaskCV.Notify();
    849  } else if ((mActiveTaskCount < MaxResolverThreadsAnyPriority()) ||
    850             (IsHighPriority(rec->flags) &&
    851              mActiveTaskCount < MaxResolverThreads())) {
    852    nsCOMPtr<nsIRunnable> event = mozilla::NewRunnableMethod(
    853        "nsHostResolver::ThreadFunc", this, &nsHostResolver::ThreadFunc);
    854    mActiveTaskCount++;
    855    nsresult rv =
    856        mResolverThreads->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
    857    if (NS_FAILED(rv)) {
    858      mActiveTaskCount--;
    859    }
    860  } else {
    861    LOG(("  Unable to find a thread for looking up host [%s].\n",
    862         rec->host.get()));
    863  }
    864  return NS_OK;
    865 }
    866 
    867 nsresult nsHostResolver::TrrLookup_unlocked(nsHostRecord* rec, TRR* pushedTRR) {
    868  MutexAutoLock lock(mLock);
    869  return TrrLookup(rec, lock, pushedTRR);
    870 }
    871 
    872 void nsHostResolver::MaybeRenewHostRecord(nsHostRecord* aRec) {
    873  MutexAutoLock lock(mLock);
    874  MaybeRenewHostRecordLocked(aRec, lock);
    875 }
    876 
    877 void nsHostResolver::MaybeRenewHostRecordLocked(nsHostRecord* aRec,
    878                                                const MutexAutoLock& aLock) {
    879  mQueue.MaybeRenewHostRecord(aRec, aLock);
    880 }
    881 
    882 bool nsHostResolver::TRRServiceEnabledForRecord(nsHostRecord* aRec) {
    883  MOZ_ASSERT(aRec, "Record must not be empty");
    884  MOZ_ASSERT(aRec->mEffectiveTRRMode != nsIRequest::TRR_DEFAULT_MODE,
    885             "effective TRR mode must be computed before this call");
    886  if (!TRRService::Get()) {
    887    aRec->RecordReason(TRRSkippedReason::TRR_NO_GSERVICE);
    888    return false;
    889  }
    890 
    891  // We always try custom resolvers.
    892  if (!aRec->mTrrServer.IsEmpty()) {
    893    return true;
    894  }
    895 
    896  nsIRequest::TRRMode reqMode = aRec->mEffectiveTRRMode;
    897  if (TRRService::Get()->Enabled(reqMode)) {
    898    return true;
    899  }
    900 
    901  if (gIOService->InSleepMode()) {
    902    aRec->RecordReason(TRRSkippedReason::TRR_SYSTEM_SLEEP_MODE);
    903    return false;
    904  }
    905  if (NS_IsOffline()) {
    906    // If we are in the NOT_CONFIRMED state _because_ we lack connectivity,
    907    // then we should report that the browser is offline instead.
    908    aRec->RecordReason(TRRSkippedReason::TRR_BROWSER_IS_OFFLINE);
    909    return false;
    910  }
    911 
    912  auto hasConnectivity = [this]() -> bool {
    913    mLock.AssertCurrentThreadOwns();
    914    if (!mNCS) {
    915      return true;
    916    }
    917    nsINetworkConnectivityService::ConnectivityState ipv4 = mNCS->GetIPv4();
    918    nsINetworkConnectivityService::ConnectivityState ipv6 = mNCS->GetIPv6();
    919 
    920    if (ipv4 == nsINetworkConnectivityService::OK ||
    921        ipv6 == nsINetworkConnectivityService::OK) {
    922      return true;
    923    }
    924 
    925    if (ipv4 == nsINetworkConnectivityService::UNKNOWN ||
    926        ipv6 == nsINetworkConnectivityService::UNKNOWN) {
    927      // One of the checks hasn't completed yet. Optimistically assume we'll
    928      // have network connectivity.
    929      return true;
    930    }
    931 
    932    return false;
    933  };
    934 
    935  if (!hasConnectivity()) {
    936    aRec->RecordReason(TRRSkippedReason::TRR_NO_CONNECTIVITY);
    937    return false;
    938  }
    939 
    940  bool isConfirmed = TRRService::Get()->IsConfirmed();
    941  if (!isConfirmed) {
    942    aRec->RecordReason(TRRSkippedReason::TRR_NOT_CONFIRMED);
    943  }
    944 
    945  return isConfirmed;
    946 }
    947 
    948 // returns error if no TRR resolve is issued
    949 // it is impt this is not called while a native lookup is going on
    950 nsresult nsHostResolver::TrrLookup(nsHostRecord* aRec,
    951                                   const MutexAutoLock& aLock, TRR* pushedTRR) {
    952  if (Mode() == nsIDNSService::MODE_TRROFF ||
    953      StaticPrefs::network_dns_disabled()) {
    954    return NS_ERROR_UNKNOWN_HOST;
    955  }
    956  LOG(("TrrLookup host:%s af:%" PRId16, aRec->host.get(), aRec->af));
    957 
    958  RefPtr<nsHostRecord> rec(aRec);
    959  mLock.AssertCurrentThreadOwns();
    960 
    961  RefPtr<AddrHostRecord> addrRec;
    962  RefPtr<TypeHostRecord> typeRec;
    963 
    964  if (rec->IsAddrRecord()) {
    965    addrRec = do_QueryObject(rec);
    966    MOZ_ASSERT(addrRec);
    967  } else {
    968    typeRec = do_QueryObject(rec);
    969    MOZ_ASSERT(typeRec);
    970  }
    971 
    972  MOZ_ASSERT(!rec->mResolving);
    973 
    974  if (!TRRServiceEnabledForRecord(aRec)) {
    975    return NS_ERROR_UNKNOWN_HOST;
    976  }
    977 
    978  MaybeRenewHostRecordLocked(rec, aLock);
    979 
    980  RefPtr<TRRQuery> query = new TRRQuery(this, rec);
    981  nsresult rv = query->DispatchLookup(pushedTRR);
    982  if (NS_FAILED(rv)) {
    983    rec->RecordReason(TRRSkippedReason::TRR_DID_NOT_MAKE_QUERY);
    984    return rv;
    985  }
    986 
    987  {
    988    auto lock = rec->mTRRQuery.Lock();
    989    MOZ_ASSERT(!lock.ref(), "TRR already in progress");
    990    lock.ref() = query;
    991  }
    992 
    993  rec->mResolving++;
    994  rec->mTrrAttempts++;
    995  rec->StoreNative(false);
    996  return NS_OK;
    997 }
    998 
    999 nsresult nsHostResolver::NativeLookup(nsHostRecord* aRec,
   1000                                      const MutexAutoLock& aLock) {
   1001  if (StaticPrefs::network_dns_disabled()) {
   1002    return NS_ERROR_UNKNOWN_HOST;
   1003  }
   1004  LOG(("NativeLookup host:%s af:%" PRId16, aRec->host.get(), aRec->af));
   1005 
   1006  // If this is not a A/AAAA request, make sure native HTTPS is enabled.
   1007  MOZ_ASSERT(aRec->IsAddrRecord() || IsNativeHTTPSEnabled());
   1008  mLock.AssertCurrentThreadOwns();
   1009 
   1010  RefPtr<nsHostRecord> rec(aRec);
   1011 
   1012  rec->mNativeStart = TimeStamp::Now();
   1013 
   1014  // Add rec to one of the pending queues, possibly removing it from mEvictionQ.
   1015  MaybeRenewHostRecordLocked(aRec, aLock);
   1016 
   1017  mQueue.InsertRecord(rec, rec->flags, aLock);
   1018 
   1019  rec->StoreNative(true);
   1020  rec->StoreNativeUsed(true);
   1021  rec->mResolving++;
   1022 
   1023  nsresult rv = ConditionallyCreateThread(rec);
   1024 
   1025  LOG(("  DNS thread counters: total=%d any-live=%d idle=%d pending=%d\n",
   1026       static_cast<uint32_t>(mActiveTaskCount),
   1027       static_cast<uint32_t>(mActiveAnyThreadCount),
   1028       static_cast<uint32_t>(mNumIdleTasks), mQueue.PendingCount()));
   1029 
   1030  return rv;
   1031 }
   1032 
   1033 // static
   1034 nsIDNSService::ResolverMode nsHostResolver::Mode() {
   1035  if (TRRService::Get()) {
   1036    return TRRService::Get()->Mode();
   1037  }
   1038 
   1039  // If we don't have a TRR service just return MODE_TRROFF so we don't make
   1040  // any TRR requests by mistake.
   1041  return nsIDNSService::MODE_TRROFF;
   1042 }
   1043 
   1044 nsIRequest::TRRMode nsHostRecord::TRRMode() {
   1045  return nsIDNSService::GetTRRModeFromFlags(flags);
   1046 }
   1047 
   1048 // static
   1049 void nsHostResolver::ComputeEffectiveTRRMode(nsHostRecord* aRec) {
   1050  nsIDNSService::ResolverMode resolverMode = nsHostResolver::Mode();
   1051  nsIRequest::TRRMode requestMode = aRec->TRRMode();
   1052 
   1053  // For domains that are excluded from TRR or when parental control is enabled,
   1054  // we fallback to NativeLookup. This happens even in MODE_TRRONLY. By default
   1055  // localhost and local are excluded (so we cover *.local hosts) See the
   1056  // network.trr.excluded-domains pref.
   1057 
   1058  if (!TRRService::Get()) {
   1059    aRec->RecordReason(TRRSkippedReason::TRR_NO_GSERVICE);
   1060    aRec->mEffectiveTRRMode = requestMode;
   1061    return;
   1062  }
   1063 
   1064  if (!aRec->mTrrServer.IsEmpty()) {
   1065    aRec->mEffectiveTRRMode = nsIRequest::TRR_ONLY_MODE;
   1066    return;
   1067  }
   1068 
   1069  if (TRRService::Get()->IsExcludedFromTRR(aRec->host)) {
   1070    aRec->RecordReason(TRRSkippedReason::TRR_EXCLUDED);
   1071    aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
   1072    return;
   1073  }
   1074 
   1075  if (TRRService::Get()->ParentalControlEnabled()) {
   1076    aRec->RecordReason(TRRSkippedReason::TRR_PARENTAL_CONTROL);
   1077    aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
   1078    return;
   1079  }
   1080 
   1081  if (resolverMode == nsIDNSService::MODE_TRROFF) {
   1082    aRec->RecordReason(TRRSkippedReason::TRR_OFF_EXPLICIT);
   1083    aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
   1084    return;
   1085  }
   1086 
   1087  if (requestMode == nsIRequest::TRR_DISABLED_MODE) {
   1088    aRec->RecordReason(TRRSkippedReason::TRR_REQ_MODE_DISABLED);
   1089    aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
   1090    return;
   1091  }
   1092 
   1093  if ((requestMode == nsIRequest::TRR_DEFAULT_MODE &&
   1094       resolverMode == nsIDNSService::MODE_NATIVEONLY)) {
   1095    aRec->RecordReason(TRRSkippedReason::TRR_MODE_NOT_ENABLED);
   1096    aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
   1097    return;
   1098  }
   1099 
   1100  if (requestMode == nsIRequest::TRR_DEFAULT_MODE &&
   1101      resolverMode == nsIDNSService::MODE_TRRFIRST) {
   1102    aRec->mEffectiveTRRMode = nsIRequest::TRR_FIRST_MODE;
   1103    return;
   1104  }
   1105 
   1106  if (requestMode == nsIRequest::TRR_DEFAULT_MODE &&
   1107      resolverMode == nsIDNSService::MODE_TRRONLY) {
   1108    aRec->mEffectiveTRRMode = nsIRequest::TRR_ONLY_MODE;
   1109    return;
   1110  }
   1111 
   1112  aRec->mEffectiveTRRMode = requestMode;
   1113 }
   1114 
   1115 // Kick-off a name resolve operation, using native resolver and/or TRR
   1116 nsresult nsHostResolver::NameLookup(nsHostRecord* rec,
   1117                                    const mozilla::MutexAutoLock& aLock) {
   1118  LOG(("NameLookup host:%s af:%" PRId16, rec->host.get(), rec->af));
   1119  mLock.AssertCurrentThreadOwns();
   1120 
   1121  if (rec->flags & nsIDNSService::RESOLVE_IP_HINT) {
   1122    LOG(("Skip lookup if nsIDNSService::RESOLVE_IP_HINT is set\n"));
   1123    return NS_ERROR_UNKNOWN_HOST;
   1124  }
   1125 
   1126  nsresult rv = NS_ERROR_UNKNOWN_HOST;
   1127  if (rec->mResolving) {
   1128    LOG(("NameLookup %s while already resolving\n", rec->host.get()));
   1129    return NS_OK;
   1130  }
   1131 
   1132  // Make sure we reset the reason each time we attempt to do a new lookup
   1133  // so we don't wrongly report the reason for the previous one.
   1134  rec->Reset();
   1135 
   1136  ComputeEffectiveTRRMode(rec);
   1137 
   1138  if (!rec->mTrrServer.IsEmpty()) {
   1139    LOG(("NameLookup: %s use trr:%s", rec->host.get(), rec->mTrrServer.get()));
   1140    if (rec->mEffectiveTRRMode != nsIRequest::TRR_ONLY_MODE) {
   1141      return NS_ERROR_UNKNOWN_HOST;
   1142    }
   1143 
   1144    if (rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR) {
   1145      LOG(("TRR with server and DISABLE_TRR flag. Returning error."));
   1146      return NS_ERROR_UNKNOWN_HOST;
   1147    }
   1148    return TrrLookup(rec, aLock);
   1149  }
   1150 
   1151  LOG(("NameLookup: %s effectiveTRRmode: %d flags: %X", rec->host.get(),
   1152       static_cast<nsIRequest::TRRMode>(rec->mEffectiveTRRMode),
   1153       static_cast<uint32_t>(rec->flags)));
   1154 
   1155  if (rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR) {
   1156    rec->RecordReason(TRRSkippedReason::TRR_DISABLED_FLAG);
   1157  }
   1158 
   1159  bool serviceNotReady = !TRRServiceEnabledForRecord(rec);
   1160 
   1161  if (rec->mEffectiveTRRMode != nsIRequest::TRR_DISABLED_MODE &&
   1162      !((rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR)) &&
   1163      !serviceNotReady) {
   1164    rv = TrrLookup(rec, aLock);
   1165  }
   1166 
   1167  if (rec->mEffectiveTRRMode == nsIRequest::TRR_DISABLED_MODE ||
   1168      (rec->mEffectiveTRRMode == nsIRequest::TRR_FIRST_MODE &&
   1169       (rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR || serviceNotReady ||
   1170        NS_FAILED(rv)))) {
   1171    if (!rec->IsAddrRecord()) {
   1172      if (!IsNativeHTTPSEnabled()) {
   1173        return NS_ERROR_UNKNOWN_HOST;
   1174      }
   1175 
   1176      if (rec->flags & nsIDNSService::RESOLVE_DISABLE_NATIVE_HTTPS_QUERY) {
   1177        return NS_ERROR_UNKNOWN_HOST;
   1178      }
   1179    }
   1180 
   1181 #ifdef DEBUG
   1182    // If we use this branch then the mTRRUsed flag should not be set
   1183    // Even if we did call TrrLookup above, the fact that it failed sync-ly
   1184    // means that we didn't actually succeed in opening the channel.
   1185    RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
   1186    MOZ_ASSERT_IF(addrRec, addrRec->mResolverType == DNSResolverType::Native);
   1187 #endif
   1188 
   1189    rv = NativeLookup(rec, aLock);
   1190  }
   1191 
   1192  return rv;
   1193 }
   1194 
   1195 nsresult nsHostResolver::ConditionallyRefreshRecord(
   1196    nsHostRecord* rec, const nsACString& host, const MutexAutoLock& aLock) {
   1197  if ((rec->CheckExpiration(TimeStamp::NowLoRes()) == nsHostRecord::EXP_GRACE ||
   1198       rec->negative) &&
   1199      !rec->mResolving && rec->RefreshForNegativeResponse()) {
   1200    LOG(("  Using %s cache entry for host [%s] but starting async renewal.",
   1201         rec->negative ? "negative" : "positive", host.BeginReading()));
   1202    NameLookup(rec, aLock);
   1203 
   1204    if (rec->IsAddrRecord()) {
   1205      if (!rec->negative) {
   1206        glean::dns::lookup_method.AccumulateSingleSample(METHOD_RENEWAL);
   1207      } else {
   1208        glean::dns::lookup_method.AccumulateSingleSample(METHOD_NEGATIVE_HIT);
   1209      }
   1210    }
   1211  } else if (rec->IsAddrRecord()) {
   1212    // it could be that the record is negative, but we haven't entered the above
   1213    // if condition due to the second expression being false. In that case we
   1214    // need to record the telemetry for the negative record here.
   1215    if (!rec->negative) {
   1216      glean::dns::lookup_method.AccumulateSingleSample(METHOD_HIT);
   1217    } else {
   1218      glean::dns::lookup_method.AccumulateSingleSample(METHOD_NEGATIVE_HIT);
   1219    }
   1220  }
   1221 
   1222  return NS_OK;
   1223 }
   1224 
   1225 bool nsHostResolver::GetHostToLookup(nsHostRecord** result) {
   1226  bool timedOut = false;
   1227  TimeDuration timeout;
   1228  TimeStamp epoch, now;
   1229 
   1230  MutexAutoLock lock(mLock);
   1231 
   1232  timeout = (mNumIdleTasks >= MaxResolverThreadsAnyPriority())
   1233                ? mShortIdleTimeout
   1234                : mLongIdleTimeout;
   1235  epoch = TimeStamp::Now();
   1236 
   1237  while (!mShutdown) {
   1238    // remove next record from Q; hand over owning reference. Check high, then
   1239    // med, then low
   1240 
   1241 #define SET_GET_TTL(var, val) \
   1242  (var)->StoreGetTtl(StaticPrefs::network_dns_get_ttl() && (val))
   1243 
   1244    RefPtr<nsHostRecord> rec = mQueue.Dequeue(true, lock);
   1245    if (rec) {
   1246      SET_GET_TTL(rec, false);
   1247      rec.forget(result);
   1248      return true;
   1249    }
   1250 
   1251    if (mActiveAnyThreadCount < MaxResolverThreadsAnyPriority()) {
   1252      rec = mQueue.Dequeue(false, lock);
   1253      if (rec) {
   1254        MOZ_ASSERT(IsMediumPriority(rec->flags) || IsLowPriority(rec->flags));
   1255        mActiveAnyThreadCount++;
   1256        rec->StoreUsingAnyThread(true);
   1257        SET_GET_TTL(rec, true);
   1258        rec.forget(result);
   1259        return true;
   1260      }
   1261    }
   1262 
   1263    // Determining timeout is racy, so allow one cycle through checking the
   1264    // queues before exiting.
   1265    if (timedOut) {
   1266      break;
   1267    }
   1268 
   1269    // wait for one or more of the following to occur:
   1270    //  (1) the pending queue has a host record to process
   1271    //  (2) the shutdown flag has been set
   1272    //  (3) the thread has been idle for too long
   1273 
   1274    mNumIdleTasks++;
   1275    mIdleTaskCV.Wait(timeout);
   1276    mNumIdleTasks--;
   1277 
   1278    now = TimeStamp::Now();
   1279 
   1280    if (now - epoch >= timeout) {
   1281      timedOut = true;
   1282    } else {
   1283      // It is possible that CondVar::Wait() was interrupted and returned
   1284      // early, in which case we will loop back and re-enter it. In that
   1285      // case we want to do so with the new timeout reduced to reflect
   1286      // time already spent waiting.
   1287      timeout -= now - epoch;
   1288      epoch = now;
   1289    }
   1290  }
   1291 
   1292  // tell thread to exit...
   1293  return false;
   1294 }
   1295 
   1296 void nsHostResolver::PrepareRecordExpirationAddrRecord(
   1297    AddrHostRecord* rec) const {
   1298  // NOTE: rec->addr_info_lock is already held by parent
   1299  MOZ_ASSERT(((bool)rec->addr_info) != rec->negative);
   1300  mLock.AssertCurrentThreadOwns();
   1301  if (!rec->addr_info) {
   1302    rec->SetExpiration(TimeStamp::NowLoRes(), NEGATIVE_RECORD_LIFETIME, 0);
   1303    LOG(("Caching host [%s] negative record for %u seconds.\n", rec->host.get(),
   1304         NEGATIVE_RECORD_LIFETIME));
   1305    return;
   1306  }
   1307 
   1308  unsigned int lifetime = StaticPrefs::network_dnsCacheExpiration();
   1309  unsigned int grace = StaticPrefs::network_dnsCacheExpirationGracePeriod();
   1310 
   1311  if (rec->addr_info && rec->addr_info->TTL() != AddrInfo::NO_TTL_DATA) {
   1312    lifetime = rec->addr_info->TTL();
   1313  }
   1314 
   1315  rec->SetExpiration(TimeStamp::NowLoRes(), lifetime, grace);
   1316  LOG(("Caching host [%s] record for %u seconds (grace %d).", rec->host.get(),
   1317       lifetime, grace));
   1318 }
   1319 
   1320 static bool different_rrset(AddrInfo* rrset1, AddrInfo* rrset2) {
   1321  if (!rrset1 || !rrset2) {
   1322    return true;
   1323  }
   1324 
   1325  LOG(("different_rrset %s\n", rrset1->Hostname().get()));
   1326 
   1327  if (rrset1->ResolverType() != rrset2->ResolverType()) {
   1328    return true;
   1329  }
   1330 
   1331  if (rrset1->TRRType() != rrset2->TRRType()) {
   1332    return true;
   1333  }
   1334 
   1335  if (rrset1->Addresses().Length() != rrset2->Addresses().Length()) {
   1336    LOG(("different_rrset true due to length change\n"));
   1337    return true;
   1338  }
   1339 
   1340  nsTArray<NetAddr> orderedSet1 = rrset1->Addresses().Clone();
   1341  nsTArray<NetAddr> orderedSet2 = rrset2->Addresses().Clone();
   1342  orderedSet1.Sort();
   1343  orderedSet2.Sort();
   1344 
   1345  bool eq = orderedSet1 == orderedSet2;
   1346  if (!eq) {
   1347    LOG(("different_rrset true due to content change\n"));
   1348  } else {
   1349    LOG(("different_rrset false\n"));
   1350  }
   1351  return !eq;
   1352 }
   1353 
   1354 void nsHostResolver::AddToEvictionQ(nsHostRecord* rec,
   1355                                    const MutexAutoLock& aLock) {
   1356  mQueue.AddToEvictionQ(rec, StaticPrefs::network_dnsCacheEntries(), mRecordDB,
   1357                        aLock);
   1358 }
   1359 
   1360 // After a first lookup attempt with TRR in mode 2, we may:
   1361 // - If network.trr.retry_on_recoverable_errors is false, retry with native.
   1362 // - If network.trr.retry_on_recoverable_errors is true:
   1363 //   - Retry with native if the first attempt failed because we got NXDOMAIN, an
   1364 //     unreachable address (TRR_DISABLED_FLAG), or we skipped TRR because
   1365 //     Confirmation failed.
   1366 //   - Trigger a "RetryTRR" Confirmation which will start a fresh
   1367 //     connection for TRR, and then retry the lookup with TRR.
   1368 //   - If the second attempt failed, fallback to native if
   1369 //     network.trr.strict_native_fallback is false.
   1370 // Returns true if we retried with either TRR or Native.
   1371 bool nsHostResolver::MaybeRetryTRRLookup(
   1372    AddrHostRecord* aAddrRec, nsresult aFirstAttemptStatus,
   1373    TRRSkippedReason aFirstAttemptSkipReason, nsresult aChannelStatus,
   1374    const MutexAutoLock& aLock) {
   1375  if (NS_FAILED(aFirstAttemptStatus) &&
   1376      (aChannelStatus == NS_ERROR_PROXY_UNAUTHORIZED ||
   1377       aChannelStatus == NS_ERROR_PROXY_AUTHENTICATION_FAILED) &&
   1378      aAddrRec->mEffectiveTRRMode == nsIRequest::TRR_ONLY_MODE) {
   1379    LOG(("MaybeRetryTRRLookup retry because of proxy connect failed"));
   1380    TRRService::Get()->DontUseTRRThread();
   1381    return DoRetryTRR(aAddrRec, aLock);
   1382  }
   1383 
   1384  if (NS_SUCCEEDED(aFirstAttemptStatus) ||
   1385      aAddrRec->mEffectiveTRRMode != nsIRequest::TRR_FIRST_MODE ||
   1386      aFirstAttemptStatus == NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
   1387    return false;
   1388  }
   1389 
   1390  MOZ_ASSERT(!aAddrRec->mResolving);
   1391  if (!StaticPrefs::network_trr_retry_on_recoverable_errors()) {
   1392    LOG(("nsHostResolver::MaybeRetryTRRLookup retrying with native"));
   1393 
   1394    // Trigger a confirmation retry, in order to cycle connection if needed
   1395    TRRService::Get()->RetryTRRConfirm();
   1396    return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock));
   1397  }
   1398 
   1399  if (IsFailedConfirmationOrNoConnectivity(aFirstAttemptSkipReason) ||
   1400      IsNonRecoverableTRRSkipReason(aFirstAttemptSkipReason) ||
   1401      IsBlockedTRRRequest(aFirstAttemptSkipReason)) {
   1402    LOG(
   1403        ("nsHostResolver::MaybeRetryTRRLookup retrying with native in strict "
   1404         "mode, skip reason was %d",
   1405         static_cast<uint32_t>(aFirstAttemptSkipReason)));
   1406    return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock));
   1407  }
   1408 
   1409  if (aAddrRec->mTrrAttempts > 1) {
   1410    if (!StaticPrefs::network_trr_strict_native_fallback()) {
   1411      LOG(
   1412          ("nsHostResolver::MaybeRetryTRRLookup retry failed. Using "
   1413           "native."));
   1414      return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock));
   1415    }
   1416 
   1417    if (aFirstAttemptSkipReason == TRRSkippedReason::TRR_TIMEOUT &&
   1418        StaticPrefs::network_trr_strict_native_fallback_allow_timeouts()) {
   1419      LOG(
   1420          ("nsHostResolver::MaybeRetryTRRLookup retry timed out. Using "
   1421           "native."));
   1422      return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock));
   1423    }
   1424    LOG(("nsHostResolver::MaybeRetryTRRLookup mTrrAttempts>1, not retrying."));
   1425    return false;
   1426  }
   1427 
   1428  LOG(
   1429      ("nsHostResolver::MaybeRetryTRRLookup triggering Confirmation and "
   1430       "retrying with TRR, skip reason was %d",
   1431       static_cast<uint32_t>(aFirstAttemptSkipReason)));
   1432  TRRService::Get()->RetryTRRConfirm();
   1433 
   1434  return DoRetryTRR(aAddrRec, aLock);
   1435 }
   1436 
   1437 bool nsHostResolver::DoRetryTRR(AddrHostRecord* aAddrRec,
   1438                                const mozilla::MutexAutoLock& aLock) {
   1439  {
   1440    // Clear out the old query
   1441    auto trrQuery = aAddrRec->mTRRQuery.Lock();
   1442    trrQuery.ref() = nullptr;
   1443  }
   1444 
   1445  if (NS_SUCCEEDED(TrrLookup(aAddrRec, aLock, nullptr /* pushedTRR */))) {
   1446    aAddrRec->NotifyRetryingTrr();
   1447    return true;
   1448  }
   1449 
   1450  return false;
   1451 }
   1452 
   1453 //
   1454 // CompleteLookup() checks if the resolving should be redone and if so it
   1455 // returns LOOKUP_RESOLVEAGAIN, but only if 'status' is not NS_ERROR_ABORT.
   1456 nsHostResolver::LookupStatus nsHostResolver::CompleteLookup(
   1457    nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb,
   1458    const nsACString& aOriginsuffix, TRRSkippedReason aReason,
   1459    mozilla::net::TRR* aTRRRequest) {
   1460  MutexAutoLock lock(mLock);
   1461  return CompleteLookupLocked(rec, status, aNewRRSet, pb, aOriginsuffix,
   1462                              aReason, aTRRRequest, lock);
   1463 }
   1464 
   1465 nsHostResolver::LookupStatus nsHostResolver::CompleteLookupLocked(
   1466    nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb,
   1467    const nsACString& aOriginsuffix, TRRSkippedReason aReason,
   1468    mozilla::net::TRR* aTRRRequest, const mozilla::MutexAutoLock& aLock) {
   1469  MOZ_ASSERT(rec);
   1470  MOZ_ASSERT(rec->pb == pb);
   1471  MOZ_ASSERT(rec->IsAddrRecord());
   1472 
   1473  RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
   1474  MOZ_ASSERT(addrRec);
   1475 
   1476  RefPtr<AddrInfo> newRRSet(aNewRRSet);
   1477  MOZ_ASSERT(NS_FAILED(status) || newRRSet->Addresses().Length() > 0);
   1478 
   1479  DNSResolverType type =
   1480      newRRSet ? newRRSet->ResolverType() : DNSResolverType::Native;
   1481 
   1482  if (NS_FAILED(status)) {
   1483    newRRSet = nullptr;
   1484  }
   1485 
   1486  if (addrRec->LoadResolveAgain() && (status != NS_ERROR_ABORT) &&
   1487      type == DNSResolverType::Native) {
   1488    LOG(("nsHostResolver record %p resolve again due to flushcache\n",
   1489         addrRec.get()));
   1490    addrRec->StoreResolveAgain(false);
   1491    return LOOKUP_RESOLVEAGAIN;
   1492  }
   1493 
   1494  MOZ_ASSERT(addrRec->mResolving);
   1495  addrRec->mResolving--;
   1496  LOG((
   1497      "nsHostResolver::CompleteLookup %s %p %X resolver=%d stillResolving=%d\n",
   1498      addrRec->host.get(), aNewRRSet, (unsigned int)status, (int)type,
   1499      int(addrRec->mResolving)));
   1500 
   1501  if (type != DNSResolverType::Native) {
   1502    if (NS_FAILED(status) && status != NS_ERROR_UNKNOWN_HOST &&
   1503        status != NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
   1504      // the errors are not failed resolves, that means
   1505      // something else failed, consider this as *TRR not used*
   1506      // for actually trying to resolve the host
   1507      addrRec->mResolverType = DNSResolverType::Native;
   1508    }
   1509 
   1510    if (NS_FAILED(status)) {
   1511      if (aReason != TRRSkippedReason::TRR_UNSET) {
   1512        addrRec->RecordReason(aReason);
   1513      } else {
   1514        // Unknown failed reason.
   1515        addrRec->RecordReason(TRRSkippedReason::TRR_FAILED);
   1516      }
   1517    } else {
   1518      addrRec->mTRRSuccess = true;
   1519      addrRec->RecordReason(TRRSkippedReason::TRR_OK);
   1520    }
   1521 
   1522    nsresult channelStatus = aTRRRequest->ChannelStatus();
   1523    if (MaybeRetryTRRLookup(addrRec, status, aReason, channelStatus, aLock)) {
   1524      MOZ_ASSERT(addrRec->mResolving);
   1525      return LOOKUP_OK;
   1526    }
   1527 
   1528    if (!addrRec->mTRRSuccess) {
   1529      // no TRR success
   1530      newRRSet = nullptr;
   1531    }
   1532 
   1533    if (NS_FAILED(status)) {
   1534      // This is the error that consumers expect.
   1535      status = NS_ERROR_UNKNOWN_HOST;
   1536    }
   1537  } else {  // native resolve completed
   1538    if (addrRec->LoadUsingAnyThread()) {
   1539      mActiveAnyThreadCount--;
   1540      addrRec->StoreUsingAnyThread(false);
   1541    }
   1542 
   1543    addrRec->mNativeSuccess = static_cast<bool>(newRRSet);
   1544    if (addrRec->mNativeSuccess) {
   1545      addrRec->mNativeDuration = TimeStamp::Now() - addrRec->mNativeStart;
   1546    }
   1547  }
   1548 
   1549  addrRec->OnCompleteLookup();
   1550 
   1551  // update record fields.  We might have a addrRec->addr_info already if a
   1552  // previous lookup result expired and we're reresolving it or we get
   1553  // a late second TRR response.
   1554  if (!mShutdown) {
   1555    MutexAutoLock lock(addrRec->addr_info_lock);
   1556    RefPtr<AddrInfo> old_addr_info;
   1557    bool isDifferentRRSet = different_rrset(addrRec->addr_info, newRRSet);
   1558    if (isDifferentRRSet) {
   1559      LOG(("nsHostResolver record %p new gencnt\n", addrRec.get()));
   1560      old_addr_info = addrRec->addr_info;
   1561      addrRec->addr_info = std::move(newRRSet);
   1562      addrRec->addr_info_gencnt++;
   1563      addrRec->mLastUpdate = TimeStamp::NowLoRes();
   1564    } else {
   1565      if (addrRec->addr_info && newRRSet) {
   1566        auto builder = addrRec->addr_info->Build();
   1567        builder.SetTTL(newRRSet->TTL());
   1568        // Update trr timings
   1569        builder.SetTrrFetchDuration(newRRSet->GetTrrFetchDuration());
   1570        builder.SetTrrFetchDurationNetworkOnly(
   1571            newRRSet->GetTrrFetchDurationNetworkOnly());
   1572 
   1573        addrRec->addr_info = builder.Finish();
   1574      }
   1575      old_addr_info = std::move(newRRSet);
   1576    }
   1577    addrRec->negative = !addrRec->addr_info;
   1578 
   1579    if (addrRec->addr_info && StaticPrefs::network_dns_preferIPv6() &&
   1580        addrRec->addr_info->Addresses().Length() > 1 &&
   1581        addrRec->addr_info->Addresses()[0].IsIPAddrV4()) {
   1582      // Sort IPv6 addresses first.
   1583      auto builder = addrRec->addr_info->Build();
   1584      builder.SortAddresses(NetAddrIPv6FirstComparator());
   1585      addrRec->addr_info = builder.Finish();
   1586    }
   1587 
   1588    PrepareRecordExpirationAddrRecord(addrRec);
   1589  }
   1590 
   1591  if (LOG_ENABLED()) {
   1592    MutexAutoLock lock(addrRec->addr_info_lock);
   1593    if (addrRec->addr_info) {
   1594      for (const auto& elem : addrRec->addr_info->Addresses()) {
   1595        char buf[128];
   1596        elem.ToStringBuffer(buf, sizeof(buf));
   1597        LOG(("CompleteLookup: %s has %s\n", addrRec->host.get(), buf));
   1598      }
   1599    } else {
   1600      LOG(("CompleteLookup: %s has NO address\n", addrRec->host.get()));
   1601    }
   1602  }
   1603 
   1604  // get the list of pending callbacks for this lookup, and notify
   1605  // them that the lookup is complete.
   1606  mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs =
   1607      std::move(rec->mCallbacks);
   1608 
   1609  LOG(("nsHostResolver record %p calling back dns users status:%X\n",
   1610       addrRec.get(), int(status)));
   1611 
   1612  for (nsResolveHostCallback* c = cbs.getFirst(); c;
   1613       c = c->removeAndGetNext()) {
   1614    c->OnResolveHostComplete(this, rec, status);
   1615  }
   1616 
   1617  OnResolveComplete(rec, aLock);
   1618 
   1619 #ifdef DNSQUERY_AVAILABLE
   1620  // Unless the result is from TRR, resolve again to get TTL
   1621  bool hasNativeResult = false;
   1622  {
   1623    MutexAutoLock lock(addrRec->addr_info_lock);
   1624    if (addrRec->addr_info && !addrRec->addr_info->IsTRR()) {
   1625      hasNativeResult = true;
   1626    }
   1627  }
   1628  if (hasNativeResult && !mShutdown && !addrRec->LoadGetTtl() &&
   1629      !rec->mResolving && StaticPrefs::network_dns_get_ttl()) {
   1630    LOG(("Issuing second async lookup for TTL for host [%s].",
   1631         addrRec->host.get()));
   1632    addrRec->flags =
   1633        (addrRec->flags & ~nsIDNSService::RESOLVE_PRIORITY_MEDIUM) |
   1634        nsIDNSService::RESOLVE_PRIORITY_LOW;
   1635    DebugOnly<nsresult> rv = NativeLookup(rec, aLock);
   1636    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1637                         "Could not issue second async lookup for TTL.");
   1638  }
   1639 #endif
   1640  return LOOKUP_OK;
   1641 }
   1642 
   1643 nsHostResolver::LookupStatus nsHostResolver::CompleteLookupByType(
   1644    nsHostRecord* rec, nsresult status,
   1645    mozilla::net::TypeRecordResultType& aResult, TRRSkippedReason aReason,
   1646    uint32_t aTtl, bool pb) {
   1647  MutexAutoLock lock(mLock);
   1648  return CompleteLookupByTypeLocked(rec, status, aResult, aReason, aTtl, pb,
   1649                                    lock);
   1650 }
   1651 
   1652 nsHostResolver::LookupStatus nsHostResolver::CompleteLookupByTypeLocked(
   1653    nsHostRecord* rec, nsresult status,
   1654    mozilla::net::TypeRecordResultType& aResult, TRRSkippedReason aReason,
   1655    uint32_t aTtl, bool pb, const mozilla::MutexAutoLock& aLock) {
   1656  MOZ_ASSERT(rec);
   1657  MOZ_ASSERT(rec->pb == pb);
   1658  MOZ_ASSERT(!rec->IsAddrRecord());
   1659 
   1660  if (rec->LoadNative()) {
   1661    // If this was resolved using the native resolver
   1662    // we also need to update the global count.
   1663    if (rec->LoadUsingAnyThread()) {
   1664      mActiveAnyThreadCount--;
   1665      rec->StoreUsingAnyThread(false);
   1666    }
   1667  }
   1668 
   1669  RefPtr<TypeHostRecord> typeRec = do_QueryObject(rec);
   1670  MOZ_ASSERT(typeRec);
   1671 
   1672  MOZ_ASSERT(typeRec->mResolving);
   1673  typeRec->mResolving--;
   1674 
   1675  if (NS_FAILED(status)) {
   1676    if (status != NS_ERROR_UNKNOWN_HOST &&
   1677        status != NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
   1678      // See nsHostResolver::CompleteLookupLocked. Use the same logic as
   1679      // AddrRecord here.
   1680      typeRec->mResolverType = DNSResolverType::Native;
   1681    }
   1682    LOG(("nsHostResolver::CompleteLookupByType record %p [%s] status %x\n",
   1683         typeRec.get(), typeRec->host.get(), (unsigned int)status));
   1684    typeRec->SetExpiration(
   1685        TimeStamp::NowLoRes(),
   1686        StaticPrefs::network_dns_negative_ttl_for_type_record(), 0);
   1687    MOZ_ASSERT(aResult.is<TypeRecordEmpty>());
   1688    status = NS_ERROR_UNKNOWN_HOST;
   1689    typeRec->negative = true;
   1690    if (aReason != TRRSkippedReason::TRR_UNSET) {
   1691      typeRec->RecordReason(aReason);
   1692    } else {
   1693      // Unknown failed reason.
   1694      typeRec->RecordReason(TRRSkippedReason::TRR_FAILED);
   1695    }
   1696  } else {
   1697    size_t recordCount = 0;
   1698    if (aResult.is<TypeRecordTxt>()) {
   1699      recordCount = aResult.as<TypeRecordTxt>().Length();
   1700    } else if (aResult.is<TypeRecordHTTPSSVC>()) {
   1701      recordCount = aResult.as<TypeRecordHTTPSSVC>().Length();
   1702    }
   1703    LOG(
   1704        ("nsHostResolver::CompleteLookupByType record %p [%s], number of "
   1705         "records %zu\n",
   1706         typeRec.get(), typeRec->host.get(), recordCount));
   1707    MutexAutoLock typeLock(typeRec->mResultsLock);
   1708    typeRec->mResults = aResult;
   1709    typeRec->SetExpiration(
   1710        TimeStamp::NowLoRes(), aTtl,
   1711        StaticPrefs::network_dnsCacheExpirationGracePeriod());
   1712    typeRec->negative = false;
   1713    typeRec->mTRRSuccess = !rec->LoadNative();
   1714    typeRec->mNativeSuccess = rec->LoadNative();
   1715    MOZ_ASSERT(aReason != TRRSkippedReason::TRR_UNSET);
   1716    typeRec->RecordReason(aReason);
   1717  }
   1718 
   1719  mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs =
   1720      std::move(typeRec->mCallbacks);
   1721 
   1722  LOG(
   1723      ("nsHostResolver::CompleteLookupByType record %p calling back dns "
   1724       "users\n",
   1725       typeRec.get()));
   1726 
   1727  for (nsResolveHostCallback* c = cbs.getFirst(); c;
   1728       c = c->removeAndGetNext()) {
   1729    c->OnResolveHostComplete(this, rec, status);
   1730  }
   1731 
   1732  OnResolveComplete(rec, aLock);
   1733 
   1734  return LOOKUP_OK;
   1735 }
   1736 
   1737 void nsHostResolver::OnResolveComplete(nsHostRecord* aRec,
   1738                                       const mozilla::MutexAutoLock& aLock) {
   1739  if (!aRec->mResolving && !mShutdown) {
   1740    {
   1741      auto trrQuery = aRec->mTRRQuery.Lock();
   1742      if (trrQuery.ref()) {
   1743        aRec->mTrrDuration = trrQuery.ref()->Duration();
   1744      }
   1745      trrQuery.ref() = nullptr;
   1746    }
   1747    aRec->ResolveComplete();
   1748 
   1749    AddToEvictionQ(aRec, aLock);
   1750  }
   1751 }
   1752 
   1753 void nsHostResolver::CancelAsyncRequest(
   1754    const nsACString& host, const nsACString& aTrrServer, uint16_t aType,
   1755    const OriginAttributes& aOriginAttributes, nsIDNSService::DNSFlags flags,
   1756    uint16_t af, nsIDNSListener* aListener, nsresult status)
   1757 
   1758 {
   1759  MutexAutoLock lock(mLock);
   1760 
   1761  nsAutoCString originSuffix;
   1762  aOriginAttributes.CreateSuffix(originSuffix);
   1763 
   1764  // Lookup the host record associated with host, flags & address family
   1765 
   1766  nsHostKey key(host, aTrrServer, aType, flags, af,
   1767                (aOriginAttributes.IsPrivateBrowsing()), originSuffix);
   1768  RefPtr<nsHostRecord> rec = mRecordDB.Get(key);
   1769  if (!rec) {
   1770    return;
   1771  }
   1772 
   1773  for (RefPtr<nsResolveHostCallback> c : rec->mCallbacks) {
   1774    if (c->EqualsAsyncListener(aListener)) {
   1775      c->remove();
   1776      c->OnResolveHostComplete(this, rec.get(), status);
   1777      break;
   1778    }
   1779  }
   1780 
   1781  // If there are no more callbacks, remove the hash table entry
   1782  if (rec->mCallbacks.isEmpty()) {
   1783    mRecordDB.Remove(*static_cast<nsHostKey*>(rec.get()));
   1784    // If record is on a Queue, remove it
   1785    mQueue.MaybeRemoveFromQ(rec, lock);
   1786  }
   1787 }
   1788 
   1789 size_t nsHostResolver::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const {
   1790  MutexAutoLock lock(mLock);
   1791 
   1792  size_t n = mallocSizeOf(this);
   1793 
   1794  n += mRecordDB.ShallowSizeOfExcludingThis(mallocSizeOf);
   1795  for (const auto& entry : mRecordDB.Values()) {
   1796    n += entry->SizeOfIncludingThis(mallocSizeOf);
   1797  }
   1798 
   1799  // The following fields aren't measured.
   1800  // - mHighQ, mMediumQ, mLowQ, mEvictionQ, because they just point to
   1801  //   nsHostRecords that also pointed to by entries |mRecordDB|, and
   1802  //   measured when |mRecordDB| is measured.
   1803 
   1804  return n;
   1805 }
   1806 
   1807 void nsHostResolver::ThreadFunc() {
   1808  LOG(("DNS lookup thread - starting execution.\n"));
   1809 
   1810  RefPtr<nsHostRecord> rec;
   1811  RefPtr<AddrInfo> ai;
   1812 
   1813  do {
   1814    if (!rec) {
   1815      RefPtr<nsHostRecord> tmpRec;
   1816      if (!GetHostToLookup(getter_AddRefs(tmpRec))) {
   1817        break;  // thread shutdown signal
   1818      }
   1819      // GetHostToLookup() returns an owning reference
   1820      MOZ_ASSERT(tmpRec);
   1821      rec.swap(tmpRec);
   1822    }
   1823 
   1824    LOG1(("DNS lookup thread - Calling getaddrinfo for host [%s].\n",
   1825          rec->host.get()));
   1826 
   1827    TimeStamp startTime = TimeStamp::Now();
   1828    bool getTtl = rec->LoadGetTtl();
   1829    TimeDuration inQueue = startTime - rec->mNativeStart;
   1830    glean::dns::native_queuing.AccumulateRawDuration(inQueue);
   1831 
   1832    if (!rec->IsAddrRecord()) {
   1833      LOG(("byType on DNS thread"));
   1834      TypeRecordResultType result = AsVariant(mozilla::Nothing());
   1835      uint32_t ttl = UINT32_MAX;
   1836      nsresult status = ResolveHTTPSRecord(rec->host, rec->flags, result, ttl);
   1837      mozilla::glean::networking::dns_native_count
   1838          .EnumGet(rec->pb
   1839                       ? glean::networking::DnsNativeCountLabel::eHttpsPrivate
   1840                       : glean::networking::DnsNativeCountLabel::eHttpsRegular)
   1841          .Add(1);
   1842      CompleteLookupByType(rec, status, result, rec->mTRRSkippedReason, ttl,
   1843                           rec->pb);
   1844      rec = nullptr;
   1845      continue;
   1846    }
   1847 
   1848    nsresult status =
   1849        GetAddrInfo(rec->host, rec->af, rec->flags, getter_AddRefs(ai), getTtl);
   1850 
   1851    mozilla::glean::networking::dns_native_count
   1852        .EnumGet(rec->pb ? glean::networking::DnsNativeCountLabel::ePrivate
   1853                         : glean::networking::DnsNativeCountLabel::eRegular)
   1854        .Add(1);
   1855 
   1856    if (RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec)) {
   1857      // obtain lock to check shutdown and manage inter-module telemetry
   1858      MutexAutoLock lock(mLock);
   1859 
   1860      if (!mShutdown) {
   1861        TimeDuration elapsed = TimeStamp::Now() - startTime;
   1862        if (NS_SUCCEEDED(status)) {
   1863          if (!addrRec->addr_info_gencnt) {
   1864            // Time for initial lookup.
   1865            glean::networking::dns_lookup_time.AccumulateRawDuration(elapsed);
   1866          } else if (!getTtl) {
   1867            // Time for renewal; categorized by expiration strategy.
   1868            glean::networking::dns_renewal_time.AccumulateRawDuration(elapsed);
   1869          } else {
   1870            // Time to get TTL; categorized by expiration strategy.
   1871            glean::networking::dns_renewal_time_for_ttl.AccumulateRawDuration(
   1872                elapsed);
   1873          }
   1874        } else {
   1875          glean::networking::dns_failed_lookup_time.AccumulateRawDuration(
   1876              elapsed);
   1877        }
   1878      }
   1879    }
   1880 
   1881    LOG1(("DNS lookup thread - lookup completed for host [%s]: %s.\n",
   1882          rec->host.get(), ai ? "success" : "failure: unknown host"));
   1883 
   1884    if (LOOKUP_RESOLVEAGAIN ==
   1885        CompleteLookup(rec, status, ai, rec->pb, rec->originSuffix,
   1886                       rec->mTRRSkippedReason, nullptr)) {
   1887      // leave 'rec' assigned and loop to make a renewed host resolve
   1888      LOG(("DNS lookup thread - Re-resolving host [%s].\n", rec->host.get()));
   1889    } else {
   1890      rec = nullptr;
   1891    }
   1892  } while (true);
   1893 
   1894  MutexAutoLock lock(mLock);
   1895  mActiveTaskCount--;
   1896  LOG(("DNS lookup thread - queue empty, task finished.\n"));
   1897 }
   1898 
   1899 nsresult nsHostResolver::Create(nsHostResolver** result) {
   1900  RefPtr<nsHostResolver> res = new nsHostResolver();
   1901 
   1902  nsresult rv = res->Init();
   1903  if (NS_FAILED(rv)) {
   1904    return rv;
   1905  }
   1906 
   1907  res.forget(result);
   1908  return NS_OK;
   1909 }
   1910 
   1911 void nsHostResolver::GetDNSCacheEntries(nsTArray<DNSCacheEntries>* args) {
   1912  MutexAutoLock lock(mLock);
   1913  for (const auto& recordEntry : mRecordDB) {
   1914    // We don't pay attention to address literals, only resolved domains.
   1915    // Also require a host.
   1916    nsHostRecord* rec = recordEntry.GetWeak();
   1917    MOZ_ASSERT(rec, "rec should never be null here!");
   1918 
   1919    if (!rec) {
   1920      continue;
   1921    }
   1922 
   1923    DNSCacheEntries info;
   1924    info.resolveType = rec->type;
   1925    info.hostname = rec->host;
   1926    info.family = rec->af;
   1927    if (rec->mValidEnd.IsNull()) {
   1928      continue;
   1929    }
   1930    info.expiration =
   1931        (int64_t)(rec->mValidEnd - TimeStamp::NowLoRes()).ToSeconds();
   1932    if (info.expiration <= 0) {
   1933      // We only need valid DNS cache entries
   1934      continue;
   1935    }
   1936 
   1937    info.originAttributesSuffix = recordEntry.GetKey().originSuffix;
   1938    info.flags = nsPrintfCString("%u|0x%x|%u|%d|%s", rec->type,
   1939                                 static_cast<uint32_t>(rec->flags), rec->af,
   1940                                 rec->pb, rec->mTrrServer.get());
   1941 
   1942    RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
   1943    if (addrRec && addrRec->addr_info) {
   1944      MutexAutoLock lock(addrRec->addr_info_lock);
   1945      for (const auto& addr : addrRec->addr_info->Addresses()) {
   1946        char buf[kIPv6CStrBufSize];
   1947        if (addr.ToStringBuffer(buf, sizeof(buf))) {
   1948          info.hostaddr.AppendElement(buf);
   1949        }
   1950      }
   1951      info.TRR = addrRec->addr_info->IsTRR();
   1952    }
   1953 
   1954    args->AppendElement(std::move(info));
   1955  }
   1956 }
   1957 
   1958 #undef LOG
   1959 #undef LOG_ENABLED