netwerk/dns/nsHostResolver.cpp

Wed, 31 Dec 2014 06:55:46 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:46 +0100
changeset 1
ca08bd8f51b2
permissions
-rw-r--r--

Added tag TORBROWSER_REPLICA for changeset 6474c204b198

     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/. */
     6 #if defined(MOZ_LOGGING)
     7 #define FORCE_PR_LOG
     8 #endif
    10 #if defined(HAVE_RES_NINIT)
    11 #include <sys/types.h>
    12 #include <netinet/in.h>
    13 #include <arpa/inet.h>   
    14 #include <arpa/nameser.h>
    15 #include <resolv.h>
    16 #define RES_RETRY_ON_FAILURE
    17 #endif
    19 #include <stdlib.h>
    20 #include "nsHostResolver.h"
    21 #include "nsError.h"
    22 #include "nsISupportsBase.h"
    23 #include "nsISupportsUtils.h"
    24 #include "nsAutoPtr.h"
    25 #include "prthread.h"
    26 #include "prerror.h"
    27 #include "prtime.h"
    28 #include "prlog.h"
    29 #include "pldhash.h"
    30 #include "plstr.h"
    31 #include "nsURLHelper.h"
    32 #include "nsThreadUtils.h"
    34 #include "mozilla/HashFunctions.h"
    35 #include "mozilla/TimeStamp.h"
    36 #include "mozilla/Telemetry.h"
    37 #include "mozilla/VisualEventTracer.h"
    39 using namespace mozilla;
    40 using namespace mozilla::net;
    42 //----------------------------------------------------------------------------
    44 // Use a persistent thread pool in order to avoid spinning up new threads all the time.
    45 // In particular, thread creation results in a res_init() call from libc which is 
    46 // quite expensive.
    47 //
    48 // The pool dynamically grows between 0 and MAX_RESOLVER_THREADS in size. New requests
    49 // go first to an idle thread. If that cannot be found and there are fewer than MAX_RESOLVER_THREADS
    50 // currently in the pool a new thread is created for high priority requests. If
    51 // the new request is at a lower priority a new thread will only be created if 
    52 // there are fewer than HighThreadThreshold currently outstanding. If a thread cannot be
    53 // created or an idle thread located for the request it is queued.
    54 //
    55 // When the pool is greater than HighThreadThreshold in size a thread will be destroyed after
    56 // ShortIdleTimeoutSeconds of idle time. Smaller pools use LongIdleTimeoutSeconds for a 
    57 // timeout period.
    59 #define HighThreadThreshold     MAX_RESOLVER_THREADS_FOR_ANY_PRIORITY
    60 #define LongIdleTimeoutSeconds  300           // for threads 1 -> HighThreadThreshold
    61 #define ShortIdleTimeoutSeconds 60            // for threads HighThreadThreshold+1 -> MAX_RESOLVER_THREADS
    63 PR_STATIC_ASSERT (HighThreadThreshold <= MAX_RESOLVER_THREADS);
    65 //----------------------------------------------------------------------------
    67 #if defined(PR_LOGGING)
    68 static PRLogModuleInfo *gHostResolverLog = nullptr;
    69 #define LOG(args) PR_LOG(gHostResolverLog, PR_LOG_DEBUG, args)
    70 #else
    71 #define LOG(args)
    72 #endif
    74 //----------------------------------------------------------------------------
    76 static inline void
    77 MoveCList(PRCList &from, PRCList &to)
    78 {
    79     if (!PR_CLIST_IS_EMPTY(&from)) {
    80         to.next = from.next;
    81         to.prev = from.prev;
    82         to.next->prev = &to;
    83         to.prev->next = &to;
    84         PR_INIT_CLIST(&from);
    85     }             
    86 }
    88 //----------------------------------------------------------------------------
    90 #if defined(RES_RETRY_ON_FAILURE)
    92 // this class represents the resolver state for a given thread.  if we
    93 // encounter a lookup failure, then we can invoke the Reset method on an
    94 // instance of this class to reset the resolver (in case /etc/resolv.conf
    95 // for example changed).  this is mainly an issue on GNU systems since glibc
    96 // only reads in /etc/resolv.conf once per thread.  it may be an issue on
    97 // other systems as well.
    99 class nsResState
   100 {
   101 public:
   102     nsResState()
   103         // initialize mLastReset to the time when this object
   104         // is created.  this means that a reset will not occur
   105         // if a thread is too young.  the alternative would be
   106         // to initialize this to the beginning of time, so that
   107         // the first failure would cause a reset, but since the
   108         // thread would have just started up, it likely would
   109         // already have current /etc/resolv.conf info.
   110         : mLastReset(PR_IntervalNow())
   111     {
   112     }
   114     bool Reset()
   115     {
   116         // reset no more than once per second
   117         if (PR_IntervalToSeconds(PR_IntervalNow() - mLastReset) < 1)
   118             return false;
   120         LOG(("Calling 'res_ninit'.\n"));
   122         mLastReset = PR_IntervalNow();
   123         return (res_ninit(&_res) == 0);
   124     }
   126 private:
   127     PRIntervalTime mLastReset;
   128 };
   130 #endif // RES_RETRY_ON_FAILURE
   132 //----------------------------------------------------------------------------
   134 static inline bool
   135 IsHighPriority(uint16_t flags)
   136 {
   137     return !(flags & (nsHostResolver::RES_PRIORITY_LOW | nsHostResolver::RES_PRIORITY_MEDIUM));
   138 }
   140 static inline bool
   141 IsMediumPriority(uint16_t flags)
   142 {
   143     return flags & nsHostResolver::RES_PRIORITY_MEDIUM;
   144 }
   146 static inline bool
   147 IsLowPriority(uint16_t flags)
   148 {
   149     return flags & nsHostResolver::RES_PRIORITY_LOW;
   150 }
   152 //----------------------------------------------------------------------------
   154 // this macro filters out any flags that are not used when constructing the
   155 // host key.  the significant flags are those that would affect the resulting
   156 // host record (i.e., the flags that are passed down to PR_GetAddrInfoByName).
   157 #define RES_KEY_FLAGS(_f) ((_f) & nsHostResolver::RES_CANON_NAME)
   159 nsHostRecord::nsHostRecord(const nsHostKey *key)
   160     : addr_info_lock("nsHostRecord.addr_info_lock")
   161     , addr_info_gencnt(0)
   162     , addr_info(nullptr)
   163     , addr(nullptr)
   164     , negative(false)
   165     , resolving(false)
   166     , onQueue(false)
   167     , usingAnyThread(false)
   168     , mDoomed(false)
   169 {
   170     host = ((char *) this) + sizeof(nsHostRecord);
   171     memcpy((char *) host, key->host, strlen(key->host) + 1);
   172     flags = key->flags;
   173     af = key->af;
   175     expiration = TimeStamp::NowLoRes();
   177     PR_INIT_CLIST(this);
   178     PR_INIT_CLIST(&callbacks);
   179 }
   181 nsresult
   182 nsHostRecord::Create(const nsHostKey *key, nsHostRecord **result)
   183 {
   184     size_t hostLen = strlen(key->host) + 1;
   185     size_t size = hostLen + sizeof(nsHostRecord);
   187     // Use placement new to create the object with room for the hostname
   188     // allocated after it.
   189     void *place = ::operator new(size);
   190     *result = new(place) nsHostRecord(key);
   191     NS_ADDREF(*result);
   193     MOZ_EVENT_TRACER_NAME_OBJECT(*result, key->host);
   195     return NS_OK;
   196 }
   198 nsHostRecord::~nsHostRecord()
   199 {
   200     delete addr_info;
   201     delete addr;
   202 }
   204 bool
   205 nsHostRecord::Blacklisted(NetAddr *aQuery)
   206 {
   207     // must call locked
   208     LOG(("Checking blacklist for host [%s], host record [%p].\n", host, this));
   210     // skip the string conversion for the common case of no blacklist
   211     if (!mBlacklistedItems.Length()) {
   212         return false;
   213     }
   215     char buf[kIPv6CStrBufSize];
   216     if (!NetAddrToString(aQuery, buf, sizeof(buf))) {
   217         return false;
   218     }
   219     nsDependentCString strQuery(buf);
   221     for (uint32_t i = 0; i < mBlacklistedItems.Length(); i++) {
   222         if (mBlacklistedItems.ElementAt(i).Equals(strQuery)) {
   223             LOG(("Address [%s] is blacklisted for host [%s].\n", buf, host));
   224             return true;
   225         }
   226     }
   228     return false;
   229 }
   231 void
   232 nsHostRecord::ReportUnusable(NetAddr *aAddress)
   233 {
   234     // must call locked
   235     LOG(("Adding address to blacklist for host [%s], host record [%p].\n", host, this));
   237     if (negative)
   238         mDoomed = true;
   240     char buf[kIPv6CStrBufSize];
   241     if (NetAddrToString(aAddress, buf, sizeof(buf))) {
   242         LOG(("Successfully adding address [%s] to blacklist for host [%s].\n", buf, host));
   243         mBlacklistedItems.AppendElement(nsCString(buf));
   244     }
   245 }
   247 void
   248 nsHostRecord::ResetBlacklist()
   249 {
   250     // must call locked
   251     LOG(("Resetting blacklist for host [%s], host record [%p].\n", host, this));
   252     mBlacklistedItems.Clear();
   253 }
   255 bool
   256 nsHostRecord::HasUsableResult(uint16_t queryFlags) const
   257 {
   258     if (mDoomed)
   259         return false;
   261     // don't use cached negative results for high priority queries.
   262     if (negative && IsHighPriority(queryFlags))
   263         return false;
   265     return addr_info || addr || negative;
   266 }
   268 static size_t
   269 SizeOfResolveHostCallbackListExcludingHead(const PRCList *head,
   270                                            MallocSizeOf mallocSizeOf)
   271 {
   272     size_t n = 0;
   273     PRCList *curr = head->next;
   274     while (curr != head) {
   275         nsResolveHostCallback *callback =
   276             static_cast<nsResolveHostCallback*>(curr);
   277         n += callback->SizeOfIncludingThis(mallocSizeOf);
   278         curr = curr->next;
   279     }
   280     return n;
   281 }
   283 size_t
   284 nsHostRecord::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
   285 {
   286     size_t n = mallocSizeOf(this);
   288     // The |host| field (inherited from nsHostKey) actually points to extra
   289     // memory that is allocated beyond the end of the nsHostRecord (see
   290     // nsHostRecord::Create()).  So it will be included in the
   291     // |mallocSizeOf(this)| call above.
   293     n += SizeOfResolveHostCallbackListExcludingHead(&callbacks, mallocSizeOf);
   294     n += addr_info ? addr_info->SizeOfIncludingThis(mallocSizeOf) : 0;
   295     n += mallocSizeOf(addr);
   297     n += mBlacklistedItems.SizeOfExcludingThis(mallocSizeOf);
   298     for (size_t i = 0; i < mBlacklistedItems.Length(); i++) {
   299         n += mBlacklistedItems[i].SizeOfExcludingThisMustBeUnshared(mallocSizeOf);
   300     }
   301     return n;
   302 }
   304 //----------------------------------------------------------------------------
   306 struct nsHostDBEnt : PLDHashEntryHdr
   307 {
   308     nsHostRecord *rec;
   309 };
   311 static PLDHashNumber
   312 HostDB_HashKey(PLDHashTable *table, const void *key)
   313 {
   314     const nsHostKey *hk = static_cast<const nsHostKey *>(key);
   315     return AddToHash(HashString(hk->host), RES_KEY_FLAGS(hk->flags), hk->af);
   316 }
   318 static bool
   319 HostDB_MatchEntry(PLDHashTable *table,
   320                   const PLDHashEntryHdr *entry,
   321                   const void *key)
   322 {
   323     const nsHostDBEnt *he = static_cast<const nsHostDBEnt *>(entry);
   324     const nsHostKey *hk = static_cast<const nsHostKey *>(key); 
   326     return !strcmp(he->rec->host, hk->host) &&
   327             RES_KEY_FLAGS (he->rec->flags) == RES_KEY_FLAGS(hk->flags) &&
   328             he->rec->af == hk->af;
   329 }
   331 static void
   332 HostDB_MoveEntry(PLDHashTable *table,
   333                  const PLDHashEntryHdr *from,
   334                  PLDHashEntryHdr *to)
   335 {
   336     static_cast<nsHostDBEnt *>(to)->rec =
   337             static_cast<const nsHostDBEnt *>(from)->rec;
   338 }
   340 static void
   341 HostDB_ClearEntry(PLDHashTable *table,
   342                   PLDHashEntryHdr *entry)
   343 {
   344     nsHostDBEnt *he = static_cast<nsHostDBEnt*>(entry);
   345     MOZ_ASSERT(he, "nsHostDBEnt is null!");
   347     nsHostRecord *hr = he->rec;
   348     MOZ_ASSERT(hr, "nsHostDBEnt has null host record!");
   350     LOG(("Clearing cache db entry for host [%s].\n", hr->host));
   351 #if defined(DEBUG) && defined(PR_LOGGING)
   352     {
   353         MutexAutoLock lock(hr->addr_info_lock);
   354         if (!hr->addr_info) {
   355             LOG(("No address info for host [%s].\n", hr->host));
   356         } else {
   357             TimeDuration diff = hr->expiration - TimeStamp::NowLoRes();
   358             LOG(("Record for [%s] expires in %f seconds.\n", hr->host, diff.ToSeconds()));
   360             NetAddrElement *addrElement = nullptr;
   361             char buf[kIPv6CStrBufSize];
   362             do {
   363                 if (!addrElement) {
   364                     addrElement = hr->addr_info->mAddresses.getFirst();
   365                 } else {
   366                     addrElement = addrElement->getNext();
   367                 }
   369                 if (addrElement) {
   370                     NetAddrToString(&addrElement->mAddress, buf, sizeof(buf));
   371                     LOG(("  [%s]\n", buf));
   372                 }
   373             }
   374             while (addrElement);
   375         }
   376     }
   377 #endif
   378     NS_RELEASE(he->rec);
   379 }
   381 static bool
   382 HostDB_InitEntry(PLDHashTable *table,
   383                  PLDHashEntryHdr *entry,
   384                  const void *key)
   385 {
   386     nsHostDBEnt *he = static_cast<nsHostDBEnt *>(entry);
   387     nsHostRecord::Create(static_cast<const nsHostKey *>(key), &he->rec);
   388     return true;
   389 }
   391 static const PLDHashTableOps gHostDB_ops =
   392 {
   393     PL_DHashAllocTable,
   394     PL_DHashFreeTable,
   395     HostDB_HashKey,
   396     HostDB_MatchEntry,
   397     HostDB_MoveEntry,
   398     HostDB_ClearEntry,
   399     PL_DHashFinalizeStub,
   400     HostDB_InitEntry,
   401 };
   403 static PLDHashOperator
   404 HostDB_RemoveEntry(PLDHashTable *table,
   405                    PLDHashEntryHdr *hdr,
   406                    uint32_t number,
   407                    void *arg)
   408 {
   409     return PL_DHASH_REMOVE;
   410 }
   412 //----------------------------------------------------------------------------
   414 nsHostResolver::nsHostResolver(uint32_t maxCacheEntries,
   415                                uint32_t maxCacheLifetime,
   416                                uint32_t lifetimeGracePeriod)
   417     : mMaxCacheEntries(maxCacheEntries)
   418     , mMaxCacheLifetime(TimeDuration::FromSeconds(maxCacheLifetime))
   419     , mGracePeriod(TimeDuration::FromSeconds(lifetimeGracePeriod))
   420     , mLock("nsHostResolver.mLock")
   421     , mIdleThreadCV(mLock, "nsHostResolver.mIdleThreadCV")
   422     , mNumIdleThreads(0)
   423     , mThreadCount(0)
   424     , mActiveAnyThreadCount(0)
   425     , mEvictionQSize(0)
   426     , mPendingCount(0)
   427     , mShutdown(true)
   428 {
   429     mCreationTime = PR_Now();
   430     PR_INIT_CLIST(&mHighQ);
   431     PR_INIT_CLIST(&mMediumQ);
   432     PR_INIT_CLIST(&mLowQ);
   433     PR_INIT_CLIST(&mEvictionQ);
   435     mLongIdleTimeout  = PR_SecondsToInterval(LongIdleTimeoutSeconds);
   436     mShortIdleTimeout = PR_SecondsToInterval(ShortIdleTimeoutSeconds);
   437 }
   439 nsHostResolver::~nsHostResolver()
   440 {
   441     PL_DHashTableFinish(&mDB);
   442 }
   444 nsresult
   445 nsHostResolver::Init()
   446 {
   447     PL_DHashTableInit(&mDB, &gHostDB_ops, nullptr, sizeof(nsHostDBEnt), 0);
   449     mShutdown = false;
   451 #if defined(HAVE_RES_NINIT)
   452     // We want to make sure the system is using the correct resolver settings,
   453     // so we force it to reload those settings whenever we startup a subsequent
   454     // nsHostResolver instance.  We assume that there is no reason to do this
   455     // for the first nsHostResolver instance since that is usually created
   456     // during application startup.
   457     static int initCount = 0;
   458     if (initCount++ > 0) {
   459         LOG(("Calling 'res_ninit'.\n"));
   460         res_ninit(&_res);
   461     }
   462 #endif
   463     return NS_OK;
   464 }
   466 void
   467 nsHostResolver::ClearPendingQueue(PRCList *aPendingQ)
   468 {
   469     // loop through pending queue, erroring out pending lookups.
   470     if (!PR_CLIST_IS_EMPTY(aPendingQ)) {
   471         PRCList *node = aPendingQ->next;
   472         while (node != aPendingQ) {
   473             nsHostRecord *rec = static_cast<nsHostRecord *>(node);
   474             node = node->next;
   475             OnLookupComplete(rec, NS_ERROR_ABORT, nullptr);
   476         }
   477     }
   478 }
   480 void
   481 nsHostResolver::Shutdown()
   482 {
   483     LOG(("Shutting down host resolver.\n"));
   485     PRCList pendingQHigh, pendingQMed, pendingQLow, evictionQ;
   486     PR_INIT_CLIST(&pendingQHigh);
   487     PR_INIT_CLIST(&pendingQMed);
   488     PR_INIT_CLIST(&pendingQLow);
   489     PR_INIT_CLIST(&evictionQ);
   491     {
   492         MutexAutoLock lock(mLock);
   494         mShutdown = true;
   496         MoveCList(mHighQ, pendingQHigh);
   497         MoveCList(mMediumQ, pendingQMed);
   498         MoveCList(mLowQ, pendingQLow);
   499         MoveCList(mEvictionQ, evictionQ);
   500         mEvictionQSize = 0;
   501         mPendingCount = 0;
   503         if (mNumIdleThreads)
   504             mIdleThreadCV.NotifyAll();
   506         // empty host database
   507         PL_DHashTableEnumerate(&mDB, HostDB_RemoveEntry, nullptr);
   508     }
   510     ClearPendingQueue(&pendingQHigh);
   511     ClearPendingQueue(&pendingQMed);
   512     ClearPendingQueue(&pendingQLow);
   514     if (!PR_CLIST_IS_EMPTY(&evictionQ)) {
   515         PRCList *node = evictionQ.next;
   516         while (node != &evictionQ) {
   517             nsHostRecord *rec = static_cast<nsHostRecord *>(node);
   518             node = node->next;
   519             NS_RELEASE(rec);
   520         }
   521     }
   523 #ifdef NS_BUILD_REFCNT_LOGGING
   525     // Logically join the outstanding worker threads with a timeout.
   526     // Use this approach instead of PR_JoinThread() because that does 
   527     // not allow a timeout which may be necessary for a semi-responsive 
   528     // shutdown if the thread is blocked on a very slow DNS resolution.
   529     // mThreadCount is read outside of mLock, but the worst case 
   530     // scenario for that race is one extra 25ms sleep.
   532     PRIntervalTime delay = PR_MillisecondsToInterval(25);
   533     PRIntervalTime stopTime = PR_IntervalNow() + PR_SecondsToInterval(20);
   534     while (mThreadCount && PR_IntervalNow() < stopTime)
   535         PR_Sleep(delay);
   536 #endif
   537 }
   539 void 
   540 nsHostResolver::MoveQueue(nsHostRecord *aRec, PRCList &aDestQ)
   541 {
   542     NS_ASSERTION(aRec->onQueue, "Moving Host Record Not Currently Queued");
   544     PR_REMOVE_LINK(aRec);
   545     PR_APPEND_LINK(aRec, &aDestQ);
   546 }
   548 nsresult
   549 nsHostResolver::ResolveHost(const char            *host,
   550                             uint16_t               flags,
   551                             uint16_t               af,
   552                             nsResolveHostCallback *callback)
   553 {
   554     NS_ENSURE_TRUE(host && *host, NS_ERROR_UNEXPECTED);
   556     LOG(("Resolving host [%s]%s.\n",
   557          host, flags & RES_BYPASS_CACHE ? " - bypassing cache" : ""));
   559     // ensure that we are working with a valid hostname before proceeding.  see
   560     // bug 304904 for details.
   561     if (!net_IsValidHostName(nsDependentCString(host)))
   562         return NS_ERROR_UNKNOWN_HOST;
   564     // if result is set inside the lock, then we need to issue the
   565     // callback before returning.
   566     nsRefPtr<nsHostRecord> result;
   567     nsresult status = NS_OK, rv = NS_OK;
   568     {
   569         MutexAutoLock lock(mLock);
   571         if (mShutdown)
   572             rv = NS_ERROR_NOT_INITIALIZED;
   573         else {
   574             // Used to try to parse to an IP address literal.
   575             PRNetAddr tempAddr;
   576             // Unfortunately, PR_StringToNetAddr does not properly initialize
   577             // the output buffer in the case of IPv6 input. See bug 223145.
   578             memset(&tempAddr, 0, sizeof(PRNetAddr));
   580             // check to see if there is already an entry for this |host|
   581             // in the hash table.  if so, then check to see if we can't
   582             // just reuse the lookup result.  otherwise, if there are
   583             // any pending callbacks, then add to pending callbacks queue,
   584             // and return.  otherwise, add ourselves as first pending
   585             // callback, and proceed to do the lookup.
   587             nsHostKey key = { host, flags, af };
   588             nsHostDBEnt *he = static_cast<nsHostDBEnt *>
   589                                          (PL_DHashTableOperate(&mDB, &key, PL_DHASH_ADD));
   591             // if the record is null, then HostDB_InitEntry failed.
   592             if (!he || !he->rec) {
   593                 LOG(("  Out of memory: no cache entry for [%s].\n", host));
   594                 rv = NS_ERROR_OUT_OF_MEMORY;
   595             }
   596             // do we have a cached result that we can reuse?
   597             else if (!(flags & RES_BYPASS_CACHE) &&
   598                      he->rec->HasUsableResult(flags) &&
   599                      TimeStamp::NowLoRes() <= (he->rec->expiration + mGracePeriod)) {
   600                 LOG(("  Using cached record for host [%s].\n", host));
   601                 // put reference to host record on stack...
   602                 result = he->rec;
   603                 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
   605                 // For entries that are in the grace period
   606                 // or all cached negative entries, use the cache but start a new
   607                 // lookup in the background
   608                 ConditionallyRefreshRecord(he->rec, host);
   610                 if (he->rec->negative) {
   611                     LOG(("  Negative cache entry for[%s].\n", host));
   612                     Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
   613                                           METHOD_NEGATIVE_HIT);
   614                     status = NS_ERROR_UNKNOWN_HOST;
   615                 }
   616             }
   617             // if the host name is an IP address literal and has been parsed,
   618             // go ahead and use it.
   619             else if (he->rec->addr) {
   620                 LOG(("  Using cached address for IP Literal [%s].\n", host));
   621                 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
   622                                       METHOD_LITERAL);
   623                 result = he->rec;
   624             }
   625             // try parsing the host name as an IP address literal to short
   626             // circuit full host resolution.  (this is necessary on some
   627             // platforms like Win9x.  see bug 219376 for more details.)
   628             else if (PR_StringToNetAddr(host, &tempAddr) == PR_SUCCESS) {
   629                 LOG(("  Host is IP Literal [%s].\n", host));
   630                 // ok, just copy the result into the host record, and be done
   631                 // with it! ;-)
   632                 he->rec->addr = new NetAddr();
   633                 PRNetAddrToNetAddr(&tempAddr, he->rec->addr);
   634                 // put reference to host record on stack...
   635                 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
   636                                       METHOD_LITERAL);
   637                 result = he->rec;
   638             }
   639             else if (mPendingCount >= MAX_NON_PRIORITY_REQUESTS &&
   640                      !IsHighPriority(flags) &&
   641                      !he->rec->resolving) {
   642                 LOG(("  Lookup queue full: dropping %s priority request for "
   643                      "[%s].\n",
   644                      IsMediumPriority(flags) ? "medium" : "low", host));
   645                 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
   646                                       METHOD_OVERFLOW);
   647                 // This is a lower priority request and we are swamped, so refuse it.
   648                 rv = NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
   649             }
   650             else if (flags & RES_OFFLINE) {
   651                 LOG(("  Offline request for [%s]; ignoring.\n", host));
   652                 rv = NS_ERROR_OFFLINE;
   653             }
   655             // If this is an IPV4 or IPV6 specific request, check if there is
   656             // an AF_UNSPEC entry we can use. Otherwise, hit the resolver...
   657             else if (!he->rec->resolving) {
   658                 if (!(flags & RES_BYPASS_CACHE) &&
   659                     ((af == PR_AF_INET) || (af == PR_AF_INET6))) {
   660                     // First, search for an entry with AF_UNSPEC
   661                     const nsHostKey unspecKey = { host, flags, PR_AF_UNSPEC };
   662                     nsHostDBEnt *unspecHe = static_cast<nsHostDBEnt *>
   663                         (PL_DHashTableOperate(&mDB, &unspecKey, PL_DHASH_LOOKUP));
   664                     NS_ASSERTION(PL_DHASH_ENTRY_IS_FREE(unspecHe) ||
   665                                  (PL_DHASH_ENTRY_IS_BUSY(unspecHe) &&
   666                                   unspecHe->rec),
   667                                 "Valid host entries should contain a record");
   668                     if (PL_DHASH_ENTRY_IS_BUSY(unspecHe) &&
   669                         unspecHe->rec &&
   670                         unspecHe->rec->HasUsableResult(flags) &&
   671                         TimeStamp::NowLoRes() <= (he->rec->expiration + mGracePeriod)) {
   673                         MOZ_ASSERT(unspecHe->rec->addr_info || unspecHe->rec->negative,
   674                                    "Entry should be resolved or negative.");
   676                         LOG(("  Trying AF_UNSPEC entry for [%s] af: %s.\n",
   677                             host, (af == PR_AF_INET) ? "AF_INET" : "AF_INET6"));
   679                         he->rec->addr_info = nullptr;
   680                         if (unspecHe->rec->negative) {
   681                             he->rec->negative = unspecHe->rec->negative;
   682                         } else if (unspecHe->rec->addr_info) {
   683                             // Search for any valid address in the AF_UNSPEC entry
   684                             // in the cache (not blacklisted and from the right
   685                             // family).
   686                             NetAddrElement *addrIter =
   687                                 unspecHe->rec->addr_info->mAddresses.getFirst();
   688                             while (addrIter) {
   689                                 if ((af == addrIter->mAddress.inet.family) &&
   690                                      !unspecHe->rec->Blacklisted(&addrIter->mAddress)) {
   691                                     if (!he->rec->addr_info) {
   692                                         he->rec->addr_info = new AddrInfo(
   693                                             unspecHe->rec->addr_info->mHostName,
   694                                             unspecHe->rec->addr_info->mCanonicalName);
   695                                     }
   696                                     he->rec->addr_info->AddAddress(
   697                                         new NetAddrElement(*addrIter));
   698                                 }
   699                                 addrIter = addrIter->getNext();
   700                             }
   701                         }
   702                         if (he->rec->HasUsableResult(flags)) {
   703                             result = he->rec;
   704                             Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
   705                                                   METHOD_HIT);
   706                             ConditionallyRefreshRecord(he->rec, host);
   707                         }
   708                         // For AF_INET6, a new lookup means another AF_UNSPEC
   709                         // lookup. We have already iterated through the
   710                         // AF_UNSPEC addresses, so we mark this record as
   711                         // negative.
   712                         else if (af == PR_AF_INET6) {
   713                             LOG(("  No AF_INET6 in AF_UNSPEC entry: "
   714                                  "[%s] unknown host", host));
   715                             result = he->rec;
   716                             he->rec->negative = true;
   717                             status = NS_ERROR_UNKNOWN_HOST;
   718                             Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
   719                                                   METHOD_NEGATIVE_HIT);
   720                         }
   721                     }
   722                 }
   723                 // If no valid address was found in the cache or this is an
   724                 // AF_UNSPEC request, then start a new lookup.
   725                 if (!result) {
   726                     LOG(("  No usable address in cache for [%s]", host));
   727                     // Add callback to the list of pending callbacks.
   728                     PR_APPEND_LINK(callback, &he->rec->callbacks);
   729                     he->rec->flags = flags;
   730                     rv = IssueLookup(he->rec);
   731                     Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
   732                                           METHOD_NETWORK_FIRST);
   733                     if (NS_FAILED(rv)) {
   734                         PR_REMOVE_AND_INIT_LINK(callback);
   735                     }
   736                     else {
   737                         LOG(("  DNS lookup for host [%s] blocking pending "
   738                              "'getaddrinfo' query: callback [%p]",
   739                              host, callback));
   740                     }
   741                 }
   742             }
   743             else {
   744                 LOG(("  Host [%s] is being resolved. Appending callback [%p].",
   745                      host, callback));
   746                 PR_APPEND_LINK(callback, &he->rec->callbacks);
   747                 if (he->rec->onQueue) {
   748                     Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
   749                                           METHOD_NETWORK_SHARED);
   751                     // Consider the case where we are on a pending queue of
   752                     // lower priority than the request is being made at.
   753                     // In that case we should upgrade to the higher queue.
   755                     if (IsHighPriority(flags) &&
   756                         !IsHighPriority(he->rec->flags)) {
   757                         // Move from (low|med) to high.
   758                         MoveQueue(he->rec, mHighQ);
   759                         he->rec->flags = flags;
   760                         ConditionallyCreateThread(he->rec);
   761                     } else if (IsMediumPriority(flags) &&
   762                                IsLowPriority(he->rec->flags)) {
   763                         // Move from low to med.
   764                         MoveQueue(he->rec, mMediumQ);
   765                         he->rec->flags = flags;
   766                         mIdleThreadCV.Notify();
   767                     }
   768                 }
   769             }
   770         }
   771     }
   772     if (result)
   773         callback->OnLookupComplete(this, result, status);
   774     return rv;
   775 }
   777 void
   778 nsHostResolver::DetachCallback(const char            *host,
   779                                uint16_t               flags,
   780                                uint16_t               af,
   781                                nsResolveHostCallback *callback,
   782                                nsresult               status)
   783 {
   784     nsRefPtr<nsHostRecord> rec;
   785     {
   786         MutexAutoLock lock(mLock);
   788         nsHostKey key = { host, flags, af };
   789         nsHostDBEnt *he = static_cast<nsHostDBEnt *>
   790                                      (PL_DHashTableOperate(&mDB, &key, PL_DHASH_LOOKUP));
   791         if (he && he->rec) {
   792             // walk list looking for |callback|... we cannot assume
   793             // that it will be there!
   794             PRCList *node = he->rec->callbacks.next;
   795             while (node != &he->rec->callbacks) {
   796                 if (static_cast<nsResolveHostCallback *>(node) == callback) {
   797                     PR_REMOVE_LINK(callback);
   798                     rec = he->rec;
   799                     break;
   800                 }
   801                 node = node->next;
   802             }
   803         }
   804     }
   806     // complete callback with the given status code; this would only be done if
   807     // the record was in the process of being resolved.
   808     if (rec)
   809         callback->OnLookupComplete(this, rec, status);
   810 }
   812 nsresult
   813 nsHostResolver::ConditionallyCreateThread(nsHostRecord *rec)
   814 {
   815     if (mNumIdleThreads) {
   816         // wake up idle thread to process this lookup
   817         mIdleThreadCV.Notify();
   818     }
   819     else if ((mThreadCount < HighThreadThreshold) ||
   820              (IsHighPriority(rec->flags) && mThreadCount < MAX_RESOLVER_THREADS)) {
   821         // dispatch new worker thread
   822         NS_ADDREF_THIS(); // owning reference passed to thread
   824         mThreadCount++;
   825         PRThread *thr = PR_CreateThread(PR_SYSTEM_THREAD,
   826                                         ThreadFunc,
   827                                         this,
   828                                         PR_PRIORITY_NORMAL,
   829                                         PR_GLOBAL_THREAD,
   830                                         PR_UNJOINABLE_THREAD,
   831                                         0);
   832         if (!thr) {
   833             mThreadCount--;
   834             NS_RELEASE_THIS();
   835             return NS_ERROR_OUT_OF_MEMORY;
   836         }
   837     }
   838 #if defined(PR_LOGGING)
   839     else
   840       LOG(("  Unable to find a thread for looking up host [%s].\n", rec->host));
   841 #endif
   842     return NS_OK;
   843 }
   845 nsresult
   846 nsHostResolver::IssueLookup(nsHostRecord *rec)
   847 {
   848     MOZ_EVENT_TRACER_WAIT(rec, "net::dns::resolve");
   850     nsresult rv = NS_OK;
   851     NS_ASSERTION(!rec->resolving, "record is already being resolved");
   853     // Add rec to one of the pending queues, possibly removing it from mEvictionQ.
   854     // If rec is on mEvictionQ, then we can just move the owning
   855     // reference over to the new active queue.
   856     if (rec->next == rec)
   857         NS_ADDREF(rec);
   858     else {
   859         PR_REMOVE_LINK(rec);
   860         mEvictionQSize--;
   861     }
   863     if (IsHighPriority(rec->flags))
   864         PR_APPEND_LINK(rec, &mHighQ);
   865     else if (IsMediumPriority(rec->flags))
   866         PR_APPEND_LINK(rec, &mMediumQ);
   867     else
   868         PR_APPEND_LINK(rec, &mLowQ);
   869     mPendingCount++;
   871     rec->resolving = true;
   872     rec->onQueue = true;
   874     rv = ConditionallyCreateThread(rec);
   876     LOG (("  DNS thread counters: total=%d any-live=%d idle=%d pending=%d\n",
   877           mThreadCount,
   878           mActiveAnyThreadCount,
   879           mNumIdleThreads,
   880           mPendingCount));
   882     return rv;
   883 }
   885 nsresult
   886 nsHostResolver::ConditionallyRefreshRecord(nsHostRecord *rec, const char *host)
   887 {
   888     if (((TimeStamp::NowLoRes() > rec->expiration) || rec->negative) &&
   889         !rec->resolving) {
   890         LOG(("  Using %s cache entry for host [%s] but starting async renewal.",
   891             rec->negative ? "negative" :"positive", host));
   892         IssueLookup(rec);
   894         if (!rec->negative) {
   895             // negative entries are constantly being refreshed, only
   896             // track positive grace period induced renewals
   897             Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
   898                 METHOD_RENEWAL);
   899         }
   900     }
   901     return NS_OK;
   902 }
   904 void
   905 nsHostResolver::DeQueue(PRCList &aQ, nsHostRecord **aResult)
   906 {
   907     *aResult = static_cast<nsHostRecord *>(aQ.next);
   908     PR_REMOVE_AND_INIT_LINK(*aResult);
   909     mPendingCount--;
   910     (*aResult)->onQueue = false;
   911 }
   913 bool
   914 nsHostResolver::GetHostToLookup(nsHostRecord **result)
   915 {
   916     bool timedOut = false;
   917     PRIntervalTime epoch, now, timeout;
   919     MutexAutoLock lock(mLock);
   921     timeout = (mNumIdleThreads >= HighThreadThreshold) ? mShortIdleTimeout : mLongIdleTimeout;
   922     epoch = PR_IntervalNow();
   924     while (!mShutdown) {
   925         // remove next record from Q; hand over owning reference. Check high, then med, then low
   927         if (!PR_CLIST_IS_EMPTY(&mHighQ)) {
   928             DeQueue (mHighQ, result);
   929             return true;
   930         }
   932         if (mActiveAnyThreadCount < HighThreadThreshold) {
   933             if (!PR_CLIST_IS_EMPTY(&mMediumQ)) {
   934                 DeQueue (mMediumQ, result);
   935                 mActiveAnyThreadCount++;
   936                 (*result)->usingAnyThread = true;
   937                 return true;
   938             }
   940             if (!PR_CLIST_IS_EMPTY(&mLowQ)) {
   941                 DeQueue (mLowQ, result);
   942                 mActiveAnyThreadCount++;
   943                 (*result)->usingAnyThread = true;
   944                 return true;
   945             }
   946         }
   948         // Determining timeout is racy, so allow one cycle through checking the queues
   949         // before exiting.
   950         if (timedOut)
   951             break;
   953         // wait for one or more of the following to occur:
   954         //  (1) the pending queue has a host record to process
   955         //  (2) the shutdown flag has been set
   956         //  (3) the thread has been idle for too long
   958         mNumIdleThreads++;
   959         mIdleThreadCV.Wait(timeout);
   960         mNumIdleThreads--;
   962         now = PR_IntervalNow();
   964         if ((PRIntervalTime)(now - epoch) >= timeout)
   965             timedOut = true;
   966         else {
   967             // It is possible that PR_WaitCondVar() was interrupted and returned early,
   968             // in which case we will loop back and re-enter it. In that case we want to
   969             // do so with the new timeout reduced to reflect time already spent waiting.
   970             timeout -= (PRIntervalTime)(now - epoch);
   971             epoch = now;
   972         }
   973     }
   975     // tell thread to exit...
   976     mThreadCount--;
   977     return false;
   978 }
   980 void
   981 nsHostResolver::OnLookupComplete(nsHostRecord *rec, nsresult status, AddrInfo *result)
   982 {
   983     // get the list of pending callbacks for this lookup, and notify
   984     // them that the lookup is complete.
   985     PRCList cbs;
   986     PR_INIT_CLIST(&cbs);
   987     {
   988         MutexAutoLock lock(mLock);
   990         // grab list of callbacks to notify
   991         MoveCList(rec->callbacks, cbs);
   993         // update record fields.  We might have a rec->addr_info already if a
   994         // previous lookup result expired and we're reresolving it..
   995         AddrInfo  *old_addr_info;
   996         {
   997             MutexAutoLock lock(rec->addr_info_lock);
   998             old_addr_info = rec->addr_info;
   999             rec->addr_info = result;
  1000             rec->addr_info_gencnt++;
  1002         delete old_addr_info;
  1004         rec->expiration = TimeStamp::NowLoRes();
  1005         if (result) {
  1006             rec->expiration += mMaxCacheLifetime;
  1007             rec->negative = false;
  1009         else {
  1010             rec->expiration += TimeDuration::FromSeconds(60); /* one minute for negative cache */
  1011             rec->negative = true;
  1013         rec->resolving = false;
  1015         if (rec->usingAnyThread) {
  1016             mActiveAnyThreadCount--;
  1017             rec->usingAnyThread = false;
  1020         if (!mShutdown) {
  1021             // add to mEvictionQ
  1022             PR_APPEND_LINK(rec, &mEvictionQ);
  1023             NS_ADDREF(rec);
  1024             if (mEvictionQSize < mMaxCacheEntries)
  1025                 mEvictionQSize++;
  1026             else {
  1027                 // remove first element on mEvictionQ
  1028                 nsHostRecord *head =
  1029                     static_cast<nsHostRecord *>(PR_LIST_HEAD(&mEvictionQ));
  1030                 PR_REMOVE_AND_INIT_LINK(head);
  1031                 PL_DHashTableOperate(&mDB, (nsHostKey *) head, PL_DHASH_REMOVE);
  1033                 if (!head->negative) {
  1034                     // record the age of the entry upon eviction.
  1035                     TimeDuration age = TimeStamp::NowLoRes() -
  1036                                          (head->expiration - mMaxCacheLifetime);
  1037                     Telemetry::Accumulate(Telemetry::DNS_CLEANUP_AGE,
  1038                                           static_cast<uint32_t>(age.ToSeconds() / 60));
  1041                 // release reference to rec owned by mEvictionQ
  1042                 NS_RELEASE(head);
  1047     MOZ_EVENT_TRACER_DONE(rec, "net::dns::resolve");
  1049     if (!PR_CLIST_IS_EMPTY(&cbs)) {
  1050         PRCList *node = cbs.next;
  1051         while (node != &cbs) {
  1052             nsResolveHostCallback *callback =
  1053                     static_cast<nsResolveHostCallback *>(node);
  1054             node = node->next;
  1055             callback->OnLookupComplete(this, rec, status);
  1056             // NOTE: callback must not be dereferenced after this point!!
  1060     NS_RELEASE(rec);
  1063 void
  1064 nsHostResolver::CancelAsyncRequest(const char            *host,
  1065                                    uint16_t               flags,
  1066                                    uint16_t               af,
  1067                                    nsIDNSListener        *aListener,
  1068                                    nsresult               status)
  1071     MutexAutoLock lock(mLock);
  1073     // Lookup the host record associated with host, flags & address family
  1074     nsHostKey key = { host, flags, af };
  1075     nsHostDBEnt *he = static_cast<nsHostDBEnt *>
  1076                       (PL_DHashTableOperate(&mDB, &key, PL_DHASH_LOOKUP));
  1077     if (he && he->rec) {
  1078         nsHostRecord* recPtr = nullptr;
  1079         PRCList *node = he->rec->callbacks.next;
  1080         // Remove the first nsDNSAsyncRequest callback which matches the
  1081         // supplied listener object
  1082         while (node != &he->rec->callbacks) {
  1083             nsResolveHostCallback *callback
  1084                 = static_cast<nsResolveHostCallback *>(node);
  1085             if (callback && (callback->EqualsAsyncListener(aListener))) {
  1086                 // Remove from the list of callbacks
  1087                 PR_REMOVE_LINK(callback);
  1088                 recPtr = he->rec;
  1089                 callback->OnLookupComplete(this, recPtr, status);
  1090                 break;
  1092             node = node->next;
  1095         // If there are no more callbacks, remove the hash table entry
  1096         if (recPtr && PR_CLIST_IS_EMPTY(&recPtr->callbacks)) {
  1097             PL_DHashTableOperate(&mDB, (nsHostKey *)recPtr, PL_DHASH_REMOVE);
  1098             // If record is on a Queue, remove it and then deref it
  1099             if (recPtr->next != recPtr) {
  1100                 PR_REMOVE_LINK(recPtr);
  1101                 NS_RELEASE(recPtr);
  1107 static size_t
  1108 SizeOfHostDBEntExcludingThis(PLDHashEntryHdr* hdr, MallocSizeOf mallocSizeOf,
  1109                              void*)
  1111     nsHostDBEnt* ent = static_cast<nsHostDBEnt*>(hdr);
  1112     return ent->rec->SizeOfIncludingThis(mallocSizeOf);
  1115 size_t
  1116 nsHostResolver::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
  1118     MutexAutoLock lock(mLock);
  1120     size_t n = mallocSizeOf(this);
  1121     n += PL_DHashTableSizeOfExcludingThis(&mDB, SizeOfHostDBEntExcludingThis,
  1122                                           mallocSizeOf);
  1124     // The following fields aren't measured.
  1125     // - mHighQ, mMediumQ, mLowQ, mEvictionQ, because they just point to
  1126     //   nsHostRecords that also pointed to by entries |mDB|, and measured when
  1127     //   |mDB| is measured.
  1129     return n;
  1132 void
  1133 nsHostResolver::ThreadFunc(void *arg)
  1135     LOG(("DNS lookup thread - starting execution.\n"));
  1137     static nsThreadPoolNaming naming;
  1138     naming.SetThreadPoolName(NS_LITERAL_CSTRING("DNS Resolver"));
  1140 #if defined(RES_RETRY_ON_FAILURE)
  1141     nsResState rs;
  1142 #endif
  1143     nsHostResolver *resolver = (nsHostResolver *)arg;
  1144     nsHostRecord *rec;
  1145     PRAddrInfo *prai = nullptr;
  1146     while (resolver->GetHostToLookup(&rec)) {
  1147         LOG(("DNS lookup thread - Calling getaddrinfo for host [%s].\n",
  1148              rec->host));
  1150         int flags = PR_AI_ADDRCONFIG;
  1151         if (!(rec->flags & RES_CANON_NAME))
  1152             flags |= PR_AI_NOCANONNAME;
  1154         TimeStamp startTime = TimeStamp::Now();
  1155         MOZ_EVENT_TRACER_EXEC(rec, "net::dns::resolve");
  1157         // We need to remove IPv4 records manually
  1158         // because PR_GetAddrInfoByName doesn't support PR_AF_INET6.
  1159         bool disableIPv4 = rec->af == PR_AF_INET6;
  1160         uint16_t af = disableIPv4 ? PR_AF_UNSPEC : rec->af;
  1161         prai = PR_GetAddrInfoByName(rec->host, af, flags);
  1162 #if defined(RES_RETRY_ON_FAILURE)
  1163         if (!prai && rs.Reset())
  1164             prai = PR_GetAddrInfoByName(rec->host, af, flags);
  1165 #endif
  1167         TimeDuration elapsed = TimeStamp::Now() - startTime;
  1168         uint32_t millis = static_cast<uint32_t>(elapsed.ToMilliseconds());
  1170         // convert error code to nsresult
  1171         nsresult status;
  1172         AddrInfo *ai = nullptr;
  1173         if (prai) {
  1174             const char *cname = nullptr;
  1175             if (rec->flags & RES_CANON_NAME)
  1176                 cname = PR_GetCanonNameFromAddrInfo(prai);
  1177             ai = new AddrInfo(rec->host, prai, disableIPv4, cname);
  1178             PR_FreeAddrInfo(prai);
  1179             if (ai->mAddresses.isEmpty()) {
  1180                 delete ai;
  1181                 ai = nullptr;
  1184         if (ai) {
  1185             status = NS_OK;
  1187             Telemetry::Accumulate(!rec->addr_info_gencnt ?
  1188                                     Telemetry::DNS_LOOKUP_TIME :
  1189                                     Telemetry::DNS_RENEWAL_TIME,
  1190                                   millis);
  1192         else {
  1193             status = NS_ERROR_UNKNOWN_HOST;
  1194             Telemetry::Accumulate(Telemetry::DNS_FAILED_LOOKUP_TIME, millis);
  1197         // OnLookupComplete may release "rec", log before we lose it.
  1198         LOG(("DNS lookup thread - lookup completed for host [%s]: %s.\n",
  1199              rec->host, ai ? "success" : "failure: unknown host"));
  1200         resolver->OnLookupComplete(rec, status, ai);
  1202     NS_RELEASE(resolver);
  1203     LOG(("DNS lookup thread - queue empty, thread finished.\n"));
  1206 nsresult
  1207 nsHostResolver::Create(uint32_t         maxCacheEntries,
  1208                        uint32_t         maxCacheLifetime,
  1209                        uint32_t         lifetimeGracePeriod,
  1210                        nsHostResolver **result)
  1212 #if defined(PR_LOGGING)
  1213     if (!gHostResolverLog)
  1214         gHostResolverLog = PR_NewLogModule("nsHostResolver");
  1215 #endif
  1217     nsHostResolver *res = new nsHostResolver(maxCacheEntries,
  1218                                              maxCacheLifetime,
  1219                                              lifetimeGracePeriod);
  1220     if (!res)
  1221         return NS_ERROR_OUT_OF_MEMORY;
  1222     NS_ADDREF(res);
  1224     nsresult rv = res->Init();
  1225     if (NS_FAILED(rv))
  1226         NS_RELEASE(res);
  1228     *result = res;
  1229     return rv;
  1232 PLDHashOperator
  1233 CacheEntryEnumerator(PLDHashTable *table, PLDHashEntryHdr *entry,
  1234                      uint32_t number, void *arg)
  1236     // We don't pay attention to address literals, only resolved domains.
  1237     // Also require a host.
  1238     nsHostRecord *rec = static_cast<nsHostDBEnt*>(entry)->rec;
  1239     MOZ_ASSERT(rec, "rec should never be null here!");
  1240     if (!rec || !rec->addr_info || !rec->host) {
  1241         return PL_DHASH_NEXT;
  1244     DNSCacheEntries info;
  1245     info.hostname = rec->host;
  1246     info.family = rec->af;
  1247     info.expiration = (int64_t)(rec->expiration - TimeStamp::NowLoRes()).ToSeconds();
  1248     if (info.expiration <= 0) {
  1249         // We only need valid DNS cache entries
  1250         return PL_DHASH_NEXT;
  1254         MutexAutoLock lock(rec->addr_info_lock);
  1256         NetAddr *addr = nullptr;
  1257         NetAddrElement *addrElement = rec->addr_info->mAddresses.getFirst();
  1258         if (addrElement) {
  1259             addr = &addrElement->mAddress;
  1261         while (addr) {
  1262             char buf[kIPv6CStrBufSize];
  1263             if (NetAddrToString(addr, buf, sizeof(buf))) {
  1264                 info.hostaddr.AppendElement(buf);
  1266             addr = nullptr;
  1267             addrElement = addrElement->getNext();
  1268             if (addrElement) {
  1269                 addr = &addrElement->mAddress;
  1274     nsTArray<DNSCacheEntries> *args = static_cast<nsTArray<DNSCacheEntries> *>(arg);
  1275     args->AppendElement(info);
  1277     return PL_DHASH_NEXT;
  1280 void
  1281 nsHostResolver::GetDNSCacheEntries(nsTArray<DNSCacheEntries> *args)
  1283     PL_DHashTableEnumerate(&mDB, CacheEntryEnumerator, args);

mercurial