diff options
Diffstat (limited to '')
89 files changed, 36421 insertions, 0 deletions
diff --git a/netwerk/dns/ChildDNSService.cpp b/netwerk/dns/ChildDNSService.cpp new file mode 100644 index 0000000000..653608ab01 --- /dev/null +++ b/netwerk/dns/ChildDNSService.cpp @@ -0,0 +1,514 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/net/ChildDNSService.h" +#include "nsDNSPrefetch.h" +#include "nsIDNSListener.h" +#include "nsIOService.h" +#include "nsThreadUtils.h" +#include "nsIXPConnect.h" +#include "nsIProtocolProxyService.h" +#include "nsNetCID.h" +#include "nsQueryObject.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/StaticPrefs_network.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/SyncRunnable.h" +#include "mozilla/net/NeckoChild.h" +#include "mozilla/net/DNSListenerProxy.h" +#include "mozilla/net/TRRServiceParent.h" +#include "nsHostResolver.h" +#include "nsServiceManagerUtils.h" +#include "prsystem.h" +#include "DNSAdditionalInfo.h" +#include "TRRService.h" + +namespace mozilla { +namespace net { + +//----------------------------------------------------------------------------- +// ChildDNSService +//----------------------------------------------------------------------------- + +static StaticRefPtr<ChildDNSService> gChildDNSService; + +already_AddRefed<ChildDNSService> ChildDNSService::GetSingleton() { + MOZ_ASSERT_IF(nsIOService::UseSocketProcess(), + XRE_IsContentProcess() || XRE_IsParentProcess()); + MOZ_ASSERT_IF(!nsIOService::UseSocketProcess(), + XRE_IsContentProcess() || XRE_IsSocketProcess()); + + if (!gChildDNSService) { + if (NS_WARN_IF(!NS_IsMainThread())) { + return nullptr; + } + gChildDNSService = new ChildDNSService(); + gChildDNSService->Init(); + ClearOnShutdown(&gChildDNSService); + } + + return do_AddRef(gChildDNSService); +} + +NS_IMPL_ISUPPORTS_INHERITED(ChildDNSService, DNSServiceBase, nsIDNSService, + nsPIDNSService) + +ChildDNSService::ChildDNSService() { + MOZ_ASSERT_IF(nsIOService::UseSocketProcess(), + XRE_IsContentProcess() || XRE_IsParentProcess()); + MOZ_ASSERT_IF(!nsIOService::UseSocketProcess(), + XRE_IsContentProcess() || XRE_IsSocketProcess()); + if (XRE_IsParentProcess() && nsIOService::UseSocketProcess()) { + nsDNSPrefetch::Initialize(this); + mTRRServiceParent = new TRRServiceParent(); + mTRRServiceParent->Init(); + } +} + +void ChildDNSService::GetDNSRecordHashKey( + const nsACString& aHost, const nsACString& aTrrServer, int32_t aPort, + uint16_t aType, const OriginAttributes& aOriginAttributes, + nsIDNSService::DNSFlags aFlags, uintptr_t aListenerAddr, + nsACString& aHashKey) { + aHashKey.Assign(aHost); + aHashKey.Assign(aTrrServer); + aHashKey.AppendInt(aPort); + aHashKey.AppendInt(aType); + + nsAutoCString originSuffix; + aOriginAttributes.CreateSuffix(originSuffix); + aHashKey.Append(originSuffix); + + aHashKey.AppendInt(aFlags); + aHashKey.AppendPrintf("0x%" PRIxPTR, aListenerAddr); +} + +nsresult ChildDNSService::AsyncResolveInternal( + const nsACString& hostname, uint16_t type, nsIDNSService::DNSFlags flags, + nsIDNSAdditionalInfo* aInfo, nsIDNSListener* listener, + nsIEventTarget* target_, const OriginAttributes& aOriginAttributes, + nsICancelable** result) { + if (XRE_IsContentProcess()) { + NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE); + } + + if (DNSForbiddenByActiveProxy(hostname, flags)) { + // nsHostResolver returns NS_ERROR_UNKNOWN_HOST for lots of reasons. + // We use a different error code to differentiate this failure and to make + // it clear(er) where this error comes from. + return NS_ERROR_UNKNOWN_PROXY_HOST; + } + + bool resolveDNSInSocketProcess = false; + if (XRE_IsParentProcess() && nsIOService::UseSocketProcess()) { + resolveDNSInSocketProcess = true; + if (type != nsIDNSService::RESOLVE_TYPE_DEFAULT && + (mTRRServiceParent->Mode() != nsIDNSService::MODE_TRRFIRST && + mTRRServiceParent->Mode() != nsIDNSService::MODE_TRRONLY) && + !StaticPrefs::network_dns_native_https_query()) { + return NS_ERROR_UNKNOWN_HOST; + } + } + + if (mDisablePrefetch && (flags & RESOLVE_SPECULATE)) { + return NS_ERROR_DNS_LOOKUP_QUEUE_FULL; + } + + // We need original listener for the pending requests hash. + uintptr_t originalListenerAddr = reinterpret_cast<uintptr_t>(listener); + + // make sure JS callers get notification on the main thread + nsCOMPtr<nsIEventTarget> target = target_; + nsCOMPtr<nsIXPConnectWrappedJS> wrappedListener = do_QueryInterface(listener); + if (wrappedListener && !target) { + target = GetMainThreadSerialEventTarget(); + } + if (target) { + // Guarantee listener freed on main thread. Not sure we need this in child + // (or in parent in nsDNSService.cpp) but doesn't hurt. + listener = new DNSListenerProxy(listener, target); + } + + RefPtr<DNSRequestSender> sender = new DNSRequestSender( + hostname, DNSAdditionalInfo::URL(aInfo), DNSAdditionalInfo::Port(aInfo), + type, aOriginAttributes, flags, listener, target); + RefPtr<DNSRequestActor> dnsReq; + if (resolveDNSInSocketProcess) { + dnsReq = new DNSRequestParent(sender); + if (!mTRRServiceParent->TRRConnectionInfoInited()) { + mTRRServiceParent->InitTRRConnectionInfo(); + } + } else { + dnsReq = new DNSRequestChild(sender); + } + + { + MutexAutoLock lock(mPendingRequestsLock); + nsCString key; + GetDNSRecordHashKey(hostname, DNSAdditionalInfo::URL(aInfo), + DNSAdditionalInfo::Port(aInfo), type, aOriginAttributes, + flags, originalListenerAddr, key); + mPendingRequests.GetOrInsertNew(key)->AppendElement(sender); + } + + sender->StartRequest(); + + sender.forget(result); + return NS_OK; +} + +nsresult ChildDNSService::CancelAsyncResolveInternal( + const nsACString& aHostname, uint16_t aType, nsIDNSService::DNSFlags aFlags, + nsIDNSAdditionalInfo* aInfo, nsIDNSListener* aListener, nsresult aReason, + const OriginAttributes& aOriginAttributes) { + if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE)) { + return NS_ERROR_DNS_LOOKUP_QUEUE_FULL; + } + + MutexAutoLock lock(mPendingRequestsLock); + nsTArray<RefPtr<DNSRequestSender>>* hashEntry; + nsCString key; + uintptr_t listenerAddr = reinterpret_cast<uintptr_t>(aListener); + GetDNSRecordHashKey(aHostname, DNSAdditionalInfo::URL(aInfo), + DNSAdditionalInfo::Port(aInfo), aType, aOriginAttributes, + aFlags, listenerAddr, key); + if (mPendingRequests.Get(key, &hashEntry)) { + // We cancel just one. + hashEntry->ElementAt(0)->Cancel(aReason); + } + + return NS_OK; +} + +//----------------------------------------------------------------------------- +// ChildDNSService::nsIDNSService +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +ChildDNSService::AsyncResolve(const nsACString& hostname, + nsIDNSService::ResolveType aType, + nsIDNSService::DNSFlags flags, + nsIDNSAdditionalInfo* aInfo, + nsIDNSListener* listener, nsIEventTarget* target_, + JS::Handle<JS::Value> aOriginAttributes, + JSContext* aCx, uint8_t aArgc, + nsICancelable** result) { + OriginAttributes attrs; + + if (aArgc == 1) { + if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) { + return NS_ERROR_INVALID_ARG; + } + } + + return AsyncResolveInternal(hostname, aType, flags, aInfo, listener, target_, + attrs, result); +} + +NS_IMETHODIMP +ChildDNSService::AsyncResolveNative( + const nsACString& hostname, nsIDNSService::ResolveType aType, + nsIDNSService::DNSFlags flags, nsIDNSAdditionalInfo* aInfo, + nsIDNSListener* listener, nsIEventTarget* target_, + const OriginAttributes& aOriginAttributes, nsICancelable** result) { + return AsyncResolveInternal(hostname, aType, flags, aInfo, listener, target_, + aOriginAttributes, result); +} + +NS_IMETHODIMP +ChildDNSService::NewAdditionalInfo(const nsACString& aTrrURL, int32_t aPort, + nsIDNSAdditionalInfo** aInfo) { + RefPtr<DNSAdditionalInfo> res = new DNSAdditionalInfo(aTrrURL, aPort); + res.forget(aInfo); + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSService::CancelAsyncResolve(const nsACString& aHostname, + nsIDNSService::ResolveType aType, + nsIDNSService::DNSFlags aFlags, + nsIDNSAdditionalInfo* aInfo, + nsIDNSListener* aListener, nsresult aReason, + JS::Handle<JS::Value> aOriginAttributes, + JSContext* aCx, uint8_t aArgc) { + OriginAttributes attrs; + + if (aArgc == 1) { + if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) { + return NS_ERROR_INVALID_ARG; + } + } + + return CancelAsyncResolveInternal(aHostname, aType, aFlags, aInfo, aListener, + aReason, attrs); +} + +NS_IMETHODIMP +ChildDNSService::CancelAsyncResolveNative( + const nsACString& aHostname, nsIDNSService::ResolveType aType, + nsIDNSService::DNSFlags aFlags, nsIDNSAdditionalInfo* aInfo, + nsIDNSListener* aListener, nsresult aReason, + const OriginAttributes& aOriginAttributes) { + return CancelAsyncResolveInternal(aHostname, aType, aFlags, aInfo, aListener, + aReason, aOriginAttributes); +} + +NS_IMETHODIMP +ChildDNSService::Resolve(const nsACString& hostname, + nsIDNSService::DNSFlags flags, + JS::Handle<JS::Value> aOriginAttributes, + JSContext* aCx, uint8_t aArgc, nsIDNSRecord** result) { + // not planning to ever support this, since sync IPDL is evil. + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +ChildDNSService::ResolveNative(const nsACString& hostname, + nsIDNSService::DNSFlags flags, + const OriginAttributes& aOriginAttributes, + nsIDNSRecord** result) { + // not planning to ever support this, since sync IPDL is evil. + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +ChildDNSService::GetDNSCacheEntries( + nsTArray<mozilla::net::DNSCacheEntries>* args) { + // Only used by networking dashboard, so may not ever need this in child. + // (and would provide a way to spy on what hosts other apps are connecting to, + // unless we start keeping per-app DNS caches). + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +ChildDNSService::ClearCache(bool aTrrToo) { + if (!mTRRServiceParent || !mTRRServiceParent->CanSend()) { + return NS_ERROR_NOT_AVAILABLE; + } + + Unused << mTRRServiceParent->SendClearDNSCache(aTrrToo); + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSService::ReloadParentalControlEnabled() { + if (!mTRRServiceParent) { + return NS_ERROR_NOT_AVAILABLE; + } + + mTRRServiceParent->UpdateParentalControlEnabled(); + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSService::SetDetectedTrrURI(const nsACString& aURI) { + if (!mTRRServiceParent) { + return NS_ERROR_NOT_AVAILABLE; + } + + mTRRServiceParent->SetDetectedTrrURI(aURI); + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSService::SetHeuristicDetectionResult(nsITRRSkipReason::value aValue) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +ChildDNSService::GetHeuristicDetectionResult(nsITRRSkipReason::value* aValue) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +ChildDNSService::GetTRRSkipReasonName(nsITRRSkipReason::value aValue, + nsACString& aName) { + return mozilla::net::GetTRRSkipReasonName(aValue, aName); +} + +NS_IMETHODIMP +ChildDNSService::GetCurrentTrrURI(nsACString& aURI) { + if (!mTRRServiceParent) { + return NS_ERROR_NOT_AVAILABLE; + } + + mTRRServiceParent->GetURI(aURI); + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSService::GetCurrentTrrMode(nsIDNSService::ResolverMode* aMode) { + if (XRE_IsContentProcess()) { + *aMode = mTRRMode; + return NS_OK; + } + if (!mTRRServiceParent) { + return NS_ERROR_NOT_AVAILABLE; + } + + *aMode = mTRRServiceParent->Mode(); + return NS_OK; +} + +void ChildDNSService::SetTRRModeInChild( + nsIDNSService::ResolverMode mode, + nsIDNSService::ResolverMode modeFromPref) { + if (!XRE_IsContentProcess()) { + MOZ_ASSERT(false, "Why are we calling this?"); + return; + } + mTRRMode = mode; + TRRService::SetCurrentTRRMode(modeFromPref); +} + +NS_IMETHODIMP +ChildDNSService::GetCurrentTrrConfirmationState(uint32_t* aConfirmationState) { + if (!mTRRServiceParent) { + return NS_ERROR_NOT_AVAILABLE; + } + + *aConfirmationState = mTRRServiceParent->GetConfirmationState(); + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSService::GetMyHostName(nsACString& result) { + if (XRE_IsParentProcess()) { + char name[100]; + if (PR_GetSystemInfo(PR_SI_HOSTNAME, name, sizeof(name)) == PR_SUCCESS) { + result = name; + return NS_OK; + } + + return NS_ERROR_FAILURE; + } + // TODO: get value from parent during PNecko construction? + return NS_ERROR_NOT_AVAILABLE; +} + +void ChildDNSService::NotifyRequestDone(DNSRequestSender* aDnsRequest) { + // We need the original flags and listener for the pending requests hash. + nsIDNSService::DNSFlags originalFlags = + aDnsRequest->mFlags & ~RESOLVE_OFFLINE; + uintptr_t originalListenerAddr = + reinterpret_cast<uintptr_t>(aDnsRequest->mListener.get()); + RefPtr<DNSListenerProxy> wrapper = do_QueryObject(aDnsRequest->mListener); + if (wrapper) { + originalListenerAddr = wrapper->GetOriginalListenerAddress(); + } + + MutexAutoLock lock(mPendingRequestsLock); + + nsCString key; + GetDNSRecordHashKey(aDnsRequest->mHost, aDnsRequest->mTrrServer, + aDnsRequest->mPort, aDnsRequest->mType, + aDnsRequest->mOriginAttributes, originalFlags, + originalListenerAddr, key); + + nsTArray<RefPtr<DNSRequestSender>>* hashEntry; + + if (mPendingRequests.Get(key, &hashEntry)) { + auto idx = hashEntry->IndexOf(aDnsRequest); + if (idx != nsTArray<RefPtr<DNSRequestSender>>::NoIndex) { + hashEntry->RemoveElementAt(idx); + if (hashEntry->IsEmpty()) { + mPendingRequests.Remove(key); + } + } + } +} + +//----------------------------------------------------------------------------- +// ChildDNSService::nsPIDNSService +//----------------------------------------------------------------------------- + +nsresult ChildDNSService::Init() { + ReadPrefs(nullptr); + + nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); + if (prefs) { + AddPrefObserver(prefs); + } + + return NS_OK; +} + +nsresult ChildDNSService::Shutdown() { return NS_OK; } + +NS_IMETHODIMP +ChildDNSService::GetPrefetchEnabled(bool* outVal) { + *outVal = !mDisablePrefetch; + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSService::SetPrefetchEnabled(bool inVal) { + mDisablePrefetch = !inVal; + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSService::ReportFailedSVCDomainName(const nsACString& aOwnerName, + const nsACString& aSVCDomainName) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +ChildDNSService::IsSVCDomainNameFailed(const nsACString& aOwnerName, + const nsACString& aSVCDomainName, + bool* aResult) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +ChildDNSService::ResetExcludedSVCDomainName(const nsACString& aOwnerName) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +//----------------------------------------------------------------------------- +// ChildDNSService::nsIObserver +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +ChildDNSService::Observe(nsISupports* subject, const char* topic, + const char16_t* data) { + if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { + // Reread prefs + ReadPrefs(NS_ConvertUTF16toUTF8(data).get()); + } + return NS_OK; +} + +void ChildDNSService::SetTRRDomain(const nsACString& aTRRDomain) { + mTRRDomain = aTRRDomain; + TRRService::SetProviderDomain(aTRRDomain); +} + +nsresult ChildDNSService::GetTRRDomainKey(nsACString& aTRRDomain) { + aTRRDomain = TRRService::ProviderKey(); + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSService::GetTrrDomain(nsACString& aTRRDomain) { + aTRRDomain = mTRRDomain; + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSService::GetLastConfirmationStatus(nsresult* aConfirmationStatus) { + // XXX(valentin): Fix for socket process + *aConfirmationStatus = NS_OK; + return NS_OK; +} + +NS_IMETHODIMP ChildDNSService::GetLastConfirmationSkipReason( + TRRSkippedReason* aSkipReason) { + // XXX(valentin): Fix for socket process + *aSkipReason = nsITRRSkipReason::TRR_UNSET; + return NS_OK; +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/ChildDNSService.h b/netwerk/dns/ChildDNSService.h new file mode 100644 index 0000000000..611f8b871f --- /dev/null +++ b/netwerk/dns/ChildDNSService.h @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_ChildDNSService_h +#define mozilla_net_ChildDNSService_h + +#include "DNSServiceBase.h" +#include "nsPIDNSService.h" +#include "mozilla/Attributes.h" +#include "mozilla/Mutex.h" +#include "DNSRequestChild.h" +#include "DNSRequestParent.h" +#include "nsHashKeys.h" +#include "nsClassHashtable.h" + +namespace mozilla { +namespace net { + +class TRRServiceParent; + +class ChildDNSService final : public DNSServiceBase, public nsPIDNSService { + public: + // AsyncResolve (and CancelAsyncResolve) can be called off-main + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSPIDNSSERVICE + NS_DECL_NSIDNSSERVICE + NS_DECL_NSIOBSERVER + + ChildDNSService(); + + static already_AddRefed<ChildDNSService> GetSingleton(); + + void NotifyRequestDone(DNSRequestSender* aDnsRequest); + + void SetTRRDomain(const nsACString& aTRRDomain); + void SetTRRModeInChild(nsIDNSService::ResolverMode mode, + nsIDNSService::ResolverMode modeFromPref); + + private: + virtual ~ChildDNSService() = default; + + void MOZ_ALWAYS_INLINE GetDNSRecordHashKey( + const nsACString& aHost, const nsACString& aTrrServer, int32_t aPort, + uint16_t aType, const OriginAttributes& aOriginAttributes, + nsIDNSService::DNSFlags aFlags, uintptr_t aListenerAddr, + nsACString& aHashKey); + nsresult AsyncResolveInternal(const nsACString& hostname, uint16_t type, + nsIDNSService::DNSFlags flags, + nsIDNSAdditionalInfo* aInfo, + nsIDNSListener* listener, + nsIEventTarget* target_, + const OriginAttributes& aOriginAttributes, + nsICancelable** result); + nsresult CancelAsyncResolveInternal( + const nsACString& aHostname, uint16_t aType, + nsIDNSService::DNSFlags aFlags, nsIDNSAdditionalInfo* aInfo, + nsIDNSListener* aListener, nsresult aReason, + const OriginAttributes& aOriginAttributes); + + // We need to remember pending dns requests to be able to cancel them. + nsClassHashtable<nsCStringHashKey, nsTArray<RefPtr<DNSRequestSender>>> + mPendingRequests; + Mutex mPendingRequestsLock MOZ_UNANNOTATED{"DNSPendingRequestsLock"}; + RefPtr<TRRServiceParent> mTRRServiceParent; + + nsCString mTRRDomain; + // Only set in the content process. + nsIDNSService::ResolverMode mTRRMode = + nsIDNSService::ResolverMode::MODE_NATIVEONLY; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_ChildDNSService_h diff --git a/netwerk/dns/DNS.cpp b/netwerk/dns/DNS.cpp new file mode 100644 index 0000000000..91730989db --- /dev/null +++ b/netwerk/dns/DNS.cpp @@ -0,0 +1,445 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=4 sw=2 sts=2 et cin: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/net/DNS.h" + +#include "mozilla/ArrayUtils.h" +#include "mozilla/Assertions.h" +#include "mozilla/mozalloc.h" +#include "mozilla/StaticPrefs_network.h" +#include "nsContentUtils.h" +#include "nsString.h" +#include <string.h> + +#ifdef XP_WIN +# include "ws2tcpip.h" +#endif + +namespace mozilla { +namespace net { + +const char* inet_ntop_internal(int af, const void* src, char* dst, + socklen_t size) { +#ifdef XP_WIN + if (af == AF_INET) { + struct sockaddr_in s; + memset(&s, 0, sizeof(s)); + s.sin_family = AF_INET; + memcpy(&s.sin_addr, src, sizeof(struct in_addr)); + int result = getnameinfo((struct sockaddr*)&s, sizeof(struct sockaddr_in), + dst, size, nullptr, 0, NI_NUMERICHOST); + if (result == 0) { + return dst; + } + } else if (af == AF_INET6) { + struct sockaddr_in6 s; + memset(&s, 0, sizeof(s)); + s.sin6_family = AF_INET6; + memcpy(&s.sin6_addr, src, sizeof(struct in_addr6)); + int result = getnameinfo((struct sockaddr*)&s, sizeof(struct sockaddr_in6), + dst, size, nullptr, 0, NI_NUMERICHOST); + if (result == 0) { + return dst; + } + } + return nullptr; +#else + return inet_ntop(af, src, dst, size); +#endif +} + +// Copies the contents of a PRNetAddr to a NetAddr. +// Does not do a ptr safety check! +void PRNetAddrToNetAddr(const PRNetAddr* prAddr, NetAddr* addr) { + if (prAddr->raw.family == PR_AF_INET) { + addr->inet.family = AF_INET; + addr->inet.port = prAddr->inet.port; + addr->inet.ip = prAddr->inet.ip; + } else if (prAddr->raw.family == PR_AF_INET6) { + addr->inet6.family = AF_INET6; + addr->inet6.port = prAddr->ipv6.port; + addr->inet6.flowinfo = prAddr->ipv6.flowinfo; + memcpy(&addr->inet6.ip, &prAddr->ipv6.ip, sizeof(addr->inet6.ip.u8)); + addr->inet6.scope_id = prAddr->ipv6.scope_id; + } +#if defined(XP_UNIX) + else if (prAddr->raw.family == PR_AF_LOCAL) { + addr->local.family = AF_LOCAL; + memcpy(addr->local.path, prAddr->local.path, sizeof(addr->local.path)); + } +#endif +} + +extern "C" { +// Rust bindings + +uint16_t moz_netaddr_get_family(const NetAddr* addr) { + return addr->raw.family; +} + +uint32_t moz_netaddr_get_network_order_ip(const NetAddr* addr) { + return addr->inet.ip; +} + +uint8_t const* moz_netaddr_get_ipv6(const NetAddr* addr) { + return addr->inet6.ip.u8; +} + +uint16_t moz_netaddr_get_network_order_port(const NetAddr* addr) { + if (addr->raw.family == PR_AF_INET) { + return addr->inet.port; + } + if (addr->raw.family == PR_AF_INET6) { + return addr->inet6.port; + } + return 0; +} + +} // extern "C" + +// Copies the contents of a NetAddr to a PRNetAddr. +// Does not do a ptr safety check! +void NetAddrToPRNetAddr(const NetAddr* addr, PRNetAddr* prAddr) { + if (addr->raw.family == AF_INET) { + prAddr->inet.family = PR_AF_INET; + prAddr->inet.port = addr->inet.port; + prAddr->inet.ip = addr->inet.ip; + } else if (addr->raw.family == AF_INET6) { + prAddr->ipv6.family = PR_AF_INET6; + prAddr->ipv6.port = addr->inet6.port; + prAddr->ipv6.flowinfo = addr->inet6.flowinfo; + memcpy(&prAddr->ipv6.ip, &addr->inet6.ip, sizeof(addr->inet6.ip.u8)); + prAddr->ipv6.scope_id = addr->inet6.scope_id; + } +#if defined(XP_UNIX) + else if (addr->raw.family == AF_LOCAL) { + prAddr->local.family = PR_AF_LOCAL; + memcpy(prAddr->local.path, addr->local.path, sizeof(addr->local.path)); + } +#elif defined(XP_WIN) + else if (addr->raw.family == AF_LOCAL) { + prAddr->local.family = PR_AF_LOCAL; + memcpy(prAddr->local.path, addr->local.path, sizeof(addr->local.path)); + } +#endif +} + +bool NetAddr::ToStringBuffer(char* buf, uint32_t bufSize) const { + const NetAddr* addr = this; + if (addr->raw.family == AF_INET) { + if (bufSize < INET_ADDRSTRLEN) { + return false; + } + struct in_addr nativeAddr = {}; + nativeAddr.s_addr = addr->inet.ip; + return !!inet_ntop_internal(AF_INET, &nativeAddr, buf, bufSize); + } + if (addr->raw.family == AF_INET6) { + if (bufSize < INET6_ADDRSTRLEN) { + return false; + } + struct in6_addr nativeAddr = {}; + memcpy(&nativeAddr.s6_addr, &addr->inet6.ip, sizeof(addr->inet6.ip.u8)); + return !!inet_ntop_internal(AF_INET6, &nativeAddr, buf, bufSize); + } +#if defined(XP_UNIX) + if (addr->raw.family == AF_LOCAL) { + if (bufSize < sizeof(addr->local.path)) { + // Many callers don't bother checking our return value, so + // null-terminate just in case. + if (bufSize > 0) { + buf[0] = '\0'; + } + return false; + } + + // Usually, the size passed to memcpy should be the size of the + // destination. Here, we know that the source is no larger than the + // destination, so using the source's size is always safe, whereas + // using the destination's size may cause us to read off the end of the + // source. + memcpy(buf, addr->local.path, sizeof(addr->local.path)); + return true; + } +#endif + return false; +} + +nsCString NetAddr::ToString() const { + nsCString out; + out.SetLength(kNetAddrMaxCStrBufSize); + if (ToStringBuffer(out.BeginWriting(), kNetAddrMaxCStrBufSize)) { + out.SetLength(strlen(out.BeginWriting())); + return out; + } + return ""_ns; +} + +bool NetAddr::IsLoopbackAddr() const { + if (IsLoopBackAddressWithoutIPv6Mapping()) { + return true; + } + const NetAddr* addr = this; + if (addr->raw.family != AF_INET6) { + return false; + } + + return IPv6ADDR_IS_V4MAPPED(&addr->inet6.ip) && + IPv6ADDR_V4MAPPED_TO_IPADDR(&addr->inet6.ip) == htonl(INADDR_LOOPBACK); +} + +bool NetAddr::IsLoopBackAddressWithoutIPv6Mapping() const { + const NetAddr* addr = this; + if (addr->raw.family == AF_INET) { + // Consider 127.0.0.1/8 as loopback + uint32_t ipv4Addr = ntohl(addr->inet.ip); + return (ipv4Addr >> 24) == 127; + } + + return addr->raw.family == AF_INET6 && IPv6ADDR_IS_LOOPBACK(&addr->inet6.ip); +} + +bool IsLoopbackHostname(const nsACString& aAsciiHost) { + // If the user has configured to proxy localhost addresses don't consider them + // to be secure + if (StaticPrefs::network_proxy_allow_hijacking_localhost() && + !StaticPrefs::network_proxy_testing_localhost_is_secure_when_hijacked()) { + return false; + } + + nsAutoCString host; + nsContentUtils::ASCIIToLower(aAsciiHost, host); + + return host.EqualsLiteral("localhost") || host.EqualsLiteral("localhost.") || + StringEndsWith(host, ".localhost"_ns) || + StringEndsWith(host, ".localhost."_ns); +} + +bool HostIsIPLiteral(const nsACString& aAsciiHost) { + NetAddr addr; + return NS_SUCCEEDED(addr.InitFromString(aAsciiHost)); +} + +bool NetAddr::IsIPAddrAny() const { + if (this->raw.family == AF_INET) { + if (this->inet.ip == htonl(INADDR_ANY)) { + return true; + } + } else if (this->raw.family == AF_INET6) { + if (IPv6ADDR_IS_UNSPECIFIED(&this->inet6.ip)) { + return true; + } + if (IPv6ADDR_IS_V4MAPPED(&this->inet6.ip) && + IPv6ADDR_V4MAPPED_TO_IPADDR(&this->inet6.ip) == htonl(INADDR_ANY)) { + return true; + } + } + return false; +} + +NetAddr::NetAddr(const PRNetAddr* prAddr) { PRNetAddrToNetAddr(prAddr, this); } + +nsresult NetAddr::InitFromString(const nsACString& aString, uint16_t aPort) { + PRNetAddr prAddr{}; + memset(&prAddr, 0, sizeof(PRNetAddr)); + if (PR_StringToNetAddr(PromiseFlatCString(aString).get(), &prAddr) != + PR_SUCCESS) { + return NS_ERROR_FAILURE; + } + + PRNetAddrToNetAddr(&prAddr, this); + + if (this->raw.family == PR_AF_INET) { + this->inet.port = PR_htons(aPort); + } else if (this->raw.family == PR_AF_INET6) { + this->inet6.port = PR_htons(aPort); + } + return NS_OK; +} + +bool NetAddr::IsIPAddrV4() const { return this->raw.family == AF_INET; } + +bool NetAddr::IsIPAddrV4Mapped() const { + if (this->raw.family == AF_INET6) { + return IPv6ADDR_IS_V4MAPPED(&this->inet6.ip); + } + return false; +} + +static bool isLocalIPv4(uint32_t networkEndianIP) { + uint32_t addr32 = ntohl(networkEndianIP); + return addr32 >> 24 == 0x0A || // 10/8 prefix (RFC 1918). + addr32 >> 20 == 0xAC1 || // 172.16/12 prefix (RFC 1918). + addr32 >> 16 == 0xC0A8 || // 192.168/16 prefix (RFC 1918). + addr32 >> 16 == 0xA9FE; // 169.254/16 prefix (Link Local). +} + +bool NetAddr::IsIPAddrLocal() const { + const NetAddr* addr = this; + + // IPv4 RFC1918 and Link Local Addresses. + if (addr->raw.family == AF_INET) { + return isLocalIPv4(addr->inet.ip); + } + // IPv6 Unique and Link Local Addresses. + // or mapped IPv4 addresses + if (addr->raw.family == AF_INET6) { + uint16_t addr16 = ntohs(addr->inet6.ip.u16[0]); + if (addr16 >> 9 == 0xfc >> 1 || // fc00::/7 Unique Local Address. + addr16 >> 6 == 0xfe80 >> 6) { // fe80::/10 Link Local Address. + return true; + } + if (IPv6ADDR_IS_V4MAPPED(&addr->inet6.ip)) { + return isLocalIPv4(IPv6ADDR_V4MAPPED_TO_IPADDR(&addr->inet6.ip)); + } + } + + // Not an IPv4/6 local address. + return false; +} + +bool NetAddr::IsIPAddrShared() const { + const NetAddr* addr = this; + + // IPv4 RFC6598. + if (addr->raw.family == AF_INET) { + uint32_t addr32 = ntohl(addr->inet.ip); + if (addr32 >> 22 == 0x644 >> 2) { // 100.64/10 prefix (RFC 6598). + return true; + } + } + + // Not an IPv4 shared address. + return false; +} + +nsresult NetAddr::GetPort(uint16_t* aResult) const { + uint16_t port; + if (this->raw.family == PR_AF_INET) { + port = this->inet.port; + } else if (this->raw.family == PR_AF_INET6) { + port = this->inet6.port; + } else { + return NS_ERROR_NOT_INITIALIZED; + } + + *aResult = ntohs(port); + return NS_OK; +} + +bool NetAddr::operator==(const NetAddr& other) const { + if (this->raw.family != other.raw.family) { + return false; + } + if (this->raw.family == AF_INET) { + return (this->inet.port == other.inet.port) && + (this->inet.ip == other.inet.ip); + } + if (this->raw.family == AF_INET6) { + return (this->inet6.port == other.inet6.port) && + (this->inet6.flowinfo == other.inet6.flowinfo) && + (memcmp(&this->inet6.ip, &other.inet6.ip, sizeof(this->inet6.ip)) == + 0) && + (this->inet6.scope_id == other.inet6.scope_id); +#if defined(XP_UNIX) + } + if (this->raw.family == AF_LOCAL) { + return strncmp(this->local.path, other.local.path, + ArrayLength(this->local.path)); +#endif + } + return false; +} + +bool NetAddr::operator<(const NetAddr& other) const { + if (this->raw.family != other.raw.family) { + return this->raw.family < other.raw.family; + } + if (this->raw.family == AF_INET) { + if (this->inet.ip == other.inet.ip) { + return this->inet.port < other.inet.port; + } + return this->inet.ip < other.inet.ip; + } + if (this->raw.family == AF_INET6) { + int cmpResult = + memcmp(&this->inet6.ip, &other.inet6.ip, sizeof(this->inet6.ip)); + if (cmpResult) { + return cmpResult < 0; + } + if (this->inet6.port != other.inet6.port) { + return this->inet6.port < other.inet6.port; + } + return this->inet6.flowinfo < other.inet6.flowinfo; + } + return false; +} + +AddrInfo::AddrInfo(const nsACString& host, const PRAddrInfo* prAddrInfo, + bool disableIPv4, bool filterNameCollision, + const nsACString& cname) + : mHostName(host), mCanonicalName(cname) { + MOZ_ASSERT(prAddrInfo, + "Cannot construct AddrInfo with a null prAddrInfo pointer!"); + const uint32_t nameCollisionAddr = htonl(0x7f003535); // 127.0.53.53 + + PRNetAddr tmpAddr; + void* iter = nullptr; + do { + iter = PR_EnumerateAddrInfo(iter, prAddrInfo, 0, &tmpAddr); + bool addIt = iter && (!disableIPv4 || tmpAddr.raw.family != PR_AF_INET) && + (!filterNameCollision || tmpAddr.raw.family != PR_AF_INET || + (tmpAddr.inet.ip != nameCollisionAddr)); + if (addIt) { + NetAddr elem(&tmpAddr); + mAddresses.AppendElement(elem); + } + } while (iter); +} + +AddrInfo::AddrInfo(const nsACString& host, const nsACString& cname, + DNSResolverType aResolverType, unsigned int aTRRType, + nsTArray<NetAddr>&& addresses) + : mHostName(host), + mCanonicalName(cname), + mResolverType(aResolverType), + mTRRType(aTRRType), + mAddresses(std::move(addresses)) {} + +AddrInfo::AddrInfo(const nsACString& host, DNSResolverType aResolverType, + unsigned int aTRRType, nsTArray<NetAddr>&& addresses, + uint32_t aTTL) + : ttl(aTTL), + mHostName(host), + mResolverType(aResolverType), + mTRRType(aTRRType), + mAddresses(std::move(addresses)) {} + +// deep copy constructor +AddrInfo::AddrInfo(const AddrInfo* src) { + mHostName = src->mHostName; + mCanonicalName = src->mCanonicalName; + ttl = src->ttl; + mResolverType = src->mResolverType; + mTRRType = src->mTRRType; + mTrrFetchDuration = src->mTrrFetchDuration; + mTrrFetchDurationNetworkOnly = src->mTrrFetchDurationNetworkOnly; + + mAddresses = src->mAddresses.Clone(); +} + +AddrInfo::~AddrInfo() = default; + +size_t AddrInfo::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const { + size_t n = mallocSizeOf(this); + n += mHostName.SizeOfExcludingThisIfUnshared(mallocSizeOf); + n += mCanonicalName.SizeOfExcludingThisIfUnshared(mallocSizeOf); + n += mAddresses.ShallowSizeOfExcludingThis(mallocSizeOf); + return n; +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/DNS.h b/netwerk/dns/DNS.h new file mode 100644 index 0000000000..a0dbbaa17d --- /dev/null +++ b/netwerk/dns/DNS.h @@ -0,0 +1,260 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef DNS_h_ +#define DNS_h_ + +#include "nscore.h" +#include "nsString.h" +#include "prio.h" +#include "prnetdb.h" +#include "nsISupportsImpl.h" +#include "mozilla/MemoryReporting.h" +#include "nsTArray.h" + +#if !defined(XP_WIN) +# include <arpa/inet.h> +#endif + +#ifdef XP_WIN +# include "winsock2.h" +#endif + +#ifndef AF_LOCAL +# define AF_LOCAL 1 // used for named pipe +#endif + +#define IPv6ADDR_IS_LOOPBACK(a) \ + (((a)->u32[0] == 0) && ((a)->u32[1] == 0) && ((a)->u32[2] == 0) && \ + ((a)->u8[12] == 0) && ((a)->u8[13] == 0) && ((a)->u8[14] == 0) && \ + ((a)->u8[15] == 0x1U)) + +#define IPv6ADDR_IS_V4MAPPED(a) \ + (((a)->u32[0] == 0) && ((a)->u32[1] == 0) && ((a)->u8[8] == 0) && \ + ((a)->u8[9] == 0) && ((a)->u8[10] == 0xff) && ((a)->u8[11] == 0xff)) + +#define IPv6ADDR_V4MAPPED_TO_IPADDR(a) ((a)->u32[3]) + +#define IPv6ADDR_IS_UNSPECIFIED(a) \ + (((a)->u32[0] == 0) && ((a)->u32[1] == 0) && ((a)->u32[2] == 0) && \ + ((a)->u32[3] == 0)) + +namespace mozilla { +namespace net { + +// IMPORTANT: when adding new values, always add them to the end, otherwise +// it will mess up telemetry. +// Stage_0: Receive the record before the http transaction is created. +// Stage_1: Receive the record after the http transaction is created and the +// transaction is not dispatched. +// Stage_2: Receive the record after the http transaction is dispatched. +enum HTTPSSVC_RECEIVED_STAGE : uint32_t { + HTTPSSVC_NOT_PRESENT = 0, + HTTPSSVC_WITH_IPHINT_RECEIVED_STAGE_0 = 1, + HTTPSSVC_WITHOUT_IPHINT_RECEIVED_STAGE_0 = 2, + HTTPSSVC_WITH_IPHINT_RECEIVED_STAGE_1 = 3, + HTTPSSVC_WITHOUT_IPHINT_RECEIVED_STAGE_1 = 4, + HTTPSSVC_WITH_IPHINT_RECEIVED_STAGE_2 = 5, + HTTPSSVC_WITHOUT_IPHINT_RECEIVED_STAGE_2 = 6, + HTTPSSVC_NOT_USED = 7, + HTTPSSVC_NO_USABLE_RECORD = 8, +}; + +#define HTTPS_RR_IS_USED(s) \ + (s > HTTPSSVC_NOT_PRESENT && s < HTTPSSVC_WITH_IPHINT_RECEIVED_STAGE_2) + +// Required buffer size for text form of an IP address. +// Includes space for null termination. We make our own contants +// because we don't want higher-level code depending on things +// like INET6_ADDRSTRLEN and having to include the associated +// platform-specific headers. +#ifdef XP_WIN +// Windows requires longer buffers for some reason. +const int kIPv4CStrBufSize = 22; +const int kIPv6CStrBufSize = 65; +const int kNetAddrMaxCStrBufSize = kIPv6CStrBufSize; +#else +const int kIPv4CStrBufSize = 16; +const int kIPv6CStrBufSize = 46; +const int kLocalCStrBufSize = 108; +const int kNetAddrMaxCStrBufSize = kLocalCStrBufSize; +#endif + +// This was all created at a time in which we were using NSPR for host +// resolution and we were propagating NSPR types like "PRAddrInfo" and +// "PRNetAddr" all over Gecko. This made it hard to use another host +// resolver -- we were locked into NSPR. The goal here is to get away +// from that. We'll translate what we get from NSPR or any other host +// resolution library into the types below and use them in Gecko. + +union IPv6Addr { + uint8_t u8[16]; + uint16_t u16[8]; + uint32_t u32[4]; + uint64_t u64[2]; +}; + +// This struct is similar to operating system structs like "sockaddr", used for +// things like "connect" and "getsockname". When tempted to cast or do dumb +// copies of this struct to another struct, bear compiler-computed padding +// in mind. The size of this struct, and the layout of the data in it, may +// not be what you expect. +union NetAddr { + struct { + uint16_t family; /* address family (0x00ff maskable) */ + char data[14]; /* raw address data */ + } raw{}; + struct { + uint16_t family; /* address family (AF_INET) */ + uint16_t port; /* port number */ + uint32_t ip; /* The actual 32 bits of address */ + } inet; + struct { + uint16_t family; /* address family (AF_INET6) */ + uint16_t port; /* port number */ + uint32_t flowinfo; /* routing information */ + IPv6Addr ip; /* the actual 128 bits of address */ + uint32_t scope_id; /* set of interfaces for a scope */ + } inet6; +#if defined(XP_UNIX) || defined(XP_WIN) + struct { /* Unix domain socket or + Windows Named Pipes address */ + uint16_t family; /* address family (AF_UNIX) */ + char path[104]; /* null-terminated pathname */ + } local; +#endif + // introduced to support nsTArray<NetAddr> comparisons and sorting + bool operator==(const NetAddr& other) const; + bool operator<(const NetAddr& other) const; + + inline NetAddr& operator=(const NetAddr& other) { + memcpy(this, &other, sizeof(NetAddr)); + return *this; + } + + NetAddr() { memset(this, 0, sizeof(NetAddr)); } + explicit NetAddr(const PRNetAddr* prAddr); + + // Will parse aString into a NetAddr using PR_StringToNetAddr. + // Returns an error code if parsing fails. + // If aPort is non-0 will set the NetAddr's port to (the network endian + // value of) that. + nsresult InitFromString(const nsACString& aString, uint16_t aPort = 0); + + bool IsIPAddrAny() const; + bool IsLoopbackAddr() const; + bool IsLoopBackAddressWithoutIPv6Mapping() const; + bool IsIPAddrV4() const; + bool IsIPAddrV4Mapped() const; + bool IsIPAddrLocal() const; + bool IsIPAddrShared() const; + nsresult GetPort(uint16_t* aResult) const; + bool ToStringBuffer(char* buf, uint32_t bufSize) const; + nsCString ToString() const; +}; + +enum class DNSResolverType : uint32_t { Native = 0, TRR }; + +class AddrInfo { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AddrInfo) + + public: + static const uint32_t NO_TTL_DATA = (uint32_t)-1; + + // Creates an AddrInfo object. + explicit AddrInfo(const nsACString& host, const PRAddrInfo* prAddrInfo, + bool disableIPv4, bool filterNameCollision, + const nsACString& cname); + + // Creates a basic AddrInfo object (initialize only the host, cname and TRR + // type). + explicit AddrInfo(const nsACString& host, const nsACString& cname, + DNSResolverType aResolverType, unsigned int aTRRType, + nsTArray<NetAddr>&& addresses); + + // Creates a basic AddrInfo object (initialize only the host and TRR status). + explicit AddrInfo(const nsACString& host, DNSResolverType aResolverType, + unsigned int aTRRType, nsTArray<NetAddr>&& addresses, + uint32_t aTTL = NO_TTL_DATA); + + explicit AddrInfo(const AddrInfo* src); // copy + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; + + bool IsTRR() const { return mResolverType == DNSResolverType::TRR; } + DNSResolverType ResolverType() const { return mResolverType; } + unsigned int TRRType() { return mTRRType; } + + double GetTrrFetchDuration() { return mTrrFetchDuration; } + double GetTrrFetchDurationNetworkOnly() { + return mTrrFetchDurationNetworkOnly; + } + + const nsTArray<NetAddr>& Addresses() { return mAddresses; } + const nsCString& Hostname() { return mHostName; } + const nsCString& CanonicalHostname() { return mCanonicalName; } + uint32_t TTL() { return ttl; } + + class MOZ_STACK_CLASS AddrInfoBuilder { + public: + explicit AddrInfoBuilder(AddrInfo* aInfo) { + mInfo = new AddrInfo(aInfo); // Clone it + } + + void SetTrrFetchDurationNetworkOnly(double aTime) { + mInfo->mTrrFetchDurationNetworkOnly = aTime; + } + + void SetTrrFetchDuration(double aTime) { mInfo->mTrrFetchDuration = aTime; } + + void SetTTL(uint32_t aTTL) { mInfo->ttl = aTTL; } + + void SetAddresses(nsTArray<NetAddr>&& addresses) { + mInfo->mAddresses = std::move(addresses); + } + + void SetCanonicalHostname(const nsACString& aCname) { + mInfo->mCanonicalName = aCname; + } + + already_AddRefed<AddrInfo> Finish() { return mInfo.forget(); } + + private: + RefPtr<AddrInfo> mInfo; + }; + + AddrInfoBuilder Build() { return AddrInfoBuilder(this); } + + private: + ~AddrInfo(); + uint32_t ttl = NO_TTL_DATA; + + nsCString mHostName; + nsCString mCanonicalName; + DNSResolverType mResolverType = DNSResolverType::Native; + unsigned int mTRRType = 0; + double mTrrFetchDuration = 0; + double mTrrFetchDurationNetworkOnly = 0; + + nsTArray<NetAddr> mAddresses; +}; + +// Copies the contents of a PRNetAddr to a NetAddr. +// Does not do a ptr safety check! +void PRNetAddrToNetAddr(const PRNetAddr* prAddr, NetAddr* addr); + +// Copies the contents of a NetAddr to a PRNetAddr. +// Does not do a ptr safety check! +void NetAddrToPRNetAddr(const NetAddr* addr, PRNetAddr* prAddr); + +bool IsLoopbackHostname(const nsACString& aAsciiHost); + +bool HostIsIPLiteral(const nsACString& aAsciiHost); + +} // namespace net +} // namespace mozilla + +#endif // DNS_h_ diff --git a/netwerk/dns/DNSAdditionalInfo.cpp b/netwerk/dns/DNSAdditionalInfo.cpp new file mode 100644 index 0000000000..885e9e10dc --- /dev/null +++ b/netwerk/dns/DNSAdditionalInfo.cpp @@ -0,0 +1,25 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DNSAdditionalInfo.h" + +namespace mozilla { +namespace net { + +NS_IMPL_ISUPPORTS(DNSAdditionalInfo, nsIDNSAdditionalInfo) + +NS_IMETHODIMP +DNSAdditionalInfo::GetPort(int32_t* aPort) { + *aPort = mPort; + return NS_OK; +} + +NS_IMETHODIMP +DNSAdditionalInfo::GetResolverURL(nsACString& aURL) { + aURL = mURL; + return NS_OK; +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/DNSAdditionalInfo.h b/netwerk/dns/DNSAdditionalInfo.h new file mode 100644 index 0000000000..658b9623dd --- /dev/null +++ b/netwerk/dns/DNSAdditionalInfo.h @@ -0,0 +1,44 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_DNSAdditionalInfo_h__ +#define mozilla_net_DNSAdditionalInfo_h__ + +#include "nsIDNSAdditionalInfo.h" +#include "nsString.h" + +namespace mozilla { +namespace net { + +class DNSAdditionalInfo : public nsIDNSAdditionalInfo { + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIDNSADDITIONALINFO + public: + explicit DNSAdditionalInfo(const nsACString& aURL, int32_t aPort) + : mURL(aURL), mPort(aPort){}; + static nsCString URL(nsIDNSAdditionalInfo* aInfo) { + nsCString url; + if (aInfo) { + MOZ_ALWAYS_SUCCEEDS(aInfo->GetResolverURL(url)); + } + return url; + } + static int32_t Port(nsIDNSAdditionalInfo* aInfo) { + int32_t port = -1; + if (aInfo) { + MOZ_ALWAYS_SUCCEEDS(aInfo->GetPort(&port)); + } + return port; + } + + private: + virtual ~DNSAdditionalInfo() = default; + nsCString mURL; + int32_t mPort; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_DNSAdditionalInfo_h__ diff --git a/netwerk/dns/DNSByTypeRecord.h b/netwerk/dns/DNSByTypeRecord.h new file mode 100644 index 0000000000..8b80787698 --- /dev/null +++ b/netwerk/dns/DNSByTypeRecord.h @@ -0,0 +1,259 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef DNSByTypeRecord_h__ +#define DNSByTypeRecord_h__ + +#include "mozilla/net/HTTPSSVC.h" +#include "mozilla/ipc/IPDLParamTraits.h" +#include "ipc/IPCMessageUtils.h" +#include "mozilla/net/NeckoMessageUtils.h" + +namespace mozilla { +namespace net { + +// The types of nsIDNSByTypeRecord: Nothing, TXT, HTTPSSVC +using TypeRecordEmpty = Nothing; +using TypeRecordTxt = CopyableTArray<nsCString>; +using TypeRecordHTTPSSVC = CopyableTArray<SVCB>; + +// This variant reflects the multiple types of data a nsIDNSByTypeRecord +// can hold. +using TypeRecordResultType = + Variant<TypeRecordEmpty, TypeRecordTxt, TypeRecordHTTPSSVC>; + +// TypeRecordResultType is a variant, but since it doesn't have a default +// constructor it's not a type we can pass directly over IPC. +struct IPCTypeRecord { + bool operator==(const IPCTypeRecord& aOther) const { + return mData == aOther.mData; + } + explicit IPCTypeRecord() : mData(Nothing{}) {} + TypeRecordResultType mData; + uint32_t mTTL = 0; +}; + +} // namespace net +} // namespace mozilla + +namespace mozilla { +namespace ipc { + +template <> +struct IPDLParamTraits<mozilla::net::IPCTypeRecord> { + typedef mozilla::net::IPCTypeRecord paramType; + static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor, + const paramType& aParam) { + WriteIPDLParam(aWriter, aActor, aParam.mData); + WriteIPDLParam(aWriter, aActor, aParam.mTTL); + } + + static bool Read(IPC::MessageReader* aReader, IProtocol* aActor, + paramType* aResult) { + if (!ReadIPDLParam(aReader, aActor, &aResult->mData)) { + return false; + } + if (!ReadIPDLParam(aReader, aActor, &aResult->mTTL)) { + return false; + } + return true; + } +}; + +template <> +struct IPDLParamTraits<mozilla::Nothing> { + typedef mozilla::Nothing paramType; + static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor, + const paramType& aParam) { + bool isSome = false; + WriteIPDLParam(aWriter, aActor, isSome); + } + + static bool Read(IPC::MessageReader* aReader, IProtocol* aActor, + paramType* aResult) { + bool isSome; + if (!ReadIPDLParam(aReader, aActor, &isSome)) { + return false; + } + *aResult = Nothing(); + return true; + } +}; + +template <> +struct IPDLParamTraits<mozilla::net::SVCB> { + typedef mozilla::net::SVCB paramType; + static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor, + const paramType& aParam) { + WriteIPDLParam(aWriter, aActor, aParam.mSvcFieldPriority); + WriteIPDLParam(aWriter, aActor, aParam.mSvcDomainName); + WriteIPDLParam(aWriter, aActor, aParam.mEchConfig); + WriteIPDLParam(aWriter, aActor, aParam.mODoHConfig); + WriteIPDLParam(aWriter, aActor, aParam.mHasIPHints); + WriteIPDLParam(aWriter, aActor, aParam.mHasEchConfig); + WriteIPDLParam(aWriter, aActor, aParam.mSvcFieldValue); + } + + static bool Read(IPC::MessageReader* aReader, IProtocol* aActor, + paramType* aResult) { + if (!ReadIPDLParam(aReader, aActor, &aResult->mSvcFieldPriority)) { + return false; + } + if (!ReadIPDLParam(aReader, aActor, &aResult->mSvcDomainName)) { + return false; + } + if (!ReadIPDLParam(aReader, aActor, &aResult->mEchConfig)) { + return false; + } + if (!ReadIPDLParam(aReader, aActor, &aResult->mODoHConfig)) { + return false; + } + if (!ReadIPDLParam(aReader, aActor, &aResult->mHasIPHints)) { + return false; + } + if (!ReadIPDLParam(aReader, aActor, &aResult->mHasEchConfig)) { + return false; + } + if (!ReadIPDLParam(aReader, aActor, &aResult->mSvcFieldValue)) { + return false; + } + return true; + } +}; + +template <> +struct IPDLParamTraits<mozilla::net::SvcParamAlpn> { + typedef mozilla::net::SvcParamAlpn paramType; + static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor, + const paramType& aParam) { + WriteIPDLParam(aWriter, aActor, aParam.mValue); + } + + static bool Read(IPC::MessageReader* aReader, IProtocol* aActor, + paramType* aResult) { + if (!ReadIPDLParam(aReader, aActor, &aResult->mValue)) { + return false; + } + return true; + } +}; + +template <> +struct IPDLParamTraits<mozilla::net::SvcParamNoDefaultAlpn> { + typedef mozilla::net::SvcParamNoDefaultAlpn paramType; + static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor, + const paramType& aParam) {} + + static bool Read(IPC::MessageReader* aReader, IProtocol* aActor, + paramType* aResult) { + return true; + } +}; + +template <> +struct IPDLParamTraits<mozilla::net::SvcParamPort> { + typedef mozilla::net::SvcParamPort paramType; + static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor, + const paramType& aParam) { + WriteIPDLParam(aWriter, aActor, aParam.mValue); + } + + static bool Read(IPC::MessageReader* aReader, IProtocol* aActor, + paramType* aResult) { + if (!ReadIPDLParam(aReader, aActor, &aResult->mValue)) { + return false; + } + return true; + } +}; + +template <> +struct IPDLParamTraits<mozilla::net::SvcParamIpv4Hint> { + typedef mozilla::net::SvcParamIpv4Hint paramType; + static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor, + const paramType& aParam) { + WriteIPDLParam(aWriter, aActor, aParam.mValue); + } + + static bool Read(IPC::MessageReader* aReader, IProtocol* aActor, + paramType* aResult) { + if (!ReadIPDLParam(aReader, aActor, &aResult->mValue)) { + return false; + } + return true; + } +}; + +template <> +struct IPDLParamTraits<mozilla::net::SvcParamEchConfig> { + typedef mozilla::net::SvcParamEchConfig paramType; + static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor, + const paramType& aParam) { + WriteIPDLParam(aWriter, aActor, aParam.mValue); + } + + static bool Read(IPC::MessageReader* aReader, IProtocol* aActor, + paramType* aResult) { + if (!ReadIPDLParam(aReader, aActor, &aResult->mValue)) { + return false; + } + return true; + } +}; + +template <> +struct IPDLParamTraits<mozilla::net::SvcParamIpv6Hint> { + typedef mozilla::net::SvcParamIpv6Hint paramType; + static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor, + const paramType& aParam) { + WriteIPDLParam(aWriter, aActor, aParam.mValue); + } + + static bool Read(IPC::MessageReader* aReader, IProtocol* aActor, + paramType* aResult) { + if (!ReadIPDLParam(aReader, aActor, &aResult->mValue)) { + return false; + } + return true; + } +}; + +template <> +struct IPDLParamTraits<mozilla::net::SvcParamODoHConfig> { + typedef mozilla::net::SvcParamODoHConfig paramType; + static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor, + const paramType& aParam) { + WriteIPDLParam(aWriter, aActor, aParam.mValue); + } + + static bool Read(IPC::MessageReader* aReader, IProtocol* aActor, + paramType* aResult) { + if (!ReadIPDLParam(aReader, aActor, &aResult->mValue)) { + return false; + } + return true; + } +}; + +template <> +struct IPDLParamTraits<mozilla::net::SvcFieldValue> { + typedef mozilla::net::SvcFieldValue paramType; + static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor, + const paramType& aParam) { + WriteIPDLParam(aWriter, aActor, aParam.mValue); + } + + static bool Read(IPC::MessageReader* aReader, IProtocol* aActor, + paramType* aResult) { + if (!ReadIPDLParam(aReader, aActor, &aResult->mValue)) { + return false; + } + return true; + } +}; + +} // namespace ipc +} // namespace mozilla + +#endif // DNSByTypeRecord_h__ diff --git a/netwerk/dns/DNSListenerProxy.cpp b/netwerk/dns/DNSListenerProxy.cpp new file mode 100644 index 0000000000..8468edca2d --- /dev/null +++ b/netwerk/dns/DNSListenerProxy.cpp @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/net/DNSListenerProxy.h" +#include "nsICancelable.h" + +namespace mozilla { +namespace net { + +NS_IMPL_ADDREF(DNSListenerProxy) +NS_IMPL_RELEASE(DNSListenerProxy) +NS_INTERFACE_MAP_BEGIN(DNSListenerProxy) + NS_INTERFACE_MAP_ENTRY(nsIDNSListener) + NS_INTERFACE_MAP_ENTRY_CONCRETE(DNSListenerProxy) +NS_INTERFACE_MAP_END + +NS_IMETHODIMP +DNSListenerProxy::OnLookupComplete(nsICancelable* aRequest, + nsIDNSRecord* aRecord, nsresult aStatus) { + RefPtr<DNSListenerProxy> self = this; + nsCOMPtr<nsICancelable> request = aRequest; + nsCOMPtr<nsIDNSRecord> record = aRecord; + return mTargetThread->Dispatch( + NS_NewRunnableFunction("DNSListenerProxy::OnLookupComplete", + [self, request, record, aStatus]() { + Unused << self->mListener->OnLookupComplete( + request, record, aStatus); + self->mListener = nullptr; + }), + NS_DISPATCH_NORMAL); +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/DNSListenerProxy.h b/netwerk/dns/DNSListenerProxy.h new file mode 100644 index 0000000000..5baddfd8e7 --- /dev/null +++ b/netwerk/dns/DNSListenerProxy.h @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef DNSListenerProxy_h__ +#define DNSListenerProxy_h__ + +#include "nsIDNSListener.h" +#include "nsIDNSRecord.h" +#include "nsProxyRelease.h" +#include "nsThreadUtils.h" + +class nsIEventTarget; +class nsICancelable; + +namespace mozilla { +namespace net { + +#define DNS_LISTENER_PROXY_IID \ + { \ + 0x8f172ca3, 0x7a7f, 0x4941, { \ + 0xa7, 0x0b, 0xbc, 0x72, 0x80, 0x2e, 0x9d, 0x9b \ + } \ + } + +class DNSListenerProxy final : public nsIDNSListener { + public: + DNSListenerProxy(nsIDNSListener* aListener, nsIEventTarget* aTargetThread) + // We want to make sure that |aListener| is only accessed on the target + // thread. + : mListener(aListener), + mTargetThread(aTargetThread), + mListenerAddress(reinterpret_cast<uintptr_t>(aListener)) { + MOZ_ASSERT(mListener); + MOZ_ASSERT(mTargetThread); + } + + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIDNSLISTENER + NS_DECLARE_STATIC_IID_ACCESSOR(DNS_LISTENER_PROXY_IID) + + uintptr_t GetOriginalListenerAddress() const { return mListenerAddress; } + + private: + ~DNSListenerProxy() { + NS_ProxyRelease("DNSListenerProxy::mListener", mTargetThread, + mListener.forget()); + } + + nsCOMPtr<nsIDNSListener> mListener; + nsCOMPtr<nsIEventTarget> mTargetThread; + uintptr_t mListenerAddress; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(DNSListenerProxy, DNS_LISTENER_PROXY_IID) + +} // namespace net +} // namespace mozilla + +#endif // DNSListenerProxy_h__ diff --git a/netwerk/dns/DNSLogging.h b/netwerk/dns/DNSLogging.h new file mode 100644 index 0000000000..4fd63730ca --- /dev/null +++ b/netwerk/dns/DNSLogging.h @@ -0,0 +1,26 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_DNSLogging_h +#define mozilla_DNSLogging_h + +#include "mozilla/Logging.h" + +#undef LOG + +namespace mozilla { +namespace net { +extern LazyLogModule gHostResolverLog; +} // namespace net +} // namespace mozilla + +#define LOG(msg) \ + MOZ_LOG(mozilla::net::gHostResolverLog, mozilla::LogLevel::Debug, msg) +#define LOG1(msg) \ + MOZ_LOG(mozilla::net::gHostResolverLog, mozilla::LogLevel::Error, msg) +#define LOG_ENABLED() \ + MOZ_LOG_TEST(mozilla::net::gHostResolverLog, mozilla::LogLevel::Debug) + +#endif // mozilla_DNSLogging_h diff --git a/netwerk/dns/DNSPacket.cpp b/netwerk/dns/DNSPacket.cpp new file mode 100644 index 0000000000..6aef801d63 --- /dev/null +++ b/netwerk/dns/DNSPacket.cpp @@ -0,0 +1,1100 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DNSPacket.h" + +#include "DNS.h" +#include "mozilla/EndianUtils.h" +#include "mozilla/ScopeExit.h" +#include "mozilla/StaticPrefs_network.h" +// Put DNSLogging.h at the end to avoid LOG being overwritten by other headers. +#include "DNSLogging.h" + +#include "nsIInputStream.h" + +namespace mozilla { +namespace net { + +static uint16_t get16bit(const unsigned char* aData, unsigned int index) { + return ((aData[index] << 8) | aData[index + 1]); +} + +static uint32_t get32bit(const unsigned char* aData, unsigned int index) { + return (aData[index] << 24) | (aData[index + 1] << 16) | + (aData[index + 2] << 8) | aData[index + 3]; +} + +// https://datatracker.ietf.org/doc/html/rfc8914#name-defined-extended-dns-errors +// This is a list of errors for which we should not fallback to Do53. +// These are normally explicit filtering performed by the recursive resolver. +bool hardFail(uint16_t code) { + const uint16_t noFallbackErrors[] = { + 4, // Forged answer (malware filtering) + 17, // Filtered + }; + + for (const auto& err : noFallbackErrors) { + if (code == err) { + return true; + } + } + return false; +} + +nsresult DNSPacket::FillBuffer( + std::function<int(unsigned char response[MAX_SIZE])>&& aPredicate) { + int response_length = aPredicate(mResponse); + if (response_length < 0) { + LOG(("FillBuffer response len < 0")); + mBodySize = 0; + mStatus = NS_ERROR_UNEXPECTED; + return mStatus; + } + + mBodySize = response_length; + return NS_OK; +} +// static +nsresult DNSPacket::ParseSvcParam(unsigned int svcbIndex, uint16_t key, + SvcFieldValue& field, uint16_t length, + const unsigned char* aBuffer) { + switch (key) { + case SvcParamKeyMandatory: { + if (length % 2 != 0) { + // This key should encode a list of uint16_t + return NS_ERROR_UNEXPECTED; + } + while (length > 0) { + uint16_t mandatoryKey = get16bit(aBuffer, svcbIndex); + length -= 2; + svcbIndex += 2; + + if (!IsValidSvcParamKey(mandatoryKey)) { + LOG(("The mandatory field includes a key we don't support %u", + mandatoryKey)); + return NS_ERROR_UNEXPECTED; + } + } + break; + } + case SvcParamKeyAlpn: { + field.mValue = AsVariant(SvcParamAlpn()); + auto& alpnArray = field.mValue.as<SvcParamAlpn>().mValue; + while (length > 0) { + uint8_t alpnIdLength = aBuffer[svcbIndex++]; + length -= 1; + if (alpnIdLength > length) { + return NS_ERROR_UNEXPECTED; + } + + alpnArray.AppendElement( + nsCString((const char*)&aBuffer[svcbIndex], alpnIdLength)); + length -= alpnIdLength; + svcbIndex += alpnIdLength; + } + break; + } + case SvcParamKeyNoDefaultAlpn: { + if (length != 0) { + // This key should not contain a value + return NS_ERROR_UNEXPECTED; + } + field.mValue = AsVariant(SvcParamNoDefaultAlpn{}); + break; + } + case SvcParamKeyPort: { + if (length != 2) { + // This key should only encode a uint16_t + return NS_ERROR_UNEXPECTED; + } + field.mValue = + AsVariant(SvcParamPort{.mValue = get16bit(aBuffer, svcbIndex)}); + break; + } + case SvcParamKeyIpv4Hint: { + if (length % 4 != 0) { + // This key should only encode IPv4 addresses + return NS_ERROR_UNEXPECTED; + } + + field.mValue = AsVariant(SvcParamIpv4Hint()); + auto& ipv4array = field.mValue.as<SvcParamIpv4Hint>().mValue; + while (length > 0) { + NetAddr addr; + addr.inet.family = AF_INET; + addr.inet.port = 0; + addr.inet.ip = ntohl(get32bit(aBuffer, svcbIndex)); + ipv4array.AppendElement(addr); + length -= 4; + svcbIndex += 4; + } + break; + } + case SvcParamKeyEchConfig: { + field.mValue = AsVariant(SvcParamEchConfig{ + .mValue = nsCString((const char*)(&aBuffer[svcbIndex]), length)}); + break; + } + case SvcParamKeyIpv6Hint: { + if (length % 16 != 0) { + // This key should only encode IPv6 addresses + return NS_ERROR_UNEXPECTED; + } + + field.mValue = AsVariant(SvcParamIpv6Hint()); + auto& ipv6array = field.mValue.as<SvcParamIpv6Hint>().mValue; + while (length > 0) { + NetAddr addr; + addr.inet6.family = AF_INET6; + addr.inet6.port = 0; // unknown + addr.inet6.flowinfo = 0; // unknown + addr.inet6.scope_id = 0; // unknown + for (int i = 0; i < 16; i++, svcbIndex++) { + addr.inet6.ip.u8[i] = aBuffer[svcbIndex]; + } + ipv6array.AppendElement(addr); + length -= 16; + // no need to increase svcbIndex - we did it in the for above. + } + break; + } + case SvcParamKeyODoHConfig: { + field.mValue = AsVariant(SvcParamODoHConfig{ + .mValue = nsCString((const char*)(&aBuffer[svcbIndex]), length)}); + break; + } + default: { + // Unespected type. We'll just ignore it. + return NS_OK; + break; + } + } + return NS_OK; +} + +nsresult DNSPacket::PassQName(unsigned int& index, + const unsigned char* aBuffer) { + uint8_t length; + do { + if (mBodySize < (index + 1)) { + LOG(("TRR: PassQName:%d fail at index %d\n", __LINE__, index)); + return NS_ERROR_ILLEGAL_VALUE; + } + length = static_cast<uint8_t>(aBuffer[index]); + if ((length & 0xc0) == 0xc0) { + // name pointer, advance over it and be done + if (mBodySize < (index + 2)) { + return NS_ERROR_ILLEGAL_VALUE; + } + index += 2; + break; + } + if (length & 0xc0) { + LOG(("TRR: illegal label length byte (%x) at index %d\n", length, index)); + return NS_ERROR_ILLEGAL_VALUE; + } + // pass label + if (mBodySize < (index + 1 + length)) { + LOG(("TRR: PassQName:%d fail at index %d\n", __LINE__, index)); + return NS_ERROR_ILLEGAL_VALUE; + } + index += 1 + length; + } while (length); + return NS_OK; +} + +// GetQname: retrieves the qname (stores in 'aQname') and stores the index +// after qname was parsed into the 'aIndex'. +// static +nsresult DNSPacket::GetQname(nsACString& aQname, unsigned int& aIndex, + const unsigned char* aBuffer, + unsigned int aBodySize) { + uint8_t clength = 0; + unsigned int cindex = aIndex; + unsigned int loop = 128; // a valid DNS name can never loop this much + unsigned int endindex = 0; // index position after this data + do { + if (cindex >= aBodySize) { + LOG(("TRR: bad Qname packet\n")); + return NS_ERROR_ILLEGAL_VALUE; + } + clength = static_cast<uint8_t>(aBuffer[cindex]); + if ((clength & 0xc0) == 0xc0) { + // name pointer, get the new offset (14 bits) + if ((cindex + 1) >= aBodySize) { + return NS_ERROR_ILLEGAL_VALUE; + } + // extract the new index position for the next label + uint16_t newpos = (clength & 0x3f) << 8 | aBuffer[cindex + 1]; + if (!endindex) { + // only update on the first "jump" + endindex = cindex + 2; + } + cindex = newpos; + continue; + } + if (clength & 0xc0) { + // any of those bits set individually is an error + LOG(("TRR: bad Qname packet\n")); + return NS_ERROR_ILLEGAL_VALUE; + } + + cindex++; + + if (clength) { + if (!aQname.IsEmpty()) { + aQname.Append("."); + } + if ((cindex + clength) > aBodySize) { + return NS_ERROR_ILLEGAL_VALUE; + } + aQname.Append((const char*)(&aBuffer[cindex]), clength); + cindex += clength; // skip label + } + } while (clength && --loop); + + if (!loop) { + LOG(("DNSPacket::DohDecode pointer loop error\n")); + return NS_ERROR_ILLEGAL_VALUE; + } + if (!endindex) { + // there was no "jump" + endindex = cindex; + } + aIndex = endindex; + return NS_OK; +} + +nsresult DOHresp::Add(uint32_t TTL, unsigned char const* dns, + unsigned int index, uint16_t len, bool aLocalAllowed) { + NetAddr addr; + if (4 == len) { + // IPv4 + addr.inet.family = AF_INET; + addr.inet.port = 0; // unknown + addr.inet.ip = ntohl(get32bit(dns, index)); + } else if (16 == len) { + // IPv6 + addr.inet6.family = AF_INET6; + addr.inet6.port = 0; // unknown + addr.inet6.flowinfo = 0; // unknown + addr.inet6.scope_id = 0; // unknown + for (int i = 0; i < 16; i++, index++) { + addr.inet6.ip.u8[i] = dns[index]; + } + } else { + return NS_ERROR_UNEXPECTED; + } + + if (addr.IsIPAddrLocal() && !aLocalAllowed) { + return NS_ERROR_FAILURE; + } + + // While the DNS packet might return individual TTLs for each address, + // we can only return one value in the AddrInfo class so pick the + // lowest number. + if (mTtl < TTL) { + mTtl = TTL; + } + + if (LOG_ENABLED()) { + char buf[128]; + addr.ToStringBuffer(buf, sizeof(buf)); + LOG(("DOHresp:Add %s\n", buf)); + } + mAddresses.AppendElement(addr); + return NS_OK; +} + +nsresult DNSPacket::OnDataAvailable(nsIRequest* aRequest, + nsIInputStream* aInputStream, + uint64_t aOffset, const uint32_t aCount) { + if (aCount + mBodySize > MAX_SIZE) { + LOG(("DNSPacket::OnDataAvailable:%d fail\n", __LINE__)); + return NS_ERROR_FAILURE; + } + uint32_t count; + nsresult rv = + aInputStream->Read((char*)mResponse + mBodySize, aCount, &count); + if (NS_FAILED(rv)) { + return rv; + } + MOZ_ASSERT(count == aCount); + mBodySize += aCount; + return NS_OK; +} + +const uint8_t kDNS_CLASS_IN = 1; + +nsresult DNSPacket::EncodeRequest(nsCString& aBody, const nsACString& aHost, + uint16_t aType, bool aDisableECS) { + aBody.Truncate(); + // Header + aBody += '\0'; + aBody += '\0'; // 16 bit id + aBody += 0x01; // |QR| Opcode |AA|TC|RD| Set the RD bit + aBody += '\0'; // |RA| Z | RCODE | + aBody += '\0'; + aBody += 1; // QDCOUNT (number of entries in the question section) + aBody += '\0'; + aBody += '\0'; // ANCOUNT + aBody += '\0'; + aBody += '\0'; // NSCOUNT + + char additionalRecords = + (aDisableECS || StaticPrefs::network_trr_padding()) ? 1 : 0; + aBody += '\0'; // ARCOUNT + aBody += additionalRecords; // ARCOUNT low byte for EDNS(0) + + // Question + + // The input host name should be converted to a sequence of labels, where + // each label consists of a length octet followed by that number of + // octets. The domain name terminates with the zero length octet for the + // null label of the root. + // Followed by 16 bit QTYPE and 16 bit QCLASS + + int32_t index = 0; + int32_t offset = 0; + do { + bool dotFound = false; + int32_t labelLength; + index = aHost.FindChar('.', offset); + if (kNotFound != index) { + dotFound = true; + labelLength = index - offset; + } else { + labelLength = aHost.Length() - offset; + } + if (labelLength > 63) { + // too long label! + return NS_ERROR_ILLEGAL_VALUE; + } + if (labelLength > 0) { + aBody += static_cast<unsigned char>(labelLength); + nsDependentCSubstring label = Substring(aHost, offset, labelLength); + aBody.Append(label); + } + if (!dotFound) { + aBody += '\0'; // terminate with a final zero + break; + } + offset += labelLength + 1; // move over label and dot + } while (true); + + aBody += static_cast<uint8_t>(aType >> 8); // upper 8 bit TYPE + aBody += static_cast<uint8_t>(aType); + aBody += '\0'; // upper 8 bit CLASS + aBody += kDNS_CLASS_IN; // IN - "the Internet" + + if (additionalRecords) { + // EDNS(0) is RFC 6891, ECS is RFC 7871 + aBody += '\0'; // NAME | domain name | MUST be 0 (root domain) | + aBody += '\0'; + aBody += 41; // TYPE | u_int16_t | OPT (41) | + aBody += 16; // CLASS | u_int16_t | requestor's UDP payload size | + aBody += + '\0'; // advertise 4K (high-byte: 16 | low-byte: 0), ignored by DoH + aBody += '\0'; // TTL | u_int32_t | extended RCODE and flags | + aBody += '\0'; + aBody += '\0'; + aBody += '\0'; + + // calculate padding length + unsigned int paddingLen = 0; + unsigned int rdlen = 0; + bool padding = StaticPrefs::network_trr_padding(); + if (padding) { + // always add padding specified in rfc 7830 when this config is enabled + // to allow the reponse to be padded as well + + // two bytes RDLEN, 4 bytes padding header + unsigned int packetLen = aBody.Length() + 2 + 4; + if (aDisableECS) { + // 8 bytes for disabling ecs + packetLen += 8; + } + + // clamp the padding length, because the padding extension only allows up + // to 2^16 - 1 bytes padding and adding too much padding wastes resources + uint32_t padTo = std::clamp<uint32_t>( + StaticPrefs::network_trr_padding_length(), 0, 1024); + + // Calculate number of padding bytes. The second '%'-operator is necessary + // because we prefer to add 0 bytes padding rather than padTo bytes + if (padTo > 0) { + paddingLen = (padTo - (packetLen % padTo)) % padTo; + } + // padding header + padding length + rdlen += 4 + paddingLen; + } + if (aDisableECS) { + rdlen += 8; + } + + // RDLEN | u_int16_t | length of all RDATA | + aBody += (char)((rdlen >> 8) & 0xff); // upper 8 bit RDLEN + aBody += (char)(rdlen & 0xff); + + // RDATA | octet stream | {attribute,value} pairs | + // The RDATA is just the ECS option setting zero subnet prefix + + if (aDisableECS) { + aBody += '\0'; // upper 8 bit OPTION-CODE ECS + aBody += 8; // OPTION-CODE, 2 octets, for ECS is 8 + + aBody += '\0'; // upper 8 bit OPTION-LENGTH + aBody += 4; // OPTION-LENGTH, 2 octets, contains the length of the + // payload after OPTION-LENGTH + aBody += '\0'; // upper 8 bit FAMILY. IANA Address Family Numbers + // registry, not the AF_* constants! + aBody += 1; // FAMILY (Ipv4), 2 octets + + aBody += '\0'; // SOURCE PREFIX-LENGTH | SCOPE PREFIX-LENGTH | + aBody += '\0'; + + // ADDRESS, minimum number of octets == nothing because zero bits + } + + if (padding) { + aBody += '\0'; // upper 8 bit option OPTION-CODE PADDING + aBody += 12; // OPTION-CODE, 2 octets, for PADDING is 12 + + // OPTION-LENGTH, 2 octets + aBody += (char)((paddingLen >> 8) & 0xff); + aBody += (char)(paddingLen & 0xff); + for (unsigned int i = 0; i < paddingLen; i++) { + aBody += '\0'; + } + } + } + + return NS_OK; +} + +// static +nsresult DNSPacket::ParseHTTPS(uint16_t aRDLen, struct SVCB& aParsed, + unsigned int aIndex, + const unsigned char* aBuffer, + unsigned int aBodySize, + const nsACString& aOriginHost) { + int32_t lastSvcParamKey = -1; + nsresult rv = NS_OK; + unsigned int svcbIndex = aIndex; + CheckedInt<uint16_t> available = aRDLen; + + // Should have at least 2 bytes for the priority and one for the + // qname length. + if (available.value() < 3) { + return NS_ERROR_UNEXPECTED; + } + + aParsed.mSvcFieldPriority = get16bit(aBuffer, svcbIndex); + svcbIndex += 2; + + rv = GetQname(aParsed.mSvcDomainName, svcbIndex, aBuffer, aBodySize); + if (NS_FAILED(rv)) { + return rv; + } + + if (aParsed.mSvcDomainName.IsEmpty()) { + if (aParsed.mSvcFieldPriority == 0) { + // For AliasMode SVCB RRs, a TargetName of "." indicates that + // the service is not available or does not exist. + return NS_OK; + } + + // For ServiceMode SVCB RRs, if TargetName has the value ".", + // then the owner name of this record MUST be used as + // the effective TargetName. + // When the qname is port prefix name, we need to use the + // original host name as TargetName. + aParsed.mSvcDomainName = aOriginHost; + } + + available -= (svcbIndex - aIndex); + if (!available.isValid()) { + return NS_ERROR_UNEXPECTED; + } + while (available.value() >= 4) { + // Every SvcFieldValues must have at least 4 bytes for the + // SvcParamKey (2 bytes) and length of SvcParamValue (2 bytes) + // If the length ever goes above the available data, meaning if + // available ever underflows, then that is an error. + struct SvcFieldValue value; + uint16_t key = get16bit(aBuffer, svcbIndex); + svcbIndex += 2; + + // 2.2 Clients MUST consider an RR malformed if SvcParamKeys are + // not in strictly increasing numeric order. + if (key <= lastSvcParamKey) { + LOG(("SvcParamKeys not in increasing order")); + return NS_ERROR_UNEXPECTED; + } + lastSvcParamKey = key; + + uint16_t len = get16bit(aBuffer, svcbIndex); + svcbIndex += 2; + + available -= 4 + len; + if (!available.isValid()) { + return NS_ERROR_UNEXPECTED; + } + + rv = ParseSvcParam(svcbIndex, key, value, len, aBuffer); + if (NS_FAILED(rv)) { + return rv; + } + svcbIndex += len; + + // If this is an unknown key, we will simply ignore it. + // We also don't need to record SvcParamKeyMandatory + if (key == SvcParamKeyMandatory || !IsValidSvcParamKey(key)) { + continue; + } + + if (value.mValue.is<SvcParamIpv4Hint>() || + value.mValue.is<SvcParamIpv6Hint>()) { + aParsed.mHasIPHints = true; + } + if (value.mValue.is<SvcParamEchConfig>()) { + aParsed.mHasEchConfig = true; + aParsed.mEchConfig = value.mValue.as<SvcParamEchConfig>().mValue; + } + if (value.mValue.is<SvcParamODoHConfig>()) { + aParsed.mODoHConfig = value.mValue.as<SvcParamODoHConfig>().mValue; + } + aParsed.mSvcFieldValue.AppendElement(value); + } + + return NS_OK; +} + +Result<uint8_t, nsresult> DNSPacket::GetRCode() const { + if (mBodySize < 12) { + LOG(("DNSPacket::GetRCode - packet too small")); + return Err(NS_ERROR_ILLEGAL_VALUE); + } + + return mResponse[3] & 0x0F; +} + +Result<bool, nsresult> DNSPacket::RecursionAvailable() const { + if (mBodySize < 12) { + LOG(("DNSPacket::GetRCode - packet too small")); + return Err(NS_ERROR_ILLEGAL_VALUE); + } + + return mResponse[3] & 0x80; +} + +nsresult DNSPacket::DecodeInternal( + nsCString& aHost, enum TrrType aType, nsCString& aCname, bool aAllowRFC1918, + DOHresp& aResp, TypeRecordResultType& aTypeResult, + nsClassHashtable<nsCStringHashKey, DOHresp>& aAdditionalRecords, + uint32_t& aTTL, const unsigned char* aBuffer, uint32_t aLen) { + // The response has a 12 byte header followed by the question (returned) + // and then the answer. The answer section itself contains the name, type + // and class again and THEN the record data. + + // www.example.com response: + // header: + // abcd 8180 0001 0001 0000 0000 + // the question: + // 0377 7777 0765 7861 6d70 6c65 0363 6f6d 0000 0100 01 + // the answer: + // 03 7777 7707 6578 616d 706c 6503 636f 6d00 0001 0001 + // 0000 0080 0004 5db8 d822 + + unsigned int index = 12; + uint8_t length; + nsAutoCString host; + nsresult rv; + uint16_t extendedError = UINT16_MAX; + + LOG(("doh decode %s %d bytes\n", aHost.get(), aLen)); + + aCname.Truncate(); + + if (aLen < 12) { + LOG(("TRR bad incoming DOH, eject!\n")); + return NS_ERROR_ILLEGAL_VALUE; + } + + if (!mNativePacket && (aBuffer[0] || aBuffer[1])) { + LOG(("Packet ID is unexpectedly non-zero")); + return NS_ERROR_ILLEGAL_VALUE; + } + + uint8_t rcode = mResponse[3] & 0x0F; + LOG(("TRR Decode %s RCODE %d\n", PromiseFlatCString(aHost).get(), rcode)); + + uint16_t questionRecords = get16bit(aBuffer, 4); // qdcount + // iterate over the single(?) host name in question + while (questionRecords) { + do { + if (aLen < (index + 1)) { + LOG(("TRR Decode 1 index: %u size: %u", index, aLen)); + return NS_ERROR_ILLEGAL_VALUE; + } + length = static_cast<uint8_t>(aBuffer[index]); + if (length) { + if (host.Length()) { + host.Append("."); + } + if (aLen < (index + 1 + length)) { + LOG(("TRR Decode 2 index: %u size: %u len: %u", index, aLen, length)); + return NS_ERROR_ILLEGAL_VALUE; + } + host.Append(((char*)aBuffer) + index + 1, length); + } + index += 1 + length; // skip length byte + label + } while (length); + if (aLen < (index + 4)) { + LOG(("TRR Decode 3 index: %u size: %u", index, aLen)); + return NS_ERROR_ILLEGAL_VALUE; + } + index += 4; // skip question's type, class + questionRecords--; + } + + // Figure out the number of answer records from ANCOUNT + uint16_t answerRecords = get16bit(aBuffer, 6); + + LOG(("TRR Decode: %d answer records (%u bytes body) %s index=%u\n", + answerRecords, aLen, host.get(), index)); + + while (answerRecords) { + nsAutoCString qname; + rv = GetQname(qname, index, aBuffer, mBodySize); + if (NS_FAILED(rv)) { + return rv; + } + // 16 bit TYPE + if (aLen < (index + 2)) { + LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index + 2)); + return NS_ERROR_ILLEGAL_VALUE; + } + uint16_t TYPE = get16bit(aBuffer, index); + + index += 2; + + // 16 bit class + if (aLen < (index + 2)) { + LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index + 2)); + return NS_ERROR_ILLEGAL_VALUE; + } + uint16_t CLASS = get16bit(aBuffer, index); + if (kDNS_CLASS_IN != CLASS) { + LOG(("TRR bad CLASS (%u) at index %d\n", CLASS, index)); + return NS_ERROR_UNEXPECTED; + } + index += 2; + + // 32 bit TTL (seconds) + if (aLen < (index + 4)) { + LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index)); + return NS_ERROR_ILLEGAL_VALUE; + } + uint32_t TTL = get32bit(aBuffer, index); + index += 4; + + // 16 bit RDLENGTH + if (aLen < (index + 2)) { + LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index)); + return NS_ERROR_ILLEGAL_VALUE; + } + uint16_t RDLENGTH = get16bit(aBuffer, index); + index += 2; + + if (aLen < (index + RDLENGTH)) { + LOG(("TRR: Dohdecode:%d fail RDLENGTH=%d at index %d\n", __LINE__, + RDLENGTH, index)); + return NS_ERROR_ILLEGAL_VALUE; + } + + if ((TYPE != TRRTYPE_CNAME) && (TYPE != TRRTYPE_HTTPSSVC) && + (TYPE != static_cast<uint16_t>(aType))) { + // Not the same type as was asked for nor CNAME + LOG(("TRR: Dohdecode:%d asked for type %d got %d\n", __LINE__, aType, + TYPE)); + index += RDLENGTH; + answerRecords--; + continue; + } + + // We check if the qname is a case-insensitive match for the host or the + // FQDN version of the host + bool responseMatchesQuestion = + (qname.Length() == aHost.Length() || + (aHost.Length() == qname.Length() + 1 && aHost.Last() == '.')) && + StringBeginsWith(aHost, qname, nsCaseInsensitiveCStringComparator); + + if (responseMatchesQuestion) { + // RDATA + // - A (TYPE 1): 4 bytes + // - AAAA (TYPE 28): 16 bytes + // - NS (TYPE 2): N bytes + + switch (TYPE) { + case TRRTYPE_A: + if (RDLENGTH != 4) { + LOG(("TRR bad length for A (%u)\n", RDLENGTH)); + return NS_ERROR_UNEXPECTED; + } + rv = aResp.Add(TTL, aBuffer, index, RDLENGTH, aAllowRFC1918); + if (NS_FAILED(rv)) { + LOG( + ("TRR:DohDecode failed: local IP addresses or unknown IP " + "family\n")); + return rv; + } + break; + case TRRTYPE_AAAA: + if (RDLENGTH != 16) { + LOG(("TRR bad length for AAAA (%u)\n", RDLENGTH)); + return NS_ERROR_UNEXPECTED; + } + rv = aResp.Add(TTL, aBuffer, index, RDLENGTH, aAllowRFC1918); + if (NS_FAILED(rv)) { + LOG(("TRR got unique/local IPv6 address!\n")); + return rv; + } + break; + + case TRRTYPE_NS: + break; + case TRRTYPE_CNAME: + if (aCname.IsEmpty()) { + nsAutoCString qname; + unsigned int qnameindex = index; + rv = GetQname(qname, qnameindex, aBuffer, mBodySize); + if (NS_FAILED(rv)) { + return rv; + } + if (!qname.IsEmpty()) { + ToLowerCase(qname); + aCname = qname; + LOG(("DNSPacket::DohDecode CNAME host %s => %s\n", host.get(), + aCname.get())); + } else { + LOG(("DNSPacket::DohDecode empty CNAME for host %s!\n", + host.get())); + } + } else { + LOG(("DNSPacket::DohDecode CNAME - ignoring another entry\n")); + } + break; + case TRRTYPE_TXT: { + // TXT record RRDATA sections are a series of character-strings + // each character string is a length byte followed by that many data + // bytes + nsAutoCString txt; + unsigned int txtIndex = index; + uint16_t available = RDLENGTH; + + while (available > 0) { + uint8_t characterStringLen = aBuffer[txtIndex++]; + available--; + if (characterStringLen > available) { + LOG(("DNSPacket::DohDecode MALFORMED TXT RECORD\n")); + break; + } + txt.Append((const char*)(&aBuffer[txtIndex]), characterStringLen); + txtIndex += characterStringLen; + available -= characterStringLen; + } + + if (!aTypeResult.is<TypeRecordTxt>()) { + aTypeResult = AsVariant(CopyableTArray<nsCString>()); + } + + { + auto& results = aTypeResult.as<TypeRecordTxt>(); + results.AppendElement(txt); + } + if (aTTL > TTL) { + aTTL = TTL; + } + LOG(("DNSPacket::DohDecode TXT host %s => %s\n", host.get(), + txt.get())); + + break; + } + case TRRTYPE_HTTPSSVC: { + struct SVCB parsed; + + if (aType != TRRTYPE_HTTPSSVC) { + // Ignore the entry that we just parsed if we didn't ask for it. + break; + } + + rv = ParseHTTPS(RDLENGTH, parsed, index, aBuffer, mBodySize, + mOriginHost ? *mOriginHost : qname); + if (NS_FAILED(rv)) { + return rv; + } + + if (parsed.mSvcDomainName.IsEmpty() && + parsed.mSvcFieldPriority == 0) { + // For AliasMode SVCB RRs, a TargetName of "." indicates that the + // service is not available or does not exist. + continue; + } + + // Check for AliasForm + if (aCname.IsEmpty() && parsed.mSvcFieldPriority == 0) { + // Alias form SvcDomainName must not have the "." value (empty) + if (parsed.mSvcDomainName.IsEmpty()) { + return NS_ERROR_UNEXPECTED; + } + aCname = parsed.mSvcDomainName; + // If aliasForm is present, Service form must be ignored. + aTypeResult = mozilla::AsVariant(Nothing()); + ToLowerCase(aCname); + LOG(("DNSPacket::DohDecode HTTPSSVC AliasForm host %s => %s\n", + host.get(), aCname.get())); + break; + } + + aTTL = TTL; + + if (!aTypeResult.is<TypeRecordHTTPSSVC>()) { + aTypeResult = mozilla::AsVariant(CopyableTArray<SVCB>()); + } + { + auto& results = aTypeResult.as<TypeRecordHTTPSSVC>(); + results.AppendElement(parsed); + } + + break; + } + default: + // skip unknown record types + LOG(("TRR unsupported TYPE (%u) RDLENGTH %u\n", TYPE, RDLENGTH)); + break; + } + } else { + LOG(("TRR asked for %s data but got %s\n", aHost.get(), qname.get())); + } + + index += RDLENGTH; + LOG(("done with record type %u len %u index now %u of %u\n", TYPE, RDLENGTH, + index, aLen)); + answerRecords--; + } + + // NSCOUNT + uint16_t nsRecords = get16bit(aBuffer, 8); + LOG(("TRR Decode: %d ns records (%u bytes body)\n", nsRecords, aLen)); + while (nsRecords) { + rv = PassQName(index, aBuffer); + if (NS_FAILED(rv)) { + return rv; + } + + if (aLen < (index + 8)) { + return NS_ERROR_ILLEGAL_VALUE; + } + index += 2; // type + index += 2; // class + index += 4; // ttl + + // 16 bit RDLENGTH + if (aLen < (index + 2)) { + return NS_ERROR_ILLEGAL_VALUE; + } + uint16_t RDLENGTH = get16bit(aBuffer, index); + index += 2; + if (aLen < (index + RDLENGTH)) { + return NS_ERROR_ILLEGAL_VALUE; + } + index += RDLENGTH; + LOG(("done with nsRecord now %u of %u\n", index, aLen)); + nsRecords--; + } + + // additional resource records + uint16_t arRecords = get16bit(aBuffer, 10); + LOG(("TRR Decode: %d additional resource records (%u bytes body)\n", + arRecords, aLen)); + + while (arRecords) { + nsAutoCString qname; + rv = GetQname(qname, index, aBuffer, mBodySize); + if (NS_FAILED(rv)) { + LOG(("Bad qname for additional record")); + return rv; + } + + if (aLen < (index + 8)) { + return NS_ERROR_ILLEGAL_VALUE; + } + uint16_t type = get16bit(aBuffer, index); + index += 2; + // The next two bytes encode class + // (or udpPayloadSize when type is TRRTYPE_OPT) + uint16_t cls = get16bit(aBuffer, index); + index += 2; + // The next 4 bytes encode TTL + // (or extRCode + ednsVersion + flags when type is TRRTYPE_OPT) + uint32_t ttl = get32bit(aBuffer, index); + index += 4; + // cls and ttl are unused when type is TRRTYPE_OPT + + // 16 bit RDLENGTH + if (aLen < (index + 2)) { + LOG(("Record too small")); + return NS_ERROR_ILLEGAL_VALUE; + } + + uint16_t rdlength = get16bit(aBuffer, index); + index += 2; + if (aLen < (index + rdlength)) { + LOG(("rdlength too big")); + return NS_ERROR_ILLEGAL_VALUE; + } + + auto parseRecord = [&]() { + LOG(("Parsing additional record type: %u", type)); + auto* entry = aAdditionalRecords.GetOrInsertNew(qname); + + switch (type) { + case TRRTYPE_A: + if (kDNS_CLASS_IN != cls) { + LOG(("NOT IN - returning")); + return; + } + if (rdlength != 4) { + LOG(("TRR bad length for A (%u)\n", rdlength)); + return; + } + rv = entry->Add(ttl, aBuffer, index, rdlength, aAllowRFC1918); + if (NS_FAILED(rv)) { + LOG( + ("TRR:DohDecode failed: local IP addresses or unknown IP " + "family\n")); + return; + } + break; + case TRRTYPE_AAAA: + if (kDNS_CLASS_IN != cls) { + LOG(("NOT IN - returning")); + return; + } + if (rdlength != 16) { + LOG(("TRR bad length for AAAA (%u)\n", rdlength)); + return; + } + rv = entry->Add(ttl, aBuffer, index, rdlength, aAllowRFC1918); + if (NS_FAILED(rv)) { + LOG(("TRR got unique/local IPv6 address!\n")); + return; + } + break; + case TRRTYPE_OPT: { // OPT + LOG(("Parsing opt rdlen: %u", rdlength)); + unsigned int offset = 0; + while (offset + 2 <= rdlength) { + uint16_t optCode = get16bit(aBuffer, index + offset); + LOG(("optCode: %u", optCode)); + offset += 2; + if (offset + 2 > rdlength) { + break; + } + uint16_t optLen = get16bit(aBuffer, index + offset); + LOG(("optLen: %u", optLen)); + offset += 2; + if (offset + optLen > rdlength) { + LOG(("offset: %u, optLen: %u, rdlen: %u", offset, optLen, + rdlength)); + break; + } + + LOG(("OPT: code: %u len:%u", optCode, optLen)); + + if (optCode != 15) { + offset += optLen; + continue; + } + + // optCode == 15; Extended DNS error + + if (offset + 2 > rdlength || optLen < 2) { + break; + } + extendedError = get16bit(aBuffer, index + offset); + + LOG(("Extended error code: %u message: %s", extendedError, + nsAutoCString((char*)aBuffer + index + offset + 2, optLen - 2) + .get())); + offset += optLen; + } + break; + } + default: + break; + } + }; + + parseRecord(); + + index += rdlength; + LOG(("done with additional rr now %u of %u\n", index, aLen)); + arRecords--; + } + + if (index != aLen) { + LOG(("DohDecode failed to parse entire response body, %u out of %u bytes\n", + index, aLen)); + // failed to parse 100%, do not continue + return NS_ERROR_ILLEGAL_VALUE; + } + + if (aType == TRRTYPE_NS && rcode != 0) { + return NS_ERROR_UNKNOWN_HOST; + } + + if ((aType != TRRTYPE_NS) && aCname.IsEmpty() && aResp.mAddresses.IsEmpty() && + aTypeResult.is<TypeRecordEmpty>()) { + // no entries were stored! + LOG(("TRR: No entries were stored!\n")); + + if (extendedError != UINT16_MAX && + StaticPrefs::network_trr_hard_fail_on_extended_error() && + hardFail(extendedError)) { + return NS_ERROR_DEFINITIVE_UNKNOWN_HOST; + } + return NS_ERROR_UNKNOWN_HOST; + } + + // https://tools.ietf.org/html/draft-ietf-dnsop-svcb-httpssvc-03#page-14 + // If one or more SVCB records of ServiceForm SvcRecordType are returned for + // HOST, clients should select the highest-priority option with acceptable + // parameters. + if (aTypeResult.is<TypeRecordHTTPSSVC>()) { + auto& results = aTypeResult.as<TypeRecordHTTPSSVC>(); + results.Sort(); + } + + return NS_OK; +} + +// +// DohDecode() collects the TTL and the IP addresses in the response +// +nsresult DNSPacket::Decode( + nsCString& aHost, enum TrrType aType, nsCString& aCname, bool aAllowRFC1918, + DOHresp& aResp, TypeRecordResultType& aTypeResult, + nsClassHashtable<nsCStringHashKey, DOHresp>& aAdditionalRecords, + uint32_t& aTTL) { + nsresult rv = + DecodeInternal(aHost, aType, aCname, aAllowRFC1918, aResp, aTypeResult, + aAdditionalRecords, aTTL, mResponse, mBodySize); + mStatus = rv; + return rv; +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/DNSPacket.h b/netwerk/dns/DNSPacket.h new file mode 100644 index 0000000000..943bcfe246 --- /dev/null +++ b/netwerk/dns/DNSPacket.h @@ -0,0 +1,105 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_DNSPacket_h__ +#define mozilla_net_DNSPacket_h__ + +#include "mozilla/Maybe.h" +#include "mozilla/Result.h" +#include "nsClassHashtable.h" +#include "nsIDNSService.h" +#include "DNS.h" +#include "DNSByTypeRecord.h" + +#include <functional> + +namespace mozilla { +namespace net { + +class DOHresp { + public: + nsresult Add(uint32_t TTL, unsigned char const* dns, unsigned int index, + uint16_t len, bool aLocalAllowed); + nsTArray<NetAddr> mAddresses; + uint32_t mTtl = 0; +}; + +// the values map to RFC1035 type identifiers +enum TrrType { + TRRTYPE_A = 1, + TRRTYPE_NS = 2, + TRRTYPE_CNAME = 5, + TRRTYPE_AAAA = 28, + TRRTYPE_OPT = 41, + TRRTYPE_TXT = 16, + TRRTYPE_HTTPSSVC = nsIDNSService::RESOLVE_TYPE_HTTPSSVC, // 65 +}; + +class DNSPacket { + public: + // Never accept larger DOH responses than this as that would indicate + // something is wrong. Typical ones are much smaller. + static const unsigned int MAX_SIZE = 3200; + + DNSPacket() = default; + virtual ~DNSPacket() = default; + + Result<uint8_t, nsresult> GetRCode() const; + Result<bool, nsresult> RecursionAvailable() const; + + // Called in order to feed data into the buffer. + nsresult OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream, + uint64_t aOffset, const uint32_t aCount); + + // Encodes the name request into a buffer that represents a DNS packet + virtual nsresult EncodeRequest(nsCString& aBody, const nsACString& aHost, + uint16_t aType, bool aDisableECS); + + // Decodes the DNS response and extracts the responses, additional records, + // etc. XXX: This should probably be refactored to reduce the number of + // output parameters and have a common format for different record types. + virtual nsresult Decode( + nsCString& aHost, enum TrrType aType, nsCString& aCname, + bool aAllowRFC1918, DOHresp& aResp, TypeRecordResultType& aTypeResult, + nsClassHashtable<nsCStringHashKey, DOHresp>& aAdditionalRecords, + uint32_t& aTTL); + + void SetOriginHost(const Maybe<nsCString>& aHost) { mOriginHost = aHost; } + + nsresult FillBuffer(std::function<int(unsigned char response[MAX_SIZE])>&&); + + static nsresult ParseHTTPS(uint16_t aRDLen, struct SVCB& aParsed, + unsigned int aIndex, const unsigned char* aBuffer, + unsigned int aBodySize, + const nsACString& aOriginHost); + void SetNativePacket(bool aNative) { mNativePacket = aNative; } + + protected: + nsresult PassQName(unsigned int& index, const unsigned char* aBuffer); + static nsresult GetQname(nsACString& aQname, unsigned int& aIndex, + const unsigned char* aBuffer, + unsigned int aBodySize); + static nsresult ParseSvcParam(unsigned int svcbIndex, uint16_t key, + SvcFieldValue& field, uint16_t length, + const unsigned char* aBuffer); + nsresult DecodeInternal( + nsCString& aHost, enum TrrType aType, nsCString& aCname, + bool aAllowRFC1918, DOHresp& aResp, TypeRecordResultType& aTypeResult, + nsClassHashtable<nsCStringHashKey, DOHresp>& aAdditionalRecords, + uint32_t& aTTL, const unsigned char* aBuffer, uint32_t aLen); + + // The response buffer. + unsigned char mResponse[MAX_SIZE]{}; + unsigned int mBodySize = 0; + // True when decoding a DNS packet received from OS. Decoding will + // not panic if packet ID is not zero. + bool mNativePacket = false; + nsresult mStatus = NS_OK; + Maybe<nsCString> mOriginHost; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_DNSPacket_h__ diff --git a/netwerk/dns/DNSRequestBase.h b/netwerk/dns/DNSRequestBase.h new file mode 100644 index 0000000000..7b26846f5e --- /dev/null +++ b/netwerk/dns/DNSRequestBase.h @@ -0,0 +1,152 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_DNSRequestBase_h +#define mozilla_net_DNSRequestBase_h + +#include "mozilla/net/PDNSRequestParent.h" +#include "nsICancelable.h" +#include "nsIDNSRecord.h" +#include "nsIDNSListener.h" +#include "nsIDNSByTypeRecord.h" +#include "nsIEventTarget.h" + +namespace mozilla { +namespace net { + +class DNSRequestActor; +class DNSRequestChild; +class DNSRequestHandler; +class DNSRequestParent; +class DNSRequestSender; + +// A base class for DNSRequestSender and DNSRequestHandler. +// Provide interfaces for processing DNS requests. +class DNSRequestBase : public nsISupports { + public: + explicit DNSRequestBase() = default; + + void SetIPCActor(DNSRequestActor* aActor); + + virtual void OnRecvCancelDNSRequest(const nsCString& hostName, + const nsCString& trrServer, + const int32_t& port, const uint16_t& type, + const OriginAttributes& originAttributes, + const nsIDNSService::DNSFlags& flags, + const nsresult& reason) = 0; + virtual bool OnRecvLookupCompleted(const DNSRequestResponse& reply) = 0; + virtual void OnIPCActorDestroy() = 0; + + virtual DNSRequestSender* AsDNSRequestSender() = 0; + virtual DNSRequestHandler* AsDNSRequestHandler() = 0; + + protected: + virtual ~DNSRequestBase() = default; + + RefPtr<DNSRequestActor> mIPCActor; +}; + +// DNSRequestSender is used to send an IPC request to DNSRequestHandler and +// deliver the result to nsIDNSListener. +// Note this class could be used both in content process and parent process. +class DNSRequestSender final : public DNSRequestBase, public nsICancelable { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSICANCELABLE + + DNSRequestSender(const nsACString& aHost, const nsACString& aTrrServer, + int32_t aPort, const uint16_t& aType, + const OriginAttributes& aOriginAttributes, + const nsIDNSService::DNSFlags& aFlags, + nsIDNSListener* aListener, nsIEventTarget* target); + + void OnRecvCancelDNSRequest(const nsCString& hostName, + const nsCString& trrServer, const int32_t& port, + const uint16_t& type, + const OriginAttributes& originAttributes, + const nsIDNSService::DNSFlags& flags, + const nsresult& reason) override; + bool OnRecvLookupCompleted(const DNSRequestResponse& reply) override; + void OnIPCActorDestroy() override; + + // Sends IPDL request to DNSRequestHandler + void StartRequest(); + void CallOnLookupComplete(); + + DNSRequestSender* AsDNSRequestSender() override { return this; } + DNSRequestHandler* AsDNSRequestHandler() override { return nullptr; } + + private: + friend class ChildDNSService; + virtual ~DNSRequestSender() = default; + + nsCOMPtr<nsIDNSListener> mListener; + nsCOMPtr<nsIEventTarget> mTarget; + nsCOMPtr<nsIDNSRecord> mResultRecord; + nsresult mResultStatus = NS_OK; + nsCString mHost; + nsCString mTrrServer; + int32_t mPort; + uint16_t mType = 0; + const OriginAttributes mOriginAttributes; + nsIDNSService::DNSFlags mFlags = nsIDNSService::RESOLVE_DEFAULT_FLAGS; +}; + +// DNSRequestHandler handles the dns request and sends the result back via IPC. +// Note this class could be used both in parent process and socket process. +class DNSRequestHandler final : public DNSRequestBase, public nsIDNSListener { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIDNSLISTENER + + DNSRequestHandler() = default; + + void DoAsyncResolve(const nsACString& hostname, const nsACString& trrServer, + int32_t port, uint16_t type, + const OriginAttributes& originAttributes, + nsIDNSService::DNSFlags flags); + + void OnRecvCancelDNSRequest(const nsCString& hostName, + const nsCString& trrServer, const int32_t& port, + const uint16_t& type, + const OriginAttributes& originAttributes, + const nsIDNSService::DNSFlags& flags, + const nsresult& reason) override; + bool OnRecvLookupCompleted(const DNSRequestResponse& reply) override; + void OnIPCActorDestroy() override; + + DNSRequestSender* AsDNSRequestSender() override { return nullptr; } + DNSRequestHandler* AsDNSRequestHandler() override { return this; } + + private: + virtual ~DNSRequestHandler() = default; + + nsIDNSService::DNSFlags mFlags = nsIDNSService::RESOLVE_DEFAULT_FLAGS; +}; + +// Provides some common methods for DNSRequestChild and DNSRequestParent. +class DNSRequestActor { + public: + NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING + + explicit DNSRequestActor(DNSRequestBase* aRequest) : mDNSRequest(aRequest) {} + + virtual bool CanSend() const = 0; + virtual DNSRequestChild* AsDNSRequestChild() = 0; + virtual DNSRequestParent* AsDNSRequestParent() = 0; + + DNSRequestBase* GetDNSRequest() { return mDNSRequest.get(); }; + + protected: + virtual ~DNSRequestActor() = default; + + RefPtr<DNSRequestBase> mDNSRequest; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_DNSRequestBase_h diff --git a/netwerk/dns/DNSRequestChild.cpp b/netwerk/dns/DNSRequestChild.cpp new file mode 100644 index 0000000000..ec0f56b3f1 --- /dev/null +++ b/netwerk/dns/DNSRequestChild.cpp @@ -0,0 +1,574 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/ContentChild.h" +#include "mozilla/net/ChildDNSService.h" +#include "mozilla/net/DNSByTypeRecord.h" +#include "mozilla/net/DNSRequestChild.h" +#include "mozilla/net/DNSRequestParent.h" +#include "mozilla/net/NeckoChild.h" +#include "mozilla/net/SocketProcessChild.h" +#include "mozilla/SchedulerGroup.h" +#include "mozilla/net/SocketProcessParent.h" +#include "mozilla/Unused.h" +#include "nsIDNSRecord.h" +#include "nsIDNSByTypeRecord.h" +#include "nsHostResolver.h" +#include "nsIOService.h" +#include "nsTArray.h" +#include "nsNetAddr.h" +#include "nsThreadUtils.h" + +using namespace mozilla::ipc; + +namespace mozilla { +namespace net { + +void DNSRequestBase::SetIPCActor(DNSRequestActor* aActor) { + mIPCActor = aActor; +} + +//----------------------------------------------------------------------------- +// ChildDNSRecord: +// A simple class to provide nsIDNSRecord on the child +//----------------------------------------------------------------------------- + +class ChildDNSRecord : public nsIDNSAddrRecord { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIDNSRECORD + NS_DECL_NSIDNSADDRRECORD + + ChildDNSRecord(const DNSRecord& reply, nsIDNSService::DNSFlags flags); + + private: + virtual ~ChildDNSRecord() = default; + + nsCString mCanonicalName; + nsTArray<NetAddr> mAddresses; + uint32_t mCurrent = 0; // addr iterator + nsIDNSService::DNSFlags mFlags = nsIDNSService::RESOLVE_DEFAULT_FLAGS; + double mTrrFetchDuration = 0; + double mTrrFetchDurationNetworkOnly = 0; + bool mIsTRR = false; + bool mResolvedInSocketProcess = false; + nsIRequest::TRRMode mEffectiveTRRMode = nsIRequest::TRR_DEFAULT_MODE; + nsITRRSkipReason::value mTRRSkipReason = nsITRRSkipReason::TRR_UNSET; + uint32_t mTTL = 0; +}; + +NS_IMPL_ISUPPORTS(ChildDNSRecord, nsIDNSRecord, nsIDNSAddrRecord) + +ChildDNSRecord::ChildDNSRecord(const DNSRecord& reply, + nsIDNSService::DNSFlags flags) + : mFlags(flags) { + mCanonicalName = reply.canonicalName(); + mTrrFetchDuration = reply.trrFetchDuration(); + mTrrFetchDurationNetworkOnly = reply.trrFetchDurationNetworkOnly(); + mIsTRR = reply.isTRR(); + // When ChildDNSRecord is created in parent process, we know this is case that + // DNS resolution is done in socket process. + mResolvedInSocketProcess = XRE_IsParentProcess(); + mEffectiveTRRMode = reply.effectiveTRRMode(); + + // A shame IPDL gives us no way to grab ownership of array: so copy it. + const nsTArray<NetAddr>& addrs = reply.addrs(); + mAddresses = addrs.Clone(); + mTTL = reply.ttl(); +} + +//----------------------------------------------------------------------------- +// ChildDNSRecord::nsIDNSAddrRecord +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +ChildDNSRecord::GetCanonicalName(nsACString& result) { + if (!(mFlags & nsHostResolver::RES_CANON_NAME)) { + return NS_ERROR_NOT_AVAILABLE; + } + + result = mCanonicalName; + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSRecord::IsTRR(bool* retval) { + *retval = mIsTRR; + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSRecord::ResolvedInSocketProcess(bool* retval) { + *retval = mResolvedInSocketProcess; + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSRecord::GetTrrFetchDuration(double* aTime) { + *aTime = mTrrFetchDuration; + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSRecord::GetTrrFetchDurationNetworkOnly(double* aTime) { + *aTime = mTrrFetchDurationNetworkOnly; + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSRecord::GetNextAddr(uint16_t port, NetAddr* addr) { + if (mCurrent >= mAddresses.Length()) { + return NS_ERROR_NOT_AVAILABLE; + } + + memcpy(addr, &mAddresses[mCurrent++], sizeof(NetAddr)); + + // both Ipv4/6 use same bits for port, so safe to just use ipv4's field + addr->inet.port = htons(port); + + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSRecord::GetAddresses(nsTArray<NetAddr>& aAddressArray) { + aAddressArray = mAddresses.Clone(); + return NS_OK; +} + +// shamelessly copied from nsDNSRecord +NS_IMETHODIMP +ChildDNSRecord::GetScriptableNextAddr(uint16_t port, nsINetAddr** result) { + NetAddr addr; + nsresult rv = GetNextAddr(port, &addr); + if (NS_FAILED(rv)) { + return rv; + } + + RefPtr<nsNetAddr> netaddr = new nsNetAddr(&addr); + netaddr.forget(result); + + return NS_OK; +} + +// also copied from nsDNSRecord +NS_IMETHODIMP +ChildDNSRecord::GetNextAddrAsString(nsACString& result) { + NetAddr addr; + nsresult rv = GetNextAddr(0, &addr); + if (NS_FAILED(rv)) { + return rv; + } + + char buf[kIPv6CStrBufSize]; + if (addr.ToStringBuffer(buf, sizeof(buf))) { + result.Assign(buf); + return NS_OK; + } + NS_ERROR("NetAddrToString failed unexpectedly"); + return NS_ERROR_FAILURE; // conversion failed for some reason +} + +NS_IMETHODIMP +ChildDNSRecord::HasMore(bool* result) { + *result = mCurrent < mAddresses.Length(); + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSRecord::Rewind() { + mCurrent = 0; + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSRecord::ReportUnusable(uint16_t aPort) { + // "We thank you for your feedback" == >/dev/null + // TODO: we could send info back to parent. + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSRecord::GetEffectiveTRRMode(nsIRequest::TRRMode* aMode) { + *aMode = mEffectiveTRRMode; + return NS_OK; +} + +NS_IMETHODIMP ChildDNSRecord::GetTrrSkipReason( + nsITRRSkipReason::value* aTrrSkipReason) { + *aTrrSkipReason = mTRRSkipReason; + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSRecord::GetTtl(uint32_t* aTtl) { + *aTtl = mTTL; + return NS_OK; +} + +class ChildDNSByTypeRecord : public nsIDNSByTypeRecord, + public nsIDNSTXTRecord, + public nsIDNSHTTPSSVCRecord, + public DNSHTTPSSVCRecordBase { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIDNSRECORD + NS_DECL_NSIDNSBYTYPERECORD + NS_DECL_NSIDNSTXTRECORD + NS_DECL_NSIDNSHTTPSSVCRECORD + + explicit ChildDNSByTypeRecord(const TypeRecordResultType& reply, + const nsACString& aHost, uint32_t aTTL); + + private: + virtual ~ChildDNSByTypeRecord() = default; + + TypeRecordResultType mResults = AsVariant(mozilla::Nothing()); + bool mAllRecordsExcluded = false; + uint32_t mTTL = 0; +}; + +NS_IMPL_ISUPPORTS(ChildDNSByTypeRecord, nsIDNSByTypeRecord, nsIDNSRecord, + nsIDNSTXTRecord, nsIDNSHTTPSSVCRecord) + +ChildDNSByTypeRecord::ChildDNSByTypeRecord(const TypeRecordResultType& reply, + const nsACString& aHost, + uint32_t aTTL) + : DNSHTTPSSVCRecordBase(aHost) { + mResults = reply; + mTTL = aTTL; +} + +NS_IMETHODIMP +ChildDNSByTypeRecord::GetType(uint32_t* aType) { + *aType = mResults.match( + [](TypeRecordEmpty&) { + MOZ_ASSERT(false, "This should never be the case"); + return nsIDNSService::RESOLVE_TYPE_DEFAULT; + }, + [](TypeRecordTxt&) { return nsIDNSService::RESOLVE_TYPE_TXT; }, + [](TypeRecordHTTPSSVC&) { return nsIDNSService::RESOLVE_TYPE_HTTPSSVC; }); + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSByTypeRecord::GetRecords(CopyableTArray<nsCString>& aRecords) { + if (!mResults.is<TypeRecordTxt>()) { + return NS_ERROR_NOT_AVAILABLE; + } + aRecords = mResults.as<CopyableTArray<nsCString>>(); + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSByTypeRecord::GetRecordsAsOneString(nsACString& aRecords) { + // deep copy + if (!mResults.is<TypeRecordTxt>()) { + return NS_ERROR_NOT_AVAILABLE; + } + auto& results = mResults.as<CopyableTArray<nsCString>>(); + for (uint32_t i = 0; i < results.Length(); i++) { + aRecords.Append(results[i]); + } + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSByTypeRecord::GetRecords(nsTArray<RefPtr<nsISVCBRecord>>& aRecords) { + if (!mResults.is<TypeRecordHTTPSSVC>()) { + return NS_ERROR_NOT_AVAILABLE; + } + + auto& results = mResults.as<TypeRecordHTTPSSVC>(); + + for (const SVCB& r : results) { + RefPtr<nsISVCBRecord> rec = new SVCBRecord(r); + aRecords.AppendElement(rec); + } + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSByTypeRecord::GetServiceModeRecord(bool aNoHttp2, bool aNoHttp3, + nsISVCBRecord** aRecord) { + if (!mResults.is<TypeRecordHTTPSSVC>()) { + return NS_ERROR_NOT_AVAILABLE; + } + + auto& results = mResults.as<TypeRecordHTTPSSVC>(); + nsCOMPtr<nsISVCBRecord> result = GetServiceModeRecordInternal( + aNoHttp2, aNoHttp3, results, mAllRecordsExcluded); + if (!result) { + return NS_ERROR_NOT_AVAILABLE; + } + + result.forget(aRecord); + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSByTypeRecord::GetAllRecordsWithEchConfig( + bool aNoHttp2, bool aNoHttp3, bool* aAllRecordsHaveEchConfig, + bool* aAllRecordsInH3ExcludedList, + nsTArray<RefPtr<nsISVCBRecord>>& aResult) { + if (!mResults.is<TypeRecordHTTPSSVC>()) { + return NS_ERROR_NOT_AVAILABLE; + } + + auto& records = mResults.as<TypeRecordHTTPSSVC>(); + GetAllRecordsWithEchConfigInternal(aNoHttp2, aNoHttp3, records, + aAllRecordsHaveEchConfig, + aAllRecordsInH3ExcludedList, aResult); + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSByTypeRecord::GetHasIPAddresses(bool* aResult) { + NS_ENSURE_ARG(aResult); + + if (!mResults.is<TypeRecordHTTPSSVC>()) { + return NS_ERROR_NOT_AVAILABLE; + } + + auto& results = mResults.as<TypeRecordHTTPSSVC>(); + *aResult = HasIPAddressesInternal(results); + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSByTypeRecord::GetAllRecordsExcluded(bool* aResult) { + NS_ENSURE_ARG(aResult); + + if (!mResults.is<TypeRecordHTTPSSVC>()) { + return NS_ERROR_NOT_AVAILABLE; + } + + *aResult = mAllRecordsExcluded; + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSByTypeRecord::GetResults(mozilla::net::TypeRecordResultType* aResults) { + *aResults = mResults; + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSByTypeRecord::GetTtl(uint32_t* aResult) { + *aResult = mTTL; + return NS_OK; +} + +//----------------------------------------------------------------------------- +// DNSRequestSender +//----------------------------------------------------------------------------- + +NS_IMPL_ISUPPORTS(DNSRequestSender, nsICancelable) + +DNSRequestSender::DNSRequestSender(const nsACString& aHost, + const nsACString& aTrrServer, int32_t aPort, + const uint16_t& aType, + const OriginAttributes& aOriginAttributes, + const nsIDNSService::DNSFlags& aFlags, + nsIDNSListener* aListener, + nsIEventTarget* target) + : mListener(aListener), + mTarget(target), + mResultStatus(NS_OK), + mHost(aHost), + mTrrServer(aTrrServer), + mPort(aPort), + mType(aType), + mOriginAttributes(aOriginAttributes), + mFlags(aFlags) {} + +void DNSRequestSender::OnRecvCancelDNSRequest( + const nsCString& hostName, const nsCString& trrServer, const int32_t& port, + const uint16_t& type, const OriginAttributes& originAttributes, + const nsIDNSService::DNSFlags& flags, const nsresult& reason) {} + +NS_IMETHODIMP +DNSRequestSender::Cancel(nsresult reason) { + if (!mIPCActor) { + return NS_ERROR_NOT_AVAILABLE; + } + + // We can only do IPC on the MainThread + nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction( + "net::CancelDNSRequestEvent", + [actor(mIPCActor), host(mHost), trrServer(mTrrServer), port(mPort), + type(mType), originAttributes(mOriginAttributes), flags(mFlags), + reason]() { + if (!actor->CanSend()) { + return; + } + + if (DNSRequestChild* child = actor->AsDNSRequestChild()) { + Unused << child->SendCancelDNSRequest( + host, trrServer, port, type, originAttributes, flags, reason); + } else if (DNSRequestParent* parent = actor->AsDNSRequestParent()) { + Unused << parent->SendCancelDNSRequest( + host, trrServer, port, type, originAttributes, flags, reason); + } + }); + SchedulerGroup::Dispatch(runnable.forget()); + return NS_OK; +} + +void DNSRequestSender::StartRequest() { + // we can only do IPC on the MainThread + if (!NS_IsMainThread()) { + SchedulerGroup::Dispatch( + NewRunnableMethod("net::DNSRequestSender::StartRequest", this, + &DNSRequestSender::StartRequest)); + return; + } + + if (RefPtr<DNSRequestChild> child = mIPCActor->AsDNSRequestChild()) { + if (XRE_IsContentProcess()) { + mozilla::dom::ContentChild* cc = + static_cast<mozilla::dom::ContentChild*>(gNeckoChild->Manager()); + if (cc->IsShuttingDown()) { + return; + } + + // Send request to Parent process. + gNeckoChild->SendPDNSRequestConstructor(child, mHost, mTrrServer, mPort, + mType, mOriginAttributes, mFlags); + } else if (XRE_IsSocketProcess()) { + // DNS resolution is done in the parent process. Send a DNS request to + // parent process. + MOZ_ASSERT(!nsIOService::UseSocketProcess()); + + SocketProcessChild* socketProcessChild = + SocketProcessChild::GetSingleton(); + if (!socketProcessChild->CanSend()) { + return; + } + + MOZ_ALWAYS_TRUE(socketProcessChild->SendPDNSRequestConstructor( + child, mHost, mTrrServer, mPort, mType, mOriginAttributes, mFlags)); + } else { + MOZ_ASSERT(false, "Wrong process"); + return; + } + } else if (DNSRequestParent* parent = mIPCActor->AsDNSRequestParent()) { + // DNS resolution is done in the socket process. Send a DNS request to + // socket process. + MOZ_ASSERT(nsIOService::UseSocketProcess()); + + RefPtr<DNSRequestParent> requestParent = parent; + RefPtr<DNSRequestSender> self = this; + auto task = [requestParent, self]() { + Unused << SocketProcessParent::GetSingleton()->SendPDNSRequestConstructor( + requestParent, self->mHost, self->mTrrServer, self->mPort, + self->mType, self->mOriginAttributes, self->mFlags); + }; + if (!gIOService->SocketProcessReady()) { + gIOService->CallOrWaitForSocketProcess(std::move(task)); + return; + } + + task(); + } +} + +void DNSRequestSender::CallOnLookupComplete() { + MOZ_ASSERT(mListener); + mListener->OnLookupComplete(this, mResultRecord, mResultStatus); +} + +bool DNSRequestSender::OnRecvLookupCompleted(const DNSRequestResponse& reply) { + MOZ_ASSERT(mListener); + + switch (reply.type()) { + case DNSRequestResponse::TDNSRecord: { + mResultRecord = new ChildDNSRecord(reply.get_DNSRecord(), mFlags); + break; + } + case DNSRequestResponse::Tnsresult: { + mResultStatus = reply.get_nsresult(); + break; + } + case DNSRequestResponse::TIPCTypeRecord: { + MOZ_ASSERT(mType != nsIDNSService::RESOLVE_TYPE_DEFAULT); + mResultRecord = + new ChildDNSByTypeRecord(reply.get_IPCTypeRecord().mData, mHost, + reply.get_IPCTypeRecord().mTTL); + break; + } + default: + MOZ_ASSERT_UNREACHABLE("unknown type"); + return false; + } + + MOZ_ASSERT(NS_IsMainThread()); + + bool targetIsMain = false; + if (!mTarget) { + targetIsMain = true; + } else { + mTarget->IsOnCurrentThread(&targetIsMain); + } + + if (targetIsMain) { + CallOnLookupComplete(); + } else { + nsCOMPtr<nsIRunnable> event = + NewRunnableMethod("net::DNSRequestSender::CallOnLookupComplete", this, + &DNSRequestSender::CallOnLookupComplete); + mTarget->Dispatch(event, NS_DISPATCH_NORMAL); + } + + if (DNSRequestChild* child = mIPCActor->AsDNSRequestChild()) { + Unused << mozilla::net::DNSRequestChild::Send__delete__(child); + } else if (DNSRequestParent* parent = mIPCActor->AsDNSRequestParent()) { + Unused << mozilla::net::DNSRequestParent::Send__delete__(parent); + } + + return true; +} + +void DNSRequestSender::OnIPCActorDestroy() { + // Request is done or destroyed. Remove it from the hash table. + RefPtr<ChildDNSService> dnsServiceChild = + dont_AddRef(ChildDNSService::GetSingleton()); + dnsServiceChild->NotifyRequestDone(this); + + mIPCActor = nullptr; +} + +//----------------------------------------------------------------------------- +// DNSRequestChild +//----------------------------------------------------------------------------- + +DNSRequestChild::DNSRequestChild(DNSRequestBase* aRequest) + : DNSRequestActor(aRequest) { + aRequest->SetIPCActor(this); +} + +mozilla::ipc::IPCResult DNSRequestChild::RecvCancelDNSRequest( + const nsCString& hostName, const nsCString& trrServer, const int32_t& port, + const uint16_t& type, const OriginAttributes& originAttributes, + const nsIDNSService::DNSFlags& flags, const nsresult& reason) { + mDNSRequest->OnRecvCancelDNSRequest(hostName, trrServer, port, type, + originAttributes, flags, reason); + return IPC_OK(); +} + +mozilla::ipc::IPCResult DNSRequestChild::RecvLookupCompleted( + const DNSRequestResponse& reply) { + return mDNSRequest->OnRecvLookupCompleted(reply) ? IPC_OK() + : IPC_FAIL_NO_REASON(this); +} + +void DNSRequestChild::ActorDestroy(ActorDestroyReason) { + mDNSRequest->OnIPCActorDestroy(); + mDNSRequest = nullptr; +} + +//------------------------------------------------------------------------------ +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/DNSRequestChild.h b/netwerk/dns/DNSRequestChild.h new file mode 100644 index 0000000000..064c2425e0 --- /dev/null +++ b/netwerk/dns/DNSRequestChild.h @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_DNSRequestChild_h +#define mozilla_net_DNSRequestChild_h + +#include "mozilla/net/DNSRequestBase.h" +#include "mozilla/net/PDNSRequestChild.h" + +namespace mozilla { +namespace net { + +class DNSRequestChild final : public DNSRequestActor, public PDNSRequestChild { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DNSRequestChild, override) + friend class PDNSRequestChild; + + explicit DNSRequestChild(DNSRequestBase* aRequest); + + bool CanSend() const override { return PDNSRequestChild::CanSend(); } + DNSRequestChild* AsDNSRequestChild() override { return this; } + DNSRequestParent* AsDNSRequestParent() override { return nullptr; } + + private: + virtual ~DNSRequestChild() = default; + + mozilla::ipc::IPCResult RecvCancelDNSRequest( + const nsCString& hostName, const nsCString& trrServer, + const int32_t& port, const uint16_t& type, + const OriginAttributes& originAttributes, + const nsIDNSService::DNSFlags& flags, const nsresult& reason); + mozilla::ipc::IPCResult RecvLookupCompleted(const DNSRequestResponse& reply); + virtual void ActorDestroy(ActorDestroyReason why) override; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_DNSRequestChild_h diff --git a/netwerk/dns/DNSRequestParent.cpp b/netwerk/dns/DNSRequestParent.cpp new file mode 100644 index 0000000000..09213b59ba --- /dev/null +++ b/netwerk/dns/DNSRequestParent.cpp @@ -0,0 +1,183 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/net/DNSRequestParent.h" +#include "mozilla/net/DNSRequestChild.h" +#include "nsIDNSService.h" +#include "nsNetCID.h" +#include "nsThreadUtils.h" +#include "nsICancelable.h" +#include "nsIDNSRecord.h" +#include "nsHostResolver.h" +#include "mozilla/Unused.h" +#include "DNSAdditionalInfo.h" +#include "nsServiceManagerUtils.h" + +using namespace mozilla::ipc; + +namespace mozilla { +namespace net { + +//----------------------------------------------------------------------------- +// DNSRequestHandler::nsISupports +//----------------------------------------------------------------------------- + +NS_IMPL_ISUPPORTS(DNSRequestHandler, nsIDNSListener) + +static void SendLookupCompletedHelper(DNSRequestActor* aActor, + const DNSRequestResponse& aReply) { + if (DNSRequestParent* parent = aActor->AsDNSRequestParent()) { + Unused << parent->SendLookupCompleted(aReply); + } else if (DNSRequestChild* child = aActor->AsDNSRequestChild()) { + Unused << child->SendLookupCompleted(aReply); + } +} + +void DNSRequestHandler::DoAsyncResolve(const nsACString& hostname, + const nsACString& trrServer, + int32_t port, uint16_t type, + const OriginAttributes& originAttributes, + nsIDNSService::DNSFlags flags) { + nsresult rv; + mFlags = flags; + nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr<nsIEventTarget> main = GetMainThreadSerialEventTarget(); + nsCOMPtr<nsICancelable> unused; + RefPtr<DNSAdditionalInfo> info; + if (!trrServer.IsEmpty() || port != -1) { + info = new DNSAdditionalInfo(trrServer, port); + } + rv = dns->AsyncResolveNative( + hostname, static_cast<nsIDNSService::ResolveType>(type), flags, info, + this, main, originAttributes, getter_AddRefs(unused)); + } + + if (NS_FAILED(rv) && mIPCActor->CanSend()) { + SendLookupCompletedHelper(mIPCActor, DNSRequestResponse(rv)); + } +} + +void DNSRequestHandler::OnRecvCancelDNSRequest( + const nsCString& hostName, const nsCString& aTrrServer, const int32_t& port, + const uint16_t& type, const OriginAttributes& originAttributes, + const nsIDNSService::DNSFlags& flags, const nsresult& reason) { + nsresult rv; + nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv); + if (NS_SUCCEEDED(rv)) { + RefPtr<DNSAdditionalInfo> info; + if (!aTrrServer.IsEmpty() || port != -1) { + info = new DNSAdditionalInfo(aTrrServer, port); + } + rv = dns->CancelAsyncResolveNative( + hostName, static_cast<nsIDNSService::ResolveType>(type), flags, info, + this, reason, originAttributes); + } +} + +bool DNSRequestHandler::OnRecvLookupCompleted(const DNSRequestResponse& reply) { + return true; +} + +//----------------------------------------------------------------------------- +// nsIDNSListener functions +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +DNSRequestHandler::OnLookupComplete(nsICancelable* request, + nsIDNSRecord* aRecord, nsresult status) { + if (!mIPCActor || !mIPCActor->CanSend()) { + // nothing to do: child probably crashed + return NS_OK; + } + + if (NS_SUCCEEDED(status)) { + MOZ_ASSERT(aRecord); + + nsCOMPtr<nsIDNSByTypeRecord> byTypeRec = do_QueryInterface(aRecord); + if (byTypeRec) { + IPCTypeRecord result; + byTypeRec->GetResults(&result.mData); + if (nsCOMPtr<nsIDNSHTTPSSVCRecord> rec = do_QueryInterface(aRecord)) { + rec->GetTtl(&result.mTTL); + } + SendLookupCompletedHelper(mIPCActor, DNSRequestResponse(result)); + return NS_OK; + } + + nsCOMPtr<nsIDNSAddrRecord> rec = do_QueryInterface(aRecord); + MOZ_ASSERT(rec); + nsAutoCString cname; + if (mFlags & nsHostResolver::RES_CANON_NAME) { + rec->GetCanonicalName(cname); + } + + // Get IP addresses for hostname (use port 80 as dummy value for NetAddr) + nsTArray<NetAddr> array; + NetAddr addr; + while (NS_SUCCEEDED(rec->GetNextAddr(80, &addr))) { + array.AppendElement(addr); + } + + double trrFetchDuration; + rec->GetTrrFetchDuration(&trrFetchDuration); + + double trrFetchDurationNetworkOnly; + rec->GetTrrFetchDurationNetworkOnly(&trrFetchDurationNetworkOnly); + + bool isTRR = false; + rec->IsTRR(&isTRR); + + nsIRequest::TRRMode effectiveTRRMode = nsIRequest::TRR_DEFAULT_MODE; + rec->GetEffectiveTRRMode(&effectiveTRRMode); + + uint32_t ttl = 0; + rec->GetTtl(&ttl); + + SendLookupCompletedHelper( + mIPCActor, DNSRequestResponse(DNSRecord(cname, array, trrFetchDuration, + trrFetchDurationNetworkOnly, + isTRR, effectiveTRRMode, ttl))); + } else { + SendLookupCompletedHelper(mIPCActor, DNSRequestResponse(status)); + } + + return NS_OK; +} + +void DNSRequestHandler::OnIPCActorDestroy() { mIPCActor = nullptr; } + +//----------------------------------------------------------------------------- +// DNSRequestParent functions +//----------------------------------------------------------------------------- + +DNSRequestParent::DNSRequestParent(DNSRequestBase* aRequest) + : DNSRequestActor(aRequest) { + aRequest->SetIPCActor(this); +} + +mozilla::ipc::IPCResult DNSRequestParent::RecvCancelDNSRequest( + const nsCString& hostName, const nsCString& trrServer, const int32_t& port, + const uint16_t& type, const OriginAttributes& originAttributes, + const nsIDNSService::DNSFlags& flags, const nsresult& reason) { + mDNSRequest->OnRecvCancelDNSRequest(hostName, trrServer, port, type, + originAttributes, flags, reason); + return IPC_OK(); +} + +mozilla::ipc::IPCResult DNSRequestParent::RecvLookupCompleted( + const DNSRequestResponse& reply) { + return mDNSRequest->OnRecvLookupCompleted(reply) ? IPC_OK() + : IPC_FAIL_NO_REASON(this); +} + +void DNSRequestParent::ActorDestroy(ActorDestroyReason) { + mDNSRequest->OnIPCActorDestroy(); + mDNSRequest = nullptr; +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/DNSRequestParent.h b/netwerk/dns/DNSRequestParent.h new file mode 100644 index 0000000000..7583709678 --- /dev/null +++ b/netwerk/dns/DNSRequestParent.h @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_DNSRequestParent_h +#define mozilla_net_DNSRequestParent_h + +#include "mozilla/net/DNSRequestBase.h" +#include "mozilla/net/PDNSRequestParent.h" +#include "nsIDNSListener.h" + +namespace mozilla { +namespace net { + +class DNSRequestParent : public DNSRequestActor, public PDNSRequestParent { + public: + friend class PDNSRequestParent; + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DNSRequestParent, override) + + explicit DNSRequestParent(DNSRequestBase* aRequest); + + bool CanSend() const override { return PDNSRequestParent::CanSend(); } + DNSRequestChild* AsDNSRequestChild() override { return nullptr; } + DNSRequestParent* AsDNSRequestParent() override { return this; } + + private: + virtual ~DNSRequestParent() = default; + + // Pass args here rather than storing them in the parent; they are only + // needed if the request is to be canceled. + mozilla::ipc::IPCResult RecvCancelDNSRequest( + const nsCString& hostName, const nsCString& trrServer, + const int32_t& port, const uint16_t& type, + const OriginAttributes& originAttributes, + const nsIDNSService::DNSFlags& flags, const nsresult& reason); + mozilla::ipc::IPCResult RecvLookupCompleted(const DNSRequestResponse& reply); + void ActorDestroy(ActorDestroyReason) override; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_DNSRequestParent_h diff --git a/netwerk/dns/DNSServiceBase.cpp b/netwerk/dns/DNSServiceBase.cpp new file mode 100644 index 0000000000..85b2da37e3 --- /dev/null +++ b/netwerk/dns/DNSServiceBase.cpp @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DNSServiceBase.h" + +#include "DNS.h" +#include "mozilla/Preferences.h" +#include "mozilla/StaticPrefs_network.h" +#include "nsIDNSService.h" +#include "nsIProtocolProxyService2.h" +#include "nsIPrefBranch.h" + +namespace mozilla::net { + +static const char kPrefProxyType[] = "network.proxy.type"; +static const char kPrefDisablePrefetch[] = "network.dns.disablePrefetch"; +static const char kPrefNetworkProxySOCKS[] = "network.proxy.socks"; + +NS_IMPL_ISUPPORTS(DNSServiceBase, nsIObserver) + +void DNSServiceBase::AddPrefObserver(nsIPrefBranch* aPrefs) { + aPrefs->AddObserver(kPrefProxyType, this, false); + aPrefs->AddObserver(kPrefDisablePrefetch, this, false); + // Monitor these to see if there is a change in proxy configuration + aPrefs->AddObserver(kPrefNetworkProxySOCKS, this, false); +} + +void DNSServiceBase::ReadPrefs(const char* aName) { + if (!aName || !strcmp(aName, kPrefNetworkProxySOCKS)) { + nsAutoCString socks; + if (NS_SUCCEEDED(Preferences::GetCString(kPrefNetworkProxySOCKS, socks))) { + mHasSocksProxy = !socks.IsEmpty(); + } + } + if (!aName || !strcmp(aName, kPrefDisablePrefetch) || + !strcmp(aName, kPrefProxyType)) { + mDisablePrefetch = Preferences::GetBool(kPrefDisablePrefetch, false) || + (StaticPrefs::network_proxy_type() == + nsIProtocolProxyService::PROXYCONFIG_MANUAL); + } +} + +bool DNSServiceBase::DNSForbiddenByActiveProxy(const nsACString& aHostname, + uint32_t aFlags) { + if (aFlags & nsIDNSService::RESOLVE_IGNORE_SOCKS_DNS) { + return false; + } + + // We should avoid doing DNS when a proxy is in use. + if (StaticPrefs::network_proxy_type() == + nsIProtocolProxyService::PROXYCONFIG_MANUAL && + mHasSocksProxy && StaticPrefs::network_proxy_socks_remote_dns()) { + // Allow IP lookups through, but nothing else. + if (!HostIsIPLiteral(aHostname)) { + return true; + } + } + return false; +} + +} // namespace mozilla::net diff --git a/netwerk/dns/DNSServiceBase.h b/netwerk/dns/DNSServiceBase.h new file mode 100644 index 0000000000..9657d809f7 --- /dev/null +++ b/netwerk/dns/DNSServiceBase.h @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_DNSServiceBase_h +#define mozilla_net_DNSServiceBase_h + +#include "mozilla/Atomics.h" +#include "nsIObserver.h" +#include "nsString.h" + +class nsIPrefBranch; + +namespace mozilla::net { + +class DNSServiceBase : public nsIObserver { + public: + NS_DECL_THREADSAFE_ISUPPORTS + + DNSServiceBase() = default; + + protected: + virtual ~DNSServiceBase() = default; + void AddPrefObserver(nsIPrefBranch* aPrefs); + virtual void ReadPrefs(const char* aName); + bool DNSForbiddenByActiveProxy(const nsACString& aHostname, uint32_t aFlags); + + mozilla::Atomic<bool, mozilla::Relaxed> mDisablePrefetch{false}; + mozilla::Atomic<bool, mozilla::Relaxed> mHasSocksProxy{false}; +}; + +} // namespace mozilla::net + +#endif // mozilla_net_DNSServiceBase_h diff --git a/netwerk/dns/DNSUtils.cpp b/netwerk/dns/DNSUtils.cpp new file mode 100644 index 0000000000..8101f28116 --- /dev/null +++ b/netwerk/dns/DNSUtils.cpp @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=4 sw=2 sts=2 et cin: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DNSUtils.h" + +#include "nsContentUtils.h" +#include "nsHttpHandler.h" +#include "nsIHttpChannel.h" +#include "nsIHttpChannelInternal.h" +#include "nsIIOService.h" +#include "mozilla/SyncRunnable.h" +#include "TRRServiceChannel.h" +#include "TRRLoadInfo.h" + +namespace mozilla { +namespace net { + +static void InitHttpHandler() { + nsresult rv; + nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv); + if (NS_FAILED(rv)) { + return; + } + + nsCOMPtr<nsIProtocolHandler> handler; + rv = ios->GetProtocolHandler("http", getter_AddRefs(handler)); + if (NS_FAILED(rv)) { + return; + } +} + +// static +nsresult DNSUtils::CreateChannelHelper(nsIURI* aUri, nsIChannel** aResult) { + *aResult = nullptr; + + if (NS_IsMainThread() && !XRE_IsSocketProcess()) { + nsresult rv; + nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv)); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_NewChannel( + aResult, aUri, nsContentUtils::GetSystemPrincipal(), + nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, + nsIContentPolicy::TYPE_OTHER, + nullptr, // nsICookieJarSettings + nullptr, // PerformanceStorage + nullptr, // aLoadGroup + nullptr, // aCallbacks + nsIRequest::LOAD_NORMAL, ios); + } + + // Unfortunately, we can only initialize gHttpHandler on main thread. + if (!gHttpHandler) { + nsCOMPtr<nsIEventTarget> main = GetMainThreadSerialEventTarget(); + if (main) { + // Forward to the main thread synchronously. + SyncRunnable::DispatchToThread( + main, NS_NewRunnableFunction("InitHttpHandler", + []() { InitHttpHandler(); })); + } + } + + if (!gHttpHandler) { + return NS_ERROR_UNEXPECTED; + } + + RefPtr<TRRLoadInfo> loadInfo = + new TRRLoadInfo(aUri, nsIContentPolicy::TYPE_OTHER); + return gHttpHandler->CreateTRRServiceChannel(aUri, + nullptr, // givenProxyInfo + 0, // proxyResolveFlags + nullptr, // proxyURI + loadInfo, // aLoadInfo + aResult); +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/DNSUtils.h b/netwerk/dns/DNSUtils.h new file mode 100644 index 0000000000..f5b53c5d72 --- /dev/null +++ b/netwerk/dns/DNSUtils.h @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef DNSUtils_h_ +#define DNSUtils_h_ + +#include "nsError.h" + +class nsIURI; +class nsIChannel; + +namespace mozilla { +namespace net { + +class NetworkConnectivityService; +class TRR; + +class DNSUtils final { + private: + friend class NetworkConnectivityService; + friend class ObliviousHttpService; + friend class TRR; + static nsresult CreateChannelHelper(nsIURI* aUri, nsIChannel** aResult); +}; + +} // namespace net +} // namespace mozilla + +#endif // DNSUtils_h_ diff --git a/netwerk/dns/GetAddrInfo.cpp b/netwerk/dns/GetAddrInfo.cpp new file mode 100644 index 0000000000..0dd1f0099a --- /dev/null +++ b/netwerk/dns/GetAddrInfo.cpp @@ -0,0 +1,584 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "GetAddrInfo.h" + +#ifdef DNSQUERY_AVAILABLE +// There is a bug in windns.h where the type of parameter ppQueryResultsSet for +// DnsQuery_A is dependent on UNICODE being set. It should *always* be +// PDNS_RECORDA, but if UNICODE is set it is PDNS_RECORDW. To get around this +// we make sure that UNICODE is unset. +# undef UNICODE +# include <ws2tcpip.h> +# undef GetAddrInfo +# include <windns.h> +#endif // DNSQUERY_AVAILABLE + +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/net/DNS.h" +#include "NativeDNSResolverOverrideParent.h" +#include "prnetdb.h" +#include "nsIOService.h" +#include "nsHostResolver.h" +#include "nsError.h" +#include "mozilla/net/DNS.h" +#include <algorithm> +#include "prerror.h" + +#include "mozilla/Logging.h" +#include "mozilla/StaticPrefs_network.h" +#include "mozilla/net/DNSPacket.h" +#include "nsIDNSService.h" + +namespace mozilla::net { + +static StaticRefPtr<NativeDNSResolverOverride> gOverrideService; + +LazyLogModule gGetAddrInfoLog("GetAddrInfo"); +#define LOG(msg, ...) \ + MOZ_LOG(gGetAddrInfoLog, LogLevel::Debug, ("[DNS]: " msg, ##__VA_ARGS__)) +#define LOG_WARNING(msg, ...) \ + MOZ_LOG(gGetAddrInfoLog, LogLevel::Warning, ("[DNS]: " msg, ##__VA_ARGS__)) + +#ifdef DNSQUERY_AVAILABLE + +# define COMPUTER_NAME_BUFFER_SIZE 100 +static char sDNSComputerName[COMPUTER_NAME_BUFFER_SIZE]; +static char sNETBIOSComputerName[MAX_COMPUTERNAME_LENGTH + 1]; + +//////////////////////////// +// WINDOWS IMPLEMENTATION // +//////////////////////////// + +// Ensure consistency of PR_* and AF_* constants to allow for legacy usage of +// PR_* constants with this API. +static_assert(PR_AF_INET == AF_INET && PR_AF_INET6 == AF_INET6 && + PR_AF_UNSPEC == AF_UNSPEC, + "PR_AF_* must match AF_*"); + +// If successful, returns in aResult a TTL value that is smaller or +// equal with the one already there. Gets the TTL value by calling +// to DnsQuery_A and iterating through the returned +// records to find the one with the smallest TTL value. +static MOZ_ALWAYS_INLINE nsresult _CallDnsQuery_A_Windows( + const nsACString& aHost, uint16_t aAddressFamily, DWORD aFlags, + std::function<void(PDNS_RECORDA)> aCallback) { + NS_ConvertASCIItoUTF16 name(aHost); + + auto callDnsQuery_A = [&](uint16_t reqFamily) { + PDNS_RECORDA dnsData = nullptr; + DNS_STATUS status = DnsQuery_A(aHost.BeginReading(), reqFamily, aFlags, + nullptr, &dnsData, nullptr); + if (status == DNS_INFO_NO_RECORDS || status == DNS_ERROR_RCODE_NAME_ERROR || + !dnsData) { + LOG("No DNS records found for %s. status=%lX. reqFamily = %X\n", + aHost.BeginReading(), status, reqFamily); + return NS_ERROR_FAILURE; + } else if (status != NOERROR) { + LOG_WARNING("DnsQuery_A failed with status %lX.\n", status); + return NS_ERROR_UNEXPECTED; + } + + for (PDNS_RECORDA curRecord = dnsData; curRecord; + curRecord = curRecord->pNext) { + // Only records in the answer section are important + if (curRecord->Flags.S.Section != DnsSectionAnswer) { + continue; + } + if (curRecord->wType != reqFamily) { + continue; + } + + aCallback(curRecord); + } + + DnsFree(dnsData, DNS_FREE_TYPE::DnsFreeRecordList); + return NS_OK; + }; + + if (aAddressFamily == PR_AF_UNSPEC || aAddressFamily == PR_AF_INET) { + callDnsQuery_A(DNS_TYPE_A); + } + + if (aAddressFamily == PR_AF_UNSPEC || aAddressFamily == PR_AF_INET6) { + callDnsQuery_A(DNS_TYPE_AAAA); + } + return NS_OK; +} + +bool recordTypeMatchesRequest(uint16_t wType, uint16_t aAddressFamily) { + if (aAddressFamily == PR_AF_UNSPEC) { + return wType == DNS_TYPE_A || wType == DNS_TYPE_AAAA; + } + if (aAddressFamily == PR_AF_INET) { + return wType == DNS_TYPE_A; + } + if (aAddressFamily == PR_AF_INET6) { + return wType == DNS_TYPE_AAAA; + } + return false; +} + +static MOZ_ALWAYS_INLINE nsresult _GetTTLData_Windows(const nsACString& aHost, + uint32_t* aResult, + uint16_t aAddressFamily) { + MOZ_ASSERT(!aHost.IsEmpty()); + MOZ_ASSERT(aResult); + if (aAddressFamily != PR_AF_UNSPEC && aAddressFamily != PR_AF_INET && + aAddressFamily != PR_AF_INET6) { + return NS_ERROR_UNEXPECTED; + } + + // In order to avoid using ANY records which are not always implemented as a + // "Gimme what you have" request in hostname resolvers, we should send A + // and/or AAAA requests, based on the address family requested. + const DWORD ttlFlags = + (DNS_QUERY_STANDARD | DNS_QUERY_NO_NETBT | DNS_QUERY_NO_HOSTS_FILE | + DNS_QUERY_NO_MULTICAST | DNS_QUERY_ACCEPT_TRUNCATED_RESPONSE | + DNS_QUERY_DONT_RESET_TTL_VALUES); + unsigned int ttl = (unsigned int)-1; + _CallDnsQuery_A_Windows( + aHost, aAddressFamily, ttlFlags, + [&ttl, &aHost, aAddressFamily](PDNS_RECORDA curRecord) { + if (recordTypeMatchesRequest(curRecord->wType, aAddressFamily)) { + ttl = std::min<unsigned int>(ttl, curRecord->dwTtl); + } else { + LOG("Received unexpected record type %u in response for %s.\n", + curRecord->wType, aHost.BeginReading()); + } + }); + + if (ttl == (unsigned int)-1) { + LOG("No useable TTL found."); + return NS_ERROR_FAILURE; + } + + *aResult = ttl; + return NS_OK; +} + +static MOZ_ALWAYS_INLINE nsresult +_DNSQuery_A_SingleLabel(const nsACString& aCanonHost, uint16_t aAddressFamily, + uint16_t aFlags, AddrInfo** aAddrInfo) { + bool setCanonName = aFlags & nsHostResolver::RES_CANON_NAME; + nsAutoCString canonName; + const DWORD flags = (DNS_QUERY_STANDARD | DNS_QUERY_NO_MULTICAST | + DNS_QUERY_ACCEPT_TRUNCATED_RESPONSE); + nsTArray<NetAddr> addresses; + + _CallDnsQuery_A_Windows( + aCanonHost, aAddressFamily, flags, [&](PDNS_RECORDA curRecord) { + MOZ_DIAGNOSTIC_ASSERT(curRecord->wType == DNS_TYPE_A || + curRecord->wType == DNS_TYPE_AAAA); + if (setCanonName) { + canonName.Assign(curRecord->pName); + } + NetAddr addr{}; + addr.inet.family = AF_INET; + addr.inet.ip = curRecord->Data.A.IpAddress; + addresses.AppendElement(addr); + }); + + LOG("Query for: %s has %zu results", aCanonHost.BeginReading(), + addresses.Length()); + if (addresses.IsEmpty()) { + return NS_ERROR_UNKNOWN_HOST; + } + RefPtr<AddrInfo> ai(new AddrInfo( + aCanonHost, canonName, DNSResolverType::Native, 0, std::move(addresses))); + ai.forget(aAddrInfo); + + return NS_OK; +} + +#endif + +//////////////////////////////////// +// PORTABLE RUNTIME IMPLEMENTATION// +//////////////////////////////////// + +static MOZ_ALWAYS_INLINE nsresult +_GetAddrInfo_Portable(const nsACString& aCanonHost, uint16_t aAddressFamily, + uint16_t aFlags, AddrInfo** aAddrInfo) { + MOZ_ASSERT(!aCanonHost.IsEmpty()); + MOZ_ASSERT(aAddrInfo); + + // We accept the same aFlags that nsHostResolver::ResolveHost accepts, but we + // need to translate the aFlags into a form that PR_GetAddrInfoByName + // accepts. + int prFlags = PR_AI_ADDRCONFIG; + if (!(aFlags & nsHostResolver::RES_CANON_NAME)) { + prFlags |= PR_AI_NOCANONNAME; + } + + // We need to remove IPv4 records manually because PR_GetAddrInfoByName + // doesn't support PR_AF_INET6. + bool disableIPv4 = aAddressFamily == PR_AF_INET6; + if (disableIPv4) { + aAddressFamily = PR_AF_UNSPEC; + } + +#if defined(DNSQUERY_AVAILABLE) + if (StaticPrefs::network_dns_dns_query_single_label() && + !aCanonHost.Contains('.') && aCanonHost != "localhost"_ns) { + // For some reason we can't use DnsQuery_A to get the computer's IP. + if (!aCanonHost.Equals(nsDependentCString(sDNSComputerName), + nsCaseInsensitiveCStringComparator) && + !aCanonHost.Equals(nsDependentCString(sNETBIOSComputerName), + nsCaseInsensitiveCStringComparator)) { + // This is a single label name resolve without a dot. + // We use DNSQuery_A for these. + LOG("Resolving %s using DnsQuery_A (computername: %s)\n", + aCanonHost.BeginReading(), sDNSComputerName); + return _DNSQuery_A_SingleLabel(aCanonHost, aAddressFamily, aFlags, + aAddrInfo); + } + } +#endif + + LOG("Resolving %s using PR_GetAddrInfoByName", aCanonHost.BeginReading()); + PRAddrInfo* prai = + PR_GetAddrInfoByName(aCanonHost.BeginReading(), aAddressFamily, prFlags); + + if (!prai) { + LOG("PR_GetAddrInfoByName returned null PR_GetError:%d PR_GetOSErrpr:%d", + PR_GetError(), PR_GetOSError()); + return NS_ERROR_UNKNOWN_HOST; + } + + nsAutoCString canonName; + if (aFlags & nsHostResolver::RES_CANON_NAME) { + canonName.Assign(PR_GetCanonNameFromAddrInfo(prai)); + } + + bool filterNameCollision = + !(aFlags & nsHostResolver::RES_ALLOW_NAME_COLLISION); + RefPtr<AddrInfo> ai(new AddrInfo(aCanonHost, prai, disableIPv4, + filterNameCollision, canonName)); + PR_FreeAddrInfo(prai); + if (ai->Addresses().IsEmpty()) { + LOG("PR_GetAddrInfoByName returned empty address list"); + return NS_ERROR_UNKNOWN_HOST; + } + + ai.forget(aAddrInfo); + + LOG("PR_GetAddrInfoByName resolved successfully"); + return NS_OK; +} + +////////////////////////////////////// +// COMMON/PLATFORM INDEPENDENT CODE // +////////////////////////////////////// +nsresult GetAddrInfoInit() { + LOG("Initializing GetAddrInfo.\n"); + +#ifdef DNSQUERY_AVAILABLE + DWORD namesize = COMPUTER_NAME_BUFFER_SIZE; + if (!GetComputerNameExA(ComputerNameDnsHostname, sDNSComputerName, + &namesize)) { + sDNSComputerName[0] = 0; + } + namesize = MAX_COMPUTERNAME_LENGTH + 1; + if (!GetComputerNameExA(ComputerNameNetBIOS, sNETBIOSComputerName, + &namesize)) { + sNETBIOSComputerName[0] = 0; + } +#endif + return NS_OK; +} + +nsresult GetAddrInfoShutdown() { + LOG("Shutting down GetAddrInfo.\n"); + return NS_OK; +} + +bool FindAddrOverride(const nsACString& aHost, uint16_t aAddressFamily, + uint16_t aFlags, AddrInfo** aAddrInfo) { + RefPtr<NativeDNSResolverOverride> overrideService = gOverrideService; + if (!overrideService) { + return false; + } + AutoReadLock lock(overrideService->mLock); + auto overrides = overrideService->mOverrides.Lookup(aHost); + if (!overrides) { + return false; + } + nsCString* cname = nullptr; + if (aFlags & nsHostResolver::RES_CANON_NAME) { + cname = overrideService->mCnames.Lookup(aHost).DataPtrOrNull(); + } + + RefPtr<AddrInfo> ai; + + nsTArray<NetAddr> addresses; + for (const auto& ip : *overrides) { + if (aAddressFamily != AF_UNSPEC && ip.raw.family != aAddressFamily) { + continue; + } + addresses.AppendElement(ip); + } + + if (!cname) { + ai = new AddrInfo(aHost, DNSResolverType::Native, 0, std::move(addresses)); + } else { + ai = new AddrInfo(aHost, *cname, DNSResolverType::Native, 0, + std::move(addresses)); + } + + ai.forget(aAddrInfo); + return true; +} + +nsresult GetAddrInfo(const nsACString& aHost, uint16_t aAddressFamily, + uint16_t aFlags, AddrInfo** aAddrInfo, bool aGetTtl) { + if (NS_WARN_IF(aHost.IsEmpty()) || NS_WARN_IF(!aAddrInfo)) { + return NS_ERROR_NULL_POINTER; + } + *aAddrInfo = nullptr; + + if (StaticPrefs::network_dns_disabled()) { + return NS_ERROR_UNKNOWN_HOST; + } + +#ifdef DNSQUERY_AVAILABLE + // The GetTTLData needs the canonical name to function properly + if (aGetTtl) { + aFlags |= nsHostResolver::RES_CANON_NAME; + } +#endif + + // If there is an override for this host, then we synthetize a result. + if (gOverrideService && + FindAddrOverride(aHost, aAddressFamily, aFlags, aAddrInfo)) { + LOG("Returning IP address from NativeDNSResolverOverride"); + return (*aAddrInfo)->Addresses().Length() ? NS_OK : NS_ERROR_UNKNOWN_HOST; + } + + nsAutoCString host; + if (StaticPrefs::network_dns_copy_string_before_call()) { + host = Substring(aHost.BeginReading(), aHost.Length()); + MOZ_ASSERT(aHost.BeginReading() != host.BeginReading()); + } else { + host = aHost; + } + + if (gNativeIsLocalhost) { + // pretend we use the given host but use IPv4 localhost instead! + host = "localhost"_ns; + aAddressFamily = PR_AF_INET; + } + + RefPtr<AddrInfo> info; + nsresult rv = + _GetAddrInfo_Portable(host, aAddressFamily, aFlags, getter_AddRefs(info)); + +#ifdef DNSQUERY_AVAILABLE + if (aGetTtl && NS_SUCCEEDED(rv)) { + // Figure out the canonical name, or if that fails, just use the host name + // we have. + nsAutoCString name; + if (info && !info->CanonicalHostname().IsEmpty()) { + name = info->CanonicalHostname(); + } else { + name = host; + } + + LOG("Getting TTL for %s (cname = %s).", host.get(), name.get()); + uint32_t ttl = 0; + nsresult ttlRv = _GetTTLData_Windows(name, &ttl, aAddressFamily); + if (NS_SUCCEEDED(ttlRv)) { + auto builder = info->Build(); + builder.SetTTL(ttl); + info = builder.Finish(); + LOG("Got TTL %u for %s (name = %s).", ttl, host.get(), name.get()); + } else { + LOG_WARNING("Could not get TTL for %s (cname = %s).", host.get(), + name.get()); + } + } +#endif + + info.forget(aAddrInfo); + return rv; +} + +bool FindHTTPSRecordOverride(const nsACString& aHost, + TypeRecordResultType& aResult) { + LOG("FindHTTPSRecordOverride aHost=%s", nsCString(aHost).get()); + RefPtr<NativeDNSResolverOverride> overrideService = gOverrideService; + if (!overrideService) { + return false; + } + + AutoReadLock lock(overrideService->mLock); + auto overrides = overrideService->mHTTPSRecordOverrides.Lookup(aHost); + if (!overrides) { + return false; + } + + DNSPacket packet; + nsAutoCString host(aHost); + nsAutoCString cname; + + LOG("resolving %s\n", host.get()); + // Perform the query + nsresult rv = packet.FillBuffer( + [&](unsigned char response[DNSPacket::MAX_SIZE]) -> int { + if (overrides->Length() > DNSPacket::MAX_SIZE) { + return -1; + } + memcpy(response, overrides->Elements(), overrides->Length()); + return overrides->Length(); + }); + if (NS_FAILED(rv)) { + return false; + } + + uint32_t ttl = 0; + rv = ParseHTTPSRecord(host, packet, aResult, ttl); + + return NS_SUCCEEDED(rv); +} + +nsresult ParseHTTPSRecord(nsCString& aHost, DNSPacket& aDNSPacket, + TypeRecordResultType& aResult, uint32_t& aTTL) { + nsAutoCString cname; + nsresult rv; + + aDNSPacket.SetNativePacket(true); + + int32_t loopCount = 64; + while (loopCount > 0 && aResult.is<Nothing>()) { + loopCount--; + DOHresp resp; + nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords; + rv = aDNSPacket.Decode(aHost, TRRTYPE_HTTPSSVC, cname, true, resp, aResult, + additionalRecords, aTTL); + if (NS_FAILED(rv)) { + LOG("Decode failed %x", static_cast<uint32_t>(rv)); + return rv; + } + if (!cname.IsEmpty() && aResult.is<Nothing>()) { + aHost = cname; + cname.Truncate(); + continue; + } + } + + if (aResult.is<Nothing>()) { + LOG("Result is nothing"); + // The call succeeded, but no HTTPS records were found. + return NS_ERROR_UNKNOWN_HOST; + } + + return NS_OK; +} + +nsresult ResolveHTTPSRecord(const nsACString& aHost, uint16_t aFlags, + TypeRecordResultType& aResult, uint32_t& aTTL) { + if (gOverrideService) { + return FindHTTPSRecordOverride(aHost, aResult) ? NS_OK + : NS_ERROR_UNKNOWN_HOST; + } + + return ResolveHTTPSRecordImpl(aHost, aFlags, aResult, aTTL); +} + +// static +already_AddRefed<nsINativeDNSResolverOverride> +NativeDNSResolverOverride::GetSingleton() { + if (nsIOService::UseSocketProcess() && XRE_IsParentProcess()) { + return NativeDNSResolverOverrideParent::GetSingleton(); + } + + if (gOverrideService) { + return do_AddRef(gOverrideService); + } + + gOverrideService = new NativeDNSResolverOverride(); + ClearOnShutdown(&gOverrideService); + return do_AddRef(gOverrideService); +} + +NS_IMPL_ISUPPORTS(NativeDNSResolverOverride, nsINativeDNSResolverOverride) + +NS_IMETHODIMP NativeDNSResolverOverride::AddIPOverride( + const nsACString& aHost, const nsACString& aIPLiteral) { + NetAddr tempAddr; + + if (aIPLiteral.Equals("N/A"_ns)) { + AutoWriteLock lock(mLock); + auto& overrides = mOverrides.LookupOrInsert(aHost); + overrides.Clear(); + return NS_OK; + } + + if (NS_FAILED(tempAddr.InitFromString(aIPLiteral))) { + return NS_ERROR_UNEXPECTED; + } + + AutoWriteLock lock(mLock); + auto& overrides = mOverrides.LookupOrInsert(aHost); + overrides.AppendElement(tempAddr); + + return NS_OK; +} + +NS_IMETHODIMP NativeDNSResolverOverride::AddHTTPSRecordOverride( + const nsACString& aHost, const uint8_t* aData, uint32_t aLength) { + AutoWriteLock lock(mLock); + nsTArray<uint8_t> data(aData, aLength); + mHTTPSRecordOverrides.InsertOrUpdate(aHost, std::move(data)); + + return NS_OK; +} + +NS_IMETHODIMP NativeDNSResolverOverride::SetCnameOverride( + const nsACString& aHost, const nsACString& aCNAME) { + if (aCNAME.IsEmpty()) { + return NS_ERROR_UNEXPECTED; + } + + AutoWriteLock lock(mLock); + mCnames.InsertOrUpdate(aHost, nsCString(aCNAME)); + + return NS_OK; +} + +NS_IMETHODIMP NativeDNSResolverOverride::ClearHostOverride( + const nsACString& aHost) { + AutoWriteLock lock(mLock); + mCnames.Remove(aHost); + auto overrides = mOverrides.Extract(aHost); + if (!overrides) { + return NS_OK; + } + + overrides->Clear(); + return NS_OK; +} + +NS_IMETHODIMP NativeDNSResolverOverride::ClearOverrides() { + AutoWriteLock lock(mLock); + mOverrides.Clear(); + mCnames.Clear(); + return NS_OK; +} + +#ifdef MOZ_NO_HTTPS_IMPL + +// If there is no platform specific implementation of ResolveHTTPSRecordImpl +// we link a dummy implementation here. +// Otherwise this is implemented in GetAddrInfoWin/Linux/etc +nsresult ResolveHTTPSRecordImpl(const nsACString& aHost, uint16_t aFlags, + TypeRecordResultType& aResult, uint32_t& aTTL) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +#endif // MOZ_NO_HTTPS_IMPL + +} // namespace mozilla::net diff --git a/netwerk/dns/GetAddrInfo.h b/netwerk/dns/GetAddrInfo.h new file mode 100644 index 0000000000..e8dc095415 --- /dev/null +++ b/netwerk/dns/GetAddrInfo.h @@ -0,0 +1,111 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef netwerk_dns_GetAddrInfo_h +#define netwerk_dns_GetAddrInfo_h + +#include "nsError.h" +#include "nscore.h" +#include "nsINativeDNSResolverOverride.h" +#include "nsHashKeys.h" +#include "nsTHashMap.h" +#include "mozilla/RWLock.h" +#include "nsTArray.h" +#include "prio.h" +#include "mozilla/net/DNS.h" +#include "nsIDNSByTypeRecord.h" +#include "mozilla/Logging.h" + +#if defined(XP_WIN) +# define DNSQUERY_AVAILABLE 1 +#else +# undef DNSQUERY_AVAILABLE +#endif + +namespace mozilla { +namespace net { + +extern LazyLogModule gGetAddrInfoLog; +class AddrInfo; +class DNSPacket; + +/** + * Look up a host by name. Mostly equivalent to getaddrinfo(host, NULL, ...) of + * RFC 3493. + * + * @param aHost[in] Character string defining the host name of interest + * @param aAddressFamily[in] May be AF_INET, AF_INET6, or AF_UNSPEC. + * @param aFlags[in] May be either PR_AI_ADDRCONFIG or + * PR_AI_ADDRCONFIG | PR_AI_NOCANONNAME. Include PR_AI_NOCANONNAME to + * suppress the determination of the canonical name corresponding to + * hostname (PR_AI_NOCANONNAME will be ignored if the TTL is retrieved). + * @param aAddrInfo[out] Will point to the results of the host lookup, or be + * null if the lookup failed. + * @param aGetTtl[in] If true, the TTL will be retrieved if DNS provides the + * answers.. + */ +nsresult GetAddrInfo(const nsACString& aHost, uint16_t aAddressFamily, + uint16_t aFlags, AddrInfo** aAddrInfo, bool aGetTtl); + +/** + * Initialize the GetAddrInfo module. + * + * GetAddrInfoShutdown() should be called for every time this function is + * called. + */ +nsresult GetAddrInfoInit(); + +/** + * Shutdown the GetAddrInfo module. + * + * This function should be called for every time GetAddrInfoInit() is called. + * An assertion may throw (but is not guarenteed) if this function is called + * too many times. + */ +nsresult GetAddrInfoShutdown(); + +/** + * Resolves a HTTPS record. Will check overrides before calling the + * native OS implementation. + */ +nsresult ResolveHTTPSRecord(const nsACString& aHost, uint16_t aFlags, + TypeRecordResultType& aResult, uint32_t& aTTL); + +/** + * The platform specific implementation of HTTPS resolution. + */ +nsresult ResolveHTTPSRecordImpl(const nsACString& aHost, uint16_t aFlags, + TypeRecordResultType& aResult, uint32_t& aTTL); + +nsresult ParseHTTPSRecord(nsCString& aHost, DNSPacket& aDNSPacket, + TypeRecordResultType& aResult, uint32_t& aTTL); + +class NativeDNSResolverOverride : public nsINativeDNSResolverOverride { + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSINATIVEDNSRESOLVEROVERRIDE + public: + NativeDNSResolverOverride() = default; + + static already_AddRefed<nsINativeDNSResolverOverride> GetSingleton(); + + private: + virtual ~NativeDNSResolverOverride() = default; + mozilla::RWLock mLock MOZ_UNANNOTATED{"NativeDNSResolverOverride"}; + + nsTHashMap<nsCStringHashKey, nsTArray<NetAddr>> mOverrides; + nsTHashMap<nsCStringHashKey, nsCString> mCnames; + nsTHashMap<nsCStringHashKey, nsTArray<uint8_t>> mHTTPSRecordOverrides; + + friend bool FindAddrOverride(const nsACString& aHost, uint16_t aAddressFamily, + uint16_t aFlags, AddrInfo** aAddrInfo); + friend bool FindHTTPSRecordOverride(const nsACString& aHost, + TypeRecordResultType& aResult); +}; + +} // namespace net +} // namespace mozilla + +#endif // netwerk_dns_GetAddrInfo_h diff --git a/netwerk/dns/HTTPSSVC.cpp b/netwerk/dns/HTTPSSVC.cpp new file mode 100644 index 0000000000..d36bdf91a2 --- /dev/null +++ b/netwerk/dns/HTTPSSVC.cpp @@ -0,0 +1,523 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "HTTPSSVC.h" +#include "mozilla/net/DNS.h" +#include "nsHttp.h" +#include "nsHttpHandler.h" +#include "nsNetAddr.h" +#include "nsNetUtil.h" +#include "nsIDNSService.h" + +namespace mozilla { +namespace net { + +NS_IMPL_ISUPPORTS(SVCBRecord, nsISVCBRecord) + +class SvcParam : public nsISVCParam, + public nsISVCParamAlpn, + public nsISVCParamNoDefaultAlpn, + public nsISVCParamPort, + public nsISVCParamIPv4Hint, + public nsISVCParamEchConfig, + public nsISVCParamIPv6Hint, + public nsISVCParamODoHConfig { + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSISVCPARAM + NS_DECL_NSISVCPARAMALPN + NS_DECL_NSISVCPARAMNODEFAULTALPN + NS_DECL_NSISVCPARAMPORT + NS_DECL_NSISVCPARAMIPV4HINT + NS_DECL_NSISVCPARAMECHCONFIG + NS_DECL_NSISVCPARAMIPV6HINT + NS_DECL_NSISVCPARAMODOHCONFIG + public: + explicit SvcParam(const SvcParamType& value) : mValue(value){}; + + private: + virtual ~SvcParam() = default; + SvcParamType mValue; +}; + +NS_IMPL_ADDREF(SvcParam) +NS_IMPL_RELEASE(SvcParam) +NS_INTERFACE_MAP_BEGIN(SvcParam) + NS_INTERFACE_MAP_ENTRY(nsISVCParam) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISVCParam) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamAlpn, mValue.is<SvcParamAlpn>()) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamNoDefaultAlpn, + mValue.is<SvcParamNoDefaultAlpn>()) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamPort, mValue.is<SvcParamPort>()) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamIPv4Hint, + mValue.is<SvcParamIpv4Hint>()) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamEchConfig, + mValue.is<SvcParamEchConfig>()) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamIPv6Hint, + mValue.is<SvcParamIpv6Hint>()) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamODoHConfig, + mValue.is<SvcParamODoHConfig>()) +NS_INTERFACE_MAP_END + +NS_IMETHODIMP +SvcParam::GetType(uint16_t* aType) { + *aType = mValue.match( + [](Nothing&) { return SvcParamKeyMandatory; }, + [](SvcParamAlpn&) { return SvcParamKeyAlpn; }, + [](SvcParamNoDefaultAlpn&) { return SvcParamKeyNoDefaultAlpn; }, + [](SvcParamPort&) { return SvcParamKeyPort; }, + [](SvcParamIpv4Hint&) { return SvcParamKeyIpv4Hint; }, + [](SvcParamEchConfig&) { return SvcParamKeyEchConfig; }, + [](SvcParamIpv6Hint&) { return SvcParamKeyIpv6Hint; }, + [](SvcParamODoHConfig&) { return SvcParamKeyODoHConfig; }); + return NS_OK; +} + +NS_IMETHODIMP +SvcParam::GetAlpn(nsTArray<nsCString>& aAlpn) { + if (!mValue.is<SvcParamAlpn>()) { + MOZ_ASSERT(false, "Unexpected type for variant"); + return NS_ERROR_NOT_AVAILABLE; + } + aAlpn.AppendElements(mValue.as<SvcParamAlpn>().mValue); + return NS_OK; +} + +NS_IMETHODIMP +SvcParam::GetPort(uint16_t* aPort) { + if (!mValue.is<SvcParamPort>()) { + MOZ_ASSERT(false, "Unexpected type for variant"); + return NS_ERROR_NOT_AVAILABLE; + } + *aPort = mValue.as<SvcParamPort>().mValue; + return NS_OK; +} + +NS_IMETHODIMP +SvcParam::GetEchconfig(nsACString& aEchConfig) { + if (!mValue.is<SvcParamEchConfig>()) { + MOZ_ASSERT(false, "Unexpected type for variant"); + return NS_ERROR_NOT_AVAILABLE; + } + aEchConfig = mValue.as<SvcParamEchConfig>().mValue; + return NS_OK; +} + +NS_IMETHODIMP +SvcParam::GetIpv4Hint(nsTArray<RefPtr<nsINetAddr>>& aIpv4Hint) { + if (!mValue.is<SvcParamIpv4Hint>()) { + MOZ_ASSERT(false, "Unexpected type for variant"); + return NS_ERROR_NOT_AVAILABLE; + } + const auto& results = mValue.as<SvcParamIpv4Hint>().mValue; + for (const auto& ip : results) { + if (ip.raw.family != AF_INET) { + return NS_ERROR_UNEXPECTED; + } + RefPtr<nsINetAddr> hint = new nsNetAddr(&ip); + aIpv4Hint.AppendElement(hint); + } + + return NS_OK; +} + +NS_IMETHODIMP +SvcParam::GetIpv6Hint(nsTArray<RefPtr<nsINetAddr>>& aIpv6Hint) { + if (!mValue.is<SvcParamIpv6Hint>()) { + MOZ_ASSERT(false, "Unexpected type for variant"); + return NS_ERROR_NOT_AVAILABLE; + } + const auto& results = mValue.as<SvcParamIpv6Hint>().mValue; + for (const auto& ip : results) { + if (ip.raw.family != AF_INET6) { + return NS_ERROR_UNEXPECTED; + } + RefPtr<nsINetAddr> hint = new nsNetAddr(&ip); + aIpv6Hint.AppendElement(hint); + } + return NS_OK; +} + +NS_IMETHODIMP +SvcParam::GetODoHConfig(nsACString& aODoHConfig) { + if (!mValue.is<SvcParamODoHConfig>()) { + MOZ_ASSERT(false, "Unexpected type for variant"); + return NS_ERROR_NOT_AVAILABLE; + } + aODoHConfig = mValue.as<SvcParamODoHConfig>().mValue; + return NS_OK; +} + +bool SVCB::operator<(const SVCB& aOther) const { + if (gHttpHandler->EchConfigEnabled()) { + if (mHasEchConfig && !aOther.mHasEchConfig) { + return true; + } + if (!mHasEchConfig && aOther.mHasEchConfig) { + return false; + } + } + + return mSvcFieldPriority < aOther.mSvcFieldPriority; +} + +Maybe<uint16_t> SVCB::GetPort() const { + Maybe<uint16_t> port; + for (const auto& value : mSvcFieldValue) { + if (value.mValue.is<SvcParamPort>()) { + port.emplace(value.mValue.as<SvcParamPort>().mValue); + if (NS_FAILED(NS_CheckPortSafety(*port, "https"))) { + *port = 0; + } + return port; + } + } + + return Nothing(); +} + +bool SVCB::NoDefaultAlpn() const { + for (const auto& value : mSvcFieldValue) { + if (value.mValue.is<SvcParamKeyNoDefaultAlpn>()) { + return true; + } + } + + return false; +} + +void SVCB::GetIPHints(CopyableTArray<mozilla::net::NetAddr>& aAddresses) const { + if (mSvcFieldPriority == 0) { + return; + } + + for (const auto& value : mSvcFieldValue) { + if (value.mValue.is<SvcParamIpv4Hint>()) { + aAddresses.AppendElements(value.mValue.as<SvcParamIpv4Hint>().mValue); + } else if (value.mValue.is<SvcParamIpv6Hint>()) { + aAddresses.AppendElements(value.mValue.as<SvcParamIpv6Hint>().mValue); + } + } +} + +class AlpnComparator { + public: + bool Equals(const std::tuple<nsCString, SupportedAlpnRank>& aA, + const std::tuple<nsCString, SupportedAlpnRank>& aB) const { + return std::get<1>(aA) == std::get<1>(aB); + } + bool LessThan(const std::tuple<nsCString, SupportedAlpnRank>& aA, + const std::tuple<nsCString, SupportedAlpnRank>& aB) const { + return std::get<1>(aA) > std::get<1>(aB); + } +}; + +nsTArray<std::tuple<nsCString, SupportedAlpnRank>> SVCB::GetAllAlpn() const { + nsTArray<std::tuple<nsCString, SupportedAlpnRank>> alpnList; + for (const auto& value : mSvcFieldValue) { + if (value.mValue.is<SvcParamAlpn>()) { + for (const auto& alpn : value.mValue.as<SvcParamAlpn>().mValue) { + alpnList.AppendElement(std::make_tuple(alpn, IsAlpnSupported(alpn))); + } + } + } + alpnList.Sort(AlpnComparator()); + return alpnList; +} + +SVCBRecord::SVCBRecord(const SVCB& data, + Maybe<std::tuple<nsCString, SupportedAlpnRank>> aAlpn) + : mData(data), mAlpn(aAlpn) { + mPort = mData.GetPort(); +} + +NS_IMETHODIMP SVCBRecord::GetPriority(uint16_t* aPriority) { + *aPriority = mData.mSvcFieldPriority; + return NS_OK; +} + +NS_IMETHODIMP SVCBRecord::GetName(nsACString& aName) { + aName = mData.mSvcDomainName; + return NS_OK; +} + +Maybe<uint16_t> SVCBRecord::GetPort() { return mPort; } + +Maybe<std::tuple<nsCString, SupportedAlpnRank>> SVCBRecord::GetAlpn() { + return mAlpn; +} + +NS_IMETHODIMP SVCBRecord::GetSelectedAlpn(nsACString& aAlpn) { + if (!mAlpn) { + return NS_ERROR_NOT_AVAILABLE; + } + + aAlpn = std::get<0>(*mAlpn); + return NS_OK; +} + +NS_IMETHODIMP SVCBRecord::GetEchConfig(nsACString& aEchConfig) { + aEchConfig = mData.mEchConfig; + return NS_OK; +} + +NS_IMETHODIMP SVCBRecord::GetODoHConfig(nsACString& aODoHConfig) { + aODoHConfig = mData.mODoHConfig; + return NS_OK; +} + +NS_IMETHODIMP SVCBRecord::GetValues(nsTArray<RefPtr<nsISVCParam>>& aValues) { + for (const auto& v : mData.mSvcFieldValue) { + RefPtr<nsISVCParam> param = new SvcParam(v.mValue); + aValues.AppendElement(param); + } + return NS_OK; +} + +NS_IMETHODIMP SVCBRecord::GetHasIPHintAddress(bool* aHasIPHintAddress) { + *aHasIPHintAddress = mData.mHasIPHints; + return NS_OK; +} + +static bool CheckRecordIsUsable(const SVCB& aRecord, nsIDNSService* aDNSService, + const nsACString& aHost, + uint32_t& aExcludedCount) { + if (!aHost.IsEmpty()) { + bool excluded = false; + if (NS_SUCCEEDED(aDNSService->IsSVCDomainNameFailed( + aHost, aRecord.mSvcDomainName, &excluded)) && + excluded) { + // Skip if the domain name of this record was failed to connect before. + ++aExcludedCount; + return false; + } + } + + Maybe<uint16_t> port = aRecord.GetPort(); + if (port && *port == 0) { + // Found an unsafe port, skip this record. + return false; + } + + return true; +} + +static bool CheckAlpnIsUsable(SupportedAlpnRank aAlpnType, bool aNoHttp2, + bool aNoHttp3, bool aCheckHttp3ExcludedList, + const nsACString& aTargetName, + uint32_t& aExcludedCount) { + // Skip if this alpn is not supported. + if (aAlpnType == SupportedAlpnRank::NOT_SUPPORTED) { + return false; + } + + // Skip if we don't want to use http2. + if (aNoHttp2 && aAlpnType == SupportedAlpnRank::HTTP_2) { + return false; + } + + if (IsHttp3(aAlpnType)) { + if (aCheckHttp3ExcludedList && gHttpHandler->IsHttp3Excluded(aTargetName)) { + aExcludedCount++; + return false; + } + + if (aNoHttp3) { + return false; + } + } + + return true; +} + +static nsTArray<SVCBWrapper> FlattenRecords(const nsTArray<SVCB>& aRecords) { + nsTArray<SVCBWrapper> result; + for (const auto& record : aRecords) { + nsTArray<std::tuple<nsCString, SupportedAlpnRank>> alpnList = + record.GetAllAlpn(); + if (alpnList.IsEmpty()) { + result.AppendElement(SVCBWrapper(record)); + } else { + for (const auto& alpn : alpnList) { + SVCBWrapper wrapper(record); + wrapper.mAlpn = Some(alpn); + result.AppendElement(wrapper); + } + } + } + return result; +} + +already_AddRefed<nsISVCBRecord> +DNSHTTPSSVCRecordBase::GetServiceModeRecordInternal( + bool aNoHttp2, bool aNoHttp3, const nsTArray<SVCB>& aRecords, + bool& aRecordsAllExcluded, bool aCheckHttp3ExcludedList) { + RefPtr<SVCBRecord> selectedRecord; + RefPtr<SVCBRecord> h3RecordWithEchConfig; + uint32_t recordHasNoDefaultAlpnCount = 0; + uint32_t recordExcludedCount = 0; + aRecordsAllExcluded = false; + nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID); + uint32_t h3ExcludedCount = 0; + + nsTArray<SVCBWrapper> records = FlattenRecords(aRecords); + for (const auto& record : records) { + if (record.mRecord.mSvcFieldPriority == 0) { + // In ServiceMode, the SvcPriority should never be 0. + return nullptr; + } + + if (record.mRecord.NoDefaultAlpn()) { + ++recordHasNoDefaultAlpnCount; + } + + if (!CheckRecordIsUsable(record.mRecord, dns, mHost, recordExcludedCount)) { + // Skip if this record is not usable. + continue; + } + + if (record.mAlpn) { + if (!CheckAlpnIsUsable(std::get<1>(*(record.mAlpn)), aNoHttp2, aNoHttp3, + aCheckHttp3ExcludedList, + record.mRecord.mSvcDomainName, h3ExcludedCount)) { + continue; + } + + if (IsHttp3(std::get<1>(*(record.mAlpn)))) { + // If the selected alpn is h3 and ech for h3 is disabled, we want + // to find out if there is another non-h3 record that has + // echConfig. If yes, we'll use the non-h3 record with echConfig + // to connect. If not, we'll use h3 to connect without echConfig. + if (record.mRecord.mHasEchConfig && + (gHttpHandler->EchConfigEnabled() && + !gHttpHandler->EchConfigEnabled(true))) { + if (!h3RecordWithEchConfig) { + // Save this h3 record for later use. + h3RecordWithEchConfig = + new SVCBRecord(record.mRecord, record.mAlpn); + // Make sure the next record is not h3. + aNoHttp3 = true; + continue; + } + } + } + } + + if (!selectedRecord) { + selectedRecord = new SVCBRecord(record.mRecord, record.mAlpn); + } + } + + if (!selectedRecord && !h3RecordWithEchConfig) { + // If all records indicate "no-default-alpn", we should not use this RRSet. + if (recordHasNoDefaultAlpnCount == records.Length()) { + return nullptr; + } + + if (recordExcludedCount == records.Length()) { + aRecordsAllExcluded = true; + return nullptr; + } + + // If all records are in http3 excluded list, try again without checking the + // excluded list. This is better than returning nothing. + if (h3ExcludedCount == records.Length() && aCheckHttp3ExcludedList) { + return GetServiceModeRecordInternal(aNoHttp2, aNoHttp3, aRecords, + aRecordsAllExcluded, false); + } + } + + if (h3RecordWithEchConfig) { + if (selectedRecord && selectedRecord->mData.mHasEchConfig) { + return selectedRecord.forget(); + } + + return h3RecordWithEchConfig.forget(); + } + + return selectedRecord.forget(); +} + +void DNSHTTPSSVCRecordBase::GetAllRecordsWithEchConfigInternal( + bool aNoHttp2, bool aNoHttp3, const nsTArray<SVCB>& aRecords, + bool* aAllRecordsHaveEchConfig, bool* aAllRecordsInH3ExcludedList, + nsTArray<RefPtr<nsISVCBRecord>>& aResult, bool aCheckHttp3ExcludedList) { + if (aRecords.IsEmpty()) { + return; + } + + *aAllRecordsHaveEchConfig = aRecords[0].mHasEchConfig; + *aAllRecordsInH3ExcludedList = false; + // The first record should have echConfig. + if (!(*aAllRecordsHaveEchConfig)) { + return; + } + + uint32_t h3ExcludedCount = 0; + nsTArray<SVCBWrapper> records = FlattenRecords(aRecords); + for (const auto& record : records) { + if (record.mRecord.mSvcFieldPriority == 0) { + // This should not happen, since GetAllRecordsWithEchConfigInternal() + // should be called only if GetServiceModeRecordInternal() returns a + // non-null record. + MOZ_ASSERT(false); + return; + } + + // Records with echConfig are in front of records without echConfig, so we + // don't have to continue. + *aAllRecordsHaveEchConfig &= record.mRecord.mHasEchConfig; + if (!(*aAllRecordsHaveEchConfig)) { + aResult.Clear(); + return; + } + + Maybe<uint16_t> port = record.mRecord.GetPort(); + if (port && *port == 0) { + // Found an unsafe port, skip this record. + continue; + } + + if (record.mAlpn) { + if (!CheckAlpnIsUsable(std::get<1>(*(record.mAlpn)), aNoHttp2, aNoHttp3, + aCheckHttp3ExcludedList, + record.mRecord.mSvcDomainName, h3ExcludedCount)) { + continue; + } + } + + RefPtr<nsISVCBRecord> svcbRecord = + new SVCBRecord(record.mRecord, record.mAlpn); + aResult.AppendElement(svcbRecord); + } + + // If all records are in http3 excluded list, try again without checking the + // excluded list. This is better than returning nothing. + if (h3ExcludedCount == records.Length() && aCheckHttp3ExcludedList) { + GetAllRecordsWithEchConfigInternal( + aNoHttp2, aNoHttp3, aRecords, aAllRecordsHaveEchConfig, + aAllRecordsInH3ExcludedList, aResult, false); + *aAllRecordsInH3ExcludedList = true; + } +} + +bool DNSHTTPSSVCRecordBase::HasIPAddressesInternal( + const nsTArray<SVCB>& aRecords) { + for (const SVCB& record : aRecords) { + if (record.mSvcFieldPriority != 0) { + for (const auto& value : record.mSvcFieldValue) { + if (value.mValue.is<SvcParamIpv4Hint>()) { + return true; + } + if (value.mValue.is<SvcParamIpv6Hint>()) { + return true; + } + } + } + } + + return false; +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/HTTPSSVC.h b/netwerk/dns/HTTPSSVC.h new file mode 100644 index 0000000000..792f894a02 --- /dev/null +++ b/netwerk/dns/HTTPSSVC.h @@ -0,0 +1,164 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef HTTPSSVC_h__ +#define HTTPSSVC_h__ + +#include "nsIDNSByTypeRecord.h" +#include "mozilla/net/DNS.h" +#include "mozilla/Variant.h" +#include "mozilla/Maybe.h" +#include "nsHttp.h" + +namespace mozilla { +namespace net { + +class DNSHTTPSSVCRecordBase; + +enum SvcParamKey : uint16_t { + SvcParamKeyMandatory = 0, + SvcParamKeyAlpn = 1, + SvcParamKeyNoDefaultAlpn = 2, + SvcParamKeyPort = 3, + SvcParamKeyIpv4Hint = 4, + SvcParamKeyEchConfig = 5, + SvcParamKeyIpv6Hint = 6, + SvcParamKeyODoHConfig = 32769, +}; + +inline bool IsValidSvcParamKey(uint16_t aKey) { + return aKey <= SvcParamKeyIpv6Hint || aKey == SvcParamKeyODoHConfig; +} + +struct SvcParamAlpn { + bool operator==(const SvcParamAlpn& aOther) const { + return mValue == aOther.mValue; + } + CopyableTArray<nsCString> mValue; +}; + +struct SvcParamNoDefaultAlpn { + bool operator==(const SvcParamNoDefaultAlpn& aOther) const { return true; } +}; + +struct SvcParamPort { + bool operator==(const SvcParamPort& aOther) const { + return mValue == aOther.mValue; + } + uint16_t mValue; +}; + +struct SvcParamIpv4Hint { + bool operator==(const SvcParamIpv4Hint& aOther) const { + return mValue == aOther.mValue; + } + CopyableTArray<mozilla::net::NetAddr> mValue; +}; + +struct SvcParamEchConfig { + bool operator==(const SvcParamEchConfig& aOther) const { + return mValue == aOther.mValue; + } + nsCString mValue; +}; + +struct SvcParamIpv6Hint { + bool operator==(const SvcParamIpv6Hint& aOther) const { + return mValue == aOther.mValue; + } + CopyableTArray<mozilla::net::NetAddr> mValue; +}; + +struct SvcParamODoHConfig { + bool operator==(const SvcParamODoHConfig& aOther) const { + return mValue == aOther.mValue; + } + nsCString mValue; +}; + +using SvcParamType = + mozilla::Variant<Nothing, SvcParamAlpn, SvcParamNoDefaultAlpn, SvcParamPort, + SvcParamIpv4Hint, SvcParamEchConfig, SvcParamIpv6Hint, + SvcParamODoHConfig>; + +struct SvcFieldValue { + bool operator==(const SvcFieldValue& aOther) const { + return mValue == aOther.mValue; + } + SvcFieldValue() : mValue(AsVariant(Nothing{})) {} + SvcParamType mValue; +}; + +struct SVCB { + bool operator==(const SVCB& aOther) const { + return mSvcFieldPriority == aOther.mSvcFieldPriority && + mSvcDomainName == aOther.mSvcDomainName && + mSvcFieldValue == aOther.mSvcFieldValue; + } + bool operator<(const SVCB& aOther) const; + Maybe<uint16_t> GetPort() const; + bool NoDefaultAlpn() const; + void GetIPHints(CopyableTArray<mozilla::net::NetAddr>& aAddresses) const; + nsTArray<std::tuple<nsCString, SupportedAlpnRank>> GetAllAlpn() const; + uint16_t mSvcFieldPriority = 0; + nsCString mSvcDomainName; + nsCString mEchConfig; + nsCString mODoHConfig; + bool mHasIPHints = false; + bool mHasEchConfig = false; + CopyableTArray<SvcFieldValue> mSvcFieldValue; +}; + +struct SVCBWrapper { + explicit SVCBWrapper(const SVCB& aRecord) : mRecord(aRecord) {} + Maybe<std::tuple<nsCString, SupportedAlpnRank>> mAlpn; + const SVCB& mRecord; +}; + +class SVCBRecord : public nsISVCBRecord { + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSISVCBRECORD + public: + explicit SVCBRecord(const SVCB& data) + : mData(data), mPort(Nothing()), mAlpn(Nothing()) {} + explicit SVCBRecord(const SVCB& data, + Maybe<std::tuple<nsCString, SupportedAlpnRank>> aAlpn); + + private: + friend class DNSHTTPSSVCRecordBase; + + virtual ~SVCBRecord() = default; + + SVCB mData; + Maybe<uint16_t> mPort; + Maybe<std::tuple<nsCString, SupportedAlpnRank>> mAlpn; +}; + +class DNSHTTPSSVCRecordBase { + public: + explicit DNSHTTPSSVCRecordBase(const nsACString& aHost) : mHost(aHost) {} + + protected: + virtual ~DNSHTTPSSVCRecordBase() = default; + + already_AddRefed<nsISVCBRecord> GetServiceModeRecordInternal( + bool aNoHttp2, bool aNoHttp3, const nsTArray<SVCB>& aRecords, + bool& aRecordsAllExcluded, bool aCheckHttp3ExcludedList = true); + + bool HasIPAddressesInternal(const nsTArray<SVCB>& aRecords); + + void GetAllRecordsWithEchConfigInternal( + bool aNoHttp2, bool aNoHttp3, const nsTArray<SVCB>& aRecords, + bool* aAllRecordsHaveEchConfig, bool* aAllRecordsInH3ExcludedList, + nsTArray<RefPtr<nsISVCBRecord>>& aResult, + bool aCheckHttp3ExcludedList = true); + + // The owner name of this HTTPS RR. + nsCString mHost; +}; + +} // namespace net +} // namespace mozilla + +#endif // HTTPSSVC_h__ diff --git a/netwerk/dns/HostRecordQueue.cpp b/netwerk/dns/HostRecordQueue.cpp new file mode 100644 index 0000000000..6e8fd5488f --- /dev/null +++ b/netwerk/dns/HostRecordQueue.cpp @@ -0,0 +1,223 @@ +/* vim:set ts=4 sw=2 sts=2 et cin: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "HostRecordQueue.h" +#include "mozilla/Telemetry.h" +#include "nsQueryObject.h" + +namespace mozilla { +namespace net { + +void HostRecordQueue::InsertRecord(nsHostRecord* aRec, + nsIDNSService::DNSFlags aFlags, + const MutexAutoLock& aProofOfLock) { + if (aRec->isInList()) { + MOZ_DIAGNOSTIC_ASSERT(!mEvictionQ.contains(aRec), + "Already in eviction queue"); + MOZ_DIAGNOSTIC_ASSERT(!mHighQ.contains(aRec), "Already in high queue"); + MOZ_DIAGNOSTIC_ASSERT(!mMediumQ.contains(aRec), "Already in med queue"); + MOZ_DIAGNOSTIC_ASSERT(!mLowQ.contains(aRec), "Already in low queue"); + MOZ_DIAGNOSTIC_ASSERT(false, "Already on some other queue?"); + } + + switch (AddrHostRecord::GetPriority(aFlags)) { + case AddrHostRecord::DNS_PRIORITY_HIGH: + mHighQ.insertBack(aRec); + break; + + case AddrHostRecord::DNS_PRIORITY_MEDIUM: + mMediumQ.insertBack(aRec); + break; + + case AddrHostRecord::DNS_PRIORITY_LOW: + mLowQ.insertBack(aRec); + break; + } + mPendingCount++; +} + +void HostRecordQueue::AddToEvictionQ( + nsHostRecord* aRec, uint32_t aMaxCacheEntries, + nsRefPtrHashtable<nsGenericHashKey<nsHostKey>, nsHostRecord>& aDB, + const MutexAutoLock& aProofOfLock) { + if (aRec->isInList()) { + bool inEvictionQ = mEvictionQ.contains(aRec); + MOZ_DIAGNOSTIC_ASSERT(!inEvictionQ, "Already in eviction queue"); + bool inHighQ = mHighQ.contains(aRec); + MOZ_DIAGNOSTIC_ASSERT(!inHighQ, "Already in high queue"); + bool inMediumQ = mMediumQ.contains(aRec); + MOZ_DIAGNOSTIC_ASSERT(!inMediumQ, "Already in med queue"); + bool inLowQ = mLowQ.contains(aRec); + MOZ_DIAGNOSTIC_ASSERT(!inLowQ, "Already in low queue"); + MOZ_DIAGNOSTIC_ASSERT(false, "Already on some other queue?"); + + // Bug 1678117 - it's not clear why this can happen, but let's fix it + // for release users. + aRec->remove(); + if (inEvictionQ) { + MOZ_DIAGNOSTIC_ASSERT(mEvictionQSize > 0); + mEvictionQSize--; + } else if (inHighQ || inMediumQ || inLowQ) { + MOZ_DIAGNOSTIC_ASSERT(mPendingCount > 0); + mPendingCount--; + } + } + mEvictionQ.insertBack(aRec); + if (mEvictionQSize < aMaxCacheEntries) { + mEvictionQSize++; + } else { + // remove first element on mEvictionQ + RefPtr<nsHostRecord> head = mEvictionQ.popFirst(); + aDB.Remove(*static_cast<nsHostKey*>(head.get())); + + if (!head->negative) { + // record the age of the entry upon eviction. + TimeDuration age = TimeStamp::NowLoRes() - head->mValidStart; + if (aRec->IsAddrRecord()) { + Telemetry::Accumulate(Telemetry::DNS_CLEANUP_AGE, + static_cast<uint32_t>(age.ToSeconds() / 60)); + } else { + Telemetry::Accumulate(Telemetry::DNS_BY_TYPE_CLEANUP_AGE, + static_cast<uint32_t>(age.ToSeconds() / 60)); + } + if (head->CheckExpiration(TimeStamp::Now()) != + nsHostRecord::EXP_EXPIRED) { + if (aRec->IsAddrRecord()) { + Telemetry::Accumulate(Telemetry::DNS_PREMATURE_EVICTION, + static_cast<uint32_t>(age.ToSeconds() / 60)); + } else { + Telemetry::Accumulate(Telemetry::DNS_BY_TYPE_PREMATURE_EVICTION, + static_cast<uint32_t>(age.ToSeconds() / 60)); + } + } + } + } +} + +void HostRecordQueue::MaybeRenewHostRecord(nsHostRecord* aRec, + const MutexAutoLock& aProofOfLock) { + if (!aRec->isInList()) { + return; + } + + bool inEvictionQ = mEvictionQ.contains(aRec); + MOZ_DIAGNOSTIC_ASSERT(inEvictionQ, "Should be in eviction queue"); + bool inHighQ = mHighQ.contains(aRec); + MOZ_DIAGNOSTIC_ASSERT(!inHighQ, "Already in high queue"); + bool inMediumQ = mMediumQ.contains(aRec); + MOZ_DIAGNOSTIC_ASSERT(!inMediumQ, "Already in med queue"); + bool inLowQ = mLowQ.contains(aRec); + MOZ_DIAGNOSTIC_ASSERT(!inLowQ, "Already in low queue"); + + // we're already on the eviction queue. This is a renewal + aRec->remove(); + if (inEvictionQ) { + MOZ_DIAGNOSTIC_ASSERT(mEvictionQSize > 0); + mEvictionQSize--; + } else if (inHighQ || inMediumQ || inLowQ) { + MOZ_DIAGNOSTIC_ASSERT(mPendingCount > 0); + mPendingCount--; + } +} + +void HostRecordQueue::FlushEvictionQ( + nsRefPtrHashtable<nsGenericHashKey<nsHostKey>, nsHostRecord>& aDB, + const MutexAutoLock& aProofOfLock) { + mEvictionQSize = 0; + + // Clear the evictionQ and remove all its corresponding entries from + // the cache first + if (!mEvictionQ.isEmpty()) { + for (const RefPtr<nsHostRecord>& rec : mEvictionQ) { + rec->Cancel(); + aDB.Remove(*static_cast<nsHostKey*>(rec)); + } + mEvictionQ.clear(); + } +} + +void HostRecordQueue::MaybeRemoveFromQ(nsHostRecord* aRec, + const MutexAutoLock& aProofOfLock) { + if (!aRec->isInList()) { + return; + } + + if (mHighQ.contains(aRec) || mMediumQ.contains(aRec) || + mLowQ.contains(aRec)) { + mPendingCount--; + } else if (mEvictionQ.contains(aRec)) { + mEvictionQSize--; + } else { + MOZ_ASSERT(false, "record is in other queue"); + } + + aRec->remove(); +} + +void HostRecordQueue::MoveToAnotherPendingQ(nsHostRecord* aRec, + nsIDNSService::DNSFlags aFlags, + const MutexAutoLock& aProofOfLock) { + if (!(mHighQ.contains(aRec) || mMediumQ.contains(aRec) || + mLowQ.contains(aRec))) { + MOZ_ASSERT(false, "record is not in the pending queue"); + return; + } + + aRec->remove(); + InsertRecord(aRec, aFlags, aProofOfLock); +} + +already_AddRefed<nsHostRecord> HostRecordQueue::Dequeue( + bool aHighQOnly, const MutexAutoLock& aProofOfLock) { + RefPtr<nsHostRecord> rec; + if (!mHighQ.isEmpty()) { + rec = mHighQ.popFirst(); + } else if (!mMediumQ.isEmpty() && !aHighQOnly) { + rec = mMediumQ.popFirst(); + } else if (!mLowQ.isEmpty() && !aHighQOnly) { + rec = mLowQ.popFirst(); + } + + if (rec) { + mPendingCount--; + } + + return rec.forget(); +} + +void HostRecordQueue::ClearAll( + const std::function<void(nsHostRecord*)>& aCallback, + const MutexAutoLock& aProofOfLock) { + mPendingCount = 0; + + auto clearPendingQ = [&](LinkedList<RefPtr<nsHostRecord>>& aPendingQ) { + if (aPendingQ.isEmpty()) { + return; + } + + // loop through pending queue, erroring out pending lookups. + for (const RefPtr<nsHostRecord>& rec : aPendingQ) { + rec->Cancel(); + aCallback(rec); + } + aPendingQ.clear(); + }; + + clearPendingQ(mHighQ); + clearPendingQ(mMediumQ); + clearPendingQ(mLowQ); + + mEvictionQSize = 0; + if (!mEvictionQ.isEmpty()) { + for (const RefPtr<nsHostRecord>& rec : mEvictionQ) { + rec->Cancel(); + } + } + + mEvictionQ.clear(); +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/HostRecordQueue.h b/netwerk/dns/HostRecordQueue.h new file mode 100644 index 0000000000..dfa56d1c88 --- /dev/null +++ b/netwerk/dns/HostRecordQueue.h @@ -0,0 +1,72 @@ +/* vim:set ts=4 sw=2 sts=2 et cin: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef HostRecordQueue_h__ +#define HostRecordQueue_h__ + +#include <functional> +#include "mozilla/Mutex.h" +#include "nsHostRecord.h" +#include "nsRefPtrHashtable.h" + +namespace mozilla { +namespace net { + +class HostRecordQueue final { + public: + HostRecordQueue() = default; + ~HostRecordQueue() = default; + HostRecordQueue(const HostRecordQueue& aCopy) = delete; + HostRecordQueue& operator=(const HostRecordQueue& aCopy) = delete; + + uint32_t PendingCount() const { return mPendingCount; } + uint32_t EvictionQSize() const { return mEvictionQSize; } + + // Insert the record to mHighQ or mMediumQ or mLowQ based on the record's + // priority. + void InsertRecord(nsHostRecord* aRec, nsIDNSService::DNSFlags aFlags, + const MutexAutoLock& aProofOfLock); + // Insert the record to mEvictionQ. In theory, this function should be called + // when the record is not in any queue. + void AddToEvictionQ( + nsHostRecord* aRec, uint32_t aMaxCacheEntries, + nsRefPtrHashtable<nsGenericHashKey<nsHostKey>, nsHostRecord>& aDB, + const MutexAutoLock& aProofOfLock); + // Called for removing the record from mEvictionQ. When this function is + // called, the record should be either in mEvictionQ or not in any queue. + void MaybeRenewHostRecord(nsHostRecord* aRec, + const MutexAutoLock& aProofOfLock); + // Called for clearing mEvictionQ. + void FlushEvictionQ( + nsRefPtrHashtable<nsGenericHashKey<nsHostKey>, nsHostRecord>& aDB, + const MutexAutoLock& aProofOfLock); + // Remove the record from the queue that contains it. + void MaybeRemoveFromQ(nsHostRecord* aRec, const MutexAutoLock& aProofOfLock); + // When the record's priority changes, move the record between pending queues. + void MoveToAnotherPendingQ(nsHostRecord* aRec, nsIDNSService::DNSFlags aFlags, + const MutexAutoLock& aProofOfLock); + // Returning the first record from one of the pending queue. When |aHighQOnly| + // is true, returning the record from mHighQ only. When false, return the + // record from mMediumQ or mLowQ. + already_AddRefed<nsHostRecord> Dequeue(bool aHighQOnly, + const MutexAutoLock& aProofOfLock); + // Clear all queues and is called only during shutdown. |aCallback| is invoked + // when a record is removed from a queue. + void ClearAll(const std::function<void(nsHostRecord*)>& aCallback, + const MutexAutoLock& aProofOfLock); + + private: + Atomic<uint32_t> mPendingCount{0}; + Atomic<uint32_t> mEvictionQSize{0}; + LinkedList<RefPtr<nsHostRecord>> mHighQ; + LinkedList<RefPtr<nsHostRecord>> mMediumQ; + LinkedList<RefPtr<nsHostRecord>> mLowQ; + LinkedList<RefPtr<nsHostRecord>> mEvictionQ; +}; + +} // namespace net +} // namespace mozilla + +#endif // HostRecordQueue_h__ diff --git a/netwerk/dns/IDNBlocklistUtils.cpp b/netwerk/dns/IDNBlocklistUtils.cpp new file mode 100644 index 0000000000..92c73d0997 --- /dev/null +++ b/netwerk/dns/IDNBlocklistUtils.cpp @@ -0,0 +1,86 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "IDNBlocklistUtils.h" + +#include "mozilla/Preferences.h" +#include "nsStringFwd.h" + +namespace mozilla { +namespace net { + +static constexpr char16_t sBlocklistPairs[][2] = { +#include "IDNCharacterBlocklist.inc" +}; + +void RemoveCharFromBlocklist(char16_t aChar, + nsTArray<BlocklistRange>& aBlocklist) { + auto pos = aBlocklist.BinaryIndexOf(aChar, BlocklistPairToCharComparator()); + if (pos == nsTArray<BlocklistRange>::NoIndex) { + return; + } + + auto& pair = aBlocklist[pos]; + + // If the matched range has a length of one, we can just remove it + if (pair.second == pair.first) { + aBlocklist.RemoveElementAt(pos); + return; + } + + // If the character matches the first element in the range, just update + // the range. + if (aChar == pair.first) { + pair.first = pair.first + 1; + return; + } + + // Also if it matches the last character in the range, we just update it. + if (aChar == pair.second) { + pair.second = pair.second - 1; + return; + } + + // Our character is in the middle of the range, splitting it in two. + // We update the matched range to reflect the values before the character, + // and insert a new range that represents the values after. + char16_t lastElement = pair.second; + pair.second = aChar - 1; + aBlocklist.InsertElementAt(pos + 1, + std::make_pair(char16_t(aChar + 1), lastElement)); +} + +void InitializeBlocklist(nsTArray<BlocklistRange>& aBlocklist) { + aBlocklist.Clear(); + for (auto const& arr : sBlocklistPairs) { + // The hardcoded pairs are already sorted. + aBlocklist.AppendElement(std::make_pair(arr[0], arr[1])); + } + + nsAutoString extraAllowed; + nsresult rv = + Preferences::GetString("network.IDN.extra_allowed_chars", extraAllowed); + if (NS_SUCCEEDED(rv) && !extraAllowed.IsEmpty()) { + const char16_t* cur = extraAllowed.BeginReading(); + const char16_t* end = extraAllowed.EndReading(); + // Characters in the allowed list are removed from the blocklist. + for (; cur < end; ++cur) { + RemoveCharFromBlocklist(*cur, aBlocklist); + } + } + + nsAutoString extraBlocked; + rv = Preferences::GetString("network.IDN.extra_blocked_chars", extraBlocked); + // We add each extra blocked character to the blocklist as a separate range. + if (NS_SUCCEEDED(rv) && !extraBlocked.IsEmpty()) { + for (size_t i = 0; i < extraBlocked.Length(); ++i) { + aBlocklist.AppendElement( + std::make_pair(extraBlocked[i], extraBlocked[i])); + } + aBlocklist.Sort(BlocklistEntryComparator()); + } +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/IDNBlocklistUtils.h b/netwerk/dns/IDNBlocklistUtils.h new file mode 100644 index 0000000000..6e1c6c0a77 --- /dev/null +++ b/netwerk/dns/IDNBlocklistUtils.h @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef IDNBlocklistUtils_h__ +#define IDNBlocklistUtils_h__ + +#include <utility> +#include "nsTArray.h" + +namespace mozilla { +namespace net { + +// A blocklist range is defined as all of the characters between: +// { firstCharacterInRange, lastCharacterInRange } +using BlocklistRange = std::pair<char16_t, char16_t>; + +// Used to perform a binary search of the needle in the sorted array of pairs +class BlocklistPairToCharComparator { + public: + bool Equals(const BlocklistRange& pair, char16_t needle) const { + // If the needle is between pair.first and pair.second it + // is part of the range. + return pair.first <= needle && needle <= pair.second; + } + + bool LessThan(const BlocklistRange& pair, char16_t needle) const { + // The needle has to be larger than the second value, + // otherwise it may be equal. + return pair.second < needle; + } +}; + +// Used to sort the array of pairs +class BlocklistEntryComparator { + public: + bool Equals(const BlocklistRange& a, const BlocklistRange& b) const { + return a.first == b.first && a.second == b.second; + } + + bool LessThan(const BlocklistRange& a, const BlocklistRange& b) const { + return a.first < b.first; + } +}; + +// Returns true if the char can be found in the blocklist +inline bool CharInBlocklist(char16_t aChar, + const nsTArray<BlocklistRange>& aBlocklist) { + return aBlocklist.ContainsSorted(aChar, BlocklistPairToCharComparator()); +} + +// Initializes the blocklist based on the statically defined list and the +// values of the following preferences: +// - network.IDN.extra_allowed_chars +// - network.IDN.extra_blocked_chars +void InitializeBlocklist(nsTArray<BlocklistRange>& aBlocklist); + +void RemoveCharFromBlocklist(char16_t aChar, + nsTArray<BlocklistRange>& aBlocklist); + +} // namespace net +} // namespace mozilla + +#endif // IDNBlocklistUtils_h__ diff --git a/netwerk/dns/IDNCharacterBlocklist.inc b/netwerk/dns/IDNCharacterBlocklist.inc new file mode 100644 index 0000000000..754c4f9518 --- /dev/null +++ b/netwerk/dns/IDNCharacterBlocklist.inc @@ -0,0 +1,63 @@ +// This file contains the IDN character blocklist. +// Each entry represents a range of blocked characters. +// Ranges are defined as: +// { firstCharacterInRange, lastCharacterInRange } +// IMPORTANT: Make sure this list is sorted in ascending order + + +// ASCII Space +{ 0x0020, 0x0020 }, +{ 0x00A0, 0x00A0 }, +{ 0x00BC, 0x00BE }, +{ 0x0138, 0x0138 }, +{ 0x01C3, 0x01C3 }, +{ 0x02D0, 0x02D0 }, +{ 0x0337, 0x0338 }, +{ 0x0589, 0x058A }, +{ 0x05C3, 0x05C3 }, +{ 0x05F4, 0x05F4 }, +{ 0x0609, 0x060A }, +{ 0x066A, 0x066A }, +{ 0x06D4, 0x06D4 }, +{ 0x0701, 0x0704 }, +{ 0x115F, 0x1160 }, +{ 0x1735, 0x1735 }, +{ 0x2000, 0x200B }, +{ 0x200E, 0x2010 }, +{ 0x2019, 0x2019 }, +{ 0x2024, 0x2024 }, +{ 0x2027, 0x202F }, +{ 0x2039, 0x203A }, +{ 0x2041, 0x2041 }, +{ 0x2044, 0x2044 }, +{ 0x2052, 0x2052 }, +{ 0x205F, 0x205F }, +{ 0x2153, 0x215F }, +{ 0x2215, 0x2215 }, +{ 0x2236, 0x2236 }, +{ 0x23AE, 0x23AE }, +{ 0x2571, 0x2571 }, +{ 0x29F6, 0x29F6 }, +{ 0x29F8, 0x29F8 }, +{ 0x2AFB, 0x2AFB }, +{ 0x2AFD, 0x2AFD }, +{ 0x2FF0, 0x2FFB }, +// Ideographic Space +{ 0x3000, 0x3000 }, +{ 0x3002, 0x3002 }, +{ 0x3014, 0x3015 }, +{ 0x3033, 0x3033 }, +{ 0x30A0, 0x30A0 }, +{ 0x3164, 0x3164 }, +{ 0x321D, 0x321E }, +{ 0x33AE, 0x33AF }, +{ 0x33C6, 0x33C6 }, +{ 0x33DF, 0x33DF }, +{ 0xFE14, 0xFE15 }, +{ 0xFE3F, 0xFE3F }, +{ 0xFE5D, 0xFE5E }, +{ 0xFEFF, 0xFEFF }, +{ 0xFF0E, 0xFF0F }, +{ 0xFF61, 0xFF61 }, +{ 0xFFA0, 0xFFA0 }, +{ 0xFFF9, 0xFFFD }, diff --git a/netwerk/dns/NativeDNSResolverOverrideChild.cpp b/netwerk/dns/NativeDNSResolverOverrideChild.cpp new file mode 100644 index 0000000000..b52e929ab3 --- /dev/null +++ b/netwerk/dns/NativeDNSResolverOverrideChild.cpp @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "NativeDNSResolverOverrideChild.h" +#include "GetAddrInfo.h" + +namespace mozilla { +namespace net { + +NativeDNSResolverOverrideChild::NativeDNSResolverOverrideChild() { + mOverrideService = NativeDNSResolverOverride::GetSingleton(); +} + +mozilla::ipc::IPCResult NativeDNSResolverOverrideChild::RecvAddIPOverride( + const nsCString& aHost, const nsCString& aIPLiteral) { + Unused << mOverrideService->AddIPOverride(aHost, aIPLiteral); + return IPC_OK(); +} + +mozilla::ipc::IPCResult +NativeDNSResolverOverrideChild::RecvAddHTTPSRecordOverride( + const nsCString& aHost, nsTArray<uint8_t>&& aData) { + Unused << mOverrideService->AddHTTPSRecordOverride(aHost, aData.Elements(), + aData.Length()); + return IPC_OK(); +} + +mozilla::ipc::IPCResult NativeDNSResolverOverrideChild::RecvSetCnameOverride( + const nsCString& aHost, const nsCString& aCNAME) { + Unused << mOverrideService->SetCnameOverride(aHost, aCNAME); + return IPC_OK(); +} + +mozilla::ipc::IPCResult NativeDNSResolverOverrideChild::RecvClearHostOverride( + const nsCString& aHost) { + Unused << mOverrideService->ClearHostOverride(aHost); + return IPC_OK(); +} + +mozilla::ipc::IPCResult NativeDNSResolverOverrideChild::RecvClearOverrides() { + Unused << mOverrideService->ClearOverrides(); + return IPC_OK(); +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/NativeDNSResolverOverrideChild.h b/netwerk/dns/NativeDNSResolverOverrideChild.h new file mode 100644 index 0000000000..42fcc0b7c8 --- /dev/null +++ b/netwerk/dns/NativeDNSResolverOverrideChild.h @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_NativeDNSResolverOverrideChild_h +#define mozilla_net_NativeDNSResolverOverrideChild_h + +#include "mozilla/net/PNativeDNSResolverOverrideChild.h" +#include "nsINativeDNSResolverOverride.h" + +namespace mozilla { +namespace net { + +class NativeDNSResolverOverrideChild : public PNativeDNSResolverOverrideChild { + public: + NS_INLINE_DECL_REFCOUNTING(NativeDNSResolverOverrideChild, override) + + NativeDNSResolverOverrideChild(); + + mozilla::ipc::IPCResult RecvAddIPOverride(const nsCString& aHost, + const nsCString& aIPLiteral); + mozilla::ipc::IPCResult RecvAddHTTPSRecordOverride(const nsCString& aHost, + nsTArray<uint8_t>&& aData); + mozilla::ipc::IPCResult RecvSetCnameOverride(const nsCString& aHost, + const nsCString& aCNAME); + mozilla::ipc::IPCResult RecvClearHostOverride(const nsCString& aHost); + mozilla::ipc::IPCResult RecvClearOverrides(); + + private: + virtual ~NativeDNSResolverOverrideChild() = default; + + nsCOMPtr<nsINativeDNSResolverOverride> mOverrideService; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_NativeDNSResolverOverrideChild_h diff --git a/netwerk/dns/NativeDNSResolverOverrideParent.cpp b/netwerk/dns/NativeDNSResolverOverrideParent.cpp new file mode 100644 index 0000000000..47ba16918f --- /dev/null +++ b/netwerk/dns/NativeDNSResolverOverrideParent.cpp @@ -0,0 +1,111 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "NativeDNSResolverOverrideParent.h" + +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/net/SocketProcessParent.h" +#include "nsIOService.h" +#include "DNS.h" + +namespace mozilla { +namespace net { + +NS_IMPL_ISUPPORTS(NativeDNSResolverOverrideParent, nsINativeDNSResolverOverride) + +static StaticRefPtr<NativeDNSResolverOverrideParent> + gNativeDNSResolverOverrideParent; + +// static +already_AddRefed<nsINativeDNSResolverOverride> +NativeDNSResolverOverrideParent::GetSingleton() { + if (gNativeDNSResolverOverrideParent) { + return do_AddRef(gNativeDNSResolverOverrideParent); + } + + if (!gIOService) { + return nullptr; + } + + gNativeDNSResolverOverrideParent = new NativeDNSResolverOverrideParent(); + ClearOnShutdown(&gNativeDNSResolverOverrideParent); + + auto initTask = []() { + Unused << SocketProcessParent::GetSingleton() + ->SendPNativeDNSResolverOverrideConstructor( + gNativeDNSResolverOverrideParent); + }; + gIOService->CallOrWaitForSocketProcess(initTask); + return do_AddRef(gNativeDNSResolverOverrideParent); +} + +NS_IMETHODIMP NativeDNSResolverOverrideParent::AddIPOverride( + const nsACString& aHost, const nsACString& aIPLiteral) { + NetAddr tempAddr; + if (!aIPLiteral.Equals("N/A"_ns) && + NS_FAILED(tempAddr.InitFromString(aIPLiteral))) { + return NS_ERROR_UNEXPECTED; + } + + RefPtr<NativeDNSResolverOverrideParent> self = this; + nsCString host(aHost); + nsCString ip(aIPLiteral); + auto task = [self{std::move(self)}, host, ip]() { + Unused << self->SendAddIPOverride(host, ip); + }; + gIOService->CallOrWaitForSocketProcess(task); + return NS_OK; +} + +NS_IMETHODIMP NativeDNSResolverOverrideParent::AddHTTPSRecordOverride( + const nsACString& aHost, const uint8_t* aData, uint32_t aLength) { + nsCString host(aHost); + CopyableTArray<uint8_t> data(aData, aLength); + auto task = [self = RefPtr{this}, host, data = std::move(data)]() { + Unused << self->SendAddHTTPSRecordOverride(host, data); + }; + gIOService->CallOrWaitForSocketProcess(std::move(task)); + return NS_OK; +} + +NS_IMETHODIMP NativeDNSResolverOverrideParent::SetCnameOverride( + const nsACString& aHost, const nsACString& aCNAME) { + if (aCNAME.IsEmpty()) { + return NS_ERROR_UNEXPECTED; + } + + RefPtr<NativeDNSResolverOverrideParent> self = this; + nsCString host(aHost); + nsCString cname(aCNAME); + auto task = [self{std::move(self)}, host, cname]() { + Unused << self->SendSetCnameOverride(host, cname); + }; + gIOService->CallOrWaitForSocketProcess(task); + return NS_OK; +} + +NS_IMETHODIMP NativeDNSResolverOverrideParent::ClearHostOverride( + const nsACString& aHost) { + RefPtr<NativeDNSResolverOverrideParent> self = this; + nsCString host(aHost); + auto task = [self{std::move(self)}, host]() { + Unused << self->SendClearHostOverride(host); + }; + gIOService->CallOrWaitForSocketProcess(task); + return NS_OK; +} + +NS_IMETHODIMP NativeDNSResolverOverrideParent::ClearOverrides() { + RefPtr<NativeDNSResolverOverrideParent> self = this; + auto task = [self{std::move(self)}]() { + Unused << self->SendClearOverrides(); + }; + gIOService->CallOrWaitForSocketProcess(task); + return NS_OK; +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/NativeDNSResolverOverrideParent.h b/netwerk/dns/NativeDNSResolverOverrideParent.h new file mode 100644 index 0000000000..b0cb5e4d6d --- /dev/null +++ b/netwerk/dns/NativeDNSResolverOverrideParent.h @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_NativeDNSResolverOverrideParent_h +#define mozilla_net_NativeDNSResolverOverrideParent_h + +#include "mozilla/net/PNativeDNSResolverOverrideParent.h" +#include "nsINativeDNSResolverOverride.h" + +namespace mozilla { +namespace net { + +class NativeDNSResolverOverrideParent : public PNativeDNSResolverOverrideParent, + public nsINativeDNSResolverOverride { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSINATIVEDNSRESOLVEROVERRIDE + + explicit NativeDNSResolverOverrideParent() = default; + static already_AddRefed<nsINativeDNSResolverOverride> GetSingleton(); + + private: + virtual ~NativeDNSResolverOverrideParent() = default; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_NativeDNSResolverOverrideParent_h diff --git a/netwerk/dns/PDNSRequest.ipdl b/netwerk/dns/PDNSRequest.ipdl new file mode 100644 index 0000000000..0613ff67b4 --- /dev/null +++ b/netwerk/dns/PDNSRequest.ipdl @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PNecko; +include protocol PSocketProcess; + +include PDNSRequestParams; + +include "mozilla/net/NeckoMessageUtils.h"; + +using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h"; +using nsIDNSService::DNSFlags from "nsIDNSService.h"; + +namespace mozilla { +namespace net { + +async protocol PDNSRequest +{ + manager PNecko or PSocketProcess; + +both: + // constructor in PNecko takes AsyncResolve args that initialize request + + // Pass args here rather than storing them in the parent; they are only + // needed if the request is to be canceled. + async CancelDNSRequest(nsCString hostName, nsCString trrServer, int32_t port, + uint16_t type, OriginAttributes originAttributes, + DNSFlags flags, nsresult reason); + async __delete__(); + + async LookupCompleted(DNSRequestResponse reply); + +}; + +} //namespace net +} //namespace mozilla diff --git a/netwerk/dns/PDNSRequestParams.ipdlh b/netwerk/dns/PDNSRequestParams.ipdlh new file mode 100644 index 0000000000..de15e91a59 --- /dev/null +++ b/netwerk/dns/PDNSRequestParams.ipdlh @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +using mozilla::net::NetAddr from "mozilla/net/DNS.h"; +using mozilla::net::IPCTypeRecord from "mozilla/net/DNSByTypeRecord.h"; +using nsIRequest::TRRMode from "nsIRequest.h"; + +namespace mozilla { +namespace net { + +//----------------------------------------------------------------------------- +// DNS IPDL structs +//----------------------------------------------------------------------------- + +struct DNSRecord +{ + nsCString canonicalName; + NetAddr[] addrs; + double trrFetchDuration; + double trrFetchDurationNetworkOnly; + bool isTRR; + TRRMode effectiveTRRMode; + uint32_t ttl; +}; + +union DNSRequestResponse +{ + DNSRecord; + IPCTypeRecord; // The result of a by-type query + nsresult; // if error +}; + + +} // namespace ipc +} // namespace mozilla diff --git a/netwerk/dns/PNativeDNSResolverOverride.ipdl b/netwerk/dns/PNativeDNSResolverOverride.ipdl new file mode 100644 index 0000000000..a1cd4c9ff5 --- /dev/null +++ b/netwerk/dns/PNativeDNSResolverOverride.ipdl @@ -0,0 +1,26 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PSocketProcess; + +namespace mozilla { +namespace net { + +async protocol PNativeDNSResolverOverride +{ + manager PSocketProcess; + +child: + async __delete__(); + async AddIPOverride(nsCString aHost, nsCString aIPLiteral); + async AddHTTPSRecordOverride(nsCString aHost, uint8_t[] aData); + async SetCnameOverride(nsCString aHost, nsCString aCNAME); + async ClearHostOverride(nsCString aHost); + async ClearOverrides(); +}; + +} //namespace net +} //namespace mozilla diff --git a/netwerk/dns/PTRRService.ipdl b/netwerk/dns/PTRRService.ipdl new file mode 100644 index 0000000000..14059e033f --- /dev/null +++ b/netwerk/dns/PTRRService.ipdl @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PSocketProcess; + +include NeckoChannelParams; +include PSMIPCTypes; + +namespace mozilla { +namespace net { + +async protocol PTRRService +{ + manager PSocketProcess; + +parent: + async NotifyNetworkConnectivityServiceObservers(nsCString aTopic); + async InitTRRConnectionInfo(); + async SetConfirmationState(uint32_t aNewState); + +child: + async __delete__(); + async UpdatePlatformDNSInformation(nsCString[] aSuffixList); + async UpdateParentalControlEnabled(bool aEnabled); + async ClearDNSCache(bool aTrrToo); + async SetDetectedTrrURI(nsCString aURI); + async SetDefaultTRRConnectionInfo(HttpConnectionInfoCloneArgs? aConnInfoArgs); + async UpdateEtcHosts(nsCString[] aHosts); +}; + +} //namespace net +} //namespace mozilla diff --git a/netwerk/dns/PlatformDNSAndroid.cpp b/netwerk/dns/PlatformDNSAndroid.cpp new file mode 100644 index 0000000000..78a944777f --- /dev/null +++ b/netwerk/dns/PlatformDNSAndroid.cpp @@ -0,0 +1,154 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=4 sw=2 sts=2 et cin: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "GetAddrInfo.h" +#include "mozilla/net/DNSPacket.h" +#include "nsIDNSService.h" +#include "mozilla/Maybe.h" +#include "mozilla/Atomics.h" +#include "mozilla/StaticPrefs_network.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netinet/in.h> +#include <resolv.h> +#include <poll.h> +#include <dlfcn.h> +#include <android/multinetwork.h> + +namespace mozilla::net { + +// The first call to ResolveHTTPSRecordImpl will load the library +// and function pointers. +static Atomic<bool> sLibLoading{false}; + +// https://developer.android.com/ndk/reference/group/networking#android_res_nquery +// The function android_res_nquery is defined in <android/multinetwork.h> +typedef int (*android_res_nquery_ptr)(net_handle_t network, const char* dname, + int ns_class, int ns_type, + uint32_t flags); +static Atomic<android_res_nquery_ptr> sAndroidResNQuery; + +#define LOG(msg, ...) \ + MOZ_LOG(gGetAddrInfoLog, LogLevel::Debug, ("[DNS]: " msg, ##__VA_ARGS__)) + +nsresult ResolveHTTPSRecordImpl(const nsACString& aHost, uint16_t aFlags, + TypeRecordResultType& aResult, uint32_t& aTTL) { + DNSPacket packet; + nsAutoCString host(aHost); + nsAutoCString cname; + nsresult rv; + + if (xpc::IsInAutomation() && + !StaticPrefs::network_dns_native_https_query_in_automation()) { + return NS_ERROR_UNKNOWN_HOST; + } + + if (!sLibLoading.exchange(true)) { + // We're the first call here, load the library and symbols. + void* handle = dlopen("libandroid.so", RTLD_LAZY | RTLD_LOCAL); + if (!handle) { + LOG("Error loading libandroid_net %s", dlerror()); + return NS_ERROR_UNKNOWN_HOST; + } + + auto x = dlsym(handle, "android_res_nquery"); + if (!x) { + LOG("No android_res_nquery symbol"); + } + + sAndroidResNQuery = (android_res_nquery_ptr)x; + } + + if (!sAndroidResNQuery) { + LOG("nquery not loaded"); + // The library hasn't been loaded yet. + return NS_ERROR_UNKNOWN_HOST; + } + + LOG("resolving %s\n", host.get()); + // Perform the query + rv = packet.FillBuffer( + [&](unsigned char response[DNSPacket::MAX_SIZE]) -> int { + int fd = 0; + auto closeSocket = MakeScopeExit([&] { + if (fd > 0) { + close(fd); + } + }); + uint32_t flags = 0; + if (aFlags & nsIDNSService::RESOLVE_BYPASS_CACHE) { + flags = ANDROID_RESOLV_NO_CACHE_LOOKUP; + } + fd = sAndroidResNQuery(0, host.get(), ns_c_in, + nsIDNSService::RESOLVE_TYPE_HTTPSSVC, flags); + + if (fd < 0) { + LOG("DNS query failed"); + return fd; + } + + struct pollfd fds; + fds.fd = fd; + fds.events = POLLIN; // Wait for read events + + // Wait for an event on the file descriptor + int ret = poll(&fds, 1, + StaticPrefs::network_dns_native_https_timeout_android()); + if (ret <= 0) { + LOG("poll failed %d", ret); + return -1; + } + + ssize_t len = recv(fd, response, DNSPacket::MAX_SIZE - 1, 0); + if (len <= 8) { + LOG("size too small %zd", len); + return len < 0 ? len : -1; + } + + // The first 8 bytes are UDP header. + // XXX: we should consider avoiding this move somehow. + for (int i = 0; i < len - 8; i++) { + response[i] = response[i + 8]; + } + + return len - 8; + }); + if (NS_FAILED(rv)) { + LOG("failed rv"); + return rv; + } + packet.SetNativePacket(true); + + int32_t loopCount = 64; + while (loopCount > 0 && aResult.is<Nothing>()) { + loopCount--; + DOHresp resp; + nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords; + rv = packet.Decode(host, TRRTYPE_HTTPSSVC, cname, true, resp, aResult, + additionalRecords, aTTL); + if (NS_FAILED(rv)) { + LOG("Decode failed %x", static_cast<uint32_t>(rv)); + return rv; + } + if (!cname.IsEmpty() && aResult.is<Nothing>()) { + host = cname; + cname.Truncate(); + continue; + } + } + + if (aResult.is<Nothing>()) { + LOG("Result is nothing"); + // The call succeeded, but no HTTPS records were found. + return NS_ERROR_UNKNOWN_HOST; + } + + return NS_OK; +} + +} // namespace mozilla::net diff --git a/netwerk/dns/PlatformDNSUnix.cpp b/netwerk/dns/PlatformDNSUnix.cpp new file mode 100644 index 0000000000..dedbe337df --- /dev/null +++ b/netwerk/dns/PlatformDNSUnix.cpp @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=4 sw=2 sts=2 et cin: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "GetAddrInfo.h" +#include "mozilla/net/DNSPacket.h" +#include "nsIDNSService.h" +#include "mozilla/Maybe.h" +#include "mozilla/StaticPrefs_network.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netinet/in.h> +#include <resolv.h> + +namespace mozilla::net { + +#define LOG(msg, ...) \ + MOZ_LOG(gGetAddrInfoLog, LogLevel::Debug, ("[DNS]: " msg, ##__VA_ARGS__)) + +nsresult ResolveHTTPSRecordImpl(const nsACString& aHost, uint16_t aFlags, + TypeRecordResultType& aResult, uint32_t& aTTL) { + DNSPacket packet; + nsAutoCString host(aHost); + nsAutoCString cname; + nsresult rv; + + if (xpc::IsInAutomation() && + !StaticPrefs::network_dns_native_https_query_in_automation()) { + return NS_ERROR_UNKNOWN_HOST; + } + + LOG("resolving %s\n", host.get()); + // Perform the query + rv = packet.FillBuffer( + [&](unsigned char response[DNSPacket::MAX_SIZE]) -> int { + int len = 0; +#if defined(XP_LINUX) + len = res_nquery(&_res, host.get(), ns_c_in, + nsIDNSService::RESOLVE_TYPE_HTTPSSVC, response, + DNSPacket::MAX_SIZE); +#elif defined(XP_MACOSX) + len = + res_query(host.get(), ns_c_in, nsIDNSService::RESOLVE_TYPE_HTTPSSVC, + response, DNSPacket::MAX_SIZE); +#endif + + if (len < 0) { + LOG("DNS query failed"); + } + return len; + }); + if (NS_FAILED(rv)) { + return rv; + } + + return ParseHTTPSRecord(host, packet, aResult, aTTL); +} + +} // namespace mozilla::net diff --git a/netwerk/dns/PlatformDNSWin.cpp b/netwerk/dns/PlatformDNSWin.cpp new file mode 100644 index 0000000000..8b5539d7de --- /dev/null +++ b/netwerk/dns/PlatformDNSWin.cpp @@ -0,0 +1,140 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=4 sw=2 sts=2 et cin: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "GetAddrInfo.h" +#include "mozilla/net/DNSPacket.h" +#include "nsIDNSService.h" +#include "mozilla/Maybe.h" +#include "mozilla/ScopeExit.h" +#include "mozilla/StaticPrefs_network.h" + +#ifdef DNSQUERY_AVAILABLE +// There is a bug in windns.h where the type of parameter ppQueryResultsSet for +// DnsQuery_A is dependent on UNICODE being set. It should *always* be +// PDNS_RECORDA, but if UNICODE is set it is PDNS_RECORDW. To get around this +// we make sure that UNICODE is unset. +# undef UNICODE +# include <ws2tcpip.h> +# undef GetAddrInfo +# include <windns.h> +#endif // DNSQUERY_AVAILABLE + +namespace mozilla::net { + +#define LOG(msg, ...) \ + MOZ_LOG(gGetAddrInfoLog, LogLevel::Debug, ("[DNS]: " msg, ##__VA_ARGS__)) + +nsresult ResolveHTTPSRecordImpl(const nsACString& aHost, uint16_t aFlags, + TypeRecordResultType& aResult, uint32_t& aTTL) { + nsAutoCString host(aHost); + PDNS_RECORD result = nullptr; + nsAutoCString cname; + aTTL = UINT32_MAX; + + if (xpc::IsInAutomation() && + !StaticPrefs::network_dns_native_https_query_in_automation()) { + return NS_ERROR_UNKNOWN_HOST; + } + + DNS_STATUS status = + DnsQuery_A(host.get(), nsIDNSService::RESOLVE_TYPE_HTTPSSVC, + DNS_QUERY_STANDARD, nullptr, &result, nullptr); + if (status != ERROR_SUCCESS) { + LOG("DnsQuery_A failed with error: %ld\n", status); + return NS_ERROR_UNKNOWN_HOST; + } + + // This will free the record if we exit early from this function. + auto freeDnsRecord = + MakeScopeExit([&]() { DnsRecordListFree(result, DnsFreeRecordList); }); + + auto CheckRecords = [&aResult, &cname, &aTTL]( + PDNS_RECORD result, + const nsCString& aHost) -> nsresult { + PDNS_RECORD current = result; + + for (current = result; current; current = current->pNext) { + if (strcmp(current->pName, aHost.get()) != 0) { + continue; + } + if (current->wType == nsIDNSService::RESOLVE_TYPE_HTTPSSVC) { + const unsigned char* ptr = (const unsigned char*)&(current->Data); + struct SVCB parsed; + nsresult rv = DNSPacket::ParseHTTPS(current->wDataLength, parsed, 0, + ptr, current->wDataLength, aHost); + if (NS_FAILED(rv)) { + return rv; + } + + if (parsed.mSvcDomainName.IsEmpty() && parsed.mSvcFieldPriority == 0) { + // For AliasMode SVCB RRs, a TargetName of "." indicates that the + // service is not available or does not exist. + continue; + } + + if (parsed.mSvcFieldPriority == 0) { + // Alias form SvcDomainName must not have the "." value (empty) + if (parsed.mSvcDomainName.IsEmpty()) { + return NS_ERROR_UNEXPECTED; + } + cname = parsed.mSvcDomainName; + ToLowerCase(cname); + break; + } + + if (!aResult.is<TypeRecordHTTPSSVC>()) { + aResult = mozilla::AsVariant(CopyableTArray<SVCB>()); + } + auto& results = aResult.as<TypeRecordHTTPSSVC>(); + results.AppendElement(parsed); + aTTL = std::min<uint32_t>(aTTL, current->dwTtl); + } else if (current->wType == DNS_TYPE_CNAME) { + cname = current->Data.Cname.pNameHost; + ToLowerCase(cname); + aTTL = std::min<uint32_t>(aTTL, current->dwTtl); + break; + } + } + return NS_OK; + }; + + int32_t loopCount = 64; + while (loopCount > 0 && aResult.is<Nothing>()) { + loopCount--; + + nsresult rv = CheckRecords(result, host); + if (NS_FAILED(rv)) { + return rv; + } + + if (aResult.is<Nothing>() && !cname.IsEmpty()) { + host = cname; + cname.Truncate(); + continue; + } + + if (aResult.is<Nothing>()) { + return NS_ERROR_UNKNOWN_HOST; + } + } + + // CNAME loop + if (loopCount == 0) { + return NS_ERROR_UNKNOWN_HOST; + } + + if (aResult.is<Nothing>()) { + // The call succeeded, but no HTTPS records were found. + return NS_ERROR_UNKNOWN_HOST; + } + + if (aTTL == UINT32_MAX) { + aTTL = 60; // Defaults to 60 seconds + } + return NS_OK; +} + +} // namespace mozilla::net diff --git a/netwerk/dns/PublicSuffixList.sys.mjs b/netwerk/dns/PublicSuffixList.sys.mjs new file mode 100644 index 0000000000..89cec38fea --- /dev/null +++ b/netwerk/dns/PublicSuffixList.sys.mjs @@ -0,0 +1,104 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import { RemoteSettings } from "resource://services-settings/remote-settings.sys.mjs"; + +const FileUtils = ChromeUtils.importESModule( + "resource://gre/modules/FileUtils.sys.mjs" +).FileUtils; + +const RECORD_ID = "tld-dafsa"; +const SIGNAL = "public-suffix-list-updated"; + +export const PublicSuffixList = { + CLIENT: RemoteSettings("public-suffix-list"), + + init() { + // Only initialize once. + if (this._initialized) { + return; + } + this._initialized = true; + + this.CLIENT.on("sync", this.onUpdate.bind(this)); + /* We have a single record for this collection. Let's see if we already have it locally. + * Note that on startup, we don't need to synchronize immediately on new profiles. + */ + this.CLIENT.get({ syncIfEmpty: false, filters: { id: RECORD_ID } }) + .then(async records => { + if (records.length == 1) { + // Get the downloaded file URI (most likely to be a no-op here, since file will exist). + const fileURI = await this.CLIENT.attachments.downloadToDisk( + records[0] + ); + // Send a signal so that the C++ code loads the updated list on startup. + this.notifyUpdate(fileURI); + } + }) + .catch(err => console.error(err)); + }, + + /** + * This method returns the path to the file based on the file uri received + * Example: + * On windows "file://C:/Users/AppData/main/public-suffix-list/dafsa.bin" + * will be converted to "C:\\Users\\main\\public-suffix-list\\dafsa.bin + * + * On macOS/linux "file:///home/main/public-suffix-list/dafsa.bin" + * will be converted to "/home/main/public-suffix-list/dafsa.bin" + */ + getFilePath(fileURI) { + const uri = Services.io.newURI(fileURI); + const file = uri.QueryInterface(Ci.nsIFileURL).file; + return file.path; + }, + + notifyUpdate(fileURI) { + if (!Services.prefs.getBoolPref("network.psl.onUpdate_notify", false)) { + // Updating the PSL while Firefox is running could cause principals to + // have a different base domain before/after the update. + // See bug 1582647 comment 30 + return; + } + + const filePath = this.getFilePath(fileURI); + const nsifile = new FileUtils.File(filePath); + /* Send a signal to be read by the C++, the method + * ::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) + * at netwerk/dns/nsEffectiveTLDService.cpp + */ + Services.obs.notifyObservers( + nsifile, // aSubject + SIGNAL, // aTopic + filePath // aData + ); + }, + + async onUpdate({ data: { created, updated, deleted } }) { + // In theory, this will never happen, we will never delete the record. + if (deleted.length == 1) { + await this.CLIENT.attachments.deleteFromDisk(deleted[0]); + } + // Handle creation and update the same way + const changed = created.concat(updated.map(u => u.new)); + /* In theory, we should never have more than one record. And if we receive + * this event, it's because the single record was updated. + */ + if (changed.length != 1) { + console.warn("Unsupported sync event for Public Suffix List"); + return; + } + // Download the updated file. + let fileURI; + try { + fileURI = await this.CLIENT.attachments.downloadToDisk(changed[0]); + } catch (err) { + console.error(err); + return; + } + + // Notify the C++ part to reload it from disk. + this.notifyUpdate(fileURI); + }, +}; diff --git a/netwerk/dns/TRR.cpp b/netwerk/dns/TRR.cpp new file mode 100644 index 0000000000..aad65ab809 --- /dev/null +++ b/netwerk/dns/TRR.cpp @@ -0,0 +1,1139 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=4 sw=2 sts=2 et cin: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DNS.h" +#include "DNSUtils.h" +#include "nsCharSeparatedTokenizer.h" +#include "nsContentUtils.h" +#include "nsHttpHandler.h" +#include "nsHttpChannel.h" +#include "nsHostResolver.h" +#include "nsIHttpChannel.h" +#include "nsIHttpChannelInternal.h" +#include "nsIIOService.h" +#include "nsIInputStream.h" +#include "nsIObliviousHttp.h" +#include "nsISupports.h" +#include "nsISupportsUtils.h" +#include "nsITimedChannel.h" +#include "nsIUploadChannel2.h" +#include "nsIURIMutator.h" +#include "nsNetUtil.h" +#include "nsQueryObject.h" +#include "nsStringStream.h" +#include "nsThreadUtils.h" +#include "nsURLHelper.h" +#include "ObliviousHttpChannel.h" +#include "TRR.h" +#include "TRRService.h" +#include "TRRServiceChannel.h" +#include "TRRLoadInfo.h" + +#include "mozilla/Base64.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/Logging.h" +#include "mozilla/Preferences.h" +#include "mozilla/StaticPrefs_network.h" +#include "mozilla/Telemetry.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/Tokenizer.h" +#include "mozilla/UniquePtr.h" +// Put DNSLogging.h at the end to avoid LOG being overwritten by other headers. +#include "DNSLogging.h" +#include "mozilla/glean/GleanMetrics.h" + +namespace mozilla { +namespace net { + +NS_IMPL_ISUPPORTS(TRR, nsIHttpPushListener, nsIInterfaceRequestor, + nsIStreamListener, nsIRunnable, nsITimerCallback) + +// when firing off a normal A or AAAA query +TRR::TRR(AHostResolver* aResolver, nsHostRecord* aRec, enum TrrType aType) + : mozilla::Runnable("TRR"), + mRec(aRec), + mHostResolver(aResolver), + mType(aType), + mOriginSuffix(aRec->originSuffix) { + mHost = aRec->host; + mPB = aRec->pb; + MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(), + "TRR must be in parent or socket process"); +} + +// when following CNAMEs +TRR::TRR(AHostResolver* aResolver, nsHostRecord* aRec, nsCString& aHost, + enum TrrType& aType, unsigned int aLoopCount, bool aPB) + : mozilla::Runnable("TRR"), + mHost(aHost), + mRec(aRec), + mHostResolver(aResolver), + mType(aType), + mPB(aPB), + mCnameLoop(aLoopCount), + mOriginSuffix(aRec ? aRec->originSuffix : ""_ns) { + MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(), + "TRR must be in parent or socket process"); +} + +// used on push +TRR::TRR(AHostResolver* aResolver, bool aPB) + : mozilla::Runnable("TRR"), mHostResolver(aResolver), mPB(aPB) { + MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(), + "TRR must be in parent or socket process"); +} + +// to verify a domain +TRR::TRR(AHostResolver* aResolver, nsACString& aHost, enum TrrType aType, + const nsACString& aOriginSuffix, bool aPB, bool aUseFreshConnection) + : mozilla::Runnable("TRR"), + mHost(aHost), + mRec(nullptr), + mHostResolver(aResolver), + mType(aType), + mPB(aPB), + mOriginSuffix(aOriginSuffix), + mUseFreshConnection(aUseFreshConnection) { + MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(), + "TRR must be in parent or socket process"); +} + +void TRR::HandleTimeout() { + mTimeout = nullptr; + RecordReason(TRRSkippedReason::TRR_TIMEOUT); + Cancel(NS_ERROR_NET_TIMEOUT_EXTERNAL); +} + +NS_IMETHODIMP +TRR::Notify(nsITimer* aTimer) { + if (aTimer == mTimeout) { + HandleTimeout(); + } else { + MOZ_CRASH("Unknown timer"); + } + + return NS_OK; +} + +NS_IMETHODIMP +TRR::Run() { + MOZ_ASSERT_IF(XRE_IsParentProcess() && TRRService::Get(), + NS_IsMainThread() || TRRService::Get()->IsOnTRRThread()); + MOZ_ASSERT_IF(XRE_IsSocketProcess(), NS_IsMainThread()); + + if ((TRRService::Get() == nullptr) || NS_FAILED(SendHTTPRequest())) { + RecordReason(TRRSkippedReason::TRR_SEND_FAILED); + FailData(NS_ERROR_FAILURE); + // The dtor will now be run + } + return NS_OK; +} + +DNSPacket* TRR::GetOrCreateDNSPacket() { + if (!mPacket) { + mPacket = MakeUnique<DNSPacket>(); + } + + return mPacket.get(); +} + +nsresult TRR::CreateQueryURI(nsIURI** aOutURI) { + nsAutoCString uri; + nsCOMPtr<nsIURI> dnsURI; + if (UseDefaultServer()) { + TRRService::Get()->GetURI(uri); + } else { + uri = mRec->mTrrServer; + } + + nsresult rv = NS_NewURI(getter_AddRefs(dnsURI), uri); + if (NS_FAILED(rv)) { + RecordReason(TRRSkippedReason::TRR_BAD_URL); + return rv; + } + + dnsURI.forget(aOutURI); + return NS_OK; +} + +bool TRR::MaybeBlockRequest() { + if (((mType == TRRTYPE_A) || (mType == TRRTYPE_AAAA)) && + mRec->mEffectiveTRRMode != nsIRequest::TRR_ONLY_MODE) { + // let NS resolves skip the blocklist check + // we also don't check the blocklist for TRR only requests + MOZ_ASSERT(mRec); + + // If TRRService isn't enabled anymore for the req, don't do TRR. + if (!TRRService::Get()->Enabled(mRec->mEffectiveTRRMode)) { + RecordReason(TRRSkippedReason::TRR_MODE_NOT_ENABLED); + return true; + } + + if (!StaticPrefs::network_trr_strict_native_fallback() && + UseDefaultServer() && + TRRService::Get()->IsTemporarilyBlocked(mHost, mOriginSuffix, mPB, + true)) { + if (mType == TRRTYPE_A) { + // count only blocklist for A records to avoid double counts + Telemetry::Accumulate(Telemetry::DNS_TRR_BLACKLISTED3, + TRRService::ProviderKey(), true); + } + + RecordReason(TRRSkippedReason::TRR_HOST_BLOCKED_TEMPORARY); + // not really an error but no TRR is issued + return true; + } + + if (TRRService::Get()->IsExcludedFromTRR(mHost)) { + RecordReason(TRRSkippedReason::TRR_EXCLUDED); + return true; + } + + if (UseDefaultServer() && (mType == TRRTYPE_A)) { + Telemetry::Accumulate(Telemetry::DNS_TRR_BLACKLISTED3, + TRRService::ProviderKey(), false); + } + } + + return false; +} + +nsresult TRR::SendHTTPRequest() { + // This is essentially the "run" method - created from nsHostResolver + if (mCancelled) { + return NS_ERROR_FAILURE; + } + + if ((mType != TRRTYPE_A) && (mType != TRRTYPE_AAAA) && + (mType != TRRTYPE_NS) && (mType != TRRTYPE_TXT) && + (mType != TRRTYPE_HTTPSSVC)) { + // limit the calling interface because nsHostResolver has explicit slots for + // these types + return NS_ERROR_FAILURE; + } + + if (MaybeBlockRequest()) { + return NS_ERROR_UNKNOWN_HOST; + } + + LOG(("TRR::SendHTTPRequest resolve %s type %u\n", mHost.get(), mType)); + + nsAutoCString body; + bool disableECS = StaticPrefs::network_trr_disable_ECS(); + nsresult rv = + GetOrCreateDNSPacket()->EncodeRequest(body, mHost, mType, disableECS); + if (NS_FAILED(rv)) { + HandleEncodeError(rv); + return rv; + } + + bool useGet = StaticPrefs::network_trr_useGET(); + nsCOMPtr<nsIURI> dnsURI; + rv = CreateQueryURI(getter_AddRefs(dnsURI)); + if (NS_FAILED(rv)) { + LOG(("TRR:SendHTTPRequest: NewURI failed!\n")); + return rv; + } + + if (useGet) { + /* For GET requests, the outgoing packet needs to be Base64url-encoded and + then appended to the end of the URI. */ + nsAutoCString encoded; + rv = Base64URLEncode(body.Length(), + reinterpret_cast<const unsigned char*>(body.get()), + Base64URLEncodePaddingPolicy::Omit, encoded); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoCString query; + rv = dnsURI->GetQuery(query); + if (NS_FAILED(rv)) { + return rv; + } + + if (query.IsEmpty()) { + query.Assign("?dns="_ns); + } else { + query.Append("&dns="_ns); + } + query.Append(encoded); + + rv = NS_MutateURI(dnsURI).SetQuery(query).Finalize(dnsURI); + LOG(("TRR::SendHTTPRequest GET dns=%s\n", body.get())); + } + + nsCOMPtr<nsIChannel> channel; + bool useOHTTP = StaticPrefs::network_trr_use_ohttp(); + if (useOHTTP) { + nsCOMPtr<nsIObliviousHttpService> ohttpService( + do_GetService("@mozilla.org/network/oblivious-http-service;1")); + if (!ohttpService) { + return NS_ERROR_FAILURE; + } + nsCOMPtr<nsIURI> relayURI; + nsTArray<uint8_t> encodedConfig; + rv = ohttpService->GetTRRSettings(getter_AddRefs(relayURI), encodedConfig); + if (NS_FAILED(rv)) { + return rv; + } + if (!relayURI) { + return NS_ERROR_FAILURE; + } + rv = ohttpService->NewChannel(relayURI, dnsURI, encodedConfig, + getter_AddRefs(channel)); + } else { + rv = DNSUtils::CreateChannelHelper(dnsURI, getter_AddRefs(channel)); + } + if (NS_FAILED(rv) || !channel) { + LOG(("TRR:SendHTTPRequest: NewChannel failed!\n")); + return rv; + } + + auto loadFlags = nsIRequest::LOAD_ANONYMOUS | nsIRequest::INHIBIT_CACHING | + nsIRequest::LOAD_BYPASS_CACHE | + nsIChannel::LOAD_BYPASS_URL_CLASSIFIER; + if (mUseFreshConnection) { + // Causes TRRServiceChannel to tell the connection manager + // to clear out any connection with the current conn info. + loadFlags |= nsIRequest::LOAD_FRESH_CONNECTION; + } + channel->SetLoadFlags(loadFlags); + NS_ENSURE_SUCCESS(rv, rv); + + rv = channel->SetNotificationCallbacks(this); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel); + if (!httpChannel) { + return NS_ERROR_UNEXPECTED; + } + + // This connection should not use TRR + rv = httpChannel->SetTRRMode(nsIRequest::TRR_DISABLED_MODE); + NS_ENSURE_SUCCESS(rv, rv); + + nsCString contentType(ContentType()); + rv = httpChannel->SetRequestHeader("Accept"_ns, contentType, false); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoCString cred; + if (UseDefaultServer()) { + TRRService::Get()->GetCredentials(cred); + } + if (!cred.IsEmpty()) { + rv = httpChannel->SetRequestHeader("Authorization"_ns, cred, false); + NS_ENSURE_SUCCESS(rv, rv); + } + + nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(channel); + if (!internalChannel) { + return NS_ERROR_UNEXPECTED; + } + + // setting a small stream window means the h2 stack won't pipeline a window + // update with each HEADERS or reply to a DATA with a WINDOW UPDATE + rv = internalChannel->SetInitialRwin(127 * 1024); + NS_ENSURE_SUCCESS(rv, rv); + rv = internalChannel->SetIsTRRServiceChannel(true); + NS_ENSURE_SUCCESS(rv, rv); + + if (UseDefaultServer() && StaticPrefs::network_trr_async_connInfo()) { + RefPtr<nsHttpConnectionInfo> trrConnInfo = + TRRService::Get()->TRRConnectionInfo(); + if (trrConnInfo) { + nsAutoCString host; + dnsURI->GetHost(host); + if (host.Equals(trrConnInfo->GetOrigin())) { + internalChannel->SetConnectionInfo(trrConnInfo); + LOG(("TRR::SendHTTPRequest use conn info:%s\n", + trrConnInfo->HashKey().get())); + } else { + MOZ_DIAGNOSTIC_ASSERT(false); + } + } else { + TRRService::Get()->InitTRRConnectionInfo(); + } + } + + if (useGet) { + rv = httpChannel->SetRequestMethod("GET"_ns); + NS_ENSURE_SUCCESS(rv, rv); + } else { + nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(httpChannel); + if (!uploadChannel) { + return NS_ERROR_UNEXPECTED; + } + uint32_t streamLength = body.Length(); + nsCOMPtr<nsIInputStream> uploadStream; + rv = + NS_NewCStringInputStream(getter_AddRefs(uploadStream), std::move(body)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = uploadChannel->ExplicitSetUploadStream(uploadStream, contentType, + streamLength, "POST"_ns, false); + NS_ENSURE_SUCCESS(rv, rv); + } + + rv = SetupTRRServiceChannelInternal(httpChannel, useGet, contentType); + if (NS_FAILED(rv)) { + return rv; + } + + rv = httpChannel->AsyncOpen(this); + if (NS_FAILED(rv)) { + return rv; + } + + // If the asyncOpen succeeded we can say that we actually attempted to + // use the TRR connection. + RefPtr<AddrHostRecord> addrRec = do_QueryObject(mRec); + if (addrRec) { + addrRec->mResolverType = ResolverType(); + } + + NS_NewTimerWithCallback( + getter_AddRefs(mTimeout), this, + mTimeoutMs ? mTimeoutMs : TRRService::Get()->GetRequestTimeout(), + nsITimer::TYPE_ONE_SHOT); + + mChannel = channel; + return NS_OK; +} + +// static +nsresult TRR::SetupTRRServiceChannelInternal(nsIHttpChannel* aChannel, + bool aUseGet, + const nsACString& aContentType) { + nsCOMPtr<nsIHttpChannel> httpChannel = aChannel; + MOZ_ASSERT(httpChannel); + + nsresult rv = NS_OK; + if (!aUseGet) { + rv = + httpChannel->SetRequestHeader("Cache-Control"_ns, "no-store"_ns, false); + NS_ENSURE_SUCCESS(rv, rv); + } + + // Sanitize the request by removing the Accept-Language header so we minimize + // the amount of fingerprintable information we send to the server. + if (!StaticPrefs::network_trr_send_accept_language_headers()) { + rv = httpChannel->SetRequestHeader("Accept-Language"_ns, ""_ns, false); + NS_ENSURE_SUCCESS(rv, rv); + } + + // Sanitize the request by removing the User-Agent + if (!StaticPrefs::network_trr_send_user_agent_headers()) { + rv = httpChannel->SetRequestHeader("User-Agent"_ns, ""_ns, false); + NS_ENSURE_SUCCESS(rv, rv); + } + + if (StaticPrefs::network_trr_send_empty_accept_encoding_headers()) { + rv = httpChannel->SetEmptyRequestHeader("Accept-Encoding"_ns); + NS_ENSURE_SUCCESS(rv, rv); + } + + // set the *default* response content type + if (NS_FAILED(httpChannel->SetContentType(aContentType))) { + LOG(("TRR::SetupTRRServiceChannelInternal: couldn't set content-type!\n")); + } + + nsCOMPtr<nsITimedChannel> timedChan(do_QueryInterface(httpChannel)); + if (timedChan) { + timedChan->SetTimingEnabled(true); + } + + return NS_OK; +} + +NS_IMETHODIMP +TRR::GetInterface(const nsIID& iid, void** result) { + if (!iid.Equals(NS_GET_IID(nsIHttpPushListener))) { + return NS_ERROR_NO_INTERFACE; + } + + nsCOMPtr<nsIHttpPushListener> copy(this); + *result = copy.forget().take(); + return NS_OK; +} + +nsresult TRR::DohDecodeQuery(const nsCString& query, nsCString& host, + enum TrrType& type) { + FallibleTArray<uint8_t> binary; + bool found_dns = false; + LOG(("TRR::DohDecodeQuery %s!\n", query.get())); + + // extract "dns=" from the query string + nsAutoCString data; + for (const nsACString& token : + nsCCharSeparatedTokenizer(query, '&').ToRange()) { + nsDependentCSubstring dns = Substring(token, 0, 4); + nsAutoCString check(dns); + if (check.Equals("dns=")) { + nsDependentCSubstring q = Substring(token, 4, -1); + data = q; + found_dns = true; + break; + } + } + if (!found_dns) { + LOG(("TRR::DohDecodeQuery no dns= in pushed URI query string\n")); + return NS_ERROR_ILLEGAL_VALUE; + } + + nsresult rv = + Base64URLDecode(data, Base64URLDecodePaddingPolicy::Ignore, binary); + NS_ENSURE_SUCCESS(rv, rv); + uint32_t avail = binary.Length(); + if (avail < 12) { + return NS_ERROR_FAILURE; + } + // check the query bit and the opcode + if ((binary[2] & 0xf8) != 0) { + return NS_ERROR_FAILURE; + } + uint32_t qdcount = (binary[4] << 8) + binary[5]; + if (!qdcount) { + return NS_ERROR_FAILURE; + } + + uint32_t index = 12; + uint32_t length = 0; + host.Truncate(); + do { + if (avail < (index + 1)) { + return NS_ERROR_UNEXPECTED; + } + + length = binary[index]; + if (length) { + if (host.Length()) { + host.Append("."); + } + if (avail < (index + 1 + length)) { + return NS_ERROR_UNEXPECTED; + } + host.Append((const char*)(&binary[0]) + index + 1, length); + } + index += 1 + length; // skip length byte + label + } while (length); + + LOG(("TRR::DohDecodeQuery host %s\n", host.get())); + + if (avail < (index + 2)) { + return NS_ERROR_UNEXPECTED; + } + uint16_t i16 = 0; + i16 += binary[index] << 8; + i16 += binary[index + 1]; + type = (enum TrrType)i16; + + LOG(("TRR::DohDecodeQuery type %d\n", (int)type)); + + return NS_OK; +} + +nsresult TRR::ReceivePush(nsIHttpChannel* pushed, nsHostRecord* pushedRec) { + if (!mHostResolver) { + return NS_ERROR_UNEXPECTED; + } + + LOG(("TRR::ReceivePush: PUSH incoming!\n")); + + nsCOMPtr<nsIURI> uri; + pushed->GetURI(getter_AddRefs(uri)); + nsAutoCString query; + if (uri) { + uri->GetQuery(query); + } + + if (NS_FAILED(DohDecodeQuery(query, mHost, mType)) || + HostIsIPLiteral(mHost)) { // literal + LOG(("TRR::ReceivePush failed to decode %s\n", mHost.get())); + return NS_ERROR_UNEXPECTED; + } + + if ((mType != TRRTYPE_A) && (mType != TRRTYPE_AAAA) && + (mType != TRRTYPE_TXT) && (mType != TRRTYPE_HTTPSSVC)) { + LOG(("TRR::ReceivePush unknown type %d\n", mType)); + return NS_ERROR_UNEXPECTED; + } + + if (TRRService::Get()->IsExcludedFromTRR(mHost)) { + return NS_ERROR_FAILURE; + } + + uint32_t type = nsIDNSService::RESOLVE_TYPE_DEFAULT; + if (mType == TRRTYPE_TXT) { + type = nsIDNSService::RESOLVE_TYPE_TXT; + } else if (mType == TRRTYPE_HTTPSSVC) { + type = nsIDNSService::RESOLVE_TYPE_HTTPSSVC; + } + + RefPtr<nsHostRecord> hostRecord; + nsresult rv; + rv = mHostResolver->GetHostRecord( + mHost, ""_ns, type, pushedRec->flags, pushedRec->af, pushedRec->pb, + pushedRec->originSuffix, getter_AddRefs(hostRecord)); + if (NS_FAILED(rv)) { + return rv; + } + + // Since we don't ever call nsHostResolver::NameLookup for this record, + // we need to copy the trr mode from the previous record + if (hostRecord->mEffectiveTRRMode == nsIRequest::TRR_DEFAULT_MODE) { + hostRecord->mEffectiveTRRMode = + static_cast<nsIRequest::TRRMode>(pushedRec->mEffectiveTRRMode); + } + + rv = mHostResolver->TrrLookup_unlocked(hostRecord, this); + if (NS_FAILED(rv)) { + return rv; + } + + rv = pushed->AsyncOpen(this); + if (NS_FAILED(rv)) { + return rv; + } + + // OK! + mChannel = pushed; + mRec.swap(hostRecord); + + return NS_OK; +} + +NS_IMETHODIMP +TRR::OnPush(nsIHttpChannel* associated, nsIHttpChannel* pushed) { + LOG(("TRR::OnPush entry\n")); + MOZ_ASSERT(associated == mChannel); + if (!mRec) { + return NS_ERROR_FAILURE; + } + if (!UseDefaultServer()) { + return NS_ERROR_FAILURE; + } + + RefPtr<TRR> trr = new TRR(mHostResolver, mPB); + return trr->ReceivePush(pushed, mRec); +} + +NS_IMETHODIMP +TRR::OnStartRequest(nsIRequest* aRequest) { + LOG(("TRR::OnStartRequest %p %s %d\n", this, mHost.get(), mType)); + + nsresult status = NS_OK; + aRequest->GetStatus(&status); + + if (NS_FAILED(status)) { + if (NS_IsOffline()) { + RecordReason(TRRSkippedReason::TRR_IS_OFFLINE); + } + + switch (status) { + case NS_ERROR_UNKNOWN_HOST: + RecordReason(TRRSkippedReason::TRR_CHANNEL_DNS_FAIL); + break; + case NS_ERROR_OFFLINE: + RecordReason(TRRSkippedReason::TRR_IS_OFFLINE); + break; + case NS_ERROR_NET_RESET: + RecordReason(TRRSkippedReason::TRR_NET_RESET); + break; + case NS_ERROR_NET_TIMEOUT: + case NS_ERROR_NET_TIMEOUT_EXTERNAL: + RecordReason(TRRSkippedReason::TRR_NET_TIMEOUT); + break; + case NS_ERROR_PROXY_CONNECTION_REFUSED: + RecordReason(TRRSkippedReason::TRR_NET_REFUSED); + break; + case NS_ERROR_NET_INTERRUPT: + RecordReason(TRRSkippedReason::TRR_NET_INTERRUPT); + break; + case NS_ERROR_NET_INADEQUATE_SECURITY: + RecordReason(TRRSkippedReason::TRR_NET_INADEQ_SEQURITY); + break; + default: + RecordReason(TRRSkippedReason::TRR_UNKNOWN_CHANNEL_FAILURE); + } + } + + return NS_OK; +} + +void TRR::SaveAdditionalRecords( + const nsClassHashtable<nsCStringHashKey, DOHresp>& aRecords) { + if (!mRec) { + return; + } + nsresult rv; + for (const auto& recordEntry : aRecords) { + if (!recordEntry.GetData() || recordEntry.GetData()->mAddresses.IsEmpty()) { + // no point in adding empty records. + continue; + } + // If IPv6 is disabled don't add anything else than IPv4. + if (StaticPrefs::network_dns_disableIPv6() && + std::find_if(recordEntry.GetData()->mAddresses.begin(), + recordEntry.GetData()->mAddresses.end(), + [](const NetAddr& addr) { return !addr.IsIPAddrV4(); }) != + recordEntry.GetData()->mAddresses.end()) { + continue; + } + RefPtr<nsHostRecord> hostRecord; + rv = mHostResolver->GetHostRecord( + recordEntry.GetKey(), EmptyCString(), + nsIDNSService::RESOLVE_TYPE_DEFAULT, mRec->flags, AF_UNSPEC, mRec->pb, + mRec->originSuffix, getter_AddRefs(hostRecord)); + if (NS_FAILED(rv)) { + LOG(("Failed to get host record for additional record %s", + nsCString(recordEntry.GetKey()).get())); + continue; + } + RefPtr<AddrInfo> ai( + new AddrInfo(recordEntry.GetKey(), ResolverType(), TRRTYPE_A, + std::move(recordEntry.GetData()->mAddresses), + recordEntry.GetData()->mTtl)); + mHostResolver->MaybeRenewHostRecord(hostRecord); + + // Since we're not actually calling NameLookup for this record, we need + // to set these fields to avoid assertions in CompleteLookup. + // This is quite hacky, and should be fixed. + hostRecord->Reset(); + hostRecord->mResolving++; + hostRecord->mEffectiveTRRMode = + static_cast<nsIRequest::TRRMode>(mRec->mEffectiveTRRMode); + LOG(("Completing lookup for additional: %s", + nsCString(recordEntry.GetKey()).get())); + (void)mHostResolver->CompleteLookup(hostRecord, NS_OK, ai, mPB, + mOriginSuffix, TRRSkippedReason::TRR_OK, + this); + } +} + +void TRR::StoreIPHintAsDNSRecord(const struct SVCB& aSVCBRecord) { + LOG(("TRR::StoreIPHintAsDNSRecord [%p] [%s]", this, + aSVCBRecord.mSvcDomainName.get())); + CopyableTArray<NetAddr> addresses; + aSVCBRecord.GetIPHints(addresses); + + if (StaticPrefs::network_dns_disableIPv6()) { + addresses.RemoveElementsBy( + [](const NetAddr& addr) { return !addr.IsIPAddrV4(); }); + } + + if (addresses.IsEmpty()) { + return; + } + + RefPtr<nsHostRecord> hostRecord; + nsresult rv = mHostResolver->GetHostRecord( + aSVCBRecord.mSvcDomainName, EmptyCString(), + nsIDNSService::RESOLVE_TYPE_DEFAULT, + mRec->flags | nsIDNSService::RESOLVE_IP_HINT, AF_UNSPEC, mRec->pb, + mRec->originSuffix, getter_AddRefs(hostRecord)); + if (NS_FAILED(rv)) { + LOG(("Failed to get host record")); + return; + } + + mHostResolver->MaybeRenewHostRecord(hostRecord); + + RefPtr<AddrInfo> ai(new AddrInfo(aSVCBRecord.mSvcDomainName, ResolverType(), + TRRTYPE_A, std::move(addresses), mTTL)); + + // Since we're not actually calling NameLookup for this record, we need + // to set these fields to avoid assertions in CompleteLookup. + // This is quite hacky, and should be fixed. + hostRecord->mResolving++; + hostRecord->mEffectiveTRRMode = + static_cast<nsIRequest::TRRMode>(mRec->mEffectiveTRRMode); + (void)mHostResolver->CompleteLookup(hostRecord, NS_OK, ai, mPB, mOriginSuffix, + TRRSkippedReason::TRR_OK, this); +} + +nsresult TRR::ReturnData(nsIChannel* aChannel) { + if (mType != TRRTYPE_TXT && mType != TRRTYPE_HTTPSSVC) { + // create and populate an AddrInfo instance to pass on + RefPtr<AddrInfo> ai(new AddrInfo(mHost, ResolverType(), mType, + nsTArray<NetAddr>(), mDNS.mTtl)); + auto builder = ai->Build(); + builder.SetAddresses(std::move(mDNS.mAddresses)); + builder.SetCanonicalHostname(mCname); + + // Set timings. + nsCOMPtr<nsITimedChannel> timedChan = do_QueryInterface(aChannel); + if (timedChan) { + TimeStamp asyncOpen, start, end; + if (NS_SUCCEEDED(timedChan->GetAsyncOpen(&asyncOpen)) && + !asyncOpen.IsNull()) { + builder.SetTrrFetchDuration( + (TimeStamp::Now() - asyncOpen).ToMilliseconds()); + } + if (NS_SUCCEEDED(timedChan->GetRequestStart(&start)) && + NS_SUCCEEDED(timedChan->GetResponseEnd(&end)) && !start.IsNull() && + !end.IsNull()) { + builder.SetTrrFetchDurationNetworkOnly((end - start).ToMilliseconds()); + } + } + ai = builder.Finish(); + + if (!mHostResolver) { + return NS_ERROR_FAILURE; + } + RecordReason(TRRSkippedReason::TRR_OK); + (void)mHostResolver->CompleteLookup(mRec, NS_OK, ai, mPB, mOriginSuffix, + mTRRSkippedReason, this); + mHostResolver = nullptr; + mRec = nullptr; + } else { + RecordReason(TRRSkippedReason::TRR_OK); + (void)mHostResolver->CompleteLookupByType(mRec, NS_OK, mResult, + mTRRSkippedReason, mTTL, mPB); + } + return NS_OK; +} + +nsresult TRR::FailData(nsresult error) { + if (!mHostResolver) { + return NS_ERROR_FAILURE; + } + + // If we didn't record a reason until now, record a default one. + RecordReason(TRRSkippedReason::TRR_FAILED); + + if (mType == TRRTYPE_TXT || mType == TRRTYPE_HTTPSSVC) { + TypeRecordResultType empty(Nothing{}); + (void)mHostResolver->CompleteLookupByType(mRec, error, empty, + mTRRSkippedReason, 0, mPB); + } else { + // create and populate an TRR AddrInfo instance to pass on to signal that + // this comes from TRR + nsTArray<NetAddr> noAddresses; + RefPtr<AddrInfo> ai = + new AddrInfo(mHost, ResolverType(), mType, std::move(noAddresses)); + + (void)mHostResolver->CompleteLookup(mRec, error, ai, mPB, mOriginSuffix, + mTRRSkippedReason, this); + } + + mHostResolver = nullptr; + mRec = nullptr; + return NS_OK; +} + +void TRR::HandleDecodeError(nsresult aStatusCode) { + auto rcode = mPacket->GetRCode(); + if (rcode.isOk() && rcode.unwrap() != 0) { + if (rcode.unwrap() == 0x03) { + RecordReason(TRRSkippedReason::TRR_NXDOMAIN); + } else { + RecordReason(TRRSkippedReason::TRR_RCODE_FAIL); + } + } else if (aStatusCode == NS_ERROR_UNKNOWN_HOST || + aStatusCode == NS_ERROR_DEFINITIVE_UNKNOWN_HOST) { + RecordReason(TRRSkippedReason::TRR_NO_ANSWERS); + } else { + RecordReason(TRRSkippedReason::TRR_DECODE_FAILED); + } +} + +bool TRR::HasUsableResponse() { + if (mType == TRRTYPE_A || mType == TRRTYPE_AAAA) { + return !mDNS.mAddresses.IsEmpty(); + } + if (mType == TRRTYPE_TXT) { + return mResult.is<TypeRecordTxt>(); + } + if (mType == TRRTYPE_HTTPSSVC) { + return mResult.is<TypeRecordHTTPSSVC>(); + } + return false; +} + +nsresult TRR::FollowCname(nsIChannel* aChannel) { + nsresult rv = NS_OK; + nsAutoCString cname; + while (NS_SUCCEEDED(rv) && mDNS.mAddresses.IsEmpty() && !mCname.IsEmpty() && + mCnameLoop > 0) { + mCnameLoop--; + LOG(("TRR::On200Response CNAME %s => %s (%u)\n", mHost.get(), mCname.get(), + mCnameLoop)); + cname = mCname; + mCname.Truncate(); + + LOG(("TRR: check for CNAME record for %s within previous response\n", + cname.get())); + nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords; + rv = GetOrCreateDNSPacket()->Decode( + cname, mType, mCname, StaticPrefs::network_trr_allow_rfc1918(), mDNS, + mResult, additionalRecords, mTTL); + if (NS_FAILED(rv)) { + LOG(("TRR::FollowCname DohDecode %x\n", (int)rv)); + HandleDecodeError(rv); + } + } + + // restore mCname as DohDecode() change it + mCname = cname; + if (NS_SUCCEEDED(rv) && HasUsableResponse()) { + ReturnData(aChannel); + return NS_OK; + } + + bool ra = mPacket && mPacket->RecursionAvailable().unwrapOr(false); + LOG(("ra = %d", ra)); + if (rv == NS_ERROR_UNKNOWN_HOST && ra) { + // If recursion is available, but no addresses have been returned, + // we can just return a failure here. + LOG(("TRR::FollowCname not sending another request as RA flag is set.")); + FailData(NS_ERROR_UNKNOWN_HOST); + return NS_OK; + } + + if (!mCnameLoop) { + LOG(("TRR::On200Response CNAME loop, eject!\n")); + return NS_ERROR_REDIRECT_LOOP; + } + + LOG(("TRR::On200Response CNAME %s => %s (%u)\n", mHost.get(), mCname.get(), + mCnameLoop)); + RefPtr<TRR> trr = + new TRR(mHostResolver, mRec, mCname, mType, mCnameLoop, mPB); + if (!TRRService::Get()) { + return NS_ERROR_FAILURE; + } + return TRRService::Get()->DispatchTRRRequest(trr); +} + +nsresult TRR::On200Response(nsIChannel* aChannel) { + // decode body and create an AddrInfo struct for the response + nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords; + RefPtr<TypeHostRecord> typeRec = do_QueryObject(mRec); + if (typeRec && typeRec->mOriginHost) { + GetOrCreateDNSPacket()->SetOriginHost(typeRec->mOriginHost); + } + nsresult rv = GetOrCreateDNSPacket()->Decode( + mHost, mType, mCname, StaticPrefs::network_trr_allow_rfc1918(), mDNS, + mResult, additionalRecords, mTTL); + if (NS_FAILED(rv)) { + LOG(("TRR::On200Response DohDecode %x\n", (int)rv)); + HandleDecodeError(rv); + return rv; + } + if (StaticPrefs::network_trr_add_additional_records()) { + SaveAdditionalRecords(additionalRecords); + } + + if (mResult.is<TypeRecordHTTPSSVC>()) { + auto& results = mResult.as<TypeRecordHTTPSSVC>(); + for (const auto& rec : results) { + StoreIPHintAsDNSRecord(rec); + } + } + + if (!mDNS.mAddresses.IsEmpty() || mType == TRRTYPE_TXT || mCname.IsEmpty()) { + // pass back the response data + ReturnData(aChannel); + return NS_OK; + } + + LOG(("TRR::On200Response trying CNAME %s", mCname.get())); + return FollowCname(aChannel); +} + +void TRR::RecordProcessingTime(nsIChannel* aChannel) { + // This method records the time it took from the last received byte of the + // DoH response until we've notified the consumer with a host record. + nsCOMPtr<nsITimedChannel> timedChan = do_QueryInterface(aChannel); + if (!timedChan) { + return; + } + TimeStamp end; + if (NS_FAILED(timedChan->GetResponseEnd(&end))) { + return; + } + + if (end.IsNull()) { + return; + } + + Telemetry::AccumulateTimeDelta(Telemetry::DNS_TRR_PROCESSING_TIME, end); + + LOG(("Processing DoH response took %f ms", + (TimeStamp::Now() - end).ToMilliseconds())); +} + +void TRR::ReportStatus(nsresult aStatusCode) { + // If the TRR was cancelled by nsHostResolver, then we don't need to report + // it as failed; otherwise it can cause the confirmation to fail. + if (UseDefaultServer() && aStatusCode != NS_ERROR_ABORT) { + // Bad content is still considered "okay" if the HTTP response is okay + TRRService::Get()->RecordTRRStatus(this); + } +} + +static void RecordHttpVersion(nsIHttpChannel* aHttpChannel) { + nsCOMPtr<nsIHttpChannelInternal> internalChannel = + do_QueryInterface(aHttpChannel); + if (!internalChannel) { + LOG(("RecordHttpVersion: Failed to QI nsIHttpChannelInternal")); + return; + } + + uint32_t major, minor; + if (NS_FAILED(internalChannel->GetResponseVersion(&major, &minor))) { + LOG(("RecordHttpVersion: Failed to get protocol version")); + return; + } + + auto label = Telemetry::LABELS_DNS_TRR_HTTP_VERSION2::h_1; + if (major == 2) { + label = Telemetry::LABELS_DNS_TRR_HTTP_VERSION2::h_2; + } else if (major == 3) { + label = Telemetry::LABELS_DNS_TRR_HTTP_VERSION2::h_3; + } + + Telemetry::AccumulateCategoricalKeyed(TRRService::ProviderKey(), label); + + LOG(("RecordHttpVersion: Provider responded using HTTP version: %d", major)); +} + +NS_IMETHODIMP +TRR::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) { + // The dtor will be run after the function returns + LOG(("TRR:OnStopRequest %p %s %d failed=%d code=%X\n", this, mHost.get(), + mType, mFailed, (unsigned int)aStatusCode)); + nsCOMPtr<nsIChannel> channel; + channel.swap(mChannel); + + mChannelStatus = aStatusCode; + if (NS_SUCCEEDED(aStatusCode)) { + nsCString label = "regular"_ns; + if (mPB) { + label = "private"_ns; + } + mozilla::glean::networking::trr_request_count.Get(label).Add(1); + } + + { + // Cancel the timer since we don't need it anymore. + nsCOMPtr<nsITimer> timer; + mTimeout.swap(timer); + if (timer) { + timer->Cancel(); + } + } + + auto scopeExit = MakeScopeExit([&] { ReportStatus(aStatusCode); }); + + nsresult rv = NS_OK; + // if status was "fine", parse the response and pass on the answer + if (!mFailed && NS_SUCCEEDED(aStatusCode)) { + nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest); + if (!httpChannel) { + return NS_ERROR_UNEXPECTED; + } + nsAutoCString contentType; + httpChannel->GetContentType(contentType); + if (contentType.Length() && + !contentType.LowerCaseEqualsASCII(ContentType())) { + LOG(("TRR:OnStopRequest %p %s %d wrong content type %s\n", this, + mHost.get(), mType, contentType.get())); + FailData(NS_ERROR_UNEXPECTED); + return NS_OK; + } + + uint32_t httpStatus; + rv = httpChannel->GetResponseStatus(&httpStatus); + if (NS_SUCCEEDED(rv) && httpStatus == 200) { + rv = On200Response(channel); + if (NS_SUCCEEDED(rv) && UseDefaultServer()) { + RecordReason(TRRSkippedReason::TRR_OK); + RecordProcessingTime(channel); + RecordHttpVersion(httpChannel); + return rv; + } + } else { + RecordReason(TRRSkippedReason::TRR_SERVER_RESPONSE_ERR); + LOG(("TRR:OnStopRequest:%d %p rv %x httpStatus %d\n", __LINE__, this, + (int)rv, httpStatus)); + } + } + + LOG(("TRR:OnStopRequest %p status %x mFailed %d\n", this, (int)aStatusCode, + mFailed)); + FailData(NS_SUCCEEDED(rv) ? NS_ERROR_UNKNOWN_HOST : rv); + return NS_OK; +} + +NS_IMETHODIMP +TRR::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream, + uint64_t aOffset, const uint32_t aCount) { + LOG(("TRR:OnDataAvailable %p %s %d failed=%d aCount=%u\n", this, mHost.get(), + mType, mFailed, (unsigned int)aCount)); + // receive DNS response into the local buffer + if (mFailed) { + return NS_ERROR_FAILURE; + } + + nsresult rv = GetOrCreateDNSPacket()->OnDataAvailable(aRequest, aInputStream, + aOffset, aCount); + if (NS_FAILED(rv)) { + LOG(("TRR::OnDataAvailable:%d fail\n", __LINE__)); + mFailed = true; + return rv; + } + return NS_OK; +} + +void TRR::Cancel(nsresult aStatus) { + bool isTRRServiceChannel = false; + nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal( + do_QueryInterface(mChannel)); + if (httpChannelInternal) { + nsresult rv = + httpChannelInternal->GetIsTRRServiceChannel(&isTRRServiceChannel); + if (NS_FAILED(rv)) { + isTRRServiceChannel = false; + } + } + // nsHttpChannel can be only canceled on the main thread. + RefPtr<nsHttpChannel> httpChannel = do_QueryObject(mChannel); + if (isTRRServiceChannel && !XRE_IsSocketProcess() && !httpChannel) { + if (TRRService::Get()) { + nsCOMPtr<nsIThread> thread = TRRService::Get()->TRRThread(); + if (thread && !thread->IsOnCurrentThread()) { + thread->Dispatch(NS_NewRunnableFunction( + "TRR::Cancel", + [self = RefPtr(this), aStatus]() { self->Cancel(aStatus); })); + return; + } + } + } else { + if (!NS_IsMainThread()) { + NS_DispatchToMainThread(NS_NewRunnableFunction( + "TRR::Cancel", + [self = RefPtr(this), aStatus]() { self->Cancel(aStatus); })); + return; + } + } + + if (mCancelled) { + return; + } + mCancelled = true; + + if (mChannel) { + RecordReason(TRRSkippedReason::TRR_REQ_CANCELLED); + LOG(("TRR: %p canceling Channel %p %s %d status=%" PRIx32 "\n", this, + mChannel.get(), mHost.get(), mType, static_cast<uint32_t>(aStatus))); + mChannel->Cancel(aStatus); + } +} + +bool TRR::UseDefaultServer() { return !mRec || mRec->mTrrServer.IsEmpty(); } + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/TRR.h b/netwerk/dns/TRR.h new file mode 100644 index 0000000000..82244c97a0 --- /dev/null +++ b/netwerk/dns/TRR.h @@ -0,0 +1,159 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_TRR_h +#define mozilla_net_TRR_h + +#include "mozilla/net/DNSByTypeRecord.h" +#include "mozilla/Assertions.h" +#include "nsClassHashtable.h" +#include "nsIChannel.h" +#include "nsIHttpPushListener.h" +#include "nsIInterfaceRequestor.h" +#include "nsIStreamListener.h" +#include "nsThreadUtils.h" +#include "nsXULAppAPI.h" +#include "DNSPacket.h" +#include "nsITRRSkipReason.h" + +class AHostResolver; +class nsHostRecord; + +namespace mozilla { +namespace net { + +class TRRService; +class TRRServiceChannel; + +class TRR : public Runnable, + public nsITimerCallback, + public nsIHttpPushListener, + public nsIInterfaceRequestor, + public nsIStreamListener { + public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIHTTPPUSHLISTENER + NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSISTREAMLISTENER + NS_DECL_NSITIMERCALLBACK + + // Number of "steps" we follow CNAME chains + static const unsigned int kCnameChaseMax = 64; + + // when firing off a normal A or AAAA query + explicit TRR(AHostResolver* aResolver, nsHostRecord* aRec, + enum TrrType aType); + // when following CNAMEs + explicit TRR(AHostResolver* aResolver, nsHostRecord* aRec, nsCString& aHost, + enum TrrType& aType, unsigned int aLoopCount, bool aPB); + // used on push + explicit TRR(AHostResolver* aResolver, bool aPB); + // to verify a domain + explicit TRR(AHostResolver* aResolver, nsACString& aHost, enum TrrType aType, + const nsACString& aOriginSuffix, bool aPB, + bool aUseFreshConnection); + + NS_IMETHOD Run() override; + void Cancel(nsresult aStatus); + enum TrrType Type() { return mType; } + nsCString mHost; + RefPtr<nsHostRecord> mRec; + RefPtr<AHostResolver> mHostResolver; + + void SetTimeout(uint32_t aTimeoutMs) { mTimeoutMs = aTimeoutMs; } + + nsresult ChannelStatus() { return mChannelStatus; } + + enum RequestPurpose { + Resolve, + Confirmation, + Blocklist, + }; + + RequestPurpose Purpose() { return mPurpose; } + void SetPurpose(RequestPurpose aPurpose) { mPurpose = aPurpose; } + TRRSkippedReason SkipReason() const { return mTRRSkippedReason; } + + protected: + virtual ~TRR() = default; + virtual DNSPacket* GetOrCreateDNSPacket(); + virtual nsresult CreateQueryURI(nsIURI** aOutURI); + virtual const char* ContentType() const { return "application/dns-message"; } + virtual DNSResolverType ResolverType() const { return DNSResolverType::TRR; } + virtual bool MaybeBlockRequest(); + virtual void RecordProcessingTime(nsIChannel* aChannel); + virtual void ReportStatus(nsresult aStatusCode); + virtual void HandleTimeout(); + virtual void HandleEncodeError(nsresult aStatusCode) {} + virtual void HandleDecodeError(nsresult aStatusCode); + nsresult SendHTTPRequest(); + nsresult ReturnData(nsIChannel* aChannel); + + // FailData() must be called to signal that the asynch TRR resolve is + // completed. For failed name resolves ("no such host"), the 'error' it + // passses on in its argument must be NS_ERROR_UNKNOWN_HOST. Other errors + // (if host was blocklisted, there as a bad content-type received, etc) + // other error codes must be used. This distinction is important for the + // subsequent logic to separate the error reasons. + nsresult FailData(nsresult error); + static nsresult DohDecodeQuery(const nsCString& query, nsCString& host, + enum TrrType& type); + nsresult ReceivePush(nsIHttpChannel* pushed, nsHostRecord* pushedRec); + nsresult On200Response(nsIChannel* aChannel); + nsresult FollowCname(nsIChannel* aChannel); + + bool HasUsableResponse(); + + bool UseDefaultServer(); + void SaveAdditionalRecords( + const nsClassHashtable<nsCStringHashKey, DOHresp>& aRecords); + + friend class TRRServiceChannel; + static nsresult SetupTRRServiceChannelInternal( + nsIHttpChannel* aChannel, bool aUseGet, const nsACString& aContentType); + + void StoreIPHintAsDNSRecord(const struct SVCB& aSVCBRecord); + + nsCOMPtr<nsIChannel> mChannel; + enum TrrType mType { TRRTYPE_A }; + UniquePtr<DNSPacket> mPacket; + bool mFailed = false; + bool mPB = false; + DOHresp mDNS; + nsresult mChannelStatus = NS_OK; + + RequestPurpose mPurpose = Resolve; + Atomic<bool, Relaxed> mCancelled{false}; + + // The request timeout in milliseconds. If 0 we will use the default timeout + // we get from the prefs. + uint32_t mTimeoutMs = 0; + nsCOMPtr<nsITimer> mTimeout; + nsCString mCname; + uint32_t mCnameLoop = kCnameChaseMax; // loop detection counter + + uint32_t mTTL = UINT32_MAX; + TypeRecordResultType mResult = mozilla::AsVariant(Nothing()); + + TRRSkippedReason mTRRSkippedReason = TRRSkippedReason::TRR_UNSET; + void RecordReason(TRRSkippedReason reason) { + if (mTRRSkippedReason == TRRSkippedReason::TRR_UNSET) { + mTRRSkippedReason = reason; + } + } + + // keep a copy of the originSuffix for the cases where mRec == nullptr */ + const nsCString mOriginSuffix; + + // If true, we set LOAD_FRESH_CONNECTION on our channel's load flags. + bool mUseFreshConnection = false; +}; + +} // namespace net +} // namespace mozilla + +#endif // include guard diff --git a/netwerk/dns/TRRQuery.cpp b/netwerk/dns/TRRQuery.cpp new file mode 100644 index 0000000000..7602f586a7 --- /dev/null +++ b/netwerk/dns/TRRQuery.cpp @@ -0,0 +1,387 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "TRRQuery.h" + +#include "mozilla/StaticPrefs_network.h" +#include "mozilla/Telemetry.h" +#include "nsQueryObject.h" +#include "TRR.h" +#include "TRRService.h" +// Put DNSLogging.h at the end to avoid LOG being overwritten by other headers. +#include "DNSLogging.h" + +namespace mozilla { +namespace net { + +static already_AddRefed<AddrInfo> merge_rrset(AddrInfo* rrto, + AddrInfo* rrfrom) { + MOZ_ASSERT(rrto && rrfrom); + // Each of the arguments are all-IPv4 or all-IPv6 hence judging + // by the first element. This is true only for TRR resolutions. + bool isIPv6 = rrfrom->Addresses().Length() > 0 && + rrfrom->Addresses()[0].raw.family == PR_AF_INET6; + + nsTArray<NetAddr> addresses; + if (isIPv6) { + addresses = rrfrom->Addresses().Clone(); + addresses.AppendElements(rrto->Addresses()); + } else { + addresses = rrto->Addresses().Clone(); + addresses.AppendElements(rrfrom->Addresses()); + } + auto builder = rrto->Build(); + builder.SetAddresses(std::move(addresses)); + return builder.Finish(); +} + +void TRRQuery::Cancel(nsresult aStatus) { + MutexAutoLock trrlock(mTrrLock); + if (mTrrA) { + mTrrA->Cancel(aStatus); + } + if (mTrrAAAA) { + mTrrAAAA->Cancel(aStatus); + } + if (mTrrByType) { + mTrrByType->Cancel(aStatus); + } +} + +void TRRQuery::MarkSendingTRR(TRR* trr, enum TrrType rectype, MutexAutoLock&) { + if (rectype == TRRTYPE_A) { + MOZ_ASSERT(!mTrrA); + mTrrA = trr; + mTrrAUsed = STARTED; + } else if (rectype == TRRTYPE_AAAA) { + MOZ_ASSERT(!mTrrAAAA); + mTrrAAAA = trr; + mTrrAAAAUsed = STARTED; + } else { + LOG(("TrrLookup called with bad type set: %d\n", rectype)); + MOZ_ASSERT(0); + } +} + +void TRRQuery::PrepareQuery(enum TrrType aRecType, + nsTArray<RefPtr<TRR>>& aRequestsToSend) { + LOG(("TRR Resolve %s type %d\n", mRecord->host.get(), (int)aRecType)); + RefPtr<TRR> trr = new TRR(this, mRecord, aRecType); + + { + MutexAutoLock trrlock(mTrrLock); + MarkSendingTRR(trr, aRecType, trrlock); + aRequestsToSend.AppendElement(trr); + } +} + +bool TRRQuery::SendQueries(nsTArray<RefPtr<TRR>>& aRequestsToSend) { + bool madeQuery = false; + mTRRRequestCounter = aRequestsToSend.Length(); + for (const auto& request : aRequestsToSend) { + if (NS_SUCCEEDED(TRRService::Get()->DispatchTRRRequest(request))) { + madeQuery = true; + } else { + mTRRRequestCounter--; + MutexAutoLock trrlock(mTrrLock); + if (request == mTrrA) { + mTrrA = nullptr; + mTrrAUsed = INIT; + } + if (request == mTrrAAAA) { + mTrrAAAA = nullptr; + mTrrAAAAUsed = INIT; + } + } + } + aRequestsToSend.Clear(); + return madeQuery; +} + +nsresult TRRQuery::DispatchLookup(TRR* pushedTRR) { + mTrrStart = TimeStamp::Now(); + + if (!mRecord->IsAddrRecord()) { + return DispatchByTypeLookup(pushedTRR); + } + + RefPtr<AddrHostRecord> addrRec = do_QueryObject(mRecord); + MOZ_ASSERT(addrRec); + if (!addrRec) { + return NS_ERROR_UNEXPECTED; + } + + mTrrAUsed = INIT; + mTrrAAAAUsed = INIT; + + // Always issue both A and AAAA. + // When both are complete we filter out the unneeded results. + enum TrrType rectype = (mRecord->af == AF_INET6) ? TRRTYPE_AAAA : TRRTYPE_A; + + if (pushedTRR) { + MutexAutoLock trrlock(mTrrLock); + rectype = pushedTRR->Type(); + MarkSendingTRR(pushedTRR, rectype, trrlock); + return NS_OK; + } + + // Need to dispatch TRR requests after |mTrrA| and |mTrrAAAA| are set + // properly so as to avoid the race when CompleteLookup() is called at the + // same time. + nsTArray<RefPtr<TRR>> requestsToSend; + if ((mRecord->af == AF_UNSPEC || mRecord->af == AF_INET6) && + !StaticPrefs::network_dns_disableIPv6()) { + PrepareQuery(TRRTYPE_AAAA, requestsToSend); + } + if (mRecord->af == AF_UNSPEC || mRecord->af == AF_INET) { + PrepareQuery(TRRTYPE_A, requestsToSend); + } + + if (SendQueries(requestsToSend)) { + return NS_OK; + } + + return NS_ERROR_UNKNOWN_HOST; +} + +nsresult TRRQuery::DispatchByTypeLookup(TRR* pushedTRR) { + RefPtr<TypeHostRecord> typeRec = do_QueryObject(mRecord); + MOZ_ASSERT(typeRec); + if (!typeRec) { + return NS_ERROR_UNEXPECTED; + } + + enum TrrType rectype; + + // XXX this could use a more extensible approach. + if (mRecord->type == nsIDNSService::RESOLVE_TYPE_TXT) { + rectype = TRRTYPE_TXT; + } else if (mRecord->type == nsIDNSService::RESOLVE_TYPE_HTTPSSVC) { + rectype = TRRTYPE_HTTPSSVC; + } else if (pushedTRR) { + rectype = pushedTRR->Type(); + } else { + MOZ_ASSERT(false, "Not an expected request type"); + return NS_ERROR_UNKNOWN_HOST; + } + + LOG(("TRR Resolve %s type %d\n", typeRec->host.get(), (int)rectype)); + RefPtr<TRR> trr = pushedTRR ? pushedTRR : new TRR(this, mRecord, rectype); + + if (pushedTRR || NS_SUCCEEDED(TRRService::Get()->DispatchTRRRequest(trr))) { + MutexAutoLock trrlock(mTrrLock); + MOZ_ASSERT(!mTrrByType); + mTrrByType = trr; + return NS_OK; + } + + return NS_ERROR_UNKNOWN_HOST; +} + +AHostResolver::LookupStatus TRRQuery::CompleteLookup( + nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb, + const nsACString& aOriginsuffix, nsHostRecord::TRRSkippedReason aReason, + TRR* aTRRRequest) { + if (rec != mRecord) { + LOG(("TRRQuery::CompleteLookup - Pushed record. Go to resolver")); + return mHostResolver->CompleteLookup(rec, status, aNewRRSet, pb, + aOriginsuffix, aReason, aTRRRequest); + } + + LOG(("TRRQuery::CompleteLookup > host: %s", rec->host.get())); + + RefPtr<AddrInfo> newRRSet(aNewRRSet); + DNSResolverType resolverType = newRRSet->ResolverType(); + { + MutexAutoLock trrlock(mTrrLock); + if (newRRSet->TRRType() == TRRTYPE_A) { + MOZ_ASSERT(mTrrA); + mTRRAFailReason = aReason; + mTrrA = nullptr; + mTrrAUsed = NS_SUCCEEDED(status) ? OK : FAILED; + MOZ_ASSERT(!mAddrInfoA); + mAddrInfoA = newRRSet; + mAResult = status; + LOG(("A query status: 0x%x", static_cast<uint32_t>(status))); + } else if (newRRSet->TRRType() == TRRTYPE_AAAA) { + MOZ_ASSERT(mTrrAAAA); + mTRRAAAAFailReason = aReason; + mTrrAAAA = nullptr; + mTrrAAAAUsed = NS_SUCCEEDED(status) ? OK : FAILED; + MOZ_ASSERT(!mAddrInfoAAAA); + mAddrInfoAAAA = newRRSet; + mAAAAResult = status; + LOG(("AAAA query status: 0x%x", static_cast<uint32_t>(status))); + } else { + MOZ_ASSERT(0); + } + } + + if (NS_SUCCEEDED(status)) { + mTRRSuccess++; + if (mTRRSuccess == 1) { + // Store the duration on first succesful TRR response. We + // don't know that there will be a second response nor can we + // tell which of two has useful data. + mTrrDuration = TimeStamp::Now() - mTrrStart; + } + } + + bool pendingRequest = false; + if (mTRRRequestCounter) { + mTRRRequestCounter--; + pendingRequest = (mTRRRequestCounter != 0); + } else { + MOZ_DIAGNOSTIC_ASSERT(false, "Request counter is messed up"); + } + if (pendingRequest) { // There are other outstanding requests + LOG(("CompleteLookup: waiting for all responses!\n")); + return LOOKUP_OK; + } + + if (mRecord->af == AF_UNSPEC) { + // merge successful records + if (mTrrAUsed == OK) { + LOG(("Have A response")); + newRRSet = mAddrInfoA; + status = mAResult; + if (mTrrAAAAUsed == OK) { + LOG(("Merging A and AAAA responses")); + newRRSet = merge_rrset(newRRSet, mAddrInfoAAAA); + } + } else { + newRRSet = mAddrInfoAAAA; + status = mAAAAResult; + } + + if (NS_FAILED(status) && (mAAAAResult == NS_ERROR_DEFINITIVE_UNKNOWN_HOST || + mAResult == NS_ERROR_DEFINITIVE_UNKNOWN_HOST)) { + status = NS_ERROR_DEFINITIVE_UNKNOWN_HOST; + } + } else { + // If this is a failed AAAA request, but the server only has a A record, + // then we should not fallback to Do53. Instead we also send a A request + // and return NS_ERROR_DEFINITIVE_UNKNOWN_HOST if that succeeds. + if (NS_FAILED(status) && status != NS_ERROR_DEFINITIVE_UNKNOWN_HOST && + (mTrrAUsed == INIT || mTrrAAAAUsed == INIT)) { + if (newRRSet->TRRType() == TRRTYPE_A) { + LOG(("A lookup failed. Checking if AAAA record exists")); + nsTArray<RefPtr<TRR>> requestsToSend; + PrepareQuery(TRRTYPE_AAAA, requestsToSend); + if (SendQueries(requestsToSend)) { + LOG(("Sent AAAA request")); + return LOOKUP_OK; + } + } else if (newRRSet->TRRType() == TRRTYPE_AAAA) { + LOG(("AAAA lookup failed. Checking if A record exists")); + nsTArray<RefPtr<TRR>> requestsToSend; + PrepareQuery(TRRTYPE_A, requestsToSend); + if (SendQueries(requestsToSend)) { + LOG(("Sent A request")); + return LOOKUP_OK; + } + } else { + MOZ_ASSERT(false, "Unexpected family"); + } + } + bool otherSucceeded = + mRecord->af == AF_INET6 ? mTrrAUsed == OK : mTrrAAAAUsed == OK; + LOG(("TRRQuery::CompleteLookup other request succeeded")); + + if (mRecord->af == AF_INET) { + // return only A record + newRRSet = mAddrInfoA; + status = mAResult; + if (NS_FAILED(status) && + (otherSucceeded || mAAAAResult == NS_ERROR_DEFINITIVE_UNKNOWN_HOST)) { + LOG(("status set to NS_ERROR_DEFINITIVE_UNKNOWN_HOST")); + status = NS_ERROR_DEFINITIVE_UNKNOWN_HOST; + } + + } else if (mRecord->af == AF_INET6) { + // return only AAAA record + newRRSet = mAddrInfoAAAA; + status = mAAAAResult; + + if (NS_FAILED(status) && + (otherSucceeded || mAResult == NS_ERROR_DEFINITIVE_UNKNOWN_HOST)) { + LOG(("status set to NS_ERROR_DEFINITIVE_UNKNOWN_HOST")); + status = NS_ERROR_DEFINITIVE_UNKNOWN_HOST; + } + + } else { + MOZ_ASSERT(false, "Unexpected AF"); + return LOOKUP_OK; + } + + // If this record failed, but there is a record for the other AF + // we prevent fallback to the native resolver. + } + + if (mTRRSuccess && mHostResolver->GetNCS() && + (mHostResolver->GetNCS()->GetNAT64() == + nsINetworkConnectivityService::OK) && + newRRSet) { + newRRSet = mHostResolver->GetNCS()->MapNAT64IPs(newRRSet); + } + + if (resolverType == DNSResolverType::TRR) { + if (mTrrAUsed == OK) { + AccumulateCategoricalKeyed( + TRRService::ProviderKey(), + Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::trrAOK); + } else if (mTrrAUsed == FAILED) { + AccumulateCategoricalKeyed( + TRRService::ProviderKey(), + Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::trrAFail); + } + + if (mTrrAAAAUsed == OK) { + AccumulateCategoricalKeyed( + TRRService::ProviderKey(), + Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::trrAAAAOK); + } else if (mTrrAAAAUsed == FAILED) { + AccumulateCategoricalKeyed( + TRRService::ProviderKey(), + Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::trrAAAAFail); + } + } + + mAddrInfoAAAA = nullptr; + mAddrInfoA = nullptr; + + MOZ_DIAGNOSTIC_ASSERT(!mCalledCompleteLookup, + "must not call CompleteLookup more than once"); + mCalledCompleteLookup = true; + return mHostResolver->CompleteLookup(rec, status, newRRSet, pb, aOriginsuffix, + aReason, aTRRRequest); +} + +AHostResolver::LookupStatus TRRQuery::CompleteLookupByType( + nsHostRecord* rec, nsresult status, + mozilla::net::TypeRecordResultType& aResult, + mozilla::net::TRRSkippedReason aReason, uint32_t aTtl, bool pb) { + if (rec != mRecord) { + LOG(("TRRQuery::CompleteLookup - Pushed record. Go to resolver")); + return mHostResolver->CompleteLookupByType(rec, status, aResult, aReason, + aTtl, pb); + } + + { + MutexAutoLock trrlock(mTrrLock); + mTrrByType = nullptr; + } + + // Unlike the address record, we store the duration regardless of the status. + mTrrDuration = TimeStamp::Now() - mTrrStart; + + MOZ_DIAGNOSTIC_ASSERT(!mCalledCompleteLookup, + "must not call CompleteLookup more than once"); + mCalledCompleteLookup = true; + return mHostResolver->CompleteLookupByType(rec, status, aResult, aReason, + aTtl, pb); +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/TRRQuery.h b/netwerk/dns/TRRQuery.h new file mode 100644 index 0000000000..211aacbe74 --- /dev/null +++ b/netwerk/dns/TRRQuery.h @@ -0,0 +1,111 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_TRRQuery_h +#define mozilla_net_TRRQuery_h + +#include "nsHostResolver.h" +#include "DNSPacket.h" + +namespace mozilla { +namespace net { + +class TRRQuery : public AHostResolver { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TRRQuery, override) + + public: + TRRQuery(nsHostResolver* aHostResolver, nsHostRecord* aHostRecord) + : mHostResolver(aHostResolver), + mRecord(aHostRecord), + mTrrLock("TRRQuery.mTrrLock") {} + + nsresult DispatchLookup(TRR* pushedTRR = nullptr); + + void Cancel(nsresult aStatus); + + enum TRRState { INIT, STARTED, OK, FAILED }; + TRRState mTrrAUsed = INIT; + TRRState mTrrAAAAUsed = INIT; + + TRRSkippedReason mTRRAFailReason = TRRSkippedReason::TRR_UNSET; + TRRSkippedReason mTRRAAAAFailReason = TRRSkippedReason::TRR_UNSET; + + virtual LookupStatus CompleteLookup(nsHostRecord*, nsresult, + mozilla::net::AddrInfo*, bool pb, + const nsACString& aOriginsuffix, + nsHostRecord::TRRSkippedReason aReason, + TRR* aTRRRequest) override; + virtual LookupStatus CompleteLookupByType( + nsHostRecord*, nsresult, mozilla::net::TypeRecordResultType& aResult, + mozilla::net::TRRSkippedReason aReason, uint32_t aTtl, bool pb) override; + virtual nsresult GetHostRecord(const nsACString& host, + const nsACString& aTrrServer, uint16_t type, + nsIDNSService::DNSFlags flags, uint16_t af, + bool pb, const nsCString& originSuffix, + nsHostRecord** result) override { + if (!mHostResolver) { + return NS_ERROR_FAILURE; + } + return mHostResolver->GetHostRecord(host, aTrrServer, type, flags, af, pb, + originSuffix, result); + } + virtual nsresult TrrLookup_unlocked( + nsHostRecord* rec, mozilla::net::TRR* pushedTRR = nullptr) override { + if (!mHostResolver) { + return NS_ERROR_FAILURE; + } + return mHostResolver->TrrLookup_unlocked(rec, pushedTRR); + } + virtual void MaybeRenewHostRecord(nsHostRecord* aRec) override { + if (!mHostResolver) { + return; + } + mHostResolver->MaybeRenewHostRecord(aRec); + } + + mozilla::TimeDuration Duration() { return mTrrDuration; } + + private: + nsresult DispatchByTypeLookup(TRR* pushedTRR = nullptr); + + private: + ~TRRQuery() = default; + + void MarkSendingTRR(TRR* trr, TrrType rectype, MutexAutoLock&); + void PrepareQuery(TrrType aRecType, nsTArray<RefPtr<TRR>>& aRequestsToSend); + bool SendQueries(nsTArray<RefPtr<TRR>>& aRequestsToSend); + + RefPtr<nsHostResolver> mHostResolver; + RefPtr<nsHostRecord> mRecord; + + Mutex mTrrLock + MOZ_UNANNOTATED; // lock when accessing the mTrrA[AAA] pointers + RefPtr<mozilla::net::TRR> mTrrA; + RefPtr<mozilla::net::TRR> mTrrAAAA; + RefPtr<mozilla::net::TRR> mTrrByType; + // |mTRRRequestCounter| indicates the number of TRR requests that were + // dispatched sucessfully. Generally, this counter is increased to 2 after + // mTrrA and mTrrAAAA are dispatched, and is decreased by 1 when + // CompleteLookup is called. Note that nsHostResolver::CompleteLookup is only + // called when this counter equals to 0. + Atomic<uint32_t> mTRRRequestCounter{0}; + + uint8_t mTRRSuccess = 0; // number of successful TRR responses + bool mCalledCompleteLookup = false; + + mozilla::TimeDuration mTrrDuration; + mozilla::TimeStamp mTrrStart; + + RefPtr<mozilla::net::AddrInfo> mAddrInfoA; + RefPtr<mozilla::net::AddrInfo> mAddrInfoAAAA; + nsresult mAResult = NS_OK; + nsresult mAAAAResult = NS_OK; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_TRRQuery_h diff --git a/netwerk/dns/TRRService.cpp b/netwerk/dns/TRRService.cpp new file mode 100644 index 0000000000..fbaa67ee14 --- /dev/null +++ b/netwerk/dns/TRRService.cpp @@ -0,0 +1,1411 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsCharSeparatedTokenizer.h" +#include "nsComponentManagerUtils.h" +#include "nsDirectoryServiceUtils.h" +#include "nsHttpConnectionInfo.h" +#include "nsICaptivePortalService.h" +#include "nsIFile.h" +#include "nsIParentalControlsService.h" +#include "nsINetworkLinkService.h" +#include "nsIObserverService.h" +#include "nsIOService.h" +#include "nsNetUtil.h" +#include "nsStandardURL.h" +#include "TRR.h" +#include "TRRService.h" + +#include "mozilla/Preferences.h" +#include "mozilla/StaticPrefs_network.h" +#include "mozilla/Telemetry.h" +#include "mozilla/TelemetryComms.h" +#include "mozilla/Tokenizer.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/net/NeckoParent.h" +#include "mozilla/net/TRRServiceChild.h" +// Put DNSLogging.h at the end to avoid LOG being overwritten by other headers. +#include "DNSLogging.h" + +static const char kOpenCaptivePortalLoginEvent[] = "captive-portal-login"; +static const char kClearPrivateData[] = "clear-private-data"; +static const char kPurge[] = "browser:purge-session-history"; + +#define TRR_PREF_PREFIX "network.trr." +#define TRR_PREF(x) TRR_PREF_PREFIX x + +namespace mozilla::net { + +StaticRefPtr<nsIThread> sTRRBackgroundThread; +static Atomic<TRRService*> sTRRServicePtr; + +static Atomic<size_t, Relaxed> sDomainIndex(0); +static Atomic<size_t, Relaxed> sCurrentTRRModeIndex(0); + +constexpr nsLiteralCString kTRRDomains[3][7] = { + // clang-format off + { + // When mode is 0, the provider key has no postfix. + "(other)"_ns, + "mozilla.cloudflare-dns.com"_ns, + "firefox.dns.nextdns.io"_ns, + "private.canadianshield.cira.ca"_ns, + "doh.xfinity.com"_ns, // Steered clients + "dns.shaw.ca"_ns, // Steered clients + "dooh.cloudflare-dns.com"_ns, // DNS over Oblivious HTTP + }, + { + "(other)_2"_ns, + "mozilla.cloudflare-dns.com_2"_ns, + "firefox.dns.nextdns.io_2"_ns, + "private.canadianshield.cira.ca_2"_ns, + "doh.xfinity.com_2"_ns, // Steered clients + "dns.shaw.ca_2"_ns, // Steered clients + "dooh.cloudflare-dns.com_2"_ns, // DNS over Oblivious HTTP + }, + { + "(other)_3"_ns, + "mozilla.cloudflare-dns.com_3"_ns, + "firefox.dns.nextdns.io_3"_ns, + "private.canadianshield.cira.ca_3"_ns, + "doh.xfinity.com_3"_ns, // Steered clients + "dns.shaw.ca_3"_ns, // Steered clients + "dooh.cloudflare-dns.com_3"_ns, // DNS over Oblivious HTTP + }, + // clang-format on +}; + +// static +void TRRService::SetCurrentTRRMode(nsIDNSService::ResolverMode aMode) { + // A table to map ResolverMode to the row of kTRRDomains. + // When the aMode is 2, we use kTRRDomains[1] as provider keys. When aMode is + // 3, we use kTRRDomains[2]. Otherwise, we kTRRDomains[0] is used. + static const uint32_t index[] = {0, 0, 1, 2, 0, 0}; + if (aMode > nsIDNSService::MODE_TRROFF) { + aMode = nsIDNSService::MODE_TRROFF; + } + sCurrentTRRModeIndex = index[static_cast<size_t>(aMode)]; +} + +// static +void TRRService::SetProviderDomain(const nsACString& aTRRDomain) { + sDomainIndex = 0; + for (size_t i = 1; i < std::size(kTRRDomains[0]); i++) { + if (aTRRDomain.Equals(kTRRDomains[0][i])) { + sDomainIndex = i; + break; + } + } +} + +// static +const nsCString& TRRService::ProviderKey() { + return kTRRDomains[sCurrentTRRModeIndex][sDomainIndex]; +} + +NS_IMPL_ISUPPORTS_INHERITED(TRRService, TRRServiceBase, nsIObserver, + nsISupportsWeakReference) + +NS_IMPL_ADDREF_USING_AGGREGATOR(TRRService::ConfirmationContext, OwningObject()) +NS_IMPL_RELEASE_USING_AGGREGATOR(TRRService::ConfirmationContext, + OwningObject()) +NS_IMPL_QUERY_INTERFACE(TRRService::ConfirmationContext, nsITimerCallback, + nsINamed) + +TRRService::TRRService() : mLock("TRRService", this) { + MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); +} + +// static +TRRService* TRRService::Get() { return sTRRServicePtr; } + +// static +void TRRService::AddObserver(nsIObserver* aObserver, + nsIObserverService* aObserverService) { + nsCOMPtr<nsIObserverService> observerService; + if (aObserverService) { + observerService = aObserverService; + } else { + observerService = mozilla::services::GetObserverService(); + } + + if (observerService) { + observerService->AddObserver(aObserver, NS_CAPTIVE_PORTAL_CONNECTIVITY, + true); + observerService->AddObserver(aObserver, kOpenCaptivePortalLoginEvent, true); + observerService->AddObserver(aObserver, kClearPrivateData, true); + observerService->AddObserver(aObserver, kPurge, true); + observerService->AddObserver(aObserver, NS_NETWORK_LINK_TOPIC, true); + observerService->AddObserver(aObserver, NS_DNS_SUFFIX_LIST_UPDATED_TOPIC, + true); + observerService->AddObserver(aObserver, "xpcom-shutdown-threads", true); + } +} + +// static +bool TRRService::CheckCaptivePortalIsPassed() { + bool result = false; + nsCOMPtr<nsICaptivePortalService> captivePortalService = + do_GetService(NS_CAPTIVEPORTAL_CID); + if (captivePortalService) { + int32_t captiveState; + MOZ_ALWAYS_SUCCEEDS(captivePortalService->GetState(&captiveState)); + + if ((captiveState == nsICaptivePortalService::UNLOCKED_PORTAL) || + (captiveState == nsICaptivePortalService::NOT_CAPTIVE)) { + result = true; + } + LOG(("TRRService::Init mCaptiveState=%d mCaptiveIsPassed=%d\n", + captiveState, (int)result)); + } + + return result; +} + +static void EventTelemetryPrefChanged(const char* aPref, void* aData) { + Telemetry::SetEventRecordingEnabled( + "network.dns"_ns, + StaticPrefs::network_trr_confirmation_telemetry_enabled()); +} + +nsresult TRRService::Init() { + MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); + if (mInitialized) { + return NS_OK; + } + mInitialized = true; + + AddObserver(this); + + nsCOMPtr<nsIPrefBranch> prefBranch; + GetPrefBranch(getter_AddRefs(prefBranch)); + if (prefBranch) { + prefBranch->AddObserver(TRR_PREF_PREFIX, this, true); + prefBranch->AddObserver(kRolloutURIPref, this, true); + prefBranch->AddObserver(kRolloutModePref, this, true); + } + + sTRRServicePtr = this; + + ReadPrefs(nullptr); + mConfirmation.HandleEvent(ConfirmationEvent::Init); + + if (XRE_IsParentProcess()) { + mCaptiveIsPassed = CheckCaptivePortalIsPassed(); + + mParentalControlEnabled = GetParentalControlEnabledInternal(); + + mLinkService = do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID); + if (mLinkService) { + nsTArray<nsCString> suffixList; + mLinkService->GetDnsSuffixList(suffixList); + RebuildSuffixList(std::move(suffixList)); + } + + nsCOMPtr<nsIThread> thread; + if (NS_FAILED( + NS_NewNamedThread("TRR Background", getter_AddRefs(thread)))) { + NS_WARNING("NS_NewNamedThread failed!"); + return NS_ERROR_FAILURE; + } + + sTRRBackgroundThread = thread; + } + + Preferences::RegisterCallbackAndCall( + EventTelemetryPrefChanged, + "network.trr.confirmation_telemetry_enabled"_ns); + + LOG(("Initialized TRRService\n")); + return NS_OK; +} + +// static +bool TRRService::GetParentalControlEnabledInternal() { + nsCOMPtr<nsIParentalControlsService> pc = + do_CreateInstance("@mozilla.org/parental-controls-service;1"); + if (pc) { + bool result = false; + pc->GetParentalControlsEnabled(&result); + LOG(("TRRService::GetParentalControlEnabledInternal=%d\n", result)); + return result; + } + + return false; +} + +void TRRService::SetDetectedTrrURI(const nsACString& aURI) { + LOG(("SetDetectedTrrURI(%s", nsPromiseFlatCString(aURI).get())); + // If the user has set a custom URI then we don't want to override that. + // If the URI is set via doh-rollout.uri, mURIPref will be empty + // (see TRRServiceBase::OnTRRURIChange) + if (!mURIPref.IsEmpty()) { + LOG(("Already has user value. Not setting URI")); + return; + } + + if (StaticPrefs::network_trr_use_ohttp()) { + LOG(("No autodetection when using OHTTP")); + return; + } + + mURISetByDetection = MaybeSetPrivateURI(aURI); +} + +bool TRRService::Enabled(nsIRequest::TRRMode aRequestMode) { + if (mMode == nsIDNSService::MODE_TRROFF || + aRequestMode == nsIRequest::TRR_DISABLED_MODE) { + LOG(("TRR service not enabled - off or disabled")); + return false; + } + + // If already confirmed, service is enabled. + if (mConfirmation.State() == CONFIRM_OK || + aRequestMode == nsIRequest::TRR_ONLY_MODE) { + LOG(("TRR service enabled - confirmed or trr_only request")); + return true; + } + + // If this is a TRR_FIRST request but the resolver has a different mode, + // just go ahead and let it try to use TRR. + if (aRequestMode == nsIRequest::TRR_FIRST_MODE && + mMode != nsIDNSService::MODE_TRRFIRST) { + LOG(("TRR service enabled - trr_first request")); + return true; + } + + // In TRR_ONLY_MODE / confirmationNS == "skip" we don't try to confirm. + if (mConfirmation.State() == CONFIRM_DISABLED) { + LOG(("TRRService service enabled - confirmation is disabled")); + return true; + } + + LOG(("TRRService::Enabled mConfirmation.mState=%d mCaptiveIsPassed=%d\n", + mConfirmation.State(), (int)mCaptiveIsPassed)); + + if (StaticPrefs::network_trr_wait_for_confirmation()) { + return mConfirmation.State() == CONFIRM_OK; + } + + if (StaticPrefs::network_trr_attempt_when_retrying_confirmation()) { + return mConfirmation.State() == CONFIRM_OK || + mConfirmation.State() == CONFIRM_TRYING_OK || + mConfirmation.State() == CONFIRM_TRYING_FAILED; + } + + return mConfirmation.State() == CONFIRM_OK || + mConfirmation.State() == CONFIRM_TRYING_OK; +} + +void TRRService::GetPrefBranch(nsIPrefBranch** result) { + MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); + *result = nullptr; + CallGetService(NS_PREFSERVICE_CONTRACTID, result); +} + +bool TRRService::MaybeSetPrivateURI(const nsACString& aURI) { + bool clearCache = false; + nsAutoCString newURI(aURI); + LOG(("MaybeSetPrivateURI(%s)", newURI.get())); + + ProcessURITemplate(newURI); + { + MutexSingleWriterAutoLock lock(mLock); + if (mPrivateURI.Equals(newURI)) { + return false; + } + + if (!mPrivateURI.IsEmpty()) { + LOG(("TRRService clearing blocklist because of change in uri service\n")); + auto bl = mTRRBLStorage.Lock(); + bl->Clear(); + clearCache = true; + } + + nsAutoCString host; + + nsCOMPtr<nsIURI> url; + if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(url), newURI))) { + url->GetHost(host); + } + + SetProviderDomain(host); + + mPrivateURI = newURI; + + // Notify the content processes of the new TRR + for (auto* cp : + dom::ContentParent::AllProcesses(dom::ContentParent::eLive)) { + PNeckoParent* neckoParent = + SingleManagedOrNull(cp->ManagedPNeckoParent()); + if (!neckoParent) { + continue; + } + Unused << neckoParent->SendSetTRRDomain(host); + } + + AsyncCreateTRRConnectionInfo(mPrivateURI); + + // The URI has changed. We should trigger a new confirmation immediately. + // We must do this here because the URI could also change because of + // steering. + mConfirmationTriggered = + mConfirmation.HandleEvent(ConfirmationEvent::URIChange, lock); + } + + // Clear the cache because we changed the URI + if (clearCache) { + ClearEntireCache(); + } + + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + obs->NotifyObservers(nullptr, NS_NETWORK_TRR_URI_CHANGED_TOPIC, nullptr); + } + return true; +} + +nsresult TRRService::ReadPrefs(const char* name) { + MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); + + // Whenever a pref change occurs that would cause us to clear the cache + // we set this to true then do it at the end of the method. + bool clearEntireCache = false; + + if (!name || !strcmp(name, TRR_PREF("mode")) || + !strcmp(name, kRolloutModePref)) { + nsIDNSService::ResolverMode prevMode = Mode(); + + OnTRRModeChange(); + // When the TRR service gets disabled we should purge the TRR cache to + // make sure we don't use any of the cached entries on a network where + // they are invalid - for example after turning on a VPN. + if (TRR_DISABLED(Mode()) && !TRR_DISABLED(prevMode)) { + clearEntireCache = true; + } + } + if (!name || !strcmp(name, TRR_PREF("uri")) || + !strcmp(name, TRR_PREF("default_provider_uri")) || + !strcmp(name, kRolloutURIPref) || !strcmp(name, TRR_PREF("ohttp.uri")) || + !strcmp(name, TRR_PREF("use_ohttp"))) { + OnTRRURIChange(); + } + if (!name || !strcmp(name, TRR_PREF("credentials"))) { + MutexSingleWriterAutoLock lock(mLock); + Preferences::GetCString(TRR_PREF("credentials"), mPrivateCred); + } + if (!name || !strcmp(name, TRR_PREF("confirmationNS"))) { + MutexSingleWriterAutoLock lock(mLock); + Preferences::GetCString(TRR_PREF("confirmationNS"), mConfirmationNS); + LOG(("confirmationNS = %s", mConfirmationNS.get())); + } + if (!name || !strcmp(name, TRR_PREF("bootstrapAddr"))) { + MutexSingleWriterAutoLock lock(mLock); + Preferences::GetCString(TRR_PREF("bootstrapAddr"), mBootstrapAddr); + clearEntireCache = true; + } + if (!name || !strcmp(name, TRR_PREF("excluded-domains")) || + !strcmp(name, TRR_PREF("builtin-excluded-domains"))) { + MutexSingleWriterAutoLock lock(mLock); + mExcludedDomains.Clear(); + + auto parseExcludedDomains = [this](const char* aPrefName) { + nsAutoCString excludedDomains; + mLock.AssertCurrentThreadOwns(); + Preferences::GetCString(aPrefName, excludedDomains); + if (excludedDomains.IsEmpty()) { + return; + } + + for (const nsACString& tokenSubstring : + nsCCharSeparatedTokenizerTemplate< + NS_IsAsciiWhitespace, nsTokenizerFlags::SeparatorOptional>( + excludedDomains, ',') + .ToRange()) { + nsCString token{tokenSubstring}; + LOG(("TRRService::ReadPrefs %s host:[%s]\n", aPrefName, token.get())); + mExcludedDomains.Insert(token); + } + }; + + parseExcludedDomains(TRR_PREF("excluded-domains")); + parseExcludedDomains(TRR_PREF("builtin-excluded-domains")); + clearEntireCache = true; + } + + // if name is null, then we're just now initializing. In that case we don't + // need to clear the cache. + if (name && clearEntireCache) { + ClearEntireCache(); + } + + return NS_OK; +} + +void TRRService::ClearEntireCache() { + if (!StaticPrefs::network_trr_clear_cache_on_pref_change()) { + return; + } + nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID); + if (!dns) { + return; + } + dns->ClearCache(true); +} + +void TRRService::AddEtcHosts(const nsTArray<nsCString>& aArray) { + MutexSingleWriterAutoLock lock(mLock); + for (const auto& item : aArray) { + LOG(("Adding %s from /etc/hosts to excluded domains", item.get())); + mEtcHostsDomains.Insert(item); + } +} + +void TRRService::ReadEtcHostsFile() { + if (!XRE_IsParentProcess()) { + return; + } + + DoReadEtcHostsFile([](const nsTArray<nsCString>* aArray) -> bool { + RefPtr<TRRService> service(sTRRServicePtr); + if (service && aArray) { + service->AddEtcHosts(*aArray); + } + return !!service; + }); +} + +void TRRService::GetURI(nsACString& result) { + MutexSingleWriterAutoLock lock(mLock); + result = mPrivateURI; +} + +nsresult TRRService::GetCredentials(nsCString& result) { + MutexSingleWriterAutoLock lock(mLock); + result = mPrivateCred; + return NS_OK; +} + +uint32_t TRRService::GetRequestTimeout() { + if (mMode == nsIDNSService::MODE_TRRONLY) { + return StaticPrefs::network_trr_request_timeout_mode_trronly_ms(); + } + + if (StaticPrefs::network_trr_strict_native_fallback()) { + return StaticPrefs::network_trr_strict_fallback_request_timeout_ms(); + } + + return StaticPrefs::network_trr_request_timeout_ms(); +} + +nsresult TRRService::Start() { + MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); + if (!mInitialized) { + return NS_ERROR_NOT_INITIALIZED; + } + return NS_OK; +} + +TRRService::~TRRService() { + MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); + LOG(("Exiting TRRService\n")); +} + +nsresult TRRService::DispatchTRRRequest(TRR* aTrrRequest) { + return DispatchTRRRequestInternal(aTrrRequest, true); +} + +nsresult TRRService::DispatchTRRRequestInternal(TRR* aTrrRequest, + bool aWithLock) { + NS_ENSURE_ARG_POINTER(aTrrRequest); + + nsCOMPtr<nsIThread> thread = MainThreadOrTRRThread(aWithLock); + if (!thread) { + return NS_ERROR_FAILURE; + } + + RefPtr<TRR> trr = aTrrRequest; + return thread->Dispatch(trr.forget()); +} + +already_AddRefed<nsIThread> TRRService::MainThreadOrTRRThread(bool aWithLock) { + if (!StaticPrefs::network_trr_fetch_off_main_thread() || + XRE_IsSocketProcess() || mDontUseTRRThread) { + return do_GetMainThread(); + } + + nsCOMPtr<nsIThread> thread = aWithLock ? TRRThread() : TRRThread_locked(); + return thread.forget(); +} + +already_AddRefed<nsIThread> TRRService::TRRThread() { + MutexSingleWriterAutoLock lock(mLock); + return TRRThread_locked(); +} + +already_AddRefed<nsIThread> TRRService::TRRThread_locked() { + RefPtr<nsIThread> thread = sTRRBackgroundThread; + return thread.forget(); +} + +bool TRRService::IsOnTRRThread() { + nsCOMPtr<nsIThread> thread; + { + MutexSingleWriterAutoLock lock(mLock); + thread = sTRRBackgroundThread; + } + if (!thread) { + return false; + } + + return thread->IsOnCurrentThread(); +} + +NS_IMETHODIMP +TRRService::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) { + MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); + LOG(("TRR::Observe() topic=%s\n", aTopic)); + if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { + // Reset the state of whether a confirmation is triggered, so we can check + // if we create a new one after ReadPrefs(). + mConfirmationTriggered = false; + ReadPrefs(NS_ConvertUTF16toUTF8(aData).get()); + { + MutexSingleWriterAutoLock lock(mLock); + mConfirmation.RecordEvent("pref-change", lock); + } + + // We should only trigger a new confirmation if reading the prefs didn't + // already trigger one. + if (!mConfirmationTriggered) { + mConfirmation.HandleEvent(ConfirmationEvent::PrefChange); + } + } else if (!strcmp(aTopic, kOpenCaptivePortalLoginEvent)) { + // We are in a captive portal + LOG(("TRRservice in captive portal\n")); + mCaptiveIsPassed = false; + mConfirmation.SetCaptivePortalStatus( + nsICaptivePortalService::LOCKED_PORTAL); + } else if (!strcmp(aTopic, NS_CAPTIVE_PORTAL_CONNECTIVITY)) { + nsAutoCString data = NS_ConvertUTF16toUTF8(aData); + LOG(("TRRservice captive portal was %s\n", data.get())); + nsCOMPtr<nsICaptivePortalService> cps = do_QueryInterface(aSubject); + if (cps) { + mConfirmation.SetCaptivePortalStatus(cps->State()); + } + + // If we were previously in a captive portal, this event means we will + // need to trigger confirmation again. Otherwise it's just a periodical + // captive-portal check that completed and we don't need to react to it. + if (!mCaptiveIsPassed) { + mConfirmation.HandleEvent(ConfirmationEvent::CaptivePortalConnectivity); + } + + mCaptiveIsPassed = true; + } else if (!strcmp(aTopic, kClearPrivateData) || !strcmp(aTopic, kPurge)) { + // flush the TRR blocklist + auto bl = mTRRBLStorage.Lock(); + bl->Clear(); + } else if (!strcmp(aTopic, NS_DNS_SUFFIX_LIST_UPDATED_TOPIC) || + !strcmp(aTopic, NS_NETWORK_LINK_TOPIC)) { + // nsINetworkLinkService is only available on parent process. + if (XRE_IsParentProcess()) { + nsCOMPtr<nsINetworkLinkService> link = do_QueryInterface(aSubject); + // The network link service notification normally passes itself as the + // subject, but some unit tests will sometimes pass a null subject. + if (link) { + nsTArray<nsCString> suffixList; + link->GetDnsSuffixList(suffixList); + RebuildSuffixList(std::move(suffixList)); + } + } + + if (!strcmp(aTopic, NS_NETWORK_LINK_TOPIC)) { + if (NS_ConvertUTF16toUTF8(aData).EqualsLiteral( + NS_NETWORK_LINK_DATA_DOWN)) { + MutexSingleWriterAutoLock lock(mLock); + mConfirmation.RecordEvent("network-change", lock); + } + + if (mURISetByDetection) { + // If the URI was set via SetDetectedTrrURI we need to restore it to the + // default pref when a network link change occurs. + CheckURIPrefs(); + } + + if (NS_ConvertUTF16toUTF8(aData).EqualsLiteral(NS_NETWORK_LINK_DATA_UP)) { + mConfirmation.HandleEvent(ConfirmationEvent::NetworkUp); + } + } + } else if (!strcmp(aTopic, "xpcom-shutdown-threads")) { + mShutdown = true; + // If a confirmation is still in progress we record the event. + // Since there should be no more confirmations after this, the shutdown + // reason would not really be recorded in telemetry. + { + MutexSingleWriterAutoLock lock(mLock); + mConfirmation.RecordEvent("shutdown", lock); + } + + if (sTRRBackgroundThread) { + nsCOMPtr<nsIThread> thread; + thread = sTRRBackgroundThread.get(); + sTRRBackgroundThread = nullptr; + MOZ_ALWAYS_SUCCEEDS(thread->Shutdown()); + sTRRServicePtr = nullptr; + } + } + return NS_OK; +} + +void TRRService::RebuildSuffixList(nsTArray<nsCString>&& aSuffixList) { + if (!StaticPrefs::network_trr_split_horizon_mitigations() || mShutdown) { + return; + } + + MutexSingleWriterAutoLock lock(mLock); + mDNSSuffixDomains.Clear(); + for (const auto& item : aSuffixList) { + LOG(("TRRService adding %s to suffix list", item.get())); + mDNSSuffixDomains.Insert(item); + } +} + +void TRRService::ConfirmationContext::SetState( + enum ConfirmationState aNewState) { + mState = aNewState; + + enum ConfirmationState state = mState; + if (XRE_IsParentProcess()) { + NS_DispatchToMainThread(NS_NewRunnableFunction( + "TRRService::ConfirmationContextNotify", [state] { + if (nsCOMPtr<nsIObserverService> obs = + mozilla::services::GetObserverService()) { + auto stateString = + [](enum ConfirmationState aState) -> const char16_t* { + switch (aState) { + case CONFIRM_OFF: + return u"CONFIRM_OFF"; + case CONFIRM_TRYING_OK: + return u"CONFIRM_TRYING_OK"; + case CONFIRM_OK: + return u"CONFIRM_OK"; + case CONFIRM_FAILED: + return u"CONFIRM_FAILED"; + case CONFIRM_TRYING_FAILED: + return u"CONFIRM_TRYING_FAILED"; + case CONFIRM_DISABLED: + return u"CONFIRM_DISABLED"; + } + MOZ_ASSERT_UNREACHABLE(); + return u""; + }; + + obs->NotifyObservers(nullptr, "network:trr-confirmation", + stateString(state)); + } + })); + } + + if (XRE_IsParentProcess()) { + return; + } + + MOZ_ASSERT(XRE_IsSocketProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + TRRServiceChild* child = TRRServiceChild::GetSingleton(); + if (child && child->CanSend()) { + LOG(("TRRService::SendSetConfirmationState")); + Unused << child->SendSetConfirmationState(mState); + } +} + +bool TRRService::ConfirmationContext::HandleEvent(ConfirmationEvent aEvent) { + MutexSingleWriterAutoLock lock(OwningObject()->mLock); + return HandleEvent(aEvent, lock); +} + +// We're protected by service->mLock +bool TRRService::ConfirmationContext::HandleEvent( + ConfirmationEvent aEvent, const MutexSingleWriterAutoLock&) { + auto prevAddr = TaskAddr(); + TRRService* service = OwningObject(); + service->mLock.AssertCurrentThreadOwns(); + nsIDNSService::ResolverMode mode = service->Mode(); + + auto resetConfirmation = [&]() { + service->mLock.AssertCurrentThreadOwns(); + mTask = nullptr; + nsCOMPtr<nsITimer> timer = std::move(mTimer); + if (timer) { + timer->Cancel(); + } + + mRetryInterval = StaticPrefs::network_trr_retry_timeout_ms(); + mTRRFailures = 0; + + if (TRR_DISABLED(mode)) { + LOG(("TRR is disabled. mConfirmation.mState -> CONFIRM_OFF")); + SetState(CONFIRM_OFF); + return; + } + + if (mode == nsIDNSService::MODE_TRRONLY) { + LOG(("TRR_ONLY_MODE. mConfirmation.mState -> CONFIRM_DISABLED")); + SetState(CONFIRM_DISABLED); + return; + } + + if (service->mConfirmationNS.Equals("skip"_ns)) { + LOG(( + "mConfirmationNS == skip. mConfirmation.mState -> CONFIRM_DISABLED")); + SetState(CONFIRM_DISABLED); + return; + } + + // The next call to maybeConfirm will transition to CONFIRM_TRYING_OK + LOG(("mConfirmation.mState -> CONFIRM_OK")); + SetState(CONFIRM_OK); + }; + + auto maybeConfirm = [&](const char* aReason) { + service->mLock.AssertCurrentThreadOwns(); + if (TRR_DISABLED(mode) || mState == CONFIRM_DISABLED || mTask) { + LOG( + ("TRRService:MaybeConfirm(%s) mode=%d, mTask=%p " + "mState=%d\n", + aReason, (int)mode, (void*)mTask, (int)mState)); + return; + } + + MOZ_ASSERT(mode != nsIDNSService::MODE_TRRONLY, + "Confirmation should be disabled"); + MOZ_ASSERT(!service->mConfirmationNS.Equals("skip"), + "Confirmation should be disabled"); + + LOG(("maybeConfirm(%s) starting confirmation test %s %s\n", aReason, + service->mPrivateURI.get(), service->mConfirmationNS.get())); + + MOZ_ASSERT(mState == CONFIRM_OK || mState == CONFIRM_FAILED); + + if (mState == CONFIRM_FAILED) { + LOG(("mConfirmation.mState -> CONFIRM_TRYING_FAILED")); + SetState(CONFIRM_TRYING_FAILED); + } else { + LOG(("mConfirmation.mState -> CONFIRM_TRYING_OK")); + SetState(CONFIRM_TRYING_OK); + } + + nsCOMPtr<nsITimer> timer = std::move(mTimer); + if (timer) { + timer->Cancel(); + } + + MOZ_ASSERT(mode == nsIDNSService::MODE_TRRFIRST, + "Should only confirm in TRR first mode"); + // Set aUseFreshConnection if TRR lookups are retried. + mTask = new TRR(service, service->mConfirmationNS, TRRTYPE_NS, ""_ns, false, + StaticPrefs::network_trr_retry_on_recoverable_errors()); + mTask->SetTimeout(StaticPrefs::network_trr_confirmation_timeout_ms()); + mTask->SetPurpose(TRR::Confirmation); + + if (service->mLinkService) { + service->mLinkService->GetNetworkID(mNetworkId); + } + + if (mFirstRequestTime.IsNull()) { + mFirstRequestTime = TimeStamp::Now(); + } + if (mTrigger.IsEmpty()) { + mTrigger.Assign(aReason); + } + + LOG(("Dispatching confirmation task: %p", mTask.get())); + service->DispatchTRRRequestInternal(mTask, false); + }; + + switch (aEvent) { + case ConfirmationEvent::Init: + resetConfirmation(); + maybeConfirm("context-init"); + break; + case ConfirmationEvent::PrefChange: + resetConfirmation(); + maybeConfirm("pref-change"); + break; + case ConfirmationEvent::ConfirmationRetry: + MOZ_ASSERT(mState == CONFIRM_FAILED); + if (mState == CONFIRM_FAILED) { + maybeConfirm("confirmation-retry"); + } + break; + case ConfirmationEvent::FailedLookups: + MOZ_ASSERT(mState == CONFIRM_OK); + mTrigger.Assign("failed-lookups"); + mFailedLookups = nsDependentCSubstring( + mFailureReasons, mTRRFailures % ConfirmationContext::RESULTS_SIZE); + maybeConfirm("failed-lookups"); + break; + case ConfirmationEvent::RetryTRR: + MOZ_ASSERT(mState == CONFIRM_OK); + maybeConfirm("retry-trr"); + break; + case ConfirmationEvent::URIChange: + resetConfirmation(); + maybeConfirm("uri-change"); + break; + case ConfirmationEvent::CaptivePortalConnectivity: + // If we area already confirmed then we're fine. + // If there is a confirmation in progress, likely it started before + // we had full connectivity, so it may be hanging. We reset and try again. + if (mState == CONFIRM_FAILED || mState == CONFIRM_TRYING_FAILED || + mState == CONFIRM_TRYING_OK) { + resetConfirmation(); + maybeConfirm("cp-connectivity"); + } + break; + case ConfirmationEvent::NetworkUp: + if (mState != CONFIRM_OK) { + resetConfirmation(); + maybeConfirm("network-up"); + } + break; + case ConfirmationEvent::ConfirmOK: + SetState(CONFIRM_OK); + mTask = nullptr; + break; + case ConfirmationEvent::ConfirmFail: + MOZ_ASSERT(mState == CONFIRM_TRYING_OK || + mState == CONFIRM_TRYING_FAILED); + SetState(CONFIRM_FAILED); + mTask = nullptr; + // retry failed NS confirmation + + NS_NewTimerWithCallback(getter_AddRefs(mTimer), this, mRetryInterval, + nsITimer::TYPE_ONE_SHOT); + if (mRetryInterval < 64000) { + // double the interval up to this point + mRetryInterval *= 2; + } + break; + default: + MOZ_ASSERT_UNREACHABLE("Unexpected ConfirmationEvent"); + } + + return prevAddr != TaskAddr(); +} + +bool TRRService::MaybeBootstrap(const nsACString& aPossible, + nsACString& aResult) { + MutexSingleWriterAutoLock lock(mLock); + if (mMode == nsIDNSService::MODE_TRROFF || mBootstrapAddr.IsEmpty()) { + return false; + } + + nsCOMPtr<nsIURI> url; + nsresult rv = + NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID) + .Apply(&nsIStandardURLMutator::Init, nsIStandardURL::URLTYPE_STANDARD, + 443, mPrivateURI, nullptr, nullptr, nullptr) + .Finalize(url); + if (NS_FAILED(rv)) { + LOG(("TRRService::MaybeBootstrap failed to create URI!\n")); + return false; + } + + nsAutoCString host; + url->GetHost(host); + if (!aPossible.Equals(host)) { + return false; + } + LOG(("TRRService::MaybeBootstrap: use %s instead of %s\n", + mBootstrapAddr.get(), host.get())); + aResult = mBootstrapAddr; + return true; +} + +bool TRRService::IsDomainBlocked(const nsACString& aHost, + const nsACString& aOriginSuffix, + bool aPrivateBrowsing) { + auto bl = mTRRBLStorage.Lock(); + if (bl->IsEmpty()) { + return false; + } + + // use a unified casing for the hashkey + nsAutoCString hashkey(aHost + aOriginSuffix); + if (auto val = bl->Lookup(hashkey)) { + int32_t until = + *val + int32_t(StaticPrefs::network_trr_temp_blocklist_duration_sec()); + int32_t expire = NowInSeconds(); + if (until > expire) { + LOG(("Host [%s] is TRR blocklisted\n", nsCString(aHost).get())); + return true; + } + + // the blocklisted entry has expired + val.Remove(); + } + return false; +} + +// When running in TRR-only mode, the blocklist is not used and it will also +// try resolving the localhost / .local names. +bool TRRService::IsTemporarilyBlocked(const nsACString& aHost, + const nsACString& aOriginSuffix, + bool aPrivateBrowsing, + bool aParentsToo) // false if domain +{ + if (!StaticPrefs::network_trr_temp_blocklist()) { + LOG(("TRRService::IsTemporarilyBlocked temp blocklist disabled by pref")); + return false; + } + + if (mMode == nsIDNSService::MODE_TRRONLY) { + return false; // might as well try + } + + LOG(("Checking if host [%s] is blocklisted", aHost.BeginReading())); + + int32_t dot = aHost.FindChar('.'); + if ((dot == kNotFound) && aParentsToo) { + // Only if a full host name. Domains can be dotless to be able to + // blocklist entire TLDs + return true; + } + + if (IsDomainBlocked(aHost, aOriginSuffix, aPrivateBrowsing)) { + return true; + } + + nsDependentCSubstring domain = Substring(aHost, 0); + while (dot != kNotFound) { + dot++; + domain.Rebind(domain, dot, domain.Length() - dot); + + if (IsDomainBlocked(domain, aOriginSuffix, aPrivateBrowsing)) { + return true; + } + + dot = domain.FindChar('.'); + } + + return false; +} + +bool TRRService::IsExcludedFromTRR(const nsACString& aHost) { + // This method may be called off the main thread. We need to lock so + // mExcludedDomains and mDNSSuffixDomains don't change while this code + // is running. + MutexSingleWriterAutoLock lock(mLock); + + return IsExcludedFromTRR_unlocked(aHost); +} + +bool TRRService::IsExcludedFromTRR_unlocked(const nsACString& aHost) { + mLock.AssertOnWritingThreadOrHeld(); + + int32_t dot = 0; + // iteratively check the sub-domain of |aHost| + while (dot < static_cast<int32_t>(aHost.Length())) { + nsDependentCSubstring subdomain = + Substring(aHost, dot, aHost.Length() - dot); + + if (mExcludedDomains.Contains(subdomain)) { + LOG(("Subdomain [%s] of host [%s] Is Excluded From TRR via pref\n", + subdomain.BeginReading(), aHost.BeginReading())); + return true; + } + if (mDNSSuffixDomains.Contains(subdomain)) { + LOG(("Subdomain [%s] of host [%s] Is Excluded From TRR via pref\n", + subdomain.BeginReading(), aHost.BeginReading())); + return true; + } + if (mEtcHostsDomains.Contains(subdomain)) { + LOG(("Subdomain [%s] of host [%s] Is Excluded From TRR by /etc/hosts\n", + subdomain.BeginReading(), aHost.BeginReading())); + return true; + } + + dot = aHost.FindChar('.', dot + 1); + if (dot == kNotFound) { + break; + } + dot++; + } + + return false; +} + +void TRRService::AddToBlocklist(const nsACString& aHost, + const nsACString& aOriginSuffix, + bool privateBrowsing, bool aParentsToo) { + if (!StaticPrefs::network_trr_temp_blocklist()) { + LOG(("TRRService::AddToBlocklist temp blocklist disabled by pref")); + return; + } + + LOG(("TRR blocklist %s\n", nsCString(aHost).get())); + nsAutoCString hashkey(aHost + aOriginSuffix); + + // this overwrites any existing entry + { + auto bl = mTRRBLStorage.Lock(); + bl->InsertOrUpdate(hashkey, NowInSeconds()); + } + + // See bug 1700405. Some test expects 15 trr consecutive failures, but the NS + // check against the base domain is successful. So, we skip this NS check when + // the pref said so in order to pass the test reliably. + if (aParentsToo && !StaticPrefs::network_trr_skip_check_for_blocked_host()) { + // when given a full host name, verify its domain as well + int32_t dot = aHost.FindChar('.'); + if (dot != kNotFound) { + // this has a domain to be checked + dot++; + nsDependentCSubstring domain = + Substring(aHost, dot, aHost.Length() - dot); + nsAutoCString check(domain); + if (IsTemporarilyBlocked(check, aOriginSuffix, privateBrowsing, false)) { + // the domain part is already blocklisted, no need to add this entry + return; + } + // verify 'check' over TRR + LOG(("TRR: verify if '%s' resolves as NS\n", check.get())); + + // check if there's an NS entry for this name + RefPtr<TRR> trr = new TRR(this, check, TRRTYPE_NS, aOriginSuffix, + privateBrowsing, false); + trr->SetPurpose(TRR::Blocklist); + DispatchTRRRequest(trr); + } + } +} + +NS_IMETHODIMP +TRRService::ConfirmationContext::Notify(nsITimer* aTimer) { + MutexSingleWriterAutoLock lock(OwningObject()->mLock); + if (aTimer == mTimer) { + HandleEvent(ConfirmationEvent::ConfirmationRetry, lock); + } + + return NS_OK; +} + +NS_IMETHODIMP +TRRService::ConfirmationContext::GetName(nsACString& aName) { + aName.AssignLiteral("TRRService::ConfirmationContext"); + return NS_OK; +} + +static char StatusToChar(nsresult aLookupStatus, nsresult aChannelStatus) { + // If the resolution fails in the TRR channel then we'll have a failed + // aChannelStatus. Otherwise, we parse the response - if it's not a valid DNS + // packet or doesn't contain the correct responses aLookupStatus will be a + // failure code. + if (aChannelStatus == NS_OK) { + // Return + if confirmation was OK, or - if confirmation failed + return aLookupStatus == NS_OK ? '+' : '-'; + } + + if (nsCOMPtr<nsIIOService> ios = do_GetIOService()) { + bool hasConnectiviy = true; + ios->GetConnectivity(&hasConnectiviy); + if (!hasConnectiviy) { + // Browser has no active network interfaces = is offline. + return 'o'; + } + } + + switch (aChannelStatus) { + case NS_ERROR_NET_TIMEOUT_EXTERNAL: + // TRR timeout expired + return 't'; + case NS_ERROR_UNKNOWN_HOST: + // TRRServiceChannel failed to due to unresolved host + return 'd'; + default: + break; + } + + // The error is a network error + if (NS_ERROR_GET_MODULE(aChannelStatus) == NS_ERROR_MODULE_NETWORK) { + return 'n'; + } + + // Some other kind of failure. + return '?'; +} + +void TRRService::RetryTRRConfirm() { + if (mConfirmation.State() == CONFIRM_OK) { + LOG(("TRRService::RetryTRRConfirm triggering confirmation")); + mConfirmation.HandleEvent(ConfirmationEvent::RetryTRR); + } +} + +void TRRService::RecordTRRStatus(TRR* aTrrRequest) { + MOZ_ASSERT_IF(XRE_IsParentProcess(), NS_IsMainThread() || IsOnTRRThread()); + MOZ_ASSERT_IF(XRE_IsSocketProcess(), NS_IsMainThread()); + + nsresult channelStatus = aTrrRequest->ChannelStatus(); + + Telemetry::AccumulateCategoricalKeyed( + ProviderKey(), NS_SUCCEEDED(channelStatus) + ? Telemetry::LABELS_DNS_TRR_SUCCESS3::Fine + : (channelStatus == NS_ERROR_NET_TIMEOUT_EXTERNAL + ? Telemetry::LABELS_DNS_TRR_SUCCESS3::Timeout + : Telemetry::LABELS_DNS_TRR_SUCCESS3::Bad)); + + mConfirmation.RecordTRRStatus(aTrrRequest); +} + +void TRRService::ConfirmationContext::RecordTRRStatus(TRR* aTrrRequest) { + nsresult channelStatus = aTrrRequest->ChannelStatus(); + + if (OwningObject()->Mode() == nsIDNSService::MODE_TRRONLY) { + mLastConfirmationSkipReason = aTrrRequest->SkipReason(); + mLastConfirmationStatus = channelStatus; + } + + if (NS_SUCCEEDED(channelStatus)) { + LOG(("TRRService::RecordTRRStatus channel success")); + mTRRFailures = 0; + return; + } + + if (OwningObject()->Mode() != nsIDNSService::MODE_TRRFIRST) { + return; + } + + // only count failures while in OK state + if (State() != CONFIRM_OK) { + return; + } + + // When TRR retry is enabled, nsHostResolver will trigger Confirmation + // immediately upon a lookup failure, so nothing to be done here. + // nsHostResolver can assess the success of the lookup considering all the + // involved results (A, AAAA) so we let it tell us when to re-Confirm. + if (StaticPrefs::network_trr_retry_on_recoverable_errors()) { + LOG(("TRRService not counting failures when retry is enabled")); + return; + } + + mFailureReasons[mTRRFailures % ConfirmationContext::RESULTS_SIZE] = + StatusToChar(NS_OK, channelStatus); + uint32_t fails = ++mTRRFailures; + LOG(("TRRService::RecordTRRStatus fails=%u", fails)); + + if (fails >= StaticPrefs::network_trr_max_fails()) { + LOG(("TRRService had %u failures in a row\n", fails)); + // When several failures occur we trigger a confirmation causing + // us to transition into the CONFIRM_TRYING_OK state. + // Only after the confirmation fails do we finally go into CONFIRM_FAILED + // and start skipping TRR. + + // Trigger a confirmation immediately. + // If it fails, it will fire off a timer to start retrying again. + HandleEvent(ConfirmationEvent::FailedLookups); + } +} + +void TRRService::ConfirmationContext::RecordEvent( + const char* aReason, const MutexSingleWriterAutoLock&) { + // Reset the confirmation context attributes + // Only resets the attributes that we keep for telemetry purposes. + auto reset = [&]() { + mAttemptCount = 0; + mNetworkId.Truncate(); + mFirstRequestTime = TimeStamp(); + mContextChangeReason.Assign(aReason); + mTrigger.Truncate(); + mFailedLookups.Truncate(); + + mRetryInterval = StaticPrefs::network_trr_retry_timeout_ms(); + }; + + if (mAttemptCount == 0) { + // XXX: resetting everything might not be the best thing here, even if the + // context changes, because there might still be a confirmation pending. + // But cancelling and retrying that confirmation might just make the whole + // confirmation longer for no reason. + reset(); + return; + } + + Telemetry::EventID eventType = + Telemetry::EventID::NetworkDns_Trrconfirmation_Context; + + nsAutoCString results; + static_assert(RESULTS_SIZE < 64); + + // mResults is a circular buffer ending at mAttemptCount + if (mAttemptCount <= RESULTS_SIZE) { + // We have fewer attempts than the size of the buffer, so all of the + // results are in the buffer. + results.Append(nsDependentCSubstring(mResults, mAttemptCount)); + } else { + // More attempts than the buffer size. + // That means past RESULTS_SIZE attempts in order are + // [posInResults .. end-of-buffer) + [start-of-buffer .. posInResults) + uint32_t posInResults = mAttemptCount % RESULTS_SIZE; + + results.Append(nsDependentCSubstring(mResults + posInResults, + RESULTS_SIZE - posInResults)); + results.Append(nsDependentCSubstring(mResults, posInResults)); + } + + auto extra = Some<nsTArray<mozilla::Telemetry::EventExtraEntry>>({ + Telemetry::EventExtraEntry{"trigger"_ns, mTrigger}, + Telemetry::EventExtraEntry{"contextReason"_ns, mContextChangeReason}, + Telemetry::EventExtraEntry{"attemptCount"_ns, + nsPrintfCString("%u", mAttemptCount)}, + Telemetry::EventExtraEntry{"results"_ns, results}, + Telemetry::EventExtraEntry{ + "time"_ns, + nsPrintfCString( + "%f", + !mFirstRequestTime.IsNull() + ? (TimeStamp::Now() - mFirstRequestTime).ToMilliseconds() + : 0.0)}, + Telemetry::EventExtraEntry{"networkID"_ns, mNetworkId}, + Telemetry::EventExtraEntry{"captivePortal"_ns, + nsPrintfCString("%i", mCaptivePortalStatus)}, + }); + + if (mTrigger.Equals("failed-lookups"_ns)) { + extra.ref().AppendElement( + Telemetry::EventExtraEntry{"failedLookups"_ns, mFailedLookups}); + } + + enum ConfirmationState state = mState; + Telemetry::RecordEvent(eventType, mozilla::Some(nsPrintfCString("%u", state)), + extra); + + reset(); +} + +void TRRService::ConfirmationContext::RequestCompleted( + nsresult aLookupStatus, nsresult aChannelStatus) { + mResults[mAttemptCount % RESULTS_SIZE] = + StatusToChar(aLookupStatus, aChannelStatus); + mAttemptCount++; +} + +void TRRService::ConfirmationContext::CompleteConfirmation(nsresult aStatus, + TRR* aTRRRequest) { + { + MutexSingleWriterAutoLock lock(OwningObject()->mLock); + // Ignore confirmations that dont match the pending task. + if (mTask != aTRRRequest) { + return; + } + MOZ_ASSERT(State() == CONFIRM_TRYING_OK || + State() == CONFIRM_TRYING_FAILED); + if (State() != CONFIRM_TRYING_OK && State() != CONFIRM_TRYING_FAILED) { + return; + } + + RequestCompleted(aStatus, aTRRRequest->ChannelStatus()); + mLastConfirmationSkipReason = aTRRRequest->SkipReason(); + mLastConfirmationStatus = aTRRRequest->ChannelStatus(); + + MOZ_ASSERT(mTask); + if (NS_SUCCEEDED(aStatus)) { + HandleEvent(ConfirmationEvent::ConfirmOK, lock); + } else { + HandleEvent(ConfirmationEvent::ConfirmFail, lock); + } + + if (State() == CONFIRM_OK) { + // Record event and start new confirmation context + RecordEvent("success", lock); + } + LOG(("TRRService finishing confirmation test %s %d %X\n", + OwningObject()->mPrivateURI.get(), State(), (unsigned int)aStatus)); + } + + if (State() == CONFIRM_OK) { + // A fresh confirmation means previous blocked entries might not + // be valid anymore. + auto bl = OwningObject()->mTRRBLStorage.Lock(); + bl->Clear(); + } else { + MOZ_ASSERT(State() == CONFIRM_FAILED); + } + + Telemetry::Accumulate(Telemetry::DNS_TRR_NS_VERFIFIED3, + TRRService::ProviderKey(), (State() == CONFIRM_OK)); +} + +AHostResolver::LookupStatus TRRService::CompleteLookup( + nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb, + const nsACString& aOriginSuffix, TRRSkippedReason aReason, + TRR* aTRRRequest) { + // this is an NS check for the TRR blocklist or confirmationNS check + + MOZ_ASSERT_IF(XRE_IsParentProcess(), NS_IsMainThread() || IsOnTRRThread()); + MOZ_ASSERT_IF(XRE_IsSocketProcess(), NS_IsMainThread()); + MOZ_ASSERT(!rec); + + RefPtr<AddrInfo> newRRSet(aNewRRSet); + MOZ_ASSERT(newRRSet && newRRSet->TRRType() == TRRTYPE_NS); + + if (aTRRRequest->Purpose() == TRR::Confirmation) { + mConfirmation.CompleteConfirmation(status, aTRRRequest); + return LOOKUP_OK; + } + + if (aTRRRequest->Purpose() == TRR::Blocklist) { + if (NS_SUCCEEDED(status)) { + LOG(("TRR verified %s to be fine!\n", newRRSet->Hostname().get())); + } else { + LOG(("TRR says %s doesn't resolve as NS!\n", newRRSet->Hostname().get())); + AddToBlocklist(newRRSet->Hostname(), aOriginSuffix, pb, false); + } + return LOOKUP_OK; + } + + MOZ_ASSERT_UNREACHABLE( + "TRRService::CompleteLookup called for unexpected request"); + return LOOKUP_OK; +} + +AHostResolver::LookupStatus TRRService::CompleteLookupByType( + nsHostRecord*, nsresult, mozilla::net::TypeRecordResultType& aResult, + mozilla::net::TRRSkippedReason aReason, uint32_t aTtl, bool aPb) { + return LOOKUP_OK; +} + +NS_IMETHODIMP TRRService::OnProxyConfigChanged() { + LOG(("TRRService::OnProxyConfigChanged")); + + nsAutoCString uri; + GetURI(uri); + AsyncCreateTRRConnectionInfo(uri); + + return NS_OK; +} + +void TRRService::InitTRRConnectionInfo() { + if (XRE_IsParentProcess()) { + TRRServiceBase::InitTRRConnectionInfo(); + return; + } + + MOZ_ASSERT(XRE_IsSocketProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + TRRServiceChild* child = TRRServiceChild::GetSingleton(); + if (child && child->CanSend()) { + LOG(("TRRService::SendInitTRRConnectionInfo")); + Unused << child->SendInitTRRConnectionInfo(); + } +} + +} // namespace mozilla::net diff --git a/netwerk/dns/TRRService.h b/netwerk/dns/TRRService.h new file mode 100644 index 0000000000..3283a8ea06 --- /dev/null +++ b/netwerk/dns/TRRService.h @@ -0,0 +1,384 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef TRRService_h_ +#define TRRService_h_ + +#include "mozilla/DataMutex.h" +#include "nsHostResolver.h" +#include "nsIObserver.h" +#include "nsITimer.h" +#include "nsWeakReference.h" +#include "TRRServiceBase.h" +#include "nsICaptivePortalService.h" +#include "nsTHashSet.h" +#include "TRR.h" + +class nsDNSService; +class nsIPrefBranch; +class nsINetworkLinkService; +class nsIObserverService; + +namespace mozilla { +namespace net { + +class TRRServiceChild; +class TRRServiceParent; + +class TRRService : public TRRServiceBase, + public nsIObserver, + public nsSupportsWeakReference, + public AHostResolver, + public SingleWriterLockOwner { + public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIOBSERVER + NS_DECL_NSIPROXYCONFIGCHANGEDCALLBACK + + TRRService(); + static TRRService* Get(); + + bool OnWritingThread() const override { return NS_IsMainThread(); } + + nsresult Init(); + nsresult Start(); + bool Enabled(nsIRequest::TRRMode aRequestMode = nsIRequest::TRR_DEFAULT_MODE); + bool IsConfirmed() { return mConfirmation.State() == CONFIRM_OK; } + uint32_t ConfirmationState() { return mConfirmation.State(); } + + void GetURI(nsACString& result) override; + nsresult GetCredentials(nsCString& result); + uint32_t GetRequestTimeout(); + void RetryTRRConfirm(); + + LookupStatus CompleteLookup(nsHostRecord*, nsresult, mozilla::net::AddrInfo*, + bool pb, const nsACString& aOriginSuffix, + TRRSkippedReason aReason, + TRR* aTrrRequest) override; + LookupStatus CompleteLookupByType(nsHostRecord*, nsresult, + mozilla::net::TypeRecordResultType&, + TRRSkippedReason, uint32_t, + bool pb) override; + void AddToBlocklist(const nsACString& host, const nsACString& originSuffix, + bool privateBrowsing, bool aParentsToo); + bool IsTemporarilyBlocked(const nsACString& aHost, + const nsACString& aOriginSuffix, + bool aPrivateBrowsing, bool aParentsToo); + bool IsExcludedFromTRR(const nsACString& aHost); + + bool MaybeBootstrap(const nsACString& possible, nsACString& result); + void RecordTRRStatus(TRR* aTrrRequest); + bool ParentalControlEnabled() const { return mParentalControlEnabled; } + + nsresult DispatchTRRRequest(TRR* aTrrRequest); + already_AddRefed<nsIThread> TRRThread(); + bool IsOnTRRThread(); + + bool IsUsingAutoDetectedURL() { return mURISetByDetection; } + + void SetHeuristicDetectionResult(TRRSkippedReason aValue) { + mHeuristicDetectionValue = aValue; + } + TRRSkippedReason GetHeuristicDetectionResult() { + return mHeuristicDetectionValue; + } + + nsresult LastConfirmationStatus() { + return mConfirmation.LastConfirmationStatus(); + } + TRRSkippedReason LastConfirmationSkipReason() { + return mConfirmation.LastConfirmationSkipReason(); + } + + // Returns a reference to a static string identifying the current DoH server + // If the DoH server is not one of the built-in ones it will return "(other)" + static const nsCString& ProviderKey(); + static void SetProviderDomain(const nsACString& aTRRDomain); + // Only called when TRR mode changed. + static void SetCurrentTRRMode(nsIDNSService::ResolverMode aMode); + + void InitTRRConnectionInfo() override; + + void DontUseTRRThread() { mDontUseTRRThread = true; } + + private: + virtual ~TRRService(); + + friend class TRRServiceChild; + friend class TRRServiceParent; + static void AddObserver(nsIObserver* aObserver, + nsIObserverService* aObserverService = nullptr); + static bool CheckCaptivePortalIsPassed(); + static bool GetParentalControlEnabledInternal(); + static bool CheckPlatformDNSStatus(nsINetworkLinkService* aLinkService); + + nsresult ReadPrefs(const char* name); + void GetPrefBranch(nsIPrefBranch** result); + friend class ::nsDNSService; + void SetDetectedTrrURI(const nsACString& aURI); + + bool IsDomainBlocked(const nsACString& aHost, const nsACString& aOriginSuffix, + bool aPrivateBrowsing); + bool IsExcludedFromTRR_unlocked(const nsACString& aHost); + + void RebuildSuffixList(nsTArray<nsCString>&& aSuffixList); + + nsresult DispatchTRRRequestInternal(TRR* aTrrRequest, bool aWithLock); + already_AddRefed<nsIThread> TRRThread_locked(); + already_AddRefed<nsIThread> MainThreadOrTRRThread(bool aWithLock = true); + + // This method will process the URI and try to set mPrivateURI to that value. + // Will return true if performed the change (if the value was different) + // or false if mPrivateURI already had that value. + bool MaybeSetPrivateURI(const nsACString& aURI) override; + void ClearEntireCache(); + + virtual void ReadEtcHostsFile() override; + void AddEtcHosts(const nsTArray<nsCString>&); + + bool mInitialized{false}; + MutexSingleWriter mLock; + + nsCString mPrivateCred; // main thread only + nsCString mConfirmationNS MOZ_GUARDED_BY(mLock){"example.com"_ns}; + nsCString mBootstrapAddr MOZ_GUARDED_BY(mLock); + + Atomic<bool, Relaxed> mCaptiveIsPassed{ + false}; // set when captive portal check is passed + Atomic<bool, Relaxed> mShutdown{false}; + Atomic<bool, Relaxed> mDontUseTRRThread{false}; + + // TRR Blocklist storage + // mTRRBLStorage is only modified on the main thread, but we query whether it + // is initialized or not off the main thread as well. Therefore we need to + // lock while creating it and while accessing it off the main thread. + DataMutex<nsTHashMap<nsCStringHashKey, int32_t>> mTRRBLStorage{ + "DataMutex::TRRBlocklist"}; + + // A set of domains that we should not use TRR for. + nsTHashSet<nsCString> mExcludedDomains MOZ_GUARDED_BY(mLock); + nsTHashSet<nsCString> mDNSSuffixDomains MOZ_GUARDED_BY(mLock); + nsTHashSet<nsCString> mEtcHostsDomains MOZ_GUARDED_BY(mLock); + + // The result of the TRR heuristic detection + TRRSkippedReason mHeuristicDetectionValue = nsITRRSkipReason::TRR_UNSET; + + enum class ConfirmationEvent { + Init, + PrefChange, + ConfirmationRetry, + FailedLookups, + RetryTRR, + URIChange, + CaptivePortalConnectivity, + NetworkUp, + ConfirmOK, + ConfirmFail, + }; + + // (FailedLookups/RetryTRR/URIChange/NetworkUp) + // +---------------------------+ + // +-----------+ | | + // | (Init) | +------v---------+ +-+--+ + // | | TRR turned on | | (ConfirmOK) | | + // | OFF +---------------> TRY-OK +---------------> OK | + // | | (PrefChange) | | | | + // +-----^-----+ +^-^----+--------+ +-^--+ + // | (PrefChange/CP) | | | | + // TRR + +------------------+ | | | + // off | | +----+ |(ConfirmFail) |(ConfirmOK) + // (Pref)| | | | | + // +---------+-+ | | | + // | | (CPConn) | +-------v--------+ +-+---------+ + // | ANY-STATE | (NetworkUp)| | | timer | | + // | | (URIChange)+-+ FAIL +---------------> TRY-FAIL | + // +-----+-----+ | | (Confirmation | | + // | +------^---------+ Retry) +------+----+ + // | (PrefChange) | | + // | TRR_ONLY mode or +--------------------------------+ + // | confirmationNS = skip (ConfirmFail) + // +-----v-----+ + // | | + // | DISABLED | + // | | + // +-----------+ + // + enum ConfirmationState { + CONFIRM_OFF = 0, + CONFIRM_TRYING_OK = 1, + CONFIRM_OK = 2, + CONFIRM_FAILED = 3, + CONFIRM_TRYING_FAILED = 4, + CONFIRM_DISABLED = 5, + }; + + class ConfirmationContext final : public nsITimerCallback, public nsINamed { + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSITIMERCALLBACK + NS_DECL_NSINAMED + + private: + static const size_t RESULTS_SIZE = 32; + + RefPtr<TRR> mTask; + nsCOMPtr<nsITimer> mTimer; + uint32_t mRetryInterval = 125; // milliseconds until retry + // The number of TRR requests that failed in a row. + Atomic<uint32_t, Relaxed> mTRRFailures{0}; + + // This buffer holds consecutive TRR failures reported by calling + // RecordTRRStatus(). It is only meant for reporting event telemetry. + char mFailureReasons[RESULTS_SIZE] = {0}; + + // The number of confirmation retries. + uint32_t mAttemptCount = 0; + + // The results of past confirmation attempts. + // This is circular buffer ending at mAttemptCount. + char mResults[RESULTS_SIZE] = {0}; + + // Time when first confirmation started. Needed so we can + // record the time from start to confirmed. + TimeStamp mFirstRequestTime; + // The network ID at the start of the last confirmation attempt + nsCString mNetworkId; + // Captive portal status at the time of recording. + int32_t mCaptivePortalStatus = nsICaptivePortalService::UNKNOWN; + + // The reason the confirmation context changed. + nsCString mContextChangeReason; + + // What triggered the confirmation + nsCString mTrigger; + + // String representation of consecutive failed lookups that triggered + // confirmation. + nsCString mFailedLookups; + + Atomic<TRRSkippedReason, Relaxed> mLastConfirmationSkipReason{ + nsITRRSkipReason::TRR_UNSET}; + Atomic<nsresult, Relaxed> mLastConfirmationStatus{NS_OK}; + + void SetState(enum ConfirmationState aNewState); + + public: + // Called when a confirmation completes successfully or when the + // confirmation context changes. + void RecordEvent(const char* aReason, const MutexSingleWriterAutoLock&); + + // Called when a confirmation request is completed. The status is recorded + // in the results. + void RequestCompleted(nsresult aLookupStatus, nsresult aChannelStatus); + + enum ConfirmationState State() { return mState; } + + void CompleteConfirmation(nsresult aStatus, TRR* aTrrRequest); + + void RecordTRRStatus(TRR* aTrrRequest); + + // Returns true when handling the event caused a new confirmation task to be + // dispatched. + bool HandleEvent(ConfirmationEvent aEvent); + bool HandleEvent(ConfirmationEvent aEvent, + const MutexSingleWriterAutoLock&); + + void SetCaptivePortalStatus(int32_t aStatus) { + mCaptivePortalStatus = aStatus; + } + + TRRSkippedReason LastConfirmationSkipReason() { + return mLastConfirmationSkipReason; + } + nsresult LastConfirmationStatus() { return mLastConfirmationStatus; } + + uintptr_t TaskAddr() { return uintptr_t(mTask.get()); } + + private: + // Since the ConfirmationContext is embedded in the TRRService object + // we can easily get a pointer to the TRRService. ConfirmationContext + // delegates AddRef/Release calls to the owning object since they are + // guaranteed to have the same lifetime. + TRRService* OwningObject() { + return reinterpret_cast<TRRService*>( + reinterpret_cast<uint8_t*>(this) - + offsetof(TRRService, mConfirmation) - + offsetof(ConfirmationWrapper, mConfirmation)); + } + + Atomic<enum ConfirmationState, Relaxed> mState{CONFIRM_OFF}; + + // TRRService needs to be a friend class because it needs to access the + // destructor. + friend class TRRService; + ~ConfirmationContext() = default; + }; + + // Because TRRService needs to be a friend class to ConfirmationContext that + // means it can access member variables. In order to properly separate logic + // and prevent direct access to its member variables we embed it in a wrapper + // class. + class ConfirmationWrapper { + public: + // Called when a confirmation completes successfully or when the + // confirmation context changes. + void RecordEvent(const char* aReason, + const MutexSingleWriterAutoLock& aLock) { + mConfirmation.RecordEvent(aReason, aLock); + } + + // Called when a confirmation request is completed. The status is recorded + // in the results. + void RequestCompleted(nsresult aLookupStatus, nsresult aChannelStatus) { + mConfirmation.RequestCompleted(aLookupStatus, aChannelStatus); + } + + enum ConfirmationState State() { return mConfirmation.State(); } + + void CompleteConfirmation(nsresult aStatus, TRR* aTrrRequest) { + mConfirmation.CompleteConfirmation(aStatus, aTrrRequest); + } + + void RecordTRRStatus(TRR* aTrrRequest) { + mConfirmation.RecordTRRStatus(aTrrRequest); + } + + bool HandleEvent(ConfirmationEvent aEvent) { + return mConfirmation.HandleEvent(aEvent); + } + + bool HandleEvent(ConfirmationEvent aEvent, + const MutexSingleWriterAutoLock& lock) { + return mConfirmation.HandleEvent(aEvent, lock); + } + + void SetCaptivePortalStatus(int32_t aStatus) { + mConfirmation.SetCaptivePortalStatus(aStatus); + } + + TRRSkippedReason LastConfirmationSkipReason() { + return mConfirmation.LastConfirmationSkipReason(); + } + nsresult LastConfirmationStatus() { + return mConfirmation.LastConfirmationStatus(); + } + + private: + friend TRRService* ConfirmationContext::OwningObject(); + ConfirmationContext mConfirmation; + }; + + ConfirmationWrapper mConfirmation; + + bool mParentalControlEnabled{false}; + // This is used to track whether a confirmation was triggered by a URI change, + // so we don't trigger another one just because other prefs have changed. + bool mConfirmationTriggered{false}; + nsCOMPtr<nsINetworkLinkService> mLinkService; +}; + +} // namespace net +} // namespace mozilla + +#endif // TRRService_h_ diff --git a/netwerk/dns/TRRServiceBase.cpp b/netwerk/dns/TRRServiceBase.cpp new file mode 100644 index 0000000000..943edc41dd --- /dev/null +++ b/netwerk/dns/TRRServiceBase.cpp @@ -0,0 +1,399 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "TRRServiceBase.h" + +#include "TRRService.h" +#include "mozilla/Preferences.h" +#include "mozilla/ScopeExit.h" +#include "nsHostResolver.h" +#include "nsNetUtil.h" +#include "nsIOService.h" +#include "nsIDNSService.h" +#include "nsIProxyInfo.h" +#include "nsHttpConnectionInfo.h" +#include "nsHttpHandler.h" +#include "mozilla/StaticPrefs_network.h" +#include "AlternateServices.h" +#include "ProxyConfigLookup.h" +// Put DNSLogging.h at the end to avoid LOG being overwritten by other headers. +#include "DNSLogging.h" + +#if defined(XP_WIN) +# include <shlobj.h> // for SHGetSpecialFolderPathA +#endif // XP_WIN + +namespace mozilla { +namespace net { + +NS_IMPL_ISUPPORTS(TRRServiceBase, nsIProxyConfigChangedCallback) + +TRRServiceBase::TRRServiceBase() + : mDefaultTRRConnectionInfo("DataMutex::mDefaultTRRConnectionInfo") {} + +TRRServiceBase::~TRRServiceBase() { + if (mTRRConnectionInfoInited) { + UnregisterProxyChangeListener(); + } +} + +void TRRServiceBase::ProcessURITemplate(nsACString& aURI) { + // URI Template, RFC 6570. + if (aURI.IsEmpty()) { + return; + } + nsAutoCString scheme; + nsCOMPtr<nsIIOService> ios(do_GetIOService()); + if (ios) { + ios->ExtractScheme(aURI, scheme); + } + if (!scheme.Equals("https")) { + LOG(("TRRService TRR URI %s is not https. Not used.\n", + PromiseFlatCString(aURI).get())); + aURI.Truncate(); + return; + } + + // cut off everything from "{" to "}" sequences (potentially multiple), + // as a crude conversion from template into URI. + nsAutoCString uri(aURI); + + do { + nsCCharSeparatedTokenizer openBrace(uri, '{'); + if (openBrace.hasMoreTokens()) { + // the 'nextToken' is the left side of the open brace (or full uri) + nsAutoCString prefix(openBrace.nextToken()); + + // if there is an open brace, there's another token + const nsACString& endBrace = openBrace.nextToken(); + nsCCharSeparatedTokenizer closeBrace(endBrace, '}'); + if (closeBrace.hasMoreTokens()) { + // there is a close brace as well, make a URI out of the prefix + // and the suffix + closeBrace.nextToken(); + nsAutoCString suffix(closeBrace.nextToken()); + uri = prefix + suffix; + } else { + // no (more) close brace + break; + } + } else { + // no (more) open brace + break; + } + } while (true); + + aURI = uri; +} + +void TRRServiceBase::CheckURIPrefs() { + mURISetByDetection = false; + + if (StaticPrefs::network_trr_use_ohttp() && !mOHTTPURIPref.IsEmpty()) { + MaybeSetPrivateURI(mOHTTPURIPref); + return; + } + + // The user has set a custom URI so it takes precedence. + if (!mURIPref.IsEmpty()) { + MaybeSetPrivateURI(mURIPref); + return; + } + + // Check if the rollout addon has set a pref. + if (!mRolloutURIPref.IsEmpty()) { + MaybeSetPrivateURI(mRolloutURIPref); + return; + } + + // Otherwise just use the default value. + MaybeSetPrivateURI(mDefaultURIPref); +} + +// static +nsIDNSService::ResolverMode ModeFromPrefs( + nsIDNSService::ResolverMode& aTRRModePrefValue) { + // 0 - off, 1 - reserved, 2 - TRR first, 3 - TRR only, 4 - reserved, + // 5 - explicit off + + auto processPrefValue = [](uint32_t value) -> nsIDNSService::ResolverMode { + if (value == nsIDNSService::MODE_RESERVED1 || + value == nsIDNSService::MODE_RESERVED4 || + value > nsIDNSService::MODE_TRROFF) { + return nsIDNSService::MODE_TRROFF; + } + return static_cast<nsIDNSService::ResolverMode>(value); + }; + + uint32_t tmp; + if (NS_FAILED(Preferences::GetUint("network.trr.mode", &tmp))) { + tmp = 0; + } + nsIDNSService::ResolverMode modeFromPref = processPrefValue(tmp); + aTRRModePrefValue = modeFromPref; + + if (modeFromPref != nsIDNSService::MODE_NATIVEONLY) { + return modeFromPref; + } + + if (NS_FAILED(Preferences::GetUint(kRolloutModePref, &tmp))) { + tmp = 0; + } + modeFromPref = processPrefValue(tmp); + + return modeFromPref; +} + +void TRRServiceBase::OnTRRModeChange() { + uint32_t oldMode = mMode; + // This is the value of the pref "network.trr.mode" + nsIDNSService::ResolverMode trrModePrefValue; + mMode = ModeFromPrefs(trrModePrefValue); + if (mMode != oldMode) { + LOG(("TRR Mode changed from %d to %d", oldMode, int(mMode))); + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + obs->NotifyObservers(nullptr, NS_NETWORK_TRR_MODE_CHANGED_TOPIC, nullptr); + } + + TRRService::SetCurrentTRRMode(trrModePrefValue); + } + + static bool readHosts = false; + if ((mMode == nsIDNSService::MODE_TRRFIRST || + mMode == nsIDNSService::MODE_TRRONLY) && + !readHosts) { + readHosts = true; + ReadEtcHostsFile(); + } +} + +void TRRServiceBase::OnTRRURIChange() { + Preferences::GetCString("network.trr.uri", mURIPref); + Preferences::GetCString(kRolloutURIPref, mRolloutURIPref); + Preferences::GetCString("network.trr.default_provider_uri", mDefaultURIPref); + Preferences::GetCString("network.trr.ohttp.uri", mOHTTPURIPref); + + CheckURIPrefs(); +} + +static already_AddRefed<nsHttpConnectionInfo> CreateConnInfoHelper( + nsIURI* aURI, nsIProxyInfo* aProxyInfo) { + MOZ_ASSERT(NS_IsMainThread()); + + nsAutoCString host; + nsAutoCString scheme; + nsAutoCString username; + int32_t port = -1; + bool isHttps = aURI->SchemeIs("https"); + + nsresult rv = aURI->GetScheme(scheme); + if (NS_FAILED(rv)) { + return nullptr; + } + + rv = aURI->GetAsciiHost(host); + if (NS_FAILED(rv)) { + return nullptr; + } + + rv = aURI->GetPort(&port); + if (NS_FAILED(rv)) { + return nullptr; + } + + // Just a warning here because some nsIURIs do not implement this method. + if (NS_WARN_IF(NS_FAILED(aURI->GetUsername(username)))) { + LOG(("Failed to get username for aURI(%s)", + aURI->GetSpecOrDefault().get())); + } + + gHttpHandler->MaybeAddAltSvcForTesting(aURI, username, false, nullptr, + OriginAttributes()); + + nsCOMPtr<nsProxyInfo> proxyInfo = do_QueryInterface(aProxyInfo); + RefPtr<nsHttpConnectionInfo> connInfo = new nsHttpConnectionInfo( + host, port, ""_ns, username, proxyInfo, OriginAttributes(), isHttps); + bool http2Allowed = !gHttpHandler->IsHttp2Excluded(connInfo); + bool http3Allowed = proxyInfo ? proxyInfo->IsDirect() : true; + + RefPtr<AltSvcMapping> mapping; + if ((http2Allowed || http3Allowed) && + AltSvcMapping::AcceptableProxy(proxyInfo) && + (scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https")) && + (mapping = gHttpHandler->GetAltServiceMapping( + scheme, host, port, false, OriginAttributes(), http2Allowed, + http3Allowed))) { + mapping->GetConnectionInfo(getter_AddRefs(connInfo), proxyInfo, + OriginAttributes()); + } + + return connInfo.forget(); +} + +void TRRServiceBase::InitTRRConnectionInfo() { + if (!XRE_IsParentProcess()) { + return; + } + + if (mTRRConnectionInfoInited) { + return; + } + + if (!NS_IsMainThread()) { + NS_DispatchToMainThread(NS_NewRunnableFunction( + "TRRServiceBase::InitTRRConnectionInfo", + [self = RefPtr{this}]() { self->InitTRRConnectionInfo(); })); + return; + } + + LOG(("TRRServiceBase::InitTRRConnectionInfo")); + nsAutoCString uri; + GetURI(uri); + AsyncCreateTRRConnectionInfoInternal(uri); +} + +void TRRServiceBase::AsyncCreateTRRConnectionInfo(const nsACString& aURI) { + LOG( + ("TRRServiceBase::AsyncCreateTRRConnectionInfo " + "mTRRConnectionInfoInited=%d", + bool(mTRRConnectionInfoInited))); + if (!mTRRConnectionInfoInited) { + return; + } + + AsyncCreateTRRConnectionInfoInternal(aURI); +} + +void TRRServiceBase::AsyncCreateTRRConnectionInfoInternal( + const nsACString& aURI) { + if (!XRE_IsParentProcess()) { + return; + } + + SetDefaultTRRConnectionInfo(nullptr); + + MOZ_ASSERT(NS_IsMainThread()); + + nsCOMPtr<nsIURI> dnsURI; + nsresult rv = NS_NewURI(getter_AddRefs(dnsURI), aURI); + if (NS_FAILED(rv)) { + return; + } + + rv = ProxyConfigLookup::Create( + [self = RefPtr{this}, uri(dnsURI)](nsIProxyInfo* aProxyInfo, + nsresult aStatus) mutable { + if (NS_FAILED(aStatus)) { + self->SetDefaultTRRConnectionInfo(nullptr); + return; + } + + RefPtr<nsHttpConnectionInfo> connInfo = + CreateConnInfoHelper(uri, aProxyInfo); + self->SetDefaultTRRConnectionInfo(connInfo); + if (!self->mTRRConnectionInfoInited) { + self->mTRRConnectionInfoInited = true; + self->RegisterProxyChangeListener(); + } + }, + dnsURI, 0, nullptr); + + // mDefaultTRRConnectionInfo is set to nullptr at the beginning of this + // method, so we don't really care aobut the |rv| here. If it's failed, + // mDefaultTRRConnectionInfo stays as nullptr and we'll create a new + // connection info in TRRServiceChannel again. + Unused << NS_WARN_IF(NS_FAILED(rv)); +} + +already_AddRefed<nsHttpConnectionInfo> TRRServiceBase::TRRConnectionInfo() { + RefPtr<nsHttpConnectionInfo> connInfo; + { + auto lock = mDefaultTRRConnectionInfo.Lock(); + connInfo = *lock; + } + return connInfo.forget(); +} + +void TRRServiceBase::SetDefaultTRRConnectionInfo( + nsHttpConnectionInfo* aConnInfo) { + LOG(("TRRService::SetDefaultTRRConnectionInfo aConnInfo=%s", + aConnInfo ? aConnInfo->HashKey().get() : "none")); + { + auto lock = mDefaultTRRConnectionInfo.Lock(); + lock.ref() = aConnInfo; + } +} + +void TRRServiceBase::RegisterProxyChangeListener() { + if (!XRE_IsParentProcess()) { + return; + } + + nsCOMPtr<nsIProtocolProxyService> pps = + do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID); + if (!pps) { + return; + } + + pps->AddProxyConfigCallback(this); +} + +void TRRServiceBase::UnregisterProxyChangeListener() { + if (!XRE_IsParentProcess()) { + return; + } + + nsCOMPtr<nsIProtocolProxyService> pps = + do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID); + if (!pps) { + return; + } + + pps->RemoveProxyConfigCallback(this); +} + +void TRRServiceBase::DoReadEtcHostsFile(ParsingCallback aCallback) { + MOZ_ASSERT(XRE_IsParentProcess()); + + if (!StaticPrefs::network_trr_exclude_etc_hosts()) { + return; + } + + auto readHostsTask = [aCallback]() { + MOZ_ASSERT(!NS_IsMainThread(), "Must not run on the main thread"); +#if defined(XP_WIN) + // Inspired by libevent/evdns.c + // Windows is a little coy about where it puts its configuration + // files. Sure, they're _usually_ in C:\windows\system32, but + // there's no reason in principle they couldn't be in + // W:\hoboken chicken emergency + + nsCString path; + path.SetLength(MAX_PATH + 1); + if (!SHGetSpecialFolderPathA(NULL, path.BeginWriting(), CSIDL_SYSTEM, + false)) { + LOG(("Calling SHGetSpecialFolderPathA failed")); + return; + } + + path.SetLength(strlen(path.get())); + path.Append("\\drivers\\etc\\hosts"); +#else + nsAutoCString path("/etc/hosts"_ns); +#endif + + LOG(("Reading hosts file at %s", path.get())); + rust_parse_etc_hosts(&path, aCallback); + }; + + Unused << NS_DispatchBackgroundTask( + NS_NewRunnableFunction("Read /etc/hosts file", readHostsTask), + NS_DISPATCH_EVENT_MAY_BLOCK); +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/TRRServiceBase.h b/netwerk/dns/TRRServiceBase.h new file mode 100644 index 0000000000..a7f85fc95d --- /dev/null +++ b/netwerk/dns/TRRServiceBase.h @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef TRRServiceBase_h_ +#define TRRServiceBase_h_ + +#include "mozilla/Atomics.h" +#include "mozilla/DataMutex.h" +#include "mozilla/net/rust_helper.h" +#include "nsString.h" +#include "nsIDNSService.h" +#include "nsIProtocolProxyService2.h" + +class nsICancelable; +class nsIProxyInfo; + +namespace mozilla { +namespace net { + +class nsHttpConnectionInfo; + +static const char kRolloutURIPref[] = "doh-rollout.uri"; +static const char kRolloutModePref[] = "doh-rollout.mode"; + +class TRRServiceBase : public nsIProxyConfigChangedCallback { + public: + NS_DECL_THREADSAFE_ISUPPORTS + + TRRServiceBase(); + nsIDNSService::ResolverMode Mode() { return mMode; } + virtual void GetURI(nsACString& result) = 0; + already_AddRefed<nsHttpConnectionInfo> TRRConnectionInfo(); + // Called to initialize the connection info. Once the connection info is + // created first time, mTRRConnectionInfoInited will be set to true. + virtual void InitTRRConnectionInfo(); + bool TRRConnectionInfoInited() const { return mTRRConnectionInfoInited; } + + protected: + virtual ~TRRServiceBase(); + + virtual bool MaybeSetPrivateURI(const nsACString& aURI) = 0; + void ProcessURITemplate(nsACString& aURI); + // Checks the network.trr.uri or the doh-rollout.uri prefs and sets the URI + // in order of preference: + // 1. The value of network.trr.uri if it is not the default one, meaning + // is was set by an explicit user action + // 2. The value of doh-rollout.uri if it exists + // this is set by the rollout addon + // 3. The default value of network.trr.uri + void CheckURIPrefs(); + + void OnTRRModeChange(); + void OnTRRURIChange(); + + void DoReadEtcHostsFile(ParsingCallback aCallback); + virtual void ReadEtcHostsFile() = 0; + // Called to create a connection info that will be used by TRRServiceChannel. + // Note that when this function is called, mDefaultTRRConnectionInfo will be + // set to null to invalidate the connection info. + // When the connection info is created, SetDefaultTRRConnectionInfo() is + // called to set the result to mDefaultTRRConnectionInfo. + // Note that this method does nothing when mTRRConnectionInfoInited is false. + // We want to starting updating the connection info after it's create first + // time. + void AsyncCreateTRRConnectionInfo(const nsACString& aURI); + void AsyncCreateTRRConnectionInfoInternal(const nsACString& aURI); + virtual void SetDefaultTRRConnectionInfo(nsHttpConnectionInfo* aConnInfo); + void RegisterProxyChangeListener(); + void UnregisterProxyChangeListener(); + + nsCString mPrivateURI; // protected by mMutex + // Pref caches should only be used on the main thread. + nsCString mURIPref; + nsCString mRolloutURIPref; + nsCString mDefaultURIPref; + nsCString mOHTTPURIPref; + + Atomic<nsIDNSService::ResolverMode, Relaxed> mMode{ + nsIDNSService::MODE_NATIVEONLY}; + Atomic<bool, Relaxed> mURISetByDetection{false}; + Atomic<bool, Relaxed> mTRRConnectionInfoInited{false}; + DataMutex<RefPtr<nsHttpConnectionInfo>> mDefaultTRRConnectionInfo; +}; + +} // namespace net +} // namespace mozilla + +#endif // TRRServiceBase_h_ diff --git a/netwerk/dns/TRRServiceChild.cpp b/netwerk/dns/TRRServiceChild.cpp new file mode 100644 index 0000000000..37be446f8f --- /dev/null +++ b/netwerk/dns/TRRServiceChild.cpp @@ -0,0 +1,121 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/net/TRRServiceChild.h" +#include "mozilla/Atomics.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/Services.h" +#include "mozilla/StaticPtr.h" +#include "nsHttpConnectionInfo.h" +#include "nsIDNService.h" +#include "nsIObserverService.h" +#include "nsServiceManagerUtils.h" +#include "TRRService.h" + +namespace mozilla { +namespace net { + +static StaticRefPtr<nsIDNSService> sDNSService; +static Atomic<TRRServiceChild*> sTRRServiceChild; + +NS_IMPL_ISUPPORTS(TRRServiceChild, nsIObserver, nsISupportsWeakReference) + +TRRServiceChild::TRRServiceChild() { + MOZ_ASSERT(NS_IsMainThread()); + sTRRServiceChild = this; +} + +TRRServiceChild::~TRRServiceChild() { + MOZ_ASSERT(NS_IsMainThread()); + sTRRServiceChild = nullptr; +} + +/* static */ +TRRServiceChild* TRRServiceChild::GetSingleton() { + MOZ_ASSERT(NS_IsMainThread()); + return sTRRServiceChild; +} + +void TRRServiceChild::Init(const bool& aCaptiveIsPassed, + const bool& aParentalControlEnabled, + nsTArray<nsCString>&& aDNSSuffixList) { + nsCOMPtr<nsIDNSService> dns = + do_GetService("@mozilla.org/network/dns-service;1"); + sDNSService = dns; + ClearOnShutdown(&sDNSService); + MOZ_ASSERT(sDNSService); + + TRRService* trrService = TRRService::Get(); + MOZ_ASSERT(trrService); + + trrService->mCaptiveIsPassed = aCaptiveIsPassed; + trrService->mParentalControlEnabled = aParentalControlEnabled; + trrService->RebuildSuffixList(std::move(aDNSSuffixList)); + + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + obs->AddObserver(this, "network:connectivity-service:dns-checks-complete", + true); + obs->AddObserver(this, "network:connectivity-service:ip-checks-complete", + true); +} + +NS_IMETHODIMP +TRRServiceChild::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) { + if (!strcmp(aTopic, "network:connectivity-service:ip-checks-complete") || + !strcmp(aTopic, "network:connectivity-service:dns-checks-complete")) { + Unused << SendNotifyNetworkConnectivityServiceObservers( + nsPrintfCString("%s-from-socket-process", aTopic)); + } + + return NS_OK; +} + +mozilla::ipc::IPCResult TRRServiceChild::RecvUpdatePlatformDNSInformation( + nsTArray<nsCString>&& aDNSSuffixList) { + TRRService::Get()->RebuildSuffixList(std::move(aDNSSuffixList)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult TRRServiceChild::RecvUpdateParentalControlEnabled( + const bool& aEnabled) { + TRRService::Get()->mParentalControlEnabled = aEnabled; + return IPC_OK(); +} + +mozilla::ipc::IPCResult TRRServiceChild::RecvClearDNSCache( + const bool& aTrrToo) { + Unused << sDNSService->ClearCache(aTrrToo); + return IPC_OK(); +} + +mozilla::ipc::IPCResult TRRServiceChild::RecvSetDetectedTrrURI( + const nsCString& aURI) { + TRRService::Get()->SetDetectedTrrURI(aURI); + return IPC_OK(); +} + +mozilla::ipc::IPCResult TRRServiceChild::RecvSetDefaultTRRConnectionInfo( + Maybe<HttpConnectionInfoCloneArgs>&& aArgs) { + if (!aArgs) { + TRRService::Get()->SetDefaultTRRConnectionInfo(nullptr); + return IPC_OK(); + } + + RefPtr<nsHttpConnectionInfo> cinfo = + nsHttpConnectionInfo::DeserializeHttpConnectionInfoCloneArgs(aArgs.ref()); + TRRService::Get()->SetDefaultTRRConnectionInfo(cinfo); + return IPC_OK(); +} + +mozilla::ipc::IPCResult TRRServiceChild::RecvUpdateEtcHosts( + nsTArray<nsCString>&& aHosts) { + TRRService::Get()->AddEtcHosts(aHosts); + return IPC_OK(); +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/TRRServiceChild.h b/netwerk/dns/TRRServiceChild.h new file mode 100644 index 0000000000..f9c6136f70 --- /dev/null +++ b/netwerk/dns/TRRServiceChild.h @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_TRRServiceChild_h +#define mozilla_net_TRRServiceChild_h + +#include "mozilla/net/PTRRServiceChild.h" +#include "nsIObserver.h" +#include "nsWeakReference.h" + +namespace mozilla { + +namespace ipc { +class FileDescriptor; +} // namespace ipc + +namespace net { + +class TRRServiceChild : public PTRRServiceChild, + public nsIObserver, + public nsSupportsWeakReference { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + + TRRServiceChild(); + static TRRServiceChild* GetSingleton(); + + void Init(const bool& aCaptiveIsPassed, const bool& aParentalControlEnabled, + nsTArray<nsCString>&& aDNSSuffixList); + mozilla::ipc::IPCResult RecvNotifyObserver(const nsCString& aTopic, + const nsString& aData); + mozilla::ipc::IPCResult RecvUpdatePlatformDNSInformation( + nsTArray<nsCString>&& aDNSSuffixList); + mozilla::ipc::IPCResult RecvUpdateParentalControlEnabled( + const bool& aEnabled); + mozilla::ipc::IPCResult RecvClearDNSCache(const bool& aTrrToo); + mozilla::ipc::IPCResult RecvSetDetectedTrrURI(const nsCString& aURI); + mozilla::ipc::IPCResult RecvSetDefaultTRRConnectionInfo( + Maybe<HttpConnectionInfoCloneArgs>&& aArgs); + mozilla::ipc::IPCResult RecvUpdateEtcHosts(nsTArray<nsCString>&& aHosts); + + private: + virtual ~TRRServiceChild(); +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_TRRServiceChild_h diff --git a/netwerk/dns/TRRServiceParent.cpp b/netwerk/dns/TRRServiceParent.cpp new file mode 100644 index 0000000000..d5c072921b --- /dev/null +++ b/netwerk/dns/TRRServiceParent.cpp @@ -0,0 +1,239 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/net/TRRServiceParent.h" + +#include "mozilla/ipc/FileDescriptor.h" +#include "mozilla/net/SocketProcessParent.h" +#include "mozilla/psm/PSMIPCTypes.h" +#include "mozilla/Preferences.h" +#include "mozilla/Unused.h" +#include "nsHttpConnectionInfo.h" +#include "nsICaptivePortalService.h" +#include "nsIParentalControlsService.h" +#include "nsINetworkLinkService.h" +#include "nsIObserverService.h" +#include "nsIOService.h" +#include "nsNetCID.h" +#include "TRRService.h" + +#include "DNSLogging.h" + +namespace mozilla { +namespace net { + +static Atomic<TRRServiceParent*> sTRRServiceParentPtr; + +static const char* gTRRUriCallbackPrefs[] = { + "network.trr.uri", "network.trr.default_provider_uri", + "network.trr.mode", kRolloutURIPref, + kRolloutModePref, nullptr, +}; + +NS_IMPL_ISUPPORTS_INHERITED(TRRServiceParent, TRRServiceBase, nsIObserver, + nsISupportsWeakReference) + +TRRServiceParent::~TRRServiceParent() = default; + +void TRRServiceParent::Init() { + MOZ_ASSERT(gIOService); + + if (!gIOService->SocketProcessReady()) { + RefPtr<TRRServiceParent> self = this; + gIOService->CallOrWaitForSocketProcess([self]() { self->Init(); }); + return; + } + + SocketProcessParent* socketParent = SocketProcessParent::GetSingleton(); + if (!socketParent) { + return; + } + + nsCOMPtr<nsIObserverService> obs = + static_cast<nsIObserverService*>(gIOService); + TRRService::AddObserver(this, obs); + + bool captiveIsPassed = TRRService::CheckCaptivePortalIsPassed(); + bool parentalControlEnabled = TRRService::GetParentalControlEnabledInternal(); + + nsCOMPtr<nsINetworkLinkService> nls = + do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID); + nsTArray<nsCString> suffixList; + if (nls) { + nls->GetDnsSuffixList(suffixList); + } + + Preferences::RegisterPrefixCallbacks(TRRServiceParent::PrefsChanged, + gTRRUriCallbackPrefs, this); + prefsChanged(nullptr); + + if (socketParent->SendPTRRServiceConstructor( + this, captiveIsPassed, parentalControlEnabled, suffixList)) { + sTRRServiceParentPtr = this; + } +} + +NS_IMETHODIMP +TRRServiceParent::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) { + if (!strcmp(aTopic, NS_DNS_SUFFIX_LIST_UPDATED_TOPIC) || + !strcmp(aTopic, NS_NETWORK_LINK_TOPIC)) { + nsCOMPtr<nsINetworkLinkService> link = do_QueryInterface(aSubject); + // The network link service notification normally passes itself as the + // subject, but some unit tests will sometimes pass a null subject. + if (link) { + nsTArray<nsCString> suffixList; + link->GetDnsSuffixList(suffixList); + Unused << SendUpdatePlatformDNSInformation(suffixList); + } + + if (!strcmp(aTopic, NS_NETWORK_LINK_TOPIC) && mURISetByDetection) { + CheckURIPrefs(); + } + } + + return NS_OK; +} + +mozilla::ipc::IPCResult +TRRServiceParent::RecvNotifyNetworkConnectivityServiceObservers( + const nsCString& aTopic) { + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + obs->NotifyObservers(nullptr, aTopic.get(), nullptr); + } + return IPC_OK(); +} + +bool TRRServiceParent::MaybeSetPrivateURI(const nsACString& aURI) { + nsAutoCString newURI(aURI); + ProcessURITemplate(newURI); + + if (mPrivateURI.Equals(newURI)) { + return false; + } + + mPrivateURI = newURI; + AsyncCreateTRRConnectionInfo(mPrivateURI); + + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + obs->NotifyObservers(nullptr, NS_NETWORK_TRR_URI_CHANGED_TOPIC, nullptr); + } + return true; +} + +void TRRServiceParent::SetDetectedTrrURI(const nsACString& aURI) { + if (!mURIPref.IsEmpty()) { + return; + } + + mURISetByDetection = MaybeSetPrivateURI(aURI); + gIOService->CallOrWaitForSocketProcess( + [self = RefPtr{this}, uri = nsAutoCString(aURI)]() { + Unused << self->SendSetDetectedTrrURI(uri); + }); +} + +void TRRServiceParent::GetURI(nsACString& aURI) { + // We don't need a lock here, since mPrivateURI is only touched on main + // thread. + MOZ_ASSERT(NS_IsMainThread()); + aURI = mPrivateURI; +} + +void TRRServiceParent::UpdateParentalControlEnabled() { + bool enabled = TRRService::GetParentalControlEnabledInternal(); + RefPtr<TRRServiceParent> self = this; + gIOService->CallOrWaitForSocketProcess([self, enabled]() { + Unused << self->SendUpdateParentalControlEnabled(enabled); + }); +} + +// static +void TRRServiceParent::PrefsChanged(const char* aName, void* aSelf) { + static_cast<TRRServiceParent*>(aSelf)->prefsChanged(aName); +} + +void TRRServiceParent::prefsChanged(const char* aName) { + if (!aName || !strcmp(aName, "network.trr.uri") || + !strcmp(aName, "network.trr.default_provider_uri") || + !strcmp(aName, kRolloutURIPref) || + !strcmp(aName, "network.trr.ohttp.uri")) { + OnTRRURIChange(); + } + + if (!aName || !strcmp(aName, "network.trr.mode") || + !strcmp(aName, kRolloutModePref)) { + OnTRRModeChange(); + } +} + +void TRRServiceParent::ActorDestroy(ActorDestroyReason why) { + sTRRServiceParentPtr = nullptr; + Preferences::UnregisterPrefixCallbacks(TRRServiceParent::PrefsChanged, + gTRRUriCallbackPrefs, this); +} + +NS_IMETHODIMP TRRServiceParent::OnProxyConfigChanged() { + LOG(("TRRServiceParent::OnProxyConfigChanged")); + + AsyncCreateTRRConnectionInfo(mPrivateURI); + return NS_OK; +} + +void TRRServiceParent::SetDefaultTRRConnectionInfo( + nsHttpConnectionInfo* aConnInfo) { + TRRServiceBase::SetDefaultTRRConnectionInfo(aConnInfo); + + if (!CanSend()) { + return; + } + + if (!aConnInfo) { + Unused << SendSetDefaultTRRConnectionInfo(Nothing()); + return; + } + + HttpConnectionInfoCloneArgs infoArgs; + nsHttpConnectionInfo::SerializeHttpConnectionInfo(aConnInfo, infoArgs); + Unused << SendSetDefaultTRRConnectionInfo(Some(infoArgs)); +} + +mozilla::ipc::IPCResult TRRServiceParent::RecvInitTRRConnectionInfo() { + InitTRRConnectionInfo(); + return IPC_OK(); +} + +mozilla::ipc::IPCResult TRRServiceParent::RecvSetConfirmationState( + uint32_t aNewState) { + mConfirmationState = aNewState; + return IPC_OK(); +} + +void TRRServiceParent::ReadEtcHostsFile() { + if (!sTRRServiceParentPtr) { + return; + } + + DoReadEtcHostsFile([](const nsTArray<nsCString>* aArray) -> bool { + RefPtr<TRRServiceParent> service(sTRRServiceParentPtr); + if (service && aArray) { + nsTArray<nsCString> hosts(aArray->Clone()); + NS_DispatchToMainThread(NS_NewRunnableFunction( + "TRRServiceParent::ReadEtcHostsFile", + [service, hosts = std::move(hosts)]() mutable { + if (service->CanSend()) { + Unused << service->SendUpdateEtcHosts(hosts); + } + })); + } + return !!service; + }); +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/TRRServiceParent.h b/netwerk/dns/TRRServiceParent.h new file mode 100644 index 0000000000..c61310c029 --- /dev/null +++ b/netwerk/dns/TRRServiceParent.h @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_TRRServiceParent_h +#define mozilla_net_TRRServiceParent_h + +#include "mozilla/net/PTRRServiceParent.h" +#include "mozilla/net/TRRServiceBase.h" +#include "nsIObserver.h" +#include "nsWeakReference.h" + +namespace mozilla { +namespace net { + +class TRRServiceParent : public TRRServiceBase, + public nsIObserver, + public nsSupportsWeakReference, + public PTRRServiceParent { + public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIOBSERVER + NS_DECL_NSIPROXYCONFIGCHANGEDCALLBACK + + TRRServiceParent() = default; + void Init(); + void UpdateParentalControlEnabled(); + static void PrefsChanged(const char* aName, void* aSelf); + void SetDetectedTrrURI(const nsACString& aURI); + bool MaybeSetPrivateURI(const nsACString& aURI) override; + void GetURI(nsACString& result) override; + mozilla::ipc::IPCResult RecvNotifyNetworkConnectivityServiceObservers( + const nsCString& aTopic); + mozilla::ipc::IPCResult RecvInitTRRConnectionInfo(); + mozilla::ipc::IPCResult RecvSetConfirmationState(uint32_t aNewState); + uint32_t GetConfirmationState() { return mConfirmationState; } + virtual void ReadEtcHostsFile() override; + + private: + virtual ~TRRServiceParent(); + virtual void ActorDestroy(ActorDestroyReason why) override; + void prefsChanged(const char* aName); + void SetDefaultTRRConnectionInfo(nsHttpConnectionInfo* aConnInfo) override; + uint32_t mConfirmationState = 0; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_TRRServiceParent_h diff --git a/netwerk/dns/effective_tld_names.dat b/netwerk/dns/effective_tld_names.dat new file mode 100644 index 0000000000..c2b813fa68 --- /dev/null +++ b/netwerk/dns/effective_tld_names.dat @@ -0,0 +1,15582 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +// Please pull this list from, and only from https://publicsuffix.org/list/public_suffix_list.dat, +// rather than any other VCS sites. Pulling from any other URL is not guaranteed to be supported. + +// Instructions on pulling and using this list can be found at https://publicsuffix.org/list/. + +// ===BEGIN ICANN DOMAINS=== + +// ac : http://nic.ac/rules.htm +ac +com.ac +edu.ac +gov.ac +net.ac +mil.ac +org.ac + +// ad : https://en.wikipedia.org/wiki/.ad +ad +nom.ad + +// ae : https://tdra.gov.ae/en/aeda/ae-policies +ae +co.ae +net.ae +org.ae +sch.ae +ac.ae +gov.ae +mil.ae + +// aero : see https://www.information.aero/index.php?id=66 +aero +accident-investigation.aero +accident-prevention.aero +aerobatic.aero +aeroclub.aero +aerodrome.aero +agents.aero +aircraft.aero +airline.aero +airport.aero +air-surveillance.aero +airtraffic.aero +air-traffic-control.aero +ambulance.aero +amusement.aero +association.aero +author.aero +ballooning.aero +broker.aero +caa.aero +cargo.aero +catering.aero +certification.aero +championship.aero +charter.aero +civilaviation.aero +club.aero +conference.aero +consultant.aero +consulting.aero +control.aero +council.aero +crew.aero +design.aero +dgca.aero +educator.aero +emergency.aero +engine.aero +engineer.aero +entertainment.aero +equipment.aero +exchange.aero +express.aero +federation.aero +flight.aero +fuel.aero +gliding.aero +government.aero +groundhandling.aero +group.aero +hanggliding.aero +homebuilt.aero +insurance.aero +journal.aero +journalist.aero +leasing.aero +logistics.aero +magazine.aero +maintenance.aero +media.aero +microlight.aero +modelling.aero +navigation.aero +parachuting.aero +paragliding.aero +passenger-association.aero +pilot.aero +press.aero +production.aero +recreation.aero +repbody.aero +res.aero +research.aero +rotorcraft.aero +safety.aero +scientist.aero +services.aero +show.aero +skydiving.aero +software.aero +student.aero +trader.aero +trading.aero +trainer.aero +union.aero +workinggroup.aero +works.aero + +// af : http://www.nic.af/help.jsp +af +gov.af +com.af +org.af +net.af +edu.af + +// ag : http://www.nic.ag/prices.htm +ag +com.ag +org.ag +net.ag +co.ag +nom.ag + +// ai : http://nic.com.ai/ +ai +off.ai +com.ai +net.ai +org.ai + +// al : http://www.ert.gov.al/ert_alb/faq_det.html?Id=31 +al +com.al +edu.al +gov.al +mil.al +net.al +org.al + +// am : https://www.amnic.net/policy/en/Policy_EN.pdf +am +co.am +com.am +commune.am +net.am +org.am + +// ao : https://en.wikipedia.org/wiki/.ao +// http://www.dns.ao/REGISTR.DOC +ao +ed.ao +gv.ao +og.ao +co.ao +pb.ao +it.ao + +// aq : https://en.wikipedia.org/wiki/.aq +aq + +// ar : https://nic.ar/es/nic-argentina/normativa +ar +bet.ar +com.ar +coop.ar +edu.ar +gob.ar +gov.ar +int.ar +mil.ar +musica.ar +mutual.ar +net.ar +org.ar +senasa.ar +tur.ar + +// arpa : https://en.wikipedia.org/wiki/.arpa +// Confirmed by registry <iana-questions@icann.org> 2008-06-18 +arpa +e164.arpa +in-addr.arpa +ip6.arpa +iris.arpa +uri.arpa +urn.arpa + +// as : https://en.wikipedia.org/wiki/.as +as +gov.as + +// asia : https://en.wikipedia.org/wiki/.asia +asia + +// at : https://en.wikipedia.org/wiki/.at +// Confirmed by registry <it@nic.at> 2008-06-17 +at +ac.at +co.at +gv.at +or.at +sth.ac.at + +// au : https://en.wikipedia.org/wiki/.au +// http://www.auda.org.au/ +au +// 2LDs +com.au +net.au +org.au +edu.au +gov.au +asn.au +id.au +// Historic 2LDs (closed to new registration, but sites still exist) +info.au +conf.au +oz.au +// CGDNs - http://www.cgdn.org.au/ +act.au +nsw.au +nt.au +qld.au +sa.au +tas.au +vic.au +wa.au +// 3LDs +act.edu.au +catholic.edu.au +// eq.edu.au - Removed at the request of the Queensland Department of Education +nsw.edu.au +nt.edu.au +qld.edu.au +sa.edu.au +tas.edu.au +vic.edu.au +wa.edu.au +// act.gov.au Bug 984824 - Removed at request of Greg Tankard +// nsw.gov.au Bug 547985 - Removed at request of <Shae.Donelan@services.nsw.gov.au> +// nt.gov.au Bug 940478 - Removed at request of Greg Connors <Greg.Connors@nt.gov.au> +qld.gov.au +sa.gov.au +tas.gov.au +vic.gov.au +wa.gov.au +// 4LDs +// education.tas.edu.au - Removed at the request of the Department of Education Tasmania +schools.nsw.edu.au + +// aw : https://en.wikipedia.org/wiki/.aw +aw +com.aw + +// ax : https://en.wikipedia.org/wiki/.ax +ax + +// az : https://en.wikipedia.org/wiki/.az +az +com.az +net.az +int.az +gov.az +org.az +edu.az +info.az +pp.az +mil.az +name.az +pro.az +biz.az + +// ba : http://nic.ba/users_data/files/pravilnik_o_registraciji.pdf +ba +com.ba +edu.ba +gov.ba +mil.ba +net.ba +org.ba + +// bb : https://en.wikipedia.org/wiki/.bb +bb +biz.bb +co.bb +com.bb +edu.bb +gov.bb +info.bb +net.bb +org.bb +store.bb +tv.bb + +// bd : https://en.wikipedia.org/wiki/.bd +*.bd + +// be : https://en.wikipedia.org/wiki/.be +// Confirmed by registry <tech@dns.be> 2008-06-08 +be +ac.be + +// bf : https://en.wikipedia.org/wiki/.bf +bf +gov.bf + +// bg : https://en.wikipedia.org/wiki/.bg +// https://www.register.bg/user/static/rules/en/index.html +bg +a.bg +b.bg +c.bg +d.bg +e.bg +f.bg +g.bg +h.bg +i.bg +j.bg +k.bg +l.bg +m.bg +n.bg +o.bg +p.bg +q.bg +r.bg +s.bg +t.bg +u.bg +v.bg +w.bg +x.bg +y.bg +z.bg +0.bg +1.bg +2.bg +3.bg +4.bg +5.bg +6.bg +7.bg +8.bg +9.bg + +// bh : https://en.wikipedia.org/wiki/.bh +bh +com.bh +edu.bh +net.bh +org.bh +gov.bh + +// bi : https://en.wikipedia.org/wiki/.bi +// http://whois.nic.bi/ +bi +co.bi +com.bi +edu.bi +or.bi +org.bi + +// biz : https://en.wikipedia.org/wiki/.biz +biz + +// bj : https://nic.bj/bj-suffixes.txt +// submitted by registry <contact@nic.bj> +bj +africa.bj +agro.bj +architectes.bj +assur.bj +avocats.bj +co.bj +com.bj +eco.bj +econo.bj +edu.bj +info.bj +loisirs.bj +money.bj +net.bj +org.bj +ote.bj +resto.bj +restaurant.bj +tourism.bj +univ.bj + +// bm : http://www.bermudanic.bm/dnr-text.txt +bm +com.bm +edu.bm +gov.bm +net.bm +org.bm + +// bn : http://www.bnnic.bn/faqs +bn +com.bn +edu.bn +gov.bn +net.bn +org.bn + +// bo : https://nic.bo/delegacion2015.php#h-1.10 +bo +com.bo +edu.bo +gob.bo +int.bo +org.bo +net.bo +mil.bo +tv.bo +web.bo +// Social Domains +academia.bo +agro.bo +arte.bo +blog.bo +bolivia.bo +ciencia.bo +cooperativa.bo +democracia.bo +deporte.bo +ecologia.bo +economia.bo +empresa.bo +indigena.bo +industria.bo +info.bo +medicina.bo +movimiento.bo +musica.bo +natural.bo +nombre.bo +noticias.bo +patria.bo +politica.bo +profesional.bo +plurinacional.bo +pueblo.bo +revista.bo +salud.bo +tecnologia.bo +tksat.bo +transporte.bo +wiki.bo + +// br : http://registro.br/dominio/categoria.html +// Submitted by registry <fneves@registro.br> +br +9guacu.br +abc.br +adm.br +adv.br +agr.br +aju.br +am.br +anani.br +aparecida.br +app.br +arq.br +art.br +ato.br +b.br +barueri.br +belem.br +bhz.br +bib.br +bio.br +blog.br +bmd.br +boavista.br +bsb.br +campinagrande.br +campinas.br +caxias.br +cim.br +cng.br +cnt.br +com.br +contagem.br +coop.br +coz.br +cri.br +cuiaba.br +curitiba.br +def.br +des.br +det.br +dev.br +ecn.br +eco.br +edu.br +emp.br +enf.br +eng.br +esp.br +etc.br +eti.br +far.br +feira.br +flog.br +floripa.br +fm.br +fnd.br +fortal.br +fot.br +foz.br +fst.br +g12.br +geo.br +ggf.br +goiania.br +gov.br +// gov.br 26 states + df https://en.wikipedia.org/wiki/States_of_Brazil +ac.gov.br +al.gov.br +am.gov.br +ap.gov.br +ba.gov.br +ce.gov.br +df.gov.br +es.gov.br +go.gov.br +ma.gov.br +mg.gov.br +ms.gov.br +mt.gov.br +pa.gov.br +pb.gov.br +pe.gov.br +pi.gov.br +pr.gov.br +rj.gov.br +rn.gov.br +ro.gov.br +rr.gov.br +rs.gov.br +sc.gov.br +se.gov.br +sp.gov.br +to.gov.br +gru.br +imb.br +ind.br +inf.br +jab.br +jampa.br +jdf.br +joinville.br +jor.br +jus.br +leg.br +lel.br +log.br +londrina.br +macapa.br +maceio.br +manaus.br +maringa.br +mat.br +med.br +mil.br +morena.br +mp.br +mus.br +natal.br +net.br +niteroi.br +*.nom.br +not.br +ntr.br +odo.br +ong.br +org.br +osasco.br +palmas.br +poa.br +ppg.br +pro.br +psc.br +psi.br +pvh.br +qsl.br +radio.br +rec.br +recife.br +rep.br +ribeirao.br +rio.br +riobranco.br +riopreto.br +salvador.br +sampa.br +santamaria.br +santoandre.br +saobernardo.br +saogonca.br +seg.br +sjc.br +slg.br +slz.br +sorocaba.br +srv.br +taxi.br +tc.br +tec.br +teo.br +the.br +tmp.br +trd.br +tur.br +tv.br +udi.br +vet.br +vix.br +vlog.br +wiki.br +zlg.br + +// bs : http://www.nic.bs/rules.html +bs +com.bs +net.bs +org.bs +edu.bs +gov.bs + +// bt : https://en.wikipedia.org/wiki/.bt +bt +com.bt +edu.bt +gov.bt +net.bt +org.bt + +// bv : No registrations at this time. +// Submitted by registry <jarle@uninett.no> +bv + +// bw : https://en.wikipedia.org/wiki/.bw +// http://www.gobin.info/domainname/bw.doc +// list of other 2nd level tlds ? +bw +co.bw +org.bw + +// by : https://en.wikipedia.org/wiki/.by +// http://tld.by/rules_2006_en.html +// list of other 2nd level tlds ? +by +gov.by +mil.by +// Official information does not indicate that com.by is a reserved +// second-level domain, but it's being used as one (see www.google.com.by and +// www.yahoo.com.by, for example), so we list it here for safety's sake. +com.by + +// http://hoster.by/ +of.by + +// bz : https://en.wikipedia.org/wiki/.bz +// http://www.belizenic.bz/ +bz +com.bz +net.bz +org.bz +edu.bz +gov.bz + +// ca : https://en.wikipedia.org/wiki/.ca +ca +// ca geographical names +ab.ca +bc.ca +mb.ca +nb.ca +nf.ca +nl.ca +ns.ca +nt.ca +nu.ca +on.ca +pe.ca +qc.ca +sk.ca +yk.ca +// gc.ca: https://en.wikipedia.org/wiki/.gc.ca +// see also: http://registry.gc.ca/en/SubdomainFAQ +gc.ca + +// cat : https://en.wikipedia.org/wiki/.cat +cat + +// cc : https://en.wikipedia.org/wiki/.cc +cc + +// cd : https://en.wikipedia.org/wiki/.cd +// see also: https://www.nic.cd/domain/insertDomain_2.jsp?act=1 +cd +gov.cd + +// cf : https://en.wikipedia.org/wiki/.cf +cf + +// cg : https://en.wikipedia.org/wiki/.cg +cg + +// ch : https://en.wikipedia.org/wiki/.ch +ch + +// ci : https://en.wikipedia.org/wiki/.ci +// http://www.nic.ci/index.php?page=charte +ci +org.ci +or.ci +com.ci +co.ci +edu.ci +ed.ci +ac.ci +net.ci +go.ci +asso.ci +aéroport.ci +int.ci +presse.ci +md.ci +gouv.ci + +// ck : https://en.wikipedia.org/wiki/.ck +*.ck +!www.ck + +// cl : https://www.nic.cl +// Confirmed by .CL registry <hsalgado@nic.cl> +cl +co.cl +gob.cl +gov.cl +mil.cl + +// cm : https://en.wikipedia.org/wiki/.cm plus bug 981927 +cm +co.cm +com.cm +gov.cm +net.cm + +// cn : https://en.wikipedia.org/wiki/.cn +// Submitted by registry <tanyaling@cnnic.cn> +cn +ac.cn +com.cn +edu.cn +gov.cn +net.cn +org.cn +mil.cn +公司.cn +网络.cn +網絡.cn +// cn geographic names +ah.cn +bj.cn +cq.cn +fj.cn +gd.cn +gs.cn +gz.cn +gx.cn +ha.cn +hb.cn +he.cn +hi.cn +hl.cn +hn.cn +jl.cn +js.cn +jx.cn +ln.cn +nm.cn +nx.cn +qh.cn +sc.cn +sd.cn +sh.cn +sn.cn +sx.cn +tj.cn +xj.cn +xz.cn +yn.cn +zj.cn +hk.cn +mo.cn +tw.cn + +// co : https://en.wikipedia.org/wiki/.co +// Submitted by registry <tecnico@uniandes.edu.co> +co +arts.co +com.co +edu.co +firm.co +gov.co +info.co +int.co +mil.co +net.co +nom.co +org.co +rec.co +web.co + +// com : https://en.wikipedia.org/wiki/.com +com + +// coop : https://en.wikipedia.org/wiki/.coop +coop + +// cr : http://www.nic.cr/niccr_publico/showRegistroDominiosScreen.do +cr +ac.cr +co.cr +ed.cr +fi.cr +go.cr +or.cr +sa.cr + +// cu : https://en.wikipedia.org/wiki/.cu +cu +com.cu +edu.cu +org.cu +net.cu +gov.cu +inf.cu + +// cv : https://en.wikipedia.org/wiki/.cv +// cv : http://www.dns.cv/tldcv_portal/do?com=DS;5446457100;111;+PAGE(4000018)+K-CAT-CODIGO(RDOM)+RCNT(100); <- registration rules +cv +com.cv +edu.cv +int.cv +nome.cv +org.cv + +// cw : http://www.una.cw/cw_registry/ +// Confirmed by registry <registry@una.net> 2013-03-26 +cw +com.cw +edu.cw +net.cw +org.cw + +// cx : https://en.wikipedia.org/wiki/.cx +// list of other 2nd level tlds ? +cx +gov.cx + +// cy : http://www.nic.cy/ +// Submitted by registry Panayiotou Fotia <cydns@ucy.ac.cy> +// namespace policies URL https://www.nic.cy/portal//sites/default/files/symfonia_gia_eggrafi.pdf +cy +ac.cy +biz.cy +com.cy +ekloges.cy +gov.cy +ltd.cy +mil.cy +net.cy +org.cy +press.cy +pro.cy +tm.cy + +// cz : https://en.wikipedia.org/wiki/.cz +cz + +// de : https://en.wikipedia.org/wiki/.de +// Confirmed by registry <ops@denic.de> (with technical +// reservations) 2008-07-01 +de + +// dj : https://en.wikipedia.org/wiki/.dj +dj + +// dk : https://en.wikipedia.org/wiki/.dk +// Confirmed by registry <robert@dk-hostmaster.dk> 2008-06-17 +dk + +// dm : https://en.wikipedia.org/wiki/.dm +dm +com.dm +net.dm +org.dm +edu.dm +gov.dm + +// do : https://en.wikipedia.org/wiki/.do +do +art.do +com.do +edu.do +gob.do +gov.do +mil.do +net.do +org.do +sld.do +web.do + +// dz : http://www.nic.dz/images/pdf_nic/charte.pdf +dz +art.dz +asso.dz +com.dz +edu.dz +gov.dz +org.dz +net.dz +pol.dz +soc.dz +tm.dz + +// ec : http://www.nic.ec/reg/paso1.asp +// Submitted by registry <vabboud@nic.ec> +ec +com.ec +info.ec +net.ec +fin.ec +k12.ec +med.ec +pro.ec +org.ec +edu.ec +gov.ec +gob.ec +mil.ec + +// edu : https://en.wikipedia.org/wiki/.edu +edu + +// ee : http://www.eenet.ee/EENet/dom_reeglid.html#lisa_B +ee +edu.ee +gov.ee +riik.ee +lib.ee +med.ee +com.ee +pri.ee +aip.ee +org.ee +fie.ee + +// eg : https://en.wikipedia.org/wiki/.eg +eg +com.eg +edu.eg +eun.eg +gov.eg +mil.eg +name.eg +net.eg +org.eg +sci.eg + +// er : https://en.wikipedia.org/wiki/.er +*.er + +// es : https://www.nic.es/site_ingles/ingles/dominios/index.html +es +com.es +nom.es +org.es +gob.es +edu.es + +// et : https://en.wikipedia.org/wiki/.et +et +com.et +gov.et +org.et +edu.et +biz.et +name.et +info.et +net.et + +// eu : https://en.wikipedia.org/wiki/.eu +eu + +// fi : https://en.wikipedia.org/wiki/.fi +fi +// aland.fi : https://en.wikipedia.org/wiki/.ax +// This domain is being phased out in favor of .ax. As there are still many +// domains under aland.fi, we still keep it on the list until aland.fi is +// completely removed. +// TODO: Check for updates (expected to be phased out around Q1/2009) +aland.fi + +// fj : http://domains.fj/ +// Submitted by registry <garth.miller@cocca.org.nz> 2020-02-11 +fj +ac.fj +biz.fj +com.fj +gov.fj +info.fj +mil.fj +name.fj +net.fj +org.fj +pro.fj + +// fk : https://en.wikipedia.org/wiki/.fk +*.fk + +// fm : https://en.wikipedia.org/wiki/.fm +com.fm +edu.fm +net.fm +org.fm +fm + +// fo : https://en.wikipedia.org/wiki/.fo +fo + +// fr : https://www.afnic.fr/ https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf +fr +asso.fr +com.fr +gouv.fr +nom.fr +prd.fr +tm.fr +// Other SLDs now selfmanaged out of AFNIC range. Former "domaines sectoriels", still registration suffixes +avoues.fr +cci.fr +greta.fr +huissier-justice.fr + +// ga : https://en.wikipedia.org/wiki/.ga +ga + +// gb : This registry is effectively dormant +// Submitted by registry <Damien.Shaw@ja.net> +gb + +// gd : https://en.wikipedia.org/wiki/.gd +edu.gd +gov.gd +gd + +// ge : http://www.nic.net.ge/policy_en.pdf +ge +com.ge +edu.ge +gov.ge +org.ge +mil.ge +net.ge +pvt.ge + +// gf : https://en.wikipedia.org/wiki/.gf +gf + +// gg : http://www.channelisles.net/register-domains/ +// Confirmed by registry <nigel@channelisles.net> 2013-11-28 +gg +co.gg +net.gg +org.gg + +// gh : https://en.wikipedia.org/wiki/.gh +// see also: http://www.nic.gh/reg_now.php +// Although domains directly at second level are not possible at the moment, +// they have been possible for some time and may come back. +gh +com.gh +edu.gh +gov.gh +org.gh +mil.gh + +// gi : http://www.nic.gi/rules.html +gi +com.gi +ltd.gi +gov.gi +mod.gi +edu.gi +org.gi + +// gl : https://en.wikipedia.org/wiki/.gl +// http://nic.gl +gl +co.gl +com.gl +edu.gl +net.gl +org.gl + +// gm : http://www.nic.gm/htmlpages%5Cgm-policy.htm +gm + +// gn : http://psg.com/dns/gn/gn.txt +// Submitted by registry <randy@psg.com> +gn +ac.gn +com.gn +edu.gn +gov.gn +org.gn +net.gn + +// gov : https://en.wikipedia.org/wiki/.gov +gov + +// gp : http://www.nic.gp/index.php?lang=en +gp +com.gp +net.gp +mobi.gp +edu.gp +org.gp +asso.gp + +// gq : https://en.wikipedia.org/wiki/.gq +gq + +// gr : https://grweb.ics.forth.gr/english/1617-B-2005.html +// Submitted by registry <segred@ics.forth.gr> +gr +com.gr +edu.gr +net.gr +org.gr +gov.gr + +// gs : https://en.wikipedia.org/wiki/.gs +gs + +// gt : https://www.gt/sitio/registration_policy.php?lang=en +gt +com.gt +edu.gt +gob.gt +ind.gt +mil.gt +net.gt +org.gt + +// gu : http://gadao.gov.gu/register.html +// University of Guam : https://www.uog.edu +// Submitted by uognoc@triton.uog.edu +gu +com.gu +edu.gu +gov.gu +guam.gu +info.gu +net.gu +org.gu +web.gu + +// gw : https://en.wikipedia.org/wiki/.gw +// gw : https://nic.gw/regras/ +gw + +// gy : https://en.wikipedia.org/wiki/.gy +// http://registry.gy/ +gy +co.gy +com.gy +edu.gy +gov.gy +net.gy +org.gy + +// hk : https://www.hkirc.hk +// Submitted by registry <hk.tech@hkirc.hk> +hk +com.hk +edu.hk +gov.hk +idv.hk +net.hk +org.hk +公司.hk +教育.hk +敎育.hk +政府.hk +個人.hk +个人.hk +箇人.hk +網络.hk +网络.hk +组織.hk +網絡.hk +网絡.hk +组织.hk +組織.hk +組织.hk + +// hm : https://en.wikipedia.org/wiki/.hm +hm + +// hn : http://www.nic.hn/politicas/ps02,,05.html +hn +com.hn +edu.hn +org.hn +net.hn +mil.hn +gob.hn + +// hr : http://www.dns.hr/documents/pdf/HRTLD-regulations.pdf +hr +iz.hr +from.hr +name.hr +com.hr + +// ht : http://www.nic.ht/info/charte.cfm +ht +com.ht +shop.ht +firm.ht +info.ht +adult.ht +net.ht +pro.ht +org.ht +med.ht +art.ht +coop.ht +pol.ht +asso.ht +edu.ht +rel.ht +gouv.ht +perso.ht + +// hu : http://www.domain.hu/domain/English/sld.html +// Confirmed by registry <pasztor@iszt.hu> 2008-06-12 +hu +co.hu +info.hu +org.hu +priv.hu +sport.hu +tm.hu +2000.hu +agrar.hu +bolt.hu +casino.hu +city.hu +erotica.hu +erotika.hu +film.hu +forum.hu +games.hu +hotel.hu +ingatlan.hu +jogasz.hu +konyvelo.hu +lakas.hu +media.hu +news.hu +reklam.hu +sex.hu +shop.hu +suli.hu +szex.hu +tozsde.hu +utazas.hu +video.hu + +// id : https://pandi.id/en/domain/registration-requirements/ +id +ac.id +biz.id +co.id +desa.id +go.id +mil.id +my.id +net.id +or.id +ponpes.id +sch.id +web.id + +// ie : https://en.wikipedia.org/wiki/.ie +ie +gov.ie + +// il : http://www.isoc.org.il/domains/ +// see also: https://en.isoc.org.il/il-cctld/registration-rules +// ISOC-IL (operated by .il Registry) +il +ac.il +co.il +gov.il +idf.il +k12.il +muni.il +net.il +org.il +// xn--4dbrk0ce ("Israel", Hebrew) : IL +ישראל +// xn--4dbgdty6c.xn--4dbrk0ce. +אקדמיה.ישראל +// xn--5dbhl8d.xn--4dbrk0ce. +ישוב.ישראל +// xn--8dbq2a.xn--4dbrk0ce. +צהל.ישראל +// xn--hebda8b.xn--4dbrk0ce. +ממשל.ישראל + +// im : https://www.nic.im/ +// Submitted by registry <info@nic.im> +im +ac.im +co.im +com.im +ltd.co.im +net.im +org.im +plc.co.im +tt.im +tv.im + +// in : https://en.wikipedia.org/wiki/.in +// see also: https://registry.in/policies +// Please note, that nic.in is not an official eTLD, but used by most +// government institutions. +in +5g.in +6g.in +ac.in +ai.in +am.in +bihar.in +biz.in +business.in +ca.in +cn.in +co.in +com.in +coop.in +cs.in +delhi.in +dr.in +edu.in +er.in +firm.in +gen.in +gov.in +gujarat.in +ind.in +info.in +int.in +internet.in +io.in +me.in +mil.in +net.in +nic.in +org.in +pg.in +post.in +pro.in +res.in +travel.in +tv.in +uk.in +up.in +us.in + +// info : https://en.wikipedia.org/wiki/.info +info + +// int : https://en.wikipedia.org/wiki/.int +// Confirmed by registry <iana-questions@icann.org> 2008-06-18 +int +eu.int + +// io : http://www.nic.io/rules.htm +// list of other 2nd level tlds ? +io +com.io + +// iq : http://www.cmc.iq/english/iq/iqregister1.htm +iq +gov.iq +edu.iq +mil.iq +com.iq +org.iq +net.iq + +// ir : http://www.nic.ir/Terms_and_Conditions_ir,_Appendix_1_Domain_Rules +// Also see http://www.nic.ir/Internationalized_Domain_Names +// Two <iran>.ir entries added at request of <tech-team@nic.ir>, 2010-04-16 +ir +ac.ir +co.ir +gov.ir +id.ir +net.ir +org.ir +sch.ir +// xn--mgba3a4f16a.ir (<iran>.ir, Persian YEH) +ایران.ir +// xn--mgba3a4fra.ir (<iran>.ir, Arabic YEH) +ايران.ir + +// is : http://www.isnic.is/domain/rules.php +// Confirmed by registry <marius@isgate.is> 2008-12-06 +is +net.is +com.is +edu.is +gov.is +org.is +int.is + +// it : https://en.wikipedia.org/wiki/.it +it +gov.it +edu.it +// Reserved geo-names (regions and provinces): +// https://www.nic.it/sites/default/files/archivio/docs/Regulation_assignation_v7.1.pdf +// Regions +abr.it +abruzzo.it +aosta-valley.it +aostavalley.it +bas.it +basilicata.it +cal.it +calabria.it +cam.it +campania.it +emilia-romagna.it +emiliaromagna.it +emr.it +friuli-v-giulia.it +friuli-ve-giulia.it +friuli-vegiulia.it +friuli-venezia-giulia.it +friuli-veneziagiulia.it +friuli-vgiulia.it +friuliv-giulia.it +friulive-giulia.it +friulivegiulia.it +friulivenezia-giulia.it +friuliveneziagiulia.it +friulivgiulia.it +fvg.it +laz.it +lazio.it +lig.it +liguria.it +lom.it +lombardia.it +lombardy.it +lucania.it +mar.it +marche.it +mol.it +molise.it +piedmont.it +piemonte.it +pmn.it +pug.it +puglia.it +sar.it +sardegna.it +sardinia.it +sic.it +sicilia.it +sicily.it +taa.it +tos.it +toscana.it +trentin-sud-tirol.it +trentin-süd-tirol.it +trentin-sudtirol.it +trentin-südtirol.it +trentin-sued-tirol.it +trentin-suedtirol.it +trentino-a-adige.it +trentino-aadige.it +trentino-alto-adige.it +trentino-altoadige.it +trentino-s-tirol.it +trentino-stirol.it +trentino-sud-tirol.it +trentino-süd-tirol.it +trentino-sudtirol.it +trentino-südtirol.it +trentino-sued-tirol.it +trentino-suedtirol.it +trentino.it +trentinoa-adige.it +trentinoaadige.it +trentinoalto-adige.it +trentinoaltoadige.it +trentinos-tirol.it +trentinostirol.it +trentinosud-tirol.it +trentinosüd-tirol.it +trentinosudtirol.it +trentinosüdtirol.it +trentinosued-tirol.it +trentinosuedtirol.it +trentinsud-tirol.it +trentinsüd-tirol.it +trentinsudtirol.it +trentinsüdtirol.it +trentinsued-tirol.it +trentinsuedtirol.it +tuscany.it +umb.it +umbria.it +val-d-aosta.it +val-daosta.it +vald-aosta.it +valdaosta.it +valle-aosta.it +valle-d-aosta.it +valle-daosta.it +valleaosta.it +valled-aosta.it +valledaosta.it +vallee-aoste.it +vallée-aoste.it +vallee-d-aoste.it +vallée-d-aoste.it +valleeaoste.it +valléeaoste.it +valleedaoste.it +valléedaoste.it +vao.it +vda.it +ven.it +veneto.it +// Provinces +ag.it +agrigento.it +al.it +alessandria.it +alto-adige.it +altoadige.it +an.it +ancona.it +andria-barletta-trani.it +andria-trani-barletta.it +andriabarlettatrani.it +andriatranibarletta.it +ao.it +aosta.it +aoste.it +ap.it +aq.it +aquila.it +ar.it +arezzo.it +ascoli-piceno.it +ascolipiceno.it +asti.it +at.it +av.it +avellino.it +ba.it +balsan-sudtirol.it +balsan-südtirol.it +balsan-suedtirol.it +balsan.it +bari.it +barletta-trani-andria.it +barlettatraniandria.it +belluno.it +benevento.it +bergamo.it +bg.it +bi.it +biella.it +bl.it +bn.it +bo.it +bologna.it +bolzano-altoadige.it +bolzano.it +bozen-sudtirol.it +bozen-südtirol.it +bozen-suedtirol.it +bozen.it +br.it +brescia.it +brindisi.it +bs.it +bt.it +bulsan-sudtirol.it +bulsan-südtirol.it +bulsan-suedtirol.it +bulsan.it +bz.it +ca.it +cagliari.it +caltanissetta.it +campidano-medio.it +campidanomedio.it +campobasso.it +carbonia-iglesias.it +carboniaiglesias.it +carrara-massa.it +carraramassa.it +caserta.it +catania.it +catanzaro.it +cb.it +ce.it +cesena-forli.it +cesena-forlì.it +cesenaforli.it +cesenaforlì.it +ch.it +chieti.it +ci.it +cl.it +cn.it +co.it +como.it +cosenza.it +cr.it +cremona.it +crotone.it +cs.it +ct.it +cuneo.it +cz.it +dell-ogliastra.it +dellogliastra.it +en.it +enna.it +fc.it +fe.it +fermo.it +ferrara.it +fg.it +fi.it +firenze.it +florence.it +fm.it +foggia.it +forli-cesena.it +forlì-cesena.it +forlicesena.it +forlìcesena.it +fr.it +frosinone.it +ge.it +genoa.it +genova.it +go.it +gorizia.it +gr.it +grosseto.it +iglesias-carbonia.it +iglesiascarbonia.it +im.it +imperia.it +is.it +isernia.it +kr.it +la-spezia.it +laquila.it +laspezia.it +latina.it +lc.it +le.it +lecce.it +lecco.it +li.it +livorno.it +lo.it +lodi.it +lt.it +lu.it +lucca.it +macerata.it +mantova.it +massa-carrara.it +massacarrara.it +matera.it +mb.it +mc.it +me.it +medio-campidano.it +mediocampidano.it +messina.it +mi.it +milan.it +milano.it +mn.it +mo.it +modena.it +monza-brianza.it +monza-e-della-brianza.it +monza.it +monzabrianza.it +monzaebrianza.it +monzaedellabrianza.it +ms.it +mt.it +na.it +naples.it +napoli.it +no.it +novara.it +nu.it +nuoro.it +og.it +ogliastra.it +olbia-tempio.it +olbiatempio.it +or.it +oristano.it +ot.it +pa.it +padova.it +padua.it +palermo.it +parma.it +pavia.it +pc.it +pd.it +pe.it +perugia.it +pesaro-urbino.it +pesarourbino.it +pescara.it +pg.it +pi.it +piacenza.it +pisa.it +pistoia.it +pn.it +po.it +pordenone.it +potenza.it +pr.it +prato.it +pt.it +pu.it +pv.it +pz.it +ra.it +ragusa.it +ravenna.it +rc.it +re.it +reggio-calabria.it +reggio-emilia.it +reggiocalabria.it +reggioemilia.it +rg.it +ri.it +rieti.it +rimini.it +rm.it +rn.it +ro.it +roma.it +rome.it +rovigo.it +sa.it +salerno.it +sassari.it +savona.it +si.it +siena.it +siracusa.it +so.it +sondrio.it +sp.it +sr.it +ss.it +suedtirol.it +südtirol.it +sv.it +ta.it +taranto.it +te.it +tempio-olbia.it +tempioolbia.it +teramo.it +terni.it +tn.it +to.it +torino.it +tp.it +tr.it +trani-andria-barletta.it +trani-barletta-andria.it +traniandriabarletta.it +tranibarlettaandria.it +trapani.it +trento.it +treviso.it +trieste.it +ts.it +turin.it +tv.it +ud.it +udine.it +urbino-pesaro.it +urbinopesaro.it +va.it +varese.it +vb.it +vc.it +ve.it +venezia.it +venice.it +verbania.it +vercelli.it +verona.it +vi.it +vibo-valentia.it +vibovalentia.it +vicenza.it +viterbo.it +vr.it +vs.it +vt.it +vv.it + +// je : http://www.channelisles.net/register-domains/ +// Confirmed by registry <nigel@channelisles.net> 2013-11-28 +je +co.je +net.je +org.je + +// jm : http://www.com.jm/register.html +*.jm + +// jo : http://www.dns.jo/Registration_policy.aspx +jo +com.jo +org.jo +net.jo +edu.jo +sch.jo +gov.jo +mil.jo +name.jo + +// jobs : https://en.wikipedia.org/wiki/.jobs +jobs + +// jp : https://en.wikipedia.org/wiki/.jp +// http://jprs.co.jp/en/jpdomain.html +// Submitted by registry <info@jprs.jp> +jp +// jp organizational type names +ac.jp +ad.jp +co.jp +ed.jp +go.jp +gr.jp +lg.jp +ne.jp +or.jp +// jp prefecture type names +aichi.jp +akita.jp +aomori.jp +chiba.jp +ehime.jp +fukui.jp +fukuoka.jp +fukushima.jp +gifu.jp +gunma.jp +hiroshima.jp +hokkaido.jp +hyogo.jp +ibaraki.jp +ishikawa.jp +iwate.jp +kagawa.jp +kagoshima.jp +kanagawa.jp +kochi.jp +kumamoto.jp +kyoto.jp +mie.jp +miyagi.jp +miyazaki.jp +nagano.jp +nagasaki.jp +nara.jp +niigata.jp +oita.jp +okayama.jp +okinawa.jp +osaka.jp +saga.jp +saitama.jp +shiga.jp +shimane.jp +shizuoka.jp +tochigi.jp +tokushima.jp +tokyo.jp +tottori.jp +toyama.jp +wakayama.jp +yamagata.jp +yamaguchi.jp +yamanashi.jp +栃木.jp +愛知.jp +愛媛.jp +兵庫.jp +熊本.jp +茨城.jp +北海道.jp +千葉.jp +和歌山.jp +長崎.jp +長野.jp +新潟.jp +青森.jp +静岡.jp +東京.jp +石川.jp +埼玉.jp +三重.jp +京都.jp +佐賀.jp +大分.jp +大阪.jp +奈良.jp +宮城.jp +宮崎.jp +富山.jp +山口.jp +山形.jp +山梨.jp +岩手.jp +岐阜.jp +岡山.jp +島根.jp +広島.jp +徳島.jp +沖縄.jp +滋賀.jp +神奈川.jp +福井.jp +福岡.jp +福島.jp +秋田.jp +群馬.jp +香川.jp +高知.jp +鳥取.jp +鹿児島.jp +// jp geographic type names +// http://jprs.jp/doc/rule/saisoku-1.html +*.kawasaki.jp +*.kitakyushu.jp +*.kobe.jp +*.nagoya.jp +*.sapporo.jp +*.sendai.jp +*.yokohama.jp +!city.kawasaki.jp +!city.kitakyushu.jp +!city.kobe.jp +!city.nagoya.jp +!city.sapporo.jp +!city.sendai.jp +!city.yokohama.jp +// 4th level registration +aisai.aichi.jp +ama.aichi.jp +anjo.aichi.jp +asuke.aichi.jp +chiryu.aichi.jp +chita.aichi.jp +fuso.aichi.jp +gamagori.aichi.jp +handa.aichi.jp +hazu.aichi.jp +hekinan.aichi.jp +higashiura.aichi.jp +ichinomiya.aichi.jp +inazawa.aichi.jp +inuyama.aichi.jp +isshiki.aichi.jp +iwakura.aichi.jp +kanie.aichi.jp +kariya.aichi.jp +kasugai.aichi.jp +kira.aichi.jp +kiyosu.aichi.jp +komaki.aichi.jp +konan.aichi.jp +kota.aichi.jp +mihama.aichi.jp +miyoshi.aichi.jp +nishio.aichi.jp +nisshin.aichi.jp +obu.aichi.jp +oguchi.aichi.jp +oharu.aichi.jp +okazaki.aichi.jp +owariasahi.aichi.jp +seto.aichi.jp +shikatsu.aichi.jp +shinshiro.aichi.jp +shitara.aichi.jp +tahara.aichi.jp +takahama.aichi.jp +tobishima.aichi.jp +toei.aichi.jp +togo.aichi.jp +tokai.aichi.jp +tokoname.aichi.jp +toyoake.aichi.jp +toyohashi.aichi.jp +toyokawa.aichi.jp +toyone.aichi.jp +toyota.aichi.jp +tsushima.aichi.jp +yatomi.aichi.jp +akita.akita.jp +daisen.akita.jp +fujisato.akita.jp +gojome.akita.jp +hachirogata.akita.jp +happou.akita.jp +higashinaruse.akita.jp +honjo.akita.jp +honjyo.akita.jp +ikawa.akita.jp +kamikoani.akita.jp +kamioka.akita.jp +katagami.akita.jp +kazuno.akita.jp +kitaakita.akita.jp +kosaka.akita.jp +kyowa.akita.jp +misato.akita.jp +mitane.akita.jp +moriyoshi.akita.jp +nikaho.akita.jp +noshiro.akita.jp +odate.akita.jp +oga.akita.jp +ogata.akita.jp +semboku.akita.jp +yokote.akita.jp +yurihonjo.akita.jp +aomori.aomori.jp +gonohe.aomori.jp +hachinohe.aomori.jp +hashikami.aomori.jp +hiranai.aomori.jp +hirosaki.aomori.jp +itayanagi.aomori.jp +kuroishi.aomori.jp +misawa.aomori.jp +mutsu.aomori.jp +nakadomari.aomori.jp +noheji.aomori.jp +oirase.aomori.jp +owani.aomori.jp +rokunohe.aomori.jp +sannohe.aomori.jp +shichinohe.aomori.jp +shingo.aomori.jp +takko.aomori.jp +towada.aomori.jp +tsugaru.aomori.jp +tsuruta.aomori.jp +abiko.chiba.jp +asahi.chiba.jp +chonan.chiba.jp +chosei.chiba.jp +choshi.chiba.jp +chuo.chiba.jp +funabashi.chiba.jp +futtsu.chiba.jp +hanamigawa.chiba.jp +ichihara.chiba.jp +ichikawa.chiba.jp +ichinomiya.chiba.jp +inzai.chiba.jp +isumi.chiba.jp +kamagaya.chiba.jp +kamogawa.chiba.jp +kashiwa.chiba.jp +katori.chiba.jp +katsuura.chiba.jp +kimitsu.chiba.jp +kisarazu.chiba.jp +kozaki.chiba.jp +kujukuri.chiba.jp +kyonan.chiba.jp +matsudo.chiba.jp +midori.chiba.jp +mihama.chiba.jp +minamiboso.chiba.jp +mobara.chiba.jp +mutsuzawa.chiba.jp +nagara.chiba.jp +nagareyama.chiba.jp +narashino.chiba.jp +narita.chiba.jp +noda.chiba.jp +oamishirasato.chiba.jp +omigawa.chiba.jp +onjuku.chiba.jp +otaki.chiba.jp +sakae.chiba.jp +sakura.chiba.jp +shimofusa.chiba.jp +shirako.chiba.jp +shiroi.chiba.jp +shisui.chiba.jp +sodegaura.chiba.jp +sosa.chiba.jp +tako.chiba.jp +tateyama.chiba.jp +togane.chiba.jp +tohnosho.chiba.jp +tomisato.chiba.jp +urayasu.chiba.jp +yachimata.chiba.jp +yachiyo.chiba.jp +yokaichiba.chiba.jp +yokoshibahikari.chiba.jp +yotsukaido.chiba.jp +ainan.ehime.jp +honai.ehime.jp +ikata.ehime.jp +imabari.ehime.jp +iyo.ehime.jp +kamijima.ehime.jp +kihoku.ehime.jp +kumakogen.ehime.jp +masaki.ehime.jp +matsuno.ehime.jp +matsuyama.ehime.jp +namikata.ehime.jp +niihama.ehime.jp +ozu.ehime.jp +saijo.ehime.jp +seiyo.ehime.jp +shikokuchuo.ehime.jp +tobe.ehime.jp +toon.ehime.jp +uchiko.ehime.jp +uwajima.ehime.jp +yawatahama.ehime.jp +echizen.fukui.jp +eiheiji.fukui.jp +fukui.fukui.jp +ikeda.fukui.jp +katsuyama.fukui.jp +mihama.fukui.jp +minamiechizen.fukui.jp +obama.fukui.jp +ohi.fukui.jp +ono.fukui.jp +sabae.fukui.jp +sakai.fukui.jp +takahama.fukui.jp +tsuruga.fukui.jp +wakasa.fukui.jp +ashiya.fukuoka.jp +buzen.fukuoka.jp +chikugo.fukuoka.jp +chikuho.fukuoka.jp +chikujo.fukuoka.jp +chikushino.fukuoka.jp +chikuzen.fukuoka.jp +chuo.fukuoka.jp +dazaifu.fukuoka.jp +fukuchi.fukuoka.jp +hakata.fukuoka.jp +higashi.fukuoka.jp +hirokawa.fukuoka.jp +hisayama.fukuoka.jp +iizuka.fukuoka.jp +inatsuki.fukuoka.jp +kaho.fukuoka.jp +kasuga.fukuoka.jp +kasuya.fukuoka.jp +kawara.fukuoka.jp +keisen.fukuoka.jp +koga.fukuoka.jp +kurate.fukuoka.jp +kurogi.fukuoka.jp +kurume.fukuoka.jp +minami.fukuoka.jp +miyako.fukuoka.jp +miyama.fukuoka.jp +miyawaka.fukuoka.jp +mizumaki.fukuoka.jp +munakata.fukuoka.jp +nakagawa.fukuoka.jp +nakama.fukuoka.jp +nishi.fukuoka.jp +nogata.fukuoka.jp +ogori.fukuoka.jp +okagaki.fukuoka.jp +okawa.fukuoka.jp +oki.fukuoka.jp +omuta.fukuoka.jp +onga.fukuoka.jp +onojo.fukuoka.jp +oto.fukuoka.jp +saigawa.fukuoka.jp +sasaguri.fukuoka.jp +shingu.fukuoka.jp +shinyoshitomi.fukuoka.jp +shonai.fukuoka.jp +soeda.fukuoka.jp +sue.fukuoka.jp +tachiarai.fukuoka.jp +tagawa.fukuoka.jp +takata.fukuoka.jp +toho.fukuoka.jp +toyotsu.fukuoka.jp +tsuiki.fukuoka.jp +ukiha.fukuoka.jp +umi.fukuoka.jp +usui.fukuoka.jp +yamada.fukuoka.jp +yame.fukuoka.jp +yanagawa.fukuoka.jp +yukuhashi.fukuoka.jp +aizubange.fukushima.jp +aizumisato.fukushima.jp +aizuwakamatsu.fukushima.jp +asakawa.fukushima.jp +bandai.fukushima.jp +date.fukushima.jp +fukushima.fukushima.jp +furudono.fukushima.jp +futaba.fukushima.jp +hanawa.fukushima.jp +higashi.fukushima.jp +hirata.fukushima.jp +hirono.fukushima.jp +iitate.fukushima.jp +inawashiro.fukushima.jp +ishikawa.fukushima.jp +iwaki.fukushima.jp +izumizaki.fukushima.jp +kagamiishi.fukushima.jp +kaneyama.fukushima.jp +kawamata.fukushima.jp +kitakata.fukushima.jp +kitashiobara.fukushima.jp +koori.fukushima.jp +koriyama.fukushima.jp +kunimi.fukushima.jp +miharu.fukushima.jp +mishima.fukushima.jp +namie.fukushima.jp +nango.fukushima.jp +nishiaizu.fukushima.jp +nishigo.fukushima.jp +okuma.fukushima.jp +omotego.fukushima.jp +ono.fukushima.jp +otama.fukushima.jp +samegawa.fukushima.jp +shimogo.fukushima.jp +shirakawa.fukushima.jp +showa.fukushima.jp +soma.fukushima.jp +sukagawa.fukushima.jp +taishin.fukushima.jp +tamakawa.fukushima.jp +tanagura.fukushima.jp +tenei.fukushima.jp +yabuki.fukushima.jp +yamato.fukushima.jp +yamatsuri.fukushima.jp +yanaizu.fukushima.jp +yugawa.fukushima.jp +anpachi.gifu.jp +ena.gifu.jp +gifu.gifu.jp +ginan.gifu.jp +godo.gifu.jp +gujo.gifu.jp +hashima.gifu.jp +hichiso.gifu.jp +hida.gifu.jp +higashishirakawa.gifu.jp +ibigawa.gifu.jp +ikeda.gifu.jp +kakamigahara.gifu.jp +kani.gifu.jp +kasahara.gifu.jp +kasamatsu.gifu.jp +kawaue.gifu.jp +kitagata.gifu.jp +mino.gifu.jp +minokamo.gifu.jp +mitake.gifu.jp +mizunami.gifu.jp +motosu.gifu.jp +nakatsugawa.gifu.jp +ogaki.gifu.jp +sakahogi.gifu.jp +seki.gifu.jp +sekigahara.gifu.jp +shirakawa.gifu.jp +tajimi.gifu.jp +takayama.gifu.jp +tarui.gifu.jp +toki.gifu.jp +tomika.gifu.jp +wanouchi.gifu.jp +yamagata.gifu.jp +yaotsu.gifu.jp +yoro.gifu.jp +annaka.gunma.jp +chiyoda.gunma.jp +fujioka.gunma.jp +higashiagatsuma.gunma.jp +isesaki.gunma.jp +itakura.gunma.jp +kanna.gunma.jp +kanra.gunma.jp +katashina.gunma.jp +kawaba.gunma.jp +kiryu.gunma.jp +kusatsu.gunma.jp +maebashi.gunma.jp +meiwa.gunma.jp +midori.gunma.jp +minakami.gunma.jp +naganohara.gunma.jp +nakanojo.gunma.jp +nanmoku.gunma.jp +numata.gunma.jp +oizumi.gunma.jp +ora.gunma.jp +ota.gunma.jp +shibukawa.gunma.jp +shimonita.gunma.jp +shinto.gunma.jp +showa.gunma.jp +takasaki.gunma.jp +takayama.gunma.jp +tamamura.gunma.jp +tatebayashi.gunma.jp +tomioka.gunma.jp +tsukiyono.gunma.jp +tsumagoi.gunma.jp +ueno.gunma.jp +yoshioka.gunma.jp +asaminami.hiroshima.jp +daiwa.hiroshima.jp +etajima.hiroshima.jp +fuchu.hiroshima.jp +fukuyama.hiroshima.jp +hatsukaichi.hiroshima.jp +higashihiroshima.hiroshima.jp +hongo.hiroshima.jp +jinsekikogen.hiroshima.jp +kaita.hiroshima.jp +kui.hiroshima.jp +kumano.hiroshima.jp +kure.hiroshima.jp +mihara.hiroshima.jp +miyoshi.hiroshima.jp +naka.hiroshima.jp +onomichi.hiroshima.jp +osakikamijima.hiroshima.jp +otake.hiroshima.jp +saka.hiroshima.jp +sera.hiroshima.jp +seranishi.hiroshima.jp +shinichi.hiroshima.jp +shobara.hiroshima.jp +takehara.hiroshima.jp +abashiri.hokkaido.jp +abira.hokkaido.jp +aibetsu.hokkaido.jp +akabira.hokkaido.jp +akkeshi.hokkaido.jp +asahikawa.hokkaido.jp +ashibetsu.hokkaido.jp +ashoro.hokkaido.jp +assabu.hokkaido.jp +atsuma.hokkaido.jp +bibai.hokkaido.jp +biei.hokkaido.jp +bifuka.hokkaido.jp +bihoro.hokkaido.jp +biratori.hokkaido.jp +chippubetsu.hokkaido.jp +chitose.hokkaido.jp +date.hokkaido.jp +ebetsu.hokkaido.jp +embetsu.hokkaido.jp +eniwa.hokkaido.jp +erimo.hokkaido.jp +esan.hokkaido.jp +esashi.hokkaido.jp +fukagawa.hokkaido.jp +fukushima.hokkaido.jp +furano.hokkaido.jp +furubira.hokkaido.jp +haboro.hokkaido.jp +hakodate.hokkaido.jp +hamatonbetsu.hokkaido.jp +hidaka.hokkaido.jp +higashikagura.hokkaido.jp +higashikawa.hokkaido.jp +hiroo.hokkaido.jp +hokuryu.hokkaido.jp +hokuto.hokkaido.jp +honbetsu.hokkaido.jp +horokanai.hokkaido.jp +horonobe.hokkaido.jp +ikeda.hokkaido.jp +imakane.hokkaido.jp +ishikari.hokkaido.jp +iwamizawa.hokkaido.jp +iwanai.hokkaido.jp +kamifurano.hokkaido.jp +kamikawa.hokkaido.jp +kamishihoro.hokkaido.jp +kamisunagawa.hokkaido.jp +kamoenai.hokkaido.jp +kayabe.hokkaido.jp +kembuchi.hokkaido.jp +kikonai.hokkaido.jp +kimobetsu.hokkaido.jp +kitahiroshima.hokkaido.jp +kitami.hokkaido.jp +kiyosato.hokkaido.jp +koshimizu.hokkaido.jp +kunneppu.hokkaido.jp +kuriyama.hokkaido.jp +kuromatsunai.hokkaido.jp +kushiro.hokkaido.jp +kutchan.hokkaido.jp +kyowa.hokkaido.jp +mashike.hokkaido.jp +matsumae.hokkaido.jp +mikasa.hokkaido.jp +minamifurano.hokkaido.jp +mombetsu.hokkaido.jp +moseushi.hokkaido.jp +mukawa.hokkaido.jp +muroran.hokkaido.jp +naie.hokkaido.jp +nakagawa.hokkaido.jp +nakasatsunai.hokkaido.jp +nakatombetsu.hokkaido.jp +nanae.hokkaido.jp +nanporo.hokkaido.jp +nayoro.hokkaido.jp +nemuro.hokkaido.jp +niikappu.hokkaido.jp +niki.hokkaido.jp +nishiokoppe.hokkaido.jp +noboribetsu.hokkaido.jp +numata.hokkaido.jp +obihiro.hokkaido.jp +obira.hokkaido.jp +oketo.hokkaido.jp +okoppe.hokkaido.jp +otaru.hokkaido.jp +otobe.hokkaido.jp +otofuke.hokkaido.jp +otoineppu.hokkaido.jp +oumu.hokkaido.jp +ozora.hokkaido.jp +pippu.hokkaido.jp +rankoshi.hokkaido.jp +rebun.hokkaido.jp +rikubetsu.hokkaido.jp +rishiri.hokkaido.jp +rishirifuji.hokkaido.jp +saroma.hokkaido.jp +sarufutsu.hokkaido.jp +shakotan.hokkaido.jp +shari.hokkaido.jp +shibecha.hokkaido.jp +shibetsu.hokkaido.jp +shikabe.hokkaido.jp +shikaoi.hokkaido.jp +shimamaki.hokkaido.jp +shimizu.hokkaido.jp +shimokawa.hokkaido.jp +shinshinotsu.hokkaido.jp +shintoku.hokkaido.jp +shiranuka.hokkaido.jp +shiraoi.hokkaido.jp +shiriuchi.hokkaido.jp +sobetsu.hokkaido.jp +sunagawa.hokkaido.jp +taiki.hokkaido.jp +takasu.hokkaido.jp +takikawa.hokkaido.jp +takinoue.hokkaido.jp +teshikaga.hokkaido.jp +tobetsu.hokkaido.jp +tohma.hokkaido.jp +tomakomai.hokkaido.jp +tomari.hokkaido.jp +toya.hokkaido.jp +toyako.hokkaido.jp +toyotomi.hokkaido.jp +toyoura.hokkaido.jp +tsubetsu.hokkaido.jp +tsukigata.hokkaido.jp +urakawa.hokkaido.jp +urausu.hokkaido.jp +uryu.hokkaido.jp +utashinai.hokkaido.jp +wakkanai.hokkaido.jp +wassamu.hokkaido.jp +yakumo.hokkaido.jp +yoichi.hokkaido.jp +aioi.hyogo.jp +akashi.hyogo.jp +ako.hyogo.jp +amagasaki.hyogo.jp +aogaki.hyogo.jp +asago.hyogo.jp +ashiya.hyogo.jp +awaji.hyogo.jp +fukusaki.hyogo.jp +goshiki.hyogo.jp +harima.hyogo.jp +himeji.hyogo.jp +ichikawa.hyogo.jp +inagawa.hyogo.jp +itami.hyogo.jp +kakogawa.hyogo.jp +kamigori.hyogo.jp +kamikawa.hyogo.jp +kasai.hyogo.jp +kasuga.hyogo.jp +kawanishi.hyogo.jp +miki.hyogo.jp +minamiawaji.hyogo.jp +nishinomiya.hyogo.jp +nishiwaki.hyogo.jp +ono.hyogo.jp +sanda.hyogo.jp +sannan.hyogo.jp +sasayama.hyogo.jp +sayo.hyogo.jp +shingu.hyogo.jp +shinonsen.hyogo.jp +shiso.hyogo.jp +sumoto.hyogo.jp +taishi.hyogo.jp +taka.hyogo.jp +takarazuka.hyogo.jp +takasago.hyogo.jp +takino.hyogo.jp +tamba.hyogo.jp +tatsuno.hyogo.jp +toyooka.hyogo.jp +yabu.hyogo.jp +yashiro.hyogo.jp +yoka.hyogo.jp +yokawa.hyogo.jp +ami.ibaraki.jp +asahi.ibaraki.jp +bando.ibaraki.jp +chikusei.ibaraki.jp +daigo.ibaraki.jp +fujishiro.ibaraki.jp +hitachi.ibaraki.jp +hitachinaka.ibaraki.jp +hitachiomiya.ibaraki.jp +hitachiota.ibaraki.jp +ibaraki.ibaraki.jp +ina.ibaraki.jp +inashiki.ibaraki.jp +itako.ibaraki.jp +iwama.ibaraki.jp +joso.ibaraki.jp +kamisu.ibaraki.jp +kasama.ibaraki.jp +kashima.ibaraki.jp +kasumigaura.ibaraki.jp +koga.ibaraki.jp +miho.ibaraki.jp +mito.ibaraki.jp +moriya.ibaraki.jp +naka.ibaraki.jp +namegata.ibaraki.jp +oarai.ibaraki.jp +ogawa.ibaraki.jp +omitama.ibaraki.jp +ryugasaki.ibaraki.jp +sakai.ibaraki.jp +sakuragawa.ibaraki.jp +shimodate.ibaraki.jp +shimotsuma.ibaraki.jp +shirosato.ibaraki.jp +sowa.ibaraki.jp +suifu.ibaraki.jp +takahagi.ibaraki.jp +tamatsukuri.ibaraki.jp +tokai.ibaraki.jp +tomobe.ibaraki.jp +tone.ibaraki.jp +toride.ibaraki.jp +tsuchiura.ibaraki.jp +tsukuba.ibaraki.jp +uchihara.ibaraki.jp +ushiku.ibaraki.jp +yachiyo.ibaraki.jp +yamagata.ibaraki.jp +yawara.ibaraki.jp +yuki.ibaraki.jp +anamizu.ishikawa.jp +hakui.ishikawa.jp +hakusan.ishikawa.jp +kaga.ishikawa.jp +kahoku.ishikawa.jp +kanazawa.ishikawa.jp +kawakita.ishikawa.jp +komatsu.ishikawa.jp +nakanoto.ishikawa.jp +nanao.ishikawa.jp +nomi.ishikawa.jp +nonoichi.ishikawa.jp +noto.ishikawa.jp +shika.ishikawa.jp +suzu.ishikawa.jp +tsubata.ishikawa.jp +tsurugi.ishikawa.jp +uchinada.ishikawa.jp +wajima.ishikawa.jp +fudai.iwate.jp +fujisawa.iwate.jp +hanamaki.iwate.jp +hiraizumi.iwate.jp +hirono.iwate.jp +ichinohe.iwate.jp +ichinoseki.iwate.jp +iwaizumi.iwate.jp +iwate.iwate.jp +joboji.iwate.jp +kamaishi.iwate.jp +kanegasaki.iwate.jp +karumai.iwate.jp +kawai.iwate.jp +kitakami.iwate.jp +kuji.iwate.jp +kunohe.iwate.jp +kuzumaki.iwate.jp +miyako.iwate.jp +mizusawa.iwate.jp +morioka.iwate.jp +ninohe.iwate.jp +noda.iwate.jp +ofunato.iwate.jp +oshu.iwate.jp +otsuchi.iwate.jp +rikuzentakata.iwate.jp +shiwa.iwate.jp +shizukuishi.iwate.jp +sumita.iwate.jp +tanohata.iwate.jp +tono.iwate.jp +yahaba.iwate.jp +yamada.iwate.jp +ayagawa.kagawa.jp +higashikagawa.kagawa.jp +kanonji.kagawa.jp +kotohira.kagawa.jp +manno.kagawa.jp +marugame.kagawa.jp +mitoyo.kagawa.jp +naoshima.kagawa.jp +sanuki.kagawa.jp +tadotsu.kagawa.jp +takamatsu.kagawa.jp +tonosho.kagawa.jp +uchinomi.kagawa.jp +utazu.kagawa.jp +zentsuji.kagawa.jp +akune.kagoshima.jp +amami.kagoshima.jp +hioki.kagoshima.jp +isa.kagoshima.jp +isen.kagoshima.jp +izumi.kagoshima.jp +kagoshima.kagoshima.jp +kanoya.kagoshima.jp +kawanabe.kagoshima.jp +kinko.kagoshima.jp +kouyama.kagoshima.jp +makurazaki.kagoshima.jp +matsumoto.kagoshima.jp +minamitane.kagoshima.jp +nakatane.kagoshima.jp +nishinoomote.kagoshima.jp +satsumasendai.kagoshima.jp +soo.kagoshima.jp +tarumizu.kagoshima.jp +yusui.kagoshima.jp +aikawa.kanagawa.jp +atsugi.kanagawa.jp +ayase.kanagawa.jp +chigasaki.kanagawa.jp +ebina.kanagawa.jp +fujisawa.kanagawa.jp +hadano.kanagawa.jp +hakone.kanagawa.jp +hiratsuka.kanagawa.jp +isehara.kanagawa.jp +kaisei.kanagawa.jp +kamakura.kanagawa.jp +kiyokawa.kanagawa.jp +matsuda.kanagawa.jp +minamiashigara.kanagawa.jp +miura.kanagawa.jp +nakai.kanagawa.jp +ninomiya.kanagawa.jp +odawara.kanagawa.jp +oi.kanagawa.jp +oiso.kanagawa.jp +sagamihara.kanagawa.jp +samukawa.kanagawa.jp +tsukui.kanagawa.jp +yamakita.kanagawa.jp +yamato.kanagawa.jp +yokosuka.kanagawa.jp +yugawara.kanagawa.jp +zama.kanagawa.jp +zushi.kanagawa.jp +aki.kochi.jp +geisei.kochi.jp +hidaka.kochi.jp +higashitsuno.kochi.jp +ino.kochi.jp +kagami.kochi.jp +kami.kochi.jp +kitagawa.kochi.jp +kochi.kochi.jp +mihara.kochi.jp +motoyama.kochi.jp +muroto.kochi.jp +nahari.kochi.jp +nakamura.kochi.jp +nankoku.kochi.jp +nishitosa.kochi.jp +niyodogawa.kochi.jp +ochi.kochi.jp +okawa.kochi.jp +otoyo.kochi.jp +otsuki.kochi.jp +sakawa.kochi.jp +sukumo.kochi.jp +susaki.kochi.jp +tosa.kochi.jp +tosashimizu.kochi.jp +toyo.kochi.jp +tsuno.kochi.jp +umaji.kochi.jp +yasuda.kochi.jp +yusuhara.kochi.jp +amakusa.kumamoto.jp +arao.kumamoto.jp +aso.kumamoto.jp +choyo.kumamoto.jp +gyokuto.kumamoto.jp +kamiamakusa.kumamoto.jp +kikuchi.kumamoto.jp +kumamoto.kumamoto.jp +mashiki.kumamoto.jp +mifune.kumamoto.jp +minamata.kumamoto.jp +minamioguni.kumamoto.jp +nagasu.kumamoto.jp +nishihara.kumamoto.jp +oguni.kumamoto.jp +ozu.kumamoto.jp +sumoto.kumamoto.jp +takamori.kumamoto.jp +uki.kumamoto.jp +uto.kumamoto.jp +yamaga.kumamoto.jp +yamato.kumamoto.jp +yatsushiro.kumamoto.jp +ayabe.kyoto.jp +fukuchiyama.kyoto.jp +higashiyama.kyoto.jp +ide.kyoto.jp +ine.kyoto.jp +joyo.kyoto.jp +kameoka.kyoto.jp +kamo.kyoto.jp +kita.kyoto.jp +kizu.kyoto.jp +kumiyama.kyoto.jp +kyotamba.kyoto.jp +kyotanabe.kyoto.jp +kyotango.kyoto.jp +maizuru.kyoto.jp +minami.kyoto.jp +minamiyamashiro.kyoto.jp +miyazu.kyoto.jp +muko.kyoto.jp +nagaokakyo.kyoto.jp +nakagyo.kyoto.jp +nantan.kyoto.jp +oyamazaki.kyoto.jp +sakyo.kyoto.jp +seika.kyoto.jp +tanabe.kyoto.jp +uji.kyoto.jp +ujitawara.kyoto.jp +wazuka.kyoto.jp +yamashina.kyoto.jp +yawata.kyoto.jp +asahi.mie.jp +inabe.mie.jp +ise.mie.jp +kameyama.mie.jp +kawagoe.mie.jp +kiho.mie.jp +kisosaki.mie.jp +kiwa.mie.jp +komono.mie.jp +kumano.mie.jp +kuwana.mie.jp +matsusaka.mie.jp +meiwa.mie.jp +mihama.mie.jp +minamiise.mie.jp +misugi.mie.jp +miyama.mie.jp +nabari.mie.jp +shima.mie.jp +suzuka.mie.jp +tado.mie.jp +taiki.mie.jp +taki.mie.jp +tamaki.mie.jp +toba.mie.jp +tsu.mie.jp +udono.mie.jp +ureshino.mie.jp +watarai.mie.jp +yokkaichi.mie.jp +furukawa.miyagi.jp +higashimatsushima.miyagi.jp +ishinomaki.miyagi.jp +iwanuma.miyagi.jp +kakuda.miyagi.jp +kami.miyagi.jp +kawasaki.miyagi.jp +marumori.miyagi.jp +matsushima.miyagi.jp +minamisanriku.miyagi.jp +misato.miyagi.jp +murata.miyagi.jp +natori.miyagi.jp +ogawara.miyagi.jp +ohira.miyagi.jp +onagawa.miyagi.jp +osaki.miyagi.jp +rifu.miyagi.jp +semine.miyagi.jp +shibata.miyagi.jp +shichikashuku.miyagi.jp +shikama.miyagi.jp +shiogama.miyagi.jp +shiroishi.miyagi.jp +tagajo.miyagi.jp +taiwa.miyagi.jp +tome.miyagi.jp +tomiya.miyagi.jp +wakuya.miyagi.jp +watari.miyagi.jp +yamamoto.miyagi.jp +zao.miyagi.jp +aya.miyazaki.jp +ebino.miyazaki.jp +gokase.miyazaki.jp +hyuga.miyazaki.jp +kadogawa.miyazaki.jp +kawaminami.miyazaki.jp +kijo.miyazaki.jp +kitagawa.miyazaki.jp +kitakata.miyazaki.jp +kitaura.miyazaki.jp +kobayashi.miyazaki.jp +kunitomi.miyazaki.jp +kushima.miyazaki.jp +mimata.miyazaki.jp +miyakonojo.miyazaki.jp +miyazaki.miyazaki.jp +morotsuka.miyazaki.jp +nichinan.miyazaki.jp +nishimera.miyazaki.jp +nobeoka.miyazaki.jp +saito.miyazaki.jp +shiiba.miyazaki.jp +shintomi.miyazaki.jp +takaharu.miyazaki.jp +takanabe.miyazaki.jp +takazaki.miyazaki.jp +tsuno.miyazaki.jp +achi.nagano.jp +agematsu.nagano.jp +anan.nagano.jp +aoki.nagano.jp +asahi.nagano.jp +azumino.nagano.jp +chikuhoku.nagano.jp +chikuma.nagano.jp +chino.nagano.jp +fujimi.nagano.jp +hakuba.nagano.jp +hara.nagano.jp +hiraya.nagano.jp +iida.nagano.jp +iijima.nagano.jp +iiyama.nagano.jp +iizuna.nagano.jp +ikeda.nagano.jp +ikusaka.nagano.jp +ina.nagano.jp +karuizawa.nagano.jp +kawakami.nagano.jp +kiso.nagano.jp +kisofukushima.nagano.jp +kitaaiki.nagano.jp +komagane.nagano.jp +komoro.nagano.jp +matsukawa.nagano.jp +matsumoto.nagano.jp +miasa.nagano.jp +minamiaiki.nagano.jp +minamimaki.nagano.jp +minamiminowa.nagano.jp +minowa.nagano.jp +miyada.nagano.jp +miyota.nagano.jp +mochizuki.nagano.jp +nagano.nagano.jp +nagawa.nagano.jp +nagiso.nagano.jp +nakagawa.nagano.jp +nakano.nagano.jp +nozawaonsen.nagano.jp +obuse.nagano.jp +ogawa.nagano.jp +okaya.nagano.jp +omachi.nagano.jp +omi.nagano.jp +ookuwa.nagano.jp +ooshika.nagano.jp +otaki.nagano.jp +otari.nagano.jp +sakae.nagano.jp +sakaki.nagano.jp +saku.nagano.jp +sakuho.nagano.jp +shimosuwa.nagano.jp +shinanomachi.nagano.jp +shiojiri.nagano.jp +suwa.nagano.jp +suzaka.nagano.jp +takagi.nagano.jp +takamori.nagano.jp +takayama.nagano.jp +tateshina.nagano.jp +tatsuno.nagano.jp +togakushi.nagano.jp +togura.nagano.jp +tomi.nagano.jp +ueda.nagano.jp +wada.nagano.jp +yamagata.nagano.jp +yamanouchi.nagano.jp +yasaka.nagano.jp +yasuoka.nagano.jp +chijiwa.nagasaki.jp +futsu.nagasaki.jp +goto.nagasaki.jp +hasami.nagasaki.jp +hirado.nagasaki.jp +iki.nagasaki.jp +isahaya.nagasaki.jp +kawatana.nagasaki.jp +kuchinotsu.nagasaki.jp +matsuura.nagasaki.jp +nagasaki.nagasaki.jp +obama.nagasaki.jp +omura.nagasaki.jp +oseto.nagasaki.jp +saikai.nagasaki.jp +sasebo.nagasaki.jp +seihi.nagasaki.jp +shimabara.nagasaki.jp +shinkamigoto.nagasaki.jp +togitsu.nagasaki.jp +tsushima.nagasaki.jp +unzen.nagasaki.jp +ando.nara.jp +gose.nara.jp +heguri.nara.jp +higashiyoshino.nara.jp +ikaruga.nara.jp +ikoma.nara.jp +kamikitayama.nara.jp +kanmaki.nara.jp +kashiba.nara.jp +kashihara.nara.jp +katsuragi.nara.jp +kawai.nara.jp +kawakami.nara.jp +kawanishi.nara.jp +koryo.nara.jp +kurotaki.nara.jp +mitsue.nara.jp +miyake.nara.jp +nara.nara.jp +nosegawa.nara.jp +oji.nara.jp +ouda.nara.jp +oyodo.nara.jp +sakurai.nara.jp +sango.nara.jp +shimoichi.nara.jp +shimokitayama.nara.jp +shinjo.nara.jp +soni.nara.jp +takatori.nara.jp +tawaramoto.nara.jp +tenkawa.nara.jp +tenri.nara.jp +uda.nara.jp +yamatokoriyama.nara.jp +yamatotakada.nara.jp +yamazoe.nara.jp +yoshino.nara.jp +aga.niigata.jp +agano.niigata.jp +gosen.niigata.jp +itoigawa.niigata.jp +izumozaki.niigata.jp +joetsu.niigata.jp +kamo.niigata.jp +kariwa.niigata.jp +kashiwazaki.niigata.jp +minamiuonuma.niigata.jp +mitsuke.niigata.jp +muika.niigata.jp +murakami.niigata.jp +myoko.niigata.jp +nagaoka.niigata.jp +niigata.niigata.jp +ojiya.niigata.jp +omi.niigata.jp +sado.niigata.jp +sanjo.niigata.jp +seiro.niigata.jp +seirou.niigata.jp +sekikawa.niigata.jp +shibata.niigata.jp +tagami.niigata.jp +tainai.niigata.jp +tochio.niigata.jp +tokamachi.niigata.jp +tsubame.niigata.jp +tsunan.niigata.jp +uonuma.niigata.jp +yahiko.niigata.jp +yoita.niigata.jp +yuzawa.niigata.jp +beppu.oita.jp +bungoono.oita.jp +bungotakada.oita.jp +hasama.oita.jp +hiji.oita.jp +himeshima.oita.jp +hita.oita.jp +kamitsue.oita.jp +kokonoe.oita.jp +kuju.oita.jp +kunisaki.oita.jp +kusu.oita.jp +oita.oita.jp +saiki.oita.jp +taketa.oita.jp +tsukumi.oita.jp +usa.oita.jp +usuki.oita.jp +yufu.oita.jp +akaiwa.okayama.jp +asakuchi.okayama.jp +bizen.okayama.jp +hayashima.okayama.jp +ibara.okayama.jp +kagamino.okayama.jp +kasaoka.okayama.jp +kibichuo.okayama.jp +kumenan.okayama.jp +kurashiki.okayama.jp +maniwa.okayama.jp +misaki.okayama.jp +nagi.okayama.jp +niimi.okayama.jp +nishiawakura.okayama.jp +okayama.okayama.jp +satosho.okayama.jp +setouchi.okayama.jp +shinjo.okayama.jp +shoo.okayama.jp +soja.okayama.jp +takahashi.okayama.jp +tamano.okayama.jp +tsuyama.okayama.jp +wake.okayama.jp +yakage.okayama.jp +aguni.okinawa.jp +ginowan.okinawa.jp +ginoza.okinawa.jp +gushikami.okinawa.jp +haebaru.okinawa.jp +higashi.okinawa.jp +hirara.okinawa.jp +iheya.okinawa.jp +ishigaki.okinawa.jp +ishikawa.okinawa.jp +itoman.okinawa.jp +izena.okinawa.jp +kadena.okinawa.jp +kin.okinawa.jp +kitadaito.okinawa.jp +kitanakagusuku.okinawa.jp +kumejima.okinawa.jp +kunigami.okinawa.jp +minamidaito.okinawa.jp +motobu.okinawa.jp +nago.okinawa.jp +naha.okinawa.jp +nakagusuku.okinawa.jp +nakijin.okinawa.jp +nanjo.okinawa.jp +nishihara.okinawa.jp +ogimi.okinawa.jp +okinawa.okinawa.jp +onna.okinawa.jp +shimoji.okinawa.jp +taketomi.okinawa.jp +tarama.okinawa.jp +tokashiki.okinawa.jp +tomigusuku.okinawa.jp +tonaki.okinawa.jp +urasoe.okinawa.jp +uruma.okinawa.jp +yaese.okinawa.jp +yomitan.okinawa.jp +yonabaru.okinawa.jp +yonaguni.okinawa.jp +zamami.okinawa.jp +abeno.osaka.jp +chihayaakasaka.osaka.jp +chuo.osaka.jp +daito.osaka.jp +fujiidera.osaka.jp +habikino.osaka.jp +hannan.osaka.jp +higashiosaka.osaka.jp +higashisumiyoshi.osaka.jp +higashiyodogawa.osaka.jp +hirakata.osaka.jp +ibaraki.osaka.jp +ikeda.osaka.jp +izumi.osaka.jp +izumiotsu.osaka.jp +izumisano.osaka.jp +kadoma.osaka.jp +kaizuka.osaka.jp +kanan.osaka.jp +kashiwara.osaka.jp +katano.osaka.jp +kawachinagano.osaka.jp +kishiwada.osaka.jp +kita.osaka.jp +kumatori.osaka.jp +matsubara.osaka.jp +minato.osaka.jp +minoh.osaka.jp +misaki.osaka.jp +moriguchi.osaka.jp +neyagawa.osaka.jp +nishi.osaka.jp +nose.osaka.jp +osakasayama.osaka.jp +sakai.osaka.jp +sayama.osaka.jp +sennan.osaka.jp +settsu.osaka.jp +shijonawate.osaka.jp +shimamoto.osaka.jp +suita.osaka.jp +tadaoka.osaka.jp +taishi.osaka.jp +tajiri.osaka.jp +takaishi.osaka.jp +takatsuki.osaka.jp +tondabayashi.osaka.jp +toyonaka.osaka.jp +toyono.osaka.jp +yao.osaka.jp +ariake.saga.jp +arita.saga.jp +fukudomi.saga.jp +genkai.saga.jp +hamatama.saga.jp +hizen.saga.jp +imari.saga.jp +kamimine.saga.jp +kanzaki.saga.jp +karatsu.saga.jp +kashima.saga.jp +kitagata.saga.jp +kitahata.saga.jp +kiyama.saga.jp +kouhoku.saga.jp +kyuragi.saga.jp +nishiarita.saga.jp +ogi.saga.jp +omachi.saga.jp +ouchi.saga.jp +saga.saga.jp +shiroishi.saga.jp +taku.saga.jp +tara.saga.jp +tosu.saga.jp +yoshinogari.saga.jp +arakawa.saitama.jp +asaka.saitama.jp +chichibu.saitama.jp +fujimi.saitama.jp +fujimino.saitama.jp +fukaya.saitama.jp +hanno.saitama.jp +hanyu.saitama.jp +hasuda.saitama.jp +hatogaya.saitama.jp +hatoyama.saitama.jp +hidaka.saitama.jp +higashichichibu.saitama.jp +higashimatsuyama.saitama.jp +honjo.saitama.jp +ina.saitama.jp +iruma.saitama.jp +iwatsuki.saitama.jp +kamiizumi.saitama.jp +kamikawa.saitama.jp +kamisato.saitama.jp +kasukabe.saitama.jp +kawagoe.saitama.jp +kawaguchi.saitama.jp +kawajima.saitama.jp +kazo.saitama.jp +kitamoto.saitama.jp +koshigaya.saitama.jp +kounosu.saitama.jp +kuki.saitama.jp +kumagaya.saitama.jp +matsubushi.saitama.jp +minano.saitama.jp +misato.saitama.jp +miyashiro.saitama.jp +miyoshi.saitama.jp +moroyama.saitama.jp +nagatoro.saitama.jp +namegawa.saitama.jp +niiza.saitama.jp +ogano.saitama.jp +ogawa.saitama.jp +ogose.saitama.jp +okegawa.saitama.jp +omiya.saitama.jp +otaki.saitama.jp +ranzan.saitama.jp +ryokami.saitama.jp +saitama.saitama.jp +sakado.saitama.jp +satte.saitama.jp +sayama.saitama.jp +shiki.saitama.jp +shiraoka.saitama.jp +soka.saitama.jp +sugito.saitama.jp +toda.saitama.jp +tokigawa.saitama.jp +tokorozawa.saitama.jp +tsurugashima.saitama.jp +urawa.saitama.jp +warabi.saitama.jp +yashio.saitama.jp +yokoze.saitama.jp +yono.saitama.jp +yorii.saitama.jp +yoshida.saitama.jp +yoshikawa.saitama.jp +yoshimi.saitama.jp +aisho.shiga.jp +gamo.shiga.jp +higashiomi.shiga.jp +hikone.shiga.jp +koka.shiga.jp +konan.shiga.jp +kosei.shiga.jp +koto.shiga.jp +kusatsu.shiga.jp +maibara.shiga.jp +moriyama.shiga.jp +nagahama.shiga.jp +nishiazai.shiga.jp +notogawa.shiga.jp +omihachiman.shiga.jp +otsu.shiga.jp +ritto.shiga.jp +ryuoh.shiga.jp +takashima.shiga.jp +takatsuki.shiga.jp +torahime.shiga.jp +toyosato.shiga.jp +yasu.shiga.jp +akagi.shimane.jp +ama.shimane.jp +gotsu.shimane.jp +hamada.shimane.jp +higashiizumo.shimane.jp +hikawa.shimane.jp +hikimi.shimane.jp +izumo.shimane.jp +kakinoki.shimane.jp +masuda.shimane.jp +matsue.shimane.jp +misato.shimane.jp +nishinoshima.shimane.jp +ohda.shimane.jp +okinoshima.shimane.jp +okuizumo.shimane.jp +shimane.shimane.jp +tamayu.shimane.jp +tsuwano.shimane.jp +unnan.shimane.jp +yakumo.shimane.jp +yasugi.shimane.jp +yatsuka.shimane.jp +arai.shizuoka.jp +atami.shizuoka.jp +fuji.shizuoka.jp +fujieda.shizuoka.jp +fujikawa.shizuoka.jp +fujinomiya.shizuoka.jp +fukuroi.shizuoka.jp +gotemba.shizuoka.jp +haibara.shizuoka.jp +hamamatsu.shizuoka.jp +higashiizu.shizuoka.jp +ito.shizuoka.jp +iwata.shizuoka.jp +izu.shizuoka.jp +izunokuni.shizuoka.jp +kakegawa.shizuoka.jp +kannami.shizuoka.jp +kawanehon.shizuoka.jp +kawazu.shizuoka.jp +kikugawa.shizuoka.jp +kosai.shizuoka.jp +makinohara.shizuoka.jp +matsuzaki.shizuoka.jp +minamiizu.shizuoka.jp +mishima.shizuoka.jp +morimachi.shizuoka.jp +nishiizu.shizuoka.jp +numazu.shizuoka.jp +omaezaki.shizuoka.jp +shimada.shizuoka.jp +shimizu.shizuoka.jp +shimoda.shizuoka.jp +shizuoka.shizuoka.jp +susono.shizuoka.jp +yaizu.shizuoka.jp +yoshida.shizuoka.jp +ashikaga.tochigi.jp +bato.tochigi.jp +haga.tochigi.jp +ichikai.tochigi.jp +iwafune.tochigi.jp +kaminokawa.tochigi.jp +kanuma.tochigi.jp +karasuyama.tochigi.jp +kuroiso.tochigi.jp +mashiko.tochigi.jp +mibu.tochigi.jp +moka.tochigi.jp +motegi.tochigi.jp +nasu.tochigi.jp +nasushiobara.tochigi.jp +nikko.tochigi.jp +nishikata.tochigi.jp +nogi.tochigi.jp +ohira.tochigi.jp +ohtawara.tochigi.jp +oyama.tochigi.jp +sakura.tochigi.jp +sano.tochigi.jp +shimotsuke.tochigi.jp +shioya.tochigi.jp +takanezawa.tochigi.jp +tochigi.tochigi.jp +tsuga.tochigi.jp +ujiie.tochigi.jp +utsunomiya.tochigi.jp +yaita.tochigi.jp +aizumi.tokushima.jp +anan.tokushima.jp +ichiba.tokushima.jp +itano.tokushima.jp +kainan.tokushima.jp +komatsushima.tokushima.jp +matsushige.tokushima.jp +mima.tokushima.jp +minami.tokushima.jp +miyoshi.tokushima.jp +mugi.tokushima.jp +nakagawa.tokushima.jp +naruto.tokushima.jp +sanagochi.tokushima.jp +shishikui.tokushima.jp +tokushima.tokushima.jp +wajiki.tokushima.jp +adachi.tokyo.jp +akiruno.tokyo.jp +akishima.tokyo.jp +aogashima.tokyo.jp +arakawa.tokyo.jp +bunkyo.tokyo.jp +chiyoda.tokyo.jp +chofu.tokyo.jp +chuo.tokyo.jp +edogawa.tokyo.jp +fuchu.tokyo.jp +fussa.tokyo.jp +hachijo.tokyo.jp +hachioji.tokyo.jp +hamura.tokyo.jp +higashikurume.tokyo.jp +higashimurayama.tokyo.jp +higashiyamato.tokyo.jp +hino.tokyo.jp +hinode.tokyo.jp +hinohara.tokyo.jp +inagi.tokyo.jp +itabashi.tokyo.jp +katsushika.tokyo.jp +kita.tokyo.jp +kiyose.tokyo.jp +kodaira.tokyo.jp +koganei.tokyo.jp +kokubunji.tokyo.jp +komae.tokyo.jp +koto.tokyo.jp +kouzushima.tokyo.jp +kunitachi.tokyo.jp +machida.tokyo.jp +meguro.tokyo.jp +minato.tokyo.jp +mitaka.tokyo.jp +mizuho.tokyo.jp +musashimurayama.tokyo.jp +musashino.tokyo.jp +nakano.tokyo.jp +nerima.tokyo.jp +ogasawara.tokyo.jp +okutama.tokyo.jp +ome.tokyo.jp +oshima.tokyo.jp +ota.tokyo.jp +setagaya.tokyo.jp +shibuya.tokyo.jp +shinagawa.tokyo.jp +shinjuku.tokyo.jp +suginami.tokyo.jp +sumida.tokyo.jp +tachikawa.tokyo.jp +taito.tokyo.jp +tama.tokyo.jp +toshima.tokyo.jp +chizu.tottori.jp +hino.tottori.jp +kawahara.tottori.jp +koge.tottori.jp +kotoura.tottori.jp +misasa.tottori.jp +nanbu.tottori.jp +nichinan.tottori.jp +sakaiminato.tottori.jp +tottori.tottori.jp +wakasa.tottori.jp +yazu.tottori.jp +yonago.tottori.jp +asahi.toyama.jp +fuchu.toyama.jp +fukumitsu.toyama.jp +funahashi.toyama.jp +himi.toyama.jp +imizu.toyama.jp +inami.toyama.jp +johana.toyama.jp +kamiichi.toyama.jp +kurobe.toyama.jp +nakaniikawa.toyama.jp +namerikawa.toyama.jp +nanto.toyama.jp +nyuzen.toyama.jp +oyabe.toyama.jp +taira.toyama.jp +takaoka.toyama.jp +tateyama.toyama.jp +toga.toyama.jp +tonami.toyama.jp +toyama.toyama.jp +unazuki.toyama.jp +uozu.toyama.jp +yamada.toyama.jp +arida.wakayama.jp +aridagawa.wakayama.jp +gobo.wakayama.jp +hashimoto.wakayama.jp +hidaka.wakayama.jp +hirogawa.wakayama.jp +inami.wakayama.jp +iwade.wakayama.jp +kainan.wakayama.jp +kamitonda.wakayama.jp +katsuragi.wakayama.jp +kimino.wakayama.jp +kinokawa.wakayama.jp +kitayama.wakayama.jp +koya.wakayama.jp +koza.wakayama.jp +kozagawa.wakayama.jp +kudoyama.wakayama.jp +kushimoto.wakayama.jp +mihama.wakayama.jp +misato.wakayama.jp +nachikatsuura.wakayama.jp +shingu.wakayama.jp +shirahama.wakayama.jp +taiji.wakayama.jp +tanabe.wakayama.jp +wakayama.wakayama.jp +yuasa.wakayama.jp +yura.wakayama.jp +asahi.yamagata.jp +funagata.yamagata.jp +higashine.yamagata.jp +iide.yamagata.jp +kahoku.yamagata.jp +kaminoyama.yamagata.jp +kaneyama.yamagata.jp +kawanishi.yamagata.jp +mamurogawa.yamagata.jp +mikawa.yamagata.jp +murayama.yamagata.jp +nagai.yamagata.jp +nakayama.yamagata.jp +nanyo.yamagata.jp +nishikawa.yamagata.jp +obanazawa.yamagata.jp +oe.yamagata.jp +oguni.yamagata.jp +ohkura.yamagata.jp +oishida.yamagata.jp +sagae.yamagata.jp +sakata.yamagata.jp +sakegawa.yamagata.jp +shinjo.yamagata.jp +shirataka.yamagata.jp +shonai.yamagata.jp +takahata.yamagata.jp +tendo.yamagata.jp +tozawa.yamagata.jp +tsuruoka.yamagata.jp +yamagata.yamagata.jp +yamanobe.yamagata.jp +yonezawa.yamagata.jp +yuza.yamagata.jp +abu.yamaguchi.jp +hagi.yamaguchi.jp +hikari.yamaguchi.jp +hofu.yamaguchi.jp +iwakuni.yamaguchi.jp +kudamatsu.yamaguchi.jp +mitou.yamaguchi.jp +nagato.yamaguchi.jp +oshima.yamaguchi.jp +shimonoseki.yamaguchi.jp +shunan.yamaguchi.jp +tabuse.yamaguchi.jp +tokuyama.yamaguchi.jp +toyota.yamaguchi.jp +ube.yamaguchi.jp +yuu.yamaguchi.jp +chuo.yamanashi.jp +doshi.yamanashi.jp +fuefuki.yamanashi.jp +fujikawa.yamanashi.jp +fujikawaguchiko.yamanashi.jp +fujiyoshida.yamanashi.jp +hayakawa.yamanashi.jp +hokuto.yamanashi.jp +ichikawamisato.yamanashi.jp +kai.yamanashi.jp +kofu.yamanashi.jp +koshu.yamanashi.jp +kosuge.yamanashi.jp +minami-alps.yamanashi.jp +minobu.yamanashi.jp +nakamichi.yamanashi.jp +nanbu.yamanashi.jp +narusawa.yamanashi.jp +nirasaki.yamanashi.jp +nishikatsura.yamanashi.jp +oshino.yamanashi.jp +otsuki.yamanashi.jp +showa.yamanashi.jp +tabayama.yamanashi.jp +tsuru.yamanashi.jp +uenohara.yamanashi.jp +yamanakako.yamanashi.jp +yamanashi.yamanashi.jp + +// ke : http://www.kenic.or.ke/index.php/en/ke-domains/ke-domains +ke +ac.ke +co.ke +go.ke +info.ke +me.ke +mobi.ke +ne.ke +or.ke +sc.ke + +// kg : http://www.domain.kg/dmn_n.html +kg +org.kg +net.kg +com.kg +edu.kg +gov.kg +mil.kg + +// kh : http://www.mptc.gov.kh/dns_registration.htm +*.kh + +// ki : http://www.ki/dns/index.html +ki +edu.ki +biz.ki +net.ki +org.ki +gov.ki +info.ki +com.ki + +// km : https://en.wikipedia.org/wiki/.km +// http://www.domaine.km/documents/charte.doc +km +org.km +nom.km +gov.km +prd.km +tm.km +edu.km +mil.km +ass.km +com.km +// These are only mentioned as proposed suggestions at domaine.km, but +// https://en.wikipedia.org/wiki/.km says they're available for registration: +coop.km +asso.km +presse.km +medecin.km +notaires.km +pharmaciens.km +veterinaire.km +gouv.km + +// kn : https://en.wikipedia.org/wiki/.kn +// http://www.dot.kn/domainRules.html +kn +net.kn +org.kn +edu.kn +gov.kn + +// kp : http://www.kcce.kp/en_index.php +kp +com.kp +edu.kp +gov.kp +org.kp +rep.kp +tra.kp + +// kr : https://en.wikipedia.org/wiki/.kr +// see also: http://domain.nida.or.kr/eng/registration.jsp +kr +ac.kr +co.kr +es.kr +go.kr +hs.kr +kg.kr +mil.kr +ms.kr +ne.kr +or.kr +pe.kr +re.kr +sc.kr +// kr geographical names +busan.kr +chungbuk.kr +chungnam.kr +daegu.kr +daejeon.kr +gangwon.kr +gwangju.kr +gyeongbuk.kr +gyeonggi.kr +gyeongnam.kr +incheon.kr +jeju.kr +jeonbuk.kr +jeonnam.kr +seoul.kr +ulsan.kr + +// kw : https://www.nic.kw/policies/ +// Confirmed by registry <nic.tech@citra.gov.kw> +kw +com.kw +edu.kw +emb.kw +gov.kw +ind.kw +net.kw +org.kw + +// ky : http://www.icta.ky/da_ky_reg_dom.php +// Confirmed by registry <kysupport@perimeterusa.com> 2008-06-17 +ky +com.ky +edu.ky +net.ky +org.ky + +// kz : https://en.wikipedia.org/wiki/.kz +// see also: http://www.nic.kz/rules/index.jsp +kz +org.kz +edu.kz +net.kz +gov.kz +mil.kz +com.kz + +// la : https://en.wikipedia.org/wiki/.la +// Submitted by registry <gavin.brown@nic.la> +la +int.la +net.la +info.la +edu.la +gov.la +per.la +com.la +org.la + +// lb : https://en.wikipedia.org/wiki/.lb +// Submitted by registry <randy@psg.com> +lb +com.lb +edu.lb +gov.lb +net.lb +org.lb + +// lc : https://en.wikipedia.org/wiki/.lc +// see also: http://www.nic.lc/rules.htm +lc +com.lc +net.lc +co.lc +org.lc +edu.lc +gov.lc + +// li : https://en.wikipedia.org/wiki/.li +li + +// lk : https://www.nic.lk/index.php/domain-registration/lk-domain-naming-structure +lk +gov.lk +sch.lk +net.lk +int.lk +com.lk +org.lk +edu.lk +ngo.lk +soc.lk +web.lk +ltd.lk +assn.lk +grp.lk +hotel.lk +ac.lk + +// lr : http://psg.com/dns/lr/lr.txt +// Submitted by registry <randy@psg.com> +lr +com.lr +edu.lr +gov.lr +org.lr +net.lr + +// ls : http://www.nic.ls/ +// Confirmed by registry <lsadmin@nic.ls> +ls +ac.ls +biz.ls +co.ls +edu.ls +gov.ls +info.ls +net.ls +org.ls +sc.ls + +// lt : https://en.wikipedia.org/wiki/.lt +lt +// gov.lt : http://www.gov.lt/index_en.php +gov.lt + +// lu : http://www.dns.lu/en/ +lu + +// lv : http://www.nic.lv/DNS/En/generic.php +lv +com.lv +edu.lv +gov.lv +org.lv +mil.lv +id.lv +net.lv +asn.lv +conf.lv + +// ly : http://www.nic.ly/regulations.php +ly +com.ly +net.ly +gov.ly +plc.ly +edu.ly +sch.ly +med.ly +org.ly +id.ly + +// ma : https://en.wikipedia.org/wiki/.ma +// http://www.anrt.ma/fr/admin/download/upload/file_fr782.pdf +ma +co.ma +net.ma +gov.ma +org.ma +ac.ma +press.ma + +// mc : http://www.nic.mc/ +mc +tm.mc +asso.mc + +// md : https://en.wikipedia.org/wiki/.md +md + +// me : https://en.wikipedia.org/wiki/.me +me +co.me +net.me +org.me +edu.me +ac.me +gov.me +its.me +priv.me + +// mg : http://nic.mg/nicmg/?page_id=39 +mg +org.mg +nom.mg +gov.mg +prd.mg +tm.mg +edu.mg +mil.mg +com.mg +co.mg + +// mh : https://en.wikipedia.org/wiki/.mh +mh + +// mil : https://en.wikipedia.org/wiki/.mil +mil + +// mk : https://en.wikipedia.org/wiki/.mk +// see also: http://dns.marnet.net.mk/postapka.php +mk +com.mk +org.mk +net.mk +edu.mk +gov.mk +inf.mk +name.mk + +// ml : http://www.gobin.info/domainname/ml-template.doc +// see also: https://en.wikipedia.org/wiki/.ml +ml +com.ml +edu.ml +gouv.ml +gov.ml +net.ml +org.ml +presse.ml + +// mm : https://en.wikipedia.org/wiki/.mm +*.mm + +// mn : https://en.wikipedia.org/wiki/.mn +mn +gov.mn +edu.mn +org.mn + +// mo : http://www.monic.net.mo/ +mo +com.mo +net.mo +org.mo +edu.mo +gov.mo + +// mobi : https://en.wikipedia.org/wiki/.mobi +mobi + +// mp : http://www.dot.mp/ +// Confirmed by registry <dcamacho@saipan.com> 2008-06-17 +mp + +// mq : https://en.wikipedia.org/wiki/.mq +mq + +// mr : https://en.wikipedia.org/wiki/.mr +mr +gov.mr + +// ms : http://www.nic.ms/pdf/MS_Domain_Name_Rules.pdf +ms +com.ms +edu.ms +gov.ms +net.ms +org.ms + +// mt : https://www.nic.org.mt/go/policy +// Submitted by registry <help@nic.org.mt> +mt +com.mt +edu.mt +net.mt +org.mt + +// mu : https://en.wikipedia.org/wiki/.mu +mu +com.mu +net.mu +org.mu +gov.mu +ac.mu +co.mu +or.mu + +// museum : https://welcome.museum/wp-content/uploads/2018/05/20180525-Registration-Policy-MUSEUM-EN_VF-2.pdf https://welcome.museum/buy-your-dot-museum-2/ +museum + +// mv : https://en.wikipedia.org/wiki/.mv +// "mv" included because, contra Wikipedia, google.mv exists. +mv +aero.mv +biz.mv +com.mv +coop.mv +edu.mv +gov.mv +info.mv +int.mv +mil.mv +museum.mv +name.mv +net.mv +org.mv +pro.mv + +// mw : http://www.registrar.mw/ +mw +ac.mw +biz.mw +co.mw +com.mw +coop.mw +edu.mw +gov.mw +int.mw +museum.mw +net.mw +org.mw + +// mx : http://www.nic.mx/ +// Submitted by registry <farias@nic.mx> +mx +com.mx +org.mx +gob.mx +edu.mx +net.mx + +// my : http://www.mynic.my/ +// Available strings: https://mynic.my/resources/domains/buying-a-domain/ +my +biz.my +com.my +edu.my +gov.my +mil.my +name.my +net.my +org.my + +// mz : http://www.uem.mz/ +// Submitted by registry <antonio@uem.mz> +mz +ac.mz +adv.mz +co.mz +edu.mz +gov.mz +mil.mz +net.mz +org.mz + +// na : http://www.na-nic.com.na/ +// http://www.info.na/domain/ +na +info.na +pro.na +name.na +school.na +or.na +dr.na +us.na +mx.na +ca.na +in.na +cc.na +tv.na +ws.na +mobi.na +co.na +com.na +org.na + +// name : has 2nd-level tlds, but there's no list of them +name + +// nc : http://www.cctld.nc/ +nc +asso.nc +nom.nc + +// ne : https://en.wikipedia.org/wiki/.ne +ne + +// net : https://en.wikipedia.org/wiki/.net +net + +// nf : https://en.wikipedia.org/wiki/.nf +nf +com.nf +net.nf +per.nf +rec.nf +web.nf +arts.nf +firm.nf +info.nf +other.nf +store.nf + +// ng : http://www.nira.org.ng/index.php/join-us/register-ng-domain/189-nira-slds +ng +com.ng +edu.ng +gov.ng +i.ng +mil.ng +mobi.ng +name.ng +net.ng +org.ng +sch.ng + +// ni : http://www.nic.ni/ +ni +ac.ni +biz.ni +co.ni +com.ni +edu.ni +gob.ni +in.ni +info.ni +int.ni +mil.ni +net.ni +nom.ni +org.ni +web.ni + +// nl : https://en.wikipedia.org/wiki/.nl +// https://www.sidn.nl/ +// ccTLD for the Netherlands +nl + +// no : https://www.norid.no/en/om-domenenavn/regelverk-for-no/ +// Norid geographical second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-b/ +// Norid category second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-c/ +// Norid category second-level domains managed by parties other than Norid : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-d/ +// RSS feed: https://teknisk.norid.no/en/feed/ +no +// Norid category second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-c/ +fhs.no +vgs.no +fylkesbibl.no +folkebibl.no +museum.no +idrett.no +priv.no +// Norid category second-level domains managed by parties other than Norid : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-d/ +mil.no +stat.no +dep.no +kommune.no +herad.no +// Norid geographical second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-b/ +// counties +aa.no +ah.no +bu.no +fm.no +hl.no +hm.no +jan-mayen.no +mr.no +nl.no +nt.no +of.no +ol.no +oslo.no +rl.no +sf.no +st.no +svalbard.no +tm.no +tr.no +va.no +vf.no +// primary and lower secondary schools per county +gs.aa.no +gs.ah.no +gs.bu.no +gs.fm.no +gs.hl.no +gs.hm.no +gs.jan-mayen.no +gs.mr.no +gs.nl.no +gs.nt.no +gs.of.no +gs.ol.no +gs.oslo.no +gs.rl.no +gs.sf.no +gs.st.no +gs.svalbard.no +gs.tm.no +gs.tr.no +gs.va.no +gs.vf.no +// cities +akrehamn.no +åkrehamn.no +algard.no +ålgård.no +arna.no +brumunddal.no +bryne.no +bronnoysund.no +brønnøysund.no +drobak.no +drøbak.no +egersund.no +fetsund.no +floro.no +florø.no +fredrikstad.no +hokksund.no +honefoss.no +hønefoss.no +jessheim.no +jorpeland.no +jørpeland.no +kirkenes.no +kopervik.no +krokstadelva.no +langevag.no +langevåg.no +leirvik.no +mjondalen.no +mjøndalen.no +mo-i-rana.no +mosjoen.no +mosjøen.no +nesoddtangen.no +orkanger.no +osoyro.no +osøyro.no +raholt.no +råholt.no +sandnessjoen.no +sandnessjøen.no +skedsmokorset.no +slattum.no +spjelkavik.no +stathelle.no +stavern.no +stjordalshalsen.no +stjørdalshalsen.no +tananger.no +tranby.no +vossevangen.no +// communities +afjord.no +åfjord.no +agdenes.no +al.no +ål.no +alesund.no +ålesund.no +alstahaug.no +alta.no +áltá.no +alaheadju.no +álaheadju.no +alvdal.no +amli.no +åmli.no +amot.no +åmot.no +andebu.no +andoy.no +andøy.no +andasuolo.no +ardal.no +årdal.no +aremark.no +arendal.no +ås.no +aseral.no +åseral.no +asker.no +askim.no +askvoll.no +askoy.no +askøy.no +asnes.no +åsnes.no +audnedaln.no +aukra.no +aure.no +aurland.no +aurskog-holand.no +aurskog-høland.no +austevoll.no +austrheim.no +averoy.no +averøy.no +balestrand.no +ballangen.no +balat.no +bálát.no +balsfjord.no +bahccavuotna.no +báhccavuotna.no +bamble.no +bardu.no +beardu.no +beiarn.no +bajddar.no +bájddar.no +baidar.no +báidár.no +berg.no +bergen.no +berlevag.no +berlevåg.no +bearalvahki.no +bearalváhki.no +bindal.no +birkenes.no +bjarkoy.no +bjarkøy.no +bjerkreim.no +bjugn.no +bodo.no +bodø.no +badaddja.no +bådåddjå.no +budejju.no +bokn.no +bremanger.no +bronnoy.no +brønnøy.no +bygland.no +bykle.no +barum.no +bærum.no +bo.telemark.no +bø.telemark.no +bo.nordland.no +bø.nordland.no +bievat.no +bievát.no +bomlo.no +bømlo.no +batsfjord.no +båtsfjord.no +bahcavuotna.no +báhcavuotna.no +dovre.no +drammen.no +drangedal.no +dyroy.no +dyrøy.no +donna.no +dønna.no +eid.no +eidfjord.no +eidsberg.no +eidskog.no +eidsvoll.no +eigersund.no +elverum.no +enebakk.no +engerdal.no +etne.no +etnedal.no +evenes.no +evenassi.no +evenášši.no +evje-og-hornnes.no +farsund.no +fauske.no +fuossko.no +fuoisku.no +fedje.no +fet.no +finnoy.no +finnøy.no +fitjar.no +fjaler.no +fjell.no +flakstad.no +flatanger.no +flekkefjord.no +flesberg.no +flora.no +fla.no +flå.no +folldal.no +forsand.no +fosnes.no +frei.no +frogn.no +froland.no +frosta.no +frana.no +fræna.no +froya.no +frøya.no +fusa.no +fyresdal.no +forde.no +førde.no +gamvik.no +gangaviika.no +gáŋgaviika.no +gaular.no +gausdal.no +gildeskal.no +gildeskål.no +giske.no +gjemnes.no +gjerdrum.no +gjerstad.no +gjesdal.no +gjovik.no +gjøvik.no +gloppen.no +gol.no +gran.no +grane.no +granvin.no +gratangen.no +grimstad.no +grong.no +kraanghke.no +kråanghke.no +grue.no +gulen.no +hadsel.no +halden.no +halsa.no +hamar.no +hamaroy.no +habmer.no +hábmer.no +hapmir.no +hápmir.no +hammerfest.no +hammarfeasta.no +hámmárfeasta.no +haram.no +hareid.no +harstad.no +hasvik.no +aknoluokta.no +ákŋoluokta.no +hattfjelldal.no +aarborte.no +haugesund.no +hemne.no +hemnes.no +hemsedal.no +heroy.more-og-romsdal.no +herøy.møre-og-romsdal.no +heroy.nordland.no +herøy.nordland.no +hitra.no +hjartdal.no +hjelmeland.no +hobol.no +hobøl.no +hof.no +hol.no +hole.no +holmestrand.no +holtalen.no +holtålen.no +hornindal.no +horten.no +hurdal.no +hurum.no +hvaler.no +hyllestad.no +hagebostad.no +hægebostad.no +hoyanger.no +høyanger.no +hoylandet.no +høylandet.no +ha.no +hå.no +ibestad.no +inderoy.no +inderøy.no +iveland.no +jevnaker.no +jondal.no +jolster.no +jølster.no +karasjok.no +karasjohka.no +kárášjohka.no +karlsoy.no +galsa.no +gálsá.no +karmoy.no +karmøy.no +kautokeino.no +guovdageaidnu.no +klepp.no +klabu.no +klæbu.no +kongsberg.no +kongsvinger.no +kragero.no +kragerø.no +kristiansand.no +kristiansund.no +krodsherad.no +krødsherad.no +kvalsund.no +rahkkeravju.no +ráhkkerávju.no +kvam.no +kvinesdal.no +kvinnherad.no +kviteseid.no +kvitsoy.no +kvitsøy.no +kvafjord.no +kvæfjord.no +giehtavuoatna.no +kvanangen.no +kvænangen.no +navuotna.no +návuotna.no +kafjord.no +kåfjord.no +gaivuotna.no +gáivuotna.no +larvik.no +lavangen.no +lavagis.no +loabat.no +loabát.no +lebesby.no +davvesiida.no +leikanger.no +leirfjord.no +leka.no +leksvik.no +lenvik.no +leangaviika.no +leaŋgaviika.no +lesja.no +levanger.no +lier.no +lierne.no +lillehammer.no +lillesand.no +lindesnes.no +lindas.no +lindås.no +lom.no +loppa.no +lahppi.no +láhppi.no +lund.no +lunner.no +luroy.no +lurøy.no +luster.no +lyngdal.no +lyngen.no +ivgu.no +lardal.no +lerdal.no +lærdal.no +lodingen.no +lødingen.no +lorenskog.no +lørenskog.no +loten.no +løten.no +malvik.no +masoy.no +måsøy.no +muosat.no +muosát.no +mandal.no +marker.no +marnardal.no +masfjorden.no +meland.no +meldal.no +melhus.no +meloy.no +meløy.no +meraker.no +meråker.no +moareke.no +moåreke.no +midsund.no +midtre-gauldal.no +modalen.no +modum.no +molde.no +moskenes.no +moss.no +mosvik.no +malselv.no +målselv.no +malatvuopmi.no +málatvuopmi.no +namdalseid.no +aejrie.no +namsos.no +namsskogan.no +naamesjevuemie.no +nååmesjevuemie.no +laakesvuemie.no +nannestad.no +narvik.no +narviika.no +naustdal.no +nedre-eiker.no +nes.akershus.no +nes.buskerud.no +nesna.no +nesodden.no +nesseby.no +unjarga.no +unjárga.no +nesset.no +nissedal.no +nittedal.no +nord-aurdal.no +nord-fron.no +nord-odal.no +norddal.no +nordkapp.no +davvenjarga.no +davvenjárga.no +nordre-land.no +nordreisa.no +raisa.no +ráisa.no +nore-og-uvdal.no +notodden.no +naroy.no +nærøy.no +notteroy.no +nøtterøy.no +odda.no +oksnes.no +øksnes.no +oppdal.no +oppegard.no +oppegård.no +orkdal.no +orland.no +ørland.no +orskog.no +ørskog.no +orsta.no +ørsta.no +os.hedmark.no +os.hordaland.no +osen.no +osteroy.no +osterøy.no +ostre-toten.no +østre-toten.no +overhalla.no +ovre-eiker.no +øvre-eiker.no +oyer.no +øyer.no +oygarden.no +øygarden.no +oystre-slidre.no +øystre-slidre.no +porsanger.no +porsangu.no +porsáŋgu.no +porsgrunn.no +radoy.no +radøy.no +rakkestad.no +rana.no +ruovat.no +randaberg.no +rauma.no +rendalen.no +rennebu.no +rennesoy.no +rennesøy.no +rindal.no +ringebu.no +ringerike.no +ringsaker.no +rissa.no +risor.no +risør.no +roan.no +rollag.no +rygge.no +ralingen.no +rælingen.no +rodoy.no +rødøy.no +romskog.no +rømskog.no +roros.no +røros.no +rost.no +røst.no +royken.no +røyken.no +royrvik.no +røyrvik.no +rade.no +råde.no +salangen.no +siellak.no +saltdal.no +salat.no +sálát.no +sálat.no +samnanger.no +sande.more-og-romsdal.no +sande.møre-og-romsdal.no +sande.vestfold.no +sandefjord.no +sandnes.no +sandoy.no +sandøy.no +sarpsborg.no +sauda.no +sauherad.no +sel.no +selbu.no +selje.no +seljord.no +sigdal.no +siljan.no +sirdal.no +skaun.no +skedsmo.no +ski.no +skien.no +skiptvet.no +skjervoy.no +skjervøy.no +skierva.no +skiervá.no +skjak.no +skjåk.no +skodje.no +skanland.no +skånland.no +skanit.no +skánit.no +smola.no +smøla.no +snillfjord.no +snasa.no +snåsa.no +snoasa.no +snaase.no +snåase.no +sogndal.no +sokndal.no +sola.no +solund.no +songdalen.no +sortland.no +spydeberg.no +stange.no +stavanger.no +steigen.no +steinkjer.no +stjordal.no +stjørdal.no +stokke.no +stor-elvdal.no +stord.no +stordal.no +storfjord.no +omasvuotna.no +strand.no +stranda.no +stryn.no +sula.no +suldal.no +sund.no +sunndal.no +surnadal.no +sveio.no +svelvik.no +sykkylven.no +sogne.no +søgne.no +somna.no +sømna.no +sondre-land.no +søndre-land.no +sor-aurdal.no +sør-aurdal.no +sor-fron.no +sør-fron.no +sor-odal.no +sør-odal.no +sor-varanger.no +sør-varanger.no +matta-varjjat.no +mátta-várjjat.no +sorfold.no +sørfold.no +sorreisa.no +sørreisa.no +sorum.no +sørum.no +tana.no +deatnu.no +time.no +tingvoll.no +tinn.no +tjeldsund.no +dielddanuorri.no +tjome.no +tjøme.no +tokke.no +tolga.no +torsken.no +tranoy.no +tranøy.no +tromso.no +tromsø.no +tromsa.no +romsa.no +trondheim.no +troandin.no +trysil.no +trana.no +træna.no +trogstad.no +trøgstad.no +tvedestrand.no +tydal.no +tynset.no +tysfjord.no +divtasvuodna.no +divttasvuotna.no +tysnes.no +tysvar.no +tysvær.no +tonsberg.no +tønsberg.no +ullensaker.no +ullensvang.no +ulvik.no +utsira.no +vadso.no +vadsø.no +cahcesuolo.no +čáhcesuolo.no +vaksdal.no +valle.no +vang.no +vanylven.no +vardo.no +vardø.no +varggat.no +várggát.no +vefsn.no +vaapste.no +vega.no +vegarshei.no +vegårshei.no +vennesla.no +verdal.no +verran.no +vestby.no +vestnes.no +vestre-slidre.no +vestre-toten.no +vestvagoy.no +vestvågøy.no +vevelstad.no +vik.no +vikna.no +vindafjord.no +volda.no +voss.no +varoy.no +værøy.no +vagan.no +vågan.no +voagat.no +vagsoy.no +vågsøy.no +vaga.no +vågå.no +valer.ostfold.no +våler.østfold.no +valer.hedmark.no +våler.hedmark.no + +// np : http://www.mos.com.np/register.html +*.np + +// nr : http://cenpac.net.nr/dns/index.html +// Submitted by registry <technician@cenpac.net.nr> +nr +biz.nr +info.nr +gov.nr +edu.nr +org.nr +net.nr +com.nr + +// nu : https://en.wikipedia.org/wiki/.nu +nu + +// nz : https://en.wikipedia.org/wiki/.nz +// Submitted by registry <jay@nzrs.net.nz> +nz +ac.nz +co.nz +cri.nz +geek.nz +gen.nz +govt.nz +health.nz +iwi.nz +kiwi.nz +maori.nz +mil.nz +māori.nz +net.nz +org.nz +parliament.nz +school.nz + +// om : https://en.wikipedia.org/wiki/.om +om +co.om +com.om +edu.om +gov.om +med.om +museum.om +net.om +org.om +pro.om + +// onion : https://tools.ietf.org/html/rfc7686 +onion + +// org : https://en.wikipedia.org/wiki/.org +org + +// pa : http://www.nic.pa/ +// Some additional second level "domains" resolve directly as hostnames, such as +// pannet.pa, so we add a rule for "pa". +pa +ac.pa +gob.pa +com.pa +org.pa +sld.pa +edu.pa +net.pa +ing.pa +abo.pa +med.pa +nom.pa + +// pe : https://www.nic.pe/InformeFinalComision.pdf +pe +edu.pe +gob.pe +nom.pe +mil.pe +org.pe +com.pe +net.pe + +// pf : http://www.gobin.info/domainname/formulaire-pf.pdf +pf +com.pf +org.pf +edu.pf + +// pg : https://en.wikipedia.org/wiki/.pg +*.pg + +// ph : http://www.domains.ph/FAQ2.asp +// Submitted by registry <jed@email.com.ph> +ph +com.ph +net.ph +org.ph +gov.ph +edu.ph +ngo.ph +mil.ph +i.ph + +// pk : http://pk5.pknic.net.pk/pk5/msgNamepk.PK +pk +com.pk +net.pk +edu.pk +org.pk +fam.pk +biz.pk +web.pk +gov.pk +gob.pk +gok.pk +gon.pk +gop.pk +gos.pk +info.pk + +// pl http://www.dns.pl/english/index.html +// Submitted by registry +pl +com.pl +net.pl +org.pl +// pl functional domains (http://www.dns.pl/english/index.html) +aid.pl +agro.pl +atm.pl +auto.pl +biz.pl +edu.pl +gmina.pl +gsm.pl +info.pl +mail.pl +miasta.pl +media.pl +mil.pl +nieruchomosci.pl +nom.pl +pc.pl +powiat.pl +priv.pl +realestate.pl +rel.pl +sex.pl +shop.pl +sklep.pl +sos.pl +szkola.pl +targi.pl +tm.pl +tourism.pl +travel.pl +turystyka.pl +// Government domains +gov.pl +ap.gov.pl +griw.gov.pl +ic.gov.pl +is.gov.pl +kmpsp.gov.pl +konsulat.gov.pl +kppsp.gov.pl +kwp.gov.pl +kwpsp.gov.pl +mup.gov.pl +mw.gov.pl +oia.gov.pl +oirm.gov.pl +oke.gov.pl +oow.gov.pl +oschr.gov.pl +oum.gov.pl +pa.gov.pl +pinb.gov.pl +piw.gov.pl +po.gov.pl +pr.gov.pl +psp.gov.pl +psse.gov.pl +pup.gov.pl +rzgw.gov.pl +sa.gov.pl +sdn.gov.pl +sko.gov.pl +so.gov.pl +sr.gov.pl +starostwo.gov.pl +ug.gov.pl +ugim.gov.pl +um.gov.pl +umig.gov.pl +upow.gov.pl +uppo.gov.pl +us.gov.pl +uw.gov.pl +uzs.gov.pl +wif.gov.pl +wiih.gov.pl +winb.gov.pl +wios.gov.pl +witd.gov.pl +wiw.gov.pl +wkz.gov.pl +wsa.gov.pl +wskr.gov.pl +wsse.gov.pl +wuoz.gov.pl +wzmiuw.gov.pl +zp.gov.pl +zpisdn.gov.pl +// pl regional domains (http://www.dns.pl/english/index.html) +augustow.pl +babia-gora.pl +bedzin.pl +beskidy.pl +bialowieza.pl +bialystok.pl +bielawa.pl +bieszczady.pl +boleslawiec.pl +bydgoszcz.pl +bytom.pl +cieszyn.pl +czeladz.pl +czest.pl +dlugoleka.pl +elblag.pl +elk.pl +glogow.pl +gniezno.pl +gorlice.pl +grajewo.pl +ilawa.pl +jaworzno.pl +jelenia-gora.pl +jgora.pl +kalisz.pl +kazimierz-dolny.pl +karpacz.pl +kartuzy.pl +kaszuby.pl +katowice.pl +kepno.pl +ketrzyn.pl +klodzko.pl +kobierzyce.pl +kolobrzeg.pl +konin.pl +konskowola.pl +kutno.pl +lapy.pl +lebork.pl +legnica.pl +lezajsk.pl +limanowa.pl +lomza.pl +lowicz.pl +lubin.pl +lukow.pl +malbork.pl +malopolska.pl +mazowsze.pl +mazury.pl +mielec.pl +mielno.pl +mragowo.pl +naklo.pl +nowaruda.pl +nysa.pl +olawa.pl +olecko.pl +olkusz.pl +olsztyn.pl +opoczno.pl +opole.pl +ostroda.pl +ostroleka.pl +ostrowiec.pl +ostrowwlkp.pl +pila.pl +pisz.pl +podhale.pl +podlasie.pl +polkowice.pl +pomorze.pl +pomorskie.pl +prochowice.pl +pruszkow.pl +przeworsk.pl +pulawy.pl +radom.pl +rawa-maz.pl +rybnik.pl +rzeszow.pl +sanok.pl +sejny.pl +slask.pl +slupsk.pl +sosnowiec.pl +stalowa-wola.pl +skoczow.pl +starachowice.pl +stargard.pl +suwalki.pl +swidnica.pl +swiebodzin.pl +swinoujscie.pl +szczecin.pl +szczytno.pl +tarnobrzeg.pl +tgory.pl +turek.pl +tychy.pl +ustka.pl +walbrzych.pl +warmia.pl +warszawa.pl +waw.pl +wegrow.pl +wielun.pl +wlocl.pl +wloclawek.pl +wodzislaw.pl +wolomin.pl +wroclaw.pl +zachpomor.pl +zagan.pl +zarow.pl +zgora.pl +zgorzelec.pl + +// pm : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf +pm + +// pn : http://www.government.pn/PnRegistry/policies.htm +pn +gov.pn +co.pn +org.pn +edu.pn +net.pn + +// post : https://en.wikipedia.org/wiki/.post +post + +// pr : http://www.nic.pr/index.asp?f=1 +pr +com.pr +net.pr +org.pr +gov.pr +edu.pr +isla.pr +pro.pr +biz.pr +info.pr +name.pr +// these aren't mentioned on nic.pr, but on https://en.wikipedia.org/wiki/.pr +est.pr +prof.pr +ac.pr + +// pro : http://registry.pro/get-pro +pro +aaa.pro +aca.pro +acct.pro +avocat.pro +bar.pro +cpa.pro +eng.pro +jur.pro +law.pro +med.pro +recht.pro + +// ps : https://en.wikipedia.org/wiki/.ps +// http://www.nic.ps/registration/policy.html#reg +ps +edu.ps +gov.ps +sec.ps +plo.ps +com.ps +org.ps +net.ps + +// pt : https://www.dns.pt/en/domain/pt-terms-and-conditions-registration-rules/ +pt +net.pt +gov.pt +org.pt +edu.pt +int.pt +publ.pt +com.pt +nome.pt + +// pw : https://en.wikipedia.org/wiki/.pw +pw +co.pw +ne.pw +or.pw +ed.pw +go.pw +belau.pw + +// py : http://www.nic.py/pautas.html#seccion_9 +// Submitted by registry +py +com.py +coop.py +edu.py +gov.py +mil.py +net.py +org.py + +// qa : http://domains.qa/en/ +qa +com.qa +edu.qa +gov.qa +mil.qa +name.qa +net.qa +org.qa +sch.qa + +// re : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf +re +asso.re +com.re +nom.re + +// ro : http://www.rotld.ro/ +ro +arts.ro +com.ro +firm.ro +info.ro +nom.ro +nt.ro +org.ro +rec.ro +store.ro +tm.ro +www.ro + +// rs : https://www.rnids.rs/en/domains/national-domains +rs +ac.rs +co.rs +edu.rs +gov.rs +in.rs +org.rs + +// ru : https://cctld.ru/files/pdf/docs/en/rules_ru-rf.pdf +// Submitted by George Georgievsky <gug@cctld.ru> +ru + +// rw : https://www.ricta.org.rw/sites/default/files/resources/registry_registrar_contract_0.pdf +rw +ac.rw +co.rw +coop.rw +gov.rw +mil.rw +net.rw +org.rw + +// sa : http://www.nic.net.sa/ +sa +com.sa +net.sa +org.sa +gov.sa +med.sa +pub.sa +edu.sa +sch.sa + +// sb : http://www.sbnic.net.sb/ +// Submitted by registry <lee.humphries@telekom.com.sb> +sb +com.sb +edu.sb +gov.sb +net.sb +org.sb + +// sc : http://www.nic.sc/ +sc +com.sc +gov.sc +net.sc +org.sc +edu.sc + +// sd : http://www.isoc.sd/sudanic.isoc.sd/billing_pricing.htm +// Submitted by registry <admin@isoc.sd> +sd +com.sd +net.sd +org.sd +edu.sd +med.sd +tv.sd +gov.sd +info.sd + +// se : https://en.wikipedia.org/wiki/.se +// Submitted by registry <patrik.wallstrom@iis.se> +se +a.se +ac.se +b.se +bd.se +brand.se +c.se +d.se +e.se +f.se +fh.se +fhsk.se +fhv.se +g.se +h.se +i.se +k.se +komforb.se +kommunalforbund.se +komvux.se +l.se +lanbib.se +m.se +n.se +naturbruksgymn.se +o.se +org.se +p.se +parti.se +pp.se +press.se +r.se +s.se +t.se +tm.se +u.se +w.se +x.se +y.se +z.se + +// sg : http://www.nic.net.sg/page/registration-policies-procedures-and-guidelines +sg +com.sg +net.sg +org.sg +gov.sg +edu.sg +per.sg + +// sh : http://nic.sh/rules.htm +sh +com.sh +net.sh +gov.sh +org.sh +mil.sh + +// si : https://en.wikipedia.org/wiki/.si +si + +// sj : No registrations at this time. +// Submitted by registry <jarle@uninett.no> +sj + +// sk : https://en.wikipedia.org/wiki/.sk +// list of 2nd level domains ? +sk + +// sl : http://www.nic.sl +// Submitted by registry <adam@neoip.com> +sl +com.sl +net.sl +edu.sl +gov.sl +org.sl + +// sm : https://en.wikipedia.org/wiki/.sm +sm + +// sn : https://en.wikipedia.org/wiki/.sn +sn +art.sn +com.sn +edu.sn +gouv.sn +org.sn +perso.sn +univ.sn + +// so : http://sonic.so/policies/ +so +com.so +edu.so +gov.so +me.so +net.so +org.so + +// sr : https://en.wikipedia.org/wiki/.sr +sr + +// ss : https://registry.nic.ss/ +// Submitted by registry <technical@nic.ss> +ss +biz.ss +com.ss +edu.ss +gov.ss +me.ss +net.ss +org.ss +sch.ss + +// st : http://www.nic.st/html/policyrules/ +st +co.st +com.st +consulado.st +edu.st +embaixada.st +mil.st +net.st +org.st +principe.st +saotome.st +store.st + +// su : https://en.wikipedia.org/wiki/.su +su + +// sv : http://www.svnet.org.sv/niveldos.pdf +sv +com.sv +edu.sv +gob.sv +org.sv +red.sv + +// sx : https://en.wikipedia.org/wiki/.sx +// Submitted by registry <jcvignes@openregistry.com> +sx +gov.sx + +// sy : https://en.wikipedia.org/wiki/.sy +// see also: http://www.gobin.info/domainname/sy.doc +sy +edu.sy +gov.sy +net.sy +mil.sy +com.sy +org.sy + +// sz : https://en.wikipedia.org/wiki/.sz +// http://www.sispa.org.sz/ +sz +co.sz +ac.sz +org.sz + +// tc : https://en.wikipedia.org/wiki/.tc +tc + +// td : https://en.wikipedia.org/wiki/.td +td + +// tel: https://en.wikipedia.org/wiki/.tel +// http://www.telnic.org/ +tel + +// tf : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf +tf + +// tg : https://en.wikipedia.org/wiki/.tg +// http://www.nic.tg/ +tg + +// th : https://en.wikipedia.org/wiki/.th +// Submitted by registry <krit@thains.co.th> +th +ac.th +co.th +go.th +in.th +mi.th +net.th +or.th + +// tj : http://www.nic.tj/policy.html +tj +ac.tj +biz.tj +co.tj +com.tj +edu.tj +go.tj +gov.tj +int.tj +mil.tj +name.tj +net.tj +nic.tj +org.tj +test.tj +web.tj + +// tk : https://en.wikipedia.org/wiki/.tk +tk + +// tl : https://en.wikipedia.org/wiki/.tl +tl +gov.tl + +// tm : http://www.nic.tm/local.html +tm +com.tm +co.tm +org.tm +net.tm +nom.tm +gov.tm +mil.tm +edu.tm + +// tn : http://www.registre.tn/fr/ +// https://whois.ati.tn/ +tn +com.tn +ens.tn +fin.tn +gov.tn +ind.tn +info.tn +intl.tn +mincom.tn +nat.tn +net.tn +org.tn +perso.tn +tourism.tn + +// to : https://en.wikipedia.org/wiki/.to +// Submitted by registry <egullich@colo.to> +to +com.to +gov.to +net.to +org.to +edu.to +mil.to + +// tr : https://nic.tr/ +// https://nic.tr/forms/eng/policies.pdf +// https://nic.tr/index.php?USRACTN=PRICELST +tr +av.tr +bbs.tr +bel.tr +biz.tr +com.tr +dr.tr +edu.tr +gen.tr +gov.tr +info.tr +mil.tr +k12.tr +kep.tr +name.tr +net.tr +org.tr +pol.tr +tel.tr +tsk.tr +tv.tr +web.tr +// Used by Northern Cyprus +nc.tr +// Used by government agencies of Northern Cyprus +gov.nc.tr + +// tt : http://www.nic.tt/ +tt +co.tt +com.tt +org.tt +net.tt +biz.tt +info.tt +pro.tt +int.tt +coop.tt +jobs.tt +mobi.tt +travel.tt +museum.tt +aero.tt +name.tt +gov.tt +edu.tt + +// tv : https://en.wikipedia.org/wiki/.tv +// Not listing any 2LDs as reserved since none seem to exist in practice, +// Wikipedia notwithstanding. +tv + +// tw : https://en.wikipedia.org/wiki/.tw +tw +edu.tw +gov.tw +mil.tw +com.tw +net.tw +org.tw +idv.tw +game.tw +ebiz.tw +club.tw +網路.tw +組織.tw +商業.tw + +// tz : http://www.tznic.or.tz/index.php/domains +// Submitted by registry <manager@tznic.or.tz> +tz +ac.tz +co.tz +go.tz +hotel.tz +info.tz +me.tz +mil.tz +mobi.tz +ne.tz +or.tz +sc.tz +tv.tz + +// ua : https://hostmaster.ua/policy/?ua +// Submitted by registry <dk@cctld.ua> +ua +// ua 2LD +com.ua +edu.ua +gov.ua +in.ua +net.ua +org.ua +// ua geographic names +// https://hostmaster.ua/2ld/ +cherkassy.ua +cherkasy.ua +chernigov.ua +chernihiv.ua +chernivtsi.ua +chernovtsy.ua +ck.ua +cn.ua +cr.ua +crimea.ua +cv.ua +dn.ua +dnepropetrovsk.ua +dnipropetrovsk.ua +donetsk.ua +dp.ua +if.ua +ivano-frankivsk.ua +kh.ua +kharkiv.ua +kharkov.ua +kherson.ua +khmelnitskiy.ua +khmelnytskyi.ua +kiev.ua +kirovograd.ua +km.ua +kr.ua +kropyvnytskyi.ua +krym.ua +ks.ua +kv.ua +kyiv.ua +lg.ua +lt.ua +lugansk.ua +luhansk.ua +lutsk.ua +lv.ua +lviv.ua +mk.ua +mykolaiv.ua +nikolaev.ua +od.ua +odesa.ua +odessa.ua +pl.ua +poltava.ua +rivne.ua +rovno.ua +rv.ua +sb.ua +sebastopol.ua +sevastopol.ua +sm.ua +sumy.ua +te.ua +ternopil.ua +uz.ua +uzhgorod.ua +uzhhorod.ua +vinnica.ua +vinnytsia.ua +vn.ua +volyn.ua +yalta.ua +zakarpattia.ua +zaporizhzhe.ua +zaporizhzhia.ua +zhitomir.ua +zhytomyr.ua +zp.ua +zt.ua + +// ug : https://www.registry.co.ug/ +ug +co.ug +or.ug +ac.ug +sc.ug +go.ug +ne.ug +com.ug +org.ug + +// uk : https://en.wikipedia.org/wiki/.uk +// Submitted by registry <Michael.Daly@nominet.org.uk> +uk +ac.uk +co.uk +gov.uk +ltd.uk +me.uk +net.uk +nhs.uk +org.uk +plc.uk +police.uk +*.sch.uk + +// us : https://en.wikipedia.org/wiki/.us +us +dni.us +fed.us +isa.us +kids.us +nsn.us +// us geographic names +ak.us +al.us +ar.us +as.us +az.us +ca.us +co.us +ct.us +dc.us +de.us +fl.us +ga.us +gu.us +hi.us +ia.us +id.us +il.us +in.us +ks.us +ky.us +la.us +ma.us +md.us +me.us +mi.us +mn.us +mo.us +ms.us +mt.us +nc.us +nd.us +ne.us +nh.us +nj.us +nm.us +nv.us +ny.us +oh.us +ok.us +or.us +pa.us +pr.us +ri.us +sc.us +sd.us +tn.us +tx.us +ut.us +vi.us +vt.us +va.us +wa.us +wi.us +wv.us +wy.us +// The registrar notes several more specific domains available in each state, +// such as state.*.us, dst.*.us, etc., but resolution of these is somewhat +// haphazard; in some states these domains resolve as addresses, while in others +// only subdomains are available, or even nothing at all. We include the +// most common ones where it's clear that different sites are different +// entities. +k12.ak.us +k12.al.us +k12.ar.us +k12.as.us +k12.az.us +k12.ca.us +k12.co.us +k12.ct.us +k12.dc.us +k12.fl.us +k12.ga.us +k12.gu.us +// k12.hi.us Bug 614565 - Hawaii has a state-wide DOE login +k12.ia.us +k12.id.us +k12.il.us +k12.in.us +k12.ks.us +k12.ky.us +k12.la.us +k12.ma.us +k12.md.us +k12.me.us +k12.mi.us +k12.mn.us +k12.mo.us +k12.ms.us +k12.mt.us +k12.nc.us +// k12.nd.us Bug 1028347 - Removed at request of Travis Rosso <trossow@nd.gov> +k12.ne.us +k12.nh.us +k12.nj.us +k12.nm.us +k12.nv.us +k12.ny.us +k12.oh.us +k12.ok.us +k12.or.us +k12.pa.us +k12.pr.us +// k12.ri.us Removed at request of Kim Cournoyer <netsupport@staff.ri.net> +k12.sc.us +// k12.sd.us Bug 934131 - Removed at request of James Booze <James.Booze@k12.sd.us> +k12.tn.us +k12.tx.us +k12.ut.us +k12.vi.us +k12.vt.us +k12.va.us +k12.wa.us +k12.wi.us +// k12.wv.us Bug 947705 - Removed at request of Verne Britton <verne@wvnet.edu> +k12.wy.us +cc.ak.us +cc.al.us +cc.ar.us +cc.as.us +cc.az.us +cc.ca.us +cc.co.us +cc.ct.us +cc.dc.us +cc.de.us +cc.fl.us +cc.ga.us +cc.gu.us +cc.hi.us +cc.ia.us +cc.id.us +cc.il.us +cc.in.us +cc.ks.us +cc.ky.us +cc.la.us +cc.ma.us +cc.md.us +cc.me.us +cc.mi.us +cc.mn.us +cc.mo.us +cc.ms.us +cc.mt.us +cc.nc.us +cc.nd.us +cc.ne.us +cc.nh.us +cc.nj.us +cc.nm.us +cc.nv.us +cc.ny.us +cc.oh.us +cc.ok.us +cc.or.us +cc.pa.us +cc.pr.us +cc.ri.us +cc.sc.us +cc.sd.us +cc.tn.us +cc.tx.us +cc.ut.us +cc.vi.us +cc.vt.us +cc.va.us +cc.wa.us +cc.wi.us +cc.wv.us +cc.wy.us +lib.ak.us +lib.al.us +lib.ar.us +lib.as.us +lib.az.us +lib.ca.us +lib.co.us +lib.ct.us +lib.dc.us +// lib.de.us Issue #243 - Moved to Private section at request of Ed Moore <Ed.Moore@lib.de.us> +lib.fl.us +lib.ga.us +lib.gu.us +lib.hi.us +lib.ia.us +lib.id.us +lib.il.us +lib.in.us +lib.ks.us +lib.ky.us +lib.la.us +lib.ma.us +lib.md.us +lib.me.us +lib.mi.us +lib.mn.us +lib.mo.us +lib.ms.us +lib.mt.us +lib.nc.us +lib.nd.us +lib.ne.us +lib.nh.us +lib.nj.us +lib.nm.us +lib.nv.us +lib.ny.us +lib.oh.us +lib.ok.us +lib.or.us +lib.pa.us +lib.pr.us +lib.ri.us +lib.sc.us +lib.sd.us +lib.tn.us +lib.tx.us +lib.ut.us +lib.vi.us +lib.vt.us +lib.va.us +lib.wa.us +lib.wi.us +// lib.wv.us Bug 941670 - Removed at request of Larry W Arnold <arnold@wvlc.lib.wv.us> +lib.wy.us +// k12.ma.us contains school districts in Massachusetts. The 4LDs are +// managed independently except for private (PVT), charter (CHTR) and +// parochial (PAROCH) schools. Those are delegated directly to the +// 5LD operators. <k12-ma-hostmaster _ at _ rsuc.gweep.net> +pvt.k12.ma.us +chtr.k12.ma.us +paroch.k12.ma.us +// Merit Network, Inc. maintains the registry for =~ /(k12|cc|lib).mi.us/ and the following +// see also: http://domreg.merit.edu +// see also: whois -h whois.domreg.merit.edu help +ann-arbor.mi.us +cog.mi.us +dst.mi.us +eaton.mi.us +gen.mi.us +mus.mi.us +tec.mi.us +washtenaw.mi.us + +// uy : http://www.nic.org.uy/ +uy +com.uy +edu.uy +gub.uy +mil.uy +net.uy +org.uy + +// uz : http://www.reg.uz/ +uz +co.uz +com.uz +net.uz +org.uz + +// va : https://en.wikipedia.org/wiki/.va +va + +// vc : https://en.wikipedia.org/wiki/.vc +// Submitted by registry <kshah@ca.afilias.info> +vc +com.vc +net.vc +org.vc +gov.vc +mil.vc +edu.vc + +// ve : https://registro.nic.ve/ +// Submitted by registry nic@nic.ve and nicve@conatel.gob.ve +ve +arts.ve +bib.ve +co.ve +com.ve +e12.ve +edu.ve +firm.ve +gob.ve +gov.ve +info.ve +int.ve +mil.ve +net.ve +nom.ve +org.ve +rar.ve +rec.ve +store.ve +tec.ve +web.ve + +// vg : https://en.wikipedia.org/wiki/.vg +vg + +// vi : http://www.nic.vi/newdomainform.htm +// http://www.nic.vi/Domain_Rules/body_domain_rules.html indicates some other +// TLDs are "reserved", such as edu.vi and gov.vi, but doesn't actually say they +// are available for registration (which they do not seem to be). +vi +co.vi +com.vi +k12.vi +net.vi +org.vi + +// vn : https://www.vnnic.vn/en/domain/cctld-vn +// https://vnnic.vn/sites/default/files/tailieu/vn.cctld.domains.txt +vn +ac.vn +ai.vn +biz.vn +com.vn +edu.vn +gov.vn +health.vn +id.vn +info.vn +int.vn +io.vn +name.vn +net.vn +org.vn +pro.vn + +// vn geographical names +angiang.vn +bacgiang.vn +backan.vn +baclieu.vn +bacninh.vn +baria-vungtau.vn +bentre.vn +binhdinh.vn +binhduong.vn +binhphuoc.vn +binhthuan.vn +camau.vn +cantho.vn +caobang.vn +daklak.vn +daknong.vn +danang.vn +dienbien.vn +dongnai.vn +dongthap.vn +gialai.vn +hagiang.vn +haiduong.vn +haiphong.vn +hanam.vn +hanoi.vn +hatinh.vn +haugiang.vn +hoabinh.vn +hungyen.vn +khanhhoa.vn +kiengiang.vn +kontum.vn +laichau.vn +lamdong.vn +langson.vn +laocai.vn +longan.vn +namdinh.vn +nghean.vn +ninhbinh.vn +ninhthuan.vn +phutho.vn +phuyen.vn +quangbinh.vn +quangnam.vn +quangngai.vn +quangninh.vn +quangtri.vn +soctrang.vn +sonla.vn +tayninh.vn +thaibinh.vn +thainguyen.vn +thanhhoa.vn +thanhphohochiminh.vn +thuathienhue.vn +tiengiang.vn +travinh.vn +tuyenquang.vn +vinhlong.vn +vinhphuc.vn +yenbai.vn + +// vu : https://en.wikipedia.org/wiki/.vu +// http://www.vunic.vu/ +vu +com.vu +edu.vu +net.vu +org.vu + +// wf : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf +wf + +// ws : https://en.wikipedia.org/wiki/.ws +// http://samoanic.ws/index.dhtml +ws +com.ws +net.ws +org.ws +gov.ws +edu.ws + +// yt : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf +yt + +// IDN ccTLDs +// When submitting patches, please maintain a sort by ISO 3166 ccTLD, then +// U-label, and follow this format: +// // A-Label ("<Latin renderings>", <language name>[, variant info]) : <ISO 3166 ccTLD> +// // [sponsoring org] +// U-Label + +// xn--mgbaam7a8h ("Emerat", Arabic) : AE +// http://nic.ae/english/arabicdomain/rules.jsp +امارات + +// xn--y9a3aq ("hye", Armenian) : AM +// ISOC AM (operated by .am Registry) +հայ + +// xn--54b7fta0cc ("Bangla", Bangla) : BD +বাংলা + +// xn--90ae ("bg", Bulgarian) : BG +бг + +// xn--mgbcpq6gpa1a ("albahrain", Arabic) : BH +البحرين + +// xn--90ais ("bel", Belarusian/Russian Cyrillic) : BY +// Operated by .by registry +бел + +// xn--fiqs8s ("Zhongguo/China", Chinese, Simplified) : CN +// CNNIC +// http://cnnic.cn/html/Dir/2005/10/11/3218.htm +中国 + +// xn--fiqz9s ("Zhongguo/China", Chinese, Traditional) : CN +// CNNIC +// http://cnnic.cn/html/Dir/2005/10/11/3218.htm +中國 + +// xn--lgbbat1ad8j ("Algeria/Al Jazair", Arabic) : DZ +الجزائر + +// xn--wgbh1c ("Egypt/Masr", Arabic) : EG +// http://www.dotmasr.eg/ +مصر + +// xn--e1a4c ("eu", Cyrillic) : EU +// https://eurid.eu +ею + +// xn--qxa6a ("eu", Greek) : EU +// https://eurid.eu +ευ + +// xn--mgbah1a3hjkrd ("Mauritania", Arabic) : MR +موريتانيا + +// xn--node ("ge", Georgian Mkhedruli) : GE +გე + +// xn--qxam ("el", Greek) : GR +// Hellenic Ministry of Infrastructure, Transport, and Networks +ελ + +// xn--j6w193g ("Hong Kong", Chinese) : HK +// https://www.hkirc.hk +// Submitted by registry <hk.tech@hkirc.hk> +// https://www.hkirc.hk/content.jsp?id=30#!/34 +香港 +公司.香港 +教育.香港 +政府.香港 +個人.香港 +網絡.香港 +組織.香港 + +// xn--2scrj9c ("Bharat", Kannada) : IN +// India +ಭಾರತ + +// xn--3hcrj9c ("Bharat", Oriya) : IN +// India +ଭାରତ + +// xn--45br5cyl ("Bharatam", Assamese) : IN +// India +ভাৰত + +// xn--h2breg3eve ("Bharatam", Sanskrit) : IN +// India +भारतम् + +// xn--h2brj9c8c ("Bharot", Santali) : IN +// India +भारोत + +// xn--mgbgu82a ("Bharat", Sindhi) : IN +// India +ڀارت + +// xn--rvc1e0am3e ("Bharatam", Malayalam) : IN +// India +ഭാരതം + +// xn--h2brj9c ("Bharat", Devanagari) : IN +// India +भारत + +// xn--mgbbh1a ("Bharat", Kashmiri) : IN +// India +بارت + +// xn--mgbbh1a71e ("Bharat", Arabic) : IN +// India +بھارت + +// xn--fpcrj9c3d ("Bharat", Telugu) : IN +// India +భారత్ + +// xn--gecrj9c ("Bharat", Gujarati) : IN +// India +ભારત + +// xn--s9brj9c ("Bharat", Gurmukhi) : IN +// India +ਭਾਰਤ + +// xn--45brj9c ("Bharat", Bengali) : IN +// India +ভারত + +// xn--xkc2dl3a5ee0h ("India", Tamil) : IN +// India +இந்தியா + +// xn--mgba3a4f16a ("Iran", Persian) : IR +ایران + +// xn--mgba3a4fra ("Iran", Arabic) : IR +ايران + +// xn--mgbtx2b ("Iraq", Arabic) : IQ +// Communications and Media Commission +عراق + +// xn--mgbayh7gpa ("al-Ordon", Arabic) : JO +// National Information Technology Center (NITC) +// Royal Scientific Society, Al-Jubeiha +الاردن + +// xn--3e0b707e ("Republic of Korea", Hangul) : KR +한국 + +// xn--80ao21a ("Kaz", Kazakh) : KZ +қаз + +// xn--q7ce6a ("Lao", Lao) : LA +ລາວ + +// xn--fzc2c9e2c ("Lanka", Sinhalese-Sinhala) : LK +// https://nic.lk +ලංකා + +// xn--xkc2al3hye2a ("Ilangai", Tamil) : LK +// https://nic.lk +இலங்கை + +// xn--mgbc0a9azcg ("Morocco/al-Maghrib", Arabic) : MA +المغرب + +// xn--d1alf ("mkd", Macedonian) : MK +// MARnet +мкд + +// xn--l1acc ("mon", Mongolian) : MN +мон + +// xn--mix891f ("Macao", Chinese, Traditional) : MO +// MONIC / HNET Asia (Registry Operator for .mo) +澳門 + +// xn--mix082f ("Macao", Chinese, Simplified) : MO +澳门 + +// xn--mgbx4cd0ab ("Malaysia", Malay) : MY +مليسيا + +// xn--mgb9awbf ("Oman", Arabic) : OM +عمان + +// xn--mgbai9azgqp6j ("Pakistan", Urdu/Arabic) : PK +پاکستان + +// xn--mgbai9a5eva00b ("Pakistan", Urdu/Arabic, variant) : PK +پاكستان + +// xn--ygbi2ammx ("Falasteen", Arabic) : PS +// The Palestinian National Internet Naming Authority (PNINA) +// http://www.pnina.ps +فلسطين + +// xn--90a3ac ("srb", Cyrillic) : RS +// https://www.rnids.rs/en/domains/national-domains +срб +пр.срб +орг.срб +обр.срб +од.срб +упр.срб +ак.срб + +// xn--p1ai ("rf", Russian-Cyrillic) : RU +// https://cctld.ru/files/pdf/docs/en/rules_ru-rf.pdf +// Submitted by George Georgievsky <gug@cctld.ru> +рф + +// xn--wgbl6a ("Qatar", Arabic) : QA +// http://www.ict.gov.qa/ +قطر + +// xn--mgberp4a5d4ar ("AlSaudiah", Arabic) : SA +// http://www.nic.net.sa/ +السعودية + +// xn--mgberp4a5d4a87g ("AlSaudiah", Arabic, variant) : SA +السعودیة + +// xn--mgbqly7c0a67fbc ("AlSaudiah", Arabic, variant) : SA +السعودیۃ + +// xn--mgbqly7cvafr ("AlSaudiah", Arabic, variant) : SA +السعوديه + +// xn--mgbpl2fh ("sudan", Arabic) : SD +// Operated by .sd registry +سودان + +// xn--yfro4i67o Singapore ("Singapore", Chinese) : SG +新加坡 + +// xn--clchc0ea0b2g2a9gcd ("Singapore", Tamil) : SG +சிங்கப்பூர் + +// xn--ogbpf8fl ("Syria", Arabic) : SY +سورية + +// xn--mgbtf8fl ("Syria", Arabic, variant) : SY +سوريا + +// xn--o3cw4h ("Thai", Thai) : TH +// http://www.thnic.co.th +ไทย +ศึกษา.ไทย +ธุรกิจ.ไทย +รัฐบาล.ไทย +ทหาร.ไทย +เน็ต.ไทย +องค์กร.ไทย + +// xn--pgbs0dh ("Tunisia", Arabic) : TN +// http://nic.tn +تونس + +// xn--kpry57d ("Taiwan", Chinese, Traditional) : TW +// http://www.twnic.net/english/dn/dn_07a.htm +台灣 + +// xn--kprw13d ("Taiwan", Chinese, Simplified) : TW +// http://www.twnic.net/english/dn/dn_07a.htm +台湾 + +// xn--nnx388a ("Taiwan", Chinese, variant) : TW +臺灣 + +// xn--j1amh ("ukr", Cyrillic) : UA +укр + +// xn--mgb2ddes ("AlYemen", Arabic) : YE +اليمن + +// xxx : http://icmregistry.com +xxx + +// ye : http://www.y.net.ye/services/domain_name.htm +ye +com.ye +edu.ye +gov.ye +net.ye +mil.ye +org.ye + +// za : https://www.zadna.org.za/content/page/domain-information/ +ac.za +agric.za +alt.za +co.za +edu.za +gov.za +grondar.za +law.za +mil.za +net.za +ngo.za +nic.za +nis.za +nom.za +org.za +school.za +tm.za +web.za + +// zm : https://zicta.zm/ +// Submitted by registry <info@zicta.zm> +zm +ac.zm +biz.zm +co.zm +com.zm +edu.zm +gov.zm +info.zm +mil.zm +net.zm +org.zm +sch.zm + +// zw : https://www.potraz.gov.zw/ +// Confirmed by registry <bmtengwa@potraz.gov.zw> 2017-01-25 +zw +ac.zw +co.zw +gov.zw +mil.zw +org.zw + + +// newGTLDs + +// List of new gTLDs imported from https://www.icann.org/resources/registries/gtlds/v2/gtlds.json on 2024-03-06T15:14:58Z +// This list is auto-generated, don't edit it manually. +// aaa : American Automobile Association, Inc. +// https://www.iana.org/domains/root/db/aaa.html +aaa + +// aarp : AARP +// https://www.iana.org/domains/root/db/aarp.html +aarp + +// abb : ABB Ltd +// https://www.iana.org/domains/root/db/abb.html +abb + +// abbott : Abbott Laboratories, Inc. +// https://www.iana.org/domains/root/db/abbott.html +abbott + +// abbvie : AbbVie Inc. +// https://www.iana.org/domains/root/db/abbvie.html +abbvie + +// abc : Disney Enterprises, Inc. +// https://www.iana.org/domains/root/db/abc.html +abc + +// able : Able Inc. +// https://www.iana.org/domains/root/db/able.html +able + +// abogado : Registry Services, LLC +// https://www.iana.org/domains/root/db/abogado.html +abogado + +// abudhabi : Abu Dhabi Systems and Information Centre +// https://www.iana.org/domains/root/db/abudhabi.html +abudhabi + +// academy : Binky Moon, LLC +// https://www.iana.org/domains/root/db/academy.html +academy + +// accenture : Accenture plc +// https://www.iana.org/domains/root/db/accenture.html +accenture + +// accountant : dot Accountant Limited +// https://www.iana.org/domains/root/db/accountant.html +accountant + +// accountants : Binky Moon, LLC +// https://www.iana.org/domains/root/db/accountants.html +accountants + +// aco : ACO Severin Ahlmann GmbH & Co. KG +// https://www.iana.org/domains/root/db/aco.html +aco + +// actor : Dog Beach, LLC +// https://www.iana.org/domains/root/db/actor.html +actor + +// ads : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/ads.html +ads + +// adult : ICM Registry AD LLC +// https://www.iana.org/domains/root/db/adult.html +adult + +// aeg : Aktiebolaget Electrolux +// https://www.iana.org/domains/root/db/aeg.html +aeg + +// aetna : Aetna Life Insurance Company +// https://www.iana.org/domains/root/db/aetna.html +aetna + +// afl : Australian Football League +// https://www.iana.org/domains/root/db/afl.html +afl + +// africa : ZA Central Registry NPC trading as Registry.Africa +// https://www.iana.org/domains/root/db/africa.html +africa + +// agakhan : Fondation Aga Khan (Aga Khan Foundation) +// https://www.iana.org/domains/root/db/agakhan.html +agakhan + +// agency : Binky Moon, LLC +// https://www.iana.org/domains/root/db/agency.html +agency + +// aig : American International Group, Inc. +// https://www.iana.org/domains/root/db/aig.html +aig + +// airbus : Airbus S.A.S. +// https://www.iana.org/domains/root/db/airbus.html +airbus + +// airforce : Dog Beach, LLC +// https://www.iana.org/domains/root/db/airforce.html +airforce + +// airtel : Bharti Airtel Limited +// https://www.iana.org/domains/root/db/airtel.html +airtel + +// akdn : Fondation Aga Khan (Aga Khan Foundation) +// https://www.iana.org/domains/root/db/akdn.html +akdn + +// alibaba : Alibaba Group Holding Limited +// https://www.iana.org/domains/root/db/alibaba.html +alibaba + +// alipay : Alibaba Group Holding Limited +// https://www.iana.org/domains/root/db/alipay.html +alipay + +// allfinanz : Allfinanz Deutsche Vermögensberatung Aktiengesellschaft +// https://www.iana.org/domains/root/db/allfinanz.html +allfinanz + +// allstate : Allstate Fire and Casualty Insurance Company +// https://www.iana.org/domains/root/db/allstate.html +allstate + +// ally : Ally Financial Inc. +// https://www.iana.org/domains/root/db/ally.html +ally + +// alsace : Region Grand Est +// https://www.iana.org/domains/root/db/alsace.html +alsace + +// alstom : ALSTOM +// https://www.iana.org/domains/root/db/alstom.html +alstom + +// amazon : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/amazon.html +amazon + +// americanexpress : American Express Travel Related Services Company, Inc. +// https://www.iana.org/domains/root/db/americanexpress.html +americanexpress + +// americanfamily : AmFam, Inc. +// https://www.iana.org/domains/root/db/americanfamily.html +americanfamily + +// amex : American Express Travel Related Services Company, Inc. +// https://www.iana.org/domains/root/db/amex.html +amex + +// amfam : AmFam, Inc. +// https://www.iana.org/domains/root/db/amfam.html +amfam + +// amica : Amica Mutual Insurance Company +// https://www.iana.org/domains/root/db/amica.html +amica + +// amsterdam : Gemeente Amsterdam +// https://www.iana.org/domains/root/db/amsterdam.html +amsterdam + +// analytics : Campus IP LLC +// https://www.iana.org/domains/root/db/analytics.html +analytics + +// android : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/android.html +android + +// anquan : Beijing Qihu Keji Co., Ltd. +// https://www.iana.org/domains/root/db/anquan.html +anquan + +// anz : Australia and New Zealand Banking Group Limited +// https://www.iana.org/domains/root/db/anz.html +anz + +// aol : Oath Inc. +// https://www.iana.org/domains/root/db/aol.html +aol + +// apartments : Binky Moon, LLC +// https://www.iana.org/domains/root/db/apartments.html +apartments + +// app : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/app.html +app + +// apple : Apple Inc. +// https://www.iana.org/domains/root/db/apple.html +apple + +// aquarelle : Aquarelle.com +// https://www.iana.org/domains/root/db/aquarelle.html +aquarelle + +// arab : League of Arab States +// https://www.iana.org/domains/root/db/arab.html +arab + +// aramco : Aramco Services Company +// https://www.iana.org/domains/root/db/aramco.html +aramco + +// archi : Identity Digital Limited +// https://www.iana.org/domains/root/db/archi.html +archi + +// army : Dog Beach, LLC +// https://www.iana.org/domains/root/db/army.html +army + +// art : UK Creative Ideas Limited +// https://www.iana.org/domains/root/db/art.html +art + +// arte : Association Relative à la Télévision Européenne G.E.I.E. +// https://www.iana.org/domains/root/db/arte.html +arte + +// asda : Wal-Mart Stores, Inc. +// https://www.iana.org/domains/root/db/asda.html +asda + +// associates : Binky Moon, LLC +// https://www.iana.org/domains/root/db/associates.html +associates + +// athleta : The Gap, Inc. +// https://www.iana.org/domains/root/db/athleta.html +athleta + +// attorney : Dog Beach, LLC +// https://www.iana.org/domains/root/db/attorney.html +attorney + +// auction : Dog Beach, LLC +// https://www.iana.org/domains/root/db/auction.html +auction + +// audi : AUDI Aktiengesellschaft +// https://www.iana.org/domains/root/db/audi.html +audi + +// audible : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/audible.html +audible + +// audio : XYZ.COM LLC +// https://www.iana.org/domains/root/db/audio.html +audio + +// auspost : Australian Postal Corporation +// https://www.iana.org/domains/root/db/auspost.html +auspost + +// author : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/author.html +author + +// auto : XYZ.COM LLC +// https://www.iana.org/domains/root/db/auto.html +auto + +// autos : XYZ.COM LLC +// https://www.iana.org/domains/root/db/autos.html +autos + +// avianca : Avianca Inc. +// https://www.iana.org/domains/root/db/avianca.html +avianca + +// aws : AWS Registry LLC +// https://www.iana.org/domains/root/db/aws.html +aws + +// axa : AXA Group Operations SAS +// https://www.iana.org/domains/root/db/axa.html +axa + +// azure : Microsoft Corporation +// https://www.iana.org/domains/root/db/azure.html +azure + +// baby : XYZ.COM LLC +// https://www.iana.org/domains/root/db/baby.html +baby + +// baidu : Baidu, Inc. +// https://www.iana.org/domains/root/db/baidu.html +baidu + +// banamex : Citigroup Inc. +// https://www.iana.org/domains/root/db/banamex.html +banamex + +// band : Dog Beach, LLC +// https://www.iana.org/domains/root/db/band.html +band + +// bank : fTLD Registry Services LLC +// https://www.iana.org/domains/root/db/bank.html +bank + +// bar : Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable +// https://www.iana.org/domains/root/db/bar.html +bar + +// barcelona : Municipi de Barcelona +// https://www.iana.org/domains/root/db/barcelona.html +barcelona + +// barclaycard : Barclays Bank PLC +// https://www.iana.org/domains/root/db/barclaycard.html +barclaycard + +// barclays : Barclays Bank PLC +// https://www.iana.org/domains/root/db/barclays.html +barclays + +// barefoot : Gallo Vineyards, Inc. +// https://www.iana.org/domains/root/db/barefoot.html +barefoot + +// bargains : Binky Moon, LLC +// https://www.iana.org/domains/root/db/bargains.html +bargains + +// baseball : MLB Advanced Media DH, LLC +// https://www.iana.org/domains/root/db/baseball.html +baseball + +// basketball : Fédération Internationale de Basketball (FIBA) +// https://www.iana.org/domains/root/db/basketball.html +basketball + +// bauhaus : Werkhaus GmbH +// https://www.iana.org/domains/root/db/bauhaus.html +bauhaus + +// bayern : Bayern Connect GmbH +// https://www.iana.org/domains/root/db/bayern.html +bayern + +// bbc : British Broadcasting Corporation +// https://www.iana.org/domains/root/db/bbc.html +bbc + +// bbt : BB&T Corporation +// https://www.iana.org/domains/root/db/bbt.html +bbt + +// bbva : BANCO BILBAO VIZCAYA ARGENTARIA, S.A. +// https://www.iana.org/domains/root/db/bbva.html +bbva + +// bcg : The Boston Consulting Group, Inc. +// https://www.iana.org/domains/root/db/bcg.html +bcg + +// bcn : Municipi de Barcelona +// https://www.iana.org/domains/root/db/bcn.html +bcn + +// beats : Beats Electronics, LLC +// https://www.iana.org/domains/root/db/beats.html +beats + +// beauty : XYZ.COM LLC +// https://www.iana.org/domains/root/db/beauty.html +beauty + +// beer : Registry Services, LLC +// https://www.iana.org/domains/root/db/beer.html +beer + +// bentley : Bentley Motors Limited +// https://www.iana.org/domains/root/db/bentley.html +bentley + +// berlin : dotBERLIN GmbH & Co. KG +// https://www.iana.org/domains/root/db/berlin.html +berlin + +// best : BestTLD Pty Ltd +// https://www.iana.org/domains/root/db/best.html +best + +// bestbuy : BBY Solutions, Inc. +// https://www.iana.org/domains/root/db/bestbuy.html +bestbuy + +// bet : Identity Digital Limited +// https://www.iana.org/domains/root/db/bet.html +bet + +// bharti : Bharti Enterprises (Holding) Private Limited +// https://www.iana.org/domains/root/db/bharti.html +bharti + +// bible : American Bible Society +// https://www.iana.org/domains/root/db/bible.html +bible + +// bid : dot Bid Limited +// https://www.iana.org/domains/root/db/bid.html +bid + +// bike : Binky Moon, LLC +// https://www.iana.org/domains/root/db/bike.html +bike + +// bing : Microsoft Corporation +// https://www.iana.org/domains/root/db/bing.html +bing + +// bingo : Binky Moon, LLC +// https://www.iana.org/domains/root/db/bingo.html +bingo + +// bio : Identity Digital Limited +// https://www.iana.org/domains/root/db/bio.html +bio + +// black : Identity Digital Limited +// https://www.iana.org/domains/root/db/black.html +black + +// blackfriday : Registry Services, LLC +// https://www.iana.org/domains/root/db/blackfriday.html +blackfriday + +// blockbuster : Dish DBS Corporation +// https://www.iana.org/domains/root/db/blockbuster.html +blockbuster + +// blog : Knock Knock WHOIS There, LLC +// https://www.iana.org/domains/root/db/blog.html +blog + +// bloomberg : Bloomberg IP Holdings LLC +// https://www.iana.org/domains/root/db/bloomberg.html +bloomberg + +// blue : Identity Digital Limited +// https://www.iana.org/domains/root/db/blue.html +blue + +// bms : Bristol-Myers Squibb Company +// https://www.iana.org/domains/root/db/bms.html +bms + +// bmw : Bayerische Motoren Werke Aktiengesellschaft +// https://www.iana.org/domains/root/db/bmw.html +bmw + +// bnpparibas : BNP Paribas +// https://www.iana.org/domains/root/db/bnpparibas.html +bnpparibas + +// boats : XYZ.COM LLC +// https://www.iana.org/domains/root/db/boats.html +boats + +// boehringer : Boehringer Ingelheim International GmbH +// https://www.iana.org/domains/root/db/boehringer.html +boehringer + +// bofa : Bank of America Corporation +// https://www.iana.org/domains/root/db/bofa.html +bofa + +// bom : Núcleo de Informação e Coordenação do Ponto BR - NIC.br +// https://www.iana.org/domains/root/db/bom.html +bom + +// bond : ShortDot SA +// https://www.iana.org/domains/root/db/bond.html +bond + +// boo : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/boo.html +boo + +// book : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/book.html +book + +// booking : Booking.com B.V. +// https://www.iana.org/domains/root/db/booking.html +booking + +// bosch : Robert Bosch GMBH +// https://www.iana.org/domains/root/db/bosch.html +bosch + +// bostik : Bostik SA +// https://www.iana.org/domains/root/db/bostik.html +bostik + +// boston : Registry Services, LLC +// https://www.iana.org/domains/root/db/boston.html +boston + +// bot : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/bot.html +bot + +// boutique : Binky Moon, LLC +// https://www.iana.org/domains/root/db/boutique.html +boutique + +// box : Intercap Registry Inc. +// https://www.iana.org/domains/root/db/box.html +box + +// bradesco : Banco Bradesco S.A. +// https://www.iana.org/domains/root/db/bradesco.html +bradesco + +// bridgestone : Bridgestone Corporation +// https://www.iana.org/domains/root/db/bridgestone.html +bridgestone + +// broadway : Celebrate Broadway, Inc. +// https://www.iana.org/domains/root/db/broadway.html +broadway + +// broker : Dog Beach, LLC +// https://www.iana.org/domains/root/db/broker.html +broker + +// brother : Brother Industries, Ltd. +// https://www.iana.org/domains/root/db/brother.html +brother + +// brussels : DNS.be vzw +// https://www.iana.org/domains/root/db/brussels.html +brussels + +// build : Plan Bee LLC +// https://www.iana.org/domains/root/db/build.html +build + +// builders : Binky Moon, LLC +// https://www.iana.org/domains/root/db/builders.html +builders + +// business : Binky Moon, LLC +// https://www.iana.org/domains/root/db/business.html +business + +// buy : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/buy.html +buy + +// buzz : DOTSTRATEGY CO. +// https://www.iana.org/domains/root/db/buzz.html +buzz + +// bzh : Association www.bzh +// https://www.iana.org/domains/root/db/bzh.html +bzh + +// cab : Binky Moon, LLC +// https://www.iana.org/domains/root/db/cab.html +cab + +// cafe : Binky Moon, LLC +// https://www.iana.org/domains/root/db/cafe.html +cafe + +// cal : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/cal.html +cal + +// call : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/call.html +call + +// calvinklein : PVH gTLD Holdings LLC +// https://www.iana.org/domains/root/db/calvinklein.html +calvinklein + +// cam : Cam Connecting SARL +// https://www.iana.org/domains/root/db/cam.html +cam + +// camera : Binky Moon, LLC +// https://www.iana.org/domains/root/db/camera.html +camera + +// camp : Binky Moon, LLC +// https://www.iana.org/domains/root/db/camp.html +camp + +// canon : Canon Inc. +// https://www.iana.org/domains/root/db/canon.html +canon + +// capetown : ZA Central Registry NPC trading as ZA Central Registry +// https://www.iana.org/domains/root/db/capetown.html +capetown + +// capital : Binky Moon, LLC +// https://www.iana.org/domains/root/db/capital.html +capital + +// capitalone : Capital One Financial Corporation +// https://www.iana.org/domains/root/db/capitalone.html +capitalone + +// car : XYZ.COM LLC +// https://www.iana.org/domains/root/db/car.html +car + +// caravan : Caravan International, Inc. +// https://www.iana.org/domains/root/db/caravan.html +caravan + +// cards : Binky Moon, LLC +// https://www.iana.org/domains/root/db/cards.html +cards + +// care : Binky Moon, LLC +// https://www.iana.org/domains/root/db/care.html +care + +// career : dotCareer LLC +// https://www.iana.org/domains/root/db/career.html +career + +// careers : Binky Moon, LLC +// https://www.iana.org/domains/root/db/careers.html +careers + +// cars : XYZ.COM LLC +// https://www.iana.org/domains/root/db/cars.html +cars + +// casa : Registry Services, LLC +// https://www.iana.org/domains/root/db/casa.html +casa + +// case : Digity, LLC +// https://www.iana.org/domains/root/db/case.html +case + +// cash : Binky Moon, LLC +// https://www.iana.org/domains/root/db/cash.html +cash + +// casino : Binky Moon, LLC +// https://www.iana.org/domains/root/db/casino.html +casino + +// catering : Binky Moon, LLC +// https://www.iana.org/domains/root/db/catering.html +catering + +// catholic : Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication) +// https://www.iana.org/domains/root/db/catholic.html +catholic + +// cba : COMMONWEALTH BANK OF AUSTRALIA +// https://www.iana.org/domains/root/db/cba.html +cba + +// cbn : The Christian Broadcasting Network, Inc. +// https://www.iana.org/domains/root/db/cbn.html +cbn + +// cbre : CBRE, Inc. +// https://www.iana.org/domains/root/db/cbre.html +cbre + +// center : Binky Moon, LLC +// https://www.iana.org/domains/root/db/center.html +center + +// ceo : XYZ.COM LLC +// https://www.iana.org/domains/root/db/ceo.html +ceo + +// cern : European Organization for Nuclear Research ("CERN") +// https://www.iana.org/domains/root/db/cern.html +cern + +// cfa : CFA Institute +// https://www.iana.org/domains/root/db/cfa.html +cfa + +// cfd : ShortDot SA +// https://www.iana.org/domains/root/db/cfd.html +cfd + +// chanel : Chanel International B.V. +// https://www.iana.org/domains/root/db/chanel.html +chanel + +// channel : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/channel.html +channel + +// charity : Public Interest Registry +// https://www.iana.org/domains/root/db/charity.html +charity + +// chase : JPMorgan Chase Bank, National Association +// https://www.iana.org/domains/root/db/chase.html +chase + +// chat : Binky Moon, LLC +// https://www.iana.org/domains/root/db/chat.html +chat + +// cheap : Binky Moon, LLC +// https://www.iana.org/domains/root/db/cheap.html +cheap + +// chintai : CHINTAI Corporation +// https://www.iana.org/domains/root/db/chintai.html +chintai + +// christmas : XYZ.COM LLC +// https://www.iana.org/domains/root/db/christmas.html +christmas + +// chrome : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/chrome.html +chrome + +// church : Binky Moon, LLC +// https://www.iana.org/domains/root/db/church.html +church + +// cipriani : Hotel Cipriani Srl +// https://www.iana.org/domains/root/db/cipriani.html +cipriani + +// circle : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/circle.html +circle + +// cisco : Cisco Technology, Inc. +// https://www.iana.org/domains/root/db/cisco.html +cisco + +// citadel : Citadel Domain LLC +// https://www.iana.org/domains/root/db/citadel.html +citadel + +// citi : Citigroup Inc. +// https://www.iana.org/domains/root/db/citi.html +citi + +// citic : CITIC Group Corporation +// https://www.iana.org/domains/root/db/citic.html +citic + +// city : Binky Moon, LLC +// https://www.iana.org/domains/root/db/city.html +city + +// claims : Binky Moon, LLC +// https://www.iana.org/domains/root/db/claims.html +claims + +// cleaning : Binky Moon, LLC +// https://www.iana.org/domains/root/db/cleaning.html +cleaning + +// click : Internet Naming Company LLC +// https://www.iana.org/domains/root/db/click.html +click + +// clinic : Binky Moon, LLC +// https://www.iana.org/domains/root/db/clinic.html +clinic + +// clinique : The Estée Lauder Companies Inc. +// https://www.iana.org/domains/root/db/clinique.html +clinique + +// clothing : Binky Moon, LLC +// https://www.iana.org/domains/root/db/clothing.html +clothing + +// cloud : Aruba PEC S.p.A. +// https://www.iana.org/domains/root/db/cloud.html +cloud + +// club : Registry Services, LLC +// https://www.iana.org/domains/root/db/club.html +club + +// clubmed : Club Méditerranée S.A. +// https://www.iana.org/domains/root/db/clubmed.html +clubmed + +// coach : Binky Moon, LLC +// https://www.iana.org/domains/root/db/coach.html +coach + +// codes : Binky Moon, LLC +// https://www.iana.org/domains/root/db/codes.html +codes + +// coffee : Binky Moon, LLC +// https://www.iana.org/domains/root/db/coffee.html +coffee + +// college : XYZ.COM LLC +// https://www.iana.org/domains/root/db/college.html +college + +// cologne : dotKoeln GmbH +// https://www.iana.org/domains/root/db/cologne.html +cologne + +// commbank : COMMONWEALTH BANK OF AUSTRALIA +// https://www.iana.org/domains/root/db/commbank.html +commbank + +// community : Binky Moon, LLC +// https://www.iana.org/domains/root/db/community.html +community + +// company : Binky Moon, LLC +// https://www.iana.org/domains/root/db/company.html +company + +// compare : Registry Services, LLC +// https://www.iana.org/domains/root/db/compare.html +compare + +// computer : Binky Moon, LLC +// https://www.iana.org/domains/root/db/computer.html +computer + +// comsec : VeriSign, Inc. +// https://www.iana.org/domains/root/db/comsec.html +comsec + +// condos : Binky Moon, LLC +// https://www.iana.org/domains/root/db/condos.html +condos + +// construction : Binky Moon, LLC +// https://www.iana.org/domains/root/db/construction.html +construction + +// consulting : Dog Beach, LLC +// https://www.iana.org/domains/root/db/consulting.html +consulting + +// contact : Dog Beach, LLC +// https://www.iana.org/domains/root/db/contact.html +contact + +// contractors : Binky Moon, LLC +// https://www.iana.org/domains/root/db/contractors.html +contractors + +// cooking : Registry Services, LLC +// https://www.iana.org/domains/root/db/cooking.html +cooking + +// cool : Binky Moon, LLC +// https://www.iana.org/domains/root/db/cool.html +cool + +// corsica : Collectivité de Corse +// https://www.iana.org/domains/root/db/corsica.html +corsica + +// country : Internet Naming Company LLC +// https://www.iana.org/domains/root/db/country.html +country + +// coupon : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/coupon.html +coupon + +// coupons : Binky Moon, LLC +// https://www.iana.org/domains/root/db/coupons.html +coupons + +// courses : Registry Services, LLC +// https://www.iana.org/domains/root/db/courses.html +courses + +// cpa : American Institute of Certified Public Accountants +// https://www.iana.org/domains/root/db/cpa.html +cpa + +// credit : Binky Moon, LLC +// https://www.iana.org/domains/root/db/credit.html +credit + +// creditcard : Binky Moon, LLC +// https://www.iana.org/domains/root/db/creditcard.html +creditcard + +// creditunion : DotCooperation LLC +// https://www.iana.org/domains/root/db/creditunion.html +creditunion + +// cricket : dot Cricket Limited +// https://www.iana.org/domains/root/db/cricket.html +cricket + +// crown : Crown Equipment Corporation +// https://www.iana.org/domains/root/db/crown.html +crown + +// crs : Federated Co-operatives Limited +// https://www.iana.org/domains/root/db/crs.html +crs + +// cruise : Viking River Cruises (Bermuda) Ltd. +// https://www.iana.org/domains/root/db/cruise.html +cruise + +// cruises : Binky Moon, LLC +// https://www.iana.org/domains/root/db/cruises.html +cruises + +// cuisinella : SCHMIDT GROUPE S.A.S. +// https://www.iana.org/domains/root/db/cuisinella.html +cuisinella + +// cymru : Nominet UK +// https://www.iana.org/domains/root/db/cymru.html +cymru + +// cyou : ShortDot SA +// https://www.iana.org/domains/root/db/cyou.html +cyou + +// dabur : Dabur India Limited +// https://www.iana.org/domains/root/db/dabur.html +dabur + +// dad : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/dad.html +dad + +// dance : Dog Beach, LLC +// https://www.iana.org/domains/root/db/dance.html +dance + +// data : Dish DBS Corporation +// https://www.iana.org/domains/root/db/data.html +data + +// date : dot Date Limited +// https://www.iana.org/domains/root/db/date.html +date + +// dating : Binky Moon, LLC +// https://www.iana.org/domains/root/db/dating.html +dating + +// datsun : NISSAN MOTOR CO., LTD. +// https://www.iana.org/domains/root/db/datsun.html +datsun + +// day : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/day.html +day + +// dclk : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/dclk.html +dclk + +// dds : Registry Services, LLC +// https://www.iana.org/domains/root/db/dds.html +dds + +// deal : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/deal.html +deal + +// dealer : Intercap Registry Inc. +// https://www.iana.org/domains/root/db/dealer.html +dealer + +// deals : Binky Moon, LLC +// https://www.iana.org/domains/root/db/deals.html +deals + +// degree : Dog Beach, LLC +// https://www.iana.org/domains/root/db/degree.html +degree + +// delivery : Binky Moon, LLC +// https://www.iana.org/domains/root/db/delivery.html +delivery + +// dell : Dell Inc. +// https://www.iana.org/domains/root/db/dell.html +dell + +// deloitte : Deloitte Touche Tohmatsu +// https://www.iana.org/domains/root/db/deloitte.html +deloitte + +// delta : Delta Air Lines, Inc. +// https://www.iana.org/domains/root/db/delta.html +delta + +// democrat : Dog Beach, LLC +// https://www.iana.org/domains/root/db/democrat.html +democrat + +// dental : Binky Moon, LLC +// https://www.iana.org/domains/root/db/dental.html +dental + +// dentist : Dog Beach, LLC +// https://www.iana.org/domains/root/db/dentist.html +dentist + +// desi +// https://www.iana.org/domains/root/db/desi.html +desi + +// design : Registry Services, LLC +// https://www.iana.org/domains/root/db/design.html +design + +// dev : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/dev.html +dev + +// dhl : Deutsche Post AG +// https://www.iana.org/domains/root/db/dhl.html +dhl + +// diamonds : Binky Moon, LLC +// https://www.iana.org/domains/root/db/diamonds.html +diamonds + +// diet : XYZ.COM LLC +// https://www.iana.org/domains/root/db/diet.html +diet + +// digital : Binky Moon, LLC +// https://www.iana.org/domains/root/db/digital.html +digital + +// direct : Binky Moon, LLC +// https://www.iana.org/domains/root/db/direct.html +direct + +// directory : Binky Moon, LLC +// https://www.iana.org/domains/root/db/directory.html +directory + +// discount : Binky Moon, LLC +// https://www.iana.org/domains/root/db/discount.html +discount + +// discover : Discover Financial Services +// https://www.iana.org/domains/root/db/discover.html +discover + +// dish : Dish DBS Corporation +// https://www.iana.org/domains/root/db/dish.html +dish + +// diy : Internet Naming Company LLC +// https://www.iana.org/domains/root/db/diy.html +diy + +// dnp : Dai Nippon Printing Co., Ltd. +// https://www.iana.org/domains/root/db/dnp.html +dnp + +// docs : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/docs.html +docs + +// doctor : Binky Moon, LLC +// https://www.iana.org/domains/root/db/doctor.html +doctor + +// dog : Binky Moon, LLC +// https://www.iana.org/domains/root/db/dog.html +dog + +// domains : Binky Moon, LLC +// https://www.iana.org/domains/root/db/domains.html +domains + +// dot : Dish DBS Corporation +// https://www.iana.org/domains/root/db/dot.html +dot + +// download : dot Support Limited +// https://www.iana.org/domains/root/db/download.html +download + +// drive : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/drive.html +drive + +// dtv : Dish DBS Corporation +// https://www.iana.org/domains/root/db/dtv.html +dtv + +// dubai : Dubai Smart Government Department +// https://www.iana.org/domains/root/db/dubai.html +dubai + +// dunlop : The Goodyear Tire & Rubber Company +// https://www.iana.org/domains/root/db/dunlop.html +dunlop + +// dupont : DuPont Specialty Products USA, LLC +// https://www.iana.org/domains/root/db/dupont.html +dupont + +// durban : ZA Central Registry NPC trading as ZA Central Registry +// https://www.iana.org/domains/root/db/durban.html +durban + +// dvag : Deutsche Vermögensberatung Aktiengesellschaft DVAG +// https://www.iana.org/domains/root/db/dvag.html +dvag + +// dvr : DISH Technologies L.L.C. +// https://www.iana.org/domains/root/db/dvr.html +dvr + +// earth : Interlink Systems Innovation Institute K.K. +// https://www.iana.org/domains/root/db/earth.html +earth + +// eat : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/eat.html +eat + +// eco : Big Room Inc. +// https://www.iana.org/domains/root/db/eco.html +eco + +// edeka : EDEKA Verband kaufmännischer Genossenschaften e.V. +// https://www.iana.org/domains/root/db/edeka.html +edeka + +// education : Binky Moon, LLC +// https://www.iana.org/domains/root/db/education.html +education + +// email : Binky Moon, LLC +// https://www.iana.org/domains/root/db/email.html +email + +// emerck : Merck KGaA +// https://www.iana.org/domains/root/db/emerck.html +emerck + +// energy : Binky Moon, LLC +// https://www.iana.org/domains/root/db/energy.html +energy + +// engineer : Dog Beach, LLC +// https://www.iana.org/domains/root/db/engineer.html +engineer + +// engineering : Binky Moon, LLC +// https://www.iana.org/domains/root/db/engineering.html +engineering + +// enterprises : Binky Moon, LLC +// https://www.iana.org/domains/root/db/enterprises.html +enterprises + +// epson : Seiko Epson Corporation +// https://www.iana.org/domains/root/db/epson.html +epson + +// equipment : Binky Moon, LLC +// https://www.iana.org/domains/root/db/equipment.html +equipment + +// ericsson : Telefonaktiebolaget L M Ericsson +// https://www.iana.org/domains/root/db/ericsson.html +ericsson + +// erni : ERNI Group Holding AG +// https://www.iana.org/domains/root/db/erni.html +erni + +// esq : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/esq.html +esq + +// estate : Binky Moon, LLC +// https://www.iana.org/domains/root/db/estate.html +estate + +// eurovision : European Broadcasting Union (EBU) +// https://www.iana.org/domains/root/db/eurovision.html +eurovision + +// eus : Puntueus Fundazioa +// https://www.iana.org/domains/root/db/eus.html +eus + +// events : Binky Moon, LLC +// https://www.iana.org/domains/root/db/events.html +events + +// exchange : Binky Moon, LLC +// https://www.iana.org/domains/root/db/exchange.html +exchange + +// expert : Binky Moon, LLC +// https://www.iana.org/domains/root/db/expert.html +expert + +// exposed : Binky Moon, LLC +// https://www.iana.org/domains/root/db/exposed.html +exposed + +// express : Binky Moon, LLC +// https://www.iana.org/domains/root/db/express.html +express + +// extraspace : Extra Space Storage LLC +// https://www.iana.org/domains/root/db/extraspace.html +extraspace + +// fage : Fage International S.A. +// https://www.iana.org/domains/root/db/fage.html +fage + +// fail : Binky Moon, LLC +// https://www.iana.org/domains/root/db/fail.html +fail + +// fairwinds : FairWinds Partners, LLC +// https://www.iana.org/domains/root/db/fairwinds.html +fairwinds + +// faith : dot Faith Limited +// https://www.iana.org/domains/root/db/faith.html +faith + +// family : Dog Beach, LLC +// https://www.iana.org/domains/root/db/family.html +family + +// fan : Dog Beach, LLC +// https://www.iana.org/domains/root/db/fan.html +fan + +// fans : ZDNS International Limited +// https://www.iana.org/domains/root/db/fans.html +fans + +// farm : Binky Moon, LLC +// https://www.iana.org/domains/root/db/farm.html +farm + +// farmers : Farmers Insurance Exchange +// https://www.iana.org/domains/root/db/farmers.html +farmers + +// fashion : Registry Services, LLC +// https://www.iana.org/domains/root/db/fashion.html +fashion + +// fast : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/fast.html +fast + +// fedex : Federal Express Corporation +// https://www.iana.org/domains/root/db/fedex.html +fedex + +// feedback : Top Level Spectrum, Inc. +// https://www.iana.org/domains/root/db/feedback.html +feedback + +// ferrari : Fiat Chrysler Automobiles N.V. +// https://www.iana.org/domains/root/db/ferrari.html +ferrari + +// ferrero : Ferrero Trading Lux S.A. +// https://www.iana.org/domains/root/db/ferrero.html +ferrero + +// fidelity : Fidelity Brokerage Services LLC +// https://www.iana.org/domains/root/db/fidelity.html +fidelity + +// fido : Rogers Communications Canada Inc. +// https://www.iana.org/domains/root/db/fido.html +fido + +// film : Motion Picture Domain Registry Pty Ltd +// https://www.iana.org/domains/root/db/film.html +film + +// final : Núcleo de Informação e Coordenação do Ponto BR - NIC.br +// https://www.iana.org/domains/root/db/final.html +final + +// finance : Binky Moon, LLC +// https://www.iana.org/domains/root/db/finance.html +finance + +// financial : Binky Moon, LLC +// https://www.iana.org/domains/root/db/financial.html +financial + +// fire : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/fire.html +fire + +// firestone : Bridgestone Licensing Services, Inc +// https://www.iana.org/domains/root/db/firestone.html +firestone + +// firmdale : Firmdale Holdings Limited +// https://www.iana.org/domains/root/db/firmdale.html +firmdale + +// fish : Binky Moon, LLC +// https://www.iana.org/domains/root/db/fish.html +fish + +// fishing : Registry Services, LLC +// https://www.iana.org/domains/root/db/fishing.html +fishing + +// fit : Registry Services, LLC +// https://www.iana.org/domains/root/db/fit.html +fit + +// fitness : Binky Moon, LLC +// https://www.iana.org/domains/root/db/fitness.html +fitness + +// flickr : Flickr, Inc. +// https://www.iana.org/domains/root/db/flickr.html +flickr + +// flights : Binky Moon, LLC +// https://www.iana.org/domains/root/db/flights.html +flights + +// flir : FLIR Systems, Inc. +// https://www.iana.org/domains/root/db/flir.html +flir + +// florist : Binky Moon, LLC +// https://www.iana.org/domains/root/db/florist.html +florist + +// flowers : XYZ.COM LLC +// https://www.iana.org/domains/root/db/flowers.html +flowers + +// fly : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/fly.html +fly + +// foo : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/foo.html +foo + +// food : Internet Naming Company LLC +// https://www.iana.org/domains/root/db/food.html +food + +// football : Binky Moon, LLC +// https://www.iana.org/domains/root/db/football.html +football + +// ford : Ford Motor Company +// https://www.iana.org/domains/root/db/ford.html +ford + +// forex : Dog Beach, LLC +// https://www.iana.org/domains/root/db/forex.html +forex + +// forsale : Dog Beach, LLC +// https://www.iana.org/domains/root/db/forsale.html +forsale + +// forum : Fegistry, LLC +// https://www.iana.org/domains/root/db/forum.html +forum + +// foundation : Public Interest Registry +// https://www.iana.org/domains/root/db/foundation.html +foundation + +// fox : FOX Registry, LLC +// https://www.iana.org/domains/root/db/fox.html +fox + +// free : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/free.html +free + +// fresenius : Fresenius Immobilien-Verwaltungs-GmbH +// https://www.iana.org/domains/root/db/fresenius.html +fresenius + +// frl : FRLregistry B.V. +// https://www.iana.org/domains/root/db/frl.html +frl + +// frogans : OP3FT +// https://www.iana.org/domains/root/db/frogans.html +frogans + +// frontier : Frontier Communications Corporation +// https://www.iana.org/domains/root/db/frontier.html +frontier + +// ftr : Frontier Communications Corporation +// https://www.iana.org/domains/root/db/ftr.html +ftr + +// fujitsu : Fujitsu Limited +// https://www.iana.org/domains/root/db/fujitsu.html +fujitsu + +// fun : Radix Technologies Inc. +// https://www.iana.org/domains/root/db/fun.html +fun + +// fund : Binky Moon, LLC +// https://www.iana.org/domains/root/db/fund.html +fund + +// furniture : Binky Moon, LLC +// https://www.iana.org/domains/root/db/furniture.html +furniture + +// futbol : Dog Beach, LLC +// https://www.iana.org/domains/root/db/futbol.html +futbol + +// fyi : Binky Moon, LLC +// https://www.iana.org/domains/root/db/fyi.html +fyi + +// gal : Asociación puntoGAL +// https://www.iana.org/domains/root/db/gal.html +gal + +// gallery : Binky Moon, LLC +// https://www.iana.org/domains/root/db/gallery.html +gallery + +// gallo : Gallo Vineyards, Inc. +// https://www.iana.org/domains/root/db/gallo.html +gallo + +// gallup : Gallup, Inc. +// https://www.iana.org/domains/root/db/gallup.html +gallup + +// game : XYZ.COM LLC +// https://www.iana.org/domains/root/db/game.html +game + +// games : Dog Beach, LLC +// https://www.iana.org/domains/root/db/games.html +games + +// gap : The Gap, Inc. +// https://www.iana.org/domains/root/db/gap.html +gap + +// garden : Registry Services, LLC +// https://www.iana.org/domains/root/db/garden.html +garden + +// gay : Registry Services, LLC +// https://www.iana.org/domains/root/db/gay.html +gay + +// gbiz : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/gbiz.html +gbiz + +// gdn : Joint Stock Company "Navigation-information systems" +// https://www.iana.org/domains/root/db/gdn.html +gdn + +// gea : GEA Group Aktiengesellschaft +// https://www.iana.org/domains/root/db/gea.html +gea + +// gent : Easyhost BV +// https://www.iana.org/domains/root/db/gent.html +gent + +// genting : Resorts World Inc Pte. Ltd. +// https://www.iana.org/domains/root/db/genting.html +genting + +// george : Wal-Mart Stores, Inc. +// https://www.iana.org/domains/root/db/george.html +george + +// ggee : GMO Internet, Inc. +// https://www.iana.org/domains/root/db/ggee.html +ggee + +// gift : DotGift, LLC +// https://www.iana.org/domains/root/db/gift.html +gift + +// gifts : Binky Moon, LLC +// https://www.iana.org/domains/root/db/gifts.html +gifts + +// gives : Public Interest Registry +// https://www.iana.org/domains/root/db/gives.html +gives + +// giving : Public Interest Registry +// https://www.iana.org/domains/root/db/giving.html +giving + +// glass : Binky Moon, LLC +// https://www.iana.org/domains/root/db/glass.html +glass + +// gle : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/gle.html +gle + +// global : Identity Digital Limited +// https://www.iana.org/domains/root/db/global.html +global + +// globo : Globo Comunicação e Participações S.A +// https://www.iana.org/domains/root/db/globo.html +globo + +// gmail : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/gmail.html +gmail + +// gmbh : Binky Moon, LLC +// https://www.iana.org/domains/root/db/gmbh.html +gmbh + +// gmo : GMO Internet, Inc. +// https://www.iana.org/domains/root/db/gmo.html +gmo + +// gmx : 1&1 Mail & Media GmbH +// https://www.iana.org/domains/root/db/gmx.html +gmx + +// godaddy : Go Daddy East, LLC +// https://www.iana.org/domains/root/db/godaddy.html +godaddy + +// gold : Binky Moon, LLC +// https://www.iana.org/domains/root/db/gold.html +gold + +// goldpoint : YODOBASHI CAMERA CO.,LTD. +// https://www.iana.org/domains/root/db/goldpoint.html +goldpoint + +// golf : Binky Moon, LLC +// https://www.iana.org/domains/root/db/golf.html +golf + +// goo : NTT DOCOMO, INC. +// https://www.iana.org/domains/root/db/goo.html +goo + +// goodyear : The Goodyear Tire & Rubber Company +// https://www.iana.org/domains/root/db/goodyear.html +goodyear + +// goog : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/goog.html +goog + +// google : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/google.html +google + +// gop : Republican State Leadership Committee, Inc. +// https://www.iana.org/domains/root/db/gop.html +gop + +// got : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/got.html +got + +// grainger : Grainger Registry Services, LLC +// https://www.iana.org/domains/root/db/grainger.html +grainger + +// graphics : Binky Moon, LLC +// https://www.iana.org/domains/root/db/graphics.html +graphics + +// gratis : Binky Moon, LLC +// https://www.iana.org/domains/root/db/gratis.html +gratis + +// green : Identity Digital Limited +// https://www.iana.org/domains/root/db/green.html +green + +// gripe : Binky Moon, LLC +// https://www.iana.org/domains/root/db/gripe.html +gripe + +// grocery : Wal-Mart Stores, Inc. +// https://www.iana.org/domains/root/db/grocery.html +grocery + +// group : Binky Moon, LLC +// https://www.iana.org/domains/root/db/group.html +group + +// gucci : Guccio Gucci S.p.a. +// https://www.iana.org/domains/root/db/gucci.html +gucci + +// guge : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/guge.html +guge + +// guide : Binky Moon, LLC +// https://www.iana.org/domains/root/db/guide.html +guide + +// guitars : XYZ.COM LLC +// https://www.iana.org/domains/root/db/guitars.html +guitars + +// guru : Binky Moon, LLC +// https://www.iana.org/domains/root/db/guru.html +guru + +// hair : XYZ.COM LLC +// https://www.iana.org/domains/root/db/hair.html +hair + +// hamburg : Hamburg Top-Level-Domain GmbH +// https://www.iana.org/domains/root/db/hamburg.html +hamburg + +// hangout : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/hangout.html +hangout + +// haus : Dog Beach, LLC +// https://www.iana.org/domains/root/db/haus.html +haus + +// hbo : HBO Registry Services, Inc. +// https://www.iana.org/domains/root/db/hbo.html +hbo + +// hdfc : HOUSING DEVELOPMENT FINANCE CORPORATION LIMITED +// https://www.iana.org/domains/root/db/hdfc.html +hdfc + +// hdfcbank : HDFC Bank Limited +// https://www.iana.org/domains/root/db/hdfcbank.html +hdfcbank + +// health : Registry Services, LLC +// https://www.iana.org/domains/root/db/health.html +health + +// healthcare : Binky Moon, LLC +// https://www.iana.org/domains/root/db/healthcare.html +healthcare + +// help : Innovation service Limited +// https://www.iana.org/domains/root/db/help.html +help + +// helsinki : City of Helsinki +// https://www.iana.org/domains/root/db/helsinki.html +helsinki + +// here : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/here.html +here + +// hermes : HERMES INTERNATIONAL +// https://www.iana.org/domains/root/db/hermes.html +hermes + +// hiphop : Dot Hip Hop, LLC +// https://www.iana.org/domains/root/db/hiphop.html +hiphop + +// hisamitsu : Hisamitsu Pharmaceutical Co.,Inc. +// https://www.iana.org/domains/root/db/hisamitsu.html +hisamitsu + +// hitachi : Hitachi, Ltd. +// https://www.iana.org/domains/root/db/hitachi.html +hitachi + +// hiv : Internet Naming Company LLC +// https://www.iana.org/domains/root/db/hiv.html +hiv + +// hkt : PCCW-HKT DataCom Services Limited +// https://www.iana.org/domains/root/db/hkt.html +hkt + +// hockey : Binky Moon, LLC +// https://www.iana.org/domains/root/db/hockey.html +hockey + +// holdings : Binky Moon, LLC +// https://www.iana.org/domains/root/db/holdings.html +holdings + +// holiday : Binky Moon, LLC +// https://www.iana.org/domains/root/db/holiday.html +holiday + +// homedepot : Home Depot Product Authority, LLC +// https://www.iana.org/domains/root/db/homedepot.html +homedepot + +// homegoods : The TJX Companies, Inc. +// https://www.iana.org/domains/root/db/homegoods.html +homegoods + +// homes : XYZ.COM LLC +// https://www.iana.org/domains/root/db/homes.html +homes + +// homesense : The TJX Companies, Inc. +// https://www.iana.org/domains/root/db/homesense.html +homesense + +// honda : Honda Motor Co., Ltd. +// https://www.iana.org/domains/root/db/honda.html +honda + +// horse : Registry Services, LLC +// https://www.iana.org/domains/root/db/horse.html +horse + +// hospital : Binky Moon, LLC +// https://www.iana.org/domains/root/db/hospital.html +hospital + +// host : Radix Technologies Inc. +// https://www.iana.org/domains/root/db/host.html +host + +// hosting : XYZ.COM LLC +// https://www.iana.org/domains/root/db/hosting.html +hosting + +// hot : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/hot.html +hot + +// hotels : Booking.com B.V. +// https://www.iana.org/domains/root/db/hotels.html +hotels + +// hotmail : Microsoft Corporation +// https://www.iana.org/domains/root/db/hotmail.html +hotmail + +// house : Binky Moon, LLC +// https://www.iana.org/domains/root/db/house.html +house + +// how : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/how.html +how + +// hsbc : HSBC Global Services (UK) Limited +// https://www.iana.org/domains/root/db/hsbc.html +hsbc + +// hughes : Hughes Satellite Systems Corporation +// https://www.iana.org/domains/root/db/hughes.html +hughes + +// hyatt : Hyatt GTLD, L.L.C. +// https://www.iana.org/domains/root/db/hyatt.html +hyatt + +// hyundai : Hyundai Motor Company +// https://www.iana.org/domains/root/db/hyundai.html +hyundai + +// ibm : International Business Machines Corporation +// https://www.iana.org/domains/root/db/ibm.html +ibm + +// icbc : Industrial and Commercial Bank of China Limited +// https://www.iana.org/domains/root/db/icbc.html +icbc + +// ice : IntercontinentalExchange, Inc. +// https://www.iana.org/domains/root/db/ice.html +ice + +// icu : ShortDot SA +// https://www.iana.org/domains/root/db/icu.html +icu + +// ieee : IEEE Global LLC +// https://www.iana.org/domains/root/db/ieee.html +ieee + +// ifm : ifm electronic gmbh +// https://www.iana.org/domains/root/db/ifm.html +ifm + +// ikano : Ikano S.A. +// https://www.iana.org/domains/root/db/ikano.html +ikano + +// imamat : Fondation Aga Khan (Aga Khan Foundation) +// https://www.iana.org/domains/root/db/imamat.html +imamat + +// imdb : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/imdb.html +imdb + +// immo : Binky Moon, LLC +// https://www.iana.org/domains/root/db/immo.html +immo + +// immobilien : Dog Beach, LLC +// https://www.iana.org/domains/root/db/immobilien.html +immobilien + +// inc : Intercap Registry Inc. +// https://www.iana.org/domains/root/db/inc.html +inc + +// industries : Binky Moon, LLC +// https://www.iana.org/domains/root/db/industries.html +industries + +// infiniti : NISSAN MOTOR CO., LTD. +// https://www.iana.org/domains/root/db/infiniti.html +infiniti + +// ing : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/ing.html +ing + +// ink : Registry Services, LLC +// https://www.iana.org/domains/root/db/ink.html +ink + +// institute : Binky Moon, LLC +// https://www.iana.org/domains/root/db/institute.html +institute + +// insurance : fTLD Registry Services LLC +// https://www.iana.org/domains/root/db/insurance.html +insurance + +// insure : Binky Moon, LLC +// https://www.iana.org/domains/root/db/insure.html +insure + +// international : Binky Moon, LLC +// https://www.iana.org/domains/root/db/international.html +international + +// intuit : Intuit Administrative Services, Inc. +// https://www.iana.org/domains/root/db/intuit.html +intuit + +// investments : Binky Moon, LLC +// https://www.iana.org/domains/root/db/investments.html +investments + +// ipiranga : Ipiranga Produtos de Petroleo S.A. +// https://www.iana.org/domains/root/db/ipiranga.html +ipiranga + +// irish : Binky Moon, LLC +// https://www.iana.org/domains/root/db/irish.html +irish + +// ismaili : Fondation Aga Khan (Aga Khan Foundation) +// https://www.iana.org/domains/root/db/ismaili.html +ismaili + +// ist : Istanbul Metropolitan Municipality +// https://www.iana.org/domains/root/db/ist.html +ist + +// istanbul : Istanbul Metropolitan Municipality +// https://www.iana.org/domains/root/db/istanbul.html +istanbul + +// itau : Itau Unibanco Holding S.A. +// https://www.iana.org/domains/root/db/itau.html +itau + +// itv : ITV Services Limited +// https://www.iana.org/domains/root/db/itv.html +itv + +// jaguar : Jaguar Land Rover Ltd +// https://www.iana.org/domains/root/db/jaguar.html +jaguar + +// java : Oracle Corporation +// https://www.iana.org/domains/root/db/java.html +java + +// jcb : JCB Co., Ltd. +// https://www.iana.org/domains/root/db/jcb.html +jcb + +// jeep : FCA US LLC. +// https://www.iana.org/domains/root/db/jeep.html +jeep + +// jetzt : Binky Moon, LLC +// https://www.iana.org/domains/root/db/jetzt.html +jetzt + +// jewelry : Binky Moon, LLC +// https://www.iana.org/domains/root/db/jewelry.html +jewelry + +// jio : Reliance Industries Limited +// https://www.iana.org/domains/root/db/jio.html +jio + +// jll : Jones Lang LaSalle Incorporated +// https://www.iana.org/domains/root/db/jll.html +jll + +// jmp : Matrix IP LLC +// https://www.iana.org/domains/root/db/jmp.html +jmp + +// jnj : Johnson & Johnson Services, Inc. +// https://www.iana.org/domains/root/db/jnj.html +jnj + +// joburg : ZA Central Registry NPC trading as ZA Central Registry +// https://www.iana.org/domains/root/db/joburg.html +joburg + +// jot : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/jot.html +jot + +// joy : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/joy.html +joy + +// jpmorgan : JPMorgan Chase Bank, National Association +// https://www.iana.org/domains/root/db/jpmorgan.html +jpmorgan + +// jprs : Japan Registry Services Co., Ltd. +// https://www.iana.org/domains/root/db/jprs.html +jprs + +// juegos : Dog Beach, LLC +// https://www.iana.org/domains/root/db/juegos.html +juegos + +// juniper : JUNIPER NETWORKS, INC. +// https://www.iana.org/domains/root/db/juniper.html +juniper + +// kaufen : Dog Beach, LLC +// https://www.iana.org/domains/root/db/kaufen.html +kaufen + +// kddi : KDDI CORPORATION +// https://www.iana.org/domains/root/db/kddi.html +kddi + +// kerryhotels : Kerry Trading Co. Limited +// https://www.iana.org/domains/root/db/kerryhotels.html +kerryhotels + +// kerrylogistics : Kerry Trading Co. Limited +// https://www.iana.org/domains/root/db/kerrylogistics.html +kerrylogistics + +// kerryproperties : Kerry Trading Co. Limited +// https://www.iana.org/domains/root/db/kerryproperties.html +kerryproperties + +// kfh : Kuwait Finance House +// https://www.iana.org/domains/root/db/kfh.html +kfh + +// kia : KIA MOTORS CORPORATION +// https://www.iana.org/domains/root/db/kia.html +kia + +// kids : DotKids Foundation Limited +// https://www.iana.org/domains/root/db/kids.html +kids + +// kim : Identity Digital Limited +// https://www.iana.org/domains/root/db/kim.html +kim + +// kindle : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/kindle.html +kindle + +// kitchen : Binky Moon, LLC +// https://www.iana.org/domains/root/db/kitchen.html +kitchen + +// kiwi : DOT KIWI LIMITED +// https://www.iana.org/domains/root/db/kiwi.html +kiwi + +// koeln : dotKoeln GmbH +// https://www.iana.org/domains/root/db/koeln.html +koeln + +// komatsu : Komatsu Ltd. +// https://www.iana.org/domains/root/db/komatsu.html +komatsu + +// kosher : Kosher Marketing Assets LLC +// https://www.iana.org/domains/root/db/kosher.html +kosher + +// kpmg : KPMG International Cooperative (KPMG International Genossenschaft) +// https://www.iana.org/domains/root/db/kpmg.html +kpmg + +// kpn : Koninklijke KPN N.V. +// https://www.iana.org/domains/root/db/kpn.html +kpn + +// krd : KRG Department of Information Technology +// https://www.iana.org/domains/root/db/krd.html +krd + +// kred : KredTLD Pty Ltd +// https://www.iana.org/domains/root/db/kred.html +kred + +// kuokgroup : Kerry Trading Co. Limited +// https://www.iana.org/domains/root/db/kuokgroup.html +kuokgroup + +// kyoto : Academic Institution: Kyoto Jyoho Gakuen +// https://www.iana.org/domains/root/db/kyoto.html +kyoto + +// lacaixa : Fundación Bancaria Caixa d’Estalvis i Pensions de Barcelona, “la Caixa” +// https://www.iana.org/domains/root/db/lacaixa.html +lacaixa + +// lamborghini : Automobili Lamborghini S.p.A. +// https://www.iana.org/domains/root/db/lamborghini.html +lamborghini + +// lamer : The Estée Lauder Companies Inc. +// https://www.iana.org/domains/root/db/lamer.html +lamer + +// lancaster : LANCASTER +// https://www.iana.org/domains/root/db/lancaster.html +lancaster + +// land : Binky Moon, LLC +// https://www.iana.org/domains/root/db/land.html +land + +// landrover : Jaguar Land Rover Ltd +// https://www.iana.org/domains/root/db/landrover.html +landrover + +// lanxess : LANXESS Corporation +// https://www.iana.org/domains/root/db/lanxess.html +lanxess + +// lasalle : Jones Lang LaSalle Incorporated +// https://www.iana.org/domains/root/db/lasalle.html +lasalle + +// lat : XYZ.COM LLC +// https://www.iana.org/domains/root/db/lat.html +lat + +// latino : Dish DBS Corporation +// https://www.iana.org/domains/root/db/latino.html +latino + +// latrobe : La Trobe University +// https://www.iana.org/domains/root/db/latrobe.html +latrobe + +// law : Registry Services, LLC +// https://www.iana.org/domains/root/db/law.html +law + +// lawyer : Dog Beach, LLC +// https://www.iana.org/domains/root/db/lawyer.html +lawyer + +// lds : IRI Domain Management, LLC +// https://www.iana.org/domains/root/db/lds.html +lds + +// lease : Binky Moon, LLC +// https://www.iana.org/domains/root/db/lease.html +lease + +// leclerc : A.C.D. LEC Association des Centres Distributeurs Edouard Leclerc +// https://www.iana.org/domains/root/db/leclerc.html +leclerc + +// lefrak : LeFrak Organization, Inc. +// https://www.iana.org/domains/root/db/lefrak.html +lefrak + +// legal : Binky Moon, LLC +// https://www.iana.org/domains/root/db/legal.html +legal + +// lego : LEGO Juris A/S +// https://www.iana.org/domains/root/db/lego.html +lego + +// lexus : TOYOTA MOTOR CORPORATION +// https://www.iana.org/domains/root/db/lexus.html +lexus + +// lgbt : Identity Digital Limited +// https://www.iana.org/domains/root/db/lgbt.html +lgbt + +// lidl : Schwarz Domains und Services GmbH & Co. KG +// https://www.iana.org/domains/root/db/lidl.html +lidl + +// life : Binky Moon, LLC +// https://www.iana.org/domains/root/db/life.html +life + +// lifeinsurance : American Council of Life Insurers +// https://www.iana.org/domains/root/db/lifeinsurance.html +lifeinsurance + +// lifestyle : Internet Naming Company LLC +// https://www.iana.org/domains/root/db/lifestyle.html +lifestyle + +// lighting : Binky Moon, LLC +// https://www.iana.org/domains/root/db/lighting.html +lighting + +// like : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/like.html +like + +// lilly : Eli Lilly and Company +// https://www.iana.org/domains/root/db/lilly.html +lilly + +// limited : Binky Moon, LLC +// https://www.iana.org/domains/root/db/limited.html +limited + +// limo : Binky Moon, LLC +// https://www.iana.org/domains/root/db/limo.html +limo + +// lincoln : Ford Motor Company +// https://www.iana.org/domains/root/db/lincoln.html +lincoln + +// link : Nova Registry Ltd +// https://www.iana.org/domains/root/db/link.html +link + +// lipsy : Lipsy Ltd +// https://www.iana.org/domains/root/db/lipsy.html +lipsy + +// live : Dog Beach, LLC +// https://www.iana.org/domains/root/db/live.html +live + +// living : Internet Naming Company LLC +// https://www.iana.org/domains/root/db/living.html +living + +// llc : Identity Digital Limited +// https://www.iana.org/domains/root/db/llc.html +llc + +// llp : Intercap Registry Inc. +// https://www.iana.org/domains/root/db/llp.html +llp + +// loan : dot Loan Limited +// https://www.iana.org/domains/root/db/loan.html +loan + +// loans : Binky Moon, LLC +// https://www.iana.org/domains/root/db/loans.html +loans + +// locker : Orange Domains LLC +// https://www.iana.org/domains/root/db/locker.html +locker + +// locus : Locus Analytics LLC +// https://www.iana.org/domains/root/db/locus.html +locus + +// lol : XYZ.COM LLC +// https://www.iana.org/domains/root/db/lol.html +lol + +// london : Dot London Domains Limited +// https://www.iana.org/domains/root/db/london.html +london + +// lotte : Lotte Holdings Co., Ltd. +// https://www.iana.org/domains/root/db/lotte.html +lotte + +// lotto : Identity Digital Limited +// https://www.iana.org/domains/root/db/lotto.html +lotto + +// love : Merchant Law Group LLP +// https://www.iana.org/domains/root/db/love.html +love + +// lpl : LPL Holdings, Inc. +// https://www.iana.org/domains/root/db/lpl.html +lpl + +// lplfinancial : LPL Holdings, Inc. +// https://www.iana.org/domains/root/db/lplfinancial.html +lplfinancial + +// ltd : Binky Moon, LLC +// https://www.iana.org/domains/root/db/ltd.html +ltd + +// ltda : InterNetX, Corp +// https://www.iana.org/domains/root/db/ltda.html +ltda + +// lundbeck : H. Lundbeck A/S +// https://www.iana.org/domains/root/db/lundbeck.html +lundbeck + +// luxe : Registry Services, LLC +// https://www.iana.org/domains/root/db/luxe.html +luxe + +// luxury : Luxury Partners, LLC +// https://www.iana.org/domains/root/db/luxury.html +luxury + +// madrid : Comunidad de Madrid +// https://www.iana.org/domains/root/db/madrid.html +madrid + +// maif : Mutuelle Assurance Instituteur France (MAIF) +// https://www.iana.org/domains/root/db/maif.html +maif + +// maison : Binky Moon, LLC +// https://www.iana.org/domains/root/db/maison.html +maison + +// makeup : XYZ.COM LLC +// https://www.iana.org/domains/root/db/makeup.html +makeup + +// man : MAN SE +// https://www.iana.org/domains/root/db/man.html +man + +// management : Binky Moon, LLC +// https://www.iana.org/domains/root/db/management.html +management + +// mango : PUNTO FA S.L. +// https://www.iana.org/domains/root/db/mango.html +mango + +// map : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/map.html +map + +// market : Dog Beach, LLC +// https://www.iana.org/domains/root/db/market.html +market + +// marketing : Binky Moon, LLC +// https://www.iana.org/domains/root/db/marketing.html +marketing + +// markets : Dog Beach, LLC +// https://www.iana.org/domains/root/db/markets.html +markets + +// marriott : Marriott Worldwide Corporation +// https://www.iana.org/domains/root/db/marriott.html +marriott + +// marshalls : The TJX Companies, Inc. +// https://www.iana.org/domains/root/db/marshalls.html +marshalls + +// mattel : Mattel Sites, Inc. +// https://www.iana.org/domains/root/db/mattel.html +mattel + +// mba : Binky Moon, LLC +// https://www.iana.org/domains/root/db/mba.html +mba + +// mckinsey : McKinsey Holdings, Inc. +// https://www.iana.org/domains/root/db/mckinsey.html +mckinsey + +// med : Medistry LLC +// https://www.iana.org/domains/root/db/med.html +med + +// media : Binky Moon, LLC +// https://www.iana.org/domains/root/db/media.html +media + +// meet : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/meet.html +meet + +// melbourne : The Crown in right of the State of Victoria, represented by its Department of State Development, Business and Innovation +// https://www.iana.org/domains/root/db/melbourne.html +melbourne + +// meme : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/meme.html +meme + +// memorial : Dog Beach, LLC +// https://www.iana.org/domains/root/db/memorial.html +memorial + +// men : Exclusive Registry Limited +// https://www.iana.org/domains/root/db/men.html +men + +// menu : Dot Menu Registry, LLC +// https://www.iana.org/domains/root/db/menu.html +menu + +// merckmsd : MSD Registry Holdings, Inc. +// https://www.iana.org/domains/root/db/merckmsd.html +merckmsd + +// miami : Registry Services, LLC +// https://www.iana.org/domains/root/db/miami.html +miami + +// microsoft : Microsoft Corporation +// https://www.iana.org/domains/root/db/microsoft.html +microsoft + +// mini : Bayerische Motoren Werke Aktiengesellschaft +// https://www.iana.org/domains/root/db/mini.html +mini + +// mint : Intuit Administrative Services, Inc. +// https://www.iana.org/domains/root/db/mint.html +mint + +// mit : Massachusetts Institute of Technology +// https://www.iana.org/domains/root/db/mit.html +mit + +// mitsubishi : Mitsubishi Corporation +// https://www.iana.org/domains/root/db/mitsubishi.html +mitsubishi + +// mlb : MLB Advanced Media DH, LLC +// https://www.iana.org/domains/root/db/mlb.html +mlb + +// mls : The Canadian Real Estate Association +// https://www.iana.org/domains/root/db/mls.html +mls + +// mma : MMA IARD +// https://www.iana.org/domains/root/db/mma.html +mma + +// mobile : Dish DBS Corporation +// https://www.iana.org/domains/root/db/mobile.html +mobile + +// moda : Dog Beach, LLC +// https://www.iana.org/domains/root/db/moda.html +moda + +// moe : Interlink Systems Innovation Institute K.K. +// https://www.iana.org/domains/root/db/moe.html +moe + +// moi : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/moi.html +moi + +// mom : XYZ.COM LLC +// https://www.iana.org/domains/root/db/mom.html +mom + +// monash : Monash University +// https://www.iana.org/domains/root/db/monash.html +monash + +// money : Binky Moon, LLC +// https://www.iana.org/domains/root/db/money.html +money + +// monster : XYZ.COM LLC +// https://www.iana.org/domains/root/db/monster.html +monster + +// mormon : IRI Domain Management, LLC +// https://www.iana.org/domains/root/db/mormon.html +mormon + +// mortgage : Dog Beach, LLC +// https://www.iana.org/domains/root/db/mortgage.html +mortgage + +// moscow : Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID) +// https://www.iana.org/domains/root/db/moscow.html +moscow + +// moto : Motorola Trademark Holdings, LLC +// https://www.iana.org/domains/root/db/moto.html +moto + +// motorcycles : XYZ.COM LLC +// https://www.iana.org/domains/root/db/motorcycles.html +motorcycles + +// mov : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/mov.html +mov + +// movie : Binky Moon, LLC +// https://www.iana.org/domains/root/db/movie.html +movie + +// msd : MSD Registry Holdings, Inc. +// https://www.iana.org/domains/root/db/msd.html +msd + +// mtn : MTN Dubai Limited +// https://www.iana.org/domains/root/db/mtn.html +mtn + +// mtr : MTR Corporation Limited +// https://www.iana.org/domains/root/db/mtr.html +mtr + +// music : DotMusic Limited +// https://www.iana.org/domains/root/db/music.html +music + +// nab : National Australia Bank Limited +// https://www.iana.org/domains/root/db/nab.html +nab + +// nagoya : GMO Registry, Inc. +// https://www.iana.org/domains/root/db/nagoya.html +nagoya + +// natura : NATURA COSMÉTICOS S.A. +// https://www.iana.org/domains/root/db/natura.html +natura + +// navy : Dog Beach, LLC +// https://www.iana.org/domains/root/db/navy.html +navy + +// nba : NBA REGISTRY, LLC +// https://www.iana.org/domains/root/db/nba.html +nba + +// nec : NEC Corporation +// https://www.iana.org/domains/root/db/nec.html +nec + +// netbank : COMMONWEALTH BANK OF AUSTRALIA +// https://www.iana.org/domains/root/db/netbank.html +netbank + +// netflix : Netflix, Inc. +// https://www.iana.org/domains/root/db/netflix.html +netflix + +// network : Binky Moon, LLC +// https://www.iana.org/domains/root/db/network.html +network + +// neustar : NeuStar, Inc. +// https://www.iana.org/domains/root/db/neustar.html +neustar + +// new : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/new.html +new + +// news : Dog Beach, LLC +// https://www.iana.org/domains/root/db/news.html +news + +// next : Next plc +// https://www.iana.org/domains/root/db/next.html +next + +// nextdirect : Next plc +// https://www.iana.org/domains/root/db/nextdirect.html +nextdirect + +// nexus : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/nexus.html +nexus + +// nfl : NFL Reg Ops LLC +// https://www.iana.org/domains/root/db/nfl.html +nfl + +// ngo : Public Interest Registry +// https://www.iana.org/domains/root/db/ngo.html +ngo + +// nhk : Japan Broadcasting Corporation (NHK) +// https://www.iana.org/domains/root/db/nhk.html +nhk + +// nico : DWANGO Co., Ltd. +// https://www.iana.org/domains/root/db/nico.html +nico + +// nike : NIKE, Inc. +// https://www.iana.org/domains/root/db/nike.html +nike + +// nikon : NIKON CORPORATION +// https://www.iana.org/domains/root/db/nikon.html +nikon + +// ninja : Dog Beach, LLC +// https://www.iana.org/domains/root/db/ninja.html +ninja + +// nissan : NISSAN MOTOR CO., LTD. +// https://www.iana.org/domains/root/db/nissan.html +nissan + +// nissay : Nippon Life Insurance Company +// https://www.iana.org/domains/root/db/nissay.html +nissay + +// nokia : Nokia Corporation +// https://www.iana.org/domains/root/db/nokia.html +nokia + +// norton : NortonLifeLock Inc. +// https://www.iana.org/domains/root/db/norton.html +norton + +// now : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/now.html +now + +// nowruz : Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. +// https://www.iana.org/domains/root/db/nowruz.html +nowruz + +// nowtv : Starbucks (HK) Limited +// https://www.iana.org/domains/root/db/nowtv.html +nowtv + +// nra : NRA Holdings Company, INC. +// https://www.iana.org/domains/root/db/nra.html +nra + +// nrw : Minds + Machines GmbH +// https://www.iana.org/domains/root/db/nrw.html +nrw + +// ntt : NIPPON TELEGRAPH AND TELEPHONE CORPORATION +// https://www.iana.org/domains/root/db/ntt.html +ntt + +// nyc : The City of New York by and through the New York City Department of Information Technology & Telecommunications +// https://www.iana.org/domains/root/db/nyc.html +nyc + +// obi : OBI Group Holding SE & Co. KGaA +// https://www.iana.org/domains/root/db/obi.html +obi + +// observer : Fegistry, LLC +// https://www.iana.org/domains/root/db/observer.html +observer + +// office : Microsoft Corporation +// https://www.iana.org/domains/root/db/office.html +office + +// okinawa : BRregistry, Inc. +// https://www.iana.org/domains/root/db/okinawa.html +okinawa + +// olayan : Competrol (Luxembourg) Sarl +// https://www.iana.org/domains/root/db/olayan.html +olayan + +// olayangroup : Competrol (Luxembourg) Sarl +// https://www.iana.org/domains/root/db/olayangroup.html +olayangroup + +// ollo : Dish DBS Corporation +// https://www.iana.org/domains/root/db/ollo.html +ollo + +// omega : The Swatch Group Ltd +// https://www.iana.org/domains/root/db/omega.html +omega + +// one : One.com A/S +// https://www.iana.org/domains/root/db/one.html +one + +// ong : Public Interest Registry +// https://www.iana.org/domains/root/db/ong.html +ong + +// onl : iRegistry GmbH +// https://www.iana.org/domains/root/db/onl.html +onl + +// online : Radix Technologies Inc. +// https://www.iana.org/domains/root/db/online.html +online + +// ooo : INFIBEAM AVENUES LIMITED +// https://www.iana.org/domains/root/db/ooo.html +ooo + +// open : American Express Travel Related Services Company, Inc. +// https://www.iana.org/domains/root/db/open.html +open + +// oracle : Oracle Corporation +// https://www.iana.org/domains/root/db/oracle.html +oracle + +// orange : Orange Brand Services Limited +// https://www.iana.org/domains/root/db/orange.html +orange + +// organic : Identity Digital Limited +// https://www.iana.org/domains/root/db/organic.html +organic + +// origins : The Estée Lauder Companies Inc. +// https://www.iana.org/domains/root/db/origins.html +origins + +// osaka : Osaka Registry Co., Ltd. +// https://www.iana.org/domains/root/db/osaka.html +osaka + +// otsuka : Otsuka Holdings Co., Ltd. +// https://www.iana.org/domains/root/db/otsuka.html +otsuka + +// ott : Dish DBS Corporation +// https://www.iana.org/domains/root/db/ott.html +ott + +// ovh : MédiaBC +// https://www.iana.org/domains/root/db/ovh.html +ovh + +// page : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/page.html +page + +// panasonic : Panasonic Holdings Corporation +// https://www.iana.org/domains/root/db/panasonic.html +panasonic + +// paris : City of Paris +// https://www.iana.org/domains/root/db/paris.html +paris + +// pars : Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. +// https://www.iana.org/domains/root/db/pars.html +pars + +// partners : Binky Moon, LLC +// https://www.iana.org/domains/root/db/partners.html +partners + +// parts : Binky Moon, LLC +// https://www.iana.org/domains/root/db/parts.html +parts + +// party : Blue Sky Registry Limited +// https://www.iana.org/domains/root/db/party.html +party + +// pay : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/pay.html +pay + +// pccw : PCCW Enterprises Limited +// https://www.iana.org/domains/root/db/pccw.html +pccw + +// pet : Identity Digital Limited +// https://www.iana.org/domains/root/db/pet.html +pet + +// pfizer : Pfizer Inc. +// https://www.iana.org/domains/root/db/pfizer.html +pfizer + +// pharmacy : National Association of Boards of Pharmacy +// https://www.iana.org/domains/root/db/pharmacy.html +pharmacy + +// phd : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/phd.html +phd + +// philips : Koninklijke Philips N.V. +// https://www.iana.org/domains/root/db/philips.html +philips + +// phone : Dish DBS Corporation +// https://www.iana.org/domains/root/db/phone.html +phone + +// photo : Registry Services, LLC +// https://www.iana.org/domains/root/db/photo.html +photo + +// photography : Binky Moon, LLC +// https://www.iana.org/domains/root/db/photography.html +photography + +// photos : Binky Moon, LLC +// https://www.iana.org/domains/root/db/photos.html +photos + +// physio : PhysBiz Pty Ltd +// https://www.iana.org/domains/root/db/physio.html +physio + +// pics : XYZ.COM LLC +// https://www.iana.org/domains/root/db/pics.html +pics + +// pictet : Pictet Europe S.A. +// https://www.iana.org/domains/root/db/pictet.html +pictet + +// pictures : Binky Moon, LLC +// https://www.iana.org/domains/root/db/pictures.html +pictures + +// pid : Top Level Spectrum, Inc. +// https://www.iana.org/domains/root/db/pid.html +pid + +// pin : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/pin.html +pin + +// ping : Ping Registry Provider, Inc. +// https://www.iana.org/domains/root/db/ping.html +ping + +// pink : Identity Digital Limited +// https://www.iana.org/domains/root/db/pink.html +pink + +// pioneer : Pioneer Corporation +// https://www.iana.org/domains/root/db/pioneer.html +pioneer + +// pizza : Binky Moon, LLC +// https://www.iana.org/domains/root/db/pizza.html +pizza + +// place : Binky Moon, LLC +// https://www.iana.org/domains/root/db/place.html +place + +// play : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/play.html +play + +// playstation : Sony Interactive Entertainment Inc. +// https://www.iana.org/domains/root/db/playstation.html +playstation + +// plumbing : Binky Moon, LLC +// https://www.iana.org/domains/root/db/plumbing.html +plumbing + +// plus : Binky Moon, LLC +// https://www.iana.org/domains/root/db/plus.html +plus + +// pnc : PNC Domain Co., LLC +// https://www.iana.org/domains/root/db/pnc.html +pnc + +// pohl : Deutsche Vermögensberatung Aktiengesellschaft DVAG +// https://www.iana.org/domains/root/db/pohl.html +pohl + +// poker : Identity Digital Limited +// https://www.iana.org/domains/root/db/poker.html +poker + +// politie : Politie Nederland +// https://www.iana.org/domains/root/db/politie.html +politie + +// porn : ICM Registry PN LLC +// https://www.iana.org/domains/root/db/porn.html +porn + +// pramerica : Prudential Financial, Inc. +// https://www.iana.org/domains/root/db/pramerica.html +pramerica + +// praxi : Praxi S.p.A. +// https://www.iana.org/domains/root/db/praxi.html +praxi + +// press : Radix Technologies Inc. +// https://www.iana.org/domains/root/db/press.html +press + +// prime : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/prime.html +prime + +// prod : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/prod.html +prod + +// productions : Binky Moon, LLC +// https://www.iana.org/domains/root/db/productions.html +productions + +// prof : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/prof.html +prof + +// progressive : Progressive Casualty Insurance Company +// https://www.iana.org/domains/root/db/progressive.html +progressive + +// promo : Identity Digital Limited +// https://www.iana.org/domains/root/db/promo.html +promo + +// properties : Binky Moon, LLC +// https://www.iana.org/domains/root/db/properties.html +properties + +// property : Digital Property Infrastructure Limited +// https://www.iana.org/domains/root/db/property.html +property + +// protection : XYZ.COM LLC +// https://www.iana.org/domains/root/db/protection.html +protection + +// pru : Prudential Financial, Inc. +// https://www.iana.org/domains/root/db/pru.html +pru + +// prudential : Prudential Financial, Inc. +// https://www.iana.org/domains/root/db/prudential.html +prudential + +// pub : Dog Beach, LLC +// https://www.iana.org/domains/root/db/pub.html +pub + +// pwc : PricewaterhouseCoopers LLP +// https://www.iana.org/domains/root/db/pwc.html +pwc + +// qpon : dotQPON LLC +// https://www.iana.org/domains/root/db/qpon.html +qpon + +// quebec : PointQuébec Inc +// https://www.iana.org/domains/root/db/quebec.html +quebec + +// quest : XYZ.COM LLC +// https://www.iana.org/domains/root/db/quest.html +quest + +// racing : Premier Registry Limited +// https://www.iana.org/domains/root/db/racing.html +racing + +// radio : European Broadcasting Union (EBU) +// https://www.iana.org/domains/root/db/radio.html +radio + +// read : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/read.html +read + +// realestate : dotRealEstate LLC +// https://www.iana.org/domains/root/db/realestate.html +realestate + +// realtor : Real Estate Domains LLC +// https://www.iana.org/domains/root/db/realtor.html +realtor + +// realty : Internet Naming Company LLC +// https://www.iana.org/domains/root/db/realty.html +realty + +// recipes : Binky Moon, LLC +// https://www.iana.org/domains/root/db/recipes.html +recipes + +// red : Identity Digital Limited +// https://www.iana.org/domains/root/db/red.html +red + +// redstone : Redstone Haute Couture Co., Ltd. +// https://www.iana.org/domains/root/db/redstone.html +redstone + +// redumbrella : Travelers TLD, LLC +// https://www.iana.org/domains/root/db/redumbrella.html +redumbrella + +// rehab : Dog Beach, LLC +// https://www.iana.org/domains/root/db/rehab.html +rehab + +// reise : Binky Moon, LLC +// https://www.iana.org/domains/root/db/reise.html +reise + +// reisen : Binky Moon, LLC +// https://www.iana.org/domains/root/db/reisen.html +reisen + +// reit : National Association of Real Estate Investment Trusts, Inc. +// https://www.iana.org/domains/root/db/reit.html +reit + +// reliance : Reliance Industries Limited +// https://www.iana.org/domains/root/db/reliance.html +reliance + +// ren : ZDNS International Limited +// https://www.iana.org/domains/root/db/ren.html +ren + +// rent : XYZ.COM LLC +// https://www.iana.org/domains/root/db/rent.html +rent + +// rentals : Binky Moon, LLC +// https://www.iana.org/domains/root/db/rentals.html +rentals + +// repair : Binky Moon, LLC +// https://www.iana.org/domains/root/db/repair.html +repair + +// report : Binky Moon, LLC +// https://www.iana.org/domains/root/db/report.html +report + +// republican : Dog Beach, LLC +// https://www.iana.org/domains/root/db/republican.html +republican + +// rest : Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable +// https://www.iana.org/domains/root/db/rest.html +rest + +// restaurant : Binky Moon, LLC +// https://www.iana.org/domains/root/db/restaurant.html +restaurant + +// review : dot Review Limited +// https://www.iana.org/domains/root/db/review.html +review + +// reviews : Dog Beach, LLC +// https://www.iana.org/domains/root/db/reviews.html +reviews + +// rexroth : Robert Bosch GMBH +// https://www.iana.org/domains/root/db/rexroth.html +rexroth + +// rich : iRegistry GmbH +// https://www.iana.org/domains/root/db/rich.html +rich + +// richardli : Pacific Century Asset Management (HK) Limited +// https://www.iana.org/domains/root/db/richardli.html +richardli + +// ricoh : Ricoh Company, Ltd. +// https://www.iana.org/domains/root/db/ricoh.html +ricoh + +// ril : Reliance Industries Limited +// https://www.iana.org/domains/root/db/ril.html +ril + +// rio : Empresa Municipal de Informática SA - IPLANRIO +// https://www.iana.org/domains/root/db/rio.html +rio + +// rip : Dog Beach, LLC +// https://www.iana.org/domains/root/db/rip.html +rip + +// rocks : Dog Beach, LLC +// https://www.iana.org/domains/root/db/rocks.html +rocks + +// rodeo : Registry Services, LLC +// https://www.iana.org/domains/root/db/rodeo.html +rodeo + +// rogers : Rogers Communications Canada Inc. +// https://www.iana.org/domains/root/db/rogers.html +rogers + +// room : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/room.html +room + +// rsvp : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/rsvp.html +rsvp + +// rugby : World Rugby Strategic Developments Limited +// https://www.iana.org/domains/root/db/rugby.html +rugby + +// ruhr : dotSaarland GmbH +// https://www.iana.org/domains/root/db/ruhr.html +ruhr + +// run : Binky Moon, LLC +// https://www.iana.org/domains/root/db/run.html +run + +// rwe : RWE AG +// https://www.iana.org/domains/root/db/rwe.html +rwe + +// ryukyu : BRregistry, Inc. +// https://www.iana.org/domains/root/db/ryukyu.html +ryukyu + +// saarland : dotSaarland GmbH +// https://www.iana.org/domains/root/db/saarland.html +saarland + +// safe : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/safe.html +safe + +// safety : Safety Registry Services, LLC. +// https://www.iana.org/domains/root/db/safety.html +safety + +// sakura : SAKURA Internet Inc. +// https://www.iana.org/domains/root/db/sakura.html +sakura + +// sale : Dog Beach, LLC +// https://www.iana.org/domains/root/db/sale.html +sale + +// salon : Binky Moon, LLC +// https://www.iana.org/domains/root/db/salon.html +salon + +// samsclub : Wal-Mart Stores, Inc. +// https://www.iana.org/domains/root/db/samsclub.html +samsclub + +// samsung : SAMSUNG SDS CO., LTD +// https://www.iana.org/domains/root/db/samsung.html +samsung + +// sandvik : Sandvik AB +// https://www.iana.org/domains/root/db/sandvik.html +sandvik + +// sandvikcoromant : Sandvik AB +// https://www.iana.org/domains/root/db/sandvikcoromant.html +sandvikcoromant + +// sanofi : Sanofi +// https://www.iana.org/domains/root/db/sanofi.html +sanofi + +// sap : SAP AG +// https://www.iana.org/domains/root/db/sap.html +sap + +// sarl : Binky Moon, LLC +// https://www.iana.org/domains/root/db/sarl.html +sarl + +// sas : Research IP LLC +// https://www.iana.org/domains/root/db/sas.html +sas + +// save : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/save.html +save + +// saxo : Saxo Bank A/S +// https://www.iana.org/domains/root/db/saxo.html +saxo + +// sbi : STATE BANK OF INDIA +// https://www.iana.org/domains/root/db/sbi.html +sbi + +// sbs : ShortDot SA +// https://www.iana.org/domains/root/db/sbs.html +sbs + +// scb : The Siam Commercial Bank Public Company Limited ("SCB") +// https://www.iana.org/domains/root/db/scb.html +scb + +// schaeffler : Schaeffler Technologies AG & Co. KG +// https://www.iana.org/domains/root/db/schaeffler.html +schaeffler + +// schmidt : SCHMIDT GROUPE S.A.S. +// https://www.iana.org/domains/root/db/schmidt.html +schmidt + +// scholarships : Scholarships.com, LLC +// https://www.iana.org/domains/root/db/scholarships.html +scholarships + +// school : Binky Moon, LLC +// https://www.iana.org/domains/root/db/school.html +school + +// schule : Binky Moon, LLC +// https://www.iana.org/domains/root/db/schule.html +schule + +// schwarz : Schwarz Domains und Services GmbH & Co. KG +// https://www.iana.org/domains/root/db/schwarz.html +schwarz + +// science : dot Science Limited +// https://www.iana.org/domains/root/db/science.html +science + +// scot : Dot Scot Registry Limited +// https://www.iana.org/domains/root/db/scot.html +scot + +// search : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/search.html +search + +// seat : SEAT, S.A. (Sociedad Unipersonal) +// https://www.iana.org/domains/root/db/seat.html +seat + +// secure : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/secure.html +secure + +// security : XYZ.COM LLC +// https://www.iana.org/domains/root/db/security.html +security + +// seek : Seek Limited +// https://www.iana.org/domains/root/db/seek.html +seek + +// select : Registry Services, LLC +// https://www.iana.org/domains/root/db/select.html +select + +// sener : Sener Ingeniería y Sistemas, S.A. +// https://www.iana.org/domains/root/db/sener.html +sener + +// services : Binky Moon, LLC +// https://www.iana.org/domains/root/db/services.html +services + +// seven : Seven West Media Ltd +// https://www.iana.org/domains/root/db/seven.html +seven + +// sew : SEW-EURODRIVE GmbH & Co KG +// https://www.iana.org/domains/root/db/sew.html +sew + +// sex : ICM Registry SX LLC +// https://www.iana.org/domains/root/db/sex.html +sex + +// sexy : Internet Naming Company LLC +// https://www.iana.org/domains/root/db/sexy.html +sexy + +// sfr : Societe Francaise du Radiotelephone - SFR +// https://www.iana.org/domains/root/db/sfr.html +sfr + +// shangrila : Shangri‐La International Hotel Management Limited +// https://www.iana.org/domains/root/db/shangrila.html +shangrila + +// sharp : Sharp Corporation +// https://www.iana.org/domains/root/db/sharp.html +sharp + +// shaw : Shaw Cablesystems G.P. +// https://www.iana.org/domains/root/db/shaw.html +shaw + +// shell : Shell Information Technology International Inc +// https://www.iana.org/domains/root/db/shell.html +shell + +// shia : Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. +// https://www.iana.org/domains/root/db/shia.html +shia + +// shiksha : Identity Digital Limited +// https://www.iana.org/domains/root/db/shiksha.html +shiksha + +// shoes : Binky Moon, LLC +// https://www.iana.org/domains/root/db/shoes.html +shoes + +// shop : GMO Registry, Inc. +// https://www.iana.org/domains/root/db/shop.html +shop + +// shopping : Binky Moon, LLC +// https://www.iana.org/domains/root/db/shopping.html +shopping + +// shouji : Beijing Qihu Keji Co., Ltd. +// https://www.iana.org/domains/root/db/shouji.html +shouji + +// show : Binky Moon, LLC +// https://www.iana.org/domains/root/db/show.html +show + +// silk : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/silk.html +silk + +// sina : Sina Corporation +// https://www.iana.org/domains/root/db/sina.html +sina + +// singles : Binky Moon, LLC +// https://www.iana.org/domains/root/db/singles.html +singles + +// site : Radix Technologies Inc. +// https://www.iana.org/domains/root/db/site.html +site + +// ski : Identity Digital Limited +// https://www.iana.org/domains/root/db/ski.html +ski + +// skin : XYZ.COM LLC +// https://www.iana.org/domains/root/db/skin.html +skin + +// sky : Sky International AG +// https://www.iana.org/domains/root/db/sky.html +sky + +// skype : Microsoft Corporation +// https://www.iana.org/domains/root/db/skype.html +skype + +// sling : DISH Technologies L.L.C. +// https://www.iana.org/domains/root/db/sling.html +sling + +// smart : Smart Communications, Inc. (SMART) +// https://www.iana.org/domains/root/db/smart.html +smart + +// smile : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/smile.html +smile + +// sncf : Société Nationale SNCF +// https://www.iana.org/domains/root/db/sncf.html +sncf + +// soccer : Binky Moon, LLC +// https://www.iana.org/domains/root/db/soccer.html +soccer + +// social : Dog Beach, LLC +// https://www.iana.org/domains/root/db/social.html +social + +// softbank : SoftBank Group Corp. +// https://www.iana.org/domains/root/db/softbank.html +softbank + +// software : Dog Beach, LLC +// https://www.iana.org/domains/root/db/software.html +software + +// sohu : Sohu.com Limited +// https://www.iana.org/domains/root/db/sohu.html +sohu + +// solar : Binky Moon, LLC +// https://www.iana.org/domains/root/db/solar.html +solar + +// solutions : Binky Moon, LLC +// https://www.iana.org/domains/root/db/solutions.html +solutions + +// song : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/song.html +song + +// sony : Sony Corporation +// https://www.iana.org/domains/root/db/sony.html +sony + +// soy : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/soy.html +soy + +// spa : Asia Spa and Wellness Promotion Council Limited +// https://www.iana.org/domains/root/db/spa.html +spa + +// space : Radix Technologies Inc. +// https://www.iana.org/domains/root/db/space.html +space + +// sport : SportAccord +// https://www.iana.org/domains/root/db/sport.html +sport + +// spot : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/spot.html +spot + +// srl : InterNetX, Corp +// https://www.iana.org/domains/root/db/srl.html +srl + +// stada : STADA Arzneimittel AG +// https://www.iana.org/domains/root/db/stada.html +stada + +// staples : Staples, Inc. +// https://www.iana.org/domains/root/db/staples.html +staples + +// star : Star India Private Limited +// https://www.iana.org/domains/root/db/star.html +star + +// statebank : STATE BANK OF INDIA +// https://www.iana.org/domains/root/db/statebank.html +statebank + +// statefarm : State Farm Mutual Automobile Insurance Company +// https://www.iana.org/domains/root/db/statefarm.html +statefarm + +// stc : Saudi Telecom Company +// https://www.iana.org/domains/root/db/stc.html +stc + +// stcgroup : Saudi Telecom Company +// https://www.iana.org/domains/root/db/stcgroup.html +stcgroup + +// stockholm : Stockholms kommun +// https://www.iana.org/domains/root/db/stockholm.html +stockholm + +// storage : XYZ.COM LLC +// https://www.iana.org/domains/root/db/storage.html +storage + +// store : Radix Technologies Inc. +// https://www.iana.org/domains/root/db/store.html +store + +// stream : dot Stream Limited +// https://www.iana.org/domains/root/db/stream.html +stream + +// studio : Dog Beach, LLC +// https://www.iana.org/domains/root/db/studio.html +studio + +// study : Registry Services, LLC +// https://www.iana.org/domains/root/db/study.html +study + +// style : Binky Moon, LLC +// https://www.iana.org/domains/root/db/style.html +style + +// sucks : Vox Populi Registry Ltd. +// https://www.iana.org/domains/root/db/sucks.html +sucks + +// supplies : Binky Moon, LLC +// https://www.iana.org/domains/root/db/supplies.html +supplies + +// supply : Binky Moon, LLC +// https://www.iana.org/domains/root/db/supply.html +supply + +// support : Binky Moon, LLC +// https://www.iana.org/domains/root/db/support.html +support + +// surf : Registry Services, LLC +// https://www.iana.org/domains/root/db/surf.html +surf + +// surgery : Binky Moon, LLC +// https://www.iana.org/domains/root/db/surgery.html +surgery + +// suzuki : SUZUKI MOTOR CORPORATION +// https://www.iana.org/domains/root/db/suzuki.html +suzuki + +// swatch : The Swatch Group Ltd +// https://www.iana.org/domains/root/db/swatch.html +swatch + +// swiss : Swiss Confederation +// https://www.iana.org/domains/root/db/swiss.html +swiss + +// sydney : State of New South Wales, Department of Premier and Cabinet +// https://www.iana.org/domains/root/db/sydney.html +sydney + +// systems : Binky Moon, LLC +// https://www.iana.org/domains/root/db/systems.html +systems + +// tab : Tabcorp Holdings Limited +// https://www.iana.org/domains/root/db/tab.html +tab + +// taipei : Taipei City Government +// https://www.iana.org/domains/root/db/taipei.html +taipei + +// talk : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/talk.html +talk + +// taobao : Alibaba Group Holding Limited +// https://www.iana.org/domains/root/db/taobao.html +taobao + +// target : Target Domain Holdings, LLC +// https://www.iana.org/domains/root/db/target.html +target + +// tatamotors : Tata Motors Ltd +// https://www.iana.org/domains/root/db/tatamotors.html +tatamotors + +// tatar : Limited Liability Company "Coordination Center of Regional Domain of Tatarstan Republic" +// https://www.iana.org/domains/root/db/tatar.html +tatar + +// tattoo : Registry Services, LLC +// https://www.iana.org/domains/root/db/tattoo.html +tattoo + +// tax : Binky Moon, LLC +// https://www.iana.org/domains/root/db/tax.html +tax + +// taxi : Binky Moon, LLC +// https://www.iana.org/domains/root/db/taxi.html +taxi + +// tci : Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. +// https://www.iana.org/domains/root/db/tci.html +tci + +// tdk : TDK Corporation +// https://www.iana.org/domains/root/db/tdk.html +tdk + +// team : Binky Moon, LLC +// https://www.iana.org/domains/root/db/team.html +team + +// tech : Radix Technologies Inc. +// https://www.iana.org/domains/root/db/tech.html +tech + +// technology : Binky Moon, LLC +// https://www.iana.org/domains/root/db/technology.html +technology + +// temasek : Temasek Holdings (Private) Limited +// https://www.iana.org/domains/root/db/temasek.html +temasek + +// tennis : Binky Moon, LLC +// https://www.iana.org/domains/root/db/tennis.html +tennis + +// teva : Teva Pharmaceutical Industries Limited +// https://www.iana.org/domains/root/db/teva.html +teva + +// thd : Home Depot Product Authority, LLC +// https://www.iana.org/domains/root/db/thd.html +thd + +// theater : Binky Moon, LLC +// https://www.iana.org/domains/root/db/theater.html +theater + +// theatre : XYZ.COM LLC +// https://www.iana.org/domains/root/db/theatre.html +theatre + +// tiaa : Teachers Insurance and Annuity Association of America +// https://www.iana.org/domains/root/db/tiaa.html +tiaa + +// tickets : XYZ.COM LLC +// https://www.iana.org/domains/root/db/tickets.html +tickets + +// tienda : Binky Moon, LLC +// https://www.iana.org/domains/root/db/tienda.html +tienda + +// tips : Binky Moon, LLC +// https://www.iana.org/domains/root/db/tips.html +tips + +// tires : Binky Moon, LLC +// https://www.iana.org/domains/root/db/tires.html +tires + +// tirol : punkt Tirol GmbH +// https://www.iana.org/domains/root/db/tirol.html +tirol + +// tjmaxx : The TJX Companies, Inc. +// https://www.iana.org/domains/root/db/tjmaxx.html +tjmaxx + +// tjx : The TJX Companies, Inc. +// https://www.iana.org/domains/root/db/tjx.html +tjx + +// tkmaxx : The TJX Companies, Inc. +// https://www.iana.org/domains/root/db/tkmaxx.html +tkmaxx + +// tmall : Alibaba Group Holding Limited +// https://www.iana.org/domains/root/db/tmall.html +tmall + +// today : Binky Moon, LLC +// https://www.iana.org/domains/root/db/today.html +today + +// tokyo : GMO Registry, Inc. +// https://www.iana.org/domains/root/db/tokyo.html +tokyo + +// tools : Binky Moon, LLC +// https://www.iana.org/domains/root/db/tools.html +tools + +// top : .TOP Registry +// https://www.iana.org/domains/root/db/top.html +top + +// toray : Toray Industries, Inc. +// https://www.iana.org/domains/root/db/toray.html +toray + +// toshiba : TOSHIBA Corporation +// https://www.iana.org/domains/root/db/toshiba.html +toshiba + +// total : TotalEnergies SE +// https://www.iana.org/domains/root/db/total.html +total + +// tours : Binky Moon, LLC +// https://www.iana.org/domains/root/db/tours.html +tours + +// town : Binky Moon, LLC +// https://www.iana.org/domains/root/db/town.html +town + +// toyota : TOYOTA MOTOR CORPORATION +// https://www.iana.org/domains/root/db/toyota.html +toyota + +// toys : Binky Moon, LLC +// https://www.iana.org/domains/root/db/toys.html +toys + +// trade : Elite Registry Limited +// https://www.iana.org/domains/root/db/trade.html +trade + +// trading : Dog Beach, LLC +// https://www.iana.org/domains/root/db/trading.html +trading + +// training : Binky Moon, LLC +// https://www.iana.org/domains/root/db/training.html +training + +// travel : Dog Beach, LLC +// https://www.iana.org/domains/root/db/travel.html +travel + +// travelers : Travelers TLD, LLC +// https://www.iana.org/domains/root/db/travelers.html +travelers + +// travelersinsurance : Travelers TLD, LLC +// https://www.iana.org/domains/root/db/travelersinsurance.html +travelersinsurance + +// trust : Internet Naming Company LLC +// https://www.iana.org/domains/root/db/trust.html +trust + +// trv : Travelers TLD, LLC +// https://www.iana.org/domains/root/db/trv.html +trv + +// tube : Latin American Telecom LLC +// https://www.iana.org/domains/root/db/tube.html +tube + +// tui : TUI AG +// https://www.iana.org/domains/root/db/tui.html +tui + +// tunes : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/tunes.html +tunes + +// tushu : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/tushu.html +tushu + +// tvs : T V SUNDRAM IYENGAR & SONS LIMITED +// https://www.iana.org/domains/root/db/tvs.html +tvs + +// ubank : National Australia Bank Limited +// https://www.iana.org/domains/root/db/ubank.html +ubank + +// ubs : UBS AG +// https://www.iana.org/domains/root/db/ubs.html +ubs + +// unicom : China United Network Communications Corporation Limited +// https://www.iana.org/domains/root/db/unicom.html +unicom + +// university : Binky Moon, LLC +// https://www.iana.org/domains/root/db/university.html +university + +// uno : Radix Technologies Inc. +// https://www.iana.org/domains/root/db/uno.html +uno + +// uol : UBN INTERNET LTDA. +// https://www.iana.org/domains/root/db/uol.html +uol + +// ups : UPS Market Driver, Inc. +// https://www.iana.org/domains/root/db/ups.html +ups + +// vacations : Binky Moon, LLC +// https://www.iana.org/domains/root/db/vacations.html +vacations + +// vana : Internet Naming Company LLC +// https://www.iana.org/domains/root/db/vana.html +vana + +// vanguard : The Vanguard Group, Inc. +// https://www.iana.org/domains/root/db/vanguard.html +vanguard + +// vegas : Dot Vegas, Inc. +// https://www.iana.org/domains/root/db/vegas.html +vegas + +// ventures : Binky Moon, LLC +// https://www.iana.org/domains/root/db/ventures.html +ventures + +// verisign : VeriSign, Inc. +// https://www.iana.org/domains/root/db/verisign.html +verisign + +// versicherung : tldbox GmbH +// https://www.iana.org/domains/root/db/versicherung.html +versicherung + +// vet : Dog Beach, LLC +// https://www.iana.org/domains/root/db/vet.html +vet + +// viajes : Binky Moon, LLC +// https://www.iana.org/domains/root/db/viajes.html +viajes + +// video : Dog Beach, LLC +// https://www.iana.org/domains/root/db/video.html +video + +// vig : VIENNA INSURANCE GROUP AG Wiener Versicherung Gruppe +// https://www.iana.org/domains/root/db/vig.html +vig + +// viking : Viking River Cruises (Bermuda) Ltd. +// https://www.iana.org/domains/root/db/viking.html +viking + +// villas : Binky Moon, LLC +// https://www.iana.org/domains/root/db/villas.html +villas + +// vin : Binky Moon, LLC +// https://www.iana.org/domains/root/db/vin.html +vin + +// vip : Registry Services, LLC +// https://www.iana.org/domains/root/db/vip.html +vip + +// virgin : Virgin Enterprises Limited +// https://www.iana.org/domains/root/db/virgin.html +virgin + +// visa : Visa Worldwide Pte. Limited +// https://www.iana.org/domains/root/db/visa.html +visa + +// vision : Binky Moon, LLC +// https://www.iana.org/domains/root/db/vision.html +vision + +// viva : Saudi Telecom Company +// https://www.iana.org/domains/root/db/viva.html +viva + +// vivo : Telefonica Brasil S.A. +// https://www.iana.org/domains/root/db/vivo.html +vivo + +// vlaanderen : DNS.be vzw +// https://www.iana.org/domains/root/db/vlaanderen.html +vlaanderen + +// vodka : Registry Services, LLC +// https://www.iana.org/domains/root/db/vodka.html +vodka + +// volvo : Volvo Holding Sverige Aktiebolag +// https://www.iana.org/domains/root/db/volvo.html +volvo + +// vote : Monolith Registry LLC +// https://www.iana.org/domains/root/db/vote.html +vote + +// voting : Valuetainment Corp. +// https://www.iana.org/domains/root/db/voting.html +voting + +// voto : Monolith Registry LLC +// https://www.iana.org/domains/root/db/voto.html +voto + +// voyage : Binky Moon, LLC +// https://www.iana.org/domains/root/db/voyage.html +voyage + +// wales : Nominet UK +// https://www.iana.org/domains/root/db/wales.html +wales + +// walmart : Wal-Mart Stores, Inc. +// https://www.iana.org/domains/root/db/walmart.html +walmart + +// walter : Sandvik AB +// https://www.iana.org/domains/root/db/walter.html +walter + +// wang : Zodiac Wang Limited +// https://www.iana.org/domains/root/db/wang.html +wang + +// wanggou : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/wanggou.html +wanggou + +// watch : Binky Moon, LLC +// https://www.iana.org/domains/root/db/watch.html +watch + +// watches : Identity Digital Limited +// https://www.iana.org/domains/root/db/watches.html +watches + +// weather : International Business Machines Corporation +// https://www.iana.org/domains/root/db/weather.html +weather + +// weatherchannel : International Business Machines Corporation +// https://www.iana.org/domains/root/db/weatherchannel.html +weatherchannel + +// webcam : dot Webcam Limited +// https://www.iana.org/domains/root/db/webcam.html +webcam + +// weber : Saint-Gobain Weber SA +// https://www.iana.org/domains/root/db/weber.html +weber + +// website : Radix Technologies Inc. +// https://www.iana.org/domains/root/db/website.html +website + +// wed +// https://www.iana.org/domains/root/db/wed.html +wed + +// wedding : Registry Services, LLC +// https://www.iana.org/domains/root/db/wedding.html +wedding + +// weibo : Sina Corporation +// https://www.iana.org/domains/root/db/weibo.html +weibo + +// weir : Weir Group IP Limited +// https://www.iana.org/domains/root/db/weir.html +weir + +// whoswho : Who's Who Registry +// https://www.iana.org/domains/root/db/whoswho.html +whoswho + +// wien : punkt.wien GmbH +// https://www.iana.org/domains/root/db/wien.html +wien + +// wiki : Registry Services, LLC +// https://www.iana.org/domains/root/db/wiki.html +wiki + +// williamhill : William Hill Organization Limited +// https://www.iana.org/domains/root/db/williamhill.html +williamhill + +// win : First Registry Limited +// https://www.iana.org/domains/root/db/win.html +win + +// windows : Microsoft Corporation +// https://www.iana.org/domains/root/db/windows.html +windows + +// wine : Binky Moon, LLC +// https://www.iana.org/domains/root/db/wine.html +wine + +// winners : The TJX Companies, Inc. +// https://www.iana.org/domains/root/db/winners.html +winners + +// wme : William Morris Endeavor Entertainment, LLC +// https://www.iana.org/domains/root/db/wme.html +wme + +// wolterskluwer : Wolters Kluwer N.V. +// https://www.iana.org/domains/root/db/wolterskluwer.html +wolterskluwer + +// woodside : Woodside Petroleum Limited +// https://www.iana.org/domains/root/db/woodside.html +woodside + +// work : Registry Services, LLC +// https://www.iana.org/domains/root/db/work.html +work + +// works : Binky Moon, LLC +// https://www.iana.org/domains/root/db/works.html +works + +// world : Binky Moon, LLC +// https://www.iana.org/domains/root/db/world.html +world + +// wow : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/wow.html +wow + +// wtc : World Trade Centers Association, Inc. +// https://www.iana.org/domains/root/db/wtc.html +wtc + +// wtf : Binky Moon, LLC +// https://www.iana.org/domains/root/db/wtf.html +wtf + +// xbox : Microsoft Corporation +// https://www.iana.org/domains/root/db/xbox.html +xbox + +// xerox : Xerox DNHC LLC +// https://www.iana.org/domains/root/db/xerox.html +xerox + +// xihuan : Beijing Qihu Keji Co., Ltd. +// https://www.iana.org/domains/root/db/xihuan.html +xihuan + +// xin : Elegant Leader Limited +// https://www.iana.org/domains/root/db/xin.html +xin + +// xn--11b4c3d : VeriSign Sarl +// https://www.iana.org/domains/root/db/xn--11b4c3d.html +कॉम + +// xn--1ck2e1b : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/xn--1ck2e1b.html +セール + +// xn--1qqw23a : Guangzhou YU Wei Information Technology Co., Ltd. +// https://www.iana.org/domains/root/db/xn--1qqw23a.html +佛山 + +// xn--30rr7y : Excellent First Limited +// https://www.iana.org/domains/root/db/xn--30rr7y.html +慈善 + +// xn--3bst00m : Eagle Horizon Limited +// https://www.iana.org/domains/root/db/xn--3bst00m.html +集团 + +// xn--3ds443g : TLD REGISTRY LIMITED OY +// https://www.iana.org/domains/root/db/xn--3ds443g.html +在线 + +// xn--3pxu8k : VeriSign Sarl +// https://www.iana.org/domains/root/db/xn--3pxu8k.html +点看 + +// xn--42c2d9a : VeriSign Sarl +// https://www.iana.org/domains/root/db/xn--42c2d9a.html +คอม + +// xn--45q11c : Zodiac Gemini Ltd +// https://www.iana.org/domains/root/db/xn--45q11c.html +八卦 + +// xn--4gbrim : Helium TLDs Ltd +// https://www.iana.org/domains/root/db/xn--4gbrim.html +موقع + +// xn--55qw42g : China Organizational Name Administration Center +// https://www.iana.org/domains/root/db/xn--55qw42g.html +公益 + +// xn--55qx5d : China Internet Network Information Center (CNNIC) +// https://www.iana.org/domains/root/db/xn--55qx5d.html +公司 + +// xn--5su34j936bgsg : Shangri‐La International Hotel Management Limited +// https://www.iana.org/domains/root/db/xn--5su34j936bgsg.html +香格里拉 + +// xn--5tzm5g : Global Website TLD Asia Limited +// https://www.iana.org/domains/root/db/xn--5tzm5g.html +网站 + +// xn--6frz82g : Identity Digital Limited +// https://www.iana.org/domains/root/db/xn--6frz82g.html +移动 + +// xn--6qq986b3xl : Tycoon Treasure Limited +// https://www.iana.org/domains/root/db/xn--6qq986b3xl.html +我爱你 + +// xn--80adxhks : Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID) +// https://www.iana.org/domains/root/db/xn--80adxhks.html +москва + +// xn--80aqecdr1a : Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication) +// https://www.iana.org/domains/root/db/xn--80aqecdr1a.html +католик + +// xn--80asehdb : CORE Association +// https://www.iana.org/domains/root/db/xn--80asehdb.html +онлайн + +// xn--80aswg : CORE Association +// https://www.iana.org/domains/root/db/xn--80aswg.html +сайт + +// xn--8y0a063a : China United Network Communications Corporation Limited +// https://www.iana.org/domains/root/db/xn--8y0a063a.html +联通 + +// xn--9dbq2a : VeriSign Sarl +// https://www.iana.org/domains/root/db/xn--9dbq2a.html +קום + +// xn--9et52u : RISE VICTORY LIMITED +// https://www.iana.org/domains/root/db/xn--9et52u.html +时尚 + +// xn--9krt00a : Sina Corporation +// https://www.iana.org/domains/root/db/xn--9krt00a.html +微博 + +// xn--b4w605ferd : Temasek Holdings (Private) Limited +// https://www.iana.org/domains/root/db/xn--b4w605ferd.html +淡马锡 + +// xn--bck1b9a5dre4c : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/xn--bck1b9a5dre4c.html +ファッション + +// xn--c1avg : Public Interest Registry +// https://www.iana.org/domains/root/db/xn--c1avg.html +орг + +// xn--c2br7g : VeriSign Sarl +// https://www.iana.org/domains/root/db/xn--c2br7g.html +नेट + +// xn--cck2b3b : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/xn--cck2b3b.html +ストア + +// xn--cckwcxetd : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/xn--cckwcxetd.html +アマゾン + +// xn--cg4bki : SAMSUNG SDS CO., LTD +// https://www.iana.org/domains/root/db/xn--cg4bki.html +삼성 + +// xn--czr694b : Internet DotTrademark Organisation Limited +// https://www.iana.org/domains/root/db/xn--czr694b.html +商标 + +// xn--czrs0t : Binky Moon, LLC +// https://www.iana.org/domains/root/db/xn--czrs0t.html +商店 + +// xn--czru2d : Zodiac Aquarius Limited +// https://www.iana.org/domains/root/db/xn--czru2d.html +商城 + +// xn--d1acj3b : The Foundation for Network Initiatives “The Smart Internet” +// https://www.iana.org/domains/root/db/xn--d1acj3b.html +дети + +// xn--eckvdtc9d : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/xn--eckvdtc9d.html +ポイント + +// xn--efvy88h : Guangzhou YU Wei Information Technology Co., Ltd. +// https://www.iana.org/domains/root/db/xn--efvy88h.html +新闻 + +// xn--fct429k : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/xn--fct429k.html +家電 + +// xn--fhbei : VeriSign Sarl +// https://www.iana.org/domains/root/db/xn--fhbei.html +كوم + +// xn--fiq228c5hs : TLD REGISTRY LIMITED OY +// https://www.iana.org/domains/root/db/xn--fiq228c5hs.html +中文网 + +// xn--fiq64b : CITIC Group Corporation +// https://www.iana.org/domains/root/db/xn--fiq64b.html +中信 + +// xn--fjq720a : Binky Moon, LLC +// https://www.iana.org/domains/root/db/xn--fjq720a.html +娱乐 + +// xn--flw351e : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/xn--flw351e.html +谷歌 + +// xn--fzys8d69uvgm : PCCW Enterprises Limited +// https://www.iana.org/domains/root/db/xn--fzys8d69uvgm.html +電訊盈科 + +// xn--g2xx48c : Nawang Heli(Xiamen) Network Service Co., LTD. +// https://www.iana.org/domains/root/db/xn--g2xx48c.html +购物 + +// xn--gckr3f0f : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/xn--gckr3f0f.html +クラウド + +// xn--gk3at1e : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/xn--gk3at1e.html +通販 + +// xn--hxt814e : Zodiac Taurus Limited +// https://www.iana.org/domains/root/db/xn--hxt814e.html +网店 + +// xn--i1b6b1a6a2e : Public Interest Registry +// https://www.iana.org/domains/root/db/xn--i1b6b1a6a2e.html +संगठन + +// xn--imr513n : Internet DotTrademark Organisation Limited +// https://www.iana.org/domains/root/db/xn--imr513n.html +餐厅 + +// xn--io0a7i : China Internet Network Information Center (CNNIC) +// https://www.iana.org/domains/root/db/xn--io0a7i.html +网络 + +// xn--j1aef : VeriSign Sarl +// https://www.iana.org/domains/root/db/xn--j1aef.html +ком + +// xn--jlq480n2rg : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/xn--jlq480n2rg.html +亚马逊 + +// xn--jvr189m : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/xn--jvr189m.html +食品 + +// xn--kcrx77d1x4a : Koninklijke Philips N.V. +// https://www.iana.org/domains/root/db/xn--kcrx77d1x4a.html +飞利浦 + +// xn--kput3i : Beijing RITT-Net Technology Development Co., Ltd +// https://www.iana.org/domains/root/db/xn--kput3i.html +手机 + +// xn--mgba3a3ejt : Aramco Services Company +// https://www.iana.org/domains/root/db/xn--mgba3a3ejt.html +ارامكو + +// xn--mgba7c0bbn0a : Competrol (Luxembourg) Sarl +// https://www.iana.org/domains/root/db/xn--mgba7c0bbn0a.html +العليان + +// xn--mgbab2bd : CORE Association +// https://www.iana.org/domains/root/db/xn--mgbab2bd.html +بازار + +// xn--mgbca7dzdo : Abu Dhabi Systems and Information Centre +// https://www.iana.org/domains/root/db/xn--mgbca7dzdo.html +ابوظبي + +// xn--mgbi4ecexp : Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication) +// https://www.iana.org/domains/root/db/xn--mgbi4ecexp.html +كاثوليك + +// xn--mgbt3dhd : Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. +// https://www.iana.org/domains/root/db/xn--mgbt3dhd.html +همراه + +// xn--mk1bu44c : VeriSign Sarl +// https://www.iana.org/domains/root/db/xn--mk1bu44c.html +닷컴 + +// xn--mxtq1m : Net-Chinese Co., Ltd. +// https://www.iana.org/domains/root/db/xn--mxtq1m.html +政府 + +// xn--ngbc5azd : International Domain Registry Pty. Ltd. +// https://www.iana.org/domains/root/db/xn--ngbc5azd.html +شبكة + +// xn--ngbe9e0a : Kuwait Finance House +// https://www.iana.org/domains/root/db/xn--ngbe9e0a.html +بيتك + +// xn--ngbrx : League of Arab States +// https://www.iana.org/domains/root/db/xn--ngbrx.html +عرب + +// xn--nqv7f : Public Interest Registry +// https://www.iana.org/domains/root/db/xn--nqv7f.html +机构 + +// xn--nqv7fs00ema : Public Interest Registry +// https://www.iana.org/domains/root/db/xn--nqv7fs00ema.html +组织机构 + +// xn--nyqy26a : Stable Tone Limited +// https://www.iana.org/domains/root/db/xn--nyqy26a.html +健康 + +// xn--otu796d : Jiang Yu Liang Cai Technology Company Limited +// https://www.iana.org/domains/root/db/xn--otu796d.html +招聘 + +// xn--p1acf : Rusnames Limited +// https://www.iana.org/domains/root/db/xn--p1acf.html +рус + +// xn--pssy2u : VeriSign Sarl +// https://www.iana.org/domains/root/db/xn--pssy2u.html +大拿 + +// xn--q9jyb4c : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/xn--q9jyb4c.html +みんな + +// xn--qcka1pmc : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/xn--qcka1pmc.html +グーグル + +// xn--rhqv96g : Stable Tone Limited +// https://www.iana.org/domains/root/db/xn--rhqv96g.html +世界 + +// xn--rovu88b : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/xn--rovu88b.html +書籍 + +// xn--ses554g : KNET Co., Ltd. +// https://www.iana.org/domains/root/db/xn--ses554g.html +网址 + +// xn--t60b56a : VeriSign Sarl +// https://www.iana.org/domains/root/db/xn--t60b56a.html +닷넷 + +// xn--tckwe : VeriSign Sarl +// https://www.iana.org/domains/root/db/xn--tckwe.html +コム + +// xn--tiq49xqyj : Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication) +// https://www.iana.org/domains/root/db/xn--tiq49xqyj.html +天主教 + +// xn--unup4y : Binky Moon, LLC +// https://www.iana.org/domains/root/db/xn--unup4y.html +游戏 + +// xn--vermgensberater-ctb : Deutsche Vermögensberatung Aktiengesellschaft DVAG +// https://www.iana.org/domains/root/db/xn--vermgensberater-ctb.html +vermögensberater + +// xn--vermgensberatung-pwb : Deutsche Vermögensberatung Aktiengesellschaft DVAG +// https://www.iana.org/domains/root/db/xn--vermgensberatung-pwb.html +vermögensberatung + +// xn--vhquv : Binky Moon, LLC +// https://www.iana.org/domains/root/db/xn--vhquv.html +企业 + +// xn--vuq861b : Beijing Tele-info Technology Co., Ltd. +// https://www.iana.org/domains/root/db/xn--vuq861b.html +信息 + +// xn--w4r85el8fhu5dnra : Kerry Trading Co. Limited +// https://www.iana.org/domains/root/db/xn--w4r85el8fhu5dnra.html +嘉里大酒店 + +// xn--w4rs40l : Kerry Trading Co. Limited +// https://www.iana.org/domains/root/db/xn--w4rs40l.html +嘉里 + +// xn--xhq521b : Guangzhou YU Wei Information Technology Co., Ltd. +// https://www.iana.org/domains/root/db/xn--xhq521b.html +广东 + +// xn--zfr164b : China Organizational Name Administration Center +// https://www.iana.org/domains/root/db/xn--zfr164b.html +政务 + +// xyz : XYZ.COM LLC +// https://www.iana.org/domains/root/db/xyz.html +xyz + +// yachts : XYZ.COM LLC +// https://www.iana.org/domains/root/db/yachts.html +yachts + +// yahoo : Oath Inc. +// https://www.iana.org/domains/root/db/yahoo.html +yahoo + +// yamaxun : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/yamaxun.html +yamaxun + +// yandex : Yandex Europe B.V. +// https://www.iana.org/domains/root/db/yandex.html +yandex + +// yodobashi : YODOBASHI CAMERA CO.,LTD. +// https://www.iana.org/domains/root/db/yodobashi.html +yodobashi + +// yoga : Registry Services, LLC +// https://www.iana.org/domains/root/db/yoga.html +yoga + +// yokohama : GMO Registry, Inc. +// https://www.iana.org/domains/root/db/yokohama.html +yokohama + +// you : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/you.html +you + +// youtube : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/youtube.html +youtube + +// yun : Beijing Qihu Keji Co., Ltd. +// https://www.iana.org/domains/root/db/yun.html +yun + +// zappos : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/zappos.html +zappos + +// zara : Industria de Diseño Textil, S.A. (INDITEX, S.A.) +// https://www.iana.org/domains/root/db/zara.html +zara + +// zero : Amazon Registry Services, Inc. +// https://www.iana.org/domains/root/db/zero.html +zero + +// zip : Charleston Road Registry Inc. +// https://www.iana.org/domains/root/db/zip.html +zip + +// zone : Binky Moon, LLC +// https://www.iana.org/domains/root/db/zone.html +zone + +// zuerich : Kanton Zürich (Canton of Zurich) +// https://www.iana.org/domains/root/db/zuerich.html +zuerich + + +// ===END ICANN DOMAINS=== +// ===BEGIN PRIVATE DOMAINS=== +// (Note: these are in alphabetical order by company name) + +// 12CHARS: https://12chars.com +// Submitted by Kenny Niehage <psl@12chars.com> +12chars.dev +12chars.it +12chars.pro + +// 1GB LLC : https://www.1gb.ua/ +// Submitted by 1GB LLC <noc@1gb.com.ua> +cc.ua +inf.ua +ltd.ua + +// 611coin : https://611project.org/ +611.to + +// A2 Hosting +// Submitted by Tyler Hall <sysadmin@a2hosting.com> +a2hosted.com +cpserver.com + +// Aaron Marais' Gitlab pages: https://lab.aaronleem.co.za +// Submitted by Aaron Marais <its_me@aaronleem.co.za> +graphox.us + +// accesso Technology Group, plc. : https://accesso.com/ +// Submitted by accesso Team <accessoecommerce@accesso.com> +*.devcdnaccesso.com + +// Acorn Labs : https://acorn.io +// Submitted by Craig Jellick <domains@acorn.io> +*.on-acorn.io + +// ActiveTrail: https://www.activetrail.biz/ +// Submitted by Ofer Kalaora <postmaster@activetrail.com> +activetrail.biz + +// Adaptable.io : https://adaptable.io +// Submitted by Mark Terrel <support@adaptable.io> +adaptable.app + +// Adobe : https://www.adobe.com/ +// Submitted by Ian Boston <boston@adobe.com> and Lars Trieloff <trieloff@adobe.com> +adobeaemcloud.com +*.dev.adobeaemcloud.com +aem.live +hlx.live +adobeaemcloud.net +aem.page +hlx.page +hlx3.page + +// Adobe Developer Platform : https://developer.adobe.com +// Submitted by Jesse MacFadyen<jessem@adobe.com> +adobeio-static.net +adobeioruntime.net + +// Agnat sp. z o.o. : https://domena.pl +// Submitted by Przemyslaw Plewa <it-admin@domena.pl> +beep.pl + +// Airkit : https://www.airkit.com/ +// Submitted by Grant Cooksey <security@airkit.com> +airkitapps.com +airkitapps-au.com +airkitapps.eu + +// Aiven: https://aiven.io/ +// Submitted by Etienne Stalmans <security@aiven.io> +aivencloud.com + +// Akamai : https://www.akamai.com/ +// Submitted by Akamai Team <publicsuffixlist@akamai.com> +akadns.net +akamai.net +akamai-staging.net +akamaiedge.net +akamaiedge-staging.net +akamaihd.net +akamaihd-staging.net +akamaiorigin.net +akamaiorigin-staging.net +akamaized.net +akamaized-staging.net +edgekey.net +edgekey-staging.net +edgesuite.net +edgesuite-staging.net + +// alboto.ca : http://alboto.ca +// Submitted by Anton Avramov <avramov@alboto.ca> +barsy.ca + +// Alces Software Ltd : http://alces-software.com +// Submitted by Mark J. Titorenko <mark.titorenko@alces-software.com> +*.compute.estate +*.alces.network + +// all-inkl.com : https://all-inkl.com +// Submitted by Werner Kaltofen <wk@all-inkl.com> +kasserver.com + +// Altervista: https://www.altervista.org +// Submitted by Carlo Cannas <tech_staff@altervista.it> +altervista.org + +// alwaysdata : https://www.alwaysdata.com +// Submitted by Cyril <admin@alwaysdata.com> +alwaysdata.net + +// Amaze Software : https://amaze.co +// Submitted by Domain Admin <domainadmin@amaze.co> +myamaze.net + +// Amazon : https://www.amazon.com/ +// Submitted by AWS Security <psl-maintainers@amazon.com> +// Subsections of Amazon/subsidiaries will appear until "concludes" tag + +// Amazon API Gateway +// Submitted by AWS Security <psl-maintainers@amazon.com> +// Reference: 9e37648f-a66c-4655-9ab1-5981f8737197 +execute-api.cn-north-1.amazonaws.com.cn +execute-api.cn-northwest-1.amazonaws.com.cn +execute-api.af-south-1.amazonaws.com +execute-api.ap-east-1.amazonaws.com +execute-api.ap-northeast-1.amazonaws.com +execute-api.ap-northeast-2.amazonaws.com +execute-api.ap-northeast-3.amazonaws.com +execute-api.ap-south-1.amazonaws.com +execute-api.ap-south-2.amazonaws.com +execute-api.ap-southeast-1.amazonaws.com +execute-api.ap-southeast-2.amazonaws.com +execute-api.ap-southeast-3.amazonaws.com +execute-api.ap-southeast-4.amazonaws.com +execute-api.ca-central-1.amazonaws.com +execute-api.ca-west-1.amazonaws.com +execute-api.eu-central-1.amazonaws.com +execute-api.eu-central-2.amazonaws.com +execute-api.eu-north-1.amazonaws.com +execute-api.eu-south-1.amazonaws.com +execute-api.eu-south-2.amazonaws.com +execute-api.eu-west-1.amazonaws.com +execute-api.eu-west-2.amazonaws.com +execute-api.eu-west-3.amazonaws.com +execute-api.il-central-1.amazonaws.com +execute-api.me-central-1.amazonaws.com +execute-api.me-south-1.amazonaws.com +execute-api.sa-east-1.amazonaws.com +execute-api.us-east-1.amazonaws.com +execute-api.us-east-2.amazonaws.com +execute-api.us-gov-east-1.amazonaws.com +execute-api.us-gov-west-1.amazonaws.com +execute-api.us-west-1.amazonaws.com +execute-api.us-west-2.amazonaws.com + +// Amazon CloudFront +// Submitted by Donavan Miller <donavanm@amazon.com> +// Reference: 54144616-fd49-4435-8535-19c6a601bdb3 +cloudfront.net + +// Amazon Cognito +// Submitted by AWS Security <psl-maintainers@amazon.com> +// Reference: 7bee1013-f456-47df-bfe8-03c78d946d61 +auth.af-south-1.amazoncognito.com +auth.ap-northeast-1.amazoncognito.com +auth.ap-northeast-2.amazoncognito.com +auth.ap-northeast-3.amazoncognito.com +auth.ap-south-1.amazoncognito.com +auth.ap-southeast-1.amazoncognito.com +auth.ap-southeast-2.amazoncognito.com +auth.ap-southeast-3.amazoncognito.com +auth.ca-central-1.amazoncognito.com +auth.eu-central-1.amazoncognito.com +auth.eu-north-1.amazoncognito.com +auth.eu-south-1.amazoncognito.com +auth.eu-west-1.amazoncognito.com +auth.eu-west-2.amazoncognito.com +auth.eu-west-3.amazoncognito.com +auth.il-central-1.amazoncognito.com +auth.me-south-1.amazoncognito.com +auth.sa-east-1.amazoncognito.com +auth.us-east-1.amazoncognito.com +auth-fips.us-east-1.amazoncognito.com +auth.us-east-2.amazoncognito.com +auth-fips.us-east-2.amazoncognito.com +auth-fips.us-gov-west-1.amazoncognito.com +auth.us-west-1.amazoncognito.com +auth-fips.us-west-1.amazoncognito.com +auth.us-west-2.amazoncognito.com +auth-fips.us-west-2.amazoncognito.com + +// Amazon EC2 +// Submitted by Luke Wells <psl-maintainers@amazon.com> +// Reference: 4c38fa71-58ac-4768-99e5-689c1767e537 +*.compute.amazonaws.com +*.compute-1.amazonaws.com +*.compute.amazonaws.com.cn +us-east-1.amazonaws.com + +// Amazon EMR +// Submitted by AWS Security <psl-maintainers@amazon.com> +// Reference: 597f3f8e-9283-4e48-8e32-7ee25a1ff6ab +emrappui-prod.cn-north-1.amazonaws.com.cn +emrnotebooks-prod.cn-north-1.amazonaws.com.cn +emrstudio-prod.cn-north-1.amazonaws.com.cn +emrappui-prod.cn-northwest-1.amazonaws.com.cn +emrnotebooks-prod.cn-northwest-1.amazonaws.com.cn +emrstudio-prod.cn-northwest-1.amazonaws.com.cn +emrappui-prod.af-south-1.amazonaws.com +emrnotebooks-prod.af-south-1.amazonaws.com +emrstudio-prod.af-south-1.amazonaws.com +emrappui-prod.ap-east-1.amazonaws.com +emrnotebooks-prod.ap-east-1.amazonaws.com +emrstudio-prod.ap-east-1.amazonaws.com +emrappui-prod.ap-northeast-1.amazonaws.com +emrnotebooks-prod.ap-northeast-1.amazonaws.com +emrstudio-prod.ap-northeast-1.amazonaws.com +emrappui-prod.ap-northeast-2.amazonaws.com +emrnotebooks-prod.ap-northeast-2.amazonaws.com +emrstudio-prod.ap-northeast-2.amazonaws.com +emrappui-prod.ap-northeast-3.amazonaws.com +emrnotebooks-prod.ap-northeast-3.amazonaws.com +emrstudio-prod.ap-northeast-3.amazonaws.com +emrappui-prod.ap-south-1.amazonaws.com +emrnotebooks-prod.ap-south-1.amazonaws.com +emrstudio-prod.ap-south-1.amazonaws.com +emrappui-prod.ap-southeast-1.amazonaws.com +emrnotebooks-prod.ap-southeast-1.amazonaws.com +emrstudio-prod.ap-southeast-1.amazonaws.com +emrappui-prod.ap-southeast-2.amazonaws.com +emrnotebooks-prod.ap-southeast-2.amazonaws.com +emrstudio-prod.ap-southeast-2.amazonaws.com +emrappui-prod.ap-southeast-3.amazonaws.com +emrnotebooks-prod.ap-southeast-3.amazonaws.com +emrstudio-prod.ap-southeast-3.amazonaws.com +emrappui-prod.ca-central-1.amazonaws.com +emrnotebooks-prod.ca-central-1.amazonaws.com +emrstudio-prod.ca-central-1.amazonaws.com +emrappui-prod.eu-central-1.amazonaws.com +emrnotebooks-prod.eu-central-1.amazonaws.com +emrstudio-prod.eu-central-1.amazonaws.com +emrappui-prod.eu-north-1.amazonaws.com +emrnotebooks-prod.eu-north-1.amazonaws.com +emrstudio-prod.eu-north-1.amazonaws.com +emrappui-prod.eu-south-1.amazonaws.com +emrnotebooks-prod.eu-south-1.amazonaws.com +emrstudio-prod.eu-south-1.amazonaws.com +emrappui-prod.eu-west-1.amazonaws.com +emrnotebooks-prod.eu-west-1.amazonaws.com +emrstudio-prod.eu-west-1.amazonaws.com +emrappui-prod.eu-west-2.amazonaws.com +emrnotebooks-prod.eu-west-2.amazonaws.com +emrstudio-prod.eu-west-2.amazonaws.com +emrappui-prod.eu-west-3.amazonaws.com +emrnotebooks-prod.eu-west-3.amazonaws.com +emrstudio-prod.eu-west-3.amazonaws.com +emrappui-prod.me-central-1.amazonaws.com +emrnotebooks-prod.me-central-1.amazonaws.com +emrstudio-prod.me-central-1.amazonaws.com +emrappui-prod.me-south-1.amazonaws.com +emrnotebooks-prod.me-south-1.amazonaws.com +emrstudio-prod.me-south-1.amazonaws.com +emrappui-prod.sa-east-1.amazonaws.com +emrnotebooks-prod.sa-east-1.amazonaws.com +emrstudio-prod.sa-east-1.amazonaws.com +emrappui-prod.us-east-1.amazonaws.com +emrnotebooks-prod.us-east-1.amazonaws.com +emrstudio-prod.us-east-1.amazonaws.com +emrappui-prod.us-east-2.amazonaws.com +emrnotebooks-prod.us-east-2.amazonaws.com +emrstudio-prod.us-east-2.amazonaws.com +emrappui-prod.us-gov-east-1.amazonaws.com +emrnotebooks-prod.us-gov-east-1.amazonaws.com +emrstudio-prod.us-gov-east-1.amazonaws.com +emrappui-prod.us-gov-west-1.amazonaws.com +emrnotebooks-prod.us-gov-west-1.amazonaws.com +emrstudio-prod.us-gov-west-1.amazonaws.com +emrappui-prod.us-west-1.amazonaws.com +emrnotebooks-prod.us-west-1.amazonaws.com +emrstudio-prod.us-west-1.amazonaws.com +emrappui-prod.us-west-2.amazonaws.com +emrnotebooks-prod.us-west-2.amazonaws.com +emrstudio-prod.us-west-2.amazonaws.com + +// Amazon Managed Workflows for Apache Airflow +// Submitted by AWS Security <psl-maintainers@amazon.com> +// Reference: 4ab55e6f-90c0-4a8d-b6a0-52ca5dbb1c2e +*.cn-north-1.airflow.amazonaws.com.cn +*.cn-northwest-1.airflow.amazonaws.com.cn +*.ap-northeast-1.airflow.amazonaws.com +*.ap-northeast-2.airflow.amazonaws.com +*.ap-south-1.airflow.amazonaws.com +*.ap-southeast-1.airflow.amazonaws.com +*.ap-southeast-2.airflow.amazonaws.com +*.ca-central-1.airflow.amazonaws.com +*.eu-central-1.airflow.amazonaws.com +*.eu-north-1.airflow.amazonaws.com +*.eu-west-1.airflow.amazonaws.com +*.eu-west-2.airflow.amazonaws.com +*.eu-west-3.airflow.amazonaws.com +*.sa-east-1.airflow.amazonaws.com +*.us-east-1.airflow.amazonaws.com +*.us-east-2.airflow.amazonaws.com +*.us-west-2.airflow.amazonaws.com + +// Amazon S3 +// Submitted by AWS Security <psl-maintainers@amazon.com> +// Reference: cd5c8b3a-67b7-4b40-9236-c87ce81a3d10 +s3.dualstack.cn-north-1.amazonaws.com.cn +s3-accesspoint.dualstack.cn-north-1.amazonaws.com.cn +s3-website.dualstack.cn-north-1.amazonaws.com.cn +s3.cn-north-1.amazonaws.com.cn +s3-accesspoint.cn-north-1.amazonaws.com.cn +s3-deprecated.cn-north-1.amazonaws.com.cn +s3-object-lambda.cn-north-1.amazonaws.com.cn +s3-website.cn-north-1.amazonaws.com.cn +s3.dualstack.cn-northwest-1.amazonaws.com.cn +s3-accesspoint.dualstack.cn-northwest-1.amazonaws.com.cn +s3.cn-northwest-1.amazonaws.com.cn +s3-accesspoint.cn-northwest-1.amazonaws.com.cn +s3-object-lambda.cn-northwest-1.amazonaws.com.cn +s3-website.cn-northwest-1.amazonaws.com.cn +s3.dualstack.af-south-1.amazonaws.com +s3-accesspoint.dualstack.af-south-1.amazonaws.com +s3-website.dualstack.af-south-1.amazonaws.com +s3.af-south-1.amazonaws.com +s3-accesspoint.af-south-1.amazonaws.com +s3-object-lambda.af-south-1.amazonaws.com +s3-website.af-south-1.amazonaws.com +s3.dualstack.ap-east-1.amazonaws.com +s3-accesspoint.dualstack.ap-east-1.amazonaws.com +s3.ap-east-1.amazonaws.com +s3-accesspoint.ap-east-1.amazonaws.com +s3-object-lambda.ap-east-1.amazonaws.com +s3-website.ap-east-1.amazonaws.com +s3.dualstack.ap-northeast-1.amazonaws.com +s3-accesspoint.dualstack.ap-northeast-1.amazonaws.com +s3-website.dualstack.ap-northeast-1.amazonaws.com +s3.ap-northeast-1.amazonaws.com +s3-accesspoint.ap-northeast-1.amazonaws.com +s3-object-lambda.ap-northeast-1.amazonaws.com +s3-website.ap-northeast-1.amazonaws.com +s3.dualstack.ap-northeast-2.amazonaws.com +s3-accesspoint.dualstack.ap-northeast-2.amazonaws.com +s3-website.dualstack.ap-northeast-2.amazonaws.com +s3.ap-northeast-2.amazonaws.com +s3-accesspoint.ap-northeast-2.amazonaws.com +s3-object-lambda.ap-northeast-2.amazonaws.com +s3-website.ap-northeast-2.amazonaws.com +s3.dualstack.ap-northeast-3.amazonaws.com +s3-accesspoint.dualstack.ap-northeast-3.amazonaws.com +s3-website.dualstack.ap-northeast-3.amazonaws.com +s3.ap-northeast-3.amazonaws.com +s3-accesspoint.ap-northeast-3.amazonaws.com +s3-object-lambda.ap-northeast-3.amazonaws.com +s3-website.ap-northeast-3.amazonaws.com +s3.dualstack.ap-south-1.amazonaws.com +s3-accesspoint.dualstack.ap-south-1.amazonaws.com +s3-website.dualstack.ap-south-1.amazonaws.com +s3.ap-south-1.amazonaws.com +s3-accesspoint.ap-south-1.amazonaws.com +s3-object-lambda.ap-south-1.amazonaws.com +s3-website.ap-south-1.amazonaws.com +s3.dualstack.ap-south-2.amazonaws.com +s3-accesspoint.dualstack.ap-south-2.amazonaws.com +s3.ap-south-2.amazonaws.com +s3-accesspoint.ap-south-2.amazonaws.com +s3-object-lambda.ap-south-2.amazonaws.com +s3-website.ap-south-2.amazonaws.com +s3.dualstack.ap-southeast-1.amazonaws.com +s3-accesspoint.dualstack.ap-southeast-1.amazonaws.com +s3-website.dualstack.ap-southeast-1.amazonaws.com +s3.ap-southeast-1.amazonaws.com +s3-accesspoint.ap-southeast-1.amazonaws.com +s3-object-lambda.ap-southeast-1.amazonaws.com +s3-website.ap-southeast-1.amazonaws.com +s3.dualstack.ap-southeast-2.amazonaws.com +s3-accesspoint.dualstack.ap-southeast-2.amazonaws.com +s3-website.dualstack.ap-southeast-2.amazonaws.com +s3.ap-southeast-2.amazonaws.com +s3-accesspoint.ap-southeast-2.amazonaws.com +s3-object-lambda.ap-southeast-2.amazonaws.com +s3-website.ap-southeast-2.amazonaws.com +s3.dualstack.ap-southeast-3.amazonaws.com +s3-accesspoint.dualstack.ap-southeast-3.amazonaws.com +s3.ap-southeast-3.amazonaws.com +s3-accesspoint.ap-southeast-3.amazonaws.com +s3-object-lambda.ap-southeast-3.amazonaws.com +s3-website.ap-southeast-3.amazonaws.com +s3.dualstack.ap-southeast-4.amazonaws.com +s3-accesspoint.dualstack.ap-southeast-4.amazonaws.com +s3.ap-southeast-4.amazonaws.com +s3-accesspoint.ap-southeast-4.amazonaws.com +s3-object-lambda.ap-southeast-4.amazonaws.com +s3-website.ap-southeast-4.amazonaws.com +s3.dualstack.ca-central-1.amazonaws.com +s3-accesspoint.dualstack.ca-central-1.amazonaws.com +s3-accesspoint-fips.dualstack.ca-central-1.amazonaws.com +s3-fips.dualstack.ca-central-1.amazonaws.com +s3-website.dualstack.ca-central-1.amazonaws.com +s3.ca-central-1.amazonaws.com +s3-accesspoint.ca-central-1.amazonaws.com +s3-accesspoint-fips.ca-central-1.amazonaws.com +s3-fips.ca-central-1.amazonaws.com +s3-object-lambda.ca-central-1.amazonaws.com +s3-website.ca-central-1.amazonaws.com +s3.dualstack.ca-west-1.amazonaws.com +s3-accesspoint.dualstack.ca-west-1.amazonaws.com +s3-accesspoint-fips.dualstack.ca-west-1.amazonaws.com +s3-fips.dualstack.ca-west-1.amazonaws.com +s3-website.dualstack.ca-west-1.amazonaws.com +s3.ca-west-1.amazonaws.com +s3-accesspoint.ca-west-1.amazonaws.com +s3-accesspoint-fips.ca-west-1.amazonaws.com +s3-fips.ca-west-1.amazonaws.com +s3-website.ca-west-1.amazonaws.com +s3.dualstack.eu-central-1.amazonaws.com +s3-accesspoint.dualstack.eu-central-1.amazonaws.com +s3-website.dualstack.eu-central-1.amazonaws.com +s3.eu-central-1.amazonaws.com +s3-accesspoint.eu-central-1.amazonaws.com +s3-object-lambda.eu-central-1.amazonaws.com +s3-website.eu-central-1.amazonaws.com +s3.dualstack.eu-central-2.amazonaws.com +s3-accesspoint.dualstack.eu-central-2.amazonaws.com +s3.eu-central-2.amazonaws.com +s3-accesspoint.eu-central-2.amazonaws.com +s3-object-lambda.eu-central-2.amazonaws.com +s3-website.eu-central-2.amazonaws.com +s3.dualstack.eu-north-1.amazonaws.com +s3-accesspoint.dualstack.eu-north-1.amazonaws.com +s3.eu-north-1.amazonaws.com +s3-accesspoint.eu-north-1.amazonaws.com +s3-object-lambda.eu-north-1.amazonaws.com +s3-website.eu-north-1.amazonaws.com +s3.dualstack.eu-south-1.amazonaws.com +s3-accesspoint.dualstack.eu-south-1.amazonaws.com +s3-website.dualstack.eu-south-1.amazonaws.com +s3.eu-south-1.amazonaws.com +s3-accesspoint.eu-south-1.amazonaws.com +s3-object-lambda.eu-south-1.amazonaws.com +s3-website.eu-south-1.amazonaws.com +s3.dualstack.eu-south-2.amazonaws.com +s3-accesspoint.dualstack.eu-south-2.amazonaws.com +s3.eu-south-2.amazonaws.com +s3-accesspoint.eu-south-2.amazonaws.com +s3-object-lambda.eu-south-2.amazonaws.com +s3-website.eu-south-2.amazonaws.com +s3.dualstack.eu-west-1.amazonaws.com +s3-accesspoint.dualstack.eu-west-1.amazonaws.com +s3-website.dualstack.eu-west-1.amazonaws.com +s3.eu-west-1.amazonaws.com +s3-accesspoint.eu-west-1.amazonaws.com +s3-deprecated.eu-west-1.amazonaws.com +s3-object-lambda.eu-west-1.amazonaws.com +s3-website.eu-west-1.amazonaws.com +s3.dualstack.eu-west-2.amazonaws.com +s3-accesspoint.dualstack.eu-west-2.amazonaws.com +s3.eu-west-2.amazonaws.com +s3-accesspoint.eu-west-2.amazonaws.com +s3-object-lambda.eu-west-2.amazonaws.com +s3-website.eu-west-2.amazonaws.com +s3.dualstack.eu-west-3.amazonaws.com +s3-accesspoint.dualstack.eu-west-3.amazonaws.com +s3-website.dualstack.eu-west-3.amazonaws.com +s3.eu-west-3.amazonaws.com +s3-accesspoint.eu-west-3.amazonaws.com +s3-object-lambda.eu-west-3.amazonaws.com +s3-website.eu-west-3.amazonaws.com +s3.dualstack.il-central-1.amazonaws.com +s3-accesspoint.dualstack.il-central-1.amazonaws.com +s3.il-central-1.amazonaws.com +s3-accesspoint.il-central-1.amazonaws.com +s3-object-lambda.il-central-1.amazonaws.com +s3-website.il-central-1.amazonaws.com +s3.dualstack.me-central-1.amazonaws.com +s3-accesspoint.dualstack.me-central-1.amazonaws.com +s3.me-central-1.amazonaws.com +s3-accesspoint.me-central-1.amazonaws.com +s3-object-lambda.me-central-1.amazonaws.com +s3-website.me-central-1.amazonaws.com +s3.dualstack.me-south-1.amazonaws.com +s3-accesspoint.dualstack.me-south-1.amazonaws.com +s3.me-south-1.amazonaws.com +s3-accesspoint.me-south-1.amazonaws.com +s3-object-lambda.me-south-1.amazonaws.com +s3-website.me-south-1.amazonaws.com +s3.amazonaws.com +s3-1.amazonaws.com +s3-ap-east-1.amazonaws.com +s3-ap-northeast-1.amazonaws.com +s3-ap-northeast-2.amazonaws.com +s3-ap-northeast-3.amazonaws.com +s3-ap-south-1.amazonaws.com +s3-ap-southeast-1.amazonaws.com +s3-ap-southeast-2.amazonaws.com +s3-ca-central-1.amazonaws.com +s3-eu-central-1.amazonaws.com +s3-eu-north-1.amazonaws.com +s3-eu-west-1.amazonaws.com +s3-eu-west-2.amazonaws.com +s3-eu-west-3.amazonaws.com +s3-external-1.amazonaws.com +s3-fips-us-gov-east-1.amazonaws.com +s3-fips-us-gov-west-1.amazonaws.com +mrap.accesspoint.s3-global.amazonaws.com +s3-me-south-1.amazonaws.com +s3-sa-east-1.amazonaws.com +s3-us-east-2.amazonaws.com +s3-us-gov-east-1.amazonaws.com +s3-us-gov-west-1.amazonaws.com +s3-us-west-1.amazonaws.com +s3-us-west-2.amazonaws.com +s3-website-ap-northeast-1.amazonaws.com +s3-website-ap-southeast-1.amazonaws.com +s3-website-ap-southeast-2.amazonaws.com +s3-website-eu-west-1.amazonaws.com +s3-website-sa-east-1.amazonaws.com +s3-website-us-east-1.amazonaws.com +s3-website-us-gov-west-1.amazonaws.com +s3-website-us-west-1.amazonaws.com +s3-website-us-west-2.amazonaws.com +s3.dualstack.sa-east-1.amazonaws.com +s3-accesspoint.dualstack.sa-east-1.amazonaws.com +s3-website.dualstack.sa-east-1.amazonaws.com +s3.sa-east-1.amazonaws.com +s3-accesspoint.sa-east-1.amazonaws.com +s3-object-lambda.sa-east-1.amazonaws.com +s3-website.sa-east-1.amazonaws.com +s3.dualstack.us-east-1.amazonaws.com +s3-accesspoint.dualstack.us-east-1.amazonaws.com +s3-accesspoint-fips.dualstack.us-east-1.amazonaws.com +s3-fips.dualstack.us-east-1.amazonaws.com +s3-website.dualstack.us-east-1.amazonaws.com +s3.us-east-1.amazonaws.com +s3-accesspoint.us-east-1.amazonaws.com +s3-accesspoint-fips.us-east-1.amazonaws.com +s3-deprecated.us-east-1.amazonaws.com +s3-fips.us-east-1.amazonaws.com +s3-object-lambda.us-east-1.amazonaws.com +s3-website.us-east-1.amazonaws.com +s3.dualstack.us-east-2.amazonaws.com +s3-accesspoint.dualstack.us-east-2.amazonaws.com +s3-accesspoint-fips.dualstack.us-east-2.amazonaws.com +s3-fips.dualstack.us-east-2.amazonaws.com +s3.us-east-2.amazonaws.com +s3-accesspoint.us-east-2.amazonaws.com +s3-accesspoint-fips.us-east-2.amazonaws.com +s3-deprecated.us-east-2.amazonaws.com +s3-fips.us-east-2.amazonaws.com +s3-object-lambda.us-east-2.amazonaws.com +s3-website.us-east-2.amazonaws.com +s3.dualstack.us-gov-east-1.amazonaws.com +s3-accesspoint.dualstack.us-gov-east-1.amazonaws.com +s3-accesspoint-fips.dualstack.us-gov-east-1.amazonaws.com +s3-fips.dualstack.us-gov-east-1.amazonaws.com +s3.us-gov-east-1.amazonaws.com +s3-accesspoint.us-gov-east-1.amazonaws.com +s3-accesspoint-fips.us-gov-east-1.amazonaws.com +s3-fips.us-gov-east-1.amazonaws.com +s3-object-lambda.us-gov-east-1.amazonaws.com +s3-website.us-gov-east-1.amazonaws.com +s3.dualstack.us-gov-west-1.amazonaws.com +s3-accesspoint.dualstack.us-gov-west-1.amazonaws.com +s3-accesspoint-fips.dualstack.us-gov-west-1.amazonaws.com +s3-fips.dualstack.us-gov-west-1.amazonaws.com +s3.us-gov-west-1.amazonaws.com +s3-accesspoint.us-gov-west-1.amazonaws.com +s3-accesspoint-fips.us-gov-west-1.amazonaws.com +s3-fips.us-gov-west-1.amazonaws.com +s3-object-lambda.us-gov-west-1.amazonaws.com +s3-website.us-gov-west-1.amazonaws.com +s3.dualstack.us-west-1.amazonaws.com +s3-accesspoint.dualstack.us-west-1.amazonaws.com +s3-accesspoint-fips.dualstack.us-west-1.amazonaws.com +s3-fips.dualstack.us-west-1.amazonaws.com +s3-website.dualstack.us-west-1.amazonaws.com +s3.us-west-1.amazonaws.com +s3-accesspoint.us-west-1.amazonaws.com +s3-accesspoint-fips.us-west-1.amazonaws.com +s3-fips.us-west-1.amazonaws.com +s3-object-lambda.us-west-1.amazonaws.com +s3-website.us-west-1.amazonaws.com +s3.dualstack.us-west-2.amazonaws.com +s3-accesspoint.dualstack.us-west-2.amazonaws.com +s3-accesspoint-fips.dualstack.us-west-2.amazonaws.com +s3-fips.dualstack.us-west-2.amazonaws.com +s3-website.dualstack.us-west-2.amazonaws.com +s3.us-west-2.amazonaws.com +s3-accesspoint.us-west-2.amazonaws.com +s3-accesspoint-fips.us-west-2.amazonaws.com +s3-deprecated.us-west-2.amazonaws.com +s3-fips.us-west-2.amazonaws.com +s3-object-lambda.us-west-2.amazonaws.com +s3-website.us-west-2.amazonaws.com + +// Amazon SageMaker Notebook Instances +// Submitted by AWS Security <psl-maintainers@amazon.com> +// Reference: ce8ae0b1-0070-496d-be88-37c31837af9d +notebook.af-south-1.sagemaker.aws +notebook.ap-east-1.sagemaker.aws +notebook.ap-northeast-1.sagemaker.aws +notebook.ap-northeast-2.sagemaker.aws +notebook.ap-northeast-3.sagemaker.aws +notebook.ap-south-1.sagemaker.aws +notebook.ap-south-2.sagemaker.aws +notebook.ap-southeast-1.sagemaker.aws +notebook.ap-southeast-2.sagemaker.aws +notebook.ap-southeast-3.sagemaker.aws +notebook.ap-southeast-4.sagemaker.aws +notebook.ca-central-1.sagemaker.aws +notebook-fips.ca-central-1.sagemaker.aws +notebook.ca-west-1.sagemaker.aws +notebook-fips.ca-west-1.sagemaker.aws +notebook.eu-central-1.sagemaker.aws +notebook.eu-central-2.sagemaker.aws +notebook.eu-north-1.sagemaker.aws +notebook.eu-south-1.sagemaker.aws +notebook.eu-south-2.sagemaker.aws +notebook.eu-west-1.sagemaker.aws +notebook.eu-west-2.sagemaker.aws +notebook.eu-west-3.sagemaker.aws +notebook.il-central-1.sagemaker.aws +notebook.me-central-1.sagemaker.aws +notebook.me-south-1.sagemaker.aws +notebook.sa-east-1.sagemaker.aws +notebook.us-east-1.sagemaker.aws +notebook-fips.us-east-1.sagemaker.aws +notebook.us-east-2.sagemaker.aws +notebook-fips.us-east-2.sagemaker.aws +notebook.us-gov-east-1.sagemaker.aws +notebook-fips.us-gov-east-1.sagemaker.aws +notebook.us-gov-west-1.sagemaker.aws +notebook-fips.us-gov-west-1.sagemaker.aws +notebook.us-west-1.sagemaker.aws +notebook.us-west-2.sagemaker.aws +notebook-fips.us-west-2.sagemaker.aws +notebook.cn-north-1.sagemaker.com.cn +notebook.cn-northwest-1.sagemaker.com.cn + +// Amazon SageMaker Studio +// Submitted by AWS Security <psl-maintainers@amazon.com> +// Reference: 057ee397-6bf8-4f20-b807-d7bc145ac980 +studio.af-south-1.sagemaker.aws +studio.ap-east-1.sagemaker.aws +studio.ap-northeast-1.sagemaker.aws +studio.ap-northeast-2.sagemaker.aws +studio.ap-northeast-3.sagemaker.aws +studio.ap-south-1.sagemaker.aws +studio.ap-southeast-1.sagemaker.aws +studio.ap-southeast-2.sagemaker.aws +studio.ap-southeast-3.sagemaker.aws +studio.ca-central-1.sagemaker.aws +studio.eu-central-1.sagemaker.aws +studio.eu-north-1.sagemaker.aws +studio.eu-south-1.sagemaker.aws +studio.eu-west-1.sagemaker.aws +studio.eu-west-2.sagemaker.aws +studio.eu-west-3.sagemaker.aws +studio.il-central-1.sagemaker.aws +studio.me-central-1.sagemaker.aws +studio.me-south-1.sagemaker.aws +studio.sa-east-1.sagemaker.aws +studio.us-east-1.sagemaker.aws +studio.us-east-2.sagemaker.aws +studio.us-gov-east-1.sagemaker.aws +studio-fips.us-gov-east-1.sagemaker.aws +studio.us-gov-west-1.sagemaker.aws +studio-fips.us-gov-west-1.sagemaker.aws +studio.us-west-1.sagemaker.aws +studio.us-west-2.sagemaker.aws +studio.cn-north-1.sagemaker.com.cn +studio.cn-northwest-1.sagemaker.com.cn + +// Analytics on AWS +// Submitted by AWS Security <psl-maintainers@amazon.com> +// Reference: 955f9f40-a495-4e73-ae85-67b77ac9cadd +analytics-gateway.ap-northeast-1.amazonaws.com +analytics-gateway.ap-northeast-2.amazonaws.com +analytics-gateway.ap-south-1.amazonaws.com +analytics-gateway.ap-southeast-1.amazonaws.com +analytics-gateway.ap-southeast-2.amazonaws.com +analytics-gateway.eu-central-1.amazonaws.com +analytics-gateway.eu-west-1.amazonaws.com +analytics-gateway.us-east-1.amazonaws.com +analytics-gateway.us-east-2.amazonaws.com +analytics-gateway.us-west-2.amazonaws.com + +// AWS Amplify +// Submitted by AWS Security <psl-maintainers@amazon.com> +// Reference: 5ecce854-c033-4fc4-a755-1a9916d9a9bb +*.amplifyapp.com + +// AWS App Runner +// Submitted by AWS Security <psl-maintainers@amazon.com> +// Reference: 6828c008-ba5d-442f-ade5-48da4e7c2316 +*.awsapprunner.com + +// AWS Cloud9 +// Submitted by: AWS Security <psl-maintainers@amazon.com> +// Reference: 30717f72-4007-4f0f-8ed4-864c6f2efec9 +webview-assets.aws-cloud9.af-south-1.amazonaws.com +vfs.cloud9.af-south-1.amazonaws.com +webview-assets.cloud9.af-south-1.amazonaws.com +webview-assets.aws-cloud9.ap-east-1.amazonaws.com +vfs.cloud9.ap-east-1.amazonaws.com +webview-assets.cloud9.ap-east-1.amazonaws.com +webview-assets.aws-cloud9.ap-northeast-1.amazonaws.com +vfs.cloud9.ap-northeast-1.amazonaws.com +webview-assets.cloud9.ap-northeast-1.amazonaws.com +webview-assets.aws-cloud9.ap-northeast-2.amazonaws.com +vfs.cloud9.ap-northeast-2.amazonaws.com +webview-assets.cloud9.ap-northeast-2.amazonaws.com +webview-assets.aws-cloud9.ap-northeast-3.amazonaws.com +vfs.cloud9.ap-northeast-3.amazonaws.com +webview-assets.cloud9.ap-northeast-3.amazonaws.com +webview-assets.aws-cloud9.ap-south-1.amazonaws.com +vfs.cloud9.ap-south-1.amazonaws.com +webview-assets.cloud9.ap-south-1.amazonaws.com +webview-assets.aws-cloud9.ap-southeast-1.amazonaws.com +vfs.cloud9.ap-southeast-1.amazonaws.com +webview-assets.cloud9.ap-southeast-1.amazonaws.com +webview-assets.aws-cloud9.ap-southeast-2.amazonaws.com +vfs.cloud9.ap-southeast-2.amazonaws.com +webview-assets.cloud9.ap-southeast-2.amazonaws.com +webview-assets.aws-cloud9.ca-central-1.amazonaws.com +vfs.cloud9.ca-central-1.amazonaws.com +webview-assets.cloud9.ca-central-1.amazonaws.com +webview-assets.aws-cloud9.eu-central-1.amazonaws.com +vfs.cloud9.eu-central-1.amazonaws.com +webview-assets.cloud9.eu-central-1.amazonaws.com +webview-assets.aws-cloud9.eu-north-1.amazonaws.com +vfs.cloud9.eu-north-1.amazonaws.com +webview-assets.cloud9.eu-north-1.amazonaws.com +webview-assets.aws-cloud9.eu-south-1.amazonaws.com +vfs.cloud9.eu-south-1.amazonaws.com +webview-assets.cloud9.eu-south-1.amazonaws.com +webview-assets.aws-cloud9.eu-west-1.amazonaws.com +vfs.cloud9.eu-west-1.amazonaws.com +webview-assets.cloud9.eu-west-1.amazonaws.com +webview-assets.aws-cloud9.eu-west-2.amazonaws.com +vfs.cloud9.eu-west-2.amazonaws.com +webview-assets.cloud9.eu-west-2.amazonaws.com +webview-assets.aws-cloud9.eu-west-3.amazonaws.com +vfs.cloud9.eu-west-3.amazonaws.com +webview-assets.cloud9.eu-west-3.amazonaws.com +webview-assets.aws-cloud9.il-central-1.amazonaws.com +vfs.cloud9.il-central-1.amazonaws.com +webview-assets.aws-cloud9.me-south-1.amazonaws.com +vfs.cloud9.me-south-1.amazonaws.com +webview-assets.cloud9.me-south-1.amazonaws.com +webview-assets.aws-cloud9.sa-east-1.amazonaws.com +vfs.cloud9.sa-east-1.amazonaws.com +webview-assets.cloud9.sa-east-1.amazonaws.com +webview-assets.aws-cloud9.us-east-1.amazonaws.com +vfs.cloud9.us-east-1.amazonaws.com +webview-assets.cloud9.us-east-1.amazonaws.com +webview-assets.aws-cloud9.us-east-2.amazonaws.com +vfs.cloud9.us-east-2.amazonaws.com +webview-assets.cloud9.us-east-2.amazonaws.com +webview-assets.aws-cloud9.us-west-1.amazonaws.com +vfs.cloud9.us-west-1.amazonaws.com +webview-assets.cloud9.us-west-1.amazonaws.com +webview-assets.aws-cloud9.us-west-2.amazonaws.com +vfs.cloud9.us-west-2.amazonaws.com +webview-assets.cloud9.us-west-2.amazonaws.com + +// AWS Elastic Beanstalk +// Submitted by AWS Security <psl-maintainers@amazon.com> +// Reference: bb5a965c-dec3-4967-aa22-e306ad064797 +cn-north-1.eb.amazonaws.com.cn +cn-northwest-1.eb.amazonaws.com.cn +elasticbeanstalk.com +af-south-1.elasticbeanstalk.com +ap-east-1.elasticbeanstalk.com +ap-northeast-1.elasticbeanstalk.com +ap-northeast-2.elasticbeanstalk.com +ap-northeast-3.elasticbeanstalk.com +ap-south-1.elasticbeanstalk.com +ap-southeast-1.elasticbeanstalk.com +ap-southeast-2.elasticbeanstalk.com +ap-southeast-3.elasticbeanstalk.com +ca-central-1.elasticbeanstalk.com +eu-central-1.elasticbeanstalk.com +eu-north-1.elasticbeanstalk.com +eu-south-1.elasticbeanstalk.com +eu-west-1.elasticbeanstalk.com +eu-west-2.elasticbeanstalk.com +eu-west-3.elasticbeanstalk.com +il-central-1.elasticbeanstalk.com +me-south-1.elasticbeanstalk.com +sa-east-1.elasticbeanstalk.com +us-east-1.elasticbeanstalk.com +us-east-2.elasticbeanstalk.com +us-gov-east-1.elasticbeanstalk.com +us-gov-west-1.elasticbeanstalk.com +us-west-1.elasticbeanstalk.com +us-west-2.elasticbeanstalk.com + +// (AWS) Elastic Load Balancing +// Submitted by Luke Wells <psl-maintainers@amazon.com> +// Reference: 12a3d528-1bac-4433-a359-a395867ffed2 +*.elb.amazonaws.com.cn +*.elb.amazonaws.com + +// AWS Global Accelerator +// Submitted by Daniel Massaguer <psl-maintainers@amazon.com> +// Reference: d916759d-a08b-4241-b536-4db887383a6a +awsglobalaccelerator.com + +// AWS re:Post Private +// Submitted by AWS Security <psl-maintainers@amazon.com> +// Reference: 83385945-225f-416e-9aa0-ad0632bfdcee +*.private.repost.aws + +// eero +// Submitted by Yue Kang <eero-dynamic-dns@amazon.com> +// Reference: 264afe70-f62c-4c02-8ab9-b5281ed24461 +eero.online +eero-stage.online + +// concludes Amazon + +// Amune : https://amune.org/ +// Submitted by Team Amune <cert@amune.org> +t3l3p0rt.net +tele.amune.org + +// Apigee : https://apigee.com/ +// Submitted by Apigee Security Team <security@apigee.com> +apigee.io + +// Apis Networks: https://apisnetworks.com +// Submitted by Matt Saladna <matt@apisnetworks.com> +panel.dev + +// Apphud : https://apphud.com +// Submitted by Alexander Selivanov <alex@apphud.com> +siiites.com + +// Appspace : https://www.appspace.com +// Submitted by Appspace Security Team <security@appspace.com> +appspacehosted.com +appspaceusercontent.com + +// Appudo UG (haftungsbeschränkt) : https://www.appudo.com +// Submitted by Alexander Hochbaum <admin@appudo.com> +appudo.net + +// Aptible : https://www.aptible.com/ +// Submitted by Thomas Orozco <thomas@aptible.com> +on-aptible.com + +// Aquapal : https://aquapal.net/ +// Submitted by Aki Ueno <admin@aquapal.net> +f5.si + +// ASEINet : https://www.aseinet.com/ +// Submitted by Asei SEKIGUCHI <mail@aseinet.com> +user.aseinet.ne.jp +gv.vc +d.gv.vc + +// Asociación Amigos de la Informática "Euskalamiga" : http://encounter.eus/ +// Submitted by Hector Martin <marcan@euskalencounter.org> +user.party.eus + +// Association potager.org : https://potager.org/ +// Submitted by Lunar <jardiniers@potager.org> +pimienta.org +poivron.org +potager.org +sweetpepper.org + +// ASUSTOR Inc. : http://www.asustor.com +// Submitted by Vincent Tseng <vincenttseng@asustor.com> +myasustor.com + +// Atlassian : https://atlassian.com +// Submitted by Sam Smyth <devloop@atlassian.com> +cdn.prod.atlassian-dev.net + +// Authentick UG (haftungsbeschränkt) : https://authentick.net +// Submitted by Lukas Reschke <lukas@authentick.net> +translated.page + +// Autocode : https://autocode.com +// Submitted by Jacob Lee <jacob@autocode.com> +autocode.dev + +// AVM : https://avm.de +// Submitted by Andreas Weise <a.weise@avm.de> +myfritz.net + +// AVStack Pte. Ltd. : https://avstack.io +// Submitted by Jasper Hugo <jasper@avstack.io> +onavstack.net + +// AW AdvisorWebsites.com Software Inc : https://advisorwebsites.com +// Submitted by James Kennedy <domains@advisorwebsites.com> +*.awdev.ca +*.advisor.ws + +// AZ.pl sp. z.o.o: https://az.pl +// Submitted by Krzysztof Wolski <krzysztof.wolski@home.eu> +ecommerce-shop.pl + +// b-data GmbH : https://www.b-data.io +// Submitted by Olivier Benz <olivier.benz@b-data.ch> +b-data.io + +// backplane : https://www.backplane.io +// Submitted by Anthony Voutas <anthony@backplane.io> +backplaneapp.io + +// Balena : https://www.balena.io +// Submitted by Petros Angelatos <petrosagg@balena.io> +balena-devices.com + +// University of Banja Luka : https://unibl.org +// Domains for Republic of Srpska administrative entity. +// Submitted by Marko Ivanovic <kormang@hotmail.rs> +rs.ba + +// Banzai Cloud +// Submitted by Janos Matyas <info@banzaicloud.com> +*.banzai.cloud +app.banzaicloud.io +*.backyards.banzaicloud.io + +// BASE, Inc. : https://binc.jp +// Submitted by Yuya NAGASAWA <public-suffix-list@binc.jp> +base.ec +official.ec +buyshop.jp +fashionstore.jp +handcrafted.jp +kawaiishop.jp +supersale.jp +theshop.jp +shopselect.net +base.shop + +// BeagleBoard.org Foundation : https://beagleboard.org +// Submitted by Jason Kridner <jkridner@beagleboard.org> +beagleboard.io + +// Beget Ltd +// Submitted by Lev Nekrasov <lnekrasov@beget.com> +*.beget.app + +// Besties : https://besties.house +// Submitted by Hazel Cora <hazy@besties.house> +pages.gay + +// BetaInABox +// Submitted by Adrian <adrian@betainabox.com> +betainabox.com + +// BinaryLane : http://www.binarylane.com +// Submitted by Nathan O'Sullivan <nathan@mammoth.com.au> +bnr.la + +// Bitbucket : http://bitbucket.org +// Submitted by Andy Ortlieb <aortlieb@atlassian.com> +bitbucket.io + +// Blackbaud, Inc. : https://www.blackbaud.com +// Submitted by Paul Crowder <paul.crowder@blackbaud.com> +blackbaudcdn.net + +// Blatech : http://www.blatech.net +// Submitted by Luke Bratch <luke@bratch.co.uk> +of.je + +// Blue Bite, LLC : https://bluebite.com +// Submitted by Joshua Weiss <admin.engineering@bluebite.com> +bluebite.io + +// Boomla : https://boomla.com +// Submitted by Tibor Halter <thalter@boomla.com> +boomla.net + +// Boutir : https://www.boutir.com +// Submitted by Eric Ng Ka Ka <ngkaka@boutir.com> +boutir.com + +// Boxfuse : https://boxfuse.com +// Submitted by Axel Fontaine <axel@boxfuse.com> +boxfuse.io + +// bplaced : https://www.bplaced.net/ +// Submitted by Miroslav Bozic <security@bplaced.net> +square7.ch +bplaced.com +bplaced.de +square7.de +bplaced.net +square7.net + +// Brave : https://brave.com +// Submitted by Andrea Brancaleoni <abrancaleoni@brave.com> +*.s.brave.io + +// Brendly : https://brendly.rs +// Submitted by Dusan Radovanovic <dusan.radovanovic@brendly.rs> +shop.brendly.rs + +// BrowserSafetyMark +// Submitted by Dave Tharp <browsersafetymark.io@quicinc.com> +browsersafetymark.io + +// Bytemark Hosting : https://www.bytemark.co.uk +// Submitted by Paul Cammish <paul.cammish@bytemark.co.uk> +uk0.bigv.io +dh.bytemark.co.uk +vm.bytemark.co.uk + +// Caf.js Labs LLC : https://www.cafjs.com +// Submitted by Antonio Lain <antlai@cafjs.com> +cafjs.com + +// callidomus : https://www.callidomus.com/ +// Submitted by Marcus Popp <admin@callidomus.com> +mycd.eu + +// Canva Pty Ltd : https://canva.com/ +// Submitted by Joel Aquilina <publicsuffixlist@canva.com> +canva-apps.cn +*.my.canvasite.cn +canva-apps.com +*.my.canva.site + +// Carrd : https://carrd.co +// Submitted by AJ <aj@carrd.co> +drr.ac +uwu.ai +carrd.co +crd.co +ju.mp + +// CentralNic : http://www.centralnic.com/names/domains +// Submitted by registry <gavin.brown@centralnic.com> +ae.org +br.com +cn.com +com.de +com.se +de.com +eu.com +gb.net +hu.net +jp.net +jpn.com +mex.com +ru.com +sa.com +se.net +uk.com +uk.net +us.com +za.bz +za.com + +// No longer operated by CentralNic, these entries should be adopted and/or removed by current operators +// Submitted by Gavin Brown <gavin.brown@centralnic.com> +ar.com +hu.com +kr.com +no.com +qc.com +uy.com + +// Africa.com Web Solutions Ltd : https://registry.africa.com +// Submitted by Gavin Brown <gavin.brown@centralnic.com> +africa.com + +// iDOT Services Limited : http://www.domain.gr.com +// Submitted by Gavin Brown <gavin.brown@centralnic.com> +gr.com + +// Radix FZC : http://domains.in.net +// Submitted by Gavin Brown <gavin.brown@centralnic.com> +in.net +web.in + +// US REGISTRY LLC : http://us.org +// Submitted by Gavin Brown <gavin.brown@centralnic.com> +us.org + +// co.com Registry, LLC : https://registry.co.com +// Submitted by Gavin Brown <gavin.brown@centralnic.com> +co.com + +// Roar Domains LLC : https://roar.basketball/ +// Submitted by Gavin Brown <gavin.brown@centralnic.com> +aus.basketball +nz.basketball + +// BRS Media : https://brsmedia.com/ +// Submitted by Gavin Brown <gavin.brown@centralnic.com> +radio.am +radio.fm + +// c.la : http://www.c.la/ +c.la + +// certmgr.org : https://certmgr.org +// Submitted by B. Blechschmidt <hostmaster@certmgr.org> +certmgr.org + +// Cityhost LLC : https://cityhost.ua +// Submitted by Maksym Rivtin <support@cityhost.net.ua> +cx.ua + +// Civilized Discourse Construction Kit, Inc. : https://www.discourse.org/ +// Submitted by Rishabh Nambiar & Michael Brown <team@discourse.org> +discourse.group +discourse.team + +// Clever Cloud : https://www.clever-cloud.com/ +// Submitted by Quentin Adam <noc@clever-cloud.com> +cleverapps.io + +// Clerk : https://www.clerk.dev +// Submitted by Colin Sidoti <systems@clerk.dev> +clerk.app +clerkstage.app +*.lcl.dev +*.lclstage.dev +*.stg.dev +*.stgstage.dev + +// ClickRising : https://clickrising.com/ +// Submitted by Umut Gumeli <infrastructure-publicsuffixlist@clickrising.com> +clickrising.net + +// Cloud66 : https://www.cloud66.com/ +// Submitted by Khash Sajadi <khash@cloud66.com> +c66.me +cloud66.ws +cloud66.zone + +// CloudAccess.net : https://www.cloudaccess.net/ +// Submitted by Pawel Panek <noc@cloudaccess.net> +jdevcloud.com +wpdevcloud.com +cloudaccess.host +freesite.host +cloudaccess.net + +// cloudControl : https://www.cloudcontrol.com/ +// Submitted by Tobias Wilken <tw@cloudcontrol.com> +cloudcontrolled.com +cloudcontrolapp.com + +// Cloudera, Inc. : https://www.cloudera.com/ +// Submitted by Kedarnath Waikar <security@cloudera.com> +*.cloudera.site + +// Cloudflare, Inc. : https://www.cloudflare.com/ +// Submitted by Cloudflare Team <publicsuffixlist@cloudflare.com> +cf-ipfs.com +cloudflare-ipfs.com +trycloudflare.com +pages.dev +r2.dev +workers.dev + +// Clovyr : https://clovyr.io +// Submitted by Patrick Nielsen <patrick@clovyr.io> +wnext.app + +// co.ca : http://registry.co.ca/ +co.ca + +// Co & Co : https://co-co.nl/ +// Submitted by Govert Versluis <govert@co-co.nl> +*.otap.co + +// i-registry s.r.o. : http://www.i-registry.cz/ +// Submitted by Martin Semrad <semrad@i-registry.cz> +co.cz + +// CDN77.com : http://www.cdn77.com +// Submitted by Jan Krpes <jan.krpes@cdn77.com> +c.cdn77.org +cdn77-ssl.net +r.cdn77.net +rsc.cdn77.org +ssl.origin.cdn77-secure.org + +// Cloud DNS Ltd : http://www.cloudns.net +// Submitted by Aleksander Hristov <noc@cloudns.net> +cloudns.asia +cloudns.biz +cloudns.club +cloudns.cc +cloudns.eu +cloudns.in +cloudns.info +cloudns.org +cloudns.pro +cloudns.pw +cloudns.us + +// CNPY : https://cnpy.gdn +// Submitted by Angelo Gladding <angelo@lahacker.net> +cnpy.gdn + +// Codeberg e. V. : https://codeberg.org +// Submitted by Moritz Marquardt <git@momar.de> +codeberg.page + +// CoDNS B.V. +co.nl +co.no + +// Combell.com : https://www.combell.com +// Submitted by Thomas Wouters <thomas.wouters@combellgroup.com> +webhosting.be +hosting-cluster.nl + +// Convex : https://convex.dev/ +// Submitted by James Cowling <security@convex.dev> +convex.site + +// Coordination Center for TLD RU and XN--P1AI : https://cctld.ru/en/domains/domens_ru/reserved/ +// Submitted by George Georgievsky <gug@cctld.ru> +ac.ru +edu.ru +gov.ru +int.ru +mil.ru +test.ru + +// COSIMO GmbH : http://www.cosimo.de +// Submitted by Rene Marticke <rmarticke@cosimo.de> +dyn.cosidns.de +dynamisches-dns.de +dnsupdater.de +internet-dns.de +l-o-g-i-n.de +dynamic-dns.info +feste-ip.net +knx-server.net +static-access.net + +// cPanel L.L.C. : https://www.cpanel.net/ +// Submitted by Dustin Scherer <public.suffix@cpanel.net> +*.cprapid.com + +// Craynic, s.r.o. : http://www.craynic.com/ +// Submitted by Ales Krajnik <ales.krajnik@craynic.com> +realm.cz + +// Crisp IM SAS : https://crisp.chat/ +// Submitted by Baptiste Jamin <hostmaster@crisp.chat> +on.crisp.email + +// Cryptonomic : https://cryptonomic.net/ +// Submitted by Andrew Cady <public-suffix-list@cryptonomic.net> +*.cryptonomic.net + +// Cupcake : https://cupcake.io/ +// Submitted by Jonathan Rudenberg <jonathan@cupcake.io> +cupcake.is + +// Curv UG : https://curv-labs.de/ +// Submitted by Marvin Wiesner <Marvin@curv-labs.de> +curv.dev + +// Customer OCI - Oracle Dyn https://cloud.oracle.com/home https://dyn.com/dns/ +// Submitted by Gregory Drake <support@dyn.com> +// Note: This is intended to also include customer-oci.com due to wildcards implicitly including the current label +*.customer-oci.com +*.oci.customer-oci.com +*.ocp.customer-oci.com +*.ocs.customer-oci.com + +// Cyclic Software : https://www.cyclic.sh +// Submitted by Kam Lasater <dns-admin@cyclic.sh> +cyclic.app +cyclic.cloud +cyclic-app.com +cyclic.co.in + +// cyon GmbH : https://www.cyon.ch/ +// Submitted by Dominic Luechinger <dol@cyon.ch> +cyon.link +cyon.site + +// Danger Science Group: https://dangerscience.com/ +// Submitted by Skylar MacDonald <skylar@dangerscience.com> +fnwk.site +folionetwork.site +platform0.app + +// Daplie, Inc : https://daplie.com +// Submitted by AJ ONeal <aj@daplie.com> +daplie.me +localhost.daplie.me + +// Datto, Inc. : https://www.datto.com/ +// Submitted by Philipp Heckel <ph@datto.com> +dattolocal.com +dattorelay.com +dattoweb.com +mydatto.com +dattolocal.net +mydatto.net + +// Dansk.net : http://www.dansk.net/ +// Submitted by Anani Voule <digital@digital.co.dk> +biz.dk +co.dk +firm.dk +reg.dk +store.dk + +// dappnode.io : https://dappnode.io/ +// Submitted by Abel Boldu / DAppNode Team <community@dappnode.io> +dyndns.dappnode.io + +// dapps.earth : https://dapps.earth/ +// Submitted by Daniil Burdakov <icqkill@gmail.com> +*.dapps.earth +*.bzz.dapps.earth + +// Dark, Inc. : https://darklang.com +// Submitted by Paul Biggar <ops@darklang.com> +builtwithdark.com + +// DataDetect, LLC. : https://datadetect.com +// Submitted by Andrew Banchich <abanchich@sceven.com> +demo.datadetect.com +instance.datadetect.com + +// Datawire, Inc : https://www.datawire.io +// Submitted by Richard Li <secalert@datawire.io> +edgestack.me + +// DDNS5 : https://ddns5.com +// Submitted by Cameron Elliott <cameron@cameronelliott.com> +ddns5.com + +// Debian : https://www.debian.org/ +// Submitted by Peter Palfrader / Debian Sysadmin Team <dsa-publicsuffixlist@debian.org> +debian.net + +// Deno Land Inc : https://deno.com/ +// Submitted by Luca Casonato <hostmaster@deno.com> +deno.dev +deno-staging.dev + +// deSEC : https://desec.io/ +// Submitted by Peter Thomassen <peter@desec.io> +dedyn.io + +// Deta: https://www.deta.sh/ +// Submitted by Aavash Shrestha <aavash@deta.sh> +deta.app +deta.dev + +// Diher Solutions : https://diher.solutions +// Submitted by Didi Hermawan <mail@diher.solutions> +*.rss.my.id +*.diher.solutions + +// Discord Inc : https://discord.com +// Submitted by Sahn Lam <slam@discordapp.com> +discordsays.com +discordsez.com + +// DNS Africa Ltd https://dns.business +// Submitted by Calvin Browne <calvin@dns.business> +jozi.biz + +// DNShome : https://www.dnshome.de/ +// Submitted by Norbert Auler <mail@dnshome.de> +dnshome.de + +// DotArai : https://www.dotarai.com/ +// Submitted by Atsadawat Netcharadsang <atsadawat@dotarai.co.th> +online.th +shop.th + +// DrayTek Corp. : https://www.draytek.com/ +// Submitted by Paul Fang <mis@draytek.com> +drayddns.com + +// DreamCommerce : https://shoper.pl/ +// Submitted by Konrad Kotarba <konrad.kotarba@dreamcommerce.com> +shoparena.pl + +// DreamHost : http://www.dreamhost.com/ +// Submitted by Andrew Farmer <andrew.farmer@dreamhost.com> +dreamhosters.com + +// Drobo : http://www.drobo.com/ +// Submitted by Ricardo Padilha <rpadilha@drobo.com> +mydrobo.com + +// Drud Holdings, LLC. : https://www.drud.com/ +// Submitted by Kevin Bridges <kevin@drud.com> +drud.io +drud.us + +// DuckDNS : http://www.duckdns.org/ +// Submitted by Richard Harper <richard@duckdns.org> +duckdns.org + +// Bip : https://bip.sh +// Submitted by Joel Kennedy <joel@bip.sh> +bip.sh + +// bitbridge.net : Submitted by Craig Welch, abeliidev@gmail.com +bitbridge.net + +// dy.fi : http://dy.fi/ +// Submitted by Heikki Hannikainen <hessu@hes.iki.fi> +dy.fi +tunk.org + +// DynDNS.com : http://www.dyndns.com/services/dns/dyndns/ +dyndns-at-home.com +dyndns-at-work.com +dyndns-blog.com +dyndns-free.com +dyndns-home.com +dyndns-ip.com +dyndns-mail.com +dyndns-office.com +dyndns-pics.com +dyndns-remote.com +dyndns-server.com +dyndns-web.com +dyndns-wiki.com +dyndns-work.com +dyndns.biz +dyndns.info +dyndns.org +dyndns.tv +at-band-camp.net +ath.cx +barrel-of-knowledge.info +barrell-of-knowledge.info +better-than.tv +blogdns.com +blogdns.net +blogdns.org +blogsite.org +boldlygoingnowhere.org +broke-it.net +buyshouses.net +cechire.com +dnsalias.com +dnsalias.net +dnsalias.org +dnsdojo.com +dnsdojo.net +dnsdojo.org +does-it.net +doesntexist.com +doesntexist.org +dontexist.com +dontexist.net +dontexist.org +doomdns.com +doomdns.org +dvrdns.org +dyn-o-saur.com +dynalias.com +dynalias.net +dynalias.org +dynathome.net +dyndns.ws +endofinternet.net +endofinternet.org +endoftheinternet.org +est-a-la-maison.com +est-a-la-masion.com +est-le-patron.com +est-mon-blogueur.com +for-better.biz +for-more.biz +for-our.info +for-some.biz +for-the.biz +forgot.her.name +forgot.his.name +from-ak.com +from-al.com +from-ar.com +from-az.net +from-ca.com +from-co.net +from-ct.com +from-dc.com +from-de.com +from-fl.com +from-ga.com +from-hi.com +from-ia.com +from-id.com +from-il.com +from-in.com +from-ks.com +from-ky.com +from-la.net +from-ma.com +from-md.com +from-me.org +from-mi.com +from-mn.com +from-mo.com +from-ms.com +from-mt.com +from-nc.com +from-nd.com +from-ne.com +from-nh.com +from-nj.com +from-nm.com +from-nv.com +from-ny.net +from-oh.com +from-ok.com +from-or.com +from-pa.com +from-pr.com +from-ri.com +from-sc.com +from-sd.com +from-tn.com +from-tx.com +from-ut.com +from-va.com +from-vt.com +from-wa.com +from-wi.com +from-wv.com +from-wy.com +ftpaccess.cc +fuettertdasnetz.de +game-host.org +game-server.cc +getmyip.com +gets-it.net +go.dyndns.org +gotdns.com +gotdns.org +groks-the.info +groks-this.info +ham-radio-op.net +here-for-more.info +hobby-site.com +hobby-site.org +home.dyndns.org +homedns.org +homeftp.net +homeftp.org +homeip.net +homelinux.com +homelinux.net +homelinux.org +homeunix.com +homeunix.net +homeunix.org +iamallama.com +in-the-band.net +is-a-anarchist.com +is-a-blogger.com +is-a-bookkeeper.com +is-a-bruinsfan.org +is-a-bulls-fan.com +is-a-candidate.org +is-a-caterer.com +is-a-celticsfan.org +is-a-chef.com +is-a-chef.net +is-a-chef.org +is-a-conservative.com +is-a-cpa.com +is-a-cubicle-slave.com +is-a-democrat.com +is-a-designer.com +is-a-doctor.com +is-a-financialadvisor.com +is-a-geek.com +is-a-geek.net +is-a-geek.org +is-a-green.com +is-a-guru.com +is-a-hard-worker.com +is-a-hunter.com +is-a-knight.org +is-a-landscaper.com +is-a-lawyer.com +is-a-liberal.com +is-a-libertarian.com +is-a-linux-user.org +is-a-llama.com +is-a-musician.com +is-a-nascarfan.com +is-a-nurse.com +is-a-painter.com +is-a-patsfan.org +is-a-personaltrainer.com +is-a-photographer.com +is-a-player.com +is-a-republican.com +is-a-rockstar.com +is-a-socialist.com +is-a-soxfan.org +is-a-student.com +is-a-teacher.com +is-a-techie.com +is-a-therapist.com +is-an-accountant.com +is-an-actor.com +is-an-actress.com +is-an-anarchist.com +is-an-artist.com +is-an-engineer.com +is-an-entertainer.com +is-by.us +is-certified.com +is-found.org +is-gone.com +is-into-anime.com +is-into-cars.com +is-into-cartoons.com +is-into-games.com +is-leet.com +is-lost.org +is-not-certified.com +is-saved.org +is-slick.com +is-uberleet.com +is-very-bad.org +is-very-evil.org +is-very-good.org +is-very-nice.org +is-very-sweet.org +is-with-theband.com +isa-geek.com +isa-geek.net +isa-geek.org +isa-hockeynut.com +issmarterthanyou.com +isteingeek.de +istmein.de +kicks-ass.net +kicks-ass.org +knowsitall.info +land-4-sale.us +lebtimnetz.de +leitungsen.de +likes-pie.com +likescandy.com +merseine.nu +mine.nu +misconfused.org +mypets.ws +myphotos.cc +neat-url.com +office-on-the.net +on-the-web.tv +podzone.net +podzone.org +readmyblog.org +saves-the-whales.com +scrapper-site.net +scrapping.cc +selfip.biz +selfip.com +selfip.info +selfip.net +selfip.org +sells-for-less.com +sells-for-u.com +sells-it.net +sellsyourhome.org +servebbs.com +servebbs.net +servebbs.org +serveftp.net +serveftp.org +servegame.org +shacknet.nu +simple-url.com +space-to-rent.com +stuff-4-sale.org +stuff-4-sale.us +teaches-yoga.com +thruhere.net +traeumtgerade.de +webhop.biz +webhop.info +webhop.net +webhop.org +worse-than.tv +writesthisblog.com + +// ddnss.de : https://www.ddnss.de/ +// Submitted by Robert Niedziela <webmaster@ddnss.de> +ddnss.de +dyn.ddnss.de +dyndns.ddnss.de +dyndns1.de +dyn-ip24.de +home-webserver.de +dyn.home-webserver.de +myhome-server.de +ddnss.org + +// Definima : http://www.definima.com/ +// Submitted by Maxence Bitterli <maxence@definima.com> +definima.net +definima.io + +// DigitalOcean App Platform : https://www.digitalocean.com/products/app-platform/ +// Submitted by Braxton Huggins <psl-maintainers@digitalocean.com> +ondigitalocean.app + +// DigitalOcean Spaces : https://www.digitalocean.com/products/spaces/ +// Submitted by Robin H. Johnson <psl-maintainers@digitalocean.com> +*.digitaloceanspaces.com + +// dnstrace.pro : https://dnstrace.pro/ +// Submitted by Chris Partridge <chris@partridge.tech> +bci.dnstrace.pro + +// Dynu.com : https://www.dynu.com/ +// Submitted by Sue Ye <sue@dynu.com> +ddnsfree.com +ddnsgeek.com +giize.com +gleeze.com +kozow.com +loseyourip.com +ooguy.com +theworkpc.com +casacam.net +dynu.net +accesscam.org +camdvr.org +freeddns.org +mywire.org +webredirect.org +myddns.rocks +blogsite.xyz + +// dynv6 : https://dynv6.com +// Submitted by Dominik Menke <dom@digineo.de> +dynv6.net + +// E4YOU spol. s.r.o. : https://e4you.cz/ +// Submitted by Vladimir Dudr <info@e4you.cz> +e4.cz + +// Easypanel : https://easypanel.io +// Submitted by Andrei Canta <andrei@easypanel.io> +easypanel.app +easypanel.host + +// EasyWP : https://www.easywp.com +// Submitted by <infracloudteam@namecheap.com> +*.ewp.live + +// Elementor : Elementor Ltd. +// Submitted by Anton Barkan <antonb@elementor.com> +elementor.cloud +elementor.cool + +// En root‽ : https://en-root.org +// Submitted by Emmanuel Raviart <emmanuel@raviart.com> +en-root.fr + +// Enalean SAS: https://www.enalean.com +// Submitted by Thomas Cottier <thomas.cottier@enalean.com> +mytuleap.com +tuleap-partners.com + +// Encoretivity AB: https://encore.dev +// Submitted by André Eriksson <andre@encore.dev> +encr.app +encoreapi.com + +// ECG Robotics, Inc: https://ecgrobotics.org +// Submitted by <frc1533@ecgrobotics.org> +onred.one +staging.onred.one + +// encoway GmbH : https://www.encoway.de +// Submitted by Marcel Daus <cloudops@encoway.de> +eu.encoway.cloud + +// EU.org https://eu.org/ +// Submitted by Pierre Beyssac <hostmaster@eu.org> +eu.org +al.eu.org +asso.eu.org +at.eu.org +au.eu.org +be.eu.org +bg.eu.org +ca.eu.org +cd.eu.org +ch.eu.org +cn.eu.org +cy.eu.org +cz.eu.org +de.eu.org +dk.eu.org +edu.eu.org +ee.eu.org +es.eu.org +fi.eu.org +fr.eu.org +gr.eu.org +hr.eu.org +hu.eu.org +ie.eu.org +il.eu.org +in.eu.org +int.eu.org +is.eu.org +it.eu.org +jp.eu.org +kr.eu.org +lt.eu.org +lu.eu.org +lv.eu.org +mc.eu.org +me.eu.org +mk.eu.org +mt.eu.org +my.eu.org +net.eu.org +ng.eu.org +nl.eu.org +no.eu.org +nz.eu.org +paris.eu.org +pl.eu.org +pt.eu.org +q-a.eu.org +ro.eu.org +ru.eu.org +se.eu.org +si.eu.org +sk.eu.org +tr.eu.org +uk.eu.org +us.eu.org + +// Eurobyte : https://eurobyte.ru +// Submitted by Evgeniy Subbotin <e.subbotin@eurobyte.ru> +eurodir.ru + +// Evennode : http://www.evennode.com/ +// Submitted by Michal Kralik <support@evennode.com> +eu-1.evennode.com +eu-2.evennode.com +eu-3.evennode.com +eu-4.evennode.com +us-1.evennode.com +us-2.evennode.com +us-3.evennode.com +us-4.evennode.com + +// eDirect Corp. : https://hosting.url.com.tw/ +// Submitted by C.S. chang <cschang@corp.url.com.tw> +twmail.cc +twmail.net +twmail.org +mymailer.com.tw +url.tw + +// Fabrica Technologies, Inc. : https://www.fabrica.dev/ +// Submitted by Eric Jiang <eric@fabrica.dev> +onfabrica.com + +// FAITID : https://faitid.org/ +// Submitted by Maxim Alzoba <tech.contact@faitid.org> +// https://www.flexireg.net/stat_info +ru.net +adygeya.ru +bashkiria.ru +bir.ru +cbg.ru +com.ru +dagestan.ru +grozny.ru +kalmykia.ru +kustanai.ru +marine.ru +mordovia.ru +msk.ru +mytis.ru +nalchik.ru +nov.ru +pyatigorsk.ru +spb.ru +vladikavkaz.ru +vladimir.ru +abkhazia.su +adygeya.su +aktyubinsk.su +arkhangelsk.su +armenia.su +ashgabad.su +azerbaijan.su +balashov.su +bashkiria.su +bryansk.su +bukhara.su +chimkent.su +dagestan.su +east-kazakhstan.su +exnet.su +georgia.su +grozny.su +ivanovo.su +jambyl.su +kalmykia.su +kaluga.su +karacol.su +karaganda.su +karelia.su +khakassia.su +krasnodar.su +kurgan.su +kustanai.su +lenug.su +mangyshlak.su +mordovia.su +msk.su +murmansk.su +nalchik.su +navoi.su +north-kazakhstan.su +nov.su +obninsk.su +penza.su +pokrovsk.su +sochi.su +spb.su +tashkent.su +termez.su +togliatti.su +troitsk.su +tselinograd.su +tula.su +tuva.su +vladikavkaz.su +vladimir.su +vologda.su + +// Fancy Bits, LLC : http://getchannels.com +// Submitted by Aman Gupta <aman@getchannels.com> +channelsdvr.net +u.channelsdvr.net + +// Fastly Inc. : http://www.fastly.com/ +// Submitted by Fastly Security <security@fastly.com> +edgecompute.app +fastly-edge.com +fastly-terrarium.com +fastlylb.net +map.fastlylb.net +freetls.fastly.net +map.fastly.net +a.prod.fastly.net +global.prod.fastly.net +a.ssl.fastly.net +b.ssl.fastly.net +global.ssl.fastly.net + +// Fastmail : https://www.fastmail.com/ +// Submitted by Marc Bradshaw <marc@fastmailteam.com> +*.user.fm + +// FASTVPS EESTI OU : https://fastvps.ru/ +// Submitted by Likhachev Vasiliy <lihachev@fastvps.ru> +fastvps-server.com +fastvps.host +myfast.host +fastvps.site +myfast.space + +// Fedora : https://fedoraproject.org/ +// submitted by Patrick Uiterwijk <puiterwijk@fedoraproject.org> +fedorainfracloud.org +fedorapeople.org +cloud.fedoraproject.org +app.os.fedoraproject.org +app.os.stg.fedoraproject.org + +// FearWorks Media Ltd. : https://fearworksmedia.co.uk +// submitted by Keith Fairley <domains@fearworksmedia.co.uk> +conn.uk +copro.uk +hosp.uk + +// Fermax : https://fermax.com/ +// submitted by Koen Van Isterdael <k.vanisterdael@fermax.be> +mydobiss.com + +// FH Muenster : https://www.fh-muenster.de +// Submitted by Robin Naundorf <r.naundorf@fh-muenster.de> +fh-muenster.io + +// Filegear Inc. : https://www.filegear.com +// Submitted by Jason Zhu <jason@owtware.com> +filegear.me +filegear-au.me +filegear-de.me +filegear-gb.me +filegear-ie.me +filegear-jp.me +filegear-sg.me + +// Firebase, Inc. +// Submitted by Chris Raynor <chris@firebase.com> +firebaseapp.com + +// Firewebkit : https://www.firewebkit.com +// Submitted by Majid Qureshi <mqureshi@amrayn.com> +fireweb.app + +// FLAP : https://www.flap.cloud +// Submitted by Louis Chemineau <louis@chmn.me> +flap.id + +// FlashDrive : https://flashdrive.io +// Submitted by Eric Chan <support@flashdrive.io> +onflashdrive.app +fldrv.com + +// FlutterFlow : https://flutterflow.io +// Submitted by Anton Emelyanov <anton@flutterflow.io> +flutterflow.app + +// fly.io: https://fly.io +// Submitted by Kurt Mackey <kurt@fly.io> +fly.dev +edgeapp.net +shw.io + +// Flynn : https://flynn.io +// Submitted by Jonathan Rudenberg <jonathan@flynn.io> +flynnhosting.net + +// Forgerock : https://www.forgerock.com +// Submitted by Roderick Parr <roderick.parr@forgerock.com> +forgeblocks.com +id.forgerock.io + +// Framer : https://www.framer.com +// Submitted by Koen Rouwhorst <koenrh@framer.com> +framer.app +framercanvas.com +framer.media +framer.photos +framer.website +framer.wiki + +// Frusky MEDIA&PR : https://www.frusky.de +// Submitted by Victor Pupynin <hallo@frusky.de> +*.frusky.de + +// RavPage : https://www.ravpage.co.il +// Submitted by Roni Horowitz <roni@responder.co.il> +ravpage.co.il + +// Frederik Braun https://frederik-braun.com +// Submitted by Frederik Braun <fb@frederik-braun.com> +0e.vc + +// Freebox : http://www.freebox.fr +// Submitted by Romain Fliedel <rfliedel@freebox.fr> +freebox-os.com +freeboxos.com +fbx-os.fr +fbxos.fr +freebox-os.fr +freeboxos.fr + +// freedesktop.org : https://www.freedesktop.org +// Submitted by Daniel Stone <daniel@fooishbar.org> +freedesktop.org + +// freemyip.com : https://freemyip.com +// Submitted by Cadence <contact@freemyip.com> +freemyip.com + +// FunkFeuer - Verein zur Förderung freier Netze : https://www.funkfeuer.at +// Submitted by Daniel A. Maierhofer <vorstand@funkfeuer.at> +wien.funkfeuer.at + +// Futureweb GmbH : https://www.futureweb.at +// Submitted by Andreas Schnederle-Wagner <schnederle@futureweb.at> +*.futurecms.at +*.ex.futurecms.at +*.in.futurecms.at +futurehosting.at +futuremailing.at +*.ex.ortsinfo.at +*.kunden.ortsinfo.at +*.statics.cloud + +// GCom Internet : https://www.gcom.net.au +// Submitted by Leo Julius <support@gcom.net.au> +aliases121.com + +// GDS : https://www.gov.uk/service-manual/technology/managing-domain-names +// Submitted by Stephen Ford <hostmaster@digital.cabinet-office.gov.uk> +independent-commission.uk +independent-inquest.uk +independent-inquiry.uk +independent-panel.uk +independent-review.uk +public-inquiry.uk +royal-commission.uk +campaign.gov.uk +service.gov.uk + +// CDDO : https://www.gov.uk/guidance/get-an-api-domain-on-govuk +// Submitted by Jamie Tanna <jamie.tanna@digital.cabinet-office.gov.uk> +api.gov.uk + +// Gehirn Inc. : https://www.gehirn.co.jp/ +// Submitted by Kohei YOSHIDA <tech@gehirn.co.jp> +gehirn.ne.jp +usercontent.jp + +// Gentlent, Inc. : https://www.gentlent.com +// Submitted by Tom Klein <tom@gentlent.com> +gentapps.com +gentlentapis.com +lab.ms +cdn-edges.net + +// Ghost Foundation : https://ghost.org +// Submitted by Matt Hanley <security@ghost.org> +ghost.io + +// GignoSystemJapan: http://gsj.bz +// Submitted by GignoSystemJapan <kakutou-ec@gsj.bz> +gsj.bz + +// GitHub, Inc. +// Submitted by Patrick Toomey <security@github.com> +githubusercontent.com +githubpreview.dev +github.io + +// GitLab, Inc. +// Submitted by Alex Hanselka <alex@gitlab.com> +gitlab.io + +// Gitplac.si - https://gitplac.si +// Submitted by Aljaž Starc <me@aljaxus.eu> +gitapp.si +gitpage.si + +// Glitch, Inc : https://glitch.com +// Submitted by Mads Hartmann <mads@glitch.com> +glitch.me + +// Global NOG Alliance : https://nogalliance.org/ +// Submitted by Sander Steffann <sander@nogalliance.org> +nog.community + +// Globe Hosting SRL : https://www.globehosting.com/ +// Submitted by Gavin Brown <gavin.brown@centralnic.com> +co.ro +shop.ro + +// GMO Pepabo, Inc. : https://pepabo.com/ +// Submitted by Hosting Div <admin@pepabo.com> +lolipop.io +angry.jp +babyblue.jp +babymilk.jp +backdrop.jp +bambina.jp +bitter.jp +blush.jp +boo.jp +boy.jp +boyfriend.jp +but.jp +candypop.jp +capoo.jp +catfood.jp +cheap.jp +chicappa.jp +chillout.jp +chips.jp +chowder.jp +chu.jp +ciao.jp +cocotte.jp +coolblog.jp +cranky.jp +cutegirl.jp +daa.jp +deca.jp +deci.jp +digick.jp +egoism.jp +fakefur.jp +fem.jp +flier.jp +floppy.jp +fool.jp +frenchkiss.jp +girlfriend.jp +girly.jp +gloomy.jp +gonna.jp +greater.jp +hacca.jp +heavy.jp +her.jp +hiho.jp +hippy.jp +holy.jp +hungry.jp +icurus.jp +itigo.jp +jellybean.jp +kikirara.jp +kill.jp +kilo.jp +kuron.jp +littlestar.jp +lolipopmc.jp +lolitapunk.jp +lomo.jp +lovepop.jp +lovesick.jp +main.jp +mods.jp +mond.jp +mongolian.jp +moo.jp +namaste.jp +nikita.jp +nobushi.jp +noor.jp +oops.jp +parallel.jp +parasite.jp +pecori.jp +peewee.jp +penne.jp +pepper.jp +perma.jp +pigboat.jp +pinoko.jp +punyu.jp +pupu.jp +pussycat.jp +pya.jp +raindrop.jp +readymade.jp +sadist.jp +schoolbus.jp +secret.jp +staba.jp +stripper.jp +sub.jp +sunnyday.jp +thick.jp +tonkotsu.jp +under.jp +upper.jp +velvet.jp +verse.jp +versus.jp +vivian.jp +watson.jp +weblike.jp +whitesnow.jp +zombie.jp +heteml.net + +// GOV.UK Platform as a Service : https://www.cloud.service.gov.uk/ +// Submitted by Tom Whitwell <gov-uk-paas-support@digital.cabinet-office.gov.uk> +cloudapps.digital +london.cloudapps.digital + +// GOV.UK Pay : https://www.payments.service.gov.uk/ +// Submitted by Richard Baker <richard.baker@digital.cabinet-office.gov.uk> +pymnt.uk + +// GlobeHosting, Inc. +// Submitted by Zoltan Egresi <egresi@globehosting.com> +ro.im + +// GoIP DNS Services : http://www.goip.de +// Submitted by Christian Poulter <milchstrasse@goip.de> +goip.de + +// Google, Inc. +// Submitted by Eduardo Vela <evn@google.com> +*.run.app +web.app +*.0emm.com +appspot.com +*.r.appspot.com +codespot.com +googleapis.com +googlecode.com +pagespeedmobilizer.com +publishproxy.com +withgoogle.com +withyoutube.com +*.gateway.dev +cloud.goog +translate.goog +*.usercontent.goog +cloudfunctions.net +blogspot.ae +blogspot.al +blogspot.am +blogspot.ba +blogspot.be +blogspot.bg +blogspot.bj +blogspot.ca +blogspot.cf +blogspot.ch +blogspot.cl +blogspot.co.at +blogspot.co.id +blogspot.co.il +blogspot.co.ke +blogspot.co.nz +blogspot.co.uk +blogspot.co.za +blogspot.com +blogspot.com.ar +blogspot.com.au +blogspot.com.br +blogspot.com.by +blogspot.com.co +blogspot.com.cy +blogspot.com.ee +blogspot.com.eg +blogspot.com.es +blogspot.com.mt +blogspot.com.ng +blogspot.com.tr +blogspot.com.uy +blogspot.cv +blogspot.cz +blogspot.de +blogspot.dk +blogspot.fi +blogspot.fr +blogspot.gr +blogspot.hk +blogspot.hr +blogspot.hu +blogspot.ie +blogspot.in +blogspot.is +blogspot.it +blogspot.jp +blogspot.kr +blogspot.li +blogspot.lt +blogspot.lu +blogspot.md +blogspot.mk +blogspot.mr +blogspot.mx +blogspot.my +blogspot.nl +blogspot.no +blogspot.pe +blogspot.pt +blogspot.qa +blogspot.re +blogspot.ro +blogspot.rs +blogspot.ru +blogspot.se +blogspot.sg +blogspot.si +blogspot.sk +blogspot.sn +blogspot.td +blogspot.tw +blogspot.ug +blogspot.vn + +// Goupile : https://goupile.fr +// Submitted by Niels Martignene <hello@goupile.fr> +goupile.fr + +// Government of the Netherlands: https://www.government.nl +// Submitted by <domeinnaam@minaz.nl> +gov.nl + +// Group 53, LLC : https://www.group53.com +// Submitted by Tyler Todd <noc@nova53.net> +awsmppl.com + +// GünstigBestellen : https://günstigbestellen.de +// Submitted by Furkan Akkoc <info@hendelzon.de> +günstigbestellen.de +günstigliefern.de + +// Hakaran group: http://hakaran.cz +// Submitted by Arseniy Sokolov <security@hakaran.cz> +fin.ci +free.hr +caa.li +ua.rs +conf.se + +// Handshake : https://handshake.org +// Submitted by Mike Damm <md@md.vc> +hs.zone +hs.run + +// Hashbang : https://hashbang.sh +hashbang.sh + +// Hasura : https://hasura.io +// Submitted by Shahidh K Muhammed <shahidh@hasura.io> +hasura.app +hasura-app.io + +// Heilbronn University of Applied Sciences - Faculty Informatics (GitLab Pages): https://www.hs-heilbronn.de +// Submitted by Richard Zowalla <mi-admin@hs-heilbronn.de> +pages.it.hs-heilbronn.de + +// Hepforge : https://www.hepforge.org +// Submitted by David Grellscheid <admin@hepforge.org> +hepforge.org + +// Heroku : https://www.heroku.com/ +// Submitted by Tom Maher <tmaher@heroku.com> +herokuapp.com +herokussl.com + +// Hibernating Rhinos +// Submitted by Oren Eini <oren@ravendb.net> +ravendb.cloud +ravendb.community +ravendb.me +development.run +ravendb.run + +// home.pl S.A.: https://home.pl +// Submitted by Krzysztof Wolski <krzysztof.wolski@home.eu> +homesklep.pl + +// Homebase : https://homebase.id/ +// Submitted by Jason Babo <info@homebase.id> +*.kin.one +*.id.pub +*.kin.pub + +// Hong Kong Productivity Council: https://www.hkpc.org/ +// Submitted by SECaaS Team <summchan@hkpc.org> +secaas.hk + +// Hoplix : https://www.hoplix.com +// Submitted by Danilo De Franco<info@hoplix.shop> +hoplix.shop + + +// HOSTBIP REGISTRY : https://www.hostbip.com/ +// Submitted by Atanunu Igbunuroghene <publicsuffixlist@hostbip.com> +orx.biz +biz.gl +col.ng +firm.ng +gen.ng +ltd.ng +ngo.ng +edu.scot +sch.so + +// HostFly : https://www.ie.ua +// Submitted by Bohdan Dub <support@hostfly.com.ua> +ie.ua + +// HostyHosting (hostyhosting.com) +hostyhosting.io + +// Häkkinen.fi +// Submitted by Eero Häkkinen <Eero+psl@Häkkinen.fi> +häkkinen.fi + +// Ici la Lune : http://www.icilalune.com/ +// Submitted by Simon Morvan <simon@icilalune.com> +*.moonscale.io +moonscale.net + +// iki.fi +// Submitted by Hannu Aronsson <haa@iki.fi> +iki.fi + +// iliad italia: https://www.iliad.it +// Submitted by Marios Makassikis <mmakassikis@freebox.fr> +ibxos.it +iliadboxos.it + +// Impertrix Solutions : <https://impertrixcdn.com> +// Submitted by Zhixiang Zhao <csuite@impertrix.com> +impertrixcdn.com +impertrix.com + +// Incsub, LLC: https://incsub.com/ +// Submitted by Aaron Edwards <sysadmins@incsub.com> +smushcdn.com +wphostedmail.com +wpmucdn.com +tempurl.host +wpmudev.host + +// Individual Network Berlin e.V. : https://www.in-berlin.de/ +// Submitted by Christian Seitz <chris@in-berlin.de> +dyn-berlin.de +in-berlin.de +in-brb.de +in-butter.de +in-dsl.de +in-dsl.net +in-dsl.org +in-vpn.de +in-vpn.net +in-vpn.org + +// info.at : http://www.info.at/ +biz.at +info.at + +// info.cx : http://info.cx +// Submitted by Jacob Slater <whois@igloo.to> +info.cx + +// Interlegis : http://www.interlegis.leg.br +// Submitted by Gabriel Ferreira <registrobr@interlegis.leg.br> +ac.leg.br +al.leg.br +am.leg.br +ap.leg.br +ba.leg.br +ce.leg.br +df.leg.br +es.leg.br +go.leg.br +ma.leg.br +mg.leg.br +ms.leg.br +mt.leg.br +pa.leg.br +pb.leg.br +pe.leg.br +pi.leg.br +pr.leg.br +rj.leg.br +rn.leg.br +ro.leg.br +rr.leg.br +rs.leg.br +sc.leg.br +se.leg.br +sp.leg.br +to.leg.br + +// intermetrics GmbH : https://pixolino.com/ +// Submitted by Wolfgang Schwarz <admin@intermetrics.de> +pixolino.com + +// Internet-Pro, LLP: https://netangels.ru/ +// Submitted by Vasiliy Sheredeko <piphon@gmail.com> +na4u.ru + +// iopsys software solutions AB : https://iopsys.eu/ +// Submitted by Roman Azarenko <roman.azarenko@iopsys.eu> +iopsys.se + +// IPiFony Systems, Inc. : https://www.ipifony.com/ +// Submitted by Matthew Hardeman <mhardeman@ipifony.com> +ipifony.net + +// IServ GmbH : https://iserv.de +// Submitted by Mario Hoberg <info@iserv.de> +iservschule.de +mein-iserv.de +schulplattform.de +schulserver.de +test-iserv.de +iserv.dev + +// I-O DATA DEVICE, INC. : http://www.iodata.com/ +// Submitted by Yuji Minagawa <domains-admin@iodata.jp> +iobb.net + +// Jelastic, Inc. : https://jelastic.com/ +// Submitted by Ihor Kolodyuk <ik@jelastic.com> +mel.cloudlets.com.au +cloud.interhostsolutions.be +mycloud.by +alp1.ae.flow.ch +appengine.flow.ch +es-1.axarnet.cloud +diadem.cloud +vip.jelastic.cloud +jele.cloud +it1.eur.aruba.jenv-aruba.cloud +it1.jenv-aruba.cloud +keliweb.cloud +cs.keliweb.cloud +oxa.cloud +tn.oxa.cloud +uk.oxa.cloud +primetel.cloud +uk.primetel.cloud +ca.reclaim.cloud +uk.reclaim.cloud +us.reclaim.cloud +ch.trendhosting.cloud +de.trendhosting.cloud +jele.club +amscompute.com +dopaas.com +paas.hosted-by-previder.com +rag-cloud.hosteur.com +rag-cloud-ch.hosteur.com +jcloud.ik-server.com +jcloud-ver-jpc.ik-server.com +demo.jelastic.com +kilatiron.com +paas.massivegrid.com +jed.wafaicloud.com +lon.wafaicloud.com +ryd.wafaicloud.com +j.scaleforce.com.cy +jelastic.dogado.eu +fi.cloudplatform.fi +demo.datacenter.fi +paas.datacenter.fi +jele.host +mircloud.host +paas.beebyte.io +sekd1.beebyteapp.io +jele.io +cloud-fr1.unispace.io +jc.neen.it +cloud.jelastic.open.tim.it +jcloud.kz +upaas.kazteleport.kz +cloudjiffy.net +fra1-de.cloudjiffy.net +west1-us.cloudjiffy.net +jls-sto1.elastx.net +jls-sto2.elastx.net +jls-sto3.elastx.net +faststacks.net +fr-1.paas.massivegrid.net +lon-1.paas.massivegrid.net +lon-2.paas.massivegrid.net +ny-1.paas.massivegrid.net +ny-2.paas.massivegrid.net +sg-1.paas.massivegrid.net +jelastic.saveincloud.net +nordeste-idc.saveincloud.net +j.scaleforce.net +jelastic.tsukaeru.net +sdscloud.pl +unicloud.pl +mircloud.ru +jelastic.regruhosting.ru +enscaled.sg +jele.site +jelastic.team +orangecloud.tn +j.layershift.co.uk +phx.enscaled.us +mircloud.us + +// Jino : https://www.jino.ru +// Submitted by Sergey Ulyashin <ulyashin@jino.ru> +myjino.ru +*.hosting.myjino.ru +*.landing.myjino.ru +*.spectrum.myjino.ru +*.vps.myjino.ru + +// Jotelulu S.L. : https://jotelulu.com +// Submitted by Daniel Fariña <ingenieria@jotelulu.com> +jotelulu.cloud + +// Joyent : https://www.joyent.com/ +// Submitted by Brian Bennett <brian.bennett@joyent.com> +*.triton.zone +*.cns.joyent.com + +// JS.ORG : http://dns.js.org +// Submitted by Stefan Keim <admin@js.org> +js.org + +// KaasHosting : http://www.kaashosting.nl/ +// Submitted by Wouter Bakker <hostmaster@kaashosting.nl> +kaas.gg +khplay.nl + +// Kakao : https://www.kakaocorp.com/ +// Submitted by JaeYoong Lee <cec@kakaocorp.com> +ktistory.com + +// Kapsi : https://kapsi.fi +// Submitted by Tomi Juntunen <erani@kapsi.fi> +kapsi.fi + +// Keyweb AG : https://www.keyweb.de +// Submitted by Martin Dannehl <postmaster@keymachine.de> +keymachine.de + +// KingHost : https://king.host +// Submitted by Felipe Keller Braz <felipebraz@kinghost.com.br> +kinghost.net +uni5.net + +// KnightPoint Systems, LLC : http://www.knightpoint.com/ +// Submitted by Roy Keene <rkeene@knightpoint.com> +knightpoint.systems + +// KoobinEvent, SL: https://www.koobin.com +// Submitted by Iván Oliva <ivan.oliva@koobin.com> +koobin.events + +// KUROKU LTD : https://kuroku.ltd/ +// Submitted by DisposaBoy <security@oya.to> +oya.to + +// Katholieke Universiteit Leuven: https://www.kuleuven.be +// Submitted by Abuse KU Leuven <abuse@kuleuven.be> +kuleuven.cloud +ezproxy.kuleuven.be + +// .KRD : http://nic.krd/data/krd/Registration%20Policy.pdf +co.krd +edu.krd + +// Krellian Ltd. : https://krellian.com +// Submitted by Ben Francis <ben@krellian.com> +krellian.net +webthings.io + +// LCube - Professional hosting e.K. : https://www.lcube-webhosting.de +// Submitted by Lars Laehn <info@lcube.de> +git-repos.de +lcube-server.de +svn-repos.de + +// Leadpages : https://www.leadpages.net +// Submitted by Greg Dallavalle <domains@leadpages.net> +leadpages.co +lpages.co +lpusercontent.com + +// Lelux.fi : https://lelux.fi/ +// Submitted by Lelux Admin <publisuffix@lelux.site> +lelux.site + +// Lifetime Hosting : https://Lifetime.Hosting/ +// Submitted by Mike Fillator <support@lifetime.hosting> +co.business +co.education +co.events +co.financial +co.network +co.place +co.technology + +// Lightmaker Property Manager, Inc. : https://app.lmpm.com/ +// Submitted by Greg Holland <greg.holland@lmpm.com> +app.lmpm.com + +// linkyard ldt: https://www.linkyard.ch/ +// Submitted by Mario Siegenthaler <mario.siegenthaler@linkyard.ch> +linkyard.cloud +linkyard-cloud.ch + +// Linode : https://linode.com +// Submitted by <security@linode.com> +members.linode.com +*.nodebalancer.linode.com +*.linodeobjects.com +ip.linodeusercontent.com + +// LiquidNet Ltd : http://www.liquidnetlimited.com/ +// Submitted by Victor Velchev <admin@liquidnetlimited.com> +we.bs + +// Localcert : https://localcert.dev +// Submitted by Lann Martin <security@localcert.dev> +*.user.localcert.dev + +// localzone.xyz +// Submitted by Kenny Niehage <hello@yahe.sh> +localzone.xyz + +// Log'in Line : https://www.loginline.com/ +// Submitted by Rémi Mach <remi.mach@loginline.com> +loginline.app +loginline.dev +loginline.io +loginline.services +loginline.site + +// Lokalized : https://lokalized.nl +// Submitted by Noah Taheij <noah@lokalized.nl> +servers.run + +// Lõhmus Family, The +// Submitted by Heiki Lõhmus <hostmaster at lohmus dot me> +lohmus.me + +// LubMAN UMCS Sp. z o.o : https://lubman.pl/ +// Submitted by Ireneusz Maliszewski <ireneusz.maliszewski@lubman.pl> +krasnik.pl +leczna.pl +lubartow.pl +lublin.pl +poniatowa.pl +swidnik.pl + +// Lug.org.uk : https://lug.org.uk +// Submitted by Jon Spriggs <admin@lug.org.uk> +glug.org.uk +lug.org.uk +lugs.org.uk + +// Lukanet Ltd : https://lukanet.com +// Submitted by Anton Avramov <register@lukanet.com> +barsy.bg +barsy.co.uk +barsyonline.co.uk +barsycenter.com +barsyonline.com +barsy.club +barsy.de +barsy.eu +barsy.in +barsy.info +barsy.io +barsy.me +barsy.menu +barsy.mobi +barsy.net +barsy.online +barsy.org +barsy.pro +barsy.pub +barsy.ro +barsy.shop +barsy.site +barsy.support +barsy.uk + +// Magento Commerce +// Submitted by Damien Tournoud <dtournoud@magento.cloud> +*.magentosite.cloud + +// May First - People Link : https://mayfirst.org/ +// Submitted by Jamie McClelland <info@mayfirst.org> +mayfirst.info +mayfirst.org + +// Mail.Ru Group : https://hb.cldmail.ru +// Submitted by Ilya Zaretskiy <zaretskiy@corp.mail.ru> +hb.cldmail.ru + +// Mail Transfer Platform : https://www.neupeer.com +// Submitted by Li Hui <lihui@neupeer.com> +cn.vu + +// Maze Play: https://www.mazeplay.com +// Submitted by Adam Humpherys <adam@mws.dev> +mazeplay.com + +// mcpe.me : https://mcpe.me +// Submitted by Noa Heyl <hi@noa.dev> +mcpe.me + +// McHost : https://mchost.ru +// Submitted by Evgeniy Subbotin <e.subbotin@mchost.ru> +mcdir.me +mcdir.ru +mcpre.ru +vps.mcdir.ru + +// Mediatech : https://mediatech.by +// Submitted by Evgeniy Kozhuhovskiy <ugenk@mediatech.by> +mediatech.by +mediatech.dev + +// Medicom Health : https://medicomhealth.com +// Submitted by Michael Olson <molson@medicomhealth.com> +hra.health + +// Memset hosting : https://www.memset.com +// Submitted by Tom Whitwell <domains@memset.com> +miniserver.com +memset.net + +// Messerli Informatik AG : https://www.messerli.ch/ +// Submitted by Ruben Schmidmeister <psl-maintainers@messerli.ch> +messerli.app + +// Meta Platforms, Inc. : https://meta.com/ +// Submitted by Jacob Cordero <public-suffix@meta.com> +atmeta.com +apps.fbsbx.com + +// MetaCentrum, CESNET z.s.p.o. : https://www.metacentrum.cz/en/ +// Submitted by Zdeněk Šustr <zdenek.sustr@cesnet.cz> +*.cloud.metacentrum.cz +custom.metacentrum.cz + +// MetaCentrum, CESNET z.s.p.o. : https://www.metacentrum.cz/en/ +// Submitted by Radim Janča <janca@cesnet.cz> +flt.cloud.muni.cz +usr.cloud.muni.cz + +// Meteor Development Group : https://www.meteor.com/hosting +// Submitted by Pierre Carrier <pierre@meteor.com> +meteorapp.com +eu.meteorapp.com + +// Michau Enterprises Limited : http://www.co.pl/ +co.pl + +// Microsoft Corporation : http://microsoft.com +// Submitted by Public Suffix List Admin <msftpsladmin@microsoft.com> +// Managed by Corporate Domains +// Microsoft Azure : https://home.azure +*.azurecontainer.io +*.cloudapp.azure.com +azure-api.net +azureedge.net +azurefd.net +azurewebsites.net +azure-mobile.net +azurestaticapps.net +1.azurestaticapps.net +2.azurestaticapps.net +3.azurestaticapps.net +4.azurestaticapps.net +5.azurestaticapps.net +6.azurestaticapps.net +7.azurestaticapps.net +centralus.azurestaticapps.net +eastasia.azurestaticapps.net +eastus2.azurestaticapps.net +westeurope.azurestaticapps.net +westus2.azurestaticapps.net +cloudapp.net +trafficmanager.net +blob.core.windows.net +servicebus.windows.net + +// minion.systems : http://minion.systems +// Submitted by Robert Böttinger <r@minion.systems> +csx.cc + +// Mintere : https://mintere.com/ +// Submitted by Ben Aubin <security@mintere.com> +mintere.site + +// MobileEducation, LLC : https://joinforte.com +// Submitted by Grayson Martin <grayson.martin@mobileeducation.us> +forte.id + +// MODX Systems LLC : https://modx.com +// Submitted by Elizabeth Southwell <elizabeth@modx.com> +modx.dev + +// Mozilla Corporation : https://mozilla.com +// Submitted by Ben Francis <bfrancis@mozilla.com> +mozilla-iot.org + +// Mozilla Foundation : https://mozilla.org/ +// Submitted by glob <glob@mozilla.com> +bmoattachments.org + +// MSK-IX : https://www.msk-ix.ru/ +// Submitted by Khannanov Roman <r.khannanov@msk-ix.ru> +net.ru +org.ru +pp.ru + +// Mythic Beasts : https://www.mythic-beasts.com +// Submitted by Paul Cammish <kelduum@mythic-beasts.com> +hostedpi.com +customer.mythic-beasts.com +caracal.mythic-beasts.com +fentiger.mythic-beasts.com +lynx.mythic-beasts.com +ocelot.mythic-beasts.com +oncilla.mythic-beasts.com +onza.mythic-beasts.com +sphinx.mythic-beasts.com +vs.mythic-beasts.com +x.mythic-beasts.com +yali.mythic-beasts.com +cust.retrosnub.co.uk + +// Nabu Casa : https://www.nabucasa.com +// Submitted by Paulus Schoutsen <infra@nabucasa.com> +ui.nabu.casa + +// Net at Work Gmbh : https://www.netatwork.de +// Submitted by Jan Jaeschke <jan.jaeschke@netatwork.de> +cloud.nospamproxy.com + +// Netlify : https://www.netlify.com +// Submitted by Jessica Parsons <jessica@netlify.com> +netlify.app + +// Neustar Inc. +// Submitted by Trung Tran <Trung.Tran@neustar.biz> +4u.com + +// ngrok : https://ngrok.com/ +// Submitted by Alan Shreve <alan@ngrok.com> +ngrok.app +ngrok-free.app +ngrok.dev +ngrok-free.dev +ngrok.io +ap.ngrok.io +au.ngrok.io +eu.ngrok.io +in.ngrok.io +jp.ngrok.io +sa.ngrok.io +us.ngrok.io +ngrok.pizza +ngrok.pro + +// Nicolaus Copernicus University in Torun - MSK TORMAN (https://www.man.torun.pl) +torun.pl + +// Nimbus Hosting Ltd. : https://www.nimbushosting.co.uk/ +// Submitted by Nicholas Ford <nick@nimbushosting.co.uk> +nh-serv.co.uk + +// NFSN, Inc. : https://www.NearlyFreeSpeech.NET/ +// Submitted by Jeff Wheelhouse <support@nearlyfreespeech.net> +nfshost.com + +// Noop : https://noop.app +// Submitted by Nathaniel Schweinberg <noop@rearc.io> +*.developer.app +noop.app + +// Northflank Ltd. : https://northflank.com/ +// Submitted by Marco Suter <marco@northflank.com> +*.northflank.app +*.build.run +*.code.run +*.database.run +*.migration.run + +// Noticeable : https://noticeable.io +// Submitted by Laurent Pellegrino <security@noticeable.io> +noticeable.news + +// Now-DNS : https://now-dns.com +// Submitted by Steve Russell <steve@now-dns.com> +dnsking.ch +mypi.co +n4t.co +001www.com +ddnslive.com +myiphost.com +forumz.info +16-b.it +32-b.it +64-b.it +soundcast.me +tcp4.me +dnsup.net +hicam.net +now-dns.net +ownip.net +vpndns.net +dynserv.org +now-dns.org +x443.pw +now-dns.top +ntdll.top +freeddns.us +crafting.xyz +zapto.xyz + +// nsupdate.info : https://www.nsupdate.info/ +// Submitted by Thomas Waldmann <info@nsupdate.info> +nsupdate.info +nerdpol.ovh + +// No-IP.com : https://noip.com/ +// Submitted by Deven Reza <publicsuffixlist@noip.com> +blogsyte.com +brasilia.me +cable-modem.org +ciscofreak.com +collegefan.org +couchpotatofries.org +damnserver.com +ddns.me +ditchyourip.com +dnsfor.me +dnsiskinky.com +dvrcam.info +dynns.com +eating-organic.net +fantasyleague.cc +geekgalaxy.com +golffan.us +health-carereform.com +homesecuritymac.com +homesecuritypc.com +hopto.me +ilovecollege.info +loginto.me +mlbfan.org +mmafan.biz +myactivedirectory.com +mydissent.net +myeffect.net +mymediapc.net +mypsx.net +mysecuritycamera.com +mysecuritycamera.net +mysecuritycamera.org +net-freaks.com +nflfan.org +nhlfan.net +no-ip.ca +no-ip.co.uk +no-ip.net +noip.us +onthewifi.com +pgafan.net +point2this.com +pointto.us +privatizehealthinsurance.net +quicksytes.com +read-books.org +securitytactics.com +serveexchange.com +servehumour.com +servep2p.com +servesarcasm.com +stufftoread.com +ufcfan.org +unusualperson.com +workisboring.com +3utilities.com +bounceme.net +ddns.net +ddnsking.com +gotdns.ch +hopto.org +myftp.biz +myftp.org +myvnc.com +no-ip.biz +no-ip.info +no-ip.org +noip.me +redirectme.net +servebeer.com +serveblog.net +servecounterstrike.com +serveftp.com +servegame.com +servehalflife.com +servehttp.com +serveirc.com +serveminecraft.net +servemp3.com +servepics.com +servequake.com +sytes.net +webhop.me +zapto.org + +// NodeArt : https://nodeart.io +// Submitted by Konstantin Nosov <Nosov@nodeart.io> +stage.nodeart.io + +// Nucleos Inc. : https://nucleos.com +// Submitted by Piotr Zduniak <piotr@nucleos.com> +pcloud.host + +// NYC.mn : http://www.information.nyc.mn +// Submitted by Matthew Brown <mattbrown@nyc.mn> +nyc.mn + +// Observable, Inc. : https://observablehq.com +// Submitted by Mike Bostock <dns@observablehq.com> +static.observableusercontent.com + +// Octopodal Solutions, LLC. : https://ulterius.io/ +// Submitted by Andrew Sampson <andrew@ulterius.io> +cya.gg + +// OMG.LOL : <https://omg.lol> +// Submitted by Adam Newbold <adam@omg.lol> +omg.lol + +// Omnibond Systems, LLC. : https://www.omnibond.com +// Submitted by Cole Estep <cole@omnibond.com> +cloudycluster.net + +// OmniWe Limited: https://omniwe.com +// Submitted by Vicary Archangel <vicary@omniwe.com> +omniwe.site + +// One.com: https://www.one.com/ +// Submitted by Jacob Bunk Nielsen <jbn@one.com> +123hjemmeside.dk +123hjemmeside.no +123homepage.it +123kotisivu.fi +123minsida.se +123miweb.es +123paginaweb.pt +123sait.ru +123siteweb.fr +123webseite.at +123webseite.de +123website.be +123website.ch +123website.lu +123website.nl +service.one +simplesite.com +simplesite.com.br +simplesite.gr +simplesite.pl + +// One Fold Media : http://www.onefoldmedia.com/ +// Submitted by Eddie Jones <eddie@onefoldmedia.com> +nid.io + +// Open Social : https://www.getopensocial.com/ +// Submitted by Alexander Varwijk <security@getopensocial.com> +opensocial.site + +// OpenCraft GmbH : http://opencraft.com/ +// Submitted by Sven Marnach <sven@opencraft.com> +opencraft.hosting + +// OpenResearch GmbH: https://openresearch.com/ +// Submitted by Philipp Schmid <ops@openresearch.com> +orsites.com + +// Opera Software, A.S.A. +// Submitted by Yngve Pettersen <yngve@opera.com> +operaunite.com + +// Orange : https://www.orange.com +// Submitted by Alexandre Linte <alexandre.linte@orange.com> +tech.orange + +// Oursky Limited : https://authgear.com/, https://skygear.io/ +// Submitted by Authgear Team <hello@authgear.com>, Skygear Developer <hello@skygear.io> +authgear-staging.com +authgearapps.com +skygearapp.com + +// OutSystems +// Submitted by Duarte Santos <domain-admin@outsystemscloud.com> +outsystemscloud.com + +// OVHcloud: https://ovhcloud.com +// Submitted by Vincent Cassé <vincent.casse@ovhcloud.com> +*.webpaas.ovh.net +*.hosting.ovh.net + +// OwnProvider GmbH: http://www.ownprovider.com +// Submitted by Jan Moennich <jan.moennich@ownprovider.com> +ownprovider.com +own.pm + +// OwO : https://whats-th.is/ +// Submitted by Dean Sheather <dean@deansheather.com> +*.owo.codes + +// OX : http://www.ox.rs +// Submitted by Adam Grand <webmaster@mail.ox.rs> +ox.rs + +// oy.lc +// Submitted by Charly Coste <changaco@changaco.oy.lc> +oy.lc + +// Pagefog : https://pagefog.com/ +// Submitted by Derek Myers <derek@pagefog.com> +pgfog.com + +// Pagefront : https://www.pagefronthq.com/ +// Submitted by Jason Kriss <jason@pagefronthq.com> +pagefrontapp.com + +// PageXL : https://pagexl.com +// Submitted by Yann Guichard <yann@pagexl.com> +pagexl.com + +// Paywhirl, Inc : https://paywhirl.com/ +// Submitted by Daniel Netzer <dan@paywhirl.com> +*.paywhirl.com + +// pcarrier.ca Software Inc: https://pcarrier.ca/ +// Submitted by Pierre Carrier <pc@rrier.ca> +bar0.net +bar1.net +bar2.net +rdv.to + +// .pl domains (grandfathered) +art.pl +gliwice.pl +krakow.pl +poznan.pl +wroc.pl +zakopane.pl + +// Pantheon Systems, Inc. : https://pantheon.io/ +// Submitted by Gary Dylina <gary@pantheon.io> +pantheonsite.io +gotpantheon.com + +// Peplink | Pepwave : http://peplink.com/ +// Submitted by Steve Leung <steveleung@peplink.com> +mypep.link + +// Perspecta : https://perspecta.com/ +// Submitted by Kenneth Van Alstyne <kvanalstyne@perspecta.com> +perspecta.cloud + +// PE Ulyanov Kirill Sergeevich : https://airy.host +// Submitted by Kirill Ulyanov <k.ulyanov@airy.host> +lk3.ru + +// Planet-Work : https://www.planet-work.com/ +// Submitted by Frédéric VANNIÈRE <f.vanniere@planet-work.com> +on-web.fr + +// Platform.sh : https://platform.sh +// Submitted by Nikola Kotur <nikola@platform.sh> +*.upsun.app +upsunapp.com +ent.platform.sh +eu.platform.sh +us.platform.sh +*.platformsh.site +*.tst.site + +// Platter: https://platter.dev +// Submitted by Patrick Flor <patrick@platter.dev> +platter-app.com +platter-app.dev +platterp.us + +// Plesk : https://www.plesk.com/ +// Submitted by Anton Akhtyamov <program-managers@plesk.com> +pdns.page +plesk.page +pleskns.com + +// Pley AB : https://www.pley.com/ +// Submitted by Henning Pohl <infra@pley.com> +pley.games + +// Port53 : https://port53.io/ +// Submitted by Maximilian Schieder <maxi@zeug.co> +dyn53.io + +// Porter : https://porter.run/ +// Submitted by Rudraksh MK <rudi@porter.run> +onporter.run + +// Positive Codes Technology Company : http://co.bn/faq.html +// Submitted by Zulfais <pc@co.bn> +co.bn + +// Postman, Inc : https://postman.com +// Submitted by Rahul Dhawan <security@postman.com> +postman-echo.com +pstmn.io +mock.pstmn.io +httpbin.org + +//prequalifyme.today : https://prequalifyme.today +//Submitted by DeepakTiwari deepak@ivylead.io +prequalifyme.today + +// prgmr.com : https://prgmr.com/ +// Submitted by Sarah Newman <owner@prgmr.com> +xen.prgmr.com + +// priv.at : http://www.nic.priv.at/ +// Submitted by registry <lendl@nic.at> +priv.at + +// privacytools.io : https://www.privacytools.io/ +// Submitted by Jonah Aragon <jonah@privacytools.io> +prvcy.page + +// Protocol Labs : https://protocol.ai/ +// Submitted by Michael Burns <noc@protocol.ai> +*.dweb.link + +// Protonet GmbH : http://protonet.io +// Submitted by Martin Meier <admin@protonet.io> +protonet.io + +// Publication Presse Communication SARL : https://ppcom.fr +// Submitted by Yaacov Akiba Slama <admin@chirurgiens-dentistes-en-france.fr> +chirurgiens-dentistes-en-france.fr +byen.site + +// pubtls.org: https://www.pubtls.org +// Submitted by Kor Nielsen <kor@pubtls.org> +pubtls.org + +// PythonAnywhere LLP: https://www.pythonanywhere.com +// Submitted by Giles Thomas <giles@pythonanywhere.com> +pythonanywhere.com +eu.pythonanywhere.com + +// QOTO, Org. +// Submitted by Jeffrey Phillips Freeman <jeffrey.freeman@qoto.org> +qoto.io + +// Qualifio : https://qualifio.com/ +// Submitted by Xavier De Cock <xdecock@gmail.com> +qualifioapp.com + +// Quality Unit: https://qualityunit.com +// Submitted by Vasyl Tsalko <vtsalko@qualityunit.com> +ladesk.com + +// QuickBackend: https://www.quickbackend.com +// Submitted by Dani Biro <dani@pymet.com> +qbuser.com + +// Rad Web Hosting: https://radwebhosting.com +// Submitted by Scott Claeys <s.claeys@radwebhosting.com> +cloudsite.builders +myradweb.net +servername.us + +// Redgate Software: https://red-gate.com +// Submitted by Andrew Farries <andrew.farries@red-gate.com> +instances.spawn.cc + +// Redstar Consultants : https://www.redstarconsultants.com/ +// Submitted by Jons Slemmer <jons@redstarconsultants.com> +instantcloud.cn + +// Russian Academy of Sciences +// Submitted by Tech Support <support@rasnet.ru> +ras.ru + +// QA2 +// Submitted by Daniel Dent (https://www.danieldent.com/) +qa2.com + +// QCX +// Submitted by Cassandra Beelen <cassandra@beelen.one> +qcx.io +*.sys.qcx.io + +// QNAP System Inc : https://www.qnap.com +// Submitted by Nick Chang <nickchang@qnap.com> +dev-myqnapcloud.com +alpha-myqnapcloud.com +myqnapcloud.com + +// Quip : https://quip.com +// Submitted by Patrick Linehan <plinehan@quip.com> +*.quipelements.com + +// Qutheory LLC : http://qutheory.io +// Submitted by Jonas Schwartz <jonas@qutheory.io> +vapor.cloud +vaporcloud.io + +// Rackmaze LLC : https://www.rackmaze.com +// Submitted by Kirill Pertsev <kika@rackmaze.com> +rackmaze.com +rackmaze.net + +// Rakuten Games, Inc : https://dev.viberplay.io +// Submitted by Joshua Zhang <public-suffix@rgames.jp> +g.vbrplsbx.io + +// Rancher Labs, Inc : https://rancher.com +// Submitted by Vincent Fiduccia <domains@rancher.com> +*.on-k3s.io +*.on-rancher.cloud +*.on-rio.io + +// Read The Docs, Inc : https://www.readthedocs.org +// Submitted by David Fischer <team@readthedocs.org> +readthedocs.io + +// Red Hat, Inc. OpenShift : https://openshift.redhat.com/ +// Submitted by Tim Kramer <tkramer@rhcloud.com> +rhcloud.com + +// Render : https://render.com +// Submitted by Anurag Goel <dev@render.com> +app.render.com +onrender.com + +// Repl.it : https://repl.it +// Submitted by Lincoln Bergeson <psl@repl.it> +replit.app +id.replit.app +firewalledreplit.co +id.firewalledreplit.co +repl.co +id.repl.co +replit.dev +archer.replit.dev +bones.replit.dev +canary.replit.dev +global.replit.dev +hacker.replit.dev +id.replit.dev +janeway.replit.dev +kim.replit.dev +kira.replit.dev +kirk.replit.dev +odo.replit.dev +paris.replit.dev +picard.replit.dev +pike.replit.dev +prerelease.replit.dev +reed.replit.dev +riker.replit.dev +sisko.replit.dev +spock.replit.dev +staging.replit.dev +sulu.replit.dev +tarpit.replit.dev +teams.replit.dev +tucker.replit.dev +wesley.replit.dev +worf.replit.dev +repl.run + +// Resin.io : https://resin.io +// Submitted by Tim Perry <tim@resin.io> +resindevice.io +devices.resinstaging.io + +// RethinkDB : https://www.rethinkdb.com/ +// Submitted by Chris Kastorff <info@rethinkdb.com> +hzc.io + +// Revitalised Limited : http://www.revitalised.co.uk +// Submitted by Jack Price <jack@revitalised.co.uk> +wellbeingzone.eu +wellbeingzone.co.uk + +// Rico Developments Limited : https://adimo.co +// Submitted by Colin Brown <hello@adimo.co> +adimo.co.uk + +// Riseup Networks : https://riseup.net +// Submitted by Micah Anderson <micah@riseup.net> +itcouldbewor.se + +// Rochester Institute of Technology : http://www.rit.edu/ +// Submitted by Jennifer Herting <jchits@rit.edu> +git-pages.rit.edu + +// Rocky Enterprise Software Foundation : https://resf.org +// Submitted by Neil Hanlon <neil@resf.org> +rocky.page + +// Rusnames Limited: http://rusnames.ru/ +// Submitted by Sergey Zotov <admin@rusnames.ru> +биз.рус +ком.рус +крым.рус +мир.рус +мск.рус +орг.рус +самара.рус +сочи.рус +спб.рус +я.рус + +// SAKURA Internet Inc. : https://www.sakura.ad.jp/ +// Submitted by Internet Service Department <rs-vendor-ml@sakura.ad.jp> +180r.com +dojin.com +sakuratan.com +sakuraweb.com +x0.com +2-d.jp +bona.jp +crap.jp +daynight.jp +eek.jp +flop.jp +halfmoon.jp +jeez.jp +matrix.jp +mimoza.jp +ivory.ne.jp +mail-box.ne.jp +mints.ne.jp +mokuren.ne.jp +opal.ne.jp +sakura.ne.jp +sumomo.ne.jp +topaz.ne.jp +netgamers.jp +nyanta.jp +o0o0.jp +rdy.jp +rgr.jp +rulez.jp +s3.isk01.sakurastorage.jp +s3.isk02.sakurastorage.jp +saloon.jp +sblo.jp +skr.jp +tank.jp +uh-oh.jp +undo.jp +rs.webaccel.jp +user.webaccel.jp +websozai.jp +xii.jp +squares.net +jpn.org +kirara.st +x0.to +from.tv +sakura.tv + +// Salesforce.com, Inc. https://salesforce.com/ +// Submitted by Michael Biven <mbiven@salesforce.com> and Aaron Romeo <aaron.romeo@salesforce.com> +*.builder.code.com +*.dev-builder.code.com +*.stg-builder.code.com +*.001.test.code-builder-stg.platform.salesforce.com + +// Sandstorm Development Group, Inc. : https://sandcats.io/ +// Submitted by Asheesh Laroia <asheesh@sandstorm.io> +sandcats.io + +// SBE network solutions GmbH : https://www.sbe.de/ +// Submitted by Norman Meilick <nm@sbe.de> +logoip.de +logoip.com + +// Scaleway : https://www.scaleway.com/ +// Submitted by Rémy Léone <rleone@scaleway.com> +fr-par-1.baremetal.scw.cloud +fr-par-2.baremetal.scw.cloud +nl-ams-1.baremetal.scw.cloud +cockpit.fr-par.scw.cloud +fnc.fr-par.scw.cloud +functions.fnc.fr-par.scw.cloud +k8s.fr-par.scw.cloud +nodes.k8s.fr-par.scw.cloud +s3.fr-par.scw.cloud +s3-website.fr-par.scw.cloud +whm.fr-par.scw.cloud +priv.instances.scw.cloud +pub.instances.scw.cloud +k8s.scw.cloud +cockpit.nl-ams.scw.cloud +k8s.nl-ams.scw.cloud +nodes.k8s.nl-ams.scw.cloud +s3.nl-ams.scw.cloud +s3-website.nl-ams.scw.cloud +whm.nl-ams.scw.cloud +cockpit.pl-waw.scw.cloud +k8s.pl-waw.scw.cloud +nodes.k8s.pl-waw.scw.cloud +s3.pl-waw.scw.cloud +s3-website.pl-waw.scw.cloud +scalebook.scw.cloud +smartlabeling.scw.cloud +dedibox.fr + +// schokokeks.org GbR : https://schokokeks.org/ +// Submitted by Hanno Böck <hanno@schokokeks.org> +schokokeks.net + +// Scottish Government: https://www.gov.scot +// Submitted by Martin Ellis <martin.ellis@gov.scot> +gov.scot +service.gov.scot + +// Scry Security : http://www.scrysec.com +// Submitted by Shante Adam <shante@skyhat.io> +scrysec.com + +// Securepoint GmbH : https://www.securepoint.de +// Submitted by Erik Anders <erik.anders@securepoint.de> +firewall-gateway.com +firewall-gateway.de +my-gateway.de +my-router.de +spdns.de +spdns.eu +firewall-gateway.net +my-firewall.org +myfirewall.org +spdns.org + +// Seidat : https://www.seidat.com +// Submitted by Artem Kondratev <accounts@seidat.com> +seidat.net + +// Sellfy : https://sellfy.com +// Submitted by Yuriy Romadin <contact@sellfy.com> +sellfy.store + +// Senseering GmbH : https://www.senseering.de +// Submitted by Felix Mönckemeyer <f.moenckemeyer@senseering.de> +senseering.net + +// Sendmsg: https://www.sendmsg.co.il +// Submitted by Assaf Stern <domains@comstar.co.il> +minisite.ms + +// Service Magnet : https://myservicemagnet.com +// Submitted by Dave Sanders <dave@myservicemagnet.com> +magnet.page + +// Service Online LLC : http://drs.ua/ +// Submitted by Serhii Bulakh <support@drs.ua> +biz.ua +co.ua +pp.ua + +// Shift Crypto AG : https://shiftcrypto.ch +// Submitted by alex <alex@shiftcrypto.ch> +shiftcrypto.dev +shiftcrypto.io + +// ShiftEdit : https://shiftedit.net/ +// Submitted by Adam Jimenez <adam@shiftcreate.com> +shiftedit.io + +// Shopblocks : http://www.shopblocks.com/ +// Submitted by Alex Bowers <alex@shopblocks.com> +myshopblocks.com + +// Shopify : https://www.shopify.com +// Submitted by Alex Richter <alex.richter@shopify.com> +myshopify.com + +// Shopit : https://www.shopitcommerce.com/ +// Submitted by Craig McMahon <craig@shopitcommerce.com> +shopitsite.com + +// shopware AG : https://shopware.com +// Submitted by Jens Küper <cloud@shopware.com> +shopware.store + +// Siemens Mobility GmbH +// Submitted by Oliver Graebner <security@mo-siemens.io> +mo-siemens.io + +// SinaAppEngine : http://sae.sina.com.cn/ +// Submitted by SinaAppEngine <saesupport@sinacloud.com> +1kapp.com +appchizi.com +applinzi.com +sinaapp.com +vipsinaapp.com + +// Siteleaf : https://www.siteleaf.com/ +// Submitted by Skylar Challand <support@siteleaf.com> +siteleaf.net + +// Skyhat : http://www.skyhat.io +// Submitted by Shante Adam <shante@skyhat.io> +bounty-full.com +alpha.bounty-full.com +beta.bounty-full.com + +// Smallregistry by Promopixel SARL: https://www.smallregistry.net +// Former AFNIC's SLDs +// Submitted by Jérôme Lipowicz <support@promopixel.com> +aeroport.fr +avocat.fr +chambagri.fr +chirurgiens-dentistes.fr +experts-comptables.fr +medecin.fr +notaires.fr +pharmacien.fr +port.fr +veterinaire.fr + +// Small Technology Foundation : https://small-tech.org +// Submitted by Aral Balkan <aral@small-tech.org> +small-web.org + +// Smoove.io : https://www.smoove.io/ +// Submitted by Dan Kozak <dan@smoove.io> +vp4.me + +// Snowflake Inc : https://www.snowflake.com/ +// Submitted by Faith Olapade <faith.olapade@snowflake.com> +snowflake.app +privatelink.snowflake.app +streamlit.app +streamlitapp.com + +// Snowplow Analytics : https://snowplowanalytics.com/ +// Submitted by Ian Streeter <ian@snowplowanalytics.com> +try-snowplow.com + +// SourceHut : https://sourcehut.org +// Submitted by Drew DeVault <sir@cmpwn.com> +srht.site + +// Stackhero : https://www.stackhero.io +// Submitted by Adrien Gillon <adrien+public-suffix-list@stackhero.io> +stackhero-network.com + +// STACKIT : https://www.stackit.de/en/ +// Submitted by STACKIT-DNS Team (Simon Stier) <stackit-dns@mail.schwarz> +runs.onstackit.cloud +stackit.gg +stackit.rocks +stackit.run +stackit.zone + +// Staclar : https://staclar.com +// Submitted by Q Misell <q@staclar.com> +musician.io +// Submitted by Matthias Merkel <matthias.merkel@staclar.com> +novecore.site + +// staticland : https://static.land +// Submitted by Seth Vincent <sethvincent@gmail.com> +static.land +dev.static.land +sites.static.land + +// Storebase : https://www.storebase.io +// Submitted by Tony Schirmer <tony@storebase.io> +storebase.store + +// Strategic System Consulting (eApps Hosting): https://www.eapps.com/ +// Submitted by Alex Oancea <aoancea@cloudscale365.com> +vps-host.net +atl.jelastic.vps-host.net +njs.jelastic.vps-host.net +ric.jelastic.vps-host.net + +// Sony Interactive Entertainment LLC : https://sie.com/ +// Submitted by David Coles <david.coles@sony.com> +playstation-cloud.com + +// SourceLair PC : https://www.sourcelair.com +// Submitted by Antonis Kalipetis <akalipetis@sourcelair.com> +apps.lair.io +*.stolos.io + +// SpaceKit : https://www.spacekit.io/ +// Submitted by Reza Akhavan <spacekit.io@gmail.com> +spacekit.io + +// SpeedPartner GmbH: https://www.speedpartner.de/ +// Submitted by Stefan Neufeind <info@speedpartner.de> +customer.speedpartner.de + +// Spreadshop (sprd.net AG) : https://www.spreadshop.com/ +// Submitted by Martin Breest <security@spreadshop.com> +myspreadshop.at +myspreadshop.com.au +myspreadshop.be +myspreadshop.ca +myspreadshop.ch +myspreadshop.com +myspreadshop.de +myspreadshop.dk +myspreadshop.es +myspreadshop.fi +myspreadshop.fr +myspreadshop.ie +myspreadshop.it +myspreadshop.net +myspreadshop.nl +myspreadshop.no +myspreadshop.pl +myspreadshop.se +myspreadshop.co.uk + +// Standard Library : https://stdlib.com +// Submitted by Jacob Lee <jacob@stdlib.com> +api.stdlib.com + +// stereosense GmbH : https://www.involve.me +// Submitted by Florian Burmann <publicsuffix@involve.me> +feedback.ac +forms.ac +assessments.cx +calculators.cx +funnels.cx +paynow.cx +quizzes.cx +researched.cx +tests.cx +surveys.so + +// Storipress : https://storipress.com +// Submitted by Benno Liu <benno@storipress.com> +storipress.app + +// Storj Labs Inc. : https://storj.io/ +// Submitted by Philip Hutchins <hostmaster@storj.io> +storj.farm + +// Streak : https://streak.com +// Submitted by Blake Kadatz <eng@streak.com> +streak-link.com +streaklinks.com +streakusercontent.com + +// Studenten Net Twente : http://www.snt.utwente.nl/ +// Submitted by Silke Hofstra <syscom@snt.utwente.nl> +utwente.io + +// Student-Run Computing Facility : https://www.srcf.net/ +// Submitted by Edwin Balani <sysadmins@srcf.net> +soc.srcf.net +user.srcf.net + +// Sub 6 Limited: http://www.sub6.com +// Submitted by Dan Miller <dm@sub6.com> +temp-dns.com + +// Supabase : https://supabase.io +// Submitted by Inian Parameshwaran <security@supabase.io> +supabase.co +supabase.in +supabase.net +su.paba.se + +// Symfony, SAS : https://symfony.com/ +// Submitted by Fabien Potencier <fabien@symfony.com> +*.s5y.io +*.sensiosite.cloud + +// Syncloud : https://syncloud.org +// Submitted by Boris Rybalkin <syncloud@syncloud.it> +syncloud.it + +// Synology, Inc. : https://www.synology.com/ +// Submitted by Rony Weng <ronyweng@synology.com> +dscloud.biz +direct.quickconnect.cn +dsmynas.com +familyds.com +diskstation.me +dscloud.me +i234.me +myds.me +synology.me +dscloud.mobi +dsmynas.net +familyds.net +dsmynas.org +familyds.org +vpnplus.to +direct.quickconnect.to + +// Tabit Technologies Ltd. : https://tabit.cloud/ +// Submitted by Oren Agiv <oren@tabit.cloud> +tabitorder.co.il +mytabit.co.il +mytabit.com + +// TAIFUN Software AG : http://taifun-software.de +// Submitted by Bjoern Henke <dev-server@taifun-software.de> +taifun-dns.de + +// Tailscale Inc. : https://www.tailscale.com +// Submitted by David Anderson <danderson@tailscale.com> +beta.tailscale.net +ts.net +*.c.ts.net + +// TASK geographical domains (www.task.gda.pl/uslugi/dns) +gda.pl +gdansk.pl +gdynia.pl +med.pl +sopot.pl + +// team.blue https://team.blue +// Submitted by Cedric Dubois <cedric.dubois@team.blue> +site.tb-hosting.com + +// Teckids e.V. : https://www.teckids.org +// Submitted by Dominik George <dominik.george@teckids.org> +edugit.io +s3.teckids.org + +// Telebit : https://telebit.cloud +// Submitted by AJ ONeal <aj@telebit.cloud> +telebit.app +telebit.io +*.telebit.xyz + +// Thingdust AG : https://thingdust.com/ +// Submitted by Adrian Imboden <adi@thingdust.com> +*.firenet.ch +*.svc.firenet.ch +reservd.com +thingdustdata.com +cust.dev.thingdust.io +cust.disrec.thingdust.io +cust.prod.thingdust.io +cust.testing.thingdust.io +reservd.dev.thingdust.io +reservd.disrec.thingdust.io +reservd.testing.thingdust.io + +// ticket i/O GmbH : https://ticket.io +// Submitted by Christian Franke <it@ticket.io> +tickets.io + +// Tlon.io : https://tlon.io +// Submitted by Mark Staarink <mark@tlon.io> +arvo.network +azimuth.network +tlon.network + +// Tor Project, Inc. : https://torproject.org +// Submitted by Antoine Beaupré <anarcat@torproject.org +torproject.net +pages.torproject.net + +// TownNews.com : http://www.townnews.com +// Submitted by Dustin Ward <dward@townnews.com> +bloxcms.com +townnews-staging.com + +// TrafficPlex GmbH : https://www.trafficplex.de/ +// Submitted by Phillipp Röll <phillipp.roell@trafficplex.de> +12hp.at +2ix.at +4lima.at +lima-city.at +12hp.ch +2ix.ch +4lima.ch +lima-city.ch +trafficplex.cloud +de.cool +12hp.de +2ix.de +4lima.de +lima-city.de +1337.pictures +clan.rip +lima-city.rocks +webspace.rocks +lima.zone + +// TransIP : https://www.transip.nl +// Submitted by Rory Breuk <rbreuk@transip.nl> +*.transurl.be +*.transurl.eu +*.transurl.nl + +// TransIP: https://www.transip.nl +// Submitted by Cedric Dubois <cedric.dubois@team.blue> +site.transip.me + +// TuxFamily : http://tuxfamily.org +// Submitted by TuxFamily administrators <adm@staff.tuxfamily.org> +tuxfamily.org + +// TwoDNS : https://www.twodns.de/ +// Submitted by TwoDNS-Support <support@two-dns.de> +dd-dns.de +diskstation.eu +diskstation.org +dray-dns.de +draydns.de +dyn-vpn.de +dynvpn.de +mein-vigor.de +my-vigor.de +my-wan.de +syno-ds.de +synology-diskstation.de +synology-ds.de + +// Typedream : https://typedream.com +// Submitted by Putri Karunia <putri@typedream.com> +typedream.app + +// Typeform : https://www.typeform.com +// Submitted by Sergi Ferriz <sergi.ferriz@typeform.com> +pro.typeform.com + +// Uberspace : https://uberspace.de +// Submitted by Moritz Werner <mwerner@jonaspasche.com> +uber.space +*.uberspace.de + +// UDR Limited : http://www.udr.hk.com +// Submitted by registry <hostmaster@udr.hk.com> +hk.com +hk.org +ltd.hk +inc.hk + +// UK Intis Telecom LTD : https://it.com +// Submitted by ITComdomains <to@it.com> +it.com + +// UNIVERSAL DOMAIN REGISTRY : https://www.udr.org.yt/ +// see also: whois -h whois.udr.org.yt help +// Submitted by Atanunu Igbunuroghene <publicsuffixlist@udr.org.yt> +name.pm +sch.tf +biz.wf +sch.wf +org.yt + +// United Gameserver GmbH : https://united-gameserver.de +// Submitted by Stefan Schwarz <sysadm@united-gameserver.de> +virtualuser.de +virtual-user.de + +// Upli : https://upli.io +// Submitted by Lenny Bakkalian <lenny.bakkalian@gmail.com> +upli.io + +// urown.net : https://urown.net +// Submitted by Hostmaster <hostmaster@urown.net> +urown.cloud +dnsupdate.info + +// .US +// Submitted by Ed Moore <Ed.Moore@lib.de.us> +lib.de.us + +// VeryPositive SIA : http://very.lv +// Submitted by Danko Aleksejevs <danko@very.lv> +2038.io + +// Vercel, Inc : https://vercel.com/ +// Submitted by Connor Davis <security@vercel.com> +vercel.app +vercel.dev +now.sh + +// Viprinet Europe GmbH : http://www.viprinet.com +// Submitted by Simon Kissel <hostmaster@viprinet.com> +router.management + +// Virtual-Info : https://www.virtual-info.info/ +// Submitted by Adnan RIHAN <hostmaster@v-info.info> +v-info.info + +// Voorloper.com: https://voorloper.com +// Submitted by Nathan van Bakel <info@voorloper.com> +voorloper.cloud + +// Voxel.sh DNS : https://voxel.sh/dns/ +// Submitted by Mia Rehlinger <dns@voxel.sh> +neko.am +nyaa.am +be.ax +cat.ax +es.ax +eu.ax +gg.ax +mc.ax +us.ax +xy.ax +nl.ci +xx.gl +app.gp +blog.gt +de.gt +to.gt +be.gy +cc.hn +io.kg +jp.kg +tv.kg +uk.kg +us.kg +de.ls +at.md +de.md +jp.md +to.md +indie.porn +vxl.sh +ch.tc +me.tc +we.tc +nyan.to +at.vg +blog.vu +dev.vu +me.vu + +// V.UA Domain Administrator : https://domain.v.ua/ +// Submitted by Serhii Rostilo <sergey@rostilo.kiev.ua> +v.ua + +// Vultr Objects : https://www.vultr.com/products/object-storage/ +// Submitted by Niels Maumenee <storage@vultr.com> +*.vultrobjects.com + +// Waffle Computer Inc., Ltd. : https://docs.waffleinfo.com +// Submitted by Masayuki Note <masa@blade.wafflecell.com> +wafflecell.com + +// Webflow, Inc. : https://www.webflow.com +// Submitted by Webflow Security Team <security@webflow.com> +webflow.io +webflowtest.io + +// WebHare bv: https://www.webhare.com/ +// Submitted by Arnold Hendriks <info@webhare.com> +*.webhare.dev + +// WebHotelier Technologies Ltd: https://www.webhotelier.net/ +// Submitted by Apostolos Tsakpinis <apostolos.tsakpinis@gmail.com> +reserve-online.net +reserve-online.com +bookonline.app +hotelwithflight.com + +// WeDeploy by Liferay, Inc. : https://www.wedeploy.com +// Submitted by Henrique Vicente <security@wedeploy.com> +wedeploy.io +wedeploy.me +wedeploy.sh + +// Western Digital Technologies, Inc : https://www.wdc.com +// Submitted by Jung Jin <jungseok.jin@wdc.com> +remotewd.com + +// WIARD Enterprises : https://wiardweb.com +// Submitted by Kidd Hustle <kiddhustle@wiardweb.com> +pages.wiardweb.com + +// Wikimedia Labs : https://wikitech.wikimedia.org +// Submitted by Arturo Borrero Gonzalez <aborrero@wikimedia.org> +wmflabs.org +toolforge.org +wmcloud.org + +// WISP : https://wisp.gg +// Submitted by Stepan Fedotov <stepan@wisp.gg> +panel.gg +daemon.panel.gg + +// Wizard Zines : https://wizardzines.com +// Submitted by Julia Evans <julia@wizardzines.com> +messwithdns.com + +// WoltLab GmbH : https://www.woltlab.com +// Submitted by Tim Düsterhus <security@woltlab.cloud> +woltlab-demo.com +myforum.community +community-pro.de +diskussionsbereich.de +community-pro.net +meinforum.net + +// Woods Valldata : https://www.woodsvalldata.co.uk/ +// Submitted by Chris Whittle <chris.whittle@woodsvalldata.co.uk> +affinitylottery.org.uk +raffleentry.org.uk +weeklylottery.org.uk + +// WP Engine : https://wpengine.com/ +// Submitted by Michael Smith <michael.smith@wpengine.com> +// Submitted by Brandon DuRette <brandon.durette@wpengine.com> +wpenginepowered.com +js.wpenginepowered.com + +// Wix.com, Inc. : https://www.wix.com +// Submitted by Shahar Talmi <shahar@wix.com> +wixsite.com +editorx.io +wixstudio.io +wix.run + +// XenonCloud GbR: https://xenoncloud.net +// Submitted by Julian Uphoff <publicsuffixlist@xenoncloud.net> +half.host + +// XnBay Technology : http://www.xnbay.com/ +// Submitted by XnBay Developer <developer.xncloud@gmail.com> +xnbay.com +u2.xnbay.com +u2-local.xnbay.com + +// XS4ALL Internet bv : https://www.xs4all.nl/ +// Submitted by Daniel Mostertman <unixbeheer+publicsuffix@xs4all.net> +cistron.nl +demon.nl +xs4all.space + +// Yandex.Cloud LLC: https://cloud.yandex.com +// Submitted by Alexander Lodin <security+psl@yandex-team.ru> +yandexcloud.net +storage.yandexcloud.net +website.yandexcloud.net + +// YesCourse Pty Ltd : https://yescourse.com +// Submitted by Atul Bhouraskar <atul@yescourse.com> +official.academy + +// Yola : https://www.yola.com/ +// Submitted by Stefano Rivera <stefano@yola.com> +yolasite.com + +// Yombo : https://yombo.net +// Submitted by Mitch Schwenk <mitch@yombo.net> +ybo.faith +yombo.me +homelink.one +ybo.party +ybo.review +ybo.science +ybo.trade + +// Yunohost : https://yunohost.org +// Submitted by Valentin Grimaud <security@yunohost.org> +ynh.fr +nohost.me +noho.st + +// ZaNiC : http://www.za.net/ +// Submitted by registry <hostmaster@nic.za.net> +za.net +za.org + +// ZAP-Hosting GmbH & Co. KG : https://zap-hosting.com +// Submitted by Julian Alker <security@zap-hosting.com> +zap.cloud + +// Zine EOOD : https://zine.bg/ +// Submitted by Martin Angelov <martin@zine.bg> +bss.design + +// Zitcom A/S : https://www.zitcom.dk +// Submitted by Emil Stahl <esp@zitcom.dk> +basicserver.io +virtualserver.io +enterprisecloud.nu + +// ===END PRIVATE DOMAINS=== diff --git a/netwerk/dns/moz.build b/netwerk/dns/moz.build new file mode 100644 index 0000000000..c926d14707 --- /dev/null +++ b/netwerk/dns/moz.build @@ -0,0 +1,122 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +with Files("**"): + BUG_COMPONENT = ("Core", "Networking: DNS") + +DIRS += ["tests"] + +XPIDL_SOURCES += [ + "nsIDNSAdditionalInfo.idl", + "nsIDNSByTypeRecord.idl", + "nsIDNSListener.idl", + "nsIDNSRecord.idl", + "nsIDNSService.idl", + "nsIEffectiveTLDService.idl", + "nsIIDNService.idl", + "nsINativeDNSResolverOverride.idl", + "nsITRRSkipReason.idl", + "nsPIDNSService.idl", +] + +XPIDL_MODULE = "necko_dns" + +EXTRA_JS_MODULES["netwerk-dns"] += [ + "PublicSuffixList.sys.mjs", +] + +XPCSHELL_TESTS_MANIFESTS += ["tests/unit/xpcshell.toml"] + +EXPORTS += [ + "nsEffectiveTLDService.h", +] + +EXPORTS.mozilla.net += [ + "ChildDNSService.h", + "DNS.h", + "DNSByTypeRecord.h", + "DNSListenerProxy.h", + "DNSPacket.h", + "DNSRequestBase.h", + "DNSRequestChild.h", + "DNSRequestParent.h", + "DNSServiceBase.h", + "HTTPSSVC.h", + "IDNBlocklistUtils.h", + "NativeDNSResolverOverrideChild.h", + "NativeDNSResolverOverrideParent.h", + "TRRService.h", + "TRRServiceBase.h", + "TRRServiceChild.h", + "TRRServiceParent.h", +] + +SOURCES += [ + "GetAddrInfo.cpp", # Undefines UNICODE + "nsEffectiveTLDService.cpp", # Excluded from UNIFIED_SOURCES due to special build flags. +] + +if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows": + SOURCES += ["PlatformDNSWin.cpp"] +elif CONFIG["OS_TARGET"] == "Linux" or CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa": + SOURCES += ["PlatformDNSUnix.cpp"] + OS_LIBS += ["resolv"] +elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "android": + SOURCES += ["PlatformDNSAndroid.cpp"] +else: + DEFINES["MOZ_NO_HTTPS_IMPL"] = 1 + +UNIFIED_SOURCES += [ + "ChildDNSService.cpp", + "DNS.cpp", + "DNSAdditionalInfo.cpp", + "DNSListenerProxy.cpp", + "DNSPacket.cpp", + "DNSRequestChild.cpp", + "DNSRequestParent.cpp", + "DNSServiceBase.cpp", + "DNSUtils.cpp", + "HostRecordQueue.cpp", + "HTTPSSVC.cpp", + "IDNBlocklistUtils.cpp", + "NativeDNSResolverOverrideChild.cpp", + "NativeDNSResolverOverrideParent.cpp", + "nsDNSService2.cpp", + "nsHostRecord.cpp", + "nsHostResolver.cpp", + "nsIDNService.cpp", + "punycode.c", + "TRR.cpp", + "TRRQuery.cpp", + "TRRService.cpp", + "TRRServiceBase.cpp", + "TRRServiceChild.cpp", + "TRRServiceParent.cpp", +] + +IPDL_SOURCES = [ + "PDNSRequest.ipdl", + "PDNSRequestParams.ipdlh", + "PNativeDNSResolverOverride.ipdl", + "PTRRService.ipdl", +] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul" + +GeneratedFile( + "etld_data.inc", script="prepare_tlds.py", inputs=["effective_tld_names.dat"] +) + +# need to include etld_data.inc +LOCAL_INCLUDES += [ + "/netwerk/base", + "/netwerk/ipc", + "/netwerk/protocol/http", +] + +USE_LIBS += ["icu"] diff --git a/netwerk/dns/nsDNSService2.cpp b/netwerk/dns/nsDNSService2.cpp new file mode 100644 index 0000000000..68a76a0c1f --- /dev/null +++ b/netwerk/dns/nsDNSService2.cpp @@ -0,0 +1,1792 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsDNSService2.h" +#include "nsIDNSRecord.h" +#include "nsIDNSListener.h" +#include "nsIDNSByTypeRecord.h" +#include "nsICancelable.h" +#include "nsIPrefBranch.h" +#include "nsIOService.h" +#include "nsIXPConnect.h" +#include "nsProxyRelease.h" +#include "nsReadableUtils.h" +#include "nsString.h" +#include "nsCRT.h" +#include "nsNetCID.h" +#include "nsError.h" +#include "nsDNSPrefetch.h" +#include "nsThreadUtils.h" +#include "nsIProtocolProxyService.h" +#include "nsIObliviousHttp.h" +#include "prsystem.h" +#include "prnetdb.h" +#include "prmon.h" +#include "prio.h" +#include "nsCharSeparatedTokenizer.h" +#include "nsNetAddr.h" +#include "nsProxyRelease.h" +#include "nsQueryObject.h" +#include "nsIObserverService.h" +#include "nsINetworkLinkService.h" +#include "DNSAdditionalInfo.h" +#include "TRRService.h" + +#include "mozilla/Attributes.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/net/NeckoCommon.h" +#include "mozilla/net/ChildDNSService.h" +#include "mozilla/net/DNSListenerProxy.h" +#include "mozilla/Services.h" +#include "mozilla/StaticPrefs_network.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/SyncRunnable.h" +#include "mozilla/TextUtils.h" +#include "mozilla/Utf8.h" + +using namespace mozilla; +using namespace mozilla::net; + +static const char kPrefDnsCacheEntries[] = "network.dnsCacheEntries"; +static const char kPrefDnsCacheExpiration[] = "network.dnsCacheExpiration"; +static const char kPrefDnsCacheGrace[] = + "network.dnsCacheExpirationGracePeriod"; +static const char kPrefIPv4OnlyDomains[] = "network.dns.ipv4OnlyDomains"; +static const char kPrefBlockDotOnion[] = "network.dns.blockDotOnion"; +static const char kPrefDnsLocalDomains[] = "network.dns.localDomains"; +static const char kPrefDnsForceResolve[] = "network.dns.forceResolve"; +static const char kPrefDnsOfflineLocalhost[] = "network.dns.offline-localhost"; +static const char kPrefDnsNotifyResolution[] = "network.dns.notifyResolution"; + +//----------------------------------------------------------------------------- + +class nsDNSRecord : public nsIDNSAddrRecord { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIDNSRECORD + NS_DECL_NSIDNSADDRRECORD + + explicit nsDNSRecord(nsHostRecord* hostRecord) { + mHostRecord = do_QueryObject(hostRecord); + } + + private: + virtual ~nsDNSRecord() = default; + + RefPtr<AddrHostRecord> mHostRecord; + // Since mIter is holding a weak reference to the NetAddr array we must + // make sure it is not released. So we also keep a RefPtr to the AddrInfo + // which is immutable. + RefPtr<AddrInfo> mAddrInfo; + nsTArray<NetAddr>::const_iterator mIter; + const NetAddr* iter() { + if (!mIter.GetArray()) { + return nullptr; + } + if (mIter.GetArray()->end() == mIter) { + return nullptr; + } + return &*mIter; + } + + int mIterGenCnt = -1; // the generation count of + // mHostRecord->addr_info when we + // start iterating + bool mDone = false; +}; + +NS_IMPL_ISUPPORTS(nsDNSRecord, nsIDNSRecord, nsIDNSAddrRecord) + +NS_IMETHODIMP +nsDNSRecord::GetCanonicalName(nsACString& result) { + // this method should only be called if we have a CNAME + NS_ENSURE_TRUE(mHostRecord->flags & nsHostResolver::RES_CANON_NAME, + NS_ERROR_NOT_AVAILABLE); + + MutexAutoLock lock(mHostRecord->addr_info_lock); + + // if the record is for an IP address literal, then the canonical + // host name is the IP address literal. + if (!mHostRecord->addr_info) { + result = mHostRecord->host; + return NS_OK; + } + + if (mHostRecord->addr_info->CanonicalHostname().IsEmpty()) { + result = mHostRecord->addr_info->Hostname(); + } else { + result = mHostRecord->addr_info->CanonicalHostname(); + } + return NS_OK; +} + +NS_IMETHODIMP +nsDNSRecord::IsTRR(bool* retval) { + MutexAutoLock lock(mHostRecord->addr_info_lock); + if (mHostRecord->addr_info) { + *retval = mHostRecord->addr_info->IsTRR(); + } else { + *retval = false; + } + return NS_OK; +} + +NS_IMETHODIMP +nsDNSRecord::ResolvedInSocketProcess(bool* retval) { + *retval = false; + return NS_OK; +} + +NS_IMETHODIMP +nsDNSRecord::GetTrrFetchDuration(double* aTime) { + MutexAutoLock lock(mHostRecord->addr_info_lock); + if (mHostRecord->addr_info && mHostRecord->addr_info->IsTRR()) { + *aTime = mHostRecord->addr_info->GetTrrFetchDuration(); + } else { + *aTime = 0; + } + return NS_OK; +} + +NS_IMETHODIMP +nsDNSRecord::GetTrrFetchDurationNetworkOnly(double* aTime) { + MutexAutoLock lock(mHostRecord->addr_info_lock); + if (mHostRecord->addr_info && mHostRecord->addr_info->IsTRR()) { + *aTime = mHostRecord->addr_info->GetTrrFetchDurationNetworkOnly(); + } else { + *aTime = 0; + } + return NS_OK; +} + +NS_IMETHODIMP +nsDNSRecord::GetNextAddr(uint16_t port, NetAddr* addr) { + if (mDone) { + return NS_ERROR_NOT_AVAILABLE; + } + + mHostRecord->addr_info_lock.Lock(); + if (mHostRecord->addr_info) { + if (mIterGenCnt != mHostRecord->addr_info_gencnt) { + // mHostRecord->addr_info has changed, restart the iteration. + mIter = nsTArray<NetAddr>::const_iterator(); + mIterGenCnt = mHostRecord->addr_info_gencnt; + // Make sure to hold a RefPtr to the AddrInfo so we can iterate through + // the NetAddr array. + mAddrInfo = mHostRecord->addr_info; + } + + bool startedFresh = !iter(); + + do { + if (!iter()) { + mIter = mAddrInfo->Addresses().begin(); + } else { + mIter++; + } + } while (iter() && mHostRecord->Blocklisted(iter())); + + if (!iter() && startedFresh) { + // If everything was blocklisted we want to reset the blocklist (and + // likely relearn it) and return the first address. That is better + // than nothing. + mHostRecord->ResetBlocklist(); + mIter = mAddrInfo->Addresses().begin(); + } + + if (iter()) { + *addr = *mIter; + } + + mHostRecord->addr_info_lock.Unlock(); + + if (!iter()) { + mDone = true; + mIter = nsTArray<NetAddr>::const_iterator(); + mAddrInfo = nullptr; + mIterGenCnt = -1; + return NS_ERROR_NOT_AVAILABLE; + } + } else { + mHostRecord->addr_info_lock.Unlock(); + + if (!mHostRecord->addr) { + // Both mHostRecord->addr_info and mHostRecord->addr are null. + // This can happen if mHostRecord->addr_info expired and the + // attempt to reresolve it failed. + return NS_ERROR_NOT_AVAILABLE; + } + memcpy(addr, mHostRecord->addr.get(), sizeof(NetAddr)); + mDone = true; + } + + // set given port + port = htons(port); + if (addr->raw.family == AF_INET) { + addr->inet.port = port; + } else if (addr->raw.family == AF_INET6) { + addr->inet6.port = port; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsDNSRecord::GetAddresses(nsTArray<NetAddr>& aAddressArray) { + if (mDone) { + return NS_ERROR_NOT_AVAILABLE; + } + + mHostRecord->addr_info_lock.Lock(); + if (mHostRecord->addr_info) { + for (const auto& address : mHostRecord->addr_info->Addresses()) { + if (mHostRecord->Blocklisted(&address)) { + continue; + } + NetAddr* addr = aAddressArray.AppendElement(address); + if (addr->raw.family == AF_INET) { + addr->inet.port = 0; + } else if (addr->raw.family == AF_INET6) { + addr->inet6.port = 0; + } + } + mHostRecord->addr_info_lock.Unlock(); + } else { + mHostRecord->addr_info_lock.Unlock(); + + if (!mHostRecord->addr) { + return NS_ERROR_NOT_AVAILABLE; + } + NetAddr* addr = aAddressArray.AppendElement(NetAddr()); + memcpy(addr, mHostRecord->addr.get(), sizeof(NetAddr)); + if (addr->raw.family == AF_INET) { + addr->inet.port = 0; + } else if (addr->raw.family == AF_INET6) { + addr->inet6.port = 0; + } + } + return NS_OK; +} + +NS_IMETHODIMP +nsDNSRecord::GetScriptableNextAddr(uint16_t port, nsINetAddr** result) { + NetAddr addr; + nsresult rv = GetNextAddr(port, &addr); + if (NS_FAILED(rv)) { + return rv; + } + + RefPtr<nsNetAddr> netaddr = new nsNetAddr(&addr); + netaddr.forget(result); + + return NS_OK; +} + +NS_IMETHODIMP +nsDNSRecord::GetNextAddrAsString(nsACString& result) { + NetAddr addr; + nsresult rv = GetNextAddr(0, &addr); + if (NS_FAILED(rv)) { + return rv; + } + + char buf[kIPv6CStrBufSize]; + if (addr.ToStringBuffer(buf, sizeof(buf))) { + result.Assign(buf); + return NS_OK; + } + NS_ERROR("NetAddrToString failed unexpectedly"); + return NS_ERROR_FAILURE; // conversion failed for some reason +} + +NS_IMETHODIMP +nsDNSRecord::HasMore(bool* result) { + if (mDone) { + *result = false; + return NS_OK; + } + + nsTArray<NetAddr>::const_iterator iterCopy = mIter; + int iterGenCntCopy = mIterGenCnt; + + NetAddr addr; + *result = NS_SUCCEEDED(GetNextAddr(0, &addr)); + + mIter = iterCopy; + mIterGenCnt = iterGenCntCopy; + mDone = false; + + return NS_OK; +} + +NS_IMETHODIMP +nsDNSRecord::Rewind() { + mIter = nsTArray<NetAddr>::const_iterator(); + mIterGenCnt = -1; + mDone = false; + return NS_OK; +} + +NS_IMETHODIMP +nsDNSRecord::ReportUnusable(uint16_t aPort) { + // right now we don't use the port in the blocklist + + MutexAutoLock lock(mHostRecord->addr_info_lock); + + // Check that we are using a real addr_info (as opposed to a single + // constant address), and that the generation count is valid. Otherwise, + // ignore the report. + + if (mHostRecord->addr_info && mIterGenCnt == mHostRecord->addr_info_gencnt && + iter()) { + mHostRecord->ReportUnusable(iter()); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsDNSRecord::GetEffectiveTRRMode(nsIRequest::TRRMode* aMode) { + *aMode = mHostRecord->EffectiveTRRMode(); + return NS_OK; +} + +NS_IMETHODIMP nsDNSRecord::GetTrrSkipReason( + nsITRRSkipReason::value* aTrrSkipReason) { + *aTrrSkipReason = mHostRecord->TrrSkipReason(); + return NS_OK; +} + +NS_IMETHODIMP +nsDNSRecord::GetTtl(uint32_t* aTtl) { return mHostRecord->GetTtl(aTtl); } + +class nsDNSByTypeRecord : public nsIDNSByTypeRecord, + public nsIDNSTXTRecord, + public nsIDNSHTTPSSVCRecord { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIDNSRECORD + NS_DECL_NSIDNSBYTYPERECORD + NS_DECL_NSIDNSTXTRECORD + NS_DECL_NSIDNSHTTPSSVCRECORD + + explicit nsDNSByTypeRecord(nsHostRecord* hostRecord) { + mHostRecord = do_QueryObject(hostRecord); + } + + private: + virtual ~nsDNSByTypeRecord() = default; + RefPtr<TypeHostRecord> mHostRecord; +}; + +NS_IMPL_ISUPPORTS(nsDNSByTypeRecord, nsIDNSRecord, nsIDNSByTypeRecord, + nsIDNSTXTRecord, nsIDNSHTTPSSVCRecord) + +NS_IMETHODIMP +nsDNSByTypeRecord::GetType(uint32_t* aType) { + *aType = mHostRecord->GetType(); + return NS_OK; +} + +NS_IMETHODIMP +nsDNSByTypeRecord::GetRecords(CopyableTArray<nsCString>& aRecords) { + // deep copy + return mHostRecord->GetRecords(aRecords); +} + +NS_IMETHODIMP +nsDNSByTypeRecord::GetRecordsAsOneString(nsACString& aRecords) { + // deep copy + return mHostRecord->GetRecordsAsOneString(aRecords); +} + +NS_IMETHODIMP +nsDNSByTypeRecord::GetRecords(nsTArray<RefPtr<nsISVCBRecord>>& aRecords) { + return mHostRecord->GetRecords(aRecords); +} + +NS_IMETHODIMP +nsDNSByTypeRecord::GetServiceModeRecord(bool aNoHttp2, bool aNoHttp3, + nsISVCBRecord** aRecord) { + return mHostRecord->GetServiceModeRecord(aNoHttp2, aNoHttp3, aRecord); +} + +NS_IMETHODIMP +nsDNSByTypeRecord::GetAllRecordsWithEchConfig( + bool aNoHttp2, bool aNoHttp3, bool* aAllRecordsHaveEchConfig, + bool* aAllRecordsInH3ExcludedList, + nsTArray<RefPtr<nsISVCBRecord>>& aResult) { + return mHostRecord->GetAllRecordsWithEchConfig( + aNoHttp2, aNoHttp3, aAllRecordsHaveEchConfig, aAllRecordsInH3ExcludedList, + aResult); +} + +NS_IMETHODIMP +nsDNSByTypeRecord::GetHasIPAddresses(bool* aResult) { + return mHostRecord->GetHasIPAddresses(aResult); +} + +NS_IMETHODIMP +nsDNSByTypeRecord::GetAllRecordsExcluded(bool* aResult) { + return mHostRecord->GetAllRecordsExcluded(aResult); +} + +NS_IMETHODIMP +nsDNSByTypeRecord::GetResults(mozilla::net::TypeRecordResultType* aResults) { + *aResults = mHostRecord->GetResults(); + return NS_OK; +} + +NS_IMETHODIMP +nsDNSByTypeRecord::GetTtl(uint32_t* aTtl) { return mHostRecord->GetTtl(aTtl); } + +//----------------------------------------------------------------------------- + +class nsDNSAsyncRequest final : public nsResolveHostCallback, + public nsICancelable { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSICANCELABLE + + nsDNSAsyncRequest(nsHostResolver* res, const nsACString& host, + const nsACString& trrServer, uint16_t type, + const OriginAttributes& attrs, nsIDNSListener* listener, + nsIDNSService::DNSFlags flags, uint16_t af) + : mResolver(res), + mHost(host), + mTrrServer(trrServer), + mType(type), + mOriginAttributes(attrs), + mListener(listener), + mFlags(flags), + mAF(af) {} + + void OnResolveHostComplete(nsHostResolver*, nsHostRecord*, nsresult) override; + // Returns TRUE if the DNS listener arg is the same as the member listener + // Used in Cancellations to remove DNS requests associated with a + // particular hostname and nsIDNSListener + bool EqualsAsyncListener(nsIDNSListener* aListener) override; + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const override; + + RefPtr<nsHostResolver> mResolver; + nsCString mHost; // hostname we're resolving + nsCString mTrrServer; // A trr server to be used. + uint16_t mType = 0; + const OriginAttributes + mOriginAttributes; // The originAttributes for this resolving + nsCOMPtr<nsIDNSListener> mListener; + nsIDNSService::DNSFlags mFlags = nsIDNSService::RESOLVE_DEFAULT_FLAGS; + uint16_t mAF = 0; + + private: + virtual ~nsDNSAsyncRequest() = default; +}; + +NS_IMPL_ISUPPORTS(nsDNSAsyncRequest, nsICancelable) + +void nsDNSAsyncRequest::OnResolveHostComplete(nsHostResolver* resolver, + nsHostRecord* hostRecord, + nsresult status) { + // need to have an owning ref when we issue the callback to enable + // the caller to be able to addref/release multiple times without + // destroying the record prematurely. + nsCOMPtr<nsIDNSRecord> rec; + if (NS_SUCCEEDED(status) || + mFlags & nsIDNSService::RESOLVE_WANT_RECORD_ON_ERROR) { + MOZ_ASSERT(hostRecord, "no host record"); + if (hostRecord->type != nsDNSService::RESOLVE_TYPE_DEFAULT) { + rec = new nsDNSByTypeRecord(hostRecord); + } else { + rec = new nsDNSRecord(hostRecord); + } + } + + mListener->OnLookupComplete(this, rec, status); + mListener = nullptr; +} + +bool nsDNSAsyncRequest::EqualsAsyncListener(nsIDNSListener* aListener) { + uintptr_t originalListenerAddr = reinterpret_cast<uintptr_t>(mListener.get()); + RefPtr<DNSListenerProxy> wrapper = do_QueryObject(mListener); + if (wrapper) { + originalListenerAddr = wrapper->GetOriginalListenerAddress(); + } + + uintptr_t listenerAddr = reinterpret_cast<uintptr_t>(aListener); + return (listenerAddr == originalListenerAddr); +} + +size_t nsDNSAsyncRequest::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const { + size_t n = mallocSizeOf(this); + + // The following fields aren't measured. + // - mHost, because it's a non-owning pointer + // - mResolver, because it's a non-owning pointer + // - mListener, because it's a non-owning pointer + + return n; +} + +NS_IMETHODIMP +nsDNSAsyncRequest::Cancel(nsresult reason) { + NS_ENSURE_ARG(NS_FAILED(reason)); + MOZ_DIAGNOSTIC_ASSERT(mResolver, "mResolver should not be null"); + mResolver->DetachCallback(mHost, mTrrServer, mType, mOriginAttributes, mFlags, + mAF, this, reason); + return NS_OK; +} + +//----------------------------------------------------------------------------- + +class nsDNSSyncRequest : public nsResolveHostCallback { + NS_DECL_THREADSAFE_ISUPPORTS + public: + explicit nsDNSSyncRequest(PRMonitor* mon) : mMonitor(mon) {} + + void OnResolveHostComplete(nsHostResolver*, nsHostRecord*, nsresult) override; + bool EqualsAsyncListener(nsIDNSListener* aListener) override; + size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const override; + + bool mDone = false; + nsresult mStatus = NS_OK; + RefPtr<nsHostRecord> mHostRecord; + + private: + virtual ~nsDNSSyncRequest() = default; + + PRMonitor* mMonitor = nullptr; +}; + +NS_IMPL_ISUPPORTS0(nsDNSSyncRequest) + +void nsDNSSyncRequest::OnResolveHostComplete(nsHostResolver* resolver, + nsHostRecord* hostRecord, + nsresult status) { + // store results, and wake up nsDNSService::Resolve to process results. + PR_EnterMonitor(mMonitor); + mDone = true; + mStatus = status; + mHostRecord = hostRecord; + PR_Notify(mMonitor); + PR_ExitMonitor(mMonitor); +} + +bool nsDNSSyncRequest::EqualsAsyncListener(nsIDNSListener* aListener) { + // Sync request: no listener to compare + return false; +} + +size_t nsDNSSyncRequest::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const { + size_t n = mallocSizeOf(this); + + // The following fields aren't measured. + // - mHostRecord, because it's a non-owning pointer + + // Measurement of the following members may be added later if DMD finds it + // is worthwhile: + // - mMonitor + + return n; +} + +class NotifyDNSResolution : public Runnable { + public: + explicit NotifyDNSResolution(const nsACString& aHostname) + : mozilla::Runnable("NotifyDNSResolution"), mHostname(aHostname) {} + + NS_IMETHOD Run() override { + MOZ_ASSERT(NS_IsMainThread()); + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); + if (obs) { + obs->NotifyObservers(nullptr, "dns-resolution-request", + NS_ConvertUTF8toUTF16(mHostname).get()); + } + return NS_OK; + } + + private: + nsCString mHostname; +}; + +//----------------------------------------------------------------------------- + +static StaticRefPtr<DNSServiceWrapper> gDNSServiceWrapper; + +NS_IMPL_ISUPPORTS(DNSServiceWrapper, nsIDNSService, nsPIDNSService) + +// static +already_AddRefed<nsIDNSService> DNSServiceWrapper::GetSingleton() { + if (!gDNSServiceWrapper) { + gDNSServiceWrapper = new DNSServiceWrapper(); + gDNSServiceWrapper->mDNSServiceInUse = ChildDNSService::GetSingleton(); + if (gDNSServiceWrapper->mDNSServiceInUse) { + ClearOnShutdown(&gDNSServiceWrapper); + nsDNSPrefetch::Initialize(gDNSServiceWrapper); + } else { + gDNSServiceWrapper = nullptr; + } + } + + return do_AddRef(gDNSServiceWrapper); +} + +// static +void DNSServiceWrapper::SwitchToBackupDNSService() { + if (!gDNSServiceWrapper) { + return; + } + + gDNSServiceWrapper->mBackupDNSService = nsDNSService::GetSingleton(); + + MutexAutoLock lock(gDNSServiceWrapper->mLock); + gDNSServiceWrapper->mBackupDNSService.swap( + gDNSServiceWrapper->mDNSServiceInUse); +} + +nsIDNSService* DNSServiceWrapper::DNSService() { + MOZ_ASSERT(XRE_IsParentProcess()); + + MutexAutoLock lock(mLock); + return mDNSServiceInUse.get(); +} + +nsPIDNSService* DNSServiceWrapper::PIDNSService() { + MOZ_ASSERT(XRE_IsParentProcess()); + + nsCOMPtr<nsPIDNSService> service = do_QueryInterface(DNSService()); + return service.get(); +} + +//----------------------------------------------------------------------------- +NS_IMPL_ISUPPORTS_INHERITED(nsDNSService, DNSServiceBase, nsIDNSService, + nsPIDNSService, nsIMemoryReporter) + +/****************************************************************************** + * nsDNSService impl: + * singleton instance ctor/dtor methods + ******************************************************************************/ +static StaticRefPtr<nsDNSService> gDNSService; +static Atomic<bool> gInited(false); + +already_AddRefed<nsIDNSService> GetOrInitDNSService() { + if (gInited) { + return nsDNSService::GetXPCOMSingleton(); + } + + nsCOMPtr<nsIDNSService> dns = nullptr; + auto initTask = [&dns]() { dns = do_GetService(NS_DNSSERVICE_CID); }; + if (!NS_IsMainThread()) { + // Forward to the main thread synchronously. + RefPtr<nsIThread> mainThread = do_GetMainThread(); + if (!mainThread) { + return nullptr; + } + + SyncRunnable::DispatchToThread( + mainThread, NS_NewRunnableFunction("GetOrInitDNSService", initTask)); + } else { + initTask(); + } + + return dns.forget(); +} + +already_AddRefed<nsIDNSService> nsDNSService::GetXPCOMSingleton() { + auto getDNSHelper = []() -> already_AddRefed<nsIDNSService> { + if (nsIOService::UseSocketProcess()) { + if (XRE_IsSocketProcess()) { + return GetSingleton(); + } + + if (XRE_IsParentProcess()) { + return DNSServiceWrapper::GetSingleton(); + } + + if (XRE_IsContentProcess()) { + return ChildDNSService::GetSingleton(); + } + + return nullptr; + } + + if (XRE_IsParentProcess()) { + return GetSingleton(); + } + + if (XRE_IsContentProcess() || XRE_IsSocketProcess()) { + return ChildDNSService::GetSingleton(); + } + + return nullptr; + }; + + if (gInited) { + return getDNSHelper(); + } + + nsCOMPtr<nsIDNSService> dns = getDNSHelper(); + if (dns) { + gInited = true; + } + return dns.forget(); +} + +already_AddRefed<nsDNSService> nsDNSService::GetSingleton() { + MOZ_ASSERT_IF(nsIOService::UseSocketProcess(), XRE_IsSocketProcess()); + MOZ_ASSERT_IF(!nsIOService::UseSocketProcess(), XRE_IsParentProcess()); + + if (!gDNSService) { + if (!NS_IsMainThread()) { + return nullptr; + } + gDNSService = new nsDNSService(); + if (NS_SUCCEEDED(gDNSService->Init())) { + ClearOnShutdown(&gDNSService); + } else { + gDNSService = nullptr; + } + } + + return do_AddRef(gDNSService); +} + +void nsDNSService::ReadPrefs(const char* name) { + DNSServiceBase::ReadPrefs(name); + + bool tmpbool; + uint32_t tmpint; + mResolverPrefsUpdated = false; + + // resolver-specific prefs first + if (!name || !strcmp(name, kPrefDnsCacheEntries)) { + if (NS_SUCCEEDED(Preferences::GetUint(kPrefDnsCacheEntries, &tmpint))) { + if (!name || (tmpint != mResCacheEntries)) { + mResCacheEntries = tmpint; + mResolverPrefsUpdated = true; + } + } + } + if (!name || !strcmp(name, kPrefDnsCacheExpiration)) { + if (NS_SUCCEEDED(Preferences::GetUint(kPrefDnsCacheExpiration, &tmpint))) { + if (!name || (tmpint != mResCacheExpiration)) { + mResCacheExpiration = tmpint; + mResolverPrefsUpdated = true; + } + } + } + if (!name || !strcmp(name, kPrefDnsCacheGrace)) { + if (NS_SUCCEEDED(Preferences::GetUint(kPrefDnsCacheGrace, &tmpint))) { + if (!name || (tmpint != mResCacheGrace)) { + mResCacheGrace = tmpint; + mResolverPrefsUpdated = true; + } + } + } + + // DNSservice prefs + if (!name || !strcmp(name, kPrefDnsOfflineLocalhost)) { + if (NS_SUCCEEDED( + Preferences::GetBool(kPrefDnsOfflineLocalhost, &tmpbool))) { + mOfflineLocalhost = tmpbool; + } + } + if (!name || !strcmp(name, kPrefBlockDotOnion)) { + if (NS_SUCCEEDED(Preferences::GetBool(kPrefBlockDotOnion, &tmpbool))) { + mBlockDotOnion = tmpbool; + } + } + if (!name || !strcmp(name, kPrefDnsNotifyResolution)) { + if (NS_SUCCEEDED( + Preferences::GetBool(kPrefDnsNotifyResolution, &tmpbool))) { + mNotifyResolution = tmpbool; + } + } + if (!name || !strcmp(name, kPrefIPv4OnlyDomains)) { + Preferences::GetCString(kPrefIPv4OnlyDomains, mIPv4OnlyDomains); + } + if (!name || !strcmp(name, kPrefDnsLocalDomains)) { + nsCString localDomains; + Preferences::GetCString(kPrefDnsLocalDomains, localDomains); + MutexAutoLock lock(mLock); + mLocalDomains.Clear(); + for (const auto& token : + nsCCharSeparatedTokenizerTemplate<NS_IsAsciiWhitespace, + nsTokenizerFlags::SeparatorOptional>( + localDomains, ',') + .ToRange()) { + mLocalDomains.Insert(token); + } + } + if (!name || !strcmp(name, kPrefDnsForceResolve)) { + Preferences::GetCString(kPrefDnsForceResolve, mForceResolve); + mForceResolveOn = !mForceResolve.IsEmpty(); + } +} + +NS_IMETHODIMP +nsDNSService::Init() { + MOZ_ASSERT(!mResolver); + MOZ_ASSERT(NS_IsMainThread()); + + ReadPrefs(nullptr); + + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + if (observerService) { + observerService->AddObserver(this, "last-pb-context-exited", false); + observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, false); + observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); + } + + RefPtr<nsHostResolver> res; + nsresult rv = nsHostResolver::Create(mResCacheEntries, mResCacheExpiration, + mResCacheGrace, getter_AddRefs(res)); + if (NS_SUCCEEDED(rv)) { + // now, set all of our member variables while holding the lock + MutexAutoLock lock(mLock); + mResolver = res; + } + + nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); + if (prefs) { + // register as prefs observer + prefs->AddObserver(kPrefDnsCacheEntries, this, false); + prefs->AddObserver(kPrefDnsCacheExpiration, this, false); + prefs->AddObserver(kPrefDnsCacheGrace, this, false); + prefs->AddObserver(kPrefIPv4OnlyDomains, this, false); + prefs->AddObserver(kPrefDnsLocalDomains, this, false); + prefs->AddObserver(kPrefDnsForceResolve, this, false); + prefs->AddObserver(kPrefDnsOfflineLocalhost, this, false); + prefs->AddObserver(kPrefBlockDotOnion, this, false); + prefs->AddObserver(kPrefDnsNotifyResolution, this, false); + AddPrefObserver(prefs); + } + + nsDNSPrefetch::Initialize(this); + + RegisterWeakMemoryReporter(this); + + nsCOMPtr<nsIObliviousHttpService> ohttpService( + do_GetService("@mozilla.org/network/oblivious-http-service;1")); + + mTrrService = new TRRService(); + if (NS_FAILED(mTrrService->Init())) { + mTrrService = nullptr; + } + + nsCOMPtr<nsIIDNService> idn = do_GetService(NS_IDNSERVICE_CONTRACTID); + mIDN = idn; + + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::Shutdown() { + UnregisterWeakMemoryReporter(this); + + RefPtr<nsHostResolver> res; + { + MutexAutoLock lock(mLock); + res = std::move(mResolver); + } + if (res) { + // Shutdown outside lock. + res->Shutdown(); + } + + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + if (observerService) { + observerService->RemoveObserver(this, NS_NETWORK_LINK_TOPIC); + observerService->RemoveObserver(this, "last-pb-context-exited"); + observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID); + } + + return NS_OK; +} + +bool nsDNSService::GetOffline() const { + bool offline = false; + nsCOMPtr<nsIIOService> io = do_GetService(NS_IOSERVICE_CONTRACTID); + if (io) { + io->GetOffline(&offline); + } + return offline; +} + +NS_IMETHODIMP +nsDNSService::GetPrefetchEnabled(bool* outVal) { + MutexAutoLock lock(mLock); + *outVal = !mDisablePrefetch; + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::SetPrefetchEnabled(bool inVal) { + MutexAutoLock lock(mLock); + mDisablePrefetch = !inVal; + return NS_OK; +} + +already_AddRefed<nsHostResolver> nsDNSService::GetResolverLocked() { + MutexAutoLock lock(mLock); + return do_AddRef(mResolver); +} + +nsresult nsDNSService::PreprocessHostname(bool aLocalDomain, + const nsACString& aInput, + nsIIDNService* aIDN, + nsACString& aACE) { + // Enforce RFC 7686 + if (mBlockDotOnion && StringEndsWith(aInput, ".onion"_ns)) { + return NS_ERROR_UNKNOWN_HOST; + } + + if (aLocalDomain) { + aACE.AssignLiteral("localhost"); + return NS_OK; + } + + if (mTrrService && mTrrService->MaybeBootstrap(aInput, aACE)) { + return NS_OK; + } + + if (mForceResolveOn) { + MutexAutoLock lock(mLock); + if (!aInput.LowerCaseEqualsASCII("localhost") && + !aInput.LowerCaseEqualsASCII("127.0.0.1")) { + aACE.Assign(mForceResolve); + return NS_OK; + } + } + + if (!aIDN || IsAscii(aInput)) { + aACE = aInput; + return NS_OK; + } + + if (!(IsUtf8(aInput) && NS_SUCCEEDED(aIDN->ConvertUTF8toACE(aInput, aACE)))) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +nsresult nsDNSService::AsyncResolveInternal( + const nsACString& aHostname, uint16_t type, nsIDNSService::DNSFlags flags, + nsIDNSAdditionalInfo* aInfo, nsIDNSListener* aListener, + nsIEventTarget* target_, const OriginAttributes& aOriginAttributes, + nsICancelable** result) { + // grab reference to global host resolver and IDN service. beware + // simultaneous shutdown!! + RefPtr<nsHostResolver> res; + nsCOMPtr<nsIIDNService> idn; + nsCOMPtr<nsIEventTarget> target = target_; + nsCOMPtr<nsIDNSListener> listener = aListener; + bool localDomain = false; + { + MutexAutoLock lock(mLock); + + if (mDisablePrefetch && (flags & RESOLVE_SPECULATE)) { + return NS_ERROR_DNS_LOOKUP_QUEUE_FULL; + } + + res = mResolver; + idn = mIDN; + localDomain = mLocalDomains.Contains(aHostname); + } + + if (mNotifyResolution) { + NS_DispatchToMainThread(new NotifyDNSResolution(aHostname)); + } + + if (!res) { + return NS_ERROR_OFFLINE; + } + + if ((type != RESOLVE_TYPE_DEFAULT) && (type != RESOLVE_TYPE_TXT) && + (type != RESOLVE_TYPE_HTTPSSVC)) { + return NS_ERROR_INVALID_ARG; + } + + if (DNSForbiddenByActiveProxy(aHostname, flags)) { + // nsHostResolver returns NS_ERROR_UNKNOWN_HOST for lots of reasons. + // We use a different error code to differentiate this failure and to make + // it clear(er) where this error comes from. + return NS_ERROR_UNKNOWN_PROXY_HOST; + } + + nsCString hostname; + nsresult rv = PreprocessHostname(localDomain, aHostname, idn, hostname); + if (NS_FAILED(rv)) { + return rv; + } + + if (GetOffline() && + (!mOfflineLocalhost || !hostname.LowerCaseEqualsASCII("localhost"))) { + flags |= RESOLVE_OFFLINE; + } + + // make sure JS callers get notification on the main thread + nsCOMPtr<nsIXPConnectWrappedJS> wrappedListener = do_QueryInterface(listener); + if (wrappedListener && !target) { + target = GetMainThreadSerialEventTarget(); + } + + if (target) { + listener = new DNSListenerProxy(listener, target); + } + + uint16_t af = + (type != RESOLVE_TYPE_DEFAULT) ? 0 : GetAFForLookup(hostname, flags); + + MOZ_ASSERT(listener); + RefPtr<nsDNSAsyncRequest> req = + new nsDNSAsyncRequest(res, hostname, DNSAdditionalInfo::URL(aInfo), type, + aOriginAttributes, listener, flags, af); + if (!req) { + return NS_ERROR_OUT_OF_MEMORY; + } + + rv = res->ResolveHost(req->mHost, DNSAdditionalInfo::URL(aInfo), + DNSAdditionalInfo::Port(aInfo), type, + req->mOriginAttributes, flags, af, req); + req.forget(result); + return rv; +} + +nsresult nsDNSService::CancelAsyncResolveInternal( + const nsACString& aHostname, uint16_t aType, nsIDNSService::DNSFlags aFlags, + nsIDNSAdditionalInfo* aInfo, nsIDNSListener* aListener, nsresult aReason, + const OriginAttributes& aOriginAttributes) { + // grab reference to global host resolver and IDN service. beware + // simultaneous shutdown!! + RefPtr<nsHostResolver> res; + nsCOMPtr<nsIIDNService> idn; + bool localDomain = false; + { + MutexAutoLock lock(mLock); + + if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE)) { + return NS_ERROR_DNS_LOOKUP_QUEUE_FULL; + } + + res = mResolver; + idn = mIDN; + localDomain = mLocalDomains.Contains(aHostname); + } + if (!res) { + return NS_ERROR_OFFLINE; + } + + nsCString hostname; + nsresult rv = PreprocessHostname(localDomain, aHostname, idn, hostname); + if (NS_FAILED(rv)) { + return rv; + } + + uint16_t af = + (aType != RESOLVE_TYPE_DEFAULT) ? 0 : GetAFForLookup(hostname, aFlags); + + res->CancelAsyncRequest(hostname, DNSAdditionalInfo::URL(aInfo), aType, + aOriginAttributes, aFlags, af, aListener, aReason); + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::AsyncResolve(const nsACString& aHostname, + nsIDNSService::ResolveType aType, + nsIDNSService::DNSFlags flags, + nsIDNSAdditionalInfo* aInfo, + nsIDNSListener* listener, nsIEventTarget* target_, + JS::Handle<JS::Value> aOriginAttributes, + JSContext* aCx, uint8_t aArgc, + nsICancelable** result) { + OriginAttributes attrs; + + if (aArgc == 1) { + if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) { + return NS_ERROR_INVALID_ARG; + } + } + + return AsyncResolveInternal(aHostname, aType, flags, aInfo, listener, target_, + attrs, result); +} + +NS_IMETHODIMP +nsDNSService::AsyncResolveNative( + const nsACString& aHostname, nsIDNSService::ResolveType aType, + nsIDNSService::DNSFlags flags, nsIDNSAdditionalInfo* aInfo, + nsIDNSListener* aListener, nsIEventTarget* target_, + const OriginAttributes& aOriginAttributes, nsICancelable** result) { + return AsyncResolveInternal(aHostname, aType, flags, aInfo, aListener, + target_, aOriginAttributes, result); +} + +NS_IMETHODIMP +nsDNSService::NewAdditionalInfo(const nsACString& aTrrURL, int32_t aPort, + nsIDNSAdditionalInfo** aInfo) { + RefPtr<DNSAdditionalInfo> res = new DNSAdditionalInfo(aTrrURL, aPort); + res.forget(aInfo); + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::CancelAsyncResolve(const nsACString& aHostname, + nsIDNSService::ResolveType aType, + nsIDNSService::DNSFlags aFlags, + nsIDNSAdditionalInfo* aInfo, + nsIDNSListener* aListener, nsresult aReason, + JS::Handle<JS::Value> aOriginAttributes, + JSContext* aCx, uint8_t aArgc) { + OriginAttributes attrs; + + if (aArgc == 1) { + if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) { + return NS_ERROR_INVALID_ARG; + } + } + + return CancelAsyncResolveInternal(aHostname, aType, aFlags, aInfo, aListener, + aReason, attrs); +} + +NS_IMETHODIMP +nsDNSService::CancelAsyncResolveNative( + const nsACString& aHostname, nsIDNSService::ResolveType aType, + nsIDNSService::DNSFlags aFlags, nsIDNSAdditionalInfo* aInfo, + nsIDNSListener* aListener, nsresult aReason, + const OriginAttributes& aOriginAttributes) { + return CancelAsyncResolveInternal(aHostname, aType, aFlags, aInfo, aListener, + aReason, aOriginAttributes); +} + +NS_IMETHODIMP +nsDNSService::Resolve(const nsACString& aHostname, + nsIDNSService::DNSFlags flags, + JS::Handle<JS::Value> aOriginAttributes, JSContext* aCx, + uint8_t aArgc, nsIDNSRecord** result) { + OriginAttributes attrs; + + if (aArgc == 1) { + if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) { + return NS_ERROR_INVALID_ARG; + } + } + + return ResolveNative(aHostname, flags, attrs, result); +} + +NS_IMETHODIMP +nsDNSService::ResolveNative(const nsACString& aHostname, + nsIDNSService::DNSFlags flags, + const OriginAttributes& aOriginAttributes, + nsIDNSRecord** result) { + // Synchronous resolution is not available on the main thread. + if (NS_IsMainThread()) { + return NS_ERROR_NOT_AVAILABLE; + } + + return ResolveInternal(aHostname, flags, aOriginAttributes, result); +} + +nsresult nsDNSService::DeprecatedSyncResolve( + const nsACString& aHostname, nsIDNSService::DNSFlags flags, + const OriginAttributes& aOriginAttributes, nsIDNSRecord** result) { + return ResolveInternal(aHostname, flags, aOriginAttributes, result); +} + +nsresult nsDNSService::ResolveInternal( + const nsACString& aHostname, nsIDNSService::DNSFlags flags, + const OriginAttributes& aOriginAttributes, nsIDNSRecord** result) { + // grab reference to global host resolver and IDN service. beware + // simultaneous shutdown!! + RefPtr<nsHostResolver> res; + nsCOMPtr<nsIIDNService> idn; + bool localDomain = false; + { + MutexAutoLock lock(mLock); + res = mResolver; + idn = mIDN; + localDomain = mLocalDomains.Contains(aHostname); + } + + if (mNotifyResolution) { + NS_DispatchToMainThread(new NotifyDNSResolution(aHostname)); + } + + NS_ENSURE_TRUE(res, NS_ERROR_OFFLINE); + + nsCString hostname; + nsresult rv = PreprocessHostname(localDomain, aHostname, idn, hostname); + if (NS_FAILED(rv)) { + return rv; + } + + if (GetOffline() && + (!mOfflineLocalhost || !hostname.LowerCaseEqualsASCII("localhost"))) { + flags |= RESOLVE_OFFLINE; + } + + if (DNSForbiddenByActiveProxy(aHostname, flags)) { + return NS_ERROR_UNKNOWN_PROXY_HOST; + } + + // + // sync resolve: since the host resolver only works asynchronously, we need + // to use a mutex and a condvar to wait for the result. however, since the + // result may be in the resolvers cache, we might get called back recursively + // on the same thread. so, our mutex needs to be re-entrant. in other words, + // we need to use a monitor! ;-) + // + + PRMonitor* mon = PR_NewMonitor(); + if (!mon) { + return NS_ERROR_OUT_OF_MEMORY; + } + + PR_EnterMonitor(mon); + RefPtr<nsDNSSyncRequest> syncReq = new nsDNSSyncRequest(mon); + + uint16_t af = GetAFForLookup(hostname, flags); + + // TRR uses the main thread for the HTTPS channel to the DoH server. + // If this were to block the main thread while waiting for TRR it would + // likely cause a deadlock. Instead we intentionally choose to not use TRR + // for this. + if (NS_IsMainThread()) { + flags |= RESOLVE_DISABLE_TRR; + } + + rv = res->ResolveHost(hostname, ""_ns, -1, RESOLVE_TYPE_DEFAULT, + aOriginAttributes, flags, af, syncReq); + if (NS_SUCCEEDED(rv)) { + // wait for result + while (!syncReq->mDone) { + PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT); + } + + if (NS_FAILED(syncReq->mStatus)) { + rv = syncReq->mStatus; + } else { + NS_ASSERTION(syncReq->mHostRecord, "no host record"); + RefPtr<nsDNSRecord> rec = new nsDNSRecord(syncReq->mHostRecord); + rec.forget(result); + } + } + + PR_ExitMonitor(mon); + PR_DestroyMonitor(mon); + return rv; +} + +NS_IMETHODIMP +nsDNSService::GetMyHostName(nsACString& result) { + char name[100]; + if (PR_GetSystemInfo(PR_SI_HOSTNAME, name, sizeof(name)) == PR_SUCCESS) { + result = name; + return NS_OK; + } + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsDNSService::Observe(nsISupports* subject, const char* topic, + const char16_t* data) { + bool flushCache = false; + RefPtr<nsHostResolver> resolver = GetResolverLocked(); + + if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) { + nsAutoCString converted = NS_ConvertUTF16toUTF8(data); + if (!strcmp(converted.get(), NS_NETWORK_LINK_DATA_CHANGED)) { + flushCache = true; + } + } else if (!strcmp(topic, "last-pb-context-exited")) { + flushCache = true; + } else if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { + ReadPrefs(NS_ConvertUTF16toUTF8(data).get()); + NS_ENSURE_TRUE(resolver, NS_ERROR_NOT_INITIALIZED); + if (mResolverPrefsUpdated && resolver) { + resolver->SetCacheLimits(mResCacheEntries, mResCacheExpiration, + mResCacheGrace); + } + } else if (!strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { + Shutdown(); + } + + if (flushCache && resolver) { + resolver->FlushCache(false); + return NS_OK; + } + + return NS_OK; +} + +uint16_t nsDNSService::GetAFForLookup(const nsACString& host, + nsIDNSService::DNSFlags flags) { + if (StaticPrefs::network_dns_disableIPv6() || + (flags & RESOLVE_DISABLE_IPV6)) { + return PR_AF_INET; + } + + MutexAutoLock lock(mLock); + + uint16_t af = PR_AF_UNSPEC; + + if (!mIPv4OnlyDomains.IsEmpty()) { + const char *domain, *domainEnd, *end; + uint32_t hostLen, domainLen; + + // see if host is in one of the IPv4-only domains + domain = mIPv4OnlyDomains.BeginReading(); + domainEnd = mIPv4OnlyDomains.EndReading(); + + nsACString::const_iterator hostStart; + host.BeginReading(hostStart); + hostLen = host.Length(); + + do { + // skip any whitespace + while (*domain == ' ' || *domain == '\t') { + ++domain; + } + + // find end of this domain in the string + end = strchr(domain, ','); + if (!end) { + end = domainEnd; + } + + // to see if the hostname is in the domain, check if the domain + // matches the end of the hostname. + domainLen = end - domain; + if (domainLen && hostLen >= domainLen) { + const char* hostTail = hostStart.get() + hostLen - domainLen; + if (nsCRT::strncasecmp(domain, hostTail, domainLen) == 0) { + // now, make sure either that the hostname is a direct match or + // that the hostname begins with a dot. + if (hostLen == domainLen || *hostTail == '.' || + *(hostTail - 1) == '.') { + af = PR_AF_INET; + break; + } + } + } + + domain = end + 1; + } while (*end); + } + + if ((af != PR_AF_INET) && (flags & RESOLVE_DISABLE_IPV4)) { + af = PR_AF_INET6; + } + + return af; +} + +NS_IMETHODIMP +nsDNSService::GetDNSCacheEntries( + nsTArray<mozilla::net::DNSCacheEntries>* args) { + RefPtr<nsHostResolver> resolver = GetResolverLocked(); + NS_ENSURE_TRUE(resolver, NS_ERROR_NOT_INITIALIZED); + resolver->GetDNSCacheEntries(args); + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::ClearCache(bool aTrrToo) { + RefPtr<nsHostResolver> resolver = GetResolverLocked(); + NS_ENSURE_TRUE(resolver, NS_ERROR_NOT_INITIALIZED); + resolver->FlushCache(aTrrToo); + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::ReloadParentalControlEnabled() { + if (mTrrService) { + mTrrService->mParentalControlEnabled = + TRRService::GetParentalControlEnabledInternal(); + } + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::SetDetectedTrrURI(const nsACString& aURI) { + if (mTrrService) { + mTrrService->SetDetectedTrrURI(aURI); + } + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::SetHeuristicDetectionResult(nsITRRSkipReason::value aValue) { + if (mTrrService) { + mTrrService->SetHeuristicDetectionResult(aValue); + } + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::GetHeuristicDetectionResult(nsITRRSkipReason::value* aValue) { + if (!mTrrService) { + return NS_ERROR_NOT_AVAILABLE; + } + + *aValue = mTrrService->GetHeuristicDetectionResult(); + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::GetTRRSkipReasonName(nsITRRSkipReason::value aValue, + nsACString& aName) { + return mozilla::net::GetTRRSkipReasonName(aValue, aName); +} + +NS_IMETHODIMP +nsDNSService::GetCurrentTrrURI(nsACString& aURI) { + if (mTrrService) { + mTrrService->GetURI(aURI); + } + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::GetCurrentTrrMode(nsIDNSService::ResolverMode* aMode) { + *aMode = nsIDNSService::MODE_NATIVEONLY; // The default mode. + if (mTrrService) { + *aMode = mTrrService->Mode(); + } + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::GetCurrentTrrConfirmationState(uint32_t* aConfirmationState) { + *aConfirmationState = uint32_t(TRRService::CONFIRM_OFF); + if (mTrrService) { + *aConfirmationState = mTrrService->ConfirmationState(); + } + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::GetTrrDomain(nsACString& aTRRDomain) { + aTRRDomain.Truncate(); + nsAutoCString url; + if (mTrrService) { + mTrrService->GetURI(url); + } + nsCOMPtr<nsIURI> uri; + nsresult rv = NS_NewURI(getter_AddRefs(uri), url); + if (NS_FAILED(rv)) { + // An empty TRR domain in case of invalid URL. + return NS_OK; + } + return uri->GetHost(aTRRDomain); +} + +nsresult nsDNSService::GetTRRDomainKey(nsACString& aTRRDomain) { + aTRRDomain = TRRService::ProviderKey(); + return NS_OK; +} + +size_t nsDNSService::SizeOfIncludingThis( + mozilla::MallocSizeOf mallocSizeOf) const { + // Measurement of the following members may be added later if DMD finds it + // is worthwhile: + // - mIDN + // - mLock + + size_t n = mallocSizeOf(this); + n += mResolver ? mResolver->SizeOfIncludingThis(mallocSizeOf) : 0; + n += mIPv4OnlyDomains.SizeOfExcludingThisIfUnshared(mallocSizeOf); + n += mLocalDomains.SizeOfExcludingThis(mallocSizeOf); + n += mFailedSVCDomainNames.ShallowSizeOfExcludingThis(mallocSizeOf); + for (const auto& data : mFailedSVCDomainNames.Values()) { + n += data->ShallowSizeOfExcludingThis(mallocSizeOf); + for (const auto& name : *data) { + n += name.SizeOfExcludingThisIfUnshared(mallocSizeOf); + } + } + return n; +} + +MOZ_DEFINE_MALLOC_SIZE_OF(DNSServiceMallocSizeOf) + +NS_IMETHODIMP +nsDNSService::CollectReports(nsIHandleReportCallback* aHandleReport, + nsISupports* aData, bool aAnonymize) { + MOZ_COLLECT_REPORT("explicit/network/dns-service", KIND_HEAP, UNITS_BYTES, + SizeOfIncludingThis(DNSServiceMallocSizeOf), + "Memory used for the DNS service."); + + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::ReportFailedSVCDomainName(const nsACString& aOwnerName, + const nsACString& aSVCDomainName) { + MutexAutoLock lock(mLock); + + mFailedSVCDomainNames.GetOrInsertNew(aOwnerName, 1) + ->AppendElement(aSVCDomainName); + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::IsSVCDomainNameFailed(const nsACString& aOwnerName, + const nsACString& aSVCDomainName, + bool* aResult) { + NS_ENSURE_ARG(aResult); + + MutexAutoLock lock(mLock); + *aResult = false; + nsTArray<nsCString>* failedList = mFailedSVCDomainNames.Get(aOwnerName); + if (!failedList) { + return NS_OK; + } + + *aResult = failedList->Contains(aSVCDomainName); + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::ResetExcludedSVCDomainName(const nsACString& aOwnerName) { + MutexAutoLock lock(mLock); + mFailedSVCDomainNames.Remove(aOwnerName); + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::GetLastConfirmationStatus(nsresult* aConfirmationStatus) { + if (!mTrrService) { + return NS_ERROR_NOT_AVAILABLE; + } + *aConfirmationStatus = mTrrService->LastConfirmationStatus(); + return NS_OK; +} + +NS_IMETHODIMP nsDNSService::GetLastConfirmationSkipReason( + TRRSkippedReason* aSkipReason) { + if (!mTrrService) { + return NS_ERROR_NOT_AVAILABLE; + } + *aSkipReason = mTrrService->LastConfirmationSkipReason(); + return NS_OK; +} + +namespace mozilla::net { +nsresult GetTRRSkipReasonName(TRRSkippedReason aReason, nsACString& aName) { + static_assert(TRRSkippedReason::TRR_UNSET == 0); + static_assert(TRRSkippedReason::TRR_OK == 1); + static_assert(TRRSkippedReason::TRR_NO_GSERVICE == 2); + static_assert(TRRSkippedReason::TRR_PARENTAL_CONTROL == 3); + static_assert(TRRSkippedReason::TRR_OFF_EXPLICIT == 4); + static_assert(TRRSkippedReason::TRR_REQ_MODE_DISABLED == 5); + static_assert(TRRSkippedReason::TRR_MODE_NOT_ENABLED == 6); + static_assert(TRRSkippedReason::TRR_FAILED == 7); + static_assert(TRRSkippedReason::TRR_MODE_UNHANDLED_DEFAULT == 8); + static_assert(TRRSkippedReason::TRR_MODE_UNHANDLED_DISABLED == 9); + static_assert(TRRSkippedReason::TRR_DISABLED_FLAG == 10); + static_assert(TRRSkippedReason::TRR_TIMEOUT == 11); + static_assert(TRRSkippedReason::TRR_CHANNEL_DNS_FAIL == 12); + static_assert(TRRSkippedReason::TRR_IS_OFFLINE == 13); + static_assert(TRRSkippedReason::TRR_NOT_CONFIRMED == 14); + static_assert(TRRSkippedReason::TRR_DID_NOT_MAKE_QUERY == 15); + static_assert(TRRSkippedReason::TRR_UNKNOWN_CHANNEL_FAILURE == 16); + static_assert(TRRSkippedReason::TRR_HOST_BLOCKED_TEMPORARY == 17); + static_assert(TRRSkippedReason::TRR_SEND_FAILED == 18); + static_assert(TRRSkippedReason::TRR_NET_RESET == 19); + static_assert(TRRSkippedReason::TRR_NET_TIMEOUT == 20); + static_assert(TRRSkippedReason::TRR_NET_REFUSED == 21); + static_assert(TRRSkippedReason::TRR_NET_INTERRUPT == 22); + static_assert(TRRSkippedReason::TRR_NET_INADEQ_SEQURITY == 23); + static_assert(TRRSkippedReason::TRR_NO_ANSWERS == 24); + static_assert(TRRSkippedReason::TRR_DECODE_FAILED == 25); + static_assert(TRRSkippedReason::TRR_EXCLUDED == 26); + static_assert(TRRSkippedReason::TRR_SERVER_RESPONSE_ERR == 27); + static_assert(TRRSkippedReason::TRR_RCODE_FAIL == 28); + static_assert(TRRSkippedReason::TRR_NO_CONNECTIVITY == 29); + static_assert(TRRSkippedReason::TRR_NXDOMAIN == 30); + static_assert(TRRSkippedReason::TRR_REQ_CANCELLED == 31); + static_assert(TRRSkippedReason::ODOH_KEY_NOT_USABLE == 32); + static_assert(TRRSkippedReason::ODOH_UPDATE_KEY_FAILED == 33); + static_assert(TRRSkippedReason::ODOH_KEY_NOT_AVAILABLE == 34); + static_assert(TRRSkippedReason::ODOH_ENCRYPTION_FAILED == 35); + static_assert(TRRSkippedReason::ODOH_DECRYPTION_FAILED == 36); + static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_GOOGLE_SAFESEARCH == + 37); + static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_YOUTUBE_SAFESEARCH == + 38); + static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_ZSCALER_CANARY == 39); + static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_CANARY == 40); + static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_MODIFIED_ROOTS == 41); + static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_PARENTAL_CONTROLS == + 42); + static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_THIRD_PARTY_ROOTS == + 43); + static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_ENTERPRISE_POLICY == + 44); + static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_VPN == 45); + static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_PROXY == 46); + static_assert(TRRSkippedReason::TRR_HEURISTIC_TRIPPED_NRPT == 47); + static_assert(TRRSkippedReason::TRR_BAD_URL == 48); + + switch (aReason) { + case TRRSkippedReason::TRR_UNSET: + aName = "TRR_UNSET"_ns; + break; + case TRRSkippedReason::TRR_OK: + aName = "TRR_OK"_ns; + break; + case TRRSkippedReason::TRR_NO_GSERVICE: + aName = "TRR_NO_GSERVICE"_ns; + break; + case TRRSkippedReason::TRR_PARENTAL_CONTROL: + aName = "TRR_PARENTAL_CONTROL"_ns; + break; + case TRRSkippedReason::TRR_OFF_EXPLICIT: + aName = "TRR_OFF_EXPLICIT"_ns; + break; + case TRRSkippedReason::TRR_REQ_MODE_DISABLED: + aName = "TRR_REQ_MODE_DISABLED"_ns; + break; + case TRRSkippedReason::TRR_MODE_NOT_ENABLED: + aName = "TRR_MODE_NOT_ENABLED"_ns; + break; + case TRRSkippedReason::TRR_FAILED: + aName = "TRR_FAILED"_ns; + break; + case TRRSkippedReason::TRR_MODE_UNHANDLED_DEFAULT: + aName = "TRR_MODE_UNHANDLED_DEFAULT"_ns; + break; + case TRRSkippedReason::TRR_MODE_UNHANDLED_DISABLED: + aName = "TRR_MODE_UNHANDLED_DISABLED"_ns; + break; + case TRRSkippedReason::TRR_DISABLED_FLAG: + aName = "TRR_DISABLED_FLAG"_ns; + break; + case TRRSkippedReason::TRR_TIMEOUT: + aName = "TRR_TIMEOUT"_ns; + break; + case TRRSkippedReason::TRR_CHANNEL_DNS_FAIL: + aName = "TRR_CHANNEL_DNS_FAIL"_ns; + break; + case TRRSkippedReason::TRR_IS_OFFLINE: + aName = "TRR_IS_OFFLINE"_ns; + break; + case TRRSkippedReason::TRR_NOT_CONFIRMED: + aName = "TRR_NOT_CONFIRMED"_ns; + break; + case TRRSkippedReason::TRR_DID_NOT_MAKE_QUERY: + aName = "TRR_DID_NOT_MAKE_QUERY"_ns; + break; + case TRRSkippedReason::TRR_UNKNOWN_CHANNEL_FAILURE: + aName = "TRR_UNKNOWN_CHANNEL_FAILURE"_ns; + break; + case TRRSkippedReason::TRR_HOST_BLOCKED_TEMPORARY: + aName = "TRR_HOST_BLOCKED_TEMPORARY"_ns; + break; + case TRRSkippedReason::TRR_SEND_FAILED: + aName = "TRR_SEND_FAILED"_ns; + break; + case TRRSkippedReason::TRR_NET_RESET: + aName = "TRR_NET_RESET"_ns; + break; + case TRRSkippedReason::TRR_NET_TIMEOUT: + aName = "TRR_NET_TIMEOUT"_ns; + break; + case TRRSkippedReason::TRR_NET_REFUSED: + aName = "TRR_NET_REFUSED"_ns; + break; + case TRRSkippedReason::TRR_NET_INTERRUPT: + aName = "TRR_NET_INTERRUPT"_ns; + break; + case TRRSkippedReason::TRR_NET_INADEQ_SEQURITY: + aName = "TRR_NET_INADEQ_SEQURITY"_ns; + break; + case TRRSkippedReason::TRR_NO_ANSWERS: + aName = "TRR_NO_ANSWERS"_ns; + break; + case TRRSkippedReason::TRR_DECODE_FAILED: + aName = "TRR_DECODE_FAILED"_ns; + break; + case TRRSkippedReason::TRR_EXCLUDED: + aName = "TRR_EXCLUDED"_ns; + break; + case TRRSkippedReason::TRR_SERVER_RESPONSE_ERR: + aName = "TRR_SERVER_RESPONSE_ERR"_ns; + break; + case TRRSkippedReason::TRR_RCODE_FAIL: + aName = "TRR_RCODE_FAIL"_ns; + break; + case TRRSkippedReason::TRR_NO_CONNECTIVITY: + aName = "TRR_NO_CONNECTIVITY"_ns; + break; + case TRRSkippedReason::TRR_NXDOMAIN: + aName = "TRR_NXDOMAIN"_ns; + break; + case TRRSkippedReason::TRR_REQ_CANCELLED: + aName = "TRR_REQ_CANCELLED"_ns; + break; + case TRRSkippedReason::ODOH_KEY_NOT_USABLE: + aName = "ODOH_KEY_NOT_USABLE"_ns; + break; + case TRRSkippedReason::ODOH_UPDATE_KEY_FAILED: + aName = "ODOH_UPDATE_KEY_FAILED"_ns; + break; + case TRRSkippedReason::ODOH_KEY_NOT_AVAILABLE: + aName = "ODOH_KEY_NOT_AVAILABLE"_ns; + break; + case TRRSkippedReason::ODOH_ENCRYPTION_FAILED: + aName = "ODOH_ENCRYPTION_FAILED"_ns; + break; + case TRRSkippedReason::ODOH_DECRYPTION_FAILED: + aName = "ODOH_DECRYPTION_FAILED"_ns; + break; + case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_GOOGLE_SAFESEARCH: + aName = "TRR_HEURISTIC_TRIPPED_GOOGLE_SAFESEARCH"_ns; + break; + case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_YOUTUBE_SAFESEARCH: + aName = "TRR_HEURISTIC_TRIPPED_YOUTUBE_SAFESEARCH"_ns; + break; + case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_ZSCALER_CANARY: + aName = "TRR_HEURISTIC_TRIPPED_ZSCALER_CANARY"_ns; + break; + case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_CANARY: + aName = "TRR_HEURISTIC_TRIPPED_CANARY"_ns; + break; + case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_MODIFIED_ROOTS: + aName = "TRR_HEURISTIC_TRIPPED_MODIFIED_ROOTS"_ns; + break; + case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_PARENTAL_CONTROLS: + aName = "TRR_HEURISTIC_TRIPPED_PARENTAL_CONTROLS"_ns; + break; + case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_THIRD_PARTY_ROOTS: + aName = "TRR_HEURISTIC_TRIPPED_THIRD_PARTY_ROOTS"_ns; + break; + case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_ENTERPRISE_POLICY: + aName = "TRR_HEURISTIC_TRIPPED_ENTERPRISE_POLICY"_ns; + break; + case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_VPN: + aName = "TRR_HEURISTIC_TRIPPED_VPN"_ns; + break; + case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_PROXY: + aName = "TRR_HEURISTIC_TRIPPED_PROXY"_ns; + break; + case TRRSkippedReason::TRR_HEURISTIC_TRIPPED_NRPT: + aName = "TRR_HEURISTIC_TRIPPED_NRPT"_ns; + break; + case TRRSkippedReason::TRR_BAD_URL: + aName = "TRR_BAD_URL"_ns; + break; + default: + MOZ_ASSERT(false, "Unknown value"); + } + + return NS_OK; +} +} // namespace mozilla::net diff --git a/netwerk/dns/nsDNSService2.h b/netwerk/dns/nsDNSService2.h new file mode 100644 index 0000000000..11f22c038e --- /dev/null +++ b/netwerk/dns/nsDNSService2.h @@ -0,0 +1,135 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsDNSService2_h__ +#define nsDNSService2_h__ + +#include "DNSServiceBase.h" +#include "nsClassHashtable.h" +#include "nsPIDNSService.h" +#include "nsIIDNService.h" +#include "nsIMemoryReporter.h" +#include "nsIObserver.h" +#include "nsHostResolver.h" +#include "nsString.h" +#include "nsTHashSet.h" +#include "nsHashKeys.h" +#include "mozilla/Mutex.h" +#include "mozilla/Attributes.h" +#include "TRRService.h" + +class nsAuthSSPI; + +class DNSServiceWrapper final : public nsPIDNSService { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_FORWARD_NSPIDNSSERVICE(PIDNSService()->) + NS_FORWARD_NSIDNSSERVICE(DNSService()->) + + DNSServiceWrapper() = default; + + static already_AddRefed<nsIDNSService> GetSingleton(); + static void SwitchToBackupDNSService(); + + private: + ~DNSServiceWrapper() = default; + nsIDNSService* DNSService(); + nsPIDNSService* PIDNSService(); + + mozilla::Mutex mLock{"DNSServiceWrapper.mLock"}; + nsCOMPtr<nsIDNSService> mDNSServiceInUse; + nsCOMPtr<nsIDNSService> mBackupDNSService; +}; + +class nsDNSService final : public mozilla::net::DNSServiceBase, + public nsPIDNSService, + public nsIMemoryReporter { + public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSPIDNSSERVICE + NS_DECL_NSIDNSSERVICE + NS_DECL_NSIOBSERVER + NS_DECL_NSIMEMORYREPORTER + + nsDNSService() = default; + + static already_AddRefed<nsIDNSService> GetXPCOMSingleton(); + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; + + bool GetOffline() const; + + protected: + friend class nsAuthSSPI; + friend class DNSServiceWrapper; + + nsresult DeprecatedSyncResolve( + const nsACString& aHostname, nsIDNSService::DNSFlags flags, + const mozilla::OriginAttributes& aOriginAttributes, + nsIDNSRecord** result); + + private: + ~nsDNSService() = default; + + void ReadPrefs(const char* name) override; + static already_AddRefed<nsDNSService> GetSingleton(); + + uint16_t GetAFForLookup(const nsACString& host, + nsIDNSService::DNSFlags flags); + + nsresult PreprocessHostname(bool aLocalDomain, const nsACString& aInput, + nsIIDNService* aIDN, nsACString& aACE); + + nsresult AsyncResolveInternal( + const nsACString& aHostname, uint16_t type, nsIDNSService::DNSFlags flags, + nsIDNSAdditionalInfo* aInfo, nsIDNSListener* aListener, + nsIEventTarget* target_, + const mozilla::OriginAttributes& aOriginAttributes, + nsICancelable** result); + + nsresult CancelAsyncResolveInternal( + const nsACString& aHostname, uint16_t aType, + nsIDNSService::DNSFlags aFlags, nsIDNSAdditionalInfo* aInfo, + nsIDNSListener* aListener, nsresult aReason, + const mozilla::OriginAttributes& aOriginAttributes); + + nsresult ResolveInternal(const nsACString& aHostname, + nsIDNSService::DNSFlags flags, + const mozilla::OriginAttributes& aOriginAttributes, + nsIDNSRecord** result); + + // Locks the mutex and returns an addreffed resolver. May return null. + already_AddRefed<nsHostResolver> GetResolverLocked(); + + RefPtr<nsHostResolver> mResolver; + nsCOMPtr<nsIIDNService> mIDN; + + // mLock protects access to mResolver, mLocalDomains, mIPv4OnlyDomains and + // mFailedSVCDomainNames + mozilla::Mutex mLock MOZ_UNANNOTATED{"nsDNSServer.mLock"}; + + // mIPv4OnlyDomains is a comma-separated list of domains for which only + // IPv4 DNS lookups are performed. This allows the user to disable IPv6 on + // a per-domain basis and work around broken DNS servers. See bug 68796. + nsCString mIPv4OnlyDomains; + nsCString mForceResolve; + bool mBlockDotOnion = false; + bool mNotifyResolution = false; + bool mOfflineLocalhost = false; + bool mForceResolveOn = false; + nsTHashSet<nsCString> mLocalDomains; + RefPtr<mozilla::net::TRRService> mTrrService; + + uint32_t mResCacheEntries = 0; + uint32_t mResCacheExpiration = 0; + uint32_t mResCacheGrace = 0; + bool mResolverPrefsUpdated = false; + nsClassHashtable<nsCStringHashKey, nsTArray<nsCString>> mFailedSVCDomainNames; +}; + +already_AddRefed<nsIDNSService> GetOrInitDNSService(); + +#endif // nsDNSService2_h__ diff --git a/netwerk/dns/nsEffectiveTLDService.cpp b/netwerk/dns/nsEffectiveTLDService.cpp new file mode 100644 index 0000000000..fafbc296d5 --- /dev/null +++ b/netwerk/dns/nsEffectiveTLDService.cpp @@ -0,0 +1,518 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// This service reads a file of rules describing TLD-like domain names. For a +// complete description of the expected file format and parsing rules, see +// http://wiki.mozilla.org/Gecko:Effective_TLD_Service + +#include "mozilla/ArrayUtils.h" +#include "mozilla/HashFunctions.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/ResultExtensions.h" +#include "mozilla/TextUtils.h" +#include "mozilla/Try.h" + +#include "MainThreadUtils.h" +#include "nsContentUtils.h" +#include "nsCRT.h" +#include "nsEffectiveTLDService.h" +#include "nsIFile.h" +#include "nsIIDNService.h" +#include "nsIObserverService.h" +#include "nsIURI.h" +#include "nsNetCID.h" +#include "nsNetUtil.h" +#include "nsServiceManagerUtils.h" +#include "mozilla/net/DNS.h" + +namespace etld_dafsa { + +// Generated file that includes kDafsa +#include "etld_data.inc" + +} // namespace etld_dafsa + +using namespace mozilla; + +NS_IMPL_ISUPPORTS(nsEffectiveTLDService, nsIEffectiveTLDService, + nsIMemoryReporter, nsIObserver) + +// ---------------------------------------------------------------------- + +static nsEffectiveTLDService* gService = nullptr; + +nsEffectiveTLDService::nsEffectiveTLDService() + : mGraphLock("nsEffectiveTLDService::mGraph") { + mGraph.emplace(etld_dafsa::kDafsa); +} + +nsresult nsEffectiveTLDService::Init() { + MOZ_ASSERT(NS_IsMainThread()); + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + obs->AddObserver(this, "public-suffix-list-updated", false); + + if (gService) { + return NS_ERROR_ALREADY_INITIALIZED; + } + + nsresult rv; + mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv); + if (NS_FAILED(rv)) { + return rv; + } + + gService = this; + RegisterWeakMemoryReporter(this); + + return NS_OK; +} + +NS_IMETHODIMP nsEffectiveTLDService::Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) { + /** + * Signal sent from netwerk/dns/PublicSuffixList.jsm + * aSubject is the nsIFile object for dafsa.bin + * aData is the absolute path to the dafsa.bin file (not used) + */ + if (aSubject && (nsCRT::strcmp(aTopic, "public-suffix-list-updated") == 0)) { + nsCOMPtr<nsIFile> mDafsaBinFile(do_QueryInterface(aSubject)); + NS_ENSURE_TRUE(mDafsaBinFile, NS_ERROR_ILLEGAL_VALUE); + + AutoWriteLock lock(mGraphLock); + // Reset mGraph with kDafsa in case reassigning to mDafsaMap fails + mGraph.reset(); + mGraph.emplace(etld_dafsa::kDafsa); + + mDafsaMap.reset(); + mMruTable.Clear(); + + MOZ_TRY(mDafsaMap.init(mDafsaBinFile)); + + size_t size = mDafsaMap.size(); + const uint8_t* remoteDafsaPtr = mDafsaMap.get<uint8_t>().get(); + + auto remoteDafsa = mozilla::Span(remoteDafsaPtr, size); + + mGraph.reset(); + mGraph.emplace(remoteDafsa); + } + return NS_OK; +} + +nsEffectiveTLDService::~nsEffectiveTLDService() { + UnregisterWeakMemoryReporter(this); + if (mIDNService) { + // Only clear gService if Init() finished successfully. + gService = nullptr; + } +} + +// static +nsEffectiveTLDService* nsEffectiveTLDService::GetInstance() { + if (gService) { + return gService; + } + nsCOMPtr<nsIEffectiveTLDService> tldService = + do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID); + if (!tldService) { + return nullptr; + } + MOZ_ASSERT( + gService, + "gService must have been initialized in nsEffectiveTLDService::Init"); + return gService; +} + +MOZ_DEFINE_MALLOC_SIZE_OF(EffectiveTLDServiceMallocSizeOf) + +// The amount of heap memory measured here is tiny. It used to be bigger when +// nsEffectiveTLDService used a separate hash table instead of binary search. +// Nonetheless, we keep this code here in anticipation of bug 1083971 which will +// change ETLDEntries::entries to a heap-allocated array modifiable at runtime. +NS_IMETHODIMP +nsEffectiveTLDService::CollectReports(nsIHandleReportCallback* aHandleReport, + nsISupports* aData, bool aAnonymize) { + MOZ_COLLECT_REPORT("explicit/network/effective-TLD-service", KIND_HEAP, + UNITS_BYTES, + SizeOfIncludingThis(EffectiveTLDServiceMallocSizeOf), + "Memory used by the effective TLD service."); + + return NS_OK; +} + +size_t nsEffectiveTLDService::SizeOfIncludingThis( + mozilla::MallocSizeOf aMallocSizeOf) { + size_t n = aMallocSizeOf(this); + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - mIDNService + + return n; +} + +// External function for dealing with URI's correctly. +// Pulls out the host portion from an nsIURI, and calls through to +// GetPublicSuffixFromHost(). +NS_IMETHODIMP +nsEffectiveTLDService::GetPublicSuffix(nsIURI* aURI, + nsACString& aPublicSuffix) { + NS_ENSURE_ARG_POINTER(aURI); + + nsAutoCString host; + nsresult rv = NS_GetInnermostURIHost(aURI, host); + if (NS_FAILED(rv)) { + return rv; + } + + return GetBaseDomainInternal(host, 0, false, aPublicSuffix); +} + +NS_IMETHODIMP +nsEffectiveTLDService::GetKnownPublicSuffix(nsIURI* aURI, + nsACString& aPublicSuffix) { + NS_ENSURE_ARG_POINTER(aURI); + + nsAutoCString host; + nsresult rv = NS_GetInnermostURIHost(aURI, host); + if (NS_FAILED(rv)) { + return rv; + } + + return GetBaseDomainInternal(host, 0, true, aPublicSuffix); +} + +// External function for dealing with URI's correctly. +// Pulls out the host portion from an nsIURI, and calls through to +// GetBaseDomainFromHost(). +NS_IMETHODIMP +nsEffectiveTLDService::GetBaseDomain(nsIURI* aURI, uint32_t aAdditionalParts, + nsACString& aBaseDomain) { + NS_ENSURE_ARG_POINTER(aURI); + NS_ENSURE_TRUE(((int32_t)aAdditionalParts) >= 0, NS_ERROR_INVALID_ARG); + + nsAutoCString host; + nsresult rv = NS_GetInnermostURIHost(aURI, host); + if (NS_FAILED(rv)) { + return rv; + } + + return GetBaseDomainInternal(host, aAdditionalParts + 1, false, aBaseDomain); +} + +// External function for dealing with URIs to get a schemeless site. +// Calls through to GetBaseDomain(), handling IP addresses and aliases by +// just returning their serialized host. +NS_IMETHODIMP +nsEffectiveTLDService::GetSchemelessSite(nsIURI* aURI, nsACString& aSite) { + NS_ENSURE_ARG_POINTER(aURI); + + nsresult rv = GetBaseDomain(aURI, 0, aSite); + if (rv == NS_ERROR_HOST_IS_IP_ADDRESS || + rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) { + rv = nsContentUtils::GetHostOrIPv6WithBrackets(aURI, aSite); + } + return rv; +} + +// External function for dealing with URIs to get site correctly. +// Calls through to GetSchemelessSite(), and serializes with the scheme and +// "://" prepended. +NS_IMETHODIMP +nsEffectiveTLDService::GetSite(nsIURI* aURI, nsACString& aSite) { + NS_ENSURE_ARG_POINTER(aURI); + + nsAutoCString scheme; + nsresult rv = aURI->GetScheme(scheme); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoCString schemeless; + rv = GetSchemelessSite(aURI, schemeless); + NS_ENSURE_SUCCESS(rv, rv); + + // aURI (and thus BaseDomain) may be the string '.'. If so, fail. + if (schemeless.Length() == 1 && schemeless.Last() == '.') { + return NS_ERROR_INVALID_ARG; + } + + // Reject any URIs without a host that aren't file:// URIs. + if (schemeless.IsEmpty() && !aURI->SchemeIs("file")) { + return NS_ERROR_INVALID_ARG; + } + + aSite.SetCapacity(scheme.Length() + 3 + schemeless.Length()); + aSite.Append(scheme); + aSite.Append("://"_ns); + aSite.Append(schemeless); + + return NS_OK; +} + +// External function for dealing with a host string directly: finds the public +// suffix (e.g. co.uk) for the given hostname. See GetBaseDomainInternal(). +NS_IMETHODIMP +nsEffectiveTLDService::GetPublicSuffixFromHost(const nsACString& aHostname, + nsACString& aPublicSuffix) { + // Create a mutable copy of the hostname and normalize it to ACE. + // This will fail if the hostname includes invalid characters. + nsAutoCString normHostname(aHostname); + nsresult rv = NormalizeHostname(normHostname); + if (NS_FAILED(rv)) { + return rv; + } + + return GetBaseDomainInternal(normHostname, 0, false, aPublicSuffix); +} + +NS_IMETHODIMP +nsEffectiveTLDService::GetKnownPublicSuffixFromHost(const nsACString& aHostname, + nsACString& aPublicSuffix) { + // Create a mutable copy of the hostname and normalize it to ACE. + // This will fail if the hostname includes invalid characters. + nsAutoCString normHostname(aHostname); + nsresult rv = NormalizeHostname(normHostname); + if (NS_FAILED(rv)) { + return rv; + } + + return GetBaseDomainInternal(normHostname, 0, true, aPublicSuffix); +} + +// External function for dealing with a host string directly: finds the base +// domain (e.g. www.co.uk) for the given hostname and number of subdomain parts +// requested. See GetBaseDomainInternal(). +NS_IMETHODIMP +nsEffectiveTLDService::GetBaseDomainFromHost(const nsACString& aHostname, + uint32_t aAdditionalParts, + nsACString& aBaseDomain) { + NS_ENSURE_TRUE(((int32_t)aAdditionalParts) >= 0, NS_ERROR_INVALID_ARG); + + // Create a mutable copy of the hostname and normalize it to ACE. + // This will fail if the hostname includes invalid characters. + nsAutoCString normHostname(aHostname); + nsresult rv = NormalizeHostname(normHostname); + if (NS_FAILED(rv)) { + return rv; + } + + return GetBaseDomainInternal(normHostname, aAdditionalParts + 1, false, + aBaseDomain); +} + +NS_IMETHODIMP +nsEffectiveTLDService::GetNextSubDomain(const nsACString& aHostname, + nsACString& aBaseDomain) { + // Create a mutable copy of the hostname and normalize it to ACE. + // This will fail if the hostname includes invalid characters. + nsAutoCString normHostname(aHostname); + nsresult rv = NormalizeHostname(normHostname); + NS_ENSURE_SUCCESS(rv, rv); + + return GetBaseDomainInternal(normHostname, -1, false, aBaseDomain); +} + +// Finds the base domain for a host, with requested number of additional parts. +// This will fail, generating an error, if the host is an IPv4/IPv6 address, +// if more subdomain parts are requested than are available, or if the hostname +// includes characters that are not valid in a URL. Normalization is performed +// on the host string and the result will be in UTF8. +nsresult nsEffectiveTLDService::GetBaseDomainInternal( + nsCString& aHostname, int32_t aAdditionalParts, bool aOnlyKnownPublicSuffix, + nsACString& aBaseDomain) { + const int kExceptionRule = 1; + const int kWildcardRule = 2; + + if (aHostname.IsEmpty()) { + return NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS; + } + + // chomp any trailing dot, and keep track of it for later + bool trailingDot = aHostname.Last() == '.'; + if (trailingDot) { + aHostname.Truncate(aHostname.Length() - 1); + } + + // check the edge cases of the host being '.' or having a second trailing '.', + // since subsequent checks won't catch it. + if (aHostname.IsEmpty() || aHostname.Last() == '.') { + return NS_ERROR_INVALID_ARG; + } + + // Lookup in the cache if this is a normal query. This is restricted to + // main thread-only as the cache is not thread-safe. + Maybe<TldCache::Entry> entry; + if (aAdditionalParts == 1 && NS_IsMainThread()) { + auto p = mMruTable.Lookup(aHostname); + if (p) { + if (NS_FAILED(p.Data().mResult)) { + return p.Data().mResult; + } + + // There was a match, just return the cached value. + aBaseDomain = p.Data().mBaseDomain; + if (trailingDot) { + aBaseDomain.Append('.'); + } + + return NS_OK; + } + + entry = Some(p); + } + + // Check if we're dealing with an IPv4/IPv6 hostname, and return + if (mozilla::net::HostIsIPLiteral(aHostname)) { + // Update the MRU table if in use. + if (entry) { + entry->Set(TLDCacheEntry{aHostname, ""_ns, NS_ERROR_HOST_IS_IP_ADDRESS}); + } + + return NS_ERROR_HOST_IS_IP_ADDRESS; + } + + // Walk up the domain tree, most specific to least specific, + // looking for matches at each level. Note that a given level may + // have multiple attributes (e.g. IsWild() and IsNormal()). + const char* prevDomain = nullptr; + const char* currDomain = aHostname.get(); + const char* nextDot = strchr(currDomain, '.'); + const char* end = currDomain + aHostname.Length(); + // Default value of *eTLD is currDomain as set in the while loop below + const char* eTLD = nullptr; + bool hasKnownPublicSuffix = false; + while (true) { + // sanity check the string we're about to look up: it should not begin + // with a '.'; this would mean the hostname began with a '.' or had an + // embedded '..' sequence. + if (*currDomain == '.') { + // Update the MRU table if in use. + if (entry) { + entry->Set(TLDCacheEntry{aHostname, ""_ns, NS_ERROR_INVALID_ARG}); + } + + return NS_ERROR_INVALID_ARG; + } + + int result; + { + AutoReadLock lock(mGraphLock); + // Perform the lookup. + result = mGraph->Lookup(Substring(currDomain, end)); + } + if (result != Dafsa::kKeyNotFound) { + hasKnownPublicSuffix = true; + if (result == kWildcardRule && prevDomain) { + // wildcard rules imply an eTLD one level inferior to the match. + eTLD = prevDomain; + break; + } + if (result != kExceptionRule || !nextDot) { + // specific match, or we've hit the top domain level + eTLD = currDomain; + break; + } + if (result == kExceptionRule) { + // exception rules imply an eTLD one level superior to the match. + eTLD = nextDot + 1; + break; + } + } + + if (!nextDot) { + // we've hit the top domain level; use it by default. + eTLD = currDomain; + break; + } + + prevDomain = currDomain; + currDomain = nextDot + 1; + nextDot = strchr(currDomain, '.'); + } + + if (aOnlyKnownPublicSuffix && !hasKnownPublicSuffix) { + aBaseDomain.Truncate(); + return NS_OK; + } + + const char *begin, *iter; + if (aAdditionalParts < 0) { + NS_ASSERTION(aAdditionalParts == -1, + "aAdditionalParts can't be negative and different from -1"); + + for (iter = aHostname.get(); iter != eTLD && *iter != '.'; iter++) { + ; + } + + if (iter != eTLD) { + iter++; + } + if (iter != eTLD) { + aAdditionalParts = 0; + } + } else { + // count off the number of requested domains. + begin = aHostname.get(); + iter = eTLD; + + while (true) { + if (iter == begin) { + break; + } + + if (*(--iter) == '.' && aAdditionalParts-- == 0) { + ++iter; + ++aAdditionalParts; + break; + } + } + } + + if (aAdditionalParts != 0) { + // Update the MRU table if in use. + if (entry) { + entry->Set( + TLDCacheEntry{aHostname, ""_ns, NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS}); + } + + return NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS; + } + + aBaseDomain = Substring(iter, end); + + // Update the MRU table if in use. + if (entry) { + entry->Set(TLDCacheEntry{aHostname, nsCString(aBaseDomain), NS_OK}); + } + + // add on the trailing dot, if applicable + if (trailingDot) { + aBaseDomain.Append('.'); + } + + return NS_OK; +} + +// Normalizes the given hostname, component by component. ASCII/ACE +// components are lower-cased, and UTF-8 components are normalized per +// RFC 3454 and converted to ACE. +nsresult nsEffectiveTLDService::NormalizeHostname(nsCString& aHostname) { + if (!IsAscii(aHostname)) { + nsresult rv = mIDNService->ConvertUTF8toACE(aHostname, aHostname); + if (NS_FAILED(rv)) { + return rv; + } + } + + ToLowerCase(aHostname); + return NS_OK; +} + +NS_IMETHODIMP +nsEffectiveTLDService::HasRootDomain(const nsACString& aInput, + const nsACString& aHost, bool* aResult) { + return net::HasRootDomain(aInput, aHost, aResult); +} diff --git a/netwerk/dns/nsEffectiveTLDService.h b/netwerk/dns/nsEffectiveTLDService.h new file mode 100644 index 0000000000..e68156582a --- /dev/null +++ b/netwerk/dns/nsEffectiveTLDService.h @@ -0,0 +1,94 @@ +//* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef EffectiveTLDService_h +#define EffectiveTLDService_h + +#include "nsIEffectiveTLDService.h" + +#include "mozilla/AutoMemMap.h" +#include "mozilla/Attributes.h" +#include "mozilla/Dafsa.h" +#include "mozilla/Maybe.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/MruCache.h" +#include "mozilla/RWLock.h" + +#include "nsCOMPtr.h" +#include "nsHashKeys.h" +#include "nsIMemoryReporter.h" +#include "nsIObserver.h" +#include "nsString.h" + +class nsIIDNService; + +class nsEffectiveTLDService final : public nsIEffectiveTLDService, + public nsIObserver, + public nsIMemoryReporter { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIEFFECTIVETLDSERVICE + NS_DECL_NSIMEMORYREPORTER + NS_DECL_NSIOBSERVER + + nsEffectiveTLDService(); + nsresult Init(); + + static nsEffectiveTLDService* GetInstance(); + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf); + + private: + nsresult GetBaseDomainInternal(nsCString& aHostname, int32_t aAdditionalParts, + bool aOnlyKnownPublicSuffix, + nsACString& aBaseDomain); + nsresult NormalizeHostname(nsCString& aHostname); + ~nsEffectiveTLDService(); + + // Only set in `Init()` which is called during service construction. + nsCOMPtr<nsIIDNService> mIDNService; + + // The DAFSA provides a compact encoding of the rather large eTLD list. + mozilla::Maybe<mozilla::Dafsa> mGraph MOZ_GUARDED_BY(mGraphLock); + + // Memory map used for a new updated dafsa + mozilla::loader::AutoMemMap mDafsaMap MOZ_GUARDED_BY(mGraphLock); + + // Lock for mGraph and mDafsaMap + mozilla::RWLock mGraphLock; + + // Note that the cache entries here can record entries that were cached + // successfully or unsuccessfully. mResult must be checked before using an + // entry. If it's a success error code, the cache entry is valid and can be + // used. + struct TLDCacheEntry { + nsCString mHost; + nsCString mBaseDomain; + nsresult mResult; + }; + + // We use a small most recently used cache to compensate for DAFSA lookups + // being slightly slower than a binary search on a larger table of strings. + // + // We first check the cache for a matching result and avoid a DAFSA lookup + // if a match is found. Otherwise we lookup the domain in the DAFSA and then + // cache the result. During standard browsing the same domains are repeatedly + // fed into |GetBaseDomainInternal| so this ends up being an effective + // mitigation getting about a 99% hit rate with four tabs open. + struct TldCache + : public mozilla::MruCache<nsACString, TLDCacheEntry, TldCache> { + static mozilla::HashNumber Hash(const nsACString& aKey) { + return mozilla::HashString(aKey); + } + static bool Match(const nsACString& aKey, const TLDCacheEntry& aVal) { + return aKey == aVal.mHost; + } + }; + + // NOTE: Only used on the main thread, so not guarded by mGraphLock. + TldCache mMruTable; +}; + +#endif // EffectiveTLDService_h diff --git a/netwerk/dns/nsHostRecord.cpp b/netwerk/dns/nsHostRecord.cpp new file mode 100644 index 0000000000..159abb6e54 --- /dev/null +++ b/netwerk/dns/nsHostRecord.cpp @@ -0,0 +1,598 @@ +/* vim:set ts=4 sw=2 sts=2 et cin: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsHostRecord.h" +#include "TRRQuery.h" +// Put DNSLogging.h at the end to avoid LOG being overwritten by other headers. +#include "DNSLogging.h" +#include "mozilla/StaticPrefs_network.h" +#include "mozilla/Telemetry.h" +#include "TRRService.h" + +//---------------------------------------------------------------------------- +// this macro filters out any flags that are not used when constructing the +// host key. the significant flags are those that would affect the resulting +// host record (i.e., the flags that are passed down to PR_GetAddrInfoByName). +#define RES_KEY_FLAGS(_f) \ + ((_f) & \ + (nsHostResolver::RES_CANON_NAME | nsHostResolver::RES_DISABLE_TRR | \ + nsIDNSService::RESOLVE_TRR_MODE_MASK | nsHostResolver::RES_IP_HINT)) + +#define IS_ADDR_TYPE(_type) ((_type) == nsIDNSService::RESOLVE_TYPE_DEFAULT) +#define IS_OTHER_TYPE(_type) ((_type) != nsIDNSService::RESOLVE_TYPE_DEFAULT) + +//---------------------------------------------------------------------------- + +using namespace mozilla; +using namespace mozilla::net; + +nsHostKey::nsHostKey(const nsACString& aHost, const nsACString& aTrrServer, + uint16_t aType, nsIDNSService::DNSFlags aFlags, + uint16_t aAf, bool aPb, const nsACString& aOriginsuffix) + : host(aHost), + mTrrServer(aTrrServer), + type(aType), + flags(aFlags), + af(aAf), + pb(aPb), + originSuffix(aOriginsuffix) {} + +bool nsHostKey::operator==(const nsHostKey& other) const { + return host == other.host && mTrrServer == other.mTrrServer && + type == other.type && + RES_KEY_FLAGS(flags) == RES_KEY_FLAGS(other.flags) && af == other.af && + originSuffix == other.originSuffix; +} + +PLDHashNumber nsHostKey::Hash() const { + return AddToHash(HashString(host.get()), HashString(mTrrServer.get()), type, + RES_KEY_FLAGS(flags), af, HashString(originSuffix.get())); +} + +size_t nsHostKey::SizeOfExcludingThis( + mozilla::MallocSizeOf mallocSizeOf) const { + size_t n = 0; + n += host.SizeOfExcludingThisIfUnshared(mallocSizeOf); + n += mTrrServer.SizeOfExcludingThisIfUnshared(mallocSizeOf); + n += originSuffix.SizeOfExcludingThisIfUnshared(mallocSizeOf); + return n; +} + +//---------------------------------------------------------------------------- +// nsHostRecord +//---------------------------------------------------------------------------- + +NS_IMPL_ISUPPORTS0(nsHostRecord) + +nsHostRecord::nsHostRecord(const nsHostKey& key) + : nsHostKey(key), mTRRQuery("nsHostRecord.mTRRQuery") {} + +void nsHostRecord::Invalidate() { mDoomed = true; } + +void nsHostRecord::Cancel() { + RefPtr<TRRQuery> query; + { + auto lock = mTRRQuery.Lock(); + query.swap(lock.ref()); + } + + if (query) { + query->Cancel(NS_ERROR_ABORT); + } +} + +nsHostRecord::ExpirationStatus nsHostRecord::CheckExpiration( + const mozilla::TimeStamp& now) const { + if (!mGraceStart.IsNull() && now >= mGraceStart && !mValidEnd.IsNull() && + now < mValidEnd) { + return nsHostRecord::EXP_GRACE; + } + if (!mValidEnd.IsNull() && now < mValidEnd) { + return nsHostRecord::EXP_VALID; + } + + return nsHostRecord::EXP_EXPIRED; +} + +void nsHostRecord::SetExpiration(const mozilla::TimeStamp& now, + unsigned int valid, unsigned int grace) { + mValidStart = now; + if ((valid + grace) < 60) { + grace = 60 - valid; + LOG(("SetExpiration: artificially bumped grace to %d\n", grace)); + } + mGraceStart = now + TimeDuration::FromSeconds(valid); + mValidEnd = now + TimeDuration::FromSeconds(valid + grace); + mTtl = valid; +} + +void nsHostRecord::CopyExpirationTimesAndFlagsFrom( + const nsHostRecord* aFromHostRecord) { + // This is used to copy information from a cache entry to a record. All + // information necessary for HasUsableRecord needs to be copied. + mValidStart = aFromHostRecord->mValidStart; + mValidEnd = aFromHostRecord->mValidEnd; + mGraceStart = aFromHostRecord->mGraceStart; + mDoomed = aFromHostRecord->mDoomed; + mTtl = uint32_t(aFromHostRecord->mTtl); +} + +bool nsHostRecord::HasUsableResult(const mozilla::TimeStamp& now, + nsIDNSService::DNSFlags queryFlags) const { + if (mDoomed) { + return false; + } + + return HasUsableResultInternal(now, queryFlags); +} + +//---------------------------------------------------------------------------- +// AddrHostRecord +//---------------------------------------------------------------------------- + +static size_t SizeOfResolveHostCallbackListExcludingHead( + const mozilla::LinkedList<RefPtr<nsResolveHostCallback>>& aCallbacks, + MallocSizeOf mallocSizeOf) { + size_t n = aCallbacks.sizeOfExcludingThis(mallocSizeOf); + + for (const nsResolveHostCallback* t = aCallbacks.getFirst(); t; + t = t->getNext()) { + n += t->SizeOfIncludingThis(mallocSizeOf); + } + + return n; +} + +NS_IMPL_ISUPPORTS_INHERITED(AddrHostRecord, nsHostRecord, AddrHostRecord) + +AddrHostRecord::AddrHostRecord(const nsHostKey& key) : nsHostRecord(key) {} + +AddrHostRecord::~AddrHostRecord() { + mCallbacks.clear(); + Telemetry::Accumulate(Telemetry::DNS_BLACKLIST_COUNT, mUnusableCount); +} + +bool AddrHostRecord::Blocklisted(const NetAddr* aQuery) { + addr_info_lock.AssertCurrentThreadOwns(); + LOG(("Checking unusable list for host [%s], host record [%p].\n", host.get(), + this)); + + // skip the string conversion for the common case of no blocklist + if (!mUnusableItems.Length()) { + return false; + } + + char buf[kIPv6CStrBufSize]; + if (!aQuery->ToStringBuffer(buf, sizeof(buf))) { + return false; + } + nsDependentCString strQuery(buf); + + for (uint32_t i = 0; i < mUnusableItems.Length(); i++) { + if (mUnusableItems.ElementAt(i).Equals(strQuery)) { + LOG(("Address [%s] is blocklisted for host [%s].\n", buf, host.get())); + return true; + } + } + + return false; +} + +void AddrHostRecord::ReportUnusable(const NetAddr* aAddress) { + addr_info_lock.AssertCurrentThreadOwns(); + LOG( + ("Adding address to blocklist for host [%s], host record [%p]." + "used trr=%d\n", + host.get(), this, mTRRSuccess)); + + ++mUnusableCount; + + char buf[kIPv6CStrBufSize]; + if (aAddress->ToStringBuffer(buf, sizeof(buf))) { + LOG( + ("Successfully adding address [%s] to blocklist for host " + "[%s].\n", + buf, host.get())); + mUnusableItems.AppendElement(nsCString(buf)); + } +} + +void AddrHostRecord::ResetBlocklist() { + addr_info_lock.AssertCurrentThreadOwns(); + LOG(("Resetting blocklist for host [%s], host record [%p].\n", host.get(), + this)); + mUnusableItems.Clear(); +} + +size_t AddrHostRecord::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const { + size_t n = mallocSizeOf(this); + + n += nsHostKey::SizeOfExcludingThis(mallocSizeOf); + n += SizeOfResolveHostCallbackListExcludingHead(mCallbacks, mallocSizeOf); + + n += addr_info ? addr_info->SizeOfIncludingThis(mallocSizeOf) : 0; + n += mallocSizeOf(addr.get()); + + n += mUnusableItems.ShallowSizeOfExcludingThis(mallocSizeOf); + for (size_t i = 0; i < mUnusableItems.Length(); i++) { + n += mUnusableItems[i].SizeOfExcludingThisIfUnshared(mallocSizeOf); + } + return n; +} + +bool AddrHostRecord::HasUsableResultInternal( + const mozilla::TimeStamp& now, nsIDNSService::DNSFlags queryFlags) const { + // don't use cached negative results for high priority queries. + if (negative && IsHighPriority(queryFlags)) { + return false; + } + + if (CheckExpiration(now) == EXP_EXPIRED) { + return false; + } + + if (negative) { + return true; + } + + return addr_info || addr; +} + +// Returns true if the entry can be removed, or false if it should be left. +// Sets ResolveAgain true for entries being resolved right now. +bool AddrHostRecord::RemoveOrRefresh(bool aTrrToo) { + // no need to flush TRRed names, they're not resolved "locally" + MutexAutoLock lock(addr_info_lock); + if (addr_info && !aTrrToo && addr_info->IsTRR()) { + return false; + } + if (LoadNative()) { + if (!onQueue()) { + // The request has been passed to the OS resolver. The resultant DNS + // record should be considered stale and not trusted; set a flag to + // ensure it is called again. + StoreResolveAgain(true); + } + // if onQueue is true, the host entry is already added to the cache + // but is still pending to get resolved: just leave it in hash. + return false; + } + // Already resolved; not in a pending state; remove from cache + return true; +} + +void AddrHostRecord::NotifyRetryingTrr() { + MOZ_ASSERT(mFirstTRRSkippedReason == + mozilla::net::TRRSkippedReason::TRR_UNSET); + + // Save the skip reason of our first attempt for recording telemetry later. + mFirstTRRSkippedReason = mTRRSkippedReason; + mTRRSkippedReason = mozilla::net::TRRSkippedReason::TRR_UNSET; +} + +void AddrHostRecord::ResolveComplete() { + if (LoadNativeUsed()) { + if (mNativeSuccess) { + uint32_t millis = static_cast<uint32_t>(mNativeDuration.ToMilliseconds()); + Telemetry::Accumulate(Telemetry::DNS_NATIVE_LOOKUP_TIME, millis); + } + AccumulateCategoricalKeyed( + TRRService::ProviderKey(), + mNativeSuccess ? Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::osOK + : Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::osFail); + } + + if (mResolverType == DNSResolverType::TRR) { + if (mTRRSuccess) { + MOZ_DIAGNOSTIC_ASSERT(mTRRSkippedReason == + mozilla::net::TRRSkippedReason::TRR_OK); + uint32_t millis = static_cast<uint32_t>(mTrrDuration.ToMilliseconds()); + Telemetry::Accumulate(Telemetry::DNS_TRR_LOOKUP_TIME3, + TRRService::ProviderKey(), millis); + } + AccumulateCategoricalKeyed( + TRRService::ProviderKey(), + mTRRSuccess ? Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::trrOK + : Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::trrFail); + } + + if (nsHostResolver::Mode() == nsIDNSService::MODE_TRRFIRST || + nsHostResolver::Mode() == nsIDNSService::MODE_TRRONLY) { + MOZ_ASSERT(mTRRSkippedReason != mozilla::net::TRRSkippedReason::TRR_UNSET); + + Telemetry::Accumulate(Telemetry::TRR_SKIP_REASON_TRR_FIRST2, + TRRService::ProviderKey(), + static_cast<uint32_t>(mTRRSkippedReason)); + if (!mTRRSuccess && LoadNativeUsed()) { + Telemetry::Accumulate( + mNativeSuccess ? Telemetry::TRR_SKIP_REASON_NATIVE_SUCCESS + : Telemetry::TRR_SKIP_REASON_NATIVE_FAILED, + TRRService::ProviderKey(), static_cast<uint32_t>(mTRRSkippedReason)); + } + + if (IsRelevantTRRSkipReason(mTRRSkippedReason)) { + Telemetry::Accumulate(Telemetry::TRR_RELEVANT_SKIP_REASON_TRR_FIRST, + TRRService::ProviderKey(), + static_cast<uint32_t>(mTRRSkippedReason)); + + if (!mTRRSuccess && LoadNativeUsed()) { + Telemetry::Accumulate( + mNativeSuccess ? Telemetry::TRR_RELEVANT_SKIP_REASON_NATIVE_SUCCESS + : Telemetry::TRR_RELEVANT_SKIP_REASON_NATIVE_FAILED, + TRRService::ProviderKey(), + static_cast<uint32_t>(mTRRSkippedReason)); + } + } + + if (StaticPrefs::network_trr_retry_on_recoverable_errors() && + nsHostResolver::Mode() == nsIDNSService::MODE_TRRFIRST) { + nsAutoCString telemetryKey(TRRService::ProviderKey()); + + if (mFirstTRRSkippedReason != mozilla::net::TRRSkippedReason::TRR_UNSET) { + telemetryKey.AppendLiteral("|"); + telemetryKey.AppendInt(static_cast<uint32_t>(mFirstTRRSkippedReason)); + + Telemetry::Accumulate(mTRRSuccess + ? Telemetry::TRR_SKIP_REASON_RETRY_SUCCESS + : Telemetry::TRR_SKIP_REASON_RETRY_FAILED, + TRRService::ProviderKey(), + static_cast<uint32_t>(mFirstTRRSkippedReason)); + } + + Telemetry::Accumulate(Telemetry::TRR_SKIP_REASON_STRICT_MODE, + telemetryKey, + static_cast<uint32_t>(mTRRSkippedReason)); + + if (mTRRSuccess) { + Telemetry::Accumulate(Telemetry::TRR_ATTEMPT_COUNT, + TRRService::ProviderKey(), mTrrAttempts); + } + } + } + + if (mEffectiveTRRMode == nsIRequest::TRR_FIRST_MODE) { + if (flags & nsIDNSService::RESOLVE_DISABLE_TRR) { + // TRR is disabled on request, which is a next-level back-off method. + Telemetry::Accumulate(Telemetry::DNS_TRR_DISABLED3, + TRRService::ProviderKey(), mNativeSuccess); + } else { + if (mTRRSuccess) { + AccumulateCategoricalKeyed(TRRService::ProviderKey(), + Telemetry::LABELS_DNS_TRR_FIRST4::TRR); + } else if (mNativeSuccess) { + if (mResolverType == DNSResolverType::TRR) { + AccumulateCategoricalKeyed( + TRRService::ProviderKey(), + Telemetry::LABELS_DNS_TRR_FIRST4::NativeAfterTRR); + } else { + AccumulateCategoricalKeyed(TRRService::ProviderKey(), + Telemetry::LABELS_DNS_TRR_FIRST4::Native); + } + } else { + AccumulateCategoricalKeyed( + TRRService::ProviderKey(), + Telemetry::LABELS_DNS_TRR_FIRST4::BothFailed); + } + } + } + + switch (mEffectiveTRRMode) { + case nsIRequest::TRR_DISABLED_MODE: + AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::nativeOnly); + break; + case nsIRequest::TRR_FIRST_MODE: + AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::trrFirst); + break; + case nsIRequest::TRR_ONLY_MODE: + AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::trrOnly); + break; + case nsIRequest::TRR_DEFAULT_MODE: + MOZ_ASSERT_UNREACHABLE("We should not have a default value here"); + break; + } + + if (mResolverType == DNSResolverType::TRR && !mTRRSuccess && mNativeSuccess && + !LoadGetTtl() && TRRService::Get()) { + TRRService::Get()->AddToBlocklist(nsCString(host), originSuffix, pb, true); + } +} + +AddrHostRecord::DnsPriority AddrHostRecord::GetPriority( + nsIDNSService::DNSFlags aFlags) { + if (IsHighPriority(aFlags)) { + return AddrHostRecord::DNS_PRIORITY_HIGH; + } + if (IsMediumPriority(aFlags)) { + return AddrHostRecord::DNS_PRIORITY_MEDIUM; + } + + return AddrHostRecord::DNS_PRIORITY_LOW; +} + +nsresult AddrHostRecord::GetTtl(uint32_t* aResult) { + NS_ENSURE_ARG(aResult); + *aResult = mTtl; + return NS_OK; +} + +//---------------------------------------------------------------------------- +// TypeHostRecord +//---------------------------------------------------------------------------- + +NS_IMPL_ISUPPORTS_INHERITED(TypeHostRecord, nsHostRecord, TypeHostRecord, + nsIDNSTXTRecord, nsIDNSHTTPSSVCRecord) + +TypeHostRecord::TypeHostRecord(const nsHostKey& key) + : nsHostRecord(key), DNSHTTPSSVCRecordBase(key.host) {} + +TypeHostRecord::~TypeHostRecord() { mCallbacks.clear(); } + +bool TypeHostRecord::HasUsableResultInternal( + const mozilla::TimeStamp& now, nsIDNSService::DNSFlags queryFlags) const { + if (CheckExpiration(now) == EXP_EXPIRED) { + return false; + } + + if (negative) { + return true; + } + + return !mResults.is<Nothing>(); +} + +bool TypeHostRecord::RefreshForNegativeResponse() const { return false; } + +NS_IMETHODIMP TypeHostRecord::GetRecords(CopyableTArray<nsCString>& aRecords) { + // deep copy + MutexAutoLock lock(mResultsLock); + + if (!mResults.is<TypeRecordTxt>()) { + return NS_ERROR_NOT_AVAILABLE; + } + aRecords = mResults.as<CopyableTArray<nsCString>>(); + return NS_OK; +} + +NS_IMETHODIMP TypeHostRecord::GetRecordsAsOneString(nsACString& aRecords) { + // deep copy + MutexAutoLock lock(mResultsLock); + + if (!mResults.is<TypeRecordTxt>()) { + return NS_ERROR_NOT_AVAILABLE; + } + auto& results = mResults.as<CopyableTArray<nsCString>>(); + for (uint32_t i = 0; i < results.Length(); i++) { + aRecords.Append(results[i]); + } + return NS_OK; +} + +size_t TypeHostRecord::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const { + size_t n = mallocSizeOf(this); + + n += nsHostKey::SizeOfExcludingThis(mallocSizeOf); + n += SizeOfResolveHostCallbackListExcludingHead(mCallbacks, mallocSizeOf); + + return n; +} + +uint32_t TypeHostRecord::GetType() { + MutexAutoLock lock(mResultsLock); + + return mResults.match( + [](TypeRecordEmpty&) { + MOZ_ASSERT(false, "This should never be the case"); + return nsIDNSService::RESOLVE_TYPE_DEFAULT; + }, + [](TypeRecordTxt&) { return nsIDNSService::RESOLVE_TYPE_TXT; }, + [](TypeRecordHTTPSSVC&) { return nsIDNSService::RESOLVE_TYPE_HTTPSSVC; }); +} + +TypeRecordResultType TypeHostRecord::GetResults() { + MutexAutoLock lock(mResultsLock); + return mResults; +} + +NS_IMETHODIMP +TypeHostRecord::GetRecords(nsTArray<RefPtr<nsISVCBRecord>>& aRecords) { + MutexAutoLock lock(mResultsLock); + if (!mResults.is<TypeRecordHTTPSSVC>()) { + return NS_ERROR_NOT_AVAILABLE; + } + + auto& results = mResults.as<TypeRecordHTTPSSVC>(); + + for (const SVCB& r : results) { + RefPtr<nsISVCBRecord> rec = new mozilla::net::SVCBRecord(r); + aRecords.AppendElement(rec); + } + + return NS_OK; +} + +NS_IMETHODIMP +TypeHostRecord::GetServiceModeRecord(bool aNoHttp2, bool aNoHttp3, + nsISVCBRecord** aRecord) { + MutexAutoLock lock(mResultsLock); + if (!mResults.is<TypeRecordHTTPSSVC>()) { + return NS_ERROR_NOT_AVAILABLE; + } + + auto& results = mResults.as<TypeRecordHTTPSSVC>(); + nsCOMPtr<nsISVCBRecord> result = GetServiceModeRecordInternal( + aNoHttp2, aNoHttp3, results, mAllRecordsExcluded); + if (!result) { + return NS_ERROR_NOT_AVAILABLE; + } + + result.forget(aRecord); + return NS_OK; +} + +NS_IMETHODIMP +TypeHostRecord::GetAllRecordsWithEchConfig( + bool aNoHttp2, bool aNoHttp3, bool* aAllRecordsHaveEchConfig, + bool* aAllRecordsInH3ExcludedList, + nsTArray<RefPtr<nsISVCBRecord>>& aResult) { + MutexAutoLock lock(mResultsLock); + if (!mResults.is<TypeRecordHTTPSSVC>()) { + return NS_ERROR_NOT_AVAILABLE; + } + + auto& records = mResults.as<TypeRecordHTTPSSVC>(); + GetAllRecordsWithEchConfigInternal(aNoHttp2, aNoHttp3, records, + aAllRecordsHaveEchConfig, + aAllRecordsInH3ExcludedList, aResult); + return NS_OK; +} + +NS_IMETHODIMP +TypeHostRecord::GetHasIPAddresses(bool* aResult) { + NS_ENSURE_ARG(aResult); + MutexAutoLock lock(mResultsLock); + + if (!mResults.is<TypeRecordHTTPSSVC>()) { + return NS_ERROR_NOT_AVAILABLE; + } + + auto& results = mResults.as<TypeRecordHTTPSSVC>(); + *aResult = HasIPAddressesInternal(results); + return NS_OK; +} + +NS_IMETHODIMP +TypeHostRecord::GetAllRecordsExcluded(bool* aResult) { + NS_ENSURE_ARG(aResult); + MutexAutoLock lock(mResultsLock); + + if (!mResults.is<TypeRecordHTTPSSVC>()) { + return NS_ERROR_NOT_AVAILABLE; + } + + *aResult = mAllRecordsExcluded; + return NS_OK; +} + +NS_IMETHODIMP +TypeHostRecord::GetTtl(uint32_t* aResult) { + NS_ENSURE_ARG(aResult); + *aResult = mTtl; + return NS_OK; +} + +void TypeHostRecord::ResolveComplete() { + if (IsRelevantTRRSkipReason(mTRRSkippedReason)) { + Telemetry::Accumulate( + Telemetry::TRR_RELEVANT_SKIP_REASON_TRR_FIRST_TYPE_REC, + TRRService::ProviderKey(), static_cast<uint32_t>(mTRRSkippedReason)); + } + + uint32_t millis = static_cast<uint32_t>(mTrrDuration.ToMilliseconds()); + if (mTRRSuccess) { + Telemetry::Accumulate(Telemetry::DNS_BY_TYPE_SUCCEEDED_LOOKUP_TIME, millis); + } else { + Telemetry::Accumulate(Telemetry::DNS_BY_TYPE_FAILED_LOOKUP_TIME, millis); + } +} diff --git a/netwerk/dns/nsHostRecord.h b/netwerk/dns/nsHostRecord.h new file mode 100644 index 0000000000..c3a73907ae --- /dev/null +++ b/netwerk/dns/nsHostRecord.h @@ -0,0 +1,416 @@ +/* vim:set ts=4 sw=2 sts=2 et cin: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsHostRecord_h__ +#define nsHostRecord_h__ + +#include "mozilla/AtomicBitfields.h" +#include "mozilla/DataMutex.h" +#include "mozilla/LinkedList.h" +#include "mozilla/net/HTTPSSVC.h" +#include "nsIDNSService.h" +#include "nsIDNSByTypeRecord.h" +#include "PLDHashTable.h" +#include "nsITRRSkipReason.h" + +class nsHostRecord; +class nsHostResolver; + +namespace mozilla { +namespace net { +class HostRecordQueue; +class TRR; +class TRRQuery; +} // namespace net +} // namespace mozilla + +/** + * This class is used to notify listeners when a ResolveHost operation is + * complete. Classes that derive it must implement threadsafe nsISupports + * to be able to use RefPtr with this class. + */ +class nsResolveHostCallback + : public mozilla::LinkedListElement<RefPtr<nsResolveHostCallback>>, + public nsISupports { + public: + /** + * OnResolveHostComplete + * + * this function is called to complete a host lookup initiated by + * nsHostResolver::ResolveHost. it may be invoked recursively from + * ResolveHost or on an unspecified background thread. + * + * NOTE: it is the responsibility of the implementor of this method + * to handle the callback in a thread safe manner. + * + * @param resolver + * nsHostResolver object associated with this result + * @param record + * the host record containing the results of the lookup + * @param status + * if successful, |record| contains non-null results + */ + virtual void OnResolveHostComplete(nsHostResolver* resolver, + nsHostRecord* record, nsresult status) = 0; + /** + * EqualsAsyncListener + * + * Determines if the listener argument matches the listener member var. + * For subclasses not implementing a member listener, should return false. + * For subclasses having a member listener, the function should check if + * they are the same. Used for cases where a pointer to an object + * implementing nsResolveHostCallback is unknown, but a pointer to + * the original listener is known. + * + * @param aListener + * nsIDNSListener object associated with the original request + */ + virtual bool EqualsAsyncListener(nsIDNSListener* aListener) = 0; + + virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const = 0; + + protected: + virtual ~nsResolveHostCallback() = default; +}; + +struct nsHostKey { + const nsCString host; + const nsCString mTrrServer; + uint16_t type = 0; + nsIDNSService::DNSFlags flags = nsIDNSService::RESOLVE_DEFAULT_FLAGS; + uint16_t af = 0; + bool pb = false; + const nsCString originSuffix; + explicit nsHostKey(const nsACString& host, const nsACString& aTrrServer, + uint16_t type, nsIDNSService::DNSFlags flags, uint16_t af, + bool pb, const nsACString& originSuffix); + bool operator==(const nsHostKey& other) const; + size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; + PLDHashNumber Hash() const; +}; + +/** + * nsHostRecord - ref counted object type stored in host resolver cache. + */ +class nsHostRecord : public mozilla::LinkedListElement<RefPtr<nsHostRecord>>, + public nsHostKey, + public nsISupports { + using TRRSkippedReason = mozilla::net::TRRSkippedReason; + + public: + NS_DECL_THREADSAFE_ISUPPORTS + + virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { + return 0; + } + + // Returns the TRR mode encoded by the flags + nsIRequest::TRRMode TRRMode(); + + // Records the first reason that caused TRR to be skipped or to fail. + void RecordReason(TRRSkippedReason reason) { + if (mTRRSkippedReason == TRRSkippedReason::TRR_UNSET) { + mTRRSkippedReason = reason; + } + } + + enum DnsPriority { + DNS_PRIORITY_LOW = nsIDNSService::RESOLVE_PRIORITY_LOW, + DNS_PRIORITY_MEDIUM = nsIDNSService::RESOLVE_PRIORITY_MEDIUM, + DNS_PRIORITY_HIGH, + }; + + protected: + friend class nsHostResolver; + friend class mozilla::net::HostRecordQueue; + friend class mozilla::net::TRR; + friend class mozilla::net::TRRQuery; + + explicit nsHostRecord(const nsHostKey& key); + virtual ~nsHostRecord() = default; + + // Mark hostrecord as not usable + void Invalidate(); + + enum ExpirationStatus { + EXP_VALID, + EXP_GRACE, + EXP_EXPIRED, + }; + + ExpirationStatus CheckExpiration(const mozilla::TimeStamp& now) const; + + // Convenience function for setting the timestamps above (mValidStart, + // mValidEnd, and mGraceStart). valid and grace are durations in seconds. + void SetExpiration(const mozilla::TimeStamp& now, unsigned int valid, + unsigned int grace); + void CopyExpirationTimesAndFlagsFrom(const nsHostRecord* aFromHostRecord); + + // Checks if the record is usable (not expired and has a value) + bool HasUsableResult(const mozilla::TimeStamp& now, + nsIDNSService::DNSFlags queryFlags = + nsIDNSService::RESOLVE_DEFAULT_FLAGS) const; + + static DnsPriority GetPriority(nsIDNSService::DNSFlags aFlags); + + virtual void Cancel(); + virtual bool HasUsableResultInternal( + const mozilla::TimeStamp& now, + nsIDNSService::DNSFlags queryFlags) const = 0; + virtual bool RefreshForNegativeResponse() const { return true; } + + mozilla::LinkedList<RefPtr<nsResolveHostCallback>> mCallbacks; + + bool IsAddrRecord() const { + return type == nsIDNSService::RESOLVE_TYPE_DEFAULT; + } + + virtual void Reset() { + mTRRSkippedReason = TRRSkippedReason::TRR_UNSET; + mFirstTRRSkippedReason = TRRSkippedReason::TRR_UNSET; + mTrrAttempts = 0; + mTRRSuccess = false; + mNativeSuccess = false; + } + + virtual void OnCompleteLookup() {} + + virtual void ResolveComplete() = 0; + + // true if pending and on the queue (not yet given to getaddrinfo()) + bool onQueue() { return LoadNative() && isInList(); } + + // When the record began being valid. Used mainly for bookkeeping. + mozilla::TimeStamp mValidStart; + + // When the record is no longer valid (it's time of expiration) + mozilla::TimeStamp mValidEnd; + + // When the record enters its grace period. This must be before mValidEnd. + // If a record is in its grace period (and not expired), it will be used + // but a request to refresh it will be made. + mozilla::TimeStamp mGraceStart; + + mozilla::TimeDuration mTrrDuration; + + mozilla::Atomic<uint32_t, mozilla::Relaxed> mTtl{0}; + + // The computed TRR mode that is actually used by the request. + // It is set in nsHostResolver::NameLookup and is based on the mode of the + // default resolver and the TRRMode encoded in the flags. + // The mode into account if the TRR service is disabled, + // parental controls are on, domain matches exclusion list, etc. + mozilla::Atomic<nsIRequest::TRRMode> mEffectiveTRRMode{ + nsIRequest::TRR_DEFAULT_MODE}; + + mozilla::Atomic<TRRSkippedReason> mTRRSkippedReason{ + TRRSkippedReason::TRR_UNSET}; + TRRSkippedReason mFirstTRRSkippedReason = TRRSkippedReason::TRR_UNSET; + + mozilla::DataMutex<RefPtr<mozilla::net::TRRQuery>> mTRRQuery; + + // counter of outstanding resolving calls + mozilla::Atomic<int32_t> mResolving{0}; + + // Number of times we've attempted TRR. Reset when we refresh. + // TRR is attempted at most twice - first attempt and retry. + mozilla::Atomic<int32_t> mTrrAttempts{0}; + + // True if this record is a cache of a failed lookup. Negative cache + // entries are valid just like any other (though never for more than 60 + // seconds), but a use of that negative entry forces an asynchronous refresh. + bool negative = false; + + // Explicitly expired + bool mDoomed = false; + + // Whether this is resolved by TRR successfully or not. + bool mTRRSuccess = false; + + // Whether this is resolved by native resolver successfully or not. + bool mNativeSuccess = false; + + // When the lookups of this record started and their durations + mozilla::TimeStamp mNativeStart; + mozilla::TimeDuration mNativeDuration; + + // clang-format off + MOZ_ATOMIC_BITFIELDS(mAtomicBitfields, 8, ( + // true if this record is being resolved "natively", which means that + // it is either on the pending queue or owned by one of the worker threads. + (uint16_t, Native, 1), + (uint16_t, NativeUsed, 1), + // true if off queue and contributing to mActiveAnyThreadCount + (uint16_t, UsingAnyThread, 1), + (uint16_t, GetTtl, 1), + (uint16_t, ResolveAgain, 1) + )) + // clang-format on +}; + +// b020e996-f6ab-45e5-9bf5-1da71dd0053a +#define ADDRHOSTRECORD_IID \ + { \ + 0xb020e996, 0xf6ab, 0x45e5, { \ + 0x9b, 0xf5, 0x1d, 0xa7, 0x1d, 0xd0, 0x05, 0x3a \ + } \ + } + +class AddrHostRecord final : public nsHostRecord { + using Mutex = mozilla::Mutex; + using DNSResolverType = mozilla::net::DNSResolverType; + + public: + NS_DECLARE_STATIC_IID_ACCESSOR(ADDRHOSTRECORD_IID) + NS_DECL_ISUPPORTS_INHERITED + + /* a fully resolved host record has either a non-null |addr_info| or |addr| + * field. if |addr_info| is null, it implies that the |host| is an IP + * address literal. in which case, |addr| contains the parsed address. + * otherwise, if |addr_info| is non-null, then it contains one or many + * IP addresses corresponding to the given host name. if both |addr_info| + * and |addr| are null, then the given host has not yet been fully resolved. + * |af| is the address family of the record we are querying for. + */ + + /* the lock protects |addr_info| and |addr_info_gencnt| because they + * are mutable and accessed by the resolver worker thread and the + * nsDNSService2 class. |addr| doesn't change after it has been + * assigned a value. only the resolver worker thread modifies + * nsHostRecord (and only in nsHostResolver::CompleteLookup); + * the other threads just read it. therefore the resolver worker + * thread doesn't need to lock when reading |addr_info|. + */ + Mutex addr_info_lock MOZ_UNANNOTATED{"AddrHostRecord.addr_info_lock"}; + // generation count of |addr_info| + int addr_info_gencnt = 0; + RefPtr<mozilla::net::AddrInfo> addr_info; + mozilla::UniquePtr<mozilla::net::NetAddr> addr; + + // hold addr_info_lock when calling the blocklist functions + bool Blocklisted(const mozilla::net::NetAddr* query); + void ResetBlocklist(); + void ReportUnusable(const mozilla::net::NetAddr* aAddress); + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const override; + + nsIRequest::TRRMode EffectiveTRRMode() const { return mEffectiveTRRMode; } + nsITRRSkipReason::value TrrSkipReason() const { return mTRRSkippedReason; } + + nsresult GetTtl(uint32_t* aResult); + + private: + friend class nsHostResolver; + friend class mozilla::net::HostRecordQueue; + friend class mozilla::net::TRR; + friend class mozilla::net::TRRQuery; + + explicit AddrHostRecord(const nsHostKey& key); + ~AddrHostRecord(); + + // Checks if the record is usable (not expired and has a value) + bool HasUsableResultInternal( + const mozilla::TimeStamp& now, + nsIDNSService::DNSFlags queryFlags) const override; + + bool RemoveOrRefresh(bool aTrrToo); // Mark records currently being resolved + // as needed to resolve again. + + // Saves the skip reason of a first-attempt TRR lookup and clears + // it to prepare for a retry attempt. + void NotifyRetryingTrr(); + + static DnsPriority GetPriority(nsIDNSService::DNSFlags aFlags); + + virtual void Reset() override { + nsHostRecord::Reset(); + StoreNativeUsed(false); + mResolverType = DNSResolverType::Native; + } + + virtual void OnCompleteLookup() override { + nsHostRecord::OnCompleteLookup(); + // This should always be cleared when a request is completed. + StoreNative(false); + } + + void ResolveComplete() override; + + // TRR was used on this record + mozilla::Atomic<DNSResolverType> mResolverType{DNSResolverType::Native}; + + // The number of times ReportUnusable() has been called in the record's + // lifetime. + uint32_t mUnusableCount = 0; + + // a list of addresses associated with this record that have been reported + // as unusable. the list is kept as a set of strings to make it independent + // of gencnt. + nsTArray<nsCString> mUnusableItems; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(AddrHostRecord, ADDRHOSTRECORD_IID) + +// 77b786a7-04be-44f2-987c-ab8aa96676e0 +#define TYPEHOSTRECORD_IID \ + { \ + 0x77b786a7, 0x04be, 0x44f2, { \ + 0x98, 0x7c, 0xab, 0x8a, 0xa9, 0x66, 0x76, 0xe0 \ + } \ + } + +class TypeHostRecord final : public nsHostRecord, + public nsIDNSTXTRecord, + public nsIDNSHTTPSSVCRecord, + public mozilla::net::DNSHTTPSSVCRecordBase { + public: + NS_DECLARE_STATIC_IID_ACCESSOR(TYPEHOSTRECORD_IID) + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIDNSTXTRECORD + NS_DECL_NSIDNSHTTPSSVCRECORD + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const override; + uint32_t GetType(); + mozilla::net::TypeRecordResultType GetResults(); + + private: + friend class nsHostResolver; + friend class mozilla::net::TRR; + friend class mozilla::net::TRRQuery; + + explicit TypeHostRecord(const nsHostKey& key); + ~TypeHostRecord(); + + // Checks if the record is usable (not expired and has a value) + bool HasUsableResultInternal( + const mozilla::TimeStamp& now, + nsIDNSService::DNSFlags queryFlags) const override; + bool RefreshForNegativeResponse() const override; + + void ResolveComplete() override; + + mozilla::net::TypeRecordResultType mResults = AsVariant(mozilla::Nothing()); + mozilla::Mutex mResultsLock MOZ_UNANNOTATED{"TypeHostRecord.mResultsLock"}; + + mozilla::Maybe<nsCString> mOriginHost; + bool mAllRecordsExcluded = false; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(TypeHostRecord, TYPEHOSTRECORD_IID) + +static inline bool IsHighPriority(nsIDNSService::DNSFlags flags) { + return !(flags & (nsHostRecord::DNS_PRIORITY_LOW | + nsHostRecord::DNS_PRIORITY_MEDIUM)); +} + +static inline bool IsMediumPriority(nsIDNSService::DNSFlags flags) { + return flags & nsHostRecord::DNS_PRIORITY_MEDIUM; +} + +static inline bool IsLowPriority(nsIDNSService::DNSFlags flags) { + return flags & nsHostRecord::DNS_PRIORITY_LOW; +} + +#endif // nsHostRecord_h__ diff --git a/netwerk/dns/nsHostResolver.cpp b/netwerk/dns/nsHostResolver.cpp new file mode 100644 index 0000000000..ccd6dca57b --- /dev/null +++ b/netwerk/dns/nsHostResolver.cpp @@ -0,0 +1,1965 @@ +/* vim:set ts=4 sw=2 sts=2 et cin: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#if defined(HAVE_RES_NINIT) +# include <sys/types.h> +# include <netinet/in.h> +# include <arpa/inet.h> +# include <arpa/nameser.h> +# include <resolv.h> +# define RES_RETRY_ON_FAILURE +#endif + +#include <stdlib.h> +#include <ctime> +#include "nsHostResolver.h" +#include "nsError.h" +#include "nsISupports.h" +#include "nsISupportsUtils.h" +#include "nsIThreadManager.h" +#include "nsComponentManagerUtils.h" +#include "nsNetUtil.h" +#include "nsPrintfCString.h" +#include "nsXPCOMCIDInternal.h" +#include "prthread.h" +#include "prerror.h" +#include "prtime.h" +#include "mozilla/Logging.h" +#include "PLDHashTable.h" +#include "nsQueryObject.h" +#include "nsURLHelper.h" +#include "nsThreadUtils.h" +#include "nsThreadPool.h" +#include "GetAddrInfo.h" +#include "TRR.h" +#include "TRRQuery.h" +#include "TRRService.h" + +#include "mozilla/Atomics.h" +#include "mozilla/glean/GleanMetrics.h" +#include "mozilla/HashFunctions.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/Telemetry.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/Preferences.h" +#include "mozilla/StaticPrefs_network.h" +// Put DNSLogging.h at the end to avoid LOG being overwritten by other headers. +#include "DNSLogging.h" + +#ifdef XP_WIN +# include "mozilla/WindowsVersion.h" +#endif // XP_WIN + +#ifdef MOZ_WIDGET_ANDROID +# include "mozilla/jni/Utils.h" +#endif + +#define IS_ADDR_TYPE(_type) ((_type) == nsIDNSService::RESOLVE_TYPE_DEFAULT) +#define IS_OTHER_TYPE(_type) ((_type) != nsIDNSService::RESOLVE_TYPE_DEFAULT) + +using namespace mozilla; +using namespace mozilla::net; + +// None of our implementations expose a TTL for negative responses, so we use a +// constant always. +static const unsigned int NEGATIVE_RECORD_LIFETIME = 60; + +//---------------------------------------------------------------------------- + +// Use a persistent thread pool in order to avoid spinning up new threads all +// the time. In particular, thread creation results in a res_init() call from +// libc which is quite expensive. +// +// The pool dynamically grows between 0 and MaxResolverThreads() in size. New +// requests go first to an idle thread. If that cannot be found and there are +// fewer than MaxResolverThreads() currently in the pool a new thread is created +// for high priority requests. If the new request is at a lower priority a new +// thread will only be created if there are fewer than +// MaxResolverThreadsAnyPriority() currently outstanding. If a thread cannot be +// created or an idle thread located for the request it is queued. +// +// When the pool is greater than MaxResolverThreadsAnyPriority() in size a +// thread will be destroyed after ShortIdleTimeoutSeconds of idle time. Smaller +// pools use LongIdleTimeoutSeconds for a timeout period. + +// for threads 1 -> MaxResolverThreadsAnyPriority() +#define LongIdleTimeoutSeconds 300 +// for threads MaxResolverThreadsAnyPriority() + 1 -> MaxResolverThreads() +#define ShortIdleTimeoutSeconds 60 + +//---------------------------------------------------------------------------- + +namespace mozilla::net { +LazyLogModule gHostResolverLog("nsHostResolver"); +} // namespace mozilla::net + +//---------------------------------------------------------------------------- + +#if defined(RES_RETRY_ON_FAILURE) + +// this class represents the resolver state for a given thread. if we +// encounter a lookup failure, then we can invoke the Reset method on an +// instance of this class to reset the resolver (in case /etc/resolv.conf +// for example changed). this is mainly an issue on GNU systems since glibc +// only reads in /etc/resolv.conf once per thread. it may be an issue on +// other systems as well. + +class nsResState { + public: + nsResState() + // initialize mLastReset to the time when this object + // is created. this means that a reset will not occur + // if a thread is too young. the alternative would be + // to initialize this to the beginning of time, so that + // the first failure would cause a reset, but since the + // thread would have just started up, it likely would + // already have current /etc/resolv.conf info. + : mLastReset(PR_IntervalNow()) {} + + bool Reset() { + // reset no more than once per second + if (PR_IntervalToSeconds(PR_IntervalNow() - mLastReset) < 1) { + return false; + } + + mLastReset = PR_IntervalNow(); + auto result = res_ninit(&_res); + + LOG(("nsResState::Reset() > 'res_ninit' returned %d", result)); + return (result == 0); + } + + private: + PRIntervalTime mLastReset; +}; + +#endif // RES_RETRY_ON_FAILURE + +//---------------------------------------------------------------------------- + +static const char kPrefGetTtl[] = "network.dns.get-ttl"; +static const char kPrefNativeIsLocalhost[] = "network.dns.native-is-localhost"; +static const char kPrefThreadIdleTime[] = + "network.dns.resolver-thread-extra-idle-time-seconds"; +static bool sGetTtlEnabled = false; +mozilla::Atomic<bool, mozilla::Relaxed> gNativeIsLocalhost; +mozilla::Atomic<bool, mozilla::Relaxed> sNativeHTTPSSupported{false}; + +static void DnsPrefChanged(const char* aPref, void* aSelf) { + MOZ_ASSERT(NS_IsMainThread(), + "Should be getting pref changed notification on main thread!"); + + MOZ_ASSERT(aSelf); + + if (!strcmp(aPref, kPrefGetTtl)) { +#ifdef DNSQUERY_AVAILABLE + sGetTtlEnabled = Preferences::GetBool(kPrefGetTtl); +#endif + } else if (!strcmp(aPref, kPrefNativeIsLocalhost)) { + gNativeIsLocalhost = Preferences::GetBool(kPrefNativeIsLocalhost); + } +} + +NS_IMPL_ISUPPORTS0(nsHostResolver) + +nsHostResolver::nsHostResolver(uint32_t maxCacheEntries, + uint32_t defaultCacheEntryLifetime, + uint32_t defaultGracePeriod) + : mMaxCacheEntries(maxCacheEntries), + mDefaultCacheLifetime(defaultCacheEntryLifetime), + mDefaultGracePeriod(defaultGracePeriod), + mIdleTaskCV(mLock, "nsHostResolver.mIdleTaskCV") { + mCreationTime = PR_Now(); + + mLongIdleTimeout = TimeDuration::FromSeconds(LongIdleTimeoutSeconds); + mShortIdleTimeout = TimeDuration::FromSeconds(ShortIdleTimeoutSeconds); +} + +nsHostResolver::~nsHostResolver() = default; + +nsresult nsHostResolver::Init() MOZ_NO_THREAD_SAFETY_ANALYSIS { + MOZ_ASSERT(NS_IsMainThread()); + if (NS_FAILED(GetAddrInfoInit())) { + return NS_ERROR_FAILURE; + } + + LOG(("nsHostResolver::Init this=%p", this)); + + mShutdown = false; + mNCS = NetworkConnectivityService::GetSingleton(); + + // The preferences probably haven't been loaded from the disk yet, so we + // need to register a callback that will set up the experiment once they + // are. We also need to explicitly set a value for the props otherwise the + // callback won't be called. + { + DebugOnly<nsresult> rv = Preferences::RegisterCallbackAndCall( + &DnsPrefChanged, kPrefGetTtl, this); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "Could not register DNS TTL pref callback."); + rv = Preferences::RegisterCallbackAndCall(&DnsPrefChanged, + kPrefNativeIsLocalhost, this); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "Could not register DNS pref callback."); + } + +#if defined(HAVE_RES_NINIT) + // We want to make sure the system is using the correct resolver settings, + // so we force it to reload those settings whenever we startup a subsequent + // nsHostResolver instance. We assume that there is no reason to do this + // for the first nsHostResolver instance since that is usually created + // during application startup. + static int initCount = 0; + if (initCount++ > 0) { + auto result = res_ninit(&_res); + LOG(("nsHostResolver::Init > 'res_ninit' returned %d", result)); + } +#endif + + // We can configure the threadpool to keep threads alive for a while after + // the last ThreadFunc task has been executed. + int32_t poolTimeoutSecs = Preferences::GetInt(kPrefThreadIdleTime, 60); + uint32_t poolTimeoutMs; + if (poolTimeoutSecs < 0) { + // This means never shut down the idle threads + poolTimeoutMs = UINT32_MAX; + } else { + // We clamp down the idle time between 0 and one hour. + poolTimeoutMs = + mozilla::clamped<uint32_t>(poolTimeoutSecs * 1000, 0, 3600 * 1000); + } + +#if defined(XP_WIN) + // For some reason, the DNSQuery_A API doesn't work on Windows 10. + // It returns a success code, but no records. We only allow + // native HTTPS records on Win 11 for now. + sNativeHTTPSSupported = StaticPrefs::network_dns_native_https_query_win10() || + mozilla::IsWin11OrLater(); +#elif defined(MOZ_WIDGET_ANDROID) + // android_res_nquery only got added in API level 29 + sNativeHTTPSSupported = jni::GetAPIVersion() >= 29; +#elif defined(XP_LINUX) || defined(XP_MACOSX) + sNativeHTTPSSupported = true; +#endif + LOG(("Native HTTPS records supported=%d", bool(sNativeHTTPSSupported))); + + nsCOMPtr<nsIThreadPool> threadPool = new nsThreadPool(); + MOZ_ALWAYS_SUCCEEDS(threadPool->SetThreadLimit(MaxResolverThreads())); + MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadLimit(MaxResolverThreads())); + MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadTimeout(poolTimeoutMs)); + MOZ_ALWAYS_SUCCEEDS( + threadPool->SetThreadStackSize(nsIThreadManager::kThreadPoolStackSize)); + MOZ_ALWAYS_SUCCEEDS(threadPool->SetName("DNS Resolver"_ns)); + mResolverThreads = ToRefPtr(std::move(threadPool)); + + return NS_OK; +} + +void nsHostResolver::ClearPendingQueue( + LinkedList<RefPtr<nsHostRecord>>& aPendingQ) { + // loop through pending queue, erroring out pending lookups. + if (!aPendingQ.isEmpty()) { + for (const RefPtr<nsHostRecord>& rec : aPendingQ) { + rec->Cancel(); + if (rec->IsAddrRecord()) { + CompleteLookup(rec, NS_ERROR_ABORT, nullptr, rec->pb, rec->originSuffix, + rec->mTRRSkippedReason, nullptr); + } else { + mozilla::net::TypeRecordResultType empty(Nothing{}); + CompleteLookupByType(rec, NS_ERROR_ABORT, empty, rec->mTRRSkippedReason, + 0, rec->pb); + } + } + } +} + +// +// FlushCache() is what we call when the network has changed. We must not +// trust names that were resolved before this change. They may resolve +// differently now. +// +// This function removes all existing resolved host entries from the hash. +// Names that are in the pending queues can be left there. Entries in the +// cache that have 'Resolve' set true but not 'OnQueue' are being resolved +// right now, so we need to mark them to get re-resolved on completion! + +void nsHostResolver::FlushCache(bool aTrrToo) { + MutexAutoLock lock(mLock); + + mQueue.FlushEvictionQ(mRecordDB, lock); + + // Refresh the cache entries that are resolving RIGHT now, remove the rest. + for (auto iter = mRecordDB.Iter(); !iter.Done(); iter.Next()) { + nsHostRecord* record = iter.UserData(); + // Try to remove the record, or mark it for refresh. + // By-type records are from TRR. We do not need to flush those entry + // when the network has change, because they are not local. + if (record->IsAddrRecord()) { + RefPtr<AddrHostRecord> addrRec = do_QueryObject(record); + MOZ_ASSERT(addrRec); + if (addrRec->RemoveOrRefresh(aTrrToo)) { + mQueue.MaybeRemoveFromQ(record, lock); + LOG(("Removing (%s) Addr record from mRecordDB", record->host.get())); + iter.Remove(); + } + } else if (aTrrToo) { + // remove by type records + LOG(("Removing (%s) type record from mRecordDB", record->host.get())); + iter.Remove(); + } + } +} + +void nsHostResolver::Shutdown() { + LOG(("Shutting down host resolver.\n")); + + { + DebugOnly<nsresult> rv = + Preferences::UnregisterCallback(&DnsPrefChanged, kPrefGetTtl, this); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "Could not unregister DNS TTL pref callback."); + } + + LinkedList<RefPtr<nsHostRecord>> pendingQHigh, pendingQMed, pendingQLow, + evictionQ; + + { + MutexAutoLock lock(mLock); + + mShutdown = true; + + if (mNumIdleTasks) { + mIdleTaskCV.NotifyAll(); + } + + mQueue.ClearAll( + [&](nsHostRecord* aRec) { + mLock.AssertCurrentThreadOwns(); + if (aRec->IsAddrRecord()) { + CompleteLookupLocked(aRec, NS_ERROR_ABORT, nullptr, aRec->pb, + aRec->originSuffix, aRec->mTRRSkippedReason, + nullptr, lock); + } else { + mozilla::net::TypeRecordResultType empty(Nothing{}); + CompleteLookupByTypeLocked(aRec, NS_ERROR_ABORT, empty, + aRec->mTRRSkippedReason, 0, aRec->pb, + lock); + } + }, + lock); + + for (const auto& data : mRecordDB.Values()) { + data->Cancel(); + } + // empty host database + mRecordDB.Clear(); + + mNCS = nullptr; + } + + // Shutdown the resolver threads, but with a timeout of 2 seconds (prefable). + // If the timeout is exceeded, any stuck threads will be leaked. + mResolverThreads->ShutdownWithTimeout( + StaticPrefs::network_dns_resolver_shutdown_timeout_ms()); + + { + mozilla::DebugOnly<nsresult> rv = GetAddrInfoShutdown(); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to shutdown GetAddrInfo"); + } +} + +nsresult nsHostResolver::GetHostRecord( + const nsACString& host, const nsACString& aTrrServer, uint16_t type, + nsIDNSService::DNSFlags flags, uint16_t af, bool pb, + const nsCString& originSuffix, nsHostRecord** result) { + MutexAutoLock lock(mLock); + nsHostKey key(host, aTrrServer, type, flags, af, pb, originSuffix); + + RefPtr<nsHostRecord> rec = + mRecordDB.LookupOrInsertWith(key, [&] { return InitRecord(key); }); + if (rec->IsAddrRecord()) { + RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec); + if (addrRec->addr) { + return NS_ERROR_FAILURE; + } + } + + if (rec->mResolving) { + return NS_ERROR_FAILURE; + } + + *result = rec.forget().take(); + return NS_OK; +} + +nsHostRecord* nsHostResolver::InitRecord(const nsHostKey& key) { + if (IS_ADDR_TYPE(key.type)) { + return new AddrHostRecord(key); + } + return new TypeHostRecord(key); +} + +already_AddRefed<nsHostRecord> nsHostResolver::InitLoopbackRecord( + const nsHostKey& key, nsresult* aRv) { + MOZ_ASSERT(aRv); + MOZ_ASSERT(IS_ADDR_TYPE(key.type)); + + *aRv = NS_ERROR_FAILURE; + RefPtr<nsHostRecord> rec = InitRecord(key); + + nsTArray<NetAddr> addresses; + NetAddr addr; + if (key.af == PR_AF_INET || key.af == PR_AF_UNSPEC) { + MOZ_RELEASE_ASSERT(NS_SUCCEEDED(addr.InitFromString("127.0.0.1"_ns))); + addresses.AppendElement(addr); + } + if (key.af == PR_AF_INET6 || key.af == PR_AF_UNSPEC) { + MOZ_RELEASE_ASSERT(NS_SUCCEEDED(addr.InitFromString("::1"_ns))); + addresses.AppendElement(addr); + } + + RefPtr<AddrInfo> ai = + new AddrInfo(rec->host, DNSResolverType::Native, 0, std::move(addresses)); + + RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec); + MutexAutoLock lock(addrRec->addr_info_lock); + addrRec->addr_info = ai; + addrRec->SetExpiration(TimeStamp::NowLoRes(), mDefaultCacheLifetime, + mDefaultGracePeriod); + addrRec->negative = false; + + *aRv = NS_OK; + return rec.forget(); +} + +static bool IsNativeHTTPSEnabled() { + if (!StaticPrefs::network_dns_native_https_query()) { + return false; + } + return sNativeHTTPSSupported; +} + +nsresult nsHostResolver::ResolveHost(const nsACString& aHost, + const nsACString& aTrrServer, + int32_t aPort, uint16_t type, + const OriginAttributes& aOriginAttributes, + nsIDNSService::DNSFlags flags, uint16_t af, + nsResolveHostCallback* aCallback) { + nsAutoCString host(aHost); + NS_ENSURE_TRUE(!host.IsEmpty(), NS_ERROR_UNEXPECTED); + + nsAutoCString originSuffix; + aOriginAttributes.CreateSuffix(originSuffix); + LOG(("Resolving host [%s]<%s>%s%s type %d. [this=%p]\n", host.get(), + originSuffix.get(), flags & RES_BYPASS_CACHE ? " - bypassing cache" : "", + flags & RES_REFRESH_CACHE ? " - refresh cache" : "", type, this)); + + // ensure that we are working with a valid hostname before proceeding. see + // bug 304904 for details. + if (!net_IsValidHostName(host)) { + return NS_ERROR_UNKNOWN_HOST; + } + + // If TRR is disabled we can return immediately if the native API is disabled + if (!IsNativeHTTPSEnabled() && IS_OTHER_TYPE(type) && + Mode() == nsIDNSService::MODE_TRROFF) { + return NS_ERROR_UNKNOWN_HOST; + } + + // Used to try to parse to an IP address literal. + NetAddr tempAddr; + if (IS_OTHER_TYPE(type) && (NS_SUCCEEDED(tempAddr.InitFromString(host)))) { + // For by-type queries the host cannot be IP literal. + return NS_ERROR_UNKNOWN_HOST; + } + + RefPtr<nsResolveHostCallback> callback(aCallback); + // if result is set inside the lock, then we need to issue the + // callback before returning. + RefPtr<nsHostRecord> result; + nsresult status = NS_OK, rv = NS_OK; + { + MutexAutoLock lock(mLock); + + if (mShutdown) { + return NS_ERROR_NOT_INITIALIZED; + } + + // check to see if there is already an entry for this |host| + // in the hash table. if so, then check to see if we can't + // just reuse the lookup result. otherwise, if there are + // any pending callbacks, then add to pending callbacks queue, + // and return. otherwise, add ourselves as first pending + // callback, and proceed to do the lookup. + + Maybe<nsCString> originHost; + if (StaticPrefs::network_dns_port_prefixed_qname_https_rr() && + type == nsIDNSService::RESOLVE_TYPE_HTTPSSVC && aPort != -1 && + aPort != 443) { + originHost = Some(host); + host = nsPrintfCString("_%d._https.%s", aPort, host.get()); + LOG((" Using port prefixed host name [%s]", host.get())); + } + + bool excludedFromTRR = false; + if (TRRService::Get() && TRRService::Get()->IsExcludedFromTRR(host)) { + flags |= nsIDNSService::RESOLVE_DISABLE_TRR; + excludedFromTRR = true; + + if (!aTrrServer.IsEmpty()) { + return NS_ERROR_UNKNOWN_HOST; + } + } + + nsHostKey key(host, aTrrServer, type, flags, af, + (aOriginAttributes.mPrivateBrowsingId > 0), originSuffix); + + // Check if we have a localhost domain, if so hardcode to loopback + if (IS_ADDR_TYPE(type) && IsLoopbackHostname(host)) { + nsresult rv; + RefPtr<nsHostRecord> result = InitLoopbackRecord(key, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + MOZ_ASSERT(result); + aCallback->OnResolveHostComplete(this, result, NS_OK); + return NS_OK; + } + + RefPtr<nsHostRecord> rec = + mRecordDB.LookupOrInsertWith(key, [&] { return InitRecord(key); }); + + RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec); + MOZ_ASSERT(rec, "Record should not be null"); + MOZ_ASSERT((IS_ADDR_TYPE(type) && rec->IsAddrRecord() && addrRec) || + (IS_OTHER_TYPE(type) && !rec->IsAddrRecord())); + + if (IS_OTHER_TYPE(type) && originHost) { + RefPtr<TypeHostRecord> typeRec = do_QueryObject(rec); + typeRec->mOriginHost = std::move(originHost); + } + + if (excludedFromTRR) { + rec->RecordReason(TRRSkippedReason::TRR_EXCLUDED); + } + + if (!(flags & RES_BYPASS_CACHE) && + rec->HasUsableResult(TimeStamp::NowLoRes(), flags)) { + result = FromCache(rec, host, type, status, lock); + } else if (addrRec && addrRec->addr) { + // if the host name is an IP address literal and has been + // parsed, go ahead and use it. + LOG((" Using cached address for IP Literal [%s].\n", host.get())); + result = FromCachedIPLiteral(rec); + } else if (addrRec && NS_SUCCEEDED(tempAddr.InitFromString(host))) { + // try parsing the host name as an IP address literal to short + // circuit full host resolution. (this is necessary on some + // platforms like Win9x. see bug 219376 for more details.) + LOG((" Host is IP Literal [%s].\n", host.get())); + result = FromIPLiteral(addrRec, tempAddr); + } else if (mQueue.PendingCount() >= MAX_NON_PRIORITY_REQUESTS && + !IsHighPriority(flags) && !rec->mResolving) { + LOG( + (" Lookup queue full: dropping %s priority request for " + "host [%s].\n", + IsMediumPriority(flags) ? "medium" : "low", host.get())); + if (IS_ADDR_TYPE(type)) { + Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_OVERFLOW); + } + // This is a lower priority request and we are swamped, so refuse it. + rv = NS_ERROR_DNS_LOOKUP_QUEUE_FULL; + + // Check if the offline flag is set. + } else if (flags & RES_OFFLINE) { + LOG((" Offline request for host [%s]; ignoring.\n", host.get())); + rv = NS_ERROR_OFFLINE; + + // We do not have a valid result till here. + // A/AAAA request can check for an alternative entry like AF_UNSPEC. + // Otherwise we need to start a new query. + } else if (!rec->mResolving) { + result = + FromUnspecEntry(rec, host, aTrrServer, originSuffix, type, flags, af, + aOriginAttributes.mPrivateBrowsingId > 0, status); + // If this is a by-type request or if no valid record was found + // in the cache or this is an AF_UNSPEC request, then start a + // new lookup. + if (!result) { + LOG((" No usable record in cache for host [%s] type %d.", host.get(), + type)); + + if (flags & RES_REFRESH_CACHE) { + rec->Invalidate(); + } + + // Add callback to the list of pending callbacks. + rec->mCallbacks.insertBack(callback); + rec->flags = flags; + rv = NameLookup(rec, lock); + if (IS_ADDR_TYPE(type)) { + Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, + METHOD_NETWORK_FIRST); + } + if (NS_FAILED(rv) && callback->isInList()) { + callback->remove(); + } else { + LOG( + (" DNS lookup for host [%s] blocking " + "pending 'getaddrinfo' or trr query: " + "callback [%p]", + host.get(), callback.get())); + } + } + } else { + LOG( + (" Host [%s] is being resolved. Appending callback " + "[%p].", + host.get(), callback.get())); + + rec->mCallbacks.insertBack(callback); + + if (rec && rec->onQueue()) { + Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, + METHOD_NETWORK_SHARED); + + // Consider the case where we are on a pending queue of + // lower priority than the request is being made at. + // In that case we should upgrade to the higher queue. + + if (IsHighPriority(flags) && !IsHighPriority(rec->flags)) { + // Move from (low|med) to high. + mQueue.MoveToAnotherPendingQ(rec, flags, lock); + rec->flags = flags; + ConditionallyCreateThread(rec); + } else if (IsMediumPriority(flags) && IsLowPriority(rec->flags)) { + // Move from low to med. + mQueue.MoveToAnotherPendingQ(rec, flags, lock); + rec->flags = flags; + mIdleTaskCV.Notify(); + } + } + } + + if (result && callback->isInList()) { + callback->remove(); + } + } // lock + + if (result) { + callback->OnResolveHostComplete(this, result, status); + } + + return rv; +} + +already_AddRefed<nsHostRecord> nsHostResolver::FromCache( + nsHostRecord* aRec, const nsACString& aHost, uint16_t aType, + nsresult& aStatus, const MutexAutoLock& aLock) { + LOG((" Using cached record for host [%s].\n", + nsPromiseFlatCString(aHost).get())); + + // put reference to host record on stack... + RefPtr<nsHostRecord> result = aRec; + if (IS_ADDR_TYPE(aType)) { + Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT); + } + + // For entries that are in the grace period + // or all cached negative entries, use the cache but start a new + // lookup in the background + ConditionallyRefreshRecord(aRec, aHost, aLock); + + if (aRec->negative) { + LOG((" Negative cache entry for host [%s].\n", + nsPromiseFlatCString(aHost).get())); + if (IS_ADDR_TYPE(aType)) { + Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_NEGATIVE_HIT); + } + aStatus = NS_ERROR_UNKNOWN_HOST; + } + + return result.forget(); +} + +already_AddRefed<nsHostRecord> nsHostResolver::FromCachedIPLiteral( + nsHostRecord* aRec) { + Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_LITERAL); + RefPtr<nsHostRecord> result = aRec; + return result.forget(); +} + +already_AddRefed<nsHostRecord> nsHostResolver::FromIPLiteral( + AddrHostRecord* aAddrRec, const NetAddr& aAddr) { + // ok, just copy the result into the host record, and be + // done with it! ;-) + aAddrRec->addr = MakeUnique<NetAddr>(aAddr); + Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_LITERAL); + // put reference to host record on stack... + RefPtr<nsHostRecord> result = aAddrRec; + return result.forget(); +} + +already_AddRefed<nsHostRecord> nsHostResolver::FromUnspecEntry( + nsHostRecord* aRec, const nsACString& aHost, const nsACString& aTrrServer, + const nsACString& aOriginSuffix, uint16_t aType, + nsIDNSService::DNSFlags aFlags, uint16_t af, bool aPb, nsresult& aStatus) { + RefPtr<nsHostRecord> result = nullptr; + // If this is an IPV4 or IPV6 specific request, check if there is + // an AF_UNSPEC entry we can use. Otherwise, hit the resolver... + RefPtr<AddrHostRecord> addrRec = do_QueryObject(aRec); + if (addrRec && !(aFlags & RES_BYPASS_CACHE) && + ((af == PR_AF_INET) || (af == PR_AF_INET6))) { + // Check for an AF_UNSPEC entry. + + const nsHostKey unspecKey(aHost, aTrrServer, + nsIDNSService::RESOLVE_TYPE_DEFAULT, aFlags, + PR_AF_UNSPEC, aPb, aOriginSuffix); + RefPtr<nsHostRecord> unspecRec = mRecordDB.Get(unspecKey); + + TimeStamp now = TimeStamp::NowLoRes(); + if (unspecRec && unspecRec->HasUsableResult(now, aFlags)) { + MOZ_ASSERT(unspecRec->IsAddrRecord()); + + RefPtr<AddrHostRecord> addrUnspecRec = do_QueryObject(unspecRec); + MOZ_ASSERT(addrUnspecRec); + MOZ_ASSERT(addrUnspecRec->addr_info || addrUnspecRec->negative, + "Entry should be resolved or negative."); + + LOG((" Trying AF_UNSPEC entry for host [%s] af: %s.\n", + PromiseFlatCString(aHost).get(), + (af == PR_AF_INET) ? "AF_INET" : "AF_INET6")); + + // We need to lock in case any other thread is reading + // addr_info. + MutexAutoLock lock(addrRec->addr_info_lock); + + addrRec->addr_info = nullptr; + addrRec->addr_info_gencnt++; + if (unspecRec->negative) { + aRec->negative = unspecRec->negative; + aRec->CopyExpirationTimesAndFlagsFrom(unspecRec); + } else if (addrUnspecRec->addr_info) { + MutexAutoLock lock(addrUnspecRec->addr_info_lock); + if (addrUnspecRec->addr_info) { + // Search for any valid address in the AF_UNSPEC entry + // in the cache (not blocklisted and from the right + // family). + nsTArray<NetAddr> addresses; + for (const auto& addr : addrUnspecRec->addr_info->Addresses()) { + if ((af == addr.inet.family) && + !addrUnspecRec->Blocklisted(&addr)) { + addresses.AppendElement(addr); + } + } + if (!addresses.IsEmpty()) { + addrRec->addr_info = new AddrInfo( + addrUnspecRec->addr_info->Hostname(), + addrUnspecRec->addr_info->CanonicalHostname(), + addrUnspecRec->addr_info->ResolverType(), + addrUnspecRec->addr_info->TRRType(), std::move(addresses)); + addrRec->addr_info_gencnt++; + aRec->CopyExpirationTimesAndFlagsFrom(unspecRec); + } + } + } + // Now check if we have a new record. + if (aRec->HasUsableResult(now, aFlags)) { + result = aRec; + if (aRec->negative) { + aStatus = NS_ERROR_UNKNOWN_HOST; + } + Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT); + ConditionallyRefreshRecord(aRec, aHost, lock); + } else if (af == PR_AF_INET6) { + // For AF_INET6, a new lookup means another AF_UNSPEC + // lookup. We have already iterated through the + // AF_UNSPEC addresses, so we mark this record as + // negative. + LOG( + (" No AF_INET6 in AF_UNSPEC entry: " + "host [%s] unknown host.", + nsPromiseFlatCString(aHost).get())); + result = aRec; + aRec->negative = true; + aStatus = NS_ERROR_UNKNOWN_HOST; + Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, + METHOD_NEGATIVE_HIT); + } + } + } + + return result.forget(); +} + +void nsHostResolver::DetachCallback( + const nsACString& host, const nsACString& aTrrServer, uint16_t aType, + const OriginAttributes& aOriginAttributes, nsIDNSService::DNSFlags flags, + uint16_t af, nsResolveHostCallback* aCallback, nsresult status) { + RefPtr<nsHostRecord> rec; + RefPtr<nsResolveHostCallback> callback(aCallback); + + { + MutexAutoLock lock(mLock); + + nsAutoCString originSuffix; + aOriginAttributes.CreateSuffix(originSuffix); + + nsHostKey key(host, aTrrServer, aType, flags, af, + (aOriginAttributes.mPrivateBrowsingId > 0), originSuffix); + RefPtr<nsHostRecord> entry = mRecordDB.Get(key); + if (entry) { + // walk list looking for |callback|... we cannot assume + // that it will be there! + + for (nsResolveHostCallback* c : entry->mCallbacks) { + if (c == callback) { + rec = entry; + c->remove(); + break; + } + } + } + } + + // complete callback with the given status code; this would only be done if + // the record was in the process of being resolved. + if (rec) { + callback->OnResolveHostComplete(this, rec, status); + } +} + +nsresult nsHostResolver::ConditionallyCreateThread(nsHostRecord* rec) { + if (mNumIdleTasks) { + // wake up idle tasks to process this lookup + mIdleTaskCV.Notify(); + } else if ((mActiveTaskCount < MaxResolverThreadsAnyPriority()) || + (IsHighPriority(rec->flags) && + mActiveTaskCount < MaxResolverThreads())) { + nsCOMPtr<nsIRunnable> event = mozilla::NewRunnableMethod( + "nsHostResolver::ThreadFunc", this, &nsHostResolver::ThreadFunc); + mActiveTaskCount++; + nsresult rv = + mResolverThreads->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL); + if (NS_FAILED(rv)) { + mActiveTaskCount--; + } + } else { + LOG((" Unable to find a thread for looking up host [%s].\n", + rec->host.get())); + } + return NS_OK; +} + +nsresult nsHostResolver::TrrLookup_unlocked(nsHostRecord* rec, TRR* pushedTRR) { + MutexAutoLock lock(mLock); + return TrrLookup(rec, lock, pushedTRR); +} + +void nsHostResolver::MaybeRenewHostRecord(nsHostRecord* aRec) { + MutexAutoLock lock(mLock); + MaybeRenewHostRecordLocked(aRec, lock); +} + +void nsHostResolver::MaybeRenewHostRecordLocked(nsHostRecord* aRec, + const MutexAutoLock& aLock) { + mQueue.MaybeRenewHostRecord(aRec, aLock); +} + +bool nsHostResolver::TRRServiceEnabledForRecord(nsHostRecord* aRec) { + MOZ_ASSERT(aRec, "Record must not be empty"); + MOZ_ASSERT(aRec->mEffectiveTRRMode != nsIRequest::TRR_DEFAULT_MODE, + "effective TRR mode must be computed before this call"); + if (!TRRService::Get()) { + aRec->RecordReason(TRRSkippedReason::TRR_NO_GSERVICE); + return false; + } + + // We always try custom resolvers. + if (!aRec->mTrrServer.IsEmpty()) { + return true; + } + + nsIRequest::TRRMode reqMode = aRec->mEffectiveTRRMode; + if (TRRService::Get()->Enabled(reqMode)) { + return true; + } + + if (NS_IsOffline()) { + // If we are in the NOT_CONFIRMED state _because_ we lack connectivity, + // then we should report that the browser is offline instead. + aRec->RecordReason(TRRSkippedReason::TRR_IS_OFFLINE); + return false; + } + + auto hasConnectivity = [this]() -> bool { + mLock.AssertCurrentThreadOwns(); + if (!mNCS) { + return true; + } + nsINetworkConnectivityService::ConnectivityState ipv4 = mNCS->GetIPv4(); + nsINetworkConnectivityService::ConnectivityState ipv6 = mNCS->GetIPv6(); + + if (ipv4 == nsINetworkConnectivityService::OK || + ipv6 == nsINetworkConnectivityService::OK) { + return true; + } + + if (ipv4 == nsINetworkConnectivityService::UNKNOWN || + ipv6 == nsINetworkConnectivityService::UNKNOWN) { + // One of the checks hasn't completed yet. Optimistically assume we'll + // have network connectivity. + return true; + } + + return false; + }; + + if (!hasConnectivity()) { + aRec->RecordReason(TRRSkippedReason::TRR_NO_CONNECTIVITY); + return false; + } + + bool isConfirmed = TRRService::Get()->IsConfirmed(); + if (!isConfirmed) { + aRec->RecordReason(TRRSkippedReason::TRR_NOT_CONFIRMED); + } + + return isConfirmed; +} + +// returns error if no TRR resolve is issued +// it is impt this is not called while a native lookup is going on +nsresult nsHostResolver::TrrLookup(nsHostRecord* aRec, + const MutexAutoLock& aLock, TRR* pushedTRR) { + if (Mode() == nsIDNSService::MODE_TRROFF || + StaticPrefs::network_dns_disabled()) { + return NS_ERROR_UNKNOWN_HOST; + } + LOG(("TrrLookup host:%s af:%" PRId16, aRec->host.get(), aRec->af)); + + RefPtr<nsHostRecord> rec(aRec); + mLock.AssertCurrentThreadOwns(); + + RefPtr<AddrHostRecord> addrRec; + RefPtr<TypeHostRecord> typeRec; + + if (rec->IsAddrRecord()) { + addrRec = do_QueryObject(rec); + MOZ_ASSERT(addrRec); + } else { + typeRec = do_QueryObject(rec); + MOZ_ASSERT(typeRec); + } + + MOZ_ASSERT(!rec->mResolving); + + if (!TRRServiceEnabledForRecord(aRec)) { + return NS_ERROR_UNKNOWN_HOST; + } + + MaybeRenewHostRecordLocked(rec, aLock); + + RefPtr<TRRQuery> query = new TRRQuery(this, rec); + nsresult rv = query->DispatchLookup(pushedTRR); + if (NS_FAILED(rv)) { + rec->RecordReason(TRRSkippedReason::TRR_DID_NOT_MAKE_QUERY); + return rv; + } + + { + auto lock = rec->mTRRQuery.Lock(); + MOZ_ASSERT(!lock.ref(), "TRR already in progress"); + lock.ref() = query; + } + + rec->mResolving++; + rec->mTrrAttempts++; + rec->StoreNative(false); + return NS_OK; +} + +nsresult nsHostResolver::NativeLookup(nsHostRecord* aRec, + const MutexAutoLock& aLock) { + if (StaticPrefs::network_dns_disabled()) { + return NS_ERROR_UNKNOWN_HOST; + } + LOG(("NativeLookup host:%s af:%" PRId16, aRec->host.get(), aRec->af)); + + // If this is not a A/AAAA request, make sure native HTTPS is enabled. + MOZ_ASSERT(aRec->IsAddrRecord() || IsNativeHTTPSEnabled()); + mLock.AssertCurrentThreadOwns(); + + RefPtr<nsHostRecord> rec(aRec); + + rec->mNativeStart = TimeStamp::Now(); + + // Add rec to one of the pending queues, possibly removing it from mEvictionQ. + MaybeRenewHostRecordLocked(aRec, aLock); + + mQueue.InsertRecord(rec, rec->flags, aLock); + + rec->StoreNative(true); + rec->StoreNativeUsed(true); + rec->mResolving++; + + nsresult rv = ConditionallyCreateThread(rec); + + LOG((" DNS thread counters: total=%d any-live=%d idle=%d pending=%d\n", + static_cast<uint32_t>(mActiveTaskCount), + static_cast<uint32_t>(mActiveAnyThreadCount), + static_cast<uint32_t>(mNumIdleTasks), mQueue.PendingCount())); + + return rv; +} + +// static +nsIDNSService::ResolverMode nsHostResolver::Mode() { + if (TRRService::Get()) { + return TRRService::Get()->Mode(); + } + + // If we don't have a TRR service just return MODE_TRROFF so we don't make + // any TRR requests by mistake. + return nsIDNSService::MODE_TRROFF; +} + +nsIRequest::TRRMode nsHostRecord::TRRMode() { + return nsIDNSService::GetTRRModeFromFlags(flags); +} + +// static +void nsHostResolver::ComputeEffectiveTRRMode(nsHostRecord* aRec) { + nsIDNSService::ResolverMode resolverMode = nsHostResolver::Mode(); + nsIRequest::TRRMode requestMode = aRec->TRRMode(); + + // For domains that are excluded from TRR or when parental control is enabled, + // we fallback to NativeLookup. This happens even in MODE_TRRONLY. By default + // localhost and local are excluded (so we cover *.local hosts) See the + // network.trr.excluded-domains pref. + + if (!TRRService::Get()) { + aRec->RecordReason(TRRSkippedReason::TRR_NO_GSERVICE); + aRec->mEffectiveTRRMode = requestMode; + return; + } + + if (!aRec->mTrrServer.IsEmpty()) { + aRec->mEffectiveTRRMode = nsIRequest::TRR_ONLY_MODE; + return; + } + + if (TRRService::Get()->IsExcludedFromTRR(aRec->host)) { + aRec->RecordReason(TRRSkippedReason::TRR_EXCLUDED); + aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE; + return; + } + + if (TRRService::Get()->ParentalControlEnabled()) { + aRec->RecordReason(TRRSkippedReason::TRR_PARENTAL_CONTROL); + aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE; + return; + } + + if (resolverMode == nsIDNSService::MODE_TRROFF) { + aRec->RecordReason(TRRSkippedReason::TRR_OFF_EXPLICIT); + aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE; + return; + } + + if (requestMode == nsIRequest::TRR_DISABLED_MODE) { + aRec->RecordReason(TRRSkippedReason::TRR_REQ_MODE_DISABLED); + aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE; + return; + } + + if ((requestMode == nsIRequest::TRR_DEFAULT_MODE && + resolverMode == nsIDNSService::MODE_NATIVEONLY)) { + if (StaticPrefs::network_trr_display_fallback_warning()) { + TRRSkippedReason heuristicResult = + TRRService::Get()->GetHeuristicDetectionResult(); + if (heuristicResult != TRRSkippedReason::TRR_UNSET && + heuristicResult != TRRSkippedReason::TRR_OK) { + aRec->RecordReason(heuristicResult); + aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE; + return; + } + } + aRec->RecordReason(TRRSkippedReason::TRR_MODE_NOT_ENABLED); + aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE; + return; + } + + if (requestMode == nsIRequest::TRR_DEFAULT_MODE && + resolverMode == nsIDNSService::MODE_TRRFIRST) { + aRec->mEffectiveTRRMode = nsIRequest::TRR_FIRST_MODE; + return; + } + + if (requestMode == nsIRequest::TRR_DEFAULT_MODE && + resolverMode == nsIDNSService::MODE_TRRONLY) { + aRec->mEffectiveTRRMode = nsIRequest::TRR_ONLY_MODE; + return; + } + + aRec->mEffectiveTRRMode = requestMode; +} + +// Kick-off a name resolve operation, using native resolver and/or TRR +nsresult nsHostResolver::NameLookup(nsHostRecord* rec, + const mozilla::MutexAutoLock& aLock) { + LOG(("NameLookup host:%s af:%" PRId16, rec->host.get(), rec->af)); + mLock.AssertCurrentThreadOwns(); + + if (rec->flags & RES_IP_HINT) { + LOG(("Skip lookup if RES_IP_HINT is set\n")); + return NS_ERROR_UNKNOWN_HOST; + } + + nsresult rv = NS_ERROR_UNKNOWN_HOST; + if (rec->mResolving) { + LOG(("NameLookup %s while already resolving\n", rec->host.get())); + return NS_OK; + } + + // Make sure we reset the reason each time we attempt to do a new lookup + // so we don't wrongly report the reason for the previous one. + rec->Reset(); + + ComputeEffectiveTRRMode(rec); + + if (!rec->mTrrServer.IsEmpty()) { + LOG(("NameLookup: %s use trr:%s", rec->host.get(), rec->mTrrServer.get())); + if (rec->mEffectiveTRRMode != nsIRequest::TRR_ONLY_MODE) { + return NS_ERROR_UNKNOWN_HOST; + } + + if (rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR) { + LOG(("TRR with server and DISABLE_TRR flag. Returning error.")); + return NS_ERROR_UNKNOWN_HOST; + } + return TrrLookup(rec, aLock); + } + + LOG(("NameLookup: %s effectiveTRRmode: %d flags: %X", rec->host.get(), + static_cast<nsIRequest::TRRMode>(rec->mEffectiveTRRMode), rec->flags)); + + if (rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR) { + rec->RecordReason(TRRSkippedReason::TRR_DISABLED_FLAG); + } + + bool serviceNotReady = !TRRServiceEnabledForRecord(rec); + + if (rec->mEffectiveTRRMode != nsIRequest::TRR_DISABLED_MODE && + !((rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR)) && + !serviceNotReady) { + rv = TrrLookup(rec, aLock); + } + + if (rec->mEffectiveTRRMode == nsIRequest::TRR_DISABLED_MODE || + (rec->mEffectiveTRRMode == nsIRequest::TRR_FIRST_MODE && + (rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR || serviceNotReady || + NS_FAILED(rv)))) { + if (!IsNativeHTTPSEnabled() && !rec->IsAddrRecord()) { + return rv; + } + +#ifdef DEBUG + // If we use this branch then the mTRRUsed flag should not be set + // Even if we did call TrrLookup above, the fact that it failed sync-ly + // means that we didn't actually succeed in opening the channel. + RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec); + MOZ_ASSERT_IF(addrRec, addrRec->mResolverType == DNSResolverType::Native); +#endif + + // We did not lookup via TRR - don't fallback to native if the + // network.trr.display_fallback_warning pref is set and either + // 1. we are in TRR first mode and confirmation failed + // 2. the record has trr_disabled and a heuristic skip reason + if (StaticPrefs::network_trr_display_fallback_warning() && + rec->mEffectiveTRRMode != nsIRequest::TRR_ONLY_MODE) { + if ((rec->mEffectiveTRRMode == nsIRequest::TRR_FIRST_MODE && + rec->mTRRSkippedReason == TRRSkippedReason::TRR_NOT_CONFIRMED) || + (rec->mEffectiveTRRMode == nsIRequest::TRR_DISABLED_MODE && + rec->mTRRSkippedReason >= + nsITRRSkipReason::TRR_HEURISTIC_TRIPPED_GOOGLE_SAFESEARCH && + rec->mTRRSkippedReason <= + nsITRRSkipReason::TRR_HEURISTIC_TRIPPED_NRPT)) { + LOG(( + "NameLookup: ResolveHostComplete with status NS_ERROR_UNKNOWN_HOST " + "for: %s effectiveTRRmode: " + "%d SkippedReason: %d", + rec->host.get(), + static_cast<nsIRequest::TRRMode>(rec->mEffectiveTRRMode), + static_cast<int32_t>(rec->mTRRSkippedReason))); + + mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs = + std::move(rec->mCallbacks); + for (nsResolveHostCallback* c = cbs.getFirst(); c; + c = c->removeAndGetNext()) { + c->OnResolveHostComplete(this, rec, NS_ERROR_UNKNOWN_HOST); + } + + return NS_OK; + } + } + + rv = NativeLookup(rec, aLock); + } + + return rv; +} + +nsresult nsHostResolver::ConditionallyRefreshRecord( + nsHostRecord* rec, const nsACString& host, const MutexAutoLock& aLock) { + if ((rec->CheckExpiration(TimeStamp::NowLoRes()) != nsHostRecord::EXP_VALID || + rec->negative) && + !rec->mResolving && rec->RefreshForNegativeResponse()) { + LOG((" Using %s cache entry for host [%s] but starting async renewal.", + rec->negative ? "negative" : "positive", host.BeginReading())); + NameLookup(rec, aLock); + + if (rec->IsAddrRecord() && !rec->negative) { + // negative entries are constantly being refreshed, only + // track positive grace period induced renewals + Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_RENEWAL); + } + } + return NS_OK; +} + +bool nsHostResolver::GetHostToLookup(nsHostRecord** result) { + bool timedOut = false; + TimeDuration timeout; + TimeStamp epoch, now; + + MutexAutoLock lock(mLock); + + timeout = (mNumIdleTasks >= MaxResolverThreadsAnyPriority()) + ? mShortIdleTimeout + : mLongIdleTimeout; + epoch = TimeStamp::Now(); + + while (!mShutdown) { + // remove next record from Q; hand over owning reference. Check high, then + // med, then low + +#define SET_GET_TTL(var, val) (var)->StoreGetTtl(sGetTtlEnabled && (val)) + + RefPtr<nsHostRecord> rec = mQueue.Dequeue(true, lock); + if (rec) { + SET_GET_TTL(rec, false); + rec.forget(result); + return true; + } + + if (mActiveAnyThreadCount < MaxResolverThreadsAnyPriority()) { + rec = mQueue.Dequeue(false, lock); + if (rec) { + MOZ_ASSERT(IsMediumPriority(rec->flags) || IsLowPriority(rec->flags)); + mActiveAnyThreadCount++; + rec->StoreUsingAnyThread(true); + SET_GET_TTL(rec, true); + rec.forget(result); + return true; + } + } + + // Determining timeout is racy, so allow one cycle through checking the + // queues before exiting. + if (timedOut) { + break; + } + + // wait for one or more of the following to occur: + // (1) the pending queue has a host record to process + // (2) the shutdown flag has been set + // (3) the thread has been idle for too long + + mNumIdleTasks++; + mIdleTaskCV.Wait(timeout); + mNumIdleTasks--; + + now = TimeStamp::Now(); + + if (now - epoch >= timeout) { + timedOut = true; + } else { + // It is possible that CondVar::Wait() was interrupted and returned + // early, in which case we will loop back and re-enter it. In that + // case we want to do so with the new timeout reduced to reflect + // time already spent waiting. + timeout -= now - epoch; + epoch = now; + } + } + + // tell thread to exit... + return false; +} + +void nsHostResolver::PrepareRecordExpirationAddrRecord( + AddrHostRecord* rec) const { + // NOTE: rec->addr_info_lock is already held by parent + MOZ_ASSERT(((bool)rec->addr_info) != rec->negative); + mLock.AssertCurrentThreadOwns(); + if (!rec->addr_info) { + rec->SetExpiration(TimeStamp::NowLoRes(), NEGATIVE_RECORD_LIFETIME, 0); + LOG(("Caching host [%s] negative record for %u seconds.\n", rec->host.get(), + NEGATIVE_RECORD_LIFETIME)); + return; + } + + unsigned int lifetime = mDefaultCacheLifetime; + unsigned int grace = mDefaultGracePeriod; + + unsigned int ttl = mDefaultCacheLifetime; + if (sGetTtlEnabled || rec->addr_info->IsTRR()) { + if (rec->addr_info && rec->addr_info->TTL() != AddrInfo::NO_TTL_DATA) { + ttl = rec->addr_info->TTL(); + } + lifetime = ttl; + grace = 0; + } + + rec->SetExpiration(TimeStamp::NowLoRes(), lifetime, grace); + LOG(("Caching host [%s] record for %u seconds (grace %d).", rec->host.get(), + lifetime, grace)); +} + +static bool different_rrset(AddrInfo* rrset1, AddrInfo* rrset2) { + if (!rrset1 || !rrset2) { + return true; + } + + LOG(("different_rrset %s\n", rrset1->Hostname().get())); + + if (rrset1->ResolverType() != rrset2->ResolverType()) { + return true; + } + + if (rrset1->TRRType() != rrset2->TRRType()) { + return true; + } + + if (rrset1->Addresses().Length() != rrset2->Addresses().Length()) { + LOG(("different_rrset true due to length change\n")); + return true; + } + + nsTArray<NetAddr> orderedSet1 = rrset1->Addresses().Clone(); + nsTArray<NetAddr> orderedSet2 = rrset2->Addresses().Clone(); + orderedSet1.Sort(); + orderedSet2.Sort(); + + bool eq = orderedSet1 == orderedSet2; + if (!eq) { + LOG(("different_rrset true due to content change\n")); + } else { + LOG(("different_rrset false\n")); + } + return !eq; +} + +void nsHostResolver::AddToEvictionQ(nsHostRecord* rec, + const MutexAutoLock& aLock) { + mQueue.AddToEvictionQ(rec, mMaxCacheEntries, mRecordDB, aLock); +} + +// After a first lookup attempt with TRR in mode 2, we may: +// - If network.trr.retry_on_recoverable_errors is false, retry with native. +// - If network.trr.retry_on_recoverable_errors is true: +// - Retry with native if the first attempt failed because we got NXDOMAIN, an +// unreachable address (TRR_DISABLED_FLAG), or we skipped TRR because +// Confirmation failed. +// - Trigger a "RetryTRR" Confirmation which will start a fresh +// connection for TRR, and then retry the lookup with TRR. +// - If the second attempt failed, fallback to native if +// network.trr.strict_native_fallback is false. +// Returns true if we retried with either TRR or Native. +bool nsHostResolver::MaybeRetryTRRLookup( + AddrHostRecord* aAddrRec, nsresult aFirstAttemptStatus, + TRRSkippedReason aFirstAttemptSkipReason, nsresult aChannelStatus, + const MutexAutoLock& aLock) { + if (NS_FAILED(aFirstAttemptStatus) && + (aChannelStatus == NS_ERROR_PROXY_UNAUTHORIZED || + aChannelStatus == NS_ERROR_PROXY_AUTHENTICATION_FAILED) && + aAddrRec->mEffectiveTRRMode == nsIRequest::TRR_ONLY_MODE) { + LOG(("MaybeRetryTRRLookup retry because of proxy connect failed")); + TRRService::Get()->DontUseTRRThread(); + return DoRetryTRR(aAddrRec, aLock); + } + + if (NS_SUCCEEDED(aFirstAttemptStatus) || + aAddrRec->mEffectiveTRRMode != nsIRequest::TRR_FIRST_MODE || + aFirstAttemptStatus == NS_ERROR_DEFINITIVE_UNKNOWN_HOST) { + return false; + } + + MOZ_ASSERT(!aAddrRec->mResolving); + if (!StaticPrefs::network_trr_retry_on_recoverable_errors()) { + LOG(("nsHostResolver::MaybeRetryTRRLookup retrying with native")); + return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock)); + } + + if (IsFailedConfirmationOrNoConnectivity(aFirstAttemptSkipReason) || + IsNonRecoverableTRRSkipReason(aFirstAttemptSkipReason) || + IsBlockedTRRRequest(aFirstAttemptSkipReason)) { + LOG( + ("nsHostResolver::MaybeRetryTRRLookup retrying with native in strict " + "mode, skip reason was %d", + static_cast<uint32_t>(aFirstAttemptSkipReason))); + return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock)); + } + + if (aAddrRec->mTrrAttempts > 1) { + if (!StaticPrefs::network_trr_strict_native_fallback()) { + LOG( + ("nsHostResolver::MaybeRetryTRRLookup retry failed. Using " + "native.")); + return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock)); + } + + if (aFirstAttemptSkipReason == TRRSkippedReason::TRR_TIMEOUT && + StaticPrefs::network_trr_strict_native_fallback_allow_timeouts()) { + LOG( + ("nsHostResolver::MaybeRetryTRRLookup retry timed out. Using " + "native.")); + return NS_SUCCEEDED(NativeLookup(aAddrRec, aLock)); + } + LOG(("nsHostResolver::MaybeRetryTRRLookup mTrrAttempts>1, not retrying.")); + return false; + } + + LOG( + ("nsHostResolver::MaybeRetryTRRLookup triggering Confirmation and " + "retrying with TRR, skip reason was %d", + static_cast<uint32_t>(aFirstAttemptSkipReason))); + TRRService::Get()->RetryTRRConfirm(); + + return DoRetryTRR(aAddrRec, aLock); +} + +bool nsHostResolver::DoRetryTRR(AddrHostRecord* aAddrRec, + const mozilla::MutexAutoLock& aLock) { + { + // Clear out the old query + auto trrQuery = aAddrRec->mTRRQuery.Lock(); + trrQuery.ref() = nullptr; + } + + if (NS_SUCCEEDED(TrrLookup(aAddrRec, aLock, nullptr /* pushedTRR */))) { + aAddrRec->NotifyRetryingTrr(); + return true; + } + + return false; +} + +// +// CompleteLookup() checks if the resolving should be redone and if so it +// returns LOOKUP_RESOLVEAGAIN, but only if 'status' is not NS_ERROR_ABORT. +nsHostResolver::LookupStatus nsHostResolver::CompleteLookup( + nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb, + const nsACString& aOriginsuffix, TRRSkippedReason aReason, + mozilla::net::TRR* aTRRRequest) { + MutexAutoLock lock(mLock); + return CompleteLookupLocked(rec, status, aNewRRSet, pb, aOriginsuffix, + aReason, aTRRRequest, lock); +} + +nsHostResolver::LookupStatus nsHostResolver::CompleteLookupLocked( + nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb, + const nsACString& aOriginsuffix, TRRSkippedReason aReason, + mozilla::net::TRR* aTRRRequest, const mozilla::MutexAutoLock& aLock) { + MOZ_ASSERT(rec); + MOZ_ASSERT(rec->pb == pb); + MOZ_ASSERT(rec->IsAddrRecord()); + + RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec); + MOZ_ASSERT(addrRec); + + RefPtr<AddrInfo> newRRSet(aNewRRSet); + MOZ_ASSERT(NS_FAILED(status) || newRRSet->Addresses().Length() > 0); + + DNSResolverType type = + newRRSet ? newRRSet->ResolverType() : DNSResolverType::Native; + + if (NS_FAILED(status)) { + newRRSet = nullptr; + } + + if (addrRec->LoadResolveAgain() && (status != NS_ERROR_ABORT) && + type == DNSResolverType::Native) { + LOG(("nsHostResolver record %p resolve again due to flushcache\n", + addrRec.get())); + addrRec->StoreResolveAgain(false); + return LOOKUP_RESOLVEAGAIN; + } + + MOZ_ASSERT(addrRec->mResolving); + addrRec->mResolving--; + LOG(( + "nsHostResolver::CompleteLookup %s %p %X resolver=%d stillResolving=%d\n", + addrRec->host.get(), aNewRRSet, (unsigned int)status, (int)type, + int(addrRec->mResolving))); + + if (type != DNSResolverType::Native) { + if (NS_FAILED(status) && status != NS_ERROR_UNKNOWN_HOST && + status != NS_ERROR_DEFINITIVE_UNKNOWN_HOST) { + // the errors are not failed resolves, that means + // something else failed, consider this as *TRR not used* + // for actually trying to resolve the host + addrRec->mResolverType = DNSResolverType::Native; + } + + if (NS_FAILED(status)) { + if (aReason != TRRSkippedReason::TRR_UNSET) { + addrRec->RecordReason(aReason); + } else { + // Unknown failed reason. + addrRec->RecordReason(TRRSkippedReason::TRR_FAILED); + } + } else { + addrRec->mTRRSuccess = true; + addrRec->RecordReason(TRRSkippedReason::TRR_OK); + } + + nsresult channelStatus = aTRRRequest->ChannelStatus(); + if (MaybeRetryTRRLookup(addrRec, status, aReason, channelStatus, aLock)) { + MOZ_ASSERT(addrRec->mResolving); + return LOOKUP_OK; + } + + if (!addrRec->mTRRSuccess) { + // no TRR success + newRRSet = nullptr; + } + + if (NS_FAILED(status)) { + // This is the error that consumers expect. + status = NS_ERROR_UNKNOWN_HOST; + } + } else { // native resolve completed + if (addrRec->LoadUsingAnyThread()) { + mActiveAnyThreadCount--; + addrRec->StoreUsingAnyThread(false); + } + + addrRec->mNativeSuccess = static_cast<bool>(newRRSet); + if (addrRec->mNativeSuccess) { + addrRec->mNativeDuration = TimeStamp::Now() - addrRec->mNativeStart; + } + } + + addrRec->OnCompleteLookup(); + + // update record fields. We might have a addrRec->addr_info already if a + // previous lookup result expired and we're reresolving it or we get + // a late second TRR response. + if (!mShutdown) { + MutexAutoLock lock(addrRec->addr_info_lock); + RefPtr<AddrInfo> old_addr_info; + if (different_rrset(addrRec->addr_info, newRRSet)) { + LOG(("nsHostResolver record %p new gencnt\n", addrRec.get())); + old_addr_info = addrRec->addr_info; + addrRec->addr_info = std::move(newRRSet); + addrRec->addr_info_gencnt++; + } else { + if (addrRec->addr_info && newRRSet) { + auto builder = addrRec->addr_info->Build(); + builder.SetTTL(newRRSet->TTL()); + // Update trr timings + builder.SetTrrFetchDuration(newRRSet->GetTrrFetchDuration()); + builder.SetTrrFetchDurationNetworkOnly( + newRRSet->GetTrrFetchDurationNetworkOnly()); + + addrRec->addr_info = builder.Finish(); + } + old_addr_info = std::move(newRRSet); + } + addrRec->negative = !addrRec->addr_info; + PrepareRecordExpirationAddrRecord(addrRec); + } + + if (LOG_ENABLED()) { + MutexAutoLock lock(addrRec->addr_info_lock); + if (addrRec->addr_info) { + for (const auto& elem : addrRec->addr_info->Addresses()) { + char buf[128]; + elem.ToStringBuffer(buf, sizeof(buf)); + LOG(("CompleteLookup: %s has %s\n", addrRec->host.get(), buf)); + } + } else { + LOG(("CompleteLookup: %s has NO address\n", addrRec->host.get())); + } + } + + // get the list of pending callbacks for this lookup, and notify + // them that the lookup is complete. + mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs = + std::move(rec->mCallbacks); + + LOG(("nsHostResolver record %p calling back dns users status:%X\n", + addrRec.get(), int(status))); + + for (nsResolveHostCallback* c = cbs.getFirst(); c; + c = c->removeAndGetNext()) { + c->OnResolveHostComplete(this, rec, status); + } + + OnResolveComplete(rec, aLock); + +#ifdef DNSQUERY_AVAILABLE + // Unless the result is from TRR, resolve again to get TTL + bool hasNativeResult = false; + { + MutexAutoLock lock(addrRec->addr_info_lock); + if (addrRec->addr_info && !addrRec->addr_info->IsTRR()) { + hasNativeResult = true; + } + } + if (hasNativeResult && !mShutdown && !addrRec->LoadGetTtl() && + !rec->mResolving && sGetTtlEnabled) { + LOG(("Issuing second async lookup for TTL for host [%s].", + addrRec->host.get())); + addrRec->flags = + (addrRec->flags & ~nsIDNSService::RESOLVE_PRIORITY_MEDIUM) | + nsIDNSService::RESOLVE_PRIORITY_LOW; + DebugOnly<nsresult> rv = NativeLookup(rec, aLock); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "Could not issue second async lookup for TTL."); + } +#endif + return LOOKUP_OK; +} + +nsHostResolver::LookupStatus nsHostResolver::CompleteLookupByType( + nsHostRecord* rec, nsresult status, + mozilla::net::TypeRecordResultType& aResult, TRRSkippedReason aReason, + uint32_t aTtl, bool pb) { + MutexAutoLock lock(mLock); + return CompleteLookupByTypeLocked(rec, status, aResult, aReason, aTtl, pb, + lock); +} + +nsHostResolver::LookupStatus nsHostResolver::CompleteLookupByTypeLocked( + nsHostRecord* rec, nsresult status, + mozilla::net::TypeRecordResultType& aResult, TRRSkippedReason aReason, + uint32_t aTtl, bool pb, const mozilla::MutexAutoLock& aLock) { + MOZ_ASSERT(rec); + MOZ_ASSERT(rec->pb == pb); + MOZ_ASSERT(!rec->IsAddrRecord()); + + RefPtr<TypeHostRecord> typeRec = do_QueryObject(rec); + MOZ_ASSERT(typeRec); + + MOZ_ASSERT(typeRec->mResolving); + typeRec->mResolving--; + + if (NS_FAILED(status)) { + LOG(("nsHostResolver::CompleteLookupByType record %p [%s] status %x\n", + typeRec.get(), typeRec->host.get(), (unsigned int)status)); + typeRec->SetExpiration( + TimeStamp::NowLoRes(), + StaticPrefs::network_dns_negative_ttl_for_type_record(), 0); + MOZ_ASSERT(aResult.is<TypeRecordEmpty>()); + status = NS_ERROR_UNKNOWN_HOST; + typeRec->negative = true; + if (aReason != TRRSkippedReason::TRR_UNSET) { + typeRec->RecordReason(aReason); + } else { + // Unknown failed reason. + typeRec->RecordReason(TRRSkippedReason::TRR_FAILED); + } + } else { + size_t recordCount = 0; + if (aResult.is<TypeRecordTxt>()) { + recordCount = aResult.as<TypeRecordTxt>().Length(); + } else if (aResult.is<TypeRecordHTTPSSVC>()) { + recordCount = aResult.as<TypeRecordHTTPSSVC>().Length(); + } + LOG( + ("nsHostResolver::CompleteLookupByType record %p [%s], number of " + "records %zu\n", + typeRec.get(), typeRec->host.get(), recordCount)); + MutexAutoLock typeLock(typeRec->mResultsLock); + typeRec->mResults = aResult; + typeRec->SetExpiration(TimeStamp::NowLoRes(), aTtl, mDefaultGracePeriod); + typeRec->negative = false; + typeRec->mTRRSuccess = !rec->LoadNative(); + typeRec->mNativeSuccess = rec->LoadNative(); + MOZ_ASSERT(aReason != TRRSkippedReason::TRR_UNSET); + typeRec->RecordReason(aReason); + } + + mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs = + std::move(typeRec->mCallbacks); + + LOG( + ("nsHostResolver::CompleteLookupByType record %p calling back dns " + "users\n", + typeRec.get())); + + for (nsResolveHostCallback* c = cbs.getFirst(); c; + c = c->removeAndGetNext()) { + c->OnResolveHostComplete(this, rec, status); + } + + OnResolveComplete(rec, aLock); + + return LOOKUP_OK; +} + +void nsHostResolver::OnResolveComplete(nsHostRecord* aRec, + const mozilla::MutexAutoLock& aLock) { + if (!aRec->mResolving && !mShutdown) { + { + auto trrQuery = aRec->mTRRQuery.Lock(); + if (trrQuery.ref()) { + aRec->mTrrDuration = trrQuery.ref()->Duration(); + } + trrQuery.ref() = nullptr; + } + aRec->ResolveComplete(); + + AddToEvictionQ(aRec, aLock); + } +} + +void nsHostResolver::CancelAsyncRequest( + const nsACString& host, const nsACString& aTrrServer, uint16_t aType, + const OriginAttributes& aOriginAttributes, nsIDNSService::DNSFlags flags, + uint16_t af, nsIDNSListener* aListener, nsresult status) + +{ + MutexAutoLock lock(mLock); + + nsAutoCString originSuffix; + aOriginAttributes.CreateSuffix(originSuffix); + + // Lookup the host record associated with host, flags & address family + + nsHostKey key(host, aTrrServer, aType, flags, af, + (aOriginAttributes.mPrivateBrowsingId > 0), originSuffix); + RefPtr<nsHostRecord> rec = mRecordDB.Get(key); + if (!rec) { + return; + } + + for (RefPtr<nsResolveHostCallback> c : rec->mCallbacks) { + if (c->EqualsAsyncListener(aListener)) { + c->remove(); + c->OnResolveHostComplete(this, rec.get(), status); + break; + } + } + + // If there are no more callbacks, remove the hash table entry + if (rec->mCallbacks.isEmpty()) { + mRecordDB.Remove(*static_cast<nsHostKey*>(rec.get())); + // If record is on a Queue, remove it + mQueue.MaybeRemoveFromQ(rec, lock); + } +} + +size_t nsHostResolver::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const { + MutexAutoLock lock(mLock); + + size_t n = mallocSizeOf(this); + + n += mRecordDB.ShallowSizeOfExcludingThis(mallocSizeOf); + for (const auto& entry : mRecordDB.Values()) { + n += entry->SizeOfIncludingThis(mallocSizeOf); + } + + // The following fields aren't measured. + // - mHighQ, mMediumQ, mLowQ, mEvictionQ, because they just point to + // nsHostRecords that also pointed to by entries |mRecordDB|, and + // measured when |mRecordDB| is measured. + + return n; +} + +void nsHostResolver::ThreadFunc() { + LOG(("DNS lookup thread - starting execution.\n")); + +#if defined(RES_RETRY_ON_FAILURE) + nsResState rs; +#endif + RefPtr<nsHostRecord> rec; + RefPtr<AddrInfo> ai; + + do { + if (!rec) { + RefPtr<nsHostRecord> tmpRec; + if (!GetHostToLookup(getter_AddRefs(tmpRec))) { + break; // thread shutdown signal + } + // GetHostToLookup() returns an owning reference + MOZ_ASSERT(tmpRec); + rec.swap(tmpRec); + } + + LOG1(("DNS lookup thread - Calling getaddrinfo for host [%s].\n", + rec->host.get())); + + TimeStamp startTime = TimeStamp::Now(); + bool getTtl = rec->LoadGetTtl(); + TimeDuration inQueue = startTime - rec->mNativeStart; + uint32_t ms = static_cast<uint32_t>(inQueue.ToMilliseconds()); + Telemetry::Accumulate(Telemetry::DNS_NATIVE_QUEUING, ms); + + if (!rec->IsAddrRecord()) { + LOG(("byType on DNS thread")); + TypeRecordResultType result = AsVariant(mozilla::Nothing()); + uint32_t ttl = UINT32_MAX; + nsresult status = ResolveHTTPSRecord(rec->host, rec->flags, result, ttl); + mozilla::glean::networking::dns_native_count + .EnumGet(rec->pb + ? glean::networking::DnsNativeCountLabel::eHttpsPrivate + : glean::networking::DnsNativeCountLabel::eHttpsRegular) + .Add(1); + CompleteLookupByType(rec, status, result, rec->mTRRSkippedReason, ttl, + rec->pb); + rec = nullptr; + continue; + } + + nsresult status = + GetAddrInfo(rec->host, rec->af, rec->flags, getter_AddRefs(ai), getTtl); +#if defined(RES_RETRY_ON_FAILURE) + if (NS_FAILED(status) && rs.Reset()) { + status = GetAddrInfo(rec->host, rec->af, rec->flags, getter_AddRefs(ai), + getTtl); + } +#endif + + mozilla::glean::networking::dns_native_count + .EnumGet(rec->pb ? glean::networking::DnsNativeCountLabel::ePrivate + : glean::networking::DnsNativeCountLabel::eRegular) + .Add(1); + + if (RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec)) { + // obtain lock to check shutdown and manage inter-module telemetry + MutexAutoLock lock(mLock); + + if (!mShutdown) { + TimeDuration elapsed = TimeStamp::Now() - startTime; + if (NS_SUCCEEDED(status)) { + if (!addrRec->addr_info_gencnt) { + // Time for initial lookup. + glean::networking::dns_lookup_time.AccumulateRawDuration(elapsed); + } else if (!getTtl) { + // Time for renewal; categorized by expiration strategy. + glean::networking::dns_renewal_time.AccumulateRawDuration(elapsed); + } else { + // Time to get TTL; categorized by expiration strategy. + glean::networking::dns_renewal_time_for_ttl.AccumulateRawDuration( + elapsed); + } + } else { + glean::networking::dns_failed_lookup_time.AccumulateRawDuration( + elapsed); + } + } + } + + LOG1(("DNS lookup thread - lookup completed for host [%s]: %s.\n", + rec->host.get(), ai ? "success" : "failure: unknown host")); + + if (LOOKUP_RESOLVEAGAIN == + CompleteLookup(rec, status, ai, rec->pb, rec->originSuffix, + rec->mTRRSkippedReason, nullptr)) { + // leave 'rec' assigned and loop to make a renewed host resolve + LOG(("DNS lookup thread - Re-resolving host [%s].\n", rec->host.get())); + } else { + rec = nullptr; + } + } while (true); + + MutexAutoLock lock(mLock); + mActiveTaskCount--; + LOG(("DNS lookup thread - queue empty, task finished.\n")); +} + +void nsHostResolver::SetCacheLimits(uint32_t aMaxCacheEntries, + uint32_t aDefaultCacheEntryLifetime, + uint32_t aDefaultGracePeriod) { + MutexAutoLock lock(mLock); + mMaxCacheEntries = aMaxCacheEntries; + mDefaultCacheLifetime = aDefaultCacheEntryLifetime; + mDefaultGracePeriod = aDefaultGracePeriod; +} + +nsresult nsHostResolver::Create(uint32_t maxCacheEntries, + uint32_t defaultCacheEntryLifetime, + uint32_t defaultGracePeriod, + nsHostResolver** result) { + RefPtr<nsHostResolver> res = new nsHostResolver( + maxCacheEntries, defaultCacheEntryLifetime, defaultGracePeriod); + + nsresult rv = res->Init(); + if (NS_FAILED(rv)) { + return rv; + } + + res.forget(result); + return NS_OK; +} + +void nsHostResolver::GetDNSCacheEntries(nsTArray<DNSCacheEntries>* args) { + MutexAutoLock lock(mLock); + for (const auto& recordEntry : mRecordDB) { + // We don't pay attention to address literals, only resolved domains. + // Also require a host. + nsHostRecord* rec = recordEntry.GetWeak(); + MOZ_ASSERT(rec, "rec should never be null here!"); + + if (!rec) { + continue; + } + + // For now we only show A/AAAA records. + if (!rec->IsAddrRecord()) { + continue; + } + + RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec); + MOZ_ASSERT(addrRec); + if (!addrRec || !addrRec->addr_info) { + continue; + } + + DNSCacheEntries info; + info.hostname = rec->host; + info.family = rec->af; + info.expiration = + (int64_t)(rec->mValidEnd - TimeStamp::NowLoRes()).ToSeconds(); + if (info.expiration <= 0) { + // We only need valid DNS cache entries + continue; + } + + { + MutexAutoLock lock(addrRec->addr_info_lock); + for (const auto& addr : addrRec->addr_info->Addresses()) { + char buf[kIPv6CStrBufSize]; + if (addr.ToStringBuffer(buf, sizeof(buf))) { + info.hostaddr.AppendElement(buf); + } + } + info.TRR = addrRec->addr_info->IsTRR(); + } + + info.originAttributesSuffix = recordEntry.GetKey().originSuffix; + info.flags = nsPrintfCString("%u|0x%x|%u|%d|%s", rec->type, rec->flags, + rec->af, rec->pb, rec->mTrrServer.get()); + + args->AppendElement(std::move(info)); + } +} + +#undef LOG +#undef LOG_ENABLED diff --git a/netwerk/dns/nsHostResolver.h b/netwerk/dns/nsHostResolver.h new file mode 100644 index 0000000000..02e6a343f8 --- /dev/null +++ b/netwerk/dns/nsHostResolver.h @@ -0,0 +1,344 @@ +/* vim:set ts=4 sw=2 sts=2 et cin: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsHostResolver_h__ +#define nsHostResolver_h__ + +#include "nscore.h" +#include "prnetdb.h" +#include "PLDHashTable.h" +#include "mozilla/CondVar.h" +#include "mozilla/DataMutex.h" +#include "nsISupportsImpl.h" +#include "nsIDNSListener.h" +#include "nsTArray.h" +#include "GetAddrInfo.h" +#include "HostRecordQueue.h" +#include "mozilla/net/DNS.h" +#include "mozilla/net/DashboardTypes.h" +#include "mozilla/Atomics.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/UniquePtr.h" +#include "nsHostRecord.h" +#include "nsRefPtrHashtable.h" +#include "nsIThreadPool.h" +#include "mozilla/net/NetworkConnectivityService.h" +#include "mozilla/net/DNSByTypeRecord.h" +#include "mozilla/Maybe.h" +#include "mozilla/StaticPrefs_network.h" + +namespace mozilla { +namespace net { +class TRR; +class TRRQuery; + +static inline uint32_t MaxResolverThreadsAnyPriority() { + return StaticPrefs::network_dns_max_any_priority_threads(); +} + +static inline uint32_t MaxResolverThreadsHighPriority() { + return StaticPrefs::network_dns_max_high_priority_threads(); +} + +static inline uint32_t MaxResolverThreads() { + return MaxResolverThreadsAnyPriority() + MaxResolverThreadsHighPriority(); +} + +} // namespace net +} // namespace mozilla + +#define TRR_DISABLED(x) \ + (((x) == nsIDNSService::MODE_NATIVEONLY) || \ + ((x) == nsIDNSService::MODE_TRROFF)) + +extern mozilla::Atomic<bool, mozilla::Relaxed> gNativeIsLocalhost; + +#define MAX_NON_PRIORITY_REQUESTS 150 + +class AHostResolver { + public: + AHostResolver() = default; + virtual ~AHostResolver() = default; + NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING + + enum LookupStatus { + LOOKUP_OK, + LOOKUP_RESOLVEAGAIN, + }; + + virtual LookupStatus CompleteLookup(nsHostRecord*, nsresult, + mozilla::net::AddrInfo*, bool pb, + const nsACString& aOriginsuffix, + mozilla::net::TRRSkippedReason aReason, + mozilla::net::TRR*) = 0; + virtual LookupStatus CompleteLookupByType( + nsHostRecord*, nsresult, mozilla::net::TypeRecordResultType& aResult, + mozilla::net::TRRSkippedReason aReason, uint32_t aTtl, bool pb) = 0; + virtual nsresult GetHostRecord(const nsACString& host, + const nsACString& aTrrServer, uint16_t type, + nsIDNSService::DNSFlags flags, uint16_t af, + bool pb, const nsCString& originSuffix, + nsHostRecord** result) { + return NS_ERROR_FAILURE; + } + virtual nsresult TrrLookup_unlocked(nsHostRecord*, + mozilla::net::TRR* pushedTRR = nullptr) { + return NS_ERROR_FAILURE; + } + virtual void MaybeRenewHostRecord(nsHostRecord* aRec) {} +}; + +/** + * nsHostResolver - an asynchronous host name resolver. + */ +class nsHostResolver : public nsISupports, public AHostResolver { + using CondVar = mozilla::CondVar; + using Mutex = mozilla::Mutex; + + public: + NS_DECL_THREADSAFE_ISUPPORTS + + /** + * creates an addref'd instance of a nsHostResolver object. + */ + static nsresult Create(uint32_t maxCacheEntries, // zero disables cache + uint32_t defaultCacheEntryLifetime, // seconds + uint32_t defaultGracePeriod, // seconds + nsHostResolver** result); + + /** + * Set (new) cache limits. + */ + void SetCacheLimits(uint32_t maxCacheEntries, // zero disables cache + uint32_t defaultCacheEntryLifetime, // seconds + uint32_t defaultGracePeriod); // seconds + + /** + * puts the resolver in the shutdown state, which will cause any pending + * callbacks to be detached. any future calls to ResolveHost will fail. + */ + void Shutdown(); + + /** + * resolve the given hostname and originAttributes asynchronously. the caller + * can synthesize a synchronous host lookup using a lock and a cvar. as noted + * above the callback will occur re-entrantly from an unspecified thread. the + * host lookup cannot be canceled (cancelation can be layered above this by + * having the callback implementation return without doing anything). + */ + nsresult ResolveHost(const nsACString& aHost, const nsACString& trrServer, + int32_t aPort, uint16_t type, + const mozilla::OriginAttributes& aOriginAttributes, + nsIDNSService::DNSFlags flags, uint16_t af, + nsResolveHostCallback* callback); + + nsHostRecord* InitRecord(const nsHostKey& key); + mozilla::net::NetworkConnectivityService* GetNCS() { + return mNCS; + } // This is actually a singleton + + /** + * return a resolved hard coded loopback dns record for the specified key + */ + already_AddRefed<nsHostRecord> InitLoopbackRecord(const nsHostKey& key, + nsresult* aRv); + + /** + * removes the specified callback from the nsHostRecord for the given + * hostname, originAttributes, flags, and address family. these parameters + * should correspond to the parameters passed to ResolveHost. this function + * executes the callback if the callback is still pending with the given + * status. + */ + void DetachCallback(const nsACString& hostname, const nsACString& trrServer, + uint16_t type, + const mozilla::OriginAttributes& aOriginAttributes, + nsIDNSService::DNSFlags flags, uint16_t af, + nsResolveHostCallback* callback, nsresult status); + + /** + * Cancels an async request associated with the hostname, originAttributes, + * flags, address family and listener. Cancels first callback found which + * matches these criteria. These parameters should correspond to the + * parameters passed to ResolveHost. If this is the last callback associated + * with the host record, it is removed from any request queues it might be on. + */ + void CancelAsyncRequest(const nsACString& host, const nsACString& trrServer, + uint16_t type, + const mozilla::OriginAttributes& aOriginAttributes, + nsIDNSService::DNSFlags flags, uint16_t af, + nsIDNSListener* aListener, nsresult status); + /** + * values for the flags parameter passed to ResolveHost and DetachCallback + * that may be bitwise OR'd together. + * + * NOTE: in this implementation, these flags correspond exactly in value + * to the flags defined on nsIDNSService. + */ + enum { + RES_BYPASS_CACHE = nsIDNSService::RESOLVE_BYPASS_CACHE, + RES_CANON_NAME = nsIDNSService::RESOLVE_CANONICAL_NAME, + RES_PRIORITY_MEDIUM = nsHostRecord::DNS_PRIORITY_MEDIUM, + RES_PRIORITY_LOW = nsHostRecord::DNS_PRIORITY_LOW, + RES_SPECULATE = nsIDNSService::RESOLVE_SPECULATE, + // RES_DISABLE_IPV6 = nsIDNSService::RESOLVE_DISABLE_IPV6, // Not used + RES_OFFLINE = nsIDNSService::RESOLVE_OFFLINE, + // RES_DISABLE_IPv4 = nsIDNSService::RESOLVE_DISABLE_IPV4, // Not Used + RES_ALLOW_NAME_COLLISION = nsIDNSService::RESOLVE_ALLOW_NAME_COLLISION, + RES_DISABLE_TRR = nsIDNSService::RESOLVE_DISABLE_TRR, + RES_REFRESH_CACHE = nsIDNSService::RESOLVE_REFRESH_CACHE, + RES_IP_HINT = nsIDNSService::RESOLVE_IP_HINT + }; + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; + + /** + * Flush the DNS cache. + */ + void FlushCache(bool aTrrToo); + + LookupStatus CompleteLookup(nsHostRecord*, nsresult, mozilla::net::AddrInfo*, + bool pb, const nsACString& aOriginsuffix, + mozilla::net::TRRSkippedReason aReason, + mozilla::net::TRR* aTRRRequest) override; + LookupStatus CompleteLookupByType(nsHostRecord*, nsresult, + mozilla::net::TypeRecordResultType& aResult, + mozilla::net::TRRSkippedReason aReason, + uint32_t aTtl, bool pb) override; + nsresult GetHostRecord(const nsACString& host, const nsACString& trrServer, + uint16_t type, nsIDNSService::DNSFlags flags, + uint16_t af, bool pb, const nsCString& originSuffix, + nsHostRecord** result) override; + nsresult TrrLookup_unlocked(nsHostRecord*, + mozilla::net::TRR* pushedTRR = nullptr) override; + static nsIDNSService::ResolverMode Mode(); + + virtual void MaybeRenewHostRecord(nsHostRecord* aRec) override; + + // Records true if the TRR service is enabled for the record's effective + // TRR mode. Also records the TRRSkipReason when the TRR service is not + // available/enabled. + bool TRRServiceEnabledForRecord(nsHostRecord* aRec) MOZ_REQUIRES(mLock); + + private: + explicit nsHostResolver(uint32_t maxCacheEntries, + uint32_t defaultCacheEntryLifetime, + uint32_t defaultGracePeriod); + virtual ~nsHostResolver(); + + bool DoRetryTRR(AddrHostRecord* aAddrRec, + const mozilla::MutexAutoLock& aLock); + bool MaybeRetryTRRLookup( + AddrHostRecord* aAddrRec, nsresult aFirstAttemptStatus, + mozilla::net::TRRSkippedReason aFirstAttemptSkipReason, + nsresult aChannelStatus, const mozilla::MutexAutoLock& aLock); + + LookupStatus CompleteLookupLocked(nsHostRecord*, nsresult, + mozilla::net::AddrInfo*, bool pb, + const nsACString& aOriginsuffix, + mozilla::net::TRRSkippedReason aReason, + mozilla::net::TRR* aTRRRequest, + const mozilla::MutexAutoLock& aLock) + MOZ_REQUIRES(mLock); + LookupStatus CompleteLookupByTypeLocked( + nsHostRecord*, nsresult, mozilla::net::TypeRecordResultType& aResult, + mozilla::net::TRRSkippedReason aReason, uint32_t aTtl, bool pb, + const mozilla::MutexAutoLock& aLock) MOZ_REQUIRES(mLock); + nsresult Init(); + static void ComputeEffectiveTRRMode(nsHostRecord* aRec); + nsresult NativeLookup(nsHostRecord* aRec, + const mozilla::MutexAutoLock& aLock); + nsresult TrrLookup(nsHostRecord*, const mozilla::MutexAutoLock& aLock, + mozilla::net::TRR* pushedTRR = nullptr); + + // Kick-off a name resolve operation, using native resolver and/or TRR + nsresult NameLookup(nsHostRecord* aRec, const mozilla::MutexAutoLock& aLock); + bool GetHostToLookup(nsHostRecord** result); + void MaybeRenewHostRecordLocked(nsHostRecord* aRec, + const mozilla::MutexAutoLock& aLock) + MOZ_REQUIRES(mLock); + + // Cancels host records in the pending queue and also + // calls CompleteLookup with the NS_ERROR_ABORT result code. + void ClearPendingQueue(mozilla::LinkedList<RefPtr<nsHostRecord>>& aPendingQ); + nsresult ConditionallyCreateThread(nsHostRecord* rec) MOZ_REQUIRES(mLock); + + /** + * Starts a new lookup in the background for entries that are in the grace + * period with a failed connect or all cached entries are negative. + */ + nsresult ConditionallyRefreshRecord(nsHostRecord* rec, const nsACString& host, + const mozilla::MutexAutoLock& aLock) + MOZ_REQUIRES(mLock); + + void OnResolveComplete(nsHostRecord* aRec, + const mozilla::MutexAutoLock& aLock) + MOZ_REQUIRES(mLock); + + void AddToEvictionQ(nsHostRecord* rec, const mozilla::MutexAutoLock& aLock) + MOZ_REQUIRES(mLock); + + void ThreadFunc(); + + // Resolve the host from the DNS cache. + already_AddRefed<nsHostRecord> FromCache(nsHostRecord* aRec, + const nsACString& aHost, + uint16_t aType, nsresult& aStatus, + const mozilla::MutexAutoLock& aLock) + MOZ_REQUIRES(mLock); + // Called when the host name is an IP address and has been passed. + already_AddRefed<nsHostRecord> FromCachedIPLiteral(nsHostRecord* aRec); + // Like the above function, but the host name is not parsed to NetAddr yet. + already_AddRefed<nsHostRecord> FromIPLiteral( + AddrHostRecord* aAddrRec, const mozilla::net::NetAddr& aAddr); + // Called to check if we have an AF_UNSPEC entry in the cache. + already_AddRefed<nsHostRecord> FromUnspecEntry( + nsHostRecord* aRec, const nsACString& aHost, const nsACString& aTrrServer, + const nsACString& aOriginSuffix, uint16_t aType, + nsIDNSService::DNSFlags aFlags, uint16_t af, bool aPb, nsresult& aStatus) + MOZ_REQUIRES(mLock); + + enum { + METHOD_HIT = 1, + METHOD_RENEWAL = 2, + METHOD_NEGATIVE_HIT = 3, + METHOD_LITERAL = 4, + METHOD_OVERFLOW = 5, + METHOD_NETWORK_FIRST = 6, + METHOD_NETWORK_SHARED = 7 + }; + + uint32_t mMaxCacheEntries = 0; + uint32_t mDefaultCacheLifetime = 0; // granularity seconds + uint32_t mDefaultGracePeriod = 0; // granularity seconds + // mutable so SizeOfIncludingThis can be const + mutable Mutex mLock{"nsHostResolver.mLock"}; + CondVar mIdleTaskCV; + nsRefPtrHashtable<nsGenericHashKey<nsHostKey>, nsHostRecord> mRecordDB + MOZ_GUARDED_BY(mLock); + PRTime mCreationTime; + mozilla::TimeDuration mLongIdleTimeout; + mozilla::TimeDuration mShortIdleTimeout; + + RefPtr<nsIThreadPool> mResolverThreads; + RefPtr<mozilla::net::NetworkConnectivityService> + mNCS; // reference to a singleton + mozilla::net::HostRecordQueue mQueue MOZ_GUARDED_BY(mLock); + mozilla::Atomic<bool> mShutdown MOZ_GUARDED_BY(mLock){true}; + mozilla::Atomic<uint32_t> mNumIdleTasks MOZ_GUARDED_BY(mLock){0}; + mozilla::Atomic<uint32_t> mActiveTaskCount MOZ_GUARDED_BY(mLock){0}; + mozilla::Atomic<uint32_t> mActiveAnyThreadCount MOZ_GUARDED_BY(mLock){0}; + + // Set the expiration time stamps appropriately. + void PrepareRecordExpirationAddrRecord(AddrHostRecord* rec) const; + + public: + /* + * Called by the networking dashboard via the DnsService2 + */ + void GetDNSCacheEntries(nsTArray<mozilla::net::DNSCacheEntries>*); +}; + +#endif // nsHostResolver_h__ diff --git a/netwerk/dns/nsIDNKitInterface.h b/netwerk/dns/nsIDNKitInterface.h new file mode 100644 index 0000000000..3e0f0d60ac --- /dev/null +++ b/netwerk/dns/nsIDNKitInterface.h @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2000-2002 Japan Network Information Center. All rights reserved. + * + * By using this file, you agree to the terms and conditions set forth bellow. + * + * LICENSE TERMS AND CONDITIONS + * + * The following License Terms and Conditions apply, unless a different + * license is obtained from Japan Network Information Center ("JPNIC"), + * a Japanese association, Kokusai-Kougyou-Kanda Bldg 6F, 2-3-4 Uchi-Kanda, + * Chiyoda-ku, Tokyo 101-0047, Japan. + + * 1. Use, Modification and Redistribution (including distribution of any + * modified or derived work) in source and/or binary forms is permitted + * under this License Terms and Conditions. + * + * 2. Redistribution of source code must retain the copyright notices as they + * appear in each source code file, this License Terms and Conditions. + * + * 3. Redistribution in binary form must reproduce the Copyright Notice, + * this License Terms and Conditions, in the documentation and/or other + * materials provided with the distribution. For the purposes of binary + * distribution the "Copyright Notice" refers to the following language: + * "Copyright (c) 2000-2002 Japan Network Information Center. All rights reserved." + * + * 4. The name of JPNIC may not be used to endorse or promote products + * derived from this Software without specific prior written approval of + * JPNIC. + * + * 5. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY JPNIC + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JPNIC BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef nsIDNKitWrapper_h__ +#define nsIDNKitWrapper_h__ + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * libidnkit result code. + */ +typedef enum { + idn_success, + idn_notfound, + idn_invalid_encoding, + idn_invalid_syntax, + idn_invalid_name, + idn_invalid_message, + idn_invalid_action, + idn_invalid_codepoint, + idn_invalid_length, + idn_buffer_overflow, + idn_noentry, + idn_nomemory, + idn_nofile, + idn_nomapping, + idn_context_required, + idn_prohibited, + idn_failure /* !!This must be the last one!! */ +} idn_result_t; + +/* + * BIDI type codes. + */ +typedef enum { + idn_biditype_r_al, + idn_biditype_l, + idn_biditype_others +} idn_biditype_t; + +/* + * A Handle for nameprep operations. + */ +typedef struct idn_nameprep *idn_nameprep_t; + + +/* + * The latest version of nameprep. + */ +#define IDN_NAMEPREP_CURRENT "nameprep-11" + +#undef assert +#define assert(a) +#define TRACE(a) + + +/* race.c */ +idn_result_t race_decode_decompress(const char *from, + uint16_t *buf, + size_t buflen); +idn_result_t race_compress_encode(const uint16_t *p, + int compress_mode, + char *to, size_t tolen); +int get_compress_mode(uint16_t *p); + + +/* nameprep.c */ + +/* + * Create a handle for nameprep operations. + * The handle is stored in '*handlep', which is used other functions + * in this module. + * The version of the NAMEPREP specification can be specified with + * 'version' parameter. If 'version' is nullptr, the latest version + * is used. + * + * Returns: + * idn_success -- ok. + * idn_notfound -- specified version not found. + */ +idn_result_t +idn_nameprep_create(const char *version, idn_nameprep_t *handlep); + +/* + * Close a handle, which was created by 'idn_nameprep_create'. + */ +void +idn_nameprep_destroy(idn_nameprep_t handle); + +/* + * Perform character mapping on an UCS4 string specified by 'from', and + * store the result into 'to', whose length is specified by 'tolen'. + * + * Returns: + * idn_success -- ok. + * idn_buffer_overflow -- result buffer is too small. + */ +idn_result_t +idn_nameprep_map(idn_nameprep_t handle, const uint32_t *from, + uint32_t *to, size_t tolen); + +/* + * Check if an UCS4 string 'str' contains any prohibited characters specified + * by the draft. If found, the pointer to the first such character is stored + * into '*found'. Otherwise '*found' will be nullptr. + * + * Returns: + * idn_success -- check has been done properly. (But this + * does not mean that no prohibited character + * was found. Check '*found' to see the + * result.) + */ +idn_result_t +idn_nameprep_isprohibited(idn_nameprep_t handle, const uint32_t *str, + const uint32_t **found); + +/* + * Check if an UCS4 string 'str' contains any unassigned characters specified + * by the draft. If found, the pointer to the first such character is stored + * into '*found'. Otherwise '*found' will be nullptr. + * + * Returns: + * idn_success -- check has been done properly. (But this + * does not mean that no unassinged character + * was found. Check '*found' to see the + * result.) + */ +idn_result_t +idn_nameprep_isunassigned(idn_nameprep_t handle, const uint32_t *str, + const uint32_t **found); + +/* + * Check if an UCS4 string 'str' is valid string specified by ``bidi check'' + * of the draft. If it is not valid, the pointer to the first invalid + * character is stored into '*found'. Otherwise '*found' will be nullptr. + * + * Returns: + * idn_success -- check has been done properly. (But this + * does not mean that the string was valid. + * Check '*found' to see the result.) + */ +idn_result_t +idn_nameprep_isvalidbidi(idn_nameprep_t handle, const uint32_t *str, + const uint32_t **found); + + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* nsIDNKitWrapper_h__ */ diff --git a/netwerk/dns/nsIDNSAdditionalInfo.idl b/netwerk/dns/nsIDNSAdditionalInfo.idl new file mode 100644 index 0000000000..4f266d7937 --- /dev/null +++ b/netwerk/dns/nsIDNSAdditionalInfo.idl @@ -0,0 +1,12 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +[scriptable, builtinclass, uuid(74db2955-6298-4d82-a3b9-7f9e8ba9e854)] +interface nsIDNSAdditionalInfo : nsISupports +{ + readonly attribute int32_t port; + readonly attribute ACString resolverURL; +}; diff --git a/netwerk/dns/nsIDNSByTypeRecord.idl b/netwerk/dns/nsIDNSByTypeRecord.idl new file mode 100644 index 0000000000..1d11325af8 --- /dev/null +++ b/netwerk/dns/nsIDNSByTypeRecord.idl @@ -0,0 +1,133 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsIDNSRecord.idl" + +%{ C++ + +#include "mozilla/Maybe.h" + +#include "nsTArrayForwardDeclare.h" +#include "nsHttp.h" +#include "nsStringFwd.h" + +namespace mozilla { + +template <typename... Ts> class Variant; +struct Nothing; + +namespace net { + struct SVCB; + using TypeRecordResultType = + Variant<Nothing, CopyableTArray<nsCString>, CopyableTArray<SVCB>>; +} +} + +%} + +[ref] native CStringArrayRef(CopyableTArray<nsCString>); +native TypeResult(mozilla::net::TypeRecordResultType); + +native MaybePort(mozilla::Maybe<uint16_t>); +native MaybeAlpnTuple(mozilla::Maybe<std::tuple<nsCString, mozilla::net::SupportedAlpnRank>>); + +[scriptable, uuid(5d13241b-9d46-448a-90d8-77c418491026)] +interface nsIDNSByTypeRecord : nsIDNSRecord +{ + /** + * Returns DNS request type that was made for this request. + */ + readonly attribute unsigned long type; + + [noscript] readonly attribute TypeResult results; +}; + +[scriptable, uuid(2a71750d-cb21-45f1-9e1c-666d18dd7645)] +interface nsIDNSTXTRecord : nsISupports +{ + CStringArrayRef getRecords(); + + /* + * Return concatenated strings. + */ + ACString getRecordsAsOneString(); +}; + +[scriptable, uuid(2979ceaa-9c7e-49de-84b8-ea81c16aebf1)] +interface nsISVCParam : nsISupports { + readonly attribute uint16_t type; +}; + +[scriptable, uuid(0dc58309-4d67-4fc4-a4e3-38dbde9d9f4c)] +interface nsISVCParamAlpn : nsISupports { + readonly attribute Array<ACString> alpn; +}; + +[scriptable, uuid(b3ed89c3-2ae6-4c92-8176-b76bc2437fcb)] +interface nsISVCParamNoDefaultAlpn : nsISupports { +}; + +[scriptable, uuid(a37c7bcb-bfcd-4ab4-b826-cc583859ba73)] +interface nsISVCParamPort : nsISupports { + readonly attribute uint16_t port; +}; + +[scriptable, uuid(d3163d2f-0bbe-47d4-bcac-db3fb1433b39)] +interface nsISVCParamIPv4Hint : nsISupports { + readonly attribute Array<nsINetAddr> ipv4Hint; +}; + +[scriptable, uuid(1f31e41d-b6d8-4796-b12a-82ef8d2b0e43)] +interface nsISVCParamEchConfig : nsISupports { + readonly attribute ACString echconfig; +}; + +[scriptable, uuid(5100bce4-9d3b-42e1-a3c9-0f386bbc9dad)] +interface nsISVCParamIPv6Hint : nsISupports { + readonly attribute Array<nsINetAddr> ipv6Hint; +}; + +[scriptable, uuid(bdcef040-452e-11eb-b378-0242ac130002)] +interface nsISVCParamODoHConfig : nsISupports { + readonly attribute ACString ODoHConfig; +}; + +[scriptable, builtinclass, uuid(a4da5645-2160-4439-bd11-540a2d26c989)] +interface nsISVCBRecord : nsISupports { + readonly attribute uint16_t priority; + readonly attribute ACString name; + [noscript, nostdcall, notxpcom] readonly attribute MaybePort port; + [noscript, nostdcall, notxpcom] readonly attribute MaybeAlpnTuple alpn; + readonly attribute ACString selectedAlpn; + readonly attribute ACString echConfig; + readonly attribute ACString ODoHConfig; + readonly attribute bool hasIPHintAddress; + readonly attribute Array<nsISVCParam> values; +}; + +[scriptable, uuid(5b649e95-e0d3-422b-99a6-79d70a041387)] +interface nsIDNSHTTPSSVCRecord : nsISupports +{ + readonly attribute Array<nsISVCBRecord> records; + nsISVCBRecord GetServiceModeRecord(in boolean aNoHttp2, in boolean aNoHttp3); + /** + * Returns true if one of SVCB records has IPv4 or IPv6 hint addresses. + */ + readonly attribute boolean hasIPAddresses; + + /** + * Returns true when all names of SVCB records are in exclusion list. + */ + readonly attribute boolean allRecordsExcluded; + + /** + * Returns the ttl of this record. + */ + readonly attribute uint32_t ttl; + + Array<nsISVCBRecord> GetAllRecordsWithEchConfig(in boolean aNoHttp2, + in boolean aNoHttp3, + out boolean aAllRecordsHaveEchConfig, + out boolean aAllRecordsInH3ExcludedList); +}; diff --git a/netwerk/dns/nsIDNSListener.idl b/netwerk/dns/nsIDNSListener.idl new file mode 100644 index 0000000000..d925294733 --- /dev/null +++ b/netwerk/dns/nsIDNSListener.idl @@ -0,0 +1,34 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsICancelable; +interface nsIDNSRecord; +interface nsIDNSByTypeRecord; + +/** + * nsIDNSListener + */ +[scriptable, uuid(27d49bfe-280c-49e0-bbaa-f6200c232c3d)] +interface nsIDNSListener : nsISupports +{ + /** + * called when an asynchronous host lookup completes. + * + * @param aRequest + * the value returned from asyncResolve. + * @param aRecord + * the DNS record corresponding to the hostname that was resolved. + * this parameter is null if there was an error. + * depending on the type parameter passed to asyncResolve() the + * caller should QueryInterface to either nsIDNSAddrRecord or + * nsIDNSByTypeRecord. + * @param aStatus + * if the lookup failed, this parameter gives the reason. + */ + void onLookupComplete(in nsICancelable aRequest, + in nsIDNSRecord aRecord, + in nsresult aStatus); +}; diff --git a/netwerk/dns/nsIDNSRecord.idl b/netwerk/dns/nsIDNSRecord.idl new file mode 100644 index 0000000000..82682bd900 --- /dev/null +++ b/netwerk/dns/nsIDNSRecord.idl @@ -0,0 +1,154 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" +#include "nsIRequest.idl" +#include "nsITRRSkipReason.idl" + +%{ C++ +namespace mozilla { +namespace net { +union NetAddr; +} +} +#include "nsTArrayForwardDeclare.h" +%} +native NetAddr(mozilla::net::NetAddr); +[ref] native nsNetAddrTArrayRef(nsTArray<mozilla::net::NetAddr>); +interface nsINetAddr; + +/** + * nsIDNSRecord + * + * this interface represents the result of a DNS lookup. since a DNS + * query may return more than one resolved IP address, the record acts + * like an enumerator, allowing the caller to easily step through the + * list of IP addresses. + */ +[scriptable, uuid(f92228ae-c417-4188-a604-0830a95e7eb9)] +interface nsIDNSRecord : nsISupports +{ +}; + +[scriptable, uuid(cb260e20-943f-4309-953b-78c90d3a7638)] +interface nsIDNSAddrRecord : nsIDNSRecord +{ + /** + * @return the canonical hostname for this record. this value is empty if + * the record was not fetched with the RESOLVE_CANONICAL_NAME flag. + * + * e.g., www.mozilla.org --> rheet.mozilla.org + * + * That the result, if IDN will be returned as punycode. + * e.g., élève.w3c-test.org --> xn--lve-6lad.w3c-test.org + */ + readonly attribute ACString canonicalName; + + /** + * this function copies the value of the next IP address into the + * given NetAddr struct and increments the internal address iterator. + * + * @param aPort + * A port number to initialize the NetAddr with. + * + * @throws NS_ERROR_NOT_AVAILABLE if there is not another IP address in + * the record. + */ + [noscript] NetAddr getNextAddr(in uint16_t aPort); + + /** + * this function copies the value of all working members of the RR + * set into the output array. + * + * @param aAddressArray + * The result set + */ + [noscript] void getAddresses(out nsNetAddrTArrayRef aAddressArray); + + /** + * this function returns the value of the next IP address as a + * scriptable address and increments the internal address iterator. + * + * @param aPort + * A port number to initialize the nsINetAddr with. + * + * @throws NS_ERROR_NOT_AVAILABLE if there is not another IP address in + * the record. + */ + nsINetAddr getScriptableNextAddr(in uint16_t aPort); + + /** + * this function returns the value of the next IP address as a + * string and increments the internal address iterator. + * + * @throws NS_ERROR_NOT_AVAILABLE if there is not another IP address in + * the record. + */ + ACString getNextAddrAsString(); + + /** + * this function returns true if there is another address in the record. + */ + boolean hasMore(); + + /** + * this function resets the internal address iterator to the first + * address in the record. + */ + void rewind(); + + /** + * This function indicates that the last address obtained via getNextAddr*() + * was not usuable and should be skipped in future uses of this + * record if other addresses are available. + * + * @param aPort is the port number associated with the failure, if any. + * It may be zero if not applicable. + */ + void reportUnusable(in uint16_t aPort); + + /** + * Record retreived with TRR. + */ + bool IsTRR(); + + /** + * Record is resolved in socket process. + */ + bool resolvedInSocketProcess(); + + /** + * This attribute is only set if TRR is used and it measures time between + * asyncOpen on a channel and the time parsing of response if done. + * Thee time is measured in milliseconds. + */ + readonly attribute double trrFetchDuration; + + /** + * This attribute is only set if TRR is used and it measures time between + * sending a request and the time response is received from the network. + * This time is similat to the time above, but exludes a time needed to + * make a connection and a time neededto parse results (this also does not + * include delays that may be introduce because parsing is perform on the main + * thread). + * Thee time is measured in milliseconds. + */ + readonly attribute double trrFetchDurationNetworkOnly; + + /** + * The TRR mode this record is used. + */ + readonly attribute nsIRequest_TRRMode effectiveTRRMode; + + /** + * If the DNS request didn't use TRR, this value + * contains the reason why that was skipped. + */ + readonly attribute nsITRRSkipReason_value trrSkipReason; + + /** + * Returns the ttl of this record. + */ + readonly attribute uint32_t ttl; +}; diff --git a/netwerk/dns/nsIDNSService.idl b/netwerk/dns/nsIDNSService.idl new file mode 100644 index 0000000000..c1aecccd8c --- /dev/null +++ b/netwerk/dns/nsIDNSService.idl @@ -0,0 +1,383 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" +#include "nsIRequest.idl" +#include "nsITRRSkipReason.idl" + +%{ C++ +#include "mozilla/BasePrincipal.h" +#include "mozilla/TypedEnumBits.h" +%} + +interface nsICancelable; +interface nsIEventTarget; +interface nsIDNSRecord; +interface nsIDNSListener; +interface nsIDNSAdditionalInfo; + +%{C++ +#include "nsTArrayForwardDeclare.h" +namespace mozilla { namespace net { + struct DNSCacheEntries; +} } +%} + +[ptr] native EntriesArray(nsTArray<mozilla::net::DNSCacheEntries>); +[ref] native OriginAttributes(const mozilla::OriginAttributes); + +/** + * nsIDNSService + */ +[scriptable, builtinclass, uuid(de5642c6-61fc-4fcf-9a47-03226b0d4e21)] +interface nsIDNSService : nsISupports +{ + /** + * These are the dns request types that are currently supported. + * RESOLVE_TYPE_DEFAULT is standard A/AAAA lookup + */ + cenum ResolveType : 16 { + RESOLVE_TYPE_DEFAULT = 0, + RESOLVE_TYPE_TXT = 16, + RESOLVE_TYPE_HTTPSSVC = 65, + }; + + cenum ResolverMode : 32 { // 32 bits to allow this to be stored in an Atomic + MODE_NATIVEONLY = 0, // TRR OFF (by default) + MODE_RESERVED1 = 1, // Reserved value. Used to be parallel resolve. + MODE_TRRFIRST = 2, // fallback to native on TRR failure + MODE_TRRONLY = 3, // don't even fallback + MODE_RESERVED4 = 4, // Reserved value. Used to be race TRR with native. + MODE_TRROFF = 5 // identical to MODE_NATIVEONLY but explicitly selected + }; + + cenum DNSFlags : 32 { + RESOLVE_DEFAULT_FLAGS = 0, + // if set, this flag suppresses the internal DNS lookup cache. + RESOLVE_BYPASS_CACHE = (1 << 0), + // if set, the canonical name of the specified host will be queried. + RESOLVE_CANONICAL_NAME = (1 << 1), + // If PRIORITY flags are set, the query is given lower priority. + // Medium takes precedence if both MEDIUM and LOW are used. + RESOLVE_PRIORITY_MEDIUM = (1 << 2), + RESOLVE_PRIORITY_LOW = (1 << 3), + // if set, indicates request is speculative. Speculative requests + // return errors if prefetching is disabled by configuration. + RESOLVE_SPECULATE = (1 << 4), + // If set, only IPv4 addresses will be returned from resolve/asyncResolve. + RESOLVE_DISABLE_IPV6 = (1 << 5), + // If set, only literals and cached entries will be returned from resolve/asyncResolve. + RESOLVE_OFFLINE = (1 << 6), + // If set, only IPv6 addresses will be returned from resolve/asyncResolve. + RESOLVE_DISABLE_IPV4 = (1 << 7), + // If set, allow name collision results (127.0.53.53) which are normally filtered. + RESOLVE_ALLOW_NAME_COLLISION = (1 << 8), + // If set, do not use TRR for resolving the host name. + RESOLVE_DISABLE_TRR = (1 << 9), + // if set (together with RESOLVE_BYPASS_CACHE), invalidate the DNS + // existing cache entry first (if existing) then make a new resolve. + RESOLVE_REFRESH_CACHE = (1 << 10), + // These two bits encode the TRR mode of the request. + // Use the static helper methods GetFlagsFromTRRMode and + // GetTRRModeFromFlags to convert between the TRR mode and flags. + RESOLVE_TRR_MODE_MASK = (1 << 11) | (1 << 12), + RESOLVE_TRR_DISABLED_MODE = (1 << 11), + // Force resolution even when SOCKS proxy with DNS forwarding is configured. + // Only to be used for the proxy host resolution. + RESOLVE_IGNORE_SOCKS_DNS = (1 << 13), + // If set, only cached IP hint addresses will be returned from resolve/asyncResolve. + RESOLVE_IP_HINT = (1 << 14), + // If set, the DNS service will pass a DNS record to + // OnLookupComplete even when there was a resolution error. + RESOLVE_WANT_RECORD_ON_ERROR = (1 << 16), + + // Bitflag containing all possible flags. + ALL_DNSFLAGS_BITS = ((1 << 17) - 1), + }; + + cenum ConfirmationState : 8 { + CONFIRM_OFF = 0, + CONFIRM_TRYING_OK = 1, + CONFIRM_OK = 2, + CONFIRM_FAILED = 3, + CONFIRM_TRYING_FAILED = 4, + CONFIRM_DISABLED = 5, + }; + + /** + * kicks off an asynchronous host lookup. + * + * @param aHostName + * the hostname or IP-address-literal to resolve. + * @param aType + * one of RESOLVE_TYPE_*. + * @param aFlags + * a bitwise OR of the RESOLVE_ prefixed constants defined below. + * @param aInfo + * a AdditionalInfo object that holds information about: + * - the resolver to be used such as TRR URL + * - the port number that could be used to construct a QNAME + * for HTTPS RR + * If null we use the default configuration. + * @param aListener + * the listener to be notified when the result is available. + * @param aListenerTarget + * optional parameter (may be null). if non-null, this parameter + * specifies the nsIEventTarget of the thread on which the + * listener's onLookupComplete should be called. however, if this + * parameter is null, then onLookupComplete will be called on an + * unspecified thread (possibly recursively). + * @param aOriginAttributes + * the originAttribute for this resolving, the DNS cache will be + * separated according to this originAttributes. This attribute is + * optional to avoid breaking add-ons. + * + * @return An object that can be used to cancel the host lookup. + */ + [implicit_jscontext, optional_argc] + nsICancelable asyncResolve(in AUTF8String aHostName, + in nsIDNSService_ResolveType aType, + in nsIDNSService_DNSFlags aFlags, + in nsIDNSAdditionalInfo aInfo, + in nsIDNSListener aListener, + in nsIEventTarget aListenerTarget, + [optional] in jsval aOriginAttributes); + + [notxpcom] + nsresult asyncResolveNative(in AUTF8String aHostName, + in nsIDNSService_ResolveType aType, + in nsIDNSService_DNSFlags aFlags, + in nsIDNSAdditionalInfo aInfo, + in nsIDNSListener aListener, + in nsIEventTarget aListenerTarget, + in OriginAttributes aOriginAttributes, + out nsICancelable aResult); + + /** + * Returns a new nsIDNSAdditionalInfo object containing the URL we pass to it. + */ + nsIDNSAdditionalInfo newAdditionalInfo(in AUTF8String aTrrURL, + in int32_t aPort); + + /** + * Attempts to cancel a previously requested async DNS lookup + * + * @param aHostName + * the hostname or IP-address-literal to resolve. + * @param aType + * one of RESOLVE_TYPE_*. + * @param aFlags + * a bitwise OR of the RESOLVE_ prefixed constants defined below. + * @param aInfo + * a AdditionalInfo object that holds information about: + * - the resolver to be used such as TRR URL + * - the port number that could be used to construct a QNAME + * for HTTPS RR + * If null we use the default configuration. + * @param aListener + * the original listener which was to be notified about the host lookup + * result - used to match request information to requestor. + * @param aReason + * nsresult reason for the cancellation + * @param aOriginAttributes + * the originAttribute for this resolving. This attribute is optional + * to avoid breaking add-ons. + */ + [implicit_jscontext, optional_argc] + void cancelAsyncResolve(in AUTF8String aHostName, + in nsIDNSService_ResolveType aType, + in nsIDNSService_DNSFlags aFlags, + in nsIDNSAdditionalInfo aResolver, + in nsIDNSListener aListener, + in nsresult aReason, + [optional] in jsval aOriginAttributes); + + [notxpcom] + nsresult cancelAsyncResolveNative(in AUTF8String aHostName, + in nsIDNSService_ResolveType aType, + in nsIDNSService_DNSFlags aFlags, + in nsIDNSAdditionalInfo aResolver, + in nsIDNSListener aListener, + in nsresult aReason, + in OriginAttributes aOriginAttributes); + + /** + * called to synchronously resolve a hostname. + * + * Since this method may block the calling thread for a long period of + * time, it may not be accessed from the main thread. + * + * @param aHostName + * the hostname or IP-address-literal to resolve. + * @param aFlags + * a bitwise OR of the RESOLVE_ prefixed constants defined below. + * @param aOriginAttributes + * the originAttribute for this resolving, the DNS cache will be + * separated according to this originAttributes. This attribute is + * optional to avoid breaking add-ons. + * + * @return DNS record corresponding to the given hostname. + * @throws NS_ERROR_UNKNOWN_HOST if host could not be resolved. + * @throws NS_ERROR_NOT_AVAILABLE if accessed from the main thread. + */ + [implicit_jscontext, optional_argc] + nsIDNSRecord resolve(in AUTF8String aHostName, + in nsIDNSService_DNSFlags aFlags, + [optional] in jsval aOriginAttributes); + + [notxpcom] + nsresult resolveNative(in AUTF8String aHostName, + in nsIDNSService_DNSFlags aFlags, + in OriginAttributes aOriginAttributes, + out nsIDNSRecord aResult); + + /** + * The method takes a pointer to an nsTArray + * and fills it with cache entry data + * Called by the networking dashboard + */ + [noscript] void getDNSCacheEntries(in EntriesArray args); + + + /** + * Clears the DNS cache. + * @param aTrrToo + * If true we will clear TRR cached entries too. Since these + * are resolved remotely it's not necessary to clear them when + * the network status changes, but it's sometimes useful to do so + * for tests or other situations. + */ + void clearCache(in boolean aTrrToo); + + /** + * The method is used only for test purpose. We use this to recheck if + * parental control is enabled or not. + */ + void reloadParentalControlEnabled(); + + /** + * Notifies the TRR service of a TRR that was automatically detected based + * on network preferences. + */ + void setDetectedTrrURI(in AUTF8String aURI); + + /** + * Stores the result of the TRR heuristic detection. + * Will be TRR_OK if no heuristics failed. + */ + void setHeuristicDetectionResult(in nsITRRSkipReason_value value); + + /** + * Returns the result of the last TRR heuristic detection. + * Will be TRR_OK if no heuristics failed. + */ + readonly attribute nsITRRSkipReason_value heuristicDetectionResult; + + ACString getTRRSkipReasonName(in nsITRRSkipReason_value value); + + /** + * The channel status of the last TRR confirmation attempt. + * In strict mode it reflects the channel status of the last TRR request. + */ + readonly attribute nsresult lastConfirmationStatus; + + /** + * The TRR skip reason of the last TRR confirmation attempt. + * In strict mode it reflects the TRR skip reason of the last TRR request. + */ + readonly attribute nsITRRSkipReason_value lastConfirmationSkipReason; + + /** + * Notifies the DNS service that we failed to connect to this alternative + * endpoint. + * @param aOwnerName + * The owner name of this HTTPS RRs. + * @param aSVCDomainName + * The domain name of this alternative endpoint. + */ + [noscript] void ReportFailedSVCDomainName(in ACString aOwnerName, + in ACString aSVCDomainName); + + /** + * Check if the given domain name was failed to connect to before. + * @param aOwnerName + * The owner name of this HTTPS RRs. + * @param aSVCDomainName + * The domain name of this alternative endpoint. + */ + [noscript] boolean IsSVCDomainNameFailed(in ACString aOwnerName, + in ACString aSVCDomainName); + + /** + * Reset the exclusion list. + * @param aOwnerName + * The owner name of this HTTPS RRs. + */ + [noscript] void ResetExcludedSVCDomainName(in ACString aOwnerName); + + /** + * Returns a string containing the URI currently used by the TRR service. + */ + readonly attribute AUTF8String currentTrrURI; + + /** + * Returns the value of the TRR Service's current default mode. + */ + readonly attribute nsIDNSService_ResolverMode currentTrrMode; + + /** + * The TRRService's current confirmation state. + * This is mostly for testing purposes. + */ + readonly attribute unsigned long currentTrrConfirmationState; + + /** + * @return the hostname of the operating system. + */ + readonly attribute AUTF8String myHostName; + + /** + * returns the current TRR domain. + */ + readonly attribute ACString trrDomain; + + /** + * returns the telemetry key for current TRR domain. + */ + readonly attribute ACString TRRDomainKey; + + /************************************************************************* + * Listed below are the various flags that may be OR'd together to form + * the aFlags parameter passed to asyncResolve() and resolve(). + */ + +%{C++ + static nsIDNSService::DNSFlags GetFlagsFromTRRMode(nsIRequest::TRRMode aMode) { + return static_cast<nsIDNSService::DNSFlags>(static_cast<uint32_t>(aMode) << 11); + } + + static nsIRequest::TRRMode GetTRRModeFromFlags(nsIDNSService::DNSFlags aFlags) { + return static_cast<nsIRequest::TRRMode>((aFlags & RESOLVE_TRR_MODE_MASK) >> 11); + } +%} + +}; + +%{C++ + +/** + * An observer notification for this topic is sent whenever the URI that the + * TRR service is using has changed. + */ +#define NS_NETWORK_TRR_URI_CHANGED_TOPIC "network:trr-uri-changed" + +/** + * An observer notification for this topic is sent whenever the mode that the + * TRR service is using has changed. + */ +#define NS_NETWORK_TRR_MODE_CHANGED_TOPIC "network:trr-mode-changed" + +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsIDNSService::DNSFlags) + +%} diff --git a/netwerk/dns/nsIDNService.cpp b/netwerk/dns/nsIDNService.cpp new file mode 100644 index 0000000000..3db169d3af --- /dev/null +++ b/netwerk/dns/nsIDNService.cpp @@ -0,0 +1,855 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "MainThreadUtils.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/Preferences.h" +#include "nsIDNService.h" +#include "nsReadableUtils.h" +#include "nsCRT.h" +#include "nsServiceManagerUtils.h" +#include "nsUnicharUtils.h" +#include "nsUnicodeProperties.h" +#include "harfbuzz/hb.h" +#include "punycode.h" +#include "mozilla/ArrayUtils.h" +#include "mozilla/Casting.h" +#include "mozilla/StaticPrefs_network.h" +#include "mozilla/TextUtils.h" +#include "mozilla/Utf8.h" +#include "mozilla/intl/FormatBuffer.h" +#include "mozilla/intl/UnicodeProperties.h" +#include "mozilla/intl/UnicodeScriptCodes.h" + +#include "ICUUtils.h" + +using namespace mozilla; +using namespace mozilla::intl; +using namespace mozilla::unicode; +using namespace mozilla::net; +using mozilla::Preferences; + +// Currently we use the non-transitional processing option -- see +// http://unicode.org/reports/tr46/ +// To switch to transitional processing, change the value of this flag +// and kTransitionalProcessing in netwerk/test/unit/test_idna2008.js to true +// (revert bug 1218179). +const intl::IDNA::ProcessingType kIDNA2008_DefaultProcessingType = + intl::IDNA::ProcessingType::NonTransitional; + +//----------------------------------------------------------------------------- +// According to RFC 1034 - 3.1. Name space specifications and terminology +// the maximum label size would be 63. However, this is enforced at the DNS +// level and none of the other browsers seem to not enforce the VerifyDnsLength +// check in https://unicode.org/reports/tr46/#ToASCII +// Instead, we choose a rather arbitrary but larger size. +static const uint32_t kMaxULabelSize = 256; +// RFC 3490 - 5. ACE prefix +static const char kACEPrefix[] = "xn--"; + +//----------------------------------------------------------------------------- + +#define NS_NET_PREF_EXTRAALLOWED "network.IDN.extra_allowed_chars" +#define NS_NET_PREF_EXTRABLOCKED "network.IDN.extra_blocked_chars" +#define NS_NET_PREF_IDNRESTRICTION "network.IDN.restriction_profile" + +static inline bool isOnlySafeChars(const nsString& in, + const nsTArray<BlocklistRange>& aBlocklist) { + if (aBlocklist.IsEmpty()) { + return true; + } + const char16_t* cur = in.BeginReading(); + const char16_t* end = in.EndReading(); + + for (; cur < end; ++cur) { + if (CharInBlocklist(*cur, aBlocklist)) { + return false; + } + } + return true; +} + +//----------------------------------------------------------------------------- +// nsIDNService +//----------------------------------------------------------------------------- + +/* Implementation file */ +NS_IMPL_ISUPPORTS(nsIDNService, nsIIDNService) + +static const char* gCallbackPrefs[] = { + NS_NET_PREF_EXTRAALLOWED, + NS_NET_PREF_EXTRABLOCKED, + NS_NET_PREF_IDNRESTRICTION, + nullptr, +}; + +nsresult nsIDNService::Init() { + MOZ_ASSERT(NS_IsMainThread()); + // Take a strong reference for our listener with the preferences service, + // which we will release on shutdown. + // It's OK if we remove the observer a bit early, as it just means we won't + // respond to `network.IDN.extra_{allowed,blocked}_chars` and + // `network.IDN.restriction_profile` pref changes during shutdown. + Preferences::RegisterPrefixCallbacks(PrefChanged, gCallbackPrefs, this); + RunOnShutdown( + [self = RefPtr{this}]() mutable { + Preferences::UnregisterPrefixCallbacks(PrefChanged, gCallbackPrefs, + self.get()); + self = nullptr; + }, + ShutdownPhase::XPCOMWillShutdown); + prefsChanged(nullptr); + + return NS_OK; +} + +void nsIDNService::prefsChanged(const char* pref) { + MOZ_ASSERT(NS_IsMainThread()); + AutoWriteLock lock(mLock); + + if (!pref || nsLiteralCString(NS_NET_PREF_EXTRAALLOWED).Equals(pref) || + nsLiteralCString(NS_NET_PREF_EXTRABLOCKED).Equals(pref)) { + InitializeBlocklist(mIDNBlocklist); + } + if (!pref || nsLiteralCString(NS_NET_PREF_IDNRESTRICTION).Equals(pref)) { + nsAutoCString profile; + if (NS_FAILED( + Preferences::GetCString(NS_NET_PREF_IDNRESTRICTION, profile))) { + profile.Truncate(); + } + if (profile.EqualsLiteral("moderate")) { + mRestrictionProfile = eModeratelyRestrictiveProfile; + } else if (profile.EqualsLiteral("high")) { + mRestrictionProfile = eHighlyRestrictiveProfile; + } else { + mRestrictionProfile = eASCIIOnlyProfile; + } + } +} + +nsIDNService::nsIDNService() { + MOZ_ASSERT(NS_IsMainThread()); + + auto createResult = + mozilla::intl::IDNA::TryCreate(kIDNA2008_DefaultProcessingType); + MOZ_ASSERT(createResult.isOk()); + mIDNA = createResult.unwrap(); +} + +nsIDNService::~nsIDNService() = default; + +nsresult nsIDNService::IDNA2008ToUnicode(const nsACString& input, + nsAString& output) { + NS_ConvertUTF8toUTF16 inputStr(input); + + Span<const char16_t> inputSpan{inputStr}; + intl::nsTStringToBufferAdapter buffer(output); + auto result = mIDNA->LabelToUnicode(inputSpan, buffer); + + nsresult rv = NS_OK; + if (result.isErr()) { + rv = ICUUtils::ICUErrorToNsResult(result.unwrapErr()); + if (rv == NS_ERROR_FAILURE) { + rv = NS_ERROR_MALFORMED_URI; + } + } + NS_ENSURE_SUCCESS(rv, rv); + + intl::IDNA::Info info = result.unwrap(); + if (info.HasErrors()) { + rv = NS_ERROR_MALFORMED_URI; + } + + return rv; +} + +nsresult nsIDNService::IDNA2008StringPrep(const nsAString& input, + nsAString& output, + stringPrepFlag flag) { + Span<const char16_t> inputSpan{input}; + intl::nsTStringToBufferAdapter buffer(output); + auto result = mIDNA->LabelToUnicode(inputSpan, buffer); + + nsresult rv = NS_OK; + if (result.isErr()) { + rv = ICUUtils::ICUErrorToNsResult(result.unwrapErr()); + if (rv == NS_ERROR_FAILURE) { + rv = NS_ERROR_MALFORMED_URI; + } + } + NS_ENSURE_SUCCESS(rv, rv); + + intl::IDNA::Info info = result.unwrap(); + + // Output the result of nameToUnicode even if there were errors. + // But in the case of invalid punycode, the uidna_labelToUnicode result + // appears to get an appended U+FFFD REPLACEMENT CHARACTER, which will + // confuse our subsequent processing, so we drop that. + // (https://bugzilla.mozilla.org/show_bug.cgi?id=1399540#c9) + if ((info.HasInvalidPunycode() || info.HasInvalidAceLabel()) && + !output.IsEmpty() && output.Last() == 0xfffd) { + output.Truncate(output.Length() - 1); + } + + if (flag == eStringPrepIgnoreErrors) { + return NS_OK; + } + + if (flag == eStringPrepForDNS) { + // We ignore errors if the result is empty, or if the errors were just + // invalid hyphens (not punycode-decoding failure or invalid chars). + if (!output.IsEmpty()) { + if (info.HasErrorsIgnoringInvalidHyphen()) { + output.Truncate(); + rv = NS_ERROR_MALFORMED_URI; + } + } + } else { + if (info.HasErrors()) { + rv = NS_ERROR_MALFORMED_URI; + } + } + + return rv; +} + +NS_IMETHODIMP nsIDNService::ConvertUTF8toACE(const nsACString& input, + nsACString& ace) { + return UTF8toACE(input, ace, eStringPrepForDNS); +} + +nsresult nsIDNService::UTF8toACE(const nsACString& input, nsACString& ace, + stringPrepFlag flag) { + nsresult rv; + NS_ConvertUTF8toUTF16 ustr(input); + + // map ideographic period to ASCII period etc. + normalizeFullStops(ustr); + + uint32_t len, offset; + len = 0; + offset = 0; + nsAutoCString encodedBuf; + + nsAString::const_iterator start, end; + ustr.BeginReading(start); + ustr.EndReading(end); + ace.Truncate(); + + // encode nodes if non ASCII + while (start != end) { + len++; + if (*start++ == (char16_t)'.') { + rv = stringPrepAndACE(Substring(ustr, offset, len - 1), encodedBuf, flag); + NS_ENSURE_SUCCESS(rv, rv); + + ace.Append(encodedBuf); + ace.Append('.'); + offset += len; + len = 0; + } + } + + // encode the last node if non ASCII + if (len) { + rv = stringPrepAndACE(Substring(ustr, offset, len), encodedBuf, flag); + NS_ENSURE_SUCCESS(rv, rv); + + ace.Append(encodedBuf); + } + + return NS_OK; +} + +NS_IMETHODIMP nsIDNService::ConvertACEtoUTF8(const nsACString& input, + nsACString& _retval) { + return ACEtoUTF8(input, _retval, eStringPrepForDNS); +} + +nsresult nsIDNService::ACEtoUTF8(const nsACString& input, nsACString& _retval, + stringPrepFlag flag) { + // RFC 3490 - 4.2 ToUnicode + // ToUnicode never fails. If any step fails, then the original input + // sequence is returned immediately in that step. + // + // Note that this refers to the decoding of a single label. + // ACEtoUTF8 may be called with a sequence of labels separated by dots; + // this test applies individually to each label. + + uint32_t len = 0, offset = 0; + nsAutoCString decodedBuf; + + nsACString::const_iterator start, end; + input.BeginReading(start); + input.EndReading(end); + _retval.Truncate(); + + // loop and decode nodes + while (start != end) { + len++; + if (*start++ == '.') { + nsDependentCSubstring origLabel(input, offset, len - 1); + if (NS_FAILED(decodeACE(origLabel, decodedBuf, flag))) { + // If decoding failed, use the original input sequence + // for this label. + _retval.Append(origLabel); + } else { + _retval.Append(decodedBuf); + } + + _retval.Append('.'); + offset += len; + len = 0; + } + } + // decode the last node + if (len) { + nsDependentCSubstring origLabel(input, offset, len); + if (NS_FAILED(decodeACE(origLabel, decodedBuf, flag))) { + _retval.Append(origLabel); + } else { + _retval.Append(decodedBuf); + } + } + + return NS_OK; +} + +NS_IMETHODIMP nsIDNService::IsACE(const nsACString& input, bool* _retval) { + // look for the ACE prefix in the input string. it may occur + // at the beginning of any segment in the domain name. for + // example: "www.xn--ENCODED.com" + + if (!IsAscii(input)) { + *_retval = false; + return NS_OK; + } + + auto stringContains = [](const nsACString& haystack, + const nsACString& needle) { + return std::search(haystack.BeginReading(), haystack.EndReading(), + needle.BeginReading(), needle.EndReading(), + [](unsigned char ch1, unsigned char ch2) { + return tolower(ch1) == tolower(ch2); + }) != haystack.EndReading(); + }; + + *_retval = + StringBeginsWith(input, "xn--"_ns, nsCaseInsensitiveCStringComparator) || + (!input.IsEmpty() && input[0] != '.' && + stringContains(input, ".xn--"_ns)); + return NS_OK; +} + +NS_IMETHODIMP nsIDNService::Normalize(const nsACString& input, + nsACString& output) { + // protect against bogus input + NS_ENSURE_TRUE(IsUtf8(input), NS_ERROR_UNEXPECTED); + + NS_ConvertUTF8toUTF16 inUTF16(input); + normalizeFullStops(inUTF16); + + // pass the domain name to stringprep label by label + nsAutoString outUTF16, outLabel; + + uint32_t len = 0, offset = 0; + nsresult rv; + nsAString::const_iterator start, end; + inUTF16.BeginReading(start); + inUTF16.EndReading(end); + + while (start != end) { + len++; + if (*start++ == char16_t('.')) { + rv = stringPrep(Substring(inUTF16, offset, len - 1), outLabel, + eStringPrepIgnoreErrors); + NS_ENSURE_SUCCESS(rv, rv); + + outUTF16.Append(outLabel); + outUTF16.Append(char16_t('.')); + offset += len; + len = 0; + } + } + if (len) { + rv = stringPrep(Substring(inUTF16, offset, len), outLabel, + eStringPrepIgnoreErrors); + NS_ENSURE_SUCCESS(rv, rv); + + outUTF16.Append(outLabel); + } + + CopyUTF16toUTF8(outUTF16, output); + return NS_OK; +} + +NS_IMETHODIMP nsIDNService::ConvertToDisplayIDN(const nsACString& input, + bool* _isASCII, + nsACString& _retval) { + // If host is ACE, then convert to UTF-8 if the host is in the IDN whitelist. + // Else, if host is already UTF-8, then make sure it is normalized per IDN. + + nsresult rv = NS_OK; + + // Even if the hostname is not ASCII, individual labels may still be ACE, so + // test IsACE before testing IsASCII + bool isACE; + IsACE(input, &isACE); + + if (IsAscii(input)) { + // first, canonicalize the host to lowercase, for whitelist lookup + _retval = input; + ToLowerCase(_retval); + + if (isACE && !StaticPrefs::network_IDN_show_punycode()) { + // ACEtoUTF8() can't fail, but might return the original ACE string + nsAutoCString temp(_retval); + // Convert from ACE to UTF8 only those labels which are considered safe + // for display + ACEtoUTF8(temp, _retval, eStringPrepForUI); + *_isASCII = IsAscii(_retval); + } else { + *_isASCII = true; + } + } else { + // We have to normalize the hostname before testing against the domain + // whitelist (see bug 315411), and to ensure the entire string gets + // normalized. + // + // Normalization and the tests for safe display below, assume that the + // input is Unicode, so first convert any ACE labels to UTF8 + if (isACE) { + nsAutoCString temp; + ACEtoUTF8(input, temp, eStringPrepIgnoreErrors); + rv = Normalize(temp, _retval); + } else { + rv = Normalize(input, _retval); + } + if (NS_FAILED(rv)) { + return rv; + } + + if (StaticPrefs::network_IDN_show_punycode() && + NS_SUCCEEDED(UTF8toACE(_retval, _retval, eStringPrepIgnoreErrors))) { + *_isASCII = true; + return NS_OK; + } + + // normalization could result in an ASCII-only hostname. alternatively, if + // the host is converted to ACE by the normalizer, then the host may contain + // unsafe characters, so leave it ACE encoded. see bug 283016, bug 301694, + // and bug 309311. + *_isASCII = IsAscii(_retval); + if (!*_isASCII) { + // UTF8toACE with eStringPrepForUI may return a domain name where + // some labels are in UTF-8 and some are in ACE, depending on + // whether they are considered safe for display + rv = UTF8toACE(_retval, _retval, eStringPrepForUI); + *_isASCII = IsAscii(_retval); + return rv; + } + } + + return NS_OK; +} // Will generate a mutex still-held warning + +//----------------------------------------------------------------------------- + +static nsresult utf16ToUcs4(const nsAString& in, uint32_t* out, + uint32_t outBufLen, uint32_t* outLen) { + uint32_t i = 0; + nsAString::const_iterator start, end; + in.BeginReading(start); + in.EndReading(end); + + while (start != end) { + char16_t curChar; + + curChar = *start++; + + if (start != end && NS_IS_SURROGATE_PAIR(curChar, *start)) { + out[i] = SURROGATE_TO_UCS4(curChar, *start); + ++start; + } else { + out[i] = curChar; + } + + i++; + if (i >= outBufLen) { + return NS_ERROR_MALFORMED_URI; + } + } + out[i] = (uint32_t)'\0'; + *outLen = i; + return NS_OK; +} + +static nsresult punycode(const nsAString& in, nsACString& out) { + uint32_t ucs4Buf[kMaxULabelSize + 1]; + uint32_t ucs4Len = 0u; + nsresult rv = utf16ToUcs4(in, ucs4Buf, kMaxULabelSize, &ucs4Len); + NS_ENSURE_SUCCESS(rv, rv); + + // need maximum 20 bits to encode 16 bit Unicode character + // (include null terminator) + const uint32_t kEncodedBufSize = kMaxULabelSize * 20 / 8 + 1 + 1; + char encodedBuf[kEncodedBufSize]; + punycode_uint encodedLength = kEncodedBufSize; + + enum punycode_status status = + punycode_encode(ucs4Len, ucs4Buf, nullptr, &encodedLength, encodedBuf); + + if (punycode_success != status || encodedLength >= kEncodedBufSize) { + return NS_ERROR_MALFORMED_URI; + } + + encodedBuf[encodedLength] = '\0'; + out.Assign(nsDependentCString(kACEPrefix) + nsDependentCString(encodedBuf)); + + return rv; +} + +// RFC 3454 +// +// 1) Map -- For each character in the input, check if it has a mapping +// and, if so, replace it with its mapping. This is described in section 3. +// +// 2) Normalize -- Possibly normalize the result of step 1 using Unicode +// normalization. This is described in section 4. +// +// 3) Prohibit -- Check for any characters that are not allowed in the +// output. If any are found, return an error. This is described in section +// 5. +// +// 4) Check bidi -- Possibly check for right-to-left characters, and if any +// are found, make sure that the whole string satisfies the requirements +// for bidirectional strings. If the string does not satisfy the requirements +// for bidirectional strings, return an error. This is described in section 6. +// +// 5) Check unassigned code points -- If allowUnassigned is false, check for +// any unassigned Unicode points and if any are found return an error. +// This is described in section 7. +// +nsresult nsIDNService::stringPrep(const nsAString& in, nsAString& out, + stringPrepFlag flag) { + return IDNA2008StringPrep(in, out, flag); +} + +nsresult nsIDNService::stringPrepAndACE(const nsAString& in, nsACString& out, + stringPrepFlag flag) { + nsresult rv = NS_OK; + + out.Truncate(); + + if (IsAscii(in)) { + LossyCopyUTF16toASCII(in, out); + // If label begins with xn-- we still want to check its validity + if (!StringBeginsWith(in, u"xn--"_ns, nsCaseInsensitiveStringComparator)) { + return NS_OK; + } + } + + nsAutoString strPrep; + rv = stringPrep(in, strPrep, flag); + if (flag == eStringPrepForDNS) { + NS_ENSURE_SUCCESS(rv, rv); + } + + if (IsAscii(strPrep)) { + LossyCopyUTF16toASCII(strPrep, out); + return NS_OK; + } + + if (flag == eStringPrepForUI && NS_SUCCEEDED(rv) && isLabelSafe(in)) { + CopyUTF16toUTF8(strPrep, out); + return NS_OK; + } + + return punycode(strPrep, out); +} + +// RFC 3490 +// 1) Whenever dots are used as label separators, the following characters +// MUST be recognized as dots: U+002E (full stop), U+3002 (ideographic full +// stop), U+FF0E (fullwidth full stop), U+FF61 (halfwidth ideographic full +// stop). + +void nsIDNService::normalizeFullStops(nsAString& s) { + nsAString::const_iterator start, end; + s.BeginReading(start); + s.EndReading(end); + int32_t index = 0; + + while (start != end) { + switch (*start) { + case 0x3002: + case 0xFF0E: + case 0xFF61: + s.ReplaceLiteral(index, 1, u"."); + break; + default: + break; + } + start++; + index++; + } +} + +nsresult nsIDNService::decodeACE(const nsACString& in, nsACString& out, + stringPrepFlag flag) { + bool isAce; + IsACE(in, &isAce); + if (!isAce) { + out.Assign(in); + return NS_OK; + } + + nsAutoString utf16; + nsresult result = IDNA2008ToUnicode(in, utf16); + NS_ENSURE_SUCCESS(result, result); + + if (flag != eStringPrepForUI || isLabelSafe(utf16)) { + CopyUTF16toUTF8(utf16, out); + } else { + out.Assign(in); + return NS_OK; + } + + // Validation: encode back to ACE and compare the strings + nsAutoCString ace; + nsresult rv = UTF8toACE(out, ace, flag); + NS_ENSURE_SUCCESS(rv, rv); + + if (flag == eStringPrepForDNS && + !ace.Equals(in, nsCaseInsensitiveCStringComparator)) { + return NS_ERROR_MALFORMED_URI; + } + + return NS_OK; +} + +namespace mozilla::net { + +enum ScriptCombo : int32_t { + UNSET = -1, + BOPO = 0, + CYRL = 1, + GREK = 2, + HANG = 3, + HANI = 4, + HIRA = 5, + KATA = 6, + LATN = 7, + OTHR = 8, + JPAN = 9, // Latin + Han + Hiragana + Katakana + CHNA = 10, // Latin + Han + Bopomofo + KORE = 11, // Latin + Han + Hangul + HNLT = 12, // Latin + Han (could be any of the above combinations) + FAIL = 13, +}; + +} // namespace mozilla::net + +bool nsIDNService::isLabelSafe(const nsAString& label) { + AutoReadLock lock(mLock); + + if (!isOnlySafeChars(PromiseFlatString(label), mIDNBlocklist)) { + return false; + } + + // We should never get here if the label is ASCII + NS_ASSERTION(!IsAscii(label), "ASCII label in IDN checking"); + if (mRestrictionProfile == eASCIIOnlyProfile) { + return false; + } + + nsAString::const_iterator current, end; + label.BeginReading(current); + label.EndReading(end); + + Script lastScript = Script::INVALID; + uint32_t previousChar = 0; + uint32_t baseChar = 0; // last non-diacritic seen (base char for marks) + uint32_t savedNumberingSystem = 0; +// Simplified/Traditional Chinese check temporarily disabled -- bug 857481 +#if 0 + HanVariantType savedHanVariant = HVT_NotHan; +#endif + + ScriptCombo savedScript = ScriptCombo::UNSET; + + while (current != end) { + uint32_t ch = *current++; + + if (current != end && NS_IS_SURROGATE_PAIR(ch, *current)) { + ch = SURROGATE_TO_UCS4(ch, *current++); + } + + IdentifierType idType = GetIdentifierType(ch); + if (idType == IDTYPE_RESTRICTED) { + return false; + } + MOZ_ASSERT(idType == IDTYPE_ALLOWED); + + // Check for mixed script + Script script = UnicodeProperties::GetScriptCode(ch); + if (script != Script::COMMON && script != Script::INHERITED && + script != lastScript) { + if (illegalScriptCombo(script, savedScript)) { + return false; + } + } + + // U+30FC should be preceded by a Hiragana/Katakana. + if (ch == 0x30fc && lastScript != Script::HIRAGANA && + lastScript != Script::KATAKANA) { + return false; + } + + if (ch == 0x307 && + (previousChar == 'i' || previousChar == 'j' || previousChar == 'l')) { + return false; + } + + // Check for mixed numbering systems + auto genCat = GetGeneralCategory(ch); + if (genCat == HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) { + uint32_t zeroCharacter = + ch - mozilla::intl::UnicodeProperties::GetNumericValue(ch); + if (savedNumberingSystem == 0) { + // If we encounter a decimal number, save the zero character from that + // numbering system. + savedNumberingSystem = zeroCharacter; + } else if (zeroCharacter != savedNumberingSystem) { + return false; + } + } + + if (genCat == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) { + // Check for consecutive non-spacing marks. + if (previousChar != 0 && previousChar == ch) { + return false; + } + // Check for marks whose expected script doesn't match the base script. + if (lastScript != Script::INVALID) { + UnicodeProperties::ScriptExtensionVector scripts; + auto extResult = UnicodeProperties::GetExtensions(ch, scripts); + MOZ_ASSERT(extResult.isOk()); + if (extResult.isErr()) { + return false; + } + + int nScripts = AssertedCast<int>(scripts.length()); + + // nScripts will always be >= 1, because even for undefined characters + // it will return Script::INVALID. + // If the mark just has script=COMMON or INHERITED, we can't check any + // more carefully, but if it has specific scriptExtension codes, then + // assume those are the only valid scripts to use it with. + if (nScripts > 1 || (Script(scripts[0]) != Script::COMMON && + Script(scripts[0]) != Script::INHERITED)) { + while (--nScripts >= 0) { + if (Script(scripts[nScripts]) == lastScript) { + break; + } + } + if (nScripts == -1) { + return false; + } + } + } + // Check for diacritics on dotless-i, which would be indistinguishable + // from normal accented letter i. + if (baseChar == 0x0131 && + ((ch >= 0x0300 && ch <= 0x0314) || ch == 0x031a)) { + return false; + } + } else { + baseChar = ch; + } + + if (script != Script::COMMON && script != Script::INHERITED) { + lastScript = script; + } + + // Simplified/Traditional Chinese check temporarily disabled -- bug 857481 +#if 0 + + // Check for both simplified-only and traditional-only Chinese characters + HanVariantType hanVariant = GetHanVariant(ch); + if (hanVariant == HVT_SimplifiedOnly || hanVariant == HVT_TraditionalOnly) { + if (savedHanVariant == HVT_NotHan) { + savedHanVariant = hanVariant; + } else if (hanVariant != savedHanVariant) { + return false; + } + } +#endif + + previousChar = ch; + } + return true; +} + +// Scripts that we care about in illegalScriptCombo +static inline ScriptCombo findScriptIndex(Script aScript) { + switch (aScript) { + case Script::BOPOMOFO: + return ScriptCombo::BOPO; + case Script::CYRILLIC: + return ScriptCombo::CYRL; + case Script::GREEK: + return ScriptCombo::GREK; + case Script::HANGUL: + return ScriptCombo::HANG; + case Script::HAN: + return ScriptCombo::HANI; + case Script::HIRAGANA: + return ScriptCombo::HIRA; + case Script::KATAKANA: + return ScriptCombo::KATA; + case Script::LATIN: + return ScriptCombo::LATN; + default: + return ScriptCombo::OTHR; + } +} + +static const ScriptCombo scriptComboTable[13][9] = { + /* thisScript: BOPO CYRL GREK HANG HANI HIRA KATA LATN OTHR + * savedScript */ + /* BOPO */ {BOPO, FAIL, FAIL, FAIL, CHNA, FAIL, FAIL, CHNA, FAIL}, + /* CYRL */ {FAIL, CYRL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL}, + /* GREK */ {FAIL, FAIL, GREK, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL}, + /* HANG */ {FAIL, FAIL, FAIL, HANG, KORE, FAIL, FAIL, KORE, FAIL}, + /* HANI */ {CHNA, FAIL, FAIL, KORE, HANI, JPAN, JPAN, HNLT, FAIL}, + /* HIRA */ {FAIL, FAIL, FAIL, FAIL, JPAN, HIRA, JPAN, JPAN, FAIL}, + /* KATA */ {FAIL, FAIL, FAIL, FAIL, JPAN, JPAN, KATA, JPAN, FAIL}, + /* LATN */ {CHNA, FAIL, FAIL, KORE, HNLT, JPAN, JPAN, LATN, OTHR}, + /* OTHR */ {FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, OTHR, FAIL}, + /* JPAN */ {FAIL, FAIL, FAIL, FAIL, JPAN, JPAN, JPAN, JPAN, FAIL}, + /* CHNA */ {CHNA, FAIL, FAIL, FAIL, CHNA, FAIL, FAIL, CHNA, FAIL}, + /* KORE */ {FAIL, FAIL, FAIL, KORE, KORE, FAIL, FAIL, KORE, FAIL}, + /* HNLT */ {CHNA, FAIL, FAIL, KORE, HNLT, JPAN, JPAN, HNLT, FAIL}}; + +bool nsIDNService::illegalScriptCombo(Script script, ScriptCombo& savedScript) { + if (savedScript == ScriptCombo::UNSET) { + savedScript = findScriptIndex(script); + return false; + } + + savedScript = scriptComboTable[savedScript][findScriptIndex(script)]; + /* + * Special case combinations that depend on which profile is in use + * In the Highly Restrictive profile Latin is not allowed with any + * other script + * + * In the Moderately Restrictive profile Latin mixed with any other + * single script is allowed. + */ + return ((savedScript == OTHR && + mRestrictionProfile == eHighlyRestrictiveProfile) || + savedScript == FAIL); +} diff --git a/netwerk/dns/nsIDNService.h b/netwerk/dns/nsIDNService.h new file mode 100644 index 0000000000..1e90191326 --- /dev/null +++ b/netwerk/dns/nsIDNService.h @@ -0,0 +1,191 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsIDNService_h__ +#define nsIDNService_h__ + +#include "nsIIDNService.h" +#include "nsCOMPtr.h" + +#include "mozilla/RWLock.h" +#include "mozilla/intl/UnicodeScriptCodes.h" +#include "mozilla/net/IDNBlocklistUtils.h" +#include "mozilla/intl/IDNA.h" +#include "mozilla/UniquePtr.h" + +#include "nsString.h" + +class nsIPrefBranch; + +//----------------------------------------------------------------------------- +// nsIDNService +//----------------------------------------------------------------------------- + +namespace mozilla::net { +enum ScriptCombo : int32_t; +} + +class nsIDNService final : public nsIIDNService { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIIDNSERVICE + + nsIDNService(); + + nsresult Init(); + + protected: + virtual ~nsIDNService(); + + private: + enum stringPrepFlag { + eStringPrepForDNS, + eStringPrepForUI, + eStringPrepIgnoreErrors + }; + + /** + * Convert the following characters that must be recognized as label + * separators per RFC 3490 to ASCII full stop characters + * + * U+3002 (ideographic full stop) + * U+FF0E (fullwidth full stop) + * U+FF61 (halfwidth ideographic full stop) + */ + void normalizeFullStops(nsAString& s); + + /** + * Convert and encode a DNS label in ACE/punycode. + * @param flag + * if eStringPrepIgnoreErrors, all non-ASCII labels are + * converted to punycode. + * if eStringPrepForUI, only labels that are considered safe + * for display are converted. + * @see isLabelSafe + * if eStringPrepForDNS and stringPrep finds an illegal + * character, returns NS_FAILURE and out is empty + */ + nsresult stringPrepAndACE(const nsAString& in, nsACString& out, + stringPrepFlag flag); + + /** + * Convert a DNS label using the stringprep profile defined in RFC 3454 + */ + nsresult stringPrep(const nsAString& in, nsAString& out, stringPrepFlag flag); + + /** + * Decode an ACE-encoded DNS label to UTF-8 + * + * @param flag + * if eStringPrepForUI and the label is not considered safe to + * display, the output is the same as the input + * @see isLabelSafe + */ + nsresult decodeACE(const nsACString& in, nsACString& out, + stringPrepFlag flag); + + /** + * Convert complete domain names between UTF8 and ACE and vice versa + * + * @param flag is passed to decodeACE or stringPrepAndACE for each + * label individually, so the output may contain some labels in + * punycode and some in UTF-8 + */ + nsresult UTF8toACE(const nsACString& input, nsACString& ace, + stringPrepFlag flag); + nsresult ACEtoUTF8(const nsACString& input, nsACString& _retval, + stringPrepFlag flag); + + void prefsChanged(const char* pref); + + static void PrefChanged(const char* aPref, void* aSelf) { + auto* self = static_cast<nsIDNService*>(aSelf); + self->prefsChanged(aPref); + } + + /** + * Determine whether a label is considered safe to display to the user + * according to the algorithm defined in UTR 39 and the profile + * selected in mRestrictionProfile. + * + * For the ASCII-only profile, returns false for all labels containing + * non-ASCII characters. + * + * For the other profiles, returns false for labels containing any of + * the following: + * + * Characters in scripts other than the "recommended scripts" and + * "aspirational scripts" defined in + * http://www.unicode.org/reports/tr31/#Table_Recommended_Scripts + * and http://www.unicode.org/reports/tr31/#Aspirational_Use_Scripts + * This includes codepoints that are not defined as Unicode + * characters + * + * Illegal combinations of scripts (@see illegalScriptCombo) + * + * Numbers from more than one different numbering system + * + * Sequences of the same non-spacing mark + * + * Both simplified-only and traditional-only Chinese characters + * XXX this test was disabled by bug 857481 + */ + bool isLabelSafe(const nsAString& label) MOZ_EXCLUDES(mLock); + + /** + * Determine whether a combination of scripts in a single label is + * permitted according to the algorithm defined in UTR 39 and the + * profile selected in mRestrictionProfile. + * + * For the "Highly restrictive" profile, all characters in each + * identifier must be from a single script, or from the combinations: + * Latin + Han + Hiragana + Katakana; + * Latin + Han + Bopomofo; or + * Latin + Han + Hangul + * + * For the "Moderately restrictive" profile, Latin is also allowed + * with other scripts except Cyrillic and Greek + */ + bool illegalScriptCombo(mozilla::intl::Script script, + mozilla::net::ScriptCombo& savedScript) + MOZ_REQUIRES_SHARED(mLock); + + /** + * Convert a DNS label from ASCII to Unicode using IDNA2008 + */ + nsresult IDNA2008ToUnicode(const nsACString& input, nsAString& output); + + /** + * Convert a DNS label to a normalized form conforming to IDNA2008 + */ + nsresult IDNA2008StringPrep(const nsAString& input, nsAString& output, + stringPrepFlag flag); + + // never mutated after initializing. + mozilla::UniquePtr<mozilla::intl::IDNA> mIDNA; + + // We use this rwlock to guard access to: + // |mIDNBlocklist|, |mRestrictionProfile| + mozilla::RWLock mLock{"nsIDNService"}; + + // guarded by mLock + nsTArray<mozilla::net::BlocklistRange> mIDNBlocklist MOZ_GUARDED_BY(mLock); + + /** + * Restriction-level Detection profiles defined in UTR 39 + * http://www.unicode.org/reports/tr39/#Restriction_Level_Detection, + * and selected by the pref network.IDN.restriction_profile + */ + enum restrictionProfile { + eASCIIOnlyProfile, + eHighlyRestrictiveProfile, + eModeratelyRestrictiveProfile + }; + // guarded by mLock; + restrictionProfile mRestrictionProfile MOZ_GUARDED_BY(mLock){ + eASCIIOnlyProfile}; +}; + +#endif // nsIDNService_h__ diff --git a/netwerk/dns/nsIEffectiveTLDService.idl b/netwerk/dns/nsIEffectiveTLDService.idl new file mode 100644 index 0000000000..abf786e5ed --- /dev/null +++ b/netwerk/dns/nsIEffectiveTLDService.idl @@ -0,0 +1,205 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsIURI; + +[scriptable, uuid(68067eb5-ad8d-43cb-a043-1cc85ebe06e7)] +interface nsIEffectiveTLDService : nsISupports +{ + /** + * Returns the public suffix of a URI. A public suffix is the highest-level domain + * under which individual domains may be registered; it may therefore contain one + * or more dots. For example, the public suffix for "www.bbc.co.uk" is "co.uk", + * because the .uk TLD does not allow the registration of domains at the + * second level ("bbc.uk" is forbidden). + * + * The public suffix will be returned encoded in ASCII/ACE and will be normalized + * according to RFC 3454, i.e. the same encoding returned by nsIURI::GetAsciiHost(). + * If consumers wish to compare the result of this method against the host from + * another nsIURI, the host should be obtained using nsIURI::GetAsciiHost(). + * In the case of nested URIs, the innermost URI will be used. + * + * @param aURI The URI to be analyzed + * + * @returns the public suffix + * + * @throws NS_ERROR_UNEXPECTED + * or other error returned by nsIIDNService::normalize when + * the hostname contains characters disallowed in URIs + * @throws NS_ERROR_HOST_IS_IP_ADDRESS + * if the host is a numeric IPv4 or IPv6 address (as determined by + * the success of a call to PR_StringToNetAddr()). + */ + ACString getPublicSuffix(in nsIURI aURI); + + /** + * Similar to getPublicSuffix, but the suffix is validated against + * the Public Suffix List. If the suffix is unknown this will return + * an empty string. + * + * @param aURI The URI to be analyzed + * @returns the public suffix if known, an empty string otherwise + * @see getPublicSuffixFromHost() + */ + ACString getKnownPublicSuffix(in nsIURI aURI); + + /** + * Returns the base domain of a URI; that is, the public suffix with a given + * number of additional domain name parts. For example, the result of this method + * for "www.bbc.co.uk", depending on the value of aAdditionalParts parameter, will + * be: + * + * 0 (default) -> bbc.co.uk + * 1 -> www.bbc.co.uk + * + * Similarly, the public suffix for "www.developer.mozilla.org" is "org", and the base + * domain will be: + * + * 0 (default) -> mozilla.org + * 1 -> developer.mozilla.org + * 2 -> www.developer.mozilla.org + * + * The base domain will be returned encoded in ASCII/ACE and will be normalized + * according to RFC 3454, i.e. the same encoding returned by nsIURI::GetAsciiHost(). + * If consumers wish to compare the result of this method against the host from + * another nsIURI, the host should be obtained using nsIURI::GetAsciiHost(). + * In the case of nested URIs, the innermost URI will be used. + * + * @param aURI The URI to be analyzed + * @param aAdditionalParts Number of domain name parts to be + * returned in addition to the public suffix + * + * @returns the base domain (public suffix plus the requested number of additional parts) + * + * @throws NS_ERROR_UNEXPECTED + * or other error returned by nsIIDNService::normalize when + * the hostname contains characters disallowed in URIs + * @throws NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS + * when there are insufficient subdomain levels in the hostname to satisfy the + * requested aAdditionalParts value. + * @throws NS_ERROR_HOST_IS_IP_ADDRESS + * if aHost is a numeric IPv4 or IPv6 address (as determined by + * the success of a call to PR_StringToNetAddr()). + * + * @see getPublicSuffix() + */ + ACString getBaseDomain(in nsIURI aURI, [optional] in uint32_t aAdditionalParts); + + /** + * Get the Site without the scheme for the origin of aURI; e.g. for + * "https://www.bbc.co.uk/index.html", this would be "bbc.co.uk". + * This uses getBaseDomain() internally. This is appropriately permissive, + * and will return a schemeless site for aliased hostnames and IP addresses + * and will therefore not throw NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS or + * NS_ERROR_HOST_IS_IP_ADDRESS, e.g. "http://localhost/index.html" will + * return "localhost" successfully, rather than throwing an error. + * + * @param aHostURI + * The URI to analyze. + * + * @return the Site. + * + * @throws NS_ERROR_UNEXPECTED + * or other error returned by nsIIDNService::normalize when + * the hostname contains characters disallowed in URIs + * + * @see getBaseDomain() + * @see getSite() + * + * @warning This function should not be used without good reason. Please + * use getSite() or the Origin if you are not absolutely certain. + */ + ACString getSchemelessSite(in nsIURI aURI); + + /** + * Get the Site for the origin of aURI; e.g. for + * "https://www.bbc.co.uk/index.html", this would be "https://bbc.co.uk". + * This uses getBaseDomain() internally. This is appropriately permissive, + * and will return a scheme for alaised hostnames and IP addresses and will + * therefore not throw NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS or + * NS_ERROR_HOST_IS_IP_ADDRESS, e.g. "http://localhost/index.html" will + * return "http://localhost" successfully, rather than throwing an error. + * + * @param aHostURI + * The URI to analyze. + * + * @return the Site. + * + * @throws NS_ERROR_UNEXPECTED + * or other error returned by nsIIDNService::normalize when + * the hostname contains characters disallowed in URIs + * + * @see getBaseDomain() + */ + ACString getSite(in nsIURI aURI); + + /** + * NOTE: It is strongly recommended to use getPublicSuffix() above if a suitable + * nsIURI is available. Only use this method if this is not the case. + * + * Returns the public suffix of a host string. Otherwise identical to getPublicSuffix(). + * + * @param aHost The host to be analyzed. Any additional parts (e.g. scheme, + * port, or path) will cause this method to throw. ASCII/ACE and + * UTF8 encodings are acceptable as input; normalization will + * be performed as specified in getBaseDomain(). + * + * @see getPublicSuffix() + */ + ACString getPublicSuffixFromHost(in AUTF8String aHost); + + /** + * Similar to getPublicSuffixFromHost, but the suffix is validated against + * the Public Suffix List. If the suffix is unknown this will return + * an empty string. + * + * @param aHost The host to be analyzed. + * @returns the public suffix if known, an empty string otherwise + * @see getPublicSuffixFromHost() + */ + ACString getKnownPublicSuffixFromHost(in AUTF8String aHost); + + /** + * NOTE: It is strongly recommended to use getBaseDomain() above if a suitable + * nsIURI is available. Only use this method if this is not the case. + * + * Returns the base domain of a host string. Otherwise identical to getBaseDomain(). + * + * @param aHost The host to be analyzed. Any additional parts (e.g. scheme, + * port, or path) will cause this method to throw. ASCII/ACE and + * UTF8 encodings are acceptable as input; normalization will + * be performed as specified in getBaseDomain(). + * + * @see getBaseDomain() + */ + ACString getBaseDomainFromHost(in AUTF8String aHost, [optional] in uint32_t aAdditionalParts); + + /** + * Returns the parent sub-domain of a host string. If the host is a base + * domain, it will throw NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS. + * + * For example: "player.bbc.co.uk" would return "bbc.co.uk" and + * "bbc.co.uk" would throw NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS. + * + * @param aHost The host to be analyzed. Any additional parts (e.g. scheme, + * port, or path) will cause this method to throw. ASCII/ACE and + * UTF8 encodings are acceptable as input; normalization will + * be performed as specified in getBaseDomain(). + */ + ACString getNextSubDomain(in AUTF8String aHost); + + /** + * Returns true if the |aInput| in is part of the root domain of |aHost|. + * For example, if |aInput| is "www.mozilla.org", and we pass in + * "mozilla.org" as |aHost|, this will return true. It would return false + * the other way around. + * + * @param aInput The host to be analyzed. + * @param aHost The host to compare to. + */ + bool hasRootDomain(in AUTF8String aInput, in AUTF8String aHost); +}; diff --git a/netwerk/dns/nsIIDNService.idl b/netwerk/dns/nsIIDNService.idl new file mode 100644 index 0000000000..47ef561237 --- /dev/null +++ b/netwerk/dns/nsIIDNService.idl @@ -0,0 +1,58 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +/** + * nsIIDNService interface. + * + * IDN (Internationalized Domain Name) support. Provides facilities + * for manipulating IDN hostnames according to the specification set + * forth by the IETF. + * + * IDN effort: + * http://www.ietf.org/html.characters/idn-charter.html + * http://www.i-dns.net + * + * IDNA specification: + * http://search.ietf.org/internet-drafts/draft-ietf-idn-idna-06.txt + */ + +[scriptable, uuid(a592a60e-3621-4f19-a318-2bf233cfad3e)] +interface nsIIDNService : nsISupports +{ + /** + * Prepares the input hostname according to IDNA ToASCII operation, + * the input hostname is assumed to be UTF8-encoded. + */ + ACString convertUTF8toACE(in AUTF8String input); + + + /** + * This is the ToUnicode operation as specified in the IDNA proposal, + * with an additional step to encode the result in UTF-8. + * It takes an ACE-encoded hostname and performs ToUnicode to it, then + * encodes the resulting string into UTF8. + */ + AUTF8String convertACEtoUTF8(in ACString input); + + /** + * Checks if the input string is ACE encoded or not. + */ + boolean isACE(in ACString input); + + /** + * Performs the unicode normalization needed for hostnames in IDN, + * for callers that want early normalization. + */ + AUTF8String normalize(in AUTF8String input); + + /** + * Normalizes and converts a host to UTF-8 if the host is in the IDN + * whitelist, otherwise converts it to ACE. This is useful for display + * purposes and to ensure an encoding consistent with nsIURI::GetHost(). + * If the result is ASCII or ACE encoded, |isASCII| will be true. + */ + AUTF8String convertToDisplayIDN(in AUTF8String input, out boolean isASCII); +}; diff --git a/netwerk/dns/nsINativeDNSResolverOverride.idl b/netwerk/dns/nsINativeDNSResolverOverride.idl new file mode 100644 index 0000000000..0a016af437 --- /dev/null +++ b/netwerk/dns/nsINativeDNSResolverOverride.idl @@ -0,0 +1,37 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +[scriptable, builtinclass, uuid(8e38d536-5501-48c0-a412-6c450040c8c8)] +interface nsINativeDNSResolverOverride : nsISupports +{ + /** + * Adds an IP override for this specific host. + */ + void addIPOverride(in AUTF8String aHost, in ACString aIPLiteral); + + /** + * Adds an HTTPS record override for this specific host. + * The input needs to be the raw bytes of a DNS answer. + */ + void addHTTPSRecordOverride(in AUTF8String aHost, + [array, size_is(aLength), const] in uint8_t aData, + in unsigned long aLength); + + /** + * Sets a CNAME override for this specific host. + */ + void setCnameOverride(in AUTF8String aHost, in ACString aCNAME); + + /** + * Clears the overrides for this specific host + */ + void clearHostOverride(in AUTF8String aHost); + + /** + * Clears all the host overrides that were previously set. + */ + void clearOverrides(); +}; diff --git a/netwerk/dns/nsITRRSkipReason.idl b/netwerk/dns/nsITRRSkipReason.idl new file mode 100644 index 0000000000..096f0151bb --- /dev/null +++ b/netwerk/dns/nsITRRSkipReason.idl @@ -0,0 +1,121 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +[scriptable, uuid(e61b5d39-f6d6-4ed3-aead-1213b24c6f27)] +interface nsITRRSkipReason: nsISupports +{ + // IMPORTANT: when adding new values, always add them to the end, otherwise + // it will mess up telemetry. + // When adding a reason here, make sure it is documented in + // netwerk/docs/dns/trr-skip-reasons.md + cenum value: 32 { + TRR_UNSET = 0, + TRR_OK = 1, // Only set when we actually got a positive TRR result + TRR_NO_GSERVICE = 2, // no gService + TRR_PARENTAL_CONTROL = 3, // parental control is on + TRR_OFF_EXPLICIT = 4, // user has set mode5 + TRR_REQ_MODE_DISABLED = 5, // request has disabled flags set + TRR_MODE_NOT_ENABLED = 6, // mode0 + TRR_FAILED = 7, // unknown failure + TRR_MODE_UNHANDLED_DEFAULT = 8, // Unhandled case in ComputeEffectiveMode + TRR_MODE_UNHANDLED_DISABLED = 9, // Unhandled case in ComputeEffectiveMode + TRR_DISABLED_FLAG = 10, // the DISABLE_TRR flag was set + TRR_TIMEOUT = 11, // the TRR channel timed out + TRR_CHANNEL_DNS_FAIL = 12, // DoH server name failed to resolve + TRR_IS_OFFLINE = 13, // The browser is offline/no interfaces up + TRR_NOT_CONFIRMED = 14, // TRR confirmation is not done yet + TRR_DID_NOT_MAKE_QUERY = 15, // TrrLookup exited without doing a TRR query + TRR_UNKNOWN_CHANNEL_FAILURE = 16, // unknown channel failure reason + TRR_HOST_BLOCKED_TEMPORARY = 17, // host blocklisted + TRR_SEND_FAILED = 18, // The call to TRR::SendHTTPRequest failed + TRR_NET_RESET = 19, // NS_ERROR_NET_RESET + TRR_NET_TIMEOUT = 20, // NS_ERROR_NET_TIMEOUT + TRR_NET_REFUSED = 21, // NS_ERROR_CONNECTION_REFUSED + TRR_NET_INTERRUPT = 22, // NS_ERROR_NET_INTERRUPT + TRR_NET_INADEQ_SEQURITY = 23, // NS_ERROR_NET_INADEQUATE_SECURITY + TRR_NO_ANSWERS = 24, // TRR returned no answers + TRR_DECODE_FAILED = 25, // DohDecode failed + TRR_EXCLUDED = 26, // ExcludedFromTRR + TRR_SERVER_RESPONSE_ERR = 27, // Server responded with non-200 code + TRR_RCODE_FAIL = 28, // DNS response contains a non-NOERROR rcode + TRR_NO_CONNECTIVITY = 29, // Not confirmed because of no connectivity + TRR_NXDOMAIN = 30, // DNS response contains NXDOMAIN rcode (0x03) + TRR_REQ_CANCELLED = 31, // The request has been cancelled + ODOH_KEY_NOT_USABLE = 32, // We don't have a valid ODoHConfig to use. + ODOH_UPDATE_KEY_FAILED = 33, // Failed to update the ODoHConfigs. + ODOH_KEY_NOT_AVAILABLE = 34, // ODoH requests timeout because of no key. + ODOH_ENCRYPTION_FAILED = 35, // Failed to encrypt DNS packets. + ODOH_DECRYPTION_FAILED = 36, // Failed to decrypt DNS packets. + TRR_HEURISTIC_TRIPPED_GOOGLE_SAFESEARCH = 37, // The google safesearch heuristic was tripped + TRR_HEURISTIC_TRIPPED_YOUTUBE_SAFESEARCH = 38, // The youtube safesearch heuristic was tripped + TRR_HEURISTIC_TRIPPED_ZSCALER_CANARY = 39, // The zscaler canary heuristic was tripped + TRR_HEURISTIC_TRIPPED_CANARY = 40, // The global canary heuristic was tripped + TRR_HEURISTIC_TRIPPED_MODIFIED_ROOTS = 41, // The modified roots (enterprise_roots cert pref) heuristic was tripped + TRR_HEURISTIC_TRIPPED_PARENTAL_CONTROLS = 42, // The parental controls heuristic was tripped + TRR_HEURISTIC_TRIPPED_THIRD_PARTY_ROOTS = 43, // The third party roots heuristic was tripped + TRR_HEURISTIC_TRIPPED_ENTERPRISE_POLICY = 44, // The enterprise policy heuristic was tripped + TRR_HEURISTIC_TRIPPED_VPN = 45, // The heuristic was tripped due to a vpn being detected + TRR_HEURISTIC_TRIPPED_PROXY = 46, // The heuristic was tripped due to a proxy being detected + TRR_HEURISTIC_TRIPPED_NRPT = 47, // The heuristic was tripped due to a NRPT being detected + TRR_BAD_URL = 48, // We attempted to use a bad URL (doesn't parse or is not https). + }; +}; + +%{ C++ +namespace mozilla { +namespace net { + +using TRRSkippedReason = nsITRRSkipReason::value; + +inline bool IsRelevantTRRSkipReason(TRRSkippedReason aReason) { + // - TRR_REQ_MODE_DISABLED - these requests are intentionally skipping TRR. + // These include DNS queries used to bootstrap the TRR connection, + // captive portal checks, connectivity checks, etc. + // Since we don't want to use TRR for these connections, we don't need + // to include them with other relevant skip reasons. + // - TRR_DISABLED_FLAG - This reason is used when retrying failed connections, + // sync DNS resolves on the main thread, or requests coming from + // webextensions that choose to skip TRR + // - TRR_EXCLUDED - This reason is used when a certain domain is excluded + // from TRR because it is explicitly set by the user, or because it + // is part of the user's DNS suffix list, indicating a host that is likely + // to be on the local network. + if (aReason == TRRSkippedReason::TRR_REQ_MODE_DISABLED || + aReason == TRRSkippedReason::TRR_DISABLED_FLAG || + aReason == TRRSkippedReason::TRR_EXCLUDED) { + return false; + } + return true; +} + +inline bool IsBlockedTRRRequest(TRRSkippedReason aReason) { + // See TRR::MaybeBlockRequest. These are the reasons that could block sending + // TRR requests. + return (aReason == TRRSkippedReason::TRR_EXCLUDED || + aReason == TRRSkippedReason::TRR_MODE_NOT_ENABLED || + aReason == TRRSkippedReason::TRR_HOST_BLOCKED_TEMPORARY); +} + +inline bool IsNonRecoverableTRRSkipReason(TRRSkippedReason aReason) { + // These are non-recoverable reasons and we'll fallback to native without + // retrying. + return (aReason == TRRSkippedReason::TRR_NXDOMAIN || + aReason == TRRSkippedReason::TRR_NO_ANSWERS || + aReason == TRRSkippedReason::TRR_DISABLED_FLAG || + aReason == TRRSkippedReason::TRR_RCODE_FAIL); +} + +inline bool IsFailedConfirmationOrNoConnectivity(TRRSkippedReason aReason) { + // TRR is in non-confirmed state now, so we don't try to use TRR at all. + return (aReason == TRRSkippedReason::TRR_NOT_CONFIRMED || + aReason == TRRSkippedReason::TRR_NO_CONNECTIVITY); +} + +extern nsresult GetTRRSkipReasonName(TRRSkippedReason aReason, nsACString& aName); + +} // net +} // mozilla +%} diff --git a/netwerk/dns/nsPIDNSService.idl b/netwerk/dns/nsPIDNSService.idl new file mode 100644 index 0000000000..6d65e6be41 --- /dev/null +++ b/netwerk/dns/nsPIDNSService.idl @@ -0,0 +1,34 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsIDNSService.idl" + +/** + * This is a private interface used by the internals of the networking library. + * It will never be frozen. Do not use it in external code. + */ +[scriptable, builtinclass, uuid(24e598fd-7b1a-436c-9154-14d8b38df8a5)] +interface nsPIDNSService : nsIDNSService +{ + /** + * called to initialize the DNS service. + */ + void init(); + + /** + * called to shutdown the DNS service. any pending asynchronous + * requests will be canceled, and the local cache of DNS records + * will be cleared. NOTE: the operating system may still have + * its own cache of DNS records, which would be unaffected by + * this method. + */ + void shutdown(); + + /** + * Whether or not DNS prefetching (aka RESOLVE_SPECULATE) is enabled + */ + attribute boolean prefetchEnabled; +}; diff --git a/netwerk/dns/prepare_tlds.py b/netwerk/dns/prepare_tlds.py new file mode 100644 index 0000000000..adebcec487 --- /dev/null +++ b/netwerk/dns/prepare_tlds.py @@ -0,0 +1,150 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import codecs +import encodings.idna +import re +import sys + +from make_dafsa import words_to_bin, words_to_cxx + +""" +Processes a file containing effective TLD data. See the following URL for a +description of effective TLDs and of the file format that this script +processes (although for the latter you're better off just reading this file's +short source code). + +http://wiki.mozilla.org/Gecko:Effective_TLD_Service +""" + + +def getEffectiveTLDs(path): + file = codecs.open(path, "r", "UTF-8") + domains = set() + for line in file: + # line always contains a line terminator unless the file is empty + if len(line) == 0: + raise StopIteration + line = line.rstrip() + # comment, empty, or superfluous line for explicitness purposes + if line.startswith("//") or not line.strip(): + continue + line = re.split(r"[ \t\n]", line, 1)[0] + entry = EffectiveTLDEntry(line) + domain = entry.domain() + assert domain not in domains, "repeating domain %s makes no sense" % domain + domains.add(domain) + yield entry + + +def _normalizeHostname(domain): + """ + Normalizes the given domain, component by component. ASCII components are + lowercased, while non-ASCII components are processed using the ToASCII + algorithm. + """ + + def convertLabel(label): + if _isASCII(label): + return label.lower() + return encodings.idna.ToASCII(label).decode("utf-8") + + return ".".join(map(convertLabel, domain.split("."))) + + +def _isASCII(s): + "True if s consists entirely of ASCII characters, false otherwise." + for c in s: + if ord(c) > 127: + return False + return True + + +class EffectiveTLDEntry: + """ + Stores an entry in an effective-TLD name file. + """ + + _exception = False + _wild = False + + def __init__(self, line): + """ + Creates a TLD entry from a line of data, which must have been stripped of + the line ending. + """ + if line.startswith("!"): + self._exception = True + domain = line[1:] + elif line.startswith("*."): + self._wild = True + domain = line[2:] + else: + domain = line + self._domain = _normalizeHostname(domain) + + def domain(self): + "The domain this represents." + return self._domain + + def exception(self): + "True if this entry's domain denotes does not denote an effective TLD." + return self._exception + + def wild(self): + "True if this entry represents a class of effective TLDs." + return self._wild + + +################# +# DO EVERYTHING # +################# + + +def main(output, effective_tld_filename, output_format="cxx"): + """ + effective_tld_filename is the effective TLD file to parse. + based on the output format, either a C++ array of a binary representation + of a DAFSA representing the eTLD file is then printed to standard output + or a binary file is written to disk. + """ + + def typeEnum(etld): + """ + Maps the flags to the DAFSA's enum types. + """ + if etld.exception(): + return 1 + elif etld.wild(): + return 2 + else: + return 0 + + def dafsa_words(): + """ + make_dafsa expects lines of the form "<domain_name><enum_value>" + """ + for etld in getEffectiveTLDs(effective_tld_filename): + yield "%s%d" % (etld.domain(), typeEnum(etld)) + + """ words_to_bin() returns a bytes while words_to_cxx() returns string """ + if output_format == "bin": + output.write(words_to_bin(dafsa_words())) + else: + output.write(words_to_cxx(dafsa_words())) + + +if __name__ == "__main__": + """ + This program can output the DAFSA in two formats: + as C++ code that will be included and compiled at build time + or as a binary file that will be published in Remote Settings. + + Flags for format options: + "cxx" -> C++ array [default] + "bin" -> Binary file + """ + + output_format = "bin" if "--bin" in sys.argv else "cxx" + main(sys.stdout, sys.argv[1], output_format=output_format) diff --git a/netwerk/dns/punycode.c b/netwerk/dns/punycode.c new file mode 100644 index 0000000000..4653216507 --- /dev/null +++ b/netwerk/dns/punycode.c @@ -0,0 +1,325 @@ +/* +punycode.c from RFC 3492 +http://www.nicemice.net/idn/ +Adam M. Costello +http://www.nicemice.net/amc/ + +This is ANSI C code (C89) implementing Punycode (RFC 3492). + + +C. Disclaimer and license + + Regarding this entire document or any portion of it (including + the pseudocode and C code), the author makes no guarantees and + is not responsible for any damage resulting from its use. The + author grants irrevocable permission to anyone to use, modify, + and distribute it in any way that does not diminish the rights + of anyone else to use, modify, and distribute it, provided that + redistributed derivative works do not contain misleading author or + version information. Derivative works need not be licensed under + similar terms. +*/ + +#include "punycode.h" + +/**********************************************************/ +/* Implementation (would normally go in its own .c file): */ + +#include <string.h> + +/*** Bootstring parameters for Punycode ***/ + +enum { + base = 36, + tmin = 1, + tmax = 26, + skew = 38, + damp = 700, + initial_bias = 72, + initial_n = 0x80, + delimiter = 0x2D +}; + +/* basic(cp) tests whether cp is a basic code point: */ +#define basic(cp) ((punycode_uint)(cp) < 0x80) + +/* delim(cp) tests whether cp is a delimiter: */ +#define delim(cp) ((cp) == delimiter) + +/* decode_digit(cp) returns the numeric value of a basic code */ +/* point (for use in representing integers) in the range 0 to */ +/* base-1, or base if cp is does not represent a value. */ + +static punycode_uint decode_digit(punycode_uint cp) { + return cp - 48 < 10 ? cp - 22 + : cp - 65 < 26 ? cp - 65 + : cp - 97 < 26 ? cp - 97 + : base; +} + +/* encode_digit(d,flag) returns the basic code point whose value */ +/* (when used for representing integers) is d, which needs to be in */ +/* the range 0 to base-1. The lowercase form is used unless flag is */ +/* nonzero, in which case the uppercase form is used. The behavior */ +/* is undefined if flag is nonzero and digit d has no uppercase form. */ + +static char encode_digit(punycode_uint d, int flag) { + return d + 22 + 75 * (d < 26) - ((flag != 0) << 5); + /* 0..25 map to ASCII a..z or A..Z */ + /* 26..35 map to ASCII 0..9 */ +} + +/* flagged(bcp) tests whether a basic code point is flagged */ +/* (uppercase). The behavior is undefined if bcp is not a */ +/* basic code point. */ + +#define flagged(bcp) ((punycode_uint)(bcp)-65 < 26) + +/* encode_basic(bcp,flag) forces a basic code point to lowercase */ +/* if flag is zero, uppercase if flag is nonzero, and returns */ +/* the resulting code point. The code point is unchanged if it */ +/* is caseless. The behavior is undefined if bcp is not a basic */ +/* code point. */ + +static char encode_basic(punycode_uint bcp, int flag) { + bcp -= (bcp - 97 < 26) << 5; + return bcp + ((!flag && (bcp - 65 < 26)) << 5); +} + +/*** Platform-specific constants ***/ + +/* maxint is the maximum value of a punycode_uint variable: */ +static const punycode_uint maxint = (punycode_uint)-1; +/* Because maxint is unsigned, -1 becomes the maximum value. */ + +/*** Bias adaptation function ***/ + +static punycode_uint adapt(punycode_uint delta, punycode_uint numpoints, + int firsttime) { + punycode_uint k; + + delta = firsttime ? delta / damp : delta >> 1; + /* delta >> 1 is a faster way of doing delta / 2 */ + delta += delta / numpoints; + + for (k = 0; delta > ((base - tmin) * tmax) / 2; k += base) { + delta /= base - tmin; + } + + return k + (base - tmin + 1) * delta / (delta + skew); +} + +/*** Main encode function ***/ + +enum punycode_status punycode_encode(punycode_uint input_length, + const punycode_uint input[], + const unsigned char case_flags[], + punycode_uint* output_length, + char output[]) { + punycode_uint n, delta, h, b, out, max_out, bias, j, m, q, k, t; + + /* Initialize the state: */ + + n = initial_n; + delta = out = 0; + max_out = *output_length; + bias = initial_bias; + + /* Handle the basic code points: */ + + for (j = 0; j < input_length; ++j) { + if (basic(input[j])) { + if (max_out - out < 2) { + return punycode_big_output; + } + output[out++] = + case_flags ? encode_basic(input[j], case_flags[j]) : (char)input[j]; + } + /* else if (input[j] < n) return punycode_bad_input; */ + /* (not needed for Punycode with unsigned code points) */ + } + + h = b = out; + + /* h is the number of code points that have been handled, b is the */ + /* number of basic code points, and out is the number of characters */ + /* that have been output. */ + + if (b > 0) { + output[out++] = delimiter; + } + + /* Main encoding loop: */ + + while (h < input_length) { + /* All non-basic code points < n have been */ + /* handled already. Find the next larger one: */ + + for (m = maxint, j = 0; j < input_length; ++j) { + /* if (basic(input[j])) continue; */ + /* (not needed for Punycode) */ + if (input[j] >= n && input[j] < m) { + m = input[j]; + } + } + + /* Increase delta enough to advance the decoder's */ + /* <n,i> state to <m,0>, but guard against overflow: */ + + if (m - n > (maxint - delta) / (h + 1)) { + return punycode_overflow; + } + delta += (m - n) * (h + 1); + n = m; + + for (j = 0; j < input_length; ++j) { + /* Punycode does not need to check whether input[j] is basic: */ + if (input[j] < n /* || basic(input[j]) */) { + if (++delta == 0) { + return punycode_overflow; + } + } + + if (input[j] == n) { + /* Represent delta as a generalized variable-length integer: */ + + for (q = delta, k = base;; k += base) { + if (out >= max_out) { + return punycode_big_output; + } + t = k <= bias /* + tmin */ ? tmin : /* +tmin not needed */ + k >= bias + tmax ? tmax + : k - bias; + if (q < t) { + break; + } + output[out++] = encode_digit(t + (q - t) % (base - t), 0); + q = (q - t) / (base - t); + } + + output[out++] = encode_digit(q, case_flags && case_flags[j]); + bias = adapt(delta, h + 1, h == b); + delta = 0; + ++h; + } + } + + ++delta, ++n; + } + + *output_length = out; + return punycode_success; +} + +/*** Main decode function ***/ + +enum punycode_status punycode_decode(punycode_uint input_length, + const char input[], + punycode_uint* output_length, + punycode_uint output[], + unsigned char case_flags[]) { + punycode_uint n, out, i, max_out, bias, b, j, in, oldi, w, k, digit, t; + + if (!input_length) { + return punycode_bad_input; + } + + /* Initialize the state: */ + + n = initial_n; + out = i = 0; + max_out = *output_length; + bias = initial_bias; + + /* Handle the basic code points: Let b be the number of input code */ + /* points before the last delimiter, or 0 if there is none, then */ + /* copy the first b code points to the output. */ + + for (b = 0, j = input_length - 1; j > 0; --j) { + if (delim(input[j])) { + b = j; + break; + } + } + if (b > max_out) { + return punycode_big_output; + } + + for (j = 0; j < b; ++j) { + if (case_flags) { + case_flags[out] = flagged(input[j]); + } + if (!basic(input[j])) { + return punycode_bad_input; + } + output[out++] = input[j]; + } + + /* Main decoding loop: Start just after the last delimiter if any */ + /* basic code points were copied; start at the beginning otherwise. */ + + for (in = b > 0 ? b + 1 : 0; in < input_length; ++out) { + /* in is the index of the next character to be consumed, and */ + /* out is the number of code points in the output array. */ + + /* Decode a generalized variable-length integer into delta, */ + /* which gets added to i. The overflow checking is easier */ + /* if we increase i as we go, then subtract off its starting */ + /* value at the end to obtain delta. */ + + for (oldi = i, w = 1, k = base;; k += base) { + if (in >= input_length) { + return punycode_bad_input; + } + digit = decode_digit(input[in++]); + if (digit >= base) { + return punycode_bad_input; + } + if (digit > (maxint - i) / w) { + return punycode_overflow; + } + i += digit * w; + t = k <= bias /* + tmin */ ? tmin : /* +tmin not needed */ + k >= bias + tmax ? tmax + : k - bias; + if (digit < t) { + break; + } + if (w > maxint / (base - t)) { + return punycode_overflow; + } + w *= (base - t); + } + + bias = adapt(i - oldi, out + 1, oldi == 0); + + /* i was supposed to wrap around from out+1 to 0, */ + /* incrementing n each time, so we'll fix that now: */ + + if (i / (out + 1) > maxint - n) { + return punycode_overflow; + } + n += i / (out + 1); + i %= (out + 1); + + /* Insert n at position i of the output: */ + + /* not needed for Punycode: */ + /* if (decode_digit(n) <= base) return punycode_invalid_input; */ + if (out >= max_out) { + return punycode_big_output; + } + + if (case_flags) { + memmove(case_flags + i + 1, case_flags + i, out - i); + /* Case of last character determines uppercase flag: */ + case_flags[i] = flagged(input[in - 1]); + } + + memmove(output + i + 1, output + i, (out - i) * sizeof *output); + output[i++] = n; + } + + *output_length = out; + return punycode_success; +} diff --git a/netwerk/dns/punycode.h b/netwerk/dns/punycode.h new file mode 100644 index 0000000000..6a626c7a07 --- /dev/null +++ b/netwerk/dns/punycode.h @@ -0,0 +1,106 @@ +/* +punycode.c from RFC 3492 +http://www.nicemice.net/idn/ +Adam M. Costello +http://www.nicemice.net/amc/ + +This is ANSI C code (C89) implementing Punycode (RFC 3492). + + + +C. Disclaimer and license + + Regarding this entire document or any portion of it (including + the pseudocode and C code), the author makes no guarantees and + is not responsible for any damage resulting from its use. The + author grants irrevocable permission to anyone to use, modify, + and distribute it in any way that does not diminish the rights + of anyone else to use, modify, and distribute it, provided that + redistributed derivative works do not contain misleading author or + version information. Derivative works need not be licensed under + similar terms. +*/ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/************************************************************/ +/* Public interface (would normally go in its own .h file): */ + +#include <limits.h> + +enum punycode_status { + punycode_success, + punycode_bad_input, /* Input is invalid. */ + punycode_big_output, /* Output would exceed the space provided. */ + punycode_overflow /* Input needs wider integers to process. */ +}; + +#if UINT_MAX >= (1 << 26) - 1 +typedef unsigned int punycode_uint; +#else +typedef unsigned long punycode_uint; +#endif + +enum punycode_status punycode_encode(punycode_uint input_length, + const punycode_uint input[], + const unsigned char case_flags[], + punycode_uint* output_length, + char output[]); + +/* punycode_encode() converts Unicode to Punycode. The input */ +/* is represented as an array of Unicode code points (not code */ +/* units; surrogate pairs are not allowed), and the output */ +/* will be represented as an array of ASCII code points. The */ +/* output string is *not* null-terminated; it will contain */ +/* zeros if and only if the input contains zeros. (Of course */ +/* the caller can leave room for a terminator and add one if */ +/* needed.) The input_length is the number of code points in */ +/* the input. The output_length is an in/out argument: the */ +/* caller passes in the maximum number of code points that it */ +/* can receive, and on successful return it will contain the */ +/* number of code points actually output. The case_flags array */ +/* holds input_length boolean values, where nonzero suggests that */ +/* the corresponding Unicode character be forced to uppercase */ +/* after being decoded (if possible), and zero suggests that */ +/* it be forced to lowercase (if possible). ASCII code points */ +/* are encoded literally, except that ASCII letters are forced */ +/* to uppercase or lowercase according to the corresponding */ +/* uppercase flags. If case_flags is a null pointer then ASCII */ +/* letters are left as they are, and other code points are */ +/* treated as if their uppercase flags were zero. The return */ +/* value can be any of the punycode_status values defined above */ +/* except punycode_bad_input; if not punycode_success, then */ +/* output_size and output might contain garbage. */ + +enum punycode_status punycode_decode(punycode_uint input_length, + const char input[], + punycode_uint* output_length, + punycode_uint output[], + unsigned char case_flags[]); + +/* punycode_decode() converts Punycode to Unicode. The input is */ +/* represented as an array of ASCII code points, and the output */ +/* will be represented as an array of Unicode code points. The */ +/* input_length is the number of code points in the input. The */ +/* output_length is an in/out argument: the caller passes in */ +/* the maximum number of code points that it can receive, and */ +/* on successful return it will contain the actual number of */ +/* code points output. The case_flags array needs room for at */ +/* least output_length values, or it can be a null pointer if the */ +/* case information is not needed. A nonzero flag suggests that */ +/* the corresponding Unicode character be forced to uppercase */ +/* by the caller (if possible), while zero suggests that it be */ +/* forced to lowercase (if possible). ASCII code points are */ +/* output already in the proper case, but their flags will be set */ +/* appropriately so that applying the flags would be harmless. */ +/* The return value can be any of the punycode_status values */ +/* defined above; if not punycode_success, then output_length, */ +/* output, and case_flags might contain garbage. On success, the */ +/* decoder will never need to write an output_length greater than */ +/* input_length, because of how the encoding is defined. */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/netwerk/dns/tests/moz.build b/netwerk/dns/tests/moz.build new file mode 100644 index 0000000000..fb176c1bd3 --- /dev/null +++ b/netwerk/dns/tests/moz.build @@ -0,0 +1,7 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DIRS += ["unit"] diff --git a/netwerk/dns/tests/unit/data/fake_public_suffix_list.dat b/netwerk/dns/tests/unit/data/fake_public_suffix_list.dat new file mode 100644 index 0000000000..ca1da32eeb --- /dev/null +++ b/netwerk/dns/tests/unit/data/fake_public_suffix_list.dat @@ -0,0 +1,57 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +// This is a fake suffix list created only for the purposes of testing, +// The real PSL is rather large thus this file is kept small by cutting out most of the suffixes + +// The Original list can be found at https://publicsuffix.org/list/public_suffix_list.dat, +// Learn more about the PSL at https://publicsuffix.org. + +// .xpcshelltest is the fake domain created specifically for the the tests while +// the others are ripped from the real list, serving as examples. + +// This fake public suffix list was used to create the binary file fake_remote_dafsa.bin +// The binary is built at compile time and can be found at +// obj-dir/_tests/xpcshell/netwerk/dns/tests/unit/data/ + +// The list created with help of netwerk/dns/prepare_tlds.py and xpcom/ds/tools/make_dafsa.py +// The build directive for the binary is at moz.build at netwerk/dns/tests/unit/data/ + + + +// ===BEGIN ICANN DOMAINS=== + +// xpcshelltest : Used in tests +xpcshelltest +website.xpcshelltest +com.xpcshelltest +edu.xpcshelltest +gov.xpcshelltest +net.xpcshelltest +mil.xpcshelltest +org.xpcshelltest + +// ac : https://en.wikipedia.org/wiki/.ac +ac +coc.ac +com.ac +edu.ac +gov.ac +net.ac +mil.ac +org.ac + +// bj : https://en.wikipedia.org/wiki/.bj +bj +asso.bj +barreau.bj +gouv.bj + +// bm : http://www.bermudanic.bm/dnr-text.txt +bm +com.bm +edu.bm +gov.bm +net.bm +org.bm diff --git a/netwerk/dns/tests/unit/data/moz.build b/netwerk/dns/tests/unit/data/moz.build new file mode 100644 index 0000000000..0f5a164f77 --- /dev/null +++ b/netwerk/dns/tests/unit/data/moz.build @@ -0,0 +1,14 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +GeneratedFile( + "fake_remote_dafsa.bin", + script="../../../prepare_tlds.py", + inputs=["fake_public_suffix_list.dat"], + flags=["bin"], +) + +TEST_HARNESS_FILES.xpcshell.netwerk.dns.tests.unit.data += ["!fake_remote_dafsa.bin"] diff --git a/netwerk/dns/tests/unit/moz.build b/netwerk/dns/tests/unit/moz.build new file mode 100644 index 0000000000..e5ce568e70 --- /dev/null +++ b/netwerk/dns/tests/unit/moz.build @@ -0,0 +1,7 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DIRS += ["data"] diff --git a/netwerk/dns/tests/unit/test_PublicSuffixList.js b/netwerk/dns/tests/unit/test_PublicSuffixList.js new file mode 100644 index 0000000000..ad61abea8e --- /dev/null +++ b/netwerk/dns/tests/unit/test_PublicSuffixList.js @@ -0,0 +1,174 @@ +/* Any copyright is dedicated to the Public Domain. + * https://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { PublicSuffixList } = ChromeUtils.importESModule( + "resource://gre/modules/netwerk-dns/PublicSuffixList.sys.mjs" +); +const { TestUtils } = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); + +const CLIENT = PublicSuffixList.CLIENT; +const SIGNAL = "public-suffix-list-updated"; + +const PAYLOAD_UPDATED_RECORDS = { + current: [{ id: "tld-dafsa", "commit-hash": "current-commit-hash" }], + created: [], + updated: [ + { + old: { id: "tld-dafsa", "commit-hash": "current-commit-hash" }, + new: { id: "tld-dafsa", "commit-hash": "new-commit-hash" }, + }, + ], + deleted: [], +}; +const PAYLOAD_CREATED_RECORDS = { + current: [], + created: [ + { + id: "tld-dafsa", + "commit-hash": "new-commit-hash", + attachment: {}, + }, + ], + updated: [], + deleted: [], +}; +const PAYLOAD_UPDATED_AND_CREATED_RECORDS = { + current: [{ id: "tld-dafsa", "commit-hash": "current-commit-hash" }], + created: [{ id: "tld-dafsa", "commit-hash": "another-commit-hash" }], + updated: [ + { + old: { id: "tld-dafsa", "commit-hash": "current-commit-hash" }, + new: { id: "tld-dafsa", "commit-hash": "new-commit-hash" }, + }, + ], + deleted: [], +}; + +const fakeDafsaBinFile = do_get_file("data/fake_remote_dafsa.bin"); +const mockedFilePath = fakeDafsaBinFile.path; + +function setup() { + Services.prefs.setBoolPref("network.psl.onUpdate_notify", true); +} +setup(); +registerCleanupFunction(() => { + Services.prefs.clearUserPref("network.psl.onUpdate_notify"); +}); + +/** + * downloadToDiskCalled is used by mockDownloadToDisk() and resetMockDownloadToDisk() + * to keep track weather CLIENT.attachments.download is called or not + * downloadToDiskBackup will help restore CLIENT.attachments.download to original definition + * notifyUpdateBackup will help restore PublicSuffixList.notifyUpdate to original definition + */ +let downloadToDiskCalled = false; +const downloadToDiskBackup = CLIENT.attachments.downloadToDisk; + +// returns a fake fileURI and sends a signal with filePath and no nsifile +function mockDownloadToDisk() { + downloadToDiskCalled = false; + CLIENT.attachments.downloadToDisk = async rec => { + downloadToDiskCalled = true; + return `file://${mockedFilePath}`; // Create a fake file URI + }; +} + +// resetMockDownloadToDisk() must be run at the end of the test that uses mockDownloadToDisk() +const resetMockDownloadToDisk = () => { + CLIENT.attachments.downloadToDisk = downloadToDiskBackup; +}; + +add_task(async () => { + info("File path sent when record is in DB."); + + await CLIENT.db.clear(); // Make sure there's no record initially + await CLIENT.db.create({ + id: "tld-dafsa", + "commit-hash": "fake-commit-hash", + attachment: {}, + }); + + mockDownloadToDisk(); + + const promiseSignal = TestUtils.topicObserved(SIGNAL); + await PublicSuffixList.init(); + const observed = await promiseSignal; + + Assert.equal( + observed[1], + mockedFilePath, + "File path sent when record is in DB." + ); + await CLIENT.db.clear(); // Clean up the mockDownloaded record + resetMockDownloadToDisk(); +}); + +add_task(async () => { + info("File path sent when record updated."); + + mockDownloadToDisk(); + + const promiseSignal = TestUtils.topicObserved(SIGNAL); + await PublicSuffixList.init(); + await CLIENT.emit("sync", { data: PAYLOAD_UPDATED_RECORDS }); + const observed = await promiseSignal; + + Assert.equal( + observed[1], + mockedFilePath, + "File path sent when record updated." + ); + resetMockDownloadToDisk(); +}); + +add_task(async () => { + info("Attachment downloaded when record created."); + + mockDownloadToDisk(); + + await PublicSuffixList.init(); + await CLIENT.emit("sync", { data: PAYLOAD_CREATED_RECORDS }); + + Assert.equal( + downloadToDiskCalled, + true, + "Attachment downloaded when record created." + ); + resetMockDownloadToDisk(); +}); + +add_task(async () => { + info("Attachment downloaded when record updated."); + + mockDownloadToDisk(); + + await PublicSuffixList.init(); + await CLIENT.emit("sync", { data: PAYLOAD_UPDATED_RECORDS }); + + Assert.equal( + downloadToDiskCalled, + true, + "Attachment downloaded when record updated." + ); + resetMockDownloadToDisk(); +}); + +add_task(async () => { + info("No download when more than one record is changed."); + + mockDownloadToDisk(); + + await PublicSuffixList.init(); + await CLIENT.emit("sync", { data: PAYLOAD_UPDATED_AND_CREATED_RECORDS }); + + Assert.equal( + downloadToDiskCalled, + false, + "No download when more than one record is changed." + ); + resetMockDownloadToDisk(); +}); diff --git a/netwerk/dns/tests/unit/test_nsEffectiveTLDService_Reload_DAFSA.js b/netwerk/dns/tests/unit/test_nsEffectiveTLDService_Reload_DAFSA.js new file mode 100644 index 0000000000..6b89b070d5 --- /dev/null +++ b/netwerk/dns/tests/unit/test_nsEffectiveTLDService_Reload_DAFSA.js @@ -0,0 +1,32 @@ +/* Any copyright is dedicated to the Public Domain. + * https://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const SIGNAL = "public-suffix-list-updated"; + +add_task(async () => { + info("Before fake dafsa reload."); + + let suffix = Services.eTLD.getPublicSuffixFromHost("website.xpcshelltest"); + Assert.equal( + suffix, + "xpcshelltest", + "Fake Suffix does not exist in current PSL." + ); +}); + +add_task(async () => { + info("After fake dafsa reload."); + + // reload the PSL with fake data containing .xpcshelltest + const fakeDafsaFile = do_get_file("data/fake_remote_dafsa.bin"); + Services.obs.notifyObservers(fakeDafsaFile, SIGNAL, fakeDafsaFile.path); + + let suffix = Services.eTLD.getPublicSuffixFromHost("website.xpcshelltest"); + Assert.equal( + suffix, + "website.xpcshelltest", + "Fake Suffix now exists in PSL after DAFSA reload." + ); +}); diff --git a/netwerk/dns/tests/unit/test_nsEffectiveTLDService_getKnownPublicSuffix.js b/netwerk/dns/tests/unit/test_nsEffectiveTLDService_getKnownPublicSuffix.js new file mode 100644 index 0000000000..333692af97 --- /dev/null +++ b/netwerk/dns/tests/unit/test_nsEffectiveTLDService_getKnownPublicSuffix.js @@ -0,0 +1,44 @@ +/* Any copyright is dedicated to the Public Domain. + * https://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests getPublicSuffix with the validate argument. + */ + +"use strict"; + +add_task(() => { + for (let [suffix, isKnown] of [ + ["", false], + [null, false], + ["mozbacon", false], + ["com", true], + ["circle", true], + ["bd", true], + ["gov.bd", true], + ["ck", true], + ["www.ck", true], + ["bs", true], + ["com.bs", true], + ["網絡.cn", true], + ["valléedaoste.it", true], + ["aurskog-høland.no", true], + ["公司.香港", true], + ["भारतम्", true], + ["فلسطين", true], + ]) { + let origin = "test." + suffix; + Assert.equal( + !!Services.eTLD.getKnownPublicSuffixFromHost(origin), + isKnown, + `"${suffix}" should ${isKnown ? " " : "not "}be a known public suffix` + ); + Assert.equal( + !!Services.eTLD.getKnownPublicSuffix( + Services.io.newURI("http://" + origin) + ), + isKnown, + `"${suffix}" should ${isKnown ? " " : "not "}be a known public suffix` + ); + } +}); diff --git a/netwerk/dns/tests/unit/test_nsEffectiveTLDService_getSite.js b/netwerk/dns/tests/unit/test_nsEffectiveTLDService_getSite.js new file mode 100644 index 0000000000..5f6f3b1eb3 --- /dev/null +++ b/netwerk/dns/tests/unit/test_nsEffectiveTLDService_getSite.js @@ -0,0 +1,77 @@ +/* Any copyright is dedicated to the Public Domain. + * https://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests getSite and getSchemelessSite with example arguments + */ + +"use strict"; + +add_task(() => { + for (let [originString, result] of [ + ["http://.", null], + ["http://com", "http://com"], + ["http://test", "http://test"], + ["http://test.", "http://test."], + ["http://[::1]", "http://[::1]"], + ["http://[::1]:8888", "http://[::1]"], + ["http://localhost", "http://localhost"], + ["http://127.0.0.1", "http://127.0.0.1"], + ["http://user:pass@[::1]", "http://[::1]"], + ["http://example.com", "http://example.com"], + ["https://example.com", "https://example.com"], + ["https://test.example.com", "https://example.com"], + ["https://test1.test2.example.com", "https://example.com"], + ["https://test1.test2.example.co.uk", "https://example.co.uk"], + ["https://test.example.com:8888/index.html", "https://example.com"], + [ + "https://test1.test2.example.公司.香港", + "https://example.xn--55qx5d.xn--j6w193g", + ], + ]) { + let origin = Services.io.newURI(originString); + if (result === null) { + Assert.throws( + () => { + Services.eTLD.getSite(origin); + }, + /NS_ERROR_ILLEGAL_VALUE/, + "Invalid origin for getSite throws" + ); + } else { + let answer = Services.eTLD.getSite(origin); + Assert.equal( + answer, + result, + `"${originString}" should have site ${result}, got ${answer}.` + ); + } + } +}); + +add_task(() => { + for (let [originString, result] of [ + ["http://com", "com"], + ["http://test", "test"], + ["http://test.", "test."], + ["http://[::1]", "[::1]"], + ["http://[::1]:8888", "[::1]"], + ["http://localhost", "localhost"], + ["http://127.0.0.1", "127.0.0.1"], + ["http://user:pass@[::1]", "[::1]"], + ["http://example.com", "example.com"], + ["https://example.com", "example.com"], + ["https://test.example.com", "example.com"], + ["https://test1.test2.example.com", "example.com"], + ["https://test1.test2.example.co.uk", "example.co.uk"], + ["https://test1.test2.example.公司.香港", "example.xn--55qx5d.xn--j6w193g"], + ]) { + let origin = Services.io.newURI(originString); + let answer = Services.eTLD.getSchemelessSite(origin); + Assert.equal( + answer, + result, + `"${originString}" should have schemeless site ${result}, got ${answer}.` + ); + } +}); diff --git a/netwerk/dns/tests/unit/xpcshell.toml b/netwerk/dns/tests/unit/xpcshell.toml new file mode 100644 index 0000000000..1047b7d496 --- /dev/null +++ b/netwerk/dns/tests/unit/xpcshell.toml @@ -0,0 +1,17 @@ +[DEFAULT] +head = "../../../../services/common/tests/unit/head_global.js ../../../../services/common/tests/unit/head_helpers.js" +firefox-appdir = "browser" +support-files = [ + "data/**", + "!/services/common/tests/unit/head_global.js", + "!/services/common/tests/unit/head_helpers.js", +] + +["test_PublicSuffixList.js"] +tags = "remote-settings" + +["test_nsEffectiveTLDService_Reload_DAFSA.js"] + +["test_nsEffectiveTLDService_getKnownPublicSuffix.js"] + +["test_nsEffectiveTLDService_getSite.js"] |