LCOV - code coverage report
Current view: top level - netwerk/dns - nsHostResolver.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 321 664 48.3 %
Date: 2017-07-14 16:53:18 Functions: 29 46 63.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             : 
      34             : #include "mozilla/HashFunctions.h"
      35             : #include "mozilla/TimeStamp.h"
      36             : #include "mozilla/Telemetry.h"
      37             : #include "mozilla/DebugOnly.h"
      38             : #include "mozilla/Preferences.h"
      39             : 
      40             : using namespace mozilla;
      41             : using namespace mozilla::net;
      42             : 
      43             : // None of our implementations expose a TTL for negative responses, so we use a
      44             : // constant always.
      45             : static const unsigned int NEGATIVE_RECORD_LIFETIME = 60;
      46             : 
      47             : //----------------------------------------------------------------------------
      48             : 
      49             : // Use a persistent thread pool in order to avoid spinning up new threads all the time.
      50             : // In particular, thread creation results in a res_init() call from libc which is
      51             : // quite expensive.
      52             : //
      53             : // The pool dynamically grows between 0 and MAX_RESOLVER_THREADS in size. New requests
      54             : // go first to an idle thread. If that cannot be found and there are fewer than MAX_RESOLVER_THREADS
      55             : // currently in the pool a new thread is created for high priority requests. If
      56             : // the new request is at a lower priority a new thread will only be created if
      57             : // there are fewer than HighThreadThreshold currently outstanding. If a thread cannot be
      58             : // created or an idle thread located for the request it is queued.
      59             : //
      60             : // When the pool is greater than HighThreadThreshold in size a thread will be destroyed after
      61             : // ShortIdleTimeoutSeconds of idle time. Smaller pools use LongIdleTimeoutSeconds for a
      62             : // timeout period.
      63             : 
      64             : #define HighThreadThreshold     MAX_RESOLVER_THREADS_FOR_ANY_PRIORITY
      65             : #define LongIdleTimeoutSeconds  300           // for threads 1 -> HighThreadThreshold
      66             : #define ShortIdleTimeoutSeconds 60            // for threads HighThreadThreshold+1 -> MAX_RESOLVER_THREADS
      67             : 
      68             : static_assert(HighThreadThreshold <= MAX_RESOLVER_THREADS,
      69             :               "High Thread Threshold should be less equal Maximum allowed thread");
      70             : 
      71             : //----------------------------------------------------------------------------
      72             : 
      73             : static LazyLogModule gHostResolverLog("nsHostResolver");
      74             : #define LOG(args) MOZ_LOG(gHostResolverLog, mozilla::LogLevel::Debug, args)
      75             : #define LOG_ENABLED() MOZ_LOG_TEST(gHostResolverLog, mozilla::LogLevel::Debug)
      76             : 
      77             : #define LOG_HOST(host, interface) host,                                        \
      78             :                  (interface && interface[0] != '\0') ? " on interface " : "",  \
      79             :                  (interface && interface[0] != '\0') ? interface : ""
      80             : 
      81             : //----------------------------------------------------------------------------
      82             : 
      83             : static inline void
      84           5 : MoveCList(PRCList &from, PRCList &to)
      85             : {
      86           5 :     if (!PR_CLIST_IS_EMPTY(&from)) {
      87           1 :         to.next = from.next;
      88           1 :         to.prev = from.prev;
      89           1 :         to.next->prev = &to;
      90           1 :         to.prev->next = &to;
      91           1 :         PR_INIT_CLIST(&from);
      92             :     }
      93           5 : }
      94             : 
      95             : //----------------------------------------------------------------------------
      96             : 
      97             : #if defined(RES_RETRY_ON_FAILURE)
      98             : 
      99             : // this class represents the resolver state for a given thread.  if we
     100             : // encounter a lookup failure, then we can invoke the Reset method on an
     101             : // instance of this class to reset the resolver (in case /etc/resolv.conf
     102             : // for example changed).  this is mainly an issue on GNU systems since glibc
     103             : // only reads in /etc/resolv.conf once per thread.  it may be an issue on
     104             : // other systems as well.
     105             : 
     106             : class nsResState
     107             : {
     108             : public:
     109           1 :     nsResState()
     110             :         // initialize mLastReset to the time when this object
     111             :         // is created.  this means that a reset will not occur
     112             :         // if a thread is too young.  the alternative would be
     113             :         // to initialize this to the beginning of time, so that
     114             :         // the first failure would cause a reset, but since the
     115             :         // thread would have just started up, it likely would
     116             :         // already have current /etc/resolv.conf info.
     117           1 :         : mLastReset(PR_IntervalNow())
     118             :     {
     119           1 :     }
     120             : 
     121           0 :     bool Reset()
     122             :     {
     123             :         // reset no more than once per second
     124           0 :         if (PR_IntervalToSeconds(PR_IntervalNow() - mLastReset) < 1)
     125           0 :             return false;
     126             : 
     127           0 :         LOG(("Calling 'res_ninit'.\n"));
     128             : 
     129           0 :         mLastReset = PR_IntervalNow();
     130           0 :         return (res_ninit(&_res) == 0);
     131             :     }
     132             : 
     133             : private:
     134             :     PRIntervalTime mLastReset;
     135             : };
     136             : 
     137             : #endif // RES_RETRY_ON_FAILURE
     138             : 
     139             : //----------------------------------------------------------------------------
     140             : 
     141             : static inline bool
     142           1 : IsHighPriority(uint16_t flags)
     143             : {
     144           1 :     return !(flags & (nsHostResolver::RES_PRIORITY_LOW | nsHostResolver::RES_PRIORITY_MEDIUM));
     145             : }
     146             : 
     147             : static inline bool
     148           0 : IsMediumPriority(uint16_t flags)
     149             : {
     150           0 :     return flags & nsHostResolver::RES_PRIORITY_MEDIUM;
     151             : }
     152             : 
     153             : static inline bool
     154           0 : IsLowPriority(uint16_t flags)
     155             : {
     156           0 :     return flags & nsHostResolver::RES_PRIORITY_LOW;
     157             : }
     158             : 
     159             : //----------------------------------------------------------------------------
     160             : // this macro filters out any flags that are not used when constructing the
     161             : // host key.  the significant flags are those that would affect the resulting
     162             : // host record (i.e., the flags that are passed down to PR_GetAddrInfoByName).
     163             : #define RES_KEY_FLAGS(_f) ((_f) & nsHostResolver::RES_CANON_NAME)
     164             : 
     165           3 : nsHostRecord::nsHostRecord(const nsHostKey *key)
     166             :     : addr_info_lock("nsHostRecord.addr_info_lock")
     167             :     , addr_info_gencnt(0)
     168             :     , addr_info(nullptr)
     169             :     , addr(nullptr)
     170             :     , negative(false)
     171             :     , resolving(false)
     172             :     , onQueue(false)
     173             :     , usingAnyThread(false)
     174             :     , mDoomed(false)
     175             : #if TTL_AVAILABLE
     176             :     , mGetTtl(false)
     177             : #endif
     178             :     , mBlacklistedCount(0)
     179           3 :     , mResolveAgain(false)
     180             : {
     181           3 :     host = ((char *) this) + sizeof(nsHostRecord);
     182           3 :     memcpy((char *) host, key->host, strlen(key->host) + 1);
     183           3 :     flags = key->flags;
     184           3 :     af = key->af;
     185           3 :     netInterface = host + strlen(key->host) + 1;
     186           3 :     memcpy((char *) netInterface, key->netInterface,
     187           6 :            strlen(key->netInterface) + 1);
     188           3 :     originSuffix = netInterface + strlen(key->netInterface) + 1;
     189           3 :     memcpy((char *) originSuffix, key->originSuffix,
     190           6 :            strlen(key->originSuffix) + 1);
     191           3 :     PR_INIT_CLIST(this);
     192           3 :     PR_INIT_CLIST(&callbacks);
     193           3 : }
     194             : 
     195             : nsresult
     196           3 : nsHostRecord::Create(const nsHostKey *key, nsHostRecord **result)
     197             : {
     198           3 :     size_t hostLen = strlen(key->host) + 1;
     199           3 :     size_t netInterfaceLen = strlen(key->netInterface) + 1;
     200           3 :     size_t originSuffixLen = strlen(key->originSuffix) + 1;
     201           3 :     size_t size = hostLen + netInterfaceLen + originSuffixLen + sizeof(nsHostRecord);
     202             : 
     203             :     // Use placement new to create the object with room for the hostname,
     204             :     // network interface name and originSuffix allocated after it.
     205           3 :     void *place = ::operator new(size);
     206           3 :     *result = new(place) nsHostRecord(key);
     207           3 :     NS_ADDREF(*result);
     208             : 
     209           3 :     return NS_OK;
     210             : }
     211             : 
     212             : void
     213           1 : nsHostRecord::SetExpiration(const mozilla::TimeStamp& now, unsigned int valid, unsigned int grace)
     214             : {
     215           1 :     mValidStart = now;
     216           1 :     mGraceStart = now + TimeDuration::FromSeconds(valid);
     217           1 :     mValidEnd = now + TimeDuration::FromSeconds(valid + grace);
     218           1 : }
     219             : 
     220             : void
     221           1 : nsHostRecord::CopyExpirationTimesAndFlagsFrom(const nsHostRecord *aFromHostRecord)
     222             : {
     223             :     // This is used to copy information from a cache entry to a record. All
     224             :     // information necessary for HasUsableRecord needs to be copied.
     225           1 :     mValidStart = aFromHostRecord->mValidStart;
     226           1 :     mValidEnd = aFromHostRecord->mValidEnd;
     227           1 :     mGraceStart = aFromHostRecord->mGraceStart;
     228           1 :     mDoomed = aFromHostRecord->mDoomed;
     229           1 : }
     230             : 
     231           0 : nsHostRecord::~nsHostRecord()
     232             : {
     233           0 :     Telemetry::Accumulate(Telemetry::DNS_BLACKLIST_COUNT, mBlacklistedCount);
     234           0 :     delete addr_info;
     235           0 :     delete addr;
     236           0 : }
     237             : 
     238             : bool
     239           3 : nsHostRecord::Blacklisted(NetAddr *aQuery)
     240             : {
     241             :     // must call locked
     242           3 :     LOG(("Checking blacklist for host [%s%s%s], host record [%p].\n",
     243             :           LOG_HOST(host, netInterface), this));
     244             : 
     245             :     // skip the string conversion for the common case of no blacklist
     246           3 :     if (!mBlacklistedItems.Length()) {
     247           3 :         return false;
     248             :     }
     249             : 
     250             :     char buf[kIPv6CStrBufSize];
     251           0 :     if (!NetAddrToString(aQuery, buf, sizeof(buf))) {
     252           0 :         return false;
     253             :     }
     254           0 :     nsDependentCString strQuery(buf);
     255             : 
     256           0 :     for (uint32_t i = 0; i < mBlacklistedItems.Length(); i++) {
     257           0 :         if (mBlacklistedItems.ElementAt(i).Equals(strQuery)) {
     258           0 :             LOG(("Address [%s] is blacklisted for host [%s%s%s].\n", buf,
     259             :                  LOG_HOST(host, netInterface)));
     260           0 :             return true;
     261             :         }
     262             :     }
     263             : 
     264           0 :     return false;
     265             : }
     266             : 
     267             : void
     268           0 : nsHostRecord::ReportUnusable(NetAddr *aAddress)
     269             : {
     270             :     // must call locked
     271           0 :     LOG(("Adding address to blacklist for host [%s%s%s], host record [%p].\n",
     272             :          LOG_HOST(host, netInterface), this));
     273             : 
     274           0 :     ++mBlacklistedCount;
     275             : 
     276           0 :     if (negative)
     277           0 :         mDoomed = true;
     278             : 
     279             :     char buf[kIPv6CStrBufSize];
     280           0 :     if (NetAddrToString(aAddress, buf, sizeof(buf))) {
     281           0 :         LOG(("Successfully adding address [%s] to blacklist for host "
     282             :              "[%s%s%s].\n", buf, LOG_HOST(host, netInterface)));
     283           0 :         mBlacklistedItems.AppendElement(nsCString(buf));
     284             :     }
     285           0 : }
     286             : 
     287             : void
     288           0 : nsHostRecord::ResetBlacklist()
     289             : {
     290             :     // must call locked
     291           0 :     LOG(("Resetting blacklist for host [%s%s%s], host record [%p].\n",
     292             :          LOG_HOST(host, netInterface), this));
     293           0 :     mBlacklistedItems.Clear();
     294           0 : }
     295             : 
     296             : nsHostRecord::ExpirationStatus
     297           5 : nsHostRecord::CheckExpiration(const mozilla::TimeStamp& now) const {
     298          13 :     if (!mGraceStart.IsNull() && now >= mGraceStart
     299           5 :             && !mValidEnd.IsNull() && now < mValidEnd) {
     300           0 :         return nsHostRecord::EXP_GRACE;
     301             :     }
     302           5 :     if (!mValidEnd.IsNull() && now < mValidEnd) {
     303           3 :         return nsHostRecord::EXP_VALID;
     304             :     }
     305             : 
     306           2 :     return nsHostRecord::EXP_EXPIRED;
     307             : }
     308             : 
     309             : 
     310             : bool
     311           4 : nsHostRecord::HasUsableResult(const mozilla::TimeStamp& now, uint16_t queryFlags) const
     312             : {
     313           4 :     if (mDoomed) {
     314           0 :         return false;
     315             :     }
     316             : 
     317             :     // don't use cached negative results for high priority queries.
     318           4 :     if (negative && IsHighPriority(queryFlags)) {
     319           0 :         return false;
     320             :     }
     321             : 
     322           4 :     if (CheckExpiration(now) == EXP_EXPIRED) {
     323           2 :         return false;
     324             :     }
     325             : 
     326           2 :     return addr_info || addr || negative;
     327             : }
     328             : 
     329             : static size_t
     330           0 : SizeOfResolveHostCallbackListExcludingHead(const PRCList *head,
     331             :                                            MallocSizeOf mallocSizeOf)
     332             : {
     333           0 :     size_t n = 0;
     334           0 :     PRCList *curr = head->next;
     335           0 :     while (curr != head) {
     336             :         nsResolveHostCallback *callback =
     337           0 :             static_cast<nsResolveHostCallback*>(curr);
     338           0 :         n += callback->SizeOfIncludingThis(mallocSizeOf);
     339           0 :         curr = curr->next;
     340             :     }
     341           0 :     return n;
     342             : }
     343             : 
     344             : size_t
     345           0 : nsHostRecord::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
     346             : {
     347           0 :     size_t n = mallocSizeOf(this);
     348             : 
     349             :     // The |host| field (inherited from nsHostKey) actually points to extra
     350             :     // memory that is allocated beyond the end of the nsHostRecord (see
     351             :     // nsHostRecord::Create()).  So it will be included in the
     352             :     // |mallocSizeOf(this)| call above.
     353             : 
     354           0 :     n += SizeOfResolveHostCallbackListExcludingHead(&callbacks, mallocSizeOf);
     355           0 :     n += addr_info ? addr_info->SizeOfIncludingThis(mallocSizeOf) : 0;
     356           0 :     n += mallocSizeOf(addr);
     357             : 
     358           0 :     n += mBlacklistedItems.ShallowSizeOfExcludingThis(mallocSizeOf);
     359           0 :     for (size_t i = 0; i < mBlacklistedItems.Length(); i++) {
     360           0 :         n += mBlacklistedItems[i].SizeOfExcludingThisIfUnshared(mallocSizeOf);
     361             :     }
     362           0 :     return n;
     363             : }
     364             : 
     365             : nsHostRecord::DnsPriority
     366           1 : nsHostRecord::GetPriority(uint16_t aFlags)
     367             : {
     368           1 :     if (IsHighPriority(aFlags)){
     369           1 :         return nsHostRecord::DNS_PRIORITY_HIGH;
     370             :     }
     371           0 :     if (IsMediumPriority(aFlags)) {
     372           0 :         return nsHostRecord::DNS_PRIORITY_MEDIUM;
     373             :     }
     374             : 
     375           0 :     return nsHostRecord::DNS_PRIORITY_LOW;
     376             : }
     377             : 
     378             : // Returns true if the entry can be removed, or false if it should be left.
     379             : // Sets mResolveAgain true for entries being resolved right now.
     380             : bool
     381           0 : nsHostRecord::RemoveOrRefresh()
     382             : {
     383           0 :     if (resolving) {
     384           0 :         if (!onQueue) {
     385             :             // The request has been passed to the OS resolver. The resultant DNS
     386             :             // record should be considered stale and not trusted; set a flag to
     387             :             // ensure it is called again.
     388           0 :             mResolveAgain = true;
     389             :         }
     390             :         // if Onqueue is true, the host entry is already added to the cache
     391             :         // but is still pending to get resolved: just leave it in hash.
     392           0 :         return false;
     393             :     }
     394             :     // Already resolved; not in a pending state; remove from cache.
     395           0 :     return true;
     396             : }
     397             : 
     398             : //----------------------------------------------------------------------------
     399             : 
     400             : struct nsHostDBEnt : PLDHashEntryHdr
     401             : {
     402             :     nsHostRecord *rec;
     403             : };
     404             : 
     405             : static PLDHashNumber
     406           4 : HostDB_HashKey(const void *key)
     407             : {
     408           4 :     const nsHostKey *hk = static_cast<const nsHostKey *>(key);
     409           8 :     return AddToHash(HashString(hk->host), RES_KEY_FLAGS(hk->flags), hk->af,
     410          12 :                      HashString(hk->netInterface), HashString(hk->originSuffix));
     411             : }
     412             : 
     413             : static bool
     414           1 : HostDB_MatchEntry(const PLDHashEntryHdr *entry,
     415             :                   const void *key)
     416             : {
     417           1 :     const nsHostDBEnt *he = static_cast<const nsHostDBEnt *>(entry);
     418           1 :     const nsHostKey *hk = static_cast<const nsHostKey *>(key);
     419             : 
     420           1 :     return !strcmp(he->rec->host ? he->rec->host : "",
     421           2 :                    hk->host ? hk->host : "") &&
     422           2 :             RES_KEY_FLAGS (he->rec->flags) == RES_KEY_FLAGS(hk->flags) &&
     423           2 :             he->rec->af == hk->af &&
     424           3 :             !strcmp(he->rec->netInterface, hk->netInterface) &&
     425           2 :             !strcmp(he->rec->originSuffix, hk->originSuffix);
     426             : }
     427             : 
     428             : static void
     429           0 : HostDB_MoveEntry(PLDHashTable *table,
     430             :                  const PLDHashEntryHdr *from,
     431             :                  PLDHashEntryHdr *to)
     432             : {
     433           0 :     static_cast<nsHostDBEnt *>(to)->rec =
     434           0 :             static_cast<const nsHostDBEnt *>(from)->rec;
     435           0 : }
     436             : 
     437             : static void
     438           0 : HostDB_ClearEntry(PLDHashTable *table,
     439             :                   PLDHashEntryHdr *entry)
     440             : {
     441           0 :     nsHostDBEnt *he = static_cast<nsHostDBEnt*>(entry);
     442           0 :     MOZ_ASSERT(he, "nsHostDBEnt is null!");
     443             : 
     444           0 :     nsHostRecord *hr = he->rec;
     445           0 :     MOZ_ASSERT(hr, "nsHostDBEnt has null host record!");
     446             : 
     447           0 :     LOG(("Clearing cache db entry for host [%s%s%s].\n",
     448             :          LOG_HOST(hr->host, hr->netInterface)));
     449             : #if defined(DEBUG)
     450             :     {
     451           0 :         MutexAutoLock lock(hr->addr_info_lock);
     452           0 :         if (!hr->addr_info) {
     453           0 :             LOG(("No address info for host [%s%s%s].\n",
     454             :                  LOG_HOST(hr->host, hr->netInterface)));
     455             :         } else {
     456           0 :             if (!hr->mValidEnd.IsNull()) {
     457           0 :                 TimeDuration diff = hr->mValidEnd - TimeStamp::NowLoRes();
     458           0 :                 LOG(("Record for host [%s%s%s] expires in %f seconds.\n",
     459             :                      LOG_HOST(hr->host, hr->netInterface),
     460             :                      diff.ToSeconds()));
     461             :             } else {
     462           0 :                 LOG(("Record for host [%s%s%s] not yet valid.\n",
     463             :                      LOG_HOST(hr->host, hr->netInterface)));
     464             :             }
     465             : 
     466           0 :             NetAddrElement *addrElement = nullptr;
     467             :             char buf[kIPv6CStrBufSize];
     468           0 :             do {
     469           0 :                 if (!addrElement) {
     470           0 :                     addrElement = hr->addr_info->mAddresses.getFirst();
     471             :                 } else {
     472           0 :                     addrElement = addrElement->getNext();
     473             :                 }
     474             : 
     475           0 :                 if (addrElement) {
     476           0 :                     NetAddrToString(&addrElement->mAddress, buf, sizeof(buf));
     477           0 :                     LOG(("  [%s]\n", buf));
     478             :                 }
     479             :             }
     480           0 :             while (addrElement);
     481             :         }
     482             :     }
     483             : #endif
     484           0 :     NS_RELEASE(he->rec);
     485           0 : }
     486             : 
     487             : static void
     488           3 : HostDB_InitEntry(PLDHashEntryHdr *entry,
     489             :                  const void *key)
     490             : {
     491           3 :     nsHostDBEnt *he = static_cast<nsHostDBEnt *>(entry);
     492           3 :     nsHostRecord::Create(static_cast<const nsHostKey *>(key), &he->rec);
     493           3 : }
     494             : 
     495             : static const PLDHashTableOps gHostDB_ops =
     496             : {
     497             :     HostDB_HashKey,
     498             :     HostDB_MatchEntry,
     499             :     HostDB_MoveEntry,
     500             :     HostDB_ClearEntry,
     501             :     HostDB_InitEntry,
     502             : };
     503             : 
     504             : //----------------------------------------------------------------------------
     505             : 
     506             : #if TTL_AVAILABLE
     507             : static const char kPrefGetTtl[] = "network.dns.get-ttl";
     508             : static bool sGetTtlEnabled = false;
     509             : 
     510             : static void DnsPrefChanged(const char* aPref, void* aClosure)
     511             : {
     512             :     MOZ_ASSERT(NS_IsMainThread(),
     513             :                "Should be getting pref changed notification on main thread!");
     514             : 
     515             :     if (strcmp(aPref, kPrefGetTtl) != 0) {
     516             :         LOG(("DnsPrefChanged ignoring pref \"%s\"", aPref));
     517             :         return;
     518             :     }
     519             : 
     520             :     auto self = static_cast<nsHostResolver*>(aClosure);
     521             :     MOZ_ASSERT(self);
     522             : 
     523             :     sGetTtlEnabled = Preferences::GetBool(kPrefGetTtl);
     524             : }
     525             : #endif
     526             : 
     527           2 : nsHostResolver::nsHostResolver(uint32_t maxCacheEntries,
     528             :                                uint32_t defaultCacheEntryLifetime,
     529           2 :                                uint32_t defaultGracePeriod)
     530             :     : mMaxCacheEntries(maxCacheEntries)
     531             :     , mDefaultCacheLifetime(defaultCacheEntryLifetime)
     532             :     , mDefaultGracePeriod(defaultGracePeriod)
     533             :     , mLock("nsHostResolver.mLock")
     534             :     , mIdleThreadCV(mLock, "nsHostResolver.mIdleThreadCV")
     535             :     , mDB(&gHostDB_ops, sizeof(nsHostDBEnt), 0)
     536             :     , mEvictionQSize(0)
     537             :     , mShutdown(true)
     538             :     , mNumIdleThreads(0)
     539             :     , mThreadCount(0)
     540             :     , mActiveAnyThreadCount(0)
     541           2 :     , mPendingCount(0)
     542             : {
     543           2 :     mCreationTime = PR_Now();
     544           2 :     PR_INIT_CLIST(&mHighQ);
     545           2 :     PR_INIT_CLIST(&mMediumQ);
     546           2 :     PR_INIT_CLIST(&mLowQ);
     547           2 :     PR_INIT_CLIST(&mEvictionQ);
     548             : 
     549           2 :     mLongIdleTimeout  = PR_SecondsToInterval(LongIdleTimeoutSeconds);
     550           2 :     mShortIdleTimeout = PR_SecondsToInterval(ShortIdleTimeoutSeconds);
     551           2 : }
     552             : 
     553             : nsHostResolver::~nsHostResolver() = default;
     554             : 
     555             : nsresult
     556           2 : nsHostResolver::Init()
     557             : {
     558           2 :     if (NS_FAILED(GetAddrInfoInit())) {
     559           0 :         return NS_ERROR_FAILURE;
     560             :     }
     561             : 
     562           2 :     mShutdown = false;
     563             : 
     564             : #if TTL_AVAILABLE
     565             :     // The preferences probably haven't been loaded from the disk yet, so we
     566             :     // need to register a callback that will set up the experiment once they
     567             :     // are. We also need to explicitly set a value for the props otherwise the
     568             :     // callback won't be called.
     569             :     {
     570             :         DebugOnly<nsresult> rv = Preferences::RegisterCallbackAndCall(
     571             :             &DnsPrefChanged, kPrefGetTtl, this);
     572             :         NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
     573             :                              "Could not register DNS TTL pref callback.");
     574             :     }
     575             : #endif
     576             : 
     577             : #if defined(HAVE_RES_NINIT)
     578             :     // We want to make sure the system is using the correct resolver settings,
     579             :     // so we force it to reload those settings whenever we startup a subsequent
     580             :     // nsHostResolver instance.  We assume that there is no reason to do this
     581             :     // for the first nsHostResolver instance since that is usually created
     582             :     // during application startup.
     583             :     static int initCount = 0;
     584           2 :     if (initCount++ > 0) {
     585           1 :         LOG(("Calling 'res_ninit'.\n"));
     586           1 :         res_ninit(&_res);
     587             :     }
     588             : #endif
     589           2 :     return NS_OK;
     590             : }
     591             : 
     592             : void
     593           3 : nsHostResolver::ClearPendingQueue(PRCList *aPendingQ)
     594             : {
     595             :     // loop through pending queue, erroring out pending lookups.
     596           3 :     if (!PR_CLIST_IS_EMPTY(aPendingQ)) {
     597           0 :         PRCList *node = aPendingQ->next;
     598           0 :         while (node != aPendingQ) {
     599           0 :             nsHostRecord *rec = static_cast<nsHostRecord *>(node);
     600           0 :             node = node->next;
     601           0 :             OnLookupComplete(rec, NS_ERROR_ABORT, nullptr);
     602             :         }
     603             :     }
     604           3 : }
     605             : 
     606             : //
     607             : // FlushCache() is what we call when the network has changed. We must not
     608             : // trust names that were resolved before this change. They may resolve
     609             : // differently now.
     610             : //
     611             : // This function removes all existing resolved host entries from the hash.
     612             : // Names that are in the pending queues can be left there. Entries in the
     613             : // cache that have 'Resolve' set true but not 'onQueue' are being resolved
     614             : // right now, so we need to mark them to get re-resolved on completion!
     615             : 
     616             : void
     617           0 : nsHostResolver::FlushCache()
     618             : {
     619           0 :     MutexAutoLock lock(mLock);
     620           0 :     mEvictionQSize = 0;
     621             : 
     622             :     // Clear the evictionQ and remove all its corresponding entries from
     623             :     // the cache first
     624           0 :     if (!PR_CLIST_IS_EMPTY(&mEvictionQ)) {
     625           0 :         PRCList *node = mEvictionQ.next;
     626           0 :         while (node != &mEvictionQ) {
     627           0 :             nsHostRecord *rec = static_cast<nsHostRecord *>(node);
     628           0 :             node = node->next;
     629           0 :             PR_REMOVE_AND_INIT_LINK(rec);
     630           0 :             mDB.Remove((nsHostKey *) rec);
     631           0 :             NS_RELEASE(rec);
     632             :         }
     633             :     }
     634             : 
     635             :     // Refresh the cache entries that are resolving RIGHT now, remove the rest.
     636           0 :     for (auto iter = mDB.Iter(); !iter.Done(); iter.Next()) {
     637           0 :         auto entry = static_cast<nsHostDBEnt *>(iter.Get());
     638             :         // Try to remove the record, or mark it for refresh.
     639           0 :         if (entry->rec->RemoveOrRefresh()) {
     640           0 :             PR_REMOVE_LINK(entry->rec);
     641           0 :             iter.Remove();
     642             :         }
     643             :     }
     644           0 : }
     645             : 
     646             : void
     647           1 : nsHostResolver::Shutdown()
     648             : {
     649           1 :     LOG(("Shutting down host resolver.\n"));
     650             : 
     651             : #if TTL_AVAILABLE
     652             :     {
     653             :         DebugOnly<nsresult> rv = Preferences::UnregisterCallback(
     654             :             &DnsPrefChanged, kPrefGetTtl, this);
     655             :         NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
     656             :                              "Could not unregister DNS TTL pref callback.");
     657             :     }
     658             : #endif
     659             : 
     660             :     PRCList pendingQHigh, pendingQMed, pendingQLow, evictionQ;
     661           1 :     PR_INIT_CLIST(&pendingQHigh);
     662           1 :     PR_INIT_CLIST(&pendingQMed);
     663           1 :     PR_INIT_CLIST(&pendingQLow);
     664           1 :     PR_INIT_CLIST(&evictionQ);
     665             : 
     666             :     {
     667           2 :         MutexAutoLock lock(mLock);
     668             : 
     669           1 :         mShutdown = true;
     670             : 
     671           1 :         MoveCList(mHighQ, pendingQHigh);
     672           1 :         MoveCList(mMediumQ, pendingQMed);
     673           1 :         MoveCList(mLowQ, pendingQLow);
     674           1 :         MoveCList(mEvictionQ, evictionQ);
     675           1 :         mEvictionQSize = 0;
     676           1 :         mPendingCount = 0;
     677             : 
     678           1 :         if (mNumIdleThreads)
     679           0 :             mIdleThreadCV.NotifyAll();
     680             : 
     681             :         // empty host database
     682           1 :         mDB.Clear();
     683             :     }
     684             : 
     685           1 :     ClearPendingQueue(&pendingQHigh);
     686           1 :     ClearPendingQueue(&pendingQMed);
     687           1 :     ClearPendingQueue(&pendingQLow);
     688             : 
     689           1 :     if (!PR_CLIST_IS_EMPTY(&evictionQ)) {
     690           0 :         PRCList *node = evictionQ.next;
     691           0 :         while (node != &evictionQ) {
     692           0 :             nsHostRecord *rec = static_cast<nsHostRecord *>(node);
     693           0 :             node = node->next;
     694           0 :             NS_RELEASE(rec);
     695             :         }
     696             :     }
     697             : 
     698             : #ifdef NS_BUILD_REFCNT_LOGGING
     699             : 
     700             :     // Logically join the outstanding worker threads with a timeout.
     701             :     // Use this approach instead of PR_JoinThread() because that does
     702             :     // not allow a timeout which may be necessary for a semi-responsive
     703             :     // shutdown if the thread is blocked on a very slow DNS resolution.
     704             :     // mThreadCount is read outside of mLock, but the worst case
     705             :     // scenario for that race is one extra 25ms sleep.
     706             : 
     707           1 :     PRIntervalTime delay = PR_MillisecondsToInterval(25);
     708           1 :     PRIntervalTime stopTime = PR_IntervalNow() + PR_SecondsToInterval(20);
     709           1 :     while (mThreadCount && PR_IntervalNow() < stopTime)
     710           0 :         PR_Sleep(delay);
     711             : #endif
     712             : 
     713             :     {
     714           2 :         mozilla::DebugOnly<nsresult> rv = GetAddrInfoShutdown();
     715           1 :         NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
     716             :                              "Failed to shutdown GetAddrInfo");
     717             :     }
     718           1 : }
     719             : 
     720             : void
     721           0 : nsHostResolver::MoveQueue(nsHostRecord *aRec, PRCList &aDestQ)
     722             : {
     723           0 :     NS_ASSERTION(aRec->onQueue, "Moving Host Record Not Currently Queued");
     724             : 
     725           0 :     PR_REMOVE_LINK(aRec);
     726           0 :     PR_APPEND_LINK(aRec, &aDestQ);
     727           0 : }
     728             : 
     729             : nsresult
     730           3 : nsHostResolver::ResolveHost(const char             *host,
     731             :                             const OriginAttributes &aOriginAttributes,
     732             :                             uint16_t                flags,
     733             :                             uint16_t                af,
     734             :                             const char             *netInterface,
     735             :                             nsResolveHostCallback  *callback)
     736             : {
     737           3 :     NS_ENSURE_TRUE(host && *host, NS_ERROR_UNEXPECTED);
     738           3 :     NS_ENSURE_TRUE(netInterface, NS_ERROR_UNEXPECTED);
     739             : 
     740           3 :     LOG(("Resolving host [%s%s%s]%s.\n", LOG_HOST(host, netInterface),
     741             :          flags & RES_BYPASS_CACHE ? " - bypassing cache" : ""));
     742             : 
     743             :     // ensure that we are working with a valid hostname before proceeding.  see
     744             :     // bug 304904 for details.
     745           3 :     if (!net_IsValidHostName(nsDependentCString(host)))
     746           0 :         return NS_ERROR_UNKNOWN_HOST;
     747             : 
     748             :     // if result is set inside the lock, then we need to issue the
     749             :     // callback before returning.
     750           6 :     RefPtr<nsHostRecord> result;
     751           3 :     nsresult status = NS_OK, rv = NS_OK;
     752             :     {
     753           6 :         MutexAutoLock lock(mLock);
     754             : 
     755           3 :         if (mShutdown)
     756           0 :             rv = NS_ERROR_NOT_INITIALIZED;
     757             :         else {
     758             :             // Used to try to parse to an IP address literal.
     759             :             PRNetAddr tempAddr;
     760             :             // Unfortunately, PR_StringToNetAddr does not properly initialize
     761             :             // the output buffer in the case of IPv6 input. See bug 223145.
     762           3 :             memset(&tempAddr, 0, sizeof(PRNetAddr));
     763             : 
     764             :             // check to see if there is already an entry for this |host|
     765             :             // in the hash table.  if so, then check to see if we can't
     766             :             // just reuse the lookup result.  otherwise, if there are
     767             :             // any pending callbacks, then add to pending callbacks queue,
     768             :             // and return.  otherwise, add ourselves as first pending
     769             :             // callback, and proceed to do the lookup.
     770           6 :             nsAutoCString originSuffix;
     771           3 :             aOriginAttributes.CreateSuffix(originSuffix);
     772             : 
     773           3 :             nsHostKey key = { host, flags, af, netInterface, originSuffix.get() };
     774           3 :             auto he = static_cast<nsHostDBEnt*>(mDB.Add(&key, fallible));
     775             : 
     776             :             // if the record is null, the hash table OOM'd.
     777           3 :             if (!he) {
     778           0 :                 LOG(("  Out of memory: no cache entry for host [%s%s%s].\n",
     779             :                      LOG_HOST(host, netInterface)));
     780           0 :                 rv = NS_ERROR_OUT_OF_MEMORY;
     781             :             }
     782             :             // do we have a cached result that we can reuse?
     783          11 :             else if (!(flags & RES_BYPASS_CACHE) &&
     784           9 :                      he->rec->HasUsableResult(TimeStamp::NowLoRes(), flags)) {
     785           0 :                 LOG(("  Using cached record for host [%s%s%s].\n",
     786             :                      LOG_HOST(host, netInterface)));
     787             :                 // put reference to host record on stack...
     788           0 :                 result = he->rec;
     789           0 :                 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
     790             : 
     791             :                 // For entries that are in the grace period
     792             :                 // or all cached negative entries, use the cache but start a new
     793             :                 // lookup in the background
     794           0 :                 ConditionallyRefreshRecord(he->rec, host);
     795             : 
     796           0 :                 if (he->rec->negative) {
     797           0 :                     LOG(("  Negative cache entry for host [%s%s%s].\n",
     798             :                          LOG_HOST(host, netInterface)));
     799             :                     Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
     800           0 :                                           METHOD_NEGATIVE_HIT);
     801           0 :                     status = NS_ERROR_UNKNOWN_HOST;
     802             :                 }
     803             :             }
     804             :             // if the host name is an IP address literal and has been parsed,
     805             :             // go ahead and use it.
     806           3 :             else if (he->rec->addr) {
     807           0 :                 LOG(("  Using cached address for IP Literal [%s].\n", host));
     808             :                 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
     809           0 :                                       METHOD_LITERAL);
     810           0 :                 result = he->rec;
     811             :             }
     812             :             // try parsing the host name as an IP address literal to short
     813             :             // circuit full host resolution.  (this is necessary on some
     814             :             // platforms like Win9x.  see bug 219376 for more details.)
     815           3 :             else if (PR_StringToNetAddr(host, &tempAddr) == PR_SUCCESS) {
     816           1 :                 LOG(("  Host is IP Literal [%s].\n", host));
     817             :                 // ok, just copy the result into the host record, and be done
     818             :                 // with it! ;-)
     819           1 :                 he->rec->addr = new NetAddr();
     820           1 :                 PRNetAddrToNetAddr(&tempAddr, he->rec->addr);
     821             :                 // put reference to host record on stack...
     822             :                 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
     823           1 :                                       METHOD_LITERAL);
     824           1 :                 result = he->rec;
     825             :             }
     826           4 :             else if (mPendingCount >= MAX_NON_PRIORITY_REQUESTS &&
     827           2 :                      !IsHighPriority(flags) &&
     828           0 :                      !he->rec->resolving) {
     829           0 :                 LOG(("  Lookup queue full: dropping %s priority request for "
     830             :                      "host [%s%s%s].\n",
     831             :                      IsMediumPriority(flags) ? "medium" : "low",
     832             :                      LOG_HOST(host, netInterface)));
     833             :                 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
     834           0 :                                       METHOD_OVERFLOW);
     835             :                 // This is a lower priority request and we are swamped, so refuse it.
     836           0 :                 rv = NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
     837             :             }
     838           2 :             else if (flags & RES_OFFLINE) {
     839           0 :                 LOG(("  Offline request for host [%s%s%s]; ignoring.\n",
     840             :                      LOG_HOST(host, netInterface)));
     841           0 :                 rv = NS_ERROR_OFFLINE;
     842             :             }
     843             : 
     844             :             // If this is an IPV4 or IPV6 specific request, check if there is
     845             :             // an AF_UNSPEC entry we can use. Otherwise, hit the resolver...
     846           2 :             else if (!he->rec->resolving) {
     847           2 :                 if (!(flags & RES_BYPASS_CACHE) &&
     848           1 :                     ((af == PR_AF_INET) || (af == PR_AF_INET6))) {
     849             :                     // First, search for an entry with AF_UNSPEC
     850             :                     const nsHostKey unspecKey = { host, flags, PR_AF_UNSPEC,
     851           1 :                                                   netInterface, originSuffix.get() };
     852             :                     auto unspecHe =
     853           1 :                         static_cast<nsHostDBEnt*>(mDB.Search(&unspecKey));
     854           1 :                     NS_ASSERTION(!unspecHe ||
     855             :                                  (unspecHe && unspecHe->rec),
     856             :                                 "Valid host entries should contain a record");
     857           1 :                     TimeStamp now = TimeStamp::NowLoRes();
     858           2 :                     if (unspecHe &&
     859           1 :                         unspecHe->rec->HasUsableResult(now, flags)) {
     860             : 
     861           1 :                         MOZ_ASSERT(unspecHe->rec->addr_info || unspecHe->rec->negative,
     862             :                                    "Entry should be resolved or negative.");
     863             : 
     864           1 :                         LOG(("  Trying AF_UNSPEC entry for host [%s%s%s] af: %s.\n",
     865             :                              LOG_HOST(host, netInterface),
     866             :                              (af == PR_AF_INET) ? "AF_INET" : "AF_INET6"));
     867             : 
     868           1 :                         he->rec->addr_info = nullptr;
     869           1 :                         if (unspecHe->rec->negative) {
     870           0 :                             he->rec->negative = unspecHe->rec->negative;
     871           0 :                             he->rec->CopyExpirationTimesAndFlagsFrom(unspecHe->rec);
     872           1 :                         } else if (unspecHe->rec->addr_info) {
     873             :                             // Search for any valid address in the AF_UNSPEC entry
     874             :                             // in the cache (not blacklisted and from the right
     875             :                             // family).
     876             :                             NetAddrElement *addrIter =
     877           1 :                                 unspecHe->rec->addr_info->mAddresses.getFirst();
     878           3 :                             while (addrIter) {
     879           2 :                                 if ((af == addrIter->mAddress.inet.family) &&
     880           1 :                                      !unspecHe->rec->Blacklisted(&addrIter->mAddress)) {
     881           1 :                                     if (!he->rec->addr_info) {
     882           1 :                                         he->rec->addr_info = new AddrInfo(
     883           1 :                                             unspecHe->rec->addr_info->mHostName,
     884           2 :                                             unspecHe->rec->addr_info->mCanonicalName);
     885           1 :                                         he->rec->CopyExpirationTimesAndFlagsFrom(unspecHe->rec);
     886             :                                     }
     887           1 :                                     he->rec->addr_info->AddAddress(
     888           2 :                                         new NetAddrElement(*addrIter));
     889             :                                 }
     890           1 :                                 addrIter = addrIter->getNext();
     891             :                             }
     892             :                         }
     893             :                         // Now check if we have a new record.
     894           1 :                         if (he->rec->HasUsableResult(now, flags)) {
     895           1 :                             result = he->rec;
     896           1 :                             if (he->rec->negative) {
     897           0 :                                 status = NS_ERROR_UNKNOWN_HOST;
     898             :                             }
     899             :                             Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
     900           1 :                                                   METHOD_HIT);
     901           1 :                             ConditionallyRefreshRecord(he->rec, host);
     902             :                         }
     903             :                         // For AF_INET6, a new lookup means another AF_UNSPEC
     904             :                         // lookup. We have already iterated through the
     905             :                         // AF_UNSPEC addresses, so we mark this record as
     906             :                         // negative.
     907           0 :                         else if (af == PR_AF_INET6) {
     908           0 :                             LOG(("  No AF_INET6 in AF_UNSPEC entry: "
     909             :                                  "host [%s%s%s] unknown host.",
     910             :                                  LOG_HOST(host, netInterface)));
     911           0 :                             result = he->rec;
     912           0 :                             he->rec->negative = true;
     913           0 :                             status = NS_ERROR_UNKNOWN_HOST;
     914             :                             Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
     915           0 :                                                   METHOD_NEGATIVE_HIT);
     916             :                         }
     917             :                     }
     918             :                 }
     919             :                 // If no valid address was found in the cache or this is an
     920             :                 // AF_UNSPEC request, then start a new lookup.
     921           2 :                 if (!result) {
     922           1 :                     LOG(("  No usable address in cache for host [%s%s%s].",
     923             :                          LOG_HOST(host, netInterface)));
     924             : 
     925             :                     // Add callback to the list of pending callbacks.
     926           1 :                     PR_APPEND_LINK(callback, &he->rec->callbacks);
     927           1 :                     he->rec->flags = flags;
     928           1 :                     rv = IssueLookup(he->rec);
     929             :                     Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
     930           1 :                                           METHOD_NETWORK_FIRST);
     931           1 :                     if (NS_FAILED(rv)) {
     932           0 :                         PR_REMOVE_AND_INIT_LINK(callback);
     933             :                     }
     934             :                     else {
     935           1 :                         LOG(("  DNS lookup for host [%s%s%s] blocking "
     936             :                              "pending 'getaddrinfo' query: callback [%p]",
     937             :                              LOG_HOST(host, netInterface), callback));
     938             :                     }
     939             :                 }
     940             :             }
     941             :             else {
     942           0 :                 LOG(("  Host [%s%s%s] is being resolved. Appending callback "
     943             :                      "[%p].", LOG_HOST(host, netInterface), callback));
     944             : 
     945           0 :                 PR_APPEND_LINK(callback, &he->rec->callbacks);
     946           0 :                 if (he->rec->onQueue) {
     947             :                     Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
     948           0 :                                           METHOD_NETWORK_SHARED);
     949             : 
     950             :                     // Consider the case where we are on a pending queue of
     951             :                     // lower priority than the request is being made at.
     952             :                     // In that case we should upgrade to the higher queue.
     953             : 
     954           0 :                     if (IsHighPriority(flags) &&
     955           0 :                         !IsHighPriority(he->rec->flags)) {
     956             :                         // Move from (low|med) to high.
     957           0 :                         MoveQueue(he->rec, mHighQ);
     958           0 :                         he->rec->flags = flags;
     959           0 :                         ConditionallyCreateThread(he->rec);
     960           0 :                     } else if (IsMediumPriority(flags) &&
     961           0 :                                IsLowPriority(he->rec->flags)) {
     962             :                         // Move from low to med.
     963           0 :                         MoveQueue(he->rec, mMediumQ);
     964           0 :                         he->rec->flags = flags;
     965           0 :                         mIdleThreadCV.Notify();
     966             :                     }
     967             :                 }
     968             :             }
     969             :         }
     970             :     }
     971           3 :     if (result) {
     972           2 :         callback->OnLookupComplete(this, result, status);
     973             :     }
     974             : 
     975           3 :     return rv;
     976             : }
     977             : 
     978             : void
     979           0 : nsHostResolver::DetachCallback(const char             *host,
     980             :                                const OriginAttributes &aOriginAttributes,
     981             :                                uint16_t                flags,
     982             :                                uint16_t                af,
     983             :                                const char             *netInterface,
     984             :                                nsResolveHostCallback  *callback,
     985             :                                nsresult                status)
     986             : {
     987           0 :     RefPtr<nsHostRecord> rec;
     988             :     {
     989           0 :         MutexAutoLock lock(mLock);
     990             : 
     991           0 :         nsAutoCString originSuffix;
     992           0 :         aOriginAttributes.CreateSuffix(originSuffix);
     993             : 
     994           0 :         nsHostKey key = { host, flags, af, netInterface, originSuffix.get() };
     995           0 :         auto he = static_cast<nsHostDBEnt*>(mDB.Search(&key));
     996           0 :         if (he) {
     997             :             // walk list looking for |callback|... we cannot assume
     998             :             // that it will be there!
     999           0 :             PRCList *node = he->rec->callbacks.next;
    1000           0 :             while (node != &he->rec->callbacks) {
    1001           0 :                 if (static_cast<nsResolveHostCallback *>(node) == callback) {
    1002           0 :                     PR_REMOVE_LINK(callback);
    1003           0 :                     rec = he->rec;
    1004           0 :                     break;
    1005             :                 }
    1006           0 :                 node = node->next;
    1007             :             }
    1008             :         }
    1009             :     }
    1010             : 
    1011             :     // complete callback with the given status code; this would only be done if
    1012             :     // the record was in the process of being resolved.
    1013           0 :     if (rec)
    1014           0 :         callback->OnLookupComplete(this, rec, status);
    1015           0 : }
    1016             : 
    1017             : nsresult
    1018           1 : nsHostResolver::ConditionallyCreateThread(nsHostRecord *rec)
    1019             : {
    1020           1 :     if (mNumIdleThreads) {
    1021             :         // wake up idle thread to process this lookup
    1022           0 :         mIdleThreadCV.Notify();
    1023             :     }
    1024           2 :     else if ((mThreadCount < HighThreadThreshold) ||
    1025           0 :              (IsHighPriority(rec->flags) && mThreadCount < MAX_RESOLVER_THREADS)) {
    1026             :         // dispatch new worker thread
    1027           1 :         NS_ADDREF_THIS(); // owning reference passed to thread
    1028             : 
    1029           1 :         mThreadCount++;
    1030             :         PRThread *thr = PR_CreateThread(PR_SYSTEM_THREAD,
    1031             :                                         ThreadFunc,
    1032             :                                         this,
    1033             :                                         PR_PRIORITY_NORMAL,
    1034             :                                         PR_GLOBAL_THREAD,
    1035             :                                         PR_UNJOINABLE_THREAD,
    1036           1 :                                         0);
    1037           1 :         if (!thr) {
    1038           0 :             mThreadCount--;
    1039           0 :             NS_RELEASE_THIS();
    1040           0 :             return NS_ERROR_OUT_OF_MEMORY;
    1041             :         }
    1042             :     }
    1043             :     else {
    1044           0 :         LOG(("  Unable to find a thread for looking up host [%s%s%s].\n",
    1045             :              LOG_HOST(rec->host, rec->netInterface)));
    1046             :     }
    1047           1 :     return NS_OK;
    1048             : }
    1049             : 
    1050             : nsresult
    1051           1 : nsHostResolver::IssueLookup(nsHostRecord *rec)
    1052             : {
    1053           1 :     nsresult rv = NS_OK;
    1054           1 :     NS_ASSERTION(!rec->resolving, "record is already being resolved");
    1055             : 
    1056             :     // Add rec to one of the pending queues, possibly removing it from mEvictionQ.
    1057             :     // If rec is on mEvictionQ, then we can just move the owning
    1058             :     // reference over to the new active queue.
    1059           1 :     if (rec->next == rec)
    1060           1 :         NS_ADDREF(rec);
    1061             :     else {
    1062           0 :         PR_REMOVE_LINK(rec);
    1063           0 :         mEvictionQSize--;
    1064             :     }
    1065             : 
    1066           1 :     switch (nsHostRecord::GetPriority(rec->flags)) {
    1067             :         case nsHostRecord::DNS_PRIORITY_HIGH:
    1068           1 :             PR_APPEND_LINK(rec, &mHighQ);
    1069           1 :             break;
    1070             : 
    1071             :         case nsHostRecord::DNS_PRIORITY_MEDIUM:
    1072           0 :             PR_APPEND_LINK(rec, &mMediumQ);
    1073           0 :             break;
    1074             : 
    1075             :         case nsHostRecord::DNS_PRIORITY_LOW:
    1076           0 :             PR_APPEND_LINK(rec, &mLowQ);
    1077           0 :             break;
    1078             :     }
    1079           1 :     mPendingCount++;
    1080             : 
    1081           1 :     rec->resolving = true;
    1082           1 :     rec->onQueue = true;
    1083             : 
    1084           1 :     rv = ConditionallyCreateThread(rec);
    1085             : 
    1086           1 :     LOG (("  DNS thread counters: total=%d any-live=%d idle=%d pending=%d\n",
    1087             :           static_cast<uint32_t>(mThreadCount),
    1088             :           static_cast<uint32_t>(mActiveAnyThreadCount),
    1089             :           static_cast<uint32_t>(mNumIdleThreads),
    1090             :           static_cast<uint32_t>(mPendingCount)));
    1091             : 
    1092           1 :     return rv;
    1093             : }
    1094             : 
    1095             : nsresult
    1096           1 : nsHostResolver::ConditionallyRefreshRecord(nsHostRecord *rec, const char *host)
    1097             : {
    1098           3 :     if ((rec->CheckExpiration(TimeStamp::NowLoRes()) != nsHostRecord::EXP_VALID
    1099           3 :             || rec->negative) && !rec->resolving) {
    1100           0 :         LOG(("  Using %s cache entry for host [%s] but starting async renewal.",
    1101             :             rec->negative ? "negative" :"positive", host));
    1102           0 :         IssueLookup(rec);
    1103             : 
    1104           0 :         if (!rec->negative) {
    1105             :             // negative entries are constantly being refreshed, only
    1106             :             // track positive grace period induced renewals
    1107             :             Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
    1108           0 :                 METHOD_RENEWAL);
    1109             :         }
    1110             :     }
    1111           1 :     return NS_OK;
    1112             : }
    1113             : 
    1114             : void
    1115           1 : nsHostResolver::DeQueue(PRCList &aQ, nsHostRecord **aResult)
    1116             : {
    1117           1 :     *aResult = static_cast<nsHostRecord *>(aQ.next);
    1118           1 :     PR_REMOVE_AND_INIT_LINK(*aResult);
    1119           1 :     mPendingCount--;
    1120           1 :     (*aResult)->onQueue = false;
    1121           1 : }
    1122             : 
    1123             : bool
    1124           2 : nsHostResolver::GetHostToLookup(nsHostRecord **result)
    1125             : {
    1126           2 :     bool timedOut = false;
    1127             :     PRIntervalTime epoch, now, timeout;
    1128             : 
    1129           3 :     MutexAutoLock lock(mLock);
    1130             : 
    1131           2 :     timeout = (mNumIdleThreads >= HighThreadThreshold) ? mShortIdleTimeout : mLongIdleTimeout;
    1132           2 :     epoch = PR_IntervalNow();
    1133             : 
    1134           2 :     while (!mShutdown) {
    1135             :         // remove next record from Q; hand over owning reference. Check high, then med, then low
    1136             : 
    1137             : #if TTL_AVAILABLE
    1138             :         #define SET_GET_TTL(var, val) \
    1139             :             (var)->mGetTtl = sGetTtlEnabled && (val)
    1140             : #else
    1141             :         #define SET_GET_TTL(var, val)
    1142             : #endif
    1143             : 
    1144           2 :         if (!PR_CLIST_IS_EMPTY(&mHighQ)) {
    1145           1 :             DeQueue (mHighQ, result);
    1146             :             SET_GET_TTL(*result, false);
    1147           1 :             return true;
    1148             :         }
    1149             : 
    1150           1 :         if (mActiveAnyThreadCount < HighThreadThreshold) {
    1151           1 :             if (!PR_CLIST_IS_EMPTY(&mMediumQ)) {
    1152           0 :                 DeQueue (mMediumQ, result);
    1153           0 :                 mActiveAnyThreadCount++;
    1154           0 :                 (*result)->usingAnyThread = true;
    1155             :                 SET_GET_TTL(*result, true);
    1156           0 :                 return true;
    1157             :             }
    1158             : 
    1159           1 :             if (!PR_CLIST_IS_EMPTY(&mLowQ)) {
    1160           0 :                 DeQueue (mLowQ, result);
    1161           0 :                 mActiveAnyThreadCount++;
    1162           0 :                 (*result)->usingAnyThread = true;
    1163             :                 SET_GET_TTL(*result, true);
    1164           0 :                 return true;
    1165             :             }
    1166             :         }
    1167             : 
    1168             :         // Determining timeout is racy, so allow one cycle through checking the queues
    1169             :         // before exiting.
    1170           1 :         if (timedOut)
    1171           0 :             break;
    1172             : 
    1173             :         // wait for one or more of the following to occur:
    1174             :         //  (1) the pending queue has a host record to process
    1175             :         //  (2) the shutdown flag has been set
    1176             :         //  (3) the thread has been idle for too long
    1177             : 
    1178           1 :         mNumIdleThreads++;
    1179           1 :         mIdleThreadCV.Wait(timeout);
    1180           0 :         mNumIdleThreads--;
    1181             : 
    1182           0 :         now = PR_IntervalNow();
    1183             : 
    1184           0 :         if ((PRIntervalTime)(now - epoch) >= timeout)
    1185           0 :             timedOut = true;
    1186             :         else {
    1187             :             // It is possible that PR_WaitCondVar() was interrupted and returned early,
    1188             :             // in which case we will loop back and re-enter it. In that case we want to
    1189             :             // do so with the new timeout reduced to reflect time already spent waiting.
    1190           0 :             timeout -= (PRIntervalTime)(now - epoch);
    1191           0 :             epoch = now;
    1192             :         }
    1193             :     }
    1194             : 
    1195             :     // tell thread to exit...
    1196           0 :     return false;
    1197             : }
    1198             : 
    1199             : void
    1200           1 : nsHostResolver::PrepareRecordExpiration(nsHostRecord* rec) const
    1201             : {
    1202           1 :     MOZ_ASSERT(((bool)rec->addr_info) != rec->negative);
    1203           1 :     if (!rec->addr_info) {
    1204           0 :         rec->SetExpiration(TimeStamp::NowLoRes(),
    1205           0 :                            NEGATIVE_RECORD_LIFETIME, 0);
    1206           0 :         LOG(("Caching host [%s%s%s] negative record for %u seconds.\n",
    1207             :              LOG_HOST(rec->host, rec->netInterface),
    1208             :              NEGATIVE_RECORD_LIFETIME));
    1209           0 :         return;
    1210             :     }
    1211             : 
    1212           1 :     unsigned int lifetime = mDefaultCacheLifetime;
    1213           1 :     unsigned int grace = mDefaultGracePeriod;
    1214             : #if TTL_AVAILABLE
    1215             :     unsigned int ttl = mDefaultCacheLifetime;
    1216             :     if (sGetTtlEnabled) {
    1217             :         MutexAutoLock lock(rec->addr_info_lock);
    1218             :         if (rec->addr_info && rec->addr_info->ttl != AddrInfo::NO_TTL_DATA) {
    1219             :             ttl = rec->addr_info->ttl;
    1220             :         }
    1221             :         lifetime = ttl;
    1222             :         grace = 0;
    1223             :     }
    1224             : #endif
    1225             : 
    1226           1 :     rec->SetExpiration(TimeStamp::NowLoRes(), lifetime, grace);
    1227           1 :     LOG(("Caching host [%s%s%s] record for %u seconds (grace %d).",
    1228             :          LOG_HOST(rec->host, rec->netInterface), lifetime, grace));
    1229             : }
    1230             : 
    1231             : static bool
    1232           1 : different_rrset(AddrInfo *rrset1, AddrInfo *rrset2)
    1233             : {
    1234           1 :     if (!rrset1 || !rrset2) {
    1235           1 :         return true;
    1236             :     }
    1237             : 
    1238           0 :     LOG(("different_rrset %s\n", rrset1->mHostName));
    1239           0 :     nsTArray<NetAddr> orderedSet1;
    1240           0 :     nsTArray<NetAddr> orderedSet2;
    1241             : 
    1242           0 :     for (NetAddrElement *element = rrset1->mAddresses.getFirst();
    1243           0 :          element; element = element->getNext()) {
    1244           0 :         if (LOG_ENABLED()) {
    1245             :             char buf[128];
    1246           0 :             NetAddrToString(&element->mAddress, buf, 128);
    1247           0 :             LOG(("different_rrset add to set 1 %s\n", buf));
    1248             :         }
    1249           0 :         orderedSet1.InsertElementAt(orderedSet1.Length(), element->mAddress);
    1250             :     }
    1251             : 
    1252           0 :     for (NetAddrElement *element = rrset2->mAddresses.getFirst();
    1253           0 :          element; element = element->getNext()) {
    1254           0 :         if (LOG_ENABLED()) {
    1255             :             char buf[128];
    1256           0 :             NetAddrToString(&element->mAddress, buf, 128);
    1257           0 :             LOG(("different_rrset add to set 2 %s\n", buf));
    1258             :         }
    1259           0 :         orderedSet2.InsertElementAt(orderedSet2.Length(), element->mAddress);
    1260             :     }
    1261             : 
    1262           0 :     if (orderedSet1.Length() != orderedSet2.Length()) {
    1263           0 :         LOG(("different_rrset true due to length change\n"));
    1264           0 :         return true;
    1265             :     }
    1266           0 :     orderedSet1.Sort();
    1267           0 :     orderedSet2.Sort();
    1268             : 
    1269           0 :     for (uint32_t i = 0; i < orderedSet1.Length(); ++i) {
    1270           0 :         if (!(orderedSet1[i] == orderedSet2[i])) {
    1271           0 :             LOG(("different_rrset true due to content change\n"));
    1272           0 :             return true;
    1273             :         }
    1274             :     }
    1275           0 :     LOG(("different_rrset false\n"));
    1276           0 :     return false;
    1277             : }
    1278             : 
    1279             : //
    1280             : // OnLookupComplete() checks if the resolving should be redone and if so it
    1281             : // returns LOOKUP_RESOLVEAGAIN, but only if 'status' is not NS_ERROR_ABORT.
    1282             : // takes ownership of AddrInfo parameter
    1283             : nsHostResolver::LookupStatus
    1284           1 : nsHostResolver::OnLookupComplete(nsHostRecord* rec, nsresult status, AddrInfo* newRRSet)
    1285             : {
    1286             :     // get the list of pending callbacks for this lookup, and notify
    1287             :     // them that the lookup is complete.
    1288             :     PRCList cbs;
    1289           1 :     PR_INIT_CLIST(&cbs);
    1290             :     {
    1291           2 :         MutexAutoLock lock(mLock);
    1292             : 
    1293           1 :         if (rec->mResolveAgain && (status != NS_ERROR_ABORT)) {
    1294           0 :             LOG(("nsHostResolver record %p resolve again due to flushcache\n", rec));
    1295           0 :             rec->mResolveAgain = false;
    1296           0 :             delete newRRSet;
    1297           0 :             return LOOKUP_RESOLVEAGAIN;
    1298             :         }
    1299             : 
    1300             :         // grab list of callbacks to notify
    1301           1 :         MoveCList(rec->callbacks, cbs);
    1302             : 
    1303             :         // update record fields.  We might have a rec->addr_info already if a
    1304             :         // previous lookup result expired and we're reresolving it..
    1305             :         AddrInfo  *old_addr_info;
    1306             :         {
    1307           2 :             MutexAutoLock lock(rec->addr_info_lock);
    1308           1 :             if (different_rrset(rec->addr_info, newRRSet)) {
    1309           1 :                 LOG(("nsHostResolver record %p new gencnt\n", rec));
    1310           1 :                 old_addr_info = rec->addr_info;
    1311           1 :                 rec->addr_info = newRRSet;
    1312           1 :                 rec->addr_info_gencnt++;
    1313             :             } else {
    1314           0 :                 if (rec->addr_info && newRRSet) {
    1315           0 :                     rec->addr_info->ttl = newRRSet->ttl;
    1316             :                 }
    1317           0 :                 old_addr_info = newRRSet;
    1318             :             }
    1319             :         }
    1320           1 :         delete old_addr_info;
    1321             : 
    1322           1 :         rec->negative = !rec->addr_info;
    1323           1 :         PrepareRecordExpiration(rec);
    1324           1 :         rec->resolving = false;
    1325             : 
    1326           1 :         if (rec->usingAnyThread) {
    1327           0 :             mActiveAnyThreadCount--;
    1328           0 :             rec->usingAnyThread = false;
    1329             :         }
    1330             : 
    1331           1 :         if (!mShutdown) {
    1332             :             // add to mEvictionQ
    1333           1 :             PR_APPEND_LINK(rec, &mEvictionQ);
    1334           1 :             NS_ADDREF(rec);
    1335           1 :             if (mEvictionQSize < mMaxCacheEntries)
    1336           1 :                 mEvictionQSize++;
    1337             :             else {
    1338             :                 // remove first element on mEvictionQ
    1339             :                 nsHostRecord *head =
    1340           0 :                     static_cast<nsHostRecord *>(PR_LIST_HEAD(&mEvictionQ));
    1341           0 :                 PR_REMOVE_AND_INIT_LINK(head);
    1342           0 :                 mDB.Remove((nsHostKey *) head);
    1343             : 
    1344           0 :                 if (!head->negative) {
    1345             :                     // record the age of the entry upon eviction.
    1346           0 :                     TimeDuration age = TimeStamp::NowLoRes() - head->mValidStart;
    1347           0 :                     Telemetry::Accumulate(Telemetry::DNS_CLEANUP_AGE,
    1348           0 :                                           static_cast<uint32_t>(age.ToSeconds() / 60));
    1349             :                 }
    1350             : 
    1351             :                 // release reference to rec owned by mEvictionQ
    1352           0 :                 NS_RELEASE(head);
    1353             :             }
    1354             : #if TTL_AVAILABLE
    1355             :             if (!rec->mGetTtl && !rec->resolving && sGetTtlEnabled) {
    1356             :                 LOG(("Issuing second async lookup for TTL for host [%s%s%s].",
    1357             :                      LOG_HOST(rec->host, rec->netInterface)));
    1358             :                 rec->flags =
    1359             :                   (rec->flags & ~RES_PRIORITY_MEDIUM) | RES_PRIORITY_LOW;
    1360             :                 DebugOnly<nsresult> rv = IssueLookup(rec);
    1361             :                 NS_WARNING_ASSERTION(
    1362             :                     NS_SUCCEEDED(rv),
    1363             :                     "Could not issue second async lookup for TTL.");
    1364             :             }
    1365             : #endif
    1366             :         }
    1367             :     }
    1368             : 
    1369           1 :     if (!PR_CLIST_IS_EMPTY(&cbs)) {
    1370           1 :         PRCList *node = cbs.next;
    1371           3 :         while (node != &cbs) {
    1372             :             nsResolveHostCallback *callback =
    1373           1 :                     static_cast<nsResolveHostCallback *>(node);
    1374           1 :             node = node->next;
    1375           1 :             callback->OnLookupComplete(this, rec, status);
    1376             :             // NOTE: callback must not be dereferenced after this point!!
    1377             :         }
    1378             :     }
    1379             : 
    1380           1 :     NS_RELEASE(rec);
    1381             : 
    1382           1 :     return LOOKUP_OK;
    1383             : }
    1384             : 
    1385             : void
    1386           0 : nsHostResolver::CancelAsyncRequest(const char             *host,
    1387             :                                    const OriginAttributes &aOriginAttributes,
    1388             :                                    uint16_t                flags,
    1389             :                                    uint16_t                af,
    1390             :                                    const char             *netInterface,
    1391             :                                    nsIDNSListener         *aListener,
    1392             :                                    nsresult                status)
    1393             : 
    1394             : {
    1395           0 :     MutexAutoLock lock(mLock);
    1396             : 
    1397           0 :     nsAutoCString originSuffix;
    1398           0 :     aOriginAttributes.CreateSuffix(originSuffix);
    1399             : 
    1400             :     // Lookup the host record associated with host, flags & address family
    1401           0 :     nsHostKey key = { host, flags, af, netInterface, originSuffix.get() };
    1402           0 :     auto he = static_cast<nsHostDBEnt*>(mDB.Search(&key));
    1403           0 :     if (he) {
    1404           0 :         nsHostRecord* recPtr = nullptr;
    1405           0 :         PRCList *node = he->rec->callbacks.next;
    1406             :         // Remove the first nsDNSAsyncRequest callback which matches the
    1407             :         // supplied listener object
    1408           0 :         while (node != &he->rec->callbacks) {
    1409             :             nsResolveHostCallback *callback
    1410           0 :                 = static_cast<nsResolveHostCallback *>(node);
    1411           0 :             if (callback && (callback->EqualsAsyncListener(aListener))) {
    1412             :                 // Remove from the list of callbacks
    1413           0 :                 PR_REMOVE_LINK(callback);
    1414           0 :                 recPtr = he->rec;
    1415           0 :                 callback->OnLookupComplete(this, recPtr, status);
    1416           0 :                 break;
    1417             :             }
    1418           0 :             node = node->next;
    1419             :         }
    1420             : 
    1421             :         // If there are no more callbacks, remove the hash table entry
    1422           0 :         if (recPtr && PR_CLIST_IS_EMPTY(&recPtr->callbacks)) {
    1423           0 :             mDB.Remove((nsHostKey *)recPtr);
    1424             :             // If record is on a Queue, remove it and then deref it
    1425           0 :             if (recPtr->next != recPtr) {
    1426           0 :                 PR_REMOVE_LINK(recPtr);
    1427           0 :                 NS_RELEASE(recPtr);
    1428             :             }
    1429             :         }
    1430             :     }
    1431           0 : }
    1432             : 
    1433             : size_t
    1434           0 : nsHostResolver::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
    1435             : {
    1436           0 :     MutexAutoLock lock(mLock);
    1437             : 
    1438           0 :     size_t n = mallocSizeOf(this);
    1439             : 
    1440           0 :     n += mDB.ShallowSizeOfExcludingThis(mallocSizeOf);
    1441           0 :     for (auto iter = mDB.ConstIter(); !iter.Done(); iter.Next()) {
    1442           0 :         auto entry = static_cast<nsHostDBEnt*>(iter.Get());
    1443           0 :         n += entry->rec->SizeOfIncludingThis(mallocSizeOf);
    1444             :     }
    1445             : 
    1446             :     // The following fields aren't measured.
    1447             :     // - mHighQ, mMediumQ, mLowQ, mEvictionQ, because they just point to
    1448             :     //   nsHostRecords that also pointed to by entries |mDB|, and measured when
    1449             :     //   |mDB| is measured.
    1450             : 
    1451           0 :     return n;
    1452             : }
    1453             : 
    1454             : void
    1455           1 : nsHostResolver::ThreadFunc(void *arg)
    1456             : {
    1457             :     char stackTop;
    1458             : 
    1459           1 :     LOG(("DNS lookup thread - starting execution.\n"));
    1460             : 
    1461           1 :     static nsThreadPoolNaming naming;
    1462           1 :     nsCString name = naming.GetNextThreadName("DNS Resolver");
    1463             : 
    1464           1 :     NS_SetCurrentThreadName(name.BeginReading());
    1465           1 :     profiler_register_thread(name.BeginReading(), &stackTop);
    1466             : 
    1467             : #if defined(RES_RETRY_ON_FAILURE)
    1468           1 :     nsResState rs;
    1469             : #endif
    1470           1 :     nsHostResolver *resolver = (nsHostResolver *)arg;
    1471           1 :     nsHostRecord *rec  = nullptr;
    1472           1 :     AddrInfo *ai = nullptr;
    1473             : 
    1474           3 :     while (rec || resolver->GetHostToLookup(&rec)) {
    1475           1 :         LOG(("DNS lookup thread - Calling getaddrinfo for host [%s%s%s].\n",
    1476             :              LOG_HOST(rec->host, rec->netInterface)));
    1477             : 
    1478           1 :         TimeStamp startTime = TimeStamp::Now();
    1479             : #if TTL_AVAILABLE
    1480             :         bool getTtl = rec->mGetTtl;
    1481             : #else
    1482           1 :         bool getTtl = false;
    1483             : #endif
    1484             : 
    1485           1 :         nsresult status = GetAddrInfo(rec->host, rec->af, rec->flags, rec->netInterface,
    1486           1 :                                       &ai, getTtl);
    1487             : #if defined(RES_RETRY_ON_FAILURE)
    1488           1 :         if (NS_FAILED(status) && rs.Reset()) {
    1489           0 :             status = GetAddrInfo(rec->host, rec->af, rec->flags, rec->netInterface, &ai,
    1490           0 :                                  getTtl);
    1491             :         }
    1492             : #endif
    1493             : 
    1494             :         {   // obtain lock to check shutdown and manage inter-module telemetry
    1495           2 :             MutexAutoLock lock(resolver->mLock);
    1496             : 
    1497           1 :             if (!resolver->mShutdown) {
    1498           1 :                 TimeDuration elapsed = TimeStamp::Now() - startTime;
    1499           1 :                 uint32_t millis = static_cast<uint32_t>(elapsed.ToMilliseconds());
    1500             : 
    1501           1 :                 if (NS_SUCCEEDED(status)) {
    1502             :                     Telemetry::HistogramID histogramID;
    1503           1 :                     if (!rec->addr_info_gencnt) {
    1504             :                         // Time for initial lookup.
    1505           1 :                         histogramID = Telemetry::DNS_LOOKUP_TIME;
    1506           0 :                     } else if (!getTtl) {
    1507             :                         // Time for renewal; categorized by expiration strategy.
    1508           0 :                         histogramID = Telemetry::DNS_RENEWAL_TIME;
    1509             :                     } else {
    1510             :                         // Time to get TTL; categorized by expiration strategy.
    1511           0 :                         histogramID = Telemetry::DNS_RENEWAL_TIME_FOR_TTL;
    1512             :                     }
    1513           1 :                     Telemetry::Accumulate(histogramID, millis);
    1514             :                 } else {
    1515           0 :                     Telemetry::Accumulate(Telemetry::DNS_FAILED_LOOKUP_TIME, millis);
    1516             :                 }
    1517             :             }
    1518             :         }
    1519             : 
    1520             :         // OnLookupComplete may release "rec", long before we lose it.
    1521           1 :         LOG(("DNS lookup thread - lookup completed for host [%s%s%s]: %s.\n",
    1522             :              LOG_HOST(rec->host, rec->netInterface),
    1523             :              ai ? "success" : "failure: unknown host"));
    1524             : 
    1525           1 :         if (LOOKUP_RESOLVEAGAIN == resolver->OnLookupComplete(rec, status, ai)) {
    1526             :             // leave 'rec' assigned and loop to make a renewed host resolve
    1527           0 :             LOG(("DNS lookup thread - Re-resolving host [%s%s%s].\n",
    1528             :                  LOG_HOST(rec->host, rec->netInterface)));
    1529             :         } else {
    1530           1 :             rec = nullptr;
    1531             :         }
    1532             :     }
    1533           0 :     resolver->mThreadCount--;
    1534           0 :     NS_RELEASE(resolver);
    1535           0 :     LOG(("DNS lookup thread - queue empty, thread finished.\n"));
    1536             : 
    1537           0 :     profiler_unregister_thread();
    1538           0 : }
    1539             : 
    1540             : nsresult
    1541           2 : nsHostResolver::Create(uint32_t maxCacheEntries,
    1542             :                        uint32_t defaultCacheEntryLifetime,
    1543             :                        uint32_t defaultGracePeriod,
    1544             :                        nsHostResolver **result)
    1545             : {
    1546             :     auto *res = new nsHostResolver(maxCacheEntries, defaultCacheEntryLifetime,
    1547           2 :                                              defaultGracePeriod);
    1548           2 :     NS_ADDREF(res);
    1549             : 
    1550           2 :     nsresult rv = res->Init();
    1551           2 :     if (NS_FAILED(rv))
    1552           0 :         NS_RELEASE(res);
    1553             : 
    1554           2 :     *result = res;
    1555           2 :     return rv;
    1556             : }
    1557             : 
    1558             : void
    1559           0 : nsHostResolver::GetDNSCacheEntries(nsTArray<DNSCacheEntries> *args)
    1560             : {
    1561           0 :     for (auto iter = mDB.Iter(); !iter.Done(); iter.Next()) {
    1562             :         // We don't pay attention to address literals, only resolved domains.
    1563             :         // Also require a host.
    1564           0 :         auto entry = static_cast<nsHostDBEnt*>(iter.Get());
    1565           0 :         nsHostRecord* rec = entry->rec;
    1566           0 :         MOZ_ASSERT(rec, "rec should never be null here!");
    1567           0 :         if (!rec || !rec->addr_info || !rec->host) {
    1568           0 :             continue;
    1569             :         }
    1570             : 
    1571           0 :         DNSCacheEntries info;
    1572           0 :         info.hostname = rec->host;
    1573           0 :         info.family = rec->af;
    1574           0 :         info.netInterface = rec->netInterface;
    1575           0 :         info.expiration =
    1576           0 :             (int64_t)(rec->mValidEnd - TimeStamp::NowLoRes()).ToSeconds();
    1577           0 :         if (info.expiration <= 0) {
    1578             :             // We only need valid DNS cache entries
    1579           0 :             continue;
    1580             :         }
    1581             : 
    1582             :         {
    1583           0 :             MutexAutoLock lock(rec->addr_info_lock);
    1584             : 
    1585           0 :             NetAddr *addr = nullptr;
    1586           0 :             NetAddrElement *addrElement = rec->addr_info->mAddresses.getFirst();
    1587           0 :             if (addrElement) {
    1588           0 :                 addr = &addrElement->mAddress;
    1589             :             }
    1590           0 :             while (addr) {
    1591             :                 char buf[kIPv6CStrBufSize];
    1592           0 :                 if (NetAddrToString(addr, buf, sizeof(buf))) {
    1593           0 :                     info.hostaddr.AppendElement(buf);
    1594             :                 }
    1595           0 :                 addr = nullptr;
    1596           0 :                 addrElement = addrElement->getNext();
    1597           0 :                 if (addrElement) {
    1598           0 :                     addr = &addrElement->mAddress;
    1599             :                 }
    1600             :             }
    1601             :         }
    1602             : 
    1603           0 :         args->AppendElement(info);
    1604             :     }
    1605           0 : }

Generated by: LCOV version 1.13