Coverage Report

Created: 2018-09-25 14:53

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