nsHostResolver.cpp (66882B)
1 /* vim:set ts=4 sw=2 sts=2 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 #include "nsIThreadPool.h" 7 #if defined(HAVE_RES_NINIT) 8 # include <sys/types.h> 9 # include <netinet/in.h> 10 # include <arpa/inet.h> 11 # include <arpa/nameser.h> 12 # include <resolv.h> 13 #endif 14 15 #include <stdlib.h> 16 #include <ctime> 17 #include "nsHostResolver.h" 18 #include "nsError.h" 19 #include "nsIOService.h" 20 #include "nsISupports.h" 21 #include "nsISupportsUtils.h" 22 #include "nsIThreadManager.h" 23 #include "nsComponentManagerUtils.h" 24 #include "nsNetUtil.h" 25 #include "nsPrintfCString.h" 26 #include "nsXPCOMCIDInternal.h" 27 #include "prthread.h" 28 #include "prerror.h" 29 #include "prtime.h" 30 #include "mozilla/Logging.h" 31 #include "PLDHashTable.h" 32 #include "nsQueryObject.h" 33 #include "nsURLHelper.h" 34 #include "nsThreadUtils.h" 35 #include "nsThreadPool.h" 36 #include "GetAddrInfo.h" 37 #include "TRR.h" 38 #include "TRRQuery.h" 39 #include "TRRService.h" 40 41 #include "mozilla/Atomics.h" 42 #include "mozilla/glean/NetwerkMetrics.h" 43 #include "mozilla/TimeStamp.h" 44 #include "mozilla/glean/NetwerkDnsMetrics.h" 45 #include "mozilla/DebugOnly.h" 46 #include "mozilla/Preferences.h" 47 #include "mozilla/StaticPrefs_network.h" 48 // Put DNSLogging.h at the end to avoid LOG being overwritten by other headers. 49 #include "DNSLogging.h" 50 51 #ifdef XP_WIN 52 # include "mozilla/WindowsVersion.h" 53 #endif // XP_WIN 54 55 #ifdef MOZ_WIDGET_ANDROID 56 # include "mozilla/jni/Utils.h" 57 #endif 58 59 #define IS_ADDR_TYPE(_type) ((_type) == nsIDNSService::RESOLVE_TYPE_DEFAULT) 60 #define IS_OTHER_TYPE(_type) ((_type) != nsIDNSService::RESOLVE_TYPE_DEFAULT) 61 62 using namespace mozilla; 63 using namespace mozilla::net; 64 65 // None of our implementations expose a TTL for negative responses, so we use a 66 // constant always. 67 static const unsigned int NEGATIVE_RECORD_LIFETIME = 60; 68 69 //---------------------------------------------------------------------------- 70 71 // Use a persistent thread pool in order to avoid spinning up new threads all 72 // the time. In particular, thread creation results in a res_init() call from 73 // libc which is quite expensive. 74 // 75 // The pool dynamically grows between 0 and MaxResolverThreads() in size. New 76 // requests go first to an idle thread. If that cannot be found and there are 77 // fewer than MaxResolverThreads() currently in the pool a new thread is created 78 // for high priority requests. If the new request is at a lower priority a new 79 // thread will only be created if there are fewer than 80 // MaxResolverThreadsAnyPriority() currently outstanding. If a thread cannot be 81 // created or an idle thread located for the request it is queued. 82 // 83 // When the pool is greater than MaxResolverThreadsAnyPriority() in size a 84 // thread will be destroyed after ShortIdleTimeoutSeconds of idle time. Smaller 85 // pools use LongIdleTimeoutSeconds for a timeout period. 86 87 // for threads 1 -> MaxResolverThreadsAnyPriority() 88 #define LongIdleTimeoutSeconds 300 89 // for threads MaxResolverThreadsAnyPriority() + 1 -> MaxResolverThreads() 90 #define ShortIdleTimeoutSeconds 60 91 92 using namespace mozilla; 93 94 namespace mozilla::net { 95 LazyLogModule gHostResolverLog("nsHostResolver"); 96 } // namespace mozilla::net 97 98 //---------------------------------------------------------------------------- 99 100 class DnsThreadListener final : public nsIThreadPoolListener { 101 NS_DECL_THREADSAFE_ISUPPORTS 102 NS_DECL_NSITHREADPOOLLISTENER 103 private: 104 virtual ~DnsThreadListener() = default; 105 }; 106 107 NS_IMETHODIMP 108 DnsThreadListener::OnThreadCreated() { return NS_OK; } 109 110 NS_IMETHODIMP 111 DnsThreadListener::OnThreadShuttingDown() { 112 DNSThreadShutdown(); 113 return NS_OK; 114 } 115 116 NS_IMPL_ISUPPORTS(DnsThreadListener, nsIThreadPoolListener) 117 118 //---------------------------------------------------------------------------- 119 120 mozilla::Atomic<bool, mozilla::Relaxed> sNativeHTTPSSupported{false}; 121 122 NS_IMPL_ISUPPORTS0(nsHostResolver) 123 124 nsHostResolver::nsHostResolver() 125 : mIdleTaskCV(mLock, "nsHostResolver.mIdleTaskCV") { 126 mCreationTime = PR_Now(); 127 128 mLongIdleTimeout = TimeDuration::FromSeconds(LongIdleTimeoutSeconds); 129 mShortIdleTimeout = TimeDuration::FromSeconds(ShortIdleTimeoutSeconds); 130 } 131 132 nsHostResolver::~nsHostResolver() = default; 133 134 nsresult nsHostResolver::Init() MOZ_NO_THREAD_SAFETY_ANALYSIS { 135 MOZ_ASSERT(NS_IsMainThread()); 136 if (NS_FAILED(GetAddrInfoInit())) { 137 return NS_ERROR_FAILURE; 138 } 139 140 LOG(("nsHostResolver::Init this=%p", this)); 141 142 mShutdown = false; 143 mNCS = NetworkConnectivityService::GetSingleton(); 144 145 #if defined(HAVE_RES_NINIT) 146 // We want to make sure the system is using the correct resolver settings, 147 // so we force it to reload those settings whenever we startup a subsequent 148 // nsHostResolver instance. We assume that there is no reason to do this 149 // for the first nsHostResolver instance since that is usually created 150 // during application startup. 151 static int initCount = 0; 152 if (initCount++ > 0) { 153 auto result = res_ninit(&_res); 154 LOG(("nsHostResolver::Init > 'res_ninit' returned %d", result)); 155 } 156 #endif 157 158 // We can configure the threadpool to keep threads alive for a while after 159 // the last ThreadFunc task has been executed. 160 int32_t poolTimeoutSecs = 161 StaticPrefs::network_dns_resolver_thread_extra_idle_time_seconds(); 162 uint32_t poolTimeoutMs; 163 if (poolTimeoutSecs < 0) { 164 // This means never shut down the idle threads 165 poolTimeoutMs = UINT32_MAX; 166 } else { 167 // We clamp down the idle time between 0 and one hour. 168 poolTimeoutMs = 169 std::clamp<uint32_t>(poolTimeoutSecs * 1000, 0, 3600 * 1000); 170 } 171 172 #if defined(XP_WIN) 173 // For some reason, the DNSQuery_A API doesn't work on Windows 10. 174 // It returns a success code, but no records. We only allow 175 // native HTTPS records on Win 11 for now. 176 sNativeHTTPSSupported = mozilla::IsWin11OrLater(); 177 #elif defined(MOZ_WIDGET_ANDROID) 178 // android_res_nquery only got added in API level 29 179 sNativeHTTPSSupported = jni::GetAPIVersion() >= 29; 180 #elif defined(XP_LINUX) || defined(XP_MACOSX) 181 sNativeHTTPSSupported = true; 182 #endif 183 LOG(("Native HTTPS records supported=%d", bool(sNativeHTTPSSupported))); 184 185 // The ThreadFunc has its own loop and will live very long and block one 186 // thread from the thread pool's point of view, such that the timeouts are 187 // less important here. The pool is mostly used to provide an easy way to 188 // create and shutdown those threads. 189 // TODO: It seems, the ThreadFunc resembles some quite similar timeout and 190 // wait for events logic as the pool offers, maybe we could simplify this 191 // a bit, see bug 1478732 for a previous attempt. 192 nsCOMPtr<nsIThreadPool> threadPool = new nsThreadPool(); 193 MOZ_ALWAYS_SUCCEEDS(threadPool->SetThreadLimit(MaxResolverThreads())); 194 MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadLimit( 195 std::max(MaxResolverThreads() / 4, (uint32_t)1))); 196 MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadMaximumTimeout(poolTimeoutMs)); 197 MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadGraceTimeout(100)); 198 MOZ_ALWAYS_SUCCEEDS( 199 threadPool->SetThreadStackSize(nsIThreadManager::kThreadPoolStackSize)); 200 MOZ_ALWAYS_SUCCEEDS(threadPool->SetName("DNS Resolver"_ns)); 201 nsCOMPtr<nsIThreadPoolListener> listener = new DnsThreadListener(); 202 threadPool->SetListener(listener); 203 mResolverThreads = ToRefPtr(std::move(threadPool)); 204 205 return NS_OK; 206 } 207 208 void nsHostResolver::ClearPendingQueue( 209 LinkedList<RefPtr<nsHostRecord>>& aPendingQ) { 210 // loop through pending queue, erroring out pending lookups. 211 if (!aPendingQ.isEmpty()) { 212 for (const RefPtr<nsHostRecord>& rec : aPendingQ) { 213 rec->Cancel(); 214 if (rec->IsAddrRecord()) { 215 CompleteLookup(rec, NS_ERROR_ABORT, nullptr, rec->pb, rec->originSuffix, 216 rec->mTRRSkippedReason, nullptr); 217 } else { 218 mozilla::net::TypeRecordResultType empty(Nothing{}); 219 CompleteLookupByType(rec, NS_ERROR_ABORT, empty, rec->mTRRSkippedReason, 220 0, rec->pb); 221 } 222 } 223 } 224 } 225 226 // 227 // FlushCache() is what we call when the network has changed. We must not 228 // trust names that were resolved before this change. They may resolve 229 // differently now. 230 // 231 // This function removes all existing resolved host entries from the hash. 232 // Names that are in the pending queues can be left there. Entries in the 233 // cache that have 'Resolve' set true but not 'OnQueue' are being resolved 234 // right now, so we need to mark them to get re-resolved on completion! 235 236 void nsHostResolver::FlushCache(bool aTrrToo, bool aFlushEvictionQueue) { 237 MutexAutoLock lock(mLock); 238 239 if (aFlushEvictionQueue) { 240 mQueue.FlushEvictionQ(mRecordDB, lock); 241 } 242 243 // Refresh the cache entries that are resolving RIGHT now, remove the rest. 244 for (auto iter = mRecordDB.Iter(); !iter.Done(); iter.Next()) { 245 nsHostRecord* record = iter.UserData(); 246 // Try to remove the record, or mark it for refresh. 247 // By-type records are from TRR. We do not need to flush those entry 248 // when the network has change, because they are not local. 249 if (record->IsAddrRecord()) { 250 RefPtr<AddrHostRecord> addrRec = do_QueryObject(record); 251 MOZ_ASSERT(addrRec); 252 if (addrRec->RemoveOrRefresh(aTrrToo)) { 253 mQueue.MaybeRemoveFromQ(record, lock); 254 LOG(("Removing (%s) Addr record from mRecordDB", record->host.get())); 255 iter.Remove(); 256 } 257 } else if (aTrrToo) { 258 // remove by type records 259 LOG(("Removing (%s) type record from mRecordDB", record->host.get())); 260 iter.Remove(); 261 } 262 } 263 } 264 265 void nsHostResolver::Shutdown() { 266 LOG(("Shutting down host resolver.\n")); 267 268 LinkedList<RefPtr<nsHostRecord>> pendingQHigh, pendingQMed, pendingQLow, 269 evictionQ; 270 271 { 272 MutexAutoLock lock(mLock); 273 274 mShutdown = true; 275 276 if (mNumIdleTasks) { 277 mIdleTaskCV.NotifyAll(); 278 } 279 280 mQueue.ClearAll( 281 [&](nsHostRecord* aRec) { 282 mLock.AssertCurrentThreadOwns(); 283 if (aRec->IsAddrRecord()) { 284 CompleteLookupLocked(aRec, NS_ERROR_ABORT, nullptr, aRec->pb, 285 aRec->originSuffix, aRec->mTRRSkippedReason, 286 nullptr, lock); 287 } else { 288 mozilla::net::TypeRecordResultType empty(Nothing{}); 289 CompleteLookupByTypeLocked(aRec, NS_ERROR_ABORT, empty, 290 aRec->mTRRSkippedReason, 0, aRec->pb, 291 lock); 292 } 293 }, 294 lock); 295 296 for (const auto& data : mRecordDB.Values()) { 297 data->Cancel(); 298 } 299 // empty host database 300 mRecordDB.Clear(); 301 302 mNCS = nullptr; 303 } 304 305 // Shutdown the resolver threads, but with a timeout of 5 seconds (prefable). 306 // If the timeout is exceeded, any stuck threads will be leaked. 307 mResolverThreads->ShutdownWithTimeout( 308 StaticPrefs::network_dns_resolver_shutdown_timeout_ms()); 309 310 { 311 mozilla::DebugOnly<nsresult> rv = GetAddrInfoShutdown(); 312 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to shutdown GetAddrInfo"); 313 } 314 } 315 316 nsresult nsHostResolver::GetHostRecord( 317 const nsACString& host, const nsACString& aTrrServer, uint16_t type, 318 nsIDNSService::DNSFlags flags, uint16_t af, bool pb, 319 const nsCString& originSuffix, nsHostRecord** result) { 320 MutexAutoLock lock(mLock); 321 nsHostKey key(host, aTrrServer, type, flags, af, pb, originSuffix); 322 323 RefPtr<nsHostRecord> rec = 324 mRecordDB.LookupOrInsertWith(key, [&] { return InitRecord(key); }); 325 if (rec->IsAddrRecord()) { 326 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec); 327 if (addrRec->addr) { 328 return NS_ERROR_FAILURE; 329 } 330 } 331 332 if (rec->mResolving) { 333 return NS_ERROR_FAILURE; 334 } 335 336 *result = rec.forget().take(); 337 return NS_OK; 338 } 339 340 nsHostRecord* nsHostResolver::InitRecord(const nsHostKey& key) { 341 if (IS_ADDR_TYPE(key.type)) { 342 return new AddrHostRecord(key); 343 } 344 return new TypeHostRecord(key); 345 } 346 347 namespace { 348 class NetAddrIPv6FirstComparator { 349 public: 350 static bool Equals(const NetAddr& aLhs, const NetAddr& aRhs) { 351 return aLhs.raw.family == aRhs.raw.family; 352 } 353 static bool LessThan(const NetAddr& aLhs, const NetAddr& aRhs) { 354 return aLhs.raw.family > aRhs.raw.family; 355 } 356 }; 357 } // namespace 358 359 already_AddRefed<nsHostRecord> nsHostResolver::InitLoopbackRecord( 360 const nsHostKey& key, nsresult* aRv) { 361 MOZ_ASSERT(aRv); 362 MOZ_ASSERT(IS_ADDR_TYPE(key.type)); 363 364 *aRv = NS_ERROR_FAILURE; 365 RefPtr<nsHostRecord> rec = InitRecord(key); 366 367 nsTArray<NetAddr> addresses; 368 NetAddr addr; 369 if (key.af == PR_AF_INET || key.af == PR_AF_UNSPEC) { 370 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(addr.InitFromString("127.0.0.1"_ns))); 371 addresses.AppendElement(addr); 372 } 373 if (key.af == PR_AF_INET6 || key.af == PR_AF_UNSPEC) { 374 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(addr.InitFromString("::1"_ns))); 375 addresses.AppendElement(addr); 376 } 377 378 if (StaticPrefs::network_dns_preferIPv6() && addresses.Length() > 1 && 379 addresses[0].IsIPAddrV4()) { 380 // Sort IPv6 addresses first. 381 addresses.Sort(NetAddrIPv6FirstComparator()); 382 } 383 384 RefPtr<AddrInfo> ai = 385 new AddrInfo(rec->host, DNSResolverType::Native, 0, std::move(addresses)); 386 387 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec); 388 MutexAutoLock lock(addrRec->addr_info_lock); 389 addrRec->addr_info = ai; 390 addrRec->SetExpiration(TimeStamp::NowLoRes(), 391 StaticPrefs::network_dnsCacheExpiration(), 392 StaticPrefs::network_dnsCacheExpirationGracePeriod()); 393 addrRec->negative = false; 394 // Use the oldest possible timestamp, since the contents of this record never 395 // change. 396 addrRec->mLastUpdate = TimeStamp::ProcessCreation(); 397 398 *aRv = NS_OK; 399 return rec.forget(); 400 } 401 402 already_AddRefed<nsHostRecord> nsHostResolver::InitMockHTTPSRecord( 403 const nsHostKey& key) { 404 MOZ_ASSERT(IS_OTHER_TYPE(key.type)); 405 if (key.type != nsIDNSService::RESOLVE_TYPE_HTTPSSVC) { 406 return nullptr; 407 } 408 409 RefPtr<nsHostRecord> rec = InitRecord(key); 410 LOG(("InitMockHTTPSRecord host=%s\n", rec->host.get())); 411 412 TypeRecordResultType result = AsVariant(mozilla::Nothing()); 413 uint32_t ttl = UINT32_MAX; 414 nsresult rv = 415 CreateAndResolveMockHTTPSRecord(rec->host, rec->flags, result, ttl); 416 if (NS_FAILED(rv)) { 417 return nullptr; 418 } 419 420 RefPtr<TypeHostRecord> typeRec = do_QueryObject(rec); 421 MutexAutoLock lock(typeRec->mResultsLock); 422 typeRec->mResults = result; 423 typeRec->negative = false; 424 return rec.forget(); 425 } 426 427 // static 428 bool nsHostResolver::IsNativeHTTPSEnabled() { 429 if (!StaticPrefs::network_dns_native_https_query()) { 430 return false; 431 } 432 #ifdef XP_WIN 433 if (StaticPrefs::network_dns_native_https_query_win10()) { 434 // If this pref is true, we allow resolving HTTPS records. 435 // It might not work, or we might use the HTTPS override records. 436 return true; 437 } 438 #endif 439 return sNativeHTTPSSupported; 440 } 441 442 nsresult nsHostResolver::ResolveHost(const nsACString& aHost, 443 const nsACString& aTrrServer, 444 int32_t aPort, uint16_t type, 445 const OriginAttributes& aOriginAttributes, 446 nsIDNSService::DNSFlags flags, uint16_t af, 447 nsResolveHostCallback* aCallback) { 448 nsAutoCString host(aHost); 449 NS_ENSURE_TRUE(!host.IsEmpty(), NS_ERROR_UNEXPECTED); 450 451 nsAutoCString originSuffix; 452 aOriginAttributes.CreateSuffix(originSuffix); 453 LOG(("Resolving host [%s]<%s>%s%s type %d. [this=%p]\n", host.get(), 454 originSuffix.get(), 455 flags & nsIDNSService::RESOLVE_BYPASS_CACHE ? " - bypassing cache" : "", 456 flags & nsIDNSService::RESOLVE_REFRESH_CACHE ? " - refresh cache" : "", 457 type, this)); 458 459 // When this pref is set, we always set the flag, to make sure consumers 460 // that forget to set the flag don't end up being a cache miss. 461 if (StaticPrefs::network_dns_always_ai_canonname()) { 462 flags |= nsIDNSService::RESOLVE_CANONICAL_NAME; 463 } 464 465 // ensure that we are working with a valid hostname before proceeding. see 466 // bug 304904 for details. 467 if (!net_IsValidDNSHost(host)) { 468 return NS_ERROR_UNKNOWN_HOST; 469 } 470 471 // If TRR is disabled we can return immediately if the native API is disabled 472 if (!IsNativeHTTPSEnabled() && IS_OTHER_TYPE(type) && 473 Mode() == nsIDNSService::MODE_TRROFF) { 474 return NS_ERROR_UNKNOWN_HOST; 475 } 476 477 // Used to try to parse to an IP address literal. 478 NetAddr tempAddr; 479 if (IS_OTHER_TYPE(type) && (NS_SUCCEEDED(tempAddr.InitFromString(host)))) { 480 // For by-type queries the host cannot be IP literal. 481 return NS_ERROR_UNKNOWN_HOST; 482 } 483 484 RefPtr<nsResolveHostCallback> callback(aCallback); 485 // if result is set inside the lock, then we need to issue the 486 // callback before returning. 487 RefPtr<nsHostRecord> result; 488 nsresult status = NS_OK, rv = NS_OK; 489 { 490 MutexAutoLock lock(mLock); 491 492 if (mShutdown) { 493 return NS_ERROR_NOT_INITIALIZED; 494 } 495 496 // check to see if there is already an entry for this |host| 497 // in the hash table. if so, then check to see if we can't 498 // just reuse the lookup result. otherwise, if there are 499 // any pending callbacks, then add to pending callbacks queue, 500 // and return. otherwise, add ourselves as first pending 501 // callback, and proceed to do the lookup. 502 503 Maybe<nsCString> originHost; 504 if (StaticPrefs::network_dns_port_prefixed_qname_https_rr() && 505 type == nsIDNSService::RESOLVE_TYPE_HTTPSSVC && aPort != -1 && 506 aPort != 443) { 507 originHost = Some(host); 508 host = nsPrintfCString("_%d._https.%s", aPort, host.get()); 509 LOG((" Using port prefixed host name [%s]", host.get())); 510 } 511 512 bool excludedFromTRR = false; 513 if (TRRService::Get() && TRRService::Get()->IsExcludedFromTRR(host)) { 514 flags |= nsIDNSService::RESOLVE_DISABLE_TRR; 515 flags |= nsIDNSService::RESOLVE_DISABLE_NATIVE_HTTPS_QUERY; 516 excludedFromTRR = true; 517 518 if (!aTrrServer.IsEmpty()) { 519 return NS_ERROR_UNKNOWN_HOST; 520 } 521 } 522 523 nsHostKey key(host, aTrrServer, type, flags, af, 524 (aOriginAttributes.IsPrivateBrowsing()), originSuffix); 525 526 // Check if we have a localhost domain, if so hardcode to loopback 527 if (IS_ADDR_TYPE(type) && IsLoopbackHostname(host)) { 528 nsresult rv; 529 RefPtr<nsHostRecord> result = InitLoopbackRecord(key, &rv); 530 if (NS_WARN_IF(NS_FAILED(rv))) { 531 return rv; 532 } 533 MOZ_ASSERT(result); 534 aCallback->OnResolveHostComplete(this, result, NS_OK); 535 return NS_OK; 536 } 537 538 if (flags & nsIDNSService::RESOLVE_CREATE_MOCK_HTTPS_RR) { 539 RefPtr<nsHostRecord> result = InitMockHTTPSRecord(key); 540 aCallback->OnResolveHostComplete(this, result, 541 result ? NS_OK : NS_ERROR_UNKNOWN_HOST); 542 return NS_OK; 543 } 544 545 RefPtr<nsHostRecord> rec = 546 mRecordDB.LookupOrInsertWith(key, [&] { return InitRecord(key); }); 547 548 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec); 549 MOZ_ASSERT(rec, "Record should not be null"); 550 MOZ_ASSERT((IS_ADDR_TYPE(type) && rec->IsAddrRecord() && addrRec) || 551 (IS_OTHER_TYPE(type) && !rec->IsAddrRecord())); 552 553 if (IS_OTHER_TYPE(type) && originHost) { 554 RefPtr<TypeHostRecord> typeRec = do_QueryObject(rec); 555 MutexAutoLock lock(typeRec->mResultsLock); 556 typeRec->mOriginHost = std::move(originHost); 557 } 558 559 if (excludedFromTRR) { 560 rec->RecordReason(TRRSkippedReason::TRR_EXCLUDED); 561 } 562 563 if (!(flags & nsIDNSService::RESOLVE_BYPASS_CACHE) && 564 rec->HasUsableResult(TimeStamp::NowLoRes(), flags)) { 565 result = FromCache(rec, host, type, status, lock); 566 } else if (addrRec && addrRec->addr) { 567 // if the host name is an IP address literal and has been 568 // parsed, go ahead and use it. 569 LOG((" Using cached address for IP Literal [%s].\n", host.get())); 570 result = FromCachedIPLiteral(rec); 571 } else if (addrRec && NS_SUCCEEDED(tempAddr.InitFromString(host))) { 572 // try parsing the host name as an IP address literal to short 573 // circuit full host resolution. (this is necessary on some 574 // platforms like Win9x. see bug 219376 for more details.) 575 LOG((" Host is IP Literal [%s].\n", host.get())); 576 result = FromIPLiteral(addrRec, tempAddr); 577 } else if (mQueue.PendingCount() >= MAX_NON_PRIORITY_REQUESTS && 578 !IsHighPriority(flags) && !rec->mResolving) { 579 LOG( 580 (" Lookup queue full: dropping %s priority request for " 581 "host [%s].\n", 582 IsMediumPriority(flags) ? "medium" : "low", host.get())); 583 if (IS_ADDR_TYPE(type)) { 584 glean::dns::lookup_method.AccumulateSingleSample(METHOD_OVERFLOW); 585 } 586 // This is a lower priority request and we are swamped, so refuse it. 587 rv = NS_ERROR_DNS_LOOKUP_QUEUE_FULL; 588 589 // Check if the offline flag is set. 590 } else if (flags & nsIDNSService::RESOLVE_OFFLINE) { 591 LOG((" Offline request for host [%s]; ignoring.\n", host.get())); 592 rv = NS_ERROR_OFFLINE; 593 594 // We do not have a valid result till here. 595 // A/AAAA request can check for an alternative entry like AF_UNSPEC. 596 // Otherwise we need to start a new query. 597 } else if (!rec->mResolving) { 598 result = 599 FromUnspecEntry(rec, host, aTrrServer, originSuffix, type, flags, af, 600 aOriginAttributes.IsPrivateBrowsing(), status, lock); 601 // If this is a by-type request or if no valid record was found 602 // in the cache or this is an AF_UNSPEC request, then start a 603 // new lookup. 604 if (!result) { 605 LOG((" No usable record in cache for host [%s] type %d.", host.get(), 606 type)); 607 608 if (flags & nsIDNSService::RESOLVE_REFRESH_CACHE) { 609 rec->Invalidate(); 610 } 611 612 // Add callback to the list of pending callbacks. 613 rec->mCallbacks.insertBack(callback); 614 rec->flags = flags; 615 rv = NameLookup(rec, lock); 616 if (IS_ADDR_TYPE(type)) { 617 glean::dns::lookup_method.AccumulateSingleSample( 618 METHOD_NETWORK_FIRST); 619 } 620 if (NS_FAILED(rv) && callback->isInList()) { 621 callback->remove(); 622 } else { 623 LOG( 624 (" DNS lookup for host [%s] blocking " 625 "pending 'getaddrinfo' or trr query: " 626 "callback [%p]", 627 host.get(), callback.get())); 628 } 629 } 630 } else { 631 LOG( 632 (" Host [%s] is being resolved. Appending callback " 633 "[%p].", 634 host.get(), callback.get())); 635 636 rec->mCallbacks.insertBack(callback); 637 638 if (rec && rec->onQueue()) { 639 glean::dns::lookup_method.AccumulateSingleSample(METHOD_NETWORK_SHARED); 640 641 // Consider the case where we are on a pending queue of 642 // lower priority than the request is being made at. 643 // In that case we should upgrade to the higher queue. 644 645 if (IsHighPriority(flags) && !IsHighPriority(rec->flags)) { 646 // Move from (low|med) to high. 647 mQueue.MoveToAnotherPendingQ(rec, flags, lock); 648 rec->flags = flags; 649 ConditionallyCreateThread(rec); 650 } else if (IsMediumPriority(flags) && IsLowPriority(rec->flags)) { 651 // Move from low to med. 652 mQueue.MoveToAnotherPendingQ(rec, flags, lock); 653 rec->flags = flags; 654 mIdleTaskCV.Notify(); 655 } 656 } 657 } 658 659 if (result && callback->isInList()) { 660 callback->remove(); 661 } 662 } // lock 663 664 if (result) { 665 callback->OnResolveHostComplete(this, result, status); 666 } 667 668 return rv; 669 } 670 671 already_AddRefed<nsHostRecord> nsHostResolver::FromCache( 672 nsHostRecord* aRec, const nsACString& aHost, uint16_t aType, 673 nsresult& aStatus, const MutexAutoLock& aLock) { 674 LOG((" Using cached record for host [%s].\n", 675 nsPromiseFlatCString(aHost).get())); 676 677 // put reference to host record on stack... 678 RefPtr<nsHostRecord> result = aRec; 679 680 // For cached entries that are in the grace period or negative, use the cache 681 // but start a new lookup in the background. 682 // 683 // Also records telemetry for type of cache hit (HIT/NEGATIVE_HIT/RENEWAL). 684 ConditionallyRefreshRecord(aRec, aHost, aLock); 685 686 if (aRec->negative) { 687 LOG((" Negative cache entry for host [%s].\n", 688 nsPromiseFlatCString(aHost).get())); 689 aStatus = NS_ERROR_UNKNOWN_HOST; 690 } else if (StaticPrefs::network_dns_mru_to_tail()) { 691 mQueue.MoveToEvictionQueueTail(aRec, aLock); 692 } 693 694 return result.forget(); 695 } 696 697 already_AddRefed<nsHostRecord> nsHostResolver::FromCachedIPLiteral( 698 nsHostRecord* aRec) { 699 glean::dns::lookup_method.AccumulateSingleSample(METHOD_LITERAL); 700 RefPtr<nsHostRecord> result = aRec; 701 return result.forget(); 702 } 703 704 already_AddRefed<nsHostRecord> nsHostResolver::FromIPLiteral( 705 AddrHostRecord* aAddrRec, const NetAddr& aAddr) { 706 // ok, just copy the result into the host record, and be 707 // done with it! ;-) 708 aAddrRec->addr = MakeUnique<NetAddr>(aAddr); 709 glean::dns::lookup_method.AccumulateSingleSample(METHOD_LITERAL); 710 // put reference to host record on stack... 711 RefPtr<nsHostRecord> result = aAddrRec; 712 return result.forget(); 713 } 714 715 already_AddRefed<nsHostRecord> nsHostResolver::FromUnspecEntry( 716 nsHostRecord* aRec, const nsACString& aHost, const nsACString& aTrrServer, 717 const nsACString& aOriginSuffix, uint16_t aType, 718 nsIDNSService::DNSFlags aFlags, uint16_t af, bool aPb, nsresult& aStatus, 719 const MutexAutoLock& aLock) { 720 RefPtr<nsHostRecord> result = nullptr; 721 // If this is an IPV4 or IPV6 specific request, check if there is 722 // an AF_UNSPEC entry we can use. Otherwise, hit the resolver... 723 RefPtr<AddrHostRecord> addrRec = do_QueryObject(aRec); 724 if (addrRec && !(aFlags & nsIDNSService::RESOLVE_BYPASS_CACHE) && 725 ((af == PR_AF_INET) || (af == PR_AF_INET6))) { 726 // Check for an AF_UNSPEC entry. 727 728 const nsHostKey unspecKey(aHost, aTrrServer, 729 nsIDNSService::RESOLVE_TYPE_DEFAULT, aFlags, 730 PR_AF_UNSPEC, aPb, aOriginSuffix); 731 RefPtr<nsHostRecord> unspecRec = mRecordDB.Get(unspecKey); 732 733 TimeStamp now = TimeStamp::NowLoRes(); 734 if (unspecRec && unspecRec->HasUsableResult(now, aFlags)) { 735 MOZ_ASSERT(unspecRec->IsAddrRecord()); 736 737 RefPtr<AddrHostRecord> addrUnspecRec = do_QueryObject(unspecRec); 738 MOZ_ASSERT(addrUnspecRec); 739 MOZ_ASSERT(addrUnspecRec->addr_info || addrUnspecRec->negative, 740 "Entry should be resolved or negative."); 741 742 LOG((" Trying AF_UNSPEC entry for host [%s] af: %s.\n", 743 PromiseFlatCString(aHost).get(), 744 (af == PR_AF_INET) ? "AF_INET" : "AF_INET6")); 745 746 // We need to lock in case any other thread is reading 747 // addr_info. 748 MutexAutoLock lock(addrRec->addr_info_lock); 749 750 addrRec->addr_info = nullptr; 751 addrRec->addr_info_gencnt++; 752 if (unspecRec->negative) { 753 aRec->negative = unspecRec->negative; 754 aRec->CopyExpirationTimesAndFlagsFrom(unspecRec); 755 } else if (addrUnspecRec->addr_info) { 756 MutexAutoLock lock(addrUnspecRec->addr_info_lock); 757 if (addrUnspecRec->addr_info) { 758 // Search for any valid address in the AF_UNSPEC entry 759 // in the cache (not blocklisted and from the right 760 // family). 761 nsTArray<NetAddr> addresses; 762 for (const auto& addr : addrUnspecRec->addr_info->Addresses()) { 763 if ((af == addr.inet.family) && 764 !addrUnspecRec->Blocklisted(&addr)) { 765 addresses.AppendElement(addr); 766 } 767 } 768 if (!addresses.IsEmpty()) { 769 addrRec->addr_info = new AddrInfo( 770 addrUnspecRec->addr_info->Hostname(), 771 addrUnspecRec->addr_info->CanonicalHostname(), 772 addrUnspecRec->addr_info->ResolverType(), 773 addrUnspecRec->addr_info->TRRType(), std::move(addresses)); 774 addrRec->addr_info_gencnt++; 775 aRec->CopyExpirationTimesAndFlagsFrom(unspecRec); 776 } 777 } 778 } 779 // Now check if we have a new record. 780 if (aRec->HasUsableResult(now, aFlags)) { 781 result = aRec; 782 if (aRec->negative) { 783 aStatus = NS_ERROR_UNKNOWN_HOST; 784 } 785 ConditionallyRefreshRecord(aRec, aHost, lock); 786 } else if (af == PR_AF_INET6) { 787 // For AF_INET6, a new lookup means another AF_UNSPEC 788 // lookup. We have already iterated through the 789 // AF_UNSPEC addresses, so we mark this record as 790 // negative. 791 LOG( 792 (" No AF_INET6 in AF_UNSPEC entry: " 793 "host [%s] unknown host.", 794 nsPromiseFlatCString(aHost).get())); 795 result = aRec; 796 aRec->negative = true; 797 aStatus = NS_ERROR_UNKNOWN_HOST; 798 // this record has just been marked as negative so we record the 799 // telemetry for it. 800 glean::dns::lookup_method.AccumulateSingleSample(METHOD_NEGATIVE_HIT); 801 } 802 } 803 } 804 805 return result.forget(); 806 } 807 808 void nsHostResolver::DetachCallback( 809 const nsACString& host, const nsACString& aTrrServer, uint16_t aType, 810 const OriginAttributes& aOriginAttributes, nsIDNSService::DNSFlags flags, 811 uint16_t af, nsResolveHostCallback* aCallback, nsresult status) { 812 RefPtr<nsHostRecord> rec; 813 RefPtr<nsResolveHostCallback> callback(aCallback); 814 815 { 816 MutexAutoLock lock(mLock); 817 818 nsAutoCString originSuffix; 819 aOriginAttributes.CreateSuffix(originSuffix); 820 821 nsHostKey key(host, aTrrServer, aType, flags, af, 822 (aOriginAttributes.IsPrivateBrowsing()), originSuffix); 823 RefPtr<nsHostRecord> entry = mRecordDB.Get(key); 824 if (entry) { 825 // walk list looking for |callback|... we cannot assume 826 // that it will be there! 827 828 for (nsResolveHostCallback* c : entry->mCallbacks) { 829 if (c == callback) { 830 rec = entry; 831 c->remove(); 832 break; 833 } 834 } 835 } 836 } 837 838 // complete callback with the given status code; this would only be done if 839 // the record was in the process of being resolved. 840 if (rec) { 841 callback->OnResolveHostComplete(this, rec, status); 842 } 843 } 844 845 nsresult nsHostResolver::ConditionallyCreateThread(nsHostRecord* rec) { 846 if (mNumIdleTasks) { 847 // wake up idle tasks to process this lookup 848 mIdleTaskCV.Notify(); 849 } else if ((mActiveTaskCount < MaxResolverThreadsAnyPriority()) || 850 (IsHighPriority(rec->flags) && 851 mActiveTaskCount < MaxResolverThreads())) { 852 nsCOMPtr<nsIRunnable> event = mozilla::NewRunnableMethod( 853 "nsHostResolver::ThreadFunc", this, &nsHostResolver::ThreadFunc); 854 mActiveTaskCount++; 855 nsresult rv = 856 mResolverThreads->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL); 857 if (NS_FAILED(rv)) { 858 mActiveTaskCount--; 859 } 860 } else { 861 LOG((" Unable to find a thread for looking up host [%s].\n", 862 rec->host.get())); 863 } 864 return NS_OK; 865 } 866 867 nsresult nsHostResolver::TrrLookup_unlocked(nsHostRecord* rec, TRR* pushedTRR) { 868 MutexAutoLock lock(mLock); 869 return TrrLookup(rec, lock, pushedTRR); 870 } 871 872 void nsHostResolver::MaybeRenewHostRecord(nsHostRecord* aRec) { 873 MutexAutoLock lock(mLock); 874 MaybeRenewHostRecordLocked(aRec, lock); 875 } 876 877 void nsHostResolver::MaybeRenewHostRecordLocked(nsHostRecord* aRec, 878 const MutexAutoLock& aLock) { 879 mQueue.MaybeRenewHostRecord(aRec, aLock); 880 } 881 882 bool nsHostResolver::TRRServiceEnabledForRecord(nsHostRecord* aRec) { 883 MOZ_ASSERT(aRec, "Record must not be empty"); 884 MOZ_ASSERT(aRec->mEffectiveTRRMode != nsIRequest::TRR_DEFAULT_MODE, 885 "effective TRR mode must be computed before this call"); 886 if (!TRRService::Get()) { 887 aRec->RecordReason(TRRSkippedReason::TRR_NO_GSERVICE); 888 return false; 889 } 890 891 // We always try custom resolvers. 892 if (!aRec->mTrrServer.IsEmpty()) { 893 return true; 894 } 895 896 nsIRequest::TRRMode reqMode = aRec->mEffectiveTRRMode; 897 if (TRRService::Get()->Enabled(reqMode)) { 898 return true; 899 } 900 901 if (gIOService->InSleepMode()) { 902 aRec->RecordReason(TRRSkippedReason::TRR_SYSTEM_SLEEP_MODE); 903 return false; 904 } 905 if (NS_IsOffline()) { 906 // If we are in the NOT_CONFIRMED state _because_ we lack connectivity, 907 // then we should report that the browser is offline instead. 908 aRec->RecordReason(TRRSkippedReason::TRR_BROWSER_IS_OFFLINE); 909 return false; 910 } 911 912 auto hasConnectivity = [this]() -> bool { 913 mLock.AssertCurrentThreadOwns(); 914 if (!mNCS) { 915 return true; 916 } 917 nsINetworkConnectivityService::ConnectivityState ipv4 = mNCS->GetIPv4(); 918 nsINetworkConnectivityService::ConnectivityState ipv6 = mNCS->GetIPv6(); 919 920 if (ipv4 == nsINetworkConnectivityService::OK || 921 ipv6 == nsINetworkConnectivityService::OK) { 922 return true; 923 } 924 925 if (ipv4 == nsINetworkConnectivityService::UNKNOWN || 926 ipv6 == nsINetworkConnectivityService::UNKNOWN) { 927 // One of the checks hasn't completed yet. Optimistically assume we'll 928 // have network connectivity. 929 return true; 930 } 931 932 return false; 933 }; 934 935 if (!hasConnectivity()) { 936 aRec->RecordReason(TRRSkippedReason::TRR_NO_CONNECTIVITY); 937 return false; 938 } 939 940 bool isConfirmed = TRRService::Get()->IsConfirmed(); 941 if (!isConfirmed) { 942 aRec->RecordReason(TRRSkippedReason::TRR_NOT_CONFIRMED); 943 } 944 945 return isConfirmed; 946 } 947 948 // returns error if no TRR resolve is issued 949 // it is impt this is not called while a native lookup is going on 950 nsresult nsHostResolver::TrrLookup(nsHostRecord* aRec, 951 const MutexAutoLock& aLock, TRR* pushedTRR) { 952 if (Mode() == nsIDNSService::MODE_TRROFF || 953 StaticPrefs::network_dns_disabled()) { 954 return NS_ERROR_UNKNOWN_HOST; 955 } 956 LOG(("TrrLookup host:%s af:%" PRId16, aRec->host.get(), aRec->af)); 957 958 RefPtr<nsHostRecord> rec(aRec); 959 mLock.AssertCurrentThreadOwns(); 960 961 RefPtr<AddrHostRecord> addrRec; 962 RefPtr<TypeHostRecord> typeRec; 963 964 if (rec->IsAddrRecord()) { 965 addrRec = do_QueryObject(rec); 966 MOZ_ASSERT(addrRec); 967 } else { 968 typeRec = do_QueryObject(rec); 969 MOZ_ASSERT(typeRec); 970 } 971 972 MOZ_ASSERT(!rec->mResolving); 973 974 if (!TRRServiceEnabledForRecord(aRec)) { 975 return NS_ERROR_UNKNOWN_HOST; 976 } 977 978 MaybeRenewHostRecordLocked(rec, aLock); 979 980 RefPtr<TRRQuery> query = new TRRQuery(this, rec); 981 nsresult rv = query->DispatchLookup(pushedTRR); 982 if (NS_FAILED(rv)) { 983 rec->RecordReason(TRRSkippedReason::TRR_DID_NOT_MAKE_QUERY); 984 return rv; 985 } 986 987 { 988 auto lock = rec->mTRRQuery.Lock(); 989 MOZ_ASSERT(!lock.ref(), "TRR already in progress"); 990 lock.ref() = query; 991 } 992 993 rec->mResolving++; 994 rec->mTrrAttempts++; 995 rec->StoreNative(false); 996 return NS_OK; 997 } 998 999 nsresult nsHostResolver::NativeLookup(nsHostRecord* aRec, 1000 const MutexAutoLock& aLock) { 1001 if (StaticPrefs::network_dns_disabled()) { 1002 return NS_ERROR_UNKNOWN_HOST; 1003 } 1004 LOG(("NativeLookup host:%s af:%" PRId16, aRec->host.get(), aRec->af)); 1005 1006 // If this is not a A/AAAA request, make sure native HTTPS is enabled. 1007 MOZ_ASSERT(aRec->IsAddrRecord() || IsNativeHTTPSEnabled()); 1008 mLock.AssertCurrentThreadOwns(); 1009 1010 RefPtr<nsHostRecord> rec(aRec); 1011 1012 rec->mNativeStart = TimeStamp::Now(); 1013 1014 // Add rec to one of the pending queues, possibly removing it from mEvictionQ. 1015 MaybeRenewHostRecordLocked(aRec, aLock); 1016 1017 mQueue.InsertRecord(rec, rec->flags, aLock); 1018 1019 rec->StoreNative(true); 1020 rec->StoreNativeUsed(true); 1021 rec->mResolving++; 1022 1023 nsresult rv = ConditionallyCreateThread(rec); 1024 1025 LOG((" DNS thread counters: total=%d any-live=%d idle=%d pending=%d\n", 1026 static_cast<uint32_t>(mActiveTaskCount), 1027 static_cast<uint32_t>(mActiveAnyThreadCount), 1028 static_cast<uint32_t>(mNumIdleTasks), mQueue.PendingCount())); 1029 1030 return rv; 1031 } 1032 1033 // static 1034 nsIDNSService::ResolverMode nsHostResolver::Mode() { 1035 if (TRRService::Get()) { 1036 return TRRService::Get()->Mode(); 1037 } 1038 1039 // If we don't have a TRR service just return MODE_TRROFF so we don't make 1040 // any TRR requests by mistake. 1041 return nsIDNSService::MODE_TRROFF; 1042 } 1043 1044 nsIRequest::TRRMode nsHostRecord::TRRMode() { 1045 return nsIDNSService::GetTRRModeFromFlags(flags); 1046 } 1047 1048 // static 1049 void nsHostResolver::ComputeEffectiveTRRMode(nsHostRecord* aRec) { 1050 nsIDNSService::ResolverMode resolverMode = nsHostResolver::Mode(); 1051 nsIRequest::TRRMode requestMode = aRec->TRRMode(); 1052 1053 // For domains that are excluded from TRR or when parental control is enabled, 1054 // we fallback to NativeLookup. This happens even in MODE_TRRONLY. By default 1055 // localhost and local are excluded (so we cover *.local hosts) See the 1056 // network.trr.excluded-domains pref. 1057 1058 if (!TRRService::Get()) { 1059 aRec->RecordReason(TRRSkippedReason::TRR_NO_GSERVICE); 1060 aRec->mEffectiveTRRMode = requestMode; 1061 return; 1062 } 1063 1064 if (!aRec->mTrrServer.IsEmpty()) { 1065 aRec->mEffectiveTRRMode = nsIRequest::TRR_ONLY_MODE; 1066 return; 1067 } 1068 1069 if (TRRService::Get()->IsExcludedFromTRR(aRec->host)) { 1070 aRec->RecordReason(TRRSkippedReason::TRR_EXCLUDED); 1071 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE; 1072 return; 1073 } 1074 1075 if (TRRService::Get()->ParentalControlEnabled()) { 1076 aRec->RecordReason(TRRSkippedReason::TRR_PARENTAL_CONTROL); 1077 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE; 1078 return; 1079 } 1080 1081 if (resolverMode == nsIDNSService::MODE_TRROFF) { 1082 aRec->RecordReason(TRRSkippedReason::TRR_OFF_EXPLICIT); 1083 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE; 1084 return; 1085 } 1086 1087 if (requestMode == nsIRequest::TRR_DISABLED_MODE) { 1088 aRec->RecordReason(TRRSkippedReason::TRR_REQ_MODE_DISABLED); 1089 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE; 1090 return; 1091 } 1092 1093 if ((requestMode == nsIRequest::TRR_DEFAULT_MODE && 1094 resolverMode == nsIDNSService::MODE_NATIVEONLY)) { 1095 aRec->RecordReason(TRRSkippedReason::TRR_MODE_NOT_ENABLED); 1096 aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE; 1097 return; 1098 } 1099 1100 if (requestMode == nsIRequest::TRR_DEFAULT_MODE && 1101 resolverMode == nsIDNSService::MODE_TRRFIRST) { 1102 aRec->mEffectiveTRRMode = nsIRequest::TRR_FIRST_MODE; 1103 return; 1104 } 1105 1106 if (requestMode == nsIRequest::TRR_DEFAULT_MODE && 1107 resolverMode == nsIDNSService::MODE_TRRONLY) { 1108 aRec->mEffectiveTRRMode = nsIRequest::TRR_ONLY_MODE; 1109 return; 1110 } 1111 1112 aRec->mEffectiveTRRMode = requestMode; 1113 } 1114 1115 // Kick-off a name resolve operation, using native resolver and/or TRR 1116 nsresult nsHostResolver::NameLookup(nsHostRecord* rec, 1117 const mozilla::MutexAutoLock& aLock) { 1118 LOG(("NameLookup host:%s af:%" PRId16, rec->host.get(), rec->af)); 1119 mLock.AssertCurrentThreadOwns(); 1120 1121 if (rec->flags & nsIDNSService::RESOLVE_IP_HINT) { 1122 LOG(("Skip lookup if nsIDNSService::RESOLVE_IP_HINT is set\n")); 1123 return NS_ERROR_UNKNOWN_HOST; 1124 } 1125 1126 nsresult rv = NS_ERROR_UNKNOWN_HOST; 1127 if (rec->mResolving) { 1128 LOG(("NameLookup %s while already resolving\n", rec->host.get())); 1129 return NS_OK; 1130 } 1131 1132 // Make sure we reset the reason each time we attempt to do a new lookup 1133 // so we don't wrongly report the reason for the previous one. 1134 rec->Reset(); 1135 1136 ComputeEffectiveTRRMode(rec); 1137 1138 if (!rec->mTrrServer.IsEmpty()) { 1139 LOG(("NameLookup: %s use trr:%s", rec->host.get(), rec->mTrrServer.get())); 1140 if (rec->mEffectiveTRRMode != nsIRequest::TRR_ONLY_MODE) { 1141 return NS_ERROR_UNKNOWN_HOST; 1142 } 1143 1144 if (rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR) { 1145 LOG(("TRR with server and DISABLE_TRR flag. Returning error.")); 1146 return NS_ERROR_UNKNOWN_HOST; 1147 } 1148 return TrrLookup(rec, aLock); 1149 } 1150 1151 LOG(("NameLookup: %s effectiveTRRmode: %d flags: %X", rec->host.get(), 1152 static_cast<nsIRequest::TRRMode>(rec->mEffectiveTRRMode), 1153 static_cast<uint32_t>(rec->flags))); 1154 1155 if (rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR) { 1156 rec->RecordReason(TRRSkippedReason::TRR_DISABLED_FLAG); 1157 } 1158 1159 bool serviceNotReady = !TRRServiceEnabledForRecord(rec); 1160 1161 if (rec->mEffectiveTRRMode != nsIRequest::TRR_DISABLED_MODE && 1162 !((rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR)) && 1163 !serviceNotReady) { 1164 rv = TrrLookup(rec, aLock); 1165 } 1166 1167 if (rec->mEffectiveTRRMode == nsIRequest::TRR_DISABLED_MODE || 1168 (rec->mEffectiveTRRMode == nsIRequest::TRR_FIRST_MODE && 1169 (rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR || serviceNotReady || 1170 NS_FAILED(rv)))) { 1171 if (!rec->IsAddrRecord()) { 1172 if (!IsNativeHTTPSEnabled()) { 1173 return NS_ERROR_UNKNOWN_HOST; 1174 } 1175 1176 if (rec->flags & nsIDNSService::RESOLVE_DISABLE_NATIVE_HTTPS_QUERY) { 1177 return NS_ERROR_UNKNOWN_HOST; 1178 } 1179 } 1180 1181 #ifdef DEBUG 1182 // If we use this branch then the mTRRUsed flag should not be set 1183 // Even if we did call TrrLookup above, the fact that it failed sync-ly 1184 // means that we didn't actually succeed in opening the channel. 1185 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec); 1186 MOZ_ASSERT_IF(addrRec, addrRec->mResolverType == DNSResolverType::Native); 1187 #endif 1188 1189 rv = NativeLookup(rec, aLock); 1190 } 1191 1192 return rv; 1193 } 1194 1195 nsresult nsHostResolver::ConditionallyRefreshRecord( 1196 nsHostRecord* rec, const nsACString& host, const MutexAutoLock& aLock) { 1197 if ((rec->CheckExpiration(TimeStamp::NowLoRes()) == nsHostRecord::EXP_GRACE || 1198 rec->negative) && 1199 !rec->mResolving && rec->RefreshForNegativeResponse()) { 1200 LOG((" Using %s cache entry for host [%s] but starting async renewal.", 1201 rec->negative ? "negative" : "positive", host.BeginReading())); 1202 NameLookup(rec, aLock); 1203 1204 if (rec->IsAddrRecord()) { 1205 if (!rec->negative) { 1206 glean::dns::lookup_method.AccumulateSingleSample(METHOD_RENEWAL); 1207 } else { 1208 glean::dns::lookup_method.AccumulateSingleSample(METHOD_NEGATIVE_HIT); 1209 } 1210 } 1211 } else if (rec->IsAddrRecord()) { 1212 // it could be that the record is negative, but we haven't entered the above 1213 // if condition due to the second expression being false. In that case we 1214 // need to record the telemetry for the negative record here. 1215 if (!rec->negative) { 1216 glean::dns::lookup_method.AccumulateSingleSample(METHOD_HIT); 1217 } else { 1218 glean::dns::lookup_method.AccumulateSingleSample(METHOD_NEGATIVE_HIT); 1219 } 1220 } 1221 1222 return NS_OK; 1223 } 1224 1225 bool nsHostResolver::GetHostToLookup(nsHostRecord** result) { 1226 bool timedOut = false; 1227 TimeDuration timeout; 1228 TimeStamp epoch, now; 1229 1230 MutexAutoLock lock(mLock); 1231 1232 timeout = (mNumIdleTasks >= MaxResolverThreadsAnyPriority()) 1233 ? mShortIdleTimeout 1234 : mLongIdleTimeout; 1235 epoch = TimeStamp::Now(); 1236 1237 while (!mShutdown) { 1238 // remove next record from Q; hand over owning reference. Check high, then 1239 // med, then low 1240 1241 #define SET_GET_TTL(var, val) \ 1242 (var)->StoreGetTtl(StaticPrefs::network_dns_get_ttl() && (val)) 1243 1244 RefPtr<nsHostRecord> rec = mQueue.Dequeue(true, lock); 1245 if (rec) { 1246 SET_GET_TTL(rec, false); 1247 rec.forget(result); 1248 return true; 1249 } 1250 1251 if (mActiveAnyThreadCount < MaxResolverThreadsAnyPriority()) { 1252 rec = mQueue.Dequeue(false, lock); 1253 if (rec) { 1254 MOZ_ASSERT(IsMediumPriority(rec->flags) || IsLowPriority(rec->flags)); 1255 mActiveAnyThreadCount++; 1256 rec->StoreUsingAnyThread(true); 1257 SET_GET_TTL(rec, true); 1258 rec.forget(result); 1259 return true; 1260 } 1261 } 1262 1263 // Determining timeout is racy, so allow one cycle through checking the 1264 // queues before exiting. 1265 if (timedOut) { 1266 break; 1267 } 1268 1269 // wait for one or more of the following to occur: 1270 // (1) the pending queue has a host record to process 1271 // (2) the shutdown flag has been set 1272 // (3) the thread has been idle for too long 1273 1274 mNumIdleTasks++; 1275 mIdleTaskCV.Wait(timeout); 1276 mNumIdleTasks--; 1277 1278 now = TimeStamp::Now(); 1279 1280 if (now - epoch >= timeout) { 1281 timedOut = true; 1282 } else { 1283 // It is possible that CondVar::Wait() was interrupted and returned 1284 // early, in which case we will loop back and re-enter it. In that 1285 // case we want to do so with the new timeout reduced to reflect 1286 // time already spent waiting. 1287 timeout -= now - epoch; 1288 epoch = now; 1289 } 1290 } 1291 1292 // tell thread to exit... 1293 return false; 1294 } 1295 1296 void nsHostResolver::PrepareRecordExpirationAddrRecord( 1297 AddrHostRecord* rec) const { 1298 // NOTE: rec->addr_info_lock is already held by parent 1299 MOZ_ASSERT(((bool)rec->addr_info) != rec->negative); 1300 mLock.AssertCurrentThreadOwns(); 1301 if (!rec->addr_info) { 1302 rec->SetExpiration(TimeStamp::NowLoRes(), NEGATIVE_RECORD_LIFETIME, 0); 1303 LOG(("Caching host [%s] negative record for %u seconds.\n", rec->host.get(), 1304 NEGATIVE_RECORD_LIFETIME)); 1305 return; 1306 } 1307 1308 unsigned int lifetime = StaticPrefs::network_dnsCacheExpiration(); 1309 unsigned int grace = StaticPrefs::network_dnsCacheExpirationGracePeriod(); 1310 1311 if (rec->addr_info && rec->addr_info->TTL() != AddrInfo::NO_TTL_DATA) { 1312 lifetime = rec->addr_info->TTL(); 1313 } 1314 1315 rec->SetExpiration(TimeStamp::NowLoRes(), lifetime, grace); 1316 LOG(("Caching host [%s] record for %u seconds (grace %d).", rec->host.get(), 1317 lifetime, grace)); 1318 } 1319 1320 static bool different_rrset(AddrInfo* rrset1, AddrInfo* rrset2) { 1321 if (!rrset1 || !rrset2) { 1322 return true; 1323 } 1324 1325 LOG(("different_rrset %s\n", rrset1->Hostname().get())); 1326 1327 if (rrset1->ResolverType() != rrset2->ResolverType()) { 1328 return true; 1329 } 1330 1331 if (rrset1->TRRType() != rrset2->TRRType()) { 1332 return true; 1333 } 1334 1335 if (rrset1->Addresses().Length() != rrset2->Addresses().Length()) { 1336 LOG(("different_rrset true due to length change\n")); 1337 return true; 1338 } 1339 1340 nsTArray<NetAddr> orderedSet1 = rrset1->Addresses().Clone(); 1341 nsTArray<NetAddr> orderedSet2 = rrset2->Addresses().Clone(); 1342 orderedSet1.Sort(); 1343 orderedSet2.Sort(); 1344 1345 bool eq = orderedSet1 == orderedSet2; 1346 if (!eq) { 1347 LOG(("different_rrset true due to content change\n")); 1348 } else { 1349 LOG(("different_rrset false\n")); 1350 } 1351 return !eq; 1352 } 1353 1354 void nsHostResolver::AddToEvictionQ(nsHostRecord* rec, 1355 const MutexAutoLock& aLock) { 1356 mQueue.AddToEvictionQ(rec, StaticPrefs::network_dnsCacheEntries(), mRecordDB, 1357 aLock); 1358 } 1359 1360 // After a first lookup attempt with TRR in mode 2, we may: 1361 // - If network.trr.retry_on_recoverable_errors is false, retry with native. 1362 // - If network.trr.retry_on_recoverable_errors is true: 1363 // - Retry with native if the first attempt failed because we got NXDOMAIN, an 1364 // unreachable address (TRR_DISABLED_FLAG), or we skipped TRR because 1365 // Confirmation failed. 1366 // - Trigger a "RetryTRR" Confirmation which will start a fresh 1367 // connection for TRR, and then retry the lookup with TRR. 1368 // - If the second attempt failed, fallback to native if 1369 // network.trr.strict_native_fallback is false. 1370 // Returns true if we retried with either TRR or Native. 1371 bool nsHostResolver::MaybeRetryTRRLookup( 1372 AddrHostRecord* aAddrRec, nsresult aFirstAttemptStatus, 1373 TRRSkippedReason aFirstAttemptSkipReason, nsresult aChannelStatus, 1374 const MutexAutoLock& aLock) { 1375 if (NS_FAILED(aFirstAttemptStatus) && 1376 (aChannelStatus == NS_ERROR_PROXY_UNAUTHORIZED || 1377 aChannelStatus == NS_ERROR_PROXY_AUTHENTICATION_FAILED) && 1378 aAddrRec->mEffectiveTRRMode == nsIRequest::TRR_ONLY_MODE) { 1379 LOG(("MaybeRetryTRRLookup retry because of proxy connect failed")); 1380 TRRService::Get()->DontUseTRRThread(); 1381 return DoRetryTRR(aAddrRec, aLock); 1382 } 1383 1384 if (NS_SUCCEEDED(aFirstAttemptStatus) || 1385 aAddrRec->mEffectiveTRRMode != nsIRequest::TRR_FIRST_MODE || 1386 aFirstAttemptStatus == NS_ERROR_DEFINITIVE_UNKNOWN_HOST) { 1387 return false; 1388 } 1389 1390 MOZ_ASSERT(!aAddrRec->mResolving); 1391 if (!StaticPrefs::network_trr_retry_on_recoverable_errors()) { 1392 LOG(("nsHostResolver::MaybeRetryTRRLookup retrying with native")); 1393 1394 // Trigger a confirmation retry, in order to cycle connection if needed 1395 TRRService::Get()->RetryTRRConfirm(); 1396 return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock)); 1397 } 1398 1399 if (IsFailedConfirmationOrNoConnectivity(aFirstAttemptSkipReason) || 1400 IsNonRecoverableTRRSkipReason(aFirstAttemptSkipReason) || 1401 IsBlockedTRRRequest(aFirstAttemptSkipReason)) { 1402 LOG( 1403 ("nsHostResolver::MaybeRetryTRRLookup retrying with native in strict " 1404 "mode, skip reason was %d", 1405 static_cast<uint32_t>(aFirstAttemptSkipReason))); 1406 return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock)); 1407 } 1408 1409 if (aAddrRec->mTrrAttempts > 1) { 1410 if (!StaticPrefs::network_trr_strict_native_fallback()) { 1411 LOG( 1412 ("nsHostResolver::MaybeRetryTRRLookup retry failed. Using " 1413 "native.")); 1414 return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock)); 1415 } 1416 1417 if (aFirstAttemptSkipReason == TRRSkippedReason::TRR_TIMEOUT && 1418 StaticPrefs::network_trr_strict_native_fallback_allow_timeouts()) { 1419 LOG( 1420 ("nsHostResolver::MaybeRetryTRRLookup retry timed out. Using " 1421 "native.")); 1422 return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock)); 1423 } 1424 LOG(("nsHostResolver::MaybeRetryTRRLookup mTrrAttempts>1, not retrying.")); 1425 return false; 1426 } 1427 1428 LOG( 1429 ("nsHostResolver::MaybeRetryTRRLookup triggering Confirmation and " 1430 "retrying with TRR, skip reason was %d", 1431 static_cast<uint32_t>(aFirstAttemptSkipReason))); 1432 TRRService::Get()->RetryTRRConfirm(); 1433 1434 return DoRetryTRR(aAddrRec, aLock); 1435 } 1436 1437 bool nsHostResolver::DoRetryTRR(AddrHostRecord* aAddrRec, 1438 const mozilla::MutexAutoLock& aLock) { 1439 { 1440 // Clear out the old query 1441 auto trrQuery = aAddrRec->mTRRQuery.Lock(); 1442 trrQuery.ref() = nullptr; 1443 } 1444 1445 if (NS_SUCCEEDED(TrrLookup(aAddrRec, aLock, nullptr /* pushedTRR */))) { 1446 aAddrRec->NotifyRetryingTrr(); 1447 return true; 1448 } 1449 1450 return false; 1451 } 1452 1453 // 1454 // CompleteLookup() checks if the resolving should be redone and if so it 1455 // returns LOOKUP_RESOLVEAGAIN, but only if 'status' is not NS_ERROR_ABORT. 1456 nsHostResolver::LookupStatus nsHostResolver::CompleteLookup( 1457 nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb, 1458 const nsACString& aOriginsuffix, TRRSkippedReason aReason, 1459 mozilla::net::TRR* aTRRRequest) { 1460 MutexAutoLock lock(mLock); 1461 return CompleteLookupLocked(rec, status, aNewRRSet, pb, aOriginsuffix, 1462 aReason, aTRRRequest, lock); 1463 } 1464 1465 nsHostResolver::LookupStatus nsHostResolver::CompleteLookupLocked( 1466 nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb, 1467 const nsACString& aOriginsuffix, TRRSkippedReason aReason, 1468 mozilla::net::TRR* aTRRRequest, const mozilla::MutexAutoLock& aLock) { 1469 MOZ_ASSERT(rec); 1470 MOZ_ASSERT(rec->pb == pb); 1471 MOZ_ASSERT(rec->IsAddrRecord()); 1472 1473 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec); 1474 MOZ_ASSERT(addrRec); 1475 1476 RefPtr<AddrInfo> newRRSet(aNewRRSet); 1477 MOZ_ASSERT(NS_FAILED(status) || newRRSet->Addresses().Length() > 0); 1478 1479 DNSResolverType type = 1480 newRRSet ? newRRSet->ResolverType() : DNSResolverType::Native; 1481 1482 if (NS_FAILED(status)) { 1483 newRRSet = nullptr; 1484 } 1485 1486 if (addrRec->LoadResolveAgain() && (status != NS_ERROR_ABORT) && 1487 type == DNSResolverType::Native) { 1488 LOG(("nsHostResolver record %p resolve again due to flushcache\n", 1489 addrRec.get())); 1490 addrRec->StoreResolveAgain(false); 1491 return LOOKUP_RESOLVEAGAIN; 1492 } 1493 1494 MOZ_ASSERT(addrRec->mResolving); 1495 addrRec->mResolving--; 1496 LOG(( 1497 "nsHostResolver::CompleteLookup %s %p %X resolver=%d stillResolving=%d\n", 1498 addrRec->host.get(), aNewRRSet, (unsigned int)status, (int)type, 1499 int(addrRec->mResolving))); 1500 1501 if (type != DNSResolverType::Native) { 1502 if (NS_FAILED(status) && status != NS_ERROR_UNKNOWN_HOST && 1503 status != NS_ERROR_DEFINITIVE_UNKNOWN_HOST) { 1504 // the errors are not failed resolves, that means 1505 // something else failed, consider this as *TRR not used* 1506 // for actually trying to resolve the host 1507 addrRec->mResolverType = DNSResolverType::Native; 1508 } 1509 1510 if (NS_FAILED(status)) { 1511 if (aReason != TRRSkippedReason::TRR_UNSET) { 1512 addrRec->RecordReason(aReason); 1513 } else { 1514 // Unknown failed reason. 1515 addrRec->RecordReason(TRRSkippedReason::TRR_FAILED); 1516 } 1517 } else { 1518 addrRec->mTRRSuccess = true; 1519 addrRec->RecordReason(TRRSkippedReason::TRR_OK); 1520 } 1521 1522 nsresult channelStatus = aTRRRequest->ChannelStatus(); 1523 if (MaybeRetryTRRLookup(addrRec, status, aReason, channelStatus, aLock)) { 1524 MOZ_ASSERT(addrRec->mResolving); 1525 return LOOKUP_OK; 1526 } 1527 1528 if (!addrRec->mTRRSuccess) { 1529 // no TRR success 1530 newRRSet = nullptr; 1531 } 1532 1533 if (NS_FAILED(status)) { 1534 // This is the error that consumers expect. 1535 status = NS_ERROR_UNKNOWN_HOST; 1536 } 1537 } else { // native resolve completed 1538 if (addrRec->LoadUsingAnyThread()) { 1539 mActiveAnyThreadCount--; 1540 addrRec->StoreUsingAnyThread(false); 1541 } 1542 1543 addrRec->mNativeSuccess = static_cast<bool>(newRRSet); 1544 if (addrRec->mNativeSuccess) { 1545 addrRec->mNativeDuration = TimeStamp::Now() - addrRec->mNativeStart; 1546 } 1547 } 1548 1549 addrRec->OnCompleteLookup(); 1550 1551 // update record fields. We might have a addrRec->addr_info already if a 1552 // previous lookup result expired and we're reresolving it or we get 1553 // a late second TRR response. 1554 if (!mShutdown) { 1555 MutexAutoLock lock(addrRec->addr_info_lock); 1556 RefPtr<AddrInfo> old_addr_info; 1557 bool isDifferentRRSet = different_rrset(addrRec->addr_info, newRRSet); 1558 if (isDifferentRRSet) { 1559 LOG(("nsHostResolver record %p new gencnt\n", addrRec.get())); 1560 old_addr_info = addrRec->addr_info; 1561 addrRec->addr_info = std::move(newRRSet); 1562 addrRec->addr_info_gencnt++; 1563 addrRec->mLastUpdate = TimeStamp::NowLoRes(); 1564 } else { 1565 if (addrRec->addr_info && newRRSet) { 1566 auto builder = addrRec->addr_info->Build(); 1567 builder.SetTTL(newRRSet->TTL()); 1568 // Update trr timings 1569 builder.SetTrrFetchDuration(newRRSet->GetTrrFetchDuration()); 1570 builder.SetTrrFetchDurationNetworkOnly( 1571 newRRSet->GetTrrFetchDurationNetworkOnly()); 1572 1573 addrRec->addr_info = builder.Finish(); 1574 } 1575 old_addr_info = std::move(newRRSet); 1576 } 1577 addrRec->negative = !addrRec->addr_info; 1578 1579 if (addrRec->addr_info && StaticPrefs::network_dns_preferIPv6() && 1580 addrRec->addr_info->Addresses().Length() > 1 && 1581 addrRec->addr_info->Addresses()[0].IsIPAddrV4()) { 1582 // Sort IPv6 addresses first. 1583 auto builder = addrRec->addr_info->Build(); 1584 builder.SortAddresses(NetAddrIPv6FirstComparator()); 1585 addrRec->addr_info = builder.Finish(); 1586 } 1587 1588 PrepareRecordExpirationAddrRecord(addrRec); 1589 } 1590 1591 if (LOG_ENABLED()) { 1592 MutexAutoLock lock(addrRec->addr_info_lock); 1593 if (addrRec->addr_info) { 1594 for (const auto& elem : addrRec->addr_info->Addresses()) { 1595 char buf[128]; 1596 elem.ToStringBuffer(buf, sizeof(buf)); 1597 LOG(("CompleteLookup: %s has %s\n", addrRec->host.get(), buf)); 1598 } 1599 } else { 1600 LOG(("CompleteLookup: %s has NO address\n", addrRec->host.get())); 1601 } 1602 } 1603 1604 // get the list of pending callbacks for this lookup, and notify 1605 // them that the lookup is complete. 1606 mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs = 1607 std::move(rec->mCallbacks); 1608 1609 LOG(("nsHostResolver record %p calling back dns users status:%X\n", 1610 addrRec.get(), int(status))); 1611 1612 for (nsResolveHostCallback* c = cbs.getFirst(); c; 1613 c = c->removeAndGetNext()) { 1614 c->OnResolveHostComplete(this, rec, status); 1615 } 1616 1617 OnResolveComplete(rec, aLock); 1618 1619 #ifdef DNSQUERY_AVAILABLE 1620 // Unless the result is from TRR, resolve again to get TTL 1621 bool hasNativeResult = false; 1622 { 1623 MutexAutoLock lock(addrRec->addr_info_lock); 1624 if (addrRec->addr_info && !addrRec->addr_info->IsTRR()) { 1625 hasNativeResult = true; 1626 } 1627 } 1628 if (hasNativeResult && !mShutdown && !addrRec->LoadGetTtl() && 1629 !rec->mResolving && StaticPrefs::network_dns_get_ttl()) { 1630 LOG(("Issuing second async lookup for TTL for host [%s].", 1631 addrRec->host.get())); 1632 addrRec->flags = 1633 (addrRec->flags & ~nsIDNSService::RESOLVE_PRIORITY_MEDIUM) | 1634 nsIDNSService::RESOLVE_PRIORITY_LOW; 1635 DebugOnly<nsresult> rv = NativeLookup(rec, aLock); 1636 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), 1637 "Could not issue second async lookup for TTL."); 1638 } 1639 #endif 1640 return LOOKUP_OK; 1641 } 1642 1643 nsHostResolver::LookupStatus nsHostResolver::CompleteLookupByType( 1644 nsHostRecord* rec, nsresult status, 1645 mozilla::net::TypeRecordResultType& aResult, TRRSkippedReason aReason, 1646 uint32_t aTtl, bool pb) { 1647 MutexAutoLock lock(mLock); 1648 return CompleteLookupByTypeLocked(rec, status, aResult, aReason, aTtl, pb, 1649 lock); 1650 } 1651 1652 nsHostResolver::LookupStatus nsHostResolver::CompleteLookupByTypeLocked( 1653 nsHostRecord* rec, nsresult status, 1654 mozilla::net::TypeRecordResultType& aResult, TRRSkippedReason aReason, 1655 uint32_t aTtl, bool pb, const mozilla::MutexAutoLock& aLock) { 1656 MOZ_ASSERT(rec); 1657 MOZ_ASSERT(rec->pb == pb); 1658 MOZ_ASSERT(!rec->IsAddrRecord()); 1659 1660 if (rec->LoadNative()) { 1661 // If this was resolved using the native resolver 1662 // we also need to update the global count. 1663 if (rec->LoadUsingAnyThread()) { 1664 mActiveAnyThreadCount--; 1665 rec->StoreUsingAnyThread(false); 1666 } 1667 } 1668 1669 RefPtr<TypeHostRecord> typeRec = do_QueryObject(rec); 1670 MOZ_ASSERT(typeRec); 1671 1672 MOZ_ASSERT(typeRec->mResolving); 1673 typeRec->mResolving--; 1674 1675 if (NS_FAILED(status)) { 1676 if (status != NS_ERROR_UNKNOWN_HOST && 1677 status != NS_ERROR_DEFINITIVE_UNKNOWN_HOST) { 1678 // See nsHostResolver::CompleteLookupLocked. Use the same logic as 1679 // AddrRecord here. 1680 typeRec->mResolverType = DNSResolverType::Native; 1681 } 1682 LOG(("nsHostResolver::CompleteLookupByType record %p [%s] status %x\n", 1683 typeRec.get(), typeRec->host.get(), (unsigned int)status)); 1684 typeRec->SetExpiration( 1685 TimeStamp::NowLoRes(), 1686 StaticPrefs::network_dns_negative_ttl_for_type_record(), 0); 1687 MOZ_ASSERT(aResult.is<TypeRecordEmpty>()); 1688 status = NS_ERROR_UNKNOWN_HOST; 1689 typeRec->negative = true; 1690 if (aReason != TRRSkippedReason::TRR_UNSET) { 1691 typeRec->RecordReason(aReason); 1692 } else { 1693 // Unknown failed reason. 1694 typeRec->RecordReason(TRRSkippedReason::TRR_FAILED); 1695 } 1696 } else { 1697 size_t recordCount = 0; 1698 if (aResult.is<TypeRecordTxt>()) { 1699 recordCount = aResult.as<TypeRecordTxt>().Length(); 1700 } else if (aResult.is<TypeRecordHTTPSSVC>()) { 1701 recordCount = aResult.as<TypeRecordHTTPSSVC>().Length(); 1702 } 1703 LOG( 1704 ("nsHostResolver::CompleteLookupByType record %p [%s], number of " 1705 "records %zu\n", 1706 typeRec.get(), typeRec->host.get(), recordCount)); 1707 MutexAutoLock typeLock(typeRec->mResultsLock); 1708 typeRec->mResults = aResult; 1709 typeRec->SetExpiration( 1710 TimeStamp::NowLoRes(), aTtl, 1711 StaticPrefs::network_dnsCacheExpirationGracePeriod()); 1712 typeRec->negative = false; 1713 typeRec->mTRRSuccess = !rec->LoadNative(); 1714 typeRec->mNativeSuccess = rec->LoadNative(); 1715 MOZ_ASSERT(aReason != TRRSkippedReason::TRR_UNSET); 1716 typeRec->RecordReason(aReason); 1717 } 1718 1719 mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs = 1720 std::move(typeRec->mCallbacks); 1721 1722 LOG( 1723 ("nsHostResolver::CompleteLookupByType record %p calling back dns " 1724 "users\n", 1725 typeRec.get())); 1726 1727 for (nsResolveHostCallback* c = cbs.getFirst(); c; 1728 c = c->removeAndGetNext()) { 1729 c->OnResolveHostComplete(this, rec, status); 1730 } 1731 1732 OnResolveComplete(rec, aLock); 1733 1734 return LOOKUP_OK; 1735 } 1736 1737 void nsHostResolver::OnResolveComplete(nsHostRecord* aRec, 1738 const mozilla::MutexAutoLock& aLock) { 1739 if (!aRec->mResolving && !mShutdown) { 1740 { 1741 auto trrQuery = aRec->mTRRQuery.Lock(); 1742 if (trrQuery.ref()) { 1743 aRec->mTrrDuration = trrQuery.ref()->Duration(); 1744 } 1745 trrQuery.ref() = nullptr; 1746 } 1747 aRec->ResolveComplete(); 1748 1749 AddToEvictionQ(aRec, aLock); 1750 } 1751 } 1752 1753 void nsHostResolver::CancelAsyncRequest( 1754 const nsACString& host, const nsACString& aTrrServer, uint16_t aType, 1755 const OriginAttributes& aOriginAttributes, nsIDNSService::DNSFlags flags, 1756 uint16_t af, nsIDNSListener* aListener, nsresult status) 1757 1758 { 1759 MutexAutoLock lock(mLock); 1760 1761 nsAutoCString originSuffix; 1762 aOriginAttributes.CreateSuffix(originSuffix); 1763 1764 // Lookup the host record associated with host, flags & address family 1765 1766 nsHostKey key(host, aTrrServer, aType, flags, af, 1767 (aOriginAttributes.IsPrivateBrowsing()), originSuffix); 1768 RefPtr<nsHostRecord> rec = mRecordDB.Get(key); 1769 if (!rec) { 1770 return; 1771 } 1772 1773 for (RefPtr<nsResolveHostCallback> c : rec->mCallbacks) { 1774 if (c->EqualsAsyncListener(aListener)) { 1775 c->remove(); 1776 c->OnResolveHostComplete(this, rec.get(), status); 1777 break; 1778 } 1779 } 1780 1781 // If there are no more callbacks, remove the hash table entry 1782 if (rec->mCallbacks.isEmpty()) { 1783 mRecordDB.Remove(*static_cast<nsHostKey*>(rec.get())); 1784 // If record is on a Queue, remove it 1785 mQueue.MaybeRemoveFromQ(rec, lock); 1786 } 1787 } 1788 1789 size_t nsHostResolver::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const { 1790 MutexAutoLock lock(mLock); 1791 1792 size_t n = mallocSizeOf(this); 1793 1794 n += mRecordDB.ShallowSizeOfExcludingThis(mallocSizeOf); 1795 for (const auto& entry : mRecordDB.Values()) { 1796 n += entry->SizeOfIncludingThis(mallocSizeOf); 1797 } 1798 1799 // The following fields aren't measured. 1800 // - mHighQ, mMediumQ, mLowQ, mEvictionQ, because they just point to 1801 // nsHostRecords that also pointed to by entries |mRecordDB|, and 1802 // measured when |mRecordDB| is measured. 1803 1804 return n; 1805 } 1806 1807 void nsHostResolver::ThreadFunc() { 1808 LOG(("DNS lookup thread - starting execution.\n")); 1809 1810 RefPtr<nsHostRecord> rec; 1811 RefPtr<AddrInfo> ai; 1812 1813 do { 1814 if (!rec) { 1815 RefPtr<nsHostRecord> tmpRec; 1816 if (!GetHostToLookup(getter_AddRefs(tmpRec))) { 1817 break; // thread shutdown signal 1818 } 1819 // GetHostToLookup() returns an owning reference 1820 MOZ_ASSERT(tmpRec); 1821 rec.swap(tmpRec); 1822 } 1823 1824 LOG1(("DNS lookup thread - Calling getaddrinfo for host [%s].\n", 1825 rec->host.get())); 1826 1827 TimeStamp startTime = TimeStamp::Now(); 1828 bool getTtl = rec->LoadGetTtl(); 1829 TimeDuration inQueue = startTime - rec->mNativeStart; 1830 glean::dns::native_queuing.AccumulateRawDuration(inQueue); 1831 1832 if (!rec->IsAddrRecord()) { 1833 LOG(("byType on DNS thread")); 1834 TypeRecordResultType result = AsVariant(mozilla::Nothing()); 1835 uint32_t ttl = UINT32_MAX; 1836 nsresult status = ResolveHTTPSRecord(rec->host, rec->flags, result, ttl); 1837 mozilla::glean::networking::dns_native_count 1838 .EnumGet(rec->pb 1839 ? glean::networking::DnsNativeCountLabel::eHttpsPrivate 1840 : glean::networking::DnsNativeCountLabel::eHttpsRegular) 1841 .Add(1); 1842 CompleteLookupByType(rec, status, result, rec->mTRRSkippedReason, ttl, 1843 rec->pb); 1844 rec = nullptr; 1845 continue; 1846 } 1847 1848 nsresult status = 1849 GetAddrInfo(rec->host, rec->af, rec->flags, getter_AddRefs(ai), getTtl); 1850 1851 mozilla::glean::networking::dns_native_count 1852 .EnumGet(rec->pb ? glean::networking::DnsNativeCountLabel::ePrivate 1853 : glean::networking::DnsNativeCountLabel::eRegular) 1854 .Add(1); 1855 1856 if (RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec)) { 1857 // obtain lock to check shutdown and manage inter-module telemetry 1858 MutexAutoLock lock(mLock); 1859 1860 if (!mShutdown) { 1861 TimeDuration elapsed = TimeStamp::Now() - startTime; 1862 if (NS_SUCCEEDED(status)) { 1863 if (!addrRec->addr_info_gencnt) { 1864 // Time for initial lookup. 1865 glean::networking::dns_lookup_time.AccumulateRawDuration(elapsed); 1866 } else if (!getTtl) { 1867 // Time for renewal; categorized by expiration strategy. 1868 glean::networking::dns_renewal_time.AccumulateRawDuration(elapsed); 1869 } else { 1870 // Time to get TTL; categorized by expiration strategy. 1871 glean::networking::dns_renewal_time_for_ttl.AccumulateRawDuration( 1872 elapsed); 1873 } 1874 } else { 1875 glean::networking::dns_failed_lookup_time.AccumulateRawDuration( 1876 elapsed); 1877 } 1878 } 1879 } 1880 1881 LOG1(("DNS lookup thread - lookup completed for host [%s]: %s.\n", 1882 rec->host.get(), ai ? "success" : "failure: unknown host")); 1883 1884 if (LOOKUP_RESOLVEAGAIN == 1885 CompleteLookup(rec, status, ai, rec->pb, rec->originSuffix, 1886 rec->mTRRSkippedReason, nullptr)) { 1887 // leave 'rec' assigned and loop to make a renewed host resolve 1888 LOG(("DNS lookup thread - Re-resolving host [%s].\n", rec->host.get())); 1889 } else { 1890 rec = nullptr; 1891 } 1892 } while (true); 1893 1894 MutexAutoLock lock(mLock); 1895 mActiveTaskCount--; 1896 LOG(("DNS lookup thread - queue empty, task finished.\n")); 1897 } 1898 1899 nsresult nsHostResolver::Create(nsHostResolver** result) { 1900 RefPtr<nsHostResolver> res = new nsHostResolver(); 1901 1902 nsresult rv = res->Init(); 1903 if (NS_FAILED(rv)) { 1904 return rv; 1905 } 1906 1907 res.forget(result); 1908 return NS_OK; 1909 } 1910 1911 void nsHostResolver::GetDNSCacheEntries(nsTArray<DNSCacheEntries>* args) { 1912 MutexAutoLock lock(mLock); 1913 for (const auto& recordEntry : mRecordDB) { 1914 // We don't pay attention to address literals, only resolved domains. 1915 // Also require a host. 1916 nsHostRecord* rec = recordEntry.GetWeak(); 1917 MOZ_ASSERT(rec, "rec should never be null here!"); 1918 1919 if (!rec) { 1920 continue; 1921 } 1922 1923 DNSCacheEntries info; 1924 info.resolveType = rec->type; 1925 info.hostname = rec->host; 1926 info.family = rec->af; 1927 if (rec->mValidEnd.IsNull()) { 1928 continue; 1929 } 1930 info.expiration = 1931 (int64_t)(rec->mValidEnd - TimeStamp::NowLoRes()).ToSeconds(); 1932 if (info.expiration <= 0) { 1933 // We only need valid DNS cache entries 1934 continue; 1935 } 1936 1937 info.originAttributesSuffix = recordEntry.GetKey().originSuffix; 1938 info.flags = nsPrintfCString("%u|0x%x|%u|%d|%s", rec->type, 1939 static_cast<uint32_t>(rec->flags), rec->af, 1940 rec->pb, rec->mTrrServer.get()); 1941 1942 RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec); 1943 if (addrRec && addrRec->addr_info) { 1944 MutexAutoLock lock(addrRec->addr_info_lock); 1945 for (const auto& addr : addrRec->addr_info->Addresses()) { 1946 char buf[kIPv6CStrBufSize]; 1947 if (addr.ToStringBuffer(buf, sizeof(buf))) { 1948 info.hostaddr.AppendElement(buf); 1949 } 1950 } 1951 info.TRR = addrRec->addr_info->IsTRR(); 1952 } 1953 1954 args->AppendElement(std::move(info)); 1955 } 1956 } 1957 1958 #undef LOG 1959 #undef LOG_ENABLED