Line data Source code
1 : /* vim:set ts=4 sw=4 sts=4 et cin: */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #if defined(HAVE_RES_NINIT)
7 : #include <sys/types.h>
8 : #include <netinet/in.h>
9 : #include <arpa/inet.h>
10 : #include <arpa/nameser.h>
11 : #include <resolv.h>
12 : #define RES_RETRY_ON_FAILURE
13 : #endif
14 :
15 : #include <stdlib.h>
16 : #include <ctime>
17 : #include "nsHostResolver.h"
18 : #include "nsError.h"
19 : #include "nsISupportsBase.h"
20 : #include "nsISupportsUtils.h"
21 : #include "nsAutoPtr.h"
22 : #include "nsPrintfCString.h"
23 : #include "prthread.h"
24 : #include "prerror.h"
25 : #include "prtime.h"
26 : #include "mozilla/Logging.h"
27 : #include "PLDHashTable.h"
28 : #include "plstr.h"
29 : #include "nsURLHelper.h"
30 : #include "nsThreadUtils.h"
31 : #include "GetAddrInfo.h"
32 : #include "GeckoProfiler.h"
33 : #include "TRR.h"
34 : #include "TRRService.h"
35 :
36 : #include "mozilla/Atomics.h"
37 : #include "mozilla/HashFunctions.h"
38 : #include "mozilla/TimeStamp.h"
39 : #include "mozilla/Telemetry.h"
40 : #include "mozilla/DebugOnly.h"
41 : #include "mozilla/Preferences.h"
42 :
43 : using namespace mozilla;
44 : using namespace mozilla::net;
45 :
46 : // None of our implementations expose a TTL for negative responses, so we use a
47 : // constant always.
48 : static const unsigned int NEGATIVE_RECORD_LIFETIME = 60;
49 :
50 : //----------------------------------------------------------------------------
51 :
52 : // Use a persistent thread pool in order to avoid spinning up new threads all the time.
53 : // In particular, thread creation results in a res_init() call from libc which is
54 : // quite expensive.
55 : //
56 : // The pool dynamically grows between 0 and MAX_RESOLVER_THREADS in size. New requests
57 : // go first to an idle thread. If that cannot be found and there are fewer than MAX_RESOLVER_THREADS
58 : // currently in the pool a new thread is created for high priority requests. If
59 : // the new request is at a lower priority a new thread will only be created if
60 : // there are fewer than HighThreadThreshold currently outstanding. If a thread cannot be
61 : // created or an idle thread located for the request it is queued.
62 : //
63 : // When the pool is greater than HighThreadThreshold in size a thread will be destroyed after
64 : // ShortIdleTimeoutSeconds of idle time. Smaller pools use LongIdleTimeoutSeconds for a
65 : // timeout period.
66 :
67 : #define HighThreadThreshold MAX_RESOLVER_THREADS_FOR_ANY_PRIORITY
68 : #define LongIdleTimeoutSeconds 300 // for threads 1 -> HighThreadThreshold
69 : #define ShortIdleTimeoutSeconds 60 // for threads HighThreadThreshold+1 -> MAX_RESOLVER_THREADS
70 :
71 : static_assert(HighThreadThreshold <= MAX_RESOLVER_THREADS,
72 : "High Thread Threshold should be less equal Maximum allowed thread");
73 :
74 : //----------------------------------------------------------------------------
75 :
76 : namespace mozilla {
77 : namespace net {
78 : LazyLogModule gHostResolverLog("nsHostResolver");
79 : #define LOG(args) MOZ_LOG(mozilla::net::gHostResolverLog, mozilla::LogLevel::Debug, args)
80 : #define LOG_ENABLED() MOZ_LOG_TEST(mozilla::net::gHostResolverLog, mozilla::LogLevel::Debug)
81 : }
82 : }
83 :
84 : //----------------------------------------------------------------------------
85 :
86 : #if defined(RES_RETRY_ON_FAILURE)
87 :
88 : // this class represents the resolver state for a given thread. if we
89 : // encounter a lookup failure, then we can invoke the Reset method on an
90 : // instance of this class to reset the resolver (in case /etc/resolv.conf
91 : // for example changed). this is mainly an issue on GNU systems since glibc
92 : // only reads in /etc/resolv.conf once per thread. it may be an issue on
93 : // other systems as well.
94 :
95 : class nsResState
96 : {
97 : public:
98 0 : nsResState()
99 : // initialize mLastReset to the time when this object
100 : // is created. this means that a reset will not occur
101 : // if a thread is too young. the alternative would be
102 : // to initialize this to the beginning of time, so that
103 : // the first failure would cause a reset, but since the
104 : // thread would have just started up, it likely would
105 : // already have current /etc/resolv.conf info.
106 0 : : mLastReset(PR_IntervalNow())
107 : {
108 0 : }
109 :
110 0 : bool Reset()
111 : {
112 : // reset no more than once per second
113 0 : if (PR_IntervalToSeconds(PR_IntervalNow() - mLastReset) < 1)
114 : return false;
115 :
116 0 : LOG(("Calling 'res_ninit'.\n"));
117 :
118 0 : mLastReset = PR_IntervalNow();
119 0 : return (res_ninit(&_res) == 0);
120 : }
121 :
122 : private:
123 : PRIntervalTime mLastReset;
124 : };
125 :
126 : #endif // RES_RETRY_ON_FAILURE
127 :
128 : //----------------------------------------------------------------------------
129 :
130 : static inline bool
131 : IsHighPriority(uint16_t flags)
132 : {
133 : return !(flags & (nsHostResolver::RES_PRIORITY_LOW | nsHostResolver::RES_PRIORITY_MEDIUM));
134 : }
135 :
136 : static inline bool
137 : IsMediumPriority(uint16_t flags)
138 : {
139 : return flags & nsHostResolver::RES_PRIORITY_MEDIUM;
140 : }
141 :
142 : static inline bool
143 : IsLowPriority(uint16_t flags)
144 : {
145 : return flags & nsHostResolver::RES_PRIORITY_LOW;
146 : }
147 :
148 : //----------------------------------------------------------------------------
149 : // this macro filters out any flags that are not used when constructing the
150 : // host key. the significant flags are those that would affect the resulting
151 : // host record (i.e., the flags that are passed down to PR_GetAddrInfoByName).
152 : #define RES_KEY_FLAGS(_f) ((_f) & nsHostResolver::RES_CANON_NAME)
153 :
154 : bool
155 0 : nsHostKey::operator==(const nsHostKey& other) const
156 : {
157 0 : return host == other.host &&
158 0 : RES_KEY_FLAGS (flags) == RES_KEY_FLAGS(other.flags) &&
159 0 : af == other.af &&
160 0 : originSuffix == other.originSuffix;
161 : }
162 :
163 : PLDHashNumber
164 0 : nsHostKey::Hash() const
165 : {
166 0 : return AddToHash(HashString(host.get()), RES_KEY_FLAGS(flags), af,
167 0 : HashString(originSuffix.get()));
168 : }
169 :
170 : size_t
171 0 : nsHostKey::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
172 : {
173 0 : size_t n = 0;
174 0 : n += host.SizeOfExcludingThisIfUnshared(mallocSizeOf);
175 0 : n += originSuffix.SizeOfExcludingThisIfUnshared(mallocSizeOf);
176 0 : return n;
177 : }
178 :
179 0 : nsHostRecord::nsHostRecord(const nsHostKey& key)
180 : : nsHostKey(key)
181 : , addr_info_lock("nsHostRecord.addr_info_lock")
182 : , addr_info_gencnt(0)
183 : , addr_info(nullptr)
184 : , addr(nullptr)
185 : , negative(false)
186 : , mResolverMode(MODE_NATIVEONLY)
187 : , mResolving(0)
188 : , mTRRSuccess(0)
189 : , mNativeSuccess(0)
190 : , mNative(false)
191 : , mTRRUsed(false)
192 : , mNativeUsed(false)
193 : , onQueue(false)
194 : , usingAnyThread(false)
195 : , mDoomed(false)
196 : , mDidCallbacks(false)
197 : , mGetTtl(false)
198 : , mResolveAgain(false)
199 : , mTrrAUsed(INIT)
200 : , mTrrAAAAUsed(INIT)
201 0 : , mTrrLock("nsHostRecord.mTrrLock")
202 : , mBlacklistedCount(0)
203 0 : {
204 : }
205 :
206 0 : void
207 : nsHostRecord::Cancel()
208 0 : {
209 0 : MutexAutoLock trrlock(mTrrLock);
210 0 : if (mTrrA) {
211 0 : mTrrA->Cancel();
212 : mTrrA = nullptr;
213 0 : }
214 0 : if (mTrrAAAA) {
215 0 : mTrrAAAA->Cancel();
216 : mTrrAAAA = nullptr;
217 0 : }
218 : }
219 :
220 0 : void
221 : nsHostRecord::Invalidate()
222 0 : {
223 0 : mDoomed = true;
224 : }
225 :
226 0 : void
227 : nsHostRecord::SetExpiration(const mozilla::TimeStamp& now, unsigned int valid, unsigned int grace)
228 0 : {
229 0 : mValidStart = now;
230 0 : mGraceStart = now + TimeDuration::FromSeconds(valid);
231 0 : mValidEnd = now + TimeDuration::FromSeconds(valid + grace);
232 : }
233 :
234 0 : void
235 : nsHostRecord::CopyExpirationTimesAndFlagsFrom(const nsHostRecord *aFromHostRecord)
236 : {
237 : // This is used to copy information from a cache entry to a record. All
238 0 : // information necessary for HasUsableRecord needs to be copied.
239 0 : mValidStart = aFromHostRecord->mValidStart;
240 0 : mValidEnd = aFromHostRecord->mValidEnd;
241 0 : mGraceStart = aFromHostRecord->mGraceStart;
242 0 : mDoomed = aFromHostRecord->mDoomed;
243 : }
244 :
245 0 : void
246 : nsHostRecord::ResolveComplete()
247 0 : {
248 0 : if (mNativeUsed) {
249 0 : if (mNativeSuccess) {
250 0 : uint32_t millis = static_cast<uint32_t>(mNativeDuration.ToMilliseconds());
251 : Telemetry::Accumulate(Telemetry::DNS_NATIVE_LOOKUP_TIME, millis);
252 0 : }
253 : AccumulateCategorical(mNativeSuccess ?
254 0 : Telemetry::LABELS_DNS_LOOKUP_DISPOSITION::osOK :
255 : Telemetry::LABELS_DNS_LOOKUP_DISPOSITION::osFail);
256 : }
257 0 :
258 0 : if (mTRRUsed) {
259 0 : if (mTRRSuccess) {
260 0 : uint32_t millis = static_cast<uint32_t>(mTrrDuration.ToMilliseconds());
261 : Telemetry::Accumulate(Telemetry::DNS_TRR_LOOKUP_TIME, millis);
262 0 : }
263 : AccumulateCategorical(mTRRSuccess ?
264 0 : Telemetry::LABELS_DNS_LOOKUP_DISPOSITION::trrOK :
265 : Telemetry::LABELS_DNS_LOOKUP_DISPOSITION::trrFail);
266 0 :
267 0 : if (mTrrAUsed == OK) {
268 0 : AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_DISPOSITION::trrAOK);
269 0 : } else if (mTrrAUsed == FAILED) {
270 : AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_DISPOSITION::trrAFail);
271 : }
272 0 :
273 0 : if (mTrrAAAAUsed == OK) {
274 0 : AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_DISPOSITION::trrAAAAOK);
275 0 : } else if (mTrrAAAAUsed == FAILED) {
276 : AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_DISPOSITION::trrAAAAFail);
277 : }
278 : }
279 0 :
280 0 : if (mTRRUsed && mNativeUsed && mNativeSuccess && mTRRSuccess) { // race or shadow!
281 0 : static const TimeDuration k50ms = TimeDuration::FromMilliseconds(50);
282 0 : if (mTrrDuration <= mNativeDuration) {
283 : AccumulateCategorical(((mNativeDuration - mTrrDuration) > k50ms) ?
284 0 : Telemetry::LABELS_DNS_TRR_RACE::TRRFasterBy50 :
285 0 : Telemetry::LABELS_DNS_TRR_RACE::TRRFaster);
286 : LOG(("nsHostRecord::Complete %s Dns Race: TRR\n", host.get()));
287 0 : } else {
288 : AccumulateCategorical(((mTrrDuration - mNativeDuration) > k50ms) ?
289 0 : Telemetry::LABELS_DNS_TRR_RACE::NativeFasterBy50 :
290 0 : Telemetry::LABELS_DNS_TRR_RACE::NativeFaster);
291 : LOG(("nsHostRecord::Complete %s Dns Race: NATIVE\n", host.get()));
292 : }
293 : }
294 0 :
295 : if (mTRRUsed && mNativeUsed) {
296 0 : // both were used, accumulate comparative success
297 : AccumulateCategorical(mNativeSuccess && mTRRSuccess?
298 0 : Telemetry::LABELS_DNS_TRR_COMPARE::BothWorked :
299 0 : ((mNativeSuccess ? Telemetry::LABELS_DNS_TRR_COMPARE::NativeWorked :
300 0 : (mTRRSuccess ? Telemetry::LABELS_DNS_TRR_COMPARE::TRRWorked:
301 : Telemetry::LABELS_DNS_TRR_COMPARE::BothFailed))));
302 : }
303 0 :
304 : switch(mResolverMode) {
305 : case MODE_NATIVEONLY:
306 0 : case MODE_TRROFF:
307 0 : AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::nativeOnly);
308 : break;
309 0 : case MODE_PARALLEL:
310 0 : AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::trrRace);
311 : break;
312 0 : case MODE_TRRFIRST:
313 0 : AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::trrFirst);
314 : break;
315 0 : case MODE_TRRONLY:
316 0 : AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::trrOnly);
317 : break;
318 0 : case MODE_SHADOW:
319 0 : AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::trrShadow);
320 : break;
321 : }
322 0 :
323 0 : if (mTRRUsed && !mTRRSuccess && mNativeSuccess && gTRRService) {
324 : gTRRService->TRRBlacklist(nsCString(host), pb, true);
325 0 : }
326 : }
327 0 :
328 : nsHostRecord::~nsHostRecord()
329 0 : {
330 : mCallbacks.clear();
331 0 :
332 0 : Telemetry::Accumulate(Telemetry::DNS_BLACKLIST_COUNT, mBlacklistedCount);
333 0 : delete addr_info;
334 : }
335 :
336 0 : bool
337 : nsHostRecord::Blacklisted(NetAddr *aQuery)
338 : {
339 0 : // must call locked
340 : LOG(("Checking blacklist for host [%s], host record [%p].\n",
341 : host.get(), this));
342 :
343 0 : // skip the string conversion for the common case of no blacklist
344 : if (!mBlacklistedItems.Length()) {
345 : return false;
346 : }
347 :
348 0 : char buf[kIPv6CStrBufSize];
349 : if (!NetAddrToString(aQuery, buf, sizeof(buf))) {
350 : return false;
351 0 : }
352 : nsDependentCString strQuery(buf);
353 0 :
354 0 : for (uint32_t i = 0; i < mBlacklistedItems.Length(); i++) {
355 0 : if (mBlacklistedItems.ElementAt(i).Equals(strQuery)) {
356 : LOG(("Address [%s] is blacklisted for host [%s].\n", buf, host.get()));
357 : return true;
358 : }
359 : }
360 :
361 : return false;
362 : }
363 :
364 0 : void
365 : nsHostRecord::ReportUnusable(NetAddr *aAddress)
366 : {
367 0 : // must call locked
368 : LOG(("Adding address to blacklist for host [%s], host record [%p]."
369 : "used trr=%d\n", host.get(), this, mTRRSuccess));
370 0 :
371 : ++mBlacklistedCount;
372 0 :
373 0 : if (negative)
374 : mDoomed = true;
375 :
376 0 : char buf[kIPv6CStrBufSize];
377 0 : if (NetAddrToString(aAddress, buf, sizeof(buf))) {
378 : LOG(("Successfully adding address [%s] to blacklist for host "
379 0 : "[%s].\n", buf, host.get()));
380 : mBlacklistedItems.AppendElement(nsCString(buf));
381 0 : }
382 : }
383 :
384 0 : void
385 : nsHostRecord::ResetBlacklist()
386 : {
387 0 : // must call locked
388 : LOG(("Resetting blacklist for host [%s], host record [%p].\n",
389 0 : host.get(), this));
390 0 : mBlacklistedItems.Clear();
391 : }
392 :
393 0 : nsHostRecord::ExpirationStatus
394 0 : nsHostRecord::CheckExpiration(const mozilla::TimeStamp& now) const {
395 0 : if (!mGraceStart.IsNull() && now >= mGraceStart
396 : && !mValidEnd.IsNull() && now < mValidEnd) {
397 : return nsHostRecord::EXP_GRACE;
398 0 : }
399 : if (!mValidEnd.IsNull() && now < mValidEnd) {
400 : return nsHostRecord::EXP_VALID;
401 : }
402 0 :
403 : return nsHostRecord::EXP_EXPIRED;
404 : }
405 :
406 :
407 0 : bool
408 : nsHostRecord::HasUsableResult(const mozilla::TimeStamp& now, uint16_t queryFlags) const
409 0 : {
410 : if (mDoomed) {
411 : return false;
412 : }
413 :
414 0 : // don't use cached negative results for high priority queries.
415 : if (negative && IsHighPriority(queryFlags)) {
416 : return false;
417 : }
418 0 :
419 : if (CheckExpiration(now) == EXP_EXPIRED) {
420 : return false;
421 : }
422 0 :
423 : return addr_info || addr || negative;
424 : }
425 :
426 0 : static size_t
427 : SizeOfResolveHostCallbackListExcludingHead(const mozilla::LinkedList<RefPtr<nsResolveHostCallback>>& aCallbacks,
428 : MallocSizeOf mallocSizeOf)
429 0 : {
430 : size_t n = aCallbacks.sizeOfExcludingThis(mallocSizeOf);
431 0 :
432 0 : for (const nsResolveHostCallback* t = aCallbacks.getFirst(); t; t = t->getNext()) {
433 : n += t->SizeOfIncludingThis(mallocSizeOf);
434 : }
435 0 :
436 : return n;
437 : }
438 :
439 0 : size_t
440 : nsHostRecord::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
441 0 : {
442 : size_t n = mallocSizeOf(this);
443 0 :
444 0 : n += nsHostKey::SizeOfExcludingThis(mallocSizeOf);
445 0 : n += SizeOfResolveHostCallbackListExcludingHead(mCallbacks, mallocSizeOf);
446 0 : n += addr_info ? addr_info->SizeOfIncludingThis(mallocSizeOf) : 0;
447 : n += mallocSizeOf(addr.get());
448 0 :
449 0 : n += mBlacklistedItems.ShallowSizeOfExcludingThis(mallocSizeOf);
450 0 : for (size_t i = 0; i < mBlacklistedItems.Length(); i++) {
451 : n += mBlacklistedItems[i].SizeOfExcludingThisIfUnshared(mallocSizeOf);
452 0 : }
453 : return n;
454 : }
455 :
456 0 : nsHostRecord::DnsPriority
457 : nsHostRecord::GetPriority(uint16_t aFlags)
458 0 : {
459 : if (IsHighPriority(aFlags)){
460 : return nsHostRecord::DNS_PRIORITY_HIGH;
461 0 : }
462 : if (IsMediumPriority(aFlags)) {
463 : return nsHostRecord::DNS_PRIORITY_MEDIUM;
464 : }
465 0 :
466 : return nsHostRecord::DNS_PRIORITY_LOW;
467 : }
468 :
469 : // Returns true if the entry can be removed, or false if it should be left.
470 : // Sets mResolveAgain true for entries being resolved right now.
471 0 : bool
472 : nsHostRecord::RemoveOrRefresh()
473 : {
474 0 : // no need to flush TRRed names, they're not resolved "locally"
475 0 : MutexAutoLock lock(addr_info_lock);
476 : if (addr_info && addr_info->IsTRR()) {
477 : return false;
478 0 : }
479 0 : if (mNative) {
480 : if (!onQueue) {
481 : // The request has been passed to the OS resolver. The resultant DNS
482 : // record should be considered stale and not trusted; set a flag to
483 0 : // ensure it is called again.
484 : mResolveAgain = true;
485 : }
486 : // if Onqueue is true, the host entry is already added to the cache
487 : // but is still pending to get resolved: just leave it in hash.
488 : return false;
489 : }
490 : // Already resolved; not in a pending state; remove from cache
491 : return true;
492 : }
493 :
494 : //----------------------------------------------------------------------------
495 :
496 : static const char kPrefGetTtl[] = "network.dns.get-ttl";
497 : static const char kPrefNativeIsLocalhost[] = "network.dns.native-is-localhost";
498 : static bool sGetTtlEnabled = false;
499 : mozilla::Atomic<bool, mozilla::Relaxed> gNativeIsLocalhost;
500 0 :
501 : static void DnsPrefChanged(const char* aPref, void* aClosure)
502 0 : {
503 : MOZ_ASSERT(NS_IsMainThread(),
504 : "Should be getting pref changed notification on main thread!");
505 0 :
506 0 : DebugOnly<nsHostResolver*> self = static_cast<nsHostResolver*>(aClosure);
507 : MOZ_ASSERT(self);
508 0 :
509 0 : if (!strcmp(aPref, kPrefGetTtl)) {
510 0 : sGetTtlEnabled = Preferences::GetBool(kPrefGetTtl);
511 0 : } else if (!strcmp(aPref, kPrefNativeIsLocalhost)) {
512 : gNativeIsLocalhost = Preferences::GetBool(kPrefNativeIsLocalhost);
513 0 : }
514 : }
515 0 :
516 : NS_IMPL_ISUPPORTS0(nsHostResolver)
517 0 :
518 : nsHostResolver::nsHostResolver(uint32_t maxCacheEntries,
519 0 : uint32_t defaultCacheEntryLifetime,
520 : uint32_t defaultGracePeriod)
521 : : mMaxCacheEntries(maxCacheEntries)
522 : , mDefaultCacheLifetime(defaultCacheEntryLifetime)
523 : , mDefaultGracePeriod(defaultGracePeriod)
524 : , mLock("nsHostResolver.mLock")
525 : , mIdleThreadCV(mLock, "nsHostResolver.mIdleThreadCV")
526 : , mEvictionQSize(0)
527 : , mShutdown(true)
528 : , mNumIdleThreads(0)
529 : , mThreadCount(0)
530 0 : , mActiveAnyThreadCount(0)
531 : , mPendingCount(0)
532 0 : {
533 : mCreationTime = PR_Now();
534 0 :
535 0 : mLongIdleTimeout = TimeDuration::FromSeconds(LongIdleTimeoutSeconds);
536 0 : mShortIdleTimeout = TimeDuration::FromSeconds(ShortIdleTimeoutSeconds);
537 : }
538 :
539 : nsHostResolver::~nsHostResolver() = default;
540 :
541 0 : nsresult
542 : nsHostResolver::Init()
543 0 : {
544 0 : MOZ_ASSERT(NS_IsMainThread());
545 : if (NS_FAILED(GetAddrInfoInit())) {
546 : return NS_ERROR_FAILURE;
547 : }
548 0 :
549 : LOG(("nsHostResolver::Init this=%p", this));
550 0 :
551 : mShutdown = false;
552 :
553 : // The preferences probably haven't been loaded from the disk yet, so we
554 : // need to register a callback that will set up the experiment once they
555 : // are. We also need to explicitly set a value for the props otherwise the
556 : // callback won't be called.
557 0 : {
558 0 : DebugOnly<nsresult> rv = Preferences::RegisterCallbackAndCall(
559 0 : &DnsPrefChanged, kPrefGetTtl, this);
560 : NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
561 0 : "Could not register DNS TTL pref callback.");
562 0 : rv = Preferences::RegisterCallbackAndCall(
563 0 : &DnsPrefChanged, kPrefNativeIsLocalhost, this);
564 : NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
565 : "Could not register DNS pref callback.");
566 : }
567 :
568 : #if defined(HAVE_RES_NINIT)
569 : // We want to make sure the system is using the correct resolver settings,
570 : // so we force it to reload those settings whenever we startup a subsequent
571 : // nsHostResolver instance. We assume that there is no reason to do this
572 : // for the first nsHostResolver instance since that is usually created
573 : // during application startup.
574 0 : static int initCount = 0;
575 0 : if (initCount++ > 0) {
576 0 : LOG(("Calling 'res_ninit'.\n"));
577 : res_ninit(&_res);
578 : }
579 : #endif
580 :
581 : return NS_OK;
582 : }
583 :
584 0 : void
585 : nsHostResolver::ClearPendingQueue(LinkedList<RefPtr<nsHostRecord>>& aPendingQ)
586 : {
587 0 : // loop through pending queue, erroring out pending lookups.
588 0 : if (!aPendingQ.isEmpty()) {
589 0 : for (RefPtr<nsHostRecord> rec : aPendingQ) {
590 0 : rec->Cancel();
591 : CompleteLookup(rec, NS_ERROR_ABORT, nullptr, rec->pb);
592 : }
593 0 : }
594 : }
595 :
596 : //
597 : // FlushCache() is what we call when the network has changed. We must not
598 : // trust names that were resolved before this change. They may resolve
599 : // differently now.
600 : //
601 : // This function removes all existing resolved host entries from the hash.
602 : // Names that are in the pending queues can be left there. Entries in the
603 : // cache that have 'Resolve' set true but not 'onQueue' are being resolved
604 : // right now, so we need to mark them to get re-resolved on completion!
605 :
606 0 : void
607 : nsHostResolver::FlushCache()
608 0 : {
609 0 : MutexAutoLock lock(mLock);
610 : mEvictionQSize = 0;
611 :
612 : // Clear the evictionQ and remove all its corresponding entries from
613 0 : // the cache first
614 0 : if (!mEvictionQ.isEmpty()) {
615 0 : for (RefPtr<nsHostRecord> rec : mEvictionQ) {
616 0 : rec->Cancel();
617 : mRecordDB.Remove(*static_cast<nsHostKey *>(rec));
618 0 : }
619 : mEvictionQ.clear();
620 : }
621 :
622 0 : // Refresh the cache entries that are resolving RIGHT now, remove the rest.
623 0 : for (auto iter = mRecordDB.Iter(); !iter.Done(); iter.Next()) {
624 : nsHostRecord* record = iter.UserData();
625 0 : // Try to remove the record, or mark it for refresh.
626 0 : if (record->RemoveOrRefresh()) {
627 0 : if (record->isInList()) {
628 : record->remove();
629 0 : }
630 : iter.Remove();
631 : }
632 0 : }
633 : }
634 :
635 0 : void
636 : nsHostResolver::Shutdown()
637 0 : {
638 : LOG(("Shutting down host resolver.\n"));
639 :
640 0 : {
641 0 : DebugOnly<nsresult> rv = Preferences::UnregisterCallback(
642 0 : &DnsPrefChanged, kPrefGetTtl, this);
643 : NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
644 : "Could not unregister DNS TTL pref callback.");
645 : }
646 0 :
647 : LinkedList<RefPtr<nsHostRecord>> pendingQHigh, pendingQMed, pendingQLow, evictionQ;
648 :
649 0 : {
650 : MutexAutoLock lock(mLock);
651 0 :
652 : mShutdown = true;
653 :
654 0 : // Move queues to temporary lists.
655 0 : pendingQHigh = std::move(mHighQ);
656 0 : pendingQMed = std::move(mMediumQ);
657 0 : pendingQLow = std::move(mLowQ);
658 : evictionQ = std::move(mEvictionQ);
659 0 :
660 0 : mEvictionQSize = 0;
661 : mPendingCount = 0;
662 0 :
663 0 : if (mNumIdleThreads)
664 : mIdleThreadCV.NotifyAll();
665 :
666 0 : // empty host database
667 : mRecordDB.Clear();
668 : }
669 0 :
670 0 : ClearPendingQueue(pendingQHigh);
671 0 : ClearPendingQueue(pendingQMed);
672 : ClearPendingQueue(pendingQLow);
673 0 :
674 0 : if (!evictionQ.isEmpty()) {
675 0 : for (RefPtr<nsHostRecord> rec : evictionQ) {
676 : rec->Cancel();
677 : }
678 : }
679 0 :
680 0 : pendingQHigh.clear();
681 0 : pendingQMed.clear();
682 0 : pendingQLow.clear();
683 : evictionQ.clear();
684 0 :
685 0 : for (auto iter = mRecordDB.Iter(); !iter.Done(); iter.Next()) {
686 : iter.UserData()->Cancel();
687 : }
688 : #ifdef NS_BUILD_REFCNT_LOGGING
689 :
690 : // Logically join the outstanding worker threads with a timeout.
691 : // Use this approach instead of PR_JoinThread() because that does
692 : // not allow a timeout which may be necessary for a semi-responsive
693 : // shutdown if the thread is blocked on a very slow DNS resolution.
694 : // mThreadCount is read outside of mLock, but the worst case
695 : // scenario for that race is one extra 25ms sleep.
696 0 :
697 0 : PRIntervalTime delay = PR_MillisecondsToInterval(25);
698 0 : PRIntervalTime stopTime = PR_IntervalNow() + PR_SecondsToInterval(20);
699 0 : while (mThreadCount && PR_IntervalNow() < stopTime)
700 : PR_Sleep(delay);
701 : #endif
702 :
703 0 : {
704 0 : mozilla::DebugOnly<nsresult> rv = GetAddrInfoShutdown();
705 : NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
706 : "Failed to shutdown GetAddrInfo");
707 0 : }
708 : }
709 :
710 0 : nsresult
711 : nsHostResolver::GetHostRecord(const nsACString &host,
712 : uint16_t flags, uint16_t af, bool pb,
713 : const nsCString &originSuffix,
714 : nsHostRecord **result)
715 0 : {
716 0 : MutexAutoLock lock(mLock);
717 : nsHostKey key(host, flags, af, pb, originSuffix);
718 0 :
719 0 : RefPtr<nsHostRecord>& entry = mRecordDB.GetOrInsert(key);
720 0 : if (!entry) {
721 : entry = new nsHostRecord(key);
722 : }
723 0 :
724 0 : RefPtr<nsHostRecord> rec = entry;
725 : if (rec->addr) {
726 : return NS_ERROR_FAILURE;
727 0 : }
728 : if (rec->mResolving) {
729 : return NS_ERROR_FAILURE;
730 0 : }
731 0 : *result = rec.forget().take();
732 : return NS_OK;
733 : }
734 :
735 0 : nsresult
736 : nsHostResolver::ResolveHost(const nsACString &aHost,
737 : const OriginAttributes &aOriginAttributes,
738 : uint16_t flags,
739 : uint16_t af,
740 : nsResolveHostCallback *aCallback)
741 0 : {
742 : nsAutoCString host(aHost);
743 0 : NS_ENSURE_TRUE(!host.IsEmpty(), NS_ERROR_UNEXPECTED);
744 :
745 : LOG(("Resolving host [%s]%s%s.\n", host.get(),
746 : flags & RES_BYPASS_CACHE ? " - bypassing cache" : "",
747 : flags & RES_REFRESH_CACHE ? " - refresh cache" : ""));
748 :
749 0 : // ensure that we are working with a valid hostname before proceeding. see
750 : // bug 304904 for details.
751 : if (!net_IsValidHostName(host))
752 0 : return NS_ERROR_UNKNOWN_HOST;
753 :
754 : RefPtr<nsResolveHostCallback> callback(aCallback);
755 0 : // if result is set inside the lock, then we need to issue the
756 0 : // callback before returning.
757 : RefPtr<nsHostRecord> result;
758 0 : nsresult status = NS_OK, rv = NS_OK;
759 : {
760 0 : MutexAutoLock lock(mLock);
761 :
762 : if (mShutdown) {
763 : rv = NS_ERROR_NOT_INITIALIZED;
764 : } else {
765 : // Used to try to parse to an IP address literal.
766 : PRNetAddr tempAddr;
767 0 : // Unfortunately, PR_StringToNetAddr does not properly initialize
768 : // the output buffer in the case of IPv6 input. See bug 223145.
769 : memset(&tempAddr, 0, sizeof(PRNetAddr));
770 :
771 : // check to see if there is already an entry for this |host|
772 : // in the hash table. if so, then check to see if we can't
773 : // just reuse the lookup result. otherwise, if there are
774 : // any pending callbacks, then add to pending callbacks queue,
775 0 : // and return. otherwise, add ourselves as first pending
776 0 : // callback, and proceed to do the lookup.
777 : nsAutoCString originSuffix;
778 0 : aOriginAttributes.CreateSuffix(originSuffix);
779 0 :
780 0 : nsHostKey key(host, flags, af,
781 0 : (aOriginAttributes.mPrivateBrowsingId > 0),
782 0 : originSuffix);
783 0 : RefPtr<nsHostRecord>& entry = mRecordDB.GetOrInsert(key);
784 : if (!entry) {
785 : entry = new nsHostRecord(key);
786 0 : }
787 0 :
788 0 : RefPtr<nsHostRecord> rec = entry;
789 0 : MOZ_ASSERT(rec, "Record should not be null");
790 0 : if (!(flags & RES_BYPASS_CACHE) &&
791 : rec->HasUsableResult(TimeStamp::NowLoRes(), flags)) {
792 0 : LOG((" Using cached record for host [%s].\n", host.get()));
793 0 : // put reference to host record on stack...
794 : result = rec;
795 : Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
796 :
797 : // For entries that are in the grace period
798 0 : // or all cached negative entries, use the cache but start a new
799 : // lookup in the background
800 0 : ConditionallyRefreshRecord(rec, host);
801 0 :
802 : if (rec->negative) {
803 0 : LOG((" Negative cache entry for host [%s].\n", host.get()));
804 0 : Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
805 : METHOD_NEGATIVE_HIT);
806 0 : status = NS_ERROR_UNKNOWN_HOST;
807 : }
808 : } else if (rec->addr) {
809 0 : // if the host name is an IP address literal and has been parsed,
810 : // go ahead and use it.
811 0 : LOG((" Using cached address for IP Literal [%s].\n", host.get()));
812 0 : Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
813 0 : METHOD_LITERAL);
814 : result = rec;
815 : } else if (PR_StringToNetAddr(host.get(), &tempAddr) == PR_SUCCESS) {
816 : // try parsing the host name as an IP address literal to short
817 0 : // circuit full host resolution. (this is necessary on some
818 : // platforms like Win9x. see bug 219376 for more details.)
819 : LOG((" Host is IP Literal [%s].\n", host.get()));
820 0 : // ok, just copy the result into the host record, and be done
821 0 : // with it! ;-)
822 : rec->addr = MakeUnique<NetAddr>();
823 : PRNetAddrToNetAddr(&tempAddr, rec->addr.get());
824 0 : // put reference to host record on stack...
825 0 : Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
826 0 : METHOD_LITERAL);
827 0 : result = rec;
828 0 : } else if (mPendingCount >= MAX_NON_PRIORITY_REQUESTS &&
829 0 : !IsHighPriority(flags) &&
830 : !rec->mResolving) {
831 : LOG((" Lookup queue full: dropping %s priority request for "
832 : "host [%s].\n",
833 0 : IsMediumPriority(flags) ? "medium" : "low", host.get()));
834 : Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
835 0 : METHOD_OVERFLOW);
836 0 : // This is a lower priority request and we are swamped, so refuse it.
837 0 : rv = NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
838 : } else if (flags & RES_OFFLINE) {
839 0 : LOG((" Offline request for host [%s]; ignoring.\n", host.get()));
840 : rv = NS_ERROR_OFFLINE;
841 : } else if (!rec->mResolving) {
842 : // If this is an IPV4 or IPV6 specific request, check if there is
843 0 : // an AF_UNSPEC entry we can use. Otherwise, hit the resolver...
844 0 :
845 : if (!(flags & RES_BYPASS_CACHE) &&
846 0 : ((af == PR_AF_INET) || (af == PR_AF_INET6))) {
847 0 : // First, search for an entry with AF_UNSPEC
848 0 : const nsHostKey unspecKey(host, flags, PR_AF_UNSPEC,
849 0 : (aOriginAttributes.mPrivateBrowsingId > 0),
850 : originSuffix);
851 0 : RefPtr<nsHostRecord> unspecRec = mRecordDB.Get(unspecKey);
852 0 :
853 : TimeStamp now = TimeStamp::NowLoRes();
854 0 : if (unspecRec && unspecRec->HasUsableResult(now, flags)) {
855 :
856 : MOZ_ASSERT(unspecRec->addr_info || unspecRec->negative,
857 0 : "Entry should be resolved or negative.");
858 :
859 : LOG((" Trying AF_UNSPEC entry for host [%s] af: %s.\n", host.get(),
860 : (af == PR_AF_INET) ? "AF_INET" : "AF_INET6"));
861 :
862 0 : // We need to lock in case any other thread is reading
863 : // addr_info.
864 : MutexAutoLock lock(rec->addr_info_lock);
865 :
866 : // XXX: note that this actually leaks addr_info.
867 0 : // For some reason, freeing the memory causes a crash in
868 0 : // nsDNSRecord::GetNextAddr - see bug 1422173
869 0 : rec->addr_info = nullptr;
870 0 : if (unspecRec->negative) {
871 0 : rec->negative = unspecRec->negative;
872 : rec->CopyExpirationTimesAndFlagsFrom(unspecRec);
873 : } else if (unspecRec->addr_info) {
874 : // Search for any valid address in the AF_UNSPEC entry
875 : // in the cache (not blacklisted and from the right
876 0 : // family).
877 0 : NetAddrElement *addrIter =
878 0 : unspecRec->addr_info->mAddresses.getFirst();
879 0 : while (addrIter) {
880 0 : if ((af == addrIter->mAddress.inet.family) &&
881 0 : !unspecRec->Blacklisted(&addrIter->mAddress)) {
882 0 : if (!rec->addr_info) {
883 0 : rec->addr_info = new AddrInfo(
884 0 : unspecRec->addr_info->mHostName,
885 0 : unspecRec->addr_info->mCanonicalName,
886 0 : unspecRec->addr_info->IsTRR()
887 : );
888 0 : rec->CopyExpirationTimesAndFlagsFrom(unspecRec);
889 0 : }
890 : rec->addr_info->AddAddress(
891 0 : new NetAddrElement(*addrIter));
892 : }
893 : addrIter = addrIter->getNext();
894 : }
895 0 : }
896 0 : // Now check if we have a new record.
897 0 : if (rec->HasUsableResult(now, flags)) {
898 0 : result = rec;
899 : if (rec->negative) {
900 : status = NS_ERROR_UNKNOWN_HOST;
901 0 : }
902 0 : Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
903 0 : METHOD_HIT);
904 : ConditionallyRefreshRecord(rec, host);
905 : } else if (af == PR_AF_INET6) {
906 : // For AF_INET6, a new lookup means another AF_UNSPEC
907 : // lookup. We have already iterated through the
908 0 : // AF_UNSPEC addresses, so we mark this record as
909 : // negative.
910 0 : LOG((" No AF_INET6 in AF_UNSPEC entry: "
911 0 : "host [%s] unknown host.", host.get()));
912 0 : result = rec;
913 : rec->negative = true;
914 0 : status = NS_ERROR_UNKNOWN_HOST;
915 : Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
916 : METHOD_NEGATIVE_HIT);
917 : }
918 : }
919 : }
920 0 : // If no valid address was found in the cache or this is an
921 0 : // AF_UNSPEC request, then start a new lookup.
922 : if (!result) {
923 0 : LOG((" No usable address in cache for host [%s].", host.get()));
924 0 :
925 : if (flags & RES_REFRESH_CACHE) {
926 : rec->Invalidate();
927 : }
928 0 :
929 0 : // Add callback to the list of pending callbacks.
930 0 : rec->mCallbacks.insertBack(callback);
931 : rec->flags = flags;
932 0 : rv = NameLookup(rec);
933 0 : Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
934 0 : METHOD_NETWORK_FIRST);
935 : if (NS_FAILED(rv) && callback->isInList()) {
936 0 : callback->remove();
937 : } else {
938 : LOG((" DNS lookup for host [%s] blocking "
939 : "pending 'getaddrinfo' query: callback [%p]",
940 : host.get(), callback.get()));
941 0 : }
942 : }
943 : } else if (rec->mDidCallbacks) {
944 0 : // record is still pending more (TRR) data; make the callback
945 : // at once
946 0 : result = rec;
947 0 : // make it count as a hit
948 : Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
949 0 : LOG((" Host [%s] re-using early TRR resolve data\n", host.get()));
950 : } else {
951 : LOG((" Host [%s] is being resolved. Appending callback "
952 0 : "[%p].", host.get(), callback.get()));
953 0 :
954 : rec->mCallbacks.insertBack(callback);
955 0 : if (rec->onQueue) {
956 : Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
957 : METHOD_NETWORK_SHARED);
958 :
959 : // Consider the case where we are on a pending queue of
960 : // lower priority than the request is being made at.
961 0 : // In that case we should upgrade to the higher queue.
962 0 :
963 : if (IsHighPriority(flags) &&
964 0 : !IsHighPriority(rec->flags)) {
965 0 : // Move from (low|med) to high.
966 0 : NS_ASSERTION(rec->onQueue, "Moving Host Record Not Currently Queued");
967 0 : rec->remove();
968 0 : mHighQ.insertBack(rec);
969 0 : rec->flags = flags;
970 0 : ConditionallyCreateThread(rec);
971 : } else if (IsMediumPriority(flags) &&
972 0 : IsLowPriority(rec->flags)) {
973 0 : // Move from low to med.
974 0 : NS_ASSERTION(rec->onQueue, "Moving Host Record Not Currently Queued");
975 0 : rec->remove();
976 0 : mMediumQ.insertBack(rec);
977 : rec->flags = flags;
978 : mIdleThreadCV.Notify();
979 : }
980 : }
981 : }
982 : }
983 0 : }
984 0 :
985 0 : if (result) {
986 : if (callback->isInList()) {
987 0 : callback->remove();
988 : }
989 : callback->OnResolveHostComplete(this, result, status);
990 : }
991 :
992 : return rv;
993 : }
994 0 :
995 : void
996 : nsHostResolver::DetachCallback(const nsACString &host,
997 : const OriginAttributes &aOriginAttributes,
998 : uint16_t flags,
999 : uint16_t af,
1000 : nsResolveHostCallback *aCallback,
1001 0 : nsresult status)
1002 0 : {
1003 : RefPtr<nsHostRecord> rec;
1004 : RefPtr<nsResolveHostCallback> callback(aCallback);
1005 0 :
1006 : {
1007 0 : MutexAutoLock lock(mLock);
1008 0 :
1009 : nsAutoCString originSuffix;
1010 0 : aOriginAttributes.CreateSuffix(originSuffix);
1011 0 :
1012 0 : nsHostKey key(host, flags, af,
1013 0 : (aOriginAttributes.mPrivateBrowsingId > 0),
1014 0 : originSuffix);
1015 : RefPtr<nsHostRecord> entry = mRecordDB.Get(key);
1016 : if (entry) {
1017 : // walk list looking for |callback|... we cannot assume
1018 0 : // that it will be there!
1019 0 :
1020 0 : for (nsResolveHostCallback* c: entry->mCallbacks) {
1021 0 : if (c == callback) {
1022 0 : rec = entry;
1023 : c->remove();
1024 : break;
1025 : }
1026 : }
1027 : }
1028 : }
1029 :
1030 0 : // complete callback with the given status code; this would only be done if
1031 0 : // the record was in the process of being resolved.
1032 : if (rec) {
1033 0 : callback->OnResolveHostComplete(this, rec, status);
1034 : }
1035 : }
1036 0 :
1037 : nsresult
1038 0 : nsHostResolver::ConditionallyCreateThread(nsHostRecord *rec)
1039 : {
1040 0 : if (mNumIdleThreads) {
1041 : // wake up idle thread to process this lookup
1042 0 : mIdleThreadCV.Notify();
1043 0 : }
1044 : else if ((mThreadCount < HighThreadThreshold) ||
1045 0 : (IsHighPriority(rec->flags) && mThreadCount < MAX_RESOLVER_THREADS)) {
1046 : // dispatch new worker thread
1047 0 : NS_ADDREF_THIS(); // owning reference passed to thread
1048 :
1049 : mThreadCount++;
1050 : PRThread *thr = PR_CreateThread(PR_SYSTEM_THREAD,
1051 : ThreadFunc,
1052 : this,
1053 : PR_PRIORITY_NORMAL,
1054 0 : PR_GLOBAL_THREAD,
1055 0 : PR_UNJOINABLE_THREAD,
1056 0 : 0);
1057 0 : if (!thr) {
1058 0 : mThreadCount--;
1059 : NS_RELEASE_THIS();
1060 : return NS_ERROR_OUT_OF_MEMORY;
1061 : }
1062 0 : }
1063 : else {
1064 : LOG((" Unable to find a thread for looking up host [%s].\n", rec->host.get()));
1065 : }
1066 : return NS_OK;
1067 : }
1068 :
1069 : // make sure the mTrrLock is held when this is used!
1070 : #define TRROutstanding() ((rec->mTrrA || rec->mTrrAAAA))
1071 0 :
1072 : nsresult
1073 0 : nsHostResolver::TrrLookup_unlocked(nsHostRecord *rec, TRR *pushedTRR)
1074 0 : {
1075 : MutexAutoLock lock(mLock);
1076 : return TrrLookup(rec, pushedTRR);
1077 : }
1078 :
1079 : // returns error if no TRR resolve is issued
1080 0 : // it is impt this is not called while a native lookup is going on
1081 : nsresult
1082 0 : nsHostResolver::TrrLookup(nsHostRecord *aRec, TRR *pushedTRR)
1083 0 : {
1084 : RefPtr<nsHostRecord> rec(aRec);
1085 : mLock.AssertCurrentThreadOwns();
1086 0 : #ifdef DEBUG
1087 0 : {
1088 : MutexAutoLock trrlock(rec->mTrrLock);
1089 : MOZ_ASSERT(!TRROutstanding());
1090 0 : }
1091 : #endif
1092 0 : MOZ_ASSERT(!rec->mResolving);
1093 0 :
1094 : if (!gTRRService || !gTRRService->Enabled()) {
1095 : LOG(("TrrLookup:: %s service not enabled\n", rec->host.get()));
1096 : return NS_ERROR_UNKNOWN_HOST;
1097 0 : }
1098 :
1099 0 : if (rec->isInList()) {
1100 0 : // we're already on the eviction queue. This is a renewal
1101 : MOZ_ASSERT(mEvictionQSize);
1102 0 : AssertOnQ(rec, mEvictionQ);
1103 0 :
1104 : rec->remove();
1105 : mEvictionQSize--;
1106 0 : }
1107 0 :
1108 0 : rec->mTRRSuccess = 0; // bump for each successful TRR response
1109 0 : rec->mTrrAUsed = nsHostRecord::INIT;
1110 0 : rec->mTrrAAAAUsed = nsHostRecord::INIT;
1111 : rec->mTrrStart = TimeStamp::Now();
1112 : rec->mTRRUsed = true; // this record gets TRR treatment
1113 :
1114 0 : // If asking for AF_UNSPEC, issue both A and AAAA.
1115 0 : // If asking for AF_INET6 or AF_INET, do only that single type
1116 0 : enum TrrType rectype = (rec->af == AF_INET6)? TRRTYPE_AAAA : TRRTYPE_A;
1117 : if (pushedTRR) {
1118 : rectype = pushedTRR->Type();
1119 : }
1120 : bool sendAgain;
1121 0 :
1122 0 : bool madeQuery = false;
1123 0 : do {
1124 : sendAgain = false;
1125 : if ((TRRTYPE_AAAA == rectype) && gTRRService && gTRRService->DisableIPv6()) {
1126 0 : break;
1127 0 : }
1128 0 : LOG(("TRR Resolve %s type %d\n", rec->host.get(), (int)rectype));
1129 0 : RefPtr<TRR> trr;
1130 0 : MutexAutoLock trrlock(rec->mTrrLock);
1131 0 : trr = pushedTRR ? pushedTRR : new TRR(this, rec, rectype);
1132 0 : if (pushedTRR || NS_SUCCEEDED(NS_DispatchToMainThread(trr))) {
1133 0 : rec->mResolving++;
1134 0 : if (rectype == TRRTYPE_A) {
1135 0 : MOZ_ASSERT(!rec->mTrrA);
1136 0 : rec->mTrrA = trr;
1137 0 : rec->mTrrAUsed = nsHostRecord::STARTED;
1138 0 : } else if (rectype == TRRTYPE_AAAA) {
1139 0 : MOZ_ASSERT(!rec->mTrrAAAA);
1140 : rec->mTrrAAAA = trr;
1141 0 : rec->mTrrAAAAUsed = nsHostRecord::STARTED;
1142 0 : } else {
1143 : LOG(("TrrLookup called with bad type set: %d\n", rectype));
1144 0 : MOZ_ASSERT(0);
1145 0 : }
1146 0 : madeQuery = true;
1147 0 : if (!pushedTRR && (rec->af == AF_UNSPEC) && (rectype == TRRTYPE_A)) {
1148 : rectype = TRRTYPE_AAAA;
1149 : sendAgain = true;
1150 : }
1151 : }
1152 0 : } while (sendAgain);
1153 :
1154 : return madeQuery ? NS_OK : NS_ERROR_UNKNOWN_HOST;
1155 : }
1156 0 :
1157 : void
1158 : nsHostResolver::AssertOnQ(nsHostRecord *rec, LinkedList<RefPtr<nsHostRecord>>& q)
1159 0 : {
1160 0 : #ifdef DEBUG
1161 0 : MOZ_ASSERT(!q.isEmpty());
1162 0 : MOZ_ASSERT(rec->isInList());
1163 0 : for (RefPtr<nsHostRecord> r: q) {
1164 : if (rec == r) {
1165 : return;
1166 0 : }
1167 : }
1168 : MOZ_ASSERT(false, "Did not find element");
1169 : #endif
1170 : }
1171 0 :
1172 : nsresult
1173 0 : nsHostResolver::NativeLookup(nsHostRecord *aRec)
1174 0 : {
1175 : mLock.AssertCurrentThreadOwns();
1176 0 : RefPtr<nsHostRecord> rec(aRec);
1177 :
1178 : rec->mNativeStart = TimeStamp::Now();
1179 0 :
1180 0 : // Add rec to one of the pending queues, possibly removing it from mEvictionQ.
1181 0 : if (rec->isInList()) {
1182 0 : MOZ_ASSERT(mEvictionQSize);
1183 0 : AssertOnQ(rec, mEvictionQ);
1184 : rec->remove(); // was on the eviction queue
1185 : mEvictionQSize--;
1186 0 : }
1187 :
1188 0 : switch (nsHostRecord::GetPriority(rec->flags)) {
1189 : case nsHostRecord::DNS_PRIORITY_HIGH:
1190 : mHighQ.insertBack(rec);
1191 : break;
1192 0 :
1193 : case nsHostRecord::DNS_PRIORITY_MEDIUM:
1194 : mMediumQ.insertBack(rec);
1195 : break;
1196 0 :
1197 : case nsHostRecord::DNS_PRIORITY_LOW:
1198 : mLowQ.insertBack(rec);
1199 0 : break;
1200 : }
1201 0 : mPendingCount++;
1202 0 :
1203 0 : rec->mNative = true;
1204 0 : rec->mNativeUsed = true;
1205 : rec->onQueue = true;
1206 0 : rec->mResolving++;
1207 :
1208 0 : nsresult rv = ConditionallyCreateThread(rec);
1209 :
1210 : LOG ((" DNS thread counters: total=%d any-live=%d idle=%d pending=%d\n",
1211 : static_cast<uint32_t>(mThreadCount),
1212 : static_cast<uint32_t>(mActiveAnyThreadCount),
1213 : static_cast<uint32_t>(mNumIdleThreads),
1214 0 : static_cast<uint32_t>(mPendingCount)));
1215 :
1216 : return rv;
1217 : }
1218 0 :
1219 : ResolverMode
1220 0 : nsHostResolver::Mode()
1221 0 : {
1222 : if (gTRRService) {
1223 : return static_cast<ResolverMode>(gTRRService->Mode());
1224 : }
1225 :
1226 : return MODE_NATIVEONLY;
1227 : }
1228 :
1229 0 : // Kick-off a name resolve operation, using native resolver and/or TRR
1230 : nsresult
1231 0 : nsHostResolver::NameLookup(nsHostRecord *rec)
1232 0 : {
1233 0 : nsresult rv = NS_ERROR_UNKNOWN_HOST;
1234 : if (rec->mResolving) {
1235 : LOG(("NameLookup %s while already resolving\n", rec->host.get()));
1236 : return NS_OK;
1237 0 : }
1238 :
1239 0 : ResolverMode mode = rec->mResolverMode = Mode();
1240 0 :
1241 0 : rec->mNativeUsed = false;
1242 0 : rec->mTRRUsed = false;
1243 0 : rec->mNativeSuccess = false;
1244 0 : rec->mTRRSuccess = 0;
1245 0 : rec->mDidCallbacks = false;
1246 : rec->mTrrAUsed = nsHostRecord::INIT;
1247 0 : rec->mTrrAAAAUsed = nsHostRecord::INIT;
1248 0 :
1249 : if (rec->flags & RES_DISABLE_TRR) {
1250 : if (mode == MODE_TRRONLY) {
1251 : return rv;
1252 : }
1253 : mode = MODE_NATIVEONLY;
1254 0 : }
1255 0 :
1256 : if (!TRR_DISABLED(mode)) {
1257 : rv = TrrLookup(rec);
1258 0 : }
1259 0 :
1260 0 : if ((mode == MODE_PARALLEL) ||
1261 0 : TRR_DISABLED(mode) ||
1262 0 : (mode == MODE_SHADOW) ||
1263 : ((mode == MODE_TRRFIRST) && NS_FAILED(rv))) {
1264 : rv = NativeLookup(rec);
1265 : }
1266 :
1267 : return rv;
1268 : }
1269 0 :
1270 : nsresult
1271 0 : nsHostResolver::ConditionallyRefreshRecord(nsHostRecord *rec, const nsACString &host)
1272 0 : {
1273 0 : if ((rec->CheckExpiration(TimeStamp::NowLoRes()) != nsHostRecord::EXP_VALID
1274 : || rec->negative) && !rec->mResolving) {
1275 0 : LOG((" Using %s cache entry for host [%s] but starting async renewal.",
1276 : rec->negative ? "negative" :"positive", host.BeginReading()));
1277 0 : NameLookup(rec);
1278 :
1279 : if (!rec->negative) {
1280 : // negative entries are constantly being refreshed, only
1281 0 : // track positive grace period induced renewals
1282 : Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
1283 : METHOD_RENEWAL);
1284 0 : }
1285 : }
1286 : return NS_OK;
1287 : }
1288 0 :
1289 : void
1290 0 : nsHostResolver::DeQueue(LinkedList<RefPtr<nsHostRecord>>& aQ, nsHostRecord **aResult)
1291 0 : {
1292 0 : RefPtr<nsHostRecord> rec = aQ.popFirst();
1293 0 : mPendingCount--;
1294 0 : rec.forget(aResult);
1295 : (*aResult)->onQueue = false;
1296 : }
1297 0 :
1298 : bool
1299 0 : nsHostResolver::GetHostToLookup(nsHostRecord **result)
1300 0 : {
1301 0 : bool timedOut = false;
1302 : TimeDuration timeout;
1303 0 : TimeStamp epoch, now;
1304 :
1305 0 : MutexAutoLock lock(mLock);
1306 0 :
1307 : timeout = (mNumIdleThreads >= HighThreadThreshold) ? mShortIdleTimeout : mLongIdleTimeout;
1308 0 : epoch = TimeStamp::Now();
1309 :
1310 : while (!mShutdown) {
1311 : // remove next record from Q; hand over owning reference. Check high, then med, then low
1312 :
1313 0 : #define SET_GET_TTL(var, val) (var)->mGetTtl = sGetTtlEnabled && (val)
1314 0 :
1315 0 : if (!mHighQ.isEmpty()) {
1316 0 : DeQueue (mHighQ, result);
1317 : SET_GET_TTL(*result, false);
1318 : return true;
1319 0 : }
1320 0 :
1321 0 : if (mActiveAnyThreadCount < HighThreadThreshold) {
1322 0 : if (!mMediumQ.isEmpty()) {
1323 0 : DeQueue (mMediumQ, result);
1324 0 : mActiveAnyThreadCount++;
1325 0 : (*result)->usingAnyThread = true;
1326 : SET_GET_TTL(*result, true);
1327 : return true;
1328 0 : }
1329 0 :
1330 0 : if (!mLowQ.isEmpty()) {
1331 0 : DeQueue (mLowQ, result);
1332 0 : mActiveAnyThreadCount++;
1333 0 : (*result)->usingAnyThread = true;
1334 : SET_GET_TTL(*result, true);
1335 : return true;
1336 : }
1337 : }
1338 :
1339 0 : // Determining timeout is racy, so allow one cycle through checking the queues
1340 : // before exiting.
1341 : if (timedOut)
1342 : break;
1343 :
1344 : // wait for one or more of the following to occur:
1345 : // (1) the pending queue has a host record to process
1346 : // (2) the shutdown flag has been set
1347 0 : // (3) the thread has been idle for too long
1348 0 :
1349 0 : mNumIdleThreads++;
1350 : mIdleThreadCV.Wait(timeout);
1351 0 : mNumIdleThreads--;
1352 :
1353 0 : now = TimeStamp::Now();
1354 :
1355 : if (now - epoch >= timeout) {
1356 : timedOut = true;
1357 : } else {
1358 : // It is possible that CondVar::Wait() was interrupted and returned
1359 : // early, in which case we will loop back and re-enter it. In that
1360 0 : // case we want to do so with the new timeout reduced to reflect
1361 0 : // time already spent waiting.
1362 : timeout -= now - epoch;
1363 : epoch = now;
1364 : }
1365 : }
1366 :
1367 : // tell thread to exit...
1368 : return false;
1369 : }
1370 0 :
1371 : void
1372 : nsHostResolver::PrepareRecordExpiration(nsHostRecord* rec) const
1373 0 : {
1374 0 : // NOTE: rec->addr_info_lock is already held by parent
1375 0 : MOZ_ASSERT(((bool)rec->addr_info) != rec->negative);
1376 0 : mLock.AssertCurrentThreadOwns();
1377 0 : if (!rec->addr_info) {
1378 0 : rec->SetExpiration(TimeStamp::NowLoRes(),
1379 : NEGATIVE_RECORD_LIFETIME, 0);
1380 : LOG(("Caching host [%s] negative record for %u seconds.\n",
1381 : rec->host.get(), NEGATIVE_RECORD_LIFETIME));
1382 : return;
1383 0 : }
1384 0 :
1385 : unsigned int lifetime = mDefaultCacheLifetime;
1386 0 : unsigned int grace = mDefaultGracePeriod;
1387 0 :
1388 0 : unsigned int ttl = mDefaultCacheLifetime;
1389 0 : if (sGetTtlEnabled || rec->addr_info->IsTRR()) {
1390 : if (rec->addr_info && rec->addr_info->ttl != AddrInfo::NO_TTL_DATA) {
1391 : ttl = rec->addr_info->ttl;
1392 : }
1393 : lifetime = ttl;
1394 : grace = 0;
1395 0 : }
1396 0 :
1397 : rec->SetExpiration(TimeStamp::NowLoRes(), lifetime, grace);
1398 : LOG(("Caching host [%s] record for %u seconds (grace %d).",
1399 : rec->host.get(), lifetime, grace));
1400 : }
1401 0 :
1402 : static nsresult
1403 0 : merge_rrset(AddrInfo *rrto, AddrInfo *rrfrom)
1404 : {
1405 : if (!rrto || !rrfrom) {
1406 : return NS_ERROR_NULL_POINTER;
1407 0 : }
1408 0 : NetAddrElement *element;
1409 0 : while ((element = rrfrom->mAddresses.getFirst())) {
1410 : element->remove(); // unlist from old
1411 : rrto->AddAddress(element); // enlist on new
1412 : }
1413 : return NS_OK;
1414 : }
1415 0 :
1416 : static bool
1417 0 : different_rrset(AddrInfo *rrset1, AddrInfo *rrset2)
1418 : {
1419 : if (!rrset1 || !rrset2) {
1420 : return true;
1421 0 : }
1422 0 :
1423 0 : LOG(("different_rrset %s\n", rrset1->mHostName.get()));
1424 : nsTArray<NetAddr> orderedSet1;
1425 0 : nsTArray<NetAddr> orderedSet2;
1426 :
1427 : if (rrset1->IsTRR() != rrset2->IsTRR()) {
1428 : return true;
1429 0 : }
1430 0 :
1431 0 : for (NetAddrElement *element = rrset1->mAddresses.getFirst();
1432 : element; element = element->getNext()) {
1433 0 : if (LOG_ENABLED()) {
1434 0 : char buf[128];
1435 : NetAddrToString(&element->mAddress, buf, 128);
1436 0 : LOG(("different_rrset add to set 1 %s\n", buf));
1437 : }
1438 : orderedSet1.InsertElementAt(orderedSet1.Length(), element->mAddress);
1439 0 : }
1440 0 :
1441 0 : for (NetAddrElement *element = rrset2->mAddresses.getFirst();
1442 : element; element = element->getNext()) {
1443 0 : if (LOG_ENABLED()) {
1444 0 : char buf[128];
1445 : NetAddrToString(&element->mAddress, buf, 128);
1446 0 : LOG(("different_rrset add to set 2 %s\n", buf));
1447 : }
1448 : orderedSet2.InsertElementAt(orderedSet2.Length(), element->mAddress);
1449 0 : }
1450 0 :
1451 : if (orderedSet1.Length() != orderedSet2.Length()) {
1452 : LOG(("different_rrset true due to length change\n"));
1453 0 : return true;
1454 0 : }
1455 : orderedSet1.Sort();
1456 0 : orderedSet2.Sort();
1457 0 :
1458 0 : for (uint32_t i = 0; i < orderedSet1.Length(); ++i) {
1459 : if (!(orderedSet1[i] == orderedSet2[i])) {
1460 : LOG(("different_rrset true due to content change\n"));
1461 : return true;
1462 0 : }
1463 : }
1464 : LOG(("different_rrset false\n"));
1465 : return false;
1466 : }
1467 :
1468 : //
1469 : // CompleteLookup() checks if the resolving should be redone and if so it
1470 : // returns LOOKUP_RESOLVEAGAIN, but only if 'status' is not NS_ERROR_ABORT.
1471 0 : // takes ownership of AddrInfo parameter
1472 : nsHostResolver::LookupStatus
1473 0 : nsHostResolver::CompleteLookup(nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb)
1474 0 : {
1475 0 : MutexAutoLock lock(mLock);
1476 : MOZ_ASSERT(rec);
1477 : MOZ_ASSERT(rec->pb == pb);
1478 :
1479 0 : // newRRSet needs to be taken into the hostrecord (which will then own it)
1480 : // or deleted on early return.
1481 0 : nsAutoPtr<AddrInfo> newRRSet(aNewRRSet);
1482 :
1483 0 : bool trrResult = newRRSet && newRRSet->IsTRR();
1484 0 :
1485 0 : if (rec->mResolveAgain && (status != NS_ERROR_ABORT) && !trrResult) {
1486 0 : LOG(("nsHostResolver record %p resolve again due to flushcache\n", rec));
1487 : rec->mResolveAgain = false;
1488 : return LOOKUP_RESOLVEAGAIN;
1489 0 : }
1490 0 :
1491 0 : MOZ_ASSERT(rec->mResolving);
1492 : rec->mResolving--;
1493 : LOG(("nsHostResolver::CompleteLookup %s %p %X trr=%d stillResolving=%d\n",
1494 : rec->host.get(), aNewRRSet, (unsigned int)status,
1495 0 : aNewRRSet ? aNewRRSet->IsTRR() : 0, rec->mResolving));
1496 0 :
1497 0 : if (trrResult) {
1498 : MutexAutoLock trrlock(rec->mTrrLock);
1499 : LOG(("TRR lookup Complete (%d) %s %s\n",
1500 0 : newRRSet->IsTRR(), newRRSet->mHostName.get(),
1501 0 : NS_SUCCEEDED(status) ? "OK" : "FAILED"));
1502 0 : MOZ_ASSERT(TRROutstanding());
1503 0 : if (newRRSet->IsTRR() == TRRTYPE_A) {
1504 0 : MOZ_ASSERT(rec->mTrrA);
1505 0 : rec->mTrrA = nullptr;
1506 0 : rec->mTrrAUsed = NS_SUCCEEDED(status) ? nsHostRecord::OK : nsHostRecord::FAILED;
1507 0 : } else if (newRRSet->IsTRR() == TRRTYPE_AAAA) {
1508 0 : MOZ_ASSERT(rec->mTrrAAAA);
1509 : rec->mTrrAAAA = nullptr;
1510 0 : rec->mTrrAAAAUsed = NS_SUCCEEDED(status) ? nsHostRecord::OK : nsHostRecord::FAILED;
1511 : } else {
1512 : MOZ_ASSERT(0);
1513 0 : }
1514 0 :
1515 : if (NS_SUCCEEDED(status)) {
1516 0 : rec->mTRRSuccess++;
1517 0 : }
1518 0 : if (TRROutstanding()) {
1519 : if (NS_FAILED(status)) {
1520 : return LOOKUP_OK; // wait for outstanding
1521 : }
1522 :
1523 0 : // There's another TRR complete pending. Wait for it and keep
1524 0 : // this RRset around until then.
1525 0 : MOZ_ASSERT(!rec->mFirstTRR && newRRSet);
1526 : rec->mFirstTRR = newRRSet; // autoPtr.swap()
1527 0 : MOZ_ASSERT(rec->mFirstTRR && !newRRSet);
1528 :
1529 : if (rec->mDidCallbacks || rec->mResolverMode == MODE_SHADOW) {
1530 : return LOOKUP_OK;
1531 0 : }
1532 :
1533 : if (rec->mTrrA && (!gTRRService || !gTRRService->EarlyAAAA())) {
1534 0 : // This is an early AAAA with a pending A response. Allowed
1535 : // only by pref.
1536 : LOG(("CompleteLookup: avoiding early use of TRR AAAA!\n"));
1537 : return LOOKUP_OK;
1538 : }
1539 :
1540 0 : // we can do some callbacks with this partial result which requires
1541 0 : // a deep copy
1542 : newRRSet = new AddrInfo(rec->mFirstTRR);
1543 : MOZ_ASSERT(rec->mFirstTRR && newRRSet);
1544 :
1545 : } else {
1546 0 : // no more outstanding TRRs
1547 0 : // If mFirstTRR is set, merge those addresses into current set!
1548 0 : if (rec->mFirstTRR) {
1549 : if (NS_SUCCEEDED(status)) {
1550 : merge_rrset(newRRSet, rec->mFirstTRR);
1551 0 : }
1552 : else {
1553 0 : newRRSet = rec->mFirstTRR; // transfers
1554 : }
1555 : rec->mFirstTRR = nullptr;
1556 0 : }
1557 :
1558 0 : if (!rec->mTRRSuccess) {
1559 0 : // no TRR success
1560 : newRRSet = nullptr;
1561 : status = NS_ERROR_UNKNOWN_HOST;
1562 0 : }
1563 0 :
1564 0 : if (!rec->mTRRSuccess && rec->mResolverMode == MODE_TRRFIRST) {
1565 0 : MOZ_ASSERT(!rec->mResolving);
1566 : NativeLookup(rec);
1567 : MOZ_ASSERT(rec->mResolving);
1568 : return LOOKUP_OK;
1569 : }
1570 : // continue
1571 0 : }
1572 :
1573 0 : if (NS_SUCCEEDED(status) && (rec->mTRRSuccess == 1)) {
1574 : // store the duration on first (used) TRR response
1575 : rec->mTrrDuration = TimeStamp::Now() - rec->mTrrStart;
1576 : }
1577 0 :
1578 0 : } else { // native resolve completed
1579 0 : if (rec->usingAnyThread) {
1580 : mActiveAnyThreadCount--;
1581 : rec->usingAnyThread = false;
1582 0 : }
1583 0 :
1584 0 : rec->mNative = false;
1585 3 : rec->mNativeSuccess = newRRSet ? true : false;
1586 : if (rec->mNativeSuccess) {
1587 : rec->mNativeDuration = TimeStamp::Now() - rec->mNativeStart;
1588 : }
1589 : }
1590 :
1591 : // update record fields. We might have a rec->addr_info already if a
1592 : // previous lookup result expired and we're reresolving it or we get
1593 0 : // a late second TRR response.
1594 0 : // note that we don't update the addr_info if this is trr shadow results
1595 0 : if (!mShutdown &&
1596 0 : !(trrResult && rec->mResolverMode == MODE_SHADOW)) {
1597 0 : MutexAutoLock lock(rec->addr_info_lock);
1598 0 : nsAutoPtr<AddrInfo> old_addr_info;
1599 0 : if (different_rrset(rec->addr_info, newRRSet)) {
1600 0 : LOG(("nsHostResolver record %p new gencnt\n", rec));
1601 3 : old_addr_info = rec->addr_info;
1602 : rec->addr_info = newRRSet.forget();
1603 0 : rec->addr_info_gencnt++;
1604 0 : } else {
1605 : if (rec->addr_info && newRRSet) {
1606 0 : rec->addr_info->ttl = newRRSet->ttl;
1607 : }
1608 0 : old_addr_info = newRRSet.forget();
1609 3 : }
1610 : rec->negative = !rec->addr_info;
1611 : PrepareRecordExpiration(rec);
1612 3 : }
1613 :
1614 3 : bool doCallbacks = true;
1615 :
1616 0 : if (trrResult && (rec->mResolverMode == MODE_SHADOW) && !rec->mDidCallbacks) {
1617 0 : // don't report result based only on suppressed TRR info
1618 : doCallbacks = false;
1619 3 : LOG(("nsHostResolver Suppressing TRR %s because it is first shadow result\n",
1620 : rec->host.get()));
1621 0 : } else if(trrResult && rec->mDidCallbacks) {
1622 : // already callback'ed on the first TRR response
1623 : LOG(("nsHostResolver Suppressing callback for second TRR response for %s\n",
1624 : rec->host.get()));
1625 : doCallbacks = false;
1626 : }
1627 1 :
1628 0 :
1629 : if (LOG_ENABLED()) {
1630 0 : MutexAutoLock lock(rec->addr_info_lock);
1631 0 : NetAddrElement *element;
1632 0 : if (rec->addr_info) {
1633 : for (element = rec->addr_info->mAddresses.getFirst();
1634 0 : element; element = element->getNext()) {
1635 0 : char buf[128];
1636 : NetAddrToString(&element->mAddress, buf, sizeof(buf));
1637 : LOG(("CompleteLookup: %s has %s\n", rec->host.get(), buf));
1638 0 : }
1639 : } else {
1640 : LOG(("CompleteLookup: %s has NO address\n", rec->host.get()));
1641 : }
1642 3 : }
1643 :
1644 : if (doCallbacks) {
1645 9 : // get the list of pending callbacks for this lookup, and notify
1646 : // them that the lookup is complete.
1647 3 : mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs = std::move(rec->mCallbacks);
1648 :
1649 0 : LOG(("nsHostResolver record %p calling back dns users\n", rec));
1650 5 :
1651 : for (nsResolveHostCallback* c = cbs.getFirst(); c; c = c->removeAndGetNext()) {
1652 3 : c->OnResolveHostComplete(this, rec, status);
1653 : }
1654 : rec->mDidCallbacks = true;
1655 0 : }
1656 3 :
1657 : if (!rec->mResolving && !mShutdown) {
1658 : rec->ResolveComplete();
1659 0 :
1660 0 : // add to mEvictionQ
1661 0 : MOZ_ASSERT(!rec->isInList());
1662 3 : mEvictionQ.insertBack(rec);
1663 : if (mEvictionQSize < mMaxCacheEntries) {
1664 : mEvictionQSize++;
1665 0 : } else {
1666 0 : // remove first element on mEvictionQ
1667 : RefPtr<nsHostRecord> head = mEvictionQ.popFirst();
1668 0 : mRecordDB.Remove(*static_cast<nsHostKey *>(head.get()));
1669 :
1670 0 : if (!head->negative) {
1671 0 : // record the age of the entry upon eviction.
1672 0 : TimeDuration age = TimeStamp::NowLoRes() - head->mValidStart;
1673 0 : Telemetry::Accumulate(Telemetry::DNS_CLEANUP_AGE,
1674 : static_cast<uint32_t>(age.ToSeconds() / 60));
1675 0 : if (head->CheckExpiration(TimeStamp::Now()) !=
1676 0 : nsHostRecord::EXP_EXPIRED) {
1677 : Telemetry::Accumulate(Telemetry::DNS_PREMATURE_EVICTION,
1678 : static_cast<uint32_t>(age.ToSeconds() / 60));
1679 : }
1680 : }
1681 : }
1682 : }
1683 :
1684 : #ifdef DNSQUERY_AVAILABLE
1685 : // Unless the result is from TRR, resolve again to get TTL
1686 : bool fromTRR = false;
1687 : {
1688 : MutexAutoLock lock(rec->addr_info_lock);
1689 : if(rec->addr_info && rec->addr_info->IsTRR()) {
1690 : fromTRR = true;
1691 : }
1692 : }
1693 : if (!fromTRR &&
1694 : !mShutdown && !rec->mGetTtl && !rec->mResolving && sGetTtlEnabled) {
1695 : LOG(("Issuing second async lookup for TTL for host [%s].", rec->host.get()));
1696 : rec->flags =
1697 : (rec->flags & ~RES_PRIORITY_MEDIUM) | RES_PRIORITY_LOW |
1698 : RES_DISABLE_TRR;
1699 : DebugOnly<nsresult> rv = NameLookup(rec);
1700 : NS_WARNING_ASSERTION(
1701 : NS_SUCCEEDED(rv),
1702 : "Could not issue second async lookup for TTL.");
1703 : }
1704 : #endif
1705 : return LOOKUP_OK;
1706 : }
1707 0 :
1708 : void
1709 : nsHostResolver::CancelAsyncRequest(const nsACString &host,
1710 : const OriginAttributes &aOriginAttributes,
1711 : uint16_t flags,
1712 : uint16_t af,
1713 : nsIDNSListener *aListener,
1714 : nsresult status)
1715 0 :
1716 : {
1717 0 : MutexAutoLock lock(mLock);
1718 0 :
1719 : nsAutoCString originSuffix;
1720 : aOriginAttributes.CreateSuffix(originSuffix);
1721 :
1722 0 : // Lookup the host record associated with host, flags & address family
1723 0 :
1724 0 : nsHostKey key(host, flags, af,
1725 0 : (aOriginAttributes.mPrivateBrowsingId > 0),
1726 0 : originSuffix);
1727 0 : RefPtr<nsHostRecord> rec = mRecordDB.Get(key);
1728 : if (rec) {
1729 0 : nsHostRecord* recPtr = nullptr;
1730 0 :
1731 0 : for (RefPtr<nsResolveHostCallback> c : rec->mCallbacks) {
1732 0 : if (c->EqualsAsyncListener(aListener)) {
1733 0 : c->remove();
1734 0 : recPtr = rec;
1735 : c->OnResolveHostComplete(this, recPtr, status);
1736 : break;
1737 : }
1738 : }
1739 0 :
1740 0 : // If there are no more callbacks, remove the hash table entry
1741 : if (recPtr && recPtr->mCallbacks.isEmpty()) {
1742 0 : mRecordDB.Remove(*static_cast<nsHostKey *>(recPtr));
1743 0 : // If record is on a Queue, remove it and then deref it
1744 : if (recPtr->isInList()) {
1745 : recPtr->remove();
1746 : }
1747 0 : }
1748 : }
1749 : }
1750 0 :
1751 : size_t
1752 0 : nsHostResolver::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
1753 : {
1754 0 : MutexAutoLock lock(mLock);
1755 :
1756 0 : size_t n = mallocSizeOf(this);
1757 0 :
1758 0 : n += mRecordDB.ShallowSizeOfExcludingThis(mallocSizeOf);
1759 0 : for (auto iter = mRecordDB.ConstIter(); !iter.Done(); iter.Next()) {
1760 : auto entry = iter.UserData();
1761 : n += entry->SizeOfIncludingThis(mallocSizeOf);
1762 : }
1763 :
1764 : // The following fields aren't measured.
1765 : // - mHighQ, mMediumQ, mLowQ, mEvictionQ, because they just point to
1766 : // nsHostRecords that also pointed to by entries |mRecordDB|, and
1767 0 : // measured when |mRecordDB| is measured.
1768 :
1769 : return n;
1770 : }
1771 1 :
1772 : void
1773 1 : nsHostResolver::ThreadFunc(void *arg)
1774 : {
1775 0 : LOG(("DNS lookup thread - starting execution.\n"));
1776 1 :
1777 : static nsThreadPoolNaming naming;
1778 0 : nsCString name = naming.GetNextThreadName("DNS Resolver");
1779 1 :
1780 : AUTO_PROFILER_REGISTER_THREAD(name.BeginReading());
1781 : NS_SetCurrentThreadName(name.BeginReading());
1782 1 :
1783 : #if defined(RES_RETRY_ON_FAILURE)
1784 0 : nsResState rs;
1785 0 : #endif
1786 1 : RefPtr<nsHostResolver> resolver = dont_AddRef((nsHostResolver *)arg);
1787 : RefPtr<nsHostRecord> rec;
1788 : AddrInfo *ai = nullptr;
1789 0 :
1790 0 : do {
1791 4 : if (!rec) {
1792 : RefPtr<nsHostRecord> tmpRec;
1793 : if (!resolver->GetHostToLookup(getter_AddRefs(tmpRec))) {
1794 : break; // thread shutdown signal
1795 0 : }
1796 3 : // GetHostToLookup() returns an owning reference
1797 : MOZ_ASSERT(tmpRec);
1798 : rec.swap(tmpRec);
1799 3 : }
1800 :
1801 : LOG(("DNS lookup thread - Calling getaddrinfo for host [%s].\n",
1802 0 : rec->host.get()));
1803 0 :
1804 0 : TimeStamp startTime = TimeStamp::Now();
1805 0 : bool getTtl = rec->mGetTtl;
1806 3 : nsresult status = GetAddrInfo(rec->host, rec->af,
1807 : rec->flags, &ai,
1808 0 : getTtl);
1809 0 : #if defined(RES_RETRY_ON_FAILURE)
1810 0 : if (NS_FAILED(status) && rs.Reset()) {
1811 : status = GetAddrInfo(rec->host, rec->af,
1812 : rec->flags, &ai, getTtl);
1813 : }
1814 : #endif
1815 6 :
1816 : { // obtain lock to check shutdown and manage inter-module telemetry
1817 0 : MutexAutoLock lock(resolver->mLock);
1818 0 :
1819 3 : if (!resolver->mShutdown) {
1820 : TimeDuration elapsed = TimeStamp::Now() - startTime;
1821 3 : uint32_t millis = static_cast<uint32_t>(elapsed.ToMilliseconds());
1822 :
1823 3 : if (NS_SUCCEEDED(status)) {
1824 : Telemetry::HistogramID histogramID;
1825 : if (!rec->addr_info_gencnt) {
1826 0 : // Time for initial lookup.
1827 : histogramID = Telemetry::DNS_LOOKUP_TIME;
1828 : } else if (!getTtl) {
1829 : // Time for renewal; categorized by expiration strategy.
1830 : histogramID = Telemetry::DNS_RENEWAL_TIME;
1831 0 : } else {
1832 : // Time to get TTL; categorized by expiration strategy.
1833 3 : histogramID = Telemetry::DNS_RENEWAL_TIME_FOR_TTL;
1834 : }
1835 0 : Telemetry::Accumulate(histogramID, millis);
1836 : } else {
1837 : Telemetry::Accumulate(Telemetry::DNS_FAILED_LOOKUP_TIME, millis);
1838 : }
1839 : }
1840 3 : }
1841 :
1842 : LOG(("DNS lookup thread - lookup completed for host [%s]: %s.\n",
1843 : rec->host.get(),
1844 6 : ai ? "success" : "failure: unknown host"));
1845 :
1846 0 : if (LOOKUP_RESOLVEAGAIN == resolver->CompleteLookup(rec, status, ai, rec->pb)) {
1847 : // leave 'rec' assigned and loop to make a renewed host resolve
1848 0 : LOG(("DNS lookup thread - Re-resolving host [%s].\n", rec->host.get()));
1849 3 : } else {
1850 : rec = nullptr;
1851 : }
1852 0 : } while(true);
1853 0 :
1854 0 : resolver->mThreadCount--;
1855 0 : resolver = nullptr;
1856 : LOG(("DNS lookup thread - queue empty, thread finished.\n"));
1857 : }
1858 0 :
1859 : void
1860 : nsHostResolver::SetCacheLimits(uint32_t aMaxCacheEntries,
1861 : uint32_t aDefaultCacheEntryLifetime,
1862 0 : uint32_t aDefaultGracePeriod)
1863 0 : {
1864 0 : MutexAutoLock lock(mLock);
1865 0 : mMaxCacheEntries = aMaxCacheEntries;
1866 0 : mDefaultCacheLifetime = aDefaultCacheEntryLifetime;
1867 : mDefaultGracePeriod = aDefaultGracePeriod;
1868 : }
1869 1 :
1870 : nsresult
1871 : nsHostResolver::Create(uint32_t maxCacheEntries,
1872 : uint32_t defaultCacheEntryLifetime,
1873 : uint32_t defaultGracePeriod,
1874 : nsHostResolver **result)
1875 0 : {
1876 1 : auto *res = new nsHostResolver(maxCacheEntries, defaultCacheEntryLifetime,
1877 : defaultGracePeriod);
1878 0 : NS_ADDREF(res);
1879 1 :
1880 0 : nsresult rv = res->Init();
1881 : if (NS_FAILED(rv))
1882 0 : NS_RELEASE(res);
1883 1 :
1884 : *result = res;
1885 : return rv;
1886 : }
1887 0 :
1888 : void
1889 0 : nsHostResolver::GetDNSCacheEntries(nsTArray<DNSCacheEntries> *args)
1890 0 : {
1891 : MutexAutoLock lock(mLock);
1892 : for (auto iter = mRecordDB.Iter(); !iter.Done(); iter.Next()) {
1893 0 : // We don't pay attention to address literals, only resolved domains.
1894 0 : // Also require a host.
1895 0 : nsHostRecord* rec = iter.UserData();
1896 0 : MOZ_ASSERT(rec, "rec should never be null here!");
1897 : if (!rec || !rec->addr_info) {
1898 : continue;
1899 0 : }
1900 0 :
1901 0 : DNSCacheEntries info;
1902 0 : info.hostname = rec->host;
1903 0 : info.family = rec->af;
1904 0 : info.expiration =
1905 : (int64_t)(rec->mValidEnd - TimeStamp::NowLoRes()).ToSeconds();
1906 0 : if (info.expiration <= 0) {
1907 : // We only need valid DNS cache entries
1908 : continue;
1909 : }
1910 0 :
1911 : {
1912 0 : MutexAutoLock lock(rec->addr_info_lock);
1913 0 :
1914 0 : NetAddr *addr = nullptr;
1915 0 : NetAddrElement *addrElement = rec->addr_info->mAddresses.getFirst();
1916 : if (addrElement) {
1917 0 : addr = &addrElement->mAddress;
1918 : }
1919 0 : while (addr) {
1920 0 : char buf[kIPv6CStrBufSize];
1921 : if (NetAddrToString(addr, buf, sizeof(buf))) {
1922 0 : info.hostaddr.AppendElement(buf);
1923 0 : }
1924 0 : addr = nullptr;
1925 0 : addrElement = addrElement->getNext();
1926 : if (addrElement) {
1927 : addr = &addrElement->mAddress;
1928 0 : }
1929 : }
1930 : info.TRR = rec->addr_info->IsTRR();
1931 0 : }
1932 :
1933 : args->AppendElement(info);
1934 : }
1935 : }
1936 :
1937 : #undef LOG
1938 : #undef LOG_ENABLED
|