LCOV - code coverage report
Current view: directory - netwerk/dns - nsHostResolver.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 422 323 76.5 %
Date: 2012-06-02 Functions: 35 30 85.7 %

       1                 : /* vim:set ts=4 sw=4 sts=4 et cin: */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is Mozilla.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is IBM Corporation.
      18                 :  * Portions created by IBM Corporation are Copyright (C) 2003
      19                 :  * IBM Corporation. All Rights Reserved.
      20                 :  *
      21                 :  * Contributor(s):
      22                 :  *   IBM Corp.
      23                 :  *
      24                 :  * Alternatively, the contents of this file may be used under the terms of
      25                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      26                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      27                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      28                 :  * of those above. If you wish to allow use of your version of this file only
      29                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      30                 :  * use your version of this file under the terms of the MPL, indicate your
      31                 :  * decision by deleting the provisions above and replace them with the notice
      32                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      33                 :  * the provisions above, a recipient may use your version of this file under
      34                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      35                 :  *
      36                 :  * ***** END LICENSE BLOCK ***** */
      37                 : 
      38                 : #if defined(MOZ_LOGGING)
      39                 : #define FORCE_PR_LOG
      40                 : #endif
      41                 : 
      42                 : #if defined(HAVE_RES_NINIT)
      43                 : #include <sys/types.h>
      44                 : #include <netinet/in.h>
      45                 : #include <arpa/inet.h>   
      46                 : #include <arpa/nameser.h>
      47                 : #include <resolv.h>
      48                 : #define RES_RETRY_ON_FAILURE
      49                 : #endif
      50                 : 
      51                 : #include <stdlib.h>
      52                 : #include "nsHostResolver.h"
      53                 : #include "nsNetError.h"
      54                 : #include "nsISupportsBase.h"
      55                 : #include "nsISupportsUtils.h"
      56                 : #include "nsAutoPtr.h"
      57                 : #include "pratom.h"
      58                 : #include "prthread.h"
      59                 : #include "prerror.h"
      60                 : #include "prtime.h"
      61                 : #include "prlong.h"
      62                 : #include "prlog.h"
      63                 : #include "pldhash.h"
      64                 : #include "plstr.h"
      65                 : #include "nsURLHelper.h"
      66                 : 
      67                 : #include "mozilla/HashFunctions.h"
      68                 : #include "mozilla/FunctionTimer.h"
      69                 : #include "mozilla/TimeStamp.h"
      70                 : #include "mozilla/Telemetry.h"
      71                 : 
      72                 : using namespace mozilla;
      73                 : 
      74                 : //----------------------------------------------------------------------------
      75                 : 
      76                 : // Use a persistent thread pool in order to avoid spinning up new threads all the time.
      77                 : // In particular, thread creation results in a res_init() call from libc which is 
      78                 : // quite expensive.
      79                 : //
      80                 : // The pool dynamically grows between 0 and MAX_RESOLVER_THREADS in size. New requests
      81                 : // go first to an idle thread. If that cannot be found and there are fewer than MAX_RESOLVER_THREADS
      82                 : // currently in the pool a new thread is created for high priority requests. If
      83                 : // the new request is at a lower priority a new thread will only be created if 
      84                 : // there are fewer than HighThreadThreshold currently outstanding. If a thread cannot be
      85                 : // created or an idle thread located for the request it is queued.
      86                 : //
      87                 : // When the pool is greater than HighThreadThreshold in size a thread will be destroyed after
      88                 : // ShortIdleTimeoutSeconds of idle time. Smaller pools use LongIdleTimeoutSeconds for a 
      89                 : // timeout period.
      90                 : 
      91                 : #define HighThreadThreshold     MAX_RESOLVER_THREADS_FOR_ANY_PRIORITY
      92                 : #define LongIdleTimeoutSeconds  300           // for threads 1 -> HighThreadThreshold
      93                 : #define ShortIdleTimeoutSeconds 60            // for threads HighThreadThreshold+1 -> MAX_RESOLVER_THREADS
      94                 : 
      95                 : PR_STATIC_ASSERT (HighThreadThreshold <= MAX_RESOLVER_THREADS);
      96                 : 
      97                 : //----------------------------------------------------------------------------
      98                 : 
      99                 : #if defined(PR_LOGGING)
     100                 : static PRLogModuleInfo *gHostResolverLog = nsnull;
     101                 : #define LOG(args) PR_LOG(gHostResolverLog, PR_LOG_DEBUG, args)
     102                 : #else
     103                 : #define LOG(args)
     104                 : #endif
     105                 : 
     106                 : //----------------------------------------------------------------------------
     107                 : 
     108                 : static inline void
     109            7982 : MoveCList(PRCList &from, PRCList &to)
     110                 : {
     111            7982 :     if (!PR_CLIST_IS_EMPTY(&from)) {
     112            2351 :         to.next = from.next;
     113            2351 :         to.prev = from.prev;
     114            2351 :         to.next->prev = &to;
     115            2351 :         to.prev->next = &to;
     116            2351 :         PR_INIT_CLIST(&from);
     117                 :     }             
     118            7982 : }
     119                 : 
     120                 : static PRUint32
     121           11309 : NowInMinutes()
     122                 : {
     123           11309 :     PRTime now = PR_Now(), minutes, factor;
     124           11309 :     LL_I2L(factor, 60 * PR_USEC_PER_SEC);
     125           11309 :     LL_DIV(minutes, now, factor);
     126                 :     PRUint32 result;
     127           11309 :     LL_L2UI(result, minutes);
     128           11309 :     return result;
     129                 : }
     130                 : 
     131                 : //----------------------------------------------------------------------------
     132                 : 
     133                 : #if defined(RES_RETRY_ON_FAILURE)
     134                 : 
     135                 : // this class represents the resolver state for a given thread.  if we
     136                 : // encounter a lookup failure, then we can invoke the Reset method on an
     137                 : // instance of this class to reset the resolver (in case /etc/resolv.conf
     138                 : // for example changed).  this is mainly an issue on GNU systems since glibc
     139                 : // only reads in /etc/resolv.conf once per thread.  it may be an issue on
     140                 : // other systems as well.
     141                 : 
     142                 : class nsResState
     143                 : {
     144                 : public:
     145             273 :     nsResState()
     146                 :         // initialize mLastReset to the time when this object
     147                 :         // is created.  this means that a reset will not occur
     148                 :         // if a thread is too young.  the alternative would be
     149                 :         // to initialize this to the beginning of time, so that
     150                 :         // the first failure would cause a reset, but since the
     151                 :         // thread would have just started up, it likely would
     152                 :         // already have current /etc/resolv.conf info.
     153             273 :         : mLastReset(PR_IntervalNow())
     154                 :     {
     155             273 :     }
     156                 : 
     157             129 :     bool Reset()
     158                 :     {
     159                 :         // reset no more than once per second
     160             129 :         if (PR_IntervalToSeconds(PR_IntervalNow() - mLastReset) < 1)
     161             124 :             return false;
     162                 : 
     163               5 :         LOG(("calling res_ninit\n"));
     164                 : 
     165               5 :         mLastReset = PR_IntervalNow();
     166               5 :         return (res_ninit(&_res) == 0);
     167                 :     }
     168                 : 
     169                 : private:
     170                 :     PRIntervalTime mLastReset;
     171                 : };
     172                 : 
     173                 : #endif // RES_RETRY_ON_FAILURE
     174                 : 
     175                 : //----------------------------------------------------------------------------
     176                 : 
     177                 : // this macro filters out any flags that are not used when constructing the
     178                 : // host key.  the significant flags are those that would affect the resulting
     179                 : // host record (i.e., the flags that are passed down to PR_GetAddrInfoByName).
     180                 : #define RES_KEY_FLAGS(_f) ((_f) & nsHostResolver::RES_CANON_NAME)
     181                 : 
     182             301 : nsHostRecord::nsHostRecord(const nsHostKey *key)
     183                 :     : _refc(1)
     184                 :     , addr_info_lock("nsHostRecord.addr_info_lock")
     185                 :     , addr_info_gencnt(0)
     186                 :     , addr_info(nsnull)
     187                 :     , addr(nsnull)
     188                 :     , negative(false)
     189                 :     , resolving(false)
     190                 :     , onQueue(false)
     191             301 :     , usingAnyThread(false)
     192                 : {
     193             301 :     host = ((char *) this) + sizeof(nsHostRecord);
     194             301 :     memcpy((char *) host, key->host, strlen(key->host) + 1);
     195             301 :     flags = key->flags;
     196             301 :     af = key->af;
     197                 : 
     198             301 :     NS_LOG_ADDREF(this, 1, "nsHostRecord", sizeof(nsHostRecord));
     199             301 :     expiration = NowInMinutes();
     200                 : 
     201             301 :     PR_INIT_CLIST(this);
     202             301 :     PR_INIT_CLIST(&callbacks);
     203             301 : }
     204                 : 
     205                 : nsresult
     206             301 : nsHostRecord::Create(const nsHostKey *key, nsHostRecord **result)
     207                 : {
     208             301 :     size_t hostLen = strlen(key->host) + 1;
     209             301 :     size_t size = hostLen + sizeof(nsHostRecord);
     210                 : 
     211                 :     // Use placement new to create the object with room for the hostname
     212                 :     // allocated after it.
     213             301 :     void *place = ::operator new(size);
     214             301 :     *result = new(place) nsHostRecord(key);
     215             301 :     return NS_OK;
     216                 : }
     217                 : 
     218             602 : nsHostRecord::~nsHostRecord()
     219                 : {
     220             301 :     if (addr)
     221              24 :         free(addr);
     222             301 : }
     223                 : 
     224                 : bool
     225            2919 : nsHostRecord::Blacklisted(PRNetAddr *aQuery)
     226                 : {
     227                 :     // must call locked
     228            2919 :     LOG(("nsHostRecord::Blacklisted() %p %s\n", this, host));
     229                 : 
     230                 :     // skip the string conversion for the common case of no blacklist
     231            2919 :     if (!mBlacklistedItems.Length())
     232            2869 :         return false;
     233                 :     
     234                 :     char buf[64];
     235              50 :     if (PR_NetAddrToString(aQuery, buf, sizeof(buf)) != PR_SUCCESS)
     236               0 :         return false;
     237                 : 
     238             100 :     nsDependentCString strQuery(buf);
     239              50 :     LOG(("nsHostRecord::Blacklisted() query %s\n", buf));
     240                 :     
     241              50 :     for (PRUint32 i = 0; i < mBlacklistedItems.Length(); i++)
     242              50 :         if (mBlacklistedItems.ElementAt(i).Equals(strQuery)) {
     243              50 :             LOG(("nsHostRecord::Blacklisted() %s blacklist confirmed\n", buf));
     244              50 :             return true;
     245                 :         }
     246                 : 
     247               0 :     return false;
     248                 : }
     249                 : 
     250                 : void
     251              68 : nsHostRecord::ReportUnusable(PRNetAddr *aAddress)
     252                 : {
     253                 :     // must call locked
     254              68 :     LOG(("nsHostRecord::ReportUnusable() %p %s\n", this, host));
     255                 : 
     256                 :     char buf[64];
     257              68 :     if (PR_NetAddrToString(aAddress, buf, sizeof(buf)) == PR_SUCCESS) {
     258              68 :         LOG(("nsHostrecord::ReportUnusable addr %s\n",buf));
     259              68 :         mBlacklistedItems.AppendElement(nsCString(buf));
     260                 :     }
     261              68 : }
     262                 : 
     263                 : void
     264              50 : nsHostRecord::ResetBlacklist()
     265                 : {
     266                 :     // must call locked
     267              50 :     LOG(("nsHostRecord::ResetBlacklist() %p %s\n", this, host));
     268              50 :     mBlacklistedItems.Clear();
     269              50 : }
     270                 : 
     271                 : //----------------------------------------------------------------------------
     272                 : 
     273                 : struct nsHostDBEnt : PLDHashEntryHdr
     274                 : {
     275                 :     nsHostRecord *rec;
     276                 : };
     277                 : 
     278                 : static PLDHashNumber
     279            6493 : HostDB_HashKey(PLDHashTable *table, const void *key)
     280                 : {
     281            6493 :     const nsHostKey *hk = static_cast<const nsHostKey *>(key);
     282            6493 :     return AddToHash(HashString(hk->host), RES_KEY_FLAGS(hk->flags), hk->af);
     283                 : }
     284                 : 
     285                 : static bool
     286            6192 : HostDB_MatchEntry(PLDHashTable *table,
     287                 :                   const PLDHashEntryHdr *entry,
     288                 :                   const void *key)
     289                 : {
     290            6192 :     const nsHostDBEnt *he = static_cast<const nsHostDBEnt *>(entry);
     291            6192 :     const nsHostKey *hk = static_cast<const nsHostKey *>(key); 
     292                 : 
     293            6192 :     return !strcmp(he->rec->host, hk->host) &&
     294                 :             RES_KEY_FLAGS (he->rec->flags) == RES_KEY_FLAGS(hk->flags) &&
     295            6192 :             he->rec->af == hk->af;
     296                 : }
     297                 : 
     298                 : static void
     299               0 : HostDB_MoveEntry(PLDHashTable *table,
     300                 :                  const PLDHashEntryHdr *from,
     301                 :                  PLDHashEntryHdr *to)
     302                 : {
     303                 :     static_cast<nsHostDBEnt *>(to)->rec =
     304               0 :             static_cast<const nsHostDBEnt *>(from)->rec;
     305               0 : }
     306                 : 
     307                 : static void
     308             301 : HostDB_ClearEntry(PLDHashTable *table,
     309                 :                   PLDHashEntryHdr *entry)
     310                 : {
     311             301 :     LOG(("evicting record\n"));
     312             301 :     nsHostDBEnt *he = static_cast<nsHostDBEnt *>(entry);
     313                 : #if defined(DEBUG) && defined(PR_LOGGING)
     314             301 :     if (!he->rec->addr_info)
     315              33 :         LOG(("%s: => no addr_info\n", he->rec->host));
     316                 :     else {
     317             268 :         PRInt32 now = (PRInt32) NowInMinutes();
     318             268 :         PRInt32 diff = (PRInt32) he->rec->expiration - now;
     319             268 :         LOG(("%s: exp=%d => %s\n",
     320                 :             he->rec->host, diff,
     321                 :             PR_GetCanonNameFromAddrInfo(he->rec->addr_info)));
     322             268 :         void *iter = nsnull;
     323                 :         PRNetAddr addr;
     324                 :         char buf[64];
     325             268 :         for (;;) {
     326             536 :             iter = PR_EnumerateAddrInfo(iter, he->rec->addr_info, 0, &addr);
     327             536 :             if (!iter)
     328             268 :                 break;
     329             268 :             PR_NetAddrToString(&addr, buf, sizeof(buf));
     330             268 :             LOG(("  %s\n", buf));
     331                 :         }
     332                 :     }
     333                 : #endif
     334             301 :     NS_RELEASE(he->rec);
     335             301 : }
     336                 : 
     337                 : static bool
     338             301 : HostDB_InitEntry(PLDHashTable *table,
     339                 :                  PLDHashEntryHdr *entry,
     340                 :                  const void *key)
     341                 : {
     342             301 :     nsHostDBEnt *he = static_cast<nsHostDBEnt *>(entry);
     343             301 :     nsHostRecord::Create(static_cast<const nsHostKey *>(key), &he->rec);
     344             301 :     return true;
     345                 : }
     346                 : 
     347                 : static PLDHashTableOps gHostDB_ops =
     348                 : {
     349                 :     PL_DHashAllocTable,
     350                 :     PL_DHashFreeTable,
     351                 :     HostDB_HashKey,
     352                 :     HostDB_MatchEntry,
     353                 :     HostDB_MoveEntry,
     354                 :     HostDB_ClearEntry,
     355                 :     PL_DHashFinalizeStub,
     356                 :     HostDB_InitEntry,
     357                 : };
     358                 : 
     359                 : static PLDHashOperator
     360             301 : HostDB_RemoveEntry(PLDHashTable *table,
     361                 :                    PLDHashEntryHdr *hdr,
     362                 :                    PRUint32 number,
     363                 :                    void *arg)
     364                 : {
     365             301 :     return PL_DHASH_REMOVE;
     366                 : }
     367                 : 
     368                 : //----------------------------------------------------------------------------
     369                 : 
     370            1444 : nsHostResolver::nsHostResolver(PRUint32 maxCacheEntries,
     371                 :                                PRUint32 maxCacheLifetime,
     372                 :                                PRUint32 lifetimeGracePeriod)
     373                 :     : mMaxCacheEntries(maxCacheEntries)
     374                 :     , mMaxCacheLifetime(maxCacheLifetime)
     375                 :     , mGracePeriod(lifetimeGracePeriod)
     376                 :     , mLock("nsHostResolver.mLock")
     377                 :     , mIdleThreadCV(mLock, "nsHostResolver.mIdleThreadCV")
     378                 :     , mNumIdleThreads(0)
     379                 :     , mThreadCount(0)
     380                 :     , mActiveAnyThreadCount(0)
     381                 :     , mEvictionQSize(0)
     382                 :     , mPendingCount(0)
     383            1444 :     , mShutdown(true)
     384                 : {
     385            1444 :     mCreationTime = PR_Now();
     386            1444 :     PR_INIT_CLIST(&mHighQ);
     387            1444 :     PR_INIT_CLIST(&mMediumQ);
     388            1444 :     PR_INIT_CLIST(&mLowQ);
     389            1444 :     PR_INIT_CLIST(&mEvictionQ);
     390                 : 
     391            1444 :     mLongIdleTimeout  = PR_SecondsToInterval(LongIdleTimeoutSeconds);
     392            1444 :     mShortIdleTimeout = PR_SecondsToInterval(ShortIdleTimeoutSeconds);
     393            1444 : }
     394                 : 
     395            2888 : nsHostResolver::~nsHostResolver()
     396                 : {
     397            1444 :     PL_DHashTableFinish(&mDB);
     398            1444 : }
     399                 : 
     400                 : nsresult
     401            1444 : nsHostResolver::Init()
     402                 : {
     403                 :     NS_TIME_FUNCTION;
     404                 : 
     405            1444 :     PL_DHashTableInit(&mDB, &gHostDB_ops, nsnull, sizeof(nsHostDBEnt), 0);
     406                 : 
     407            1444 :     mShutdown = false;
     408                 : 
     409                 : #if defined(HAVE_RES_NINIT)
     410                 :     // We want to make sure the system is using the correct resolver settings,
     411                 :     // so we force it to reload those settings whenever we startup a subsequent
     412                 :     // nsHostResolver instance.  We assume that there is no reason to do this
     413                 :     // for the first nsHostResolver instance since that is usually created
     414                 :     // during application startup.
     415                 :     static int initCount = 0;
     416            1444 :     if (initCount++ > 0) {
     417              25 :         LOG(("calling res_ninit\n"));
     418              25 :         res_ninit(&_res);
     419                 :     }
     420                 : #endif
     421            1444 :     return NS_OK;
     422                 : }
     423                 : 
     424                 : void
     425            4332 : nsHostResolver::ClearPendingQueue(PRCList *aPendingQ)
     426                 : {
     427                 :     // loop through pending queue, erroring out pending lookups.
     428            4332 :     if (!PR_CLIST_IS_EMPTY(aPendingQ)) {
     429               0 :         PRCList *node = aPendingQ->next;
     430               0 :         while (node != aPendingQ) {
     431               0 :             nsHostRecord *rec = static_cast<nsHostRecord *>(node);
     432               0 :             node = node->next;
     433               0 :             OnLookupComplete(rec, NS_ERROR_ABORT, nsnull);
     434                 :         }
     435                 :     }
     436            4332 : }
     437                 : 
     438                 : void
     439            1444 : nsHostResolver::Shutdown()
     440                 : {
     441            1444 :     LOG(("nsHostResolver::Shutdown\n"));
     442                 : 
     443                 :     PRCList pendingQHigh, pendingQMed, pendingQLow, evictionQ;
     444            1444 :     PR_INIT_CLIST(&pendingQHigh);
     445            1444 :     PR_INIT_CLIST(&pendingQMed);
     446            1444 :     PR_INIT_CLIST(&pendingQLow);
     447            1444 :     PR_INIT_CLIST(&evictionQ);
     448                 : 
     449                 :     {
     450            2888 :         MutexAutoLock lock(mLock);
     451                 :         
     452            1444 :         mShutdown = true;
     453                 : 
     454            1444 :         MoveCList(mHighQ, pendingQHigh);
     455            1444 :         MoveCList(mMediumQ, pendingQMed);
     456            1444 :         MoveCList(mLowQ, pendingQLow);
     457            1444 :         MoveCList(mEvictionQ, evictionQ);
     458            1444 :         mEvictionQSize = 0;
     459            1444 :         mPendingCount = 0;
     460                 :         
     461            1444 :         if (mNumIdleThreads)
     462             267 :             mIdleThreadCV.NotifyAll();
     463                 :         
     464                 :         // empty host database
     465            1444 :         PL_DHashTableEnumerate(&mDB, HostDB_RemoveEntry, nsnull);
     466                 :     }
     467                 :     
     468            1444 :     ClearPendingQueue(&pendingQHigh);
     469            1444 :     ClearPendingQueue(&pendingQMed);
     470            1444 :     ClearPendingQueue(&pendingQLow);
     471                 : 
     472            1444 :     if (!PR_CLIST_IS_EMPTY(&evictionQ)) {
     473             264 :         PRCList *node = evictionQ.next;
     474             796 :         while (node != &evictionQ) {
     475             268 :             nsHostRecord *rec = static_cast<nsHostRecord *>(node);
     476             268 :             node = node->next;
     477             268 :             NS_RELEASE(rec);
     478                 :         }
     479                 :     }
     480                 : 
     481                 : #ifdef NS_BUILD_REFCNT_LOGGING
     482                 :     
     483                 :     // Logically join the outstanding worker threads with a timeout.
     484                 :     // Use this approach instead of PR_JoinThread() because that does 
     485                 :     // not allow a timeout which may be necessary for a semi-responsive 
     486                 :     // shutdown if the thread is blocked on a very slow DNS resolution.
     487                 :     // mThreadCount is read outside of mLock, but the worst case 
     488                 :     // scenario for that race is one extra 25ms sleep.
     489                 : 
     490            1444 :     PRIntervalTime delay = PR_MillisecondsToInterval(25);
     491            1444 :     PRIntervalTime stopTime = PR_IntervalNow() + PR_SecondsToInterval(20);
     492            3219 :     while (mThreadCount && PR_IntervalNow() < stopTime)
     493             331 :         PR_Sleep(delay);
     494                 : #endif
     495            1444 : }
     496                 : 
     497                 : static inline bool
     498            2252 : IsHighPriority(PRUint16 flags)
     499                 : {
     500            2252 :     return !(flags & (nsHostResolver::RES_PRIORITY_LOW | nsHostResolver::RES_PRIORITY_MEDIUM));
     501                 : }
     502                 : 
     503                 : static inline bool
     504              23 : IsMediumPriority(PRUint16 flags)
     505                 : {
     506              23 :     return flags & nsHostResolver::RES_PRIORITY_MEDIUM;
     507                 : }
     508                 : 
     509                 : static inline bool
     510               0 : IsLowPriority(PRUint16 flags)
     511                 : {
     512               0 :     return flags & nsHostResolver::RES_PRIORITY_LOW;
     513                 : }
     514                 : 
     515                 : void 
     516               0 : nsHostResolver::MoveQueue(nsHostRecord *aRec, PRCList &aDestQ)
     517                 : {
     518               0 :     NS_ASSERTION(aRec->onQueue, "Moving Host Record Not Currently Queued");
     519                 :     
     520               0 :     PR_REMOVE_LINK(aRec);
     521               0 :     PR_APPEND_LINK(aRec, &aDestQ);
     522               0 : }
     523                 : 
     524                 : nsresult
     525            6493 : nsHostResolver::ResolveHost(const char            *host,
     526                 :                             PRUint16               flags,
     527                 :                             PRUint16               af,
     528                 :                             nsResolveHostCallback *callback)
     529                 : {
     530            6493 :     NS_ENSURE_TRUE(host && *host, NS_ERROR_UNEXPECTED);
     531                 : 
     532            6493 :     LOG(("nsHostResolver::ResolveHost [host=%s]\n", host));
     533                 : 
     534                 :     // ensure that we are working with a valid hostname before proceeding.  see
     535                 :     // bug 304904 for details.
     536            6493 :     if (!net_IsValidHostName(nsDependentCString(host)))
     537               0 :         return NS_ERROR_UNKNOWN_HOST;
     538                 : 
     539                 :     // if result is set inside the lock, then we need to issue the
     540                 :     // callback before returning.
     541           12986 :     nsRefPtr<nsHostRecord> result;
     542            6493 :     nsresult status = NS_OK, rv = NS_OK;
     543                 :     {
     544           12986 :         MutexAutoLock lock(mLock);
     545                 : 
     546            6493 :         if (mShutdown)
     547               0 :             rv = NS_ERROR_NOT_INITIALIZED;
     548                 :         else {
     549                 :             PRNetAddr tempAddr;
     550                 : 
     551                 :             // unfortunately, PR_StringToNetAddr does not properly initialize
     552                 :             // the output buffer in the case of IPv6 input.  see bug 223145.
     553            6493 :             memset(&tempAddr, 0, sizeof(PRNetAddr));
     554                 :             
     555                 :             // check to see if there is already an entry for this |host|
     556                 :             // in the hash table.  if so, then check to see if we can't
     557                 :             // just reuse the lookup result.  otherwise, if there are
     558                 :             // any pending callbacks, then add to pending callbacks queue,
     559                 :             // and return.  otherwise, add ourselves as first pending
     560                 :             // callback, and proceed to do the lookup.
     561                 : 
     562            6493 :             nsHostKey key = { host, flags, af };
     563                 :             nsHostDBEnt *he = static_cast<nsHostDBEnt *>
     564            6493 :                                          (PL_DHashTableOperate(&mDB, &key, PL_DHASH_ADD));
     565                 : 
     566                 :             // if the record is null, then HostDB_InitEntry failed.
     567            6493 :             if (!he || !he->rec)
     568               0 :                 rv = NS_ERROR_OUT_OF_MEMORY;
     569                 :             // do we have a cached result that we can reuse?
     570           15330 :             else if (!(flags & RES_BYPASS_CACHE) &&
     571            4570 :                      he->rec->HasResult() &&
     572            4267 :                      NowInMinutes() <= he->rec->expiration + mGracePeriod) {
     573                 :                         
     574            4267 :                 LOG(("using cached record\n"));
     575                 :                 // put reference to host record on stack...
     576            4267 :                 result = he->rec;
     577            4267 :                 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD, METHOD_HIT);
     578                 : 
     579                 :                 // For entries that are in the grace period, or all cached
     580                 :                 // negative entries, use the cache but start a new lookup in
     581                 :                 // the background
     582            4543 :                 if (((NowInMinutes() > he->rec->expiration) ||
     583             276 :                      he->rec->negative) && !he->rec->resolving) {
     584             119 :                     LOG(("Using %s cache entry but starting async renewal",
     585                 :                          he->rec->negative ? "negative" :"positive"));
     586             119 :                     IssueLookup(he->rec);
     587                 : 
     588             119 :                     if (!he->rec->negative) {
     589                 :                         // negative entries are constantly being refreshed, only
     590                 :                         // track positive grace period induced renewals
     591                 :                         Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD,
     592               0 :                                               METHOD_RENEWAL);
     593                 :                     }
     594                 :                 }
     595                 :                 
     596            4267 :                 if (he->rec->negative) {
     597                 :                     Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD,
     598             276 :                                           METHOD_NEGATIVE_HIT);
     599             276 :                     status = NS_ERROR_UNKNOWN_HOST;
     600                 :                 }
     601                 :             }
     602                 :             // if the host name is an IP address literal and has been parsed,
     603                 :             // go ahead and use it.
     604            2226 :             else if (he->rec->addr) {
     605                 :                 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD,
     606              81 :                                       METHOD_LITERAL);
     607              81 :                 result = he->rec;
     608                 :             }
     609                 :             // try parsing the host name as an IP address literal to short
     610                 :             // circuit full host resolution.  (this is necessary on some
     611                 :             // platforms like Win9x.  see bug 219376 for more details.)
     612            2145 :             else if (PR_StringToNetAddr(host, &tempAddr) == PR_SUCCESS) {
     613                 :                 // ok, just copy the result into the host record, and be done
     614                 :                 // with it! ;-)
     615              24 :                 he->rec->addr = (PRNetAddr *) malloc(sizeof(PRNetAddr));
     616              24 :                 if (!he->rec->addr)
     617               0 :                     status = NS_ERROR_OUT_OF_MEMORY;
     618                 :                 else
     619              24 :                     memcpy(he->rec->addr, &tempAddr, sizeof(PRNetAddr));
     620                 :                 // put reference to host record on stack...
     621                 :                 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD,
     622              24 :                                       METHOD_LITERAL);
     623              24 :                 result = he->rec;
     624                 :             }
     625            2121 :             else if (mPendingCount >= MAX_NON_PRIORITY_REQUESTS &&
     626               0 :                      !IsHighPriority(flags) &&
     627               0 :                      !he->rec->resolving) {
     628                 :                 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD,
     629               0 :                                       METHOD_OVERFLOW);
     630                 :                 // This is a lower priority request and we are swamped, so refuse it.
     631               0 :                 rv = NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
     632                 :             }
     633                 :             // otherwise, hit the resolver...
     634                 :             else {
     635                 :                 // Add callback to the list of pending callbacks.
     636            2121 :                 PR_APPEND_LINK(callback, &he->rec->callbacks);
     637                 : 
     638            2121 :                 if (!he->rec->resolving) {
     639            2087 :                     he->rec->flags = flags;
     640            2087 :                     rv = IssueLookup(he->rec);
     641                 :                     Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD,
     642            2087 :                                           METHOD_NETWORK_FIRST);
     643            2087 :                     if (NS_FAILED(rv))
     644               0 :                         PR_REMOVE_AND_INIT_LINK(callback);
     645                 :                     else
     646            2087 :                         LOG(("dns lookup blocking pending getaddrinfo query"));
     647                 :                 }
     648              34 :                 else if (he->rec->onQueue) {
     649                 :                     Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD,
     650              23 :                                           METHOD_NETWORK_SHARED);
     651                 : 
     652                 :                     // Consider the case where we are on a pending queue of 
     653                 :                     // lower priority than the request is being made at.
     654                 :                     // In that case we should upgrade to the higher queue.
     655                 : 
     656              23 :                     if (IsHighPriority(flags) && !IsHighPriority(he->rec->flags)) {
     657                 :                         // Move from (low|med) to high.
     658               0 :                         MoveQueue(he->rec, mHighQ);
     659               0 :                         he->rec->flags = flags;
     660               0 :                         ConditionallyCreateThread(he->rec);
     661              23 :                     } else if (IsMediumPriority(flags) && IsLowPriority(he->rec->flags)) {
     662                 :                         // Move from low to med.
     663               0 :                         MoveQueue(he->rec, mMediumQ);
     664               0 :                         he->rec->flags = flags;
     665               0 :                         mIdleThreadCV.Notify();
     666                 :                     }
     667                 :                 }
     668                 :             }
     669                 :         }
     670                 :     }
     671            6493 :     if (result)
     672            4372 :         callback->OnLookupComplete(this, result, status);
     673            6493 :     return rv;
     674                 : }
     675                 : 
     676                 : void
     677               0 : nsHostResolver::DetachCallback(const char            *host,
     678                 :                                PRUint16               flags,
     679                 :                                PRUint16               af,
     680                 :                                nsResolveHostCallback *callback,
     681                 :                                nsresult               status)
     682                 : {
     683               0 :     nsRefPtr<nsHostRecord> rec;
     684                 :     {
     685               0 :         MutexAutoLock lock(mLock);
     686                 : 
     687               0 :         nsHostKey key = { host, flags, af };
     688                 :         nsHostDBEnt *he = static_cast<nsHostDBEnt *>
     689               0 :                                      (PL_DHashTableOperate(&mDB, &key, PL_DHASH_LOOKUP));
     690               0 :         if (he && he->rec) {
     691                 :             // walk list looking for |callback|... we cannot assume
     692                 :             // that it will be there!
     693               0 :             PRCList *node = he->rec->callbacks.next;
     694               0 :             while (node != &he->rec->callbacks) {
     695               0 :                 if (static_cast<nsResolveHostCallback *>(node) == callback) {
     696               0 :                     PR_REMOVE_LINK(callback);
     697               0 :                     rec = he->rec;
     698               0 :                     break;
     699                 :                 }
     700               0 :                 node = node->next;
     701                 :             }
     702                 :         }
     703                 :     }
     704                 : 
     705                 :     // complete callback with the given status code; this would only be done if
     706                 :     // the record was in the process of being resolved.
     707               0 :     if (rec)
     708               0 :         callback->OnLookupComplete(this, rec, status);
     709               0 : }
     710                 : 
     711                 : nsresult
     712            2206 : nsHostResolver::ConditionallyCreateThread(nsHostRecord *rec)
     713                 : {
     714            2206 :     if (mNumIdleThreads) {
     715                 :         // wake up idle thread to process this lookup
     716            1933 :         mIdleThreadCV.Notify();
     717                 :     }
     718             273 :     else if ((mThreadCount < HighThreadThreshold) ||
     719               0 :              (IsHighPriority(rec->flags) && mThreadCount < MAX_RESOLVER_THREADS)) {
     720                 :         // dispatch new worker thread
     721             273 :         NS_ADDREF_THIS(); // owning reference passed to thread
     722                 : 
     723             273 :         mThreadCount++;
     724                 :         PRThread *thr = PR_CreateThread(PR_SYSTEM_THREAD,
     725                 :                                         ThreadFunc,
     726                 :                                         this,
     727                 :                                         PR_PRIORITY_NORMAL,
     728                 :                                         PR_GLOBAL_THREAD,
     729                 :                                         PR_UNJOINABLE_THREAD,
     730             273 :                                         0);
     731             273 :         if (!thr) {
     732               0 :             mThreadCount--;
     733               0 :             NS_RELEASE_THIS();
     734               0 :             return NS_ERROR_OUT_OF_MEMORY;
     735                 :         }
     736                 :     }
     737                 : #if defined(PR_LOGGING)
     738                 :     else
     739               0 :       LOG(("lookup waiting for thread - %s ...\n", rec->host));
     740                 : #endif
     741            2206 :     return NS_OK;
     742                 : }
     743                 : 
     744                 : nsresult
     745            2206 : nsHostResolver::IssueLookup(nsHostRecord *rec)
     746                 : {
     747            2206 :     nsresult rv = NS_OK;
     748            2206 :     NS_ASSERTION(!rec->resolving, "record is already being resolved"); 
     749                 : 
     750                 :     // Add rec to one of the pending queues, possibly removing it from mEvictionQ.
     751                 :     // If rec is on mEvictionQ, then we can just move the owning
     752                 :     // reference over to the new active queue.
     753            2206 :     if (rec->next == rec)
     754             397 :         NS_ADDREF(rec);
     755                 :     else {
     756            1809 :         PR_REMOVE_LINK(rec);
     757            1809 :         mEvictionQSize--;
     758                 :     }
     759                 :     
     760            2206 :     if (IsHighPriority(rec->flags))
     761            2206 :         PR_APPEND_LINK(rec, &mHighQ);
     762               0 :     else if (IsMediumPriority(rec->flags))
     763               0 :         PR_APPEND_LINK(rec, &mMediumQ);
     764                 :     else
     765               0 :         PR_APPEND_LINK(rec, &mLowQ);
     766            2206 :     mPendingCount++;
     767                 :     
     768            2206 :     rec->resolving = true;
     769            2206 :     rec->onQueue = true;
     770                 : 
     771            2206 :     rv = ConditionallyCreateThread(rec);
     772                 :     
     773            2206 :     LOG (("DNS Thread Counters: total=%d any-live=%d idle=%d pending=%d\n",
     774                 :           mThreadCount,
     775                 :           mActiveAnyThreadCount,
     776                 :           mNumIdleThreads,
     777                 :           mPendingCount));
     778                 : 
     779            2206 :     return rv;
     780                 : }
     781                 : 
     782                 : void
     783            2206 : nsHostResolver::DeQueue(PRCList &aQ, nsHostRecord **aResult)
     784                 : {
     785            2206 :     *aResult = static_cast<nsHostRecord *>(aQ.next);
     786            2206 :     PR_REMOVE_AND_INIT_LINK(*aResult);
     787            2206 :     mPendingCount--;
     788            2206 :     (*aResult)->onQueue = false;
     789            2206 : }
     790                 : 
     791                 : bool
     792            2479 : nsHostResolver::GetHostToLookup(nsHostRecord **result)
     793                 : {
     794            2479 :     bool timedOut = false;
     795                 :     PRIntervalTime epoch, now, timeout;
     796                 :     
     797            4958 :     MutexAutoLock lock(mLock);
     798                 : 
     799            2479 :     timeout = (mNumIdleThreads >= HighThreadThreshold) ? mShortIdleTimeout : mLongIdleTimeout;
     800            2479 :     epoch = PR_IntervalNow();
     801                 : 
     802            7163 :     while (!mShutdown) {
     803                 :         // remove next record from Q; hand over owning reference. Check high, then med, then low
     804                 :         
     805            4411 :         if (!PR_CLIST_IS_EMPTY(&mHighQ)) {
     806            2206 :             DeQueue (mHighQ, result);
     807            2206 :             return true;
     808                 :         }
     809                 : 
     810            2205 :         if (mActiveAnyThreadCount < HighThreadThreshold) {
     811            2205 :             if (!PR_CLIST_IS_EMPTY(&mMediumQ)) {
     812               0 :                 DeQueue (mMediumQ, result);
     813               0 :                 mActiveAnyThreadCount++;
     814               0 :                 (*result)->usingAnyThread = true;
     815               0 :                 return true;
     816                 :             }
     817                 :             
     818            2205 :             if (!PR_CLIST_IS_EMPTY(&mLowQ)) {
     819               0 :                 DeQueue (mLowQ, result);
     820               0 :                 mActiveAnyThreadCount++;
     821               0 :                 (*result)->usingAnyThread = true;
     822               0 :                 return true;
     823                 :             }
     824                 :         }
     825                 :         
     826                 :         // Determining timeout is racy, so allow one cycle through checking the queues
     827                 :         // before exiting.
     828            2205 :         if (timedOut)
     829               0 :             break;
     830                 : 
     831                 :         // wait for one or more of the following to occur:
     832                 :         //  (1) the pending queue has a host record to process
     833                 :         //  (2) the shutdown flag has been set
     834                 :         //  (3) the thread has been idle for too long
     835                 :         
     836            2205 :         mNumIdleThreads++;
     837            2205 :         mIdleThreadCV.Wait(timeout);
     838            2205 :         mNumIdleThreads--;
     839                 :         
     840            2205 :         now = PR_IntervalNow();
     841                 :         
     842            2205 :         if ((PRIntervalTime)(now - epoch) >= timeout)
     843               0 :             timedOut = true;
     844                 :         else {
     845                 :             // It is possible that PR_WaitCondVar() was interrupted and returned early,
     846                 :             // in which case we will loop back and re-enter it. In that case we want to
     847                 :             // do so with the new timeout reduced to reflect time already spent waiting.
     848            2205 :             timeout -= (PRIntervalTime)(now - epoch);
     849            2205 :             epoch = now;
     850                 :         }
     851                 :     }
     852                 :     
     853                 :     // tell thread to exit...
     854             273 :     mThreadCount--;
     855             273 :     return false;
     856                 : }
     857                 : 
     858                 : void
     859            2206 : nsHostResolver::OnLookupComplete(nsHostRecord *rec, nsresult status, PRAddrInfo *result)
     860                 : {
     861                 :     // get the list of pending callbacks for this lookup, and notify
     862                 :     // them that the lookup is complete.
     863                 :     PRCList cbs;
     864            2206 :     PR_INIT_CLIST(&cbs);
     865                 :     {
     866            4412 :         MutexAutoLock lock(mLock);
     867                 : 
     868                 :         // grab list of callbacks to notify
     869            2206 :         MoveCList(rec->callbacks, cbs);
     870                 : 
     871                 :         // update record fields.  We might have a rec->addr_info already if a
     872                 :         // previous lookup result expired and we're reresolving it..
     873                 :         PRAddrInfo  *old_addr_info;
     874                 :         {
     875            4412 :             MutexAutoLock lock(rec->addr_info_lock);
     876            2206 :             old_addr_info = rec->addr_info;
     877            2206 :             rec->addr_info = result;
     878            2206 :             rec->addr_info_gencnt++;
     879                 :         }
     880            2206 :         if (old_addr_info)
     881            1809 :             PR_FreeAddrInfo(old_addr_info);
     882            2206 :         rec->expiration = NowInMinutes();
     883            2206 :         if (result) {
     884            2077 :             rec->expiration += mMaxCacheLifetime;
     885            2077 :             rec->negative = false;
     886                 :         }
     887                 :         else {
     888             129 :             rec->expiration += 1;                 /* one minute for negative cache */
     889             129 :             rec->negative = true;
     890                 :         }
     891            2206 :         rec->resolving = false;
     892                 :         
     893            2206 :         if (rec->usingAnyThread) {
     894               0 :             mActiveAnyThreadCount--;
     895               0 :             rec->usingAnyThread = false;
     896                 :         }
     897                 : 
     898            2206 :         if (rec->addr_info && !mShutdown) {
     899                 :             // add to mEvictionQ
     900            2077 :             PR_APPEND_LINK(rec, &mEvictionQ);
     901            2077 :             NS_ADDREF(rec);
     902            2077 :             if (mEvictionQSize < mMaxCacheEntries)
     903            2077 :                 mEvictionQSize++;
     904                 :             else {
     905                 :                 // remove first element on mEvictionQ
     906                 :                 nsHostRecord *head =
     907               0 :                     static_cast<nsHostRecord *>(PR_LIST_HEAD(&mEvictionQ));
     908               0 :                 PR_REMOVE_AND_INIT_LINK(head);
     909               0 :                 PL_DHashTableOperate(&mDB, (nsHostKey *) head, PL_DHASH_REMOVE);
     910                 : 
     911               0 :                 if (!head->negative) {
     912                 :                     // record the age of the entry upon eviction.
     913                 :                     PRUint32 age =
     914               0 :                         NowInMinutes() - (head->expiration - mMaxCacheLifetime);
     915               0 :                     Telemetry::Accumulate(Telemetry::DNS_CLEANUP_AGE, age);
     916                 :                 }
     917                 : 
     918                 :                 // release reference to rec owned by mEvictionQ
     919               0 :                 NS_RELEASE(head);
     920                 :             }
     921                 :         }
     922                 :     }
     923                 : 
     924            2206 :     if (!PR_CLIST_IS_EMPTY(&cbs)) {
     925            2087 :         PRCList *node = cbs.next;
     926            6295 :         while (node != &cbs) {
     927                 :             nsResolveHostCallback *callback =
     928            2121 :                     static_cast<nsResolveHostCallback *>(node);
     929            2121 :             node = node->next;
     930            2121 :             callback->OnLookupComplete(this, rec, status);
     931                 :             // NOTE: callback must not be dereferenced after this point!!
     932                 :         }
     933                 :     }
     934                 : 
     935            2206 :     NS_RELEASE(rec);
     936            2206 : }
     937                 : 
     938                 : void
     939               0 : nsHostResolver::CancelAsyncRequest(const char            *host,
     940                 :                                    PRUint16               flags,
     941                 :                                    PRUint16               af,
     942                 :                                    nsIDNSListener        *aListener,
     943                 :                                    nsresult               status)
     944                 : 
     945                 : {
     946               0 :     MutexAutoLock lock(mLock);
     947                 : 
     948                 :     // Lookup the host record associated with host, flags & address family
     949               0 :     nsHostKey key = { host, flags, af };
     950                 :     nsHostDBEnt *he = static_cast<nsHostDBEnt *>
     951               0 :                       (PL_DHashTableOperate(&mDB, &key, PL_DHASH_LOOKUP));
     952               0 :     if (he && he->rec) {
     953               0 :         nsHostRecord* recPtr = NULL;
     954               0 :         PRCList *node = he->rec->callbacks.next;
     955                 :         // Remove the first nsDNSAsyncRequest callback which matches the
     956                 :         // supplied listener object
     957               0 :         while (node != &he->rec->callbacks) {
     958                 :             nsResolveHostCallback *callback
     959               0 :                 = static_cast<nsResolveHostCallback *>(node);
     960               0 :             if (callback && (callback->EqualsAsyncListener(aListener))) {
     961                 :                 // Remove from the list of callbacks
     962               0 :                 PR_REMOVE_LINK(callback);
     963               0 :                 recPtr = he->rec;
     964               0 :                 callback->OnLookupComplete(this, recPtr, status);
     965               0 :                 break;
     966                 :             }
     967               0 :             node = node->next;
     968                 :         }
     969                 : 
     970                 :         // If there are no more callbacks, remove the hash table entry
     971               0 :         if (recPtr && PR_CLIST_IS_EMPTY(&recPtr->callbacks)) {
     972               0 :             PL_DHashTableOperate(&mDB, (nsHostKey *)recPtr, PL_DHASH_REMOVE);
     973                 :             // If record is on a Queue, remove it and then deref it
     974               0 :             if (recPtr->next != recPtr) {
     975               0 :                 PR_REMOVE_LINK(recPtr);
     976               0 :                 NS_RELEASE(recPtr);
     977                 :             }
     978                 :         }
     979                 :     }
     980               0 : }
     981                 : 
     982                 : //----------------------------------------------------------------------------
     983                 : 
     984                 : void
     985             273 : nsHostResolver::ThreadFunc(void *arg)
     986                 : {
     987             273 :     LOG(("nsHostResolver::ThreadFunc entering\n"));
     988                 : #if defined(RES_RETRY_ON_FAILURE)
     989             273 :     nsResState rs;
     990                 : #endif
     991             273 :     nsHostResolver *resolver = (nsHostResolver *)arg;
     992                 :     nsHostRecord *rec;
     993                 :     PRAddrInfo *ai;
     994            2752 :     while (resolver->GetHostToLookup(&rec)) {
     995            2206 :         LOG(("resolving %s ...\n", rec->host));
     996                 : 
     997            2206 :         PRIntn flags = PR_AI_ADDRCONFIG;
     998            2206 :         if (!(rec->flags & RES_CANON_NAME))
     999            2206 :             flags |= PR_AI_NOCANONNAME;
    1000                 : 
    1001            2206 :         TimeStamp startTime = TimeStamp::Now();
    1002                 : 
    1003            2206 :         ai = PR_GetAddrInfoByName(rec->host, rec->af, flags);
    1004                 : #if defined(RES_RETRY_ON_FAILURE)
    1005            2206 :         if (!ai && rs.Reset())
    1006               5 :             ai = PR_GetAddrInfoByName(rec->host, rec->af, flags);
    1007                 : #endif
    1008                 : 
    1009            2206 :         TimeDuration elapsed = TimeStamp::Now() - startTime;
    1010            2206 :         PRUint32 millis = static_cast<PRUint32>(elapsed.ToMilliseconds());
    1011                 : 
    1012                 :         // convert error code to nsresult.
    1013                 :         nsresult status;
    1014            2206 :         if (ai) {
    1015            2077 :             status = NS_OK;
    1016                 : 
    1017            2077 :             Telemetry::Accumulate(!rec->addr_info_gencnt ?
    1018                 :                                     Telemetry::DNS_LOOKUP_TIME :
    1019                 :                                     Telemetry::DNS_RENEWAL_TIME,
    1020            2077 :                                   millis);
    1021                 :         }
    1022                 :         else {
    1023             129 :             status = NS_ERROR_UNKNOWN_HOST;
    1024             129 :             Telemetry::Accumulate(Telemetry::DNS_FAILED_LOOKUP_TIME, millis);
    1025                 :         }
    1026                 :         
    1027            2206 :         resolver->OnLookupComplete(rec, status, ai);
    1028            2206 :         LOG(("lookup complete for %s ...\n", rec->host));
    1029                 :     }
    1030             273 :     NS_RELEASE(resolver);
    1031             273 :     LOG(("nsHostResolver::ThreadFunc exiting\n"));
    1032             273 : }
    1033                 : 
    1034                 : //----------------------------------------------------------------------------
    1035                 : 
    1036                 : nsresult
    1037            1444 : nsHostResolver::Create(PRUint32         maxCacheEntries,
    1038                 :                        PRUint32         maxCacheLifetime,
    1039                 :                        PRUint32         lifetimeGracePeriod,
    1040                 :                        nsHostResolver **result)
    1041                 : {
    1042                 : #if defined(PR_LOGGING)
    1043            1444 :     if (!gHostResolverLog)
    1044            1419 :         gHostResolverLog = PR_NewLogModule("nsHostResolver");
    1045                 : #endif
    1046                 : 
    1047                 :     nsHostResolver *res = new nsHostResolver(maxCacheEntries,
    1048                 :                                              maxCacheLifetime,
    1049            1444 :                                              lifetimeGracePeriod);
    1050            1444 :     if (!res)
    1051               0 :         return NS_ERROR_OUT_OF_MEMORY;
    1052            1444 :     NS_ADDREF(res);
    1053                 : 
    1054            1444 :     nsresult rv = res->Init();
    1055            1444 :     if (NS_FAILED(rv))
    1056               0 :         NS_RELEASE(res);
    1057                 : 
    1058            1444 :     *result = res;
    1059            1444 :     return rv;
    1060                 : }

Generated by: LCOV version 1.7