Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / netwerk / dns / nsDNSService2.cpp
blobbcf7c9772f44e4f0c0009b009e6114ec9450487d
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=8 et tw=80 : */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsDNSService2.h"
8 #include "nsIDNSRecord.h"
9 #include "nsIDNSListener.h"
10 #include "nsIDNSByTypeRecord.h"
11 #include "nsICancelable.h"
12 #include "nsIPrefBranch.h"
13 #include "nsIOService.h"
14 #include "nsIXPConnect.h"
15 #include "nsProxyRelease.h"
16 #include "nsReadableUtils.h"
17 #include "nsString.h"
18 #include "nsCRT.h"
19 #include "nsNetCID.h"
20 #include "nsError.h"
21 #include "nsDNSPrefetch.h"
22 #include "nsThreadUtils.h"
23 #include "nsIProtocolProxyService.h"
24 #include "nsIObliviousHttp.h"
25 #include "prsystem.h"
26 #include "prnetdb.h"
27 #include "prmon.h"
28 #include "prio.h"
29 #include "nsCharSeparatedTokenizer.h"
30 #include "nsNetAddr.h"
31 #include "nsNetUtil.h"
32 #include "nsProxyRelease.h"
33 #include "nsQueryObject.h"
34 #include "nsIObserverService.h"
35 #include "nsINetworkLinkService.h"
36 #include "DNSAdditionalInfo.h"
37 #include "TRRService.h"
39 #include "mozilla/Attributes.h"
40 #include "mozilla/ClearOnShutdown.h"
41 #include "mozilla/net/NeckoCommon.h"
42 #include "mozilla/net/ChildDNSService.h"
43 #include "mozilla/net/DNSListenerProxy.h"
44 #include "mozilla/Services.h"
45 #include "mozilla/StaticPrefs_network.h"
46 #include "mozilla/StaticPtr.h"
47 #include "mozilla/SyncRunnable.h"
48 #include "mozilla/TextUtils.h"
49 #include "mozilla/Utf8.h"
51 using namespace mozilla;
52 using namespace mozilla::net;
54 static const char kPrefDnsCacheEntries[] = "network.dnsCacheEntries";
55 static const char kPrefDnsCacheExpiration[] = "network.dnsCacheExpiration";
56 static const char kPrefDnsCacheGrace[] =
57 "network.dnsCacheExpirationGracePeriod";
58 static const char kPrefIPv4OnlyDomains[] = "network.dns.ipv4OnlyDomains";
59 static const char kPrefDnsLocalDomains[] = "network.dns.localDomains";
60 static const char kPrefDnsForceResolve[] = "network.dns.forceResolve";
61 static const char kPrefDnsNotifyResolution[] = "network.dns.notifyResolution";
62 static const char kPrefDnsMockHTTPSRRDomain[] =
63 "network.dns.mock_HTTPS_RR_domain";
65 //-----------------------------------------------------------------------------
67 class nsDNSRecord : public nsIDNSAddrRecord {
68 public:
69 NS_DECL_THREADSAFE_ISUPPORTS
70 NS_DECL_NSIDNSRECORD
71 NS_DECL_NSIDNSADDRRECORD
73 explicit nsDNSRecord(nsHostRecord* hostRecord) {
74 mHostRecord = do_QueryObject(hostRecord);
77 private:
78 virtual ~nsDNSRecord() = default;
80 RefPtr<AddrHostRecord> mHostRecord;
81 // Since mIter is holding a weak reference to the NetAddr array we must
82 // make sure it is not released. So we also keep a RefPtr to the AddrInfo
83 // which is immutable.
84 RefPtr<AddrInfo> mAddrInfo;
85 nsTArray<NetAddr>::const_iterator mIter;
86 const NetAddr* iter() {
87 if (!mIter.GetArray()) {
88 return nullptr;
90 if (mIter.GetArray()->end() == mIter) {
91 return nullptr;
93 return &*mIter;
96 int mIterGenCnt = -1; // the generation count of
97 // mHostRecord->addr_info when we
98 // start iterating
99 bool mDone = false;
102 NS_IMPL_ISUPPORTS(nsDNSRecord, nsIDNSRecord, nsIDNSAddrRecord)
104 NS_IMETHODIMP
105 nsDNSRecord::GetCanonicalName(nsACString& result) {
106 // this method should only be called if we have a CNAME
107 NS_ENSURE_TRUE(mHostRecord->flags & nsIDNSService::RESOLVE_CANONICAL_NAME,
108 NS_ERROR_NOT_AVAILABLE);
110 MutexAutoLock lock(mHostRecord->addr_info_lock);
112 // if the record is for an IP address literal, then the canonical
113 // host name is the IP address literal.
114 if (!mHostRecord->addr_info) {
115 result = mHostRecord->host;
116 return NS_OK;
119 if (mHostRecord->addr_info->CanonicalHostname().IsEmpty()) {
120 result = mHostRecord->addr_info->Hostname();
121 } else {
122 result = mHostRecord->addr_info->CanonicalHostname();
124 return NS_OK;
127 NS_IMETHODIMP
128 nsDNSRecord::IsTRR(bool* retval) {
129 MutexAutoLock lock(mHostRecord->addr_info_lock);
130 if (mHostRecord->addr_info) {
131 *retval = mHostRecord->addr_info->IsTRR();
132 } else {
133 *retval = false;
135 return NS_OK;
138 NS_IMETHODIMP
139 nsDNSRecord::ResolvedInSocketProcess(bool* retval) {
140 *retval = false;
141 return NS_OK;
144 NS_IMETHODIMP
145 nsDNSRecord::GetTrrFetchDuration(double* aTime) {
146 MutexAutoLock lock(mHostRecord->addr_info_lock);
147 if (mHostRecord->addr_info && mHostRecord->addr_info->IsTRR()) {
148 *aTime = mHostRecord->addr_info->GetTrrFetchDuration();
149 } else {
150 *aTime = 0;
152 return NS_OK;
155 NS_IMETHODIMP
156 nsDNSRecord::GetTrrFetchDurationNetworkOnly(double* aTime) {
157 MutexAutoLock lock(mHostRecord->addr_info_lock);
158 if (mHostRecord->addr_info && mHostRecord->addr_info->IsTRR()) {
159 *aTime = mHostRecord->addr_info->GetTrrFetchDurationNetworkOnly();
160 } else {
161 *aTime = 0;
163 return NS_OK;
166 NS_IMETHODIMP
167 nsDNSRecord::GetNextAddr(uint16_t port, NetAddr* addr) {
168 if (mDone) {
169 return NS_ERROR_NOT_AVAILABLE;
172 mHostRecord->addr_info_lock.Lock();
173 if (mHostRecord->addr_info) {
174 if (mIterGenCnt != mHostRecord->addr_info_gencnt) {
175 // mHostRecord->addr_info has changed, restart the iteration.
176 mIter = nsTArray<NetAddr>::const_iterator();
177 mIterGenCnt = mHostRecord->addr_info_gencnt;
178 // Make sure to hold a RefPtr to the AddrInfo so we can iterate through
179 // the NetAddr array.
180 mAddrInfo = mHostRecord->addr_info;
183 bool startedFresh = !iter();
185 do {
186 if (!iter()) {
187 mIter = mAddrInfo->Addresses().begin();
188 } else {
189 mIter++;
191 } while (iter() && mHostRecord->Blocklisted(iter()));
193 if (!iter() && startedFresh) {
194 // If everything was blocklisted we want to reset the blocklist (and
195 // likely relearn it) and return the first address. That is better
196 // than nothing.
197 mHostRecord->ResetBlocklist();
198 mIter = mAddrInfo->Addresses().begin();
201 if (iter()) {
202 *addr = *mIter;
205 mHostRecord->addr_info_lock.Unlock();
207 if (!iter()) {
208 mDone = true;
209 mIter = nsTArray<NetAddr>::const_iterator();
210 mAddrInfo = nullptr;
211 mIterGenCnt = -1;
212 return NS_ERROR_NOT_AVAILABLE;
214 } else {
215 mHostRecord->addr_info_lock.Unlock();
217 if (!mHostRecord->addr) {
218 // Both mHostRecord->addr_info and mHostRecord->addr are null.
219 // This can happen if mHostRecord->addr_info expired and the
220 // attempt to reresolve it failed.
221 return NS_ERROR_NOT_AVAILABLE;
223 *addr = *mHostRecord->addr;
224 mDone = true;
227 // set given port
228 port = htons(port);
229 if (addr->raw.family == AF_INET) {
230 addr->inet.port = port;
231 } else if (addr->raw.family == AF_INET6) {
232 addr->inet6.port = port;
235 return NS_OK;
238 NS_IMETHODIMP
239 nsDNSRecord::GetAddresses(nsTArray<NetAddr>& aAddressArray) {
240 if (mDone) {
241 return NS_ERROR_NOT_AVAILABLE;
244 mHostRecord->addr_info_lock.Lock();
245 if (mHostRecord->addr_info) {
246 for (const auto& address : mHostRecord->addr_info->Addresses()) {
247 if (mHostRecord->Blocklisted(&address)) {
248 continue;
250 NetAddr* addr = aAddressArray.AppendElement(address);
251 if (addr->raw.family == AF_INET) {
252 addr->inet.port = 0;
253 } else if (addr->raw.family == AF_INET6) {
254 addr->inet6.port = 0;
257 mHostRecord->addr_info_lock.Unlock();
258 } else {
259 mHostRecord->addr_info_lock.Unlock();
261 if (!mHostRecord->addr) {
262 return NS_ERROR_NOT_AVAILABLE;
264 NetAddr* addr = aAddressArray.AppendElement(NetAddr());
265 *addr = *mHostRecord->addr;
266 if (addr->raw.family == AF_INET) {
267 addr->inet.port = 0;
268 } else if (addr->raw.family == AF_INET6) {
269 addr->inet6.port = 0;
272 return NS_OK;
275 NS_IMETHODIMP
276 nsDNSRecord::GetScriptableNextAddr(uint16_t port, nsINetAddr** result) {
277 NetAddr addr;
278 nsresult rv = GetNextAddr(port, &addr);
279 if (NS_FAILED(rv)) {
280 return rv;
283 RefPtr<nsNetAddr> netaddr = new nsNetAddr(&addr);
284 netaddr.forget(result);
286 return NS_OK;
289 NS_IMETHODIMP
290 nsDNSRecord::GetNextAddrAsString(nsACString& result) {
291 NetAddr addr;
292 nsresult rv = GetNextAddr(0, &addr);
293 if (NS_FAILED(rv)) {
294 return rv;
297 char buf[kIPv6CStrBufSize];
298 if (addr.ToStringBuffer(buf, sizeof(buf))) {
299 result.Assign(buf);
300 return NS_OK;
302 NS_ERROR("NetAddrToString failed unexpectedly");
303 return NS_ERROR_FAILURE; // conversion failed for some reason
306 NS_IMETHODIMP
307 nsDNSRecord::HasMore(bool* result) {
308 if (mDone) {
309 *result = false;
310 return NS_OK;
313 nsTArray<NetAddr>::const_iterator iterCopy = mIter;
314 int iterGenCntCopy = mIterGenCnt;
316 NetAddr addr;
317 *result = NS_SUCCEEDED(GetNextAddr(0, &addr));
319 mIter = iterCopy;
320 mIterGenCnt = iterGenCntCopy;
321 mDone = false;
323 return NS_OK;
326 NS_IMETHODIMP
327 nsDNSRecord::Rewind() {
328 mIter = nsTArray<NetAddr>::const_iterator();
329 mIterGenCnt = -1;
330 mDone = false;
331 return NS_OK;
334 NS_IMETHODIMP
335 nsDNSRecord::ReportUnusable(uint16_t aPort) {
336 // right now we don't use the port in the blocklist
338 MutexAutoLock lock(mHostRecord->addr_info_lock);
340 // Check that we are using a real addr_info (as opposed to a single
341 // constant address), and that the generation count is valid. Otherwise,
342 // ignore the report.
344 if (mHostRecord->addr_info && mIterGenCnt == mHostRecord->addr_info_gencnt &&
345 iter()) {
346 mHostRecord->ReportUnusable(iter());
349 return NS_OK;
352 NS_IMETHODIMP
353 nsDNSRecord::GetEffectiveTRRMode(nsIRequest::TRRMode* aMode) {
354 *aMode = mHostRecord->EffectiveTRRMode();
355 return NS_OK;
358 NS_IMETHODIMP nsDNSRecord::GetTrrSkipReason(
359 nsITRRSkipReason::value* aTrrSkipReason) {
360 *aTrrSkipReason = mHostRecord->TrrSkipReason();
361 return NS_OK;
364 NS_IMETHODIMP
365 nsDNSRecord::GetTtl(uint32_t* aTtl) { return mHostRecord->GetTtl(aTtl); }
367 class nsDNSByTypeRecord : public nsIDNSByTypeRecord,
368 public nsIDNSTXTRecord,
369 public nsIDNSHTTPSSVCRecord {
370 public:
371 NS_DECL_THREADSAFE_ISUPPORTS
372 NS_DECL_NSIDNSRECORD
373 NS_DECL_NSIDNSBYTYPERECORD
374 NS_DECL_NSIDNSTXTRECORD
375 NS_DECL_NSIDNSHTTPSSVCRECORD
377 explicit nsDNSByTypeRecord(nsHostRecord* hostRecord) {
378 mHostRecord = do_QueryObject(hostRecord);
381 private:
382 virtual ~nsDNSByTypeRecord() = default;
383 RefPtr<TypeHostRecord> mHostRecord;
386 NS_IMPL_ISUPPORTS(nsDNSByTypeRecord, nsIDNSRecord, nsIDNSByTypeRecord,
387 nsIDNSTXTRecord, nsIDNSHTTPSSVCRecord)
389 NS_IMETHODIMP
390 nsDNSByTypeRecord::GetType(uint32_t* aType) {
391 *aType = mHostRecord->GetType();
392 return NS_OK;
395 NS_IMETHODIMP
396 nsDNSByTypeRecord::GetRecords(CopyableTArray<nsCString>& aRecords) {
397 // deep copy
398 return mHostRecord->GetRecords(aRecords);
401 NS_IMETHODIMP
402 nsDNSByTypeRecord::GetRecordsAsOneString(nsACString& aRecords) {
403 // deep copy
404 return mHostRecord->GetRecordsAsOneString(aRecords);
407 NS_IMETHODIMP
408 nsDNSByTypeRecord::GetRecords(nsTArray<RefPtr<nsISVCBRecord>>& aRecords) {
409 return mHostRecord->GetRecords(aRecords);
412 NS_IMETHODIMP
413 nsDNSByTypeRecord::GetServiceModeRecord(bool aNoHttp2, bool aNoHttp3,
414 nsISVCBRecord** aRecord) {
415 return mHostRecord->GetServiceModeRecord(aNoHttp2, aNoHttp3, aRecord);
418 NS_IMETHODIMP
419 nsDNSByTypeRecord::GetServiceModeRecordWithCname(bool aNoHttp2, bool aNoHttp3,
420 const nsACString& aCname,
421 nsISVCBRecord** aRecord) {
422 return mHostRecord->GetServiceModeRecordWithCname(aNoHttp2, aNoHttp3, aCname,
423 aRecord);
426 NS_IMETHODIMP
427 nsDNSByTypeRecord::IsTRR(bool* aResult) { return mHostRecord->IsTRR(aResult); }
429 NS_IMETHODIMP
430 nsDNSByTypeRecord::GetAllRecords(bool aNoHttp2, bool aNoHttp3,
431 const nsACString& aCname,
432 nsTArray<RefPtr<nsISVCBRecord>>& aResult) {
433 return mHostRecord->GetAllRecords(aNoHttp2, aNoHttp3, aCname, aResult);
436 NS_IMETHODIMP
437 nsDNSByTypeRecord::GetAllRecordsWithEchConfig(
438 bool aNoHttp2, bool aNoHttp3, const nsACString& aCname,
439 bool* aAllRecordsHaveEchConfig, bool* aAllRecordsInH3ExcludedList,
440 nsTArray<RefPtr<nsISVCBRecord>>& aResult) {
441 return mHostRecord->GetAllRecordsWithEchConfig(
442 aNoHttp2, aNoHttp3, aCname, aAllRecordsHaveEchConfig,
443 aAllRecordsInH3ExcludedList, aResult);
446 NS_IMETHODIMP
447 nsDNSByTypeRecord::GetHasIPAddresses(bool* aResult) {
448 return mHostRecord->GetHasIPAddresses(aResult);
451 NS_IMETHODIMP
452 nsDNSByTypeRecord::GetAllRecordsExcluded(bool* aResult) {
453 return mHostRecord->GetAllRecordsExcluded(aResult);
456 NS_IMETHODIMP
457 nsDNSByTypeRecord::GetResults(mozilla::net::TypeRecordResultType* aResults) {
458 *aResults = mHostRecord->GetResults();
459 return NS_OK;
462 NS_IMETHODIMP
463 nsDNSByTypeRecord::GetTtl(uint32_t* aTtl) { return mHostRecord->GetTtl(aTtl); }
465 //-----------------------------------------------------------------------------
467 class nsDNSAsyncRequest final : public nsResolveHostCallback,
468 public nsICancelable {
469 public:
470 NS_DECL_THREADSAFE_ISUPPORTS
471 NS_DECL_NSICANCELABLE
473 nsDNSAsyncRequest(nsHostResolver* res, const nsACString& host,
474 const nsACString& trrServer, uint16_t type,
475 const OriginAttributes& attrs, nsIDNSListener* listener,
476 nsIDNSService::DNSFlags flags, uint16_t af)
477 : mResolver(res),
478 mHost(host),
479 mTrrServer(trrServer),
480 mType(type),
481 mOriginAttributes(attrs),
482 mListener(listener),
483 mFlags(flags),
484 mAF(af) {}
486 void OnResolveHostComplete(nsHostResolver*, nsHostRecord*, nsresult) override;
487 // Returns TRUE if the DNS listener arg is the same as the member listener
488 // Used in Cancellations to remove DNS requests associated with a
489 // particular hostname and nsIDNSListener
490 bool EqualsAsyncListener(nsIDNSListener* aListener) override;
492 size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const override;
494 RefPtr<nsHostResolver> mResolver;
495 nsCString mHost; // hostname we're resolving
496 nsCString mTrrServer; // A trr server to be used.
497 uint16_t mType = 0;
498 const OriginAttributes
499 mOriginAttributes; // The originAttributes for this resolving
500 nsCOMPtr<nsIDNSListener> mListener;
501 nsIDNSService::DNSFlags mFlags = nsIDNSService::RESOLVE_DEFAULT_FLAGS;
502 uint16_t mAF = 0;
504 private:
505 virtual ~nsDNSAsyncRequest() = default;
508 NS_IMPL_ISUPPORTS(nsDNSAsyncRequest, nsICancelable)
510 void nsDNSAsyncRequest::OnResolveHostComplete(nsHostResolver* resolver,
511 nsHostRecord* hostRecord,
512 nsresult status) {
513 // need to have an owning ref when we issue the callback to enable
514 // the caller to be able to addref/release multiple times without
515 // destroying the record prematurely.
516 nsCOMPtr<nsIDNSRecord> rec;
517 if (NS_SUCCEEDED(status) ||
518 mFlags & nsIDNSService::RESOLVE_WANT_RECORD_ON_ERROR) {
519 MOZ_ASSERT(hostRecord, "no host record");
520 if (!hostRecord) {
521 mListener->OnLookupComplete(this, nullptr, NS_ERROR_UNKNOWN_HOST);
522 mListener = nullptr;
523 return;
525 if (hostRecord->type != nsDNSService::RESOLVE_TYPE_DEFAULT) {
526 rec = new nsDNSByTypeRecord(hostRecord);
527 } else {
528 rec = new nsDNSRecord(hostRecord);
532 mListener->OnLookupComplete(this, rec, status);
533 mListener = nullptr;
536 bool nsDNSAsyncRequest::EqualsAsyncListener(nsIDNSListener* aListener) {
537 uintptr_t originalListenerAddr = reinterpret_cast<uintptr_t>(mListener.get());
538 RefPtr<DNSListenerProxy> wrapper = do_QueryObject(mListener);
539 if (wrapper) {
540 originalListenerAddr = wrapper->GetOriginalListenerAddress();
543 uintptr_t listenerAddr = reinterpret_cast<uintptr_t>(aListener);
544 return (listenerAddr == originalListenerAddr);
547 size_t nsDNSAsyncRequest::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const {
548 size_t n = mallocSizeOf(this);
550 // The following fields aren't measured.
551 // - mHost, because it's a non-owning pointer
552 // - mResolver, because it's a non-owning pointer
553 // - mListener, because it's a non-owning pointer
555 return n;
558 NS_IMETHODIMP
559 nsDNSAsyncRequest::Cancel(nsresult reason) {
560 NS_ENSURE_ARG(NS_FAILED(reason));
561 MOZ_DIAGNOSTIC_ASSERT(mResolver, "mResolver should not be null");
562 mResolver->DetachCallback(mHost, mTrrServer, mType, mOriginAttributes, mFlags,
563 mAF, this, reason);
564 return NS_OK;
567 //-----------------------------------------------------------------------------
569 class nsDNSSyncRequest : public nsResolveHostCallback {
570 NS_DECL_THREADSAFE_ISUPPORTS
571 public:
572 explicit nsDNSSyncRequest(PRMonitor* mon) : mMonitor(mon) {}
574 void OnResolveHostComplete(nsHostResolver*, nsHostRecord*, nsresult) override;
575 bool EqualsAsyncListener(nsIDNSListener* aListener) override;
576 size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const override;
578 bool mDone = false;
579 nsresult mStatus = NS_OK;
580 RefPtr<nsHostRecord> mHostRecord;
582 private:
583 virtual ~nsDNSSyncRequest() = default;
585 PRMonitor* mMonitor = nullptr;
588 NS_IMPL_ISUPPORTS0(nsDNSSyncRequest)
590 void nsDNSSyncRequest::OnResolveHostComplete(nsHostResolver* resolver,
591 nsHostRecord* hostRecord,
592 nsresult status) {
593 // store results, and wake up nsDNSService::Resolve to process results.
594 PR_EnterMonitor(mMonitor);
595 mDone = true;
596 mStatus = status;
597 mHostRecord = hostRecord;
598 PR_Notify(mMonitor);
599 PR_ExitMonitor(mMonitor);
602 bool nsDNSSyncRequest::EqualsAsyncListener(nsIDNSListener* aListener) {
603 // Sync request: no listener to compare
604 return false;
607 size_t nsDNSSyncRequest::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const {
608 size_t n = mallocSizeOf(this);
610 // The following fields aren't measured.
611 // - mHostRecord, because it's a non-owning pointer
613 // Measurement of the following members may be added later if DMD finds it
614 // is worthwhile:
615 // - mMonitor
617 return n;
620 class NotifyDNSResolution : public Runnable {
621 public:
622 explicit NotifyDNSResolution(const nsACString& aHostname)
623 : mozilla::Runnable("NotifyDNSResolution"), mHostname(aHostname) {}
625 NS_IMETHOD Run() override {
626 MOZ_ASSERT(NS_IsMainThread());
627 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
628 if (obs) {
629 obs->NotifyObservers(nullptr, "dns-resolution-request",
630 NS_ConvertUTF8toUTF16(mHostname).get());
632 return NS_OK;
635 private:
636 nsCString mHostname;
639 //-----------------------------------------------------------------------------
641 static StaticRefPtr<DNSServiceWrapper> gDNSServiceWrapper;
643 NS_IMPL_ISUPPORTS(DNSServiceWrapper, nsIDNSService, nsPIDNSService)
645 // static
646 already_AddRefed<nsIDNSService> DNSServiceWrapper::GetSingleton() {
647 if (!gDNSServiceWrapper) {
648 gDNSServiceWrapper = new DNSServiceWrapper();
649 gDNSServiceWrapper->mDNSServiceInUse = ChildDNSService::GetSingleton();
650 if (gDNSServiceWrapper->mDNSServiceInUse) {
651 ClearOnShutdown(&gDNSServiceWrapper);
652 nsDNSPrefetch::Initialize(gDNSServiceWrapper);
653 } else {
654 gDNSServiceWrapper = nullptr;
658 return do_AddRef(gDNSServiceWrapper);
661 // static
662 void DNSServiceWrapper::SwitchToBackupDNSService() {
663 if (!gDNSServiceWrapper) {
664 return;
667 gDNSServiceWrapper->mBackupDNSService = nsDNSService::GetSingleton();
669 MutexAutoLock lock(gDNSServiceWrapper->mLock);
670 gDNSServiceWrapper->mBackupDNSService.swap(
671 gDNSServiceWrapper->mDNSServiceInUse);
674 nsIDNSService* DNSServiceWrapper::DNSService() {
675 MOZ_ASSERT(XRE_IsParentProcess());
677 MutexAutoLock lock(mLock);
678 return mDNSServiceInUse.get();
681 nsPIDNSService* DNSServiceWrapper::PIDNSService() {
682 MOZ_ASSERT(XRE_IsParentProcess());
684 nsCOMPtr<nsPIDNSService> service = do_QueryInterface(DNSService());
685 return service.get();
688 //-----------------------------------------------------------------------------
689 NS_IMPL_ISUPPORTS_INHERITED(nsDNSService, DNSServiceBase, nsIDNSService,
690 nsPIDNSService, nsIMemoryReporter)
692 /******************************************************************************
693 * nsDNSService impl:
694 * singleton instance ctor/dtor methods
695 ******************************************************************************/
696 static StaticRefPtr<nsDNSService> gDNSService;
697 static Atomic<bool> gInited(false);
699 already_AddRefed<nsIDNSService> GetOrInitDNSService() {
700 if (gInited) {
701 return nsDNSService::GetXPCOMSingleton();
704 nsCOMPtr<nsIDNSService> dns = nullptr;
705 auto initTask = [&dns]() { dns = do_GetService(NS_DNSSERVICE_CID); };
706 if (!NS_IsMainThread()) {
707 // Forward to the main thread synchronously.
708 RefPtr<nsIThread> mainThread = do_GetMainThread();
709 if (!mainThread) {
710 return nullptr;
713 SyncRunnable::DispatchToThread(
714 mainThread, NS_NewRunnableFunction("GetOrInitDNSService", initTask));
715 } else {
716 initTask();
719 return dns.forget();
722 already_AddRefed<nsIDNSService> nsDNSService::GetXPCOMSingleton() {
723 auto getDNSHelper = []() -> already_AddRefed<nsIDNSService> {
724 if (nsIOService::UseSocketProcess()) {
725 if (XRE_IsSocketProcess()) {
726 return GetSingleton();
729 if (XRE_IsParentProcess()) {
730 return DNSServiceWrapper::GetSingleton();
733 if (XRE_IsContentProcess()) {
734 return ChildDNSService::GetSingleton();
737 return nullptr;
740 if (XRE_IsParentProcess()) {
741 return GetSingleton();
744 if (XRE_IsContentProcess() || XRE_IsSocketProcess()) {
745 return ChildDNSService::GetSingleton();
748 return nullptr;
751 if (gInited) {
752 return getDNSHelper();
755 nsCOMPtr<nsIDNSService> dns = getDNSHelper();
756 if (dns) {
757 gInited = true;
759 return dns.forget();
762 already_AddRefed<nsDNSService> nsDNSService::GetSingleton() {
763 MOZ_ASSERT_IF(nsIOService::UseSocketProcess(), XRE_IsSocketProcess());
764 MOZ_ASSERT_IF(!nsIOService::UseSocketProcess(), XRE_IsParentProcess());
766 if (!gDNSService) {
767 if (!NS_IsMainThread()) {
768 return nullptr;
770 gDNSService = new nsDNSService();
771 if (NS_SUCCEEDED(gDNSService->Init())) {
772 ClearOnShutdown(&gDNSService);
773 } else {
774 gDNSService = nullptr;
778 return do_AddRef(gDNSService);
781 void nsDNSService::ReadPrefs(const char* name) {
782 DNSServiceBase::ReadPrefs(name);
784 bool tmpbool;
786 // DNSservice prefs
787 if (!name || !strcmp(name, kPrefDnsNotifyResolution)) {
788 if (NS_SUCCEEDED(
789 Preferences::GetBool(kPrefDnsNotifyResolution, &tmpbool))) {
790 mNotifyResolution = tmpbool;
793 if (!name || !strcmp(name, kPrefIPv4OnlyDomains)) {
794 Preferences::GetCString(kPrefIPv4OnlyDomains, mIPv4OnlyDomains);
796 if (!name || !strcmp(name, kPrefDnsLocalDomains)) {
797 nsCString localDomains;
798 Preferences::GetCString(kPrefDnsLocalDomains, localDomains);
799 MutexAutoLock lock(mLock);
800 mLocalDomains.Clear();
801 for (const auto& token :
802 nsCCharSeparatedTokenizerTemplate<NS_IsAsciiWhitespace,
803 nsTokenizerFlags::SeparatorOptional>(
804 localDomains, ',')
805 .ToRange()) {
806 mLocalDomains.Insert(token);
809 if (!name || !strcmp(name, kPrefDnsForceResolve)) {
810 Preferences::GetCString(kPrefDnsForceResolve, mForceResolve);
811 mForceResolveOn = !mForceResolve.IsEmpty();
813 if (!name || !strcmp(name, kPrefDnsMockHTTPSRRDomain)) {
814 nsCString mockHTTPSRRDomain;
815 Preferences::GetCString(kPrefDnsMockHTTPSRRDomain, mockHTTPSRRDomain);
816 if (mockHTTPSRRDomain.IsEmpty()) {
817 mHasMockHTTPSRRDomainSet = false;
818 } else {
819 mHasMockHTTPSRRDomainSet = true;
820 MutexAutoLock lock(mLock);
821 mMockHTTPSRRDomain = mockHTTPSRRDomain;
826 NS_IMETHODIMP
827 nsDNSService::Init() {
828 MOZ_ASSERT(!mResolver);
829 MOZ_ASSERT(NS_IsMainThread());
831 ReadPrefs(nullptr);
833 nsCOMPtr<nsIObserverService> observerService =
834 mozilla::services::GetObserverService();
835 if (observerService) {
836 observerService->AddObserver(this, "last-pb-context-exited", false);
837 observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
838 observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
841 RefPtr<nsHostResolver> res;
842 nsresult rv = nsHostResolver::Create(getter_AddRefs(res));
843 if (NS_SUCCEEDED(rv)) {
844 // now, set all of our member variables while holding the lock
845 MutexAutoLock lock(mLock);
846 mResolver = res;
849 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
850 if (prefs) {
851 // register as prefs observer
852 prefs->AddObserver(kPrefDnsCacheEntries, this, false);
853 prefs->AddObserver(kPrefDnsCacheExpiration, this, false);
854 prefs->AddObserver(kPrefDnsCacheGrace, this, false);
855 prefs->AddObserver(kPrefIPv4OnlyDomains, this, false);
856 prefs->AddObserver(kPrefDnsLocalDomains, this, false);
857 prefs->AddObserver(kPrefDnsForceResolve, this, false);
858 prefs->AddObserver(kPrefDnsNotifyResolution, this, false);
859 prefs->AddObserver(kPrefDnsMockHTTPSRRDomain, this, false);
860 AddPrefObserver(prefs);
863 nsDNSPrefetch::Initialize(this);
865 RegisterWeakMemoryReporter(this);
867 nsCOMPtr<nsIObliviousHttpService> ohttpService(
868 do_GetService("@mozilla.org/network/oblivious-http-service;1"));
870 mTrrService = new TRRService();
871 if (NS_FAILED(mTrrService->Init(mResolver->IsNativeHTTPSEnabled()))) {
872 mTrrService = nullptr;
875 return NS_OK;
878 NS_IMETHODIMP
879 nsDNSService::Shutdown() {
880 UnregisterWeakMemoryReporter(this);
882 RefPtr<nsHostResolver> res;
884 MutexAutoLock lock(mLock);
885 res = std::move(mResolver);
887 if (res) {
888 // Shutdown outside lock.
889 res->Shutdown();
892 nsCOMPtr<nsIObserverService> observerService =
893 mozilla::services::GetObserverService();
894 if (observerService) {
895 observerService->RemoveObserver(this, NS_NETWORK_LINK_TOPIC);
896 observerService->RemoveObserver(this, "last-pb-context-exited");
897 observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
900 return NS_OK;
903 bool nsDNSService::GetOffline() const {
904 bool offline = false;
905 nsCOMPtr<nsIIOService> io = do_GetService(NS_IOSERVICE_CONTRACTID);
906 if (io) {
907 io->GetOffline(&offline);
909 return offline;
912 NS_IMETHODIMP
913 nsDNSService::GetPrefetchEnabled(bool* outVal) {
914 MutexAutoLock lock(mLock);
915 *outVal = !mDisablePrefetch;
916 return NS_OK;
919 NS_IMETHODIMP
920 nsDNSService::SetPrefetchEnabled(bool inVal) {
921 MutexAutoLock lock(mLock);
922 mDisablePrefetch = !inVal;
923 return NS_OK;
926 already_AddRefed<nsHostResolver> nsDNSService::GetResolverLocked() {
927 MutexAutoLock lock(mLock);
928 return do_AddRef(mResolver);
931 nsresult nsDNSService::PreprocessHostname(bool aLocalDomain,
932 const nsACString& aInput,
933 nsACString& aACE) {
934 // Enforce RFC 7686
935 if (StaticPrefs::network_dns_blockDotOnion() &&
936 StringEndsWith(aInput, ".onion"_ns)) {
937 return NS_ERROR_UNKNOWN_HOST;
940 if (aLocalDomain) {
941 aACE.AssignLiteral("localhost");
942 return NS_OK;
945 if (mTrrService && mTrrService->MaybeBootstrap(aInput, aACE)) {
946 return NS_OK;
949 if (mForceResolveOn) {
950 MutexAutoLock lock(mLock);
951 if (!aInput.LowerCaseEqualsASCII("localhost") &&
952 !aInput.LowerCaseEqualsASCII("127.0.0.1")) {
953 aACE.Assign(mForceResolve);
954 return NS_OK;
958 if (!NS_SUCCEEDED(NS_DomainToASCIIAllowAnyGlyphfulASCII(aInput, aACE))) {
959 return NS_ERROR_FAILURE;
961 return NS_OK;
964 bool nsDNSService::IsLocalDomain(const nsACString& aHostname) const {
965 bool localDomain = mLocalDomains.Contains(aHostname);
966 if (StringEndsWith(aHostname, "."_ns)) {
967 localDomain = localDomain || mLocalDomains.Contains(Substring(
968 aHostname, 0, aHostname.Length() - 1));
970 return localDomain;
973 nsresult nsDNSService::AsyncResolveInternal(
974 const nsACString& aHostname, uint16_t type, nsIDNSService::DNSFlags flags,
975 nsIDNSAdditionalInfo* aInfo, nsIDNSListener* aListener,
976 nsIEventTarget* target_, const OriginAttributes& aOriginAttributes,
977 nsICancelable** result) {
978 // grab reference to global host resolver and IDN service. beware
979 // simultaneous shutdown!!
980 RefPtr<nsHostResolver> res;
981 nsCOMPtr<nsIEventTarget> target = target_;
982 nsCOMPtr<nsIDNSListener> listener = aListener;
983 bool localDomain = false;
985 MutexAutoLock lock(mLock);
987 if (mDisablePrefetch && (flags & RESOLVE_SPECULATE)) {
988 return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
991 res = mResolver;
993 localDomain = IsLocalDomain(aHostname);
996 if (mNotifyResolution) {
997 NS_DispatchToMainThread(new NotifyDNSResolution(aHostname));
1000 if (!res) {
1001 return NS_ERROR_OFFLINE;
1004 if ((type != RESOLVE_TYPE_DEFAULT) && (type != RESOLVE_TYPE_TXT) &&
1005 (type != RESOLVE_TYPE_HTTPSSVC)) {
1006 return NS_ERROR_INVALID_ARG;
1009 if (DNSForbiddenByActiveProxy(aHostname, flags)) {
1010 // nsHostResolver returns NS_ERROR_UNKNOWN_HOST for lots of reasons.
1011 // We use a different error code to differentiate this failure and to make
1012 // it clear(er) where this error comes from.
1013 return NS_ERROR_UNKNOWN_PROXY_HOST;
1016 nsCString hostname;
1017 nsresult rv = PreprocessHostname(localDomain, aHostname, hostname);
1018 if (NS_FAILED(rv)) {
1019 return rv;
1022 if (GetOffline() && (!StaticPrefs::network_dns_offline_localhost() ||
1023 !hostname.LowerCaseEqualsASCII("localhost"))) {
1024 flags |= RESOLVE_OFFLINE;
1027 // make sure JS callers get notification on the main thread
1028 nsCOMPtr<nsIXPConnectWrappedJS> wrappedListener = do_QueryInterface(listener);
1029 if (wrappedListener && !target) {
1030 target = GetMainThreadSerialEventTarget();
1033 if (target) {
1034 listener = new DNSListenerProxy(listener, target);
1037 uint16_t af =
1038 (type != RESOLVE_TYPE_DEFAULT) ? 0 : GetAFForLookup(hostname, flags);
1040 MOZ_ASSERT(listener);
1041 RefPtr<nsDNSAsyncRequest> req =
1042 new nsDNSAsyncRequest(res, hostname, DNSAdditionalInfo::URL(aInfo), type,
1043 aOriginAttributes, listener, flags, af);
1044 if (!req) {
1045 return NS_ERROR_OUT_OF_MEMORY;
1048 if (type == RESOLVE_TYPE_HTTPSSVC && mHasMockHTTPSRRDomainSet) {
1049 MutexAutoLock lock(mLock);
1050 if (req->mHost == mMockHTTPSRRDomain) {
1051 flags |= nsIDNSService::RESOLVE_CREATE_MOCK_HTTPS_RR;
1055 rv = res->ResolveHost(req->mHost, DNSAdditionalInfo::URL(aInfo),
1056 DNSAdditionalInfo::Port(aInfo), type,
1057 req->mOriginAttributes, flags, af, req);
1058 req.forget(result);
1059 return rv;
1062 nsresult nsDNSService::CancelAsyncResolveInternal(
1063 const nsACString& aHostname, uint16_t aType, nsIDNSService::DNSFlags aFlags,
1064 nsIDNSAdditionalInfo* aInfo, nsIDNSListener* aListener, nsresult aReason,
1065 const OriginAttributes& aOriginAttributes) {
1066 // grab reference to global host resolver and IDN service. beware
1067 // simultaneous shutdown!!
1068 RefPtr<nsHostResolver> res;
1069 bool localDomain = false;
1071 MutexAutoLock lock(mLock);
1073 if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE)) {
1074 return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
1077 res = mResolver;
1078 localDomain = IsLocalDomain(aHostname);
1080 if (!res) {
1081 return NS_ERROR_OFFLINE;
1084 nsCString hostname;
1085 nsresult rv = PreprocessHostname(localDomain, aHostname, hostname);
1086 if (NS_FAILED(rv)) {
1087 return rv;
1090 uint16_t af =
1091 (aType != RESOLVE_TYPE_DEFAULT) ? 0 : GetAFForLookup(hostname, aFlags);
1093 res->CancelAsyncRequest(hostname, DNSAdditionalInfo::URL(aInfo), aType,
1094 aOriginAttributes, aFlags, af, aListener, aReason);
1095 return NS_OK;
1098 NS_IMETHODIMP
1099 nsDNSService::AsyncResolve(const nsACString& aHostname,
1100 nsIDNSService::ResolveType aType,
1101 nsIDNSService::DNSFlags flags,
1102 nsIDNSAdditionalInfo* aInfo,
1103 nsIDNSListener* listener, nsIEventTarget* target_,
1104 JS::Handle<JS::Value> aOriginAttributes,
1105 JSContext* aCx, uint8_t aArgc,
1106 nsICancelable** result) {
1107 OriginAttributes attrs;
1109 if (aArgc == 1) {
1110 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
1111 return NS_ERROR_INVALID_ARG;
1115 return AsyncResolveInternal(aHostname, aType, flags, aInfo, listener, target_,
1116 attrs, result);
1119 NS_IMETHODIMP
1120 nsDNSService::AsyncResolveNative(
1121 const nsACString& aHostname, nsIDNSService::ResolveType aType,
1122 nsIDNSService::DNSFlags flags, nsIDNSAdditionalInfo* aInfo,
1123 nsIDNSListener* aListener, nsIEventTarget* target_,
1124 const OriginAttributes& aOriginAttributes, nsICancelable** result) {
1125 return AsyncResolveInternal(aHostname, aType, flags, aInfo, aListener,
1126 target_, aOriginAttributes, result);
1129 NS_IMETHODIMP
1130 nsDNSService::NewAdditionalInfo(const nsACString& aTrrURL, int32_t aPort,
1131 nsIDNSAdditionalInfo** aInfo) {
1132 RefPtr<DNSAdditionalInfo> res = new DNSAdditionalInfo(aTrrURL, aPort);
1133 res.forget(aInfo);
1134 return NS_OK;
1137 NS_IMETHODIMP
1138 nsDNSService::CancelAsyncResolve(const nsACString& aHostname,
1139 nsIDNSService::ResolveType aType,
1140 nsIDNSService::DNSFlags aFlags,
1141 nsIDNSAdditionalInfo* aInfo,
1142 nsIDNSListener* aListener, nsresult aReason,
1143 JS::Handle<JS::Value> aOriginAttributes,
1144 JSContext* aCx, uint8_t aArgc) {
1145 OriginAttributes attrs;
1147 if (aArgc == 1) {
1148 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
1149 return NS_ERROR_INVALID_ARG;
1153 return CancelAsyncResolveInternal(aHostname, aType, aFlags, aInfo, aListener,
1154 aReason, attrs);
1157 NS_IMETHODIMP
1158 nsDNSService::CancelAsyncResolveNative(
1159 const nsACString& aHostname, nsIDNSService::ResolveType aType,
1160 nsIDNSService::DNSFlags aFlags, nsIDNSAdditionalInfo* aInfo,
1161 nsIDNSListener* aListener, nsresult aReason,
1162 const OriginAttributes& aOriginAttributes) {
1163 return CancelAsyncResolveInternal(aHostname, aType, aFlags, aInfo, aListener,
1164 aReason, aOriginAttributes);
1167 NS_IMETHODIMP
1168 nsDNSService::Resolve(const nsACString& aHostname,
1169 nsIDNSService::DNSFlags flags,
1170 JS::Handle<JS::Value> aOriginAttributes, JSContext* aCx,
1171 uint8_t aArgc, nsIDNSRecord** result) {
1172 OriginAttributes attrs;
1174 if (aArgc == 1) {
1175 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
1176 return NS_ERROR_INVALID_ARG;
1180 return ResolveNative(aHostname, flags, attrs, result);
1183 NS_IMETHODIMP
1184 nsDNSService::ResolveNative(const nsACString& aHostname,
1185 nsIDNSService::DNSFlags flags,
1186 const OriginAttributes& aOriginAttributes,
1187 nsIDNSRecord** result) {
1188 // Synchronous resolution is not available on the main thread.
1189 if (NS_IsMainThread()) {
1190 return NS_ERROR_NOT_AVAILABLE;
1193 return ResolveInternal(aHostname, flags, aOriginAttributes, result);
1196 nsresult nsDNSService::DeprecatedSyncResolve(
1197 const nsACString& aHostname, nsIDNSService::DNSFlags flags,
1198 const OriginAttributes& aOriginAttributes, nsIDNSRecord** result) {
1199 return ResolveInternal(aHostname, flags, aOriginAttributes, result);
1202 nsresult nsDNSService::ResolveInternal(
1203 const nsACString& aHostname, nsIDNSService::DNSFlags flags,
1204 const OriginAttributes& aOriginAttributes, nsIDNSRecord** result) {
1205 // grab reference to global host resolver and IDN service. beware
1206 // simultaneous shutdown!!
1207 RefPtr<nsHostResolver> res;
1208 bool localDomain = false;
1210 MutexAutoLock lock(mLock);
1211 res = mResolver;
1212 localDomain = IsLocalDomain(aHostname);
1215 if (mNotifyResolution) {
1216 NS_DispatchToMainThread(new NotifyDNSResolution(aHostname));
1219 NS_ENSURE_TRUE(res, NS_ERROR_OFFLINE);
1221 nsCString hostname;
1222 nsresult rv = PreprocessHostname(localDomain, aHostname, hostname);
1223 if (NS_FAILED(rv)) {
1224 return rv;
1227 if (GetOffline() && (!StaticPrefs::network_dns_offline_localhost() ||
1228 !hostname.LowerCaseEqualsASCII("localhost"))) {
1229 flags |= RESOLVE_OFFLINE;
1232 if (DNSForbiddenByActiveProxy(aHostname, flags)) {
1233 return NS_ERROR_UNKNOWN_PROXY_HOST;
1237 // sync resolve: since the host resolver only works asynchronously, we need
1238 // to use a mutex and a condvar to wait for the result. however, since the
1239 // result may be in the resolvers cache, we might get called back recursively
1240 // on the same thread. so, our mutex needs to be re-entrant. in other words,
1241 // we need to use a monitor! ;-)
1244 PRMonitor* mon = PR_NewMonitor();
1245 if (!mon) {
1246 return NS_ERROR_OUT_OF_MEMORY;
1249 PR_EnterMonitor(mon);
1250 RefPtr<nsDNSSyncRequest> syncReq = new nsDNSSyncRequest(mon);
1252 uint16_t af = GetAFForLookup(hostname, flags);
1254 // TRR uses the main thread for the HTTPS channel to the DoH server.
1255 // If this were to block the main thread while waiting for TRR it would
1256 // likely cause a deadlock. Instead we intentionally choose to not use TRR
1257 // for this.
1258 if (NS_IsMainThread()) {
1259 flags |= RESOLVE_DISABLE_TRR;
1262 rv = res->ResolveHost(hostname, ""_ns, -1, RESOLVE_TYPE_DEFAULT,
1263 aOriginAttributes, flags, af, syncReq);
1264 if (NS_SUCCEEDED(rv)) {
1265 // wait for result
1266 while (!syncReq->mDone) {
1267 PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
1270 if (NS_FAILED(syncReq->mStatus)) {
1271 rv = syncReq->mStatus;
1272 } else {
1273 NS_ASSERTION(syncReq->mHostRecord, "no host record");
1274 RefPtr<nsDNSRecord> rec = new nsDNSRecord(syncReq->mHostRecord);
1275 rec.forget(result);
1279 PR_ExitMonitor(mon);
1280 PR_DestroyMonitor(mon);
1281 return rv;
1284 NS_IMETHODIMP
1285 nsDNSService::GetMyHostName(nsACString& result) {
1286 char name[100];
1287 if (PR_GetSystemInfo(PR_SI_HOSTNAME, name, sizeof(name)) == PR_SUCCESS) {
1288 result = name;
1289 return NS_OK;
1291 return NS_ERROR_FAILURE;
1294 NS_IMETHODIMP
1295 nsDNSService::Observe(nsISupports* subject, const char* topic,
1296 const char16_t* data) {
1297 bool flushCache = false;
1298 RefPtr<nsHostResolver> resolver = GetResolverLocked();
1300 if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
1301 nsAutoCString converted = NS_ConvertUTF16toUTF8(data);
1302 if (!strcmp(converted.get(), NS_NETWORK_LINK_DATA_CHANGED)) {
1303 flushCache = true;
1305 } else if (!strcmp(topic, "last-pb-context-exited")) {
1306 flushCache = true;
1307 } else if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
1308 ReadPrefs(NS_ConvertUTF16toUTF8(data).get());
1309 NS_ENSURE_TRUE(resolver, NS_ERROR_NOT_INITIALIZED);
1310 } else if (!strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
1311 Shutdown();
1314 if (flushCache && resolver) {
1315 resolver->FlushCache(false);
1316 return NS_OK;
1319 return NS_OK;
1322 uint16_t nsDNSService::GetAFForLookup(const nsACString& host,
1323 nsIDNSService::DNSFlags flags) {
1324 if (StaticPrefs::network_dns_disableIPv6() ||
1325 (flags & RESOLVE_DISABLE_IPV6)) {
1326 return PR_AF_INET;
1329 MutexAutoLock lock(mLock);
1331 uint16_t af = PR_AF_UNSPEC;
1333 if (!mIPv4OnlyDomains.IsEmpty()) {
1334 const char *domain, *domainEnd, *end;
1335 uint32_t hostLen, domainLen;
1337 // see if host is in one of the IPv4-only domains
1338 domain = mIPv4OnlyDomains.BeginReading();
1339 domainEnd = mIPv4OnlyDomains.EndReading();
1341 nsACString::const_iterator hostStart;
1342 host.BeginReading(hostStart);
1343 hostLen = host.Length();
1345 do {
1346 // skip any whitespace
1347 while (*domain == ' ' || *domain == '\t') {
1348 ++domain;
1351 // find end of this domain in the string
1352 end = strchr(domain, ',');
1353 if (!end) {
1354 end = domainEnd;
1357 // to see if the hostname is in the domain, check if the domain
1358 // matches the end of the hostname.
1359 domainLen = end - domain;
1360 if (domainLen && hostLen >= domainLen) {
1361 const char* hostTail = hostStart.get() + hostLen - domainLen;
1362 if (nsCRT::strncasecmp(domain, hostTail, domainLen) == 0) {
1363 // now, make sure either that the hostname is a direct match or
1364 // that the hostname begins with a dot.
1365 if (hostLen == domainLen || *hostTail == '.' ||
1366 *(hostTail - 1) == '.') {
1367 af = PR_AF_INET;
1368 break;
1373 domain = end + 1;
1374 } while (*end);
1377 if ((af != PR_AF_INET) && (flags & RESOLVE_DISABLE_IPV4)) {
1378 af = PR_AF_INET6;
1381 return af;
1384 NS_IMETHODIMP
1385 nsDNSService::GetDNSCacheEntries(
1386 nsTArray<mozilla::net::DNSCacheEntries>* args) {
1387 RefPtr<nsHostResolver> resolver = GetResolverLocked();
1388 NS_ENSURE_TRUE(resolver, NS_ERROR_NOT_INITIALIZED);
1389 resolver->GetDNSCacheEntries(args);
1390 return NS_OK;
1393 NS_IMETHODIMP
1394 nsDNSService::ClearCache(bool aTrrToo) {
1395 RefPtr<nsHostResolver> resolver = GetResolverLocked();
1396 NS_ENSURE_TRUE(resolver, NS_ERROR_NOT_INITIALIZED);
1397 resolver->FlushCache(aTrrToo);
1398 return NS_OK;
1401 // For testing purposes only
1402 NS_IMETHODIMP
1403 nsDNSService::ReloadParentalControlEnabled() {
1404 if (mTrrService) {
1405 mTrrService->mParentalControlEnabled =
1406 TRRService::ReloadParentalControlsEnabled();
1408 return NS_OK;
1411 NS_IMETHODIMP
1412 nsDNSService::SetDetectedTrrURI(const nsACString& aURI) {
1413 if (mTrrService) {
1414 mTrrService->SetDetectedTrrURI(aURI);
1416 return NS_OK;
1419 NS_IMETHODIMP
1420 nsDNSService::SetHeuristicDetectionResult(nsITRRSkipReason::value aValue) {
1421 if (mTrrService) {
1422 mTrrService->SetHeuristicDetectionResult(aValue);
1424 return NS_OK;
1427 NS_IMETHODIMP
1428 nsDNSService::GetHeuristicDetectionResult(nsITRRSkipReason::value* aValue) {
1429 if (!mTrrService) {
1430 return NS_ERROR_NOT_AVAILABLE;
1433 *aValue = mTrrService->GetHeuristicDetectionResult();
1434 return NS_OK;
1437 NS_IMETHODIMP
1438 nsDNSService::GetTRRSkipReasonName(nsITRRSkipReason::value aValue,
1439 nsACString& aName) {
1440 return mozilla::net::GetTRRSkipReasonName(aValue, aName);
1443 NS_IMETHODIMP
1444 nsDNSService::GetCurrentTrrURI(nsACString& aURI) {
1445 if (mTrrService) {
1446 mTrrService->GetURI(aURI);
1448 return NS_OK;
1451 NS_IMETHODIMP
1452 nsDNSService::GetCurrentTrrMode(nsIDNSService::ResolverMode* aMode) {
1453 *aMode = nsIDNSService::MODE_NATIVEONLY; // The default mode.
1454 if (mTrrService) {
1455 *aMode = mTrrService->Mode();
1457 return NS_OK;
1460 NS_IMETHODIMP
1461 nsDNSService::GetCurrentTrrConfirmationState(uint32_t* aConfirmationState) {
1462 *aConfirmationState = uint32_t(TRRService::CONFIRM_OFF);
1463 if (mTrrService) {
1464 *aConfirmationState = mTrrService->ConfirmationState();
1466 return NS_OK;
1469 NS_IMETHODIMP
1470 nsDNSService::GetTrrDomain(nsACString& aTRRDomain) {
1471 aTRRDomain.Truncate();
1472 nsAutoCString url;
1473 if (mTrrService) {
1474 mTrrService->GetURI(url);
1476 nsCOMPtr<nsIURI> uri;
1477 nsresult rv = NS_NewURI(getter_AddRefs(uri), url);
1478 if (NS_FAILED(rv)) {
1479 // An empty TRR domain in case of invalid URL.
1480 return NS_OK;
1482 return uri->GetHost(aTRRDomain);
1485 nsresult nsDNSService::GetTRRDomainKey(nsACString& aTRRDomain) {
1486 aTRRDomain = TRRService::ProviderKey();
1487 return NS_OK;
1490 size_t nsDNSService::SizeOfIncludingThis(
1491 mozilla::MallocSizeOf mallocSizeOf) const {
1492 // Measurement of the following members may be added later if DMD finds it
1493 // is worthwhile:
1494 // - mIDN
1495 // - mLock
1497 size_t n = mallocSizeOf(this);
1498 n += mResolver ? mResolver->SizeOfIncludingThis(mallocSizeOf) : 0;
1499 n += mIPv4OnlyDomains.SizeOfExcludingThisIfUnshared(mallocSizeOf);
1500 n += mLocalDomains.SizeOfExcludingThis(mallocSizeOf);
1501 n += mFailedSVCDomainNames.ShallowSizeOfExcludingThis(mallocSizeOf);
1502 for (const auto& data : mFailedSVCDomainNames.Values()) {
1503 n += data->ShallowSizeOfExcludingThis(mallocSizeOf);
1504 for (const auto& name : *data) {
1505 n += name.SizeOfExcludingThisIfUnshared(mallocSizeOf);
1508 return n;
1511 MOZ_DEFINE_MALLOC_SIZE_OF(DNSServiceMallocSizeOf)
1513 NS_IMETHODIMP
1514 nsDNSService::CollectReports(nsIHandleReportCallback* aHandleReport,
1515 nsISupports* aData, bool aAnonymize) {
1516 MOZ_COLLECT_REPORT("explicit/network/dns-service", KIND_HEAP, UNITS_BYTES,
1517 SizeOfIncludingThis(DNSServiceMallocSizeOf),
1518 "Memory used for the DNS service.");
1520 return NS_OK;
1523 NS_IMETHODIMP
1524 nsDNSService::ReportFailedSVCDomainName(const nsACString& aOwnerName,
1525 const nsACString& aSVCDomainName) {
1526 MutexAutoLock lock(mLock);
1528 mFailedSVCDomainNames.GetOrInsertNew(aOwnerName, 1)
1529 ->AppendElement(aSVCDomainName);
1530 return NS_OK;
1533 NS_IMETHODIMP
1534 nsDNSService::IsSVCDomainNameFailed(const nsACString& aOwnerName,
1535 const nsACString& aSVCDomainName,
1536 bool* aResult) {
1537 NS_ENSURE_ARG(aResult);
1539 MutexAutoLock lock(mLock);
1540 *aResult = false;
1541 nsTArray<nsCString>* failedList = mFailedSVCDomainNames.Get(aOwnerName);
1542 if (!failedList) {
1543 return NS_OK;
1546 *aResult = failedList->Contains(aSVCDomainName);
1547 return NS_OK;
1550 NS_IMETHODIMP
1551 nsDNSService::ResetExcludedSVCDomainName(const nsACString& aOwnerName) {
1552 MutexAutoLock lock(mLock);
1553 mFailedSVCDomainNames.Remove(aOwnerName);
1554 return NS_OK;
1557 NS_IMETHODIMP
1558 nsDNSService::GetLastConfirmationStatus(nsresult* aConfirmationStatus) {
1559 if (!mTrrService) {
1560 return NS_ERROR_NOT_AVAILABLE;
1562 *aConfirmationStatus = mTrrService->LastConfirmationStatus();
1563 return NS_OK;
1566 NS_IMETHODIMP nsDNSService::GetLastConfirmationSkipReason(
1567 TRRSkippedReason* aSkipReason) {
1568 if (!mTrrService) {
1569 return NS_ERROR_NOT_AVAILABLE;
1571 *aSkipReason = mTrrService->LastConfirmationSkipReason();
1572 return NS_OK;
1575 namespace mozilla::net {
1576 nsresult GetTRRSkipReasonName(TRRSkippedReason aReason, nsACString& aName) {
1577 static_assert(TRRSkippedReason::TRR_UNSET == 0);
1578 static_assert(TRRSkippedReason::TRR_OK == 1);
1579 static_assert(TRRSkippedReason::TRR_NO_GSERVICE == 2);
1580 static_assert(TRRSkippedReason::TRR_PARENTAL_CONTROL == 3);
1581 static_assert(TRRSkippedReason::TRR_OFF_EXPLICIT == 4);
1582 static_assert(TRRSkippedReason::TRR_REQ_MODE_DISABLED == 5);
1583 static_assert(TRRSkippedReason::TRR_MODE_NOT_ENABLED == 6);
1584 static_assert(TRRSkippedReason::TRR_FAILED == 7);
1585 static_assert(TRRSkippedReason::TRR_MODE_UNHANDLED_DEFAULT == 8);
1586 static_assert(TRRSkippedReason::TRR_MODE_UNHANDLED_DISABLED == 9);
1587 static_assert(TRRSkippedReason::TRR_DISABLED_FLAG == 10);
1588 static_assert(TRRSkippedReason::TRR_TIMEOUT == 11);
1589 static_assert(TRRSkippedReason::TRR_CHANNEL_DNS_FAIL == 12);
1590 static_assert(TRRSkippedReason::TRR_BROWSER_IS_OFFLINE == 13);
1591 static_assert(TRRSkippedReason::TRR_NOT_CONFIRMED == 14);
1592 static_assert(TRRSkippedReason::TRR_DID_NOT_MAKE_QUERY == 15);
1593 static_assert(TRRSkippedReason::TRR_UNKNOWN_CHANNEL_FAILURE == 16);
1594 static_assert(TRRSkippedReason::TRR_HOST_BLOCKED_TEMPORARY == 17);
1595 static_assert(TRRSkippedReason::TRR_SEND_FAILED == 18);
1596 static_assert(TRRSkippedReason::TRR_NET_RESET == 19);
1597 static_assert(TRRSkippedReason::TRR_NET_TIMEOUT == 20);
1598 static_assert(TRRSkippedReason::TRR_NET_REFUSED == 21);
1599 static_assert(TRRSkippedReason::TRR_NET_INTERRUPT == 22);
1600 static_assert(TRRSkippedReason::TRR_NET_INADEQ_SEQURITY == 23);
1601 static_assert(TRRSkippedReason::TRR_NO_ANSWERS == 24);
1602 static_assert(TRRSkippedReason::TRR_DECODE_FAILED == 25);
1603 static_assert(TRRSkippedReason::TRR_EXCLUDED == 26);
1604 static_assert(TRRSkippedReason::TRR_SERVER_RESPONSE_ERR == 27);
1605 static_assert(TRRSkippedReason::TRR_RCODE_FAIL == 28);
1606 static_assert(TRRSkippedReason::TRR_NO_CONNECTIVITY == 29);
1607 static_assert(TRRSkippedReason::TRR_NXDOMAIN == 30);
1608 static_assert(TRRSkippedReason::TRR_REQ_CANCELLED == 31);
1609 static_assert(TRRSkippedReason::ODOH_KEY_NOT_USABLE == 32);
1610 static_assert(TRRSkippedReason::ODOH_UPDATE_KEY_FAILED == 33);
1611 static_assert(TRRSkippedReason::ODOH_KEY_NOT_AVAILABLE == 34);
1612 static_assert(TRRSkippedReason::ODOH_ENCRYPTION_FAILED == 35);
1613 static_assert(TRRSkippedReason::ODOH_DECRYPTION_FAILED == 36);
1614 static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_GOOGLE_SAFESEARCH ==
1615 37);
1616 static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_YOUTUBE_SAFESEARCH ==
1617 38);
1618 static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_ZSCALER_CANARY == 39);
1619 static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_CANARY == 40);
1620 static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_MODIFIED_ROOTS == 41);
1621 static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_PARENTAL_CONTROLS ==
1622 42);
1623 static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_THIRD_PARTY_ROOTS ==
1624 43);
1625 static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_ENTERPRISE_POLICY ==
1626 44);
1627 static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_VPN == 45);
1628 static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_PROXY == 46);
1629 static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_NRPT == 47);
1630 static_assert(TRRSkippedReason::TRR_BAD_URL == 48);
1631 static_assert(TRRSkippedReason::TRR_SYSTEM_SLEEP_MODE == 49);
1633 switch (aReason) {
1634 case TRRSkippedReason::TRR_UNSET:
1635 aName = "TRR_UNSET"_ns;
1636 break;
1637 case TRRSkippedReason::TRR_OK:
1638 aName = "TRR_OK"_ns;
1639 break;
1640 case TRRSkippedReason::TRR_NO_GSERVICE:
1641 aName = "TRR_NO_GSERVICE"_ns;
1642 break;
1643 case TRRSkippedReason::TRR_PARENTAL_CONTROL:
1644 aName = "TRR_PARENTAL_CONTROL"_ns;
1645 break;
1646 case TRRSkippedReason::TRR_OFF_EXPLICIT:
1647 aName = "TRR_OFF_EXPLICIT"_ns;
1648 break;
1649 case TRRSkippedReason::TRR_REQ_MODE_DISABLED:
1650 aName = "TRR_REQ_MODE_DISABLED"_ns;
1651 break;
1652 case TRRSkippedReason::TRR_MODE_NOT_ENABLED:
1653 aName = "TRR_MODE_NOT_ENABLED"_ns;
1654 break;
1655 case TRRSkippedReason::TRR_FAILED:
1656 aName = "TRR_FAILED"_ns;
1657 break;
1658 case TRRSkippedReason::TRR_MODE_UNHANDLED_DEFAULT:
1659 aName = "TRR_MODE_UNHANDLED_DEFAULT"_ns;
1660 break;
1661 case TRRSkippedReason::TRR_MODE_UNHANDLED_DISABLED:
1662 aName = "TRR_MODE_UNHANDLED_DISABLED"_ns;
1663 break;
1664 case TRRSkippedReason::TRR_DISABLED_FLAG:
1665 aName = "TRR_DISABLED_FLAG"_ns;
1666 break;
1667 case TRRSkippedReason::TRR_TIMEOUT:
1668 aName = "TRR_TIMEOUT"_ns;
1669 break;
1670 case TRRSkippedReason::TRR_CHANNEL_DNS_FAIL:
1671 aName = "TRR_CHANNEL_DNS_FAIL"_ns;
1672 break;
1673 case TRRSkippedReason::TRR_BROWSER_IS_OFFLINE:
1674 aName = "TRR_BROWSER_IS_OFFLINE"_ns;
1675 break;
1676 case TRRSkippedReason::TRR_NOT_CONFIRMED:
1677 aName = "TRR_NOT_CONFIRMED"_ns;
1678 break;
1679 case TRRSkippedReason::TRR_DID_NOT_MAKE_QUERY:
1680 aName = "TRR_DID_NOT_MAKE_QUERY"_ns;
1681 break;
1682 case TRRSkippedReason::TRR_UNKNOWN_CHANNEL_FAILURE:
1683 aName = "TRR_UNKNOWN_CHANNEL_FAILURE"_ns;
1684 break;
1685 case TRRSkippedReason::TRR_HOST_BLOCKED_TEMPORARY:
1686 aName = "TRR_HOST_BLOCKED_TEMPORARY"_ns;
1687 break;
1688 case TRRSkippedReason::TRR_SEND_FAILED:
1689 aName = "TRR_SEND_FAILED"_ns;
1690 break;
1691 case TRRSkippedReason::TRR_NET_RESET:
1692 aName = "TRR_NET_RESET"_ns;
1693 break;
1694 case TRRSkippedReason::TRR_NET_TIMEOUT:
1695 aName = "TRR_NET_TIMEOUT"_ns;
1696 break;
1697 case TRRSkippedReason::TRR_NET_REFUSED:
1698 aName = "TRR_NET_REFUSED"_ns;
1699 break;
1700 case TRRSkippedReason::TRR_NET_INTERRUPT:
1701 aName = "TRR_NET_INTERRUPT"_ns;
1702 break;
1703 case TRRSkippedReason::TRR_NET_INADEQ_SEQURITY:
1704 aName = "TRR_NET_INADEQ_SEQURITY"_ns;
1705 break;
1706 case TRRSkippedReason::TRR_NO_ANSWERS:
1707 aName = "TRR_NO_ANSWERS"_ns;
1708 break;
1709 case TRRSkippedReason::TRR_DECODE_FAILED:
1710 aName = "TRR_DECODE_FAILED"_ns;
1711 break;
1712 case TRRSkippedReason::TRR_EXCLUDED:
1713 aName = "TRR_EXCLUDED"_ns;
1714 break;
1715 case TRRSkippedReason::TRR_SERVER_RESPONSE_ERR:
1716 aName = "TRR_SERVER_RESPONSE_ERR"_ns;
1717 break;
1718 case TRRSkippedReason::TRR_RCODE_FAIL:
1719 aName = "TRR_RCODE_FAIL"_ns;
1720 break;
1721 case TRRSkippedReason::TRR_NO_CONNECTIVITY:
1722 aName = "TRR_NO_CONNECTIVITY"_ns;
1723 break;
1724 case TRRSkippedReason::TRR_NXDOMAIN:
1725 aName = "TRR_NXDOMAIN"_ns;
1726 break;
1727 case TRRSkippedReason::TRR_REQ_CANCELLED:
1728 aName = "TRR_REQ_CANCELLED"_ns;
1729 break;
1730 case TRRSkippedReason::ODOH_KEY_NOT_USABLE:
1731 aName = "ODOH_KEY_NOT_USABLE"_ns;
1732 break;
1733 case TRRSkippedReason::ODOH_UPDATE_KEY_FAILED:
1734 aName = "ODOH_UPDATE_KEY_FAILED"_ns;
1735 break;
1736 case TRRSkippedReason::ODOH_KEY_NOT_AVAILABLE:
1737 aName = "ODOH_KEY_NOT_AVAILABLE"_ns;
1738 break;
1739 case TRRSkippedReason::ODOH_ENCRYPTION_FAILED:
1740 aName = "ODOH_ENCRYPTION_FAILED"_ns;
1741 break;
1742 case TRRSkippedReason::ODOH_DECRYPTION_FAILED:
1743 aName = "ODOH_DECRYPTION_FAILED"_ns;
1744 break;
1745 case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_GOOGLE_SAFESEARCH:
1746 aName = "TRR_HEURISTIC_TRIPPED_GOOGLE_SAFESEARCH"_ns;
1747 break;
1748 case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_YOUTUBE_SAFESEARCH:
1749 aName = "TRR_HEURISTIC_TRIPPED_YOUTUBE_SAFESEARCH"_ns;
1750 break;
1751 case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_ZSCALER_CANARY:
1752 aName = "TRR_HEURISTIC_TRIPPED_ZSCALER_CANARY"_ns;
1753 break;
1754 case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_CANARY:
1755 aName = "TRR_HEURISTIC_TRIPPED_CANARY"_ns;
1756 break;
1757 case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_MODIFIED_ROOTS:
1758 aName = "TRR_HEURISTIC_TRIPPED_MODIFIED_ROOTS"_ns;
1759 break;
1760 case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_PARENTAL_CONTROLS:
1761 aName = "TRR_HEURISTIC_TRIPPED_PARENTAL_CONTROLS"_ns;
1762 break;
1763 case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_THIRD_PARTY_ROOTS:
1764 aName = "TRR_HEURISTIC_TRIPPED_THIRD_PARTY_ROOTS"_ns;
1765 break;
1766 case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_ENTERPRISE_POLICY:
1767 aName = "TRR_HEURISTIC_TRIPPED_ENTERPRISE_POLICY"_ns;
1768 break;
1769 case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_VPN:
1770 aName = "TRR_HEURISTIC_TRIPPED_VPN"_ns;
1771 break;
1772 case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_PROXY:
1773 aName = "TRR_HEURISTIC_TRIPPED_PROXY"_ns;
1774 break;
1775 case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_NRPT:
1776 aName = "TRR_HEURISTIC_TRIPPED_NRPT"_ns;
1777 break;
1778 case TRRSkippedReason::TRR_BAD_URL:
1779 aName = "TRR_BAD_URL"_ns;
1780 break;
1781 case TRRSkippedReason::TRR_SYSTEM_SLEEP_MODE:
1782 aName = "TRR_SYSTEM_SLEEP_MODE"_ns;
1783 break;
1784 default:
1785 MOZ_ASSERT(false, "Unknown value");
1788 return NS_OK;
1790 } // namespace mozilla::net