Back to index

lightning-sunbird  0.9+nobinonly
nsHostResolver.cpp
Go to the documentation of this file.
00001 /* vim:set ts=4 sw=4 sts=4 et cin: */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla.
00016  *
00017  * The Initial Developer of the Original Code is IBM Corporation.
00018  * Portions created by IBM Corporation are Copyright (C) 2003
00019  * IBM Corporation. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   IBM Corp.
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #if defined(MOZ_LOGGING)
00039 #define FORCE_PR_LOG
00040 #endif
00041 
00042 #if defined(HAVE_RES_NINIT)
00043 #include <sys/types.h>
00044 #include <netinet/in.h>
00045 #include <arpa/inet.h>   
00046 #include <arpa/nameser.h>
00047 #include <resolv.h>
00048 #define RES_RETRY_ON_FAILURE
00049 #endif
00050 
00051 #include <stdlib.h>
00052 #include "nsHostResolver.h"
00053 #include "nsNetError.h"
00054 #include "nsISupportsBase.h"
00055 #include "nsISupportsUtils.h"
00056 #include "nsAutoLock.h"
00057 #include "nsAutoPtr.h"
00058 #include "pratom.h"
00059 #include "prthread.h"
00060 #include "prerror.h"
00061 #include "prcvar.h"
00062 #include "prtime.h"
00063 #include "prlong.h"
00064 #include "prlog.h"
00065 #include "pldhash.h"
00066 #include "plstr.h"
00067 #include "nsURLHelper.h"
00068 
00069 //----------------------------------------------------------------------------
00070 
00071 #define MAX_THREADS 8
00072 #define IDLE_TIMEOUT PR_SecondsToInterval(60)
00073 
00074 //----------------------------------------------------------------------------
00075 
00076 #if defined(PR_LOGGING)
00077 static PRLogModuleInfo *gHostResolverLog = nsnull;
00078 #define LOG(args) PR_LOG(gHostResolverLog, PR_LOG_DEBUG, args)
00079 #else
00080 #define LOG(args)
00081 #endif
00082 
00083 //----------------------------------------------------------------------------
00084 
00085 static inline void
00086 MoveCList(PRCList &from, PRCList &to)
00087 {
00088     if (!PR_CLIST_IS_EMPTY(&from)) {
00089         to.next = from.next;
00090         to.prev = from.prev;
00091         to.next->prev = &to;
00092         to.prev->next = &to;
00093         PR_INIT_CLIST(&from);
00094     }             
00095 }
00096 
00097 static PRUint32
00098 NowInMinutes()
00099 {
00100     PRTime now = PR_Now(), minutes, factor;
00101     LL_I2L(factor, 60 * PR_USEC_PER_SEC);
00102     LL_DIV(minutes, now, factor);
00103     PRUint32 result;
00104     LL_L2UI(result, minutes);
00105     return result;
00106 }
00107 
00108 //----------------------------------------------------------------------------
00109 
00110 #if defined(RES_RETRY_ON_FAILURE)
00111 
00112 // this class represents the resolver state for a given thread.  if we
00113 // encounter a lookup failure, then we can invoke the Reset method on an
00114 // instance of this class to reset the resolver (in case /etc/resolv.conf
00115 // for example changed).  this is mainly an issue on GNU systems since glibc
00116 // only reads in /etc/resolv.conf once per thread.  it may be an issue on
00117 // other systems as well.
00118 
00119 class nsResState
00120 {
00121 public:
00122     nsResState()
00123         // initialize mLastReset to the time when this object
00124         // is created.  this means that a reset will not occur
00125         // if a thread is too young.  the alternative would be
00126         // to initialize this to the beginning of time, so that
00127         // the first failure would cause a reset, but since the
00128         // thread would have just started up, it likely would
00129         // already have current /etc/resolv.conf info.
00130         : mLastReset(PR_IntervalNow())
00131     {
00132     }
00133 
00134     PRBool Reset()
00135     {
00136         // reset no more than once per second
00137         if (PR_IntervalToSeconds(PR_IntervalNow() - mLastReset) < 1)
00138             return PR_FALSE;
00139 
00140         LOG(("calling res_ninit\n"));
00141 
00142         mLastReset = PR_IntervalNow();
00143         return (res_ninit(&_res) == 0);
00144     }
00145 
00146 private:
00147     PRIntervalTime mLastReset;
00148 };
00149 
00150 #endif // RES_RETRY_ON_FAILURE
00151 
00152 //----------------------------------------------------------------------------
00153 
00154 // this macro filters out any flags that are not used when constructing the
00155 // host key.  the significant flags are those that would affect the resulting
00156 // host record (i.e., the flags that are passed down to PR_GetAddrInfoByName).
00157 #define RES_KEY_FLAGS(_f) ((_f) & nsHostResolver::RES_CANON_NAME)
00158 
00159 nsresult
00160 nsHostRecord::Create(const nsHostKey *key, nsHostRecord **result)
00161 {
00162     size_t hostLen = strlen(key->host) + 1;
00163     size_t size = hostLen + sizeof(nsHostRecord);
00164 
00165     nsHostRecord *rec = (nsHostRecord*) ::operator new(size);
00166     if (!rec)
00167         return NS_ERROR_OUT_OF_MEMORY;
00168 
00169     rec->host = ((char *) rec) + sizeof(nsHostRecord);
00170     rec->flags = RES_KEY_FLAGS(key->flags);
00171     rec->af = key->af;
00172 
00173     rec->_refc = 1; // addref
00174     rec->addr_info = nsnull;
00175     rec->addr = nsnull;
00176     rec->expiration = NowInMinutes();
00177     rec->resolving = PR_FALSE;
00178     PR_INIT_CLIST(rec);
00179     PR_INIT_CLIST(&rec->callbacks);
00180     memcpy((char *) rec->host, key->host, hostLen);
00181 
00182     *result = rec;
00183     return NS_OK;
00184 }
00185 
00186 nsHostRecord::~nsHostRecord()
00187 {
00188     if (addr_info)
00189         PR_FreeAddrInfo(addr_info);
00190     if (addr)
00191         free(addr);
00192 }
00193 
00194 //----------------------------------------------------------------------------
00195 
00196 struct nsHostDBEnt : PLDHashEntryHdr
00197 {
00198     nsHostRecord *rec;
00199 };
00200 
00201 PR_STATIC_CALLBACK(const void *)
00202 HostDB_GetKey(PLDHashTable *table, PLDHashEntryHdr *entry)
00203 {
00204     nsHostDBEnt *he = NS_STATIC_CAST(nsHostDBEnt *, entry);
00205     return NS_STATIC_CAST(const nsHostKey *, he->rec);
00206 }
00207 
00208 PR_STATIC_CALLBACK(PLDHashNumber)
00209 HostDB_HashKey(PLDHashTable *table, const void *key)
00210 {
00211     const nsHostKey *hk = NS_STATIC_CAST(const nsHostKey *, key);
00212     return PL_DHashStringKey(table, hk->host) ^ hk->flags ^ hk->af;
00213 }
00214 
00215 PR_STATIC_CALLBACK(PRBool)
00216 HostDB_MatchEntry(PLDHashTable *table,
00217                   const PLDHashEntryHdr *entry,
00218                   const void *key)
00219 {
00220     const nsHostDBEnt *he = NS_STATIC_CAST(const nsHostDBEnt *, entry);
00221     const nsHostKey *hk = NS_STATIC_CAST(const nsHostKey *, key); 
00222 
00223     return !strcmp(he->rec->host, hk->host) &&
00224             he->rec->flags == hk->flags &&
00225             he->rec->af == hk->af;
00226 }
00227 
00228 PR_STATIC_CALLBACK(void)
00229 HostDB_MoveEntry(PLDHashTable *table,
00230                  const PLDHashEntryHdr *from,
00231                  PLDHashEntryHdr *to)
00232 {
00233     NS_STATIC_CAST(nsHostDBEnt *, to)->rec =
00234             NS_STATIC_CAST(const nsHostDBEnt *, from)->rec;
00235 }
00236 
00237 PR_STATIC_CALLBACK(void)
00238 HostDB_ClearEntry(PLDHashTable *table,
00239                   PLDHashEntryHdr *entry)
00240 {
00241     LOG(("evicting record\n"));
00242     nsHostDBEnt *he = NS_STATIC_CAST(nsHostDBEnt *, entry);
00243 #if defined(DEBUG) && defined(PR_LOGGING)
00244     if (!he->rec->addr_info)
00245         LOG(("%s: => no addr_info\n", he->rec->host));
00246     else {
00247         PRInt32 now = (PRInt32) NowInMinutes();
00248         PRInt32 diff = (PRInt32) he->rec->expiration - now;
00249         LOG(("%s: exp=%d => %s\n",
00250             he->rec->host, diff,
00251             PR_GetCanonNameFromAddrInfo(he->rec->addr_info)));
00252         void *iter = nsnull;
00253         PRNetAddr addr;
00254         char buf[64];
00255         for (;;) {
00256             iter = PR_EnumerateAddrInfo(iter, he->rec->addr_info, 0, &addr);
00257             if (!iter)
00258                 break;
00259             PR_NetAddrToString(&addr, buf, sizeof(buf));
00260             LOG(("  %s\n", buf));
00261         }
00262     }
00263 #endif
00264     NS_RELEASE(he->rec);
00265 }
00266 
00267 PR_STATIC_CALLBACK(PRBool)
00268 HostDB_InitEntry(PLDHashTable *table,
00269                  PLDHashEntryHdr *entry,
00270                  const void *key)
00271 {
00272     nsHostDBEnt *he = NS_STATIC_CAST(nsHostDBEnt *, entry);
00273     nsHostRecord::Create(NS_STATIC_CAST(const nsHostKey *, key), &he->rec);
00274     return PR_TRUE;
00275 }
00276 
00277 static PLDHashTableOps gHostDB_ops =
00278 {
00279     PL_DHashAllocTable,
00280     PL_DHashFreeTable,
00281     HostDB_GetKey,
00282     HostDB_HashKey,
00283     HostDB_MatchEntry,
00284     HostDB_MoveEntry,
00285     HostDB_ClearEntry,
00286     PL_DHashFinalizeStub,
00287     HostDB_InitEntry,
00288 };
00289 
00290 PR_STATIC_CALLBACK(PLDHashOperator)
00291 HostDB_RemoveEntry(PLDHashTable *table,
00292                    PLDHashEntryHdr *hdr,
00293                    PRUint32 number,
00294                    void *arg)
00295 {
00296     return PL_DHASH_REMOVE;
00297 }
00298 
00299 //----------------------------------------------------------------------------
00300 
00301 nsHostResolver::nsHostResolver(PRUint32 maxCacheEntries,
00302                                PRUint32 maxCacheLifetime)
00303     : mMaxCacheEntries(maxCacheEntries)
00304     , mMaxCacheLifetime(maxCacheLifetime)
00305     , mLock(nsnull)
00306     , mIdleThreadCV(nsnull)
00307     , mHaveIdleThread(PR_FALSE)
00308     , mThreadCount(0)
00309     , mEvictionQSize(0)
00310     , mShutdown(PR_TRUE)
00311 {
00312     mCreationTime = PR_Now();
00313     PR_INIT_CLIST(&mPendingQ);
00314     PR_INIT_CLIST(&mEvictionQ);
00315 }
00316 
00317 nsHostResolver::~nsHostResolver()
00318 {
00319     if (mIdleThreadCV)
00320         PR_DestroyCondVar(mIdleThreadCV);
00321 
00322     if (mLock)
00323         PR_DestroyLock(mLock);
00324 
00325     PL_DHashTableFinish(&mDB);
00326 }
00327 
00328 nsresult
00329 nsHostResolver::Init()
00330 {
00331     mLock = PR_NewLock();
00332     if (!mLock)
00333         return NS_ERROR_OUT_OF_MEMORY;
00334 
00335     mIdleThreadCV = PR_NewCondVar(mLock);
00336     if (!mIdleThreadCV)
00337         return NS_ERROR_OUT_OF_MEMORY;
00338 
00339     PL_DHashTableInit(&mDB, &gHostDB_ops, nsnull, sizeof(nsHostDBEnt), 0);
00340 
00341     mShutdown = PR_FALSE;
00342     return NS_OK;
00343 }
00344 
00345 void
00346 nsHostResolver::Shutdown()
00347 {
00348     LOG(("nsHostResolver::Shutdown\n"));
00349 
00350     PRCList pendingQ;
00351     PR_INIT_CLIST(&pendingQ);
00352     {
00353         nsAutoLock lock(mLock);
00354         
00355         mShutdown = PR_TRUE;
00356 
00357         MoveCList(mPendingQ, pendingQ);
00358 
00359         if (mHaveIdleThread)
00360             PR_NotifyCondVar(mIdleThreadCV);
00361         
00362         // empty host database
00363         PL_DHashTableEnumerate(&mDB, HostDB_RemoveEntry, nsnull);
00364     }
00365 
00366     // loop through pending queue, erroring out pending lookups.
00367     if (!PR_CLIST_IS_EMPTY(&pendingQ)) {
00368         PRCList *node = pendingQ.next;
00369         while (node != &pendingQ) {
00370             nsHostRecord *rec = NS_STATIC_CAST(nsHostRecord *, node);
00371             node = node->next;
00372             OnLookupComplete(rec, NS_ERROR_ABORT, nsnull);
00373         }
00374     }
00375 }
00376 
00377 nsresult
00378 nsHostResolver::ResolveHost(const char            *host,
00379                             PRUint16               flags,
00380                             PRUint16               af,
00381                             nsResolveHostCallback *callback)
00382 {
00383     NS_ENSURE_TRUE(host && *host, NS_ERROR_UNEXPECTED);
00384 
00385     LOG(("nsHostResolver::ResolveHost [host=%s]\n", host));
00386 
00387     // ensure that we are working with a valid hostname before proceeding.  see
00388     // bug 304904 for details.
00389     if (!net_IsValidHostName(nsDependentCString(host)))
00390         return NS_ERROR_UNKNOWN_HOST;
00391 
00392     // if result is set inside the lock, then we need to issue the
00393     // callback before returning.
00394     nsRefPtr<nsHostRecord> result;
00395     nsresult status = NS_OK, rv = NS_OK;
00396     {
00397         nsAutoLock lock(mLock);
00398 
00399         if (mShutdown)
00400             rv = NS_ERROR_NOT_INITIALIZED;
00401         else {
00402             PRNetAddr tempAddr;
00403 
00404             // unfortunately, PR_StringToNetAddr does not properly initialize
00405             // the output buffer in the case of IPv6 input.  see bug 223145.
00406             memset(&tempAddr, 0, sizeof(PRNetAddr));
00407             
00408             // check to see if there is already an entry for this |host|
00409             // in the hash table.  if so, then check to see if we can't
00410             // just reuse the lookup result.  otherwise, if there are
00411             // any pending callbacks, then add to pending callbacks queue,
00412             // and return.  otherwise, add ourselves as first pending
00413             // callback, and proceed to do the lookup.
00414 
00415             nsHostKey key = { host, flags, af };
00416             nsHostDBEnt *he = NS_STATIC_CAST(nsHostDBEnt *,
00417                     PL_DHashTableOperate(&mDB, &key, PL_DHASH_ADD));
00418 
00419             // if the record is null, then HostDB_InitEntry failed.
00420             if (!he || !he->rec)
00421                 rv = NS_ERROR_OUT_OF_MEMORY;
00422             // do we have a cached result that we can reuse?
00423             else if (!(flags & RES_BYPASS_CACHE) &&
00424                      he->rec->HasResult() &&
00425                      NowInMinutes() <= he->rec->expiration) {
00426                 LOG(("using cached record\n"));
00427                 // put reference to host record on stack...
00428                 result = he->rec;
00429             }
00430             // try parsing the host name as an IP address literal to short
00431             // circuit full host resolution.  (this is necessary on some
00432             // platforms like Win9x.  see bug 219376 for more details.)
00433             else if (PR_StringToNetAddr(host, &tempAddr) == PR_SUCCESS) {
00434                 // ok, just copy the result into the host record, and be done
00435                 // with it! ;-)
00436                 he->rec->addr = (PRNetAddr *) malloc(sizeof(PRNetAddr));
00437                 if (!he->rec->addr)
00438                     status = NS_ERROR_OUT_OF_MEMORY;
00439                 else
00440                     memcpy(he->rec->addr, &tempAddr, sizeof(PRNetAddr));
00441                 // put reference to host record on stack...
00442                 result = he->rec;
00443             }
00444             // otherwise, hit the resolver...
00445             else {
00446                 // add callback to the list of pending callbacks
00447                 PR_APPEND_LINK(callback, &he->rec->callbacks);
00448 
00449                 if (!he->rec->resolving) {
00450                     rv = IssueLookup(he->rec);
00451                     if (NS_FAILED(rv))
00452                         PR_REMOVE_AND_INIT_LINK(callback);
00453                 }
00454             }
00455         }
00456     }
00457     if (result)
00458         callback->OnLookupComplete(this, result, status);
00459     return rv;
00460 }
00461 
00462 void
00463 nsHostResolver::DetachCallback(const char            *host,
00464                                PRUint16               flags,
00465                                PRUint16               af,
00466                                nsResolveHostCallback *callback,
00467                                nsresult               status)
00468 {
00469     nsRefPtr<nsHostRecord> rec;
00470     {
00471         nsAutoLock lock(mLock);
00472 
00473         nsHostKey key = { host, flags, af };
00474         nsHostDBEnt *he = NS_STATIC_CAST(nsHostDBEnt *,
00475                 PL_DHashTableOperate(&mDB, &key, PL_DHASH_LOOKUP));
00476         if (he && he->rec) {
00477             // walk list looking for |callback|... we cannot assume
00478             // that it will be there!
00479             PRCList *node = he->rec->callbacks.next;
00480             while (node != &he->rec->callbacks) {
00481                 if (NS_STATIC_CAST(nsResolveHostCallback *, node) == callback) {
00482                     PR_REMOVE_LINK(callback);
00483                     rec = he->rec;
00484                     break;
00485                 }
00486                 node = node->next;
00487             }
00488         }
00489     }
00490 
00491     // complete callback with the given status code; this would only be done if
00492     // the record was in the process of being resolved.
00493     if (rec)
00494         callback->OnLookupComplete(this, rec, status);
00495 }
00496 
00497 nsresult
00498 nsHostResolver::IssueLookup(nsHostRecord *rec)
00499 {
00500     NS_ASSERTION(!rec->resolving, "record is already being resolved"); 
00501 
00502     // add rec to mPendingQ, possibly removing it from mEvictionQ.
00503     // if rec is on mEvictionQ, then we can just move the owning
00504     // reference over to mPendingQ.
00505     if (rec->next == rec)
00506         NS_ADDREF(rec);
00507     else {
00508         PR_REMOVE_LINK(rec);
00509         mEvictionQSize--;
00510     }
00511     PR_APPEND_LINK(rec, &mPendingQ);
00512     rec->resolving = PR_TRUE;
00513 
00514     if (mHaveIdleThread) {
00515         // wake up idle thread to process this lookup
00516         PR_NotifyCondVar(mIdleThreadCV);
00517     }
00518     else if (mThreadCount < MAX_THREADS) {
00519         // dispatch new worker thread
00520         NS_ADDREF_THIS(); // owning reference passed to thread
00521         mThreadCount++;
00522         PRThread *thr = PR_CreateThread(PR_SYSTEM_THREAD,
00523                                         ThreadFunc,
00524                                         this,
00525                                         PR_PRIORITY_NORMAL,
00526                                         PR_GLOBAL_THREAD,
00527                                         PR_UNJOINABLE_THREAD,
00528                                         0);
00529         if (!thr) {
00530             mThreadCount--;
00531             NS_RELEASE_THIS();
00532             return NS_ERROR_OUT_OF_MEMORY;
00533         }
00534     }
00535 
00536     return NS_OK;
00537 }
00538 
00539 PRBool
00540 nsHostResolver::GetHostToLookup(nsHostRecord **result)
00541 {
00542     nsAutoLock lock(mLock);
00543 
00544     PRIntervalTime start = PR_IntervalNow(), timeout = IDLE_TIMEOUT;
00545     //
00546     // wait for one or more of the following to occur:
00547     //  (1) the pending queue has a host record to process
00548     //  (2) the shutdown flag has been set
00549     //  (3) the thread has been idle for too long
00550     //
00551     // PR_WaitCondVar will return when any of these conditions is true.
00552     //
00553     while (PR_CLIST_IS_EMPTY(&mPendingQ) && !mHaveIdleThread && !mShutdown) {
00554         // become the idle thread and wait for a lookup
00555         mHaveIdleThread = PR_TRUE;
00556         PR_WaitCondVar(mIdleThreadCV, timeout);
00557         mHaveIdleThread = PR_FALSE;
00558 
00559         PRIntervalTime delta = PR_IntervalNow() - start;
00560         if (delta >= timeout)
00561             break;
00562         timeout -= delta;
00563         start += delta;
00564     }
00565 
00566     if (!PR_CLIST_IS_EMPTY(&mPendingQ)) {
00567         // remove next record from mPendingQ; hand over owning reference.
00568         *result = NS_STATIC_CAST(nsHostRecord *, mPendingQ.next);
00569         PR_REMOVE_AND_INIT_LINK(*result);
00570         return PR_TRUE;
00571     }
00572 
00573     // tell thread to exit...
00574     mThreadCount--;
00575     return PR_FALSE;
00576 }
00577 
00578 void
00579 nsHostResolver::OnLookupComplete(nsHostRecord *rec, nsresult status, PRAddrInfo *result)
00580 {
00581     // get the list of pending callbacks for this lookup, and notify
00582     // them that the lookup is complete.
00583     PRCList cbs;
00584     PR_INIT_CLIST(&cbs);
00585     {
00586         nsAutoLock lock(mLock);
00587 
00588         // grab list of callbacks to notify
00589         MoveCList(rec->callbacks, cbs);
00590 
00591         // update record fields
00592         rec->addr_info = result;
00593         rec->expiration = NowInMinutes() + mMaxCacheLifetime;
00594         rec->resolving = PR_FALSE;
00595         
00596         if (rec->addr_info) {
00597             // add to mEvictionQ
00598             PR_APPEND_LINK(rec, &mEvictionQ);
00599             NS_ADDREF(rec);
00600             if (mEvictionQSize < mMaxCacheEntries)
00601                 mEvictionQSize++;
00602             else {
00603                 // remove first element on mEvictionQ
00604                 nsHostRecord *head =
00605                     NS_STATIC_CAST(nsHostRecord *, PR_LIST_HEAD(&mEvictionQ));
00606                 PR_REMOVE_AND_INIT_LINK(head);
00607                 PL_DHashTableOperate(&mDB, (nsHostKey *) head, PL_DHASH_REMOVE);
00608                 // release reference to rec owned by mEvictionQ
00609                 NS_RELEASE(head);
00610             }
00611         }
00612     }
00613 
00614     if (!PR_CLIST_IS_EMPTY(&cbs)) {
00615         PRCList *node = cbs.next;
00616         while (node != &cbs) {
00617             nsResolveHostCallback *callback =
00618                     NS_STATIC_CAST(nsResolveHostCallback *, node);
00619             node = node->next;
00620             callback->OnLookupComplete(this, rec, status);
00621             // NOTE: callback must not be dereferenced after this point!!
00622         }
00623     }
00624 
00625     NS_RELEASE(rec);
00626 }
00627 
00628 //----------------------------------------------------------------------------
00629 
00630 void PR_CALLBACK
00631 nsHostResolver::ThreadFunc(void *arg)
00632 {
00633     LOG(("nsHostResolver::ThreadFunc entering\n"));
00634 #if defined(RES_RETRY_ON_FAILURE)
00635     nsResState rs;
00636 #endif
00637 
00638     nsHostResolver *resolver = (nsHostResolver *) arg;
00639     nsHostRecord *rec;
00640     PRAddrInfo *ai;
00641     while (resolver->GetHostToLookup(&rec)) {
00642         LOG(("resolving %s ...\n", rec->host));
00643 
00644         PRIntn flags = PR_AI_ADDRCONFIG;
00645         if (!(rec->flags & RES_CANON_NAME))
00646             flags |= PR_AI_NOCANONNAME;
00647 
00648         ai = PR_GetAddrInfoByName(rec->host, rec->af, flags);
00649 #if defined(RES_RETRY_ON_FAILURE)
00650         if (!ai && rs.Reset())
00651             ai = PR_GetAddrInfoByName(rec->host, rec->af, flags);
00652 #endif
00653 
00654         // convert error code to nsresult.
00655         nsresult status = ai ? NS_OK : NS_ERROR_UNKNOWN_HOST;
00656         resolver->OnLookupComplete(rec, status, ai);
00657     }
00658     NS_RELEASE(resolver);
00659     LOG(("nsHostResolver::ThreadFunc exiting\n"));
00660 }
00661 
00662 //----------------------------------------------------------------------------
00663 
00664 nsresult
00665 nsHostResolver::Create(PRUint32         maxCacheEntries,
00666                        PRUint32         maxCacheLifetime,
00667                        nsHostResolver **result)
00668 {
00669 #if defined(PR_LOGGING)
00670     if (!gHostResolverLog)
00671         gHostResolverLog = PR_NewLogModule("nsHostResolver");
00672 #endif
00673 
00674     nsHostResolver *res = new nsHostResolver(maxCacheEntries,
00675                                              maxCacheLifetime);
00676     if (!res)
00677         return NS_ERROR_OUT_OF_MEMORY;
00678     NS_ADDREF(res);
00679 
00680     nsresult rv = res->Init();
00681     if (NS_FAILED(rv))
00682         NS_RELEASE(res);
00683 
00684     *result = res;
00685     return rv;
00686 }