summaryrefslogtreecommitdiffstats
path: root/netwerk/dns
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--netwerk/dns/ChildDNSService.cpp450
-rw-r--r--netwerk/dns/ChildDNSService.h69
-rw-r--r--netwerk/dns/DNS.cpp382
-rw-r--r--netwerk/dns/DNS.h247
-rw-r--r--netwerk/dns/DNSByTypeRecord.h238
-rw-r--r--netwerk/dns/DNSListenerProxy.cpp37
-rw-r--r--netwerk/dns/DNSListenerProxy.h62
-rw-r--r--netwerk/dns/DNSPacket.cpp975
-rw-r--r--netwerk/dns/DNSPacket.h71
-rw-r--r--netwerk/dns/DNSRequestBase.h148
-rw-r--r--netwerk/dns/DNSRequestChild.cpp536
-rw-r--r--netwerk/dns/DNSRequestChild.h41
-rw-r--r--netwerk/dns/DNSRequestParent.cpp178
-rw-r--r--netwerk/dns/DNSRequestParent.h44
-rw-r--r--netwerk/dns/DNSResolverInfo.cpp19
-rw-r--r--netwerk/dns/DNSResolverInfo.h34
-rw-r--r--netwerk/dns/GetAddrInfo.cpp465
-rw-r--r--netwerk/dns/GetAddrInfo.h87
-rw-r--r--netwerk/dns/HTTPSSVC.cpp433
-rw-r--r--netwerk/dns/HTTPSSVC.h153
-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.cpp41
-rw-r--r--netwerk/dns/NativeDNSResolverOverrideChild.h38
-rw-r--r--netwerk/dns/NativeDNSResolverOverrideParent.cpp103
-rw-r--r--netwerk/dns/NativeDNSResolverOverrideParent.h32
-rw-r--r--netwerk/dns/PDNSRequest.ipdl39
-rw-r--r--netwerk/dns/PDNSRequestParams.ipdlh38
-rw-r--r--netwerk/dns/PNativeDNSResolverOverride.ipdl25
-rw-r--r--netwerk/dns/PTRRService.ipdl27
-rw-r--r--netwerk/dns/PublicSuffixList.jsm106
-rw-r--r--netwerk/dns/TRR.cpp965
-rw-r--r--netwerk/dns/TRR.h161
-rw-r--r--netwerk/dns/TRRQuery.cpp288
-rw-r--r--netwerk/dns/TRRQuery.h90
-rw-r--r--netwerk/dns/TRRService.cpp944
-rw-r--r--netwerk/dns/TRRService.h152
-rw-r--r--netwerk/dns/TRRServiceBase.cpp144
-rw-r--r--netwerk/dns/TRRServiceBase.h51
-rw-r--r--netwerk/dns/TRRServiceChild.cpp59
-rw-r--r--netwerk/dns/TRRServiceChild.h44
-rw-r--r--netwerk/dns/TRRServiceParent.cpp155
-rw-r--r--netwerk/dns/TRRServiceParent.h43
-rw-r--r--netwerk/dns/effective_tld_names.dat13597
-rw-r--r--netwerk/dns/mdns/libmdns/DNSServiceDiscovery.jsm219
-rw-r--r--netwerk/dns/mdns/libmdns/MDNSResponderOperator.cpp719
-rw-r--r--netwerk/dns/mdns/libmdns/MDNSResponderOperator.h133
-rw-r--r--netwerk/dns/mdns/libmdns/MDNSResponderReply.cpp215
-rw-r--r--netwerk/dns/mdns/libmdns/MDNSResponderReply.h130
-rw-r--r--netwerk/dns/mdns/libmdns/MulticastDNSAndroid.jsm262
-rw-r--r--netwerk/dns/mdns/libmdns/components.conf24
-rw-r--r--netwerk/dns/mdns/libmdns/fallback/DNSPacket.jsm304
-rw-r--r--netwerk/dns/mdns/libmdns/fallback/DNSRecord.jsm71
-rw-r--r--netwerk/dns/mdns/libmdns/fallback/DNSResourceRecord.jsm221
-rw-r--r--netwerk/dns/mdns/libmdns/fallback/DNSTypes.jsm99
-rw-r--r--netwerk/dns/mdns/libmdns/fallback/DataReader.jsm134
-rw-r--r--netwerk/dns/mdns/libmdns/fallback/DataWriter.jsm97
-rw-r--r--netwerk/dns/mdns/libmdns/fallback/MulticastDNS.jsm985
-rw-r--r--netwerk/dns/mdns/libmdns/moz.build51
-rw-r--r--netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.cpp228
-rw-r--r--netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.h47
-rw-r--r--netwerk/dns/mdns/libmdns/nsDNSServiceInfo.cpp170
-rw-r--r--netwerk/dns/mdns/libmdns/nsDNSServiceInfo.h49
-rw-r--r--netwerk/dns/mdns/moz.build13
-rw-r--r--netwerk/dns/mdns/nsIDNSServiceDiscovery.idl219
-rw-r--r--netwerk/dns/moz.build107
-rw-r--r--netwerk/dns/nsDNSService2.cpp1453
-rw-r--r--netwerk/dns/nsDNSService2.h109
-rw-r--r--netwerk/dns/nsEffectiveTLDService.cpp492
-rw-r--r--netwerk/dns/nsEffectiveTLDService.h92
-rw-r--r--netwerk/dns/nsHostResolver.cpp2325
-rw-r--r--netwerk/dns/nsHostResolver.h654
-rw-r--r--netwerk/dns/nsIDNKitInterface.h195
-rw-r--r--netwerk/dns/nsIDNSByTypeRecord.idl126
-rw-r--r--netwerk/dns/nsIDNSListener.idl34
-rw-r--r--netwerk/dns/nsIDNSRecord.idl136
-rw-r--r--netwerk/dns/nsIDNSResolverInfo.idl11
-rw-r--r--netwerk/dns/nsIDNSService.idl341
-rw-r--r--netwerk/dns/nsIDNService.cpp925
-rw-r--r--netwerk/dns/nsIDNService.h205
-rw-r--r--netwerk/dns/nsIEffectiveTLDService.idl158
-rw-r--r--netwerk/dns/nsIIDNService.idl58
-rw-r--r--netwerk/dns/nsINativeDNSResolverOverride.idl29
-rw-r--r--netwerk/dns/nsPIDNSService.idl34
-rw-r--r--netwerk/dns/prepare_tlds.py152
-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.js176
-rw-r--r--netwerk/dns/tests/unit/test_nsEffectiveTLDService_Reload_DAFSA.js34
-rw-r--r--netwerk/dns/tests/unit/test_nsEffectiveTLDService_getKnownPublicSuffix.js46
-rw-r--r--netwerk/dns/tests/unit/xpcshell.ini12
96 files changed, 34835 insertions, 0 deletions
diff --git a/netwerk/dns/ChildDNSService.cpp b/netwerk/dns/ChildDNSService.cpp
new file mode 100644
index 0000000000..ebfb4aee26
--- /dev/null
+++ b/netwerk/dns/ChildDNSService.cpp
@@ -0,0 +1,450 @@
+/* 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 "DNSResolverInfo.h"
+
+namespace mozilla {
+namespace net {
+
+//-----------------------------------------------------------------------------
+// ChildDNSService
+//-----------------------------------------------------------------------------
+
+static StaticRefPtr<ChildDNSService> gChildDNSService;
+static const char kPrefNameDisablePrefetch[] = "network.dns.disablePrefetch";
+
+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_IsMainThread()) {
+ return nullptr;
+ }
+ gChildDNSService = new ChildDNSService();
+ ClearOnShutdown(&gChildDNSService);
+ }
+
+ return do_AddRef(gChildDNSService);
+}
+
+NS_IMPL_ISUPPORTS(ChildDNSService, nsIDNSService, nsPIDNSService, nsIObserver)
+
+ChildDNSService::ChildDNSService()
+ : mFirstTime(true),
+ mDisablePrefetch(false),
+ mPendingRequestsLock("DNSPendingRequestsLock") {
+ 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, uint16_t aType,
+ const OriginAttributes& aOriginAttributes, uint32_t aFlags,
+ uintptr_t aListenerAddr, nsACString& aHashKey) {
+ aHashKey.Assign(aHost);
+ aHashKey.Assign(aTrrServer);
+ 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, uint32_t flags,
+ nsIDNSResolverInfo* aResolver, nsIDNSListener* listener,
+ nsIEventTarget* target_, const OriginAttributes& aOriginAttributes,
+ nsICancelable** result) {
+ if (XRE_IsContentProcess()) {
+ NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE);
+ }
+
+ bool resolveDNSInSocketProcess = false;
+ if (XRE_IsParentProcess() && nsIOService::UseSocketProcess()) {
+ resolveDNSInSocketProcess = true;
+ if (type != nsIDNSService::RESOLVE_TYPE_DEFAULT &&
+ (mTRRServiceParent->Mode() != MODE_TRRFIRST &&
+ mTRRServiceParent->Mode() != MODE_TRRONLY)) {
+ 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, DNSResolverInfo::URL(aResolver), type,
+ aOriginAttributes, flags, listener, target);
+ RefPtr<DNSRequestActor> dnsReq;
+ if (resolveDNSInSocketProcess) {
+ dnsReq = new DNSRequestParent(sender);
+ } else {
+ dnsReq = new DNSRequestChild(sender);
+ }
+
+ {
+ MutexAutoLock lock(mPendingRequestsLock);
+ nsCString key;
+ GetDNSRecordHashKey(hostname, DNSResolverInfo::URL(aResolver), type,
+ aOriginAttributes, flags, originalListenerAddr, key);
+ auto entry = mPendingRequests.LookupForAdd(key);
+ if (entry) {
+ entry.Data()->AppendElement(sender);
+ } else {
+ entry.OrInsert([&]() {
+ auto* hashEntry = new nsTArray<RefPtr<DNSRequestSender>>();
+ hashEntry->AppendElement(sender);
+ return hashEntry;
+ });
+ }
+ }
+
+ sender->StartRequest();
+
+ sender.forget(result);
+ return NS_OK;
+}
+
+nsresult ChildDNSService::CancelAsyncResolveInternal(
+ const nsACString& aHostname, uint16_t aType, uint32_t aFlags,
+ nsIDNSResolverInfo* aResolver, 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, DNSResolverInfo::URL(aResolver), 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, uint32_t flags,
+ nsIDNSResolverInfo* aResolver,
+ nsIDNSListener* listener, nsIEventTarget* target_,
+ JS::HandleValue 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, aResolver, listener,
+ target_, attrs, result);
+}
+
+NS_IMETHODIMP
+ChildDNSService::AsyncResolveNative(
+ const nsACString& hostname, nsIDNSService::ResolveType aType,
+ uint32_t flags, nsIDNSResolverInfo* aResolver, nsIDNSListener* listener,
+ nsIEventTarget* target_, const OriginAttributes& aOriginAttributes,
+ nsICancelable** result) {
+ return AsyncResolveInternal(hostname, aType, flags, aResolver, listener,
+ target_, aOriginAttributes, result);
+}
+
+NS_IMETHODIMP
+ChildDNSService::NewTRRResolverInfo(const nsACString& aTrrURL,
+ nsIDNSResolverInfo** aResolver) {
+ RefPtr<DNSResolverInfo> res = new DNSResolverInfo(aTrrURL);
+ res.forget(aResolver);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSService::CancelAsyncResolve(const nsACString& aHostname,
+ nsIDNSService::ResolveType aType,
+ uint32_t aFlags,
+ nsIDNSResolverInfo* aResolver,
+ nsIDNSListener* aListener, nsresult aReason,
+ JS::HandleValue 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, aResolver,
+ aListener, aReason, attrs);
+}
+
+NS_IMETHODIMP
+ChildDNSService::CancelAsyncResolveNative(
+ const nsACString& aHostname, nsIDNSService::ResolveType aType,
+ uint32_t aFlags, nsIDNSResolverInfo* aResolver, nsIDNSListener* aListener,
+ nsresult aReason, const OriginAttributes& aOriginAttributes) {
+ return CancelAsyncResolveInternal(aHostname, aType, aFlags, aResolver,
+ aListener, aReason, aOriginAttributes);
+}
+
+NS_IMETHODIMP
+ChildDNSService::Resolve(const nsACString& hostname, uint32_t flags,
+ JS::HandleValue 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, uint32_t 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::GetCurrentTrrURI(nsACString& aURI) {
+ if (!mTRRServiceParent) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ mTRRServiceParent->GetTrrURI(aURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSService::GetCurrentTrrMode(uint32_t* aMode) {
+ if (!mTRRServiceParent) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ *aMode = mTRRServiceParent->Mode();
+ 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.
+ uint32_t 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->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() {
+ // Disable prefetching either by explicit preference or if a manual proxy
+ // is configured
+ bool disablePrefetch = false;
+
+ nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+ if (prefs) {
+ prefs->GetBoolPref(kPrefNameDisablePrefetch, &disablePrefetch);
+ }
+
+ if (mFirstTime) {
+ mFirstTime = false;
+ if (prefs) {
+ prefs->AddObserver(kPrefNameDisablePrefetch, this, false);
+
+ // Monitor these to see if there is a change in proxy configuration
+ // If a manual proxy is in use, disable prefetch implicitly
+ prefs->AddObserver("network.proxy.type", this, false);
+ }
+ }
+
+ mDisablePrefetch =
+ disablePrefetch || (StaticPrefs::network_proxy_type() ==
+ nsIProtocolProxyService::PROXYCONFIG_MANUAL);
+
+ 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) {
+ // we are only getting called if a preference has changed.
+ NS_ASSERTION(strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0,
+ "unexpected observe call");
+
+ // Reread prefs
+ Init();
+ 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..4788b6c11d
--- /dev/null
+++ b/netwerk/dns/ChildDNSService.h
@@ -0,0 +1,69 @@
+/* -*- 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 "nsPIDNSService.h"
+#include "nsIObserver.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 nsPIDNSService, public nsIObserver {
+ public:
+ // AsyncResolve (and CancelAsyncResolve) can be called off-main
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSPIDNSSERVICE
+ NS_DECL_NSIDNSSERVICE
+ NS_DECL_NSIOBSERVER
+
+ ChildDNSService();
+
+ static already_AddRefed<ChildDNSService> GetSingleton();
+
+ void NotifyRequestDone(DNSRequestSender* aDnsRequest);
+
+ private:
+ virtual ~ChildDNSService() = default;
+
+ void MOZ_ALWAYS_INLINE GetDNSRecordHashKey(
+ const nsACString& aHost, const nsACString& aTrrServer, uint16_t aType,
+ const OriginAttributes& aOriginAttributes, uint32_t aFlags,
+ uintptr_t aListenerAddr, nsACString& aHashKey);
+ nsresult AsyncResolveInternal(const nsACString& hostname, uint16_t type,
+ uint32_t flags, nsIDNSResolverInfo* aResolver,
+ nsIDNSListener* listener,
+ nsIEventTarget* target_,
+ const OriginAttributes& aOriginAttributes,
+ nsICancelable** result);
+ nsresult CancelAsyncResolveInternal(
+ const nsACString& aHostname, uint16_t aType, uint32_t aFlags,
+ nsIDNSResolverInfo* aResolver, nsIDNSListener* aListener,
+ nsresult aReason, const OriginAttributes& aOriginAttributes);
+
+ bool mFirstTime;
+ bool mDisablePrefetch;
+
+ // We need to remember pending dns requests to be able to cancel them.
+ nsClassHashtable<nsCStringHashKey, nsTArray<RefPtr<DNSRequestSender>>>
+ mPendingRequests;
+ Mutex mPendingRequestsLock;
+ RefPtr<TRRServiceParent> mTRRServiceParent;
+};
+
+} // 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..c9d3e46ca3
--- /dev/null
+++ b/netwerk/dns/DNS.cpp
@@ -0,0 +1,382 @@
+/* -*- 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
+}
+
+// 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;
+}
+
+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()) {
+ return false;
+ }
+
+ nsAutoCString host;
+ nsContentUtils::ASCIIToLower(aAsciiHost, host);
+
+ return host.EqualsLiteral("localhost") ||
+ StringEndsWith(host, ".localhost"_ns);
+}
+
+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); }
+
+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);
+ if (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).
+ return true;
+ }
+ return false;
+}
+
+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 PL_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,
+ unsigned int aTRR, nsTArray<NetAddr>&& addresses)
+ : mHostName(host),
+ mCanonicalName(cname),
+ mFromTRR(aTRR),
+ mAddresses(std::move(addresses)) {}
+
+AddrInfo::AddrInfo(const nsACString& host, unsigned int aTRR,
+ nsTArray<NetAddr>&& addresses, uint32_t aTTL)
+ : ttl(aTTL),
+ mHostName(host),
+ mCanonicalName(),
+ mFromTRR(aTRR),
+ mAddresses(std::move(addresses)) {}
+
+// deep copy constructor
+AddrInfo::AddrInfo(const AddrInfo* src) {
+ mHostName = src->mHostName;
+ mCanonicalName = src->mCanonicalName;
+ ttl = src->ttl;
+ mFromTRR = src->mFromTRR;
+ 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..0763980d64
--- /dev/null
+++ b/netwerk/dns/DNS.h
@@ -0,0 +1,247 @@
+/* -*- 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 "plstr.h"
+#include "nsISupportsImpl.h"
+#include "mozilla/LinkedList.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);
+
+ 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;
+};
+
+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,
+ unsigned int aTRR, nsTArray<NetAddr>&& addresses);
+
+ // Creates a basic AddrInfo object (initialize only the host and TRR status).
+ explicit AddrInfo(const nsACString& host, unsigned int aTRR,
+ nsTArray<NetAddr>&& addresses, uint32_t aTTL = NO_TTL_DATA);
+
+ explicit AddrInfo(const AddrInfo* src); // copy
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
+ unsigned int IsTRR() { return mFromTRR; }
+
+ 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;
+
+ unsigned int mFromTRR = 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);
+
+} // namespace net
+} // namespace mozilla
+
+#endif // DNS_h_
diff --git a/netwerk/dns/DNSByTypeRecord.h b/netwerk/dns/DNSByTypeRecord.h
new file mode 100644
index 0000000000..2008b6d213
--- /dev/null
+++ b/netwerk/dns/DNSByTypeRecord.h
@@ -0,0 +1,238 @@
+/* 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;
+};
+
+} // namespace net
+} // namespace mozilla
+
+namespace mozilla {
+namespace ipc {
+
+template <>
+struct IPDLParamTraits<mozilla::net::IPCTypeRecord> {
+ typedef mozilla::net::IPCTypeRecord paramType;
+ static void Write(IPC::Message* aMsg, IProtocol* aActor,
+ const paramType& aParam) {
+ WriteIPDLParam(aMsg, aActor, aParam.mData);
+ }
+
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, paramType* aResult) {
+ if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mData)) {
+ return false;
+ }
+ return true;
+ }
+};
+
+template <>
+struct IPDLParamTraits<mozilla::Nothing> {
+ typedef mozilla::Nothing paramType;
+ static void Write(IPC::Message* aMsg, IProtocol* aActor,
+ const paramType& aParam) {
+ bool isSome = false;
+ WriteIPDLParam(aMsg, aActor, isSome);
+ }
+
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, paramType* aResult) {
+ bool isSome;
+ if (!ReadIPDLParam(aMsg, aIter, aActor, &isSome)) {
+ return false;
+ }
+ *aResult = Nothing();
+ return true;
+ }
+};
+
+template <>
+struct IPDLParamTraits<mozilla::net::SVCB> {
+ typedef mozilla::net::SVCB paramType;
+ static void Write(IPC::Message* aMsg, IProtocol* aActor,
+ const paramType& aParam) {
+ WriteIPDLParam(aMsg, aActor, aParam.mSvcFieldPriority);
+ WriteIPDLParam(aMsg, aActor, aParam.mSvcDomainName);
+ WriteIPDLParam(aMsg, aActor, aParam.mSvcFieldValue);
+ }
+
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, paramType* aResult) {
+ if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mSvcFieldPriority)) {
+ return false;
+ }
+ if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mSvcDomainName)) {
+ return false;
+ }
+ if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mSvcFieldValue)) {
+ return false;
+ }
+ return true;
+ }
+};
+
+template <>
+struct IPDLParamTraits<mozilla::net::SvcParamAlpn> {
+ typedef mozilla::net::SvcParamAlpn paramType;
+ static void Write(IPC::Message* aMsg, IProtocol* aActor,
+ const paramType& aParam) {
+ WriteIPDLParam(aMsg, aActor, aParam.mValue);
+ }
+
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, paramType* aResult) {
+ if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mValue)) {
+ return false;
+ }
+ return true;
+ }
+};
+
+template <>
+struct IPDLParamTraits<mozilla::net::SvcParamNoDefaultAlpn> {
+ typedef mozilla::net::SvcParamNoDefaultAlpn paramType;
+ static void Write(IPC::Message* aMsg, IProtocol* aActor,
+ const paramType& aParam) {}
+
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, paramType* aResult) {
+ return true;
+ }
+};
+
+template <>
+struct IPDLParamTraits<mozilla::net::SvcParamPort> {
+ typedef mozilla::net::SvcParamPort paramType;
+ static void Write(IPC::Message* aMsg, IProtocol* aActor,
+ const paramType& aParam) {
+ WriteIPDLParam(aMsg, aActor, aParam.mValue);
+ }
+
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, paramType* aResult) {
+ if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mValue)) {
+ return false;
+ }
+ return true;
+ }
+};
+
+template <>
+struct IPDLParamTraits<mozilla::net::SvcParamIpv4Hint> {
+ typedef mozilla::net::SvcParamIpv4Hint paramType;
+ static void Write(IPC::Message* aMsg, IProtocol* aActor,
+ const paramType& aParam) {
+ WriteIPDLParam(aMsg, aActor, aParam.mValue);
+ }
+
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, paramType* aResult) {
+ if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mValue)) {
+ return false;
+ }
+ return true;
+ }
+};
+
+template <>
+struct IPDLParamTraits<mozilla::net::SvcParamEchConfig> {
+ typedef mozilla::net::SvcParamEchConfig paramType;
+ static void Write(IPC::Message* aMsg, IProtocol* aActor,
+ const paramType& aParam) {
+ WriteIPDLParam(aMsg, aActor, aParam.mValue);
+ }
+
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, paramType* aResult) {
+ if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mValue)) {
+ return false;
+ }
+ return true;
+ }
+};
+
+template <>
+struct IPDLParamTraits<mozilla::net::SvcParamIpv6Hint> {
+ typedef mozilla::net::SvcParamIpv6Hint paramType;
+ static void Write(IPC::Message* aMsg, IProtocol* aActor,
+ const paramType& aParam) {
+ WriteIPDLParam(aMsg, aActor, aParam.mValue);
+ }
+
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, paramType* aResult) {
+ if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mValue)) {
+ return false;
+ }
+ return true;
+ }
+};
+
+template <>
+struct IPDLParamTraits<mozilla::net::SvcParamODoHConfig> {
+ typedef mozilla::net::SvcParamODoHConfig paramType;
+ static void Write(IPC::Message* aMsg, IProtocol* aActor,
+ const paramType& aParam) {
+ WriteIPDLParam(aMsg, aActor, aParam.mValue);
+ }
+
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, paramType* aResult) {
+ if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mValue)) {
+ return false;
+ }
+ return true;
+ }
+};
+
+template <>
+struct IPDLParamTraits<mozilla::net::SvcFieldValue> {
+ typedef mozilla::net::SvcFieldValue paramType;
+ static void Write(IPC::Message* aMsg, IProtocol* aActor,
+ const paramType& aParam) {
+ WriteIPDLParam(aMsg, aActor, aParam.mValue);
+ }
+
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, paramType* aResult) {
+ if (!ReadIPDLParam(aMsg, aIter, 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/DNSPacket.cpp b/netwerk/dns/DNSPacket.cpp
new file mode 100644
index 0000000000..460fe92636
--- /dev/null
+++ b/netwerk/dns/DNSPacket.cpp
@@ -0,0 +1,975 @@
+/* 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"
+
+namespace mozilla {
+namespace net {
+
+extern mozilla::LazyLogModule gHostResolverLog;
+#undef LOG
+#undef LOG_ENABLED
+#define LOG(args) MOZ_LOG(gHostResolverLog, mozilla::LogLevel::Debug, args)
+#define LOG_ENABLED() \
+ MOZ_LOG_TEST(mozilla::net::gHostResolverLog, mozilla::LogLevel::Debug)
+
+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/draft-ietf-dnsop-extended-error-16#section-4
+// This is a list of errors for which we should not fallback to Do53.
+// These are normally DNSSEC failures or explicit filtering performed by the
+// recursive resolver.
+bool hardFail(uint16_t code) {
+ const uint16_t noFallbackErrors[] = {
+ 4, // Forged answer (malware filtering)
+ 6, // DNSSEC Boggus
+ 7, // Signature expired
+ 8, // Signature not yet valid
+ 9, // DNSKEY Missing
+ 10, // RRSIG missing
+ 11, // No ZONE Key Bit set
+ 12, // NSEC Missing
+ 17, // Filtered
+ };
+
+ for (const auto& err : noFallbackErrors) {
+ if (code == err) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// static
+nsresult DNSPacket::ParseSvcParam(unsigned int svcbIndex, uint16_t key,
+ SvcFieldValue& field, uint16_t length) {
+ 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(mResponse, 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 = mResponse[svcbIndex++];
+ length -= 1;
+ if (alpnIdLength > length) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ alpnArray.AppendElement(
+ nsCString((const char*)&mResponse[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(mResponse, 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(mResponse, svcbIndex));
+ ipv4array.AppendElement(addr);
+ length -= 4;
+ svcbIndex += 4;
+ }
+ break;
+ }
+ case SvcParamKeyEchConfig: {
+ field.mValue = AsVariant(SvcParamEchConfig{
+ .mValue = nsCString((const char*)(&mResponse[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] = mResponse[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*)(&mResponse[svcbIndex]), length)});
+ break;
+ }
+ default: {
+ // Unespected type. We'll just ignore it.
+ return NS_OK;
+ break;
+ }
+ }
+ return NS_OK;
+}
+
+nsresult DNSPacket::PassQName(unsigned int& index) {
+ 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>(mResponse[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'.
+nsresult DNSPacket::GetQname(nsACString& aQname, unsigned int& aIndex) {
+ 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 >= mBodySize) {
+ LOG(("TRR: bad Qname packet\n"));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ clength = static_cast<uint8_t>(mResponse[cindex]);
+ if ((clength & 0xc0) == 0xc0) {
+ // name pointer, get the new offset (14 bits)
+ if ((cindex + 1) >= mBodySize) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ // extract the new index position for the next label
+ uint16_t newpos = (clength & 0x3f) << 8 | mResponse[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) > mBodySize) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ aQname.Append((const char*)(&mResponse[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;
+
+// static
+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
+
+ aBody += '\0'; // ARCOUNT
+ aBody += aDisableECS ? 1 : '\0'; // 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 (aDisableECS) {
+ // 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';
+
+ aBody += '\0'; // upper 8 bit RDLEN
+ aBody += 8; // RDLEN | u_int16_t | length of all RDATA |
+
+ // RDATA | octet stream | {attribute,value} pairs |
+ // The RDATA is just the ECS option setting zero subnet prefix
+
+ 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
+ }
+ return NS_OK;
+}
+
+//
+// DohDecode() collects the TTL and the IP addresses in the response
+//
+// static
+nsresult DNSPacket::Decode(
+ nsCString& aHost, enum TrrType aType, nsCString& aCname, bool aAllowRFC1918,
+ nsHostRecord::TRRSkippedReason& aReason, DOHresp& aResp,
+ TypeRecordResultType& aTypeResult,
+ nsClassHashtable<nsCStringHashKey, DOHresp>& aAdditionalRecords,
+ uint32_t& aTTL) {
+ // 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(), mBodySize));
+
+ aCname.Truncate();
+
+ if (mBodySize < 12 || mResponse[0] || mResponse[1]) {
+ LOG(("TRR bad incoming DOH, eject!\n"));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ uint8_t rcode = mResponse[3] & 0x0F;
+ LOG(("TRR Decode %s RCODE %d\n", aHost.get(), rcode));
+ if (rcode) {
+ if (aReason == nsHostRecord::TRR_UNSET) {
+ aReason = nsHostRecord::TRR_RCODE_FAIL;
+ }
+ }
+
+ uint16_t questionRecords = get16bit(mResponse, 4); // qdcount
+ // iterate over the single(?) host name in question
+ while (questionRecords) {
+ do {
+ if (mBodySize < (index + 1)) {
+ LOG(("TRR Decode 1 index: %u size: %u", index, mBodySize));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ length = static_cast<uint8_t>(mResponse[index]);
+ if (length) {
+ if (host.Length()) {
+ host.Append(".");
+ }
+ if (mBodySize < (index + 1 + length)) {
+ LOG(("TRR Decode 2 index: %u size: %u len: %u", index, mBodySize,
+ length));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ host.Append(((char*)mResponse) + index + 1, length);
+ }
+ index += 1 + length; // skip length byte + label
+ } while (length);
+ if (mBodySize < (index + 4)) {
+ LOG(("TRR Decode 3 index: %u size: %u", index, mBodySize));
+ 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(mResponse, 6);
+
+ LOG(("TRR Decode: %d answer records (%u bytes body) %s index=%u\n",
+ answerRecords, mBodySize, host.get(), index));
+
+ while (answerRecords) {
+ nsAutoCString qname;
+ rv = GetQname(qname, index);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ // 16 bit TYPE
+ if (mBodySize < (index + 2)) {
+ LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index + 2));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ uint16_t TYPE = get16bit(mResponse, index);
+
+ 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));
+ return NS_ERROR_UNEXPECTED;
+ }
+ index += 2;
+
+ // 16 bit class
+ if (mBodySize < (index + 2)) {
+ LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index + 2));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ uint16_t CLASS = get16bit(mResponse, 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 (mBodySize < (index + 4)) {
+ LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ uint32_t TTL = get32bit(mResponse, index);
+ index += 4;
+
+ // 16 bit RDLENGTH
+ if (mBodySize < (index + 2)) {
+ LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ uint16_t RDLENGTH = get16bit(mResponse, index);
+ index += 2;
+
+ if (mBodySize < (index + RDLENGTH)) {
+ LOG(("TRR: Dohdecode:%d fail RDLENGTH=%d at index %d\n", __LINE__,
+ RDLENGTH, index));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ // 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() == '.')) &&
+ qname.Compare(aHost.BeginReading(), true, qname.Length()) == 0;
+
+ 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, mResponse, 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, mResponse, 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);
+ 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 = mResponse[txtIndex++];
+ available--;
+ if (characterStringLen > available) {
+ LOG(("DNSPacket::DohDecode MALFORMED TXT RECORD\n"));
+ break;
+ }
+ txt.Append((const char*)(&mResponse[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;
+ int32_t lastSvcParamKey = -1;
+
+ unsigned int svcbIndex = index;
+ CheckedInt<uint16_t> available = RDLENGTH;
+
+ // Should have at least 2 bytes for the priority and one for the
+ // qname length.
+ if (available.value() < 3) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ parsed.mSvcFieldPriority = get16bit(mResponse, svcbIndex);
+ svcbIndex += 2;
+
+ rv = GetQname(parsed.mSvcDomainName, svcbIndex);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (parsed.mSvcDomainName.IsEmpty()) {
+ if (parsed.mSvcFieldPriority == 0) {
+ // For AliasMode SVCB RRs, a TargetName of "." indicates that the
+ // service is not available or does not exist.
+ continue;
+ }
+
+ // For ServiceMode SVCB RRs, if TargetName has the value ".",
+ // then the owner name of this record MUST be used as
+ // the effective TargetName.
+ parsed.mSvcDomainName = qname;
+ }
+
+ available -= (svcbIndex - index);
+ 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(mResponse, 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(mResponse, svcbIndex);
+ svcbIndex += 2;
+
+ available -= 4 + len;
+ if (!available.isValid()) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ rv = ParseSvcParam(svcbIndex, key, value, len);
+ 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>()) {
+ parsed.mHasIPHints = true;
+ }
+ if (value.mValue.is<SvcParamEchConfig>()) {
+ parsed.mHasEchConfig = true;
+ parsed.mEchConfig = value.mValue.as<SvcParamEchConfig>().mValue;
+ }
+ if (value.mValue.is<SvcParamODoHConfig>()) {
+ parsed.mODoHConfig = value.mValue.as<SvcParamODoHConfig>().mValue;
+ }
+ parsed.mSvcFieldValue.AppendElement(value);
+ }
+
+ // 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;
+ ToLowerCase(aCname);
+ LOG(("DNSPacket::DohDecode HTTPSSVC AliasForm host %s => %s\n",
+ host.get(), aCname.get()));
+ break;
+ }
+
+ if (aType != TRRTYPE_HTTPSSVC) {
+ // Ignore the entry that we just parsed if we didn't ask for it.
+ break;
+ }
+
+ 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, mBodySize));
+ answerRecords--;
+ }
+
+ // NSCOUNT
+ uint16_t nsRecords = get16bit(mResponse, 8);
+ LOG(("TRR Decode: %d ns records (%u bytes body)\n", nsRecords, mBodySize));
+ while (nsRecords) {
+ rv = PassQName(index);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (mBodySize < (index + 8)) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ index += 2; // type
+ index += 2; // class
+ index += 4; // ttl
+
+ // 16 bit RDLENGTH
+ if (mBodySize < (index + 2)) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ uint16_t RDLENGTH = get16bit(mResponse, index);
+ index += 2;
+ if (mBodySize < (index + RDLENGTH)) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ index += RDLENGTH;
+ LOG(("done with nsRecord now %u of %u\n", index, mBodySize));
+ nsRecords--;
+ }
+
+ // additional resource records
+ uint16_t arRecords = get16bit(mResponse, 10);
+ LOG(("TRR Decode: %d additional resource records (%u bytes body)\n",
+ arRecords, mBodySize));
+
+ while (arRecords) {
+ nsAutoCString qname;
+ rv = GetQname(qname, index);
+ if (NS_FAILED(rv)) {
+ LOG(("Bad qname for additional record"));
+ return rv;
+ }
+
+ if (mBodySize < (index + 8)) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ uint16_t type = get16bit(mResponse, index);
+ index += 2;
+ // The next two bytes encode class
+ // (or udpPayloadSize when type is TRRTYPE_OPT)
+ uint16_t cls = get16bit(mResponse, index);
+ index += 2;
+ // The next 4 bytes encode TTL
+ // (or extRCode + ednsVersion + flags when type is TRRTYPE_OPT)
+ uint32_t ttl = get32bit(mResponse, index);
+ index += 4;
+ // cls and ttl are unused when type is TRRTYPE_OPT
+
+ // 16 bit RDLENGTH
+ if (mBodySize < (index + 2)) {
+ LOG(("Record too small"));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ uint16_t rdlength = get16bit(mResponse, index);
+ index += 2;
+ if (mBodySize < (index + rdlength)) {
+ LOG(("rdlength too big"));
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ auto parseRecord = [&]() {
+ LOG(("Parsing additional record type: %u", type));
+ auto& entry = aAdditionalRecords.GetOrInsert(qname);
+ if (!entry) {
+ entry.reset(new DOHresp());
+ }
+
+ 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, mResponse, 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, mResponse, 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(mResponse, index + offset);
+ LOG(("optCode: %u", optCode));
+ offset += 2;
+ if (offset + 2 > rdlength) {
+ break;
+ }
+ uint16_t optLen = get16bit(mResponse, 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(mResponse, index + offset);
+
+ LOG((
+ "Extended error code: %u message: %s", extendedError,
+ nsAutoCString((char*)mResponse + 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, mBodySize));
+ arRecords--;
+ }
+
+ if (index != mBodySize) {
+ LOG(("DohDecode failed to parse entire response body, %u out of %u bytes\n",
+ index, mBodySize));
+ // failed to parse 100%, do not continue
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ 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 && hardFail(extendedError)) {
+ return NS_ERROR_DEFINITIVE_UNKNOWN_HOST;
+ }
+ return NS_ERROR_FAILURE;
+ }
+
+ // 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;
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/DNSPacket.h b/netwerk/dns/DNSPacket.h
new file mode 100644
index 0000000000..93caed20d0
--- /dev/null
+++ b/netwerk/dns/DNSPacket.h
@@ -0,0 +1,71 @@
+/* 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/Result.h"
+#include "nsHostResolver.h"
+
+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 = UINT32_MAX;
+};
+
+// 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:
+ // 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
+ static 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.
+ nsresult Decode(
+ nsCString& aHost, enum TrrType aType, nsCString& aCname,
+ bool aAllowRFC1918, nsHostRecord::TRRSkippedReason& reason,
+ DOHresp& aResp, TypeRecordResultType& aTypeResult,
+ nsClassHashtable<nsCStringHashKey, DOHresp>& aAdditionalRecords,
+ uint32_t& aTTL);
+
+ private:
+ // 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;
+
+ nsresult PassQName(unsigned int& index);
+ nsresult GetQname(nsACString& aQname, unsigned int& aIndex);
+ nsresult ParseSvcParam(unsigned int svcbIndex, uint16_t key,
+ SvcFieldValue& field, uint16_t length);
+
+ // The response buffer.
+ unsigned char mResponse[MAX_SIZE]{};
+ unsigned int mBodySize = 0;
+};
+
+} // 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..a46eb1e4e2
--- /dev/null
+++ b/netwerk/dns/DNSRequestBase.h
@@ -0,0 +1,148 @@
+/* -*- 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 uint16_t& type,
+ const OriginAttributes& originAttributes,
+ const uint32_t& 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,
+ const uint16_t& aType,
+ const OriginAttributes& aOriginAttributes,
+ const uint32_t& aFlags, nsIDNSListener* aListener,
+ nsIEventTarget* target);
+
+ void OnRecvCancelDNSRequest(const nsCString& hostName,
+ const nsCString& trrServer, const uint16_t& type,
+ const OriginAttributes& originAttributes,
+ const uint32_t& 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;
+ nsCString mHost;
+ nsCString mTrrServer;
+ uint16_t mType;
+ const OriginAttributes mOriginAttributes;
+ uint16_t mFlags;
+};
+
+// 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();
+
+ void DoAsyncResolve(const nsACString& hostname, const nsACString& trrServer,
+ uint16_t type, const OriginAttributes& originAttributes,
+ uint32_t flags);
+
+ void OnRecvCancelDNSRequest(const nsCString& hostName,
+ const nsCString& trrServer, const uint16_t& type,
+ const OriginAttributes& originAttributes,
+ const uint32_t& 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;
+
+ uint32_t mFlags;
+};
+
+// 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..b25a9ee03e
--- /dev/null
+++ b/netwerk/dns/DNSRequestChild.cpp
@@ -0,0 +1,536 @@
+/* -*- 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, uint16_t flags);
+
+ private:
+ virtual ~ChildDNSRecord() = default;
+
+ nsCString mCanonicalName;
+ nsTArray<NetAddr> mAddresses;
+ uint32_t mCurrent; // addr iterator
+ uint16_t mFlags;
+ double mTrrFetchDuration;
+ double mTrrFetchDurationNetworkOnly;
+ bool mIsTRR;
+ uint32_t mEffectiveTRRMode;
+};
+
+NS_IMPL_ISUPPORTS(ChildDNSRecord, nsIDNSRecord, nsIDNSAddrRecord)
+
+ChildDNSRecord::ChildDNSRecord(const DNSRecord& reply, uint16_t flags)
+ : mCurrent(0), mFlags(flags) {
+ mCanonicalName = reply.canonicalName();
+ mTrrFetchDuration = reply.trrFetchDuration();
+ mTrrFetchDurationNetworkOnly = reply.trrFetchDurationNetworkOnly();
+ mIsTRR = reply.isTRR();
+ 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();
+}
+
+//-----------------------------------------------------------------------------
+// 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::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(uint32_t* aMode) {
+ *aMode = mEffectiveTRRMode;
+ 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);
+
+ private:
+ virtual ~ChildDNSByTypeRecord() = default;
+
+ TypeRecordResultType mResults = AsVariant(mozilla::Nothing());
+ bool mAllRecordsExcluded = false;
+};
+
+NS_IMPL_ISUPPORTS(ChildDNSByTypeRecord, nsIDNSByTypeRecord, nsIDNSRecord,
+ nsIDNSTXTRecord, nsIDNSHTTPSSVCRecord)
+
+ChildDNSByTypeRecord::ChildDNSByTypeRecord(const TypeRecordResultType& reply,
+ const nsACString& aHost)
+ : DNSHTTPSSVCRecordBase(aHost), mAllRecordsExcluded(false) {
+ mResults = reply;
+}
+
+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;
+}
+
+//-----------------------------------------------------------------------------
+// DNSRequestSender
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS(DNSRequestSender, nsICancelable)
+
+DNSRequestSender::DNSRequestSender(
+ const nsACString& aHost, const nsACString& aTrrServer,
+ const uint16_t& aType, const OriginAttributes& aOriginAttributes,
+ const uint32_t& aFlags, nsIDNSListener* aListener, nsIEventTarget* target)
+ : mListener(aListener),
+ mTarget(target),
+ mResultStatus(NS_OK),
+ mHost(aHost),
+ mTrrServer(aTrrServer),
+ mType(aType),
+ mOriginAttributes(aOriginAttributes),
+ mFlags(aFlags) {}
+
+void DNSRequestSender::OnRecvCancelDNSRequest(
+ const nsCString& hostName, const nsCString& trrServer, const uint16_t& type,
+ const OriginAttributes& originAttributes, const uint32_t& flags,
+ const nsresult& reason) {}
+
+NS_IMETHODIMP
+DNSRequestSender::Cancel(nsresult reason) {
+ if (!mIPCActor) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ if (mIPCActor->CanSend()) {
+ // We can only do IPDL on the main thread
+ nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction(
+ "net::CancelDNSRequestEvent",
+ [actor(mIPCActor), host(mHost), trrServer(mTrrServer), type(mType),
+ originAttributes(mOriginAttributes), flags(mFlags), reason]() {
+ if (!actor->CanSend()) {
+ return;
+ }
+
+ if (DNSRequestChild* child = actor->AsDNSRequestChild()) {
+ Unused << child->SendCancelDNSRequest(
+ host, trrServer, type, originAttributes, flags, reason);
+ } else if (DNSRequestParent* parent = actor->AsDNSRequestParent()) {
+ Unused << parent->SendCancelDNSRequest(
+ host, trrServer, type, originAttributes, flags, reason);
+ }
+ });
+ SchedulerGroup::Dispatch(TaskCategory::Other, runnable.forget());
+ }
+ return NS_OK;
+}
+
+void DNSRequestSender::StartRequest() {
+ // we can only do IPDL on the main thread
+ if (!NS_IsMainThread()) {
+ SchedulerGroup::Dispatch(
+ TaskCategory::Other,
+ NewRunnableMethod("net::DNSRequestSender::StartRequest", this,
+ &DNSRequestSender::StartRequest));
+ return;
+ }
+
+ if (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, 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;
+ }
+
+ socketProcessChild->SendPDNSRequestConstructor(
+ child, mHost, mTrrServer, 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->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);
+ 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 uint16_t& type,
+ const OriginAttributes& originAttributes, const uint32_t& flags,
+ const nsresult& reason) {
+ mDNSRequest->OnRecvCancelDNSRequest(hostName, trrServer, 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..c360b8d58b
--- /dev/null
+++ b/netwerk/dns/DNSRequestChild.h
@@ -0,0 +1,41 @@
+/* -*- 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 uint16_t& type, const OriginAttributes& originAttributes,
+ const uint32_t& 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..0159745a6e
--- /dev/null
+++ b/netwerk/dns/DNSRequestParent.cpp
@@ -0,0 +1,178 @@
+/* -*- 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 "DNSResolverInfo.h"
+
+using namespace mozilla::ipc;
+
+namespace mozilla {
+namespace net {
+
+DNSRequestHandler::DNSRequestHandler() : mFlags(0) {}
+
+//-----------------------------------------------------------------------------
+// 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,
+ uint16_t type,
+ const OriginAttributes& originAttributes,
+ uint32_t flags) {
+ nsresult rv;
+ mFlags = flags;
+ nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIEventTarget> main = GetMainThreadEventTarget();
+ nsCOMPtr<nsICancelable> unused;
+ RefPtr<DNSResolverInfo> res;
+ if (!trrServer.IsEmpty()) {
+ res = new DNSResolverInfo(trrServer);
+ }
+ rv = dns->AsyncResolveNative(
+ hostname, static_cast<nsIDNSService::ResolveType>(type), flags, res,
+ 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 uint16_t& type, const OriginAttributes& originAttributes,
+ const uint32_t& flags, const nsresult& reason) {
+ nsresult rv;
+ nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv)) {
+ RefPtr<DNSResolverInfo> res;
+ if (!aTrrServer.IsEmpty()) {
+ res = new DNSResolverInfo(aTrrServer);
+ }
+ rv = dns->CancelAsyncResolveNative(
+ hostName, static_cast<nsIDNSService::ResolveType>(type), flags, res,
+ 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);
+ 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);
+
+ uint32_t effectiveTRRMode = 0;
+ rec->GetEffectiveTRRMode(&effectiveTRRMode);
+
+ SendLookupCompletedHelper(
+ mIPCActor, DNSRequestResponse(DNSRecord(cname, array, trrFetchDuration,
+ trrFetchDurationNetworkOnly,
+ isTRR, effectiveTRRMode)));
+ } 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 uint16_t& type,
+ const OriginAttributes& originAttributes, const uint32_t& flags,
+ const nsresult& reason) {
+ mDNSRequest->OnRecvCancelDNSRequest(hostName, trrServer, 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..59e6df8235
--- /dev/null
+++ b/netwerk/dns/DNSRequestParent.h
@@ -0,0 +1,44 @@
+/* -*- 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 uint16_t& type, const OriginAttributes& originAttributes,
+ const uint32_t& 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/DNSResolverInfo.cpp b/netwerk/dns/DNSResolverInfo.cpp
new file mode 100644
index 0000000000..41e3bc7bb7
--- /dev/null
+++ b/netwerk/dns/DNSResolverInfo.cpp
@@ -0,0 +1,19 @@
+/* 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 "DNSResolverInfo.h"
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS(DNSResolverInfo, nsIDNSResolverInfo)
+
+NS_IMETHODIMP
+DNSResolverInfo::GetURL(nsACString& aURL) {
+ aURL = mURL;
+ return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/DNSResolverInfo.h b/netwerk/dns/DNSResolverInfo.h
new file mode 100644
index 0000000000..dd4c9b1c85
--- /dev/null
+++ b/netwerk/dns/DNSResolverInfo.h
@@ -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/. */
+
+#ifndef mozilla_net_DNSResolverInfo_h__
+#define mozilla_net_DNSResolverInfo_h__
+
+#include "nsIDNSResolverInfo.h"
+
+namespace mozilla {
+namespace net {
+
+class DNSResolverInfo : public nsIDNSResolverInfo {
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIDNSRESOLVERINFO
+ public:
+ explicit DNSResolverInfo(const nsACString& aURL) : mURL(aURL){};
+ static nsCString URL(nsIDNSResolverInfo* aResolver) {
+ nsCString url;
+ if (aResolver) {
+ MOZ_ALWAYS_SUCCEEDS(aResolver->GetURL(url));
+ }
+ return url;
+ }
+
+ private:
+ virtual ~DNSResolverInfo() = default;
+ nsCString mURL;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_DNSResolverInfo_h__
diff --git a/netwerk/dns/GetAddrInfo.cpp b/netwerk/dns/GetAddrInfo.cpp
new file mode 100644
index 0000000000..b586499b19
--- /dev/null
+++ b/netwerk/dns/GetAddrInfo.cpp
@@ -0,0 +1,465 @@
+/* -*- 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"
+
+namespace mozilla {
+namespace net {
+
+static StaticRefPtr<NativeDNSResolverOverride> gOverrideService;
+
+static 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=%X. reqFamily = %X\n",
+ aHost.BeginReading(), status, reqFamily);
+ return NS_ERROR_FAILURE;
+ } else if (status != NOERROR) {
+ LOG_WARNING("DnsQuery_A failed with status %X.\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 %u results", aCanonHost.BeginReading(),
+ addresses.Length());
+ if (addresses.IsEmpty()) {
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+ RefPtr<AddrInfo> ai(
+ new AddrInfo(aCanonHost, canonName, 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
+
+ PRAddrInfo* prai =
+ PR_GetAddrInfoByName(aCanonHost.BeginReading(), aAddressFamily, prFlags);
+
+ if (!prai) {
+ 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()) {
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ ai.forget(aAddrInfo);
+
+ 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);
+ nsTArray<PRNetAddr>* overrides = overrideService->mOverrides.GetValue(aHost);
+ if (!overrides) {
+ return false;
+ }
+ nsCString* cname = nullptr;
+ if (aFlags & nsHostResolver::RES_CANON_NAME) {
+ cname = overrideService->mCnames.GetValue(aHost);
+ }
+
+ RefPtr<AddrInfo> ai;
+
+ nsTArray<NetAddr> addresses;
+ for (const auto& ip : *overrides) {
+ if (aAddressFamily != AF_UNSPEC && ip.raw.family != aAddressFamily) {
+ continue;
+ }
+ NetAddr addr(&ip);
+ addresses.AppendElement(addr);
+ }
+
+ if (!cname) {
+ ai = new AddrInfo(aHost, 0, std::move(addresses));
+ } else {
+ ai = new AddrInfo(aHost, *cname, 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)) {
+ return NS_OK;
+ }
+
+ nsAutoCString 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;
+}
+
+// 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) {
+ PRNetAddr tempAddr;
+ // Unfortunately, PR_StringToNetAddr does not properly initialize
+ // the output buffer in the case of IPv6 input. See bug 223145.
+ memset(&tempAddr, 0, sizeof(PRNetAddr));
+
+ if (PR_StringToNetAddr(nsCString(aIPLiteral).get(), &tempAddr) !=
+ PR_SUCCESS) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ AutoWriteLock lock(mLock);
+ auto& overrides = mOverrides.GetOrInsert(aHost);
+ overrides.AppendElement(tempAddr);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP NativeDNSResolverOverride::SetCnameOverride(
+ const nsACString& aHost, const nsACString& aCNAME) {
+ if (aCNAME.IsEmpty()) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ AutoWriteLock lock(mLock);
+ mCnames.Put(aHost, nsCString(aCNAME));
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP NativeDNSResolverOverride::ClearHostOverride(
+ const nsACString& aHost) {
+ AutoWriteLock lock(mLock);
+ mCnames.Remove(aHost);
+ auto overrides = mOverrides.GetAndRemove(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;
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/GetAddrInfo.h b/netwerk/dns/GetAddrInfo.h
new file mode 100644
index 0000000000..e7dfdfdeec
--- /dev/null
+++ b/netwerk/dns/GetAddrInfo.h
@@ -0,0 +1,87 @@
+/* -*- 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 "nsDataHashtable.h"
+#include "mozilla/RWLock.h"
+#include "nsTArray.h"
+#include "prio.h"
+
+#if defined(XP_WIN)
+# define DNSQUERY_AVAILABLE 1
+#else
+# undef DNSQUERY_AVAILABLE
+#endif
+
+namespace mozilla {
+namespace net {
+
+class AddrInfo;
+
+/**
+ * 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();
+
+class NativeDNSResolverOverride : public nsINativeDNSResolverOverride {
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSINATIVEDNSRESOLVEROVERRIDE
+ public:
+ NativeDNSResolverOverride() : mLock("NativeDNSResolverOverride") {}
+
+ static already_AddRefed<nsINativeDNSResolverOverride> GetSingleton();
+
+ private:
+ virtual ~NativeDNSResolverOverride() = default;
+ mozilla::RWLock mLock;
+
+ nsDataHashtable<nsCStringHashKey, nsTArray<PRNetAddr>> mOverrides;
+ nsDataHashtable<nsCStringHashKey, nsCString> mCnames;
+
+ friend bool FindAddrOverride(const nsACString& aHost, uint16_t aAddressFamily,
+ uint16_t aFlags, AddrInfo** aAddrInfo);
+};
+
+} // 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..9eb477d8df
--- /dev/null
+++ b/netwerk/dns/HTTPSSVC.cpp
@@ -0,0 +1,433 @@
+/* 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"
+
+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;
+}
+
+Maybe<Tuple<nsCString, bool>> SVCB::GetAlpn(bool aNoHttp2,
+ bool aNoHttp3) const {
+ Maybe<Tuple<nsCString, bool>> alpn;
+ for (const auto& value : mSvcFieldValue) {
+ if (value.mValue.is<SvcParamAlpn>()) {
+ nsTArray<nsCString> alpnList;
+ alpnList.AppendElements(value.mValue.as<SvcParamAlpn>().mValue);
+ if (!alpnList.IsEmpty()) {
+ alpn.emplace();
+ alpn = Some(SelectAlpnFromAlpnList(alpnList, aNoHttp2, aNoHttp3));
+ }
+ return alpn;
+ }
+ }
+
+ return Nothing();
+}
+
+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);
+ }
+ }
+}
+
+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<Tuple<nsCString, bool>> SVCBRecord::GetAlpn() { return mAlpn; }
+
+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 CheckAlpnIsUsable(const nsACString& aTargetName,
+ const nsACString& aAlpn, bool aIsHttp3,
+ uint32_t& aExcludedCount) {
+ if (aAlpn.IsEmpty()) {
+ return false;
+ }
+
+ if (aIsHttp3 && gHttpHandler->IsHttp3Excluded(aTargetName)) {
+ aExcludedCount++;
+ return false;
+ }
+
+ return true;
+}
+
+already_AddRefed<nsISVCBRecord>
+DNSHTTPSSVCRecordBase::GetServiceModeRecordInternal(
+ bool aNoHttp2, bool aNoHttp3, const nsTArray<SVCB>& aRecords,
+ bool& aRecordsAllExcluded, bool aCheckHttp3ExcludedList) {
+ nsCOMPtr<nsISVCBRecord> selectedRecord;
+ uint32_t recordHasNoDefaultAlpnCount = 0;
+ uint32_t recordExcludedCount = 0;
+ aRecordsAllExcluded = false;
+ nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
+ bool RRSetHasEchConfig = false;
+ uint32_t h3ExcludedCount = 0;
+
+ for (const SVCB& record : aRecords) {
+ if (record.mSvcFieldPriority == 0) {
+ // In ServiceMode, the SvcPriority should never be 0.
+ return nullptr;
+ }
+
+ if (record.NoDefaultAlpn()) {
+ ++recordHasNoDefaultAlpnCount;
+ }
+
+ RRSetHasEchConfig |= record.mHasEchConfig;
+
+ bool excluded = false;
+ if (NS_SUCCEEDED(dns->IsSVCDomainNameFailed(mHost, record.mSvcDomainName,
+ &excluded)) &&
+ excluded) {
+ // Skip if the domain name of this record was failed to connect before.
+ ++recordExcludedCount;
+ continue;
+ }
+
+ Maybe<uint16_t> port = record.GetPort();
+ if (port && *port == 0) {
+ // Found an unsafe port, skip this record.
+ continue;
+ }
+
+ Maybe<Tuple<nsCString, bool>> alpn = record.GetAlpn(aNoHttp2, aNoHttp3);
+ if (alpn) {
+ if (!CheckAlpnIsUsable(record.mSvcDomainName, Get<0>(*alpn),
+ aCheckHttp3ExcludedList && Get<1>(*alpn),
+ h3ExcludedCount)) {
+ continue;
+ }
+ }
+
+ if (gHttpHandler->EchConfigEnabled() && RRSetHasEchConfig &&
+ !record.mHasEchConfig) {
+ // Don't use this record if this record has no echConfig, but others have.
+ continue;
+ }
+
+ if (!selectedRecord) {
+ selectedRecord = new SVCBRecord(record, std::move(port), std::move(alpn));
+ }
+ }
+
+ // If all records indicate "no-default-alpn", we should not use this RRSet.
+ if (recordHasNoDefaultAlpnCount == aRecords.Length()) {
+ return nullptr;
+ }
+
+ if (recordExcludedCount == aRecords.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 == aRecords.Length() && aCheckHttp3ExcludedList) {
+ return GetServiceModeRecordInternal(aNoHttp2, aNoHttp3, aRecords,
+ aRecordsAllExcluded, false);
+ }
+
+ 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;
+ for (const SVCB& record : aRecords) {
+ if (record.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.mHasEchConfig;
+ if (!(*aAllRecordsHaveEchConfig)) {
+ aResult.Clear();
+ return;
+ }
+
+ Maybe<uint16_t> port = record.GetPort();
+ if (port && *port == 0) {
+ // Found an unsafe port, skip this record.
+ continue;
+ }
+
+ Maybe<Tuple<nsCString, bool>> alpn = record.GetAlpn(aNoHttp2, aNoHttp3);
+ if (alpn) {
+ if (!CheckAlpnIsUsable(record.mSvcDomainName, Get<0>(*alpn),
+ aCheckHttp3ExcludedList && Get<1>(*alpn),
+ h3ExcludedCount)) {
+ continue;
+ }
+ }
+
+ RefPtr<nsISVCBRecord> svcbRecord =
+ new SVCBRecord(record, std::move(port), std::move(alpn));
+ 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 == aRecords.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..354f779b34
--- /dev/null
+++ b/netwerk/dns/HTTPSSVC.h
@@ -0,0 +1,153 @@
+/* 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"
+
+namespace mozilla {
+namespace net {
+
+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;
+ Maybe<Tuple<nsCString, bool>> GetAlpn(bool aNoHttp2, bool aNoHttp3) const;
+ void GetIPHints(CopyableTArray<mozilla::net::NetAddr>& aAddresses) const;
+ uint16_t mSvcFieldPriority = 0;
+ nsCString mSvcDomainName;
+ nsCString mEchConfig;
+ nsCString mODoHConfig;
+ bool mHasIPHints = false;
+ bool mHasEchConfig = false;
+ CopyableTArray<SvcFieldValue> mSvcFieldValue;
+};
+
+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<uint16_t>&& aPort,
+ Maybe<Tuple<nsCString, bool>>&& aAlpn)
+ : mData(data), mPort(std::move(aPort)), mAlpn(std::move(aAlpn)) {}
+
+ private:
+ virtual ~SVCBRecord() = default;
+ SVCB mData;
+ Maybe<uint16_t> mPort;
+ Maybe<Tuple<nsCString, bool>> 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/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..658329693a
--- /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 }
+typedef std::pair<char16_t, char16_t> BlocklistRange;
+
+// 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..0e6c76dc5c
--- /dev/null
+++ b/netwerk/dns/NativeDNSResolverOverrideChild.cpp
@@ -0,0 +1,41 @@
+/* -*- 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::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..5b24cb3dcd
--- /dev/null
+++ b/netwerk/dns/NativeDNSResolverOverrideChild.h
@@ -0,0 +1,38 @@
+/* -*- 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 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..75fab125df
--- /dev/null
+++ b/netwerk/dns/NativeDNSResolverOverrideParent.cpp
@@ -0,0 +1,103 @@
+/* -*- 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"
+
+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) {
+ PRNetAddr tempAddr;
+ // Unfortunately, PR_StringToNetAddr does not properly initialize
+ // the output buffer in the case of IPv6 input. See bug 223145.
+ memset(&tempAddr, 0, sizeof(PRNetAddr));
+
+ if (PR_StringToNetAddr(nsCString(aIPLiteral).get(), &tempAddr) !=
+ PR_SUCCESS) {
+ 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::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..f6429c7c54
--- /dev/null
+++ b/netwerk/dns/PDNSRequest.ipdl
@@ -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/. */
+
+include protocol PNecko;
+include protocol PSocketProcess;
+
+include PDNSRequestParams;
+
+include "mozilla/net/NeckoMessageUtils.h";
+
+using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
+
+namespace mozilla {
+namespace net {
+
+async refcounted 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,
+ uint16_t type, OriginAttributes originAttributes,
+ uint32_t 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..1f52e7f39b
--- /dev/null
+++ b/netwerk/dns/PDNSRequestParams.ipdlh
@@ -0,0 +1,38 @@
+/* -*- 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 NetAddr from "mozilla/net/DNS.h";
+using mozilla::net::IPCTypeRecord from "mozilla/net/DNSByTypeRecord.h";
+
+
+namespace mozilla {
+namespace net {
+
+//-----------------------------------------------------------------------------
+// DNS IPDL structs
+//-----------------------------------------------------------------------------
+
+struct DNSRecord
+{
+ nsCString canonicalName;
+ NetAddr[] addrs;
+ double trrFetchDuration;
+ double trrFetchDurationNetworkOnly;
+ bool isTRR;
+ uint32_t effectiveTRRMode;
+};
+
+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..1737e61dd2
--- /dev/null
+++ b/netwerk/dns/PNativeDNSResolverOverride.ipdl
@@ -0,0 +1,25 @@
+/* -*- 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 refcounted protocol PNativeDNSResolverOverride
+{
+ manager PSocketProcess;
+
+child:
+ async __delete__();
+ async AddIPOverride(nsCString aHost, nsCString aIPLiteral);
+ 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..02509b76ec
--- /dev/null
+++ b/netwerk/dns/PTRRService.ipdl
@@ -0,0 +1,27 @@
+/* -*- 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 PSMIPCTypes;
+
+namespace mozilla {
+namespace net {
+
+async refcounted protocol PTRRService
+{
+ manager PSocketProcess;
+
+child:
+ async __delete__();
+ async UpdatePlatformDNSInformation(nsCString[] aSuffixList);
+ async UpdateParentalControlEnabled(bool aEnabled);
+ async ClearDNSCache(bool aTrrToo);
+ async SetDetectedTrrURI(nsCString aURI);
+};
+
+} //namespace net
+} //namespace mozilla
diff --git a/netwerk/dns/PublicSuffixList.jsm b/netwerk/dns/PublicSuffixList.jsm
new file mode 100644
index 0000000000..c6988c0b8b
--- /dev/null
+++ b/netwerk/dns/PublicSuffixList.jsm
@@ -0,0 +1,106 @@
+/* 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/. */
+"use strict";
+
+const { RemoteSettings } = ChromeUtils.import(
+ "resource://services-settings/remote-settings.js"
+);
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const FileUtils = ChromeUtils.import("resource://gre/modules/FileUtils.jsm")
+ .FileUtils;
+
+const EXPORTED_SYMBOLS = ["PublicSuffixList"];
+
+const RECORD_ID = "tld-dafsa";
+const SIGNAL = "public-suffix-list-updated";
+
+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.download(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.delete(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.download(changed[0]);
+ } catch (err) {
+ Cu.reportError(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..dde39fd25e
--- /dev/null
+++ b/netwerk/dns/TRR.cpp
@@ -0,0 +1,965 @@
+/* -*- 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 "nsCharSeparatedTokenizer.h"
+#include "nsContentUtils.h"
+#include "nsHttpHandler.h"
+#include "nsIHttpChannel.h"
+#include "nsIHttpChannelInternal.h"
+#include "nsIIOService.h"
+#include "nsIInputStream.h"
+#include "nsISupportsBase.h"
+#include "nsISupportsUtils.h"
+#include "nsITimedChannel.h"
+#include "nsIUploadChannel2.h"
+#include "nsIURIMutator.h"
+#include "nsNetUtil.h"
+#include "nsStringStream.h"
+#include "nsThreadUtils.h"
+#include "nsURLHelper.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/SyncRunnable.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/Tokenizer.h"
+#include "mozilla/UniquePtr.h"
+
+namespace mozilla {
+namespace net {
+
+#undef LOG
+#undef LOG_ENABLED
+extern mozilla::LazyLogModule gHostResolverLog;
+#define LOG(args) MOZ_LOG(gHostResolverLog, mozilla::LogLevel::Debug, args)
+#define LOG_ENABLED() \
+ MOZ_LOG_TEST(mozilla::net::gHostResolverLog, mozilla::LogLevel::Debug)
+
+NS_IMPL_ISUPPORTS(TRR, nsIHttpPushListener, nsIInterfaceRequestor,
+ nsIStreamListener, nsIRunnable)
+
+NS_IMETHODIMP
+TRR::Notify(nsITimer* aTimer) {
+ if (aTimer == mTimeout) {
+ mTimeout = nullptr;
+ Cancel();
+ } else {
+ MOZ_CRASH("Unknown timer");
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TRR::Run() {
+ MOZ_ASSERT_IF(XRE_IsParentProcess() && gTRRService,
+ NS_IsMainThread() || gTRRService->IsOnTRRThread());
+ MOZ_ASSERT_IF(XRE_IsSocketProcess(), NS_IsMainThread());
+
+ if ((gTRRService == nullptr) || NS_FAILED(SendHTTPRequest())) {
+ RecordReason(nsHostRecord::TRR_SEND_FAILED);
+ FailData(NS_ERROR_FAILURE);
+ // The dtor will now be run
+ }
+ return NS_OK;
+}
+
+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;
+ }
+}
+
+nsresult TRR::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 = GetMainThreadEventTarget();
+ if (main) {
+ // Forward to the main thread synchronously.
+ SyncRunnable::DispatchToThread(
+ main, new SyncRunnable(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);
+}
+
+nsresult TRR::SendHTTPRequest() {
+ // This is essentially the "run" method - created from nsHostResolver
+
+ 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 (((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 (UseDefaultServer() &&
+ gTRRService->IsTemporarilyBlocked(mHost, mOriginSuffix, mPB, true)) {
+ if (mType == TRRTYPE_A) {
+ // count only blocklist for A records to avoid double counts
+ Telemetry::Accumulate(Telemetry::DNS_TRR_BLACKLISTED2,
+ TRRService::AutoDetectedKey(), true);
+ }
+
+ RecordReason(nsHostRecord::TRR_HOST_BLOCKED_TEMPORARY);
+ // not really an error but no TRR is issued
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ if (gTRRService->IsExcludedFromTRR(mHost)) {
+ RecordReason(nsHostRecord::TRR_EXCLUDED);
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ if (UseDefaultServer() && (mType == TRRTYPE_A)) {
+ Telemetry::Accumulate(Telemetry::DNS_TRR_BLACKLISTED2,
+ TRRService::AutoDetectedKey(), false);
+ }
+ }
+
+ bool useGet = StaticPrefs::network_trr_useGET();
+ nsAutoCString body;
+ nsCOMPtr<nsIURI> dnsURI;
+ bool disableECS = StaticPrefs::network_trr_disable_ECS();
+ nsresult rv;
+
+ LOG(("TRR::SendHTTPRequest resolve %s type %u\n", mHost.get(), mType));
+
+ if (useGet) {
+ nsAutoCString tmp;
+ rv = DNSPacket::EncodeRequest(tmp, mHost, mType, disableECS);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ /* For GET requests, the outgoing packet needs to be Base64url-encoded and
+ then appended to the end of the URI. */
+ rv = Base64URLEncode(tmp.Length(),
+ reinterpret_cast<const unsigned char*>(tmp.get()),
+ Base64URLEncodePaddingPolicy::Omit, body);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString uri;
+ if (UseDefaultServer()) {
+ gTRRService->GetURI(uri);
+ } else {
+ uri = mRec->mTrrServer;
+ }
+
+ rv = NS_NewURI(getter_AddRefs(dnsURI), uri);
+ if (NS_FAILED(rv)) {
+ LOG(("TRR:SendHTTPRequest: NewURI failed!\n"));
+ return 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(body);
+
+ rv = NS_MutateURI(dnsURI).SetQuery(query).Finalize(dnsURI);
+ LOG(("TRR::SendHTTPRequest GET dns=%s\n", body.get()));
+ } else {
+ rv = DNSPacket::EncodeRequest(body, mHost, mType, disableECS);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString uri;
+ if (UseDefaultServer()) {
+ gTRRService->GetURI(uri);
+ } else {
+ uri = mRec->mTrrServer;
+ }
+ rv = NS_NewURI(getter_AddRefs(dnsURI), uri);
+ }
+ if (NS_FAILED(rv)) {
+ LOG(("TRR:SendHTTPRequest: NewURI failed!\n"));
+ return rv;
+ }
+
+ nsCOMPtr<nsIChannel> channel;
+ rv = CreateChannelHelper(dnsURI, getter_AddRefs(channel));
+ if (NS_FAILED(rv) || !channel) {
+ LOG(("TRR:SendHTTPRequest: NewChannel failed!\n"));
+ return rv;
+ }
+
+ channel->SetLoadFlags(
+ nsIRequest::LOAD_ANONYMOUS | nsIRequest::INHIBIT_CACHING |
+ nsIRequest::LOAD_BYPASS_CACHE | nsIChannel::LOAD_BYPASS_URL_CLASSIFIER);
+ 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);
+
+ rv = httpChannel->SetRequestHeader("Accept"_ns, "application/dns-message"_ns,
+ false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString cred;
+ if (UseDefaultServer()) {
+ gTRRService->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 (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,
+ "application/dns-message"_ns,
+ streamLength, "POST"_ns, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ rv = SetupTRRServiceChannelInternal(httpChannel, useGet);
+ 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->mTRRUsed = true;
+ }
+
+ NS_NewTimerWithCallback(getter_AddRefs(mTimeout), this,
+ gTRRService->GetRequestTimeout(),
+ nsITimer::TYPE_ONE_SHOT);
+
+ mChannel = channel;
+ return NS_OK;
+}
+
+// static
+nsresult TRR::SetupTRRServiceChannelInternal(nsIHttpChannel* aChannel,
+ bool aUseGet) {
+ 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("application/dns-message"_ns))) {
+ 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);
+ }
+
+ PRNetAddr tempAddr;
+ if (NS_FAILED(DohDecodeQuery(query, mHost, mType)) ||
+ (PR_StringToNetAddr(mHost.get(), &tempAddr) == PR_SUCCESS)) { // 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 (gTRRService->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 = 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(nsHostRecord::TRR_IS_OFFLINE);
+ }
+
+ switch (status) {
+ case NS_ERROR_UNKNOWN_HOST:
+ RecordReason(nsHostRecord::TRR_CHANNEL_DNS_FAIL);
+ break;
+ case NS_ERROR_OFFLINE:
+ RecordReason(nsHostRecord::TRR_IS_OFFLINE);
+ break;
+ case NS_ERROR_NET_RESET:
+ RecordReason(nsHostRecord::TRR_NET_RESET);
+ break;
+ case NS_ERROR_NET_TIMEOUT:
+ RecordReason(nsHostRecord::TRR_NET_TIMEOUT);
+ break;
+ case NS_ERROR_PROXY_CONNECTION_REFUSED:
+ RecordReason(nsHostRecord::TRR_NET_REFUSED);
+ break;
+ case NS_ERROR_NET_INTERRUPT:
+ RecordReason(nsHostRecord::TRR_NET_INTERRUPT);
+ break;
+ case NS_ERROR_NET_INADEQUATE_SECURITY:
+ RecordReason(nsHostRecord::TRR_NET_INADEQ_SEQURITY);
+ break;
+ default:
+ RecordReason(nsHostRecord::TRR_UNKNOWN_CHANNEL_FAILURE);
+ }
+ }
+
+ return NS_OK;
+}
+
+void TRR::SaveAdditionalRecords(
+ const nsClassHashtable<nsCStringHashKey, DOHresp>& aRecords) {
+ if (!mRec) {
+ return;
+ }
+ nsresult rv;
+ for (auto iter = aRecords.ConstIter(); !iter.Done(); iter.Next()) {
+ if (iter.Data() && iter.Data()->mAddresses.IsEmpty()) {
+ // no point in adding empty records.
+ continue;
+ }
+ RefPtr<nsHostRecord> hostRecord;
+ rv = mHostResolver->GetHostRecord(
+ iter.Key(), 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(iter.Key()).get()));
+ continue;
+ }
+ RefPtr<AddrInfo> ai(new AddrInfo(iter.Key(), TRRTYPE_A,
+ std::move(iter.Data()->mAddresses),
+ iter.Data()->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->mResolving++;
+ hostRecord->mEffectiveTRRMode = mRec->mEffectiveTRRMode;
+ RefPtr<AddrHostRecord> addrRec = do_QueryObject(hostRecord);
+ addrRec->mTrrStart = TimeStamp::Now();
+ LOG(("Completing lookup for additional: %s", nsCString(iter.Key()).get()));
+ (void)mHostResolver->CompleteLookup(hostRecord, NS_OK, ai, mPB,
+ mOriginSuffix, AddrHostRecord::TRR_OK);
+ }
+}
+
+void TRR::StoreIPHintAsDNSRecord(const struct SVCB& aSVCBRecord) {
+ LOG(("TRR::StoreIPHintAsDNSRecord [%p] [%s]", this,
+ aSVCBRecord.mSvcDomainName.get()));
+ CopyableTArray<NetAddr> addresses;
+ aSVCBRecord.GetIPHints(addresses);
+ if (addresses.IsEmpty()) {
+ return;
+ }
+
+ RefPtr<nsHostRecord> hostRecord;
+ nsresult rv = mHostResolver->GetHostRecord(
+ aSVCBRecord.mSvcDomainName, EmptyCString(),
+ nsIDNSService::RESOLVE_TYPE_DEFAULT,
+ mRec->flags | nsHostResolver::RES_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);
+
+ uint32_t ttl = AddrInfo::NO_TTL_DATA;
+ RefPtr<AddrInfo> ai(new AddrInfo(aSVCBRecord.mSvcDomainName, TRRTYPE_A,
+ std::move(addresses), ttl));
+
+ // 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 = mRec->mEffectiveTRRMode;
+ RefPtr<AddrHostRecord> addrRec = do_QueryObject(hostRecord);
+ addrRec->mTrrStart = TimeStamp::Now();
+
+ (void)mHostResolver->CompleteLookup(hostRecord, NS_OK, ai, mPB, mOriginSuffix,
+ AddrHostRecord::TRR_OK);
+}
+
+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, 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;
+ }
+ (void)mHostResolver->CompleteLookup(mRec, NS_OK, ai, mPB, mOriginSuffix,
+ mTRRSkippedReason);
+ mHostResolver = nullptr;
+ mRec = nullptr;
+ } else {
+ (void)mHostResolver->CompleteLookupByType(mRec, NS_OK, mResult, 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(nsHostRecord::TRR_FAILED);
+
+ if (mType == TRRTYPE_TXT || mType == TRRTYPE_HTTPSSVC) {
+ TypeRecordResultType empty(Nothing{});
+ (void)mHostResolver->CompleteLookupByType(mRec, error, empty, 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, mType, std::move(noAddresses));
+
+ (void)mHostResolver->CompleteLookup(mRec, error, ai, mPB, mOriginSuffix,
+ mTRRSkippedReason);
+ }
+
+ mHostResolver = nullptr;
+ mRec = nullptr;
+ return NS_OK;
+}
+
+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 = mPacket.Decode(
+ cname, mType, mCname, StaticPrefs::network_trr_allow_rfc1918(),
+ mTRRSkippedReason, mDNS, mResult, additionalRecords, mTTL);
+ if (NS_FAILED(rv)) {
+ LOG(("TRR::On200Response DohDecode %x\n", (int)rv));
+ }
+ }
+
+ // restore mCname as DohDecode() change it
+ mCname = cname;
+ if (NS_SUCCEEDED(rv) && !mDNS.mAddresses.IsEmpty()) {
+ ReturnData(aChannel);
+ 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 (!gTRRService) {
+ return NS_ERROR_FAILURE;
+ }
+ return gTRRService->DispatchTRRRequest(trr);
+}
+
+nsresult TRR::On200Response(nsIChannel* aChannel) {
+ // decode body and create an AddrInfo struct for the response
+ nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords;
+ nsresult rv = mPacket.Decode(
+ mHost, mType, mCname, StaticPrefs::network_trr_allow_rfc1918(),
+ mTRRSkippedReason, mDNS, mResult, additionalRecords, mTTL);
+
+ if (NS_FAILED(rv)) {
+ LOG(("TRR::On200Response DohDecode %x\n", (int)rv));
+ RecordReason(nsHostRecord::TRR_DECODE_FAILED);
+ return rv;
+ }
+ 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);
+}
+
+static void 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()));
+}
+
+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);
+
+ {
+ // Cancel the timer since we don't need it anymore.
+ nsCOMPtr<nsITimer> timer;
+ mTimeout.swap(timer);
+ if (timer) {
+ timer->Cancel();
+ }
+ }
+
+ if (UseDefaultServer()) {
+ // Bad content is still considered "okay" if the HTTP response is okay
+ gTRRService->TRRIsOkay(NS_SUCCEEDED(aStatusCode) ? TRRService::OKAY_NORMAL
+ : TRRService::OKAY_BAD);
+ }
+
+ 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.LowerCaseEqualsLiteral("application/dns-message")) {
+ 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(nsHostRecord::TRR_OK);
+ RecordProcessingTime(channel);
+ return rv;
+ }
+ } else {
+ RecordReason(nsHostRecord::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 =
+ mPacket.OnDataAvailable(aRequest, aInputStream, aOffset, aCount);
+ if (NS_FAILED(rv)) {
+ LOG(("TRR::OnDataAvailable:%d fail\n", __LINE__));
+ mFailed = true;
+ return rv;
+ }
+ return NS_OK;
+}
+
+class ProxyCancel : public Runnable {
+ public:
+ explicit ProxyCancel(TRR* aTRR) : Runnable("proxyTrrCancel"), mTRR(aTRR) {}
+
+ NS_IMETHOD Run() override {
+ mTRR->Cancel();
+ mTRR = nullptr;
+ return NS_OK;
+ }
+
+ private:
+ RefPtr<TRR> mTRR;
+};
+
+void TRR::Cancel() {
+ RefPtr<TRRServiceChannel> trrServiceChannel = do_QueryObject(mChannel);
+ if (trrServiceChannel && !XRE_IsSocketProcess()) {
+ if (gTRRService) {
+ nsCOMPtr<nsIThread> thread = gTRRService->TRRThread();
+ if (thread && !thread->IsOnCurrentThread()) {
+ nsCOMPtr<nsIRunnable> r = new ProxyCancel(this);
+ thread->Dispatch(r.forget());
+ return;
+ }
+ }
+ } else {
+ if (!NS_IsMainThread()) {
+ NS_DispatchToMainThread(new ProxyCancel(this));
+ return;
+ }
+ }
+
+ if (mChannel) {
+ RecordReason(nsHostRecord::TRR_TIMEOUT);
+ LOG(("TRR: %p canceling Channel %p %s %d\n", this, mChannel.get(),
+ mHost.get(), mType));
+ mChannel->Cancel(NS_ERROR_ABORT);
+ if (UseDefaultServer()) {
+ gTRRService->TRRIsOkay(TRRService::OKAY_TIMEOUT);
+ }
+ }
+}
+
+bool TRR::UseDefaultServer() { return !mRec || mRec->mTrrServer.IsEmpty(); }
+
+#undef LOG
+
+// namespace
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/TRR.h b/netwerk/dns/TRR.h
new file mode 100644
index 0000000000..489cca08b8
--- /dev/null
+++ b/netwerk/dns/TRR.h
@@ -0,0 +1,161 @@
+/* -*- 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 "nsHostResolver.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+#include "DNSPacket.h"
+
+namespace mozilla {
+namespace net {
+
+class TRRService;
+class TRRServiceChannel;
+extern TRRService* gTRRService;
+
+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)
+ : 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
+ explicit 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
+ explicit TRR(AHostResolver* aResolver, bool aPB)
+ : mozilla::Runnable("TRR"),
+ mHostResolver(aResolver),
+ mType(TRRTYPE_A),
+ mPB(aPB) {
+ MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(),
+ "TRR must be in parent or socket process");
+ }
+
+ // to verify a domain
+ explicit TRR(AHostResolver* aResolver, nsACString& aHost, enum TrrType aType,
+ const nsACString& aOriginSuffix, bool aPB)
+ : mozilla::Runnable("TRR"),
+ mHost(aHost),
+ mRec(nullptr),
+ mHostResolver(aResolver),
+ mType(aType),
+ mPB(aPB),
+ mOriginSuffix(aOriginSuffix) {
+ MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(),
+ "TRR must be in parent or socket process");
+ }
+
+ NS_IMETHOD Run() override;
+ void Cancel();
+ enum TrrType Type() { return mType; }
+ nsCString mHost;
+ RefPtr<nsHostRecord> mRec;
+ RefPtr<AHostResolver> mHostResolver;
+
+ private:
+ ~TRR() = default;
+ 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 blacklisted, 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 UseDefaultServer();
+ void SaveAdditionalRecords(
+ const nsClassHashtable<nsCStringHashKey, DOHresp>& aRecords);
+
+ nsresult CreateChannelHelper(nsIURI* aUri, nsIChannel** aResult);
+
+ friend class TRRServiceChannel;
+ static nsresult SetupTRRServiceChannelInternal(nsIHttpChannel* aChannel,
+ bool aUseGet);
+
+ void StoreIPHintAsDNSRecord(const struct SVCB& aSVCBRecord);
+
+ nsCOMPtr<nsIChannel> mChannel;
+ enum TrrType mType;
+ DNSPacket mPacket;
+ bool mFailed = false;
+ bool mPB;
+ DOHresp mDNS;
+ nsCOMPtr<nsITimer> mTimeout;
+ nsCString mCname;
+ uint32_t mCnameLoop = kCnameChaseMax; // loop detection counter
+
+ uint32_t mTTL = UINT32_MAX;
+ TypeRecordResultType mResult = mozilla::AsVariant(Nothing());
+
+ nsHostRecord::TRRSkippedReason mTRRSkippedReason = nsHostRecord::TRR_UNSET;
+ void RecordReason(nsHostRecord::TRRSkippedReason reason) {
+ if (mTRRSkippedReason == nsHostRecord::TRR_UNSET) {
+ mTRRSkippedReason = reason;
+ }
+ }
+
+ // keep a copy of the originSuffix for the cases where mRec == nullptr */
+ const nsCString mOriginSuffix;
+};
+
+} // 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..989780aa33
--- /dev/null
+++ b/netwerk/dns/TRRQuery.cpp
@@ -0,0 +1,288 @@
+/* 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 "TRR.h"
+
+namespace mozilla {
+namespace net {
+
+#undef LOG
+extern mozilla::LazyLogModule gHostResolverLog;
+#define LOG(args) MOZ_LOG(gHostResolverLog, mozilla::LogLevel::Debug, args)
+
+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() {
+ MutexAutoLock trrlock(mTrrLock);
+ if (mTrrA) {
+ mTrrA->Cancel();
+ mTrrA = nullptr;
+ }
+ if (mTrrAAAA) {
+ mTrrAAAA->Cancel();
+ mTrrAAAA = nullptr;
+ }
+ if (mTrrByType) {
+ mTrrByType->Cancel();
+ mTrrByType = nullptr;
+ }
+}
+
+nsresult TRRQuery::DispatchLookup(TRR* pushedTRR) {
+ mTrrStart = TimeStamp::Now();
+
+ RefPtr<AddrHostRecord> addrRec;
+ RefPtr<TypeHostRecord> typeRec;
+
+ if (mRecord->IsAddrRecord()) {
+ addrRec = do_QueryObject(mRecord);
+ MOZ_ASSERT(addrRec);
+ } else {
+ typeRec = do_QueryObject(mRecord);
+ MOZ_ASSERT(typeRec);
+ }
+
+ mTrrStart = TimeStamp::Now();
+ bool madeQuery = false;
+
+ if (addrRec) {
+ mTrrAUsed = INIT;
+ mTrrAAAAUsed = INIT;
+
+ // If asking for AF_UNSPEC, issue both A and AAAA.
+ // If asking for AF_INET6 or AF_INET, do only that single type
+ enum TrrType rectype = (mRecord->af == AF_INET6) ? TRRTYPE_AAAA : TRRTYPE_A;
+
+ if (pushedTRR) {
+ rectype = pushedTRR->Type();
+ }
+ bool sendAgain;
+
+ do {
+ sendAgain = false;
+ if ((TRRTYPE_AAAA == rectype) && gTRRService &&
+ (gTRRService->DisableIPv6() ||
+ (StaticPrefs::network_trr_skip_AAAA_when_not_supported() &&
+ mHostResolver->GetNCS() &&
+ mHostResolver->GetNCS()->GetIPv6() ==
+ nsINetworkConnectivityService::NOT_AVAILABLE))) {
+ break;
+ }
+ LOG(("TRR Resolve %s type %d\n", addrRec->host.get(), (int)rectype));
+ RefPtr<TRR> trr;
+ trr = pushedTRR ? pushedTRR : new TRR(this, mRecord, rectype);
+ if (pushedTRR || NS_SUCCEEDED(gTRRService->DispatchTRRRequest(trr))) {
+ MutexAutoLock trrlock(mTrrLock);
+ 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);
+ }
+ madeQuery = true;
+ if (!pushedTRR && (mRecord->af == AF_UNSPEC) &&
+ (rectype == TRRTYPE_A)) {
+ rectype = TRRTYPE_AAAA;
+ sendAgain = true;
+ }
+ }
+ } while (sendAgain);
+ } else {
+ typeRec->mStart = TimeStamp::Now();
+ 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;
+ trr = pushedTRR ? pushedTRR : new TRR(this, mRecord, rectype);
+ RefPtr<TRR> trrRequest = trr;
+ if (pushedTRR || NS_SUCCEEDED(gTRRService->DispatchTRRRequest(trr))) {
+ MutexAutoLock trrlock(mTrrLock);
+ MOZ_ASSERT(!mTrrByType);
+ mTrrByType = trr;
+ madeQuery = true;
+ }
+ }
+
+ return madeQuery ? NS_OK : NS_ERROR_UNKNOWN_HOST;
+}
+
+AHostResolver::LookupStatus TRRQuery::CompleteLookup(
+ nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb,
+ const nsACString& aOriginsuffix, nsHostRecord::TRRSkippedReason aReason) {
+ if (rec != mRecord) {
+ return mHostResolver->CompleteLookup(rec, status, aNewRRSet, pb,
+ aOriginsuffix, aReason);
+ }
+
+ RefPtr<AddrInfo> newRRSet(aNewRRSet);
+ bool pendingARequest = false;
+ bool pendingAAAARequest = false;
+ {
+ MutexAutoLock trrlock(mTrrLock);
+ if (newRRSet->IsTRR() == TRRTYPE_A) {
+ MOZ_ASSERT(mTrrA);
+ mTRRAFailReason = aReason;
+ mTrrA = nullptr;
+ mTrrAUsed = NS_SUCCEEDED(status) ? OK : FAILED;
+ } else if (newRRSet->IsTRR() == TRRTYPE_AAAA) {
+ MOZ_ASSERT(mTrrAAAA);
+ mTRRAAAAFailReason = aReason;
+ mTrrAAAA = nullptr;
+ mTrrAAAAUsed = NS_SUCCEEDED(status) ? OK : FAILED;
+ } else {
+ MOZ_ASSERT(0);
+ }
+ if (mTrrA) {
+ pendingARequest = true;
+ }
+ if (mTrrAAAA) {
+ pendingAAAARequest = true;
+ }
+ }
+
+ 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;
+ }
+ }
+
+ if (pendingARequest ||
+ pendingAAAARequest) { // There are other outstanding requests
+ mFirstTRRresult = status;
+ if (NS_FAILED(status)) {
+ return LOOKUP_OK; // wait for outstanding
+ }
+
+ // There's another TRR complete pending. Wait for it and keep
+ // this RRset around until then.
+ MOZ_ASSERT(!mFirstTRR && newRRSet);
+ mFirstTRR.swap(newRRSet); // autoPtr.swap()
+ MOZ_ASSERT(mFirstTRR && !newRRSet);
+
+ if (StaticPrefs::network_trr_wait_for_A_and_AAAA()) {
+ LOG(("CompleteLookup: waiting for all responses!\n"));
+ return LOOKUP_OK;
+ }
+
+ if (pendingARequest && !StaticPrefs::network_trr_early_AAAA()) {
+ // This is an early AAAA with a pending A response. Allowed
+ // only by pref.
+ LOG(("CompleteLookup: avoiding early use of TRR AAAA!\n"));
+ return LOOKUP_OK;
+ }
+
+ // we can do some callbacks with this partial result which requires
+ // a deep copy
+ newRRSet = mFirstTRR;
+
+ // Increment mResolving so we wait for the next resolve too.
+ rec->mResolving++;
+ } else {
+ // no more outstanding TRRs
+ // If mFirstTRR is set, merge those addresses into current set!
+ if (mFirstTRR) {
+ if (NS_SUCCEEDED(status)) {
+ LOG(("Merging responses"));
+ newRRSet = merge_rrset(newRRSet, mFirstTRR);
+ } else {
+ LOG(("Will use previous response"));
+ newRRSet.swap(mFirstTRR); // transfers
+ // We must use the status of the first response, otherwise we'll
+ // pass an error result to the consumers.
+ status = mFirstTRRresult;
+ }
+ mFirstTRR = nullptr;
+ } else {
+ if (NS_FAILED(status) && status != NS_ERROR_DEFINITIVE_UNKNOWN_HOST &&
+ mFirstTRRresult == NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
+ status = NS_ERROR_DEFINITIVE_UNKNOWN_HOST;
+ }
+ }
+
+ if (mTRRSuccess && mHostResolver->GetNCS() &&
+ (mHostResolver->GetNCS()->GetNAT64() ==
+ nsINetworkConnectivityService::OK) &&
+ newRRSet) {
+ newRRSet = mHostResolver->GetNCS()->MapNAT64IPs(newRRSet);
+ }
+ }
+
+ if (mTrrAUsed == OK) {
+ AccumulateCategoricalKeyed(
+ TRRService::AutoDetectedKey(),
+ Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::trrAOK);
+ } else if (mTrrAUsed == FAILED) {
+ AccumulateCategoricalKeyed(
+ TRRService::AutoDetectedKey(),
+ Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::trrAFail);
+ }
+
+ if (mTrrAAAAUsed == OK) {
+ AccumulateCategoricalKeyed(
+ TRRService::AutoDetectedKey(),
+ Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::trrAAAAOK);
+ } else if (mTrrAAAAUsed == FAILED) {
+ AccumulateCategoricalKeyed(
+ TRRService::AutoDetectedKey(),
+ Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::trrAAAAFail);
+ }
+
+ return mHostResolver->CompleteLookup(rec, status, newRRSet, pb, aOriginsuffix,
+ aReason);
+}
+
+AHostResolver::LookupStatus TRRQuery::CompleteLookupByType(
+ nsHostRecord* rec, nsresult status,
+ mozilla::net::TypeRecordResultType& aResult, uint32_t aTtl, bool pb) {
+ if (rec == mRecord) {
+ MutexAutoLock trrlock(mTrrLock);
+ mTrrByType = nullptr;
+ }
+ return mHostResolver->CompleteLookupByType(rec, status, aResult, aTtl, pb);
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/TRRQuery.h b/netwerk/dns/TRRQuery.h
new file mode 100644
index 0000000000..d99d16d5fc
--- /dev/null
+++ b/netwerk/dns/TRRQuery.h
@@ -0,0 +1,90 @@
+/* -*- 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
+
+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();
+
+ enum TRRState { INIT, STARTED, OK, FAILED };
+ TRRState mTrrAUsed = INIT;
+ TRRState mTrrAAAAUsed = INIT;
+
+ AddrHostRecord::TRRSkippedReason mTRRAFailReason = AddrHostRecord::TRR_UNSET;
+ AddrHostRecord::TRRSkippedReason mTRRAAAAFailReason =
+ AddrHostRecord::TRR_UNSET;
+
+ virtual LookupStatus CompleteLookup(
+ nsHostRecord*, nsresult, mozilla::net::AddrInfo*, bool pb,
+ const nsACString& aOriginsuffix,
+ nsHostRecord::TRRSkippedReason aReason) override;
+ virtual LookupStatus CompleteLookupByType(
+ nsHostRecord*, nsresult, mozilla::net::TypeRecordResultType& aResult,
+ uint32_t aTtl, bool pb) override;
+ virtual nsresult GetHostRecord(const nsACString& host,
+ const nsACString& aTrrServer, uint16_t type,
+ uint16_t 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:
+ ~TRRQuery() = default;
+ RefPtr<nsHostResolver> mHostResolver;
+ RefPtr<nsHostRecord> mRecord;
+
+ Mutex mTrrLock; // lock when accessing the mTrrA[AAA] pointers
+ RefPtr<mozilla::net::TRR> mTrrA;
+ RefPtr<mozilla::net::TRR> mTrrAAAA;
+ RefPtr<mozilla::net::TRR> mTrrByType;
+
+ uint8_t mTRRSuccess = 0; // number of successful TRR responses
+
+ mozilla::TimeDuration mTrrDuration;
+ mozilla::TimeStamp mTrrStart;
+
+ RefPtr<mozilla::net::AddrInfo> mFirstTRR; // partial TRR storage
+ nsresult mFirstTRRresult = 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..601069dbb2
--- /dev/null
+++ b/netwerk/dns/TRRService.cpp
@@ -0,0 +1,944 @@
+/* -*- 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 "nsAppDirectoryServiceDefs.h"
+#include "nsCharSeparatedTokenizer.h"
+#include "nsComponentManagerUtils.h"
+#include "nsDirectoryServiceUtils.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/Tokenizer.h"
+#include "mozilla/net/rust_helper.h"
+
+#if defined(XP_WIN) && !defined(__MINGW32__)
+# include <shlobj_core.h> // for SHGetSpecialFolderPathA
+#endif // XP_WIN
+
+static const char kOpenCaptivePortalLoginEvent[] = "captive-portal-login";
+static const char kClearPrivateData[] = "clear-private-data";
+static const char kPurge[] = "browser:purge-session-history";
+static const char kDisableIpv6Pref[] = "network.dns.disableIPv6";
+static const char kRolloutURIPref[] = "doh-rollout.uri";
+static const char kRolloutModePref[] = "doh-rollout.mode";
+
+#define TRR_PREF_PREFIX "network.trr."
+#define TRR_PREF(x) TRR_PREF_PREFIX x
+
+namespace mozilla {
+namespace net {
+
+#undef LOG
+extern mozilla::LazyLogModule gHostResolverLog;
+#define LOG(args) MOZ_LOG(gHostResolverLog, mozilla::LogLevel::Debug, args)
+
+TRRService* gTRRService = nullptr;
+StaticRefPtr<nsIThread> sTRRBackgroundThread;
+static Atomic<TRRService*> sTRRServicePtr;
+
+NS_IMPL_ISUPPORTS(TRRService, nsIObserver, nsISupportsWeakReference)
+
+TRRService::TRRService()
+ : mInitialized(false),
+ mBlocklistDurationSeconds(60),
+ mLock("trrservice"),
+ mConfirmationNS("example.com"_ns),
+ mCaptiveIsPassed(false),
+ mTRRBLStorage("DataMutex::TRRBlocklist"),
+ mConfirmationState(CONFIRM_INIT),
+ mRetryConfirmInterval(125),
+ mTRRFailures(0),
+ mParentalControlEnabled(false) {
+ MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
+}
+
+// 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;
+}
+
+constexpr auto kTRRIsAutoDetectedKey = "(auto-detected)"_ns;
+constexpr auto kTRRNotAutoDetectedKey = "(default)"_ns;
+// static
+const nsCString& TRRService::AutoDetectedKey() {
+ if (gTRRService && gTRRService->IsUsingAutoDetectedURL()) {
+ return kTRRIsAutoDetectedKey.AsString();
+ }
+
+ return kTRRNotAutoDetectedKey.AsString();
+}
+
+static void RemoveTRRBlocklistFile() {
+ MOZ_ASSERT(NS_IsMainThread(), "Getting the profile dir on the main thread");
+
+ nsCOMPtr<nsIFile> file;
+ nsresult rv =
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ rv = file->AppendNative("TRRBlacklist.txt"_ns);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ // Dispatch an async task that removes the blocklist file from the profile.
+ rv = NS_DispatchBackgroundTask(
+ NS_NewRunnableFunction("RemoveTRRBlocklistFile::Remove",
+ [file] { file->Remove(false); }),
+ NS_DISPATCH_EVENT_MAY_BLOCK);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ Preferences::SetBool("network.trr.blocklist_cleanup_done", true);
+}
+
+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(kDisableIpv6Pref, this, true);
+ prefBranch->AddObserver(kRolloutURIPref, this, true);
+ prefBranch->AddObserver(kRolloutModePref, this, true);
+ }
+
+ gTRRService = this;
+ sTRRServicePtr = this;
+
+ ReadPrefs(nullptr);
+
+ if (XRE_IsParentProcess()) {
+ mCaptiveIsPassed = CheckCaptivePortalIsPassed();
+
+ mParentalControlEnabled = GetParentalControlEnabledInternal();
+
+ nsCOMPtr<nsINetworkLinkService> nls =
+ do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID);
+ if (nls) {
+ nsTArray<nsCString> suffixList;
+ nls->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;
+
+ if (!StaticPrefs::network_trr_blocklist_cleanup_done()) {
+ // Dispatch an idle task to the main thread that gets the profile dir
+ // then attempts to delete the blocklist file on a background thread.
+ Unused << NS_DispatchToMainThreadQueue(
+ NS_NewCancelableRunnableFunction("RemoveTRRBlocklistFile::GetDir",
+ [] { RemoveTRRBlocklistFile(); }),
+ EventQueuePriority::Idle);
+ }
+ }
+
+ 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) {
+ // If the user has set a custom URI then we don't want to override that.
+ if (mURIPrefHasUserValue) {
+ return;
+ }
+
+ mURISetByDetection = MaybeSetPrivateURI(aURI);
+}
+
+bool TRRService::Enabled(nsIRequest::TRRMode aMode) {
+ if (mMode == MODE_TRROFF) {
+ return false;
+ }
+ if (mConfirmationState == CONFIRM_INIT &&
+ (!StaticPrefs::network_trr_wait_for_portal() || mCaptiveIsPassed ||
+ (mMode == MODE_TRRONLY || aMode == nsIRequest::TRR_ONLY_MODE))) {
+ LOG(("TRRService::Enabled => CONFIRM_TRYING\n"));
+ mConfirmationState = CONFIRM_TRYING;
+ }
+
+ if (mConfirmationState == CONFIRM_TRYING) {
+ LOG(("TRRService::Enabled MaybeConfirm()\n"));
+ MaybeConfirm();
+ }
+
+ if (mConfirmationState != CONFIRM_OK) {
+ LOG(("TRRService::Enabled mConfirmationState=%d mCaptiveIsPassed=%d\n",
+ (int)mConfirmationState, (int)mCaptiveIsPassed));
+ }
+
+ return (mConfirmationState == CONFIRM_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);
+ ProcessURITemplate(newURI);
+
+ {
+ MutexAutoLock 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;
+ }
+ mPrivateURI = newURI;
+ }
+
+ // 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)) {
+ uint32_t 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, kRolloutURIPref)) {
+ OnTRRURIChange();
+ }
+ if (!name || !strcmp(name, TRR_PREF("credentials"))) {
+ MutexAutoLock lock(mLock);
+ Preferences::GetCString(TRR_PREF("credentials"), mPrivateCred);
+ }
+ if (!name || !strcmp(name, TRR_PREF("confirmationNS"))) {
+ MutexAutoLock lock(mLock);
+ nsAutoCString old(mConfirmationNS);
+ Preferences::GetCString(TRR_PREF("confirmationNS"), mConfirmationNS);
+ if (name && !old.IsEmpty() && !mConfirmationNS.Equals(old) &&
+ (mConfirmationState > CONFIRM_TRYING) &&
+ (mMode == MODE_TRRFIRST || mMode == MODE_TRRONLY)) {
+ LOG(("TRR::ReadPrefs: restart confirmationNS state\n"));
+ mConfirmationState = CONFIRM_TRYING;
+ MaybeConfirm_locked();
+ }
+ }
+ if (!name || !strcmp(name, TRR_PREF("bootstrapAddress"))) {
+ MutexAutoLock lock(mLock);
+ Preferences::GetCString(TRR_PREF("bootstrapAddress"), mBootstrapAddr);
+ clearEntireCache = true;
+ }
+ if (!name || !strcmp(name, TRR_PREF("blacklist-duration"))) {
+ // prefs is given in number of seconds
+ uint32_t secs;
+ if (NS_SUCCEEDED(
+ Preferences::GetUint(TRR_PREF("blacklist-duration"), &secs))) {
+ mBlocklistDurationSeconds = secs;
+ }
+ }
+ if (!name || !strcmp(name, kDisableIpv6Pref)) {
+ bool tmp;
+ if (NS_SUCCEEDED(Preferences::GetBool(kDisableIpv6Pref, &tmp))) {
+ mDisableIPv6 = tmp;
+ }
+ }
+ if (!name || !strcmp(name, TRR_PREF("excluded-domains")) ||
+ !strcmp(name, TRR_PREF("builtin-excluded-domains"))) {
+ MutexAutoLock lock(mLock);
+ mExcludedDomains.Clear();
+
+ auto parseExcludedDomains = [this](const char* aPrefName) {
+ nsAutoCString excludedDomains;
+ 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.PutEntry(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) {
+ MutexAutoLock lock(mLock);
+ for (const auto& item : aArray) {
+ LOG(("Adding %s from /etc/hosts to excluded domains", item.get()));
+ mEtcHostsDomains.PutEntry(item);
+ }
+}
+
+void TRRService::ReadEtcHostsFile() {
+ if (!StaticPrefs::network_trr_exclude_etc_hosts()) {
+ return;
+ }
+
+ auto readHostsTask = []() {
+ MOZ_ASSERT(!NS_IsMainThread(), "Must not run on the main thread");
+#if defined(XP_WIN) && !defined(__MINGW32__)
+ // 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");
+#elif defined(__MINGW32__)
+ nsAutoCString path("C:\\windows\\system32\\drivers\\etc\\hosts"_ns);
+#else
+ nsAutoCString path("/etc/hosts"_ns);
+#endif
+
+ LOG(("Reading hosts file at %s", path.get()));
+ rust_parse_etc_hosts(&path, [](const nsTArray<nsCString>* aArray) -> bool {
+ RefPtr<TRRService> service(sTRRServicePtr);
+ if (service && aArray) {
+ service->AddEtcHosts(*aArray);
+ }
+ return !!service;
+ });
+ };
+
+ Unused << NS_DispatchBackgroundTask(
+ NS_NewRunnableFunction("Read /etc/hosts file", readHostsTask),
+ NS_DISPATCH_EVENT_MAY_BLOCK);
+}
+
+nsresult TRRService::GetURI(nsACString& result) {
+ MutexAutoLock lock(mLock);
+ result = mPrivateURI;
+ return NS_OK;
+}
+
+nsresult TRRService::GetCredentials(nsCString& result) {
+ MutexAutoLock lock(mLock);
+ result = mPrivateCred;
+ return NS_OK;
+}
+
+uint32_t TRRService::GetRequestTimeout() {
+ if (mMode == MODE_TRRONLY) {
+ return StaticPrefs::network_trr_request_timeout_mode_trronly_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"));
+ gTRRService = nullptr;
+}
+
+nsresult TRRService::DispatchTRRRequest(TRR* aTrrRequest) {
+ return DispatchTRRRequestInternal(aTrrRequest, true);
+}
+
+nsresult TRRService::DispatchTRRRequestInternal(TRR* aTrrRequest,
+ bool aWithLock) {
+ NS_ENSURE_ARG_POINTER(aTrrRequest);
+ if (!StaticPrefs::network_trr_fetch_off_main_thread() ||
+ XRE_IsSocketProcess()) {
+ return NS_DispatchToMainThread(aTrrRequest);
+ }
+
+ RefPtr<TRR> trr = aTrrRequest;
+ nsCOMPtr<nsIThread> thread = aWithLock ? TRRThread() : TRRThread_locked();
+ if (!thread) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return thread->Dispatch(trr.forget());
+}
+
+already_AddRefed<nsIThread> TRRService::TRRThread() {
+ MutexAutoLock lock(mLock);
+ return TRRThread_locked();
+}
+
+already_AddRefed<nsIThread> TRRService::TRRThread_locked() {
+ RefPtr<nsIThread> thread = sTRRBackgroundThread;
+ return thread.forget();
+}
+
+bool TRRService::IsOnTRRThread() {
+ nsCOMPtr<nsIThread> thread;
+ {
+ MutexAutoLock 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)) {
+ ReadPrefs(NS_ConvertUTF16toUTF8(aData).get());
+
+ MutexAutoLock lock(mLock);
+ if (((mConfirmationState == CONFIRM_INIT) && !mBootstrapAddr.IsEmpty() &&
+ (mMode == MODE_TRRONLY)) ||
+ (mConfirmationState == CONFIRM_FAILED)) {
+ mConfirmationState = CONFIRM_TRYING;
+ MaybeConfirm_locked();
+ }
+
+ } else if (!strcmp(aTopic, kOpenCaptivePortalLoginEvent)) {
+ // We are in a captive portal
+ LOG(("TRRservice in captive portal\n"));
+ mCaptiveIsPassed = false;
+ } else if (!strcmp(aTopic, NS_CAPTIVE_PORTAL_CONNECTIVITY)) {
+ nsAutoCString data = NS_ConvertUTF16toUTF8(aData);
+ LOG(("TRRservice captive portal was %s\n", data.get()));
+
+ // We should avoid doing calling MaybeConfirm in response to a pref change
+ // unless the service is in a TRR=enabled mode.
+ if (mMode == MODE_TRRFIRST || mMode == MODE_TRRONLY) {
+ if (!mCaptiveIsPassed) {
+ if (mConfirmationState != CONFIRM_OK) {
+ mConfirmationState = CONFIRM_TRYING;
+ MaybeConfirm();
+ }
+ } else {
+ LOG(("TRRservice CP clear when already up!\n"));
+ }
+ 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) && mURISetByDetection) {
+ // If the URI was set via SetDetectedTrrURI we need to restore it to the
+ // default pref when a network link change occurs.
+ CheckURIPrefs();
+ }
+ } else if (!strcmp(aTopic, "xpcom-shutdown-threads")) {
+ if (sTRRBackgroundThread) {
+ nsCOMPtr<nsIThread> thread;
+ {
+ MutexAutoLock lock(mLock);
+ 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()) {
+ return;
+ }
+
+ MutexAutoLock lock(mLock);
+ mDNSSuffixDomains.Clear();
+ for (const auto& item : aSuffixList) {
+ LOG(("TRRService adding %s to suffix list", item.get()));
+ mDNSSuffixDomains.PutEntry(item);
+ }
+}
+
+void TRRService::MaybeConfirm() {
+ MutexAutoLock lock(mLock);
+ MaybeConfirm_locked();
+}
+
+void TRRService::MaybeConfirm_locked() {
+ mLock.AssertCurrentThreadOwns();
+ if (mMode == MODE_TRROFF || mConfirmer ||
+ mConfirmationState != CONFIRM_TRYING) {
+ LOG(
+ ("TRRService:MaybeConfirm mode=%d, mConfirmer=%p "
+ "mConfirmationState=%d\n",
+ (int)mMode, (void*)mConfirmer, (int)mConfirmationState));
+ return;
+ }
+
+ if (mConfirmationNS.Equals("skip") || mMode == MODE_TRRONLY) {
+ LOG(("TRRService starting confirmation test %s SKIPPED\n",
+ mPrivateURI.get()));
+ mConfirmationState = CONFIRM_OK;
+ } else {
+ LOG(("TRRService starting confirmation test %s %s\n", mPrivateURI.get(),
+ mConfirmationNS.get()));
+ mConfirmer = new TRR(this, mConfirmationNS, TRRTYPE_NS, ""_ns, false);
+ DispatchTRRRequestInternal(mConfirmer, false);
+ }
+}
+
+bool TRRService::MaybeBootstrap(const nsACString& aPossible,
+ nsACString& aResult) {
+ MutexAutoLock lock(mLock);
+ if (mMode == MODE_TRROFF || mBootstrapAddr.IsEmpty()) {
+ return false;
+ }
+
+ nsCOMPtr<nsIURI> url;
+ nsresult rv =
+ NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID)
+ .Apply(NS_MutatorMethod(&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) {
+ if (!Enabled(nsIRequest::TRR_DEFAULT_MODE)) {
+ return true;
+ }
+
+ auto bl = mTRRBLStorage.Lock();
+ if (bl->IsEmpty()) {
+ return false;
+ }
+
+ // use a unified casing for the hashkey
+ nsAutoCString hashkey(aHost + aOriginSuffix);
+ if (int32_t* val = bl->GetValue(hashkey)) {
+ int32_t until = *val + mBlocklistDurationSeconds;
+ int32_t expire = NowInSeconds();
+ if (until > expire) {
+ LOG(("Host [%s] is TRR blocklisted\n", nsCString(aHost).get()));
+ return true;
+ }
+
+ // the blocklisted entry has expired
+ bl->Remove(hashkey);
+ }
+ 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 (mMode == 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.
+ MutexAutoLock lock(mLock);
+
+ return IsExcludedFromTRR_unlocked(aHost);
+}
+
+bool TRRService::IsExcludedFromTRR_unlocked(const nsACString& aHost) {
+ if (!NS_IsMainThread()) {
+ mLock.AssertCurrentThreadOwns();
+ }
+
+ 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.GetEntry(subdomain)) {
+ LOG(("Subdomain [%s] of host [%s] Is Excluded From TRR via pref\n",
+ subdomain.BeginReading(), aHost.BeginReading()));
+ return true;
+ }
+ if (mDNSSuffixDomains.GetEntry(subdomain)) {
+ LOG(("Subdomain [%s] of host [%s] Is Excluded From TRR via pref\n",
+ subdomain.BeginReading(), aHost.BeginReading()));
+ return true;
+ }
+ if (mEtcHostsDomains.GetEntry(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) {
+ LOG(("TRR blocklist %s\n", nsCString(aHost).get()));
+ nsAutoCString hashkey(aHost + aOriginSuffix);
+
+ // this overwrites any existing entry
+ {
+ auto bl = mTRRBLStorage.Lock();
+ bl->Put(hashkey, NowInSeconds());
+ }
+
+ if (aParentsToo) {
+ // 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);
+ DispatchTRRRequest(trr);
+ }
+ }
+}
+
+NS_IMETHODIMP
+TRRService::Notify(nsITimer* aTimer) {
+ if (aTimer == mRetryConfirmTimer) {
+ mRetryConfirmTimer = nullptr;
+ if (mConfirmationState == CONFIRM_FAILED) {
+ LOG(("TRRService retry NS of %s\n", mConfirmationNS.get()));
+ mConfirmationState = CONFIRM_TRYING;
+ MaybeConfirm();
+ }
+ } else {
+ MOZ_CRASH("Unknown timer");
+ }
+
+ return NS_OK;
+}
+
+void TRRService::TRRIsOkay(enum TrrOkay aReason) {
+ MOZ_ASSERT_IF(XRE_IsParentProcess(), NS_IsMainThread() || IsOnTRRThread());
+ MOZ_ASSERT_IF(XRE_IsSocketProcess(), NS_IsMainThread());
+
+ Telemetry::AccumulateCategoricalKeyed(
+ AutoDetectedKey(),
+ aReason == OKAY_NORMAL
+ ? Telemetry::LABELS_DNS_TRR_SUCCESS2::Fine
+ : (aReason == OKAY_TIMEOUT
+ ? Telemetry::LABELS_DNS_TRR_SUCCESS2::Timeout
+ : Telemetry::LABELS_DNS_TRR_SUCCESS2::Bad));
+ if (aReason == OKAY_NORMAL) {
+ mTRRFailures = 0;
+ } else if ((mMode == MODE_TRRFIRST) && (mConfirmationState == CONFIRM_OK)) {
+ // only count failures while in OK state
+ uint32_t fails = ++mTRRFailures;
+ if (fails >= StaticPrefs::network_trr_max_fails()) {
+ LOG(("TRRService goes FAILED after %u failures in a row\n", fails));
+ mConfirmationState = CONFIRM_FAILED;
+ // Fire off a timer and start re-trying the NS domain again
+ NS_NewTimerWithCallback(getter_AddRefs(mRetryConfirmTimer), this,
+ mRetryConfirmInterval, nsITimer::TYPE_ONE_SHOT);
+ mTRRFailures = 0; // clear it again
+ }
+ }
+}
+
+AHostResolver::LookupStatus TRRService::CompleteLookup(
+ nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb,
+ const nsACString& aOriginSuffix, nsHostRecord::TRRSkippedReason aReason) {
+ // 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->IsTRR() == TRRTYPE_NS);
+
+#ifdef DEBUG
+ {
+ MutexAutoLock lock(mLock);
+ MOZ_ASSERT(!mConfirmer || (mConfirmationState == CONFIRM_TRYING));
+ }
+#endif
+ if (mConfirmationState == CONFIRM_TRYING) {
+ {
+ MutexAutoLock lock(mLock);
+ MOZ_ASSERT(mConfirmer);
+ mConfirmationState = NS_SUCCEEDED(status) ? CONFIRM_OK : CONFIRM_FAILED;
+ LOG(("TRRService finishing confirmation test %s %d %X\n",
+ mPrivateURI.get(), (int)mConfirmationState, (unsigned int)status));
+ mConfirmer = nullptr;
+
+ if (mConfirmationState == CONFIRM_OK) {
+ // A fresh confirmation means previous blocked entries might not
+ // be valid anymore.
+ auto bl = mTRRBLStorage.Lock();
+ bl->Clear();
+ }
+ }
+ if (mConfirmationState == CONFIRM_FAILED) {
+ // retry failed NS confirmation
+ NS_NewTimerWithCallback(getter_AddRefs(mRetryConfirmTimer), this,
+ mRetryConfirmInterval, nsITimer::TYPE_ONE_SHOT);
+ if (mRetryConfirmInterval < 64000) {
+ // double the interval up to this point
+ mRetryConfirmInterval *= 2;
+ }
+ } else {
+ if (mMode != MODE_TRRONLY) {
+ // don't accumulate trronly data here since trronly failures are
+ // handled above by trying again, so counting the successes here would
+ // skew the numbers
+ Telemetry::Accumulate(Telemetry::DNS_TRR_NS_VERFIFIED2,
+ TRRService::AutoDetectedKey(),
+ (mConfirmationState == CONFIRM_OK));
+ }
+ mRetryConfirmInterval = StaticPrefs::network_trr_retry_timeout_ms();
+ }
+ return LOOKUP_OK;
+ }
+
+ // when called without a host record, this is a domain name check response.
+ 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;
+}
+
+AHostResolver::LookupStatus TRRService::CompleteLookupByType(
+ nsHostRecord*, nsresult, mozilla::net::TypeRecordResultType& aResult,
+ uint32_t aTtl, bool aPb) {
+ return LOOKUP_OK;
+}
+
+#undef LOG
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/TRRService.h b/netwerk/dns/TRRService.h
new file mode 100644
index 0000000000..0a34696a03
--- /dev/null
+++ b/netwerk/dns/TRRService.h
@@ -0,0 +1,152 @@
+/* -*- 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"
+
+class nsDNSService;
+class nsIPrefBranch;
+class nsINetworkLinkService;
+class nsIObserverService;
+
+namespace mozilla {
+namespace net {
+
+class TRRServiceChild;
+class TRRServiceParent;
+
+class TRRService : public TRRServiceBase,
+ public nsIObserver,
+ public nsITimerCallback,
+ public nsSupportsWeakReference,
+ public AHostResolver {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+ NS_DECL_NSITIMERCALLBACK
+
+ TRRService();
+ nsresult Init();
+ nsresult Start();
+ bool Enabled(nsIRequest::TRRMode aMode = nsIRequest::TRR_FIRST_MODE);
+ bool IsConfirmed() { return mConfirmationState == CONFIRM_OK; }
+
+ bool DisableIPv6() { return mDisableIPv6; }
+ nsresult GetURI(nsACString& result);
+ nsresult GetCredentials(nsCString& result);
+ uint32_t GetRequestTimeout();
+
+ LookupStatus CompleteLookup(nsHostRecord*, nsresult, mozilla::net::AddrInfo*,
+ bool pb, const nsACString& aOriginSuffix,
+ nsHostRecord::TRRSkippedReason aReason) override;
+ LookupStatus CompleteLookupByType(nsHostRecord*, nsresult,
+ mozilla::net::TypeRecordResultType&,
+ 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);
+ enum TrrOkay { OKAY_NORMAL = 0, OKAY_TIMEOUT = 1, OKAY_BAD = 2 };
+ void TRRIsOkay(enum TrrOkay aReason);
+ bool ParentalControlEnabled() const { return mParentalControlEnabled; }
+
+ nsresult DispatchTRRRequest(TRR* aTrrRequest);
+ already_AddRefed<nsIThread> TRRThread();
+ bool IsOnTRRThread();
+
+ bool IsUsingAutoDetectedURL() { return mURISetByDetection; }
+ static const nsCString& AutoDetectedKey();
+
+ 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);
+ void MaybeConfirm();
+ void MaybeConfirm_locked();
+ 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();
+
+ // 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;
+ Atomic<uint32_t, Relaxed> mBlocklistDurationSeconds;
+
+ Mutex mLock;
+
+ nsCString mPrivateCred; // main thread only
+ nsCString mConfirmationNS;
+ nsCString mBootstrapAddr;
+
+ Atomic<bool, Relaxed>
+ mCaptiveIsPassed; // set when captive portal check is passed
+ Atomic<bool, Relaxed> mDisableIPv6; // don't even try
+
+ // 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<nsDataHashtable<nsCStringHashKey, int32_t>> mTRRBLStorage;
+
+ // A set of domains that we should not use TRR for.
+ nsTHashtable<nsCStringHashKey> mExcludedDomains;
+ nsTHashtable<nsCStringHashKey> mDNSSuffixDomains;
+ nsTHashtable<nsCStringHashKey> mEtcHostsDomains;
+
+ enum ConfirmationState {
+ CONFIRM_INIT = 0,
+ CONFIRM_TRYING = 1,
+ CONFIRM_OK = 2,
+ CONFIRM_FAILED = 3
+ };
+ Atomic<ConfirmationState, Relaxed> mConfirmationState;
+ RefPtr<TRR> mConfirmer;
+ nsCOMPtr<nsITimer> mRetryConfirmTimer;
+ uint32_t mRetryConfirmInterval; // milliseconds until retry
+ Atomic<uint32_t, Relaxed> mTRRFailures;
+ bool mParentalControlEnabled;
+};
+
+extern TRRService* gTRRService;
+
+} // 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..122af1103e
--- /dev/null
+++ b/netwerk/dns/TRRServiceBase.cpp
@@ -0,0 +1,144 @@
+/* -*- 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 "mozilla/Preferences.h"
+#include "nsIOService.h"
+
+namespace mozilla {
+namespace net {
+
+#undef LOG
+extern mozilla::LazyLogModule gHostResolverLog;
+#define LOG(args) MOZ_LOG(gHostResolverLog, mozilla::LogLevel::Debug, args)
+
+TRRServiceBase::TRRServiceBase() : mMode(0), mURISetByDetection(false) {}
+
+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;
+
+ // The user has set a custom URI so it takes precedence.
+ if (mURIPrefHasUserValue) {
+ 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(mURIPref);
+}
+
+// static
+uint32_t ModeFromPrefs() {
+ // 0 - off, 1 - reserved, 2 - TRR first, 3 - TRR only, 4 - reserved,
+ // 5 - explicit off
+
+ auto processPrefValue = [](uint32_t value) -> uint32_t {
+ if (value == MODE_RESERVED1 || value == MODE_RESERVED4 ||
+ value > MODE_TRROFF) {
+ return MODE_TRROFF;
+ }
+ return value;
+ };
+
+ uint32_t tmp = MODE_NATIVEONLY;
+ if (NS_SUCCEEDED(Preferences::GetUint("network.trr.mode", &tmp))) {
+ tmp = processPrefValue(tmp);
+ }
+
+ if (tmp != MODE_NATIVEONLY) {
+ return tmp;
+ }
+
+ if (NS_SUCCEEDED(Preferences::GetUint(kRolloutModePref, &tmp))) {
+ return processPrefValue(tmp);
+ }
+
+ return MODE_NATIVEONLY;
+}
+
+void TRRServiceBase::OnTRRModeChange() {
+ uint32_t oldMode = mMode;
+ mMode = ModeFromPrefs();
+ if (mMode != oldMode) {
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->NotifyObservers(nullptr, NS_NETWORK_TRR_MODE_CHANGED_TOPIC, nullptr);
+ }
+ }
+
+ static bool readHosts = false;
+ if ((mMode == MODE_TRRFIRST || mMode == MODE_TRRONLY) && !readHosts) {
+ readHosts = true;
+ ReadEtcHostsFile();
+ }
+}
+
+void TRRServiceBase::OnTRRURIChange() {
+ mURIPrefHasUserValue = Preferences::HasUserValue("network.trr.uri");
+ Preferences::GetCString("network.trr.uri", mURIPref);
+ Preferences::GetCString(kRolloutURIPref, mRolloutURIPref);
+
+ CheckURIPrefs();
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/TRRServiceBase.h b/netwerk/dns/TRRServiceBase.h
new file mode 100644
index 0000000000..d552b26099
--- /dev/null
+++ b/netwerk/dns/TRRServiceBase.h
@@ -0,0 +1,51 @@
+/* -*- 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 "nsString.h"
+
+namespace mozilla {
+namespace net {
+
+class TRRServiceBase {
+ public:
+ TRRServiceBase();
+ uint32_t Mode() { return mMode; }
+
+ protected:
+ ~TRRServiceBase() = default;
+ 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();
+
+ virtual void ReadEtcHostsFile() {}
+
+ nsCString mPrivateURI;
+ // Pref caches should only be used on the main thread.
+ bool mURIPrefHasUserValue = false;
+ nsCString mURIPref;
+ nsCString mRolloutURIPref;
+
+ Atomic<uint32_t, Relaxed> mMode;
+ Atomic<bool, Relaxed> mURISetByDetection;
+};
+
+} // 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..365f061a0d
--- /dev/null
+++ b/netwerk/dns/TRRServiceChild.cpp
@@ -0,0 +1,59 @@
+/* -*- 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/ClearOnShutdown.h"
+#include "mozilla/StaticPtr.h"
+#include "nsIDNService.h"
+#include "nsIObserverService.h"
+#include "TRRService.h"
+
+namespace mozilla {
+namespace net {
+
+static StaticRefPtr<nsIDNSService> sDNSService;
+
+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);
+ MOZ_ASSERT(gTRRService);
+
+ gTRRService->mCaptiveIsPassed = aCaptiveIsPassed;
+ gTRRService->mParentalControlEnabled = aParentalControlEnabled;
+ gTRRService->RebuildSuffixList(std::move(aDNSSuffixList));
+}
+
+mozilla::ipc::IPCResult TRRServiceChild::RecvUpdatePlatformDNSInformation(
+ nsTArray<nsCString>&& aDNSSuffixList) {
+ gTRRService->RebuildSuffixList(std::move(aDNSSuffixList));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult TRRServiceChild::RecvUpdateParentalControlEnabled(
+ const bool& aEnabled) {
+ gTRRService->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) {
+ gTRRService->SetDetectedTrrURI(aURI);
+ 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..7334a5f3d3
--- /dev/null
+++ b/netwerk/dns/TRRServiceChild.h
@@ -0,0 +1,44 @@
+/* -*- 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"
+
+namespace mozilla {
+
+namespace ipc {
+class FileDescriptor;
+} // namespace ipc
+
+namespace net {
+
+class TRRServiceChild : public PTRRServiceChild {
+ public:
+ NS_INLINE_DECL_REFCOUNTING(TRRServiceChild, override)
+
+ explicit TRRServiceChild() = default;
+
+ 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);
+
+ private:
+ virtual ~TRRServiceChild() = default;
+};
+
+} // 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..9002f239fe
--- /dev/null
+++ b/netwerk/dns/TRRServiceParent.cpp
@@ -0,0 +1,155 @@
+/* -*- 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 "nsICaptivePortalService.h"
+#include "nsIParentalControlsService.h"
+#include "nsINetworkLinkService.h"
+#include "nsIObserverService.h"
+#include "nsIOService.h"
+#include "nsNetCID.h"
+#include "TRRService.h"
+
+namespace mozilla {
+namespace net {
+
+static const char kRolloutURIPref[] = "doh-rollout.uri";
+static const char kRolloutModePref[] = "doh-rollout.mode";
+
+static const char* gTRRUriCallbackPrefs[] = {
+ "network.trr.uri", "network.trr.mode", kRolloutURIPref, kRolloutModePref,
+ nullptr,
+};
+
+NS_IMPL_ISUPPORTS(TRRServiceParent, nsIObserver, nsISupportsWeakReference)
+
+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);
+
+ Unused << socketParent->SendPTRRServiceConstructor(
+ this, captiveIsPassed, parentalControlEnabled, suffixList);
+}
+
+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;
+}
+
+bool TRRServiceParent::MaybeSetPrivateURI(const nsACString& aURI) {
+ nsAutoCString newURI(aURI);
+ ProcessURITemplate(newURI);
+
+ if (mPrivateURI.Equals(newURI)) {
+ return false;
+ }
+
+ mPrivateURI = newURI;
+
+ 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 (mURIPrefHasUserValue) {
+ return;
+ }
+
+ mURISetByDetection = MaybeSetPrivateURI(aURI);
+ RefPtr<TRRServiceParent> self = this;
+ nsCString uri(aURI);
+ gIOService->CallOrWaitForSocketProcess(
+ [self, uri]() { Unused << self->SendSetDetectedTrrURI(uri); });
+}
+
+void TRRServiceParent::GetTrrURI(nsACString& aURI) { 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, kRolloutURIPref)) {
+ OnTRRURIChange();
+ }
+
+ if (!aName || !strcmp(aName, "network.trr.mode") ||
+ !strcmp(aName, kRolloutModePref)) {
+ OnTRRModeChange();
+ }
+}
+
+void TRRServiceParent::ActorDestroy(ActorDestroyReason why) {
+ Preferences::UnregisterPrefixCallbacks(TRRServiceParent::PrefsChanged,
+ gTRRUriCallbackPrefs, this);
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/TRRServiceParent.h b/netwerk/dns/TRRServiceParent.h
new file mode 100644
index 0000000000..4df599fe1b
--- /dev/null
+++ b/netwerk/dns/TRRServiceParent.h
@@ -0,0 +1,43 @@
+/* -*- 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_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ 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 GetTrrURI(nsACString& aURI);
+
+ private:
+ virtual ~TRRServiceParent() = default;
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+ void prefsChanged(const char* aName);
+};
+
+} // 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..038b492cb4
--- /dev/null
+++ b/netwerk/dns/effective_tld_names.dat
@@ -0,0 +1,13597 @@
+// 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 : https://en.wikipedia.org/wiki/.ac
+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://en.wikipedia.org/wiki/.ae
+// see also: "Domain Name Eligibility Policy" at http://www.aeda.ae/eng/aepolicy.php
+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/nic-argentina/normativa-vigente
+ar
+com.ar
+edu.ar
+gob.ar
+gov.ar
+int.ar
+mil.ar
+musica.ar
+net.ar
+org.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://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
+
+// 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
+aprendemas.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
+
+// 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>
+cy
+ac.cy
+biz.cy
+com.cy
+ekloges.cy
+gov.cy
+ltd.cy
+name.cy
+net.cy
+org.cy
+parliament.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 : http://www.afnic.fr/
+// domaines descriptifs : https://www.afnic.fr/medias/documents/Cadre_legal/Afnic_Naming_Policy_12122016_VEN.pdf
+fr
+asso.fr
+com.fr
+gouv.fr
+nom.fr
+prd.fr
+tm.fr
+// domaines sectoriels : https://www.afnic.fr/en/products-and-services/the-fr-tld/sector-based-fr-domains-4.html
+aeroport.fr
+avocat.fr
+avoues.fr
+cci.fr
+chambagri.fr
+chirurgiens-dentistes.fr
+experts-comptables.fr
+geometre-expert.fr
+greta.fr
+huissier-justice.fr
+medecin.fr
+notaires.fr
+pharmacien.fr
+port.fr
+veterinaire.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
+
+// 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/
+il
+ac.il
+co.il
+gov.il
+idf.il
+k12.il
+muni.il
+net.il
+org.il
+
+// 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
+co.in
+firm.in
+net.in
+org.in
+gen.in
+ind.in
+nic.in
+ac.in
+edu.in
+res.in
+gov.in
+mil.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.html
+// 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
+edu.ky
+gov.ky
+com.ky
+org.ky
+net.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 : http://about.museum/naming/
+// http://index.museum/
+museum
+academy.museum
+agriculture.museum
+air.museum
+airguard.museum
+alabama.museum
+alaska.museum
+amber.museum
+ambulance.museum
+american.museum
+americana.museum
+americanantiques.museum
+americanart.museum
+amsterdam.museum
+and.museum
+annefrank.museum
+anthro.museum
+anthropology.museum
+antiques.museum
+aquarium.museum
+arboretum.museum
+archaeological.museum
+archaeology.museum
+architecture.museum
+art.museum
+artanddesign.museum
+artcenter.museum
+artdeco.museum
+arteducation.museum
+artgallery.museum
+arts.museum
+artsandcrafts.museum
+asmatart.museum
+assassination.museum
+assisi.museum
+association.museum
+astronomy.museum
+atlanta.museum
+austin.museum
+australia.museum
+automotive.museum
+aviation.museum
+axis.museum
+badajoz.museum
+baghdad.museum
+bahn.museum
+bale.museum
+baltimore.museum
+barcelona.museum
+baseball.museum
+basel.museum
+baths.museum
+bauern.museum
+beauxarts.museum
+beeldengeluid.museum
+bellevue.museum
+bergbau.museum
+berkeley.museum
+berlin.museum
+bern.museum
+bible.museum
+bilbao.museum
+bill.museum
+birdart.museum
+birthplace.museum
+bonn.museum
+boston.museum
+botanical.museum
+botanicalgarden.museum
+botanicgarden.museum
+botany.museum
+brandywinevalley.museum
+brasil.museum
+bristol.museum
+british.museum
+britishcolumbia.museum
+broadcast.museum
+brunel.museum
+brussel.museum
+brussels.museum
+bruxelles.museum
+building.museum
+burghof.museum
+bus.museum
+bushey.museum
+cadaques.museum
+california.museum
+cambridge.museum
+can.museum
+canada.museum
+capebreton.museum
+carrier.museum
+cartoonart.museum
+casadelamoneda.museum
+castle.museum
+castres.museum
+celtic.museum
+center.museum
+chattanooga.museum
+cheltenham.museum
+chesapeakebay.museum
+chicago.museum
+children.museum
+childrens.museum
+childrensgarden.museum
+chiropractic.museum
+chocolate.museum
+christiansburg.museum
+cincinnati.museum
+cinema.museum
+circus.museum
+civilisation.museum
+civilization.museum
+civilwar.museum
+clinton.museum
+clock.museum
+coal.museum
+coastaldefence.museum
+cody.museum
+coldwar.museum
+collection.museum
+colonialwilliamsburg.museum
+coloradoplateau.museum
+columbia.museum
+columbus.museum
+communication.museum
+communications.museum
+community.museum
+computer.museum
+computerhistory.museum
+comunicações.museum
+contemporary.museum
+contemporaryart.museum
+convent.museum
+copenhagen.museum
+corporation.museum
+correios-e-telecomunicações.museum
+corvette.museum
+costume.museum
+countryestate.museum
+county.museum
+crafts.museum
+cranbrook.museum
+creation.museum
+cultural.museum
+culturalcenter.museum
+culture.museum
+cyber.museum
+cymru.museum
+dali.museum
+dallas.museum
+database.museum
+ddr.museum
+decorativearts.museum
+delaware.museum
+delmenhorst.museum
+denmark.museum
+depot.museum
+design.museum
+detroit.museum
+dinosaur.museum
+discovery.museum
+dolls.museum
+donostia.museum
+durham.museum
+eastafrica.museum
+eastcoast.museum
+education.museum
+educational.museum
+egyptian.museum
+eisenbahn.museum
+elburg.museum
+elvendrell.museum
+embroidery.museum
+encyclopedic.museum
+england.museum
+entomology.museum
+environment.museum
+environmentalconservation.museum
+epilepsy.museum
+essex.museum
+estate.museum
+ethnology.museum
+exeter.museum
+exhibition.museum
+family.museum
+farm.museum
+farmequipment.museum
+farmers.museum
+farmstead.museum
+field.museum
+figueres.museum
+filatelia.museum
+film.museum
+fineart.museum
+finearts.museum
+finland.museum
+flanders.museum
+florida.museum
+force.museum
+fortmissoula.museum
+fortworth.museum
+foundation.museum
+francaise.museum
+frankfurt.museum
+franziskaner.museum
+freemasonry.museum
+freiburg.museum
+fribourg.museum
+frog.museum
+fundacio.museum
+furniture.museum
+gallery.museum
+garden.museum
+gateway.museum
+geelvinck.museum
+gemological.museum
+geology.museum
+georgia.museum
+giessen.museum
+glas.museum
+glass.museum
+gorge.museum
+grandrapids.museum
+graz.museum
+guernsey.museum
+halloffame.museum
+hamburg.museum
+handson.museum
+harvestcelebration.museum
+hawaii.museum
+health.museum
+heimatunduhren.museum
+hellas.museum
+helsinki.museum
+hembygdsforbund.museum
+heritage.museum
+histoire.museum
+historical.museum
+historicalsociety.museum
+historichouses.museum
+historisch.museum
+historisches.museum
+history.museum
+historyofscience.museum
+horology.museum
+house.museum
+humanities.museum
+illustration.museum
+imageandsound.museum
+indian.museum
+indiana.museum
+indianapolis.museum
+indianmarket.museum
+intelligence.museum
+interactive.museum
+iraq.museum
+iron.museum
+isleofman.museum
+jamison.museum
+jefferson.museum
+jerusalem.museum
+jewelry.museum
+jewish.museum
+jewishart.museum
+jfk.museum
+journalism.museum
+judaica.museum
+judygarland.museum
+juedisches.museum
+juif.museum
+karate.museum
+karikatur.museum
+kids.museum
+koebenhavn.museum
+koeln.museum
+kunst.museum
+kunstsammlung.museum
+kunstunddesign.museum
+labor.museum
+labour.museum
+lajolla.museum
+lancashire.museum
+landes.museum
+lans.museum
+läns.museum
+larsson.museum
+lewismiller.museum
+lincoln.museum
+linz.museum
+living.museum
+livinghistory.museum
+localhistory.museum
+london.museum
+losangeles.museum
+louvre.museum
+loyalist.museum
+lucerne.museum
+luxembourg.museum
+luzern.museum
+mad.museum
+madrid.museum
+mallorca.museum
+manchester.museum
+mansion.museum
+mansions.museum
+manx.museum
+marburg.museum
+maritime.museum
+maritimo.museum
+maryland.museum
+marylhurst.museum
+media.museum
+medical.museum
+medizinhistorisches.museum
+meeres.museum
+memorial.museum
+mesaverde.museum
+michigan.museum
+midatlantic.museum
+military.museum
+mill.museum
+miners.museum
+mining.museum
+minnesota.museum
+missile.museum
+missoula.museum
+modern.museum
+moma.museum
+money.museum
+monmouth.museum
+monticello.museum
+montreal.museum
+moscow.museum
+motorcycle.museum
+muenchen.museum
+muenster.museum
+mulhouse.museum
+muncie.museum
+museet.museum
+museumcenter.museum
+museumvereniging.museum
+music.museum
+national.museum
+nationalfirearms.museum
+nationalheritage.museum
+nativeamerican.museum
+naturalhistory.museum
+naturalhistorymuseum.museum
+naturalsciences.museum
+nature.museum
+naturhistorisches.museum
+natuurwetenschappen.museum
+naumburg.museum
+naval.museum
+nebraska.museum
+neues.museum
+newhampshire.museum
+newjersey.museum
+newmexico.museum
+newport.museum
+newspaper.museum
+newyork.museum
+niepce.museum
+norfolk.museum
+north.museum
+nrw.museum
+nyc.museum
+nyny.museum
+oceanographic.museum
+oceanographique.museum
+omaha.museum
+online.museum
+ontario.museum
+openair.museum
+oregon.museum
+oregontrail.museum
+otago.museum
+oxford.museum
+pacific.museum
+paderborn.museum
+palace.museum
+paleo.museum
+palmsprings.museum
+panama.museum
+paris.museum
+pasadena.museum
+pharmacy.museum
+philadelphia.museum
+philadelphiaarea.museum
+philately.museum
+phoenix.museum
+photography.museum
+pilots.museum
+pittsburgh.museum
+planetarium.museum
+plantation.museum
+plants.museum
+plaza.museum
+portal.museum
+portland.museum
+portlligat.museum
+posts-and-telecommunications.museum
+preservation.museum
+presidio.museum
+press.museum
+project.museum
+public.museum
+pubol.museum
+quebec.museum
+railroad.museum
+railway.museum
+research.museum
+resistance.museum
+riodejaneiro.museum
+rochester.museum
+rockart.museum
+roma.museum
+russia.museum
+saintlouis.museum
+salem.museum
+salvadordali.museum
+salzburg.museum
+sandiego.museum
+sanfrancisco.museum
+santabarbara.museum
+santacruz.museum
+santafe.museum
+saskatchewan.museum
+satx.museum
+savannahga.museum
+schlesisches.museum
+schoenbrunn.museum
+schokoladen.museum
+school.museum
+schweiz.museum
+science.museum
+scienceandhistory.museum
+scienceandindustry.museum
+sciencecenter.museum
+sciencecenters.museum
+science-fiction.museum
+sciencehistory.museum
+sciences.museum
+sciencesnaturelles.museum
+scotland.museum
+seaport.museum
+settlement.museum
+settlers.museum
+shell.museum
+sherbrooke.museum
+sibenik.museum
+silk.museum
+ski.museum
+skole.museum
+society.museum
+sologne.museum
+soundandvision.museum
+southcarolina.museum
+southwest.museum
+space.museum
+spy.museum
+square.museum
+stadt.museum
+stalbans.museum
+starnberg.museum
+state.museum
+stateofdelaware.museum
+station.museum
+steam.museum
+steiermark.museum
+stjohn.museum
+stockholm.museum
+stpetersburg.museum
+stuttgart.museum
+suisse.museum
+surgeonshall.museum
+surrey.museum
+svizzera.museum
+sweden.museum
+sydney.museum
+tank.museum
+tcm.museum
+technology.museum
+telekommunikation.museum
+television.museum
+texas.museum
+textile.museum
+theater.museum
+time.museum
+timekeeping.museum
+topology.museum
+torino.museum
+touch.museum
+town.museum
+transport.museum
+tree.museum
+trolley.museum
+trust.museum
+trustee.museum
+uhren.museum
+ulm.museum
+undersea.museum
+university.museum
+usa.museum
+usantiques.museum
+usarts.museum
+uscountryestate.museum
+usculture.museum
+usdecorativearts.museum
+usgarden.museum
+ushistory.museum
+ushuaia.museum
+uslivinghistory.museum
+utah.museum
+uvic.museum
+valley.museum
+vantaa.museum
+versailles.museum
+viking.museum
+village.museum
+virginia.museum
+virtual.museum
+virtuel.museum
+vlaanderen.museum
+volkenkunde.museum
+wales.museum
+wallonie.museum
+war.museum
+washingtondc.museum
+watchandclock.museum
+watch-and-clock.museum
+western.museum
+westfalen.museum
+whaling.museum
+wildlife.museum
+williamsburg.museum
+windmill.museum
+workshop.museum
+york.museum
+yorkshire.museum
+yosemite.museum
+youth.museum
+zoological.museum
+zoology.museum
+ירושלים.museum
+иком.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.net.my/
+my
+com.my
+net.my
+org.my
+gov.my
+edu.my
+mil.my
+name.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
+ic.gov.pl
+is.gov.pl
+us.gov.pl
+kmpsp.gov.pl
+kppsp.gov.pl
+kwpsp.gov.pl
+psp.gov.pl
+wskr.gov.pl
+kwp.gov.pl
+mw.gov.pl
+ug.gov.pl
+um.gov.pl
+umig.gov.pl
+ugim.gov.pl
+upow.gov.pl
+uw.gov.pl
+starostwo.gov.pl
+pa.gov.pl
+po.gov.pl
+psse.gov.pl
+pup.gov.pl
+rzgw.gov.pl
+sa.gov.pl
+so.gov.pl
+sr.gov.pl
+wsa.gov.pl
+sko.gov.pl
+uzs.gov.pl
+wiih.gov.pl
+winb.gov.pl
+pinb.gov.pl
+wios.gov.pl
+witd.gov.pl
+wzmiuw.gov.pl
+piw.gov.pl
+wiw.gov.pl
+griw.gov.pl
+wif.gov.pl
+oum.gov.pl
+sdn.gov.pl
+zp.gov.pl
+uppo.gov.pl
+mup.gov.pl
+wuoz.gov.pl
+konsulat.gov.pl
+oirm.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 : http://www.afnic.fr/medias/documents/AFNIC-naming-policy2012.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 : http://online.dns.pt/dns/start_dns
+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 : http://www.afnic.re/obtenir/chartes/nommage-re/annexe-descriptifs
+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://www.nic.sh/registrar.html
+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
+net.ss
+org.ss
+
+// st : http://www.nic.st/html/policyrules/
+st
+co.st
+com.st
+consulado.st
+edu.st
+embaixada.st
+gov.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://en.wikipedia.org/wiki/.tf
+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 : https://en.wikipedia.org/wiki/.tn
+// http://whois.ati.tn/
+tn
+com.tn
+ens.tn
+fin.tn
+gov.tn
+ind.tn
+intl.tn
+nat.tn
+net.tn
+org.tn
+info.tn
+perso.tn
+tourism.tn
+edunet.tn
+rnrt.tn
+rns.tn
+rnu.tn
+mincom.tn
+agrinet.tn
+defense.tn
+turen.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
+krym.ua
+ks.ua
+kv.ua
+kyiv.ua
+lg.ua
+lt.ua
+lugansk.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
+vinnica.ua
+vinnytsia.ua
+vn.ua
+volyn.ua
+yalta.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.de.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
+ve
+arts.ve
+co.ve
+com.ve
+e12.ve
+edu.ve
+firm.ve
+gob.ve
+gov.ve
+info.ve
+int.ve
+mil.ve
+net.ve
+org.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.dot.vn/vnnic/vnnic/domainregistration.jsp
+vn
+com.vn
+net.vn
+org.vn
+edu.vn
+gov.vn
+int.vn
+ac.vn
+biz.vn
+info.vn
+name.vn
+pro.vn
+health.vn
+
+// vu : https://en.wikipedia.org/wiki/.vu
+// http://www.vunic.vu/
+vu
+com.vu
+edu.vu
+net.vu
+org.vu
+
+// wf : http://www.afnic.fr/medias/documents/AFNIC-naming-policy2012.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 : http://www.afnic.fr/medias/documents/AFNIC-naming-policy2012.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 2021-02-07T16:47:40Z
+// This list is auto-generated, don't edit it manually.
+// aaa : 2015-02-26 American Automobile Association, Inc.
+aaa
+
+// aarp : 2015-05-21 AARP
+aarp
+
+// abarth : 2015-07-30 Fiat Chrysler Automobiles N.V.
+abarth
+
+// abb : 2014-10-24 ABB Ltd
+abb
+
+// abbott : 2014-07-24 Abbott Laboratories, Inc.
+abbott
+
+// abbvie : 2015-07-30 AbbVie Inc.
+abbvie
+
+// abc : 2015-07-30 Disney Enterprises, Inc.
+abc
+
+// able : 2015-06-25 Able Inc.
+able
+
+// abogado : 2014-04-24 Minds + Machines Group Limited
+abogado
+
+// abudhabi : 2015-07-30 Abu Dhabi Systems and Information Centre
+abudhabi
+
+// academy : 2013-11-07 Binky Moon, LLC
+academy
+
+// accenture : 2014-08-15 Accenture plc
+accenture
+
+// accountant : 2014-11-20 dot Accountant Limited
+accountant
+
+// accountants : 2014-03-20 Binky Moon, LLC
+accountants
+
+// aco : 2015-01-08 ACO Severin Ahlmann GmbH & Co. KG
+aco
+
+// actor : 2013-12-12 Dog Beach, LLC
+actor
+
+// adac : 2015-07-16 Allgemeiner Deutscher Automobil-Club e.V. (ADAC)
+adac
+
+// ads : 2014-12-04 Charleston Road Registry Inc.
+ads
+
+// adult : 2014-10-16 ICM Registry AD LLC
+adult
+
+// aeg : 2015-03-19 Aktiebolaget Electrolux
+aeg
+
+// aetna : 2015-05-21 Aetna Life Insurance Company
+aetna
+
+// afamilycompany : 2015-07-23 Johnson Shareholdings, Inc.
+afamilycompany
+
+// afl : 2014-10-02 Australian Football League
+afl
+
+// africa : 2014-03-24 ZA Central Registry NPC trading as Registry.Africa
+africa
+
+// agakhan : 2015-04-23 Fondation Aga Khan (Aga Khan Foundation)
+agakhan
+
+// agency : 2013-11-14 Binky Moon, LLC
+agency
+
+// aig : 2014-12-18 American International Group, Inc.
+aig
+
+// airbus : 2015-07-30 Airbus S.A.S.
+airbus
+
+// airforce : 2014-03-06 Dog Beach, LLC
+airforce
+
+// airtel : 2014-10-24 Bharti Airtel Limited
+airtel
+
+// akdn : 2015-04-23 Fondation Aga Khan (Aga Khan Foundation)
+akdn
+
+// alfaromeo : 2015-07-31 Fiat Chrysler Automobiles N.V.
+alfaromeo
+
+// alibaba : 2015-01-15 Alibaba Group Holding Limited
+alibaba
+
+// alipay : 2015-01-15 Alibaba Group Holding Limited
+alipay
+
+// allfinanz : 2014-07-03 Allfinanz Deutsche Vermögensberatung Aktiengesellschaft
+allfinanz
+
+// allstate : 2015-07-31 Allstate Fire and Casualty Insurance Company
+allstate
+
+// ally : 2015-06-18 Ally Financial Inc.
+ally
+
+// alsace : 2014-07-02 Region Grand Est
+alsace
+
+// alstom : 2015-07-30 ALSTOM
+alstom
+
+// amazon : 2019-12-19 Amazon Registry Services, Inc.
+amazon
+
+// americanexpress : 2015-07-31 American Express Travel Related Services Company, Inc.
+americanexpress
+
+// americanfamily : 2015-07-23 AmFam, Inc.
+americanfamily
+
+// amex : 2015-07-31 American Express Travel Related Services Company, Inc.
+amex
+
+// amfam : 2015-07-23 AmFam, Inc.
+amfam
+
+// amica : 2015-05-28 Amica Mutual Insurance Company
+amica
+
+// amsterdam : 2014-07-24 Gemeente Amsterdam
+amsterdam
+
+// analytics : 2014-12-18 Campus IP LLC
+analytics
+
+// android : 2014-08-07 Charleston Road Registry Inc.
+android
+
+// anquan : 2015-01-08 Beijing Qihu Keji Co., Ltd.
+anquan
+
+// anz : 2015-07-31 Australia and New Zealand Banking Group Limited
+anz
+
+// aol : 2015-09-17 Oath Inc.
+aol
+
+// apartments : 2014-12-11 Binky Moon, LLC
+apartments
+
+// app : 2015-05-14 Charleston Road Registry Inc.
+app
+
+// apple : 2015-05-14 Apple Inc.
+apple
+
+// aquarelle : 2014-07-24 Aquarelle.com
+aquarelle
+
+// arab : 2015-11-12 League of Arab States
+arab
+
+// aramco : 2014-11-20 Aramco Services Company
+aramco
+
+// archi : 2014-02-06 Afilias Limited
+archi
+
+// army : 2014-03-06 Dog Beach, LLC
+army
+
+// art : 2016-03-24 UK Creative Ideas Limited
+art
+
+// arte : 2014-12-11 Association Relative à la Télévision Européenne G.E.I.E.
+arte
+
+// asda : 2015-07-31 Wal-Mart Stores, Inc.
+asda
+
+// associates : 2014-03-06 Binky Moon, LLC
+associates
+
+// athleta : 2015-07-30 The Gap, Inc.
+athleta
+
+// attorney : 2014-03-20 Dog Beach, LLC
+attorney
+
+// auction : 2014-03-20 Dog Beach, LLC
+auction
+
+// audi : 2015-05-21 AUDI Aktiengesellschaft
+audi
+
+// audible : 2015-06-25 Amazon Registry Services, Inc.
+audible
+
+// audio : 2014-03-20 UNR Corp.
+audio
+
+// auspost : 2015-08-13 Australian Postal Corporation
+auspost
+
+// author : 2014-12-18 Amazon Registry Services, Inc.
+author
+
+// auto : 2014-11-13 XYZ.COM LLC
+auto
+
+// autos : 2014-01-09 XYZ.COM LLC
+autos
+
+// avianca : 2015-01-08 Avianca Holdings S.A.
+avianca
+
+// aws : 2015-06-25 AWS Registry LLC
+aws
+
+// axa : 2013-12-19 AXA Group Operations SAS
+axa
+
+// azure : 2014-12-18 Microsoft Corporation
+azure
+
+// baby : 2015-04-09 XYZ.COM LLC
+baby
+
+// baidu : 2015-01-08 Baidu, Inc.
+baidu
+
+// banamex : 2015-07-30 Citigroup Inc.
+banamex
+
+// bananarepublic : 2015-07-31 The Gap, Inc.
+bananarepublic
+
+// band : 2014-06-12 Dog Beach, LLC
+band
+
+// bank : 2014-09-25 fTLD Registry Services LLC
+bank
+
+// bar : 2013-12-12 Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable
+bar
+
+// barcelona : 2014-07-24 Municipi de Barcelona
+barcelona
+
+// barclaycard : 2014-11-20 Barclays Bank PLC
+barclaycard
+
+// barclays : 2014-11-20 Barclays Bank PLC
+barclays
+
+// barefoot : 2015-06-11 Gallo Vineyards, Inc.
+barefoot
+
+// bargains : 2013-11-14 Binky Moon, LLC
+bargains
+
+// baseball : 2015-10-29 MLB Advanced Media DH, LLC
+baseball
+
+// basketball : 2015-08-20 Fédération Internationale de Basketball (FIBA)
+basketball
+
+// bauhaus : 2014-04-17 Werkhaus GmbH
+bauhaus
+
+// bayern : 2014-01-23 Bayern Connect GmbH
+bayern
+
+// bbc : 2014-12-18 British Broadcasting Corporation
+bbc
+
+// bbt : 2015-07-23 BB&T Corporation
+bbt
+
+// bbva : 2014-10-02 BANCO BILBAO VIZCAYA ARGENTARIA, S.A.
+bbva
+
+// bcg : 2015-04-02 The Boston Consulting Group, Inc.
+bcg
+
+// bcn : 2014-07-24 Municipi de Barcelona
+bcn
+
+// beats : 2015-05-14 Beats Electronics, LLC
+beats
+
+// beauty : 2015-12-03 XYZ.COM LLC
+beauty
+
+// beer : 2014-01-09 Minds + Machines Group Limited
+beer
+
+// bentley : 2014-12-18 Bentley Motors Limited
+bentley
+
+// berlin : 2013-10-31 dotBERLIN GmbH & Co. KG
+berlin
+
+// best : 2013-12-19 BestTLD Pty Ltd
+best
+
+// bestbuy : 2015-07-31 BBY Solutions, Inc.
+bestbuy
+
+// bet : 2015-05-07 Afilias Limited
+bet
+
+// bharti : 2014-01-09 Bharti Enterprises (Holding) Private Limited
+bharti
+
+// bible : 2014-06-19 American Bible Society
+bible
+
+// bid : 2013-12-19 dot Bid Limited
+bid
+
+// bike : 2013-08-27 Binky Moon, LLC
+bike
+
+// bing : 2014-12-18 Microsoft Corporation
+bing
+
+// bingo : 2014-12-04 Binky Moon, LLC
+bingo
+
+// bio : 2014-03-06 Afilias Limited
+bio
+
+// black : 2014-01-16 Afilias Limited
+black
+
+// blackfriday : 2014-01-16 UNR Corp.
+blackfriday
+
+// blockbuster : 2015-07-30 Dish DBS Corporation
+blockbuster
+
+// blog : 2015-05-14 Knock Knock WHOIS There, LLC
+blog
+
+// bloomberg : 2014-07-17 Bloomberg IP Holdings LLC
+bloomberg
+
+// blue : 2013-11-07 Afilias Limited
+blue
+
+// bms : 2014-10-30 Bristol-Myers Squibb Company
+bms
+
+// bmw : 2014-01-09 Bayerische Motoren Werke Aktiengesellschaft
+bmw
+
+// bnpparibas : 2014-05-29 BNP Paribas
+bnpparibas
+
+// boats : 2014-12-04 XYZ.COM LLC
+boats
+
+// boehringer : 2015-07-09 Boehringer Ingelheim International GmbH
+boehringer
+
+// bofa : 2015-07-31 Bank of America Corporation
+bofa
+
+// bom : 2014-10-16 Núcleo de Informação e Coordenação do Ponto BR - NIC.br
+bom
+
+// bond : 2014-06-05 ShortDot SA
+bond
+
+// boo : 2014-01-30 Charleston Road Registry Inc.
+boo
+
+// book : 2015-08-27 Amazon Registry Services, Inc.
+book
+
+// booking : 2015-07-16 Booking.com B.V.
+booking
+
+// bosch : 2015-06-18 Robert Bosch GMBH
+bosch
+
+// bostik : 2015-05-28 Bostik SA
+bostik
+
+// boston : 2015-12-10 Boston TLD Management, LLC
+boston
+
+// bot : 2014-12-18 Amazon Registry Services, Inc.
+bot
+
+// boutique : 2013-11-14 Binky Moon, LLC
+boutique
+
+// box : 2015-11-12 Intercap Registry Inc.
+box
+
+// bradesco : 2014-12-18 Banco Bradesco S.A.
+bradesco
+
+// bridgestone : 2014-12-18 Bridgestone Corporation
+bridgestone
+
+// broadway : 2014-12-22 Celebrate Broadway, Inc.
+broadway
+
+// broker : 2014-12-11 Dotbroker Registry Limited
+broker
+
+// brother : 2015-01-29 Brother Industries, Ltd.
+brother
+
+// brussels : 2014-02-06 DNS.be vzw
+brussels
+
+// budapest : 2013-11-21 Minds + Machines Group Limited
+budapest
+
+// bugatti : 2015-07-23 Bugatti International SA
+bugatti
+
+// build : 2013-11-07 Plan Bee LLC
+build
+
+// builders : 2013-11-07 Binky Moon, LLC
+builders
+
+// business : 2013-11-07 Binky Moon, LLC
+business
+
+// buy : 2014-12-18 Amazon Registry Services, Inc.
+buy
+
+// buzz : 2013-10-02 DOTSTRATEGY CO.
+buzz
+
+// bzh : 2014-02-27 Association www.bzh
+bzh
+
+// cab : 2013-10-24 Binky Moon, LLC
+cab
+
+// cafe : 2015-02-11 Binky Moon, LLC
+cafe
+
+// cal : 2014-07-24 Charleston Road Registry Inc.
+cal
+
+// call : 2014-12-18 Amazon Registry Services, Inc.
+call
+
+// calvinklein : 2015-07-30 PVH gTLD Holdings LLC
+calvinklein
+
+// cam : 2016-04-21 AC Webconnecting Holding B.V.
+cam
+
+// camera : 2013-08-27 Binky Moon, LLC
+camera
+
+// camp : 2013-11-07 Binky Moon, LLC
+camp
+
+// cancerresearch : 2014-05-15 Australian Cancer Research Foundation
+cancerresearch
+
+// canon : 2014-09-12 Canon Inc.
+canon
+
+// capetown : 2014-03-24 ZA Central Registry NPC trading as ZA Central Registry
+capetown
+
+// capital : 2014-03-06 Binky Moon, LLC
+capital
+
+// capitalone : 2015-08-06 Capital One Financial Corporation
+capitalone
+
+// car : 2015-01-22 XYZ.COM LLC
+car
+
+// caravan : 2013-12-12 Caravan International, Inc.
+caravan
+
+// cards : 2013-12-05 Binky Moon, LLC
+cards
+
+// care : 2014-03-06 Binky Moon, LLC
+care
+
+// career : 2013-10-09 dotCareer LLC
+career
+
+// careers : 2013-10-02 Binky Moon, LLC
+careers
+
+// cars : 2014-11-13 XYZ.COM LLC
+cars
+
+// casa : 2013-11-21 Minds + Machines Group Limited
+casa
+
+// case : 2015-09-03 CNH Industrial N.V.
+case
+
+// caseih : 2015-09-03 CNH Industrial N.V.
+caseih
+
+// cash : 2014-03-06 Binky Moon, LLC
+cash
+
+// casino : 2014-12-18 Binky Moon, LLC
+casino
+
+// catering : 2013-12-05 Binky Moon, LLC
+catering
+
+// catholic : 2015-10-21 Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication)
+catholic
+
+// cba : 2014-06-26 COMMONWEALTH BANK OF AUSTRALIA
+cba
+
+// cbn : 2014-08-22 The Christian Broadcasting Network, Inc.
+cbn
+
+// cbre : 2015-07-02 CBRE, Inc.
+cbre
+
+// cbs : 2015-08-06 CBS Domains Inc.
+cbs
+
+// center : 2013-11-07 Binky Moon, LLC
+center
+
+// ceo : 2013-11-07 CEOTLD Pty Ltd
+ceo
+
+// cern : 2014-06-05 European Organization for Nuclear Research ("CERN")
+cern
+
+// cfa : 2014-08-28 CFA Institute
+cfa
+
+// cfd : 2014-12-11 ShortDot SA
+cfd
+
+// chanel : 2015-04-09 Chanel International B.V.
+chanel
+
+// channel : 2014-05-08 Charleston Road Registry Inc.
+channel
+
+// charity : 2018-04-11 Binky Moon, LLC
+charity
+
+// chase : 2015-04-30 JPMorgan Chase Bank, National Association
+chase
+
+// chat : 2014-12-04 Binky Moon, LLC
+chat
+
+// cheap : 2013-11-14 Binky Moon, LLC
+cheap
+
+// chintai : 2015-06-11 CHINTAI Corporation
+chintai
+
+// christmas : 2013-11-21 UNR Corp.
+christmas
+
+// chrome : 2014-07-24 Charleston Road Registry Inc.
+chrome
+
+// church : 2014-02-06 Binky Moon, LLC
+church
+
+// cipriani : 2015-02-19 Hotel Cipriani Srl
+cipriani
+
+// circle : 2014-12-18 Amazon Registry Services, Inc.
+circle
+
+// cisco : 2014-12-22 Cisco Technology, Inc.
+cisco
+
+// citadel : 2015-07-23 Citadel Domain LLC
+citadel
+
+// citi : 2015-07-30 Citigroup Inc.
+citi
+
+// citic : 2014-01-09 CITIC Group Corporation
+citic
+
+// city : 2014-05-29 Binky Moon, LLC
+city
+
+// cityeats : 2014-12-11 Lifestyle Domain Holdings, Inc.
+cityeats
+
+// claims : 2014-03-20 Binky Moon, LLC
+claims
+
+// cleaning : 2013-12-05 Binky Moon, LLC
+cleaning
+
+// click : 2014-06-05 UNR Corp.
+click
+
+// clinic : 2014-03-20 Binky Moon, LLC
+clinic
+
+// clinique : 2015-10-01 The Estée Lauder Companies Inc.
+clinique
+
+// clothing : 2013-08-27 Binky Moon, LLC
+clothing
+
+// cloud : 2015-04-16 Aruba PEC S.p.A.
+cloud
+
+// club : 2013-11-08 .CLUB DOMAINS, LLC
+club
+
+// clubmed : 2015-06-25 Club Méditerranée S.A.
+clubmed
+
+// coach : 2014-10-09 Binky Moon, LLC
+coach
+
+// codes : 2013-10-31 Binky Moon, LLC
+codes
+
+// coffee : 2013-10-17 Binky Moon, LLC
+coffee
+
+// college : 2014-01-16 XYZ.COM LLC
+college
+
+// cologne : 2014-02-05 dotKoeln GmbH
+cologne
+
+// comcast : 2015-07-23 Comcast IP Holdings I, LLC
+comcast
+
+// commbank : 2014-06-26 COMMONWEALTH BANK OF AUSTRALIA
+commbank
+
+// community : 2013-12-05 Binky Moon, LLC
+community
+
+// company : 2013-11-07 Binky Moon, LLC
+company
+
+// compare : 2015-10-08 Registry Services, LLC
+compare
+
+// computer : 2013-10-24 Binky Moon, LLC
+computer
+
+// comsec : 2015-01-08 VeriSign, Inc.
+comsec
+
+// condos : 2013-12-05 Binky Moon, LLC
+condos
+
+// construction : 2013-09-16 Binky Moon, LLC
+construction
+
+// consulting : 2013-12-05 Dog Beach, LLC
+consulting
+
+// contact : 2015-01-08 Dog Beach, LLC
+contact
+
+// contractors : 2013-09-10 Binky Moon, LLC
+contractors
+
+// cooking : 2013-11-21 Minds + Machines Group Limited
+cooking
+
+// cookingchannel : 2015-07-02 Lifestyle Domain Holdings, Inc.
+cookingchannel
+
+// cool : 2013-11-14 Binky Moon, LLC
+cool
+
+// corsica : 2014-09-25 Collectivité de Corse
+corsica
+
+// country : 2013-12-19 DotCountry LLC
+country
+
+// coupon : 2015-02-26 Amazon Registry Services, Inc.
+coupon
+
+// coupons : 2015-03-26 Binky Moon, LLC
+coupons
+
+// courses : 2014-12-04 OPEN UNIVERSITIES AUSTRALIA PTY LTD
+courses
+
+// cpa : 2019-06-10 American Institute of Certified Public Accountants
+cpa
+
+// credit : 2014-03-20 Binky Moon, LLC
+credit
+
+// creditcard : 2014-03-20 Binky Moon, LLC
+creditcard
+
+// creditunion : 2015-01-22 DotCooperation LLC
+creditunion
+
+// cricket : 2014-10-09 dot Cricket Limited
+cricket
+
+// crown : 2014-10-24 Crown Equipment Corporation
+crown
+
+// crs : 2014-04-03 Federated Co-operatives Limited
+crs
+
+// cruise : 2015-12-10 Viking River Cruises (Bermuda) Ltd.
+cruise
+
+// cruises : 2013-12-05 Binky Moon, LLC
+cruises
+
+// csc : 2014-09-25 Alliance-One Services, Inc.
+csc
+
+// cuisinella : 2014-04-03 SCHMIDT GROUPE S.A.S.
+cuisinella
+
+// cymru : 2014-05-08 Nominet UK
+cymru
+
+// cyou : 2015-01-22 ShortDot SA
+cyou
+
+// dabur : 2014-02-06 Dabur India Limited
+dabur
+
+// dad : 2014-01-23 Charleston Road Registry Inc.
+dad
+
+// dance : 2013-10-24 Dog Beach, LLC
+dance
+
+// data : 2016-06-02 Dish DBS Corporation
+data
+
+// date : 2014-11-20 dot Date Limited
+date
+
+// dating : 2013-12-05 Binky Moon, LLC
+dating
+
+// datsun : 2014-03-27 NISSAN MOTOR CO., LTD.
+datsun
+
+// day : 2014-01-30 Charleston Road Registry Inc.
+day
+
+// dclk : 2014-11-20 Charleston Road Registry Inc.
+dclk
+
+// dds : 2015-05-07 Minds + Machines Group Limited
+dds
+
+// deal : 2015-06-25 Amazon Registry Services, Inc.
+deal
+
+// dealer : 2014-12-22 Intercap Registry Inc.
+dealer
+
+// deals : 2014-05-22 Binky Moon, LLC
+deals
+
+// degree : 2014-03-06 Dog Beach, LLC
+degree
+
+// delivery : 2014-09-11 Binky Moon, LLC
+delivery
+
+// dell : 2014-10-24 Dell Inc.
+dell
+
+// deloitte : 2015-07-31 Deloitte Touche Tohmatsu
+deloitte
+
+// delta : 2015-02-19 Delta Air Lines, Inc.
+delta
+
+// democrat : 2013-10-24 Dog Beach, LLC
+democrat
+
+// dental : 2014-03-20 Binky Moon, LLC
+dental
+
+// dentist : 2014-03-20 Dog Beach, LLC
+dentist
+
+// desi : 2013-11-14 Desi Networks LLC
+desi
+
+// design : 2014-11-07 Top Level Design, LLC
+design
+
+// dev : 2014-10-16 Charleston Road Registry Inc.
+dev
+
+// dhl : 2015-07-23 Deutsche Post AG
+dhl
+
+// diamonds : 2013-09-22 Binky Moon, LLC
+diamonds
+
+// diet : 2014-06-26 UNR Corp.
+diet
+
+// digital : 2014-03-06 Binky Moon, LLC
+digital
+
+// direct : 2014-04-10 Binky Moon, LLC
+direct
+
+// directory : 2013-09-20 Binky Moon, LLC
+directory
+
+// discount : 2014-03-06 Binky Moon, LLC
+discount
+
+// discover : 2015-07-23 Discover Financial Services
+discover
+
+// dish : 2015-07-30 Dish DBS Corporation
+dish
+
+// diy : 2015-11-05 Lifestyle Domain Holdings, Inc.
+diy
+
+// dnp : 2013-12-13 Dai Nippon Printing Co., Ltd.
+dnp
+
+// docs : 2014-10-16 Charleston Road Registry Inc.
+docs
+
+// doctor : 2016-06-02 Binky Moon, LLC
+doctor
+
+// dog : 2014-12-04 Binky Moon, LLC
+dog
+
+// domains : 2013-10-17 Binky Moon, LLC
+domains
+
+// dot : 2015-05-21 Dish DBS Corporation
+dot
+
+// download : 2014-11-20 dot Support Limited
+download
+
+// drive : 2015-03-05 Charleston Road Registry Inc.
+drive
+
+// dtv : 2015-06-04 Dish DBS Corporation
+dtv
+
+// dubai : 2015-01-01 Dubai Smart Government Department
+dubai
+
+// duck : 2015-07-23 Johnson Shareholdings, Inc.
+duck
+
+// dunlop : 2015-07-02 The Goodyear Tire & Rubber Company
+dunlop
+
+// dupont : 2015-06-25 E. I. du Pont de Nemours and Company
+dupont
+
+// durban : 2014-03-24 ZA Central Registry NPC trading as ZA Central Registry
+durban
+
+// dvag : 2014-06-23 Deutsche Vermögensberatung Aktiengesellschaft DVAG
+dvag
+
+// dvr : 2016-05-26 DISH Technologies L.L.C.
+dvr
+
+// earth : 2014-12-04 Interlink Co., Ltd.
+earth
+
+// eat : 2014-01-23 Charleston Road Registry Inc.
+eat
+
+// eco : 2016-07-08 Big Room Inc.
+eco
+
+// edeka : 2014-12-18 EDEKA Verband kaufmännischer Genossenschaften e.V.
+edeka
+
+// education : 2013-11-07 Binky Moon, LLC
+education
+
+// email : 2013-10-31 Binky Moon, LLC
+email
+
+// emerck : 2014-04-03 Merck KGaA
+emerck
+
+// energy : 2014-09-11 Binky Moon, LLC
+energy
+
+// engineer : 2014-03-06 Dog Beach, LLC
+engineer
+
+// engineering : 2014-03-06 Binky Moon, LLC
+engineering
+
+// enterprises : 2013-09-20 Binky Moon, LLC
+enterprises
+
+// epson : 2014-12-04 Seiko Epson Corporation
+epson
+
+// equipment : 2013-08-27 Binky Moon, LLC
+equipment
+
+// ericsson : 2015-07-09 Telefonaktiebolaget L M Ericsson
+ericsson
+
+// erni : 2014-04-03 ERNI Group Holding AG
+erni
+
+// esq : 2014-05-08 Charleston Road Registry Inc.
+esq
+
+// estate : 2013-08-27 Binky Moon, LLC
+estate
+
+// etisalat : 2015-09-03 Emirates Telecommunications Corporation (trading as Etisalat)
+etisalat
+
+// eurovision : 2014-04-24 European Broadcasting Union (EBU)
+eurovision
+
+// eus : 2013-12-12 Puntueus Fundazioa
+eus
+
+// events : 2013-12-05 Binky Moon, LLC
+events
+
+// exchange : 2014-03-06 Binky Moon, LLC
+exchange
+
+// expert : 2013-11-21 Binky Moon, LLC
+expert
+
+// exposed : 2013-12-05 Binky Moon, LLC
+exposed
+
+// express : 2015-02-11 Binky Moon, LLC
+express
+
+// extraspace : 2015-05-14 Extra Space Storage LLC
+extraspace
+
+// fage : 2014-12-18 Fage International S.A.
+fage
+
+// fail : 2014-03-06 Binky Moon, LLC
+fail
+
+// fairwinds : 2014-11-13 FairWinds Partners, LLC
+fairwinds
+
+// faith : 2014-11-20 dot Faith Limited
+faith
+
+// family : 2015-04-02 Dog Beach, LLC
+family
+
+// fan : 2014-03-06 Dog Beach, LLC
+fan
+
+// fans : 2014-11-07 ZDNS International Limited
+fans
+
+// farm : 2013-11-07 Binky Moon, LLC
+farm
+
+// farmers : 2015-07-09 Farmers Insurance Exchange
+farmers
+
+// fashion : 2014-07-03 Minds + Machines Group Limited
+fashion
+
+// fast : 2014-12-18 Amazon Registry Services, Inc.
+fast
+
+// fedex : 2015-08-06 Federal Express Corporation
+fedex
+
+// feedback : 2013-12-19 Top Level Spectrum, Inc.
+feedback
+
+// ferrari : 2015-07-31 Fiat Chrysler Automobiles N.V.
+ferrari
+
+// ferrero : 2014-12-18 Ferrero Trading Lux S.A.
+ferrero
+
+// fiat : 2015-07-31 Fiat Chrysler Automobiles N.V.
+fiat
+
+// fidelity : 2015-07-30 Fidelity Brokerage Services LLC
+fidelity
+
+// fido : 2015-08-06 Rogers Communications Canada Inc.
+fido
+
+// film : 2015-01-08 Motion Picture Domain Registry Pty Ltd
+film
+
+// final : 2014-10-16 Núcleo de Informação e Coordenação do Ponto BR - NIC.br
+final
+
+// finance : 2014-03-20 Binky Moon, LLC
+finance
+
+// financial : 2014-03-06 Binky Moon, LLC
+financial
+
+// fire : 2015-06-25 Amazon Registry Services, Inc.
+fire
+
+// firestone : 2014-12-18 Bridgestone Licensing Services, Inc
+firestone
+
+// firmdale : 2014-03-27 Firmdale Holdings Limited
+firmdale
+
+// fish : 2013-12-12 Binky Moon, LLC
+fish
+
+// fishing : 2013-11-21 Minds + Machines Group Limited
+fishing
+
+// fit : 2014-11-07 Minds + Machines Group Limited
+fit
+
+// fitness : 2014-03-06 Binky Moon, LLC
+fitness
+
+// flickr : 2015-04-02 Flickr, Inc.
+flickr
+
+// flights : 2013-12-05 Binky Moon, LLC
+flights
+
+// flir : 2015-07-23 FLIR Systems, Inc.
+flir
+
+// florist : 2013-11-07 Binky Moon, LLC
+florist
+
+// flowers : 2014-10-09 UNR Corp.
+flowers
+
+// fly : 2014-05-08 Charleston Road Registry Inc.
+fly
+
+// foo : 2014-01-23 Charleston Road Registry Inc.
+foo
+
+// food : 2016-04-21 Lifestyle Domain Holdings, Inc.
+food
+
+// foodnetwork : 2015-07-02 Lifestyle Domain Holdings, Inc.
+foodnetwork
+
+// football : 2014-12-18 Binky Moon, LLC
+football
+
+// ford : 2014-11-13 Ford Motor Company
+ford
+
+// forex : 2014-12-11 Dotforex Registry Limited
+forex
+
+// forsale : 2014-05-22 Dog Beach, LLC
+forsale
+
+// forum : 2015-04-02 Fegistry, LLC
+forum
+
+// foundation : 2013-12-05 Binky Moon, LLC
+foundation
+
+// fox : 2015-09-11 FOX Registry, LLC
+fox
+
+// free : 2015-12-10 Amazon Registry Services, Inc.
+free
+
+// fresenius : 2015-07-30 Fresenius Immobilien-Verwaltungs-GmbH
+fresenius
+
+// frl : 2014-05-15 FRLregistry B.V.
+frl
+
+// frogans : 2013-12-19 OP3FT
+frogans
+
+// frontdoor : 2015-07-02 Lifestyle Domain Holdings, Inc.
+frontdoor
+
+// frontier : 2015-02-05 Frontier Communications Corporation
+frontier
+
+// ftr : 2015-07-16 Frontier Communications Corporation
+ftr
+
+// fujitsu : 2015-07-30 Fujitsu Limited
+fujitsu
+
+// fujixerox : 2015-07-23 Xerox DNHC LLC
+fujixerox
+
+// fun : 2016-01-14 DotSpace Inc.
+fun
+
+// fund : 2014-03-20 Binky Moon, LLC
+fund
+
+// furniture : 2014-03-20 Binky Moon, LLC
+furniture
+
+// futbol : 2013-09-20 Dog Beach, LLC
+futbol
+
+// fyi : 2015-04-02 Binky Moon, LLC
+fyi
+
+// gal : 2013-11-07 Asociación puntoGAL
+gal
+
+// gallery : 2013-09-13 Binky Moon, LLC
+gallery
+
+// gallo : 2015-06-11 Gallo Vineyards, Inc.
+gallo
+
+// gallup : 2015-02-19 Gallup, Inc.
+gallup
+
+// game : 2015-05-28 UNR Corp.
+game
+
+// games : 2015-05-28 Dog Beach, LLC
+games
+
+// gap : 2015-07-31 The Gap, Inc.
+gap
+
+// garden : 2014-06-26 Minds + Machines Group Limited
+garden
+
+// gay : 2019-05-23 Top Level Design, LLC
+gay
+
+// gbiz : 2014-07-17 Charleston Road Registry Inc.
+gbiz
+
+// gdn : 2014-07-31 Joint Stock Company "Navigation-information systems"
+gdn
+
+// gea : 2014-12-04 GEA Group Aktiengesellschaft
+gea
+
+// gent : 2014-01-23 COMBELL NV
+gent
+
+// genting : 2015-03-12 Resorts World Inc Pte. Ltd.
+genting
+
+// george : 2015-07-31 Wal-Mart Stores, Inc.
+george
+
+// ggee : 2014-01-09 GMO Internet, Inc.
+ggee
+
+// gift : 2013-10-17 DotGift, LLC
+gift
+
+// gifts : 2014-07-03 Binky Moon, LLC
+gifts
+
+// gives : 2014-03-06 Dog Beach, LLC
+gives
+
+// giving : 2014-11-13 Giving Limited
+giving
+
+// glade : 2015-07-23 Johnson Shareholdings, Inc.
+glade
+
+// glass : 2013-11-07 Binky Moon, LLC
+glass
+
+// gle : 2014-07-24 Charleston Road Registry Inc.
+gle
+
+// global : 2014-04-17 Dot Global Domain Registry Limited
+global
+
+// globo : 2013-12-19 Globo Comunicação e Participações S.A
+globo
+
+// gmail : 2014-05-01 Charleston Road Registry Inc.
+gmail
+
+// gmbh : 2016-01-29 Binky Moon, LLC
+gmbh
+
+// gmo : 2014-01-09 GMO Internet, Inc.
+gmo
+
+// gmx : 2014-04-24 1&1 Mail & Media GmbH
+gmx
+
+// godaddy : 2015-07-23 Go Daddy East, LLC
+godaddy
+
+// gold : 2015-01-22 Binky Moon, LLC
+gold
+
+// goldpoint : 2014-11-20 YODOBASHI CAMERA CO.,LTD.
+goldpoint
+
+// golf : 2014-12-18 Binky Moon, LLC
+golf
+
+// goo : 2014-12-18 NTT Resonant Inc.
+goo
+
+// goodyear : 2015-07-02 The Goodyear Tire & Rubber Company
+goodyear
+
+// goog : 2014-11-20 Charleston Road Registry Inc.
+goog
+
+// google : 2014-07-24 Charleston Road Registry Inc.
+google
+
+// gop : 2014-01-16 Republican State Leadership Committee, Inc.
+gop
+
+// got : 2014-12-18 Amazon Registry Services, Inc.
+got
+
+// grainger : 2015-05-07 Grainger Registry Services, LLC
+grainger
+
+// graphics : 2013-09-13 Binky Moon, LLC
+graphics
+
+// gratis : 2014-03-20 Binky Moon, LLC
+gratis
+
+// green : 2014-05-08 Afilias Limited
+green
+
+// gripe : 2014-03-06 Binky Moon, LLC
+gripe
+
+// grocery : 2016-06-16 Wal-Mart Stores, Inc.
+grocery
+
+// group : 2014-08-15 Binky Moon, LLC
+group
+
+// guardian : 2015-07-30 The Guardian Life Insurance Company of America
+guardian
+
+// gucci : 2014-11-13 Guccio Gucci S.p.a.
+gucci
+
+// guge : 2014-08-28 Charleston Road Registry Inc.
+guge
+
+// guide : 2013-09-13 Binky Moon, LLC
+guide
+
+// guitars : 2013-11-14 UNR Corp.
+guitars
+
+// guru : 2013-08-27 Binky Moon, LLC
+guru
+
+// hair : 2015-12-03 XYZ.COM LLC
+hair
+
+// hamburg : 2014-02-20 Hamburg Top-Level-Domain GmbH
+hamburg
+
+// hangout : 2014-11-13 Charleston Road Registry Inc.
+hangout
+
+// haus : 2013-12-05 Dog Beach, LLC
+haus
+
+// hbo : 2015-07-30 HBO Registry Services, Inc.
+hbo
+
+// hdfc : 2015-07-30 HOUSING DEVELOPMENT FINANCE CORPORATION LIMITED
+hdfc
+
+// hdfcbank : 2015-02-12 HDFC Bank Limited
+hdfcbank
+
+// health : 2015-02-11 DotHealth, LLC
+health
+
+// healthcare : 2014-06-12 Binky Moon, LLC
+healthcare
+
+// help : 2014-06-26 UNR Corp.
+help
+
+// helsinki : 2015-02-05 City of Helsinki
+helsinki
+
+// here : 2014-02-06 Charleston Road Registry Inc.
+here
+
+// hermes : 2014-07-10 HERMES INTERNATIONAL
+hermes
+
+// hgtv : 2015-07-02 Lifestyle Domain Holdings, Inc.
+hgtv
+
+// hiphop : 2014-03-06 UNR Corp.
+hiphop
+
+// hisamitsu : 2015-07-16 Hisamitsu Pharmaceutical Co.,Inc.
+hisamitsu
+
+// hitachi : 2014-10-31 Hitachi, Ltd.
+hitachi
+
+// hiv : 2014-03-13 UNR Corp.
+hiv
+
+// hkt : 2015-05-14 PCCW-HKT DataCom Services Limited
+hkt
+
+// hockey : 2015-03-19 Binky Moon, LLC
+hockey
+
+// holdings : 2013-08-27 Binky Moon, LLC
+holdings
+
+// holiday : 2013-11-07 Binky Moon, LLC
+holiday
+
+// homedepot : 2015-04-02 Home Depot Product Authority, LLC
+homedepot
+
+// homegoods : 2015-07-16 The TJX Companies, Inc.
+homegoods
+
+// homes : 2014-01-09 XYZ.COM LLC
+homes
+
+// homesense : 2015-07-16 The TJX Companies, Inc.
+homesense
+
+// honda : 2014-12-18 Honda Motor Co., Ltd.
+honda
+
+// horse : 2013-11-21 Minds + Machines Group Limited
+horse
+
+// hospital : 2016-10-20 Binky Moon, LLC
+hospital
+
+// host : 2014-04-17 DotHost Inc.
+host
+
+// hosting : 2014-05-29 UNR Corp.
+hosting
+
+// hot : 2015-08-27 Amazon Registry Services, Inc.
+hot
+
+// hoteles : 2015-03-05 Travel Reservations SRL
+hoteles
+
+// hotels : 2016-04-07 Booking.com B.V.
+hotels
+
+// hotmail : 2014-12-18 Microsoft Corporation
+hotmail
+
+// house : 2013-11-07 Binky Moon, LLC
+house
+
+// how : 2014-01-23 Charleston Road Registry Inc.
+how
+
+// hsbc : 2014-10-24 HSBC Global Services (UK) Limited
+hsbc
+
+// hughes : 2015-07-30 Hughes Satellite Systems Corporation
+hughes
+
+// hyatt : 2015-07-30 Hyatt GTLD, L.L.C.
+hyatt
+
+// hyundai : 2015-07-09 Hyundai Motor Company
+hyundai
+
+// ibm : 2014-07-31 International Business Machines Corporation
+ibm
+
+// icbc : 2015-02-19 Industrial and Commercial Bank of China Limited
+icbc
+
+// ice : 2014-10-30 IntercontinentalExchange, Inc.
+ice
+
+// icu : 2015-01-08 ShortDot SA
+icu
+
+// ieee : 2015-07-23 IEEE Global LLC
+ieee
+
+// ifm : 2014-01-30 ifm electronic gmbh
+ifm
+
+// ikano : 2015-07-09 Ikano S.A.
+ikano
+
+// imamat : 2015-08-06 Fondation Aga Khan (Aga Khan Foundation)
+imamat
+
+// imdb : 2015-06-25 Amazon Registry Services, Inc.
+imdb
+
+// immo : 2014-07-10 Binky Moon, LLC
+immo
+
+// immobilien : 2013-11-07 Dog Beach, LLC
+immobilien
+
+// inc : 2018-03-10 Intercap Registry Inc.
+inc
+
+// industries : 2013-12-05 Binky Moon, LLC
+industries
+
+// infiniti : 2014-03-27 NISSAN MOTOR CO., LTD.
+infiniti
+
+// ing : 2014-01-23 Charleston Road Registry Inc.
+ing
+
+// ink : 2013-12-05 Top Level Design, LLC
+ink
+
+// institute : 2013-11-07 Binky Moon, LLC
+institute
+
+// insurance : 2015-02-19 fTLD Registry Services LLC
+insurance
+
+// insure : 2014-03-20 Binky Moon, LLC
+insure
+
+// international : 2013-11-07 Binky Moon, LLC
+international
+
+// intuit : 2015-07-30 Intuit Administrative Services, Inc.
+intuit
+
+// investments : 2014-03-20 Binky Moon, LLC
+investments
+
+// ipiranga : 2014-08-28 Ipiranga Produtos de Petroleo S.A.
+ipiranga
+
+// irish : 2014-08-07 Binky Moon, LLC
+irish
+
+// ismaili : 2015-08-06 Fondation Aga Khan (Aga Khan Foundation)
+ismaili
+
+// ist : 2014-08-28 Istanbul Metropolitan Municipality
+ist
+
+// istanbul : 2014-08-28 Istanbul Metropolitan Municipality
+istanbul
+
+// itau : 2014-10-02 Itau Unibanco Holding S.A.
+itau
+
+// itv : 2015-07-09 ITV Services Limited
+itv
+
+// iveco : 2015-09-03 CNH Industrial N.V.
+iveco
+
+// jaguar : 2014-11-13 Jaguar Land Rover Ltd
+jaguar
+
+// java : 2014-06-19 Oracle Corporation
+java
+
+// jcb : 2014-11-20 JCB Co., Ltd.
+jcb
+
+// jeep : 2015-07-30 FCA US LLC.
+jeep
+
+// jetzt : 2014-01-09 Binky Moon, LLC
+jetzt
+
+// jewelry : 2015-03-05 Binky Moon, LLC
+jewelry
+
+// jio : 2015-04-02 Reliance Industries Limited
+jio
+
+// jll : 2015-04-02 Jones Lang LaSalle Incorporated
+jll
+
+// jmp : 2015-03-26 Matrix IP LLC
+jmp
+
+// jnj : 2015-06-18 Johnson & Johnson Services, Inc.
+jnj
+
+// joburg : 2014-03-24 ZA Central Registry NPC trading as ZA Central Registry
+joburg
+
+// jot : 2014-12-18 Amazon Registry Services, Inc.
+jot
+
+// joy : 2014-12-18 Amazon Registry Services, Inc.
+joy
+
+// jpmorgan : 2015-04-30 JPMorgan Chase Bank, National Association
+jpmorgan
+
+// jprs : 2014-09-18 Japan Registry Services Co., Ltd.
+jprs
+
+// juegos : 2014-03-20 UNR Corp.
+juegos
+
+// juniper : 2015-07-30 JUNIPER NETWORKS, INC.
+juniper
+
+// kaufen : 2013-11-07 Dog Beach, LLC
+kaufen
+
+// kddi : 2014-09-12 KDDI CORPORATION
+kddi
+
+// kerryhotels : 2015-04-30 Kerry Trading Co. Limited
+kerryhotels
+
+// kerrylogistics : 2015-04-09 Kerry Trading Co. Limited
+kerrylogistics
+
+// kerryproperties : 2015-04-09 Kerry Trading Co. Limited
+kerryproperties
+
+// kfh : 2014-12-04 Kuwait Finance House
+kfh
+
+// kia : 2015-07-09 KIA MOTORS CORPORATION
+kia
+
+// kim : 2013-09-23 Afilias Limited
+kim
+
+// kinder : 2014-11-07 Ferrero Trading Lux S.A.
+kinder
+
+// kindle : 2015-06-25 Amazon Registry Services, Inc.
+kindle
+
+// kitchen : 2013-09-20 Binky Moon, LLC
+kitchen
+
+// kiwi : 2013-09-20 DOT KIWI LIMITED
+kiwi
+
+// koeln : 2014-01-09 dotKoeln GmbH
+koeln
+
+// komatsu : 2015-01-08 Komatsu Ltd.
+komatsu
+
+// kosher : 2015-08-20 Kosher Marketing Assets LLC
+kosher
+
+// kpmg : 2015-04-23 KPMG International Cooperative (KPMG International Genossenschaft)
+kpmg
+
+// kpn : 2015-01-08 Koninklijke KPN N.V.
+kpn
+
+// krd : 2013-12-05 KRG Department of Information Technology
+krd
+
+// kred : 2013-12-19 KredTLD Pty Ltd
+kred
+
+// kuokgroup : 2015-04-09 Kerry Trading Co. Limited
+kuokgroup
+
+// kyoto : 2014-11-07 Academic Institution: Kyoto Jyoho Gakuen
+kyoto
+
+// lacaixa : 2014-01-09 Fundación Bancaria Caixa d’Estalvis i Pensions de Barcelona, “la Caixa”
+lacaixa
+
+// lamborghini : 2015-06-04 Automobili Lamborghini S.p.A.
+lamborghini
+
+// lamer : 2015-10-01 The Estée Lauder Companies Inc.
+lamer
+
+// lancaster : 2015-02-12 LANCASTER
+lancaster
+
+// lancia : 2015-07-31 Fiat Chrysler Automobiles N.V.
+lancia
+
+// land : 2013-09-10 Binky Moon, LLC
+land
+
+// landrover : 2014-11-13 Jaguar Land Rover Ltd
+landrover
+
+// lanxess : 2015-07-30 LANXESS Corporation
+lanxess
+
+// lasalle : 2015-04-02 Jones Lang LaSalle Incorporated
+lasalle
+
+// lat : 2014-10-16 ECOM-LAC Federaciòn de Latinoamèrica y el Caribe para Internet y el Comercio Electrònico
+lat
+
+// latino : 2015-07-30 Dish DBS Corporation
+latino
+
+// latrobe : 2014-06-16 La Trobe University
+latrobe
+
+// law : 2015-01-22 LW TLD Limited
+law
+
+// lawyer : 2014-03-20 Dog Beach, LLC
+lawyer
+
+// lds : 2014-03-20 IRI Domain Management, LLC
+lds
+
+// lease : 2014-03-06 Binky Moon, LLC
+lease
+
+// leclerc : 2014-08-07 A.C.D. LEC Association des Centres Distributeurs Edouard Leclerc
+leclerc
+
+// lefrak : 2015-07-16 LeFrak Organization, Inc.
+lefrak
+
+// legal : 2014-10-16 Binky Moon, LLC
+legal
+
+// lego : 2015-07-16 LEGO Juris A/S
+lego
+
+// lexus : 2015-04-23 TOYOTA MOTOR CORPORATION
+lexus
+
+// lgbt : 2014-05-08 Afilias Limited
+lgbt
+
+// lidl : 2014-09-18 Schwarz Domains und Services GmbH & Co. KG
+lidl
+
+// life : 2014-02-06 Binky Moon, LLC
+life
+
+// lifeinsurance : 2015-01-15 American Council of Life Insurers
+lifeinsurance
+
+// lifestyle : 2014-12-11 Lifestyle Domain Holdings, Inc.
+lifestyle
+
+// lighting : 2013-08-27 Binky Moon, LLC
+lighting
+
+// like : 2014-12-18 Amazon Registry Services, Inc.
+like
+
+// lilly : 2015-07-31 Eli Lilly and Company
+lilly
+
+// limited : 2014-03-06 Binky Moon, LLC
+limited
+
+// limo : 2013-10-17 Binky Moon, LLC
+limo
+
+// lincoln : 2014-11-13 Ford Motor Company
+lincoln
+
+// linde : 2014-12-04 Linde Aktiengesellschaft
+linde
+
+// link : 2013-11-14 UNR Corp.
+link
+
+// lipsy : 2015-06-25 Lipsy Ltd
+lipsy
+
+// live : 2014-12-04 Dog Beach, LLC
+live
+
+// living : 2015-07-30 Lifestyle Domain Holdings, Inc.
+living
+
+// lixil : 2015-03-19 LIXIL Group Corporation
+lixil
+
+// llc : 2017-12-14 Afilias Limited
+llc
+
+// llp : 2019-08-26 UNR Corp.
+llp
+
+// loan : 2014-11-20 dot Loan Limited
+loan
+
+// loans : 2014-03-20 Binky Moon, LLC
+loans
+
+// locker : 2015-06-04 Dish DBS Corporation
+locker
+
+// locus : 2015-06-25 Locus Analytics LLC
+locus
+
+// loft : 2015-07-30 Annco, Inc.
+loft
+
+// lol : 2015-01-30 UNR Corp.
+lol
+
+// london : 2013-11-14 Dot London Domains Limited
+london
+
+// lotte : 2014-11-07 Lotte Holdings Co., Ltd.
+lotte
+
+// lotto : 2014-04-10 Afilias Limited
+lotto
+
+// love : 2014-12-22 Merchant Law Group LLP
+love
+
+// lpl : 2015-07-30 LPL Holdings, Inc.
+lpl
+
+// lplfinancial : 2015-07-30 LPL Holdings, Inc.
+lplfinancial
+
+// ltd : 2014-09-25 Binky Moon, LLC
+ltd
+
+// ltda : 2014-04-17 InterNetX, Corp
+ltda
+
+// lundbeck : 2015-08-06 H. Lundbeck A/S
+lundbeck
+
+// luxe : 2014-01-09 Minds + Machines Group Limited
+luxe
+
+// luxury : 2013-10-17 Luxury Partners, LLC
+luxury
+
+// macys : 2015-07-31 Macys, Inc.
+macys
+
+// madrid : 2014-05-01 Comunidad de Madrid
+madrid
+
+// maif : 2014-10-02 Mutuelle Assurance Instituteur France (MAIF)
+maif
+
+// maison : 2013-12-05 Binky Moon, LLC
+maison
+
+// makeup : 2015-01-15 XYZ.COM LLC
+makeup
+
+// man : 2014-12-04 MAN SE
+man
+
+// management : 2013-11-07 Binky Moon, LLC
+management
+
+// mango : 2013-10-24 PUNTO FA S.L.
+mango
+
+// map : 2016-06-09 Charleston Road Registry Inc.
+map
+
+// market : 2014-03-06 Dog Beach, LLC
+market
+
+// marketing : 2013-11-07 Binky Moon, LLC
+marketing
+
+// markets : 2014-12-11 Dotmarkets Registry Limited
+markets
+
+// marriott : 2014-10-09 Marriott Worldwide Corporation
+marriott
+
+// marshalls : 2015-07-16 The TJX Companies, Inc.
+marshalls
+
+// maserati : 2015-07-31 Fiat Chrysler Automobiles N.V.
+maserati
+
+// mattel : 2015-08-06 Mattel Sites, Inc.
+mattel
+
+// mba : 2015-04-02 Binky Moon, LLC
+mba
+
+// mckinsey : 2015-07-31 McKinsey Holdings, Inc.
+mckinsey
+
+// med : 2015-08-06 Medistry LLC
+med
+
+// media : 2014-03-06 Binky Moon, LLC
+media
+
+// meet : 2014-01-16 Charleston Road Registry Inc.
+meet
+
+// melbourne : 2014-05-29 The Crown in right of the State of Victoria, represented by its Department of State Development, Business and Innovation
+melbourne
+
+// meme : 2014-01-30 Charleston Road Registry Inc.
+meme
+
+// memorial : 2014-10-16 Dog Beach, LLC
+memorial
+
+// men : 2015-02-26 Exclusive Registry Limited
+men
+
+// menu : 2013-09-11 Dot Menu Registry, LLC
+menu
+
+// merckmsd : 2016-07-14 MSD Registry Holdings, Inc.
+merckmsd
+
+// miami : 2013-12-19 Minds + Machines Group Limited
+miami
+
+// microsoft : 2014-12-18 Microsoft Corporation
+microsoft
+
+// mini : 2014-01-09 Bayerische Motoren Werke Aktiengesellschaft
+mini
+
+// mint : 2015-07-30 Intuit Administrative Services, Inc.
+mint
+
+// mit : 2015-07-02 Massachusetts Institute of Technology
+mit
+
+// mitsubishi : 2015-07-23 Mitsubishi Corporation
+mitsubishi
+
+// mlb : 2015-05-21 MLB Advanced Media DH, LLC
+mlb
+
+// mls : 2015-04-23 The Canadian Real Estate Association
+mls
+
+// mma : 2014-11-07 MMA IARD
+mma
+
+// mobile : 2016-06-02 Dish DBS Corporation
+mobile
+
+// moda : 2013-11-07 Dog Beach, LLC
+moda
+
+// moe : 2013-11-13 Interlink Co., Ltd.
+moe
+
+// moi : 2014-12-18 Amazon Registry Services, Inc.
+moi
+
+// mom : 2015-04-16 UNR Corp.
+mom
+
+// monash : 2013-09-30 Monash University
+monash
+
+// money : 2014-10-16 Binky Moon, LLC
+money
+
+// monster : 2015-09-11 XYZ.COM LLC
+monster
+
+// mormon : 2013-12-05 IRI Domain Management, LLC
+mormon
+
+// mortgage : 2014-03-20 Dog Beach, LLC
+mortgage
+
+// moscow : 2013-12-19 Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID)
+moscow
+
+// moto : 2015-06-04 Motorola Trademark Holdings, LLC
+moto
+
+// motorcycles : 2014-01-09 XYZ.COM LLC
+motorcycles
+
+// mov : 2014-01-30 Charleston Road Registry Inc.
+mov
+
+// movie : 2015-02-05 Binky Moon, LLC
+movie
+
+// msd : 2015-07-23 MSD Registry Holdings, Inc.
+msd
+
+// mtn : 2014-12-04 MTN Dubai Limited
+mtn
+
+// mtr : 2015-03-12 MTR Corporation Limited
+mtr
+
+// mutual : 2015-04-02 Northwestern Mutual MU TLD Registry, LLC
+mutual
+
+// nab : 2015-08-20 National Australia Bank Limited
+nab
+
+// nagoya : 2013-10-24 GMO Registry, Inc.
+nagoya
+
+// nationwide : 2015-07-23 Nationwide Mutual Insurance Company
+nationwide
+
+// natura : 2015-03-12 NATURA COSMÉTICOS S.A.
+natura
+
+// navy : 2014-03-06 Dog Beach, LLC
+navy
+
+// nba : 2015-07-31 NBA REGISTRY, LLC
+nba
+
+// nec : 2015-01-08 NEC Corporation
+nec
+
+// netbank : 2014-06-26 COMMONWEALTH BANK OF AUSTRALIA
+netbank
+
+// netflix : 2015-06-18 Netflix, Inc.
+netflix
+
+// network : 2013-11-14 Binky Moon, LLC
+network
+
+// neustar : 2013-12-05 NeuStar, Inc.
+neustar
+
+// new : 2014-01-30 Charleston Road Registry Inc.
+new
+
+// newholland : 2015-09-03 CNH Industrial N.V.
+newholland
+
+// news : 2014-12-18 Dog Beach, LLC
+news
+
+// next : 2015-06-18 Next plc
+next
+
+// nextdirect : 2015-06-18 Next plc
+nextdirect
+
+// nexus : 2014-07-24 Charleston Road Registry Inc.
+nexus
+
+// nfl : 2015-07-23 NFL Reg Ops LLC
+nfl
+
+// ngo : 2014-03-06 Public Interest Registry
+ngo
+
+// nhk : 2014-02-13 Japan Broadcasting Corporation (NHK)
+nhk
+
+// nico : 2014-12-04 DWANGO Co., Ltd.
+nico
+
+// nike : 2015-07-23 NIKE, Inc.
+nike
+
+// nikon : 2015-05-21 NIKON CORPORATION
+nikon
+
+// ninja : 2013-11-07 Dog Beach, LLC
+ninja
+
+// nissan : 2014-03-27 NISSAN MOTOR CO., LTD.
+nissan
+
+// nissay : 2015-10-29 Nippon Life Insurance Company
+nissay
+
+// nokia : 2015-01-08 Nokia Corporation
+nokia
+
+// northwesternmutual : 2015-06-18 Northwestern Mutual Registry, LLC
+northwesternmutual
+
+// norton : 2014-12-04 NortonLifeLock Inc.
+norton
+
+// now : 2015-06-25 Amazon Registry Services, Inc.
+now
+
+// nowruz : 2014-09-04 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
+nowruz
+
+// nowtv : 2015-05-14 Starbucks (HK) Limited
+nowtv
+
+// nra : 2014-05-22 NRA Holdings Company, INC.
+nra
+
+// nrw : 2013-11-21 Minds + Machines GmbH
+nrw
+
+// ntt : 2014-10-31 NIPPON TELEGRAPH AND TELEPHONE CORPORATION
+ntt
+
+// nyc : 2014-01-23 The City of New York by and through the New York City Department of Information Technology & Telecommunications
+nyc
+
+// obi : 2014-09-25 OBI Group Holding SE & Co. KGaA
+obi
+
+// observer : 2015-04-30 Dog Beach, LLC
+observer
+
+// off : 2015-07-23 Johnson Shareholdings, Inc.
+off
+
+// office : 2015-03-12 Microsoft Corporation
+office
+
+// okinawa : 2013-12-05 BRregistry, Inc.
+okinawa
+
+// olayan : 2015-05-14 Crescent Holding GmbH
+olayan
+
+// olayangroup : 2015-05-14 Crescent Holding GmbH
+olayangroup
+
+// oldnavy : 2015-07-31 The Gap, Inc.
+oldnavy
+
+// ollo : 2015-06-04 Dish DBS Corporation
+ollo
+
+// omega : 2015-01-08 The Swatch Group Ltd
+omega
+
+// one : 2014-11-07 One.com A/S
+one
+
+// ong : 2014-03-06 Public Interest Registry
+ong
+
+// onl : 2013-09-16 iRegistry GmbH
+onl
+
+// online : 2015-01-15 DotOnline Inc.
+online
+
+// onyourside : 2015-07-23 Nationwide Mutual Insurance Company
+onyourside
+
+// ooo : 2014-01-09 INFIBEAM AVENUES LIMITED
+ooo
+
+// open : 2015-07-31 American Express Travel Related Services Company, Inc.
+open
+
+// oracle : 2014-06-19 Oracle Corporation
+oracle
+
+// orange : 2015-03-12 Orange Brand Services Limited
+orange
+
+// organic : 2014-03-27 Afilias Limited
+organic
+
+// origins : 2015-10-01 The Estée Lauder Companies Inc.
+origins
+
+// osaka : 2014-09-04 Osaka Registry Co., Ltd.
+osaka
+
+// otsuka : 2013-10-11 Otsuka Holdings Co., Ltd.
+otsuka
+
+// ott : 2015-06-04 Dish DBS Corporation
+ott
+
+// ovh : 2014-01-16 MédiaBC
+ovh
+
+// page : 2014-12-04 Charleston Road Registry Inc.
+page
+
+// panasonic : 2015-07-30 Panasonic Corporation
+panasonic
+
+// paris : 2014-01-30 City of Paris
+paris
+
+// pars : 2014-09-04 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
+pars
+
+// partners : 2013-12-05 Binky Moon, LLC
+partners
+
+// parts : 2013-12-05 Binky Moon, LLC
+parts
+
+// party : 2014-09-11 Blue Sky Registry Limited
+party
+
+// passagens : 2015-03-05 Travel Reservations SRL
+passagens
+
+// pay : 2015-08-27 Amazon Registry Services, Inc.
+pay
+
+// pccw : 2015-05-14 PCCW Enterprises Limited
+pccw
+
+// pet : 2015-05-07 Afilias Limited
+pet
+
+// pfizer : 2015-09-11 Pfizer Inc.
+pfizer
+
+// pharmacy : 2014-06-19 National Association of Boards of Pharmacy
+pharmacy
+
+// phd : 2016-07-28 Charleston Road Registry Inc.
+phd
+
+// philips : 2014-11-07 Koninklijke Philips N.V.
+philips
+
+// phone : 2016-06-02 Dish DBS Corporation
+phone
+
+// photo : 2013-11-14 UNR Corp.
+photo
+
+// photography : 2013-09-20 Binky Moon, LLC
+photography
+
+// photos : 2013-10-17 Binky Moon, LLC
+photos
+
+// physio : 2014-05-01 PhysBiz Pty Ltd
+physio
+
+// pics : 2013-11-14 UNR Corp.
+pics
+
+// pictet : 2014-06-26 Pictet Europe S.A.
+pictet
+
+// pictures : 2014-03-06 Binky Moon, LLC
+pictures
+
+// pid : 2015-01-08 Top Level Spectrum, Inc.
+pid
+
+// pin : 2014-12-18 Amazon Registry Services, Inc.
+pin
+
+// ping : 2015-06-11 Ping Registry Provider, Inc.
+ping
+
+// pink : 2013-10-01 Afilias Limited
+pink
+
+// pioneer : 2015-07-16 Pioneer Corporation
+pioneer
+
+// pizza : 2014-06-26 Binky Moon, LLC
+pizza
+
+// place : 2014-04-24 Binky Moon, LLC
+place
+
+// play : 2015-03-05 Charleston Road Registry Inc.
+play
+
+// playstation : 2015-07-02 Sony Interactive Entertainment Inc.
+playstation
+
+// plumbing : 2013-09-10 Binky Moon, LLC
+plumbing
+
+// plus : 2015-02-05 Binky Moon, LLC
+plus
+
+// pnc : 2015-07-02 PNC Domain Co., LLC
+pnc
+
+// pohl : 2014-06-23 Deutsche Vermögensberatung Aktiengesellschaft DVAG
+pohl
+
+// poker : 2014-07-03 Afilias Limited
+poker
+
+// politie : 2015-08-20 Politie Nederland
+politie
+
+// porn : 2014-10-16 ICM Registry PN LLC
+porn
+
+// pramerica : 2015-07-30 Prudential Financial, Inc.
+pramerica
+
+// praxi : 2013-12-05 Praxi S.p.A.
+praxi
+
+// press : 2014-04-03 DotPress Inc.
+press
+
+// prime : 2015-06-25 Amazon Registry Services, Inc.
+prime
+
+// prod : 2014-01-23 Charleston Road Registry Inc.
+prod
+
+// productions : 2013-12-05 Binky Moon, LLC
+productions
+
+// prof : 2014-07-24 Charleston Road Registry Inc.
+prof
+
+// progressive : 2015-07-23 Progressive Casualty Insurance Company
+progressive
+
+// promo : 2014-12-18 Afilias Limited
+promo
+
+// properties : 2013-12-05 Binky Moon, LLC
+properties
+
+// property : 2014-05-22 UNR Corp.
+property
+
+// protection : 2015-04-23 XYZ.COM LLC
+protection
+
+// pru : 2015-07-30 Prudential Financial, Inc.
+pru
+
+// prudential : 2015-07-30 Prudential Financial, Inc.
+prudential
+
+// pub : 2013-12-12 Dog Beach, LLC
+pub
+
+// pwc : 2015-10-29 PricewaterhouseCoopers LLP
+pwc
+
+// qpon : 2013-11-14 dotCOOL, Inc.
+qpon
+
+// quebec : 2013-12-19 PointQuébec Inc
+quebec
+
+// quest : 2015-03-26 XYZ.COM LLC
+quest
+
+// qvc : 2015-07-30 QVC, Inc.
+qvc
+
+// racing : 2014-12-04 Premier Registry Limited
+racing
+
+// radio : 2016-07-21 European Broadcasting Union (EBU)
+radio
+
+// raid : 2015-07-23 Johnson Shareholdings, Inc.
+raid
+
+// read : 2014-12-18 Amazon Registry Services, Inc.
+read
+
+// realestate : 2015-09-11 dotRealEstate LLC
+realestate
+
+// realtor : 2014-05-29 Real Estate Domains LLC
+realtor
+
+// realty : 2015-03-19 Dog Beach, LLC
+realty
+
+// recipes : 2013-10-17 Binky Moon, LLC
+recipes
+
+// red : 2013-11-07 Afilias Limited
+red
+
+// redstone : 2014-10-31 Redstone Haute Couture Co., Ltd.
+redstone
+
+// redumbrella : 2015-03-26 Travelers TLD, LLC
+redumbrella
+
+// rehab : 2014-03-06 Dog Beach, LLC
+rehab
+
+// reise : 2014-03-13 Binky Moon, LLC
+reise
+
+// reisen : 2014-03-06 Binky Moon, LLC
+reisen
+
+// reit : 2014-09-04 National Association of Real Estate Investment Trusts, Inc.
+reit
+
+// reliance : 2015-04-02 Reliance Industries Limited
+reliance
+
+// ren : 2013-12-12 ZDNS International Limited
+ren
+
+// rent : 2014-12-04 XYZ.COM LLC
+rent
+
+// rentals : 2013-12-05 Binky Moon, LLC
+rentals
+
+// repair : 2013-11-07 Binky Moon, LLC
+repair
+
+// report : 2013-12-05 Binky Moon, LLC
+report
+
+// republican : 2014-03-20 Dog Beach, LLC
+republican
+
+// rest : 2013-12-19 Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable
+rest
+
+// restaurant : 2014-07-03 Binky Moon, LLC
+restaurant
+
+// review : 2014-11-20 dot Review Limited
+review
+
+// reviews : 2013-09-13 Dog Beach, LLC
+reviews
+
+// rexroth : 2015-06-18 Robert Bosch GMBH
+rexroth
+
+// rich : 2013-11-21 iRegistry GmbH
+rich
+
+// richardli : 2015-05-14 Pacific Century Asset Management (HK) Limited
+richardli
+
+// ricoh : 2014-11-20 Ricoh Company, Ltd.
+ricoh
+
+// ril : 2015-04-02 Reliance Industries Limited
+ril
+
+// rio : 2014-02-27 Empresa Municipal de Informática SA - IPLANRIO
+rio
+
+// rip : 2014-07-10 Dog Beach, LLC
+rip
+
+// rmit : 2015-11-19 Royal Melbourne Institute of Technology
+rmit
+
+// rocher : 2014-12-18 Ferrero Trading Lux S.A.
+rocher
+
+// rocks : 2013-11-14 Dog Beach, LLC
+rocks
+
+// rodeo : 2013-12-19 Minds + Machines Group Limited
+rodeo
+
+// rogers : 2015-08-06 Rogers Communications Canada Inc.
+rogers
+
+// room : 2014-12-18 Amazon Registry Services, Inc.
+room
+
+// rsvp : 2014-05-08 Charleston Road Registry Inc.
+rsvp
+
+// rugby : 2016-12-15 World Rugby Strategic Developments Limited
+rugby
+
+// ruhr : 2013-10-02 regiodot GmbH & Co. KG
+ruhr
+
+// run : 2015-03-19 Binky Moon, LLC
+run
+
+// rwe : 2015-04-02 RWE AG
+rwe
+
+// ryukyu : 2014-01-09 BRregistry, Inc.
+ryukyu
+
+// saarland : 2013-12-12 dotSaarland GmbH
+saarland
+
+// safe : 2014-12-18 Amazon Registry Services, Inc.
+safe
+
+// safety : 2015-01-08 Safety Registry Services, LLC.
+safety
+
+// sakura : 2014-12-18 SAKURA Internet Inc.
+sakura
+
+// sale : 2014-10-16 Dog Beach, LLC
+sale
+
+// salon : 2014-12-11 Binky Moon, LLC
+salon
+
+// samsclub : 2015-07-31 Wal-Mart Stores, Inc.
+samsclub
+
+// samsung : 2014-04-03 SAMSUNG SDS CO., LTD
+samsung
+
+// sandvik : 2014-11-13 Sandvik AB
+sandvik
+
+// sandvikcoromant : 2014-11-07 Sandvik AB
+sandvikcoromant
+
+// sanofi : 2014-10-09 Sanofi
+sanofi
+
+// sap : 2014-03-27 SAP AG
+sap
+
+// sarl : 2014-07-03 Binky Moon, LLC
+sarl
+
+// sas : 2015-04-02 Research IP LLC
+sas
+
+// save : 2015-06-25 Amazon Registry Services, Inc.
+save
+
+// saxo : 2014-10-31 Saxo Bank A/S
+saxo
+
+// sbi : 2015-03-12 STATE BANK OF INDIA
+sbi
+
+// sbs : 2014-11-07 SPECIAL BROADCASTING SERVICE CORPORATION
+sbs
+
+// sca : 2014-03-13 SVENSKA CELLULOSA AKTIEBOLAGET SCA (publ)
+sca
+
+// scb : 2014-02-20 The Siam Commercial Bank Public Company Limited ("SCB")
+scb
+
+// schaeffler : 2015-08-06 Schaeffler Technologies AG & Co. KG
+schaeffler
+
+// schmidt : 2014-04-03 SCHMIDT GROUPE S.A.S.
+schmidt
+
+// scholarships : 2014-04-24 Scholarships.com, LLC
+scholarships
+
+// school : 2014-12-18 Binky Moon, LLC
+school
+
+// schule : 2014-03-06 Binky Moon, LLC
+schule
+
+// schwarz : 2014-09-18 Schwarz Domains und Services GmbH & Co. KG
+schwarz
+
+// science : 2014-09-11 dot Science Limited
+science
+
+// scjohnson : 2015-07-23 Johnson Shareholdings, Inc.
+scjohnson
+
+// scot : 2014-01-23 Dot Scot Registry Limited
+scot
+
+// search : 2016-06-09 Charleston Road Registry Inc.
+search
+
+// seat : 2014-05-22 SEAT, S.A. (Sociedad Unipersonal)
+seat
+
+// secure : 2015-08-27 Amazon Registry Services, Inc.
+secure
+
+// security : 2015-05-14 XYZ.COM LLC
+security
+
+// seek : 2014-12-04 Seek Limited
+seek
+
+// select : 2015-10-08 Registry Services, LLC
+select
+
+// sener : 2014-10-24 Sener Ingeniería y Sistemas, S.A.
+sener
+
+// services : 2014-02-27 Binky Moon, LLC
+services
+
+// ses : 2015-07-23 SES
+ses
+
+// seven : 2015-08-06 Seven West Media Ltd
+seven
+
+// sew : 2014-07-17 SEW-EURODRIVE GmbH & Co KG
+sew
+
+// sex : 2014-11-13 ICM Registry SX LLC
+sex
+
+// sexy : 2013-09-11 UNR Corp.
+sexy
+
+// sfr : 2015-08-13 Societe Francaise du Radiotelephone - SFR
+sfr
+
+// shangrila : 2015-09-03 Shangri‐La International Hotel Management Limited
+shangrila
+
+// sharp : 2014-05-01 Sharp Corporation
+sharp
+
+// shaw : 2015-04-23 Shaw Cablesystems G.P.
+shaw
+
+// shell : 2015-07-30 Shell Information Technology International Inc
+shell
+
+// shia : 2014-09-04 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
+shia
+
+// shiksha : 2013-11-14 Afilias Limited
+shiksha
+
+// shoes : 2013-10-02 Binky Moon, LLC
+shoes
+
+// shop : 2016-04-08 GMO Registry, Inc.
+shop
+
+// shopping : 2016-03-31 Binky Moon, LLC
+shopping
+
+// shouji : 2015-01-08 Beijing Qihu Keji Co., Ltd.
+shouji
+
+// show : 2015-03-05 Binky Moon, LLC
+show
+
+// showtime : 2015-08-06 CBS Domains Inc.
+showtime
+
+// silk : 2015-06-25 Amazon Registry Services, Inc.
+silk
+
+// sina : 2015-03-12 Sina Corporation
+sina
+
+// singles : 2013-08-27 Binky Moon, LLC
+singles
+
+// site : 2015-01-15 DotSite Inc.
+site
+
+// ski : 2015-04-09 Afilias Limited
+ski
+
+// skin : 2015-01-15 XYZ.COM LLC
+skin
+
+// sky : 2014-06-19 Sky International AG
+sky
+
+// skype : 2014-12-18 Microsoft Corporation
+skype
+
+// sling : 2015-07-30 DISH Technologies L.L.C.
+sling
+
+// smart : 2015-07-09 Smart Communications, Inc. (SMART)
+smart
+
+// smile : 2014-12-18 Amazon Registry Services, Inc.
+smile
+
+// sncf : 2015-02-19 Société Nationale des Chemins de fer Francais S N C F
+sncf
+
+// soccer : 2015-03-26 Binky Moon, LLC
+soccer
+
+// social : 2013-11-07 Dog Beach, LLC
+social
+
+// softbank : 2015-07-02 SoftBank Group Corp.
+softbank
+
+// software : 2014-03-20 Dog Beach, LLC
+software
+
+// sohu : 2013-12-19 Sohu.com Limited
+sohu
+
+// solar : 2013-11-07 Binky Moon, LLC
+solar
+
+// solutions : 2013-11-07 Binky Moon, LLC
+solutions
+
+// song : 2015-02-26 Amazon Registry Services, Inc.
+song
+
+// sony : 2015-01-08 Sony Corporation
+sony
+
+// soy : 2014-01-23 Charleston Road Registry Inc.
+soy
+
+// spa : 2019-09-19 Asia Spa and Wellness Promotion Council Limited
+spa
+
+// space : 2014-04-03 DotSpace Inc.
+space
+
+// sport : 2017-11-16 Global Association of International Sports Federations (GAISF)
+sport
+
+// spot : 2015-02-26 Amazon Registry Services, Inc.
+spot
+
+// spreadbetting : 2014-12-11 Dotspreadbetting Registry Limited
+spreadbetting
+
+// srl : 2015-05-07 InterNetX, Corp
+srl
+
+// stada : 2014-11-13 STADA Arzneimittel AG
+stada
+
+// staples : 2015-07-30 Staples, Inc.
+staples
+
+// star : 2015-01-08 Star India Private Limited
+star
+
+// statebank : 2015-03-12 STATE BANK OF INDIA
+statebank
+
+// statefarm : 2015-07-30 State Farm Mutual Automobile Insurance Company
+statefarm
+
+// stc : 2014-10-09 Saudi Telecom Company
+stc
+
+// stcgroup : 2014-10-09 Saudi Telecom Company
+stcgroup
+
+// stockholm : 2014-12-18 Stockholms kommun
+stockholm
+
+// storage : 2014-12-22 XYZ.COM LLC
+storage
+
+// store : 2015-04-09 DotStore Inc.
+store
+
+// stream : 2016-01-08 dot Stream Limited
+stream
+
+// studio : 2015-02-11 Dog Beach, LLC
+studio
+
+// study : 2014-12-11 OPEN UNIVERSITIES AUSTRALIA PTY LTD
+study
+
+// style : 2014-12-04 Binky Moon, LLC
+style
+
+// sucks : 2014-12-22 Vox Populi Registry Ltd.
+sucks
+
+// supplies : 2013-12-19 Binky Moon, LLC
+supplies
+
+// supply : 2013-12-19 Binky Moon, LLC
+supply
+
+// support : 2013-10-24 Binky Moon, LLC
+support
+
+// surf : 2014-01-09 Minds + Machines Group Limited
+surf
+
+// surgery : 2014-03-20 Binky Moon, LLC
+surgery
+
+// suzuki : 2014-02-20 SUZUKI MOTOR CORPORATION
+suzuki
+
+// swatch : 2015-01-08 The Swatch Group Ltd
+swatch
+
+// swiftcover : 2015-07-23 Swiftcover Insurance Services Limited
+swiftcover
+
+// swiss : 2014-10-16 Swiss Confederation
+swiss
+
+// sydney : 2014-09-18 State of New South Wales, Department of Premier and Cabinet
+sydney
+
+// systems : 2013-11-07 Binky Moon, LLC
+systems
+
+// tab : 2014-12-04 Tabcorp Holdings Limited
+tab
+
+// taipei : 2014-07-10 Taipei City Government
+taipei
+
+// talk : 2015-04-09 Amazon Registry Services, Inc.
+talk
+
+// taobao : 2015-01-15 Alibaba Group Holding Limited
+taobao
+
+// target : 2015-07-31 Target Domain Holdings, LLC
+target
+
+// tatamotors : 2015-03-12 Tata Motors Ltd
+tatamotors
+
+// tatar : 2014-04-24 Limited Liability Company "Coordination Center of Regional Domain of Tatarstan Republic"
+tatar
+
+// tattoo : 2013-08-30 UNR Corp.
+tattoo
+
+// tax : 2014-03-20 Binky Moon, LLC
+tax
+
+// taxi : 2015-03-19 Binky Moon, LLC
+taxi
+
+// tci : 2014-09-12 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
+tci
+
+// tdk : 2015-06-11 TDK Corporation
+tdk
+
+// team : 2015-03-05 Binky Moon, LLC
+team
+
+// tech : 2015-01-30 Personals TLD Inc.
+tech
+
+// technology : 2013-09-13 Binky Moon, LLC
+technology
+
+// temasek : 2014-08-07 Temasek Holdings (Private) Limited
+temasek
+
+// tennis : 2014-12-04 Binky Moon, LLC
+tennis
+
+// teva : 2015-07-02 Teva Pharmaceutical Industries Limited
+teva
+
+// thd : 2015-04-02 Home Depot Product Authority, LLC
+thd
+
+// theater : 2015-03-19 Binky Moon, LLC
+theater
+
+// theatre : 2015-05-07 XYZ.COM LLC
+theatre
+
+// tiaa : 2015-07-23 Teachers Insurance and Annuity Association of America
+tiaa
+
+// tickets : 2015-02-05 Accent Media Limited
+tickets
+
+// tienda : 2013-11-14 Binky Moon, LLC
+tienda
+
+// tiffany : 2015-01-30 Tiffany and Company
+tiffany
+
+// tips : 2013-09-20 Binky Moon, LLC
+tips
+
+// tires : 2014-11-07 Binky Moon, LLC
+tires
+
+// tirol : 2014-04-24 punkt Tirol GmbH
+tirol
+
+// tjmaxx : 2015-07-16 The TJX Companies, Inc.
+tjmaxx
+
+// tjx : 2015-07-16 The TJX Companies, Inc.
+tjx
+
+// tkmaxx : 2015-07-16 The TJX Companies, Inc.
+tkmaxx
+
+// tmall : 2015-01-15 Alibaba Group Holding Limited
+tmall
+
+// today : 2013-09-20 Binky Moon, LLC
+today
+
+// tokyo : 2013-11-13 GMO Registry, Inc.
+tokyo
+
+// tools : 2013-11-21 Binky Moon, LLC
+tools
+
+// top : 2014-03-20 .TOP Registry
+top
+
+// toray : 2014-12-18 Toray Industries, Inc.
+toray
+
+// toshiba : 2014-04-10 TOSHIBA Corporation
+toshiba
+
+// total : 2015-08-06 Total SA
+total
+
+// tours : 2015-01-22 Binky Moon, LLC
+tours
+
+// town : 2014-03-06 Binky Moon, LLC
+town
+
+// toyota : 2015-04-23 TOYOTA MOTOR CORPORATION
+toyota
+
+// toys : 2014-03-06 Binky Moon, LLC
+toys
+
+// trade : 2014-01-23 Elite Registry Limited
+trade
+
+// trading : 2014-12-11 Dottrading Registry Limited
+trading
+
+// training : 2013-11-07 Binky Moon, LLC
+training
+
+// travel : 2015-10-09 Dog Beach, LLC
+travel
+
+// travelchannel : 2015-07-02 Lifestyle Domain Holdings, Inc.
+travelchannel
+
+// travelers : 2015-03-26 Travelers TLD, LLC
+travelers
+
+// travelersinsurance : 2015-03-26 Travelers TLD, LLC
+travelersinsurance
+
+// trust : 2014-10-16 UNR Corp.
+trust
+
+// trv : 2015-03-26 Travelers TLD, LLC
+trv
+
+// tube : 2015-06-11 Latin American Telecom LLC
+tube
+
+// tui : 2014-07-03 TUI AG
+tui
+
+// tunes : 2015-02-26 Amazon Registry Services, Inc.
+tunes
+
+// tushu : 2014-12-18 Amazon Registry Services, Inc.
+tushu
+
+// tvs : 2015-02-19 T V SUNDRAM IYENGAR & SONS LIMITED
+tvs
+
+// ubank : 2015-08-20 National Australia Bank Limited
+ubank
+
+// ubs : 2014-12-11 UBS AG
+ubs
+
+// unicom : 2015-10-15 China United Network Communications Corporation Limited
+unicom
+
+// university : 2014-03-06 Binky Moon, LLC
+university
+
+// uno : 2013-09-11 DotSite Inc.
+uno
+
+// uol : 2014-05-01 UBN INTERNET LTDA.
+uol
+
+// ups : 2015-06-25 UPS Market Driver, Inc.
+ups
+
+// vacations : 2013-12-05 Binky Moon, LLC
+vacations
+
+// vana : 2014-12-11 Lifestyle Domain Holdings, Inc.
+vana
+
+// vanguard : 2015-09-03 The Vanguard Group, Inc.
+vanguard
+
+// vegas : 2014-01-16 Dot Vegas, Inc.
+vegas
+
+// ventures : 2013-08-27 Binky Moon, LLC
+ventures
+
+// verisign : 2015-08-13 VeriSign, Inc.
+verisign
+
+// versicherung : 2014-03-20 tldbox GmbH
+versicherung
+
+// vet : 2014-03-06 Dog Beach, LLC
+vet
+
+// viajes : 2013-10-17 Binky Moon, LLC
+viajes
+
+// video : 2014-10-16 Dog Beach, LLC
+video
+
+// vig : 2015-05-14 VIENNA INSURANCE GROUP AG Wiener Versicherung Gruppe
+vig
+
+// viking : 2015-04-02 Viking River Cruises (Bermuda) Ltd.
+viking
+
+// villas : 2013-12-05 Binky Moon, LLC
+villas
+
+// vin : 2015-06-18 Binky Moon, LLC
+vin
+
+// vip : 2015-01-22 Minds + Machines Group Limited
+vip
+
+// virgin : 2014-09-25 Virgin Enterprises Limited
+virgin
+
+// visa : 2015-07-30 Visa Worldwide Pte. Limited
+visa
+
+// vision : 2013-12-05 Binky Moon, LLC
+vision
+
+// viva : 2014-11-07 Saudi Telecom Company
+viva
+
+// vivo : 2015-07-31 Telefonica Brasil S.A.
+vivo
+
+// vlaanderen : 2014-02-06 DNS.be vzw
+vlaanderen
+
+// vodka : 2013-12-19 Minds + Machines Group Limited
+vodka
+
+// volkswagen : 2015-05-14 Volkswagen Group of America Inc.
+volkswagen
+
+// volvo : 2015-11-12 Volvo Holding Sverige Aktiebolag
+volvo
+
+// vote : 2013-11-21 Monolith Registry LLC
+vote
+
+// voting : 2013-11-13 Valuetainment Corp.
+voting
+
+// voto : 2013-11-21 Monolith Registry LLC
+voto
+
+// voyage : 2013-08-27 Binky Moon, LLC
+voyage
+
+// vuelos : 2015-03-05 Travel Reservations SRL
+vuelos
+
+// wales : 2014-05-08 Nominet UK
+wales
+
+// walmart : 2015-07-31 Wal-Mart Stores, Inc.
+walmart
+
+// walter : 2014-11-13 Sandvik AB
+walter
+
+// wang : 2013-10-24 Zodiac Wang Limited
+wang
+
+// wanggou : 2014-12-18 Amazon Registry Services, Inc.
+wanggou
+
+// watch : 2013-11-14 Binky Moon, LLC
+watch
+
+// watches : 2014-12-22 Richemont DNS Inc.
+watches
+
+// weather : 2015-01-08 International Business Machines Corporation
+weather
+
+// weatherchannel : 2015-03-12 International Business Machines Corporation
+weatherchannel
+
+// webcam : 2014-01-23 dot Webcam Limited
+webcam
+
+// weber : 2015-06-04 Saint-Gobain Weber SA
+weber
+
+// website : 2014-04-03 DotWebsite Inc.
+website
+
+// wedding : 2014-04-24 Minds + Machines Group Limited
+wedding
+
+// weibo : 2015-03-05 Sina Corporation
+weibo
+
+// weir : 2015-01-29 Weir Group IP Limited
+weir
+
+// whoswho : 2014-02-20 Who's Who Registry
+whoswho
+
+// wien : 2013-10-28 punkt.wien GmbH
+wien
+
+// wiki : 2013-11-07 Top Level Design, LLC
+wiki
+
+// williamhill : 2014-03-13 William Hill Organization Limited
+williamhill
+
+// win : 2014-11-20 First Registry Limited
+win
+
+// windows : 2014-12-18 Microsoft Corporation
+windows
+
+// wine : 2015-06-18 Binky Moon, LLC
+wine
+
+// winners : 2015-07-16 The TJX Companies, Inc.
+winners
+
+// wme : 2014-02-13 William Morris Endeavor Entertainment, LLC
+wme
+
+// wolterskluwer : 2015-08-06 Wolters Kluwer N.V.
+wolterskluwer
+
+// woodside : 2015-07-09 Woodside Petroleum Limited
+woodside
+
+// work : 2013-12-19 Minds + Machines Group Limited
+work
+
+// works : 2013-11-14 Binky Moon, LLC
+works
+
+// world : 2014-06-12 Binky Moon, LLC
+world
+
+// wow : 2015-10-08 Amazon Registry Services, Inc.
+wow
+
+// wtc : 2013-12-19 World Trade Centers Association, Inc.
+wtc
+
+// wtf : 2014-03-06 Binky Moon, LLC
+wtf
+
+// xbox : 2014-12-18 Microsoft Corporation
+xbox
+
+// xerox : 2014-10-24 Xerox DNHC LLC
+xerox
+
+// xfinity : 2015-07-09 Comcast IP Holdings I, LLC
+xfinity
+
+// xihuan : 2015-01-08 Beijing Qihu Keji Co., Ltd.
+xihuan
+
+// xin : 2014-12-11 Elegant Leader Limited
+xin
+
+// xn--11b4c3d : 2015-01-15 VeriSign Sarl
+कॉम
+
+// xn--1ck2e1b : 2015-02-26 Amazon Registry Services, Inc.
+セール
+
+// xn--1qqw23a : 2014-01-09 Guangzhou YU Wei Information Technology Co., Ltd.
+佛山
+
+// xn--30rr7y : 2014-06-12 Excellent First Limited
+慈善
+
+// xn--3bst00m : 2013-09-13 Eagle Horizon Limited
+集团
+
+// xn--3ds443g : 2013-09-08 TLD REGISTRY LIMITED OY
+在线
+
+// xn--3oq18vl8pn36a : 2015-07-02 Volkswagen (China) Investment Co., Ltd.
+大众汽车
+
+// xn--3pxu8k : 2015-01-15 VeriSign Sarl
+点看
+
+// xn--42c2d9a : 2015-01-15 VeriSign Sarl
+คอม
+
+// xn--45q11c : 2013-11-21 Zodiac Gemini Ltd
+八卦
+
+// xn--4gbrim : 2013-10-04 Fans TLD Limited
+موقع
+
+// xn--55qw42g : 2013-11-08 China Organizational Name Administration Center
+公益
+
+// xn--55qx5d : 2013-11-14 China Internet Network Information Center (CNNIC)
+公司
+
+// xn--5su34j936bgsg : 2015-09-03 Shangri‐La International Hotel Management Limited
+香格里拉
+
+// xn--5tzm5g : 2014-12-22 Global Website TLD Asia Limited
+网站
+
+// xn--6frz82g : 2013-09-23 Afilias Limited
+移动
+
+// xn--6qq986b3xl : 2013-09-13 Tycoon Treasure Limited
+我爱你
+
+// xn--80adxhks : 2013-12-19 Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID)
+москва
+
+// xn--80aqecdr1a : 2015-10-21 Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication)
+католик
+
+// xn--80asehdb : 2013-07-14 CORE Association
+онлайн
+
+// xn--80aswg : 2013-07-14 CORE Association
+сайт
+
+// xn--8y0a063a : 2015-03-26 China United Network Communications Corporation Limited
+联通
+
+// xn--9dbq2a : 2015-01-15 VeriSign Sarl
+קום
+
+// xn--9et52u : 2014-06-12 RISE VICTORY LIMITED
+时尚
+
+// xn--9krt00a : 2015-03-12 Sina Corporation
+微博
+
+// xn--b4w605ferd : 2014-08-07 Temasek Holdings (Private) Limited
+淡马锡
+
+// xn--bck1b9a5dre4c : 2015-02-26 Amazon Registry Services, Inc.
+ファッション
+
+// xn--c1avg : 2013-11-14 Public Interest Registry
+орг
+
+// xn--c2br7g : 2015-01-15 VeriSign Sarl
+नेट
+
+// xn--cck2b3b : 2015-02-26 Amazon Registry Services, Inc.
+ストア
+
+// xn--cckwcxetd : 2019-12-19 Amazon Registry Services, Inc.
+アマゾン
+
+// xn--cg4bki : 2013-09-27 SAMSUNG SDS CO., LTD
+삼성
+
+// xn--czr694b : 2014-01-16 Internet DotTrademark Organisation Limited
+商标
+
+// xn--czrs0t : 2013-12-19 Binky Moon, LLC
+商店
+
+// xn--czru2d : 2013-11-21 Zodiac Aquarius Limited
+商城
+
+// xn--d1acj3b : 2013-11-20 The Foundation for Network Initiatives “The Smart Internet”
+дети
+
+// xn--eckvdtc9d : 2014-12-18 Amazon Registry Services, Inc.
+ポイント
+
+// xn--efvy88h : 2014-08-22 Guangzhou YU Wei Information Technology Co., Ltd.
+新闻
+
+// xn--fct429k : 2015-04-09 Amazon Registry Services, Inc.
+家電
+
+// xn--fhbei : 2015-01-15 VeriSign Sarl
+كوم
+
+// xn--fiq228c5hs : 2013-09-08 TLD REGISTRY LIMITED OY
+中文网
+
+// xn--fiq64b : 2013-10-14 CITIC Group Corporation
+中信
+
+// xn--fjq720a : 2014-05-22 Binky Moon, LLC
+娱乐
+
+// xn--flw351e : 2014-07-31 Charleston Road Registry Inc.
+谷歌
+
+// xn--fzys8d69uvgm : 2015-05-14 PCCW Enterprises Limited
+電訊盈科
+
+// xn--g2xx48c : 2015-01-30 Nawang Heli(Xiamen) Network Service Co., LTD.
+购物
+
+// xn--gckr3f0f : 2015-02-26 Amazon Registry Services, Inc.
+クラウド
+
+// xn--gk3at1e : 2015-10-08 Amazon Registry Services, Inc.
+通販
+
+// xn--hxt814e : 2014-05-15 Zodiac Taurus Limited
+网店
+
+// xn--i1b6b1a6a2e : 2013-11-14 Public Interest Registry
+संगठन
+
+// xn--imr513n : 2014-12-11 Internet DotTrademark Organisation Limited
+餐厅
+
+// xn--io0a7i : 2013-11-14 China Internet Network Information Center (CNNIC)
+网络
+
+// xn--j1aef : 2015-01-15 VeriSign Sarl
+ком
+
+// xn--jlq480n2rg : 2019-12-19 Amazon Registry Services, Inc.
+亚马逊
+
+// xn--jlq61u9w7b : 2015-01-08 Nokia Corporation
+诺基亚
+
+// xn--jvr189m : 2015-02-26 Amazon Registry Services, Inc.
+食品
+
+// xn--kcrx77d1x4a : 2014-11-07 Koninklijke Philips N.V.
+飞利浦
+
+// xn--kput3i : 2014-02-13 Beijing RITT-Net Technology Development Co., Ltd
+手机
+
+// xn--mgba3a3ejt : 2014-11-20 Aramco Services Company
+ارامكو
+
+// xn--mgba7c0bbn0a : 2015-05-14 Crescent Holding GmbH
+العليان
+
+// xn--mgbaakc7dvf : 2015-09-03 Emirates Telecommunications Corporation (trading as Etisalat)
+اتصالات
+
+// xn--mgbab2bd : 2013-10-31 CORE Association
+بازار
+
+// xn--mgbca7dzdo : 2015-07-30 Abu Dhabi Systems and Information Centre
+ابوظبي
+
+// xn--mgbi4ecexp : 2015-10-21 Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication)
+كاثوليك
+
+// xn--mgbt3dhd : 2014-09-04 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
+همراه
+
+// xn--mk1bu44c : 2015-01-15 VeriSign Sarl
+닷컴
+
+// xn--mxtq1m : 2014-03-06 Net-Chinese Co., Ltd.
+政府
+
+// xn--ngbc5azd : 2013-07-13 International Domain Registry Pty. Ltd.
+شبكة
+
+// xn--ngbe9e0a : 2014-12-04 Kuwait Finance House
+بيتك
+
+// xn--ngbrx : 2015-11-12 League of Arab States
+عرب
+
+// xn--nqv7f : 2013-11-14 Public Interest Registry
+机构
+
+// xn--nqv7fs00ema : 2013-11-14 Public Interest Registry
+组织机构
+
+// xn--nyqy26a : 2014-11-07 Stable Tone Limited
+健康
+
+// xn--otu796d : 2017-08-06 Jiang Yu Liang Cai Technology Company Limited
+招聘
+
+// xn--p1acf : 2013-12-12 Rusnames Limited
+рус
+
+// xn--pssy2u : 2015-01-15 VeriSign Sarl
+大拿
+
+// xn--q9jyb4c : 2013-09-17 Charleston Road Registry Inc.
+みんな
+
+// xn--qcka1pmc : 2014-07-31 Charleston Road Registry Inc.
+グーグル
+
+// xn--rhqv96g : 2013-09-11 Stable Tone Limited
+世界
+
+// xn--rovu88b : 2015-02-26 Amazon Registry Services, Inc.
+書籍
+
+// xn--ses554g : 2014-01-16 KNET Co., Ltd.
+网址
+
+// xn--t60b56a : 2015-01-15 VeriSign Sarl
+닷넷
+
+// xn--tckwe : 2015-01-15 VeriSign Sarl
+コム
+
+// xn--tiq49xqyj : 2015-10-21 Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication)
+天主教
+
+// xn--unup4y : 2013-07-14 Binky Moon, LLC
+游戏
+
+// xn--vermgensberater-ctb : 2014-06-23 Deutsche Vermögensberatung Aktiengesellschaft DVAG
+vermögensberater
+
+// xn--vermgensberatung-pwb : 2014-06-23 Deutsche Vermögensberatung Aktiengesellschaft DVAG
+vermögensberatung
+
+// xn--vhquv : 2013-08-27 Binky Moon, LLC
+企业
+
+// xn--vuq861b : 2014-10-16 Beijing Tele-info Network Technology Co., Ltd.
+信息
+
+// xn--w4r85el8fhu5dnra : 2015-04-30 Kerry Trading Co. Limited
+嘉里大酒店
+
+// xn--w4rs40l : 2015-07-30 Kerry Trading Co. Limited
+嘉里
+
+// xn--xhq521b : 2013-11-14 Guangzhou YU Wei Information Technology Co., Ltd.
+广东
+
+// xn--zfr164b : 2013-11-08 China Organizational Name Administration Center
+政务
+
+// xyz : 2013-12-05 XYZ.COM LLC
+xyz
+
+// yachts : 2014-01-09 XYZ.COM LLC
+yachts
+
+// yahoo : 2015-04-02 Yahoo! Domain Services Inc.
+yahoo
+
+// yamaxun : 2014-12-18 Amazon Registry Services, Inc.
+yamaxun
+
+// yandex : 2014-04-10 Yandex Europe B.V.
+yandex
+
+// yodobashi : 2014-11-20 YODOBASHI CAMERA CO.,LTD.
+yodobashi
+
+// yoga : 2014-05-29 Minds + Machines Group Limited
+yoga
+
+// yokohama : 2013-12-12 GMO Registry, Inc.
+yokohama
+
+// you : 2015-04-09 Amazon Registry Services, Inc.
+you
+
+// youtube : 2014-05-01 Charleston Road Registry Inc.
+youtube
+
+// yun : 2015-01-08 Beijing Qihu Keji Co., Ltd.
+yun
+
+// zappos : 2015-06-25 Amazon Registry Services, Inc.
+zappos
+
+// zara : 2014-11-07 Industria de Diseño Textil, S.A. (INDITEX, S.A.)
+zara
+
+// zero : 2014-12-18 Amazon Registry Services, Inc.
+zero
+
+// zip : 2014-05-08 Charleston Road Registry Inc.
+zip
+
+// zone : 2013-11-14 Binky Moon, LLC
+zone
+
+// zuerich : 2014-11-07 Kanton Zürich (Canton of Zurich)
+zuerich
+
+
+// ===END ICANN DOMAINS===
+// ===BEGIN PRIVATE DOMAINS===
+// (Note: these are in alphabetical order by company name)
+
+// 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
+
+// Adobe : https://www.adobe.com/
+// Submitted by Ian Boston <boston@adobe.com>
+adobeaemcloud.com
+adobeaemcloud.net
+*.dev.adobeaemcloud.com
+
+// Agnat sp. z o.o. : https://domena.pl
+// Submitted by Przemyslaw Plewa <it-admin@domena.pl>
+beep.pl
+
+// 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
+
+// Amazon CloudFront : https://aws.amazon.com/cloudfront/
+// Submitted by Donavan Miller <donavanm@amazon.com>
+cloudfront.net
+
+// Amazon Elastic Compute Cloud : https://aws.amazon.com/ec2/
+// Submitted by Luke Wells <psl-maintainers@amazon.com>
+*.compute.amazonaws.com
+*.compute-1.amazonaws.com
+*.compute.amazonaws.com.cn
+us-east-1.amazonaws.com
+
+// Amazon Elastic Beanstalk : https://aws.amazon.com/elasticbeanstalk/
+// Submitted by Luke Wells <psl-maintainers@amazon.com>
+cn-north-1.eb.amazonaws.com.cn
+cn-northwest-1.eb.amazonaws.com.cn
+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
+ca-central-1.elasticbeanstalk.com
+eu-central-1.elasticbeanstalk.com
+eu-west-1.elasticbeanstalk.com
+eu-west-2.elasticbeanstalk.com
+eu-west-3.elasticbeanstalk.com
+sa-east-1.elasticbeanstalk.com
+us-east-1.elasticbeanstalk.com
+us-east-2.elasticbeanstalk.com
+us-gov-west-1.elasticbeanstalk.com
+us-west-1.elasticbeanstalk.com
+us-west-2.elasticbeanstalk.com
+
+// Amazon Elastic Load Balancing : https://aws.amazon.com/elasticloadbalancing/
+// Submitted by Luke Wells <psl-maintainers@amazon.com>
+*.elb.amazonaws.com
+*.elb.amazonaws.com.cn
+
+// Amazon Global Accelerator : https://aws.amazon.com/global-accelerator/
+// Submitted by Daniel Massaguer <psl-maintainers@amazon.com>
+awsglobalaccelerator.com
+
+// Amazon S3 : https://aws.amazon.com/s3/
+// Submitted by Luke Wells <psl-maintainers@amazon.com>
+s3.amazonaws.com
+s3-ap-northeast-1.amazonaws.com
+s3-ap-northeast-2.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-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-west-1.amazonaws.com
+s3-sa-east-1.amazonaws.com
+s3-us-gov-west-1.amazonaws.com
+s3-us-east-2.amazonaws.com
+s3-us-west-1.amazonaws.com
+s3-us-west-2.amazonaws.com
+s3.ap-northeast-2.amazonaws.com
+s3.ap-south-1.amazonaws.com
+s3.cn-north-1.amazonaws.com.cn
+s3.ca-central-1.amazonaws.com
+s3.eu-central-1.amazonaws.com
+s3.eu-west-2.amazonaws.com
+s3.eu-west-3.amazonaws.com
+s3.us-east-2.amazonaws.com
+s3.dualstack.ap-northeast-1.amazonaws.com
+s3.dualstack.ap-northeast-2.amazonaws.com
+s3.dualstack.ap-south-1.amazonaws.com
+s3.dualstack.ap-southeast-1.amazonaws.com
+s3.dualstack.ap-southeast-2.amazonaws.com
+s3.dualstack.ca-central-1.amazonaws.com
+s3.dualstack.eu-central-1.amazonaws.com
+s3.dualstack.eu-west-1.amazonaws.com
+s3.dualstack.eu-west-2.amazonaws.com
+s3.dualstack.eu-west-3.amazonaws.com
+s3.dualstack.sa-east-1.amazonaws.com
+s3.dualstack.us-east-1.amazonaws.com
+s3.dualstack.us-east-2.amazonaws.com
+s3-website-us-east-1.amazonaws.com
+s3-website-us-west-1.amazonaws.com
+s3-website-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.ap-northeast-2.amazonaws.com
+s3-website.ap-south-1.amazonaws.com
+s3-website.ca-central-1.amazonaws.com
+s3-website.eu-central-1.amazonaws.com
+s3-website.eu-west-2.amazonaws.com
+s3-website.eu-west-3.amazonaws.com
+s3-website.us-east-2.amazonaws.com
+
+// Amsterdam Wireless: https://www.amsterdamwireless.nl/
+// Submitted by Imre Jonk <hostmaster@amsterdamwireless.nl>
+amsw.nl
+
+// 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
+
+// Appspace : https://www.appspace.com
+// Submitted by Appspace Security Team <security@appspace.com>
+appspacehosted.com
+appspaceusercontent.com
+
+// Aptible : https://www.aptible.com/
+// Submitted by Thomas Orozco <thomas@aptible.com>
+on-aptible.com
+
+// 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
+
+// AVM : https://avm.de
+// Submitted by Andreas Weise <a.weise@avm.de>
+myfritz.net
+
+// AW AdvisorWebsites.com Software Inc : https://advisorwebsites.com
+// Submitted by James Kennedy <domains@advisorwebsites.com>
+*.awdev.ca
+*.advisor.ws
+
+// 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
+
+// Banzai Cloud
+// Submitted by Janos Matyas <info@banzaicloud.com>
+*.banzai.cloud
+app.banzaicloud.io
+*.backyards.banzaicloud.io
+
+
+// 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
+
+// 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
+
+// Boomla : https://boomla.com
+// Submitted by Tibor Halter <thalter@boomla.com>
+boomla.net
+
+// 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
+
+// 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
+
+// callidomus : https://www.callidomus.com/
+// Submitted by Marcus Popp <admin@callidomus.com>
+mycd.eu
+
+// Carrd : https://carrd.co
+// Submitted by AJ <aj@carrd.co>
+carrd.co
+crd.co
+uwu.ai
+
+// 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
+gb.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
+
+// Globe Hosting SRL : https://www.globehosting.com/
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+co.ro
+shop.ro
+
+// c.la : http://www.c.la/
+c.la
+
+// certmgr.org : https://certmgr.org
+// Submitted by B. Blechschmidt <hostmaster@certmgr.org>
+certmgr.org
+
+// Civilized Discourse Construction Kit, Inc. : https://www.discourse.org/
+// Submitted by Rishabh Nambiar & Michael Brown <team@discourse.org>
+discourse.group
+discourse.team
+
+// ClearVox : http://www.clearvox.nl/
+// Submitted by Leon Rowland <leon@clearvox.nl>
+virtueeldomein.nl
+
+// 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 <colin@clerk.dev>
+*.lcl.dev
+*.stg.dev
+
+// Clic2000 : https://clic2000.fr
+// Submitted by Mathilde Blanchemanche <mathilde@clic2000.fr>
+clic2000.net
+
+// 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 Philip Langdale <security@cloudera.com>
+cloudera.site
+
+// Cloudflare, Inc. : https://www.cloudflare.com/
+// Submitted by Cloudflare Team <publicsuffixlist@cloudflare.com>
+pages.dev
+trycloudflare.com
+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
+
+// 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
+
+// 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
+
+// Craynic, s.r.o. : http://www.craynic.com/
+// Submitted by Ales Krajnik <ales.krajnik@craynic.com>
+realm.cz
+
+// 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
+
+// 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
+
+// Datawire, Inc : https://www.datawire.io
+// Submitted by Richard Li <secalert@datawire.io>
+edgestack.me
+
+// 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
+
+// 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 : https://digitalocean.com/
+// Submitted by Braxton Huggins <bhuggins@digitalocean.com>
+ondigitalocean.app
+
+// 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
+
+// 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
+
+// ECG Robotics, Inc: https://ecgrobotics.org
+// Submitted by <frc1533@ecgrobotics.org>
+onred.one
+staging.onred.one
+
+// One.com: https://www.one.com/
+// Submitted by Jacob Bunk Nielsen <jbn@one.com>
+service.one
+
+// Enonic : http://enonic.com/
+// Submitted by Erik Kaareng-Sunde <esu@enonic.com>
+enonic.io
+customer.enonic.io
+
+// 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
+
+// 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
+
+// Facebook, Inc.
+// Submitted by Peter Ruibal <public-suffix@fb.com>
+apps.fbsbx.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>
+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
+
+// 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
+couk.me
+ukco.me
+
+// 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
+
+// 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
+
+// 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 OG : http://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
+
+// GDS : https://www.gov.uk/service-manual/operations/operating-servicegovuk-subdomains
+// Submitted by David Illsley <david.illsley@digital.cabinet-office.gov.uk>
+service.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
+
+// GitHub, Inc.
+// Submitted by Patrick Toomey <security@github.com>
+github.io
+githubusercontent.com
+
+// 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
+
+// GMO Pepabo, Inc. : https://pepabo.com/
+// Submitted by dojineko <admin@pepabo.com>
+lolipop.io
+
+// GOV.UK Platform as a Service : https://www.cloud.service.gov.uk/
+// Submitted by Tom Whitwell <tom.whitwell@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
+
+// UKHomeOffice : https://www.gov.uk/government/organisations/home-office
+// Submitted by Jon Shanks <jon.shanks@digital.homeoffice.gov.uk>
+homeoffice.gov.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
+a.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
+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
+
+// Aaron Marais' Gitlab pages: https://lab.aaronleem.co.za
+// Submitted by Aaron Marais <its_me@aaronleem.co.za>
+graphox.us
+
+// 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
+// Submited 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
+
+// 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>
+myravendb.com
+ravendb.community
+ravendb.me
+development.run
+ravendb.run
+
+// Hong Kong Productivity Council: https://www.hkpc.org/
+// Submitted by SECaaS Team <summchan@hkpc.org>
+secaas.hk
+
+// 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
+org.yt
+
+// 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
+
+// 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/
+// Submited 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.eu
+// Submitted by Kim-Alexander Brodowski <info@iserv.eu>
+mein-iserv.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/
+// Submited by Ihor Kolodyuk <ik@jelastic.com>
+mel.cloudlets.com.au
+cloud.interhostsolutions.be
+users.scale.virtualcloud.com.br
+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
+it1-eur.jenv-arubabiz.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
+clicketcloud.com
+ams.cloudswitches.com
+au.cloudswitches.com
+sg.cloudswitches.com
+dopaas.com
+elastyco.com
+nv.elastyco.com
+hidora.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
+paas.leviracloud.eu
+fi.cloudplatform.fi
+demo.datacenter.fi
+paas.datacenter.fi
+jele.host
+mircloud.host
+jele.io
+ocs.opusinteractive.io
+cloud.unispace.io
+cloud-de.unispace.io
+cloud-fr1.unispace.io
+jc.neen.it
+cloud.jelastic.open.tim.it
+jcloud.kz
+upaas.kazteleport.kz
+jl.serv.net.mx
+cloudjiffy.net
+fra1-de.cloudjiffy.net
+west1-us.cloudjiffy.net
+ams1.jls.docktera.net
+jls-sto1.elastx.net
+jls-sto2.elastx.net
+jls-sto3.elastx.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
+atl.jelastic.vps-host.net
+njs.jelastic.vps-host.net
+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
+
+// 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
+
+// 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
+
+// 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
+
+// LiquidNet Ltd : http://www.liquidnetlimited.com/
+// Submitted by Victor Velchev <admin@liquidnetlimited.com>
+we.bs
+
+// 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
+
+// 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.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
+
+// 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
+
+// Memset hosting : https://www.memset.com
+// Submitted by Tom Whitwell <domains@memset.com>
+miniserver.com
+memset.net
+
+// 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 Mitch Webster <miwebst@microsoft.com>
+*.azurecontainer.io
+azurewebsites.net
+azure-mobile.net
+cloudapp.net
+azurestaticapps.net
+centralus.azurestaticapps.net
+eastasia.azurestaticapps.net
+eastus2.azurestaticapps.net
+westeurope.azurestaticapps.net
+westus2.azurestaticapps.net
+
+// minion.systems : http://minion.systems
+// Submitted by Robert Böttinger <r@minion.systems>
+csx.cc
+
+// MobileEducation, LLC : https://joinforte.com
+// Submitted by Grayson Martin <grayson.martin@mobileeducation.us>
+forte.id
+
+// 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
+
+// Names.of.London : https://names.of.london/
+// Submitted by James Stevens <registry[at]names.of.london> or <publiclist[at]jrcs.net>
+pony.club
+of.fashion
+in.london
+of.london
+from.marketing
+with.marketing
+for.men
+repair.men
+and.mom
+for.mom
+for.one
+under.one
+for.sale
+that.win
+from.work
+to.work
+
+// NCTU.ME : https://nctu.me/
+// Submitted by Tocknicsu <admin@nctu.me>
+nctu.me
+
+// 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.io
+
+// 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
+
+// Northflank Ltd. : https://northflank.com/
+// Submitted by Marco Suter <marco@northflank.com>
+northflank.app
+code.run
+
+// 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
+
+// Nodum B.V. : https://nodum.io/
+// Submitted by Wietse Wind <hello+publicsuffixlist@nodum.io>
+nodum.co
+nodum.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
+
+// NymNom : https://nymnom.com/
+// Submitted by NymNom <psl@nymnom.com>
+nom.ae
+nom.af
+nom.ai
+nom.al
+nym.by
+nom.bz
+nym.bz
+nom.cl
+nym.ec
+nom.gd
+nom.ge
+nom.gl
+nym.gr
+nom.gt
+nym.gy
+nym.hk
+nom.hn
+nym.ie
+nom.im
+nom.ke
+nym.kz
+nym.la
+nym.lc
+nom.li
+nym.li
+nym.lt
+nym.lu
+nom.lv
+nym.me
+nom.mk
+nym.mn
+nym.mx
+nom.nu
+nym.nz
+nym.pe
+nym.pt
+nom.pw
+nom.qa
+nym.ro
+nom.rs
+nom.si
+nym.sk
+nom.st
+nym.su
+nym.sx
+nom.tj
+nym.tw
+nom.ug
+nom.uy
+nom.vc
+nom.vg
+
+// 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 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
+
+// Oursky Limited : https://skygear.io/
+// Submited by Skygear Developer <hello@skygear.io>
+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
+
+// 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
+ra-ru.ru
+zsew.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>
+bc.platform.sh
+ent.platform.sh
+eu.platform.sh
+us.platform.sh
+*.platformsh.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
+
+// Port53 : https://port53.io/
+// Submitted by Maximilian Schieder <maxi@zeug.co>
+dyn53.io
+
+// Positive Codes Technology Company : http://co.bn/faq.html
+// Submitted by Zulfais <pc@co.bn>
+co.bn
+
+// 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
+
+// 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
+
+// QuickBackend: https://www.quickbackend.com
+// Submitted by Dani Biro <dani@pymet.com>
+qbuser.com
+
+// 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 Mason Clayton <mason@repl.it>
+repl.co
+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
+
+// Rochester Institute of Technology : http://www.rit.edu/
+// Submitted by Jennifer Herting <jchits@rit.edu>
+git-pages.rit.edu
+
+// 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
+
+// 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
+
+// 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
+
+// Senseering GmbH : https://www.senseering.de
+// Submitted by Felix Mönckemeyer <f.moenckemeyer@senseering.de>
+senseering.net
+
+// Service Online LLC : http://drs.ua/
+// Submitted by Serhii Bulakh <support@drs.ua>
+biz.ua
+co.ua
+pp.ua
+
+// 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
+
+// Small Technology Foundation : https://small-tech.org
+// Submitted by Aral Balkan <aral@small-tech.org>
+small-web.org
+
+// Snowplow Analytics : https://snowplowanalytics.com/
+// Submitted by Ian Streeter <ian@snowplowanalytics.com>
+try-snowplow.com
+
+// Stackhero : https://www.stackhero.io
+// Submitted by Adrien Gillon <adrien+public-suffix-list@stackhero.io>
+stackhero-network.com
+
+// staticland : https://static.land
+// Submitted by Seth Vincent <sethvincent@gmail.com>
+static.land
+dev.static.land
+sites.static.land
+
+// 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
+
+// Standard Library : https://stdlib.com
+// Submitted by Jacob Lee <jacob@stdlib.com>
+api.stdlib.com
+
+// Storj Labs Inc. : https://storj.io/
+// Submitted by Philip Hutchins <hostmaster@storj.io>
+storj.farm
+
+// 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
+
+// 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>
+diskstation.me
+dscloud.biz
+dscloud.me
+dscloud.mobi
+dsmynas.com
+dsmynas.net
+dsmynas.org
+familyds.com
+familyds.net
+familyds.org
+i234.me
+myds.me
+synology.me
+vpnplus.to
+direct.quickconnect.to
+
+// TAIFUN Software AG : http://taifun-software.de
+// Submitted by Bjoern Henke <dev-server@taifun-software.de>
+taifun-dns.de
+
+// TASK geographical domains (www.task.gda.pl/uslugi/dns)
+gda.pl
+gdansk.pl
+gdynia.pl
+med.pl
+sopot.pl
+
+// Teckids e.V. : https://www.teckids.org
+// Submitted by Dominik George <dominik.george@teckids.org>
+edugit.org
+
+// Telebit : https://telebit.cloud
+// Submitted by AJ ONeal <aj@telebit.cloud>
+telebit.app
+telebit.io
+*.telebit.xyz
+
+// The Gwiddle Foundation : https://gwiddlefoundation.org.uk
+// Submitted by Joshua Bayfield <joshua.bayfield@gwiddlefoundation.org.uk>
+gwiddle.co.uk
+
+// Thingdust AG : https://thingdust.com/
+// Submitted by Adrian Imboden <adi@thingdust.com>
+thingdustdata.com
+cust.dev.thingdust.io
+cust.disrec.thingdust.io
+cust.prod.thingdust.io
+cust.testing.thingdust.io
+*.firenet.ch
+*.svc.firenet.ch
+
+// 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
+
+// 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
+
+// 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
+
+// United Gameserver GmbH : https://united-gameserver.de
+// Submitted by Stefan Schwarz <sysadm@united-gameserver.de>
+virtualuser.de
+virtual-user.de
+
+// 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
+blog.kg
+io.kg
+jp.kg
+tv.kg
+uk.kg
+us.kg
+de.ls
+at.md
+de.md
+jp.md
+to.md
+uwu.nu
+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
+
+// Waffle Computer Inc., Ltd. : https://docs.waffleinfo.com
+// Submitted by Masayuki Note <masa@blade.wafflecell.com>
+wafflecell.com
+
+// WapBlog.ID : https://www.wapblog.id
+// Submitted by Fajar Sodik <official@wapblog.id>
+idnblogger.com
+indowapblog.com
+bloger.id
+wblog.id
+wbq.me
+fastblog.net
+
+// WebHare bv: https://www.webhare.com/
+// Submitted by Arnold Hendriks <info@webhare.com>
+*.webhare.dev
+
+// 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
+
+// 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
+
+// www.com.vc : http://www.com.vc
+// Submitted by Li Hui <lihui@sinopub.com>
+cn.vu
+
+// 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>
+nohost.me
+noho.st
+
+// ZaNiC : http://www.za.net/
+// Submitted by registry <hostmaster@nic.za.net>
+za.net
+za.org
+
+// 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
+
+// Mintere : https://mintere.com/
+// Submitted by Ben Aubin <security@mintere.com>
+mintere.site
+
+// Cityhost LLC : https://cityhost.ua
+// Submitted by Maksym Rivtin <support@cityhost.net.ua>
+cx.ua
+
+// 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
+
+// Impertrix Solutions : <https://impertrixcdn.com>
+// Submitted by Zhixiang Zhao <csuite@impertrix.com>
+impertrixcdn.com
+impertrix.com
+
+// GignoSystemJapan: http://gsj.bz
+// Submitted by GignoSystemJapan <kakutou-ec@gsj.bz>
+gsj.bz
+
+// Rusnames Limited: http://rusnames.ru/
+// Submitted by Sergey Zotov <admin@rusnames.ru>
+биз.рус
+ком.рус
+крым.рус
+мир.рус
+мск.рус
+орг.рус
+самара.рус
+сочи.рус
+спб.рус
+я.рус
+// ===END PRIVATE DOMAINS===
diff --git a/netwerk/dns/mdns/libmdns/DNSServiceDiscovery.jsm b/netwerk/dns/mdns/libmdns/DNSServiceDiscovery.jsm
new file mode 100644
index 0000000000..bf153ea1bd
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/DNSServiceDiscovery.jsm
@@ -0,0 +1,219 @@
+/* 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/. */
+"use strict";
+
+const { AppConstants } = ChromeUtils.import(
+ "resource://gre/modules/AppConstants.jsm"
+);
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+const { MulticastDNS } = ChromeUtils.import(
+ AppConstants.platform == "android" &&
+ !Services.prefs.getBoolPref("network.mdns.use_js_fallback")
+ ? "resource://gre/modules/MulticastDNSAndroid.jsm"
+ : "resource://gre/modules/MulticastDNS.jsm"
+);
+
+const DNSSERVICEINFO_CONTRACT_ID =
+ "@mozilla.org/toolkit/components/mdnsresponder/dns-info;1";
+
+function log(aMsg) {
+ dump("-*- nsDNSServiceDiscovery.js : " + aMsg + "\n");
+}
+
+function generateUuid() {
+ var uuidGenerator = Cc["@mozilla.org/uuid-generator;1"].getService(
+ Ci.nsIUUIDGenerator
+ );
+ return uuidGenerator.generateUUID().toString();
+}
+
+// Helper class to transform return objects to correct type.
+function ListenerWrapper(aListener, aMdns) {
+ this.listener = aListener;
+ this.mdns = aMdns;
+
+ this.discoveryStarting = false;
+ this.stopDiscovery = false;
+
+ this.registrationStarting = false;
+ this.stopRegistration = false;
+
+ this.uuid = generateUuid();
+}
+
+ListenerWrapper.prototype = {
+ // Helper function for transforming an Object into nsIDNSServiceInfo.
+ makeServiceInfo(aServiceInfo) {
+ let serviceInfo = Cc[DNSSERVICEINFO_CONTRACT_ID].createInstance(
+ Ci.nsIDNSServiceInfo
+ );
+
+ for (let name of [
+ "host",
+ "address",
+ "port",
+ "serviceName",
+ "serviceType",
+ ]) {
+ try {
+ serviceInfo[name] = aServiceInfo[name];
+ } catch (e) {
+ // ignore exceptions
+ }
+ }
+
+ let attributes;
+ try {
+ attributes = _toPropertyBag2(aServiceInfo.attributes);
+ } catch (err) {
+ // Ignore unset attributes in object.
+ log("Caught unset attributes error: " + err + " - " + err.stack);
+ attributes = Cc["@mozilla.org/hash-property-bag;1"].createInstance(
+ Ci.nsIWritablePropertyBag2
+ );
+ }
+ serviceInfo.attributes = attributes;
+
+ return serviceInfo;
+ },
+
+ /* transparent types */
+ onDiscoveryStarted(aServiceType) {
+ this.discoveryStarting = false;
+ this.listener.onDiscoveryStarted(aServiceType);
+
+ if (this.stopDiscovery) {
+ this.mdns.stopDiscovery(aServiceType, this);
+ }
+ },
+ onDiscoveryStopped(aServiceType) {
+ this.listener.onDiscoveryStopped(aServiceType);
+ },
+ onStartDiscoveryFailed(aServiceType, aErrorCode) {
+ log("onStartDiscoveryFailed: " + aServiceType + " (" + aErrorCode + ")");
+ this.discoveryStarting = false;
+ this.stopDiscovery = true;
+ this.listener.onStartDiscoveryFailed(aServiceType, aErrorCode);
+ },
+ onStopDiscoveryFailed(aServiceType, aErrorCode) {
+ log("onStopDiscoveryFailed: " + aServiceType + " (" + aErrorCode + ")");
+ this.listener.onStopDiscoveryFailed(aServiceType, aErrorCode);
+ },
+
+ /* transform types */
+ onServiceFound(aServiceInfo) {
+ this.listener.onServiceFound(this.makeServiceInfo(aServiceInfo));
+ },
+ onServiceLost(aServiceInfo) {
+ this.listener.onServiceLost(this.makeServiceInfo(aServiceInfo));
+ },
+ onServiceRegistered(aServiceInfo) {
+ this.registrationStarting = false;
+ this.listener.onServiceRegistered(this.makeServiceInfo(aServiceInfo));
+
+ if (this.stopRegistration) {
+ this.mdns.unregisterService(aServiceInfo, this);
+ }
+ },
+ onServiceUnregistered(aServiceInfo) {
+ this.listener.onServiceUnregistered(this.makeServiceInfo(aServiceInfo));
+ },
+ onServiceResolved(aServiceInfo) {
+ this.listener.onServiceResolved(this.makeServiceInfo(aServiceInfo));
+ },
+
+ onRegistrationFailed(aServiceInfo, aErrorCode) {
+ log("onRegistrationFailed: (" + aErrorCode + ")");
+ this.registrationStarting = false;
+ this.stopRegistration = true;
+ this.listener.onRegistrationFailed(
+ this.makeServiceInfo(aServiceInfo),
+ aErrorCode
+ );
+ },
+ onUnregistrationFailed(aServiceInfo, aErrorCode) {
+ log("onUnregistrationFailed: (" + aErrorCode + ")");
+ this.listener.onUnregistrationFailed(
+ this.makeServiceInfo(aServiceInfo),
+ aErrorCode
+ );
+ },
+ onResolveFailed(aServiceInfo, aErrorCode) {
+ log("onResolveFailed: (" + aErrorCode + ")");
+ this.listener.onResolveFailed(
+ this.makeServiceInfo(aServiceInfo),
+ aErrorCode
+ );
+ },
+};
+
+function nsDNSServiceDiscovery() {
+ log("nsDNSServiceDiscovery");
+ this.mdns = new MulticastDNS();
+}
+
+nsDNSServiceDiscovery.prototype = {
+ QueryInterface: ChromeUtils.generateQI([
+ "nsISupportsWeakReference",
+ "nsIDNSServiceDiscovery",
+ ]),
+
+ startDiscovery(aServiceType, aListener) {
+ log("startDiscovery");
+ let listener = new ListenerWrapper(aListener, this.mdns);
+ listener.discoveryStarting = true;
+ this.mdns.startDiscovery(aServiceType, listener);
+
+ return {
+ QueryInterface: ChromeUtils.generateQI(["nsICancelable"]),
+ cancel: function() {
+ if (this.discoveryStarting || this.stopDiscovery) {
+ this.stopDiscovery = true;
+ return;
+ }
+ this.mdns.stopDiscovery(aServiceType, listener);
+ }.bind(listener),
+ };
+ },
+
+ registerService(aServiceInfo, aListener) {
+ log("registerService");
+ let listener = new ListenerWrapper(aListener, this.mdns);
+ listener.registrationStarting = true;
+ this.mdns.registerService(aServiceInfo, listener);
+
+ return {
+ QueryInterface: ChromeUtils.generateQI(["nsICancelable"]),
+ cancel: function() {
+ if (this.registrationStarting || this.stopRegistration) {
+ this.stopRegistration = true;
+ return;
+ }
+ this.mdns.unregisterService(aServiceInfo, listener);
+ }.bind(listener),
+ };
+ },
+
+ resolveService(aServiceInfo, aListener) {
+ log("resolveService");
+ this.mdns.resolveService(aServiceInfo, new ListenerWrapper(aListener));
+ },
+};
+
+var EXPORTED_SYMBOLS = ["nsDNSServiceDiscovery"];
+
+function _toPropertyBag2(obj) {
+ if (obj.QueryInterface) {
+ return obj.QueryInterface(Ci.nsIPropertyBag2);
+ }
+
+ let result = Cc["@mozilla.org/hash-property-bag;1"].createInstance(
+ Ci.nsIWritablePropertyBag2
+ );
+ for (let name in obj) {
+ result.setPropertyAsAString(name, obj[name]);
+ }
+ return result;
+}
diff --git a/netwerk/dns/mdns/libmdns/MDNSResponderOperator.cpp b/netwerk/dns/mdns/libmdns/MDNSResponderOperator.cpp
new file mode 100644
index 0000000000..ae780b614d
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/MDNSResponderOperator.cpp
@@ -0,0 +1,719 @@
+/* -*- 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 "MDNSResponderOperator.h"
+#include "MDNSResponderReply.h"
+#include "mozilla/EndianUtils.h"
+#include "mozilla/Logging.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/Unused.h"
+#include "nsComponentManagerUtils.h"
+#include "nsCOMPtr.h"
+#include "nsDebug.h"
+#include "nsDNSServiceInfo.h"
+#include "nsHashPropertyBag.h"
+#include "nsIProperty.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIVariant.h"
+#include "nsServiceManagerUtils.h"
+#include "nsNetAddr.h"
+#include "nsNetCID.h"
+#include "nsSocketTransportService2.h"
+#include "nsThreadUtils.h"
+#include "nsXPCOMCID.h"
+#include "private/pprio.h"
+
+#include "nsASocketHandler.h"
+
+namespace mozilla {
+namespace net {
+
+static LazyLogModule gMDNSLog("MDNSResponderOperator");
+#undef LOG_I
+#define LOG_I(...) \
+ MOZ_LOG(mozilla::net::gMDNSLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
+#undef LOG_E
+#define LOG_E(...) \
+ MOZ_LOG(mozilla::net::gMDNSLog, mozilla::LogLevel::Error, (__VA_ARGS__))
+
+class MDNSResponderOperator::ServiceWatcher final : public nsASocketHandler {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ // nsASocketHandler methods
+ virtual void OnSocketReady(PRFileDesc* fd, int16_t outFlags) override {
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+ MOZ_ASSERT(fd == mFD);
+
+ if (outFlags & (PR_POLL_ERR | PR_POLL_HUP | PR_POLL_NVAL)) {
+ LOG_E("error polling on listening socket (%p)", fd);
+ mCondition = NS_ERROR_UNEXPECTED;
+ }
+
+ if (!(outFlags & PR_POLL_READ)) {
+ return;
+ }
+
+ DNSServiceProcessResult(mService);
+ }
+
+ virtual void OnSocketDetached(PRFileDesc* fd) override {
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+ MOZ_ASSERT(mThread);
+ MOZ_ASSERT(fd == mFD);
+
+ if (!mFD) {
+ return;
+ }
+
+ // Bug 1175387: do not double close the handle here.
+ PR_ChangeFileDescNativeHandle(mFD, -1);
+ PR_Close(mFD);
+ mFD = nullptr;
+
+ mThread->Dispatch(
+ NewRunnableMethod("MDNSResponderOperator::ServiceWatcher::Deallocate",
+ this, &ServiceWatcher::Deallocate),
+ NS_DISPATCH_NORMAL);
+ }
+
+ virtual void IsLocal(bool* aIsLocal) override { *aIsLocal = true; }
+
+ virtual void KeepWhenOffline(bool* aKeepWhenOffline) override {
+ *aKeepWhenOffline = true;
+ }
+
+ virtual uint64_t ByteCountSent() override { return 0; }
+ virtual uint64_t ByteCountReceived() override { return 0; }
+
+ explicit ServiceWatcher(DNSServiceRef aService,
+ MDNSResponderOperator* aOperator)
+ : mThread(nullptr),
+ mSts(nullptr),
+ mOperatorHolder(aOperator),
+ mService(aService),
+ mFD(nullptr),
+ mAttached(false) {
+ if (!gSocketTransportService) {
+ nsCOMPtr<nsISocketTransportService> sts =
+ do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
+ }
+ }
+
+ nsresult Init() {
+ MOZ_ASSERT(!OnSocketThread(), "on socket thread");
+ mThread = NS_GetCurrentThread();
+
+ if (!mService) {
+ return NS_OK;
+ }
+
+ if (!gSocketTransportService) {
+ return NS_ERROR_FAILURE;
+ }
+ mSts = gSocketTransportService;
+
+ int osfd = DNSServiceRefSockFD(mService);
+ if (osfd == -1) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mFD = PR_ImportFile(osfd);
+ return PostEvent("MDNSResponderOperator::ServiceWatcher::OnMsgAttach",
+ &ServiceWatcher::OnMsgAttach);
+ }
+
+ void Close() {
+ MOZ_ASSERT(!OnSocketThread(), "on socket thread");
+
+ if (!gSocketTransportService) {
+ Deallocate();
+ return;
+ }
+
+ PostEvent("MDNSResponderOperator::ServiceWatcher::OnMsgClose",
+ &ServiceWatcher::OnMsgClose);
+ }
+
+ private:
+ ~ServiceWatcher() = default;
+
+ void Deallocate() {
+ if (mService) {
+ DNSServiceRefDeallocate(mService);
+ mService = nullptr;
+ }
+ mOperatorHolder = nullptr;
+ }
+
+ nsresult PostEvent(const char* aName, void (ServiceWatcher::*func)(void)) {
+ return gSocketTransportService->Dispatch(
+ NewRunnableMethod(aName, this, func), NS_DISPATCH_NORMAL);
+ }
+
+ void OnMsgClose() {
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+
+ if (NS_FAILED(mCondition)) {
+ return;
+ }
+
+ // tear down socket. this signals the STS to detach our socket handler.
+ mCondition = NS_BINDING_ABORTED;
+
+ // if we are attached, then socket transport service will call our
+ // OnSocketDetached method automatically. Otherwise, we have to call it
+ // (and thus close the socket) manually.
+ if (!mAttached) {
+ OnSocketDetached(mFD);
+ }
+ }
+
+ void OnMsgAttach() {
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+
+ if (NS_FAILED(mCondition)) {
+ return;
+ }
+
+ mCondition = TryAttach();
+
+ // if we hit an error while trying to attach then bail...
+ if (NS_FAILED(mCondition)) {
+ NS_ASSERTION(!mAttached, "should not be attached already");
+ OnSocketDetached(mFD);
+ }
+ }
+
+ nsresult TryAttach() {
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+
+ nsresult rv;
+
+ if (!gSocketTransportService) {
+ return NS_ERROR_FAILURE;
+ }
+
+ //
+ // find out if it is going to be ok to attach another socket to the STS.
+ // if not then we have to wait for the STS to tell us that it is ok.
+ // the notification is asynchronous, which means that when we could be
+ // in a race to call AttachSocket once notified. for this reason, when
+ // we get notified, we just re-enter this function. as a result, we are
+ // sure to ask again before calling AttachSocket. in this way we deal
+ // with the race condition. though it isn't the most elegant solution,
+ // it is far simpler than trying to build a system that would guarantee
+ // FIFO ordering (which wouldn't even be that valuable IMO). see bug
+ // 194402 for more info.
+ //
+ if (!gSocketTransportService->CanAttachSocket()) {
+ nsCOMPtr<nsIRunnable> event = NewRunnableMethod(
+ "MDNSResponderOperator::ServiceWatcher::OnMsgAttach", this,
+ &ServiceWatcher::OnMsgAttach);
+
+ nsresult rv = gSocketTransportService->NotifyWhenCanAttachSocket(event);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ //
+ // ok, we can now attach our socket to the STS for polling
+ //
+ rv = gSocketTransportService->AttachSocket(mFD, this);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ mAttached = true;
+
+ //
+ // now, configure our poll flags for listening...
+ //
+ mPollFlags = (PR_POLL_READ | PR_POLL_EXCEPT);
+
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIThread> mThread;
+ RefPtr<nsSocketTransportService> mSts;
+ RefPtr<MDNSResponderOperator> mOperatorHolder;
+ DNSServiceRef mService;
+ PRFileDesc* mFD;
+ bool mAttached;
+};
+
+NS_IMPL_ISUPPORTS(MDNSResponderOperator::ServiceWatcher, nsISupports)
+
+MDNSResponderOperator::MDNSResponderOperator()
+ : mService(nullptr),
+ mWatcher(nullptr),
+ mThread(NS_GetCurrentThread()),
+ mIsCancelled(false) {}
+
+MDNSResponderOperator::~MDNSResponderOperator() { Stop(); }
+
+nsresult MDNSResponderOperator::Start() {
+ if (mIsCancelled) {
+ return NS_OK;
+ }
+
+ if (IsServing()) {
+ Stop();
+ }
+
+ return NS_OK;
+}
+
+nsresult MDNSResponderOperator::Stop() { return ResetService(nullptr); }
+
+nsresult MDNSResponderOperator::ResetService(DNSServiceRef aService) {
+ nsresult rv;
+
+ if (aService != mService) {
+ if (mWatcher) {
+ mWatcher->Close();
+ mWatcher = nullptr;
+ }
+
+ if (aService) {
+ RefPtr<ServiceWatcher> watcher = new ServiceWatcher(aService, this);
+ if (NS_WARN_IF(NS_FAILED(rv = watcher->Init()))) {
+ return rv;
+ }
+ mWatcher = watcher;
+ }
+
+ mService = aService;
+ }
+ return NS_OK;
+}
+
+BrowseOperator::BrowseOperator(const nsACString& aServiceType,
+ nsIDNSServiceDiscoveryListener* aListener)
+ : MDNSResponderOperator(),
+ mServiceType(aServiceType),
+ mListener(aListener) {}
+
+nsresult BrowseOperator::Start() {
+ nsresult rv;
+ if (NS_WARN_IF(NS_FAILED(rv = MDNSResponderOperator::Start()))) {
+ return rv;
+ }
+
+ DNSServiceRef service = nullptr;
+ DNSServiceErrorType err = DNSServiceBrowse(
+ &service, 0, kDNSServiceInterfaceIndexAny, mServiceType.get(), nullptr,
+ &BrowseReplyRunnable::Reply, this);
+ NS_WARNING_ASSERTION(kDNSServiceErr_NoError == err, "DNSServiceBrowse fail");
+
+ if (mListener) {
+ if (kDNSServiceErr_NoError == err) {
+ mListener->OnDiscoveryStarted(mServiceType);
+ } else {
+ mListener->OnStartDiscoveryFailed(mServiceType, err);
+ }
+ }
+
+ if (NS_WARN_IF(kDNSServiceErr_NoError != err)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return ResetService(service);
+}
+
+nsresult BrowseOperator::Stop() {
+ bool isServing = IsServing();
+ nsresult rv = MDNSResponderOperator::Stop();
+
+ if (isServing && mListener) {
+ if (NS_SUCCEEDED(rv)) {
+ mListener->OnDiscoveryStopped(mServiceType);
+ } else {
+ mListener->OnStopDiscoveryFailed(mServiceType, static_cast<uint32_t>(rv));
+ }
+ }
+
+ return rv;
+}
+
+void BrowseOperator::Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode,
+ const nsACString& aServiceName,
+ const nsACString& aRegType,
+ const nsACString& aReplyDomain) {
+ MOZ_ASSERT(GetThread() == NS_GetCurrentThread());
+
+ if (NS_WARN_IF(kDNSServiceErr_NoError != aErrorCode)) {
+ LOG_E("BrowseOperator::Reply (%d)", aErrorCode);
+ if (mListener) {
+ mListener->OnStartDiscoveryFailed(mServiceType, aErrorCode);
+ }
+ return;
+ }
+
+ if (!mListener) {
+ return;
+ }
+ nsCOMPtr<nsIDNSServiceInfo> info = new nsDNSServiceInfo();
+
+ if (NS_WARN_IF(!info)) {
+ return;
+ }
+ if (NS_WARN_IF(NS_FAILED(info->SetServiceName(aServiceName)))) {
+ return;
+ }
+ if (NS_WARN_IF(NS_FAILED(info->SetServiceType(aRegType)))) {
+ return;
+ }
+ if (NS_WARN_IF(NS_FAILED(info->SetDomainName(aReplyDomain)))) {
+ return;
+ }
+
+ if (aFlags & kDNSServiceFlagsAdd) {
+ mListener->OnServiceFound(info);
+ } else {
+ mListener->OnServiceLost(info);
+ }
+}
+
+RegisterOperator::RegisterOperator(nsIDNSServiceInfo* aServiceInfo,
+ nsIDNSRegistrationListener* aListener)
+ : MDNSResponderOperator(),
+ mServiceInfo(aServiceInfo),
+ mListener(aListener) {}
+
+nsresult RegisterOperator::Start() {
+ nsresult rv;
+
+ rv = MDNSResponderOperator::Start();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ uint16_t port;
+ if (NS_WARN_IF(NS_FAILED(rv = mServiceInfo->GetPort(&port)))) {
+ return rv;
+ }
+ nsAutoCString type;
+ if (NS_WARN_IF(NS_FAILED(rv = mServiceInfo->GetServiceType(type)))) {
+ return rv;
+ }
+
+ TXTRecordRef txtRecord;
+ char buf[TXT_BUFFER_SIZE] = {0};
+ TXTRecordCreate(&txtRecord, TXT_BUFFER_SIZE, buf);
+
+ nsCOMPtr<nsIPropertyBag2> attributes;
+ if (NS_FAILED(rv = mServiceInfo->GetAttributes(getter_AddRefs(attributes)))) {
+ LOG_I("register: no attributes");
+ } else {
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ if (NS_WARN_IF(NS_FAILED(
+ rv = attributes->GetEnumerator(getter_AddRefs(enumerator))))) {
+ return rv;
+ }
+
+ bool hasMoreElements;
+ while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreElements)) &&
+ hasMoreElements) {
+ nsCOMPtr<nsISupports> element;
+ MOZ_ALWAYS_SUCCEEDS(enumerator->GetNext(getter_AddRefs(element)));
+ nsCOMPtr<nsIProperty> property = do_QueryInterface(element);
+ MOZ_ASSERT(property);
+
+ nsAutoString name;
+ nsCOMPtr<nsIVariant> value;
+ MOZ_ALWAYS_SUCCEEDS(property->GetName(name));
+ MOZ_ALWAYS_SUCCEEDS(property->GetValue(getter_AddRefs(value)));
+
+ nsAutoCString str;
+ if (NS_WARN_IF(NS_FAILED(value->GetAsACString(str)))) {
+ continue;
+ }
+
+ TXTRecordSetValue(&txtRecord,
+ /* it's safe because key name is ASCII only. */
+ NS_LossyConvertUTF16toASCII(name).get(), str.Length(),
+ str.get());
+ }
+ }
+
+ nsAutoCString host;
+ nsAutoCString name;
+ nsAutoCString domain;
+
+ DNSServiceRef service = nullptr;
+ DNSServiceErrorType err = DNSServiceRegister(
+ &service, 0, 0,
+ NS_SUCCEEDED(mServiceInfo->GetServiceName(name)) ? name.get() : nullptr,
+ type.get(),
+ NS_SUCCEEDED(mServiceInfo->GetDomainName(domain)) ? domain.get()
+ : nullptr,
+ NS_SUCCEEDED(mServiceInfo->GetHost(host)) ? host.get() : nullptr,
+ NativeEndian::swapToNetworkOrder(port), TXTRecordGetLength(&txtRecord),
+ TXTRecordGetBytesPtr(&txtRecord), &RegisterReplyRunnable::Reply, this);
+ NS_WARNING_ASSERTION(kDNSServiceErr_NoError == err,
+ "DNSServiceRegister fail");
+
+ TXTRecordDeallocate(&txtRecord);
+
+ if (NS_WARN_IF(kDNSServiceErr_NoError != err)) {
+ if (mListener) {
+ mListener->OnRegistrationFailed(mServiceInfo, err);
+ }
+ return NS_ERROR_FAILURE;
+ }
+
+ return ResetService(service);
+}
+
+nsresult RegisterOperator::Stop() {
+ bool isServing = IsServing();
+ nsresult rv = MDNSResponderOperator::Stop();
+
+ if (isServing && mListener) {
+ if (NS_SUCCEEDED(rv)) {
+ mListener->OnServiceUnregistered(mServiceInfo);
+ } else {
+ mListener->OnUnregistrationFailed(mServiceInfo,
+ static_cast<uint32_t>(rv));
+ }
+ }
+
+ return rv;
+}
+
+void RegisterOperator::Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags,
+ DNSServiceErrorType aErrorCode,
+ const nsACString& aName,
+ const nsACString& aRegType,
+ const nsACString& aDomain) {
+ MOZ_ASSERT(GetThread() == NS_GetCurrentThread());
+
+ if (kDNSServiceErr_NoError != aErrorCode) {
+ LOG_E("RegisterOperator::Reply (%d)", aErrorCode);
+ }
+
+ if (!mListener) {
+ return;
+ }
+ nsCOMPtr<nsIDNSServiceInfo> info = new nsDNSServiceInfo(mServiceInfo);
+ if (NS_WARN_IF(NS_FAILED(info->SetServiceName(aName)))) {
+ return;
+ }
+ if (NS_WARN_IF(NS_FAILED(info->SetServiceType(aRegType)))) {
+ return;
+ }
+ if (NS_WARN_IF(NS_FAILED(info->SetDomainName(aDomain)))) {
+ return;
+ }
+
+ if (kDNSServiceErr_NoError == aErrorCode) {
+ if (aFlags & kDNSServiceFlagsAdd) {
+ mListener->OnServiceRegistered(info);
+ } else {
+ // If a successfully-registered name later suffers a name conflict
+ // or similar problem and has to be deregistered, the callback will
+ // be invoked with the kDNSServiceFlagsAdd flag not set.
+ LOG_E("RegisterOperator::Reply: deregister");
+ if (NS_WARN_IF(NS_FAILED(Stop()))) {
+ return;
+ }
+ }
+ } else {
+ mListener->OnRegistrationFailed(info, aErrorCode);
+ }
+}
+
+ResolveOperator::ResolveOperator(nsIDNSServiceInfo* aServiceInfo,
+ nsIDNSServiceResolveListener* aListener)
+ : MDNSResponderOperator(),
+ mServiceInfo(aServiceInfo),
+ mListener(aListener) {}
+
+nsresult ResolveOperator::Start() {
+ nsresult rv;
+ if (NS_WARN_IF(NS_FAILED(rv = MDNSResponderOperator::Start()))) {
+ return rv;
+ }
+
+ nsAutoCString name;
+ mServiceInfo->GetServiceName(name);
+ nsAutoCString type;
+ mServiceInfo->GetServiceType(type);
+ nsAutoCString domain;
+ mServiceInfo->GetDomainName(domain);
+
+ LOG_I("Resolve: (%s), (%s), (%s)", name.get(), type.get(), domain.get());
+
+ DNSServiceRef service = nullptr;
+ DNSServiceErrorType err = DNSServiceResolve(
+ &service, 0, kDNSServiceInterfaceIndexAny, name.get(), type.get(),
+ domain.get(), (DNSServiceResolveReply)&ResolveReplyRunnable::Reply, this);
+
+ if (NS_WARN_IF(kDNSServiceErr_NoError != err)) {
+ if (mListener) {
+ mListener->OnResolveFailed(mServiceInfo, err);
+ }
+ return NS_ERROR_FAILURE;
+ }
+
+ return ResetService(service);
+}
+
+void ResolveOperator::Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode,
+ const nsACString& aFullName,
+ const nsACString& aHostTarget, uint16_t aPort,
+ uint16_t aTxtLen, const unsigned char* aTxtRecord) {
+ MOZ_ASSERT(GetThread() == NS_GetCurrentThread());
+
+ auto guard = MakeScopeExit([&] { Unused << NS_WARN_IF(NS_FAILED(Stop())); });
+
+ if (NS_WARN_IF(kDNSServiceErr_NoError != aErrorCode)) {
+ LOG_E("ResolveOperator::Reply (%d)", aErrorCode);
+ return;
+ }
+
+ // Resolve TXT record
+ int count = TXTRecordGetCount(aTxtLen, aTxtRecord);
+ LOG_I("resolve: txt count = %d, len = %d", count, aTxtLen);
+ nsCOMPtr<nsIWritablePropertyBag2> attributes = new nsHashPropertyBag();
+ if (NS_WARN_IF(!attributes)) {
+ return;
+ }
+ if (count) {
+ for (int i = 0; i < count; ++i) {
+ char key[TXT_BUFFER_SIZE] = {'\0'};
+ uint8_t vSize = 0;
+ const void* value = nullptr;
+ if (kDNSServiceErr_NoError !=
+ TXTRecordGetItemAtIndex(aTxtLen, aTxtRecord, i, TXT_BUFFER_SIZE, key,
+ &vSize, &value)) {
+ break;
+ }
+
+ nsAutoCString str(reinterpret_cast<const char*>(value), vSize);
+ LOG_I("resolve TXT: (%d) %s=%s", vSize, key, str.get());
+
+ if (NS_WARN_IF(NS_FAILED(attributes->SetPropertyAsACString(
+ /* it's safe to convert because key name is ASCII only. */
+ NS_ConvertASCIItoUTF16(key), str)))) {
+ break;
+ }
+ }
+ }
+
+ if (!mListener) {
+ return;
+ }
+ nsCOMPtr<nsIDNSServiceInfo> info = new nsDNSServiceInfo(mServiceInfo);
+ if (NS_WARN_IF(NS_FAILED(info->SetHost(aHostTarget)))) {
+ return;
+ }
+ if (NS_WARN_IF(NS_FAILED(info->SetPort(aPort)))) {
+ return;
+ }
+ if (NS_WARN_IF(NS_FAILED(info->SetAttributes(attributes)))) {
+ return;
+ }
+
+ if (kDNSServiceErr_NoError == aErrorCode) {
+ GetAddrInfor(info);
+ } else {
+ mListener->OnResolveFailed(info, aErrorCode);
+ Unused << NS_WARN_IF(NS_FAILED(Stop()));
+ }
+}
+
+void ResolveOperator::GetAddrInfor(nsIDNSServiceInfo* aServiceInfo) {
+ RefPtr<GetAddrInfoOperator> getAddreOp =
+ new GetAddrInfoOperator(aServiceInfo, mListener);
+ Unused << NS_WARN_IF(NS_FAILED(getAddreOp->Start()));
+}
+
+GetAddrInfoOperator::GetAddrInfoOperator(
+ nsIDNSServiceInfo* aServiceInfo, nsIDNSServiceResolveListener* aListener)
+ : MDNSResponderOperator(),
+ mServiceInfo(aServiceInfo),
+ mListener(aListener) {}
+
+nsresult GetAddrInfoOperator::Start() {
+ nsresult rv;
+ if (NS_WARN_IF(NS_FAILED(rv = MDNSResponderOperator::Start()))) {
+ return rv;
+ }
+
+ nsAutoCString host;
+ mServiceInfo->GetHost(host);
+
+ LOG_I("GetAddrInfo: (%s)", host.get());
+
+ DNSServiceRef service = nullptr;
+ DNSServiceErrorType err = DNSServiceGetAddrInfo(
+ &service, kDNSServiceFlagsForceMulticast, kDNSServiceInterfaceIndexAny,
+ kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, host.get(),
+ (DNSServiceGetAddrInfoReply)&GetAddrInfoReplyRunnable::Reply, this);
+
+ if (NS_WARN_IF(kDNSServiceErr_NoError != err)) {
+ if (mListener) {
+ mListener->OnResolveFailed(mServiceInfo, err);
+ }
+ return NS_ERROR_FAILURE;
+ }
+
+ return ResetService(service);
+}
+
+void GetAddrInfoOperator::Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode,
+ const nsACString& aHostName,
+ const NetAddr& aAddress, uint32_t aTTL) {
+ MOZ_ASSERT(GetThread() == NS_GetCurrentThread());
+
+ auto guard = MakeScopeExit([&] { Unused << NS_WARN_IF(NS_FAILED(Stop())); });
+
+ if (NS_WARN_IF(kDNSServiceErr_NoError != aErrorCode)) {
+ LOG_E("GetAddrInfoOperator::Reply (%d)", aErrorCode);
+ return;
+ }
+
+ if (!mListener) {
+ return;
+ }
+
+ NetAddr addr = aAddress;
+ nsCOMPtr<nsINetAddr> address = new nsNetAddr(&addr);
+ nsCString addressStr;
+ if (NS_WARN_IF(NS_FAILED(address->GetAddress(addressStr)))) {
+ return;
+ }
+
+ nsCOMPtr<nsIDNSServiceInfo> info = new nsDNSServiceInfo(mServiceInfo);
+ if (NS_WARN_IF(NS_FAILED(info->SetAddress(addressStr)))) {
+ return;
+ }
+
+ /**
+ * |kDNSServiceFlagsMoreComing| means this callback will be one or more
+ * callback events later, so this instance should be kept alive until all
+ * follow-up events are processed.
+ */
+ if (aFlags & kDNSServiceFlagsMoreComing) {
+ guard.release();
+ }
+
+ if (kDNSServiceErr_NoError == aErrorCode) {
+ mListener->OnServiceResolved(info);
+ } else {
+ mListener->OnResolveFailed(info, aErrorCode);
+ }
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/mdns/libmdns/MDNSResponderOperator.h b/netwerk/dns/mdns/libmdns/MDNSResponderOperator.h
new file mode 100644
index 0000000000..394f68777c
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/MDNSResponderOperator.h
@@ -0,0 +1,133 @@
+/* -*- 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_netwerk_dns_mdns_libmdns_MDNSResponderOperator_h
+#define mozilla_netwerk_dns_mdns_libmdns_MDNSResponderOperator_h
+
+#include "dns_sd.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/RefPtr.h"
+#include "nsCOMPtr.h"
+#include "nsIDNSServiceDiscovery.h"
+#include "nsIThread.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace net {
+
+class MDNSResponderOperator {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MDNSResponderOperator)
+
+ public:
+ MDNSResponderOperator();
+
+ virtual nsresult Start();
+ virtual nsresult Stop();
+ void Cancel() { mIsCancelled = true; }
+ nsIThread* GetThread() const { return mThread; }
+
+ protected:
+ virtual ~MDNSResponderOperator();
+
+ bool IsServing() const { return mService; }
+ nsresult ResetService(DNSServiceRef aService);
+
+ private:
+ class ServiceWatcher;
+
+ DNSServiceRef mService;
+ RefPtr<ServiceWatcher> mWatcher;
+ nsCOMPtr<nsIThread> mThread; // remember caller thread for callback
+ Atomic<bool> mIsCancelled;
+};
+
+class BrowseOperator final : public MDNSResponderOperator {
+ public:
+ BrowseOperator(const nsACString& aServiceType,
+ nsIDNSServiceDiscoveryListener* aListener);
+
+ nsresult Start() override;
+ nsresult Stop() override;
+
+ void Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex, DNSServiceErrorType aErrorCode,
+ const nsACString& aServiceName, const nsACString& aRegType,
+ const nsACString& aReplyDomain);
+
+ private:
+ ~BrowseOperator() = default;
+
+ nsCString mServiceType;
+ nsCOMPtr<nsIDNSServiceDiscoveryListener> mListener;
+};
+
+class RegisterOperator final : public MDNSResponderOperator {
+ enum { TXT_BUFFER_SIZE = 256 };
+
+ public:
+ RegisterOperator(nsIDNSServiceInfo* aServiceInfo,
+ nsIDNSRegistrationListener* aListener);
+
+ nsresult Start() override;
+ nsresult Stop() override;
+
+ void Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags,
+ DNSServiceErrorType aErrorCode, const nsACString& aName,
+ const nsACString& aRegType, const nsACString& aDomain);
+
+ private:
+ ~RegisterOperator() = default;
+
+ nsCOMPtr<nsIDNSServiceInfo> mServiceInfo;
+ nsCOMPtr<nsIDNSRegistrationListener> mListener;
+};
+
+class ResolveOperator final : public MDNSResponderOperator {
+ enum { TXT_BUFFER_SIZE = 256 };
+
+ public:
+ ResolveOperator(nsIDNSServiceInfo* aServiceInfo,
+ nsIDNSServiceResolveListener* aListener);
+
+ nsresult Start() override;
+
+ void Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex, DNSServiceErrorType aErrorCode,
+ const nsACString& aFullName, const nsACString& aHostTarget,
+ uint16_t aPort, uint16_t aTxtLen, const unsigned char* aTxtRecord);
+
+ private:
+ ~ResolveOperator() = default;
+ void GetAddrInfor(nsIDNSServiceInfo* aServiceInfo);
+
+ nsCOMPtr<nsIDNSServiceInfo> mServiceInfo;
+ nsCOMPtr<nsIDNSServiceResolveListener> mListener;
+};
+
+union NetAddr;
+
+class GetAddrInfoOperator final : public MDNSResponderOperator {
+ public:
+ GetAddrInfoOperator(nsIDNSServiceInfo* aServiceInfo,
+ nsIDNSServiceResolveListener* aListener);
+
+ nsresult Start() override;
+
+ void Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex, DNSServiceErrorType aErrorCode,
+ const nsACString& aHostName, const NetAddr& aAddress,
+ uint32_t aTTL);
+
+ private:
+ ~GetAddrInfoOperator() = default;
+
+ nsCOMPtr<nsIDNSServiceInfo> mServiceInfo;
+ nsCOMPtr<nsIDNSServiceResolveListener> mListener;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_netwerk_dns_mdns_libmdns_MDNSResponderOperator_h
diff --git a/netwerk/dns/mdns/libmdns/MDNSResponderReply.cpp b/netwerk/dns/mdns/libmdns/MDNSResponderReply.cpp
new file mode 100644
index 0000000000..ee08a87211
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/MDNSResponderReply.cpp
@@ -0,0 +1,215 @@
+/* -*- 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 "MDNSResponderReply.h"
+#include "mozilla/EndianUtils.h"
+#include "private/pprio.h"
+
+namespace mozilla {
+namespace net {
+
+BrowseReplyRunnable::BrowseReplyRunnable(
+ DNSServiceRef aSdRef, DNSServiceFlags aFlags, uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode, const nsACString& aServiceName,
+ const nsACString& aRegType, const nsACString& aReplyDomain,
+ BrowseOperator* aContext)
+ : Runnable("net::BrowseReplyRunnable"),
+ mSdRef(aSdRef),
+ mFlags(aFlags),
+ mInterfaceIndex(aInterfaceIndex),
+ mErrorCode(aErrorCode),
+ mServiceName(aServiceName),
+ mRegType(aRegType),
+ mReplyDomain(aReplyDomain),
+ mContext(aContext) {}
+
+NS_IMETHODIMP
+BrowseReplyRunnable::Run() {
+ MOZ_ASSERT(mContext);
+ mContext->Reply(mSdRef, mFlags, mInterfaceIndex, mErrorCode, mServiceName,
+ mRegType, mReplyDomain);
+ return NS_OK;
+}
+
+void BrowseReplyRunnable::Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode,
+ const char* aServiceName, const char* aRegType,
+ const char* aReplyDomain, void* aContext) {
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+
+ BrowseOperator* obj(reinterpret_cast<BrowseOperator*>(aContext));
+ if (!obj) {
+ return;
+ }
+
+ nsCOMPtr<nsIThread> thread(obj->GetThread());
+ if (!thread) {
+ return;
+ }
+
+ thread->Dispatch(
+ new BrowseReplyRunnable(aSdRef, aFlags, aInterfaceIndex, aErrorCode,
+ nsCString(aServiceName), nsCString(aRegType),
+ nsCString(aReplyDomain), obj),
+ NS_DISPATCH_NORMAL);
+}
+
+RegisterReplyRunnable::RegisterReplyRunnable(DNSServiceRef aSdRef,
+ DNSServiceFlags aFlags,
+ DNSServiceErrorType aErrorCode,
+ const nsACString& aName,
+ const nsACString& aRegType,
+ const nsACString& domain,
+ RegisterOperator* aContext)
+ : Runnable("net::RegisterReplyRunnable"),
+ mSdRef(aSdRef),
+ mFlags(aFlags),
+ mErrorCode(aErrorCode),
+ mName(aName),
+ mRegType(aRegType),
+ mDomain(domain),
+ mContext(aContext) {}
+
+NS_IMETHODIMP
+RegisterReplyRunnable::Run() {
+ MOZ_ASSERT(mContext);
+
+ mContext->Reply(mSdRef, mFlags, mErrorCode, mName, mRegType, mDomain);
+ return NS_OK;
+}
+
+void RegisterReplyRunnable::Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags,
+ DNSServiceErrorType aErrorCode,
+ const char* aName, const char* aRegType,
+ const char* domain, void* aContext) {
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+
+ RegisterOperator* obj(reinterpret_cast<RegisterOperator*>(aContext));
+ if (!obj) {
+ return;
+ }
+
+ nsCOMPtr<nsIThread> thread(obj->GetThread());
+ if (!thread) {
+ return;
+ }
+
+ thread->Dispatch(
+ new RegisterReplyRunnable(aSdRef, aFlags, aErrorCode, nsCString(aName),
+ nsCString(aRegType), nsCString(domain), obj),
+ NS_DISPATCH_NORMAL);
+}
+
+ResolveReplyRunnable::ResolveReplyRunnable(
+ DNSServiceRef aSdRef, DNSServiceFlags aFlags, uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode, const nsACString& aFullName,
+ const nsACString& aHostTarget, uint16_t aPort, uint16_t aTxtLen,
+ const unsigned char* aTxtRecord, ResolveOperator* aContext)
+ : Runnable("net::ResolveReplyRunnable"),
+ mSdRef(aSdRef),
+ mFlags(aFlags),
+ mInterfaceIndex(aInterfaceIndex),
+ mErrorCode(aErrorCode),
+ mFullname(aFullName),
+ mHosttarget(aHostTarget),
+ mPort(aPort),
+ mTxtLen(aTxtLen),
+ mTxtRecord(new unsigned char[aTxtLen]),
+ mContext(aContext) {
+ if (mTxtRecord) {
+ memcpy(mTxtRecord.get(), aTxtRecord, aTxtLen);
+ }
+}
+
+NS_IMETHODIMP
+ResolveReplyRunnable::Run() {
+ MOZ_ASSERT(mContext);
+ mContext->Reply(mSdRef, mFlags, mInterfaceIndex, mErrorCode, mFullname,
+ mHosttarget, mPort, mTxtLen, mTxtRecord.get());
+ return NS_OK;
+}
+
+void ResolveReplyRunnable::Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode,
+ const char* aFullName, const char* aHostTarget,
+ uint16_t aPort, uint16_t aTxtLen,
+ const unsigned char* aTxtRecord,
+ void* aContext) {
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+
+ ResolveOperator* obj(reinterpret_cast<ResolveOperator*>(aContext));
+ if (!obj) {
+ return;
+ }
+
+ nsCOMPtr<nsIThread> thread(obj->GetThread());
+ if (!thread) {
+ return;
+ }
+
+ thread->Dispatch(
+ new ResolveReplyRunnable(aSdRef, aFlags, aInterfaceIndex, aErrorCode,
+ nsCString(aFullName), nsCString(aHostTarget),
+ NativeEndian::swapFromNetworkOrder(aPort),
+ aTxtLen, aTxtRecord, obj),
+ NS_DISPATCH_NORMAL);
+}
+
+GetAddrInfoReplyRunnable::GetAddrInfoReplyRunnable(
+ DNSServiceRef aSdRef, DNSServiceFlags aFlags, uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode, const nsACString& aHostName,
+ const mozilla::net::NetAddr& aAddress, uint32_t aTTL,
+ GetAddrInfoOperator* aContext)
+ : Runnable("net::GetAddrInfoReplyRunnable"),
+ mSdRef(aSdRef),
+ mFlags(aFlags),
+ mInterfaceIndex(aInterfaceIndex),
+ mErrorCode(aErrorCode),
+ mHostName(aHostName),
+ mAddress(aAddress),
+ mTTL(aTTL),
+ mContext(aContext) {}
+
+NS_IMETHODIMP
+GetAddrInfoReplyRunnable::Run() {
+ MOZ_ASSERT(mContext);
+ mContext->Reply(mSdRef, mFlags, mInterfaceIndex, mErrorCode, mHostName,
+ mAddress, mTTL);
+ return NS_OK;
+}
+
+void GetAddrInfoReplyRunnable::Reply(
+ DNSServiceRef aSdRef, DNSServiceFlags aFlags, uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode, const char* aHostName,
+ const struct sockaddr* aAddress, uint32_t aTTL, void* aContext) {
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+
+ GetAddrInfoOperator* obj(reinterpret_cast<GetAddrInfoOperator*>(aContext));
+ if (!obj) {
+ return;
+ }
+
+ nsCOMPtr<nsIThread> thread(obj->GetThread());
+ if (!thread) {
+ return;
+ }
+
+ NetAddr address;
+ address.raw.family = aAddress->sa_family;
+
+ static_assert(sizeof(address.raw.data) >= sizeof(aAddress->sa_data),
+ "size of sockaddr.sa_data is too big");
+ memcpy(&address.raw.data, aAddress->sa_data, sizeof(aAddress->sa_data));
+
+ thread->Dispatch(
+ new GetAddrInfoReplyRunnable(aSdRef, aFlags, aInterfaceIndex, aErrorCode,
+ nsCString(aHostName), address, aTTL, obj),
+ NS_DISPATCH_NORMAL);
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/mdns/libmdns/MDNSResponderReply.h b/netwerk/dns/mdns/libmdns/MDNSResponderReply.h
new file mode 100644
index 0000000000..120bb6dae7
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/MDNSResponderReply.h
@@ -0,0 +1,130 @@
+/* -*- 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_netwerk_dns_mdns_libmdns_MDNSResponderReply_h
+#define mozilla_netwerk_dns_mdns_libmdns_MDNSResponderReply_h
+
+#include "dns_sd.h"
+#include "MDNSResponderOperator.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/net/DNS.h"
+#include "mozilla/RefPtr.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace net {
+
+class BrowseReplyRunnable final : public Runnable {
+ public:
+ BrowseReplyRunnable(DNSServiceRef aSdRef, DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex, DNSServiceErrorType aErrorCode,
+ const nsACString& aServiceName,
+ const nsACString& aRegType,
+ const nsACString& aReplyDomain, BrowseOperator* aContext);
+
+ NS_IMETHOD Run() override;
+
+ static void Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex, DNSServiceErrorType aErrorCode,
+ const char* aServiceName, const char* aRegType,
+ const char* aReplyDomain, void* aContext);
+
+ private:
+ DNSServiceRef mSdRef;
+ DNSServiceFlags mFlags;
+ uint32_t mInterfaceIndex;
+ DNSServiceErrorType mErrorCode;
+ nsCString mServiceName;
+ nsCString mRegType;
+ nsCString mReplyDomain;
+ RefPtr<BrowseOperator> mContext;
+};
+
+class RegisterReplyRunnable final : public Runnable {
+ public:
+ RegisterReplyRunnable(DNSServiceRef aSdRef, DNSServiceFlags aFlags,
+ DNSServiceErrorType aErrorCode, const nsACString& aName,
+ const nsACString& aRegType, const nsACString& aDomain,
+ RegisterOperator* aContext);
+
+ NS_IMETHOD Run() override;
+
+ static void Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags,
+ DNSServiceErrorType aErrorCode, const char* aName,
+ const char* aRegType, const char* aDomain, void* aContext);
+
+ private:
+ DNSServiceRef mSdRef;
+ DNSServiceFlags mFlags;
+ DNSServiceErrorType mErrorCode;
+ nsCString mName;
+ nsCString mRegType;
+ nsCString mDomain;
+ RefPtr<RegisterOperator> mContext;
+};
+
+class ResolveReplyRunnable final : public Runnable {
+ public:
+ ResolveReplyRunnable(DNSServiceRef aSdRef, DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex, DNSServiceErrorType aErrorCode,
+ const nsACString& aFullName,
+ const nsACString& aHostTarget, uint16_t aPort,
+ uint16_t aTxtLen, const unsigned char* aTxtRecord,
+ ResolveOperator* aContext);
+ ~ResolveReplyRunnable() = default;
+
+ NS_IMETHOD Run() override;
+
+ static void Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex, DNSServiceErrorType aErrorCode,
+ const char* aFullName, const char* aHostTarget,
+ uint16_t aPort, uint16_t aTxtLen,
+ const unsigned char* aTxtRecord, void* aContext);
+
+ private:
+ DNSServiceRef mSdRef;
+ DNSServiceFlags mFlags;
+ uint32_t mInterfaceIndex;
+ DNSServiceErrorType mErrorCode;
+ nsCString mFullname;
+ nsCString mHosttarget;
+ uint16_t mPort;
+ uint16_t mTxtLen;
+ UniquePtr<unsigned char> mTxtRecord;
+ RefPtr<ResolveOperator> mContext;
+};
+
+class GetAddrInfoReplyRunnable final : public Runnable {
+ public:
+ GetAddrInfoReplyRunnable(DNSServiceRef aSdRef, DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode,
+ const nsACString& aHostName,
+ const mozilla::net::NetAddr& aAddress, uint32_t aTTL,
+ GetAddrInfoOperator* aContext);
+ ~GetAddrInfoReplyRunnable() = default;
+
+ NS_IMETHOD Run() override;
+
+ static void Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex, DNSServiceErrorType aErrorCode,
+ const char* aHostName, const struct sockaddr* aAddress,
+ uint32_t aTTL, void* aContext);
+
+ private:
+ DNSServiceRef mSdRef;
+ DNSServiceFlags mFlags;
+ uint32_t mInterfaceIndex;
+ DNSServiceErrorType mErrorCode;
+ nsCString mHostName;
+ mozilla::net::NetAddr mAddress;
+ uint32_t mTTL;
+ RefPtr<GetAddrInfoOperator> mContext;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_netwerk_dns_mdns_libmdns_MDNSResponderReply_h
diff --git a/netwerk/dns/mdns/libmdns/MulticastDNSAndroid.jsm b/netwerk/dns/mdns/libmdns/MulticastDNSAndroid.jsm
new file mode 100644
index 0000000000..d94e90efaa
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/MulticastDNSAndroid.jsm
@@ -0,0 +1,262 @@
+// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
+/* 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/. */
+
+"use strict";
+
+var EXPORTED_SYMBOLS = ["MulticastDNS"];
+
+const { EventDispatcher } = ChromeUtils.import(
+ "resource://gre/modules/Messaging.jsm"
+);
+
+const DEBUG = false;
+
+var log = function(s) {};
+if (DEBUG) {
+ log = ChromeUtils.import(
+ "resource://gre/modules/AndroidLog.jsm",
+ {}
+ ).AndroidLog.d.bind(null, "MulticastDNS");
+}
+
+const FAILURE_INTERNAL_ERROR = -65537;
+
+// Helper function for sending commands to Java.
+function send(type, data, callback) {
+ let msg = {
+ type,
+ };
+
+ for (let i in data) {
+ try {
+ msg[i] = data[i];
+ } catch (e) {}
+ }
+
+ EventDispatcher.instance.sendRequestForResult(msg).then(
+ result => callback(result, null),
+ err =>
+ callback(null, typeof err === "number" ? err : FAILURE_INTERNAL_ERROR)
+ );
+}
+
+// Receives service found/lost event from NsdManager
+function ServiceManager() {}
+
+ServiceManager.prototype = {
+ listeners: {},
+ numListeners: 0,
+
+ onEvent(event, data, callback) {
+ if (event === "NsdManager:ServiceFound") {
+ this.onServiceFound();
+ } else if (event === "NsdManager:ServiceLost") {
+ this.onServiceLost();
+ }
+ },
+
+ registerEvent() {
+ log("registerEvent");
+ EventDispatcher.instance.registerListener(this, [
+ "NsdManager:ServiceFound",
+ "NsdManager:ServiceLost",
+ ]);
+ },
+
+ unregisterEvent() {
+ log("unregisterEvent");
+ EventDispatcher.instance.unregisterListener(this, [
+ "NsdManager:ServiceFound",
+ "NsdManager:ServiceLost",
+ ]);
+ },
+
+ addListener(aServiceType, aListener) {
+ log("addListener: " + aServiceType + ", " + aListener);
+
+ if (!this.listeners[aServiceType]) {
+ this.listeners[aServiceType] = [];
+ }
+ if (this.listeners[aServiceType].includes(aListener)) {
+ log("listener already exists");
+ return;
+ }
+
+ this.listeners[aServiceType].push(aListener);
+ ++this.numListeners;
+
+ if (this.numListeners === 1) {
+ this.registerEvent();
+ }
+
+ log("listener added: " + this);
+ },
+
+ removeListener(aServiceType, aListener) {
+ log("removeListener: " + aServiceType + ", " + aListener);
+
+ if (!this.listeners[aServiceType]) {
+ log("listener doesn't exist");
+ return;
+ }
+ let index = this.listeners[aServiceType].indexOf(aListener);
+ if (index < 0) {
+ log("listener doesn't exist");
+ return;
+ }
+
+ this.listeners[aServiceType].splice(index, 1);
+ --this.numListeners;
+
+ if (this.numListeners === 0) {
+ this.unregisterEvent();
+ }
+
+ log("listener removed" + this);
+ },
+
+ onServiceFound(aServiceInfo) {
+ let listeners = this.listeners[aServiceInfo.serviceType];
+ if (listeners) {
+ for (let listener of listeners) {
+ listener.onServiceFound(aServiceInfo);
+ }
+ } else {
+ log("no listener");
+ }
+ return {};
+ },
+
+ onServiceLost(aServiceInfo) {
+ let listeners = this.listeners[aServiceInfo.serviceType];
+ if (listeners) {
+ for (let listener of listeners) {
+ listener.onServiceLost(aServiceInfo);
+ }
+ } else {
+ log("no listener");
+ }
+ return {};
+ },
+};
+
+// make an object from nsIPropertyBag2
+function parsePropertyBag2(bag) {
+ if (!bag || !(bag instanceof Ci.nsIPropertyBag2)) {
+ throw new TypeError("Not a property bag");
+ }
+
+ let attributes = [];
+ for (let { name } of bag.enumerator) {
+ let value = bag.getPropertyAsACString(name);
+ attributes.push({
+ name,
+ value,
+ });
+ }
+
+ return attributes;
+}
+
+function MulticastDNS() {
+ this.serviceManager = new ServiceManager();
+}
+
+MulticastDNS.prototype = {
+ startDiscovery(aServiceType, aListener) {
+ this.serviceManager.addListener(aServiceType, aListener);
+
+ let serviceInfo = {
+ serviceType: aServiceType,
+ uniqueId: aListener.uuid,
+ };
+
+ send("NsdManager:DiscoverServices", serviceInfo, (result, err) => {
+ if (err) {
+ log("onStartDiscoveryFailed: " + aServiceType + " (" + err + ")");
+ this.serviceManager.removeListener(aServiceType, aListener);
+ aListener.onStartDiscoveryFailed(aServiceType, err);
+ } else {
+ aListener.onDiscoveryStarted(result);
+ }
+ });
+ },
+
+ stopDiscovery(aServiceType, aListener) {
+ this.serviceManager.removeListener(aServiceType, aListener);
+
+ let serviceInfo = {
+ uniqueId: aListener.uuid,
+ };
+
+ send("NsdManager:StopServiceDiscovery", serviceInfo, (result, err) => {
+ if (err) {
+ log("onStopDiscoveryFailed: " + aServiceType + " (" + err + ")");
+ aListener.onStopDiscoveryFailed(aServiceType, err);
+ } else {
+ aListener.onDiscoveryStopped(aServiceType);
+ }
+ });
+ },
+
+ registerService(aServiceInfo, aListener) {
+ let serviceInfo = {
+ port: aServiceInfo.port,
+ serviceType: aServiceInfo.serviceType,
+ uniqueId: aListener.uuid,
+ };
+
+ try {
+ serviceInfo.host = aServiceInfo.host;
+ } catch (e) {
+ // host unspecified
+ }
+ try {
+ serviceInfo.serviceName = aServiceInfo.serviceName;
+ } catch (e) {
+ // serviceName unspecified
+ }
+ try {
+ serviceInfo.attributes = parsePropertyBag2(aServiceInfo.attributes);
+ } catch (e) {
+ // attributes unspecified
+ }
+
+ send("NsdManager:RegisterService", serviceInfo, (result, err) => {
+ if (err) {
+ log("onRegistrationFailed: (" + err + ")");
+ aListener.onRegistrationFailed(aServiceInfo, err);
+ } else {
+ aListener.onServiceRegistered(result);
+ }
+ });
+ },
+
+ unregisterService(aServiceInfo, aListener) {
+ let serviceInfo = {
+ uniqueId: aListener.uuid,
+ };
+
+ send("NsdManager:UnregisterService", serviceInfo, (result, err) => {
+ if (err) {
+ log("onUnregistrationFailed: (" + err + ")");
+ aListener.onUnregistrationFailed(aServiceInfo, err);
+ } else {
+ aListener.onServiceUnregistered(aServiceInfo);
+ }
+ });
+ },
+
+ resolveService(aServiceInfo, aListener) {
+ send("NsdManager:ResolveService", aServiceInfo, (result, err) => {
+ if (err) {
+ log("onResolveFailed: (" + err + ")");
+ aListener.onResolveFailed(aServiceInfo, err);
+ } else {
+ aListener.onServiceResolved(result);
+ }
+ });
+ },
+};
diff --git a/netwerk/dns/mdns/libmdns/components.conf b/netwerk/dns/mdns/libmdns/components.conf
new file mode 100644
index 0000000000..6e64140c82
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/components.conf
@@ -0,0 +1,24 @@
+# -*- 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/.
+
+Classes = [
+ {
+ 'cid': '{14a50f2b-7ff6-48a5-88e3-615fd111f5d3}',
+ 'contract_ids': ['@mozilla.org/toolkit/components/mdnsresponder/dns-info;1'],
+ 'type': 'mozilla::net::nsDNSServiceInfo',
+ 'headers': ['/netwerk/dns/mdns/libmdns/nsDNSServiceInfo.h'],
+ },
+]
+
+if buildconfig.substs['MOZ_WIDGET_TOOLKIT'] != 'cocoa':
+ Classes += [
+ {
+ 'cid': '{f9346d98-f27a-4e89-b744-493843416480}',
+ 'contract_ids': ['@mozilla.org/toolkit/components/mdnsresponder/dns-sd;1'],
+ 'jsm': 'resource://gre/modules/DNSServiceDiscovery.jsm',
+ 'constructor': 'nsDNSServiceDiscovery',
+ },
+ ]
diff --git a/netwerk/dns/mdns/libmdns/fallback/DNSPacket.jsm b/netwerk/dns/mdns/libmdns/fallback/DNSPacket.jsm
new file mode 100644
index 0000000000..1dd859c6d0
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/fallback/DNSPacket.jsm
@@ -0,0 +1,304 @@
+/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 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/. */
+
+"use strict";
+
+var EXPORTED_SYMBOLS = ["DNSPacket"];
+
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+const { DataReader } = ChromeUtils.import(
+ "resource://gre/modules/DataReader.jsm"
+);
+const { DataWriter } = ChromeUtils.import(
+ "resource://gre/modules/DataWriter.jsm"
+);
+const { DNSRecord } = ChromeUtils.import(
+ "resource://gre/modules/DNSRecord.jsm"
+);
+const { DNSResourceRecord } = ChromeUtils.import(
+ "resource://gre/modules/DNSResourceRecord.jsm"
+);
+
+const DEBUG = true;
+
+function debug(msg) {
+ Services.console.logStringMessage("DNSPacket: " + msg);
+}
+
+let DNS_PACKET_SECTION_TYPES = [
+ "QD", // Question
+ "AN", // Answer
+ "NS", // Authority
+ "AR", // Additional
+];
+
+/**
+ * DNS Packet Structure
+ * *************************************************
+ *
+ * Header
+ * ======
+ *
+ * 00 2-Bytes 15
+ * -------------------------------------------------
+ * |00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|
+ * -------------------------------------------------
+ * |<==================== ID =====================>|
+ * |QR|<== OP ===>|AA|TC|RD|RA|UN|AD|CD|<== RC ===>|
+ * |<================== QDCOUNT ==================>|
+ * |<================== ANCOUNT ==================>|
+ * |<================== NSCOUNT ==================>|
+ * |<================== ARCOUNT ==================>|
+ * -------------------------------------------------
+ *
+ * ID: 2-Bytes
+ * FLAGS: 2-Bytes
+ * - QR: 1-Bit
+ * - OP: 4-Bits
+ * - AA: 1-Bit
+ * - TC: 1-Bit
+ * - RD: 1-Bit
+ * - RA: 1-Bit
+ * - UN: 1-Bit
+ * - AD: 1-Bit
+ * - CD: 1-Bit
+ * - RC: 4-Bits
+ * QDCOUNT: 2-Bytes
+ * ANCOUNT: 2-Bytes
+ * NSCOUNT: 2-Bytes
+ * ARCOUNT: 2-Bytes
+ *
+ *
+ * Data
+ * ====
+ *
+ * 00 2-Bytes 15
+ * -------------------------------------------------
+ * |00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|
+ * -------------------------------------------------
+ * |<???=============== QD[...] ===============???>|
+ * |<???=============== AN[...] ===============???>|
+ * |<???=============== NS[...] ===============???>|
+ * |<???=============== AR[...] ===============???>|
+ * -------------------------------------------------
+ *
+ * QD: ??-Bytes
+ * AN: ??-Bytes
+ * NS: ??-Bytes
+ * AR: ??-Bytes
+ *
+ *
+ * Question Record
+ * ===============
+ *
+ * 00 2-Bytes 15
+ * -------------------------------------------------
+ * |00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|
+ * -------------------------------------------------
+ * |<???================ NAME =================???>|
+ * |<=================== TYPE ====================>|
+ * |<=================== CLASS ===================>|
+ * -------------------------------------------------
+ *
+ * NAME: ??-Bytes
+ * TYPE: 2-Bytes
+ * CLASS: 2-Bytes
+ *
+ *
+ * Resource Record
+ * ===============
+ *
+ * 00 4-Bytes 31
+ * -------------------------------------------------
+ * |00|02|04|06|08|10|12|14|16|18|20|22|24|26|28|30|
+ * -------------------------------------------------
+ * |<???================ NAME =================???>|
+ * |<======= TYPE ========>|<======= CLASS =======>|
+ * |<==================== TTL ====================>|
+ * |<====== DATALEN ======>|<???==== DATA =====???>|
+ * -------------------------------------------------
+ *
+ * NAME: ??-Bytes
+ * TYPE: 2-Bytes
+ * CLASS: 2-Bytes
+ * DATALEN: 2-Bytes
+ * DATA: ??-Bytes (Specified By DATALEN)
+ */
+class DNSPacket {
+ constructor() {
+ this._flags = _valueToFlags(0x0000);
+ this._records = {};
+
+ DNS_PACKET_SECTION_TYPES.forEach(sectionType => {
+ this._records[sectionType] = [];
+ });
+ }
+
+ static parse(data) {
+ let reader = new DataReader(data);
+ if (reader.getValue(2) !== 0x0000) {
+ throw new Error("Packet must start with 0x0000");
+ }
+
+ let packet = new DNSPacket();
+ packet._flags = _valueToFlags(reader.getValue(2));
+
+ let recordCounts = {};
+
+ // Parse the record counts.
+ DNS_PACKET_SECTION_TYPES.forEach(sectionType => {
+ recordCounts[sectionType] = reader.getValue(2);
+ });
+
+ // Parse the actual records.
+ DNS_PACKET_SECTION_TYPES.forEach(sectionType => {
+ let recordCount = recordCounts[sectionType];
+ for (let i = 0; i < recordCount; i++) {
+ if (sectionType === "QD") {
+ packet.addRecord(
+ sectionType,
+ DNSRecord.parseFromPacketReader(reader)
+ );
+ } else {
+ packet.addRecord(
+ sectionType,
+ DNSResourceRecord.parseFromPacketReader(reader)
+ );
+ }
+ }
+ });
+
+ if (!reader.eof) {
+ DEBUG && debug("Did not complete parsing packet data");
+ }
+
+ return packet;
+ }
+
+ getFlag(flag) {
+ return this._flags[flag];
+ }
+
+ setFlag(flag, value) {
+ this._flags[flag] = value;
+ }
+
+ addRecord(sectionType, record) {
+ this._records[sectionType].push(record);
+ }
+
+ getRecords(sectionTypes, recordType) {
+ let records = [];
+
+ sectionTypes.forEach(sectionType => {
+ records = records.concat(this._records[sectionType]);
+ });
+
+ if (!recordType) {
+ return records;
+ }
+
+ return records.filter(r => r.recordType === recordType);
+ }
+
+ serialize() {
+ let writer = new DataWriter();
+
+ // Write leading 0x0000 (2 bytes)
+ writer.putValue(0x0000, 2);
+
+ // Write `flags` (2 bytes)
+ writer.putValue(_flagsToValue(this._flags), 2);
+
+ // Write lengths of record sections (2 bytes each)
+ DNS_PACKET_SECTION_TYPES.forEach(sectionType => {
+ writer.putValue(this._records[sectionType].length, 2);
+ });
+
+ // Write records
+ DNS_PACKET_SECTION_TYPES.forEach(sectionType => {
+ this._records[sectionType].forEach(record => {
+ writer.putBytes(record.serialize());
+ });
+ });
+
+ return writer.data;
+ }
+
+ toJSON() {
+ return JSON.stringify(this.toJSONObject());
+ }
+
+ toJSONObject() {
+ let result = { flags: this._flags };
+ DNS_PACKET_SECTION_TYPES.forEach(sectionType => {
+ result[sectionType] = [];
+
+ let records = this._records[sectionType];
+ records.forEach(record => {
+ result[sectionType].push(record.toJSONObject());
+ });
+ });
+
+ return result;
+ }
+}
+
+/**
+ * @private
+ */
+function _valueToFlags(value) {
+ return {
+ QR: (value & 0x8000) >> 15,
+ OP: (value & 0x7800) >> 11,
+ AA: (value & 0x0400) >> 10,
+ TC: (value & 0x0200) >> 9,
+ RD: (value & 0x0100) >> 8,
+ RA: (value & 0x0080) >> 7,
+ UN: (value & 0x0040) >> 6,
+ AD: (value & 0x0020) >> 5,
+ CD: (value & 0x0010) >> 4,
+ RC: (value & 0x000f) >> 0,
+ };
+}
+
+/**
+ * @private
+ */
+function _flagsToValue(flags) {
+ let value = 0x0000;
+
+ value += flags.QR & 0x01;
+
+ value <<= 4;
+ value += flags.OP & 0x0f;
+
+ value <<= 1;
+ value += flags.AA & 0x01;
+
+ value <<= 1;
+ value += flags.TC & 0x01;
+
+ value <<= 1;
+ value += flags.RD & 0x01;
+
+ value <<= 1;
+ value += flags.RA & 0x01;
+
+ value <<= 1;
+ value += flags.UN & 0x01;
+
+ value <<= 1;
+ value += flags.AD & 0x01;
+
+ value <<= 1;
+ value += flags.CD & 0x01;
+
+ value <<= 4;
+ value += flags.RC & 0x0f;
+
+ return value;
+}
diff --git a/netwerk/dns/mdns/libmdns/fallback/DNSRecord.jsm b/netwerk/dns/mdns/libmdns/fallback/DNSRecord.jsm
new file mode 100644
index 0000000000..682fd8b103
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/fallback/DNSRecord.jsm
@@ -0,0 +1,71 @@
+/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 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/. */
+
+"use strict";
+
+var EXPORTED_SYMBOLS = ["DNSRecord"];
+
+const { DataWriter } = ChromeUtils.import(
+ "resource://gre/modules/DataWriter.jsm"
+);
+const { DNS_CLASS_CODES, DNS_RECORD_TYPES } = ChromeUtils.import(
+ "resource://gre/modules/DNSTypes.jsm"
+);
+
+class DNSRecord {
+ constructor(properties = {}) {
+ this.name = properties.name || "";
+ this.recordType = properties.recordType || DNS_RECORD_TYPES.ANY;
+ this.classCode = properties.classCode || DNS_CLASS_CODES.IN;
+ this.cacheFlush = properties.cacheFlush || false;
+ }
+
+ static parseFromPacketReader(reader) {
+ let name = reader.getLabel();
+ let recordType = reader.getValue(2);
+ let classCode = reader.getValue(2);
+ let cacheFlush = !!(classCode & 0x8000);
+ classCode &= 0xff;
+
+ return new this({
+ name,
+ recordType,
+ classCode,
+ cacheFlush,
+ });
+ }
+
+ serialize() {
+ let writer = new DataWriter();
+
+ // Write `name` (ends with trailing 0x00 byte)
+ writer.putLabel(this.name);
+
+ // Write `recordType` (2 bytes)
+ writer.putValue(this.recordType, 2);
+
+ // Write `classCode` (2 bytes)
+ let classCode = this.classCode;
+ if (this.cacheFlush) {
+ classCode |= 0x8000;
+ }
+ writer.putValue(classCode, 2);
+
+ return writer.data;
+ }
+
+ toJSON() {
+ return JSON.stringify(this.toJSONObject());
+ }
+
+ toJSONObject() {
+ return {
+ name: this.name,
+ recordType: this.recordType,
+ classCode: this.classCode,
+ cacheFlush: this.cacheFlush,
+ };
+ }
+}
diff --git a/netwerk/dns/mdns/libmdns/fallback/DNSResourceRecord.jsm b/netwerk/dns/mdns/libmdns/fallback/DNSResourceRecord.jsm
new file mode 100644
index 0000000000..e89f418410
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/fallback/DNSResourceRecord.jsm
@@ -0,0 +1,221 @@
+/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 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/. */
+
+"use strict";
+
+var EXPORTED_SYMBOLS = ["DNSResourceRecord"];
+
+const { DataReader } = ChromeUtils.import(
+ "resource://gre/modules/DataReader.jsm"
+);
+const { DataWriter } = ChromeUtils.import(
+ "resource://gre/modules/DataWriter.jsm"
+);
+const { DNSRecord } = ChromeUtils.import(
+ "resource://gre/modules/DNSRecord.jsm"
+);
+const { DNS_RECORD_TYPES } = ChromeUtils.import(
+ "resource://gre/modules/DNSTypes.jsm"
+);
+
+const DNS_RESOURCE_RECORD_DEFAULT_TTL = 120; // 120 seconds
+
+class DNSResourceRecord extends DNSRecord {
+ constructor(properties = {}) {
+ super(properties);
+
+ this.ttl = properties.ttl || DNS_RESOURCE_RECORD_DEFAULT_TTL;
+ this.data = properties.data || {};
+ }
+
+ static parseFromPacketReader(reader) {
+ let record = super.parseFromPacketReader(reader);
+
+ let ttl = reader.getValue(4);
+ let recordData = reader.getBytes(reader.getValue(2));
+ let packetData = reader.data;
+
+ let data;
+
+ switch (record.recordType) {
+ case DNS_RECORD_TYPES.A:
+ data = _parseA(recordData, packetData);
+ break;
+ case DNS_RECORD_TYPES.PTR:
+ data = _parsePTR(recordData, packetData);
+ break;
+ case DNS_RECORD_TYPES.TXT:
+ data = _parseTXT(recordData, packetData);
+ break;
+ case DNS_RECORD_TYPES.SRV:
+ data = _parseSRV(recordData, packetData);
+ break;
+ default:
+ data = null;
+ break;
+ }
+
+ record.ttl = ttl;
+ record.data = data;
+
+ return record;
+ }
+
+ serialize() {
+ let writer = new DataWriter(super.serialize());
+
+ // Write `ttl` (4 bytes)
+ writer.putValue(this.ttl, 4);
+
+ let data;
+
+ switch (this.recordType) {
+ case DNS_RECORD_TYPES.A:
+ data = _serializeA(this.data);
+ break;
+ case DNS_RECORD_TYPES.PTR:
+ data = _serializePTR(this.data);
+ break;
+ case DNS_RECORD_TYPES.TXT:
+ data = _serializeTXT(this.data);
+ break;
+ case DNS_RECORD_TYPES.SRV:
+ data = _serializeSRV(this.data);
+ break;
+ default:
+ data = new Uint8Array();
+ break;
+ }
+
+ // Write `data` length.
+ writer.putValue(data.length, 2);
+
+ // Write `data` (ends with trailing 0x00 byte)
+ writer.putBytes(data);
+
+ return writer.data;
+ }
+
+ toJSON() {
+ return JSON.stringify(this.toJSONObject());
+ }
+
+ toJSONObject() {
+ let result = super.toJSONObject();
+ result.ttl = this.ttl;
+ result.data = this.data;
+ return result;
+ }
+}
+
+/**
+ * @private
+ */
+function _parseA(recordData, packetData) {
+ let reader = new DataReader(recordData);
+
+ let parts = [];
+ for (let i = 0; i < 4; i++) {
+ parts.push(reader.getValue(1));
+ }
+
+ return parts.join(".");
+}
+
+/**
+ * @private
+ */
+function _parsePTR(recordData, packetData) {
+ let reader = new DataReader(recordData);
+
+ return reader.getLabel(packetData);
+}
+
+/**
+ * @private
+ */
+function _parseTXT(recordData, packetData) {
+ let reader = new DataReader(recordData);
+
+ let result = {};
+
+ let label = reader.getLabel(packetData);
+ if (label.length > 0) {
+ let parts = label.split(".");
+ parts.forEach(part => {
+ let [name] = part.split("=", 1);
+ let value = part.substr(name.length + 1);
+ result[name] = value;
+ });
+ }
+
+ return result;
+}
+
+/**
+ * @private
+ */
+function _parseSRV(recordData, packetData) {
+ let reader = new DataReader(recordData);
+
+ let priority = reader.getValue(2);
+ let weight = reader.getValue(2);
+ let port = reader.getValue(2);
+ let target = reader.getLabel(packetData);
+
+ return { priority, weight, port, target };
+}
+
+/**
+ * @private
+ */
+function _serializeA(data) {
+ let writer = new DataWriter();
+
+ let parts = data.split(".");
+ for (let i = 0; i < 4; i++) {
+ writer.putValue(parseInt(parts[i], 10) || 0);
+ }
+
+ return writer.data;
+}
+
+/**
+ * @private
+ */
+function _serializePTR(data) {
+ let writer = new DataWriter();
+
+ writer.putLabel(data);
+
+ return writer.data;
+}
+
+/**
+ * @private
+ */
+function _serializeTXT(data) {
+ let writer = new DataWriter();
+
+ for (let name in data) {
+ writer.putLengthString(name + "=" + data[name]);
+ }
+
+ return writer.data;
+}
+
+/**
+ * @private
+ */
+function _serializeSRV(data) {
+ let writer = new DataWriter();
+
+ writer.putValue(data.priority || 0, 2);
+ writer.putValue(data.weight || 0, 2);
+ writer.putValue(data.port || 0, 2);
+ writer.putLabel(data.target);
+
+ return writer.data;
+}
diff --git a/netwerk/dns/mdns/libmdns/fallback/DNSTypes.jsm b/netwerk/dns/mdns/libmdns/fallback/DNSTypes.jsm
new file mode 100644
index 0000000000..878e22917d
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/fallback/DNSTypes.jsm
@@ -0,0 +1,99 @@
+/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 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/. */
+
+"use strict";
+
+var EXPORTED_SYMBOLS = [
+ "DNS_QUERY_RESPONSE_CODES",
+ "DNS_AUTHORITATIVE_ANSWER_CODES",
+ "DNS_CLASS_CODES",
+ "DNS_RECORD_TYPES",
+];
+
+let DNS_QUERY_RESPONSE_CODES = {
+ QUERY: 0, // RFC 1035 - Query
+ RESPONSE: 1, // RFC 1035 - Reponse
+};
+
+let DNS_AUTHORITATIVE_ANSWER_CODES = {
+ NO: 0, // RFC 1035 - Not Authoritative
+ YES: 1, // RFC 1035 - Is Authoritative
+};
+
+let DNS_CLASS_CODES = {
+ IN: 0x01, // RFC 1035 - Internet
+ CS: 0x02, // RFC 1035 - CSNET
+ CH: 0x03, // RFC 1035 - CHAOS
+ HS: 0x04, // RFC 1035 - Hesiod
+ NONE: 0xfe, // RFC 2136 - None
+ ANY: 0xff, // RFC 1035 - Any
+};
+
+let DNS_RECORD_TYPES = {
+ SIGZERO: 0, // RFC 2931
+ A: 1, // RFC 1035
+ NS: 2, // RFC 1035
+ MD: 3, // RFC 1035
+ MF: 4, // RFC 1035
+ CNAME: 5, // RFC 1035
+ SOA: 6, // RFC 1035
+ MB: 7, // RFC 1035
+ MG: 8, // RFC 1035
+ MR: 9, // RFC 1035
+ NULL: 10, // RFC 1035
+ WKS: 11, // RFC 1035
+ PTR: 12, // RFC 1035
+ HINFO: 13, // RFC 1035
+ MINFO: 14, // RFC 1035
+ MX: 15, // RFC 1035
+ TXT: 16, // RFC 1035
+ RP: 17, // RFC 1183
+ AFSDB: 18, // RFC 1183
+ X25: 19, // RFC 1183
+ ISDN: 20, // RFC 1183
+ RT: 21, // RFC 1183
+ NSAP: 22, // RFC 1706
+ NSAP_PTR: 23, // RFC 1348
+ SIG: 24, // RFC 2535
+ KEY: 25, // RFC 2535
+ PX: 26, // RFC 2163
+ GPOS: 27, // RFC 1712
+ AAAA: 28, // RFC 1886
+ LOC: 29, // RFC 1876
+ NXT: 30, // RFC 2535
+ EID: 31, // RFC ????
+ NIMLOC: 32, // RFC ????
+ SRV: 33, // RFC 2052
+ ATMA: 34, // RFC ????
+ NAPTR: 35, // RFC 2168
+ KX: 36, // RFC 2230
+ CERT: 37, // RFC 2538
+ DNAME: 39, // RFC 2672
+ OPT: 41, // RFC 2671
+ APL: 42, // RFC 3123
+ DS: 43, // RFC 4034
+ SSHFP: 44, // RFC 4255
+ IPSECKEY: 45, // RFC 4025
+ RRSIG: 46, // RFC 4034
+ NSEC: 47, // RFC 4034
+ DNSKEY: 48, // RFC 4034
+ DHCID: 49, // RFC 4701
+ NSEC3: 50, // RFC ????
+ NSEC3PARAM: 51, // RFC ????
+ HIP: 55, // RFC 5205
+ SPF: 99, // RFC 4408
+ UINFO: 100, // RFC ????
+ UID: 101, // RFC ????
+ GID: 102, // RFC ????
+ UNSPEC: 103, // RFC ????
+ TKEY: 249, // RFC 2930
+ TSIG: 250, // RFC 2931
+ IXFR: 251, // RFC 1995
+ AXFR: 252, // RFC 1035
+ MAILB: 253, // RFC 1035
+ MAILA: 254, // RFC 1035
+ ANY: 255, // RFC 1035
+ DLV: 32769, // RFC 4431
+};
diff --git a/netwerk/dns/mdns/libmdns/fallback/DataReader.jsm b/netwerk/dns/mdns/libmdns/fallback/DataReader.jsm
new file mode 100644
index 0000000000..978ab4dde0
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/fallback/DataReader.jsm
@@ -0,0 +1,134 @@
+/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 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/. */
+
+"use strict";
+
+var EXPORTED_SYMBOLS = ["DataReader"];
+
+class DataReader {
+ // `data` is `Uint8Array`
+ constructor(data, startByte = 0) {
+ this._data = data;
+ this._cursor = startByte;
+ }
+
+ get buffer() {
+ return this._data.buffer;
+ }
+
+ get data() {
+ return this._data;
+ }
+
+ get eof() {
+ return this._cursor >= this._data.length;
+ }
+
+ getBytes(length = 1) {
+ if (!length) {
+ return new Uint8Array();
+ }
+
+ let end = this._cursor + length;
+ if (end > this._data.length) {
+ return new Uint8Array();
+ }
+
+ let uint8Array = new Uint8Array(this.buffer.slice(this._cursor, end));
+ this._cursor += length;
+
+ return uint8Array;
+ }
+
+ getString(length) {
+ let uint8Array = this.getBytes(length);
+ return _uint8ArrayToString(uint8Array);
+ }
+
+ getValue(length) {
+ let uint8Array = this.getBytes(length);
+ return _uint8ArrayToValue(uint8Array);
+ }
+
+ getLabel(decompressData) {
+ let parts = [];
+ let partLength;
+
+ while ((partLength = this.getValue(1))) {
+ // If a length has been specified instead of a pointer,
+ // read the string of the specified length.
+ if (partLength !== 0xc0) {
+ parts.push(this.getString(partLength));
+ continue;
+ }
+
+ // TODO: Handle case where we have a pointer to the label
+ parts.push(String.fromCharCode(0xc0) + this.getString(1));
+ break;
+ }
+
+ let label = parts.join(".");
+
+ return _decompressLabel(label, decompressData || this._data);
+ }
+}
+
+/**
+ * @private
+ */
+function _uint8ArrayToValue(uint8Array) {
+ let length = uint8Array.length;
+ if (length === 0) {
+ return null;
+ }
+
+ let value = 0;
+ for (let i = 0; i < length; i++) {
+ value = value << 8;
+ value += uint8Array[i];
+ }
+
+ return value;
+}
+
+/**
+ * @private
+ */
+function _uint8ArrayToString(uint8Array) {
+ let length = uint8Array.length;
+ if (length === 0) {
+ return "";
+ }
+
+ let results = [];
+ for (let i = 0; i < length; i += 1024) {
+ results.push(
+ String.fromCharCode.apply(null, uint8Array.subarray(i, i + 1024))
+ );
+ }
+
+ return results.join("");
+}
+
+/**
+ * @private
+ */
+function _decompressLabel(label, decompressData) {
+ let result = "";
+
+ for (let i = 0, length = label.length; i < length; i++) {
+ if (label.charCodeAt(i) !== 0xc0) {
+ result += label.charAt(i);
+ continue;
+ }
+
+ i++;
+
+ let reader = new DataReader(decompressData, label.charCodeAt(i));
+ result += _decompressLabel(reader.getLabel(), decompressData);
+ }
+
+ return result;
+}
diff --git a/netwerk/dns/mdns/libmdns/fallback/DataWriter.jsm b/netwerk/dns/mdns/libmdns/fallback/DataWriter.jsm
new file mode 100644
index 0000000000..05949a1d05
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/fallback/DataWriter.jsm
@@ -0,0 +1,97 @@
+/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 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/. */
+
+"use strict";
+
+var EXPORTED_SYMBOLS = ["DataWriter"];
+
+class DataWriter {
+ constructor(data, maxBytes = 512) {
+ if (typeof data === "number") {
+ maxBytes = data;
+ data = undefined;
+ }
+
+ this._buffer = new ArrayBuffer(maxBytes);
+ this._data = new Uint8Array(this._buffer);
+ this._cursor = 0;
+
+ if (data) {
+ this.putBytes(data);
+ }
+ }
+
+ get buffer() {
+ return this._buffer.slice(0, this._cursor);
+ }
+
+ get data() {
+ return new Uint8Array(this.buffer);
+ }
+
+ // `data` is `Uint8Array`
+ putBytes(data) {
+ if (this._cursor + data.length > this._data.length) {
+ throw new Error("DataWriter buffer is exceeded");
+ }
+
+ for (let i = 0, length = data.length; i < length; i++) {
+ this._data[this._cursor] = data[i];
+ this._cursor++;
+ }
+ }
+
+ putByte(byte) {
+ if (this._cursor + 1 > this._data.length) {
+ throw new Error("DataWriter buffer is exceeded");
+ }
+
+ this._data[this._cursor] = byte;
+ this._cursor++;
+ }
+
+ putValue(value, length) {
+ length = length || 1;
+ if (length == 1) {
+ this.putByte(value);
+ } else {
+ this.putBytes(_valueToUint8Array(value, length));
+ }
+ }
+
+ putLabel(label) {
+ // Eliminate any trailing '.'s in the label (valid in text representation).
+ label = label.replace(/\.$/, "");
+ let parts = label.split(".");
+ parts.forEach(part => {
+ this.putLengthString(part);
+ });
+ this.putValue(0);
+ }
+
+ putLengthString(string) {
+ if (string.length > 0xff) {
+ throw new Error("String too long.");
+ }
+ this.putValue(string.length);
+ for (let i = 0; i < string.length; i++) {
+ this.putValue(string.charCodeAt(i));
+ }
+ }
+}
+
+/**
+ * @private
+ */
+function _valueToUint8Array(value, length) {
+ let arrayBuffer = new ArrayBuffer(length);
+ let uint8Array = new Uint8Array(arrayBuffer);
+ for (let i = length - 1; i >= 0; i--) {
+ uint8Array[i] = value & 0xff;
+ value = value >> 8;
+ }
+
+ return uint8Array;
+}
diff --git a/netwerk/dns/mdns/libmdns/fallback/MulticastDNS.jsm b/netwerk/dns/mdns/libmdns/fallback/MulticastDNS.jsm
new file mode 100644
index 0000000000..efc21fe523
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/fallback/MulticastDNS.jsm
@@ -0,0 +1,985 @@
+/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 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/. */
+
+"use strict";
+
+var EXPORTED_SYMBOLS = ["MulticastDNS"];
+
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { clearTimeout, setTimeout } = ChromeUtils.import(
+ "resource://gre/modules/Timer.jsm"
+);
+
+const { DNSPacket } = ChromeUtils.import(
+ "resource://gre/modules/DNSPacket.jsm"
+);
+const { DNSRecord } = ChromeUtils.import(
+ "resource://gre/modules/DNSRecord.jsm"
+);
+const { DNSResourceRecord } = ChromeUtils.import(
+ "resource://gre/modules/DNSResourceRecord.jsm"
+);
+const {
+ DNS_AUTHORITATIVE_ANSWER_CODES,
+ DNS_CLASS_CODES,
+ DNS_QUERY_RESPONSE_CODES,
+ DNS_RECORD_TYPES,
+} = ChromeUtils.import("resource://gre/modules/DNSTypes.jsm");
+
+const NS_NETWORK_LINK_TOPIC = "network:link-status-changed";
+
+let networkInfoService = Cc[
+ "@mozilla.org/network-info-service;1"
+].createInstance(Ci.nsINetworkInfoService);
+
+const DEBUG = true;
+
+const MDNS_MULTICAST_GROUP = "224.0.0.251";
+const MDNS_PORT = 5353;
+const DEFAULT_TTL = 120;
+
+function debug(msg) {
+ dump("MulticastDNS: " + msg + "\n");
+}
+
+function ServiceKey(svc) {
+ return (
+ "" +
+ svc.serviceType.length +
+ "/" +
+ svc.serviceType +
+ "|" +
+ svc.serviceName.length +
+ "/" +
+ svc.serviceName +
+ "|" +
+ svc.port
+ );
+}
+
+function TryGet(obj, name) {
+ try {
+ return obj[name];
+ } catch (err) {
+ return undefined;
+ }
+}
+
+function IsIpv4Address(addr) {
+ let parts = addr.split(".");
+ if (parts.length != 4) {
+ return false;
+ }
+ for (let part of parts) {
+ let partInt = Number.parseInt(part, 10);
+ if (partInt.toString() != part) {
+ return false;
+ }
+ if (partInt < 0 || partInt >= 256) {
+ return false;
+ }
+ }
+ return true;
+}
+
+class PublishedService {
+ constructor(attrs) {
+ this.serviceType = attrs.serviceType.replace(/\.$/, "");
+ this.serviceName = attrs.serviceName;
+ this.domainName = TryGet(attrs, "domainName") || "local";
+ this.address = TryGet(attrs, "address") || "0.0.0.0";
+ this.port = attrs.port;
+ this.serviceAttrs = _propertyBagToObject(TryGet(attrs, "attributes") || {});
+ this.host = TryGet(attrs, "host");
+ this.key = this.generateKey();
+ this.lastAdvertised = undefined;
+ this.advertiseTimer = undefined;
+ }
+
+ equals(svc) {
+ return (
+ this.port == svc.port &&
+ this.serviceName == svc.serviceName &&
+ this.serviceType == svc.serviceType
+ );
+ }
+
+ generateKey() {
+ return ServiceKey(this);
+ }
+
+ ptrMatch(name) {
+ return name == this.serviceType + "." + this.domainName;
+ }
+
+ clearAdvertiseTimer() {
+ if (!this.advertiseTimer) {
+ return;
+ }
+ clearTimeout(this.advertiseTimer);
+ this.advertiseTimer = undefined;
+ }
+}
+
+class MulticastDNS {
+ constructor() {
+ this._listeners = new Map();
+ this._sockets = new Map();
+ this._services = new Map();
+ this._discovered = new Map();
+ this._querySocket = undefined;
+ this._broadcastReceiverSocket = undefined;
+ this._broadcastTimer = undefined;
+
+ this._networkLinkObserver = {
+ observe: (subject, topic, data) => {
+ DEBUG &&
+ debug(
+ NS_NETWORK_LINK_TOPIC +
+ "(" +
+ data +
+ "); Clearing list of previously discovered services"
+ );
+ this._discovered.clear();
+ },
+ };
+ }
+
+ _attachNetworkLinkObserver() {
+ if (this._networkLinkObserverTimeout) {
+ clearTimeout(this._networkLinkObserverTimeout);
+ }
+
+ if (!this._isNetworkLinkObserverAttached) {
+ DEBUG && debug("Attaching observer " + NS_NETWORK_LINK_TOPIC);
+ Services.obs.addObserver(
+ this._networkLinkObserver,
+ NS_NETWORK_LINK_TOPIC
+ );
+ this._isNetworkLinkObserverAttached = true;
+ }
+ }
+
+ _detachNetworkLinkObserver() {
+ if (this._isNetworkLinkObserverAttached) {
+ if (this._networkLinkObserverTimeout) {
+ clearTimeout(this._networkLinkObserverTimeout);
+ }
+
+ this._networkLinkObserverTimeout = setTimeout(() => {
+ DEBUG && debug("Detaching observer " + NS_NETWORK_LINK_TOPIC);
+ Services.obs.removeObserver(
+ this._networkLinkObserver,
+ NS_NETWORK_LINK_TOPIC
+ );
+ this._isNetworkLinkObserverAttached = false;
+ this._networkLinkObserverTimeout = null;
+ }, 5000);
+ }
+ }
+
+ startDiscovery(aServiceType, aListener) {
+ DEBUG && debug('startDiscovery("' + aServiceType + '")');
+ let { serviceType } = _parseServiceDomainName(aServiceType);
+
+ this._attachNetworkLinkObserver();
+ this._addServiceListener(serviceType, aListener);
+
+ try {
+ this._query(serviceType + ".local");
+ aListener.onDiscoveryStarted(serviceType);
+ } catch (e) {
+ DEBUG && debug('startDiscovery("' + serviceType + '") FAILED: ' + e);
+ this._removeServiceListener(serviceType, aListener);
+ aListener.onStartDiscoveryFailed(serviceType, Cr.NS_ERROR_FAILURE);
+ }
+ }
+
+ stopDiscovery(aServiceType, aListener) {
+ DEBUG && debug('stopDiscovery("' + aServiceType + '")');
+ let { serviceType } = _parseServiceDomainName(aServiceType);
+
+ this._detachNetworkLinkObserver();
+ this._removeServiceListener(serviceType, aListener);
+
+ aListener.onDiscoveryStopped(serviceType);
+
+ this._checkCloseSockets();
+ }
+
+ resolveService(aServiceInfo, aListener) {
+ DEBUG && debug("resolveService(): " + aServiceInfo.serviceName);
+
+ // Address info is already resolved during discovery
+ setTimeout(() => aListener.onServiceResolved(aServiceInfo));
+ }
+
+ registerService(aServiceInfo, aListener) {
+ DEBUG && debug("registerService(): " + aServiceInfo.serviceName);
+
+ // Initialize the broadcast receiver socket in case it
+ // hasn't already been started so we can listen for
+ // multicast queries/announcements on all interfaces.
+ this._getBroadcastReceiverSocket();
+
+ for (let name of ["port", "serviceName", "serviceType"]) {
+ if (!TryGet(aServiceInfo, name)) {
+ aListener.onRegistrationFailed(aServiceInfo, Cr.NS_ERROR_FAILURE);
+ throw new Error('Invalid nsIDNSServiceInfo; Missing "' + name + '"');
+ }
+ }
+
+ let publishedService;
+ try {
+ publishedService = new PublishedService(aServiceInfo);
+ } catch (e) {
+ DEBUG &&
+ debug("Error constructing PublishedService: " + e + " - " + e.stack);
+ setTimeout(() =>
+ aListener.onRegistrationFailed(aServiceInfo, Cr.NS_ERROR_FAILURE)
+ );
+ return;
+ }
+
+ // Ensure such a service does not already exist.
+ if (this._services.get(publishedService.key)) {
+ setTimeout(() =>
+ aListener.onRegistrationFailed(aServiceInfo, Cr.NS_ERROR_FAILURE)
+ );
+ return;
+ }
+
+ // Make sure that the service addr is '0.0.0.0', or there is at least one
+ // socket open on the address the service is open on.
+ this._getSockets().then(sockets => {
+ if (
+ publishedService.address != "0.0.0.0" &&
+ !sockets.get(publishedService.address)
+ ) {
+ setTimeout(() =>
+ aListener.onRegistrationFailed(aServiceInfo, Cr.NS_ERROR_FAILURE)
+ );
+ return;
+ }
+
+ this._services.set(publishedService.key, publishedService);
+
+ // Service registered.. call onServiceRegistered on next tick.
+ setTimeout(() => aListener.onServiceRegistered(aServiceInfo));
+
+ // Set a timeout to start advertising the service too.
+ publishedService.advertiseTimer = setTimeout(() => {
+ this._advertiseService(publishedService.key, /* firstAdv = */ true);
+ });
+ });
+ }
+
+ unregisterService(aServiceInfo, aListener) {
+ DEBUG && debug("unregisterService(): " + aServiceInfo.serviceName);
+
+ let serviceKey;
+ try {
+ serviceKey = ServiceKey(aServiceInfo);
+ } catch (e) {
+ setTimeout(() =>
+ aListener.onUnregistrationFailed(aServiceInfo, Cr.NS_ERROR_FAILURE)
+ );
+ return;
+ }
+
+ let publishedService = this._services.get(serviceKey);
+ if (!publishedService) {
+ setTimeout(() =>
+ aListener.onUnregistrationFailed(aServiceInfo, Cr.NS_ERROR_FAILURE)
+ );
+ return;
+ }
+
+ // Clear any advertise timeout for this published service.
+ publishedService.clearAdvertiseTimer();
+
+ // Delete the service from the service map.
+ if (!this._services.delete(serviceKey)) {
+ setTimeout(() =>
+ aListener.onUnregistrationFailed(aServiceInfo, Cr.NS_ERROR_FAILURE)
+ );
+ return;
+ }
+
+ // Check the broadcast timer again to rejig when it should run next.
+ this._checkStartBroadcastTimer();
+
+ // Check to see if sockets should be closed, and if so close them.
+ this._checkCloseSockets();
+
+ aListener.onServiceUnregistered(aServiceInfo);
+ }
+
+ _respondToQuery(serviceKey, message) {
+ let address = message.fromAddr.address;
+ let port = message.fromAddr.port;
+ DEBUG &&
+ debug(
+ "_respondToQuery(): key=" +
+ serviceKey +
+ ", fromAddr=" +
+ address +
+ ":" +
+ port
+ );
+
+ let publishedService = this._services.get(serviceKey);
+ if (!publishedService) {
+ debug("_respondToQuery Could not find service (key=" + serviceKey + ")");
+ return;
+ }
+
+ DEBUG &&
+ debug("_respondToQuery(): key=" + serviceKey + ": SENDING RESPONSE");
+ this._advertiseServiceHelper(publishedService, { address, port });
+ }
+
+ _advertiseService(serviceKey, firstAdv) {
+ DEBUG && debug("_advertiseService(): key=" + serviceKey);
+ let publishedService = this._services.get(serviceKey);
+ if (!publishedService) {
+ debug(
+ "_advertiseService Could not find service to advertise (key=" +
+ serviceKey +
+ ")"
+ );
+ return;
+ }
+
+ publishedService.advertiseTimer = undefined;
+
+ this._advertiseServiceHelper(publishedService, null).then(() => {
+ // If first advertisement, re-advertise in 1 second.
+ // Otherwise, set the lastAdvertised time.
+ if (firstAdv) {
+ publishedService.advertiseTimer = setTimeout(() => {
+ this._advertiseService(serviceKey);
+ }, 1000);
+ } else {
+ publishedService.lastAdvertised = Date.now();
+ this._checkStartBroadcastTimer();
+ }
+ });
+ }
+
+ _advertiseServiceHelper(svc, target) {
+ if (!target) {
+ target = { address: MDNS_MULTICAST_GROUP, port: MDNS_PORT };
+ }
+
+ return this._getSockets().then(sockets => {
+ sockets.forEach((socket, address) => {
+ if (svc.address == "0.0.0.0" || address == svc.address) {
+ let packet = this._makeServicePacket(svc, [address]);
+ let data = packet.serialize();
+ try {
+ socket.send(target.address, target.port, data);
+ } catch (err) {
+ DEBUG &&
+ debug(
+ "Failed to send packet to " + target.address + ":" + target.port
+ );
+ }
+ }
+ });
+ });
+ }
+
+ _cancelBroadcastTimer() {
+ if (!this._broadcastTimer) {
+ return;
+ }
+ clearTimeout(this._broadcastTimer);
+ this._broadcastTimer = undefined;
+ }
+
+ _checkStartBroadcastTimer() {
+ DEBUG && debug("_checkStartBroadcastTimer()");
+ // Cancel any existing broadcasting timer.
+ this._cancelBroadcastTimer();
+
+ let now = Date.now();
+
+ // Go through services and find services to broadcast.
+ let bcastServices = [];
+ let nextBcastWait = undefined;
+ for (let [, publishedService] of this._services) {
+ // if lastAdvertised is undefined, service hasn't finished it's initial
+ // two broadcasts.
+ if (publishedService.lastAdvertised === undefined) {
+ continue;
+ }
+
+ // Otherwise, check lastAdvertised against now.
+ let msSinceAdv = now - publishedService.lastAdvertised;
+
+ // If msSinceAdv is more than 90% of the way to the TTL, advertise now.
+ if (msSinceAdv > DEFAULT_TTL * 1000 * 0.9) {
+ bcastServices.push(publishedService);
+ continue;
+ }
+
+ // Otherwise, calculate the next time to advertise for this service.
+ // We set that at 95% of the time to the TTL expiry.
+ let nextAdvWait = DEFAULT_TTL * 1000 * 0.95 - msSinceAdv;
+ if (nextBcastWait === undefined || nextBcastWait > nextAdvWait) {
+ nextBcastWait = nextAdvWait;
+ }
+ }
+
+ // Schedule an immediate advertisement of all services to be advertised now.
+ for (let svc of bcastServices) {
+ svc.advertiseTimer = setTimeout(() => this._advertiseService(svc.key));
+ }
+
+ // Schedule next broadcast check for the next bcast time.
+ if (nextBcastWait !== undefined) {
+ DEBUG &&
+ debug(
+ "_checkStartBroadcastTimer(): Scheduling next check in " +
+ nextBcastWait +
+ "ms"
+ );
+ this._broadcastTimer = setTimeout(
+ () => this._checkStartBroadcastTimer(),
+ nextBcastWait
+ );
+ }
+ }
+
+ _query(name) {
+ DEBUG && debug('query("' + name + '")');
+ let packet = new DNSPacket();
+ packet.setFlag("QR", DNS_QUERY_RESPONSE_CODES.QUERY);
+
+ // PTR Record
+ packet.addRecord(
+ "QD",
+ new DNSRecord({
+ name,
+ recordType: DNS_RECORD_TYPES.PTR,
+ classCode: DNS_CLASS_CODES.IN,
+ cacheFlush: true,
+ })
+ );
+
+ let data = packet.serialize();
+
+ // Initialize the broadcast receiver socket in case it
+ // hasn't already been started so we can listen for
+ // multicast queries/announcements on all interfaces.
+ this._getBroadcastReceiverSocket();
+
+ this._getQuerySocket().then(querySocket => {
+ DEBUG && debug('sending query on query socket ("' + name + '")');
+ querySocket.send(MDNS_MULTICAST_GROUP, MDNS_PORT, data);
+ });
+
+ // Automatically announce previously-discovered
+ // services that match and haven't expired yet.
+ setTimeout(() => {
+ DEBUG &&
+ debug('announcing previously discovered services ("' + name + '")');
+ let { serviceType } = _parseServiceDomainName(name);
+
+ this._clearExpiredDiscoveries();
+ this._discovered.forEach((discovery, key) => {
+ let serviceInfo = discovery.serviceInfo;
+ if (serviceInfo.serviceType !== serviceType) {
+ return;
+ }
+
+ let listeners = this._listeners.get(serviceInfo.serviceType) || [];
+ listeners.forEach(listener => {
+ listener.onServiceFound(serviceInfo);
+ });
+ });
+ });
+ }
+
+ _clearExpiredDiscoveries() {
+ this._discovered.forEach((discovery, key) => {
+ if (discovery.expireTime < Date.now()) {
+ this._discovered.delete(key);
+ }
+ });
+ }
+
+ _handleQueryPacket(packet, message) {
+ packet.getRecords(["QD"]).forEach(record => {
+ // Don't respond if the query's class code is not IN or ANY.
+ if (
+ record.classCode !== DNS_CLASS_CODES.IN &&
+ record.classCode !== DNS_CLASS_CODES.ANY
+ ) {
+ return;
+ }
+
+ // Don't respond if the query's record type is not PTR or ANY.
+ if (
+ record.recordType !== DNS_RECORD_TYPES.PTR &&
+ record.recordType !== DNS_RECORD_TYPES.ANY
+ ) {
+ return;
+ }
+
+ for (let [serviceKey, publishedService] of this._services) {
+ DEBUG && debug("_handleQueryPacket: " + packet.toJSON());
+ if (publishedService.ptrMatch(record.name)) {
+ this._respondToQuery(serviceKey, message);
+ }
+ }
+ });
+ }
+
+ _makeServicePacket(service, addresses) {
+ let packet = new DNSPacket();
+ packet.setFlag("QR", DNS_QUERY_RESPONSE_CODES.RESPONSE);
+ packet.setFlag("AA", DNS_AUTHORITATIVE_ANSWER_CODES.YES);
+
+ let host = service.host || _hostname;
+
+ // e.g.: foo-bar-service._http._tcp.local
+ let serviceDomainName =
+ service.serviceName + "." + service.serviceType + ".local";
+
+ // PTR Record
+ packet.addRecord(
+ "AN",
+ new DNSResourceRecord({
+ name: service.serviceType + ".local", // e.g.: _http._tcp.local
+ recordType: DNS_RECORD_TYPES.PTR,
+ data: serviceDomainName,
+ })
+ );
+
+ // SRV Record
+ packet.addRecord(
+ "AR",
+ new DNSResourceRecord({
+ name: serviceDomainName,
+ recordType: DNS_RECORD_TYPES.SRV,
+ classCode: DNS_CLASS_CODES.IN,
+ cacheFlush: true,
+ data: {
+ priority: 0,
+ weight: 0,
+ port: service.port,
+ target: host, // e.g.: My-Android-Phone.local
+ },
+ })
+ );
+
+ // A Records
+ for (let address of addresses) {
+ packet.addRecord(
+ "AR",
+ new DNSResourceRecord({
+ name: host,
+ recordType: DNS_RECORD_TYPES.A,
+ data: address,
+ })
+ );
+ }
+
+ // TXT Record
+ packet.addRecord(
+ "AR",
+ new DNSResourceRecord({
+ name: serviceDomainName,
+ recordType: DNS_RECORD_TYPES.TXT,
+ classCode: DNS_CLASS_CODES.IN,
+ cacheFlush: true,
+ data: service.serviceAttrs || {},
+ })
+ );
+
+ return packet;
+ }
+
+ _handleResponsePacket(packet, message) {
+ let services = {};
+ let hosts = {};
+
+ let srvRecords = packet.getRecords(["AN", "AR"], DNS_RECORD_TYPES.SRV);
+ let txtRecords = packet.getRecords(["AN", "AR"], DNS_RECORD_TYPES.TXT);
+ let ptrRecords = packet.getRecords(["AN", "AR"], DNS_RECORD_TYPES.PTR);
+ let aRecords = packet.getRecords(["AN", "AR"], DNS_RECORD_TYPES.A);
+
+ srvRecords.forEach(record => {
+ let data = record.data || {};
+
+ services[record.name] = {
+ host: data.target,
+ port: data.port,
+ ttl: record.ttl,
+ };
+ });
+
+ txtRecords.forEach(record => {
+ if (!services[record.name]) {
+ return;
+ }
+
+ services[record.name].attributes = record.data;
+ });
+
+ aRecords.forEach(record => {
+ if (IsIpv4Address(record.data)) {
+ hosts[record.name] = record.data;
+ }
+ });
+
+ ptrRecords.forEach(record => {
+ let name = record.data;
+ if (!services[name]) {
+ return;
+ }
+
+ let { host, port } = services[name];
+ if (!host || !port) {
+ return;
+ }
+
+ let { serviceName, serviceType, domainName } = _parseServiceDomainName(
+ name
+ );
+ if (!serviceName || !serviceType || !domainName) {
+ return;
+ }
+
+ let address = hosts[host];
+ if (!address) {
+ return;
+ }
+
+ let ttl = services[name].ttl || 0;
+ let serviceInfo = {
+ serviceName,
+ serviceType,
+ host,
+ address,
+ port,
+ domainName,
+ attributes: services[name].attributes || {},
+ };
+
+ this._onServiceFound(serviceInfo, ttl);
+ });
+ }
+
+ _onServiceFound(serviceInfo, ttl = 0) {
+ let expireTime = Date.now() + ttl * 1000;
+ let key =
+ serviceInfo.serviceName +
+ "." +
+ serviceInfo.serviceType +
+ "." +
+ serviceInfo.domainName +
+ " @" +
+ serviceInfo.address +
+ ":" +
+ serviceInfo.port;
+
+ // If this service was already discovered, just update
+ // its expiration time and don't re-emit it.
+ if (this._discovered.has(key)) {
+ this._discovered.get(key).expireTime = expireTime;
+ return;
+ }
+
+ this._discovered.set(key, {
+ serviceInfo,
+ expireTime,
+ });
+
+ let listeners = this._listeners.get(serviceInfo.serviceType) || [];
+ listeners.forEach(listener => {
+ listener.onServiceFound(serviceInfo);
+ });
+
+ DEBUG && debug("_onServiceFound()" + serviceInfo.serviceName);
+ }
+
+ /**
+ * Gets a non-exclusive socket on 0.0.0.0:{random} to send
+ * multicast queries on all interfaces. This socket does
+ * not need to join a multicast group since it is still
+ * able to *send* multicast queries, but it does not need
+ * to *listen* for multicast queries/announcements since
+ * the `_broadcastReceiverSocket` is already handling them.
+ */
+ _getQuerySocket() {
+ return new Promise((resolve, reject) => {
+ if (!this._querySocket) {
+ this._querySocket = _openSocket("0.0.0.0", 0, {
+ onPacketReceived: this._onPacketReceived.bind(this),
+ onStopListening: this._onStopListening.bind(this),
+ });
+ }
+ resolve(this._querySocket);
+ });
+ }
+
+ /**
+ * Gets a non-exclusive socket on 0.0.0.0:5353 to listen
+ * for multicast queries/announcements on all interfaces.
+ * Since this socket needs to listen for multicast queries
+ * and announcements, this socket joins the multicast
+ * group on *all* interfaces (0.0.0.0).
+ */
+ _getBroadcastReceiverSocket() {
+ return new Promise((resolve, reject) => {
+ if (!this._broadcastReceiverSocket) {
+ this._broadcastReceiverSocket = _openSocket(
+ "0.0.0.0",
+ MDNS_PORT,
+ {
+ onPacketReceived: this._onPacketReceived.bind(this),
+ onStopListening: this._onStopListening.bind(this),
+ },
+ /* multicastInterface = */ "0.0.0.0"
+ );
+ }
+ resolve(this._broadcastReceiverSocket);
+ });
+ }
+
+ /**
+ * Gets a non-exclusive socket for each interface on
+ * {iface-ip}:5353 for sending query responses as
+ * well as for listening for unicast queries. These
+ * sockets do not need to join a multicast group
+ * since they are still able to *send* multicast
+ * query responses, but they do not need to *listen*
+ * for multicast queries since the `_querySocket` is
+ * already handling them.
+ */
+ _getSockets() {
+ return new Promise(resolve => {
+ if (this._sockets.size > 0) {
+ resolve(this._sockets);
+ return;
+ }
+
+ Promise.all([getAddresses(), getHostname()]).then(() => {
+ _addresses.forEach(address => {
+ let socket = _openSocket(address, MDNS_PORT, null);
+ this._sockets.set(address, socket);
+ });
+
+ resolve(this._sockets);
+ });
+ });
+ }
+
+ _checkCloseSockets() {
+ // Nothing to do if no sockets to close.
+ if (this._sockets.size == 0) {
+ return;
+ }
+
+ // Don't close sockets if discovery listeners are still present.
+ if (this._listeners.size > 0) {
+ return;
+ }
+
+ // Don't close sockets if advertised services are present.
+ // Since we need to listen for service queries and respond to them.
+ if (this._services.size > 0) {
+ return;
+ }
+
+ this._closeSockets();
+ }
+
+ _closeSockets() {
+ this._sockets.forEach(socket => socket.close());
+ this._sockets.clear();
+ }
+
+ _onPacketReceived(socket, message) {
+ let packet = DNSPacket.parse(message.rawData);
+
+ switch (packet.getFlag("QR")) {
+ case DNS_QUERY_RESPONSE_CODES.QUERY:
+ this._handleQueryPacket(packet, message);
+ break;
+ case DNS_QUERY_RESPONSE_CODES.RESPONSE:
+ this._handleResponsePacket(packet, message);
+ break;
+ default:
+ break;
+ }
+ }
+
+ _onStopListening(socket, status) {
+ DEBUG && debug("_onStopListening() " + status);
+ }
+
+ _addServiceListener(serviceType, listener) {
+ let listeners = this._listeners.get(serviceType);
+ if (!listeners) {
+ listeners = [];
+ this._listeners.set(serviceType, listeners);
+ }
+
+ if (!listeners.find(l => l === listener)) {
+ listeners.push(listener);
+ }
+ }
+
+ _removeServiceListener(serviceType, listener) {
+ let listeners = this._listeners.get(serviceType);
+ if (!listeners) {
+ return;
+ }
+
+ let index = listeners.findIndex(l => l === listener);
+ if (index >= 0) {
+ listeners.splice(index, 1);
+ }
+
+ if (listeners.length === 0) {
+ this._listeners.delete(serviceType);
+ }
+ }
+}
+
+let _addresses;
+
+/**
+ * @private
+ */
+function getAddresses() {
+ return new Promise((resolve, reject) => {
+ if (_addresses) {
+ resolve(_addresses);
+ return;
+ }
+
+ networkInfoService.listNetworkAddresses({
+ onListedNetworkAddresses(aAddressArray) {
+ _addresses = aAddressArray.filter(address => {
+ return (
+ !address.includes("%p2p") && // No WiFi Direct interfaces
+ !address.includes(":") && // XXX: No IPv6 for now
+ address != "127.0.0.1"
+ ); // No ipv4 loopback addresses.
+ });
+
+ DEBUG && debug("getAddresses(): " + _addresses);
+ resolve(_addresses);
+ },
+
+ onListNetworkAddressesFailed() {
+ DEBUG && debug("getAddresses() FAILED!");
+ resolve([]);
+ },
+ });
+ });
+}
+
+let _hostname;
+
+/**
+ * @private
+ */
+function getHostname() {
+ return new Promise(resolve => {
+ if (_hostname) {
+ resolve(_hostname);
+ return;
+ }
+
+ networkInfoService.getHostname({
+ onGotHostname(aHostname) {
+ _hostname = aHostname.replace(/\s/g, "-") + ".local";
+
+ DEBUG && debug("getHostname(): " + _hostname);
+ resolve(_hostname);
+ },
+
+ onGetHostnameFailed() {
+ DEBUG && debug("getHostname() FAILED");
+ resolve("localhost");
+ },
+ });
+ });
+}
+
+/**
+ * Parse fully qualified domain name to service name, instance name,
+ * and domain name. See https://tools.ietf.org/html/rfc6763#section-7.
+ *
+ * Example: 'foo-bar-service._http._tcp.local' -> {
+ * serviceName: 'foo-bar-service',
+ * serviceType: '_http._tcp',
+ * domainName: 'local'
+ * }
+ *
+ * @private
+ */
+function _parseServiceDomainName(serviceDomainName) {
+ let parts = serviceDomainName.split(".");
+ let index = Math.max(parts.lastIndexOf("_tcp"), parts.lastIndexOf("_udp"));
+
+ return {
+ serviceName: parts.splice(0, index - 1).join("."),
+ serviceType: parts.splice(0, 2).join("."),
+ domainName: parts.join("."),
+ };
+}
+
+/**
+ * @private
+ */
+function _propertyBagToObject(propBag) {
+ let result = {};
+ if (propBag.QueryInterface) {
+ propBag.QueryInterface(Ci.nsIPropertyBag2);
+ for (let prop of propBag.enumerator) {
+ result[prop.name] = prop.value.toString();
+ }
+ } else {
+ for (let name in propBag) {
+ result[name] = propBag[name].toString();
+ }
+ }
+ return result;
+}
+
+/**
+ * @private
+ */
+function _openSocket(addr, port, handler, multicastInterface) {
+ let socket = Cc["@mozilla.org/network/udp-socket;1"].createInstance(
+ Ci.nsIUDPSocket
+ );
+ socket.init2(
+ addr,
+ port,
+ Services.scriptSecurityManager.getSystemPrincipal(),
+ true
+ );
+
+ if (handler) {
+ socket.asyncListen({
+ onPacketReceived: handler.onPacketReceived,
+ onStopListening: handler.onStopListening,
+ });
+ }
+
+ if (multicastInterface) {
+ socket.joinMulticast(MDNS_MULTICAST_GROUP, multicastInterface);
+ }
+
+ return socket;
+}
diff --git a/netwerk/dns/mdns/libmdns/moz.build b/netwerk/dns/mdns/libmdns/moz.build
new file mode 100644
index 0000000000..f9c025fa82
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/moz.build
@@ -0,0 +1,51 @@
+# -*- 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/.
+
+if CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
+ UNIFIED_SOURCES += [
+ "MDNSResponderOperator.cpp",
+ "MDNSResponderReply.cpp",
+ "nsDNSServiceDiscovery.cpp",
+ ]
+
+ LOCAL_INCLUDES += [
+ "/netwerk/base",
+ ]
+
+else:
+ EXTRA_JS_MODULES += [
+ "DNSServiceDiscovery.jsm",
+ "fallback/DataReader.jsm",
+ "fallback/DataWriter.jsm",
+ "fallback/DNSPacket.jsm",
+ "fallback/DNSRecord.jsm",
+ "fallback/DNSResourceRecord.jsm",
+ "fallback/DNSTypes.jsm",
+ "fallback/MulticastDNS.jsm",
+ ]
+
+ if CONFIG["MOZ_WIDGET_TOOLKIT"] == "android":
+ EXTRA_JS_MODULES += [
+ "MulticastDNSAndroid.jsm",
+ ]
+
+UNIFIED_SOURCES += [
+ "nsDNSServiceInfo.cpp",
+]
+
+XPCOM_MANIFESTS += [
+ "components.conf",
+]
+
+XPCOM_MANIFESTS += [
+ "components.conf",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+FINAL_LIBRARY = "xul"
+
+if CONFIG["CC_TYPE"] in ("clang", "gcc"):
+ CXXFLAGS += ["-Wno-error=shadow"]
diff --git a/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.cpp b/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.cpp
new file mode 100644
index 0000000000..12a23e56a4
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.cpp
@@ -0,0 +1,228 @@
+/* -*- 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 "nsDNSServiceDiscovery.h"
+#include "MDNSResponderOperator.h"
+#include "nsICancelable.h"
+#include "nsXULAppAPI.h"
+#include "private/pprio.h"
+
+namespace mozilla {
+namespace net {
+
+namespace {
+
+inline void StartService() {}
+
+inline void StopService() {}
+
+class ServiceCounter {
+ public:
+ static bool IsServiceRunning() { return !!sUseCount; }
+
+ private:
+ static uint32_t sUseCount;
+
+ protected:
+ ServiceCounter() {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!sUseCount++) {
+ StartService();
+ }
+ }
+
+ virtual ~ServiceCounter() {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!--sUseCount) {
+ StopService();
+ }
+ }
+};
+
+uint32_t ServiceCounter::sUseCount = 0;
+
+class DiscoveryRequest final : public nsICancelable, private ServiceCounter {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSICANCELABLE
+
+ explicit DiscoveryRequest(nsDNSServiceDiscovery* aService,
+ nsIDNSServiceDiscoveryListener* aListener);
+
+ private:
+ virtual ~DiscoveryRequest() { Cancel(NS_OK); }
+
+ RefPtr<nsDNSServiceDiscovery> mService;
+ nsIDNSServiceDiscoveryListener* mListener;
+};
+
+NS_IMPL_ISUPPORTS(DiscoveryRequest, nsICancelable)
+
+DiscoveryRequest::DiscoveryRequest(nsDNSServiceDiscovery* aService,
+ nsIDNSServiceDiscoveryListener* aListener)
+ : mService(aService), mListener(aListener) {}
+
+NS_IMETHODIMP
+DiscoveryRequest::Cancel(nsresult aReason) {
+ if (mService) {
+ mService->StopDiscovery(mListener);
+ }
+
+ mService = nullptr;
+ return NS_OK;
+}
+
+class RegisterRequest final : public nsICancelable, private ServiceCounter {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSICANCELABLE
+
+ explicit RegisterRequest(nsDNSServiceDiscovery* aService,
+ nsIDNSRegistrationListener* aListener);
+
+ private:
+ virtual ~RegisterRequest() { Cancel(NS_OK); }
+
+ RefPtr<nsDNSServiceDiscovery> mService;
+ nsIDNSRegistrationListener* mListener;
+};
+
+NS_IMPL_ISUPPORTS(RegisterRequest, nsICancelable)
+
+RegisterRequest::RegisterRequest(nsDNSServiceDiscovery* aService,
+ nsIDNSRegistrationListener* aListener)
+ : mService(aService), mListener(aListener) {}
+
+NS_IMETHODIMP
+RegisterRequest::Cancel(nsresult aReason) {
+ if (mService) {
+ mService->UnregisterService(mListener);
+ }
+
+ mService = nullptr;
+ return NS_OK;
+}
+
+} // namespace
+
+NS_IMPL_ISUPPORTS(nsDNSServiceDiscovery, nsIDNSServiceDiscovery)
+
+nsresult nsDNSServiceDiscovery::Init() {
+ if (!XRE_IsParentProcess()) {
+ MOZ_ASSERT(false,
+ "nsDNSServiceDiscovery can only be used in parent process");
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSServiceDiscovery::StartDiscovery(const nsACString& aServiceType,
+ nsIDNSServiceDiscoveryListener* aListener,
+ nsICancelable** aRetVal) {
+ MOZ_ASSERT(aRetVal);
+
+ nsresult rv;
+ if (NS_WARN_IF(NS_FAILED(rv = StopDiscovery(aListener)))) {
+ return rv;
+ }
+
+ nsCOMPtr<nsICancelable> req = new DiscoveryRequest(this, aListener);
+ RefPtr<BrowseOperator> browserOp =
+ new BrowseOperator(aServiceType, aListener);
+ if (NS_WARN_IF(NS_FAILED(rv = browserOp->Start()))) {
+ return rv;
+ }
+
+ mDiscoveryMap.Put(aListener, std::move(browserOp));
+
+ req.forget(aRetVal);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSServiceDiscovery::StopDiscovery(
+ nsIDNSServiceDiscoveryListener* aListener) {
+ nsresult rv;
+
+ RefPtr<BrowseOperator> browserOp;
+ if (!mDiscoveryMap.Get(aListener, getter_AddRefs(browserOp))) {
+ return NS_OK;
+ }
+
+ browserOp->Cancel(); // cancel non-started operation
+ if (NS_WARN_IF(NS_FAILED(rv = browserOp->Stop()))) {
+ return rv;
+ }
+
+ mDiscoveryMap.Remove(aListener);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSServiceDiscovery::RegisterService(nsIDNSServiceInfo* aServiceInfo,
+ nsIDNSRegistrationListener* aListener,
+ nsICancelable** aRetVal) {
+ MOZ_ASSERT(aRetVal);
+
+ nsresult rv;
+ if (NS_WARN_IF(NS_FAILED(rv = UnregisterService(aListener)))) {
+ return rv;
+ }
+
+ nsCOMPtr<nsICancelable> req = new RegisterRequest(this, aListener);
+ RefPtr<RegisterOperator> registerOp =
+ new RegisterOperator(aServiceInfo, aListener);
+ if (NS_WARN_IF(NS_FAILED(rv = registerOp->Start()))) {
+ return rv;
+ }
+
+ mRegisterMap.Put(aListener, std::move(registerOp));
+
+ req.forget(aRetVal);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSServiceDiscovery::UnregisterService(
+ nsIDNSRegistrationListener* aListener) {
+ nsresult rv;
+
+ RefPtr<RegisterOperator> registerOp;
+ if (!mRegisterMap.Get(aListener, getter_AddRefs(registerOp))) {
+ return NS_OK;
+ }
+
+ registerOp->Cancel(); // cancel non-started operation
+ if (NS_WARN_IF(NS_FAILED(rv = registerOp->Stop()))) {
+ return rv;
+ }
+
+ mRegisterMap.Remove(aListener);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSServiceDiscovery::ResolveService(nsIDNSServiceInfo* aServiceInfo,
+ nsIDNSServiceResolveListener* aListener) {
+ if (!ServiceCounter::IsServiceRunning()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv;
+
+ RefPtr<ResolveOperator> resolveOp =
+ new ResolveOperator(aServiceInfo, aListener);
+ if (NS_WARN_IF(NS_FAILED(rv = resolveOp->Start()))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.h b/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.h
new file mode 100644
index 0000000000..0acc9198bd
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.h
@@ -0,0 +1,47 @@
+/* -*- 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_netwerk_dns_mdns_libmdns_nsDNSServiceDiscovery_h
+#define mozilla_netwerk_dns_mdns_libmdns_nsDNSServiceDiscovery_h
+
+#include "nsIDNSServiceDiscovery.h"
+#include "nsCOMPtr.h"
+#include "mozilla/RefPtr.h"
+#include "nsRefPtrHashtable.h"
+
+namespace mozilla {
+namespace net {
+
+class BrowseOperator;
+class RegisterOperator;
+
+class nsDNSServiceDiscovery final : public nsIDNSServiceDiscovery {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIDNSSERVICEDISCOVERY
+
+ explicit nsDNSServiceDiscovery() = default;
+
+ /*
+ ** The mDNS service is started on demand. If no one uses, mDNS service will
+ ** not start. Therefore, all operations before service started will fail
+ ** and get error code |kDNSServiceErr_ServiceNotRunning| defined in dns_sd.h.
+ **/
+ nsresult Init();
+
+ nsresult StopDiscovery(nsIDNSServiceDiscoveryListener* aListener);
+ nsresult UnregisterService(nsIDNSRegistrationListener* aListener);
+
+ private:
+ virtual ~nsDNSServiceDiscovery() = default;
+
+ nsRefPtrHashtable<nsISupportsHashKey, BrowseOperator> mDiscoveryMap;
+ nsRefPtrHashtable<nsISupportsHashKey, RegisterOperator> mRegisterMap;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_netwerk_dns_mdns_libmdns_nsDNSServiceDiscovery_h
diff --git a/netwerk/dns/mdns/libmdns/nsDNSServiceInfo.cpp b/netwerk/dns/mdns/libmdns/nsDNSServiceInfo.cpp
new file mode 100644
index 0000000000..a6825a8d31
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/nsDNSServiceInfo.cpp
@@ -0,0 +1,170 @@
+/* -*- 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 "nsDNSServiceInfo.h"
+#include "nsHashPropertyBag.h"
+#include "nsIProperty.h"
+#include "nsISimpleEnumerator.h"
+#include "nsISupportsImpl.h"
+#include "mozilla/Unused.h"
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS(nsDNSServiceInfo, nsIDNSServiceInfo)
+
+nsDNSServiceInfo::nsDNSServiceInfo(nsIDNSServiceInfo* aServiceInfo) {
+ if (NS_WARN_IF(!aServiceInfo)) {
+ return;
+ }
+
+ nsAutoCString str;
+ uint16_t value;
+
+ if (NS_SUCCEEDED(aServiceInfo->GetHost(str))) {
+ Unused << NS_WARN_IF(NS_FAILED(SetHost(str)));
+ }
+ if (NS_SUCCEEDED(aServiceInfo->GetAddress(str))) {
+ Unused << NS_WARN_IF(NS_FAILED(SetAddress(str)));
+ }
+ if (NS_SUCCEEDED(aServiceInfo->GetPort(&value))) {
+ Unused << NS_WARN_IF(NS_FAILED(SetPort(value)));
+ }
+ if (NS_SUCCEEDED(aServiceInfo->GetServiceName(str))) {
+ Unused << NS_WARN_IF(NS_FAILED(SetServiceName(str)));
+ }
+ if (NS_SUCCEEDED(aServiceInfo->GetServiceType(str))) {
+ Unused << NS_WARN_IF(NS_FAILED(SetServiceType(str)));
+ }
+ if (NS_SUCCEEDED(aServiceInfo->GetDomainName(str))) {
+ Unused << NS_WARN_IF(NS_FAILED(SetDomainName(str)));
+ }
+
+ nsCOMPtr<nsIPropertyBag2> attributes; // deep copy
+ if (NS_SUCCEEDED(aServiceInfo->GetAttributes(getter_AddRefs(attributes)))) {
+ RefPtr<nsHashPropertyBag> newAttributes = new nsHashPropertyBag();
+ newAttributes->CopyFrom(attributes);
+ Unused << NS_WARN_IF(NS_FAILED(SetAttributes(newAttributes)));
+ }
+}
+
+NS_IMETHODIMP
+nsDNSServiceInfo::GetHost(nsACString& aHost) {
+ if (!mIsHostSet) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ aHost = mHost;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSServiceInfo::SetHost(const nsACString& aHost) {
+ mHost = aHost;
+ mIsHostSet = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSServiceInfo::GetAddress(nsACString& aAddress) {
+ if (!mIsAddressSet) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ aAddress = mAddress;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSServiceInfo::SetAddress(const nsACString& aAddress) {
+ mAddress = aAddress;
+ mIsAddressSet = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSServiceInfo::GetPort(uint16_t* aPort) {
+ if (NS_WARN_IF(!aPort)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ if (!mIsPortSet) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ *aPort = mPort;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSServiceInfo::SetPort(uint16_t aPort) {
+ mPort = aPort;
+ mIsPortSet = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSServiceInfo::GetServiceName(nsACString& aServiceName) {
+ if (!mIsServiceNameSet) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ aServiceName = mServiceName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSServiceInfo::SetServiceName(const nsACString& aServiceName) {
+ mServiceName = aServiceName;
+ mIsServiceNameSet = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSServiceInfo::GetServiceType(nsACString& aServiceType) {
+ if (!mIsServiceTypeSet) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ aServiceType = mServiceType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSServiceInfo::SetServiceType(const nsACString& aServiceType) {
+ mServiceType = aServiceType;
+ mIsServiceTypeSet = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSServiceInfo::GetDomainName(nsACString& aDomainName) {
+ if (!mIsDomainNameSet) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ aDomainName = mDomainName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSServiceInfo::SetDomainName(const nsACString& aDomainName) {
+ mDomainName = aDomainName;
+ mIsDomainNameSet = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSServiceInfo::GetAttributes(nsIPropertyBag2** aAttributes) {
+ if (!mIsAttributesSet) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ nsCOMPtr<nsIPropertyBag2> attributes(mAttributes);
+ attributes.forget(aAttributes);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSServiceInfo::SetAttributes(nsIPropertyBag2* aAttributes) {
+ mAttributes = aAttributes;
+ mIsAttributesSet = aAttributes ? true : false;
+ return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/mdns/libmdns/nsDNSServiceInfo.h b/netwerk/dns/mdns/libmdns/nsDNSServiceInfo.h
new file mode 100644
index 0000000000..31fffcc74e
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/nsDNSServiceInfo.h
@@ -0,0 +1,49 @@
+/* -*- 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_netwerk_dns_mdns_libmdns_nsDNSServiceInfo_h
+#define mozilla_netwerk_dns_mdns_libmdns_nsDNSServiceInfo_h
+
+#include "nsCOMPtr.h"
+#include "nsIDNSServiceDiscovery.h"
+#include "nsIPropertyBag2.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace net {
+
+class nsDNSServiceInfo final : public nsIDNSServiceInfo {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDNSSERVICEINFO
+
+ explicit nsDNSServiceInfo() = default;
+ explicit nsDNSServiceInfo(nsIDNSServiceInfo* aServiceInfo);
+
+ private:
+ virtual ~nsDNSServiceInfo() = default;
+
+ private:
+ nsCString mHost;
+ nsCString mAddress;
+ uint16_t mPort = 0;
+ nsCString mServiceName;
+ nsCString mServiceType;
+ nsCString mDomainName;
+ nsCOMPtr<nsIPropertyBag2> mAttributes;
+
+ bool mIsHostSet = false;
+ bool mIsAddressSet = false;
+ bool mIsPortSet = false;
+ bool mIsServiceNameSet = false;
+ bool mIsServiceTypeSet = false;
+ bool mIsDomainNameSet = false;
+ bool mIsAttributesSet = false;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_netwerk_dns_mdns_libmdns_nsDNSServiceInfo_h
diff --git a/netwerk/dns/mdns/moz.build b/netwerk/dns/mdns/moz.build
new file mode 100644
index 0000000000..561031af96
--- /dev/null
+++ b/netwerk/dns/mdns/moz.build
@@ -0,0 +1,13 @@
+# -*- 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 += ["libmdns"]
+
+XPIDL_SOURCES += [
+ "nsIDNSServiceDiscovery.idl",
+]
+
+XPIDL_MODULE = "necko_mdns"
diff --git a/netwerk/dns/mdns/nsIDNSServiceDiscovery.idl b/netwerk/dns/mdns/nsIDNSServiceDiscovery.idl
new file mode 100644
index 0000000000..23c678eccc
--- /dev/null
+++ b/netwerk/dns/mdns/nsIDNSServiceDiscovery.idl
@@ -0,0 +1,219 @@
+/* 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 nsIPropertyBag2;
+
+/**
+ * Service information
+ */
+[scriptable, uuid(670ed0f9-2fa5-4544-bf1e-ea58ac179374)]
+interface nsIDNSServiceInfo : nsISupports
+{
+ /**
+ * The host name of the service. (E.g. "Android.local.")
+ * @throws NS_ERROR_NOT_INITIALIZED when getting unset value.
+ */
+ attribute AUTF8String host;
+
+ /**
+ * The IP address of the service.
+ * @throws NS_ERROR_NOT_INITIALIZED when getting unset value.
+ */
+ attribute AUTF8String address;
+
+ /**
+ * The port number of the service. (E.g. 80)
+ * @throws NS_ERROR_NOT_INITIALIZED when getting unset value.
+ */
+ attribute unsigned short port;
+
+ /**
+ * The service name of the service for display. (E.g. "My TV")
+ * @throws NS_ERROR_NOT_INITIALIZED when getting unset value.
+ */
+ attribute AUTF8String serviceName;
+
+ /**
+ * The type of the service. (E.g. "_http._tcp")
+ * @throws NS_ERROR_NOT_INITIALIZED when getting unset value.
+ */
+ attribute AUTF8String serviceType;
+
+ /**
+ * The domain name of the service. (E.g. "local.")
+ * @throws NS_ERROR_NOT_INITIALIZED when getting unset value.
+ */
+ attribute AUTF8String domainName;
+
+ /**
+ * The attributes of the service.
+ */
+ attribute nsIPropertyBag2 attributes;
+};
+
+/**
+ * The callback interface for service discovery
+ */
+[scriptable, uuid(3025b7f2-97bb-435b-b43d-26731b3f5fc4)]
+interface nsIDNSServiceDiscoveryListener : nsISupports
+{
+ /**
+ * Callback when the discovery begins.
+ * @param aServiceType
+ * the service type of |startDiscovery|.
+ */
+ void onDiscoveryStarted(in AUTF8String aServiceType);
+
+ /**
+ * Callback when the discovery ends.
+ * @param aServiceType
+ * the service type of |startDiscovery|.
+ */
+ void onDiscoveryStopped(in AUTF8String aServiceType);
+
+ /**
+ * Callback when the a service is found.
+ * @param aServiceInfo
+ * the info about the found service, where |serviceName|, |aServiceType|, and |domainName| are set.
+ */
+ void onServiceFound(in nsIDNSServiceInfo aServiceInfo);
+
+ /**
+ * Callback when the a service is lost.
+ * @param aServiceInfo
+ * the info about the lost service, where |serviceName|, |aServiceType|, and |domainName| are set.
+ */
+ void onServiceLost(in nsIDNSServiceInfo aServiceInfo);
+
+ /**
+ * Callback when the discovery cannot start.
+ * @param aServiceType
+ * the service type of |startDiscovery|.
+ * @param aErrorCode
+ * the error code.
+ */
+ void onStartDiscoveryFailed(in AUTF8String aServiceType, in long aErrorCode);
+
+ /**
+ * Callback when the discovery cannot stop.
+ * @param aServiceType
+ * the service type of |startDiscovery|.
+ * @param aErrorCode
+ * the error code.
+ */
+ void onStopDiscoveryFailed(in AUTF8String aServiceType, in long aErrorCode);
+};
+
+/**
+ * The callback interface for service registration
+ */
+[scriptable, uuid(e165e4be-abf4-4963-a66d-ed3ca116e5e4)]
+interface nsIDNSRegistrationListener : nsISupports
+{
+ const long ERROR_SERVICE_NOT_RUNNING = -65563;
+
+ /**
+ * Callback when the service is registered successfully.
+ * @param aServiceInfo
+ * the info about the registered service,
+ * where |serviceName|, |aServiceType|, and |domainName| are set.
+ */
+ void onServiceRegistered(in nsIDNSServiceInfo aServiceInfo);
+
+ /**
+ * Callback when the service is unregistered successfully.
+ * @param aServiceInfo
+ * the info about the unregistered service.
+ */
+ void onServiceUnregistered(in nsIDNSServiceInfo aServiceInfo);
+
+ /**
+ * Callback when the service cannot be registered.
+ * @param aServiceInfo
+ * the info about the service to be registered.
+ * @param aErrorCode
+ * the error code.
+ */
+ void onRegistrationFailed(in nsIDNSServiceInfo aServiceInfo, in long aErrorCode);
+
+ /**
+ * Callback when the service cannot be unregistered.
+ * @param aServiceInfo
+ * the info about the registered service.
+ * @param aErrorCode
+ * the error code.
+ */
+ void onUnregistrationFailed(in nsIDNSServiceInfo aServiceInfo, in long aErrorCode);
+};
+
+/**
+ * The callback interface for service resolve
+ */
+[scriptable, uuid(24ee6408-648e-421d-accf-c6e5adeccf97)]
+interface nsIDNSServiceResolveListener : nsISupports
+{
+ /**
+ * Callback when the service is resolved successfully.
+ * @param aServiceInfo
+ * the info about the resolved service, where |host| and |port| are set.
+ */
+ void onServiceResolved(in nsIDNSServiceInfo aServiceInfo);
+
+ /**
+ * Callback when the service cannot be resolved.
+ * @param aServiceInfo
+ * the info about the service to be resolved.
+ * @param aErrorCode
+ * the error code.
+ */
+ void onResolveFailed(in nsIDNSServiceInfo aServiceInfo, in long aErrorCode);
+};
+
+/**
+ * The interface for DNS service discovery/registration/resolve
+ */
+[scriptable, uuid(6487899b-beb1-455a-ba65-e4fd465066d7)]
+interface nsIDNSServiceDiscovery : nsISupports
+{
+ /**
+ * Browse for instances of a service.
+ * @param aServiceType
+ * the service type to be discovered, E.g. "_http._tcp".
+ * @param aListener
+ * callback interface for discovery notifications.
+ * @return An object that can be used to cancel the service discovery.
+ */
+ nsICancelable startDiscovery(in AUTF8String aServiceType, in nsIDNSServiceDiscoveryListener aListener);
+
+ /**
+ * Register a service that is discovered via |startDiscovery| and |resolveService| calls.
+ * @param aServiceInfo
+ * the service information to be registered.
+ * |port| and |aServiceType| are required attributes.
+ * @param aListener
+ * callback interface for registration notifications.
+ * @return An object that can be used to cancel the service registration.
+ */
+ nsICancelable registerService(in nsIDNSServiceInfo aServiceInfo, in nsIDNSRegistrationListener aListener);
+
+ /**
+ * Resolve a service name discovered via |startDiscovery| to a target host name, port number.
+ * @param aServiceInfo
+ * the service information to be registered.
+ * |serviceName|, |aServiceType|, and |domainName| are required attributes as reported to the |onServiceFound| callback.
+ * @param aListener
+ * callback interface for registration notifications.
+ */
+ void resolveService(in nsIDNSServiceInfo aServiceInfo, in nsIDNSServiceResolveListener aListener);
+};
+
+%{ C++
+#define DNSSERVICEDISCOVERY_CONTRACT_ID \
+ "@mozilla.org/toolkit/components/mdnsresponder/dns-sd;1"
+#define DNSSERVICEINFO_CONTRACT_ID \
+ "@mozilla.org/toolkit/components/mdnsresponder/dns-info;1"
+%}
diff --git a/netwerk/dns/moz.build b/netwerk/dns/moz.build
new file mode 100644
index 0000000000..bc5ff1e58c
--- /dev/null
+++ b/netwerk/dns/moz.build
@@ -0,0 +1,107 @@
+# -*- 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 += ["mdns", "tests"]
+
+XPIDL_SOURCES += [
+ "nsIDNSByTypeRecord.idl",
+ "nsIDNSListener.idl",
+ "nsIDNSRecord.idl",
+ "nsIDNSResolverInfo.idl",
+ "nsIDNSService.idl",
+ "nsIEffectiveTLDService.idl",
+ "nsIIDNService.idl",
+ "nsINativeDNSResolverOverride.idl",
+ "nsPIDNSService.idl",
+]
+
+XPIDL_MODULE = "necko_dns"
+
+EXTRA_JS_MODULES["netwerk-dns"] += [
+ "PublicSuffixList.jsm",
+]
+
+XPCSHELL_TESTS_MANIFESTS += ["tests/unit/xpcshell.ini"]
+
+EXPORTS += [
+ "nsEffectiveTLDService.h",
+]
+
+EXPORTS.mozilla.net += [
+ "ChildDNSService.h",
+ "DNS.h",
+ "DNSByTypeRecord.h",
+ "DNSListenerProxy.h",
+ "DNSRequestBase.h",
+ "DNSRequestChild.h",
+ "DNSRequestParent.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.
+ "nsHostResolver.cpp", # Redefines LOG
+]
+
+UNIFIED_SOURCES += [
+ "ChildDNSService.cpp",
+ "DNS.cpp",
+ "DNSListenerProxy.cpp",
+ "DNSPacket.cpp",
+ "DNSRequestChild.cpp",
+ "DNSRequestParent.cpp",
+ "DNSResolverInfo.cpp",
+ "HTTPSSVC.cpp",
+ "IDNBlocklistUtils.cpp",
+ "NativeDNSResolverOverrideChild.cpp",
+ "NativeDNSResolverOverrideParent.cpp",
+ "nsDNSService2.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/protocol/http",
+]
+
+USE_LIBS += ["icu"]
+
+if CONFIG["CC_TYPE"] in ("clang", "gcc"):
+ CXXFLAGS += ["-Wno-error=shadow"]
diff --git a/netwerk/dns/nsDNSService2.cpp b/netwerk/dns/nsDNSService2.cpp
new file mode 100644
index 0000000000..443c226116
--- /dev/null
+++ b/netwerk/dns/nsDNSService2.cpp
@@ -0,0 +1,1453 @@
+/* -*- 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 "nsNetCID.h"
+#include "nsError.h"
+#include "nsDNSPrefetch.h"
+#include "nsThreadUtils.h"
+#include "nsIProtocolProxyService.h"
+#include "prsystem.h"
+#include "prnetdb.h"
+#include "prmon.h"
+#include "prio.h"
+#include "plstr.h"
+#include "nsCharSeparatedTokenizer.h"
+#include "nsNetAddr.h"
+#include "nsProxyRelease.h"
+#include "nsQueryObject.h"
+#include "nsIObserverService.h"
+#include "nsINetworkLinkService.h"
+#include "DNSResolverInfo.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 kPrefDisableIPv6[] = "network.dns.disableIPv6";
+static const char kPrefDisablePrefetch[] = "network.dns.disablePrefetch";
+static const char kPrefBlockDotOnion[] = "network.dns.blockDotOnion";
+static const char kPrefDnsLocalDomains[] = "network.dns.localDomains";
+static const char kPrefDnsForceResolve[] = "network.dns.forceResolve";
+static const char kPrefDnsOfflineLocalhost[] = "network.dns.offline-localhost";
+static const char kPrefDnsNotifyResolution[] = "network.dns.notifyResolution";
+static const char kPrefNetworkProxySOCKS[] = "network.proxy.socks";
+
+//-----------------------------------------------------------------------------
+
+class nsDNSRecord : public nsIDNSAddrRecord {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIDNSRECORD
+ NS_DECL_NSIDNSADDRRECORD
+
+ explicit nsDNSRecord(nsHostRecord* hostRecord)
+ : mIterGenCnt(-1), mDone(false) {
+ 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; // the generation count of
+ // mHostRecord->addr_info when we
+ // start iterating
+ bool mDone;
+};
+
+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::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(uint32_t* aMode) {
+ *aMode = mHostRecord->EffectiveTRRMode();
+ return NS_OK;
+}
+
+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;
+}
+
+//-----------------------------------------------------------------------------
+
+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,
+ uint16_t 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;
+ const OriginAttributes
+ mOriginAttributes; // The originAttributes for this resolving
+ nsCOMPtr<nsIDNSListener> mListener;
+ uint16_t mFlags;
+ uint16_t mAF;
+
+ 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)) {
+ 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));
+ 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)
+ : mDone(false), mStatus(NS_OK), mMonitor(mon) {}
+
+ void OnResolveHostComplete(nsHostResolver*, nsHostRecord*, nsresult) override;
+ bool EqualsAsyncListener(nsIDNSListener* aListener) override;
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const override;
+
+ bool mDone;
+ nsresult mStatus;
+ RefPtr<nsHostRecord> mHostRecord;
+
+ private:
+ virtual ~nsDNSSyncRequest() = default;
+
+ PRMonitor* mMonitor;
+};
+
+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;
+};
+
+//-----------------------------------------------------------------------------
+
+nsDNSService::nsDNSService()
+ : mLock("nsDNSServer.mLock"),
+ mDisableIPv6(false),
+ mDisablePrefetch(false),
+ mBlockDotOnion(false),
+ mNotifyResolution(false),
+ mOfflineLocalhost(false),
+ mForceResolveOn(false),
+ mTrrService(nullptr),
+ mHasSocksProxy(false),
+ mResCacheEntries(0),
+ mResCacheExpiration(0),
+ mResCacheGrace(0),
+ mResolverPrefsUpdated(false) {}
+
+nsDNSService::~nsDNSService() = default;
+
+NS_IMPL_ISUPPORTS(nsDNSService, nsIDNSService, nsPIDNSService, nsIObserver,
+ nsIMemoryReporter)
+
+/******************************************************************************
+ * nsDNSService impl:
+ * singleton instance ctor/dtor methods
+ ******************************************************************************/
+static StaticRefPtr<nsDNSService> gDNSService;
+
+already_AddRefed<nsIDNSService> nsDNSService::GetXPCOMSingleton() {
+ if (nsIOService::UseSocketProcess()) {
+ if (XRE_IsSocketProcess()) {
+ return GetSingleton();
+ }
+
+ if (XRE_IsContentProcess() || XRE_IsParentProcess()) {
+ return ChildDNSService::GetSingleton();
+ }
+
+ return nullptr;
+ }
+
+ if (XRE_IsParentProcess()) {
+ return GetSingleton();
+ }
+
+ if (XRE_IsContentProcess() || XRE_IsSocketProcess()) {
+ return ChildDNSService::GetSingleton();
+ }
+
+ return nullptr;
+}
+
+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);
+}
+
+nsresult nsDNSService::ReadPrefs(const char* 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, kPrefDisableIPv6)) {
+ if (NS_SUCCEEDED(Preferences::GetBool(kPrefDisableIPv6, &tmpbool))) {
+ mDisableIPv6 = tmpbool;
+ }
+ }
+ if (!name || !strcmp(name, kPrefDnsOfflineLocalhost)) {
+ if (NS_SUCCEEDED(
+ Preferences::GetBool(kPrefDnsOfflineLocalhost, &tmpbool))) {
+ mOfflineLocalhost = tmpbool;
+ }
+ }
+ if (!name || !strcmp(name, kPrefDisablePrefetch)) {
+ if (NS_SUCCEEDED(Preferences::GetBool(kPrefDisablePrefetch, &tmpbool))) {
+ mDisablePrefetch = 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, kPrefNetworkProxySOCKS)) {
+ nsAutoCString socks;
+ if (NS_SUCCEEDED(Preferences::GetCString(kPrefNetworkProxySOCKS, socks))) {
+ mHasSocksProxy = !socks.IsEmpty();
+ }
+ }
+ 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.PutEntry(token);
+ }
+ }
+ if (!name || !strcmp(name, kPrefDnsForceResolve)) {
+ Preferences::GetCString(kPrefDnsForceResolve, mForceResolve);
+ mForceResolveOn = !mForceResolve.IsEmpty();
+ }
+
+ if (StaticPrefs::network_proxy_type() ==
+ nsIProtocolProxyService::PROXYCONFIG_MANUAL) {
+ // Disable prefetching either by explicit preference or if a
+ // manual proxy is configured
+ mDisablePrefetch = true;
+ }
+ return NS_OK;
+}
+
+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(kPrefDisableIPv6, this, false);
+ prefs->AddObserver(kPrefDnsOfflineLocalhost, this, false);
+ prefs->AddObserver(kPrefDisablePrefetch, this, false);
+ prefs->AddObserver(kPrefBlockDotOnion, this, false);
+ prefs->AddObserver(kPrefDnsNotifyResolution, this, false);
+
+ // Monitor these to see if there is a change in proxy configuration
+ prefs->AddObserver(kPrefNetworkProxySOCKS, this, false);
+ }
+
+ nsDNSPrefetch::Initialize(this);
+
+ RegisterWeakMemoryReporter(this);
+
+ 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 = mResolver;
+ mResolver = nullptr;
+ }
+ if (res) {
+ 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;
+}
+
+bool nsDNSService::DNSForbiddenByActiveProxy(const nsACString& aHostname,
+ uint32_t flags) {
+ if (flags & nsIDNSService::RESOLVE_IGNORE_SOCKS_DNS) {
+ return false;
+ }
+
+ // We should avoid doing DNS when a proxy is in use.
+ PRNetAddr tempAddr;
+ if (StaticPrefs::network_proxy_type() ==
+ nsIProtocolProxyService::PROXYCONFIG_MANUAL &&
+ mHasSocksProxy && StaticPrefs::network_proxy_socks_remote_dns()) {
+ // Allow IP lookups through, but nothing else.
+ if (PR_StringToNetAddr(nsCString(aHostname).get(), &tempAddr) !=
+ PR_SUCCESS) {
+ return true;
+ }
+ }
+ return false;
+}
+
+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, uint32_t flags,
+ nsIDNSResolverInfo* aResolver, 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.GetEntry(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 = GetMainThreadEventTarget();
+ }
+
+ 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, DNSResolverInfo::URL(aResolver),
+ type, aOriginAttributes, listener, flags, af);
+ if (!req) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ rv = res->ResolveHost(req->mHost, DNSResolverInfo::URL(aResolver), type,
+ req->mOriginAttributes, flags, af, req);
+ req.forget(result);
+ return rv;
+}
+
+nsresult nsDNSService::CancelAsyncResolveInternal(
+ const nsACString& aHostname, uint16_t aType, uint32_t aFlags,
+ nsIDNSResolverInfo* aResolver, 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.GetEntry(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, DNSResolverInfo::URL(aResolver), aType,
+ aOriginAttributes, aFlags, af, aListener, aReason);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSService::AsyncResolve(const nsACString& aHostname,
+ nsIDNSService::ResolveType aType, uint32_t flags,
+ nsIDNSResolverInfo* aResolver,
+ nsIDNSListener* listener, nsIEventTarget* target_,
+ JS::HandleValue 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, aResolver, listener,
+ target_, attrs, result);
+}
+
+NS_IMETHODIMP
+nsDNSService::AsyncResolveNative(const nsACString& aHostname,
+ nsIDNSService::ResolveType aType,
+ uint32_t flags, nsIDNSResolverInfo* aResolver,
+ nsIDNSListener* aListener,
+ nsIEventTarget* target_,
+ const OriginAttributes& aOriginAttributes,
+ nsICancelable** result) {
+ return AsyncResolveInternal(aHostname, aType, flags, aResolver, aListener,
+ target_, aOriginAttributes, result);
+}
+
+NS_IMETHODIMP
+nsDNSService::NewTRRResolverInfo(const nsACString& aTrrURL,
+ nsIDNSResolverInfo** aResolver) {
+ RefPtr<DNSResolverInfo> res = new DNSResolverInfo(aTrrURL);
+ res.forget(aResolver);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSService::CancelAsyncResolve(const nsACString& aHostname,
+ nsIDNSService::ResolveType aType,
+ uint32_t aFlags, nsIDNSResolverInfo* aResolver,
+ nsIDNSListener* aListener, nsresult aReason,
+ JS::HandleValue 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, aResolver,
+ aListener, aReason, attrs);
+}
+
+NS_IMETHODIMP
+nsDNSService::CancelAsyncResolveNative(
+ const nsACString& aHostname, nsIDNSService::ResolveType aType,
+ uint32_t aFlags, nsIDNSResolverInfo* aResolver, nsIDNSListener* aListener,
+ nsresult aReason, const OriginAttributes& aOriginAttributes) {
+ return CancelAsyncResolveInternal(aHostname, aType, aFlags, aResolver,
+ aListener, aReason, aOriginAttributes);
+}
+
+NS_IMETHODIMP
+nsDNSService::Resolve(const nsACString& aHostname, uint32_t flags,
+ JS::HandleValue 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, uint32_t 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, uint32_t flags,
+ const OriginAttributes& aOriginAttributes, nsIDNSRecord** result) {
+ return ResolveInternal(aHostname, flags, aOriginAttributes, result);
+}
+
+nsresult nsDNSService::ResolveInternal(
+ const nsACString& aHostname, uint32_t 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.GetEntry(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, 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;
+ if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
+ nsAutoCString converted = NS_ConvertUTF16toUTF8(data);
+ if (mResolver && !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(mResolver, NS_ERROR_NOT_INITIALIZED);
+ if (mResolverPrefsUpdated && mResolver) {
+ mResolver->SetCacheLimits(mResCacheEntries, mResCacheExpiration,
+ mResCacheGrace);
+ }
+ } else if (!strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+ Shutdown();
+ }
+
+ if (flushCache && mResolver) {
+ mResolver->FlushCache(false);
+ return NS_OK;
+ }
+
+ return NS_OK;
+}
+
+uint16_t nsDNSService::GetAFForLookup(const nsACString& host, uint32_t flags) {
+ if (mDisableIPv6 || (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 (PL_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) {
+ NS_ENSURE_TRUE(mResolver, NS_ERROR_NOT_INITIALIZED);
+ mResolver->GetDNSCacheEntries(args);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSService::ClearCache(bool aTrrToo) {
+ NS_ENSURE_TRUE(mResolver, NS_ERROR_NOT_INITIALIZED);
+ mResolver->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::GetCurrentTrrURI(nsACString& aURI) {
+ if (mTrrService) {
+ return mTrrService->GetURI(aURI);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSService::GetCurrentTrrMode(uint32_t* aMode) {
+ *aMode = 0; // The default mode.
+ if (mTrrService) {
+ *aMode = mTrrService->Mode();
+ }
+ 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 (auto iter = mFailedSVCDomainNames.ConstIter(); !iter.Done();
+ iter.Next()) {
+ n += iter.UserData()->ShallowSizeOfExcludingThis(mallocSizeOf);
+ for (const auto& name : *iter.UserData()) {
+ 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);
+
+ nsTArray<nsCString>* failedList = mFailedSVCDomainNames.Get(aOwnerName);
+ if (!failedList) {
+ failedList = new nsTArray<nsCString>(1);
+ mFailedSVCDomainNames.Put(aOwnerName, failedList);
+ }
+
+ failedList->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;
+}
diff --git a/netwerk/dns/nsDNSService2.h b/netwerk/dns/nsDNSService2.h
new file mode 100644
index 0000000000..06e901ddbb
--- /dev/null
+++ b/netwerk/dns/nsDNSService2.h
@@ -0,0 +1,109 @@
+/* -*- 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 "nsClassHashtable.h"
+#include "nsPIDNSService.h"
+#include "nsIIDNService.h"
+#include "nsIMemoryReporter.h"
+#include "nsIObserver.h"
+#include "nsHostResolver.h"
+#include "nsString.h"
+#include "nsTHashtable.h"
+#include "nsHashKeys.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/Attributes.h"
+#include "TRRService.h"
+
+class nsAuthSSPI;
+
+class nsDNSService final : public nsPIDNSService,
+ public nsIObserver,
+ public nsIMemoryReporter {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSPIDNSSERVICE
+ NS_DECL_NSIDNSSERVICE
+ NS_DECL_NSIOBSERVER
+ NS_DECL_NSIMEMORYREPORTER
+
+ nsDNSService();
+
+ static already_AddRefed<nsIDNSService> GetXPCOMSingleton();
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
+ bool GetOffline() const;
+
+ protected:
+ friend class nsAuthSSPI;
+
+ nsresult DeprecatedSyncResolve(
+ const nsACString& aHostname, uint32_t flags,
+ const mozilla::OriginAttributes& aOriginAttributes,
+ nsIDNSRecord** result);
+
+ private:
+ ~nsDNSService();
+
+ nsresult ReadPrefs(const char* name);
+ static already_AddRefed<nsDNSService> GetSingleton();
+
+ uint16_t GetAFForLookup(const nsACString& host, uint32_t flags);
+
+ nsresult PreprocessHostname(bool aLocalDomain, const nsACString& aInput,
+ nsIIDNService* aIDN, nsACString& aACE);
+
+ nsresult AsyncResolveInternal(
+ const nsACString& aHostname, uint16_t type, uint32_t flags,
+ nsIDNSResolverInfo* aResolver, nsIDNSListener* aListener,
+ nsIEventTarget* target_,
+ const mozilla::OriginAttributes& aOriginAttributes,
+ nsICancelable** result);
+
+ nsresult CancelAsyncResolveInternal(
+ const nsACString& aHostname, uint16_t aType, uint32_t aFlags,
+ nsIDNSResolverInfo* aResolver, nsIDNSListener* aListener,
+ nsresult aReason, const mozilla::OriginAttributes& aOriginAttributes);
+
+ nsresult ResolveInternal(const nsACString& aHostname, uint32_t flags,
+ const mozilla::OriginAttributes& aOriginAttributes,
+ nsIDNSRecord** result);
+
+ bool DNSForbiddenByActiveProxy(const nsACString& aHostname, uint32_t flags);
+
+ RefPtr<nsHostResolver> mResolver;
+ nsCOMPtr<nsIIDNService> mIDN;
+
+ // mLock protects access to mResolver, mLocalDomains, mIPv4OnlyDomains and
+ // mFailedSVCDomainNames
+ mozilla::Mutex 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 mDisableIPv6;
+ bool mDisablePrefetch;
+ bool mBlockDotOnion;
+ bool mNotifyResolution;
+ bool mOfflineLocalhost;
+ bool mForceResolveOn;
+ nsTHashtable<nsCStringHashKey> mLocalDomains;
+ RefPtr<mozilla::net::TRRService> mTrrService;
+ mozilla::Atomic<bool, mozilla::Relaxed> mHasSocksProxy;
+
+ uint32_t mResCacheEntries;
+ uint32_t mResCacheExpiration;
+ uint32_t mResCacheGrace;
+ bool mResolverPrefsUpdated;
+ nsClassHashtable<nsCStringHashKey, nsTArray<nsCString>> mFailedSVCDomainNames;
+};
+
+#endif // nsDNSService2_h__
diff --git a/netwerk/dns/nsEffectiveTLDService.cpp b/netwerk/dns/nsEffectiveTLDService.cpp
new file mode 100644
index 0000000000..3a2270a212
--- /dev/null
+++ b/netwerk/dns/nsEffectiveTLDService.cpp
@@ -0,0 +1,492 @@
+/* -*- 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 "MainThreadUtils.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 "prnetdb.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()
+ : mIDNService(), mGraphLock("nsEffectiveTLDService::mGraph") {
+ mGraph.emplace(etld_dafsa::kDafsa);
+}
+
+nsresult nsEffectiveTLDService::Init() {
+ 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 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
+ PRNetAddr addr;
+ PRStatus result = PR_StringToNetAddr(aHostname.get(), &addr);
+ if (result == PR_SUCCESS) {
+ // 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) {
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *aResult = false;
+
+ // If the strings are the same, we obviously have a match.
+ if (aInput == aHost) {
+ *aResult = true;
+ return NS_OK;
+ }
+
+ // If aHost is not found, we know we do not have it as a root domain.
+ int32_t index = nsAutoCString(aInput).Find(aHost.BeginReading());
+ if (index == kNotFound) {
+ return NS_OK;
+ }
+
+ // Otherwise, we have aHost as our root domain iff the index of aHost is
+ // aHost.length subtracted from our length and (since we do not have an
+ // exact match) the character before the index is a dot or slash.
+ *aResult = index > 0 && (uint32_t)index == aInput.Length() - aHost.Length() &&
+ (aInput[index - 1] == '.' || aInput[index - 1] == '/');
+ return NS_OK;
+}
diff --git a/netwerk/dns/nsEffectiveTLDService.h b/netwerk/dns/nsEffectiveTLDService.h
new file mode 100644
index 0000000000..9a9ad22977
--- /dev/null
+++ b/netwerk/dns/nsEffectiveTLDService.h
@@ -0,0 +1,92 @@
+//* -*- 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_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();
+
+ nsCOMPtr<nsIIDNService> mIDNService;
+
+ // The DAFSA provides a compact encoding of the rather large eTLD list.
+ mozilla::Maybe<mozilla::Dafsa> mGraph;
+
+ // Memory map used for a new updated dafsa
+ mozilla::loader::AutoMemMap mDafsaMap;
+
+ // 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;
+ }
+ };
+
+ TldCache mMruTable;
+};
+
+#endif // EffectiveTLDService_h
diff --git a/netwerk/dns/nsHostResolver.cpp b/netwerk/dns/nsHostResolver.cpp
new file mode 100644
index 0000000000..268e2ec38b
--- /dev/null
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -0,0 +1,2325 @@
+/* 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 "nsISupportsBase.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 "plstr.h"
+#include "nsQueryObject.h"
+#include "nsURLHelper.h"
+#include "nsThreadUtils.h"
+#include "nsThreadPool.h"
+#include "GetAddrInfo.h"
+#include "GeckoProfiler.h"
+#include "TRR.h"
+#include "TRRQuery.h"
+#include "TRRService.h"
+
+#include "mozilla/Atomics.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"
+
+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 MAX_RESOLVER_THREADS in size. New
+// requests go first to an idle thread. If that cannot be found and there are
+// fewer than MAX_RESOLVER_THREADS 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 HighThreadThreshold
+// 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 HighThreadThreshold in size a thread will be
+// destroyed after ShortIdleTimeoutSeconds of idle time. Smaller pools use
+// LongIdleTimeoutSeconds for a timeout period.
+
+#define HighThreadThreshold MAX_RESOLVER_THREADS_FOR_ANY_PRIORITY
+#define LongIdleTimeoutSeconds 300 // for threads 1 -> HighThreadThreshold
+#define ShortIdleTimeoutSeconds \
+ 60 // for threads HighThreadThreshold+1 -> MAX_RESOLVER_THREADS
+
+static_assert(
+ HighThreadThreshold <= MAX_RESOLVER_THREADS,
+ "High Thread Threshold should be less equal Maximum allowed thread");
+
+//----------------------------------------------------------------------------
+
+namespace mozilla::net {
+LazyLogModule gHostResolverLog("nsHostResolver");
+#define LOG(args) \
+ MOZ_LOG(mozilla::net::gHostResolverLog, mozilla::LogLevel::Debug, args)
+#define LOG1(args) \
+ MOZ_LOG(mozilla::net::gHostResolverLog, mozilla::LogLevel::Error, args)
+#define LOG_ENABLED() \
+ MOZ_LOG_TEST(mozilla::net::gHostResolverLog, mozilla::LogLevel::Debug)
+} // 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;
+ }
+
+ LOG(("Calling 'res_ninit'.\n"));
+
+ mLastReset = PR_IntervalNow();
+ return (res_ninit(&_res) == 0);
+ }
+
+ private:
+ PRIntervalTime mLastReset;
+};
+
+#endif // RES_RETRY_ON_FAILURE
+
+//----------------------------------------------------------------------------
+
+static inline bool IsHighPriority(uint16_t flags) {
+ return !(flags & (nsHostResolver::RES_PRIORITY_LOW |
+ nsHostResolver::RES_PRIORITY_MEDIUM));
+}
+
+static inline bool IsMediumPriority(uint16_t flags) {
+ return flags & nsHostResolver::RES_PRIORITY_MEDIUM;
+}
+
+static inline bool IsLowPriority(uint16_t flags) {
+ return flags & nsHostResolver::RES_PRIORITY_LOW;
+}
+
+//----------------------------------------------------------------------------
+// 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)
+
+nsHostKey::nsHostKey(const nsACString& aHost, const nsACString& aTrrServer,
+ uint16_t aType, uint16_t 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;
+}
+
+NS_IMPL_ISUPPORTS0(nsHostRecord)
+
+nsHostRecord::nsHostRecord(const nsHostKey& key)
+ : nsHostKey(key),
+ mEffectiveTRRMode(nsIRequest::TRR_DEFAULT_MODE),
+ mTRRQuery("nsHostRecord.mTRRQuery"),
+ mResolving(0),
+ negative(false),
+ mDoomed(false) {}
+
+void nsHostRecord::Invalidate() { mDoomed = true; }
+
+void nsHostRecord::Cancel() {
+ RefPtr<TRRQuery> query;
+ {
+ auto lock = mTRRQuery.Lock();
+ query.swap(lock.ref());
+ }
+
+ if (query) {
+ query->Cancel();
+ }
+}
+
+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);
+}
+
+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;
+}
+
+bool nsHostRecord::HasUsableResult(const mozilla::TimeStamp& now,
+ uint16_t queryFlags) const {
+ if (mDoomed) {
+ return false;
+ }
+
+ // 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 HasUsableResultInternal();
+}
+
+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),
+ addr_info_lock("AddrHostRecord.addr_info_lock"),
+ addr_info_gencnt(0),
+ addr_info(nullptr),
+ addr(nullptr),
+ mTRRUsed(false),
+ mTRRSuccess(0),
+ mNativeSuccess(0) {}
+
+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 {
+ 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::ResolveComplete() {
+ if (LoadNativeUsed()) {
+ if (mNativeSuccess) {
+ uint32_t millis = static_cast<uint32_t>(mNativeDuration.ToMilliseconds());
+ Telemetry::Accumulate(Telemetry::DNS_NATIVE_LOOKUP_TIME, millis);
+ }
+ AccumulateCategoricalKeyed(
+ TRRService::AutoDetectedKey(),
+ mNativeSuccess ? Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::osOK
+ : Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::osFail);
+ }
+
+ if (mTRRUsed) {
+ if (mTRRSuccess) {
+ uint32_t millis = static_cast<uint32_t>(mTrrDuration.ToMilliseconds());
+ Telemetry::Accumulate(Telemetry::DNS_TRR_LOOKUP_TIME2,
+ TRRService::AutoDetectedKey(), millis);
+ }
+ AccumulateCategoricalKeyed(
+ TRRService::AutoDetectedKey(),
+ mTRRSuccess ? Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::trrOK
+ : Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::trrFail);
+ }
+
+ if (nsHostResolver::Mode() == MODE_TRRFIRST) {
+ Telemetry::Accumulate(Telemetry::TRR_SKIP_REASON_TRR_FIRST,
+ mTRRTRRSkippedReason);
+
+ if (mNativeSuccess) {
+ Telemetry::Accumulate(Telemetry::TRR_SKIP_REASON_DNS_WORKED,
+ mTRRTRRSkippedReason);
+ }
+ }
+
+ 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_DISABLED2,
+ TRRService::AutoDetectedKey(), mNativeSuccess);
+ } else {
+ if (mTRRSuccess) {
+ AccumulateCategoricalKeyed(TRRService::AutoDetectedKey(),
+ Telemetry::LABELS_DNS_TRR_FIRST3::TRR);
+ } else if (mNativeSuccess) {
+ if (mTRRUsed) {
+ AccumulateCategoricalKeyed(
+ TRRService::AutoDetectedKey(),
+ Telemetry::LABELS_DNS_TRR_FIRST3::NativeAfterTRR);
+ } else {
+ AccumulateCategoricalKeyed(TRRService::AutoDetectedKey(),
+ Telemetry::LABELS_DNS_TRR_FIRST3::Native);
+ }
+ } else {
+ AccumulateCategoricalKeyed(
+ TRRService::AutoDetectedKey(),
+ Telemetry::LABELS_DNS_TRR_FIRST3::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 (mTRRUsed && !mTRRSuccess && mNativeSuccess && gTRRService) {
+ gTRRService->AddToBlocklist(nsCString(host), originSuffix, pb, true);
+ }
+}
+
+AddrHostRecord::DnsPriority AddrHostRecord::GetPriority(uint16_t aFlags) {
+ if (IsHighPriority(aFlags)) {
+ return AddrHostRecord::DNS_PRIORITY_HIGH;
+ }
+ if (IsMediumPriority(aFlags)) {
+ return AddrHostRecord::DNS_PRIORITY_MEDIUM;
+ }
+
+ return AddrHostRecord::DNS_PRIORITY_LOW;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(TypeHostRecord, nsHostRecord, TypeHostRecord,
+ nsIDNSTXTRecord, nsIDNSHTTPSSVCRecord)
+
+TypeHostRecord::TypeHostRecord(const nsHostKey& key)
+ : nsHostRecord(key),
+ DNSHTTPSSVCRecordBase(key.host),
+ mResultsLock("TypeHostRecord.mResultsLock"),
+ mAllRecordsExcluded(false) {}
+
+TypeHostRecord::~TypeHostRecord() { mCallbacks.clear(); }
+
+bool TypeHostRecord::HasUsableResultInternal() const {
+ return !mResults.is<Nothing>();
+}
+
+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);
+
+ 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);
+
+ if (!mResults.is<TypeRecordHTTPSSVC>()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ *aResult = mAllRecordsExcluded;
+ return NS_OK;
+}
+
+//----------------------------------------------------------------------------
+
+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;
+
+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),
+ mLock("nsHostResolver.mLock"),
+ mIdleTaskCV(mLock, "nsHostResolver.mIdleTaskCV"),
+ mEvictionQSize(0),
+ mShutdown(true),
+ mNumIdleTasks(0),
+ mActiveTaskCount(0),
+ mActiveAnyThreadCount(0),
+ mPendingCount(0) {
+ mCreationTime = PR_Now();
+
+ mLongIdleTimeout = TimeDuration::FromSeconds(LongIdleTimeoutSeconds);
+ mShortIdleTimeout = TimeDuration::FromSeconds(ShortIdleTimeoutSeconds);
+}
+
+nsHostResolver::~nsHostResolver() = default;
+
+nsresult nsHostResolver::Init() {
+ 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) {
+ LOG(("Calling 'res_ninit'.\n"));
+ res_ninit(&_res);
+ }
+#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);
+ }
+
+ nsCOMPtr<nsIThreadPool> threadPool = new nsThreadPool();
+ MOZ_ALWAYS_SUCCEEDS(threadPool->SetThreadLimit(MAX_RESOLVER_THREADS));
+ MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadLimit(MAX_RESOLVER_THREADS));
+ 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->mTRRTRRSkippedReason);
+ } else {
+ mozilla::net::TypeRecordResultType empty(Nothing{});
+ CompleteLookupByType(rec, NS_ERROR_ABORT, empty, 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);
+ 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();
+ mRecordDB.Remove(*static_cast<nsHostKey*>(rec));
+ }
+ mEvictionQ.clear();
+ }
+
+ // 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)) {
+ if (record->isInList()) {
+ record->remove();
+ }
+ 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;
+
+ // Move queues to temporary lists.
+ pendingQHigh = std::move(mHighQ);
+ pendingQMed = std::move(mMediumQ);
+ pendingQLow = std::move(mLowQ);
+ evictionQ = std::move(mEvictionQ);
+
+ mEvictionQSize = 0;
+ mPendingCount = 0;
+
+ if (mNumIdleTasks) {
+ mIdleTaskCV.NotifyAll();
+ }
+
+ for (auto iter = mRecordDB.Iter(); !iter.Done(); iter.Next()) {
+ iter.UserData()->Cancel();
+ }
+ // empty host database
+ mRecordDB.Clear();
+
+ mNCS = nullptr;
+ }
+
+ ClearPendingQueue(pendingQHigh);
+ ClearPendingQueue(pendingQMed);
+ ClearPendingQueue(pendingQLow);
+
+ if (!evictionQ.isEmpty()) {
+ for (const RefPtr<nsHostRecord>& rec : evictionQ) {
+ rec->Cancel();
+ }
+ }
+
+ pendingQHigh.clear();
+ pendingQMed.clear();
+ pendingQLow.clear();
+ evictionQ.clear();
+
+ // 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, uint16_t 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>& entry = mRecordDB.GetOrInsert(key);
+ if (!entry) {
+ entry = InitRecord(key);
+ }
+
+ RefPtr<nsHostRecord> rec = entry;
+ 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;
+ PRNetAddr prAddr;
+ memset(&prAddr, 0, sizeof(prAddr));
+ if (key.af == PR_AF_INET || key.af == PR_AF_UNSPEC) {
+ MOZ_RELEASE_ASSERT(PR_StringToNetAddr("127.0.0.1", &prAddr) == PR_SUCCESS);
+ addresses.AppendElement(NetAddr(&prAddr));
+ }
+ if (key.af == PR_AF_INET6 || key.af == PR_AF_UNSPEC) {
+ MOZ_RELEASE_ASSERT(PR_StringToNetAddr("::1", &prAddr) == PR_SUCCESS);
+ addresses.AppendElement(NetAddr(&prAddr));
+ }
+
+ RefPtr<AddrInfo> ai = new AddrInfo(rec->host, 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();
+}
+
+nsresult nsHostResolver::ResolveHost(const nsACString& aHost,
+ const nsACString& aTrrServer,
+ uint16_t type,
+ const OriginAttributes& aOriginAttributes,
+ uint16_t 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;
+ }
+
+ // By-Type requests use only TRR. If TRR is disabled we can return
+ // immediately.
+ if (IS_OTHER_TYPE(type) && Mode() == MODE_TRROFF) {
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ // Used to try to parse to an IP address literal.
+ PRNetAddr tempAddr;
+ // Unfortunately, PR_StringToNetAddr does not properly initialize
+ // the output buffer in the case of IPv6 input. See bug 223145.
+ memset(&tempAddr, 0, sizeof(PRNetAddr));
+
+ if (IS_OTHER_TYPE(type) &&
+ (PR_StringToNetAddr(host.get(), &tempAddr) == PR_SUCCESS)) {
+ // For by-type queries the host cannot be IP literal.
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+ memset(&tempAddr, 0, sizeof(PRNetAddr));
+
+ 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.
+
+ bool excludedFromTRR = false;
+
+ if (gTRRService && gTRRService->IsExcludedFromTRR(host)) {
+ flags |= RES_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>& entry = mRecordDB.GetOrInsert(key);
+ if (!entry) {
+ entry = InitRecord(key);
+ }
+
+ RefPtr<nsHostRecord> rec = entry;
+ 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 (excludedFromTRR) {
+ rec->RecordReason(nsHostRecord::TRR_EXCLUDED);
+ }
+
+ if (!(flags & RES_BYPASS_CACHE) &&
+ rec->HasUsableResult(TimeStamp::NowLoRes(), flags)) {
+ LOG((" Using cached record for host [%s].\n", host.get()));
+ // put reference to host record on stack...
+ result = rec;
+ if (IS_ADDR_TYPE(type)) {
+ 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(rec, host);
+
+ if (rec->negative) {
+ LOG((" Negative cache entry for host [%s].\n", host.get()));
+ if (IS_ADDR_TYPE(type)) {
+ Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
+ METHOD_NEGATIVE_HIT);
+ }
+ status = NS_ERROR_UNKNOWN_HOST;
+ }
+
+ // Check whether host is a IP address for A/AAAA queries.
+ // For by-type records we have already checked at the beginning of
+ // this function.
+ } 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()));
+ Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_LITERAL);
+ result = rec;
+ } else if (addrRec &&
+ PR_StringToNetAddr(host.get(), &tempAddr) == PR_SUCCESS) {
+ // 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()));
+
+ // ok, just copy the result into the host record, and be
+ // done with it! ;-)
+ addrRec->addr = MakeUnique<NetAddr>();
+ PRNetAddrToNetAddr(&tempAddr, addrRec->addr.get());
+ // put reference to host record on stack...
+ Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_LITERAL);
+ result = rec;
+
+ // Check if we have received too many requests.
+ } else if (mPendingCount >= 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) {
+ // If this is an IPV4 or IPV6 specific request, check if there is
+ // an AF_UNSPEC entry we can use. Otherwise, hit the resolver...
+ if (addrRec && !(flags & RES_BYPASS_CACHE) &&
+ ((af == PR_AF_INET) || (af == PR_AF_INET6))) {
+ // Check for an AF_UNSPEC entry.
+
+ const nsHostKey unspecKey(
+ host, aTrrServer, nsIDNSService::RESOLVE_TYPE_DEFAULT, flags,
+ PR_AF_UNSPEC, (aOriginAttributes.mPrivateBrowsingId > 0),
+ originSuffix);
+ RefPtr<nsHostRecord> unspecRec = mRecordDB.Get(unspecKey);
+
+ TimeStamp now = TimeStamp::NowLoRes();
+ if (unspecRec && unspecRec->HasUsableResult(now, flags)) {
+ 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", host.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) {
+ rec->negative = unspecRec->negative;
+ rec->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->IsTRR(), std::move(addresses));
+ addrRec->addr_info_gencnt++;
+ rec->CopyExpirationTimesAndFlagsFrom(unspecRec);
+ }
+ }
+ }
+ // Now check if we have a new record.
+ if (rec->HasUsableResult(now, flags)) {
+ result = rec;
+ if (rec->negative) {
+ status = NS_ERROR_UNKNOWN_HOST;
+ }
+ Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
+ ConditionallyRefreshRecord(rec, host);
+ } 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.",
+ host.get()));
+ result = rec;
+ rec->negative = true;
+ status = NS_ERROR_UNKNOWN_HOST;
+ Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
+ METHOD_NEGATIVE_HIT);
+ }
+ }
+ }
+
+ // 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);
+ 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);
+
+ // Only A/AAAA records are place in a queue. The queues are for
+ // the native resolver, therefore by-type request are never put
+ // into a queue.
+ if (addrRec && addrRec->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.
+ rec->remove();
+ mHighQ.insertBack(rec);
+ rec->flags = flags;
+ ConditionallyCreateThread(rec);
+ } else if (IsMediumPriority(flags) && IsLowPriority(rec->flags)) {
+ // Move from low to med.
+ rec->remove();
+ mMediumQ.insertBack(rec);
+ rec->flags = flags;
+ mIdleTaskCV.Notify();
+ }
+ }
+ }
+
+ if (result && callback->isInList()) {
+ callback->remove();
+ }
+ } // lock
+
+ if (result) {
+ callback->OnResolveHostComplete(this, result, status);
+ }
+
+ return rv;
+}
+
+void nsHostResolver::DetachCallback(
+ const nsACString& host, const nsACString& aTrrServer, uint16_t aType,
+ const OriginAttributes& aOriginAttributes, uint16_t 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 < HighThreadThreshold) ||
+ (IsHighPriority(rec->flags) &&
+ mActiveTaskCount < MAX_RESOLVER_THREADS)) {
+ 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, pushedTRR);
+}
+
+void nsHostResolver::MaybeRenewHostRecord(nsHostRecord* aRec) {
+ MutexAutoLock lock(mLock);
+ MaybeRenewHostRecordLocked(aRec);
+}
+
+void nsHostResolver::MaybeRenewHostRecordLocked(nsHostRecord* aRec) {
+ mLock.AssertCurrentThreadOwns();
+ if (aRec->isInList()) {
+ // we're already on the eviction queue. This is a renewal
+ MOZ_ASSERT(mEvictionQSize);
+ AssertOnQ(aRec, mEvictionQ);
+ aRec->remove();
+ mEvictionQSize--;
+ }
+}
+
+// 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, TRR* pushedTRR) {
+ if (Mode() == 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);
+
+ auto hasConnectivity = [this]() -> bool {
+ 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;
+ };
+
+ nsIRequest::TRRMode reqMode = rec->mEffectiveTRRMode;
+ if (rec->mTrrServer.IsEmpty() &&
+ (!gTRRService || !gTRRService->Enabled(reqMode))) {
+ 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.
+ rec->RecordReason(nsHostRecord::TRR_IS_OFFLINE);
+ }
+ if (!hasConnectivity()) {
+ rec->RecordReason(nsHostRecord::TRR_NO_CONNECTIVITY);
+ } else {
+ rec->RecordReason(nsHostRecord::TRR_NOT_CONFIRMED);
+ }
+
+ LOG(("TrrLookup:: %s service not enabled\n", rec->host.get()));
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+
+ MaybeRenewHostRecordLocked(rec);
+
+ RefPtr<TRRQuery> query = new TRRQuery(this, rec);
+ nsresult rv = query->DispatchLookup(pushedTRR);
+ if (NS_FAILED(rv)) {
+ rec->RecordReason(nsHostRecord::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++;
+ return NS_OK;
+}
+
+void nsHostResolver::AssertOnQ(nsHostRecord* rec,
+ LinkedList<RefPtr<nsHostRecord>>& q) {
+#ifdef DEBUG
+ MOZ_ASSERT(!q.isEmpty());
+ MOZ_ASSERT(rec->isInList());
+ for (const RefPtr<nsHostRecord>& r : q) {
+ if (rec == r) {
+ return;
+ }
+ }
+ MOZ_ASSERT(false, "Did not find element");
+#endif
+}
+
+nsresult nsHostResolver::NativeLookup(nsHostRecord* aRec) {
+ if (StaticPrefs::network_dns_disabled()) {
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+ LOG(("NativeLookup host:%s af:%" PRId16, aRec->host.get(), aRec->af));
+
+ // Only A/AAAA request are resolve natively.
+ MOZ_ASSERT(aRec->IsAddrRecord());
+ mLock.AssertCurrentThreadOwns();
+
+ RefPtr<nsHostRecord> rec(aRec);
+ RefPtr<AddrHostRecord> addrRec;
+ addrRec = do_QueryObject(rec);
+ MOZ_ASSERT(addrRec);
+
+ addrRec->mNativeStart = TimeStamp::Now();
+
+ // Add rec to one of the pending queues, possibly removing it from mEvictionQ.
+ MaybeRenewHostRecordLocked(rec);
+
+ switch (AddrHostRecord::GetPriority(rec->flags)) {
+ case AddrHostRecord::DNS_PRIORITY_HIGH:
+ mHighQ.insertBack(rec);
+ break;
+
+ case AddrHostRecord::DNS_PRIORITY_MEDIUM:
+ mMediumQ.insertBack(rec);
+ break;
+
+ case AddrHostRecord::DNS_PRIORITY_LOW:
+ mLowQ.insertBack(rec);
+ break;
+ }
+ mPendingCount++;
+
+ addrRec->StoreNative(true);
+ addrRec->StoreNativeUsed(true);
+ addrRec->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),
+ static_cast<uint32_t>(mPendingCount)));
+
+ return rv;
+}
+
+// static
+ResolverMode nsHostResolver::Mode() {
+ if (gTRRService) {
+ return static_cast<ResolverMode>(gTRRService->Mode());
+ }
+
+ // If we don't have a TRR service just return MODE_TRROFF so we don't make
+ // any TRR requests by mistake.
+ return MODE_TRROFF;
+}
+
+nsIRequest::TRRMode nsHostRecord::TRRMode() {
+ return nsIDNSService::GetTRRModeFromFlags(flags);
+}
+
+// static
+void nsHostResolver::ComputeEffectiveTRRMode(nsHostRecord* aRec) {
+ 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 (!gTRRService) {
+ aRec->RecordReason(nsHostRecord::TRR_NO_GSERVICE);
+ aRec->mEffectiveTRRMode = requestMode;
+ return;
+ }
+
+ if (!aRec->mTrrServer.IsEmpty()) {
+ aRec->mEffectiveTRRMode = nsIRequest::TRR_ONLY_MODE;
+ return;
+ }
+
+ if (gTRRService->IsExcludedFromTRR(aRec->host)) {
+ aRec->RecordReason(nsHostRecord::TRR_EXCLUDED);
+ aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
+ return;
+ }
+
+ if (StaticPrefs::network_dns_skipTRR_when_parental_control_enabled() &&
+ gTRRService->ParentalControlEnabled()) {
+ aRec->RecordReason(nsHostRecord::TRR_PARENTAL_CONTROL);
+ aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
+ return;
+ }
+
+ if (resolverMode == MODE_TRROFF) {
+ aRec->RecordReason(nsHostRecord::TRR_OFF_EXPLICIT);
+ aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
+ return;
+ }
+
+ if (requestMode == nsIRequest::TRR_DISABLED_MODE) {
+ aRec->RecordReason(nsHostRecord::TRR_REQ_MODE_DISABLED);
+ aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
+ return;
+ }
+
+ if ((requestMode == nsIRequest::TRR_DEFAULT_MODE &&
+ resolverMode == MODE_NATIVEONLY)) {
+ aRec->RecordReason(nsHostRecord::TRR_MODE_NOT_ENABLED);
+ aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE;
+ return;
+ }
+
+ if (requestMode == nsIRequest::TRR_DEFAULT_MODE &&
+ resolverMode == MODE_TRRFIRST) {
+ aRec->mEffectiveTRRMode = nsIRequest::TRR_FIRST_MODE;
+ return;
+ }
+
+ if (requestMode == nsIRequest::TRR_DEFAULT_MODE &&
+ resolverMode == 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) {
+ LOG(("NameLookup host:%s af:%" PRId16, rec->host.get(), rec->af));
+
+ 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 wronly report the reason for the previous one.
+ rec->mTRRTRRSkippedReason = nsHostRecord::TRR_UNSET;
+
+ ComputeEffectiveTRRMode(rec);
+
+ if (rec->IsAddrRecord()) {
+ RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
+ MOZ_ASSERT(addrRec);
+
+ addrRec->StoreNativeUsed(false);
+ addrRec->mTRRUsed = false;
+ addrRec->mNativeSuccess = false;
+ }
+
+ 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 & RES_DISABLE_TRR) {
+ LOG(("TRR with server and DISABLE_TRR flag. Returning error."));
+ return NS_ERROR_UNKNOWN_HOST;
+ }
+ return TrrLookup(rec);
+ }
+
+ LOG(("NameLookup: %s effectiveTRRmode: %d flags: %X", rec->host.get(),
+ rec->mEffectiveTRRMode, rec->flags));
+
+ if (rec->flags & RES_DISABLE_TRR) {
+ rec->RecordReason(nsHostRecord::TRR_DISABLED_FLAG);
+ }
+
+ if (rec->mEffectiveTRRMode != nsIRequest::TRR_DISABLED_MODE &&
+ !((rec->flags & RES_DISABLE_TRR))) {
+ rv = TrrLookup(rec);
+ }
+
+ bool serviceNotReady = !gTRRService || !gTRRService->IsConfirmed();
+
+ if (rec->mEffectiveTRRMode == nsIRequest::TRR_DISABLED_MODE ||
+ (rec->mEffectiveTRRMode == nsIRequest::TRR_FIRST_MODE &&
+ (rec->flags & RES_DISABLE_TRR || serviceNotReady || NS_FAILED(rv)))) {
+ if (!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(addrRec && !addrRec->mTRRUsed);
+#endif
+
+ rv = NativeLookup(rec);
+ }
+
+ return rv;
+}
+
+nsresult nsHostResolver::ConditionallyRefreshRecord(nsHostRecord* rec,
+ const nsACString& host) {
+ if ((rec->CheckExpiration(TimeStamp::NowLoRes()) != nsHostRecord::EXP_VALID ||
+ rec->negative) &&
+ !rec->mResolving) {
+ LOG((" Using %s cache entry for host [%s] but starting async renewal.",
+ rec->negative ? "negative" : "positive", host.BeginReading()));
+ NameLookup(rec);
+
+ 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;
+}
+
+void nsHostResolver::DeQueue(LinkedList<RefPtr<nsHostRecord>>& aQ,
+ AddrHostRecord** aResult) {
+ RefPtr<nsHostRecord> rec = aQ.popFirst();
+ mPendingCount--;
+ MOZ_ASSERT(rec->IsAddrRecord());
+ RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
+ MOZ_ASSERT(addrRec);
+ addrRec.forget(aResult);
+}
+
+bool nsHostResolver::GetHostToLookup(AddrHostRecord** result) {
+ bool timedOut = false;
+ TimeDuration timeout;
+ TimeStamp epoch, now;
+
+ MutexAutoLock lock(mLock);
+
+ timeout = (mNumIdleTasks >= HighThreadThreshold) ? 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))
+
+ if (!mHighQ.isEmpty()) {
+ DeQueue(mHighQ, result);
+ SET_GET_TTL(*result, false);
+ return true;
+ }
+
+ if (mActiveAnyThreadCount < HighThreadThreshold) {
+ if (!mMediumQ.isEmpty()) {
+ DeQueue(mMediumQ, result);
+ mActiveAnyThreadCount++;
+ (*result)->StoreUsingAnyThread(true);
+ SET_GET_TTL(*result, true);
+ return true;
+ }
+
+ if (!mLowQ.isEmpty()) {
+ DeQueue(mLowQ, result);
+ mActiveAnyThreadCount++;
+ (*result)->StoreUsingAnyThread(true);
+ SET_GET_TTL(*result, true);
+ 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->IsTRR() != rrset2->IsTRR()) {
+ 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) {
+ if (rec->isInList()) {
+ MOZ_DIAGNOSTIC_ASSERT(!mEvictionQ.contains(rec),
+ "Already in eviction queue");
+ MOZ_DIAGNOSTIC_ASSERT(!mHighQ.contains(rec), "Already in high queue");
+ MOZ_DIAGNOSTIC_ASSERT(!mMediumQ.contains(rec), "Already in med queue");
+ MOZ_DIAGNOSTIC_ASSERT(!mLowQ.contains(rec), "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.
+ rec->remove();
+ }
+ mEvictionQ.insertBack(rec);
+ if (mEvictionQSize < mMaxCacheEntries) {
+ mEvictionQSize++;
+ } else {
+ // remove first element on mEvictionQ
+ RefPtr<nsHostRecord> head = mEvictionQ.popFirst();
+ mRecordDB.Remove(*static_cast<nsHostKey*>(head.get()));
+
+ if (!head->negative) {
+ // record the age of the entry upon eviction.
+ TimeDuration age = TimeStamp::NowLoRes() - head->mValidStart;
+ if (rec->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 (rec->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));
+ }
+ }
+ }
+ }
+}
+
+//
+// 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, nsHostRecord::TRRSkippedReason aReason) {
+ MutexAutoLock lock(mLock);
+ 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);
+
+ bool trrResult = newRRSet && newRRSet->IsTRR();
+ if (NS_FAILED(status)) {
+ newRRSet = nullptr;
+ }
+
+ if (addrRec->LoadResolveAgain() && (status != NS_ERROR_ABORT) && !trrResult) {
+ 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 trr=%d stillResolving=%d\n",
+ addrRec->host.get(), aNewRRSet, (unsigned int)status,
+ aNewRRSet ? aNewRRSet->IsTRR() : 0, int(addrRec->mResolving)));
+
+ if (trrResult) {
+ 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->mTRRUsed = false;
+ } else {
+ addrRec->mTRRUsed = true;
+ }
+
+ if (NS_FAILED(status)) {
+ if (aReason != nsHostRecord::TRR_UNSET) {
+ addrRec->RecordReason(aReason);
+ } else {
+ // Unknown failed reason.
+ addrRec->RecordReason(nsHostRecord::TRR_FAILED);
+ }
+ } else {
+ addrRec->mTRRSuccess++;
+ addrRec->RecordReason(nsHostRecord::TRR_OK);
+ }
+
+ if (NS_FAILED(status) &&
+ addrRec->mEffectiveTRRMode == nsIRequest::TRR_FIRST_MODE &&
+ status != NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
+ MOZ_ASSERT(!addrRec->mResolving);
+ NativeLookup(addrRec);
+ 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;
+ }
+ }
+
+ // This should always be cleared when a request is completed.
+ addrRec->StoreNative(false);
+
+ // 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);
+ }
+
+ if (!addrRec->mResolving && !mShutdown) {
+ {
+ auto trrQuery = addrRec->mTRRQuery.Lock();
+ if (trrQuery.ref()) {
+ addrRec->mTrrDuration = trrQuery.ref()->Duration();
+ }
+ trrQuery.ref() = nullptr;
+ }
+ addrRec->ResolveComplete();
+
+ AddToEvictionQ(rec);
+ }
+
+#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 & ~RES_PRIORITY_MEDIUM) | RES_PRIORITY_LOW;
+ DebugOnly<nsresult> rv = NativeLookup(rec);
+ 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, uint32_t aTtl, bool pb) {
+ MutexAutoLock lock(mLock);
+ 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--;
+
+ {
+ auto lock = rec->mTRRQuery.Lock();
+ lock.ref() = nullptr;
+ }
+
+ uint32_t duration = static_cast<uint32_t>(
+ (TimeStamp::Now() - typeRec->mStart).ToMilliseconds());
+
+ 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(), NEGATIVE_RECORD_LIFETIME, 0);
+ MOZ_ASSERT(aResult.is<TypeRecordEmpty>());
+ status = NS_ERROR_UNKNOWN_HOST;
+ typeRec->negative = true;
+ Telemetry::Accumulate(Telemetry::DNS_BY_TYPE_FAILED_LOOKUP_TIME, duration);
+ } 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;
+ Telemetry::Accumulate(Telemetry::DNS_BY_TYPE_SUCCEEDED_LOOKUP_TIME,
+ duration);
+ }
+
+ 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);
+ }
+
+ AddToEvictionQ(rec);
+ return LOOKUP_OK;
+}
+
+void nsHostResolver::CancelAsyncRequest(
+ const nsACString& host, const nsACString& aTrrServer, uint16_t aType,
+ const OriginAttributes& aOriginAttributes, uint16_t 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) {
+ nsHostRecord* recPtr = nullptr;
+
+ for (const RefPtr<nsResolveHostCallback>& c : rec->mCallbacks) {
+ if (c->EqualsAsyncListener(aListener)) {
+ c->remove();
+ recPtr = rec;
+ c->OnResolveHostComplete(this, recPtr, status);
+ break;
+ }
+ }
+
+ // If there are no more callbacks, remove the hash table entry
+ if (recPtr && recPtr->mCallbacks.isEmpty()) {
+ mRecordDB.Remove(*static_cast<nsHostKey*>(recPtr));
+ // If record is on a Queue, remove it and then deref it
+ if (recPtr->isInList()) {
+ recPtr->remove();
+ }
+ }
+ }
+}
+
+size_t nsHostResolver::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const {
+ MutexAutoLock lock(mLock);
+
+ size_t n = mallocSizeOf(this);
+
+ n += mRecordDB.ShallowSizeOfExcludingThis(mallocSizeOf);
+ for (auto iter = mRecordDB.ConstIter(); !iter.Done(); iter.Next()) {
+ auto* entry = iter.UserData();
+ 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<AddrHostRecord> rec;
+ RefPtr<AddrInfo> ai;
+
+ do {
+ if (!rec) {
+ RefPtr<AddrHostRecord> 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);
+ 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
+
+ { // obtain lock to check shutdown and manage inter-module telemetry
+ MutexAutoLock lock(mLock);
+
+ if (!mShutdown) {
+ TimeDuration elapsed = TimeStamp::Now() - startTime;
+ uint32_t millis = static_cast<uint32_t>(elapsed.ToMilliseconds());
+
+ if (NS_SUCCEEDED(status)) {
+ Telemetry::HistogramID histogramID;
+ if (!rec->addr_info_gencnt) {
+ // Time for initial lookup.
+ histogramID = Telemetry::DNS_LOOKUP_TIME;
+ } else if (!getTtl) {
+ // Time for renewal; categorized by expiration strategy.
+ histogramID = Telemetry::DNS_RENEWAL_TIME;
+ } else {
+ // Time to get TTL; categorized by expiration strategy.
+ histogramID = Telemetry::DNS_RENEWAL_TIME_FOR_TTL;
+ }
+ Telemetry::Accumulate(histogramID, millis);
+ } else {
+ Telemetry::Accumulate(Telemetry::DNS_FAILED_LOOKUP_TIME, millis);
+ }
+ }
+ }
+
+ 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->mTRRTRRSkippedReason)) {
+ // 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);
+
+ 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 (auto iter = mRecordDB.Iter(); !iter.Done(); iter.Next()) {
+ // We don't pay attention to address literals, only resolved domains.
+ // Also require a host.
+ nsHostRecord* rec = iter.UserData();
+ 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 = iter.Key().originSuffix;
+
+ 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..798d0d0065
--- /dev/null
+++ b/netwerk/dns/nsHostResolver.h
@@ -0,0 +1,654 @@
+/* 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 "mozilla/Mutex.h"
+#include "nsISupportsImpl.h"
+#include "nsIDNSListener.h"
+#include "nsIDNSService.h"
+#include "nsTArray.h"
+#include "GetAddrInfo.h"
+#include "mozilla/net/DNS.h"
+#include "mozilla/net/DashboardTypes.h"
+#include "mozilla/AtomicBitfields.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/LinkedList.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/UniquePtr.h"
+#include "nsRefPtrHashtable.h"
+#include "nsIThreadPool.h"
+#include "mozilla/net/NetworkConnectivityService.h"
+#include "nsIDNSByTypeRecord.h"
+#include "mozilla/net/DNSByTypeRecord.h"
+#include "mozilla/Maybe.h"
+
+class nsHostResolver;
+class nsResolveHostCallback;
+namespace mozilla {
+namespace net {
+class TRR;
+class TRRQuery;
+enum ResolverMode {
+ 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
+};
+} // namespace net
+} // namespace mozilla
+
+#define TRR_DISABLED(x) (((x) == MODE_NATIVEONLY) || ((x) == MODE_TRROFF))
+
+extern mozilla::Atomic<bool, mozilla::Relaxed> gNativeIsLocalhost;
+
+#define MAX_RESOLVER_THREADS_FOR_ANY_PRIORITY 3
+#define MAX_RESOLVER_THREADS_FOR_HIGH_PRIORITY 5
+#define MAX_NON_PRIORITY_REQUESTS 150
+
+#define MAX_RESOLVER_THREADS \
+ (MAX_RESOLVER_THREADS_FOR_ANY_PRIORITY + \
+ MAX_RESOLVER_THREADS_FOR_HIGH_PRIORITY)
+
+struct nsHostKey {
+ const nsCString host;
+ const nsCString mTrrServer;
+ uint16_t type;
+ uint16_t flags;
+ uint16_t af;
+ bool pb;
+ const nsCString originSuffix;
+ explicit nsHostKey(const nsACString& host, const nsACString& aTrrServer,
+ uint16_t type, uint16_t 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 {
+ 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();
+
+ // IMPORTANT: when adding new values, always add them to the end, otherwise
+ // it will mess up telemetry.
+ enum TRRSkippedReason : uint32_t {
+ 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
+ };
+
+ // Records the first reason that caused TRR to be skipped or to fail.
+ void RecordReason(TRRSkippedReason reason) {
+ if (mTRRTRRSkippedReason == TRR_UNSET) {
+ mTRRTRRSkippedReason = reason;
+ }
+ }
+
+ protected:
+ friend class nsHostResolver;
+ 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,
+ uint16_t queryFlags = 0) const;
+
+ enum DnsPriority {
+ DNS_PRIORITY_LOW,
+ DNS_PRIORITY_MEDIUM,
+ DNS_PRIORITY_HIGH,
+ };
+ static DnsPriority GetPriority(uint16_t aFlags);
+
+ virtual void Cancel();
+ virtual bool HasUsableResultInternal() const { return false; }
+
+ mozilla::LinkedList<RefPtr<nsResolveHostCallback>> mCallbacks;
+
+ bool IsAddrRecord() const {
+ return type == nsIDNSService::RESOLVE_TYPE_DEFAULT;
+ }
+
+ // 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;
+
+ // 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.
+ nsIRequest::TRRMode mEffectiveTRRMode;
+
+ TRRSkippedReason mTRRTRRSkippedReason = TRR_UNSET;
+ TRRSkippedReason mTRRAFailReason = TRR_UNSET;
+ TRRSkippedReason mTRRAAAAFailReason = TRR_UNSET;
+
+ mozilla::DataMutex<RefPtr<mozilla::net::TRRQuery>> mTRRQuery;
+
+ mozilla::Atomic<int32_t>
+ mResolving; // counter of outstanding resolving calls
+
+ uint8_t negative : 1; /* 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. */
+ uint8_t mDoomed : 1; // explicitly expired
+};
+
+// b020e996-f6ab-45e5-9bf5-1da71dd0053a
+#define ADDRHOSTRECORD_IID \
+ { \
+ 0xb020e996, 0xf6ab, 0x45e5, { \
+ 0x9b, 0xf5, 0x1d, 0xa7, 0x1d, 0xd0, 0x05, 0x3a \
+ } \
+ }
+
+class AddrHostRecord final : public nsHostRecord {
+ typedef mozilla::Mutex Mutex;
+
+ 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;
+ int addr_info_gencnt; /* generation count of |addr_info| */
+ 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; }
+
+ private:
+ friend class nsHostResolver;
+ 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 override;
+
+ bool RemoveOrRefresh(bool aTrrToo); // Mark records currently being resolved
+ // as needed to resolve again.
+
+ void ResolveComplete();
+
+ enum DnsPriority {
+ DNS_PRIORITY_LOW,
+ DNS_PRIORITY_MEDIUM,
+ DNS_PRIORITY_HIGH,
+ };
+ static DnsPriority GetPriority(uint16_t aFlags);
+
+ // true if pending and on the queue (not yet given to getaddrinfo())
+ bool onQueue() { return LoadNative() && isInList(); }
+
+ // When the lookups of this record started and their durations
+ mozilla::TimeStamp mTrrStart;
+ mozilla::TimeStamp mNativeStart;
+ mozilla::TimeDuration mTrrDuration;
+ mozilla::TimeDuration mNativeDuration;
+
+ mozilla::Atomic<bool> mTRRUsed; // TRR was used on this record
+ uint8_t mTRRSuccess; // number of successful TRR responses
+ uint8_t mNativeSuccess; // number of native lookup responses
+
+ // 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
+
+ // 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::TRRQuery;
+
+ explicit TypeHostRecord(const nsHostKey& key);
+ ~TypeHostRecord();
+
+ // Checks if the record is usable (not expired and has a value)
+ bool HasUsableResultInternal() const override;
+
+ bool HasUsableResult();
+
+ mozilla::net::TypeRecordResultType mResults = AsVariant(mozilla::Nothing());
+ mozilla::Mutex mResultsLock;
+
+ // When the lookups of this record started (for telemetry).
+ mozilla::TimeStamp mStart;
+ bool mAllRecordsExcluded = false;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(TypeHostRecord, TYPEHOSTRECORD_IID)
+
+/**
+ * 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;
+};
+
+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,
+ nsHostRecord::TRRSkippedReason aReason) = 0;
+ virtual LookupStatus CompleteLookupByType(
+ nsHostRecord*, nsresult, mozilla::net::TypeRecordResultType& aResult,
+ uint32_t aTtl, bool pb) = 0;
+ virtual nsresult GetHostRecord(const nsACString& host,
+ const nsACString& aTrrServer, uint16_t type,
+ uint16_t 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,
+ uint16_t type,
+ const mozilla::OriginAttributes& aOriginAttributes,
+ uint16_t flags, uint16_t af,
+ nsResolveHostCallback* callback);
+
+ nsHostRecord* InitRecord(const nsHostKey& key);
+ mozilla::net::NetworkConnectivityService* GetNCS() { return mNCS; }
+
+ /**
+ * 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,
+ uint16_t 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,
+ uint16_t 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 = nsIDNSService::RESOLVE_PRIORITY_MEDIUM,
+ RES_PRIORITY_LOW = nsIDNSService::RESOLVE_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,
+ nsHostRecord::TRRSkippedReason aReason) override;
+ LookupStatus CompleteLookupByType(nsHostRecord*, nsresult,
+ mozilla::net::TypeRecordResultType& aResult,
+ uint32_t aTtl, bool pb) override;
+ nsresult GetHostRecord(const nsACString& host, const nsACString& trrServer,
+ uint16_t type, uint16_t flags, uint16_t af, bool pb,
+ const nsCString& originSuffix,
+ nsHostRecord** result) override;
+ nsresult TrrLookup_unlocked(nsHostRecord*,
+ mozilla::net::TRR* pushedTRR = nullptr) override;
+ static mozilla::net::ResolverMode Mode();
+
+ virtual void MaybeRenewHostRecord(nsHostRecord* aRec) override;
+
+ private:
+ explicit nsHostResolver(uint32_t maxCacheEntries,
+ uint32_t defaultCacheEntryLifetime,
+ uint32_t defaultGracePeriod);
+ virtual ~nsHostResolver();
+
+ nsresult Init();
+ // In debug builds it asserts that the element is in the list.
+ void AssertOnQ(nsHostRecord*, mozilla::LinkedList<RefPtr<nsHostRecord>>&);
+ static void ComputeEffectiveTRRMode(nsHostRecord* aRec);
+ nsresult NativeLookup(nsHostRecord*);
+ nsresult TrrLookup(nsHostRecord*, mozilla::net::TRR* pushedTRR = nullptr);
+
+ // Kick-off a name resolve operation, using native resolver and/or TRR
+ nsresult NameLookup(nsHostRecord*);
+ bool GetHostToLookup(AddrHostRecord** result);
+ void MaybeRenewHostRecordLocked(nsHostRecord* aRec);
+
+ // Removes the first element from the list and returns it AddRef-ed in aResult
+ // Should not be called for an empty linked list.
+ void DeQueue(mozilla::LinkedList<RefPtr<nsHostRecord>>& aQ,
+ AddrHostRecord** aResult);
+ // 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);
+
+ /**
+ * 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);
+
+ void AddToEvictionQ(nsHostRecord* rec);
+
+ void ThreadFunc();
+
+ 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;
+ uint32_t mDefaultCacheLifetime; // granularity seconds
+ uint32_t mDefaultGracePeriod; // granularity seconds
+ mutable Mutex mLock; // mutable so SizeOfIncludingThis can be const
+ CondVar mIdleTaskCV;
+ nsRefPtrHashtable<nsGenericHashKey<nsHostKey>, nsHostRecord> mRecordDB;
+ mozilla::LinkedList<RefPtr<nsHostRecord>> mHighQ;
+ mozilla::LinkedList<RefPtr<nsHostRecord>> mMediumQ;
+ mozilla::LinkedList<RefPtr<nsHostRecord>> mLowQ;
+ mozilla::LinkedList<RefPtr<nsHostRecord>> mEvictionQ;
+ uint32_t mEvictionQSize;
+ PRTime mCreationTime;
+ mozilla::TimeDuration mLongIdleTimeout;
+ mozilla::TimeDuration mShortIdleTimeout;
+
+ RefPtr<nsIThreadPool> mResolverThreads;
+ RefPtr<mozilla::net::NetworkConnectivityService> mNCS;
+
+ mozilla::Atomic<bool> mShutdown;
+ mozilla::Atomic<uint32_t> mNumIdleTasks;
+ mozilla::Atomic<uint32_t> mActiveTaskCount;
+ mozilla::Atomic<uint32_t> mActiveAnyThreadCount;
+ mozilla::Atomic<uint32_t> mPendingCount;
+
+ // 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/nsIDNSByTypeRecord.idl b/netwerk/dns/nsIDNSByTypeRecord.idl
new file mode 100644
index 0000000000..e311b34a1b
--- /dev/null
+++ b/netwerk/dns/nsIDNSByTypeRecord.idl
@@ -0,0 +1,126 @@
+/* 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 "mozilla/Tuple.h"
+#include "nsTArrayForwardDeclare.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<mozilla::Tuple<nsCString, bool>>);
+
+[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 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;
+
+ 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..4ac3156fb0
--- /dev/null
+++ b/netwerk/dns/nsIDNSRecord.idl
@@ -0,0 +1,136 @@
+/* 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"
+
+%{ 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();
+
+ /**
+ * 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 unsigned long effectiveTRRMode;
+};
diff --git a/netwerk/dns/nsIDNSResolverInfo.idl b/netwerk/dns/nsIDNSResolverInfo.idl
new file mode 100644
index 0000000000..546d2d789c
--- /dev/null
+++ b/netwerk/dns/nsIDNSResolverInfo.idl
@@ -0,0 +1,11 @@
+/* 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 nsIDNSResolverInfo : nsISupports
+{
+ readonly attribute ACString URL;
+};
diff --git a/netwerk/dns/nsIDNSService.idl b/netwerk/dns/nsIDNSService.idl
new file mode 100644
index 0000000000..c999385bf7
--- /dev/null
+++ b/netwerk/dns/nsIDNSService.idl
@@ -0,0 +1,341 @@
+/* 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"
+
+%{ C++
+#include "mozilla/BasePrincipal.h"
+%}
+
+interface nsICancelable;
+interface nsIEventTarget;
+interface nsIDNSRecord;
+interface nsIDNSListener;
+interface nsIDNSResolverInfo;
+
+%{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,
+ };
+
+ /**
+ * 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 aResolver
+ * a resolverInfo object that holds information about the resolver
+ * to be used such as TRR URL. 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 unsigned long aFlags,
+ in nsIDNSResolverInfo aResolver,
+ in nsIDNSListener aListener,
+ in nsIEventTarget aListenerTarget,
+ [optional] in jsval aOriginAttributes);
+
+ [notxpcom]
+ nsresult asyncResolveNative(in AUTF8String aHostName,
+ in nsIDNSService_ResolveType aType,
+ in unsigned long aFlags,
+ in nsIDNSResolverInfo aResolver,
+ in nsIDNSListener aListener,
+ in nsIEventTarget aListenerTarget,
+ in OriginAttributes aOriginAttributes,
+ out nsICancelable aResult);
+
+ /**
+ * Returns a new resolverInfo object containing the URL we pass to it.
+ */
+ nsIDNSResolverInfo newTRRResolverInfo(in AUTF8String aTrrURL);
+
+ /**
+ * 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 aResolver
+ * a resolverInfo object that holds information about the resolver
+ * to be used such as TRR URL. 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 unsigned long aFlags,
+ in nsIDNSResolverInfo aResolver,
+ in nsIDNSListener aListener,
+ in nsresult aReason,
+ [optional] in jsval aOriginAttributes);
+
+ [notxpcom]
+ nsresult cancelAsyncResolveNative(in AUTF8String aHostName,
+ in nsIDNSService_ResolveType aType,
+ in unsigned long aFlags,
+ in nsIDNSResolverInfo 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 unsigned long aFlags,
+ [optional] in jsval aOriginAttributes);
+
+ [notxpcom]
+ nsresult resolveNative(in AUTF8String aHostName,
+ in unsigned long 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);
+
+ /**
+ * 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 unsigned long currentTrrMode;
+
+ /**
+ * @return the hostname of the operating system.
+ */
+ readonly attribute AUTF8String myHostName;
+
+ /*************************************************************************
+ * Listed below are the various flags that may be OR'd together to form
+ * the aFlags parameter passed to asyncResolve() and resolve().
+ */
+
+ /**
+ * if set, this flag suppresses the internal DNS lookup cache.
+ */
+ const unsigned long RESOLVE_BYPASS_CACHE = (1 << 0);
+
+ /**
+ * if set, the canonical name of the specified host will be queried.
+ */
+ const unsigned long RESOLVE_CANONICAL_NAME = (1 << 1);
+
+ /**
+ * if set, the query is given lower priority. Medium takes precedence
+ * if both are used.
+ */
+ const unsigned long RESOLVE_PRIORITY_MEDIUM = (1 << 2);
+ const unsigned long RESOLVE_PRIORITY_LOW = (1 << 3);
+
+ /**
+ * if set, indicates request is speculative. Speculative requests
+ * return errors if prefetching is disabled by configuration.
+ */
+ const unsigned long RESOLVE_SPECULATE = (1 << 4);
+
+ /**
+ * If set, only IPv4 addresses will be returned from resolve/asyncResolve.
+ */
+ const unsigned long RESOLVE_DISABLE_IPV6 = (1 << 5);
+
+ /**
+ * If set, only literals and cached entries will be returned from resolve/
+ * asyncResolve.
+ */
+ const unsigned long RESOLVE_OFFLINE = (1 << 6);
+
+ /**
+ * If set, only IPv6 addresses will be returned from resolve/asyncResolve.
+ */
+ const unsigned long RESOLVE_DISABLE_IPV4 = (1 << 7);
+
+ /**
+ * If set, allow name collision results (127.0.53.53) which are normally filtered.
+ */
+ const unsigned long RESOLVE_ALLOW_NAME_COLLISION = (1 << 8);
+
+ /**
+ * If set, do not use TRR for resolving the host name.
+ */
+ const unsigned long 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.
+ */
+ const unsigned long RESOLVE_REFRESH_CACHE = (1 << 10);
+
+ /**
+ * These two bits encode the TRR mode of the request.
+ * Use the static helper methods convert between the TRR mode and flags.
+ */
+ const unsigned long RESOLVE_TRR_MODE_MASK = (1 << 11) | (1 << 12);
+ const unsigned long RESOLVE_TRR_DISABLED_MODE = (1 << 11);
+%{C++
+ static uint32_t GetFlagsFromTRRMode(nsIRequest::TRRMode aMode) {
+ return static_cast<uint32_t>(aMode) << 11;
+ }
+
+ static nsIRequest::TRRMode GetTRRModeFromFlags(uint32_t aFlags) {
+ return static_cast<nsIRequest::TRRMode>((aFlags & RESOLVE_TRR_MODE_MASK) >> 11);
+ }
+%}
+
+ /**
+ * Force resolution even when SOCKS proxy with DNS forwarding is configured.
+ * Only to be used for the proxy host resolution.
+ */
+ const unsigned long RESOLVE_IGNORE_SOCKS_DNS = 1 << 13;
+
+ /**
+ * If set, only cached IP hint addresses will be returned from
+ * resolve/asyncResolve.
+ */
+ const unsigned long RESOLVE_IP_HINT = 1 << 14;
+};
+
+%{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"
+
+%}
+
diff --git a/netwerk/dns/nsIDNService.cpp b/netwerk/dns/nsIDNService.cpp
new file mode 100644
index 0000000000..2932a28444
--- /dev/null
+++ b/netwerk/dns/nsIDNService.cpp
@@ -0,0 +1,925 @@
+/* -*- 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/Preferences.h"
+#include "nsIDNService.h"
+#include "nsReadableUtils.h"
+#include "nsCRT.h"
+#include "nsServiceManagerUtils.h"
+#include "nsUnicharUtils.h"
+#include "nsUnicodeProperties.h"
+#include "nsUnicodeScriptCodes.h"
+#include "harfbuzz/hb.h"
+#include "punycode.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/TextUtils.h"
+#include "mozilla/Utf8.h"
+
+// 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 bool kIDNA2008_TransitionalProcessing = false;
+
+#include "ICUUtils.h"
+#include "unicode/uscript.h"
+
+using namespace mozilla;
+using namespace mozilla::unicode;
+using namespace mozilla::net;
+using mozilla::Preferences;
+
+//-----------------------------------------------------------------------------
+// RFC 1034 - 3.1. Name space specifications and terminology
+static const uint32_t kMaxDNSNodeLen = 63;
+// RFC 3490 - 5. ACE prefix
+static const char kACEPrefix[] = "xn--";
+#define kACEPrefixLen 4
+
+//-----------------------------------------------------------------------------
+
+#define NS_NET_PREF_EXTRAALLOWED "network.IDN.extra_allowed_chars"
+#define NS_NET_PREF_EXTRABLOCKED "network.IDN.extra_blocked_chars"
+#define NS_NET_PREF_SHOWPUNYCODE "network.IDN_show_punycode"
+#define NS_NET_PREF_IDNWHITELIST "network.IDN.whitelist."
+#define NS_NET_PREF_IDNUSEWHITELIST "network.IDN.use_whitelist"
+#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, nsISupportsWeakReference)
+
+static const char* gCallbackPrefs[] = {
+ NS_NET_PREF_EXTRAALLOWED, NS_NET_PREF_EXTRABLOCKED,
+ NS_NET_PREF_SHOWPUNYCODE, NS_NET_PREF_IDNRESTRICTION,
+ NS_NET_PREF_IDNUSEWHITELIST, nullptr,
+};
+
+nsresult nsIDNService::Init() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MutexAutoLock lock(mLock);
+
+ nsCOMPtr<nsIPrefService> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ if (prefs) {
+ prefs->GetBranch(NS_NET_PREF_IDNWHITELIST,
+ getter_AddRefs(mIDNWhitelistPrefBranch));
+ }
+
+ Preferences::RegisterPrefixCallbacks(PrefChanged, gCallbackPrefs, this);
+ prefsChanged(nullptr);
+ InitializeBlocklist(mIDNBlocklist);
+
+ return NS_OK;
+}
+
+void nsIDNService::prefsChanged(const char* pref) {
+ MOZ_ASSERT(NS_IsMainThread());
+ mLock.AssertCurrentThreadOwns();
+
+ if (pref && nsLiteralCString(NS_NET_PREF_EXTRAALLOWED).Equals(pref)) {
+ InitializeBlocklist(mIDNBlocklist);
+ }
+ if (pref && nsLiteralCString(NS_NET_PREF_EXTRABLOCKED).Equals(pref)) {
+ InitializeBlocklist(mIDNBlocklist);
+ }
+ if (!pref || nsLiteralCString(NS_NET_PREF_SHOWPUNYCODE).Equals(pref)) {
+ bool val;
+ if (NS_SUCCEEDED(Preferences::GetBool(NS_NET_PREF_SHOWPUNYCODE, &val))) {
+ mShowPunycode = val;
+ }
+ }
+ if (!pref || nsLiteralCString(NS_NET_PREF_IDNUSEWHITELIST).Equals(pref)) {
+ bool val;
+ if (NS_SUCCEEDED(Preferences::GetBool(NS_NET_PREF_IDNUSEWHITELIST, &val))) {
+ mIDNUseWhitelist = val;
+ }
+ }
+ 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()
+ : mLock("DNService pref value lock"),
+ mShowPunycode(false),
+ mRestrictionProfile(static_cast<restrictionProfile>(0)),
+ mIDNUseWhitelist(false) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ uint32_t IDNAOptions = UIDNA_CHECK_BIDI | UIDNA_CHECK_CONTEXTJ;
+ if (!kIDNA2008_TransitionalProcessing) {
+ IDNAOptions |= UIDNA_NONTRANSITIONAL_TO_UNICODE;
+ }
+ UErrorCode errorCode = U_ZERO_ERROR;
+ mIDNA = uidna_openUTS46(IDNAOptions, &errorCode);
+}
+
+nsIDNService::~nsIDNService() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ Preferences::UnregisterPrefixCallbacks(PrefChanged, gCallbackPrefs, this);
+
+ uidna_close(mIDNA);
+}
+
+nsresult nsIDNService::IDNA2008ToUnicode(const nsACString& input,
+ nsAString& output) {
+ NS_ConvertUTF8toUTF16 inputStr(input);
+ UIDNAInfo info = UIDNA_INFO_INITIALIZER;
+ UErrorCode errorCode = U_ZERO_ERROR;
+ int32_t inLen = inputStr.Length();
+ int32_t outMaxLen = kMaxDNSNodeLen + 1;
+ UChar outputBuffer[kMaxDNSNodeLen + 1];
+
+ int32_t outLen =
+ uidna_labelToUnicode(mIDNA, (const UChar*)inputStr.get(), inLen,
+ outputBuffer, outMaxLen, &info, &errorCode);
+ if (info.errors != 0) {
+ return NS_ERROR_MALFORMED_URI;
+ }
+
+ if (U_SUCCESS(errorCode)) {
+ ICUUtils::AssignUCharArrayToString(outputBuffer, outLen, output);
+ }
+
+ nsresult rv = ICUUtils::UErrorToNsResult(errorCode);
+ if (rv == NS_ERROR_FAILURE) {
+ rv = NS_ERROR_MALFORMED_URI;
+ }
+ return rv;
+}
+
+nsresult nsIDNService::IDNA2008StringPrep(const nsAString& input,
+ nsAString& output,
+ stringPrepFlag flag) {
+ UIDNAInfo info = UIDNA_INFO_INITIALIZER;
+ UErrorCode errorCode = U_ZERO_ERROR;
+ int32_t inLen = input.Length();
+ int32_t outMaxLen = kMaxDNSNodeLen + 1;
+ UChar outputBuffer[kMaxDNSNodeLen + 1];
+
+ int32_t outLen =
+ uidna_labelToUnicode(mIDNA, (const UChar*)PromiseFlatString(input).get(),
+ inLen, outputBuffer, outMaxLen, &info, &errorCode);
+ nsresult rv = ICUUtils::UErrorToNsResult(errorCode);
+ if (rv == NS_ERROR_FAILURE) {
+ rv = NS_ERROR_MALFORMED_URI;
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // 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.errors & UIDNA_ERROR_PUNYCODE) && outLen > 0 &&
+ outputBuffer[outLen - 1] == 0xfffd) {
+ --outLen;
+ }
+ ICUUtils::AssignUCharArrayToString(outputBuffer, outLen, output);
+
+ if (flag == eStringPrepIgnoreErrors) {
+ return NS_OK;
+ }
+
+ if (info.errors != 0) {
+ if (flag == eStringPrepForDNS) {
+ output.Truncate();
+ }
+ 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) {
+ const char* data = input.BeginReading();
+ uint32_t dataLen = input.Length();
+
+ // 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"
+
+ const char* p = PL_strncasestr(data, kACEPrefix, dataLen);
+
+ *_retval = p && (p == data || *(p - 1) == '.');
+ 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;
+}
+
+namespace {
+
+class MOZ_STACK_CLASS MutexSettableAutoUnlock final {
+ Mutex* mMutex;
+
+ public:
+ MutexSettableAutoUnlock() : mMutex(nullptr) {}
+
+ void Acquire(mozilla::Mutex& aMutex) {
+ MOZ_ASSERT(!mMutex);
+ mMutex = &aMutex;
+ mMutex->Lock();
+ }
+
+ ~MutexSettableAutoUnlock() {
+ if (mMutex) {
+ mMutex->Unlock();
+ }
+ }
+};
+
+} // anonymous namespace
+
+NS_IMETHODIMP nsIDNService::ConvertToDisplayIDN(const nsACString& input,
+ bool* _isASCII,
+ nsACString& _retval) {
+ MutexSettableAutoUnlock lock;
+ if (!NS_IsMainThread()) {
+ lock.Acquire(mLock);
+ }
+
+ // 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 && !mShowPunycode) {
+ // ACEtoUTF8() can't fail, but might return the original ACE string
+ nsAutoCString temp(_retval);
+ // If the domain is in the whitelist, return the host in UTF-8.
+ // Otherwise convert from ACE to UTF8 only those labels which are
+ // considered safe for display
+ ACEtoUTF8(
+ temp, _retval,
+ isInWhitelist(temp) ? eStringPrepIgnoreErrors : 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 (mShowPunycode &&
+ 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 && !isInWhitelist(_retval)) {
+ // 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;
+}
+
+//-----------------------------------------------------------------------------
+
+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[kMaxDNSNodeLen + 1];
+ uint32_t ucs4Len = 0u;
+ nsresult rv = utf16ToUcs4(in, ucs4Buf, kMaxDNSNodeLen, &ucs4Len);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // need maximum 20 bits to encode 16 bit Unicode character
+ // (include null terminator)
+ const uint32_t kEncodedBufSize = kMaxDNSNodeLen * 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 (in.Length() > kMaxDNSNodeLen) {
+ NS_WARNING("IDN node too large");
+ return NS_ERROR_MALFORMED_URI;
+ }
+
+ if (IsAscii(in)) {
+ LossyCopyUTF16toASCII(in, out);
+ 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;
+ }
+
+ rv = punycode(strPrep, out);
+ // Check that the encoded output isn't larger than the maximum length
+ // of a DNS node per RFC 1034.
+ // This test isn't necessary in the code paths above where the input
+ // is ASCII (since the output will be the same length as the input) or
+ // where we convert to UTF-8 (since the output is only used for
+ // display in the UI and not passed to DNS and can legitimately be
+ // longer than the limit).
+ if (out.Length() > kMaxDNSNodeLen) {
+ NS_WARNING("IDN node too large");
+ return NS_ERROR_MALFORMED_URI;
+ }
+
+ return rv;
+}
+
+// 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;
+}
+
+bool nsIDNService::isInWhitelist(const nsACString& host) {
+ if (!NS_IsMainThread()) {
+ mLock.AssertCurrentThreadOwns();
+ }
+
+ if (mIDNUseWhitelist && mIDNWhitelistPrefBranch) {
+ nsAutoCString tld(host);
+ // make sure the host is ACE for lookup and check that there are no
+ // unassigned codepoints
+ if (!IsAscii(tld) && NS_FAILED(UTF8toACE(tld, tld, eStringPrepForDNS))) {
+ return false;
+ }
+
+ // truncate trailing dots first
+ tld.Trim(".");
+ int32_t pos = tld.RFind(".");
+ if (pos == kNotFound) {
+ return false;
+ }
+
+ tld.Cut(0, pos + 1);
+
+ bool safe;
+ if (NS_SUCCEEDED(mIDNWhitelistPrefBranch->GetBoolPref(tld.get(), &safe))) {
+ return safe;
+ }
+ }
+
+ return false;
+}
+
+bool nsIDNService::isLabelSafe(const nsAString& label) {
+ if (!NS_IsMainThread()) {
+ mLock.AssertCurrentThreadOwns();
+ }
+
+ 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
+
+ int32_t savedScript = -1;
+
+ 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 = GetScriptCode(ch);
+ if (script != Script::COMMON && script != Script::INHERITED &&
+ script != lastScript) {
+ if (illegalScriptCombo(script, savedScript)) {
+ return false;
+ }
+ }
+
+ // Check for mixed numbering systems
+ auto genCat = GetGeneralCategory(ch);
+ if (genCat == HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) {
+ uint32_t zeroCharacter = ch - 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) {
+ const size_t kMaxScripts = 32; // more than ample for current values
+ // of ScriptExtensions property
+ UScriptCode scripts[kMaxScripts];
+ UErrorCode errorCode = U_ZERO_ERROR;
+ int nScripts =
+ uscript_getScriptExtensions(ch, scripts, kMaxScripts, &errorCode);
+ MOZ_ASSERT(U_SUCCESS(errorCode), "uscript_getScriptExtensions failed");
+ if (U_FAILURE(errorCode)) {
+ return false;
+ }
+ // nScripts will always be >= 1, because even for undefined characters
+ // uscript_getScriptExtensions 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 const Script scriptTable[] = {
+ Script::BOPOMOFO, Script::CYRILLIC, Script::GREEK, Script::HANGUL,
+ Script::HAN, Script::HIRAGANA, Script::KATAKANA, Script::LATIN};
+
+#define BOPO 0
+#define CYRL 1
+#define GREK 2
+#define HANG 3
+#define HANI 4
+#define HIRA 5
+#define KATA 6
+#define LATN 7
+#define OTHR 8
+#define JPAN 9 // Latin + Han + Hiragana + Katakana
+#define CHNA 10 // Latin + Han + Bopomofo
+#define KORE 11 // Latin + Han + Hangul
+#define HNLT 12 // Latin + Han (could be any of the above combinations)
+#define FAIL 13
+
+static inline int32_t findScriptIndex(Script aScript) {
+ int32_t tableLength = mozilla::ArrayLength(scriptTable);
+ for (int32_t index = 0; index < tableLength; ++index) {
+ if (aScript == scriptTable[index]) {
+ return index;
+ }
+ }
+ return OTHR;
+}
+
+static const int32_t 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, int32_t& savedScript) {
+ if (!NS_IsMainThread()) {
+ mLock.AssertCurrentThreadOwns();
+ }
+
+ if (savedScript == -1) {
+ 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);
+}
+
+#undef BOPO
+#undef CYRL
+#undef GREK
+#undef HANG
+#undef HANI
+#undef HIRA
+#undef KATA
+#undef LATN
+#undef OTHR
+#undef JPAN
+#undef CHNA
+#undef KORE
+#undef HNLT
+#undef FAIL
diff --git a/netwerk/dns/nsIDNService.h b/netwerk/dns/nsIDNService.h
new file mode 100644
index 0000000000..6fef8e7670
--- /dev/null
+++ b/netwerk/dns/nsIDNService.h
@@ -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/. */
+
+#ifndef nsIDNService_h__
+#define nsIDNService_h__
+
+#include "nsIIDNService.h"
+#include "nsCOMPtr.h"
+#include "nsUnicodeScriptCodes.h"
+#include "nsWeakReference.h"
+
+#include "unicode/uidna.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/net/IDNBlocklistUtils.h"
+
+#include "nsString.h"
+
+class nsIPrefBranch;
+
+//-----------------------------------------------------------------------------
+// nsIDNService
+//-----------------------------------------------------------------------------
+
+class nsIDNService final : public nsIIDNService,
+ public nsSupportsWeakReference {
+ 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);
+
+ bool isInWhitelist(const nsACString& host);
+ void prefsChanged(const char* pref);
+
+ static void PrefChanged(const char* aPref, void* aSelf) {
+ auto* self = static_cast<nsIDNService*>(aSelf);
+ mozilla::MutexAutoLock lock(self->mLock);
+ 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);
+
+ /**
+ * 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::unicode::Script script,
+ int32_t& savedScript);
+
+ /**
+ * 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);
+
+ UIDNA* mIDNA;
+
+ // We use this mutex to guard access to:
+ // |mIDNBlocklist|, |mShowPunycode|, |mRestrictionProfile|,
+ // |mIDNUseWhitelist|.
+ //
+ // These members can only be updated on the main thread and
+ // read on any thread. Therefore, acquiring the mutex is required
+ // only for threads other than the main thread.
+ mozilla::Mutex mLock;
+
+ // guarded by mLock
+ nsTArray<mozilla::net::BlocklistRange> mIDNBlocklist;
+
+ /**
+ * Flag set by the pref network.IDN_show_punycode. When it is true,
+ * IDNs containing non-ASCII characters are always displayed to the
+ * user in punycode
+ *
+ * guarded by mLock
+ */
+ bool mShowPunycode;
+
+ /**
+ * 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;
+ // guarded by mLock;
+ nsCOMPtr<nsIPrefBranch> mIDNWhitelistPrefBranch;
+ // guarded by mLock
+ bool mIDNUseWhitelist;
+};
+
+#endif // nsIDNService_h__
diff --git a/netwerk/dns/nsIEffectiveTLDService.idl b/netwerk/dns/nsIEffectiveTLDService.idl
new file mode 100644
index 0000000000..32addd31fe
--- /dev/null
+++ b/netwerk/dns/nsIEffectiveTLDService.idl
@@ -0,0 +1,158 @@
+/* -*- 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);
+
+ /**
+ * 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..874328fabc
--- /dev/null
+++ b/netwerk/dns/nsINativeDNSResolverOverride.idl
@@ -0,0 +1,29 @@
+/* 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);
+
+ /**
+ * 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/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..53d0bf526d
--- /dev/null
+++ b/netwerk/dns/prepare_tlds.py
@@ -0,0 +1,152 @@
+# 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 imp
+import os
+import re
+import sys
+from make_dafsa import words_to_cxx, words_to_bin
+
+"""
+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")
+ entries = []
+ 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..6e0a9400cd
--- /dev/null
+++ b/netwerk/dns/tests/unit/test_PublicSuffixList.js
@@ -0,0 +1,176 @@
+/* Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { PublicSuffixList } = ChromeUtils.import(
+ "resource://gre/modules/netwerk-dns/PublicSuffixList.jsm"
+);
+const { TestUtils } = ChromeUtils.import(
+ "resource://testing-common/TestUtils.jsm"
+);
+
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+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");
+});
+
+/**
+ * downloadCalled is used by mockDownload() and resetMockDownload()
+ * to keep track weather CLIENT.attachments.download is called or not
+ * downloadBackup will help restore CLIENT.attachments.download to original definition
+ * notifyUpdateBackup will help restore PublicSuffixList.notifyUpdate to original definition
+ */
+let downloadCalled = false;
+const downloadBackup = CLIENT.attachments.download;
+
+// returns a fake fileURI and sends a signal with filePath and no nsifile
+const mockDownload = () => {
+ downloadCalled = false;
+ CLIENT.attachments.download = async rec => {
+ downloadCalled = true;
+ return `file://${mockedFilePath}`; // Create a fake file URI
+ };
+};
+
+// resetMockDownload() must be run at the end of the test that uses mockDownload()
+const resetMockDownload = () => {
+ CLIENT.attachments.download = downloadBackup;
+};
+
+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: {},
+ });
+
+ mockDownload();
+
+ 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
+ resetMockDownload();
+});
+
+add_task(async () => {
+ info("File path sent when record updated.");
+
+ mockDownload();
+
+ 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."
+ );
+ resetMockDownload();
+});
+
+add_task(async () => {
+ info("Attachment downloaded when record created.");
+
+ mockDownload();
+
+ await PublicSuffixList.init();
+ await CLIENT.emit("sync", { data: PAYLOAD_CREATED_RECORDS });
+
+ Assert.equal(
+ downloadCalled,
+ true,
+ "Attachment downloaded when record created."
+ );
+ resetMockDownload();
+});
+
+add_task(async () => {
+ info("Attachment downloaded when record updated.");
+
+ mockDownload();
+
+ await PublicSuffixList.init();
+ await CLIENT.emit("sync", { data: PAYLOAD_UPDATED_RECORDS });
+
+ Assert.equal(
+ downloadCalled,
+ true,
+ "Attachment downloaded when record updated."
+ );
+ resetMockDownload();
+});
+
+add_task(async () => {
+ info("No download when more than one record is changed.");
+
+ mockDownload();
+
+ await PublicSuffixList.init();
+ await CLIENT.emit("sync", { data: PAYLOAD_UPDATED_AND_CREATED_RECORDS });
+
+ Assert.equal(
+ downloadCalled,
+ false,
+ "No download when more than one record is changed."
+ );
+ resetMockDownload();
+});
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..2758d2627f
--- /dev/null
+++ b/netwerk/dns/tests/unit/test_nsEffectiveTLDService_Reload_DAFSA.js
@@ -0,0 +1,34 @@
+/* Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+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..3d63fc29b4
--- /dev/null
+++ b/netwerk/dns/tests/unit/test_nsEffectiveTLDService_getKnownPublicSuffix.js
@@ -0,0 +1,46 @@
+/* Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests getPublicSuffix with the validate argument.
+ */
+
+"use strict";
+
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+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/xpcshell.ini b/netwerk/dns/tests/unit/xpcshell.ini
new file mode 100644
index 0000000000..1b72bc700a
--- /dev/null
+++ b/netwerk/dns/tests/unit/xpcshell.ini
@@ -0,0 +1,12 @@
+[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_getKnownPublicSuffix.js]
+[test_nsEffectiveTLDService_Reload_DAFSA.js]