/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 |