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(HAVE_RES_NINIT)
8 #include <netinet/in.h>
10 #include <arpa/nameser.h>
12 #define RES_RETRY_ON_FAILURE
17 #include "nsHostResolver.h"
19 #include "nsISupportsBase.h"
20 #include "nsISupportsUtils.h"
21 #include "nsAutoPtr.h"
22 #include "nsPrintfCString.h"
29 #include "nsURLHelper.h"
30 #include "nsThreadUtils.h"
31 #include "GetAddrInfo.h"
33 #include "mozilla/HashFunctions.h"
34 #include "mozilla/TimeStamp.h"
35 #include "mozilla/Telemetry.h"
36 #include "mozilla/VisualEventTracer.h"
37 #include "mozilla/DebugOnly.h"
38 #include "mozilla/Preferences.h"
40 using namespace mozilla
;
41 using namespace mozilla::net
;
43 // None of our implementations expose a TTL for negative responses, so we use a
45 static const unsigned int NEGATIVE_RECORD_LIFETIME
= 60;
47 //----------------------------------------------------------------------------
49 // Use a persistent thread pool in order to avoid spinning up new threads all the time.
50 // In particular, thread creation results in a res_init() call from libc which is
53 // The pool dynamically grows between 0 and MAX_RESOLVER_THREADS in size. New requests
54 // go first to an idle thread. If that cannot be found and there are fewer than MAX_RESOLVER_THREADS
55 // currently in the pool a new thread is created for high priority requests. If
56 // the new request is at a lower priority a new thread will only be created if
57 // there are fewer than HighThreadThreshold currently outstanding. If a thread cannot be
58 // created or an idle thread located for the request it is queued.
60 // When the pool is greater than HighThreadThreshold in size a thread will be destroyed after
61 // ShortIdleTimeoutSeconds of idle time. Smaller pools use LongIdleTimeoutSeconds for a
64 #define HighThreadThreshold MAX_RESOLVER_THREADS_FOR_ANY_PRIORITY
65 #define LongIdleTimeoutSeconds 300 // for threads 1 -> HighThreadThreshold
66 #define ShortIdleTimeoutSeconds 60 // for threads HighThreadThreshold+1 -> MAX_RESOLVER_THREADS
68 PR_STATIC_ASSERT (HighThreadThreshold
<= MAX_RESOLVER_THREADS
);
70 //----------------------------------------------------------------------------
72 #if defined(PR_LOGGING)
73 static PRLogModuleInfo
*gHostResolverLog
= nullptr;
74 #define LOG(args) PR_LOG(gHostResolverLog, PR_LOG_DEBUG, args)
79 #define LOG_HOST(host, interface) host, \
80 (interface && interface[0] != '\0') ? " on interface " : "", \
81 (interface && interface[0] != '\0') ? interface : ""
83 //----------------------------------------------------------------------------
86 MoveCList(PRCList
&from
, PRCList
&to
)
88 if (!PR_CLIST_IS_EMPTY(&from
)) {
97 //----------------------------------------------------------------------------
99 #if defined(RES_RETRY_ON_FAILURE)
101 // this class represents the resolver state for a given thread. if we
102 // encounter a lookup failure, then we can invoke the Reset method on an
103 // instance of this class to reset the resolver (in case /etc/resolv.conf
104 // for example changed). this is mainly an issue on GNU systems since glibc
105 // only reads in /etc/resolv.conf once per thread. it may be an issue on
106 // other systems as well.
112 // initialize mLastReset to the time when this object
113 // is created. this means that a reset will not occur
114 // if a thread is too young. the alternative would be
115 // to initialize this to the beginning of time, so that
116 // the first failure would cause a reset, but since the
117 // thread would have just started up, it likely would
118 // already have current /etc/resolv.conf info.
119 : mLastReset(PR_IntervalNow())
125 // reset no more than once per second
126 if (PR_IntervalToSeconds(PR_IntervalNow() - mLastReset
) < 1)
129 LOG(("Calling 'res_ninit'.\n"));
131 mLastReset
= PR_IntervalNow();
132 return (res_ninit(&_res
) == 0);
136 PRIntervalTime mLastReset
;
139 #endif // RES_RETRY_ON_FAILURE
141 //----------------------------------------------------------------------------
144 IsHighPriority(uint16_t flags
)
146 return !(flags
& (nsHostResolver::RES_PRIORITY_LOW
| nsHostResolver::RES_PRIORITY_MEDIUM
));
150 IsMediumPriority(uint16_t flags
)
152 return flags
& nsHostResolver::RES_PRIORITY_MEDIUM
;
156 IsLowPriority(uint16_t flags
)
158 return flags
& nsHostResolver::RES_PRIORITY_LOW
;
161 //----------------------------------------------------------------------------
162 // this macro filters out any flags that are not used when constructing the
163 // host key. the significant flags are those that would affect the resulting
164 // host record (i.e., the flags that are passed down to PR_GetAddrInfoByName).
165 #define RES_KEY_FLAGS(_f) ((_f) & nsHostResolver::RES_CANON_NAME)
167 nsHostRecord::nsHostRecord(const nsHostKey
*key
)
168 : addr_info_lock("nsHostRecord.addr_info_lock")
169 , addr_info_gencnt(0)
175 , usingAnyThread(false)
180 , mBlacklistedCount(0)
181 , mResolveAgain(false)
183 host
= ((char *) this) + sizeof(nsHostRecord
);
184 memcpy((char *) host
, key
->host
, strlen(key
->host
) + 1);
187 netInterface
= host
+ strlen(key
->host
) + 1;
188 memcpy((char *) netInterface
, key
->netInterface
,
189 strlen(key
->netInterface
) + 1);
191 PR_INIT_CLIST(&callbacks
);
195 nsHostRecord::Create(const nsHostKey
*key
, nsHostRecord
**result
)
197 size_t hostLen
= strlen(key
->host
) + 1;
198 size_t netInterfaceLen
= strlen(key
->netInterface
) + 1;
199 size_t size
= hostLen
+ netInterfaceLen
+ sizeof(nsHostRecord
);
201 // Use placement new to create the object with room for the hostname and
202 // network interface name allocated after it.
203 void *place
= ::operator new(size
);
204 *result
= new(place
) nsHostRecord(key
);
207 MOZ_EVENT_TRACER_NAME_OBJECT(*result
, key
->host
);
213 nsHostRecord::SetExpiration(const mozilla::TimeStamp
& now
, unsigned int valid
, unsigned int grace
)
216 mGraceStart
= now
+ TimeDuration::FromSeconds(valid
);
217 mValidEnd
= now
+ TimeDuration::FromSeconds(valid
+ grace
);
221 nsHostRecord::CopyExpirationTimesAndFlagsFrom(const nsHostRecord
*aFromHostRecord
)
223 // This is used to copy information from a cache entry to a record. All
224 // information necessary for HasUsableRecord needs to be copied.
225 mValidStart
= aFromHostRecord
->mValidStart
;
226 mValidEnd
= aFromHostRecord
->mValidEnd
;
227 mGraceStart
= aFromHostRecord
->mGraceStart
;
228 mDoomed
= aFromHostRecord
->mDoomed
;
231 nsHostRecord::~nsHostRecord()
233 Telemetry::Accumulate(Telemetry::DNS_BLACKLIST_COUNT
, mBlacklistedCount
);
239 nsHostRecord::Blacklisted(NetAddr
*aQuery
)
242 LOG(("Checking blacklist for host [%s%s%s], host record [%p].\n",
243 LOG_HOST(host
, netInterface
), this));
245 // skip the string conversion for the common case of no blacklist
246 if (!mBlacklistedItems
.Length()) {
250 char buf
[kIPv6CStrBufSize
];
251 if (!NetAddrToString(aQuery
, buf
, sizeof(buf
))) {
254 nsDependentCString
strQuery(buf
);
256 for (uint32_t i
= 0; i
< mBlacklistedItems
.Length(); i
++) {
257 if (mBlacklistedItems
.ElementAt(i
).Equals(strQuery
)) {
258 LOG(("Address [%s] is blacklisted for host [%s%s%s].\n", buf
,
259 LOG_HOST(host
, netInterface
)));
268 nsHostRecord::ReportUnusable(NetAddr
*aAddress
)
271 LOG(("Adding address to blacklist for host [%s%s%s], host record [%p].\n",
272 LOG_HOST(host
, netInterface
), this));
279 char buf
[kIPv6CStrBufSize
];
280 if (NetAddrToString(aAddress
, buf
, sizeof(buf
))) {
281 LOG(("Successfully adding address [%s] to blacklist for host "
282 "[%s%s%s].\n", buf
, LOG_HOST(host
, netInterface
)));
283 mBlacklistedItems
.AppendElement(nsCString(buf
));
288 nsHostRecord::ResetBlacklist()
291 LOG(("Resetting blacklist for host [%s%s%s], host record [%p].\n",
292 LOG_HOST(host
, netInterface
), this));
293 mBlacklistedItems
.Clear();
296 nsHostRecord::ExpirationStatus
297 nsHostRecord::CheckExpiration(const mozilla::TimeStamp
& now
) const {
298 if (!mGraceStart
.IsNull() && now
>= mGraceStart
299 && !mValidEnd
.IsNull() && now
< mValidEnd
) {
300 return nsHostRecord::EXP_GRACE
;
301 } else if (!mValidEnd
.IsNull() && now
< mValidEnd
) {
302 return nsHostRecord::EXP_VALID
;
305 return nsHostRecord::EXP_EXPIRED
;
310 nsHostRecord::HasUsableResult(const mozilla::TimeStamp
& now
, uint16_t queryFlags
) const
316 // don't use cached negative results for high priority queries.
317 if (negative
&& IsHighPriority(queryFlags
)) {
321 if (CheckExpiration(now
) == EXP_EXPIRED
) {
325 return addr_info
|| addr
|| negative
;
329 SizeOfResolveHostCallbackListExcludingHead(const PRCList
*head
,
330 MallocSizeOf mallocSizeOf
)
333 PRCList
*curr
= head
->next
;
334 while (curr
!= head
) {
335 nsResolveHostCallback
*callback
=
336 static_cast<nsResolveHostCallback
*>(curr
);
337 n
+= callback
->SizeOfIncludingThis(mallocSizeOf
);
344 nsHostRecord::SizeOfIncludingThis(MallocSizeOf mallocSizeOf
) const
346 size_t n
= mallocSizeOf(this);
348 // The |host| field (inherited from nsHostKey) actually points to extra
349 // memory that is allocated beyond the end of the nsHostRecord (see
350 // nsHostRecord::Create()). So it will be included in the
351 // |mallocSizeOf(this)| call above.
353 n
+= SizeOfResolveHostCallbackListExcludingHead(&callbacks
, mallocSizeOf
);
354 n
+= addr_info
? addr_info
->SizeOfIncludingThis(mallocSizeOf
) : 0;
355 n
+= mallocSizeOf(addr
);
357 n
+= mBlacklistedItems
.SizeOfExcludingThis(mallocSizeOf
);
358 for (size_t i
= 0; i
< mBlacklistedItems
.Length(); i
++) {
359 n
+= mBlacklistedItems
[i
].SizeOfExcludingThisMustBeUnshared(mallocSizeOf
);
364 nsHostRecord::DnsPriority
365 nsHostRecord::GetPriority(uint16_t aFlags
)
367 if (IsHighPriority(aFlags
)){
368 return nsHostRecord::DNS_PRIORITY_HIGH
;
369 } else if (IsMediumPriority(aFlags
)) {
370 return nsHostRecord::DNS_PRIORITY_MEDIUM
;
373 return nsHostRecord::DNS_PRIORITY_LOW
;
376 // Returns true if the entry can be removed, or false if it should be left.
377 // Sets mResolveAgain true for entries being resolved right now.
379 nsHostRecord::RemoveOrRefresh()
383 // The request has been passed to the OS resolver. The resultant DNS
384 // record should be considered stale and not trusted; set a flag to
385 // ensure it is called again.
386 mResolveAgain
= true;
388 // if Onqueue is true, the host entry is already added to the cache
389 // but is still pending to get resolved: just leave it in hash.
392 // Already resolved; not in a pending state; remove from cache.
396 //----------------------------------------------------------------------------
398 struct nsHostDBEnt
: PLDHashEntryHdr
404 HostDB_HashKey(PLDHashTable
*table
, const void *key
)
406 const nsHostKey
*hk
= static_cast<const nsHostKey
*>(key
);
407 return AddToHash(HashString(hk
->host
), RES_KEY_FLAGS(hk
->flags
), hk
->af
,
408 HashString(hk
->netInterface
));
412 HostDB_MatchEntry(PLDHashTable
*table
,
413 const PLDHashEntryHdr
*entry
,
416 const nsHostDBEnt
*he
= static_cast<const nsHostDBEnt
*>(entry
);
417 const nsHostKey
*hk
= static_cast<const nsHostKey
*>(key
);
419 return !strcmp(he
->rec
->host
, hk
->host
) &&
420 RES_KEY_FLAGS (he
->rec
->flags
) == RES_KEY_FLAGS(hk
->flags
) &&
421 he
->rec
->af
== hk
->af
&&
422 !strcmp(he
->rec
->netInterface
, hk
->netInterface
);
426 HostDB_MoveEntry(PLDHashTable
*table
,
427 const PLDHashEntryHdr
*from
,
430 static_cast<nsHostDBEnt
*>(to
)->rec
=
431 static_cast<const nsHostDBEnt
*>(from
)->rec
;
435 HostDB_ClearEntry(PLDHashTable
*table
,
436 PLDHashEntryHdr
*entry
)
438 nsHostDBEnt
*he
= static_cast<nsHostDBEnt
*>(entry
);
439 MOZ_ASSERT(he
, "nsHostDBEnt is null!");
441 nsHostRecord
*hr
= he
->rec
;
442 MOZ_ASSERT(hr
, "nsHostDBEnt has null host record!");
444 LOG(("Clearing cache db entry for host [%s%s%s].\n",
445 LOG_HOST(hr
->host
, hr
->netInterface
)));
446 #if defined(DEBUG) && defined(PR_LOGGING)
448 MutexAutoLock
lock(hr
->addr_info_lock
);
449 if (!hr
->addr_info
) {
450 LOG(("No address info for host [%s%s%s].\n",
451 LOG_HOST(hr
->host
, hr
->netInterface
)));
453 if (!hr
->mValidEnd
.IsNull()) {
454 TimeDuration diff
= hr
->mValidEnd
- TimeStamp::NowLoRes();
455 LOG(("Record for host [%s%s%s] expires in %f seconds.\n",
456 LOG_HOST(hr
->host
, hr
->netInterface
),
459 LOG(("Record for host [%s%s%s] not yet valid.\n",
460 LOG_HOST(hr
->host
, hr
->netInterface
)));
463 NetAddrElement
*addrElement
= nullptr;
464 char buf
[kIPv6CStrBufSize
];
467 addrElement
= hr
->addr_info
->mAddresses
.getFirst();
469 addrElement
= addrElement
->getNext();
473 NetAddrToString(&addrElement
->mAddress
, buf
, sizeof(buf
));
474 LOG((" [%s]\n", buf
));
485 HostDB_InitEntry(PLDHashTable
*table
,
486 PLDHashEntryHdr
*entry
,
489 nsHostDBEnt
*he
= static_cast<nsHostDBEnt
*>(entry
);
490 nsHostRecord::Create(static_cast<const nsHostKey
*>(key
), &he
->rec
);
494 static const PLDHashTableOps gHostDB_ops
=
502 PL_DHashFinalizeStub
,
506 static PLDHashOperator
507 HostDB_RemoveEntry(PLDHashTable
*table
,
508 PLDHashEntryHdr
*hdr
,
512 return PL_DHASH_REMOVE
;
515 static PLDHashOperator
516 HostDB_PruneEntry(PLDHashTable
*table
,
517 PLDHashEntryHdr
*hdr
,
521 nsHostDBEnt
* ent
= static_cast<nsHostDBEnt
*>(hdr
);
522 // Try to remove the record, or mark it for refresh
523 if (ent
->rec
->RemoveOrRefresh()) {
524 PR_REMOVE_LINK(ent
->rec
);
525 return PL_DHASH_REMOVE
;
527 return PL_DHASH_NEXT
;
530 //----------------------------------------------------------------------------
533 static const char kPrefGetTtl
[] = "network.dns.get-ttl";
534 static bool sGetTtlEnabled
= false;
536 static void DnsPrefChanged(const char* aPref
, void* aClosure
)
538 MOZ_ASSERT(NS_IsMainThread(),
539 "Should be getting pref changed notification on main thread!");
541 if (strcmp(aPref
, kPrefGetTtl
) != 0) {
542 LOG(("DnsPrefChanged ignoring pref \"%s\"", aPref
));
546 auto self
= static_cast<nsHostResolver
*>(aClosure
);
549 sGetTtlEnabled
= Preferences::GetBool(kPrefGetTtl
);
553 nsHostResolver::nsHostResolver(uint32_t maxCacheEntries
,
554 uint32_t defaultCacheEntryLifetime
,
555 uint32_t defaultGracePeriod
)
556 : mMaxCacheEntries(maxCacheEntries
)
557 , mDefaultCacheLifetime(defaultCacheEntryLifetime
)
558 , mDefaultGracePeriod(defaultGracePeriod
)
559 , mLock("nsHostResolver.mLock")
560 , mIdleThreadCV(mLock
, "nsHostResolver.mIdleThreadCV")
563 , mActiveAnyThreadCount(0)
568 mCreationTime
= PR_Now();
569 PR_INIT_CLIST(&mHighQ
);
570 PR_INIT_CLIST(&mMediumQ
);
571 PR_INIT_CLIST(&mLowQ
);
572 PR_INIT_CLIST(&mEvictionQ
);
574 mLongIdleTimeout
= PR_SecondsToInterval(LongIdleTimeoutSeconds
);
575 mShortIdleTimeout
= PR_SecondsToInterval(ShortIdleTimeoutSeconds
);
578 nsHostResolver::~nsHostResolver()
580 PL_DHashTableFinish(&mDB
);
584 nsHostResolver::Init()
586 if (NS_FAILED(GetAddrInfoInit())) {
587 return NS_ERROR_FAILURE
;
590 PL_DHashTableInit(&mDB
, &gHostDB_ops
, nullptr, sizeof(nsHostDBEnt
), 0);
595 // The preferences probably haven't been loaded from the disk yet, so we
596 // need to register a callback that will set up the experiment once they
597 // are. We also need to explicitly set a value for the props otherwise the
598 // callback won't be called.
600 DebugOnly
<nsresult
> rv
= Preferences::RegisterCallbackAndCall(
601 &DnsPrefChanged
, kPrefGetTtl
, this);
602 NS_WARN_IF_FALSE(NS_SUCCEEDED(rv
),
603 "Could not register DNS TTL pref callback.");
607 #if defined(HAVE_RES_NINIT)
608 // We want to make sure the system is using the correct resolver settings,
609 // so we force it to reload those settings whenever we startup a subsequent
610 // nsHostResolver instance. We assume that there is no reason to do this
611 // for the first nsHostResolver instance since that is usually created
612 // during application startup.
613 static int initCount
= 0;
614 if (initCount
++ > 0) {
615 LOG(("Calling 'res_ninit'.\n"));
623 nsHostResolver::ClearPendingQueue(PRCList
*aPendingQ
)
625 // loop through pending queue, erroring out pending lookups.
626 if (!PR_CLIST_IS_EMPTY(aPendingQ
)) {
627 PRCList
*node
= aPendingQ
->next
;
628 while (node
!= aPendingQ
) {
629 nsHostRecord
*rec
= static_cast<nsHostRecord
*>(node
);
631 OnLookupComplete(rec
, NS_ERROR_ABORT
, nullptr);
637 // FlushCache() is what we call when the network has changed. We must not
638 // trust names that were resolved before this change. They may resolve
641 // This function removes all existing resolved host entries from the hash.
642 // Names that are in the pending queues can be left there. Entries in the
643 // cache that have 'Resolve' set true but not 'onQueue' are being resolved
644 // right now, so we need to mark them to get re-resolved on completion!
647 nsHostResolver::FlushCache()
649 MutexAutoLock
lock(mLock
);
652 // Clear the evictionQ and remove all its corresponding entries from
654 if (!PR_CLIST_IS_EMPTY(&mEvictionQ
)) {
655 PRCList
*node
= mEvictionQ
.next
;
656 while (node
!= &mEvictionQ
) {
657 nsHostRecord
*rec
= static_cast<nsHostRecord
*>(node
);
659 PR_REMOVE_AND_INIT_LINK(rec
);
660 PL_DHashTableRemove(&mDB
, (nsHostKey
*) rec
);
665 // Refresh the cache entries that are resolving RIGHT now, remove the rest.
666 PL_DHashTableEnumerate(&mDB
, HostDB_PruneEntry
, nullptr);
670 nsHostResolver::Shutdown()
672 LOG(("Shutting down host resolver.\n"));
676 DebugOnly
<nsresult
> rv
= Preferences::UnregisterCallback(
677 &DnsPrefChanged
, kPrefGetTtl
, this);
678 NS_WARN_IF_FALSE(NS_SUCCEEDED(rv
),
679 "Could not unregister DNS TTL pref callback.");
683 PRCList pendingQHigh
, pendingQMed
, pendingQLow
, evictionQ
;
684 PR_INIT_CLIST(&pendingQHigh
);
685 PR_INIT_CLIST(&pendingQMed
);
686 PR_INIT_CLIST(&pendingQLow
);
687 PR_INIT_CLIST(&evictionQ
);
690 MutexAutoLock
lock(mLock
);
694 MoveCList(mHighQ
, pendingQHigh
);
695 MoveCList(mMediumQ
, pendingQMed
);
696 MoveCList(mLowQ
, pendingQLow
);
697 MoveCList(mEvictionQ
, evictionQ
);
702 mIdleThreadCV
.NotifyAll();
704 // empty host database
705 PL_DHashTableEnumerate(&mDB
, HostDB_RemoveEntry
, nullptr);
708 ClearPendingQueue(&pendingQHigh
);
709 ClearPendingQueue(&pendingQMed
);
710 ClearPendingQueue(&pendingQLow
);
712 if (!PR_CLIST_IS_EMPTY(&evictionQ
)) {
713 PRCList
*node
= evictionQ
.next
;
714 while (node
!= &evictionQ
) {
715 nsHostRecord
*rec
= static_cast<nsHostRecord
*>(node
);
721 #ifdef NS_BUILD_REFCNT_LOGGING
723 // Logically join the outstanding worker threads with a timeout.
724 // Use this approach instead of PR_JoinThread() because that does
725 // not allow a timeout which may be necessary for a semi-responsive
726 // shutdown if the thread is blocked on a very slow DNS resolution.
727 // mThreadCount is read outside of mLock, but the worst case
728 // scenario for that race is one extra 25ms sleep.
730 PRIntervalTime delay
= PR_MillisecondsToInterval(25);
731 PRIntervalTime stopTime
= PR_IntervalNow() + PR_SecondsToInterval(20);
732 while (mThreadCount
&& PR_IntervalNow() < stopTime
)
737 mozilla::DebugOnly
<nsresult
> rv
= GetAddrInfoShutdown();
738 NS_WARN_IF_FALSE(NS_SUCCEEDED(rv
), "Failed to shutdown GetAddrInfo");
743 nsHostResolver::MoveQueue(nsHostRecord
*aRec
, PRCList
&aDestQ
)
745 NS_ASSERTION(aRec
->onQueue
, "Moving Host Record Not Currently Queued");
747 PR_REMOVE_LINK(aRec
);
748 PR_APPEND_LINK(aRec
, &aDestQ
);
752 nsHostResolver::ResolveHost(const char *host
,
755 const char *netInterface
,
756 nsResolveHostCallback
*callback
)
758 NS_ENSURE_TRUE(host
&& *host
, NS_ERROR_UNEXPECTED
);
759 NS_ENSURE_TRUE(netInterface
, NS_ERROR_UNEXPECTED
);
761 LOG(("Resolving host [%s%s%s]%s.\n", LOG_HOST(host
, netInterface
),
762 flags
& RES_BYPASS_CACHE
? " - bypassing cache" : ""));
764 // ensure that we are working with a valid hostname before proceeding. see
765 // bug 304904 for details.
766 if (!net_IsValidHostName(nsDependentCString(host
)))
767 return NS_ERROR_UNKNOWN_HOST
;
769 // if result is set inside the lock, then we need to issue the
770 // callback before returning.
771 nsRefPtr
<nsHostRecord
> result
;
772 nsresult status
= NS_OK
, rv
= NS_OK
;
774 MutexAutoLock
lock(mLock
);
777 rv
= NS_ERROR_NOT_INITIALIZED
;
779 // Used to try to parse to an IP address literal.
781 // Unfortunately, PR_StringToNetAddr does not properly initialize
782 // the output buffer in the case of IPv6 input. See bug 223145.
783 memset(&tempAddr
, 0, sizeof(PRNetAddr
));
785 // check to see if there is already an entry for this |host|
786 // in the hash table. if so, then check to see if we can't
787 // just reuse the lookup result. otherwise, if there are
788 // any pending callbacks, then add to pending callbacks queue,
789 // and return. otherwise, add ourselves as first pending
790 // callback, and proceed to do the lookup.
792 nsHostKey key
= { host
, flags
, af
, netInterface
};
793 nsHostDBEnt
*he
= static_cast<nsHostDBEnt
*>
794 (PL_DHashTableAdd(&mDB
, &key
));
796 // if the record is null, then HostDB_InitEntry failed.
797 if (!he
|| !he
->rec
) {
798 LOG((" Out of memory: no cache entry for host [%s%s%s].\n",
799 LOG_HOST(host
, netInterface
)));
800 rv
= NS_ERROR_OUT_OF_MEMORY
;
802 // do we have a cached result that we can reuse?
803 else if (!(flags
& RES_BYPASS_CACHE
) &&
804 he
->rec
->HasUsableResult(TimeStamp::NowLoRes(), flags
)) {
805 LOG((" Using cached record for host [%s%s%s].\n",
806 LOG_HOST(host
, netInterface
)));
807 // put reference to host record on stack...
809 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2
, METHOD_HIT
);
811 // For entries that are in the grace period
812 // or all cached negative entries, use the cache but start a new
813 // lookup in the background
814 ConditionallyRefreshRecord(he
->rec
, host
);
816 if (he
->rec
->negative
) {
817 LOG((" Negative cache entry for host [%s%s%s].\n",
818 LOG_HOST(host
, netInterface
)));
819 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2
,
820 METHOD_NEGATIVE_HIT
);
821 status
= NS_ERROR_UNKNOWN_HOST
;
824 // if the host name is an IP address literal and has been parsed,
825 // go ahead and use it.
826 else if (he
->rec
->addr
) {
827 LOG((" Using cached address for IP Literal [%s].\n", host
));
828 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2
,
832 // try parsing the host name as an IP address literal to short
833 // circuit full host resolution. (this is necessary on some
834 // platforms like Win9x. see bug 219376 for more details.)
835 else if (PR_StringToNetAddr(host
, &tempAddr
) == PR_SUCCESS
) {
836 LOG((" Host is IP Literal [%s].\n", host
));
837 // ok, just copy the result into the host record, and be done
839 he
->rec
->addr
= new NetAddr();
840 PRNetAddrToNetAddr(&tempAddr
, he
->rec
->addr
);
841 // put reference to host record on stack...
842 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2
,
846 else if (mPendingCount
>= MAX_NON_PRIORITY_REQUESTS
&&
847 !IsHighPriority(flags
) &&
848 !he
->rec
->resolving
) {
849 LOG((" Lookup queue full: dropping %s priority request for "
851 IsMediumPriority(flags
) ? "medium" : "low",
852 LOG_HOST(host
, netInterface
)));
853 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2
,
855 // This is a lower priority request and we are swamped, so refuse it.
856 rv
= NS_ERROR_DNS_LOOKUP_QUEUE_FULL
;
858 else if (flags
& RES_OFFLINE
) {
859 LOG((" Offline request for host [%s%s%s]; ignoring.\n",
860 LOG_HOST(host
, netInterface
)));
861 rv
= NS_ERROR_OFFLINE
;
864 // If this is an IPV4 or IPV6 specific request, check if there is
865 // an AF_UNSPEC entry we can use. Otherwise, hit the resolver...
866 else if (!he
->rec
->resolving
) {
867 if (!(flags
& RES_BYPASS_CACHE
) &&
868 ((af
== PR_AF_INET
) || (af
== PR_AF_INET6
))) {
869 // First, search for an entry with AF_UNSPEC
870 const nsHostKey unspecKey
= { host
, flags
, PR_AF_UNSPEC
,
872 nsHostDBEnt
*unspecHe
= static_cast<nsHostDBEnt
*>
873 (PL_DHashTableLookup(&mDB
, &unspecKey
));
874 NS_ASSERTION(PL_DHASH_ENTRY_IS_FREE(unspecHe
) ||
875 (PL_DHASH_ENTRY_IS_BUSY(unspecHe
) &&
877 "Valid host entries should contain a record");
878 TimeStamp now
= TimeStamp::NowLoRes();
879 if (PL_DHASH_ENTRY_IS_BUSY(unspecHe
) &&
881 unspecHe
->rec
->HasUsableResult(now
, flags
)) {
883 MOZ_ASSERT(unspecHe
->rec
->addr_info
|| unspecHe
->rec
->negative
,
884 "Entry should be resolved or negative.");
886 LOG((" Trying AF_UNSPEC entry for host [%s%s%s] af: %s.\n",
887 LOG_HOST(host
, netInterface
),
888 (af
== PR_AF_INET
) ? "AF_INET" : "AF_INET6"));
890 he
->rec
->addr_info
= nullptr;
891 if (unspecHe
->rec
->negative
) {
892 he
->rec
->negative
= unspecHe
->rec
->negative
;
893 he
->rec
->CopyExpirationTimesAndFlagsFrom(unspecHe
->rec
);
894 } else if (unspecHe
->rec
->addr_info
) {
895 // Search for any valid address in the AF_UNSPEC entry
896 // in the cache (not blacklisted and from the right
898 NetAddrElement
*addrIter
=
899 unspecHe
->rec
->addr_info
->mAddresses
.getFirst();
901 if ((af
== addrIter
->mAddress
.inet
.family
) &&
902 !unspecHe
->rec
->Blacklisted(&addrIter
->mAddress
)) {
903 if (!he
->rec
->addr_info
) {
904 he
->rec
->addr_info
= new AddrInfo(
905 unspecHe
->rec
->addr_info
->mHostName
,
906 unspecHe
->rec
->addr_info
->mCanonicalName
);
907 he
->rec
->CopyExpirationTimesAndFlagsFrom(unspecHe
->rec
);
909 he
->rec
->addr_info
->AddAddress(
910 new NetAddrElement(*addrIter
));
912 addrIter
= addrIter
->getNext();
915 // Now check if we have a new record.
916 if (he
->rec
->HasUsableResult(now
, flags
)) {
918 if (he
->rec
->negative
) {
919 status
= NS_ERROR_UNKNOWN_HOST
;
921 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2
,
923 ConditionallyRefreshRecord(he
->rec
, host
);
925 // For AF_INET6, a new lookup means another AF_UNSPEC
926 // lookup. We have already iterated through the
927 // AF_UNSPEC addresses, so we mark this record as
929 else if (af
== PR_AF_INET6
) {
930 LOG((" No AF_INET6 in AF_UNSPEC entry: "
931 "host [%s%s%s] unknown host.",
932 LOG_HOST(host
, netInterface
)));
934 he
->rec
->negative
= true;
935 status
= NS_ERROR_UNKNOWN_HOST
;
936 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2
,
937 METHOD_NEGATIVE_HIT
);
941 // If no valid address was found in the cache or this is an
942 // AF_UNSPEC request, then start a new lookup.
944 LOG((" No usable address in cache for host [%s%s%s].",
945 LOG_HOST(host
, netInterface
)));
947 // Add callback to the list of pending callbacks.
948 PR_APPEND_LINK(callback
, &he
->rec
->callbacks
);
949 he
->rec
->flags
= flags
;
950 rv
= IssueLookup(he
->rec
);
951 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2
,
952 METHOD_NETWORK_FIRST
);
954 PR_REMOVE_AND_INIT_LINK(callback
);
957 LOG((" DNS lookup for host [%s%s%s] blocking "
958 "pending 'getaddrinfo' query: callback [%p]",
959 LOG_HOST(host
, netInterface
), callback
));
964 LOG((" Host [%s%s%s] is being resolved. Appending callback "
965 "[%p].", LOG_HOST(host
, netInterface
), callback
));
967 PR_APPEND_LINK(callback
, &he
->rec
->callbacks
);
968 if (he
->rec
->onQueue
) {
969 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2
,
970 METHOD_NETWORK_SHARED
);
972 // Consider the case where we are on a pending queue of
973 // lower priority than the request is being made at.
974 // In that case we should upgrade to the higher queue.
976 if (IsHighPriority(flags
) &&
977 !IsHighPriority(he
->rec
->flags
)) {
978 // Move from (low|med) to high.
979 MoveQueue(he
->rec
, mHighQ
);
980 he
->rec
->flags
= flags
;
981 ConditionallyCreateThread(he
->rec
);
982 } else if (IsMediumPriority(flags
) &&
983 IsLowPriority(he
->rec
->flags
)) {
984 // Move from low to med.
985 MoveQueue(he
->rec
, mMediumQ
);
986 he
->rec
->flags
= flags
;
987 mIdleThreadCV
.Notify();
994 callback
->OnLookupComplete(this, result
, status
);
1001 nsHostResolver::DetachCallback(const char *host
,
1004 const char *netInterface
,
1005 nsResolveHostCallback
*callback
,
1008 nsRefPtr
<nsHostRecord
> rec
;
1010 MutexAutoLock
lock(mLock
);
1012 nsHostKey key
= { host
, flags
, af
, netInterface
};
1013 nsHostDBEnt
*he
= static_cast<nsHostDBEnt
*>
1014 (PL_DHashTableLookup(&mDB
, &key
));
1015 if (he
&& he
->rec
) {
1016 // walk list looking for |callback|... we cannot assume
1017 // that it will be there!
1018 PRCList
*node
= he
->rec
->callbacks
.next
;
1019 while (node
!= &he
->rec
->callbacks
) {
1020 if (static_cast<nsResolveHostCallback
*>(node
) == callback
) {
1021 PR_REMOVE_LINK(callback
);
1030 // complete callback with the given status code; this would only be done if
1031 // the record was in the process of being resolved.
1033 callback
->OnLookupComplete(this, rec
, status
);
1037 nsHostResolver::ConditionallyCreateThread(nsHostRecord
*rec
)
1039 if (mNumIdleThreads
) {
1040 // wake up idle thread to process this lookup
1041 mIdleThreadCV
.Notify();
1043 else if ((mThreadCount
< HighThreadThreshold
) ||
1044 (IsHighPriority(rec
->flags
) && mThreadCount
< MAX_RESOLVER_THREADS
)) {
1045 // dispatch new worker thread
1046 NS_ADDREF_THIS(); // owning reference passed to thread
1049 PRThread
*thr
= PR_CreateThread(PR_SYSTEM_THREAD
,
1054 PR_UNJOINABLE_THREAD
,
1059 return NS_ERROR_OUT_OF_MEMORY
;
1062 #if defined(PR_LOGGING)
1064 LOG((" Unable to find a thread for looking up host [%s%s%s].\n",
1065 LOG_HOST(rec
->host
, rec
->netInterface
)));
1072 nsHostResolver::IssueLookup(nsHostRecord
*rec
)
1074 MOZ_EVENT_TRACER_WAIT(rec
, "net::dns::resolve");
1076 nsresult rv
= NS_OK
;
1077 NS_ASSERTION(!rec
->resolving
, "record is already being resolved");
1079 // Add rec to one of the pending queues, possibly removing it from mEvictionQ.
1080 // If rec is on mEvictionQ, then we can just move the owning
1081 // reference over to the new active queue.
1082 if (rec
->next
== rec
)
1085 PR_REMOVE_LINK(rec
);
1089 switch (nsHostRecord::GetPriority(rec
->flags
)) {
1090 case nsHostRecord::DNS_PRIORITY_HIGH
:
1091 PR_APPEND_LINK(rec
, &mHighQ
);
1094 case nsHostRecord::DNS_PRIORITY_MEDIUM
:
1095 PR_APPEND_LINK(rec
, &mMediumQ
);
1098 case nsHostRecord::DNS_PRIORITY_LOW
:
1099 PR_APPEND_LINK(rec
, &mLowQ
);
1104 rec
->resolving
= true;
1105 rec
->onQueue
= true;
1107 rv
= ConditionallyCreateThread(rec
);
1109 LOG ((" DNS thread counters: total=%d any-live=%d idle=%d pending=%d\n",
1111 mActiveAnyThreadCount
,
1119 nsHostResolver::ConditionallyRefreshRecord(nsHostRecord
*rec
, const char *host
)
1121 if ((rec
->CheckExpiration(TimeStamp::NowLoRes()) != nsHostRecord::EXP_VALID
1122 || rec
->negative
) && !rec
->resolving
) {
1123 LOG((" Using %s cache entry for host [%s] but starting async renewal.",
1124 rec
->negative
? "negative" :"positive", host
));
1127 if (!rec
->negative
) {
1128 // negative entries are constantly being refreshed, only
1129 // track positive grace period induced renewals
1130 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2
,
1138 nsHostResolver::DeQueue(PRCList
&aQ
, nsHostRecord
**aResult
)
1140 *aResult
= static_cast<nsHostRecord
*>(aQ
.next
);
1141 PR_REMOVE_AND_INIT_LINK(*aResult
);
1143 (*aResult
)->onQueue
= false;
1147 nsHostResolver::GetHostToLookup(nsHostRecord
**result
)
1149 bool timedOut
= false;
1150 PRIntervalTime epoch
, now
, timeout
;
1152 MutexAutoLock
lock(mLock
);
1154 timeout
= (mNumIdleThreads
>= HighThreadThreshold
) ? mShortIdleTimeout
: mLongIdleTimeout
;
1155 epoch
= PR_IntervalNow();
1157 while (!mShutdown
) {
1158 // remove next record from Q; hand over owning reference. Check high, then med, then low
1161 #define SET_GET_TTL(var, val) \
1162 (var)->mGetTtl = sGetTtlEnabled && (val)
1164 #define SET_GET_TTL(var, val)
1167 if (!PR_CLIST_IS_EMPTY(&mHighQ
)) {
1168 DeQueue (mHighQ
, result
);
1169 SET_GET_TTL(*result
, false);
1173 if (mActiveAnyThreadCount
< HighThreadThreshold
) {
1174 if (!PR_CLIST_IS_EMPTY(&mMediumQ
)) {
1175 DeQueue (mMediumQ
, result
);
1176 mActiveAnyThreadCount
++;
1177 (*result
)->usingAnyThread
= true;
1178 SET_GET_TTL(*result
, true);
1182 if (!PR_CLIST_IS_EMPTY(&mLowQ
)) {
1183 DeQueue (mLowQ
, result
);
1184 mActiveAnyThreadCount
++;
1185 (*result
)->usingAnyThread
= true;
1186 SET_GET_TTL(*result
, true);
1191 // Determining timeout is racy, so allow one cycle through checking the queues
1196 // wait for one or more of the following to occur:
1197 // (1) the pending queue has a host record to process
1198 // (2) the shutdown flag has been set
1199 // (3) the thread has been idle for too long
1202 mIdleThreadCV
.Wait(timeout
);
1205 now
= PR_IntervalNow();
1207 if ((PRIntervalTime
)(now
- epoch
) >= timeout
)
1210 // It is possible that PR_WaitCondVar() was interrupted and returned early,
1211 // in which case we will loop back and re-enter it. In that case we want to
1212 // do so with the new timeout reduced to reflect time already spent waiting.
1213 timeout
-= (PRIntervalTime
)(now
- epoch
);
1218 // tell thread to exit...
1224 nsHostResolver::PrepareRecordExpiration(nsHostRecord
* rec
) const
1226 MOZ_ASSERT(((bool)rec
->addr_info
) != rec
->negative
);
1227 if (!rec
->addr_info
) {
1228 rec
->SetExpiration(TimeStamp::NowLoRes(),
1229 NEGATIVE_RECORD_LIFETIME
, 0);
1230 LOG(("Caching host [%s%s%s] negative record for %u seconds.\n",
1231 LOG_HOST(rec
->host
, rec
->netInterface
),
1232 NEGATIVE_RECORD_LIFETIME
));
1236 unsigned int lifetime
= mDefaultCacheLifetime
;
1237 unsigned int grace
= mDefaultGracePeriod
;
1239 unsigned int ttl
= mDefaultCacheLifetime
;
1240 if (sGetTtlEnabled
) {
1241 MutexAutoLock
lock(rec
->addr_info_lock
);
1242 if (rec
->addr_info
&& rec
->addr_info
->ttl
!= AddrInfo::NO_TTL_DATA
) {
1243 ttl
= rec
->addr_info
->ttl
;
1250 rec
->SetExpiration(TimeStamp::NowLoRes(), lifetime
, grace
);
1251 LOG(("Caching host [%s%s%s] record for %u seconds (grace %d).",
1252 LOG_HOST(rec
->host
, rec
->netInterface
), lifetime
, grace
));
1256 // OnLookupComplete() checks if the resolving should be redone and if so it
1257 // returns LOOKUP_RESOLVEAGAIN, but only if 'status' is not NS_ERROR_ABORT.
1260 nsHostResolver::LookupStatus
1261 nsHostResolver::OnLookupComplete(nsHostRecord
* rec
, nsresult status
, AddrInfo
* result
)
1263 // get the list of pending callbacks for this lookup, and notify
1264 // them that the lookup is complete.
1266 PR_INIT_CLIST(&cbs
);
1268 MutexAutoLock
lock(mLock
);
1270 if (rec
->mResolveAgain
&& (status
!= NS_ERROR_ABORT
)) {
1271 rec
->mResolveAgain
= false;
1272 return LOOKUP_RESOLVEAGAIN
;
1275 // grab list of callbacks to notify
1276 MoveCList(rec
->callbacks
, cbs
);
1278 // update record fields. We might have a rec->addr_info already if a
1279 // previous lookup result expired and we're reresolving it..
1280 AddrInfo
*old_addr_info
;
1282 MutexAutoLock
lock(rec
->addr_info_lock
);
1283 old_addr_info
= rec
->addr_info
;
1284 rec
->addr_info
= result
;
1285 rec
->addr_info_gencnt
++;
1287 delete old_addr_info
;
1289 rec
->negative
= !rec
->addr_info
;
1290 PrepareRecordExpiration(rec
);
1291 rec
->resolving
= false;
1293 if (rec
->usingAnyThread
) {
1294 mActiveAnyThreadCount
--;
1295 rec
->usingAnyThread
= false;
1299 // add to mEvictionQ
1300 PR_APPEND_LINK(rec
, &mEvictionQ
);
1302 if (mEvictionQSize
< mMaxCacheEntries
)
1305 // remove first element on mEvictionQ
1306 nsHostRecord
*head
=
1307 static_cast<nsHostRecord
*>(PR_LIST_HEAD(&mEvictionQ
));
1308 PR_REMOVE_AND_INIT_LINK(head
);
1309 PL_DHashTableRemove(&mDB
, (nsHostKey
*) head
);
1311 if (!head
->negative
) {
1312 // record the age of the entry upon eviction.
1313 TimeDuration age
= TimeStamp::NowLoRes() - head
->mValidStart
;
1314 Telemetry::Accumulate(Telemetry::DNS_CLEANUP_AGE
,
1315 static_cast<uint32_t>(age
.ToSeconds() / 60));
1318 // release reference to rec owned by mEvictionQ
1322 if (!rec
->mGetTtl
&& !rec
->resolving
&& sGetTtlEnabled
) {
1323 LOG(("Issuing second async lookup for TTL for host [%s%s%s].",
1324 LOG_HOST(rec
->host
, rec
->netInterface
)));
1326 (rec
->flags
& ~RES_PRIORITY_MEDIUM
) | RES_PRIORITY_LOW
;
1327 DebugOnly
<nsresult
> rv
= IssueLookup(rec
);
1328 NS_WARN_IF_FALSE(NS_SUCCEEDED(rv
),
1329 "Could not issue second async lookup for TTL.");
1335 MOZ_EVENT_TRACER_DONE(rec
, "net::dns::resolve");
1337 if (!PR_CLIST_IS_EMPTY(&cbs
)) {
1338 PRCList
*node
= cbs
.next
;
1339 while (node
!= &cbs
) {
1340 nsResolveHostCallback
*callback
=
1341 static_cast<nsResolveHostCallback
*>(node
);
1343 callback
->OnLookupComplete(this, rec
, status
);
1344 // NOTE: callback must not be dereferenced after this point!!
1354 nsHostResolver::CancelAsyncRequest(const char *host
,
1357 const char *netInterface
,
1358 nsIDNSListener
*aListener
,
1362 MutexAutoLock
lock(mLock
);
1364 // Lookup the host record associated with host, flags & address family
1365 nsHostKey key
= { host
, flags
, af
, netInterface
};
1366 nsHostDBEnt
*he
= static_cast<nsHostDBEnt
*>
1367 (PL_DHashTableLookup(&mDB
, &key
));
1368 if (he
&& he
->rec
) {
1369 nsHostRecord
* recPtr
= nullptr;
1370 PRCList
*node
= he
->rec
->callbacks
.next
;
1371 // Remove the first nsDNSAsyncRequest callback which matches the
1372 // supplied listener object
1373 while (node
!= &he
->rec
->callbacks
) {
1374 nsResolveHostCallback
*callback
1375 = static_cast<nsResolveHostCallback
*>(node
);
1376 if (callback
&& (callback
->EqualsAsyncListener(aListener
))) {
1377 // Remove from the list of callbacks
1378 PR_REMOVE_LINK(callback
);
1380 callback
->OnLookupComplete(this, recPtr
, status
);
1386 // If there are no more callbacks, remove the hash table entry
1387 if (recPtr
&& PR_CLIST_IS_EMPTY(&recPtr
->callbacks
)) {
1388 PL_DHashTableRemove(&mDB
, (nsHostKey
*)recPtr
);
1389 // If record is on a Queue, remove it and then deref it
1390 if (recPtr
->next
!= recPtr
) {
1391 PR_REMOVE_LINK(recPtr
);
1399 SizeOfHostDBEntExcludingThis(PLDHashEntryHdr
* hdr
, MallocSizeOf mallocSizeOf
,
1402 nsHostDBEnt
* ent
= static_cast<nsHostDBEnt
*>(hdr
);
1403 return ent
->rec
->SizeOfIncludingThis(mallocSizeOf
);
1407 nsHostResolver::SizeOfIncludingThis(MallocSizeOf mallocSizeOf
) const
1409 MutexAutoLock
lock(mLock
);
1411 size_t n
= mallocSizeOf(this);
1412 n
+= PL_DHashTableSizeOfExcludingThis(&mDB
, SizeOfHostDBEntExcludingThis
,
1415 // The following fields aren't measured.
1416 // - mHighQ, mMediumQ, mLowQ, mEvictionQ, because they just point to
1417 // nsHostRecords that also pointed to by entries |mDB|, and measured when
1418 // |mDB| is measured.
1424 nsHostResolver::ThreadFunc(void *arg
)
1426 LOG(("DNS lookup thread - starting execution.\n"));
1428 static nsThreadPoolNaming naming
;
1429 naming
.SetThreadPoolName(NS_LITERAL_CSTRING("DNS Resolver"));
1431 #if defined(RES_RETRY_ON_FAILURE)
1434 nsHostResolver
*resolver
= (nsHostResolver
*)arg
;
1435 nsHostRecord
*rec
= nullptr;
1436 AddrInfo
*ai
= nullptr;
1438 while (rec
|| resolver
->GetHostToLookup(&rec
)) {
1439 LOG(("DNS lookup thread - Calling getaddrinfo for host [%s%s%s].\n",
1440 LOG_HOST(rec
->host
, rec
->netInterface
)));
1442 TimeStamp startTime
= TimeStamp::Now();
1443 MOZ_EVENT_TRACER_EXEC(rec
, "net::dns::resolve");
1446 bool getTtl
= rec
->mGetTtl
;
1448 bool getTtl
= false;
1451 // We need to remove IPv4 records manually
1452 // because PR_GetAddrInfoByName doesn't support PR_AF_INET6.
1453 bool disableIPv4
= rec
->af
== PR_AF_INET6
;
1454 uint16_t af
= disableIPv4
? PR_AF_UNSPEC
: rec
->af
;
1455 nsresult status
= GetAddrInfo(rec
->host
, af
, rec
->flags
, rec
->netInterface
,
1457 #if defined(RES_RETRY_ON_FAILURE)
1458 if (NS_FAILED(status
) && rs
.Reset()) {
1459 status
= GetAddrInfo(rec
->host
, af
, rec
->flags
, rec
->netInterface
, &ai
,
1464 TimeDuration elapsed
= TimeStamp::Now() - startTime
;
1465 uint32_t millis
= static_cast<uint32_t>(elapsed
.ToMilliseconds());
1467 if (NS_SUCCEEDED(status
)) {
1468 Telemetry::ID histogramID
;
1469 if (!rec
->addr_info_gencnt
) {
1470 // Time for initial lookup.
1471 histogramID
= Telemetry::DNS_LOOKUP_TIME
;
1472 } else if (!getTtl
) {
1473 // Time for renewal; categorized by expiration strategy.
1474 histogramID
= Telemetry::DNS_RENEWAL_TIME
;
1476 // Time to get TTL; categorized by expiration strategy.
1477 histogramID
= Telemetry::DNS_RENEWAL_TIME_FOR_TTL
;
1479 Telemetry::Accumulate(histogramID
, millis
);
1481 Telemetry::Accumulate(Telemetry::DNS_FAILED_LOOKUP_TIME
, millis
);
1484 // OnLookupComplete may release "rec", long before we lose it.
1485 LOG(("DNS lookup thread - lookup completed for host [%s%s%s]: %s.\n",
1486 LOG_HOST(rec
->host
, rec
->netInterface
),
1487 ai
? "success" : "failure: unknown host"));
1489 if (LOOKUP_RESOLVEAGAIN
== resolver
->OnLookupComplete(rec
, status
, ai
)) {
1490 // leave 'rec' assigned and loop to make a renewed host resolve
1491 LOG(("DNS lookup thread - Re-resolving host [%s%s%s].\n",
1492 LOG_HOST(rec
->host
, rec
->netInterface
)));
1497 NS_RELEASE(resolver
);
1498 LOG(("DNS lookup thread - queue empty, thread finished.\n"));
1502 nsHostResolver::Create(uint32_t maxCacheEntries
,
1503 uint32_t defaultCacheEntryLifetime
,
1504 uint32_t defaultGracePeriod
,
1505 nsHostResolver
**result
)
1507 #if defined(PR_LOGGING)
1508 if (!gHostResolverLog
)
1509 gHostResolverLog
= PR_NewLogModule("nsHostResolver");
1512 nsHostResolver
*res
= new nsHostResolver(maxCacheEntries
, defaultCacheEntryLifetime
,
1513 defaultGracePeriod
);
1515 return NS_ERROR_OUT_OF_MEMORY
;
1518 nsresult rv
= res
->Init();
1527 CacheEntryEnumerator(PLDHashTable
*table
, PLDHashEntryHdr
*entry
,
1528 uint32_t number
, void *arg
)
1530 // We don't pay attention to address literals, only resolved domains.
1531 // Also require a host.
1532 nsHostRecord
*rec
= static_cast<nsHostDBEnt
*>(entry
)->rec
;
1533 MOZ_ASSERT(rec
, "rec should never be null here!");
1534 if (!rec
|| !rec
->addr_info
|| !rec
->host
) {
1535 return PL_DHASH_NEXT
;
1538 DNSCacheEntries info
;
1539 info
.hostname
= rec
->host
;
1540 info
.family
= rec
->af
;
1541 info
.netInterface
= rec
->netInterface
;
1543 (int64_t)(rec
->mValidEnd
- TimeStamp::NowLoRes()).ToSeconds();
1544 if (info
.expiration
<= 0) {
1545 // We only need valid DNS cache entries
1546 return PL_DHASH_NEXT
;
1550 MutexAutoLock
lock(rec
->addr_info_lock
);
1552 NetAddr
*addr
= nullptr;
1553 NetAddrElement
*addrElement
= rec
->addr_info
->mAddresses
.getFirst();
1555 addr
= &addrElement
->mAddress
;
1558 char buf
[kIPv6CStrBufSize
];
1559 if (NetAddrToString(addr
, buf
, sizeof(buf
))) {
1560 info
.hostaddr
.AppendElement(buf
);
1563 addrElement
= addrElement
->getNext();
1565 addr
= &addrElement
->mAddress
;
1570 nsTArray
<DNSCacheEntries
> *args
= static_cast<nsTArray
<DNSCacheEntries
> *>(arg
);
1571 args
->AppendElement(info
);
1573 return PL_DHASH_NEXT
;
1577 nsHostResolver::GetDNSCacheEntries(nsTArray
<DNSCacheEntries
> *args
)
1579 PL_DHashTableEnumerate(&mDB
, CacheEntryEnumerator
, args
);