summaryrefslogtreecommitdiffstats
path: root/netwerk/dns
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /netwerk/dns
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--netwerk/dns/ChildDNSService.cpp514
-rw-r--r--netwerk/dns/ChildDNSService.h78
-rw-r--r--netwerk/dns/DNS.cpp445
-rw-r--r--netwerk/dns/DNS.h260
-rw-r--r--netwerk/dns/DNSAdditionalInfo.cpp25
-rw-r--r--netwerk/dns/DNSAdditionalInfo.h44
-rw-r--r--netwerk/dns/DNSByTypeRecord.h259
-rw-r--r--netwerk/dns/DNSListenerProxy.cpp37
-rw-r--r--netwerk/dns/DNSListenerProxy.h62
-rw-r--r--netwerk/dns/DNSLogging.h26
-rw-r--r--netwerk/dns/DNSPacket.cpp1100
-rw-r--r--netwerk/dns/DNSPacket.h105
-rw-r--r--netwerk/dns/DNSRequestBase.h152
-rw-r--r--netwerk/dns/DNSRequestChild.cpp574
-rw-r--r--netwerk/dns/DNSRequestChild.h42
-rw-r--r--netwerk/dns/DNSRequestParent.cpp183
-rw-r--r--netwerk/dns/DNSRequestParent.h45
-rw-r--r--netwerk/dns/DNSServiceBase.cpp64
-rw-r--r--netwerk/dns/DNSServiceBase.h36
-rw-r--r--netwerk/dns/DNSUtils.cpp81
-rw-r--r--netwerk/dns/DNSUtils.h32
-rw-r--r--netwerk/dns/GetAddrInfo.cpp584
-rw-r--r--netwerk/dns/GetAddrInfo.h111
-rw-r--r--netwerk/dns/HTTPSSVC.cpp523
-rw-r--r--netwerk/dns/HTTPSSVC.h164
-rw-r--r--netwerk/dns/HostRecordQueue.cpp223
-rw-r--r--netwerk/dns/HostRecordQueue.h72
-rw-r--r--netwerk/dns/IDNBlocklistUtils.cpp86
-rw-r--r--netwerk/dns/IDNBlocklistUtils.h65
-rw-r--r--netwerk/dns/IDNCharacterBlocklist.inc63
-rw-r--r--netwerk/dns/NativeDNSResolverOverrideChild.cpp49
-rw-r--r--netwerk/dns/NativeDNSResolverOverrideChild.h40
-rw-r--r--netwerk/dns/NativeDNSResolverOverrideParent.cpp111
-rw-r--r--netwerk/dns/NativeDNSResolverOverrideParent.h32
-rw-r--r--netwerk/dns/PDNSRequest.ipdl40
-rw-r--r--netwerk/dns/PDNSRequestParams.ipdlh39
-rw-r--r--netwerk/dns/PNativeDNSResolverOverride.ipdl26
-rw-r--r--netwerk/dns/PTRRService.ipdl35
-rw-r--r--netwerk/dns/PlatformDNSAndroid.cpp154
-rw-r--r--netwerk/dns/PlatformDNSUnix.cpp63
-rw-r--r--netwerk/dns/PlatformDNSWin.cpp140
-rw-r--r--netwerk/dns/PublicSuffixList.sys.mjs104
-rw-r--r--netwerk/dns/TRR.cpp1139
-rw-r--r--netwerk/dns/TRR.h159
-rw-r--r--netwerk/dns/TRRQuery.cpp387
-rw-r--r--netwerk/dns/TRRQuery.h111
-rw-r--r--netwerk/dns/TRRService.cpp1411
-rw-r--r--netwerk/dns/TRRService.h384
-rw-r--r--netwerk/dns/TRRServiceBase.cpp399
-rw-r--r--netwerk/dns/TRRServiceBase.h90
-rw-r--r--netwerk/dns/TRRServiceChild.cpp121
-rw-r--r--netwerk/dns/TRRServiceChild.h53
-rw-r--r--netwerk/dns/TRRServiceParent.cpp239
-rw-r--r--netwerk/dns/TRRServiceParent.h52
-rw-r--r--netwerk/dns/effective_tld_names.dat15582
-rw-r--r--netwerk/dns/moz.build122
-rw-r--r--netwerk/dns/nsDNSService2.cpp1792
-rw-r--r--netwerk/dns/nsDNSService2.h135
-rw-r--r--netwerk/dns/nsEffectiveTLDService.cpp518
-rw-r--r--netwerk/dns/nsEffectiveTLDService.h94
-rw-r--r--netwerk/dns/nsHostRecord.cpp598
-rw-r--r--netwerk/dns/nsHostRecord.h416
-rw-r--r--netwerk/dns/nsHostResolver.cpp1965
-rw-r--r--netwerk/dns/nsHostResolver.h344
-rw-r--r--netwerk/dns/nsIDNKitInterface.h195
-rw-r--r--netwerk/dns/nsIDNSAdditionalInfo.idl12
-rw-r--r--netwerk/dns/nsIDNSByTypeRecord.idl133
-rw-r--r--netwerk/dns/nsIDNSListener.idl34
-rw-r--r--netwerk/dns/nsIDNSRecord.idl154
-rw-r--r--netwerk/dns/nsIDNSService.idl383
-rw-r--r--netwerk/dns/nsIDNService.cpp855
-rw-r--r--netwerk/dns/nsIDNService.h191
-rw-r--r--netwerk/dns/nsIEffectiveTLDService.idl205
-rw-r--r--netwerk/dns/nsIIDNService.idl58
-rw-r--r--netwerk/dns/nsINativeDNSResolverOverride.idl37
-rw-r--r--netwerk/dns/nsITRRSkipReason.idl121
-rw-r--r--netwerk/dns/nsPIDNSService.idl34
-rw-r--r--netwerk/dns/prepare_tlds.py150
-rw-r--r--netwerk/dns/punycode.c325
-rw-r--r--netwerk/dns/punycode.h106
-rw-r--r--netwerk/dns/tests/moz.build7
-rw-r--r--netwerk/dns/tests/unit/data/fake_public_suffix_list.dat57
-rw-r--r--netwerk/dns/tests/unit/data/moz.build14
-rw-r--r--netwerk/dns/tests/unit/moz.build7
-rw-r--r--netwerk/dns/tests/unit/test_PublicSuffixList.js174
-rw-r--r--netwerk/dns/tests/unit/test_nsEffectiveTLDService_Reload_DAFSA.js32
-rw-r--r--netwerk/dns/tests/unit/test_nsEffectiveTLDService_getKnownPublicSuffix.js44
-rw-r--r--netwerk/dns/tests/unit/test_nsEffectiveTLDService_getSite.js77
-rw-r--r--netwerk/dns/tests/unit/xpcshell.toml17
89 files changed, 36421 insertions, 0 deletions
diff --git a/netwerk/dns/ChildDNSService.cpp b/netwerk/dns/ChildDNSService.cpp
new file mode 100644
index 0000000000..653608ab01
--- /dev/null
+++ b/netwerk/dns/ChildDNSService.cpp
@@ -0,0 +1,514 @@
+/* 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 "mozilla/net/ChildDNSService.h"
+#include "nsDNSPrefetch.h"
+#include "nsIDNSListener.h"
+#include "nsIOService.h"
+#include "nsThreadUtils.h"
+#include "nsIXPConnect.h"
+#include "nsIProtocolProxyService.h"
+#include "nsNetCID.h"
+#include "nsQueryObject.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/StaticPrefs_network.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/SyncRunnable.h"
+#include "mozilla/net/NeckoChild.h"
+#include "mozilla/net/DNSListenerProxy.h"
+#include "mozilla/net/TRRServiceParent.h"
+#include "nsHostResolver.h"
+#include "nsServiceManagerUtils.h"
+#include "prsystem.h"
+#include "DNSAdditionalInfo.h"
+#include "TRRService.h"
+
+namespace mozilla {
+namespace net {
+
+//-----------------------------------------------------------------------------
+// ChildDNSService
+//-----------------------------------------------------------------------------
+
+static StaticRefPtr<ChildDNSService> gChildDNSService;
+
+already_AddRefed<ChildDNSService> ChildDNSService::GetSingleton() {
+ MOZ_ASSERT_IF(nsIOService::UseSocketProcess(),
+ XRE_IsContentProcess() || XRE_IsParentProcess());
+ MOZ_ASSERT_IF(!nsIOService::UseSocketProcess(),
+ XRE_IsContentProcess() || XRE_IsSocketProcess());
+
+ if (!gChildDNSService) {
+ if (NS_WARN_IF(!NS_IsMainThread())) {
+ return nullptr;
+ }
+ gChildDNSService = new ChildDNSService();
+ gChildDNSService->Init();
+ ClearOnShutdown(&gChildDNSService);
+ }
+
+ return do_AddRef(gChildDNSService);
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(ChildDNSService, DNSServiceBase, nsIDNSService,
+ nsPIDNSService)
+
+ChildDNSService::ChildDNSService() {
+ MOZ_ASSERT_IF(nsIOService::UseSocketProcess(),
+ XRE_IsContentProcess() || XRE_IsParentProcess());
+ MOZ_ASSERT_IF(!nsIOService::UseSocketProcess(),
+ XRE_IsContentProcess() || XRE_IsSocketProcess());
+ if (XRE_IsParentProcess() && nsIOService::UseSocketProcess()) {
+ nsDNSPrefetch::Initialize(this);
+ mTRRServiceParent = new TRRServiceParent();
+ mTRRServiceParent->Init();
+ }
+}
+
+void ChildDNSService::GetDNSRecordHashKey(
+ const nsACString& aHost, const nsACString& aTrrServer, int32_t aPort,
+ uint16_t aType, const OriginAttributes& aOriginAttributes,
+ nsIDNSService::DNSFlags aFlags, uintptr_t aListenerAddr,
+ nsACString& aHashKey) {
+ aHashKey.Assign(aHost);
+ aHashKey.Assign(aTrrServer);
+ aHashKey.AppendInt(aPort);
+ aHashKey.AppendInt(aType);
+
+ nsAutoCString originSuffix;
+ aOriginAttributes.CreateSuffix(originSuffix);
+ aHashKey.Append(originSuffix);
+
+ aHashKey.AppendInt(aFlags);
+ aHashKey.AppendPrintf("0x%" PRIxPTR, aListenerAddr);
+}
+
+nsresult ChildDNSService::AsyncResolveInternal(
+ const nsACString& hostname, uint16_t type, nsIDNSService::DNSFlags flags,
+ nsIDNSAdditionalInfo* aInfo, nsIDNSListener* listener,
+ nsIEventTarget* target_, const OriginAttributes& aOriginAttributes,
+ nsICancelable** result) {
+ if (XRE_IsContentProcess()) {
+ NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE);
+ }
+
+ if (DNSForbiddenByActiveProxy(hostname, 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;
+ }
+
+ bool resolveDNSInSocketProcess = false;
+ if (XRE_IsParentProcess() && nsIOService::UseSocketProcess()) {
+ resolveDNSInSocketProcess = true;
+ if (type != nsIDNSService::RESOLVE_TYPE_DEFAULT &&
+ (mTRRServiceParent->Mode() != nsIDNSService::MODE_TRRFIRST &&
+ mTRRServiceParent->Mode() != nsIDNSService::MODE_TRRONLY) &&
+ !StaticPrefs::network_dns_native_https_query()) {
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+ }
+
+ if (mDisablePrefetch && (flags & RESOLVE_SPECULATE)) {
+ return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
+ }
+
+ // We need original listener for the pending requests hash.
+ uintptr_t originalListenerAddr = reinterpret_cast<uintptr_t>(listener);
+
+ // make sure JS callers get notification on the main thread
+ nsCOMPtr<nsIEventTarget> target = target_;
+ nsCOMPtr<nsIXPConnectWrappedJS> wrappedListener = do_QueryInterface(listener);
+ if (wrappedListener && !target) {
+ target = GetMainThreadSerialEventTarget();
+ }
+ if (target) {
+ // Guarantee listener freed on main thread. Not sure we need this in child
+ // (or in parent in nsDNSService.cpp) but doesn't hurt.
+ listener = new DNSListenerProxy(listener, target);
+ }
+
+ RefPtr<DNSRequestSender> sender = new DNSRequestSender(
+ hostname, DNSAdditionalInfo::URL(aInfo), DNSAdditionalInfo::Port(aInfo),
+ type, aOriginAttributes, flags, listener, target);
+ RefPtr<DNSRequestActor> dnsReq;
+ if (resolveDNSInSocketProcess) {
+ dnsReq = new DNSRequestParent(sender);
+ if (!mTRRServiceParent->TRRConnectionInfoInited()) {
+ mTRRServiceParent->InitTRRConnectionInfo();
+ }
+ } else {
+ dnsReq = new DNSRequestChild(sender);
+ }
+
+ {
+ MutexAutoLock lock(mPendingRequestsLock);
+ nsCString key;
+ GetDNSRecordHashKey(hostname, DNSAdditionalInfo::URL(aInfo),
+ DNSAdditionalInfo::Port(aInfo), type, aOriginAttributes,
+ flags, originalListenerAddr, key);
+ mPendingRequests.GetOrInsertNew(key)->AppendElement(sender);
+ }
+
+ sender->StartRequest();
+
+ sender.forget(result);
+ return NS_OK;
+}
+
+nsresult ChildDNSService::CancelAsyncResolveInternal(
+ const nsACString& aHostname, uint16_t aType, nsIDNSService::DNSFlags aFlags,
+ nsIDNSAdditionalInfo* aInfo, nsIDNSListener* aListener, nsresult aReason,
+ const OriginAttributes& aOriginAttributes) {
+ if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE)) {
+ return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
+ }
+
+ MutexAutoLock lock(mPendingRequestsLock);
+ nsTArray<RefPtr<DNSRequestSender>>* hashEntry;
+ nsCString key;
+ uintptr_t listenerAddr = reinterpret_cast<uintptr_t>(aListener);
+ GetDNSRecordHashKey(aHostname, DNSAdditionalInfo::URL(aInfo),
+ DNSAdditionalInfo::Port(aInfo), aType, aOriginAttributes,
+ aFlags, listenerAddr, key);
+ if (mPendingRequests.Get(key, &hashEntry)) {
+ // We cancel just one.
+ hashEntry->ElementAt(0)->Cancel(aReason);
+ }
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// ChildDNSService::nsIDNSService
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+ChildDNSService::AsyncResolve(const nsACString& hostname,
+ 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(hostname, aType, flags, aInfo, listener, target_,
+ attrs, result);
+}
+
+NS_IMETHODIMP
+ChildDNSService::AsyncResolveNative(
+ const nsACString& hostname, nsIDNSService::ResolveType aType,
+ nsIDNSService::DNSFlags flags, nsIDNSAdditionalInfo* aInfo,
+ nsIDNSListener* listener, nsIEventTarget* target_,
+ const OriginAttributes& aOriginAttributes, nsICancelable** result) {
+ return AsyncResolveInternal(hostname, aType, flags, aInfo, listener, target_,
+ aOriginAttributes, result);
+}
+
+NS_IMETHODIMP
+ChildDNSService::NewAdditionalInfo(const nsACString& aTrrURL, int32_t aPort,
+ nsIDNSAdditionalInfo** aInfo) {
+ RefPtr<DNSAdditionalInfo> res = new DNSAdditionalInfo(aTrrURL, aPort);
+ res.forget(aInfo);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSService::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
+ChildDNSService::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
+ChildDNSService::Resolve(const nsACString& hostname,
+ nsIDNSService::DNSFlags flags,
+ JS::Handle<JS::Value> aOriginAttributes,
+ JSContext* aCx, uint8_t aArgc, nsIDNSRecord** result) {
+ // not planning to ever support this, since sync IPDL is evil.
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+ChildDNSService::ResolveNative(const nsACString& hostname,
+ nsIDNSService::DNSFlags flags,
+ const OriginAttributes& aOriginAttributes,
+ nsIDNSRecord** result) {
+ // not planning to ever support this, since sync IPDL is evil.
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+ChildDNSService::GetDNSCacheEntries(
+ nsTArray<mozilla::net::DNSCacheEntries>* args) {
+ // Only used by networking dashboard, so may not ever need this in child.
+ // (and would provide a way to spy on what hosts other apps are connecting to,
+ // unless we start keeping per-app DNS caches).
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+ChildDNSService::ClearCache(bool aTrrToo) {
+ if (!mTRRServiceParent || !mTRRServiceParent->CanSend()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ Unused << mTRRServiceParent->SendClearDNSCache(aTrrToo);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSService::ReloadParentalControlEnabled() {
+ if (!mTRRServiceParent) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ mTRRServiceParent->UpdateParentalControlEnabled();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSService::SetDetectedTrrURI(const nsACString& aURI) {
+ if (!mTRRServiceParent) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ mTRRServiceParent->SetDetectedTrrURI(aURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSService::SetHeuristicDetectionResult(nsITRRSkipReason::value aValue) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+ChildDNSService::GetHeuristicDetectionResult(nsITRRSkipReason::value* aValue) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+ChildDNSService::GetTRRSkipReasonName(nsITRRSkipReason::value aValue,
+ nsACString& aName) {
+ return mozilla::net::GetTRRSkipReasonName(aValue, aName);
+}
+
+NS_IMETHODIMP
+ChildDNSService::GetCurrentTrrURI(nsACString& aURI) {
+ if (!mTRRServiceParent) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ mTRRServiceParent->GetURI(aURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSService::GetCurrentTrrMode(nsIDNSService::ResolverMode* aMode) {
+ if (XRE_IsContentProcess()) {
+ *aMode = mTRRMode;
+ return NS_OK;
+ }
+ if (!mTRRServiceParent) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ *aMode = mTRRServiceParent->Mode();
+ return NS_OK;
+}
+
+void ChildDNSService::SetTRRModeInChild(
+ nsIDNSService::ResolverMode mode,
+ nsIDNSService::ResolverMode modeFromPref) {
+ if (!XRE_IsContentProcess()) {
+ MOZ_ASSERT(false, "Why are we calling this?");
+ return;
+ }
+ mTRRMode = mode;
+ TRRService::SetCurrentTRRMode(modeFromPref);
+}
+
+NS_IMETHODIMP
+ChildDNSService::GetCurrentTrrConfirmationState(uint32_t* aConfirmationState) {
+ if (!mTRRServiceParent) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ *aConfirmationState = mTRRServiceParent->GetConfirmationState();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSService::GetMyHostName(nsACString& result) {
+ if (XRE_IsParentProcess()) {
+ char name[100];
+ if (PR_GetSystemInfo(PR_SI_HOSTNAME, name, sizeof(name)) == PR_SUCCESS) {
+ result = name;
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+ }
+ // TODO: get value from parent during PNecko construction?
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+void ChildDNSService::NotifyRequestDone(DNSRequestSender* aDnsRequest) {
+ // We need the original flags and listener for the pending requests hash.
+ nsIDNSService::DNSFlags originalFlags =
+ aDnsRequest->mFlags & ~RESOLVE_OFFLINE;
+ uintptr_t originalListenerAddr =
+ reinterpret_cast<uintptr_t>(aDnsRequest->mListener.get());
+ RefPtr<DNSListenerProxy> wrapper = do_QueryObject(aDnsRequest->mListener);
+ if (wrapper) {
+ originalListenerAddr = wrapper->GetOriginalListenerAddress();
+ }
+
+ MutexAutoLock lock(mPendingRequestsLock);
+
+ nsCString key;
+ GetDNSRecordHashKey(aDnsRequest->mHost, aDnsRequest->mTrrServer,
+ aDnsRequest->mPort, aDnsRequest->mType,
+ aDnsRequest->mOriginAttributes, originalFlags,
+ originalListenerAddr, key);
+
+ nsTArray<RefPtr<DNSRequestSender>>* hashEntry;
+
+ if (mPendingRequests.Get(key, &hashEntry)) {
+ auto idx = hashEntry->IndexOf(aDnsRequest);
+ if (idx != nsTArray<RefPtr<DNSRequestSender>>::NoIndex) {
+ hashEntry->RemoveElementAt(idx);
+ if (hashEntry->IsEmpty()) {
+ mPendingRequests.Remove(key);
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// ChildDNSService::nsPIDNSService
+//-----------------------------------------------------------------------------
+
+nsresult ChildDNSService::Init() {
+ ReadPrefs(nullptr);
+
+ nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+ if (prefs) {
+ AddPrefObserver(prefs);
+ }
+
+ return NS_OK;
+}
+
+nsresult ChildDNSService::Shutdown() { return NS_OK; }
+
+NS_IMETHODIMP
+ChildDNSService::GetPrefetchEnabled(bool* outVal) {
+ *outVal = !mDisablePrefetch;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSService::SetPrefetchEnabled(bool inVal) {
+ mDisablePrefetch = !inVal;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSService::ReportFailedSVCDomainName(const nsACString& aOwnerName,
+ const nsACString& aSVCDomainName) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+ChildDNSService::IsSVCDomainNameFailed(const nsACString& aOwnerName,
+ const nsACString& aSVCDomainName,
+ bool* aResult) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+ChildDNSService::ResetExcludedSVCDomainName(const nsACString& aOwnerName) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+//-----------------------------------------------------------------------------
+// ChildDNSService::nsIObserver
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+ChildDNSService::Observe(nsISupports* subject, const char* topic,
+ const char16_t* data) {
+ if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
+ // Reread prefs
+ ReadPrefs(NS_ConvertUTF16toUTF8(data).get());
+ }
+ return NS_OK;
+}
+
+void ChildDNSService::SetTRRDomain(const nsACString& aTRRDomain) {
+ mTRRDomain = aTRRDomain;
+ TRRService::SetProviderDomain(aTRRDomain);
+}
+
+nsresult ChildDNSService::GetTRRDomainKey(nsACString& aTRRDomain) {
+ aTRRDomain = TRRService::ProviderKey();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSService::GetTrrDomain(nsACString& aTRRDomain) {
+ aTRRDomain = mTRRDomain;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSService::GetLastConfirmationStatus(nsresult* aConfirmationStatus) {
+ // XXX(valentin): Fix for socket process
+ *aConfirmationStatus = NS_OK;
+ return NS_OK;
+}
+
+NS_IMETHODIMP ChildDNSService::GetLastConfirmationSkipReason(
+ TRRSkippedReason* aSkipReason) {
+ // XXX(valentin): Fix for socket process
+ *aSkipReason = nsITRRSkipReason::TRR_UNSET;
+ return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/ChildDNSService.h b/netwerk/dns/ChildDNSService.h
new file mode 100644
index 0000000000..611f8b871f
--- /dev/null
+++ b/netwerk/dns/ChildDNSService.h
@@ -0,0 +1,78 @@
+/* -*- 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/. */
+
+#ifndef mozilla_net_ChildDNSService_h
+#define mozilla_net_ChildDNSService_h
+
+#include "DNSServiceBase.h"
+#include "nsPIDNSService.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Mutex.h"
+#include "DNSRequestChild.h"
+#include "DNSRequestParent.h"
+#include "nsHashKeys.h"
+#include "nsClassHashtable.h"
+
+namespace mozilla {
+namespace net {
+
+class TRRServiceParent;
+
+class ChildDNSService final : public DNSServiceBase, public nsPIDNSService {
+ public:
+ // AsyncResolve (and CancelAsyncResolve) can be called off-main
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSPIDNSSERVICE
+ NS_DECL_NSIDNSSERVICE
+ NS_DECL_NSIOBSERVER
+
+ ChildDNSService();
+
+ static already_AddRefed<ChildDNSService> GetSingleton();
+
+ void NotifyRequestDone(DNSRequestSender* aDnsRequest);
+
+ void SetTRRDomain(const nsACString& aTRRDomain);
+ void SetTRRModeInChild(nsIDNSService::ResolverMode mode,
+ nsIDNSService::ResolverMode modeFromPref);
+
+ private:
+ virtual ~ChildDNSService() = default;
+
+ void MOZ_ALWAYS_INLINE GetDNSRecordHashKey(
+ const nsACString& aHost, const nsACString& aTrrServer, int32_t aPort,
+ uint16_t aType, const OriginAttributes& aOriginAttributes,
+ nsIDNSService::DNSFlags aFlags, uintptr_t aListenerAddr,
+ nsACString& aHashKey);
+ nsresult AsyncResolveInternal(const nsACString& hostname, uint16_t type,
+ nsIDNSService::DNSFlags flags,
+ nsIDNSAdditionalInfo* aInfo,
+ nsIDNSListener* listener,
+ nsIEventTarget* target_,
+ const OriginAttributes& aOriginAttributes,
+ nsICancelable** result);
+ nsresult CancelAsyncResolveInternal(
+ const nsACString& aHostname, uint16_t aType,
+ nsIDNSService::DNSFlags aFlags, nsIDNSAdditionalInfo* aInfo,
+ nsIDNSListener* aListener, nsresult aReason,
+ const OriginAttributes& aOriginAttributes);
+
+ // We need to remember pending dns requests to be able to cancel them.
+ nsClassHashtable<nsCStringHashKey, nsTArray<RefPtr<DNSRequestSender>>>
+ mPendingRequests;
+ Mutex mPendingRequestsLock MOZ_UNANNOTATED{"DNSPendingRequestsLock"};
+ RefPtr<TRRServiceParent> mTRRServiceParent;
+
+ nsCString mTRRDomain;
+ // Only set in the content process.
+ nsIDNSService::ResolverMode mTRRMode =
+ nsIDNSService::ResolverMode::MODE_NATIVEONLY;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_ChildDNSService_h
diff --git a/netwerk/dns/DNS.cpp b/netwerk/dns/DNS.cpp
new file mode 100644
index 0000000000..91730989db
--- /dev/null
+++ b/netwerk/dns/DNS.cpp
@@ -0,0 +1,445 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=4 sw=2 sts=2 et cin: */
+/* 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 "mozilla/net/DNS.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/mozalloc.h"
+#include "mozilla/StaticPrefs_network.h"
+#include "nsContentUtils.h"
+#include "nsString.h"
+#include <string.h>
+
+#ifdef XP_WIN
+# include "ws2tcpip.h"
+#endif
+
+namespace mozilla {
+namespace net {
+
+const char* inet_ntop_internal(int af, const void* src, char* dst,
+ socklen_t size) {
+#ifdef XP_WIN
+ if (af == AF_INET) {
+ struct sockaddr_in s;
+ memset(&s, 0, sizeof(s));
+ s.sin_family = AF_INET;
+ memcpy(&s.sin_addr, src, sizeof(struct in_addr));
+ int result = getnameinfo((struct sockaddr*)&s, sizeof(struct sockaddr_in),
+ dst, size, nullptr, 0, NI_NUMERICHOST);
+ if (result == 0) {
+ return dst;
+ }
+ } else if (af == AF_INET6) {
+ struct sockaddr_in6 s;
+ memset(&s, 0, sizeof(s));
+ s.sin6_family = AF_INET6;
+ memcpy(&s.sin6_addr, src, sizeof(struct in_addr6));
+ int result = getnameinfo((struct sockaddr*)&s, sizeof(struct sockaddr_in6),
+ dst, size, nullptr, 0, NI_NUMERICHOST);
+ if (result == 0) {
+ return dst;
+ }
+ }
+ return nullptr;
+#else
+ return inet_ntop(af, src, dst, size);
+#endif
+}
+
+// Copies the contents of a PRNetAddr to a NetAddr.
+// Does not do a ptr safety check!
+void PRNetAddrToNetAddr(const PRNetAddr* prAddr, NetAddr* addr) {
+ if (prAddr->raw.family == PR_AF_INET) {
+ addr->inet.family = AF_INET;
+ addr->inet.port = prAddr->inet.port;
+ addr->inet.ip = prAddr->inet.ip;
+ } else if (prAddr->raw.family == PR_AF_INET6) {
+ addr->inet6.family = AF_INET6;
+ addr->inet6.port = prAddr->ipv6.port;
+ addr->inet6.flowinfo = prAddr->ipv6.flowinfo;
+ memcpy(&addr->inet6.ip, &prAddr->ipv6.ip, sizeof(addr->inet6.ip.u8));
+ addr->inet6.scope_id = prAddr->ipv6.scope_id;
+ }
+#if defined(XP_UNIX)
+ else if (prAddr->raw.family == PR_AF_LOCAL) {
+ addr->local.family = AF_LOCAL;
+ memcpy(addr->local.path, prAddr->local.path, sizeof(addr->local.path));
+ }
+#endif
+}
+
+extern "C" {
+// Rust bindings
+
+uint16_t moz_netaddr_get_family(const NetAddr* addr) {
+ return addr->raw.family;
+}
+
+uint32_t moz_netaddr_get_network_order_ip(const NetAddr* addr) {
+ return addr->inet.ip;
+}
+
+uint8_t const* moz_netaddr_get_ipv6(const NetAddr* addr) {
+ return addr->inet6.ip.u8;
+}
+
+uint16_t moz_netaddr_get_network_order_port(const NetAddr* addr) {
+ if (addr->raw.family == PR_AF_INET) {
+ return addr->inet.port;
+ }
+ if (addr->raw.family == PR_AF_INET6) {
+ return addr->inet6.port;
+ }
+ return 0;
+}
+
+} // extern "C"
+
+// Copies the contents of a NetAddr to a PRNetAddr.
+// Does not do a ptr safety check!
+void NetAddrToPRNetAddr(const NetAddr* addr, PRNetAddr* prAddr) {
+ if (addr->raw.family == AF_INET) {
+ prAddr->inet.family = PR_AF_INET;
+ prAddr->inet.port = addr->inet.port;
+ prAddr->inet.ip = addr->inet.ip;
+ } else if (addr->raw.family == AF_INET6) {
+ prAddr->ipv6.family = PR_AF_INET6;
+ prAddr->ipv6.port = addr->inet6.port;
+ prAddr->ipv6.flowinfo = addr->inet6.flowinfo;
+ memcpy(&prAddr->ipv6.ip, &addr->inet6.ip, sizeof(addr->inet6.ip.u8));
+ prAddr->ipv6.scope_id = addr->inet6.scope_id;
+ }
+#if defined(XP_UNIX)
+ else if (addr->raw.family == AF_LOCAL) {
+ prAddr->local.family = PR_AF_LOCAL;
+ memcpy(prAddr->local.path, addr->local.path, sizeof(addr->local.path));
+ }
+#elif defined(XP_WIN)
+ else if (addr->raw.family == AF_LOCAL) {
+ prAddr->local.family = PR_AF_LOCAL;
+ memcpy(prAddr->local.path, addr->local.path, sizeof(addr->local.path));
+ }
+#endif
+}
+
+bool NetAddr::ToStringBuffer(char* buf, uint32_t bufSize) const {
+ const NetAddr* addr = this;
+ if (addr->raw.family == AF_INET) {
+ if (bufSize < INET_ADDRSTRLEN) {
+ return false;
+ }
+ struct in_addr nativeAddr = {};
+ nativeAddr.s_addr = addr->inet.ip;
+ return !!inet_ntop_internal(AF_INET, &nativeAddr, buf, bufSize);
+ }
+ if (addr->raw.family == AF_INET6) {
+ if (bufSize < INET6_ADDRSTRLEN) {
+ return false;
+ }
+ struct in6_addr nativeAddr = {};
+ memcpy(&nativeAddr.s6_addr, &addr->inet6.ip, sizeof(addr->inet6.ip.u8));
+ return !!inet_ntop_internal(AF_INET6, &nativeAddr, buf, bufSize);
+ }
+#if defined(XP_UNIX)
+ if (addr->raw.family == AF_LOCAL) {
+ if (bufSize < sizeof(addr->local.path)) {
+ // Many callers don't bother checking our return value, so
+ // null-terminate just in case.
+ if (bufSize > 0) {
+ buf[0] = '\0';
+ }
+ return false;
+ }
+
+ // Usually, the size passed to memcpy should be the size of the
+ // destination. Here, we know that the source is no larger than the
+ // destination, so using the source's size is always safe, whereas
+ // using the destination's size may cause us to read off the end of the
+ // source.
+ memcpy(buf, addr->local.path, sizeof(addr->local.path));
+ return true;
+ }
+#endif
+ return false;
+}
+
+nsCString NetAddr::ToString() const {
+ nsCString out;
+ out.SetLength(kNetAddrMaxCStrBufSize);
+ if (ToStringBuffer(out.BeginWriting(), kNetAddrMaxCStrBufSize)) {
+ out.SetLength(strlen(out.BeginWriting()));
+ return out;
+ }
+ return ""_ns;
+}
+
+bool NetAddr::IsLoopbackAddr() const {
+ if (IsLoopBackAddressWithoutIPv6Mapping()) {
+ return true;
+ }
+ const NetAddr* addr = this;
+ if (addr->raw.family != AF_INET6) {
+ return false;
+ }
+
+ return IPv6ADDR_IS_V4MAPPED(&addr->inet6.ip) &&
+ IPv6ADDR_V4MAPPED_TO_IPADDR(&addr->inet6.ip) == htonl(INADDR_LOOPBACK);
+}
+
+bool NetAddr::IsLoopBackAddressWithoutIPv6Mapping() const {
+ const NetAddr* addr = this;
+ if (addr->raw.family == AF_INET) {
+ // Consider 127.0.0.1/8 as loopback
+ uint32_t ipv4Addr = ntohl(addr->inet.ip);
+ return (ipv4Addr >> 24) == 127;
+ }
+
+ return addr->raw.family == AF_INET6 && IPv6ADDR_IS_LOOPBACK(&addr->inet6.ip);
+}
+
+bool IsLoopbackHostname(const nsACString& aAsciiHost) {
+ // If the user has configured to proxy localhost addresses don't consider them
+ // to be secure
+ if (StaticPrefs::network_proxy_allow_hijacking_localhost() &&
+ !StaticPrefs::network_proxy_testing_localhost_is_secure_when_hijacked()) {
+ return false;
+ }
+
+ nsAutoCString host;
+ nsContentUtils::ASCIIToLower(aAsciiHost, host);
+
+ return host.EqualsLiteral("localhost") || host.EqualsLiteral("localhost.") ||
+ StringEndsWith(host, ".localhost"_ns) ||
+ StringEndsWith(host, ".localhost."_ns);
+}
+
+bool HostIsIPLiteral(const nsACString& aAsciiHost) {
+ NetAddr addr;
+ return NS_SUCCEEDED(addr.InitFromString(aAsciiHost));
+}
+
+bool NetAddr::IsIPAddrAny() const {
+ if (this->raw.family == AF_INET) {
+ if (this->inet.ip == htonl(INADDR_ANY)) {
+ return true;
+ }
+ } else if (this->raw.family == AF_INET6) {
+ if (IPv6ADDR_IS_UNSPECIFIED(&this->inet6.ip)) {
+ return true;
+ }
+ if (IPv6ADDR_IS_V4MAPPED(&this->inet6.ip) &&
+ IPv6ADDR_V4MAPPED_TO_IPADDR(&this->inet6.ip) == htonl(INADDR_ANY)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+NetAddr::NetAddr(const PRNetAddr* prAddr) { PRNetAddrToNetAddr(prAddr, this); }
+
+nsresult NetAddr::InitFromString(const nsACString& aString, uint16_t aPort) {
+ PRNetAddr prAddr{};
+ memset(&prAddr, 0, sizeof(PRNetAddr));
+ if (PR_StringToNetAddr(PromiseFlatCString(aString).get(), &prAddr) !=
+ PR_SUCCESS) {
+ return NS_ERROR_FAILURE;
+ }
+
+ PRNetAddrToNetAddr(&prAddr, this);
+
+ if (this->raw.family == PR_AF_INET) {
+ this->inet.port = PR_htons(aPort);
+ } else if (this->raw.family == PR_AF_INET6) {
+ this->inet6.port = PR_htons(aPort);
+ }
+ return NS_OK;
+}
+
+bool NetAddr::IsIPAddrV4() const { return this->raw.family == AF_INET; }
+
+bool NetAddr::IsIPAddrV4Mapped() const {
+ if (this->raw.family == AF_INET6) {
+ return IPv6ADDR_IS_V4MAPPED(&this->inet6.ip);
+ }
+ return false;
+}
+
+static bool isLocalIPv4(uint32_t networkEndianIP) {
+ uint32_t addr32 = ntohl(networkEndianIP);
+ return addr32 >> 24 == 0x0A || // 10/8 prefix (RFC 1918).
+ addr32 >> 20 == 0xAC1 || // 172.16/12 prefix (RFC 1918).
+ addr32 >> 16 == 0xC0A8 || // 192.168/16 prefix (RFC 1918).
+ addr32 >> 16 == 0xA9FE; // 169.254/16 prefix (Link Local).
+}
+
+bool NetAddr::IsIPAddrLocal() const {
+ const NetAddr* addr = this;
+
+ // IPv4 RFC1918 and Link Local Addresses.
+ if (addr->raw.family == AF_INET) {
+ return isLocalIPv4(addr->inet.ip);
+ }
+ // IPv6 Unique and Link Local Addresses.
+ // or mapped IPv4 addresses
+ if (addr->raw.family == AF_INET6) {
+ uint16_t addr16 = ntohs(addr->inet6.ip.u16[0]);
+ if (addr16 >> 9 == 0xfc >> 1 || // fc00::/7 Unique Local Address.
+ addr16 >> 6 == 0xfe80 >> 6) { // fe80::/10 Link Local Address.
+ return true;
+ }
+ if (IPv6ADDR_IS_V4MAPPED(&addr->inet6.ip)) {
+ return isLocalIPv4(IPv6ADDR_V4MAPPED_TO_IPADDR(&addr->inet6.ip));
+ }
+ }
+
+ // Not an IPv4/6 local address.
+ return false;
+}
+
+bool NetAddr::IsIPAddrShared() const {
+ const NetAddr* addr = this;
+
+ // IPv4 RFC6598.
+ if (addr->raw.family == AF_INET) {
+ uint32_t addr32 = ntohl(addr->inet.ip);
+ if (addr32 >> 22 == 0x644 >> 2) { // 100.64/10 prefix (RFC 6598).
+ return true;
+ }
+ }
+
+ // Not an IPv4 shared address.
+ return false;
+}
+
+nsresult NetAddr::GetPort(uint16_t* aResult) const {
+ uint16_t port;
+ if (this->raw.family == PR_AF_INET) {
+ port = this->inet.port;
+ } else if (this->raw.family == PR_AF_INET6) {
+ port = this->inet6.port;
+ } else {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ *aResult = ntohs(port);
+ return NS_OK;
+}
+
+bool NetAddr::operator==(const NetAddr& other) const {
+ if (this->raw.family != other.raw.family) {
+ return false;
+ }
+ if (this->raw.family == AF_INET) {
+ return (this->inet.port == other.inet.port) &&
+ (this->inet.ip == other.inet.ip);
+ }
+ if (this->raw.family == AF_INET6) {
+ return (this->inet6.port == other.inet6.port) &&
+ (this->inet6.flowinfo == other.inet6.flowinfo) &&
+ (memcmp(&this->inet6.ip, &other.inet6.ip, sizeof(this->inet6.ip)) ==
+ 0) &&
+ (this->inet6.scope_id == other.inet6.scope_id);
+#if defined(XP_UNIX)
+ }
+ if (this->raw.family == AF_LOCAL) {
+ return strncmp(this->local.path, other.local.path,
+ ArrayLength(this->local.path));
+#endif
+ }
+ return false;
+}
+
+bool NetAddr::operator<(const NetAddr& other) const {
+ if (this->raw.family != other.raw.family) {
+ return this->raw.family < other.raw.family;
+ }
+ if (this->raw.family == AF_INET) {
+ if (this->inet.ip == other.inet.ip) {
+ return this->inet.port < other.inet.port;
+ }
+ return this->inet.ip < other.inet.ip;
+ }
+ if (this->raw.family == AF_INET6) {
+ int cmpResult =
+ memcmp(&this->inet6.ip, &other.inet6.ip, sizeof(this->inet6.ip));
+ if (cmpResult) {
+ return cmpResult < 0;
+ }
+ if (this->inet6.port != other.inet6.port) {
+ return this->inet6.port < other.inet6.port;
+ }
+ return this->inet6.flowinfo < other.inet6.flowinfo;
+ }
+ return false;
+}
+
+AddrInfo::AddrInfo(const nsACString& host, const PRAddrInfo* prAddrInfo,
+ bool disableIPv4, bool filterNameCollision,
+ const nsACString& cname)
+ : mHostName(host), mCanonicalName(cname) {
+ MOZ_ASSERT(prAddrInfo,
+ "Cannot construct AddrInfo with a null prAddrInfo pointer!");
+ const uint32_t nameCollisionAddr = htonl(0x7f003535); // 127.0.53.53
+
+ PRNetAddr tmpAddr;
+ void* iter = nullptr;
+ do {
+ iter = PR_EnumerateAddrInfo(iter, prAddrInfo, 0, &tmpAddr);
+ bool addIt = iter && (!disableIPv4 || tmpAddr.raw.family != PR_AF_INET) &&
+ (!filterNameCollision || tmpAddr.raw.family != PR_AF_INET ||
+ (tmpAddr.inet.ip != nameCollisionAddr));
+ if (addIt) {
+ NetAddr elem(&tmpAddr);
+ mAddresses.AppendElement(elem);
+ }
+ } while (iter);
+}
+
+AddrInfo::AddrInfo(const nsACString& host, const nsACString& cname,
+ DNSResolverType aResolverType, unsigned int aTRRType,
+ nsTArray<NetAddr>&& addresses)
+ : mHostName(host),
+ mCanonicalName(cname),
+ mResolverType(aResolverType),
+ mTRRType(aTRRType),
+ mAddresses(std::move(addresses)) {}
+
+AddrInfo::AddrInfo(const nsACString& host, DNSResolverType aResolverType,
+ unsigned int aTRRType, nsTArray<NetAddr>&& addresses,
+ uint32_t aTTL)
+ : ttl(aTTL),
+ mHostName(host),
+ mResolverType(aResolverType),
+ mTRRType(aTRRType),
+ mAddresses(std::move(addresses)) {}
+
+// deep copy constructor
+AddrInfo::AddrInfo(const AddrInfo* src) {
+ mHostName = src->mHostName;
+ mCanonicalName = src->mCanonicalName;
+ ttl = src->ttl;
+ mResolverType = src->mResolverType;
+ mTRRType = src->mTRRType;
+ mTrrFetchDuration = src->mTrrFetchDuration;
+ mTrrFetchDurationNetworkOnly = src->mTrrFetchDurationNetworkOnly;
+
+ mAddresses = src->mAddresses.Clone();
+}
+
+AddrInfo::~AddrInfo() = default;
+
+size_t AddrInfo::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const {
+ size_t n = mallocSizeOf(this);
+ n += mHostName.SizeOfExcludingThisIfUnshared(mallocSizeOf);
+ n += mCanonicalName.SizeOfExcludingThisIfUnshared(mallocSizeOf);
+ n += mAddresses.ShallowSizeOfExcludingThis(mallocSizeOf);
+ return n;
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/DNS.h b/netwerk/dns/DNS.h
new file mode 100644
index 0000000000..a0dbbaa17d
--- /dev/null
+++ b/netwerk/dns/DNS.h
@@ -0,0 +1,260 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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/. */
+
+#ifndef DNS_h_
+#define DNS_h_
+
+#include "nscore.h"
+#include "nsString.h"
+#include "prio.h"
+#include "prnetdb.h"
+#include "nsISupportsImpl.h"
+#include "mozilla/MemoryReporting.h"
+#include "nsTArray.h"
+
+#if !defined(XP_WIN)
+# include <arpa/inet.h>
+#endif
+
+#ifdef XP_WIN
+# include "winsock2.h"
+#endif
+
+#ifndef AF_LOCAL
+# define AF_LOCAL 1 // used for named pipe
+#endif
+
+#define IPv6ADDR_IS_LOOPBACK(a) \
+ (((a)->u32[0] == 0) && ((a)->u32[1] == 0) && ((a)->u32[2] == 0) && \
+ ((a)->u8[12] == 0) && ((a)->u8[13] == 0) && ((a)->u8[14] == 0) && \
+ ((a)->u8[15] == 0x1U))
+
+#define IPv6ADDR_IS_V4MAPPED(a) \
+ (((a)->u32[0] == 0) && ((a)->u32[1] == 0) && ((a)->u8[8] == 0) && \
+ ((a)->u8[9] == 0) && ((a)->u8[10] == 0xff) && ((a)->u8[11] == 0xff))
+
+#define IPv6ADDR_V4MAPPED_TO_IPADDR(a) ((a)->u32[3])
+
+#define IPv6ADDR_IS_UNSPECIFIED(a) \
+ (((a)->u32[0] == 0) && ((a)->u32[1] == 0) && ((a)->u32[2] == 0) && \
+ ((a)->u32[3] == 0))
+
+namespace mozilla {
+namespace net {
+
+// IMPORTANT: when adding new values, always add them to the end, otherwise
+// it will mess up telemetry.
+// Stage_0: Receive the record before the http transaction is created.
+// Stage_1: Receive the record after the http transaction is created and the
+// transaction is not dispatched.
+// Stage_2: Receive the record after the http transaction is dispatched.
+enum HTTPSSVC_RECEIVED_STAGE : uint32_t {
+ HTTPSSVC_NOT_PRESENT = 0,
+ HTTPSSVC_WITH_IPHINT_RECEIVED_STAGE_0 = 1,
+ HTTPSSVC_WITHOUT_IPHINT_RECEIVED_STAGE_0 = 2,
+ HTTPSSVC_WITH_IPHINT_RECEIVED_STAGE_1 = 3,
+ HTTPSSVC_WITHOUT_IPHINT_RECEIVED_STAGE_1 = 4,
+ HTTPSSVC_WITH_IPHINT_RECEIVED_STAGE_2 = 5,
+ HTTPSSVC_WITHOUT_IPHINT_RECEIVED_STAGE_2 = 6,
+ HTTPSSVC_NOT_USED = 7,
+ HTTPSSVC_NO_USABLE_RECORD = 8,
+};
+
+#define HTTPS_RR_IS_USED(s) \
+ (s > HTTPSSVC_NOT_PRESENT && s < HTTPSSVC_WITH_IPHINT_RECEIVED_STAGE_2)
+
+// Required buffer size for text form of an IP address.
+// Includes space for null termination. We make our own contants
+// because we don't want higher-level code depending on things
+// like INET6_ADDRSTRLEN and having to include the associated
+// platform-specific headers.
+#ifdef XP_WIN
+// Windows requires longer buffers for some reason.
+const int kIPv4CStrBufSize = 22;
+const int kIPv6CStrBufSize = 65;
+const int kNetAddrMaxCStrBufSize = kIPv6CStrBufSize;
+#else
+const int kIPv4CStrBufSize = 16;
+const int kIPv6CStrBufSize = 46;
+const int kLocalCStrBufSize = 108;
+const int kNetAddrMaxCStrBufSize = kLocalCStrBufSize;
+#endif
+
+// This was all created at a time in which we were using NSPR for host
+// resolution and we were propagating NSPR types like "PRAddrInfo" and
+// "PRNetAddr" all over Gecko. This made it hard to use another host
+// resolver -- we were locked into NSPR. The goal here is to get away
+// from that. We'll translate what we get from NSPR or any other host
+// resolution library into the types below and use them in Gecko.
+
+union IPv6Addr {
+ uint8_t u8[16];
+ uint16_t u16[8];
+ uint32_t u32[4];
+ uint64_t u64[2];
+};
+
+// This struct is similar to operating system structs like "sockaddr", used for
+// things like "connect" and "getsockname". When tempted to cast or do dumb
+// copies of this struct to another struct, bear compiler-computed padding
+// in mind. The size of this struct, and the layout of the data in it, may
+// not be what you expect.
+union NetAddr {
+ struct {
+ uint16_t family; /* address family (0x00ff maskable) */
+ char data[14]; /* raw address data */
+ } raw{};
+ struct {
+ uint16_t family; /* address family (AF_INET) */
+ uint16_t port; /* port number */
+ uint32_t ip; /* The actual 32 bits of address */
+ } inet;
+ struct {
+ uint16_t family; /* address family (AF_INET6) */
+ uint16_t port; /* port number */
+ uint32_t flowinfo; /* routing information */
+ IPv6Addr ip; /* the actual 128 bits of address */
+ uint32_t scope_id; /* set of interfaces for a scope */
+ } inet6;
+#if defined(XP_UNIX) || defined(XP_WIN)
+ struct { /* Unix domain socket or
+ Windows Named Pipes address */
+ uint16_t family; /* address family (AF_UNIX) */
+ char path[104]; /* null-terminated pathname */
+ } local;
+#endif
+ // introduced to support nsTArray<NetAddr> comparisons and sorting
+ bool operator==(const NetAddr& other) const;
+ bool operator<(const NetAddr& other) const;
+
+ inline NetAddr& operator=(const NetAddr& other) {
+ memcpy(this, &other, sizeof(NetAddr));
+ return *this;
+ }
+
+ NetAddr() { memset(this, 0, sizeof(NetAddr)); }
+ explicit NetAddr(const PRNetAddr* prAddr);
+
+ // Will parse aString into a NetAddr using PR_StringToNetAddr.
+ // Returns an error code if parsing fails.
+ // If aPort is non-0 will set the NetAddr's port to (the network endian
+ // value of) that.
+ nsresult InitFromString(const nsACString& aString, uint16_t aPort = 0);
+
+ bool IsIPAddrAny() const;
+ bool IsLoopbackAddr() const;
+ bool IsLoopBackAddressWithoutIPv6Mapping() const;
+ bool IsIPAddrV4() const;
+ bool IsIPAddrV4Mapped() const;
+ bool IsIPAddrLocal() const;
+ bool IsIPAddrShared() const;
+ nsresult GetPort(uint16_t* aResult) const;
+ bool ToStringBuffer(char* buf, uint32_t bufSize) const;
+ nsCString ToString() const;
+};
+
+enum class DNSResolverType : uint32_t { Native = 0, TRR };
+
+class AddrInfo {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AddrInfo)
+
+ public:
+ static const uint32_t NO_TTL_DATA = (uint32_t)-1;
+
+ // Creates an AddrInfo object.
+ explicit AddrInfo(const nsACString& host, const PRAddrInfo* prAddrInfo,
+ bool disableIPv4, bool filterNameCollision,
+ const nsACString& cname);
+
+ // Creates a basic AddrInfo object (initialize only the host, cname and TRR
+ // type).
+ explicit AddrInfo(const nsACString& host, const nsACString& cname,
+ DNSResolverType aResolverType, unsigned int aTRRType,
+ nsTArray<NetAddr>&& addresses);
+
+ // Creates a basic AddrInfo object (initialize only the host and TRR status).
+ explicit AddrInfo(const nsACString& host, DNSResolverType aResolverType,
+ unsigned int aTRRType, nsTArray<NetAddr>&& addresses,
+ uint32_t aTTL = NO_TTL_DATA);
+
+ explicit AddrInfo(const AddrInfo* src); // copy
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
+ bool IsTRR() const { return mResolverType == DNSResolverType::TRR; }
+ DNSResolverType ResolverType() const { return mResolverType; }
+ unsigned int TRRType() { return mTRRType; }
+
+ double GetTrrFetchDuration() { return mTrrFetchDuration; }
+ double GetTrrFetchDurationNetworkOnly() {
+ return mTrrFetchDurationNetworkOnly;
+ }
+
+ const nsTArray<NetAddr>& Addresses() { return mAddresses; }
+ const nsCString& Hostname() { return mHostName; }
+ const nsCString& CanonicalHostname() { return mCanonicalName; }
+ uint32_t TTL() { return ttl; }
+
+ class MOZ_STACK_CLASS AddrInfoBuilder {
+ public:
+ explicit AddrInfoBuilder(AddrInfo* aInfo) {
+ mInfo = new AddrInfo(aInfo); // Clone it
+ }
+
+ void SetTrrFetchDurationNetworkOnly(double aTime) {
+ mInfo->mTrrFetchDurationNetworkOnly = aTime;
+ }
+
+ void SetTrrFetchDuration(double aTime) { mInfo->mTrrFetchDuration = aTime; }
+
+ void SetTTL(uint32_t aTTL) { mInfo->ttl = aTTL; }
+
+ void SetAddresses(nsTArray<NetAddr>&& addresses) {
+ mInfo->mAddresses = std::move(addresses);
+ }
+
+ void SetCanonicalHostname(const nsACString& aCname) {
+ mInfo->mCanonicalName = aCname;
+ }
+
+ already_AddRefed<AddrInfo> Finish() { return mInfo.forget(); }
+
+ private:
+ RefPtr<AddrInfo> mInfo;
+ };
+
+ AddrInfoBuilder Build() { return AddrInfoBuilder(this); }
+
+ private:
+ ~AddrInfo();
+ uint32_t ttl = NO_TTL_DATA;
+
+ nsCString mHostName;
+ nsCString mCanonicalName;
+ DNSResolverType mResolverType = DNSResolverType::Native;
+ unsigned int mTRRType = 0;
+ double mTrrFetchDuration = 0;
+ double mTrrFetchDurationNetworkOnly = 0;
+
+ nsTArray<NetAddr> mAddresses;
+};
+
+// Copies the contents of a PRNetAddr to a NetAddr.
+// Does not do a ptr safety check!
+void PRNetAddrToNetAddr(const PRNetAddr* prAddr, NetAddr* addr);
+
+// Copies the contents of a NetAddr to a PRNetAddr.
+// Does not do a ptr safety check!
+void NetAddrToPRNetAddr(const NetAddr* addr, PRNetAddr* prAddr);
+
+bool IsLoopbackHostname(const nsACString& aAsciiHost);
+
+bool HostIsIPLiteral(const nsACString& aAsciiHost);
+
+} // namespace net
+} // namespace mozilla
+
+#endif // DNS_h_
diff --git a/netwerk/dns/DNSAdditionalInfo.cpp b/netwerk/dns/DNSAdditionalInfo.cpp
new file mode 100644
index 0000000000..885e9e10dc
--- /dev/null
+++ b/netwerk/dns/DNSAdditionalInfo.cpp
@@ -0,0 +1,25 @@
+/* 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 "DNSAdditionalInfo.h"
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS(DNSAdditionalInfo, nsIDNSAdditionalInfo)
+
+NS_IMETHODIMP
+DNSAdditionalInfo::GetPort(int32_t* aPort) {
+ *aPort = mPort;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+DNSAdditionalInfo::GetResolverURL(nsACString& aURL) {
+ aURL = mURL;
+ return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/DNSAdditionalInfo.h b/netwerk/dns/DNSAdditionalInfo.h
new file mode 100644
index 0000000000..658b9623dd
--- /dev/null
+++ b/netwerk/dns/DNSAdditionalInfo.h
@@ -0,0 +1,44 @@
+/* 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/. */
+
+#ifndef mozilla_net_DNSAdditionalInfo_h__
+#define mozilla_net_DNSAdditionalInfo_h__
+
+#include "nsIDNSAdditionalInfo.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace net {
+
+class DNSAdditionalInfo : public nsIDNSAdditionalInfo {
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIDNSADDITIONALINFO
+ public:
+ explicit DNSAdditionalInfo(const nsACString& aURL, int32_t aPort)
+ : mURL(aURL), mPort(aPort){};
+ static nsCString URL(nsIDNSAdditionalInfo* aInfo) {
+ nsCString url;
+ if (aInfo) {
+ MOZ_ALWAYS_SUCCEEDS(aInfo->GetResolverURL(url));
+ }
+ return url;
+ }
+ static int32_t Port(nsIDNSAdditionalInfo* aInfo) {
+ int32_t port = -1;
+ if (aInfo) {
+ MOZ_ALWAYS_SUCCEEDS(aInfo->GetPort(&port));
+ }
+ return port;
+ }
+
+ private:
+ virtual ~DNSAdditionalInfo() = default;
+ nsCString mURL;
+ int32_t mPort;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_DNSAdditionalInfo_h__
diff --git a/netwerk/dns/DNSByTypeRecord.h b/netwerk/dns/DNSByTypeRecord.h
new file mode 100644
index 0000000000..8b80787698
--- /dev/null
+++ b/netwerk/dns/DNSByTypeRecord.h
@@ -0,0 +1,259 @@
+/* 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/. */
+
+#ifndef DNSByTypeRecord_h__
+#define DNSByTypeRecord_h__
+
+#include "mozilla/net/HTTPSSVC.h"
+#include "mozilla/ipc/IPDLParamTraits.h"
+#include "ipc/IPCMessageUtils.h"
+#include "mozilla/net/NeckoMessageUtils.h"
+
+namespace mozilla {
+namespace net {
+
+// The types of nsIDNSByTypeRecord: Nothing, TXT, HTTPSSVC
+using TypeRecordEmpty = Nothing;
+using TypeRecordTxt = CopyableTArray<nsCString>;
+using TypeRecordHTTPSSVC = CopyableTArray<SVCB>;
+
+// This variant reflects the multiple types of data a nsIDNSByTypeRecord
+// can hold.
+using TypeRecordResultType =
+ Variant<TypeRecordEmpty, TypeRecordTxt, TypeRecordHTTPSSVC>;
+
+// TypeRecordResultType is a variant, but since it doesn't have a default
+// constructor it's not a type we can pass directly over IPC.
+struct IPCTypeRecord {
+ bool operator==(const IPCTypeRecord& aOther) const {
+ return mData == aOther.mData;
+ }
+ explicit IPCTypeRecord() : mData(Nothing{}) {}
+ TypeRecordResultType mData;
+ uint32_t mTTL = 0;
+};
+
+} // namespace net
+} // namespace mozilla
+
+namespace mozilla {
+namespace ipc {
+
+template <>
+struct IPDLParamTraits<mozilla::net::IPCTypeRecord> {
+ typedef mozilla::net::IPCTypeRecord paramType;
+ static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor,
+ const paramType& aParam) {
+ WriteIPDLParam(aWriter, aActor, aParam.mData);
+ WriteIPDLParam(aWriter, aActor, aParam.mTTL);
+ }
+
+ static bool Read(IPC::MessageReader* aReader, IProtocol* aActor,
+ paramType* aResult) {
+ if (!ReadIPDLParam(aReader, aActor, &aResult->mData)) {
+ return false;
+ }
+ if (!ReadIPDLParam(aReader, aActor, &aResult->mTTL)) {
+ return false;
+ }
+ return true;
+ }
+};
+
+template <>
+struct IPDLParamTraits<mozilla::Nothing> {
+ typedef mozilla::Nothing paramType;
+ static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor,
+ const paramType& aParam) {
+ bool isSome = false;
+ WriteIPDLParam(aWriter, aActor, isSome);
+ }
+
+ static bool Read(IPC::MessageReader* aReader, IProtocol* aActor,
+ paramType* aResult) {
+ bool isSome;
+ if (!ReadIPDLParam(aReader, aActor, &isSome)) {
+ return false;
+ }
+ *aResult = Nothing();
+ return true;
+ }
+};
+
+template <>
+struct IPDLParamTraits<mozilla::net::SVCB> {
+ typedef mozilla::net::SVCB paramType;
+ static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor,
+ const paramType& aParam) {
+ WriteIPDLParam(aWriter, aActor, aParam.mSvcFieldPriority);
+ WriteIPDLParam(aWriter, aActor, aParam.mSvcDomainName);
+ WriteIPDLParam(aWriter, aActor, aParam.mEchConfig);
+ WriteIPDLParam(aWriter, aActor, aParam.mODoHConfig);
+ WriteIPDLParam(aWriter, aActor, aParam.mHasIPHints);
+ WriteIPDLParam(aWriter, aActor, aParam.mHasEchConfig);
+ WriteIPDLParam(aWriter, aActor, aParam.mSvcFieldValue);
+ }
+
+ static bool Read(IPC::MessageReader* aReader, IProtocol* aActor,
+ paramType* aResult) {
+ if (!ReadIPDLParam(aReader, aActor, &aResult->mSvcFieldPriority)) {
+ return false;
+ }
+ if (!ReadIPDLParam(aReader, aActor, &aResult->mSvcDomainName)) {
+ return false;
+ }
+ if (!ReadIPDLParam(aReader, aActor, &aResult->mEchConfig)) {
+ return false;
+ }
+ if (!ReadIPDLParam(aReader, aActor, &aResult->mODoHConfig)) {
+ return false;
+ }
+ if (!ReadIPDLParam(aReader, aActor, &aResult->mHasIPHints)) {
+ return false;
+ }
+ if (!ReadIPDLParam(aReader, aActor, &aResult->mHasEchConfig)) {
+ return false;
+ }
+ if (!ReadIPDLParam(aReader, aActor, &aResult->mSvcFieldValue)) {
+ return false;
+ }
+ return true;
+ }
+};
+
+template <>
+struct IPDLParamTraits<mozilla::net::SvcParamAlpn> {
+ typedef mozilla::net::SvcParamAlpn paramType;
+ static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor,
+ const paramType& aParam) {
+ WriteIPDLParam(aWriter, aActor, aParam.mValue);
+ }
+
+ static bool Read(IPC::MessageReader* aReader, IProtocol* aActor,
+ paramType* aResult) {
+ if (!ReadIPDLParam(aReader, aActor, &aResult->mValue)) {
+ return false;
+ }
+ return true;
+ }
+};
+
+template <>
+struct IPDLParamTraits<mozilla::net::SvcParamNoDefaultAlpn> {
+ typedef mozilla::net::SvcParamNoDefaultAlpn paramType;
+ static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor,
+ const paramType& aParam) {}
+
+ static bool Read(IPC::MessageReader* aReader, IProtocol* aActor,
+ paramType* aResult) {
+ return true;
+ }
+};
+
+template <>
+struct IPDLParamTraits<mozilla::net::SvcParamPort> {
+ typedef mozilla::net::SvcParamPort paramType;
+ static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor,
+ const paramType& aParam) {
+ WriteIPDLParam(aWriter, aActor, aParam.mValue);
+ }
+
+ static bool Read(IPC::MessageReader* aReader, IProtocol* aActor,
+ paramType* aResult) {
+ if (!ReadIPDLParam(aReader, aActor, &aResult->mValue)) {
+ return false;
+ }
+ return true;
+ }
+};
+
+template <>
+struct IPDLParamTraits<mozilla::net::SvcParamIpv4Hint> {
+ typedef mozilla::net::SvcParamIpv4Hint paramType;
+ static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor,
+ const paramType& aParam) {
+ WriteIPDLParam(aWriter, aActor, aParam.mValue);
+ }
+
+ static bool Read(IPC::MessageReader* aReader, IProtocol* aActor,
+ paramType* aResult) {
+ if (!ReadIPDLParam(aReader, aActor, &aResult->mValue)) {
+ return false;
+ }
+ return true;
+ }
+};
+
+template <>
+struct IPDLParamTraits<mozilla::net::SvcParamEchConfig> {
+ typedef mozilla::net::SvcParamEchConfig paramType;
+ static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor,
+ const paramType& aParam) {
+ WriteIPDLParam(aWriter, aActor, aParam.mValue);
+ }
+
+ static bool Read(IPC::MessageReader* aReader, IProtocol* aActor,
+ paramType* aResult) {
+ if (!ReadIPDLParam(aReader, aActor, &aResult->mValue)) {
+ return false;
+ }
+ return true;
+ }
+};
+
+template <>
+struct IPDLParamTraits<mozilla::net::SvcParamIpv6Hint> {
+ typedef mozilla::net::SvcParamIpv6Hint paramType;
+ static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor,
+ const paramType& aParam) {
+ WriteIPDLParam(aWriter, aActor, aParam.mValue);
+ }
+
+ static bool Read(IPC::MessageReader* aReader, IProtocol* aActor,
+ paramType* aResult) {
+ if (!ReadIPDLParam(aReader, aActor, &aResult->mValue)) {
+ return false;
+ }
+ return true;
+ }
+};
+
+template <>
+struct IPDLParamTraits<mozilla::net::SvcParamODoHConfig> {
+ typedef mozilla::net::SvcParamODoHConfig paramType;
+ static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor,
+ const paramType& aParam) {
+ WriteIPDLParam(aWriter, aActor, aParam.mValue);
+ }
+
+ static bool Read(IPC::MessageReader* aReader, IProtocol* aActor,
+ paramType* aResult) {
+ if (!ReadIPDLParam(aReader, aActor, &aResult->mValue)) {
+ return false;
+ }
+ return true;
+ }
+};
+
+template <>
+struct IPDLParamTraits<mozilla::net::SvcFieldValue> {
+ typedef mozilla::net::SvcFieldValue paramType;
+ static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor,
+ const paramType& aParam) {
+ WriteIPDLParam(aWriter, aActor, aParam.mValue);
+ }
+
+ static bool Read(IPC::MessageReader* aReader, IProtocol* aActor,
+ paramType* aResult) {
+ if (!ReadIPDLParam(aReader, aActor, &aResult->mValue)) {
+ return false;
+ }
+ return true;
+ }
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // DNSByTypeRecord_h__
diff --git a/netwerk/dns/DNSListenerProxy.cpp b/netwerk/dns/DNSListenerProxy.cpp
new file mode 100644
index 0000000000..8468edca2d
--- /dev/null
+++ b/netwerk/dns/DNSListenerProxy.cpp
@@ -0,0 +1,37 @@
+/* -*- 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 "mozilla/net/DNSListenerProxy.h"
+#include "nsICancelable.h"
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ADDREF(DNSListenerProxy)
+NS_IMPL_RELEASE(DNSListenerProxy)
+NS_INTERFACE_MAP_BEGIN(DNSListenerProxy)
+ NS_INTERFACE_MAP_ENTRY(nsIDNSListener)
+ NS_INTERFACE_MAP_ENTRY_CONCRETE(DNSListenerProxy)
+NS_INTERFACE_MAP_END
+
+NS_IMETHODIMP
+DNSListenerProxy::OnLookupComplete(nsICancelable* aRequest,
+ nsIDNSRecord* aRecord, nsresult aStatus) {
+ RefPtr<DNSListenerProxy> self = this;
+ nsCOMPtr<nsICancelable> request = aRequest;
+ nsCOMPtr<nsIDNSRecord> record = aRecord;
+ return mTargetThread->Dispatch(
+ NS_NewRunnableFunction("DNSListenerProxy::OnLookupComplete",
+ [self, request, record, aStatus]() {
+ Unused << self->mListener->OnLookupComplete(
+ request, record, aStatus);
+ self->mListener = nullptr;
+ }),
+ NS_DISPATCH_NORMAL);
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/DNSListenerProxy.h b/netwerk/dns/DNSListenerProxy.h
new file mode 100644
index 0000000000..5baddfd8e7
--- /dev/null
+++ b/netwerk/dns/DNSListenerProxy.h
@@ -0,0 +1,62 @@
+/* -*- 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/. */
+
+#ifndef DNSListenerProxy_h__
+#define DNSListenerProxy_h__
+
+#include "nsIDNSListener.h"
+#include "nsIDNSRecord.h"
+#include "nsProxyRelease.h"
+#include "nsThreadUtils.h"
+
+class nsIEventTarget;
+class nsICancelable;
+
+namespace mozilla {
+namespace net {
+
+#define DNS_LISTENER_PROXY_IID \
+ { \
+ 0x8f172ca3, 0x7a7f, 0x4941, { \
+ 0xa7, 0x0b, 0xbc, 0x72, 0x80, 0x2e, 0x9d, 0x9b \
+ } \
+ }
+
+class DNSListenerProxy final : public nsIDNSListener {
+ public:
+ DNSListenerProxy(nsIDNSListener* aListener, nsIEventTarget* aTargetThread)
+ // We want to make sure that |aListener| is only accessed on the target
+ // thread.
+ : mListener(aListener),
+ mTargetThread(aTargetThread),
+ mListenerAddress(reinterpret_cast<uintptr_t>(aListener)) {
+ MOZ_ASSERT(mListener);
+ MOZ_ASSERT(mTargetThread);
+ }
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIDNSLISTENER
+ NS_DECLARE_STATIC_IID_ACCESSOR(DNS_LISTENER_PROXY_IID)
+
+ uintptr_t GetOriginalListenerAddress() const { return mListenerAddress; }
+
+ private:
+ ~DNSListenerProxy() {
+ NS_ProxyRelease("DNSListenerProxy::mListener", mTargetThread,
+ mListener.forget());
+ }
+
+ nsCOMPtr<nsIDNSListener> mListener;
+ nsCOMPtr<nsIEventTarget> mTargetThread;
+ uintptr_t mListenerAddress;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(DNSListenerProxy, DNS_LISTENER_PROXY_IID)
+
+} // namespace net
+} // namespace mozilla
+
+#endif // DNSListenerProxy_h__
diff --git a/netwerk/dns/DNSLogging.h b/netwerk/dns/DNSLogging.h
new file mode 100644
index 0000000000..4fd63730ca
--- /dev/null
+++ b/netwerk/dns/DNSLogging.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef mozilla_DNSLogging_h
+#define mozilla_DNSLogging_h
+
+#include "mozilla/Logging.h"
+
+#undef LOG
+
+namespace mozilla {
+namespace net {
+extern LazyLogModule gHostResolverLog;
+} // namespace net
+} // namespace mozilla
+
+#define LOG(msg) \
+ MOZ_LOG(mozilla::net::gHostResolverLog, mozilla::LogLevel::Debug, msg)
+#define LOG1(msg) \
+ MOZ_LOG(mozilla::net::gHostResolverLog, mozilla::LogLevel::Error, msg)
+#define LOG_ENABLED() \
+ MOZ_LOG_TEST(mozilla::net::gHostResolverLog, mozilla::LogLevel::Debug)
+
+#endif // mozilla_DNSLogging_h
diff --git a/netwerk/dns/DNSPacket.cpp b/netwerk/dns/DNSPacket.cpp
new file mode 100644
index 0000000000..6aef801d63
--- /dev/null
+++ b/netwerk/dns/DNSPacket.cpp
@@ -0,0 +1,1100 @@
+/* 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 "DNSPacket.h"
+
+#include "DNS.h"
+#include "mozilla/EndianUtils.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/StaticPrefs_network.h"
+// Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
+#include "DNSLogging.h"
+
+#include "nsIInputStream.h"
+
+namespace mozilla {
+namespace net {
+
+static uint16_t get16bit(const unsigned char* aData, unsigned int index) {
+ return ((aData[index] << 8) | aData[index + 1]);
+}
+
+static uint32_t get32bit(const unsigned char* aData, unsigned int index) {
+ return (aData[index] << 24) | (aData[index + 1] << 16) |
+ (aData[index + 2] << 8) | aData[index + 3];
+}
+
+// https://datatracker.ietf.org/doc/html/rfc8914#name-defined-extended-dns-errors
+// This is a list of errors for which we should not fallback to Do53.
+// These are normally explicit filtering performed by the recursive resolver.
+bool hardFail(uint16_t code) {
+ const uint16_t noFallbackErrors[] = {
+ 4, // Forged answer (malware filtering)
+ 17, // Filtered
+ };
+
+ for (const auto& err : noFallbackErrors) {
+ if (code == err) {
+ return true;
+ }
+ }
+ return false;
+}
+
+nsresult DNSPacket::FillBuffer(
+ std::function<int(unsigned char response[MAX_SIZE])>&& aPredicate) {
+ int response_length = aPredicate(mResponse);
+ if (response_length < 0) {
+ LOG(("FillBuffer response len < 0"));
+ mBodySize = 0;
+ mStatus = NS_ERROR_UNEXPECTED;
+ return mStatus;
+ }
+
+ mBodySize = response_length;
+ return NS_OK;
+}
+// static
+nsresult DNSPacket::ParseSvcParam(unsigned int svcbIndex, uint16_t key,
+ SvcFieldValue& field, uint16_t length,
+ const unsigned char* aBuffer) {
+ switch (key) {
+ case SvcParamKeyMandatory: {
+ if (length % 2 != 0) {
+ // This key should encode a list of uint16_t
+ return NS_ERROR_UNEXPECTED;
+ }
+ while (length > 0) {
+ uint16_t mandatoryKey = get16bit(aBuffer, svcbIndex);
+ length -= 2;
+ svcbIndex += 2;
+
+ if (!IsValidSvcParamKey(mandatoryKey)) {
+ LOG(("The mandatory field includes a key we don't support %u",
+ mandatoryKey));
+ return NS_ERROR_UNEXPECTED;
+ }
+ }
+ break;
+ }
+ case SvcParamKeyAlpn: {
+ field.mValue = AsVariant(SvcParamAlpn());
+ auto& alpnArray = field.mValue.as<SvcParamAlpn>().mValue;
+ while (length > 0) {
+ uint8_t alpnIdLength = aBuffer[svcbIndex++];
+ length -= 1;
+ if (alpnIdLength > length) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ alpnArray.AppendElement(
+ nsCString((const char*)&aBuffer[svcbIndex], alpnIdLength));
+ length -= alpnIdLength;
+ svcbIndex += alpnIdLength;
+ }
+ break;
+ }
+ case SvcParamKeyNoDefaultAlpn: {
+ if (length != 0) {
+ // This key should not contain a value
+ return NS_ERROR_UNEXPECTED;
+ }
+ field.mValue = AsVariant(SvcParamNoDefaultAlpn{});
+ break;
+ }
+ case SvcParamKeyPort: {
+ if (length != 2) {
+ // This key should only encode a uint16_t
+ return NS_ERROR_UNEXPECTED;
+ }
+ field.mValue =
+ AsVariant(SvcParamPort{.mValue = get16bit(aBuffer, svcbIndex)});
+ break;
+ }
+ case SvcParamKeyIpv4Hint: {
+ if (length % 4 != 0) {
+ // This key should only encode IPv4 addresses
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ field.mValue = AsVariant(SvcParamIpv4Hint());
+ auto& ipv4array = field.mValue.as<SvcParamIpv4Hint>().mValue;
+ while (length > 0) {
+ NetAddr addr;
+ addr.inet.family = AF_INET;
+ addr.inet.port = 0;
+ addr.inet.ip = ntohl(get32bit(aBuffer, svcbIndex));
+ ipv4array.AppendElement(addr);
+ length -= 4;
+ svcbIndex += 4;
+ }
+ break;
+ }
+ case SvcParamKeyEchConfig: {
+ field.mValue = AsVariant(SvcParamEchConfig{
+ .mValue = nsCString((const char*)(&aBuffer[svcbIndex]), length)});
+ break;
+ }
+ case SvcParamKeyIpv6Hint: {
+ if (length % 16 != 0) {
+ // This key should only encode IPv6 addresses
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ field.mValue = AsVariant(SvcParamIpv6Hint());
+ auto& ipv6array = field.mValue.as<SvcParamIpv6Hint>().mValue;
+ while (length > 0) {
+ NetAddr addr;
+ addr.inet6.family = AF_INET6;
+ addr.inet6.port = 0; // unknown
+ addr.inet6.flowinfo = 0; // unknown
+ addr.inet6.scope_id = 0; // unknown
+ for (int i = 0; i < 16; i++, svcbIndex++) {
+ addr.inet6.ip.u8[i] = aBuffer[svcbIndex];
+ }
+ ipv6array.AppendElement(addr);
+ length -= 16;
+ // no need to increase svcbIndex - we did it in the for above.
+ }
+ break;
+ }
+ case SvcParamKeyODoHConfig: {
+ field.mValue = AsVariant(SvcParamODoHConfig{
+ .mValue = nsCString((const char*)(&aBuffer[svcbIndex]), length)});
+ break;
+ }
+ default: {
+ // Unespected type. We'll just ignore it.
+ return NS_OK;
+ break;
+ }
+ }
+ return NS_OK;
+}
+
+nsresult DNSPacket::PassQName(unsigned int& index,
+ const unsigned char* aBuffer) {
+ uint8_t length;
+ do {
+ if (mBodySize < (index + 1)) {
+ LOG(("TRR: PassQName:%d fail at index %d\n", __LINE__, index));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ length = static_cast<uint8_t>(aBuffer[index]);
+ if ((length & 0xc0) == 0xc0) {
+ // name pointer, advance over it and be done
+ if (mBodySize < (index + 2)) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ index += 2;
+ break;
+ }
+ if (length & 0xc0) {
+ LOG(("TRR: illegal label length byte (%x) at index %d\n", length, index));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ // pass label
+ if (mBodySize < (index + 1 + length)) {
+ LOG(("TRR: PassQName:%d fail at index %d\n", __LINE__, index));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ index += 1 + length;
+ } while (length);
+ return NS_OK;
+}
+
+// GetQname: retrieves the qname (stores in 'aQname') and stores the index
+// after qname was parsed into the 'aIndex'.
+// static
+nsresult DNSPacket::GetQname(nsACString& aQname, unsigned int& aIndex,
+ const unsigned char* aBuffer,
+ unsigned int aBodySize) {
+ uint8_t clength = 0;
+ unsigned int cindex = aIndex;
+ unsigned int loop = 128; // a valid DNS name can never loop this much
+ unsigned int endindex = 0; // index position after this data
+ do {
+ if (cindex >= aBodySize) {
+ LOG(("TRR: bad Qname packet\n"));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ clength = static_cast<uint8_t>(aBuffer[cindex]);
+ if ((clength & 0xc0) == 0xc0) {
+ // name pointer, get the new offset (14 bits)
+ if ((cindex + 1) >= aBodySize) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ // extract the new index position for the next label
+ uint16_t newpos = (clength & 0x3f) << 8 | aBuffer[cindex + 1];
+ if (!endindex) {
+ // only update on the first "jump"
+ endindex = cindex + 2;
+ }
+ cindex = newpos;
+ continue;
+ }
+ if (clength & 0xc0) {
+ // any of those bits set individually is an error
+ LOG(("TRR: bad Qname packet\n"));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ cindex++;
+
+ if (clength) {
+ if (!aQname.IsEmpty()) {
+ aQname.Append(".");
+ }
+ if ((cindex + clength) > aBodySize) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ aQname.Append((const char*)(&aBuffer[cindex]), clength);
+ cindex += clength; // skip label
+ }
+ } while (clength && --loop);
+
+ if (!loop) {
+ LOG(("DNSPacket::DohDecode pointer loop error\n"));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ if (!endindex) {
+ // there was no "jump"
+ endindex = cindex;
+ }
+ aIndex = endindex;
+ return NS_OK;
+}
+
+nsresult DOHresp::Add(uint32_t TTL, unsigned char const* dns,
+ unsigned int index, uint16_t len, bool aLocalAllowed) {
+ NetAddr addr;
+ if (4 == len) {
+ // IPv4
+ addr.inet.family = AF_INET;
+ addr.inet.port = 0; // unknown
+ addr.inet.ip = ntohl(get32bit(dns, index));
+ } else if (16 == len) {
+ // IPv6
+ addr.inet6.family = AF_INET6;
+ addr.inet6.port = 0; // unknown
+ addr.inet6.flowinfo = 0; // unknown
+ addr.inet6.scope_id = 0; // unknown
+ for (int i = 0; i < 16; i++, index++) {
+ addr.inet6.ip.u8[i] = dns[index];
+ }
+ } else {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (addr.IsIPAddrLocal() && !aLocalAllowed) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // While the DNS packet might return individual TTLs for each address,
+ // we can only return one value in the AddrInfo class so pick the
+ // lowest number.
+ if (mTtl < TTL) {
+ mTtl = TTL;
+ }
+
+ if (LOG_ENABLED()) {
+ char buf[128];
+ addr.ToStringBuffer(buf, sizeof(buf));
+ LOG(("DOHresp:Add %s\n", buf));
+ }
+ mAddresses.AppendElement(addr);
+ return NS_OK;
+}
+
+nsresult DNSPacket::OnDataAvailable(nsIRequest* aRequest,
+ nsIInputStream* aInputStream,
+ uint64_t aOffset, const uint32_t aCount) {
+ if (aCount + mBodySize > MAX_SIZE) {
+ LOG(("DNSPacket::OnDataAvailable:%d fail\n", __LINE__));
+ return NS_ERROR_FAILURE;
+ }
+ uint32_t count;
+ nsresult rv =
+ aInputStream->Read((char*)mResponse + mBodySize, aCount, &count);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ MOZ_ASSERT(count == aCount);
+ mBodySize += aCount;
+ return NS_OK;
+}
+
+const uint8_t kDNS_CLASS_IN = 1;
+
+nsresult DNSPacket::EncodeRequest(nsCString& aBody, const nsACString& aHost,
+ uint16_t aType, bool aDisableECS) {
+ aBody.Truncate();
+ // Header
+ aBody += '\0';
+ aBody += '\0'; // 16 bit id
+ aBody += 0x01; // |QR| Opcode |AA|TC|RD| Set the RD bit
+ aBody += '\0'; // |RA| Z | RCODE |
+ aBody += '\0';
+ aBody += 1; // QDCOUNT (number of entries in the question section)
+ aBody += '\0';
+ aBody += '\0'; // ANCOUNT
+ aBody += '\0';
+ aBody += '\0'; // NSCOUNT
+
+ char additionalRecords =
+ (aDisableECS || StaticPrefs::network_trr_padding()) ? 1 : 0;
+ aBody += '\0'; // ARCOUNT
+ aBody += additionalRecords; // ARCOUNT low byte for EDNS(0)
+
+ // Question
+
+ // The input host name should be converted to a sequence of labels, where
+ // each label consists of a length octet followed by that number of
+ // octets. The domain name terminates with the zero length octet for the
+ // null label of the root.
+ // Followed by 16 bit QTYPE and 16 bit QCLASS
+
+ int32_t index = 0;
+ int32_t offset = 0;
+ do {
+ bool dotFound = false;
+ int32_t labelLength;
+ index = aHost.FindChar('.', offset);
+ if (kNotFound != index) {
+ dotFound = true;
+ labelLength = index - offset;
+ } else {
+ labelLength = aHost.Length() - offset;
+ }
+ if (labelLength > 63) {
+ // too long label!
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ if (labelLength > 0) {
+ aBody += static_cast<unsigned char>(labelLength);
+ nsDependentCSubstring label = Substring(aHost, offset, labelLength);
+ aBody.Append(label);
+ }
+ if (!dotFound) {
+ aBody += '\0'; // terminate with a final zero
+ break;
+ }
+ offset += labelLength + 1; // move over label and dot
+ } while (true);
+
+ aBody += static_cast<uint8_t>(aType >> 8); // upper 8 bit TYPE
+ aBody += static_cast<uint8_t>(aType);
+ aBody += '\0'; // upper 8 bit CLASS
+ aBody += kDNS_CLASS_IN; // IN - "the Internet"
+
+ if (additionalRecords) {
+ // EDNS(0) is RFC 6891, ECS is RFC 7871
+ aBody += '\0'; // NAME | domain name | MUST be 0 (root domain) |
+ aBody += '\0';
+ aBody += 41; // TYPE | u_int16_t | OPT (41) |
+ aBody += 16; // CLASS | u_int16_t | requestor's UDP payload size |
+ aBody +=
+ '\0'; // advertise 4K (high-byte: 16 | low-byte: 0), ignored by DoH
+ aBody += '\0'; // TTL | u_int32_t | extended RCODE and flags |
+ aBody += '\0';
+ aBody += '\0';
+ aBody += '\0';
+
+ // calculate padding length
+ unsigned int paddingLen = 0;
+ unsigned int rdlen = 0;
+ bool padding = StaticPrefs::network_trr_padding();
+ if (padding) {
+ // always add padding specified in rfc 7830 when this config is enabled
+ // to allow the reponse to be padded as well
+
+ // two bytes RDLEN, 4 bytes padding header
+ unsigned int packetLen = aBody.Length() + 2 + 4;
+ if (aDisableECS) {
+ // 8 bytes for disabling ecs
+ packetLen += 8;
+ }
+
+ // clamp the padding length, because the padding extension only allows up
+ // to 2^16 - 1 bytes padding and adding too much padding wastes resources
+ uint32_t padTo = std::clamp<uint32_t>(
+ StaticPrefs::network_trr_padding_length(), 0, 1024);
+
+ // Calculate number of padding bytes. The second '%'-operator is necessary
+ // because we prefer to add 0 bytes padding rather than padTo bytes
+ if (padTo > 0) {
+ paddingLen = (padTo - (packetLen % padTo)) % padTo;
+ }
+ // padding header + padding length
+ rdlen += 4 + paddingLen;
+ }
+ if (aDisableECS) {
+ rdlen += 8;
+ }
+
+ // RDLEN | u_int16_t | length of all RDATA |
+ aBody += (char)((rdlen >> 8) & 0xff); // upper 8 bit RDLEN
+ aBody += (char)(rdlen & 0xff);
+
+ // RDATA | octet stream | {attribute,value} pairs |
+ // The RDATA is just the ECS option setting zero subnet prefix
+
+ if (aDisableECS) {
+ aBody += '\0'; // upper 8 bit OPTION-CODE ECS
+ aBody += 8; // OPTION-CODE, 2 octets, for ECS is 8
+
+ aBody += '\0'; // upper 8 bit OPTION-LENGTH
+ aBody += 4; // OPTION-LENGTH, 2 octets, contains the length of the
+ // payload after OPTION-LENGTH
+ aBody += '\0'; // upper 8 bit FAMILY. IANA Address Family Numbers
+ // registry, not the AF_* constants!
+ aBody += 1; // FAMILY (Ipv4), 2 octets
+
+ aBody += '\0'; // SOURCE PREFIX-LENGTH | SCOPE PREFIX-LENGTH |
+ aBody += '\0';
+
+ // ADDRESS, minimum number of octets == nothing because zero bits
+ }
+
+ if (padding) {
+ aBody += '\0'; // upper 8 bit option OPTION-CODE PADDING
+ aBody += 12; // OPTION-CODE, 2 octets, for PADDING is 12
+
+ // OPTION-LENGTH, 2 octets
+ aBody += (char)((paddingLen >> 8) & 0xff);
+ aBody += (char)(paddingLen & 0xff);
+ for (unsigned int i = 0; i < paddingLen; i++) {
+ aBody += '\0';
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+// static
+nsresult DNSPacket::ParseHTTPS(uint16_t aRDLen, struct SVCB& aParsed,
+ unsigned int aIndex,
+ const unsigned char* aBuffer,
+ unsigned int aBodySize,
+ const nsACString& aOriginHost) {
+ int32_t lastSvcParamKey = -1;
+ nsresult rv = NS_OK;
+ unsigned int svcbIndex = aIndex;
+ CheckedInt<uint16_t> available = aRDLen;
+
+ // Should have at least 2 bytes for the priority and one for the
+ // qname length.
+ if (available.value() < 3) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ aParsed.mSvcFieldPriority = get16bit(aBuffer, svcbIndex);
+ svcbIndex += 2;
+
+ rv = GetQname(aParsed.mSvcDomainName, svcbIndex, aBuffer, aBodySize);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (aParsed.mSvcDomainName.IsEmpty()) {
+ if (aParsed.mSvcFieldPriority == 0) {
+ // For AliasMode SVCB RRs, a TargetName of "." indicates that
+ // the service is not available or does not exist.
+ return NS_OK;
+ }
+
+ // For ServiceMode SVCB RRs, if TargetName has the value ".",
+ // then the owner name of this record MUST be used as
+ // the effective TargetName.
+ // When the qname is port prefix name, we need to use the
+ // original host name as TargetName.
+ aParsed.mSvcDomainName = aOriginHost;
+ }
+
+ available -= (svcbIndex - aIndex);
+ if (!available.isValid()) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ while (available.value() >= 4) {
+ // Every SvcFieldValues must have at least 4 bytes for the
+ // SvcParamKey (2 bytes) and length of SvcParamValue (2 bytes)
+ // If the length ever goes above the available data, meaning if
+ // available ever underflows, then that is an error.
+ struct SvcFieldValue value;
+ uint16_t key = get16bit(aBuffer, svcbIndex);
+ svcbIndex += 2;
+
+ // 2.2 Clients MUST consider an RR malformed if SvcParamKeys are
+ // not in strictly increasing numeric order.
+ if (key <= lastSvcParamKey) {
+ LOG(("SvcParamKeys not in increasing order"));
+ return NS_ERROR_UNEXPECTED;
+ }
+ lastSvcParamKey = key;
+
+ uint16_t len = get16bit(aBuffer, svcbIndex);
+ svcbIndex += 2;
+
+ available -= 4 + len;
+ if (!available.isValid()) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ rv = ParseSvcParam(svcbIndex, key, value, len, aBuffer);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ svcbIndex += len;
+
+ // If this is an unknown key, we will simply ignore it.
+ // We also don't need to record SvcParamKeyMandatory
+ if (key == SvcParamKeyMandatory || !IsValidSvcParamKey(key)) {
+ continue;
+ }
+
+ if (value.mValue.is<SvcParamIpv4Hint>() ||
+ value.mValue.is<SvcParamIpv6Hint>()) {
+ aParsed.mHasIPHints = true;
+ }
+ if (value.mValue.is<SvcParamEchConfig>()) {
+ aParsed.mHasEchConfig = true;
+ aParsed.mEchConfig = value.mValue.as<SvcParamEchConfig>().mValue;
+ }
+ if (value.mValue.is<SvcParamODoHConfig>()) {
+ aParsed.mODoHConfig = value.mValue.as<SvcParamODoHConfig>().mValue;
+ }
+ aParsed.mSvcFieldValue.AppendElement(value);
+ }
+
+ return NS_OK;
+}
+
+Result<uint8_t, nsresult> DNSPacket::GetRCode() const {
+ if (mBodySize < 12) {
+ LOG(("DNSPacket::GetRCode - packet too small"));
+ return Err(NS_ERROR_ILLEGAL_VALUE);
+ }
+
+ return mResponse[3] & 0x0F;
+}
+
+Result<bool, nsresult> DNSPacket::RecursionAvailable() const {
+ if (mBodySize < 12) {
+ LOG(("DNSPacket::GetRCode - packet too small"));
+ return Err(NS_ERROR_ILLEGAL_VALUE);
+ }
+
+ return mResponse[3] & 0x80;
+}
+
+nsresult DNSPacket::DecodeInternal(
+ nsCString& aHost, enum TrrType aType, nsCString& aCname, bool aAllowRFC1918,
+ DOHresp& aResp, TypeRecordResultType& aTypeResult,
+ nsClassHashtable<nsCStringHashKey, DOHresp>& aAdditionalRecords,
+ uint32_t& aTTL, const unsigned char* aBuffer, uint32_t aLen) {
+ // The response has a 12 byte header followed by the question (returned)
+ // and then the answer. The answer section itself contains the name, type
+ // and class again and THEN the record data.
+
+ // www.example.com response:
+ // header:
+ // abcd 8180 0001 0001 0000 0000
+ // the question:
+ // 0377 7777 0765 7861 6d70 6c65 0363 6f6d 0000 0100 01
+ // the answer:
+ // 03 7777 7707 6578 616d 706c 6503 636f 6d00 0001 0001
+ // 0000 0080 0004 5db8 d822
+
+ unsigned int index = 12;
+ uint8_t length;
+ nsAutoCString host;
+ nsresult rv;
+ uint16_t extendedError = UINT16_MAX;
+
+ LOG(("doh decode %s %d bytes\n", aHost.get(), aLen));
+
+ aCname.Truncate();
+
+ if (aLen < 12) {
+ LOG(("TRR bad incoming DOH, eject!\n"));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ if (!mNativePacket && (aBuffer[0] || aBuffer[1])) {
+ LOG(("Packet ID is unexpectedly non-zero"));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ uint8_t rcode = mResponse[3] & 0x0F;
+ LOG(("TRR Decode %s RCODE %d\n", PromiseFlatCString(aHost).get(), rcode));
+
+ uint16_t questionRecords = get16bit(aBuffer, 4); // qdcount
+ // iterate over the single(?) host name in question
+ while (questionRecords) {
+ do {
+ if (aLen < (index + 1)) {
+ LOG(("TRR Decode 1 index: %u size: %u", index, aLen));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ length = static_cast<uint8_t>(aBuffer[index]);
+ if (length) {
+ if (host.Length()) {
+ host.Append(".");
+ }
+ if (aLen < (index + 1 + length)) {
+ LOG(("TRR Decode 2 index: %u size: %u len: %u", index, aLen, length));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ host.Append(((char*)aBuffer) + index + 1, length);
+ }
+ index += 1 + length; // skip length byte + label
+ } while (length);
+ if (aLen < (index + 4)) {
+ LOG(("TRR Decode 3 index: %u size: %u", index, aLen));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ index += 4; // skip question's type, class
+ questionRecords--;
+ }
+
+ // Figure out the number of answer records from ANCOUNT
+ uint16_t answerRecords = get16bit(aBuffer, 6);
+
+ LOG(("TRR Decode: %d answer records (%u bytes body) %s index=%u\n",
+ answerRecords, aLen, host.get(), index));
+
+ while (answerRecords) {
+ nsAutoCString qname;
+ rv = GetQname(qname, index, aBuffer, mBodySize);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ // 16 bit TYPE
+ if (aLen < (index + 2)) {
+ LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index + 2));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ uint16_t TYPE = get16bit(aBuffer, index);
+
+ index += 2;
+
+ // 16 bit class
+ if (aLen < (index + 2)) {
+ LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index + 2));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ uint16_t CLASS = get16bit(aBuffer, index);
+ if (kDNS_CLASS_IN != CLASS) {
+ LOG(("TRR bad CLASS (%u) at index %d\n", CLASS, index));
+ return NS_ERROR_UNEXPECTED;
+ }
+ index += 2;
+
+ // 32 bit TTL (seconds)
+ if (aLen < (index + 4)) {
+ LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ uint32_t TTL = get32bit(aBuffer, index);
+ index += 4;
+
+ // 16 bit RDLENGTH
+ if (aLen < (index + 2)) {
+ LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ uint16_t RDLENGTH = get16bit(aBuffer, index);
+ index += 2;
+
+ if (aLen < (index + RDLENGTH)) {
+ LOG(("TRR: Dohdecode:%d fail RDLENGTH=%d at index %d\n", __LINE__,
+ RDLENGTH, index));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ if ((TYPE != TRRTYPE_CNAME) && (TYPE != TRRTYPE_HTTPSSVC) &&
+ (TYPE != static_cast<uint16_t>(aType))) {
+ // Not the same type as was asked for nor CNAME
+ LOG(("TRR: Dohdecode:%d asked for type %d got %d\n", __LINE__, aType,
+ TYPE));
+ index += RDLENGTH;
+ answerRecords--;
+ continue;
+ }
+
+ // We check if the qname is a case-insensitive match for the host or the
+ // FQDN version of the host
+ bool responseMatchesQuestion =
+ (qname.Length() == aHost.Length() ||
+ (aHost.Length() == qname.Length() + 1 && aHost.Last() == '.')) &&
+ StringBeginsWith(aHost, qname, nsCaseInsensitiveCStringComparator);
+
+ if (responseMatchesQuestion) {
+ // RDATA
+ // - A (TYPE 1): 4 bytes
+ // - AAAA (TYPE 28): 16 bytes
+ // - NS (TYPE 2): N bytes
+
+ switch (TYPE) {
+ case TRRTYPE_A:
+ if (RDLENGTH != 4) {
+ LOG(("TRR bad length for A (%u)\n", RDLENGTH));
+ return NS_ERROR_UNEXPECTED;
+ }
+ rv = aResp.Add(TTL, aBuffer, index, RDLENGTH, aAllowRFC1918);
+ if (NS_FAILED(rv)) {
+ LOG(
+ ("TRR:DohDecode failed: local IP addresses or unknown IP "
+ "family\n"));
+ return rv;
+ }
+ break;
+ case TRRTYPE_AAAA:
+ if (RDLENGTH != 16) {
+ LOG(("TRR bad length for AAAA (%u)\n", RDLENGTH));
+ return NS_ERROR_UNEXPECTED;
+ }
+ rv = aResp.Add(TTL, aBuffer, index, RDLENGTH, aAllowRFC1918);
+ if (NS_FAILED(rv)) {
+ LOG(("TRR got unique/local IPv6 address!\n"));
+ return rv;
+ }
+ break;
+
+ case TRRTYPE_NS:
+ break;
+ case TRRTYPE_CNAME:
+ if (aCname.IsEmpty()) {
+ nsAutoCString qname;
+ unsigned int qnameindex = index;
+ rv = GetQname(qname, qnameindex, aBuffer, mBodySize);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (!qname.IsEmpty()) {
+ ToLowerCase(qname);
+ aCname = qname;
+ LOG(("DNSPacket::DohDecode CNAME host %s => %s\n", host.get(),
+ aCname.get()));
+ } else {
+ LOG(("DNSPacket::DohDecode empty CNAME for host %s!\n",
+ host.get()));
+ }
+ } else {
+ LOG(("DNSPacket::DohDecode CNAME - ignoring another entry\n"));
+ }
+ break;
+ case TRRTYPE_TXT: {
+ // TXT record RRDATA sections are a series of character-strings
+ // each character string is a length byte followed by that many data
+ // bytes
+ nsAutoCString txt;
+ unsigned int txtIndex = index;
+ uint16_t available = RDLENGTH;
+
+ while (available > 0) {
+ uint8_t characterStringLen = aBuffer[txtIndex++];
+ available--;
+ if (characterStringLen > available) {
+ LOG(("DNSPacket::DohDecode MALFORMED TXT RECORD\n"));
+ break;
+ }
+ txt.Append((const char*)(&aBuffer[txtIndex]), characterStringLen);
+ txtIndex += characterStringLen;
+ available -= characterStringLen;
+ }
+
+ if (!aTypeResult.is<TypeRecordTxt>()) {
+ aTypeResult = AsVariant(CopyableTArray<nsCString>());
+ }
+
+ {
+ auto& results = aTypeResult.as<TypeRecordTxt>();
+ results.AppendElement(txt);
+ }
+ if (aTTL > TTL) {
+ aTTL = TTL;
+ }
+ LOG(("DNSPacket::DohDecode TXT host %s => %s\n", host.get(),
+ txt.get()));
+
+ break;
+ }
+ case TRRTYPE_HTTPSSVC: {
+ struct SVCB parsed;
+
+ if (aType != TRRTYPE_HTTPSSVC) {
+ // Ignore the entry that we just parsed if we didn't ask for it.
+ break;
+ }
+
+ rv = ParseHTTPS(RDLENGTH, parsed, index, aBuffer, mBodySize,
+ mOriginHost ? *mOriginHost : qname);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (parsed.mSvcDomainName.IsEmpty() &&
+ parsed.mSvcFieldPriority == 0) {
+ // For AliasMode SVCB RRs, a TargetName of "." indicates that the
+ // service is not available or does not exist.
+ continue;
+ }
+
+ // Check for AliasForm
+ if (aCname.IsEmpty() && parsed.mSvcFieldPriority == 0) {
+ // Alias form SvcDomainName must not have the "." value (empty)
+ if (parsed.mSvcDomainName.IsEmpty()) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ aCname = parsed.mSvcDomainName;
+ // If aliasForm is present, Service form must be ignored.
+ aTypeResult = mozilla::AsVariant(Nothing());
+ ToLowerCase(aCname);
+ LOG(("DNSPacket::DohDecode HTTPSSVC AliasForm host %s => %s\n",
+ host.get(), aCname.get()));
+ break;
+ }
+
+ aTTL = TTL;
+
+ if (!aTypeResult.is<TypeRecordHTTPSSVC>()) {
+ aTypeResult = mozilla::AsVariant(CopyableTArray<SVCB>());
+ }
+ {
+ auto& results = aTypeResult.as<TypeRecordHTTPSSVC>();
+ results.AppendElement(parsed);
+ }
+
+ break;
+ }
+ default:
+ // skip unknown record types
+ LOG(("TRR unsupported TYPE (%u) RDLENGTH %u\n", TYPE, RDLENGTH));
+ break;
+ }
+ } else {
+ LOG(("TRR asked for %s data but got %s\n", aHost.get(), qname.get()));
+ }
+
+ index += RDLENGTH;
+ LOG(("done with record type %u len %u index now %u of %u\n", TYPE, RDLENGTH,
+ index, aLen));
+ answerRecords--;
+ }
+
+ // NSCOUNT
+ uint16_t nsRecords = get16bit(aBuffer, 8);
+ LOG(("TRR Decode: %d ns records (%u bytes body)\n", nsRecords, aLen));
+ while (nsRecords) {
+ rv = PassQName(index, aBuffer);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (aLen < (index + 8)) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ index += 2; // type
+ index += 2; // class
+ index += 4; // ttl
+
+ // 16 bit RDLENGTH
+ if (aLen < (index + 2)) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ uint16_t RDLENGTH = get16bit(aBuffer, index);
+ index += 2;
+ if (aLen < (index + RDLENGTH)) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ index += RDLENGTH;
+ LOG(("done with nsRecord now %u of %u\n", index, aLen));
+ nsRecords--;
+ }
+
+ // additional resource records
+ uint16_t arRecords = get16bit(aBuffer, 10);
+ LOG(("TRR Decode: %d additional resource records (%u bytes body)\n",
+ arRecords, aLen));
+
+ while (arRecords) {
+ nsAutoCString qname;
+ rv = GetQname(qname, index, aBuffer, mBodySize);
+ if (NS_FAILED(rv)) {
+ LOG(("Bad qname for additional record"));
+ return rv;
+ }
+
+ if (aLen < (index + 8)) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ uint16_t type = get16bit(aBuffer, index);
+ index += 2;
+ // The next two bytes encode class
+ // (or udpPayloadSize when type is TRRTYPE_OPT)
+ uint16_t cls = get16bit(aBuffer, index);
+ index += 2;
+ // The next 4 bytes encode TTL
+ // (or extRCode + ednsVersion + flags when type is TRRTYPE_OPT)
+ uint32_t ttl = get32bit(aBuffer, index);
+ index += 4;
+ // cls and ttl are unused when type is TRRTYPE_OPT
+
+ // 16 bit RDLENGTH
+ if (aLen < (index + 2)) {
+ LOG(("Record too small"));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ uint16_t rdlength = get16bit(aBuffer, index);
+ index += 2;
+ if (aLen < (index + rdlength)) {
+ LOG(("rdlength too big"));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ auto parseRecord = [&]() {
+ LOG(("Parsing additional record type: %u", type));
+ auto* entry = aAdditionalRecords.GetOrInsertNew(qname);
+
+ switch (type) {
+ case TRRTYPE_A:
+ if (kDNS_CLASS_IN != cls) {
+ LOG(("NOT IN - returning"));
+ return;
+ }
+ if (rdlength != 4) {
+ LOG(("TRR bad length for A (%u)\n", rdlength));
+ return;
+ }
+ rv = entry->Add(ttl, aBuffer, index, rdlength, aAllowRFC1918);
+ if (NS_FAILED(rv)) {
+ LOG(
+ ("TRR:DohDecode failed: local IP addresses or unknown IP "
+ "family\n"));
+ return;
+ }
+ break;
+ case TRRTYPE_AAAA:
+ if (kDNS_CLASS_IN != cls) {
+ LOG(("NOT IN - returning"));
+ return;
+ }
+ if (rdlength != 16) {
+ LOG(("TRR bad length for AAAA (%u)\n", rdlength));
+ return;
+ }
+ rv = entry->Add(ttl, aBuffer, index, rdlength, aAllowRFC1918);
+ if (NS_FAILED(rv)) {
+ LOG(("TRR got unique/local IPv6 address!\n"));
+ return;
+ }
+ break;
+ case TRRTYPE_OPT: { // OPT
+ LOG(("Parsing opt rdlen: %u", rdlength));
+ unsigned int offset = 0;
+ while (offset + 2 <= rdlength) {
+ uint16_t optCode = get16bit(aBuffer, index + offset);
+ LOG(("optCode: %u", optCode));
+ offset += 2;
+ if (offset + 2 > rdlength) {
+ break;
+ }
+ uint16_t optLen = get16bit(aBuffer, index + offset);
+ LOG(("optLen: %u", optLen));
+ offset += 2;
+ if (offset + optLen > rdlength) {
+ LOG(("offset: %u, optLen: %u, rdlen: %u", offset, optLen,
+ rdlength));
+ break;
+ }
+
+ LOG(("OPT: code: %u len:%u", optCode, optLen));
+
+ if (optCode != 15) {
+ offset += optLen;
+ continue;
+ }
+
+ // optCode == 15; Extended DNS error
+
+ if (offset + 2 > rdlength || optLen < 2) {
+ break;
+ }
+ extendedError = get16bit(aBuffer, index + offset);
+
+ LOG(("Extended error code: %u message: %s", extendedError,
+ nsAutoCString((char*)aBuffer + index + offset + 2, optLen - 2)
+ .get()));
+ offset += optLen;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ };
+
+ parseRecord();
+
+ index += rdlength;
+ LOG(("done with additional rr now %u of %u\n", index, aLen));
+ arRecords--;
+ }
+
+ if (index != aLen) {
+ LOG(("DohDecode failed to parse entire response body, %u out of %u bytes\n",
+ index, aLen));
+ // failed to parse 100%, do not continue
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ if (aType == TRRTYPE_NS && rcode != 0) {
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ if ((aType != TRRTYPE_NS) && aCname.IsEmpty() && aResp.mAddresses.IsEmpty() &&
+ aTypeResult.is<TypeRecordEmpty>()) {
+ // no entries were stored!
+ LOG(("TRR: No entries were stored!\n"));
+
+ if (extendedError != UINT16_MAX &&
+ StaticPrefs::network_trr_hard_fail_on_extended_error() &&
+ hardFail(extendedError)) {
+ return NS_ERROR_DEFINITIVE_UNKNOWN_HOST;
+ }
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ // https://tools.ietf.org/html/draft-ietf-dnsop-svcb-httpssvc-03#page-14
+ // If one or more SVCB records of ServiceForm SvcRecordType are returned for
+ // HOST, clients should select the highest-priority option with acceptable
+ // parameters.
+ if (aTypeResult.is<TypeRecordHTTPSSVC>()) {
+ auto& results = aTypeResult.as<TypeRecordHTTPSSVC>();
+ results.Sort();
+ }
+
+ return NS_OK;
+}
+
+//
+// DohDecode() collects the TTL and the IP addresses in the response
+//
+nsresult DNSPacket::Decode(
+ nsCString& aHost, enum TrrType aType, nsCString& aCname, bool aAllowRFC1918,
+ DOHresp& aResp, TypeRecordResultType& aTypeResult,
+ nsClassHashtable<nsCStringHashKey, DOHresp>& aAdditionalRecords,
+ uint32_t& aTTL) {
+ nsresult rv =
+ DecodeInternal(aHost, aType, aCname, aAllowRFC1918, aResp, aTypeResult,
+ aAdditionalRecords, aTTL, mResponse, mBodySize);
+ mStatus = rv;
+ return rv;
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/DNSPacket.h b/netwerk/dns/DNSPacket.h
new file mode 100644
index 0000000000..943bcfe246
--- /dev/null
+++ b/netwerk/dns/DNSPacket.h
@@ -0,0 +1,105 @@
+/* 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/. */
+
+#ifndef mozilla_net_DNSPacket_h__
+#define mozilla_net_DNSPacket_h__
+
+#include "mozilla/Maybe.h"
+#include "mozilla/Result.h"
+#include "nsClassHashtable.h"
+#include "nsIDNSService.h"
+#include "DNS.h"
+#include "DNSByTypeRecord.h"
+
+#include <functional>
+
+namespace mozilla {
+namespace net {
+
+class DOHresp {
+ public:
+ nsresult Add(uint32_t TTL, unsigned char const* dns, unsigned int index,
+ uint16_t len, bool aLocalAllowed);
+ nsTArray<NetAddr> mAddresses;
+ uint32_t mTtl = 0;
+};
+
+// the values map to RFC1035 type identifiers
+enum TrrType {
+ TRRTYPE_A = 1,
+ TRRTYPE_NS = 2,
+ TRRTYPE_CNAME = 5,
+ TRRTYPE_AAAA = 28,
+ TRRTYPE_OPT = 41,
+ TRRTYPE_TXT = 16,
+ TRRTYPE_HTTPSSVC = nsIDNSService::RESOLVE_TYPE_HTTPSSVC, // 65
+};
+
+class DNSPacket {
+ public:
+ // Never accept larger DOH responses than this as that would indicate
+ // something is wrong. Typical ones are much smaller.
+ static const unsigned int MAX_SIZE = 3200;
+
+ DNSPacket() = default;
+ virtual ~DNSPacket() = default;
+
+ Result<uint8_t, nsresult> GetRCode() const;
+ Result<bool, nsresult> RecursionAvailable() const;
+
+ // Called in order to feed data into the buffer.
+ nsresult OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream,
+ uint64_t aOffset, const uint32_t aCount);
+
+ // Encodes the name request into a buffer that represents a DNS packet
+ virtual nsresult EncodeRequest(nsCString& aBody, const nsACString& aHost,
+ uint16_t aType, bool aDisableECS);
+
+ // Decodes the DNS response and extracts the responses, additional records,
+ // etc. XXX: This should probably be refactored to reduce the number of
+ // output parameters and have a common format for different record types.
+ virtual nsresult Decode(
+ nsCString& aHost, enum TrrType aType, nsCString& aCname,
+ bool aAllowRFC1918, DOHresp& aResp, TypeRecordResultType& aTypeResult,
+ nsClassHashtable<nsCStringHashKey, DOHresp>& aAdditionalRecords,
+ uint32_t& aTTL);
+
+ void SetOriginHost(const Maybe<nsCString>& aHost) { mOriginHost = aHost; }
+
+ nsresult FillBuffer(std::function<int(unsigned char response[MAX_SIZE])>&&);
+
+ static nsresult ParseHTTPS(uint16_t aRDLen, struct SVCB& aParsed,
+ unsigned int aIndex, const unsigned char* aBuffer,
+ unsigned int aBodySize,
+ const nsACString& aOriginHost);
+ void SetNativePacket(bool aNative) { mNativePacket = aNative; }
+
+ protected:
+ nsresult PassQName(unsigned int& index, const unsigned char* aBuffer);
+ static nsresult GetQname(nsACString& aQname, unsigned int& aIndex,
+ const unsigned char* aBuffer,
+ unsigned int aBodySize);
+ static nsresult ParseSvcParam(unsigned int svcbIndex, uint16_t key,
+ SvcFieldValue& field, uint16_t length,
+ const unsigned char* aBuffer);
+ nsresult DecodeInternal(
+ nsCString& aHost, enum TrrType aType, nsCString& aCname,
+ bool aAllowRFC1918, DOHresp& aResp, TypeRecordResultType& aTypeResult,
+ nsClassHashtable<nsCStringHashKey, DOHresp>& aAdditionalRecords,
+ uint32_t& aTTL, const unsigned char* aBuffer, uint32_t aLen);
+
+ // The response buffer.
+ unsigned char mResponse[MAX_SIZE]{};
+ unsigned int mBodySize = 0;
+ // True when decoding a DNS packet received from OS. Decoding will
+ // not panic if packet ID is not zero.
+ bool mNativePacket = false;
+ nsresult mStatus = NS_OK;
+ Maybe<nsCString> mOriginHost;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_DNSPacket_h__
diff --git a/netwerk/dns/DNSRequestBase.h b/netwerk/dns/DNSRequestBase.h
new file mode 100644
index 0000000000..7b26846f5e
--- /dev/null
+++ b/netwerk/dns/DNSRequestBase.h
@@ -0,0 +1,152 @@
+/* -*- 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/. */
+
+#ifndef mozilla_net_DNSRequestBase_h
+#define mozilla_net_DNSRequestBase_h
+
+#include "mozilla/net/PDNSRequestParent.h"
+#include "nsICancelable.h"
+#include "nsIDNSRecord.h"
+#include "nsIDNSListener.h"
+#include "nsIDNSByTypeRecord.h"
+#include "nsIEventTarget.h"
+
+namespace mozilla {
+namespace net {
+
+class DNSRequestActor;
+class DNSRequestChild;
+class DNSRequestHandler;
+class DNSRequestParent;
+class DNSRequestSender;
+
+// A base class for DNSRequestSender and DNSRequestHandler.
+// Provide interfaces for processing DNS requests.
+class DNSRequestBase : public nsISupports {
+ public:
+ explicit DNSRequestBase() = default;
+
+ void SetIPCActor(DNSRequestActor* aActor);
+
+ virtual void OnRecvCancelDNSRequest(const nsCString& hostName,
+ const nsCString& trrServer,
+ const int32_t& port, const uint16_t& type,
+ const OriginAttributes& originAttributes,
+ const nsIDNSService::DNSFlags& flags,
+ const nsresult& reason) = 0;
+ virtual bool OnRecvLookupCompleted(const DNSRequestResponse& reply) = 0;
+ virtual void OnIPCActorDestroy() = 0;
+
+ virtual DNSRequestSender* AsDNSRequestSender() = 0;
+ virtual DNSRequestHandler* AsDNSRequestHandler() = 0;
+
+ protected:
+ virtual ~DNSRequestBase() = default;
+
+ RefPtr<DNSRequestActor> mIPCActor;
+};
+
+// DNSRequestSender is used to send an IPC request to DNSRequestHandler and
+// deliver the result to nsIDNSListener.
+// Note this class could be used both in content process and parent process.
+class DNSRequestSender final : public DNSRequestBase, public nsICancelable {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSICANCELABLE
+
+ DNSRequestSender(const nsACString& aHost, const nsACString& aTrrServer,
+ int32_t aPort, const uint16_t& aType,
+ const OriginAttributes& aOriginAttributes,
+ const nsIDNSService::DNSFlags& aFlags,
+ nsIDNSListener* aListener, nsIEventTarget* target);
+
+ void OnRecvCancelDNSRequest(const nsCString& hostName,
+ const nsCString& trrServer, const int32_t& port,
+ const uint16_t& type,
+ const OriginAttributes& originAttributes,
+ const nsIDNSService::DNSFlags& flags,
+ const nsresult& reason) override;
+ bool OnRecvLookupCompleted(const DNSRequestResponse& reply) override;
+ void OnIPCActorDestroy() override;
+
+ // Sends IPDL request to DNSRequestHandler
+ void StartRequest();
+ void CallOnLookupComplete();
+
+ DNSRequestSender* AsDNSRequestSender() override { return this; }
+ DNSRequestHandler* AsDNSRequestHandler() override { return nullptr; }
+
+ private:
+ friend class ChildDNSService;
+ virtual ~DNSRequestSender() = default;
+
+ nsCOMPtr<nsIDNSListener> mListener;
+ nsCOMPtr<nsIEventTarget> mTarget;
+ nsCOMPtr<nsIDNSRecord> mResultRecord;
+ nsresult mResultStatus = NS_OK;
+ nsCString mHost;
+ nsCString mTrrServer;
+ int32_t mPort;
+ uint16_t mType = 0;
+ const OriginAttributes mOriginAttributes;
+ nsIDNSService::DNSFlags mFlags = nsIDNSService::RESOLVE_DEFAULT_FLAGS;
+};
+
+// DNSRequestHandler handles the dns request and sends the result back via IPC.
+// Note this class could be used both in parent process and socket process.
+class DNSRequestHandler final : public DNSRequestBase, public nsIDNSListener {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIDNSLISTENER
+
+ DNSRequestHandler() = default;
+
+ void DoAsyncResolve(const nsACString& hostname, const nsACString& trrServer,
+ int32_t port, uint16_t type,
+ const OriginAttributes& originAttributes,
+ nsIDNSService::DNSFlags flags);
+
+ void OnRecvCancelDNSRequest(const nsCString& hostName,
+ const nsCString& trrServer, const int32_t& port,
+ const uint16_t& type,
+ const OriginAttributes& originAttributes,
+ const nsIDNSService::DNSFlags& flags,
+ const nsresult& reason) override;
+ bool OnRecvLookupCompleted(const DNSRequestResponse& reply) override;
+ void OnIPCActorDestroy() override;
+
+ DNSRequestSender* AsDNSRequestSender() override { return nullptr; }
+ DNSRequestHandler* AsDNSRequestHandler() override { return this; }
+
+ private:
+ virtual ~DNSRequestHandler() = default;
+
+ nsIDNSService::DNSFlags mFlags = nsIDNSService::RESOLVE_DEFAULT_FLAGS;
+};
+
+// Provides some common methods for DNSRequestChild and DNSRequestParent.
+class DNSRequestActor {
+ public:
+ NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+
+ explicit DNSRequestActor(DNSRequestBase* aRequest) : mDNSRequest(aRequest) {}
+
+ virtual bool CanSend() const = 0;
+ virtual DNSRequestChild* AsDNSRequestChild() = 0;
+ virtual DNSRequestParent* AsDNSRequestParent() = 0;
+
+ DNSRequestBase* GetDNSRequest() { return mDNSRequest.get(); };
+
+ protected:
+ virtual ~DNSRequestActor() = default;
+
+ RefPtr<DNSRequestBase> mDNSRequest;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_DNSRequestBase_h
diff --git a/netwerk/dns/DNSRequestChild.cpp b/netwerk/dns/DNSRequestChild.cpp
new file mode 100644
index 0000000000..ec0f56b3f1
--- /dev/null
+++ b/netwerk/dns/DNSRequestChild.cpp
@@ -0,0 +1,574 @@
+/* -*- 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 "mozilla/dom/ContentChild.h"
+#include "mozilla/net/ChildDNSService.h"
+#include "mozilla/net/DNSByTypeRecord.h"
+#include "mozilla/net/DNSRequestChild.h"
+#include "mozilla/net/DNSRequestParent.h"
+#include "mozilla/net/NeckoChild.h"
+#include "mozilla/net/SocketProcessChild.h"
+#include "mozilla/SchedulerGroup.h"
+#include "mozilla/net/SocketProcessParent.h"
+#include "mozilla/Unused.h"
+#include "nsIDNSRecord.h"
+#include "nsIDNSByTypeRecord.h"
+#include "nsHostResolver.h"
+#include "nsIOService.h"
+#include "nsTArray.h"
+#include "nsNetAddr.h"
+#include "nsThreadUtils.h"
+
+using namespace mozilla::ipc;
+
+namespace mozilla {
+namespace net {
+
+void DNSRequestBase::SetIPCActor(DNSRequestActor* aActor) {
+ mIPCActor = aActor;
+}
+
+//-----------------------------------------------------------------------------
+// ChildDNSRecord:
+// A simple class to provide nsIDNSRecord on the child
+//-----------------------------------------------------------------------------
+
+class ChildDNSRecord : public nsIDNSAddrRecord {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIDNSRECORD
+ NS_DECL_NSIDNSADDRRECORD
+
+ ChildDNSRecord(const DNSRecord& reply, nsIDNSService::DNSFlags flags);
+
+ private:
+ virtual ~ChildDNSRecord() = default;
+
+ nsCString mCanonicalName;
+ nsTArray<NetAddr> mAddresses;
+ uint32_t mCurrent = 0; // addr iterator
+ nsIDNSService::DNSFlags mFlags = nsIDNSService::RESOLVE_DEFAULT_FLAGS;
+ double mTrrFetchDuration = 0;
+ double mTrrFetchDurationNetworkOnly = 0;
+ bool mIsTRR = false;
+ bool mResolvedInSocketProcess = false;
+ nsIRequest::TRRMode mEffectiveTRRMode = nsIRequest::TRR_DEFAULT_MODE;
+ nsITRRSkipReason::value mTRRSkipReason = nsITRRSkipReason::TRR_UNSET;
+ uint32_t mTTL = 0;
+};
+
+NS_IMPL_ISUPPORTS(ChildDNSRecord, nsIDNSRecord, nsIDNSAddrRecord)
+
+ChildDNSRecord::ChildDNSRecord(const DNSRecord& reply,
+ nsIDNSService::DNSFlags flags)
+ : mFlags(flags) {
+ mCanonicalName = reply.canonicalName();
+ mTrrFetchDuration = reply.trrFetchDuration();
+ mTrrFetchDurationNetworkOnly = reply.trrFetchDurationNetworkOnly();
+ mIsTRR = reply.isTRR();
+ // When ChildDNSRecord is created in parent process, we know this is case that
+ // DNS resolution is done in socket process.
+ mResolvedInSocketProcess = XRE_IsParentProcess();
+ mEffectiveTRRMode = reply.effectiveTRRMode();
+
+ // A shame IPDL gives us no way to grab ownership of array: so copy it.
+ const nsTArray<NetAddr>& addrs = reply.addrs();
+ mAddresses = addrs.Clone();
+ mTTL = reply.ttl();
+}
+
+//-----------------------------------------------------------------------------
+// ChildDNSRecord::nsIDNSAddrRecord
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+ChildDNSRecord::GetCanonicalName(nsACString& result) {
+ if (!(mFlags & nsHostResolver::RES_CANON_NAME)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ result = mCanonicalName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSRecord::IsTRR(bool* retval) {
+ *retval = mIsTRR;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSRecord::ResolvedInSocketProcess(bool* retval) {
+ *retval = mResolvedInSocketProcess;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSRecord::GetTrrFetchDuration(double* aTime) {
+ *aTime = mTrrFetchDuration;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSRecord::GetTrrFetchDurationNetworkOnly(double* aTime) {
+ *aTime = mTrrFetchDurationNetworkOnly;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSRecord::GetNextAddr(uint16_t port, NetAddr* addr) {
+ if (mCurrent >= mAddresses.Length()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ memcpy(addr, &mAddresses[mCurrent++], sizeof(NetAddr));
+
+ // both Ipv4/6 use same bits for port, so safe to just use ipv4's field
+ addr->inet.port = htons(port);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSRecord::GetAddresses(nsTArray<NetAddr>& aAddressArray) {
+ aAddressArray = mAddresses.Clone();
+ return NS_OK;
+}
+
+// shamelessly copied from nsDNSRecord
+NS_IMETHODIMP
+ChildDNSRecord::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;
+}
+
+// also copied from nsDNSRecord
+NS_IMETHODIMP
+ChildDNSRecord::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
+ChildDNSRecord::HasMore(bool* result) {
+ *result = mCurrent < mAddresses.Length();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSRecord::Rewind() {
+ mCurrent = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSRecord::ReportUnusable(uint16_t aPort) {
+ // "We thank you for your feedback" == >/dev/null
+ // TODO: we could send info back to parent.
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSRecord::GetEffectiveTRRMode(nsIRequest::TRRMode* aMode) {
+ *aMode = mEffectiveTRRMode;
+ return NS_OK;
+}
+
+NS_IMETHODIMP ChildDNSRecord::GetTrrSkipReason(
+ nsITRRSkipReason::value* aTrrSkipReason) {
+ *aTrrSkipReason = mTRRSkipReason;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSRecord::GetTtl(uint32_t* aTtl) {
+ *aTtl = mTTL;
+ return NS_OK;
+}
+
+class ChildDNSByTypeRecord : public nsIDNSByTypeRecord,
+ public nsIDNSTXTRecord,
+ public nsIDNSHTTPSSVCRecord,
+ public DNSHTTPSSVCRecordBase {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIDNSRECORD
+ NS_DECL_NSIDNSBYTYPERECORD
+ NS_DECL_NSIDNSTXTRECORD
+ NS_DECL_NSIDNSHTTPSSVCRECORD
+
+ explicit ChildDNSByTypeRecord(const TypeRecordResultType& reply,
+ const nsACString& aHost, uint32_t aTTL);
+
+ private:
+ virtual ~ChildDNSByTypeRecord() = default;
+
+ TypeRecordResultType mResults = AsVariant(mozilla::Nothing());
+ bool mAllRecordsExcluded = false;
+ uint32_t mTTL = 0;
+};
+
+NS_IMPL_ISUPPORTS(ChildDNSByTypeRecord, nsIDNSByTypeRecord, nsIDNSRecord,
+ nsIDNSTXTRecord, nsIDNSHTTPSSVCRecord)
+
+ChildDNSByTypeRecord::ChildDNSByTypeRecord(const TypeRecordResultType& reply,
+ const nsACString& aHost,
+ uint32_t aTTL)
+ : DNSHTTPSSVCRecordBase(aHost) {
+ mResults = reply;
+ mTTL = aTTL;
+}
+
+NS_IMETHODIMP
+ChildDNSByTypeRecord::GetType(uint32_t* aType) {
+ *aType = mResults.match(
+ [](TypeRecordEmpty&) {
+ MOZ_ASSERT(false, "This should never be the case");
+ return nsIDNSService::RESOLVE_TYPE_DEFAULT;
+ },
+ [](TypeRecordTxt&) { return nsIDNSService::RESOLVE_TYPE_TXT; },
+ [](TypeRecordHTTPSSVC&) { return nsIDNSService::RESOLVE_TYPE_HTTPSSVC; });
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSByTypeRecord::GetRecords(CopyableTArray<nsCString>& aRecords) {
+ if (!mResults.is<TypeRecordTxt>()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ aRecords = mResults.as<CopyableTArray<nsCString>>();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSByTypeRecord::GetRecordsAsOneString(nsACString& aRecords) {
+ // deep copy
+ if (!mResults.is<TypeRecordTxt>()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ auto& results = mResults.as<CopyableTArray<nsCString>>();
+ for (uint32_t i = 0; i < results.Length(); i++) {
+ aRecords.Append(results[i]);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSByTypeRecord::GetRecords(nsTArray<RefPtr<nsISVCBRecord>>& aRecords) {
+ if (!mResults.is<TypeRecordHTTPSSVC>()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ auto& results = mResults.as<TypeRecordHTTPSSVC>();
+
+ for (const SVCB& r : results) {
+ RefPtr<nsISVCBRecord> rec = new SVCBRecord(r);
+ aRecords.AppendElement(rec);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSByTypeRecord::GetServiceModeRecord(bool aNoHttp2, bool aNoHttp3,
+ nsISVCBRecord** aRecord) {
+ if (!mResults.is<TypeRecordHTTPSSVC>()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ auto& results = mResults.as<TypeRecordHTTPSSVC>();
+ nsCOMPtr<nsISVCBRecord> result = GetServiceModeRecordInternal(
+ aNoHttp2, aNoHttp3, results, mAllRecordsExcluded);
+ if (!result) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ result.forget(aRecord);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSByTypeRecord::GetAllRecordsWithEchConfig(
+ bool aNoHttp2, bool aNoHttp3, bool* aAllRecordsHaveEchConfig,
+ bool* aAllRecordsInH3ExcludedList,
+ nsTArray<RefPtr<nsISVCBRecord>>& aResult) {
+ if (!mResults.is<TypeRecordHTTPSSVC>()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ auto& records = mResults.as<TypeRecordHTTPSSVC>();
+ GetAllRecordsWithEchConfigInternal(aNoHttp2, aNoHttp3, records,
+ aAllRecordsHaveEchConfig,
+ aAllRecordsInH3ExcludedList, aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSByTypeRecord::GetHasIPAddresses(bool* aResult) {
+ NS_ENSURE_ARG(aResult);
+
+ if (!mResults.is<TypeRecordHTTPSSVC>()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ auto& results = mResults.as<TypeRecordHTTPSSVC>();
+ *aResult = HasIPAddressesInternal(results);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSByTypeRecord::GetAllRecordsExcluded(bool* aResult) {
+ NS_ENSURE_ARG(aResult);
+
+ if (!mResults.is<TypeRecordHTTPSSVC>()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ *aResult = mAllRecordsExcluded;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSByTypeRecord::GetResults(mozilla::net::TypeRecordResultType* aResults) {
+ *aResults = mResults;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSByTypeRecord::GetTtl(uint32_t* aResult) {
+ *aResult = mTTL;
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// DNSRequestSender
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS(DNSRequestSender, nsICancelable)
+
+DNSRequestSender::DNSRequestSender(const nsACString& aHost,
+ const nsACString& aTrrServer, int32_t aPort,
+ const uint16_t& aType,
+ const OriginAttributes& aOriginAttributes,
+ const nsIDNSService::DNSFlags& aFlags,
+ nsIDNSListener* aListener,
+ nsIEventTarget* target)
+ : mListener(aListener),
+ mTarget(target),
+ mResultStatus(NS_OK),
+ mHost(aHost),
+ mTrrServer(aTrrServer),
+ mPort(aPort),
+ mType(aType),
+ mOriginAttributes(aOriginAttributes),
+ mFlags(aFlags) {}
+
+void DNSRequestSender::OnRecvCancelDNSRequest(
+ const nsCString& hostName, const nsCString& trrServer, const int32_t& port,
+ const uint16_t& type, const OriginAttributes& originAttributes,
+ const nsIDNSService::DNSFlags& flags, const nsresult& reason) {}
+
+NS_IMETHODIMP
+DNSRequestSender::Cancel(nsresult reason) {
+ if (!mIPCActor) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ // We can only do IPC on the MainThread
+ nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction(
+ "net::CancelDNSRequestEvent",
+ [actor(mIPCActor), host(mHost), trrServer(mTrrServer), port(mPort),
+ type(mType), originAttributes(mOriginAttributes), flags(mFlags),
+ reason]() {
+ if (!actor->CanSend()) {
+ return;
+ }
+
+ if (DNSRequestChild* child = actor->AsDNSRequestChild()) {
+ Unused << child->SendCancelDNSRequest(
+ host, trrServer, port, type, originAttributes, flags, reason);
+ } else if (DNSRequestParent* parent = actor->AsDNSRequestParent()) {
+ Unused << parent->SendCancelDNSRequest(
+ host, trrServer, port, type, originAttributes, flags, reason);
+ }
+ });
+ SchedulerGroup::Dispatch(runnable.forget());
+ return NS_OK;
+}
+
+void DNSRequestSender::StartRequest() {
+ // we can only do IPC on the MainThread
+ if (!NS_IsMainThread()) {
+ SchedulerGroup::Dispatch(
+ NewRunnableMethod("net::DNSRequestSender::StartRequest", this,
+ &DNSRequestSender::StartRequest));
+ return;
+ }
+
+ if (RefPtr<DNSRequestChild> child = mIPCActor->AsDNSRequestChild()) {
+ if (XRE_IsContentProcess()) {
+ mozilla::dom::ContentChild* cc =
+ static_cast<mozilla::dom::ContentChild*>(gNeckoChild->Manager());
+ if (cc->IsShuttingDown()) {
+ return;
+ }
+
+ // Send request to Parent process.
+ gNeckoChild->SendPDNSRequestConstructor(child, mHost, mTrrServer, mPort,
+ mType, mOriginAttributes, mFlags);
+ } else if (XRE_IsSocketProcess()) {
+ // DNS resolution is done in the parent process. Send a DNS request to
+ // parent process.
+ MOZ_ASSERT(!nsIOService::UseSocketProcess());
+
+ SocketProcessChild* socketProcessChild =
+ SocketProcessChild::GetSingleton();
+ if (!socketProcessChild->CanSend()) {
+ return;
+ }
+
+ MOZ_ALWAYS_TRUE(socketProcessChild->SendPDNSRequestConstructor(
+ child, mHost, mTrrServer, mPort, mType, mOriginAttributes, mFlags));
+ } else {
+ MOZ_ASSERT(false, "Wrong process");
+ return;
+ }
+ } else if (DNSRequestParent* parent = mIPCActor->AsDNSRequestParent()) {
+ // DNS resolution is done in the socket process. Send a DNS request to
+ // socket process.
+ MOZ_ASSERT(nsIOService::UseSocketProcess());
+
+ RefPtr<DNSRequestParent> requestParent = parent;
+ RefPtr<DNSRequestSender> self = this;
+ auto task = [requestParent, self]() {
+ Unused << SocketProcessParent::GetSingleton()->SendPDNSRequestConstructor(
+ requestParent, self->mHost, self->mTrrServer, self->mPort,
+ self->mType, self->mOriginAttributes, self->mFlags);
+ };
+ if (!gIOService->SocketProcessReady()) {
+ gIOService->CallOrWaitForSocketProcess(std::move(task));
+ return;
+ }
+
+ task();
+ }
+}
+
+void DNSRequestSender::CallOnLookupComplete() {
+ MOZ_ASSERT(mListener);
+ mListener->OnLookupComplete(this, mResultRecord, mResultStatus);
+}
+
+bool DNSRequestSender::OnRecvLookupCompleted(const DNSRequestResponse& reply) {
+ MOZ_ASSERT(mListener);
+
+ switch (reply.type()) {
+ case DNSRequestResponse::TDNSRecord: {
+ mResultRecord = new ChildDNSRecord(reply.get_DNSRecord(), mFlags);
+ break;
+ }
+ case DNSRequestResponse::Tnsresult: {
+ mResultStatus = reply.get_nsresult();
+ break;
+ }
+ case DNSRequestResponse::TIPCTypeRecord: {
+ MOZ_ASSERT(mType != nsIDNSService::RESOLVE_TYPE_DEFAULT);
+ mResultRecord =
+ new ChildDNSByTypeRecord(reply.get_IPCTypeRecord().mData, mHost,
+ reply.get_IPCTypeRecord().mTTL);
+ break;
+ }
+ default:
+ MOZ_ASSERT_UNREACHABLE("unknown type");
+ return false;
+ }
+
+ MOZ_ASSERT(NS_IsMainThread());
+
+ bool targetIsMain = false;
+ if (!mTarget) {
+ targetIsMain = true;
+ } else {
+ mTarget->IsOnCurrentThread(&targetIsMain);
+ }
+
+ if (targetIsMain) {
+ CallOnLookupComplete();
+ } else {
+ nsCOMPtr<nsIRunnable> event =
+ NewRunnableMethod("net::DNSRequestSender::CallOnLookupComplete", this,
+ &DNSRequestSender::CallOnLookupComplete);
+ mTarget->Dispatch(event, NS_DISPATCH_NORMAL);
+ }
+
+ if (DNSRequestChild* child = mIPCActor->AsDNSRequestChild()) {
+ Unused << mozilla::net::DNSRequestChild::Send__delete__(child);
+ } else if (DNSRequestParent* parent = mIPCActor->AsDNSRequestParent()) {
+ Unused << mozilla::net::DNSRequestParent::Send__delete__(parent);
+ }
+
+ return true;
+}
+
+void DNSRequestSender::OnIPCActorDestroy() {
+ // Request is done or destroyed. Remove it from the hash table.
+ RefPtr<ChildDNSService> dnsServiceChild =
+ dont_AddRef(ChildDNSService::GetSingleton());
+ dnsServiceChild->NotifyRequestDone(this);
+
+ mIPCActor = nullptr;
+}
+
+//-----------------------------------------------------------------------------
+// DNSRequestChild
+//-----------------------------------------------------------------------------
+
+DNSRequestChild::DNSRequestChild(DNSRequestBase* aRequest)
+ : DNSRequestActor(aRequest) {
+ aRequest->SetIPCActor(this);
+}
+
+mozilla::ipc::IPCResult DNSRequestChild::RecvCancelDNSRequest(
+ const nsCString& hostName, const nsCString& trrServer, const int32_t& port,
+ const uint16_t& type, const OriginAttributes& originAttributes,
+ const nsIDNSService::DNSFlags& flags, const nsresult& reason) {
+ mDNSRequest->OnRecvCancelDNSRequest(hostName, trrServer, port, type,
+ originAttributes, flags, reason);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DNSRequestChild::RecvLookupCompleted(
+ const DNSRequestResponse& reply) {
+ return mDNSRequest->OnRecvLookupCompleted(reply) ? IPC_OK()
+ : IPC_FAIL_NO_REASON(this);
+}
+
+void DNSRequestChild::ActorDestroy(ActorDestroyReason) {
+ mDNSRequest->OnIPCActorDestroy();
+ mDNSRequest = nullptr;
+}
+
+//------------------------------------------------------------------------------
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/DNSRequestChild.h b/netwerk/dns/DNSRequestChild.h
new file mode 100644
index 0000000000..064c2425e0
--- /dev/null
+++ b/netwerk/dns/DNSRequestChild.h
@@ -0,0 +1,42 @@
+/* -*- 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/. */
+
+#ifndef mozilla_net_DNSRequestChild_h
+#define mozilla_net_DNSRequestChild_h
+
+#include "mozilla/net/DNSRequestBase.h"
+#include "mozilla/net/PDNSRequestChild.h"
+
+namespace mozilla {
+namespace net {
+
+class DNSRequestChild final : public DNSRequestActor, public PDNSRequestChild {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DNSRequestChild, override)
+ friend class PDNSRequestChild;
+
+ explicit DNSRequestChild(DNSRequestBase* aRequest);
+
+ bool CanSend() const override { return PDNSRequestChild::CanSend(); }
+ DNSRequestChild* AsDNSRequestChild() override { return this; }
+ DNSRequestParent* AsDNSRequestParent() override { return nullptr; }
+
+ private:
+ virtual ~DNSRequestChild() = default;
+
+ mozilla::ipc::IPCResult RecvCancelDNSRequest(
+ const nsCString& hostName, const nsCString& trrServer,
+ const int32_t& port, const uint16_t& type,
+ const OriginAttributes& originAttributes,
+ const nsIDNSService::DNSFlags& flags, const nsresult& reason);
+ mozilla::ipc::IPCResult RecvLookupCompleted(const DNSRequestResponse& reply);
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_DNSRequestChild_h
diff --git a/netwerk/dns/DNSRequestParent.cpp b/netwerk/dns/DNSRequestParent.cpp
new file mode 100644
index 0000000000..09213b59ba
--- /dev/null
+++ b/netwerk/dns/DNSRequestParent.cpp
@@ -0,0 +1,183 @@
+/* -*- 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 "mozilla/net/DNSRequestParent.h"
+#include "mozilla/net/DNSRequestChild.h"
+#include "nsIDNSService.h"
+#include "nsNetCID.h"
+#include "nsThreadUtils.h"
+#include "nsICancelable.h"
+#include "nsIDNSRecord.h"
+#include "nsHostResolver.h"
+#include "mozilla/Unused.h"
+#include "DNSAdditionalInfo.h"
+#include "nsServiceManagerUtils.h"
+
+using namespace mozilla::ipc;
+
+namespace mozilla {
+namespace net {
+
+//-----------------------------------------------------------------------------
+// DNSRequestHandler::nsISupports
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS(DNSRequestHandler, nsIDNSListener)
+
+static void SendLookupCompletedHelper(DNSRequestActor* aActor,
+ const DNSRequestResponse& aReply) {
+ if (DNSRequestParent* parent = aActor->AsDNSRequestParent()) {
+ Unused << parent->SendLookupCompleted(aReply);
+ } else if (DNSRequestChild* child = aActor->AsDNSRequestChild()) {
+ Unused << child->SendLookupCompleted(aReply);
+ }
+}
+
+void DNSRequestHandler::DoAsyncResolve(const nsACString& hostname,
+ const nsACString& trrServer,
+ int32_t port, uint16_t type,
+ const OriginAttributes& originAttributes,
+ nsIDNSService::DNSFlags flags) {
+ nsresult rv;
+ mFlags = flags;
+ nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIEventTarget> main = GetMainThreadSerialEventTarget();
+ nsCOMPtr<nsICancelable> unused;
+ RefPtr<DNSAdditionalInfo> info;
+ if (!trrServer.IsEmpty() || port != -1) {
+ info = new DNSAdditionalInfo(trrServer, port);
+ }
+ rv = dns->AsyncResolveNative(
+ hostname, static_cast<nsIDNSService::ResolveType>(type), flags, info,
+ this, main, originAttributes, getter_AddRefs(unused));
+ }
+
+ if (NS_FAILED(rv) && mIPCActor->CanSend()) {
+ SendLookupCompletedHelper(mIPCActor, DNSRequestResponse(rv));
+ }
+}
+
+void DNSRequestHandler::OnRecvCancelDNSRequest(
+ const nsCString& hostName, const nsCString& aTrrServer, const int32_t& port,
+ const uint16_t& type, const OriginAttributes& originAttributes,
+ const nsIDNSService::DNSFlags& flags, const nsresult& reason) {
+ nsresult rv;
+ nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv)) {
+ RefPtr<DNSAdditionalInfo> info;
+ if (!aTrrServer.IsEmpty() || port != -1) {
+ info = new DNSAdditionalInfo(aTrrServer, port);
+ }
+ rv = dns->CancelAsyncResolveNative(
+ hostName, static_cast<nsIDNSService::ResolveType>(type), flags, info,
+ this, reason, originAttributes);
+ }
+}
+
+bool DNSRequestHandler::OnRecvLookupCompleted(const DNSRequestResponse& reply) {
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// nsIDNSListener functions
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+DNSRequestHandler::OnLookupComplete(nsICancelable* request,
+ nsIDNSRecord* aRecord, nsresult status) {
+ if (!mIPCActor || !mIPCActor->CanSend()) {
+ // nothing to do: child probably crashed
+ return NS_OK;
+ }
+
+ if (NS_SUCCEEDED(status)) {
+ MOZ_ASSERT(aRecord);
+
+ nsCOMPtr<nsIDNSByTypeRecord> byTypeRec = do_QueryInterface(aRecord);
+ if (byTypeRec) {
+ IPCTypeRecord result;
+ byTypeRec->GetResults(&result.mData);
+ if (nsCOMPtr<nsIDNSHTTPSSVCRecord> rec = do_QueryInterface(aRecord)) {
+ rec->GetTtl(&result.mTTL);
+ }
+ SendLookupCompletedHelper(mIPCActor, DNSRequestResponse(result));
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDNSAddrRecord> rec = do_QueryInterface(aRecord);
+ MOZ_ASSERT(rec);
+ nsAutoCString cname;
+ if (mFlags & nsHostResolver::RES_CANON_NAME) {
+ rec->GetCanonicalName(cname);
+ }
+
+ // Get IP addresses for hostname (use port 80 as dummy value for NetAddr)
+ nsTArray<NetAddr> array;
+ NetAddr addr;
+ while (NS_SUCCEEDED(rec->GetNextAddr(80, &addr))) {
+ array.AppendElement(addr);
+ }
+
+ double trrFetchDuration;
+ rec->GetTrrFetchDuration(&trrFetchDuration);
+
+ double trrFetchDurationNetworkOnly;
+ rec->GetTrrFetchDurationNetworkOnly(&trrFetchDurationNetworkOnly);
+
+ bool isTRR = false;
+ rec->IsTRR(&isTRR);
+
+ nsIRequest::TRRMode effectiveTRRMode = nsIRequest::TRR_DEFAULT_MODE;
+ rec->GetEffectiveTRRMode(&effectiveTRRMode);
+
+ uint32_t ttl = 0;
+ rec->GetTtl(&ttl);
+
+ SendLookupCompletedHelper(
+ mIPCActor, DNSRequestResponse(DNSRecord(cname, array, trrFetchDuration,
+ trrFetchDurationNetworkOnly,
+ isTRR, effectiveTRRMode, ttl)));
+ } else {
+ SendLookupCompletedHelper(mIPCActor, DNSRequestResponse(status));
+ }
+
+ return NS_OK;
+}
+
+void DNSRequestHandler::OnIPCActorDestroy() { mIPCActor = nullptr; }
+
+//-----------------------------------------------------------------------------
+// DNSRequestParent functions
+//-----------------------------------------------------------------------------
+
+DNSRequestParent::DNSRequestParent(DNSRequestBase* aRequest)
+ : DNSRequestActor(aRequest) {
+ aRequest->SetIPCActor(this);
+}
+
+mozilla::ipc::IPCResult DNSRequestParent::RecvCancelDNSRequest(
+ const nsCString& hostName, const nsCString& trrServer, const int32_t& port,
+ const uint16_t& type, const OriginAttributes& originAttributes,
+ const nsIDNSService::DNSFlags& flags, const nsresult& reason) {
+ mDNSRequest->OnRecvCancelDNSRequest(hostName, trrServer, port, type,
+ originAttributes, flags, reason);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DNSRequestParent::RecvLookupCompleted(
+ const DNSRequestResponse& reply) {
+ return mDNSRequest->OnRecvLookupCompleted(reply) ? IPC_OK()
+ : IPC_FAIL_NO_REASON(this);
+}
+
+void DNSRequestParent::ActorDestroy(ActorDestroyReason) {
+ mDNSRequest->OnIPCActorDestroy();
+ mDNSRequest = nullptr;
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/DNSRequestParent.h b/netwerk/dns/DNSRequestParent.h
new file mode 100644
index 0000000000..7583709678
--- /dev/null
+++ b/netwerk/dns/DNSRequestParent.h
@@ -0,0 +1,45 @@
+/* -*- 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/. */
+
+#ifndef mozilla_net_DNSRequestParent_h
+#define mozilla_net_DNSRequestParent_h
+
+#include "mozilla/net/DNSRequestBase.h"
+#include "mozilla/net/PDNSRequestParent.h"
+#include "nsIDNSListener.h"
+
+namespace mozilla {
+namespace net {
+
+class DNSRequestParent : public DNSRequestActor, public PDNSRequestParent {
+ public:
+ friend class PDNSRequestParent;
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DNSRequestParent, override)
+
+ explicit DNSRequestParent(DNSRequestBase* aRequest);
+
+ bool CanSend() const override { return PDNSRequestParent::CanSend(); }
+ DNSRequestChild* AsDNSRequestChild() override { return nullptr; }
+ DNSRequestParent* AsDNSRequestParent() override { return this; }
+
+ private:
+ virtual ~DNSRequestParent() = default;
+
+ // Pass args here rather than storing them in the parent; they are only
+ // needed if the request is to be canceled.
+ mozilla::ipc::IPCResult RecvCancelDNSRequest(
+ const nsCString& hostName, const nsCString& trrServer,
+ const int32_t& port, const uint16_t& type,
+ const OriginAttributes& originAttributes,
+ const nsIDNSService::DNSFlags& flags, const nsresult& reason);
+ mozilla::ipc::IPCResult RecvLookupCompleted(const DNSRequestResponse& reply);
+ void ActorDestroy(ActorDestroyReason) override;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_DNSRequestParent_h
diff --git a/netwerk/dns/DNSServiceBase.cpp b/netwerk/dns/DNSServiceBase.cpp
new file mode 100644
index 0000000000..85b2da37e3
--- /dev/null
+++ b/netwerk/dns/DNSServiceBase.cpp
@@ -0,0 +1,64 @@
+/* -*- 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 "DNSServiceBase.h"
+
+#include "DNS.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/StaticPrefs_network.h"
+#include "nsIDNSService.h"
+#include "nsIProtocolProxyService2.h"
+#include "nsIPrefBranch.h"
+
+namespace mozilla::net {
+
+static const char kPrefProxyType[] = "network.proxy.type";
+static const char kPrefDisablePrefetch[] = "network.dns.disablePrefetch";
+static const char kPrefNetworkProxySOCKS[] = "network.proxy.socks";
+
+NS_IMPL_ISUPPORTS(DNSServiceBase, nsIObserver)
+
+void DNSServiceBase::AddPrefObserver(nsIPrefBranch* aPrefs) {
+ aPrefs->AddObserver(kPrefProxyType, this, false);
+ aPrefs->AddObserver(kPrefDisablePrefetch, this, false);
+ // Monitor these to see if there is a change in proxy configuration
+ aPrefs->AddObserver(kPrefNetworkProxySOCKS, this, false);
+}
+
+void DNSServiceBase::ReadPrefs(const char* aName) {
+ if (!aName || !strcmp(aName, kPrefNetworkProxySOCKS)) {
+ nsAutoCString socks;
+ if (NS_SUCCEEDED(Preferences::GetCString(kPrefNetworkProxySOCKS, socks))) {
+ mHasSocksProxy = !socks.IsEmpty();
+ }
+ }
+ if (!aName || !strcmp(aName, kPrefDisablePrefetch) ||
+ !strcmp(aName, kPrefProxyType)) {
+ mDisablePrefetch = Preferences::GetBool(kPrefDisablePrefetch, false) ||
+ (StaticPrefs::network_proxy_type() ==
+ nsIProtocolProxyService::PROXYCONFIG_MANUAL);
+ }
+}
+
+bool DNSServiceBase::DNSForbiddenByActiveProxy(const nsACString& aHostname,
+ uint32_t aFlags) {
+ if (aFlags & nsIDNSService::RESOLVE_IGNORE_SOCKS_DNS) {
+ return false;
+ }
+
+ // We should avoid doing DNS when a proxy is in use.
+ if (StaticPrefs::network_proxy_type() ==
+ nsIProtocolProxyService::PROXYCONFIG_MANUAL &&
+ mHasSocksProxy && StaticPrefs::network_proxy_socks_remote_dns()) {
+ // Allow IP lookups through, but nothing else.
+ if (!HostIsIPLiteral(aHostname)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace mozilla::net
diff --git a/netwerk/dns/DNSServiceBase.h b/netwerk/dns/DNSServiceBase.h
new file mode 100644
index 0000000000..9657d809f7
--- /dev/null
+++ b/netwerk/dns/DNSServiceBase.h
@@ -0,0 +1,36 @@
+/* -*- 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/. */
+
+#ifndef mozilla_net_DNSServiceBase_h
+#define mozilla_net_DNSServiceBase_h
+
+#include "mozilla/Atomics.h"
+#include "nsIObserver.h"
+#include "nsString.h"
+
+class nsIPrefBranch;
+
+namespace mozilla::net {
+
+class DNSServiceBase : public nsIObserver {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ DNSServiceBase() = default;
+
+ protected:
+ virtual ~DNSServiceBase() = default;
+ void AddPrefObserver(nsIPrefBranch* aPrefs);
+ virtual void ReadPrefs(const char* aName);
+ bool DNSForbiddenByActiveProxy(const nsACString& aHostname, uint32_t aFlags);
+
+ mozilla::Atomic<bool, mozilla::Relaxed> mDisablePrefetch{false};
+ mozilla::Atomic<bool, mozilla::Relaxed> mHasSocksProxy{false};
+};
+
+} // namespace mozilla::net
+
+#endif // mozilla_net_DNSServiceBase_h
diff --git a/netwerk/dns/DNSUtils.cpp b/netwerk/dns/DNSUtils.cpp
new file mode 100644
index 0000000000..8101f28116
--- /dev/null
+++ b/netwerk/dns/DNSUtils.cpp
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=4 sw=2 sts=2 et cin: */
+/* 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 "DNSUtils.h"
+
+#include "nsContentUtils.h"
+#include "nsHttpHandler.h"
+#include "nsIHttpChannel.h"
+#include "nsIHttpChannelInternal.h"
+#include "nsIIOService.h"
+#include "mozilla/SyncRunnable.h"
+#include "TRRServiceChannel.h"
+#include "TRRLoadInfo.h"
+
+namespace mozilla {
+namespace net {
+
+static void InitHttpHandler() {
+ nsresult rv;
+ nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ nsCOMPtr<nsIProtocolHandler> handler;
+ rv = ios->GetProtocolHandler("http", getter_AddRefs(handler));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+}
+
+// static
+nsresult DNSUtils::CreateChannelHelper(nsIURI* aUri, nsIChannel** aResult) {
+ *aResult = nullptr;
+
+ if (NS_IsMainThread() && !XRE_IsSocketProcess()) {
+ nsresult rv;
+ nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_NewChannel(
+ aResult, aUri, nsContentUtils::GetSystemPrincipal(),
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
+ nsIContentPolicy::TYPE_OTHER,
+ nullptr, // nsICookieJarSettings
+ nullptr, // PerformanceStorage
+ nullptr, // aLoadGroup
+ nullptr, // aCallbacks
+ nsIRequest::LOAD_NORMAL, ios);
+ }
+
+ // Unfortunately, we can only initialize gHttpHandler on main thread.
+ if (!gHttpHandler) {
+ nsCOMPtr<nsIEventTarget> main = GetMainThreadSerialEventTarget();
+ if (main) {
+ // Forward to the main thread synchronously.
+ SyncRunnable::DispatchToThread(
+ main, NS_NewRunnableFunction("InitHttpHandler",
+ []() { InitHttpHandler(); }));
+ }
+ }
+
+ if (!gHttpHandler) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ RefPtr<TRRLoadInfo> loadInfo =
+ new TRRLoadInfo(aUri, nsIContentPolicy::TYPE_OTHER);
+ return gHttpHandler->CreateTRRServiceChannel(aUri,
+ nullptr, // givenProxyInfo
+ 0, // proxyResolveFlags
+ nullptr, // proxyURI
+ loadInfo, // aLoadInfo
+ aResult);
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/DNSUtils.h b/netwerk/dns/DNSUtils.h
new file mode 100644
index 0000000000..f5b53c5d72
--- /dev/null
+++ b/netwerk/dns/DNSUtils.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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/. */
+
+#ifndef DNSUtils_h_
+#define DNSUtils_h_
+
+#include "nsError.h"
+
+class nsIURI;
+class nsIChannel;
+
+namespace mozilla {
+namespace net {
+
+class NetworkConnectivityService;
+class TRR;
+
+class DNSUtils final {
+ private:
+ friend class NetworkConnectivityService;
+ friend class ObliviousHttpService;
+ friend class TRR;
+ static nsresult CreateChannelHelper(nsIURI* aUri, nsIChannel** aResult);
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // DNSUtils_h_
diff --git a/netwerk/dns/GetAddrInfo.cpp b/netwerk/dns/GetAddrInfo.cpp
new file mode 100644
index 0000000000..0dd1f0099a
--- /dev/null
+++ b/netwerk/dns/GetAddrInfo.cpp
@@ -0,0 +1,584 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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 "GetAddrInfo.h"
+
+#ifdef DNSQUERY_AVAILABLE
+// There is a bug in windns.h where the type of parameter ppQueryResultsSet for
+// DnsQuery_A is dependent on UNICODE being set. It should *always* be
+// PDNS_RECORDA, but if UNICODE is set it is PDNS_RECORDW. To get around this
+// we make sure that UNICODE is unset.
+# undef UNICODE
+# include <ws2tcpip.h>
+# undef GetAddrInfo
+# include <windns.h>
+#endif // DNSQUERY_AVAILABLE
+
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/net/DNS.h"
+#include "NativeDNSResolverOverrideParent.h"
+#include "prnetdb.h"
+#include "nsIOService.h"
+#include "nsHostResolver.h"
+#include "nsError.h"
+#include "mozilla/net/DNS.h"
+#include <algorithm>
+#include "prerror.h"
+
+#include "mozilla/Logging.h"
+#include "mozilla/StaticPrefs_network.h"
+#include "mozilla/net/DNSPacket.h"
+#include "nsIDNSService.h"
+
+namespace mozilla::net {
+
+static StaticRefPtr<NativeDNSResolverOverride> gOverrideService;
+
+LazyLogModule gGetAddrInfoLog("GetAddrInfo");
+#define LOG(msg, ...) \
+ MOZ_LOG(gGetAddrInfoLog, LogLevel::Debug, ("[DNS]: " msg, ##__VA_ARGS__))
+#define LOG_WARNING(msg, ...) \
+ MOZ_LOG(gGetAddrInfoLog, LogLevel::Warning, ("[DNS]: " msg, ##__VA_ARGS__))
+
+#ifdef DNSQUERY_AVAILABLE
+
+# define COMPUTER_NAME_BUFFER_SIZE 100
+static char sDNSComputerName[COMPUTER_NAME_BUFFER_SIZE];
+static char sNETBIOSComputerName[MAX_COMPUTERNAME_LENGTH + 1];
+
+////////////////////////////
+// WINDOWS IMPLEMENTATION //
+////////////////////////////
+
+// Ensure consistency of PR_* and AF_* constants to allow for legacy usage of
+// PR_* constants with this API.
+static_assert(PR_AF_INET == AF_INET && PR_AF_INET6 == AF_INET6 &&
+ PR_AF_UNSPEC == AF_UNSPEC,
+ "PR_AF_* must match AF_*");
+
+// If successful, returns in aResult a TTL value that is smaller or
+// equal with the one already there. Gets the TTL value by calling
+// to DnsQuery_A and iterating through the returned
+// records to find the one with the smallest TTL value.
+static MOZ_ALWAYS_INLINE nsresult _CallDnsQuery_A_Windows(
+ const nsACString& aHost, uint16_t aAddressFamily, DWORD aFlags,
+ std::function<void(PDNS_RECORDA)> aCallback) {
+ NS_ConvertASCIItoUTF16 name(aHost);
+
+ auto callDnsQuery_A = [&](uint16_t reqFamily) {
+ PDNS_RECORDA dnsData = nullptr;
+ DNS_STATUS status = DnsQuery_A(aHost.BeginReading(), reqFamily, aFlags,
+ nullptr, &dnsData, nullptr);
+ if (status == DNS_INFO_NO_RECORDS || status == DNS_ERROR_RCODE_NAME_ERROR ||
+ !dnsData) {
+ LOG("No DNS records found for %s. status=%lX. reqFamily = %X\n",
+ aHost.BeginReading(), status, reqFamily);
+ return NS_ERROR_FAILURE;
+ } else if (status != NOERROR) {
+ LOG_WARNING("DnsQuery_A failed with status %lX.\n", status);
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ for (PDNS_RECORDA curRecord = dnsData; curRecord;
+ curRecord = curRecord->pNext) {
+ // Only records in the answer section are important
+ if (curRecord->Flags.S.Section != DnsSectionAnswer) {
+ continue;
+ }
+ if (curRecord->wType != reqFamily) {
+ continue;
+ }
+
+ aCallback(curRecord);
+ }
+
+ DnsFree(dnsData, DNS_FREE_TYPE::DnsFreeRecordList);
+ return NS_OK;
+ };
+
+ if (aAddressFamily == PR_AF_UNSPEC || aAddressFamily == PR_AF_INET) {
+ callDnsQuery_A(DNS_TYPE_A);
+ }
+
+ if (aAddressFamily == PR_AF_UNSPEC || aAddressFamily == PR_AF_INET6) {
+ callDnsQuery_A(DNS_TYPE_AAAA);
+ }
+ return NS_OK;
+}
+
+bool recordTypeMatchesRequest(uint16_t wType, uint16_t aAddressFamily) {
+ if (aAddressFamily == PR_AF_UNSPEC) {
+ return wType == DNS_TYPE_A || wType == DNS_TYPE_AAAA;
+ }
+ if (aAddressFamily == PR_AF_INET) {
+ return wType == DNS_TYPE_A;
+ }
+ if (aAddressFamily == PR_AF_INET6) {
+ return wType == DNS_TYPE_AAAA;
+ }
+ return false;
+}
+
+static MOZ_ALWAYS_INLINE nsresult _GetTTLData_Windows(const nsACString& aHost,
+ uint32_t* aResult,
+ uint16_t aAddressFamily) {
+ MOZ_ASSERT(!aHost.IsEmpty());
+ MOZ_ASSERT(aResult);
+ if (aAddressFamily != PR_AF_UNSPEC && aAddressFamily != PR_AF_INET &&
+ aAddressFamily != PR_AF_INET6) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // In order to avoid using ANY records which are not always implemented as a
+ // "Gimme what you have" request in hostname resolvers, we should send A
+ // and/or AAAA requests, based on the address family requested.
+ const DWORD ttlFlags =
+ (DNS_QUERY_STANDARD | DNS_QUERY_NO_NETBT | DNS_QUERY_NO_HOSTS_FILE |
+ DNS_QUERY_NO_MULTICAST | DNS_QUERY_ACCEPT_TRUNCATED_RESPONSE |
+ DNS_QUERY_DONT_RESET_TTL_VALUES);
+ unsigned int ttl = (unsigned int)-1;
+ _CallDnsQuery_A_Windows(
+ aHost, aAddressFamily, ttlFlags,
+ [&ttl, &aHost, aAddressFamily](PDNS_RECORDA curRecord) {
+ if (recordTypeMatchesRequest(curRecord->wType, aAddressFamily)) {
+ ttl = std::min<unsigned int>(ttl, curRecord->dwTtl);
+ } else {
+ LOG("Received unexpected record type %u in response for %s.\n",
+ curRecord->wType, aHost.BeginReading());
+ }
+ });
+
+ if (ttl == (unsigned int)-1) {
+ LOG("No useable TTL found.");
+ return NS_ERROR_FAILURE;
+ }
+
+ *aResult = ttl;
+ return NS_OK;
+}
+
+static MOZ_ALWAYS_INLINE nsresult
+_DNSQuery_A_SingleLabel(const nsACString& aCanonHost, uint16_t aAddressFamily,
+ uint16_t aFlags, AddrInfo** aAddrInfo) {
+ bool setCanonName = aFlags & nsHostResolver::RES_CANON_NAME;
+ nsAutoCString canonName;
+ const DWORD flags = (DNS_QUERY_STANDARD | DNS_QUERY_NO_MULTICAST |
+ DNS_QUERY_ACCEPT_TRUNCATED_RESPONSE);
+ nsTArray<NetAddr> addresses;
+
+ _CallDnsQuery_A_Windows(
+ aCanonHost, aAddressFamily, flags, [&](PDNS_RECORDA curRecord) {
+ MOZ_DIAGNOSTIC_ASSERT(curRecord->wType == DNS_TYPE_A ||
+ curRecord->wType == DNS_TYPE_AAAA);
+ if (setCanonName) {
+ canonName.Assign(curRecord->pName);
+ }
+ NetAddr addr{};
+ addr.inet.family = AF_INET;
+ addr.inet.ip = curRecord->Data.A.IpAddress;
+ addresses.AppendElement(addr);
+ });
+
+ LOG("Query for: %s has %zu results", aCanonHost.BeginReading(),
+ addresses.Length());
+ if (addresses.IsEmpty()) {
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+ RefPtr<AddrInfo> ai(new AddrInfo(
+ aCanonHost, canonName, DNSResolverType::Native, 0, std::move(addresses)));
+ ai.forget(aAddrInfo);
+
+ return NS_OK;
+}
+
+#endif
+
+////////////////////////////////////
+// PORTABLE RUNTIME IMPLEMENTATION//
+////////////////////////////////////
+
+static MOZ_ALWAYS_INLINE nsresult
+_GetAddrInfo_Portable(const nsACString& aCanonHost, uint16_t aAddressFamily,
+ uint16_t aFlags, AddrInfo** aAddrInfo) {
+ MOZ_ASSERT(!aCanonHost.IsEmpty());
+ MOZ_ASSERT(aAddrInfo);
+
+ // We accept the same aFlags that nsHostResolver::ResolveHost accepts, but we
+ // need to translate the aFlags into a form that PR_GetAddrInfoByName
+ // accepts.
+ int prFlags = PR_AI_ADDRCONFIG;
+ if (!(aFlags & nsHostResolver::RES_CANON_NAME)) {
+ prFlags |= PR_AI_NOCANONNAME;
+ }
+
+ // We need to remove IPv4 records manually because PR_GetAddrInfoByName
+ // doesn't support PR_AF_INET6.
+ bool disableIPv4 = aAddressFamily == PR_AF_INET6;
+ if (disableIPv4) {
+ aAddressFamily = PR_AF_UNSPEC;
+ }
+
+#if defined(DNSQUERY_AVAILABLE)
+ if (StaticPrefs::network_dns_dns_query_single_label() &&
+ !aCanonHost.Contains('.') && aCanonHost != "localhost"_ns) {
+ // For some reason we can't use DnsQuery_A to get the computer's IP.
+ if (!aCanonHost.Equals(nsDependentCString(sDNSComputerName),
+ nsCaseInsensitiveCStringComparator) &&
+ !aCanonHost.Equals(nsDependentCString(sNETBIOSComputerName),
+ nsCaseInsensitiveCStringComparator)) {
+ // This is a single label name resolve without a dot.
+ // We use DNSQuery_A for these.
+ LOG("Resolving %s using DnsQuery_A (computername: %s)\n",
+ aCanonHost.BeginReading(), sDNSComputerName);
+ return _DNSQuery_A_SingleLabel(aCanonHost, aAddressFamily, aFlags,
+ aAddrInfo);
+ }
+ }
+#endif
+
+ LOG("Resolving %s using PR_GetAddrInfoByName", aCanonHost.BeginReading());
+ PRAddrInfo* prai =
+ PR_GetAddrInfoByName(aCanonHost.BeginReading(), aAddressFamily, prFlags);
+
+ if (!prai) {
+ LOG("PR_GetAddrInfoByName returned null PR_GetError:%d PR_GetOSErrpr:%d",
+ PR_GetError(), PR_GetOSError());
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ nsAutoCString canonName;
+ if (aFlags & nsHostResolver::RES_CANON_NAME) {
+ canonName.Assign(PR_GetCanonNameFromAddrInfo(prai));
+ }
+
+ bool filterNameCollision =
+ !(aFlags & nsHostResolver::RES_ALLOW_NAME_COLLISION);
+ RefPtr<AddrInfo> ai(new AddrInfo(aCanonHost, prai, disableIPv4,
+ filterNameCollision, canonName));
+ PR_FreeAddrInfo(prai);
+ if (ai->Addresses().IsEmpty()) {
+ LOG("PR_GetAddrInfoByName returned empty address list");
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ ai.forget(aAddrInfo);
+
+ LOG("PR_GetAddrInfoByName resolved successfully");
+ return NS_OK;
+}
+
+//////////////////////////////////////
+// COMMON/PLATFORM INDEPENDENT CODE //
+//////////////////////////////////////
+nsresult GetAddrInfoInit() {
+ LOG("Initializing GetAddrInfo.\n");
+
+#ifdef DNSQUERY_AVAILABLE
+ DWORD namesize = COMPUTER_NAME_BUFFER_SIZE;
+ if (!GetComputerNameExA(ComputerNameDnsHostname, sDNSComputerName,
+ &namesize)) {
+ sDNSComputerName[0] = 0;
+ }
+ namesize = MAX_COMPUTERNAME_LENGTH + 1;
+ if (!GetComputerNameExA(ComputerNameNetBIOS, sNETBIOSComputerName,
+ &namesize)) {
+ sNETBIOSComputerName[0] = 0;
+ }
+#endif
+ return NS_OK;
+}
+
+nsresult GetAddrInfoShutdown() {
+ LOG("Shutting down GetAddrInfo.\n");
+ return NS_OK;
+}
+
+bool FindAddrOverride(const nsACString& aHost, uint16_t aAddressFamily,
+ uint16_t aFlags, AddrInfo** aAddrInfo) {
+ RefPtr<NativeDNSResolverOverride> overrideService = gOverrideService;
+ if (!overrideService) {
+ return false;
+ }
+ AutoReadLock lock(overrideService->mLock);
+ auto overrides = overrideService->mOverrides.Lookup(aHost);
+ if (!overrides) {
+ return false;
+ }
+ nsCString* cname = nullptr;
+ if (aFlags & nsHostResolver::RES_CANON_NAME) {
+ cname = overrideService->mCnames.Lookup(aHost).DataPtrOrNull();
+ }
+
+ RefPtr<AddrInfo> ai;
+
+ nsTArray<NetAddr> addresses;
+ for (const auto& ip : *overrides) {
+ if (aAddressFamily != AF_UNSPEC && ip.raw.family != aAddressFamily) {
+ continue;
+ }
+ addresses.AppendElement(ip);
+ }
+
+ if (!cname) {
+ ai = new AddrInfo(aHost, DNSResolverType::Native, 0, std::move(addresses));
+ } else {
+ ai = new AddrInfo(aHost, *cname, DNSResolverType::Native, 0,
+ std::move(addresses));
+ }
+
+ ai.forget(aAddrInfo);
+ return true;
+}
+
+nsresult GetAddrInfo(const nsACString& aHost, uint16_t aAddressFamily,
+ uint16_t aFlags, AddrInfo** aAddrInfo, bool aGetTtl) {
+ if (NS_WARN_IF(aHost.IsEmpty()) || NS_WARN_IF(!aAddrInfo)) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ *aAddrInfo = nullptr;
+
+ if (StaticPrefs::network_dns_disabled()) {
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+#ifdef DNSQUERY_AVAILABLE
+ // The GetTTLData needs the canonical name to function properly
+ if (aGetTtl) {
+ aFlags |= nsHostResolver::RES_CANON_NAME;
+ }
+#endif
+
+ // If there is an override for this host, then we synthetize a result.
+ if (gOverrideService &&
+ FindAddrOverride(aHost, aAddressFamily, aFlags, aAddrInfo)) {
+ LOG("Returning IP address from NativeDNSResolverOverride");
+ return (*aAddrInfo)->Addresses().Length() ? NS_OK : NS_ERROR_UNKNOWN_HOST;
+ }
+
+ nsAutoCString host;
+ if (StaticPrefs::network_dns_copy_string_before_call()) {
+ host = Substring(aHost.BeginReading(), aHost.Length());
+ MOZ_ASSERT(aHost.BeginReading() != host.BeginReading());
+ } else {
+ host = aHost;
+ }
+
+ if (gNativeIsLocalhost) {
+ // pretend we use the given host but use IPv4 localhost instead!
+ host = "localhost"_ns;
+ aAddressFamily = PR_AF_INET;
+ }
+
+ RefPtr<AddrInfo> info;
+ nsresult rv =
+ _GetAddrInfo_Portable(host, aAddressFamily, aFlags, getter_AddRefs(info));
+
+#ifdef DNSQUERY_AVAILABLE
+ if (aGetTtl && NS_SUCCEEDED(rv)) {
+ // Figure out the canonical name, or if that fails, just use the host name
+ // we have.
+ nsAutoCString name;
+ if (info && !info->CanonicalHostname().IsEmpty()) {
+ name = info->CanonicalHostname();
+ } else {
+ name = host;
+ }
+
+ LOG("Getting TTL for %s (cname = %s).", host.get(), name.get());
+ uint32_t ttl = 0;
+ nsresult ttlRv = _GetTTLData_Windows(name, &ttl, aAddressFamily);
+ if (NS_SUCCEEDED(ttlRv)) {
+ auto builder = info->Build();
+ builder.SetTTL(ttl);
+ info = builder.Finish();
+ LOG("Got TTL %u for %s (name = %s).", ttl, host.get(), name.get());
+ } else {
+ LOG_WARNING("Could not get TTL for %s (cname = %s).", host.get(),
+ name.get());
+ }
+ }
+#endif
+
+ info.forget(aAddrInfo);
+ return rv;
+}
+
+bool FindHTTPSRecordOverride(const nsACString& aHost,
+ TypeRecordResultType& aResult) {
+ LOG("FindHTTPSRecordOverride aHost=%s", nsCString(aHost).get());
+ RefPtr<NativeDNSResolverOverride> overrideService = gOverrideService;
+ if (!overrideService) {
+ return false;
+ }
+
+ AutoReadLock lock(overrideService->mLock);
+ auto overrides = overrideService->mHTTPSRecordOverrides.Lookup(aHost);
+ if (!overrides) {
+ return false;
+ }
+
+ DNSPacket packet;
+ nsAutoCString host(aHost);
+ nsAutoCString cname;
+
+ LOG("resolving %s\n", host.get());
+ // Perform the query
+ nsresult rv = packet.FillBuffer(
+ [&](unsigned char response[DNSPacket::MAX_SIZE]) -> int {
+ if (overrides->Length() > DNSPacket::MAX_SIZE) {
+ return -1;
+ }
+ memcpy(response, overrides->Elements(), overrides->Length());
+ return overrides->Length();
+ });
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ uint32_t ttl = 0;
+ rv = ParseHTTPSRecord(host, packet, aResult, ttl);
+
+ return NS_SUCCEEDED(rv);
+}
+
+nsresult ParseHTTPSRecord(nsCString& aHost, DNSPacket& aDNSPacket,
+ TypeRecordResultType& aResult, uint32_t& aTTL) {
+ nsAutoCString cname;
+ nsresult rv;
+
+ aDNSPacket.SetNativePacket(true);
+
+ int32_t loopCount = 64;
+ while (loopCount > 0 && aResult.is<Nothing>()) {
+ loopCount--;
+ DOHresp resp;
+ nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords;
+ rv = aDNSPacket.Decode(aHost, TRRTYPE_HTTPSSVC, cname, true, resp, aResult,
+ additionalRecords, aTTL);
+ if (NS_FAILED(rv)) {
+ LOG("Decode failed %x", static_cast<uint32_t>(rv));
+ return rv;
+ }
+ if (!cname.IsEmpty() && aResult.is<Nothing>()) {
+ aHost = cname;
+ cname.Truncate();
+ continue;
+ }
+ }
+
+ if (aResult.is<Nothing>()) {
+ LOG("Result is nothing");
+ // The call succeeded, but no HTTPS records were found.
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ return NS_OK;
+}
+
+nsresult ResolveHTTPSRecord(const nsACString& aHost, uint16_t aFlags,
+ TypeRecordResultType& aResult, uint32_t& aTTL) {
+ if (gOverrideService) {
+ return FindHTTPSRecordOverride(aHost, aResult) ? NS_OK
+ : NS_ERROR_UNKNOWN_HOST;
+ }
+
+ return ResolveHTTPSRecordImpl(aHost, aFlags, aResult, aTTL);
+}
+
+// static
+already_AddRefed<nsINativeDNSResolverOverride>
+NativeDNSResolverOverride::GetSingleton() {
+ if (nsIOService::UseSocketProcess() && XRE_IsParentProcess()) {
+ return NativeDNSResolverOverrideParent::GetSingleton();
+ }
+
+ if (gOverrideService) {
+ return do_AddRef(gOverrideService);
+ }
+
+ gOverrideService = new NativeDNSResolverOverride();
+ ClearOnShutdown(&gOverrideService);
+ return do_AddRef(gOverrideService);
+}
+
+NS_IMPL_ISUPPORTS(NativeDNSResolverOverride, nsINativeDNSResolverOverride)
+
+NS_IMETHODIMP NativeDNSResolverOverride::AddIPOverride(
+ const nsACString& aHost, const nsACString& aIPLiteral) {
+ NetAddr tempAddr;
+
+ if (aIPLiteral.Equals("N/A"_ns)) {
+ AutoWriteLock lock(mLock);
+ auto& overrides = mOverrides.LookupOrInsert(aHost);
+ overrides.Clear();
+ return NS_OK;
+ }
+
+ if (NS_FAILED(tempAddr.InitFromString(aIPLiteral))) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ AutoWriteLock lock(mLock);
+ auto& overrides = mOverrides.LookupOrInsert(aHost);
+ overrides.AppendElement(tempAddr);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP NativeDNSResolverOverride::AddHTTPSRecordOverride(
+ const nsACString& aHost, const uint8_t* aData, uint32_t aLength) {
+ AutoWriteLock lock(mLock);
+ nsTArray<uint8_t> data(aData, aLength);
+ mHTTPSRecordOverrides.InsertOrUpdate(aHost, std::move(data));
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP NativeDNSResolverOverride::SetCnameOverride(
+ const nsACString& aHost, const nsACString& aCNAME) {
+ if (aCNAME.IsEmpty()) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ AutoWriteLock lock(mLock);
+ mCnames.InsertOrUpdate(aHost, nsCString(aCNAME));
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP NativeDNSResolverOverride::ClearHostOverride(
+ const nsACString& aHost) {
+ AutoWriteLock lock(mLock);
+ mCnames.Remove(aHost);
+ auto overrides = mOverrides.Extract(aHost);
+ if (!overrides) {
+ return NS_OK;
+ }
+
+ overrides->Clear();
+ return NS_OK;
+}
+
+NS_IMETHODIMP NativeDNSResolverOverride::ClearOverrides() {
+ AutoWriteLock lock(mLock);
+ mOverrides.Clear();
+ mCnames.Clear();
+ return NS_OK;
+}
+
+#ifdef MOZ_NO_HTTPS_IMPL
+
+// If there is no platform specific implementation of ResolveHTTPSRecordImpl
+// we link a dummy implementation here.
+// Otherwise this is implemented in GetAddrInfoWin/Linux/etc
+nsresult ResolveHTTPSRecordImpl(const nsACString& aHost, uint16_t aFlags,
+ TypeRecordResultType& aResult, uint32_t& aTTL) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+#endif // MOZ_NO_HTTPS_IMPL
+
+} // namespace mozilla::net
diff --git a/netwerk/dns/GetAddrInfo.h b/netwerk/dns/GetAddrInfo.h
new file mode 100644
index 0000000000..e8dc095415
--- /dev/null
+++ b/netwerk/dns/GetAddrInfo.h
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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/. */
+
+#ifndef netwerk_dns_GetAddrInfo_h
+#define netwerk_dns_GetAddrInfo_h
+
+#include "nsError.h"
+#include "nscore.h"
+#include "nsINativeDNSResolverOverride.h"
+#include "nsHashKeys.h"
+#include "nsTHashMap.h"
+#include "mozilla/RWLock.h"
+#include "nsTArray.h"
+#include "prio.h"
+#include "mozilla/net/DNS.h"
+#include "nsIDNSByTypeRecord.h"
+#include "mozilla/Logging.h"
+
+#if defined(XP_WIN)
+# define DNSQUERY_AVAILABLE 1
+#else
+# undef DNSQUERY_AVAILABLE
+#endif
+
+namespace mozilla {
+namespace net {
+
+extern LazyLogModule gGetAddrInfoLog;
+class AddrInfo;
+class DNSPacket;
+
+/**
+ * Look up a host by name. Mostly equivalent to getaddrinfo(host, NULL, ...) of
+ * RFC 3493.
+ *
+ * @param aHost[in] Character string defining the host name of interest
+ * @param aAddressFamily[in] May be AF_INET, AF_INET6, or AF_UNSPEC.
+ * @param aFlags[in] May be either PR_AI_ADDRCONFIG or
+ * PR_AI_ADDRCONFIG | PR_AI_NOCANONNAME. Include PR_AI_NOCANONNAME to
+ * suppress the determination of the canonical name corresponding to
+ * hostname (PR_AI_NOCANONNAME will be ignored if the TTL is retrieved).
+ * @param aAddrInfo[out] Will point to the results of the host lookup, or be
+ * null if the lookup failed.
+ * @param aGetTtl[in] If true, the TTL will be retrieved if DNS provides the
+ * answers..
+ */
+nsresult GetAddrInfo(const nsACString& aHost, uint16_t aAddressFamily,
+ uint16_t aFlags, AddrInfo** aAddrInfo, bool aGetTtl);
+
+/**
+ * Initialize the GetAddrInfo module.
+ *
+ * GetAddrInfoShutdown() should be called for every time this function is
+ * called.
+ */
+nsresult GetAddrInfoInit();
+
+/**
+ * Shutdown the GetAddrInfo module.
+ *
+ * This function should be called for every time GetAddrInfoInit() is called.
+ * An assertion may throw (but is not guarenteed) if this function is called
+ * too many times.
+ */
+nsresult GetAddrInfoShutdown();
+
+/**
+ * Resolves a HTTPS record. Will check overrides before calling the
+ * native OS implementation.
+ */
+nsresult ResolveHTTPSRecord(const nsACString& aHost, uint16_t aFlags,
+ TypeRecordResultType& aResult, uint32_t& aTTL);
+
+/**
+ * The platform specific implementation of HTTPS resolution.
+ */
+nsresult ResolveHTTPSRecordImpl(const nsACString& aHost, uint16_t aFlags,
+ TypeRecordResultType& aResult, uint32_t& aTTL);
+
+nsresult ParseHTTPSRecord(nsCString& aHost, DNSPacket& aDNSPacket,
+ TypeRecordResultType& aResult, uint32_t& aTTL);
+
+class NativeDNSResolverOverride : public nsINativeDNSResolverOverride {
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSINATIVEDNSRESOLVEROVERRIDE
+ public:
+ NativeDNSResolverOverride() = default;
+
+ static already_AddRefed<nsINativeDNSResolverOverride> GetSingleton();
+
+ private:
+ virtual ~NativeDNSResolverOverride() = default;
+ mozilla::RWLock mLock MOZ_UNANNOTATED{"NativeDNSResolverOverride"};
+
+ nsTHashMap<nsCStringHashKey, nsTArray<NetAddr>> mOverrides;
+ nsTHashMap<nsCStringHashKey, nsCString> mCnames;
+ nsTHashMap<nsCStringHashKey, nsTArray<uint8_t>> mHTTPSRecordOverrides;
+
+ friend bool FindAddrOverride(const nsACString& aHost, uint16_t aAddressFamily,
+ uint16_t aFlags, AddrInfo** aAddrInfo);
+ friend bool FindHTTPSRecordOverride(const nsACString& aHost,
+ TypeRecordResultType& aResult);
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // netwerk_dns_GetAddrInfo_h
diff --git a/netwerk/dns/HTTPSSVC.cpp b/netwerk/dns/HTTPSSVC.cpp
new file mode 100644
index 0000000000..d36bdf91a2
--- /dev/null
+++ b/netwerk/dns/HTTPSSVC.cpp
@@ -0,0 +1,523 @@
+/* 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 "HTTPSSVC.h"
+#include "mozilla/net/DNS.h"
+#include "nsHttp.h"
+#include "nsHttpHandler.h"
+#include "nsNetAddr.h"
+#include "nsNetUtil.h"
+#include "nsIDNSService.h"
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS(SVCBRecord, nsISVCBRecord)
+
+class SvcParam : public nsISVCParam,
+ public nsISVCParamAlpn,
+ public nsISVCParamNoDefaultAlpn,
+ public nsISVCParamPort,
+ public nsISVCParamIPv4Hint,
+ public nsISVCParamEchConfig,
+ public nsISVCParamIPv6Hint,
+ public nsISVCParamODoHConfig {
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSISVCPARAM
+ NS_DECL_NSISVCPARAMALPN
+ NS_DECL_NSISVCPARAMNODEFAULTALPN
+ NS_DECL_NSISVCPARAMPORT
+ NS_DECL_NSISVCPARAMIPV4HINT
+ NS_DECL_NSISVCPARAMECHCONFIG
+ NS_DECL_NSISVCPARAMIPV6HINT
+ NS_DECL_NSISVCPARAMODOHCONFIG
+ public:
+ explicit SvcParam(const SvcParamType& value) : mValue(value){};
+
+ private:
+ virtual ~SvcParam() = default;
+ SvcParamType mValue;
+};
+
+NS_IMPL_ADDREF(SvcParam)
+NS_IMPL_RELEASE(SvcParam)
+NS_INTERFACE_MAP_BEGIN(SvcParam)
+ NS_INTERFACE_MAP_ENTRY(nsISVCParam)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISVCParam)
+ NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamAlpn, mValue.is<SvcParamAlpn>())
+ NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamNoDefaultAlpn,
+ mValue.is<SvcParamNoDefaultAlpn>())
+ NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamPort, mValue.is<SvcParamPort>())
+ NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamIPv4Hint,
+ mValue.is<SvcParamIpv4Hint>())
+ NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamEchConfig,
+ mValue.is<SvcParamEchConfig>())
+ NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamIPv6Hint,
+ mValue.is<SvcParamIpv6Hint>())
+ NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamODoHConfig,
+ mValue.is<SvcParamODoHConfig>())
+NS_INTERFACE_MAP_END
+
+NS_IMETHODIMP
+SvcParam::GetType(uint16_t* aType) {
+ *aType = mValue.match(
+ [](Nothing&) { return SvcParamKeyMandatory; },
+ [](SvcParamAlpn&) { return SvcParamKeyAlpn; },
+ [](SvcParamNoDefaultAlpn&) { return SvcParamKeyNoDefaultAlpn; },
+ [](SvcParamPort&) { return SvcParamKeyPort; },
+ [](SvcParamIpv4Hint&) { return SvcParamKeyIpv4Hint; },
+ [](SvcParamEchConfig&) { return SvcParamKeyEchConfig; },
+ [](SvcParamIpv6Hint&) { return SvcParamKeyIpv6Hint; },
+ [](SvcParamODoHConfig&) { return SvcParamKeyODoHConfig; });
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SvcParam::GetAlpn(nsTArray<nsCString>& aAlpn) {
+ if (!mValue.is<SvcParamAlpn>()) {
+ MOZ_ASSERT(false, "Unexpected type for variant");
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ aAlpn.AppendElements(mValue.as<SvcParamAlpn>().mValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SvcParam::GetPort(uint16_t* aPort) {
+ if (!mValue.is<SvcParamPort>()) {
+ MOZ_ASSERT(false, "Unexpected type for variant");
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ *aPort = mValue.as<SvcParamPort>().mValue;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SvcParam::GetEchconfig(nsACString& aEchConfig) {
+ if (!mValue.is<SvcParamEchConfig>()) {
+ MOZ_ASSERT(false, "Unexpected type for variant");
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ aEchConfig = mValue.as<SvcParamEchConfig>().mValue;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SvcParam::GetIpv4Hint(nsTArray<RefPtr<nsINetAddr>>& aIpv4Hint) {
+ if (!mValue.is<SvcParamIpv4Hint>()) {
+ MOZ_ASSERT(false, "Unexpected type for variant");
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ const auto& results = mValue.as<SvcParamIpv4Hint>().mValue;
+ for (const auto& ip : results) {
+ if (ip.raw.family != AF_INET) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ RefPtr<nsINetAddr> hint = new nsNetAddr(&ip);
+ aIpv4Hint.AppendElement(hint);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SvcParam::GetIpv6Hint(nsTArray<RefPtr<nsINetAddr>>& aIpv6Hint) {
+ if (!mValue.is<SvcParamIpv6Hint>()) {
+ MOZ_ASSERT(false, "Unexpected type for variant");
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ const auto& results = mValue.as<SvcParamIpv6Hint>().mValue;
+ for (const auto& ip : results) {
+ if (ip.raw.family != AF_INET6) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ RefPtr<nsINetAddr> hint = new nsNetAddr(&ip);
+ aIpv6Hint.AppendElement(hint);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SvcParam::GetODoHConfig(nsACString& aODoHConfig) {
+ if (!mValue.is<SvcParamODoHConfig>()) {
+ MOZ_ASSERT(false, "Unexpected type for variant");
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ aODoHConfig = mValue.as<SvcParamODoHConfig>().mValue;
+ return NS_OK;
+}
+
+bool SVCB::operator<(const SVCB& aOther) const {
+ if (gHttpHandler->EchConfigEnabled()) {
+ if (mHasEchConfig && !aOther.mHasEchConfig) {
+ return true;
+ }
+ if (!mHasEchConfig && aOther.mHasEchConfig) {
+ return false;
+ }
+ }
+
+ return mSvcFieldPriority < aOther.mSvcFieldPriority;
+}
+
+Maybe<uint16_t> SVCB::GetPort() const {
+ Maybe<uint16_t> port;
+ for (const auto& value : mSvcFieldValue) {
+ if (value.mValue.is<SvcParamPort>()) {
+ port.emplace(value.mValue.as<SvcParamPort>().mValue);
+ if (NS_FAILED(NS_CheckPortSafety(*port, "https"))) {
+ *port = 0;
+ }
+ return port;
+ }
+ }
+
+ return Nothing();
+}
+
+bool SVCB::NoDefaultAlpn() const {
+ for (const auto& value : mSvcFieldValue) {
+ if (value.mValue.is<SvcParamKeyNoDefaultAlpn>()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void SVCB::GetIPHints(CopyableTArray<mozilla::net::NetAddr>& aAddresses) const {
+ if (mSvcFieldPriority == 0) {
+ return;
+ }
+
+ for (const auto& value : mSvcFieldValue) {
+ if (value.mValue.is<SvcParamIpv4Hint>()) {
+ aAddresses.AppendElements(value.mValue.as<SvcParamIpv4Hint>().mValue);
+ } else if (value.mValue.is<SvcParamIpv6Hint>()) {
+ aAddresses.AppendElements(value.mValue.as<SvcParamIpv6Hint>().mValue);
+ }
+ }
+}
+
+class AlpnComparator {
+ public:
+ bool Equals(const std::tuple<nsCString, SupportedAlpnRank>& aA,
+ const std::tuple<nsCString, SupportedAlpnRank>& aB) const {
+ return std::get<1>(aA) == std::get<1>(aB);
+ }
+ bool LessThan(const std::tuple<nsCString, SupportedAlpnRank>& aA,
+ const std::tuple<nsCString, SupportedAlpnRank>& aB) const {
+ return std::get<1>(aA) > std::get<1>(aB);
+ }
+};
+
+nsTArray<std::tuple<nsCString, SupportedAlpnRank>> SVCB::GetAllAlpn() const {
+ nsTArray<std::tuple<nsCString, SupportedAlpnRank>> alpnList;
+ for (const auto& value : mSvcFieldValue) {
+ if (value.mValue.is<SvcParamAlpn>()) {
+ for (const auto& alpn : value.mValue.as<SvcParamAlpn>().mValue) {
+ alpnList.AppendElement(std::make_tuple(alpn, IsAlpnSupported(alpn)));
+ }
+ }
+ }
+ alpnList.Sort(AlpnComparator());
+ return alpnList;
+}
+
+SVCBRecord::SVCBRecord(const SVCB& data,
+ Maybe<std::tuple<nsCString, SupportedAlpnRank>> aAlpn)
+ : mData(data), mAlpn(aAlpn) {
+ mPort = mData.GetPort();
+}
+
+NS_IMETHODIMP SVCBRecord::GetPriority(uint16_t* aPriority) {
+ *aPriority = mData.mSvcFieldPriority;
+ return NS_OK;
+}
+
+NS_IMETHODIMP SVCBRecord::GetName(nsACString& aName) {
+ aName = mData.mSvcDomainName;
+ return NS_OK;
+}
+
+Maybe<uint16_t> SVCBRecord::GetPort() { return mPort; }
+
+Maybe<std::tuple<nsCString, SupportedAlpnRank>> SVCBRecord::GetAlpn() {
+ return mAlpn;
+}
+
+NS_IMETHODIMP SVCBRecord::GetSelectedAlpn(nsACString& aAlpn) {
+ if (!mAlpn) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ aAlpn = std::get<0>(*mAlpn);
+ return NS_OK;
+}
+
+NS_IMETHODIMP SVCBRecord::GetEchConfig(nsACString& aEchConfig) {
+ aEchConfig = mData.mEchConfig;
+ return NS_OK;
+}
+
+NS_IMETHODIMP SVCBRecord::GetODoHConfig(nsACString& aODoHConfig) {
+ aODoHConfig = mData.mODoHConfig;
+ return NS_OK;
+}
+
+NS_IMETHODIMP SVCBRecord::GetValues(nsTArray<RefPtr<nsISVCParam>>& aValues) {
+ for (const auto& v : mData.mSvcFieldValue) {
+ RefPtr<nsISVCParam> param = new SvcParam(v.mValue);
+ aValues.AppendElement(param);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP SVCBRecord::GetHasIPHintAddress(bool* aHasIPHintAddress) {
+ *aHasIPHintAddress = mData.mHasIPHints;
+ return NS_OK;
+}
+
+static bool CheckRecordIsUsable(const SVCB& aRecord, nsIDNSService* aDNSService,
+ const nsACString& aHost,
+ uint32_t& aExcludedCount) {
+ if (!aHost.IsEmpty()) {
+ bool excluded = false;
+ if (NS_SUCCEEDED(aDNSService->IsSVCDomainNameFailed(
+ aHost, aRecord.mSvcDomainName, &excluded)) &&
+ excluded) {
+ // Skip if the domain name of this record was failed to connect before.
+ ++aExcludedCount;
+ return false;
+ }
+ }
+
+ Maybe<uint16_t> port = aRecord.GetPort();
+ if (port && *port == 0) {
+ // Found an unsafe port, skip this record.
+ return false;
+ }
+
+ return true;
+}
+
+static bool CheckAlpnIsUsable(SupportedAlpnRank aAlpnType, bool aNoHttp2,
+ bool aNoHttp3, bool aCheckHttp3ExcludedList,
+ const nsACString& aTargetName,
+ uint32_t& aExcludedCount) {
+ // Skip if this alpn is not supported.
+ if (aAlpnType == SupportedAlpnRank::NOT_SUPPORTED) {
+ return false;
+ }
+
+ // Skip if we don't want to use http2.
+ if (aNoHttp2 && aAlpnType == SupportedAlpnRank::HTTP_2) {
+ return false;
+ }
+
+ if (IsHttp3(aAlpnType)) {
+ if (aCheckHttp3ExcludedList && gHttpHandler->IsHttp3Excluded(aTargetName)) {
+ aExcludedCount++;
+ return false;
+ }
+
+ if (aNoHttp3) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static nsTArray<SVCBWrapper> FlattenRecords(const nsTArray<SVCB>& aRecords) {
+ nsTArray<SVCBWrapper> result;
+ for (const auto& record : aRecords) {
+ nsTArray<std::tuple<nsCString, SupportedAlpnRank>> alpnList =
+ record.GetAllAlpn();
+ if (alpnList.IsEmpty()) {
+ result.AppendElement(SVCBWrapper(record));
+ } else {
+ for (const auto& alpn : alpnList) {
+ SVCBWrapper wrapper(record);
+ wrapper.mAlpn = Some(alpn);
+ result.AppendElement(wrapper);
+ }
+ }
+ }
+ return result;
+}
+
+already_AddRefed<nsISVCBRecord>
+DNSHTTPSSVCRecordBase::GetServiceModeRecordInternal(
+ bool aNoHttp2, bool aNoHttp3, const nsTArray<SVCB>& aRecords,
+ bool& aRecordsAllExcluded, bool aCheckHttp3ExcludedList) {
+ RefPtr<SVCBRecord> selectedRecord;
+ RefPtr<SVCBRecord> h3RecordWithEchConfig;
+ uint32_t recordHasNoDefaultAlpnCount = 0;
+ uint32_t recordExcludedCount = 0;
+ aRecordsAllExcluded = false;
+ nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
+ uint32_t h3ExcludedCount = 0;
+
+ nsTArray<SVCBWrapper> records = FlattenRecords(aRecords);
+ for (const auto& record : records) {
+ if (record.mRecord.mSvcFieldPriority == 0) {
+ // In ServiceMode, the SvcPriority should never be 0.
+ return nullptr;
+ }
+
+ if (record.mRecord.NoDefaultAlpn()) {
+ ++recordHasNoDefaultAlpnCount;
+ }
+
+ if (!CheckRecordIsUsable(record.mRecord, dns, mHost, recordExcludedCount)) {
+ // Skip if this record is not usable.
+ continue;
+ }
+
+ if (record.mAlpn) {
+ if (!CheckAlpnIsUsable(std::get<1>(*(record.mAlpn)), aNoHttp2, aNoHttp3,
+ aCheckHttp3ExcludedList,
+ record.mRecord.mSvcDomainName, h3ExcludedCount)) {
+ continue;
+ }
+
+ if (IsHttp3(std::get<1>(*(record.mAlpn)))) {
+ // If the selected alpn is h3 and ech for h3 is disabled, we want
+ // to find out if there is another non-h3 record that has
+ // echConfig. If yes, we'll use the non-h3 record with echConfig
+ // to connect. If not, we'll use h3 to connect without echConfig.
+ if (record.mRecord.mHasEchConfig &&
+ (gHttpHandler->EchConfigEnabled() &&
+ !gHttpHandler->EchConfigEnabled(true))) {
+ if (!h3RecordWithEchConfig) {
+ // Save this h3 record for later use.
+ h3RecordWithEchConfig =
+ new SVCBRecord(record.mRecord, record.mAlpn);
+ // Make sure the next record is not h3.
+ aNoHttp3 = true;
+ continue;
+ }
+ }
+ }
+ }
+
+ if (!selectedRecord) {
+ selectedRecord = new SVCBRecord(record.mRecord, record.mAlpn);
+ }
+ }
+
+ if (!selectedRecord && !h3RecordWithEchConfig) {
+ // If all records indicate "no-default-alpn", we should not use this RRSet.
+ if (recordHasNoDefaultAlpnCount == records.Length()) {
+ return nullptr;
+ }
+
+ if (recordExcludedCount == records.Length()) {
+ aRecordsAllExcluded = true;
+ return nullptr;
+ }
+
+ // If all records are in http3 excluded list, try again without checking the
+ // excluded list. This is better than returning nothing.
+ if (h3ExcludedCount == records.Length() && aCheckHttp3ExcludedList) {
+ return GetServiceModeRecordInternal(aNoHttp2, aNoHttp3, aRecords,
+ aRecordsAllExcluded, false);
+ }
+ }
+
+ if (h3RecordWithEchConfig) {
+ if (selectedRecord && selectedRecord->mData.mHasEchConfig) {
+ return selectedRecord.forget();
+ }
+
+ return h3RecordWithEchConfig.forget();
+ }
+
+ return selectedRecord.forget();
+}
+
+void DNSHTTPSSVCRecordBase::GetAllRecordsWithEchConfigInternal(
+ bool aNoHttp2, bool aNoHttp3, const nsTArray<SVCB>& aRecords,
+ bool* aAllRecordsHaveEchConfig, bool* aAllRecordsInH3ExcludedList,
+ nsTArray<RefPtr<nsISVCBRecord>>& aResult, bool aCheckHttp3ExcludedList) {
+ if (aRecords.IsEmpty()) {
+ return;
+ }
+
+ *aAllRecordsHaveEchConfig = aRecords[0].mHasEchConfig;
+ *aAllRecordsInH3ExcludedList = false;
+ // The first record should have echConfig.
+ if (!(*aAllRecordsHaveEchConfig)) {
+ return;
+ }
+
+ uint32_t h3ExcludedCount = 0;
+ nsTArray<SVCBWrapper> records = FlattenRecords(aRecords);
+ for (const auto& record : records) {
+ if (record.mRecord.mSvcFieldPriority == 0) {
+ // This should not happen, since GetAllRecordsWithEchConfigInternal()
+ // should be called only if GetServiceModeRecordInternal() returns a
+ // non-null record.
+ MOZ_ASSERT(false);
+ return;
+ }
+
+ // Records with echConfig are in front of records without echConfig, so we
+ // don't have to continue.
+ *aAllRecordsHaveEchConfig &= record.mRecord.mHasEchConfig;
+ if (!(*aAllRecordsHaveEchConfig)) {
+ aResult.Clear();
+ return;
+ }
+
+ Maybe<uint16_t> port = record.mRecord.GetPort();
+ if (port && *port == 0) {
+ // Found an unsafe port, skip this record.
+ continue;
+ }
+
+ if (record.mAlpn) {
+ if (!CheckAlpnIsUsable(std::get<1>(*(record.mAlpn)), aNoHttp2, aNoHttp3,
+ aCheckHttp3ExcludedList,
+ record.mRecord.mSvcDomainName, h3ExcludedCount)) {
+ continue;
+ }
+ }
+
+ RefPtr<nsISVCBRecord> svcbRecord =
+ new SVCBRecord(record.mRecord, record.mAlpn);
+ aResult.AppendElement(svcbRecord);
+ }
+
+ // If all records are in http3 excluded list, try again without checking the
+ // excluded list. This is better than returning nothing.
+ if (h3ExcludedCount == records.Length() && aCheckHttp3ExcludedList) {
+ GetAllRecordsWithEchConfigInternal(
+ aNoHttp2, aNoHttp3, aRecords, aAllRecordsHaveEchConfig,
+ aAllRecordsInH3ExcludedList, aResult, false);
+ *aAllRecordsInH3ExcludedList = true;
+ }
+}
+
+bool DNSHTTPSSVCRecordBase::HasIPAddressesInternal(
+ const nsTArray<SVCB>& aRecords) {
+ for (const SVCB& record : aRecords) {
+ if (record.mSvcFieldPriority != 0) {
+ for (const auto& value : record.mSvcFieldValue) {
+ if (value.mValue.is<SvcParamIpv4Hint>()) {
+ return true;
+ }
+ if (value.mValue.is<SvcParamIpv6Hint>()) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/HTTPSSVC.h b/netwerk/dns/HTTPSSVC.h
new file mode 100644
index 0000000000..792f894a02
--- /dev/null
+++ b/netwerk/dns/HTTPSSVC.h
@@ -0,0 +1,164 @@
+/* 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/. */
+
+#ifndef HTTPSSVC_h__
+#define HTTPSSVC_h__
+
+#include "nsIDNSByTypeRecord.h"
+#include "mozilla/net/DNS.h"
+#include "mozilla/Variant.h"
+#include "mozilla/Maybe.h"
+#include "nsHttp.h"
+
+namespace mozilla {
+namespace net {
+
+class DNSHTTPSSVCRecordBase;
+
+enum SvcParamKey : uint16_t {
+ SvcParamKeyMandatory = 0,
+ SvcParamKeyAlpn = 1,
+ SvcParamKeyNoDefaultAlpn = 2,
+ SvcParamKeyPort = 3,
+ SvcParamKeyIpv4Hint = 4,
+ SvcParamKeyEchConfig = 5,
+ SvcParamKeyIpv6Hint = 6,
+ SvcParamKeyODoHConfig = 32769,
+};
+
+inline bool IsValidSvcParamKey(uint16_t aKey) {
+ return aKey <= SvcParamKeyIpv6Hint || aKey == SvcParamKeyODoHConfig;
+}
+
+struct SvcParamAlpn {
+ bool operator==(const SvcParamAlpn& aOther) const {
+ return mValue == aOther.mValue;
+ }
+ CopyableTArray<nsCString> mValue;
+};
+
+struct SvcParamNoDefaultAlpn {
+ bool operator==(const SvcParamNoDefaultAlpn& aOther) const { return true; }
+};
+
+struct SvcParamPort {
+ bool operator==(const SvcParamPort& aOther) const {
+ return mValue == aOther.mValue;
+ }
+ uint16_t mValue;
+};
+
+struct SvcParamIpv4Hint {
+ bool operator==(const SvcParamIpv4Hint& aOther) const {
+ return mValue == aOther.mValue;
+ }
+ CopyableTArray<mozilla::net::NetAddr> mValue;
+};
+
+struct SvcParamEchConfig {
+ bool operator==(const SvcParamEchConfig& aOther) const {
+ return mValue == aOther.mValue;
+ }
+ nsCString mValue;
+};
+
+struct SvcParamIpv6Hint {
+ bool operator==(const SvcParamIpv6Hint& aOther) const {
+ return mValue == aOther.mValue;
+ }
+ CopyableTArray<mozilla::net::NetAddr> mValue;
+};
+
+struct SvcParamODoHConfig {
+ bool operator==(const SvcParamODoHConfig& aOther) const {
+ return mValue == aOther.mValue;
+ }
+ nsCString mValue;
+};
+
+using SvcParamType =
+ mozilla::Variant<Nothing, SvcParamAlpn, SvcParamNoDefaultAlpn, SvcParamPort,
+ SvcParamIpv4Hint, SvcParamEchConfig, SvcParamIpv6Hint,
+ SvcParamODoHConfig>;
+
+struct SvcFieldValue {
+ bool operator==(const SvcFieldValue& aOther) const {
+ return mValue == aOther.mValue;
+ }
+ SvcFieldValue() : mValue(AsVariant(Nothing{})) {}
+ SvcParamType mValue;
+};
+
+struct SVCB {
+ bool operator==(const SVCB& aOther) const {
+ return mSvcFieldPriority == aOther.mSvcFieldPriority &&
+ mSvcDomainName == aOther.mSvcDomainName &&
+ mSvcFieldValue == aOther.mSvcFieldValue;
+ }
+ bool operator<(const SVCB& aOther) const;
+ Maybe<uint16_t> GetPort() const;
+ bool NoDefaultAlpn() const;
+ void GetIPHints(CopyableTArray<mozilla::net::NetAddr>& aAddresses) const;
+ nsTArray<std::tuple<nsCString, SupportedAlpnRank>> GetAllAlpn() const;
+ uint16_t mSvcFieldPriority = 0;
+ nsCString mSvcDomainName;
+ nsCString mEchConfig;
+ nsCString mODoHConfig;
+ bool mHasIPHints = false;
+ bool mHasEchConfig = false;
+ CopyableTArray<SvcFieldValue> mSvcFieldValue;
+};
+
+struct SVCBWrapper {
+ explicit SVCBWrapper(const SVCB& aRecord) : mRecord(aRecord) {}
+ Maybe<std::tuple<nsCString, SupportedAlpnRank>> mAlpn;
+ const SVCB& mRecord;
+};
+
+class SVCBRecord : public nsISVCBRecord {
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSISVCBRECORD
+ public:
+ explicit SVCBRecord(const SVCB& data)
+ : mData(data), mPort(Nothing()), mAlpn(Nothing()) {}
+ explicit SVCBRecord(const SVCB& data,
+ Maybe<std::tuple<nsCString, SupportedAlpnRank>> aAlpn);
+
+ private:
+ friend class DNSHTTPSSVCRecordBase;
+
+ virtual ~SVCBRecord() = default;
+
+ SVCB mData;
+ Maybe<uint16_t> mPort;
+ Maybe<std::tuple<nsCString, SupportedAlpnRank>> mAlpn;
+};
+
+class DNSHTTPSSVCRecordBase {
+ public:
+ explicit DNSHTTPSSVCRecordBase(const nsACString& aHost) : mHost(aHost) {}
+
+ protected:
+ virtual ~DNSHTTPSSVCRecordBase() = default;
+
+ already_AddRefed<nsISVCBRecord> GetServiceModeRecordInternal(
+ bool aNoHttp2, bool aNoHttp3, const nsTArray<SVCB>& aRecords,
+ bool& aRecordsAllExcluded, bool aCheckHttp3ExcludedList = true);
+
+ bool HasIPAddressesInternal(const nsTArray<SVCB>& aRecords);
+
+ void GetAllRecordsWithEchConfigInternal(
+ bool aNoHttp2, bool aNoHttp3, const nsTArray<SVCB>& aRecords,
+ bool* aAllRecordsHaveEchConfig, bool* aAllRecordsInH3ExcludedList,
+ nsTArray<RefPtr<nsISVCBRecord>>& aResult,
+ bool aCheckHttp3ExcludedList = true);
+
+ // The owner name of this HTTPS RR.
+ nsCString mHost;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // HTTPSSVC_h__
diff --git a/netwerk/dns/HostRecordQueue.cpp b/netwerk/dns/HostRecordQueue.cpp
new file mode 100644
index 0000000000..6e8fd5488f
--- /dev/null
+++ b/netwerk/dns/HostRecordQueue.cpp
@@ -0,0 +1,223 @@
+/* vim:set ts=4 sw=2 sts=2 et cin: */
+/* 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 "HostRecordQueue.h"
+#include "mozilla/Telemetry.h"
+#include "nsQueryObject.h"
+
+namespace mozilla {
+namespace net {
+
+void HostRecordQueue::InsertRecord(nsHostRecord* aRec,
+ nsIDNSService::DNSFlags aFlags,
+ const MutexAutoLock& aProofOfLock) {
+ if (aRec->isInList()) {
+ MOZ_DIAGNOSTIC_ASSERT(!mEvictionQ.contains(aRec),
+ "Already in eviction queue");
+ MOZ_DIAGNOSTIC_ASSERT(!mHighQ.contains(aRec), "Already in high queue");
+ MOZ_DIAGNOSTIC_ASSERT(!mMediumQ.contains(aRec), "Already in med queue");
+ MOZ_DIAGNOSTIC_ASSERT(!mLowQ.contains(aRec), "Already in low queue");
+ MOZ_DIAGNOSTIC_ASSERT(false, "Already on some other queue?");
+ }
+
+ switch (AddrHostRecord::GetPriority(aFlags)) {
+ case AddrHostRecord::DNS_PRIORITY_HIGH:
+ mHighQ.insertBack(aRec);
+ break;
+
+ case AddrHostRecord::DNS_PRIORITY_MEDIUM:
+ mMediumQ.insertBack(aRec);
+ break;
+
+ case AddrHostRecord::DNS_PRIORITY_LOW:
+ mLowQ.insertBack(aRec);
+ break;
+ }
+ mPendingCount++;
+}
+
+void HostRecordQueue::AddToEvictionQ(
+ nsHostRecord* aRec, uint32_t aMaxCacheEntries,
+ nsRefPtrHashtable<nsGenericHashKey<nsHostKey>, nsHostRecord>& aDB,
+ const MutexAutoLock& aProofOfLock) {
+ if (aRec->isInList()) {
+ bool inEvictionQ = mEvictionQ.contains(aRec);
+ MOZ_DIAGNOSTIC_ASSERT(!inEvictionQ, "Already in eviction queue");
+ bool inHighQ = mHighQ.contains(aRec);
+ MOZ_DIAGNOSTIC_ASSERT(!inHighQ, "Already in high queue");
+ bool inMediumQ = mMediumQ.contains(aRec);
+ MOZ_DIAGNOSTIC_ASSERT(!inMediumQ, "Already in med queue");
+ bool inLowQ = mLowQ.contains(aRec);
+ MOZ_DIAGNOSTIC_ASSERT(!inLowQ, "Already in low queue");
+ MOZ_DIAGNOSTIC_ASSERT(false, "Already on some other queue?");
+
+ // Bug 1678117 - it's not clear why this can happen, but let's fix it
+ // for release users.
+ aRec->remove();
+ if (inEvictionQ) {
+ MOZ_DIAGNOSTIC_ASSERT(mEvictionQSize > 0);
+ mEvictionQSize--;
+ } else if (inHighQ || inMediumQ || inLowQ) {
+ MOZ_DIAGNOSTIC_ASSERT(mPendingCount > 0);
+ mPendingCount--;
+ }
+ }
+ mEvictionQ.insertBack(aRec);
+ if (mEvictionQSize < aMaxCacheEntries) {
+ mEvictionQSize++;
+ } else {
+ // remove first element on mEvictionQ
+ RefPtr<nsHostRecord> head = mEvictionQ.popFirst();
+ aDB.Remove(*static_cast<nsHostKey*>(head.get()));
+
+ if (!head->negative) {
+ // record the age of the entry upon eviction.
+ TimeDuration age = TimeStamp::NowLoRes() - head->mValidStart;
+ if (aRec->IsAddrRecord()) {
+ Telemetry::Accumulate(Telemetry::DNS_CLEANUP_AGE,
+ static_cast<uint32_t>(age.ToSeconds() / 60));
+ } else {
+ Telemetry::Accumulate(Telemetry::DNS_BY_TYPE_CLEANUP_AGE,
+ static_cast<uint32_t>(age.ToSeconds() / 60));
+ }
+ if (head->CheckExpiration(TimeStamp::Now()) !=
+ nsHostRecord::EXP_EXPIRED) {
+ if (aRec->IsAddrRecord()) {
+ Telemetry::Accumulate(Telemetry::DNS_PREMATURE_EVICTION,
+ static_cast<uint32_t>(age.ToSeconds() / 60));
+ } else {
+ Telemetry::Accumulate(Telemetry::DNS_BY_TYPE_PREMATURE_EVICTION,
+ static_cast<uint32_t>(age.ToSeconds() / 60));
+ }
+ }
+ }
+ }
+}
+
+void HostRecordQueue::MaybeRenewHostRecord(nsHostRecord* aRec,
+ const MutexAutoLock& aProofOfLock) {
+ if (!aRec->isInList()) {
+ return;
+ }
+
+ bool inEvictionQ = mEvictionQ.contains(aRec);
+ MOZ_DIAGNOSTIC_ASSERT(inEvictionQ, "Should be in eviction queue");
+ bool inHighQ = mHighQ.contains(aRec);
+ MOZ_DIAGNOSTIC_ASSERT(!inHighQ, "Already in high queue");
+ bool inMediumQ = mMediumQ.contains(aRec);
+ MOZ_DIAGNOSTIC_ASSERT(!inMediumQ, "Already in med queue");
+ bool inLowQ = mLowQ.contains(aRec);
+ MOZ_DIAGNOSTIC_ASSERT(!inLowQ, "Already in low queue");
+
+ // we're already on the eviction queue. This is a renewal
+ aRec->remove();
+ if (inEvictionQ) {
+ MOZ_DIAGNOSTIC_ASSERT(mEvictionQSize > 0);
+ mEvictionQSize--;
+ } else if (inHighQ || inMediumQ || inLowQ) {
+ MOZ_DIAGNOSTIC_ASSERT(mPendingCount > 0);
+ mPendingCount--;
+ }
+}
+
+void HostRecordQueue::FlushEvictionQ(
+ nsRefPtrHashtable<nsGenericHashKey<nsHostKey>, nsHostRecord>& aDB,
+ const MutexAutoLock& aProofOfLock) {
+ mEvictionQSize = 0;
+
+ // Clear the evictionQ and remove all its corresponding entries from
+ // the cache first
+ if (!mEvictionQ.isEmpty()) {
+ for (const RefPtr<nsHostRecord>& rec : mEvictionQ) {
+ rec->Cancel();
+ aDB.Remove(*static_cast<nsHostKey*>(rec));
+ }
+ mEvictionQ.clear();
+ }
+}
+
+void HostRecordQueue::MaybeRemoveFromQ(nsHostRecord* aRec,
+ const MutexAutoLock& aProofOfLock) {
+ if (!aRec->isInList()) {
+ return;
+ }
+
+ if (mHighQ.contains(aRec) || mMediumQ.contains(aRec) ||
+ mLowQ.contains(aRec)) {
+ mPendingCount--;
+ } else if (mEvictionQ.contains(aRec)) {
+ mEvictionQSize--;
+ } else {
+ MOZ_ASSERT(false, "record is in other queue");
+ }
+
+ aRec->remove();
+}
+
+void HostRecordQueue::MoveToAnotherPendingQ(nsHostRecord* aRec,
+ nsIDNSService::DNSFlags aFlags,
+ const MutexAutoLock& aProofOfLock) {
+ if (!(mHighQ.contains(aRec) || mMediumQ.contains(aRec) ||
+ mLowQ.contains(aRec))) {
+ MOZ_ASSERT(false, "record is not in the pending queue");
+ return;
+ }
+
+ aRec->remove();
+ InsertRecord(aRec, aFlags, aProofOfLock);
+}
+
+already_AddRefed<nsHostRecord> HostRecordQueue::Dequeue(
+ bool aHighQOnly, const MutexAutoLock& aProofOfLock) {
+ RefPtr<nsHostRecord> rec;
+ if (!mHighQ.isEmpty()) {
+ rec = mHighQ.popFirst();
+ } else if (!mMediumQ.isEmpty() && !aHighQOnly) {
+ rec = mMediumQ.popFirst();
+ } else if (!mLowQ.isEmpty() && !aHighQOnly) {
+ rec = mLowQ.popFirst();
+ }
+
+ if (rec) {
+ mPendingCount--;
+ }
+
+ return rec.forget();
+}
+
+void HostRecordQueue::ClearAll(
+ const std::function<void(nsHostRecord*)>& aCallback,
+ const MutexAutoLock& aProofOfLock) {
+ mPendingCount = 0;
+
+ auto clearPendingQ = [&](LinkedList<RefPtr<nsHostRecord>>& aPendingQ) {
+ if (aPendingQ.isEmpty()) {
+ return;
+ }
+
+ // loop through pending queue, erroring out pending lookups.
+ for (const RefPtr<nsHostRecord>& rec : aPendingQ) {
+ rec->Cancel();
+ aCallback(rec);
+ }
+ aPendingQ.clear();
+ };
+
+ clearPendingQ(mHighQ);
+ clearPendingQ(mMediumQ);
+ clearPendingQ(mLowQ);
+
+ mEvictionQSize = 0;
+ if (!mEvictionQ.isEmpty()) {
+ for (const RefPtr<nsHostRecord>& rec : mEvictionQ) {
+ rec->Cancel();
+ }
+ }
+
+ mEvictionQ.clear();
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/HostRecordQueue.h b/netwerk/dns/HostRecordQueue.h
new file mode 100644
index 0000000000..dfa56d1c88
--- /dev/null
+++ b/netwerk/dns/HostRecordQueue.h
@@ -0,0 +1,72 @@
+/* vim:set ts=4 sw=2 sts=2 et cin: */
+/* 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/. */
+
+#ifndef HostRecordQueue_h__
+#define HostRecordQueue_h__
+
+#include <functional>
+#include "mozilla/Mutex.h"
+#include "nsHostRecord.h"
+#include "nsRefPtrHashtable.h"
+
+namespace mozilla {
+namespace net {
+
+class HostRecordQueue final {
+ public:
+ HostRecordQueue() = default;
+ ~HostRecordQueue() = default;
+ HostRecordQueue(const HostRecordQueue& aCopy) = delete;
+ HostRecordQueue& operator=(const HostRecordQueue& aCopy) = delete;
+
+ uint32_t PendingCount() const { return mPendingCount; }
+ uint32_t EvictionQSize() const { return mEvictionQSize; }
+
+ // Insert the record to mHighQ or mMediumQ or mLowQ based on the record's
+ // priority.
+ void InsertRecord(nsHostRecord* aRec, nsIDNSService::DNSFlags aFlags,
+ const MutexAutoLock& aProofOfLock);
+ // Insert the record to mEvictionQ. In theory, this function should be called
+ // when the record is not in any queue.
+ void AddToEvictionQ(
+ nsHostRecord* aRec, uint32_t aMaxCacheEntries,
+ nsRefPtrHashtable<nsGenericHashKey<nsHostKey>, nsHostRecord>& aDB,
+ const MutexAutoLock& aProofOfLock);
+ // Called for removing the record from mEvictionQ. When this function is
+ // called, the record should be either in mEvictionQ or not in any queue.
+ void MaybeRenewHostRecord(nsHostRecord* aRec,
+ const MutexAutoLock& aProofOfLock);
+ // Called for clearing mEvictionQ.
+ void FlushEvictionQ(
+ nsRefPtrHashtable<nsGenericHashKey<nsHostKey>, nsHostRecord>& aDB,
+ const MutexAutoLock& aProofOfLock);
+ // Remove the record from the queue that contains it.
+ void MaybeRemoveFromQ(nsHostRecord* aRec, const MutexAutoLock& aProofOfLock);
+ // When the record's priority changes, move the record between pending queues.
+ void MoveToAnotherPendingQ(nsHostRecord* aRec, nsIDNSService::DNSFlags aFlags,
+ const MutexAutoLock& aProofOfLock);
+ // Returning the first record from one of the pending queue. When |aHighQOnly|
+ // is true, returning the record from mHighQ only. When false, return the
+ // record from mMediumQ or mLowQ.
+ already_AddRefed<nsHostRecord> Dequeue(bool aHighQOnly,
+ const MutexAutoLock& aProofOfLock);
+ // Clear all queues and is called only during shutdown. |aCallback| is invoked
+ // when a record is removed from a queue.
+ void ClearAll(const std::function<void(nsHostRecord*)>& aCallback,
+ const MutexAutoLock& aProofOfLock);
+
+ private:
+ Atomic<uint32_t> mPendingCount{0};
+ Atomic<uint32_t> mEvictionQSize{0};
+ LinkedList<RefPtr<nsHostRecord>> mHighQ;
+ LinkedList<RefPtr<nsHostRecord>> mMediumQ;
+ LinkedList<RefPtr<nsHostRecord>> mLowQ;
+ LinkedList<RefPtr<nsHostRecord>> mEvictionQ;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // HostRecordQueue_h__
diff --git a/netwerk/dns/IDNBlocklistUtils.cpp b/netwerk/dns/IDNBlocklistUtils.cpp
new file mode 100644
index 0000000000..92c73d0997
--- /dev/null
+++ b/netwerk/dns/IDNBlocklistUtils.cpp
@@ -0,0 +1,86 @@
+/* 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 "IDNBlocklistUtils.h"
+
+#include "mozilla/Preferences.h"
+#include "nsStringFwd.h"
+
+namespace mozilla {
+namespace net {
+
+static constexpr char16_t sBlocklistPairs[][2] = {
+#include "IDNCharacterBlocklist.inc"
+};
+
+void RemoveCharFromBlocklist(char16_t aChar,
+ nsTArray<BlocklistRange>& aBlocklist) {
+ auto pos = aBlocklist.BinaryIndexOf(aChar, BlocklistPairToCharComparator());
+ if (pos == nsTArray<BlocklistRange>::NoIndex) {
+ return;
+ }
+
+ auto& pair = aBlocklist[pos];
+
+ // If the matched range has a length of one, we can just remove it
+ if (pair.second == pair.first) {
+ aBlocklist.RemoveElementAt(pos);
+ return;
+ }
+
+ // If the character matches the first element in the range, just update
+ // the range.
+ if (aChar == pair.first) {
+ pair.first = pair.first + 1;
+ return;
+ }
+
+ // Also if it matches the last character in the range, we just update it.
+ if (aChar == pair.second) {
+ pair.second = pair.second - 1;
+ return;
+ }
+
+ // Our character is in the middle of the range, splitting it in two.
+ // We update the matched range to reflect the values before the character,
+ // and insert a new range that represents the values after.
+ char16_t lastElement = pair.second;
+ pair.second = aChar - 1;
+ aBlocklist.InsertElementAt(pos + 1,
+ std::make_pair(char16_t(aChar + 1), lastElement));
+}
+
+void InitializeBlocklist(nsTArray<BlocklistRange>& aBlocklist) {
+ aBlocklist.Clear();
+ for (auto const& arr : sBlocklistPairs) {
+ // The hardcoded pairs are already sorted.
+ aBlocklist.AppendElement(std::make_pair(arr[0], arr[1]));
+ }
+
+ nsAutoString extraAllowed;
+ nsresult rv =
+ Preferences::GetString("network.IDN.extra_allowed_chars", extraAllowed);
+ if (NS_SUCCEEDED(rv) && !extraAllowed.IsEmpty()) {
+ const char16_t* cur = extraAllowed.BeginReading();
+ const char16_t* end = extraAllowed.EndReading();
+ // Characters in the allowed list are removed from the blocklist.
+ for (; cur < end; ++cur) {
+ RemoveCharFromBlocklist(*cur, aBlocklist);
+ }
+ }
+
+ nsAutoString extraBlocked;
+ rv = Preferences::GetString("network.IDN.extra_blocked_chars", extraBlocked);
+ // We add each extra blocked character to the blocklist as a separate range.
+ if (NS_SUCCEEDED(rv) && !extraBlocked.IsEmpty()) {
+ for (size_t i = 0; i < extraBlocked.Length(); ++i) {
+ aBlocklist.AppendElement(
+ std::make_pair(extraBlocked[i], extraBlocked[i]));
+ }
+ aBlocklist.Sort(BlocklistEntryComparator());
+ }
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/IDNBlocklistUtils.h b/netwerk/dns/IDNBlocklistUtils.h
new file mode 100644
index 0000000000..6e1c6c0a77
--- /dev/null
+++ b/netwerk/dns/IDNBlocklistUtils.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef IDNBlocklistUtils_h__
+#define IDNBlocklistUtils_h__
+
+#include <utility>
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace net {
+
+// A blocklist range is defined as all of the characters between:
+// { firstCharacterInRange, lastCharacterInRange }
+using BlocklistRange = std::pair<char16_t, char16_t>;
+
+// Used to perform a binary search of the needle in the sorted array of pairs
+class BlocklistPairToCharComparator {
+ public:
+ bool Equals(const BlocklistRange& pair, char16_t needle) const {
+ // If the needle is between pair.first and pair.second it
+ // is part of the range.
+ return pair.first <= needle && needle <= pair.second;
+ }
+
+ bool LessThan(const BlocklistRange& pair, char16_t needle) const {
+ // The needle has to be larger than the second value,
+ // otherwise it may be equal.
+ return pair.second < needle;
+ }
+};
+
+// Used to sort the array of pairs
+class BlocklistEntryComparator {
+ public:
+ bool Equals(const BlocklistRange& a, const BlocklistRange& b) const {
+ return a.first == b.first && a.second == b.second;
+ }
+
+ bool LessThan(const BlocklistRange& a, const BlocklistRange& b) const {
+ return a.first < b.first;
+ }
+};
+
+// Returns true if the char can be found in the blocklist
+inline bool CharInBlocklist(char16_t aChar,
+ const nsTArray<BlocklistRange>& aBlocklist) {
+ return aBlocklist.ContainsSorted(aChar, BlocklistPairToCharComparator());
+}
+
+// Initializes the blocklist based on the statically defined list and the
+// values of the following preferences:
+// - network.IDN.extra_allowed_chars
+// - network.IDN.extra_blocked_chars
+void InitializeBlocklist(nsTArray<BlocklistRange>& aBlocklist);
+
+void RemoveCharFromBlocklist(char16_t aChar,
+ nsTArray<BlocklistRange>& aBlocklist);
+
+} // namespace net
+} // namespace mozilla
+
+#endif // IDNBlocklistUtils_h__
diff --git a/netwerk/dns/IDNCharacterBlocklist.inc b/netwerk/dns/IDNCharacterBlocklist.inc
new file mode 100644
index 0000000000..754c4f9518
--- /dev/null
+++ b/netwerk/dns/IDNCharacterBlocklist.inc
@@ -0,0 +1,63 @@
+// This file contains the IDN character blocklist.
+// Each entry represents a range of blocked characters.
+// Ranges are defined as:
+// { firstCharacterInRange, lastCharacterInRange }
+// IMPORTANT: Make sure this list is sorted in ascending order
+
+
+// ASCII Space
+{ 0x0020, 0x0020 },
+{ 0x00A0, 0x00A0 },
+{ 0x00BC, 0x00BE },
+{ 0x0138, 0x0138 },
+{ 0x01C3, 0x01C3 },
+{ 0x02D0, 0x02D0 },
+{ 0x0337, 0x0338 },
+{ 0x0589, 0x058A },
+{ 0x05C3, 0x05C3 },
+{ 0x05F4, 0x05F4 },
+{ 0x0609, 0x060A },
+{ 0x066A, 0x066A },
+{ 0x06D4, 0x06D4 },
+{ 0x0701, 0x0704 },
+{ 0x115F, 0x1160 },
+{ 0x1735, 0x1735 },
+{ 0x2000, 0x200B },
+{ 0x200E, 0x2010 },
+{ 0x2019, 0x2019 },
+{ 0x2024, 0x2024 },
+{ 0x2027, 0x202F },
+{ 0x2039, 0x203A },
+{ 0x2041, 0x2041 },
+{ 0x2044, 0x2044 },
+{ 0x2052, 0x2052 },
+{ 0x205F, 0x205F },
+{ 0x2153, 0x215F },
+{ 0x2215, 0x2215 },
+{ 0x2236, 0x2236 },
+{ 0x23AE, 0x23AE },
+{ 0x2571, 0x2571 },
+{ 0x29F6, 0x29F6 },
+{ 0x29F8, 0x29F8 },
+{ 0x2AFB, 0x2AFB },
+{ 0x2AFD, 0x2AFD },
+{ 0x2FF0, 0x2FFB },
+// Ideographic Space
+{ 0x3000, 0x3000 },
+{ 0x3002, 0x3002 },
+{ 0x3014, 0x3015 },
+{ 0x3033, 0x3033 },
+{ 0x30A0, 0x30A0 },
+{ 0x3164, 0x3164 },
+{ 0x321D, 0x321E },
+{ 0x33AE, 0x33AF },
+{ 0x33C6, 0x33C6 },
+{ 0x33DF, 0x33DF },
+{ 0xFE14, 0xFE15 },
+{ 0xFE3F, 0xFE3F },
+{ 0xFE5D, 0xFE5E },
+{ 0xFEFF, 0xFEFF },
+{ 0xFF0E, 0xFF0F },
+{ 0xFF61, 0xFF61 },
+{ 0xFFA0, 0xFFA0 },
+{ 0xFFF9, 0xFFFD },
diff --git a/netwerk/dns/NativeDNSResolverOverrideChild.cpp b/netwerk/dns/NativeDNSResolverOverrideChild.cpp
new file mode 100644
index 0000000000..b52e929ab3
--- /dev/null
+++ b/netwerk/dns/NativeDNSResolverOverrideChild.cpp
@@ -0,0 +1,49 @@
+/* -*- 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 "NativeDNSResolverOverrideChild.h"
+#include "GetAddrInfo.h"
+
+namespace mozilla {
+namespace net {
+
+NativeDNSResolverOverrideChild::NativeDNSResolverOverrideChild() {
+ mOverrideService = NativeDNSResolverOverride::GetSingleton();
+}
+
+mozilla::ipc::IPCResult NativeDNSResolverOverrideChild::RecvAddIPOverride(
+ const nsCString& aHost, const nsCString& aIPLiteral) {
+ Unused << mOverrideService->AddIPOverride(aHost, aIPLiteral);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+NativeDNSResolverOverrideChild::RecvAddHTTPSRecordOverride(
+ const nsCString& aHost, nsTArray<uint8_t>&& aData) {
+ Unused << mOverrideService->AddHTTPSRecordOverride(aHost, aData.Elements(),
+ aData.Length());
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NativeDNSResolverOverrideChild::RecvSetCnameOverride(
+ const nsCString& aHost, const nsCString& aCNAME) {
+ Unused << mOverrideService->SetCnameOverride(aHost, aCNAME);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NativeDNSResolverOverrideChild::RecvClearHostOverride(
+ const nsCString& aHost) {
+ Unused << mOverrideService->ClearHostOverride(aHost);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult NativeDNSResolverOverrideChild::RecvClearOverrides() {
+ Unused << mOverrideService->ClearOverrides();
+ return IPC_OK();
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/NativeDNSResolverOverrideChild.h b/netwerk/dns/NativeDNSResolverOverrideChild.h
new file mode 100644
index 0000000000..42fcc0b7c8
--- /dev/null
+++ b/netwerk/dns/NativeDNSResolverOverrideChild.h
@@ -0,0 +1,40 @@
+/* -*- 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/. */
+
+#ifndef mozilla_net_NativeDNSResolverOverrideChild_h
+#define mozilla_net_NativeDNSResolverOverrideChild_h
+
+#include "mozilla/net/PNativeDNSResolverOverrideChild.h"
+#include "nsINativeDNSResolverOverride.h"
+
+namespace mozilla {
+namespace net {
+
+class NativeDNSResolverOverrideChild : public PNativeDNSResolverOverrideChild {
+ public:
+ NS_INLINE_DECL_REFCOUNTING(NativeDNSResolverOverrideChild, override)
+
+ NativeDNSResolverOverrideChild();
+
+ mozilla::ipc::IPCResult RecvAddIPOverride(const nsCString& aHost,
+ const nsCString& aIPLiteral);
+ mozilla::ipc::IPCResult RecvAddHTTPSRecordOverride(const nsCString& aHost,
+ nsTArray<uint8_t>&& aData);
+ mozilla::ipc::IPCResult RecvSetCnameOverride(const nsCString& aHost,
+ const nsCString& aCNAME);
+ mozilla::ipc::IPCResult RecvClearHostOverride(const nsCString& aHost);
+ mozilla::ipc::IPCResult RecvClearOverrides();
+
+ private:
+ virtual ~NativeDNSResolverOverrideChild() = default;
+
+ nsCOMPtr<nsINativeDNSResolverOverride> mOverrideService;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_NativeDNSResolverOverrideChild_h
diff --git a/netwerk/dns/NativeDNSResolverOverrideParent.cpp b/netwerk/dns/NativeDNSResolverOverrideParent.cpp
new file mode 100644
index 0000000000..47ba16918f
--- /dev/null
+++ b/netwerk/dns/NativeDNSResolverOverrideParent.cpp
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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 "NativeDNSResolverOverrideParent.h"
+
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/net/SocketProcessParent.h"
+#include "nsIOService.h"
+#include "DNS.h"
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS(NativeDNSResolverOverrideParent, nsINativeDNSResolverOverride)
+
+static StaticRefPtr<NativeDNSResolverOverrideParent>
+ gNativeDNSResolverOverrideParent;
+
+// static
+already_AddRefed<nsINativeDNSResolverOverride>
+NativeDNSResolverOverrideParent::GetSingleton() {
+ if (gNativeDNSResolverOverrideParent) {
+ return do_AddRef(gNativeDNSResolverOverrideParent);
+ }
+
+ if (!gIOService) {
+ return nullptr;
+ }
+
+ gNativeDNSResolverOverrideParent = new NativeDNSResolverOverrideParent();
+ ClearOnShutdown(&gNativeDNSResolverOverrideParent);
+
+ auto initTask = []() {
+ Unused << SocketProcessParent::GetSingleton()
+ ->SendPNativeDNSResolverOverrideConstructor(
+ gNativeDNSResolverOverrideParent);
+ };
+ gIOService->CallOrWaitForSocketProcess(initTask);
+ return do_AddRef(gNativeDNSResolverOverrideParent);
+}
+
+NS_IMETHODIMP NativeDNSResolverOverrideParent::AddIPOverride(
+ const nsACString& aHost, const nsACString& aIPLiteral) {
+ NetAddr tempAddr;
+ if (!aIPLiteral.Equals("N/A"_ns) &&
+ NS_FAILED(tempAddr.InitFromString(aIPLiteral))) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ RefPtr<NativeDNSResolverOverrideParent> self = this;
+ nsCString host(aHost);
+ nsCString ip(aIPLiteral);
+ auto task = [self{std::move(self)}, host, ip]() {
+ Unused << self->SendAddIPOverride(host, ip);
+ };
+ gIOService->CallOrWaitForSocketProcess(task);
+ return NS_OK;
+}
+
+NS_IMETHODIMP NativeDNSResolverOverrideParent::AddHTTPSRecordOverride(
+ const nsACString& aHost, const uint8_t* aData, uint32_t aLength) {
+ nsCString host(aHost);
+ CopyableTArray<uint8_t> data(aData, aLength);
+ auto task = [self = RefPtr{this}, host, data = std::move(data)]() {
+ Unused << self->SendAddHTTPSRecordOverride(host, data);
+ };
+ gIOService->CallOrWaitForSocketProcess(std::move(task));
+ return NS_OK;
+}
+
+NS_IMETHODIMP NativeDNSResolverOverrideParent::SetCnameOverride(
+ const nsACString& aHost, const nsACString& aCNAME) {
+ if (aCNAME.IsEmpty()) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ RefPtr<NativeDNSResolverOverrideParent> self = this;
+ nsCString host(aHost);
+ nsCString cname(aCNAME);
+ auto task = [self{std::move(self)}, host, cname]() {
+ Unused << self->SendSetCnameOverride(host, cname);
+ };
+ gIOService->CallOrWaitForSocketProcess(task);
+ return NS_OK;
+}
+
+NS_IMETHODIMP NativeDNSResolverOverrideParent::ClearHostOverride(
+ const nsACString& aHost) {
+ RefPtr<NativeDNSResolverOverrideParent> self = this;
+ nsCString host(aHost);
+ auto task = [self{std::move(self)}, host]() {
+ Unused << self->SendClearHostOverride(host);
+ };
+ gIOService->CallOrWaitForSocketProcess(task);
+ return NS_OK;
+}
+
+NS_IMETHODIMP NativeDNSResolverOverrideParent::ClearOverrides() {
+ RefPtr<NativeDNSResolverOverrideParent> self = this;
+ auto task = [self{std::move(self)}]() {
+ Unused << self->SendClearOverrides();
+ };
+ gIOService->CallOrWaitForSocketProcess(task);
+ return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/NativeDNSResolverOverrideParent.h b/netwerk/dns/NativeDNSResolverOverrideParent.h
new file mode 100644
index 0000000000..b0cb5e4d6d
--- /dev/null
+++ b/netwerk/dns/NativeDNSResolverOverrideParent.h
@@ -0,0 +1,32 @@
+/* -*- 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/. */
+
+#ifndef mozilla_net_NativeDNSResolverOverrideParent_h
+#define mozilla_net_NativeDNSResolverOverrideParent_h
+
+#include "mozilla/net/PNativeDNSResolverOverrideParent.h"
+#include "nsINativeDNSResolverOverride.h"
+
+namespace mozilla {
+namespace net {
+
+class NativeDNSResolverOverrideParent : public PNativeDNSResolverOverrideParent,
+ public nsINativeDNSResolverOverride {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSINATIVEDNSRESOLVEROVERRIDE
+
+ explicit NativeDNSResolverOverrideParent() = default;
+ static already_AddRefed<nsINativeDNSResolverOverride> GetSingleton();
+
+ private:
+ virtual ~NativeDNSResolverOverrideParent() = default;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_NativeDNSResolverOverrideParent_h
diff --git a/netwerk/dns/PDNSRequest.ipdl b/netwerk/dns/PDNSRequest.ipdl
new file mode 100644
index 0000000000..0613ff67b4
--- /dev/null
+++ b/netwerk/dns/PDNSRequest.ipdl
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+
+/* 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 protocol PNecko;
+include protocol PSocketProcess;
+
+include PDNSRequestParams;
+
+include "mozilla/net/NeckoMessageUtils.h";
+
+using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
+using nsIDNSService::DNSFlags from "nsIDNSService.h";
+
+namespace mozilla {
+namespace net {
+
+async protocol PDNSRequest
+{
+ manager PNecko or PSocketProcess;
+
+both:
+ // constructor in PNecko takes AsyncResolve args that initialize request
+
+ // Pass args here rather than storing them in the parent; they are only
+ // needed if the request is to be canceled.
+ async CancelDNSRequest(nsCString hostName, nsCString trrServer, int32_t port,
+ uint16_t type, OriginAttributes originAttributes,
+ DNSFlags flags, nsresult reason);
+ async __delete__();
+
+ async LookupCompleted(DNSRequestResponse reply);
+
+};
+
+} //namespace net
+} //namespace mozilla
diff --git a/netwerk/dns/PDNSRequestParams.ipdlh b/netwerk/dns/PDNSRequestParams.ipdlh
new file mode 100644
index 0000000000..de15e91a59
--- /dev/null
+++ b/netwerk/dns/PDNSRequestParams.ipdlh
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+
+/* 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/. */
+
+using mozilla::net::NetAddr from "mozilla/net/DNS.h";
+using mozilla::net::IPCTypeRecord from "mozilla/net/DNSByTypeRecord.h";
+using nsIRequest::TRRMode from "nsIRequest.h";
+
+namespace mozilla {
+namespace net {
+
+//-----------------------------------------------------------------------------
+// DNS IPDL structs
+//-----------------------------------------------------------------------------
+
+struct DNSRecord
+{
+ nsCString canonicalName;
+ NetAddr[] addrs;
+ double trrFetchDuration;
+ double trrFetchDurationNetworkOnly;
+ bool isTRR;
+ TRRMode effectiveTRRMode;
+ uint32_t ttl;
+};
+
+union DNSRequestResponse
+{
+ DNSRecord;
+ IPCTypeRecord; // The result of a by-type query
+ nsresult; // if error
+};
+
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/netwerk/dns/PNativeDNSResolverOverride.ipdl b/netwerk/dns/PNativeDNSResolverOverride.ipdl
new file mode 100644
index 0000000000..a1cd4c9ff5
--- /dev/null
+++ b/netwerk/dns/PNativeDNSResolverOverride.ipdl
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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 protocol PSocketProcess;
+
+namespace mozilla {
+namespace net {
+
+async protocol PNativeDNSResolverOverride
+{
+ manager PSocketProcess;
+
+child:
+ async __delete__();
+ async AddIPOverride(nsCString aHost, nsCString aIPLiteral);
+ async AddHTTPSRecordOverride(nsCString aHost, uint8_t[] aData);
+ async SetCnameOverride(nsCString aHost, nsCString aCNAME);
+ async ClearHostOverride(nsCString aHost);
+ async ClearOverrides();
+};
+
+} //namespace net
+} //namespace mozilla
diff --git a/netwerk/dns/PTRRService.ipdl b/netwerk/dns/PTRRService.ipdl
new file mode 100644
index 0000000000..14059e033f
--- /dev/null
+++ b/netwerk/dns/PTRRService.ipdl
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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 protocol PSocketProcess;
+
+include NeckoChannelParams;
+include PSMIPCTypes;
+
+namespace mozilla {
+namespace net {
+
+async protocol PTRRService
+{
+ manager PSocketProcess;
+
+parent:
+ async NotifyNetworkConnectivityServiceObservers(nsCString aTopic);
+ async InitTRRConnectionInfo();
+ async SetConfirmationState(uint32_t aNewState);
+
+child:
+ async __delete__();
+ async UpdatePlatformDNSInformation(nsCString[] aSuffixList);
+ async UpdateParentalControlEnabled(bool aEnabled);
+ async ClearDNSCache(bool aTrrToo);
+ async SetDetectedTrrURI(nsCString aURI);
+ async SetDefaultTRRConnectionInfo(HttpConnectionInfoCloneArgs? aConnInfoArgs);
+ async UpdateEtcHosts(nsCString[] aHosts);
+};
+
+} //namespace net
+} //namespace mozilla
diff --git a/netwerk/dns/PlatformDNSAndroid.cpp b/netwerk/dns/PlatformDNSAndroid.cpp
new file mode 100644
index 0000000000..78a944777f
--- /dev/null
+++ b/netwerk/dns/PlatformDNSAndroid.cpp
@@ -0,0 +1,154 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=4 sw=2 sts=2 et cin: */
+/* 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 "GetAddrInfo.h"
+#include "mozilla/net/DNSPacket.h"
+#include "nsIDNSService.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/StaticPrefs_network.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <resolv.h>
+#include <poll.h>
+#include <dlfcn.h>
+#include <android/multinetwork.h>
+
+namespace mozilla::net {
+
+// The first call to ResolveHTTPSRecordImpl will load the library
+// and function pointers.
+static Atomic<bool> sLibLoading{false};
+
+// https://developer.android.com/ndk/reference/group/networking#android_res_nquery
+// The function android_res_nquery is defined in <android/multinetwork.h>
+typedef int (*android_res_nquery_ptr)(net_handle_t network, const char* dname,
+ int ns_class, int ns_type,
+ uint32_t flags);
+static Atomic<android_res_nquery_ptr> sAndroidResNQuery;
+
+#define LOG(msg, ...) \
+ MOZ_LOG(gGetAddrInfoLog, LogLevel::Debug, ("[DNS]: " msg, ##__VA_ARGS__))
+
+nsresult ResolveHTTPSRecordImpl(const nsACString& aHost, uint16_t aFlags,
+ TypeRecordResultType& aResult, uint32_t& aTTL) {
+ DNSPacket packet;
+ nsAutoCString host(aHost);
+ nsAutoCString cname;
+ nsresult rv;
+
+ if (xpc::IsInAutomation() &&
+ !StaticPrefs::network_dns_native_https_query_in_automation()) {
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ if (!sLibLoading.exchange(true)) {
+ // We're the first call here, load the library and symbols.
+ void* handle = dlopen("libandroid.so", RTLD_LAZY | RTLD_LOCAL);
+ if (!handle) {
+ LOG("Error loading libandroid_net %s", dlerror());
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ auto x = dlsym(handle, "android_res_nquery");
+ if (!x) {
+ LOG("No android_res_nquery symbol");
+ }
+
+ sAndroidResNQuery = (android_res_nquery_ptr)x;
+ }
+
+ if (!sAndroidResNQuery) {
+ LOG("nquery not loaded");
+ // The library hasn't been loaded yet.
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ LOG("resolving %s\n", host.get());
+ // Perform the query
+ rv = packet.FillBuffer(
+ [&](unsigned char response[DNSPacket::MAX_SIZE]) -> int {
+ int fd = 0;
+ auto closeSocket = MakeScopeExit([&] {
+ if (fd > 0) {
+ close(fd);
+ }
+ });
+ uint32_t flags = 0;
+ if (aFlags & nsIDNSService::RESOLVE_BYPASS_CACHE) {
+ flags = ANDROID_RESOLV_NO_CACHE_LOOKUP;
+ }
+ fd = sAndroidResNQuery(0, host.get(), ns_c_in,
+ nsIDNSService::RESOLVE_TYPE_HTTPSSVC, flags);
+
+ if (fd < 0) {
+ LOG("DNS query failed");
+ return fd;
+ }
+
+ struct pollfd fds;
+ fds.fd = fd;
+ fds.events = POLLIN; // Wait for read events
+
+ // Wait for an event on the file descriptor
+ int ret = poll(&fds, 1,
+ StaticPrefs::network_dns_native_https_timeout_android());
+ if (ret <= 0) {
+ LOG("poll failed %d", ret);
+ return -1;
+ }
+
+ ssize_t len = recv(fd, response, DNSPacket::MAX_SIZE - 1, 0);
+ if (len <= 8) {
+ LOG("size too small %zd", len);
+ return len < 0 ? len : -1;
+ }
+
+ // The first 8 bytes are UDP header.
+ // XXX: we should consider avoiding this move somehow.
+ for (int i = 0; i < len - 8; i++) {
+ response[i] = response[i + 8];
+ }
+
+ return len - 8;
+ });
+ if (NS_FAILED(rv)) {
+ LOG("failed rv");
+ return rv;
+ }
+ packet.SetNativePacket(true);
+
+ int32_t loopCount = 64;
+ while (loopCount > 0 && aResult.is<Nothing>()) {
+ loopCount--;
+ DOHresp resp;
+ nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords;
+ rv = packet.Decode(host, TRRTYPE_HTTPSSVC, cname, true, resp, aResult,
+ additionalRecords, aTTL);
+ if (NS_FAILED(rv)) {
+ LOG("Decode failed %x", static_cast<uint32_t>(rv));
+ return rv;
+ }
+ if (!cname.IsEmpty() && aResult.is<Nothing>()) {
+ host = cname;
+ cname.Truncate();
+ continue;
+ }
+ }
+
+ if (aResult.is<Nothing>()) {
+ LOG("Result is nothing");
+ // The call succeeded, but no HTTPS records were found.
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ return NS_OK;
+}
+
+} // namespace mozilla::net
diff --git a/netwerk/dns/PlatformDNSUnix.cpp b/netwerk/dns/PlatformDNSUnix.cpp
new file mode 100644
index 0000000000..dedbe337df
--- /dev/null
+++ b/netwerk/dns/PlatformDNSUnix.cpp
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=4 sw=2 sts=2 et cin: */
+/* 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 "GetAddrInfo.h"
+#include "mozilla/net/DNSPacket.h"
+#include "nsIDNSService.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/StaticPrefs_network.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <resolv.h>
+
+namespace mozilla::net {
+
+#define LOG(msg, ...) \
+ MOZ_LOG(gGetAddrInfoLog, LogLevel::Debug, ("[DNS]: " msg, ##__VA_ARGS__))
+
+nsresult ResolveHTTPSRecordImpl(const nsACString& aHost, uint16_t aFlags,
+ TypeRecordResultType& aResult, uint32_t& aTTL) {
+ DNSPacket packet;
+ nsAutoCString host(aHost);
+ nsAutoCString cname;
+ nsresult rv;
+
+ if (xpc::IsInAutomation() &&
+ !StaticPrefs::network_dns_native_https_query_in_automation()) {
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ LOG("resolving %s\n", host.get());
+ // Perform the query
+ rv = packet.FillBuffer(
+ [&](unsigned char response[DNSPacket::MAX_SIZE]) -> int {
+ int len = 0;
+#if defined(XP_LINUX)
+ len = res_nquery(&_res, host.get(), ns_c_in,
+ nsIDNSService::RESOLVE_TYPE_HTTPSSVC, response,
+ DNSPacket::MAX_SIZE);
+#elif defined(XP_MACOSX)
+ len =
+ res_query(host.get(), ns_c_in, nsIDNSService::RESOLVE_TYPE_HTTPSSVC,
+ response, DNSPacket::MAX_SIZE);
+#endif
+
+ if (len < 0) {
+ LOG("DNS query failed");
+ }
+ return len;
+ });
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return ParseHTTPSRecord(host, packet, aResult, aTTL);
+}
+
+} // namespace mozilla::net
diff --git a/netwerk/dns/PlatformDNSWin.cpp b/netwerk/dns/PlatformDNSWin.cpp
new file mode 100644
index 0000000000..8b5539d7de
--- /dev/null
+++ b/netwerk/dns/PlatformDNSWin.cpp
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=4 sw=2 sts=2 et cin: */
+/* 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 "GetAddrInfo.h"
+#include "mozilla/net/DNSPacket.h"
+#include "nsIDNSService.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/StaticPrefs_network.h"
+
+#ifdef DNSQUERY_AVAILABLE
+// There is a bug in windns.h where the type of parameter ppQueryResultsSet for
+// DnsQuery_A is dependent on UNICODE being set. It should *always* be
+// PDNS_RECORDA, but if UNICODE is set it is PDNS_RECORDW. To get around this
+// we make sure that UNICODE is unset.
+# undef UNICODE
+# include <ws2tcpip.h>
+# undef GetAddrInfo
+# include <windns.h>
+#endif // DNSQUERY_AVAILABLE
+
+namespace mozilla::net {
+
+#define LOG(msg, ...) \
+ MOZ_LOG(gGetAddrInfoLog, LogLevel::Debug, ("[DNS]: " msg, ##__VA_ARGS__))
+
+nsresult ResolveHTTPSRecordImpl(const nsACString& aHost, uint16_t aFlags,
+ TypeRecordResultType& aResult, uint32_t& aTTL) {
+ nsAutoCString host(aHost);
+ PDNS_RECORD result = nullptr;
+ nsAutoCString cname;
+ aTTL = UINT32_MAX;
+
+ if (xpc::IsInAutomation() &&
+ !StaticPrefs::network_dns_native_https_query_in_automation()) {
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ DNS_STATUS status =
+ DnsQuery_A(host.get(), nsIDNSService::RESOLVE_TYPE_HTTPSSVC,
+ DNS_QUERY_STANDARD, nullptr, &result, nullptr);
+ if (status != ERROR_SUCCESS) {
+ LOG("DnsQuery_A failed with error: %ld\n", status);
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ // This will free the record if we exit early from this function.
+ auto freeDnsRecord =
+ MakeScopeExit([&]() { DnsRecordListFree(result, DnsFreeRecordList); });
+
+ auto CheckRecords = [&aResult, &cname, &aTTL](
+ PDNS_RECORD result,
+ const nsCString& aHost) -> nsresult {
+ PDNS_RECORD current = result;
+
+ for (current = result; current; current = current->pNext) {
+ if (strcmp(current->pName, aHost.get()) != 0) {
+ continue;
+ }
+ if (current->wType == nsIDNSService::RESOLVE_TYPE_HTTPSSVC) {
+ const unsigned char* ptr = (const unsigned char*)&(current->Data);
+ struct SVCB parsed;
+ nsresult rv = DNSPacket::ParseHTTPS(current->wDataLength, parsed, 0,
+ ptr, current->wDataLength, aHost);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (parsed.mSvcDomainName.IsEmpty() && parsed.mSvcFieldPriority == 0) {
+ // For AliasMode SVCB RRs, a TargetName of "." indicates that the
+ // service is not available or does not exist.
+ continue;
+ }
+
+ if (parsed.mSvcFieldPriority == 0) {
+ // Alias form SvcDomainName must not have the "." value (empty)
+ if (parsed.mSvcDomainName.IsEmpty()) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ cname = parsed.mSvcDomainName;
+ ToLowerCase(cname);
+ break;
+ }
+
+ if (!aResult.is<TypeRecordHTTPSSVC>()) {
+ aResult = mozilla::AsVariant(CopyableTArray<SVCB>());
+ }
+ auto& results = aResult.as<TypeRecordHTTPSSVC>();
+ results.AppendElement(parsed);
+ aTTL = std::min<uint32_t>(aTTL, current->dwTtl);
+ } else if (current->wType == DNS_TYPE_CNAME) {
+ cname = current->Data.Cname.pNameHost;
+ ToLowerCase(cname);
+ aTTL = std::min<uint32_t>(aTTL, current->dwTtl);
+ break;
+ }
+ }
+ return NS_OK;
+ };
+
+ int32_t loopCount = 64;
+ while (loopCount > 0 && aResult.is<Nothing>()) {
+ loopCount--;
+
+ nsresult rv = CheckRecords(result, host);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (aResult.is<Nothing>() && !cname.IsEmpty()) {
+ host = cname;
+ cname.Truncate();
+ continue;
+ }
+
+ if (aResult.is<Nothing>()) {
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+ }
+
+ // CNAME loop
+ if (loopCount == 0) {
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ if (aResult.is<Nothing>()) {
+ // The call succeeded, but no HTTPS records were found.
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ if (aTTL == UINT32_MAX) {
+ aTTL = 60; // Defaults to 60 seconds
+ }
+ return NS_OK;
+}
+
+} // namespace mozilla::net
diff --git a/netwerk/dns/PublicSuffixList.sys.mjs b/netwerk/dns/PublicSuffixList.sys.mjs
new file mode 100644
index 0000000000..89cec38fea
--- /dev/null
+++ b/netwerk/dns/PublicSuffixList.sys.mjs
@@ -0,0 +1,104 @@
+/* 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/. */
+
+import { RemoteSettings } from "resource://services-settings/remote-settings.sys.mjs";
+
+const FileUtils = ChromeUtils.importESModule(
+ "resource://gre/modules/FileUtils.sys.mjs"
+).FileUtils;
+
+const RECORD_ID = "tld-dafsa";
+const SIGNAL = "public-suffix-list-updated";
+
+export const PublicSuffixList = {
+ CLIENT: RemoteSettings("public-suffix-list"),
+
+ init() {
+ // Only initialize once.
+ if (this._initialized) {
+ return;
+ }
+ this._initialized = true;
+
+ this.CLIENT.on("sync", this.onUpdate.bind(this));
+ /* We have a single record for this collection. Let's see if we already have it locally.
+ * Note that on startup, we don't need to synchronize immediately on new profiles.
+ */
+ this.CLIENT.get({ syncIfEmpty: false, filters: { id: RECORD_ID } })
+ .then(async records => {
+ if (records.length == 1) {
+ // Get the downloaded file URI (most likely to be a no-op here, since file will exist).
+ const fileURI = await this.CLIENT.attachments.downloadToDisk(
+ records[0]
+ );
+ // Send a signal so that the C++ code loads the updated list on startup.
+ this.notifyUpdate(fileURI);
+ }
+ })
+ .catch(err => console.error(err));
+ },
+
+ /**
+ * This method returns the path to the file based on the file uri received
+ * Example:
+ * On windows "file://C:/Users/AppData/main/public-suffix-list/dafsa.bin"
+ * will be converted to "C:\\Users\\main\\public-suffix-list\\dafsa.bin
+ *
+ * On macOS/linux "file:///home/main/public-suffix-list/dafsa.bin"
+ * will be converted to "/home/main/public-suffix-list/dafsa.bin"
+ */
+ getFilePath(fileURI) {
+ const uri = Services.io.newURI(fileURI);
+ const file = uri.QueryInterface(Ci.nsIFileURL).file;
+ return file.path;
+ },
+
+ notifyUpdate(fileURI) {
+ if (!Services.prefs.getBoolPref("network.psl.onUpdate_notify", false)) {
+ // Updating the PSL while Firefox is running could cause principals to
+ // have a different base domain before/after the update.
+ // See bug 1582647 comment 30
+ return;
+ }
+
+ const filePath = this.getFilePath(fileURI);
+ const nsifile = new FileUtils.File(filePath);
+ /* Send a signal to be read by the C++, the method
+ * ::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
+ * at netwerk/dns/nsEffectiveTLDService.cpp
+ */
+ Services.obs.notifyObservers(
+ nsifile, // aSubject
+ SIGNAL, // aTopic
+ filePath // aData
+ );
+ },
+
+ async onUpdate({ data: { created, updated, deleted } }) {
+ // In theory, this will never happen, we will never delete the record.
+ if (deleted.length == 1) {
+ await this.CLIENT.attachments.deleteFromDisk(deleted[0]);
+ }
+ // Handle creation and update the same way
+ const changed = created.concat(updated.map(u => u.new));
+ /* In theory, we should never have more than one record. And if we receive
+ * this event, it's because the single record was updated.
+ */
+ if (changed.length != 1) {
+ console.warn("Unsupported sync event for Public Suffix List");
+ return;
+ }
+ // Download the updated file.
+ let fileURI;
+ try {
+ fileURI = await this.CLIENT.attachments.downloadToDisk(changed[0]);
+ } catch (err) {
+ console.error(err);
+ return;
+ }
+
+ // Notify the C++ part to reload it from disk.
+ this.notifyUpdate(fileURI);
+ },
+};
diff --git a/netwerk/dns/TRR.cpp b/netwerk/dns/TRR.cpp
new file mode 100644
index 0000000000..aad65ab809
--- /dev/null
+++ b/netwerk/dns/TRR.cpp
@@ -0,0 +1,1139 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=4 sw=2 sts=2 et cin: */
+/* 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 "DNS.h"
+#include "DNSUtils.h"
+#include "nsCharSeparatedTokenizer.h"
+#include "nsContentUtils.h"
+#include "nsHttpHandler.h"
+#include "nsHttpChannel.h"
+#include "nsHostResolver.h"
+#include "nsIHttpChannel.h"
+#include "nsIHttpChannelInternal.h"
+#include "nsIIOService.h"
+#include "nsIInputStream.h"
+#include "nsIObliviousHttp.h"
+#include "nsISupports.h"
+#include "nsISupportsUtils.h"
+#include "nsITimedChannel.h"
+#include "nsIUploadChannel2.h"
+#include "nsIURIMutator.h"
+#include "nsNetUtil.h"
+#include "nsQueryObject.h"
+#include "nsStringStream.h"
+#include "nsThreadUtils.h"
+#include "nsURLHelper.h"
+#include "ObliviousHttpChannel.h"
+#include "TRR.h"
+#include "TRRService.h"
+#include "TRRServiceChannel.h"
+#include "TRRLoadInfo.h"
+
+#include "mozilla/Base64.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/Logging.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/StaticPrefs_network.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/Tokenizer.h"
+#include "mozilla/UniquePtr.h"
+// Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
+#include "DNSLogging.h"
+#include "mozilla/glean/GleanMetrics.h"
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS(TRR, nsIHttpPushListener, nsIInterfaceRequestor,
+ nsIStreamListener, nsIRunnable, nsITimerCallback)
+
+// when firing off a normal A or AAAA query
+TRR::TRR(AHostResolver* aResolver, nsHostRecord* aRec, enum TrrType aType)
+ : mozilla::Runnable("TRR"),
+ mRec(aRec),
+ mHostResolver(aResolver),
+ mType(aType),
+ mOriginSuffix(aRec->originSuffix) {
+ mHost = aRec->host;
+ mPB = aRec->pb;
+ MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(),
+ "TRR must be in parent or socket process");
+}
+
+// when following CNAMEs
+TRR::TRR(AHostResolver* aResolver, nsHostRecord* aRec, nsCString& aHost,
+ enum TrrType& aType, unsigned int aLoopCount, bool aPB)
+ : mozilla::Runnable("TRR"),
+ mHost(aHost),
+ mRec(aRec),
+ mHostResolver(aResolver),
+ mType(aType),
+ mPB(aPB),
+ mCnameLoop(aLoopCount),
+ mOriginSuffix(aRec ? aRec->originSuffix : ""_ns) {
+ MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(),
+ "TRR must be in parent or socket process");
+}
+
+// used on push
+TRR::TRR(AHostResolver* aResolver, bool aPB)
+ : mozilla::Runnable("TRR"), mHostResolver(aResolver), mPB(aPB) {
+ MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(),
+ "TRR must be in parent or socket process");
+}
+
+// to verify a domain
+TRR::TRR(AHostResolver* aResolver, nsACString& aHost, enum TrrType aType,
+ const nsACString& aOriginSuffix, bool aPB, bool aUseFreshConnection)
+ : mozilla::Runnable("TRR"),
+ mHost(aHost),
+ mRec(nullptr),
+ mHostResolver(aResolver),
+ mType(aType),
+ mPB(aPB),
+ mOriginSuffix(aOriginSuffix),
+ mUseFreshConnection(aUseFreshConnection) {
+ MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(),
+ "TRR must be in parent or socket process");
+}
+
+void TRR::HandleTimeout() {
+ mTimeout = nullptr;
+ RecordReason(TRRSkippedReason::TRR_TIMEOUT);
+ Cancel(NS_ERROR_NET_TIMEOUT_EXTERNAL);
+}
+
+NS_IMETHODIMP
+TRR::Notify(nsITimer* aTimer) {
+ if (aTimer == mTimeout) {
+ HandleTimeout();
+ } else {
+ MOZ_CRASH("Unknown timer");
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TRR::Run() {
+ MOZ_ASSERT_IF(XRE_IsParentProcess() && TRRService::Get(),
+ NS_IsMainThread() || TRRService::Get()->IsOnTRRThread());
+ MOZ_ASSERT_IF(XRE_IsSocketProcess(), NS_IsMainThread());
+
+ if ((TRRService::Get() == nullptr) || NS_FAILED(SendHTTPRequest())) {
+ RecordReason(TRRSkippedReason::TRR_SEND_FAILED);
+ FailData(NS_ERROR_FAILURE);
+ // The dtor will now be run
+ }
+ return NS_OK;
+}
+
+DNSPacket* TRR::GetOrCreateDNSPacket() {
+ if (!mPacket) {
+ mPacket = MakeUnique<DNSPacket>();
+ }
+
+ return mPacket.get();
+}
+
+nsresult TRR::CreateQueryURI(nsIURI** aOutURI) {
+ nsAutoCString uri;
+ nsCOMPtr<nsIURI> dnsURI;
+ if (UseDefaultServer()) {
+ TRRService::Get()->GetURI(uri);
+ } else {
+ uri = mRec->mTrrServer;
+ }
+
+ nsresult rv = NS_NewURI(getter_AddRefs(dnsURI), uri);
+ if (NS_FAILED(rv)) {
+ RecordReason(TRRSkippedReason::TRR_BAD_URL);
+ return rv;
+ }
+
+ dnsURI.forget(aOutURI);
+ return NS_OK;
+}
+
+bool TRR::MaybeBlockRequest() {
+ if (((mType == TRRTYPE_A) || (mType == TRRTYPE_AAAA)) &&
+ mRec->mEffectiveTRRMode != nsIRequest::TRR_ONLY_MODE) {
+ // let NS resolves skip the blocklist check
+ // we also don't check the blocklist for TRR only requests
+ MOZ_ASSERT(mRec);
+
+ // If TRRService isn't enabled anymore for the req, don't do TRR.
+ if (!TRRService::Get()->Enabled(mRec->mEffectiveTRRMode)) {
+ RecordReason(TRRSkippedReason::TRR_MODE_NOT_ENABLED);
+ return true;
+ }
+
+ if (!StaticPrefs::network_trr_strict_native_fallback() &&
+ UseDefaultServer() &&
+ TRRService::Get()->IsTemporarilyBlocked(mHost, mOriginSuffix, mPB,
+ true)) {
+ if (mType == TRRTYPE_A) {
+ // count only blocklist for A records to avoid double counts
+ Telemetry::Accumulate(Telemetry::DNS_TRR_BLACKLISTED3,
+ TRRService::ProviderKey(), true);
+ }
+
+ RecordReason(TRRSkippedReason::TRR_HOST_BLOCKED_TEMPORARY);
+ // not really an error but no TRR is issued
+ return true;
+ }
+
+ if (TRRService::Get()->IsExcludedFromTRR(mHost)) {
+ RecordReason(TRRSkippedReason::TRR_EXCLUDED);
+ return true;
+ }
+
+ if (UseDefaultServer() && (mType == TRRTYPE_A)) {
+ Telemetry::Accumulate(Telemetry::DNS_TRR_BLACKLISTED3,
+ TRRService::ProviderKey(), false);
+ }
+ }
+
+ return false;
+}
+
+nsresult TRR::SendHTTPRequest() {
+ // This is essentially the "run" method - created from nsHostResolver
+ if (mCancelled) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if ((mType != TRRTYPE_A) && (mType != TRRTYPE_AAAA) &&
+ (mType != TRRTYPE_NS) && (mType != TRRTYPE_TXT) &&
+ (mType != TRRTYPE_HTTPSSVC)) {
+ // limit the calling interface because nsHostResolver has explicit slots for
+ // these types
+ return NS_ERROR_FAILURE;
+ }
+
+ if (MaybeBlockRequest()) {
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ LOG(("TRR::SendHTTPRequest resolve %s type %u\n", mHost.get(), mType));
+
+ nsAutoCString body;
+ bool disableECS = StaticPrefs::network_trr_disable_ECS();
+ nsresult rv =
+ GetOrCreateDNSPacket()->EncodeRequest(body, mHost, mType, disableECS);
+ if (NS_FAILED(rv)) {
+ HandleEncodeError(rv);
+ return rv;
+ }
+
+ bool useGet = StaticPrefs::network_trr_useGET();
+ nsCOMPtr<nsIURI> dnsURI;
+ rv = CreateQueryURI(getter_AddRefs(dnsURI));
+ if (NS_FAILED(rv)) {
+ LOG(("TRR:SendHTTPRequest: NewURI failed!\n"));
+ return rv;
+ }
+
+ if (useGet) {
+ /* For GET requests, the outgoing packet needs to be Base64url-encoded and
+ then appended to the end of the URI. */
+ nsAutoCString encoded;
+ rv = Base64URLEncode(body.Length(),
+ reinterpret_cast<const unsigned char*>(body.get()),
+ Base64URLEncodePaddingPolicy::Omit, encoded);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString query;
+ rv = dnsURI->GetQuery(query);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (query.IsEmpty()) {
+ query.Assign("?dns="_ns);
+ } else {
+ query.Append("&dns="_ns);
+ }
+ query.Append(encoded);
+
+ rv = NS_MutateURI(dnsURI).SetQuery(query).Finalize(dnsURI);
+ LOG(("TRR::SendHTTPRequest GET dns=%s\n", body.get()));
+ }
+
+ nsCOMPtr<nsIChannel> channel;
+ bool useOHTTP = StaticPrefs::network_trr_use_ohttp();
+ if (useOHTTP) {
+ nsCOMPtr<nsIObliviousHttpService> ohttpService(
+ do_GetService("@mozilla.org/network/oblivious-http-service;1"));
+ if (!ohttpService) {
+ return NS_ERROR_FAILURE;
+ }
+ nsCOMPtr<nsIURI> relayURI;
+ nsTArray<uint8_t> encodedConfig;
+ rv = ohttpService->GetTRRSettings(getter_AddRefs(relayURI), encodedConfig);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (!relayURI) {
+ return NS_ERROR_FAILURE;
+ }
+ rv = ohttpService->NewChannel(relayURI, dnsURI, encodedConfig,
+ getter_AddRefs(channel));
+ } else {
+ rv = DNSUtils::CreateChannelHelper(dnsURI, getter_AddRefs(channel));
+ }
+ if (NS_FAILED(rv) || !channel) {
+ LOG(("TRR:SendHTTPRequest: NewChannel failed!\n"));
+ return rv;
+ }
+
+ auto loadFlags = nsIRequest::LOAD_ANONYMOUS | nsIRequest::INHIBIT_CACHING |
+ nsIRequest::LOAD_BYPASS_CACHE |
+ nsIChannel::LOAD_BYPASS_URL_CLASSIFIER;
+ if (mUseFreshConnection) {
+ // Causes TRRServiceChannel to tell the connection manager
+ // to clear out any connection with the current conn info.
+ loadFlags |= nsIRequest::LOAD_FRESH_CONNECTION;
+ }
+ channel->SetLoadFlags(loadFlags);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = channel->SetNotificationCallbacks(this);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
+ if (!httpChannel) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // This connection should not use TRR
+ rv = httpChannel->SetTRRMode(nsIRequest::TRR_DISABLED_MODE);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCString contentType(ContentType());
+ rv = httpChannel->SetRequestHeader("Accept"_ns, contentType, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString cred;
+ if (UseDefaultServer()) {
+ TRRService::Get()->GetCredentials(cred);
+ }
+ if (!cred.IsEmpty()) {
+ rv = httpChannel->SetRequestHeader("Authorization"_ns, cred, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(channel);
+ if (!internalChannel) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // setting a small stream window means the h2 stack won't pipeline a window
+ // update with each HEADERS or reply to a DATA with a WINDOW UPDATE
+ rv = internalChannel->SetInitialRwin(127 * 1024);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = internalChannel->SetIsTRRServiceChannel(true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (UseDefaultServer() && StaticPrefs::network_trr_async_connInfo()) {
+ RefPtr<nsHttpConnectionInfo> trrConnInfo =
+ TRRService::Get()->TRRConnectionInfo();
+ if (trrConnInfo) {
+ nsAutoCString host;
+ dnsURI->GetHost(host);
+ if (host.Equals(trrConnInfo->GetOrigin())) {
+ internalChannel->SetConnectionInfo(trrConnInfo);
+ LOG(("TRR::SendHTTPRequest use conn info:%s\n",
+ trrConnInfo->HashKey().get()));
+ } else {
+ MOZ_DIAGNOSTIC_ASSERT(false);
+ }
+ } else {
+ TRRService::Get()->InitTRRConnectionInfo();
+ }
+ }
+
+ if (useGet) {
+ rv = httpChannel->SetRequestMethod("GET"_ns);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else {
+ nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(httpChannel);
+ if (!uploadChannel) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ uint32_t streamLength = body.Length();
+ nsCOMPtr<nsIInputStream> uploadStream;
+ rv =
+ NS_NewCStringInputStream(getter_AddRefs(uploadStream), std::move(body));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = uploadChannel->ExplicitSetUploadStream(uploadStream, contentType,
+ streamLength, "POST"_ns, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ rv = SetupTRRServiceChannelInternal(httpChannel, useGet, contentType);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = httpChannel->AsyncOpen(this);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // If the asyncOpen succeeded we can say that we actually attempted to
+ // use the TRR connection.
+ RefPtr<AddrHostRecord> addrRec = do_QueryObject(mRec);
+ if (addrRec) {
+ addrRec->mResolverType = ResolverType();
+ }
+
+ NS_NewTimerWithCallback(
+ getter_AddRefs(mTimeout), this,
+ mTimeoutMs ? mTimeoutMs : TRRService::Get()->GetRequestTimeout(),
+ nsITimer::TYPE_ONE_SHOT);
+
+ mChannel = channel;
+ return NS_OK;
+}
+
+// static
+nsresult TRR::SetupTRRServiceChannelInternal(nsIHttpChannel* aChannel,
+ bool aUseGet,
+ const nsACString& aContentType) {
+ nsCOMPtr<nsIHttpChannel> httpChannel = aChannel;
+ MOZ_ASSERT(httpChannel);
+
+ nsresult rv = NS_OK;
+ if (!aUseGet) {
+ rv =
+ httpChannel->SetRequestHeader("Cache-Control"_ns, "no-store"_ns, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Sanitize the request by removing the Accept-Language header so we minimize
+ // the amount of fingerprintable information we send to the server.
+ if (!StaticPrefs::network_trr_send_accept_language_headers()) {
+ rv = httpChannel->SetRequestHeader("Accept-Language"_ns, ""_ns, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Sanitize the request by removing the User-Agent
+ if (!StaticPrefs::network_trr_send_user_agent_headers()) {
+ rv = httpChannel->SetRequestHeader("User-Agent"_ns, ""_ns, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ if (StaticPrefs::network_trr_send_empty_accept_encoding_headers()) {
+ rv = httpChannel->SetEmptyRequestHeader("Accept-Encoding"_ns);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // set the *default* response content type
+ if (NS_FAILED(httpChannel->SetContentType(aContentType))) {
+ LOG(("TRR::SetupTRRServiceChannelInternal: couldn't set content-type!\n"));
+ }
+
+ nsCOMPtr<nsITimedChannel> timedChan(do_QueryInterface(httpChannel));
+ if (timedChan) {
+ timedChan->SetTimingEnabled(true);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TRR::GetInterface(const nsIID& iid, void** result) {
+ if (!iid.Equals(NS_GET_IID(nsIHttpPushListener))) {
+ return NS_ERROR_NO_INTERFACE;
+ }
+
+ nsCOMPtr<nsIHttpPushListener> copy(this);
+ *result = copy.forget().take();
+ return NS_OK;
+}
+
+nsresult TRR::DohDecodeQuery(const nsCString& query, nsCString& host,
+ enum TrrType& type) {
+ FallibleTArray<uint8_t> binary;
+ bool found_dns = false;
+ LOG(("TRR::DohDecodeQuery %s!\n", query.get()));
+
+ // extract "dns=" from the query string
+ nsAutoCString data;
+ for (const nsACString& token :
+ nsCCharSeparatedTokenizer(query, '&').ToRange()) {
+ nsDependentCSubstring dns = Substring(token, 0, 4);
+ nsAutoCString check(dns);
+ if (check.Equals("dns=")) {
+ nsDependentCSubstring q = Substring(token, 4, -1);
+ data = q;
+ found_dns = true;
+ break;
+ }
+ }
+ if (!found_dns) {
+ LOG(("TRR::DohDecodeQuery no dns= in pushed URI query string\n"));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ nsresult rv =
+ Base64URLDecode(data, Base64URLDecodePaddingPolicy::Ignore, binary);
+ NS_ENSURE_SUCCESS(rv, rv);
+ uint32_t avail = binary.Length();
+ if (avail < 12) {
+ return NS_ERROR_FAILURE;
+ }
+ // check the query bit and the opcode
+ if ((binary[2] & 0xf8) != 0) {
+ return NS_ERROR_FAILURE;
+ }
+ uint32_t qdcount = (binary[4] << 8) + binary[5];
+ if (!qdcount) {
+ return NS_ERROR_FAILURE;
+ }
+
+ uint32_t index = 12;
+ uint32_t length = 0;
+ host.Truncate();
+ do {
+ if (avail < (index + 1)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ length = binary[index];
+ if (length) {
+ if (host.Length()) {
+ host.Append(".");
+ }
+ if (avail < (index + 1 + length)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ host.Append((const char*)(&binary[0]) + index + 1, length);
+ }
+ index += 1 + length; // skip length byte + label
+ } while (length);
+
+ LOG(("TRR::DohDecodeQuery host %s\n", host.get()));
+
+ if (avail < (index + 2)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ uint16_t i16 = 0;
+ i16 += binary[index] << 8;
+ i16 += binary[index + 1];
+ type = (enum TrrType)i16;
+
+ LOG(("TRR::DohDecodeQuery type %d\n", (int)type));
+
+ return NS_OK;
+}
+
+nsresult TRR::ReceivePush(nsIHttpChannel* pushed, nsHostRecord* pushedRec) {
+ if (!mHostResolver) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ LOG(("TRR::ReceivePush: PUSH incoming!\n"));
+
+ nsCOMPtr<nsIURI> uri;
+ pushed->GetURI(getter_AddRefs(uri));
+ nsAutoCString query;
+ if (uri) {
+ uri->GetQuery(query);
+ }
+
+ if (NS_FAILED(DohDecodeQuery(query, mHost, mType)) ||
+ HostIsIPLiteral(mHost)) { // literal
+ LOG(("TRR::ReceivePush failed to decode %s\n", mHost.get()));
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if ((mType != TRRTYPE_A) && (mType != TRRTYPE_AAAA) &&
+ (mType != TRRTYPE_TXT) && (mType != TRRTYPE_HTTPSSVC)) {
+ LOG(("TRR::ReceivePush unknown type %d\n", mType));
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (TRRService::Get()->IsExcludedFromTRR(mHost)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ uint32_t type = nsIDNSService::RESOLVE_TYPE_DEFAULT;
+ if (mType == TRRTYPE_TXT) {
+ type = nsIDNSService::RESOLVE_TYPE_TXT;
+ } else if (mType == TRRTYPE_HTTPSSVC) {
+ type = nsIDNSService::RESOLVE_TYPE_HTTPSSVC;
+ }
+
+ RefPtr<nsHostRecord> hostRecord;
+ nsresult rv;
+ rv = mHostResolver->GetHostRecord(
+ mHost, ""_ns, type, pushedRec->flags, pushedRec->af, pushedRec->pb,
+ pushedRec->originSuffix, getter_AddRefs(hostRecord));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // Since we don't ever call nsHostResolver::NameLookup for this record,
+ // we need to copy the trr mode from the previous record
+ if (hostRecord->mEffectiveTRRMode == nsIRequest::TRR_DEFAULT_MODE) {
+ hostRecord->mEffectiveTRRMode =
+ static_cast<nsIRequest::TRRMode>(pushedRec->mEffectiveTRRMode);
+ }
+
+ rv = mHostResolver->TrrLookup_unlocked(hostRecord, this);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = pushed->AsyncOpen(this);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // OK!
+ mChannel = pushed;
+ mRec.swap(hostRecord);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TRR::OnPush(nsIHttpChannel* associated, nsIHttpChannel* pushed) {
+ LOG(("TRR::OnPush entry\n"));
+ MOZ_ASSERT(associated == mChannel);
+ if (!mRec) {
+ return NS_ERROR_FAILURE;
+ }
+ if (!UseDefaultServer()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<TRR> trr = new TRR(mHostResolver, mPB);
+ return trr->ReceivePush(pushed, mRec);
+}
+
+NS_IMETHODIMP
+TRR::OnStartRequest(nsIRequest* aRequest) {
+ LOG(("TRR::OnStartRequest %p %s %d\n", this, mHost.get(), mType));
+
+ nsresult status = NS_OK;
+ aRequest->GetStatus(&status);
+
+ if (NS_FAILED(status)) {
+ if (NS_IsOffline()) {
+ RecordReason(TRRSkippedReason::TRR_IS_OFFLINE);
+ }
+
+ switch (status) {
+ case NS_ERROR_UNKNOWN_HOST:
+ RecordReason(TRRSkippedReason::TRR_CHANNEL_DNS_FAIL);
+ break;
+ case NS_ERROR_OFFLINE:
+ RecordReason(TRRSkippedReason::TRR_IS_OFFLINE);
+ break;
+ case NS_ERROR_NET_RESET:
+ RecordReason(TRRSkippedReason::TRR_NET_RESET);
+ break;
+ case NS_ERROR_NET_TIMEOUT:
+ case NS_ERROR_NET_TIMEOUT_EXTERNAL:
+ RecordReason(TRRSkippedReason::TRR_NET_TIMEOUT);
+ break;
+ case NS_ERROR_PROXY_CONNECTION_REFUSED:
+ RecordReason(TRRSkippedReason::TRR_NET_REFUSED);
+ break;
+ case NS_ERROR_NET_INTERRUPT:
+ RecordReason(TRRSkippedReason::TRR_NET_INTERRUPT);
+ break;
+ case NS_ERROR_NET_INADEQUATE_SECURITY:
+ RecordReason(TRRSkippedReason::TRR_NET_INADEQ_SEQURITY);
+ break;
+ default:
+ RecordReason(TRRSkippedReason::TRR_UNKNOWN_CHANNEL_FAILURE);
+ }
+ }
+
+ return NS_OK;
+}
+
+void TRR::SaveAdditionalRecords(
+ const nsClassHashtable<nsCStringHashKey, DOHresp>& aRecords) {
+ if (!mRec) {
+ return;
+ }
+ nsresult rv;
+ for (const auto& recordEntry : aRecords) {
+ if (!recordEntry.GetData() || recordEntry.GetData()->mAddresses.IsEmpty()) {
+ // no point in adding empty records.
+ continue;
+ }
+ // If IPv6 is disabled don't add anything else than IPv4.
+ if (StaticPrefs::network_dns_disableIPv6() &&
+ std::find_if(recordEntry.GetData()->mAddresses.begin(),
+ recordEntry.GetData()->mAddresses.end(),
+ [](const NetAddr& addr) { return !addr.IsIPAddrV4(); }) !=
+ recordEntry.GetData()->mAddresses.end()) {
+ continue;
+ }
+ RefPtr<nsHostRecord> hostRecord;
+ rv = mHostResolver->GetHostRecord(
+ recordEntry.GetKey(), EmptyCString(),
+ nsIDNSService::RESOLVE_TYPE_DEFAULT, mRec->flags, AF_UNSPEC, mRec->pb,
+ mRec->originSuffix, getter_AddRefs(hostRecord));
+ if (NS_FAILED(rv)) {
+ LOG(("Failed to get host record for additional record %s",
+ nsCString(recordEntry.GetKey()).get()));
+ continue;
+ }
+ RefPtr<AddrInfo> ai(
+ new AddrInfo(recordEntry.GetKey(), ResolverType(), TRRTYPE_A,
+ std::move(recordEntry.GetData()->mAddresses),
+ recordEntry.GetData()->mTtl));
+ mHostResolver->MaybeRenewHostRecord(hostRecord);
+
+ // Since we're not actually calling NameLookup for this record, we need
+ // to set these fields to avoid assertions in CompleteLookup.
+ // This is quite hacky, and should be fixed.
+ hostRecord->Reset();
+ hostRecord->mResolving++;
+ hostRecord->mEffectiveTRRMode =
+ static_cast<nsIRequest::TRRMode>(mRec->mEffectiveTRRMode);
+ LOG(("Completing lookup for additional: %s",
+ nsCString(recordEntry.GetKey()).get()));
+ (void)mHostResolver->CompleteLookup(hostRecord, NS_OK, ai, mPB,
+ mOriginSuffix, TRRSkippedReason::TRR_OK,
+ this);
+ }
+}
+
+void TRR::StoreIPHintAsDNSRecord(const struct SVCB& aSVCBRecord) {
+ LOG(("TRR::StoreIPHintAsDNSRecord [%p] [%s]", this,
+ aSVCBRecord.mSvcDomainName.get()));
+ CopyableTArray<NetAddr> addresses;
+ aSVCBRecord.GetIPHints(addresses);
+
+ if (StaticPrefs::network_dns_disableIPv6()) {
+ addresses.RemoveElementsBy(
+ [](const NetAddr& addr) { return !addr.IsIPAddrV4(); });
+ }
+
+ if (addresses.IsEmpty()) {
+ return;
+ }
+
+ RefPtr<nsHostRecord> hostRecord;
+ nsresult rv = mHostResolver->GetHostRecord(
+ aSVCBRecord.mSvcDomainName, EmptyCString(),
+ nsIDNSService::RESOLVE_TYPE_DEFAULT,
+ mRec->flags | nsIDNSService::RESOLVE_IP_HINT, AF_UNSPEC, mRec->pb,
+ mRec->originSuffix, getter_AddRefs(hostRecord));
+ if (NS_FAILED(rv)) {
+ LOG(("Failed to get host record"));
+ return;
+ }
+
+ mHostResolver->MaybeRenewHostRecord(hostRecord);
+
+ RefPtr<AddrInfo> ai(new AddrInfo(aSVCBRecord.mSvcDomainName, ResolverType(),
+ TRRTYPE_A, std::move(addresses), mTTL));
+
+ // Since we're not actually calling NameLookup for this record, we need
+ // to set these fields to avoid assertions in CompleteLookup.
+ // This is quite hacky, and should be fixed.
+ hostRecord->mResolving++;
+ hostRecord->mEffectiveTRRMode =
+ static_cast<nsIRequest::TRRMode>(mRec->mEffectiveTRRMode);
+ (void)mHostResolver->CompleteLookup(hostRecord, NS_OK, ai, mPB, mOriginSuffix,
+ TRRSkippedReason::TRR_OK, this);
+}
+
+nsresult TRR::ReturnData(nsIChannel* aChannel) {
+ if (mType != TRRTYPE_TXT && mType != TRRTYPE_HTTPSSVC) {
+ // create and populate an AddrInfo instance to pass on
+ RefPtr<AddrInfo> ai(new AddrInfo(mHost, ResolverType(), mType,
+ nsTArray<NetAddr>(), mDNS.mTtl));
+ auto builder = ai->Build();
+ builder.SetAddresses(std::move(mDNS.mAddresses));
+ builder.SetCanonicalHostname(mCname);
+
+ // Set timings.
+ nsCOMPtr<nsITimedChannel> timedChan = do_QueryInterface(aChannel);
+ if (timedChan) {
+ TimeStamp asyncOpen, start, end;
+ if (NS_SUCCEEDED(timedChan->GetAsyncOpen(&asyncOpen)) &&
+ !asyncOpen.IsNull()) {
+ builder.SetTrrFetchDuration(
+ (TimeStamp::Now() - asyncOpen).ToMilliseconds());
+ }
+ if (NS_SUCCEEDED(timedChan->GetRequestStart(&start)) &&
+ NS_SUCCEEDED(timedChan->GetResponseEnd(&end)) && !start.IsNull() &&
+ !end.IsNull()) {
+ builder.SetTrrFetchDurationNetworkOnly((end - start).ToMilliseconds());
+ }
+ }
+ ai = builder.Finish();
+
+ if (!mHostResolver) {
+ return NS_ERROR_FAILURE;
+ }
+ RecordReason(TRRSkippedReason::TRR_OK);
+ (void)mHostResolver->CompleteLookup(mRec, NS_OK, ai, mPB, mOriginSuffix,
+ mTRRSkippedReason, this);
+ mHostResolver = nullptr;
+ mRec = nullptr;
+ } else {
+ RecordReason(TRRSkippedReason::TRR_OK);
+ (void)mHostResolver->CompleteLookupByType(mRec, NS_OK, mResult,
+ mTRRSkippedReason, mTTL, mPB);
+ }
+ return NS_OK;
+}
+
+nsresult TRR::FailData(nsresult error) {
+ if (!mHostResolver) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // If we didn't record a reason until now, record a default one.
+ RecordReason(TRRSkippedReason::TRR_FAILED);
+
+ if (mType == TRRTYPE_TXT || mType == TRRTYPE_HTTPSSVC) {
+ TypeRecordResultType empty(Nothing{});
+ (void)mHostResolver->CompleteLookupByType(mRec, error, empty,
+ mTRRSkippedReason, 0, mPB);
+ } else {
+ // create and populate an TRR AddrInfo instance to pass on to signal that
+ // this comes from TRR
+ nsTArray<NetAddr> noAddresses;
+ RefPtr<AddrInfo> ai =
+ new AddrInfo(mHost, ResolverType(), mType, std::move(noAddresses));
+
+ (void)mHostResolver->CompleteLookup(mRec, error, ai, mPB, mOriginSuffix,
+ mTRRSkippedReason, this);
+ }
+
+ mHostResolver = nullptr;
+ mRec = nullptr;
+ return NS_OK;
+}
+
+void TRR::HandleDecodeError(nsresult aStatusCode) {
+ auto rcode = mPacket->GetRCode();
+ if (rcode.isOk() && rcode.unwrap() != 0) {
+ if (rcode.unwrap() == 0x03) {
+ RecordReason(TRRSkippedReason::TRR_NXDOMAIN);
+ } else {
+ RecordReason(TRRSkippedReason::TRR_RCODE_FAIL);
+ }
+ } else if (aStatusCode == NS_ERROR_UNKNOWN_HOST ||
+ aStatusCode == NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
+ RecordReason(TRRSkippedReason::TRR_NO_ANSWERS);
+ } else {
+ RecordReason(TRRSkippedReason::TRR_DECODE_FAILED);
+ }
+}
+
+bool TRR::HasUsableResponse() {
+ if (mType == TRRTYPE_A || mType == TRRTYPE_AAAA) {
+ return !mDNS.mAddresses.IsEmpty();
+ }
+ if (mType == TRRTYPE_TXT) {
+ return mResult.is<TypeRecordTxt>();
+ }
+ if (mType == TRRTYPE_HTTPSSVC) {
+ return mResult.is<TypeRecordHTTPSSVC>();
+ }
+ return false;
+}
+
+nsresult TRR::FollowCname(nsIChannel* aChannel) {
+ nsresult rv = NS_OK;
+ nsAutoCString cname;
+ while (NS_SUCCEEDED(rv) && mDNS.mAddresses.IsEmpty() && !mCname.IsEmpty() &&
+ mCnameLoop > 0) {
+ mCnameLoop--;
+ LOG(("TRR::On200Response CNAME %s => %s (%u)\n", mHost.get(), mCname.get(),
+ mCnameLoop));
+ cname = mCname;
+ mCname.Truncate();
+
+ LOG(("TRR: check for CNAME record for %s within previous response\n",
+ cname.get()));
+ nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords;
+ rv = GetOrCreateDNSPacket()->Decode(
+ cname, mType, mCname, StaticPrefs::network_trr_allow_rfc1918(), mDNS,
+ mResult, additionalRecords, mTTL);
+ if (NS_FAILED(rv)) {
+ LOG(("TRR::FollowCname DohDecode %x\n", (int)rv));
+ HandleDecodeError(rv);
+ }
+ }
+
+ // restore mCname as DohDecode() change it
+ mCname = cname;
+ if (NS_SUCCEEDED(rv) && HasUsableResponse()) {
+ ReturnData(aChannel);
+ return NS_OK;
+ }
+
+ bool ra = mPacket && mPacket->RecursionAvailable().unwrapOr(false);
+ LOG(("ra = %d", ra));
+ if (rv == NS_ERROR_UNKNOWN_HOST && ra) {
+ // If recursion is available, but no addresses have been returned,
+ // we can just return a failure here.
+ LOG(("TRR::FollowCname not sending another request as RA flag is set."));
+ FailData(NS_ERROR_UNKNOWN_HOST);
+ return NS_OK;
+ }
+
+ if (!mCnameLoop) {
+ LOG(("TRR::On200Response CNAME loop, eject!\n"));
+ return NS_ERROR_REDIRECT_LOOP;
+ }
+
+ LOG(("TRR::On200Response CNAME %s => %s (%u)\n", mHost.get(), mCname.get(),
+ mCnameLoop));
+ RefPtr<TRR> trr =
+ new TRR(mHostResolver, mRec, mCname, mType, mCnameLoop, mPB);
+ if (!TRRService::Get()) {
+ return NS_ERROR_FAILURE;
+ }
+ return TRRService::Get()->DispatchTRRRequest(trr);
+}
+
+nsresult TRR::On200Response(nsIChannel* aChannel) {
+ // decode body and create an AddrInfo struct for the response
+ nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords;
+ RefPtr<TypeHostRecord> typeRec = do_QueryObject(mRec);
+ if (typeRec && typeRec->mOriginHost) {
+ GetOrCreateDNSPacket()->SetOriginHost(typeRec->mOriginHost);
+ }
+ nsresult rv = GetOrCreateDNSPacket()->Decode(
+ mHost, mType, mCname, StaticPrefs::network_trr_allow_rfc1918(), mDNS,
+ mResult, additionalRecords, mTTL);
+ if (NS_FAILED(rv)) {
+ LOG(("TRR::On200Response DohDecode %x\n", (int)rv));
+ HandleDecodeError(rv);
+ return rv;
+ }
+ if (StaticPrefs::network_trr_add_additional_records()) {
+ SaveAdditionalRecords(additionalRecords);
+ }
+
+ if (mResult.is<TypeRecordHTTPSSVC>()) {
+ auto& results = mResult.as<TypeRecordHTTPSSVC>();
+ for (const auto& rec : results) {
+ StoreIPHintAsDNSRecord(rec);
+ }
+ }
+
+ if (!mDNS.mAddresses.IsEmpty() || mType == TRRTYPE_TXT || mCname.IsEmpty()) {
+ // pass back the response data
+ ReturnData(aChannel);
+ return NS_OK;
+ }
+
+ LOG(("TRR::On200Response trying CNAME %s", mCname.get()));
+ return FollowCname(aChannel);
+}
+
+void TRR::RecordProcessingTime(nsIChannel* aChannel) {
+ // This method records the time it took from the last received byte of the
+ // DoH response until we've notified the consumer with a host record.
+ nsCOMPtr<nsITimedChannel> timedChan = do_QueryInterface(aChannel);
+ if (!timedChan) {
+ return;
+ }
+ TimeStamp end;
+ if (NS_FAILED(timedChan->GetResponseEnd(&end))) {
+ return;
+ }
+
+ if (end.IsNull()) {
+ return;
+ }
+
+ Telemetry::AccumulateTimeDelta(Telemetry::DNS_TRR_PROCESSING_TIME, end);
+
+ LOG(("Processing DoH response took %f ms",
+ (TimeStamp::Now() - end).ToMilliseconds()));
+}
+
+void TRR::ReportStatus(nsresult aStatusCode) {
+ // If the TRR was cancelled by nsHostResolver, then we don't need to report
+ // it as failed; otherwise it can cause the confirmation to fail.
+ if (UseDefaultServer() && aStatusCode != NS_ERROR_ABORT) {
+ // Bad content is still considered "okay" if the HTTP response is okay
+ TRRService::Get()->RecordTRRStatus(this);
+ }
+}
+
+static void RecordHttpVersion(nsIHttpChannel* aHttpChannel) {
+ nsCOMPtr<nsIHttpChannelInternal> internalChannel =
+ do_QueryInterface(aHttpChannel);
+ if (!internalChannel) {
+ LOG(("RecordHttpVersion: Failed to QI nsIHttpChannelInternal"));
+ return;
+ }
+
+ uint32_t major, minor;
+ if (NS_FAILED(internalChannel->GetResponseVersion(&major, &minor))) {
+ LOG(("RecordHttpVersion: Failed to get protocol version"));
+ return;
+ }
+
+ auto label = Telemetry::LABELS_DNS_TRR_HTTP_VERSION2::h_1;
+ if (major == 2) {
+ label = Telemetry::LABELS_DNS_TRR_HTTP_VERSION2::h_2;
+ } else if (major == 3) {
+ label = Telemetry::LABELS_DNS_TRR_HTTP_VERSION2::h_3;
+ }
+
+ Telemetry::AccumulateCategoricalKeyed(TRRService::ProviderKey(), label);
+
+ LOG(("RecordHttpVersion: Provider responded using HTTP version: %d", major));
+}
+
+NS_IMETHODIMP
+TRR::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
+ // The dtor will be run after the function returns
+ LOG(("TRR:OnStopRequest %p %s %d failed=%d code=%X\n", this, mHost.get(),
+ mType, mFailed, (unsigned int)aStatusCode));
+ nsCOMPtr<nsIChannel> channel;
+ channel.swap(mChannel);
+
+ mChannelStatus = aStatusCode;
+ if (NS_SUCCEEDED(aStatusCode)) {
+ nsCString label = "regular"_ns;
+ if (mPB) {
+ label = "private"_ns;
+ }
+ mozilla::glean::networking::trr_request_count.Get(label).Add(1);
+ }
+
+ {
+ // Cancel the timer since we don't need it anymore.
+ nsCOMPtr<nsITimer> timer;
+ mTimeout.swap(timer);
+ if (timer) {
+ timer->Cancel();
+ }
+ }
+
+ auto scopeExit = MakeScopeExit([&] { ReportStatus(aStatusCode); });
+
+ nsresult rv = NS_OK;
+ // if status was "fine", parse the response and pass on the answer
+ if (!mFailed && NS_SUCCEEDED(aStatusCode)) {
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
+ if (!httpChannel) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ nsAutoCString contentType;
+ httpChannel->GetContentType(contentType);
+ if (contentType.Length() &&
+ !contentType.LowerCaseEqualsASCII(ContentType())) {
+ LOG(("TRR:OnStopRequest %p %s %d wrong content type %s\n", this,
+ mHost.get(), mType, contentType.get()));
+ FailData(NS_ERROR_UNEXPECTED);
+ return NS_OK;
+ }
+
+ uint32_t httpStatus;
+ rv = httpChannel->GetResponseStatus(&httpStatus);
+ if (NS_SUCCEEDED(rv) && httpStatus == 200) {
+ rv = On200Response(channel);
+ if (NS_SUCCEEDED(rv) && UseDefaultServer()) {
+ RecordReason(TRRSkippedReason::TRR_OK);
+ RecordProcessingTime(channel);
+ RecordHttpVersion(httpChannel);
+ return rv;
+ }
+ } else {
+ RecordReason(TRRSkippedReason::TRR_SERVER_RESPONSE_ERR);
+ LOG(("TRR:OnStopRequest:%d %p rv %x httpStatus %d\n", __LINE__, this,
+ (int)rv, httpStatus));
+ }
+ }
+
+ LOG(("TRR:OnStopRequest %p status %x mFailed %d\n", this, (int)aStatusCode,
+ mFailed));
+ FailData(NS_SUCCEEDED(rv) ? NS_ERROR_UNKNOWN_HOST : rv);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TRR::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream,
+ uint64_t aOffset, const uint32_t aCount) {
+ LOG(("TRR:OnDataAvailable %p %s %d failed=%d aCount=%u\n", this, mHost.get(),
+ mType, mFailed, (unsigned int)aCount));
+ // receive DNS response into the local buffer
+ if (mFailed) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv = GetOrCreateDNSPacket()->OnDataAvailable(aRequest, aInputStream,
+ aOffset, aCount);
+ if (NS_FAILED(rv)) {
+ LOG(("TRR::OnDataAvailable:%d fail\n", __LINE__));
+ mFailed = true;
+ return rv;
+ }
+ return NS_OK;
+}
+
+void TRR::Cancel(nsresult aStatus) {
+ bool isTRRServiceChannel = false;
+ nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(
+ do_QueryInterface(mChannel));
+ if (httpChannelInternal) {
+ nsresult rv =
+ httpChannelInternal->GetIsTRRServiceChannel(&isTRRServiceChannel);
+ if (NS_FAILED(rv)) {
+ isTRRServiceChannel = false;
+ }
+ }
+ // nsHttpChannel can be only canceled on the main thread.
+ RefPtr<nsHttpChannel> httpChannel = do_QueryObject(mChannel);
+ if (isTRRServiceChannel && !XRE_IsSocketProcess() && !httpChannel) {
+ if (TRRService::Get()) {
+ nsCOMPtr<nsIThread> thread = TRRService::Get()->TRRThread();
+ if (thread && !thread->IsOnCurrentThread()) {
+ thread->Dispatch(NS_NewRunnableFunction(
+ "TRR::Cancel",
+ [self = RefPtr(this), aStatus]() { self->Cancel(aStatus); }));
+ return;
+ }
+ }
+ } else {
+ if (!NS_IsMainThread()) {
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "TRR::Cancel",
+ [self = RefPtr(this), aStatus]() { self->Cancel(aStatus); }));
+ return;
+ }
+ }
+
+ if (mCancelled) {
+ return;
+ }
+ mCancelled = true;
+
+ if (mChannel) {
+ RecordReason(TRRSkippedReason::TRR_REQ_CANCELLED);
+ LOG(("TRR: %p canceling Channel %p %s %d status=%" PRIx32 "\n", this,
+ mChannel.get(), mHost.get(), mType, static_cast<uint32_t>(aStatus)));
+ mChannel->Cancel(aStatus);
+ }
+}
+
+bool TRR::UseDefaultServer() { return !mRec || mRec->mTrrServer.IsEmpty(); }
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/TRR.h b/netwerk/dns/TRR.h
new file mode 100644
index 0000000000..82244c97a0
--- /dev/null
+++ b/netwerk/dns/TRR.h
@@ -0,0 +1,159 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#ifndef mozilla_net_TRR_h
+#define mozilla_net_TRR_h
+
+#include "mozilla/net/DNSByTypeRecord.h"
+#include "mozilla/Assertions.h"
+#include "nsClassHashtable.h"
+#include "nsIChannel.h"
+#include "nsIHttpPushListener.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIStreamListener.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+#include "DNSPacket.h"
+#include "nsITRRSkipReason.h"
+
+class AHostResolver;
+class nsHostRecord;
+
+namespace mozilla {
+namespace net {
+
+class TRRService;
+class TRRServiceChannel;
+
+class TRR : public Runnable,
+ public nsITimerCallback,
+ public nsIHttpPushListener,
+ public nsIInterfaceRequestor,
+ public nsIStreamListener {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIHTTPPUSHLISTENER
+ NS_DECL_NSIINTERFACEREQUESTOR
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+ NS_DECL_NSITIMERCALLBACK
+
+ // Number of "steps" we follow CNAME chains
+ static const unsigned int kCnameChaseMax = 64;
+
+ // when firing off a normal A or AAAA query
+ explicit TRR(AHostResolver* aResolver, nsHostRecord* aRec,
+ enum TrrType aType);
+ // when following CNAMEs
+ explicit TRR(AHostResolver* aResolver, nsHostRecord* aRec, nsCString& aHost,
+ enum TrrType& aType, unsigned int aLoopCount, bool aPB);
+ // used on push
+ explicit TRR(AHostResolver* aResolver, bool aPB);
+ // to verify a domain
+ explicit TRR(AHostResolver* aResolver, nsACString& aHost, enum TrrType aType,
+ const nsACString& aOriginSuffix, bool aPB,
+ bool aUseFreshConnection);
+
+ NS_IMETHOD Run() override;
+ void Cancel(nsresult aStatus);
+ enum TrrType Type() { return mType; }
+ nsCString mHost;
+ RefPtr<nsHostRecord> mRec;
+ RefPtr<AHostResolver> mHostResolver;
+
+ void SetTimeout(uint32_t aTimeoutMs) { mTimeoutMs = aTimeoutMs; }
+
+ nsresult ChannelStatus() { return mChannelStatus; }
+
+ enum RequestPurpose {
+ Resolve,
+ Confirmation,
+ Blocklist,
+ };
+
+ RequestPurpose Purpose() { return mPurpose; }
+ void SetPurpose(RequestPurpose aPurpose) { mPurpose = aPurpose; }
+ TRRSkippedReason SkipReason() const { return mTRRSkippedReason; }
+
+ protected:
+ virtual ~TRR() = default;
+ virtual DNSPacket* GetOrCreateDNSPacket();
+ virtual nsresult CreateQueryURI(nsIURI** aOutURI);
+ virtual const char* ContentType() const { return "application/dns-message"; }
+ virtual DNSResolverType ResolverType() const { return DNSResolverType::TRR; }
+ virtual bool MaybeBlockRequest();
+ virtual void RecordProcessingTime(nsIChannel* aChannel);
+ virtual void ReportStatus(nsresult aStatusCode);
+ virtual void HandleTimeout();
+ virtual void HandleEncodeError(nsresult aStatusCode) {}
+ virtual void HandleDecodeError(nsresult aStatusCode);
+ nsresult SendHTTPRequest();
+ nsresult ReturnData(nsIChannel* aChannel);
+
+ // FailData() must be called to signal that the asynch TRR resolve is
+ // completed. For failed name resolves ("no such host"), the 'error' it
+ // passses on in its argument must be NS_ERROR_UNKNOWN_HOST. Other errors
+ // (if host was blocklisted, there as a bad content-type received, etc)
+ // other error codes must be used. This distinction is important for the
+ // subsequent logic to separate the error reasons.
+ nsresult FailData(nsresult error);
+ static nsresult DohDecodeQuery(const nsCString& query, nsCString& host,
+ enum TrrType& type);
+ nsresult ReceivePush(nsIHttpChannel* pushed, nsHostRecord* pushedRec);
+ nsresult On200Response(nsIChannel* aChannel);
+ nsresult FollowCname(nsIChannel* aChannel);
+
+ bool HasUsableResponse();
+
+ bool UseDefaultServer();
+ void SaveAdditionalRecords(
+ const nsClassHashtable<nsCStringHashKey, DOHresp>& aRecords);
+
+ friend class TRRServiceChannel;
+ static nsresult SetupTRRServiceChannelInternal(
+ nsIHttpChannel* aChannel, bool aUseGet, const nsACString& aContentType);
+
+ void StoreIPHintAsDNSRecord(const struct SVCB& aSVCBRecord);
+
+ nsCOMPtr<nsIChannel> mChannel;
+ enum TrrType mType { TRRTYPE_A };
+ UniquePtr<DNSPacket> mPacket;
+ bool mFailed = false;
+ bool mPB = false;
+ DOHresp mDNS;
+ nsresult mChannelStatus = NS_OK;
+
+ RequestPurpose mPurpose = Resolve;
+ Atomic<bool, Relaxed> mCancelled{false};
+
+ // The request timeout in milliseconds. If 0 we will use the default timeout
+ // we get from the prefs.
+ uint32_t mTimeoutMs = 0;
+ nsCOMPtr<nsITimer> mTimeout;
+ nsCString mCname;
+ uint32_t mCnameLoop = kCnameChaseMax; // loop detection counter
+
+ uint32_t mTTL = UINT32_MAX;
+ TypeRecordResultType mResult = mozilla::AsVariant(Nothing());
+
+ TRRSkippedReason mTRRSkippedReason = TRRSkippedReason::TRR_UNSET;
+ void RecordReason(TRRSkippedReason reason) {
+ if (mTRRSkippedReason == TRRSkippedReason::TRR_UNSET) {
+ mTRRSkippedReason = reason;
+ }
+ }
+
+ // keep a copy of the originSuffix for the cases where mRec == nullptr */
+ const nsCString mOriginSuffix;
+
+ // If true, we set LOAD_FRESH_CONNECTION on our channel's load flags.
+ bool mUseFreshConnection = false;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // include guard
diff --git a/netwerk/dns/TRRQuery.cpp b/netwerk/dns/TRRQuery.cpp
new file mode 100644
index 0000000000..7602f586a7
--- /dev/null
+++ b/netwerk/dns/TRRQuery.cpp
@@ -0,0 +1,387 @@
+/* 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 "TRRQuery.h"
+
+#include "mozilla/StaticPrefs_network.h"
+#include "mozilla/Telemetry.h"
+#include "nsQueryObject.h"
+#include "TRR.h"
+#include "TRRService.h"
+// Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
+#include "DNSLogging.h"
+
+namespace mozilla {
+namespace net {
+
+static already_AddRefed<AddrInfo> merge_rrset(AddrInfo* rrto,
+ AddrInfo* rrfrom) {
+ MOZ_ASSERT(rrto && rrfrom);
+ // Each of the arguments are all-IPv4 or all-IPv6 hence judging
+ // by the first element. This is true only for TRR resolutions.
+ bool isIPv6 = rrfrom->Addresses().Length() > 0 &&
+ rrfrom->Addresses()[0].raw.family == PR_AF_INET6;
+
+ nsTArray<NetAddr> addresses;
+ if (isIPv6) {
+ addresses = rrfrom->Addresses().Clone();
+ addresses.AppendElements(rrto->Addresses());
+ } else {
+ addresses = rrto->Addresses().Clone();
+ addresses.AppendElements(rrfrom->Addresses());
+ }
+ auto builder = rrto->Build();
+ builder.SetAddresses(std::move(addresses));
+ return builder.Finish();
+}
+
+void TRRQuery::Cancel(nsresult aStatus) {
+ MutexAutoLock trrlock(mTrrLock);
+ if (mTrrA) {
+ mTrrA->Cancel(aStatus);
+ }
+ if (mTrrAAAA) {
+ mTrrAAAA->Cancel(aStatus);
+ }
+ if (mTrrByType) {
+ mTrrByType->Cancel(aStatus);
+ }
+}
+
+void TRRQuery::MarkSendingTRR(TRR* trr, enum TrrType rectype, MutexAutoLock&) {
+ if (rectype == TRRTYPE_A) {
+ MOZ_ASSERT(!mTrrA);
+ mTrrA = trr;
+ mTrrAUsed = STARTED;
+ } else if (rectype == TRRTYPE_AAAA) {
+ MOZ_ASSERT(!mTrrAAAA);
+ mTrrAAAA = trr;
+ mTrrAAAAUsed = STARTED;
+ } else {
+ LOG(("TrrLookup called with bad type set: %d\n", rectype));
+ MOZ_ASSERT(0);
+ }
+}
+
+void TRRQuery::PrepareQuery(enum TrrType aRecType,
+ nsTArray<RefPtr<TRR>>& aRequestsToSend) {
+ LOG(("TRR Resolve %s type %d\n", mRecord->host.get(), (int)aRecType));
+ RefPtr<TRR> trr = new TRR(this, mRecord, aRecType);
+
+ {
+ MutexAutoLock trrlock(mTrrLock);
+ MarkSendingTRR(trr, aRecType, trrlock);
+ aRequestsToSend.AppendElement(trr);
+ }
+}
+
+bool TRRQuery::SendQueries(nsTArray<RefPtr<TRR>>& aRequestsToSend) {
+ bool madeQuery = false;
+ mTRRRequestCounter = aRequestsToSend.Length();
+ for (const auto& request : aRequestsToSend) {
+ if (NS_SUCCEEDED(TRRService::Get()->DispatchTRRRequest(request))) {
+ madeQuery = true;
+ } else {
+ mTRRRequestCounter--;
+ MutexAutoLock trrlock(mTrrLock);
+ if (request == mTrrA) {
+ mTrrA = nullptr;
+ mTrrAUsed = INIT;
+ }
+ if (request == mTrrAAAA) {
+ mTrrAAAA = nullptr;
+ mTrrAAAAUsed = INIT;
+ }
+ }
+ }
+ aRequestsToSend.Clear();
+ return madeQuery;
+}
+
+nsresult TRRQuery::DispatchLookup(TRR* pushedTRR) {
+ mTrrStart = TimeStamp::Now();
+
+ if (!mRecord->IsAddrRecord()) {
+ return DispatchByTypeLookup(pushedTRR);
+ }
+
+ RefPtr<AddrHostRecord> addrRec = do_QueryObject(mRecord);
+ MOZ_ASSERT(addrRec);
+ if (!addrRec) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ mTrrAUsed = INIT;
+ mTrrAAAAUsed = INIT;
+
+ // Always issue both A and AAAA.
+ // When both are complete we filter out the unneeded results.
+ enum TrrType rectype = (mRecord->af == AF_INET6) ? TRRTYPE_AAAA : TRRTYPE_A;
+
+ if (pushedTRR) {
+ MutexAutoLock trrlock(mTrrLock);
+ rectype = pushedTRR->Type();
+ MarkSendingTRR(pushedTRR, rectype, trrlock);
+ return NS_OK;
+ }
+
+ // Need to dispatch TRR requests after |mTrrA| and |mTrrAAAA| are set
+ // properly so as to avoid the race when CompleteLookup() is called at the
+ // same time.
+ nsTArray<RefPtr<TRR>> requestsToSend;
+ if ((mRecord->af == AF_UNSPEC || mRecord->af == AF_INET6) &&
+ !StaticPrefs::network_dns_disableIPv6()) {
+ PrepareQuery(TRRTYPE_AAAA, requestsToSend);
+ }
+ if (mRecord->af == AF_UNSPEC || mRecord->af == AF_INET) {
+ PrepareQuery(TRRTYPE_A, requestsToSend);
+ }
+
+ if (SendQueries(requestsToSend)) {
+ return NS_OK;
+ }
+
+ return NS_ERROR_UNKNOWN_HOST;
+}
+
+nsresult TRRQuery::DispatchByTypeLookup(TRR* pushedTRR) {
+ RefPtr<TypeHostRecord> typeRec = do_QueryObject(mRecord);
+ MOZ_ASSERT(typeRec);
+ if (!typeRec) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ enum TrrType rectype;
+
+ // XXX this could use a more extensible approach.
+ if (mRecord->type == nsIDNSService::RESOLVE_TYPE_TXT) {
+ rectype = TRRTYPE_TXT;
+ } else if (mRecord->type == nsIDNSService::RESOLVE_TYPE_HTTPSSVC) {
+ rectype = TRRTYPE_HTTPSSVC;
+ } else if (pushedTRR) {
+ rectype = pushedTRR->Type();
+ } else {
+ MOZ_ASSERT(false, "Not an expected request type");
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ LOG(("TRR Resolve %s type %d\n", typeRec->host.get(), (int)rectype));
+ RefPtr<TRR> trr = pushedTRR ? pushedTRR : new TRR(this, mRecord, rectype);
+
+ if (pushedTRR || NS_SUCCEEDED(TRRService::Get()->DispatchTRRRequest(trr))) {
+ MutexAutoLock trrlock(mTrrLock);
+ MOZ_ASSERT(!mTrrByType);
+ mTrrByType = trr;
+ return NS_OK;
+ }
+
+ return NS_ERROR_UNKNOWN_HOST;
+}
+
+AHostResolver::LookupStatus TRRQuery::CompleteLookup(
+ nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb,
+ const nsACString& aOriginsuffix, nsHostRecord::TRRSkippedReason aReason,
+ TRR* aTRRRequest) {
+ if (rec != mRecord) {
+ LOG(("TRRQuery::CompleteLookup - Pushed record. Go to resolver"));
+ return mHostResolver->CompleteLookup(rec, status, aNewRRSet, pb,
+ aOriginsuffix, aReason, aTRRRequest);
+ }
+
+ LOG(("TRRQuery::CompleteLookup > host: %s", rec->host.get()));
+
+ RefPtr<AddrInfo> newRRSet(aNewRRSet);
+ DNSResolverType resolverType = newRRSet->ResolverType();
+ {
+ MutexAutoLock trrlock(mTrrLock);
+ if (newRRSet->TRRType() == TRRTYPE_A) {
+ MOZ_ASSERT(mTrrA);
+ mTRRAFailReason = aReason;
+ mTrrA = nullptr;
+ mTrrAUsed = NS_SUCCEEDED(status) ? OK : FAILED;
+ MOZ_ASSERT(!mAddrInfoA);
+ mAddrInfoA = newRRSet;
+ mAResult = status;
+ LOG(("A query status: 0x%x", static_cast<uint32_t>(status)));
+ } else if (newRRSet->TRRType() == TRRTYPE_AAAA) {
+ MOZ_ASSERT(mTrrAAAA);
+ mTRRAAAAFailReason = aReason;
+ mTrrAAAA = nullptr;
+ mTrrAAAAUsed = NS_SUCCEEDED(status) ? OK : FAILED;
+ MOZ_ASSERT(!mAddrInfoAAAA);
+ mAddrInfoAAAA = newRRSet;
+ mAAAAResult = status;
+ LOG(("AAAA query status: 0x%x", static_cast<uint32_t>(status)));
+ } else {
+ MOZ_ASSERT(0);
+ }
+ }
+
+ if (NS_SUCCEEDED(status)) {
+ mTRRSuccess++;
+ if (mTRRSuccess == 1) {
+ // Store the duration on first succesful TRR response. We
+ // don't know that there will be a second response nor can we
+ // tell which of two has useful data.
+ mTrrDuration = TimeStamp::Now() - mTrrStart;
+ }
+ }
+
+ bool pendingRequest = false;
+ if (mTRRRequestCounter) {
+ mTRRRequestCounter--;
+ pendingRequest = (mTRRRequestCounter != 0);
+ } else {
+ MOZ_DIAGNOSTIC_ASSERT(false, "Request counter is messed up");
+ }
+ if (pendingRequest) { // There are other outstanding requests
+ LOG(("CompleteLookup: waiting for all responses!\n"));
+ return LOOKUP_OK;
+ }
+
+ if (mRecord->af == AF_UNSPEC) {
+ // merge successful records
+ if (mTrrAUsed == OK) {
+ LOG(("Have A response"));
+ newRRSet = mAddrInfoA;
+ status = mAResult;
+ if (mTrrAAAAUsed == OK) {
+ LOG(("Merging A and AAAA responses"));
+ newRRSet = merge_rrset(newRRSet, mAddrInfoAAAA);
+ }
+ } else {
+ newRRSet = mAddrInfoAAAA;
+ status = mAAAAResult;
+ }
+
+ if (NS_FAILED(status) && (mAAAAResult == NS_ERROR_DEFINITIVE_UNKNOWN_HOST ||
+ mAResult == NS_ERROR_DEFINITIVE_UNKNOWN_HOST)) {
+ status = NS_ERROR_DEFINITIVE_UNKNOWN_HOST;
+ }
+ } else {
+ // If this is a failed AAAA request, but the server only has a A record,
+ // then we should not fallback to Do53. Instead we also send a A request
+ // and return NS_ERROR_DEFINITIVE_UNKNOWN_HOST if that succeeds.
+ if (NS_FAILED(status) && status != NS_ERROR_DEFINITIVE_UNKNOWN_HOST &&
+ (mTrrAUsed == INIT || mTrrAAAAUsed == INIT)) {
+ if (newRRSet->TRRType() == TRRTYPE_A) {
+ LOG(("A lookup failed. Checking if AAAA record exists"));
+ nsTArray<RefPtr<TRR>> requestsToSend;
+ PrepareQuery(TRRTYPE_AAAA, requestsToSend);
+ if (SendQueries(requestsToSend)) {
+ LOG(("Sent AAAA request"));
+ return LOOKUP_OK;
+ }
+ } else if (newRRSet->TRRType() == TRRTYPE_AAAA) {
+ LOG(("AAAA lookup failed. Checking if A record exists"));
+ nsTArray<RefPtr<TRR>> requestsToSend;
+ PrepareQuery(TRRTYPE_A, requestsToSend);
+ if (SendQueries(requestsToSend)) {
+ LOG(("Sent A request"));
+ return LOOKUP_OK;
+ }
+ } else {
+ MOZ_ASSERT(false, "Unexpected family");
+ }
+ }
+ bool otherSucceeded =
+ mRecord->af == AF_INET6 ? mTrrAUsed == OK : mTrrAAAAUsed == OK;
+ LOG(("TRRQuery::CompleteLookup other request succeeded"));
+
+ if (mRecord->af == AF_INET) {
+ // return only A record
+ newRRSet = mAddrInfoA;
+ status = mAResult;
+ if (NS_FAILED(status) &&
+ (otherSucceeded || mAAAAResult == NS_ERROR_DEFINITIVE_UNKNOWN_HOST)) {
+ LOG(("status set to NS_ERROR_DEFINITIVE_UNKNOWN_HOST"));
+ status = NS_ERROR_DEFINITIVE_UNKNOWN_HOST;
+ }
+
+ } else if (mRecord->af == AF_INET6) {
+ // return only AAAA record
+ newRRSet = mAddrInfoAAAA;
+ status = mAAAAResult;
+
+ if (NS_FAILED(status) &&
+ (otherSucceeded || mAResult == NS_ERROR_DEFINITIVE_UNKNOWN_HOST)) {
+ LOG(("status set to NS_ERROR_DEFINITIVE_UNKNOWN_HOST"));
+ status = NS_ERROR_DEFINITIVE_UNKNOWN_HOST;
+ }
+
+ } else {
+ MOZ_ASSERT(false, "Unexpected AF");
+ return LOOKUP_OK;
+ }
+
+ // If this record failed, but there is a record for the other AF
+ // we prevent fallback to the native resolver.
+ }
+
+ if (mTRRSuccess && mHostResolver->GetNCS() &&
+ (mHostResolver->GetNCS()->GetNAT64() ==
+ nsINetworkConnectivityService::OK) &&
+ newRRSet) {
+ newRRSet = mHostResolver->GetNCS()->MapNAT64IPs(newRRSet);
+ }
+
+ if (resolverType == DNSResolverType::TRR) {
+ if (mTrrAUsed == OK) {
+ AccumulateCategoricalKeyed(
+ TRRService::ProviderKey(),
+ Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::trrAOK);
+ } else if (mTrrAUsed == FAILED) {
+ AccumulateCategoricalKeyed(
+ TRRService::ProviderKey(),
+ Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::trrAFail);
+ }
+
+ if (mTrrAAAAUsed == OK) {
+ AccumulateCategoricalKeyed(
+ TRRService::ProviderKey(),
+ Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::trrAAAAOK);
+ } else if (mTrrAAAAUsed == FAILED) {
+ AccumulateCategoricalKeyed(
+ TRRService::ProviderKey(),
+ Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::trrAAAAFail);
+ }
+ }
+
+ mAddrInfoAAAA = nullptr;
+ mAddrInfoA = nullptr;
+
+ MOZ_DIAGNOSTIC_ASSERT(!mCalledCompleteLookup,
+ "must not call CompleteLookup more than once");
+ mCalledCompleteLookup = true;
+ return mHostResolver->CompleteLookup(rec, status, newRRSet, pb, aOriginsuffix,
+ aReason, aTRRRequest);
+}
+
+AHostResolver::LookupStatus TRRQuery::CompleteLookupByType(
+ nsHostRecord* rec, nsresult status,
+ mozilla::net::TypeRecordResultType& aResult,
+ mozilla::net::TRRSkippedReason aReason, uint32_t aTtl, bool pb) {
+ if (rec != mRecord) {
+ LOG(("TRRQuery::CompleteLookup - Pushed record. Go to resolver"));
+ return mHostResolver->CompleteLookupByType(rec, status, aResult, aReason,
+ aTtl, pb);
+ }
+
+ {
+ MutexAutoLock trrlock(mTrrLock);
+ mTrrByType = nullptr;
+ }
+
+ // Unlike the address record, we store the duration regardless of the status.
+ mTrrDuration = TimeStamp::Now() - mTrrStart;
+
+ MOZ_DIAGNOSTIC_ASSERT(!mCalledCompleteLookup,
+ "must not call CompleteLookup more than once");
+ mCalledCompleteLookup = true;
+ return mHostResolver->CompleteLookupByType(rec, status, aResult, aReason,
+ aTtl, pb);
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/TRRQuery.h b/netwerk/dns/TRRQuery.h
new file mode 100644
index 0000000000..211aacbe74
--- /dev/null
+++ b/netwerk/dns/TRRQuery.h
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#ifndef mozilla_net_TRRQuery_h
+#define mozilla_net_TRRQuery_h
+
+#include "nsHostResolver.h"
+#include "DNSPacket.h"
+
+namespace mozilla {
+namespace net {
+
+class TRRQuery : public AHostResolver {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TRRQuery, override)
+
+ public:
+ TRRQuery(nsHostResolver* aHostResolver, nsHostRecord* aHostRecord)
+ : mHostResolver(aHostResolver),
+ mRecord(aHostRecord),
+ mTrrLock("TRRQuery.mTrrLock") {}
+
+ nsresult DispatchLookup(TRR* pushedTRR = nullptr);
+
+ void Cancel(nsresult aStatus);
+
+ enum TRRState { INIT, STARTED, OK, FAILED };
+ TRRState mTrrAUsed = INIT;
+ TRRState mTrrAAAAUsed = INIT;
+
+ TRRSkippedReason mTRRAFailReason = TRRSkippedReason::TRR_UNSET;
+ TRRSkippedReason mTRRAAAAFailReason = TRRSkippedReason::TRR_UNSET;
+
+ virtual LookupStatus CompleteLookup(nsHostRecord*, nsresult,
+ mozilla::net::AddrInfo*, bool pb,
+ const nsACString& aOriginsuffix,
+ nsHostRecord::TRRSkippedReason aReason,
+ TRR* aTRRRequest) override;
+ virtual LookupStatus CompleteLookupByType(
+ nsHostRecord*, nsresult, mozilla::net::TypeRecordResultType& aResult,
+ mozilla::net::TRRSkippedReason aReason, uint32_t aTtl, bool pb) override;
+ virtual nsresult GetHostRecord(const nsACString& host,
+ const nsACString& aTrrServer, uint16_t type,
+ nsIDNSService::DNSFlags flags, uint16_t af,
+ bool pb, const nsCString& originSuffix,
+ nsHostRecord** result) override {
+ if (!mHostResolver) {
+ return NS_ERROR_FAILURE;
+ }
+ return mHostResolver->GetHostRecord(host, aTrrServer, type, flags, af, pb,
+ originSuffix, result);
+ }
+ virtual nsresult TrrLookup_unlocked(
+ nsHostRecord* rec, mozilla::net::TRR* pushedTRR = nullptr) override {
+ if (!mHostResolver) {
+ return NS_ERROR_FAILURE;
+ }
+ return mHostResolver->TrrLookup_unlocked(rec, pushedTRR);
+ }
+ virtual void MaybeRenewHostRecord(nsHostRecord* aRec) override {
+ if (!mHostResolver) {
+ return;
+ }
+ mHostResolver->MaybeRenewHostRecord(aRec);
+ }
+
+ mozilla::TimeDuration Duration() { return mTrrDuration; }
+
+ private:
+ nsresult DispatchByTypeLookup(TRR* pushedTRR = nullptr);
+
+ private:
+ ~TRRQuery() = default;
+
+ void MarkSendingTRR(TRR* trr, TrrType rectype, MutexAutoLock&);
+ void PrepareQuery(TrrType aRecType, nsTArray<RefPtr<TRR>>& aRequestsToSend);
+ bool SendQueries(nsTArray<RefPtr<TRR>>& aRequestsToSend);
+
+ RefPtr<nsHostResolver> mHostResolver;
+ RefPtr<nsHostRecord> mRecord;
+
+ Mutex mTrrLock
+ MOZ_UNANNOTATED; // lock when accessing the mTrrA[AAA] pointers
+ RefPtr<mozilla::net::TRR> mTrrA;
+ RefPtr<mozilla::net::TRR> mTrrAAAA;
+ RefPtr<mozilla::net::TRR> mTrrByType;
+ // |mTRRRequestCounter| indicates the number of TRR requests that were
+ // dispatched sucessfully. Generally, this counter is increased to 2 after
+ // mTrrA and mTrrAAAA are dispatched, and is decreased by 1 when
+ // CompleteLookup is called. Note that nsHostResolver::CompleteLookup is only
+ // called when this counter equals to 0.
+ Atomic<uint32_t> mTRRRequestCounter{0};
+
+ uint8_t mTRRSuccess = 0; // number of successful TRR responses
+ bool mCalledCompleteLookup = false;
+
+ mozilla::TimeDuration mTrrDuration;
+ mozilla::TimeStamp mTrrStart;
+
+ RefPtr<mozilla::net::AddrInfo> mAddrInfoA;
+ RefPtr<mozilla::net::AddrInfo> mAddrInfoAAAA;
+ nsresult mAResult = NS_OK;
+ nsresult mAAAAResult = NS_OK;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_TRRQuery_h
diff --git a/netwerk/dns/TRRService.cpp b/netwerk/dns/TRRService.cpp
new file mode 100644
index 0000000000..fbaa67ee14
--- /dev/null
+++ b/netwerk/dns/TRRService.cpp
@@ -0,0 +1,1411 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsCharSeparatedTokenizer.h"
+#include "nsComponentManagerUtils.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsHttpConnectionInfo.h"
+#include "nsICaptivePortalService.h"
+#include "nsIFile.h"
+#include "nsIParentalControlsService.h"
+#include "nsINetworkLinkService.h"
+#include "nsIObserverService.h"
+#include "nsIOService.h"
+#include "nsNetUtil.h"
+#include "nsStandardURL.h"
+#include "TRR.h"
+#include "TRRService.h"
+
+#include "mozilla/Preferences.h"
+#include "mozilla/StaticPrefs_network.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/TelemetryComms.h"
+#include "mozilla/Tokenizer.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/net/NeckoParent.h"
+#include "mozilla/net/TRRServiceChild.h"
+// Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
+#include "DNSLogging.h"
+
+static const char kOpenCaptivePortalLoginEvent[] = "captive-portal-login";
+static const char kClearPrivateData[] = "clear-private-data";
+static const char kPurge[] = "browser:purge-session-history";
+
+#define TRR_PREF_PREFIX "network.trr."
+#define TRR_PREF(x) TRR_PREF_PREFIX x
+
+namespace mozilla::net {
+
+StaticRefPtr<nsIThread> sTRRBackgroundThread;
+static Atomic<TRRService*> sTRRServicePtr;
+
+static Atomic<size_t, Relaxed> sDomainIndex(0);
+static Atomic<size_t, Relaxed> sCurrentTRRModeIndex(0);
+
+constexpr nsLiteralCString kTRRDomains[3][7] = {
+ // clang-format off
+ {
+ // When mode is 0, the provider key has no postfix.
+ "(other)"_ns,
+ "mozilla.cloudflare-dns.com"_ns,
+ "firefox.dns.nextdns.io"_ns,
+ "private.canadianshield.cira.ca"_ns,
+ "doh.xfinity.com"_ns, // Steered clients
+ "dns.shaw.ca"_ns, // Steered clients
+ "dooh.cloudflare-dns.com"_ns, // DNS over Oblivious HTTP
+ },
+ {
+ "(other)_2"_ns,
+ "mozilla.cloudflare-dns.com_2"_ns,
+ "firefox.dns.nextdns.io_2"_ns,
+ "private.canadianshield.cira.ca_2"_ns,
+ "doh.xfinity.com_2"_ns, // Steered clients
+ "dns.shaw.ca_2"_ns, // Steered clients
+ "dooh.cloudflare-dns.com_2"_ns, // DNS over Oblivious HTTP
+ },
+ {
+ "(other)_3"_ns,
+ "mozilla.cloudflare-dns.com_3"_ns,
+ "firefox.dns.nextdns.io_3"_ns,
+ "private.canadianshield.cira.ca_3"_ns,
+ "doh.xfinity.com_3"_ns, // Steered clients
+ "dns.shaw.ca_3"_ns, // Steered clients
+ "dooh.cloudflare-dns.com_3"_ns, // DNS over Oblivious HTTP
+ },
+ // clang-format on
+};
+
+// static
+void TRRService::SetCurrentTRRMode(nsIDNSService::ResolverMode aMode) {
+ // A table to map ResolverMode to the row of kTRRDomains.
+ // When the aMode is 2, we use kTRRDomains[1] as provider keys. When aMode is
+ // 3, we use kTRRDomains[2]. Otherwise, we kTRRDomains[0] is used.
+ static const uint32_t index[] = {0, 0, 1, 2, 0, 0};
+ if (aMode > nsIDNSService::MODE_TRROFF) {
+ aMode = nsIDNSService::MODE_TRROFF;
+ }
+ sCurrentTRRModeIndex = index[static_cast<size_t>(aMode)];
+}
+
+// static
+void TRRService::SetProviderDomain(const nsACString& aTRRDomain) {
+ sDomainIndex = 0;
+ for (size_t i = 1; i < std::size(kTRRDomains[0]); i++) {
+ if (aTRRDomain.Equals(kTRRDomains[0][i])) {
+ sDomainIndex = i;
+ break;
+ }
+ }
+}
+
+// static
+const nsCString& TRRService::ProviderKey() {
+ return kTRRDomains[sCurrentTRRModeIndex][sDomainIndex];
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(TRRService, TRRServiceBase, nsIObserver,
+ nsISupportsWeakReference)
+
+NS_IMPL_ADDREF_USING_AGGREGATOR(TRRService::ConfirmationContext, OwningObject())
+NS_IMPL_RELEASE_USING_AGGREGATOR(TRRService::ConfirmationContext,
+ OwningObject())
+NS_IMPL_QUERY_INTERFACE(TRRService::ConfirmationContext, nsITimerCallback,
+ nsINamed)
+
+TRRService::TRRService() : mLock("TRRService", this) {
+ MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
+}
+
+// static
+TRRService* TRRService::Get() { return sTRRServicePtr; }
+
+// static
+void TRRService::AddObserver(nsIObserver* aObserver,
+ nsIObserverService* aObserverService) {
+ nsCOMPtr<nsIObserverService> observerService;
+ if (aObserverService) {
+ observerService = aObserverService;
+ } else {
+ observerService = mozilla::services::GetObserverService();
+ }
+
+ if (observerService) {
+ observerService->AddObserver(aObserver, NS_CAPTIVE_PORTAL_CONNECTIVITY,
+ true);
+ observerService->AddObserver(aObserver, kOpenCaptivePortalLoginEvent, true);
+ observerService->AddObserver(aObserver, kClearPrivateData, true);
+ observerService->AddObserver(aObserver, kPurge, true);
+ observerService->AddObserver(aObserver, NS_NETWORK_LINK_TOPIC, true);
+ observerService->AddObserver(aObserver, NS_DNS_SUFFIX_LIST_UPDATED_TOPIC,
+ true);
+ observerService->AddObserver(aObserver, "xpcom-shutdown-threads", true);
+ }
+}
+
+// static
+bool TRRService::CheckCaptivePortalIsPassed() {
+ bool result = false;
+ nsCOMPtr<nsICaptivePortalService> captivePortalService =
+ do_GetService(NS_CAPTIVEPORTAL_CID);
+ if (captivePortalService) {
+ int32_t captiveState;
+ MOZ_ALWAYS_SUCCEEDS(captivePortalService->GetState(&captiveState));
+
+ if ((captiveState == nsICaptivePortalService::UNLOCKED_PORTAL) ||
+ (captiveState == nsICaptivePortalService::NOT_CAPTIVE)) {
+ result = true;
+ }
+ LOG(("TRRService::Init mCaptiveState=%d mCaptiveIsPassed=%d\n",
+ captiveState, (int)result));
+ }
+
+ return result;
+}
+
+static void EventTelemetryPrefChanged(const char* aPref, void* aData) {
+ Telemetry::SetEventRecordingEnabled(
+ "network.dns"_ns,
+ StaticPrefs::network_trr_confirmation_telemetry_enabled());
+}
+
+nsresult TRRService::Init() {
+ MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
+ if (mInitialized) {
+ return NS_OK;
+ }
+ mInitialized = true;
+
+ AddObserver(this);
+
+ nsCOMPtr<nsIPrefBranch> prefBranch;
+ GetPrefBranch(getter_AddRefs(prefBranch));
+ if (prefBranch) {
+ prefBranch->AddObserver(TRR_PREF_PREFIX, this, true);
+ prefBranch->AddObserver(kRolloutURIPref, this, true);
+ prefBranch->AddObserver(kRolloutModePref, this, true);
+ }
+
+ sTRRServicePtr = this;
+
+ ReadPrefs(nullptr);
+ mConfirmation.HandleEvent(ConfirmationEvent::Init);
+
+ if (XRE_IsParentProcess()) {
+ mCaptiveIsPassed = CheckCaptivePortalIsPassed();
+
+ mParentalControlEnabled = GetParentalControlEnabledInternal();
+
+ mLinkService = do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID);
+ if (mLinkService) {
+ nsTArray<nsCString> suffixList;
+ mLinkService->GetDnsSuffixList(suffixList);
+ RebuildSuffixList(std::move(suffixList));
+ }
+
+ nsCOMPtr<nsIThread> thread;
+ if (NS_FAILED(
+ NS_NewNamedThread("TRR Background", getter_AddRefs(thread)))) {
+ NS_WARNING("NS_NewNamedThread failed!");
+ return NS_ERROR_FAILURE;
+ }
+
+ sTRRBackgroundThread = thread;
+ }
+
+ Preferences::RegisterCallbackAndCall(
+ EventTelemetryPrefChanged,
+ "network.trr.confirmation_telemetry_enabled"_ns);
+
+ LOG(("Initialized TRRService\n"));
+ return NS_OK;
+}
+
+// static
+bool TRRService::GetParentalControlEnabledInternal() {
+ nsCOMPtr<nsIParentalControlsService> pc =
+ do_CreateInstance("@mozilla.org/parental-controls-service;1");
+ if (pc) {
+ bool result = false;
+ pc->GetParentalControlsEnabled(&result);
+ LOG(("TRRService::GetParentalControlEnabledInternal=%d\n", result));
+ return result;
+ }
+
+ return false;
+}
+
+void TRRService::SetDetectedTrrURI(const nsACString& aURI) {
+ LOG(("SetDetectedTrrURI(%s", nsPromiseFlatCString(aURI).get()));
+ // If the user has set a custom URI then we don't want to override that.
+ // If the URI is set via doh-rollout.uri, mURIPref will be empty
+ // (see TRRServiceBase::OnTRRURIChange)
+ if (!mURIPref.IsEmpty()) {
+ LOG(("Already has user value. Not setting URI"));
+ return;
+ }
+
+ if (StaticPrefs::network_trr_use_ohttp()) {
+ LOG(("No autodetection when using OHTTP"));
+ return;
+ }
+
+ mURISetByDetection = MaybeSetPrivateURI(aURI);
+}
+
+bool TRRService::Enabled(nsIRequest::TRRMode aRequestMode) {
+ if (mMode == nsIDNSService::MODE_TRROFF ||
+ aRequestMode == nsIRequest::TRR_DISABLED_MODE) {
+ LOG(("TRR service not enabled - off or disabled"));
+ return false;
+ }
+
+ // If already confirmed, service is enabled.
+ if (mConfirmation.State() == CONFIRM_OK ||
+ aRequestMode == nsIRequest::TRR_ONLY_MODE) {
+ LOG(("TRR service enabled - confirmed or trr_only request"));
+ return true;
+ }
+
+ // If this is a TRR_FIRST request but the resolver has a different mode,
+ // just go ahead and let it try to use TRR.
+ if (aRequestMode == nsIRequest::TRR_FIRST_MODE &&
+ mMode != nsIDNSService::MODE_TRRFIRST) {
+ LOG(("TRR service enabled - trr_first request"));
+ return true;
+ }
+
+ // In TRR_ONLY_MODE / confirmationNS == "skip" we don't try to confirm.
+ if (mConfirmation.State() == CONFIRM_DISABLED) {
+ LOG(("TRRService service enabled - confirmation is disabled"));
+ return true;
+ }
+
+ LOG(("TRRService::Enabled mConfirmation.mState=%d mCaptiveIsPassed=%d\n",
+ mConfirmation.State(), (int)mCaptiveIsPassed));
+
+ if (StaticPrefs::network_trr_wait_for_confirmation()) {
+ return mConfirmation.State() == CONFIRM_OK;
+ }
+
+ if (StaticPrefs::network_trr_attempt_when_retrying_confirmation()) {
+ return mConfirmation.State() == CONFIRM_OK ||
+ mConfirmation.State() == CONFIRM_TRYING_OK ||
+ mConfirmation.State() == CONFIRM_TRYING_FAILED;
+ }
+
+ return mConfirmation.State() == CONFIRM_OK ||
+ mConfirmation.State() == CONFIRM_TRYING_OK;
+}
+
+void TRRService::GetPrefBranch(nsIPrefBranch** result) {
+ MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
+ *result = nullptr;
+ CallGetService(NS_PREFSERVICE_CONTRACTID, result);
+}
+
+bool TRRService::MaybeSetPrivateURI(const nsACString& aURI) {
+ bool clearCache = false;
+ nsAutoCString newURI(aURI);
+ LOG(("MaybeSetPrivateURI(%s)", newURI.get()));
+
+ ProcessURITemplate(newURI);
+ {
+ MutexSingleWriterAutoLock lock(mLock);
+ if (mPrivateURI.Equals(newURI)) {
+ return false;
+ }
+
+ if (!mPrivateURI.IsEmpty()) {
+ LOG(("TRRService clearing blocklist because of change in uri service\n"));
+ auto bl = mTRRBLStorage.Lock();
+ bl->Clear();
+ clearCache = true;
+ }
+
+ nsAutoCString host;
+
+ nsCOMPtr<nsIURI> url;
+ if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(url), newURI))) {
+ url->GetHost(host);
+ }
+
+ SetProviderDomain(host);
+
+ mPrivateURI = newURI;
+
+ // Notify the content processes of the new TRR
+ for (auto* cp :
+ dom::ContentParent::AllProcesses(dom::ContentParent::eLive)) {
+ PNeckoParent* neckoParent =
+ SingleManagedOrNull(cp->ManagedPNeckoParent());
+ if (!neckoParent) {
+ continue;
+ }
+ Unused << neckoParent->SendSetTRRDomain(host);
+ }
+
+ AsyncCreateTRRConnectionInfo(mPrivateURI);
+
+ // The URI has changed. We should trigger a new confirmation immediately.
+ // We must do this here because the URI could also change because of
+ // steering.
+ mConfirmationTriggered =
+ mConfirmation.HandleEvent(ConfirmationEvent::URIChange, lock);
+ }
+
+ // Clear the cache because we changed the URI
+ if (clearCache) {
+ ClearEntireCache();
+ }
+
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->NotifyObservers(nullptr, NS_NETWORK_TRR_URI_CHANGED_TOPIC, nullptr);
+ }
+ return true;
+}
+
+nsresult TRRService::ReadPrefs(const char* name) {
+ MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
+
+ // Whenever a pref change occurs that would cause us to clear the cache
+ // we set this to true then do it at the end of the method.
+ bool clearEntireCache = false;
+
+ if (!name || !strcmp(name, TRR_PREF("mode")) ||
+ !strcmp(name, kRolloutModePref)) {
+ nsIDNSService::ResolverMode prevMode = Mode();
+
+ OnTRRModeChange();
+ // When the TRR service gets disabled we should purge the TRR cache to
+ // make sure we don't use any of the cached entries on a network where
+ // they are invalid - for example after turning on a VPN.
+ if (TRR_DISABLED(Mode()) && !TRR_DISABLED(prevMode)) {
+ clearEntireCache = true;
+ }
+ }
+ if (!name || !strcmp(name, TRR_PREF("uri")) ||
+ !strcmp(name, TRR_PREF("default_provider_uri")) ||
+ !strcmp(name, kRolloutURIPref) || !strcmp(name, TRR_PREF("ohttp.uri")) ||
+ !strcmp(name, TRR_PREF("use_ohttp"))) {
+ OnTRRURIChange();
+ }
+ if (!name || !strcmp(name, TRR_PREF("credentials"))) {
+ MutexSingleWriterAutoLock lock(mLock);
+ Preferences::GetCString(TRR_PREF("credentials"), mPrivateCred);
+ }
+ if (!name || !strcmp(name, TRR_PREF("confirmationNS"))) {
+ MutexSingleWriterAutoLock lock(mLock);
+ Preferences::GetCString(TRR_PREF("confirmationNS"), mConfirmationNS);
+ LOG(("confirmationNS = %s", mConfirmationNS.get()));
+ }
+ if (!name || !strcmp(name, TRR_PREF("bootstrapAddr"))) {
+ MutexSingleWriterAutoLock lock(mLock);
+ Preferences::GetCString(TRR_PREF("bootstrapAddr"), mBootstrapAddr);
+ clearEntireCache = true;
+ }
+ if (!name || !strcmp(name, TRR_PREF("excluded-domains")) ||
+ !strcmp(name, TRR_PREF("builtin-excluded-domains"))) {
+ MutexSingleWriterAutoLock lock(mLock);
+ mExcludedDomains.Clear();
+
+ auto parseExcludedDomains = [this](const char* aPrefName) {
+ nsAutoCString excludedDomains;
+ mLock.AssertCurrentThreadOwns();
+ Preferences::GetCString(aPrefName, excludedDomains);
+ if (excludedDomains.IsEmpty()) {
+ return;
+ }
+
+ for (const nsACString& tokenSubstring :
+ nsCCharSeparatedTokenizerTemplate<
+ NS_IsAsciiWhitespace, nsTokenizerFlags::SeparatorOptional>(
+ excludedDomains, ',')
+ .ToRange()) {
+ nsCString token{tokenSubstring};
+ LOG(("TRRService::ReadPrefs %s host:[%s]\n", aPrefName, token.get()));
+ mExcludedDomains.Insert(token);
+ }
+ };
+
+ parseExcludedDomains(TRR_PREF("excluded-domains"));
+ parseExcludedDomains(TRR_PREF("builtin-excluded-domains"));
+ clearEntireCache = true;
+ }
+
+ // if name is null, then we're just now initializing. In that case we don't
+ // need to clear the cache.
+ if (name && clearEntireCache) {
+ ClearEntireCache();
+ }
+
+ return NS_OK;
+}
+
+void TRRService::ClearEntireCache() {
+ if (!StaticPrefs::network_trr_clear_cache_on_pref_change()) {
+ return;
+ }
+ nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
+ if (!dns) {
+ return;
+ }
+ dns->ClearCache(true);
+}
+
+void TRRService::AddEtcHosts(const nsTArray<nsCString>& aArray) {
+ MutexSingleWriterAutoLock lock(mLock);
+ for (const auto& item : aArray) {
+ LOG(("Adding %s from /etc/hosts to excluded domains", item.get()));
+ mEtcHostsDomains.Insert(item);
+ }
+}
+
+void TRRService::ReadEtcHostsFile() {
+ if (!XRE_IsParentProcess()) {
+ return;
+ }
+
+ DoReadEtcHostsFile([](const nsTArray<nsCString>* aArray) -> bool {
+ RefPtr<TRRService> service(sTRRServicePtr);
+ if (service && aArray) {
+ service->AddEtcHosts(*aArray);
+ }
+ return !!service;
+ });
+}
+
+void TRRService::GetURI(nsACString& result) {
+ MutexSingleWriterAutoLock lock(mLock);
+ result = mPrivateURI;
+}
+
+nsresult TRRService::GetCredentials(nsCString& result) {
+ MutexSingleWriterAutoLock lock(mLock);
+ result = mPrivateCred;
+ return NS_OK;
+}
+
+uint32_t TRRService::GetRequestTimeout() {
+ if (mMode == nsIDNSService::MODE_TRRONLY) {
+ return StaticPrefs::network_trr_request_timeout_mode_trronly_ms();
+ }
+
+ if (StaticPrefs::network_trr_strict_native_fallback()) {
+ return StaticPrefs::network_trr_strict_fallback_request_timeout_ms();
+ }
+
+ return StaticPrefs::network_trr_request_timeout_ms();
+}
+
+nsresult TRRService::Start() {
+ MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
+ if (!mInitialized) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return NS_OK;
+}
+
+TRRService::~TRRService() {
+ MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
+ LOG(("Exiting TRRService\n"));
+}
+
+nsresult TRRService::DispatchTRRRequest(TRR* aTrrRequest) {
+ return DispatchTRRRequestInternal(aTrrRequest, true);
+}
+
+nsresult TRRService::DispatchTRRRequestInternal(TRR* aTrrRequest,
+ bool aWithLock) {
+ NS_ENSURE_ARG_POINTER(aTrrRequest);
+
+ nsCOMPtr<nsIThread> thread = MainThreadOrTRRThread(aWithLock);
+ if (!thread) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<TRR> trr = aTrrRequest;
+ return thread->Dispatch(trr.forget());
+}
+
+already_AddRefed<nsIThread> TRRService::MainThreadOrTRRThread(bool aWithLock) {
+ if (!StaticPrefs::network_trr_fetch_off_main_thread() ||
+ XRE_IsSocketProcess() || mDontUseTRRThread) {
+ return do_GetMainThread();
+ }
+
+ nsCOMPtr<nsIThread> thread = aWithLock ? TRRThread() : TRRThread_locked();
+ return thread.forget();
+}
+
+already_AddRefed<nsIThread> TRRService::TRRThread() {
+ MutexSingleWriterAutoLock lock(mLock);
+ return TRRThread_locked();
+}
+
+already_AddRefed<nsIThread> TRRService::TRRThread_locked() {
+ RefPtr<nsIThread> thread = sTRRBackgroundThread;
+ return thread.forget();
+}
+
+bool TRRService::IsOnTRRThread() {
+ nsCOMPtr<nsIThread> thread;
+ {
+ MutexSingleWriterAutoLock lock(mLock);
+ thread = sTRRBackgroundThread;
+ }
+ if (!thread) {
+ return false;
+ }
+
+ return thread->IsOnCurrentThread();
+}
+
+NS_IMETHODIMP
+TRRService::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) {
+ MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
+ LOG(("TRR::Observe() topic=%s\n", aTopic));
+ if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
+ // Reset the state of whether a confirmation is triggered, so we can check
+ // if we create a new one after ReadPrefs().
+ mConfirmationTriggered = false;
+ ReadPrefs(NS_ConvertUTF16toUTF8(aData).get());
+ {
+ MutexSingleWriterAutoLock lock(mLock);
+ mConfirmation.RecordEvent("pref-change", lock);
+ }
+
+ // We should only trigger a new confirmation if reading the prefs didn't
+ // already trigger one.
+ if (!mConfirmationTriggered) {
+ mConfirmation.HandleEvent(ConfirmationEvent::PrefChange);
+ }
+ } else if (!strcmp(aTopic, kOpenCaptivePortalLoginEvent)) {
+ // We are in a captive portal
+ LOG(("TRRservice in captive portal\n"));
+ mCaptiveIsPassed = false;
+ mConfirmation.SetCaptivePortalStatus(
+ nsICaptivePortalService::LOCKED_PORTAL);
+ } else if (!strcmp(aTopic, NS_CAPTIVE_PORTAL_CONNECTIVITY)) {
+ nsAutoCString data = NS_ConvertUTF16toUTF8(aData);
+ LOG(("TRRservice captive portal was %s\n", data.get()));
+ nsCOMPtr<nsICaptivePortalService> cps = do_QueryInterface(aSubject);
+ if (cps) {
+ mConfirmation.SetCaptivePortalStatus(cps->State());
+ }
+
+ // If we were previously in a captive portal, this event means we will
+ // need to trigger confirmation again. Otherwise it's just a periodical
+ // captive-portal check that completed and we don't need to react to it.
+ if (!mCaptiveIsPassed) {
+ mConfirmation.HandleEvent(ConfirmationEvent::CaptivePortalConnectivity);
+ }
+
+ mCaptiveIsPassed = true;
+ } else if (!strcmp(aTopic, kClearPrivateData) || !strcmp(aTopic, kPurge)) {
+ // flush the TRR blocklist
+ auto bl = mTRRBLStorage.Lock();
+ bl->Clear();
+ } else if (!strcmp(aTopic, NS_DNS_SUFFIX_LIST_UPDATED_TOPIC) ||
+ !strcmp(aTopic, NS_NETWORK_LINK_TOPIC)) {
+ // nsINetworkLinkService is only available on parent process.
+ if (XRE_IsParentProcess()) {
+ nsCOMPtr<nsINetworkLinkService> link = do_QueryInterface(aSubject);
+ // The network link service notification normally passes itself as the
+ // subject, but some unit tests will sometimes pass a null subject.
+ if (link) {
+ nsTArray<nsCString> suffixList;
+ link->GetDnsSuffixList(suffixList);
+ RebuildSuffixList(std::move(suffixList));
+ }
+ }
+
+ if (!strcmp(aTopic, NS_NETWORK_LINK_TOPIC)) {
+ if (NS_ConvertUTF16toUTF8(aData).EqualsLiteral(
+ NS_NETWORK_LINK_DATA_DOWN)) {
+ MutexSingleWriterAutoLock lock(mLock);
+ mConfirmation.RecordEvent("network-change", lock);
+ }
+
+ if (mURISetByDetection) {
+ // If the URI was set via SetDetectedTrrURI we need to restore it to the
+ // default pref when a network link change occurs.
+ CheckURIPrefs();
+ }
+
+ if (NS_ConvertUTF16toUTF8(aData).EqualsLiteral(NS_NETWORK_LINK_DATA_UP)) {
+ mConfirmation.HandleEvent(ConfirmationEvent::NetworkUp);
+ }
+ }
+ } else if (!strcmp(aTopic, "xpcom-shutdown-threads")) {
+ mShutdown = true;
+ // If a confirmation is still in progress we record the event.
+ // Since there should be no more confirmations after this, the shutdown
+ // reason would not really be recorded in telemetry.
+ {
+ MutexSingleWriterAutoLock lock(mLock);
+ mConfirmation.RecordEvent("shutdown", lock);
+ }
+
+ if (sTRRBackgroundThread) {
+ nsCOMPtr<nsIThread> thread;
+ thread = sTRRBackgroundThread.get();
+ sTRRBackgroundThread = nullptr;
+ MOZ_ALWAYS_SUCCEEDS(thread->Shutdown());
+ sTRRServicePtr = nullptr;
+ }
+ }
+ return NS_OK;
+}
+
+void TRRService::RebuildSuffixList(nsTArray<nsCString>&& aSuffixList) {
+ if (!StaticPrefs::network_trr_split_horizon_mitigations() || mShutdown) {
+ return;
+ }
+
+ MutexSingleWriterAutoLock lock(mLock);
+ mDNSSuffixDomains.Clear();
+ for (const auto& item : aSuffixList) {
+ LOG(("TRRService adding %s to suffix list", item.get()));
+ mDNSSuffixDomains.Insert(item);
+ }
+}
+
+void TRRService::ConfirmationContext::SetState(
+ enum ConfirmationState aNewState) {
+ mState = aNewState;
+
+ enum ConfirmationState state = mState;
+ if (XRE_IsParentProcess()) {
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "TRRService::ConfirmationContextNotify", [state] {
+ if (nsCOMPtr<nsIObserverService> obs =
+ mozilla::services::GetObserverService()) {
+ auto stateString =
+ [](enum ConfirmationState aState) -> const char16_t* {
+ switch (aState) {
+ case CONFIRM_OFF:
+ return u"CONFIRM_OFF";
+ case CONFIRM_TRYING_OK:
+ return u"CONFIRM_TRYING_OK";
+ case CONFIRM_OK:
+ return u"CONFIRM_OK";
+ case CONFIRM_FAILED:
+ return u"CONFIRM_FAILED";
+ case CONFIRM_TRYING_FAILED:
+ return u"CONFIRM_TRYING_FAILED";
+ case CONFIRM_DISABLED:
+ return u"CONFIRM_DISABLED";
+ }
+ MOZ_ASSERT_UNREACHABLE();
+ return u"";
+ };
+
+ obs->NotifyObservers(nullptr, "network:trr-confirmation",
+ stateString(state));
+ }
+ }));
+ }
+
+ if (XRE_IsParentProcess()) {
+ return;
+ }
+
+ MOZ_ASSERT(XRE_IsSocketProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+
+ TRRServiceChild* child = TRRServiceChild::GetSingleton();
+ if (child && child->CanSend()) {
+ LOG(("TRRService::SendSetConfirmationState"));
+ Unused << child->SendSetConfirmationState(mState);
+ }
+}
+
+bool TRRService::ConfirmationContext::HandleEvent(ConfirmationEvent aEvent) {
+ MutexSingleWriterAutoLock lock(OwningObject()->mLock);
+ return HandleEvent(aEvent, lock);
+}
+
+// We're protected by service->mLock
+bool TRRService::ConfirmationContext::HandleEvent(
+ ConfirmationEvent aEvent, const MutexSingleWriterAutoLock&) {
+ auto prevAddr = TaskAddr();
+ TRRService* service = OwningObject();
+ service->mLock.AssertCurrentThreadOwns();
+ nsIDNSService::ResolverMode mode = service->Mode();
+
+ auto resetConfirmation = [&]() {
+ service->mLock.AssertCurrentThreadOwns();
+ mTask = nullptr;
+ nsCOMPtr<nsITimer> timer = std::move(mTimer);
+ if (timer) {
+ timer->Cancel();
+ }
+
+ mRetryInterval = StaticPrefs::network_trr_retry_timeout_ms();
+ mTRRFailures = 0;
+
+ if (TRR_DISABLED(mode)) {
+ LOG(("TRR is disabled. mConfirmation.mState -> CONFIRM_OFF"));
+ SetState(CONFIRM_OFF);
+ return;
+ }
+
+ if (mode == nsIDNSService::MODE_TRRONLY) {
+ LOG(("TRR_ONLY_MODE. mConfirmation.mState -> CONFIRM_DISABLED"));
+ SetState(CONFIRM_DISABLED);
+ return;
+ }
+
+ if (service->mConfirmationNS.Equals("skip"_ns)) {
+ LOG((
+ "mConfirmationNS == skip. mConfirmation.mState -> CONFIRM_DISABLED"));
+ SetState(CONFIRM_DISABLED);
+ return;
+ }
+
+ // The next call to maybeConfirm will transition to CONFIRM_TRYING_OK
+ LOG(("mConfirmation.mState -> CONFIRM_OK"));
+ SetState(CONFIRM_OK);
+ };
+
+ auto maybeConfirm = [&](const char* aReason) {
+ service->mLock.AssertCurrentThreadOwns();
+ if (TRR_DISABLED(mode) || mState == CONFIRM_DISABLED || mTask) {
+ LOG(
+ ("TRRService:MaybeConfirm(%s) mode=%d, mTask=%p "
+ "mState=%d\n",
+ aReason, (int)mode, (void*)mTask, (int)mState));
+ return;
+ }
+
+ MOZ_ASSERT(mode != nsIDNSService::MODE_TRRONLY,
+ "Confirmation should be disabled");
+ MOZ_ASSERT(!service->mConfirmationNS.Equals("skip"),
+ "Confirmation should be disabled");
+
+ LOG(("maybeConfirm(%s) starting confirmation test %s %s\n", aReason,
+ service->mPrivateURI.get(), service->mConfirmationNS.get()));
+
+ MOZ_ASSERT(mState == CONFIRM_OK || mState == CONFIRM_FAILED);
+
+ if (mState == CONFIRM_FAILED) {
+ LOG(("mConfirmation.mState -> CONFIRM_TRYING_FAILED"));
+ SetState(CONFIRM_TRYING_FAILED);
+ } else {
+ LOG(("mConfirmation.mState -> CONFIRM_TRYING_OK"));
+ SetState(CONFIRM_TRYING_OK);
+ }
+
+ nsCOMPtr<nsITimer> timer = std::move(mTimer);
+ if (timer) {
+ timer->Cancel();
+ }
+
+ MOZ_ASSERT(mode == nsIDNSService::MODE_TRRFIRST,
+ "Should only confirm in TRR first mode");
+ // Set aUseFreshConnection if TRR lookups are retried.
+ mTask = new TRR(service, service->mConfirmationNS, TRRTYPE_NS, ""_ns, false,
+ StaticPrefs::network_trr_retry_on_recoverable_errors());
+ mTask->SetTimeout(StaticPrefs::network_trr_confirmation_timeout_ms());
+ mTask->SetPurpose(TRR::Confirmation);
+
+ if (service->mLinkService) {
+ service->mLinkService->GetNetworkID(mNetworkId);
+ }
+
+ if (mFirstRequestTime.IsNull()) {
+ mFirstRequestTime = TimeStamp::Now();
+ }
+ if (mTrigger.IsEmpty()) {
+ mTrigger.Assign(aReason);
+ }
+
+ LOG(("Dispatching confirmation task: %p", mTask.get()));
+ service->DispatchTRRRequestInternal(mTask, false);
+ };
+
+ switch (aEvent) {
+ case ConfirmationEvent::Init:
+ resetConfirmation();
+ maybeConfirm("context-init");
+ break;
+ case ConfirmationEvent::PrefChange:
+ resetConfirmation();
+ maybeConfirm("pref-change");
+ break;
+ case ConfirmationEvent::ConfirmationRetry:
+ MOZ_ASSERT(mState == CONFIRM_FAILED);
+ if (mState == CONFIRM_FAILED) {
+ maybeConfirm("confirmation-retry");
+ }
+ break;
+ case ConfirmationEvent::FailedLookups:
+ MOZ_ASSERT(mState == CONFIRM_OK);
+ mTrigger.Assign("failed-lookups");
+ mFailedLookups = nsDependentCSubstring(
+ mFailureReasons, mTRRFailures % ConfirmationContext::RESULTS_SIZE);
+ maybeConfirm("failed-lookups");
+ break;
+ case ConfirmationEvent::RetryTRR:
+ MOZ_ASSERT(mState == CONFIRM_OK);
+ maybeConfirm("retry-trr");
+ break;
+ case ConfirmationEvent::URIChange:
+ resetConfirmation();
+ maybeConfirm("uri-change");
+ break;
+ case ConfirmationEvent::CaptivePortalConnectivity:
+ // If we area already confirmed then we're fine.
+ // If there is a confirmation in progress, likely it started before
+ // we had full connectivity, so it may be hanging. We reset and try again.
+ if (mState == CONFIRM_FAILED || mState == CONFIRM_TRYING_FAILED ||
+ mState == CONFIRM_TRYING_OK) {
+ resetConfirmation();
+ maybeConfirm("cp-connectivity");
+ }
+ break;
+ case ConfirmationEvent::NetworkUp:
+ if (mState != CONFIRM_OK) {
+ resetConfirmation();
+ maybeConfirm("network-up");
+ }
+ break;
+ case ConfirmationEvent::ConfirmOK:
+ SetState(CONFIRM_OK);
+ mTask = nullptr;
+ break;
+ case ConfirmationEvent::ConfirmFail:
+ MOZ_ASSERT(mState == CONFIRM_TRYING_OK ||
+ mState == CONFIRM_TRYING_FAILED);
+ SetState(CONFIRM_FAILED);
+ mTask = nullptr;
+ // retry failed NS confirmation
+
+ NS_NewTimerWithCallback(getter_AddRefs(mTimer), this, mRetryInterval,
+ nsITimer::TYPE_ONE_SHOT);
+ if (mRetryInterval < 64000) {
+ // double the interval up to this point
+ mRetryInterval *= 2;
+ }
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unexpected ConfirmationEvent");
+ }
+
+ return prevAddr != TaskAddr();
+}
+
+bool TRRService::MaybeBootstrap(const nsACString& aPossible,
+ nsACString& aResult) {
+ MutexSingleWriterAutoLock lock(mLock);
+ if (mMode == nsIDNSService::MODE_TRROFF || mBootstrapAddr.IsEmpty()) {
+ return false;
+ }
+
+ nsCOMPtr<nsIURI> url;
+ nsresult rv =
+ NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID)
+ .Apply(&nsIStandardURLMutator::Init, nsIStandardURL::URLTYPE_STANDARD,
+ 443, mPrivateURI, nullptr, nullptr, nullptr)
+ .Finalize(url);
+ if (NS_FAILED(rv)) {
+ LOG(("TRRService::MaybeBootstrap failed to create URI!\n"));
+ return false;
+ }
+
+ nsAutoCString host;
+ url->GetHost(host);
+ if (!aPossible.Equals(host)) {
+ return false;
+ }
+ LOG(("TRRService::MaybeBootstrap: use %s instead of %s\n",
+ mBootstrapAddr.get(), host.get()));
+ aResult = mBootstrapAddr;
+ return true;
+}
+
+bool TRRService::IsDomainBlocked(const nsACString& aHost,
+ const nsACString& aOriginSuffix,
+ bool aPrivateBrowsing) {
+ auto bl = mTRRBLStorage.Lock();
+ if (bl->IsEmpty()) {
+ return false;
+ }
+
+ // use a unified casing for the hashkey
+ nsAutoCString hashkey(aHost + aOriginSuffix);
+ if (auto val = bl->Lookup(hashkey)) {
+ int32_t until =
+ *val + int32_t(StaticPrefs::network_trr_temp_blocklist_duration_sec());
+ int32_t expire = NowInSeconds();
+ if (until > expire) {
+ LOG(("Host [%s] is TRR blocklisted\n", nsCString(aHost).get()));
+ return true;
+ }
+
+ // the blocklisted entry has expired
+ val.Remove();
+ }
+ return false;
+}
+
+// When running in TRR-only mode, the blocklist is not used and it will also
+// try resolving the localhost / .local names.
+bool TRRService::IsTemporarilyBlocked(const nsACString& aHost,
+ const nsACString& aOriginSuffix,
+ bool aPrivateBrowsing,
+ bool aParentsToo) // false if domain
+{
+ if (!StaticPrefs::network_trr_temp_blocklist()) {
+ LOG(("TRRService::IsTemporarilyBlocked temp blocklist disabled by pref"));
+ return false;
+ }
+
+ if (mMode == nsIDNSService::MODE_TRRONLY) {
+ return false; // might as well try
+ }
+
+ LOG(("Checking if host [%s] is blocklisted", aHost.BeginReading()));
+
+ int32_t dot = aHost.FindChar('.');
+ if ((dot == kNotFound) && aParentsToo) {
+ // Only if a full host name. Domains can be dotless to be able to
+ // blocklist entire TLDs
+ return true;
+ }
+
+ if (IsDomainBlocked(aHost, aOriginSuffix, aPrivateBrowsing)) {
+ return true;
+ }
+
+ nsDependentCSubstring domain = Substring(aHost, 0);
+ while (dot != kNotFound) {
+ dot++;
+ domain.Rebind(domain, dot, domain.Length() - dot);
+
+ if (IsDomainBlocked(domain, aOriginSuffix, aPrivateBrowsing)) {
+ return true;
+ }
+
+ dot = domain.FindChar('.');
+ }
+
+ return false;
+}
+
+bool TRRService::IsExcludedFromTRR(const nsACString& aHost) {
+ // This method may be called off the main thread. We need to lock so
+ // mExcludedDomains and mDNSSuffixDomains don't change while this code
+ // is running.
+ MutexSingleWriterAutoLock lock(mLock);
+
+ return IsExcludedFromTRR_unlocked(aHost);
+}
+
+bool TRRService::IsExcludedFromTRR_unlocked(const nsACString& aHost) {
+ mLock.AssertOnWritingThreadOrHeld();
+
+ int32_t dot = 0;
+ // iteratively check the sub-domain of |aHost|
+ while (dot < static_cast<int32_t>(aHost.Length())) {
+ nsDependentCSubstring subdomain =
+ Substring(aHost, dot, aHost.Length() - dot);
+
+ if (mExcludedDomains.Contains(subdomain)) {
+ LOG(("Subdomain [%s] of host [%s] Is Excluded From TRR via pref\n",
+ subdomain.BeginReading(), aHost.BeginReading()));
+ return true;
+ }
+ if (mDNSSuffixDomains.Contains(subdomain)) {
+ LOG(("Subdomain [%s] of host [%s] Is Excluded From TRR via pref\n",
+ subdomain.BeginReading(), aHost.BeginReading()));
+ return true;
+ }
+ if (mEtcHostsDomains.Contains(subdomain)) {
+ LOG(("Subdomain [%s] of host [%s] Is Excluded From TRR by /etc/hosts\n",
+ subdomain.BeginReading(), aHost.BeginReading()));
+ return true;
+ }
+
+ dot = aHost.FindChar('.', dot + 1);
+ if (dot == kNotFound) {
+ break;
+ }
+ dot++;
+ }
+
+ return false;
+}
+
+void TRRService::AddToBlocklist(const nsACString& aHost,
+ const nsACString& aOriginSuffix,
+ bool privateBrowsing, bool aParentsToo) {
+ if (!StaticPrefs::network_trr_temp_blocklist()) {
+ LOG(("TRRService::AddToBlocklist temp blocklist disabled by pref"));
+ return;
+ }
+
+ LOG(("TRR blocklist %s\n", nsCString(aHost).get()));
+ nsAutoCString hashkey(aHost + aOriginSuffix);
+
+ // this overwrites any existing entry
+ {
+ auto bl = mTRRBLStorage.Lock();
+ bl->InsertOrUpdate(hashkey, NowInSeconds());
+ }
+
+ // See bug 1700405. Some test expects 15 trr consecutive failures, but the NS
+ // check against the base domain is successful. So, we skip this NS check when
+ // the pref said so in order to pass the test reliably.
+ if (aParentsToo && !StaticPrefs::network_trr_skip_check_for_blocked_host()) {
+ // when given a full host name, verify its domain as well
+ int32_t dot = aHost.FindChar('.');
+ if (dot != kNotFound) {
+ // this has a domain to be checked
+ dot++;
+ nsDependentCSubstring domain =
+ Substring(aHost, dot, aHost.Length() - dot);
+ nsAutoCString check(domain);
+ if (IsTemporarilyBlocked(check, aOriginSuffix, privateBrowsing, false)) {
+ // the domain part is already blocklisted, no need to add this entry
+ return;
+ }
+ // verify 'check' over TRR
+ LOG(("TRR: verify if '%s' resolves as NS\n", check.get()));
+
+ // check if there's an NS entry for this name
+ RefPtr<TRR> trr = new TRR(this, check, TRRTYPE_NS, aOriginSuffix,
+ privateBrowsing, false);
+ trr->SetPurpose(TRR::Blocklist);
+ DispatchTRRRequest(trr);
+ }
+ }
+}
+
+NS_IMETHODIMP
+TRRService::ConfirmationContext::Notify(nsITimer* aTimer) {
+ MutexSingleWriterAutoLock lock(OwningObject()->mLock);
+ if (aTimer == mTimer) {
+ HandleEvent(ConfirmationEvent::ConfirmationRetry, lock);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TRRService::ConfirmationContext::GetName(nsACString& aName) {
+ aName.AssignLiteral("TRRService::ConfirmationContext");
+ return NS_OK;
+}
+
+static char StatusToChar(nsresult aLookupStatus, nsresult aChannelStatus) {
+ // If the resolution fails in the TRR channel then we'll have a failed
+ // aChannelStatus. Otherwise, we parse the response - if it's not a valid DNS
+ // packet or doesn't contain the correct responses aLookupStatus will be a
+ // failure code.
+ if (aChannelStatus == NS_OK) {
+ // Return + if confirmation was OK, or - if confirmation failed
+ return aLookupStatus == NS_OK ? '+' : '-';
+ }
+
+ if (nsCOMPtr<nsIIOService> ios = do_GetIOService()) {
+ bool hasConnectiviy = true;
+ ios->GetConnectivity(&hasConnectiviy);
+ if (!hasConnectiviy) {
+ // Browser has no active network interfaces = is offline.
+ return 'o';
+ }
+ }
+
+ switch (aChannelStatus) {
+ case NS_ERROR_NET_TIMEOUT_EXTERNAL:
+ // TRR timeout expired
+ return 't';
+ case NS_ERROR_UNKNOWN_HOST:
+ // TRRServiceChannel failed to due to unresolved host
+ return 'd';
+ default:
+ break;
+ }
+
+ // The error is a network error
+ if (NS_ERROR_GET_MODULE(aChannelStatus) == NS_ERROR_MODULE_NETWORK) {
+ return 'n';
+ }
+
+ // Some other kind of failure.
+ return '?';
+}
+
+void TRRService::RetryTRRConfirm() {
+ if (mConfirmation.State() == CONFIRM_OK) {
+ LOG(("TRRService::RetryTRRConfirm triggering confirmation"));
+ mConfirmation.HandleEvent(ConfirmationEvent::RetryTRR);
+ }
+}
+
+void TRRService::RecordTRRStatus(TRR* aTrrRequest) {
+ MOZ_ASSERT_IF(XRE_IsParentProcess(), NS_IsMainThread() || IsOnTRRThread());
+ MOZ_ASSERT_IF(XRE_IsSocketProcess(), NS_IsMainThread());
+
+ nsresult channelStatus = aTrrRequest->ChannelStatus();
+
+ Telemetry::AccumulateCategoricalKeyed(
+ ProviderKey(), NS_SUCCEEDED(channelStatus)
+ ? Telemetry::LABELS_DNS_TRR_SUCCESS3::Fine
+ : (channelStatus == NS_ERROR_NET_TIMEOUT_EXTERNAL
+ ? Telemetry::LABELS_DNS_TRR_SUCCESS3::Timeout
+ : Telemetry::LABELS_DNS_TRR_SUCCESS3::Bad));
+
+ mConfirmation.RecordTRRStatus(aTrrRequest);
+}
+
+void TRRService::ConfirmationContext::RecordTRRStatus(TRR* aTrrRequest) {
+ nsresult channelStatus = aTrrRequest->ChannelStatus();
+
+ if (OwningObject()->Mode() == nsIDNSService::MODE_TRRONLY) {
+ mLastConfirmationSkipReason = aTrrRequest->SkipReason();
+ mLastConfirmationStatus = channelStatus;
+ }
+
+ if (NS_SUCCEEDED(channelStatus)) {
+ LOG(("TRRService::RecordTRRStatus channel success"));
+ mTRRFailures = 0;
+ return;
+ }
+
+ if (OwningObject()->Mode() != nsIDNSService::MODE_TRRFIRST) {
+ return;
+ }
+
+ // only count failures while in OK state
+ if (State() != CONFIRM_OK) {
+ return;
+ }
+
+ // When TRR retry is enabled, nsHostResolver will trigger Confirmation
+ // immediately upon a lookup failure, so nothing to be done here.
+ // nsHostResolver can assess the success of the lookup considering all the
+ // involved results (A, AAAA) so we let it tell us when to re-Confirm.
+ if (StaticPrefs::network_trr_retry_on_recoverable_errors()) {
+ LOG(("TRRService not counting failures when retry is enabled"));
+ return;
+ }
+
+ mFailureReasons[mTRRFailures % ConfirmationContext::RESULTS_SIZE] =
+ StatusToChar(NS_OK, channelStatus);
+ uint32_t fails = ++mTRRFailures;
+ LOG(("TRRService::RecordTRRStatus fails=%u", fails));
+
+ if (fails >= StaticPrefs::network_trr_max_fails()) {
+ LOG(("TRRService had %u failures in a row\n", fails));
+ // When several failures occur we trigger a confirmation causing
+ // us to transition into the CONFIRM_TRYING_OK state.
+ // Only after the confirmation fails do we finally go into CONFIRM_FAILED
+ // and start skipping TRR.
+
+ // Trigger a confirmation immediately.
+ // If it fails, it will fire off a timer to start retrying again.
+ HandleEvent(ConfirmationEvent::FailedLookups);
+ }
+}
+
+void TRRService::ConfirmationContext::RecordEvent(
+ const char* aReason, const MutexSingleWriterAutoLock&) {
+ // Reset the confirmation context attributes
+ // Only resets the attributes that we keep for telemetry purposes.
+ auto reset = [&]() {
+ mAttemptCount = 0;
+ mNetworkId.Truncate();
+ mFirstRequestTime = TimeStamp();
+ mContextChangeReason.Assign(aReason);
+ mTrigger.Truncate();
+ mFailedLookups.Truncate();
+
+ mRetryInterval = StaticPrefs::network_trr_retry_timeout_ms();
+ };
+
+ if (mAttemptCount == 0) {
+ // XXX: resetting everything might not be the best thing here, even if the
+ // context changes, because there might still be a confirmation pending.
+ // But cancelling and retrying that confirmation might just make the whole
+ // confirmation longer for no reason.
+ reset();
+ return;
+ }
+
+ Telemetry::EventID eventType =
+ Telemetry::EventID::NetworkDns_Trrconfirmation_Context;
+
+ nsAutoCString results;
+ static_assert(RESULTS_SIZE < 64);
+
+ // mResults is a circular buffer ending at mAttemptCount
+ if (mAttemptCount <= RESULTS_SIZE) {
+ // We have fewer attempts than the size of the buffer, so all of the
+ // results are in the buffer.
+ results.Append(nsDependentCSubstring(mResults, mAttemptCount));
+ } else {
+ // More attempts than the buffer size.
+ // That means past RESULTS_SIZE attempts in order are
+ // [posInResults .. end-of-buffer) + [start-of-buffer .. posInResults)
+ uint32_t posInResults = mAttemptCount % RESULTS_SIZE;
+
+ results.Append(nsDependentCSubstring(mResults + posInResults,
+ RESULTS_SIZE - posInResults));
+ results.Append(nsDependentCSubstring(mResults, posInResults));
+ }
+
+ auto extra = Some<nsTArray<mozilla::Telemetry::EventExtraEntry>>({
+ Telemetry::EventExtraEntry{"trigger"_ns, mTrigger},
+ Telemetry::EventExtraEntry{"contextReason"_ns, mContextChangeReason},
+ Telemetry::EventExtraEntry{"attemptCount"_ns,
+ nsPrintfCString("%u", mAttemptCount)},
+ Telemetry::EventExtraEntry{"results"_ns, results},
+ Telemetry::EventExtraEntry{
+ "time"_ns,
+ nsPrintfCString(
+ "%f",
+ !mFirstRequestTime.IsNull()
+ ? (TimeStamp::Now() - mFirstRequestTime).ToMilliseconds()
+ : 0.0)},
+ Telemetry::EventExtraEntry{"networkID"_ns, mNetworkId},
+ Telemetry::EventExtraEntry{"captivePortal"_ns,
+ nsPrintfCString("%i", mCaptivePortalStatus)},
+ });
+
+ if (mTrigger.Equals("failed-lookups"_ns)) {
+ extra.ref().AppendElement(
+ Telemetry::EventExtraEntry{"failedLookups"_ns, mFailedLookups});
+ }
+
+ enum ConfirmationState state = mState;
+ Telemetry::RecordEvent(eventType, mozilla::Some(nsPrintfCString("%u", state)),
+ extra);
+
+ reset();
+}
+
+void TRRService::ConfirmationContext::RequestCompleted(
+ nsresult aLookupStatus, nsresult aChannelStatus) {
+ mResults[mAttemptCount % RESULTS_SIZE] =
+ StatusToChar(aLookupStatus, aChannelStatus);
+ mAttemptCount++;
+}
+
+void TRRService::ConfirmationContext::CompleteConfirmation(nsresult aStatus,
+ TRR* aTRRRequest) {
+ {
+ MutexSingleWriterAutoLock lock(OwningObject()->mLock);
+ // Ignore confirmations that dont match the pending task.
+ if (mTask != aTRRRequest) {
+ return;
+ }
+ MOZ_ASSERT(State() == CONFIRM_TRYING_OK ||
+ State() == CONFIRM_TRYING_FAILED);
+ if (State() != CONFIRM_TRYING_OK && State() != CONFIRM_TRYING_FAILED) {
+ return;
+ }
+
+ RequestCompleted(aStatus, aTRRRequest->ChannelStatus());
+ mLastConfirmationSkipReason = aTRRRequest->SkipReason();
+ mLastConfirmationStatus = aTRRRequest->ChannelStatus();
+
+ MOZ_ASSERT(mTask);
+ if (NS_SUCCEEDED(aStatus)) {
+ HandleEvent(ConfirmationEvent::ConfirmOK, lock);
+ } else {
+ HandleEvent(ConfirmationEvent::ConfirmFail, lock);
+ }
+
+ if (State() == CONFIRM_OK) {
+ // Record event and start new confirmation context
+ RecordEvent("success", lock);
+ }
+ LOG(("TRRService finishing confirmation test %s %d %X\n",
+ OwningObject()->mPrivateURI.get(), State(), (unsigned int)aStatus));
+ }
+
+ if (State() == CONFIRM_OK) {
+ // A fresh confirmation means previous blocked entries might not
+ // be valid anymore.
+ auto bl = OwningObject()->mTRRBLStorage.Lock();
+ bl->Clear();
+ } else {
+ MOZ_ASSERT(State() == CONFIRM_FAILED);
+ }
+
+ Telemetry::Accumulate(Telemetry::DNS_TRR_NS_VERFIFIED3,
+ TRRService::ProviderKey(), (State() == CONFIRM_OK));
+}
+
+AHostResolver::LookupStatus TRRService::CompleteLookup(
+ nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb,
+ const nsACString& aOriginSuffix, TRRSkippedReason aReason,
+ TRR* aTRRRequest) {
+ // this is an NS check for the TRR blocklist or confirmationNS check
+
+ MOZ_ASSERT_IF(XRE_IsParentProcess(), NS_IsMainThread() || IsOnTRRThread());
+ MOZ_ASSERT_IF(XRE_IsSocketProcess(), NS_IsMainThread());
+ MOZ_ASSERT(!rec);
+
+ RefPtr<AddrInfo> newRRSet(aNewRRSet);
+ MOZ_ASSERT(newRRSet && newRRSet->TRRType() == TRRTYPE_NS);
+
+ if (aTRRRequest->Purpose() == TRR::Confirmation) {
+ mConfirmation.CompleteConfirmation(status, aTRRRequest);
+ return LOOKUP_OK;
+ }
+
+ if (aTRRRequest->Purpose() == TRR::Blocklist) {
+ if (NS_SUCCEEDED(status)) {
+ LOG(("TRR verified %s to be fine!\n", newRRSet->Hostname().get()));
+ } else {
+ LOG(("TRR says %s doesn't resolve as NS!\n", newRRSet->Hostname().get()));
+ AddToBlocklist(newRRSet->Hostname(), aOriginSuffix, pb, false);
+ }
+ return LOOKUP_OK;
+ }
+
+ MOZ_ASSERT_UNREACHABLE(
+ "TRRService::CompleteLookup called for unexpected request");
+ return LOOKUP_OK;
+}
+
+AHostResolver::LookupStatus TRRService::CompleteLookupByType(
+ nsHostRecord*, nsresult, mozilla::net::TypeRecordResultType& aResult,
+ mozilla::net::TRRSkippedReason aReason, uint32_t aTtl, bool aPb) {
+ return LOOKUP_OK;
+}
+
+NS_IMETHODIMP TRRService::OnProxyConfigChanged() {
+ LOG(("TRRService::OnProxyConfigChanged"));
+
+ nsAutoCString uri;
+ GetURI(uri);
+ AsyncCreateTRRConnectionInfo(uri);
+
+ return NS_OK;
+}
+
+void TRRService::InitTRRConnectionInfo() {
+ if (XRE_IsParentProcess()) {
+ TRRServiceBase::InitTRRConnectionInfo();
+ return;
+ }
+
+ MOZ_ASSERT(XRE_IsSocketProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+
+ TRRServiceChild* child = TRRServiceChild::GetSingleton();
+ if (child && child->CanSend()) {
+ LOG(("TRRService::SendInitTRRConnectionInfo"));
+ Unused << child->SendInitTRRConnectionInfo();
+ }
+}
+
+} // namespace mozilla::net
diff --git a/netwerk/dns/TRRService.h b/netwerk/dns/TRRService.h
new file mode 100644
index 0000000000..3283a8ea06
--- /dev/null
+++ b/netwerk/dns/TRRService.h
@@ -0,0 +1,384 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef TRRService_h_
+#define TRRService_h_
+
+#include "mozilla/DataMutex.h"
+#include "nsHostResolver.h"
+#include "nsIObserver.h"
+#include "nsITimer.h"
+#include "nsWeakReference.h"
+#include "TRRServiceBase.h"
+#include "nsICaptivePortalService.h"
+#include "nsTHashSet.h"
+#include "TRR.h"
+
+class nsDNSService;
+class nsIPrefBranch;
+class nsINetworkLinkService;
+class nsIObserverService;
+
+namespace mozilla {
+namespace net {
+
+class TRRServiceChild;
+class TRRServiceParent;
+
+class TRRService : public TRRServiceBase,
+ public nsIObserver,
+ public nsSupportsWeakReference,
+ public AHostResolver,
+ public SingleWriterLockOwner {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIOBSERVER
+ NS_DECL_NSIPROXYCONFIGCHANGEDCALLBACK
+
+ TRRService();
+ static TRRService* Get();
+
+ bool OnWritingThread() const override { return NS_IsMainThread(); }
+
+ nsresult Init();
+ nsresult Start();
+ bool Enabled(nsIRequest::TRRMode aRequestMode = nsIRequest::TRR_DEFAULT_MODE);
+ bool IsConfirmed() { return mConfirmation.State() == CONFIRM_OK; }
+ uint32_t ConfirmationState() { return mConfirmation.State(); }
+
+ void GetURI(nsACString& result) override;
+ nsresult GetCredentials(nsCString& result);
+ uint32_t GetRequestTimeout();
+ void RetryTRRConfirm();
+
+ LookupStatus CompleteLookup(nsHostRecord*, nsresult, mozilla::net::AddrInfo*,
+ bool pb, const nsACString& aOriginSuffix,
+ TRRSkippedReason aReason,
+ TRR* aTrrRequest) override;
+ LookupStatus CompleteLookupByType(nsHostRecord*, nsresult,
+ mozilla::net::TypeRecordResultType&,
+ TRRSkippedReason, uint32_t,
+ bool pb) override;
+ void AddToBlocklist(const nsACString& host, const nsACString& originSuffix,
+ bool privateBrowsing, bool aParentsToo);
+ bool IsTemporarilyBlocked(const nsACString& aHost,
+ const nsACString& aOriginSuffix,
+ bool aPrivateBrowsing, bool aParentsToo);
+ bool IsExcludedFromTRR(const nsACString& aHost);
+
+ bool MaybeBootstrap(const nsACString& possible, nsACString& result);
+ void RecordTRRStatus(TRR* aTrrRequest);
+ bool ParentalControlEnabled() const { return mParentalControlEnabled; }
+
+ nsresult DispatchTRRRequest(TRR* aTrrRequest);
+ already_AddRefed<nsIThread> TRRThread();
+ bool IsOnTRRThread();
+
+ bool IsUsingAutoDetectedURL() { return mURISetByDetection; }
+
+ void SetHeuristicDetectionResult(TRRSkippedReason aValue) {
+ mHeuristicDetectionValue = aValue;
+ }
+ TRRSkippedReason GetHeuristicDetectionResult() {
+ return mHeuristicDetectionValue;
+ }
+
+ nsresult LastConfirmationStatus() {
+ return mConfirmation.LastConfirmationStatus();
+ }
+ TRRSkippedReason LastConfirmationSkipReason() {
+ return mConfirmation.LastConfirmationSkipReason();
+ }
+
+ // Returns a reference to a static string identifying the current DoH server
+ // If the DoH server is not one of the built-in ones it will return "(other)"
+ static const nsCString& ProviderKey();
+ static void SetProviderDomain(const nsACString& aTRRDomain);
+ // Only called when TRR mode changed.
+ static void SetCurrentTRRMode(nsIDNSService::ResolverMode aMode);
+
+ void InitTRRConnectionInfo() override;
+
+ void DontUseTRRThread() { mDontUseTRRThread = true; }
+
+ private:
+ virtual ~TRRService();
+
+ friend class TRRServiceChild;
+ friend class TRRServiceParent;
+ static void AddObserver(nsIObserver* aObserver,
+ nsIObserverService* aObserverService = nullptr);
+ static bool CheckCaptivePortalIsPassed();
+ static bool GetParentalControlEnabledInternal();
+ static bool CheckPlatformDNSStatus(nsINetworkLinkService* aLinkService);
+
+ nsresult ReadPrefs(const char* name);
+ void GetPrefBranch(nsIPrefBranch** result);
+ friend class ::nsDNSService;
+ void SetDetectedTrrURI(const nsACString& aURI);
+
+ bool IsDomainBlocked(const nsACString& aHost, const nsACString& aOriginSuffix,
+ bool aPrivateBrowsing);
+ bool IsExcludedFromTRR_unlocked(const nsACString& aHost);
+
+ void RebuildSuffixList(nsTArray<nsCString>&& aSuffixList);
+
+ nsresult DispatchTRRRequestInternal(TRR* aTrrRequest, bool aWithLock);
+ already_AddRefed<nsIThread> TRRThread_locked();
+ already_AddRefed<nsIThread> MainThreadOrTRRThread(bool aWithLock = true);
+
+ // This method will process the URI and try to set mPrivateURI to that value.
+ // Will return true if performed the change (if the value was different)
+ // or false if mPrivateURI already had that value.
+ bool MaybeSetPrivateURI(const nsACString& aURI) override;
+ void ClearEntireCache();
+
+ virtual void ReadEtcHostsFile() override;
+ void AddEtcHosts(const nsTArray<nsCString>&);
+
+ bool mInitialized{false};
+ MutexSingleWriter mLock;
+
+ nsCString mPrivateCred; // main thread only
+ nsCString mConfirmationNS MOZ_GUARDED_BY(mLock){"example.com"_ns};
+ nsCString mBootstrapAddr MOZ_GUARDED_BY(mLock);
+
+ Atomic<bool, Relaxed> mCaptiveIsPassed{
+ false}; // set when captive portal check is passed
+ Atomic<bool, Relaxed> mShutdown{false};
+ Atomic<bool, Relaxed> mDontUseTRRThread{false};
+
+ // TRR Blocklist storage
+ // mTRRBLStorage is only modified on the main thread, but we query whether it
+ // is initialized or not off the main thread as well. Therefore we need to
+ // lock while creating it and while accessing it off the main thread.
+ DataMutex<nsTHashMap<nsCStringHashKey, int32_t>> mTRRBLStorage{
+ "DataMutex::TRRBlocklist"};
+
+ // A set of domains that we should not use TRR for.
+ nsTHashSet<nsCString> mExcludedDomains MOZ_GUARDED_BY(mLock);
+ nsTHashSet<nsCString> mDNSSuffixDomains MOZ_GUARDED_BY(mLock);
+ nsTHashSet<nsCString> mEtcHostsDomains MOZ_GUARDED_BY(mLock);
+
+ // The result of the TRR heuristic detection
+ TRRSkippedReason mHeuristicDetectionValue = nsITRRSkipReason::TRR_UNSET;
+
+ enum class ConfirmationEvent {
+ Init,
+ PrefChange,
+ ConfirmationRetry,
+ FailedLookups,
+ RetryTRR,
+ URIChange,
+ CaptivePortalConnectivity,
+ NetworkUp,
+ ConfirmOK,
+ ConfirmFail,
+ };
+
+ // (FailedLookups/RetryTRR/URIChange/NetworkUp)
+ // +---------------------------+
+ // +-----------+ | |
+ // | (Init) | +------v---------+ +-+--+
+ // | | TRR turned on | | (ConfirmOK) | |
+ // | OFF +---------------> TRY-OK +---------------> OK |
+ // | | (PrefChange) | | | |
+ // +-----^-----+ +^-^----+--------+ +-^--+
+ // | (PrefChange/CP) | | | |
+ // TRR + +------------------+ | | |
+ // off | | +----+ |(ConfirmFail) |(ConfirmOK)
+ // (Pref)| | | | |
+ // +---------+-+ | | |
+ // | | (CPConn) | +-------v--------+ +-+---------+
+ // | ANY-STATE | (NetworkUp)| | | timer | |
+ // | | (URIChange)+-+ FAIL +---------------> TRY-FAIL |
+ // +-----+-----+ | | (Confirmation | |
+ // | +------^---------+ Retry) +------+----+
+ // | (PrefChange) | |
+ // | TRR_ONLY mode or +--------------------------------+
+ // | confirmationNS = skip (ConfirmFail)
+ // +-----v-----+
+ // | |
+ // | DISABLED |
+ // | |
+ // +-----------+
+ //
+ enum ConfirmationState {
+ CONFIRM_OFF = 0,
+ CONFIRM_TRYING_OK = 1,
+ CONFIRM_OK = 2,
+ CONFIRM_FAILED = 3,
+ CONFIRM_TRYING_FAILED = 4,
+ CONFIRM_DISABLED = 5,
+ };
+
+ class ConfirmationContext final : public nsITimerCallback, public nsINamed {
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSITIMERCALLBACK
+ NS_DECL_NSINAMED
+
+ private:
+ static const size_t RESULTS_SIZE = 32;
+
+ RefPtr<TRR> mTask;
+ nsCOMPtr<nsITimer> mTimer;
+ uint32_t mRetryInterval = 125; // milliseconds until retry
+ // The number of TRR requests that failed in a row.
+ Atomic<uint32_t, Relaxed> mTRRFailures{0};
+
+ // This buffer holds consecutive TRR failures reported by calling
+ // RecordTRRStatus(). It is only meant for reporting event telemetry.
+ char mFailureReasons[RESULTS_SIZE] = {0};
+
+ // The number of confirmation retries.
+ uint32_t mAttemptCount = 0;
+
+ // The results of past confirmation attempts.
+ // This is circular buffer ending at mAttemptCount.
+ char mResults[RESULTS_SIZE] = {0};
+
+ // Time when first confirmation started. Needed so we can
+ // record the time from start to confirmed.
+ TimeStamp mFirstRequestTime;
+ // The network ID at the start of the last confirmation attempt
+ nsCString mNetworkId;
+ // Captive portal status at the time of recording.
+ int32_t mCaptivePortalStatus = nsICaptivePortalService::UNKNOWN;
+
+ // The reason the confirmation context changed.
+ nsCString mContextChangeReason;
+
+ // What triggered the confirmation
+ nsCString mTrigger;
+
+ // String representation of consecutive failed lookups that triggered
+ // confirmation.
+ nsCString mFailedLookups;
+
+ Atomic<TRRSkippedReason, Relaxed> mLastConfirmationSkipReason{
+ nsITRRSkipReason::TRR_UNSET};
+ Atomic<nsresult, Relaxed> mLastConfirmationStatus{NS_OK};
+
+ void SetState(enum ConfirmationState aNewState);
+
+ public:
+ // Called when a confirmation completes successfully or when the
+ // confirmation context changes.
+ void RecordEvent(const char* aReason, const MutexSingleWriterAutoLock&);
+
+ // Called when a confirmation request is completed. The status is recorded
+ // in the results.
+ void RequestCompleted(nsresult aLookupStatus, nsresult aChannelStatus);
+
+ enum ConfirmationState State() { return mState; }
+
+ void CompleteConfirmation(nsresult aStatus, TRR* aTrrRequest);
+
+ void RecordTRRStatus(TRR* aTrrRequest);
+
+ // Returns true when handling the event caused a new confirmation task to be
+ // dispatched.
+ bool HandleEvent(ConfirmationEvent aEvent);
+ bool HandleEvent(ConfirmationEvent aEvent,
+ const MutexSingleWriterAutoLock&);
+
+ void SetCaptivePortalStatus(int32_t aStatus) {
+ mCaptivePortalStatus = aStatus;
+ }
+
+ TRRSkippedReason LastConfirmationSkipReason() {
+ return mLastConfirmationSkipReason;
+ }
+ nsresult LastConfirmationStatus() { return mLastConfirmationStatus; }
+
+ uintptr_t TaskAddr() { return uintptr_t(mTask.get()); }
+
+ private:
+ // Since the ConfirmationContext is embedded in the TRRService object
+ // we can easily get a pointer to the TRRService. ConfirmationContext
+ // delegates AddRef/Release calls to the owning object since they are
+ // guaranteed to have the same lifetime.
+ TRRService* OwningObject() {
+ return reinterpret_cast<TRRService*>(
+ reinterpret_cast<uint8_t*>(this) -
+ offsetof(TRRService, mConfirmation) -
+ offsetof(ConfirmationWrapper, mConfirmation));
+ }
+
+ Atomic<enum ConfirmationState, Relaxed> mState{CONFIRM_OFF};
+
+ // TRRService needs to be a friend class because it needs to access the
+ // destructor.
+ friend class TRRService;
+ ~ConfirmationContext() = default;
+ };
+
+ // Because TRRService needs to be a friend class to ConfirmationContext that
+ // means it can access member variables. In order to properly separate logic
+ // and prevent direct access to its member variables we embed it in a wrapper
+ // class.
+ class ConfirmationWrapper {
+ public:
+ // Called when a confirmation completes successfully or when the
+ // confirmation context changes.
+ void RecordEvent(const char* aReason,
+ const MutexSingleWriterAutoLock& aLock) {
+ mConfirmation.RecordEvent(aReason, aLock);
+ }
+
+ // Called when a confirmation request is completed. The status is recorded
+ // in the results.
+ void RequestCompleted(nsresult aLookupStatus, nsresult aChannelStatus) {
+ mConfirmation.RequestCompleted(aLookupStatus, aChannelStatus);
+ }
+
+ enum ConfirmationState State() { return mConfirmation.State(); }
+
+ void CompleteConfirmation(nsresult aStatus, TRR* aTrrRequest) {
+ mConfirmation.CompleteConfirmation(aStatus, aTrrRequest);
+ }
+
+ void RecordTRRStatus(TRR* aTrrRequest) {
+ mConfirmation.RecordTRRStatus(aTrrRequest);
+ }
+
+ bool HandleEvent(ConfirmationEvent aEvent) {
+ return mConfirmation.HandleEvent(aEvent);
+ }
+
+ bool HandleEvent(ConfirmationEvent aEvent,
+ const MutexSingleWriterAutoLock& lock) {
+ return mConfirmation.HandleEvent(aEvent, lock);
+ }
+
+ void SetCaptivePortalStatus(int32_t aStatus) {
+ mConfirmation.SetCaptivePortalStatus(aStatus);
+ }
+
+ TRRSkippedReason LastConfirmationSkipReason() {
+ return mConfirmation.LastConfirmationSkipReason();
+ }
+ nsresult LastConfirmationStatus() {
+ return mConfirmation.LastConfirmationStatus();
+ }
+
+ private:
+ friend TRRService* ConfirmationContext::OwningObject();
+ ConfirmationContext mConfirmation;
+ };
+
+ ConfirmationWrapper mConfirmation;
+
+ bool mParentalControlEnabled{false};
+ // This is used to track whether a confirmation was triggered by a URI change,
+ // so we don't trigger another one just because other prefs have changed.
+ bool mConfirmationTriggered{false};
+ nsCOMPtr<nsINetworkLinkService> mLinkService;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // TRRService_h_
diff --git a/netwerk/dns/TRRServiceBase.cpp b/netwerk/dns/TRRServiceBase.cpp
new file mode 100644
index 0000000000..943edc41dd
--- /dev/null
+++ b/netwerk/dns/TRRServiceBase.cpp
@@ -0,0 +1,399 @@
+/* -*- 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 "TRRServiceBase.h"
+
+#include "TRRService.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/ScopeExit.h"
+#include "nsHostResolver.h"
+#include "nsNetUtil.h"
+#include "nsIOService.h"
+#include "nsIDNSService.h"
+#include "nsIProxyInfo.h"
+#include "nsHttpConnectionInfo.h"
+#include "nsHttpHandler.h"
+#include "mozilla/StaticPrefs_network.h"
+#include "AlternateServices.h"
+#include "ProxyConfigLookup.h"
+// Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
+#include "DNSLogging.h"
+
+#if defined(XP_WIN)
+# include <shlobj.h> // for SHGetSpecialFolderPathA
+#endif // XP_WIN
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS(TRRServiceBase, nsIProxyConfigChangedCallback)
+
+TRRServiceBase::TRRServiceBase()
+ : mDefaultTRRConnectionInfo("DataMutex::mDefaultTRRConnectionInfo") {}
+
+TRRServiceBase::~TRRServiceBase() {
+ if (mTRRConnectionInfoInited) {
+ UnregisterProxyChangeListener();
+ }
+}
+
+void TRRServiceBase::ProcessURITemplate(nsACString& aURI) {
+ // URI Template, RFC 6570.
+ if (aURI.IsEmpty()) {
+ return;
+ }
+ nsAutoCString scheme;
+ nsCOMPtr<nsIIOService> ios(do_GetIOService());
+ if (ios) {
+ ios->ExtractScheme(aURI, scheme);
+ }
+ if (!scheme.Equals("https")) {
+ LOG(("TRRService TRR URI %s is not https. Not used.\n",
+ PromiseFlatCString(aURI).get()));
+ aURI.Truncate();
+ return;
+ }
+
+ // cut off everything from "{" to "}" sequences (potentially multiple),
+ // as a crude conversion from template into URI.
+ nsAutoCString uri(aURI);
+
+ do {
+ nsCCharSeparatedTokenizer openBrace(uri, '{');
+ if (openBrace.hasMoreTokens()) {
+ // the 'nextToken' is the left side of the open brace (or full uri)
+ nsAutoCString prefix(openBrace.nextToken());
+
+ // if there is an open brace, there's another token
+ const nsACString& endBrace = openBrace.nextToken();
+ nsCCharSeparatedTokenizer closeBrace(endBrace, '}');
+ if (closeBrace.hasMoreTokens()) {
+ // there is a close brace as well, make a URI out of the prefix
+ // and the suffix
+ closeBrace.nextToken();
+ nsAutoCString suffix(closeBrace.nextToken());
+ uri = prefix + suffix;
+ } else {
+ // no (more) close brace
+ break;
+ }
+ } else {
+ // no (more) open brace
+ break;
+ }
+ } while (true);
+
+ aURI = uri;
+}
+
+void TRRServiceBase::CheckURIPrefs() {
+ mURISetByDetection = false;
+
+ if (StaticPrefs::network_trr_use_ohttp() && !mOHTTPURIPref.IsEmpty()) {
+ MaybeSetPrivateURI(mOHTTPURIPref);
+ return;
+ }
+
+ // The user has set a custom URI so it takes precedence.
+ if (!mURIPref.IsEmpty()) {
+ MaybeSetPrivateURI(mURIPref);
+ return;
+ }
+
+ // Check if the rollout addon has set a pref.
+ if (!mRolloutURIPref.IsEmpty()) {
+ MaybeSetPrivateURI(mRolloutURIPref);
+ return;
+ }
+
+ // Otherwise just use the default value.
+ MaybeSetPrivateURI(mDefaultURIPref);
+}
+
+// static
+nsIDNSService::ResolverMode ModeFromPrefs(
+ nsIDNSService::ResolverMode& aTRRModePrefValue) {
+ // 0 - off, 1 - reserved, 2 - TRR first, 3 - TRR only, 4 - reserved,
+ // 5 - explicit off
+
+ auto processPrefValue = [](uint32_t value) -> nsIDNSService::ResolverMode {
+ if (value == nsIDNSService::MODE_RESERVED1 ||
+ value == nsIDNSService::MODE_RESERVED4 ||
+ value > nsIDNSService::MODE_TRROFF) {
+ return nsIDNSService::MODE_TRROFF;
+ }
+ return static_cast<nsIDNSService::ResolverMode>(value);
+ };
+
+ uint32_t tmp;
+ if (NS_FAILED(Preferences::GetUint("network.trr.mode", &tmp))) {
+ tmp = 0;
+ }
+ nsIDNSService::ResolverMode modeFromPref = processPrefValue(tmp);
+ aTRRModePrefValue = modeFromPref;
+
+ if (modeFromPref != nsIDNSService::MODE_NATIVEONLY) {
+ return modeFromPref;
+ }
+
+ if (NS_FAILED(Preferences::GetUint(kRolloutModePref, &tmp))) {
+ tmp = 0;
+ }
+ modeFromPref = processPrefValue(tmp);
+
+ return modeFromPref;
+}
+
+void TRRServiceBase::OnTRRModeChange() {
+ uint32_t oldMode = mMode;
+ // This is the value of the pref "network.trr.mode"
+ nsIDNSService::ResolverMode trrModePrefValue;
+ mMode = ModeFromPrefs(trrModePrefValue);
+ if (mMode != oldMode) {
+ LOG(("TRR Mode changed from %d to %d", oldMode, int(mMode)));
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->NotifyObservers(nullptr, NS_NETWORK_TRR_MODE_CHANGED_TOPIC, nullptr);
+ }
+
+ TRRService::SetCurrentTRRMode(trrModePrefValue);
+ }
+
+ static bool readHosts = false;
+ if ((mMode == nsIDNSService::MODE_TRRFIRST ||
+ mMode == nsIDNSService::MODE_TRRONLY) &&
+ !readHosts) {
+ readHosts = true;
+ ReadEtcHostsFile();
+ }
+}
+
+void TRRServiceBase::OnTRRURIChange() {
+ Preferences::GetCString("network.trr.uri", mURIPref);
+ Preferences::GetCString(kRolloutURIPref, mRolloutURIPref);
+ Preferences::GetCString("network.trr.default_provider_uri", mDefaultURIPref);
+ Preferences::GetCString("network.trr.ohttp.uri", mOHTTPURIPref);
+
+ CheckURIPrefs();
+}
+
+static already_AddRefed<nsHttpConnectionInfo> CreateConnInfoHelper(
+ nsIURI* aURI, nsIProxyInfo* aProxyInfo) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsAutoCString host;
+ nsAutoCString scheme;
+ nsAutoCString username;
+ int32_t port = -1;
+ bool isHttps = aURI->SchemeIs("https");
+
+ nsresult rv = aURI->GetScheme(scheme);
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+
+ rv = aURI->GetAsciiHost(host);
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+
+ rv = aURI->GetPort(&port);
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+
+ // Just a warning here because some nsIURIs do not implement this method.
+ if (NS_WARN_IF(NS_FAILED(aURI->GetUsername(username)))) {
+ LOG(("Failed to get username for aURI(%s)",
+ aURI->GetSpecOrDefault().get()));
+ }
+
+ gHttpHandler->MaybeAddAltSvcForTesting(aURI, username, false, nullptr,
+ OriginAttributes());
+
+ nsCOMPtr<nsProxyInfo> proxyInfo = do_QueryInterface(aProxyInfo);
+ RefPtr<nsHttpConnectionInfo> connInfo = new nsHttpConnectionInfo(
+ host, port, ""_ns, username, proxyInfo, OriginAttributes(), isHttps);
+ bool http2Allowed = !gHttpHandler->IsHttp2Excluded(connInfo);
+ bool http3Allowed = proxyInfo ? proxyInfo->IsDirect() : true;
+
+ RefPtr<AltSvcMapping> mapping;
+ if ((http2Allowed || http3Allowed) &&
+ AltSvcMapping::AcceptableProxy(proxyInfo) &&
+ (scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https")) &&
+ (mapping = gHttpHandler->GetAltServiceMapping(
+ scheme, host, port, false, OriginAttributes(), http2Allowed,
+ http3Allowed))) {
+ mapping->GetConnectionInfo(getter_AddRefs(connInfo), proxyInfo,
+ OriginAttributes());
+ }
+
+ return connInfo.forget();
+}
+
+void TRRServiceBase::InitTRRConnectionInfo() {
+ if (!XRE_IsParentProcess()) {
+ return;
+ }
+
+ if (mTRRConnectionInfoInited) {
+ return;
+ }
+
+ if (!NS_IsMainThread()) {
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "TRRServiceBase::InitTRRConnectionInfo",
+ [self = RefPtr{this}]() { self->InitTRRConnectionInfo(); }));
+ return;
+ }
+
+ LOG(("TRRServiceBase::InitTRRConnectionInfo"));
+ nsAutoCString uri;
+ GetURI(uri);
+ AsyncCreateTRRConnectionInfoInternal(uri);
+}
+
+void TRRServiceBase::AsyncCreateTRRConnectionInfo(const nsACString& aURI) {
+ LOG(
+ ("TRRServiceBase::AsyncCreateTRRConnectionInfo "
+ "mTRRConnectionInfoInited=%d",
+ bool(mTRRConnectionInfoInited)));
+ if (!mTRRConnectionInfoInited) {
+ return;
+ }
+
+ AsyncCreateTRRConnectionInfoInternal(aURI);
+}
+
+void TRRServiceBase::AsyncCreateTRRConnectionInfoInternal(
+ const nsACString& aURI) {
+ if (!XRE_IsParentProcess()) {
+ return;
+ }
+
+ SetDefaultTRRConnectionInfo(nullptr);
+
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsCOMPtr<nsIURI> dnsURI;
+ nsresult rv = NS_NewURI(getter_AddRefs(dnsURI), aURI);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ rv = ProxyConfigLookup::Create(
+ [self = RefPtr{this}, uri(dnsURI)](nsIProxyInfo* aProxyInfo,
+ nsresult aStatus) mutable {
+ if (NS_FAILED(aStatus)) {
+ self->SetDefaultTRRConnectionInfo(nullptr);
+ return;
+ }
+
+ RefPtr<nsHttpConnectionInfo> connInfo =
+ CreateConnInfoHelper(uri, aProxyInfo);
+ self->SetDefaultTRRConnectionInfo(connInfo);
+ if (!self->mTRRConnectionInfoInited) {
+ self->mTRRConnectionInfoInited = true;
+ self->RegisterProxyChangeListener();
+ }
+ },
+ dnsURI, 0, nullptr);
+
+ // mDefaultTRRConnectionInfo is set to nullptr at the beginning of this
+ // method, so we don't really care aobut the |rv| here. If it's failed,
+ // mDefaultTRRConnectionInfo stays as nullptr and we'll create a new
+ // connection info in TRRServiceChannel again.
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+}
+
+already_AddRefed<nsHttpConnectionInfo> TRRServiceBase::TRRConnectionInfo() {
+ RefPtr<nsHttpConnectionInfo> connInfo;
+ {
+ auto lock = mDefaultTRRConnectionInfo.Lock();
+ connInfo = *lock;
+ }
+ return connInfo.forget();
+}
+
+void TRRServiceBase::SetDefaultTRRConnectionInfo(
+ nsHttpConnectionInfo* aConnInfo) {
+ LOG(("TRRService::SetDefaultTRRConnectionInfo aConnInfo=%s",
+ aConnInfo ? aConnInfo->HashKey().get() : "none"));
+ {
+ auto lock = mDefaultTRRConnectionInfo.Lock();
+ lock.ref() = aConnInfo;
+ }
+}
+
+void TRRServiceBase::RegisterProxyChangeListener() {
+ if (!XRE_IsParentProcess()) {
+ return;
+ }
+
+ nsCOMPtr<nsIProtocolProxyService> pps =
+ do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID);
+ if (!pps) {
+ return;
+ }
+
+ pps->AddProxyConfigCallback(this);
+}
+
+void TRRServiceBase::UnregisterProxyChangeListener() {
+ if (!XRE_IsParentProcess()) {
+ return;
+ }
+
+ nsCOMPtr<nsIProtocolProxyService> pps =
+ do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID);
+ if (!pps) {
+ return;
+ }
+
+ pps->RemoveProxyConfigCallback(this);
+}
+
+void TRRServiceBase::DoReadEtcHostsFile(ParsingCallback aCallback) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ if (!StaticPrefs::network_trr_exclude_etc_hosts()) {
+ return;
+ }
+
+ auto readHostsTask = [aCallback]() {
+ MOZ_ASSERT(!NS_IsMainThread(), "Must not run on the main thread");
+#if defined(XP_WIN)
+ // Inspired by libevent/evdns.c
+ // Windows is a little coy about where it puts its configuration
+ // files. Sure, they're _usually_ in C:\windows\system32, but
+ // there's no reason in principle they couldn't be in
+ // W:\hoboken chicken emergency
+
+ nsCString path;
+ path.SetLength(MAX_PATH + 1);
+ if (!SHGetSpecialFolderPathA(NULL, path.BeginWriting(), CSIDL_SYSTEM,
+ false)) {
+ LOG(("Calling SHGetSpecialFolderPathA failed"));
+ return;
+ }
+
+ path.SetLength(strlen(path.get()));
+ path.Append("\\drivers\\etc\\hosts");
+#else
+ nsAutoCString path("/etc/hosts"_ns);
+#endif
+
+ LOG(("Reading hosts file at %s", path.get()));
+ rust_parse_etc_hosts(&path, aCallback);
+ };
+
+ Unused << NS_DispatchBackgroundTask(
+ NS_NewRunnableFunction("Read /etc/hosts file", readHostsTask),
+ NS_DISPATCH_EVENT_MAY_BLOCK);
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/TRRServiceBase.h b/netwerk/dns/TRRServiceBase.h
new file mode 100644
index 0000000000..a7f85fc95d
--- /dev/null
+++ b/netwerk/dns/TRRServiceBase.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef TRRServiceBase_h_
+#define TRRServiceBase_h_
+
+#include "mozilla/Atomics.h"
+#include "mozilla/DataMutex.h"
+#include "mozilla/net/rust_helper.h"
+#include "nsString.h"
+#include "nsIDNSService.h"
+#include "nsIProtocolProxyService2.h"
+
+class nsICancelable;
+class nsIProxyInfo;
+
+namespace mozilla {
+namespace net {
+
+class nsHttpConnectionInfo;
+
+static const char kRolloutURIPref[] = "doh-rollout.uri";
+static const char kRolloutModePref[] = "doh-rollout.mode";
+
+class TRRServiceBase : public nsIProxyConfigChangedCallback {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ TRRServiceBase();
+ nsIDNSService::ResolverMode Mode() { return mMode; }
+ virtual void GetURI(nsACString& result) = 0;
+ already_AddRefed<nsHttpConnectionInfo> TRRConnectionInfo();
+ // Called to initialize the connection info. Once the connection info is
+ // created first time, mTRRConnectionInfoInited will be set to true.
+ virtual void InitTRRConnectionInfo();
+ bool TRRConnectionInfoInited() const { return mTRRConnectionInfoInited; }
+
+ protected:
+ virtual ~TRRServiceBase();
+
+ virtual bool MaybeSetPrivateURI(const nsACString& aURI) = 0;
+ void ProcessURITemplate(nsACString& aURI);
+ // Checks the network.trr.uri or the doh-rollout.uri prefs and sets the URI
+ // in order of preference:
+ // 1. The value of network.trr.uri if it is not the default one, meaning
+ // is was set by an explicit user action
+ // 2. The value of doh-rollout.uri if it exists
+ // this is set by the rollout addon
+ // 3. The default value of network.trr.uri
+ void CheckURIPrefs();
+
+ void OnTRRModeChange();
+ void OnTRRURIChange();
+
+ void DoReadEtcHostsFile(ParsingCallback aCallback);
+ virtual void ReadEtcHostsFile() = 0;
+ // Called to create a connection info that will be used by TRRServiceChannel.
+ // Note that when this function is called, mDefaultTRRConnectionInfo will be
+ // set to null to invalidate the connection info.
+ // When the connection info is created, SetDefaultTRRConnectionInfo() is
+ // called to set the result to mDefaultTRRConnectionInfo.
+ // Note that this method does nothing when mTRRConnectionInfoInited is false.
+ // We want to starting updating the connection info after it's create first
+ // time.
+ void AsyncCreateTRRConnectionInfo(const nsACString& aURI);
+ void AsyncCreateTRRConnectionInfoInternal(const nsACString& aURI);
+ virtual void SetDefaultTRRConnectionInfo(nsHttpConnectionInfo* aConnInfo);
+ void RegisterProxyChangeListener();
+ void UnregisterProxyChangeListener();
+
+ nsCString mPrivateURI; // protected by mMutex
+ // Pref caches should only be used on the main thread.
+ nsCString mURIPref;
+ nsCString mRolloutURIPref;
+ nsCString mDefaultURIPref;
+ nsCString mOHTTPURIPref;
+
+ Atomic<nsIDNSService::ResolverMode, Relaxed> mMode{
+ nsIDNSService::MODE_NATIVEONLY};
+ Atomic<bool, Relaxed> mURISetByDetection{false};
+ Atomic<bool, Relaxed> mTRRConnectionInfoInited{false};
+ DataMutex<RefPtr<nsHttpConnectionInfo>> mDefaultTRRConnectionInfo;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // TRRServiceBase_h_
diff --git a/netwerk/dns/TRRServiceChild.cpp b/netwerk/dns/TRRServiceChild.cpp
new file mode 100644
index 0000000000..37be446f8f
--- /dev/null
+++ b/netwerk/dns/TRRServiceChild.cpp
@@ -0,0 +1,121 @@
+/* -*- 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 "mozilla/net/TRRServiceChild.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
+#include "nsHttpConnectionInfo.h"
+#include "nsIDNService.h"
+#include "nsIObserverService.h"
+#include "nsServiceManagerUtils.h"
+#include "TRRService.h"
+
+namespace mozilla {
+namespace net {
+
+static StaticRefPtr<nsIDNSService> sDNSService;
+static Atomic<TRRServiceChild*> sTRRServiceChild;
+
+NS_IMPL_ISUPPORTS(TRRServiceChild, nsIObserver, nsISupportsWeakReference)
+
+TRRServiceChild::TRRServiceChild() {
+ MOZ_ASSERT(NS_IsMainThread());
+ sTRRServiceChild = this;
+}
+
+TRRServiceChild::~TRRServiceChild() {
+ MOZ_ASSERT(NS_IsMainThread());
+ sTRRServiceChild = nullptr;
+}
+
+/* static */
+TRRServiceChild* TRRServiceChild::GetSingleton() {
+ MOZ_ASSERT(NS_IsMainThread());
+ return sTRRServiceChild;
+}
+
+void TRRServiceChild::Init(const bool& aCaptiveIsPassed,
+ const bool& aParentalControlEnabled,
+ nsTArray<nsCString>&& aDNSSuffixList) {
+ nsCOMPtr<nsIDNSService> dns =
+ do_GetService("@mozilla.org/network/dns-service;1");
+ sDNSService = dns;
+ ClearOnShutdown(&sDNSService);
+ MOZ_ASSERT(sDNSService);
+
+ TRRService* trrService = TRRService::Get();
+ MOZ_ASSERT(trrService);
+
+ trrService->mCaptiveIsPassed = aCaptiveIsPassed;
+ trrService->mParentalControlEnabled = aParentalControlEnabled;
+ trrService->RebuildSuffixList(std::move(aDNSSuffixList));
+
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ obs->AddObserver(this, "network:connectivity-service:dns-checks-complete",
+ true);
+ obs->AddObserver(this, "network:connectivity-service:ip-checks-complete",
+ true);
+}
+
+NS_IMETHODIMP
+TRRServiceChild::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) {
+ if (!strcmp(aTopic, "network:connectivity-service:ip-checks-complete") ||
+ !strcmp(aTopic, "network:connectivity-service:dns-checks-complete")) {
+ Unused << SendNotifyNetworkConnectivityServiceObservers(
+ nsPrintfCString("%s-from-socket-process", aTopic));
+ }
+
+ return NS_OK;
+}
+
+mozilla::ipc::IPCResult TRRServiceChild::RecvUpdatePlatformDNSInformation(
+ nsTArray<nsCString>&& aDNSSuffixList) {
+ TRRService::Get()->RebuildSuffixList(std::move(aDNSSuffixList));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TRRServiceChild::RecvUpdateParentalControlEnabled(
+ const bool& aEnabled) {
+ TRRService::Get()->mParentalControlEnabled = aEnabled;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TRRServiceChild::RecvClearDNSCache(
+ const bool& aTrrToo) {
+ Unused << sDNSService->ClearCache(aTrrToo);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TRRServiceChild::RecvSetDetectedTrrURI(
+ const nsCString& aURI) {
+ TRRService::Get()->SetDetectedTrrURI(aURI);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TRRServiceChild::RecvSetDefaultTRRConnectionInfo(
+ Maybe<HttpConnectionInfoCloneArgs>&& aArgs) {
+ if (!aArgs) {
+ TRRService::Get()->SetDefaultTRRConnectionInfo(nullptr);
+ return IPC_OK();
+ }
+
+ RefPtr<nsHttpConnectionInfo> cinfo =
+ nsHttpConnectionInfo::DeserializeHttpConnectionInfoCloneArgs(aArgs.ref());
+ TRRService::Get()->SetDefaultTRRConnectionInfo(cinfo);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TRRServiceChild::RecvUpdateEtcHosts(
+ nsTArray<nsCString>&& aHosts) {
+ TRRService::Get()->AddEtcHosts(aHosts);
+ return IPC_OK();
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/TRRServiceChild.h b/netwerk/dns/TRRServiceChild.h
new file mode 100644
index 0000000000..f9c6136f70
--- /dev/null
+++ b/netwerk/dns/TRRServiceChild.h
@@ -0,0 +1,53 @@
+/* -*- 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/. */
+
+#ifndef mozilla_net_TRRServiceChild_h
+#define mozilla_net_TRRServiceChild_h
+
+#include "mozilla/net/PTRRServiceChild.h"
+#include "nsIObserver.h"
+#include "nsWeakReference.h"
+
+namespace mozilla {
+
+namespace ipc {
+class FileDescriptor;
+} // namespace ipc
+
+namespace net {
+
+class TRRServiceChild : public PTRRServiceChild,
+ public nsIObserver,
+ public nsSupportsWeakReference {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ TRRServiceChild();
+ static TRRServiceChild* GetSingleton();
+
+ void Init(const bool& aCaptiveIsPassed, const bool& aParentalControlEnabled,
+ nsTArray<nsCString>&& aDNSSuffixList);
+ mozilla::ipc::IPCResult RecvNotifyObserver(const nsCString& aTopic,
+ const nsString& aData);
+ mozilla::ipc::IPCResult RecvUpdatePlatformDNSInformation(
+ nsTArray<nsCString>&& aDNSSuffixList);
+ mozilla::ipc::IPCResult RecvUpdateParentalControlEnabled(
+ const bool& aEnabled);
+ mozilla::ipc::IPCResult RecvClearDNSCache(const bool& aTrrToo);
+ mozilla::ipc::IPCResult RecvSetDetectedTrrURI(const nsCString& aURI);
+ mozilla::ipc::IPCResult RecvSetDefaultTRRConnectionInfo(
+ Maybe<HttpConnectionInfoCloneArgs>&& aArgs);
+ mozilla::ipc::IPCResult RecvUpdateEtcHosts(nsTArray<nsCString>&& aHosts);
+
+ private:
+ virtual ~TRRServiceChild();
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_TRRServiceChild_h
diff --git a/netwerk/dns/TRRServiceParent.cpp b/netwerk/dns/TRRServiceParent.cpp
new file mode 100644
index 0000000000..d5c072921b
--- /dev/null
+++ b/netwerk/dns/TRRServiceParent.cpp
@@ -0,0 +1,239 @@
+/* -*- 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 "mozilla/net/TRRServiceParent.h"
+
+#include "mozilla/ipc/FileDescriptor.h"
+#include "mozilla/net/SocketProcessParent.h"
+#include "mozilla/psm/PSMIPCTypes.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Unused.h"
+#include "nsHttpConnectionInfo.h"
+#include "nsICaptivePortalService.h"
+#include "nsIParentalControlsService.h"
+#include "nsINetworkLinkService.h"
+#include "nsIObserverService.h"
+#include "nsIOService.h"
+#include "nsNetCID.h"
+#include "TRRService.h"
+
+#include "DNSLogging.h"
+
+namespace mozilla {
+namespace net {
+
+static Atomic<TRRServiceParent*> sTRRServiceParentPtr;
+
+static const char* gTRRUriCallbackPrefs[] = {
+ "network.trr.uri", "network.trr.default_provider_uri",
+ "network.trr.mode", kRolloutURIPref,
+ kRolloutModePref, nullptr,
+};
+
+NS_IMPL_ISUPPORTS_INHERITED(TRRServiceParent, TRRServiceBase, nsIObserver,
+ nsISupportsWeakReference)
+
+TRRServiceParent::~TRRServiceParent() = default;
+
+void TRRServiceParent::Init() {
+ MOZ_ASSERT(gIOService);
+
+ if (!gIOService->SocketProcessReady()) {
+ RefPtr<TRRServiceParent> self = this;
+ gIOService->CallOrWaitForSocketProcess([self]() { self->Init(); });
+ return;
+ }
+
+ SocketProcessParent* socketParent = SocketProcessParent::GetSingleton();
+ if (!socketParent) {
+ return;
+ }
+
+ nsCOMPtr<nsIObserverService> obs =
+ static_cast<nsIObserverService*>(gIOService);
+ TRRService::AddObserver(this, obs);
+
+ bool captiveIsPassed = TRRService::CheckCaptivePortalIsPassed();
+ bool parentalControlEnabled = TRRService::GetParentalControlEnabledInternal();
+
+ nsCOMPtr<nsINetworkLinkService> nls =
+ do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID);
+ nsTArray<nsCString> suffixList;
+ if (nls) {
+ nls->GetDnsSuffixList(suffixList);
+ }
+
+ Preferences::RegisterPrefixCallbacks(TRRServiceParent::PrefsChanged,
+ gTRRUriCallbackPrefs, this);
+ prefsChanged(nullptr);
+
+ if (socketParent->SendPTRRServiceConstructor(
+ this, captiveIsPassed, parentalControlEnabled, suffixList)) {
+ sTRRServiceParentPtr = this;
+ }
+}
+
+NS_IMETHODIMP
+TRRServiceParent::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) {
+ if (!strcmp(aTopic, NS_DNS_SUFFIX_LIST_UPDATED_TOPIC) ||
+ !strcmp(aTopic, NS_NETWORK_LINK_TOPIC)) {
+ nsCOMPtr<nsINetworkLinkService> link = do_QueryInterface(aSubject);
+ // The network link service notification normally passes itself as the
+ // subject, but some unit tests will sometimes pass a null subject.
+ if (link) {
+ nsTArray<nsCString> suffixList;
+ link->GetDnsSuffixList(suffixList);
+ Unused << SendUpdatePlatformDNSInformation(suffixList);
+ }
+
+ if (!strcmp(aTopic, NS_NETWORK_LINK_TOPIC) && mURISetByDetection) {
+ CheckURIPrefs();
+ }
+ }
+
+ return NS_OK;
+}
+
+mozilla::ipc::IPCResult
+TRRServiceParent::RecvNotifyNetworkConnectivityServiceObservers(
+ const nsCString& aTopic) {
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->NotifyObservers(nullptr, aTopic.get(), nullptr);
+ }
+ return IPC_OK();
+}
+
+bool TRRServiceParent::MaybeSetPrivateURI(const nsACString& aURI) {
+ nsAutoCString newURI(aURI);
+ ProcessURITemplate(newURI);
+
+ if (mPrivateURI.Equals(newURI)) {
+ return false;
+ }
+
+ mPrivateURI = newURI;
+ AsyncCreateTRRConnectionInfo(mPrivateURI);
+
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->NotifyObservers(nullptr, NS_NETWORK_TRR_URI_CHANGED_TOPIC, nullptr);
+ }
+ return true;
+}
+
+void TRRServiceParent::SetDetectedTrrURI(const nsACString& aURI) {
+ if (!mURIPref.IsEmpty()) {
+ return;
+ }
+
+ mURISetByDetection = MaybeSetPrivateURI(aURI);
+ gIOService->CallOrWaitForSocketProcess(
+ [self = RefPtr{this}, uri = nsAutoCString(aURI)]() {
+ Unused << self->SendSetDetectedTrrURI(uri);
+ });
+}
+
+void TRRServiceParent::GetURI(nsACString& aURI) {
+ // We don't need a lock here, since mPrivateURI is only touched on main
+ // thread.
+ MOZ_ASSERT(NS_IsMainThread());
+ aURI = mPrivateURI;
+}
+
+void TRRServiceParent::UpdateParentalControlEnabled() {
+ bool enabled = TRRService::GetParentalControlEnabledInternal();
+ RefPtr<TRRServiceParent> self = this;
+ gIOService->CallOrWaitForSocketProcess([self, enabled]() {
+ Unused << self->SendUpdateParentalControlEnabled(enabled);
+ });
+}
+
+// static
+void TRRServiceParent::PrefsChanged(const char* aName, void* aSelf) {
+ static_cast<TRRServiceParent*>(aSelf)->prefsChanged(aName);
+}
+
+void TRRServiceParent::prefsChanged(const char* aName) {
+ if (!aName || !strcmp(aName, "network.trr.uri") ||
+ !strcmp(aName, "network.trr.default_provider_uri") ||
+ !strcmp(aName, kRolloutURIPref) ||
+ !strcmp(aName, "network.trr.ohttp.uri")) {
+ OnTRRURIChange();
+ }
+
+ if (!aName || !strcmp(aName, "network.trr.mode") ||
+ !strcmp(aName, kRolloutModePref)) {
+ OnTRRModeChange();
+ }
+}
+
+void TRRServiceParent::ActorDestroy(ActorDestroyReason why) {
+ sTRRServiceParentPtr = nullptr;
+ Preferences::UnregisterPrefixCallbacks(TRRServiceParent::PrefsChanged,
+ gTRRUriCallbackPrefs, this);
+}
+
+NS_IMETHODIMP TRRServiceParent::OnProxyConfigChanged() {
+ LOG(("TRRServiceParent::OnProxyConfigChanged"));
+
+ AsyncCreateTRRConnectionInfo(mPrivateURI);
+ return NS_OK;
+}
+
+void TRRServiceParent::SetDefaultTRRConnectionInfo(
+ nsHttpConnectionInfo* aConnInfo) {
+ TRRServiceBase::SetDefaultTRRConnectionInfo(aConnInfo);
+
+ if (!CanSend()) {
+ return;
+ }
+
+ if (!aConnInfo) {
+ Unused << SendSetDefaultTRRConnectionInfo(Nothing());
+ return;
+ }
+
+ HttpConnectionInfoCloneArgs infoArgs;
+ nsHttpConnectionInfo::SerializeHttpConnectionInfo(aConnInfo, infoArgs);
+ Unused << SendSetDefaultTRRConnectionInfo(Some(infoArgs));
+}
+
+mozilla::ipc::IPCResult TRRServiceParent::RecvInitTRRConnectionInfo() {
+ InitTRRConnectionInfo();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TRRServiceParent::RecvSetConfirmationState(
+ uint32_t aNewState) {
+ mConfirmationState = aNewState;
+ return IPC_OK();
+}
+
+void TRRServiceParent::ReadEtcHostsFile() {
+ if (!sTRRServiceParentPtr) {
+ return;
+ }
+
+ DoReadEtcHostsFile([](const nsTArray<nsCString>* aArray) -> bool {
+ RefPtr<TRRServiceParent> service(sTRRServiceParentPtr);
+ if (service && aArray) {
+ nsTArray<nsCString> hosts(aArray->Clone());
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "TRRServiceParent::ReadEtcHostsFile",
+ [service, hosts = std::move(hosts)]() mutable {
+ if (service->CanSend()) {
+ Unused << service->SendUpdateEtcHosts(hosts);
+ }
+ }));
+ }
+ return !!service;
+ });
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/TRRServiceParent.h b/netwerk/dns/TRRServiceParent.h
new file mode 100644
index 0000000000..c61310c029
--- /dev/null
+++ b/netwerk/dns/TRRServiceParent.h
@@ -0,0 +1,52 @@
+/* -*- 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/. */
+
+#ifndef mozilla_net_TRRServiceParent_h
+#define mozilla_net_TRRServiceParent_h
+
+#include "mozilla/net/PTRRServiceParent.h"
+#include "mozilla/net/TRRServiceBase.h"
+#include "nsIObserver.h"
+#include "nsWeakReference.h"
+
+namespace mozilla {
+namespace net {
+
+class TRRServiceParent : public TRRServiceBase,
+ public nsIObserver,
+ public nsSupportsWeakReference,
+ public PTRRServiceParent {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIOBSERVER
+ NS_DECL_NSIPROXYCONFIGCHANGEDCALLBACK
+
+ TRRServiceParent() = default;
+ void Init();
+ void UpdateParentalControlEnabled();
+ static void PrefsChanged(const char* aName, void* aSelf);
+ void SetDetectedTrrURI(const nsACString& aURI);
+ bool MaybeSetPrivateURI(const nsACString& aURI) override;
+ void GetURI(nsACString& result) override;
+ mozilla::ipc::IPCResult RecvNotifyNetworkConnectivityServiceObservers(
+ const nsCString& aTopic);
+ mozilla::ipc::IPCResult RecvInitTRRConnectionInfo();
+ mozilla::ipc::IPCResult RecvSetConfirmationState(uint32_t aNewState);
+ uint32_t GetConfirmationState() { return mConfirmationState; }
+ virtual void ReadEtcHostsFile() override;
+
+ private:
+ virtual ~TRRServiceParent();
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+ void prefsChanged(const char* aName);
+ void SetDefaultTRRConnectionInfo(nsHttpConnectionInfo* aConnInfo) override;
+ uint32_t mConfirmationState = 0;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_TRRServiceParent_h
diff --git a/netwerk/dns/effective_tld_names.dat b/netwerk/dns/effective_tld_names.dat
new file mode 100644
index 0000000000..c2b813fa68
--- /dev/null
+++ b/netwerk/dns/effective_tld_names.dat
@@ -0,0 +1,15582 @@
+// 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 https://mozilla.org/MPL/2.0/.
+
+// Please pull this list from, and only from https://publicsuffix.org/list/public_suffix_list.dat,
+// rather than any other VCS sites. Pulling from any other URL is not guaranteed to be supported.
+
+// Instructions on pulling and using this list can be found at https://publicsuffix.org/list/.
+
+// ===BEGIN ICANN DOMAINS===
+
+// ac : http://nic.ac/rules.htm
+ac
+com.ac
+edu.ac
+gov.ac
+net.ac
+mil.ac
+org.ac
+
+// ad : https://en.wikipedia.org/wiki/.ad
+ad
+nom.ad
+
+// ae : https://tdra.gov.ae/en/aeda/ae-policies
+ae
+co.ae
+net.ae
+org.ae
+sch.ae
+ac.ae
+gov.ae
+mil.ae
+
+// aero : see https://www.information.aero/index.php?id=66
+aero
+accident-investigation.aero
+accident-prevention.aero
+aerobatic.aero
+aeroclub.aero
+aerodrome.aero
+agents.aero
+aircraft.aero
+airline.aero
+airport.aero
+air-surveillance.aero
+airtraffic.aero
+air-traffic-control.aero
+ambulance.aero
+amusement.aero
+association.aero
+author.aero
+ballooning.aero
+broker.aero
+caa.aero
+cargo.aero
+catering.aero
+certification.aero
+championship.aero
+charter.aero
+civilaviation.aero
+club.aero
+conference.aero
+consultant.aero
+consulting.aero
+control.aero
+council.aero
+crew.aero
+design.aero
+dgca.aero
+educator.aero
+emergency.aero
+engine.aero
+engineer.aero
+entertainment.aero
+equipment.aero
+exchange.aero
+express.aero
+federation.aero
+flight.aero
+fuel.aero
+gliding.aero
+government.aero
+groundhandling.aero
+group.aero
+hanggliding.aero
+homebuilt.aero
+insurance.aero
+journal.aero
+journalist.aero
+leasing.aero
+logistics.aero
+magazine.aero
+maintenance.aero
+media.aero
+microlight.aero
+modelling.aero
+navigation.aero
+parachuting.aero
+paragliding.aero
+passenger-association.aero
+pilot.aero
+press.aero
+production.aero
+recreation.aero
+repbody.aero
+res.aero
+research.aero
+rotorcraft.aero
+safety.aero
+scientist.aero
+services.aero
+show.aero
+skydiving.aero
+software.aero
+student.aero
+trader.aero
+trading.aero
+trainer.aero
+union.aero
+workinggroup.aero
+works.aero
+
+// af : http://www.nic.af/help.jsp
+af
+gov.af
+com.af
+org.af
+net.af
+edu.af
+
+// ag : http://www.nic.ag/prices.htm
+ag
+com.ag
+org.ag
+net.ag
+co.ag
+nom.ag
+
+// ai : http://nic.com.ai/
+ai
+off.ai
+com.ai
+net.ai
+org.ai
+
+// al : http://www.ert.gov.al/ert_alb/faq_det.html?Id=31
+al
+com.al
+edu.al
+gov.al
+mil.al
+net.al
+org.al
+
+// am : https://www.amnic.net/policy/en/Policy_EN.pdf
+am
+co.am
+com.am
+commune.am
+net.am
+org.am
+
+// ao : https://en.wikipedia.org/wiki/.ao
+// http://www.dns.ao/REGISTR.DOC
+ao
+ed.ao
+gv.ao
+og.ao
+co.ao
+pb.ao
+it.ao
+
+// aq : https://en.wikipedia.org/wiki/.aq
+aq
+
+// ar : https://nic.ar/es/nic-argentina/normativa
+ar
+bet.ar
+com.ar
+coop.ar
+edu.ar
+gob.ar
+gov.ar
+int.ar
+mil.ar
+musica.ar
+mutual.ar
+net.ar
+org.ar
+senasa.ar
+tur.ar
+
+// arpa : https://en.wikipedia.org/wiki/.arpa
+// Confirmed by registry <iana-questions@icann.org> 2008-06-18
+arpa
+e164.arpa
+in-addr.arpa
+ip6.arpa
+iris.arpa
+uri.arpa
+urn.arpa
+
+// as : https://en.wikipedia.org/wiki/.as
+as
+gov.as
+
+// asia : https://en.wikipedia.org/wiki/.asia
+asia
+
+// at : https://en.wikipedia.org/wiki/.at
+// Confirmed by registry <it@nic.at> 2008-06-17
+at
+ac.at
+co.at
+gv.at
+or.at
+sth.ac.at
+
+// au : https://en.wikipedia.org/wiki/.au
+// http://www.auda.org.au/
+au
+// 2LDs
+com.au
+net.au
+org.au
+edu.au
+gov.au
+asn.au
+id.au
+// Historic 2LDs (closed to new registration, but sites still exist)
+info.au
+conf.au
+oz.au
+// CGDNs - http://www.cgdn.org.au/
+act.au
+nsw.au
+nt.au
+qld.au
+sa.au
+tas.au
+vic.au
+wa.au
+// 3LDs
+act.edu.au
+catholic.edu.au
+// eq.edu.au - Removed at the request of the Queensland Department of Education
+nsw.edu.au
+nt.edu.au
+qld.edu.au
+sa.edu.au
+tas.edu.au
+vic.edu.au
+wa.edu.au
+// act.gov.au Bug 984824 - Removed at request of Greg Tankard
+// nsw.gov.au Bug 547985 - Removed at request of <Shae.Donelan@services.nsw.gov.au>
+// nt.gov.au Bug 940478 - Removed at request of Greg Connors <Greg.Connors@nt.gov.au>
+qld.gov.au
+sa.gov.au
+tas.gov.au
+vic.gov.au
+wa.gov.au
+// 4LDs
+// education.tas.edu.au - Removed at the request of the Department of Education Tasmania
+schools.nsw.edu.au
+
+// aw : https://en.wikipedia.org/wiki/.aw
+aw
+com.aw
+
+// ax : https://en.wikipedia.org/wiki/.ax
+ax
+
+// az : https://en.wikipedia.org/wiki/.az
+az
+com.az
+net.az
+int.az
+gov.az
+org.az
+edu.az
+info.az
+pp.az
+mil.az
+name.az
+pro.az
+biz.az
+
+// ba : http://nic.ba/users_data/files/pravilnik_o_registraciji.pdf
+ba
+com.ba
+edu.ba
+gov.ba
+mil.ba
+net.ba
+org.ba
+
+// bb : https://en.wikipedia.org/wiki/.bb
+bb
+biz.bb
+co.bb
+com.bb
+edu.bb
+gov.bb
+info.bb
+net.bb
+org.bb
+store.bb
+tv.bb
+
+// bd : https://en.wikipedia.org/wiki/.bd
+*.bd
+
+// be : https://en.wikipedia.org/wiki/.be
+// Confirmed by registry <tech@dns.be> 2008-06-08
+be
+ac.be
+
+// bf : https://en.wikipedia.org/wiki/.bf
+bf
+gov.bf
+
+// bg : https://en.wikipedia.org/wiki/.bg
+// https://www.register.bg/user/static/rules/en/index.html
+bg
+a.bg
+b.bg
+c.bg
+d.bg
+e.bg
+f.bg
+g.bg
+h.bg
+i.bg
+j.bg
+k.bg
+l.bg
+m.bg
+n.bg
+o.bg
+p.bg
+q.bg
+r.bg
+s.bg
+t.bg
+u.bg
+v.bg
+w.bg
+x.bg
+y.bg
+z.bg
+0.bg
+1.bg
+2.bg
+3.bg
+4.bg
+5.bg
+6.bg
+7.bg
+8.bg
+9.bg
+
+// bh : https://en.wikipedia.org/wiki/.bh
+bh
+com.bh
+edu.bh
+net.bh
+org.bh
+gov.bh
+
+// bi : https://en.wikipedia.org/wiki/.bi
+// http://whois.nic.bi/
+bi
+co.bi
+com.bi
+edu.bi
+or.bi
+org.bi
+
+// biz : https://en.wikipedia.org/wiki/.biz
+biz
+
+// bj : https://nic.bj/bj-suffixes.txt
+// submitted by registry <contact@nic.bj>
+bj
+africa.bj
+agro.bj
+architectes.bj
+assur.bj
+avocats.bj
+co.bj
+com.bj
+eco.bj
+econo.bj
+edu.bj
+info.bj
+loisirs.bj
+money.bj
+net.bj
+org.bj
+ote.bj
+resto.bj
+restaurant.bj
+tourism.bj
+univ.bj
+
+// bm : http://www.bermudanic.bm/dnr-text.txt
+bm
+com.bm
+edu.bm
+gov.bm
+net.bm
+org.bm
+
+// bn : http://www.bnnic.bn/faqs
+bn
+com.bn
+edu.bn
+gov.bn
+net.bn
+org.bn
+
+// bo : https://nic.bo/delegacion2015.php#h-1.10
+bo
+com.bo
+edu.bo
+gob.bo
+int.bo
+org.bo
+net.bo
+mil.bo
+tv.bo
+web.bo
+// Social Domains
+academia.bo
+agro.bo
+arte.bo
+blog.bo
+bolivia.bo
+ciencia.bo
+cooperativa.bo
+democracia.bo
+deporte.bo
+ecologia.bo
+economia.bo
+empresa.bo
+indigena.bo
+industria.bo
+info.bo
+medicina.bo
+movimiento.bo
+musica.bo
+natural.bo
+nombre.bo
+noticias.bo
+patria.bo
+politica.bo
+profesional.bo
+plurinacional.bo
+pueblo.bo
+revista.bo
+salud.bo
+tecnologia.bo
+tksat.bo
+transporte.bo
+wiki.bo
+
+// br : http://registro.br/dominio/categoria.html
+// Submitted by registry <fneves@registro.br>
+br
+9guacu.br
+abc.br
+adm.br
+adv.br
+agr.br
+aju.br
+am.br
+anani.br
+aparecida.br
+app.br
+arq.br
+art.br
+ato.br
+b.br
+barueri.br
+belem.br
+bhz.br
+bib.br
+bio.br
+blog.br
+bmd.br
+boavista.br
+bsb.br
+campinagrande.br
+campinas.br
+caxias.br
+cim.br
+cng.br
+cnt.br
+com.br
+contagem.br
+coop.br
+coz.br
+cri.br
+cuiaba.br
+curitiba.br
+def.br
+des.br
+det.br
+dev.br
+ecn.br
+eco.br
+edu.br
+emp.br
+enf.br
+eng.br
+esp.br
+etc.br
+eti.br
+far.br
+feira.br
+flog.br
+floripa.br
+fm.br
+fnd.br
+fortal.br
+fot.br
+foz.br
+fst.br
+g12.br
+geo.br
+ggf.br
+goiania.br
+gov.br
+// gov.br 26 states + df https://en.wikipedia.org/wiki/States_of_Brazil
+ac.gov.br
+al.gov.br
+am.gov.br
+ap.gov.br
+ba.gov.br
+ce.gov.br
+df.gov.br
+es.gov.br
+go.gov.br
+ma.gov.br
+mg.gov.br
+ms.gov.br
+mt.gov.br
+pa.gov.br
+pb.gov.br
+pe.gov.br
+pi.gov.br
+pr.gov.br
+rj.gov.br
+rn.gov.br
+ro.gov.br
+rr.gov.br
+rs.gov.br
+sc.gov.br
+se.gov.br
+sp.gov.br
+to.gov.br
+gru.br
+imb.br
+ind.br
+inf.br
+jab.br
+jampa.br
+jdf.br
+joinville.br
+jor.br
+jus.br
+leg.br
+lel.br
+log.br
+londrina.br
+macapa.br
+maceio.br
+manaus.br
+maringa.br
+mat.br
+med.br
+mil.br
+morena.br
+mp.br
+mus.br
+natal.br
+net.br
+niteroi.br
+*.nom.br
+not.br
+ntr.br
+odo.br
+ong.br
+org.br
+osasco.br
+palmas.br
+poa.br
+ppg.br
+pro.br
+psc.br
+psi.br
+pvh.br
+qsl.br
+radio.br
+rec.br
+recife.br
+rep.br
+ribeirao.br
+rio.br
+riobranco.br
+riopreto.br
+salvador.br
+sampa.br
+santamaria.br
+santoandre.br
+saobernardo.br
+saogonca.br
+seg.br
+sjc.br
+slg.br
+slz.br
+sorocaba.br
+srv.br
+taxi.br
+tc.br
+tec.br
+teo.br
+the.br
+tmp.br
+trd.br
+tur.br
+tv.br
+udi.br
+vet.br
+vix.br
+vlog.br
+wiki.br
+zlg.br
+
+// bs : http://www.nic.bs/rules.html
+bs
+com.bs
+net.bs
+org.bs
+edu.bs
+gov.bs
+
+// bt : https://en.wikipedia.org/wiki/.bt
+bt
+com.bt
+edu.bt
+gov.bt
+net.bt
+org.bt
+
+// bv : No registrations at this time.
+// Submitted by registry <jarle@uninett.no>
+bv
+
+// bw : https://en.wikipedia.org/wiki/.bw
+// http://www.gobin.info/domainname/bw.doc
+// list of other 2nd level tlds ?
+bw
+co.bw
+org.bw
+
+// by : https://en.wikipedia.org/wiki/.by
+// http://tld.by/rules_2006_en.html
+// list of other 2nd level tlds ?
+by
+gov.by
+mil.by
+// Official information does not indicate that com.by is a reserved
+// second-level domain, but it's being used as one (see www.google.com.by and
+// www.yahoo.com.by, for example), so we list it here for safety's sake.
+com.by
+
+// http://hoster.by/
+of.by
+
+// bz : https://en.wikipedia.org/wiki/.bz
+// http://www.belizenic.bz/
+bz
+com.bz
+net.bz
+org.bz
+edu.bz
+gov.bz
+
+// ca : https://en.wikipedia.org/wiki/.ca
+ca
+// ca geographical names
+ab.ca
+bc.ca
+mb.ca
+nb.ca
+nf.ca
+nl.ca
+ns.ca
+nt.ca
+nu.ca
+on.ca
+pe.ca
+qc.ca
+sk.ca
+yk.ca
+// gc.ca: https://en.wikipedia.org/wiki/.gc.ca
+// see also: http://registry.gc.ca/en/SubdomainFAQ
+gc.ca
+
+// cat : https://en.wikipedia.org/wiki/.cat
+cat
+
+// cc : https://en.wikipedia.org/wiki/.cc
+cc
+
+// cd : https://en.wikipedia.org/wiki/.cd
+// see also: https://www.nic.cd/domain/insertDomain_2.jsp?act=1
+cd
+gov.cd
+
+// cf : https://en.wikipedia.org/wiki/.cf
+cf
+
+// cg : https://en.wikipedia.org/wiki/.cg
+cg
+
+// ch : https://en.wikipedia.org/wiki/.ch
+ch
+
+// ci : https://en.wikipedia.org/wiki/.ci
+// http://www.nic.ci/index.php?page=charte
+ci
+org.ci
+or.ci
+com.ci
+co.ci
+edu.ci
+ed.ci
+ac.ci
+net.ci
+go.ci
+asso.ci
+aéroport.ci
+int.ci
+presse.ci
+md.ci
+gouv.ci
+
+// ck : https://en.wikipedia.org/wiki/.ck
+*.ck
+!www.ck
+
+// cl : https://www.nic.cl
+// Confirmed by .CL registry <hsalgado@nic.cl>
+cl
+co.cl
+gob.cl
+gov.cl
+mil.cl
+
+// cm : https://en.wikipedia.org/wiki/.cm plus bug 981927
+cm
+co.cm
+com.cm
+gov.cm
+net.cm
+
+// cn : https://en.wikipedia.org/wiki/.cn
+// Submitted by registry <tanyaling@cnnic.cn>
+cn
+ac.cn
+com.cn
+edu.cn
+gov.cn
+net.cn
+org.cn
+mil.cn
+公司.cn
+网络.cn
+網絡.cn
+// cn geographic names
+ah.cn
+bj.cn
+cq.cn
+fj.cn
+gd.cn
+gs.cn
+gz.cn
+gx.cn
+ha.cn
+hb.cn
+he.cn
+hi.cn
+hl.cn
+hn.cn
+jl.cn
+js.cn
+jx.cn
+ln.cn
+nm.cn
+nx.cn
+qh.cn
+sc.cn
+sd.cn
+sh.cn
+sn.cn
+sx.cn
+tj.cn
+xj.cn
+xz.cn
+yn.cn
+zj.cn
+hk.cn
+mo.cn
+tw.cn
+
+// co : https://en.wikipedia.org/wiki/.co
+// Submitted by registry <tecnico@uniandes.edu.co>
+co
+arts.co
+com.co
+edu.co
+firm.co
+gov.co
+info.co
+int.co
+mil.co
+net.co
+nom.co
+org.co
+rec.co
+web.co
+
+// com : https://en.wikipedia.org/wiki/.com
+com
+
+// coop : https://en.wikipedia.org/wiki/.coop
+coop
+
+// cr : http://www.nic.cr/niccr_publico/showRegistroDominiosScreen.do
+cr
+ac.cr
+co.cr
+ed.cr
+fi.cr
+go.cr
+or.cr
+sa.cr
+
+// cu : https://en.wikipedia.org/wiki/.cu
+cu
+com.cu
+edu.cu
+org.cu
+net.cu
+gov.cu
+inf.cu
+
+// cv : https://en.wikipedia.org/wiki/.cv
+// cv : http://www.dns.cv/tldcv_portal/do?com=DS;5446457100;111;+PAGE(4000018)+K-CAT-CODIGO(RDOM)+RCNT(100); <- registration rules
+cv
+com.cv
+edu.cv
+int.cv
+nome.cv
+org.cv
+
+// cw : http://www.una.cw/cw_registry/
+// Confirmed by registry <registry@una.net> 2013-03-26
+cw
+com.cw
+edu.cw
+net.cw
+org.cw
+
+// cx : https://en.wikipedia.org/wiki/.cx
+// list of other 2nd level tlds ?
+cx
+gov.cx
+
+// cy : http://www.nic.cy/
+// Submitted by registry Panayiotou Fotia <cydns@ucy.ac.cy>
+// namespace policies URL https://www.nic.cy/portal//sites/default/files/symfonia_gia_eggrafi.pdf
+cy
+ac.cy
+biz.cy
+com.cy
+ekloges.cy
+gov.cy
+ltd.cy
+mil.cy
+net.cy
+org.cy
+press.cy
+pro.cy
+tm.cy
+
+// cz : https://en.wikipedia.org/wiki/.cz
+cz
+
+// de : https://en.wikipedia.org/wiki/.de
+// Confirmed by registry <ops@denic.de> (with technical
+// reservations) 2008-07-01
+de
+
+// dj : https://en.wikipedia.org/wiki/.dj
+dj
+
+// dk : https://en.wikipedia.org/wiki/.dk
+// Confirmed by registry <robert@dk-hostmaster.dk> 2008-06-17
+dk
+
+// dm : https://en.wikipedia.org/wiki/.dm
+dm
+com.dm
+net.dm
+org.dm
+edu.dm
+gov.dm
+
+// do : https://en.wikipedia.org/wiki/.do
+do
+art.do
+com.do
+edu.do
+gob.do
+gov.do
+mil.do
+net.do
+org.do
+sld.do
+web.do
+
+// dz : http://www.nic.dz/images/pdf_nic/charte.pdf
+dz
+art.dz
+asso.dz
+com.dz
+edu.dz
+gov.dz
+org.dz
+net.dz
+pol.dz
+soc.dz
+tm.dz
+
+// ec : http://www.nic.ec/reg/paso1.asp
+// Submitted by registry <vabboud@nic.ec>
+ec
+com.ec
+info.ec
+net.ec
+fin.ec
+k12.ec
+med.ec
+pro.ec
+org.ec
+edu.ec
+gov.ec
+gob.ec
+mil.ec
+
+// edu : https://en.wikipedia.org/wiki/.edu
+edu
+
+// ee : http://www.eenet.ee/EENet/dom_reeglid.html#lisa_B
+ee
+edu.ee
+gov.ee
+riik.ee
+lib.ee
+med.ee
+com.ee
+pri.ee
+aip.ee
+org.ee
+fie.ee
+
+// eg : https://en.wikipedia.org/wiki/.eg
+eg
+com.eg
+edu.eg
+eun.eg
+gov.eg
+mil.eg
+name.eg
+net.eg
+org.eg
+sci.eg
+
+// er : https://en.wikipedia.org/wiki/.er
+*.er
+
+// es : https://www.nic.es/site_ingles/ingles/dominios/index.html
+es
+com.es
+nom.es
+org.es
+gob.es
+edu.es
+
+// et : https://en.wikipedia.org/wiki/.et
+et
+com.et
+gov.et
+org.et
+edu.et
+biz.et
+name.et
+info.et
+net.et
+
+// eu : https://en.wikipedia.org/wiki/.eu
+eu
+
+// fi : https://en.wikipedia.org/wiki/.fi
+fi
+// aland.fi : https://en.wikipedia.org/wiki/.ax
+// This domain is being phased out in favor of .ax. As there are still many
+// domains under aland.fi, we still keep it on the list until aland.fi is
+// completely removed.
+// TODO: Check for updates (expected to be phased out around Q1/2009)
+aland.fi
+
+// fj : http://domains.fj/
+// Submitted by registry <garth.miller@cocca.org.nz> 2020-02-11
+fj
+ac.fj
+biz.fj
+com.fj
+gov.fj
+info.fj
+mil.fj
+name.fj
+net.fj
+org.fj
+pro.fj
+
+// fk : https://en.wikipedia.org/wiki/.fk
+*.fk
+
+// fm : https://en.wikipedia.org/wiki/.fm
+com.fm
+edu.fm
+net.fm
+org.fm
+fm
+
+// fo : https://en.wikipedia.org/wiki/.fo
+fo
+
+// fr : https://www.afnic.fr/ https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf
+fr
+asso.fr
+com.fr
+gouv.fr
+nom.fr
+prd.fr
+tm.fr
+// Other SLDs now selfmanaged out of AFNIC range. Former "domaines sectoriels", still registration suffixes
+avoues.fr
+cci.fr
+greta.fr
+huissier-justice.fr
+
+// ga : https://en.wikipedia.org/wiki/.ga
+ga
+
+// gb : This registry is effectively dormant
+// Submitted by registry <Damien.Shaw@ja.net>
+gb
+
+// gd : https://en.wikipedia.org/wiki/.gd
+edu.gd
+gov.gd
+gd
+
+// ge : http://www.nic.net.ge/policy_en.pdf
+ge
+com.ge
+edu.ge
+gov.ge
+org.ge
+mil.ge
+net.ge
+pvt.ge
+
+// gf : https://en.wikipedia.org/wiki/.gf
+gf
+
+// gg : http://www.channelisles.net/register-domains/
+// Confirmed by registry <nigel@channelisles.net> 2013-11-28
+gg
+co.gg
+net.gg
+org.gg
+
+// gh : https://en.wikipedia.org/wiki/.gh
+// see also: http://www.nic.gh/reg_now.php
+// Although domains directly at second level are not possible at the moment,
+// they have been possible for some time and may come back.
+gh
+com.gh
+edu.gh
+gov.gh
+org.gh
+mil.gh
+
+// gi : http://www.nic.gi/rules.html
+gi
+com.gi
+ltd.gi
+gov.gi
+mod.gi
+edu.gi
+org.gi
+
+// gl : https://en.wikipedia.org/wiki/.gl
+// http://nic.gl
+gl
+co.gl
+com.gl
+edu.gl
+net.gl
+org.gl
+
+// gm : http://www.nic.gm/htmlpages%5Cgm-policy.htm
+gm
+
+// gn : http://psg.com/dns/gn/gn.txt
+// Submitted by registry <randy@psg.com>
+gn
+ac.gn
+com.gn
+edu.gn
+gov.gn
+org.gn
+net.gn
+
+// gov : https://en.wikipedia.org/wiki/.gov
+gov
+
+// gp : http://www.nic.gp/index.php?lang=en
+gp
+com.gp
+net.gp
+mobi.gp
+edu.gp
+org.gp
+asso.gp
+
+// gq : https://en.wikipedia.org/wiki/.gq
+gq
+
+// gr : https://grweb.ics.forth.gr/english/1617-B-2005.html
+// Submitted by registry <segred@ics.forth.gr>
+gr
+com.gr
+edu.gr
+net.gr
+org.gr
+gov.gr
+
+// gs : https://en.wikipedia.org/wiki/.gs
+gs
+
+// gt : https://www.gt/sitio/registration_policy.php?lang=en
+gt
+com.gt
+edu.gt
+gob.gt
+ind.gt
+mil.gt
+net.gt
+org.gt
+
+// gu : http://gadao.gov.gu/register.html
+// University of Guam : https://www.uog.edu
+// Submitted by uognoc@triton.uog.edu
+gu
+com.gu
+edu.gu
+gov.gu
+guam.gu
+info.gu
+net.gu
+org.gu
+web.gu
+
+// gw : https://en.wikipedia.org/wiki/.gw
+// gw : https://nic.gw/regras/
+gw
+
+// gy : https://en.wikipedia.org/wiki/.gy
+// http://registry.gy/
+gy
+co.gy
+com.gy
+edu.gy
+gov.gy
+net.gy
+org.gy
+
+// hk : https://www.hkirc.hk
+// Submitted by registry <hk.tech@hkirc.hk>
+hk
+com.hk
+edu.hk
+gov.hk
+idv.hk
+net.hk
+org.hk
+公司.hk
+教育.hk
+敎育.hk
+政府.hk
+個人.hk
+个人.hk
+箇人.hk
+網络.hk
+网络.hk
+组織.hk
+網絡.hk
+网絡.hk
+组织.hk
+組織.hk
+組织.hk
+
+// hm : https://en.wikipedia.org/wiki/.hm
+hm
+
+// hn : http://www.nic.hn/politicas/ps02,,05.html
+hn
+com.hn
+edu.hn
+org.hn
+net.hn
+mil.hn
+gob.hn
+
+// hr : http://www.dns.hr/documents/pdf/HRTLD-regulations.pdf
+hr
+iz.hr
+from.hr
+name.hr
+com.hr
+
+// ht : http://www.nic.ht/info/charte.cfm
+ht
+com.ht
+shop.ht
+firm.ht
+info.ht
+adult.ht
+net.ht
+pro.ht
+org.ht
+med.ht
+art.ht
+coop.ht
+pol.ht
+asso.ht
+edu.ht
+rel.ht
+gouv.ht
+perso.ht
+
+// hu : http://www.domain.hu/domain/English/sld.html
+// Confirmed by registry <pasztor@iszt.hu> 2008-06-12
+hu
+co.hu
+info.hu
+org.hu
+priv.hu
+sport.hu
+tm.hu
+2000.hu
+agrar.hu
+bolt.hu
+casino.hu
+city.hu
+erotica.hu
+erotika.hu
+film.hu
+forum.hu
+games.hu
+hotel.hu
+ingatlan.hu
+jogasz.hu
+konyvelo.hu
+lakas.hu
+media.hu
+news.hu
+reklam.hu
+sex.hu
+shop.hu
+suli.hu
+szex.hu
+tozsde.hu
+utazas.hu
+video.hu
+
+// id : https://pandi.id/en/domain/registration-requirements/
+id
+ac.id
+biz.id
+co.id
+desa.id
+go.id
+mil.id
+my.id
+net.id
+or.id
+ponpes.id
+sch.id
+web.id
+
+// ie : https://en.wikipedia.org/wiki/.ie
+ie
+gov.ie
+
+// il : http://www.isoc.org.il/domains/
+// see also: https://en.isoc.org.il/il-cctld/registration-rules
+// ISOC-IL (operated by .il Registry)
+il
+ac.il
+co.il
+gov.il
+idf.il
+k12.il
+muni.il
+net.il
+org.il
+// xn--4dbrk0ce ("Israel", Hebrew) : IL
+ישראל
+// xn--4dbgdty6c.xn--4dbrk0ce.
+אקדמיה.ישראל
+// xn--5dbhl8d.xn--4dbrk0ce.
+ישוב.ישראל
+// xn--8dbq2a.xn--4dbrk0ce.
+צהל.ישראל
+// xn--hebda8b.xn--4dbrk0ce.
+ממשל.ישראל
+
+// im : https://www.nic.im/
+// Submitted by registry <info@nic.im>
+im
+ac.im
+co.im
+com.im
+ltd.co.im
+net.im
+org.im
+plc.co.im
+tt.im
+tv.im
+
+// in : https://en.wikipedia.org/wiki/.in
+// see also: https://registry.in/policies
+// Please note, that nic.in is not an official eTLD, but used by most
+// government institutions.
+in
+5g.in
+6g.in
+ac.in
+ai.in
+am.in
+bihar.in
+biz.in
+business.in
+ca.in
+cn.in
+co.in
+com.in
+coop.in
+cs.in
+delhi.in
+dr.in
+edu.in
+er.in
+firm.in
+gen.in
+gov.in
+gujarat.in
+ind.in
+info.in
+int.in
+internet.in
+io.in
+me.in
+mil.in
+net.in
+nic.in
+org.in
+pg.in
+post.in
+pro.in
+res.in
+travel.in
+tv.in
+uk.in
+up.in
+us.in
+
+// info : https://en.wikipedia.org/wiki/.info
+info
+
+// int : https://en.wikipedia.org/wiki/.int
+// Confirmed by registry <iana-questions@icann.org> 2008-06-18
+int
+eu.int
+
+// io : http://www.nic.io/rules.htm
+// list of other 2nd level tlds ?
+io
+com.io
+
+// iq : http://www.cmc.iq/english/iq/iqregister1.htm
+iq
+gov.iq
+edu.iq
+mil.iq
+com.iq
+org.iq
+net.iq
+
+// ir : http://www.nic.ir/Terms_and_Conditions_ir,_Appendix_1_Domain_Rules
+// Also see http://www.nic.ir/Internationalized_Domain_Names
+// Two <iran>.ir entries added at request of <tech-team@nic.ir>, 2010-04-16
+ir
+ac.ir
+co.ir
+gov.ir
+id.ir
+net.ir
+org.ir
+sch.ir
+// xn--mgba3a4f16a.ir (<iran>.ir, Persian YEH)
+ایران.ir
+// xn--mgba3a4fra.ir (<iran>.ir, Arabic YEH)
+ايران.ir
+
+// is : http://www.isnic.is/domain/rules.php
+// Confirmed by registry <marius@isgate.is> 2008-12-06
+is
+net.is
+com.is
+edu.is
+gov.is
+org.is
+int.is
+
+// it : https://en.wikipedia.org/wiki/.it
+it
+gov.it
+edu.it
+// Reserved geo-names (regions and provinces):
+// https://www.nic.it/sites/default/files/archivio/docs/Regulation_assignation_v7.1.pdf
+// Regions
+abr.it
+abruzzo.it
+aosta-valley.it
+aostavalley.it
+bas.it
+basilicata.it
+cal.it
+calabria.it
+cam.it
+campania.it
+emilia-romagna.it
+emiliaromagna.it
+emr.it
+friuli-v-giulia.it
+friuli-ve-giulia.it
+friuli-vegiulia.it
+friuli-venezia-giulia.it
+friuli-veneziagiulia.it
+friuli-vgiulia.it
+friuliv-giulia.it
+friulive-giulia.it
+friulivegiulia.it
+friulivenezia-giulia.it
+friuliveneziagiulia.it
+friulivgiulia.it
+fvg.it
+laz.it
+lazio.it
+lig.it
+liguria.it
+lom.it
+lombardia.it
+lombardy.it
+lucania.it
+mar.it
+marche.it
+mol.it
+molise.it
+piedmont.it
+piemonte.it
+pmn.it
+pug.it
+puglia.it
+sar.it
+sardegna.it
+sardinia.it
+sic.it
+sicilia.it
+sicily.it
+taa.it
+tos.it
+toscana.it
+trentin-sud-tirol.it
+trentin-süd-tirol.it
+trentin-sudtirol.it
+trentin-südtirol.it
+trentin-sued-tirol.it
+trentin-suedtirol.it
+trentino-a-adige.it
+trentino-aadige.it
+trentino-alto-adige.it
+trentino-altoadige.it
+trentino-s-tirol.it
+trentino-stirol.it
+trentino-sud-tirol.it
+trentino-süd-tirol.it
+trentino-sudtirol.it
+trentino-südtirol.it
+trentino-sued-tirol.it
+trentino-suedtirol.it
+trentino.it
+trentinoa-adige.it
+trentinoaadige.it
+trentinoalto-adige.it
+trentinoaltoadige.it
+trentinos-tirol.it
+trentinostirol.it
+trentinosud-tirol.it
+trentinosüd-tirol.it
+trentinosudtirol.it
+trentinosüdtirol.it
+trentinosued-tirol.it
+trentinosuedtirol.it
+trentinsud-tirol.it
+trentinsüd-tirol.it
+trentinsudtirol.it
+trentinsüdtirol.it
+trentinsued-tirol.it
+trentinsuedtirol.it
+tuscany.it
+umb.it
+umbria.it
+val-d-aosta.it
+val-daosta.it
+vald-aosta.it
+valdaosta.it
+valle-aosta.it
+valle-d-aosta.it
+valle-daosta.it
+valleaosta.it
+valled-aosta.it
+valledaosta.it
+vallee-aoste.it
+vallée-aoste.it
+vallee-d-aoste.it
+vallée-d-aoste.it
+valleeaoste.it
+valléeaoste.it
+valleedaoste.it
+valléedaoste.it
+vao.it
+vda.it
+ven.it
+veneto.it
+// Provinces
+ag.it
+agrigento.it
+al.it
+alessandria.it
+alto-adige.it
+altoadige.it
+an.it
+ancona.it
+andria-barletta-trani.it
+andria-trani-barletta.it
+andriabarlettatrani.it
+andriatranibarletta.it
+ao.it
+aosta.it
+aoste.it
+ap.it
+aq.it
+aquila.it
+ar.it
+arezzo.it
+ascoli-piceno.it
+ascolipiceno.it
+asti.it
+at.it
+av.it
+avellino.it
+ba.it
+balsan-sudtirol.it
+balsan-südtirol.it
+balsan-suedtirol.it
+balsan.it
+bari.it
+barletta-trani-andria.it
+barlettatraniandria.it
+belluno.it
+benevento.it
+bergamo.it
+bg.it
+bi.it
+biella.it
+bl.it
+bn.it
+bo.it
+bologna.it
+bolzano-altoadige.it
+bolzano.it
+bozen-sudtirol.it
+bozen-südtirol.it
+bozen-suedtirol.it
+bozen.it
+br.it
+brescia.it
+brindisi.it
+bs.it
+bt.it
+bulsan-sudtirol.it
+bulsan-südtirol.it
+bulsan-suedtirol.it
+bulsan.it
+bz.it
+ca.it
+cagliari.it
+caltanissetta.it
+campidano-medio.it
+campidanomedio.it
+campobasso.it
+carbonia-iglesias.it
+carboniaiglesias.it
+carrara-massa.it
+carraramassa.it
+caserta.it
+catania.it
+catanzaro.it
+cb.it
+ce.it
+cesena-forli.it
+cesena-forlì.it
+cesenaforli.it
+cesenaforlì.it
+ch.it
+chieti.it
+ci.it
+cl.it
+cn.it
+co.it
+como.it
+cosenza.it
+cr.it
+cremona.it
+crotone.it
+cs.it
+ct.it
+cuneo.it
+cz.it
+dell-ogliastra.it
+dellogliastra.it
+en.it
+enna.it
+fc.it
+fe.it
+fermo.it
+ferrara.it
+fg.it
+fi.it
+firenze.it
+florence.it
+fm.it
+foggia.it
+forli-cesena.it
+forlì-cesena.it
+forlicesena.it
+forlìcesena.it
+fr.it
+frosinone.it
+ge.it
+genoa.it
+genova.it
+go.it
+gorizia.it
+gr.it
+grosseto.it
+iglesias-carbonia.it
+iglesiascarbonia.it
+im.it
+imperia.it
+is.it
+isernia.it
+kr.it
+la-spezia.it
+laquila.it
+laspezia.it
+latina.it
+lc.it
+le.it
+lecce.it
+lecco.it
+li.it
+livorno.it
+lo.it
+lodi.it
+lt.it
+lu.it
+lucca.it
+macerata.it
+mantova.it
+massa-carrara.it
+massacarrara.it
+matera.it
+mb.it
+mc.it
+me.it
+medio-campidano.it
+mediocampidano.it
+messina.it
+mi.it
+milan.it
+milano.it
+mn.it
+mo.it
+modena.it
+monza-brianza.it
+monza-e-della-brianza.it
+monza.it
+monzabrianza.it
+monzaebrianza.it
+monzaedellabrianza.it
+ms.it
+mt.it
+na.it
+naples.it
+napoli.it
+no.it
+novara.it
+nu.it
+nuoro.it
+og.it
+ogliastra.it
+olbia-tempio.it
+olbiatempio.it
+or.it
+oristano.it
+ot.it
+pa.it
+padova.it
+padua.it
+palermo.it
+parma.it
+pavia.it
+pc.it
+pd.it
+pe.it
+perugia.it
+pesaro-urbino.it
+pesarourbino.it
+pescara.it
+pg.it
+pi.it
+piacenza.it
+pisa.it
+pistoia.it
+pn.it
+po.it
+pordenone.it
+potenza.it
+pr.it
+prato.it
+pt.it
+pu.it
+pv.it
+pz.it
+ra.it
+ragusa.it
+ravenna.it
+rc.it
+re.it
+reggio-calabria.it
+reggio-emilia.it
+reggiocalabria.it
+reggioemilia.it
+rg.it
+ri.it
+rieti.it
+rimini.it
+rm.it
+rn.it
+ro.it
+roma.it
+rome.it
+rovigo.it
+sa.it
+salerno.it
+sassari.it
+savona.it
+si.it
+siena.it
+siracusa.it
+so.it
+sondrio.it
+sp.it
+sr.it
+ss.it
+suedtirol.it
+südtirol.it
+sv.it
+ta.it
+taranto.it
+te.it
+tempio-olbia.it
+tempioolbia.it
+teramo.it
+terni.it
+tn.it
+to.it
+torino.it
+tp.it
+tr.it
+trani-andria-barletta.it
+trani-barletta-andria.it
+traniandriabarletta.it
+tranibarlettaandria.it
+trapani.it
+trento.it
+treviso.it
+trieste.it
+ts.it
+turin.it
+tv.it
+ud.it
+udine.it
+urbino-pesaro.it
+urbinopesaro.it
+va.it
+varese.it
+vb.it
+vc.it
+ve.it
+venezia.it
+venice.it
+verbania.it
+vercelli.it
+verona.it
+vi.it
+vibo-valentia.it
+vibovalentia.it
+vicenza.it
+viterbo.it
+vr.it
+vs.it
+vt.it
+vv.it
+
+// je : http://www.channelisles.net/register-domains/
+// Confirmed by registry <nigel@channelisles.net> 2013-11-28
+je
+co.je
+net.je
+org.je
+
+// jm : http://www.com.jm/register.html
+*.jm
+
+// jo : http://www.dns.jo/Registration_policy.aspx
+jo
+com.jo
+org.jo
+net.jo
+edu.jo
+sch.jo
+gov.jo
+mil.jo
+name.jo
+
+// jobs : https://en.wikipedia.org/wiki/.jobs
+jobs
+
+// jp : https://en.wikipedia.org/wiki/.jp
+// http://jprs.co.jp/en/jpdomain.html
+// Submitted by registry <info@jprs.jp>
+jp
+// jp organizational type names
+ac.jp
+ad.jp
+co.jp
+ed.jp
+go.jp
+gr.jp
+lg.jp
+ne.jp
+or.jp
+// jp prefecture type names
+aichi.jp
+akita.jp
+aomori.jp
+chiba.jp
+ehime.jp
+fukui.jp
+fukuoka.jp
+fukushima.jp
+gifu.jp
+gunma.jp
+hiroshima.jp
+hokkaido.jp
+hyogo.jp
+ibaraki.jp
+ishikawa.jp
+iwate.jp
+kagawa.jp
+kagoshima.jp
+kanagawa.jp
+kochi.jp
+kumamoto.jp
+kyoto.jp
+mie.jp
+miyagi.jp
+miyazaki.jp
+nagano.jp
+nagasaki.jp
+nara.jp
+niigata.jp
+oita.jp
+okayama.jp
+okinawa.jp
+osaka.jp
+saga.jp
+saitama.jp
+shiga.jp
+shimane.jp
+shizuoka.jp
+tochigi.jp
+tokushima.jp
+tokyo.jp
+tottori.jp
+toyama.jp
+wakayama.jp
+yamagata.jp
+yamaguchi.jp
+yamanashi.jp
+栃木.jp
+愛知.jp
+愛媛.jp
+兵庫.jp
+熊本.jp
+茨城.jp
+北海道.jp
+千葉.jp
+和歌山.jp
+長崎.jp
+長野.jp
+新潟.jp
+青森.jp
+静岡.jp
+東京.jp
+石川.jp
+埼玉.jp
+三重.jp
+京都.jp
+佐賀.jp
+大分.jp
+大阪.jp
+奈良.jp
+宮城.jp
+宮崎.jp
+富山.jp
+山口.jp
+山形.jp
+山梨.jp
+岩手.jp
+岐阜.jp
+岡山.jp
+島根.jp
+広島.jp
+徳島.jp
+沖縄.jp
+滋賀.jp
+神奈川.jp
+福井.jp
+福岡.jp
+福島.jp
+秋田.jp
+群馬.jp
+香川.jp
+高知.jp
+鳥取.jp
+鹿児島.jp
+// jp geographic type names
+// http://jprs.jp/doc/rule/saisoku-1.html
+*.kawasaki.jp
+*.kitakyushu.jp
+*.kobe.jp
+*.nagoya.jp
+*.sapporo.jp
+*.sendai.jp
+*.yokohama.jp
+!city.kawasaki.jp
+!city.kitakyushu.jp
+!city.kobe.jp
+!city.nagoya.jp
+!city.sapporo.jp
+!city.sendai.jp
+!city.yokohama.jp
+// 4th level registration
+aisai.aichi.jp
+ama.aichi.jp
+anjo.aichi.jp
+asuke.aichi.jp
+chiryu.aichi.jp
+chita.aichi.jp
+fuso.aichi.jp
+gamagori.aichi.jp
+handa.aichi.jp
+hazu.aichi.jp
+hekinan.aichi.jp
+higashiura.aichi.jp
+ichinomiya.aichi.jp
+inazawa.aichi.jp
+inuyama.aichi.jp
+isshiki.aichi.jp
+iwakura.aichi.jp
+kanie.aichi.jp
+kariya.aichi.jp
+kasugai.aichi.jp
+kira.aichi.jp
+kiyosu.aichi.jp
+komaki.aichi.jp
+konan.aichi.jp
+kota.aichi.jp
+mihama.aichi.jp
+miyoshi.aichi.jp
+nishio.aichi.jp
+nisshin.aichi.jp
+obu.aichi.jp
+oguchi.aichi.jp
+oharu.aichi.jp
+okazaki.aichi.jp
+owariasahi.aichi.jp
+seto.aichi.jp
+shikatsu.aichi.jp
+shinshiro.aichi.jp
+shitara.aichi.jp
+tahara.aichi.jp
+takahama.aichi.jp
+tobishima.aichi.jp
+toei.aichi.jp
+togo.aichi.jp
+tokai.aichi.jp
+tokoname.aichi.jp
+toyoake.aichi.jp
+toyohashi.aichi.jp
+toyokawa.aichi.jp
+toyone.aichi.jp
+toyota.aichi.jp
+tsushima.aichi.jp
+yatomi.aichi.jp
+akita.akita.jp
+daisen.akita.jp
+fujisato.akita.jp
+gojome.akita.jp
+hachirogata.akita.jp
+happou.akita.jp
+higashinaruse.akita.jp
+honjo.akita.jp
+honjyo.akita.jp
+ikawa.akita.jp
+kamikoani.akita.jp
+kamioka.akita.jp
+katagami.akita.jp
+kazuno.akita.jp
+kitaakita.akita.jp
+kosaka.akita.jp
+kyowa.akita.jp
+misato.akita.jp
+mitane.akita.jp
+moriyoshi.akita.jp
+nikaho.akita.jp
+noshiro.akita.jp
+odate.akita.jp
+oga.akita.jp
+ogata.akita.jp
+semboku.akita.jp
+yokote.akita.jp
+yurihonjo.akita.jp
+aomori.aomori.jp
+gonohe.aomori.jp
+hachinohe.aomori.jp
+hashikami.aomori.jp
+hiranai.aomori.jp
+hirosaki.aomori.jp
+itayanagi.aomori.jp
+kuroishi.aomori.jp
+misawa.aomori.jp
+mutsu.aomori.jp
+nakadomari.aomori.jp
+noheji.aomori.jp
+oirase.aomori.jp
+owani.aomori.jp
+rokunohe.aomori.jp
+sannohe.aomori.jp
+shichinohe.aomori.jp
+shingo.aomori.jp
+takko.aomori.jp
+towada.aomori.jp
+tsugaru.aomori.jp
+tsuruta.aomori.jp
+abiko.chiba.jp
+asahi.chiba.jp
+chonan.chiba.jp
+chosei.chiba.jp
+choshi.chiba.jp
+chuo.chiba.jp
+funabashi.chiba.jp
+futtsu.chiba.jp
+hanamigawa.chiba.jp
+ichihara.chiba.jp
+ichikawa.chiba.jp
+ichinomiya.chiba.jp
+inzai.chiba.jp
+isumi.chiba.jp
+kamagaya.chiba.jp
+kamogawa.chiba.jp
+kashiwa.chiba.jp
+katori.chiba.jp
+katsuura.chiba.jp
+kimitsu.chiba.jp
+kisarazu.chiba.jp
+kozaki.chiba.jp
+kujukuri.chiba.jp
+kyonan.chiba.jp
+matsudo.chiba.jp
+midori.chiba.jp
+mihama.chiba.jp
+minamiboso.chiba.jp
+mobara.chiba.jp
+mutsuzawa.chiba.jp
+nagara.chiba.jp
+nagareyama.chiba.jp
+narashino.chiba.jp
+narita.chiba.jp
+noda.chiba.jp
+oamishirasato.chiba.jp
+omigawa.chiba.jp
+onjuku.chiba.jp
+otaki.chiba.jp
+sakae.chiba.jp
+sakura.chiba.jp
+shimofusa.chiba.jp
+shirako.chiba.jp
+shiroi.chiba.jp
+shisui.chiba.jp
+sodegaura.chiba.jp
+sosa.chiba.jp
+tako.chiba.jp
+tateyama.chiba.jp
+togane.chiba.jp
+tohnosho.chiba.jp
+tomisato.chiba.jp
+urayasu.chiba.jp
+yachimata.chiba.jp
+yachiyo.chiba.jp
+yokaichiba.chiba.jp
+yokoshibahikari.chiba.jp
+yotsukaido.chiba.jp
+ainan.ehime.jp
+honai.ehime.jp
+ikata.ehime.jp
+imabari.ehime.jp
+iyo.ehime.jp
+kamijima.ehime.jp
+kihoku.ehime.jp
+kumakogen.ehime.jp
+masaki.ehime.jp
+matsuno.ehime.jp
+matsuyama.ehime.jp
+namikata.ehime.jp
+niihama.ehime.jp
+ozu.ehime.jp
+saijo.ehime.jp
+seiyo.ehime.jp
+shikokuchuo.ehime.jp
+tobe.ehime.jp
+toon.ehime.jp
+uchiko.ehime.jp
+uwajima.ehime.jp
+yawatahama.ehime.jp
+echizen.fukui.jp
+eiheiji.fukui.jp
+fukui.fukui.jp
+ikeda.fukui.jp
+katsuyama.fukui.jp
+mihama.fukui.jp
+minamiechizen.fukui.jp
+obama.fukui.jp
+ohi.fukui.jp
+ono.fukui.jp
+sabae.fukui.jp
+sakai.fukui.jp
+takahama.fukui.jp
+tsuruga.fukui.jp
+wakasa.fukui.jp
+ashiya.fukuoka.jp
+buzen.fukuoka.jp
+chikugo.fukuoka.jp
+chikuho.fukuoka.jp
+chikujo.fukuoka.jp
+chikushino.fukuoka.jp
+chikuzen.fukuoka.jp
+chuo.fukuoka.jp
+dazaifu.fukuoka.jp
+fukuchi.fukuoka.jp
+hakata.fukuoka.jp
+higashi.fukuoka.jp
+hirokawa.fukuoka.jp
+hisayama.fukuoka.jp
+iizuka.fukuoka.jp
+inatsuki.fukuoka.jp
+kaho.fukuoka.jp
+kasuga.fukuoka.jp
+kasuya.fukuoka.jp
+kawara.fukuoka.jp
+keisen.fukuoka.jp
+koga.fukuoka.jp
+kurate.fukuoka.jp
+kurogi.fukuoka.jp
+kurume.fukuoka.jp
+minami.fukuoka.jp
+miyako.fukuoka.jp
+miyama.fukuoka.jp
+miyawaka.fukuoka.jp
+mizumaki.fukuoka.jp
+munakata.fukuoka.jp
+nakagawa.fukuoka.jp
+nakama.fukuoka.jp
+nishi.fukuoka.jp
+nogata.fukuoka.jp
+ogori.fukuoka.jp
+okagaki.fukuoka.jp
+okawa.fukuoka.jp
+oki.fukuoka.jp
+omuta.fukuoka.jp
+onga.fukuoka.jp
+onojo.fukuoka.jp
+oto.fukuoka.jp
+saigawa.fukuoka.jp
+sasaguri.fukuoka.jp
+shingu.fukuoka.jp
+shinyoshitomi.fukuoka.jp
+shonai.fukuoka.jp
+soeda.fukuoka.jp
+sue.fukuoka.jp
+tachiarai.fukuoka.jp
+tagawa.fukuoka.jp
+takata.fukuoka.jp
+toho.fukuoka.jp
+toyotsu.fukuoka.jp
+tsuiki.fukuoka.jp
+ukiha.fukuoka.jp
+umi.fukuoka.jp
+usui.fukuoka.jp
+yamada.fukuoka.jp
+yame.fukuoka.jp
+yanagawa.fukuoka.jp
+yukuhashi.fukuoka.jp
+aizubange.fukushima.jp
+aizumisato.fukushima.jp
+aizuwakamatsu.fukushima.jp
+asakawa.fukushima.jp
+bandai.fukushima.jp
+date.fukushima.jp
+fukushima.fukushima.jp
+furudono.fukushima.jp
+futaba.fukushima.jp
+hanawa.fukushima.jp
+higashi.fukushima.jp
+hirata.fukushima.jp
+hirono.fukushima.jp
+iitate.fukushima.jp
+inawashiro.fukushima.jp
+ishikawa.fukushima.jp
+iwaki.fukushima.jp
+izumizaki.fukushima.jp
+kagamiishi.fukushima.jp
+kaneyama.fukushima.jp
+kawamata.fukushima.jp
+kitakata.fukushima.jp
+kitashiobara.fukushima.jp
+koori.fukushima.jp
+koriyama.fukushima.jp
+kunimi.fukushima.jp
+miharu.fukushima.jp
+mishima.fukushima.jp
+namie.fukushima.jp
+nango.fukushima.jp
+nishiaizu.fukushima.jp
+nishigo.fukushima.jp
+okuma.fukushima.jp
+omotego.fukushima.jp
+ono.fukushima.jp
+otama.fukushima.jp
+samegawa.fukushima.jp
+shimogo.fukushima.jp
+shirakawa.fukushima.jp
+showa.fukushima.jp
+soma.fukushima.jp
+sukagawa.fukushima.jp
+taishin.fukushima.jp
+tamakawa.fukushima.jp
+tanagura.fukushima.jp
+tenei.fukushima.jp
+yabuki.fukushima.jp
+yamato.fukushima.jp
+yamatsuri.fukushima.jp
+yanaizu.fukushima.jp
+yugawa.fukushima.jp
+anpachi.gifu.jp
+ena.gifu.jp
+gifu.gifu.jp
+ginan.gifu.jp
+godo.gifu.jp
+gujo.gifu.jp
+hashima.gifu.jp
+hichiso.gifu.jp
+hida.gifu.jp
+higashishirakawa.gifu.jp
+ibigawa.gifu.jp
+ikeda.gifu.jp
+kakamigahara.gifu.jp
+kani.gifu.jp
+kasahara.gifu.jp
+kasamatsu.gifu.jp
+kawaue.gifu.jp
+kitagata.gifu.jp
+mino.gifu.jp
+minokamo.gifu.jp
+mitake.gifu.jp
+mizunami.gifu.jp
+motosu.gifu.jp
+nakatsugawa.gifu.jp
+ogaki.gifu.jp
+sakahogi.gifu.jp
+seki.gifu.jp
+sekigahara.gifu.jp
+shirakawa.gifu.jp
+tajimi.gifu.jp
+takayama.gifu.jp
+tarui.gifu.jp
+toki.gifu.jp
+tomika.gifu.jp
+wanouchi.gifu.jp
+yamagata.gifu.jp
+yaotsu.gifu.jp
+yoro.gifu.jp
+annaka.gunma.jp
+chiyoda.gunma.jp
+fujioka.gunma.jp
+higashiagatsuma.gunma.jp
+isesaki.gunma.jp
+itakura.gunma.jp
+kanna.gunma.jp
+kanra.gunma.jp
+katashina.gunma.jp
+kawaba.gunma.jp
+kiryu.gunma.jp
+kusatsu.gunma.jp
+maebashi.gunma.jp
+meiwa.gunma.jp
+midori.gunma.jp
+minakami.gunma.jp
+naganohara.gunma.jp
+nakanojo.gunma.jp
+nanmoku.gunma.jp
+numata.gunma.jp
+oizumi.gunma.jp
+ora.gunma.jp
+ota.gunma.jp
+shibukawa.gunma.jp
+shimonita.gunma.jp
+shinto.gunma.jp
+showa.gunma.jp
+takasaki.gunma.jp
+takayama.gunma.jp
+tamamura.gunma.jp
+tatebayashi.gunma.jp
+tomioka.gunma.jp
+tsukiyono.gunma.jp
+tsumagoi.gunma.jp
+ueno.gunma.jp
+yoshioka.gunma.jp
+asaminami.hiroshima.jp
+daiwa.hiroshima.jp
+etajima.hiroshima.jp
+fuchu.hiroshima.jp
+fukuyama.hiroshima.jp
+hatsukaichi.hiroshima.jp
+higashihiroshima.hiroshima.jp
+hongo.hiroshima.jp
+jinsekikogen.hiroshima.jp
+kaita.hiroshima.jp
+kui.hiroshima.jp
+kumano.hiroshima.jp
+kure.hiroshima.jp
+mihara.hiroshima.jp
+miyoshi.hiroshima.jp
+naka.hiroshima.jp
+onomichi.hiroshima.jp
+osakikamijima.hiroshima.jp
+otake.hiroshima.jp
+saka.hiroshima.jp
+sera.hiroshima.jp
+seranishi.hiroshima.jp
+shinichi.hiroshima.jp
+shobara.hiroshima.jp
+takehara.hiroshima.jp
+abashiri.hokkaido.jp
+abira.hokkaido.jp
+aibetsu.hokkaido.jp
+akabira.hokkaido.jp
+akkeshi.hokkaido.jp
+asahikawa.hokkaido.jp
+ashibetsu.hokkaido.jp
+ashoro.hokkaido.jp
+assabu.hokkaido.jp
+atsuma.hokkaido.jp
+bibai.hokkaido.jp
+biei.hokkaido.jp
+bifuka.hokkaido.jp
+bihoro.hokkaido.jp
+biratori.hokkaido.jp
+chippubetsu.hokkaido.jp
+chitose.hokkaido.jp
+date.hokkaido.jp
+ebetsu.hokkaido.jp
+embetsu.hokkaido.jp
+eniwa.hokkaido.jp
+erimo.hokkaido.jp
+esan.hokkaido.jp
+esashi.hokkaido.jp
+fukagawa.hokkaido.jp
+fukushima.hokkaido.jp
+furano.hokkaido.jp
+furubira.hokkaido.jp
+haboro.hokkaido.jp
+hakodate.hokkaido.jp
+hamatonbetsu.hokkaido.jp
+hidaka.hokkaido.jp
+higashikagura.hokkaido.jp
+higashikawa.hokkaido.jp
+hiroo.hokkaido.jp
+hokuryu.hokkaido.jp
+hokuto.hokkaido.jp
+honbetsu.hokkaido.jp
+horokanai.hokkaido.jp
+horonobe.hokkaido.jp
+ikeda.hokkaido.jp
+imakane.hokkaido.jp
+ishikari.hokkaido.jp
+iwamizawa.hokkaido.jp
+iwanai.hokkaido.jp
+kamifurano.hokkaido.jp
+kamikawa.hokkaido.jp
+kamishihoro.hokkaido.jp
+kamisunagawa.hokkaido.jp
+kamoenai.hokkaido.jp
+kayabe.hokkaido.jp
+kembuchi.hokkaido.jp
+kikonai.hokkaido.jp
+kimobetsu.hokkaido.jp
+kitahiroshima.hokkaido.jp
+kitami.hokkaido.jp
+kiyosato.hokkaido.jp
+koshimizu.hokkaido.jp
+kunneppu.hokkaido.jp
+kuriyama.hokkaido.jp
+kuromatsunai.hokkaido.jp
+kushiro.hokkaido.jp
+kutchan.hokkaido.jp
+kyowa.hokkaido.jp
+mashike.hokkaido.jp
+matsumae.hokkaido.jp
+mikasa.hokkaido.jp
+minamifurano.hokkaido.jp
+mombetsu.hokkaido.jp
+moseushi.hokkaido.jp
+mukawa.hokkaido.jp
+muroran.hokkaido.jp
+naie.hokkaido.jp
+nakagawa.hokkaido.jp
+nakasatsunai.hokkaido.jp
+nakatombetsu.hokkaido.jp
+nanae.hokkaido.jp
+nanporo.hokkaido.jp
+nayoro.hokkaido.jp
+nemuro.hokkaido.jp
+niikappu.hokkaido.jp
+niki.hokkaido.jp
+nishiokoppe.hokkaido.jp
+noboribetsu.hokkaido.jp
+numata.hokkaido.jp
+obihiro.hokkaido.jp
+obira.hokkaido.jp
+oketo.hokkaido.jp
+okoppe.hokkaido.jp
+otaru.hokkaido.jp
+otobe.hokkaido.jp
+otofuke.hokkaido.jp
+otoineppu.hokkaido.jp
+oumu.hokkaido.jp
+ozora.hokkaido.jp
+pippu.hokkaido.jp
+rankoshi.hokkaido.jp
+rebun.hokkaido.jp
+rikubetsu.hokkaido.jp
+rishiri.hokkaido.jp
+rishirifuji.hokkaido.jp
+saroma.hokkaido.jp
+sarufutsu.hokkaido.jp
+shakotan.hokkaido.jp
+shari.hokkaido.jp
+shibecha.hokkaido.jp
+shibetsu.hokkaido.jp
+shikabe.hokkaido.jp
+shikaoi.hokkaido.jp
+shimamaki.hokkaido.jp
+shimizu.hokkaido.jp
+shimokawa.hokkaido.jp
+shinshinotsu.hokkaido.jp
+shintoku.hokkaido.jp
+shiranuka.hokkaido.jp
+shiraoi.hokkaido.jp
+shiriuchi.hokkaido.jp
+sobetsu.hokkaido.jp
+sunagawa.hokkaido.jp
+taiki.hokkaido.jp
+takasu.hokkaido.jp
+takikawa.hokkaido.jp
+takinoue.hokkaido.jp
+teshikaga.hokkaido.jp
+tobetsu.hokkaido.jp
+tohma.hokkaido.jp
+tomakomai.hokkaido.jp
+tomari.hokkaido.jp
+toya.hokkaido.jp
+toyako.hokkaido.jp
+toyotomi.hokkaido.jp
+toyoura.hokkaido.jp
+tsubetsu.hokkaido.jp
+tsukigata.hokkaido.jp
+urakawa.hokkaido.jp
+urausu.hokkaido.jp
+uryu.hokkaido.jp
+utashinai.hokkaido.jp
+wakkanai.hokkaido.jp
+wassamu.hokkaido.jp
+yakumo.hokkaido.jp
+yoichi.hokkaido.jp
+aioi.hyogo.jp
+akashi.hyogo.jp
+ako.hyogo.jp
+amagasaki.hyogo.jp
+aogaki.hyogo.jp
+asago.hyogo.jp
+ashiya.hyogo.jp
+awaji.hyogo.jp
+fukusaki.hyogo.jp
+goshiki.hyogo.jp
+harima.hyogo.jp
+himeji.hyogo.jp
+ichikawa.hyogo.jp
+inagawa.hyogo.jp
+itami.hyogo.jp
+kakogawa.hyogo.jp
+kamigori.hyogo.jp
+kamikawa.hyogo.jp
+kasai.hyogo.jp
+kasuga.hyogo.jp
+kawanishi.hyogo.jp
+miki.hyogo.jp
+minamiawaji.hyogo.jp
+nishinomiya.hyogo.jp
+nishiwaki.hyogo.jp
+ono.hyogo.jp
+sanda.hyogo.jp
+sannan.hyogo.jp
+sasayama.hyogo.jp
+sayo.hyogo.jp
+shingu.hyogo.jp
+shinonsen.hyogo.jp
+shiso.hyogo.jp
+sumoto.hyogo.jp
+taishi.hyogo.jp
+taka.hyogo.jp
+takarazuka.hyogo.jp
+takasago.hyogo.jp
+takino.hyogo.jp
+tamba.hyogo.jp
+tatsuno.hyogo.jp
+toyooka.hyogo.jp
+yabu.hyogo.jp
+yashiro.hyogo.jp
+yoka.hyogo.jp
+yokawa.hyogo.jp
+ami.ibaraki.jp
+asahi.ibaraki.jp
+bando.ibaraki.jp
+chikusei.ibaraki.jp
+daigo.ibaraki.jp
+fujishiro.ibaraki.jp
+hitachi.ibaraki.jp
+hitachinaka.ibaraki.jp
+hitachiomiya.ibaraki.jp
+hitachiota.ibaraki.jp
+ibaraki.ibaraki.jp
+ina.ibaraki.jp
+inashiki.ibaraki.jp
+itako.ibaraki.jp
+iwama.ibaraki.jp
+joso.ibaraki.jp
+kamisu.ibaraki.jp
+kasama.ibaraki.jp
+kashima.ibaraki.jp
+kasumigaura.ibaraki.jp
+koga.ibaraki.jp
+miho.ibaraki.jp
+mito.ibaraki.jp
+moriya.ibaraki.jp
+naka.ibaraki.jp
+namegata.ibaraki.jp
+oarai.ibaraki.jp
+ogawa.ibaraki.jp
+omitama.ibaraki.jp
+ryugasaki.ibaraki.jp
+sakai.ibaraki.jp
+sakuragawa.ibaraki.jp
+shimodate.ibaraki.jp
+shimotsuma.ibaraki.jp
+shirosato.ibaraki.jp
+sowa.ibaraki.jp
+suifu.ibaraki.jp
+takahagi.ibaraki.jp
+tamatsukuri.ibaraki.jp
+tokai.ibaraki.jp
+tomobe.ibaraki.jp
+tone.ibaraki.jp
+toride.ibaraki.jp
+tsuchiura.ibaraki.jp
+tsukuba.ibaraki.jp
+uchihara.ibaraki.jp
+ushiku.ibaraki.jp
+yachiyo.ibaraki.jp
+yamagata.ibaraki.jp
+yawara.ibaraki.jp
+yuki.ibaraki.jp
+anamizu.ishikawa.jp
+hakui.ishikawa.jp
+hakusan.ishikawa.jp
+kaga.ishikawa.jp
+kahoku.ishikawa.jp
+kanazawa.ishikawa.jp
+kawakita.ishikawa.jp
+komatsu.ishikawa.jp
+nakanoto.ishikawa.jp
+nanao.ishikawa.jp
+nomi.ishikawa.jp
+nonoichi.ishikawa.jp
+noto.ishikawa.jp
+shika.ishikawa.jp
+suzu.ishikawa.jp
+tsubata.ishikawa.jp
+tsurugi.ishikawa.jp
+uchinada.ishikawa.jp
+wajima.ishikawa.jp
+fudai.iwate.jp
+fujisawa.iwate.jp
+hanamaki.iwate.jp
+hiraizumi.iwate.jp
+hirono.iwate.jp
+ichinohe.iwate.jp
+ichinoseki.iwate.jp
+iwaizumi.iwate.jp
+iwate.iwate.jp
+joboji.iwate.jp
+kamaishi.iwate.jp
+kanegasaki.iwate.jp
+karumai.iwate.jp
+kawai.iwate.jp
+kitakami.iwate.jp
+kuji.iwate.jp
+kunohe.iwate.jp
+kuzumaki.iwate.jp
+miyako.iwate.jp
+mizusawa.iwate.jp
+morioka.iwate.jp
+ninohe.iwate.jp
+noda.iwate.jp
+ofunato.iwate.jp
+oshu.iwate.jp
+otsuchi.iwate.jp
+rikuzentakata.iwate.jp
+shiwa.iwate.jp
+shizukuishi.iwate.jp
+sumita.iwate.jp
+tanohata.iwate.jp
+tono.iwate.jp
+yahaba.iwate.jp
+yamada.iwate.jp
+ayagawa.kagawa.jp
+higashikagawa.kagawa.jp
+kanonji.kagawa.jp
+kotohira.kagawa.jp
+manno.kagawa.jp
+marugame.kagawa.jp
+mitoyo.kagawa.jp
+naoshima.kagawa.jp
+sanuki.kagawa.jp
+tadotsu.kagawa.jp
+takamatsu.kagawa.jp
+tonosho.kagawa.jp
+uchinomi.kagawa.jp
+utazu.kagawa.jp
+zentsuji.kagawa.jp
+akune.kagoshima.jp
+amami.kagoshima.jp
+hioki.kagoshima.jp
+isa.kagoshima.jp
+isen.kagoshima.jp
+izumi.kagoshima.jp
+kagoshima.kagoshima.jp
+kanoya.kagoshima.jp
+kawanabe.kagoshima.jp
+kinko.kagoshima.jp
+kouyama.kagoshima.jp
+makurazaki.kagoshima.jp
+matsumoto.kagoshima.jp
+minamitane.kagoshima.jp
+nakatane.kagoshima.jp
+nishinoomote.kagoshima.jp
+satsumasendai.kagoshima.jp
+soo.kagoshima.jp
+tarumizu.kagoshima.jp
+yusui.kagoshima.jp
+aikawa.kanagawa.jp
+atsugi.kanagawa.jp
+ayase.kanagawa.jp
+chigasaki.kanagawa.jp
+ebina.kanagawa.jp
+fujisawa.kanagawa.jp
+hadano.kanagawa.jp
+hakone.kanagawa.jp
+hiratsuka.kanagawa.jp
+isehara.kanagawa.jp
+kaisei.kanagawa.jp
+kamakura.kanagawa.jp
+kiyokawa.kanagawa.jp
+matsuda.kanagawa.jp
+minamiashigara.kanagawa.jp
+miura.kanagawa.jp
+nakai.kanagawa.jp
+ninomiya.kanagawa.jp
+odawara.kanagawa.jp
+oi.kanagawa.jp
+oiso.kanagawa.jp
+sagamihara.kanagawa.jp
+samukawa.kanagawa.jp
+tsukui.kanagawa.jp
+yamakita.kanagawa.jp
+yamato.kanagawa.jp
+yokosuka.kanagawa.jp
+yugawara.kanagawa.jp
+zama.kanagawa.jp
+zushi.kanagawa.jp
+aki.kochi.jp
+geisei.kochi.jp
+hidaka.kochi.jp
+higashitsuno.kochi.jp
+ino.kochi.jp
+kagami.kochi.jp
+kami.kochi.jp
+kitagawa.kochi.jp
+kochi.kochi.jp
+mihara.kochi.jp
+motoyama.kochi.jp
+muroto.kochi.jp
+nahari.kochi.jp
+nakamura.kochi.jp
+nankoku.kochi.jp
+nishitosa.kochi.jp
+niyodogawa.kochi.jp
+ochi.kochi.jp
+okawa.kochi.jp
+otoyo.kochi.jp
+otsuki.kochi.jp
+sakawa.kochi.jp
+sukumo.kochi.jp
+susaki.kochi.jp
+tosa.kochi.jp
+tosashimizu.kochi.jp
+toyo.kochi.jp
+tsuno.kochi.jp
+umaji.kochi.jp
+yasuda.kochi.jp
+yusuhara.kochi.jp
+amakusa.kumamoto.jp
+arao.kumamoto.jp
+aso.kumamoto.jp
+choyo.kumamoto.jp
+gyokuto.kumamoto.jp
+kamiamakusa.kumamoto.jp
+kikuchi.kumamoto.jp
+kumamoto.kumamoto.jp
+mashiki.kumamoto.jp
+mifune.kumamoto.jp
+minamata.kumamoto.jp
+minamioguni.kumamoto.jp
+nagasu.kumamoto.jp
+nishihara.kumamoto.jp
+oguni.kumamoto.jp
+ozu.kumamoto.jp
+sumoto.kumamoto.jp
+takamori.kumamoto.jp
+uki.kumamoto.jp
+uto.kumamoto.jp
+yamaga.kumamoto.jp
+yamato.kumamoto.jp
+yatsushiro.kumamoto.jp
+ayabe.kyoto.jp
+fukuchiyama.kyoto.jp
+higashiyama.kyoto.jp
+ide.kyoto.jp
+ine.kyoto.jp
+joyo.kyoto.jp
+kameoka.kyoto.jp
+kamo.kyoto.jp
+kita.kyoto.jp
+kizu.kyoto.jp
+kumiyama.kyoto.jp
+kyotamba.kyoto.jp
+kyotanabe.kyoto.jp
+kyotango.kyoto.jp
+maizuru.kyoto.jp
+minami.kyoto.jp
+minamiyamashiro.kyoto.jp
+miyazu.kyoto.jp
+muko.kyoto.jp
+nagaokakyo.kyoto.jp
+nakagyo.kyoto.jp
+nantan.kyoto.jp
+oyamazaki.kyoto.jp
+sakyo.kyoto.jp
+seika.kyoto.jp
+tanabe.kyoto.jp
+uji.kyoto.jp
+ujitawara.kyoto.jp
+wazuka.kyoto.jp
+yamashina.kyoto.jp
+yawata.kyoto.jp
+asahi.mie.jp
+inabe.mie.jp
+ise.mie.jp
+kameyama.mie.jp
+kawagoe.mie.jp
+kiho.mie.jp
+kisosaki.mie.jp
+kiwa.mie.jp
+komono.mie.jp
+kumano.mie.jp
+kuwana.mie.jp
+matsusaka.mie.jp
+meiwa.mie.jp
+mihama.mie.jp
+minamiise.mie.jp
+misugi.mie.jp
+miyama.mie.jp
+nabari.mie.jp
+shima.mie.jp
+suzuka.mie.jp
+tado.mie.jp
+taiki.mie.jp
+taki.mie.jp
+tamaki.mie.jp
+toba.mie.jp
+tsu.mie.jp
+udono.mie.jp
+ureshino.mie.jp
+watarai.mie.jp
+yokkaichi.mie.jp
+furukawa.miyagi.jp
+higashimatsushima.miyagi.jp
+ishinomaki.miyagi.jp
+iwanuma.miyagi.jp
+kakuda.miyagi.jp
+kami.miyagi.jp
+kawasaki.miyagi.jp
+marumori.miyagi.jp
+matsushima.miyagi.jp
+minamisanriku.miyagi.jp
+misato.miyagi.jp
+murata.miyagi.jp
+natori.miyagi.jp
+ogawara.miyagi.jp
+ohira.miyagi.jp
+onagawa.miyagi.jp
+osaki.miyagi.jp
+rifu.miyagi.jp
+semine.miyagi.jp
+shibata.miyagi.jp
+shichikashuku.miyagi.jp
+shikama.miyagi.jp
+shiogama.miyagi.jp
+shiroishi.miyagi.jp
+tagajo.miyagi.jp
+taiwa.miyagi.jp
+tome.miyagi.jp
+tomiya.miyagi.jp
+wakuya.miyagi.jp
+watari.miyagi.jp
+yamamoto.miyagi.jp
+zao.miyagi.jp
+aya.miyazaki.jp
+ebino.miyazaki.jp
+gokase.miyazaki.jp
+hyuga.miyazaki.jp
+kadogawa.miyazaki.jp
+kawaminami.miyazaki.jp
+kijo.miyazaki.jp
+kitagawa.miyazaki.jp
+kitakata.miyazaki.jp
+kitaura.miyazaki.jp
+kobayashi.miyazaki.jp
+kunitomi.miyazaki.jp
+kushima.miyazaki.jp
+mimata.miyazaki.jp
+miyakonojo.miyazaki.jp
+miyazaki.miyazaki.jp
+morotsuka.miyazaki.jp
+nichinan.miyazaki.jp
+nishimera.miyazaki.jp
+nobeoka.miyazaki.jp
+saito.miyazaki.jp
+shiiba.miyazaki.jp
+shintomi.miyazaki.jp
+takaharu.miyazaki.jp
+takanabe.miyazaki.jp
+takazaki.miyazaki.jp
+tsuno.miyazaki.jp
+achi.nagano.jp
+agematsu.nagano.jp
+anan.nagano.jp
+aoki.nagano.jp
+asahi.nagano.jp
+azumino.nagano.jp
+chikuhoku.nagano.jp
+chikuma.nagano.jp
+chino.nagano.jp
+fujimi.nagano.jp
+hakuba.nagano.jp
+hara.nagano.jp
+hiraya.nagano.jp
+iida.nagano.jp
+iijima.nagano.jp
+iiyama.nagano.jp
+iizuna.nagano.jp
+ikeda.nagano.jp
+ikusaka.nagano.jp
+ina.nagano.jp
+karuizawa.nagano.jp
+kawakami.nagano.jp
+kiso.nagano.jp
+kisofukushima.nagano.jp
+kitaaiki.nagano.jp
+komagane.nagano.jp
+komoro.nagano.jp
+matsukawa.nagano.jp
+matsumoto.nagano.jp
+miasa.nagano.jp
+minamiaiki.nagano.jp
+minamimaki.nagano.jp
+minamiminowa.nagano.jp
+minowa.nagano.jp
+miyada.nagano.jp
+miyota.nagano.jp
+mochizuki.nagano.jp
+nagano.nagano.jp
+nagawa.nagano.jp
+nagiso.nagano.jp
+nakagawa.nagano.jp
+nakano.nagano.jp
+nozawaonsen.nagano.jp
+obuse.nagano.jp
+ogawa.nagano.jp
+okaya.nagano.jp
+omachi.nagano.jp
+omi.nagano.jp
+ookuwa.nagano.jp
+ooshika.nagano.jp
+otaki.nagano.jp
+otari.nagano.jp
+sakae.nagano.jp
+sakaki.nagano.jp
+saku.nagano.jp
+sakuho.nagano.jp
+shimosuwa.nagano.jp
+shinanomachi.nagano.jp
+shiojiri.nagano.jp
+suwa.nagano.jp
+suzaka.nagano.jp
+takagi.nagano.jp
+takamori.nagano.jp
+takayama.nagano.jp
+tateshina.nagano.jp
+tatsuno.nagano.jp
+togakushi.nagano.jp
+togura.nagano.jp
+tomi.nagano.jp
+ueda.nagano.jp
+wada.nagano.jp
+yamagata.nagano.jp
+yamanouchi.nagano.jp
+yasaka.nagano.jp
+yasuoka.nagano.jp
+chijiwa.nagasaki.jp
+futsu.nagasaki.jp
+goto.nagasaki.jp
+hasami.nagasaki.jp
+hirado.nagasaki.jp
+iki.nagasaki.jp
+isahaya.nagasaki.jp
+kawatana.nagasaki.jp
+kuchinotsu.nagasaki.jp
+matsuura.nagasaki.jp
+nagasaki.nagasaki.jp
+obama.nagasaki.jp
+omura.nagasaki.jp
+oseto.nagasaki.jp
+saikai.nagasaki.jp
+sasebo.nagasaki.jp
+seihi.nagasaki.jp
+shimabara.nagasaki.jp
+shinkamigoto.nagasaki.jp
+togitsu.nagasaki.jp
+tsushima.nagasaki.jp
+unzen.nagasaki.jp
+ando.nara.jp
+gose.nara.jp
+heguri.nara.jp
+higashiyoshino.nara.jp
+ikaruga.nara.jp
+ikoma.nara.jp
+kamikitayama.nara.jp
+kanmaki.nara.jp
+kashiba.nara.jp
+kashihara.nara.jp
+katsuragi.nara.jp
+kawai.nara.jp
+kawakami.nara.jp
+kawanishi.nara.jp
+koryo.nara.jp
+kurotaki.nara.jp
+mitsue.nara.jp
+miyake.nara.jp
+nara.nara.jp
+nosegawa.nara.jp
+oji.nara.jp
+ouda.nara.jp
+oyodo.nara.jp
+sakurai.nara.jp
+sango.nara.jp
+shimoichi.nara.jp
+shimokitayama.nara.jp
+shinjo.nara.jp
+soni.nara.jp
+takatori.nara.jp
+tawaramoto.nara.jp
+tenkawa.nara.jp
+tenri.nara.jp
+uda.nara.jp
+yamatokoriyama.nara.jp
+yamatotakada.nara.jp
+yamazoe.nara.jp
+yoshino.nara.jp
+aga.niigata.jp
+agano.niigata.jp
+gosen.niigata.jp
+itoigawa.niigata.jp
+izumozaki.niigata.jp
+joetsu.niigata.jp
+kamo.niigata.jp
+kariwa.niigata.jp
+kashiwazaki.niigata.jp
+minamiuonuma.niigata.jp
+mitsuke.niigata.jp
+muika.niigata.jp
+murakami.niigata.jp
+myoko.niigata.jp
+nagaoka.niigata.jp
+niigata.niigata.jp
+ojiya.niigata.jp
+omi.niigata.jp
+sado.niigata.jp
+sanjo.niigata.jp
+seiro.niigata.jp
+seirou.niigata.jp
+sekikawa.niigata.jp
+shibata.niigata.jp
+tagami.niigata.jp
+tainai.niigata.jp
+tochio.niigata.jp
+tokamachi.niigata.jp
+tsubame.niigata.jp
+tsunan.niigata.jp
+uonuma.niigata.jp
+yahiko.niigata.jp
+yoita.niigata.jp
+yuzawa.niigata.jp
+beppu.oita.jp
+bungoono.oita.jp
+bungotakada.oita.jp
+hasama.oita.jp
+hiji.oita.jp
+himeshima.oita.jp
+hita.oita.jp
+kamitsue.oita.jp
+kokonoe.oita.jp
+kuju.oita.jp
+kunisaki.oita.jp
+kusu.oita.jp
+oita.oita.jp
+saiki.oita.jp
+taketa.oita.jp
+tsukumi.oita.jp
+usa.oita.jp
+usuki.oita.jp
+yufu.oita.jp
+akaiwa.okayama.jp
+asakuchi.okayama.jp
+bizen.okayama.jp
+hayashima.okayama.jp
+ibara.okayama.jp
+kagamino.okayama.jp
+kasaoka.okayama.jp
+kibichuo.okayama.jp
+kumenan.okayama.jp
+kurashiki.okayama.jp
+maniwa.okayama.jp
+misaki.okayama.jp
+nagi.okayama.jp
+niimi.okayama.jp
+nishiawakura.okayama.jp
+okayama.okayama.jp
+satosho.okayama.jp
+setouchi.okayama.jp
+shinjo.okayama.jp
+shoo.okayama.jp
+soja.okayama.jp
+takahashi.okayama.jp
+tamano.okayama.jp
+tsuyama.okayama.jp
+wake.okayama.jp
+yakage.okayama.jp
+aguni.okinawa.jp
+ginowan.okinawa.jp
+ginoza.okinawa.jp
+gushikami.okinawa.jp
+haebaru.okinawa.jp
+higashi.okinawa.jp
+hirara.okinawa.jp
+iheya.okinawa.jp
+ishigaki.okinawa.jp
+ishikawa.okinawa.jp
+itoman.okinawa.jp
+izena.okinawa.jp
+kadena.okinawa.jp
+kin.okinawa.jp
+kitadaito.okinawa.jp
+kitanakagusuku.okinawa.jp
+kumejima.okinawa.jp
+kunigami.okinawa.jp
+minamidaito.okinawa.jp
+motobu.okinawa.jp
+nago.okinawa.jp
+naha.okinawa.jp
+nakagusuku.okinawa.jp
+nakijin.okinawa.jp
+nanjo.okinawa.jp
+nishihara.okinawa.jp
+ogimi.okinawa.jp
+okinawa.okinawa.jp
+onna.okinawa.jp
+shimoji.okinawa.jp
+taketomi.okinawa.jp
+tarama.okinawa.jp
+tokashiki.okinawa.jp
+tomigusuku.okinawa.jp
+tonaki.okinawa.jp
+urasoe.okinawa.jp
+uruma.okinawa.jp
+yaese.okinawa.jp
+yomitan.okinawa.jp
+yonabaru.okinawa.jp
+yonaguni.okinawa.jp
+zamami.okinawa.jp
+abeno.osaka.jp
+chihayaakasaka.osaka.jp
+chuo.osaka.jp
+daito.osaka.jp
+fujiidera.osaka.jp
+habikino.osaka.jp
+hannan.osaka.jp
+higashiosaka.osaka.jp
+higashisumiyoshi.osaka.jp
+higashiyodogawa.osaka.jp
+hirakata.osaka.jp
+ibaraki.osaka.jp
+ikeda.osaka.jp
+izumi.osaka.jp
+izumiotsu.osaka.jp
+izumisano.osaka.jp
+kadoma.osaka.jp
+kaizuka.osaka.jp
+kanan.osaka.jp
+kashiwara.osaka.jp
+katano.osaka.jp
+kawachinagano.osaka.jp
+kishiwada.osaka.jp
+kita.osaka.jp
+kumatori.osaka.jp
+matsubara.osaka.jp
+minato.osaka.jp
+minoh.osaka.jp
+misaki.osaka.jp
+moriguchi.osaka.jp
+neyagawa.osaka.jp
+nishi.osaka.jp
+nose.osaka.jp
+osakasayama.osaka.jp
+sakai.osaka.jp
+sayama.osaka.jp
+sennan.osaka.jp
+settsu.osaka.jp
+shijonawate.osaka.jp
+shimamoto.osaka.jp
+suita.osaka.jp
+tadaoka.osaka.jp
+taishi.osaka.jp
+tajiri.osaka.jp
+takaishi.osaka.jp
+takatsuki.osaka.jp
+tondabayashi.osaka.jp
+toyonaka.osaka.jp
+toyono.osaka.jp
+yao.osaka.jp
+ariake.saga.jp
+arita.saga.jp
+fukudomi.saga.jp
+genkai.saga.jp
+hamatama.saga.jp
+hizen.saga.jp
+imari.saga.jp
+kamimine.saga.jp
+kanzaki.saga.jp
+karatsu.saga.jp
+kashima.saga.jp
+kitagata.saga.jp
+kitahata.saga.jp
+kiyama.saga.jp
+kouhoku.saga.jp
+kyuragi.saga.jp
+nishiarita.saga.jp
+ogi.saga.jp
+omachi.saga.jp
+ouchi.saga.jp
+saga.saga.jp
+shiroishi.saga.jp
+taku.saga.jp
+tara.saga.jp
+tosu.saga.jp
+yoshinogari.saga.jp
+arakawa.saitama.jp
+asaka.saitama.jp
+chichibu.saitama.jp
+fujimi.saitama.jp
+fujimino.saitama.jp
+fukaya.saitama.jp
+hanno.saitama.jp
+hanyu.saitama.jp
+hasuda.saitama.jp
+hatogaya.saitama.jp
+hatoyama.saitama.jp
+hidaka.saitama.jp
+higashichichibu.saitama.jp
+higashimatsuyama.saitama.jp
+honjo.saitama.jp
+ina.saitama.jp
+iruma.saitama.jp
+iwatsuki.saitama.jp
+kamiizumi.saitama.jp
+kamikawa.saitama.jp
+kamisato.saitama.jp
+kasukabe.saitama.jp
+kawagoe.saitama.jp
+kawaguchi.saitama.jp
+kawajima.saitama.jp
+kazo.saitama.jp
+kitamoto.saitama.jp
+koshigaya.saitama.jp
+kounosu.saitama.jp
+kuki.saitama.jp
+kumagaya.saitama.jp
+matsubushi.saitama.jp
+minano.saitama.jp
+misato.saitama.jp
+miyashiro.saitama.jp
+miyoshi.saitama.jp
+moroyama.saitama.jp
+nagatoro.saitama.jp
+namegawa.saitama.jp
+niiza.saitama.jp
+ogano.saitama.jp
+ogawa.saitama.jp
+ogose.saitama.jp
+okegawa.saitama.jp
+omiya.saitama.jp
+otaki.saitama.jp
+ranzan.saitama.jp
+ryokami.saitama.jp
+saitama.saitama.jp
+sakado.saitama.jp
+satte.saitama.jp
+sayama.saitama.jp
+shiki.saitama.jp
+shiraoka.saitama.jp
+soka.saitama.jp
+sugito.saitama.jp
+toda.saitama.jp
+tokigawa.saitama.jp
+tokorozawa.saitama.jp
+tsurugashima.saitama.jp
+urawa.saitama.jp
+warabi.saitama.jp
+yashio.saitama.jp
+yokoze.saitama.jp
+yono.saitama.jp
+yorii.saitama.jp
+yoshida.saitama.jp
+yoshikawa.saitama.jp
+yoshimi.saitama.jp
+aisho.shiga.jp
+gamo.shiga.jp
+higashiomi.shiga.jp
+hikone.shiga.jp
+koka.shiga.jp
+konan.shiga.jp
+kosei.shiga.jp
+koto.shiga.jp
+kusatsu.shiga.jp
+maibara.shiga.jp
+moriyama.shiga.jp
+nagahama.shiga.jp
+nishiazai.shiga.jp
+notogawa.shiga.jp
+omihachiman.shiga.jp
+otsu.shiga.jp
+ritto.shiga.jp
+ryuoh.shiga.jp
+takashima.shiga.jp
+takatsuki.shiga.jp
+torahime.shiga.jp
+toyosato.shiga.jp
+yasu.shiga.jp
+akagi.shimane.jp
+ama.shimane.jp
+gotsu.shimane.jp
+hamada.shimane.jp
+higashiizumo.shimane.jp
+hikawa.shimane.jp
+hikimi.shimane.jp
+izumo.shimane.jp
+kakinoki.shimane.jp
+masuda.shimane.jp
+matsue.shimane.jp
+misato.shimane.jp
+nishinoshima.shimane.jp
+ohda.shimane.jp
+okinoshima.shimane.jp
+okuizumo.shimane.jp
+shimane.shimane.jp
+tamayu.shimane.jp
+tsuwano.shimane.jp
+unnan.shimane.jp
+yakumo.shimane.jp
+yasugi.shimane.jp
+yatsuka.shimane.jp
+arai.shizuoka.jp
+atami.shizuoka.jp
+fuji.shizuoka.jp
+fujieda.shizuoka.jp
+fujikawa.shizuoka.jp
+fujinomiya.shizuoka.jp
+fukuroi.shizuoka.jp
+gotemba.shizuoka.jp
+haibara.shizuoka.jp
+hamamatsu.shizuoka.jp
+higashiizu.shizuoka.jp
+ito.shizuoka.jp
+iwata.shizuoka.jp
+izu.shizuoka.jp
+izunokuni.shizuoka.jp
+kakegawa.shizuoka.jp
+kannami.shizuoka.jp
+kawanehon.shizuoka.jp
+kawazu.shizuoka.jp
+kikugawa.shizuoka.jp
+kosai.shizuoka.jp
+makinohara.shizuoka.jp
+matsuzaki.shizuoka.jp
+minamiizu.shizuoka.jp
+mishima.shizuoka.jp
+morimachi.shizuoka.jp
+nishiizu.shizuoka.jp
+numazu.shizuoka.jp
+omaezaki.shizuoka.jp
+shimada.shizuoka.jp
+shimizu.shizuoka.jp
+shimoda.shizuoka.jp
+shizuoka.shizuoka.jp
+susono.shizuoka.jp
+yaizu.shizuoka.jp
+yoshida.shizuoka.jp
+ashikaga.tochigi.jp
+bato.tochigi.jp
+haga.tochigi.jp
+ichikai.tochigi.jp
+iwafune.tochigi.jp
+kaminokawa.tochigi.jp
+kanuma.tochigi.jp
+karasuyama.tochigi.jp
+kuroiso.tochigi.jp
+mashiko.tochigi.jp
+mibu.tochigi.jp
+moka.tochigi.jp
+motegi.tochigi.jp
+nasu.tochigi.jp
+nasushiobara.tochigi.jp
+nikko.tochigi.jp
+nishikata.tochigi.jp
+nogi.tochigi.jp
+ohira.tochigi.jp
+ohtawara.tochigi.jp
+oyama.tochigi.jp
+sakura.tochigi.jp
+sano.tochigi.jp
+shimotsuke.tochigi.jp
+shioya.tochigi.jp
+takanezawa.tochigi.jp
+tochigi.tochigi.jp
+tsuga.tochigi.jp
+ujiie.tochigi.jp
+utsunomiya.tochigi.jp
+yaita.tochigi.jp
+aizumi.tokushima.jp
+anan.tokushima.jp
+ichiba.tokushima.jp
+itano.tokushima.jp
+kainan.tokushima.jp
+komatsushima.tokushima.jp
+matsushige.tokushima.jp
+mima.tokushima.jp
+minami.tokushima.jp
+miyoshi.tokushima.jp
+mugi.tokushima.jp
+nakagawa.tokushima.jp
+naruto.tokushima.jp
+sanagochi.tokushima.jp
+shishikui.tokushima.jp
+tokushima.tokushima.jp
+wajiki.tokushima.jp
+adachi.tokyo.jp
+akiruno.tokyo.jp
+akishima.tokyo.jp
+aogashima.tokyo.jp
+arakawa.tokyo.jp
+bunkyo.tokyo.jp
+chiyoda.tokyo.jp
+chofu.tokyo.jp
+chuo.tokyo.jp
+edogawa.tokyo.jp
+fuchu.tokyo.jp
+fussa.tokyo.jp
+hachijo.tokyo.jp
+hachioji.tokyo.jp
+hamura.tokyo.jp
+higashikurume.tokyo.jp
+higashimurayama.tokyo.jp
+higashiyamato.tokyo.jp
+hino.tokyo.jp
+hinode.tokyo.jp
+hinohara.tokyo.jp
+inagi.tokyo.jp
+itabashi.tokyo.jp
+katsushika.tokyo.jp
+kita.tokyo.jp
+kiyose.tokyo.jp
+kodaira.tokyo.jp
+koganei.tokyo.jp
+kokubunji.tokyo.jp
+komae.tokyo.jp
+koto.tokyo.jp
+kouzushima.tokyo.jp
+kunitachi.tokyo.jp
+machida.tokyo.jp
+meguro.tokyo.jp
+minato.tokyo.jp
+mitaka.tokyo.jp
+mizuho.tokyo.jp
+musashimurayama.tokyo.jp
+musashino.tokyo.jp
+nakano.tokyo.jp
+nerima.tokyo.jp
+ogasawara.tokyo.jp
+okutama.tokyo.jp
+ome.tokyo.jp
+oshima.tokyo.jp
+ota.tokyo.jp
+setagaya.tokyo.jp
+shibuya.tokyo.jp
+shinagawa.tokyo.jp
+shinjuku.tokyo.jp
+suginami.tokyo.jp
+sumida.tokyo.jp
+tachikawa.tokyo.jp
+taito.tokyo.jp
+tama.tokyo.jp
+toshima.tokyo.jp
+chizu.tottori.jp
+hino.tottori.jp
+kawahara.tottori.jp
+koge.tottori.jp
+kotoura.tottori.jp
+misasa.tottori.jp
+nanbu.tottori.jp
+nichinan.tottori.jp
+sakaiminato.tottori.jp
+tottori.tottori.jp
+wakasa.tottori.jp
+yazu.tottori.jp
+yonago.tottori.jp
+asahi.toyama.jp
+fuchu.toyama.jp
+fukumitsu.toyama.jp
+funahashi.toyama.jp
+himi.toyama.jp
+imizu.toyama.jp
+inami.toyama.jp
+johana.toyama.jp
+kamiichi.toyama.jp
+kurobe.toyama.jp
+nakaniikawa.toyama.jp
+namerikawa.toyama.jp
+nanto.toyama.jp
+nyuzen.toyama.jp
+oyabe.toyama.jp
+taira.toyama.jp
+takaoka.toyama.jp
+tateyama.toyama.jp
+toga.toyama.jp
+tonami.toyama.jp
+toyama.toyama.jp
+unazuki.toyama.jp
+uozu.toyama.jp
+yamada.toyama.jp
+arida.wakayama.jp
+aridagawa.wakayama.jp
+gobo.wakayama.jp
+hashimoto.wakayama.jp
+hidaka.wakayama.jp
+hirogawa.wakayama.jp
+inami.wakayama.jp
+iwade.wakayama.jp
+kainan.wakayama.jp
+kamitonda.wakayama.jp
+katsuragi.wakayama.jp
+kimino.wakayama.jp
+kinokawa.wakayama.jp
+kitayama.wakayama.jp
+koya.wakayama.jp
+koza.wakayama.jp
+kozagawa.wakayama.jp
+kudoyama.wakayama.jp
+kushimoto.wakayama.jp
+mihama.wakayama.jp
+misato.wakayama.jp
+nachikatsuura.wakayama.jp
+shingu.wakayama.jp
+shirahama.wakayama.jp
+taiji.wakayama.jp
+tanabe.wakayama.jp
+wakayama.wakayama.jp
+yuasa.wakayama.jp
+yura.wakayama.jp
+asahi.yamagata.jp
+funagata.yamagata.jp
+higashine.yamagata.jp
+iide.yamagata.jp
+kahoku.yamagata.jp
+kaminoyama.yamagata.jp
+kaneyama.yamagata.jp
+kawanishi.yamagata.jp
+mamurogawa.yamagata.jp
+mikawa.yamagata.jp
+murayama.yamagata.jp
+nagai.yamagata.jp
+nakayama.yamagata.jp
+nanyo.yamagata.jp
+nishikawa.yamagata.jp
+obanazawa.yamagata.jp
+oe.yamagata.jp
+oguni.yamagata.jp
+ohkura.yamagata.jp
+oishida.yamagata.jp
+sagae.yamagata.jp
+sakata.yamagata.jp
+sakegawa.yamagata.jp
+shinjo.yamagata.jp
+shirataka.yamagata.jp
+shonai.yamagata.jp
+takahata.yamagata.jp
+tendo.yamagata.jp
+tozawa.yamagata.jp
+tsuruoka.yamagata.jp
+yamagata.yamagata.jp
+yamanobe.yamagata.jp
+yonezawa.yamagata.jp
+yuza.yamagata.jp
+abu.yamaguchi.jp
+hagi.yamaguchi.jp
+hikari.yamaguchi.jp
+hofu.yamaguchi.jp
+iwakuni.yamaguchi.jp
+kudamatsu.yamaguchi.jp
+mitou.yamaguchi.jp
+nagato.yamaguchi.jp
+oshima.yamaguchi.jp
+shimonoseki.yamaguchi.jp
+shunan.yamaguchi.jp
+tabuse.yamaguchi.jp
+tokuyama.yamaguchi.jp
+toyota.yamaguchi.jp
+ube.yamaguchi.jp
+yuu.yamaguchi.jp
+chuo.yamanashi.jp
+doshi.yamanashi.jp
+fuefuki.yamanashi.jp
+fujikawa.yamanashi.jp
+fujikawaguchiko.yamanashi.jp
+fujiyoshida.yamanashi.jp
+hayakawa.yamanashi.jp
+hokuto.yamanashi.jp
+ichikawamisato.yamanashi.jp
+kai.yamanashi.jp
+kofu.yamanashi.jp
+koshu.yamanashi.jp
+kosuge.yamanashi.jp
+minami-alps.yamanashi.jp
+minobu.yamanashi.jp
+nakamichi.yamanashi.jp
+nanbu.yamanashi.jp
+narusawa.yamanashi.jp
+nirasaki.yamanashi.jp
+nishikatsura.yamanashi.jp
+oshino.yamanashi.jp
+otsuki.yamanashi.jp
+showa.yamanashi.jp
+tabayama.yamanashi.jp
+tsuru.yamanashi.jp
+uenohara.yamanashi.jp
+yamanakako.yamanashi.jp
+yamanashi.yamanashi.jp
+
+// ke : http://www.kenic.or.ke/index.php/en/ke-domains/ke-domains
+ke
+ac.ke
+co.ke
+go.ke
+info.ke
+me.ke
+mobi.ke
+ne.ke
+or.ke
+sc.ke
+
+// kg : http://www.domain.kg/dmn_n.html
+kg
+org.kg
+net.kg
+com.kg
+edu.kg
+gov.kg
+mil.kg
+
+// kh : http://www.mptc.gov.kh/dns_registration.htm
+*.kh
+
+// ki : http://www.ki/dns/index.html
+ki
+edu.ki
+biz.ki
+net.ki
+org.ki
+gov.ki
+info.ki
+com.ki
+
+// km : https://en.wikipedia.org/wiki/.km
+// http://www.domaine.km/documents/charte.doc
+km
+org.km
+nom.km
+gov.km
+prd.km
+tm.km
+edu.km
+mil.km
+ass.km
+com.km
+// These are only mentioned as proposed suggestions at domaine.km, but
+// https://en.wikipedia.org/wiki/.km says they're available for registration:
+coop.km
+asso.km
+presse.km
+medecin.km
+notaires.km
+pharmaciens.km
+veterinaire.km
+gouv.km
+
+// kn : https://en.wikipedia.org/wiki/.kn
+// http://www.dot.kn/domainRules.html
+kn
+net.kn
+org.kn
+edu.kn
+gov.kn
+
+// kp : http://www.kcce.kp/en_index.php
+kp
+com.kp
+edu.kp
+gov.kp
+org.kp
+rep.kp
+tra.kp
+
+// kr : https://en.wikipedia.org/wiki/.kr
+// see also: http://domain.nida.or.kr/eng/registration.jsp
+kr
+ac.kr
+co.kr
+es.kr
+go.kr
+hs.kr
+kg.kr
+mil.kr
+ms.kr
+ne.kr
+or.kr
+pe.kr
+re.kr
+sc.kr
+// kr geographical names
+busan.kr
+chungbuk.kr
+chungnam.kr
+daegu.kr
+daejeon.kr
+gangwon.kr
+gwangju.kr
+gyeongbuk.kr
+gyeonggi.kr
+gyeongnam.kr
+incheon.kr
+jeju.kr
+jeonbuk.kr
+jeonnam.kr
+seoul.kr
+ulsan.kr
+
+// kw : https://www.nic.kw/policies/
+// Confirmed by registry <nic.tech@citra.gov.kw>
+kw
+com.kw
+edu.kw
+emb.kw
+gov.kw
+ind.kw
+net.kw
+org.kw
+
+// ky : http://www.icta.ky/da_ky_reg_dom.php
+// Confirmed by registry <kysupport@perimeterusa.com> 2008-06-17
+ky
+com.ky
+edu.ky
+net.ky
+org.ky
+
+// kz : https://en.wikipedia.org/wiki/.kz
+// see also: http://www.nic.kz/rules/index.jsp
+kz
+org.kz
+edu.kz
+net.kz
+gov.kz
+mil.kz
+com.kz
+
+// la : https://en.wikipedia.org/wiki/.la
+// Submitted by registry <gavin.brown@nic.la>
+la
+int.la
+net.la
+info.la
+edu.la
+gov.la
+per.la
+com.la
+org.la
+
+// lb : https://en.wikipedia.org/wiki/.lb
+// Submitted by registry <randy@psg.com>
+lb
+com.lb
+edu.lb
+gov.lb
+net.lb
+org.lb
+
+// lc : https://en.wikipedia.org/wiki/.lc
+// see also: http://www.nic.lc/rules.htm
+lc
+com.lc
+net.lc
+co.lc
+org.lc
+edu.lc
+gov.lc
+
+// li : https://en.wikipedia.org/wiki/.li
+li
+
+// lk : https://www.nic.lk/index.php/domain-registration/lk-domain-naming-structure
+lk
+gov.lk
+sch.lk
+net.lk
+int.lk
+com.lk
+org.lk
+edu.lk
+ngo.lk
+soc.lk
+web.lk
+ltd.lk
+assn.lk
+grp.lk
+hotel.lk
+ac.lk
+
+// lr : http://psg.com/dns/lr/lr.txt
+// Submitted by registry <randy@psg.com>
+lr
+com.lr
+edu.lr
+gov.lr
+org.lr
+net.lr
+
+// ls : http://www.nic.ls/
+// Confirmed by registry <lsadmin@nic.ls>
+ls
+ac.ls
+biz.ls
+co.ls
+edu.ls
+gov.ls
+info.ls
+net.ls
+org.ls
+sc.ls
+
+// lt : https://en.wikipedia.org/wiki/.lt
+lt
+// gov.lt : http://www.gov.lt/index_en.php
+gov.lt
+
+// lu : http://www.dns.lu/en/
+lu
+
+// lv : http://www.nic.lv/DNS/En/generic.php
+lv
+com.lv
+edu.lv
+gov.lv
+org.lv
+mil.lv
+id.lv
+net.lv
+asn.lv
+conf.lv
+
+// ly : http://www.nic.ly/regulations.php
+ly
+com.ly
+net.ly
+gov.ly
+plc.ly
+edu.ly
+sch.ly
+med.ly
+org.ly
+id.ly
+
+// ma : https://en.wikipedia.org/wiki/.ma
+// http://www.anrt.ma/fr/admin/download/upload/file_fr782.pdf
+ma
+co.ma
+net.ma
+gov.ma
+org.ma
+ac.ma
+press.ma
+
+// mc : http://www.nic.mc/
+mc
+tm.mc
+asso.mc
+
+// md : https://en.wikipedia.org/wiki/.md
+md
+
+// me : https://en.wikipedia.org/wiki/.me
+me
+co.me
+net.me
+org.me
+edu.me
+ac.me
+gov.me
+its.me
+priv.me
+
+// mg : http://nic.mg/nicmg/?page_id=39
+mg
+org.mg
+nom.mg
+gov.mg
+prd.mg
+tm.mg
+edu.mg
+mil.mg
+com.mg
+co.mg
+
+// mh : https://en.wikipedia.org/wiki/.mh
+mh
+
+// mil : https://en.wikipedia.org/wiki/.mil
+mil
+
+// mk : https://en.wikipedia.org/wiki/.mk
+// see also: http://dns.marnet.net.mk/postapka.php
+mk
+com.mk
+org.mk
+net.mk
+edu.mk
+gov.mk
+inf.mk
+name.mk
+
+// ml : http://www.gobin.info/domainname/ml-template.doc
+// see also: https://en.wikipedia.org/wiki/.ml
+ml
+com.ml
+edu.ml
+gouv.ml
+gov.ml
+net.ml
+org.ml
+presse.ml
+
+// mm : https://en.wikipedia.org/wiki/.mm
+*.mm
+
+// mn : https://en.wikipedia.org/wiki/.mn
+mn
+gov.mn
+edu.mn
+org.mn
+
+// mo : http://www.monic.net.mo/
+mo
+com.mo
+net.mo
+org.mo
+edu.mo
+gov.mo
+
+// mobi : https://en.wikipedia.org/wiki/.mobi
+mobi
+
+// mp : http://www.dot.mp/
+// Confirmed by registry <dcamacho@saipan.com> 2008-06-17
+mp
+
+// mq : https://en.wikipedia.org/wiki/.mq
+mq
+
+// mr : https://en.wikipedia.org/wiki/.mr
+mr
+gov.mr
+
+// ms : http://www.nic.ms/pdf/MS_Domain_Name_Rules.pdf
+ms
+com.ms
+edu.ms
+gov.ms
+net.ms
+org.ms
+
+// mt : https://www.nic.org.mt/go/policy
+// Submitted by registry <help@nic.org.mt>
+mt
+com.mt
+edu.mt
+net.mt
+org.mt
+
+// mu : https://en.wikipedia.org/wiki/.mu
+mu
+com.mu
+net.mu
+org.mu
+gov.mu
+ac.mu
+co.mu
+or.mu
+
+// museum : https://welcome.museum/wp-content/uploads/2018/05/20180525-Registration-Policy-MUSEUM-EN_VF-2.pdf https://welcome.museum/buy-your-dot-museum-2/
+museum
+
+// mv : https://en.wikipedia.org/wiki/.mv
+// "mv" included because, contra Wikipedia, google.mv exists.
+mv
+aero.mv
+biz.mv
+com.mv
+coop.mv
+edu.mv
+gov.mv
+info.mv
+int.mv
+mil.mv
+museum.mv
+name.mv
+net.mv
+org.mv
+pro.mv
+
+// mw : http://www.registrar.mw/
+mw
+ac.mw
+biz.mw
+co.mw
+com.mw
+coop.mw
+edu.mw
+gov.mw
+int.mw
+museum.mw
+net.mw
+org.mw
+
+// mx : http://www.nic.mx/
+// Submitted by registry <farias@nic.mx>
+mx
+com.mx
+org.mx
+gob.mx
+edu.mx
+net.mx
+
+// my : http://www.mynic.my/
+// Available strings: https://mynic.my/resources/domains/buying-a-domain/
+my
+biz.my
+com.my
+edu.my
+gov.my
+mil.my
+name.my
+net.my
+org.my
+
+// mz : http://www.uem.mz/
+// Submitted by registry <antonio@uem.mz>
+mz
+ac.mz
+adv.mz
+co.mz
+edu.mz
+gov.mz
+mil.mz
+net.mz
+org.mz
+
+// na : http://www.na-nic.com.na/
+// http://www.info.na/domain/
+na
+info.na
+pro.na
+name.na
+school.na
+or.na
+dr.na
+us.na
+mx.na
+ca.na
+in.na
+cc.na
+tv.na
+ws.na
+mobi.na
+co.na
+com.na
+org.na
+
+// name : has 2nd-level tlds, but there's no list of them
+name
+
+// nc : http://www.cctld.nc/
+nc
+asso.nc
+nom.nc
+
+// ne : https://en.wikipedia.org/wiki/.ne
+ne
+
+// net : https://en.wikipedia.org/wiki/.net
+net
+
+// nf : https://en.wikipedia.org/wiki/.nf
+nf
+com.nf
+net.nf
+per.nf
+rec.nf
+web.nf
+arts.nf
+firm.nf
+info.nf
+other.nf
+store.nf
+
+// ng : http://www.nira.org.ng/index.php/join-us/register-ng-domain/189-nira-slds
+ng
+com.ng
+edu.ng
+gov.ng
+i.ng
+mil.ng
+mobi.ng
+name.ng
+net.ng
+org.ng
+sch.ng
+
+// ni : http://www.nic.ni/
+ni
+ac.ni
+biz.ni
+co.ni
+com.ni
+edu.ni
+gob.ni
+in.ni
+info.ni
+int.ni
+mil.ni
+net.ni
+nom.ni
+org.ni
+web.ni
+
+// nl : https://en.wikipedia.org/wiki/.nl
+// https://www.sidn.nl/
+// ccTLD for the Netherlands
+nl
+
+// no : https://www.norid.no/en/om-domenenavn/regelverk-for-no/
+// Norid geographical second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-b/
+// Norid category second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-c/
+// Norid category second-level domains managed by parties other than Norid : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-d/
+// RSS feed: https://teknisk.norid.no/en/feed/
+no
+// Norid category second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-c/
+fhs.no
+vgs.no
+fylkesbibl.no
+folkebibl.no
+museum.no
+idrett.no
+priv.no
+// Norid category second-level domains managed by parties other than Norid : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-d/
+mil.no
+stat.no
+dep.no
+kommune.no
+herad.no
+// Norid geographical second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-b/
+// counties
+aa.no
+ah.no
+bu.no
+fm.no
+hl.no
+hm.no
+jan-mayen.no
+mr.no
+nl.no
+nt.no
+of.no
+ol.no
+oslo.no
+rl.no
+sf.no
+st.no
+svalbard.no
+tm.no
+tr.no
+va.no
+vf.no
+// primary and lower secondary schools per county
+gs.aa.no
+gs.ah.no
+gs.bu.no
+gs.fm.no
+gs.hl.no
+gs.hm.no
+gs.jan-mayen.no
+gs.mr.no
+gs.nl.no
+gs.nt.no
+gs.of.no
+gs.ol.no
+gs.oslo.no
+gs.rl.no
+gs.sf.no
+gs.st.no
+gs.svalbard.no
+gs.tm.no
+gs.tr.no
+gs.va.no
+gs.vf.no
+// cities
+akrehamn.no
+åkrehamn.no
+algard.no
+ålgård.no
+arna.no
+brumunddal.no
+bryne.no
+bronnoysund.no
+brønnøysund.no
+drobak.no
+drøbak.no
+egersund.no
+fetsund.no
+floro.no
+florø.no
+fredrikstad.no
+hokksund.no
+honefoss.no
+hønefoss.no
+jessheim.no
+jorpeland.no
+jørpeland.no
+kirkenes.no
+kopervik.no
+krokstadelva.no
+langevag.no
+langevåg.no
+leirvik.no
+mjondalen.no
+mjøndalen.no
+mo-i-rana.no
+mosjoen.no
+mosjøen.no
+nesoddtangen.no
+orkanger.no
+osoyro.no
+osøyro.no
+raholt.no
+råholt.no
+sandnessjoen.no
+sandnessjøen.no
+skedsmokorset.no
+slattum.no
+spjelkavik.no
+stathelle.no
+stavern.no
+stjordalshalsen.no
+stjørdalshalsen.no
+tananger.no
+tranby.no
+vossevangen.no
+// communities
+afjord.no
+åfjord.no
+agdenes.no
+al.no
+ål.no
+alesund.no
+ålesund.no
+alstahaug.no
+alta.no
+áltá.no
+alaheadju.no
+álaheadju.no
+alvdal.no
+amli.no
+åmli.no
+amot.no
+åmot.no
+andebu.no
+andoy.no
+andøy.no
+andasuolo.no
+ardal.no
+årdal.no
+aremark.no
+arendal.no
+ås.no
+aseral.no
+åseral.no
+asker.no
+askim.no
+askvoll.no
+askoy.no
+askøy.no
+asnes.no
+åsnes.no
+audnedaln.no
+aukra.no
+aure.no
+aurland.no
+aurskog-holand.no
+aurskog-høland.no
+austevoll.no
+austrheim.no
+averoy.no
+averøy.no
+balestrand.no
+ballangen.no
+balat.no
+bálát.no
+balsfjord.no
+bahccavuotna.no
+báhccavuotna.no
+bamble.no
+bardu.no
+beardu.no
+beiarn.no
+bajddar.no
+bájddar.no
+baidar.no
+báidár.no
+berg.no
+bergen.no
+berlevag.no
+berlevåg.no
+bearalvahki.no
+bearalváhki.no
+bindal.no
+birkenes.no
+bjarkoy.no
+bjarkøy.no
+bjerkreim.no
+bjugn.no
+bodo.no
+bodø.no
+badaddja.no
+bådåddjå.no
+budejju.no
+bokn.no
+bremanger.no
+bronnoy.no
+brønnøy.no
+bygland.no
+bykle.no
+barum.no
+bærum.no
+bo.telemark.no
+bø.telemark.no
+bo.nordland.no
+bø.nordland.no
+bievat.no
+bievát.no
+bomlo.no
+bømlo.no
+batsfjord.no
+båtsfjord.no
+bahcavuotna.no
+báhcavuotna.no
+dovre.no
+drammen.no
+drangedal.no
+dyroy.no
+dyrøy.no
+donna.no
+dønna.no
+eid.no
+eidfjord.no
+eidsberg.no
+eidskog.no
+eidsvoll.no
+eigersund.no
+elverum.no
+enebakk.no
+engerdal.no
+etne.no
+etnedal.no
+evenes.no
+evenassi.no
+evenášši.no
+evje-og-hornnes.no
+farsund.no
+fauske.no
+fuossko.no
+fuoisku.no
+fedje.no
+fet.no
+finnoy.no
+finnøy.no
+fitjar.no
+fjaler.no
+fjell.no
+flakstad.no
+flatanger.no
+flekkefjord.no
+flesberg.no
+flora.no
+fla.no
+flå.no
+folldal.no
+forsand.no
+fosnes.no
+frei.no
+frogn.no
+froland.no
+frosta.no
+frana.no
+fræna.no
+froya.no
+frøya.no
+fusa.no
+fyresdal.no
+forde.no
+førde.no
+gamvik.no
+gangaviika.no
+gáŋgaviika.no
+gaular.no
+gausdal.no
+gildeskal.no
+gildeskål.no
+giske.no
+gjemnes.no
+gjerdrum.no
+gjerstad.no
+gjesdal.no
+gjovik.no
+gjøvik.no
+gloppen.no
+gol.no
+gran.no
+grane.no
+granvin.no
+gratangen.no
+grimstad.no
+grong.no
+kraanghke.no
+kråanghke.no
+grue.no
+gulen.no
+hadsel.no
+halden.no
+halsa.no
+hamar.no
+hamaroy.no
+habmer.no
+hábmer.no
+hapmir.no
+hápmir.no
+hammerfest.no
+hammarfeasta.no
+hámmárfeasta.no
+haram.no
+hareid.no
+harstad.no
+hasvik.no
+aknoluokta.no
+ákŋoluokta.no
+hattfjelldal.no
+aarborte.no
+haugesund.no
+hemne.no
+hemnes.no
+hemsedal.no
+heroy.more-og-romsdal.no
+herøy.møre-og-romsdal.no
+heroy.nordland.no
+herøy.nordland.no
+hitra.no
+hjartdal.no
+hjelmeland.no
+hobol.no
+hobøl.no
+hof.no
+hol.no
+hole.no
+holmestrand.no
+holtalen.no
+holtålen.no
+hornindal.no
+horten.no
+hurdal.no
+hurum.no
+hvaler.no
+hyllestad.no
+hagebostad.no
+hægebostad.no
+hoyanger.no
+høyanger.no
+hoylandet.no
+høylandet.no
+ha.no
+hå.no
+ibestad.no
+inderoy.no
+inderøy.no
+iveland.no
+jevnaker.no
+jondal.no
+jolster.no
+jølster.no
+karasjok.no
+karasjohka.no
+kárášjohka.no
+karlsoy.no
+galsa.no
+gálsá.no
+karmoy.no
+karmøy.no
+kautokeino.no
+guovdageaidnu.no
+klepp.no
+klabu.no
+klæbu.no
+kongsberg.no
+kongsvinger.no
+kragero.no
+kragerø.no
+kristiansand.no
+kristiansund.no
+krodsherad.no
+krødsherad.no
+kvalsund.no
+rahkkeravju.no
+ráhkkerávju.no
+kvam.no
+kvinesdal.no
+kvinnherad.no
+kviteseid.no
+kvitsoy.no
+kvitsøy.no
+kvafjord.no
+kvæfjord.no
+giehtavuoatna.no
+kvanangen.no
+kvænangen.no
+navuotna.no
+návuotna.no
+kafjord.no
+kåfjord.no
+gaivuotna.no
+gáivuotna.no
+larvik.no
+lavangen.no
+lavagis.no
+loabat.no
+loabát.no
+lebesby.no
+davvesiida.no
+leikanger.no
+leirfjord.no
+leka.no
+leksvik.no
+lenvik.no
+leangaviika.no
+leaŋgaviika.no
+lesja.no
+levanger.no
+lier.no
+lierne.no
+lillehammer.no
+lillesand.no
+lindesnes.no
+lindas.no
+lindås.no
+lom.no
+loppa.no
+lahppi.no
+láhppi.no
+lund.no
+lunner.no
+luroy.no
+lurøy.no
+luster.no
+lyngdal.no
+lyngen.no
+ivgu.no
+lardal.no
+lerdal.no
+lærdal.no
+lodingen.no
+lødingen.no
+lorenskog.no
+lørenskog.no
+loten.no
+løten.no
+malvik.no
+masoy.no
+måsøy.no
+muosat.no
+muosát.no
+mandal.no
+marker.no
+marnardal.no
+masfjorden.no
+meland.no
+meldal.no
+melhus.no
+meloy.no
+meløy.no
+meraker.no
+meråker.no
+moareke.no
+moåreke.no
+midsund.no
+midtre-gauldal.no
+modalen.no
+modum.no
+molde.no
+moskenes.no
+moss.no
+mosvik.no
+malselv.no
+målselv.no
+malatvuopmi.no
+málatvuopmi.no
+namdalseid.no
+aejrie.no
+namsos.no
+namsskogan.no
+naamesjevuemie.no
+nååmesjevuemie.no
+laakesvuemie.no
+nannestad.no
+narvik.no
+narviika.no
+naustdal.no
+nedre-eiker.no
+nes.akershus.no
+nes.buskerud.no
+nesna.no
+nesodden.no
+nesseby.no
+unjarga.no
+unjárga.no
+nesset.no
+nissedal.no
+nittedal.no
+nord-aurdal.no
+nord-fron.no
+nord-odal.no
+norddal.no
+nordkapp.no
+davvenjarga.no
+davvenjárga.no
+nordre-land.no
+nordreisa.no
+raisa.no
+ráisa.no
+nore-og-uvdal.no
+notodden.no
+naroy.no
+nærøy.no
+notteroy.no
+nøtterøy.no
+odda.no
+oksnes.no
+øksnes.no
+oppdal.no
+oppegard.no
+oppegård.no
+orkdal.no
+orland.no
+ørland.no
+orskog.no
+ørskog.no
+orsta.no
+ørsta.no
+os.hedmark.no
+os.hordaland.no
+osen.no
+osteroy.no
+osterøy.no
+ostre-toten.no
+østre-toten.no
+overhalla.no
+ovre-eiker.no
+øvre-eiker.no
+oyer.no
+øyer.no
+oygarden.no
+øygarden.no
+oystre-slidre.no
+øystre-slidre.no
+porsanger.no
+porsangu.no
+porsáŋgu.no
+porsgrunn.no
+radoy.no
+radøy.no
+rakkestad.no
+rana.no
+ruovat.no
+randaberg.no
+rauma.no
+rendalen.no
+rennebu.no
+rennesoy.no
+rennesøy.no
+rindal.no
+ringebu.no
+ringerike.no
+ringsaker.no
+rissa.no
+risor.no
+risør.no
+roan.no
+rollag.no
+rygge.no
+ralingen.no
+rælingen.no
+rodoy.no
+rødøy.no
+romskog.no
+rømskog.no
+roros.no
+røros.no
+rost.no
+røst.no
+royken.no
+røyken.no
+royrvik.no
+røyrvik.no
+rade.no
+råde.no
+salangen.no
+siellak.no
+saltdal.no
+salat.no
+sálát.no
+sálat.no
+samnanger.no
+sande.more-og-romsdal.no
+sande.møre-og-romsdal.no
+sande.vestfold.no
+sandefjord.no
+sandnes.no
+sandoy.no
+sandøy.no
+sarpsborg.no
+sauda.no
+sauherad.no
+sel.no
+selbu.no
+selje.no
+seljord.no
+sigdal.no
+siljan.no
+sirdal.no
+skaun.no
+skedsmo.no
+ski.no
+skien.no
+skiptvet.no
+skjervoy.no
+skjervøy.no
+skierva.no
+skiervá.no
+skjak.no
+skjåk.no
+skodje.no
+skanland.no
+skånland.no
+skanit.no
+skánit.no
+smola.no
+smøla.no
+snillfjord.no
+snasa.no
+snåsa.no
+snoasa.no
+snaase.no
+snåase.no
+sogndal.no
+sokndal.no
+sola.no
+solund.no
+songdalen.no
+sortland.no
+spydeberg.no
+stange.no
+stavanger.no
+steigen.no
+steinkjer.no
+stjordal.no
+stjørdal.no
+stokke.no
+stor-elvdal.no
+stord.no
+stordal.no
+storfjord.no
+omasvuotna.no
+strand.no
+stranda.no
+stryn.no
+sula.no
+suldal.no
+sund.no
+sunndal.no
+surnadal.no
+sveio.no
+svelvik.no
+sykkylven.no
+sogne.no
+søgne.no
+somna.no
+sømna.no
+sondre-land.no
+søndre-land.no
+sor-aurdal.no
+sør-aurdal.no
+sor-fron.no
+sør-fron.no
+sor-odal.no
+sør-odal.no
+sor-varanger.no
+sør-varanger.no
+matta-varjjat.no
+mátta-várjjat.no
+sorfold.no
+sørfold.no
+sorreisa.no
+sørreisa.no
+sorum.no
+sørum.no
+tana.no
+deatnu.no
+time.no
+tingvoll.no
+tinn.no
+tjeldsund.no
+dielddanuorri.no
+tjome.no
+tjøme.no
+tokke.no
+tolga.no
+torsken.no
+tranoy.no
+tranøy.no
+tromso.no
+tromsø.no
+tromsa.no
+romsa.no
+trondheim.no
+troandin.no
+trysil.no
+trana.no
+træna.no
+trogstad.no
+trøgstad.no
+tvedestrand.no
+tydal.no
+tynset.no
+tysfjord.no
+divtasvuodna.no
+divttasvuotna.no
+tysnes.no
+tysvar.no
+tysvær.no
+tonsberg.no
+tønsberg.no
+ullensaker.no
+ullensvang.no
+ulvik.no
+utsira.no
+vadso.no
+vadsø.no
+cahcesuolo.no
+čáhcesuolo.no
+vaksdal.no
+valle.no
+vang.no
+vanylven.no
+vardo.no
+vardø.no
+varggat.no
+várggát.no
+vefsn.no
+vaapste.no
+vega.no
+vegarshei.no
+vegårshei.no
+vennesla.no
+verdal.no
+verran.no
+vestby.no
+vestnes.no
+vestre-slidre.no
+vestre-toten.no
+vestvagoy.no
+vestvågøy.no
+vevelstad.no
+vik.no
+vikna.no
+vindafjord.no
+volda.no
+voss.no
+varoy.no
+værøy.no
+vagan.no
+vågan.no
+voagat.no
+vagsoy.no
+vågsøy.no
+vaga.no
+vågå.no
+valer.ostfold.no
+våler.østfold.no
+valer.hedmark.no
+våler.hedmark.no
+
+// np : http://www.mos.com.np/register.html
+*.np
+
+// nr : http://cenpac.net.nr/dns/index.html
+// Submitted by registry <technician@cenpac.net.nr>
+nr
+biz.nr
+info.nr
+gov.nr
+edu.nr
+org.nr
+net.nr
+com.nr
+
+// nu : https://en.wikipedia.org/wiki/.nu
+nu
+
+// nz : https://en.wikipedia.org/wiki/.nz
+// Submitted by registry <jay@nzrs.net.nz>
+nz
+ac.nz
+co.nz
+cri.nz
+geek.nz
+gen.nz
+govt.nz
+health.nz
+iwi.nz
+kiwi.nz
+maori.nz
+mil.nz
+māori.nz
+net.nz
+org.nz
+parliament.nz
+school.nz
+
+// om : https://en.wikipedia.org/wiki/.om
+om
+co.om
+com.om
+edu.om
+gov.om
+med.om
+museum.om
+net.om
+org.om
+pro.om
+
+// onion : https://tools.ietf.org/html/rfc7686
+onion
+
+// org : https://en.wikipedia.org/wiki/.org
+org
+
+// pa : http://www.nic.pa/
+// Some additional second level "domains" resolve directly as hostnames, such as
+// pannet.pa, so we add a rule for "pa".
+pa
+ac.pa
+gob.pa
+com.pa
+org.pa
+sld.pa
+edu.pa
+net.pa
+ing.pa
+abo.pa
+med.pa
+nom.pa
+
+// pe : https://www.nic.pe/InformeFinalComision.pdf
+pe
+edu.pe
+gob.pe
+nom.pe
+mil.pe
+org.pe
+com.pe
+net.pe
+
+// pf : http://www.gobin.info/domainname/formulaire-pf.pdf
+pf
+com.pf
+org.pf
+edu.pf
+
+// pg : https://en.wikipedia.org/wiki/.pg
+*.pg
+
+// ph : http://www.domains.ph/FAQ2.asp
+// Submitted by registry <jed@email.com.ph>
+ph
+com.ph
+net.ph
+org.ph
+gov.ph
+edu.ph
+ngo.ph
+mil.ph
+i.ph
+
+// pk : http://pk5.pknic.net.pk/pk5/msgNamepk.PK
+pk
+com.pk
+net.pk
+edu.pk
+org.pk
+fam.pk
+biz.pk
+web.pk
+gov.pk
+gob.pk
+gok.pk
+gon.pk
+gop.pk
+gos.pk
+info.pk
+
+// pl http://www.dns.pl/english/index.html
+// Submitted by registry
+pl
+com.pl
+net.pl
+org.pl
+// pl functional domains (http://www.dns.pl/english/index.html)
+aid.pl
+agro.pl
+atm.pl
+auto.pl
+biz.pl
+edu.pl
+gmina.pl
+gsm.pl
+info.pl
+mail.pl
+miasta.pl
+media.pl
+mil.pl
+nieruchomosci.pl
+nom.pl
+pc.pl
+powiat.pl
+priv.pl
+realestate.pl
+rel.pl
+sex.pl
+shop.pl
+sklep.pl
+sos.pl
+szkola.pl
+targi.pl
+tm.pl
+tourism.pl
+travel.pl
+turystyka.pl
+// Government domains
+gov.pl
+ap.gov.pl
+griw.gov.pl
+ic.gov.pl
+is.gov.pl
+kmpsp.gov.pl
+konsulat.gov.pl
+kppsp.gov.pl
+kwp.gov.pl
+kwpsp.gov.pl
+mup.gov.pl
+mw.gov.pl
+oia.gov.pl
+oirm.gov.pl
+oke.gov.pl
+oow.gov.pl
+oschr.gov.pl
+oum.gov.pl
+pa.gov.pl
+pinb.gov.pl
+piw.gov.pl
+po.gov.pl
+pr.gov.pl
+psp.gov.pl
+psse.gov.pl
+pup.gov.pl
+rzgw.gov.pl
+sa.gov.pl
+sdn.gov.pl
+sko.gov.pl
+so.gov.pl
+sr.gov.pl
+starostwo.gov.pl
+ug.gov.pl
+ugim.gov.pl
+um.gov.pl
+umig.gov.pl
+upow.gov.pl
+uppo.gov.pl
+us.gov.pl
+uw.gov.pl
+uzs.gov.pl
+wif.gov.pl
+wiih.gov.pl
+winb.gov.pl
+wios.gov.pl
+witd.gov.pl
+wiw.gov.pl
+wkz.gov.pl
+wsa.gov.pl
+wskr.gov.pl
+wsse.gov.pl
+wuoz.gov.pl
+wzmiuw.gov.pl
+zp.gov.pl
+zpisdn.gov.pl
+// pl regional domains (http://www.dns.pl/english/index.html)
+augustow.pl
+babia-gora.pl
+bedzin.pl
+beskidy.pl
+bialowieza.pl
+bialystok.pl
+bielawa.pl
+bieszczady.pl
+boleslawiec.pl
+bydgoszcz.pl
+bytom.pl
+cieszyn.pl
+czeladz.pl
+czest.pl
+dlugoleka.pl
+elblag.pl
+elk.pl
+glogow.pl
+gniezno.pl
+gorlice.pl
+grajewo.pl
+ilawa.pl
+jaworzno.pl
+jelenia-gora.pl
+jgora.pl
+kalisz.pl
+kazimierz-dolny.pl
+karpacz.pl
+kartuzy.pl
+kaszuby.pl
+katowice.pl
+kepno.pl
+ketrzyn.pl
+klodzko.pl
+kobierzyce.pl
+kolobrzeg.pl
+konin.pl
+konskowola.pl
+kutno.pl
+lapy.pl
+lebork.pl
+legnica.pl
+lezajsk.pl
+limanowa.pl
+lomza.pl
+lowicz.pl
+lubin.pl
+lukow.pl
+malbork.pl
+malopolska.pl
+mazowsze.pl
+mazury.pl
+mielec.pl
+mielno.pl
+mragowo.pl
+naklo.pl
+nowaruda.pl
+nysa.pl
+olawa.pl
+olecko.pl
+olkusz.pl
+olsztyn.pl
+opoczno.pl
+opole.pl
+ostroda.pl
+ostroleka.pl
+ostrowiec.pl
+ostrowwlkp.pl
+pila.pl
+pisz.pl
+podhale.pl
+podlasie.pl
+polkowice.pl
+pomorze.pl
+pomorskie.pl
+prochowice.pl
+pruszkow.pl
+przeworsk.pl
+pulawy.pl
+radom.pl
+rawa-maz.pl
+rybnik.pl
+rzeszow.pl
+sanok.pl
+sejny.pl
+slask.pl
+slupsk.pl
+sosnowiec.pl
+stalowa-wola.pl
+skoczow.pl
+starachowice.pl
+stargard.pl
+suwalki.pl
+swidnica.pl
+swiebodzin.pl
+swinoujscie.pl
+szczecin.pl
+szczytno.pl
+tarnobrzeg.pl
+tgory.pl
+turek.pl
+tychy.pl
+ustka.pl
+walbrzych.pl
+warmia.pl
+warszawa.pl
+waw.pl
+wegrow.pl
+wielun.pl
+wlocl.pl
+wloclawek.pl
+wodzislaw.pl
+wolomin.pl
+wroclaw.pl
+zachpomor.pl
+zagan.pl
+zarow.pl
+zgora.pl
+zgorzelec.pl
+
+// pm : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf
+pm
+
+// pn : http://www.government.pn/PnRegistry/policies.htm
+pn
+gov.pn
+co.pn
+org.pn
+edu.pn
+net.pn
+
+// post : https://en.wikipedia.org/wiki/.post
+post
+
+// pr : http://www.nic.pr/index.asp?f=1
+pr
+com.pr
+net.pr
+org.pr
+gov.pr
+edu.pr
+isla.pr
+pro.pr
+biz.pr
+info.pr
+name.pr
+// these aren't mentioned on nic.pr, but on https://en.wikipedia.org/wiki/.pr
+est.pr
+prof.pr
+ac.pr
+
+// pro : http://registry.pro/get-pro
+pro
+aaa.pro
+aca.pro
+acct.pro
+avocat.pro
+bar.pro
+cpa.pro
+eng.pro
+jur.pro
+law.pro
+med.pro
+recht.pro
+
+// ps : https://en.wikipedia.org/wiki/.ps
+// http://www.nic.ps/registration/policy.html#reg
+ps
+edu.ps
+gov.ps
+sec.ps
+plo.ps
+com.ps
+org.ps
+net.ps
+
+// pt : https://www.dns.pt/en/domain/pt-terms-and-conditions-registration-rules/
+pt
+net.pt
+gov.pt
+org.pt
+edu.pt
+int.pt
+publ.pt
+com.pt
+nome.pt
+
+// pw : https://en.wikipedia.org/wiki/.pw
+pw
+co.pw
+ne.pw
+or.pw
+ed.pw
+go.pw
+belau.pw
+
+// py : http://www.nic.py/pautas.html#seccion_9
+// Submitted by registry
+py
+com.py
+coop.py
+edu.py
+gov.py
+mil.py
+net.py
+org.py
+
+// qa : http://domains.qa/en/
+qa
+com.qa
+edu.qa
+gov.qa
+mil.qa
+name.qa
+net.qa
+org.qa
+sch.qa
+
+// re : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf
+re
+asso.re
+com.re
+nom.re
+
+// ro : http://www.rotld.ro/
+ro
+arts.ro
+com.ro
+firm.ro
+info.ro
+nom.ro
+nt.ro
+org.ro
+rec.ro
+store.ro
+tm.ro
+www.ro
+
+// rs : https://www.rnids.rs/en/domains/national-domains
+rs
+ac.rs
+co.rs
+edu.rs
+gov.rs
+in.rs
+org.rs
+
+// ru : https://cctld.ru/files/pdf/docs/en/rules_ru-rf.pdf
+// Submitted by George Georgievsky <gug@cctld.ru>
+ru
+
+// rw : https://www.ricta.org.rw/sites/default/files/resources/registry_registrar_contract_0.pdf
+rw
+ac.rw
+co.rw
+coop.rw
+gov.rw
+mil.rw
+net.rw
+org.rw
+
+// sa : http://www.nic.net.sa/
+sa
+com.sa
+net.sa
+org.sa
+gov.sa
+med.sa
+pub.sa
+edu.sa
+sch.sa
+
+// sb : http://www.sbnic.net.sb/
+// Submitted by registry <lee.humphries@telekom.com.sb>
+sb
+com.sb
+edu.sb
+gov.sb
+net.sb
+org.sb
+
+// sc : http://www.nic.sc/
+sc
+com.sc
+gov.sc
+net.sc
+org.sc
+edu.sc
+
+// sd : http://www.isoc.sd/sudanic.isoc.sd/billing_pricing.htm
+// Submitted by registry <admin@isoc.sd>
+sd
+com.sd
+net.sd
+org.sd
+edu.sd
+med.sd
+tv.sd
+gov.sd
+info.sd
+
+// se : https://en.wikipedia.org/wiki/.se
+// Submitted by registry <patrik.wallstrom@iis.se>
+se
+a.se
+ac.se
+b.se
+bd.se
+brand.se
+c.se
+d.se
+e.se
+f.se
+fh.se
+fhsk.se
+fhv.se
+g.se
+h.se
+i.se
+k.se
+komforb.se
+kommunalforbund.se
+komvux.se
+l.se
+lanbib.se
+m.se
+n.se
+naturbruksgymn.se
+o.se
+org.se
+p.se
+parti.se
+pp.se
+press.se
+r.se
+s.se
+t.se
+tm.se
+u.se
+w.se
+x.se
+y.se
+z.se
+
+// sg : http://www.nic.net.sg/page/registration-policies-procedures-and-guidelines
+sg
+com.sg
+net.sg
+org.sg
+gov.sg
+edu.sg
+per.sg
+
+// sh : http://nic.sh/rules.htm
+sh
+com.sh
+net.sh
+gov.sh
+org.sh
+mil.sh
+
+// si : https://en.wikipedia.org/wiki/.si
+si
+
+// sj : No registrations at this time.
+// Submitted by registry <jarle@uninett.no>
+sj
+
+// sk : https://en.wikipedia.org/wiki/.sk
+// list of 2nd level domains ?
+sk
+
+// sl : http://www.nic.sl
+// Submitted by registry <adam@neoip.com>
+sl
+com.sl
+net.sl
+edu.sl
+gov.sl
+org.sl
+
+// sm : https://en.wikipedia.org/wiki/.sm
+sm
+
+// sn : https://en.wikipedia.org/wiki/.sn
+sn
+art.sn
+com.sn
+edu.sn
+gouv.sn
+org.sn
+perso.sn
+univ.sn
+
+// so : http://sonic.so/policies/
+so
+com.so
+edu.so
+gov.so
+me.so
+net.so
+org.so
+
+// sr : https://en.wikipedia.org/wiki/.sr
+sr
+
+// ss : https://registry.nic.ss/
+// Submitted by registry <technical@nic.ss>
+ss
+biz.ss
+com.ss
+edu.ss
+gov.ss
+me.ss
+net.ss
+org.ss
+sch.ss
+
+// st : http://www.nic.st/html/policyrules/
+st
+co.st
+com.st
+consulado.st
+edu.st
+embaixada.st
+mil.st
+net.st
+org.st
+principe.st
+saotome.st
+store.st
+
+// su : https://en.wikipedia.org/wiki/.su
+su
+
+// sv : http://www.svnet.org.sv/niveldos.pdf
+sv
+com.sv
+edu.sv
+gob.sv
+org.sv
+red.sv
+
+// sx : https://en.wikipedia.org/wiki/.sx
+// Submitted by registry <jcvignes@openregistry.com>
+sx
+gov.sx
+
+// sy : https://en.wikipedia.org/wiki/.sy
+// see also: http://www.gobin.info/domainname/sy.doc
+sy
+edu.sy
+gov.sy
+net.sy
+mil.sy
+com.sy
+org.sy
+
+// sz : https://en.wikipedia.org/wiki/.sz
+// http://www.sispa.org.sz/
+sz
+co.sz
+ac.sz
+org.sz
+
+// tc : https://en.wikipedia.org/wiki/.tc
+tc
+
+// td : https://en.wikipedia.org/wiki/.td
+td
+
+// tel: https://en.wikipedia.org/wiki/.tel
+// http://www.telnic.org/
+tel
+
+// tf : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf
+tf
+
+// tg : https://en.wikipedia.org/wiki/.tg
+// http://www.nic.tg/
+tg
+
+// th : https://en.wikipedia.org/wiki/.th
+// Submitted by registry <krit@thains.co.th>
+th
+ac.th
+co.th
+go.th
+in.th
+mi.th
+net.th
+or.th
+
+// tj : http://www.nic.tj/policy.html
+tj
+ac.tj
+biz.tj
+co.tj
+com.tj
+edu.tj
+go.tj
+gov.tj
+int.tj
+mil.tj
+name.tj
+net.tj
+nic.tj
+org.tj
+test.tj
+web.tj
+
+// tk : https://en.wikipedia.org/wiki/.tk
+tk
+
+// tl : https://en.wikipedia.org/wiki/.tl
+tl
+gov.tl
+
+// tm : http://www.nic.tm/local.html
+tm
+com.tm
+co.tm
+org.tm
+net.tm
+nom.tm
+gov.tm
+mil.tm
+edu.tm
+
+// tn : http://www.registre.tn/fr/
+// https://whois.ati.tn/
+tn
+com.tn
+ens.tn
+fin.tn
+gov.tn
+ind.tn
+info.tn
+intl.tn
+mincom.tn
+nat.tn
+net.tn
+org.tn
+perso.tn
+tourism.tn
+
+// to : https://en.wikipedia.org/wiki/.to
+// Submitted by registry <egullich@colo.to>
+to
+com.to
+gov.to
+net.to
+org.to
+edu.to
+mil.to
+
+// tr : https://nic.tr/
+// https://nic.tr/forms/eng/policies.pdf
+// https://nic.tr/index.php?USRACTN=PRICELST
+tr
+av.tr
+bbs.tr
+bel.tr
+biz.tr
+com.tr
+dr.tr
+edu.tr
+gen.tr
+gov.tr
+info.tr
+mil.tr
+k12.tr
+kep.tr
+name.tr
+net.tr
+org.tr
+pol.tr
+tel.tr
+tsk.tr
+tv.tr
+web.tr
+// Used by Northern Cyprus
+nc.tr
+// Used by government agencies of Northern Cyprus
+gov.nc.tr
+
+// tt : http://www.nic.tt/
+tt
+co.tt
+com.tt
+org.tt
+net.tt
+biz.tt
+info.tt
+pro.tt
+int.tt
+coop.tt
+jobs.tt
+mobi.tt
+travel.tt
+museum.tt
+aero.tt
+name.tt
+gov.tt
+edu.tt
+
+// tv : https://en.wikipedia.org/wiki/.tv
+// Not listing any 2LDs as reserved since none seem to exist in practice,
+// Wikipedia notwithstanding.
+tv
+
+// tw : https://en.wikipedia.org/wiki/.tw
+tw
+edu.tw
+gov.tw
+mil.tw
+com.tw
+net.tw
+org.tw
+idv.tw
+game.tw
+ebiz.tw
+club.tw
+網路.tw
+組織.tw
+商業.tw
+
+// tz : http://www.tznic.or.tz/index.php/domains
+// Submitted by registry <manager@tznic.or.tz>
+tz
+ac.tz
+co.tz
+go.tz
+hotel.tz
+info.tz
+me.tz
+mil.tz
+mobi.tz
+ne.tz
+or.tz
+sc.tz
+tv.tz
+
+// ua : https://hostmaster.ua/policy/?ua
+// Submitted by registry <dk@cctld.ua>
+ua
+// ua 2LD
+com.ua
+edu.ua
+gov.ua
+in.ua
+net.ua
+org.ua
+// ua geographic names
+// https://hostmaster.ua/2ld/
+cherkassy.ua
+cherkasy.ua
+chernigov.ua
+chernihiv.ua
+chernivtsi.ua
+chernovtsy.ua
+ck.ua
+cn.ua
+cr.ua
+crimea.ua
+cv.ua
+dn.ua
+dnepropetrovsk.ua
+dnipropetrovsk.ua
+donetsk.ua
+dp.ua
+if.ua
+ivano-frankivsk.ua
+kh.ua
+kharkiv.ua
+kharkov.ua
+kherson.ua
+khmelnitskiy.ua
+khmelnytskyi.ua
+kiev.ua
+kirovograd.ua
+km.ua
+kr.ua
+kropyvnytskyi.ua
+krym.ua
+ks.ua
+kv.ua
+kyiv.ua
+lg.ua
+lt.ua
+lugansk.ua
+luhansk.ua
+lutsk.ua
+lv.ua
+lviv.ua
+mk.ua
+mykolaiv.ua
+nikolaev.ua
+od.ua
+odesa.ua
+odessa.ua
+pl.ua
+poltava.ua
+rivne.ua
+rovno.ua
+rv.ua
+sb.ua
+sebastopol.ua
+sevastopol.ua
+sm.ua
+sumy.ua
+te.ua
+ternopil.ua
+uz.ua
+uzhgorod.ua
+uzhhorod.ua
+vinnica.ua
+vinnytsia.ua
+vn.ua
+volyn.ua
+yalta.ua
+zakarpattia.ua
+zaporizhzhe.ua
+zaporizhzhia.ua
+zhitomir.ua
+zhytomyr.ua
+zp.ua
+zt.ua
+
+// ug : https://www.registry.co.ug/
+ug
+co.ug
+or.ug
+ac.ug
+sc.ug
+go.ug
+ne.ug
+com.ug
+org.ug
+
+// uk : https://en.wikipedia.org/wiki/.uk
+// Submitted by registry <Michael.Daly@nominet.org.uk>
+uk
+ac.uk
+co.uk
+gov.uk
+ltd.uk
+me.uk
+net.uk
+nhs.uk
+org.uk
+plc.uk
+police.uk
+*.sch.uk
+
+// us : https://en.wikipedia.org/wiki/.us
+us
+dni.us
+fed.us
+isa.us
+kids.us
+nsn.us
+// us geographic names
+ak.us
+al.us
+ar.us
+as.us
+az.us
+ca.us
+co.us
+ct.us
+dc.us
+de.us
+fl.us
+ga.us
+gu.us
+hi.us
+ia.us
+id.us
+il.us
+in.us
+ks.us
+ky.us
+la.us
+ma.us
+md.us
+me.us
+mi.us
+mn.us
+mo.us
+ms.us
+mt.us
+nc.us
+nd.us
+ne.us
+nh.us
+nj.us
+nm.us
+nv.us
+ny.us
+oh.us
+ok.us
+or.us
+pa.us
+pr.us
+ri.us
+sc.us
+sd.us
+tn.us
+tx.us
+ut.us
+vi.us
+vt.us
+va.us
+wa.us
+wi.us
+wv.us
+wy.us
+// The registrar notes several more specific domains available in each state,
+// such as state.*.us, dst.*.us, etc., but resolution of these is somewhat
+// haphazard; in some states these domains resolve as addresses, while in others
+// only subdomains are available, or even nothing at all. We include the
+// most common ones where it's clear that different sites are different
+// entities.
+k12.ak.us
+k12.al.us
+k12.ar.us
+k12.as.us
+k12.az.us
+k12.ca.us
+k12.co.us
+k12.ct.us
+k12.dc.us
+k12.fl.us
+k12.ga.us
+k12.gu.us
+// k12.hi.us Bug 614565 - Hawaii has a state-wide DOE login
+k12.ia.us
+k12.id.us
+k12.il.us
+k12.in.us
+k12.ks.us
+k12.ky.us
+k12.la.us
+k12.ma.us
+k12.md.us
+k12.me.us
+k12.mi.us
+k12.mn.us
+k12.mo.us
+k12.ms.us
+k12.mt.us
+k12.nc.us
+// k12.nd.us Bug 1028347 - Removed at request of Travis Rosso <trossow@nd.gov>
+k12.ne.us
+k12.nh.us
+k12.nj.us
+k12.nm.us
+k12.nv.us
+k12.ny.us
+k12.oh.us
+k12.ok.us
+k12.or.us
+k12.pa.us
+k12.pr.us
+// k12.ri.us Removed at request of Kim Cournoyer <netsupport@staff.ri.net>
+k12.sc.us
+// k12.sd.us Bug 934131 - Removed at request of James Booze <James.Booze@k12.sd.us>
+k12.tn.us
+k12.tx.us
+k12.ut.us
+k12.vi.us
+k12.vt.us
+k12.va.us
+k12.wa.us
+k12.wi.us
+// k12.wv.us Bug 947705 - Removed at request of Verne Britton <verne@wvnet.edu>
+k12.wy.us
+cc.ak.us
+cc.al.us
+cc.ar.us
+cc.as.us
+cc.az.us
+cc.ca.us
+cc.co.us
+cc.ct.us
+cc.dc.us
+cc.de.us
+cc.fl.us
+cc.ga.us
+cc.gu.us
+cc.hi.us
+cc.ia.us
+cc.id.us
+cc.il.us
+cc.in.us
+cc.ks.us
+cc.ky.us
+cc.la.us
+cc.ma.us
+cc.md.us
+cc.me.us
+cc.mi.us
+cc.mn.us
+cc.mo.us
+cc.ms.us
+cc.mt.us
+cc.nc.us
+cc.nd.us
+cc.ne.us
+cc.nh.us
+cc.nj.us
+cc.nm.us
+cc.nv.us
+cc.ny.us
+cc.oh.us
+cc.ok.us
+cc.or.us
+cc.pa.us
+cc.pr.us
+cc.ri.us
+cc.sc.us
+cc.sd.us
+cc.tn.us
+cc.tx.us
+cc.ut.us
+cc.vi.us
+cc.vt.us
+cc.va.us
+cc.wa.us
+cc.wi.us
+cc.wv.us
+cc.wy.us
+lib.ak.us
+lib.al.us
+lib.ar.us
+lib.as.us
+lib.az.us
+lib.ca.us
+lib.co.us
+lib.ct.us
+lib.dc.us
+// lib.de.us Issue #243 - Moved to Private section at request of Ed Moore <Ed.Moore@lib.de.us>
+lib.fl.us
+lib.ga.us
+lib.gu.us
+lib.hi.us
+lib.ia.us
+lib.id.us
+lib.il.us
+lib.in.us
+lib.ks.us
+lib.ky.us
+lib.la.us
+lib.ma.us
+lib.md.us
+lib.me.us
+lib.mi.us
+lib.mn.us
+lib.mo.us
+lib.ms.us
+lib.mt.us
+lib.nc.us
+lib.nd.us
+lib.ne.us
+lib.nh.us
+lib.nj.us
+lib.nm.us
+lib.nv.us
+lib.ny.us
+lib.oh.us
+lib.ok.us
+lib.or.us
+lib.pa.us
+lib.pr.us
+lib.ri.us
+lib.sc.us
+lib.sd.us
+lib.tn.us
+lib.tx.us
+lib.ut.us
+lib.vi.us
+lib.vt.us
+lib.va.us
+lib.wa.us
+lib.wi.us
+// lib.wv.us Bug 941670 - Removed at request of Larry W Arnold <arnold@wvlc.lib.wv.us>
+lib.wy.us
+// k12.ma.us contains school districts in Massachusetts. The 4LDs are
+// managed independently except for private (PVT), charter (CHTR) and
+// parochial (PAROCH) schools. Those are delegated directly to the
+// 5LD operators. <k12-ma-hostmaster _ at _ rsuc.gweep.net>
+pvt.k12.ma.us
+chtr.k12.ma.us
+paroch.k12.ma.us
+// Merit Network, Inc. maintains the registry for =~ /(k12|cc|lib).mi.us/ and the following
+// see also: http://domreg.merit.edu
+// see also: whois -h whois.domreg.merit.edu help
+ann-arbor.mi.us
+cog.mi.us
+dst.mi.us
+eaton.mi.us
+gen.mi.us
+mus.mi.us
+tec.mi.us
+washtenaw.mi.us
+
+// uy : http://www.nic.org.uy/
+uy
+com.uy
+edu.uy
+gub.uy
+mil.uy
+net.uy
+org.uy
+
+// uz : http://www.reg.uz/
+uz
+co.uz
+com.uz
+net.uz
+org.uz
+
+// va : https://en.wikipedia.org/wiki/.va
+va
+
+// vc : https://en.wikipedia.org/wiki/.vc
+// Submitted by registry <kshah@ca.afilias.info>
+vc
+com.vc
+net.vc
+org.vc
+gov.vc
+mil.vc
+edu.vc
+
+// ve : https://registro.nic.ve/
+// Submitted by registry nic@nic.ve and nicve@conatel.gob.ve
+ve
+arts.ve
+bib.ve
+co.ve
+com.ve
+e12.ve
+edu.ve
+firm.ve
+gob.ve
+gov.ve
+info.ve
+int.ve
+mil.ve
+net.ve
+nom.ve
+org.ve
+rar.ve
+rec.ve
+store.ve
+tec.ve
+web.ve
+
+// vg : https://en.wikipedia.org/wiki/.vg
+vg
+
+// vi : http://www.nic.vi/newdomainform.htm
+// http://www.nic.vi/Domain_Rules/body_domain_rules.html indicates some other
+// TLDs are "reserved", such as edu.vi and gov.vi, but doesn't actually say they
+// are available for registration (which they do not seem to be).
+vi
+co.vi
+com.vi
+k12.vi
+net.vi
+org.vi
+
+// vn : https://www.vnnic.vn/en/domain/cctld-vn
+// https://vnnic.vn/sites/default/files/tailieu/vn.cctld.domains.txt
+vn
+ac.vn
+ai.vn
+biz.vn
+com.vn
+edu.vn
+gov.vn
+health.vn
+id.vn
+info.vn
+int.vn
+io.vn
+name.vn
+net.vn
+org.vn
+pro.vn
+
+// vn geographical names
+angiang.vn
+bacgiang.vn
+backan.vn
+baclieu.vn
+bacninh.vn
+baria-vungtau.vn
+bentre.vn
+binhdinh.vn
+binhduong.vn
+binhphuoc.vn
+binhthuan.vn
+camau.vn
+cantho.vn
+caobang.vn
+daklak.vn
+daknong.vn
+danang.vn
+dienbien.vn
+dongnai.vn
+dongthap.vn
+gialai.vn
+hagiang.vn
+haiduong.vn
+haiphong.vn
+hanam.vn
+hanoi.vn
+hatinh.vn
+haugiang.vn
+hoabinh.vn
+hungyen.vn
+khanhhoa.vn
+kiengiang.vn
+kontum.vn
+laichau.vn
+lamdong.vn
+langson.vn
+laocai.vn
+longan.vn
+namdinh.vn
+nghean.vn
+ninhbinh.vn
+ninhthuan.vn
+phutho.vn
+phuyen.vn
+quangbinh.vn
+quangnam.vn
+quangngai.vn
+quangninh.vn
+quangtri.vn
+soctrang.vn
+sonla.vn
+tayninh.vn
+thaibinh.vn
+thainguyen.vn
+thanhhoa.vn
+thanhphohochiminh.vn
+thuathienhue.vn
+tiengiang.vn
+travinh.vn
+tuyenquang.vn
+vinhlong.vn
+vinhphuc.vn
+yenbai.vn
+
+// vu : https://en.wikipedia.org/wiki/.vu
+// http://www.vunic.vu/
+vu
+com.vu
+edu.vu
+net.vu
+org.vu
+
+// wf : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf
+wf
+
+// ws : https://en.wikipedia.org/wiki/.ws
+// http://samoanic.ws/index.dhtml
+ws
+com.ws
+net.ws
+org.ws
+gov.ws
+edu.ws
+
+// yt : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf
+yt
+
+// IDN ccTLDs
+// When submitting patches, please maintain a sort by ISO 3166 ccTLD, then
+// U-label, and follow this format:
+// // A-Label ("<Latin renderings>", <language name>[, variant info]) : <ISO 3166 ccTLD>
+// // [sponsoring org]
+// U-Label
+
+// xn--mgbaam7a8h ("Emerat", Arabic) : AE
+// http://nic.ae/english/arabicdomain/rules.jsp
+امارات
+
+// xn--y9a3aq ("hye", Armenian) : AM
+// ISOC AM (operated by .am Registry)
+հայ
+
+// xn--54b7fta0cc ("Bangla", Bangla) : BD
+বাংলা
+
+// xn--90ae ("bg", Bulgarian) : BG
+бг
+
+// xn--mgbcpq6gpa1a ("albahrain", Arabic) : BH
+البحرين
+
+// xn--90ais ("bel", Belarusian/Russian Cyrillic) : BY
+// Operated by .by registry
+бел
+
+// xn--fiqs8s ("Zhongguo/China", Chinese, Simplified) : CN
+// CNNIC
+// http://cnnic.cn/html/Dir/2005/10/11/3218.htm
+中国
+
+// xn--fiqz9s ("Zhongguo/China", Chinese, Traditional) : CN
+// CNNIC
+// http://cnnic.cn/html/Dir/2005/10/11/3218.htm
+中國
+
+// xn--lgbbat1ad8j ("Algeria/Al Jazair", Arabic) : DZ
+الجزائر
+
+// xn--wgbh1c ("Egypt/Masr", Arabic) : EG
+// http://www.dotmasr.eg/
+مصر
+
+// xn--e1a4c ("eu", Cyrillic) : EU
+// https://eurid.eu
+ею
+
+// xn--qxa6a ("eu", Greek) : EU
+// https://eurid.eu
+ευ
+
+// xn--mgbah1a3hjkrd ("Mauritania", Arabic) : MR
+موريتانيا
+
+// xn--node ("ge", Georgian Mkhedruli) : GE
+გე
+
+// xn--qxam ("el", Greek) : GR
+// Hellenic Ministry of Infrastructure, Transport, and Networks
+ελ
+
+// xn--j6w193g ("Hong Kong", Chinese) : HK
+// https://www.hkirc.hk
+// Submitted by registry <hk.tech@hkirc.hk>
+// https://www.hkirc.hk/content.jsp?id=30#!/34
+香港
+公司.香港
+教育.香港
+政府.香港
+個人.香港
+網絡.香港
+組織.香港
+
+// xn--2scrj9c ("Bharat", Kannada) : IN
+// India
+ಭಾರತ
+
+// xn--3hcrj9c ("Bharat", Oriya) : IN
+// India
+ଭାରତ
+
+// xn--45br5cyl ("Bharatam", Assamese) : IN
+// India
+ভাৰত
+
+// xn--h2breg3eve ("Bharatam", Sanskrit) : IN
+// India
+भारतम्
+
+// xn--h2brj9c8c ("Bharot", Santali) : IN
+// India
+भारोत
+
+// xn--mgbgu82a ("Bharat", Sindhi) : IN
+// India
+ڀارت
+
+// xn--rvc1e0am3e ("Bharatam", Malayalam) : IN
+// India
+ഭാരതം
+
+// xn--h2brj9c ("Bharat", Devanagari) : IN
+// India
+भारत
+
+// xn--mgbbh1a ("Bharat", Kashmiri) : IN
+// India
+بارت
+
+// xn--mgbbh1a71e ("Bharat", Arabic) : IN
+// India
+بھارت
+
+// xn--fpcrj9c3d ("Bharat", Telugu) : IN
+// India
+భారత్
+
+// xn--gecrj9c ("Bharat", Gujarati) : IN
+// India
+ભારત
+
+// xn--s9brj9c ("Bharat", Gurmukhi) : IN
+// India
+ਭਾਰਤ
+
+// xn--45brj9c ("Bharat", Bengali) : IN
+// India
+ভারত
+
+// xn--xkc2dl3a5ee0h ("India", Tamil) : IN
+// India
+இந்தியா
+
+// xn--mgba3a4f16a ("Iran", Persian) : IR
+ایران
+
+// xn--mgba3a4fra ("Iran", Arabic) : IR
+ايران
+
+// xn--mgbtx2b ("Iraq", Arabic) : IQ
+// Communications and Media Commission
+عراق
+
+// xn--mgbayh7gpa ("al-Ordon", Arabic) : JO
+// National Information Technology Center (NITC)
+// Royal Scientific Society, Al-Jubeiha
+الاردن
+
+// xn--3e0b707e ("Republic of Korea", Hangul) : KR
+한국
+
+// xn--80ao21a ("Kaz", Kazakh) : KZ
+қаз
+
+// xn--q7ce6a ("Lao", Lao) : LA
+ລາວ
+
+// xn--fzc2c9e2c ("Lanka", Sinhalese-Sinhala) : LK
+// https://nic.lk
+ලංකා
+
+// xn--xkc2al3hye2a ("Ilangai", Tamil) : LK
+// https://nic.lk
+இலங்கை
+
+// xn--mgbc0a9azcg ("Morocco/al-Maghrib", Arabic) : MA
+المغرب
+
+// xn--d1alf ("mkd", Macedonian) : MK
+// MARnet
+мкд
+
+// xn--l1acc ("mon", Mongolian) : MN
+мон
+
+// xn--mix891f ("Macao", Chinese, Traditional) : MO
+// MONIC / HNET Asia (Registry Operator for .mo)
+澳門
+
+// xn--mix082f ("Macao", Chinese, Simplified) : MO
+澳门
+
+// xn--mgbx4cd0ab ("Malaysia", Malay) : MY
+مليسيا
+
+// xn--mgb9awbf ("Oman", Arabic) : OM
+عمان
+
+// xn--mgbai9azgqp6j ("Pakistan", Urdu/Arabic) : PK
+پاکستان
+
+// xn--mgbai9a5eva00b ("Pakistan", Urdu/Arabic, variant) : PK
+پاكستان
+
+// xn--ygbi2ammx ("Falasteen", Arabic) : PS
+// The Palestinian National Internet Naming Authority (PNINA)
+// http://www.pnina.ps
+فلسطين
+
+// xn--90a3ac ("srb", Cyrillic) : RS
+// https://www.rnids.rs/en/domains/national-domains
+срб
+пр.срб
+орг.срб
+обр.срб
+од.срб
+упр.срб
+ак.срб
+
+// xn--p1ai ("rf", Russian-Cyrillic) : RU
+// https://cctld.ru/files/pdf/docs/en/rules_ru-rf.pdf
+// Submitted by George Georgievsky <gug@cctld.ru>
+рф
+
+// xn--wgbl6a ("Qatar", Arabic) : QA
+// http://www.ict.gov.qa/
+قطر
+
+// xn--mgberp4a5d4ar ("AlSaudiah", Arabic) : SA
+// http://www.nic.net.sa/
+السعودية
+
+// xn--mgberp4a5d4a87g ("AlSaudiah", Arabic, variant) : SA
+السعودیة
+
+// xn--mgbqly7c0a67fbc ("AlSaudiah", Arabic, variant) : SA
+السعودیۃ
+
+// xn--mgbqly7cvafr ("AlSaudiah", Arabic, variant) : SA
+السعوديه
+
+// xn--mgbpl2fh ("sudan", Arabic) : SD
+// Operated by .sd registry
+سودان
+
+// xn--yfro4i67o Singapore ("Singapore", Chinese) : SG
+新加坡
+
+// xn--clchc0ea0b2g2a9gcd ("Singapore", Tamil) : SG
+சிங்கப்பூர்
+
+// xn--ogbpf8fl ("Syria", Arabic) : SY
+سورية
+
+// xn--mgbtf8fl ("Syria", Arabic, variant) : SY
+سوريا
+
+// xn--o3cw4h ("Thai", Thai) : TH
+// http://www.thnic.co.th
+ไทย
+ศึกษา.ไทย
+ธุรกิจ.ไทย
+รัฐบาล.ไทย
+ทหาร.ไทย
+เน็ต.ไทย
+องค์กร.ไทย
+
+// xn--pgbs0dh ("Tunisia", Arabic) : TN
+// http://nic.tn
+تونس
+
+// xn--kpry57d ("Taiwan", Chinese, Traditional) : TW
+// http://www.twnic.net/english/dn/dn_07a.htm
+台灣
+
+// xn--kprw13d ("Taiwan", Chinese, Simplified) : TW
+// http://www.twnic.net/english/dn/dn_07a.htm
+台湾
+
+// xn--nnx388a ("Taiwan", Chinese, variant) : TW
+臺灣
+
+// xn--j1amh ("ukr", Cyrillic) : UA
+укр
+
+// xn--mgb2ddes ("AlYemen", Arabic) : YE
+اليمن
+
+// xxx : http://icmregistry.com
+xxx
+
+// ye : http://www.y.net.ye/services/domain_name.htm
+ye
+com.ye
+edu.ye
+gov.ye
+net.ye
+mil.ye
+org.ye
+
+// za : https://www.zadna.org.za/content/page/domain-information/
+ac.za
+agric.za
+alt.za
+co.za
+edu.za
+gov.za
+grondar.za
+law.za
+mil.za
+net.za
+ngo.za
+nic.za
+nis.za
+nom.za
+org.za
+school.za
+tm.za
+web.za
+
+// zm : https://zicta.zm/
+// Submitted by registry <info@zicta.zm>
+zm
+ac.zm
+biz.zm
+co.zm
+com.zm
+edu.zm
+gov.zm
+info.zm
+mil.zm
+net.zm
+org.zm
+sch.zm
+
+// zw : https://www.potraz.gov.zw/
+// Confirmed by registry <bmtengwa@potraz.gov.zw> 2017-01-25
+zw
+ac.zw
+co.zw
+gov.zw
+mil.zw
+org.zw
+
+
+// newGTLDs
+
+// List of new gTLDs imported from https://www.icann.org/resources/registries/gtlds/v2/gtlds.json on 2024-03-06T15:14:58Z
+// This list is auto-generated, don't edit it manually.
+// aaa : American Automobile Association, Inc.
+// https://www.iana.org/domains/root/db/aaa.html
+aaa
+
+// aarp : AARP
+// https://www.iana.org/domains/root/db/aarp.html
+aarp
+
+// abb : ABB Ltd
+// https://www.iana.org/domains/root/db/abb.html
+abb
+
+// abbott : Abbott Laboratories, Inc.
+// https://www.iana.org/domains/root/db/abbott.html
+abbott
+
+// abbvie : AbbVie Inc.
+// https://www.iana.org/domains/root/db/abbvie.html
+abbvie
+
+// abc : Disney Enterprises, Inc.
+// https://www.iana.org/domains/root/db/abc.html
+abc
+
+// able : Able Inc.
+// https://www.iana.org/domains/root/db/able.html
+able
+
+// abogado : Registry Services, LLC
+// https://www.iana.org/domains/root/db/abogado.html
+abogado
+
+// abudhabi : Abu Dhabi Systems and Information Centre
+// https://www.iana.org/domains/root/db/abudhabi.html
+abudhabi
+
+// academy : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/academy.html
+academy
+
+// accenture : Accenture plc
+// https://www.iana.org/domains/root/db/accenture.html
+accenture
+
+// accountant : dot Accountant Limited
+// https://www.iana.org/domains/root/db/accountant.html
+accountant
+
+// accountants : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/accountants.html
+accountants
+
+// aco : ACO Severin Ahlmann GmbH & Co. KG
+// https://www.iana.org/domains/root/db/aco.html
+aco
+
+// actor : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/actor.html
+actor
+
+// ads : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/ads.html
+ads
+
+// adult : ICM Registry AD LLC
+// https://www.iana.org/domains/root/db/adult.html
+adult
+
+// aeg : Aktiebolaget Electrolux
+// https://www.iana.org/domains/root/db/aeg.html
+aeg
+
+// aetna : Aetna Life Insurance Company
+// https://www.iana.org/domains/root/db/aetna.html
+aetna
+
+// afl : Australian Football League
+// https://www.iana.org/domains/root/db/afl.html
+afl
+
+// africa : ZA Central Registry NPC trading as Registry.Africa
+// https://www.iana.org/domains/root/db/africa.html
+africa
+
+// agakhan : Fondation Aga Khan (Aga Khan Foundation)
+// https://www.iana.org/domains/root/db/agakhan.html
+agakhan
+
+// agency : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/agency.html
+agency
+
+// aig : American International Group, Inc.
+// https://www.iana.org/domains/root/db/aig.html
+aig
+
+// airbus : Airbus S.A.S.
+// https://www.iana.org/domains/root/db/airbus.html
+airbus
+
+// airforce : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/airforce.html
+airforce
+
+// airtel : Bharti Airtel Limited
+// https://www.iana.org/domains/root/db/airtel.html
+airtel
+
+// akdn : Fondation Aga Khan (Aga Khan Foundation)
+// https://www.iana.org/domains/root/db/akdn.html
+akdn
+
+// alibaba : Alibaba Group Holding Limited
+// https://www.iana.org/domains/root/db/alibaba.html
+alibaba
+
+// alipay : Alibaba Group Holding Limited
+// https://www.iana.org/domains/root/db/alipay.html
+alipay
+
+// allfinanz : Allfinanz Deutsche Vermögensberatung Aktiengesellschaft
+// https://www.iana.org/domains/root/db/allfinanz.html
+allfinanz
+
+// allstate : Allstate Fire and Casualty Insurance Company
+// https://www.iana.org/domains/root/db/allstate.html
+allstate
+
+// ally : Ally Financial Inc.
+// https://www.iana.org/domains/root/db/ally.html
+ally
+
+// alsace : Region Grand Est
+// https://www.iana.org/domains/root/db/alsace.html
+alsace
+
+// alstom : ALSTOM
+// https://www.iana.org/domains/root/db/alstom.html
+alstom
+
+// amazon : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/amazon.html
+amazon
+
+// americanexpress : American Express Travel Related Services Company, Inc.
+// https://www.iana.org/domains/root/db/americanexpress.html
+americanexpress
+
+// americanfamily : AmFam, Inc.
+// https://www.iana.org/domains/root/db/americanfamily.html
+americanfamily
+
+// amex : American Express Travel Related Services Company, Inc.
+// https://www.iana.org/domains/root/db/amex.html
+amex
+
+// amfam : AmFam, Inc.
+// https://www.iana.org/domains/root/db/amfam.html
+amfam
+
+// amica : Amica Mutual Insurance Company
+// https://www.iana.org/domains/root/db/amica.html
+amica
+
+// amsterdam : Gemeente Amsterdam
+// https://www.iana.org/domains/root/db/amsterdam.html
+amsterdam
+
+// analytics : Campus IP LLC
+// https://www.iana.org/domains/root/db/analytics.html
+analytics
+
+// android : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/android.html
+android
+
+// anquan : Beijing Qihu Keji Co., Ltd.
+// https://www.iana.org/domains/root/db/anquan.html
+anquan
+
+// anz : Australia and New Zealand Banking Group Limited
+// https://www.iana.org/domains/root/db/anz.html
+anz
+
+// aol : Oath Inc.
+// https://www.iana.org/domains/root/db/aol.html
+aol
+
+// apartments : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/apartments.html
+apartments
+
+// app : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/app.html
+app
+
+// apple : Apple Inc.
+// https://www.iana.org/domains/root/db/apple.html
+apple
+
+// aquarelle : Aquarelle.com
+// https://www.iana.org/domains/root/db/aquarelle.html
+aquarelle
+
+// arab : League of Arab States
+// https://www.iana.org/domains/root/db/arab.html
+arab
+
+// aramco : Aramco Services Company
+// https://www.iana.org/domains/root/db/aramco.html
+aramco
+
+// archi : Identity Digital Limited
+// https://www.iana.org/domains/root/db/archi.html
+archi
+
+// army : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/army.html
+army
+
+// art : UK Creative Ideas Limited
+// https://www.iana.org/domains/root/db/art.html
+art
+
+// arte : Association Relative à la Télévision Européenne G.E.I.E.
+// https://www.iana.org/domains/root/db/arte.html
+arte
+
+// asda : Wal-Mart Stores, Inc.
+// https://www.iana.org/domains/root/db/asda.html
+asda
+
+// associates : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/associates.html
+associates
+
+// athleta : The Gap, Inc.
+// https://www.iana.org/domains/root/db/athleta.html
+athleta
+
+// attorney : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/attorney.html
+attorney
+
+// auction : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/auction.html
+auction
+
+// audi : AUDI Aktiengesellschaft
+// https://www.iana.org/domains/root/db/audi.html
+audi
+
+// audible : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/audible.html
+audible
+
+// audio : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/audio.html
+audio
+
+// auspost : Australian Postal Corporation
+// https://www.iana.org/domains/root/db/auspost.html
+auspost
+
+// author : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/author.html
+author
+
+// auto : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/auto.html
+auto
+
+// autos : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/autos.html
+autos
+
+// avianca : Avianca Inc.
+// https://www.iana.org/domains/root/db/avianca.html
+avianca
+
+// aws : AWS Registry LLC
+// https://www.iana.org/domains/root/db/aws.html
+aws
+
+// axa : AXA Group Operations SAS
+// https://www.iana.org/domains/root/db/axa.html
+axa
+
+// azure : Microsoft Corporation
+// https://www.iana.org/domains/root/db/azure.html
+azure
+
+// baby : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/baby.html
+baby
+
+// baidu : Baidu, Inc.
+// https://www.iana.org/domains/root/db/baidu.html
+baidu
+
+// banamex : Citigroup Inc.
+// https://www.iana.org/domains/root/db/banamex.html
+banamex
+
+// band : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/band.html
+band
+
+// bank : fTLD Registry Services LLC
+// https://www.iana.org/domains/root/db/bank.html
+bank
+
+// bar : Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable
+// https://www.iana.org/domains/root/db/bar.html
+bar
+
+// barcelona : Municipi de Barcelona
+// https://www.iana.org/domains/root/db/barcelona.html
+barcelona
+
+// barclaycard : Barclays Bank PLC
+// https://www.iana.org/domains/root/db/barclaycard.html
+barclaycard
+
+// barclays : Barclays Bank PLC
+// https://www.iana.org/domains/root/db/barclays.html
+barclays
+
+// barefoot : Gallo Vineyards, Inc.
+// https://www.iana.org/domains/root/db/barefoot.html
+barefoot
+
+// bargains : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/bargains.html
+bargains
+
+// baseball : MLB Advanced Media DH, LLC
+// https://www.iana.org/domains/root/db/baseball.html
+baseball
+
+// basketball : Fédération Internationale de Basketball (FIBA)
+// https://www.iana.org/domains/root/db/basketball.html
+basketball
+
+// bauhaus : Werkhaus GmbH
+// https://www.iana.org/domains/root/db/bauhaus.html
+bauhaus
+
+// bayern : Bayern Connect GmbH
+// https://www.iana.org/domains/root/db/bayern.html
+bayern
+
+// bbc : British Broadcasting Corporation
+// https://www.iana.org/domains/root/db/bbc.html
+bbc
+
+// bbt : BB&T Corporation
+// https://www.iana.org/domains/root/db/bbt.html
+bbt
+
+// bbva : BANCO BILBAO VIZCAYA ARGENTARIA, S.A.
+// https://www.iana.org/domains/root/db/bbva.html
+bbva
+
+// bcg : The Boston Consulting Group, Inc.
+// https://www.iana.org/domains/root/db/bcg.html
+bcg
+
+// bcn : Municipi de Barcelona
+// https://www.iana.org/domains/root/db/bcn.html
+bcn
+
+// beats : Beats Electronics, LLC
+// https://www.iana.org/domains/root/db/beats.html
+beats
+
+// beauty : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/beauty.html
+beauty
+
+// beer : Registry Services, LLC
+// https://www.iana.org/domains/root/db/beer.html
+beer
+
+// bentley : Bentley Motors Limited
+// https://www.iana.org/domains/root/db/bentley.html
+bentley
+
+// berlin : dotBERLIN GmbH & Co. KG
+// https://www.iana.org/domains/root/db/berlin.html
+berlin
+
+// best : BestTLD Pty Ltd
+// https://www.iana.org/domains/root/db/best.html
+best
+
+// bestbuy : BBY Solutions, Inc.
+// https://www.iana.org/domains/root/db/bestbuy.html
+bestbuy
+
+// bet : Identity Digital Limited
+// https://www.iana.org/domains/root/db/bet.html
+bet
+
+// bharti : Bharti Enterprises (Holding) Private Limited
+// https://www.iana.org/domains/root/db/bharti.html
+bharti
+
+// bible : American Bible Society
+// https://www.iana.org/domains/root/db/bible.html
+bible
+
+// bid : dot Bid Limited
+// https://www.iana.org/domains/root/db/bid.html
+bid
+
+// bike : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/bike.html
+bike
+
+// bing : Microsoft Corporation
+// https://www.iana.org/domains/root/db/bing.html
+bing
+
+// bingo : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/bingo.html
+bingo
+
+// bio : Identity Digital Limited
+// https://www.iana.org/domains/root/db/bio.html
+bio
+
+// black : Identity Digital Limited
+// https://www.iana.org/domains/root/db/black.html
+black
+
+// blackfriday : Registry Services, LLC
+// https://www.iana.org/domains/root/db/blackfriday.html
+blackfriday
+
+// blockbuster : Dish DBS Corporation
+// https://www.iana.org/domains/root/db/blockbuster.html
+blockbuster
+
+// blog : Knock Knock WHOIS There, LLC
+// https://www.iana.org/domains/root/db/blog.html
+blog
+
+// bloomberg : Bloomberg IP Holdings LLC
+// https://www.iana.org/domains/root/db/bloomberg.html
+bloomberg
+
+// blue : Identity Digital Limited
+// https://www.iana.org/domains/root/db/blue.html
+blue
+
+// bms : Bristol-Myers Squibb Company
+// https://www.iana.org/domains/root/db/bms.html
+bms
+
+// bmw : Bayerische Motoren Werke Aktiengesellschaft
+// https://www.iana.org/domains/root/db/bmw.html
+bmw
+
+// bnpparibas : BNP Paribas
+// https://www.iana.org/domains/root/db/bnpparibas.html
+bnpparibas
+
+// boats : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/boats.html
+boats
+
+// boehringer : Boehringer Ingelheim International GmbH
+// https://www.iana.org/domains/root/db/boehringer.html
+boehringer
+
+// bofa : Bank of America Corporation
+// https://www.iana.org/domains/root/db/bofa.html
+bofa
+
+// bom : Núcleo de Informação e Coordenação do Ponto BR - NIC.br
+// https://www.iana.org/domains/root/db/bom.html
+bom
+
+// bond : ShortDot SA
+// https://www.iana.org/domains/root/db/bond.html
+bond
+
+// boo : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/boo.html
+boo
+
+// book : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/book.html
+book
+
+// booking : Booking.com B.V.
+// https://www.iana.org/domains/root/db/booking.html
+booking
+
+// bosch : Robert Bosch GMBH
+// https://www.iana.org/domains/root/db/bosch.html
+bosch
+
+// bostik : Bostik SA
+// https://www.iana.org/domains/root/db/bostik.html
+bostik
+
+// boston : Registry Services, LLC
+// https://www.iana.org/domains/root/db/boston.html
+boston
+
+// bot : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/bot.html
+bot
+
+// boutique : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/boutique.html
+boutique
+
+// box : Intercap Registry Inc.
+// https://www.iana.org/domains/root/db/box.html
+box
+
+// bradesco : Banco Bradesco S.A.
+// https://www.iana.org/domains/root/db/bradesco.html
+bradesco
+
+// bridgestone : Bridgestone Corporation
+// https://www.iana.org/domains/root/db/bridgestone.html
+bridgestone
+
+// broadway : Celebrate Broadway, Inc.
+// https://www.iana.org/domains/root/db/broadway.html
+broadway
+
+// broker : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/broker.html
+broker
+
+// brother : Brother Industries, Ltd.
+// https://www.iana.org/domains/root/db/brother.html
+brother
+
+// brussels : DNS.be vzw
+// https://www.iana.org/domains/root/db/brussels.html
+brussels
+
+// build : Plan Bee LLC
+// https://www.iana.org/domains/root/db/build.html
+build
+
+// builders : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/builders.html
+builders
+
+// business : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/business.html
+business
+
+// buy : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/buy.html
+buy
+
+// buzz : DOTSTRATEGY CO.
+// https://www.iana.org/domains/root/db/buzz.html
+buzz
+
+// bzh : Association www.bzh
+// https://www.iana.org/domains/root/db/bzh.html
+bzh
+
+// cab : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/cab.html
+cab
+
+// cafe : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/cafe.html
+cafe
+
+// cal : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/cal.html
+cal
+
+// call : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/call.html
+call
+
+// calvinklein : PVH gTLD Holdings LLC
+// https://www.iana.org/domains/root/db/calvinklein.html
+calvinklein
+
+// cam : Cam Connecting SARL
+// https://www.iana.org/domains/root/db/cam.html
+cam
+
+// camera : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/camera.html
+camera
+
+// camp : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/camp.html
+camp
+
+// canon : Canon Inc.
+// https://www.iana.org/domains/root/db/canon.html
+canon
+
+// capetown : ZA Central Registry NPC trading as ZA Central Registry
+// https://www.iana.org/domains/root/db/capetown.html
+capetown
+
+// capital : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/capital.html
+capital
+
+// capitalone : Capital One Financial Corporation
+// https://www.iana.org/domains/root/db/capitalone.html
+capitalone
+
+// car : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/car.html
+car
+
+// caravan : Caravan International, Inc.
+// https://www.iana.org/domains/root/db/caravan.html
+caravan
+
+// cards : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/cards.html
+cards
+
+// care : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/care.html
+care
+
+// career : dotCareer LLC
+// https://www.iana.org/domains/root/db/career.html
+career
+
+// careers : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/careers.html
+careers
+
+// cars : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/cars.html
+cars
+
+// casa : Registry Services, LLC
+// https://www.iana.org/domains/root/db/casa.html
+casa
+
+// case : Digity, LLC
+// https://www.iana.org/domains/root/db/case.html
+case
+
+// cash : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/cash.html
+cash
+
+// casino : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/casino.html
+casino
+
+// catering : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/catering.html
+catering
+
+// catholic : Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication)
+// https://www.iana.org/domains/root/db/catholic.html
+catholic
+
+// cba : COMMONWEALTH BANK OF AUSTRALIA
+// https://www.iana.org/domains/root/db/cba.html
+cba
+
+// cbn : The Christian Broadcasting Network, Inc.
+// https://www.iana.org/domains/root/db/cbn.html
+cbn
+
+// cbre : CBRE, Inc.
+// https://www.iana.org/domains/root/db/cbre.html
+cbre
+
+// center : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/center.html
+center
+
+// ceo : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/ceo.html
+ceo
+
+// cern : European Organization for Nuclear Research ("CERN")
+// https://www.iana.org/domains/root/db/cern.html
+cern
+
+// cfa : CFA Institute
+// https://www.iana.org/domains/root/db/cfa.html
+cfa
+
+// cfd : ShortDot SA
+// https://www.iana.org/domains/root/db/cfd.html
+cfd
+
+// chanel : Chanel International B.V.
+// https://www.iana.org/domains/root/db/chanel.html
+chanel
+
+// channel : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/channel.html
+channel
+
+// charity : Public Interest Registry
+// https://www.iana.org/domains/root/db/charity.html
+charity
+
+// chase : JPMorgan Chase Bank, National Association
+// https://www.iana.org/domains/root/db/chase.html
+chase
+
+// chat : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/chat.html
+chat
+
+// cheap : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/cheap.html
+cheap
+
+// chintai : CHINTAI Corporation
+// https://www.iana.org/domains/root/db/chintai.html
+chintai
+
+// christmas : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/christmas.html
+christmas
+
+// chrome : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/chrome.html
+chrome
+
+// church : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/church.html
+church
+
+// cipriani : Hotel Cipriani Srl
+// https://www.iana.org/domains/root/db/cipriani.html
+cipriani
+
+// circle : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/circle.html
+circle
+
+// cisco : Cisco Technology, Inc.
+// https://www.iana.org/domains/root/db/cisco.html
+cisco
+
+// citadel : Citadel Domain LLC
+// https://www.iana.org/domains/root/db/citadel.html
+citadel
+
+// citi : Citigroup Inc.
+// https://www.iana.org/domains/root/db/citi.html
+citi
+
+// citic : CITIC Group Corporation
+// https://www.iana.org/domains/root/db/citic.html
+citic
+
+// city : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/city.html
+city
+
+// claims : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/claims.html
+claims
+
+// cleaning : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/cleaning.html
+cleaning
+
+// click : Internet Naming Company LLC
+// https://www.iana.org/domains/root/db/click.html
+click
+
+// clinic : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/clinic.html
+clinic
+
+// clinique : The Estée Lauder Companies Inc.
+// https://www.iana.org/domains/root/db/clinique.html
+clinique
+
+// clothing : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/clothing.html
+clothing
+
+// cloud : Aruba PEC S.p.A.
+// https://www.iana.org/domains/root/db/cloud.html
+cloud
+
+// club : Registry Services, LLC
+// https://www.iana.org/domains/root/db/club.html
+club
+
+// clubmed : Club Méditerranée S.A.
+// https://www.iana.org/domains/root/db/clubmed.html
+clubmed
+
+// coach : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/coach.html
+coach
+
+// codes : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/codes.html
+codes
+
+// coffee : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/coffee.html
+coffee
+
+// college : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/college.html
+college
+
+// cologne : dotKoeln GmbH
+// https://www.iana.org/domains/root/db/cologne.html
+cologne
+
+// commbank : COMMONWEALTH BANK OF AUSTRALIA
+// https://www.iana.org/domains/root/db/commbank.html
+commbank
+
+// community : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/community.html
+community
+
+// company : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/company.html
+company
+
+// compare : Registry Services, LLC
+// https://www.iana.org/domains/root/db/compare.html
+compare
+
+// computer : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/computer.html
+computer
+
+// comsec : VeriSign, Inc.
+// https://www.iana.org/domains/root/db/comsec.html
+comsec
+
+// condos : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/condos.html
+condos
+
+// construction : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/construction.html
+construction
+
+// consulting : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/consulting.html
+consulting
+
+// contact : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/contact.html
+contact
+
+// contractors : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/contractors.html
+contractors
+
+// cooking : Registry Services, LLC
+// https://www.iana.org/domains/root/db/cooking.html
+cooking
+
+// cool : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/cool.html
+cool
+
+// corsica : Collectivité de Corse
+// https://www.iana.org/domains/root/db/corsica.html
+corsica
+
+// country : Internet Naming Company LLC
+// https://www.iana.org/domains/root/db/country.html
+country
+
+// coupon : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/coupon.html
+coupon
+
+// coupons : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/coupons.html
+coupons
+
+// courses : Registry Services, LLC
+// https://www.iana.org/domains/root/db/courses.html
+courses
+
+// cpa : American Institute of Certified Public Accountants
+// https://www.iana.org/domains/root/db/cpa.html
+cpa
+
+// credit : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/credit.html
+credit
+
+// creditcard : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/creditcard.html
+creditcard
+
+// creditunion : DotCooperation LLC
+// https://www.iana.org/domains/root/db/creditunion.html
+creditunion
+
+// cricket : dot Cricket Limited
+// https://www.iana.org/domains/root/db/cricket.html
+cricket
+
+// crown : Crown Equipment Corporation
+// https://www.iana.org/domains/root/db/crown.html
+crown
+
+// crs : Federated Co-operatives Limited
+// https://www.iana.org/domains/root/db/crs.html
+crs
+
+// cruise : Viking River Cruises (Bermuda) Ltd.
+// https://www.iana.org/domains/root/db/cruise.html
+cruise
+
+// cruises : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/cruises.html
+cruises
+
+// cuisinella : SCHMIDT GROUPE S.A.S.
+// https://www.iana.org/domains/root/db/cuisinella.html
+cuisinella
+
+// cymru : Nominet UK
+// https://www.iana.org/domains/root/db/cymru.html
+cymru
+
+// cyou : ShortDot SA
+// https://www.iana.org/domains/root/db/cyou.html
+cyou
+
+// dabur : Dabur India Limited
+// https://www.iana.org/domains/root/db/dabur.html
+dabur
+
+// dad : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/dad.html
+dad
+
+// dance : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/dance.html
+dance
+
+// data : Dish DBS Corporation
+// https://www.iana.org/domains/root/db/data.html
+data
+
+// date : dot Date Limited
+// https://www.iana.org/domains/root/db/date.html
+date
+
+// dating : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/dating.html
+dating
+
+// datsun : NISSAN MOTOR CO., LTD.
+// https://www.iana.org/domains/root/db/datsun.html
+datsun
+
+// day : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/day.html
+day
+
+// dclk : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/dclk.html
+dclk
+
+// dds : Registry Services, LLC
+// https://www.iana.org/domains/root/db/dds.html
+dds
+
+// deal : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/deal.html
+deal
+
+// dealer : Intercap Registry Inc.
+// https://www.iana.org/domains/root/db/dealer.html
+dealer
+
+// deals : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/deals.html
+deals
+
+// degree : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/degree.html
+degree
+
+// delivery : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/delivery.html
+delivery
+
+// dell : Dell Inc.
+// https://www.iana.org/domains/root/db/dell.html
+dell
+
+// deloitte : Deloitte Touche Tohmatsu
+// https://www.iana.org/domains/root/db/deloitte.html
+deloitte
+
+// delta : Delta Air Lines, Inc.
+// https://www.iana.org/domains/root/db/delta.html
+delta
+
+// democrat : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/democrat.html
+democrat
+
+// dental : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/dental.html
+dental
+
+// dentist : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/dentist.html
+dentist
+
+// desi
+// https://www.iana.org/domains/root/db/desi.html
+desi
+
+// design : Registry Services, LLC
+// https://www.iana.org/domains/root/db/design.html
+design
+
+// dev : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/dev.html
+dev
+
+// dhl : Deutsche Post AG
+// https://www.iana.org/domains/root/db/dhl.html
+dhl
+
+// diamonds : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/diamonds.html
+diamonds
+
+// diet : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/diet.html
+diet
+
+// digital : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/digital.html
+digital
+
+// direct : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/direct.html
+direct
+
+// directory : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/directory.html
+directory
+
+// discount : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/discount.html
+discount
+
+// discover : Discover Financial Services
+// https://www.iana.org/domains/root/db/discover.html
+discover
+
+// dish : Dish DBS Corporation
+// https://www.iana.org/domains/root/db/dish.html
+dish
+
+// diy : Internet Naming Company LLC
+// https://www.iana.org/domains/root/db/diy.html
+diy
+
+// dnp : Dai Nippon Printing Co., Ltd.
+// https://www.iana.org/domains/root/db/dnp.html
+dnp
+
+// docs : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/docs.html
+docs
+
+// doctor : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/doctor.html
+doctor
+
+// dog : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/dog.html
+dog
+
+// domains : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/domains.html
+domains
+
+// dot : Dish DBS Corporation
+// https://www.iana.org/domains/root/db/dot.html
+dot
+
+// download : dot Support Limited
+// https://www.iana.org/domains/root/db/download.html
+download
+
+// drive : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/drive.html
+drive
+
+// dtv : Dish DBS Corporation
+// https://www.iana.org/domains/root/db/dtv.html
+dtv
+
+// dubai : Dubai Smart Government Department
+// https://www.iana.org/domains/root/db/dubai.html
+dubai
+
+// dunlop : The Goodyear Tire & Rubber Company
+// https://www.iana.org/domains/root/db/dunlop.html
+dunlop
+
+// dupont : DuPont Specialty Products USA, LLC
+// https://www.iana.org/domains/root/db/dupont.html
+dupont
+
+// durban : ZA Central Registry NPC trading as ZA Central Registry
+// https://www.iana.org/domains/root/db/durban.html
+durban
+
+// dvag : Deutsche Vermögensberatung Aktiengesellschaft DVAG
+// https://www.iana.org/domains/root/db/dvag.html
+dvag
+
+// dvr : DISH Technologies L.L.C.
+// https://www.iana.org/domains/root/db/dvr.html
+dvr
+
+// earth : Interlink Systems Innovation Institute K.K.
+// https://www.iana.org/domains/root/db/earth.html
+earth
+
+// eat : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/eat.html
+eat
+
+// eco : Big Room Inc.
+// https://www.iana.org/domains/root/db/eco.html
+eco
+
+// edeka : EDEKA Verband kaufmännischer Genossenschaften e.V.
+// https://www.iana.org/domains/root/db/edeka.html
+edeka
+
+// education : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/education.html
+education
+
+// email : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/email.html
+email
+
+// emerck : Merck KGaA
+// https://www.iana.org/domains/root/db/emerck.html
+emerck
+
+// energy : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/energy.html
+energy
+
+// engineer : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/engineer.html
+engineer
+
+// engineering : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/engineering.html
+engineering
+
+// enterprises : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/enterprises.html
+enterprises
+
+// epson : Seiko Epson Corporation
+// https://www.iana.org/domains/root/db/epson.html
+epson
+
+// equipment : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/equipment.html
+equipment
+
+// ericsson : Telefonaktiebolaget L M Ericsson
+// https://www.iana.org/domains/root/db/ericsson.html
+ericsson
+
+// erni : ERNI Group Holding AG
+// https://www.iana.org/domains/root/db/erni.html
+erni
+
+// esq : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/esq.html
+esq
+
+// estate : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/estate.html
+estate
+
+// eurovision : European Broadcasting Union (EBU)
+// https://www.iana.org/domains/root/db/eurovision.html
+eurovision
+
+// eus : Puntueus Fundazioa
+// https://www.iana.org/domains/root/db/eus.html
+eus
+
+// events : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/events.html
+events
+
+// exchange : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/exchange.html
+exchange
+
+// expert : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/expert.html
+expert
+
+// exposed : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/exposed.html
+exposed
+
+// express : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/express.html
+express
+
+// extraspace : Extra Space Storage LLC
+// https://www.iana.org/domains/root/db/extraspace.html
+extraspace
+
+// fage : Fage International S.A.
+// https://www.iana.org/domains/root/db/fage.html
+fage
+
+// fail : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/fail.html
+fail
+
+// fairwinds : FairWinds Partners, LLC
+// https://www.iana.org/domains/root/db/fairwinds.html
+fairwinds
+
+// faith : dot Faith Limited
+// https://www.iana.org/domains/root/db/faith.html
+faith
+
+// family : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/family.html
+family
+
+// fan : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/fan.html
+fan
+
+// fans : ZDNS International Limited
+// https://www.iana.org/domains/root/db/fans.html
+fans
+
+// farm : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/farm.html
+farm
+
+// farmers : Farmers Insurance Exchange
+// https://www.iana.org/domains/root/db/farmers.html
+farmers
+
+// fashion : Registry Services, LLC
+// https://www.iana.org/domains/root/db/fashion.html
+fashion
+
+// fast : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/fast.html
+fast
+
+// fedex : Federal Express Corporation
+// https://www.iana.org/domains/root/db/fedex.html
+fedex
+
+// feedback : Top Level Spectrum, Inc.
+// https://www.iana.org/domains/root/db/feedback.html
+feedback
+
+// ferrari : Fiat Chrysler Automobiles N.V.
+// https://www.iana.org/domains/root/db/ferrari.html
+ferrari
+
+// ferrero : Ferrero Trading Lux S.A.
+// https://www.iana.org/domains/root/db/ferrero.html
+ferrero
+
+// fidelity : Fidelity Brokerage Services LLC
+// https://www.iana.org/domains/root/db/fidelity.html
+fidelity
+
+// fido : Rogers Communications Canada Inc.
+// https://www.iana.org/domains/root/db/fido.html
+fido
+
+// film : Motion Picture Domain Registry Pty Ltd
+// https://www.iana.org/domains/root/db/film.html
+film
+
+// final : Núcleo de Informação e Coordenação do Ponto BR - NIC.br
+// https://www.iana.org/domains/root/db/final.html
+final
+
+// finance : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/finance.html
+finance
+
+// financial : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/financial.html
+financial
+
+// fire : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/fire.html
+fire
+
+// firestone : Bridgestone Licensing Services, Inc
+// https://www.iana.org/domains/root/db/firestone.html
+firestone
+
+// firmdale : Firmdale Holdings Limited
+// https://www.iana.org/domains/root/db/firmdale.html
+firmdale
+
+// fish : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/fish.html
+fish
+
+// fishing : Registry Services, LLC
+// https://www.iana.org/domains/root/db/fishing.html
+fishing
+
+// fit : Registry Services, LLC
+// https://www.iana.org/domains/root/db/fit.html
+fit
+
+// fitness : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/fitness.html
+fitness
+
+// flickr : Flickr, Inc.
+// https://www.iana.org/domains/root/db/flickr.html
+flickr
+
+// flights : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/flights.html
+flights
+
+// flir : FLIR Systems, Inc.
+// https://www.iana.org/domains/root/db/flir.html
+flir
+
+// florist : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/florist.html
+florist
+
+// flowers : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/flowers.html
+flowers
+
+// fly : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/fly.html
+fly
+
+// foo : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/foo.html
+foo
+
+// food : Internet Naming Company LLC
+// https://www.iana.org/domains/root/db/food.html
+food
+
+// football : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/football.html
+football
+
+// ford : Ford Motor Company
+// https://www.iana.org/domains/root/db/ford.html
+ford
+
+// forex : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/forex.html
+forex
+
+// forsale : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/forsale.html
+forsale
+
+// forum : Fegistry, LLC
+// https://www.iana.org/domains/root/db/forum.html
+forum
+
+// foundation : Public Interest Registry
+// https://www.iana.org/domains/root/db/foundation.html
+foundation
+
+// fox : FOX Registry, LLC
+// https://www.iana.org/domains/root/db/fox.html
+fox
+
+// free : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/free.html
+free
+
+// fresenius : Fresenius Immobilien-Verwaltungs-GmbH
+// https://www.iana.org/domains/root/db/fresenius.html
+fresenius
+
+// frl : FRLregistry B.V.
+// https://www.iana.org/domains/root/db/frl.html
+frl
+
+// frogans : OP3FT
+// https://www.iana.org/domains/root/db/frogans.html
+frogans
+
+// frontier : Frontier Communications Corporation
+// https://www.iana.org/domains/root/db/frontier.html
+frontier
+
+// ftr : Frontier Communications Corporation
+// https://www.iana.org/domains/root/db/ftr.html
+ftr
+
+// fujitsu : Fujitsu Limited
+// https://www.iana.org/domains/root/db/fujitsu.html
+fujitsu
+
+// fun : Radix Technologies Inc.
+// https://www.iana.org/domains/root/db/fun.html
+fun
+
+// fund : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/fund.html
+fund
+
+// furniture : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/furniture.html
+furniture
+
+// futbol : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/futbol.html
+futbol
+
+// fyi : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/fyi.html
+fyi
+
+// gal : Asociación puntoGAL
+// https://www.iana.org/domains/root/db/gal.html
+gal
+
+// gallery : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/gallery.html
+gallery
+
+// gallo : Gallo Vineyards, Inc.
+// https://www.iana.org/domains/root/db/gallo.html
+gallo
+
+// gallup : Gallup, Inc.
+// https://www.iana.org/domains/root/db/gallup.html
+gallup
+
+// game : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/game.html
+game
+
+// games : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/games.html
+games
+
+// gap : The Gap, Inc.
+// https://www.iana.org/domains/root/db/gap.html
+gap
+
+// garden : Registry Services, LLC
+// https://www.iana.org/domains/root/db/garden.html
+garden
+
+// gay : Registry Services, LLC
+// https://www.iana.org/domains/root/db/gay.html
+gay
+
+// gbiz : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/gbiz.html
+gbiz
+
+// gdn : Joint Stock Company "Navigation-information systems"
+// https://www.iana.org/domains/root/db/gdn.html
+gdn
+
+// gea : GEA Group Aktiengesellschaft
+// https://www.iana.org/domains/root/db/gea.html
+gea
+
+// gent : Easyhost BV
+// https://www.iana.org/domains/root/db/gent.html
+gent
+
+// genting : Resorts World Inc Pte. Ltd.
+// https://www.iana.org/domains/root/db/genting.html
+genting
+
+// george : Wal-Mart Stores, Inc.
+// https://www.iana.org/domains/root/db/george.html
+george
+
+// ggee : GMO Internet, Inc.
+// https://www.iana.org/domains/root/db/ggee.html
+ggee
+
+// gift : DotGift, LLC
+// https://www.iana.org/domains/root/db/gift.html
+gift
+
+// gifts : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/gifts.html
+gifts
+
+// gives : Public Interest Registry
+// https://www.iana.org/domains/root/db/gives.html
+gives
+
+// giving : Public Interest Registry
+// https://www.iana.org/domains/root/db/giving.html
+giving
+
+// glass : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/glass.html
+glass
+
+// gle : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/gle.html
+gle
+
+// global : Identity Digital Limited
+// https://www.iana.org/domains/root/db/global.html
+global
+
+// globo : Globo Comunicação e Participações S.A
+// https://www.iana.org/domains/root/db/globo.html
+globo
+
+// gmail : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/gmail.html
+gmail
+
+// gmbh : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/gmbh.html
+gmbh
+
+// gmo : GMO Internet, Inc.
+// https://www.iana.org/domains/root/db/gmo.html
+gmo
+
+// gmx : 1&1 Mail & Media GmbH
+// https://www.iana.org/domains/root/db/gmx.html
+gmx
+
+// godaddy : Go Daddy East, LLC
+// https://www.iana.org/domains/root/db/godaddy.html
+godaddy
+
+// gold : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/gold.html
+gold
+
+// goldpoint : YODOBASHI CAMERA CO.,LTD.
+// https://www.iana.org/domains/root/db/goldpoint.html
+goldpoint
+
+// golf : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/golf.html
+golf
+
+// goo : NTT DOCOMO, INC.
+// https://www.iana.org/domains/root/db/goo.html
+goo
+
+// goodyear : The Goodyear Tire & Rubber Company
+// https://www.iana.org/domains/root/db/goodyear.html
+goodyear
+
+// goog : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/goog.html
+goog
+
+// google : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/google.html
+google
+
+// gop : Republican State Leadership Committee, Inc.
+// https://www.iana.org/domains/root/db/gop.html
+gop
+
+// got : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/got.html
+got
+
+// grainger : Grainger Registry Services, LLC
+// https://www.iana.org/domains/root/db/grainger.html
+grainger
+
+// graphics : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/graphics.html
+graphics
+
+// gratis : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/gratis.html
+gratis
+
+// green : Identity Digital Limited
+// https://www.iana.org/domains/root/db/green.html
+green
+
+// gripe : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/gripe.html
+gripe
+
+// grocery : Wal-Mart Stores, Inc.
+// https://www.iana.org/domains/root/db/grocery.html
+grocery
+
+// group : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/group.html
+group
+
+// gucci : Guccio Gucci S.p.a.
+// https://www.iana.org/domains/root/db/gucci.html
+gucci
+
+// guge : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/guge.html
+guge
+
+// guide : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/guide.html
+guide
+
+// guitars : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/guitars.html
+guitars
+
+// guru : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/guru.html
+guru
+
+// hair : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/hair.html
+hair
+
+// hamburg : Hamburg Top-Level-Domain GmbH
+// https://www.iana.org/domains/root/db/hamburg.html
+hamburg
+
+// hangout : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/hangout.html
+hangout
+
+// haus : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/haus.html
+haus
+
+// hbo : HBO Registry Services, Inc.
+// https://www.iana.org/domains/root/db/hbo.html
+hbo
+
+// hdfc : HOUSING DEVELOPMENT FINANCE CORPORATION LIMITED
+// https://www.iana.org/domains/root/db/hdfc.html
+hdfc
+
+// hdfcbank : HDFC Bank Limited
+// https://www.iana.org/domains/root/db/hdfcbank.html
+hdfcbank
+
+// health : Registry Services, LLC
+// https://www.iana.org/domains/root/db/health.html
+health
+
+// healthcare : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/healthcare.html
+healthcare
+
+// help : Innovation service Limited
+// https://www.iana.org/domains/root/db/help.html
+help
+
+// helsinki : City of Helsinki
+// https://www.iana.org/domains/root/db/helsinki.html
+helsinki
+
+// here : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/here.html
+here
+
+// hermes : HERMES INTERNATIONAL
+// https://www.iana.org/domains/root/db/hermes.html
+hermes
+
+// hiphop : Dot Hip Hop, LLC
+// https://www.iana.org/domains/root/db/hiphop.html
+hiphop
+
+// hisamitsu : Hisamitsu Pharmaceutical Co.,Inc.
+// https://www.iana.org/domains/root/db/hisamitsu.html
+hisamitsu
+
+// hitachi : Hitachi, Ltd.
+// https://www.iana.org/domains/root/db/hitachi.html
+hitachi
+
+// hiv : Internet Naming Company LLC
+// https://www.iana.org/domains/root/db/hiv.html
+hiv
+
+// hkt : PCCW-HKT DataCom Services Limited
+// https://www.iana.org/domains/root/db/hkt.html
+hkt
+
+// hockey : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/hockey.html
+hockey
+
+// holdings : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/holdings.html
+holdings
+
+// holiday : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/holiday.html
+holiday
+
+// homedepot : Home Depot Product Authority, LLC
+// https://www.iana.org/domains/root/db/homedepot.html
+homedepot
+
+// homegoods : The TJX Companies, Inc.
+// https://www.iana.org/domains/root/db/homegoods.html
+homegoods
+
+// homes : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/homes.html
+homes
+
+// homesense : The TJX Companies, Inc.
+// https://www.iana.org/domains/root/db/homesense.html
+homesense
+
+// honda : Honda Motor Co., Ltd.
+// https://www.iana.org/domains/root/db/honda.html
+honda
+
+// horse : Registry Services, LLC
+// https://www.iana.org/domains/root/db/horse.html
+horse
+
+// hospital : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/hospital.html
+hospital
+
+// host : Radix Technologies Inc.
+// https://www.iana.org/domains/root/db/host.html
+host
+
+// hosting : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/hosting.html
+hosting
+
+// hot : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/hot.html
+hot
+
+// hotels : Booking.com B.V.
+// https://www.iana.org/domains/root/db/hotels.html
+hotels
+
+// hotmail : Microsoft Corporation
+// https://www.iana.org/domains/root/db/hotmail.html
+hotmail
+
+// house : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/house.html
+house
+
+// how : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/how.html
+how
+
+// hsbc : HSBC Global Services (UK) Limited
+// https://www.iana.org/domains/root/db/hsbc.html
+hsbc
+
+// hughes : Hughes Satellite Systems Corporation
+// https://www.iana.org/domains/root/db/hughes.html
+hughes
+
+// hyatt : Hyatt GTLD, L.L.C.
+// https://www.iana.org/domains/root/db/hyatt.html
+hyatt
+
+// hyundai : Hyundai Motor Company
+// https://www.iana.org/domains/root/db/hyundai.html
+hyundai
+
+// ibm : International Business Machines Corporation
+// https://www.iana.org/domains/root/db/ibm.html
+ibm
+
+// icbc : Industrial and Commercial Bank of China Limited
+// https://www.iana.org/domains/root/db/icbc.html
+icbc
+
+// ice : IntercontinentalExchange, Inc.
+// https://www.iana.org/domains/root/db/ice.html
+ice
+
+// icu : ShortDot SA
+// https://www.iana.org/domains/root/db/icu.html
+icu
+
+// ieee : IEEE Global LLC
+// https://www.iana.org/domains/root/db/ieee.html
+ieee
+
+// ifm : ifm electronic gmbh
+// https://www.iana.org/domains/root/db/ifm.html
+ifm
+
+// ikano : Ikano S.A.
+// https://www.iana.org/domains/root/db/ikano.html
+ikano
+
+// imamat : Fondation Aga Khan (Aga Khan Foundation)
+// https://www.iana.org/domains/root/db/imamat.html
+imamat
+
+// imdb : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/imdb.html
+imdb
+
+// immo : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/immo.html
+immo
+
+// immobilien : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/immobilien.html
+immobilien
+
+// inc : Intercap Registry Inc.
+// https://www.iana.org/domains/root/db/inc.html
+inc
+
+// industries : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/industries.html
+industries
+
+// infiniti : NISSAN MOTOR CO., LTD.
+// https://www.iana.org/domains/root/db/infiniti.html
+infiniti
+
+// ing : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/ing.html
+ing
+
+// ink : Registry Services, LLC
+// https://www.iana.org/domains/root/db/ink.html
+ink
+
+// institute : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/institute.html
+institute
+
+// insurance : fTLD Registry Services LLC
+// https://www.iana.org/domains/root/db/insurance.html
+insurance
+
+// insure : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/insure.html
+insure
+
+// international : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/international.html
+international
+
+// intuit : Intuit Administrative Services, Inc.
+// https://www.iana.org/domains/root/db/intuit.html
+intuit
+
+// investments : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/investments.html
+investments
+
+// ipiranga : Ipiranga Produtos de Petroleo S.A.
+// https://www.iana.org/domains/root/db/ipiranga.html
+ipiranga
+
+// irish : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/irish.html
+irish
+
+// ismaili : Fondation Aga Khan (Aga Khan Foundation)
+// https://www.iana.org/domains/root/db/ismaili.html
+ismaili
+
+// ist : Istanbul Metropolitan Municipality
+// https://www.iana.org/domains/root/db/ist.html
+ist
+
+// istanbul : Istanbul Metropolitan Municipality
+// https://www.iana.org/domains/root/db/istanbul.html
+istanbul
+
+// itau : Itau Unibanco Holding S.A.
+// https://www.iana.org/domains/root/db/itau.html
+itau
+
+// itv : ITV Services Limited
+// https://www.iana.org/domains/root/db/itv.html
+itv
+
+// jaguar : Jaguar Land Rover Ltd
+// https://www.iana.org/domains/root/db/jaguar.html
+jaguar
+
+// java : Oracle Corporation
+// https://www.iana.org/domains/root/db/java.html
+java
+
+// jcb : JCB Co., Ltd.
+// https://www.iana.org/domains/root/db/jcb.html
+jcb
+
+// jeep : FCA US LLC.
+// https://www.iana.org/domains/root/db/jeep.html
+jeep
+
+// jetzt : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/jetzt.html
+jetzt
+
+// jewelry : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/jewelry.html
+jewelry
+
+// jio : Reliance Industries Limited
+// https://www.iana.org/domains/root/db/jio.html
+jio
+
+// jll : Jones Lang LaSalle Incorporated
+// https://www.iana.org/domains/root/db/jll.html
+jll
+
+// jmp : Matrix IP LLC
+// https://www.iana.org/domains/root/db/jmp.html
+jmp
+
+// jnj : Johnson & Johnson Services, Inc.
+// https://www.iana.org/domains/root/db/jnj.html
+jnj
+
+// joburg : ZA Central Registry NPC trading as ZA Central Registry
+// https://www.iana.org/domains/root/db/joburg.html
+joburg
+
+// jot : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/jot.html
+jot
+
+// joy : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/joy.html
+joy
+
+// jpmorgan : JPMorgan Chase Bank, National Association
+// https://www.iana.org/domains/root/db/jpmorgan.html
+jpmorgan
+
+// jprs : Japan Registry Services Co., Ltd.
+// https://www.iana.org/domains/root/db/jprs.html
+jprs
+
+// juegos : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/juegos.html
+juegos
+
+// juniper : JUNIPER NETWORKS, INC.
+// https://www.iana.org/domains/root/db/juniper.html
+juniper
+
+// kaufen : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/kaufen.html
+kaufen
+
+// kddi : KDDI CORPORATION
+// https://www.iana.org/domains/root/db/kddi.html
+kddi
+
+// kerryhotels : Kerry Trading Co. Limited
+// https://www.iana.org/domains/root/db/kerryhotels.html
+kerryhotels
+
+// kerrylogistics : Kerry Trading Co. Limited
+// https://www.iana.org/domains/root/db/kerrylogistics.html
+kerrylogistics
+
+// kerryproperties : Kerry Trading Co. Limited
+// https://www.iana.org/domains/root/db/kerryproperties.html
+kerryproperties
+
+// kfh : Kuwait Finance House
+// https://www.iana.org/domains/root/db/kfh.html
+kfh
+
+// kia : KIA MOTORS CORPORATION
+// https://www.iana.org/domains/root/db/kia.html
+kia
+
+// kids : DotKids Foundation Limited
+// https://www.iana.org/domains/root/db/kids.html
+kids
+
+// kim : Identity Digital Limited
+// https://www.iana.org/domains/root/db/kim.html
+kim
+
+// kindle : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/kindle.html
+kindle
+
+// kitchen : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/kitchen.html
+kitchen
+
+// kiwi : DOT KIWI LIMITED
+// https://www.iana.org/domains/root/db/kiwi.html
+kiwi
+
+// koeln : dotKoeln GmbH
+// https://www.iana.org/domains/root/db/koeln.html
+koeln
+
+// komatsu : Komatsu Ltd.
+// https://www.iana.org/domains/root/db/komatsu.html
+komatsu
+
+// kosher : Kosher Marketing Assets LLC
+// https://www.iana.org/domains/root/db/kosher.html
+kosher
+
+// kpmg : KPMG International Cooperative (KPMG International Genossenschaft)
+// https://www.iana.org/domains/root/db/kpmg.html
+kpmg
+
+// kpn : Koninklijke KPN N.V.
+// https://www.iana.org/domains/root/db/kpn.html
+kpn
+
+// krd : KRG Department of Information Technology
+// https://www.iana.org/domains/root/db/krd.html
+krd
+
+// kred : KredTLD Pty Ltd
+// https://www.iana.org/domains/root/db/kred.html
+kred
+
+// kuokgroup : Kerry Trading Co. Limited
+// https://www.iana.org/domains/root/db/kuokgroup.html
+kuokgroup
+
+// kyoto : Academic Institution: Kyoto Jyoho Gakuen
+// https://www.iana.org/domains/root/db/kyoto.html
+kyoto
+
+// lacaixa : Fundación Bancaria Caixa d’Estalvis i Pensions de Barcelona, “la Caixa”
+// https://www.iana.org/domains/root/db/lacaixa.html
+lacaixa
+
+// lamborghini : Automobili Lamborghini S.p.A.
+// https://www.iana.org/domains/root/db/lamborghini.html
+lamborghini
+
+// lamer : The Estée Lauder Companies Inc.
+// https://www.iana.org/domains/root/db/lamer.html
+lamer
+
+// lancaster : LANCASTER
+// https://www.iana.org/domains/root/db/lancaster.html
+lancaster
+
+// land : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/land.html
+land
+
+// landrover : Jaguar Land Rover Ltd
+// https://www.iana.org/domains/root/db/landrover.html
+landrover
+
+// lanxess : LANXESS Corporation
+// https://www.iana.org/domains/root/db/lanxess.html
+lanxess
+
+// lasalle : Jones Lang LaSalle Incorporated
+// https://www.iana.org/domains/root/db/lasalle.html
+lasalle
+
+// lat : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/lat.html
+lat
+
+// latino : Dish DBS Corporation
+// https://www.iana.org/domains/root/db/latino.html
+latino
+
+// latrobe : La Trobe University
+// https://www.iana.org/domains/root/db/latrobe.html
+latrobe
+
+// law : Registry Services, LLC
+// https://www.iana.org/domains/root/db/law.html
+law
+
+// lawyer : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/lawyer.html
+lawyer
+
+// lds : IRI Domain Management, LLC
+// https://www.iana.org/domains/root/db/lds.html
+lds
+
+// lease : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/lease.html
+lease
+
+// leclerc : A.C.D. LEC Association des Centres Distributeurs Edouard Leclerc
+// https://www.iana.org/domains/root/db/leclerc.html
+leclerc
+
+// lefrak : LeFrak Organization, Inc.
+// https://www.iana.org/domains/root/db/lefrak.html
+lefrak
+
+// legal : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/legal.html
+legal
+
+// lego : LEGO Juris A/S
+// https://www.iana.org/domains/root/db/lego.html
+lego
+
+// lexus : TOYOTA MOTOR CORPORATION
+// https://www.iana.org/domains/root/db/lexus.html
+lexus
+
+// lgbt : Identity Digital Limited
+// https://www.iana.org/domains/root/db/lgbt.html
+lgbt
+
+// lidl : Schwarz Domains und Services GmbH & Co. KG
+// https://www.iana.org/domains/root/db/lidl.html
+lidl
+
+// life : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/life.html
+life
+
+// lifeinsurance : American Council of Life Insurers
+// https://www.iana.org/domains/root/db/lifeinsurance.html
+lifeinsurance
+
+// lifestyle : Internet Naming Company LLC
+// https://www.iana.org/domains/root/db/lifestyle.html
+lifestyle
+
+// lighting : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/lighting.html
+lighting
+
+// like : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/like.html
+like
+
+// lilly : Eli Lilly and Company
+// https://www.iana.org/domains/root/db/lilly.html
+lilly
+
+// limited : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/limited.html
+limited
+
+// limo : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/limo.html
+limo
+
+// lincoln : Ford Motor Company
+// https://www.iana.org/domains/root/db/lincoln.html
+lincoln
+
+// link : Nova Registry Ltd
+// https://www.iana.org/domains/root/db/link.html
+link
+
+// lipsy : Lipsy Ltd
+// https://www.iana.org/domains/root/db/lipsy.html
+lipsy
+
+// live : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/live.html
+live
+
+// living : Internet Naming Company LLC
+// https://www.iana.org/domains/root/db/living.html
+living
+
+// llc : Identity Digital Limited
+// https://www.iana.org/domains/root/db/llc.html
+llc
+
+// llp : Intercap Registry Inc.
+// https://www.iana.org/domains/root/db/llp.html
+llp
+
+// loan : dot Loan Limited
+// https://www.iana.org/domains/root/db/loan.html
+loan
+
+// loans : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/loans.html
+loans
+
+// locker : Orange Domains LLC
+// https://www.iana.org/domains/root/db/locker.html
+locker
+
+// locus : Locus Analytics LLC
+// https://www.iana.org/domains/root/db/locus.html
+locus
+
+// lol : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/lol.html
+lol
+
+// london : Dot London Domains Limited
+// https://www.iana.org/domains/root/db/london.html
+london
+
+// lotte : Lotte Holdings Co., Ltd.
+// https://www.iana.org/domains/root/db/lotte.html
+lotte
+
+// lotto : Identity Digital Limited
+// https://www.iana.org/domains/root/db/lotto.html
+lotto
+
+// love : Merchant Law Group LLP
+// https://www.iana.org/domains/root/db/love.html
+love
+
+// lpl : LPL Holdings, Inc.
+// https://www.iana.org/domains/root/db/lpl.html
+lpl
+
+// lplfinancial : LPL Holdings, Inc.
+// https://www.iana.org/domains/root/db/lplfinancial.html
+lplfinancial
+
+// ltd : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/ltd.html
+ltd
+
+// ltda : InterNetX, Corp
+// https://www.iana.org/domains/root/db/ltda.html
+ltda
+
+// lundbeck : H. Lundbeck A/S
+// https://www.iana.org/domains/root/db/lundbeck.html
+lundbeck
+
+// luxe : Registry Services, LLC
+// https://www.iana.org/domains/root/db/luxe.html
+luxe
+
+// luxury : Luxury Partners, LLC
+// https://www.iana.org/domains/root/db/luxury.html
+luxury
+
+// madrid : Comunidad de Madrid
+// https://www.iana.org/domains/root/db/madrid.html
+madrid
+
+// maif : Mutuelle Assurance Instituteur France (MAIF)
+// https://www.iana.org/domains/root/db/maif.html
+maif
+
+// maison : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/maison.html
+maison
+
+// makeup : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/makeup.html
+makeup
+
+// man : MAN SE
+// https://www.iana.org/domains/root/db/man.html
+man
+
+// management : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/management.html
+management
+
+// mango : PUNTO FA S.L.
+// https://www.iana.org/domains/root/db/mango.html
+mango
+
+// map : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/map.html
+map
+
+// market : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/market.html
+market
+
+// marketing : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/marketing.html
+marketing
+
+// markets : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/markets.html
+markets
+
+// marriott : Marriott Worldwide Corporation
+// https://www.iana.org/domains/root/db/marriott.html
+marriott
+
+// marshalls : The TJX Companies, Inc.
+// https://www.iana.org/domains/root/db/marshalls.html
+marshalls
+
+// mattel : Mattel Sites, Inc.
+// https://www.iana.org/domains/root/db/mattel.html
+mattel
+
+// mba : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/mba.html
+mba
+
+// mckinsey : McKinsey Holdings, Inc.
+// https://www.iana.org/domains/root/db/mckinsey.html
+mckinsey
+
+// med : Medistry LLC
+// https://www.iana.org/domains/root/db/med.html
+med
+
+// media : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/media.html
+media
+
+// meet : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/meet.html
+meet
+
+// melbourne : The Crown in right of the State of Victoria, represented by its Department of State Development, Business and Innovation
+// https://www.iana.org/domains/root/db/melbourne.html
+melbourne
+
+// meme : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/meme.html
+meme
+
+// memorial : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/memorial.html
+memorial
+
+// men : Exclusive Registry Limited
+// https://www.iana.org/domains/root/db/men.html
+men
+
+// menu : Dot Menu Registry, LLC
+// https://www.iana.org/domains/root/db/menu.html
+menu
+
+// merckmsd : MSD Registry Holdings, Inc.
+// https://www.iana.org/domains/root/db/merckmsd.html
+merckmsd
+
+// miami : Registry Services, LLC
+// https://www.iana.org/domains/root/db/miami.html
+miami
+
+// microsoft : Microsoft Corporation
+// https://www.iana.org/domains/root/db/microsoft.html
+microsoft
+
+// mini : Bayerische Motoren Werke Aktiengesellschaft
+// https://www.iana.org/domains/root/db/mini.html
+mini
+
+// mint : Intuit Administrative Services, Inc.
+// https://www.iana.org/domains/root/db/mint.html
+mint
+
+// mit : Massachusetts Institute of Technology
+// https://www.iana.org/domains/root/db/mit.html
+mit
+
+// mitsubishi : Mitsubishi Corporation
+// https://www.iana.org/domains/root/db/mitsubishi.html
+mitsubishi
+
+// mlb : MLB Advanced Media DH, LLC
+// https://www.iana.org/domains/root/db/mlb.html
+mlb
+
+// mls : The Canadian Real Estate Association
+// https://www.iana.org/domains/root/db/mls.html
+mls
+
+// mma : MMA IARD
+// https://www.iana.org/domains/root/db/mma.html
+mma
+
+// mobile : Dish DBS Corporation
+// https://www.iana.org/domains/root/db/mobile.html
+mobile
+
+// moda : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/moda.html
+moda
+
+// moe : Interlink Systems Innovation Institute K.K.
+// https://www.iana.org/domains/root/db/moe.html
+moe
+
+// moi : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/moi.html
+moi
+
+// mom : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/mom.html
+mom
+
+// monash : Monash University
+// https://www.iana.org/domains/root/db/monash.html
+monash
+
+// money : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/money.html
+money
+
+// monster : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/monster.html
+monster
+
+// mormon : IRI Domain Management, LLC
+// https://www.iana.org/domains/root/db/mormon.html
+mormon
+
+// mortgage : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/mortgage.html
+mortgage
+
+// moscow : Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID)
+// https://www.iana.org/domains/root/db/moscow.html
+moscow
+
+// moto : Motorola Trademark Holdings, LLC
+// https://www.iana.org/domains/root/db/moto.html
+moto
+
+// motorcycles : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/motorcycles.html
+motorcycles
+
+// mov : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/mov.html
+mov
+
+// movie : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/movie.html
+movie
+
+// msd : MSD Registry Holdings, Inc.
+// https://www.iana.org/domains/root/db/msd.html
+msd
+
+// mtn : MTN Dubai Limited
+// https://www.iana.org/domains/root/db/mtn.html
+mtn
+
+// mtr : MTR Corporation Limited
+// https://www.iana.org/domains/root/db/mtr.html
+mtr
+
+// music : DotMusic Limited
+// https://www.iana.org/domains/root/db/music.html
+music
+
+// nab : National Australia Bank Limited
+// https://www.iana.org/domains/root/db/nab.html
+nab
+
+// nagoya : GMO Registry, Inc.
+// https://www.iana.org/domains/root/db/nagoya.html
+nagoya
+
+// natura : NATURA COSMÉTICOS S.A.
+// https://www.iana.org/domains/root/db/natura.html
+natura
+
+// navy : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/navy.html
+navy
+
+// nba : NBA REGISTRY, LLC
+// https://www.iana.org/domains/root/db/nba.html
+nba
+
+// nec : NEC Corporation
+// https://www.iana.org/domains/root/db/nec.html
+nec
+
+// netbank : COMMONWEALTH BANK OF AUSTRALIA
+// https://www.iana.org/domains/root/db/netbank.html
+netbank
+
+// netflix : Netflix, Inc.
+// https://www.iana.org/domains/root/db/netflix.html
+netflix
+
+// network : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/network.html
+network
+
+// neustar : NeuStar, Inc.
+// https://www.iana.org/domains/root/db/neustar.html
+neustar
+
+// new : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/new.html
+new
+
+// news : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/news.html
+news
+
+// next : Next plc
+// https://www.iana.org/domains/root/db/next.html
+next
+
+// nextdirect : Next plc
+// https://www.iana.org/domains/root/db/nextdirect.html
+nextdirect
+
+// nexus : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/nexus.html
+nexus
+
+// nfl : NFL Reg Ops LLC
+// https://www.iana.org/domains/root/db/nfl.html
+nfl
+
+// ngo : Public Interest Registry
+// https://www.iana.org/domains/root/db/ngo.html
+ngo
+
+// nhk : Japan Broadcasting Corporation (NHK)
+// https://www.iana.org/domains/root/db/nhk.html
+nhk
+
+// nico : DWANGO Co., Ltd.
+// https://www.iana.org/domains/root/db/nico.html
+nico
+
+// nike : NIKE, Inc.
+// https://www.iana.org/domains/root/db/nike.html
+nike
+
+// nikon : NIKON CORPORATION
+// https://www.iana.org/domains/root/db/nikon.html
+nikon
+
+// ninja : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/ninja.html
+ninja
+
+// nissan : NISSAN MOTOR CO., LTD.
+// https://www.iana.org/domains/root/db/nissan.html
+nissan
+
+// nissay : Nippon Life Insurance Company
+// https://www.iana.org/domains/root/db/nissay.html
+nissay
+
+// nokia : Nokia Corporation
+// https://www.iana.org/domains/root/db/nokia.html
+nokia
+
+// norton : NortonLifeLock Inc.
+// https://www.iana.org/domains/root/db/norton.html
+norton
+
+// now : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/now.html
+now
+
+// nowruz : Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
+// https://www.iana.org/domains/root/db/nowruz.html
+nowruz
+
+// nowtv : Starbucks (HK) Limited
+// https://www.iana.org/domains/root/db/nowtv.html
+nowtv
+
+// nra : NRA Holdings Company, INC.
+// https://www.iana.org/domains/root/db/nra.html
+nra
+
+// nrw : Minds + Machines GmbH
+// https://www.iana.org/domains/root/db/nrw.html
+nrw
+
+// ntt : NIPPON TELEGRAPH AND TELEPHONE CORPORATION
+// https://www.iana.org/domains/root/db/ntt.html
+ntt
+
+// nyc : The City of New York by and through the New York City Department of Information Technology & Telecommunications
+// https://www.iana.org/domains/root/db/nyc.html
+nyc
+
+// obi : OBI Group Holding SE & Co. KGaA
+// https://www.iana.org/domains/root/db/obi.html
+obi
+
+// observer : Fegistry, LLC
+// https://www.iana.org/domains/root/db/observer.html
+observer
+
+// office : Microsoft Corporation
+// https://www.iana.org/domains/root/db/office.html
+office
+
+// okinawa : BRregistry, Inc.
+// https://www.iana.org/domains/root/db/okinawa.html
+okinawa
+
+// olayan : Competrol (Luxembourg) Sarl
+// https://www.iana.org/domains/root/db/olayan.html
+olayan
+
+// olayangroup : Competrol (Luxembourg) Sarl
+// https://www.iana.org/domains/root/db/olayangroup.html
+olayangroup
+
+// ollo : Dish DBS Corporation
+// https://www.iana.org/domains/root/db/ollo.html
+ollo
+
+// omega : The Swatch Group Ltd
+// https://www.iana.org/domains/root/db/omega.html
+omega
+
+// one : One.com A/S
+// https://www.iana.org/domains/root/db/one.html
+one
+
+// ong : Public Interest Registry
+// https://www.iana.org/domains/root/db/ong.html
+ong
+
+// onl : iRegistry GmbH
+// https://www.iana.org/domains/root/db/onl.html
+onl
+
+// online : Radix Technologies Inc.
+// https://www.iana.org/domains/root/db/online.html
+online
+
+// ooo : INFIBEAM AVENUES LIMITED
+// https://www.iana.org/domains/root/db/ooo.html
+ooo
+
+// open : American Express Travel Related Services Company, Inc.
+// https://www.iana.org/domains/root/db/open.html
+open
+
+// oracle : Oracle Corporation
+// https://www.iana.org/domains/root/db/oracle.html
+oracle
+
+// orange : Orange Brand Services Limited
+// https://www.iana.org/domains/root/db/orange.html
+orange
+
+// organic : Identity Digital Limited
+// https://www.iana.org/domains/root/db/organic.html
+organic
+
+// origins : The Estée Lauder Companies Inc.
+// https://www.iana.org/domains/root/db/origins.html
+origins
+
+// osaka : Osaka Registry Co., Ltd.
+// https://www.iana.org/domains/root/db/osaka.html
+osaka
+
+// otsuka : Otsuka Holdings Co., Ltd.
+// https://www.iana.org/domains/root/db/otsuka.html
+otsuka
+
+// ott : Dish DBS Corporation
+// https://www.iana.org/domains/root/db/ott.html
+ott
+
+// ovh : MédiaBC
+// https://www.iana.org/domains/root/db/ovh.html
+ovh
+
+// page : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/page.html
+page
+
+// panasonic : Panasonic Holdings Corporation
+// https://www.iana.org/domains/root/db/panasonic.html
+panasonic
+
+// paris : City of Paris
+// https://www.iana.org/domains/root/db/paris.html
+paris
+
+// pars : Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
+// https://www.iana.org/domains/root/db/pars.html
+pars
+
+// partners : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/partners.html
+partners
+
+// parts : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/parts.html
+parts
+
+// party : Blue Sky Registry Limited
+// https://www.iana.org/domains/root/db/party.html
+party
+
+// pay : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/pay.html
+pay
+
+// pccw : PCCW Enterprises Limited
+// https://www.iana.org/domains/root/db/pccw.html
+pccw
+
+// pet : Identity Digital Limited
+// https://www.iana.org/domains/root/db/pet.html
+pet
+
+// pfizer : Pfizer Inc.
+// https://www.iana.org/domains/root/db/pfizer.html
+pfizer
+
+// pharmacy : National Association of Boards of Pharmacy
+// https://www.iana.org/domains/root/db/pharmacy.html
+pharmacy
+
+// phd : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/phd.html
+phd
+
+// philips : Koninklijke Philips N.V.
+// https://www.iana.org/domains/root/db/philips.html
+philips
+
+// phone : Dish DBS Corporation
+// https://www.iana.org/domains/root/db/phone.html
+phone
+
+// photo : Registry Services, LLC
+// https://www.iana.org/domains/root/db/photo.html
+photo
+
+// photography : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/photography.html
+photography
+
+// photos : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/photos.html
+photos
+
+// physio : PhysBiz Pty Ltd
+// https://www.iana.org/domains/root/db/physio.html
+physio
+
+// pics : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/pics.html
+pics
+
+// pictet : Pictet Europe S.A.
+// https://www.iana.org/domains/root/db/pictet.html
+pictet
+
+// pictures : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/pictures.html
+pictures
+
+// pid : Top Level Spectrum, Inc.
+// https://www.iana.org/domains/root/db/pid.html
+pid
+
+// pin : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/pin.html
+pin
+
+// ping : Ping Registry Provider, Inc.
+// https://www.iana.org/domains/root/db/ping.html
+ping
+
+// pink : Identity Digital Limited
+// https://www.iana.org/domains/root/db/pink.html
+pink
+
+// pioneer : Pioneer Corporation
+// https://www.iana.org/domains/root/db/pioneer.html
+pioneer
+
+// pizza : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/pizza.html
+pizza
+
+// place : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/place.html
+place
+
+// play : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/play.html
+play
+
+// playstation : Sony Interactive Entertainment Inc.
+// https://www.iana.org/domains/root/db/playstation.html
+playstation
+
+// plumbing : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/plumbing.html
+plumbing
+
+// plus : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/plus.html
+plus
+
+// pnc : PNC Domain Co., LLC
+// https://www.iana.org/domains/root/db/pnc.html
+pnc
+
+// pohl : Deutsche Vermögensberatung Aktiengesellschaft DVAG
+// https://www.iana.org/domains/root/db/pohl.html
+pohl
+
+// poker : Identity Digital Limited
+// https://www.iana.org/domains/root/db/poker.html
+poker
+
+// politie : Politie Nederland
+// https://www.iana.org/domains/root/db/politie.html
+politie
+
+// porn : ICM Registry PN LLC
+// https://www.iana.org/domains/root/db/porn.html
+porn
+
+// pramerica : Prudential Financial, Inc.
+// https://www.iana.org/domains/root/db/pramerica.html
+pramerica
+
+// praxi : Praxi S.p.A.
+// https://www.iana.org/domains/root/db/praxi.html
+praxi
+
+// press : Radix Technologies Inc.
+// https://www.iana.org/domains/root/db/press.html
+press
+
+// prime : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/prime.html
+prime
+
+// prod : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/prod.html
+prod
+
+// productions : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/productions.html
+productions
+
+// prof : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/prof.html
+prof
+
+// progressive : Progressive Casualty Insurance Company
+// https://www.iana.org/domains/root/db/progressive.html
+progressive
+
+// promo : Identity Digital Limited
+// https://www.iana.org/domains/root/db/promo.html
+promo
+
+// properties : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/properties.html
+properties
+
+// property : Digital Property Infrastructure Limited
+// https://www.iana.org/domains/root/db/property.html
+property
+
+// protection : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/protection.html
+protection
+
+// pru : Prudential Financial, Inc.
+// https://www.iana.org/domains/root/db/pru.html
+pru
+
+// prudential : Prudential Financial, Inc.
+// https://www.iana.org/domains/root/db/prudential.html
+prudential
+
+// pub : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/pub.html
+pub
+
+// pwc : PricewaterhouseCoopers LLP
+// https://www.iana.org/domains/root/db/pwc.html
+pwc
+
+// qpon : dotQPON LLC
+// https://www.iana.org/domains/root/db/qpon.html
+qpon
+
+// quebec : PointQuébec Inc
+// https://www.iana.org/domains/root/db/quebec.html
+quebec
+
+// quest : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/quest.html
+quest
+
+// racing : Premier Registry Limited
+// https://www.iana.org/domains/root/db/racing.html
+racing
+
+// radio : European Broadcasting Union (EBU)
+// https://www.iana.org/domains/root/db/radio.html
+radio
+
+// read : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/read.html
+read
+
+// realestate : dotRealEstate LLC
+// https://www.iana.org/domains/root/db/realestate.html
+realestate
+
+// realtor : Real Estate Domains LLC
+// https://www.iana.org/domains/root/db/realtor.html
+realtor
+
+// realty : Internet Naming Company LLC
+// https://www.iana.org/domains/root/db/realty.html
+realty
+
+// recipes : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/recipes.html
+recipes
+
+// red : Identity Digital Limited
+// https://www.iana.org/domains/root/db/red.html
+red
+
+// redstone : Redstone Haute Couture Co., Ltd.
+// https://www.iana.org/domains/root/db/redstone.html
+redstone
+
+// redumbrella : Travelers TLD, LLC
+// https://www.iana.org/domains/root/db/redumbrella.html
+redumbrella
+
+// rehab : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/rehab.html
+rehab
+
+// reise : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/reise.html
+reise
+
+// reisen : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/reisen.html
+reisen
+
+// reit : National Association of Real Estate Investment Trusts, Inc.
+// https://www.iana.org/domains/root/db/reit.html
+reit
+
+// reliance : Reliance Industries Limited
+// https://www.iana.org/domains/root/db/reliance.html
+reliance
+
+// ren : ZDNS International Limited
+// https://www.iana.org/domains/root/db/ren.html
+ren
+
+// rent : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/rent.html
+rent
+
+// rentals : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/rentals.html
+rentals
+
+// repair : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/repair.html
+repair
+
+// report : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/report.html
+report
+
+// republican : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/republican.html
+republican
+
+// rest : Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable
+// https://www.iana.org/domains/root/db/rest.html
+rest
+
+// restaurant : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/restaurant.html
+restaurant
+
+// review : dot Review Limited
+// https://www.iana.org/domains/root/db/review.html
+review
+
+// reviews : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/reviews.html
+reviews
+
+// rexroth : Robert Bosch GMBH
+// https://www.iana.org/domains/root/db/rexroth.html
+rexroth
+
+// rich : iRegistry GmbH
+// https://www.iana.org/domains/root/db/rich.html
+rich
+
+// richardli : Pacific Century Asset Management (HK) Limited
+// https://www.iana.org/domains/root/db/richardli.html
+richardli
+
+// ricoh : Ricoh Company, Ltd.
+// https://www.iana.org/domains/root/db/ricoh.html
+ricoh
+
+// ril : Reliance Industries Limited
+// https://www.iana.org/domains/root/db/ril.html
+ril
+
+// rio : Empresa Municipal de Informática SA - IPLANRIO
+// https://www.iana.org/domains/root/db/rio.html
+rio
+
+// rip : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/rip.html
+rip
+
+// rocks : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/rocks.html
+rocks
+
+// rodeo : Registry Services, LLC
+// https://www.iana.org/domains/root/db/rodeo.html
+rodeo
+
+// rogers : Rogers Communications Canada Inc.
+// https://www.iana.org/domains/root/db/rogers.html
+rogers
+
+// room : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/room.html
+room
+
+// rsvp : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/rsvp.html
+rsvp
+
+// rugby : World Rugby Strategic Developments Limited
+// https://www.iana.org/domains/root/db/rugby.html
+rugby
+
+// ruhr : dotSaarland GmbH
+// https://www.iana.org/domains/root/db/ruhr.html
+ruhr
+
+// run : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/run.html
+run
+
+// rwe : RWE AG
+// https://www.iana.org/domains/root/db/rwe.html
+rwe
+
+// ryukyu : BRregistry, Inc.
+// https://www.iana.org/domains/root/db/ryukyu.html
+ryukyu
+
+// saarland : dotSaarland GmbH
+// https://www.iana.org/domains/root/db/saarland.html
+saarland
+
+// safe : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/safe.html
+safe
+
+// safety : Safety Registry Services, LLC.
+// https://www.iana.org/domains/root/db/safety.html
+safety
+
+// sakura : SAKURA Internet Inc.
+// https://www.iana.org/domains/root/db/sakura.html
+sakura
+
+// sale : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/sale.html
+sale
+
+// salon : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/salon.html
+salon
+
+// samsclub : Wal-Mart Stores, Inc.
+// https://www.iana.org/domains/root/db/samsclub.html
+samsclub
+
+// samsung : SAMSUNG SDS CO., LTD
+// https://www.iana.org/domains/root/db/samsung.html
+samsung
+
+// sandvik : Sandvik AB
+// https://www.iana.org/domains/root/db/sandvik.html
+sandvik
+
+// sandvikcoromant : Sandvik AB
+// https://www.iana.org/domains/root/db/sandvikcoromant.html
+sandvikcoromant
+
+// sanofi : Sanofi
+// https://www.iana.org/domains/root/db/sanofi.html
+sanofi
+
+// sap : SAP AG
+// https://www.iana.org/domains/root/db/sap.html
+sap
+
+// sarl : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/sarl.html
+sarl
+
+// sas : Research IP LLC
+// https://www.iana.org/domains/root/db/sas.html
+sas
+
+// save : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/save.html
+save
+
+// saxo : Saxo Bank A/S
+// https://www.iana.org/domains/root/db/saxo.html
+saxo
+
+// sbi : STATE BANK OF INDIA
+// https://www.iana.org/domains/root/db/sbi.html
+sbi
+
+// sbs : ShortDot SA
+// https://www.iana.org/domains/root/db/sbs.html
+sbs
+
+// scb : The Siam Commercial Bank Public Company Limited ("SCB")
+// https://www.iana.org/domains/root/db/scb.html
+scb
+
+// schaeffler : Schaeffler Technologies AG & Co. KG
+// https://www.iana.org/domains/root/db/schaeffler.html
+schaeffler
+
+// schmidt : SCHMIDT GROUPE S.A.S.
+// https://www.iana.org/domains/root/db/schmidt.html
+schmidt
+
+// scholarships : Scholarships.com, LLC
+// https://www.iana.org/domains/root/db/scholarships.html
+scholarships
+
+// school : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/school.html
+school
+
+// schule : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/schule.html
+schule
+
+// schwarz : Schwarz Domains und Services GmbH & Co. KG
+// https://www.iana.org/domains/root/db/schwarz.html
+schwarz
+
+// science : dot Science Limited
+// https://www.iana.org/domains/root/db/science.html
+science
+
+// scot : Dot Scot Registry Limited
+// https://www.iana.org/domains/root/db/scot.html
+scot
+
+// search : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/search.html
+search
+
+// seat : SEAT, S.A. (Sociedad Unipersonal)
+// https://www.iana.org/domains/root/db/seat.html
+seat
+
+// secure : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/secure.html
+secure
+
+// security : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/security.html
+security
+
+// seek : Seek Limited
+// https://www.iana.org/domains/root/db/seek.html
+seek
+
+// select : Registry Services, LLC
+// https://www.iana.org/domains/root/db/select.html
+select
+
+// sener : Sener Ingeniería y Sistemas, S.A.
+// https://www.iana.org/domains/root/db/sener.html
+sener
+
+// services : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/services.html
+services
+
+// seven : Seven West Media Ltd
+// https://www.iana.org/domains/root/db/seven.html
+seven
+
+// sew : SEW-EURODRIVE GmbH & Co KG
+// https://www.iana.org/domains/root/db/sew.html
+sew
+
+// sex : ICM Registry SX LLC
+// https://www.iana.org/domains/root/db/sex.html
+sex
+
+// sexy : Internet Naming Company LLC
+// https://www.iana.org/domains/root/db/sexy.html
+sexy
+
+// sfr : Societe Francaise du Radiotelephone - SFR
+// https://www.iana.org/domains/root/db/sfr.html
+sfr
+
+// shangrila : Shangri‐La International Hotel Management Limited
+// https://www.iana.org/domains/root/db/shangrila.html
+shangrila
+
+// sharp : Sharp Corporation
+// https://www.iana.org/domains/root/db/sharp.html
+sharp
+
+// shaw : Shaw Cablesystems G.P.
+// https://www.iana.org/domains/root/db/shaw.html
+shaw
+
+// shell : Shell Information Technology International Inc
+// https://www.iana.org/domains/root/db/shell.html
+shell
+
+// shia : Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
+// https://www.iana.org/domains/root/db/shia.html
+shia
+
+// shiksha : Identity Digital Limited
+// https://www.iana.org/domains/root/db/shiksha.html
+shiksha
+
+// shoes : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/shoes.html
+shoes
+
+// shop : GMO Registry, Inc.
+// https://www.iana.org/domains/root/db/shop.html
+shop
+
+// shopping : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/shopping.html
+shopping
+
+// shouji : Beijing Qihu Keji Co., Ltd.
+// https://www.iana.org/domains/root/db/shouji.html
+shouji
+
+// show : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/show.html
+show
+
+// silk : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/silk.html
+silk
+
+// sina : Sina Corporation
+// https://www.iana.org/domains/root/db/sina.html
+sina
+
+// singles : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/singles.html
+singles
+
+// site : Radix Technologies Inc.
+// https://www.iana.org/domains/root/db/site.html
+site
+
+// ski : Identity Digital Limited
+// https://www.iana.org/domains/root/db/ski.html
+ski
+
+// skin : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/skin.html
+skin
+
+// sky : Sky International AG
+// https://www.iana.org/domains/root/db/sky.html
+sky
+
+// skype : Microsoft Corporation
+// https://www.iana.org/domains/root/db/skype.html
+skype
+
+// sling : DISH Technologies L.L.C.
+// https://www.iana.org/domains/root/db/sling.html
+sling
+
+// smart : Smart Communications, Inc. (SMART)
+// https://www.iana.org/domains/root/db/smart.html
+smart
+
+// smile : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/smile.html
+smile
+
+// sncf : Société Nationale SNCF
+// https://www.iana.org/domains/root/db/sncf.html
+sncf
+
+// soccer : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/soccer.html
+soccer
+
+// social : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/social.html
+social
+
+// softbank : SoftBank Group Corp.
+// https://www.iana.org/domains/root/db/softbank.html
+softbank
+
+// software : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/software.html
+software
+
+// sohu : Sohu.com Limited
+// https://www.iana.org/domains/root/db/sohu.html
+sohu
+
+// solar : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/solar.html
+solar
+
+// solutions : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/solutions.html
+solutions
+
+// song : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/song.html
+song
+
+// sony : Sony Corporation
+// https://www.iana.org/domains/root/db/sony.html
+sony
+
+// soy : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/soy.html
+soy
+
+// spa : Asia Spa and Wellness Promotion Council Limited
+// https://www.iana.org/domains/root/db/spa.html
+spa
+
+// space : Radix Technologies Inc.
+// https://www.iana.org/domains/root/db/space.html
+space
+
+// sport : SportAccord
+// https://www.iana.org/domains/root/db/sport.html
+sport
+
+// spot : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/spot.html
+spot
+
+// srl : InterNetX, Corp
+// https://www.iana.org/domains/root/db/srl.html
+srl
+
+// stada : STADA Arzneimittel AG
+// https://www.iana.org/domains/root/db/stada.html
+stada
+
+// staples : Staples, Inc.
+// https://www.iana.org/domains/root/db/staples.html
+staples
+
+// star : Star India Private Limited
+// https://www.iana.org/domains/root/db/star.html
+star
+
+// statebank : STATE BANK OF INDIA
+// https://www.iana.org/domains/root/db/statebank.html
+statebank
+
+// statefarm : State Farm Mutual Automobile Insurance Company
+// https://www.iana.org/domains/root/db/statefarm.html
+statefarm
+
+// stc : Saudi Telecom Company
+// https://www.iana.org/domains/root/db/stc.html
+stc
+
+// stcgroup : Saudi Telecom Company
+// https://www.iana.org/domains/root/db/stcgroup.html
+stcgroup
+
+// stockholm : Stockholms kommun
+// https://www.iana.org/domains/root/db/stockholm.html
+stockholm
+
+// storage : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/storage.html
+storage
+
+// store : Radix Technologies Inc.
+// https://www.iana.org/domains/root/db/store.html
+store
+
+// stream : dot Stream Limited
+// https://www.iana.org/domains/root/db/stream.html
+stream
+
+// studio : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/studio.html
+studio
+
+// study : Registry Services, LLC
+// https://www.iana.org/domains/root/db/study.html
+study
+
+// style : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/style.html
+style
+
+// sucks : Vox Populi Registry Ltd.
+// https://www.iana.org/domains/root/db/sucks.html
+sucks
+
+// supplies : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/supplies.html
+supplies
+
+// supply : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/supply.html
+supply
+
+// support : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/support.html
+support
+
+// surf : Registry Services, LLC
+// https://www.iana.org/domains/root/db/surf.html
+surf
+
+// surgery : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/surgery.html
+surgery
+
+// suzuki : SUZUKI MOTOR CORPORATION
+// https://www.iana.org/domains/root/db/suzuki.html
+suzuki
+
+// swatch : The Swatch Group Ltd
+// https://www.iana.org/domains/root/db/swatch.html
+swatch
+
+// swiss : Swiss Confederation
+// https://www.iana.org/domains/root/db/swiss.html
+swiss
+
+// sydney : State of New South Wales, Department of Premier and Cabinet
+// https://www.iana.org/domains/root/db/sydney.html
+sydney
+
+// systems : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/systems.html
+systems
+
+// tab : Tabcorp Holdings Limited
+// https://www.iana.org/domains/root/db/tab.html
+tab
+
+// taipei : Taipei City Government
+// https://www.iana.org/domains/root/db/taipei.html
+taipei
+
+// talk : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/talk.html
+talk
+
+// taobao : Alibaba Group Holding Limited
+// https://www.iana.org/domains/root/db/taobao.html
+taobao
+
+// target : Target Domain Holdings, LLC
+// https://www.iana.org/domains/root/db/target.html
+target
+
+// tatamotors : Tata Motors Ltd
+// https://www.iana.org/domains/root/db/tatamotors.html
+tatamotors
+
+// tatar : Limited Liability Company "Coordination Center of Regional Domain of Tatarstan Republic"
+// https://www.iana.org/domains/root/db/tatar.html
+tatar
+
+// tattoo : Registry Services, LLC
+// https://www.iana.org/domains/root/db/tattoo.html
+tattoo
+
+// tax : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/tax.html
+tax
+
+// taxi : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/taxi.html
+taxi
+
+// tci : Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
+// https://www.iana.org/domains/root/db/tci.html
+tci
+
+// tdk : TDK Corporation
+// https://www.iana.org/domains/root/db/tdk.html
+tdk
+
+// team : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/team.html
+team
+
+// tech : Radix Technologies Inc.
+// https://www.iana.org/domains/root/db/tech.html
+tech
+
+// technology : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/technology.html
+technology
+
+// temasek : Temasek Holdings (Private) Limited
+// https://www.iana.org/domains/root/db/temasek.html
+temasek
+
+// tennis : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/tennis.html
+tennis
+
+// teva : Teva Pharmaceutical Industries Limited
+// https://www.iana.org/domains/root/db/teva.html
+teva
+
+// thd : Home Depot Product Authority, LLC
+// https://www.iana.org/domains/root/db/thd.html
+thd
+
+// theater : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/theater.html
+theater
+
+// theatre : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/theatre.html
+theatre
+
+// tiaa : Teachers Insurance and Annuity Association of America
+// https://www.iana.org/domains/root/db/tiaa.html
+tiaa
+
+// tickets : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/tickets.html
+tickets
+
+// tienda : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/tienda.html
+tienda
+
+// tips : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/tips.html
+tips
+
+// tires : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/tires.html
+tires
+
+// tirol : punkt Tirol GmbH
+// https://www.iana.org/domains/root/db/tirol.html
+tirol
+
+// tjmaxx : The TJX Companies, Inc.
+// https://www.iana.org/domains/root/db/tjmaxx.html
+tjmaxx
+
+// tjx : The TJX Companies, Inc.
+// https://www.iana.org/domains/root/db/tjx.html
+tjx
+
+// tkmaxx : The TJX Companies, Inc.
+// https://www.iana.org/domains/root/db/tkmaxx.html
+tkmaxx
+
+// tmall : Alibaba Group Holding Limited
+// https://www.iana.org/domains/root/db/tmall.html
+tmall
+
+// today : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/today.html
+today
+
+// tokyo : GMO Registry, Inc.
+// https://www.iana.org/domains/root/db/tokyo.html
+tokyo
+
+// tools : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/tools.html
+tools
+
+// top : .TOP Registry
+// https://www.iana.org/domains/root/db/top.html
+top
+
+// toray : Toray Industries, Inc.
+// https://www.iana.org/domains/root/db/toray.html
+toray
+
+// toshiba : TOSHIBA Corporation
+// https://www.iana.org/domains/root/db/toshiba.html
+toshiba
+
+// total : TotalEnergies SE
+// https://www.iana.org/domains/root/db/total.html
+total
+
+// tours : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/tours.html
+tours
+
+// town : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/town.html
+town
+
+// toyota : TOYOTA MOTOR CORPORATION
+// https://www.iana.org/domains/root/db/toyota.html
+toyota
+
+// toys : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/toys.html
+toys
+
+// trade : Elite Registry Limited
+// https://www.iana.org/domains/root/db/trade.html
+trade
+
+// trading : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/trading.html
+trading
+
+// training : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/training.html
+training
+
+// travel : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/travel.html
+travel
+
+// travelers : Travelers TLD, LLC
+// https://www.iana.org/domains/root/db/travelers.html
+travelers
+
+// travelersinsurance : Travelers TLD, LLC
+// https://www.iana.org/domains/root/db/travelersinsurance.html
+travelersinsurance
+
+// trust : Internet Naming Company LLC
+// https://www.iana.org/domains/root/db/trust.html
+trust
+
+// trv : Travelers TLD, LLC
+// https://www.iana.org/domains/root/db/trv.html
+trv
+
+// tube : Latin American Telecom LLC
+// https://www.iana.org/domains/root/db/tube.html
+tube
+
+// tui : TUI AG
+// https://www.iana.org/domains/root/db/tui.html
+tui
+
+// tunes : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/tunes.html
+tunes
+
+// tushu : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/tushu.html
+tushu
+
+// tvs : T V SUNDRAM IYENGAR & SONS LIMITED
+// https://www.iana.org/domains/root/db/tvs.html
+tvs
+
+// ubank : National Australia Bank Limited
+// https://www.iana.org/domains/root/db/ubank.html
+ubank
+
+// ubs : UBS AG
+// https://www.iana.org/domains/root/db/ubs.html
+ubs
+
+// unicom : China United Network Communications Corporation Limited
+// https://www.iana.org/domains/root/db/unicom.html
+unicom
+
+// university : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/university.html
+university
+
+// uno : Radix Technologies Inc.
+// https://www.iana.org/domains/root/db/uno.html
+uno
+
+// uol : UBN INTERNET LTDA.
+// https://www.iana.org/domains/root/db/uol.html
+uol
+
+// ups : UPS Market Driver, Inc.
+// https://www.iana.org/domains/root/db/ups.html
+ups
+
+// vacations : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/vacations.html
+vacations
+
+// vana : Internet Naming Company LLC
+// https://www.iana.org/domains/root/db/vana.html
+vana
+
+// vanguard : The Vanguard Group, Inc.
+// https://www.iana.org/domains/root/db/vanguard.html
+vanguard
+
+// vegas : Dot Vegas, Inc.
+// https://www.iana.org/domains/root/db/vegas.html
+vegas
+
+// ventures : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/ventures.html
+ventures
+
+// verisign : VeriSign, Inc.
+// https://www.iana.org/domains/root/db/verisign.html
+verisign
+
+// versicherung : tldbox GmbH
+// https://www.iana.org/domains/root/db/versicherung.html
+versicherung
+
+// vet : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/vet.html
+vet
+
+// viajes : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/viajes.html
+viajes
+
+// video : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/video.html
+video
+
+// vig : VIENNA INSURANCE GROUP AG Wiener Versicherung Gruppe
+// https://www.iana.org/domains/root/db/vig.html
+vig
+
+// viking : Viking River Cruises (Bermuda) Ltd.
+// https://www.iana.org/domains/root/db/viking.html
+viking
+
+// villas : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/villas.html
+villas
+
+// vin : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/vin.html
+vin
+
+// vip : Registry Services, LLC
+// https://www.iana.org/domains/root/db/vip.html
+vip
+
+// virgin : Virgin Enterprises Limited
+// https://www.iana.org/domains/root/db/virgin.html
+virgin
+
+// visa : Visa Worldwide Pte. Limited
+// https://www.iana.org/domains/root/db/visa.html
+visa
+
+// vision : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/vision.html
+vision
+
+// viva : Saudi Telecom Company
+// https://www.iana.org/domains/root/db/viva.html
+viva
+
+// vivo : Telefonica Brasil S.A.
+// https://www.iana.org/domains/root/db/vivo.html
+vivo
+
+// vlaanderen : DNS.be vzw
+// https://www.iana.org/domains/root/db/vlaanderen.html
+vlaanderen
+
+// vodka : Registry Services, LLC
+// https://www.iana.org/domains/root/db/vodka.html
+vodka
+
+// volvo : Volvo Holding Sverige Aktiebolag
+// https://www.iana.org/domains/root/db/volvo.html
+volvo
+
+// vote : Monolith Registry LLC
+// https://www.iana.org/domains/root/db/vote.html
+vote
+
+// voting : Valuetainment Corp.
+// https://www.iana.org/domains/root/db/voting.html
+voting
+
+// voto : Monolith Registry LLC
+// https://www.iana.org/domains/root/db/voto.html
+voto
+
+// voyage : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/voyage.html
+voyage
+
+// wales : Nominet UK
+// https://www.iana.org/domains/root/db/wales.html
+wales
+
+// walmart : Wal-Mart Stores, Inc.
+// https://www.iana.org/domains/root/db/walmart.html
+walmart
+
+// walter : Sandvik AB
+// https://www.iana.org/domains/root/db/walter.html
+walter
+
+// wang : Zodiac Wang Limited
+// https://www.iana.org/domains/root/db/wang.html
+wang
+
+// wanggou : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/wanggou.html
+wanggou
+
+// watch : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/watch.html
+watch
+
+// watches : Identity Digital Limited
+// https://www.iana.org/domains/root/db/watches.html
+watches
+
+// weather : International Business Machines Corporation
+// https://www.iana.org/domains/root/db/weather.html
+weather
+
+// weatherchannel : International Business Machines Corporation
+// https://www.iana.org/domains/root/db/weatherchannel.html
+weatherchannel
+
+// webcam : dot Webcam Limited
+// https://www.iana.org/domains/root/db/webcam.html
+webcam
+
+// weber : Saint-Gobain Weber SA
+// https://www.iana.org/domains/root/db/weber.html
+weber
+
+// website : Radix Technologies Inc.
+// https://www.iana.org/domains/root/db/website.html
+website
+
+// wed
+// https://www.iana.org/domains/root/db/wed.html
+wed
+
+// wedding : Registry Services, LLC
+// https://www.iana.org/domains/root/db/wedding.html
+wedding
+
+// weibo : Sina Corporation
+// https://www.iana.org/domains/root/db/weibo.html
+weibo
+
+// weir : Weir Group IP Limited
+// https://www.iana.org/domains/root/db/weir.html
+weir
+
+// whoswho : Who's Who Registry
+// https://www.iana.org/domains/root/db/whoswho.html
+whoswho
+
+// wien : punkt.wien GmbH
+// https://www.iana.org/domains/root/db/wien.html
+wien
+
+// wiki : Registry Services, LLC
+// https://www.iana.org/domains/root/db/wiki.html
+wiki
+
+// williamhill : William Hill Organization Limited
+// https://www.iana.org/domains/root/db/williamhill.html
+williamhill
+
+// win : First Registry Limited
+// https://www.iana.org/domains/root/db/win.html
+win
+
+// windows : Microsoft Corporation
+// https://www.iana.org/domains/root/db/windows.html
+windows
+
+// wine : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/wine.html
+wine
+
+// winners : The TJX Companies, Inc.
+// https://www.iana.org/domains/root/db/winners.html
+winners
+
+// wme : William Morris Endeavor Entertainment, LLC
+// https://www.iana.org/domains/root/db/wme.html
+wme
+
+// wolterskluwer : Wolters Kluwer N.V.
+// https://www.iana.org/domains/root/db/wolterskluwer.html
+wolterskluwer
+
+// woodside : Woodside Petroleum Limited
+// https://www.iana.org/domains/root/db/woodside.html
+woodside
+
+// work : Registry Services, LLC
+// https://www.iana.org/domains/root/db/work.html
+work
+
+// works : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/works.html
+works
+
+// world : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/world.html
+world
+
+// wow : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/wow.html
+wow
+
+// wtc : World Trade Centers Association, Inc.
+// https://www.iana.org/domains/root/db/wtc.html
+wtc
+
+// wtf : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/wtf.html
+wtf
+
+// xbox : Microsoft Corporation
+// https://www.iana.org/domains/root/db/xbox.html
+xbox
+
+// xerox : Xerox DNHC LLC
+// https://www.iana.org/domains/root/db/xerox.html
+xerox
+
+// xihuan : Beijing Qihu Keji Co., Ltd.
+// https://www.iana.org/domains/root/db/xihuan.html
+xihuan
+
+// xin : Elegant Leader Limited
+// https://www.iana.org/domains/root/db/xin.html
+xin
+
+// xn--11b4c3d : VeriSign Sarl
+// https://www.iana.org/domains/root/db/xn--11b4c3d.html
+कॉम
+
+// xn--1ck2e1b : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/xn--1ck2e1b.html
+セール
+
+// xn--1qqw23a : Guangzhou YU Wei Information Technology Co., Ltd.
+// https://www.iana.org/domains/root/db/xn--1qqw23a.html
+佛山
+
+// xn--30rr7y : Excellent First Limited
+// https://www.iana.org/domains/root/db/xn--30rr7y.html
+慈善
+
+// xn--3bst00m : Eagle Horizon Limited
+// https://www.iana.org/domains/root/db/xn--3bst00m.html
+集团
+
+// xn--3ds443g : TLD REGISTRY LIMITED OY
+// https://www.iana.org/domains/root/db/xn--3ds443g.html
+在线
+
+// xn--3pxu8k : VeriSign Sarl
+// https://www.iana.org/domains/root/db/xn--3pxu8k.html
+点看
+
+// xn--42c2d9a : VeriSign Sarl
+// https://www.iana.org/domains/root/db/xn--42c2d9a.html
+คอม
+
+// xn--45q11c : Zodiac Gemini Ltd
+// https://www.iana.org/domains/root/db/xn--45q11c.html
+八卦
+
+// xn--4gbrim : Helium TLDs Ltd
+// https://www.iana.org/domains/root/db/xn--4gbrim.html
+موقع
+
+// xn--55qw42g : China Organizational Name Administration Center
+// https://www.iana.org/domains/root/db/xn--55qw42g.html
+公益
+
+// xn--55qx5d : China Internet Network Information Center (CNNIC)
+// https://www.iana.org/domains/root/db/xn--55qx5d.html
+公司
+
+// xn--5su34j936bgsg : Shangri‐La International Hotel Management Limited
+// https://www.iana.org/domains/root/db/xn--5su34j936bgsg.html
+香格里拉
+
+// xn--5tzm5g : Global Website TLD Asia Limited
+// https://www.iana.org/domains/root/db/xn--5tzm5g.html
+网站
+
+// xn--6frz82g : Identity Digital Limited
+// https://www.iana.org/domains/root/db/xn--6frz82g.html
+移动
+
+// xn--6qq986b3xl : Tycoon Treasure Limited
+// https://www.iana.org/domains/root/db/xn--6qq986b3xl.html
+我爱你
+
+// xn--80adxhks : Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID)
+// https://www.iana.org/domains/root/db/xn--80adxhks.html
+москва
+
+// xn--80aqecdr1a : Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication)
+// https://www.iana.org/domains/root/db/xn--80aqecdr1a.html
+католик
+
+// xn--80asehdb : CORE Association
+// https://www.iana.org/domains/root/db/xn--80asehdb.html
+онлайн
+
+// xn--80aswg : CORE Association
+// https://www.iana.org/domains/root/db/xn--80aswg.html
+сайт
+
+// xn--8y0a063a : China United Network Communications Corporation Limited
+// https://www.iana.org/domains/root/db/xn--8y0a063a.html
+联通
+
+// xn--9dbq2a : VeriSign Sarl
+// https://www.iana.org/domains/root/db/xn--9dbq2a.html
+קום
+
+// xn--9et52u : RISE VICTORY LIMITED
+// https://www.iana.org/domains/root/db/xn--9et52u.html
+时尚
+
+// xn--9krt00a : Sina Corporation
+// https://www.iana.org/domains/root/db/xn--9krt00a.html
+微博
+
+// xn--b4w605ferd : Temasek Holdings (Private) Limited
+// https://www.iana.org/domains/root/db/xn--b4w605ferd.html
+淡马锡
+
+// xn--bck1b9a5dre4c : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/xn--bck1b9a5dre4c.html
+ファッション
+
+// xn--c1avg : Public Interest Registry
+// https://www.iana.org/domains/root/db/xn--c1avg.html
+орг
+
+// xn--c2br7g : VeriSign Sarl
+// https://www.iana.org/domains/root/db/xn--c2br7g.html
+नेट
+
+// xn--cck2b3b : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/xn--cck2b3b.html
+ストア
+
+// xn--cckwcxetd : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/xn--cckwcxetd.html
+アマゾン
+
+// xn--cg4bki : SAMSUNG SDS CO., LTD
+// https://www.iana.org/domains/root/db/xn--cg4bki.html
+삼성
+
+// xn--czr694b : Internet DotTrademark Organisation Limited
+// https://www.iana.org/domains/root/db/xn--czr694b.html
+商标
+
+// xn--czrs0t : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/xn--czrs0t.html
+商店
+
+// xn--czru2d : Zodiac Aquarius Limited
+// https://www.iana.org/domains/root/db/xn--czru2d.html
+商城
+
+// xn--d1acj3b : The Foundation for Network Initiatives “The Smart Internet”
+// https://www.iana.org/domains/root/db/xn--d1acj3b.html
+дети
+
+// xn--eckvdtc9d : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/xn--eckvdtc9d.html
+ポイント
+
+// xn--efvy88h : Guangzhou YU Wei Information Technology Co., Ltd.
+// https://www.iana.org/domains/root/db/xn--efvy88h.html
+新闻
+
+// xn--fct429k : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/xn--fct429k.html
+家電
+
+// xn--fhbei : VeriSign Sarl
+// https://www.iana.org/domains/root/db/xn--fhbei.html
+كوم
+
+// xn--fiq228c5hs : TLD REGISTRY LIMITED OY
+// https://www.iana.org/domains/root/db/xn--fiq228c5hs.html
+中文网
+
+// xn--fiq64b : CITIC Group Corporation
+// https://www.iana.org/domains/root/db/xn--fiq64b.html
+中信
+
+// xn--fjq720a : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/xn--fjq720a.html
+娱乐
+
+// xn--flw351e : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/xn--flw351e.html
+谷歌
+
+// xn--fzys8d69uvgm : PCCW Enterprises Limited
+// https://www.iana.org/domains/root/db/xn--fzys8d69uvgm.html
+電訊盈科
+
+// xn--g2xx48c : Nawang Heli(Xiamen) Network Service Co., LTD.
+// https://www.iana.org/domains/root/db/xn--g2xx48c.html
+购物
+
+// xn--gckr3f0f : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/xn--gckr3f0f.html
+クラウド
+
+// xn--gk3at1e : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/xn--gk3at1e.html
+通販
+
+// xn--hxt814e : Zodiac Taurus Limited
+// https://www.iana.org/domains/root/db/xn--hxt814e.html
+网店
+
+// xn--i1b6b1a6a2e : Public Interest Registry
+// https://www.iana.org/domains/root/db/xn--i1b6b1a6a2e.html
+संगठन
+
+// xn--imr513n : Internet DotTrademark Organisation Limited
+// https://www.iana.org/domains/root/db/xn--imr513n.html
+餐厅
+
+// xn--io0a7i : China Internet Network Information Center (CNNIC)
+// https://www.iana.org/domains/root/db/xn--io0a7i.html
+网络
+
+// xn--j1aef : VeriSign Sarl
+// https://www.iana.org/domains/root/db/xn--j1aef.html
+ком
+
+// xn--jlq480n2rg : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/xn--jlq480n2rg.html
+亚马逊
+
+// xn--jvr189m : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/xn--jvr189m.html
+食品
+
+// xn--kcrx77d1x4a : Koninklijke Philips N.V.
+// https://www.iana.org/domains/root/db/xn--kcrx77d1x4a.html
+飞利浦
+
+// xn--kput3i : Beijing RITT-Net Technology Development Co., Ltd
+// https://www.iana.org/domains/root/db/xn--kput3i.html
+手机
+
+// xn--mgba3a3ejt : Aramco Services Company
+// https://www.iana.org/domains/root/db/xn--mgba3a3ejt.html
+ارامكو
+
+// xn--mgba7c0bbn0a : Competrol (Luxembourg) Sarl
+// https://www.iana.org/domains/root/db/xn--mgba7c0bbn0a.html
+العليان
+
+// xn--mgbab2bd : CORE Association
+// https://www.iana.org/domains/root/db/xn--mgbab2bd.html
+بازار
+
+// xn--mgbca7dzdo : Abu Dhabi Systems and Information Centre
+// https://www.iana.org/domains/root/db/xn--mgbca7dzdo.html
+ابوظبي
+
+// xn--mgbi4ecexp : Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication)
+// https://www.iana.org/domains/root/db/xn--mgbi4ecexp.html
+كاثوليك
+
+// xn--mgbt3dhd : Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
+// https://www.iana.org/domains/root/db/xn--mgbt3dhd.html
+همراه
+
+// xn--mk1bu44c : VeriSign Sarl
+// https://www.iana.org/domains/root/db/xn--mk1bu44c.html
+닷컴
+
+// xn--mxtq1m : Net-Chinese Co., Ltd.
+// https://www.iana.org/domains/root/db/xn--mxtq1m.html
+政府
+
+// xn--ngbc5azd : International Domain Registry Pty. Ltd.
+// https://www.iana.org/domains/root/db/xn--ngbc5azd.html
+شبكة
+
+// xn--ngbe9e0a : Kuwait Finance House
+// https://www.iana.org/domains/root/db/xn--ngbe9e0a.html
+بيتك
+
+// xn--ngbrx : League of Arab States
+// https://www.iana.org/domains/root/db/xn--ngbrx.html
+عرب
+
+// xn--nqv7f : Public Interest Registry
+// https://www.iana.org/domains/root/db/xn--nqv7f.html
+机构
+
+// xn--nqv7fs00ema : Public Interest Registry
+// https://www.iana.org/domains/root/db/xn--nqv7fs00ema.html
+组织机构
+
+// xn--nyqy26a : Stable Tone Limited
+// https://www.iana.org/domains/root/db/xn--nyqy26a.html
+健康
+
+// xn--otu796d : Jiang Yu Liang Cai Technology Company Limited
+// https://www.iana.org/domains/root/db/xn--otu796d.html
+招聘
+
+// xn--p1acf : Rusnames Limited
+// https://www.iana.org/domains/root/db/xn--p1acf.html
+рус
+
+// xn--pssy2u : VeriSign Sarl
+// https://www.iana.org/domains/root/db/xn--pssy2u.html
+大拿
+
+// xn--q9jyb4c : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/xn--q9jyb4c.html
+みんな
+
+// xn--qcka1pmc : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/xn--qcka1pmc.html
+グーグル
+
+// xn--rhqv96g : Stable Tone Limited
+// https://www.iana.org/domains/root/db/xn--rhqv96g.html
+世界
+
+// xn--rovu88b : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/xn--rovu88b.html
+書籍
+
+// xn--ses554g : KNET Co., Ltd.
+// https://www.iana.org/domains/root/db/xn--ses554g.html
+网址
+
+// xn--t60b56a : VeriSign Sarl
+// https://www.iana.org/domains/root/db/xn--t60b56a.html
+닷넷
+
+// xn--tckwe : VeriSign Sarl
+// https://www.iana.org/domains/root/db/xn--tckwe.html
+コム
+
+// xn--tiq49xqyj : Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication)
+// https://www.iana.org/domains/root/db/xn--tiq49xqyj.html
+天主教
+
+// xn--unup4y : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/xn--unup4y.html
+游戏
+
+// xn--vermgensberater-ctb : Deutsche Vermögensberatung Aktiengesellschaft DVAG
+// https://www.iana.org/domains/root/db/xn--vermgensberater-ctb.html
+vermögensberater
+
+// xn--vermgensberatung-pwb : Deutsche Vermögensberatung Aktiengesellschaft DVAG
+// https://www.iana.org/domains/root/db/xn--vermgensberatung-pwb.html
+vermögensberatung
+
+// xn--vhquv : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/xn--vhquv.html
+企业
+
+// xn--vuq861b : Beijing Tele-info Technology Co., Ltd.
+// https://www.iana.org/domains/root/db/xn--vuq861b.html
+信息
+
+// xn--w4r85el8fhu5dnra : Kerry Trading Co. Limited
+// https://www.iana.org/domains/root/db/xn--w4r85el8fhu5dnra.html
+嘉里大酒店
+
+// xn--w4rs40l : Kerry Trading Co. Limited
+// https://www.iana.org/domains/root/db/xn--w4rs40l.html
+嘉里
+
+// xn--xhq521b : Guangzhou YU Wei Information Technology Co., Ltd.
+// https://www.iana.org/domains/root/db/xn--xhq521b.html
+广东
+
+// xn--zfr164b : China Organizational Name Administration Center
+// https://www.iana.org/domains/root/db/xn--zfr164b.html
+政务
+
+// xyz : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/xyz.html
+xyz
+
+// yachts : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/yachts.html
+yachts
+
+// yahoo : Oath Inc.
+// https://www.iana.org/domains/root/db/yahoo.html
+yahoo
+
+// yamaxun : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/yamaxun.html
+yamaxun
+
+// yandex : Yandex Europe B.V.
+// https://www.iana.org/domains/root/db/yandex.html
+yandex
+
+// yodobashi : YODOBASHI CAMERA CO.,LTD.
+// https://www.iana.org/domains/root/db/yodobashi.html
+yodobashi
+
+// yoga : Registry Services, LLC
+// https://www.iana.org/domains/root/db/yoga.html
+yoga
+
+// yokohama : GMO Registry, Inc.
+// https://www.iana.org/domains/root/db/yokohama.html
+yokohama
+
+// you : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/you.html
+you
+
+// youtube : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/youtube.html
+youtube
+
+// yun : Beijing Qihu Keji Co., Ltd.
+// https://www.iana.org/domains/root/db/yun.html
+yun
+
+// zappos : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/zappos.html
+zappos
+
+// zara : Industria de Diseño Textil, S.A. (INDITEX, S.A.)
+// https://www.iana.org/domains/root/db/zara.html
+zara
+
+// zero : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/zero.html
+zero
+
+// zip : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/zip.html
+zip
+
+// zone : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/zone.html
+zone
+
+// zuerich : Kanton Zürich (Canton of Zurich)
+// https://www.iana.org/domains/root/db/zuerich.html
+zuerich
+
+
+// ===END ICANN DOMAINS===
+// ===BEGIN PRIVATE DOMAINS===
+// (Note: these are in alphabetical order by company name)
+
+// 12CHARS: https://12chars.com
+// Submitted by Kenny Niehage <psl@12chars.com>
+12chars.dev
+12chars.it
+12chars.pro
+
+// 1GB LLC : https://www.1gb.ua/
+// Submitted by 1GB LLC <noc@1gb.com.ua>
+cc.ua
+inf.ua
+ltd.ua
+
+// 611coin : https://611project.org/
+611.to
+
+// A2 Hosting
+// Submitted by Tyler Hall <sysadmin@a2hosting.com>
+a2hosted.com
+cpserver.com
+
+// Aaron Marais' Gitlab pages: https://lab.aaronleem.co.za
+// Submitted by Aaron Marais <its_me@aaronleem.co.za>
+graphox.us
+
+// accesso Technology Group, plc. : https://accesso.com/
+// Submitted by accesso Team <accessoecommerce@accesso.com>
+*.devcdnaccesso.com
+
+// Acorn Labs : https://acorn.io
+// Submitted by Craig Jellick <domains@acorn.io>
+*.on-acorn.io
+
+// ActiveTrail: https://www.activetrail.biz/
+// Submitted by Ofer Kalaora <postmaster@activetrail.com>
+activetrail.biz
+
+// Adaptable.io : https://adaptable.io
+// Submitted by Mark Terrel <support@adaptable.io>
+adaptable.app
+
+// Adobe : https://www.adobe.com/
+// Submitted by Ian Boston <boston@adobe.com> and Lars Trieloff <trieloff@adobe.com>
+adobeaemcloud.com
+*.dev.adobeaemcloud.com
+aem.live
+hlx.live
+adobeaemcloud.net
+aem.page
+hlx.page
+hlx3.page
+
+// Adobe Developer Platform : https://developer.adobe.com
+// Submitted by Jesse MacFadyen<jessem@adobe.com>
+adobeio-static.net
+adobeioruntime.net
+
+// Agnat sp. z o.o. : https://domena.pl
+// Submitted by Przemyslaw Plewa <it-admin@domena.pl>
+beep.pl
+
+// Airkit : https://www.airkit.com/
+// Submitted by Grant Cooksey <security@airkit.com>
+airkitapps.com
+airkitapps-au.com
+airkitapps.eu
+
+// Aiven: https://aiven.io/
+// Submitted by Etienne Stalmans <security@aiven.io>
+aivencloud.com
+
+// Akamai : https://www.akamai.com/
+// Submitted by Akamai Team <publicsuffixlist@akamai.com>
+akadns.net
+akamai.net
+akamai-staging.net
+akamaiedge.net
+akamaiedge-staging.net
+akamaihd.net
+akamaihd-staging.net
+akamaiorigin.net
+akamaiorigin-staging.net
+akamaized.net
+akamaized-staging.net
+edgekey.net
+edgekey-staging.net
+edgesuite.net
+edgesuite-staging.net
+
+// alboto.ca : http://alboto.ca
+// Submitted by Anton Avramov <avramov@alboto.ca>
+barsy.ca
+
+// Alces Software Ltd : http://alces-software.com
+// Submitted by Mark J. Titorenko <mark.titorenko@alces-software.com>
+*.compute.estate
+*.alces.network
+
+// all-inkl.com : https://all-inkl.com
+// Submitted by Werner Kaltofen <wk@all-inkl.com>
+kasserver.com
+
+// Altervista: https://www.altervista.org
+// Submitted by Carlo Cannas <tech_staff@altervista.it>
+altervista.org
+
+// alwaysdata : https://www.alwaysdata.com
+// Submitted by Cyril <admin@alwaysdata.com>
+alwaysdata.net
+
+// Amaze Software : https://amaze.co
+// Submitted by Domain Admin <domainadmin@amaze.co>
+myamaze.net
+
+// Amazon : https://www.amazon.com/
+// Submitted by AWS Security <psl-maintainers@amazon.com>
+// Subsections of Amazon/subsidiaries will appear until "concludes" tag
+
+// Amazon API Gateway
+// Submitted by AWS Security <psl-maintainers@amazon.com>
+// Reference: 9e37648f-a66c-4655-9ab1-5981f8737197
+execute-api.cn-north-1.amazonaws.com.cn
+execute-api.cn-northwest-1.amazonaws.com.cn
+execute-api.af-south-1.amazonaws.com
+execute-api.ap-east-1.amazonaws.com
+execute-api.ap-northeast-1.amazonaws.com
+execute-api.ap-northeast-2.amazonaws.com
+execute-api.ap-northeast-3.amazonaws.com
+execute-api.ap-south-1.amazonaws.com
+execute-api.ap-south-2.amazonaws.com
+execute-api.ap-southeast-1.amazonaws.com
+execute-api.ap-southeast-2.amazonaws.com
+execute-api.ap-southeast-3.amazonaws.com
+execute-api.ap-southeast-4.amazonaws.com
+execute-api.ca-central-1.amazonaws.com
+execute-api.ca-west-1.amazonaws.com
+execute-api.eu-central-1.amazonaws.com
+execute-api.eu-central-2.amazonaws.com
+execute-api.eu-north-1.amazonaws.com
+execute-api.eu-south-1.amazonaws.com
+execute-api.eu-south-2.amazonaws.com
+execute-api.eu-west-1.amazonaws.com
+execute-api.eu-west-2.amazonaws.com
+execute-api.eu-west-3.amazonaws.com
+execute-api.il-central-1.amazonaws.com
+execute-api.me-central-1.amazonaws.com
+execute-api.me-south-1.amazonaws.com
+execute-api.sa-east-1.amazonaws.com
+execute-api.us-east-1.amazonaws.com
+execute-api.us-east-2.amazonaws.com
+execute-api.us-gov-east-1.amazonaws.com
+execute-api.us-gov-west-1.amazonaws.com
+execute-api.us-west-1.amazonaws.com
+execute-api.us-west-2.amazonaws.com
+
+// Amazon CloudFront
+// Submitted by Donavan Miller <donavanm@amazon.com>
+// Reference: 54144616-fd49-4435-8535-19c6a601bdb3
+cloudfront.net
+
+// Amazon Cognito
+// Submitted by AWS Security <psl-maintainers@amazon.com>
+// Reference: 7bee1013-f456-47df-bfe8-03c78d946d61
+auth.af-south-1.amazoncognito.com
+auth.ap-northeast-1.amazoncognito.com
+auth.ap-northeast-2.amazoncognito.com
+auth.ap-northeast-3.amazoncognito.com
+auth.ap-south-1.amazoncognito.com
+auth.ap-southeast-1.amazoncognito.com
+auth.ap-southeast-2.amazoncognito.com
+auth.ap-southeast-3.amazoncognito.com
+auth.ca-central-1.amazoncognito.com
+auth.eu-central-1.amazoncognito.com
+auth.eu-north-1.amazoncognito.com
+auth.eu-south-1.amazoncognito.com
+auth.eu-west-1.amazoncognito.com
+auth.eu-west-2.amazoncognito.com
+auth.eu-west-3.amazoncognito.com
+auth.il-central-1.amazoncognito.com
+auth.me-south-1.amazoncognito.com
+auth.sa-east-1.amazoncognito.com
+auth.us-east-1.amazoncognito.com
+auth-fips.us-east-1.amazoncognito.com
+auth.us-east-2.amazoncognito.com
+auth-fips.us-east-2.amazoncognito.com
+auth-fips.us-gov-west-1.amazoncognito.com
+auth.us-west-1.amazoncognito.com
+auth-fips.us-west-1.amazoncognito.com
+auth.us-west-2.amazoncognito.com
+auth-fips.us-west-2.amazoncognito.com
+
+// Amazon EC2
+// Submitted by Luke Wells <psl-maintainers@amazon.com>
+// Reference: 4c38fa71-58ac-4768-99e5-689c1767e537
+*.compute.amazonaws.com
+*.compute-1.amazonaws.com
+*.compute.amazonaws.com.cn
+us-east-1.amazonaws.com
+
+// Amazon EMR
+// Submitted by AWS Security <psl-maintainers@amazon.com>
+// Reference: 597f3f8e-9283-4e48-8e32-7ee25a1ff6ab
+emrappui-prod.cn-north-1.amazonaws.com.cn
+emrnotebooks-prod.cn-north-1.amazonaws.com.cn
+emrstudio-prod.cn-north-1.amazonaws.com.cn
+emrappui-prod.cn-northwest-1.amazonaws.com.cn
+emrnotebooks-prod.cn-northwest-1.amazonaws.com.cn
+emrstudio-prod.cn-northwest-1.amazonaws.com.cn
+emrappui-prod.af-south-1.amazonaws.com
+emrnotebooks-prod.af-south-1.amazonaws.com
+emrstudio-prod.af-south-1.amazonaws.com
+emrappui-prod.ap-east-1.amazonaws.com
+emrnotebooks-prod.ap-east-1.amazonaws.com
+emrstudio-prod.ap-east-1.amazonaws.com
+emrappui-prod.ap-northeast-1.amazonaws.com
+emrnotebooks-prod.ap-northeast-1.amazonaws.com
+emrstudio-prod.ap-northeast-1.amazonaws.com
+emrappui-prod.ap-northeast-2.amazonaws.com
+emrnotebooks-prod.ap-northeast-2.amazonaws.com
+emrstudio-prod.ap-northeast-2.amazonaws.com
+emrappui-prod.ap-northeast-3.amazonaws.com
+emrnotebooks-prod.ap-northeast-3.amazonaws.com
+emrstudio-prod.ap-northeast-3.amazonaws.com
+emrappui-prod.ap-south-1.amazonaws.com
+emrnotebooks-prod.ap-south-1.amazonaws.com
+emrstudio-prod.ap-south-1.amazonaws.com
+emrappui-prod.ap-southeast-1.amazonaws.com
+emrnotebooks-prod.ap-southeast-1.amazonaws.com
+emrstudio-prod.ap-southeast-1.amazonaws.com
+emrappui-prod.ap-southeast-2.amazonaws.com
+emrnotebooks-prod.ap-southeast-2.amazonaws.com
+emrstudio-prod.ap-southeast-2.amazonaws.com
+emrappui-prod.ap-southeast-3.amazonaws.com
+emrnotebooks-prod.ap-southeast-3.amazonaws.com
+emrstudio-prod.ap-southeast-3.amazonaws.com
+emrappui-prod.ca-central-1.amazonaws.com
+emrnotebooks-prod.ca-central-1.amazonaws.com
+emrstudio-prod.ca-central-1.amazonaws.com
+emrappui-prod.eu-central-1.amazonaws.com
+emrnotebooks-prod.eu-central-1.amazonaws.com
+emrstudio-prod.eu-central-1.amazonaws.com
+emrappui-prod.eu-north-1.amazonaws.com
+emrnotebooks-prod.eu-north-1.amazonaws.com
+emrstudio-prod.eu-north-1.amazonaws.com
+emrappui-prod.eu-south-1.amazonaws.com
+emrnotebooks-prod.eu-south-1.amazonaws.com
+emrstudio-prod.eu-south-1.amazonaws.com
+emrappui-prod.eu-west-1.amazonaws.com
+emrnotebooks-prod.eu-west-1.amazonaws.com
+emrstudio-prod.eu-west-1.amazonaws.com
+emrappui-prod.eu-west-2.amazonaws.com
+emrnotebooks-prod.eu-west-2.amazonaws.com
+emrstudio-prod.eu-west-2.amazonaws.com
+emrappui-prod.eu-west-3.amazonaws.com
+emrnotebooks-prod.eu-west-3.amazonaws.com
+emrstudio-prod.eu-west-3.amazonaws.com
+emrappui-prod.me-central-1.amazonaws.com
+emrnotebooks-prod.me-central-1.amazonaws.com
+emrstudio-prod.me-central-1.amazonaws.com
+emrappui-prod.me-south-1.amazonaws.com
+emrnotebooks-prod.me-south-1.amazonaws.com
+emrstudio-prod.me-south-1.amazonaws.com
+emrappui-prod.sa-east-1.amazonaws.com
+emrnotebooks-prod.sa-east-1.amazonaws.com
+emrstudio-prod.sa-east-1.amazonaws.com
+emrappui-prod.us-east-1.amazonaws.com
+emrnotebooks-prod.us-east-1.amazonaws.com
+emrstudio-prod.us-east-1.amazonaws.com
+emrappui-prod.us-east-2.amazonaws.com
+emrnotebooks-prod.us-east-2.amazonaws.com
+emrstudio-prod.us-east-2.amazonaws.com
+emrappui-prod.us-gov-east-1.amazonaws.com
+emrnotebooks-prod.us-gov-east-1.amazonaws.com
+emrstudio-prod.us-gov-east-1.amazonaws.com
+emrappui-prod.us-gov-west-1.amazonaws.com
+emrnotebooks-prod.us-gov-west-1.amazonaws.com
+emrstudio-prod.us-gov-west-1.amazonaws.com
+emrappui-prod.us-west-1.amazonaws.com
+emrnotebooks-prod.us-west-1.amazonaws.com
+emrstudio-prod.us-west-1.amazonaws.com
+emrappui-prod.us-west-2.amazonaws.com
+emrnotebooks-prod.us-west-2.amazonaws.com
+emrstudio-prod.us-west-2.amazonaws.com
+
+// Amazon Managed Workflows for Apache Airflow
+// Submitted by AWS Security <psl-maintainers@amazon.com>
+// Reference: 4ab55e6f-90c0-4a8d-b6a0-52ca5dbb1c2e
+*.cn-north-1.airflow.amazonaws.com.cn
+*.cn-northwest-1.airflow.amazonaws.com.cn
+*.ap-northeast-1.airflow.amazonaws.com
+*.ap-northeast-2.airflow.amazonaws.com
+*.ap-south-1.airflow.amazonaws.com
+*.ap-southeast-1.airflow.amazonaws.com
+*.ap-southeast-2.airflow.amazonaws.com
+*.ca-central-1.airflow.amazonaws.com
+*.eu-central-1.airflow.amazonaws.com
+*.eu-north-1.airflow.amazonaws.com
+*.eu-west-1.airflow.amazonaws.com
+*.eu-west-2.airflow.amazonaws.com
+*.eu-west-3.airflow.amazonaws.com
+*.sa-east-1.airflow.amazonaws.com
+*.us-east-1.airflow.amazonaws.com
+*.us-east-2.airflow.amazonaws.com
+*.us-west-2.airflow.amazonaws.com
+
+// Amazon S3
+// Submitted by AWS Security <psl-maintainers@amazon.com>
+// Reference: cd5c8b3a-67b7-4b40-9236-c87ce81a3d10
+s3.dualstack.cn-north-1.amazonaws.com.cn
+s3-accesspoint.dualstack.cn-north-1.amazonaws.com.cn
+s3-website.dualstack.cn-north-1.amazonaws.com.cn
+s3.cn-north-1.amazonaws.com.cn
+s3-accesspoint.cn-north-1.amazonaws.com.cn
+s3-deprecated.cn-north-1.amazonaws.com.cn
+s3-object-lambda.cn-north-1.amazonaws.com.cn
+s3-website.cn-north-1.amazonaws.com.cn
+s3.dualstack.cn-northwest-1.amazonaws.com.cn
+s3-accesspoint.dualstack.cn-northwest-1.amazonaws.com.cn
+s3.cn-northwest-1.amazonaws.com.cn
+s3-accesspoint.cn-northwest-1.amazonaws.com.cn
+s3-object-lambda.cn-northwest-1.amazonaws.com.cn
+s3-website.cn-northwest-1.amazonaws.com.cn
+s3.dualstack.af-south-1.amazonaws.com
+s3-accesspoint.dualstack.af-south-1.amazonaws.com
+s3-website.dualstack.af-south-1.amazonaws.com
+s3.af-south-1.amazonaws.com
+s3-accesspoint.af-south-1.amazonaws.com
+s3-object-lambda.af-south-1.amazonaws.com
+s3-website.af-south-1.amazonaws.com
+s3.dualstack.ap-east-1.amazonaws.com
+s3-accesspoint.dualstack.ap-east-1.amazonaws.com
+s3.ap-east-1.amazonaws.com
+s3-accesspoint.ap-east-1.amazonaws.com
+s3-object-lambda.ap-east-1.amazonaws.com
+s3-website.ap-east-1.amazonaws.com
+s3.dualstack.ap-northeast-1.amazonaws.com
+s3-accesspoint.dualstack.ap-northeast-1.amazonaws.com
+s3-website.dualstack.ap-northeast-1.amazonaws.com
+s3.ap-northeast-1.amazonaws.com
+s3-accesspoint.ap-northeast-1.amazonaws.com
+s3-object-lambda.ap-northeast-1.amazonaws.com
+s3-website.ap-northeast-1.amazonaws.com
+s3.dualstack.ap-northeast-2.amazonaws.com
+s3-accesspoint.dualstack.ap-northeast-2.amazonaws.com
+s3-website.dualstack.ap-northeast-2.amazonaws.com
+s3.ap-northeast-2.amazonaws.com
+s3-accesspoint.ap-northeast-2.amazonaws.com
+s3-object-lambda.ap-northeast-2.amazonaws.com
+s3-website.ap-northeast-2.amazonaws.com
+s3.dualstack.ap-northeast-3.amazonaws.com
+s3-accesspoint.dualstack.ap-northeast-3.amazonaws.com
+s3-website.dualstack.ap-northeast-3.amazonaws.com
+s3.ap-northeast-3.amazonaws.com
+s3-accesspoint.ap-northeast-3.amazonaws.com
+s3-object-lambda.ap-northeast-3.amazonaws.com
+s3-website.ap-northeast-3.amazonaws.com
+s3.dualstack.ap-south-1.amazonaws.com
+s3-accesspoint.dualstack.ap-south-1.amazonaws.com
+s3-website.dualstack.ap-south-1.amazonaws.com
+s3.ap-south-1.amazonaws.com
+s3-accesspoint.ap-south-1.amazonaws.com
+s3-object-lambda.ap-south-1.amazonaws.com
+s3-website.ap-south-1.amazonaws.com
+s3.dualstack.ap-south-2.amazonaws.com
+s3-accesspoint.dualstack.ap-south-2.amazonaws.com
+s3.ap-south-2.amazonaws.com
+s3-accesspoint.ap-south-2.amazonaws.com
+s3-object-lambda.ap-south-2.amazonaws.com
+s3-website.ap-south-2.amazonaws.com
+s3.dualstack.ap-southeast-1.amazonaws.com
+s3-accesspoint.dualstack.ap-southeast-1.amazonaws.com
+s3-website.dualstack.ap-southeast-1.amazonaws.com
+s3.ap-southeast-1.amazonaws.com
+s3-accesspoint.ap-southeast-1.amazonaws.com
+s3-object-lambda.ap-southeast-1.amazonaws.com
+s3-website.ap-southeast-1.amazonaws.com
+s3.dualstack.ap-southeast-2.amazonaws.com
+s3-accesspoint.dualstack.ap-southeast-2.amazonaws.com
+s3-website.dualstack.ap-southeast-2.amazonaws.com
+s3.ap-southeast-2.amazonaws.com
+s3-accesspoint.ap-southeast-2.amazonaws.com
+s3-object-lambda.ap-southeast-2.amazonaws.com
+s3-website.ap-southeast-2.amazonaws.com
+s3.dualstack.ap-southeast-3.amazonaws.com
+s3-accesspoint.dualstack.ap-southeast-3.amazonaws.com
+s3.ap-southeast-3.amazonaws.com
+s3-accesspoint.ap-southeast-3.amazonaws.com
+s3-object-lambda.ap-southeast-3.amazonaws.com
+s3-website.ap-southeast-3.amazonaws.com
+s3.dualstack.ap-southeast-4.amazonaws.com
+s3-accesspoint.dualstack.ap-southeast-4.amazonaws.com
+s3.ap-southeast-4.amazonaws.com
+s3-accesspoint.ap-southeast-4.amazonaws.com
+s3-object-lambda.ap-southeast-4.amazonaws.com
+s3-website.ap-southeast-4.amazonaws.com
+s3.dualstack.ca-central-1.amazonaws.com
+s3-accesspoint.dualstack.ca-central-1.amazonaws.com
+s3-accesspoint-fips.dualstack.ca-central-1.amazonaws.com
+s3-fips.dualstack.ca-central-1.amazonaws.com
+s3-website.dualstack.ca-central-1.amazonaws.com
+s3.ca-central-1.amazonaws.com
+s3-accesspoint.ca-central-1.amazonaws.com
+s3-accesspoint-fips.ca-central-1.amazonaws.com
+s3-fips.ca-central-1.amazonaws.com
+s3-object-lambda.ca-central-1.amazonaws.com
+s3-website.ca-central-1.amazonaws.com
+s3.dualstack.ca-west-1.amazonaws.com
+s3-accesspoint.dualstack.ca-west-1.amazonaws.com
+s3-accesspoint-fips.dualstack.ca-west-1.amazonaws.com
+s3-fips.dualstack.ca-west-1.amazonaws.com
+s3-website.dualstack.ca-west-1.amazonaws.com
+s3.ca-west-1.amazonaws.com
+s3-accesspoint.ca-west-1.amazonaws.com
+s3-accesspoint-fips.ca-west-1.amazonaws.com
+s3-fips.ca-west-1.amazonaws.com
+s3-website.ca-west-1.amazonaws.com
+s3.dualstack.eu-central-1.amazonaws.com
+s3-accesspoint.dualstack.eu-central-1.amazonaws.com
+s3-website.dualstack.eu-central-1.amazonaws.com
+s3.eu-central-1.amazonaws.com
+s3-accesspoint.eu-central-1.amazonaws.com
+s3-object-lambda.eu-central-1.amazonaws.com
+s3-website.eu-central-1.amazonaws.com
+s3.dualstack.eu-central-2.amazonaws.com
+s3-accesspoint.dualstack.eu-central-2.amazonaws.com
+s3.eu-central-2.amazonaws.com
+s3-accesspoint.eu-central-2.amazonaws.com
+s3-object-lambda.eu-central-2.amazonaws.com
+s3-website.eu-central-2.amazonaws.com
+s3.dualstack.eu-north-1.amazonaws.com
+s3-accesspoint.dualstack.eu-north-1.amazonaws.com
+s3.eu-north-1.amazonaws.com
+s3-accesspoint.eu-north-1.amazonaws.com
+s3-object-lambda.eu-north-1.amazonaws.com
+s3-website.eu-north-1.amazonaws.com
+s3.dualstack.eu-south-1.amazonaws.com
+s3-accesspoint.dualstack.eu-south-1.amazonaws.com
+s3-website.dualstack.eu-south-1.amazonaws.com
+s3.eu-south-1.amazonaws.com
+s3-accesspoint.eu-south-1.amazonaws.com
+s3-object-lambda.eu-south-1.amazonaws.com
+s3-website.eu-south-1.amazonaws.com
+s3.dualstack.eu-south-2.amazonaws.com
+s3-accesspoint.dualstack.eu-south-2.amazonaws.com
+s3.eu-south-2.amazonaws.com
+s3-accesspoint.eu-south-2.amazonaws.com
+s3-object-lambda.eu-south-2.amazonaws.com
+s3-website.eu-south-2.amazonaws.com
+s3.dualstack.eu-west-1.amazonaws.com
+s3-accesspoint.dualstack.eu-west-1.amazonaws.com
+s3-website.dualstack.eu-west-1.amazonaws.com
+s3.eu-west-1.amazonaws.com
+s3-accesspoint.eu-west-1.amazonaws.com
+s3-deprecated.eu-west-1.amazonaws.com
+s3-object-lambda.eu-west-1.amazonaws.com
+s3-website.eu-west-1.amazonaws.com
+s3.dualstack.eu-west-2.amazonaws.com
+s3-accesspoint.dualstack.eu-west-2.amazonaws.com
+s3.eu-west-2.amazonaws.com
+s3-accesspoint.eu-west-2.amazonaws.com
+s3-object-lambda.eu-west-2.amazonaws.com
+s3-website.eu-west-2.amazonaws.com
+s3.dualstack.eu-west-3.amazonaws.com
+s3-accesspoint.dualstack.eu-west-3.amazonaws.com
+s3-website.dualstack.eu-west-3.amazonaws.com
+s3.eu-west-3.amazonaws.com
+s3-accesspoint.eu-west-3.amazonaws.com
+s3-object-lambda.eu-west-3.amazonaws.com
+s3-website.eu-west-3.amazonaws.com
+s3.dualstack.il-central-1.amazonaws.com
+s3-accesspoint.dualstack.il-central-1.amazonaws.com
+s3.il-central-1.amazonaws.com
+s3-accesspoint.il-central-1.amazonaws.com
+s3-object-lambda.il-central-1.amazonaws.com
+s3-website.il-central-1.amazonaws.com
+s3.dualstack.me-central-1.amazonaws.com
+s3-accesspoint.dualstack.me-central-1.amazonaws.com
+s3.me-central-1.amazonaws.com
+s3-accesspoint.me-central-1.amazonaws.com
+s3-object-lambda.me-central-1.amazonaws.com
+s3-website.me-central-1.amazonaws.com
+s3.dualstack.me-south-1.amazonaws.com
+s3-accesspoint.dualstack.me-south-1.amazonaws.com
+s3.me-south-1.amazonaws.com
+s3-accesspoint.me-south-1.amazonaws.com
+s3-object-lambda.me-south-1.amazonaws.com
+s3-website.me-south-1.amazonaws.com
+s3.amazonaws.com
+s3-1.amazonaws.com
+s3-ap-east-1.amazonaws.com
+s3-ap-northeast-1.amazonaws.com
+s3-ap-northeast-2.amazonaws.com
+s3-ap-northeast-3.amazonaws.com
+s3-ap-south-1.amazonaws.com
+s3-ap-southeast-1.amazonaws.com
+s3-ap-southeast-2.amazonaws.com
+s3-ca-central-1.amazonaws.com
+s3-eu-central-1.amazonaws.com
+s3-eu-north-1.amazonaws.com
+s3-eu-west-1.amazonaws.com
+s3-eu-west-2.amazonaws.com
+s3-eu-west-3.amazonaws.com
+s3-external-1.amazonaws.com
+s3-fips-us-gov-east-1.amazonaws.com
+s3-fips-us-gov-west-1.amazonaws.com
+mrap.accesspoint.s3-global.amazonaws.com
+s3-me-south-1.amazonaws.com
+s3-sa-east-1.amazonaws.com
+s3-us-east-2.amazonaws.com
+s3-us-gov-east-1.amazonaws.com
+s3-us-gov-west-1.amazonaws.com
+s3-us-west-1.amazonaws.com
+s3-us-west-2.amazonaws.com
+s3-website-ap-northeast-1.amazonaws.com
+s3-website-ap-southeast-1.amazonaws.com
+s3-website-ap-southeast-2.amazonaws.com
+s3-website-eu-west-1.amazonaws.com
+s3-website-sa-east-1.amazonaws.com
+s3-website-us-east-1.amazonaws.com
+s3-website-us-gov-west-1.amazonaws.com
+s3-website-us-west-1.amazonaws.com
+s3-website-us-west-2.amazonaws.com
+s3.dualstack.sa-east-1.amazonaws.com
+s3-accesspoint.dualstack.sa-east-1.amazonaws.com
+s3-website.dualstack.sa-east-1.amazonaws.com
+s3.sa-east-1.amazonaws.com
+s3-accesspoint.sa-east-1.amazonaws.com
+s3-object-lambda.sa-east-1.amazonaws.com
+s3-website.sa-east-1.amazonaws.com
+s3.dualstack.us-east-1.amazonaws.com
+s3-accesspoint.dualstack.us-east-1.amazonaws.com
+s3-accesspoint-fips.dualstack.us-east-1.amazonaws.com
+s3-fips.dualstack.us-east-1.amazonaws.com
+s3-website.dualstack.us-east-1.amazonaws.com
+s3.us-east-1.amazonaws.com
+s3-accesspoint.us-east-1.amazonaws.com
+s3-accesspoint-fips.us-east-1.amazonaws.com
+s3-deprecated.us-east-1.amazonaws.com
+s3-fips.us-east-1.amazonaws.com
+s3-object-lambda.us-east-1.amazonaws.com
+s3-website.us-east-1.amazonaws.com
+s3.dualstack.us-east-2.amazonaws.com
+s3-accesspoint.dualstack.us-east-2.amazonaws.com
+s3-accesspoint-fips.dualstack.us-east-2.amazonaws.com
+s3-fips.dualstack.us-east-2.amazonaws.com
+s3.us-east-2.amazonaws.com
+s3-accesspoint.us-east-2.amazonaws.com
+s3-accesspoint-fips.us-east-2.amazonaws.com
+s3-deprecated.us-east-2.amazonaws.com
+s3-fips.us-east-2.amazonaws.com
+s3-object-lambda.us-east-2.amazonaws.com
+s3-website.us-east-2.amazonaws.com
+s3.dualstack.us-gov-east-1.amazonaws.com
+s3-accesspoint.dualstack.us-gov-east-1.amazonaws.com
+s3-accesspoint-fips.dualstack.us-gov-east-1.amazonaws.com
+s3-fips.dualstack.us-gov-east-1.amazonaws.com
+s3.us-gov-east-1.amazonaws.com
+s3-accesspoint.us-gov-east-1.amazonaws.com
+s3-accesspoint-fips.us-gov-east-1.amazonaws.com
+s3-fips.us-gov-east-1.amazonaws.com
+s3-object-lambda.us-gov-east-1.amazonaws.com
+s3-website.us-gov-east-1.amazonaws.com
+s3.dualstack.us-gov-west-1.amazonaws.com
+s3-accesspoint.dualstack.us-gov-west-1.amazonaws.com
+s3-accesspoint-fips.dualstack.us-gov-west-1.amazonaws.com
+s3-fips.dualstack.us-gov-west-1.amazonaws.com
+s3.us-gov-west-1.amazonaws.com
+s3-accesspoint.us-gov-west-1.amazonaws.com
+s3-accesspoint-fips.us-gov-west-1.amazonaws.com
+s3-fips.us-gov-west-1.amazonaws.com
+s3-object-lambda.us-gov-west-1.amazonaws.com
+s3-website.us-gov-west-1.amazonaws.com
+s3.dualstack.us-west-1.amazonaws.com
+s3-accesspoint.dualstack.us-west-1.amazonaws.com
+s3-accesspoint-fips.dualstack.us-west-1.amazonaws.com
+s3-fips.dualstack.us-west-1.amazonaws.com
+s3-website.dualstack.us-west-1.amazonaws.com
+s3.us-west-1.amazonaws.com
+s3-accesspoint.us-west-1.amazonaws.com
+s3-accesspoint-fips.us-west-1.amazonaws.com
+s3-fips.us-west-1.amazonaws.com
+s3-object-lambda.us-west-1.amazonaws.com
+s3-website.us-west-1.amazonaws.com
+s3.dualstack.us-west-2.amazonaws.com
+s3-accesspoint.dualstack.us-west-2.amazonaws.com
+s3-accesspoint-fips.dualstack.us-west-2.amazonaws.com
+s3-fips.dualstack.us-west-2.amazonaws.com
+s3-website.dualstack.us-west-2.amazonaws.com
+s3.us-west-2.amazonaws.com
+s3-accesspoint.us-west-2.amazonaws.com
+s3-accesspoint-fips.us-west-2.amazonaws.com
+s3-deprecated.us-west-2.amazonaws.com
+s3-fips.us-west-2.amazonaws.com
+s3-object-lambda.us-west-2.amazonaws.com
+s3-website.us-west-2.amazonaws.com
+
+// Amazon SageMaker Notebook Instances
+// Submitted by AWS Security <psl-maintainers@amazon.com>
+// Reference: ce8ae0b1-0070-496d-be88-37c31837af9d
+notebook.af-south-1.sagemaker.aws
+notebook.ap-east-1.sagemaker.aws
+notebook.ap-northeast-1.sagemaker.aws
+notebook.ap-northeast-2.sagemaker.aws
+notebook.ap-northeast-3.sagemaker.aws
+notebook.ap-south-1.sagemaker.aws
+notebook.ap-south-2.sagemaker.aws
+notebook.ap-southeast-1.sagemaker.aws
+notebook.ap-southeast-2.sagemaker.aws
+notebook.ap-southeast-3.sagemaker.aws
+notebook.ap-southeast-4.sagemaker.aws
+notebook.ca-central-1.sagemaker.aws
+notebook-fips.ca-central-1.sagemaker.aws
+notebook.ca-west-1.sagemaker.aws
+notebook-fips.ca-west-1.sagemaker.aws
+notebook.eu-central-1.sagemaker.aws
+notebook.eu-central-2.sagemaker.aws
+notebook.eu-north-1.sagemaker.aws
+notebook.eu-south-1.sagemaker.aws
+notebook.eu-south-2.sagemaker.aws
+notebook.eu-west-1.sagemaker.aws
+notebook.eu-west-2.sagemaker.aws
+notebook.eu-west-3.sagemaker.aws
+notebook.il-central-1.sagemaker.aws
+notebook.me-central-1.sagemaker.aws
+notebook.me-south-1.sagemaker.aws
+notebook.sa-east-1.sagemaker.aws
+notebook.us-east-1.sagemaker.aws
+notebook-fips.us-east-1.sagemaker.aws
+notebook.us-east-2.sagemaker.aws
+notebook-fips.us-east-2.sagemaker.aws
+notebook.us-gov-east-1.sagemaker.aws
+notebook-fips.us-gov-east-1.sagemaker.aws
+notebook.us-gov-west-1.sagemaker.aws
+notebook-fips.us-gov-west-1.sagemaker.aws
+notebook.us-west-1.sagemaker.aws
+notebook.us-west-2.sagemaker.aws
+notebook-fips.us-west-2.sagemaker.aws
+notebook.cn-north-1.sagemaker.com.cn
+notebook.cn-northwest-1.sagemaker.com.cn
+
+// Amazon SageMaker Studio
+// Submitted by AWS Security <psl-maintainers@amazon.com>
+// Reference: 057ee397-6bf8-4f20-b807-d7bc145ac980
+studio.af-south-1.sagemaker.aws
+studio.ap-east-1.sagemaker.aws
+studio.ap-northeast-1.sagemaker.aws
+studio.ap-northeast-2.sagemaker.aws
+studio.ap-northeast-3.sagemaker.aws
+studio.ap-south-1.sagemaker.aws
+studio.ap-southeast-1.sagemaker.aws
+studio.ap-southeast-2.sagemaker.aws
+studio.ap-southeast-3.sagemaker.aws
+studio.ca-central-1.sagemaker.aws
+studio.eu-central-1.sagemaker.aws
+studio.eu-north-1.sagemaker.aws
+studio.eu-south-1.sagemaker.aws
+studio.eu-west-1.sagemaker.aws
+studio.eu-west-2.sagemaker.aws
+studio.eu-west-3.sagemaker.aws
+studio.il-central-1.sagemaker.aws
+studio.me-central-1.sagemaker.aws
+studio.me-south-1.sagemaker.aws
+studio.sa-east-1.sagemaker.aws
+studio.us-east-1.sagemaker.aws
+studio.us-east-2.sagemaker.aws
+studio.us-gov-east-1.sagemaker.aws
+studio-fips.us-gov-east-1.sagemaker.aws
+studio.us-gov-west-1.sagemaker.aws
+studio-fips.us-gov-west-1.sagemaker.aws
+studio.us-west-1.sagemaker.aws
+studio.us-west-2.sagemaker.aws
+studio.cn-north-1.sagemaker.com.cn
+studio.cn-northwest-1.sagemaker.com.cn
+
+// Analytics on AWS
+// Submitted by AWS Security <psl-maintainers@amazon.com>
+// Reference: 955f9f40-a495-4e73-ae85-67b77ac9cadd
+analytics-gateway.ap-northeast-1.amazonaws.com
+analytics-gateway.ap-northeast-2.amazonaws.com
+analytics-gateway.ap-south-1.amazonaws.com
+analytics-gateway.ap-southeast-1.amazonaws.com
+analytics-gateway.ap-southeast-2.amazonaws.com
+analytics-gateway.eu-central-1.amazonaws.com
+analytics-gateway.eu-west-1.amazonaws.com
+analytics-gateway.us-east-1.amazonaws.com
+analytics-gateway.us-east-2.amazonaws.com
+analytics-gateway.us-west-2.amazonaws.com
+
+// AWS Amplify
+// Submitted by AWS Security <psl-maintainers@amazon.com>
+// Reference: 5ecce854-c033-4fc4-a755-1a9916d9a9bb
+*.amplifyapp.com
+
+// AWS App Runner
+// Submitted by AWS Security <psl-maintainers@amazon.com>
+// Reference: 6828c008-ba5d-442f-ade5-48da4e7c2316
+*.awsapprunner.com
+
+// AWS Cloud9
+// Submitted by: AWS Security <psl-maintainers@amazon.com>
+// Reference: 30717f72-4007-4f0f-8ed4-864c6f2efec9
+webview-assets.aws-cloud9.af-south-1.amazonaws.com
+vfs.cloud9.af-south-1.amazonaws.com
+webview-assets.cloud9.af-south-1.amazonaws.com
+webview-assets.aws-cloud9.ap-east-1.amazonaws.com
+vfs.cloud9.ap-east-1.amazonaws.com
+webview-assets.cloud9.ap-east-1.amazonaws.com
+webview-assets.aws-cloud9.ap-northeast-1.amazonaws.com
+vfs.cloud9.ap-northeast-1.amazonaws.com
+webview-assets.cloud9.ap-northeast-1.amazonaws.com
+webview-assets.aws-cloud9.ap-northeast-2.amazonaws.com
+vfs.cloud9.ap-northeast-2.amazonaws.com
+webview-assets.cloud9.ap-northeast-2.amazonaws.com
+webview-assets.aws-cloud9.ap-northeast-3.amazonaws.com
+vfs.cloud9.ap-northeast-3.amazonaws.com
+webview-assets.cloud9.ap-northeast-3.amazonaws.com
+webview-assets.aws-cloud9.ap-south-1.amazonaws.com
+vfs.cloud9.ap-south-1.amazonaws.com
+webview-assets.cloud9.ap-south-1.amazonaws.com
+webview-assets.aws-cloud9.ap-southeast-1.amazonaws.com
+vfs.cloud9.ap-southeast-1.amazonaws.com
+webview-assets.cloud9.ap-southeast-1.amazonaws.com
+webview-assets.aws-cloud9.ap-southeast-2.amazonaws.com
+vfs.cloud9.ap-southeast-2.amazonaws.com
+webview-assets.cloud9.ap-southeast-2.amazonaws.com
+webview-assets.aws-cloud9.ca-central-1.amazonaws.com
+vfs.cloud9.ca-central-1.amazonaws.com
+webview-assets.cloud9.ca-central-1.amazonaws.com
+webview-assets.aws-cloud9.eu-central-1.amazonaws.com
+vfs.cloud9.eu-central-1.amazonaws.com
+webview-assets.cloud9.eu-central-1.amazonaws.com
+webview-assets.aws-cloud9.eu-north-1.amazonaws.com
+vfs.cloud9.eu-north-1.amazonaws.com
+webview-assets.cloud9.eu-north-1.amazonaws.com
+webview-assets.aws-cloud9.eu-south-1.amazonaws.com
+vfs.cloud9.eu-south-1.amazonaws.com
+webview-assets.cloud9.eu-south-1.amazonaws.com
+webview-assets.aws-cloud9.eu-west-1.amazonaws.com
+vfs.cloud9.eu-west-1.amazonaws.com
+webview-assets.cloud9.eu-west-1.amazonaws.com
+webview-assets.aws-cloud9.eu-west-2.amazonaws.com
+vfs.cloud9.eu-west-2.amazonaws.com
+webview-assets.cloud9.eu-west-2.amazonaws.com
+webview-assets.aws-cloud9.eu-west-3.amazonaws.com
+vfs.cloud9.eu-west-3.amazonaws.com
+webview-assets.cloud9.eu-west-3.amazonaws.com
+webview-assets.aws-cloud9.il-central-1.amazonaws.com
+vfs.cloud9.il-central-1.amazonaws.com
+webview-assets.aws-cloud9.me-south-1.amazonaws.com
+vfs.cloud9.me-south-1.amazonaws.com
+webview-assets.cloud9.me-south-1.amazonaws.com
+webview-assets.aws-cloud9.sa-east-1.amazonaws.com
+vfs.cloud9.sa-east-1.amazonaws.com
+webview-assets.cloud9.sa-east-1.amazonaws.com
+webview-assets.aws-cloud9.us-east-1.amazonaws.com
+vfs.cloud9.us-east-1.amazonaws.com
+webview-assets.cloud9.us-east-1.amazonaws.com
+webview-assets.aws-cloud9.us-east-2.amazonaws.com
+vfs.cloud9.us-east-2.amazonaws.com
+webview-assets.cloud9.us-east-2.amazonaws.com
+webview-assets.aws-cloud9.us-west-1.amazonaws.com
+vfs.cloud9.us-west-1.amazonaws.com
+webview-assets.cloud9.us-west-1.amazonaws.com
+webview-assets.aws-cloud9.us-west-2.amazonaws.com
+vfs.cloud9.us-west-2.amazonaws.com
+webview-assets.cloud9.us-west-2.amazonaws.com
+
+// AWS Elastic Beanstalk
+// Submitted by AWS Security <psl-maintainers@amazon.com>
+// Reference: bb5a965c-dec3-4967-aa22-e306ad064797
+cn-north-1.eb.amazonaws.com.cn
+cn-northwest-1.eb.amazonaws.com.cn
+elasticbeanstalk.com
+af-south-1.elasticbeanstalk.com
+ap-east-1.elasticbeanstalk.com
+ap-northeast-1.elasticbeanstalk.com
+ap-northeast-2.elasticbeanstalk.com
+ap-northeast-3.elasticbeanstalk.com
+ap-south-1.elasticbeanstalk.com
+ap-southeast-1.elasticbeanstalk.com
+ap-southeast-2.elasticbeanstalk.com
+ap-southeast-3.elasticbeanstalk.com
+ca-central-1.elasticbeanstalk.com
+eu-central-1.elasticbeanstalk.com
+eu-north-1.elasticbeanstalk.com
+eu-south-1.elasticbeanstalk.com
+eu-west-1.elasticbeanstalk.com
+eu-west-2.elasticbeanstalk.com
+eu-west-3.elasticbeanstalk.com
+il-central-1.elasticbeanstalk.com
+me-south-1.elasticbeanstalk.com
+sa-east-1.elasticbeanstalk.com
+us-east-1.elasticbeanstalk.com
+us-east-2.elasticbeanstalk.com
+us-gov-east-1.elasticbeanstalk.com
+us-gov-west-1.elasticbeanstalk.com
+us-west-1.elasticbeanstalk.com
+us-west-2.elasticbeanstalk.com
+
+// (AWS) Elastic Load Balancing
+// Submitted by Luke Wells <psl-maintainers@amazon.com>
+// Reference: 12a3d528-1bac-4433-a359-a395867ffed2
+*.elb.amazonaws.com.cn
+*.elb.amazonaws.com
+
+// AWS Global Accelerator
+// Submitted by Daniel Massaguer <psl-maintainers@amazon.com>
+// Reference: d916759d-a08b-4241-b536-4db887383a6a
+awsglobalaccelerator.com
+
+// AWS re:Post Private
+// Submitted by AWS Security <psl-maintainers@amazon.com>
+// Reference: 83385945-225f-416e-9aa0-ad0632bfdcee
+*.private.repost.aws
+
+// eero
+// Submitted by Yue Kang <eero-dynamic-dns@amazon.com>
+// Reference: 264afe70-f62c-4c02-8ab9-b5281ed24461
+eero.online
+eero-stage.online
+
+// concludes Amazon
+
+// Amune : https://amune.org/
+// Submitted by Team Amune <cert@amune.org>
+t3l3p0rt.net
+tele.amune.org
+
+// Apigee : https://apigee.com/
+// Submitted by Apigee Security Team <security@apigee.com>
+apigee.io
+
+// Apis Networks: https://apisnetworks.com
+// Submitted by Matt Saladna <matt@apisnetworks.com>
+panel.dev
+
+// Apphud : https://apphud.com
+// Submitted by Alexander Selivanov <alex@apphud.com>
+siiites.com
+
+// Appspace : https://www.appspace.com
+// Submitted by Appspace Security Team <security@appspace.com>
+appspacehosted.com
+appspaceusercontent.com
+
+// Appudo UG (haftungsbeschränkt) : https://www.appudo.com
+// Submitted by Alexander Hochbaum <admin@appudo.com>
+appudo.net
+
+// Aptible : https://www.aptible.com/
+// Submitted by Thomas Orozco <thomas@aptible.com>
+on-aptible.com
+
+// Aquapal : https://aquapal.net/
+// Submitted by Aki Ueno <admin@aquapal.net>
+f5.si
+
+// ASEINet : https://www.aseinet.com/
+// Submitted by Asei SEKIGUCHI <mail@aseinet.com>
+user.aseinet.ne.jp
+gv.vc
+d.gv.vc
+
+// Asociación Amigos de la Informática "Euskalamiga" : http://encounter.eus/
+// Submitted by Hector Martin <marcan@euskalencounter.org>
+user.party.eus
+
+// Association potager.org : https://potager.org/
+// Submitted by Lunar <jardiniers@potager.org>
+pimienta.org
+poivron.org
+potager.org
+sweetpepper.org
+
+// ASUSTOR Inc. : http://www.asustor.com
+// Submitted by Vincent Tseng <vincenttseng@asustor.com>
+myasustor.com
+
+// Atlassian : https://atlassian.com
+// Submitted by Sam Smyth <devloop@atlassian.com>
+cdn.prod.atlassian-dev.net
+
+// Authentick UG (haftungsbeschränkt) : https://authentick.net
+// Submitted by Lukas Reschke <lukas@authentick.net>
+translated.page
+
+// Autocode : https://autocode.com
+// Submitted by Jacob Lee <jacob@autocode.com>
+autocode.dev
+
+// AVM : https://avm.de
+// Submitted by Andreas Weise <a.weise@avm.de>
+myfritz.net
+
+// AVStack Pte. Ltd. : https://avstack.io
+// Submitted by Jasper Hugo <jasper@avstack.io>
+onavstack.net
+
+// AW AdvisorWebsites.com Software Inc : https://advisorwebsites.com
+// Submitted by James Kennedy <domains@advisorwebsites.com>
+*.awdev.ca
+*.advisor.ws
+
+// AZ.pl sp. z.o.o: https://az.pl
+// Submitted by Krzysztof Wolski <krzysztof.wolski@home.eu>
+ecommerce-shop.pl
+
+// b-data GmbH : https://www.b-data.io
+// Submitted by Olivier Benz <olivier.benz@b-data.ch>
+b-data.io
+
+// backplane : https://www.backplane.io
+// Submitted by Anthony Voutas <anthony@backplane.io>
+backplaneapp.io
+
+// Balena : https://www.balena.io
+// Submitted by Petros Angelatos <petrosagg@balena.io>
+balena-devices.com
+
+// University of Banja Luka : https://unibl.org
+// Domains for Republic of Srpska administrative entity.
+// Submitted by Marko Ivanovic <kormang@hotmail.rs>
+rs.ba
+
+// Banzai Cloud
+// Submitted by Janos Matyas <info@banzaicloud.com>
+*.banzai.cloud
+app.banzaicloud.io
+*.backyards.banzaicloud.io
+
+// BASE, Inc. : https://binc.jp
+// Submitted by Yuya NAGASAWA <public-suffix-list@binc.jp>
+base.ec
+official.ec
+buyshop.jp
+fashionstore.jp
+handcrafted.jp
+kawaiishop.jp
+supersale.jp
+theshop.jp
+shopselect.net
+base.shop
+
+// BeagleBoard.org Foundation : https://beagleboard.org
+// Submitted by Jason Kridner <jkridner@beagleboard.org>
+beagleboard.io
+
+// Beget Ltd
+// Submitted by Lev Nekrasov <lnekrasov@beget.com>
+*.beget.app
+
+// Besties : https://besties.house
+// Submitted by Hazel Cora <hazy@besties.house>
+pages.gay
+
+// BetaInABox
+// Submitted by Adrian <adrian@betainabox.com>
+betainabox.com
+
+// BinaryLane : http://www.binarylane.com
+// Submitted by Nathan O'Sullivan <nathan@mammoth.com.au>
+bnr.la
+
+// Bitbucket : http://bitbucket.org
+// Submitted by Andy Ortlieb <aortlieb@atlassian.com>
+bitbucket.io
+
+// Blackbaud, Inc. : https://www.blackbaud.com
+// Submitted by Paul Crowder <paul.crowder@blackbaud.com>
+blackbaudcdn.net
+
+// Blatech : http://www.blatech.net
+// Submitted by Luke Bratch <luke@bratch.co.uk>
+of.je
+
+// Blue Bite, LLC : https://bluebite.com
+// Submitted by Joshua Weiss <admin.engineering@bluebite.com>
+bluebite.io
+
+// Boomla : https://boomla.com
+// Submitted by Tibor Halter <thalter@boomla.com>
+boomla.net
+
+// Boutir : https://www.boutir.com
+// Submitted by Eric Ng Ka Ka <ngkaka@boutir.com>
+boutir.com
+
+// Boxfuse : https://boxfuse.com
+// Submitted by Axel Fontaine <axel@boxfuse.com>
+boxfuse.io
+
+// bplaced : https://www.bplaced.net/
+// Submitted by Miroslav Bozic <security@bplaced.net>
+square7.ch
+bplaced.com
+bplaced.de
+square7.de
+bplaced.net
+square7.net
+
+// Brave : https://brave.com
+// Submitted by Andrea Brancaleoni <abrancaleoni@brave.com>
+*.s.brave.io
+
+// Brendly : https://brendly.rs
+// Submitted by Dusan Radovanovic <dusan.radovanovic@brendly.rs>
+shop.brendly.rs
+
+// BrowserSafetyMark
+// Submitted by Dave Tharp <browsersafetymark.io@quicinc.com>
+browsersafetymark.io
+
+// Bytemark Hosting : https://www.bytemark.co.uk
+// Submitted by Paul Cammish <paul.cammish@bytemark.co.uk>
+uk0.bigv.io
+dh.bytemark.co.uk
+vm.bytemark.co.uk
+
+// Caf.js Labs LLC : https://www.cafjs.com
+// Submitted by Antonio Lain <antlai@cafjs.com>
+cafjs.com
+
+// callidomus : https://www.callidomus.com/
+// Submitted by Marcus Popp <admin@callidomus.com>
+mycd.eu
+
+// Canva Pty Ltd : https://canva.com/
+// Submitted by Joel Aquilina <publicsuffixlist@canva.com>
+canva-apps.cn
+*.my.canvasite.cn
+canva-apps.com
+*.my.canva.site
+
+// Carrd : https://carrd.co
+// Submitted by AJ <aj@carrd.co>
+drr.ac
+uwu.ai
+carrd.co
+crd.co
+ju.mp
+
+// CentralNic : http://www.centralnic.com/names/domains
+// Submitted by registry <gavin.brown@centralnic.com>
+ae.org
+br.com
+cn.com
+com.de
+com.se
+de.com
+eu.com
+gb.net
+hu.net
+jp.net
+jpn.com
+mex.com
+ru.com
+sa.com
+se.net
+uk.com
+uk.net
+us.com
+za.bz
+za.com
+
+// No longer operated by CentralNic, these entries should be adopted and/or removed by current operators
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+ar.com
+hu.com
+kr.com
+no.com
+qc.com
+uy.com
+
+// Africa.com Web Solutions Ltd : https://registry.africa.com
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+africa.com
+
+// iDOT Services Limited : http://www.domain.gr.com
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+gr.com
+
+// Radix FZC : http://domains.in.net
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+in.net
+web.in
+
+// US REGISTRY LLC : http://us.org
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+us.org
+
+// co.com Registry, LLC : https://registry.co.com
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+co.com
+
+// Roar Domains LLC : https://roar.basketball/
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+aus.basketball
+nz.basketball
+
+// BRS Media : https://brsmedia.com/
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+radio.am
+radio.fm
+
+// c.la : http://www.c.la/
+c.la
+
+// certmgr.org : https://certmgr.org
+// Submitted by B. Blechschmidt <hostmaster@certmgr.org>
+certmgr.org
+
+// Cityhost LLC : https://cityhost.ua
+// Submitted by Maksym Rivtin <support@cityhost.net.ua>
+cx.ua
+
+// Civilized Discourse Construction Kit, Inc. : https://www.discourse.org/
+// Submitted by Rishabh Nambiar & Michael Brown <team@discourse.org>
+discourse.group
+discourse.team
+
+// Clever Cloud : https://www.clever-cloud.com/
+// Submitted by Quentin Adam <noc@clever-cloud.com>
+cleverapps.io
+
+// Clerk : https://www.clerk.dev
+// Submitted by Colin Sidoti <systems@clerk.dev>
+clerk.app
+clerkstage.app
+*.lcl.dev
+*.lclstage.dev
+*.stg.dev
+*.stgstage.dev
+
+// ClickRising : https://clickrising.com/
+// Submitted by Umut Gumeli <infrastructure-publicsuffixlist@clickrising.com>
+clickrising.net
+
+// Cloud66 : https://www.cloud66.com/
+// Submitted by Khash Sajadi <khash@cloud66.com>
+c66.me
+cloud66.ws
+cloud66.zone
+
+// CloudAccess.net : https://www.cloudaccess.net/
+// Submitted by Pawel Panek <noc@cloudaccess.net>
+jdevcloud.com
+wpdevcloud.com
+cloudaccess.host
+freesite.host
+cloudaccess.net
+
+// cloudControl : https://www.cloudcontrol.com/
+// Submitted by Tobias Wilken <tw@cloudcontrol.com>
+cloudcontrolled.com
+cloudcontrolapp.com
+
+// Cloudera, Inc. : https://www.cloudera.com/
+// Submitted by Kedarnath Waikar <security@cloudera.com>
+*.cloudera.site
+
+// Cloudflare, Inc. : https://www.cloudflare.com/
+// Submitted by Cloudflare Team <publicsuffixlist@cloudflare.com>
+cf-ipfs.com
+cloudflare-ipfs.com
+trycloudflare.com
+pages.dev
+r2.dev
+workers.dev
+
+// Clovyr : https://clovyr.io
+// Submitted by Patrick Nielsen <patrick@clovyr.io>
+wnext.app
+
+// co.ca : http://registry.co.ca/
+co.ca
+
+// Co & Co : https://co-co.nl/
+// Submitted by Govert Versluis <govert@co-co.nl>
+*.otap.co
+
+// i-registry s.r.o. : http://www.i-registry.cz/
+// Submitted by Martin Semrad <semrad@i-registry.cz>
+co.cz
+
+// CDN77.com : http://www.cdn77.com
+// Submitted by Jan Krpes <jan.krpes@cdn77.com>
+c.cdn77.org
+cdn77-ssl.net
+r.cdn77.net
+rsc.cdn77.org
+ssl.origin.cdn77-secure.org
+
+// Cloud DNS Ltd : http://www.cloudns.net
+// Submitted by Aleksander Hristov <noc@cloudns.net>
+cloudns.asia
+cloudns.biz
+cloudns.club
+cloudns.cc
+cloudns.eu
+cloudns.in
+cloudns.info
+cloudns.org
+cloudns.pro
+cloudns.pw
+cloudns.us
+
+// CNPY : https://cnpy.gdn
+// Submitted by Angelo Gladding <angelo@lahacker.net>
+cnpy.gdn
+
+// Codeberg e. V. : https://codeberg.org
+// Submitted by Moritz Marquardt <git@momar.de>
+codeberg.page
+
+// CoDNS B.V.
+co.nl
+co.no
+
+// Combell.com : https://www.combell.com
+// Submitted by Thomas Wouters <thomas.wouters@combellgroup.com>
+webhosting.be
+hosting-cluster.nl
+
+// Convex : https://convex.dev/
+// Submitted by James Cowling <security@convex.dev>
+convex.site
+
+// Coordination Center for TLD RU and XN--P1AI : https://cctld.ru/en/domains/domens_ru/reserved/
+// Submitted by George Georgievsky <gug@cctld.ru>
+ac.ru
+edu.ru
+gov.ru
+int.ru
+mil.ru
+test.ru
+
+// COSIMO GmbH : http://www.cosimo.de
+// Submitted by Rene Marticke <rmarticke@cosimo.de>
+dyn.cosidns.de
+dynamisches-dns.de
+dnsupdater.de
+internet-dns.de
+l-o-g-i-n.de
+dynamic-dns.info
+feste-ip.net
+knx-server.net
+static-access.net
+
+// cPanel L.L.C. : https://www.cpanel.net/
+// Submitted by Dustin Scherer <public.suffix@cpanel.net>
+*.cprapid.com
+
+// Craynic, s.r.o. : http://www.craynic.com/
+// Submitted by Ales Krajnik <ales.krajnik@craynic.com>
+realm.cz
+
+// Crisp IM SAS : https://crisp.chat/
+// Submitted by Baptiste Jamin <hostmaster@crisp.chat>
+on.crisp.email
+
+// Cryptonomic : https://cryptonomic.net/
+// Submitted by Andrew Cady <public-suffix-list@cryptonomic.net>
+*.cryptonomic.net
+
+// Cupcake : https://cupcake.io/
+// Submitted by Jonathan Rudenberg <jonathan@cupcake.io>
+cupcake.is
+
+// Curv UG : https://curv-labs.de/
+// Submitted by Marvin Wiesner <Marvin@curv-labs.de>
+curv.dev
+
+// Customer OCI - Oracle Dyn https://cloud.oracle.com/home https://dyn.com/dns/
+// Submitted by Gregory Drake <support@dyn.com>
+// Note: This is intended to also include customer-oci.com due to wildcards implicitly including the current label
+*.customer-oci.com
+*.oci.customer-oci.com
+*.ocp.customer-oci.com
+*.ocs.customer-oci.com
+
+// Cyclic Software : https://www.cyclic.sh
+// Submitted by Kam Lasater <dns-admin@cyclic.sh>
+cyclic.app
+cyclic.cloud
+cyclic-app.com
+cyclic.co.in
+
+// cyon GmbH : https://www.cyon.ch/
+// Submitted by Dominic Luechinger <dol@cyon.ch>
+cyon.link
+cyon.site
+
+// Danger Science Group: https://dangerscience.com/
+// Submitted by Skylar MacDonald <skylar@dangerscience.com>
+fnwk.site
+folionetwork.site
+platform0.app
+
+// Daplie, Inc : https://daplie.com
+// Submitted by AJ ONeal <aj@daplie.com>
+daplie.me
+localhost.daplie.me
+
+// Datto, Inc. : https://www.datto.com/
+// Submitted by Philipp Heckel <ph@datto.com>
+dattolocal.com
+dattorelay.com
+dattoweb.com
+mydatto.com
+dattolocal.net
+mydatto.net
+
+// Dansk.net : http://www.dansk.net/
+// Submitted by Anani Voule <digital@digital.co.dk>
+biz.dk
+co.dk
+firm.dk
+reg.dk
+store.dk
+
+// dappnode.io : https://dappnode.io/
+// Submitted by Abel Boldu / DAppNode Team <community@dappnode.io>
+dyndns.dappnode.io
+
+// dapps.earth : https://dapps.earth/
+// Submitted by Daniil Burdakov <icqkill@gmail.com>
+*.dapps.earth
+*.bzz.dapps.earth
+
+// Dark, Inc. : https://darklang.com
+// Submitted by Paul Biggar <ops@darklang.com>
+builtwithdark.com
+
+// DataDetect, LLC. : https://datadetect.com
+// Submitted by Andrew Banchich <abanchich@sceven.com>
+demo.datadetect.com
+instance.datadetect.com
+
+// Datawire, Inc : https://www.datawire.io
+// Submitted by Richard Li <secalert@datawire.io>
+edgestack.me
+
+// DDNS5 : https://ddns5.com
+// Submitted by Cameron Elliott <cameron@cameronelliott.com>
+ddns5.com
+
+// Debian : https://www.debian.org/
+// Submitted by Peter Palfrader / Debian Sysadmin Team <dsa-publicsuffixlist@debian.org>
+debian.net
+
+// Deno Land Inc : https://deno.com/
+// Submitted by Luca Casonato <hostmaster@deno.com>
+deno.dev
+deno-staging.dev
+
+// deSEC : https://desec.io/
+// Submitted by Peter Thomassen <peter@desec.io>
+dedyn.io
+
+// Deta: https://www.deta.sh/
+// Submitted by Aavash Shrestha <aavash@deta.sh>
+deta.app
+deta.dev
+
+// Diher Solutions : https://diher.solutions
+// Submitted by Didi Hermawan <mail@diher.solutions>
+*.rss.my.id
+*.diher.solutions
+
+// Discord Inc : https://discord.com
+// Submitted by Sahn Lam <slam@discordapp.com>
+discordsays.com
+discordsez.com
+
+// DNS Africa Ltd https://dns.business
+// Submitted by Calvin Browne <calvin@dns.business>
+jozi.biz
+
+// DNShome : https://www.dnshome.de/
+// Submitted by Norbert Auler <mail@dnshome.de>
+dnshome.de
+
+// DotArai : https://www.dotarai.com/
+// Submitted by Atsadawat Netcharadsang <atsadawat@dotarai.co.th>
+online.th
+shop.th
+
+// DrayTek Corp. : https://www.draytek.com/
+// Submitted by Paul Fang <mis@draytek.com>
+drayddns.com
+
+// DreamCommerce : https://shoper.pl/
+// Submitted by Konrad Kotarba <konrad.kotarba@dreamcommerce.com>
+shoparena.pl
+
+// DreamHost : http://www.dreamhost.com/
+// Submitted by Andrew Farmer <andrew.farmer@dreamhost.com>
+dreamhosters.com
+
+// Drobo : http://www.drobo.com/
+// Submitted by Ricardo Padilha <rpadilha@drobo.com>
+mydrobo.com
+
+// Drud Holdings, LLC. : https://www.drud.com/
+// Submitted by Kevin Bridges <kevin@drud.com>
+drud.io
+drud.us
+
+// DuckDNS : http://www.duckdns.org/
+// Submitted by Richard Harper <richard@duckdns.org>
+duckdns.org
+
+// Bip : https://bip.sh
+// Submitted by Joel Kennedy <joel@bip.sh>
+bip.sh
+
+// bitbridge.net : Submitted by Craig Welch, abeliidev@gmail.com
+bitbridge.net
+
+// dy.fi : http://dy.fi/
+// Submitted by Heikki Hannikainen <hessu@hes.iki.fi>
+dy.fi
+tunk.org
+
+// DynDNS.com : http://www.dyndns.com/services/dns/dyndns/
+dyndns-at-home.com
+dyndns-at-work.com
+dyndns-blog.com
+dyndns-free.com
+dyndns-home.com
+dyndns-ip.com
+dyndns-mail.com
+dyndns-office.com
+dyndns-pics.com
+dyndns-remote.com
+dyndns-server.com
+dyndns-web.com
+dyndns-wiki.com
+dyndns-work.com
+dyndns.biz
+dyndns.info
+dyndns.org
+dyndns.tv
+at-band-camp.net
+ath.cx
+barrel-of-knowledge.info
+barrell-of-knowledge.info
+better-than.tv
+blogdns.com
+blogdns.net
+blogdns.org
+blogsite.org
+boldlygoingnowhere.org
+broke-it.net
+buyshouses.net
+cechire.com
+dnsalias.com
+dnsalias.net
+dnsalias.org
+dnsdojo.com
+dnsdojo.net
+dnsdojo.org
+does-it.net
+doesntexist.com
+doesntexist.org
+dontexist.com
+dontexist.net
+dontexist.org
+doomdns.com
+doomdns.org
+dvrdns.org
+dyn-o-saur.com
+dynalias.com
+dynalias.net
+dynalias.org
+dynathome.net
+dyndns.ws
+endofinternet.net
+endofinternet.org
+endoftheinternet.org
+est-a-la-maison.com
+est-a-la-masion.com
+est-le-patron.com
+est-mon-blogueur.com
+for-better.biz
+for-more.biz
+for-our.info
+for-some.biz
+for-the.biz
+forgot.her.name
+forgot.his.name
+from-ak.com
+from-al.com
+from-ar.com
+from-az.net
+from-ca.com
+from-co.net
+from-ct.com
+from-dc.com
+from-de.com
+from-fl.com
+from-ga.com
+from-hi.com
+from-ia.com
+from-id.com
+from-il.com
+from-in.com
+from-ks.com
+from-ky.com
+from-la.net
+from-ma.com
+from-md.com
+from-me.org
+from-mi.com
+from-mn.com
+from-mo.com
+from-ms.com
+from-mt.com
+from-nc.com
+from-nd.com
+from-ne.com
+from-nh.com
+from-nj.com
+from-nm.com
+from-nv.com
+from-ny.net
+from-oh.com
+from-ok.com
+from-or.com
+from-pa.com
+from-pr.com
+from-ri.com
+from-sc.com
+from-sd.com
+from-tn.com
+from-tx.com
+from-ut.com
+from-va.com
+from-vt.com
+from-wa.com
+from-wi.com
+from-wv.com
+from-wy.com
+ftpaccess.cc
+fuettertdasnetz.de
+game-host.org
+game-server.cc
+getmyip.com
+gets-it.net
+go.dyndns.org
+gotdns.com
+gotdns.org
+groks-the.info
+groks-this.info
+ham-radio-op.net
+here-for-more.info
+hobby-site.com
+hobby-site.org
+home.dyndns.org
+homedns.org
+homeftp.net
+homeftp.org
+homeip.net
+homelinux.com
+homelinux.net
+homelinux.org
+homeunix.com
+homeunix.net
+homeunix.org
+iamallama.com
+in-the-band.net
+is-a-anarchist.com
+is-a-blogger.com
+is-a-bookkeeper.com
+is-a-bruinsfan.org
+is-a-bulls-fan.com
+is-a-candidate.org
+is-a-caterer.com
+is-a-celticsfan.org
+is-a-chef.com
+is-a-chef.net
+is-a-chef.org
+is-a-conservative.com
+is-a-cpa.com
+is-a-cubicle-slave.com
+is-a-democrat.com
+is-a-designer.com
+is-a-doctor.com
+is-a-financialadvisor.com
+is-a-geek.com
+is-a-geek.net
+is-a-geek.org
+is-a-green.com
+is-a-guru.com
+is-a-hard-worker.com
+is-a-hunter.com
+is-a-knight.org
+is-a-landscaper.com
+is-a-lawyer.com
+is-a-liberal.com
+is-a-libertarian.com
+is-a-linux-user.org
+is-a-llama.com
+is-a-musician.com
+is-a-nascarfan.com
+is-a-nurse.com
+is-a-painter.com
+is-a-patsfan.org
+is-a-personaltrainer.com
+is-a-photographer.com
+is-a-player.com
+is-a-republican.com
+is-a-rockstar.com
+is-a-socialist.com
+is-a-soxfan.org
+is-a-student.com
+is-a-teacher.com
+is-a-techie.com
+is-a-therapist.com
+is-an-accountant.com
+is-an-actor.com
+is-an-actress.com
+is-an-anarchist.com
+is-an-artist.com
+is-an-engineer.com
+is-an-entertainer.com
+is-by.us
+is-certified.com
+is-found.org
+is-gone.com
+is-into-anime.com
+is-into-cars.com
+is-into-cartoons.com
+is-into-games.com
+is-leet.com
+is-lost.org
+is-not-certified.com
+is-saved.org
+is-slick.com
+is-uberleet.com
+is-very-bad.org
+is-very-evil.org
+is-very-good.org
+is-very-nice.org
+is-very-sweet.org
+is-with-theband.com
+isa-geek.com
+isa-geek.net
+isa-geek.org
+isa-hockeynut.com
+issmarterthanyou.com
+isteingeek.de
+istmein.de
+kicks-ass.net
+kicks-ass.org
+knowsitall.info
+land-4-sale.us
+lebtimnetz.de
+leitungsen.de
+likes-pie.com
+likescandy.com
+merseine.nu
+mine.nu
+misconfused.org
+mypets.ws
+myphotos.cc
+neat-url.com
+office-on-the.net
+on-the-web.tv
+podzone.net
+podzone.org
+readmyblog.org
+saves-the-whales.com
+scrapper-site.net
+scrapping.cc
+selfip.biz
+selfip.com
+selfip.info
+selfip.net
+selfip.org
+sells-for-less.com
+sells-for-u.com
+sells-it.net
+sellsyourhome.org
+servebbs.com
+servebbs.net
+servebbs.org
+serveftp.net
+serveftp.org
+servegame.org
+shacknet.nu
+simple-url.com
+space-to-rent.com
+stuff-4-sale.org
+stuff-4-sale.us
+teaches-yoga.com
+thruhere.net
+traeumtgerade.de
+webhop.biz
+webhop.info
+webhop.net
+webhop.org
+worse-than.tv
+writesthisblog.com
+
+// ddnss.de : https://www.ddnss.de/
+// Submitted by Robert Niedziela <webmaster@ddnss.de>
+ddnss.de
+dyn.ddnss.de
+dyndns.ddnss.de
+dyndns1.de
+dyn-ip24.de
+home-webserver.de
+dyn.home-webserver.de
+myhome-server.de
+ddnss.org
+
+// Definima : http://www.definima.com/
+// Submitted by Maxence Bitterli <maxence@definima.com>
+definima.net
+definima.io
+
+// DigitalOcean App Platform : https://www.digitalocean.com/products/app-platform/
+// Submitted by Braxton Huggins <psl-maintainers@digitalocean.com>
+ondigitalocean.app
+
+// DigitalOcean Spaces : https://www.digitalocean.com/products/spaces/
+// Submitted by Robin H. Johnson <psl-maintainers@digitalocean.com>
+*.digitaloceanspaces.com
+
+// dnstrace.pro : https://dnstrace.pro/
+// Submitted by Chris Partridge <chris@partridge.tech>
+bci.dnstrace.pro
+
+// Dynu.com : https://www.dynu.com/
+// Submitted by Sue Ye <sue@dynu.com>
+ddnsfree.com
+ddnsgeek.com
+giize.com
+gleeze.com
+kozow.com
+loseyourip.com
+ooguy.com
+theworkpc.com
+casacam.net
+dynu.net
+accesscam.org
+camdvr.org
+freeddns.org
+mywire.org
+webredirect.org
+myddns.rocks
+blogsite.xyz
+
+// dynv6 : https://dynv6.com
+// Submitted by Dominik Menke <dom@digineo.de>
+dynv6.net
+
+// E4YOU spol. s.r.o. : https://e4you.cz/
+// Submitted by Vladimir Dudr <info@e4you.cz>
+e4.cz
+
+// Easypanel : https://easypanel.io
+// Submitted by Andrei Canta <andrei@easypanel.io>
+easypanel.app
+easypanel.host
+
+// EasyWP : https://www.easywp.com
+// Submitted by <infracloudteam@namecheap.com>
+*.ewp.live
+
+// Elementor : Elementor Ltd.
+// Submitted by Anton Barkan <antonb@elementor.com>
+elementor.cloud
+elementor.cool
+
+// En root‽ : https://en-root.org
+// Submitted by Emmanuel Raviart <emmanuel@raviart.com>
+en-root.fr
+
+// Enalean SAS: https://www.enalean.com
+// Submitted by Thomas Cottier <thomas.cottier@enalean.com>
+mytuleap.com
+tuleap-partners.com
+
+// Encoretivity AB: https://encore.dev
+// Submitted by André Eriksson <andre@encore.dev>
+encr.app
+encoreapi.com
+
+// ECG Robotics, Inc: https://ecgrobotics.org
+// Submitted by <frc1533@ecgrobotics.org>
+onred.one
+staging.onred.one
+
+// encoway GmbH : https://www.encoway.de
+// Submitted by Marcel Daus <cloudops@encoway.de>
+eu.encoway.cloud
+
+// EU.org https://eu.org/
+// Submitted by Pierre Beyssac <hostmaster@eu.org>
+eu.org
+al.eu.org
+asso.eu.org
+at.eu.org
+au.eu.org
+be.eu.org
+bg.eu.org
+ca.eu.org
+cd.eu.org
+ch.eu.org
+cn.eu.org
+cy.eu.org
+cz.eu.org
+de.eu.org
+dk.eu.org
+edu.eu.org
+ee.eu.org
+es.eu.org
+fi.eu.org
+fr.eu.org
+gr.eu.org
+hr.eu.org
+hu.eu.org
+ie.eu.org
+il.eu.org
+in.eu.org
+int.eu.org
+is.eu.org
+it.eu.org
+jp.eu.org
+kr.eu.org
+lt.eu.org
+lu.eu.org
+lv.eu.org
+mc.eu.org
+me.eu.org
+mk.eu.org
+mt.eu.org
+my.eu.org
+net.eu.org
+ng.eu.org
+nl.eu.org
+no.eu.org
+nz.eu.org
+paris.eu.org
+pl.eu.org
+pt.eu.org
+q-a.eu.org
+ro.eu.org
+ru.eu.org
+se.eu.org
+si.eu.org
+sk.eu.org
+tr.eu.org
+uk.eu.org
+us.eu.org
+
+// Eurobyte : https://eurobyte.ru
+// Submitted by Evgeniy Subbotin <e.subbotin@eurobyte.ru>
+eurodir.ru
+
+// Evennode : http://www.evennode.com/
+// Submitted by Michal Kralik <support@evennode.com>
+eu-1.evennode.com
+eu-2.evennode.com
+eu-3.evennode.com
+eu-4.evennode.com
+us-1.evennode.com
+us-2.evennode.com
+us-3.evennode.com
+us-4.evennode.com
+
+// eDirect Corp. : https://hosting.url.com.tw/
+// Submitted by C.S. chang <cschang@corp.url.com.tw>
+twmail.cc
+twmail.net
+twmail.org
+mymailer.com.tw
+url.tw
+
+// Fabrica Technologies, Inc. : https://www.fabrica.dev/
+// Submitted by Eric Jiang <eric@fabrica.dev>
+onfabrica.com
+
+// FAITID : https://faitid.org/
+// Submitted by Maxim Alzoba <tech.contact@faitid.org>
+// https://www.flexireg.net/stat_info
+ru.net
+adygeya.ru
+bashkiria.ru
+bir.ru
+cbg.ru
+com.ru
+dagestan.ru
+grozny.ru
+kalmykia.ru
+kustanai.ru
+marine.ru
+mordovia.ru
+msk.ru
+mytis.ru
+nalchik.ru
+nov.ru
+pyatigorsk.ru
+spb.ru
+vladikavkaz.ru
+vladimir.ru
+abkhazia.su
+adygeya.su
+aktyubinsk.su
+arkhangelsk.su
+armenia.su
+ashgabad.su
+azerbaijan.su
+balashov.su
+bashkiria.su
+bryansk.su
+bukhara.su
+chimkent.su
+dagestan.su
+east-kazakhstan.su
+exnet.su
+georgia.su
+grozny.su
+ivanovo.su
+jambyl.su
+kalmykia.su
+kaluga.su
+karacol.su
+karaganda.su
+karelia.su
+khakassia.su
+krasnodar.su
+kurgan.su
+kustanai.su
+lenug.su
+mangyshlak.su
+mordovia.su
+msk.su
+murmansk.su
+nalchik.su
+navoi.su
+north-kazakhstan.su
+nov.su
+obninsk.su
+penza.su
+pokrovsk.su
+sochi.su
+spb.su
+tashkent.su
+termez.su
+togliatti.su
+troitsk.su
+tselinograd.su
+tula.su
+tuva.su
+vladikavkaz.su
+vladimir.su
+vologda.su
+
+// Fancy Bits, LLC : http://getchannels.com
+// Submitted by Aman Gupta <aman@getchannels.com>
+channelsdvr.net
+u.channelsdvr.net
+
+// Fastly Inc. : http://www.fastly.com/
+// Submitted by Fastly Security <security@fastly.com>
+edgecompute.app
+fastly-edge.com
+fastly-terrarium.com
+fastlylb.net
+map.fastlylb.net
+freetls.fastly.net
+map.fastly.net
+a.prod.fastly.net
+global.prod.fastly.net
+a.ssl.fastly.net
+b.ssl.fastly.net
+global.ssl.fastly.net
+
+// Fastmail : https://www.fastmail.com/
+// Submitted by Marc Bradshaw <marc@fastmailteam.com>
+*.user.fm
+
+// FASTVPS EESTI OU : https://fastvps.ru/
+// Submitted by Likhachev Vasiliy <lihachev@fastvps.ru>
+fastvps-server.com
+fastvps.host
+myfast.host
+fastvps.site
+myfast.space
+
+// Fedora : https://fedoraproject.org/
+// submitted by Patrick Uiterwijk <puiterwijk@fedoraproject.org>
+fedorainfracloud.org
+fedorapeople.org
+cloud.fedoraproject.org
+app.os.fedoraproject.org
+app.os.stg.fedoraproject.org
+
+// FearWorks Media Ltd. : https://fearworksmedia.co.uk
+// submitted by Keith Fairley <domains@fearworksmedia.co.uk>
+conn.uk
+copro.uk
+hosp.uk
+
+// Fermax : https://fermax.com/
+// submitted by Koen Van Isterdael <k.vanisterdael@fermax.be>
+mydobiss.com
+
+// FH Muenster : https://www.fh-muenster.de
+// Submitted by Robin Naundorf <r.naundorf@fh-muenster.de>
+fh-muenster.io
+
+// Filegear Inc. : https://www.filegear.com
+// Submitted by Jason Zhu <jason@owtware.com>
+filegear.me
+filegear-au.me
+filegear-de.me
+filegear-gb.me
+filegear-ie.me
+filegear-jp.me
+filegear-sg.me
+
+// Firebase, Inc.
+// Submitted by Chris Raynor <chris@firebase.com>
+firebaseapp.com
+
+// Firewebkit : https://www.firewebkit.com
+// Submitted by Majid Qureshi <mqureshi@amrayn.com>
+fireweb.app
+
+// FLAP : https://www.flap.cloud
+// Submitted by Louis Chemineau <louis@chmn.me>
+flap.id
+
+// FlashDrive : https://flashdrive.io
+// Submitted by Eric Chan <support@flashdrive.io>
+onflashdrive.app
+fldrv.com
+
+// FlutterFlow : https://flutterflow.io
+// Submitted by Anton Emelyanov <anton@flutterflow.io>
+flutterflow.app
+
+// fly.io: https://fly.io
+// Submitted by Kurt Mackey <kurt@fly.io>
+fly.dev
+edgeapp.net
+shw.io
+
+// Flynn : https://flynn.io
+// Submitted by Jonathan Rudenberg <jonathan@flynn.io>
+flynnhosting.net
+
+// Forgerock : https://www.forgerock.com
+// Submitted by Roderick Parr <roderick.parr@forgerock.com>
+forgeblocks.com
+id.forgerock.io
+
+// Framer : https://www.framer.com
+// Submitted by Koen Rouwhorst <koenrh@framer.com>
+framer.app
+framercanvas.com
+framer.media
+framer.photos
+framer.website
+framer.wiki
+
+// Frusky MEDIA&PR : https://www.frusky.de
+// Submitted by Victor Pupynin <hallo@frusky.de>
+*.frusky.de
+
+// RavPage : https://www.ravpage.co.il
+// Submitted by Roni Horowitz <roni@responder.co.il>
+ravpage.co.il
+
+// Frederik Braun https://frederik-braun.com
+// Submitted by Frederik Braun <fb@frederik-braun.com>
+0e.vc
+
+// Freebox : http://www.freebox.fr
+// Submitted by Romain Fliedel <rfliedel@freebox.fr>
+freebox-os.com
+freeboxos.com
+fbx-os.fr
+fbxos.fr
+freebox-os.fr
+freeboxos.fr
+
+// freedesktop.org : https://www.freedesktop.org
+// Submitted by Daniel Stone <daniel@fooishbar.org>
+freedesktop.org
+
+// freemyip.com : https://freemyip.com
+// Submitted by Cadence <contact@freemyip.com>
+freemyip.com
+
+// FunkFeuer - Verein zur Förderung freier Netze : https://www.funkfeuer.at
+// Submitted by Daniel A. Maierhofer <vorstand@funkfeuer.at>
+wien.funkfeuer.at
+
+// Futureweb GmbH : https://www.futureweb.at
+// Submitted by Andreas Schnederle-Wagner <schnederle@futureweb.at>
+*.futurecms.at
+*.ex.futurecms.at
+*.in.futurecms.at
+futurehosting.at
+futuremailing.at
+*.ex.ortsinfo.at
+*.kunden.ortsinfo.at
+*.statics.cloud
+
+// GCom Internet : https://www.gcom.net.au
+// Submitted by Leo Julius <support@gcom.net.au>
+aliases121.com
+
+// GDS : https://www.gov.uk/service-manual/technology/managing-domain-names
+// Submitted by Stephen Ford <hostmaster@digital.cabinet-office.gov.uk>
+independent-commission.uk
+independent-inquest.uk
+independent-inquiry.uk
+independent-panel.uk
+independent-review.uk
+public-inquiry.uk
+royal-commission.uk
+campaign.gov.uk
+service.gov.uk
+
+// CDDO : https://www.gov.uk/guidance/get-an-api-domain-on-govuk
+// Submitted by Jamie Tanna <jamie.tanna@digital.cabinet-office.gov.uk>
+api.gov.uk
+
+// Gehirn Inc. : https://www.gehirn.co.jp/
+// Submitted by Kohei YOSHIDA <tech@gehirn.co.jp>
+gehirn.ne.jp
+usercontent.jp
+
+// Gentlent, Inc. : https://www.gentlent.com
+// Submitted by Tom Klein <tom@gentlent.com>
+gentapps.com
+gentlentapis.com
+lab.ms
+cdn-edges.net
+
+// Ghost Foundation : https://ghost.org
+// Submitted by Matt Hanley <security@ghost.org>
+ghost.io
+
+// GignoSystemJapan: http://gsj.bz
+// Submitted by GignoSystemJapan <kakutou-ec@gsj.bz>
+gsj.bz
+
+// GitHub, Inc.
+// Submitted by Patrick Toomey <security@github.com>
+githubusercontent.com
+githubpreview.dev
+github.io
+
+// GitLab, Inc.
+// Submitted by Alex Hanselka <alex@gitlab.com>
+gitlab.io
+
+// Gitplac.si - https://gitplac.si
+// Submitted by Aljaž Starc <me@aljaxus.eu>
+gitapp.si
+gitpage.si
+
+// Glitch, Inc : https://glitch.com
+// Submitted by Mads Hartmann <mads@glitch.com>
+glitch.me
+
+// Global NOG Alliance : https://nogalliance.org/
+// Submitted by Sander Steffann <sander@nogalliance.org>
+nog.community
+
+// Globe Hosting SRL : https://www.globehosting.com/
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+co.ro
+shop.ro
+
+// GMO Pepabo, Inc. : https://pepabo.com/
+// Submitted by Hosting Div <admin@pepabo.com>
+lolipop.io
+angry.jp
+babyblue.jp
+babymilk.jp
+backdrop.jp
+bambina.jp
+bitter.jp
+blush.jp
+boo.jp
+boy.jp
+boyfriend.jp
+but.jp
+candypop.jp
+capoo.jp
+catfood.jp
+cheap.jp
+chicappa.jp
+chillout.jp
+chips.jp
+chowder.jp
+chu.jp
+ciao.jp
+cocotte.jp
+coolblog.jp
+cranky.jp
+cutegirl.jp
+daa.jp
+deca.jp
+deci.jp
+digick.jp
+egoism.jp
+fakefur.jp
+fem.jp
+flier.jp
+floppy.jp
+fool.jp
+frenchkiss.jp
+girlfriend.jp
+girly.jp
+gloomy.jp
+gonna.jp
+greater.jp
+hacca.jp
+heavy.jp
+her.jp
+hiho.jp
+hippy.jp
+holy.jp
+hungry.jp
+icurus.jp
+itigo.jp
+jellybean.jp
+kikirara.jp
+kill.jp
+kilo.jp
+kuron.jp
+littlestar.jp
+lolipopmc.jp
+lolitapunk.jp
+lomo.jp
+lovepop.jp
+lovesick.jp
+main.jp
+mods.jp
+mond.jp
+mongolian.jp
+moo.jp
+namaste.jp
+nikita.jp
+nobushi.jp
+noor.jp
+oops.jp
+parallel.jp
+parasite.jp
+pecori.jp
+peewee.jp
+penne.jp
+pepper.jp
+perma.jp
+pigboat.jp
+pinoko.jp
+punyu.jp
+pupu.jp
+pussycat.jp
+pya.jp
+raindrop.jp
+readymade.jp
+sadist.jp
+schoolbus.jp
+secret.jp
+staba.jp
+stripper.jp
+sub.jp
+sunnyday.jp
+thick.jp
+tonkotsu.jp
+under.jp
+upper.jp
+velvet.jp
+verse.jp
+versus.jp
+vivian.jp
+watson.jp
+weblike.jp
+whitesnow.jp
+zombie.jp
+heteml.net
+
+// GOV.UK Platform as a Service : https://www.cloud.service.gov.uk/
+// Submitted by Tom Whitwell <gov-uk-paas-support@digital.cabinet-office.gov.uk>
+cloudapps.digital
+london.cloudapps.digital
+
+// GOV.UK Pay : https://www.payments.service.gov.uk/
+// Submitted by Richard Baker <richard.baker@digital.cabinet-office.gov.uk>
+pymnt.uk
+
+// GlobeHosting, Inc.
+// Submitted by Zoltan Egresi <egresi@globehosting.com>
+ro.im
+
+// GoIP DNS Services : http://www.goip.de
+// Submitted by Christian Poulter <milchstrasse@goip.de>
+goip.de
+
+// Google, Inc.
+// Submitted by Eduardo Vela <evn@google.com>
+*.run.app
+web.app
+*.0emm.com
+appspot.com
+*.r.appspot.com
+codespot.com
+googleapis.com
+googlecode.com
+pagespeedmobilizer.com
+publishproxy.com
+withgoogle.com
+withyoutube.com
+*.gateway.dev
+cloud.goog
+translate.goog
+*.usercontent.goog
+cloudfunctions.net
+blogspot.ae
+blogspot.al
+blogspot.am
+blogspot.ba
+blogspot.be
+blogspot.bg
+blogspot.bj
+blogspot.ca
+blogspot.cf
+blogspot.ch
+blogspot.cl
+blogspot.co.at
+blogspot.co.id
+blogspot.co.il
+blogspot.co.ke
+blogspot.co.nz
+blogspot.co.uk
+blogspot.co.za
+blogspot.com
+blogspot.com.ar
+blogspot.com.au
+blogspot.com.br
+blogspot.com.by
+blogspot.com.co
+blogspot.com.cy
+blogspot.com.ee
+blogspot.com.eg
+blogspot.com.es
+blogspot.com.mt
+blogspot.com.ng
+blogspot.com.tr
+blogspot.com.uy
+blogspot.cv
+blogspot.cz
+blogspot.de
+blogspot.dk
+blogspot.fi
+blogspot.fr
+blogspot.gr
+blogspot.hk
+blogspot.hr
+blogspot.hu
+blogspot.ie
+blogspot.in
+blogspot.is
+blogspot.it
+blogspot.jp
+blogspot.kr
+blogspot.li
+blogspot.lt
+blogspot.lu
+blogspot.md
+blogspot.mk
+blogspot.mr
+blogspot.mx
+blogspot.my
+blogspot.nl
+blogspot.no
+blogspot.pe
+blogspot.pt
+blogspot.qa
+blogspot.re
+blogspot.ro
+blogspot.rs
+blogspot.ru
+blogspot.se
+blogspot.sg
+blogspot.si
+blogspot.sk
+blogspot.sn
+blogspot.td
+blogspot.tw
+blogspot.ug
+blogspot.vn
+
+// Goupile : https://goupile.fr
+// Submitted by Niels Martignene <hello@goupile.fr>
+goupile.fr
+
+// Government of the Netherlands: https://www.government.nl
+// Submitted by <domeinnaam@minaz.nl>
+gov.nl
+
+// Group 53, LLC : https://www.group53.com
+// Submitted by Tyler Todd <noc@nova53.net>
+awsmppl.com
+
+// GünstigBestellen : https://günstigbestellen.de
+// Submitted by Furkan Akkoc <info@hendelzon.de>
+günstigbestellen.de
+günstigliefern.de
+
+// Hakaran group: http://hakaran.cz
+// Submitted by Arseniy Sokolov <security@hakaran.cz>
+fin.ci
+free.hr
+caa.li
+ua.rs
+conf.se
+
+// Handshake : https://handshake.org
+// Submitted by Mike Damm <md@md.vc>
+hs.zone
+hs.run
+
+// Hashbang : https://hashbang.sh
+hashbang.sh
+
+// Hasura : https://hasura.io
+// Submitted by Shahidh K Muhammed <shahidh@hasura.io>
+hasura.app
+hasura-app.io
+
+// Heilbronn University of Applied Sciences - Faculty Informatics (GitLab Pages): https://www.hs-heilbronn.de
+// Submitted by Richard Zowalla <mi-admin@hs-heilbronn.de>
+pages.it.hs-heilbronn.de
+
+// Hepforge : https://www.hepforge.org
+// Submitted by David Grellscheid <admin@hepforge.org>
+hepforge.org
+
+// Heroku : https://www.heroku.com/
+// Submitted by Tom Maher <tmaher@heroku.com>
+herokuapp.com
+herokussl.com
+
+// Hibernating Rhinos
+// Submitted by Oren Eini <oren@ravendb.net>
+ravendb.cloud
+ravendb.community
+ravendb.me
+development.run
+ravendb.run
+
+// home.pl S.A.: https://home.pl
+// Submitted by Krzysztof Wolski <krzysztof.wolski@home.eu>
+homesklep.pl
+
+// Homebase : https://homebase.id/
+// Submitted by Jason Babo <info@homebase.id>
+*.kin.one
+*.id.pub
+*.kin.pub
+
+// Hong Kong Productivity Council: https://www.hkpc.org/
+// Submitted by SECaaS Team <summchan@hkpc.org>
+secaas.hk
+
+// Hoplix : https://www.hoplix.com
+// Submitted by Danilo De Franco<info@hoplix.shop>
+hoplix.shop
+
+
+// HOSTBIP REGISTRY : https://www.hostbip.com/
+// Submitted by Atanunu Igbunuroghene <publicsuffixlist@hostbip.com>
+orx.biz
+biz.gl
+col.ng
+firm.ng
+gen.ng
+ltd.ng
+ngo.ng
+edu.scot
+sch.so
+
+// HostFly : https://www.ie.ua
+// Submitted by Bohdan Dub <support@hostfly.com.ua>
+ie.ua
+
+// HostyHosting (hostyhosting.com)
+hostyhosting.io
+
+// Häkkinen.fi
+// Submitted by Eero Häkkinen <Eero+psl@Häkkinen.fi>
+häkkinen.fi
+
+// Ici la Lune : http://www.icilalune.com/
+// Submitted by Simon Morvan <simon@icilalune.com>
+*.moonscale.io
+moonscale.net
+
+// iki.fi
+// Submitted by Hannu Aronsson <haa@iki.fi>
+iki.fi
+
+// iliad italia: https://www.iliad.it
+// Submitted by Marios Makassikis <mmakassikis@freebox.fr>
+ibxos.it
+iliadboxos.it
+
+// Impertrix Solutions : <https://impertrixcdn.com>
+// Submitted by Zhixiang Zhao <csuite@impertrix.com>
+impertrixcdn.com
+impertrix.com
+
+// Incsub, LLC: https://incsub.com/
+// Submitted by Aaron Edwards <sysadmins@incsub.com>
+smushcdn.com
+wphostedmail.com
+wpmucdn.com
+tempurl.host
+wpmudev.host
+
+// Individual Network Berlin e.V. : https://www.in-berlin.de/
+// Submitted by Christian Seitz <chris@in-berlin.de>
+dyn-berlin.de
+in-berlin.de
+in-brb.de
+in-butter.de
+in-dsl.de
+in-dsl.net
+in-dsl.org
+in-vpn.de
+in-vpn.net
+in-vpn.org
+
+// info.at : http://www.info.at/
+biz.at
+info.at
+
+// info.cx : http://info.cx
+// Submitted by Jacob Slater <whois@igloo.to>
+info.cx
+
+// Interlegis : http://www.interlegis.leg.br
+// Submitted by Gabriel Ferreira <registrobr@interlegis.leg.br>
+ac.leg.br
+al.leg.br
+am.leg.br
+ap.leg.br
+ba.leg.br
+ce.leg.br
+df.leg.br
+es.leg.br
+go.leg.br
+ma.leg.br
+mg.leg.br
+ms.leg.br
+mt.leg.br
+pa.leg.br
+pb.leg.br
+pe.leg.br
+pi.leg.br
+pr.leg.br
+rj.leg.br
+rn.leg.br
+ro.leg.br
+rr.leg.br
+rs.leg.br
+sc.leg.br
+se.leg.br
+sp.leg.br
+to.leg.br
+
+// intermetrics GmbH : https://pixolino.com/
+// Submitted by Wolfgang Schwarz <admin@intermetrics.de>
+pixolino.com
+
+// Internet-Pro, LLP: https://netangels.ru/
+// Submitted by Vasiliy Sheredeko <piphon@gmail.com>
+na4u.ru
+
+// iopsys software solutions AB : https://iopsys.eu/
+// Submitted by Roman Azarenko <roman.azarenko@iopsys.eu>
+iopsys.se
+
+// IPiFony Systems, Inc. : https://www.ipifony.com/
+// Submitted by Matthew Hardeman <mhardeman@ipifony.com>
+ipifony.net
+
+// IServ GmbH : https://iserv.de
+// Submitted by Mario Hoberg <info@iserv.de>
+iservschule.de
+mein-iserv.de
+schulplattform.de
+schulserver.de
+test-iserv.de
+iserv.dev
+
+// I-O DATA DEVICE, INC. : http://www.iodata.com/
+// Submitted by Yuji Minagawa <domains-admin@iodata.jp>
+iobb.net
+
+// Jelastic, Inc. : https://jelastic.com/
+// Submitted by Ihor Kolodyuk <ik@jelastic.com>
+mel.cloudlets.com.au
+cloud.interhostsolutions.be
+mycloud.by
+alp1.ae.flow.ch
+appengine.flow.ch
+es-1.axarnet.cloud
+diadem.cloud
+vip.jelastic.cloud
+jele.cloud
+it1.eur.aruba.jenv-aruba.cloud
+it1.jenv-aruba.cloud
+keliweb.cloud
+cs.keliweb.cloud
+oxa.cloud
+tn.oxa.cloud
+uk.oxa.cloud
+primetel.cloud
+uk.primetel.cloud
+ca.reclaim.cloud
+uk.reclaim.cloud
+us.reclaim.cloud
+ch.trendhosting.cloud
+de.trendhosting.cloud
+jele.club
+amscompute.com
+dopaas.com
+paas.hosted-by-previder.com
+rag-cloud.hosteur.com
+rag-cloud-ch.hosteur.com
+jcloud.ik-server.com
+jcloud-ver-jpc.ik-server.com
+demo.jelastic.com
+kilatiron.com
+paas.massivegrid.com
+jed.wafaicloud.com
+lon.wafaicloud.com
+ryd.wafaicloud.com
+j.scaleforce.com.cy
+jelastic.dogado.eu
+fi.cloudplatform.fi
+demo.datacenter.fi
+paas.datacenter.fi
+jele.host
+mircloud.host
+paas.beebyte.io
+sekd1.beebyteapp.io
+jele.io
+cloud-fr1.unispace.io
+jc.neen.it
+cloud.jelastic.open.tim.it
+jcloud.kz
+upaas.kazteleport.kz
+cloudjiffy.net
+fra1-de.cloudjiffy.net
+west1-us.cloudjiffy.net
+jls-sto1.elastx.net
+jls-sto2.elastx.net
+jls-sto3.elastx.net
+faststacks.net
+fr-1.paas.massivegrid.net
+lon-1.paas.massivegrid.net
+lon-2.paas.massivegrid.net
+ny-1.paas.massivegrid.net
+ny-2.paas.massivegrid.net
+sg-1.paas.massivegrid.net
+jelastic.saveincloud.net
+nordeste-idc.saveincloud.net
+j.scaleforce.net
+jelastic.tsukaeru.net
+sdscloud.pl
+unicloud.pl
+mircloud.ru
+jelastic.regruhosting.ru
+enscaled.sg
+jele.site
+jelastic.team
+orangecloud.tn
+j.layershift.co.uk
+phx.enscaled.us
+mircloud.us
+
+// Jino : https://www.jino.ru
+// Submitted by Sergey Ulyashin <ulyashin@jino.ru>
+myjino.ru
+*.hosting.myjino.ru
+*.landing.myjino.ru
+*.spectrum.myjino.ru
+*.vps.myjino.ru
+
+// Jotelulu S.L. : https://jotelulu.com
+// Submitted by Daniel Fariña <ingenieria@jotelulu.com>
+jotelulu.cloud
+
+// Joyent : https://www.joyent.com/
+// Submitted by Brian Bennett <brian.bennett@joyent.com>
+*.triton.zone
+*.cns.joyent.com
+
+// JS.ORG : http://dns.js.org
+// Submitted by Stefan Keim <admin@js.org>
+js.org
+
+// KaasHosting : http://www.kaashosting.nl/
+// Submitted by Wouter Bakker <hostmaster@kaashosting.nl>
+kaas.gg
+khplay.nl
+
+// Kakao : https://www.kakaocorp.com/
+// Submitted by JaeYoong Lee <cec@kakaocorp.com>
+ktistory.com
+
+// Kapsi : https://kapsi.fi
+// Submitted by Tomi Juntunen <erani@kapsi.fi>
+kapsi.fi
+
+// Keyweb AG : https://www.keyweb.de
+// Submitted by Martin Dannehl <postmaster@keymachine.de>
+keymachine.de
+
+// KingHost : https://king.host
+// Submitted by Felipe Keller Braz <felipebraz@kinghost.com.br>
+kinghost.net
+uni5.net
+
+// KnightPoint Systems, LLC : http://www.knightpoint.com/
+// Submitted by Roy Keene <rkeene@knightpoint.com>
+knightpoint.systems
+
+// KoobinEvent, SL: https://www.koobin.com
+// Submitted by Iván Oliva <ivan.oliva@koobin.com>
+koobin.events
+
+// KUROKU LTD : https://kuroku.ltd/
+// Submitted by DisposaBoy <security@oya.to>
+oya.to
+
+// Katholieke Universiteit Leuven: https://www.kuleuven.be
+// Submitted by Abuse KU Leuven <abuse@kuleuven.be>
+kuleuven.cloud
+ezproxy.kuleuven.be
+
+// .KRD : http://nic.krd/data/krd/Registration%20Policy.pdf
+co.krd
+edu.krd
+
+// Krellian Ltd. : https://krellian.com
+// Submitted by Ben Francis <ben@krellian.com>
+krellian.net
+webthings.io
+
+// LCube - Professional hosting e.K. : https://www.lcube-webhosting.de
+// Submitted by Lars Laehn <info@lcube.de>
+git-repos.de
+lcube-server.de
+svn-repos.de
+
+// Leadpages : https://www.leadpages.net
+// Submitted by Greg Dallavalle <domains@leadpages.net>
+leadpages.co
+lpages.co
+lpusercontent.com
+
+// Lelux.fi : https://lelux.fi/
+// Submitted by Lelux Admin <publisuffix@lelux.site>
+lelux.site
+
+// Lifetime Hosting : https://Lifetime.Hosting/
+// Submitted by Mike Fillator <support@lifetime.hosting>
+co.business
+co.education
+co.events
+co.financial
+co.network
+co.place
+co.technology
+
+// Lightmaker Property Manager, Inc. : https://app.lmpm.com/
+// Submitted by Greg Holland <greg.holland@lmpm.com>
+app.lmpm.com
+
+// linkyard ldt: https://www.linkyard.ch/
+// Submitted by Mario Siegenthaler <mario.siegenthaler@linkyard.ch>
+linkyard.cloud
+linkyard-cloud.ch
+
+// Linode : https://linode.com
+// Submitted by <security@linode.com>
+members.linode.com
+*.nodebalancer.linode.com
+*.linodeobjects.com
+ip.linodeusercontent.com
+
+// LiquidNet Ltd : http://www.liquidnetlimited.com/
+// Submitted by Victor Velchev <admin@liquidnetlimited.com>
+we.bs
+
+// Localcert : https://localcert.dev
+// Submitted by Lann Martin <security@localcert.dev>
+*.user.localcert.dev
+
+// localzone.xyz
+// Submitted by Kenny Niehage <hello@yahe.sh>
+localzone.xyz
+
+// Log'in Line : https://www.loginline.com/
+// Submitted by Rémi Mach <remi.mach@loginline.com>
+loginline.app
+loginline.dev
+loginline.io
+loginline.services
+loginline.site
+
+// Lokalized : https://lokalized.nl
+// Submitted by Noah Taheij <noah@lokalized.nl>
+servers.run
+
+// Lõhmus Family, The
+// Submitted by Heiki Lõhmus <hostmaster at lohmus dot me>
+lohmus.me
+
+// LubMAN UMCS Sp. z o.o : https://lubman.pl/
+// Submitted by Ireneusz Maliszewski <ireneusz.maliszewski@lubman.pl>
+krasnik.pl
+leczna.pl
+lubartow.pl
+lublin.pl
+poniatowa.pl
+swidnik.pl
+
+// Lug.org.uk : https://lug.org.uk
+// Submitted by Jon Spriggs <admin@lug.org.uk>
+glug.org.uk
+lug.org.uk
+lugs.org.uk
+
+// Lukanet Ltd : https://lukanet.com
+// Submitted by Anton Avramov <register@lukanet.com>
+barsy.bg
+barsy.co.uk
+barsyonline.co.uk
+barsycenter.com
+barsyonline.com
+barsy.club
+barsy.de
+barsy.eu
+barsy.in
+barsy.info
+barsy.io
+barsy.me
+barsy.menu
+barsy.mobi
+barsy.net
+barsy.online
+barsy.org
+barsy.pro
+barsy.pub
+barsy.ro
+barsy.shop
+barsy.site
+barsy.support
+barsy.uk
+
+// Magento Commerce
+// Submitted by Damien Tournoud <dtournoud@magento.cloud>
+*.magentosite.cloud
+
+// May First - People Link : https://mayfirst.org/
+// Submitted by Jamie McClelland <info@mayfirst.org>
+mayfirst.info
+mayfirst.org
+
+// Mail.Ru Group : https://hb.cldmail.ru
+// Submitted by Ilya Zaretskiy <zaretskiy@corp.mail.ru>
+hb.cldmail.ru
+
+// Mail Transfer Platform : https://www.neupeer.com
+// Submitted by Li Hui <lihui@neupeer.com>
+cn.vu
+
+// Maze Play: https://www.mazeplay.com
+// Submitted by Adam Humpherys <adam@mws.dev>
+mazeplay.com
+
+// mcpe.me : https://mcpe.me
+// Submitted by Noa Heyl <hi@noa.dev>
+mcpe.me
+
+// McHost : https://mchost.ru
+// Submitted by Evgeniy Subbotin <e.subbotin@mchost.ru>
+mcdir.me
+mcdir.ru
+mcpre.ru
+vps.mcdir.ru
+
+// Mediatech : https://mediatech.by
+// Submitted by Evgeniy Kozhuhovskiy <ugenk@mediatech.by>
+mediatech.by
+mediatech.dev
+
+// Medicom Health : https://medicomhealth.com
+// Submitted by Michael Olson <molson@medicomhealth.com>
+hra.health
+
+// Memset hosting : https://www.memset.com
+// Submitted by Tom Whitwell <domains@memset.com>
+miniserver.com
+memset.net
+
+// Messerli Informatik AG : https://www.messerli.ch/
+// Submitted by Ruben Schmidmeister <psl-maintainers@messerli.ch>
+messerli.app
+
+// Meta Platforms, Inc. : https://meta.com/
+// Submitted by Jacob Cordero <public-suffix@meta.com>
+atmeta.com
+apps.fbsbx.com
+
+// MetaCentrum, CESNET z.s.p.o. : https://www.metacentrum.cz/en/
+// Submitted by Zdeněk Šustr <zdenek.sustr@cesnet.cz>
+*.cloud.metacentrum.cz
+custom.metacentrum.cz
+
+// MetaCentrum, CESNET z.s.p.o. : https://www.metacentrum.cz/en/
+// Submitted by Radim Janča <janca@cesnet.cz>
+flt.cloud.muni.cz
+usr.cloud.muni.cz
+
+// Meteor Development Group : https://www.meteor.com/hosting
+// Submitted by Pierre Carrier <pierre@meteor.com>
+meteorapp.com
+eu.meteorapp.com
+
+// Michau Enterprises Limited : http://www.co.pl/
+co.pl
+
+// Microsoft Corporation : http://microsoft.com
+// Submitted by Public Suffix List Admin <msftpsladmin@microsoft.com>
+// Managed by Corporate Domains
+// Microsoft Azure : https://home.azure
+*.azurecontainer.io
+*.cloudapp.azure.com
+azure-api.net
+azureedge.net
+azurefd.net
+azurewebsites.net
+azure-mobile.net
+azurestaticapps.net
+1.azurestaticapps.net
+2.azurestaticapps.net
+3.azurestaticapps.net
+4.azurestaticapps.net
+5.azurestaticapps.net
+6.azurestaticapps.net
+7.azurestaticapps.net
+centralus.azurestaticapps.net
+eastasia.azurestaticapps.net
+eastus2.azurestaticapps.net
+westeurope.azurestaticapps.net
+westus2.azurestaticapps.net
+cloudapp.net
+trafficmanager.net
+blob.core.windows.net
+servicebus.windows.net
+
+// minion.systems : http://minion.systems
+// Submitted by Robert Böttinger <r@minion.systems>
+csx.cc
+
+// Mintere : https://mintere.com/
+// Submitted by Ben Aubin <security@mintere.com>
+mintere.site
+
+// MobileEducation, LLC : https://joinforte.com
+// Submitted by Grayson Martin <grayson.martin@mobileeducation.us>
+forte.id
+
+// MODX Systems LLC : https://modx.com
+// Submitted by Elizabeth Southwell <elizabeth@modx.com>
+modx.dev
+
+// Mozilla Corporation : https://mozilla.com
+// Submitted by Ben Francis <bfrancis@mozilla.com>
+mozilla-iot.org
+
+// Mozilla Foundation : https://mozilla.org/
+// Submitted by glob <glob@mozilla.com>
+bmoattachments.org
+
+// MSK-IX : https://www.msk-ix.ru/
+// Submitted by Khannanov Roman <r.khannanov@msk-ix.ru>
+net.ru
+org.ru
+pp.ru
+
+// Mythic Beasts : https://www.mythic-beasts.com
+// Submitted by Paul Cammish <kelduum@mythic-beasts.com>
+hostedpi.com
+customer.mythic-beasts.com
+caracal.mythic-beasts.com
+fentiger.mythic-beasts.com
+lynx.mythic-beasts.com
+ocelot.mythic-beasts.com
+oncilla.mythic-beasts.com
+onza.mythic-beasts.com
+sphinx.mythic-beasts.com
+vs.mythic-beasts.com
+x.mythic-beasts.com
+yali.mythic-beasts.com
+cust.retrosnub.co.uk
+
+// Nabu Casa : https://www.nabucasa.com
+// Submitted by Paulus Schoutsen <infra@nabucasa.com>
+ui.nabu.casa
+
+// Net at Work Gmbh : https://www.netatwork.de
+// Submitted by Jan Jaeschke <jan.jaeschke@netatwork.de>
+cloud.nospamproxy.com
+
+// Netlify : https://www.netlify.com
+// Submitted by Jessica Parsons <jessica@netlify.com>
+netlify.app
+
+// Neustar Inc.
+// Submitted by Trung Tran <Trung.Tran@neustar.biz>
+4u.com
+
+// ngrok : https://ngrok.com/
+// Submitted by Alan Shreve <alan@ngrok.com>
+ngrok.app
+ngrok-free.app
+ngrok.dev
+ngrok-free.dev
+ngrok.io
+ap.ngrok.io
+au.ngrok.io
+eu.ngrok.io
+in.ngrok.io
+jp.ngrok.io
+sa.ngrok.io
+us.ngrok.io
+ngrok.pizza
+ngrok.pro
+
+// Nicolaus Copernicus University in Torun - MSK TORMAN (https://www.man.torun.pl)
+torun.pl
+
+// Nimbus Hosting Ltd. : https://www.nimbushosting.co.uk/
+// Submitted by Nicholas Ford <nick@nimbushosting.co.uk>
+nh-serv.co.uk
+
+// NFSN, Inc. : https://www.NearlyFreeSpeech.NET/
+// Submitted by Jeff Wheelhouse <support@nearlyfreespeech.net>
+nfshost.com
+
+// Noop : https://noop.app
+// Submitted by Nathaniel Schweinberg <noop@rearc.io>
+*.developer.app
+noop.app
+
+// Northflank Ltd. : https://northflank.com/
+// Submitted by Marco Suter <marco@northflank.com>
+*.northflank.app
+*.build.run
+*.code.run
+*.database.run
+*.migration.run
+
+// Noticeable : https://noticeable.io
+// Submitted by Laurent Pellegrino <security@noticeable.io>
+noticeable.news
+
+// Now-DNS : https://now-dns.com
+// Submitted by Steve Russell <steve@now-dns.com>
+dnsking.ch
+mypi.co
+n4t.co
+001www.com
+ddnslive.com
+myiphost.com
+forumz.info
+16-b.it
+32-b.it
+64-b.it
+soundcast.me
+tcp4.me
+dnsup.net
+hicam.net
+now-dns.net
+ownip.net
+vpndns.net
+dynserv.org
+now-dns.org
+x443.pw
+now-dns.top
+ntdll.top
+freeddns.us
+crafting.xyz
+zapto.xyz
+
+// nsupdate.info : https://www.nsupdate.info/
+// Submitted by Thomas Waldmann <info@nsupdate.info>
+nsupdate.info
+nerdpol.ovh
+
+// No-IP.com : https://noip.com/
+// Submitted by Deven Reza <publicsuffixlist@noip.com>
+blogsyte.com
+brasilia.me
+cable-modem.org
+ciscofreak.com
+collegefan.org
+couchpotatofries.org
+damnserver.com
+ddns.me
+ditchyourip.com
+dnsfor.me
+dnsiskinky.com
+dvrcam.info
+dynns.com
+eating-organic.net
+fantasyleague.cc
+geekgalaxy.com
+golffan.us
+health-carereform.com
+homesecuritymac.com
+homesecuritypc.com
+hopto.me
+ilovecollege.info
+loginto.me
+mlbfan.org
+mmafan.biz
+myactivedirectory.com
+mydissent.net
+myeffect.net
+mymediapc.net
+mypsx.net
+mysecuritycamera.com
+mysecuritycamera.net
+mysecuritycamera.org
+net-freaks.com
+nflfan.org
+nhlfan.net
+no-ip.ca
+no-ip.co.uk
+no-ip.net
+noip.us
+onthewifi.com
+pgafan.net
+point2this.com
+pointto.us
+privatizehealthinsurance.net
+quicksytes.com
+read-books.org
+securitytactics.com
+serveexchange.com
+servehumour.com
+servep2p.com
+servesarcasm.com
+stufftoread.com
+ufcfan.org
+unusualperson.com
+workisboring.com
+3utilities.com
+bounceme.net
+ddns.net
+ddnsking.com
+gotdns.ch
+hopto.org
+myftp.biz
+myftp.org
+myvnc.com
+no-ip.biz
+no-ip.info
+no-ip.org
+noip.me
+redirectme.net
+servebeer.com
+serveblog.net
+servecounterstrike.com
+serveftp.com
+servegame.com
+servehalflife.com
+servehttp.com
+serveirc.com
+serveminecraft.net
+servemp3.com
+servepics.com
+servequake.com
+sytes.net
+webhop.me
+zapto.org
+
+// NodeArt : https://nodeart.io
+// Submitted by Konstantin Nosov <Nosov@nodeart.io>
+stage.nodeart.io
+
+// Nucleos Inc. : https://nucleos.com
+// Submitted by Piotr Zduniak <piotr@nucleos.com>
+pcloud.host
+
+// NYC.mn : http://www.information.nyc.mn
+// Submitted by Matthew Brown <mattbrown@nyc.mn>
+nyc.mn
+
+// Observable, Inc. : https://observablehq.com
+// Submitted by Mike Bostock <dns@observablehq.com>
+static.observableusercontent.com
+
+// Octopodal Solutions, LLC. : https://ulterius.io/
+// Submitted by Andrew Sampson <andrew@ulterius.io>
+cya.gg
+
+// OMG.LOL : <https://omg.lol>
+// Submitted by Adam Newbold <adam@omg.lol>
+omg.lol
+
+// Omnibond Systems, LLC. : https://www.omnibond.com
+// Submitted by Cole Estep <cole@omnibond.com>
+cloudycluster.net
+
+// OmniWe Limited: https://omniwe.com
+// Submitted by Vicary Archangel <vicary@omniwe.com>
+omniwe.site
+
+// One.com: https://www.one.com/
+// Submitted by Jacob Bunk Nielsen <jbn@one.com>
+123hjemmeside.dk
+123hjemmeside.no
+123homepage.it
+123kotisivu.fi
+123minsida.se
+123miweb.es
+123paginaweb.pt
+123sait.ru
+123siteweb.fr
+123webseite.at
+123webseite.de
+123website.be
+123website.ch
+123website.lu
+123website.nl
+service.one
+simplesite.com
+simplesite.com.br
+simplesite.gr
+simplesite.pl
+
+// One Fold Media : http://www.onefoldmedia.com/
+// Submitted by Eddie Jones <eddie@onefoldmedia.com>
+nid.io
+
+// Open Social : https://www.getopensocial.com/
+// Submitted by Alexander Varwijk <security@getopensocial.com>
+opensocial.site
+
+// OpenCraft GmbH : http://opencraft.com/
+// Submitted by Sven Marnach <sven@opencraft.com>
+opencraft.hosting
+
+// OpenResearch GmbH: https://openresearch.com/
+// Submitted by Philipp Schmid <ops@openresearch.com>
+orsites.com
+
+// Opera Software, A.S.A.
+// Submitted by Yngve Pettersen <yngve@opera.com>
+operaunite.com
+
+// Orange : https://www.orange.com
+// Submitted by Alexandre Linte <alexandre.linte@orange.com>
+tech.orange
+
+// Oursky Limited : https://authgear.com/, https://skygear.io/
+// Submitted by Authgear Team <hello@authgear.com>, Skygear Developer <hello@skygear.io>
+authgear-staging.com
+authgearapps.com
+skygearapp.com
+
+// OutSystems
+// Submitted by Duarte Santos <domain-admin@outsystemscloud.com>
+outsystemscloud.com
+
+// OVHcloud: https://ovhcloud.com
+// Submitted by Vincent Cassé <vincent.casse@ovhcloud.com>
+*.webpaas.ovh.net
+*.hosting.ovh.net
+
+// OwnProvider GmbH: http://www.ownprovider.com
+// Submitted by Jan Moennich <jan.moennich@ownprovider.com>
+ownprovider.com
+own.pm
+
+// OwO : https://whats-th.is/
+// Submitted by Dean Sheather <dean@deansheather.com>
+*.owo.codes
+
+// OX : http://www.ox.rs
+// Submitted by Adam Grand <webmaster@mail.ox.rs>
+ox.rs
+
+// oy.lc
+// Submitted by Charly Coste <changaco@changaco.oy.lc>
+oy.lc
+
+// Pagefog : https://pagefog.com/
+// Submitted by Derek Myers <derek@pagefog.com>
+pgfog.com
+
+// Pagefront : https://www.pagefronthq.com/
+// Submitted by Jason Kriss <jason@pagefronthq.com>
+pagefrontapp.com
+
+// PageXL : https://pagexl.com
+// Submitted by Yann Guichard <yann@pagexl.com>
+pagexl.com
+
+// Paywhirl, Inc : https://paywhirl.com/
+// Submitted by Daniel Netzer <dan@paywhirl.com>
+*.paywhirl.com
+
+// pcarrier.ca Software Inc: https://pcarrier.ca/
+// Submitted by Pierre Carrier <pc@rrier.ca>
+bar0.net
+bar1.net
+bar2.net
+rdv.to
+
+// .pl domains (grandfathered)
+art.pl
+gliwice.pl
+krakow.pl
+poznan.pl
+wroc.pl
+zakopane.pl
+
+// Pantheon Systems, Inc. : https://pantheon.io/
+// Submitted by Gary Dylina <gary@pantheon.io>
+pantheonsite.io
+gotpantheon.com
+
+// Peplink | Pepwave : http://peplink.com/
+// Submitted by Steve Leung <steveleung@peplink.com>
+mypep.link
+
+// Perspecta : https://perspecta.com/
+// Submitted by Kenneth Van Alstyne <kvanalstyne@perspecta.com>
+perspecta.cloud
+
+// PE Ulyanov Kirill Sergeevich : https://airy.host
+// Submitted by Kirill Ulyanov <k.ulyanov@airy.host>
+lk3.ru
+
+// Planet-Work : https://www.planet-work.com/
+// Submitted by Frédéric VANNIÈRE <f.vanniere@planet-work.com>
+on-web.fr
+
+// Platform.sh : https://platform.sh
+// Submitted by Nikola Kotur <nikola@platform.sh>
+*.upsun.app
+upsunapp.com
+ent.platform.sh
+eu.platform.sh
+us.platform.sh
+*.platformsh.site
+*.tst.site
+
+// Platter: https://platter.dev
+// Submitted by Patrick Flor <patrick@platter.dev>
+platter-app.com
+platter-app.dev
+platterp.us
+
+// Plesk : https://www.plesk.com/
+// Submitted by Anton Akhtyamov <program-managers@plesk.com>
+pdns.page
+plesk.page
+pleskns.com
+
+// Pley AB : https://www.pley.com/
+// Submitted by Henning Pohl <infra@pley.com>
+pley.games
+
+// Port53 : https://port53.io/
+// Submitted by Maximilian Schieder <maxi@zeug.co>
+dyn53.io
+
+// Porter : https://porter.run/
+// Submitted by Rudraksh MK <rudi@porter.run>
+onporter.run
+
+// Positive Codes Technology Company : http://co.bn/faq.html
+// Submitted by Zulfais <pc@co.bn>
+co.bn
+
+// Postman, Inc : https://postman.com
+// Submitted by Rahul Dhawan <security@postman.com>
+postman-echo.com
+pstmn.io
+mock.pstmn.io
+httpbin.org
+
+//prequalifyme.today : https://prequalifyme.today
+//Submitted by DeepakTiwari deepak@ivylead.io
+prequalifyme.today
+
+// prgmr.com : https://prgmr.com/
+// Submitted by Sarah Newman <owner@prgmr.com>
+xen.prgmr.com
+
+// priv.at : http://www.nic.priv.at/
+// Submitted by registry <lendl@nic.at>
+priv.at
+
+// privacytools.io : https://www.privacytools.io/
+// Submitted by Jonah Aragon <jonah@privacytools.io>
+prvcy.page
+
+// Protocol Labs : https://protocol.ai/
+// Submitted by Michael Burns <noc@protocol.ai>
+*.dweb.link
+
+// Protonet GmbH : http://protonet.io
+// Submitted by Martin Meier <admin@protonet.io>
+protonet.io
+
+// Publication Presse Communication SARL : https://ppcom.fr
+// Submitted by Yaacov Akiba Slama <admin@chirurgiens-dentistes-en-france.fr>
+chirurgiens-dentistes-en-france.fr
+byen.site
+
+// pubtls.org: https://www.pubtls.org
+// Submitted by Kor Nielsen <kor@pubtls.org>
+pubtls.org
+
+// PythonAnywhere LLP: https://www.pythonanywhere.com
+// Submitted by Giles Thomas <giles@pythonanywhere.com>
+pythonanywhere.com
+eu.pythonanywhere.com
+
+// QOTO, Org.
+// Submitted by Jeffrey Phillips Freeman <jeffrey.freeman@qoto.org>
+qoto.io
+
+// Qualifio : https://qualifio.com/
+// Submitted by Xavier De Cock <xdecock@gmail.com>
+qualifioapp.com
+
+// Quality Unit: https://qualityunit.com
+// Submitted by Vasyl Tsalko <vtsalko@qualityunit.com>
+ladesk.com
+
+// QuickBackend: https://www.quickbackend.com
+// Submitted by Dani Biro <dani@pymet.com>
+qbuser.com
+
+// Rad Web Hosting: https://radwebhosting.com
+// Submitted by Scott Claeys <s.claeys@radwebhosting.com>
+cloudsite.builders
+myradweb.net
+servername.us
+
+// Redgate Software: https://red-gate.com
+// Submitted by Andrew Farries <andrew.farries@red-gate.com>
+instances.spawn.cc
+
+// Redstar Consultants : https://www.redstarconsultants.com/
+// Submitted by Jons Slemmer <jons@redstarconsultants.com>
+instantcloud.cn
+
+// Russian Academy of Sciences
+// Submitted by Tech Support <support@rasnet.ru>
+ras.ru
+
+// QA2
+// Submitted by Daniel Dent (https://www.danieldent.com/)
+qa2.com
+
+// QCX
+// Submitted by Cassandra Beelen <cassandra@beelen.one>
+qcx.io
+*.sys.qcx.io
+
+// QNAP System Inc : https://www.qnap.com
+// Submitted by Nick Chang <nickchang@qnap.com>
+dev-myqnapcloud.com
+alpha-myqnapcloud.com
+myqnapcloud.com
+
+// Quip : https://quip.com
+// Submitted by Patrick Linehan <plinehan@quip.com>
+*.quipelements.com
+
+// Qutheory LLC : http://qutheory.io
+// Submitted by Jonas Schwartz <jonas@qutheory.io>
+vapor.cloud
+vaporcloud.io
+
+// Rackmaze LLC : https://www.rackmaze.com
+// Submitted by Kirill Pertsev <kika@rackmaze.com>
+rackmaze.com
+rackmaze.net
+
+// Rakuten Games, Inc : https://dev.viberplay.io
+// Submitted by Joshua Zhang <public-suffix@rgames.jp>
+g.vbrplsbx.io
+
+// Rancher Labs, Inc : https://rancher.com
+// Submitted by Vincent Fiduccia <domains@rancher.com>
+*.on-k3s.io
+*.on-rancher.cloud
+*.on-rio.io
+
+// Read The Docs, Inc : https://www.readthedocs.org
+// Submitted by David Fischer <team@readthedocs.org>
+readthedocs.io
+
+// Red Hat, Inc. OpenShift : https://openshift.redhat.com/
+// Submitted by Tim Kramer <tkramer@rhcloud.com>
+rhcloud.com
+
+// Render : https://render.com
+// Submitted by Anurag Goel <dev@render.com>
+app.render.com
+onrender.com
+
+// Repl.it : https://repl.it
+// Submitted by Lincoln Bergeson <psl@repl.it>
+replit.app
+id.replit.app
+firewalledreplit.co
+id.firewalledreplit.co
+repl.co
+id.repl.co
+replit.dev
+archer.replit.dev
+bones.replit.dev
+canary.replit.dev
+global.replit.dev
+hacker.replit.dev
+id.replit.dev
+janeway.replit.dev
+kim.replit.dev
+kira.replit.dev
+kirk.replit.dev
+odo.replit.dev
+paris.replit.dev
+picard.replit.dev
+pike.replit.dev
+prerelease.replit.dev
+reed.replit.dev
+riker.replit.dev
+sisko.replit.dev
+spock.replit.dev
+staging.replit.dev
+sulu.replit.dev
+tarpit.replit.dev
+teams.replit.dev
+tucker.replit.dev
+wesley.replit.dev
+worf.replit.dev
+repl.run
+
+// Resin.io : https://resin.io
+// Submitted by Tim Perry <tim@resin.io>
+resindevice.io
+devices.resinstaging.io
+
+// RethinkDB : https://www.rethinkdb.com/
+// Submitted by Chris Kastorff <info@rethinkdb.com>
+hzc.io
+
+// Revitalised Limited : http://www.revitalised.co.uk
+// Submitted by Jack Price <jack@revitalised.co.uk>
+wellbeingzone.eu
+wellbeingzone.co.uk
+
+// Rico Developments Limited : https://adimo.co
+// Submitted by Colin Brown <hello@adimo.co>
+adimo.co.uk
+
+// Riseup Networks : https://riseup.net
+// Submitted by Micah Anderson <micah@riseup.net>
+itcouldbewor.se
+
+// Rochester Institute of Technology : http://www.rit.edu/
+// Submitted by Jennifer Herting <jchits@rit.edu>
+git-pages.rit.edu
+
+// Rocky Enterprise Software Foundation : https://resf.org
+// Submitted by Neil Hanlon <neil@resf.org>
+rocky.page
+
+// Rusnames Limited: http://rusnames.ru/
+// Submitted by Sergey Zotov <admin@rusnames.ru>
+биз.рус
+ком.рус
+крым.рус
+мир.рус
+мск.рус
+орг.рус
+самара.рус
+сочи.рус
+спб.рус
+я.рус
+
+// SAKURA Internet Inc. : https://www.sakura.ad.jp/
+// Submitted by Internet Service Department <rs-vendor-ml@sakura.ad.jp>
+180r.com
+dojin.com
+sakuratan.com
+sakuraweb.com
+x0.com
+2-d.jp
+bona.jp
+crap.jp
+daynight.jp
+eek.jp
+flop.jp
+halfmoon.jp
+jeez.jp
+matrix.jp
+mimoza.jp
+ivory.ne.jp
+mail-box.ne.jp
+mints.ne.jp
+mokuren.ne.jp
+opal.ne.jp
+sakura.ne.jp
+sumomo.ne.jp
+topaz.ne.jp
+netgamers.jp
+nyanta.jp
+o0o0.jp
+rdy.jp
+rgr.jp
+rulez.jp
+s3.isk01.sakurastorage.jp
+s3.isk02.sakurastorage.jp
+saloon.jp
+sblo.jp
+skr.jp
+tank.jp
+uh-oh.jp
+undo.jp
+rs.webaccel.jp
+user.webaccel.jp
+websozai.jp
+xii.jp
+squares.net
+jpn.org
+kirara.st
+x0.to
+from.tv
+sakura.tv
+
+// Salesforce.com, Inc. https://salesforce.com/
+// Submitted by Michael Biven <mbiven@salesforce.com> and Aaron Romeo <aaron.romeo@salesforce.com>
+*.builder.code.com
+*.dev-builder.code.com
+*.stg-builder.code.com
+*.001.test.code-builder-stg.platform.salesforce.com
+
+// Sandstorm Development Group, Inc. : https://sandcats.io/
+// Submitted by Asheesh Laroia <asheesh@sandstorm.io>
+sandcats.io
+
+// SBE network solutions GmbH : https://www.sbe.de/
+// Submitted by Norman Meilick <nm@sbe.de>
+logoip.de
+logoip.com
+
+// Scaleway : https://www.scaleway.com/
+// Submitted by Rémy Léone <rleone@scaleway.com>
+fr-par-1.baremetal.scw.cloud
+fr-par-2.baremetal.scw.cloud
+nl-ams-1.baremetal.scw.cloud
+cockpit.fr-par.scw.cloud
+fnc.fr-par.scw.cloud
+functions.fnc.fr-par.scw.cloud
+k8s.fr-par.scw.cloud
+nodes.k8s.fr-par.scw.cloud
+s3.fr-par.scw.cloud
+s3-website.fr-par.scw.cloud
+whm.fr-par.scw.cloud
+priv.instances.scw.cloud
+pub.instances.scw.cloud
+k8s.scw.cloud
+cockpit.nl-ams.scw.cloud
+k8s.nl-ams.scw.cloud
+nodes.k8s.nl-ams.scw.cloud
+s3.nl-ams.scw.cloud
+s3-website.nl-ams.scw.cloud
+whm.nl-ams.scw.cloud
+cockpit.pl-waw.scw.cloud
+k8s.pl-waw.scw.cloud
+nodes.k8s.pl-waw.scw.cloud
+s3.pl-waw.scw.cloud
+s3-website.pl-waw.scw.cloud
+scalebook.scw.cloud
+smartlabeling.scw.cloud
+dedibox.fr
+
+// schokokeks.org GbR : https://schokokeks.org/
+// Submitted by Hanno Böck <hanno@schokokeks.org>
+schokokeks.net
+
+// Scottish Government: https://www.gov.scot
+// Submitted by Martin Ellis <martin.ellis@gov.scot>
+gov.scot
+service.gov.scot
+
+// Scry Security : http://www.scrysec.com
+// Submitted by Shante Adam <shante@skyhat.io>
+scrysec.com
+
+// Securepoint GmbH : https://www.securepoint.de
+// Submitted by Erik Anders <erik.anders@securepoint.de>
+firewall-gateway.com
+firewall-gateway.de
+my-gateway.de
+my-router.de
+spdns.de
+spdns.eu
+firewall-gateway.net
+my-firewall.org
+myfirewall.org
+spdns.org
+
+// Seidat : https://www.seidat.com
+// Submitted by Artem Kondratev <accounts@seidat.com>
+seidat.net
+
+// Sellfy : https://sellfy.com
+// Submitted by Yuriy Romadin <contact@sellfy.com>
+sellfy.store
+
+// Senseering GmbH : https://www.senseering.de
+// Submitted by Felix Mönckemeyer <f.moenckemeyer@senseering.de>
+senseering.net
+
+// Sendmsg: https://www.sendmsg.co.il
+// Submitted by Assaf Stern <domains@comstar.co.il>
+minisite.ms
+
+// Service Magnet : https://myservicemagnet.com
+// Submitted by Dave Sanders <dave@myservicemagnet.com>
+magnet.page
+
+// Service Online LLC : http://drs.ua/
+// Submitted by Serhii Bulakh <support@drs.ua>
+biz.ua
+co.ua
+pp.ua
+
+// Shift Crypto AG : https://shiftcrypto.ch
+// Submitted by alex <alex@shiftcrypto.ch>
+shiftcrypto.dev
+shiftcrypto.io
+
+// ShiftEdit : https://shiftedit.net/
+// Submitted by Adam Jimenez <adam@shiftcreate.com>
+shiftedit.io
+
+// Shopblocks : http://www.shopblocks.com/
+// Submitted by Alex Bowers <alex@shopblocks.com>
+myshopblocks.com
+
+// Shopify : https://www.shopify.com
+// Submitted by Alex Richter <alex.richter@shopify.com>
+myshopify.com
+
+// Shopit : https://www.shopitcommerce.com/
+// Submitted by Craig McMahon <craig@shopitcommerce.com>
+shopitsite.com
+
+// shopware AG : https://shopware.com
+// Submitted by Jens Küper <cloud@shopware.com>
+shopware.store
+
+// Siemens Mobility GmbH
+// Submitted by Oliver Graebner <security@mo-siemens.io>
+mo-siemens.io
+
+// SinaAppEngine : http://sae.sina.com.cn/
+// Submitted by SinaAppEngine <saesupport@sinacloud.com>
+1kapp.com
+appchizi.com
+applinzi.com
+sinaapp.com
+vipsinaapp.com
+
+// Siteleaf : https://www.siteleaf.com/
+// Submitted by Skylar Challand <support@siteleaf.com>
+siteleaf.net
+
+// Skyhat : http://www.skyhat.io
+// Submitted by Shante Adam <shante@skyhat.io>
+bounty-full.com
+alpha.bounty-full.com
+beta.bounty-full.com
+
+// Smallregistry by Promopixel SARL: https://www.smallregistry.net
+// Former AFNIC's SLDs
+// Submitted by Jérôme Lipowicz <support@promopixel.com>
+aeroport.fr
+avocat.fr
+chambagri.fr
+chirurgiens-dentistes.fr
+experts-comptables.fr
+medecin.fr
+notaires.fr
+pharmacien.fr
+port.fr
+veterinaire.fr
+
+// Small Technology Foundation : https://small-tech.org
+// Submitted by Aral Balkan <aral@small-tech.org>
+small-web.org
+
+// Smoove.io : https://www.smoove.io/
+// Submitted by Dan Kozak <dan@smoove.io>
+vp4.me
+
+// Snowflake Inc : https://www.snowflake.com/
+// Submitted by Faith Olapade <faith.olapade@snowflake.com>
+snowflake.app
+privatelink.snowflake.app
+streamlit.app
+streamlitapp.com
+
+// Snowplow Analytics : https://snowplowanalytics.com/
+// Submitted by Ian Streeter <ian@snowplowanalytics.com>
+try-snowplow.com
+
+// SourceHut : https://sourcehut.org
+// Submitted by Drew DeVault <sir@cmpwn.com>
+srht.site
+
+// Stackhero : https://www.stackhero.io
+// Submitted by Adrien Gillon <adrien+public-suffix-list@stackhero.io>
+stackhero-network.com
+
+// STACKIT : https://www.stackit.de/en/
+// Submitted by STACKIT-DNS Team (Simon Stier) <stackit-dns@mail.schwarz>
+runs.onstackit.cloud
+stackit.gg
+stackit.rocks
+stackit.run
+stackit.zone
+
+// Staclar : https://staclar.com
+// Submitted by Q Misell <q@staclar.com>
+musician.io
+// Submitted by Matthias Merkel <matthias.merkel@staclar.com>
+novecore.site
+
+// staticland : https://static.land
+// Submitted by Seth Vincent <sethvincent@gmail.com>
+static.land
+dev.static.land
+sites.static.land
+
+// Storebase : https://www.storebase.io
+// Submitted by Tony Schirmer <tony@storebase.io>
+storebase.store
+
+// Strategic System Consulting (eApps Hosting): https://www.eapps.com/
+// Submitted by Alex Oancea <aoancea@cloudscale365.com>
+vps-host.net
+atl.jelastic.vps-host.net
+njs.jelastic.vps-host.net
+ric.jelastic.vps-host.net
+
+// Sony Interactive Entertainment LLC : https://sie.com/
+// Submitted by David Coles <david.coles@sony.com>
+playstation-cloud.com
+
+// SourceLair PC : https://www.sourcelair.com
+// Submitted by Antonis Kalipetis <akalipetis@sourcelair.com>
+apps.lair.io
+*.stolos.io
+
+// SpaceKit : https://www.spacekit.io/
+// Submitted by Reza Akhavan <spacekit.io@gmail.com>
+spacekit.io
+
+// SpeedPartner GmbH: https://www.speedpartner.de/
+// Submitted by Stefan Neufeind <info@speedpartner.de>
+customer.speedpartner.de
+
+// Spreadshop (sprd.net AG) : https://www.spreadshop.com/
+// Submitted by Martin Breest <security@spreadshop.com>
+myspreadshop.at
+myspreadshop.com.au
+myspreadshop.be
+myspreadshop.ca
+myspreadshop.ch
+myspreadshop.com
+myspreadshop.de
+myspreadshop.dk
+myspreadshop.es
+myspreadshop.fi
+myspreadshop.fr
+myspreadshop.ie
+myspreadshop.it
+myspreadshop.net
+myspreadshop.nl
+myspreadshop.no
+myspreadshop.pl
+myspreadshop.se
+myspreadshop.co.uk
+
+// Standard Library : https://stdlib.com
+// Submitted by Jacob Lee <jacob@stdlib.com>
+api.stdlib.com
+
+// stereosense GmbH : https://www.involve.me
+// Submitted by Florian Burmann <publicsuffix@involve.me>
+feedback.ac
+forms.ac
+assessments.cx
+calculators.cx
+funnels.cx
+paynow.cx
+quizzes.cx
+researched.cx
+tests.cx
+surveys.so
+
+// Storipress : https://storipress.com
+// Submitted by Benno Liu <benno@storipress.com>
+storipress.app
+
+// Storj Labs Inc. : https://storj.io/
+// Submitted by Philip Hutchins <hostmaster@storj.io>
+storj.farm
+
+// Streak : https://streak.com
+// Submitted by Blake Kadatz <eng@streak.com>
+streak-link.com
+streaklinks.com
+streakusercontent.com
+
+// Studenten Net Twente : http://www.snt.utwente.nl/
+// Submitted by Silke Hofstra <syscom@snt.utwente.nl>
+utwente.io
+
+// Student-Run Computing Facility : https://www.srcf.net/
+// Submitted by Edwin Balani <sysadmins@srcf.net>
+soc.srcf.net
+user.srcf.net
+
+// Sub 6 Limited: http://www.sub6.com
+// Submitted by Dan Miller <dm@sub6.com>
+temp-dns.com
+
+// Supabase : https://supabase.io
+// Submitted by Inian Parameshwaran <security@supabase.io>
+supabase.co
+supabase.in
+supabase.net
+su.paba.se
+
+// Symfony, SAS : https://symfony.com/
+// Submitted by Fabien Potencier <fabien@symfony.com>
+*.s5y.io
+*.sensiosite.cloud
+
+// Syncloud : https://syncloud.org
+// Submitted by Boris Rybalkin <syncloud@syncloud.it>
+syncloud.it
+
+// Synology, Inc. : https://www.synology.com/
+// Submitted by Rony Weng <ronyweng@synology.com>
+dscloud.biz
+direct.quickconnect.cn
+dsmynas.com
+familyds.com
+diskstation.me
+dscloud.me
+i234.me
+myds.me
+synology.me
+dscloud.mobi
+dsmynas.net
+familyds.net
+dsmynas.org
+familyds.org
+vpnplus.to
+direct.quickconnect.to
+
+// Tabit Technologies Ltd. : https://tabit.cloud/
+// Submitted by Oren Agiv <oren@tabit.cloud>
+tabitorder.co.il
+mytabit.co.il
+mytabit.com
+
+// TAIFUN Software AG : http://taifun-software.de
+// Submitted by Bjoern Henke <dev-server@taifun-software.de>
+taifun-dns.de
+
+// Tailscale Inc. : https://www.tailscale.com
+// Submitted by David Anderson <danderson@tailscale.com>
+beta.tailscale.net
+ts.net
+*.c.ts.net
+
+// TASK geographical domains (www.task.gda.pl/uslugi/dns)
+gda.pl
+gdansk.pl
+gdynia.pl
+med.pl
+sopot.pl
+
+// team.blue https://team.blue
+// Submitted by Cedric Dubois <cedric.dubois@team.blue>
+site.tb-hosting.com
+
+// Teckids e.V. : https://www.teckids.org
+// Submitted by Dominik George <dominik.george@teckids.org>
+edugit.io
+s3.teckids.org
+
+// Telebit : https://telebit.cloud
+// Submitted by AJ ONeal <aj@telebit.cloud>
+telebit.app
+telebit.io
+*.telebit.xyz
+
+// Thingdust AG : https://thingdust.com/
+// Submitted by Adrian Imboden <adi@thingdust.com>
+*.firenet.ch
+*.svc.firenet.ch
+reservd.com
+thingdustdata.com
+cust.dev.thingdust.io
+cust.disrec.thingdust.io
+cust.prod.thingdust.io
+cust.testing.thingdust.io
+reservd.dev.thingdust.io
+reservd.disrec.thingdust.io
+reservd.testing.thingdust.io
+
+// ticket i/O GmbH : https://ticket.io
+// Submitted by Christian Franke <it@ticket.io>
+tickets.io
+
+// Tlon.io : https://tlon.io
+// Submitted by Mark Staarink <mark@tlon.io>
+arvo.network
+azimuth.network
+tlon.network
+
+// Tor Project, Inc. : https://torproject.org
+// Submitted by Antoine Beaupré <anarcat@torproject.org
+torproject.net
+pages.torproject.net
+
+// TownNews.com : http://www.townnews.com
+// Submitted by Dustin Ward <dward@townnews.com>
+bloxcms.com
+townnews-staging.com
+
+// TrafficPlex GmbH : https://www.trafficplex.de/
+// Submitted by Phillipp Röll <phillipp.roell@trafficplex.de>
+12hp.at
+2ix.at
+4lima.at
+lima-city.at
+12hp.ch
+2ix.ch
+4lima.ch
+lima-city.ch
+trafficplex.cloud
+de.cool
+12hp.de
+2ix.de
+4lima.de
+lima-city.de
+1337.pictures
+clan.rip
+lima-city.rocks
+webspace.rocks
+lima.zone
+
+// TransIP : https://www.transip.nl
+// Submitted by Rory Breuk <rbreuk@transip.nl>
+*.transurl.be
+*.transurl.eu
+*.transurl.nl
+
+// TransIP: https://www.transip.nl
+// Submitted by Cedric Dubois <cedric.dubois@team.blue>
+site.transip.me
+
+// TuxFamily : http://tuxfamily.org
+// Submitted by TuxFamily administrators <adm@staff.tuxfamily.org>
+tuxfamily.org
+
+// TwoDNS : https://www.twodns.de/
+// Submitted by TwoDNS-Support <support@two-dns.de>
+dd-dns.de
+diskstation.eu
+diskstation.org
+dray-dns.de
+draydns.de
+dyn-vpn.de
+dynvpn.de
+mein-vigor.de
+my-vigor.de
+my-wan.de
+syno-ds.de
+synology-diskstation.de
+synology-ds.de
+
+// Typedream : https://typedream.com
+// Submitted by Putri Karunia <putri@typedream.com>
+typedream.app
+
+// Typeform : https://www.typeform.com
+// Submitted by Sergi Ferriz <sergi.ferriz@typeform.com>
+pro.typeform.com
+
+// Uberspace : https://uberspace.de
+// Submitted by Moritz Werner <mwerner@jonaspasche.com>
+uber.space
+*.uberspace.de
+
+// UDR Limited : http://www.udr.hk.com
+// Submitted by registry <hostmaster@udr.hk.com>
+hk.com
+hk.org
+ltd.hk
+inc.hk
+
+// UK Intis Telecom LTD : https://it.com
+// Submitted by ITComdomains <to@it.com>
+it.com
+
+// UNIVERSAL DOMAIN REGISTRY : https://www.udr.org.yt/
+// see also: whois -h whois.udr.org.yt help
+// Submitted by Atanunu Igbunuroghene <publicsuffixlist@udr.org.yt>
+name.pm
+sch.tf
+biz.wf
+sch.wf
+org.yt
+
+// United Gameserver GmbH : https://united-gameserver.de
+// Submitted by Stefan Schwarz <sysadm@united-gameserver.de>
+virtualuser.de
+virtual-user.de
+
+// Upli : https://upli.io
+// Submitted by Lenny Bakkalian <lenny.bakkalian@gmail.com>
+upli.io
+
+// urown.net : https://urown.net
+// Submitted by Hostmaster <hostmaster@urown.net>
+urown.cloud
+dnsupdate.info
+
+// .US
+// Submitted by Ed Moore <Ed.Moore@lib.de.us>
+lib.de.us
+
+// VeryPositive SIA : http://very.lv
+// Submitted by Danko Aleksejevs <danko@very.lv>
+2038.io
+
+// Vercel, Inc : https://vercel.com/
+// Submitted by Connor Davis <security@vercel.com>
+vercel.app
+vercel.dev
+now.sh
+
+// Viprinet Europe GmbH : http://www.viprinet.com
+// Submitted by Simon Kissel <hostmaster@viprinet.com>
+router.management
+
+// Virtual-Info : https://www.virtual-info.info/
+// Submitted by Adnan RIHAN <hostmaster@v-info.info>
+v-info.info
+
+// Voorloper.com: https://voorloper.com
+// Submitted by Nathan van Bakel <info@voorloper.com>
+voorloper.cloud
+
+// Voxel.sh DNS : https://voxel.sh/dns/
+// Submitted by Mia Rehlinger <dns@voxel.sh>
+neko.am
+nyaa.am
+be.ax
+cat.ax
+es.ax
+eu.ax
+gg.ax
+mc.ax
+us.ax
+xy.ax
+nl.ci
+xx.gl
+app.gp
+blog.gt
+de.gt
+to.gt
+be.gy
+cc.hn
+io.kg
+jp.kg
+tv.kg
+uk.kg
+us.kg
+de.ls
+at.md
+de.md
+jp.md
+to.md
+indie.porn
+vxl.sh
+ch.tc
+me.tc
+we.tc
+nyan.to
+at.vg
+blog.vu
+dev.vu
+me.vu
+
+// V.UA Domain Administrator : https://domain.v.ua/
+// Submitted by Serhii Rostilo <sergey@rostilo.kiev.ua>
+v.ua
+
+// Vultr Objects : https://www.vultr.com/products/object-storage/
+// Submitted by Niels Maumenee <storage@vultr.com>
+*.vultrobjects.com
+
+// Waffle Computer Inc., Ltd. : https://docs.waffleinfo.com
+// Submitted by Masayuki Note <masa@blade.wafflecell.com>
+wafflecell.com
+
+// Webflow, Inc. : https://www.webflow.com
+// Submitted by Webflow Security Team <security@webflow.com>
+webflow.io
+webflowtest.io
+
+// WebHare bv: https://www.webhare.com/
+// Submitted by Arnold Hendriks <info@webhare.com>
+*.webhare.dev
+
+// WebHotelier Technologies Ltd: https://www.webhotelier.net/
+// Submitted by Apostolos Tsakpinis <apostolos.tsakpinis@gmail.com>
+reserve-online.net
+reserve-online.com
+bookonline.app
+hotelwithflight.com
+
+// WeDeploy by Liferay, Inc. : https://www.wedeploy.com
+// Submitted by Henrique Vicente <security@wedeploy.com>
+wedeploy.io
+wedeploy.me
+wedeploy.sh
+
+// Western Digital Technologies, Inc : https://www.wdc.com
+// Submitted by Jung Jin <jungseok.jin@wdc.com>
+remotewd.com
+
+// WIARD Enterprises : https://wiardweb.com
+// Submitted by Kidd Hustle <kiddhustle@wiardweb.com>
+pages.wiardweb.com
+
+// Wikimedia Labs : https://wikitech.wikimedia.org
+// Submitted by Arturo Borrero Gonzalez <aborrero@wikimedia.org>
+wmflabs.org
+toolforge.org
+wmcloud.org
+
+// WISP : https://wisp.gg
+// Submitted by Stepan Fedotov <stepan@wisp.gg>
+panel.gg
+daemon.panel.gg
+
+// Wizard Zines : https://wizardzines.com
+// Submitted by Julia Evans <julia@wizardzines.com>
+messwithdns.com
+
+// WoltLab GmbH : https://www.woltlab.com
+// Submitted by Tim Düsterhus <security@woltlab.cloud>
+woltlab-demo.com
+myforum.community
+community-pro.de
+diskussionsbereich.de
+community-pro.net
+meinforum.net
+
+// Woods Valldata : https://www.woodsvalldata.co.uk/
+// Submitted by Chris Whittle <chris.whittle@woodsvalldata.co.uk>
+affinitylottery.org.uk
+raffleentry.org.uk
+weeklylottery.org.uk
+
+// WP Engine : https://wpengine.com/
+// Submitted by Michael Smith <michael.smith@wpengine.com>
+// Submitted by Brandon DuRette <brandon.durette@wpengine.com>
+wpenginepowered.com
+js.wpenginepowered.com
+
+// Wix.com, Inc. : https://www.wix.com
+// Submitted by Shahar Talmi <shahar@wix.com>
+wixsite.com
+editorx.io
+wixstudio.io
+wix.run
+
+// XenonCloud GbR: https://xenoncloud.net
+// Submitted by Julian Uphoff <publicsuffixlist@xenoncloud.net>
+half.host
+
+// XnBay Technology : http://www.xnbay.com/
+// Submitted by XnBay Developer <developer.xncloud@gmail.com>
+xnbay.com
+u2.xnbay.com
+u2-local.xnbay.com
+
+// XS4ALL Internet bv : https://www.xs4all.nl/
+// Submitted by Daniel Mostertman <unixbeheer+publicsuffix@xs4all.net>
+cistron.nl
+demon.nl
+xs4all.space
+
+// Yandex.Cloud LLC: https://cloud.yandex.com
+// Submitted by Alexander Lodin <security+psl@yandex-team.ru>
+yandexcloud.net
+storage.yandexcloud.net
+website.yandexcloud.net
+
+// YesCourse Pty Ltd : https://yescourse.com
+// Submitted by Atul Bhouraskar <atul@yescourse.com>
+official.academy
+
+// Yola : https://www.yola.com/
+// Submitted by Stefano Rivera <stefano@yola.com>
+yolasite.com
+
+// Yombo : https://yombo.net
+// Submitted by Mitch Schwenk <mitch@yombo.net>
+ybo.faith
+yombo.me
+homelink.one
+ybo.party
+ybo.review
+ybo.science
+ybo.trade
+
+// Yunohost : https://yunohost.org
+// Submitted by Valentin Grimaud <security@yunohost.org>
+ynh.fr
+nohost.me
+noho.st
+
+// ZaNiC : http://www.za.net/
+// Submitted by registry <hostmaster@nic.za.net>
+za.net
+za.org
+
+// ZAP-Hosting GmbH & Co. KG : https://zap-hosting.com
+// Submitted by Julian Alker <security@zap-hosting.com>
+zap.cloud
+
+// Zine EOOD : https://zine.bg/
+// Submitted by Martin Angelov <martin@zine.bg>
+bss.design
+
+// Zitcom A/S : https://www.zitcom.dk
+// Submitted by Emil Stahl <esp@zitcom.dk>
+basicserver.io
+virtualserver.io
+enterprisecloud.nu
+
+// ===END PRIVATE DOMAINS===
diff --git a/netwerk/dns/moz.build b/netwerk/dns/moz.build
new file mode 100644
index 0000000000..c926d14707
--- /dev/null
+++ b/netwerk/dns/moz.build
@@ -0,0 +1,122 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+with Files("**"):
+ BUG_COMPONENT = ("Core", "Networking: DNS")
+
+DIRS += ["tests"]
+
+XPIDL_SOURCES += [
+ "nsIDNSAdditionalInfo.idl",
+ "nsIDNSByTypeRecord.idl",
+ "nsIDNSListener.idl",
+ "nsIDNSRecord.idl",
+ "nsIDNSService.idl",
+ "nsIEffectiveTLDService.idl",
+ "nsIIDNService.idl",
+ "nsINativeDNSResolverOverride.idl",
+ "nsITRRSkipReason.idl",
+ "nsPIDNSService.idl",
+]
+
+XPIDL_MODULE = "necko_dns"
+
+EXTRA_JS_MODULES["netwerk-dns"] += [
+ "PublicSuffixList.sys.mjs",
+]
+
+XPCSHELL_TESTS_MANIFESTS += ["tests/unit/xpcshell.toml"]
+
+EXPORTS += [
+ "nsEffectiveTLDService.h",
+]
+
+EXPORTS.mozilla.net += [
+ "ChildDNSService.h",
+ "DNS.h",
+ "DNSByTypeRecord.h",
+ "DNSListenerProxy.h",
+ "DNSPacket.h",
+ "DNSRequestBase.h",
+ "DNSRequestChild.h",
+ "DNSRequestParent.h",
+ "DNSServiceBase.h",
+ "HTTPSSVC.h",
+ "IDNBlocklistUtils.h",
+ "NativeDNSResolverOverrideChild.h",
+ "NativeDNSResolverOverrideParent.h",
+ "TRRService.h",
+ "TRRServiceBase.h",
+ "TRRServiceChild.h",
+ "TRRServiceParent.h",
+]
+
+SOURCES += [
+ "GetAddrInfo.cpp", # Undefines UNICODE
+ "nsEffectiveTLDService.cpp", # Excluded from UNIFIED_SOURCES due to special build flags.
+]
+
+if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows":
+ SOURCES += ["PlatformDNSWin.cpp"]
+elif CONFIG["OS_TARGET"] == "Linux" or CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
+ SOURCES += ["PlatformDNSUnix.cpp"]
+ OS_LIBS += ["resolv"]
+elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "android":
+ SOURCES += ["PlatformDNSAndroid.cpp"]
+else:
+ DEFINES["MOZ_NO_HTTPS_IMPL"] = 1
+
+UNIFIED_SOURCES += [
+ "ChildDNSService.cpp",
+ "DNS.cpp",
+ "DNSAdditionalInfo.cpp",
+ "DNSListenerProxy.cpp",
+ "DNSPacket.cpp",
+ "DNSRequestChild.cpp",
+ "DNSRequestParent.cpp",
+ "DNSServiceBase.cpp",
+ "DNSUtils.cpp",
+ "HostRecordQueue.cpp",
+ "HTTPSSVC.cpp",
+ "IDNBlocklistUtils.cpp",
+ "NativeDNSResolverOverrideChild.cpp",
+ "NativeDNSResolverOverrideParent.cpp",
+ "nsDNSService2.cpp",
+ "nsHostRecord.cpp",
+ "nsHostResolver.cpp",
+ "nsIDNService.cpp",
+ "punycode.c",
+ "TRR.cpp",
+ "TRRQuery.cpp",
+ "TRRService.cpp",
+ "TRRServiceBase.cpp",
+ "TRRServiceChild.cpp",
+ "TRRServiceParent.cpp",
+]
+
+IPDL_SOURCES = [
+ "PDNSRequest.ipdl",
+ "PDNSRequestParams.ipdlh",
+ "PNativeDNSResolverOverride.ipdl",
+ "PTRRService.ipdl",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"
+
+GeneratedFile(
+ "etld_data.inc", script="prepare_tlds.py", inputs=["effective_tld_names.dat"]
+)
+
+# need to include etld_data.inc
+LOCAL_INCLUDES += [
+ "/netwerk/base",
+ "/netwerk/ipc",
+ "/netwerk/protocol/http",
+]
+
+USE_LIBS += ["icu"]
diff --git a/netwerk/dns/nsDNSService2.cpp b/netwerk/dns/nsDNSService2.cpp
new file mode 100644
index 0000000000..68a76a0c1f
--- /dev/null
+++ b/netwerk/dns/nsDNSService2.cpp
@@ -0,0 +1,1792 @@
+/* -*- 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 "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";
+
+//-----------------------------------------------------------------------------
+
+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->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();
+ }
+}
+
+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);
+ 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())) {
+ mTrrService = nullptr;
+ }
+
+ nsCOMPtr<nsIIDNService> idn = do_GetService(NS_IDNSERVICE_CONTRACTID);
+ mIDN = idn;
+
+ 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,
+ nsIIDNService* aIDN,
+ 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 (!aIDN || IsAscii(aInput)) {
+ aACE = aInput;
+ return NS_OK;
+ }
+
+ if (!(IsUtf8(aInput) && NS_SUCCEEDED(aIDN->ConvertUTF8toACE(aInput, aACE)))) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+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<nsIIDNService> idn;
+ 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;
+ idn = mIDN;
+ localDomain = mLocalDomains.Contains(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, idn, 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;
+ }
+
+ 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;
+ nsCOMPtr<nsIIDNService> idn;
+ bool localDomain = false;
+ {
+ MutexAutoLock lock(mLock);
+
+ if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE)) {
+ return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
+ }
+
+ res = mResolver;
+ idn = mIDN;
+ localDomain = mLocalDomains.Contains(aHostname);
+ }
+ if (!res) {
+ return NS_ERROR_OFFLINE;
+ }
+
+ nsCString hostname;
+ nsresult rv = PreprocessHostname(localDomain, aHostname, idn, 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;
+ nsCOMPtr<nsIIDNService> idn;
+ bool localDomain = false;
+ {
+ MutexAutoLock lock(mLock);
+ res = mResolver;
+ idn = mIDN;
+ localDomain = mLocalDomains.Contains(aHostname);
+ }
+
+ if (mNotifyResolution) {
+ NS_DispatchToMainThread(new NotifyDNSResolution(aHostname));
+ }
+
+ NS_ENSURE_TRUE(res, NS_ERROR_OFFLINE);
+
+ nsCString hostname;
+ nsresult rv = PreprocessHostname(localDomain, aHostname, idn, 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;
+}
+
+NS_IMETHODIMP
+nsDNSService::ReloadParentalControlEnabled() {
+ if (mTrrService) {
+ mTrrService->mParentalControlEnabled =
+ TRRService::GetParentalControlEnabledInternal();
+ }
+ 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_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_IS_OFFLINE:
+ aName = "TRR_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;
+ case TRRSkippedReason::TRR_NET_INTERRUPT:
+ aName = "TRR_NET_INTERRUPT"_ns;
+ break;
+ case TRRSkippedReason::TRR_NET_INADEQ_SEQURITY:
+ aName = "TRR_NET_INADEQ_SEQURITY"_ns;
+ break;
+ case TRRSkippedReason::TRR_NO_ANSWERS:
+ aName = "TRR_NO_ANSWERS"_ns;
+ break;
+ case TRRSkippedReason::TRR_DECODE_FAILED:
+ aName = "TRR_DECODE_FAILED"_ns;
+ break;
+ case TRRSkippedReason::TRR_EXCLUDED:
+ aName = "TRR_EXCLUDED"_ns;
+ break;
+ case TRRSkippedReason::TRR_SERVER_RESPONSE_ERR:
+ aName = "TRR_SERVER_RESPONSE_ERR"_ns;
+ break;
+ case TRRSkippedReason::TRR_RCODE_FAIL:
+ aName = "TRR_RCODE_FAIL"_ns;
+ break;
+ case TRRSkippedReason::TRR_NO_CONNECTIVITY:
+ aName = "TRR_NO_CONNECTIVITY"_ns;
+ break;
+ case TRRSkippedReason::TRR_NXDOMAIN:
+ aName = "TRR_NXDOMAIN"_ns;
+ break;
+ case TRRSkippedReason::TRR_REQ_CANCELLED:
+ aName = "TRR_REQ_CANCELLED"_ns;
+ break;
+ case TRRSkippedReason::ODOH_KEY_NOT_USABLE:
+ aName = "ODOH_KEY_NOT_USABLE"_ns;
+ break;
+ case TRRSkippedReason::ODOH_UPDATE_KEY_FAILED:
+ aName = "ODOH_UPDATE_KEY_FAILED"_ns;
+ break;
+ case TRRSkippedReason::ODOH_KEY_NOT_AVAILABLE:
+ aName = "ODOH_KEY_NOT_AVAILABLE"_ns;
+ break;
+ case TRRSkippedReason::ODOH_ENCRYPTION_FAILED:
+ aName = "ODOH_ENCRYPTION_FAILED"_ns;
+ break;
+ case TRRSkippedReason::ODOH_DECRYPTION_FAILED:
+ aName = "ODOH_DECRYPTION_FAILED"_ns;
+ break;
+ case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_GOOGLE_SAFESEARCH:
+ aName = "TRR_HEURISTIC_TRIPPED_GOOGLE_SAFESEARCH"_ns;
+ break;
+ case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_YOUTUBE_SAFESEARCH:
+ aName = "TRR_HEURISTIC_TRIPPED_YOUTUBE_SAFESEARCH"_ns;
+ break;
+ case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_ZSCALER_CANARY:
+ aName = "TRR_HEURISTIC_TRIPPED_ZSCALER_CANARY"_ns;
+ break;
+ case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_CANARY:
+ aName = "TRR_HEURISTIC_TRIPPED_CANARY"_ns;
+ break;
+ case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_MODIFIED_ROOTS:
+ aName = "TRR_HEURISTIC_TRIPPED_MODIFIED_ROOTS"_ns;
+ break;
+ case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_PARENTAL_CONTROLS:
+ aName = "TRR_HEURISTIC_TRIPPED_PARENTAL_CONTROLS"_ns;
+ break;
+ case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_THIRD_PARTY_ROOTS:
+ aName = "TRR_HEURISTIC_TRIPPED_THIRD_PARTY_ROOTS"_ns;
+ break;
+ case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_ENTERPRISE_POLICY:
+ aName = "TRR_HEURISTIC_TRIPPED_ENTERPRISE_POLICY"_ns;
+ break;
+ case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_VPN:
+ aName = "TRR_HEURISTIC_TRIPPED_VPN"_ns;
+ break;
+ case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_PROXY:
+ aName = "TRR_HEURISTIC_TRIPPED_PROXY"_ns;
+ break;
+ case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_NRPT:
+ aName = "TRR_HEURISTIC_TRIPPED_NRPT"_ns;
+ break;
+ case TRRSkippedReason::TRR_BAD_URL:
+ aName = "TRR_BAD_URL"_ns;
+ break;
+ default:
+ MOZ_ASSERT(false, "Unknown value");
+ }
+
+ return NS_OK;
+}
+} // namespace mozilla::net
diff --git a/netwerk/dns/nsDNSService2.h b/netwerk/dns/nsDNSService2.h
new file mode 100644
index 0000000000..11f22c038e
--- /dev/null
+++ b/netwerk/dns/nsDNSService2.h
@@ -0,0 +1,135 @@
+/* -*- 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/. */
+
+#ifndef nsDNSService2_h__
+#define nsDNSService2_h__
+
+#include "DNSServiceBase.h"
+#include "nsClassHashtable.h"
+#include "nsPIDNSService.h"
+#include "nsIIDNService.h"
+#include "nsIMemoryReporter.h"
+#include "nsIObserver.h"
+#include "nsHostResolver.h"
+#include "nsString.h"
+#include "nsTHashSet.h"
+#include "nsHashKeys.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/Attributes.h"
+#include "TRRService.h"
+
+class nsAuthSSPI;
+
+class DNSServiceWrapper final : public nsPIDNSService {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_FORWARD_NSPIDNSSERVICE(PIDNSService()->)
+ NS_FORWARD_NSIDNSSERVICE(DNSService()->)
+
+ DNSServiceWrapper() = default;
+
+ static already_AddRefed<nsIDNSService> GetSingleton();
+ static void SwitchToBackupDNSService();
+
+ private:
+ ~DNSServiceWrapper() = default;
+ nsIDNSService* DNSService();
+ nsPIDNSService* PIDNSService();
+
+ mozilla::Mutex mLock{"DNSServiceWrapper.mLock"};
+ nsCOMPtr<nsIDNSService> mDNSServiceInUse;
+ nsCOMPtr<nsIDNSService> mBackupDNSService;
+};
+
+class nsDNSService final : public mozilla::net::DNSServiceBase,
+ public nsPIDNSService,
+ public nsIMemoryReporter {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSPIDNSSERVICE
+ NS_DECL_NSIDNSSERVICE
+ NS_DECL_NSIOBSERVER
+ NS_DECL_NSIMEMORYREPORTER
+
+ nsDNSService() = default;
+
+ static already_AddRefed<nsIDNSService> GetXPCOMSingleton();
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
+ bool GetOffline() const;
+
+ protected:
+ friend class nsAuthSSPI;
+ friend class DNSServiceWrapper;
+
+ nsresult DeprecatedSyncResolve(
+ const nsACString& aHostname, nsIDNSService::DNSFlags flags,
+ const mozilla::OriginAttributes& aOriginAttributes,
+ nsIDNSRecord** result);
+
+ private:
+ ~nsDNSService() = default;
+
+ void ReadPrefs(const char* name) override;
+ static already_AddRefed<nsDNSService> GetSingleton();
+
+ uint16_t GetAFForLookup(const nsACString& host,
+ nsIDNSService::DNSFlags flags);
+
+ nsresult PreprocessHostname(bool aLocalDomain, const nsACString& aInput,
+ nsIIDNService* aIDN, nsACString& aACE);
+
+ nsresult AsyncResolveInternal(
+ const nsACString& aHostname, uint16_t type, nsIDNSService::DNSFlags flags,
+ nsIDNSAdditionalInfo* aInfo, nsIDNSListener* aListener,
+ nsIEventTarget* target_,
+ const mozilla::OriginAttributes& aOriginAttributes,
+ nsICancelable** result);
+
+ nsresult CancelAsyncResolveInternal(
+ const nsACString& aHostname, uint16_t aType,
+ nsIDNSService::DNSFlags aFlags, nsIDNSAdditionalInfo* aInfo,
+ nsIDNSListener* aListener, nsresult aReason,
+ const mozilla::OriginAttributes& aOriginAttributes);
+
+ nsresult ResolveInternal(const nsACString& aHostname,
+ nsIDNSService::DNSFlags flags,
+ const mozilla::OriginAttributes& aOriginAttributes,
+ nsIDNSRecord** result);
+
+ // Locks the mutex and returns an addreffed resolver. May return null.
+ already_AddRefed<nsHostResolver> GetResolverLocked();
+
+ RefPtr<nsHostResolver> mResolver;
+ nsCOMPtr<nsIIDNService> mIDN;
+
+ // mLock protects access to mResolver, mLocalDomains, mIPv4OnlyDomains and
+ // mFailedSVCDomainNames
+ mozilla::Mutex mLock MOZ_UNANNOTATED{"nsDNSServer.mLock"};
+
+ // mIPv4OnlyDomains is a comma-separated list of domains for which only
+ // IPv4 DNS lookups are performed. This allows the user to disable IPv6 on
+ // a per-domain basis and work around broken DNS servers. See bug 68796.
+ nsCString mIPv4OnlyDomains;
+ nsCString mForceResolve;
+ bool mBlockDotOnion = false;
+ bool mNotifyResolution = false;
+ bool mOfflineLocalhost = false;
+ bool mForceResolveOn = false;
+ nsTHashSet<nsCString> mLocalDomains;
+ RefPtr<mozilla::net::TRRService> mTrrService;
+
+ uint32_t mResCacheEntries = 0;
+ uint32_t mResCacheExpiration = 0;
+ uint32_t mResCacheGrace = 0;
+ bool mResolverPrefsUpdated = false;
+ nsClassHashtable<nsCStringHashKey, nsTArray<nsCString>> mFailedSVCDomainNames;
+};
+
+already_AddRefed<nsIDNSService> GetOrInitDNSService();
+
+#endif // nsDNSService2_h__
diff --git a/netwerk/dns/nsEffectiveTLDService.cpp b/netwerk/dns/nsEffectiveTLDService.cpp
new file mode 100644
index 0000000000..fafbc296d5
--- /dev/null
+++ b/netwerk/dns/nsEffectiveTLDService.cpp
@@ -0,0 +1,518 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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/. */
+
+// This service reads a file of rules describing TLD-like domain names. For a
+// complete description of the expected file format and parsing rules, see
+// http://wiki.mozilla.org/Gecko:Effective_TLD_Service
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/HashFunctions.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/ResultExtensions.h"
+#include "mozilla/TextUtils.h"
+#include "mozilla/Try.h"
+
+#include "MainThreadUtils.h"
+#include "nsContentUtils.h"
+#include "nsCRT.h"
+#include "nsEffectiveTLDService.h"
+#include "nsIFile.h"
+#include "nsIIDNService.h"
+#include "nsIObserverService.h"
+#include "nsIURI.h"
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "nsServiceManagerUtils.h"
+#include "mozilla/net/DNS.h"
+
+namespace etld_dafsa {
+
+// Generated file that includes kDafsa
+#include "etld_data.inc"
+
+} // namespace etld_dafsa
+
+using namespace mozilla;
+
+NS_IMPL_ISUPPORTS(nsEffectiveTLDService, nsIEffectiveTLDService,
+ nsIMemoryReporter, nsIObserver)
+
+// ----------------------------------------------------------------------
+
+static nsEffectiveTLDService* gService = nullptr;
+
+nsEffectiveTLDService::nsEffectiveTLDService()
+ : mGraphLock("nsEffectiveTLDService::mGraph") {
+ mGraph.emplace(etld_dafsa::kDafsa);
+}
+
+nsresult nsEffectiveTLDService::Init() {
+ MOZ_ASSERT(NS_IsMainThread());
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ obs->AddObserver(this, "public-suffix-list-updated", false);
+
+ if (gService) {
+ return NS_ERROR_ALREADY_INITIALIZED;
+ }
+
+ nsresult rv;
+ mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ gService = this;
+ RegisterWeakMemoryReporter(this);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsEffectiveTLDService::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData) {
+ /**
+ * Signal sent from netwerk/dns/PublicSuffixList.jsm
+ * aSubject is the nsIFile object for dafsa.bin
+ * aData is the absolute path to the dafsa.bin file (not used)
+ */
+ if (aSubject && (nsCRT::strcmp(aTopic, "public-suffix-list-updated") == 0)) {
+ nsCOMPtr<nsIFile> mDafsaBinFile(do_QueryInterface(aSubject));
+ NS_ENSURE_TRUE(mDafsaBinFile, NS_ERROR_ILLEGAL_VALUE);
+
+ AutoWriteLock lock(mGraphLock);
+ // Reset mGraph with kDafsa in case reassigning to mDafsaMap fails
+ mGraph.reset();
+ mGraph.emplace(etld_dafsa::kDafsa);
+
+ mDafsaMap.reset();
+ mMruTable.Clear();
+
+ MOZ_TRY(mDafsaMap.init(mDafsaBinFile));
+
+ size_t size = mDafsaMap.size();
+ const uint8_t* remoteDafsaPtr = mDafsaMap.get<uint8_t>().get();
+
+ auto remoteDafsa = mozilla::Span(remoteDafsaPtr, size);
+
+ mGraph.reset();
+ mGraph.emplace(remoteDafsa);
+ }
+ return NS_OK;
+}
+
+nsEffectiveTLDService::~nsEffectiveTLDService() {
+ UnregisterWeakMemoryReporter(this);
+ if (mIDNService) {
+ // Only clear gService if Init() finished successfully.
+ gService = nullptr;
+ }
+}
+
+// static
+nsEffectiveTLDService* nsEffectiveTLDService::GetInstance() {
+ if (gService) {
+ return gService;
+ }
+ nsCOMPtr<nsIEffectiveTLDService> tldService =
+ do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
+ if (!tldService) {
+ return nullptr;
+ }
+ MOZ_ASSERT(
+ gService,
+ "gService must have been initialized in nsEffectiveTLDService::Init");
+ return gService;
+}
+
+MOZ_DEFINE_MALLOC_SIZE_OF(EffectiveTLDServiceMallocSizeOf)
+
+// The amount of heap memory measured here is tiny. It used to be bigger when
+// nsEffectiveTLDService used a separate hash table instead of binary search.
+// Nonetheless, we keep this code here in anticipation of bug 1083971 which will
+// change ETLDEntries::entries to a heap-allocated array modifiable at runtime.
+NS_IMETHODIMP
+nsEffectiveTLDService::CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) {
+ MOZ_COLLECT_REPORT("explicit/network/effective-TLD-service", KIND_HEAP,
+ UNITS_BYTES,
+ SizeOfIncludingThis(EffectiveTLDServiceMallocSizeOf),
+ "Memory used by the effective TLD service.");
+
+ return NS_OK;
+}
+
+size_t nsEffectiveTLDService::SizeOfIncludingThis(
+ mozilla::MallocSizeOf aMallocSizeOf) {
+ size_t n = aMallocSizeOf(this);
+
+ // Measurement of the following members may be added later if DMD finds it is
+ // worthwhile:
+ // - mIDNService
+
+ return n;
+}
+
+// External function for dealing with URI's correctly.
+// Pulls out the host portion from an nsIURI, and calls through to
+// GetPublicSuffixFromHost().
+NS_IMETHODIMP
+nsEffectiveTLDService::GetPublicSuffix(nsIURI* aURI,
+ nsACString& aPublicSuffix) {
+ NS_ENSURE_ARG_POINTER(aURI);
+
+ nsAutoCString host;
+ nsresult rv = NS_GetInnermostURIHost(aURI, host);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return GetBaseDomainInternal(host, 0, false, aPublicSuffix);
+}
+
+NS_IMETHODIMP
+nsEffectiveTLDService::GetKnownPublicSuffix(nsIURI* aURI,
+ nsACString& aPublicSuffix) {
+ NS_ENSURE_ARG_POINTER(aURI);
+
+ nsAutoCString host;
+ nsresult rv = NS_GetInnermostURIHost(aURI, host);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return GetBaseDomainInternal(host, 0, true, aPublicSuffix);
+}
+
+// External function for dealing with URI's correctly.
+// Pulls out the host portion from an nsIURI, and calls through to
+// GetBaseDomainFromHost().
+NS_IMETHODIMP
+nsEffectiveTLDService::GetBaseDomain(nsIURI* aURI, uint32_t aAdditionalParts,
+ nsACString& aBaseDomain) {
+ NS_ENSURE_ARG_POINTER(aURI);
+ NS_ENSURE_TRUE(((int32_t)aAdditionalParts) >= 0, NS_ERROR_INVALID_ARG);
+
+ nsAutoCString host;
+ nsresult rv = NS_GetInnermostURIHost(aURI, host);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return GetBaseDomainInternal(host, aAdditionalParts + 1, false, aBaseDomain);
+}
+
+// External function for dealing with URIs to get a schemeless site.
+// Calls through to GetBaseDomain(), handling IP addresses and aliases by
+// just returning their serialized host.
+NS_IMETHODIMP
+nsEffectiveTLDService::GetSchemelessSite(nsIURI* aURI, nsACString& aSite) {
+ NS_ENSURE_ARG_POINTER(aURI);
+
+ nsresult rv = GetBaseDomain(aURI, 0, aSite);
+ if (rv == NS_ERROR_HOST_IS_IP_ADDRESS ||
+ rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
+ rv = nsContentUtils::GetHostOrIPv6WithBrackets(aURI, aSite);
+ }
+ return rv;
+}
+
+// External function for dealing with URIs to get site correctly.
+// Calls through to GetSchemelessSite(), and serializes with the scheme and
+// "://" prepended.
+NS_IMETHODIMP
+nsEffectiveTLDService::GetSite(nsIURI* aURI, nsACString& aSite) {
+ NS_ENSURE_ARG_POINTER(aURI);
+
+ nsAutoCString scheme;
+ nsresult rv = aURI->GetScheme(scheme);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString schemeless;
+ rv = GetSchemelessSite(aURI, schemeless);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // aURI (and thus BaseDomain) may be the string '.'. If so, fail.
+ if (schemeless.Length() == 1 && schemeless.Last() == '.') {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // Reject any URIs without a host that aren't file:// URIs.
+ if (schemeless.IsEmpty() && !aURI->SchemeIs("file")) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ aSite.SetCapacity(scheme.Length() + 3 + schemeless.Length());
+ aSite.Append(scheme);
+ aSite.Append("://"_ns);
+ aSite.Append(schemeless);
+
+ return NS_OK;
+}
+
+// External function for dealing with a host string directly: finds the public
+// suffix (e.g. co.uk) for the given hostname. See GetBaseDomainInternal().
+NS_IMETHODIMP
+nsEffectiveTLDService::GetPublicSuffixFromHost(const nsACString& aHostname,
+ nsACString& aPublicSuffix) {
+ // Create a mutable copy of the hostname and normalize it to ACE.
+ // This will fail if the hostname includes invalid characters.
+ nsAutoCString normHostname(aHostname);
+ nsresult rv = NormalizeHostname(normHostname);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return GetBaseDomainInternal(normHostname, 0, false, aPublicSuffix);
+}
+
+NS_IMETHODIMP
+nsEffectiveTLDService::GetKnownPublicSuffixFromHost(const nsACString& aHostname,
+ nsACString& aPublicSuffix) {
+ // Create a mutable copy of the hostname and normalize it to ACE.
+ // This will fail if the hostname includes invalid characters.
+ nsAutoCString normHostname(aHostname);
+ nsresult rv = NormalizeHostname(normHostname);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return GetBaseDomainInternal(normHostname, 0, true, aPublicSuffix);
+}
+
+// External function for dealing with a host string directly: finds the base
+// domain (e.g. www.co.uk) for the given hostname and number of subdomain parts
+// requested. See GetBaseDomainInternal().
+NS_IMETHODIMP
+nsEffectiveTLDService::GetBaseDomainFromHost(const nsACString& aHostname,
+ uint32_t aAdditionalParts,
+ nsACString& aBaseDomain) {
+ NS_ENSURE_TRUE(((int32_t)aAdditionalParts) >= 0, NS_ERROR_INVALID_ARG);
+
+ // Create a mutable copy of the hostname and normalize it to ACE.
+ // This will fail if the hostname includes invalid characters.
+ nsAutoCString normHostname(aHostname);
+ nsresult rv = NormalizeHostname(normHostname);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return GetBaseDomainInternal(normHostname, aAdditionalParts + 1, false,
+ aBaseDomain);
+}
+
+NS_IMETHODIMP
+nsEffectiveTLDService::GetNextSubDomain(const nsACString& aHostname,
+ nsACString& aBaseDomain) {
+ // Create a mutable copy of the hostname and normalize it to ACE.
+ // This will fail if the hostname includes invalid characters.
+ nsAutoCString normHostname(aHostname);
+ nsresult rv = NormalizeHostname(normHostname);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return GetBaseDomainInternal(normHostname, -1, false, aBaseDomain);
+}
+
+// Finds the base domain for a host, with requested number of additional parts.
+// This will fail, generating an error, if the host is an IPv4/IPv6 address,
+// if more subdomain parts are requested than are available, or if the hostname
+// includes characters that are not valid in a URL. Normalization is performed
+// on the host string and the result will be in UTF8.
+nsresult nsEffectiveTLDService::GetBaseDomainInternal(
+ nsCString& aHostname, int32_t aAdditionalParts, bool aOnlyKnownPublicSuffix,
+ nsACString& aBaseDomain) {
+ const int kExceptionRule = 1;
+ const int kWildcardRule = 2;
+
+ if (aHostname.IsEmpty()) {
+ return NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS;
+ }
+
+ // chomp any trailing dot, and keep track of it for later
+ bool trailingDot = aHostname.Last() == '.';
+ if (trailingDot) {
+ aHostname.Truncate(aHostname.Length() - 1);
+ }
+
+ // check the edge cases of the host being '.' or having a second trailing '.',
+ // since subsequent checks won't catch it.
+ if (aHostname.IsEmpty() || aHostname.Last() == '.') {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // Lookup in the cache if this is a normal query. This is restricted to
+ // main thread-only as the cache is not thread-safe.
+ Maybe<TldCache::Entry> entry;
+ if (aAdditionalParts == 1 && NS_IsMainThread()) {
+ auto p = mMruTable.Lookup(aHostname);
+ if (p) {
+ if (NS_FAILED(p.Data().mResult)) {
+ return p.Data().mResult;
+ }
+
+ // There was a match, just return the cached value.
+ aBaseDomain = p.Data().mBaseDomain;
+ if (trailingDot) {
+ aBaseDomain.Append('.');
+ }
+
+ return NS_OK;
+ }
+
+ entry = Some(p);
+ }
+
+ // Check if we're dealing with an IPv4/IPv6 hostname, and return
+ if (mozilla::net::HostIsIPLiteral(aHostname)) {
+ // Update the MRU table if in use.
+ if (entry) {
+ entry->Set(TLDCacheEntry{aHostname, ""_ns, NS_ERROR_HOST_IS_IP_ADDRESS});
+ }
+
+ return NS_ERROR_HOST_IS_IP_ADDRESS;
+ }
+
+ // Walk up the domain tree, most specific to least specific,
+ // looking for matches at each level. Note that a given level may
+ // have multiple attributes (e.g. IsWild() and IsNormal()).
+ const char* prevDomain = nullptr;
+ const char* currDomain = aHostname.get();
+ const char* nextDot = strchr(currDomain, '.');
+ const char* end = currDomain + aHostname.Length();
+ // Default value of *eTLD is currDomain as set in the while loop below
+ const char* eTLD = nullptr;
+ bool hasKnownPublicSuffix = false;
+ while (true) {
+ // sanity check the string we're about to look up: it should not begin
+ // with a '.'; this would mean the hostname began with a '.' or had an
+ // embedded '..' sequence.
+ if (*currDomain == '.') {
+ // Update the MRU table if in use.
+ if (entry) {
+ entry->Set(TLDCacheEntry{aHostname, ""_ns, NS_ERROR_INVALID_ARG});
+ }
+
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ int result;
+ {
+ AutoReadLock lock(mGraphLock);
+ // Perform the lookup.
+ result = mGraph->Lookup(Substring(currDomain, end));
+ }
+ if (result != Dafsa::kKeyNotFound) {
+ hasKnownPublicSuffix = true;
+ if (result == kWildcardRule && prevDomain) {
+ // wildcard rules imply an eTLD one level inferior to the match.
+ eTLD = prevDomain;
+ break;
+ }
+ if (result != kExceptionRule || !nextDot) {
+ // specific match, or we've hit the top domain level
+ eTLD = currDomain;
+ break;
+ }
+ if (result == kExceptionRule) {
+ // exception rules imply an eTLD one level superior to the match.
+ eTLD = nextDot + 1;
+ break;
+ }
+ }
+
+ if (!nextDot) {
+ // we've hit the top domain level; use it by default.
+ eTLD = currDomain;
+ break;
+ }
+
+ prevDomain = currDomain;
+ currDomain = nextDot + 1;
+ nextDot = strchr(currDomain, '.');
+ }
+
+ if (aOnlyKnownPublicSuffix && !hasKnownPublicSuffix) {
+ aBaseDomain.Truncate();
+ return NS_OK;
+ }
+
+ const char *begin, *iter;
+ if (aAdditionalParts < 0) {
+ NS_ASSERTION(aAdditionalParts == -1,
+ "aAdditionalParts can't be negative and different from -1");
+
+ for (iter = aHostname.get(); iter != eTLD && *iter != '.'; iter++) {
+ ;
+ }
+
+ if (iter != eTLD) {
+ iter++;
+ }
+ if (iter != eTLD) {
+ aAdditionalParts = 0;
+ }
+ } else {
+ // count off the number of requested domains.
+ begin = aHostname.get();
+ iter = eTLD;
+
+ while (true) {
+ if (iter == begin) {
+ break;
+ }
+
+ if (*(--iter) == '.' && aAdditionalParts-- == 0) {
+ ++iter;
+ ++aAdditionalParts;
+ break;
+ }
+ }
+ }
+
+ if (aAdditionalParts != 0) {
+ // Update the MRU table if in use.
+ if (entry) {
+ entry->Set(
+ TLDCacheEntry{aHostname, ""_ns, NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS});
+ }
+
+ return NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS;
+ }
+
+ aBaseDomain = Substring(iter, end);
+
+ // Update the MRU table if in use.
+ if (entry) {
+ entry->Set(TLDCacheEntry{aHostname, nsCString(aBaseDomain), NS_OK});
+ }
+
+ // add on the trailing dot, if applicable
+ if (trailingDot) {
+ aBaseDomain.Append('.');
+ }
+
+ return NS_OK;
+}
+
+// Normalizes the given hostname, component by component. ASCII/ACE
+// components are lower-cased, and UTF-8 components are normalized per
+// RFC 3454 and converted to ACE.
+nsresult nsEffectiveTLDService::NormalizeHostname(nsCString& aHostname) {
+ if (!IsAscii(aHostname)) {
+ nsresult rv = mIDNService->ConvertUTF8toACE(aHostname, aHostname);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ ToLowerCase(aHostname);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsEffectiveTLDService::HasRootDomain(const nsACString& aInput,
+ const nsACString& aHost, bool* aResult) {
+ return net::HasRootDomain(aInput, aHost, aResult);
+}
diff --git a/netwerk/dns/nsEffectiveTLDService.h b/netwerk/dns/nsEffectiveTLDService.h
new file mode 100644
index 0000000000..e68156582a
--- /dev/null
+++ b/netwerk/dns/nsEffectiveTLDService.h
@@ -0,0 +1,94 @@
+//* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef EffectiveTLDService_h
+#define EffectiveTLDService_h
+
+#include "nsIEffectiveTLDService.h"
+
+#include "mozilla/AutoMemMap.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Dafsa.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/MruCache.h"
+#include "mozilla/RWLock.h"
+
+#include "nsCOMPtr.h"
+#include "nsHashKeys.h"
+#include "nsIMemoryReporter.h"
+#include "nsIObserver.h"
+#include "nsString.h"
+
+class nsIIDNService;
+
+class nsEffectiveTLDService final : public nsIEffectiveTLDService,
+ public nsIObserver,
+ public nsIMemoryReporter {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIEFFECTIVETLDSERVICE
+ NS_DECL_NSIMEMORYREPORTER
+ NS_DECL_NSIOBSERVER
+
+ nsEffectiveTLDService();
+ nsresult Init();
+
+ static nsEffectiveTLDService* GetInstance();
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
+
+ private:
+ nsresult GetBaseDomainInternal(nsCString& aHostname, int32_t aAdditionalParts,
+ bool aOnlyKnownPublicSuffix,
+ nsACString& aBaseDomain);
+ nsresult NormalizeHostname(nsCString& aHostname);
+ ~nsEffectiveTLDService();
+
+ // Only set in `Init()` which is called during service construction.
+ nsCOMPtr<nsIIDNService> mIDNService;
+
+ // The DAFSA provides a compact encoding of the rather large eTLD list.
+ mozilla::Maybe<mozilla::Dafsa> mGraph MOZ_GUARDED_BY(mGraphLock);
+
+ // Memory map used for a new updated dafsa
+ mozilla::loader::AutoMemMap mDafsaMap MOZ_GUARDED_BY(mGraphLock);
+
+ // Lock for mGraph and mDafsaMap
+ mozilla::RWLock mGraphLock;
+
+ // Note that the cache entries here can record entries that were cached
+ // successfully or unsuccessfully. mResult must be checked before using an
+ // entry. If it's a success error code, the cache entry is valid and can be
+ // used.
+ struct TLDCacheEntry {
+ nsCString mHost;
+ nsCString mBaseDomain;
+ nsresult mResult;
+ };
+
+ // We use a small most recently used cache to compensate for DAFSA lookups
+ // being slightly slower than a binary search on a larger table of strings.
+ //
+ // We first check the cache for a matching result and avoid a DAFSA lookup
+ // if a match is found. Otherwise we lookup the domain in the DAFSA and then
+ // cache the result. During standard browsing the same domains are repeatedly
+ // fed into |GetBaseDomainInternal| so this ends up being an effective
+ // mitigation getting about a 99% hit rate with four tabs open.
+ struct TldCache
+ : public mozilla::MruCache<nsACString, TLDCacheEntry, TldCache> {
+ static mozilla::HashNumber Hash(const nsACString& aKey) {
+ return mozilla::HashString(aKey);
+ }
+ static bool Match(const nsACString& aKey, const TLDCacheEntry& aVal) {
+ return aKey == aVal.mHost;
+ }
+ };
+
+ // NOTE: Only used on the main thread, so not guarded by mGraphLock.
+ TldCache mMruTable;
+};
+
+#endif // EffectiveTLDService_h
diff --git a/netwerk/dns/nsHostRecord.cpp b/netwerk/dns/nsHostRecord.cpp
new file mode 100644
index 0000000000..159abb6e54
--- /dev/null
+++ b/netwerk/dns/nsHostRecord.cpp
@@ -0,0 +1,598 @@
+/* vim:set ts=4 sw=2 sts=2 et cin: */
+/* 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 "nsHostRecord.h"
+#include "TRRQuery.h"
+// Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
+#include "DNSLogging.h"
+#include "mozilla/StaticPrefs_network.h"
+#include "mozilla/Telemetry.h"
+#include "TRRService.h"
+
+//----------------------------------------------------------------------------
+// this macro filters out any flags that are not used when constructing the
+// host key. the significant flags are those that would affect the resulting
+// host record (i.e., the flags that are passed down to PR_GetAddrInfoByName).
+#define RES_KEY_FLAGS(_f) \
+ ((_f) & \
+ (nsHostResolver::RES_CANON_NAME | nsHostResolver::RES_DISABLE_TRR | \
+ nsIDNSService::RESOLVE_TRR_MODE_MASK | nsHostResolver::RES_IP_HINT))
+
+#define IS_ADDR_TYPE(_type) ((_type) == nsIDNSService::RESOLVE_TYPE_DEFAULT)
+#define IS_OTHER_TYPE(_type) ((_type) != nsIDNSService::RESOLVE_TYPE_DEFAULT)
+
+//----------------------------------------------------------------------------
+
+using namespace mozilla;
+using namespace mozilla::net;
+
+nsHostKey::nsHostKey(const nsACString& aHost, const nsACString& aTrrServer,
+ uint16_t aType, nsIDNSService::DNSFlags aFlags,
+ uint16_t aAf, bool aPb, const nsACString& aOriginsuffix)
+ : host(aHost),
+ mTrrServer(aTrrServer),
+ type(aType),
+ flags(aFlags),
+ af(aAf),
+ pb(aPb),
+ originSuffix(aOriginsuffix) {}
+
+bool nsHostKey::operator==(const nsHostKey& other) const {
+ return host == other.host && mTrrServer == other.mTrrServer &&
+ type == other.type &&
+ RES_KEY_FLAGS(flags) == RES_KEY_FLAGS(other.flags) && af == other.af &&
+ originSuffix == other.originSuffix;
+}
+
+PLDHashNumber nsHostKey::Hash() const {
+ return AddToHash(HashString(host.get()), HashString(mTrrServer.get()), type,
+ RES_KEY_FLAGS(flags), af, HashString(originSuffix.get()));
+}
+
+size_t nsHostKey::SizeOfExcludingThis(
+ mozilla::MallocSizeOf mallocSizeOf) const {
+ size_t n = 0;
+ n += host.SizeOfExcludingThisIfUnshared(mallocSizeOf);
+ n += mTrrServer.SizeOfExcludingThisIfUnshared(mallocSizeOf);
+ n += originSuffix.SizeOfExcludingThisIfUnshared(mallocSizeOf);
+ return n;
+}
+
+//----------------------------------------------------------------------------
+// nsHostRecord
+//----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS0(nsHostRecord)
+
+nsHostRecord::nsHostRecord(const nsHostKey& key)
+ : nsHostKey(key), mTRRQuery("nsHostRecord.mTRRQuery") {}
+
+void nsHostRecord::Invalidate() { mDoomed = true; }
+
+void nsHostRecord::Cancel() {
+ RefPtr<TRRQuery> query;
+ {
+ auto lock = mTRRQuery.Lock();
+ query.swap(lock.ref());
+ }
+
+ if (query) {
+ query->Cancel(NS_ERROR_ABORT);
+ }
+}
+
+nsHostRecord::ExpirationStatus nsHostRecord::CheckExpiration(
+ const mozilla::TimeStamp& now) const {
+ if (!mGraceStart.IsNull() && now >= mGraceStart && !mValidEnd.IsNull() &&
+ now < mValidEnd) {
+ return nsHostRecord::EXP_GRACE;
+ }
+ if (!mValidEnd.IsNull() && now < mValidEnd) {
+ return nsHostRecord::EXP_VALID;
+ }
+
+ return nsHostRecord::EXP_EXPIRED;
+}
+
+void nsHostRecord::SetExpiration(const mozilla::TimeStamp& now,
+ unsigned int valid, unsigned int grace) {
+ mValidStart = now;
+ if ((valid + grace) < 60) {
+ grace = 60 - valid;
+ LOG(("SetExpiration: artificially bumped grace to %d\n", grace));
+ }
+ mGraceStart = now + TimeDuration::FromSeconds(valid);
+ mValidEnd = now + TimeDuration::FromSeconds(valid + grace);
+ mTtl = valid;
+}
+
+void nsHostRecord::CopyExpirationTimesAndFlagsFrom(
+ const nsHostRecord* aFromHostRecord) {
+ // This is used to copy information from a cache entry to a record. All
+ // information necessary for HasUsableRecord needs to be copied.
+ mValidStart = aFromHostRecord->mValidStart;
+ mValidEnd = aFromHostRecord->mValidEnd;
+ mGraceStart = aFromHostRecord->mGraceStart;
+ mDoomed = aFromHostRecord->mDoomed;
+ mTtl = uint32_t(aFromHostRecord->mTtl);
+}
+
+bool nsHostRecord::HasUsableResult(const mozilla::TimeStamp& now,
+ nsIDNSService::DNSFlags queryFlags) const {
+ if (mDoomed) {
+ return false;
+ }
+
+ return HasUsableResultInternal(now, queryFlags);
+}
+
+//----------------------------------------------------------------------------
+// AddrHostRecord
+//----------------------------------------------------------------------------
+
+static size_t SizeOfResolveHostCallbackListExcludingHead(
+ const mozilla::LinkedList<RefPtr<nsResolveHostCallback>>& aCallbacks,
+ MallocSizeOf mallocSizeOf) {
+ size_t n = aCallbacks.sizeOfExcludingThis(mallocSizeOf);
+
+ for (const nsResolveHostCallback* t = aCallbacks.getFirst(); t;
+ t = t->getNext()) {
+ n += t->SizeOfIncludingThis(mallocSizeOf);
+ }
+
+ return n;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(AddrHostRecord, nsHostRecord, AddrHostRecord)
+
+AddrHostRecord::AddrHostRecord(const nsHostKey& key) : nsHostRecord(key) {}
+
+AddrHostRecord::~AddrHostRecord() {
+ mCallbacks.clear();
+ Telemetry::Accumulate(Telemetry::DNS_BLACKLIST_COUNT, mUnusableCount);
+}
+
+bool AddrHostRecord::Blocklisted(const NetAddr* aQuery) {
+ addr_info_lock.AssertCurrentThreadOwns();
+ LOG(("Checking unusable list for host [%s], host record [%p].\n", host.get(),
+ this));
+
+ // skip the string conversion for the common case of no blocklist
+ if (!mUnusableItems.Length()) {
+ return false;
+ }
+
+ char buf[kIPv6CStrBufSize];
+ if (!aQuery->ToStringBuffer(buf, sizeof(buf))) {
+ return false;
+ }
+ nsDependentCString strQuery(buf);
+
+ for (uint32_t i = 0; i < mUnusableItems.Length(); i++) {
+ if (mUnusableItems.ElementAt(i).Equals(strQuery)) {
+ LOG(("Address [%s] is blocklisted for host [%s].\n", buf, host.get()));
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void AddrHostRecord::ReportUnusable(const NetAddr* aAddress) {
+ addr_info_lock.AssertCurrentThreadOwns();
+ LOG(
+ ("Adding address to blocklist for host [%s], host record [%p]."
+ "used trr=%d\n",
+ host.get(), this, mTRRSuccess));
+
+ ++mUnusableCount;
+
+ char buf[kIPv6CStrBufSize];
+ if (aAddress->ToStringBuffer(buf, sizeof(buf))) {
+ LOG(
+ ("Successfully adding address [%s] to blocklist for host "
+ "[%s].\n",
+ buf, host.get()));
+ mUnusableItems.AppendElement(nsCString(buf));
+ }
+}
+
+void AddrHostRecord::ResetBlocklist() {
+ addr_info_lock.AssertCurrentThreadOwns();
+ LOG(("Resetting blocklist for host [%s], host record [%p].\n", host.get(),
+ this));
+ mUnusableItems.Clear();
+}
+
+size_t AddrHostRecord::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const {
+ size_t n = mallocSizeOf(this);
+
+ n += nsHostKey::SizeOfExcludingThis(mallocSizeOf);
+ n += SizeOfResolveHostCallbackListExcludingHead(mCallbacks, mallocSizeOf);
+
+ n += addr_info ? addr_info->SizeOfIncludingThis(mallocSizeOf) : 0;
+ n += mallocSizeOf(addr.get());
+
+ n += mUnusableItems.ShallowSizeOfExcludingThis(mallocSizeOf);
+ for (size_t i = 0; i < mUnusableItems.Length(); i++) {
+ n += mUnusableItems[i].SizeOfExcludingThisIfUnshared(mallocSizeOf);
+ }
+ return n;
+}
+
+bool AddrHostRecord::HasUsableResultInternal(
+ const mozilla::TimeStamp& now, nsIDNSService::DNSFlags queryFlags) const {
+ // don't use cached negative results for high priority queries.
+ if (negative && IsHighPriority(queryFlags)) {
+ return false;
+ }
+
+ if (CheckExpiration(now) == EXP_EXPIRED) {
+ return false;
+ }
+
+ if (negative) {
+ return true;
+ }
+
+ return addr_info || addr;
+}
+
+// Returns true if the entry can be removed, or false if it should be left.
+// Sets ResolveAgain true for entries being resolved right now.
+bool AddrHostRecord::RemoveOrRefresh(bool aTrrToo) {
+ // no need to flush TRRed names, they're not resolved "locally"
+ MutexAutoLock lock(addr_info_lock);
+ if (addr_info && !aTrrToo && addr_info->IsTRR()) {
+ return false;
+ }
+ if (LoadNative()) {
+ if (!onQueue()) {
+ // The request has been passed to the OS resolver. The resultant DNS
+ // record should be considered stale and not trusted; set a flag to
+ // ensure it is called again.
+ StoreResolveAgain(true);
+ }
+ // if onQueue is true, the host entry is already added to the cache
+ // but is still pending to get resolved: just leave it in hash.
+ return false;
+ }
+ // Already resolved; not in a pending state; remove from cache
+ return true;
+}
+
+void AddrHostRecord::NotifyRetryingTrr() {
+ MOZ_ASSERT(mFirstTRRSkippedReason ==
+ mozilla::net::TRRSkippedReason::TRR_UNSET);
+
+ // Save the skip reason of our first attempt for recording telemetry later.
+ mFirstTRRSkippedReason = mTRRSkippedReason;
+ mTRRSkippedReason = mozilla::net::TRRSkippedReason::TRR_UNSET;
+}
+
+void AddrHostRecord::ResolveComplete() {
+ if (LoadNativeUsed()) {
+ if (mNativeSuccess) {
+ uint32_t millis = static_cast<uint32_t>(mNativeDuration.ToMilliseconds());
+ Telemetry::Accumulate(Telemetry::DNS_NATIVE_LOOKUP_TIME, millis);
+ }
+ AccumulateCategoricalKeyed(
+ TRRService::ProviderKey(),
+ mNativeSuccess ? Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::osOK
+ : Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::osFail);
+ }
+
+ if (mResolverType == DNSResolverType::TRR) {
+ if (mTRRSuccess) {
+ MOZ_DIAGNOSTIC_ASSERT(mTRRSkippedReason ==
+ mozilla::net::TRRSkippedReason::TRR_OK);
+ uint32_t millis = static_cast<uint32_t>(mTrrDuration.ToMilliseconds());
+ Telemetry::Accumulate(Telemetry::DNS_TRR_LOOKUP_TIME3,
+ TRRService::ProviderKey(), millis);
+ }
+ AccumulateCategoricalKeyed(
+ TRRService::ProviderKey(),
+ mTRRSuccess ? Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::trrOK
+ : Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::trrFail);
+ }
+
+ if (nsHostResolver::Mode() == nsIDNSService::MODE_TRRFIRST ||
+ nsHostResolver::Mode() == nsIDNSService::MODE_TRRONLY) {
+ MOZ_ASSERT(mTRRSkippedReason != mozilla::net::TRRSkippedReason::TRR_UNSET);
+
+ Telemetry::Accumulate(Telemetry::TRR_SKIP_REASON_TRR_FIRST2,
+ TRRService::ProviderKey(),
+ static_cast<uint32_t>(mTRRSkippedReason));
+ if (!mTRRSuccess && LoadNativeUsed()) {
+ Telemetry::Accumulate(
+ mNativeSuccess ? Telemetry::TRR_SKIP_REASON_NATIVE_SUCCESS
+ : Telemetry::TRR_SKIP_REASON_NATIVE_FAILED,
+ TRRService::ProviderKey(), static_cast<uint32_t>(mTRRSkippedReason));
+ }
+
+ if (IsRelevantTRRSkipReason(mTRRSkippedReason)) {
+ Telemetry::Accumulate(Telemetry::TRR_RELEVANT_SKIP_REASON_TRR_FIRST,
+ TRRService::ProviderKey(),
+ static_cast<uint32_t>(mTRRSkippedReason));
+
+ if (!mTRRSuccess && LoadNativeUsed()) {
+ Telemetry::Accumulate(
+ mNativeSuccess ? Telemetry::TRR_RELEVANT_SKIP_REASON_NATIVE_SUCCESS
+ : Telemetry::TRR_RELEVANT_SKIP_REASON_NATIVE_FAILED,
+ TRRService::ProviderKey(),
+ static_cast<uint32_t>(mTRRSkippedReason));
+ }
+ }
+
+ if (StaticPrefs::network_trr_retry_on_recoverable_errors() &&
+ nsHostResolver::Mode() == nsIDNSService::MODE_TRRFIRST) {
+ nsAutoCString telemetryKey(TRRService::ProviderKey());
+
+ if (mFirstTRRSkippedReason != mozilla::net::TRRSkippedReason::TRR_UNSET) {
+ telemetryKey.AppendLiteral("|");
+ telemetryKey.AppendInt(static_cast<uint32_t>(mFirstTRRSkippedReason));
+
+ Telemetry::Accumulate(mTRRSuccess
+ ? Telemetry::TRR_SKIP_REASON_RETRY_SUCCESS
+ : Telemetry::TRR_SKIP_REASON_RETRY_FAILED,
+ TRRService::ProviderKey(),
+ static_cast<uint32_t>(mFirstTRRSkippedReason));
+ }
+
+ Telemetry::Accumulate(Telemetry::TRR_SKIP_REASON_STRICT_MODE,
+ telemetryKey,
+ static_cast<uint32_t>(mTRRSkippedReason));
+
+ if (mTRRSuccess) {
+ Telemetry::Accumulate(Telemetry::TRR_ATTEMPT_COUNT,
+ TRRService::ProviderKey(), mTrrAttempts);
+ }
+ }
+ }
+
+ if (mEffectiveTRRMode == nsIRequest::TRR_FIRST_MODE) {
+ if (flags & nsIDNSService::RESOLVE_DISABLE_TRR) {
+ // TRR is disabled on request, which is a next-level back-off method.
+ Telemetry::Accumulate(Telemetry::DNS_TRR_DISABLED3,
+ TRRService::ProviderKey(), mNativeSuccess);
+ } else {
+ if (mTRRSuccess) {
+ AccumulateCategoricalKeyed(TRRService::ProviderKey(),
+ Telemetry::LABELS_DNS_TRR_FIRST4::TRR);
+ } else if (mNativeSuccess) {
+ if (mResolverType == DNSResolverType::TRR) {
+ AccumulateCategoricalKeyed(
+ TRRService::ProviderKey(),
+ Telemetry::LABELS_DNS_TRR_FIRST4::NativeAfterTRR);
+ } else {
+ AccumulateCategoricalKeyed(TRRService::ProviderKey(),
+ Telemetry::LABELS_DNS_TRR_FIRST4::Native);
+ }
+ } else {
+ AccumulateCategoricalKeyed(
+ TRRService::ProviderKey(),
+ Telemetry::LABELS_DNS_TRR_FIRST4::BothFailed);
+ }
+ }
+ }
+
+ switch (mEffectiveTRRMode) {
+ case nsIRequest::TRR_DISABLED_MODE:
+ AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::nativeOnly);
+ break;
+ case nsIRequest::TRR_FIRST_MODE:
+ AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::trrFirst);
+ break;
+ case nsIRequest::TRR_ONLY_MODE:
+ AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::trrOnly);
+ break;
+ case nsIRequest::TRR_DEFAULT_MODE:
+ MOZ_ASSERT_UNREACHABLE("We should not have a default value here");
+ break;
+ }
+
+ if (mResolverType == DNSResolverType::TRR && !mTRRSuccess && mNativeSuccess &&
+ !LoadGetTtl() && TRRService::Get()) {
+ TRRService::Get()->AddToBlocklist(nsCString(host), originSuffix, pb, true);
+ }
+}
+
+AddrHostRecord::DnsPriority AddrHostRecord::GetPriority(
+ nsIDNSService::DNSFlags aFlags) {
+ if (IsHighPriority(aFlags)) {
+ return AddrHostRecord::DNS_PRIORITY_HIGH;
+ }
+ if (IsMediumPriority(aFlags)) {
+ return AddrHostRecord::DNS_PRIORITY_MEDIUM;
+ }
+
+ return AddrHostRecord::DNS_PRIORITY_LOW;
+}
+
+nsresult AddrHostRecord::GetTtl(uint32_t* aResult) {
+ NS_ENSURE_ARG(aResult);
+ *aResult = mTtl;
+ return NS_OK;
+}
+
+//----------------------------------------------------------------------------
+// TypeHostRecord
+//----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS_INHERITED(TypeHostRecord, nsHostRecord, TypeHostRecord,
+ nsIDNSTXTRecord, nsIDNSHTTPSSVCRecord)
+
+TypeHostRecord::TypeHostRecord(const nsHostKey& key)
+ : nsHostRecord(key), DNSHTTPSSVCRecordBase(key.host) {}
+
+TypeHostRecord::~TypeHostRecord() { mCallbacks.clear(); }
+
+bool TypeHostRecord::HasUsableResultInternal(
+ const mozilla::TimeStamp& now, nsIDNSService::DNSFlags queryFlags) const {
+ if (CheckExpiration(now) == EXP_EXPIRED) {
+ return false;
+ }
+
+ if (negative) {
+ return true;
+ }
+
+ return !mResults.is<Nothing>();
+}
+
+bool TypeHostRecord::RefreshForNegativeResponse() const { return false; }
+
+NS_IMETHODIMP TypeHostRecord::GetRecords(CopyableTArray<nsCString>& aRecords) {
+ // deep copy
+ MutexAutoLock lock(mResultsLock);
+
+ if (!mResults.is<TypeRecordTxt>()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ aRecords = mResults.as<CopyableTArray<nsCString>>();
+ return NS_OK;
+}
+
+NS_IMETHODIMP TypeHostRecord::GetRecordsAsOneString(nsACString& aRecords) {
+ // deep copy
+ MutexAutoLock lock(mResultsLock);
+
+ if (!mResults.is<TypeRecordTxt>()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ auto& results = mResults.as<CopyableTArray<nsCString>>();
+ for (uint32_t i = 0; i < results.Length(); i++) {
+ aRecords.Append(results[i]);
+ }
+ return NS_OK;
+}
+
+size_t TypeHostRecord::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const {
+ size_t n = mallocSizeOf(this);
+
+ n += nsHostKey::SizeOfExcludingThis(mallocSizeOf);
+ n += SizeOfResolveHostCallbackListExcludingHead(mCallbacks, mallocSizeOf);
+
+ return n;
+}
+
+uint32_t TypeHostRecord::GetType() {
+ MutexAutoLock lock(mResultsLock);
+
+ return mResults.match(
+ [](TypeRecordEmpty&) {
+ MOZ_ASSERT(false, "This should never be the case");
+ return nsIDNSService::RESOLVE_TYPE_DEFAULT;
+ },
+ [](TypeRecordTxt&) { return nsIDNSService::RESOLVE_TYPE_TXT; },
+ [](TypeRecordHTTPSSVC&) { return nsIDNSService::RESOLVE_TYPE_HTTPSSVC; });
+}
+
+TypeRecordResultType TypeHostRecord::GetResults() {
+ MutexAutoLock lock(mResultsLock);
+ return mResults;
+}
+
+NS_IMETHODIMP
+TypeHostRecord::GetRecords(nsTArray<RefPtr<nsISVCBRecord>>& aRecords) {
+ MutexAutoLock lock(mResultsLock);
+ if (!mResults.is<TypeRecordHTTPSSVC>()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ auto& results = mResults.as<TypeRecordHTTPSSVC>();
+
+ for (const SVCB& r : results) {
+ RefPtr<nsISVCBRecord> rec = new mozilla::net::SVCBRecord(r);
+ aRecords.AppendElement(rec);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TypeHostRecord::GetServiceModeRecord(bool aNoHttp2, bool aNoHttp3,
+ nsISVCBRecord** aRecord) {
+ MutexAutoLock lock(mResultsLock);
+ if (!mResults.is<TypeRecordHTTPSSVC>()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ auto& results = mResults.as<TypeRecordHTTPSSVC>();
+ nsCOMPtr<nsISVCBRecord> result = GetServiceModeRecordInternal(
+ aNoHttp2, aNoHttp3, results, mAllRecordsExcluded);
+ if (!result) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ result.forget(aRecord);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TypeHostRecord::GetAllRecordsWithEchConfig(
+ bool aNoHttp2, bool aNoHttp3, bool* aAllRecordsHaveEchConfig,
+ bool* aAllRecordsInH3ExcludedList,
+ nsTArray<RefPtr<nsISVCBRecord>>& aResult) {
+ MutexAutoLock lock(mResultsLock);
+ if (!mResults.is<TypeRecordHTTPSSVC>()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ auto& records = mResults.as<TypeRecordHTTPSSVC>();
+ GetAllRecordsWithEchConfigInternal(aNoHttp2, aNoHttp3, records,
+ aAllRecordsHaveEchConfig,
+ aAllRecordsInH3ExcludedList, aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TypeHostRecord::GetHasIPAddresses(bool* aResult) {
+ NS_ENSURE_ARG(aResult);
+ MutexAutoLock lock(mResultsLock);
+
+ if (!mResults.is<TypeRecordHTTPSSVC>()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ auto& results = mResults.as<TypeRecordHTTPSSVC>();
+ *aResult = HasIPAddressesInternal(results);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TypeHostRecord::GetAllRecordsExcluded(bool* aResult) {
+ NS_ENSURE_ARG(aResult);
+ MutexAutoLock lock(mResultsLock);
+
+ if (!mResults.is<TypeRecordHTTPSSVC>()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ *aResult = mAllRecordsExcluded;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TypeHostRecord::GetTtl(uint32_t* aResult) {
+ NS_ENSURE_ARG(aResult);
+ *aResult = mTtl;
+ return NS_OK;
+}
+
+void TypeHostRecord::ResolveComplete() {
+ if (IsRelevantTRRSkipReason(mTRRSkippedReason)) {
+ Telemetry::Accumulate(
+ Telemetry::TRR_RELEVANT_SKIP_REASON_TRR_FIRST_TYPE_REC,
+ TRRService::ProviderKey(), static_cast<uint32_t>(mTRRSkippedReason));
+ }
+
+ uint32_t millis = static_cast<uint32_t>(mTrrDuration.ToMilliseconds());
+ if (mTRRSuccess) {
+ Telemetry::Accumulate(Telemetry::DNS_BY_TYPE_SUCCEEDED_LOOKUP_TIME, millis);
+ } else {
+ Telemetry::Accumulate(Telemetry::DNS_BY_TYPE_FAILED_LOOKUP_TIME, millis);
+ }
+}
diff --git a/netwerk/dns/nsHostRecord.h b/netwerk/dns/nsHostRecord.h
new file mode 100644
index 0000000000..c3a73907ae
--- /dev/null
+++ b/netwerk/dns/nsHostRecord.h
@@ -0,0 +1,416 @@
+/* vim:set ts=4 sw=2 sts=2 et cin: */
+/* 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/. */
+
+#ifndef nsHostRecord_h__
+#define nsHostRecord_h__
+
+#include "mozilla/AtomicBitfields.h"
+#include "mozilla/DataMutex.h"
+#include "mozilla/LinkedList.h"
+#include "mozilla/net/HTTPSSVC.h"
+#include "nsIDNSService.h"
+#include "nsIDNSByTypeRecord.h"
+#include "PLDHashTable.h"
+#include "nsITRRSkipReason.h"
+
+class nsHostRecord;
+class nsHostResolver;
+
+namespace mozilla {
+namespace net {
+class HostRecordQueue;
+class TRR;
+class TRRQuery;
+} // namespace net
+} // namespace mozilla
+
+/**
+ * This class is used to notify listeners when a ResolveHost operation is
+ * complete. Classes that derive it must implement threadsafe nsISupports
+ * to be able to use RefPtr with this class.
+ */
+class nsResolveHostCallback
+ : public mozilla::LinkedListElement<RefPtr<nsResolveHostCallback>>,
+ public nsISupports {
+ public:
+ /**
+ * OnResolveHostComplete
+ *
+ * this function is called to complete a host lookup initiated by
+ * nsHostResolver::ResolveHost. it may be invoked recursively from
+ * ResolveHost or on an unspecified background thread.
+ *
+ * NOTE: it is the responsibility of the implementor of this method
+ * to handle the callback in a thread safe manner.
+ *
+ * @param resolver
+ * nsHostResolver object associated with this result
+ * @param record
+ * the host record containing the results of the lookup
+ * @param status
+ * if successful, |record| contains non-null results
+ */
+ virtual void OnResolveHostComplete(nsHostResolver* resolver,
+ nsHostRecord* record, nsresult status) = 0;
+ /**
+ * EqualsAsyncListener
+ *
+ * Determines if the listener argument matches the listener member var.
+ * For subclasses not implementing a member listener, should return false.
+ * For subclasses having a member listener, the function should check if
+ * they are the same. Used for cases where a pointer to an object
+ * implementing nsResolveHostCallback is unknown, but a pointer to
+ * the original listener is known.
+ *
+ * @param aListener
+ * nsIDNSListener object associated with the original request
+ */
+ virtual bool EqualsAsyncListener(nsIDNSListener* aListener) = 0;
+
+ virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const = 0;
+
+ protected:
+ virtual ~nsResolveHostCallback() = default;
+};
+
+struct nsHostKey {
+ const nsCString host;
+ const nsCString mTrrServer;
+ uint16_t type = 0;
+ nsIDNSService::DNSFlags flags = nsIDNSService::RESOLVE_DEFAULT_FLAGS;
+ uint16_t af = 0;
+ bool pb = false;
+ const nsCString originSuffix;
+ explicit nsHostKey(const nsACString& host, const nsACString& aTrrServer,
+ uint16_t type, nsIDNSService::DNSFlags flags, uint16_t af,
+ bool pb, const nsACString& originSuffix);
+ bool operator==(const nsHostKey& other) const;
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+ PLDHashNumber Hash() const;
+};
+
+/**
+ * nsHostRecord - ref counted object type stored in host resolver cache.
+ */
+class nsHostRecord : public mozilla::LinkedListElement<RefPtr<nsHostRecord>>,
+ public nsHostKey,
+ public nsISupports {
+ using TRRSkippedReason = mozilla::net::TRRSkippedReason;
+
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+ return 0;
+ }
+
+ // Returns the TRR mode encoded by the flags
+ nsIRequest::TRRMode TRRMode();
+
+ // Records the first reason that caused TRR to be skipped or to fail.
+ void RecordReason(TRRSkippedReason reason) {
+ if (mTRRSkippedReason == TRRSkippedReason::TRR_UNSET) {
+ mTRRSkippedReason = reason;
+ }
+ }
+
+ enum DnsPriority {
+ DNS_PRIORITY_LOW = nsIDNSService::RESOLVE_PRIORITY_LOW,
+ DNS_PRIORITY_MEDIUM = nsIDNSService::RESOLVE_PRIORITY_MEDIUM,
+ DNS_PRIORITY_HIGH,
+ };
+
+ protected:
+ friend class nsHostResolver;
+ friend class mozilla::net::HostRecordQueue;
+ friend class mozilla::net::TRR;
+ friend class mozilla::net::TRRQuery;
+
+ explicit nsHostRecord(const nsHostKey& key);
+ virtual ~nsHostRecord() = default;
+
+ // Mark hostrecord as not usable
+ void Invalidate();
+
+ enum ExpirationStatus {
+ EXP_VALID,
+ EXP_GRACE,
+ EXP_EXPIRED,
+ };
+
+ ExpirationStatus CheckExpiration(const mozilla::TimeStamp& now) const;
+
+ // Convenience function for setting the timestamps above (mValidStart,
+ // mValidEnd, and mGraceStart). valid and grace are durations in seconds.
+ void SetExpiration(const mozilla::TimeStamp& now, unsigned int valid,
+ unsigned int grace);
+ void CopyExpirationTimesAndFlagsFrom(const nsHostRecord* aFromHostRecord);
+
+ // Checks if the record is usable (not expired and has a value)
+ bool HasUsableResult(const mozilla::TimeStamp& now,
+ nsIDNSService::DNSFlags queryFlags =
+ nsIDNSService::RESOLVE_DEFAULT_FLAGS) const;
+
+ static DnsPriority GetPriority(nsIDNSService::DNSFlags aFlags);
+
+ virtual void Cancel();
+ virtual bool HasUsableResultInternal(
+ const mozilla::TimeStamp& now,
+ nsIDNSService::DNSFlags queryFlags) const = 0;
+ virtual bool RefreshForNegativeResponse() const { return true; }
+
+ mozilla::LinkedList<RefPtr<nsResolveHostCallback>> mCallbacks;
+
+ bool IsAddrRecord() const {
+ return type == nsIDNSService::RESOLVE_TYPE_DEFAULT;
+ }
+
+ virtual void Reset() {
+ mTRRSkippedReason = TRRSkippedReason::TRR_UNSET;
+ mFirstTRRSkippedReason = TRRSkippedReason::TRR_UNSET;
+ mTrrAttempts = 0;
+ mTRRSuccess = false;
+ mNativeSuccess = false;
+ }
+
+ virtual void OnCompleteLookup() {}
+
+ virtual void ResolveComplete() = 0;
+
+ // true if pending and on the queue (not yet given to getaddrinfo())
+ bool onQueue() { return LoadNative() && isInList(); }
+
+ // When the record began being valid. Used mainly for bookkeeping.
+ mozilla::TimeStamp mValidStart;
+
+ // When the record is no longer valid (it's time of expiration)
+ mozilla::TimeStamp mValidEnd;
+
+ // When the record enters its grace period. This must be before mValidEnd.
+ // If a record is in its grace period (and not expired), it will be used
+ // but a request to refresh it will be made.
+ mozilla::TimeStamp mGraceStart;
+
+ mozilla::TimeDuration mTrrDuration;
+
+ mozilla::Atomic<uint32_t, mozilla::Relaxed> mTtl{0};
+
+ // The computed TRR mode that is actually used by the request.
+ // It is set in nsHostResolver::NameLookup and is based on the mode of the
+ // default resolver and the TRRMode encoded in the flags.
+ // The mode into account if the TRR service is disabled,
+ // parental controls are on, domain matches exclusion list, etc.
+ mozilla::Atomic<nsIRequest::TRRMode> mEffectiveTRRMode{
+ nsIRequest::TRR_DEFAULT_MODE};
+
+ mozilla::Atomic<TRRSkippedReason> mTRRSkippedReason{
+ TRRSkippedReason::TRR_UNSET};
+ TRRSkippedReason mFirstTRRSkippedReason = TRRSkippedReason::TRR_UNSET;
+
+ mozilla::DataMutex<RefPtr<mozilla::net::TRRQuery>> mTRRQuery;
+
+ // counter of outstanding resolving calls
+ mozilla::Atomic<int32_t> mResolving{0};
+
+ // Number of times we've attempted TRR. Reset when we refresh.
+ // TRR is attempted at most twice - first attempt and retry.
+ mozilla::Atomic<int32_t> mTrrAttempts{0};
+
+ // True if this record is a cache of a failed lookup. Negative cache
+ // entries are valid just like any other (though never for more than 60
+ // seconds), but a use of that negative entry forces an asynchronous refresh.
+ bool negative = false;
+
+ // Explicitly expired
+ bool mDoomed = false;
+
+ // Whether this is resolved by TRR successfully or not.
+ bool mTRRSuccess = false;
+
+ // Whether this is resolved by native resolver successfully or not.
+ bool mNativeSuccess = false;
+
+ // When the lookups of this record started and their durations
+ mozilla::TimeStamp mNativeStart;
+ mozilla::TimeDuration mNativeDuration;
+
+ // clang-format off
+ MOZ_ATOMIC_BITFIELDS(mAtomicBitfields, 8, (
+ // true if this record is being resolved "natively", which means that
+ // it is either on the pending queue or owned by one of the worker threads.
+ (uint16_t, Native, 1),
+ (uint16_t, NativeUsed, 1),
+ // true if off queue and contributing to mActiveAnyThreadCount
+ (uint16_t, UsingAnyThread, 1),
+ (uint16_t, GetTtl, 1),
+ (uint16_t, ResolveAgain, 1)
+ ))
+ // clang-format on
+};
+
+// b020e996-f6ab-45e5-9bf5-1da71dd0053a
+#define ADDRHOSTRECORD_IID \
+ { \
+ 0xb020e996, 0xf6ab, 0x45e5, { \
+ 0x9b, 0xf5, 0x1d, 0xa7, 0x1d, 0xd0, 0x05, 0x3a \
+ } \
+ }
+
+class AddrHostRecord final : public nsHostRecord {
+ using Mutex = mozilla::Mutex;
+ using DNSResolverType = mozilla::net::DNSResolverType;
+
+ public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(ADDRHOSTRECORD_IID)
+ NS_DECL_ISUPPORTS_INHERITED
+
+ /* a fully resolved host record has either a non-null |addr_info| or |addr|
+ * field. if |addr_info| is null, it implies that the |host| is an IP
+ * address literal. in which case, |addr| contains the parsed address.
+ * otherwise, if |addr_info| is non-null, then it contains one or many
+ * IP addresses corresponding to the given host name. if both |addr_info|
+ * and |addr| are null, then the given host has not yet been fully resolved.
+ * |af| is the address family of the record we are querying for.
+ */
+
+ /* the lock protects |addr_info| and |addr_info_gencnt| because they
+ * are mutable and accessed by the resolver worker thread and the
+ * nsDNSService2 class. |addr| doesn't change after it has been
+ * assigned a value. only the resolver worker thread modifies
+ * nsHostRecord (and only in nsHostResolver::CompleteLookup);
+ * the other threads just read it. therefore the resolver worker
+ * thread doesn't need to lock when reading |addr_info|.
+ */
+ Mutex addr_info_lock MOZ_UNANNOTATED{"AddrHostRecord.addr_info_lock"};
+ // generation count of |addr_info|
+ int addr_info_gencnt = 0;
+ RefPtr<mozilla::net::AddrInfo> addr_info;
+ mozilla::UniquePtr<mozilla::net::NetAddr> addr;
+
+ // hold addr_info_lock when calling the blocklist functions
+ bool Blocklisted(const mozilla::net::NetAddr* query);
+ void ResetBlocklist();
+ void ReportUnusable(const mozilla::net::NetAddr* aAddress);
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const override;
+
+ nsIRequest::TRRMode EffectiveTRRMode() const { return mEffectiveTRRMode; }
+ nsITRRSkipReason::value TrrSkipReason() const { return mTRRSkippedReason; }
+
+ nsresult GetTtl(uint32_t* aResult);
+
+ private:
+ friend class nsHostResolver;
+ friend class mozilla::net::HostRecordQueue;
+ friend class mozilla::net::TRR;
+ friend class mozilla::net::TRRQuery;
+
+ explicit AddrHostRecord(const nsHostKey& key);
+ ~AddrHostRecord();
+
+ // Checks if the record is usable (not expired and has a value)
+ bool HasUsableResultInternal(
+ const mozilla::TimeStamp& now,
+ nsIDNSService::DNSFlags queryFlags) const override;
+
+ bool RemoveOrRefresh(bool aTrrToo); // Mark records currently being resolved
+ // as needed to resolve again.
+
+ // Saves the skip reason of a first-attempt TRR lookup and clears
+ // it to prepare for a retry attempt.
+ void NotifyRetryingTrr();
+
+ static DnsPriority GetPriority(nsIDNSService::DNSFlags aFlags);
+
+ virtual void Reset() override {
+ nsHostRecord::Reset();
+ StoreNativeUsed(false);
+ mResolverType = DNSResolverType::Native;
+ }
+
+ virtual void OnCompleteLookup() override {
+ nsHostRecord::OnCompleteLookup();
+ // This should always be cleared when a request is completed.
+ StoreNative(false);
+ }
+
+ void ResolveComplete() override;
+
+ // TRR was used on this record
+ mozilla::Atomic<DNSResolverType> mResolverType{DNSResolverType::Native};
+
+ // The number of times ReportUnusable() has been called in the record's
+ // lifetime.
+ uint32_t mUnusableCount = 0;
+
+ // a list of addresses associated with this record that have been reported
+ // as unusable. the list is kept as a set of strings to make it independent
+ // of gencnt.
+ nsTArray<nsCString> mUnusableItems;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(AddrHostRecord, ADDRHOSTRECORD_IID)
+
+// 77b786a7-04be-44f2-987c-ab8aa96676e0
+#define TYPEHOSTRECORD_IID \
+ { \
+ 0x77b786a7, 0x04be, 0x44f2, { \
+ 0x98, 0x7c, 0xab, 0x8a, 0xa9, 0x66, 0x76, 0xe0 \
+ } \
+ }
+
+class TypeHostRecord final : public nsHostRecord,
+ public nsIDNSTXTRecord,
+ public nsIDNSHTTPSSVCRecord,
+ public mozilla::net::DNSHTTPSSVCRecordBase {
+ public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(TYPEHOSTRECORD_IID)
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIDNSTXTRECORD
+ NS_DECL_NSIDNSHTTPSSVCRECORD
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const override;
+ uint32_t GetType();
+ mozilla::net::TypeRecordResultType GetResults();
+
+ private:
+ friend class nsHostResolver;
+ friend class mozilla::net::TRR;
+ friend class mozilla::net::TRRQuery;
+
+ explicit TypeHostRecord(const nsHostKey& key);
+ ~TypeHostRecord();
+
+ // Checks if the record is usable (not expired and has a value)
+ bool HasUsableResultInternal(
+ const mozilla::TimeStamp& now,
+ nsIDNSService::DNSFlags queryFlags) const override;
+ bool RefreshForNegativeResponse() const override;
+
+ void ResolveComplete() override;
+
+ mozilla::net::TypeRecordResultType mResults = AsVariant(mozilla::Nothing());
+ mozilla::Mutex mResultsLock MOZ_UNANNOTATED{"TypeHostRecord.mResultsLock"};
+
+ mozilla::Maybe<nsCString> mOriginHost;
+ bool mAllRecordsExcluded = false;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(TypeHostRecord, TYPEHOSTRECORD_IID)
+
+static inline bool IsHighPriority(nsIDNSService::DNSFlags flags) {
+ return !(flags & (nsHostRecord::DNS_PRIORITY_LOW |
+ nsHostRecord::DNS_PRIORITY_MEDIUM));
+}
+
+static inline bool IsMediumPriority(nsIDNSService::DNSFlags flags) {
+ return flags & nsHostRecord::DNS_PRIORITY_MEDIUM;
+}
+
+static inline bool IsLowPriority(nsIDNSService::DNSFlags flags) {
+ return flags & nsHostRecord::DNS_PRIORITY_LOW;
+}
+
+#endif // nsHostRecord_h__
diff --git a/netwerk/dns/nsHostResolver.cpp b/netwerk/dns/nsHostResolver.cpp
new file mode 100644
index 0000000000..ccd6dca57b
--- /dev/null
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -0,0 +1,1965 @@
+/* vim:set ts=4 sw=2 sts=2 et cin: */
+/* 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/. */
+
+#if defined(HAVE_RES_NINIT)
+# include <sys/types.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <arpa/nameser.h>
+# include <resolv.h>
+# define RES_RETRY_ON_FAILURE
+#endif
+
+#include <stdlib.h>
+#include <ctime>
+#include "nsHostResolver.h"
+#include "nsError.h"
+#include "nsISupports.h"
+#include "nsISupportsUtils.h"
+#include "nsIThreadManager.h"
+#include "nsComponentManagerUtils.h"
+#include "nsNetUtil.h"
+#include "nsPrintfCString.h"
+#include "nsXPCOMCIDInternal.h"
+#include "prthread.h"
+#include "prerror.h"
+#include "prtime.h"
+#include "mozilla/Logging.h"
+#include "PLDHashTable.h"
+#include "nsQueryObject.h"
+#include "nsURLHelper.h"
+#include "nsThreadUtils.h"
+#include "nsThreadPool.h"
+#include "GetAddrInfo.h"
+#include "TRR.h"
+#include "TRRQuery.h"
+#include "TRRService.h"
+
+#include "mozilla/Atomics.h"
+#include "mozilla/glean/GleanMetrics.h"
+#include "mozilla/HashFunctions.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/StaticPrefs_network.h"
+// Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
+#include "DNSLogging.h"
+
+#ifdef XP_WIN
+# include "mozilla/WindowsVersion.h"
+#endif // XP_WIN
+
+#ifdef MOZ_WIDGET_ANDROID
+# include "mozilla/jni/Utils.h"
+#endif
+
+#define IS_ADDR_TYPE(_type) ((_type) == nsIDNSService::RESOLVE_TYPE_DEFAULT)
+#define IS_OTHER_TYPE(_type) ((_type) != nsIDNSService::RESOLVE_TYPE_DEFAULT)
+
+using namespace mozilla;
+using namespace mozilla::net;
+
+// None of our implementations expose a TTL for negative responses, so we use a
+// constant always.
+static const unsigned int NEGATIVE_RECORD_LIFETIME = 60;
+
+//----------------------------------------------------------------------------
+
+// Use a persistent thread pool in order to avoid spinning up new threads all
+// the time. In particular, thread creation results in a res_init() call from
+// libc which is quite expensive.
+//
+// The pool dynamically grows between 0 and MaxResolverThreads() in size. New
+// requests go first to an idle thread. If that cannot be found and there are
+// fewer than MaxResolverThreads() currently in the pool a new thread is created
+// for high priority requests. If the new request is at a lower priority a new
+// thread will only be created if there are fewer than
+// MaxResolverThreadsAnyPriority() currently outstanding. If a thread cannot be
+// created or an idle thread located for the request it is queued.
+//
+// When the pool is greater than MaxResolverThreadsAnyPriority() in size a
+// thread will be destroyed after ShortIdleTimeoutSeconds of idle time. Smaller
+// pools use LongIdleTimeoutSeconds for a timeout period.
+
+// for threads 1 -> MaxResolverThreadsAnyPriority()
+#define LongIdleTimeoutSeconds 300
+// for threads MaxResolverThreadsAnyPriority() + 1 -> MaxResolverThreads()
+#define ShortIdleTimeoutSeconds 60
+
+//----------------------------------------------------------------------------
+
+namespace mozilla::net {
+LazyLogModule gHostResolverLog("nsHostResolver");
+} // namespace mozilla::net
+
+//----------------------------------------------------------------------------
+
+#if defined(RES_RETRY_ON_FAILURE)
+
+// this class represents the resolver state for a given thread. if we
+// encounter a lookup failure, then we can invoke the Reset method on an
+// instance of this class to reset the resolver (in case /etc/resolv.conf
+// for example changed). this is mainly an issue on GNU systems since glibc
+// only reads in /etc/resolv.conf once per thread. it may be an issue on
+// other systems as well.
+
+class nsResState {
+ public:
+ nsResState()
+ // initialize mLastReset to the time when this object
+ // is created. this means that a reset will not occur
+ // if a thread is too young. the alternative would be
+ // to initialize this to the beginning of time, so that
+ // the first failure would cause a reset, but since the
+ // thread would have just started up, it likely would
+ // already have current /etc/resolv.conf info.
+ : mLastReset(PR_IntervalNow()) {}
+
+ bool Reset() {
+ // reset no more than once per second
+ if (PR_IntervalToSeconds(PR_IntervalNow() - mLastReset) < 1) {
+ return false;
+ }
+
+ mLastReset = PR_IntervalNow();
+ auto result = res_ninit(&_res);
+
+ LOG(("nsResState::Reset() > 'res_ninit' returned %d", result));
+ return (result == 0);
+ }
+
+ private:
+ PRIntervalTime mLastReset;
+};
+
+#endif // RES_RETRY_ON_FAILURE
+
+//----------------------------------------------------------------------------
+
+static const char kPrefGetTtl[] = "network.dns.get-ttl";
+static const char kPrefNativeIsLocalhost[] = "network.dns.native-is-localhost";
+static const char kPrefThreadIdleTime[] =
+ "network.dns.resolver-thread-extra-idle-time-seconds";
+static bool sGetTtlEnabled = false;
+mozilla::Atomic<bool, mozilla::Relaxed> gNativeIsLocalhost;
+mozilla::Atomic<bool, mozilla::Relaxed> sNativeHTTPSSupported{false};
+
+static void DnsPrefChanged(const char* aPref, void* aSelf) {
+ MOZ_ASSERT(NS_IsMainThread(),
+ "Should be getting pref changed notification on main thread!");
+
+ MOZ_ASSERT(aSelf);
+
+ if (!strcmp(aPref, kPrefGetTtl)) {
+#ifdef DNSQUERY_AVAILABLE
+ sGetTtlEnabled = Preferences::GetBool(kPrefGetTtl);
+#endif
+ } else if (!strcmp(aPref, kPrefNativeIsLocalhost)) {
+ gNativeIsLocalhost = Preferences::GetBool(kPrefNativeIsLocalhost);
+ }
+}
+
+NS_IMPL_ISUPPORTS0(nsHostResolver)
+
+nsHostResolver::nsHostResolver(uint32_t maxCacheEntries,
+ uint32_t defaultCacheEntryLifetime,
+ uint32_t defaultGracePeriod)
+ : mMaxCacheEntries(maxCacheEntries),
+ mDefaultCacheLifetime(defaultCacheEntryLifetime),
+ mDefaultGracePeriod(defaultGracePeriod),
+ mIdleTaskCV(mLock, "nsHostResolver.mIdleTaskCV") {
+ mCreationTime = PR_Now();
+
+ mLongIdleTimeout = TimeDuration::FromSeconds(LongIdleTimeoutSeconds);
+ mShortIdleTimeout = TimeDuration::FromSeconds(ShortIdleTimeoutSeconds);
+}
+
+nsHostResolver::~nsHostResolver() = default;
+
+nsresult nsHostResolver::Init() MOZ_NO_THREAD_SAFETY_ANALYSIS {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (NS_FAILED(GetAddrInfoInit())) {
+ return NS_ERROR_FAILURE;
+ }
+
+ LOG(("nsHostResolver::Init this=%p", this));
+
+ mShutdown = false;
+ mNCS = NetworkConnectivityService::GetSingleton();
+
+ // The preferences probably haven't been loaded from the disk yet, so we
+ // need to register a callback that will set up the experiment once they
+ // are. We also need to explicitly set a value for the props otherwise the
+ // callback won't be called.
+ {
+ DebugOnly<nsresult> rv = Preferences::RegisterCallbackAndCall(
+ &DnsPrefChanged, kPrefGetTtl, this);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+ "Could not register DNS TTL pref callback.");
+ rv = Preferences::RegisterCallbackAndCall(&DnsPrefChanged,
+ kPrefNativeIsLocalhost, this);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+ "Could not register DNS pref callback.");
+ }
+
+#if defined(HAVE_RES_NINIT)
+ // We want to make sure the system is using the correct resolver settings,
+ // so we force it to reload those settings whenever we startup a subsequent
+ // nsHostResolver instance. We assume that there is no reason to do this
+ // for the first nsHostResolver instance since that is usually created
+ // during application startup.
+ static int initCount = 0;
+ if (initCount++ > 0) {
+ auto result = res_ninit(&_res);
+ LOG(("nsHostResolver::Init > 'res_ninit' returned %d", result));
+ }
+#endif
+
+ // We can configure the threadpool to keep threads alive for a while after
+ // the last ThreadFunc task has been executed.
+ int32_t poolTimeoutSecs = Preferences::GetInt(kPrefThreadIdleTime, 60);
+ uint32_t poolTimeoutMs;
+ if (poolTimeoutSecs < 0) {
+ // This means never shut down the idle threads
+ poolTimeoutMs = UINT32_MAX;
+ } else {
+ // We clamp down the idle time between 0 and one hour.
+ poolTimeoutMs =
+ mozilla::clamped<uint32_t>(poolTimeoutSecs * 1000, 0, 3600 * 1000);
+ }
+
+#if defined(XP_WIN)
+ // For some reason, the DNSQuery_A API doesn't work on Windows 10.
+ // It returns a success code, but no records. We only allow
+ // native HTTPS records on Win 11 for now.
+ sNativeHTTPSSupported = StaticPrefs::network_dns_native_https_query_win10() ||
+ mozilla::IsWin11OrLater();
+#elif defined(MOZ_WIDGET_ANDROID)
+ // android_res_nquery only got added in API level 29
+ sNativeHTTPSSupported = jni::GetAPIVersion() >= 29;
+#elif defined(XP_LINUX) || defined(XP_MACOSX)
+ sNativeHTTPSSupported = true;
+#endif
+ LOG(("Native HTTPS records supported=%d", bool(sNativeHTTPSSupported)));
+
+ nsCOMPtr<nsIThreadPool> threadPool = new nsThreadPool();
+ MOZ_ALWAYS_SUCCEEDS(threadPool->SetThreadLimit(MaxResolverThreads()));
+ MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadLimit(MaxResolverThreads()));
+ MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadTimeout(poolTimeoutMs));
+ MOZ_ALWAYS_SUCCEEDS(
+ threadPool->SetThreadStackSize(nsIThreadManager::kThreadPoolStackSize));
+ MOZ_ALWAYS_SUCCEEDS(threadPool->SetName("DNS Resolver"_ns));
+ mResolverThreads = ToRefPtr(std::move(threadPool));
+
+ return NS_OK;
+}
+
+void nsHostResolver::ClearPendingQueue(
+ LinkedList<RefPtr<nsHostRecord>>& aPendingQ) {
+ // loop through pending queue, erroring out pending lookups.
+ if (!aPendingQ.isEmpty()) {
+ for (const RefPtr<nsHostRecord>& rec : aPendingQ) {
+ rec->Cancel();
+ if (rec->IsAddrRecord()) {
+ CompleteLookup(rec, NS_ERROR_ABORT, nullptr, rec->pb, rec->originSuffix,
+ rec->mTRRSkippedReason, nullptr);
+ } else {
+ mozilla::net::TypeRecordResultType empty(Nothing{});
+ CompleteLookupByType(rec, NS_ERROR_ABORT, empty, rec->mTRRSkippedReason,
+ 0, rec->pb);
+ }
+ }
+ }
+}
+
+//
+// FlushCache() is what we call when the network has changed. We must not
+// trust names that were resolved before this change. They may resolve
+// differently now.
+//
+// This function removes all existing resolved host entries from the hash.
+// Names that are in the pending queues can be left there. Entries in the
+// cache that have 'Resolve' set true but not 'OnQueue' are being resolved
+// right now, so we need to mark them to get re-resolved on completion!
+
+void nsHostResolver::FlushCache(bool aTrrToo) {
+ MutexAutoLock lock(mLock);
+
+ mQueue.FlushEvictionQ(mRecordDB, lock);
+
+ // Refresh the cache entries that are resolving RIGHT now, remove the rest.
+ for (auto iter = mRecordDB.Iter(); !iter.Done(); iter.Next()) {
+ nsHostRecord* record = iter.UserData();
+ // Try to remove the record, or mark it for refresh.
+ // By-type records are from TRR. We do not need to flush those entry
+ // when the network has change, because they are not local.
+ if (record->IsAddrRecord()) {
+ RefPtr<AddrHostRecord> addrRec = do_QueryObject(record);
+ MOZ_ASSERT(addrRec);
+ if (addrRec->RemoveOrRefresh(aTrrToo)) {
+ mQueue.MaybeRemoveFromQ(record, lock);
+ LOG(("Removing (%s) Addr record from mRecordDB", record->host.get()));
+ iter.Remove();
+ }
+ } else if (aTrrToo) {
+ // remove by type records
+ LOG(("Removing (%s) type record from mRecordDB", record->host.get()));
+ iter.Remove();
+ }
+ }
+}
+
+void nsHostResolver::Shutdown() {
+ LOG(("Shutting down host resolver.\n"));
+
+ {
+ DebugOnly<nsresult> rv =
+ Preferences::UnregisterCallback(&DnsPrefChanged, kPrefGetTtl, this);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+ "Could not unregister DNS TTL pref callback.");
+ }
+
+ LinkedList<RefPtr<nsHostRecord>> pendingQHigh, pendingQMed, pendingQLow,
+ evictionQ;
+
+ {
+ MutexAutoLock lock(mLock);
+
+ mShutdown = true;
+
+ if (mNumIdleTasks) {
+ mIdleTaskCV.NotifyAll();
+ }
+
+ mQueue.ClearAll(
+ [&](nsHostRecord* aRec) {
+ mLock.AssertCurrentThreadOwns();
+ if (aRec->IsAddrRecord()) {
+ CompleteLookupLocked(aRec, NS_ERROR_ABORT, nullptr, aRec->pb,
+ aRec->originSuffix, aRec->mTRRSkippedReason,
+ nullptr, lock);
+ } else {
+ mozilla::net::TypeRecordResultType empty(Nothing{});
+ CompleteLookupByTypeLocked(aRec, NS_ERROR_ABORT, empty,
+ aRec->mTRRSkippedReason, 0, aRec->pb,
+ lock);
+ }
+ },
+ lock);
+
+ for (const auto& data : mRecordDB.Values()) {
+ data->Cancel();
+ }
+ // empty host database
+ mRecordDB.Clear();
+
+ mNCS = nullptr;
+ }
+
+ // Shutdown the resolver threads, but with a timeout of 2 seconds (prefable).
+ // If the timeout is exceeded, any stuck threads will be leaked.
+ mResolverThreads->ShutdownWithTimeout(
+ StaticPrefs::network_dns_resolver_shutdown_timeout_ms());
+
+ {
+ mozilla::DebugOnly<nsresult> rv = GetAddrInfoShutdown();
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to shutdown GetAddrInfo");
+ }
+}
+
+nsresult nsHostResolver::GetHostRecord(
+ const nsACString& host, const nsACString& aTrrServer, uint16_t type,
+ nsIDNSService::DNSFlags flags, uint16_t af, bool pb,
+ const nsCString& originSuffix, nsHostRecord** result) {
+ MutexAutoLock lock(mLock);
+ nsHostKey key(host, aTrrServer, type, flags, af, pb, originSuffix);
+
+ RefPtr<nsHostRecord> rec =
+ mRecordDB.LookupOrInsertWith(key, [&] { return InitRecord(key); });
+ if (rec->IsAddrRecord()) {
+ RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
+ if (addrRec->addr) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ if (rec->mResolving) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *result = rec.forget().take();
+ return NS_OK;
+}
+
+nsHostRecord* nsHostResolver::InitRecord(const nsHostKey& key) {
+ if (IS_ADDR_TYPE(key.type)) {
+ return new AddrHostRecord(key);
+ }
+ return new TypeHostRecord(key);
+}
+
+already_AddRefed<nsHostRecord> nsHostResolver::InitLoopbackRecord(
+ const nsHostKey& key, nsresult* aRv) {
+ MOZ_ASSERT(aRv);
+ MOZ_ASSERT(IS_ADDR_TYPE(key.type));
+
+ *aRv = NS_ERROR_FAILURE;
+ RefPtr<nsHostRecord> rec = InitRecord(key);
+
+ nsTArray<NetAddr> addresses;
+ NetAddr addr;
+ if (key.af == PR_AF_INET || key.af == PR_AF_UNSPEC) {
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(addr.InitFromString("127.0.0.1"_ns)));
+ addresses.AppendElement(addr);
+ }
+ if (key.af == PR_AF_INET6 || key.af == PR_AF_UNSPEC) {
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(addr.InitFromString("::1"_ns)));
+ addresses.AppendElement(addr);
+ }
+
+ RefPtr<AddrInfo> ai =
+ new AddrInfo(rec->host, DNSResolverType::Native, 0, std::move(addresses));
+
+ RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
+ MutexAutoLock lock(addrRec->addr_info_lock);
+ addrRec->addr_info = ai;
+ addrRec->SetExpiration(TimeStamp::NowLoRes(), mDefaultCacheLifetime,
+ mDefaultGracePeriod);
+ addrRec->negative = false;
+
+ *aRv = NS_OK;
+ return rec.forget();
+}
+
+static bool IsNativeHTTPSEnabled() {
+ if (!StaticPrefs::network_dns_native_https_query()) {
+ return false;
+ }
+ return sNativeHTTPSSupported;
+}
+
+nsresult nsHostResolver::ResolveHost(const nsACString& aHost,
+ const nsACString& aTrrServer,
+ int32_t aPort, uint16_t type,
+ const OriginAttributes& aOriginAttributes,
+ nsIDNSService::DNSFlags flags, uint16_t af,
+ nsResolveHostCallback* aCallback) {
+ nsAutoCString host(aHost);
+ NS_ENSURE_TRUE(!host.IsEmpty(), NS_ERROR_UNEXPECTED);
+
+ nsAutoCString originSuffix;
+ aOriginAttributes.CreateSuffix(originSuffix);
+ LOG(("Resolving host [%s]<%s>%s%s type %d. [this=%p]\n", host.get(),
+ originSuffix.get(), flags & RES_BYPASS_CACHE ? " - bypassing cache" : "",
+ flags & RES_REFRESH_CACHE ? " - refresh cache" : "", type, this));
+
+ // ensure that we are working with a valid hostname before proceeding. see
+ // bug 304904 for details.
+ if (!net_IsValidHostName(host)) {
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ // If TRR is disabled we can return immediately if the native API is disabled
+ if (!IsNativeHTTPSEnabled() && IS_OTHER_TYPE(type) &&
+ Mode() == nsIDNSService::MODE_TRROFF) {
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ // Used to try to parse to an IP address literal.
+ NetAddr tempAddr;
+ if (IS_OTHER_TYPE(type) && (NS_SUCCEEDED(tempAddr.InitFromString(host)))) {
+ // For by-type queries the host cannot be IP literal.
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ RefPtr<nsResolveHostCallback> callback(aCallback);
+ // if result is set inside the lock, then we need to issue the
+ // callback before returning.
+ RefPtr<nsHostRecord> result;
+ nsresult status = NS_OK, rv = NS_OK;
+ {
+ MutexAutoLock lock(mLock);
+
+ if (mShutdown) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ // check to see if there is already an entry for this |host|
+ // in the hash table. if so, then check to see if we can't
+ // just reuse the lookup result. otherwise, if there are
+ // any pending callbacks, then add to pending callbacks queue,
+ // and return. otherwise, add ourselves as first pending
+ // callback, and proceed to do the lookup.
+
+ Maybe<nsCString> originHost;
+ if (StaticPrefs::network_dns_port_prefixed_qname_https_rr() &&
+ type == nsIDNSService::RESOLVE_TYPE_HTTPSSVC && aPort != -1 &&
+ aPort != 443) {
+ originHost = Some(host);
+ host = nsPrintfCString("_%d._https.%s", aPort, host.get());
+ LOG((" Using port prefixed host name [%s]", host.get()));
+ }
+
+ bool excludedFromTRR = false;
+ if (TRRService::Get() && TRRService::Get()->IsExcludedFromTRR(host)) {
+ flags |= nsIDNSService::RESOLVE_DISABLE_TRR;
+ excludedFromTRR = true;
+
+ if (!aTrrServer.IsEmpty()) {
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+ }
+
+ nsHostKey key(host, aTrrServer, type, flags, af,
+ (aOriginAttributes.mPrivateBrowsingId > 0), originSuffix);
+
+ // Check if we have a localhost domain, if so hardcode to loopback
+ if (IS_ADDR_TYPE(type) && IsLoopbackHostname(host)) {
+ nsresult rv;
+ RefPtr<nsHostRecord> result = InitLoopbackRecord(key, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ MOZ_ASSERT(result);
+ aCallback->OnResolveHostComplete(this, result, NS_OK);
+ return NS_OK;
+ }
+
+ RefPtr<nsHostRecord> rec =
+ mRecordDB.LookupOrInsertWith(key, [&] { return InitRecord(key); });
+
+ RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
+ MOZ_ASSERT(rec, "Record should not be null");
+ MOZ_ASSERT((IS_ADDR_TYPE(type) && rec->IsAddrRecord() && addrRec) ||
+ (IS_OTHER_TYPE(type) && !rec->IsAddrRecord()));
+
+ if (IS_OTHER_TYPE(type) && originHost) {
+ RefPtr<TypeHostRecord> typeRec = do_QueryObject(rec);
+ typeRec->mOriginHost = std::move(originHost);
+ }
+
+ if (excludedFromTRR) {
+ rec->RecordReason(TRRSkippedReason::TRR_EXCLUDED);
+ }
+
+ if (!(flags & RES_BYPASS_CACHE) &&
+ rec->HasUsableResult(TimeStamp::NowLoRes(), flags)) {
+ result = FromCache(rec, host, type, status, lock);
+ } else if (addrRec && addrRec->addr) {
+ // if the host name is an IP address literal and has been
+ // parsed, go ahead and use it.
+ LOG((" Using cached address for IP Literal [%s].\n", host.get()));
+ result = FromCachedIPLiteral(rec);
+ } else if (addrRec && NS_SUCCEEDED(tempAddr.InitFromString(host))) {
+ // try parsing the host name as an IP address literal to short
+ // circuit full host resolution. (this is necessary on some
+ // platforms like Win9x. see bug 219376 for more details.)
+ LOG((" Host is IP Literal [%s].\n", host.get()));
+ result = FromIPLiteral(addrRec, tempAddr);
+ } else if (mQueue.PendingCount() >= MAX_NON_PRIORITY_REQUESTS &&
+ !IsHighPriority(flags) && !rec->mResolving) {
+ LOG(
+ (" Lookup queue full: dropping %s priority request for "
+ "host [%s].\n",
+ IsMediumPriority(flags) ? "medium" : "low", host.get()));
+ if (IS_ADDR_TYPE(type)) {
+ Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_OVERFLOW);
+ }
+ // This is a lower priority request and we are swamped, so refuse it.
+ rv = NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
+
+ // Check if the offline flag is set.
+ } else if (flags & RES_OFFLINE) {
+ LOG((" Offline request for host [%s]; ignoring.\n", host.get()));
+ rv = NS_ERROR_OFFLINE;
+
+ // We do not have a valid result till here.
+ // A/AAAA request can check for an alternative entry like AF_UNSPEC.
+ // Otherwise we need to start a new query.
+ } else if (!rec->mResolving) {
+ result =
+ FromUnspecEntry(rec, host, aTrrServer, originSuffix, type, flags, af,
+ aOriginAttributes.mPrivateBrowsingId > 0, status);
+ // If this is a by-type request or if no valid record was found
+ // in the cache or this is an AF_UNSPEC request, then start a
+ // new lookup.
+ if (!result) {
+ LOG((" No usable record in cache for host [%s] type %d.", host.get(),
+ type));
+
+ if (flags & RES_REFRESH_CACHE) {
+ rec->Invalidate();
+ }
+
+ // Add callback to the list of pending callbacks.
+ rec->mCallbacks.insertBack(callback);
+ rec->flags = flags;
+ rv = NameLookup(rec, lock);
+ if (IS_ADDR_TYPE(type)) {
+ Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
+ METHOD_NETWORK_FIRST);
+ }
+ if (NS_FAILED(rv) && callback->isInList()) {
+ callback->remove();
+ } else {
+ LOG(
+ (" DNS lookup for host [%s] blocking "
+ "pending 'getaddrinfo' or trr query: "
+ "callback [%p]",
+ host.get(), callback.get()));
+ }
+ }
+ } else {
+ LOG(
+ (" Host [%s] is being resolved. Appending callback "
+ "[%p].",
+ host.get(), callback.get()));
+
+ rec->mCallbacks.insertBack(callback);
+
+ if (rec && rec->onQueue()) {
+ Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
+ METHOD_NETWORK_SHARED);
+
+ // Consider the case where we are on a pending queue of
+ // lower priority than the request is being made at.
+ // In that case we should upgrade to the higher queue.
+
+ if (IsHighPriority(flags) && !IsHighPriority(rec->flags)) {
+ // Move from (low|med) to high.
+ mQueue.MoveToAnotherPendingQ(rec, flags, lock);
+ rec->flags = flags;
+ ConditionallyCreateThread(rec);
+ } else if (IsMediumPriority(flags) && IsLowPriority(rec->flags)) {
+ // Move from low to med.
+ mQueue.MoveToAnotherPendingQ(rec, flags, lock);
+ rec->flags = flags;
+ mIdleTaskCV.Notify();
+ }
+ }
+ }
+
+ if (result && callback->isInList()) {
+ callback->remove();
+ }
+ } // lock
+
+ if (result) {
+ callback->OnResolveHostComplete(this, result, status);
+ }
+
+ return rv;
+}
+
+already_AddRefed<nsHostRecord> nsHostResolver::FromCache(
+ nsHostRecord* aRec, const nsACString& aHost, uint16_t aType,
+ nsresult& aStatus, const MutexAutoLock& aLock) {
+ LOG((" Using cached record for host [%s].\n",
+ nsPromiseFlatCString(aHost).get()));
+
+ // put reference to host record on stack...
+ RefPtr<nsHostRecord> result = aRec;
+ if (IS_ADDR_TYPE(aType)) {
+ Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
+ }
+
+ // For entries that are in the grace period
+ // or all cached negative entries, use the cache but start a new
+ // lookup in the background
+ ConditionallyRefreshRecord(aRec, aHost, aLock);
+
+ if (aRec->negative) {
+ LOG((" Negative cache entry for host [%s].\n",
+ nsPromiseFlatCString(aHost).get()));
+ if (IS_ADDR_TYPE(aType)) {
+ Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_NEGATIVE_HIT);
+ }
+ aStatus = NS_ERROR_UNKNOWN_HOST;
+ }
+
+ return result.forget();
+}
+
+already_AddRefed<nsHostRecord> nsHostResolver::FromCachedIPLiteral(
+ nsHostRecord* aRec) {
+ Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_LITERAL);
+ RefPtr<nsHostRecord> result = aRec;
+ return result.forget();
+}
+
+already_AddRefed<nsHostRecord> nsHostResolver::FromIPLiteral(
+ AddrHostRecord* aAddrRec, const NetAddr& aAddr) {
+ // ok, just copy the result into the host record, and be
+ // done with it! ;-)
+ aAddrRec->addr = MakeUnique<NetAddr>(aAddr);
+ Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_LITERAL);
+ // put reference to host record on stack...
+ RefPtr<nsHostRecord> result = aAddrRec;
+ return result.forget();
+}
+
+already_AddRefed<nsHostRecord> nsHostResolver::FromUnspecEntry(
+ nsHostRecord* aRec, const nsACString& aHost, const nsACString& aTrrServer,
+ const nsACString& aOriginSuffix, uint16_t aType,
+ nsIDNSService::DNSFlags aFlags, uint16_t af, bool aPb, nsresult& aStatus) {
+ RefPtr<nsHostRecord> result = nullptr;
+ // If this is an IPV4 or IPV6 specific request, check if there is
+ // an AF_UNSPEC entry we can use. Otherwise, hit the resolver...
+ RefPtr<AddrHostRecord> addrRec = do_QueryObject(aRec);
+ if (addrRec && !(aFlags & RES_BYPASS_CACHE) &&
+ ((af == PR_AF_INET) || (af == PR_AF_INET6))) {
+ // Check for an AF_UNSPEC entry.
+
+ const nsHostKey unspecKey(aHost, aTrrServer,
+ nsIDNSService::RESOLVE_TYPE_DEFAULT, aFlags,
+ PR_AF_UNSPEC, aPb, aOriginSuffix);
+ RefPtr<nsHostRecord> unspecRec = mRecordDB.Get(unspecKey);
+
+ TimeStamp now = TimeStamp::NowLoRes();
+ if (unspecRec && unspecRec->HasUsableResult(now, aFlags)) {
+ MOZ_ASSERT(unspecRec->IsAddrRecord());
+
+ RefPtr<AddrHostRecord> addrUnspecRec = do_QueryObject(unspecRec);
+ MOZ_ASSERT(addrUnspecRec);
+ MOZ_ASSERT(addrUnspecRec->addr_info || addrUnspecRec->negative,
+ "Entry should be resolved or negative.");
+
+ LOG((" Trying AF_UNSPEC entry for host [%s] af: %s.\n",
+ PromiseFlatCString(aHost).get(),
+ (af == PR_AF_INET) ? "AF_INET" : "AF_INET6"));
+
+ // We need to lock in case any other thread is reading
+ // addr_info.
+ MutexAutoLock lock(addrRec->addr_info_lock);
+
+ addrRec->addr_info = nullptr;
+ addrRec->addr_info_gencnt++;
+ if (unspecRec->negative) {
+ aRec->negative = unspecRec->negative;
+ aRec->CopyExpirationTimesAndFlagsFrom(unspecRec);
+ } else if (addrUnspecRec->addr_info) {
+ MutexAutoLock lock(addrUnspecRec->addr_info_lock);
+ if (addrUnspecRec->addr_info) {
+ // Search for any valid address in the AF_UNSPEC entry
+ // in the cache (not blocklisted and from the right
+ // family).
+ nsTArray<NetAddr> addresses;
+ for (const auto& addr : addrUnspecRec->addr_info->Addresses()) {
+ if ((af == addr.inet.family) &&
+ !addrUnspecRec->Blocklisted(&addr)) {
+ addresses.AppendElement(addr);
+ }
+ }
+ if (!addresses.IsEmpty()) {
+ addrRec->addr_info = new AddrInfo(
+ addrUnspecRec->addr_info->Hostname(),
+ addrUnspecRec->addr_info->CanonicalHostname(),
+ addrUnspecRec->addr_info->ResolverType(),
+ addrUnspecRec->addr_info->TRRType(), std::move(addresses));
+ addrRec->addr_info_gencnt++;
+ aRec->CopyExpirationTimesAndFlagsFrom(unspecRec);
+ }
+ }
+ }
+ // Now check if we have a new record.
+ if (aRec->HasUsableResult(now, aFlags)) {
+ result = aRec;
+ if (aRec->negative) {
+ aStatus = NS_ERROR_UNKNOWN_HOST;
+ }
+ Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
+ ConditionallyRefreshRecord(aRec, aHost, lock);
+ } else if (af == PR_AF_INET6) {
+ // For AF_INET6, a new lookup means another AF_UNSPEC
+ // lookup. We have already iterated through the
+ // AF_UNSPEC addresses, so we mark this record as
+ // negative.
+ LOG(
+ (" No AF_INET6 in AF_UNSPEC entry: "
+ "host [%s] unknown host.",
+ nsPromiseFlatCString(aHost).get()));
+ result = aRec;
+ aRec->negative = true;
+ aStatus = NS_ERROR_UNKNOWN_HOST;
+ Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
+ METHOD_NEGATIVE_HIT);
+ }
+ }
+ }
+
+ return result.forget();
+}
+
+void nsHostResolver::DetachCallback(
+ const nsACString& host, const nsACString& aTrrServer, uint16_t aType,
+ const OriginAttributes& aOriginAttributes, nsIDNSService::DNSFlags flags,
+ uint16_t af, nsResolveHostCallback* aCallback, nsresult status) {
+ RefPtr<nsHostRecord> rec;
+ RefPtr<nsResolveHostCallback> callback(aCallback);
+
+ {
+ MutexAutoLock lock(mLock);
+
+ nsAutoCString originSuffix;
+ aOriginAttributes.CreateSuffix(originSuffix);
+
+ nsHostKey key(host, aTrrServer, aType, flags, af,
+ (aOriginAttributes.mPrivateBrowsingId > 0), originSuffix);
+ RefPtr<nsHostRecord> entry = mRecordDB.Get(key);
+ if (entry) {
+ // walk list looking for |callback|... we cannot assume
+ // that it will be there!
+
+ for (nsResolveHostCallback* c : entry->mCallbacks) {
+ if (c == callback) {
+ rec = entry;
+ c->remove();
+ break;
+ }
+ }
+ }
+ }
+
+ // complete callback with the given status code; this would only be done if
+ // the record was in the process of being resolved.
+ if (rec) {
+ callback->OnResolveHostComplete(this, rec, status);
+ }
+}
+
+nsresult nsHostResolver::ConditionallyCreateThread(nsHostRecord* rec) {
+ if (mNumIdleTasks) {
+ // wake up idle tasks to process this lookup
+ mIdleTaskCV.Notify();
+ } else if ((mActiveTaskCount < MaxResolverThreadsAnyPriority()) ||
+ (IsHighPriority(rec->flags) &&
+ mActiveTaskCount < MaxResolverThreads())) {
+ nsCOMPtr<nsIRunnable> event = mozilla::NewRunnableMethod(
+ "nsHostResolver::ThreadFunc", this, &nsHostResolver::ThreadFunc);
+ mActiveTaskCount++;
+ nsresult rv =
+ mResolverThreads->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
+ if (NS_FAILED(rv)) {
+ mActiveTaskCount--;
+ }
+ } else {
+ LOG((" Unable to find a thread for looking up host [%s].\n",
+ rec->host.get()));
+ }
+ return NS_OK;
+}
+
+nsresult nsHostResolver::TrrLookup_unlocked(nsHostRecord* rec, TRR* pushedTRR) {
+ MutexAutoLock lock(mLock);
+ return TrrLookup(rec, lock, pushedTRR);
+}
+
+void nsHostResolver::MaybeRenewHostRecord(nsHostRecord* aRec) {
+ MutexAutoLock lock(mLock);
+ MaybeRenewHostRecordLocked(aRec, lock);
+}
+
+void nsHostResolver::MaybeRenewHostRecordLocked(nsHostRecord* aRec,
+ const MutexAutoLock& aLock) {
+ mQueue.MaybeRenewHostRecord(aRec, aLock);
+}
+
+bool nsHostResolver::TRRServiceEnabledForRecord(nsHostRecord* aRec) {
+ MOZ_ASSERT(aRec, "Record must not be empty");
+ MOZ_ASSERT(aRec->mEffectiveTRRMode != nsIRequest::TRR_DEFAULT_MODE,
+ "effective TRR mode must be computed before this call");
+ if (!TRRService::Get()) {
+ aRec->RecordReason(TRRSkippedReason::TRR_NO_GSERVICE);
+ return false;
+ }
+
+ // We always try custom resolvers.
+ if (!aRec->mTrrServer.IsEmpty()) {
+ return true;
+ }
+
+ nsIRequest::TRRMode reqMode = aRec->mEffectiveTRRMode;
+ if (TRRService::Get()->Enabled(reqMode)) {
+ return true;
+ }
+
+ if (NS_IsOffline()) {
+ // If we are in the NOT_CONFIRMED state _because_ we lack connectivity,
+ // then we should report that the browser is offline instead.
+ aRec->RecordReason(TRRSkippedReason::TRR_IS_OFFLINE);
+ return false;
+ }
+
+ auto hasConnectivity = [this]() -> bool {
+ mLock.AssertCurrentThreadOwns();
+ if (!mNCS) {
+ return true;
+ }
+ nsINetworkConnectivityService::ConnectivityState ipv4 = mNCS->GetIPv4();
+ nsINetworkConnectivityService::ConnectivityState ipv6 = mNCS->GetIPv6();
+
+ if (ipv4 == nsINetworkConnectivityService::OK ||
+ ipv6 == nsINetworkConnectivityService::OK) {
+ return true;
+ }
+
+ if (ipv4 == nsINetworkConnectivityService::UNKNOWN ||
+ ipv6 == nsINetworkConnectivityService::UNKNOWN) {
+ // One of the checks hasn't completed yet. Optimistically assume we'll
+ // have network connectivity.
+ return true;
+ }
+
+ return false;
+ };
+
+ if (!hasConnectivity()) {
+ aRec->RecordReason(TRRSkippedReason::TRR_NO_CONNECTIVITY);
+ return false;
+ }
+
+ bool isConfirmed = TRRService::Get()->IsConfirmed();
+ if (!isConfirmed) {
+ aRec->RecordReason(TRRSkippedReason::TRR_NOT_CONFIRMED);
+ }
+
+ return isConfirmed;
+}
+
+// returns error if no TRR resolve is issued
+// it is impt this is not called while a native lookup is going on
+nsresult nsHostResolver::TrrLookup(nsHostRecord* aRec,
+ const MutexAutoLock& aLock, TRR* pushedTRR) {
+ if (Mode() == nsIDNSService::MODE_TRROFF ||
+ StaticPrefs::network_dns_disabled()) {
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+ LOG(("TrrLookup host:%s af:%" PRId16, aRec->host.get(), aRec->af));
+
+ RefPtr<nsHostRecord> rec(aRec);
+ mLock.AssertCurrentThreadOwns();
+
+ RefPtr<AddrHostRecord> addrRec;
+ RefPtr<TypeHostRecord> typeRec;
+
+ if (rec->IsAddrRecord()) {
+ addrRec = do_QueryObject(rec);
+ MOZ_ASSERT(addrRec);
+ } else {
+ typeRec = do_QueryObject(rec);
+ MOZ_ASSERT(typeRec);
+ }
+
+ MOZ_ASSERT(!rec->mResolving);
+
+ if (!TRRServiceEnabledForRecord(aRec)) {
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ MaybeRenewHostRecordLocked(rec, aLock);
+
+ RefPtr<TRRQuery> query = new TRRQuery(this, rec);
+ nsresult rv = query->DispatchLookup(pushedTRR);
+ if (NS_FAILED(rv)) {
+ rec->RecordReason(TRRSkippedReason::TRR_DID_NOT_MAKE_QUERY);
+ return rv;
+ }
+
+ {
+ auto lock = rec->mTRRQuery.Lock();
+ MOZ_ASSERT(!lock.ref(), "TRR already in progress");
+ lock.ref() = query;
+ }
+
+ rec->mResolving++;
+ rec->mTrrAttempts++;
+ rec->StoreNative(false);
+ return NS_OK;
+}
+
+nsresult nsHostResolver::NativeLookup(nsHostRecord* aRec,
+ const MutexAutoLock& aLock) {
+ if (StaticPrefs::network_dns_disabled()) {
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+ LOG(("NativeLookup host:%s af:%" PRId16, aRec->host.get(), aRec->af));
+
+ // If this is not a A/AAAA request, make sure native HTTPS is enabled.
+ MOZ_ASSERT(aRec->IsAddrRecord() || IsNativeHTTPSEnabled());
+ mLock.AssertCurrentThreadOwns();
+
+ RefPtr<nsHostRecord> rec(aRec);
+
+ rec->mNativeStart = TimeStamp::Now();
+
+ // Add rec to one of the pending queues, possibly removing it from mEvictionQ.
+ MaybeRenewHostRecordLocked(aRec, aLock);
+
+ mQueue.InsertRecord(rec, rec->flags, aLock);
+
+ rec->StoreNative(true);
+ rec->StoreNativeUsed(true);
+ rec->mResolving++;
+
+ nsresult rv = ConditionallyCreateThread(rec);
+
+ LOG((" DNS thread counters: total=%d any-live=%d idle=%d pending=%d\n",
+ static_cast<uint32_t>(mActiveTaskCount),
+ static_cast<uint32_t>(mActiveAnyThreadCount),
+ static_cast<uint32_t>(mNumIdleTasks), mQueue.PendingCount()));
+
+ return rv;
+}
+
+// static
+nsIDNSService::ResolverMode nsHostResolver::Mode() {
+ if (TRRService::Get()) {
+ return TRRService::Get()->Mode();
+ }
+
+ // If we don't have a TRR service just return MODE_TRROFF so we don't make
+ // any TRR requests by mistake.
+ return nsIDNSService::MODE_TRROFF;
+}
+
+nsIRequest::TRRMode nsHostRecord::TRRMode() {
+ return nsIDNSService::GetTRRModeFromFlags(flags);
+}
+
+// static
+void nsHostResolver::ComputeEffectiveTRRMode(nsHostRecord* aRec) {
+ nsIDNSService::ResolverMode resolverMode = nsHostResolver::Mode();
+ nsIRequest::TRRMode requestMode = aRec->TRRMode();
+
+ // For domains that are excluded from TRR or when parental control is enabled,
+ // we fallback to NativeLookup. This happens even in MODE_TRRONLY. By default
+ // localhost and local are excluded (so we cover *.local hosts) See the
+ // network.trr.excluded-domains pref.
+
+ if (!TRRService::Get()) {
+ aRec->RecordReason(TRRSkippedReason::TRR_NO_GSERVICE);
+ aRec->mEffectiveTRRMode = requestMode;
+ return;
+ }
+
+ if (!aRec->mTrrServer.IsEmpty()) {
+ aRec->mEffectiveTRRMode = nsIRequest::TRR_ONLY_MODE;
+ return;
+ }
+
+ if (TRRService::Get()->IsExcludedFromTRR(aRec->host)) {
+ aRec->RecordReason(TRRSkippedReason::TRR_EXCLUDED);
+ aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
+ return;
+ }
+
+ if (TRRService::Get()->ParentalControlEnabled()) {
+ aRec->RecordReason(TRRSkippedReason::TRR_PARENTAL_CONTROL);
+ aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
+ return;
+ }
+
+ if (resolverMode == nsIDNSService::MODE_TRROFF) {
+ aRec->RecordReason(TRRSkippedReason::TRR_OFF_EXPLICIT);
+ aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
+ return;
+ }
+
+ if (requestMode == nsIRequest::TRR_DISABLED_MODE) {
+ aRec->RecordReason(TRRSkippedReason::TRR_REQ_MODE_DISABLED);
+ aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
+ return;
+ }
+
+ if ((requestMode == nsIRequest::TRR_DEFAULT_MODE &&
+ resolverMode == nsIDNSService::MODE_NATIVEONLY)) {
+ if (StaticPrefs::network_trr_display_fallback_warning()) {
+ TRRSkippedReason heuristicResult =
+ TRRService::Get()->GetHeuristicDetectionResult();
+ if (heuristicResult != TRRSkippedReason::TRR_UNSET &&
+ heuristicResult != TRRSkippedReason::TRR_OK) {
+ aRec->RecordReason(heuristicResult);
+ aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
+ return;
+ }
+ }
+ aRec->RecordReason(TRRSkippedReason::TRR_MODE_NOT_ENABLED);
+ aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
+ return;
+ }
+
+ if (requestMode == nsIRequest::TRR_DEFAULT_MODE &&
+ resolverMode == nsIDNSService::MODE_TRRFIRST) {
+ aRec->mEffectiveTRRMode = nsIRequest::TRR_FIRST_MODE;
+ return;
+ }
+
+ if (requestMode == nsIRequest::TRR_DEFAULT_MODE &&
+ resolverMode == nsIDNSService::MODE_TRRONLY) {
+ aRec->mEffectiveTRRMode = nsIRequest::TRR_ONLY_MODE;
+ return;
+ }
+
+ aRec->mEffectiveTRRMode = requestMode;
+}
+
+// Kick-off a name resolve operation, using native resolver and/or TRR
+nsresult nsHostResolver::NameLookup(nsHostRecord* rec,
+ const mozilla::MutexAutoLock& aLock) {
+ LOG(("NameLookup host:%s af:%" PRId16, rec->host.get(), rec->af));
+ mLock.AssertCurrentThreadOwns();
+
+ if (rec->flags & RES_IP_HINT) {
+ LOG(("Skip lookup if RES_IP_HINT is set\n"));
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ nsresult rv = NS_ERROR_UNKNOWN_HOST;
+ if (rec->mResolving) {
+ LOG(("NameLookup %s while already resolving\n", rec->host.get()));
+ return NS_OK;
+ }
+
+ // Make sure we reset the reason each time we attempt to do a new lookup
+ // so we don't wrongly report the reason for the previous one.
+ rec->Reset();
+
+ ComputeEffectiveTRRMode(rec);
+
+ if (!rec->mTrrServer.IsEmpty()) {
+ LOG(("NameLookup: %s use trr:%s", rec->host.get(), rec->mTrrServer.get()));
+ if (rec->mEffectiveTRRMode != nsIRequest::TRR_ONLY_MODE) {
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ if (rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR) {
+ LOG(("TRR with server and DISABLE_TRR flag. Returning error."));
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+ return TrrLookup(rec, aLock);
+ }
+
+ LOG(("NameLookup: %s effectiveTRRmode: %d flags: %X", rec->host.get(),
+ static_cast<nsIRequest::TRRMode>(rec->mEffectiveTRRMode), rec->flags));
+
+ if (rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR) {
+ rec->RecordReason(TRRSkippedReason::TRR_DISABLED_FLAG);
+ }
+
+ bool serviceNotReady = !TRRServiceEnabledForRecord(rec);
+
+ if (rec->mEffectiveTRRMode != nsIRequest::TRR_DISABLED_MODE &&
+ !((rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR)) &&
+ !serviceNotReady) {
+ rv = TrrLookup(rec, aLock);
+ }
+
+ if (rec->mEffectiveTRRMode == nsIRequest::TRR_DISABLED_MODE ||
+ (rec->mEffectiveTRRMode == nsIRequest::TRR_FIRST_MODE &&
+ (rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR || serviceNotReady ||
+ NS_FAILED(rv)))) {
+ if (!IsNativeHTTPSEnabled() && !rec->IsAddrRecord()) {
+ return rv;
+ }
+
+#ifdef DEBUG
+ // If we use this branch then the mTRRUsed flag should not be set
+ // Even if we did call TrrLookup above, the fact that it failed sync-ly
+ // means that we didn't actually succeed in opening the channel.
+ RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
+ MOZ_ASSERT_IF(addrRec, addrRec->mResolverType == DNSResolverType::Native);
+#endif
+
+ // We did not lookup via TRR - don't fallback to native if the
+ // network.trr.display_fallback_warning pref is set and either
+ // 1. we are in TRR first mode and confirmation failed
+ // 2. the record has trr_disabled and a heuristic skip reason
+ if (StaticPrefs::network_trr_display_fallback_warning() &&
+ rec->mEffectiveTRRMode != nsIRequest::TRR_ONLY_MODE) {
+ if ((rec->mEffectiveTRRMode == nsIRequest::TRR_FIRST_MODE &&
+ rec->mTRRSkippedReason == TRRSkippedReason::TRR_NOT_CONFIRMED) ||
+ (rec->mEffectiveTRRMode == nsIRequest::TRR_DISABLED_MODE &&
+ rec->mTRRSkippedReason >=
+ nsITRRSkipReason::TRR_HEURISTIC_TRIPPED_GOOGLE_SAFESEARCH &&
+ rec->mTRRSkippedReason <=
+ nsITRRSkipReason::TRR_HEURISTIC_TRIPPED_NRPT)) {
+ LOG((
+ "NameLookup: ResolveHostComplete with status NS_ERROR_UNKNOWN_HOST "
+ "for: %s effectiveTRRmode: "
+ "%d SkippedReason: %d",
+ rec->host.get(),
+ static_cast<nsIRequest::TRRMode>(rec->mEffectiveTRRMode),
+ static_cast<int32_t>(rec->mTRRSkippedReason)));
+
+ mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs =
+ std::move(rec->mCallbacks);
+ for (nsResolveHostCallback* c = cbs.getFirst(); c;
+ c = c->removeAndGetNext()) {
+ c->OnResolveHostComplete(this, rec, NS_ERROR_UNKNOWN_HOST);
+ }
+
+ return NS_OK;
+ }
+ }
+
+ rv = NativeLookup(rec, aLock);
+ }
+
+ return rv;
+}
+
+nsresult nsHostResolver::ConditionallyRefreshRecord(
+ nsHostRecord* rec, const nsACString& host, const MutexAutoLock& aLock) {
+ if ((rec->CheckExpiration(TimeStamp::NowLoRes()) != nsHostRecord::EXP_VALID ||
+ rec->negative) &&
+ !rec->mResolving && rec->RefreshForNegativeResponse()) {
+ LOG((" Using %s cache entry for host [%s] but starting async renewal.",
+ rec->negative ? "negative" : "positive", host.BeginReading()));
+ NameLookup(rec, aLock);
+
+ if (rec->IsAddrRecord() && !rec->negative) {
+ // negative entries are constantly being refreshed, only
+ // track positive grace period induced renewals
+ Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_RENEWAL);
+ }
+ }
+ return NS_OK;
+}
+
+bool nsHostResolver::GetHostToLookup(nsHostRecord** result) {
+ bool timedOut = false;
+ TimeDuration timeout;
+ TimeStamp epoch, now;
+
+ MutexAutoLock lock(mLock);
+
+ timeout = (mNumIdleTasks >= MaxResolverThreadsAnyPriority())
+ ? mShortIdleTimeout
+ : mLongIdleTimeout;
+ epoch = TimeStamp::Now();
+
+ while (!mShutdown) {
+ // remove next record from Q; hand over owning reference. Check high, then
+ // med, then low
+
+#define SET_GET_TTL(var, val) (var)->StoreGetTtl(sGetTtlEnabled && (val))
+
+ RefPtr<nsHostRecord> rec = mQueue.Dequeue(true, lock);
+ if (rec) {
+ SET_GET_TTL(rec, false);
+ rec.forget(result);
+ return true;
+ }
+
+ if (mActiveAnyThreadCount < MaxResolverThreadsAnyPriority()) {
+ rec = mQueue.Dequeue(false, lock);
+ if (rec) {
+ MOZ_ASSERT(IsMediumPriority(rec->flags) || IsLowPriority(rec->flags));
+ mActiveAnyThreadCount++;
+ rec->StoreUsingAnyThread(true);
+ SET_GET_TTL(rec, true);
+ rec.forget(result);
+ return true;
+ }
+ }
+
+ // Determining timeout is racy, so allow one cycle through checking the
+ // queues before exiting.
+ if (timedOut) {
+ break;
+ }
+
+ // wait for one or more of the following to occur:
+ // (1) the pending queue has a host record to process
+ // (2) the shutdown flag has been set
+ // (3) the thread has been idle for too long
+
+ mNumIdleTasks++;
+ mIdleTaskCV.Wait(timeout);
+ mNumIdleTasks--;
+
+ now = TimeStamp::Now();
+
+ if (now - epoch >= timeout) {
+ timedOut = true;
+ } else {
+ // It is possible that CondVar::Wait() was interrupted and returned
+ // early, in which case we will loop back and re-enter it. In that
+ // case we want to do so with the new timeout reduced to reflect
+ // time already spent waiting.
+ timeout -= now - epoch;
+ epoch = now;
+ }
+ }
+
+ // tell thread to exit...
+ return false;
+}
+
+void nsHostResolver::PrepareRecordExpirationAddrRecord(
+ AddrHostRecord* rec) const {
+ // NOTE: rec->addr_info_lock is already held by parent
+ MOZ_ASSERT(((bool)rec->addr_info) != rec->negative);
+ mLock.AssertCurrentThreadOwns();
+ if (!rec->addr_info) {
+ rec->SetExpiration(TimeStamp::NowLoRes(), NEGATIVE_RECORD_LIFETIME, 0);
+ LOG(("Caching host [%s] negative record for %u seconds.\n", rec->host.get(),
+ NEGATIVE_RECORD_LIFETIME));
+ return;
+ }
+
+ unsigned int lifetime = mDefaultCacheLifetime;
+ unsigned int grace = mDefaultGracePeriod;
+
+ unsigned int ttl = mDefaultCacheLifetime;
+ if (sGetTtlEnabled || rec->addr_info->IsTRR()) {
+ if (rec->addr_info && rec->addr_info->TTL() != AddrInfo::NO_TTL_DATA) {
+ ttl = rec->addr_info->TTL();
+ }
+ lifetime = ttl;
+ grace = 0;
+ }
+
+ rec->SetExpiration(TimeStamp::NowLoRes(), lifetime, grace);
+ LOG(("Caching host [%s] record for %u seconds (grace %d).", rec->host.get(),
+ lifetime, grace));
+}
+
+static bool different_rrset(AddrInfo* rrset1, AddrInfo* rrset2) {
+ if (!rrset1 || !rrset2) {
+ return true;
+ }
+
+ LOG(("different_rrset %s\n", rrset1->Hostname().get()));
+
+ if (rrset1->ResolverType() != rrset2->ResolverType()) {
+ return true;
+ }
+
+ if (rrset1->TRRType() != rrset2->TRRType()) {
+ return true;
+ }
+
+ if (rrset1->Addresses().Length() != rrset2->Addresses().Length()) {
+ LOG(("different_rrset true due to length change\n"));
+ return true;
+ }
+
+ nsTArray<NetAddr> orderedSet1 = rrset1->Addresses().Clone();
+ nsTArray<NetAddr> orderedSet2 = rrset2->Addresses().Clone();
+ orderedSet1.Sort();
+ orderedSet2.Sort();
+
+ bool eq = orderedSet1 == orderedSet2;
+ if (!eq) {
+ LOG(("different_rrset true due to content change\n"));
+ } else {
+ LOG(("different_rrset false\n"));
+ }
+ return !eq;
+}
+
+void nsHostResolver::AddToEvictionQ(nsHostRecord* rec,
+ const MutexAutoLock& aLock) {
+ mQueue.AddToEvictionQ(rec, mMaxCacheEntries, mRecordDB, aLock);
+}
+
+// After a first lookup attempt with TRR in mode 2, we may:
+// - If network.trr.retry_on_recoverable_errors is false, retry with native.
+// - If network.trr.retry_on_recoverable_errors is true:
+// - Retry with native if the first attempt failed because we got NXDOMAIN, an
+// unreachable address (TRR_DISABLED_FLAG), or we skipped TRR because
+// Confirmation failed.
+// - Trigger a "RetryTRR" Confirmation which will start a fresh
+// connection for TRR, and then retry the lookup with TRR.
+// - If the second attempt failed, fallback to native if
+// network.trr.strict_native_fallback is false.
+// Returns true if we retried with either TRR or Native.
+bool nsHostResolver::MaybeRetryTRRLookup(
+ AddrHostRecord* aAddrRec, nsresult aFirstAttemptStatus,
+ TRRSkippedReason aFirstAttemptSkipReason, nsresult aChannelStatus,
+ const MutexAutoLock& aLock) {
+ if (NS_FAILED(aFirstAttemptStatus) &&
+ (aChannelStatus == NS_ERROR_PROXY_UNAUTHORIZED ||
+ aChannelStatus == NS_ERROR_PROXY_AUTHENTICATION_FAILED) &&
+ aAddrRec->mEffectiveTRRMode == nsIRequest::TRR_ONLY_MODE) {
+ LOG(("MaybeRetryTRRLookup retry because of proxy connect failed"));
+ TRRService::Get()->DontUseTRRThread();
+ return DoRetryTRR(aAddrRec, aLock);
+ }
+
+ if (NS_SUCCEEDED(aFirstAttemptStatus) ||
+ aAddrRec->mEffectiveTRRMode != nsIRequest::TRR_FIRST_MODE ||
+ aFirstAttemptStatus == NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
+ return false;
+ }
+
+ MOZ_ASSERT(!aAddrRec->mResolving);
+ if (!StaticPrefs::network_trr_retry_on_recoverable_errors()) {
+ LOG(("nsHostResolver::MaybeRetryTRRLookup retrying with native"));
+ return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock));
+ }
+
+ if (IsFailedConfirmationOrNoConnectivity(aFirstAttemptSkipReason) ||
+ IsNonRecoverableTRRSkipReason(aFirstAttemptSkipReason) ||
+ IsBlockedTRRRequest(aFirstAttemptSkipReason)) {
+ LOG(
+ ("nsHostResolver::MaybeRetryTRRLookup retrying with native in strict "
+ "mode, skip reason was %d",
+ static_cast<uint32_t>(aFirstAttemptSkipReason)));
+ return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock));
+ }
+
+ if (aAddrRec->mTrrAttempts > 1) {
+ if (!StaticPrefs::network_trr_strict_native_fallback()) {
+ LOG(
+ ("nsHostResolver::MaybeRetryTRRLookup retry failed. Using "
+ "native."));
+ return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock));
+ }
+
+ if (aFirstAttemptSkipReason == TRRSkippedReason::TRR_TIMEOUT &&
+ StaticPrefs::network_trr_strict_native_fallback_allow_timeouts()) {
+ LOG(
+ ("nsHostResolver::MaybeRetryTRRLookup retry timed out. Using "
+ "native."));
+ return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock));
+ }
+ LOG(("nsHostResolver::MaybeRetryTRRLookup mTrrAttempts>1, not retrying."));
+ return false;
+ }
+
+ LOG(
+ ("nsHostResolver::MaybeRetryTRRLookup triggering Confirmation and "
+ "retrying with TRR, skip reason was %d",
+ static_cast<uint32_t>(aFirstAttemptSkipReason)));
+ TRRService::Get()->RetryTRRConfirm();
+
+ return DoRetryTRR(aAddrRec, aLock);
+}
+
+bool nsHostResolver::DoRetryTRR(AddrHostRecord* aAddrRec,
+ const mozilla::MutexAutoLock& aLock) {
+ {
+ // Clear out the old query
+ auto trrQuery = aAddrRec->mTRRQuery.Lock();
+ trrQuery.ref() = nullptr;
+ }
+
+ if (NS_SUCCEEDED(TrrLookup(aAddrRec, aLock, nullptr /* pushedTRR */))) {
+ aAddrRec->NotifyRetryingTrr();
+ return true;
+ }
+
+ return false;
+}
+
+//
+// CompleteLookup() checks if the resolving should be redone and if so it
+// returns LOOKUP_RESOLVEAGAIN, but only if 'status' is not NS_ERROR_ABORT.
+nsHostResolver::LookupStatus nsHostResolver::CompleteLookup(
+ nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb,
+ const nsACString& aOriginsuffix, TRRSkippedReason aReason,
+ mozilla::net::TRR* aTRRRequest) {
+ MutexAutoLock lock(mLock);
+ return CompleteLookupLocked(rec, status, aNewRRSet, pb, aOriginsuffix,
+ aReason, aTRRRequest, lock);
+}
+
+nsHostResolver::LookupStatus nsHostResolver::CompleteLookupLocked(
+ nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb,
+ const nsACString& aOriginsuffix, TRRSkippedReason aReason,
+ mozilla::net::TRR* aTRRRequest, const mozilla::MutexAutoLock& aLock) {
+ MOZ_ASSERT(rec);
+ MOZ_ASSERT(rec->pb == pb);
+ MOZ_ASSERT(rec->IsAddrRecord());
+
+ RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
+ MOZ_ASSERT(addrRec);
+
+ RefPtr<AddrInfo> newRRSet(aNewRRSet);
+ MOZ_ASSERT(NS_FAILED(status) || newRRSet->Addresses().Length() > 0);
+
+ DNSResolverType type =
+ newRRSet ? newRRSet->ResolverType() : DNSResolverType::Native;
+
+ if (NS_FAILED(status)) {
+ newRRSet = nullptr;
+ }
+
+ if (addrRec->LoadResolveAgain() && (status != NS_ERROR_ABORT) &&
+ type == DNSResolverType::Native) {
+ LOG(("nsHostResolver record %p resolve again due to flushcache\n",
+ addrRec.get()));
+ addrRec->StoreResolveAgain(false);
+ return LOOKUP_RESOLVEAGAIN;
+ }
+
+ MOZ_ASSERT(addrRec->mResolving);
+ addrRec->mResolving--;
+ LOG((
+ "nsHostResolver::CompleteLookup %s %p %X resolver=%d stillResolving=%d\n",
+ addrRec->host.get(), aNewRRSet, (unsigned int)status, (int)type,
+ int(addrRec->mResolving)));
+
+ if (type != DNSResolverType::Native) {
+ if (NS_FAILED(status) && status != NS_ERROR_UNKNOWN_HOST &&
+ status != NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
+ // the errors are not failed resolves, that means
+ // something else failed, consider this as *TRR not used*
+ // for actually trying to resolve the host
+ addrRec->mResolverType = DNSResolverType::Native;
+ }
+
+ if (NS_FAILED(status)) {
+ if (aReason != TRRSkippedReason::TRR_UNSET) {
+ addrRec->RecordReason(aReason);
+ } else {
+ // Unknown failed reason.
+ addrRec->RecordReason(TRRSkippedReason::TRR_FAILED);
+ }
+ } else {
+ addrRec->mTRRSuccess = true;
+ addrRec->RecordReason(TRRSkippedReason::TRR_OK);
+ }
+
+ nsresult channelStatus = aTRRRequest->ChannelStatus();
+ if (MaybeRetryTRRLookup(addrRec, status, aReason, channelStatus, aLock)) {
+ MOZ_ASSERT(addrRec->mResolving);
+ return LOOKUP_OK;
+ }
+
+ if (!addrRec->mTRRSuccess) {
+ // no TRR success
+ newRRSet = nullptr;
+ }
+
+ if (NS_FAILED(status)) {
+ // This is the error that consumers expect.
+ status = NS_ERROR_UNKNOWN_HOST;
+ }
+ } else { // native resolve completed
+ if (addrRec->LoadUsingAnyThread()) {
+ mActiveAnyThreadCount--;
+ addrRec->StoreUsingAnyThread(false);
+ }
+
+ addrRec->mNativeSuccess = static_cast<bool>(newRRSet);
+ if (addrRec->mNativeSuccess) {
+ addrRec->mNativeDuration = TimeStamp::Now() - addrRec->mNativeStart;
+ }
+ }
+
+ addrRec->OnCompleteLookup();
+
+ // update record fields. We might have a addrRec->addr_info already if a
+ // previous lookup result expired and we're reresolving it or we get
+ // a late second TRR response.
+ if (!mShutdown) {
+ MutexAutoLock lock(addrRec->addr_info_lock);
+ RefPtr<AddrInfo> old_addr_info;
+ if (different_rrset(addrRec->addr_info, newRRSet)) {
+ LOG(("nsHostResolver record %p new gencnt\n", addrRec.get()));
+ old_addr_info = addrRec->addr_info;
+ addrRec->addr_info = std::move(newRRSet);
+ addrRec->addr_info_gencnt++;
+ } else {
+ if (addrRec->addr_info && newRRSet) {
+ auto builder = addrRec->addr_info->Build();
+ builder.SetTTL(newRRSet->TTL());
+ // Update trr timings
+ builder.SetTrrFetchDuration(newRRSet->GetTrrFetchDuration());
+ builder.SetTrrFetchDurationNetworkOnly(
+ newRRSet->GetTrrFetchDurationNetworkOnly());
+
+ addrRec->addr_info = builder.Finish();
+ }
+ old_addr_info = std::move(newRRSet);
+ }
+ addrRec->negative = !addrRec->addr_info;
+ PrepareRecordExpirationAddrRecord(addrRec);
+ }
+
+ if (LOG_ENABLED()) {
+ MutexAutoLock lock(addrRec->addr_info_lock);
+ if (addrRec->addr_info) {
+ for (const auto& elem : addrRec->addr_info->Addresses()) {
+ char buf[128];
+ elem.ToStringBuffer(buf, sizeof(buf));
+ LOG(("CompleteLookup: %s has %s\n", addrRec->host.get(), buf));
+ }
+ } else {
+ LOG(("CompleteLookup: %s has NO address\n", addrRec->host.get()));
+ }
+ }
+
+ // get the list of pending callbacks for this lookup, and notify
+ // them that the lookup is complete.
+ mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs =
+ std::move(rec->mCallbacks);
+
+ LOG(("nsHostResolver record %p calling back dns users status:%X\n",
+ addrRec.get(), int(status)));
+
+ for (nsResolveHostCallback* c = cbs.getFirst(); c;
+ c = c->removeAndGetNext()) {
+ c->OnResolveHostComplete(this, rec, status);
+ }
+
+ OnResolveComplete(rec, aLock);
+
+#ifdef DNSQUERY_AVAILABLE
+ // Unless the result is from TRR, resolve again to get TTL
+ bool hasNativeResult = false;
+ {
+ MutexAutoLock lock(addrRec->addr_info_lock);
+ if (addrRec->addr_info && !addrRec->addr_info->IsTRR()) {
+ hasNativeResult = true;
+ }
+ }
+ if (hasNativeResult && !mShutdown && !addrRec->LoadGetTtl() &&
+ !rec->mResolving && sGetTtlEnabled) {
+ LOG(("Issuing second async lookup for TTL for host [%s].",
+ addrRec->host.get()));
+ addrRec->flags =
+ (addrRec->flags & ~nsIDNSService::RESOLVE_PRIORITY_MEDIUM) |
+ nsIDNSService::RESOLVE_PRIORITY_LOW;
+ DebugOnly<nsresult> rv = NativeLookup(rec, aLock);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+ "Could not issue second async lookup for TTL.");
+ }
+#endif
+ return LOOKUP_OK;
+}
+
+nsHostResolver::LookupStatus nsHostResolver::CompleteLookupByType(
+ nsHostRecord* rec, nsresult status,
+ mozilla::net::TypeRecordResultType& aResult, TRRSkippedReason aReason,
+ uint32_t aTtl, bool pb) {
+ MutexAutoLock lock(mLock);
+ return CompleteLookupByTypeLocked(rec, status, aResult, aReason, aTtl, pb,
+ lock);
+}
+
+nsHostResolver::LookupStatus nsHostResolver::CompleteLookupByTypeLocked(
+ nsHostRecord* rec, nsresult status,
+ mozilla::net::TypeRecordResultType& aResult, TRRSkippedReason aReason,
+ uint32_t aTtl, bool pb, const mozilla::MutexAutoLock& aLock) {
+ MOZ_ASSERT(rec);
+ MOZ_ASSERT(rec->pb == pb);
+ MOZ_ASSERT(!rec->IsAddrRecord());
+
+ RefPtr<TypeHostRecord> typeRec = do_QueryObject(rec);
+ MOZ_ASSERT(typeRec);
+
+ MOZ_ASSERT(typeRec->mResolving);
+ typeRec->mResolving--;
+
+ if (NS_FAILED(status)) {
+ LOG(("nsHostResolver::CompleteLookupByType record %p [%s] status %x\n",
+ typeRec.get(), typeRec->host.get(), (unsigned int)status));
+ typeRec->SetExpiration(
+ TimeStamp::NowLoRes(),
+ StaticPrefs::network_dns_negative_ttl_for_type_record(), 0);
+ MOZ_ASSERT(aResult.is<TypeRecordEmpty>());
+ status = NS_ERROR_UNKNOWN_HOST;
+ typeRec->negative = true;
+ if (aReason != TRRSkippedReason::TRR_UNSET) {
+ typeRec->RecordReason(aReason);
+ } else {
+ // Unknown failed reason.
+ typeRec->RecordReason(TRRSkippedReason::TRR_FAILED);
+ }
+ } else {
+ size_t recordCount = 0;
+ if (aResult.is<TypeRecordTxt>()) {
+ recordCount = aResult.as<TypeRecordTxt>().Length();
+ } else if (aResult.is<TypeRecordHTTPSSVC>()) {
+ recordCount = aResult.as<TypeRecordHTTPSSVC>().Length();
+ }
+ LOG(
+ ("nsHostResolver::CompleteLookupByType record %p [%s], number of "
+ "records %zu\n",
+ typeRec.get(), typeRec->host.get(), recordCount));
+ MutexAutoLock typeLock(typeRec->mResultsLock);
+ typeRec->mResults = aResult;
+ typeRec->SetExpiration(TimeStamp::NowLoRes(), aTtl, mDefaultGracePeriod);
+ typeRec->negative = false;
+ typeRec->mTRRSuccess = !rec->LoadNative();
+ typeRec->mNativeSuccess = rec->LoadNative();
+ MOZ_ASSERT(aReason != TRRSkippedReason::TRR_UNSET);
+ typeRec->RecordReason(aReason);
+ }
+
+ mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs =
+ std::move(typeRec->mCallbacks);
+
+ LOG(
+ ("nsHostResolver::CompleteLookupByType record %p calling back dns "
+ "users\n",
+ typeRec.get()));
+
+ for (nsResolveHostCallback* c = cbs.getFirst(); c;
+ c = c->removeAndGetNext()) {
+ c->OnResolveHostComplete(this, rec, status);
+ }
+
+ OnResolveComplete(rec, aLock);
+
+ return LOOKUP_OK;
+}
+
+void nsHostResolver::OnResolveComplete(nsHostRecord* aRec,
+ const mozilla::MutexAutoLock& aLock) {
+ if (!aRec->mResolving && !mShutdown) {
+ {
+ auto trrQuery = aRec->mTRRQuery.Lock();
+ if (trrQuery.ref()) {
+ aRec->mTrrDuration = trrQuery.ref()->Duration();
+ }
+ trrQuery.ref() = nullptr;
+ }
+ aRec->ResolveComplete();
+
+ AddToEvictionQ(aRec, aLock);
+ }
+}
+
+void nsHostResolver::CancelAsyncRequest(
+ const nsACString& host, const nsACString& aTrrServer, uint16_t aType,
+ const OriginAttributes& aOriginAttributes, nsIDNSService::DNSFlags flags,
+ uint16_t af, nsIDNSListener* aListener, nsresult status)
+
+{
+ MutexAutoLock lock(mLock);
+
+ nsAutoCString originSuffix;
+ aOriginAttributes.CreateSuffix(originSuffix);
+
+ // Lookup the host record associated with host, flags & address family
+
+ nsHostKey key(host, aTrrServer, aType, flags, af,
+ (aOriginAttributes.mPrivateBrowsingId > 0), originSuffix);
+ RefPtr<nsHostRecord> rec = mRecordDB.Get(key);
+ if (!rec) {
+ return;
+ }
+
+ for (RefPtr<nsResolveHostCallback> c : rec->mCallbacks) {
+ if (c->EqualsAsyncListener(aListener)) {
+ c->remove();
+ c->OnResolveHostComplete(this, rec.get(), status);
+ break;
+ }
+ }
+
+ // If there are no more callbacks, remove the hash table entry
+ if (rec->mCallbacks.isEmpty()) {
+ mRecordDB.Remove(*static_cast<nsHostKey*>(rec.get()));
+ // If record is on a Queue, remove it
+ mQueue.MaybeRemoveFromQ(rec, lock);
+ }
+}
+
+size_t nsHostResolver::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const {
+ MutexAutoLock lock(mLock);
+
+ size_t n = mallocSizeOf(this);
+
+ n += mRecordDB.ShallowSizeOfExcludingThis(mallocSizeOf);
+ for (const auto& entry : mRecordDB.Values()) {
+ n += entry->SizeOfIncludingThis(mallocSizeOf);
+ }
+
+ // The following fields aren't measured.
+ // - mHighQ, mMediumQ, mLowQ, mEvictionQ, because they just point to
+ // nsHostRecords that also pointed to by entries |mRecordDB|, and
+ // measured when |mRecordDB| is measured.
+
+ return n;
+}
+
+void nsHostResolver::ThreadFunc() {
+ LOG(("DNS lookup thread - starting execution.\n"));
+
+#if defined(RES_RETRY_ON_FAILURE)
+ nsResState rs;
+#endif
+ RefPtr<nsHostRecord> rec;
+ RefPtr<AddrInfo> ai;
+
+ do {
+ if (!rec) {
+ RefPtr<nsHostRecord> tmpRec;
+ if (!GetHostToLookup(getter_AddRefs(tmpRec))) {
+ break; // thread shutdown signal
+ }
+ // GetHostToLookup() returns an owning reference
+ MOZ_ASSERT(tmpRec);
+ rec.swap(tmpRec);
+ }
+
+ LOG1(("DNS lookup thread - Calling getaddrinfo for host [%s].\n",
+ rec->host.get()));
+
+ TimeStamp startTime = TimeStamp::Now();
+ bool getTtl = rec->LoadGetTtl();
+ TimeDuration inQueue = startTime - rec->mNativeStart;
+ uint32_t ms = static_cast<uint32_t>(inQueue.ToMilliseconds());
+ Telemetry::Accumulate(Telemetry::DNS_NATIVE_QUEUING, ms);
+
+ if (!rec->IsAddrRecord()) {
+ LOG(("byType on DNS thread"));
+ TypeRecordResultType result = AsVariant(mozilla::Nothing());
+ uint32_t ttl = UINT32_MAX;
+ nsresult status = ResolveHTTPSRecord(rec->host, rec->flags, result, ttl);
+ mozilla::glean::networking::dns_native_count
+ .EnumGet(rec->pb
+ ? glean::networking::DnsNativeCountLabel::eHttpsPrivate
+ : glean::networking::DnsNativeCountLabel::eHttpsRegular)
+ .Add(1);
+ CompleteLookupByType(rec, status, result, rec->mTRRSkippedReason, ttl,
+ rec->pb);
+ rec = nullptr;
+ continue;
+ }
+
+ nsresult status =
+ GetAddrInfo(rec->host, rec->af, rec->flags, getter_AddRefs(ai), getTtl);
+#if defined(RES_RETRY_ON_FAILURE)
+ if (NS_FAILED(status) && rs.Reset()) {
+ status = GetAddrInfo(rec->host, rec->af, rec->flags, getter_AddRefs(ai),
+ getTtl);
+ }
+#endif
+
+ mozilla::glean::networking::dns_native_count
+ .EnumGet(rec->pb ? glean::networking::DnsNativeCountLabel::ePrivate
+ : glean::networking::DnsNativeCountLabel::eRegular)
+ .Add(1);
+
+ if (RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec)) {
+ // obtain lock to check shutdown and manage inter-module telemetry
+ MutexAutoLock lock(mLock);
+
+ if (!mShutdown) {
+ TimeDuration elapsed = TimeStamp::Now() - startTime;
+ if (NS_SUCCEEDED(status)) {
+ if (!addrRec->addr_info_gencnt) {
+ // Time for initial lookup.
+ glean::networking::dns_lookup_time.AccumulateRawDuration(elapsed);
+ } else if (!getTtl) {
+ // Time for renewal; categorized by expiration strategy.
+ glean::networking::dns_renewal_time.AccumulateRawDuration(elapsed);
+ } else {
+ // Time to get TTL; categorized by expiration strategy.
+ glean::networking::dns_renewal_time_for_ttl.AccumulateRawDuration(
+ elapsed);
+ }
+ } else {
+ glean::networking::dns_failed_lookup_time.AccumulateRawDuration(
+ elapsed);
+ }
+ }
+ }
+
+ LOG1(("DNS lookup thread - lookup completed for host [%s]: %s.\n",
+ rec->host.get(), ai ? "success" : "failure: unknown host"));
+
+ if (LOOKUP_RESOLVEAGAIN ==
+ CompleteLookup(rec, status, ai, rec->pb, rec->originSuffix,
+ rec->mTRRSkippedReason, nullptr)) {
+ // leave 'rec' assigned and loop to make a renewed host resolve
+ LOG(("DNS lookup thread - Re-resolving host [%s].\n", rec->host.get()));
+ } else {
+ rec = nullptr;
+ }
+ } while (true);
+
+ MutexAutoLock lock(mLock);
+ mActiveTaskCount--;
+ LOG(("DNS lookup thread - queue empty, task finished.\n"));
+}
+
+void nsHostResolver::SetCacheLimits(uint32_t aMaxCacheEntries,
+ uint32_t aDefaultCacheEntryLifetime,
+ uint32_t aDefaultGracePeriod) {
+ MutexAutoLock lock(mLock);
+ mMaxCacheEntries = aMaxCacheEntries;
+ mDefaultCacheLifetime = aDefaultCacheEntryLifetime;
+ mDefaultGracePeriod = aDefaultGracePeriod;
+}
+
+nsresult nsHostResolver::Create(uint32_t maxCacheEntries,
+ uint32_t defaultCacheEntryLifetime,
+ uint32_t defaultGracePeriod,
+ nsHostResolver** result) {
+ RefPtr<nsHostResolver> res = new nsHostResolver(
+ maxCacheEntries, defaultCacheEntryLifetime, defaultGracePeriod);
+
+ nsresult rv = res->Init();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ res.forget(result);
+ return NS_OK;
+}
+
+void nsHostResolver::GetDNSCacheEntries(nsTArray<DNSCacheEntries>* args) {
+ MutexAutoLock lock(mLock);
+ for (const auto& recordEntry : mRecordDB) {
+ // We don't pay attention to address literals, only resolved domains.
+ // Also require a host.
+ nsHostRecord* rec = recordEntry.GetWeak();
+ MOZ_ASSERT(rec, "rec should never be null here!");
+
+ if (!rec) {
+ continue;
+ }
+
+ // For now we only show A/AAAA records.
+ if (!rec->IsAddrRecord()) {
+ continue;
+ }
+
+ RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
+ MOZ_ASSERT(addrRec);
+ if (!addrRec || !addrRec->addr_info) {
+ continue;
+ }
+
+ DNSCacheEntries info;
+ info.hostname = rec->host;
+ info.family = rec->af;
+ info.expiration =
+ (int64_t)(rec->mValidEnd - TimeStamp::NowLoRes()).ToSeconds();
+ if (info.expiration <= 0) {
+ // We only need valid DNS cache entries
+ continue;
+ }
+
+ {
+ MutexAutoLock lock(addrRec->addr_info_lock);
+ for (const auto& addr : addrRec->addr_info->Addresses()) {
+ char buf[kIPv6CStrBufSize];
+ if (addr.ToStringBuffer(buf, sizeof(buf))) {
+ info.hostaddr.AppendElement(buf);
+ }
+ }
+ info.TRR = addrRec->addr_info->IsTRR();
+ }
+
+ info.originAttributesSuffix = recordEntry.GetKey().originSuffix;
+ info.flags = nsPrintfCString("%u|0x%x|%u|%d|%s", rec->type, rec->flags,
+ rec->af, rec->pb, rec->mTrrServer.get());
+
+ args->AppendElement(std::move(info));
+ }
+}
+
+#undef LOG
+#undef LOG_ENABLED
diff --git a/netwerk/dns/nsHostResolver.h b/netwerk/dns/nsHostResolver.h
new file mode 100644
index 0000000000..02e6a343f8
--- /dev/null
+++ b/netwerk/dns/nsHostResolver.h
@@ -0,0 +1,344 @@
+/* vim:set ts=4 sw=2 sts=2 et cin: */
+/* 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/. */
+
+#ifndef nsHostResolver_h__
+#define nsHostResolver_h__
+
+#include "nscore.h"
+#include "prnetdb.h"
+#include "PLDHashTable.h"
+#include "mozilla/CondVar.h"
+#include "mozilla/DataMutex.h"
+#include "nsISupportsImpl.h"
+#include "nsIDNSListener.h"
+#include "nsTArray.h"
+#include "GetAddrInfo.h"
+#include "HostRecordQueue.h"
+#include "mozilla/net/DNS.h"
+#include "mozilla/net/DashboardTypes.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/UniquePtr.h"
+#include "nsHostRecord.h"
+#include "nsRefPtrHashtable.h"
+#include "nsIThreadPool.h"
+#include "mozilla/net/NetworkConnectivityService.h"
+#include "mozilla/net/DNSByTypeRecord.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/StaticPrefs_network.h"
+
+namespace mozilla {
+namespace net {
+class TRR;
+class TRRQuery;
+
+static inline uint32_t MaxResolverThreadsAnyPriority() {
+ return StaticPrefs::network_dns_max_any_priority_threads();
+}
+
+static inline uint32_t MaxResolverThreadsHighPriority() {
+ return StaticPrefs::network_dns_max_high_priority_threads();
+}
+
+static inline uint32_t MaxResolverThreads() {
+ return MaxResolverThreadsAnyPriority() + MaxResolverThreadsHighPriority();
+}
+
+} // namespace net
+} // namespace mozilla
+
+#define TRR_DISABLED(x) \
+ (((x) == nsIDNSService::MODE_NATIVEONLY) || \
+ ((x) == nsIDNSService::MODE_TRROFF))
+
+extern mozilla::Atomic<bool, mozilla::Relaxed> gNativeIsLocalhost;
+
+#define MAX_NON_PRIORITY_REQUESTS 150
+
+class AHostResolver {
+ public:
+ AHostResolver() = default;
+ virtual ~AHostResolver() = default;
+ NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+
+ enum LookupStatus {
+ LOOKUP_OK,
+ LOOKUP_RESOLVEAGAIN,
+ };
+
+ virtual LookupStatus CompleteLookup(nsHostRecord*, nsresult,
+ mozilla::net::AddrInfo*, bool pb,
+ const nsACString& aOriginsuffix,
+ mozilla::net::TRRSkippedReason aReason,
+ mozilla::net::TRR*) = 0;
+ virtual LookupStatus CompleteLookupByType(
+ nsHostRecord*, nsresult, mozilla::net::TypeRecordResultType& aResult,
+ mozilla::net::TRRSkippedReason aReason, uint32_t aTtl, bool pb) = 0;
+ virtual nsresult GetHostRecord(const nsACString& host,
+ const nsACString& aTrrServer, uint16_t type,
+ nsIDNSService::DNSFlags flags, uint16_t af,
+ bool pb, const nsCString& originSuffix,
+ nsHostRecord** result) {
+ return NS_ERROR_FAILURE;
+ }
+ virtual nsresult TrrLookup_unlocked(nsHostRecord*,
+ mozilla::net::TRR* pushedTRR = nullptr) {
+ return NS_ERROR_FAILURE;
+ }
+ virtual void MaybeRenewHostRecord(nsHostRecord* aRec) {}
+};
+
+/**
+ * nsHostResolver - an asynchronous host name resolver.
+ */
+class nsHostResolver : public nsISupports, public AHostResolver {
+ using CondVar = mozilla::CondVar;
+ using Mutex = mozilla::Mutex;
+
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ /**
+ * creates an addref'd instance of a nsHostResolver object.
+ */
+ static nsresult Create(uint32_t maxCacheEntries, // zero disables cache
+ uint32_t defaultCacheEntryLifetime, // seconds
+ uint32_t defaultGracePeriod, // seconds
+ nsHostResolver** result);
+
+ /**
+ * Set (new) cache limits.
+ */
+ void SetCacheLimits(uint32_t maxCacheEntries, // zero disables cache
+ uint32_t defaultCacheEntryLifetime, // seconds
+ uint32_t defaultGracePeriod); // seconds
+
+ /**
+ * puts the resolver in the shutdown state, which will cause any pending
+ * callbacks to be detached. any future calls to ResolveHost will fail.
+ */
+ void Shutdown();
+
+ /**
+ * resolve the given hostname and originAttributes asynchronously. the caller
+ * can synthesize a synchronous host lookup using a lock and a cvar. as noted
+ * above the callback will occur re-entrantly from an unspecified thread. the
+ * host lookup cannot be canceled (cancelation can be layered above this by
+ * having the callback implementation return without doing anything).
+ */
+ nsresult ResolveHost(const nsACString& aHost, const nsACString& trrServer,
+ int32_t aPort, uint16_t type,
+ const mozilla::OriginAttributes& aOriginAttributes,
+ nsIDNSService::DNSFlags flags, uint16_t af,
+ nsResolveHostCallback* callback);
+
+ nsHostRecord* InitRecord(const nsHostKey& key);
+ mozilla::net::NetworkConnectivityService* GetNCS() {
+ return mNCS;
+ } // This is actually a singleton
+
+ /**
+ * return a resolved hard coded loopback dns record for the specified key
+ */
+ already_AddRefed<nsHostRecord> InitLoopbackRecord(const nsHostKey& key,
+ nsresult* aRv);
+
+ /**
+ * removes the specified callback from the nsHostRecord for the given
+ * hostname, originAttributes, flags, and address family. these parameters
+ * should correspond to the parameters passed to ResolveHost. this function
+ * executes the callback if the callback is still pending with the given
+ * status.
+ */
+ void DetachCallback(const nsACString& hostname, const nsACString& trrServer,
+ uint16_t type,
+ const mozilla::OriginAttributes& aOriginAttributes,
+ nsIDNSService::DNSFlags flags, uint16_t af,
+ nsResolveHostCallback* callback, nsresult status);
+
+ /**
+ * Cancels an async request associated with the hostname, originAttributes,
+ * flags, address family and listener. Cancels first callback found which
+ * matches these criteria. These parameters should correspond to the
+ * parameters passed to ResolveHost. If this is the last callback associated
+ * with the host record, it is removed from any request queues it might be on.
+ */
+ void CancelAsyncRequest(const nsACString& host, const nsACString& trrServer,
+ uint16_t type,
+ const mozilla::OriginAttributes& aOriginAttributes,
+ nsIDNSService::DNSFlags flags, uint16_t af,
+ nsIDNSListener* aListener, nsresult status);
+ /**
+ * values for the flags parameter passed to ResolveHost and DetachCallback
+ * that may be bitwise OR'd together.
+ *
+ * NOTE: in this implementation, these flags correspond exactly in value
+ * to the flags defined on nsIDNSService.
+ */
+ enum {
+ RES_BYPASS_CACHE = nsIDNSService::RESOLVE_BYPASS_CACHE,
+ RES_CANON_NAME = nsIDNSService::RESOLVE_CANONICAL_NAME,
+ RES_PRIORITY_MEDIUM = nsHostRecord::DNS_PRIORITY_MEDIUM,
+ RES_PRIORITY_LOW = nsHostRecord::DNS_PRIORITY_LOW,
+ RES_SPECULATE = nsIDNSService::RESOLVE_SPECULATE,
+ // RES_DISABLE_IPV6 = nsIDNSService::RESOLVE_DISABLE_IPV6, // Not used
+ RES_OFFLINE = nsIDNSService::RESOLVE_OFFLINE,
+ // RES_DISABLE_IPv4 = nsIDNSService::RESOLVE_DISABLE_IPV4, // Not Used
+ RES_ALLOW_NAME_COLLISION = nsIDNSService::RESOLVE_ALLOW_NAME_COLLISION,
+ RES_DISABLE_TRR = nsIDNSService::RESOLVE_DISABLE_TRR,
+ RES_REFRESH_CACHE = nsIDNSService::RESOLVE_REFRESH_CACHE,
+ RES_IP_HINT = nsIDNSService::RESOLVE_IP_HINT
+ };
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
+ /**
+ * Flush the DNS cache.
+ */
+ void FlushCache(bool aTrrToo);
+
+ LookupStatus CompleteLookup(nsHostRecord*, nsresult, mozilla::net::AddrInfo*,
+ bool pb, const nsACString& aOriginsuffix,
+ mozilla::net::TRRSkippedReason aReason,
+ mozilla::net::TRR* aTRRRequest) override;
+ LookupStatus CompleteLookupByType(nsHostRecord*, nsresult,
+ mozilla::net::TypeRecordResultType& aResult,
+ mozilla::net::TRRSkippedReason aReason,
+ uint32_t aTtl, bool pb) override;
+ nsresult GetHostRecord(const nsACString& host, const nsACString& trrServer,
+ uint16_t type, nsIDNSService::DNSFlags flags,
+ uint16_t af, bool pb, const nsCString& originSuffix,
+ nsHostRecord** result) override;
+ nsresult TrrLookup_unlocked(nsHostRecord*,
+ mozilla::net::TRR* pushedTRR = nullptr) override;
+ static nsIDNSService::ResolverMode Mode();
+
+ virtual void MaybeRenewHostRecord(nsHostRecord* aRec) override;
+
+ // Records true if the TRR service is enabled for the record's effective
+ // TRR mode. Also records the TRRSkipReason when the TRR service is not
+ // available/enabled.
+ bool TRRServiceEnabledForRecord(nsHostRecord* aRec) MOZ_REQUIRES(mLock);
+
+ private:
+ explicit nsHostResolver(uint32_t maxCacheEntries,
+ uint32_t defaultCacheEntryLifetime,
+ uint32_t defaultGracePeriod);
+ virtual ~nsHostResolver();
+
+ bool DoRetryTRR(AddrHostRecord* aAddrRec,
+ const mozilla::MutexAutoLock& aLock);
+ bool MaybeRetryTRRLookup(
+ AddrHostRecord* aAddrRec, nsresult aFirstAttemptStatus,
+ mozilla::net::TRRSkippedReason aFirstAttemptSkipReason,
+ nsresult aChannelStatus, const mozilla::MutexAutoLock& aLock);
+
+ LookupStatus CompleteLookupLocked(nsHostRecord*, nsresult,
+ mozilla::net::AddrInfo*, bool pb,
+ const nsACString& aOriginsuffix,
+ mozilla::net::TRRSkippedReason aReason,
+ mozilla::net::TRR* aTRRRequest,
+ const mozilla::MutexAutoLock& aLock)
+ MOZ_REQUIRES(mLock);
+ LookupStatus CompleteLookupByTypeLocked(
+ nsHostRecord*, nsresult, mozilla::net::TypeRecordResultType& aResult,
+ mozilla::net::TRRSkippedReason aReason, uint32_t aTtl, bool pb,
+ const mozilla::MutexAutoLock& aLock) MOZ_REQUIRES(mLock);
+ nsresult Init();
+ static void ComputeEffectiveTRRMode(nsHostRecord* aRec);
+ nsresult NativeLookup(nsHostRecord* aRec,
+ const mozilla::MutexAutoLock& aLock);
+ nsresult TrrLookup(nsHostRecord*, const mozilla::MutexAutoLock& aLock,
+ mozilla::net::TRR* pushedTRR = nullptr);
+
+ // Kick-off a name resolve operation, using native resolver and/or TRR
+ nsresult NameLookup(nsHostRecord* aRec, const mozilla::MutexAutoLock& aLock);
+ bool GetHostToLookup(nsHostRecord** result);
+ void MaybeRenewHostRecordLocked(nsHostRecord* aRec,
+ const mozilla::MutexAutoLock& aLock)
+ MOZ_REQUIRES(mLock);
+
+ // Cancels host records in the pending queue and also
+ // calls CompleteLookup with the NS_ERROR_ABORT result code.
+ void ClearPendingQueue(mozilla::LinkedList<RefPtr<nsHostRecord>>& aPendingQ);
+ nsresult ConditionallyCreateThread(nsHostRecord* rec) MOZ_REQUIRES(mLock);
+
+ /**
+ * Starts a new lookup in the background for entries that are in the grace
+ * period with a failed connect or all cached entries are negative.
+ */
+ nsresult ConditionallyRefreshRecord(nsHostRecord* rec, const nsACString& host,
+ const mozilla::MutexAutoLock& aLock)
+ MOZ_REQUIRES(mLock);
+
+ void OnResolveComplete(nsHostRecord* aRec,
+ const mozilla::MutexAutoLock& aLock)
+ MOZ_REQUIRES(mLock);
+
+ void AddToEvictionQ(nsHostRecord* rec, const mozilla::MutexAutoLock& aLock)
+ MOZ_REQUIRES(mLock);
+
+ void ThreadFunc();
+
+ // Resolve the host from the DNS cache.
+ already_AddRefed<nsHostRecord> FromCache(nsHostRecord* aRec,
+ const nsACString& aHost,
+ uint16_t aType, nsresult& aStatus,
+ const mozilla::MutexAutoLock& aLock)
+ MOZ_REQUIRES(mLock);
+ // Called when the host name is an IP address and has been passed.
+ already_AddRefed<nsHostRecord> FromCachedIPLiteral(nsHostRecord* aRec);
+ // Like the above function, but the host name is not parsed to NetAddr yet.
+ already_AddRefed<nsHostRecord> FromIPLiteral(
+ AddrHostRecord* aAddrRec, const mozilla::net::NetAddr& aAddr);
+ // Called to check if we have an AF_UNSPEC entry in the cache.
+ already_AddRefed<nsHostRecord> FromUnspecEntry(
+ nsHostRecord* aRec, const nsACString& aHost, const nsACString& aTrrServer,
+ const nsACString& aOriginSuffix, uint16_t aType,
+ nsIDNSService::DNSFlags aFlags, uint16_t af, bool aPb, nsresult& aStatus)
+ MOZ_REQUIRES(mLock);
+
+ enum {
+ METHOD_HIT = 1,
+ METHOD_RENEWAL = 2,
+ METHOD_NEGATIVE_HIT = 3,
+ METHOD_LITERAL = 4,
+ METHOD_OVERFLOW = 5,
+ METHOD_NETWORK_FIRST = 6,
+ METHOD_NETWORK_SHARED = 7
+ };
+
+ uint32_t mMaxCacheEntries = 0;
+ uint32_t mDefaultCacheLifetime = 0; // granularity seconds
+ uint32_t mDefaultGracePeriod = 0; // granularity seconds
+ // mutable so SizeOfIncludingThis can be const
+ mutable Mutex mLock{"nsHostResolver.mLock"};
+ CondVar mIdleTaskCV;
+ nsRefPtrHashtable<nsGenericHashKey<nsHostKey>, nsHostRecord> mRecordDB
+ MOZ_GUARDED_BY(mLock);
+ PRTime mCreationTime;
+ mozilla::TimeDuration mLongIdleTimeout;
+ mozilla::TimeDuration mShortIdleTimeout;
+
+ RefPtr<nsIThreadPool> mResolverThreads;
+ RefPtr<mozilla::net::NetworkConnectivityService>
+ mNCS; // reference to a singleton
+ mozilla::net::HostRecordQueue mQueue MOZ_GUARDED_BY(mLock);
+ mozilla::Atomic<bool> mShutdown MOZ_GUARDED_BY(mLock){true};
+ mozilla::Atomic<uint32_t> mNumIdleTasks MOZ_GUARDED_BY(mLock){0};
+ mozilla::Atomic<uint32_t> mActiveTaskCount MOZ_GUARDED_BY(mLock){0};
+ mozilla::Atomic<uint32_t> mActiveAnyThreadCount MOZ_GUARDED_BY(mLock){0};
+
+ // Set the expiration time stamps appropriately.
+ void PrepareRecordExpirationAddrRecord(AddrHostRecord* rec) const;
+
+ public:
+ /*
+ * Called by the networking dashboard via the DnsService2
+ */
+ void GetDNSCacheEntries(nsTArray<mozilla::net::DNSCacheEntries>*);
+};
+
+#endif // nsHostResolver_h__
diff --git a/netwerk/dns/nsIDNKitInterface.h b/netwerk/dns/nsIDNKitInterface.h
new file mode 100644
index 0000000000..3e0f0d60ac
--- /dev/null
+++ b/netwerk/dns/nsIDNKitInterface.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2000-2002 Japan Network Information Center. All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set forth bellow.
+ *
+ * LICENSE TERMS AND CONDITIONS
+ *
+ * The following License Terms and Conditions apply, unless a different
+ * license is obtained from Japan Network Information Center ("JPNIC"),
+ * a Japanese association, Kokusai-Kougyou-Kanda Bldg 6F, 2-3-4 Uchi-Kanda,
+ * Chiyoda-ku, Tokyo 101-0047, Japan.
+
+ * 1. Use, Modification and Redistribution (including distribution of any
+ * modified or derived work) in source and/or binary forms is permitted
+ * under this License Terms and Conditions.
+ *
+ * 2. Redistribution of source code must retain the copyright notices as they
+ * appear in each source code file, this License Terms and Conditions.
+ *
+ * 3. Redistribution in binary form must reproduce the Copyright Notice,
+ * this License Terms and Conditions, in the documentation and/or other
+ * materials provided with the distribution. For the purposes of binary
+ * distribution the "Copyright Notice" refers to the following language:
+ * "Copyright (c) 2000-2002 Japan Network Information Center. All rights reserved."
+ *
+ * 4. The name of JPNIC may not be used to endorse or promote products
+ * derived from this Software without specific prior written approval of
+ * JPNIC.
+ *
+ * 5. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY JPNIC
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JPNIC BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#ifndef nsIDNKitWrapper_h__
+#define nsIDNKitWrapper_h__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * libidnkit result code.
+ */
+typedef enum {
+ idn_success,
+ idn_notfound,
+ idn_invalid_encoding,
+ idn_invalid_syntax,
+ idn_invalid_name,
+ idn_invalid_message,
+ idn_invalid_action,
+ idn_invalid_codepoint,
+ idn_invalid_length,
+ idn_buffer_overflow,
+ idn_noentry,
+ idn_nomemory,
+ idn_nofile,
+ idn_nomapping,
+ idn_context_required,
+ idn_prohibited,
+ idn_failure /* !!This must be the last one!! */
+} idn_result_t;
+
+/*
+ * BIDI type codes.
+ */
+typedef enum {
+ idn_biditype_r_al,
+ idn_biditype_l,
+ idn_biditype_others
+} idn_biditype_t;
+
+/*
+ * A Handle for nameprep operations.
+ */
+typedef struct idn_nameprep *idn_nameprep_t;
+
+
+/*
+ * The latest version of nameprep.
+ */
+#define IDN_NAMEPREP_CURRENT "nameprep-11"
+
+#undef assert
+#define assert(a)
+#define TRACE(a)
+
+
+/* race.c */
+idn_result_t race_decode_decompress(const char *from,
+ uint16_t *buf,
+ size_t buflen);
+idn_result_t race_compress_encode(const uint16_t *p,
+ int compress_mode,
+ char *to, size_t tolen);
+int get_compress_mode(uint16_t *p);
+
+
+/* nameprep.c */
+
+/*
+ * Create a handle for nameprep operations.
+ * The handle is stored in '*handlep', which is used other functions
+ * in this module.
+ * The version of the NAMEPREP specification can be specified with
+ * 'version' parameter. If 'version' is nullptr, the latest version
+ * is used.
+ *
+ * Returns:
+ * idn_success -- ok.
+ * idn_notfound -- specified version not found.
+ */
+idn_result_t
+idn_nameprep_create(const char *version, idn_nameprep_t *handlep);
+
+/*
+ * Close a handle, which was created by 'idn_nameprep_create'.
+ */
+void
+idn_nameprep_destroy(idn_nameprep_t handle);
+
+/*
+ * Perform character mapping on an UCS4 string specified by 'from', and
+ * store the result into 'to', whose length is specified by 'tolen'.
+ *
+ * Returns:
+ * idn_success -- ok.
+ * idn_buffer_overflow -- result buffer is too small.
+ */
+idn_result_t
+idn_nameprep_map(idn_nameprep_t handle, const uint32_t *from,
+ uint32_t *to, size_t tolen);
+
+/*
+ * Check if an UCS4 string 'str' contains any prohibited characters specified
+ * by the draft. If found, the pointer to the first such character is stored
+ * into '*found'. Otherwise '*found' will be nullptr.
+ *
+ * Returns:
+ * idn_success -- check has been done properly. (But this
+ * does not mean that no prohibited character
+ * was found. Check '*found' to see the
+ * result.)
+ */
+idn_result_t
+idn_nameprep_isprohibited(idn_nameprep_t handle, const uint32_t *str,
+ const uint32_t **found);
+
+/*
+ * Check if an UCS4 string 'str' contains any unassigned characters specified
+ * by the draft. If found, the pointer to the first such character is stored
+ * into '*found'. Otherwise '*found' will be nullptr.
+ *
+ * Returns:
+ * idn_success -- check has been done properly. (But this
+ * does not mean that no unassinged character
+ * was found. Check '*found' to see the
+ * result.)
+ */
+idn_result_t
+idn_nameprep_isunassigned(idn_nameprep_t handle, const uint32_t *str,
+ const uint32_t **found);
+
+/*
+ * Check if an UCS4 string 'str' is valid string specified by ``bidi check''
+ * of the draft. If it is not valid, the pointer to the first invalid
+ * character is stored into '*found'. Otherwise '*found' will be nullptr.
+ *
+ * Returns:
+ * idn_success -- check has been done properly. (But this
+ * does not mean that the string was valid.
+ * Check '*found' to see the result.)
+ */
+idn_result_t
+idn_nameprep_isvalidbidi(idn_nameprep_t handle, const uint32_t *str,
+ const uint32_t **found);
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* nsIDNKitWrapper_h__ */
diff --git a/netwerk/dns/nsIDNSAdditionalInfo.idl b/netwerk/dns/nsIDNSAdditionalInfo.idl
new file mode 100644
index 0000000000..4f266d7937
--- /dev/null
+++ b/netwerk/dns/nsIDNSAdditionalInfo.idl
@@ -0,0 +1,12 @@
+/* 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 "nsISupports.idl"
+
+[scriptable, builtinclass, uuid(74db2955-6298-4d82-a3b9-7f9e8ba9e854)]
+interface nsIDNSAdditionalInfo : nsISupports
+{
+ readonly attribute int32_t port;
+ readonly attribute ACString resolverURL;
+};
diff --git a/netwerk/dns/nsIDNSByTypeRecord.idl b/netwerk/dns/nsIDNSByTypeRecord.idl
new file mode 100644
index 0000000000..1d11325af8
--- /dev/null
+++ b/netwerk/dns/nsIDNSByTypeRecord.idl
@@ -0,0 +1,133 @@
+/* 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 "nsIDNSRecord.idl"
+
+%{ C++
+
+#include "mozilla/Maybe.h"
+
+#include "nsTArrayForwardDeclare.h"
+#include "nsHttp.h"
+#include "nsStringFwd.h"
+
+namespace mozilla {
+
+template <typename... Ts> class Variant;
+struct Nothing;
+
+namespace net {
+ struct SVCB;
+ using TypeRecordResultType =
+ Variant<Nothing, CopyableTArray<nsCString>, CopyableTArray<SVCB>>;
+}
+}
+
+%}
+
+[ref] native CStringArrayRef(CopyableTArray<nsCString>);
+native TypeResult(mozilla::net::TypeRecordResultType);
+
+native MaybePort(mozilla::Maybe<uint16_t>);
+native MaybeAlpnTuple(mozilla::Maybe<std::tuple<nsCString, mozilla::net::SupportedAlpnRank>>);
+
+[scriptable, uuid(5d13241b-9d46-448a-90d8-77c418491026)]
+interface nsIDNSByTypeRecord : nsIDNSRecord
+{
+ /**
+ * Returns DNS request type that was made for this request.
+ */
+ readonly attribute unsigned long type;
+
+ [noscript] readonly attribute TypeResult results;
+};
+
+[scriptable, uuid(2a71750d-cb21-45f1-9e1c-666d18dd7645)]
+interface nsIDNSTXTRecord : nsISupports
+{
+ CStringArrayRef getRecords();
+
+ /*
+ * Return concatenated strings.
+ */
+ ACString getRecordsAsOneString();
+};
+
+[scriptable, uuid(2979ceaa-9c7e-49de-84b8-ea81c16aebf1)]
+interface nsISVCParam : nsISupports {
+ readonly attribute uint16_t type;
+};
+
+[scriptable, uuid(0dc58309-4d67-4fc4-a4e3-38dbde9d9f4c)]
+interface nsISVCParamAlpn : nsISupports {
+ readonly attribute Array<ACString> alpn;
+};
+
+[scriptable, uuid(b3ed89c3-2ae6-4c92-8176-b76bc2437fcb)]
+interface nsISVCParamNoDefaultAlpn : nsISupports {
+};
+
+[scriptable, uuid(a37c7bcb-bfcd-4ab4-b826-cc583859ba73)]
+interface nsISVCParamPort : nsISupports {
+ readonly attribute uint16_t port;
+};
+
+[scriptable, uuid(d3163d2f-0bbe-47d4-bcac-db3fb1433b39)]
+interface nsISVCParamIPv4Hint : nsISupports {
+ readonly attribute Array<nsINetAddr> ipv4Hint;
+};
+
+[scriptable, uuid(1f31e41d-b6d8-4796-b12a-82ef8d2b0e43)]
+interface nsISVCParamEchConfig : nsISupports {
+ readonly attribute ACString echconfig;
+};
+
+[scriptable, uuid(5100bce4-9d3b-42e1-a3c9-0f386bbc9dad)]
+interface nsISVCParamIPv6Hint : nsISupports {
+ readonly attribute Array<nsINetAddr> ipv6Hint;
+};
+
+[scriptable, uuid(bdcef040-452e-11eb-b378-0242ac130002)]
+interface nsISVCParamODoHConfig : nsISupports {
+ readonly attribute ACString ODoHConfig;
+};
+
+[scriptable, builtinclass, uuid(a4da5645-2160-4439-bd11-540a2d26c989)]
+interface nsISVCBRecord : nsISupports {
+ readonly attribute uint16_t priority;
+ readonly attribute ACString name;
+ [noscript, nostdcall, notxpcom] readonly attribute MaybePort port;
+ [noscript, nostdcall, notxpcom] readonly attribute MaybeAlpnTuple alpn;
+ readonly attribute ACString selectedAlpn;
+ readonly attribute ACString echConfig;
+ readonly attribute ACString ODoHConfig;
+ readonly attribute bool hasIPHintAddress;
+ readonly attribute Array<nsISVCParam> values;
+};
+
+[scriptable, uuid(5b649e95-e0d3-422b-99a6-79d70a041387)]
+interface nsIDNSHTTPSSVCRecord : nsISupports
+{
+ readonly attribute Array<nsISVCBRecord> records;
+ nsISVCBRecord GetServiceModeRecord(in boolean aNoHttp2, in boolean aNoHttp3);
+ /**
+ * Returns true if one of SVCB records has IPv4 or IPv6 hint addresses.
+ */
+ readonly attribute boolean hasIPAddresses;
+
+ /**
+ * Returns true when all names of SVCB records are in exclusion list.
+ */
+ readonly attribute boolean allRecordsExcluded;
+
+ /**
+ * Returns the ttl of this record.
+ */
+ readonly attribute uint32_t ttl;
+
+ Array<nsISVCBRecord> GetAllRecordsWithEchConfig(in boolean aNoHttp2,
+ in boolean aNoHttp3,
+ out boolean aAllRecordsHaveEchConfig,
+ out boolean aAllRecordsInH3ExcludedList);
+};
diff --git a/netwerk/dns/nsIDNSListener.idl b/netwerk/dns/nsIDNSListener.idl
new file mode 100644
index 0000000000..d925294733
--- /dev/null
+++ b/netwerk/dns/nsIDNSListener.idl
@@ -0,0 +1,34 @@
+/* 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 "nsISupports.idl"
+
+interface nsICancelable;
+interface nsIDNSRecord;
+interface nsIDNSByTypeRecord;
+
+/**
+ * nsIDNSListener
+ */
+[scriptable, uuid(27d49bfe-280c-49e0-bbaa-f6200c232c3d)]
+interface nsIDNSListener : nsISupports
+{
+ /**
+ * called when an asynchronous host lookup completes.
+ *
+ * @param aRequest
+ * the value returned from asyncResolve.
+ * @param aRecord
+ * the DNS record corresponding to the hostname that was resolved.
+ * this parameter is null if there was an error.
+ * depending on the type parameter passed to asyncResolve() the
+ * caller should QueryInterface to either nsIDNSAddrRecord or
+ * nsIDNSByTypeRecord.
+ * @param aStatus
+ * if the lookup failed, this parameter gives the reason.
+ */
+ void onLookupComplete(in nsICancelable aRequest,
+ in nsIDNSRecord aRecord,
+ in nsresult aStatus);
+};
diff --git a/netwerk/dns/nsIDNSRecord.idl b/netwerk/dns/nsIDNSRecord.idl
new file mode 100644
index 0000000000..82682bd900
--- /dev/null
+++ b/netwerk/dns/nsIDNSRecord.idl
@@ -0,0 +1,154 @@
+/* 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 "nsISupports.idl"
+#include "nsIRequest.idl"
+#include "nsITRRSkipReason.idl"
+
+%{ C++
+namespace mozilla {
+namespace net {
+union NetAddr;
+}
+}
+#include "nsTArrayForwardDeclare.h"
+%}
+native NetAddr(mozilla::net::NetAddr);
+[ref] native nsNetAddrTArrayRef(nsTArray<mozilla::net::NetAddr>);
+interface nsINetAddr;
+
+/**
+ * nsIDNSRecord
+ *
+ * this interface represents the result of a DNS lookup. since a DNS
+ * query may return more than one resolved IP address, the record acts
+ * like an enumerator, allowing the caller to easily step through the
+ * list of IP addresses.
+ */
+[scriptable, uuid(f92228ae-c417-4188-a604-0830a95e7eb9)]
+interface nsIDNSRecord : nsISupports
+{
+};
+
+[scriptable, uuid(cb260e20-943f-4309-953b-78c90d3a7638)]
+interface nsIDNSAddrRecord : nsIDNSRecord
+{
+ /**
+ * @return the canonical hostname for this record. this value is empty if
+ * the record was not fetched with the RESOLVE_CANONICAL_NAME flag.
+ *
+ * e.g., www.mozilla.org --> rheet.mozilla.org
+ *
+ * That the result, if IDN will be returned as punycode.
+ * e.g., élève.w3c-test.org --> xn--lve-6lad.w3c-test.org
+ */
+ readonly attribute ACString canonicalName;
+
+ /**
+ * this function copies the value of the next IP address into the
+ * given NetAddr struct and increments the internal address iterator.
+ *
+ * @param aPort
+ * A port number to initialize the NetAddr with.
+ *
+ * @throws NS_ERROR_NOT_AVAILABLE if there is not another IP address in
+ * the record.
+ */
+ [noscript] NetAddr getNextAddr(in uint16_t aPort);
+
+ /**
+ * this function copies the value of all working members of the RR
+ * set into the output array.
+ *
+ * @param aAddressArray
+ * The result set
+ */
+ [noscript] void getAddresses(out nsNetAddrTArrayRef aAddressArray);
+
+ /**
+ * this function returns the value of the next IP address as a
+ * scriptable address and increments the internal address iterator.
+ *
+ * @param aPort
+ * A port number to initialize the nsINetAddr with.
+ *
+ * @throws NS_ERROR_NOT_AVAILABLE if there is not another IP address in
+ * the record.
+ */
+ nsINetAddr getScriptableNextAddr(in uint16_t aPort);
+
+ /**
+ * this function returns the value of the next IP address as a
+ * string and increments the internal address iterator.
+ *
+ * @throws NS_ERROR_NOT_AVAILABLE if there is not another IP address in
+ * the record.
+ */
+ ACString getNextAddrAsString();
+
+ /**
+ * this function returns true if there is another address in the record.
+ */
+ boolean hasMore();
+
+ /**
+ * this function resets the internal address iterator to the first
+ * address in the record.
+ */
+ void rewind();
+
+ /**
+ * This function indicates that the last address obtained via getNextAddr*()
+ * was not usuable and should be skipped in future uses of this
+ * record if other addresses are available.
+ *
+ * @param aPort is the port number associated with the failure, if any.
+ * It may be zero if not applicable.
+ */
+ void reportUnusable(in uint16_t aPort);
+
+ /**
+ * Record retreived with TRR.
+ */
+ bool IsTRR();
+
+ /**
+ * Record is resolved in socket process.
+ */
+ bool resolvedInSocketProcess();
+
+ /**
+ * This attribute is only set if TRR is used and it measures time between
+ * asyncOpen on a channel and the time parsing of response if done.
+ * Thee time is measured in milliseconds.
+ */
+ readonly attribute double trrFetchDuration;
+
+ /**
+ * This attribute is only set if TRR is used and it measures time between
+ * sending a request and the time response is received from the network.
+ * This time is similat to the time above, but exludes a time needed to
+ * make a connection and a time neededto parse results (this also does not
+ * include delays that may be introduce because parsing is perform on the main
+ * thread).
+ * Thee time is measured in milliseconds.
+ */
+ readonly attribute double trrFetchDurationNetworkOnly;
+
+ /**
+ * The TRR mode this record is used.
+ */
+ readonly attribute nsIRequest_TRRMode effectiveTRRMode;
+
+ /**
+ * If the DNS request didn't use TRR, this value
+ * contains the reason why that was skipped.
+ */
+ readonly attribute nsITRRSkipReason_value trrSkipReason;
+
+ /**
+ * Returns the ttl of this record.
+ */
+ readonly attribute uint32_t ttl;
+};
diff --git a/netwerk/dns/nsIDNSService.idl b/netwerk/dns/nsIDNSService.idl
new file mode 100644
index 0000000000..c1aecccd8c
--- /dev/null
+++ b/netwerk/dns/nsIDNSService.idl
@@ -0,0 +1,383 @@
+/* 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 "nsISupports.idl"
+#include "nsIRequest.idl"
+#include "nsITRRSkipReason.idl"
+
+%{ C++
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/TypedEnumBits.h"
+%}
+
+interface nsICancelable;
+interface nsIEventTarget;
+interface nsIDNSRecord;
+interface nsIDNSListener;
+interface nsIDNSAdditionalInfo;
+
+%{C++
+#include "nsTArrayForwardDeclare.h"
+namespace mozilla { namespace net {
+ struct DNSCacheEntries;
+} }
+%}
+
+[ptr] native EntriesArray(nsTArray<mozilla::net::DNSCacheEntries>);
+[ref] native OriginAttributes(const mozilla::OriginAttributes);
+
+/**
+ * nsIDNSService
+ */
+[scriptable, builtinclass, uuid(de5642c6-61fc-4fcf-9a47-03226b0d4e21)]
+interface nsIDNSService : nsISupports
+{
+ /**
+ * These are the dns request types that are currently supported.
+ * RESOLVE_TYPE_DEFAULT is standard A/AAAA lookup
+ */
+ cenum ResolveType : 16 {
+ RESOLVE_TYPE_DEFAULT = 0,
+ RESOLVE_TYPE_TXT = 16,
+ RESOLVE_TYPE_HTTPSSVC = 65,
+ };
+
+ cenum ResolverMode : 32 { // 32 bits to allow this to be stored in an Atomic
+ MODE_NATIVEONLY = 0, // TRR OFF (by default)
+ MODE_RESERVED1 = 1, // Reserved value. Used to be parallel resolve.
+ MODE_TRRFIRST = 2, // fallback to native on TRR failure
+ MODE_TRRONLY = 3, // don't even fallback
+ MODE_RESERVED4 = 4, // Reserved value. Used to be race TRR with native.
+ MODE_TRROFF = 5 // identical to MODE_NATIVEONLY but explicitly selected
+ };
+
+ cenum DNSFlags : 32 {
+ RESOLVE_DEFAULT_FLAGS = 0,
+ // if set, this flag suppresses the internal DNS lookup cache.
+ RESOLVE_BYPASS_CACHE = (1 << 0),
+ // if set, the canonical name of the specified host will be queried.
+ RESOLVE_CANONICAL_NAME = (1 << 1),
+ // If PRIORITY flags are set, the query is given lower priority.
+ // Medium takes precedence if both MEDIUM and LOW are used.
+ RESOLVE_PRIORITY_MEDIUM = (1 << 2),
+ RESOLVE_PRIORITY_LOW = (1 << 3),
+ // if set, indicates request is speculative. Speculative requests
+ // return errors if prefetching is disabled by configuration.
+ RESOLVE_SPECULATE = (1 << 4),
+ // If set, only IPv4 addresses will be returned from resolve/asyncResolve.
+ RESOLVE_DISABLE_IPV6 = (1 << 5),
+ // If set, only literals and cached entries will be returned from resolve/asyncResolve.
+ RESOLVE_OFFLINE = (1 << 6),
+ // If set, only IPv6 addresses will be returned from resolve/asyncResolve.
+ RESOLVE_DISABLE_IPV4 = (1 << 7),
+ // If set, allow name collision results (127.0.53.53) which are normally filtered.
+ RESOLVE_ALLOW_NAME_COLLISION = (1 << 8),
+ // If set, do not use TRR for resolving the host name.
+ RESOLVE_DISABLE_TRR = (1 << 9),
+ // if set (together with RESOLVE_BYPASS_CACHE), invalidate the DNS
+ // existing cache entry first (if existing) then make a new resolve.
+ RESOLVE_REFRESH_CACHE = (1 << 10),
+ // These two bits encode the TRR mode of the request.
+ // Use the static helper methods GetFlagsFromTRRMode and
+ // GetTRRModeFromFlags to convert between the TRR mode and flags.
+ RESOLVE_TRR_MODE_MASK = (1 << 11) | (1 << 12),
+ RESOLVE_TRR_DISABLED_MODE = (1 << 11),
+ // Force resolution even when SOCKS proxy with DNS forwarding is configured.
+ // Only to be used for the proxy host resolution.
+ RESOLVE_IGNORE_SOCKS_DNS = (1 << 13),
+ // If set, only cached IP hint addresses will be returned from resolve/asyncResolve.
+ RESOLVE_IP_HINT = (1 << 14),
+ // If set, the DNS service will pass a DNS record to
+ // OnLookupComplete even when there was a resolution error.
+ RESOLVE_WANT_RECORD_ON_ERROR = (1 << 16),
+
+ // Bitflag containing all possible flags.
+ ALL_DNSFLAGS_BITS = ((1 << 17) - 1),
+ };
+
+ cenum ConfirmationState : 8 {
+ CONFIRM_OFF = 0,
+ CONFIRM_TRYING_OK = 1,
+ CONFIRM_OK = 2,
+ CONFIRM_FAILED = 3,
+ CONFIRM_TRYING_FAILED = 4,
+ CONFIRM_DISABLED = 5,
+ };
+
+ /**
+ * kicks off an asynchronous host lookup.
+ *
+ * @param aHostName
+ * the hostname or IP-address-literal to resolve.
+ * @param aType
+ * one of RESOLVE_TYPE_*.
+ * @param aFlags
+ * a bitwise OR of the RESOLVE_ prefixed constants defined below.
+ * @param aInfo
+ * a AdditionalInfo object that holds information about:
+ * - the resolver to be used such as TRR URL
+ * - the port number that could be used to construct a QNAME
+ * for HTTPS RR
+ * If null we use the default configuration.
+ * @param aListener
+ * the listener to be notified when the result is available.
+ * @param aListenerTarget
+ * optional parameter (may be null). if non-null, this parameter
+ * specifies the nsIEventTarget of the thread on which the
+ * listener's onLookupComplete should be called. however, if this
+ * parameter is null, then onLookupComplete will be called on an
+ * unspecified thread (possibly recursively).
+ * @param aOriginAttributes
+ * the originAttribute for this resolving, the DNS cache will be
+ * separated according to this originAttributes. This attribute is
+ * optional to avoid breaking add-ons.
+ *
+ * @return An object that can be used to cancel the host lookup.
+ */
+ [implicit_jscontext, optional_argc]
+ nsICancelable asyncResolve(in AUTF8String aHostName,
+ in nsIDNSService_ResolveType aType,
+ in nsIDNSService_DNSFlags aFlags,
+ in nsIDNSAdditionalInfo aInfo,
+ in nsIDNSListener aListener,
+ in nsIEventTarget aListenerTarget,
+ [optional] in jsval aOriginAttributes);
+
+ [notxpcom]
+ nsresult asyncResolveNative(in AUTF8String aHostName,
+ in nsIDNSService_ResolveType aType,
+ in nsIDNSService_DNSFlags aFlags,
+ in nsIDNSAdditionalInfo aInfo,
+ in nsIDNSListener aListener,
+ in nsIEventTarget aListenerTarget,
+ in OriginAttributes aOriginAttributes,
+ out nsICancelable aResult);
+
+ /**
+ * Returns a new nsIDNSAdditionalInfo object containing the URL we pass to it.
+ */
+ nsIDNSAdditionalInfo newAdditionalInfo(in AUTF8String aTrrURL,
+ in int32_t aPort);
+
+ /**
+ * Attempts to cancel a previously requested async DNS lookup
+ *
+ * @param aHostName
+ * the hostname or IP-address-literal to resolve.
+ * @param aType
+ * one of RESOLVE_TYPE_*.
+ * @param aFlags
+ * a bitwise OR of the RESOLVE_ prefixed constants defined below.
+ * @param aInfo
+ * a AdditionalInfo object that holds information about:
+ * - the resolver to be used such as TRR URL
+ * - the port number that could be used to construct a QNAME
+ * for HTTPS RR
+ * If null we use the default configuration.
+ * @param aListener
+ * the original listener which was to be notified about the host lookup
+ * result - used to match request information to requestor.
+ * @param aReason
+ * nsresult reason for the cancellation
+ * @param aOriginAttributes
+ * the originAttribute for this resolving. This attribute is optional
+ * to avoid breaking add-ons.
+ */
+ [implicit_jscontext, optional_argc]
+ void cancelAsyncResolve(in AUTF8String aHostName,
+ in nsIDNSService_ResolveType aType,
+ in nsIDNSService_DNSFlags aFlags,
+ in nsIDNSAdditionalInfo aResolver,
+ in nsIDNSListener aListener,
+ in nsresult aReason,
+ [optional] in jsval aOriginAttributes);
+
+ [notxpcom]
+ nsresult cancelAsyncResolveNative(in AUTF8String aHostName,
+ in nsIDNSService_ResolveType aType,
+ in nsIDNSService_DNSFlags aFlags,
+ in nsIDNSAdditionalInfo aResolver,
+ in nsIDNSListener aListener,
+ in nsresult aReason,
+ in OriginAttributes aOriginAttributes);
+
+ /**
+ * called to synchronously resolve a hostname.
+ *
+ * Since this method may block the calling thread for a long period of
+ * time, it may not be accessed from the main thread.
+ *
+ * @param aHostName
+ * the hostname or IP-address-literal to resolve.
+ * @param aFlags
+ * a bitwise OR of the RESOLVE_ prefixed constants defined below.
+ * @param aOriginAttributes
+ * the originAttribute for this resolving, the DNS cache will be
+ * separated according to this originAttributes. This attribute is
+ * optional to avoid breaking add-ons.
+ *
+ * @return DNS record corresponding to the given hostname.
+ * @throws NS_ERROR_UNKNOWN_HOST if host could not be resolved.
+ * @throws NS_ERROR_NOT_AVAILABLE if accessed from the main thread.
+ */
+ [implicit_jscontext, optional_argc]
+ nsIDNSRecord resolve(in AUTF8String aHostName,
+ in nsIDNSService_DNSFlags aFlags,
+ [optional] in jsval aOriginAttributes);
+
+ [notxpcom]
+ nsresult resolveNative(in AUTF8String aHostName,
+ in nsIDNSService_DNSFlags aFlags,
+ in OriginAttributes aOriginAttributes,
+ out nsIDNSRecord aResult);
+
+ /**
+ * The method takes a pointer to an nsTArray
+ * and fills it with cache entry data
+ * Called by the networking dashboard
+ */
+ [noscript] void getDNSCacheEntries(in EntriesArray args);
+
+
+ /**
+ * Clears the DNS cache.
+ * @param aTrrToo
+ * If true we will clear TRR cached entries too. Since these
+ * are resolved remotely it's not necessary to clear them when
+ * the network status changes, but it's sometimes useful to do so
+ * for tests or other situations.
+ */
+ void clearCache(in boolean aTrrToo);
+
+ /**
+ * The method is used only for test purpose. We use this to recheck if
+ * parental control is enabled or not.
+ */
+ void reloadParentalControlEnabled();
+
+ /**
+ * Notifies the TRR service of a TRR that was automatically detected based
+ * on network preferences.
+ */
+ void setDetectedTrrURI(in AUTF8String aURI);
+
+ /**
+ * Stores the result of the TRR heuristic detection.
+ * Will be TRR_OK if no heuristics failed.
+ */
+ void setHeuristicDetectionResult(in nsITRRSkipReason_value value);
+
+ /**
+ * Returns the result of the last TRR heuristic detection.
+ * Will be TRR_OK if no heuristics failed.
+ */
+ readonly attribute nsITRRSkipReason_value heuristicDetectionResult;
+
+ ACString getTRRSkipReasonName(in nsITRRSkipReason_value value);
+
+ /**
+ * The channel status of the last TRR confirmation attempt.
+ * In strict mode it reflects the channel status of the last TRR request.
+ */
+ readonly attribute nsresult lastConfirmationStatus;
+
+ /**
+ * The TRR skip reason of the last TRR confirmation attempt.
+ * In strict mode it reflects the TRR skip reason of the last TRR request.
+ */
+ readonly attribute nsITRRSkipReason_value lastConfirmationSkipReason;
+
+ /**
+ * Notifies the DNS service that we failed to connect to this alternative
+ * endpoint.
+ * @param aOwnerName
+ * The owner name of this HTTPS RRs.
+ * @param aSVCDomainName
+ * The domain name of this alternative endpoint.
+ */
+ [noscript] void ReportFailedSVCDomainName(in ACString aOwnerName,
+ in ACString aSVCDomainName);
+
+ /**
+ * Check if the given domain name was failed to connect to before.
+ * @param aOwnerName
+ * The owner name of this HTTPS RRs.
+ * @param aSVCDomainName
+ * The domain name of this alternative endpoint.
+ */
+ [noscript] boolean IsSVCDomainNameFailed(in ACString aOwnerName,
+ in ACString aSVCDomainName);
+
+ /**
+ * Reset the exclusion list.
+ * @param aOwnerName
+ * The owner name of this HTTPS RRs.
+ */
+ [noscript] void ResetExcludedSVCDomainName(in ACString aOwnerName);
+
+ /**
+ * Returns a string containing the URI currently used by the TRR service.
+ */
+ readonly attribute AUTF8String currentTrrURI;
+
+ /**
+ * Returns the value of the TRR Service's current default mode.
+ */
+ readonly attribute nsIDNSService_ResolverMode currentTrrMode;
+
+ /**
+ * The TRRService's current confirmation state.
+ * This is mostly for testing purposes.
+ */
+ readonly attribute unsigned long currentTrrConfirmationState;
+
+ /**
+ * @return the hostname of the operating system.
+ */
+ readonly attribute AUTF8String myHostName;
+
+ /**
+ * returns the current TRR domain.
+ */
+ readonly attribute ACString trrDomain;
+
+ /**
+ * returns the telemetry key for current TRR domain.
+ */
+ readonly attribute ACString TRRDomainKey;
+
+ /*************************************************************************
+ * Listed below are the various flags that may be OR'd together to form
+ * the aFlags parameter passed to asyncResolve() and resolve().
+ */
+
+%{C++
+ static nsIDNSService::DNSFlags GetFlagsFromTRRMode(nsIRequest::TRRMode aMode) {
+ return static_cast<nsIDNSService::DNSFlags>(static_cast<uint32_t>(aMode) << 11);
+ }
+
+ static nsIRequest::TRRMode GetTRRModeFromFlags(nsIDNSService::DNSFlags aFlags) {
+ return static_cast<nsIRequest::TRRMode>((aFlags & RESOLVE_TRR_MODE_MASK) >> 11);
+ }
+%}
+
+};
+
+%{C++
+
+/**
+ * An observer notification for this topic is sent whenever the URI that the
+ * TRR service is using has changed.
+ */
+#define NS_NETWORK_TRR_URI_CHANGED_TOPIC "network:trr-uri-changed"
+
+/**
+ * An observer notification for this topic is sent whenever the mode that the
+ * TRR service is using has changed.
+ */
+#define NS_NETWORK_TRR_MODE_CHANGED_TOPIC "network:trr-mode-changed"
+
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsIDNSService::DNSFlags)
+
+%}
diff --git a/netwerk/dns/nsIDNService.cpp b/netwerk/dns/nsIDNService.cpp
new file mode 100644
index 0000000000..3db169d3af
--- /dev/null
+++ b/netwerk/dns/nsIDNService.cpp
@@ -0,0 +1,855 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "MainThreadUtils.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/Preferences.h"
+#include "nsIDNService.h"
+#include "nsReadableUtils.h"
+#include "nsCRT.h"
+#include "nsServiceManagerUtils.h"
+#include "nsUnicharUtils.h"
+#include "nsUnicodeProperties.h"
+#include "harfbuzz/hb.h"
+#include "punycode.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Casting.h"
+#include "mozilla/StaticPrefs_network.h"
+#include "mozilla/TextUtils.h"
+#include "mozilla/Utf8.h"
+#include "mozilla/intl/FormatBuffer.h"
+#include "mozilla/intl/UnicodeProperties.h"
+#include "mozilla/intl/UnicodeScriptCodes.h"
+
+#include "ICUUtils.h"
+
+using namespace mozilla;
+using namespace mozilla::intl;
+using namespace mozilla::unicode;
+using namespace mozilla::net;
+using mozilla::Preferences;
+
+// Currently we use the non-transitional processing option -- see
+// http://unicode.org/reports/tr46/
+// To switch to transitional processing, change the value of this flag
+// and kTransitionalProcessing in netwerk/test/unit/test_idna2008.js to true
+// (revert bug 1218179).
+const intl::IDNA::ProcessingType kIDNA2008_DefaultProcessingType =
+ intl::IDNA::ProcessingType::NonTransitional;
+
+//-----------------------------------------------------------------------------
+// According to RFC 1034 - 3.1. Name space specifications and terminology
+// the maximum label size would be 63. However, this is enforced at the DNS
+// level and none of the other browsers seem to not enforce the VerifyDnsLength
+// check in https://unicode.org/reports/tr46/#ToASCII
+// Instead, we choose a rather arbitrary but larger size.
+static const uint32_t kMaxULabelSize = 256;
+// RFC 3490 - 5. ACE prefix
+static const char kACEPrefix[] = "xn--";
+
+//-----------------------------------------------------------------------------
+
+#define NS_NET_PREF_EXTRAALLOWED "network.IDN.extra_allowed_chars"
+#define NS_NET_PREF_EXTRABLOCKED "network.IDN.extra_blocked_chars"
+#define NS_NET_PREF_IDNRESTRICTION "network.IDN.restriction_profile"
+
+static inline bool isOnlySafeChars(const nsString& in,
+ const nsTArray<BlocklistRange>& aBlocklist) {
+ if (aBlocklist.IsEmpty()) {
+ return true;
+ }
+ const char16_t* cur = in.BeginReading();
+ const char16_t* end = in.EndReading();
+
+ for (; cur < end; ++cur) {
+ if (CharInBlocklist(*cur, aBlocklist)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// nsIDNService
+//-----------------------------------------------------------------------------
+
+/* Implementation file */
+NS_IMPL_ISUPPORTS(nsIDNService, nsIIDNService)
+
+static const char* gCallbackPrefs[] = {
+ NS_NET_PREF_EXTRAALLOWED,
+ NS_NET_PREF_EXTRABLOCKED,
+ NS_NET_PREF_IDNRESTRICTION,
+ nullptr,
+};
+
+nsresult nsIDNService::Init() {
+ MOZ_ASSERT(NS_IsMainThread());
+ // Take a strong reference for our listener with the preferences service,
+ // which we will release on shutdown.
+ // It's OK if we remove the observer a bit early, as it just means we won't
+ // respond to `network.IDN.extra_{allowed,blocked}_chars` and
+ // `network.IDN.restriction_profile` pref changes during shutdown.
+ Preferences::RegisterPrefixCallbacks(PrefChanged, gCallbackPrefs, this);
+ RunOnShutdown(
+ [self = RefPtr{this}]() mutable {
+ Preferences::UnregisterPrefixCallbacks(PrefChanged, gCallbackPrefs,
+ self.get());
+ self = nullptr;
+ },
+ ShutdownPhase::XPCOMWillShutdown);
+ prefsChanged(nullptr);
+
+ return NS_OK;
+}
+
+void nsIDNService::prefsChanged(const char* pref) {
+ MOZ_ASSERT(NS_IsMainThread());
+ AutoWriteLock lock(mLock);
+
+ if (!pref || nsLiteralCString(NS_NET_PREF_EXTRAALLOWED).Equals(pref) ||
+ nsLiteralCString(NS_NET_PREF_EXTRABLOCKED).Equals(pref)) {
+ InitializeBlocklist(mIDNBlocklist);
+ }
+ if (!pref || nsLiteralCString(NS_NET_PREF_IDNRESTRICTION).Equals(pref)) {
+ nsAutoCString profile;
+ if (NS_FAILED(
+ Preferences::GetCString(NS_NET_PREF_IDNRESTRICTION, profile))) {
+ profile.Truncate();
+ }
+ if (profile.EqualsLiteral("moderate")) {
+ mRestrictionProfile = eModeratelyRestrictiveProfile;
+ } else if (profile.EqualsLiteral("high")) {
+ mRestrictionProfile = eHighlyRestrictiveProfile;
+ } else {
+ mRestrictionProfile = eASCIIOnlyProfile;
+ }
+ }
+}
+
+nsIDNService::nsIDNService() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ auto createResult =
+ mozilla::intl::IDNA::TryCreate(kIDNA2008_DefaultProcessingType);
+ MOZ_ASSERT(createResult.isOk());
+ mIDNA = createResult.unwrap();
+}
+
+nsIDNService::~nsIDNService() = default;
+
+nsresult nsIDNService::IDNA2008ToUnicode(const nsACString& input,
+ nsAString& output) {
+ NS_ConvertUTF8toUTF16 inputStr(input);
+
+ Span<const char16_t> inputSpan{inputStr};
+ intl::nsTStringToBufferAdapter buffer(output);
+ auto result = mIDNA->LabelToUnicode(inputSpan, buffer);
+
+ nsresult rv = NS_OK;
+ if (result.isErr()) {
+ rv = ICUUtils::ICUErrorToNsResult(result.unwrapErr());
+ if (rv == NS_ERROR_FAILURE) {
+ rv = NS_ERROR_MALFORMED_URI;
+ }
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ intl::IDNA::Info info = result.unwrap();
+ if (info.HasErrors()) {
+ rv = NS_ERROR_MALFORMED_URI;
+ }
+
+ return rv;
+}
+
+nsresult nsIDNService::IDNA2008StringPrep(const nsAString& input,
+ nsAString& output,
+ stringPrepFlag flag) {
+ Span<const char16_t> inputSpan{input};
+ intl::nsTStringToBufferAdapter buffer(output);
+ auto result = mIDNA->LabelToUnicode(inputSpan, buffer);
+
+ nsresult rv = NS_OK;
+ if (result.isErr()) {
+ rv = ICUUtils::ICUErrorToNsResult(result.unwrapErr());
+ if (rv == NS_ERROR_FAILURE) {
+ rv = NS_ERROR_MALFORMED_URI;
+ }
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ intl::IDNA::Info info = result.unwrap();
+
+ // Output the result of nameToUnicode even if there were errors.
+ // But in the case of invalid punycode, the uidna_labelToUnicode result
+ // appears to get an appended U+FFFD REPLACEMENT CHARACTER, which will
+ // confuse our subsequent processing, so we drop that.
+ // (https://bugzilla.mozilla.org/show_bug.cgi?id=1399540#c9)
+ if ((info.HasInvalidPunycode() || info.HasInvalidAceLabel()) &&
+ !output.IsEmpty() && output.Last() == 0xfffd) {
+ output.Truncate(output.Length() - 1);
+ }
+
+ if (flag == eStringPrepIgnoreErrors) {
+ return NS_OK;
+ }
+
+ if (flag == eStringPrepForDNS) {
+ // We ignore errors if the result is empty, or if the errors were just
+ // invalid hyphens (not punycode-decoding failure or invalid chars).
+ if (!output.IsEmpty()) {
+ if (info.HasErrorsIgnoringInvalidHyphen()) {
+ output.Truncate();
+ rv = NS_ERROR_MALFORMED_URI;
+ }
+ }
+ } else {
+ if (info.HasErrors()) {
+ rv = NS_ERROR_MALFORMED_URI;
+ }
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP nsIDNService::ConvertUTF8toACE(const nsACString& input,
+ nsACString& ace) {
+ return UTF8toACE(input, ace, eStringPrepForDNS);
+}
+
+nsresult nsIDNService::UTF8toACE(const nsACString& input, nsACString& ace,
+ stringPrepFlag flag) {
+ nsresult rv;
+ NS_ConvertUTF8toUTF16 ustr(input);
+
+ // map ideographic period to ASCII period etc.
+ normalizeFullStops(ustr);
+
+ uint32_t len, offset;
+ len = 0;
+ offset = 0;
+ nsAutoCString encodedBuf;
+
+ nsAString::const_iterator start, end;
+ ustr.BeginReading(start);
+ ustr.EndReading(end);
+ ace.Truncate();
+
+ // encode nodes if non ASCII
+ while (start != end) {
+ len++;
+ if (*start++ == (char16_t)'.') {
+ rv = stringPrepAndACE(Substring(ustr, offset, len - 1), encodedBuf, flag);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ ace.Append(encodedBuf);
+ ace.Append('.');
+ offset += len;
+ len = 0;
+ }
+ }
+
+ // encode the last node if non ASCII
+ if (len) {
+ rv = stringPrepAndACE(Substring(ustr, offset, len), encodedBuf, flag);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ ace.Append(encodedBuf);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsIDNService::ConvertACEtoUTF8(const nsACString& input,
+ nsACString& _retval) {
+ return ACEtoUTF8(input, _retval, eStringPrepForDNS);
+}
+
+nsresult nsIDNService::ACEtoUTF8(const nsACString& input, nsACString& _retval,
+ stringPrepFlag flag) {
+ // RFC 3490 - 4.2 ToUnicode
+ // ToUnicode never fails. If any step fails, then the original input
+ // sequence is returned immediately in that step.
+ //
+ // Note that this refers to the decoding of a single label.
+ // ACEtoUTF8 may be called with a sequence of labels separated by dots;
+ // this test applies individually to each label.
+
+ uint32_t len = 0, offset = 0;
+ nsAutoCString decodedBuf;
+
+ nsACString::const_iterator start, end;
+ input.BeginReading(start);
+ input.EndReading(end);
+ _retval.Truncate();
+
+ // loop and decode nodes
+ while (start != end) {
+ len++;
+ if (*start++ == '.') {
+ nsDependentCSubstring origLabel(input, offset, len - 1);
+ if (NS_FAILED(decodeACE(origLabel, decodedBuf, flag))) {
+ // If decoding failed, use the original input sequence
+ // for this label.
+ _retval.Append(origLabel);
+ } else {
+ _retval.Append(decodedBuf);
+ }
+
+ _retval.Append('.');
+ offset += len;
+ len = 0;
+ }
+ }
+ // decode the last node
+ if (len) {
+ nsDependentCSubstring origLabel(input, offset, len);
+ if (NS_FAILED(decodeACE(origLabel, decodedBuf, flag))) {
+ _retval.Append(origLabel);
+ } else {
+ _retval.Append(decodedBuf);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsIDNService::IsACE(const nsACString& input, bool* _retval) {
+ // look for the ACE prefix in the input string. it may occur
+ // at the beginning of any segment in the domain name. for
+ // example: "www.xn--ENCODED.com"
+
+ if (!IsAscii(input)) {
+ *_retval = false;
+ return NS_OK;
+ }
+
+ auto stringContains = [](const nsACString& haystack,
+ const nsACString& needle) {
+ return std::search(haystack.BeginReading(), haystack.EndReading(),
+ needle.BeginReading(), needle.EndReading(),
+ [](unsigned char ch1, unsigned char ch2) {
+ return tolower(ch1) == tolower(ch2);
+ }) != haystack.EndReading();
+ };
+
+ *_retval =
+ StringBeginsWith(input, "xn--"_ns, nsCaseInsensitiveCStringComparator) ||
+ (!input.IsEmpty() && input[0] != '.' &&
+ stringContains(input, ".xn--"_ns));
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsIDNService::Normalize(const nsACString& input,
+ nsACString& output) {
+ // protect against bogus input
+ NS_ENSURE_TRUE(IsUtf8(input), NS_ERROR_UNEXPECTED);
+
+ NS_ConvertUTF8toUTF16 inUTF16(input);
+ normalizeFullStops(inUTF16);
+
+ // pass the domain name to stringprep label by label
+ nsAutoString outUTF16, outLabel;
+
+ uint32_t len = 0, offset = 0;
+ nsresult rv;
+ nsAString::const_iterator start, end;
+ inUTF16.BeginReading(start);
+ inUTF16.EndReading(end);
+
+ while (start != end) {
+ len++;
+ if (*start++ == char16_t('.')) {
+ rv = stringPrep(Substring(inUTF16, offset, len - 1), outLabel,
+ eStringPrepIgnoreErrors);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ outUTF16.Append(outLabel);
+ outUTF16.Append(char16_t('.'));
+ offset += len;
+ len = 0;
+ }
+ }
+ if (len) {
+ rv = stringPrep(Substring(inUTF16, offset, len), outLabel,
+ eStringPrepIgnoreErrors);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ outUTF16.Append(outLabel);
+ }
+
+ CopyUTF16toUTF8(outUTF16, output);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsIDNService::ConvertToDisplayIDN(const nsACString& input,
+ bool* _isASCII,
+ nsACString& _retval) {
+ // If host is ACE, then convert to UTF-8 if the host is in the IDN whitelist.
+ // Else, if host is already UTF-8, then make sure it is normalized per IDN.
+
+ nsresult rv = NS_OK;
+
+ // Even if the hostname is not ASCII, individual labels may still be ACE, so
+ // test IsACE before testing IsASCII
+ bool isACE;
+ IsACE(input, &isACE);
+
+ if (IsAscii(input)) {
+ // first, canonicalize the host to lowercase, for whitelist lookup
+ _retval = input;
+ ToLowerCase(_retval);
+
+ if (isACE && !StaticPrefs::network_IDN_show_punycode()) {
+ // ACEtoUTF8() can't fail, but might return the original ACE string
+ nsAutoCString temp(_retval);
+ // Convert from ACE to UTF8 only those labels which are considered safe
+ // for display
+ ACEtoUTF8(temp, _retval, eStringPrepForUI);
+ *_isASCII = IsAscii(_retval);
+ } else {
+ *_isASCII = true;
+ }
+ } else {
+ // We have to normalize the hostname before testing against the domain
+ // whitelist (see bug 315411), and to ensure the entire string gets
+ // normalized.
+ //
+ // Normalization and the tests for safe display below, assume that the
+ // input is Unicode, so first convert any ACE labels to UTF8
+ if (isACE) {
+ nsAutoCString temp;
+ ACEtoUTF8(input, temp, eStringPrepIgnoreErrors);
+ rv = Normalize(temp, _retval);
+ } else {
+ rv = Normalize(input, _retval);
+ }
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (StaticPrefs::network_IDN_show_punycode() &&
+ NS_SUCCEEDED(UTF8toACE(_retval, _retval, eStringPrepIgnoreErrors))) {
+ *_isASCII = true;
+ return NS_OK;
+ }
+
+ // normalization could result in an ASCII-only hostname. alternatively, if
+ // the host is converted to ACE by the normalizer, then the host may contain
+ // unsafe characters, so leave it ACE encoded. see bug 283016, bug 301694,
+ // and bug 309311.
+ *_isASCII = IsAscii(_retval);
+ if (!*_isASCII) {
+ // UTF8toACE with eStringPrepForUI may return a domain name where
+ // some labels are in UTF-8 and some are in ACE, depending on
+ // whether they are considered safe for display
+ rv = UTF8toACE(_retval, _retval, eStringPrepForUI);
+ *_isASCII = IsAscii(_retval);
+ return rv;
+ }
+ }
+
+ return NS_OK;
+} // Will generate a mutex still-held warning
+
+//-----------------------------------------------------------------------------
+
+static nsresult utf16ToUcs4(const nsAString& in, uint32_t* out,
+ uint32_t outBufLen, uint32_t* outLen) {
+ uint32_t i = 0;
+ nsAString::const_iterator start, end;
+ in.BeginReading(start);
+ in.EndReading(end);
+
+ while (start != end) {
+ char16_t curChar;
+
+ curChar = *start++;
+
+ if (start != end && NS_IS_SURROGATE_PAIR(curChar, *start)) {
+ out[i] = SURROGATE_TO_UCS4(curChar, *start);
+ ++start;
+ } else {
+ out[i] = curChar;
+ }
+
+ i++;
+ if (i >= outBufLen) {
+ return NS_ERROR_MALFORMED_URI;
+ }
+ }
+ out[i] = (uint32_t)'\0';
+ *outLen = i;
+ return NS_OK;
+}
+
+static nsresult punycode(const nsAString& in, nsACString& out) {
+ uint32_t ucs4Buf[kMaxULabelSize + 1];
+ uint32_t ucs4Len = 0u;
+ nsresult rv = utf16ToUcs4(in, ucs4Buf, kMaxULabelSize, &ucs4Len);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // need maximum 20 bits to encode 16 bit Unicode character
+ // (include null terminator)
+ const uint32_t kEncodedBufSize = kMaxULabelSize * 20 / 8 + 1 + 1;
+ char encodedBuf[kEncodedBufSize];
+ punycode_uint encodedLength = kEncodedBufSize;
+
+ enum punycode_status status =
+ punycode_encode(ucs4Len, ucs4Buf, nullptr, &encodedLength, encodedBuf);
+
+ if (punycode_success != status || encodedLength >= kEncodedBufSize) {
+ return NS_ERROR_MALFORMED_URI;
+ }
+
+ encodedBuf[encodedLength] = '\0';
+ out.Assign(nsDependentCString(kACEPrefix) + nsDependentCString(encodedBuf));
+
+ return rv;
+}
+
+// RFC 3454
+//
+// 1) Map -- For each character in the input, check if it has a mapping
+// and, if so, replace it with its mapping. This is described in section 3.
+//
+// 2) Normalize -- Possibly normalize the result of step 1 using Unicode
+// normalization. This is described in section 4.
+//
+// 3) Prohibit -- Check for any characters that are not allowed in the
+// output. If any are found, return an error. This is described in section
+// 5.
+//
+// 4) Check bidi -- Possibly check for right-to-left characters, and if any
+// are found, make sure that the whole string satisfies the requirements
+// for bidirectional strings. If the string does not satisfy the requirements
+// for bidirectional strings, return an error. This is described in section 6.
+//
+// 5) Check unassigned code points -- If allowUnassigned is false, check for
+// any unassigned Unicode points and if any are found return an error.
+// This is described in section 7.
+//
+nsresult nsIDNService::stringPrep(const nsAString& in, nsAString& out,
+ stringPrepFlag flag) {
+ return IDNA2008StringPrep(in, out, flag);
+}
+
+nsresult nsIDNService::stringPrepAndACE(const nsAString& in, nsACString& out,
+ stringPrepFlag flag) {
+ nsresult rv = NS_OK;
+
+ out.Truncate();
+
+ if (IsAscii(in)) {
+ LossyCopyUTF16toASCII(in, out);
+ // If label begins with xn-- we still want to check its validity
+ if (!StringBeginsWith(in, u"xn--"_ns, nsCaseInsensitiveStringComparator)) {
+ return NS_OK;
+ }
+ }
+
+ nsAutoString strPrep;
+ rv = stringPrep(in, strPrep, flag);
+ if (flag == eStringPrepForDNS) {
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ if (IsAscii(strPrep)) {
+ LossyCopyUTF16toASCII(strPrep, out);
+ return NS_OK;
+ }
+
+ if (flag == eStringPrepForUI && NS_SUCCEEDED(rv) && isLabelSafe(in)) {
+ CopyUTF16toUTF8(strPrep, out);
+ return NS_OK;
+ }
+
+ return punycode(strPrep, out);
+}
+
+// RFC 3490
+// 1) Whenever dots are used as label separators, the following characters
+// MUST be recognized as dots: U+002E (full stop), U+3002 (ideographic full
+// stop), U+FF0E (fullwidth full stop), U+FF61 (halfwidth ideographic full
+// stop).
+
+void nsIDNService::normalizeFullStops(nsAString& s) {
+ nsAString::const_iterator start, end;
+ s.BeginReading(start);
+ s.EndReading(end);
+ int32_t index = 0;
+
+ while (start != end) {
+ switch (*start) {
+ case 0x3002:
+ case 0xFF0E:
+ case 0xFF61:
+ s.ReplaceLiteral(index, 1, u".");
+ break;
+ default:
+ break;
+ }
+ start++;
+ index++;
+ }
+}
+
+nsresult nsIDNService::decodeACE(const nsACString& in, nsACString& out,
+ stringPrepFlag flag) {
+ bool isAce;
+ IsACE(in, &isAce);
+ if (!isAce) {
+ out.Assign(in);
+ return NS_OK;
+ }
+
+ nsAutoString utf16;
+ nsresult result = IDNA2008ToUnicode(in, utf16);
+ NS_ENSURE_SUCCESS(result, result);
+
+ if (flag != eStringPrepForUI || isLabelSafe(utf16)) {
+ CopyUTF16toUTF8(utf16, out);
+ } else {
+ out.Assign(in);
+ return NS_OK;
+ }
+
+ // Validation: encode back to ACE and compare the strings
+ nsAutoCString ace;
+ nsresult rv = UTF8toACE(out, ace, flag);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (flag == eStringPrepForDNS &&
+ !ace.Equals(in, nsCaseInsensitiveCStringComparator)) {
+ return NS_ERROR_MALFORMED_URI;
+ }
+
+ return NS_OK;
+}
+
+namespace mozilla::net {
+
+enum ScriptCombo : int32_t {
+ UNSET = -1,
+ BOPO = 0,
+ CYRL = 1,
+ GREK = 2,
+ HANG = 3,
+ HANI = 4,
+ HIRA = 5,
+ KATA = 6,
+ LATN = 7,
+ OTHR = 8,
+ JPAN = 9, // Latin + Han + Hiragana + Katakana
+ CHNA = 10, // Latin + Han + Bopomofo
+ KORE = 11, // Latin + Han + Hangul
+ HNLT = 12, // Latin + Han (could be any of the above combinations)
+ FAIL = 13,
+};
+
+} // namespace mozilla::net
+
+bool nsIDNService::isLabelSafe(const nsAString& label) {
+ AutoReadLock lock(mLock);
+
+ if (!isOnlySafeChars(PromiseFlatString(label), mIDNBlocklist)) {
+ return false;
+ }
+
+ // We should never get here if the label is ASCII
+ NS_ASSERTION(!IsAscii(label), "ASCII label in IDN checking");
+ if (mRestrictionProfile == eASCIIOnlyProfile) {
+ return false;
+ }
+
+ nsAString::const_iterator current, end;
+ label.BeginReading(current);
+ label.EndReading(end);
+
+ Script lastScript = Script::INVALID;
+ uint32_t previousChar = 0;
+ uint32_t baseChar = 0; // last non-diacritic seen (base char for marks)
+ uint32_t savedNumberingSystem = 0;
+// Simplified/Traditional Chinese check temporarily disabled -- bug 857481
+#if 0
+ HanVariantType savedHanVariant = HVT_NotHan;
+#endif
+
+ ScriptCombo savedScript = ScriptCombo::UNSET;
+
+ while (current != end) {
+ uint32_t ch = *current++;
+
+ if (current != end && NS_IS_SURROGATE_PAIR(ch, *current)) {
+ ch = SURROGATE_TO_UCS4(ch, *current++);
+ }
+
+ IdentifierType idType = GetIdentifierType(ch);
+ if (idType == IDTYPE_RESTRICTED) {
+ return false;
+ }
+ MOZ_ASSERT(idType == IDTYPE_ALLOWED);
+
+ // Check for mixed script
+ Script script = UnicodeProperties::GetScriptCode(ch);
+ if (script != Script::COMMON && script != Script::INHERITED &&
+ script != lastScript) {
+ if (illegalScriptCombo(script, savedScript)) {
+ return false;
+ }
+ }
+
+ // U+30FC should be preceded by a Hiragana/Katakana.
+ if (ch == 0x30fc && lastScript != Script::HIRAGANA &&
+ lastScript != Script::KATAKANA) {
+ return false;
+ }
+
+ if (ch == 0x307 &&
+ (previousChar == 'i' || previousChar == 'j' || previousChar == 'l')) {
+ return false;
+ }
+
+ // Check for mixed numbering systems
+ auto genCat = GetGeneralCategory(ch);
+ if (genCat == HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) {
+ uint32_t zeroCharacter =
+ ch - mozilla::intl::UnicodeProperties::GetNumericValue(ch);
+ if (savedNumberingSystem == 0) {
+ // If we encounter a decimal number, save the zero character from that
+ // numbering system.
+ savedNumberingSystem = zeroCharacter;
+ } else if (zeroCharacter != savedNumberingSystem) {
+ return false;
+ }
+ }
+
+ if (genCat == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) {
+ // Check for consecutive non-spacing marks.
+ if (previousChar != 0 && previousChar == ch) {
+ return false;
+ }
+ // Check for marks whose expected script doesn't match the base script.
+ if (lastScript != Script::INVALID) {
+ UnicodeProperties::ScriptExtensionVector scripts;
+ auto extResult = UnicodeProperties::GetExtensions(ch, scripts);
+ MOZ_ASSERT(extResult.isOk());
+ if (extResult.isErr()) {
+ return false;
+ }
+
+ int nScripts = AssertedCast<int>(scripts.length());
+
+ // nScripts will always be >= 1, because even for undefined characters
+ // it will return Script::INVALID.
+ // If the mark just has script=COMMON or INHERITED, we can't check any
+ // more carefully, but if it has specific scriptExtension codes, then
+ // assume those are the only valid scripts to use it with.
+ if (nScripts > 1 || (Script(scripts[0]) != Script::COMMON &&
+ Script(scripts[0]) != Script::INHERITED)) {
+ while (--nScripts >= 0) {
+ if (Script(scripts[nScripts]) == lastScript) {
+ break;
+ }
+ }
+ if (nScripts == -1) {
+ return false;
+ }
+ }
+ }
+ // Check for diacritics on dotless-i, which would be indistinguishable
+ // from normal accented letter i.
+ if (baseChar == 0x0131 &&
+ ((ch >= 0x0300 && ch <= 0x0314) || ch == 0x031a)) {
+ return false;
+ }
+ } else {
+ baseChar = ch;
+ }
+
+ if (script != Script::COMMON && script != Script::INHERITED) {
+ lastScript = script;
+ }
+
+ // Simplified/Traditional Chinese check temporarily disabled -- bug 857481
+#if 0
+
+ // Check for both simplified-only and traditional-only Chinese characters
+ HanVariantType hanVariant = GetHanVariant(ch);
+ if (hanVariant == HVT_SimplifiedOnly || hanVariant == HVT_TraditionalOnly) {
+ if (savedHanVariant == HVT_NotHan) {
+ savedHanVariant = hanVariant;
+ } else if (hanVariant != savedHanVariant) {
+ return false;
+ }
+ }
+#endif
+
+ previousChar = ch;
+ }
+ return true;
+}
+
+// Scripts that we care about in illegalScriptCombo
+static inline ScriptCombo findScriptIndex(Script aScript) {
+ switch (aScript) {
+ case Script::BOPOMOFO:
+ return ScriptCombo::BOPO;
+ case Script::CYRILLIC:
+ return ScriptCombo::CYRL;
+ case Script::GREEK:
+ return ScriptCombo::GREK;
+ case Script::HANGUL:
+ return ScriptCombo::HANG;
+ case Script::HAN:
+ return ScriptCombo::HANI;
+ case Script::HIRAGANA:
+ return ScriptCombo::HIRA;
+ case Script::KATAKANA:
+ return ScriptCombo::KATA;
+ case Script::LATIN:
+ return ScriptCombo::LATN;
+ default:
+ return ScriptCombo::OTHR;
+ }
+}
+
+static const ScriptCombo scriptComboTable[13][9] = {
+ /* thisScript: BOPO CYRL GREK HANG HANI HIRA KATA LATN OTHR
+ * savedScript */
+ /* BOPO */ {BOPO, FAIL, FAIL, FAIL, CHNA, FAIL, FAIL, CHNA, FAIL},
+ /* CYRL */ {FAIL, CYRL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL},
+ /* GREK */ {FAIL, FAIL, GREK, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL},
+ /* HANG */ {FAIL, FAIL, FAIL, HANG, KORE, FAIL, FAIL, KORE, FAIL},
+ /* HANI */ {CHNA, FAIL, FAIL, KORE, HANI, JPAN, JPAN, HNLT, FAIL},
+ /* HIRA */ {FAIL, FAIL, FAIL, FAIL, JPAN, HIRA, JPAN, JPAN, FAIL},
+ /* KATA */ {FAIL, FAIL, FAIL, FAIL, JPAN, JPAN, KATA, JPAN, FAIL},
+ /* LATN */ {CHNA, FAIL, FAIL, KORE, HNLT, JPAN, JPAN, LATN, OTHR},
+ /* OTHR */ {FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, OTHR, FAIL},
+ /* JPAN */ {FAIL, FAIL, FAIL, FAIL, JPAN, JPAN, JPAN, JPAN, FAIL},
+ /* CHNA */ {CHNA, FAIL, FAIL, FAIL, CHNA, FAIL, FAIL, CHNA, FAIL},
+ /* KORE */ {FAIL, FAIL, FAIL, KORE, KORE, FAIL, FAIL, KORE, FAIL},
+ /* HNLT */ {CHNA, FAIL, FAIL, KORE, HNLT, JPAN, JPAN, HNLT, FAIL}};
+
+bool nsIDNService::illegalScriptCombo(Script script, ScriptCombo& savedScript) {
+ if (savedScript == ScriptCombo::UNSET) {
+ savedScript = findScriptIndex(script);
+ return false;
+ }
+
+ savedScript = scriptComboTable[savedScript][findScriptIndex(script)];
+ /*
+ * Special case combinations that depend on which profile is in use
+ * In the Highly Restrictive profile Latin is not allowed with any
+ * other script
+ *
+ * In the Moderately Restrictive profile Latin mixed with any other
+ * single script is allowed.
+ */
+ return ((savedScript == OTHR &&
+ mRestrictionProfile == eHighlyRestrictiveProfile) ||
+ savedScript == FAIL);
+}
diff --git a/netwerk/dns/nsIDNService.h b/netwerk/dns/nsIDNService.h
new file mode 100644
index 0000000000..1e90191326
--- /dev/null
+++ b/netwerk/dns/nsIDNService.h
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef nsIDNService_h__
+#define nsIDNService_h__
+
+#include "nsIIDNService.h"
+#include "nsCOMPtr.h"
+
+#include "mozilla/RWLock.h"
+#include "mozilla/intl/UnicodeScriptCodes.h"
+#include "mozilla/net/IDNBlocklistUtils.h"
+#include "mozilla/intl/IDNA.h"
+#include "mozilla/UniquePtr.h"
+
+#include "nsString.h"
+
+class nsIPrefBranch;
+
+//-----------------------------------------------------------------------------
+// nsIDNService
+//-----------------------------------------------------------------------------
+
+namespace mozilla::net {
+enum ScriptCombo : int32_t;
+}
+
+class nsIDNService final : public nsIIDNService {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIIDNSERVICE
+
+ nsIDNService();
+
+ nsresult Init();
+
+ protected:
+ virtual ~nsIDNService();
+
+ private:
+ enum stringPrepFlag {
+ eStringPrepForDNS,
+ eStringPrepForUI,
+ eStringPrepIgnoreErrors
+ };
+
+ /**
+ * Convert the following characters that must be recognized as label
+ * separators per RFC 3490 to ASCII full stop characters
+ *
+ * U+3002 (ideographic full stop)
+ * U+FF0E (fullwidth full stop)
+ * U+FF61 (halfwidth ideographic full stop)
+ */
+ void normalizeFullStops(nsAString& s);
+
+ /**
+ * Convert and encode a DNS label in ACE/punycode.
+ * @param flag
+ * if eStringPrepIgnoreErrors, all non-ASCII labels are
+ * converted to punycode.
+ * if eStringPrepForUI, only labels that are considered safe
+ * for display are converted.
+ * @see isLabelSafe
+ * if eStringPrepForDNS and stringPrep finds an illegal
+ * character, returns NS_FAILURE and out is empty
+ */
+ nsresult stringPrepAndACE(const nsAString& in, nsACString& out,
+ stringPrepFlag flag);
+
+ /**
+ * Convert a DNS label using the stringprep profile defined in RFC 3454
+ */
+ nsresult stringPrep(const nsAString& in, nsAString& out, stringPrepFlag flag);
+
+ /**
+ * Decode an ACE-encoded DNS label to UTF-8
+ *
+ * @param flag
+ * if eStringPrepForUI and the label is not considered safe to
+ * display, the output is the same as the input
+ * @see isLabelSafe
+ */
+ nsresult decodeACE(const nsACString& in, nsACString& out,
+ stringPrepFlag flag);
+
+ /**
+ * Convert complete domain names between UTF8 and ACE and vice versa
+ *
+ * @param flag is passed to decodeACE or stringPrepAndACE for each
+ * label individually, so the output may contain some labels in
+ * punycode and some in UTF-8
+ */
+ nsresult UTF8toACE(const nsACString& input, nsACString& ace,
+ stringPrepFlag flag);
+ nsresult ACEtoUTF8(const nsACString& input, nsACString& _retval,
+ stringPrepFlag flag);
+
+ void prefsChanged(const char* pref);
+
+ static void PrefChanged(const char* aPref, void* aSelf) {
+ auto* self = static_cast<nsIDNService*>(aSelf);
+ self->prefsChanged(aPref);
+ }
+
+ /**
+ * Determine whether a label is considered safe to display to the user
+ * according to the algorithm defined in UTR 39 and the profile
+ * selected in mRestrictionProfile.
+ *
+ * For the ASCII-only profile, returns false for all labels containing
+ * non-ASCII characters.
+ *
+ * For the other profiles, returns false for labels containing any of
+ * the following:
+ *
+ * Characters in scripts other than the "recommended scripts" and
+ * "aspirational scripts" defined in
+ * http://www.unicode.org/reports/tr31/#Table_Recommended_Scripts
+ * and http://www.unicode.org/reports/tr31/#Aspirational_Use_Scripts
+ * This includes codepoints that are not defined as Unicode
+ * characters
+ *
+ * Illegal combinations of scripts (@see illegalScriptCombo)
+ *
+ * Numbers from more than one different numbering system
+ *
+ * Sequences of the same non-spacing mark
+ *
+ * Both simplified-only and traditional-only Chinese characters
+ * XXX this test was disabled by bug 857481
+ */
+ bool isLabelSafe(const nsAString& label) MOZ_EXCLUDES(mLock);
+
+ /**
+ * Determine whether a combination of scripts in a single label is
+ * permitted according to the algorithm defined in UTR 39 and the
+ * profile selected in mRestrictionProfile.
+ *
+ * For the "Highly restrictive" profile, all characters in each
+ * identifier must be from a single script, or from the combinations:
+ * Latin + Han + Hiragana + Katakana;
+ * Latin + Han + Bopomofo; or
+ * Latin + Han + Hangul
+ *
+ * For the "Moderately restrictive" profile, Latin is also allowed
+ * with other scripts except Cyrillic and Greek
+ */
+ bool illegalScriptCombo(mozilla::intl::Script script,
+ mozilla::net::ScriptCombo& savedScript)
+ MOZ_REQUIRES_SHARED(mLock);
+
+ /**
+ * Convert a DNS label from ASCII to Unicode using IDNA2008
+ */
+ nsresult IDNA2008ToUnicode(const nsACString& input, nsAString& output);
+
+ /**
+ * Convert a DNS label to a normalized form conforming to IDNA2008
+ */
+ nsresult IDNA2008StringPrep(const nsAString& input, nsAString& output,
+ stringPrepFlag flag);
+
+ // never mutated after initializing.
+ mozilla::UniquePtr<mozilla::intl::IDNA> mIDNA;
+
+ // We use this rwlock to guard access to:
+ // |mIDNBlocklist|, |mRestrictionProfile|
+ mozilla::RWLock mLock{"nsIDNService"};
+
+ // guarded by mLock
+ nsTArray<mozilla::net::BlocklistRange> mIDNBlocklist MOZ_GUARDED_BY(mLock);
+
+ /**
+ * Restriction-level Detection profiles defined in UTR 39
+ * http://www.unicode.org/reports/tr39/#Restriction_Level_Detection,
+ * and selected by the pref network.IDN.restriction_profile
+ */
+ enum restrictionProfile {
+ eASCIIOnlyProfile,
+ eHighlyRestrictiveProfile,
+ eModeratelyRestrictiveProfile
+ };
+ // guarded by mLock;
+ restrictionProfile mRestrictionProfile MOZ_GUARDED_BY(mLock){
+ eASCIIOnlyProfile};
+};
+
+#endif // nsIDNService_h__
diff --git a/netwerk/dns/nsIEffectiveTLDService.idl b/netwerk/dns/nsIEffectiveTLDService.idl
new file mode 100644
index 0000000000..abf786e5ed
--- /dev/null
+++ b/netwerk/dns/nsIEffectiveTLDService.idl
@@ -0,0 +1,205 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsISupports.idl"
+
+interface nsIURI;
+
+[scriptable, uuid(68067eb5-ad8d-43cb-a043-1cc85ebe06e7)]
+interface nsIEffectiveTLDService : nsISupports
+{
+ /**
+ * Returns the public suffix of a URI. A public suffix is the highest-level domain
+ * under which individual domains may be registered; it may therefore contain one
+ * or more dots. For example, the public suffix for "www.bbc.co.uk" is "co.uk",
+ * because the .uk TLD does not allow the registration of domains at the
+ * second level ("bbc.uk" is forbidden).
+ *
+ * The public suffix will be returned encoded in ASCII/ACE and will be normalized
+ * according to RFC 3454, i.e. the same encoding returned by nsIURI::GetAsciiHost().
+ * If consumers wish to compare the result of this method against the host from
+ * another nsIURI, the host should be obtained using nsIURI::GetAsciiHost().
+ * In the case of nested URIs, the innermost URI will be used.
+ *
+ * @param aURI The URI to be analyzed
+ *
+ * @returns the public suffix
+ *
+ * @throws NS_ERROR_UNEXPECTED
+ * or other error returned by nsIIDNService::normalize when
+ * the hostname contains characters disallowed in URIs
+ * @throws NS_ERROR_HOST_IS_IP_ADDRESS
+ * if the host is a numeric IPv4 or IPv6 address (as determined by
+ * the success of a call to PR_StringToNetAddr()).
+ */
+ ACString getPublicSuffix(in nsIURI aURI);
+
+ /**
+ * Similar to getPublicSuffix, but the suffix is validated against
+ * the Public Suffix List. If the suffix is unknown this will return
+ * an empty string.
+ *
+ * @param aURI The URI to be analyzed
+ * @returns the public suffix if known, an empty string otherwise
+ * @see getPublicSuffixFromHost()
+ */
+ ACString getKnownPublicSuffix(in nsIURI aURI);
+
+ /**
+ * Returns the base domain of a URI; that is, the public suffix with a given
+ * number of additional domain name parts. For example, the result of this method
+ * for "www.bbc.co.uk", depending on the value of aAdditionalParts parameter, will
+ * be:
+ *
+ * 0 (default) -> bbc.co.uk
+ * 1 -> www.bbc.co.uk
+ *
+ * Similarly, the public suffix for "www.developer.mozilla.org" is "org", and the base
+ * domain will be:
+ *
+ * 0 (default) -> mozilla.org
+ * 1 -> developer.mozilla.org
+ * 2 -> www.developer.mozilla.org
+ *
+ * The base domain will be returned encoded in ASCII/ACE and will be normalized
+ * according to RFC 3454, i.e. the same encoding returned by nsIURI::GetAsciiHost().
+ * If consumers wish to compare the result of this method against the host from
+ * another nsIURI, the host should be obtained using nsIURI::GetAsciiHost().
+ * In the case of nested URIs, the innermost URI will be used.
+ *
+ * @param aURI The URI to be analyzed
+ * @param aAdditionalParts Number of domain name parts to be
+ * returned in addition to the public suffix
+ *
+ * @returns the base domain (public suffix plus the requested number of additional parts)
+ *
+ * @throws NS_ERROR_UNEXPECTED
+ * or other error returned by nsIIDNService::normalize when
+ * the hostname contains characters disallowed in URIs
+ * @throws NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS
+ * when there are insufficient subdomain levels in the hostname to satisfy the
+ * requested aAdditionalParts value.
+ * @throws NS_ERROR_HOST_IS_IP_ADDRESS
+ * if aHost is a numeric IPv4 or IPv6 address (as determined by
+ * the success of a call to PR_StringToNetAddr()).
+ *
+ * @see getPublicSuffix()
+ */
+ ACString getBaseDomain(in nsIURI aURI, [optional] in uint32_t aAdditionalParts);
+
+ /**
+ * Get the Site without the scheme for the origin of aURI; e.g. for
+ * "https://www.bbc.co.uk/index.html", this would be "bbc.co.uk".
+ * This uses getBaseDomain() internally. This is appropriately permissive,
+ * and will return a schemeless site for aliased hostnames and IP addresses
+ * and will therefore not throw NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS or
+ * NS_ERROR_HOST_IS_IP_ADDRESS, e.g. "http://localhost/index.html" will
+ * return "localhost" successfully, rather than throwing an error.
+ *
+ * @param aHostURI
+ * The URI to analyze.
+ *
+ * @return the Site.
+ *
+ * @throws NS_ERROR_UNEXPECTED
+ * or other error returned by nsIIDNService::normalize when
+ * the hostname contains characters disallowed in URIs
+ *
+ * @see getBaseDomain()
+ * @see getSite()
+ *
+ * @warning This function should not be used without good reason. Please
+ * use getSite() or the Origin if you are not absolutely certain.
+ */
+ ACString getSchemelessSite(in nsIURI aURI);
+
+ /**
+ * Get the Site for the origin of aURI; e.g. for
+ * "https://www.bbc.co.uk/index.html", this would be "https://bbc.co.uk".
+ * This uses getBaseDomain() internally. This is appropriately permissive,
+ * and will return a scheme for alaised hostnames and IP addresses and will
+ * therefore not throw NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS or
+ * NS_ERROR_HOST_IS_IP_ADDRESS, e.g. "http://localhost/index.html" will
+ * return "http://localhost" successfully, rather than throwing an error.
+ *
+ * @param aHostURI
+ * The URI to analyze.
+ *
+ * @return the Site.
+ *
+ * @throws NS_ERROR_UNEXPECTED
+ * or other error returned by nsIIDNService::normalize when
+ * the hostname contains characters disallowed in URIs
+ *
+ * @see getBaseDomain()
+ */
+ ACString getSite(in nsIURI aURI);
+
+ /**
+ * NOTE: It is strongly recommended to use getPublicSuffix() above if a suitable
+ * nsIURI is available. Only use this method if this is not the case.
+ *
+ * Returns the public suffix of a host string. Otherwise identical to getPublicSuffix().
+ *
+ * @param aHost The host to be analyzed. Any additional parts (e.g. scheme,
+ * port, or path) will cause this method to throw. ASCII/ACE and
+ * UTF8 encodings are acceptable as input; normalization will
+ * be performed as specified in getBaseDomain().
+ *
+ * @see getPublicSuffix()
+ */
+ ACString getPublicSuffixFromHost(in AUTF8String aHost);
+
+ /**
+ * Similar to getPublicSuffixFromHost, but the suffix is validated against
+ * the Public Suffix List. If the suffix is unknown this will return
+ * an empty string.
+ *
+ * @param aHost The host to be analyzed.
+ * @returns the public suffix if known, an empty string otherwise
+ * @see getPublicSuffixFromHost()
+ */
+ ACString getKnownPublicSuffixFromHost(in AUTF8String aHost);
+
+ /**
+ * NOTE: It is strongly recommended to use getBaseDomain() above if a suitable
+ * nsIURI is available. Only use this method if this is not the case.
+ *
+ * Returns the base domain of a host string. Otherwise identical to getBaseDomain().
+ *
+ * @param aHost The host to be analyzed. Any additional parts (e.g. scheme,
+ * port, or path) will cause this method to throw. ASCII/ACE and
+ * UTF8 encodings are acceptable as input; normalization will
+ * be performed as specified in getBaseDomain().
+ *
+ * @see getBaseDomain()
+ */
+ ACString getBaseDomainFromHost(in AUTF8String aHost, [optional] in uint32_t aAdditionalParts);
+
+ /**
+ * Returns the parent sub-domain of a host string. If the host is a base
+ * domain, it will throw NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS.
+ *
+ * For example: "player.bbc.co.uk" would return "bbc.co.uk" and
+ * "bbc.co.uk" would throw NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS.
+ *
+ * @param aHost The host to be analyzed. Any additional parts (e.g. scheme,
+ * port, or path) will cause this method to throw. ASCII/ACE and
+ * UTF8 encodings are acceptable as input; normalization will
+ * be performed as specified in getBaseDomain().
+ */
+ ACString getNextSubDomain(in AUTF8String aHost);
+
+ /**
+ * Returns true if the |aInput| in is part of the root domain of |aHost|.
+ * For example, if |aInput| is "www.mozilla.org", and we pass in
+ * "mozilla.org" as |aHost|, this will return true. It would return false
+ * the other way around.
+ *
+ * @param aInput The host to be analyzed.
+ * @param aHost The host to compare to.
+ */
+ bool hasRootDomain(in AUTF8String aInput, in AUTF8String aHost);
+};
diff --git a/netwerk/dns/nsIIDNService.idl b/netwerk/dns/nsIIDNService.idl
new file mode 100644
index 0000000000..47ef561237
--- /dev/null
+++ b/netwerk/dns/nsIIDNService.idl
@@ -0,0 +1,58 @@
+/* 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 "nsISupports.idl"
+
+/**
+ * nsIIDNService interface.
+ *
+ * IDN (Internationalized Domain Name) support. Provides facilities
+ * for manipulating IDN hostnames according to the specification set
+ * forth by the IETF.
+ *
+ * IDN effort:
+ * http://www.ietf.org/html.characters/idn-charter.html
+ * http://www.i-dns.net
+ *
+ * IDNA specification:
+ * http://search.ietf.org/internet-drafts/draft-ietf-idn-idna-06.txt
+ */
+
+[scriptable, uuid(a592a60e-3621-4f19-a318-2bf233cfad3e)]
+interface nsIIDNService : nsISupports
+{
+ /**
+ * Prepares the input hostname according to IDNA ToASCII operation,
+ * the input hostname is assumed to be UTF8-encoded.
+ */
+ ACString convertUTF8toACE(in AUTF8String input);
+
+
+ /**
+ * This is the ToUnicode operation as specified in the IDNA proposal,
+ * with an additional step to encode the result in UTF-8.
+ * It takes an ACE-encoded hostname and performs ToUnicode to it, then
+ * encodes the resulting string into UTF8.
+ */
+ AUTF8String convertACEtoUTF8(in ACString input);
+
+ /**
+ * Checks if the input string is ACE encoded or not.
+ */
+ boolean isACE(in ACString input);
+
+ /**
+ * Performs the unicode normalization needed for hostnames in IDN,
+ * for callers that want early normalization.
+ */
+ AUTF8String normalize(in AUTF8String input);
+
+ /**
+ * Normalizes and converts a host to UTF-8 if the host is in the IDN
+ * whitelist, otherwise converts it to ACE. This is useful for display
+ * purposes and to ensure an encoding consistent with nsIURI::GetHost().
+ * If the result is ASCII or ACE encoded, |isASCII| will be true.
+ */
+ AUTF8String convertToDisplayIDN(in AUTF8String input, out boolean isASCII);
+};
diff --git a/netwerk/dns/nsINativeDNSResolverOverride.idl b/netwerk/dns/nsINativeDNSResolverOverride.idl
new file mode 100644
index 0000000000..0a016af437
--- /dev/null
+++ b/netwerk/dns/nsINativeDNSResolverOverride.idl
@@ -0,0 +1,37 @@
+/* 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 "nsISupports.idl"
+
+[scriptable, builtinclass, uuid(8e38d536-5501-48c0-a412-6c450040c8c8)]
+interface nsINativeDNSResolverOverride : nsISupports
+{
+ /**
+ * Adds an IP override for this specific host.
+ */
+ void addIPOverride(in AUTF8String aHost, in ACString aIPLiteral);
+
+ /**
+ * Adds an HTTPS record override for this specific host.
+ * The input needs to be the raw bytes of a DNS answer.
+ */
+ void addHTTPSRecordOverride(in AUTF8String aHost,
+ [array, size_is(aLength), const] in uint8_t aData,
+ in unsigned long aLength);
+
+ /**
+ * Sets a CNAME override for this specific host.
+ */
+ void setCnameOverride(in AUTF8String aHost, in ACString aCNAME);
+
+ /**
+ * Clears the overrides for this specific host
+ */
+ void clearHostOverride(in AUTF8String aHost);
+
+ /**
+ * Clears all the host overrides that were previously set.
+ */
+ void clearOverrides();
+};
diff --git a/netwerk/dns/nsITRRSkipReason.idl b/netwerk/dns/nsITRRSkipReason.idl
new file mode 100644
index 0000000000..096f0151bb
--- /dev/null
+++ b/netwerk/dns/nsITRRSkipReason.idl
@@ -0,0 +1,121 @@
+/* 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 "nsISupports.idl"
+
+[scriptable, uuid(e61b5d39-f6d6-4ed3-aead-1213b24c6f27)]
+interface nsITRRSkipReason: nsISupports
+{
+ // IMPORTANT: when adding new values, always add them to the end, otherwise
+ // it will mess up telemetry.
+ // When adding a reason here, make sure it is documented in
+ // netwerk/docs/dns/trr-skip-reasons.md
+ cenum value: 32 {
+ TRR_UNSET = 0,
+ TRR_OK = 1, // Only set when we actually got a positive TRR result
+ TRR_NO_GSERVICE = 2, // no gService
+ TRR_PARENTAL_CONTROL = 3, // parental control is on
+ TRR_OFF_EXPLICIT = 4, // user has set mode5
+ TRR_REQ_MODE_DISABLED = 5, // request has disabled flags set
+ TRR_MODE_NOT_ENABLED = 6, // mode0
+ TRR_FAILED = 7, // unknown failure
+ TRR_MODE_UNHANDLED_DEFAULT = 8, // Unhandled case in ComputeEffectiveMode
+ TRR_MODE_UNHANDLED_DISABLED = 9, // Unhandled case in ComputeEffectiveMode
+ TRR_DISABLED_FLAG = 10, // the DISABLE_TRR flag was set
+ TRR_TIMEOUT = 11, // the TRR channel timed out
+ TRR_CHANNEL_DNS_FAIL = 12, // DoH server name failed to resolve
+ TRR_IS_OFFLINE = 13, // The browser is offline/no interfaces up
+ TRR_NOT_CONFIRMED = 14, // TRR confirmation is not done yet
+ TRR_DID_NOT_MAKE_QUERY = 15, // TrrLookup exited without doing a TRR query
+ TRR_UNKNOWN_CHANNEL_FAILURE = 16, // unknown channel failure reason
+ TRR_HOST_BLOCKED_TEMPORARY = 17, // host blocklisted
+ TRR_SEND_FAILED = 18, // The call to TRR::SendHTTPRequest failed
+ TRR_NET_RESET = 19, // NS_ERROR_NET_RESET
+ TRR_NET_TIMEOUT = 20, // NS_ERROR_NET_TIMEOUT
+ TRR_NET_REFUSED = 21, // NS_ERROR_CONNECTION_REFUSED
+ TRR_NET_INTERRUPT = 22, // NS_ERROR_NET_INTERRUPT
+ TRR_NET_INADEQ_SEQURITY = 23, // NS_ERROR_NET_INADEQUATE_SECURITY
+ TRR_NO_ANSWERS = 24, // TRR returned no answers
+ TRR_DECODE_FAILED = 25, // DohDecode failed
+ TRR_EXCLUDED = 26, // ExcludedFromTRR
+ TRR_SERVER_RESPONSE_ERR = 27, // Server responded with non-200 code
+ TRR_RCODE_FAIL = 28, // DNS response contains a non-NOERROR rcode
+ TRR_NO_CONNECTIVITY = 29, // Not confirmed because of no connectivity
+ TRR_NXDOMAIN = 30, // DNS response contains NXDOMAIN rcode (0x03)
+ TRR_REQ_CANCELLED = 31, // The request has been cancelled
+ ODOH_KEY_NOT_USABLE = 32, // We don't have a valid ODoHConfig to use.
+ ODOH_UPDATE_KEY_FAILED = 33, // Failed to update the ODoHConfigs.
+ ODOH_KEY_NOT_AVAILABLE = 34, // ODoH requests timeout because of no key.
+ ODOH_ENCRYPTION_FAILED = 35, // Failed to encrypt DNS packets.
+ ODOH_DECRYPTION_FAILED = 36, // Failed to decrypt DNS packets.
+ TRR_HEURISTIC_TRIPPED_GOOGLE_SAFESEARCH = 37, // The google safesearch heuristic was tripped
+ TRR_HEURISTIC_TRIPPED_YOUTUBE_SAFESEARCH = 38, // The youtube safesearch heuristic was tripped
+ TRR_HEURISTIC_TRIPPED_ZSCALER_CANARY = 39, // The zscaler canary heuristic was tripped
+ TRR_HEURISTIC_TRIPPED_CANARY = 40, // The global canary heuristic was tripped
+ TRR_HEURISTIC_TRIPPED_MODIFIED_ROOTS = 41, // The modified roots (enterprise_roots cert pref) heuristic was tripped
+ TRR_HEURISTIC_TRIPPED_PARENTAL_CONTROLS = 42, // The parental controls heuristic was tripped
+ TRR_HEURISTIC_TRIPPED_THIRD_PARTY_ROOTS = 43, // The third party roots heuristic was tripped
+ TRR_HEURISTIC_TRIPPED_ENTERPRISE_POLICY = 44, // The enterprise policy heuristic was tripped
+ TRR_HEURISTIC_TRIPPED_VPN = 45, // The heuristic was tripped due to a vpn being detected
+ TRR_HEURISTIC_TRIPPED_PROXY = 46, // The heuristic was tripped due to a proxy being detected
+ TRR_HEURISTIC_TRIPPED_NRPT = 47, // The heuristic was tripped due to a NRPT being detected
+ TRR_BAD_URL = 48, // We attempted to use a bad URL (doesn't parse or is not https).
+ };
+};
+
+%{ C++
+namespace mozilla {
+namespace net {
+
+using TRRSkippedReason = nsITRRSkipReason::value;
+
+inline bool IsRelevantTRRSkipReason(TRRSkippedReason aReason) {
+ // - TRR_REQ_MODE_DISABLED - these requests are intentionally skipping TRR.
+ // These include DNS queries used to bootstrap the TRR connection,
+ // captive portal checks, connectivity checks, etc.
+ // Since we don't want to use TRR for these connections, we don't need
+ // to include them with other relevant skip reasons.
+ // - TRR_DISABLED_FLAG - This reason is used when retrying failed connections,
+ // sync DNS resolves on the main thread, or requests coming from
+ // webextensions that choose to skip TRR
+ // - TRR_EXCLUDED - This reason is used when a certain domain is excluded
+ // from TRR because it is explicitly set by the user, or because it
+ // is part of the user's DNS suffix list, indicating a host that is likely
+ // to be on the local network.
+ if (aReason == TRRSkippedReason::TRR_REQ_MODE_DISABLED ||
+ aReason == TRRSkippedReason::TRR_DISABLED_FLAG ||
+ aReason == TRRSkippedReason::TRR_EXCLUDED) {
+ return false;
+ }
+ return true;
+}
+
+inline bool IsBlockedTRRRequest(TRRSkippedReason aReason) {
+ // See TRR::MaybeBlockRequest. These are the reasons that could block sending
+ // TRR requests.
+ return (aReason == TRRSkippedReason::TRR_EXCLUDED ||
+ aReason == TRRSkippedReason::TRR_MODE_NOT_ENABLED ||
+ aReason == TRRSkippedReason::TRR_HOST_BLOCKED_TEMPORARY);
+}
+
+inline bool IsNonRecoverableTRRSkipReason(TRRSkippedReason aReason) {
+ // These are non-recoverable reasons and we'll fallback to native without
+ // retrying.
+ return (aReason == TRRSkippedReason::TRR_NXDOMAIN ||
+ aReason == TRRSkippedReason::TRR_NO_ANSWERS ||
+ aReason == TRRSkippedReason::TRR_DISABLED_FLAG ||
+ aReason == TRRSkippedReason::TRR_RCODE_FAIL);
+}
+
+inline bool IsFailedConfirmationOrNoConnectivity(TRRSkippedReason aReason) {
+ // TRR is in non-confirmed state now, so we don't try to use TRR at all.
+ return (aReason == TRRSkippedReason::TRR_NOT_CONFIRMED ||
+ aReason == TRRSkippedReason::TRR_NO_CONNECTIVITY);
+}
+
+extern nsresult GetTRRSkipReasonName(TRRSkippedReason aReason, nsACString& aName);
+
+} // net
+} // mozilla
+%}
diff --git a/netwerk/dns/nsPIDNSService.idl b/netwerk/dns/nsPIDNSService.idl
new file mode 100644
index 0000000000..6d65e6be41
--- /dev/null
+++ b/netwerk/dns/nsPIDNSService.idl
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "nsIDNSService.idl"
+
+/**
+ * This is a private interface used by the internals of the networking library.
+ * It will never be frozen. Do not use it in external code.
+ */
+[scriptable, builtinclass, uuid(24e598fd-7b1a-436c-9154-14d8b38df8a5)]
+interface nsPIDNSService : nsIDNSService
+{
+ /**
+ * called to initialize the DNS service.
+ */
+ void init();
+
+ /**
+ * called to shutdown the DNS service. any pending asynchronous
+ * requests will be canceled, and the local cache of DNS records
+ * will be cleared. NOTE: the operating system may still have
+ * its own cache of DNS records, which would be unaffected by
+ * this method.
+ */
+ void shutdown();
+
+ /**
+ * Whether or not DNS prefetching (aka RESOLVE_SPECULATE) is enabled
+ */
+ attribute boolean prefetchEnabled;
+};
diff --git a/netwerk/dns/prepare_tlds.py b/netwerk/dns/prepare_tlds.py
new file mode 100644
index 0000000000..adebcec487
--- /dev/null
+++ b/netwerk/dns/prepare_tlds.py
@@ -0,0 +1,150 @@
+# 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/.
+
+import codecs
+import encodings.idna
+import re
+import sys
+
+from make_dafsa import words_to_bin, words_to_cxx
+
+"""
+Processes a file containing effective TLD data. See the following URL for a
+description of effective TLDs and of the file format that this script
+processes (although for the latter you're better off just reading this file's
+short source code).
+
+http://wiki.mozilla.org/Gecko:Effective_TLD_Service
+"""
+
+
+def getEffectiveTLDs(path):
+ file = codecs.open(path, "r", "UTF-8")
+ domains = set()
+ for line in file:
+ # line always contains a line terminator unless the file is empty
+ if len(line) == 0:
+ raise StopIteration
+ line = line.rstrip()
+ # comment, empty, or superfluous line for explicitness purposes
+ if line.startswith("//") or not line.strip():
+ continue
+ line = re.split(r"[ \t\n]", line, 1)[0]
+ entry = EffectiveTLDEntry(line)
+ domain = entry.domain()
+ assert domain not in domains, "repeating domain %s makes no sense" % domain
+ domains.add(domain)
+ yield entry
+
+
+def _normalizeHostname(domain):
+ """
+ Normalizes the given domain, component by component. ASCII components are
+ lowercased, while non-ASCII components are processed using the ToASCII
+ algorithm.
+ """
+
+ def convertLabel(label):
+ if _isASCII(label):
+ return label.lower()
+ return encodings.idna.ToASCII(label).decode("utf-8")
+
+ return ".".join(map(convertLabel, domain.split(".")))
+
+
+def _isASCII(s):
+ "True if s consists entirely of ASCII characters, false otherwise."
+ for c in s:
+ if ord(c) > 127:
+ return False
+ return True
+
+
+class EffectiveTLDEntry:
+ """
+ Stores an entry in an effective-TLD name file.
+ """
+
+ _exception = False
+ _wild = False
+
+ def __init__(self, line):
+ """
+ Creates a TLD entry from a line of data, which must have been stripped of
+ the line ending.
+ """
+ if line.startswith("!"):
+ self._exception = True
+ domain = line[1:]
+ elif line.startswith("*."):
+ self._wild = True
+ domain = line[2:]
+ else:
+ domain = line
+ self._domain = _normalizeHostname(domain)
+
+ def domain(self):
+ "The domain this represents."
+ return self._domain
+
+ def exception(self):
+ "True if this entry's domain denotes does not denote an effective TLD."
+ return self._exception
+
+ def wild(self):
+ "True if this entry represents a class of effective TLDs."
+ return self._wild
+
+
+#################
+# DO EVERYTHING #
+#################
+
+
+def main(output, effective_tld_filename, output_format="cxx"):
+ """
+ effective_tld_filename is the effective TLD file to parse.
+ based on the output format, either a C++ array of a binary representation
+ of a DAFSA representing the eTLD file is then printed to standard output
+ or a binary file is written to disk.
+ """
+
+ def typeEnum(etld):
+ """
+ Maps the flags to the DAFSA's enum types.
+ """
+ if etld.exception():
+ return 1
+ elif etld.wild():
+ return 2
+ else:
+ return 0
+
+ def dafsa_words():
+ """
+ make_dafsa expects lines of the form "<domain_name><enum_value>"
+ """
+ for etld in getEffectiveTLDs(effective_tld_filename):
+ yield "%s%d" % (etld.domain(), typeEnum(etld))
+
+ """ words_to_bin() returns a bytes while words_to_cxx() returns string """
+ if output_format == "bin":
+ output.write(words_to_bin(dafsa_words()))
+ else:
+ output.write(words_to_cxx(dafsa_words()))
+
+
+if __name__ == "__main__":
+ """
+ This program can output the DAFSA in two formats:
+ as C++ code that will be included and compiled at build time
+ or as a binary file that will be published in Remote Settings.
+
+ Flags for format options:
+ "cxx" -> C++ array [default]
+ "bin" -> Binary file
+ """
+
+ output_format = "bin" if "--bin" in sys.argv else "cxx"
+ main(sys.stdout, sys.argv[1], output_format=output_format)
diff --git a/netwerk/dns/punycode.c b/netwerk/dns/punycode.c
new file mode 100644
index 0000000000..4653216507
--- /dev/null
+++ b/netwerk/dns/punycode.c
@@ -0,0 +1,325 @@
+/*
+punycode.c from RFC 3492
+http://www.nicemice.net/idn/
+Adam M. Costello
+http://www.nicemice.net/amc/
+
+This is ANSI C code (C89) implementing Punycode (RFC 3492).
+
+
+C. Disclaimer and license
+
+ Regarding this entire document or any portion of it (including
+ the pseudocode and C code), the author makes no guarantees and
+ is not responsible for any damage resulting from its use. The
+ author grants irrevocable permission to anyone to use, modify,
+ and distribute it in any way that does not diminish the rights
+ of anyone else to use, modify, and distribute it, provided that
+ redistributed derivative works do not contain misleading author or
+ version information. Derivative works need not be licensed under
+ similar terms.
+*/
+
+#include "punycode.h"
+
+/**********************************************************/
+/* Implementation (would normally go in its own .c file): */
+
+#include <string.h>
+
+/*** Bootstring parameters for Punycode ***/
+
+enum {
+ base = 36,
+ tmin = 1,
+ tmax = 26,
+ skew = 38,
+ damp = 700,
+ initial_bias = 72,
+ initial_n = 0x80,
+ delimiter = 0x2D
+};
+
+/* basic(cp) tests whether cp is a basic code point: */
+#define basic(cp) ((punycode_uint)(cp) < 0x80)
+
+/* delim(cp) tests whether cp is a delimiter: */
+#define delim(cp) ((cp) == delimiter)
+
+/* decode_digit(cp) returns the numeric value of a basic code */
+/* point (for use in representing integers) in the range 0 to */
+/* base-1, or base if cp is does not represent a value. */
+
+static punycode_uint decode_digit(punycode_uint cp) {
+ return cp - 48 < 10 ? cp - 22
+ : cp - 65 < 26 ? cp - 65
+ : cp - 97 < 26 ? cp - 97
+ : base;
+}
+
+/* encode_digit(d,flag) returns the basic code point whose value */
+/* (when used for representing integers) is d, which needs to be in */
+/* the range 0 to base-1. The lowercase form is used unless flag is */
+/* nonzero, in which case the uppercase form is used. The behavior */
+/* is undefined if flag is nonzero and digit d has no uppercase form. */
+
+static char encode_digit(punycode_uint d, int flag) {
+ return d + 22 + 75 * (d < 26) - ((flag != 0) << 5);
+ /* 0..25 map to ASCII a..z or A..Z */
+ /* 26..35 map to ASCII 0..9 */
+}
+
+/* flagged(bcp) tests whether a basic code point is flagged */
+/* (uppercase). The behavior is undefined if bcp is not a */
+/* basic code point. */
+
+#define flagged(bcp) ((punycode_uint)(bcp)-65 < 26)
+
+/* encode_basic(bcp,flag) forces a basic code point to lowercase */
+/* if flag is zero, uppercase if flag is nonzero, and returns */
+/* the resulting code point. The code point is unchanged if it */
+/* is caseless. The behavior is undefined if bcp is not a basic */
+/* code point. */
+
+static char encode_basic(punycode_uint bcp, int flag) {
+ bcp -= (bcp - 97 < 26) << 5;
+ return bcp + ((!flag && (bcp - 65 < 26)) << 5);
+}
+
+/*** Platform-specific constants ***/
+
+/* maxint is the maximum value of a punycode_uint variable: */
+static const punycode_uint maxint = (punycode_uint)-1;
+/* Because maxint is unsigned, -1 becomes the maximum value. */
+
+/*** Bias adaptation function ***/
+
+static punycode_uint adapt(punycode_uint delta, punycode_uint numpoints,
+ int firsttime) {
+ punycode_uint k;
+
+ delta = firsttime ? delta / damp : delta >> 1;
+ /* delta >> 1 is a faster way of doing delta / 2 */
+ delta += delta / numpoints;
+
+ for (k = 0; delta > ((base - tmin) * tmax) / 2; k += base) {
+ delta /= base - tmin;
+ }
+
+ return k + (base - tmin + 1) * delta / (delta + skew);
+}
+
+/*** Main encode function ***/
+
+enum punycode_status punycode_encode(punycode_uint input_length,
+ const punycode_uint input[],
+ const unsigned char case_flags[],
+ punycode_uint* output_length,
+ char output[]) {
+ punycode_uint n, delta, h, b, out, max_out, bias, j, m, q, k, t;
+
+ /* Initialize the state: */
+
+ n = initial_n;
+ delta = out = 0;
+ max_out = *output_length;
+ bias = initial_bias;
+
+ /* Handle the basic code points: */
+
+ for (j = 0; j < input_length; ++j) {
+ if (basic(input[j])) {
+ if (max_out - out < 2) {
+ return punycode_big_output;
+ }
+ output[out++] =
+ case_flags ? encode_basic(input[j], case_flags[j]) : (char)input[j];
+ }
+ /* else if (input[j] < n) return punycode_bad_input; */
+ /* (not needed for Punycode with unsigned code points) */
+ }
+
+ h = b = out;
+
+ /* h is the number of code points that have been handled, b is the */
+ /* number of basic code points, and out is the number of characters */
+ /* that have been output. */
+
+ if (b > 0) {
+ output[out++] = delimiter;
+ }
+
+ /* Main encoding loop: */
+
+ while (h < input_length) {
+ /* All non-basic code points < n have been */
+ /* handled already. Find the next larger one: */
+
+ for (m = maxint, j = 0; j < input_length; ++j) {
+ /* if (basic(input[j])) continue; */
+ /* (not needed for Punycode) */
+ if (input[j] >= n && input[j] < m) {
+ m = input[j];
+ }
+ }
+
+ /* Increase delta enough to advance the decoder's */
+ /* <n,i> state to <m,0>, but guard against overflow: */
+
+ if (m - n > (maxint - delta) / (h + 1)) {
+ return punycode_overflow;
+ }
+ delta += (m - n) * (h + 1);
+ n = m;
+
+ for (j = 0; j < input_length; ++j) {
+ /* Punycode does not need to check whether input[j] is basic: */
+ if (input[j] < n /* || basic(input[j]) */) {
+ if (++delta == 0) {
+ return punycode_overflow;
+ }
+ }
+
+ if (input[j] == n) {
+ /* Represent delta as a generalized variable-length integer: */
+
+ for (q = delta, k = base;; k += base) {
+ if (out >= max_out) {
+ return punycode_big_output;
+ }
+ t = k <= bias /* + tmin */ ? tmin : /* +tmin not needed */
+ k >= bias + tmax ? tmax
+ : k - bias;
+ if (q < t) {
+ break;
+ }
+ output[out++] = encode_digit(t + (q - t) % (base - t), 0);
+ q = (q - t) / (base - t);
+ }
+
+ output[out++] = encode_digit(q, case_flags && case_flags[j]);
+ bias = adapt(delta, h + 1, h == b);
+ delta = 0;
+ ++h;
+ }
+ }
+
+ ++delta, ++n;
+ }
+
+ *output_length = out;
+ return punycode_success;
+}
+
+/*** Main decode function ***/
+
+enum punycode_status punycode_decode(punycode_uint input_length,
+ const char input[],
+ punycode_uint* output_length,
+ punycode_uint output[],
+ unsigned char case_flags[]) {
+ punycode_uint n, out, i, max_out, bias, b, j, in, oldi, w, k, digit, t;
+
+ if (!input_length) {
+ return punycode_bad_input;
+ }
+
+ /* Initialize the state: */
+
+ n = initial_n;
+ out = i = 0;
+ max_out = *output_length;
+ bias = initial_bias;
+
+ /* Handle the basic code points: Let b be the number of input code */
+ /* points before the last delimiter, or 0 if there is none, then */
+ /* copy the first b code points to the output. */
+
+ for (b = 0, j = input_length - 1; j > 0; --j) {
+ if (delim(input[j])) {
+ b = j;
+ break;
+ }
+ }
+ if (b > max_out) {
+ return punycode_big_output;
+ }
+
+ for (j = 0; j < b; ++j) {
+ if (case_flags) {
+ case_flags[out] = flagged(input[j]);
+ }
+ if (!basic(input[j])) {
+ return punycode_bad_input;
+ }
+ output[out++] = input[j];
+ }
+
+ /* Main decoding loop: Start just after the last delimiter if any */
+ /* basic code points were copied; start at the beginning otherwise. */
+
+ for (in = b > 0 ? b + 1 : 0; in < input_length; ++out) {
+ /* in is the index of the next character to be consumed, and */
+ /* out is the number of code points in the output array. */
+
+ /* Decode a generalized variable-length integer into delta, */
+ /* which gets added to i. The overflow checking is easier */
+ /* if we increase i as we go, then subtract off its starting */
+ /* value at the end to obtain delta. */
+
+ for (oldi = i, w = 1, k = base;; k += base) {
+ if (in >= input_length) {
+ return punycode_bad_input;
+ }
+ digit = decode_digit(input[in++]);
+ if (digit >= base) {
+ return punycode_bad_input;
+ }
+ if (digit > (maxint - i) / w) {
+ return punycode_overflow;
+ }
+ i += digit * w;
+ t = k <= bias /* + tmin */ ? tmin : /* +tmin not needed */
+ k >= bias + tmax ? tmax
+ : k - bias;
+ if (digit < t) {
+ break;
+ }
+ if (w > maxint / (base - t)) {
+ return punycode_overflow;
+ }
+ w *= (base - t);
+ }
+
+ bias = adapt(i - oldi, out + 1, oldi == 0);
+
+ /* i was supposed to wrap around from out+1 to 0, */
+ /* incrementing n each time, so we'll fix that now: */
+
+ if (i / (out + 1) > maxint - n) {
+ return punycode_overflow;
+ }
+ n += i / (out + 1);
+ i %= (out + 1);
+
+ /* Insert n at position i of the output: */
+
+ /* not needed for Punycode: */
+ /* if (decode_digit(n) <= base) return punycode_invalid_input; */
+ if (out >= max_out) {
+ return punycode_big_output;
+ }
+
+ if (case_flags) {
+ memmove(case_flags + i + 1, case_flags + i, out - i);
+ /* Case of last character determines uppercase flag: */
+ case_flags[i] = flagged(input[in - 1]);
+ }
+
+ memmove(output + i + 1, output + i, (out - i) * sizeof *output);
+ output[i++] = n;
+ }
+
+ *output_length = out;
+ return punycode_success;
+}
diff --git a/netwerk/dns/punycode.h b/netwerk/dns/punycode.h
new file mode 100644
index 0000000000..6a626c7a07
--- /dev/null
+++ b/netwerk/dns/punycode.h
@@ -0,0 +1,106 @@
+/*
+punycode.c from RFC 3492
+http://www.nicemice.net/idn/
+Adam M. Costello
+http://www.nicemice.net/amc/
+
+This is ANSI C code (C89) implementing Punycode (RFC 3492).
+
+
+
+C. Disclaimer and license
+
+ Regarding this entire document or any portion of it (including
+ the pseudocode and C code), the author makes no guarantees and
+ is not responsible for any damage resulting from its use. The
+ author grants irrevocable permission to anyone to use, modify,
+ and distribute it in any way that does not diminish the rights
+ of anyone else to use, modify, and distribute it, provided that
+ redistributed derivative works do not contain misleading author or
+ version information. Derivative works need not be licensed under
+ similar terms.
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/************************************************************/
+/* Public interface (would normally go in its own .h file): */
+
+#include <limits.h>
+
+enum punycode_status {
+ punycode_success,
+ punycode_bad_input, /* Input is invalid. */
+ punycode_big_output, /* Output would exceed the space provided. */
+ punycode_overflow /* Input needs wider integers to process. */
+};
+
+#if UINT_MAX >= (1 << 26) - 1
+typedef unsigned int punycode_uint;
+#else
+typedef unsigned long punycode_uint;
+#endif
+
+enum punycode_status punycode_encode(punycode_uint input_length,
+ const punycode_uint input[],
+ const unsigned char case_flags[],
+ punycode_uint* output_length,
+ char output[]);
+
+/* punycode_encode() converts Unicode to Punycode. The input */
+/* is represented as an array of Unicode code points (not code */
+/* units; surrogate pairs are not allowed), and the output */
+/* will be represented as an array of ASCII code points. The */
+/* output string is *not* null-terminated; it will contain */
+/* zeros if and only if the input contains zeros. (Of course */
+/* the caller can leave room for a terminator and add one if */
+/* needed.) The input_length is the number of code points in */
+/* the input. The output_length is an in/out argument: the */
+/* caller passes in the maximum number of code points that it */
+/* can receive, and on successful return it will contain the */
+/* number of code points actually output. The case_flags array */
+/* holds input_length boolean values, where nonzero suggests that */
+/* the corresponding Unicode character be forced to uppercase */
+/* after being decoded (if possible), and zero suggests that */
+/* it be forced to lowercase (if possible). ASCII code points */
+/* are encoded literally, except that ASCII letters are forced */
+/* to uppercase or lowercase according to the corresponding */
+/* uppercase flags. If case_flags is a null pointer then ASCII */
+/* letters are left as they are, and other code points are */
+/* treated as if their uppercase flags were zero. The return */
+/* value can be any of the punycode_status values defined above */
+/* except punycode_bad_input; if not punycode_success, then */
+/* output_size and output might contain garbage. */
+
+enum punycode_status punycode_decode(punycode_uint input_length,
+ const char input[],
+ punycode_uint* output_length,
+ punycode_uint output[],
+ unsigned char case_flags[]);
+
+/* punycode_decode() converts Punycode to Unicode. The input is */
+/* represented as an array of ASCII code points, and the output */
+/* will be represented as an array of Unicode code points. The */
+/* input_length is the number of code points in the input. The */
+/* output_length is an in/out argument: the caller passes in */
+/* the maximum number of code points that it can receive, and */
+/* on successful return it will contain the actual number of */
+/* code points output. The case_flags array needs room for at */
+/* least output_length values, or it can be a null pointer if the */
+/* case information is not needed. A nonzero flag suggests that */
+/* the corresponding Unicode character be forced to uppercase */
+/* by the caller (if possible), while zero suggests that it be */
+/* forced to lowercase (if possible). ASCII code points are */
+/* output already in the proper case, but their flags will be set */
+/* appropriately so that applying the flags would be harmless. */
+/* The return value can be any of the punycode_status values */
+/* defined above; if not punycode_success, then output_length, */
+/* output, and case_flags might contain garbage. On success, the */
+/* decoder will never need to write an output_length greater than */
+/* input_length, because of how the encoding is defined. */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
diff --git a/netwerk/dns/tests/moz.build b/netwerk/dns/tests/moz.build
new file mode 100644
index 0000000000..fb176c1bd3
--- /dev/null
+++ b/netwerk/dns/tests/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DIRS += ["unit"]
diff --git a/netwerk/dns/tests/unit/data/fake_public_suffix_list.dat b/netwerk/dns/tests/unit/data/fake_public_suffix_list.dat
new file mode 100644
index 0000000000..ca1da32eeb
--- /dev/null
+++ b/netwerk/dns/tests/unit/data/fake_public_suffix_list.dat
@@ -0,0 +1,57 @@
+// 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 https://mozilla.org/MPL/2.0/.
+
+// This is a fake suffix list created only for the purposes of testing,
+// The real PSL is rather large thus this file is kept small by cutting out most of the suffixes
+
+// The Original list can be found at https://publicsuffix.org/list/public_suffix_list.dat,
+// Learn more about the PSL at https://publicsuffix.org.
+
+// .xpcshelltest is the fake domain created specifically for the the tests while
+// the others are ripped from the real list, serving as examples.
+
+// This fake public suffix list was used to create the binary file fake_remote_dafsa.bin
+// The binary is built at compile time and can be found at
+// obj-dir/_tests/xpcshell/netwerk/dns/tests/unit/data/
+
+// The list created with help of netwerk/dns/prepare_tlds.py and xpcom/ds/tools/make_dafsa.py
+// The build directive for the binary is at moz.build at netwerk/dns/tests/unit/data/
+
+
+
+// ===BEGIN ICANN DOMAINS===
+
+// xpcshelltest : Used in tests
+xpcshelltest
+website.xpcshelltest
+com.xpcshelltest
+edu.xpcshelltest
+gov.xpcshelltest
+net.xpcshelltest
+mil.xpcshelltest
+org.xpcshelltest
+
+// ac : https://en.wikipedia.org/wiki/.ac
+ac
+coc.ac
+com.ac
+edu.ac
+gov.ac
+net.ac
+mil.ac
+org.ac
+
+// bj : https://en.wikipedia.org/wiki/.bj
+bj
+asso.bj
+barreau.bj
+gouv.bj
+
+// bm : http://www.bermudanic.bm/dnr-text.txt
+bm
+com.bm
+edu.bm
+gov.bm
+net.bm
+org.bm
diff --git a/netwerk/dns/tests/unit/data/moz.build b/netwerk/dns/tests/unit/data/moz.build
new file mode 100644
index 0000000000..0f5a164f77
--- /dev/null
+++ b/netwerk/dns/tests/unit/data/moz.build
@@ -0,0 +1,14 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+GeneratedFile(
+ "fake_remote_dafsa.bin",
+ script="../../../prepare_tlds.py",
+ inputs=["fake_public_suffix_list.dat"],
+ flags=["bin"],
+)
+
+TEST_HARNESS_FILES.xpcshell.netwerk.dns.tests.unit.data += ["!fake_remote_dafsa.bin"]
diff --git a/netwerk/dns/tests/unit/moz.build b/netwerk/dns/tests/unit/moz.build
new file mode 100644
index 0000000000..e5ce568e70
--- /dev/null
+++ b/netwerk/dns/tests/unit/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DIRS += ["data"]
diff --git a/netwerk/dns/tests/unit/test_PublicSuffixList.js b/netwerk/dns/tests/unit/test_PublicSuffixList.js
new file mode 100644
index 0000000000..ad61abea8e
--- /dev/null
+++ b/netwerk/dns/tests/unit/test_PublicSuffixList.js
@@ -0,0 +1,174 @@
+/* Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { PublicSuffixList } = ChromeUtils.importESModule(
+ "resource://gre/modules/netwerk-dns/PublicSuffixList.sys.mjs"
+);
+const { TestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/TestUtils.sys.mjs"
+);
+
+const CLIENT = PublicSuffixList.CLIENT;
+const SIGNAL = "public-suffix-list-updated";
+
+const PAYLOAD_UPDATED_RECORDS = {
+ current: [{ id: "tld-dafsa", "commit-hash": "current-commit-hash" }],
+ created: [],
+ updated: [
+ {
+ old: { id: "tld-dafsa", "commit-hash": "current-commit-hash" },
+ new: { id: "tld-dafsa", "commit-hash": "new-commit-hash" },
+ },
+ ],
+ deleted: [],
+};
+const PAYLOAD_CREATED_RECORDS = {
+ current: [],
+ created: [
+ {
+ id: "tld-dafsa",
+ "commit-hash": "new-commit-hash",
+ attachment: {},
+ },
+ ],
+ updated: [],
+ deleted: [],
+};
+const PAYLOAD_UPDATED_AND_CREATED_RECORDS = {
+ current: [{ id: "tld-dafsa", "commit-hash": "current-commit-hash" }],
+ created: [{ id: "tld-dafsa", "commit-hash": "another-commit-hash" }],
+ updated: [
+ {
+ old: { id: "tld-dafsa", "commit-hash": "current-commit-hash" },
+ new: { id: "tld-dafsa", "commit-hash": "new-commit-hash" },
+ },
+ ],
+ deleted: [],
+};
+
+const fakeDafsaBinFile = do_get_file("data/fake_remote_dafsa.bin");
+const mockedFilePath = fakeDafsaBinFile.path;
+
+function setup() {
+ Services.prefs.setBoolPref("network.psl.onUpdate_notify", true);
+}
+setup();
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("network.psl.onUpdate_notify");
+});
+
+/**
+ * downloadToDiskCalled is used by mockDownloadToDisk() and resetMockDownloadToDisk()
+ * to keep track weather CLIENT.attachments.download is called or not
+ * downloadToDiskBackup will help restore CLIENT.attachments.download to original definition
+ * notifyUpdateBackup will help restore PublicSuffixList.notifyUpdate to original definition
+ */
+let downloadToDiskCalled = false;
+const downloadToDiskBackup = CLIENT.attachments.downloadToDisk;
+
+// returns a fake fileURI and sends a signal with filePath and no nsifile
+function mockDownloadToDisk() {
+ downloadToDiskCalled = false;
+ CLIENT.attachments.downloadToDisk = async rec => {
+ downloadToDiskCalled = true;
+ return `file://${mockedFilePath}`; // Create a fake file URI
+ };
+}
+
+// resetMockDownloadToDisk() must be run at the end of the test that uses mockDownloadToDisk()
+const resetMockDownloadToDisk = () => {
+ CLIENT.attachments.downloadToDisk = downloadToDiskBackup;
+};
+
+add_task(async () => {
+ info("File path sent when record is in DB.");
+
+ await CLIENT.db.clear(); // Make sure there's no record initially
+ await CLIENT.db.create({
+ id: "tld-dafsa",
+ "commit-hash": "fake-commit-hash",
+ attachment: {},
+ });
+
+ mockDownloadToDisk();
+
+ const promiseSignal = TestUtils.topicObserved(SIGNAL);
+ await PublicSuffixList.init();
+ const observed = await promiseSignal;
+
+ Assert.equal(
+ observed[1],
+ mockedFilePath,
+ "File path sent when record is in DB."
+ );
+ await CLIENT.db.clear(); // Clean up the mockDownloaded record
+ resetMockDownloadToDisk();
+});
+
+add_task(async () => {
+ info("File path sent when record updated.");
+
+ mockDownloadToDisk();
+
+ const promiseSignal = TestUtils.topicObserved(SIGNAL);
+ await PublicSuffixList.init();
+ await CLIENT.emit("sync", { data: PAYLOAD_UPDATED_RECORDS });
+ const observed = await promiseSignal;
+
+ Assert.equal(
+ observed[1],
+ mockedFilePath,
+ "File path sent when record updated."
+ );
+ resetMockDownloadToDisk();
+});
+
+add_task(async () => {
+ info("Attachment downloaded when record created.");
+
+ mockDownloadToDisk();
+
+ await PublicSuffixList.init();
+ await CLIENT.emit("sync", { data: PAYLOAD_CREATED_RECORDS });
+
+ Assert.equal(
+ downloadToDiskCalled,
+ true,
+ "Attachment downloaded when record created."
+ );
+ resetMockDownloadToDisk();
+});
+
+add_task(async () => {
+ info("Attachment downloaded when record updated.");
+
+ mockDownloadToDisk();
+
+ await PublicSuffixList.init();
+ await CLIENT.emit("sync", { data: PAYLOAD_UPDATED_RECORDS });
+
+ Assert.equal(
+ downloadToDiskCalled,
+ true,
+ "Attachment downloaded when record updated."
+ );
+ resetMockDownloadToDisk();
+});
+
+add_task(async () => {
+ info("No download when more than one record is changed.");
+
+ mockDownloadToDisk();
+
+ await PublicSuffixList.init();
+ await CLIENT.emit("sync", { data: PAYLOAD_UPDATED_AND_CREATED_RECORDS });
+
+ Assert.equal(
+ downloadToDiskCalled,
+ false,
+ "No download when more than one record is changed."
+ );
+ resetMockDownloadToDisk();
+});
diff --git a/netwerk/dns/tests/unit/test_nsEffectiveTLDService_Reload_DAFSA.js b/netwerk/dns/tests/unit/test_nsEffectiveTLDService_Reload_DAFSA.js
new file mode 100644
index 0000000000..6b89b070d5
--- /dev/null
+++ b/netwerk/dns/tests/unit/test_nsEffectiveTLDService_Reload_DAFSA.js
@@ -0,0 +1,32 @@
+/* Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const SIGNAL = "public-suffix-list-updated";
+
+add_task(async () => {
+ info("Before fake dafsa reload.");
+
+ let suffix = Services.eTLD.getPublicSuffixFromHost("website.xpcshelltest");
+ Assert.equal(
+ suffix,
+ "xpcshelltest",
+ "Fake Suffix does not exist in current PSL."
+ );
+});
+
+add_task(async () => {
+ info("After fake dafsa reload.");
+
+ // reload the PSL with fake data containing .xpcshelltest
+ const fakeDafsaFile = do_get_file("data/fake_remote_dafsa.bin");
+ Services.obs.notifyObservers(fakeDafsaFile, SIGNAL, fakeDafsaFile.path);
+
+ let suffix = Services.eTLD.getPublicSuffixFromHost("website.xpcshelltest");
+ Assert.equal(
+ suffix,
+ "website.xpcshelltest",
+ "Fake Suffix now exists in PSL after DAFSA reload."
+ );
+});
diff --git a/netwerk/dns/tests/unit/test_nsEffectiveTLDService_getKnownPublicSuffix.js b/netwerk/dns/tests/unit/test_nsEffectiveTLDService_getKnownPublicSuffix.js
new file mode 100644
index 0000000000..333692af97
--- /dev/null
+++ b/netwerk/dns/tests/unit/test_nsEffectiveTLDService_getKnownPublicSuffix.js
@@ -0,0 +1,44 @@
+/* Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests getPublicSuffix with the validate argument.
+ */
+
+"use strict";
+
+add_task(() => {
+ for (let [suffix, isKnown] of [
+ ["", false],
+ [null, false],
+ ["mozbacon", false],
+ ["com", true],
+ ["circle", true],
+ ["bd", true],
+ ["gov.bd", true],
+ ["ck", true],
+ ["www.ck", true],
+ ["bs", true],
+ ["com.bs", true],
+ ["網絡.cn", true],
+ ["valléedaoste.it", true],
+ ["aurskog-høland.no", true],
+ ["公司.香港", true],
+ ["भारतम्", true],
+ ["فلسطين", true],
+ ]) {
+ let origin = "test." + suffix;
+ Assert.equal(
+ !!Services.eTLD.getKnownPublicSuffixFromHost(origin),
+ isKnown,
+ `"${suffix}" should ${isKnown ? " " : "not "}be a known public suffix`
+ );
+ Assert.equal(
+ !!Services.eTLD.getKnownPublicSuffix(
+ Services.io.newURI("http://" + origin)
+ ),
+ isKnown,
+ `"${suffix}" should ${isKnown ? " " : "not "}be a known public suffix`
+ );
+ }
+});
diff --git a/netwerk/dns/tests/unit/test_nsEffectiveTLDService_getSite.js b/netwerk/dns/tests/unit/test_nsEffectiveTLDService_getSite.js
new file mode 100644
index 0000000000..5f6f3b1eb3
--- /dev/null
+++ b/netwerk/dns/tests/unit/test_nsEffectiveTLDService_getSite.js
@@ -0,0 +1,77 @@
+/* Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests getSite and getSchemelessSite with example arguments
+ */
+
+"use strict";
+
+add_task(() => {
+ for (let [originString, result] of [
+ ["http://.", null],
+ ["http://com", "http://com"],
+ ["http://test", "http://test"],
+ ["http://test.", "http://test."],
+ ["http://[::1]", "http://[::1]"],
+ ["http://[::1]:8888", "http://[::1]"],
+ ["http://localhost", "http://localhost"],
+ ["http://127.0.0.1", "http://127.0.0.1"],
+ ["http://user:pass@[::1]", "http://[::1]"],
+ ["http://example.com", "http://example.com"],
+ ["https://example.com", "https://example.com"],
+ ["https://test.example.com", "https://example.com"],
+ ["https://test1.test2.example.com", "https://example.com"],
+ ["https://test1.test2.example.co.uk", "https://example.co.uk"],
+ ["https://test.example.com:8888/index.html", "https://example.com"],
+ [
+ "https://test1.test2.example.公司.香港",
+ "https://example.xn--55qx5d.xn--j6w193g",
+ ],
+ ]) {
+ let origin = Services.io.newURI(originString);
+ if (result === null) {
+ Assert.throws(
+ () => {
+ Services.eTLD.getSite(origin);
+ },
+ /NS_ERROR_ILLEGAL_VALUE/,
+ "Invalid origin for getSite throws"
+ );
+ } else {
+ let answer = Services.eTLD.getSite(origin);
+ Assert.equal(
+ answer,
+ result,
+ `"${originString}" should have site ${result}, got ${answer}.`
+ );
+ }
+ }
+});
+
+add_task(() => {
+ for (let [originString, result] of [
+ ["http://com", "com"],
+ ["http://test", "test"],
+ ["http://test.", "test."],
+ ["http://[::1]", "[::1]"],
+ ["http://[::1]:8888", "[::1]"],
+ ["http://localhost", "localhost"],
+ ["http://127.0.0.1", "127.0.0.1"],
+ ["http://user:pass@[::1]", "[::1]"],
+ ["http://example.com", "example.com"],
+ ["https://example.com", "example.com"],
+ ["https://test.example.com", "example.com"],
+ ["https://test1.test2.example.com", "example.com"],
+ ["https://test1.test2.example.co.uk", "example.co.uk"],
+ ["https://test1.test2.example.公司.香港", "example.xn--55qx5d.xn--j6w193g"],
+ ]) {
+ let origin = Services.io.newURI(originString);
+ let answer = Services.eTLD.getSchemelessSite(origin);
+ Assert.equal(
+ answer,
+ result,
+ `"${originString}" should have schemeless site ${result}, got ${answer}.`
+ );
+ }
+});
diff --git a/netwerk/dns/tests/unit/xpcshell.toml b/netwerk/dns/tests/unit/xpcshell.toml
new file mode 100644
index 0000000000..1047b7d496
--- /dev/null
+++ b/netwerk/dns/tests/unit/xpcshell.toml
@@ -0,0 +1,17 @@
+[DEFAULT]
+head = "../../../../services/common/tests/unit/head_global.js ../../../../services/common/tests/unit/head_helpers.js"
+firefox-appdir = "browser"
+support-files = [
+ "data/**",
+ "!/services/common/tests/unit/head_global.js",
+ "!/services/common/tests/unit/head_helpers.js",
+]
+
+["test_PublicSuffixList.js"]
+tags = "remote-settings"
+
+["test_nsEffectiveTLDService_Reload_DAFSA.js"]
+
+["test_nsEffectiveTLDService_getKnownPublicSuffix.js"]
+
+["test_nsEffectiveTLDService_getSite.js"]