Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsDNSService2.h"
#include "nsIDNSRecord.h"
#include "nsIDNSListener.h"
#include "nsIDNSByTypeRecord.h"
#include "nsICancelable.h"
#include "nsIPrefBranch.h"
#include "nsIOService.h"
#include "nsIXPConnect.h"
#include "nsProxyRelease.h"
#include "nsReadableUtils.h"
#include "nsString.h"
#include "nsCRT.h"
#include "nsNetCID.h"
#include "nsError.h"
#include "nsDNSPrefetch.h"
#include "nsThreadUtils.h"
#include "nsIProtocolProxyService.h"
#include "nsIObliviousHttp.h"
#include "prsystem.h"
#include "prnetdb.h"
#include "prmon.h"
#include "prio.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsNetAddr.h"
#include "nsNetUtil.h"
#include "nsProxyRelease.h"
#include "nsQueryObject.h"
#include "nsIObserverService.h"
#include "nsINetworkLinkService.h"
#include "DNSAdditionalInfo.h"
#include "TRRService.h"
#include "mozilla/Attributes.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/net/NeckoCommon.h"
#include "mozilla/net/ChildDNSService.h"
#include "mozilla/net/DNSListenerProxy.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPrefs_network.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/SyncRunnable.h"
#include "mozilla/TextUtils.h"
#include "mozilla/Utf8.h"
using namespace mozilla;
using namespace mozilla::net;
static const char kPrefDnsCacheEntries[] = "network.dnsCacheEntries";
static const char kPrefDnsCacheExpiration[] = "network.dnsCacheExpiration";
static const char kPrefDnsCacheGrace[] =
"network.dnsCacheExpirationGracePeriod";
static const char kPrefIPv4OnlyDomains[] = "network.dns.ipv4OnlyDomains";
static const char kPrefBlockDotOnion[] = "network.dns.blockDotOnion";
static const char kPrefDnsLocalDomains[] = "network.dns.localDomains";
static const char kPrefDnsForceResolve[] = "network.dns.forceResolve";
static const char kPrefDnsOfflineLocalhost[] = "network.dns.offline-localhost";
static const char kPrefDnsNotifyResolution[] = "network.dns.notifyResolution";
static const char kPrefDnsMockHTTPSRRDomain[] =
"network.dns.mock_HTTPS_RR_domain";
//-----------------------------------------------------------------------------
class nsDNSRecord : public nsIDNSAddrRecord {
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIDNSRECORD
NS_DECL_NSIDNSADDRRECORD
explicit nsDNSRecord(nsHostRecord* hostRecord) {
mHostRecord = do_QueryObject(hostRecord);
}
private:
virtual ~nsDNSRecord() = default;
RefPtr<AddrHostRecord> mHostRecord;
// Since mIter is holding a weak reference to the NetAddr array we must
// make sure it is not released. So we also keep a RefPtr to the AddrInfo
// which is immutable.
RefPtr<AddrInfo> mAddrInfo;
nsTArray<NetAddr>::const_iterator mIter;
const NetAddr* iter() {
if (!mIter.GetArray()) {
return nullptr;
}
if (mIter.GetArray()->end() == mIter) {
return nullptr;
}
return &*mIter;
}
int mIterGenCnt = -1; // the generation count of
// mHostRecord->addr_info when we
// start iterating
bool mDone = false;
};
NS_IMPL_ISUPPORTS(nsDNSRecord, nsIDNSRecord, nsIDNSAddrRecord)
NS_IMETHODIMP
nsDNSRecord::GetCanonicalName(nsACString& result) {
// this method should only be called if we have a CNAME
NS_ENSURE_TRUE(mHostRecord->flags & nsHostResolver::RES_CANON_NAME,
NS_ERROR_NOT_AVAILABLE);
MutexAutoLock lock(mHostRecord->addr_info_lock);
// if the record is for an IP address literal, then the canonical
// host name is the IP address literal.
if (!mHostRecord->addr_info) {
result = mHostRecord->host;
return NS_OK;
}
if (mHostRecord->addr_info->CanonicalHostname().IsEmpty()) {
result = mHostRecord->addr_info->Hostname();
} else {
result = mHostRecord->addr_info->CanonicalHostname();
}
return NS_OK;
}
NS_IMETHODIMP
nsDNSRecord::IsTRR(bool* retval) {
MutexAutoLock lock(mHostRecord->addr_info_lock);
if (mHostRecord->addr_info) {
*retval = mHostRecord->addr_info->IsTRR();
} else {
*retval = false;
}
return NS_OK;
}
NS_IMETHODIMP
nsDNSRecord::ResolvedInSocketProcess(bool* retval) {
*retval = false;
return NS_OK;
}
NS_IMETHODIMP
nsDNSRecord::GetTrrFetchDuration(double* aTime) {
MutexAutoLock lock(mHostRecord->addr_info_lock);
if (mHostRecord->addr_info && mHostRecord->addr_info->IsTRR()) {
*aTime = mHostRecord->addr_info->GetTrrFetchDuration();
} else {
*aTime = 0;
}
return NS_OK;
}
NS_IMETHODIMP
nsDNSRecord::GetTrrFetchDurationNetworkOnly(double* aTime) {
MutexAutoLock lock(mHostRecord->addr_info_lock);
if (mHostRecord->addr_info && mHostRecord->addr_info->IsTRR()) {
*aTime = mHostRecord->addr_info->GetTrrFetchDurationNetworkOnly();
} else {
*aTime = 0;
}
return NS_OK;
}
NS_IMETHODIMP
nsDNSRecord::GetNextAddr(uint16_t port, NetAddr* addr) {
if (mDone) {
return NS_ERROR_NOT_AVAILABLE;
}
mHostRecord->addr_info_lock.Lock();
if (mHostRecord->addr_info) {
if (mIterGenCnt != mHostRecord->addr_info_gencnt) {
// mHostRecord->addr_info has changed, restart the iteration.
mIter = nsTArray<NetAddr>::const_iterator();
mIterGenCnt = mHostRecord->addr_info_gencnt;
// Make sure to hold a RefPtr to the AddrInfo so we can iterate through
// the NetAddr array.
mAddrInfo = mHostRecord->addr_info;
}
bool startedFresh = !iter();
do {
if (!iter()) {
mIter = mAddrInfo->Addresses().begin();
} else {
mIter++;
}
} while (iter() && mHostRecord->Blocklisted(iter()));
if (!iter() && startedFresh) {
// If everything was blocklisted we want to reset the blocklist (and
// likely relearn it) and return the first address. That is better
// than nothing.
mHostRecord->ResetBlocklist();
mIter = mAddrInfo->Addresses().begin();
}
if (iter()) {
*addr = *mIter;
}
mHostRecord->addr_info_lock.Unlock();
if (!iter()) {
mDone = true;
mIter = nsTArray<NetAddr>::const_iterator();
mAddrInfo = nullptr;
mIterGenCnt = -1;
return NS_ERROR_NOT_AVAILABLE;
}
} else {
mHostRecord->addr_info_lock.Unlock();
if (!mHostRecord->addr) {
// Both mHostRecord->addr_info and mHostRecord->addr are null.
// This can happen if mHostRecord->addr_info expired and the
// attempt to reresolve it failed.
return NS_ERROR_NOT_AVAILABLE;
}
memcpy(addr, mHostRecord->addr.get(), sizeof(NetAddr));
mDone = true;
}
// set given port
port = htons(port);
if (addr->raw.family == AF_INET) {
addr->inet.port = port;
} else if (addr->raw.family == AF_INET6) {
addr->inet6.port = port;
}
return NS_OK;
}
NS_IMETHODIMP
nsDNSRecord::GetAddresses(nsTArray<NetAddr>& aAddressArray) {
if (mDone) {
return NS_ERROR_NOT_AVAILABLE;
}
mHostRecord->addr_info_lock.Lock();
if (mHostRecord->addr_info) {
for (const auto& address : mHostRecord->addr_info->Addresses()) {
if (mHostRecord->Blocklisted(&address)) {
continue;
}
NetAddr* addr = aAddressArray.AppendElement(address);
if (addr->raw.family == AF_INET) {
addr->inet.port = 0;
} else if (addr->raw.family == AF_INET6) {
addr->inet6.port = 0;
}
}
mHostRecord->addr_info_lock.Unlock();
} else {
mHostRecord->addr_info_lock.Unlock();
if (!mHostRecord->addr) {
return NS_ERROR_NOT_AVAILABLE;
}
NetAddr* addr = aAddressArray.AppendElement(NetAddr());
memcpy(addr, mHostRecord->addr.get(), sizeof(NetAddr));
if (addr->raw.family == AF_INET) {
addr->inet.port = 0;
} else if (addr->raw.family == AF_INET6) {
addr->inet6.port = 0;
}
}
return NS_OK;
}
NS_IMETHODIMP
nsDNSRecord::GetScriptableNextAddr(uint16_t port, nsINetAddr** result) {
NetAddr addr;
nsresult rv = GetNextAddr(port, &addr);
if (NS_FAILED(rv)) {
return rv;
}
RefPtr<nsNetAddr> netaddr = new nsNetAddr(&addr);
netaddr.forget(result);
return NS_OK;
}
NS_IMETHODIMP
nsDNSRecord::GetNextAddrAsString(nsACString& result) {
NetAddr addr;
nsresult rv = GetNextAddr(0, &addr);
if (NS_FAILED(rv)) {
return rv;
}
char buf[kIPv6CStrBufSize];
if (addr.ToStringBuffer(buf, sizeof(buf))) {
result.Assign(buf);
return NS_OK;
}
NS_ERROR("NetAddrToString failed unexpectedly");
return NS_ERROR_FAILURE; // conversion failed for some reason
}
NS_IMETHODIMP
nsDNSRecord::HasMore(bool* result) {
if (mDone) {
*result = false;
return NS_OK;
}
nsTArray<NetAddr>::const_iterator iterCopy = mIter;
int iterGenCntCopy = mIterGenCnt;
NetAddr addr;
*result = NS_SUCCEEDED(GetNextAddr(0, &addr));
mIter = iterCopy;
mIterGenCnt = iterGenCntCopy;
mDone = false;
return NS_OK;
}
NS_IMETHODIMP
nsDNSRecord::Rewind() {
mIter = nsTArray<NetAddr>::const_iterator();
mIterGenCnt = -1;
mDone = false;
return NS_OK;
}
NS_IMETHODIMP
nsDNSRecord::ReportUnusable(uint16_t aPort) {
// right now we don't use the port in the blocklist
MutexAutoLock lock(mHostRecord->addr_info_lock);
// Check that we are using a real addr_info (as opposed to a single
// constant address), and that the generation count is valid. Otherwise,
// ignore the report.
if (mHostRecord->addr_info && mIterGenCnt == mHostRecord->addr_info_gencnt &&
iter()) {
mHostRecord->ReportUnusable(iter());
}
return NS_OK;
}
NS_IMETHODIMP
nsDNSRecord::GetEffectiveTRRMode(nsIRequest::TRRMode* aMode) {
*aMode = mHostRecord->EffectiveTRRMode();
return NS_OK;
}
NS_IMETHODIMP nsDNSRecord::GetTrrSkipReason(
nsITRRSkipReason::value* aTrrSkipReason) {
*aTrrSkipReason = mHostRecord->TrrSkipReason();
return NS_OK;
}
NS_IMETHODIMP
nsDNSRecord::GetTtl(uint32_t* aTtl) { return mHostRecord->GetTtl(aTtl); }
class nsDNSByTypeRecord : public nsIDNSByTypeRecord,
public nsIDNSTXTRecord,
public nsIDNSHTTPSSVCRecord {
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIDNSRECORD
NS_DECL_NSIDNSBYTYPERECORD
NS_DECL_NSIDNSTXTRECORD
NS_DECL_NSIDNSHTTPSSVCRECORD
explicit nsDNSByTypeRecord(nsHostRecord* hostRecord) {
mHostRecord = do_QueryObject(hostRecord);
}
private:
virtual ~nsDNSByTypeRecord() = default;
RefPtr<TypeHostRecord> mHostRecord;
};
NS_IMPL_ISUPPORTS(nsDNSByTypeRecord, nsIDNSRecord, nsIDNSByTypeRecord,
nsIDNSTXTRecord, nsIDNSHTTPSSVCRecord)
NS_IMETHODIMP
nsDNSByTypeRecord::GetType(uint32_t* aType) {
*aType = mHostRecord->GetType();
return NS_OK;
}
NS_IMETHODIMP
nsDNSByTypeRecord::GetRecords(CopyableTArray<nsCString>& aRecords) {
// deep copy
return mHostRecord->GetRecords(aRecords);
}
NS_IMETHODIMP
nsDNSByTypeRecord::GetRecordsAsOneString(nsACString& aRecords) {
// deep copy
return mHostRecord->GetRecordsAsOneString(aRecords);
}
NS_IMETHODIMP
nsDNSByTypeRecord::GetRecords(nsTArray<RefPtr<nsISVCBRecord>>& aRecords) {
return mHostRecord->GetRecords(aRecords);
}
NS_IMETHODIMP
nsDNSByTypeRecord::GetServiceModeRecord(bool aNoHttp2, bool aNoHttp3,
nsISVCBRecord** aRecord) {
return mHostRecord->GetServiceModeRecord(aNoHttp2, aNoHttp3, aRecord);
}
NS_IMETHODIMP
nsDNSByTypeRecord::GetAllRecordsWithEchConfig(
bool aNoHttp2, bool aNoHttp3, bool* aAllRecordsHaveEchConfig,
bool* aAllRecordsInH3ExcludedList,
nsTArray<RefPtr<nsISVCBRecord>>& aResult) {
return mHostRecord->GetAllRecordsWithEchConfig(
aNoHttp2, aNoHttp3, aAllRecordsHaveEchConfig, aAllRecordsInH3ExcludedList,
aResult);
}
NS_IMETHODIMP
nsDNSByTypeRecord::GetHasIPAddresses(bool* aResult) {
return mHostRecord->GetHasIPAddresses(aResult);
}
NS_IMETHODIMP
nsDNSByTypeRecord::GetAllRecordsExcluded(bool* aResult) {
return mHostRecord->GetAllRecordsExcluded(aResult);
}
NS_IMETHODIMP
nsDNSByTypeRecord::GetResults(mozilla::net::TypeRecordResultType* aResults) {
*aResults = mHostRecord->GetResults();
return NS_OK;
}
NS_IMETHODIMP
nsDNSByTypeRecord::GetTtl(uint32_t* aTtl) { return mHostRecord->GetTtl(aTtl); }
//-----------------------------------------------------------------------------
class nsDNSAsyncRequest final : public nsResolveHostCallback,
public nsICancelable {
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSICANCELABLE
nsDNSAsyncRequest(nsHostResolver* res, const nsACString& host,
const nsACString& trrServer, uint16_t type,
const OriginAttributes& attrs, nsIDNSListener* listener,
nsIDNSService::DNSFlags flags, uint16_t af)
: mResolver(res),
mHost(host),
mTrrServer(trrServer),
mType(type),
mOriginAttributes(attrs),
mListener(listener),
mFlags(flags),
mAF(af) {}
void OnResolveHostComplete(nsHostResolver*, nsHostRecord*, nsresult) override;
// Returns TRUE if the DNS listener arg is the same as the member listener
// Used in Cancellations to remove DNS requests associated with a
// particular hostname and nsIDNSListener
bool EqualsAsyncListener(nsIDNSListener* aListener) override;
size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const override;
RefPtr<nsHostResolver> mResolver;
nsCString mHost; // hostname we're resolving
nsCString mTrrServer; // A trr server to be used.
uint16_t mType = 0;
const OriginAttributes
mOriginAttributes; // The originAttributes for this resolving
nsCOMPtr<nsIDNSListener> mListener;
nsIDNSService::DNSFlags mFlags = nsIDNSService::RESOLVE_DEFAULT_FLAGS;
uint16_t mAF = 0;
private:
virtual ~nsDNSAsyncRequest() = default;
};
NS_IMPL_ISUPPORTS(nsDNSAsyncRequest, nsICancelable)
void nsDNSAsyncRequest::OnResolveHostComplete(nsHostResolver* resolver,
nsHostRecord* hostRecord,
nsresult status) {
// need to have an owning ref when we issue the callback to enable
// the caller to be able to addref/release multiple times without
// destroying the record prematurely.
nsCOMPtr<nsIDNSRecord> rec;
if (NS_SUCCEEDED(status) ||
mFlags & nsIDNSService::RESOLVE_WANT_RECORD_ON_ERROR) {
MOZ_ASSERT(hostRecord, "no host record");
if (!hostRecord) {
mListener->OnLookupComplete(this, nullptr, NS_ERROR_UNKNOWN_HOST);
mListener = nullptr;
return;
}
if (hostRecord->type != nsDNSService::RESOLVE_TYPE_DEFAULT) {
rec = new nsDNSByTypeRecord(hostRecord);
} else {
rec = new nsDNSRecord(hostRecord);
}
}
mListener->OnLookupComplete(this, rec, status);
mListener = nullptr;
}
bool nsDNSAsyncRequest::EqualsAsyncListener(nsIDNSListener* aListener) {
uintptr_t originalListenerAddr = reinterpret_cast<uintptr_t>(mListener.get());
RefPtr<DNSListenerProxy> wrapper = do_QueryObject(mListener);
if (wrapper) {
originalListenerAddr = wrapper->GetOriginalListenerAddress();
}
uintptr_t listenerAddr = reinterpret_cast<uintptr_t>(aListener);
return (listenerAddr == originalListenerAddr);
}
size_t nsDNSAsyncRequest::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const {
size_t n = mallocSizeOf(this);
// The following fields aren't measured.
// - mHost, because it's a non-owning pointer
// - mResolver, because it's a non-owning pointer
// - mListener, because it's a non-owning pointer
return n;
}
NS_IMETHODIMP
nsDNSAsyncRequest::Cancel(nsresult reason) {
NS_ENSURE_ARG(NS_FAILED(reason));
MOZ_DIAGNOSTIC_ASSERT(mResolver, "mResolver should not be null");
mResolver->DetachCallback(mHost, mTrrServer, mType, mOriginAttributes, mFlags,
mAF, this, reason);
return NS_OK;
}
//-----------------------------------------------------------------------------
class nsDNSSyncRequest : public nsResolveHostCallback {
NS_DECL_THREADSAFE_ISUPPORTS
public:
explicit nsDNSSyncRequest(PRMonitor* mon) : mMonitor(mon) {}
void OnResolveHostComplete(nsHostResolver*, nsHostRecord*, nsresult) override;
bool EqualsAsyncListener(nsIDNSListener* aListener) override;
size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const override;
bool mDone = false;
nsresult mStatus = NS_OK;
RefPtr<nsHostRecord> mHostRecord;
private:
virtual ~nsDNSSyncRequest() = default;
PRMonitor* mMonitor = nullptr;
};
NS_IMPL_ISUPPORTS0(nsDNSSyncRequest)
void nsDNSSyncRequest::OnResolveHostComplete(nsHostResolver* resolver,
nsHostRecord* hostRecord,
nsresult status) {
// store results, and wake up nsDNSService::Resolve to process results.
PR_EnterMonitor(mMonitor);
mDone = true;
mStatus = status;
mHostRecord = hostRecord;
PR_Notify(mMonitor);
PR_ExitMonitor(mMonitor);
}
bool nsDNSSyncRequest::EqualsAsyncListener(nsIDNSListener* aListener) {
// Sync request: no listener to compare
return false;
}
size_t nsDNSSyncRequest::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const {
size_t n = mallocSizeOf(this);
// The following fields aren't measured.
// - mHostRecord, because it's a non-owning pointer
// Measurement of the following members may be added later if DMD finds it
// is worthwhile:
// - mMonitor
return n;
}
class NotifyDNSResolution : public Runnable {
public:
explicit NotifyDNSResolution(const nsACString& aHostname)
: mozilla::Runnable("NotifyDNSResolution"), mHostname(aHostname) {}
NS_IMETHOD Run() override {
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (obs) {
obs->NotifyObservers(nullptr, "dns-resolution-request",
NS_ConvertUTF8toUTF16(mHostname).get());
}
return NS_OK;
}
private:
nsCString mHostname;
};
//-----------------------------------------------------------------------------
static StaticRefPtr<DNSServiceWrapper> gDNSServiceWrapper;
NS_IMPL_ISUPPORTS(DNSServiceWrapper, nsIDNSService, nsPIDNSService)
// static
already_AddRefed<nsIDNSService> DNSServiceWrapper::GetSingleton() {
if (!gDNSServiceWrapper) {
gDNSServiceWrapper = new DNSServiceWrapper();
gDNSServiceWrapper->mDNSServiceInUse = ChildDNSService::GetSingleton();
if (gDNSServiceWrapper->mDNSServiceInUse) {
ClearOnShutdown(&gDNSServiceWrapper);
nsDNSPrefetch::Initialize(gDNSServiceWrapper);
} else {
gDNSServiceWrapper = nullptr;
}
}
return do_AddRef(gDNSServiceWrapper);
}
// static
void DNSServiceWrapper::SwitchToBackupDNSService() {
if (!gDNSServiceWrapper) {
return;
}
gDNSServiceWrapper->mBackupDNSService = nsDNSService::GetSingleton();
MutexAutoLock lock(gDNSServiceWrapper->mLock);
gDNSServiceWrapper->mBackupDNSService.swap(
gDNSServiceWrapper->mDNSServiceInUse);
}
nsIDNSService* DNSServiceWrapper::DNSService() {
MOZ_ASSERT(XRE_IsParentProcess());
MutexAutoLock lock(mLock);
return mDNSServiceInUse.get();
}
nsPIDNSService* DNSServiceWrapper::PIDNSService() {
MOZ_ASSERT(XRE_IsParentProcess());
nsCOMPtr<nsPIDNSService> service = do_QueryInterface(DNSService());
return service.get();
}
//-----------------------------------------------------------------------------
NS_IMPL_ISUPPORTS_INHERITED(nsDNSService, DNSServiceBase, nsIDNSService,
nsPIDNSService, nsIMemoryReporter)
/******************************************************************************
* nsDNSService impl:
* singleton instance ctor/dtor methods
******************************************************************************/
static StaticRefPtr<nsDNSService> gDNSService;
static Atomic<bool> gInited(false);
already_AddRefed<nsIDNSService> GetOrInitDNSService() {
if (gInited) {
return nsDNSService::GetXPCOMSingleton();
}
nsCOMPtr<nsIDNSService> dns = nullptr;
auto initTask = [&dns]() { dns = do_GetService(NS_DNSSERVICE_CID); };
if (!NS_IsMainThread()) {
// Forward to the main thread synchronously.
RefPtr<nsIThread> mainThread = do_GetMainThread();
if (!mainThread) {
return nullptr;
}
SyncRunnable::DispatchToThread(
mainThread, NS_NewRunnableFunction("GetOrInitDNSService", initTask));
} else {
initTask();
}
return dns.forget();
}
already_AddRefed<nsIDNSService> nsDNSService::GetXPCOMSingleton() {
auto getDNSHelper = []() -> already_AddRefed<nsIDNSService> {
if (nsIOService::UseSocketProcess()) {
if (XRE_IsSocketProcess()) {
return GetSingleton();
}
if (XRE_IsParentProcess()) {
return DNSServiceWrapper::GetSingleton();
}
if (XRE_IsContentProcess()) {
return ChildDNSService::GetSingleton();
}
return nullptr;
}
if (XRE_IsParentProcess()) {
return GetSingleton();
}
if (XRE_IsContentProcess() || XRE_IsSocketProcess()) {
return ChildDNSService::GetSingleton();
}
return nullptr;
};
if (gInited) {
return getDNSHelper();
}
nsCOMPtr<nsIDNSService> dns = getDNSHelper();
if (dns) {
gInited = true;
}
return dns.forget();
}
already_AddRefed<nsDNSService> nsDNSService::GetSingleton() {
MOZ_ASSERT_IF(nsIOService::UseSocketProcess(), XRE_IsSocketProcess());
MOZ_ASSERT_IF(!nsIOService::UseSocketProcess(), XRE_IsParentProcess());
if (!gDNSService) {
if (!NS_IsMainThread()) {
return nullptr;
}
gDNSService = new nsDNSService();
if (NS_SUCCEEDED(gDNSService->Init())) {
ClearOnShutdown(&gDNSService);
} else {
gDNSService = nullptr;
}
}
return do_AddRef(gDNSService);
}
void nsDNSService::ReadPrefs(const char* name) {
DNSServiceBase::ReadPrefs(name);
bool tmpbool;
uint32_t tmpint;
mResolverPrefsUpdated = false;
// resolver-specific prefs first
if (!name || !strcmp(name, kPrefDnsCacheEntries)) {
if (NS_SUCCEEDED(Preferences::GetUint(kPrefDnsCacheEntries, &tmpint))) {
if (!name || (tmpint != mResCacheEntries)) {
mResCacheEntries = tmpint;
mResolverPrefsUpdated = true;
}
}
}
if (!name || !strcmp(name, kPrefDnsCacheExpiration)) {
if (NS_SUCCEEDED(Preferences::GetUint(kPrefDnsCacheExpiration, &tmpint))) {
if (!name || (tmpint != mResCacheExpiration)) {
mResCacheExpiration = tmpint;
mResolverPrefsUpdated = true;
}
}
}
if (!name || !strcmp(name, kPrefDnsCacheGrace)) {
if (NS_SUCCEEDED(Preferences::GetUint(kPrefDnsCacheGrace, &tmpint))) {
if (!name || (tmpint != mResCacheGrace)) {
mResCacheGrace = tmpint;
mResolverPrefsUpdated = true;
}
}
}
// DNSservice prefs
if (!name || !strcmp(name, kPrefDnsOfflineLocalhost)) {
if (NS_SUCCEEDED(
Preferences::GetBool(kPrefDnsOfflineLocalhost, &tmpbool))) {
mOfflineLocalhost = tmpbool;
}
}
if (!name || !strcmp(name, kPrefBlockDotOnion)) {
if (NS_SUCCEEDED(Preferences::GetBool(kPrefBlockDotOnion, &tmpbool))) {
mBlockDotOnion = tmpbool;
}
}
if (!name || !strcmp(name, kPrefDnsNotifyResolution)) {
if (NS_SUCCEEDED(
Preferences::GetBool(kPrefDnsNotifyResolution, &tmpbool))) {
mNotifyResolution = tmpbool;
}
}
if (!name || !strcmp(name, kPrefIPv4OnlyDomains)) {
Preferences::GetCString(kPrefIPv4OnlyDomains, mIPv4OnlyDomains);
}
if (!name || !strcmp(name, kPrefDnsLocalDomains)) {
nsCString localDomains;
Preferences::GetCString(kPrefDnsLocalDomains, localDomains);
MutexAutoLock lock(mLock);
mLocalDomains.Clear();
for (const auto& token :
nsCCharSeparatedTokenizerTemplate<NS_IsAsciiWhitespace,
nsTokenizerFlags::SeparatorOptional>(
localDomains, ',')
.ToRange()) {
mLocalDomains.Insert(token);
}
}
if (!name || !strcmp(name, kPrefDnsForceResolve)) {
Preferences::GetCString(kPrefDnsForceResolve, mForceResolve);
mForceResolveOn = !mForceResolve.IsEmpty();
}
if (!name || !strcmp(name, kPrefDnsMockHTTPSRRDomain)) {
nsCString mockHTTPSRRDomain;
Preferences::GetCString(kPrefDnsMockHTTPSRRDomain, mockHTTPSRRDomain);
if (mockHTTPSRRDomain.IsEmpty()) {
mHasMockHTTPSRRDomainSet = false;
} else {
mHasMockHTTPSRRDomainSet = true;
MutexAutoLock lock(mLock);
mMockHTTPSRRDomain = mockHTTPSRRDomain;
}
}
}
NS_IMETHODIMP
nsDNSService::Init() {
MOZ_ASSERT(!mResolver);
MOZ_ASSERT(NS_IsMainThread());
ReadPrefs(nullptr);
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService) {
observerService->AddObserver(this, "last-pb-context-exited", false);
observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
}
RefPtr<nsHostResolver> res;
nsresult rv = nsHostResolver::Create(mResCacheEntries, mResCacheExpiration,
mResCacheGrace, getter_AddRefs(res));
if (NS_SUCCEEDED(rv)) {
// now, set all of our member variables while holding the lock
MutexAutoLock lock(mLock);
mResolver = res;
}
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (prefs) {
// register as prefs observer
prefs->AddObserver(kPrefDnsCacheEntries, this, false);
prefs->AddObserver(kPrefDnsCacheExpiration, this, false);
prefs->AddObserver(kPrefDnsCacheGrace, this, false);
prefs->AddObserver(kPrefIPv4OnlyDomains, this, false);
prefs->AddObserver(kPrefDnsLocalDomains, this, false);
prefs->AddObserver(kPrefDnsForceResolve, this, false);
prefs->AddObserver(kPrefDnsOfflineLocalhost, this, false);
prefs->AddObserver(kPrefBlockDotOnion, this, false);
prefs->AddObserver(kPrefDnsNotifyResolution, this, false);
prefs->AddObserver(kPrefDnsMockHTTPSRRDomain, this, false);
AddPrefObserver(prefs);
}
nsDNSPrefetch::Initialize(this);
RegisterWeakMemoryReporter(this);
nsCOMPtr<nsIObliviousHttpService> ohttpService(
do_GetService("@mozilla.org/network/oblivious-http-service;1"));
mTrrService = new TRRService();
if (NS_FAILED(mTrrService->Init(mResolver->IsNativeHTTPSEnabled()))) {
mTrrService = nullptr;
}
return NS_OK;
}
NS_IMETHODIMP
nsDNSService::Shutdown() {
UnregisterWeakMemoryReporter(this);
RefPtr<nsHostResolver> res;
{
MutexAutoLock lock(mLock);
res = std::move(mResolver);
}
if (res) {
// Shutdown outside lock.
res->Shutdown();
}
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService) {
observerService->RemoveObserver(this, NS_NETWORK_LINK_TOPIC);
observerService->RemoveObserver(this, "last-pb-context-exited");
observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
}
return NS_OK;
}
bool nsDNSService::GetOffline() const {
bool offline = false;
nsCOMPtr<nsIIOService> io = do_GetService(NS_IOSERVICE_CONTRACTID);
if (io) {
io->GetOffline(&offline);
}
return offline;
}
NS_IMETHODIMP
nsDNSService::GetPrefetchEnabled(bool* outVal) {
MutexAutoLock lock(mLock);
*outVal = !mDisablePrefetch;
return NS_OK;
}
NS_IMETHODIMP
nsDNSService::SetPrefetchEnabled(bool inVal) {
MutexAutoLock lock(mLock);
mDisablePrefetch = !inVal;
return NS_OK;
}
already_AddRefed<nsHostResolver> nsDNSService::GetResolverLocked() {
MutexAutoLock lock(mLock);
return do_AddRef(mResolver);
}
nsresult nsDNSService::PreprocessHostname(bool aLocalDomain,
const nsACString& aInput,
nsACString& aACE) {
// Enforce RFC 7686
if (mBlockDotOnion && StringEndsWith(aInput, ".onion"_ns)) {
return NS_ERROR_UNKNOWN_HOST;
}
if (aLocalDomain) {
aACE.AssignLiteral("localhost");
return NS_OK;
}
if (mTrrService && mTrrService->MaybeBootstrap(aInput, aACE)) {
return NS_OK;
}
if (mForceResolveOn) {
MutexAutoLock lock(mLock);
if (!aInput.LowerCaseEqualsASCII("localhost") &&
!aInput.LowerCaseEqualsASCII("127.0.0.1")) {
aACE.Assign(mForceResolve);
return NS_OK;
}
}
if (!NS_SUCCEEDED(NS_DomainToASCIIAllowAnyGlyphfulASCII(aInput, aACE))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
bool nsDNSService::IsLocalDomain(const nsACString& aHostname) const {
bool localDomain = mLocalDomains.Contains(aHostname);
if (StringEndsWith(aHostname, "."_ns)) {
localDomain = localDomain || mLocalDomains.Contains(Substring(
aHostname, 0, aHostname.Length() - 1));
}
return localDomain;
}
nsresult nsDNSService::AsyncResolveInternal(
const nsACString& aHostname, uint16_t type, nsIDNSService::DNSFlags flags,
nsIDNSAdditionalInfo* aInfo, nsIDNSListener* aListener,
nsIEventTarget* target_, const OriginAttributes& aOriginAttributes,
nsICancelable** result) {
// grab reference to global host resolver and IDN service. beware
// simultaneous shutdown!!
RefPtr<nsHostResolver> res;
nsCOMPtr<nsIEventTarget> target = target_;
nsCOMPtr<nsIDNSListener> listener = aListener;
bool localDomain = false;
{
MutexAutoLock lock(mLock);
if (mDisablePrefetch && (flags & RESOLVE_SPECULATE)) {
return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
}
res = mResolver;
localDomain = IsLocalDomain(aHostname);
}
if (mNotifyResolution) {
NS_DispatchToMainThread(new NotifyDNSResolution(aHostname));
}
if (!res) {
return NS_ERROR_OFFLINE;
}
if ((type != RESOLVE_TYPE_DEFAULT) && (type != RESOLVE_TYPE_TXT) &&
(type != RESOLVE_TYPE_HTTPSSVC)) {
return NS_ERROR_INVALID_ARG;
}
if (DNSForbiddenByActiveProxy(aHostname, flags)) {
// nsHostResolver returns NS_ERROR_UNKNOWN_HOST for lots of reasons.
// We use a different error code to differentiate this failure and to make
// it clear(er) where this error comes from.
return NS_ERROR_UNKNOWN_PROXY_HOST;
}
nsCString hostname;
nsresult rv = PreprocessHostname(localDomain, aHostname, hostname);
if (NS_FAILED(rv)) {
return rv;
}
if (GetOffline() &&
(!mOfflineLocalhost || !hostname.LowerCaseEqualsASCII("localhost"))) {
flags |= RESOLVE_OFFLINE;
}
// make sure JS callers get notification on the main thread
nsCOMPtr<nsIXPConnectWrappedJS> wrappedListener = do_QueryInterface(listener);
if (wrappedListener && !target) {
target = GetMainThreadSerialEventTarget();
}
if (target) {
listener = new DNSListenerProxy(listener, target);
}
uint16_t af =
(type != RESOLVE_TYPE_DEFAULT) ? 0 : GetAFForLookup(hostname, flags);
MOZ_ASSERT(listener);
RefPtr<nsDNSAsyncRequest> req =
new nsDNSAsyncRequest(res, hostname, DNSAdditionalInfo::URL(aInfo), type,
aOriginAttributes, listener, flags, af);
if (!req) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (type == RESOLVE_TYPE_HTTPSSVC && mHasMockHTTPSRRDomainSet) {
MutexAutoLock lock(mLock);
if (req->mHost == mMockHTTPSRRDomain) {
flags |= nsIDNSService::RESOLVE_CREATE_MOCK_HTTPS_RR;
}
}
rv = res->ResolveHost(req->mHost, DNSAdditionalInfo::URL(aInfo),
DNSAdditionalInfo::Port(aInfo), type,
req->mOriginAttributes, flags, af, req);
req.forget(result);
return rv;
}
nsresult nsDNSService::CancelAsyncResolveInternal(
const nsACString& aHostname, uint16_t aType, nsIDNSService::DNSFlags aFlags,
nsIDNSAdditionalInfo* aInfo, nsIDNSListener* aListener, nsresult aReason,
const OriginAttributes& aOriginAttributes) {
// grab reference to global host resolver and IDN service. beware
// simultaneous shutdown!!
RefPtr<nsHostResolver> res;
bool localDomain = false;
{
MutexAutoLock lock(mLock);
if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE)) {
return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
}
res = mResolver;
localDomain = IsLocalDomain(aHostname);
}
if (!res) {
return NS_ERROR_OFFLINE;
}
nsCString hostname;
nsresult rv = PreprocessHostname(localDomain, aHostname, hostname);
if (NS_FAILED(rv)) {
return rv;
}
uint16_t af =
(aType != RESOLVE_TYPE_DEFAULT) ? 0 : GetAFForLookup(hostname, aFlags);
res->CancelAsyncRequest(hostname, DNSAdditionalInfo::URL(aInfo), aType,
aOriginAttributes, aFlags, af, aListener, aReason);
return NS_OK;
}
NS_IMETHODIMP
nsDNSService::AsyncResolve(const nsACString& aHostname,
nsIDNSService::ResolveType aType,
nsIDNSService::DNSFlags flags,
nsIDNSAdditionalInfo* aInfo,
nsIDNSListener* listener, nsIEventTarget* target_,
JS::Handle<JS::Value> aOriginAttributes,
JSContext* aCx, uint8_t aArgc,
nsICancelable** result) {
OriginAttributes attrs;
if (aArgc == 1) {
if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
return NS_ERROR_INVALID_ARG;
}
}
return AsyncResolveInternal(aHostname, aType, flags, aInfo, listener, target_,
attrs, result);
}
NS_IMETHODIMP
nsDNSService::AsyncResolveNative(
const nsACString& aHostname, nsIDNSService::ResolveType aType,
nsIDNSService::DNSFlags flags, nsIDNSAdditionalInfo* aInfo,
nsIDNSListener* aListener, nsIEventTarget* target_,
const OriginAttributes& aOriginAttributes, nsICancelable** result) {
return AsyncResolveInternal(aHostname, aType, flags, aInfo, aListener,
target_, aOriginAttributes, result);
}
NS_IMETHODIMP
nsDNSService::NewAdditionalInfo(const nsACString& aTrrURL, int32_t aPort,
nsIDNSAdditionalInfo** aInfo) {
RefPtr<DNSAdditionalInfo> res = new DNSAdditionalInfo(aTrrURL, aPort);
res.forget(aInfo);
return NS_OK;
}
NS_IMETHODIMP
nsDNSService::CancelAsyncResolve(const nsACString& aHostname,
nsIDNSService::ResolveType aType,
nsIDNSService::DNSFlags aFlags,
nsIDNSAdditionalInfo* aInfo,
nsIDNSListener* aListener, nsresult aReason,
JS::Handle<JS::Value> aOriginAttributes,
JSContext* aCx, uint8_t aArgc) {
OriginAttributes attrs;
if (aArgc == 1) {
if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
return NS_ERROR_INVALID_ARG;
}
}
return CancelAsyncResolveInternal(aHostname, aType, aFlags, aInfo, aListener,
aReason, attrs);
}
NS_IMETHODIMP
nsDNSService::CancelAsyncResolveNative(
const nsACString& aHostname, nsIDNSService::ResolveType aType,
nsIDNSService::DNSFlags aFlags, nsIDNSAdditionalInfo* aInfo,
nsIDNSListener* aListener, nsresult aReason,
const OriginAttributes& aOriginAttributes) {
return CancelAsyncResolveInternal(aHostname, aType, aFlags, aInfo, aListener,
aReason, aOriginAttributes);
}
NS_IMETHODIMP
nsDNSService::Resolve(const nsACString& aHostname,
nsIDNSService::DNSFlags flags,
JS::Handle<JS::Value> aOriginAttributes, JSContext* aCx,
uint8_t aArgc, nsIDNSRecord** result) {
OriginAttributes attrs;
if (aArgc == 1) {
if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
return NS_ERROR_INVALID_ARG;
}
}
return ResolveNative(aHostname, flags, attrs, result);
}
NS_IMETHODIMP
nsDNSService::ResolveNative(const nsACString& aHostname,
nsIDNSService::DNSFlags flags,
const OriginAttributes& aOriginAttributes,
nsIDNSRecord** result) {
// Synchronous resolution is not available on the main thread.
if (NS_IsMainThread()) {
return NS_ERROR_NOT_AVAILABLE;
}
return ResolveInternal(aHostname, flags, aOriginAttributes, result);
}
nsresult nsDNSService::DeprecatedSyncResolve(
const nsACString& aHostname, nsIDNSService::DNSFlags flags,
const OriginAttributes& aOriginAttributes, nsIDNSRecord** result) {
return ResolveInternal(aHostname, flags, aOriginAttributes, result);
}
nsresult nsDNSService::ResolveInternal(
const nsACString& aHostname, nsIDNSService::DNSFlags flags,
const OriginAttributes& aOriginAttributes, nsIDNSRecord** result) {
// grab reference to global host resolver and IDN service. beware
// simultaneous shutdown!!
RefPtr<nsHostResolver> res;
bool localDomain = false;
{
MutexAutoLock lock(mLock);
res = mResolver;
localDomain = IsLocalDomain(aHostname);
}
if (mNotifyResolution) {
NS_DispatchToMainThread(new NotifyDNSResolution(aHostname));
}
NS_ENSURE_TRUE(res, NS_ERROR_OFFLINE);
nsCString hostname;
nsresult rv = PreprocessHostname(localDomain, aHostname, hostname);
if (NS_FAILED(rv)) {
return rv;
}
if (GetOffline() &&
(!mOfflineLocalhost || !hostname.LowerCaseEqualsASCII("localhost"))) {
flags |= RESOLVE_OFFLINE;
}
if (DNSForbiddenByActiveProxy(aHostname, flags)) {
return NS_ERROR_UNKNOWN_PROXY_HOST;
}
//
// sync resolve: since the host resolver only works asynchronously, we need
// to use a mutex and a condvar to wait for the result. however, since the
// result may be in the resolvers cache, we might get called back recursively
// on the same thread. so, our mutex needs to be re-entrant. in other words,
// we need to use a monitor! ;-)
//
PRMonitor* mon = PR_NewMonitor();
if (!mon) {
return NS_ERROR_OUT_OF_MEMORY;
}
PR_EnterMonitor(mon);
RefPtr<nsDNSSyncRequest> syncReq = new nsDNSSyncRequest(mon);
uint16_t af = GetAFForLookup(hostname, flags);
// TRR uses the main thread for the HTTPS channel to the DoH server.
// If this were to block the main thread while waiting for TRR it would
// likely cause a deadlock. Instead we intentionally choose to not use TRR
// for this.
if (NS_IsMainThread()) {
flags |= RESOLVE_DISABLE_TRR;
}
rv = res->ResolveHost(hostname, ""_ns, -1, RESOLVE_TYPE_DEFAULT,
aOriginAttributes, flags, af, syncReq);
if (NS_SUCCEEDED(rv)) {
// wait for result
while (!syncReq->mDone) {
PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
}
if (NS_FAILED(syncReq->mStatus)) {
rv = syncReq->mStatus;
} else {
NS_ASSERTION(syncReq->mHostRecord, "no host record");
RefPtr<nsDNSRecord> rec = new nsDNSRecord(syncReq->mHostRecord);
rec.forget(result);
}
}
PR_ExitMonitor(mon);
PR_DestroyMonitor(mon);
return rv;
}
NS_IMETHODIMP
nsDNSService::GetMyHostName(nsACString& result) {
char name[100];
if (PR_GetSystemInfo(PR_SI_HOSTNAME, name, sizeof(name)) == PR_SUCCESS) {
result = name;
return NS_OK;
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsDNSService::Observe(nsISupports* subject, const char* topic,
const char16_t* data) {
bool flushCache = false;
RefPtr<nsHostResolver> resolver = GetResolverLocked();
if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
nsAutoCString converted = NS_ConvertUTF16toUTF8(data);
if (!strcmp(converted.get(), NS_NETWORK_LINK_DATA_CHANGED)) {
flushCache = true;
}
} else if (!strcmp(topic, "last-pb-context-exited")) {
flushCache = true;
} else if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
ReadPrefs(NS_ConvertUTF16toUTF8(data).get());
NS_ENSURE_TRUE(resolver, NS_ERROR_NOT_INITIALIZED);
if (mResolverPrefsUpdated && resolver) {
resolver->SetCacheLimits(mResCacheEntries, mResCacheExpiration,
mResCacheGrace);
}
} else if (!strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
Shutdown();
}
if (flushCache && resolver) {
resolver->FlushCache(false);
return NS_OK;
}
return NS_OK;
}
uint16_t nsDNSService::GetAFForLookup(const nsACString& host,
nsIDNSService::DNSFlags flags) {
if (StaticPrefs::network_dns_disableIPv6() ||
(flags & RESOLVE_DISABLE_IPV6)) {
return PR_AF_INET;
}
MutexAutoLock lock(mLock);
uint16_t af = PR_AF_UNSPEC;
if (!mIPv4OnlyDomains.IsEmpty()) {
const char *domain, *domainEnd, *end;
uint32_t hostLen, domainLen;
// see if host is in one of the IPv4-only domains
domain = mIPv4OnlyDomains.BeginReading();
domainEnd = mIPv4OnlyDomains.EndReading();
nsACString::const_iterator hostStart;
host.BeginReading(hostStart);
hostLen = host.Length();
do {
// skip any whitespace
while (*domain == ' ' || *domain == '\t') {
++domain;
}
// find end of this domain in the string
end = strchr(domain, ',');
if (!end) {
end = domainEnd;
}
// to see if the hostname is in the domain, check if the domain
// matches the end of the hostname.
domainLen = end - domain;
if (domainLen && hostLen >= domainLen) {
const char* hostTail = hostStart.get() + hostLen - domainLen;
if (nsCRT::strncasecmp(domain, hostTail, domainLen) == 0) {
// now, make sure either that the hostname is a direct match or
// that the hostname begins with a dot.
if (hostLen == domainLen || *hostTail == '.' ||
*(hostTail - 1) == '.') {
af = PR_AF_INET;
break;
}
}
}
domain = end + 1;
} while (*end);
}
if ((af != PR_AF_INET) && (flags & RESOLVE_DISABLE_IPV4)) {
af = PR_AF_INET6;
}
return af;
}
NS_IMETHODIMP
nsDNSService::GetDNSCacheEntries(
nsTArray<mozilla::net::DNSCacheEntries>* args) {
RefPtr<nsHostResolver> resolver = GetResolverLocked();
NS_ENSURE_TRUE(resolver, NS_ERROR_NOT_INITIALIZED);
resolver->GetDNSCacheEntries(args);
return NS_OK;
}
NS_IMETHODIMP
nsDNSService::ClearCache(bool aTrrToo) {
RefPtr<nsHostResolver> resolver = GetResolverLocked();
NS_ENSURE_TRUE(resolver, NS_ERROR_NOT_INITIALIZED);
resolver->FlushCache(aTrrToo);
return NS_OK;
}
// For testing purposes only
NS_IMETHODIMP
nsDNSService::ReloadParentalControlEnabled() {
if (mTrrService) {
mTrrService->mParentalControlEnabled =
TRRService::ReloadParentalControlsEnabled();
}
return NS_OK;
}
NS_IMETHODIMP
nsDNSService::SetDetectedTrrURI(const nsACString& aURI) {
if (mTrrService) {
mTrrService->SetDetectedTrrURI(aURI);
}
return NS_OK;
}
NS_IMETHODIMP
nsDNSService::SetHeuristicDetectionResult(nsITRRSkipReason::value aValue) {
if (mTrrService) {
mTrrService->SetHeuristicDetectionResult(aValue);
}
return NS_OK;
}
NS_IMETHODIMP
nsDNSService::GetHeuristicDetectionResult(nsITRRSkipReason::value* aValue) {
if (!mTrrService) {
return NS_ERROR_NOT_AVAILABLE;
}
*aValue = mTrrService->GetHeuristicDetectionResult();
return NS_OK;
}
NS_IMETHODIMP
nsDNSService::GetTRRSkipReasonName(nsITRRSkipReason::value aValue,
nsACString& aName) {
return mozilla::net::GetTRRSkipReasonName(aValue, aName);
}
NS_IMETHODIMP
nsDNSService::GetCurrentTrrURI(nsACString& aURI) {
if (mTrrService) {
mTrrService->GetURI(aURI);
}
return NS_OK;
}
NS_IMETHODIMP
nsDNSService::GetCurrentTrrMode(nsIDNSService::ResolverMode* aMode) {
*aMode = nsIDNSService::MODE_NATIVEONLY; // The default mode.
if (mTrrService) {
*aMode = mTrrService->Mode();
}
return NS_OK;
}
NS_IMETHODIMP
nsDNSService::GetCurrentTrrConfirmationState(uint32_t* aConfirmationState) {
*aConfirmationState = uint32_t(TRRService::CONFIRM_OFF);
if (mTrrService) {
*aConfirmationState = mTrrService->ConfirmationState();
}
return NS_OK;
}
NS_IMETHODIMP
nsDNSService::GetTrrDomain(nsACString& aTRRDomain) {
aTRRDomain.Truncate();
nsAutoCString url;
if (mTrrService) {
mTrrService->GetURI(url);
}
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), url);
if (NS_FAILED(rv)) {
// An empty TRR domain in case of invalid URL.
return NS_OK;
}
return uri->GetHost(aTRRDomain);
}
nsresult nsDNSService::GetTRRDomainKey(nsACString& aTRRDomain) {
aTRRDomain = TRRService::ProviderKey();
return NS_OK;
}
size_t nsDNSService::SizeOfIncludingThis(
mozilla::MallocSizeOf mallocSizeOf) const {
// Measurement of the following members may be added later if DMD finds it
// is worthwhile:
// - mIDN
// - mLock
size_t n = mallocSizeOf(this);
n += mResolver ? mResolver->SizeOfIncludingThis(mallocSizeOf) : 0;
n += mIPv4OnlyDomains.SizeOfExcludingThisIfUnshared(mallocSizeOf);
n += mLocalDomains.SizeOfExcludingThis(mallocSizeOf);
n += mFailedSVCDomainNames.ShallowSizeOfExcludingThis(mallocSizeOf);
for (const auto& data : mFailedSVCDomainNames.Values()) {
n += data->ShallowSizeOfExcludingThis(mallocSizeOf);
for (const auto& name : *data) {
n += name.SizeOfExcludingThisIfUnshared(mallocSizeOf);
}
}
return n;
}
MOZ_DEFINE_MALLOC_SIZE_OF(DNSServiceMallocSizeOf)
NS_IMETHODIMP
nsDNSService::CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize) {
MOZ_COLLECT_REPORT("explicit/network/dns-service", KIND_HEAP, UNITS_BYTES,
SizeOfIncludingThis(DNSServiceMallocSizeOf),
"Memory used for the DNS service.");
return NS_OK;
}
NS_IMETHODIMP
nsDNSService::ReportFailedSVCDomainName(const nsACString& aOwnerName,
const nsACString& aSVCDomainName) {
MutexAutoLock lock(mLock);
mFailedSVCDomainNames.GetOrInsertNew(aOwnerName, 1)
->AppendElement(aSVCDomainName);
return NS_OK;
}
NS_IMETHODIMP
nsDNSService::IsSVCDomainNameFailed(const nsACString& aOwnerName,
const nsACString& aSVCDomainName,
bool* aResult) {
NS_ENSURE_ARG(aResult);
MutexAutoLock lock(mLock);
*aResult = false;
nsTArray<nsCString>* failedList = mFailedSVCDomainNames.Get(aOwnerName);
if (!failedList) {
return NS_OK;
}
*aResult = failedList->Contains(aSVCDomainName);
return NS_OK;
}
NS_IMETHODIMP
nsDNSService::ResetExcludedSVCDomainName(const nsACString& aOwnerName) {
MutexAutoLock lock(mLock);
mFailedSVCDomainNames.Remove(aOwnerName);
return NS_OK;
}
NS_IMETHODIMP
nsDNSService::GetLastConfirmationStatus(nsresult* aConfirmationStatus) {
if (!mTrrService) {
return NS_ERROR_NOT_AVAILABLE;
}
*aConfirmationStatus = mTrrService->LastConfirmationStatus();
return NS_OK;
}
NS_IMETHODIMP nsDNSService::GetLastConfirmationSkipReason(
TRRSkippedReason* aSkipReason) {
if (!mTrrService) {
return NS_ERROR_NOT_AVAILABLE;
}
*aSkipReason = mTrrService->LastConfirmationSkipReason();
return NS_OK;
}
namespace mozilla::net {
nsresult GetTRRSkipReasonName(TRRSkippedReason aReason, nsACString& aName) {
static_assert(TRRSkippedReason::TRR_UNSET == 0);
static_assert(TRRSkippedReason::TRR_OK == 1);
static_assert(TRRSkippedReason::TRR_NO_GSERVICE == 2);
static_assert(TRRSkippedReason::TRR_PARENTAL_CONTROL == 3);
static_assert(TRRSkippedReason::TRR_OFF_EXPLICIT == 4);
static_assert(TRRSkippedReason::TRR_REQ_MODE_DISABLED == 5);
static_assert(TRRSkippedReason::TRR_MODE_NOT_ENABLED == 6);
static_assert(TRRSkippedReason::TRR_FAILED == 7);
static_assert(TRRSkippedReason::TRR_MODE_UNHANDLED_DEFAULT == 8);
static_assert(TRRSkippedReason::TRR_MODE_UNHANDLED_DISABLED == 9);
static_assert(TRRSkippedReason::TRR_DISABLED_FLAG == 10);
static_assert(TRRSkippedReason::TRR_TIMEOUT == 11);
static_assert(TRRSkippedReason::TRR_CHANNEL_DNS_FAIL == 12);
static_assert(TRRSkippedReason::TRR_BROWSER_IS_OFFLINE == 13);
static_assert(TRRSkippedReason::TRR_NOT_CONFIRMED == 14);
static_assert(TRRSkippedReason::TRR_DID_NOT_MAKE_QUERY == 15);
static_assert(TRRSkippedReason::TRR_UNKNOWN_CHANNEL_FAILURE == 16);
static_assert(TRRSkippedReason::TRR_HOST_BLOCKED_TEMPORARY == 17);
static_assert(TRRSkippedReason::TRR_SEND_FAILED == 18);
static_assert(TRRSkippedReason::TRR_NET_RESET == 19);
static_assert(TRRSkippedReason::TRR_NET_TIMEOUT == 20);
static_assert(TRRSkippedReason::TRR_NET_REFUSED == 21);
static_assert(TRRSkippedReason::TRR_NET_INTERRUPT == 22);
static_assert(TRRSkippedReason::TRR_NET_INADEQ_SEQURITY == 23);
static_assert(TRRSkippedReason::TRR_NO_ANSWERS == 24);
static_assert(TRRSkippedReason::TRR_DECODE_FAILED == 25);
static_assert(TRRSkippedReason::TRR_EXCLUDED == 26);
static_assert(TRRSkippedReason::TRR_SERVER_RESPONSE_ERR == 27);
static_assert(TRRSkippedReason::TRR_RCODE_FAIL == 28);
static_assert(TRRSkippedReason::TRR_NO_CONNECTIVITY == 29);
static_assert(TRRSkippedReason::TRR_NXDOMAIN == 30);
static_assert(TRRSkippedReason::TRR_REQ_CANCELLED == 31);
static_assert(TRRSkippedReason::ODOH_KEY_NOT_USABLE == 32);
static_assert(TRRSkippedReason::ODOH_UPDATE_KEY_FAILED == 33);
static_assert(TRRSkippedReason::ODOH_KEY_NOT_AVAILABLE == 34);
static_assert(TRRSkippedReason::ODOH_ENCRYPTION_FAILED == 35);
static_assert(TRRSkippedReason::ODOH_DECRYPTION_FAILED == 36);
static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_GOOGLE_SAFESEARCH ==
37);
static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_YOUTUBE_SAFESEARCH ==
38);
static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_ZSCALER_CANARY == 39);
static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_CANARY == 40);
static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_MODIFIED_ROOTS == 41);
static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_PARENTAL_CONTROLS ==
42);
static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_THIRD_PARTY_ROOTS ==
43);
static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_ENTERPRISE_POLICY ==
44);
static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_VPN == 45);
static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_PROXY == 46);
static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_NRPT == 47);
static_assert(TRRSkippedReason::TRR_BAD_URL == 48);
switch (aReason) {
case TRRSkippedReason::TRR_UNSET:
aName = "TRR_UNSET"_ns;
break;
case TRRSkippedReason::TRR_OK:
aName = "TRR_OK"_ns;
break;
case TRRSkippedReason::TRR_NO_GSERVICE:
aName = "TRR_NO_GSERVICE"_ns;
break;
case TRRSkippedReason::TRR_PARENTAL_CONTROL:
aName = "TRR_PARENTAL_CONTROL"_ns;
break;
case TRRSkippedReason::TRR_OFF_EXPLICIT:
aName = "TRR_OFF_EXPLICIT"_ns;
break;
case TRRSkippedReason::TRR_REQ_MODE_DISABLED:
aName = "TRR_REQ_MODE_DISABLED"_ns;
break;
case TRRSkippedReason::TRR_MODE_NOT_ENABLED:
aName = "TRR_MODE_NOT_ENABLED"_ns;
break;
case TRRSkippedReason::TRR_FAILED:
aName = "TRR_FAILED"_ns;
break;
case TRRSkippedReason::TRR_MODE_UNHANDLED_DEFAULT:
aName = "TRR_MODE_UNHANDLED_DEFAULT"_ns;
break;
case TRRSkippedReason::TRR_MODE_UNHANDLED_DISABLED:
aName = "TRR_MODE_UNHANDLED_DISABLED"_ns;
break;
case TRRSkippedReason::TRR_DISABLED_FLAG:
aName = "TRR_DISABLED_FLAG"_ns;
break;
case TRRSkippedReason::TRR_TIMEOUT:
aName = "TRR_TIMEOUT"_ns;
break;
case TRRSkippedReason::TRR_CHANNEL_DNS_FAIL:
aName = "TRR_CHANNEL_DNS_FAIL"_ns;
break;
case TRRSkippedReason::TRR_BROWSER_IS_OFFLINE:
aName = "TRR_BROWSER_IS_OFFLINE"_ns;
break;
case TRRSkippedReason::TRR_NOT_CONFIRMED:
aName = "TRR_NOT_CONFIRMED"_ns;
break;
case TRRSkippedReason::TRR_DID_NOT_MAKE_QUERY:
aName = "TRR_DID_NOT_MAKE_QUERY"_ns;
break;
case TRRSkippedReason::TRR_UNKNOWN_CHANNEL_FAILURE:
aName = "TRR_UNKNOWN_CHANNEL_FAILURE"_ns;
break;
case TRRSkippedReason::TRR_HOST_BLOCKED_TEMPORARY:
aName = "TRR_HOST_BLOCKED_TEMPORARY"_ns;
break;
case TRRSkippedReason::TRR_SEND_FAILED:
aName = "TRR_SEND_FAILED"_ns;
break;
case TRRSkippedReason::TRR_NET_RESET:
aName = "TRR_NET_RESET"_ns;
break;
case TRRSkippedReason::TRR_NET_TIMEOUT:
aName = "TRR_NET_TIMEOUT"_ns;
break;
case TRRSkippedReason::TRR_NET_REFUSED:
aName = "TRR_NET_REFUSED"_ns;
break;