LCOV - code coverage report
Current view: top level - netwerk/dns - nsHostResolver.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 36 832 4.3 %
Date: 2018-08-07 16:42:27 Functions: 0 0 -
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* vim:set ts=4 sw=4 sts=4 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             : #if defined(HAVE_RES_NINIT)
       7             : #include <sys/types.h>
       8             : #include <netinet/in.h>
       9             : #include <arpa/inet.h>
      10             : #include <arpa/nameser.h>
      11             : #include <resolv.h>
      12             : #define RES_RETRY_ON_FAILURE
      13             : #endif
      14             : 
      15             : #include <stdlib.h>
      16             : #include <ctime>
      17             : #include "nsHostResolver.h"
      18             : #include "nsError.h"
      19             : #include "nsISupportsBase.h"
      20             : #include "nsISupportsUtils.h"
      21             : #include "nsAutoPtr.h"
      22             : #include "nsPrintfCString.h"
      23             : #include "prthread.h"
      24             : #include "prerror.h"
      25             : #include "prtime.h"
      26             : #include "mozilla/Logging.h"
      27             : #include "PLDHashTable.h"
      28             : #include "plstr.h"
      29             : #include "nsURLHelper.h"
      30             : #include "nsThreadUtils.h"
      31             : #include "GetAddrInfo.h"
      32             : #include "GeckoProfiler.h"
      33             : #include "TRR.h"
      34             : #include "TRRService.h"
      35             : 
      36             : #include "mozilla/Atomics.h"
      37             : #include "mozilla/HashFunctions.h"
      38             : #include "mozilla/TimeStamp.h"
      39             : #include "mozilla/Telemetry.h"
      40             : #include "mozilla/DebugOnly.h"
      41             : #include "mozilla/Preferences.h"
      42             : 
      43             : using namespace mozilla;
      44             : using namespace mozilla::net;
      45             : 
      46             : // None of our implementations expose a TTL for negative responses, so we use a
      47             : // constant always.
      48             : static const unsigned int NEGATIVE_RECORD_LIFETIME = 60;
      49             : 
      50             : //----------------------------------------------------------------------------
      51             : 
      52             : // Use a persistent thread pool in order to avoid spinning up new threads all the time.
      53             : // In particular, thread creation results in a res_init() call from libc which is
      54             : // quite expensive.
      55             : //
      56             : // The pool dynamically grows between 0 and MAX_RESOLVER_THREADS in size. New requests
      57             : // go first to an idle thread. If that cannot be found and there are fewer than MAX_RESOLVER_THREADS
      58             : // currently in the pool a new thread is created for high priority requests. If
      59             : // the new request is at a lower priority a new thread will only be created if
      60             : // there are fewer than HighThreadThreshold currently outstanding. If a thread cannot be
      61             : // created or an idle thread located for the request it is queued.
      62             : //
      63             : // When the pool is greater than HighThreadThreshold in size a thread will be destroyed after
      64             : // ShortIdleTimeoutSeconds of idle time. Smaller pools use LongIdleTimeoutSeconds for a
      65             : // timeout period.
      66             : 
      67             : #define HighThreadThreshold     MAX_RESOLVER_THREADS_FOR_ANY_PRIORITY
      68             : #define LongIdleTimeoutSeconds  300           // for threads 1 -> HighThreadThreshold
      69             : #define ShortIdleTimeoutSeconds 60            // for threads HighThreadThreshold+1 -> MAX_RESOLVER_THREADS
      70             : 
      71             : static_assert(HighThreadThreshold <= MAX_RESOLVER_THREADS,
      72             :               "High Thread Threshold should be less equal Maximum allowed thread");
      73             : 
      74             : //----------------------------------------------------------------------------
      75             : 
      76             : namespace mozilla {
      77             : namespace net {
      78             : LazyLogModule gHostResolverLog("nsHostResolver");
      79             : #define LOG(args) MOZ_LOG(mozilla::net::gHostResolverLog, mozilla::LogLevel::Debug, args)
      80             : #define LOG_ENABLED() MOZ_LOG_TEST(mozilla::net::gHostResolverLog, mozilla::LogLevel::Debug)
      81             : }
      82             : }
      83             : 
      84             : //----------------------------------------------------------------------------
      85             : 
      86             : #if defined(RES_RETRY_ON_FAILURE)
      87             : 
      88             : // this class represents the resolver state for a given thread.  if we
      89             : // encounter a lookup failure, then we can invoke the Reset method on an
      90             : // instance of this class to reset the resolver (in case /etc/resolv.conf
      91             : // for example changed).  this is mainly an issue on GNU systems since glibc
      92             : // only reads in /etc/resolv.conf once per thread.  it may be an issue on
      93             : // other systems as well.
      94             : 
      95             : class nsResState
      96             : {
      97             : public:
      98           0 :     nsResState()
      99             :         // initialize mLastReset to the time when this object
     100             :         // is created.  this means that a reset will not occur
     101             :         // if a thread is too young.  the alternative would be
     102             :         // to initialize this to the beginning of time, so that
     103             :         // the first failure would cause a reset, but since the
     104             :         // thread would have just started up, it likely would
     105             :         // already have current /etc/resolv.conf info.
     106           0 :         : mLastReset(PR_IntervalNow())
     107             :     {
     108           0 :     }
     109             : 
     110           0 :     bool Reset()
     111             :     {
     112             :         // reset no more than once per second
     113           0 :         if (PR_IntervalToSeconds(PR_IntervalNow() - mLastReset) < 1)
     114             :             return false;
     115             : 
     116           0 :         LOG(("Calling 'res_ninit'.\n"));
     117             : 
     118           0 :         mLastReset = PR_IntervalNow();
     119           0 :         return (res_ninit(&_res) == 0);
     120             :     }
     121             : 
     122             : private:
     123             :     PRIntervalTime mLastReset;
     124             : };
     125             : 
     126             : #endif // RES_RETRY_ON_FAILURE
     127             : 
     128             : //----------------------------------------------------------------------------
     129             : 
     130             : static inline bool
     131             : IsHighPriority(uint16_t flags)
     132             : {
     133             :     return !(flags & (nsHostResolver::RES_PRIORITY_LOW | nsHostResolver::RES_PRIORITY_MEDIUM));
     134             : }
     135             : 
     136             : static inline bool
     137             : IsMediumPriority(uint16_t flags)
     138             : {
     139             :     return flags & nsHostResolver::RES_PRIORITY_MEDIUM;
     140             : }
     141             : 
     142             : static inline bool
     143             : IsLowPriority(uint16_t flags)
     144             : {
     145             :     return flags & nsHostResolver::RES_PRIORITY_LOW;
     146             : }
     147             : 
     148             : //----------------------------------------------------------------------------
     149             : // this macro filters out any flags that are not used when constructing the
     150             : // host key.  the significant flags are those that would affect the resulting
     151             : // host record (i.e., the flags that are passed down to PR_GetAddrInfoByName).
     152             : #define RES_KEY_FLAGS(_f) ((_f) & nsHostResolver::RES_CANON_NAME)
     153             : 
     154             : bool
     155           0 : nsHostKey::operator==(const nsHostKey& other) const
     156             : {
     157           0 :     return host == other.host &&
     158           0 :         RES_KEY_FLAGS (flags) == RES_KEY_FLAGS(other.flags) &&
     159           0 :         af == other.af &&
     160           0 :         originSuffix == other.originSuffix;
     161             : }
     162             : 
     163             : PLDHashNumber
     164           0 : nsHostKey::Hash() const
     165             : {
     166           0 :     return AddToHash(HashString(host.get()), RES_KEY_FLAGS(flags), af,
     167           0 :                      HashString(originSuffix.get()));
     168             : }
     169             : 
     170             : size_t
     171           0 : nsHostKey::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
     172             : {
     173           0 :     size_t n = 0;
     174           0 :     n += host.SizeOfExcludingThisIfUnshared(mallocSizeOf);
     175           0 :     n += originSuffix.SizeOfExcludingThisIfUnshared(mallocSizeOf);
     176           0 :     return n;
     177             : }
     178             : 
     179           0 : nsHostRecord::nsHostRecord(const nsHostKey& key)
     180             :     : nsHostKey(key)
     181             :     , addr_info_lock("nsHostRecord.addr_info_lock")
     182             :     , addr_info_gencnt(0)
     183             :     , addr_info(nullptr)
     184             :     , addr(nullptr)
     185             :     , negative(false)
     186             :     , mResolverMode(MODE_NATIVEONLY)
     187             :     , mResolving(0)
     188             :     , mTRRSuccess(0)
     189             :     , mNativeSuccess(0)
     190             :     , mNative(false)
     191             :     , mTRRUsed(false)
     192             :     , mNativeUsed(false)
     193             :     , onQueue(false)
     194             :     , usingAnyThread(false)
     195             :     , mDoomed(false)
     196             :     , mDidCallbacks(false)
     197             :     , mGetTtl(false)
     198             :     , mResolveAgain(false)
     199             :     , mTrrAUsed(INIT)
     200             :     , mTrrAAAAUsed(INIT)
     201           0 :     , mTrrLock("nsHostRecord.mTrrLock")
     202             :     , mBlacklistedCount(0)
     203           0 : {
     204             : }
     205             : 
     206           0 : void
     207             : nsHostRecord::Cancel()
     208           0 : {
     209           0 :     MutexAutoLock trrlock(mTrrLock);
     210           0 :     if (mTrrA) {
     211           0 :         mTrrA->Cancel();
     212             :         mTrrA = nullptr;
     213           0 :     }
     214           0 :     if (mTrrAAAA) {
     215           0 :         mTrrAAAA->Cancel();
     216             :         mTrrAAAA = nullptr;
     217           0 :     }
     218             : }
     219             : 
     220           0 : void
     221             : nsHostRecord::Invalidate()
     222           0 : {
     223           0 :     mDoomed = true;
     224             : }
     225             : 
     226           0 : void
     227             : nsHostRecord::SetExpiration(const mozilla::TimeStamp& now, unsigned int valid, unsigned int grace)
     228           0 : {
     229           0 :     mValidStart = now;
     230           0 :     mGraceStart = now + TimeDuration::FromSeconds(valid);
     231           0 :     mValidEnd = now + TimeDuration::FromSeconds(valid + grace);
     232             : }
     233             : 
     234           0 : void
     235             : nsHostRecord::CopyExpirationTimesAndFlagsFrom(const nsHostRecord *aFromHostRecord)
     236             : {
     237             :     // This is used to copy information from a cache entry to a record. All
     238           0 :     // information necessary for HasUsableRecord needs to be copied.
     239           0 :     mValidStart = aFromHostRecord->mValidStart;
     240           0 :     mValidEnd = aFromHostRecord->mValidEnd;
     241           0 :     mGraceStart = aFromHostRecord->mGraceStart;
     242           0 :     mDoomed = aFromHostRecord->mDoomed;
     243             : }
     244             : 
     245           0 : void
     246             : nsHostRecord::ResolveComplete()
     247           0 : {
     248           0 :     if (mNativeUsed) {
     249           0 :         if (mNativeSuccess) {
     250           0 :             uint32_t millis = static_cast<uint32_t>(mNativeDuration.ToMilliseconds());
     251             :             Telemetry::Accumulate(Telemetry::DNS_NATIVE_LOOKUP_TIME, millis);
     252           0 :         }
     253             :         AccumulateCategorical(mNativeSuccess ?
     254           0 :                               Telemetry::LABELS_DNS_LOOKUP_DISPOSITION::osOK :
     255             :                               Telemetry::LABELS_DNS_LOOKUP_DISPOSITION::osFail);
     256             :     }
     257           0 : 
     258           0 :     if (mTRRUsed) {
     259           0 :         if (mTRRSuccess) {
     260           0 :             uint32_t millis = static_cast<uint32_t>(mTrrDuration.ToMilliseconds());
     261             :             Telemetry::Accumulate(Telemetry::DNS_TRR_LOOKUP_TIME, millis);
     262           0 :         }
     263             :         AccumulateCategorical(mTRRSuccess ?
     264           0 :                               Telemetry::LABELS_DNS_LOOKUP_DISPOSITION::trrOK :
     265             :                               Telemetry::LABELS_DNS_LOOKUP_DISPOSITION::trrFail);
     266           0 : 
     267           0 :         if (mTrrAUsed == OK) {
     268           0 :             AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_DISPOSITION::trrAOK);
     269           0 :         } else if (mTrrAUsed == FAILED) {
     270             :             AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_DISPOSITION::trrAFail);
     271             :         }
     272           0 : 
     273           0 :         if (mTrrAAAAUsed == OK) {
     274           0 :             AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_DISPOSITION::trrAAAAOK);
     275           0 :         } else if (mTrrAAAAUsed == FAILED) {
     276             :             AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_DISPOSITION::trrAAAAFail);
     277             :         }
     278             :     }
     279           0 : 
     280           0 :     if (mTRRUsed && mNativeUsed && mNativeSuccess && mTRRSuccess) { // race or shadow!
     281           0 :         static const TimeDuration k50ms = TimeDuration::FromMilliseconds(50);
     282           0 :         if (mTrrDuration <= mNativeDuration) {
     283             :             AccumulateCategorical(((mNativeDuration - mTrrDuration) > k50ms) ?
     284           0 :                                   Telemetry::LABELS_DNS_TRR_RACE::TRRFasterBy50 :
     285           0 :                                   Telemetry::LABELS_DNS_TRR_RACE::TRRFaster);
     286             :             LOG(("nsHostRecord::Complete %s Dns Race: TRR\n", host.get()));
     287           0 :         } else {
     288             :             AccumulateCategorical(((mTrrDuration - mNativeDuration) > k50ms) ?
     289           0 :                                   Telemetry::LABELS_DNS_TRR_RACE::NativeFasterBy50 :
     290           0 :                                   Telemetry::LABELS_DNS_TRR_RACE::NativeFaster);
     291             :             LOG(("nsHostRecord::Complete %s Dns Race: NATIVE\n", host.get()));
     292             :         }
     293             :     }
     294           0 : 
     295             :     if (mTRRUsed && mNativeUsed) {
     296           0 :         // both were used, accumulate comparative success
     297             :         AccumulateCategorical(mNativeSuccess && mTRRSuccess?
     298           0 :                               Telemetry::LABELS_DNS_TRR_COMPARE::BothWorked :
     299           0 :                               ((mNativeSuccess ? Telemetry::LABELS_DNS_TRR_COMPARE::NativeWorked :
     300           0 :                                 (mTRRSuccess ? Telemetry::LABELS_DNS_TRR_COMPARE::TRRWorked:
     301             :                                  Telemetry::LABELS_DNS_TRR_COMPARE::BothFailed))));
     302             :     }
     303           0 : 
     304             :     switch(mResolverMode) {
     305             :     case MODE_NATIVEONLY:
     306           0 :     case MODE_TRROFF:
     307           0 :         AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::nativeOnly);
     308             :         break;
     309           0 :     case MODE_PARALLEL:
     310           0 :         AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::trrRace);
     311             :         break;
     312           0 :     case MODE_TRRFIRST:
     313           0 :         AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::trrFirst);
     314             :         break;
     315           0 :     case MODE_TRRONLY:
     316           0 :         AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::trrOnly);
     317             :         break;
     318           0 :     case MODE_SHADOW:
     319           0 :         AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::trrShadow);
     320             :         break;
     321             :     }
     322           0 : 
     323           0 :     if (mTRRUsed && !mTRRSuccess && mNativeSuccess && gTRRService) {
     324             :         gTRRService->TRRBlacklist(nsCString(host), pb, true);
     325           0 :     }
     326             : }
     327           0 : 
     328             : nsHostRecord::~nsHostRecord()
     329           0 : {
     330             :     mCallbacks.clear();
     331           0 : 
     332           0 :     Telemetry::Accumulate(Telemetry::DNS_BLACKLIST_COUNT, mBlacklistedCount);
     333           0 :     delete addr_info;
     334             : }
     335             : 
     336           0 : bool
     337             : nsHostRecord::Blacklisted(NetAddr *aQuery)
     338             : {
     339           0 :     // must call locked
     340             :     LOG(("Checking blacklist for host [%s], host record [%p].\n",
     341             :          host.get(), this));
     342             : 
     343           0 :     // skip the string conversion for the common case of no blacklist
     344             :     if (!mBlacklistedItems.Length()) {
     345             :         return false;
     346             :     }
     347             : 
     348           0 :     char buf[kIPv6CStrBufSize];
     349             :     if (!NetAddrToString(aQuery, buf, sizeof(buf))) {
     350             :         return false;
     351           0 :     }
     352             :     nsDependentCString strQuery(buf);
     353           0 : 
     354           0 :     for (uint32_t i = 0; i < mBlacklistedItems.Length(); i++) {
     355           0 :         if (mBlacklistedItems.ElementAt(i).Equals(strQuery)) {
     356             :             LOG(("Address [%s] is blacklisted for host [%s].\n", buf, host.get()));
     357             :             return true;
     358             :         }
     359             :     }
     360             : 
     361             :     return false;
     362             : }
     363             : 
     364           0 : void
     365             : nsHostRecord::ReportUnusable(NetAddr *aAddress)
     366             : {
     367           0 :     // must call locked
     368             :     LOG(("Adding address to blacklist for host [%s], host record [%p]."
     369             :          "used trr=%d\n", host.get(), this, mTRRSuccess));
     370           0 : 
     371             :     ++mBlacklistedCount;
     372           0 : 
     373           0 :     if (negative)
     374             :         mDoomed = true;
     375             : 
     376           0 :     char buf[kIPv6CStrBufSize];
     377           0 :     if (NetAddrToString(aAddress, buf, sizeof(buf))) {
     378             :         LOG(("Successfully adding address [%s] to blacklist for host "
     379           0 :              "[%s].\n", buf, host.get()));
     380             :         mBlacklistedItems.AppendElement(nsCString(buf));
     381           0 :     }
     382             : }
     383             : 
     384           0 : void
     385             : nsHostRecord::ResetBlacklist()
     386             : {
     387           0 :     // must call locked
     388             :     LOG(("Resetting blacklist for host [%s], host record [%p].\n",
     389           0 :          host.get(), this));
     390           0 :     mBlacklistedItems.Clear();
     391             : }
     392             : 
     393           0 : nsHostRecord::ExpirationStatus
     394           0 : nsHostRecord::CheckExpiration(const mozilla::TimeStamp& now) const {
     395           0 :     if (!mGraceStart.IsNull() && now >= mGraceStart
     396             :             && !mValidEnd.IsNull() && now < mValidEnd) {
     397             :         return nsHostRecord::EXP_GRACE;
     398           0 :     }
     399             :     if (!mValidEnd.IsNull() && now < mValidEnd) {
     400             :         return nsHostRecord::EXP_VALID;
     401             :     }
     402           0 : 
     403             :     return nsHostRecord::EXP_EXPIRED;
     404             : }
     405             : 
     406             : 
     407           0 : bool
     408             : nsHostRecord::HasUsableResult(const mozilla::TimeStamp& now, uint16_t queryFlags) const
     409           0 : {
     410             :     if (mDoomed) {
     411             :         return false;
     412             :     }
     413             : 
     414           0 :     // don't use cached negative results for high priority queries.
     415             :     if (negative && IsHighPriority(queryFlags)) {
     416             :         return false;
     417             :     }
     418           0 : 
     419             :     if (CheckExpiration(now) == EXP_EXPIRED) {
     420             :         return false;
     421             :     }
     422           0 : 
     423             :     return addr_info || addr || negative;
     424             : }
     425             : 
     426           0 : static size_t
     427             : SizeOfResolveHostCallbackListExcludingHead(const mozilla::LinkedList<RefPtr<nsResolveHostCallback>>& aCallbacks,
     428             :                                            MallocSizeOf mallocSizeOf)
     429           0 : {
     430             :     size_t n = aCallbacks.sizeOfExcludingThis(mallocSizeOf);
     431           0 : 
     432           0 :     for (const nsResolveHostCallback* t = aCallbacks.getFirst(); t; t = t->getNext()) {
     433             :       n += t->SizeOfIncludingThis(mallocSizeOf);
     434             :     }
     435           0 : 
     436             :     return n;
     437             : }
     438             : 
     439           0 : size_t
     440             : nsHostRecord::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
     441           0 : {
     442             :     size_t n = mallocSizeOf(this);
     443           0 : 
     444           0 :     n += nsHostKey::SizeOfExcludingThis(mallocSizeOf);
     445           0 :     n += SizeOfResolveHostCallbackListExcludingHead(mCallbacks, mallocSizeOf);
     446           0 :     n += addr_info ? addr_info->SizeOfIncludingThis(mallocSizeOf) : 0;
     447             :     n += mallocSizeOf(addr.get());
     448           0 : 
     449           0 :     n += mBlacklistedItems.ShallowSizeOfExcludingThis(mallocSizeOf);
     450           0 :     for (size_t i = 0; i < mBlacklistedItems.Length(); i++) {
     451             :         n += mBlacklistedItems[i].SizeOfExcludingThisIfUnshared(mallocSizeOf);
     452           0 :     }
     453             :     return n;
     454             : }
     455             : 
     456           0 : nsHostRecord::DnsPriority
     457             : nsHostRecord::GetPriority(uint16_t aFlags)
     458           0 : {
     459             :     if (IsHighPriority(aFlags)){
     460             :         return nsHostRecord::DNS_PRIORITY_HIGH;
     461           0 :     }
     462             :     if (IsMediumPriority(aFlags)) {
     463             :         return nsHostRecord::DNS_PRIORITY_MEDIUM;
     464             :     }
     465           0 : 
     466             :     return nsHostRecord::DNS_PRIORITY_LOW;
     467             : }
     468             : 
     469             : // Returns true if the entry can be removed, or false if it should be left.
     470             : // Sets mResolveAgain true for entries being resolved right now.
     471           0 : bool
     472             : nsHostRecord::RemoveOrRefresh()
     473             : {
     474           0 :     // no need to flush TRRed names, they're not resolved "locally"
     475           0 :     MutexAutoLock lock(addr_info_lock);
     476             :     if (addr_info && addr_info->IsTRR()) {
     477             :         return false;
     478           0 :     }
     479           0 :     if (mNative) {
     480             :         if (!onQueue) {
     481             :             // The request has been passed to the OS resolver. The resultant DNS
     482             :             // record should be considered stale and not trusted; set a flag to
     483           0 :             // ensure it is called again.
     484             :             mResolveAgain = true;
     485             :         }
     486             :         // if Onqueue is true, the host entry is already added to the cache
     487             :         // but is still pending to get resolved: just leave it in hash.
     488             :         return false;
     489             :     }
     490             :     // Already resolved; not in a pending state; remove from cache
     491             :     return true;
     492             : }
     493             : 
     494             : //----------------------------------------------------------------------------
     495             : 
     496             : static const char kPrefGetTtl[] = "network.dns.get-ttl";
     497             : static const char kPrefNativeIsLocalhost[] = "network.dns.native-is-localhost";
     498             : static bool sGetTtlEnabled = false;
     499             : mozilla::Atomic<bool, mozilla::Relaxed> gNativeIsLocalhost;
     500           0 : 
     501             : static void DnsPrefChanged(const char* aPref, void* aClosure)
     502           0 : {
     503             :     MOZ_ASSERT(NS_IsMainThread(),
     504             :                "Should be getting pref changed notification on main thread!");
     505           0 : 
     506           0 :     DebugOnly<nsHostResolver*> self = static_cast<nsHostResolver*>(aClosure);
     507             :     MOZ_ASSERT(self);
     508           0 : 
     509           0 :     if (!strcmp(aPref, kPrefGetTtl)) {
     510           0 :         sGetTtlEnabled = Preferences::GetBool(kPrefGetTtl);
     511           0 :     } else if (!strcmp(aPref, kPrefNativeIsLocalhost)) {
     512             :         gNativeIsLocalhost = Preferences::GetBool(kPrefNativeIsLocalhost);
     513           0 :     }
     514             : }
     515           0 : 
     516             : NS_IMPL_ISUPPORTS0(nsHostResolver)
     517           0 : 
     518             : nsHostResolver::nsHostResolver(uint32_t maxCacheEntries,
     519           0 :                                uint32_t defaultCacheEntryLifetime,
     520             :                                uint32_t defaultGracePeriod)
     521             :     : mMaxCacheEntries(maxCacheEntries)
     522             :     , mDefaultCacheLifetime(defaultCacheEntryLifetime)
     523             :     , mDefaultGracePeriod(defaultGracePeriod)
     524             :     , mLock("nsHostResolver.mLock")
     525             :     , mIdleThreadCV(mLock, "nsHostResolver.mIdleThreadCV")
     526             :     , mEvictionQSize(0)
     527             :     , mShutdown(true)
     528             :     , mNumIdleThreads(0)
     529             :     , mThreadCount(0)
     530           0 :     , mActiveAnyThreadCount(0)
     531             :     , mPendingCount(0)
     532           0 : {
     533             :     mCreationTime = PR_Now();
     534           0 : 
     535           0 :     mLongIdleTimeout  = TimeDuration::FromSeconds(LongIdleTimeoutSeconds);
     536           0 :     mShortIdleTimeout = TimeDuration::FromSeconds(ShortIdleTimeoutSeconds);
     537             : }
     538             : 
     539             : nsHostResolver::~nsHostResolver() = default;
     540             : 
     541           0 : nsresult
     542             : nsHostResolver::Init()
     543           0 : {
     544           0 :     MOZ_ASSERT(NS_IsMainThread());
     545             :     if (NS_FAILED(GetAddrInfoInit())) {
     546             :         return NS_ERROR_FAILURE;
     547             :     }
     548           0 : 
     549             :     LOG(("nsHostResolver::Init this=%p", this));
     550           0 : 
     551             :     mShutdown = false;
     552             : 
     553             :     // The preferences probably haven't been loaded from the disk yet, so we
     554             :     // need to register a callback that will set up the experiment once they
     555             :     // are. We also need to explicitly set a value for the props otherwise the
     556             :     // callback won't be called.
     557           0 :     {
     558           0 :         DebugOnly<nsresult> rv = Preferences::RegisterCallbackAndCall(
     559           0 :             &DnsPrefChanged, kPrefGetTtl, this);
     560             :         NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
     561           0 :                              "Could not register DNS TTL pref callback.");
     562           0 :         rv = Preferences::RegisterCallbackAndCall(
     563           0 :             &DnsPrefChanged, kPrefNativeIsLocalhost, this);
     564             :         NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
     565             :                              "Could not register DNS pref callback.");
     566             :     }
     567             : 
     568             : #if defined(HAVE_RES_NINIT)
     569             :     // We want to make sure the system is using the correct resolver settings,
     570             :     // so we force it to reload those settings whenever we startup a subsequent
     571             :     // nsHostResolver instance.  We assume that there is no reason to do this
     572             :     // for the first nsHostResolver instance since that is usually created
     573             :     // during application startup.
     574           0 :     static int initCount = 0;
     575           0 :     if (initCount++ > 0) {
     576           0 :         LOG(("Calling 'res_ninit'.\n"));
     577             :         res_ninit(&_res);
     578             :     }
     579             : #endif
     580             : 
     581             :     return NS_OK;
     582             : }
     583             : 
     584           0 : void
     585             : nsHostResolver::ClearPendingQueue(LinkedList<RefPtr<nsHostRecord>>& aPendingQ)
     586             : {
     587           0 :     // loop through pending queue, erroring out pending lookups.
     588           0 :     if (!aPendingQ.isEmpty()) {
     589           0 :         for (RefPtr<nsHostRecord> rec : aPendingQ) {
     590           0 :             rec->Cancel();
     591             :             CompleteLookup(rec, NS_ERROR_ABORT, nullptr, rec->pb);
     592             :         }
     593           0 :     }
     594             : }
     595             : 
     596             : //
     597             : // FlushCache() is what we call when the network has changed. We must not
     598             : // trust names that were resolved before this change. They may resolve
     599             : // differently now.
     600             : //
     601             : // This function removes all existing resolved host entries from the hash.
     602             : // Names that are in the pending queues can be left there. Entries in the
     603             : // cache that have 'Resolve' set true but not 'onQueue' are being resolved
     604             : // right now, so we need to mark them to get re-resolved on completion!
     605             : 
     606           0 : void
     607             : nsHostResolver::FlushCache()
     608           0 : {
     609           0 :     MutexAutoLock lock(mLock);
     610             :     mEvictionQSize = 0;
     611             : 
     612             :     // Clear the evictionQ and remove all its corresponding entries from
     613           0 :     // the cache first
     614           0 :     if (!mEvictionQ.isEmpty()) {
     615           0 :         for (RefPtr<nsHostRecord> rec : mEvictionQ) {
     616           0 :             rec->Cancel();
     617             :             mRecordDB.Remove(*static_cast<nsHostKey *>(rec));
     618           0 :         }
     619             :         mEvictionQ.clear();
     620             :     }
     621             : 
     622           0 :     // Refresh the cache entries that are resolving RIGHT now, remove the rest.
     623           0 :     for (auto iter = mRecordDB.Iter(); !iter.Done(); iter.Next()) {
     624             :         nsHostRecord* record = iter.UserData();
     625           0 :         // Try to remove the record, or mark it for refresh.
     626           0 :         if (record->RemoveOrRefresh()) {
     627           0 :             if (record->isInList()) {
     628             :                 record->remove();
     629           0 :             }
     630             :             iter.Remove();
     631             :         }
     632           0 :     }
     633             : }
     634             : 
     635           0 : void
     636             : nsHostResolver::Shutdown()
     637           0 : {
     638             :     LOG(("Shutting down host resolver.\n"));
     639             : 
     640           0 :     {
     641           0 :         DebugOnly<nsresult> rv = Preferences::UnregisterCallback(
     642           0 :             &DnsPrefChanged, kPrefGetTtl, this);
     643             :         NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
     644             :                              "Could not unregister DNS TTL pref callback.");
     645             :     }
     646           0 : 
     647             :     LinkedList<RefPtr<nsHostRecord>> pendingQHigh, pendingQMed, pendingQLow, evictionQ;
     648             : 
     649           0 :     {
     650             :         MutexAutoLock lock(mLock);
     651           0 : 
     652             :         mShutdown = true;
     653             : 
     654           0 :         // Move queues to temporary lists.
     655           0 :         pendingQHigh = std::move(mHighQ);
     656           0 :         pendingQMed = std::move(mMediumQ);
     657           0 :         pendingQLow = std::move(mLowQ);
     658             :         evictionQ = std::move(mEvictionQ);
     659           0 : 
     660           0 :         mEvictionQSize = 0;
     661             :         mPendingCount = 0;
     662           0 : 
     663           0 :         if (mNumIdleThreads)
     664             :             mIdleThreadCV.NotifyAll();
     665             : 
     666           0 :         // empty host database
     667             :         mRecordDB.Clear();
     668             :     }
     669           0 : 
     670           0 :     ClearPendingQueue(pendingQHigh);
     671           0 :     ClearPendingQueue(pendingQMed);
     672             :     ClearPendingQueue(pendingQLow);
     673           0 : 
     674           0 :     if (!evictionQ.isEmpty()) {
     675           0 :         for (RefPtr<nsHostRecord> rec : evictionQ) {
     676             :             rec->Cancel();
     677             :         }
     678             :     }
     679           0 : 
     680           0 :     pendingQHigh.clear();
     681           0 :     pendingQMed.clear();
     682           0 :     pendingQLow.clear();
     683             :     evictionQ.clear();
     684           0 : 
     685           0 :     for (auto iter = mRecordDB.Iter(); !iter.Done(); iter.Next()) {
     686             :         iter.UserData()->Cancel();
     687             :     }
     688             : #ifdef NS_BUILD_REFCNT_LOGGING
     689             : 
     690             :     // Logically join the outstanding worker threads with a timeout.
     691             :     // Use this approach instead of PR_JoinThread() because that does
     692             :     // not allow a timeout which may be necessary for a semi-responsive
     693             :     // shutdown if the thread is blocked on a very slow DNS resolution.
     694             :     // mThreadCount is read outside of mLock, but the worst case
     695             :     // scenario for that race is one extra 25ms sleep.
     696           0 : 
     697           0 :     PRIntervalTime delay = PR_MillisecondsToInterval(25);
     698           0 :     PRIntervalTime stopTime = PR_IntervalNow() + PR_SecondsToInterval(20);
     699           0 :     while (mThreadCount && PR_IntervalNow() < stopTime)
     700             :         PR_Sleep(delay);
     701             : #endif
     702             : 
     703           0 :     {
     704           0 :         mozilla::DebugOnly<nsresult> rv = GetAddrInfoShutdown();
     705             :         NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
     706             :                              "Failed to shutdown GetAddrInfo");
     707           0 :     }
     708             : }
     709             : 
     710           0 : nsresult
     711             : nsHostResolver::GetHostRecord(const nsACString &host,
     712             :                               uint16_t flags, uint16_t af, bool pb,
     713             :                               const nsCString &originSuffix,
     714             :                               nsHostRecord **result)
     715           0 : {
     716           0 :     MutexAutoLock lock(mLock);
     717             :     nsHostKey key(host, flags, af, pb, originSuffix);
     718           0 : 
     719           0 :     RefPtr<nsHostRecord>& entry = mRecordDB.GetOrInsert(key);
     720           0 :     if (!entry) {
     721             :         entry = new nsHostRecord(key);
     722             :     }
     723           0 : 
     724           0 :     RefPtr<nsHostRecord> rec = entry;
     725             :     if (rec->addr) {
     726             :         return NS_ERROR_FAILURE;
     727           0 :     }
     728             :     if (rec->mResolving) {
     729             :         return NS_ERROR_FAILURE;
     730           0 :     }
     731           0 :     *result = rec.forget().take();
     732             :     return NS_OK;
     733             : }
     734             : 
     735           0 : nsresult
     736             : nsHostResolver::ResolveHost(const nsACString &aHost,
     737             :                             const OriginAttributes &aOriginAttributes,
     738             :                             uint16_t                flags,
     739             :                             uint16_t                af,
     740             :                             nsResolveHostCallback  *aCallback)
     741           0 : {
     742             :     nsAutoCString host(aHost);
     743           0 :     NS_ENSURE_TRUE(!host.IsEmpty(), NS_ERROR_UNEXPECTED);
     744             : 
     745             :     LOG(("Resolving host [%s]%s%s.\n", host.get(),
     746             :          flags & RES_BYPASS_CACHE ? " - bypassing cache" : "",
     747             :          flags & RES_REFRESH_CACHE ? " - refresh cache" : ""));
     748             : 
     749           0 :     // ensure that we are working with a valid hostname before proceeding.  see
     750             :     // bug 304904 for details.
     751             :     if (!net_IsValidHostName(host))
     752           0 :         return NS_ERROR_UNKNOWN_HOST;
     753             : 
     754             :     RefPtr<nsResolveHostCallback> callback(aCallback);
     755           0 :     // if result is set inside the lock, then we need to issue the
     756           0 :     // callback before returning.
     757             :     RefPtr<nsHostRecord> result;
     758           0 :     nsresult status = NS_OK, rv = NS_OK;
     759             :     {
     760           0 :         MutexAutoLock lock(mLock);
     761             : 
     762             :         if (mShutdown) {
     763             :             rv = NS_ERROR_NOT_INITIALIZED;
     764             :         } else {
     765             :             // Used to try to parse to an IP address literal.
     766             :             PRNetAddr tempAddr;
     767           0 :             // Unfortunately, PR_StringToNetAddr does not properly initialize
     768             :             // the output buffer in the case of IPv6 input. See bug 223145.
     769             :             memset(&tempAddr, 0, sizeof(PRNetAddr));
     770             : 
     771             :             // check to see if there is already an entry for this |host|
     772             :             // in the hash table.  if so, then check to see if we can't
     773             :             // just reuse the lookup result.  otherwise, if there are
     774             :             // any pending callbacks, then add to pending callbacks queue,
     775           0 :             // and return.  otherwise, add ourselves as first pending
     776           0 :             // callback, and proceed to do the lookup.
     777             :             nsAutoCString originSuffix;
     778           0 :             aOriginAttributes.CreateSuffix(originSuffix);
     779           0 : 
     780           0 :             nsHostKey key(host, flags, af,
     781           0 :                           (aOriginAttributes.mPrivateBrowsingId > 0),
     782           0 :                           originSuffix);
     783           0 :             RefPtr<nsHostRecord>& entry = mRecordDB.GetOrInsert(key);
     784             :             if (!entry) {
     785             :                 entry = new nsHostRecord(key);
     786           0 :             }
     787           0 : 
     788           0 :             RefPtr<nsHostRecord> rec = entry;
     789           0 :             MOZ_ASSERT(rec, "Record should not be null");
     790           0 :             if (!(flags & RES_BYPASS_CACHE) &&
     791             :                      rec->HasUsableResult(TimeStamp::NowLoRes(), flags)) {
     792           0 :                 LOG(("  Using cached record for host [%s].\n", host.get()));
     793           0 :                 // put reference to host record on stack...
     794             :                 result = rec;
     795             :                 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
     796             : 
     797             :                 // For entries that are in the grace period
     798           0 :                 // or all cached negative entries, use the cache but start a new
     799             :                 // lookup in the background
     800           0 :                 ConditionallyRefreshRecord(rec, host);
     801           0 : 
     802             :                 if (rec->negative) {
     803           0 :                     LOG(("  Negative cache entry for host [%s].\n", host.get()));
     804           0 :                     Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
     805             :                                           METHOD_NEGATIVE_HIT);
     806           0 :                     status = NS_ERROR_UNKNOWN_HOST;
     807             :                 }
     808             :             } else if (rec->addr) {
     809           0 :                 // if the host name is an IP address literal and has been parsed,
     810             :                 // go ahead and use it.
     811           0 :                 LOG(("  Using cached address for IP Literal [%s].\n", host.get()));
     812           0 :                 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
     813           0 :                                       METHOD_LITERAL);
     814             :                 result = rec;
     815             :             } else if (PR_StringToNetAddr(host.get(), &tempAddr) == PR_SUCCESS) {
     816             :                 // try parsing the host name as an IP address literal to short
     817           0 :                 // circuit full host resolution.  (this is necessary on some
     818             :                 // platforms like Win9x.  see bug 219376 for more details.)
     819             :                 LOG(("  Host is IP Literal [%s].\n", host.get()));
     820           0 :                 // ok, just copy the result into the host record, and be done
     821           0 :                 // with it! ;-)
     822             :                 rec->addr = MakeUnique<NetAddr>();
     823             :                 PRNetAddrToNetAddr(&tempAddr, rec->addr.get());
     824           0 :                 // put reference to host record on stack...
     825           0 :                 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
     826           0 :                                       METHOD_LITERAL);
     827           0 :                 result = rec;
     828           0 :             } else if (mPendingCount >= MAX_NON_PRIORITY_REQUESTS &&
     829           0 :                        !IsHighPriority(flags) &&
     830             :                        !rec->mResolving) {
     831             :                 LOG(("  Lookup queue full: dropping %s priority request for "
     832             :                      "host [%s].\n",
     833           0 :                      IsMediumPriority(flags) ? "medium" : "low", host.get()));
     834             :                 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
     835           0 :                                       METHOD_OVERFLOW);
     836           0 :                 // This is a lower priority request and we are swamped, so refuse it.
     837           0 :                 rv = NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
     838             :             } else if (flags & RES_OFFLINE) {
     839           0 :                 LOG(("  Offline request for host [%s]; ignoring.\n", host.get()));
     840             :                 rv = NS_ERROR_OFFLINE;
     841             :             } else if (!rec->mResolving) {
     842             :                 // If this is an IPV4 or IPV6 specific request, check if there is
     843           0 :                 // an AF_UNSPEC entry we can use. Otherwise, hit the resolver...
     844           0 : 
     845             :                 if (!(flags & RES_BYPASS_CACHE) &&
     846           0 :                     ((af == PR_AF_INET) || (af == PR_AF_INET6))) {
     847           0 :                     // First, search for an entry with AF_UNSPEC
     848           0 :                     const nsHostKey unspecKey(host, flags, PR_AF_UNSPEC,
     849           0 :                                               (aOriginAttributes.mPrivateBrowsingId > 0),
     850             :                                               originSuffix);
     851           0 :                     RefPtr<nsHostRecord> unspecRec = mRecordDB.Get(unspecKey);
     852           0 : 
     853             :                     TimeStamp now = TimeStamp::NowLoRes();
     854           0 :                     if (unspecRec && unspecRec->HasUsableResult(now, flags)) {
     855             : 
     856             :                         MOZ_ASSERT(unspecRec->addr_info || unspecRec->negative,
     857           0 :                                    "Entry should be resolved or negative.");
     858             : 
     859             :                         LOG(("  Trying AF_UNSPEC entry for host [%s] af: %s.\n", host.get(),
     860             :                              (af == PR_AF_INET) ? "AF_INET" : "AF_INET6"));
     861             : 
     862           0 :                         // We need to lock in case any other thread is reading
     863             :                         // addr_info.
     864             :                         MutexAutoLock lock(rec->addr_info_lock);
     865             : 
     866             :                         // XXX: note that this actually leaks addr_info.
     867           0 :                         // For some reason, freeing the memory causes a crash in
     868           0 :                         // nsDNSRecord::GetNextAddr - see bug 1422173
     869           0 :                         rec->addr_info = nullptr;
     870           0 :                         if (unspecRec->negative) {
     871           0 :                             rec->negative = unspecRec->negative;
     872             :                             rec->CopyExpirationTimesAndFlagsFrom(unspecRec);
     873             :                         } else if (unspecRec->addr_info) {
     874             :                             // Search for any valid address in the AF_UNSPEC entry
     875             :                             // in the cache (not blacklisted and from the right
     876           0 :                             // family).
     877           0 :                             NetAddrElement *addrIter =
     878           0 :                                 unspecRec->addr_info->mAddresses.getFirst();
     879           0 :                             while (addrIter) {
     880           0 :                                 if ((af == addrIter->mAddress.inet.family) &&
     881           0 :                                      !unspecRec->Blacklisted(&addrIter->mAddress)) {
     882           0 :                                     if (!rec->addr_info) {
     883           0 :                                         rec->addr_info = new AddrInfo(
     884           0 :                                             unspecRec->addr_info->mHostName,
     885           0 :                                             unspecRec->addr_info->mCanonicalName,
     886           0 :                                             unspecRec->addr_info->IsTRR()
     887             :                                           );
     888           0 :                                         rec->CopyExpirationTimesAndFlagsFrom(unspecRec);
     889           0 :                                     }
     890             :                                     rec->addr_info->AddAddress(
     891           0 :                                         new NetAddrElement(*addrIter));
     892             :                                 }
     893             :                                 addrIter = addrIter->getNext();
     894             :                             }
     895           0 :                         }
     896           0 :                         // Now check if we have a new record.
     897           0 :                         if (rec->HasUsableResult(now, flags)) {
     898           0 :                             result = rec;
     899             :                             if (rec->negative) {
     900             :                                 status = NS_ERROR_UNKNOWN_HOST;
     901           0 :                             }
     902           0 :                             Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
     903           0 :                                                   METHOD_HIT);
     904             :                             ConditionallyRefreshRecord(rec, host);
     905             :                         } else if (af == PR_AF_INET6) {
     906             :                             // For AF_INET6, a new lookup means another AF_UNSPEC
     907             :                             // lookup. We have already iterated through the
     908           0 :                             // AF_UNSPEC addresses, so we mark this record as
     909             :                             // negative.
     910           0 :                             LOG(("  No AF_INET6 in AF_UNSPEC entry: "
     911           0 :                                  "host [%s] unknown host.", host.get()));
     912           0 :                             result = rec;
     913             :                             rec->negative = true;
     914           0 :                             status = NS_ERROR_UNKNOWN_HOST;
     915             :                             Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
     916             :                                                   METHOD_NEGATIVE_HIT);
     917             :                         }
     918             :                     }
     919             :                 }
     920           0 :                 // If no valid address was found in the cache or this is an
     921           0 :                 // AF_UNSPEC request, then start a new lookup.
     922             :                 if (!result) {
     923           0 :                     LOG(("  No usable address in cache for host [%s].", host.get()));
     924           0 : 
     925             :                     if (flags & RES_REFRESH_CACHE) {
     926             :                         rec->Invalidate();
     927             :                     }
     928           0 : 
     929           0 :                     // Add callback to the list of pending callbacks.
     930           0 :                     rec->mCallbacks.insertBack(callback);
     931             :                     rec->flags = flags;
     932           0 :                     rv = NameLookup(rec);
     933           0 :                     Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
     934           0 :                                           METHOD_NETWORK_FIRST);
     935             :                     if (NS_FAILED(rv) && callback->isInList()) {
     936           0 :                         callback->remove();
     937             :                     } else {
     938             :                         LOG(("  DNS lookup for host [%s] blocking "
     939             :                              "pending 'getaddrinfo' query: callback [%p]",
     940             :                              host.get(), callback.get()));
     941           0 :                     }
     942             :                 }
     943             :             } else if (rec->mDidCallbacks) {
     944           0 :                 // record is still pending more (TRR) data; make the callback
     945             :                 // at once
     946           0 :                 result = rec;
     947           0 :                 // make it count as a hit
     948             :                 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
     949           0 :                 LOG(("  Host [%s] re-using early TRR resolve data\n", host.get()));
     950             :             } else {
     951             :                 LOG(("  Host [%s] is being resolved. Appending callback "
     952           0 :                      "[%p].", host.get(), callback.get()));
     953           0 : 
     954             :                 rec->mCallbacks.insertBack(callback);
     955           0 :                 if (rec->onQueue) {
     956             :                     Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
     957             :                                           METHOD_NETWORK_SHARED);
     958             : 
     959             :                     // Consider the case where we are on a pending queue of
     960             :                     // lower priority than the request is being made at.
     961           0 :                     // In that case we should upgrade to the higher queue.
     962           0 : 
     963             :                     if (IsHighPriority(flags) &&
     964           0 :                         !IsHighPriority(rec->flags)) {
     965           0 :                         // Move from (low|med) to high.
     966           0 :                         NS_ASSERTION(rec->onQueue, "Moving Host Record Not Currently Queued");
     967           0 :                         rec->remove();
     968           0 :                         mHighQ.insertBack(rec);
     969           0 :                         rec->flags = flags;
     970           0 :                         ConditionallyCreateThread(rec);
     971             :                     } else if (IsMediumPriority(flags) &&
     972           0 :                                IsLowPriority(rec->flags)) {
     973           0 :                         // Move from low to med.
     974           0 :                         NS_ASSERTION(rec->onQueue, "Moving Host Record Not Currently Queued");
     975           0 :                         rec->remove();
     976           0 :                         mMediumQ.insertBack(rec);
     977             :                         rec->flags = flags;
     978             :                         mIdleThreadCV.Notify();
     979             :                     }
     980             :                 }
     981             :             }
     982             :         }
     983           0 :     }
     984           0 : 
     985           0 :     if (result) {
     986             :         if (callback->isInList()) {
     987           0 :             callback->remove();
     988             :         }
     989             :         callback->OnResolveHostComplete(this, result, status);
     990             :     }
     991             : 
     992             :     return rv;
     993             : }
     994           0 : 
     995             : void
     996             : nsHostResolver::DetachCallback(const nsACString &host,
     997             :                                const OriginAttributes &aOriginAttributes,
     998             :                                uint16_t                flags,
     999             :                                uint16_t                af,
    1000             :                                nsResolveHostCallback  *aCallback,
    1001           0 :                                nsresult                status)
    1002           0 : {
    1003             :     RefPtr<nsHostRecord> rec;
    1004             :     RefPtr<nsResolveHostCallback> callback(aCallback);
    1005           0 : 
    1006             :     {
    1007           0 :         MutexAutoLock lock(mLock);
    1008           0 : 
    1009             :         nsAutoCString originSuffix;
    1010           0 :         aOriginAttributes.CreateSuffix(originSuffix);
    1011           0 : 
    1012           0 :         nsHostKey key(host, flags, af,
    1013           0 :                       (aOriginAttributes.mPrivateBrowsingId > 0),
    1014           0 :                       originSuffix);
    1015             :         RefPtr<nsHostRecord> entry = mRecordDB.Get(key);
    1016             :         if (entry) {
    1017             :             // walk list looking for |callback|... we cannot assume
    1018           0 :             // that it will be there!
    1019           0 : 
    1020           0 :             for (nsResolveHostCallback* c: entry->mCallbacks) {
    1021           0 :                 if (c == callback) {
    1022           0 :                     rec = entry;
    1023             :                     c->remove();
    1024             :                     break;
    1025             :                 }
    1026             :             }
    1027             :         }
    1028             :     }
    1029             : 
    1030           0 :     // complete callback with the given status code; this would only be done if
    1031           0 :     // the record was in the process of being resolved.
    1032             :     if (rec) {
    1033           0 :         callback->OnResolveHostComplete(this, rec, status);
    1034             :     }
    1035             : }
    1036           0 : 
    1037             : nsresult
    1038           0 : nsHostResolver::ConditionallyCreateThread(nsHostRecord *rec)
    1039             : {
    1040           0 :     if (mNumIdleThreads) {
    1041             :         // wake up idle thread to process this lookup
    1042           0 :         mIdleThreadCV.Notify();
    1043           0 :     }
    1044             :     else if ((mThreadCount < HighThreadThreshold) ||
    1045           0 :              (IsHighPriority(rec->flags) && mThreadCount < MAX_RESOLVER_THREADS)) {
    1046             :         // dispatch new worker thread
    1047           0 :         NS_ADDREF_THIS(); // owning reference passed to thread
    1048             : 
    1049             :         mThreadCount++;
    1050             :         PRThread *thr = PR_CreateThread(PR_SYSTEM_THREAD,
    1051             :                                         ThreadFunc,
    1052             :                                         this,
    1053             :                                         PR_PRIORITY_NORMAL,
    1054           0 :                                         PR_GLOBAL_THREAD,
    1055           0 :                                         PR_UNJOINABLE_THREAD,
    1056           0 :                                         0);
    1057           0 :         if (!thr) {
    1058           0 :             mThreadCount--;
    1059             :             NS_RELEASE_THIS();
    1060             :             return NS_ERROR_OUT_OF_MEMORY;
    1061             :         }
    1062           0 :     }
    1063             :     else {
    1064             :         LOG(("  Unable to find a thread for looking up host [%s].\n", rec->host.get()));
    1065             :     }
    1066             :     return NS_OK;
    1067             : }
    1068             : 
    1069             : // make sure the mTrrLock is held when this is used!
    1070             : #define TRROutstanding() ((rec->mTrrA || rec->mTrrAAAA))
    1071           0 : 
    1072             : nsresult
    1073           0 : nsHostResolver::TrrLookup_unlocked(nsHostRecord *rec, TRR *pushedTRR)
    1074           0 : {
    1075             :     MutexAutoLock lock(mLock);
    1076             :     return TrrLookup(rec, pushedTRR);
    1077             : }
    1078             : 
    1079             : // returns error if no TRR resolve is issued
    1080           0 : // it is impt this is not called while a native lookup is going on
    1081             : nsresult
    1082           0 : nsHostResolver::TrrLookup(nsHostRecord *aRec, TRR *pushedTRR)
    1083           0 : {
    1084             :     RefPtr<nsHostRecord> rec(aRec);
    1085             :     mLock.AssertCurrentThreadOwns();
    1086           0 : #ifdef DEBUG
    1087           0 :     {
    1088             :         MutexAutoLock trrlock(rec->mTrrLock);
    1089             :         MOZ_ASSERT(!TRROutstanding());
    1090           0 :     }
    1091             : #endif
    1092           0 :     MOZ_ASSERT(!rec->mResolving);
    1093           0 : 
    1094             :     if (!gTRRService || !gTRRService->Enabled()) {
    1095             :         LOG(("TrrLookup:: %s service not enabled\n", rec->host.get()));
    1096             :         return NS_ERROR_UNKNOWN_HOST;
    1097           0 :     }
    1098             : 
    1099           0 :     if (rec->isInList()) {
    1100           0 :         // we're already on the eviction queue. This is a renewal
    1101             :         MOZ_ASSERT(mEvictionQSize);
    1102           0 :         AssertOnQ(rec, mEvictionQ);
    1103           0 : 
    1104             :         rec->remove();
    1105             :         mEvictionQSize--;
    1106           0 :     }
    1107           0 : 
    1108           0 :     rec->mTRRSuccess = 0; // bump for each successful TRR response
    1109           0 :     rec->mTrrAUsed = nsHostRecord::INIT;
    1110           0 :     rec->mTrrAAAAUsed = nsHostRecord::INIT;
    1111             :     rec->mTrrStart = TimeStamp::Now();
    1112             :     rec->mTRRUsed = true; // this record gets TRR treatment
    1113             : 
    1114           0 :     // If asking for AF_UNSPEC, issue both A and AAAA.
    1115           0 :     // If asking for AF_INET6 or AF_INET, do only that single type
    1116           0 :     enum TrrType rectype = (rec->af == AF_INET6)? TRRTYPE_AAAA : TRRTYPE_A;
    1117             :     if (pushedTRR) {
    1118             :         rectype = pushedTRR->Type();
    1119             :     }
    1120             :     bool sendAgain;
    1121           0 : 
    1122           0 :     bool madeQuery = false;
    1123           0 :     do {
    1124             :         sendAgain = false;
    1125             :         if ((TRRTYPE_AAAA == rectype) && gTRRService && gTRRService->DisableIPv6()) {
    1126           0 :             break;
    1127           0 :         }
    1128           0 :         LOG(("TRR Resolve %s type %d\n", rec->host.get(), (int)rectype));
    1129           0 :         RefPtr<TRR> trr;
    1130           0 :         MutexAutoLock trrlock(rec->mTrrLock);
    1131           0 :         trr = pushedTRR ? pushedTRR : new TRR(this, rec, rectype);
    1132           0 :         if (pushedTRR || NS_SUCCEEDED(NS_DispatchToMainThread(trr))) {
    1133           0 :             rec->mResolving++;
    1134           0 :             if (rectype == TRRTYPE_A) {
    1135           0 :                 MOZ_ASSERT(!rec->mTrrA);
    1136           0 :                 rec->mTrrA = trr;
    1137           0 :                 rec->mTrrAUsed = nsHostRecord::STARTED;
    1138           0 :             } else if (rectype == TRRTYPE_AAAA) {
    1139           0 :                 MOZ_ASSERT(!rec->mTrrAAAA);
    1140             :                 rec->mTrrAAAA = trr;
    1141           0 :                 rec->mTrrAAAAUsed = nsHostRecord::STARTED;
    1142           0 :             } else {
    1143             :                 LOG(("TrrLookup called with bad type set: %d\n", rectype));
    1144           0 :                 MOZ_ASSERT(0);
    1145           0 :             }
    1146           0 :             madeQuery = true;
    1147           0 :             if (!pushedTRR && (rec->af == AF_UNSPEC) && (rectype == TRRTYPE_A)) {
    1148             :                 rectype = TRRTYPE_AAAA;
    1149             :                 sendAgain = true;
    1150             :             }
    1151             :         }
    1152           0 :     } while (sendAgain);
    1153             : 
    1154             :     return madeQuery ? NS_OK : NS_ERROR_UNKNOWN_HOST;
    1155             : }
    1156           0 : 
    1157             : void
    1158             : nsHostResolver::AssertOnQ(nsHostRecord *rec, LinkedList<RefPtr<nsHostRecord>>& q)
    1159           0 : {
    1160           0 : #ifdef DEBUG
    1161           0 :     MOZ_ASSERT(!q.isEmpty());
    1162           0 :     MOZ_ASSERT(rec->isInList());
    1163           0 :     for (RefPtr<nsHostRecord> r: q) {
    1164             :         if (rec == r) {
    1165             :             return;
    1166           0 :         }
    1167             :     }
    1168             :     MOZ_ASSERT(false, "Did not find element");
    1169             : #endif
    1170             : }
    1171           0 : 
    1172             : nsresult
    1173           0 : nsHostResolver::NativeLookup(nsHostRecord *aRec)
    1174           0 : {
    1175             :     mLock.AssertCurrentThreadOwns();
    1176           0 :     RefPtr<nsHostRecord> rec(aRec);
    1177             : 
    1178             :     rec->mNativeStart = TimeStamp::Now();
    1179           0 : 
    1180           0 :     // Add rec to one of the pending queues, possibly removing it from mEvictionQ.
    1181           0 :     if (rec->isInList()) {
    1182           0 :         MOZ_ASSERT(mEvictionQSize);
    1183           0 :         AssertOnQ(rec, mEvictionQ);
    1184             :         rec->remove(); // was on the eviction queue
    1185             :         mEvictionQSize--;
    1186           0 :     }
    1187             : 
    1188           0 :     switch (nsHostRecord::GetPriority(rec->flags)) {
    1189             :         case nsHostRecord::DNS_PRIORITY_HIGH:
    1190             :             mHighQ.insertBack(rec);
    1191             :             break;
    1192           0 : 
    1193             :         case nsHostRecord::DNS_PRIORITY_MEDIUM:
    1194             :             mMediumQ.insertBack(rec);
    1195             :             break;
    1196           0 : 
    1197             :         case nsHostRecord::DNS_PRIORITY_LOW:
    1198             :             mLowQ.insertBack(rec);
    1199           0 :             break;
    1200             :     }
    1201           0 :     mPendingCount++;
    1202           0 : 
    1203           0 :     rec->mNative = true;
    1204           0 :     rec->mNativeUsed = true;
    1205             :     rec->onQueue = true;
    1206           0 :     rec->mResolving++;
    1207             : 
    1208           0 :     nsresult rv = ConditionallyCreateThread(rec);
    1209             : 
    1210             :     LOG (("  DNS thread counters: total=%d any-live=%d idle=%d pending=%d\n",
    1211             :           static_cast<uint32_t>(mThreadCount),
    1212             :           static_cast<uint32_t>(mActiveAnyThreadCount),
    1213             :           static_cast<uint32_t>(mNumIdleThreads),
    1214           0 :           static_cast<uint32_t>(mPendingCount)));
    1215             : 
    1216             :     return rv;
    1217             : }
    1218           0 : 
    1219             : ResolverMode
    1220           0 : nsHostResolver::Mode()
    1221           0 : {
    1222             :     if (gTRRService) {
    1223             :         return static_cast<ResolverMode>(gTRRService->Mode());
    1224             :     }
    1225             : 
    1226             :     return MODE_NATIVEONLY;
    1227             : }
    1228             : 
    1229           0 : // Kick-off a name resolve operation, using native resolver and/or TRR
    1230             : nsresult
    1231           0 : nsHostResolver::NameLookup(nsHostRecord *rec)
    1232           0 : {
    1233           0 :     nsresult rv = NS_ERROR_UNKNOWN_HOST;
    1234             :     if (rec->mResolving) {
    1235             :         LOG(("NameLookup %s while already resolving\n", rec->host.get()));
    1236             :         return NS_OK;
    1237           0 :     }
    1238             : 
    1239           0 :     ResolverMode mode = rec->mResolverMode = Mode();
    1240           0 : 
    1241           0 :     rec->mNativeUsed = false;
    1242           0 :     rec->mTRRUsed = false;
    1243           0 :     rec->mNativeSuccess = false;
    1244           0 :     rec->mTRRSuccess = 0;
    1245           0 :     rec->mDidCallbacks = false;
    1246             :     rec->mTrrAUsed = nsHostRecord::INIT;
    1247           0 :     rec->mTrrAAAAUsed = nsHostRecord::INIT;
    1248           0 : 
    1249             :     if (rec->flags & RES_DISABLE_TRR) {
    1250             :         if (mode == MODE_TRRONLY) {
    1251             :             return rv;
    1252             :         }
    1253             :         mode = MODE_NATIVEONLY;
    1254           0 :     }
    1255           0 : 
    1256             :     if (!TRR_DISABLED(mode)) {
    1257             :         rv = TrrLookup(rec);
    1258           0 :     }
    1259           0 : 
    1260           0 :     if ((mode == MODE_PARALLEL) ||
    1261           0 :         TRR_DISABLED(mode) ||
    1262           0 :         (mode == MODE_SHADOW) ||
    1263             :         ((mode == MODE_TRRFIRST) && NS_FAILED(rv))) {
    1264             :         rv = NativeLookup(rec);
    1265             :     }
    1266             : 
    1267             :     return rv;
    1268             : }
    1269           0 : 
    1270             : nsresult
    1271           0 : nsHostResolver::ConditionallyRefreshRecord(nsHostRecord *rec, const nsACString &host)
    1272           0 : {
    1273           0 :     if ((rec->CheckExpiration(TimeStamp::NowLoRes()) != nsHostRecord::EXP_VALID
    1274             :             || rec->negative) && !rec->mResolving) {
    1275           0 :         LOG(("  Using %s cache entry for host [%s] but starting async renewal.",
    1276             :             rec->negative ? "negative" :"positive", host.BeginReading()));
    1277           0 :         NameLookup(rec);
    1278             : 
    1279             :         if (!rec->negative) {
    1280             :             // negative entries are constantly being refreshed, only
    1281           0 :             // track positive grace period induced renewals
    1282             :             Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
    1283             :                 METHOD_RENEWAL);
    1284           0 :         }
    1285             :     }
    1286             :     return NS_OK;
    1287             : }
    1288           0 : 
    1289             : void
    1290           0 : nsHostResolver::DeQueue(LinkedList<RefPtr<nsHostRecord>>& aQ, nsHostRecord **aResult)
    1291           0 : {
    1292           0 :     RefPtr<nsHostRecord> rec = aQ.popFirst();
    1293           0 :     mPendingCount--;
    1294           0 :     rec.forget(aResult);
    1295             :     (*aResult)->onQueue = false;
    1296             : }
    1297           0 : 
    1298             : bool
    1299           0 : nsHostResolver::GetHostToLookup(nsHostRecord **result)
    1300           0 : {
    1301           0 :     bool timedOut = false;
    1302             :     TimeDuration timeout;
    1303           0 :     TimeStamp epoch, now;
    1304             : 
    1305           0 :     MutexAutoLock lock(mLock);
    1306           0 : 
    1307             :     timeout = (mNumIdleThreads >= HighThreadThreshold) ? mShortIdleTimeout : mLongIdleTimeout;
    1308           0 :     epoch = TimeStamp::Now();
    1309             : 
    1310             :     while (!mShutdown) {
    1311             :         // remove next record from Q; hand over owning reference. Check high, then med, then low
    1312             : 
    1313           0 : #define SET_GET_TTL(var, val) (var)->mGetTtl = sGetTtlEnabled && (val)
    1314           0 : 
    1315           0 :         if (!mHighQ.isEmpty()) {
    1316           0 :             DeQueue (mHighQ, result);
    1317             :             SET_GET_TTL(*result, false);
    1318             :             return true;
    1319           0 :         }
    1320           0 : 
    1321           0 :         if (mActiveAnyThreadCount < HighThreadThreshold) {
    1322           0 :             if (!mMediumQ.isEmpty()) {
    1323           0 :                 DeQueue (mMediumQ, result);
    1324           0 :                 mActiveAnyThreadCount++;
    1325           0 :                 (*result)->usingAnyThread = true;
    1326             :                 SET_GET_TTL(*result, true);
    1327             :                 return true;
    1328           0 :             }
    1329           0 : 
    1330           0 :             if (!mLowQ.isEmpty()) {
    1331           0 :                 DeQueue (mLowQ, result);
    1332           0 :                 mActiveAnyThreadCount++;
    1333           0 :                 (*result)->usingAnyThread = true;
    1334             :                 SET_GET_TTL(*result, true);
    1335             :                 return true;
    1336             :             }
    1337             :         }
    1338             : 
    1339           0 :         // Determining timeout is racy, so allow one cycle through checking the queues
    1340             :         // before exiting.
    1341             :         if (timedOut)
    1342             :             break;
    1343             : 
    1344             :         // wait for one or more of the following to occur:
    1345             :         //  (1) the pending queue has a host record to process
    1346             :         //  (2) the shutdown flag has been set
    1347           0 :         //  (3) the thread has been idle for too long
    1348           0 : 
    1349           0 :         mNumIdleThreads++;
    1350             :         mIdleThreadCV.Wait(timeout);
    1351           0 :         mNumIdleThreads--;
    1352             : 
    1353           0 :         now = TimeStamp::Now();
    1354             : 
    1355             :         if (now - epoch >= timeout) {
    1356             :             timedOut = true;
    1357             :         } else {
    1358             :             // It is possible that CondVar::Wait() was interrupted and returned
    1359             :             // early, in which case we will loop back and re-enter it. In that
    1360           0 :             // case we want to do so with the new timeout reduced to reflect
    1361           0 :             // time already spent waiting.
    1362             :             timeout -= now - epoch;
    1363             :             epoch = now;
    1364             :         }
    1365             :     }
    1366             : 
    1367             :     // tell thread to exit...
    1368             :     return false;
    1369             : }
    1370           0 : 
    1371             : void
    1372             : nsHostResolver::PrepareRecordExpiration(nsHostRecord* rec) const
    1373           0 : {
    1374           0 :     // NOTE: rec->addr_info_lock is already held by parent
    1375           0 :     MOZ_ASSERT(((bool)rec->addr_info) != rec->negative);
    1376           0 :     mLock.AssertCurrentThreadOwns();
    1377           0 :     if (!rec->addr_info) {
    1378           0 :         rec->SetExpiration(TimeStamp::NowLoRes(),
    1379             :                            NEGATIVE_RECORD_LIFETIME, 0);
    1380             :         LOG(("Caching host [%s] negative record for %u seconds.\n",
    1381             :              rec->host.get(), NEGATIVE_RECORD_LIFETIME));
    1382             :         return;
    1383           0 :     }
    1384           0 : 
    1385             :     unsigned int lifetime = mDefaultCacheLifetime;
    1386           0 :     unsigned int grace = mDefaultGracePeriod;
    1387           0 : 
    1388           0 :     unsigned int ttl = mDefaultCacheLifetime;
    1389           0 :     if (sGetTtlEnabled || rec->addr_info->IsTRR()) {
    1390             :         if (rec->addr_info && rec->addr_info->ttl != AddrInfo::NO_TTL_DATA) {
    1391             :             ttl = rec->addr_info->ttl;
    1392             :         }
    1393             :         lifetime = ttl;
    1394             :         grace = 0;
    1395           0 :     }
    1396           0 : 
    1397             :     rec->SetExpiration(TimeStamp::NowLoRes(), lifetime, grace);
    1398             :     LOG(("Caching host [%s] record for %u seconds (grace %d).",
    1399             :          rec->host.get(), lifetime, grace));
    1400             : }
    1401           0 : 
    1402             : static nsresult
    1403           0 : merge_rrset(AddrInfo *rrto, AddrInfo *rrfrom)
    1404             : {
    1405             :     if (!rrto || !rrfrom) {
    1406             :         return NS_ERROR_NULL_POINTER;
    1407           0 :     }
    1408           0 :     NetAddrElement *element;
    1409           0 :     while ((element = rrfrom->mAddresses.getFirst())) {
    1410             :         element->remove(); // unlist from old
    1411             :         rrto->AddAddress(element); // enlist on new
    1412             :     }
    1413             :     return NS_OK;
    1414             : }
    1415           0 : 
    1416             : static bool
    1417           0 : different_rrset(AddrInfo *rrset1, AddrInfo *rrset2)
    1418             : {
    1419             :     if (!rrset1 || !rrset2) {
    1420             :         return true;
    1421           0 :     }
    1422           0 : 
    1423           0 :     LOG(("different_rrset %s\n", rrset1->mHostName.get()));
    1424             :     nsTArray<NetAddr> orderedSet1;
    1425           0 :     nsTArray<NetAddr> orderedSet2;
    1426             : 
    1427             :     if (rrset1->IsTRR() != rrset2->IsTRR()) {
    1428             :         return true;
    1429           0 :     }
    1430           0 : 
    1431           0 :     for (NetAddrElement *element = rrset1->mAddresses.getFirst();
    1432             :          element; element = element->getNext()) {
    1433           0 :         if (LOG_ENABLED()) {
    1434           0 :             char buf[128];
    1435             :             NetAddrToString(&element->mAddress, buf, 128);
    1436           0 :             LOG(("different_rrset add to set 1 %s\n", buf));
    1437             :         }
    1438             :         orderedSet1.InsertElementAt(orderedSet1.Length(), element->mAddress);
    1439           0 :     }
    1440           0 : 
    1441           0 :     for (NetAddrElement *element = rrset2->mAddresses.getFirst();
    1442             :          element; element = element->getNext()) {
    1443           0 :         if (LOG_ENABLED()) {
    1444           0 :             char buf[128];
    1445             :             NetAddrToString(&element->mAddress, buf, 128);
    1446           0 :             LOG(("different_rrset add to set 2 %s\n", buf));
    1447             :         }
    1448             :         orderedSet2.InsertElementAt(orderedSet2.Length(), element->mAddress);
    1449           0 :     }
    1450           0 : 
    1451             :     if (orderedSet1.Length() != orderedSet2.Length()) {
    1452             :         LOG(("different_rrset true due to length change\n"));
    1453           0 :         return true;
    1454           0 :     }
    1455             :     orderedSet1.Sort();
    1456           0 :     orderedSet2.Sort();
    1457           0 : 
    1458           0 :     for (uint32_t i = 0; i < orderedSet1.Length(); ++i) {
    1459             :         if (!(orderedSet1[i] == orderedSet2[i])) {
    1460             :             LOG(("different_rrset true due to content change\n"));
    1461             :             return true;
    1462           0 :         }
    1463             :     }
    1464             :     LOG(("different_rrset false\n"));
    1465             :     return false;
    1466             : }
    1467             : 
    1468             : //
    1469             : // CompleteLookup() checks if the resolving should be redone and if so it
    1470             : // returns LOOKUP_RESOLVEAGAIN, but only if 'status' is not NS_ERROR_ABORT.
    1471           0 : // takes ownership of AddrInfo parameter
    1472             : nsHostResolver::LookupStatus
    1473           0 : nsHostResolver::CompleteLookup(nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb)
    1474           0 : {
    1475           0 :     MutexAutoLock lock(mLock);
    1476             :     MOZ_ASSERT(rec);
    1477             :     MOZ_ASSERT(rec->pb == pb);
    1478             : 
    1479           0 :     // newRRSet needs to be taken into the hostrecord (which will then own it)
    1480             :     // or deleted on early return.
    1481           0 :     nsAutoPtr<AddrInfo> newRRSet(aNewRRSet);
    1482             : 
    1483           0 :     bool trrResult = newRRSet && newRRSet->IsTRR();
    1484           0 : 
    1485           0 :     if (rec->mResolveAgain && (status != NS_ERROR_ABORT) && !trrResult) {
    1486           0 :         LOG(("nsHostResolver record %p resolve again due to flushcache\n", rec));
    1487             :         rec->mResolveAgain = false;
    1488             :         return LOOKUP_RESOLVEAGAIN;
    1489           0 :     }
    1490           0 : 
    1491           0 :     MOZ_ASSERT(rec->mResolving);
    1492             :     rec->mResolving--;
    1493             :     LOG(("nsHostResolver::CompleteLookup %s %p %X trr=%d stillResolving=%d\n",
    1494             :          rec->host.get(), aNewRRSet, (unsigned int)status,
    1495           0 :          aNewRRSet ? aNewRRSet->IsTRR() : 0, rec->mResolving));
    1496           0 : 
    1497           0 :     if (trrResult) {
    1498             :         MutexAutoLock trrlock(rec->mTrrLock);
    1499             :         LOG(("TRR lookup Complete (%d) %s %s\n",
    1500           0 :              newRRSet->IsTRR(), newRRSet->mHostName.get(),
    1501           0 :              NS_SUCCEEDED(status) ? "OK" : "FAILED"));
    1502           0 :         MOZ_ASSERT(TRROutstanding());
    1503           0 :         if (newRRSet->IsTRR() == TRRTYPE_A) {
    1504           0 :             MOZ_ASSERT(rec->mTrrA);
    1505           0 :             rec->mTrrA = nullptr;
    1506           0 :             rec->mTrrAUsed = NS_SUCCEEDED(status) ? nsHostRecord::OK : nsHostRecord::FAILED;
    1507           0 :         } else if (newRRSet->IsTRR() == TRRTYPE_AAAA) {
    1508           0 :             MOZ_ASSERT(rec->mTrrAAAA);
    1509             :             rec->mTrrAAAA = nullptr;
    1510           0 :             rec->mTrrAAAAUsed = NS_SUCCEEDED(status) ? nsHostRecord::OK : nsHostRecord::FAILED;
    1511             :         } else {
    1512             :             MOZ_ASSERT(0);
    1513           0 :         }
    1514           0 : 
    1515             :         if (NS_SUCCEEDED(status)) {
    1516           0 :             rec->mTRRSuccess++;
    1517           0 :         }
    1518           0 :         if (TRROutstanding()) {
    1519             :             if (NS_FAILED(status)) {
    1520             :                 return LOOKUP_OK; // wait for outstanding
    1521             :             }
    1522             : 
    1523           0 :             // There's another TRR complete pending. Wait for it and keep
    1524           0 :             // this RRset around until then.
    1525           0 :             MOZ_ASSERT(!rec->mFirstTRR && newRRSet);
    1526             :             rec->mFirstTRR = newRRSet; // autoPtr.swap()
    1527           0 :             MOZ_ASSERT(rec->mFirstTRR && !newRRSet);
    1528             : 
    1529             :             if (rec->mDidCallbacks || rec->mResolverMode == MODE_SHADOW) {
    1530             :                 return LOOKUP_OK;
    1531           0 :             }
    1532             : 
    1533             :             if (rec->mTrrA && (!gTRRService || !gTRRService->EarlyAAAA())) {
    1534           0 :                 // This is an early AAAA with a pending A response. Allowed
    1535             :                 // only by pref.
    1536             :                 LOG(("CompleteLookup: avoiding early use of TRR AAAA!\n"));
    1537             :                 return LOOKUP_OK;
    1538             :             }
    1539             : 
    1540           0 :             // we can do some callbacks with this partial result which requires
    1541           0 :             // a deep copy
    1542             :             newRRSet = new AddrInfo(rec->mFirstTRR);
    1543             :             MOZ_ASSERT(rec->mFirstTRR && newRRSet);
    1544             : 
    1545             :         } else {
    1546           0 :             // no more outstanding TRRs
    1547           0 :             // If mFirstTRR is set, merge those addresses into current set!
    1548           0 :             if (rec->mFirstTRR) {
    1549             :                 if (NS_SUCCEEDED(status)) {
    1550             :                     merge_rrset(newRRSet, rec->mFirstTRR);
    1551           0 :                 }
    1552             :                 else {
    1553           0 :                     newRRSet = rec->mFirstTRR; // transfers
    1554             :                 }
    1555             :                 rec->mFirstTRR = nullptr;
    1556           0 :             }
    1557             : 
    1558           0 :             if (!rec->mTRRSuccess) {
    1559           0 :                 // no TRR success
    1560             :                 newRRSet = nullptr;
    1561             :                 status = NS_ERROR_UNKNOWN_HOST;
    1562           0 :             }
    1563           0 : 
    1564           0 :             if (!rec->mTRRSuccess && rec->mResolverMode == MODE_TRRFIRST) {
    1565           0 :                 MOZ_ASSERT(!rec->mResolving);
    1566             :                 NativeLookup(rec);
    1567             :                 MOZ_ASSERT(rec->mResolving);
    1568             :                 return LOOKUP_OK;
    1569             :             }
    1570             :             // continue
    1571           0 :         }
    1572             : 
    1573           0 :         if (NS_SUCCEEDED(status) && (rec->mTRRSuccess == 1)) {
    1574             :             // store the duration on first (used) TRR response
    1575             :             rec->mTrrDuration = TimeStamp::Now() - rec->mTrrStart;
    1576             :         }
    1577           0 : 
    1578           0 :     } else { // native resolve completed
    1579           0 :         if (rec->usingAnyThread) {
    1580             :             mActiveAnyThreadCount--;
    1581             :             rec->usingAnyThread = false;
    1582           0 :         }
    1583           0 : 
    1584           0 :         rec->mNative = false;
    1585           4 :         rec->mNativeSuccess = newRRSet ? true : false;
    1586             :         if (rec->mNativeSuccess) {
    1587             :             rec->mNativeDuration = TimeStamp::Now() - rec->mNativeStart;
    1588             :         }
    1589             :     }
    1590             : 
    1591             :     // update record fields.  We might have a rec->addr_info already if a
    1592             :     // previous lookup result expired and we're reresolving it or we get
    1593           0 :     // a late second TRR response.
    1594           0 :     // note that we don't update the addr_info if this is trr shadow results
    1595           0 :     if (!mShutdown &&
    1596           0 :         !(trrResult && rec->mResolverMode == MODE_SHADOW)) {
    1597           0 :         MutexAutoLock lock(rec->addr_info_lock);
    1598           0 :         nsAutoPtr<AddrInfo> old_addr_info;
    1599           0 :         if (different_rrset(rec->addr_info, newRRSet)) {
    1600           0 :             LOG(("nsHostResolver record %p new gencnt\n", rec));
    1601           4 :             old_addr_info = rec->addr_info;
    1602             :             rec->addr_info = newRRSet.forget();
    1603           0 :             rec->addr_info_gencnt++;
    1604           0 :         } else {
    1605             :             if (rec->addr_info && newRRSet) {
    1606           0 :                 rec->addr_info->ttl = newRRSet->ttl;
    1607             :             }
    1608           0 :             old_addr_info = newRRSet.forget();
    1609           4 :         }
    1610             :         rec->negative = !rec->addr_info;
    1611             :         PrepareRecordExpiration(rec);
    1612           4 :     }
    1613             : 
    1614           4 :     bool doCallbacks = true;
    1615             : 
    1616           0 :     if (trrResult && (rec->mResolverMode == MODE_SHADOW) && !rec->mDidCallbacks) {
    1617           0 :         // don't report result based only on suppressed TRR info
    1618             :         doCallbacks = false;
    1619           4 :         LOG(("nsHostResolver Suppressing TRR %s because it is first shadow result\n",
    1620             :              rec->host.get()));
    1621           0 :     } else if(trrResult && rec->mDidCallbacks) {
    1622             :         // already callback'ed on the first TRR response
    1623             :         LOG(("nsHostResolver Suppressing callback for second TRR response for %s\n",
    1624             :              rec->host.get()));
    1625             :         doCallbacks = false;
    1626             :     }
    1627           1 : 
    1628           0 : 
    1629             :     if (LOG_ENABLED()) {
    1630           0 :         MutexAutoLock lock(rec->addr_info_lock);
    1631           0 :         NetAddrElement *element;
    1632           0 :         if (rec->addr_info) {
    1633             :             for (element = rec->addr_info->mAddresses.getFirst();
    1634           0 :                  element; element = element->getNext()) {
    1635           0 :                 char buf[128];
    1636             :                 NetAddrToString(&element->mAddress, buf, sizeof(buf));
    1637             :                 LOG(("CompleteLookup: %s has %s\n", rec->host.get(), buf));
    1638           0 :             }
    1639             :         } else {
    1640             :             LOG(("CompleteLookup: %s has NO address\n", rec->host.get()));
    1641             :         }
    1642           4 :     }
    1643             : 
    1644             :     if (doCallbacks) {
    1645          12 :         // get the list of pending callbacks for this lookup, and notify
    1646             :         // them that the lookup is complete.
    1647           4 :         mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs = std::move(rec->mCallbacks);
    1648             : 
    1649           0 :         LOG(("nsHostResolver record %p calling back dns users\n", rec));
    1650           6 : 
    1651             :         for (nsResolveHostCallback* c = cbs.getFirst(); c; c = c->removeAndGetNext()) {
    1652           4 :             c->OnResolveHostComplete(this, rec, status);
    1653             :         }
    1654             :         rec->mDidCallbacks = true;
    1655           0 :     }
    1656           4 : 
    1657             :     if (!rec->mResolving && !mShutdown) {
    1658             :         rec->ResolveComplete();
    1659           0 : 
    1660           0 :         // add to mEvictionQ
    1661           0 :         MOZ_ASSERT(!rec->isInList());
    1662           4 :         mEvictionQ.insertBack(rec);
    1663             :         if (mEvictionQSize < mMaxCacheEntries) {
    1664             :             mEvictionQSize++;
    1665           0 :         } else {
    1666           0 :             // remove first element on mEvictionQ
    1667             :             RefPtr<nsHostRecord> head = mEvictionQ.popFirst();
    1668           0 :             mRecordDB.Remove(*static_cast<nsHostKey *>(head.get()));
    1669             : 
    1670           0 :             if (!head->negative) {
    1671           0 :                 // record the age of the entry upon eviction.
    1672           0 :                 TimeDuration age = TimeStamp::NowLoRes() - head->mValidStart;
    1673           0 :                 Telemetry::Accumulate(Telemetry::DNS_CLEANUP_AGE,
    1674             :                                       static_cast<uint32_t>(age.ToSeconds() / 60));
    1675           0 :                 if (head->CheckExpiration(TimeStamp::Now()) !=
    1676           0 :                     nsHostRecord::EXP_EXPIRED) {
    1677             :                   Telemetry::Accumulate(Telemetry::DNS_PREMATURE_EVICTION,
    1678             :                                         static_cast<uint32_t>(age.ToSeconds() / 60));
    1679             :                 }
    1680             :             }
    1681             :         }
    1682             :     }
    1683             : 
    1684             : #ifdef DNSQUERY_AVAILABLE
    1685             :     // Unless the result is from TRR, resolve again to get TTL
    1686             :     bool fromTRR = false;
    1687             :     {
    1688             :         MutexAutoLock lock(rec->addr_info_lock);
    1689             :         if(rec->addr_info && rec->addr_info->IsTRR()) {
    1690             :             fromTRR = true;
    1691             :         }
    1692             :     }
    1693             :     if (!fromTRR &&
    1694             :         !mShutdown && !rec->mGetTtl && !rec->mResolving && sGetTtlEnabled) {
    1695             :         LOG(("Issuing second async lookup for TTL for host [%s].", rec->host.get()));
    1696             :         rec->flags =
    1697             :             (rec->flags & ~RES_PRIORITY_MEDIUM) | RES_PRIORITY_LOW |
    1698             :             RES_DISABLE_TRR;
    1699             :         DebugOnly<nsresult> rv = NameLookup(rec);
    1700             :         NS_WARNING_ASSERTION(
    1701             :             NS_SUCCEEDED(rv),
    1702             :             "Could not issue second async lookup for TTL.");
    1703             :     }
    1704             : #endif
    1705             :     return LOOKUP_OK;
    1706             : }
    1707           0 : 
    1708             : void
    1709             : nsHostResolver::CancelAsyncRequest(const nsACString &host,
    1710             :                                    const OriginAttributes &aOriginAttributes,
    1711             :                                    uint16_t                flags,
    1712             :                                    uint16_t                af,
    1713             :                                    nsIDNSListener         *aListener,
    1714             :                                    nsresult                status)
    1715           0 : 
    1716             : {
    1717           0 :     MutexAutoLock lock(mLock);
    1718           0 : 
    1719             :     nsAutoCString originSuffix;
    1720             :     aOriginAttributes.CreateSuffix(originSuffix);
    1721             : 
    1722           0 :     // Lookup the host record associated with host, flags & address family
    1723           0 : 
    1724           0 :     nsHostKey key(host, flags, af,
    1725           0 :                   (aOriginAttributes.mPrivateBrowsingId > 0),
    1726           0 :                   originSuffix);
    1727           0 :     RefPtr<nsHostRecord> rec = mRecordDB.Get(key);
    1728             :     if (rec) {
    1729           0 :         nsHostRecord* recPtr = nullptr;
    1730           0 : 
    1731           0 :         for (RefPtr<nsResolveHostCallback> c : rec->mCallbacks) {
    1732           0 :             if (c->EqualsAsyncListener(aListener)) {
    1733           0 :                 c->remove();
    1734           0 :                 recPtr = rec;
    1735             :                 c->OnResolveHostComplete(this, recPtr, status);
    1736             :                 break;
    1737             :             }
    1738             :         }
    1739           0 : 
    1740           0 :         // If there are no more callbacks, remove the hash table entry
    1741             :         if (recPtr && recPtr->mCallbacks.isEmpty()) {
    1742           0 :             mRecordDB.Remove(*static_cast<nsHostKey *>(recPtr));
    1743           0 :             // If record is on a Queue, remove it and then deref it
    1744             :             if (recPtr->isInList()) {
    1745             :                 recPtr->remove();
    1746             :             }
    1747           0 :         }
    1748             :     }
    1749             : }
    1750           0 : 
    1751             : size_t
    1752           0 : nsHostResolver::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
    1753             : {
    1754           0 :     MutexAutoLock lock(mLock);
    1755             : 
    1756           0 :     size_t n = mallocSizeOf(this);
    1757           0 : 
    1758           0 :     n += mRecordDB.ShallowSizeOfExcludingThis(mallocSizeOf);
    1759           0 :     for (auto iter = mRecordDB.ConstIter(); !iter.Done(); iter.Next()) {
    1760             :         auto entry = iter.UserData();
    1761             :         n += entry->SizeOfIncludingThis(mallocSizeOf);
    1762             :     }
    1763             : 
    1764             :     // The following fields aren't measured.
    1765             :     // - mHighQ, mMediumQ, mLowQ, mEvictionQ, because they just point to
    1766             :     //   nsHostRecords that also pointed to by entries |mRecordDB|, and
    1767           0 :     //   measured when |mRecordDB| is measured.
    1768             : 
    1769             :     return n;
    1770             : }
    1771           1 : 
    1772             : void
    1773           1 : nsHostResolver::ThreadFunc(void *arg)
    1774             : {
    1775           0 :     LOG(("DNS lookup thread - starting execution.\n"));
    1776           1 : 
    1777             :     static nsThreadPoolNaming naming;
    1778           0 :     nsCString name = naming.GetNextThreadName("DNS Resolver");
    1779           1 : 
    1780             :     AUTO_PROFILER_REGISTER_THREAD(name.BeginReading());
    1781             :     NS_SetCurrentThreadName(name.BeginReading());
    1782           1 : 
    1783             : #if defined(RES_RETRY_ON_FAILURE)
    1784           0 :     nsResState rs;
    1785           0 : #endif
    1786           1 :     RefPtr<nsHostResolver> resolver = dont_AddRef((nsHostResolver *)arg);
    1787             :     RefPtr<nsHostRecord> rec;
    1788             :     AddrInfo *ai = nullptr;
    1789           0 : 
    1790           0 :     do {
    1791           5 :         if (!rec) {
    1792             :             RefPtr<nsHostRecord> tmpRec;
    1793             :             if (!resolver->GetHostToLookup(getter_AddRefs(tmpRec))) {
    1794             :                 break; // thread shutdown signal
    1795           0 :             }
    1796           4 :             // GetHostToLookup() returns an owning reference
    1797             :             MOZ_ASSERT(tmpRec);
    1798             :             rec.swap(tmpRec);
    1799           4 :         }
    1800             : 
    1801             :         LOG(("DNS lookup thread - Calling getaddrinfo for host [%s].\n",
    1802           0 :              rec->host.get()));
    1803           0 :         
    1804           0 :         TimeStamp startTime = TimeStamp::Now();
    1805           0 :         bool getTtl = rec->mGetTtl;
    1806           4 :         nsresult status = GetAddrInfo(rec->host, rec->af,
    1807             :                                       rec->flags, &ai,
    1808           0 :                                       getTtl);
    1809           0 : #if defined(RES_RETRY_ON_FAILURE)
    1810           0 :         if (NS_FAILED(status) && rs.Reset()) {
    1811             :             status = GetAddrInfo(rec->host, rec->af,
    1812             :                                  rec->flags, &ai, getTtl);
    1813             :         }
    1814             : #endif
    1815           8 : 
    1816             :         {   // obtain lock to check shutdown and manage inter-module telemetry
    1817           0 :             MutexAutoLock lock(resolver->mLock);
    1818           0 : 
    1819           4 :             if (!resolver->mShutdown) {
    1820             :                 TimeDuration elapsed = TimeStamp::Now() - startTime;
    1821           4 :                 uint32_t millis = static_cast<uint32_t>(elapsed.ToMilliseconds());
    1822             : 
    1823           4 :                 if (NS_SUCCEEDED(status)) {
    1824             :                     Telemetry::HistogramID histogramID;
    1825             :                     if (!rec->addr_info_gencnt) {
    1826           0 :                         // Time for initial lookup.
    1827             :                         histogramID = Telemetry::DNS_LOOKUP_TIME;
    1828             :                     } else if (!getTtl) {
    1829             :                         // Time for renewal; categorized by expiration strategy.
    1830             :                         histogramID = Telemetry::DNS_RENEWAL_TIME;
    1831           0 :                     } else {
    1832             :                         // Time to get TTL; categorized by expiration strategy.
    1833           4 :                         histogramID = Telemetry::DNS_RENEWAL_TIME_FOR_TTL;
    1834             :                     }
    1835           0 :                     Telemetry::Accumulate(histogramID, millis);
    1836             :                 } else {
    1837             :                     Telemetry::Accumulate(Telemetry::DNS_FAILED_LOOKUP_TIME, millis);
    1838             :                 }
    1839             :             }
    1840           4 :         }
    1841             : 
    1842             :         LOG(("DNS lookup thread - lookup completed for host [%s]: %s.\n",
    1843             :              rec->host.get(),
    1844           8 :              ai ? "success" : "failure: unknown host"));
    1845             : 
    1846           0 :         if (LOOKUP_RESOLVEAGAIN == resolver->CompleteLookup(rec, status, ai, rec->pb)) {
    1847             :             // leave 'rec' assigned and loop to make a renewed host resolve
    1848           0 :             LOG(("DNS lookup thread - Re-resolving host [%s].\n", rec->host.get()));
    1849           4 :         } else {
    1850             :             rec = nullptr;
    1851             :         }
    1852           0 :     } while(true);
    1853           0 : 
    1854           0 :     resolver->mThreadCount--;
    1855           0 :     resolver = nullptr;
    1856             :     LOG(("DNS lookup thread - queue empty, thread finished.\n"));
    1857             : }
    1858           0 : 
    1859             : void
    1860             : nsHostResolver::SetCacheLimits(uint32_t aMaxCacheEntries,
    1861             :                                uint32_t aDefaultCacheEntryLifetime,
    1862           0 :                                uint32_t aDefaultGracePeriod)
    1863           0 : {
    1864           0 :     MutexAutoLock lock(mLock);
    1865           0 :     mMaxCacheEntries = aMaxCacheEntries;
    1866           0 :     mDefaultCacheLifetime = aDefaultCacheEntryLifetime;
    1867             :     mDefaultGracePeriod = aDefaultGracePeriod;
    1868             : }
    1869           1 : 
    1870             : nsresult
    1871             : nsHostResolver::Create(uint32_t maxCacheEntries,
    1872             :                        uint32_t defaultCacheEntryLifetime,
    1873             :                        uint32_t defaultGracePeriod,
    1874             :                        nsHostResolver **result)
    1875           0 : {
    1876           1 :     auto *res = new nsHostResolver(maxCacheEntries, defaultCacheEntryLifetime,
    1877             :                                    defaultGracePeriod);
    1878           0 :     NS_ADDREF(res);
    1879           1 : 
    1880           0 :     nsresult rv = res->Init();
    1881             :     if (NS_FAILED(rv))
    1882           0 :         NS_RELEASE(res);
    1883           1 : 
    1884             :     *result = res;
    1885             :     return rv;
    1886             : }
    1887           0 : 
    1888             : void
    1889           0 : nsHostResolver::GetDNSCacheEntries(nsTArray<DNSCacheEntries> *args)
    1890           0 : {
    1891             :     MutexAutoLock lock(mLock);
    1892             :     for (auto iter = mRecordDB.Iter(); !iter.Done(); iter.Next()) {
    1893           0 :         // We don't pay attention to address literals, only resolved domains.
    1894           0 :         // Also require a host.
    1895           0 :         nsHostRecord* rec = iter.UserData();
    1896           0 :         MOZ_ASSERT(rec, "rec should never be null here!");
    1897             :         if (!rec || !rec->addr_info) {
    1898             :             continue;
    1899           0 :         }
    1900           0 : 
    1901           0 :         DNSCacheEntries info;
    1902           0 :         info.hostname = rec->host;
    1903           0 :         info.family = rec->af;
    1904           0 :         info.expiration =
    1905             :             (int64_t)(rec->mValidEnd - TimeStamp::NowLoRes()).ToSeconds();
    1906           0 :         if (info.expiration <= 0) {
    1907             :             // We only need valid DNS cache entries
    1908             :             continue;
    1909             :         }
    1910           0 : 
    1911             :         {
    1912           0 :             MutexAutoLock lock(rec->addr_info_lock);
    1913           0 : 
    1914           0 :             NetAddr *addr = nullptr;
    1915           0 :             NetAddrElement *addrElement = rec->addr_info->mAddresses.getFirst();
    1916             :             if (addrElement) {
    1917           0 :                 addr = &addrElement->mAddress;
    1918             :             }
    1919           0 :             while (addr) {
    1920           0 :                 char buf[kIPv6CStrBufSize];
    1921             :                 if (NetAddrToString(addr, buf, sizeof(buf))) {
    1922           0 :                     info.hostaddr.AppendElement(buf);
    1923           0 :                 }
    1924           0 :                 addr = nullptr;
    1925           0 :                 addrElement = addrElement->getNext();
    1926             :                 if (addrElement) {
    1927             :                     addr = &addrElement->mAddress;
    1928           0 :                 }
    1929             :             }
    1930             :             info.TRR = rec->addr_info->IsTRR();
    1931           0 :         }
    1932             : 
    1933             :         args->AppendElement(info);
    1934             :     }
    1935             : }
    1936             : 
    1937             : #undef LOG
    1938             : #undef LOG_ENABLED

Generated by: LCOV version 1.13-14-ga5dd952