diff options
Diffstat (limited to 'netwerk/dns')
96 files changed, 34835 insertions, 0 deletions
diff --git a/netwerk/dns/ChildDNSService.cpp b/netwerk/dns/ChildDNSService.cpp new file mode 100644 index 0000000000..ebfb4aee26 --- /dev/null +++ b/netwerk/dns/ChildDNSService.cpp @@ -0,0 +1,450 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/net/ChildDNSService.h" +#include "nsDNSPrefetch.h" +#include "nsIDNSListener.h" +#include "nsIOService.h" +#include "nsThreadUtils.h" +#include "nsIXPConnect.h" +#include "nsIProtocolProxyService.h" +#include "nsNetCID.h" +#include "nsQueryObject.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/StaticPrefs_network.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/SyncRunnable.h" +#include "mozilla/net/NeckoChild.h" +#include "mozilla/net/DNSListenerProxy.h" +#include "mozilla/net/TRRServiceParent.h" +#include "nsHostResolver.h" +#include "nsServiceManagerUtils.h" +#include "prsystem.h" +#include "DNSResolverInfo.h" + +namespace mozilla { +namespace net { + +//----------------------------------------------------------------------------- +// ChildDNSService +//----------------------------------------------------------------------------- + +static StaticRefPtr<ChildDNSService> gChildDNSService; +static const char kPrefNameDisablePrefetch[] = "network.dns.disablePrefetch"; + +already_AddRefed<ChildDNSService> ChildDNSService::GetSingleton() { + MOZ_ASSERT_IF(nsIOService::UseSocketProcess(), + XRE_IsContentProcess() || XRE_IsParentProcess()); + MOZ_ASSERT_IF(!nsIOService::UseSocketProcess(), + XRE_IsContentProcess() || XRE_IsSocketProcess()); + + if (!gChildDNSService) { + if (!NS_IsMainThread()) { + return nullptr; + } + gChildDNSService = new ChildDNSService(); + ClearOnShutdown(&gChildDNSService); + } + + return do_AddRef(gChildDNSService); +} + +NS_IMPL_ISUPPORTS(ChildDNSService, nsIDNSService, nsPIDNSService, nsIObserver) + +ChildDNSService::ChildDNSService() + : mFirstTime(true), + mDisablePrefetch(false), + mPendingRequestsLock("DNSPendingRequestsLock") { + MOZ_ASSERT_IF(nsIOService::UseSocketProcess(), + XRE_IsContentProcess() || XRE_IsParentProcess()); + MOZ_ASSERT_IF(!nsIOService::UseSocketProcess(), + XRE_IsContentProcess() || XRE_IsSocketProcess()); + if (XRE_IsParentProcess() && nsIOService::UseSocketProcess()) { + nsDNSPrefetch::Initialize(this); + mTRRServiceParent = new TRRServiceParent(); + mTRRServiceParent->Init(); + } +} + +void ChildDNSService::GetDNSRecordHashKey( + const nsACString& aHost, const nsACString& aTrrServer, uint16_t aType, + const OriginAttributes& aOriginAttributes, uint32_t aFlags, + uintptr_t aListenerAddr, nsACString& aHashKey) { + aHashKey.Assign(aHost); + aHashKey.Assign(aTrrServer); + aHashKey.AppendInt(aType); + + nsAutoCString originSuffix; + aOriginAttributes.CreateSuffix(originSuffix); + aHashKey.Append(originSuffix); + + aHashKey.AppendInt(aFlags); + aHashKey.AppendPrintf("0x%" PRIxPTR, aListenerAddr); +} + +nsresult ChildDNSService::AsyncResolveInternal( + const nsACString& hostname, uint16_t type, uint32_t flags, + nsIDNSResolverInfo* aResolver, nsIDNSListener* listener, + nsIEventTarget* target_, const OriginAttributes& aOriginAttributes, + nsICancelable** result) { + if (XRE_IsContentProcess()) { + NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE); + } + + bool resolveDNSInSocketProcess = false; + if (XRE_IsParentProcess() && nsIOService::UseSocketProcess()) { + resolveDNSInSocketProcess = true; + if (type != nsIDNSService::RESOLVE_TYPE_DEFAULT && + (mTRRServiceParent->Mode() != MODE_TRRFIRST && + mTRRServiceParent->Mode() != MODE_TRRONLY)) { + return NS_ERROR_UNKNOWN_HOST; + } + } + + if (mDisablePrefetch && (flags & RESOLVE_SPECULATE)) { + return NS_ERROR_DNS_LOOKUP_QUEUE_FULL; + } + + // We need original listener for the pending requests hash. + uintptr_t originalListenerAddr = reinterpret_cast<uintptr_t>(listener); + + // make sure JS callers get notification on the main thread + nsCOMPtr<nsIEventTarget> target = target_; + nsCOMPtr<nsIXPConnectWrappedJS> wrappedListener = do_QueryInterface(listener); + if (wrappedListener && !target) { + target = GetMainThreadSerialEventTarget(); + } + if (target) { + // Guarantee listener freed on main thread. Not sure we need this in child + // (or in parent in nsDNSService.cpp) but doesn't hurt. + listener = new DNSListenerProxy(listener, target); + } + + RefPtr<DNSRequestSender> sender = + new DNSRequestSender(hostname, DNSResolverInfo::URL(aResolver), type, + aOriginAttributes, flags, listener, target); + RefPtr<DNSRequestActor> dnsReq; + if (resolveDNSInSocketProcess) { + dnsReq = new DNSRequestParent(sender); + } else { + dnsReq = new DNSRequestChild(sender); + } + + { + MutexAutoLock lock(mPendingRequestsLock); + nsCString key; + GetDNSRecordHashKey(hostname, DNSResolverInfo::URL(aResolver), type, + aOriginAttributes, flags, originalListenerAddr, key); + auto entry = mPendingRequests.LookupForAdd(key); + if (entry) { + entry.Data()->AppendElement(sender); + } else { + entry.OrInsert([&]() { + auto* hashEntry = new nsTArray<RefPtr<DNSRequestSender>>(); + hashEntry->AppendElement(sender); + return hashEntry; + }); + } + } + + sender->StartRequest(); + + sender.forget(result); + return NS_OK; +} + +nsresult ChildDNSService::CancelAsyncResolveInternal( + const nsACString& aHostname, uint16_t aType, uint32_t aFlags, + nsIDNSResolverInfo* aResolver, nsIDNSListener* aListener, nsresult aReason, + const OriginAttributes& aOriginAttributes) { + if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE)) { + return NS_ERROR_DNS_LOOKUP_QUEUE_FULL; + } + + MutexAutoLock lock(mPendingRequestsLock); + nsTArray<RefPtr<DNSRequestSender>>* hashEntry; + nsCString key; + uintptr_t listenerAddr = reinterpret_cast<uintptr_t>(aListener); + GetDNSRecordHashKey(aHostname, DNSResolverInfo::URL(aResolver), aType, + aOriginAttributes, aFlags, listenerAddr, key); + if (mPendingRequests.Get(key, &hashEntry)) { + // We cancel just one. + hashEntry->ElementAt(0)->Cancel(aReason); + } + + return NS_OK; +} + +//----------------------------------------------------------------------------- +// ChildDNSService::nsIDNSService +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +ChildDNSService::AsyncResolve(const nsACString& hostname, + nsIDNSService::ResolveType aType, uint32_t flags, + nsIDNSResolverInfo* aResolver, + nsIDNSListener* listener, nsIEventTarget* target_, + JS::HandleValue aOriginAttributes, JSContext* aCx, + uint8_t aArgc, nsICancelable** result) { + OriginAttributes attrs; + + if (aArgc == 1) { + if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) { + return NS_ERROR_INVALID_ARG; + } + } + + return AsyncResolveInternal(hostname, aType, flags, aResolver, listener, + target_, attrs, result); +} + +NS_IMETHODIMP +ChildDNSService::AsyncResolveNative( + const nsACString& hostname, nsIDNSService::ResolveType aType, + uint32_t flags, nsIDNSResolverInfo* aResolver, nsIDNSListener* listener, + nsIEventTarget* target_, const OriginAttributes& aOriginAttributes, + nsICancelable** result) { + return AsyncResolveInternal(hostname, aType, flags, aResolver, listener, + target_, aOriginAttributes, result); +} + +NS_IMETHODIMP +ChildDNSService::NewTRRResolverInfo(const nsACString& aTrrURL, + nsIDNSResolverInfo** aResolver) { + RefPtr<DNSResolverInfo> res = new DNSResolverInfo(aTrrURL); + res.forget(aResolver); + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSService::CancelAsyncResolve(const nsACString& aHostname, + nsIDNSService::ResolveType aType, + uint32_t aFlags, + nsIDNSResolverInfo* aResolver, + nsIDNSListener* aListener, nsresult aReason, + JS::HandleValue aOriginAttributes, + JSContext* aCx, uint8_t aArgc) { + OriginAttributes attrs; + + if (aArgc == 1) { + if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) { + return NS_ERROR_INVALID_ARG; + } + } + + return CancelAsyncResolveInternal(aHostname, aType, aFlags, aResolver, + aListener, aReason, attrs); +} + +NS_IMETHODIMP +ChildDNSService::CancelAsyncResolveNative( + const nsACString& aHostname, nsIDNSService::ResolveType aType, + uint32_t aFlags, nsIDNSResolverInfo* aResolver, nsIDNSListener* aListener, + nsresult aReason, const OriginAttributes& aOriginAttributes) { + return CancelAsyncResolveInternal(aHostname, aType, aFlags, aResolver, + aListener, aReason, aOriginAttributes); +} + +NS_IMETHODIMP +ChildDNSService::Resolve(const nsACString& hostname, uint32_t flags, + JS::HandleValue aOriginAttributes, JSContext* aCx, + uint8_t aArgc, nsIDNSRecord** result) { + // not planning to ever support this, since sync IPDL is evil. + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +ChildDNSService::ResolveNative(const nsACString& hostname, uint32_t flags, + const OriginAttributes& aOriginAttributes, + nsIDNSRecord** result) { + // not planning to ever support this, since sync IPDL is evil. + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +ChildDNSService::GetDNSCacheEntries( + nsTArray<mozilla::net::DNSCacheEntries>* args) { + // Only used by networking dashboard, so may not ever need this in child. + // (and would provide a way to spy on what hosts other apps are connecting to, + // unless we start keeping per-app DNS caches). + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +ChildDNSService::ClearCache(bool aTrrToo) { + if (!mTRRServiceParent || !mTRRServiceParent->CanSend()) { + return NS_ERROR_NOT_AVAILABLE; + } + + Unused << mTRRServiceParent->SendClearDNSCache(aTrrToo); + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSService::ReloadParentalControlEnabled() { + if (!mTRRServiceParent) { + return NS_ERROR_NOT_AVAILABLE; + } + + mTRRServiceParent->UpdateParentalControlEnabled(); + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSService::SetDetectedTrrURI(const nsACString& aURI) { + if (!mTRRServiceParent) { + return NS_ERROR_NOT_AVAILABLE; + } + + mTRRServiceParent->SetDetectedTrrURI(aURI); + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSService::GetCurrentTrrURI(nsACString& aURI) { + if (!mTRRServiceParent) { + return NS_ERROR_NOT_AVAILABLE; + } + + mTRRServiceParent->GetTrrURI(aURI); + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSService::GetCurrentTrrMode(uint32_t* aMode) { + if (!mTRRServiceParent) { + return NS_ERROR_NOT_AVAILABLE; + } + + *aMode = mTRRServiceParent->Mode(); + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSService::GetMyHostName(nsACString& result) { + if (XRE_IsParentProcess()) { + char name[100]; + if (PR_GetSystemInfo(PR_SI_HOSTNAME, name, sizeof(name)) == PR_SUCCESS) { + result = name; + return NS_OK; + } + + return NS_ERROR_FAILURE; + } + // TODO: get value from parent during PNecko construction? + return NS_ERROR_NOT_AVAILABLE; +} + +void ChildDNSService::NotifyRequestDone(DNSRequestSender* aDnsRequest) { + // We need the original flags and listener for the pending requests hash. + uint32_t originalFlags = aDnsRequest->mFlags & ~RESOLVE_OFFLINE; + uintptr_t originalListenerAddr = + reinterpret_cast<uintptr_t>(aDnsRequest->mListener.get()); + RefPtr<DNSListenerProxy> wrapper = do_QueryObject(aDnsRequest->mListener); + if (wrapper) { + originalListenerAddr = wrapper->GetOriginalListenerAddress(); + } + + MutexAutoLock lock(mPendingRequestsLock); + + nsCString key; + GetDNSRecordHashKey(aDnsRequest->mHost, aDnsRequest->mTrrServer, + aDnsRequest->mType, aDnsRequest->mOriginAttributes, + originalFlags, originalListenerAddr, key); + + nsTArray<RefPtr<DNSRequestSender>>* hashEntry; + + if (mPendingRequests.Get(key, &hashEntry)) { + auto idx = hashEntry->IndexOf(aDnsRequest); + if (idx != nsTArray<RefPtr<DNSRequestSender>>::NoIndex) { + hashEntry->RemoveElementAt(idx); + if (hashEntry->IsEmpty()) { + mPendingRequests.Remove(key); + } + } + } +} + +//----------------------------------------------------------------------------- +// ChildDNSService::nsPIDNSService +//----------------------------------------------------------------------------- + +nsresult ChildDNSService::Init() { + // Disable prefetching either by explicit preference or if a manual proxy + // is configured + bool disablePrefetch = false; + + nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); + if (prefs) { + prefs->GetBoolPref(kPrefNameDisablePrefetch, &disablePrefetch); + } + + if (mFirstTime) { + mFirstTime = false; + if (prefs) { + prefs->AddObserver(kPrefNameDisablePrefetch, this, false); + + // Monitor these to see if there is a change in proxy configuration + // If a manual proxy is in use, disable prefetch implicitly + prefs->AddObserver("network.proxy.type", this, false); + } + } + + mDisablePrefetch = + disablePrefetch || (StaticPrefs::network_proxy_type() == + nsIProtocolProxyService::PROXYCONFIG_MANUAL); + + return NS_OK; +} + +nsresult ChildDNSService::Shutdown() { return NS_OK; } + +NS_IMETHODIMP +ChildDNSService::GetPrefetchEnabled(bool* outVal) { + *outVal = !mDisablePrefetch; + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSService::SetPrefetchEnabled(bool inVal) { + mDisablePrefetch = !inVal; + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSService::ReportFailedSVCDomainName(const nsACString& aOwnerName, + const nsACString& aSVCDomainName) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +ChildDNSService::IsSVCDomainNameFailed(const nsACString& aOwnerName, + const nsACString& aSVCDomainName, + bool* aResult) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +ChildDNSService::ResetExcludedSVCDomainName(const nsACString& aOwnerName) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +//----------------------------------------------------------------------------- +// ChildDNSService::nsIObserver +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +ChildDNSService::Observe(nsISupports* subject, const char* topic, + const char16_t* data) { + // we are only getting called if a preference has changed. + NS_ASSERTION(strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0, + "unexpected observe call"); + + // Reread prefs + Init(); + return NS_OK; +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/ChildDNSService.h b/netwerk/dns/ChildDNSService.h new file mode 100644 index 0000000000..4788b6c11d --- /dev/null +++ b/netwerk/dns/ChildDNSService.h @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_ChildDNSService_h +#define mozilla_net_ChildDNSService_h + +#include "nsPIDNSService.h" +#include "nsIObserver.h" +#include "mozilla/Attributes.h" +#include "mozilla/Mutex.h" +#include "DNSRequestChild.h" +#include "DNSRequestParent.h" +#include "nsHashKeys.h" +#include "nsClassHashtable.h" + +namespace mozilla { +namespace net { + +class TRRServiceParent; + +class ChildDNSService final : public nsPIDNSService, public nsIObserver { + public: + // AsyncResolve (and CancelAsyncResolve) can be called off-main + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSPIDNSSERVICE + NS_DECL_NSIDNSSERVICE + NS_DECL_NSIOBSERVER + + ChildDNSService(); + + static already_AddRefed<ChildDNSService> GetSingleton(); + + void NotifyRequestDone(DNSRequestSender* aDnsRequest); + + private: + virtual ~ChildDNSService() = default; + + void MOZ_ALWAYS_INLINE GetDNSRecordHashKey( + const nsACString& aHost, const nsACString& aTrrServer, uint16_t aType, + const OriginAttributes& aOriginAttributes, uint32_t aFlags, + uintptr_t aListenerAddr, nsACString& aHashKey); + nsresult AsyncResolveInternal(const nsACString& hostname, uint16_t type, + uint32_t flags, nsIDNSResolverInfo* aResolver, + nsIDNSListener* listener, + nsIEventTarget* target_, + const OriginAttributes& aOriginAttributes, + nsICancelable** result); + nsresult CancelAsyncResolveInternal( + const nsACString& aHostname, uint16_t aType, uint32_t aFlags, + nsIDNSResolverInfo* aResolver, nsIDNSListener* aListener, + nsresult aReason, const OriginAttributes& aOriginAttributes); + + bool mFirstTime; + bool mDisablePrefetch; + + // We need to remember pending dns requests to be able to cancel them. + nsClassHashtable<nsCStringHashKey, nsTArray<RefPtr<DNSRequestSender>>> + mPendingRequests; + Mutex mPendingRequestsLock; + RefPtr<TRRServiceParent> mTRRServiceParent; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_ChildDNSService_h diff --git a/netwerk/dns/DNS.cpp b/netwerk/dns/DNS.cpp new file mode 100644 index 0000000000..c9d3e46ca3 --- /dev/null +++ b/netwerk/dns/DNS.cpp @@ -0,0 +1,382 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=4 sw=2 sts=2 et cin: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/net/DNS.h" + +#include "mozilla/ArrayUtils.h" +#include "mozilla/Assertions.h" +#include "mozilla/mozalloc.h" +#include "mozilla/StaticPrefs_network.h" +#include "nsContentUtils.h" +#include "nsString.h" +#include <string.h> + +#ifdef XP_WIN +# include "ws2tcpip.h" +#endif + +namespace mozilla { +namespace net { + +const char* inet_ntop_internal(int af, const void* src, char* dst, + socklen_t size) { +#ifdef XP_WIN + if (af == AF_INET) { + struct sockaddr_in s; + memset(&s, 0, sizeof(s)); + s.sin_family = AF_INET; + memcpy(&s.sin_addr, src, sizeof(struct in_addr)); + int result = getnameinfo((struct sockaddr*)&s, sizeof(struct sockaddr_in), + dst, size, nullptr, 0, NI_NUMERICHOST); + if (result == 0) { + return dst; + } + } else if (af == AF_INET6) { + struct sockaddr_in6 s; + memset(&s, 0, sizeof(s)); + s.sin6_family = AF_INET6; + memcpy(&s.sin6_addr, src, sizeof(struct in_addr6)); + int result = getnameinfo((struct sockaddr*)&s, sizeof(struct sockaddr_in6), + dst, size, nullptr, 0, NI_NUMERICHOST); + if (result == 0) { + return dst; + } + } + return nullptr; +#else + return inet_ntop(af, src, dst, size); +#endif +} + +// Copies the contents of a PRNetAddr to a NetAddr. +// Does not do a ptr safety check! +void PRNetAddrToNetAddr(const PRNetAddr* prAddr, NetAddr* addr) { + if (prAddr->raw.family == PR_AF_INET) { + addr->inet.family = AF_INET; + addr->inet.port = prAddr->inet.port; + addr->inet.ip = prAddr->inet.ip; + } else if (prAddr->raw.family == PR_AF_INET6) { + addr->inet6.family = AF_INET6; + addr->inet6.port = prAddr->ipv6.port; + addr->inet6.flowinfo = prAddr->ipv6.flowinfo; + memcpy(&addr->inet6.ip, &prAddr->ipv6.ip, sizeof(addr->inet6.ip.u8)); + addr->inet6.scope_id = prAddr->ipv6.scope_id; + } +#if defined(XP_UNIX) + else if (prAddr->raw.family == PR_AF_LOCAL) { + addr->local.family = AF_LOCAL; + memcpy(addr->local.path, prAddr->local.path, sizeof(addr->local.path)); + } +#endif +} + +// Copies the contents of a NetAddr to a PRNetAddr. +// Does not do a ptr safety check! +void NetAddrToPRNetAddr(const NetAddr* addr, PRNetAddr* prAddr) { + if (addr->raw.family == AF_INET) { + prAddr->inet.family = PR_AF_INET; + prAddr->inet.port = addr->inet.port; + prAddr->inet.ip = addr->inet.ip; + } else if (addr->raw.family == AF_INET6) { + prAddr->ipv6.family = PR_AF_INET6; + prAddr->ipv6.port = addr->inet6.port; + prAddr->ipv6.flowinfo = addr->inet6.flowinfo; + memcpy(&prAddr->ipv6.ip, &addr->inet6.ip, sizeof(addr->inet6.ip.u8)); + prAddr->ipv6.scope_id = addr->inet6.scope_id; + } +#if defined(XP_UNIX) + else if (addr->raw.family == AF_LOCAL) { + prAddr->local.family = PR_AF_LOCAL; + memcpy(prAddr->local.path, addr->local.path, sizeof(addr->local.path)); + } +#elif defined(XP_WIN) + else if (addr->raw.family == AF_LOCAL) { + prAddr->local.family = PR_AF_LOCAL; + memcpy(prAddr->local.path, addr->local.path, sizeof(addr->local.path)); + } +#endif +} + +bool NetAddr::ToStringBuffer(char* buf, uint32_t bufSize) const { + const NetAddr* addr = this; + if (addr->raw.family == AF_INET) { + if (bufSize < INET_ADDRSTRLEN) { + return false; + } + struct in_addr nativeAddr = {}; + nativeAddr.s_addr = addr->inet.ip; + return !!inet_ntop_internal(AF_INET, &nativeAddr, buf, bufSize); + } + if (addr->raw.family == AF_INET6) { + if (bufSize < INET6_ADDRSTRLEN) { + return false; + } + struct in6_addr nativeAddr = {}; + memcpy(&nativeAddr.s6_addr, &addr->inet6.ip, sizeof(addr->inet6.ip.u8)); + return !!inet_ntop_internal(AF_INET6, &nativeAddr, buf, bufSize); + } +#if defined(XP_UNIX) + if (addr->raw.family == AF_LOCAL) { + if (bufSize < sizeof(addr->local.path)) { + // Many callers don't bother checking our return value, so + // null-terminate just in case. + if (bufSize > 0) { + buf[0] = '\0'; + } + return false; + } + + // Usually, the size passed to memcpy should be the size of the + // destination. Here, we know that the source is no larger than the + // destination, so using the source's size is always safe, whereas + // using the destination's size may cause us to read off the end of the + // source. + memcpy(buf, addr->local.path, sizeof(addr->local.path)); + return true; + } +#endif + return false; +} + +bool NetAddr::IsLoopbackAddr() const { + if (IsLoopBackAddressWithoutIPv6Mapping()) { + return true; + } + const NetAddr* addr = this; + if (addr->raw.family != AF_INET6) { + return false; + } + + return IPv6ADDR_IS_V4MAPPED(&addr->inet6.ip) && + IPv6ADDR_V4MAPPED_TO_IPADDR(&addr->inet6.ip) == htonl(INADDR_LOOPBACK); +} + +bool NetAddr::IsLoopBackAddressWithoutIPv6Mapping() const { + const NetAddr* addr = this; + if (addr->raw.family == AF_INET) { + // Consider 127.0.0.1/8 as loopback + uint32_t ipv4Addr = ntohl(addr->inet.ip); + return (ipv4Addr >> 24) == 127; + } + + return addr->raw.family == AF_INET6 && IPv6ADDR_IS_LOOPBACK(&addr->inet6.ip); +} + +bool IsLoopbackHostname(const nsACString& aAsciiHost) { + // If the user has configured to proxy localhost addresses don't consider them + // to be secure + if (StaticPrefs::network_proxy_allow_hijacking_localhost()) { + return false; + } + + nsAutoCString host; + nsContentUtils::ASCIIToLower(aAsciiHost, host); + + return host.EqualsLiteral("localhost") || + StringEndsWith(host, ".localhost"_ns); +} + +bool NetAddr::IsIPAddrAny() const { + if (this->raw.family == AF_INET) { + if (this->inet.ip == htonl(INADDR_ANY)) { + return true; + } + } else if (this->raw.family == AF_INET6) { + if (IPv6ADDR_IS_UNSPECIFIED(&this->inet6.ip)) { + return true; + } + if (IPv6ADDR_IS_V4MAPPED(&this->inet6.ip) && + IPv6ADDR_V4MAPPED_TO_IPADDR(&this->inet6.ip) == htonl(INADDR_ANY)) { + return true; + } + } + return false; +} + +NetAddr::NetAddr(const PRNetAddr* prAddr) { PRNetAddrToNetAddr(prAddr, this); } + +bool NetAddr::IsIPAddrV4() const { return this->raw.family == AF_INET; } + +bool NetAddr::IsIPAddrV4Mapped() const { + if (this->raw.family == AF_INET6) { + return IPv6ADDR_IS_V4MAPPED(&this->inet6.ip); + } + return false; +} + +static bool isLocalIPv4(uint32_t networkEndianIP) { + uint32_t addr32 = ntohl(networkEndianIP); + if (addr32 >> 24 == 0x0A || // 10/8 prefix (RFC 1918). + addr32 >> 20 == 0xAC1 || // 172.16/12 prefix (RFC 1918). + addr32 >> 16 == 0xC0A8 || // 192.168/16 prefix (RFC 1918). + addr32 >> 16 == 0xA9FE) { // 169.254/16 prefix (Link Local). + return true; + } + return false; +} + +bool NetAddr::IsIPAddrLocal() const { + const NetAddr* addr = this; + + // IPv4 RFC1918 and Link Local Addresses. + if (addr->raw.family == AF_INET) { + return isLocalIPv4(addr->inet.ip); + } + // IPv6 Unique and Link Local Addresses. + // or mapped IPv4 addresses + if (addr->raw.family == AF_INET6) { + uint16_t addr16 = ntohs(addr->inet6.ip.u16[0]); + if (addr16 >> 9 == 0xfc >> 1 || // fc00::/7 Unique Local Address. + addr16 >> 6 == 0xfe80 >> 6) { // fe80::/10 Link Local Address. + return true; + } + if (IPv6ADDR_IS_V4MAPPED(&addr->inet6.ip)) { + return isLocalIPv4(IPv6ADDR_V4MAPPED_TO_IPADDR(&addr->inet6.ip)); + } + } + + // Not an IPv4/6 local address. + return false; +} + +bool NetAddr::IsIPAddrShared() const { + const NetAddr* addr = this; + + // IPv4 RFC6598. + if (addr->raw.family == AF_INET) { + uint32_t addr32 = ntohl(addr->inet.ip); + if (addr32 >> 22 == 0x644 >> 2) { // 100.64/10 prefix (RFC 6598). + return true; + } + } + + // Not an IPv4 shared address. + return false; +} + +nsresult NetAddr::GetPort(uint16_t* aResult) const { + uint16_t port; + if (this->raw.family == PR_AF_INET) { + port = this->inet.port; + } else if (this->raw.family == PR_AF_INET6) { + port = this->inet6.port; + } else { + return NS_ERROR_NOT_INITIALIZED; + } + + *aResult = ntohs(port); + return NS_OK; +} + +bool NetAddr::operator==(const NetAddr& other) const { + if (this->raw.family != other.raw.family) { + return false; + } + if (this->raw.family == AF_INET) { + return (this->inet.port == other.inet.port) && + (this->inet.ip == other.inet.ip); + } + if (this->raw.family == AF_INET6) { + return (this->inet6.port == other.inet6.port) && + (this->inet6.flowinfo == other.inet6.flowinfo) && + (memcmp(&this->inet6.ip, &other.inet6.ip, sizeof(this->inet6.ip)) == + 0) && + (this->inet6.scope_id == other.inet6.scope_id); +#if defined(XP_UNIX) + } + if (this->raw.family == AF_LOCAL) { + return PL_strncmp(this->local.path, other.local.path, + ArrayLength(this->local.path)); +#endif + } + return false; +} + +bool NetAddr::operator<(const NetAddr& other) const { + if (this->raw.family != other.raw.family) { + return this->raw.family < other.raw.family; + } + if (this->raw.family == AF_INET) { + if (this->inet.ip == other.inet.ip) { + return this->inet.port < other.inet.port; + } + return this->inet.ip < other.inet.ip; + } + if (this->raw.family == AF_INET6) { + int cmpResult = + memcmp(&this->inet6.ip, &other.inet6.ip, sizeof(this->inet6.ip)); + if (cmpResult) { + return cmpResult < 0; + } + if (this->inet6.port != other.inet6.port) { + return this->inet6.port < other.inet6.port; + } + return this->inet6.flowinfo < other.inet6.flowinfo; + } + return false; +} + +AddrInfo::AddrInfo(const nsACString& host, const PRAddrInfo* prAddrInfo, + bool disableIPv4, bool filterNameCollision, + const nsACString& cname) + : mHostName(host), mCanonicalName(cname) { + MOZ_ASSERT(prAddrInfo, + "Cannot construct AddrInfo with a null prAddrInfo pointer!"); + const uint32_t nameCollisionAddr = htonl(0x7f003535); // 127.0.53.53 + + PRNetAddr tmpAddr; + void* iter = nullptr; + do { + iter = PR_EnumerateAddrInfo(iter, prAddrInfo, 0, &tmpAddr); + bool addIt = iter && (!disableIPv4 || tmpAddr.raw.family != PR_AF_INET) && + (!filterNameCollision || tmpAddr.raw.family != PR_AF_INET || + (tmpAddr.inet.ip != nameCollisionAddr)); + if (addIt) { + NetAddr elem(&tmpAddr); + mAddresses.AppendElement(elem); + } + } while (iter); +} + +AddrInfo::AddrInfo(const nsACString& host, const nsACString& cname, + unsigned int aTRR, nsTArray<NetAddr>&& addresses) + : mHostName(host), + mCanonicalName(cname), + mFromTRR(aTRR), + mAddresses(std::move(addresses)) {} + +AddrInfo::AddrInfo(const nsACString& host, unsigned int aTRR, + nsTArray<NetAddr>&& addresses, uint32_t aTTL) + : ttl(aTTL), + mHostName(host), + mCanonicalName(), + mFromTRR(aTRR), + mAddresses(std::move(addresses)) {} + +// deep copy constructor +AddrInfo::AddrInfo(const AddrInfo* src) { + mHostName = src->mHostName; + mCanonicalName = src->mCanonicalName; + ttl = src->ttl; + mFromTRR = src->mFromTRR; + mTrrFetchDuration = src->mTrrFetchDuration; + mTrrFetchDurationNetworkOnly = src->mTrrFetchDurationNetworkOnly; + + mAddresses = src->mAddresses.Clone(); +} + +AddrInfo::~AddrInfo() = default; + +size_t AddrInfo::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const { + size_t n = mallocSizeOf(this); + n += mHostName.SizeOfExcludingThisIfUnshared(mallocSizeOf); + n += mCanonicalName.SizeOfExcludingThisIfUnshared(mallocSizeOf); + n += mAddresses.ShallowSizeOfExcludingThis(mallocSizeOf); + return n; +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/DNS.h b/netwerk/dns/DNS.h new file mode 100644 index 0000000000..0763980d64 --- /dev/null +++ b/netwerk/dns/DNS.h @@ -0,0 +1,247 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef DNS_h_ +#define DNS_h_ + +#include "nscore.h" +#include "nsString.h" +#include "prio.h" +#include "prnetdb.h" +#include "plstr.h" +#include "nsISupportsImpl.h" +#include "mozilla/LinkedList.h" +#include "mozilla/MemoryReporting.h" +#include "nsTArray.h" + +#if !defined(XP_WIN) +# include <arpa/inet.h> +#endif + +#ifdef XP_WIN +# include "winsock2.h" +#endif + +#ifndef AF_LOCAL +# define AF_LOCAL 1 // used for named pipe +#endif + +#define IPv6ADDR_IS_LOOPBACK(a) \ + (((a)->u32[0] == 0) && ((a)->u32[1] == 0) && ((a)->u32[2] == 0) && \ + ((a)->u8[12] == 0) && ((a)->u8[13] == 0) && ((a)->u8[14] == 0) && \ + ((a)->u8[15] == 0x1U)) + +#define IPv6ADDR_IS_V4MAPPED(a) \ + (((a)->u32[0] == 0) && ((a)->u32[1] == 0) && ((a)->u8[8] == 0) && \ + ((a)->u8[9] == 0) && ((a)->u8[10] == 0xff) && ((a)->u8[11] == 0xff)) + +#define IPv6ADDR_V4MAPPED_TO_IPADDR(a) ((a)->u32[3]) + +#define IPv6ADDR_IS_UNSPECIFIED(a) \ + (((a)->u32[0] == 0) && ((a)->u32[1] == 0) && ((a)->u32[2] == 0) && \ + ((a)->u32[3] == 0)) + +namespace mozilla { +namespace net { + +// IMPORTANT: when adding new values, always add them to the end, otherwise +// it will mess up telemetry. +// Stage_0: Receive the record before the http transaction is created. +// Stage_1: Receive the record after the http transaction is created and the +// transaction is not dispatched. +// Stage_2: Receive the record after the http transaction is dispatched. +enum HTTPSSVC_RECEIVED_STAGE : uint32_t { + HTTPSSVC_NOT_PRESENT = 0, + HTTPSSVC_WITH_IPHINT_RECEIVED_STAGE_0 = 1, + HTTPSSVC_WITHOUT_IPHINT_RECEIVED_STAGE_0 = 2, + HTTPSSVC_WITH_IPHINT_RECEIVED_STAGE_1 = 3, + HTTPSSVC_WITHOUT_IPHINT_RECEIVED_STAGE_1 = 4, + HTTPSSVC_WITH_IPHINT_RECEIVED_STAGE_2 = 5, + HTTPSSVC_WITHOUT_IPHINT_RECEIVED_STAGE_2 = 6, + HTTPSSVC_NOT_USED = 7, + HTTPSSVC_NO_USABLE_RECORD = 8, +}; + +#define HTTPS_RR_IS_USED(s) \ + (s > HTTPSSVC_NOT_PRESENT && s < HTTPSSVC_WITH_IPHINT_RECEIVED_STAGE_2) + +// Required buffer size for text form of an IP address. +// Includes space for null termination. We make our own contants +// because we don't want higher-level code depending on things +// like INET6_ADDRSTRLEN and having to include the associated +// platform-specific headers. +#ifdef XP_WIN +// Windows requires longer buffers for some reason. +const int kIPv4CStrBufSize = 22; +const int kIPv6CStrBufSize = 65; +const int kNetAddrMaxCStrBufSize = kIPv6CStrBufSize; +#else +const int kIPv4CStrBufSize = 16; +const int kIPv6CStrBufSize = 46; +const int kLocalCStrBufSize = 108; +const int kNetAddrMaxCStrBufSize = kLocalCStrBufSize; +#endif + +// This was all created at a time in which we were using NSPR for host +// resolution and we were propagating NSPR types like "PRAddrInfo" and +// "PRNetAddr" all over Gecko. This made it hard to use another host +// resolver -- we were locked into NSPR. The goal here is to get away +// from that. We'll translate what we get from NSPR or any other host +// resolution library into the types below and use them in Gecko. + +union IPv6Addr { + uint8_t u8[16]; + uint16_t u16[8]; + uint32_t u32[4]; + uint64_t u64[2]; +}; + +// This struct is similar to operating system structs like "sockaddr", used for +// things like "connect" and "getsockname". When tempted to cast or do dumb +// copies of this struct to another struct, bear compiler-computed padding +// in mind. The size of this struct, and the layout of the data in it, may +// not be what you expect. +union NetAddr { + struct { + uint16_t family; /* address family (0x00ff maskable) */ + char data[14]; /* raw address data */ + } raw{}; + struct { + uint16_t family; /* address family (AF_INET) */ + uint16_t port; /* port number */ + uint32_t ip; /* The actual 32 bits of address */ + } inet; + struct { + uint16_t family; /* address family (AF_INET6) */ + uint16_t port; /* port number */ + uint32_t flowinfo; /* routing information */ + IPv6Addr ip; /* the actual 128 bits of address */ + uint32_t scope_id; /* set of interfaces for a scope */ + } inet6; +#if defined(XP_UNIX) || defined(XP_WIN) + struct { /* Unix domain socket or + Windows Named Pipes address */ + uint16_t family; /* address family (AF_UNIX) */ + char path[104]; /* null-terminated pathname */ + } local; +#endif + // introduced to support nsTArray<NetAddr> comparisons and sorting + bool operator==(const NetAddr& other) const; + bool operator<(const NetAddr& other) const; + + inline NetAddr& operator=(const NetAddr& other) { + memcpy(this, &other, sizeof(NetAddr)); + return *this; + } + + NetAddr() { memset(this, 0, sizeof(NetAddr)); } + explicit NetAddr(const PRNetAddr* prAddr); + + bool IsIPAddrAny() const; + bool IsLoopbackAddr() const; + bool IsLoopBackAddressWithoutIPv6Mapping() const; + bool IsIPAddrV4() const; + bool IsIPAddrV4Mapped() const; + bool IsIPAddrLocal() const; + bool IsIPAddrShared() const; + nsresult GetPort(uint16_t* aResult) const; + bool ToStringBuffer(char* buf, uint32_t bufSize) const; +}; + +class AddrInfo { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AddrInfo) + + public: + static const uint32_t NO_TTL_DATA = (uint32_t)-1; + + // Creates an AddrInfo object. + explicit AddrInfo(const nsACString& host, const PRAddrInfo* prAddrInfo, + bool disableIPv4, bool filterNameCollision, + const nsACString& cname); + + // Creates a basic AddrInfo object (initialize only the host, cname and TRR + // type). + explicit AddrInfo(const nsACString& host, const nsACString& cname, + unsigned int aTRR, nsTArray<NetAddr>&& addresses); + + // Creates a basic AddrInfo object (initialize only the host and TRR status). + explicit AddrInfo(const nsACString& host, unsigned int aTRR, + nsTArray<NetAddr>&& addresses, uint32_t aTTL = NO_TTL_DATA); + + explicit AddrInfo(const AddrInfo* src); // copy + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; + + unsigned int IsTRR() { return mFromTRR; } + + double GetTrrFetchDuration() { return mTrrFetchDuration; } + double GetTrrFetchDurationNetworkOnly() { + return mTrrFetchDurationNetworkOnly; + } + + const nsTArray<NetAddr>& Addresses() { return mAddresses; } + const nsCString& Hostname() { return mHostName; } + const nsCString& CanonicalHostname() { return mCanonicalName; } + uint32_t TTL() { return ttl; } + + class MOZ_STACK_CLASS AddrInfoBuilder { + public: + explicit AddrInfoBuilder(AddrInfo* aInfo) { + mInfo = new AddrInfo(aInfo); // Clone it + } + + void SetTrrFetchDurationNetworkOnly(double aTime) { + mInfo->mTrrFetchDurationNetworkOnly = aTime; + } + + void SetTrrFetchDuration(double aTime) { mInfo->mTrrFetchDuration = aTime; } + + void SetTTL(uint32_t aTTL) { mInfo->ttl = aTTL; } + + void SetAddresses(nsTArray<NetAddr>&& addresses) { + mInfo->mAddresses = std::move(addresses); + } + + void SetCanonicalHostname(const nsACString& aCname) { + mInfo->mCanonicalName = aCname; + } + + already_AddRefed<AddrInfo> Finish() { return mInfo.forget(); } + + private: + RefPtr<AddrInfo> mInfo; + }; + + AddrInfoBuilder Build() { return AddrInfoBuilder(this); } + + private: + ~AddrInfo(); + uint32_t ttl = NO_TTL_DATA; + + nsCString mHostName; + nsCString mCanonicalName; + + unsigned int mFromTRR = 0; + double mTrrFetchDuration = 0; + double mTrrFetchDurationNetworkOnly = 0; + + nsTArray<NetAddr> mAddresses; +}; + +// Copies the contents of a PRNetAddr to a NetAddr. +// Does not do a ptr safety check! +void PRNetAddrToNetAddr(const PRNetAddr* prAddr, NetAddr* addr); + +// Copies the contents of a NetAddr to a PRNetAddr. +// Does not do a ptr safety check! +void NetAddrToPRNetAddr(const NetAddr* addr, PRNetAddr* prAddr); + +bool IsLoopbackHostname(const nsACString& aAsciiHost); + +} // namespace net +} // namespace mozilla + +#endif // DNS_h_ diff --git a/netwerk/dns/DNSByTypeRecord.h b/netwerk/dns/DNSByTypeRecord.h new file mode 100644 index 0000000000..2008b6d213 --- /dev/null +++ b/netwerk/dns/DNSByTypeRecord.h @@ -0,0 +1,238 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef DNSByTypeRecord_h__ +#define DNSByTypeRecord_h__ + +#include "mozilla/net/HTTPSSVC.h" +#include "mozilla/ipc/IPDLParamTraits.h" +#include "ipc/IPCMessageUtils.h" +#include "mozilla/net/NeckoMessageUtils.h" + +namespace mozilla { +namespace net { + +// The types of nsIDNSByTypeRecord: Nothing, TXT, HTTPSSVC +using TypeRecordEmpty = Nothing; +using TypeRecordTxt = CopyableTArray<nsCString>; +using TypeRecordHTTPSSVC = CopyableTArray<SVCB>; + +// This variant reflects the multiple types of data a nsIDNSByTypeRecord +// can hold. +using TypeRecordResultType = + Variant<TypeRecordEmpty, TypeRecordTxt, TypeRecordHTTPSSVC>; + +// TypeRecordResultType is a variant, but since it doesn't have a default +// constructor it's not a type we can pass directly over IPC. +struct IPCTypeRecord { + bool operator==(const IPCTypeRecord& aOther) const { + return mData == aOther.mData; + } + explicit IPCTypeRecord() : mData(Nothing{}) {} + TypeRecordResultType mData; +}; + +} // namespace net +} // namespace mozilla + +namespace mozilla { +namespace ipc { + +template <> +struct IPDLParamTraits<mozilla::net::IPCTypeRecord> { + typedef mozilla::net::IPCTypeRecord paramType; + static void Write(IPC::Message* aMsg, IProtocol* aActor, + const paramType& aParam) { + WriteIPDLParam(aMsg, aActor, aParam.mData); + } + + static bool Read(const IPC::Message* aMsg, PickleIterator* aIter, + IProtocol* aActor, paramType* aResult) { + if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mData)) { + return false; + } + return true; + } +}; + +template <> +struct IPDLParamTraits<mozilla::Nothing> { + typedef mozilla::Nothing paramType; + static void Write(IPC::Message* aMsg, IProtocol* aActor, + const paramType& aParam) { + bool isSome = false; + WriteIPDLParam(aMsg, aActor, isSome); + } + + static bool Read(const IPC::Message* aMsg, PickleIterator* aIter, + IProtocol* aActor, paramType* aResult) { + bool isSome; + if (!ReadIPDLParam(aMsg, aIter, aActor, &isSome)) { + return false; + } + *aResult = Nothing(); + return true; + } +}; + +template <> +struct IPDLParamTraits<mozilla::net::SVCB> { + typedef mozilla::net::SVCB paramType; + static void Write(IPC::Message* aMsg, IProtocol* aActor, + const paramType& aParam) { + WriteIPDLParam(aMsg, aActor, aParam.mSvcFieldPriority); + WriteIPDLParam(aMsg, aActor, aParam.mSvcDomainName); + WriteIPDLParam(aMsg, aActor, aParam.mSvcFieldValue); + } + + static bool Read(const IPC::Message* aMsg, PickleIterator* aIter, + IProtocol* aActor, paramType* aResult) { + if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mSvcFieldPriority)) { + return false; + } + if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mSvcDomainName)) { + return false; + } + if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mSvcFieldValue)) { + return false; + } + return true; + } +}; + +template <> +struct IPDLParamTraits<mozilla::net::SvcParamAlpn> { + typedef mozilla::net::SvcParamAlpn paramType; + static void Write(IPC::Message* aMsg, IProtocol* aActor, + const paramType& aParam) { + WriteIPDLParam(aMsg, aActor, aParam.mValue); + } + + static bool Read(const IPC::Message* aMsg, PickleIterator* aIter, + IProtocol* aActor, paramType* aResult) { + if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mValue)) { + return false; + } + return true; + } +}; + +template <> +struct IPDLParamTraits<mozilla::net::SvcParamNoDefaultAlpn> { + typedef mozilla::net::SvcParamNoDefaultAlpn paramType; + static void Write(IPC::Message* aMsg, IProtocol* aActor, + const paramType& aParam) {} + + static bool Read(const IPC::Message* aMsg, PickleIterator* aIter, + IProtocol* aActor, paramType* aResult) { + return true; + } +}; + +template <> +struct IPDLParamTraits<mozilla::net::SvcParamPort> { + typedef mozilla::net::SvcParamPort paramType; + static void Write(IPC::Message* aMsg, IProtocol* aActor, + const paramType& aParam) { + WriteIPDLParam(aMsg, aActor, aParam.mValue); + } + + static bool Read(const IPC::Message* aMsg, PickleIterator* aIter, + IProtocol* aActor, paramType* aResult) { + if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mValue)) { + return false; + } + return true; + } +}; + +template <> +struct IPDLParamTraits<mozilla::net::SvcParamIpv4Hint> { + typedef mozilla::net::SvcParamIpv4Hint paramType; + static void Write(IPC::Message* aMsg, IProtocol* aActor, + const paramType& aParam) { + WriteIPDLParam(aMsg, aActor, aParam.mValue); + } + + static bool Read(const IPC::Message* aMsg, PickleIterator* aIter, + IProtocol* aActor, paramType* aResult) { + if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mValue)) { + return false; + } + return true; + } +}; + +template <> +struct IPDLParamTraits<mozilla::net::SvcParamEchConfig> { + typedef mozilla::net::SvcParamEchConfig paramType; + static void Write(IPC::Message* aMsg, IProtocol* aActor, + const paramType& aParam) { + WriteIPDLParam(aMsg, aActor, aParam.mValue); + } + + static bool Read(const IPC::Message* aMsg, PickleIterator* aIter, + IProtocol* aActor, paramType* aResult) { + if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mValue)) { + return false; + } + return true; + } +}; + +template <> +struct IPDLParamTraits<mozilla::net::SvcParamIpv6Hint> { + typedef mozilla::net::SvcParamIpv6Hint paramType; + static void Write(IPC::Message* aMsg, IProtocol* aActor, + const paramType& aParam) { + WriteIPDLParam(aMsg, aActor, aParam.mValue); + } + + static bool Read(const IPC::Message* aMsg, PickleIterator* aIter, + IProtocol* aActor, paramType* aResult) { + if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mValue)) { + return false; + } + return true; + } +}; + +template <> +struct IPDLParamTraits<mozilla::net::SvcParamODoHConfig> { + typedef mozilla::net::SvcParamODoHConfig paramType; + static void Write(IPC::Message* aMsg, IProtocol* aActor, + const paramType& aParam) { + WriteIPDLParam(aMsg, aActor, aParam.mValue); + } + + static bool Read(const IPC::Message* aMsg, PickleIterator* aIter, + IProtocol* aActor, paramType* aResult) { + if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mValue)) { + return false; + } + return true; + } +}; + +template <> +struct IPDLParamTraits<mozilla::net::SvcFieldValue> { + typedef mozilla::net::SvcFieldValue paramType; + static void Write(IPC::Message* aMsg, IProtocol* aActor, + const paramType& aParam) { + WriteIPDLParam(aMsg, aActor, aParam.mValue); + } + + static bool Read(const IPC::Message* aMsg, PickleIterator* aIter, + IProtocol* aActor, paramType* aResult) { + if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mValue)) { + return false; + } + return true; + } +}; + +} // namespace ipc +} // namespace mozilla + +#endif // DNSByTypeRecord_h__ diff --git a/netwerk/dns/DNSListenerProxy.cpp b/netwerk/dns/DNSListenerProxy.cpp new file mode 100644 index 0000000000..8468edca2d --- /dev/null +++ b/netwerk/dns/DNSListenerProxy.cpp @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/net/DNSListenerProxy.h" +#include "nsICancelable.h" + +namespace mozilla { +namespace net { + +NS_IMPL_ADDREF(DNSListenerProxy) +NS_IMPL_RELEASE(DNSListenerProxy) +NS_INTERFACE_MAP_BEGIN(DNSListenerProxy) + NS_INTERFACE_MAP_ENTRY(nsIDNSListener) + NS_INTERFACE_MAP_ENTRY_CONCRETE(DNSListenerProxy) +NS_INTERFACE_MAP_END + +NS_IMETHODIMP +DNSListenerProxy::OnLookupComplete(nsICancelable* aRequest, + nsIDNSRecord* aRecord, nsresult aStatus) { + RefPtr<DNSListenerProxy> self = this; + nsCOMPtr<nsICancelable> request = aRequest; + nsCOMPtr<nsIDNSRecord> record = aRecord; + return mTargetThread->Dispatch( + NS_NewRunnableFunction("DNSListenerProxy::OnLookupComplete", + [self, request, record, aStatus]() { + Unused << self->mListener->OnLookupComplete( + request, record, aStatus); + self->mListener = nullptr; + }), + NS_DISPATCH_NORMAL); +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/DNSListenerProxy.h b/netwerk/dns/DNSListenerProxy.h new file mode 100644 index 0000000000..5baddfd8e7 --- /dev/null +++ b/netwerk/dns/DNSListenerProxy.h @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef DNSListenerProxy_h__ +#define DNSListenerProxy_h__ + +#include "nsIDNSListener.h" +#include "nsIDNSRecord.h" +#include "nsProxyRelease.h" +#include "nsThreadUtils.h" + +class nsIEventTarget; +class nsICancelable; + +namespace mozilla { +namespace net { + +#define DNS_LISTENER_PROXY_IID \ + { \ + 0x8f172ca3, 0x7a7f, 0x4941, { \ + 0xa7, 0x0b, 0xbc, 0x72, 0x80, 0x2e, 0x9d, 0x9b \ + } \ + } + +class DNSListenerProxy final : public nsIDNSListener { + public: + DNSListenerProxy(nsIDNSListener* aListener, nsIEventTarget* aTargetThread) + // We want to make sure that |aListener| is only accessed on the target + // thread. + : mListener(aListener), + mTargetThread(aTargetThread), + mListenerAddress(reinterpret_cast<uintptr_t>(aListener)) { + MOZ_ASSERT(mListener); + MOZ_ASSERT(mTargetThread); + } + + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIDNSLISTENER + NS_DECLARE_STATIC_IID_ACCESSOR(DNS_LISTENER_PROXY_IID) + + uintptr_t GetOriginalListenerAddress() const { return mListenerAddress; } + + private: + ~DNSListenerProxy() { + NS_ProxyRelease("DNSListenerProxy::mListener", mTargetThread, + mListener.forget()); + } + + nsCOMPtr<nsIDNSListener> mListener; + nsCOMPtr<nsIEventTarget> mTargetThread; + uintptr_t mListenerAddress; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(DNSListenerProxy, DNS_LISTENER_PROXY_IID) + +} // namespace net +} // namespace mozilla + +#endif // DNSListenerProxy_h__ diff --git a/netwerk/dns/DNSPacket.cpp b/netwerk/dns/DNSPacket.cpp new file mode 100644 index 0000000000..460fe92636 --- /dev/null +++ b/netwerk/dns/DNSPacket.cpp @@ -0,0 +1,975 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DNSPacket.h" + +namespace mozilla { +namespace net { + +extern mozilla::LazyLogModule gHostResolverLog; +#undef LOG +#undef LOG_ENABLED +#define LOG(args) MOZ_LOG(gHostResolverLog, mozilla::LogLevel::Debug, args) +#define LOG_ENABLED() \ + MOZ_LOG_TEST(mozilla::net::gHostResolverLog, mozilla::LogLevel::Debug) + +static uint16_t get16bit(const unsigned char* aData, unsigned int index) { + return ((aData[index] << 8) | aData[index + 1]); +} + +static uint32_t get32bit(const unsigned char* aData, unsigned int index) { + return (aData[index] << 24) | (aData[index + 1] << 16) | + (aData[index + 2] << 8) | aData[index + 3]; +} + +// https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-extended-error-16#section-4 +// This is a list of errors for which we should not fallback to Do53. +// These are normally DNSSEC failures or explicit filtering performed by the +// recursive resolver. +bool hardFail(uint16_t code) { + const uint16_t noFallbackErrors[] = { + 4, // Forged answer (malware filtering) + 6, // DNSSEC Boggus + 7, // Signature expired + 8, // Signature not yet valid + 9, // DNSKEY Missing + 10, // RRSIG missing + 11, // No ZONE Key Bit set + 12, // NSEC Missing + 17, // Filtered + }; + + for (const auto& err : noFallbackErrors) { + if (code == err) { + return true; + } + } + return false; +} + +// static +nsresult DNSPacket::ParseSvcParam(unsigned int svcbIndex, uint16_t key, + SvcFieldValue& field, uint16_t length) { + switch (key) { + case SvcParamKeyMandatory: { + if (length % 2 != 0) { + // This key should encode a list of uint16_t + return NS_ERROR_UNEXPECTED; + } + while (length > 0) { + uint16_t mandatoryKey = get16bit(mResponse, svcbIndex); + length -= 2; + svcbIndex += 2; + + if (!IsValidSvcParamKey(mandatoryKey)) { + LOG(("The mandatory field includes a key we don't support %u", + mandatoryKey)); + return NS_ERROR_UNEXPECTED; + } + } + break; + } + case SvcParamKeyAlpn: { + field.mValue = AsVariant(SvcParamAlpn()); + auto& alpnArray = field.mValue.as<SvcParamAlpn>().mValue; + while (length > 0) { + uint8_t alpnIdLength = mResponse[svcbIndex++]; + length -= 1; + if (alpnIdLength > length) { + return NS_ERROR_UNEXPECTED; + } + + alpnArray.AppendElement( + nsCString((const char*)&mResponse[svcbIndex], alpnIdLength)); + length -= alpnIdLength; + svcbIndex += alpnIdLength; + } + break; + } + case SvcParamKeyNoDefaultAlpn: { + if (length != 0) { + // This key should not contain a value + return NS_ERROR_UNEXPECTED; + } + field.mValue = AsVariant(SvcParamNoDefaultAlpn{}); + break; + } + case SvcParamKeyPort: { + if (length != 2) { + // This key should only encode a uint16_t + return NS_ERROR_UNEXPECTED; + } + field.mValue = + AsVariant(SvcParamPort{.mValue = get16bit(mResponse, svcbIndex)}); + break; + } + case SvcParamKeyIpv4Hint: { + if (length % 4 != 0) { + // This key should only encode IPv4 addresses + return NS_ERROR_UNEXPECTED; + } + + field.mValue = AsVariant(SvcParamIpv4Hint()); + auto& ipv4array = field.mValue.as<SvcParamIpv4Hint>().mValue; + while (length > 0) { + NetAddr addr; + addr.inet.family = AF_INET; + addr.inet.port = 0; + addr.inet.ip = ntohl(get32bit(mResponse, svcbIndex)); + ipv4array.AppendElement(addr); + length -= 4; + svcbIndex += 4; + } + break; + } + case SvcParamKeyEchConfig: { + field.mValue = AsVariant(SvcParamEchConfig{ + .mValue = nsCString((const char*)(&mResponse[svcbIndex]), length)}); + break; + } + case SvcParamKeyIpv6Hint: { + if (length % 16 != 0) { + // This key should only encode IPv6 addresses + return NS_ERROR_UNEXPECTED; + } + + field.mValue = AsVariant(SvcParamIpv6Hint()); + auto& ipv6array = field.mValue.as<SvcParamIpv6Hint>().mValue; + while (length > 0) { + NetAddr addr; + addr.inet6.family = AF_INET6; + addr.inet6.port = 0; // unknown + addr.inet6.flowinfo = 0; // unknown + addr.inet6.scope_id = 0; // unknown + for (int i = 0; i < 16; i++, svcbIndex++) { + addr.inet6.ip.u8[i] = mResponse[svcbIndex]; + } + ipv6array.AppendElement(addr); + length -= 16; + // no need to increase svcbIndex - we did it in the for above. + } + break; + } + case SvcParamKeyODoHConfig: { + field.mValue = AsVariant(SvcParamODoHConfig{ + .mValue = nsCString((const char*)(&mResponse[svcbIndex]), length)}); + break; + } + default: { + // Unespected type. We'll just ignore it. + return NS_OK; + break; + } + } + return NS_OK; +} + +nsresult DNSPacket::PassQName(unsigned int& index) { + uint8_t length; + do { + if (mBodySize < (index + 1)) { + LOG(("TRR: PassQName:%d fail at index %d\n", __LINE__, index)); + return NS_ERROR_ILLEGAL_VALUE; + } + length = static_cast<uint8_t>(mResponse[index]); + if ((length & 0xc0) == 0xc0) { + // name pointer, advance over it and be done + if (mBodySize < (index + 2)) { + return NS_ERROR_ILLEGAL_VALUE; + } + index += 2; + break; + } + if (length & 0xc0) { + LOG(("TRR: illegal label length byte (%x) at index %d\n", length, index)); + return NS_ERROR_ILLEGAL_VALUE; + } + // pass label + if (mBodySize < (index + 1 + length)) { + LOG(("TRR: PassQName:%d fail at index %d\n", __LINE__, index)); + return NS_ERROR_ILLEGAL_VALUE; + } + index += 1 + length; + } while (length); + return NS_OK; +} + +// GetQname: retrieves the qname (stores in 'aQname') and stores the index +// after qname was parsed into the 'aIndex'. +nsresult DNSPacket::GetQname(nsACString& aQname, unsigned int& aIndex) { + uint8_t clength = 0; + unsigned int cindex = aIndex; + unsigned int loop = 128; // a valid DNS name can never loop this much + unsigned int endindex = 0; // index position after this data + do { + if (cindex >= mBodySize) { + LOG(("TRR: bad Qname packet\n")); + return NS_ERROR_ILLEGAL_VALUE; + } + clength = static_cast<uint8_t>(mResponse[cindex]); + if ((clength & 0xc0) == 0xc0) { + // name pointer, get the new offset (14 bits) + if ((cindex + 1) >= mBodySize) { + return NS_ERROR_ILLEGAL_VALUE; + } + // extract the new index position for the next label + uint16_t newpos = (clength & 0x3f) << 8 | mResponse[cindex + 1]; + if (!endindex) { + // only update on the first "jump" + endindex = cindex + 2; + } + cindex = newpos; + continue; + } + if (clength & 0xc0) { + // any of those bits set individually is an error + LOG(("TRR: bad Qname packet\n")); + return NS_ERROR_ILLEGAL_VALUE; + } + + cindex++; + + if (clength) { + if (!aQname.IsEmpty()) { + aQname.Append("."); + } + if ((cindex + clength) > mBodySize) { + return NS_ERROR_ILLEGAL_VALUE; + } + aQname.Append((const char*)(&mResponse[cindex]), clength); + cindex += clength; // skip label + } + } while (clength && --loop); + + if (!loop) { + LOG(("DNSPacket::DohDecode pointer loop error\n")); + return NS_ERROR_ILLEGAL_VALUE; + } + if (!endindex) { + // there was no "jump" + endindex = cindex; + } + aIndex = endindex; + return NS_OK; +} + +nsresult DOHresp::Add(uint32_t TTL, unsigned char const* dns, + unsigned int index, uint16_t len, bool aLocalAllowed) { + NetAddr addr; + if (4 == len) { + // IPv4 + addr.inet.family = AF_INET; + addr.inet.port = 0; // unknown + addr.inet.ip = ntohl(get32bit(dns, index)); + } else if (16 == len) { + // IPv6 + addr.inet6.family = AF_INET6; + addr.inet6.port = 0; // unknown + addr.inet6.flowinfo = 0; // unknown + addr.inet6.scope_id = 0; // unknown + for (int i = 0; i < 16; i++, index++) { + addr.inet6.ip.u8[i] = dns[index]; + } + } else { + return NS_ERROR_UNEXPECTED; + } + + if (addr.IsIPAddrLocal() && !aLocalAllowed) { + return NS_ERROR_FAILURE; + } + + // While the DNS packet might return individual TTLs for each address, + // we can only return one value in the AddrInfo class so pick the + // lowest number. + if (mTtl < TTL) { + mTtl = TTL; + } + + if (LOG_ENABLED()) { + char buf[128]; + addr.ToStringBuffer(buf, sizeof(buf)); + LOG(("DOHresp:Add %s\n", buf)); + } + mAddresses.AppendElement(addr); + return NS_OK; +} + +nsresult DNSPacket::OnDataAvailable(nsIRequest* aRequest, + nsIInputStream* aInputStream, + uint64_t aOffset, const uint32_t aCount) { + if (aCount + mBodySize > MAX_SIZE) { + LOG(("DNSPacket::OnDataAvailable:%d fail\n", __LINE__)); + return NS_ERROR_FAILURE; + } + uint32_t count; + nsresult rv = + aInputStream->Read((char*)mResponse + mBodySize, aCount, &count); + if (NS_FAILED(rv)) { + return rv; + } + MOZ_ASSERT(count == aCount); + mBodySize += aCount; + return NS_OK; +} + +const uint8_t kDNS_CLASS_IN = 1; + +// static +nsresult DNSPacket::EncodeRequest(nsCString& aBody, const nsACString& aHost, + uint16_t aType, bool aDisableECS) { + aBody.Truncate(); + // Header + aBody += '\0'; + aBody += '\0'; // 16 bit id + aBody += 0x01; // |QR| Opcode |AA|TC|RD| Set the RD bit + aBody += '\0'; // |RA| Z | RCODE | + aBody += '\0'; + aBody += 1; // QDCOUNT (number of entries in the question section) + aBody += '\0'; + aBody += '\0'; // ANCOUNT + aBody += '\0'; + aBody += '\0'; // NSCOUNT + + aBody += '\0'; // ARCOUNT + aBody += aDisableECS ? 1 : '\0'; // ARCOUNT low byte for EDNS(0) + + // Question + + // The input host name should be converted to a sequence of labels, where + // each label consists of a length octet followed by that number of + // octets. The domain name terminates with the zero length octet for the + // null label of the root. + // Followed by 16 bit QTYPE and 16 bit QCLASS + + int32_t index = 0; + int32_t offset = 0; + do { + bool dotFound = false; + int32_t labelLength; + index = aHost.FindChar('.', offset); + if (kNotFound != index) { + dotFound = true; + labelLength = index - offset; + } else { + labelLength = aHost.Length() - offset; + } + if (labelLength > 63) { + // too long label! + return NS_ERROR_ILLEGAL_VALUE; + } + if (labelLength > 0) { + aBody += static_cast<unsigned char>(labelLength); + nsDependentCSubstring label = Substring(aHost, offset, labelLength); + aBody.Append(label); + } + if (!dotFound) { + aBody += '\0'; // terminate with a final zero + break; + } + offset += labelLength + 1; // move over label and dot + } while (true); + + aBody += static_cast<uint8_t>(aType >> 8); // upper 8 bit TYPE + aBody += static_cast<uint8_t>(aType); + aBody += '\0'; // upper 8 bit CLASS + aBody += kDNS_CLASS_IN; // IN - "the Internet" + + if (aDisableECS) { + // EDNS(0) is RFC 6891, ECS is RFC 7871 + aBody += '\0'; // NAME | domain name | MUST be 0 (root domain) | + aBody += '\0'; + aBody += 41; // TYPE | u_int16_t | OPT (41) | + aBody += 16; // CLASS | u_int16_t | requestor's UDP payload size | + aBody += + '\0'; // advertise 4K (high-byte: 16 | low-byte: 0), ignored by DoH + aBody += '\0'; // TTL | u_int32_t | extended RCODE and flags | + aBody += '\0'; + aBody += '\0'; + aBody += '\0'; + + aBody += '\0'; // upper 8 bit RDLEN + aBody += 8; // RDLEN | u_int16_t | length of all RDATA | + + // RDATA | octet stream | {attribute,value} pairs | + // The RDATA is just the ECS option setting zero subnet prefix + + aBody += '\0'; // upper 8 bit OPTION-CODE ECS + aBody += 8; // OPTION-CODE, 2 octets, for ECS is 8 + + aBody += '\0'; // upper 8 bit OPTION-LENGTH + aBody += 4; // OPTION-LENGTH, 2 octets, contains the length of the payload + // after OPTION-LENGTH + aBody += '\0'; // upper 8 bit FAMILY. IANA Address Family Numbers registry, + // not the AF_* constants! + aBody += 1; // FAMILY (Ipv4), 2 octets + + aBody += '\0'; // SOURCE PREFIX-LENGTH | SCOPE PREFIX-LENGTH | + aBody += '\0'; + + // ADDRESS, minimum number of octets == nothing because zero bits + } + return NS_OK; +} + +// +// DohDecode() collects the TTL and the IP addresses in the response +// +// static +nsresult DNSPacket::Decode( + nsCString& aHost, enum TrrType aType, nsCString& aCname, bool aAllowRFC1918, + nsHostRecord::TRRSkippedReason& aReason, DOHresp& aResp, + TypeRecordResultType& aTypeResult, + nsClassHashtable<nsCStringHashKey, DOHresp>& aAdditionalRecords, + uint32_t& aTTL) { + // The response has a 12 byte header followed by the question (returned) + // and then the answer. The answer section itself contains the name, type + // and class again and THEN the record data. + + // www.example.com response: + // header: + // abcd 8180 0001 0001 0000 0000 + // the question: + // 0377 7777 0765 7861 6d70 6c65 0363 6f6d 0000 0100 01 + // the answer: + // 03 7777 7707 6578 616d 706c 6503 636f 6d00 0001 0001 + // 0000 0080 0004 5db8 d822 + + unsigned int index = 12; + uint8_t length; + nsAutoCString host; + nsresult rv; + uint16_t extendedError = UINT16_MAX; + + LOG(("doh decode %s %d bytes\n", aHost.get(), mBodySize)); + + aCname.Truncate(); + + if (mBodySize < 12 || mResponse[0] || mResponse[1]) { + LOG(("TRR bad incoming DOH, eject!\n")); + return NS_ERROR_ILLEGAL_VALUE; + } + uint8_t rcode = mResponse[3] & 0x0F; + LOG(("TRR Decode %s RCODE %d\n", aHost.get(), rcode)); + if (rcode) { + if (aReason == nsHostRecord::TRR_UNSET) { + aReason = nsHostRecord::TRR_RCODE_FAIL; + } + } + + uint16_t questionRecords = get16bit(mResponse, 4); // qdcount + // iterate over the single(?) host name in question + while (questionRecords) { + do { + if (mBodySize < (index + 1)) { + LOG(("TRR Decode 1 index: %u size: %u", index, mBodySize)); + return NS_ERROR_ILLEGAL_VALUE; + } + length = static_cast<uint8_t>(mResponse[index]); + if (length) { + if (host.Length()) { + host.Append("."); + } + if (mBodySize < (index + 1 + length)) { + LOG(("TRR Decode 2 index: %u size: %u len: %u", index, mBodySize, + length)); + return NS_ERROR_ILLEGAL_VALUE; + } + host.Append(((char*)mResponse) + index + 1, length); + } + index += 1 + length; // skip length byte + label + } while (length); + if (mBodySize < (index + 4)) { + LOG(("TRR Decode 3 index: %u size: %u", index, mBodySize)); + return NS_ERROR_ILLEGAL_VALUE; + } + index += 4; // skip question's type, class + questionRecords--; + } + + // Figure out the number of answer records from ANCOUNT + uint16_t answerRecords = get16bit(mResponse, 6); + + LOG(("TRR Decode: %d answer records (%u bytes body) %s index=%u\n", + answerRecords, mBodySize, host.get(), index)); + + while (answerRecords) { + nsAutoCString qname; + rv = GetQname(qname, index); + if (NS_FAILED(rv)) { + return rv; + } + // 16 bit TYPE + if (mBodySize < (index + 2)) { + LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index + 2)); + return NS_ERROR_ILLEGAL_VALUE; + } + uint16_t TYPE = get16bit(mResponse, index); + + if ((TYPE != TRRTYPE_CNAME) && (TYPE != TRRTYPE_HTTPSSVC) && + (TYPE != static_cast<uint16_t>(aType))) { + // Not the same type as was asked for nor CNAME + LOG(("TRR: Dohdecode:%d asked for type %d got %d\n", __LINE__, aType, + TYPE)); + return NS_ERROR_UNEXPECTED; + } + index += 2; + + // 16 bit class + if (mBodySize < (index + 2)) { + LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index + 2)); + return NS_ERROR_ILLEGAL_VALUE; + } + uint16_t CLASS = get16bit(mResponse, index); + if (kDNS_CLASS_IN != CLASS) { + LOG(("TRR bad CLASS (%u) at index %d\n", CLASS, index)); + return NS_ERROR_UNEXPECTED; + } + index += 2; + + // 32 bit TTL (seconds) + if (mBodySize < (index + 4)) { + LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index)); + return NS_ERROR_ILLEGAL_VALUE; + } + uint32_t TTL = get32bit(mResponse, index); + index += 4; + + // 16 bit RDLENGTH + if (mBodySize < (index + 2)) { + LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index)); + return NS_ERROR_ILLEGAL_VALUE; + } + uint16_t RDLENGTH = get16bit(mResponse, index); + index += 2; + + if (mBodySize < (index + RDLENGTH)) { + LOG(("TRR: Dohdecode:%d fail RDLENGTH=%d at index %d\n", __LINE__, + RDLENGTH, index)); + return NS_ERROR_ILLEGAL_VALUE; + } + + // We check if the qname is a case-insensitive match for the host or the + // FQDN version of the host + bool responseMatchesQuestion = + (qname.Length() == aHost.Length() || + (aHost.Length() == qname.Length() + 1 && aHost.Last() == '.')) && + qname.Compare(aHost.BeginReading(), true, qname.Length()) == 0; + + if (responseMatchesQuestion) { + // RDATA + // - A (TYPE 1): 4 bytes + // - AAAA (TYPE 28): 16 bytes + // - NS (TYPE 2): N bytes + + switch (TYPE) { + case TRRTYPE_A: + if (RDLENGTH != 4) { + LOG(("TRR bad length for A (%u)\n", RDLENGTH)); + return NS_ERROR_UNEXPECTED; + } + rv = aResp.Add(TTL, mResponse, index, RDLENGTH, aAllowRFC1918); + if (NS_FAILED(rv)) { + LOG( + ("TRR:DohDecode failed: local IP addresses or unknown IP " + "family\n")); + return rv; + } + break; + case TRRTYPE_AAAA: + if (RDLENGTH != 16) { + LOG(("TRR bad length for AAAA (%u)\n", RDLENGTH)); + return NS_ERROR_UNEXPECTED; + } + rv = aResp.Add(TTL, mResponse, index, RDLENGTH, aAllowRFC1918); + if (NS_FAILED(rv)) { + LOG(("TRR got unique/local IPv6 address!\n")); + return rv; + } + break; + + case TRRTYPE_NS: + break; + case TRRTYPE_CNAME: + if (aCname.IsEmpty()) { + nsAutoCString qname; + unsigned int qnameindex = index; + rv = GetQname(qname, qnameindex); + if (NS_FAILED(rv)) { + return rv; + } + if (!qname.IsEmpty()) { + ToLowerCase(qname); + aCname = qname; + LOG(("DNSPacket::DohDecode CNAME host %s => %s\n", host.get(), + aCname.get())); + } else { + LOG(("DNSPacket::DohDecode empty CNAME for host %s!\n", + host.get())); + } + } else { + LOG(("DNSPacket::DohDecode CNAME - ignoring another entry\n")); + } + break; + case TRRTYPE_TXT: { + // TXT record RRDATA sections are a series of character-strings + // each character string is a length byte followed by that many data + // bytes + nsAutoCString txt; + unsigned int txtIndex = index; + uint16_t available = RDLENGTH; + + while (available > 0) { + uint8_t characterStringLen = mResponse[txtIndex++]; + available--; + if (characterStringLen > available) { + LOG(("DNSPacket::DohDecode MALFORMED TXT RECORD\n")); + break; + } + txt.Append((const char*)(&mResponse[txtIndex]), characterStringLen); + txtIndex += characterStringLen; + available -= characterStringLen; + } + + if (!aTypeResult.is<TypeRecordTxt>()) { + aTypeResult = AsVariant(CopyableTArray<nsCString>()); + } + + { + auto& results = aTypeResult.as<TypeRecordTxt>(); + results.AppendElement(txt); + } + if (aTTL > TTL) { + aTTL = TTL; + } + LOG(("DNSPacket::DohDecode TXT host %s => %s\n", host.get(), + txt.get())); + + break; + } + case TRRTYPE_HTTPSSVC: { + struct SVCB parsed; + int32_t lastSvcParamKey = -1; + + unsigned int svcbIndex = index; + CheckedInt<uint16_t> available = RDLENGTH; + + // Should have at least 2 bytes for the priority and one for the + // qname length. + if (available.value() < 3) { + return NS_ERROR_UNEXPECTED; + } + + parsed.mSvcFieldPriority = get16bit(mResponse, svcbIndex); + svcbIndex += 2; + + rv = GetQname(parsed.mSvcDomainName, svcbIndex); + if (NS_FAILED(rv)) { + return rv; + } + + if (parsed.mSvcDomainName.IsEmpty()) { + if (parsed.mSvcFieldPriority == 0) { + // For AliasMode SVCB RRs, a TargetName of "." indicates that the + // service is not available or does not exist. + continue; + } + + // For ServiceMode SVCB RRs, if TargetName has the value ".", + // then the owner name of this record MUST be used as + // the effective TargetName. + parsed.mSvcDomainName = qname; + } + + available -= (svcbIndex - index); + if (!available.isValid()) { + return NS_ERROR_UNEXPECTED; + } + while (available.value() >= 4) { + // Every SvcFieldValues must have at least 4 bytes for the + // SvcParamKey (2 bytes) and length of SvcParamValue (2 bytes) + // If the length ever goes above the available data, meaning if + // available ever underflows, then that is an error. + struct SvcFieldValue value; + uint16_t key = get16bit(mResponse, svcbIndex); + svcbIndex += 2; + + // 2.2 Clients MUST consider an RR malformed if SvcParamKeys are + // not in strictly increasing numeric order. + if (key <= lastSvcParamKey) { + LOG(("SvcParamKeys not in increasing order")); + return NS_ERROR_UNEXPECTED; + } + lastSvcParamKey = key; + + uint16_t len = get16bit(mResponse, svcbIndex); + svcbIndex += 2; + + available -= 4 + len; + if (!available.isValid()) { + return NS_ERROR_UNEXPECTED; + } + + rv = ParseSvcParam(svcbIndex, key, value, len); + if (NS_FAILED(rv)) { + return rv; + } + svcbIndex += len; + + // If this is an unknown key, we will simply ignore it. + // We also don't need to record SvcParamKeyMandatory + if (key == SvcParamKeyMandatory || !IsValidSvcParamKey(key)) { + continue; + } + + if (value.mValue.is<SvcParamIpv4Hint>() || + value.mValue.is<SvcParamIpv6Hint>()) { + parsed.mHasIPHints = true; + } + if (value.mValue.is<SvcParamEchConfig>()) { + parsed.mHasEchConfig = true; + parsed.mEchConfig = value.mValue.as<SvcParamEchConfig>().mValue; + } + if (value.mValue.is<SvcParamODoHConfig>()) { + parsed.mODoHConfig = value.mValue.as<SvcParamODoHConfig>().mValue; + } + parsed.mSvcFieldValue.AppendElement(value); + } + + // Check for AliasForm + if (aCname.IsEmpty() && parsed.mSvcFieldPriority == 0) { + // Alias form SvcDomainName must not have the "." value (empty) + if (parsed.mSvcDomainName.IsEmpty()) { + return NS_ERROR_UNEXPECTED; + } + aCname = parsed.mSvcDomainName; + ToLowerCase(aCname); + LOG(("DNSPacket::DohDecode HTTPSSVC AliasForm host %s => %s\n", + host.get(), aCname.get())); + break; + } + + if (aType != TRRTYPE_HTTPSSVC) { + // Ignore the entry that we just parsed if we didn't ask for it. + break; + } + + if (!aTypeResult.is<TypeRecordHTTPSSVC>()) { + aTypeResult = mozilla::AsVariant(CopyableTArray<SVCB>()); + } + { + auto& results = aTypeResult.as<TypeRecordHTTPSSVC>(); + results.AppendElement(parsed); + } + break; + } + default: + // skip unknown record types + LOG(("TRR unsupported TYPE (%u) RDLENGTH %u\n", TYPE, RDLENGTH)); + break; + } + } else { + LOG(("TRR asked for %s data but got %s\n", aHost.get(), qname.get())); + } + + index += RDLENGTH; + LOG(("done with record type %u len %u index now %u of %u\n", TYPE, RDLENGTH, + index, mBodySize)); + answerRecords--; + } + + // NSCOUNT + uint16_t nsRecords = get16bit(mResponse, 8); + LOG(("TRR Decode: %d ns records (%u bytes body)\n", nsRecords, mBodySize)); + while (nsRecords) { + rv = PassQName(index); + if (NS_FAILED(rv)) { + return rv; + } + + if (mBodySize < (index + 8)) { + return NS_ERROR_ILLEGAL_VALUE; + } + index += 2; // type + index += 2; // class + index += 4; // ttl + + // 16 bit RDLENGTH + if (mBodySize < (index + 2)) { + return NS_ERROR_ILLEGAL_VALUE; + } + uint16_t RDLENGTH = get16bit(mResponse, index); + index += 2; + if (mBodySize < (index + RDLENGTH)) { + return NS_ERROR_ILLEGAL_VALUE; + } + index += RDLENGTH; + LOG(("done with nsRecord now %u of %u\n", index, mBodySize)); + nsRecords--; + } + + // additional resource records + uint16_t arRecords = get16bit(mResponse, 10); + LOG(("TRR Decode: %d additional resource records (%u bytes body)\n", + arRecords, mBodySize)); + + while (arRecords) { + nsAutoCString qname; + rv = GetQname(qname, index); + if (NS_FAILED(rv)) { + LOG(("Bad qname for additional record")); + return rv; + } + + if (mBodySize < (index + 8)) { + return NS_ERROR_ILLEGAL_VALUE; + } + uint16_t type = get16bit(mResponse, index); + index += 2; + // The next two bytes encode class + // (or udpPayloadSize when type is TRRTYPE_OPT) + uint16_t cls = get16bit(mResponse, index); + index += 2; + // The next 4 bytes encode TTL + // (or extRCode + ednsVersion + flags when type is TRRTYPE_OPT) + uint32_t ttl = get32bit(mResponse, index); + index += 4; + // cls and ttl are unused when type is TRRTYPE_OPT + + // 16 bit RDLENGTH + if (mBodySize < (index + 2)) { + LOG(("Record too small")); + return NS_ERROR_ILLEGAL_VALUE; + } + + uint16_t rdlength = get16bit(mResponse, index); + index += 2; + if (mBodySize < (index + rdlength)) { + LOG(("rdlength too big")); + return NS_ERROR_ILLEGAL_VALUE; + } + + auto parseRecord = [&]() { + LOG(("Parsing additional record type: %u", type)); + auto& entry = aAdditionalRecords.GetOrInsert(qname); + if (!entry) { + entry.reset(new DOHresp()); + } + + switch (type) { + case TRRTYPE_A: + if (kDNS_CLASS_IN != cls) { + LOG(("NOT IN - returning")); + return; + } + if (rdlength != 4) { + LOG(("TRR bad length for A (%u)\n", rdlength)); + return; + } + rv = entry->Add(ttl, mResponse, index, rdlength, aAllowRFC1918); + if (NS_FAILED(rv)) { + LOG( + ("TRR:DohDecode failed: local IP addresses or unknown IP " + "family\n")); + return; + } + break; + case TRRTYPE_AAAA: + if (kDNS_CLASS_IN != cls) { + LOG(("NOT IN - returning")); + return; + } + if (rdlength != 16) { + LOG(("TRR bad length for AAAA (%u)\n", rdlength)); + return; + } + rv = entry->Add(ttl, mResponse, index, rdlength, aAllowRFC1918); + if (NS_FAILED(rv)) { + LOG(("TRR got unique/local IPv6 address!\n")); + return; + } + break; + case TRRTYPE_OPT: { // OPT + LOG(("Parsing opt rdlen: %u", rdlength)); + unsigned int offset = 0; + while (offset + 2 <= rdlength) { + uint16_t optCode = get16bit(mResponse, index + offset); + LOG(("optCode: %u", optCode)); + offset += 2; + if (offset + 2 > rdlength) { + break; + } + uint16_t optLen = get16bit(mResponse, index + offset); + LOG(("optLen: %u", optLen)); + offset += 2; + if (offset + optLen > rdlength) { + LOG(("offset: %u, optLen: %u, rdlen: %u", offset, optLen, + rdlength)); + break; + } + + LOG(("OPT: code: %u len:%u", optCode, optLen)); + + if (optCode != 15) { + offset += optLen; + continue; + } + + // optCode == 15; Extended DNS error + + if (offset + 2 > rdlength || optLen < 2) { + break; + } + extendedError = get16bit(mResponse, index + offset); + + LOG(( + "Extended error code: %u message: %s", extendedError, + nsAutoCString((char*)mResponse + index + offset + 2, optLen - 2) + .get())); + offset += optLen; + } + break; + } + default: + break; + } + }; + + parseRecord(); + + index += rdlength; + LOG(("done with additional rr now %u of %u\n", index, mBodySize)); + arRecords--; + } + + if (index != mBodySize) { + LOG(("DohDecode failed to parse entire response body, %u out of %u bytes\n", + index, mBodySize)); + // failed to parse 100%, do not continue + return NS_ERROR_ILLEGAL_VALUE; + } + + if ((aType != TRRTYPE_NS) && aCname.IsEmpty() && aResp.mAddresses.IsEmpty() && + aTypeResult.is<TypeRecordEmpty>()) { + // no entries were stored! + LOG(("TRR: No entries were stored!\n")); + if (extendedError != UINT16_MAX && hardFail(extendedError)) { + return NS_ERROR_DEFINITIVE_UNKNOWN_HOST; + } + return NS_ERROR_FAILURE; + } + + // https://tools.ietf.org/html/draft-ietf-dnsop-svcb-httpssvc-03#page-14 + // If one or more SVCB records of ServiceForm SvcRecordType are returned for + // HOST, clients should select the highest-priority option with acceptable + // parameters. + if (aTypeResult.is<TypeRecordHTTPSSVC>()) { + auto& results = aTypeResult.as<TypeRecordHTTPSSVC>(); + results.Sort(); + } + + return NS_OK; +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/DNSPacket.h b/netwerk/dns/DNSPacket.h new file mode 100644 index 0000000000..93caed20d0 --- /dev/null +++ b/netwerk/dns/DNSPacket.h @@ -0,0 +1,71 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_DNSPacket_h__ +#define mozilla_net_DNSPacket_h__ + +#include "mozilla/Result.h" +#include "nsHostResolver.h" + +namespace mozilla { +namespace net { + +class DOHresp { + public: + nsresult Add(uint32_t TTL, unsigned char const* dns, unsigned int index, + uint16_t len, bool aLocalAllowed); + nsTArray<NetAddr> mAddresses; + uint32_t mTtl = UINT32_MAX; +}; + +// the values map to RFC1035 type identifiers +enum TrrType { + TRRTYPE_A = 1, + TRRTYPE_NS = 2, + TRRTYPE_CNAME = 5, + TRRTYPE_AAAA = 28, + TRRTYPE_OPT = 41, + TRRTYPE_TXT = 16, + TRRTYPE_HTTPSSVC = nsIDNSService::RESOLVE_TYPE_HTTPSSVC, // 65 +}; + +class DNSPacket { + public: + // Called in order to feed data into the buffer. + nsresult OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream, + uint64_t aOffset, const uint32_t aCount); + + // Encodes the name request into a buffer that represents a DNS packet + static nsresult EncodeRequest(nsCString& aBody, const nsACString& aHost, + uint16_t aType, bool aDisableECS); + + // Decodes the DNS response and extracts the responses, additional records, + // etc. XXX: This should probably be refactored to reduce the number of + // output parameters and have a common format for different record types. + nsresult Decode( + nsCString& aHost, enum TrrType aType, nsCString& aCname, + bool aAllowRFC1918, nsHostRecord::TRRSkippedReason& reason, + DOHresp& aResp, TypeRecordResultType& aTypeResult, + nsClassHashtable<nsCStringHashKey, DOHresp>& aAdditionalRecords, + uint32_t& aTTL); + + private: + // Never accept larger DOH responses than this as that would indicate + // something is wrong. Typical ones are much smaller. + static const unsigned int MAX_SIZE = 3200; + + nsresult PassQName(unsigned int& index); + nsresult GetQname(nsACString& aQname, unsigned int& aIndex); + nsresult ParseSvcParam(unsigned int svcbIndex, uint16_t key, + SvcFieldValue& field, uint16_t length); + + // The response buffer. + unsigned char mResponse[MAX_SIZE]{}; + unsigned int mBodySize = 0; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_DNSPacket_h__ diff --git a/netwerk/dns/DNSRequestBase.h b/netwerk/dns/DNSRequestBase.h new file mode 100644 index 0000000000..a46eb1e4e2 --- /dev/null +++ b/netwerk/dns/DNSRequestBase.h @@ -0,0 +1,148 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_DNSRequestBase_h +#define mozilla_net_DNSRequestBase_h + +#include "mozilla/net/PDNSRequestParent.h" +#include "nsICancelable.h" +#include "nsIDNSRecord.h" +#include "nsIDNSListener.h" +#include "nsIDNSByTypeRecord.h" +#include "nsIEventTarget.h" + +namespace mozilla { +namespace net { + +class DNSRequestActor; +class DNSRequestChild; +class DNSRequestHandler; +class DNSRequestParent; +class DNSRequestSender; + +// A base class for DNSRequestSender and DNSRequestHandler. +// Provide interfaces for processing DNS requests. +class DNSRequestBase : public nsISupports { + public: + explicit DNSRequestBase() = default; + + void SetIPCActor(DNSRequestActor* aActor); + + virtual void OnRecvCancelDNSRequest(const nsCString& hostName, + const nsCString& trrServer, + const uint16_t& type, + const OriginAttributes& originAttributes, + const uint32_t& flags, + const nsresult& reason) = 0; + virtual bool OnRecvLookupCompleted(const DNSRequestResponse& reply) = 0; + virtual void OnIPCActorDestroy() = 0; + + virtual DNSRequestSender* AsDNSRequestSender() = 0; + virtual DNSRequestHandler* AsDNSRequestHandler() = 0; + + protected: + virtual ~DNSRequestBase() = default; + + RefPtr<DNSRequestActor> mIPCActor; +}; + +// DNSRequestSender is used to send an IPC request to DNSRequestHandler and +// deliver the result to nsIDNSListener. +// Note this class could be used both in content process and parent process. +class DNSRequestSender final : public DNSRequestBase, public nsICancelable { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSICANCELABLE + + DNSRequestSender(const nsACString& aHost, const nsACString& aTrrServer, + const uint16_t& aType, + const OriginAttributes& aOriginAttributes, + const uint32_t& aFlags, nsIDNSListener* aListener, + nsIEventTarget* target); + + void OnRecvCancelDNSRequest(const nsCString& hostName, + const nsCString& trrServer, const uint16_t& type, + const OriginAttributes& originAttributes, + const uint32_t& flags, + const nsresult& reason) override; + bool OnRecvLookupCompleted(const DNSRequestResponse& reply) override; + void OnIPCActorDestroy() override; + + // Sends IPDL request to DNSRequestHandler + void StartRequest(); + void CallOnLookupComplete(); + + DNSRequestSender* AsDNSRequestSender() override { return this; } + DNSRequestHandler* AsDNSRequestHandler() override { return nullptr; } + + private: + friend class ChildDNSService; + virtual ~DNSRequestSender() = default; + + nsCOMPtr<nsIDNSListener> mListener; + nsCOMPtr<nsIEventTarget> mTarget; + nsCOMPtr<nsIDNSRecord> mResultRecord; + nsresult mResultStatus; + nsCString mHost; + nsCString mTrrServer; + uint16_t mType; + const OriginAttributes mOriginAttributes; + uint16_t mFlags; +}; + +// DNSRequestHandler handles the dns request and sends the result back via IPC. +// Note this class could be used both in parent process and socket process. +class DNSRequestHandler final : public DNSRequestBase, public nsIDNSListener { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIDNSLISTENER + + DNSRequestHandler(); + + void DoAsyncResolve(const nsACString& hostname, const nsACString& trrServer, + uint16_t type, const OriginAttributes& originAttributes, + uint32_t flags); + + void OnRecvCancelDNSRequest(const nsCString& hostName, + const nsCString& trrServer, const uint16_t& type, + const OriginAttributes& originAttributes, + const uint32_t& flags, + const nsresult& reason) override; + bool OnRecvLookupCompleted(const DNSRequestResponse& reply) override; + void OnIPCActorDestroy() override; + + DNSRequestSender* AsDNSRequestSender() override { return nullptr; } + DNSRequestHandler* AsDNSRequestHandler() override { return this; } + + private: + virtual ~DNSRequestHandler() = default; + + uint32_t mFlags; +}; + +// Provides some common methods for DNSRequestChild and DNSRequestParent. +class DNSRequestActor { + public: + NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING + + explicit DNSRequestActor(DNSRequestBase* aRequest) : mDNSRequest(aRequest) {} + + virtual bool CanSend() const = 0; + virtual DNSRequestChild* AsDNSRequestChild() = 0; + virtual DNSRequestParent* AsDNSRequestParent() = 0; + + DNSRequestBase* GetDNSRequest() { return mDNSRequest.get(); }; + + protected: + virtual ~DNSRequestActor() = default; + + RefPtr<DNSRequestBase> mDNSRequest; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_DNSRequestBase_h diff --git a/netwerk/dns/DNSRequestChild.cpp b/netwerk/dns/DNSRequestChild.cpp new file mode 100644 index 0000000000..b25a9ee03e --- /dev/null +++ b/netwerk/dns/DNSRequestChild.cpp @@ -0,0 +1,536 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/ContentChild.h" +#include "mozilla/net/ChildDNSService.h" +#include "mozilla/net/DNSByTypeRecord.h" +#include "mozilla/net/DNSRequestChild.h" +#include "mozilla/net/DNSRequestParent.h" +#include "mozilla/net/NeckoChild.h" +#include "mozilla/net/SocketProcessChild.h" +#include "mozilla/SchedulerGroup.h" +#include "mozilla/net/SocketProcessParent.h" +#include "mozilla/Unused.h" +#include "nsIDNSRecord.h" +#include "nsIDNSByTypeRecord.h" +#include "nsHostResolver.h" +#include "nsIOService.h" +#include "nsTArray.h" +#include "nsNetAddr.h" +#include "nsThreadUtils.h" + +using namespace mozilla::ipc; + +namespace mozilla { +namespace net { + +void DNSRequestBase::SetIPCActor(DNSRequestActor* aActor) { + mIPCActor = aActor; +} + +//----------------------------------------------------------------------------- +// ChildDNSRecord: +// A simple class to provide nsIDNSRecord on the child +//----------------------------------------------------------------------------- + +class ChildDNSRecord : public nsIDNSAddrRecord { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIDNSRECORD + NS_DECL_NSIDNSADDRRECORD + + ChildDNSRecord(const DNSRecord& reply, uint16_t flags); + + private: + virtual ~ChildDNSRecord() = default; + + nsCString mCanonicalName; + nsTArray<NetAddr> mAddresses; + uint32_t mCurrent; // addr iterator + uint16_t mFlags; + double mTrrFetchDuration; + double mTrrFetchDurationNetworkOnly; + bool mIsTRR; + uint32_t mEffectiveTRRMode; +}; + +NS_IMPL_ISUPPORTS(ChildDNSRecord, nsIDNSRecord, nsIDNSAddrRecord) + +ChildDNSRecord::ChildDNSRecord(const DNSRecord& reply, uint16_t flags) + : mCurrent(0), mFlags(flags) { + mCanonicalName = reply.canonicalName(); + mTrrFetchDuration = reply.trrFetchDuration(); + mTrrFetchDurationNetworkOnly = reply.trrFetchDurationNetworkOnly(); + mIsTRR = reply.isTRR(); + mEffectiveTRRMode = reply.effectiveTRRMode(); + + // A shame IPDL gives us no way to grab ownership of array: so copy it. + const nsTArray<NetAddr>& addrs = reply.addrs(); + mAddresses = addrs.Clone(); +} + +//----------------------------------------------------------------------------- +// ChildDNSRecord::nsIDNSAddrRecord +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +ChildDNSRecord::GetCanonicalName(nsACString& result) { + if (!(mFlags & nsHostResolver::RES_CANON_NAME)) { + return NS_ERROR_NOT_AVAILABLE; + } + + result = mCanonicalName; + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSRecord::IsTRR(bool* retval) { + *retval = mIsTRR; + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSRecord::GetTrrFetchDuration(double* aTime) { + *aTime = mTrrFetchDuration; + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSRecord::GetTrrFetchDurationNetworkOnly(double* aTime) { + *aTime = mTrrFetchDurationNetworkOnly; + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSRecord::GetNextAddr(uint16_t port, NetAddr* addr) { + if (mCurrent >= mAddresses.Length()) { + return NS_ERROR_NOT_AVAILABLE; + } + + memcpy(addr, &mAddresses[mCurrent++], sizeof(NetAddr)); + + // both Ipv4/6 use same bits for port, so safe to just use ipv4's field + addr->inet.port = htons(port); + + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSRecord::GetAddresses(nsTArray<NetAddr>& aAddressArray) { + aAddressArray = mAddresses.Clone(); + return NS_OK; +} + +// shamelessly copied from nsDNSRecord +NS_IMETHODIMP +ChildDNSRecord::GetScriptableNextAddr(uint16_t port, nsINetAddr** result) { + NetAddr addr; + nsresult rv = GetNextAddr(port, &addr); + if (NS_FAILED(rv)) { + return rv; + } + + RefPtr<nsNetAddr> netaddr = new nsNetAddr(&addr); + netaddr.forget(result); + + return NS_OK; +} + +// also copied from nsDNSRecord +NS_IMETHODIMP +ChildDNSRecord::GetNextAddrAsString(nsACString& result) { + NetAddr addr; + nsresult rv = GetNextAddr(0, &addr); + if (NS_FAILED(rv)) { + return rv; + } + + char buf[kIPv6CStrBufSize]; + if (addr.ToStringBuffer(buf, sizeof(buf))) { + result.Assign(buf); + return NS_OK; + } + NS_ERROR("NetAddrToString failed unexpectedly"); + return NS_ERROR_FAILURE; // conversion failed for some reason +} + +NS_IMETHODIMP +ChildDNSRecord::HasMore(bool* result) { + *result = mCurrent < mAddresses.Length(); + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSRecord::Rewind() { + mCurrent = 0; + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSRecord::ReportUnusable(uint16_t aPort) { + // "We thank you for your feedback" == >/dev/null + // TODO: we could send info back to parent. + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSRecord::GetEffectiveTRRMode(uint32_t* aMode) { + *aMode = mEffectiveTRRMode; + return NS_OK; +} + +class ChildDNSByTypeRecord : public nsIDNSByTypeRecord, + public nsIDNSTXTRecord, + public nsIDNSHTTPSSVCRecord, + public DNSHTTPSSVCRecordBase { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIDNSRECORD + NS_DECL_NSIDNSBYTYPERECORD + NS_DECL_NSIDNSTXTRECORD + NS_DECL_NSIDNSHTTPSSVCRECORD + + explicit ChildDNSByTypeRecord(const TypeRecordResultType& reply, + const nsACString& aHost); + + private: + virtual ~ChildDNSByTypeRecord() = default; + + TypeRecordResultType mResults = AsVariant(mozilla::Nothing()); + bool mAllRecordsExcluded = false; +}; + +NS_IMPL_ISUPPORTS(ChildDNSByTypeRecord, nsIDNSByTypeRecord, nsIDNSRecord, + nsIDNSTXTRecord, nsIDNSHTTPSSVCRecord) + +ChildDNSByTypeRecord::ChildDNSByTypeRecord(const TypeRecordResultType& reply, + const nsACString& aHost) + : DNSHTTPSSVCRecordBase(aHost), mAllRecordsExcluded(false) { + mResults = reply; +} + +NS_IMETHODIMP +ChildDNSByTypeRecord::GetType(uint32_t* aType) { + *aType = mResults.match( + [](TypeRecordEmpty&) { + MOZ_ASSERT(false, "This should never be the case"); + return nsIDNSService::RESOLVE_TYPE_DEFAULT; + }, + [](TypeRecordTxt&) { return nsIDNSService::RESOLVE_TYPE_TXT; }, + [](TypeRecordHTTPSSVC&) { return nsIDNSService::RESOLVE_TYPE_HTTPSSVC; }); + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSByTypeRecord::GetRecords(CopyableTArray<nsCString>& aRecords) { + if (!mResults.is<TypeRecordTxt>()) { + return NS_ERROR_NOT_AVAILABLE; + } + aRecords = mResults.as<CopyableTArray<nsCString>>(); + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSByTypeRecord::GetRecordsAsOneString(nsACString& aRecords) { + // deep copy + if (!mResults.is<TypeRecordTxt>()) { + return NS_ERROR_NOT_AVAILABLE; + } + auto& results = mResults.as<CopyableTArray<nsCString>>(); + for (uint32_t i = 0; i < results.Length(); i++) { + aRecords.Append(results[i]); + } + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSByTypeRecord::GetRecords(nsTArray<RefPtr<nsISVCBRecord>>& aRecords) { + if (!mResults.is<TypeRecordHTTPSSVC>()) { + return NS_ERROR_NOT_AVAILABLE; + } + + auto& results = mResults.as<TypeRecordHTTPSSVC>(); + + for (const SVCB& r : results) { + RefPtr<nsISVCBRecord> rec = new SVCBRecord(r); + aRecords.AppendElement(rec); + } + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSByTypeRecord::GetServiceModeRecord(bool aNoHttp2, bool aNoHttp3, + nsISVCBRecord** aRecord) { + if (!mResults.is<TypeRecordHTTPSSVC>()) { + return NS_ERROR_NOT_AVAILABLE; + } + + auto& results = mResults.as<TypeRecordHTTPSSVC>(); + nsCOMPtr<nsISVCBRecord> result = GetServiceModeRecordInternal( + aNoHttp2, aNoHttp3, results, mAllRecordsExcluded); + if (!result) { + return NS_ERROR_NOT_AVAILABLE; + } + + result.forget(aRecord); + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSByTypeRecord::GetAllRecordsWithEchConfig( + bool aNoHttp2, bool aNoHttp3, bool* aAllRecordsHaveEchConfig, + bool* aAllRecordsInH3ExcludedList, + nsTArray<RefPtr<nsISVCBRecord>>& aResult) { + if (!mResults.is<TypeRecordHTTPSSVC>()) { + return NS_ERROR_NOT_AVAILABLE; + } + + auto& records = mResults.as<TypeRecordHTTPSSVC>(); + GetAllRecordsWithEchConfigInternal(aNoHttp2, aNoHttp3, records, + aAllRecordsHaveEchConfig, + aAllRecordsInH3ExcludedList, aResult); + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSByTypeRecord::GetHasIPAddresses(bool* aResult) { + NS_ENSURE_ARG(aResult); + + if (!mResults.is<TypeRecordHTTPSSVC>()) { + return NS_ERROR_NOT_AVAILABLE; + } + + auto& results = mResults.as<TypeRecordHTTPSSVC>(); + *aResult = HasIPAddressesInternal(results); + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSByTypeRecord::GetAllRecordsExcluded(bool* aResult) { + NS_ENSURE_ARG(aResult); + + if (!mResults.is<TypeRecordHTTPSSVC>()) { + return NS_ERROR_NOT_AVAILABLE; + } + + *aResult = mAllRecordsExcluded; + return NS_OK; +} + +NS_IMETHODIMP +ChildDNSByTypeRecord::GetResults(mozilla::net::TypeRecordResultType* aResults) { + *aResults = mResults; + return NS_OK; +} + +//----------------------------------------------------------------------------- +// DNSRequestSender +//----------------------------------------------------------------------------- + +NS_IMPL_ISUPPORTS(DNSRequestSender, nsICancelable) + +DNSRequestSender::DNSRequestSender( + const nsACString& aHost, const nsACString& aTrrServer, + const uint16_t& aType, const OriginAttributes& aOriginAttributes, + const uint32_t& aFlags, nsIDNSListener* aListener, nsIEventTarget* target) + : mListener(aListener), + mTarget(target), + mResultStatus(NS_OK), + mHost(aHost), + mTrrServer(aTrrServer), + mType(aType), + mOriginAttributes(aOriginAttributes), + mFlags(aFlags) {} + +void DNSRequestSender::OnRecvCancelDNSRequest( + const nsCString& hostName, const nsCString& trrServer, const uint16_t& type, + const OriginAttributes& originAttributes, const uint32_t& flags, + const nsresult& reason) {} + +NS_IMETHODIMP +DNSRequestSender::Cancel(nsresult reason) { + if (!mIPCActor) { + return NS_ERROR_NOT_AVAILABLE; + } + + if (mIPCActor->CanSend()) { + // We can only do IPDL on the main thread + nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction( + "net::CancelDNSRequestEvent", + [actor(mIPCActor), host(mHost), trrServer(mTrrServer), type(mType), + originAttributes(mOriginAttributes), flags(mFlags), reason]() { + if (!actor->CanSend()) { + return; + } + + if (DNSRequestChild* child = actor->AsDNSRequestChild()) { + Unused << child->SendCancelDNSRequest( + host, trrServer, type, originAttributes, flags, reason); + } else if (DNSRequestParent* parent = actor->AsDNSRequestParent()) { + Unused << parent->SendCancelDNSRequest( + host, trrServer, type, originAttributes, flags, reason); + } + }); + SchedulerGroup::Dispatch(TaskCategory::Other, runnable.forget()); + } + return NS_OK; +} + +void DNSRequestSender::StartRequest() { + // we can only do IPDL on the main thread + if (!NS_IsMainThread()) { + SchedulerGroup::Dispatch( + TaskCategory::Other, + NewRunnableMethod("net::DNSRequestSender::StartRequest", this, + &DNSRequestSender::StartRequest)); + return; + } + + if (DNSRequestChild* child = mIPCActor->AsDNSRequestChild()) { + if (XRE_IsContentProcess()) { + mozilla::dom::ContentChild* cc = + static_cast<mozilla::dom::ContentChild*>(gNeckoChild->Manager()); + if (cc->IsShuttingDown()) { + return; + } + + // Send request to Parent process. + gNeckoChild->SendPDNSRequestConstructor(child, mHost, mTrrServer, mType, + mOriginAttributes, mFlags); + } else if (XRE_IsSocketProcess()) { + // DNS resolution is done in the parent process. Send a DNS request to + // parent process. + MOZ_ASSERT(!nsIOService::UseSocketProcess()); + + SocketProcessChild* socketProcessChild = + SocketProcessChild::GetSingleton(); + if (!socketProcessChild->CanSend()) { + return; + } + + socketProcessChild->SendPDNSRequestConstructor( + child, mHost, mTrrServer, mType, mOriginAttributes, mFlags); + } else { + MOZ_ASSERT(false, "Wrong process"); + return; + } + } else if (DNSRequestParent* parent = mIPCActor->AsDNSRequestParent()) { + // DNS resolution is done in the socket process. Send a DNS request to + // socket process. + MOZ_ASSERT(nsIOService::UseSocketProcess()); + + RefPtr<DNSRequestParent> requestParent = parent; + RefPtr<DNSRequestSender> self = this; + auto task = [requestParent, self]() { + Unused << SocketProcessParent::GetSingleton()->SendPDNSRequestConstructor( + requestParent, self->mHost, self->mTrrServer, self->mType, + self->mOriginAttributes, self->mFlags); + }; + if (!gIOService->SocketProcessReady()) { + gIOService->CallOrWaitForSocketProcess(std::move(task)); + return; + } + + task(); + } +} + +void DNSRequestSender::CallOnLookupComplete() { + MOZ_ASSERT(mListener); + mListener->OnLookupComplete(this, mResultRecord, mResultStatus); +} + +bool DNSRequestSender::OnRecvLookupCompleted(const DNSRequestResponse& reply) { + MOZ_ASSERT(mListener); + + switch (reply.type()) { + case DNSRequestResponse::TDNSRecord: { + mResultRecord = new ChildDNSRecord(reply.get_DNSRecord(), mFlags); + break; + } + case DNSRequestResponse::Tnsresult: { + mResultStatus = reply.get_nsresult(); + break; + } + case DNSRequestResponse::TIPCTypeRecord: { + MOZ_ASSERT(mType != nsIDNSService::RESOLVE_TYPE_DEFAULT); + mResultRecord = + new ChildDNSByTypeRecord(reply.get_IPCTypeRecord().mData, mHost); + break; + } + default: + MOZ_ASSERT_UNREACHABLE("unknown type"); + return false; + } + + MOZ_ASSERT(NS_IsMainThread()); + + bool targetIsMain = false; + if (!mTarget) { + targetIsMain = true; + } else { + mTarget->IsOnCurrentThread(&targetIsMain); + } + + if (targetIsMain) { + CallOnLookupComplete(); + } else { + nsCOMPtr<nsIRunnable> event = + NewRunnableMethod("net::DNSRequestSender::CallOnLookupComplete", this, + &DNSRequestSender::CallOnLookupComplete); + mTarget->Dispatch(event, NS_DISPATCH_NORMAL); + } + + if (DNSRequestChild* child = mIPCActor->AsDNSRequestChild()) { + Unused << mozilla::net::DNSRequestChild::Send__delete__(child); + } else if (DNSRequestParent* parent = mIPCActor->AsDNSRequestParent()) { + Unused << mozilla::net::DNSRequestParent::Send__delete__(parent); + } + + return true; +} + +void DNSRequestSender::OnIPCActorDestroy() { + // Request is done or destroyed. Remove it from the hash table. + RefPtr<ChildDNSService> dnsServiceChild = + dont_AddRef(ChildDNSService::GetSingleton()); + dnsServiceChild->NotifyRequestDone(this); + + mIPCActor = nullptr; +} + +//----------------------------------------------------------------------------- +// DNSRequestChild +//----------------------------------------------------------------------------- + +DNSRequestChild::DNSRequestChild(DNSRequestBase* aRequest) + : DNSRequestActor(aRequest) { + aRequest->SetIPCActor(this); +} + +mozilla::ipc::IPCResult DNSRequestChild::RecvCancelDNSRequest( + const nsCString& hostName, const nsCString& trrServer, const uint16_t& type, + const OriginAttributes& originAttributes, const uint32_t& flags, + const nsresult& reason) { + mDNSRequest->OnRecvCancelDNSRequest(hostName, trrServer, type, + originAttributes, flags, reason); + return IPC_OK(); +} + +mozilla::ipc::IPCResult DNSRequestChild::RecvLookupCompleted( + const DNSRequestResponse& reply) { + return mDNSRequest->OnRecvLookupCompleted(reply) ? IPC_OK() + : IPC_FAIL_NO_REASON(this); +} + +void DNSRequestChild::ActorDestroy(ActorDestroyReason) { + mDNSRequest->OnIPCActorDestroy(); + mDNSRequest = nullptr; +} + +//------------------------------------------------------------------------------ +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/DNSRequestChild.h b/netwerk/dns/DNSRequestChild.h new file mode 100644 index 0000000000..c360b8d58b --- /dev/null +++ b/netwerk/dns/DNSRequestChild.h @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_DNSRequestChild_h +#define mozilla_net_DNSRequestChild_h + +#include "mozilla/net/DNSRequestBase.h" +#include "mozilla/net/PDNSRequestChild.h" + +namespace mozilla { +namespace net { + +class DNSRequestChild final : public DNSRequestActor, public PDNSRequestChild { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DNSRequestChild, override) + friend class PDNSRequestChild; + + explicit DNSRequestChild(DNSRequestBase* aRequest); + + bool CanSend() const override { return PDNSRequestChild::CanSend(); } + DNSRequestChild* AsDNSRequestChild() override { return this; } + DNSRequestParent* AsDNSRequestParent() override { return nullptr; } + + private: + virtual ~DNSRequestChild() = default; + + mozilla::ipc::IPCResult RecvCancelDNSRequest( + const nsCString& hostName, const nsCString& trrServer, + const uint16_t& type, const OriginAttributes& originAttributes, + const uint32_t& flags, const nsresult& reason); + mozilla::ipc::IPCResult RecvLookupCompleted(const DNSRequestResponse& reply); + virtual void ActorDestroy(ActorDestroyReason why) override; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_DNSRequestChild_h diff --git a/netwerk/dns/DNSRequestParent.cpp b/netwerk/dns/DNSRequestParent.cpp new file mode 100644 index 0000000000..0159745a6e --- /dev/null +++ b/netwerk/dns/DNSRequestParent.cpp @@ -0,0 +1,178 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/net/DNSRequestParent.h" +#include "mozilla/net/DNSRequestChild.h" +#include "nsIDNSService.h" +#include "nsNetCID.h" +#include "nsThreadUtils.h" +#include "nsICancelable.h" +#include "nsIDNSRecord.h" +#include "nsHostResolver.h" +#include "mozilla/Unused.h" +#include "DNSResolverInfo.h" + +using namespace mozilla::ipc; + +namespace mozilla { +namespace net { + +DNSRequestHandler::DNSRequestHandler() : mFlags(0) {} + +//----------------------------------------------------------------------------- +// DNSRequestHandler::nsISupports +//----------------------------------------------------------------------------- + +NS_IMPL_ISUPPORTS(DNSRequestHandler, nsIDNSListener) + +static void SendLookupCompletedHelper(DNSRequestActor* aActor, + const DNSRequestResponse& aReply) { + if (DNSRequestParent* parent = aActor->AsDNSRequestParent()) { + Unused << parent->SendLookupCompleted(aReply); + } else if (DNSRequestChild* child = aActor->AsDNSRequestChild()) { + Unused << child->SendLookupCompleted(aReply); + } +} + +void DNSRequestHandler::DoAsyncResolve(const nsACString& hostname, + const nsACString& trrServer, + uint16_t type, + const OriginAttributes& originAttributes, + uint32_t flags) { + nsresult rv; + mFlags = flags; + nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr<nsIEventTarget> main = GetMainThreadEventTarget(); + nsCOMPtr<nsICancelable> unused; + RefPtr<DNSResolverInfo> res; + if (!trrServer.IsEmpty()) { + res = new DNSResolverInfo(trrServer); + } + rv = dns->AsyncResolveNative( + hostname, static_cast<nsIDNSService::ResolveType>(type), flags, res, + this, main, originAttributes, getter_AddRefs(unused)); + } + + if (NS_FAILED(rv) && mIPCActor->CanSend()) { + SendLookupCompletedHelper(mIPCActor, DNSRequestResponse(rv)); + } +} + +void DNSRequestHandler::OnRecvCancelDNSRequest( + const nsCString& hostName, const nsCString& aTrrServer, + const uint16_t& type, const OriginAttributes& originAttributes, + const uint32_t& flags, const nsresult& reason) { + nsresult rv; + nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv); + if (NS_SUCCEEDED(rv)) { + RefPtr<DNSResolverInfo> res; + if (!aTrrServer.IsEmpty()) { + res = new DNSResolverInfo(aTrrServer); + } + rv = dns->CancelAsyncResolveNative( + hostName, static_cast<nsIDNSService::ResolveType>(type), flags, res, + this, reason, originAttributes); + } +} + +bool DNSRequestHandler::OnRecvLookupCompleted(const DNSRequestResponse& reply) { + return true; +} + +//----------------------------------------------------------------------------- +// nsIDNSListener functions +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +DNSRequestHandler::OnLookupComplete(nsICancelable* request, + nsIDNSRecord* aRecord, nsresult status) { + if (!mIPCActor || !mIPCActor->CanSend()) { + // nothing to do: child probably crashed + return NS_OK; + } + + if (NS_SUCCEEDED(status)) { + MOZ_ASSERT(aRecord); + + nsCOMPtr<nsIDNSByTypeRecord> byTypeRec = do_QueryInterface(aRecord); + if (byTypeRec) { + IPCTypeRecord result; + byTypeRec->GetResults(&result.mData); + SendLookupCompletedHelper(mIPCActor, DNSRequestResponse(result)); + return NS_OK; + } + + nsCOMPtr<nsIDNSAddrRecord> rec = do_QueryInterface(aRecord); + MOZ_ASSERT(rec); + nsAutoCString cname; + if (mFlags & nsHostResolver::RES_CANON_NAME) { + rec->GetCanonicalName(cname); + } + + // Get IP addresses for hostname (use port 80 as dummy value for NetAddr) + nsTArray<NetAddr> array; + NetAddr addr; + while (NS_SUCCEEDED(rec->GetNextAddr(80, &addr))) { + array.AppendElement(addr); + } + + double trrFetchDuration; + rec->GetTrrFetchDuration(&trrFetchDuration); + + double trrFetchDurationNetworkOnly; + rec->GetTrrFetchDurationNetworkOnly(&trrFetchDurationNetworkOnly); + + bool isTRR = false; + rec->IsTRR(&isTRR); + + uint32_t effectiveTRRMode = 0; + rec->GetEffectiveTRRMode(&effectiveTRRMode); + + SendLookupCompletedHelper( + mIPCActor, DNSRequestResponse(DNSRecord(cname, array, trrFetchDuration, + trrFetchDurationNetworkOnly, + isTRR, effectiveTRRMode))); + } else { + SendLookupCompletedHelper(mIPCActor, DNSRequestResponse(status)); + } + + return NS_OK; +} + +void DNSRequestHandler::OnIPCActorDestroy() { mIPCActor = nullptr; } + +//----------------------------------------------------------------------------- +// DNSRequestParent functions +//----------------------------------------------------------------------------- + +DNSRequestParent::DNSRequestParent(DNSRequestBase* aRequest) + : DNSRequestActor(aRequest) { + aRequest->SetIPCActor(this); +} + +mozilla::ipc::IPCResult DNSRequestParent::RecvCancelDNSRequest( + const nsCString& hostName, const nsCString& trrServer, const uint16_t& type, + const OriginAttributes& originAttributes, const uint32_t& flags, + const nsresult& reason) { + mDNSRequest->OnRecvCancelDNSRequest(hostName, trrServer, type, + originAttributes, flags, reason); + return IPC_OK(); +} + +mozilla::ipc::IPCResult DNSRequestParent::RecvLookupCompleted( + const DNSRequestResponse& reply) { + return mDNSRequest->OnRecvLookupCompleted(reply) ? IPC_OK() + : IPC_FAIL_NO_REASON(this); +} + +void DNSRequestParent::ActorDestroy(ActorDestroyReason) { + mDNSRequest->OnIPCActorDestroy(); + mDNSRequest = nullptr; +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/DNSRequestParent.h b/netwerk/dns/DNSRequestParent.h new file mode 100644 index 0000000000..59e6df8235 --- /dev/null +++ b/netwerk/dns/DNSRequestParent.h @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_DNSRequestParent_h +#define mozilla_net_DNSRequestParent_h + +#include "mozilla/net/DNSRequestBase.h" +#include "mozilla/net/PDNSRequestParent.h" +#include "nsIDNSListener.h" + +namespace mozilla { +namespace net { + +class DNSRequestParent : public DNSRequestActor, public PDNSRequestParent { + public: + friend class PDNSRequestParent; + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DNSRequestParent, override) + + explicit DNSRequestParent(DNSRequestBase* aRequest); + + bool CanSend() const override { return PDNSRequestParent::CanSend(); } + DNSRequestChild* AsDNSRequestChild() override { return nullptr; } + DNSRequestParent* AsDNSRequestParent() override { return this; } + + private: + virtual ~DNSRequestParent() = default; + + // Pass args here rather than storing them in the parent; they are only + // needed if the request is to be canceled. + mozilla::ipc::IPCResult RecvCancelDNSRequest( + const nsCString& hostName, const nsCString& trrServer, + const uint16_t& type, const OriginAttributes& originAttributes, + const uint32_t& flags, const nsresult& reason); + mozilla::ipc::IPCResult RecvLookupCompleted(const DNSRequestResponse& reply); + void ActorDestroy(ActorDestroyReason) override; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_DNSRequestParent_h diff --git a/netwerk/dns/DNSResolverInfo.cpp b/netwerk/dns/DNSResolverInfo.cpp new file mode 100644 index 0000000000..41e3bc7bb7 --- /dev/null +++ b/netwerk/dns/DNSResolverInfo.cpp @@ -0,0 +1,19 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DNSResolverInfo.h" + +namespace mozilla { +namespace net { + +NS_IMPL_ISUPPORTS(DNSResolverInfo, nsIDNSResolverInfo) + +NS_IMETHODIMP +DNSResolverInfo::GetURL(nsACString& aURL) { + aURL = mURL; + return NS_OK; +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/DNSResolverInfo.h b/netwerk/dns/DNSResolverInfo.h new file mode 100644 index 0000000000..dd4c9b1c85 --- /dev/null +++ b/netwerk/dns/DNSResolverInfo.h @@ -0,0 +1,34 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_DNSResolverInfo_h__ +#define mozilla_net_DNSResolverInfo_h__ + +#include "nsIDNSResolverInfo.h" + +namespace mozilla { +namespace net { + +class DNSResolverInfo : public nsIDNSResolverInfo { + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIDNSRESOLVERINFO + public: + explicit DNSResolverInfo(const nsACString& aURL) : mURL(aURL){}; + static nsCString URL(nsIDNSResolverInfo* aResolver) { + nsCString url; + if (aResolver) { + MOZ_ALWAYS_SUCCEEDS(aResolver->GetURL(url)); + } + return url; + } + + private: + virtual ~DNSResolverInfo() = default; + nsCString mURL; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_DNSResolverInfo_h__ diff --git a/netwerk/dns/GetAddrInfo.cpp b/netwerk/dns/GetAddrInfo.cpp new file mode 100644 index 0000000000..b586499b19 --- /dev/null +++ b/netwerk/dns/GetAddrInfo.cpp @@ -0,0 +1,465 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "GetAddrInfo.h" + +#ifdef DNSQUERY_AVAILABLE +// There is a bug in windns.h where the type of parameter ppQueryResultsSet for +// DnsQuery_A is dependent on UNICODE being set. It should *always* be +// PDNS_RECORDA, but if UNICODE is set it is PDNS_RECORDW. To get around this +// we make sure that UNICODE is unset. +# undef UNICODE +# include <ws2tcpip.h> +# undef GetAddrInfo +# include <windns.h> +#endif // DNSQUERY_AVAILABLE + +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/net/DNS.h" +#include "NativeDNSResolverOverrideParent.h" +#include "prnetdb.h" +#include "nsIOService.h" +#include "nsHostResolver.h" +#include "nsError.h" +#include "mozilla/net/DNS.h" +#include <algorithm> +#include "prerror.h" + +#include "mozilla/Logging.h" +#include "mozilla/StaticPrefs_network.h" + +namespace mozilla { +namespace net { + +static StaticRefPtr<NativeDNSResolverOverride> gOverrideService; + +static LazyLogModule gGetAddrInfoLog("GetAddrInfo"); +#define LOG(msg, ...) \ + MOZ_LOG(gGetAddrInfoLog, LogLevel::Debug, ("[DNS]: " msg, ##__VA_ARGS__)) +#define LOG_WARNING(msg, ...) \ + MOZ_LOG(gGetAddrInfoLog, LogLevel::Warning, ("[DNS]: " msg, ##__VA_ARGS__)) + +#ifdef DNSQUERY_AVAILABLE + +# define COMPUTER_NAME_BUFFER_SIZE 100 +static char sDNSComputerName[COMPUTER_NAME_BUFFER_SIZE]; +static char sNETBIOSComputerName[MAX_COMPUTERNAME_LENGTH + 1]; + +//////////////////////////// +// WINDOWS IMPLEMENTATION // +//////////////////////////// + +// Ensure consistency of PR_* and AF_* constants to allow for legacy usage of +// PR_* constants with this API. +static_assert(PR_AF_INET == AF_INET && PR_AF_INET6 == AF_INET6 && + PR_AF_UNSPEC == AF_UNSPEC, + "PR_AF_* must match AF_*"); + +// If successful, returns in aResult a TTL value that is smaller or +// equal with the one already there. Gets the TTL value by calling +// to DnsQuery_A and iterating through the returned +// records to find the one with the smallest TTL value. +static MOZ_ALWAYS_INLINE nsresult _CallDnsQuery_A_Windows( + const nsACString& aHost, uint16_t aAddressFamily, DWORD aFlags, + std::function<void(PDNS_RECORDA)> aCallback) { + NS_ConvertASCIItoUTF16 name(aHost); + + auto callDnsQuery_A = [&](uint16_t reqFamily) { + PDNS_RECORDA dnsData = nullptr; + DNS_STATUS status = DnsQuery_A(aHost.BeginReading(), reqFamily, aFlags, + nullptr, &dnsData, nullptr); + if (status == DNS_INFO_NO_RECORDS || status == DNS_ERROR_RCODE_NAME_ERROR || + !dnsData) { + LOG("No DNS records found for %s. status=%X. reqFamily = %X\n", + aHost.BeginReading(), status, reqFamily); + return NS_ERROR_FAILURE; + } else if (status != NOERROR) { + LOG_WARNING("DnsQuery_A failed with status %X.\n", status); + return NS_ERROR_UNEXPECTED; + } + + for (PDNS_RECORDA curRecord = dnsData; curRecord; + curRecord = curRecord->pNext) { + // Only records in the answer section are important + if (curRecord->Flags.S.Section != DnsSectionAnswer) { + continue; + } + if (curRecord->wType != reqFamily) { + continue; + } + + aCallback(curRecord); + } + + DnsFree(dnsData, DNS_FREE_TYPE::DnsFreeRecordList); + return NS_OK; + }; + + if (aAddressFamily == PR_AF_UNSPEC || aAddressFamily == PR_AF_INET) { + callDnsQuery_A(DNS_TYPE_A); + } + + if (aAddressFamily == PR_AF_UNSPEC || aAddressFamily == PR_AF_INET6) { + callDnsQuery_A(DNS_TYPE_AAAA); + } + return NS_OK; +} + +bool recordTypeMatchesRequest(uint16_t wType, uint16_t aAddressFamily) { + if (aAddressFamily == PR_AF_UNSPEC) { + return wType == DNS_TYPE_A || wType == DNS_TYPE_AAAA; + } + if (aAddressFamily == PR_AF_INET) { + return wType == DNS_TYPE_A; + } + if (aAddressFamily == PR_AF_INET6) { + return wType == DNS_TYPE_AAAA; + } + return false; +} + +static MOZ_ALWAYS_INLINE nsresult _GetTTLData_Windows(const nsACString& aHost, + uint32_t* aResult, + uint16_t aAddressFamily) { + MOZ_ASSERT(!aHost.IsEmpty()); + MOZ_ASSERT(aResult); + if (aAddressFamily != PR_AF_UNSPEC && aAddressFamily != PR_AF_INET && + aAddressFamily != PR_AF_INET6) { + return NS_ERROR_UNEXPECTED; + } + + // In order to avoid using ANY records which are not always implemented as a + // "Gimme what you have" request in hostname resolvers, we should send A + // and/or AAAA requests, based on the address family requested. + const DWORD ttlFlags = + (DNS_QUERY_STANDARD | DNS_QUERY_NO_NETBT | DNS_QUERY_NO_HOSTS_FILE | + DNS_QUERY_NO_MULTICAST | DNS_QUERY_ACCEPT_TRUNCATED_RESPONSE | + DNS_QUERY_DONT_RESET_TTL_VALUES); + unsigned int ttl = (unsigned int)-1; + _CallDnsQuery_A_Windows( + aHost, aAddressFamily, ttlFlags, + [&ttl, &aHost, aAddressFamily](PDNS_RECORDA curRecord) { + if (recordTypeMatchesRequest(curRecord->wType, aAddressFamily)) { + ttl = std::min<unsigned int>(ttl, curRecord->dwTtl); + } else { + LOG("Received unexpected record type %u in response for %s.\n", + curRecord->wType, aHost.BeginReading()); + } + }); + + if (ttl == (unsigned int)-1) { + LOG("No useable TTL found."); + return NS_ERROR_FAILURE; + } + + *aResult = ttl; + return NS_OK; +} + +static MOZ_ALWAYS_INLINE nsresult +_DNSQuery_A_SingleLabel(const nsACString& aCanonHost, uint16_t aAddressFamily, + uint16_t aFlags, AddrInfo** aAddrInfo) { + bool setCanonName = aFlags & nsHostResolver::RES_CANON_NAME; + nsAutoCString canonName; + const DWORD flags = (DNS_QUERY_STANDARD | DNS_QUERY_NO_MULTICAST | + DNS_QUERY_ACCEPT_TRUNCATED_RESPONSE); + nsTArray<NetAddr> addresses; + + _CallDnsQuery_A_Windows( + aCanonHost, aAddressFamily, flags, [&](PDNS_RECORDA curRecord) { + MOZ_DIAGNOSTIC_ASSERT(curRecord->wType == DNS_TYPE_A || + curRecord->wType == DNS_TYPE_AAAA); + if (setCanonName) { + canonName.Assign(curRecord->pName); + } + NetAddr addr{}; + addr.inet.family = AF_INET; + addr.inet.ip = curRecord->Data.A.IpAddress; + addresses.AppendElement(addr); + }); + + LOG("Query for: %s has %u results", aCanonHost.BeginReading(), + addresses.Length()); + if (addresses.IsEmpty()) { + return NS_ERROR_UNKNOWN_HOST; + } + RefPtr<AddrInfo> ai( + new AddrInfo(aCanonHost, canonName, 0, std::move(addresses))); + ai.forget(aAddrInfo); + + return NS_OK; +} + +#endif + +//////////////////////////////////// +// PORTABLE RUNTIME IMPLEMENTATION// +//////////////////////////////////// + +static MOZ_ALWAYS_INLINE nsresult +_GetAddrInfo_Portable(const nsACString& aCanonHost, uint16_t aAddressFamily, + uint16_t aFlags, AddrInfo** aAddrInfo) { + MOZ_ASSERT(!aCanonHost.IsEmpty()); + MOZ_ASSERT(aAddrInfo); + + // We accept the same aFlags that nsHostResolver::ResolveHost accepts, but we + // need to translate the aFlags into a form that PR_GetAddrInfoByName + // accepts. + int prFlags = PR_AI_ADDRCONFIG; + if (!(aFlags & nsHostResolver::RES_CANON_NAME)) { + prFlags |= PR_AI_NOCANONNAME; + } + + // We need to remove IPv4 records manually because PR_GetAddrInfoByName + // doesn't support PR_AF_INET6. + bool disableIPv4 = aAddressFamily == PR_AF_INET6; + if (disableIPv4) { + aAddressFamily = PR_AF_UNSPEC; + } + +#if defined(DNSQUERY_AVAILABLE) + if (StaticPrefs::network_dns_dns_query_single_label() && + !aCanonHost.Contains('.') && aCanonHost != "localhost"_ns) { + // For some reason we can't use DnsQuery_A to get the computer's IP. + if (!aCanonHost.Equals(nsDependentCString(sDNSComputerName), + nsCaseInsensitiveCStringComparator) && + !aCanonHost.Equals(nsDependentCString(sNETBIOSComputerName), + nsCaseInsensitiveCStringComparator)) { + // This is a single label name resolve without a dot. + // We use DNSQuery_A for these. + LOG("Resolving %s using DnsQuery_A (computername: %s)\n", + aCanonHost.BeginReading(), sDNSComputerName); + return _DNSQuery_A_SingleLabel(aCanonHost, aAddressFamily, aFlags, + aAddrInfo); + } + } +#endif + + PRAddrInfo* prai = + PR_GetAddrInfoByName(aCanonHost.BeginReading(), aAddressFamily, prFlags); + + if (!prai) { + return NS_ERROR_UNKNOWN_HOST; + } + + nsAutoCString canonName; + if (aFlags & nsHostResolver::RES_CANON_NAME) { + canonName.Assign(PR_GetCanonNameFromAddrInfo(prai)); + } + + bool filterNameCollision = + !(aFlags & nsHostResolver::RES_ALLOW_NAME_COLLISION); + RefPtr<AddrInfo> ai(new AddrInfo(aCanonHost, prai, disableIPv4, + filterNameCollision, canonName)); + PR_FreeAddrInfo(prai); + if (ai->Addresses().IsEmpty()) { + return NS_ERROR_UNKNOWN_HOST; + } + + ai.forget(aAddrInfo); + + return NS_OK; +} + +////////////////////////////////////// +// COMMON/PLATFORM INDEPENDENT CODE // +////////////////////////////////////// +nsresult GetAddrInfoInit() { + LOG("Initializing GetAddrInfo.\n"); + +#ifdef DNSQUERY_AVAILABLE + DWORD namesize = COMPUTER_NAME_BUFFER_SIZE; + if (!GetComputerNameExA(ComputerNameDnsHostname, sDNSComputerName, + &namesize)) { + sDNSComputerName[0] = 0; + } + namesize = MAX_COMPUTERNAME_LENGTH + 1; + if (!GetComputerNameExA(ComputerNameNetBIOS, sNETBIOSComputerName, + &namesize)) { + sNETBIOSComputerName[0] = 0; + } +#endif + return NS_OK; +} + +nsresult GetAddrInfoShutdown() { + LOG("Shutting down GetAddrInfo.\n"); + return NS_OK; +} + +bool FindAddrOverride(const nsACString& aHost, uint16_t aAddressFamily, + uint16_t aFlags, AddrInfo** aAddrInfo) { + RefPtr<NativeDNSResolverOverride> overrideService = gOverrideService; + if (!overrideService) { + return false; + } + AutoReadLock lock(overrideService->mLock); + nsTArray<PRNetAddr>* overrides = overrideService->mOverrides.GetValue(aHost); + if (!overrides) { + return false; + } + nsCString* cname = nullptr; + if (aFlags & nsHostResolver::RES_CANON_NAME) { + cname = overrideService->mCnames.GetValue(aHost); + } + + RefPtr<AddrInfo> ai; + + nsTArray<NetAddr> addresses; + for (const auto& ip : *overrides) { + if (aAddressFamily != AF_UNSPEC && ip.raw.family != aAddressFamily) { + continue; + } + NetAddr addr(&ip); + addresses.AppendElement(addr); + } + + if (!cname) { + ai = new AddrInfo(aHost, 0, std::move(addresses)); + } else { + ai = new AddrInfo(aHost, *cname, 0, std::move(addresses)); + } + + ai.forget(aAddrInfo); + return true; +} + +nsresult GetAddrInfo(const nsACString& aHost, uint16_t aAddressFamily, + uint16_t aFlags, AddrInfo** aAddrInfo, bool aGetTtl) { + if (NS_WARN_IF(aHost.IsEmpty()) || NS_WARN_IF(!aAddrInfo)) { + return NS_ERROR_NULL_POINTER; + } + *aAddrInfo = nullptr; + + if (StaticPrefs::network_dns_disabled()) { + return NS_ERROR_UNKNOWN_HOST; + } + +#ifdef DNSQUERY_AVAILABLE + // The GetTTLData needs the canonical name to function properly + if (aGetTtl) { + aFlags |= nsHostResolver::RES_CANON_NAME; + } +#endif + + // If there is an override for this host, then we synthetize a result. + if (gOverrideService && + FindAddrOverride(aHost, aAddressFamily, aFlags, aAddrInfo)) { + return NS_OK; + } + + nsAutoCString host(aHost); + if (gNativeIsLocalhost) { + // pretend we use the given host but use IPv4 localhost instead! + host = "localhost"_ns; + aAddressFamily = PR_AF_INET; + } + + RefPtr<AddrInfo> info; + nsresult rv = + _GetAddrInfo_Portable(host, aAddressFamily, aFlags, getter_AddRefs(info)); + +#ifdef DNSQUERY_AVAILABLE + if (aGetTtl && NS_SUCCEEDED(rv)) { + // Figure out the canonical name, or if that fails, just use the host name + // we have. + nsAutoCString name; + if (info && !info->CanonicalHostname().IsEmpty()) { + name = info->CanonicalHostname(); + } else { + name = host; + } + + LOG("Getting TTL for %s (cname = %s).", host.get(), name.get()); + uint32_t ttl = 0; + nsresult ttlRv = _GetTTLData_Windows(name, &ttl, aAddressFamily); + if (NS_SUCCEEDED(ttlRv)) { + auto builder = info->Build(); + builder.SetTTL(ttl); + info = builder.Finish(); + LOG("Got TTL %u for %s (name = %s).", ttl, host.get(), name.get()); + } else { + LOG_WARNING("Could not get TTL for %s (cname = %s).", host.get(), + name.get()); + } + } +#endif + + info.forget(aAddrInfo); + return rv; +} + +// static +already_AddRefed<nsINativeDNSResolverOverride> +NativeDNSResolverOverride::GetSingleton() { + if (nsIOService::UseSocketProcess() && XRE_IsParentProcess()) { + return NativeDNSResolverOverrideParent::GetSingleton(); + } + + if (gOverrideService) { + return do_AddRef(gOverrideService); + } + + gOverrideService = new NativeDNSResolverOverride(); + ClearOnShutdown(&gOverrideService); + return do_AddRef(gOverrideService); +} + +NS_IMPL_ISUPPORTS(NativeDNSResolverOverride, nsINativeDNSResolverOverride) + +NS_IMETHODIMP NativeDNSResolverOverride::AddIPOverride( + const nsACString& aHost, const nsACString& aIPLiteral) { + PRNetAddr tempAddr; + // Unfortunately, PR_StringToNetAddr does not properly initialize + // the output buffer in the case of IPv6 input. See bug 223145. + memset(&tempAddr, 0, sizeof(PRNetAddr)); + + if (PR_StringToNetAddr(nsCString(aIPLiteral).get(), &tempAddr) != + PR_SUCCESS) { + return NS_ERROR_UNEXPECTED; + } + + AutoWriteLock lock(mLock); + auto& overrides = mOverrides.GetOrInsert(aHost); + overrides.AppendElement(tempAddr); + + return NS_OK; +} + +NS_IMETHODIMP NativeDNSResolverOverride::SetCnameOverride( + const nsACString& aHost, const nsACString& aCNAME) { + if (aCNAME.IsEmpty()) { + return NS_ERROR_UNEXPECTED; + } + + AutoWriteLock lock(mLock); + mCnames.Put(aHost, nsCString(aCNAME)); + + return NS_OK; +} + +NS_IMETHODIMP NativeDNSResolverOverride::ClearHostOverride( + const nsACString& aHost) { + AutoWriteLock lock(mLock); + mCnames.Remove(aHost); + auto overrides = mOverrides.GetAndRemove(aHost); + if (!overrides) { + return NS_OK; + } + + overrides->Clear(); + return NS_OK; +} + +NS_IMETHODIMP NativeDNSResolverOverride::ClearOverrides() { + AutoWriteLock lock(mLock); + mOverrides.Clear(); + mCnames.Clear(); + return NS_OK; +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/GetAddrInfo.h b/netwerk/dns/GetAddrInfo.h new file mode 100644 index 0000000000..e7dfdfdeec --- /dev/null +++ b/netwerk/dns/GetAddrInfo.h @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef netwerk_dns_GetAddrInfo_h +#define netwerk_dns_GetAddrInfo_h + +#include "nsError.h" +#include "nscore.h" +#include "nsINativeDNSResolverOverride.h" +#include "nsHashKeys.h" +#include "nsDataHashtable.h" +#include "mozilla/RWLock.h" +#include "nsTArray.h" +#include "prio.h" + +#if defined(XP_WIN) +# define DNSQUERY_AVAILABLE 1 +#else +# undef DNSQUERY_AVAILABLE +#endif + +namespace mozilla { +namespace net { + +class AddrInfo; + +/** + * Look up a host by name. Mostly equivalent to getaddrinfo(host, NULL, ...) of + * RFC 3493. + * + * @param aHost[in] Character string defining the host name of interest + * @param aAddressFamily[in] May be AF_INET, AF_INET6, or AF_UNSPEC. + * @param aFlags[in] May be either PR_AI_ADDRCONFIG or + * PR_AI_ADDRCONFIG | PR_AI_NOCANONNAME. Include PR_AI_NOCANONNAME to + * suppress the determination of the canonical name corresponding to + * hostname (PR_AI_NOCANONNAME will be ignored if the TTL is retrieved). + * @param aAddrInfo[out] Will point to the results of the host lookup, or be + * null if the lookup failed. + * @param aGetTtl[in] If true, the TTL will be retrieved if DNS provides the + * answers.. + */ +nsresult GetAddrInfo(const nsACString& aHost, uint16_t aAddressFamily, + uint16_t aFlags, AddrInfo** aAddrInfo, bool aGetTtl); + +/** + * Initialize the GetAddrInfo module. + * + * GetAddrInfoShutdown() should be called for every time this function is + * called. + */ +nsresult GetAddrInfoInit(); + +/** + * Shutdown the GetAddrInfo module. + * + * This function should be called for every time GetAddrInfoInit() is called. + * An assertion may throw (but is not guarenteed) if this function is called + * too many times. + */ +nsresult GetAddrInfoShutdown(); + +class NativeDNSResolverOverride : public nsINativeDNSResolverOverride { + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSINATIVEDNSRESOLVEROVERRIDE + public: + NativeDNSResolverOverride() : mLock("NativeDNSResolverOverride") {} + + static already_AddRefed<nsINativeDNSResolverOverride> GetSingleton(); + + private: + virtual ~NativeDNSResolverOverride() = default; + mozilla::RWLock mLock; + + nsDataHashtable<nsCStringHashKey, nsTArray<PRNetAddr>> mOverrides; + nsDataHashtable<nsCStringHashKey, nsCString> mCnames; + + friend bool FindAddrOverride(const nsACString& aHost, uint16_t aAddressFamily, + uint16_t aFlags, AddrInfo** aAddrInfo); +}; + +} // namespace net +} // namespace mozilla + +#endif // netwerk_dns_GetAddrInfo_h diff --git a/netwerk/dns/HTTPSSVC.cpp b/netwerk/dns/HTTPSSVC.cpp new file mode 100644 index 0000000000..9eb477d8df --- /dev/null +++ b/netwerk/dns/HTTPSSVC.cpp @@ -0,0 +1,433 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "HTTPSSVC.h" +#include "mozilla/net/DNS.h" +#include "nsHttp.h" +#include "nsHttpHandler.h" +#include "nsNetAddr.h" +#include "nsNetUtil.h" + +namespace mozilla { +namespace net { + +NS_IMPL_ISUPPORTS(SVCBRecord, nsISVCBRecord) + +class SvcParam : public nsISVCParam, + public nsISVCParamAlpn, + public nsISVCParamNoDefaultAlpn, + public nsISVCParamPort, + public nsISVCParamIPv4Hint, + public nsISVCParamEchConfig, + public nsISVCParamIPv6Hint, + public nsISVCParamODoHConfig { + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSISVCPARAM + NS_DECL_NSISVCPARAMALPN + NS_DECL_NSISVCPARAMNODEFAULTALPN + NS_DECL_NSISVCPARAMPORT + NS_DECL_NSISVCPARAMIPV4HINT + NS_DECL_NSISVCPARAMECHCONFIG + NS_DECL_NSISVCPARAMIPV6HINT + NS_DECL_NSISVCPARAMODOHCONFIG + public: + explicit SvcParam(const SvcParamType& value) : mValue(value){}; + + private: + virtual ~SvcParam() = default; + SvcParamType mValue; +}; + +NS_IMPL_ADDREF(SvcParam) +NS_IMPL_RELEASE(SvcParam) +NS_INTERFACE_MAP_BEGIN(SvcParam) + NS_INTERFACE_MAP_ENTRY(nsISVCParam) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISVCParam) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamAlpn, mValue.is<SvcParamAlpn>()) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamNoDefaultAlpn, + mValue.is<SvcParamNoDefaultAlpn>()) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamPort, mValue.is<SvcParamPort>()) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamIPv4Hint, + mValue.is<SvcParamIpv4Hint>()) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamEchConfig, + mValue.is<SvcParamEchConfig>()) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamIPv6Hint, + mValue.is<SvcParamIpv6Hint>()) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISVCParamODoHConfig, + mValue.is<SvcParamODoHConfig>()) +NS_INTERFACE_MAP_END + +NS_IMETHODIMP +SvcParam::GetType(uint16_t* aType) { + *aType = mValue.match( + [](Nothing&) { return SvcParamKeyMandatory; }, + [](SvcParamAlpn&) { return SvcParamKeyAlpn; }, + [](SvcParamNoDefaultAlpn&) { return SvcParamKeyNoDefaultAlpn; }, + [](SvcParamPort&) { return SvcParamKeyPort; }, + [](SvcParamIpv4Hint&) { return SvcParamKeyIpv4Hint; }, + [](SvcParamEchConfig&) { return SvcParamKeyEchConfig; }, + [](SvcParamIpv6Hint&) { return SvcParamKeyIpv6Hint; }, + [](SvcParamODoHConfig&) { return SvcParamKeyODoHConfig; }); + return NS_OK; +} + +NS_IMETHODIMP +SvcParam::GetAlpn(nsTArray<nsCString>& aAlpn) { + if (!mValue.is<SvcParamAlpn>()) { + MOZ_ASSERT(false, "Unexpected type for variant"); + return NS_ERROR_NOT_AVAILABLE; + } + aAlpn.AppendElements(mValue.as<SvcParamAlpn>().mValue); + return NS_OK; +} + +NS_IMETHODIMP +SvcParam::GetPort(uint16_t* aPort) { + if (!mValue.is<SvcParamPort>()) { + MOZ_ASSERT(false, "Unexpected type for variant"); + return NS_ERROR_NOT_AVAILABLE; + } + *aPort = mValue.as<SvcParamPort>().mValue; + return NS_OK; +} + +NS_IMETHODIMP +SvcParam::GetEchconfig(nsACString& aEchConfig) { + if (!mValue.is<SvcParamEchConfig>()) { + MOZ_ASSERT(false, "Unexpected type for variant"); + return NS_ERROR_NOT_AVAILABLE; + } + aEchConfig = mValue.as<SvcParamEchConfig>().mValue; + return NS_OK; +} + +NS_IMETHODIMP +SvcParam::GetIpv4Hint(nsTArray<RefPtr<nsINetAddr>>& aIpv4Hint) { + if (!mValue.is<SvcParamIpv4Hint>()) { + MOZ_ASSERT(false, "Unexpected type for variant"); + return NS_ERROR_NOT_AVAILABLE; + } + const auto& results = mValue.as<SvcParamIpv4Hint>().mValue; + for (const auto& ip : results) { + if (ip.raw.family != AF_INET) { + return NS_ERROR_UNEXPECTED; + } + RefPtr<nsINetAddr> hint = new nsNetAddr(&ip); + aIpv4Hint.AppendElement(hint); + } + + return NS_OK; +} + +NS_IMETHODIMP +SvcParam::GetIpv6Hint(nsTArray<RefPtr<nsINetAddr>>& aIpv6Hint) { + if (!mValue.is<SvcParamIpv6Hint>()) { + MOZ_ASSERT(false, "Unexpected type for variant"); + return NS_ERROR_NOT_AVAILABLE; + } + const auto& results = mValue.as<SvcParamIpv6Hint>().mValue; + for (const auto& ip : results) { + if (ip.raw.family != AF_INET6) { + return NS_ERROR_UNEXPECTED; + } + RefPtr<nsINetAddr> hint = new nsNetAddr(&ip); + aIpv6Hint.AppendElement(hint); + } + return NS_OK; +} + +NS_IMETHODIMP +SvcParam::GetODoHConfig(nsACString& aODoHConfig) { + if (!mValue.is<SvcParamODoHConfig>()) { + MOZ_ASSERT(false, "Unexpected type for variant"); + return NS_ERROR_NOT_AVAILABLE; + } + aODoHConfig = mValue.as<SvcParamODoHConfig>().mValue; + return NS_OK; +} + +bool SVCB::operator<(const SVCB& aOther) const { + if (gHttpHandler->EchConfigEnabled()) { + if (mHasEchConfig && !aOther.mHasEchConfig) { + return true; + } + if (!mHasEchConfig && aOther.mHasEchConfig) { + return false; + } + } + + return mSvcFieldPriority < aOther.mSvcFieldPriority; +} + +Maybe<uint16_t> SVCB::GetPort() const { + Maybe<uint16_t> port; + for (const auto& value : mSvcFieldValue) { + if (value.mValue.is<SvcParamPort>()) { + port.emplace(value.mValue.as<SvcParamPort>().mValue); + if (NS_FAILED(NS_CheckPortSafety(*port, "https"))) { + *port = 0; + } + return port; + } + } + + return Nothing(); +} + +bool SVCB::NoDefaultAlpn() const { + for (const auto& value : mSvcFieldValue) { + if (value.mValue.is<SvcParamKeyNoDefaultAlpn>()) { + return true; + } + } + + return false; +} + +Maybe<Tuple<nsCString, bool>> SVCB::GetAlpn(bool aNoHttp2, + bool aNoHttp3) const { + Maybe<Tuple<nsCString, bool>> alpn; + for (const auto& value : mSvcFieldValue) { + if (value.mValue.is<SvcParamAlpn>()) { + nsTArray<nsCString> alpnList; + alpnList.AppendElements(value.mValue.as<SvcParamAlpn>().mValue); + if (!alpnList.IsEmpty()) { + alpn.emplace(); + alpn = Some(SelectAlpnFromAlpnList(alpnList, aNoHttp2, aNoHttp3)); + } + return alpn; + } + } + + return Nothing(); +} + +void SVCB::GetIPHints(CopyableTArray<mozilla::net::NetAddr>& aAddresses) const { + if (mSvcFieldPriority == 0) { + return; + } + + for (const auto& value : mSvcFieldValue) { + if (value.mValue.is<SvcParamIpv4Hint>()) { + aAddresses.AppendElements(value.mValue.as<SvcParamIpv4Hint>().mValue); + } else if (value.mValue.is<SvcParamIpv6Hint>()) { + aAddresses.AppendElements(value.mValue.as<SvcParamIpv6Hint>().mValue); + } + } +} + +NS_IMETHODIMP SVCBRecord::GetPriority(uint16_t* aPriority) { + *aPriority = mData.mSvcFieldPriority; + return NS_OK; +} + +NS_IMETHODIMP SVCBRecord::GetName(nsACString& aName) { + aName = mData.mSvcDomainName; + return NS_OK; +} + +Maybe<uint16_t> SVCBRecord::GetPort() { return mPort; } + +Maybe<Tuple<nsCString, bool>> SVCBRecord::GetAlpn() { return mAlpn; } + +NS_IMETHODIMP SVCBRecord::GetEchConfig(nsACString& aEchConfig) { + aEchConfig = mData.mEchConfig; + return NS_OK; +} + +NS_IMETHODIMP SVCBRecord::GetODoHConfig(nsACString& aODoHConfig) { + aODoHConfig = mData.mODoHConfig; + return NS_OK; +} + +NS_IMETHODIMP SVCBRecord::GetValues(nsTArray<RefPtr<nsISVCParam>>& aValues) { + for (const auto& v : mData.mSvcFieldValue) { + RefPtr<nsISVCParam> param = new SvcParam(v.mValue); + aValues.AppendElement(param); + } + return NS_OK; +} + +NS_IMETHODIMP SVCBRecord::GetHasIPHintAddress(bool* aHasIPHintAddress) { + *aHasIPHintAddress = mData.mHasIPHints; + return NS_OK; +} + +static bool CheckAlpnIsUsable(const nsACString& aTargetName, + const nsACString& aAlpn, bool aIsHttp3, + uint32_t& aExcludedCount) { + if (aAlpn.IsEmpty()) { + return false; + } + + if (aIsHttp3 && gHttpHandler->IsHttp3Excluded(aTargetName)) { + aExcludedCount++; + return false; + } + + return true; +} + +already_AddRefed<nsISVCBRecord> +DNSHTTPSSVCRecordBase::GetServiceModeRecordInternal( + bool aNoHttp2, bool aNoHttp3, const nsTArray<SVCB>& aRecords, + bool& aRecordsAllExcluded, bool aCheckHttp3ExcludedList) { + nsCOMPtr<nsISVCBRecord> selectedRecord; + uint32_t recordHasNoDefaultAlpnCount = 0; + uint32_t recordExcludedCount = 0; + aRecordsAllExcluded = false; + nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID); + bool RRSetHasEchConfig = false; + uint32_t h3ExcludedCount = 0; + + for (const SVCB& record : aRecords) { + if (record.mSvcFieldPriority == 0) { + // In ServiceMode, the SvcPriority should never be 0. + return nullptr; + } + + if (record.NoDefaultAlpn()) { + ++recordHasNoDefaultAlpnCount; + } + + RRSetHasEchConfig |= record.mHasEchConfig; + + bool excluded = false; + if (NS_SUCCEEDED(dns->IsSVCDomainNameFailed(mHost, record.mSvcDomainName, + &excluded)) && + excluded) { + // Skip if the domain name of this record was failed to connect before. + ++recordExcludedCount; + continue; + } + + Maybe<uint16_t> port = record.GetPort(); + if (port && *port == 0) { + // Found an unsafe port, skip this record. + continue; + } + + Maybe<Tuple<nsCString, bool>> alpn = record.GetAlpn(aNoHttp2, aNoHttp3); + if (alpn) { + if (!CheckAlpnIsUsable(record.mSvcDomainName, Get<0>(*alpn), + aCheckHttp3ExcludedList && Get<1>(*alpn), + h3ExcludedCount)) { + continue; + } + } + + if (gHttpHandler->EchConfigEnabled() && RRSetHasEchConfig && + !record.mHasEchConfig) { + // Don't use this record if this record has no echConfig, but others have. + continue; + } + + if (!selectedRecord) { + selectedRecord = new SVCBRecord(record, std::move(port), std::move(alpn)); + } + } + + // If all records indicate "no-default-alpn", we should not use this RRSet. + if (recordHasNoDefaultAlpnCount == aRecords.Length()) { + return nullptr; + } + + if (recordExcludedCount == aRecords.Length()) { + aRecordsAllExcluded = true; + return nullptr; + } + + // If all records are in http3 excluded list, try again without checking the + // excluded list. This is better than returning nothing. + if (h3ExcludedCount == aRecords.Length() && aCheckHttp3ExcludedList) { + return GetServiceModeRecordInternal(aNoHttp2, aNoHttp3, aRecords, + aRecordsAllExcluded, false); + } + + return selectedRecord.forget(); +} + +void DNSHTTPSSVCRecordBase::GetAllRecordsWithEchConfigInternal( + bool aNoHttp2, bool aNoHttp3, const nsTArray<SVCB>& aRecords, + bool* aAllRecordsHaveEchConfig, bool* aAllRecordsInH3ExcludedList, + nsTArray<RefPtr<nsISVCBRecord>>& aResult, bool aCheckHttp3ExcludedList) { + if (aRecords.IsEmpty()) { + return; + } + + *aAllRecordsHaveEchConfig = aRecords[0].mHasEchConfig; + *aAllRecordsInH3ExcludedList = false; + // The first record should have echConfig. + if (!(*aAllRecordsHaveEchConfig)) { + return; + } + + uint32_t h3ExcludedCount = 0; + for (const SVCB& record : aRecords) { + if (record.mSvcFieldPriority == 0) { + // This should not happen, since GetAllRecordsWithEchConfigInternal() + // should be called only if GetServiceModeRecordInternal() returns a + // non-null record. + MOZ_ASSERT(false); + return; + } + + // Records with echConfig are in front of records without echConfig, so we + // don't have to continue. + *aAllRecordsHaveEchConfig &= record.mHasEchConfig; + if (!(*aAllRecordsHaveEchConfig)) { + aResult.Clear(); + return; + } + + Maybe<uint16_t> port = record.GetPort(); + if (port && *port == 0) { + // Found an unsafe port, skip this record. + continue; + } + + Maybe<Tuple<nsCString, bool>> alpn = record.GetAlpn(aNoHttp2, aNoHttp3); + if (alpn) { + if (!CheckAlpnIsUsable(record.mSvcDomainName, Get<0>(*alpn), + aCheckHttp3ExcludedList && Get<1>(*alpn), + h3ExcludedCount)) { + continue; + } + } + + RefPtr<nsISVCBRecord> svcbRecord = + new SVCBRecord(record, std::move(port), std::move(alpn)); + aResult.AppendElement(svcbRecord); + } + + // If all records are in http3 excluded list, try again without checking the + // excluded list. This is better than returning nothing. + if (h3ExcludedCount == aRecords.Length() && aCheckHttp3ExcludedList) { + GetAllRecordsWithEchConfigInternal( + aNoHttp2, aNoHttp3, aRecords, aAllRecordsHaveEchConfig, + aAllRecordsInH3ExcludedList, aResult, false); + *aAllRecordsInH3ExcludedList = true; + } +} + +bool DNSHTTPSSVCRecordBase::HasIPAddressesInternal( + const nsTArray<SVCB>& aRecords) { + for (const SVCB& record : aRecords) { + if (record.mSvcFieldPriority != 0) { + for (const auto& value : record.mSvcFieldValue) { + if (value.mValue.is<SvcParamIpv4Hint>()) { + return true; + } + if (value.mValue.is<SvcParamIpv6Hint>()) { + return true; + } + } + } + } + + return false; +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/HTTPSSVC.h b/netwerk/dns/HTTPSSVC.h new file mode 100644 index 0000000000..354f779b34 --- /dev/null +++ b/netwerk/dns/HTTPSSVC.h @@ -0,0 +1,153 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef HTTPSSVC_h__ +#define HTTPSSVC_h__ + +#include "nsIDNSByTypeRecord.h" +#include "mozilla/net/DNS.h" +#include "mozilla/Variant.h" +#include "mozilla/Maybe.h" + +namespace mozilla { +namespace net { + +enum SvcParamKey : uint16_t { + SvcParamKeyMandatory = 0, + SvcParamKeyAlpn = 1, + SvcParamKeyNoDefaultAlpn = 2, + SvcParamKeyPort = 3, + SvcParamKeyIpv4Hint = 4, + SvcParamKeyEchConfig = 5, + SvcParamKeyIpv6Hint = 6, + SvcParamKeyODoHConfig = 32769, +}; + +inline bool IsValidSvcParamKey(uint16_t aKey) { + return aKey <= SvcParamKeyIpv6Hint || aKey == SvcParamKeyODoHConfig; +} + +struct SvcParamAlpn { + bool operator==(const SvcParamAlpn& aOther) const { + return mValue == aOther.mValue; + } + CopyableTArray<nsCString> mValue; +}; + +struct SvcParamNoDefaultAlpn { + bool operator==(const SvcParamNoDefaultAlpn& aOther) const { return true; } +}; + +struct SvcParamPort { + bool operator==(const SvcParamPort& aOther) const { + return mValue == aOther.mValue; + } + uint16_t mValue; +}; + +struct SvcParamIpv4Hint { + bool operator==(const SvcParamIpv4Hint& aOther) const { + return mValue == aOther.mValue; + } + CopyableTArray<mozilla::net::NetAddr> mValue; +}; + +struct SvcParamEchConfig { + bool operator==(const SvcParamEchConfig& aOther) const { + return mValue == aOther.mValue; + } + nsCString mValue; +}; + +struct SvcParamIpv6Hint { + bool operator==(const SvcParamIpv6Hint& aOther) const { + return mValue == aOther.mValue; + } + CopyableTArray<mozilla::net::NetAddr> mValue; +}; + +struct SvcParamODoHConfig { + bool operator==(const SvcParamODoHConfig& aOther) const { + return mValue == aOther.mValue; + } + nsCString mValue; +}; + +using SvcParamType = + mozilla::Variant<Nothing, SvcParamAlpn, SvcParamNoDefaultAlpn, SvcParamPort, + SvcParamIpv4Hint, SvcParamEchConfig, SvcParamIpv6Hint, + SvcParamODoHConfig>; + +struct SvcFieldValue { + bool operator==(const SvcFieldValue& aOther) const { + return mValue == aOther.mValue; + } + SvcFieldValue() : mValue(AsVariant(Nothing{})) {} + SvcParamType mValue; +}; + +struct SVCB { + bool operator==(const SVCB& aOther) const { + return mSvcFieldPriority == aOther.mSvcFieldPriority && + mSvcDomainName == aOther.mSvcDomainName && + mSvcFieldValue == aOther.mSvcFieldValue; + } + bool operator<(const SVCB& aOther) const; + Maybe<uint16_t> GetPort() const; + bool NoDefaultAlpn() const; + Maybe<Tuple<nsCString, bool>> GetAlpn(bool aNoHttp2, bool aNoHttp3) const; + void GetIPHints(CopyableTArray<mozilla::net::NetAddr>& aAddresses) const; + uint16_t mSvcFieldPriority = 0; + nsCString mSvcDomainName; + nsCString mEchConfig; + nsCString mODoHConfig; + bool mHasIPHints = false; + bool mHasEchConfig = false; + CopyableTArray<SvcFieldValue> mSvcFieldValue; +}; + +class SVCBRecord : public nsISVCBRecord { + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSISVCBRECORD + public: + explicit SVCBRecord(const SVCB& data) + : mData(data), mPort(Nothing()), mAlpn(Nothing()) {} + explicit SVCBRecord(const SVCB& data, Maybe<uint16_t>&& aPort, + Maybe<Tuple<nsCString, bool>>&& aAlpn) + : mData(data), mPort(std::move(aPort)), mAlpn(std::move(aAlpn)) {} + + private: + virtual ~SVCBRecord() = default; + SVCB mData; + Maybe<uint16_t> mPort; + Maybe<Tuple<nsCString, bool>> mAlpn; +}; + +class DNSHTTPSSVCRecordBase { + public: + explicit DNSHTTPSSVCRecordBase(const nsACString& aHost) : mHost(aHost) {} + + protected: + virtual ~DNSHTTPSSVCRecordBase() = default; + + already_AddRefed<nsISVCBRecord> GetServiceModeRecordInternal( + bool aNoHttp2, bool aNoHttp3, const nsTArray<SVCB>& aRecords, + bool& aRecordsAllExcluded, bool aCheckHttp3ExcludedList = true); + + bool HasIPAddressesInternal(const nsTArray<SVCB>& aRecords); + + void GetAllRecordsWithEchConfigInternal( + bool aNoHttp2, bool aNoHttp3, const nsTArray<SVCB>& aRecords, + bool* aAllRecordsHaveEchConfig, bool* aAllRecordsInH3ExcludedList, + nsTArray<RefPtr<nsISVCBRecord>>& aResult, + bool aCheckHttp3ExcludedList = true); + + // The owner name of this HTTPS RR. + nsCString mHost; +}; + +} // namespace net +} // namespace mozilla + +#endif // HTTPSSVC_h__ diff --git a/netwerk/dns/IDNBlocklistUtils.cpp b/netwerk/dns/IDNBlocklistUtils.cpp new file mode 100644 index 0000000000..92c73d0997 --- /dev/null +++ b/netwerk/dns/IDNBlocklistUtils.cpp @@ -0,0 +1,86 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "IDNBlocklistUtils.h" + +#include "mozilla/Preferences.h" +#include "nsStringFwd.h" + +namespace mozilla { +namespace net { + +static constexpr char16_t sBlocklistPairs[][2] = { +#include "IDNCharacterBlocklist.inc" +}; + +void RemoveCharFromBlocklist(char16_t aChar, + nsTArray<BlocklistRange>& aBlocklist) { + auto pos = aBlocklist.BinaryIndexOf(aChar, BlocklistPairToCharComparator()); + if (pos == nsTArray<BlocklistRange>::NoIndex) { + return; + } + + auto& pair = aBlocklist[pos]; + + // If the matched range has a length of one, we can just remove it + if (pair.second == pair.first) { + aBlocklist.RemoveElementAt(pos); + return; + } + + // If the character matches the first element in the range, just update + // the range. + if (aChar == pair.first) { + pair.first = pair.first + 1; + return; + } + + // Also if it matches the last character in the range, we just update it. + if (aChar == pair.second) { + pair.second = pair.second - 1; + return; + } + + // Our character is in the middle of the range, splitting it in two. + // We update the matched range to reflect the values before the character, + // and insert a new range that represents the values after. + char16_t lastElement = pair.second; + pair.second = aChar - 1; + aBlocklist.InsertElementAt(pos + 1, + std::make_pair(char16_t(aChar + 1), lastElement)); +} + +void InitializeBlocklist(nsTArray<BlocklistRange>& aBlocklist) { + aBlocklist.Clear(); + for (auto const& arr : sBlocklistPairs) { + // The hardcoded pairs are already sorted. + aBlocklist.AppendElement(std::make_pair(arr[0], arr[1])); + } + + nsAutoString extraAllowed; + nsresult rv = + Preferences::GetString("network.IDN.extra_allowed_chars", extraAllowed); + if (NS_SUCCEEDED(rv) && !extraAllowed.IsEmpty()) { + const char16_t* cur = extraAllowed.BeginReading(); + const char16_t* end = extraAllowed.EndReading(); + // Characters in the allowed list are removed from the blocklist. + for (; cur < end; ++cur) { + RemoveCharFromBlocklist(*cur, aBlocklist); + } + } + + nsAutoString extraBlocked; + rv = Preferences::GetString("network.IDN.extra_blocked_chars", extraBlocked); + // We add each extra blocked character to the blocklist as a separate range. + if (NS_SUCCEEDED(rv) && !extraBlocked.IsEmpty()) { + for (size_t i = 0; i < extraBlocked.Length(); ++i) { + aBlocklist.AppendElement( + std::make_pair(extraBlocked[i], extraBlocked[i])); + } + aBlocklist.Sort(BlocklistEntryComparator()); + } +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/IDNBlocklistUtils.h b/netwerk/dns/IDNBlocklistUtils.h new file mode 100644 index 0000000000..658329693a --- /dev/null +++ b/netwerk/dns/IDNBlocklistUtils.h @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef IDNBlocklistUtils_h__ +#define IDNBlocklistUtils_h__ + +#include <utility> +#include "nsTArray.h" + +namespace mozilla { +namespace net { + +// A blocklist range is defined as all of the characters between: +// { firstCharacterInRange, lastCharacterInRange } +typedef std::pair<char16_t, char16_t> BlocklistRange; + +// Used to perform a binary search of the needle in the sorted array of pairs +class BlocklistPairToCharComparator { + public: + bool Equals(const BlocklistRange& pair, char16_t needle) const { + // If the needle is between pair.first and pair.second it + // is part of the range. + return pair.first <= needle && needle <= pair.second; + } + + bool LessThan(const BlocklistRange& pair, char16_t needle) const { + // The needle has to be larger than the second value, + // otherwise it may be equal. + return pair.second < needle; + } +}; + +// Used to sort the array of pairs +class BlocklistEntryComparator { + public: + bool Equals(const BlocklistRange& a, const BlocklistRange& b) const { + return a.first == b.first && a.second == b.second; + } + + bool LessThan(const BlocklistRange& a, const BlocklistRange& b) const { + return a.first < b.first; + } +}; + +// Returns true if the char can be found in the blocklist +inline bool CharInBlocklist(char16_t aChar, + const nsTArray<BlocklistRange>& aBlocklist) { + return aBlocklist.ContainsSorted(aChar, BlocklistPairToCharComparator()); +} + +// Initializes the blocklist based on the statically defined list and the +// values of the following preferences: +// - network.IDN.extra_allowed_chars +// - network.IDN.extra_blocked_chars +void InitializeBlocklist(nsTArray<BlocklistRange>& aBlocklist); + +void RemoveCharFromBlocklist(char16_t aChar, + nsTArray<BlocklistRange>& aBlocklist); + +} // namespace net +} // namespace mozilla + +#endif // IDNBlocklistUtils_h__ diff --git a/netwerk/dns/IDNCharacterBlocklist.inc b/netwerk/dns/IDNCharacterBlocklist.inc new file mode 100644 index 0000000000..754c4f9518 --- /dev/null +++ b/netwerk/dns/IDNCharacterBlocklist.inc @@ -0,0 +1,63 @@ +// This file contains the IDN character blocklist. +// Each entry represents a range of blocked characters. +// Ranges are defined as: +// { firstCharacterInRange, lastCharacterInRange } +// IMPORTANT: Make sure this list is sorted in ascending order + + +// ASCII Space +{ 0x0020, 0x0020 }, +{ 0x00A0, 0x00A0 }, +{ 0x00BC, 0x00BE }, +{ 0x0138, 0x0138 }, +{ 0x01C3, 0x01C3 }, +{ 0x02D0, 0x02D0 }, +{ 0x0337, 0x0338 }, +{ 0x0589, 0x058A }, +{ 0x05C3, 0x05C3 }, +{ 0x05F4, 0x05F4 }, +{ 0x0609, 0x060A }, +{ 0x066A, 0x066A }, +{ 0x06D4, 0x06D4 }, +{ 0x0701, 0x0704 }, +{ 0x115F, 0x1160 }, +{ 0x1735, 0x1735 }, +{ 0x2000, 0x200B }, +{ 0x200E, 0x2010 }, +{ 0x2019, 0x2019 }, +{ 0x2024, 0x2024 }, +{ 0x2027, 0x202F }, +{ 0x2039, 0x203A }, +{ 0x2041, 0x2041 }, +{ 0x2044, 0x2044 }, +{ 0x2052, 0x2052 }, +{ 0x205F, 0x205F }, +{ 0x2153, 0x215F }, +{ 0x2215, 0x2215 }, +{ 0x2236, 0x2236 }, +{ 0x23AE, 0x23AE }, +{ 0x2571, 0x2571 }, +{ 0x29F6, 0x29F6 }, +{ 0x29F8, 0x29F8 }, +{ 0x2AFB, 0x2AFB }, +{ 0x2AFD, 0x2AFD }, +{ 0x2FF0, 0x2FFB }, +// Ideographic Space +{ 0x3000, 0x3000 }, +{ 0x3002, 0x3002 }, +{ 0x3014, 0x3015 }, +{ 0x3033, 0x3033 }, +{ 0x30A0, 0x30A0 }, +{ 0x3164, 0x3164 }, +{ 0x321D, 0x321E }, +{ 0x33AE, 0x33AF }, +{ 0x33C6, 0x33C6 }, +{ 0x33DF, 0x33DF }, +{ 0xFE14, 0xFE15 }, +{ 0xFE3F, 0xFE3F }, +{ 0xFE5D, 0xFE5E }, +{ 0xFEFF, 0xFEFF }, +{ 0xFF0E, 0xFF0F }, +{ 0xFF61, 0xFF61 }, +{ 0xFFA0, 0xFFA0 }, +{ 0xFFF9, 0xFFFD }, diff --git a/netwerk/dns/NativeDNSResolverOverrideChild.cpp b/netwerk/dns/NativeDNSResolverOverrideChild.cpp new file mode 100644 index 0000000000..0e6c76dc5c --- /dev/null +++ b/netwerk/dns/NativeDNSResolverOverrideChild.cpp @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "NativeDNSResolverOverrideChild.h" +#include "GetAddrInfo.h" + +namespace mozilla { +namespace net { + +NativeDNSResolverOverrideChild::NativeDNSResolverOverrideChild() { + mOverrideService = NativeDNSResolverOverride::GetSingleton(); +} + +mozilla::ipc::IPCResult NativeDNSResolverOverrideChild::RecvAddIPOverride( + const nsCString& aHost, const nsCString& aIPLiteral) { + Unused << mOverrideService->AddIPOverride(aHost, aIPLiteral); + return IPC_OK(); +} + +mozilla::ipc::IPCResult NativeDNSResolverOverrideChild::RecvSetCnameOverride( + const nsCString& aHost, const nsCString& aCNAME) { + Unused << mOverrideService->SetCnameOverride(aHost, aCNAME); + return IPC_OK(); +} + +mozilla::ipc::IPCResult NativeDNSResolverOverrideChild::RecvClearHostOverride( + const nsCString& aHost) { + Unused << mOverrideService->ClearHostOverride(aHost); + return IPC_OK(); +} + +mozilla::ipc::IPCResult NativeDNSResolverOverrideChild::RecvClearOverrides() { + Unused << mOverrideService->ClearOverrides(); + return IPC_OK(); +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/NativeDNSResolverOverrideChild.h b/netwerk/dns/NativeDNSResolverOverrideChild.h new file mode 100644 index 0000000000..5b24cb3dcd --- /dev/null +++ b/netwerk/dns/NativeDNSResolverOverrideChild.h @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_NativeDNSResolverOverrideChild_h +#define mozilla_net_NativeDNSResolverOverrideChild_h + +#include "mozilla/net/PNativeDNSResolverOverrideChild.h" +#include "nsINativeDNSResolverOverride.h" + +namespace mozilla { +namespace net { + +class NativeDNSResolverOverrideChild : public PNativeDNSResolverOverrideChild { + public: + NS_INLINE_DECL_REFCOUNTING(NativeDNSResolverOverrideChild, override) + + NativeDNSResolverOverrideChild(); + + mozilla::ipc::IPCResult RecvAddIPOverride(const nsCString& aHost, + const nsCString& aIPLiteral); + mozilla::ipc::IPCResult RecvSetCnameOverride(const nsCString& aHost, + const nsCString& aCNAME); + mozilla::ipc::IPCResult RecvClearHostOverride(const nsCString& aHost); + mozilla::ipc::IPCResult RecvClearOverrides(); + + private: + virtual ~NativeDNSResolverOverrideChild() = default; + + nsCOMPtr<nsINativeDNSResolverOverride> mOverrideService; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_NativeDNSResolverOverrideChild_h diff --git a/netwerk/dns/NativeDNSResolverOverrideParent.cpp b/netwerk/dns/NativeDNSResolverOverrideParent.cpp new file mode 100644 index 0000000000..75fab125df --- /dev/null +++ b/netwerk/dns/NativeDNSResolverOverrideParent.cpp @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "NativeDNSResolverOverrideParent.h" + +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/net/SocketProcessParent.h" +#include "nsIOService.h" + +namespace mozilla { +namespace net { + +NS_IMPL_ISUPPORTS(NativeDNSResolverOverrideParent, nsINativeDNSResolverOverride) + +static StaticRefPtr<NativeDNSResolverOverrideParent> + gNativeDNSResolverOverrideParent; + +// static +already_AddRefed<nsINativeDNSResolverOverride> +NativeDNSResolverOverrideParent::GetSingleton() { + if (gNativeDNSResolverOverrideParent) { + return do_AddRef(gNativeDNSResolverOverrideParent); + } + + if (!gIOService) { + return nullptr; + } + + gNativeDNSResolverOverrideParent = new NativeDNSResolverOverrideParent(); + ClearOnShutdown(&gNativeDNSResolverOverrideParent); + + auto initTask = []() { + Unused << SocketProcessParent::GetSingleton() + ->SendPNativeDNSResolverOverrideConstructor( + gNativeDNSResolverOverrideParent); + }; + gIOService->CallOrWaitForSocketProcess(initTask); + return do_AddRef(gNativeDNSResolverOverrideParent); +} + +NS_IMETHODIMP NativeDNSResolverOverrideParent::AddIPOverride( + const nsACString& aHost, const nsACString& aIPLiteral) { + PRNetAddr tempAddr; + // Unfortunately, PR_StringToNetAddr does not properly initialize + // the output buffer in the case of IPv6 input. See bug 223145. + memset(&tempAddr, 0, sizeof(PRNetAddr)); + + if (PR_StringToNetAddr(nsCString(aIPLiteral).get(), &tempAddr) != + PR_SUCCESS) { + return NS_ERROR_UNEXPECTED; + } + + RefPtr<NativeDNSResolverOverrideParent> self = this; + nsCString host(aHost); + nsCString ip(aIPLiteral); + auto task = [self{std::move(self)}, host, ip]() { + Unused << self->SendAddIPOverride(host, ip); + }; + gIOService->CallOrWaitForSocketProcess(task); + return NS_OK; +} + +NS_IMETHODIMP NativeDNSResolverOverrideParent::SetCnameOverride( + const nsACString& aHost, const nsACString& aCNAME) { + if (aCNAME.IsEmpty()) { + return NS_ERROR_UNEXPECTED; + } + + RefPtr<NativeDNSResolverOverrideParent> self = this; + nsCString host(aHost); + nsCString cname(aCNAME); + auto task = [self{std::move(self)}, host, cname]() { + Unused << self->SendSetCnameOverride(host, cname); + }; + gIOService->CallOrWaitForSocketProcess(task); + return NS_OK; +} + +NS_IMETHODIMP NativeDNSResolverOverrideParent::ClearHostOverride( + const nsACString& aHost) { + RefPtr<NativeDNSResolverOverrideParent> self = this; + nsCString host(aHost); + auto task = [self{std::move(self)}, host]() { + Unused << self->SendClearHostOverride(host); + }; + gIOService->CallOrWaitForSocketProcess(task); + return NS_OK; +} + +NS_IMETHODIMP NativeDNSResolverOverrideParent::ClearOverrides() { + RefPtr<NativeDNSResolverOverrideParent> self = this; + auto task = [self{std::move(self)}]() { + Unused << self->SendClearOverrides(); + }; + gIOService->CallOrWaitForSocketProcess(task); + return NS_OK; +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/NativeDNSResolverOverrideParent.h b/netwerk/dns/NativeDNSResolverOverrideParent.h new file mode 100644 index 0000000000..b0cb5e4d6d --- /dev/null +++ b/netwerk/dns/NativeDNSResolverOverrideParent.h @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_NativeDNSResolverOverrideParent_h +#define mozilla_net_NativeDNSResolverOverrideParent_h + +#include "mozilla/net/PNativeDNSResolverOverrideParent.h" +#include "nsINativeDNSResolverOverride.h" + +namespace mozilla { +namespace net { + +class NativeDNSResolverOverrideParent : public PNativeDNSResolverOverrideParent, + public nsINativeDNSResolverOverride { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSINATIVEDNSRESOLVEROVERRIDE + + explicit NativeDNSResolverOverrideParent() = default; + static already_AddRefed<nsINativeDNSResolverOverride> GetSingleton(); + + private: + virtual ~NativeDNSResolverOverrideParent() = default; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_NativeDNSResolverOverrideParent_h diff --git a/netwerk/dns/PDNSRequest.ipdl b/netwerk/dns/PDNSRequest.ipdl new file mode 100644 index 0000000000..f6429c7c54 --- /dev/null +++ b/netwerk/dns/PDNSRequest.ipdl @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PNecko; +include protocol PSocketProcess; + +include PDNSRequestParams; + +include "mozilla/net/NeckoMessageUtils.h"; + +using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h"; + +namespace mozilla { +namespace net { + +async refcounted protocol PDNSRequest +{ + manager PNecko or PSocketProcess; + +both: + // constructor in PNecko takes AsyncResolve args that initialize request + + // Pass args here rather than storing them in the parent; they are only + // needed if the request is to be canceled. + async CancelDNSRequest(nsCString hostName, nsCString trrServer, + uint16_t type, OriginAttributes originAttributes, + uint32_t flags, nsresult reason); + async __delete__(); + + async LookupCompleted(DNSRequestResponse reply); + +}; + +} //namespace net +} //namespace mozilla diff --git a/netwerk/dns/PDNSRequestParams.ipdlh b/netwerk/dns/PDNSRequestParams.ipdlh new file mode 100644 index 0000000000..1f52e7f39b --- /dev/null +++ b/netwerk/dns/PDNSRequestParams.ipdlh @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +using NetAddr from "mozilla/net/DNS.h"; +using mozilla::net::IPCTypeRecord from "mozilla/net/DNSByTypeRecord.h"; + + +namespace mozilla { +namespace net { + +//----------------------------------------------------------------------------- +// DNS IPDL structs +//----------------------------------------------------------------------------- + +struct DNSRecord +{ + nsCString canonicalName; + NetAddr[] addrs; + double trrFetchDuration; + double trrFetchDurationNetworkOnly; + bool isTRR; + uint32_t effectiveTRRMode; +}; + +union DNSRequestResponse +{ + DNSRecord; + IPCTypeRecord; // The result of a by-type query + nsresult; // if error +}; + + +} // namespace ipc +} // namespace mozilla diff --git a/netwerk/dns/PNativeDNSResolverOverride.ipdl b/netwerk/dns/PNativeDNSResolverOverride.ipdl new file mode 100644 index 0000000000..1737e61dd2 --- /dev/null +++ b/netwerk/dns/PNativeDNSResolverOverride.ipdl @@ -0,0 +1,25 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PSocketProcess; + +namespace mozilla { +namespace net { + +async refcounted protocol PNativeDNSResolverOverride +{ + manager PSocketProcess; + +child: + async __delete__(); + async AddIPOverride(nsCString aHost, nsCString aIPLiteral); + async SetCnameOverride(nsCString aHost, nsCString aCNAME); + async ClearHostOverride(nsCString aHost); + async ClearOverrides(); +}; + +} //namespace net +} //namespace mozilla diff --git a/netwerk/dns/PTRRService.ipdl b/netwerk/dns/PTRRService.ipdl new file mode 100644 index 0000000000..02509b76ec --- /dev/null +++ b/netwerk/dns/PTRRService.ipdl @@ -0,0 +1,27 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PSocketProcess; + +include PSMIPCTypes; + +namespace mozilla { +namespace net { + +async refcounted protocol PTRRService +{ + manager PSocketProcess; + +child: + async __delete__(); + async UpdatePlatformDNSInformation(nsCString[] aSuffixList); + async UpdateParentalControlEnabled(bool aEnabled); + async ClearDNSCache(bool aTrrToo); + async SetDetectedTrrURI(nsCString aURI); +}; + +} //namespace net +} //namespace mozilla diff --git a/netwerk/dns/PublicSuffixList.jsm b/netwerk/dns/PublicSuffixList.jsm new file mode 100644 index 0000000000..c6988c0b8b --- /dev/null +++ b/netwerk/dns/PublicSuffixList.jsm @@ -0,0 +1,106 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { RemoteSettings } = ChromeUtils.import( + "resource://services-settings/remote-settings.js" +); +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); +const FileUtils = ChromeUtils.import("resource://gre/modules/FileUtils.jsm") + .FileUtils; + +const EXPORTED_SYMBOLS = ["PublicSuffixList"]; + +const RECORD_ID = "tld-dafsa"; +const SIGNAL = "public-suffix-list-updated"; + +const PublicSuffixList = { + CLIENT: RemoteSettings("public-suffix-list"), + + init() { + // Only initialize once. + if (this._initialized) { + return; + } + this._initialized = true; + + this.CLIENT.on("sync", this.onUpdate.bind(this)); + /* We have a single record for this collection. Let's see if we already have it locally. + * Note that on startup, we don't need to synchronize immediately on new profiles. + */ + this.CLIENT.get({ syncIfEmpty: false, filters: { id: RECORD_ID } }) + .then(async records => { + if (records.length == 1) { + // Get the downloaded file URI (most likely to be a no-op here, since file will exist). + const fileURI = await this.CLIENT.attachments.download(records[0]); + // Send a signal so that the C++ code loads the updated list on startup. + this.notifyUpdate(fileURI); + } + }) + .catch(err => console.error(err)); + }, + + /** + * This method returns the path to the file based on the file uri received + * Example: + * On windows "file://C:/Users/AppData/main/public-suffix-list/dafsa.bin" + * will be converted to "C:\\Users\\main\\public-suffix-list\\dafsa.bin + * + * On macOS/linux "file:///home/main/public-suffix-list/dafsa.bin" + * will be converted to "/home/main/public-suffix-list/dafsa.bin" + */ + getFilePath(fileURI) { + const uri = Services.io.newURI(fileURI); + const file = uri.QueryInterface(Ci.nsIFileURL).file; + return file.path; + }, + + notifyUpdate(fileURI) { + if (!Services.prefs.getBoolPref("network.psl.onUpdate_notify", false)) { + // Updating the PSL while Firefox is running could cause principals to + // have a different base domain before/after the update. + // See bug 1582647 comment 30 + return; + } + + const filePath = this.getFilePath(fileURI); + const nsifile = new FileUtils.File(filePath); + /* Send a signal to be read by the C++, the method + * ::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) + * at netwerk/dns/nsEffectiveTLDService.cpp + */ + Services.obs.notifyObservers( + nsifile, // aSubject + SIGNAL, // aTopic + filePath // aData + ); + }, + + async onUpdate({ data: { created, updated, deleted } }) { + // In theory, this will never happen, we will never delete the record. + if (deleted.length == 1) { + await this.CLIENT.attachments.delete(deleted[0]); + } + // Handle creation and update the same way + const changed = created.concat(updated.map(u => u.new)); + /* In theory, we should never have more than one record. And if we receive + * this event, it's because the single record was updated. + */ + if (changed.length != 1) { + console.warn("Unsupported sync event for Public Suffix List"); + return; + } + // Download the updated file. + let fileURI; + try { + fileURI = await this.CLIENT.attachments.download(changed[0]); + } catch (err) { + Cu.reportError(err); + return; + } + + // Notify the C++ part to reload it from disk. + this.notifyUpdate(fileURI); + }, +}; diff --git a/netwerk/dns/TRR.cpp b/netwerk/dns/TRR.cpp new file mode 100644 index 0000000000..dde39fd25e --- /dev/null +++ b/netwerk/dns/TRR.cpp @@ -0,0 +1,965 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=4 sw=2 sts=2 et cin: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DNS.h" +#include "nsCharSeparatedTokenizer.h" +#include "nsContentUtils.h" +#include "nsHttpHandler.h" +#include "nsIHttpChannel.h" +#include "nsIHttpChannelInternal.h" +#include "nsIIOService.h" +#include "nsIInputStream.h" +#include "nsISupportsBase.h" +#include "nsISupportsUtils.h" +#include "nsITimedChannel.h" +#include "nsIUploadChannel2.h" +#include "nsIURIMutator.h" +#include "nsNetUtil.h" +#include "nsStringStream.h" +#include "nsThreadUtils.h" +#include "nsURLHelper.h" +#include "TRR.h" +#include "TRRService.h" +#include "TRRServiceChannel.h" +#include "TRRLoadInfo.h" + +#include "mozilla/Base64.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/Logging.h" +#include "mozilla/Preferences.h" +#include "mozilla/StaticPrefs_network.h" +#include "mozilla/SyncRunnable.h" +#include "mozilla/Telemetry.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/Tokenizer.h" +#include "mozilla/UniquePtr.h" + +namespace mozilla { +namespace net { + +#undef LOG +#undef LOG_ENABLED +extern mozilla::LazyLogModule gHostResolverLog; +#define LOG(args) MOZ_LOG(gHostResolverLog, mozilla::LogLevel::Debug, args) +#define LOG_ENABLED() \ + MOZ_LOG_TEST(mozilla::net::gHostResolverLog, mozilla::LogLevel::Debug) + +NS_IMPL_ISUPPORTS(TRR, nsIHttpPushListener, nsIInterfaceRequestor, + nsIStreamListener, nsIRunnable) + +NS_IMETHODIMP +TRR::Notify(nsITimer* aTimer) { + if (aTimer == mTimeout) { + mTimeout = nullptr; + Cancel(); + } else { + MOZ_CRASH("Unknown timer"); + } + + return NS_OK; +} + +NS_IMETHODIMP +TRR::Run() { + MOZ_ASSERT_IF(XRE_IsParentProcess() && gTRRService, + NS_IsMainThread() || gTRRService->IsOnTRRThread()); + MOZ_ASSERT_IF(XRE_IsSocketProcess(), NS_IsMainThread()); + + if ((gTRRService == nullptr) || NS_FAILED(SendHTTPRequest())) { + RecordReason(nsHostRecord::TRR_SEND_FAILED); + FailData(NS_ERROR_FAILURE); + // The dtor will now be run + } + return NS_OK; +} + +static void InitHttpHandler() { + nsresult rv; + nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv); + if (NS_FAILED(rv)) { + return; + } + + nsCOMPtr<nsIProtocolHandler> handler; + rv = ios->GetProtocolHandler("http", getter_AddRefs(handler)); + if (NS_FAILED(rv)) { + return; + } +} + +nsresult TRR::CreateChannelHelper(nsIURI* aUri, nsIChannel** aResult) { + *aResult = nullptr; + + if (NS_IsMainThread() && !XRE_IsSocketProcess()) { + nsresult rv; + nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv)); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_NewChannel( + aResult, aUri, nsContentUtils::GetSystemPrincipal(), + nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, + nsIContentPolicy::TYPE_OTHER, + nullptr, // nsICookieJarSettings + nullptr, // PerformanceStorage + nullptr, // aLoadGroup + nullptr, // aCallbacks + nsIRequest::LOAD_NORMAL, ios); + } + + // Unfortunately, we can only initialize gHttpHandler on main thread. + if (!gHttpHandler) { + nsCOMPtr<nsIEventTarget> main = GetMainThreadEventTarget(); + if (main) { + // Forward to the main thread synchronously. + SyncRunnable::DispatchToThread( + main, new SyncRunnable(NS_NewRunnableFunction( + "InitHttpHandler", []() { InitHttpHandler(); }))); + } + } + + if (!gHttpHandler) { + return NS_ERROR_UNEXPECTED; + } + + RefPtr<TRRLoadInfo> loadInfo = + new TRRLoadInfo(aUri, nsIContentPolicy::TYPE_OTHER); + return gHttpHandler->CreateTRRServiceChannel(aUri, + nullptr, // givenProxyInfo + 0, // proxyResolveFlags + nullptr, // proxyURI + loadInfo, // aLoadInfo + aResult); +} + +nsresult TRR::SendHTTPRequest() { + // This is essentially the "run" method - created from nsHostResolver + + if ((mType != TRRTYPE_A) && (mType != TRRTYPE_AAAA) && + (mType != TRRTYPE_NS) && (mType != TRRTYPE_TXT) && + (mType != TRRTYPE_HTTPSSVC)) { + // limit the calling interface because nsHostResolver has explicit slots for + // these types + return NS_ERROR_FAILURE; + } + + if (((mType == TRRTYPE_A) || (mType == TRRTYPE_AAAA)) && + mRec->mEffectiveTRRMode != nsIRequest::TRR_ONLY_MODE) { + // let NS resolves skip the blocklist check + // we also don't check the blocklist for TRR only requests + MOZ_ASSERT(mRec); + + if (UseDefaultServer() && + gTRRService->IsTemporarilyBlocked(mHost, mOriginSuffix, mPB, true)) { + if (mType == TRRTYPE_A) { + // count only blocklist for A records to avoid double counts + Telemetry::Accumulate(Telemetry::DNS_TRR_BLACKLISTED2, + TRRService::AutoDetectedKey(), true); + } + + RecordReason(nsHostRecord::TRR_HOST_BLOCKED_TEMPORARY); + // not really an error but no TRR is issued + return NS_ERROR_UNKNOWN_HOST; + } + + if (gTRRService->IsExcludedFromTRR(mHost)) { + RecordReason(nsHostRecord::TRR_EXCLUDED); + return NS_ERROR_UNKNOWN_HOST; + } + + if (UseDefaultServer() && (mType == TRRTYPE_A)) { + Telemetry::Accumulate(Telemetry::DNS_TRR_BLACKLISTED2, + TRRService::AutoDetectedKey(), false); + } + } + + bool useGet = StaticPrefs::network_trr_useGET(); + nsAutoCString body; + nsCOMPtr<nsIURI> dnsURI; + bool disableECS = StaticPrefs::network_trr_disable_ECS(); + nsresult rv; + + LOG(("TRR::SendHTTPRequest resolve %s type %u\n", mHost.get(), mType)); + + if (useGet) { + nsAutoCString tmp; + rv = DNSPacket::EncodeRequest(tmp, mHost, mType, disableECS); + NS_ENSURE_SUCCESS(rv, rv); + + /* For GET requests, the outgoing packet needs to be Base64url-encoded and + then appended to the end of the URI. */ + rv = Base64URLEncode(tmp.Length(), + reinterpret_cast<const unsigned char*>(tmp.get()), + Base64URLEncodePaddingPolicy::Omit, body); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoCString uri; + if (UseDefaultServer()) { + gTRRService->GetURI(uri); + } else { + uri = mRec->mTrrServer; + } + + rv = NS_NewURI(getter_AddRefs(dnsURI), uri); + if (NS_FAILED(rv)) { + LOG(("TRR:SendHTTPRequest: NewURI failed!\n")); + return rv; + } + + nsAutoCString query; + rv = dnsURI->GetQuery(query); + if (NS_FAILED(rv)) { + return rv; + } + + if (query.IsEmpty()) { + query.Assign("?dns="_ns); + } else { + query.Append("&dns="_ns); + } + query.Append(body); + + rv = NS_MutateURI(dnsURI).SetQuery(query).Finalize(dnsURI); + LOG(("TRR::SendHTTPRequest GET dns=%s\n", body.get())); + } else { + rv = DNSPacket::EncodeRequest(body, mHost, mType, disableECS); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoCString uri; + if (UseDefaultServer()) { + gTRRService->GetURI(uri); + } else { + uri = mRec->mTrrServer; + } + rv = NS_NewURI(getter_AddRefs(dnsURI), uri); + } + if (NS_FAILED(rv)) { + LOG(("TRR:SendHTTPRequest: NewURI failed!\n")); + return rv; + } + + nsCOMPtr<nsIChannel> channel; + rv = CreateChannelHelper(dnsURI, getter_AddRefs(channel)); + if (NS_FAILED(rv) || !channel) { + LOG(("TRR:SendHTTPRequest: NewChannel failed!\n")); + return rv; + } + + channel->SetLoadFlags( + nsIRequest::LOAD_ANONYMOUS | nsIRequest::INHIBIT_CACHING | + nsIRequest::LOAD_BYPASS_CACHE | nsIChannel::LOAD_BYPASS_URL_CLASSIFIER); + NS_ENSURE_SUCCESS(rv, rv); + + rv = channel->SetNotificationCallbacks(this); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel); + if (!httpChannel) { + return NS_ERROR_UNEXPECTED; + } + + // This connection should not use TRR + rv = httpChannel->SetTRRMode(nsIRequest::TRR_DISABLED_MODE); + NS_ENSURE_SUCCESS(rv, rv); + + rv = httpChannel->SetRequestHeader("Accept"_ns, "application/dns-message"_ns, + false); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoCString cred; + if (UseDefaultServer()) { + gTRRService->GetCredentials(cred); + } + if (!cred.IsEmpty()) { + rv = httpChannel->SetRequestHeader("Authorization"_ns, cred, false); + NS_ENSURE_SUCCESS(rv, rv); + } + + nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(channel); + if (!internalChannel) { + return NS_ERROR_UNEXPECTED; + } + + // setting a small stream window means the h2 stack won't pipeline a window + // update with each HEADERS or reply to a DATA with a WINDOW UPDATE + rv = internalChannel->SetInitialRwin(127 * 1024); + NS_ENSURE_SUCCESS(rv, rv); + rv = internalChannel->SetIsTRRServiceChannel(true); + NS_ENSURE_SUCCESS(rv, rv); + + if (useGet) { + rv = httpChannel->SetRequestMethod("GET"_ns); + NS_ENSURE_SUCCESS(rv, rv); + } else { + nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(httpChannel); + if (!uploadChannel) { + return NS_ERROR_UNEXPECTED; + } + uint32_t streamLength = body.Length(); + nsCOMPtr<nsIInputStream> uploadStream; + rv = + NS_NewCStringInputStream(getter_AddRefs(uploadStream), std::move(body)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = uploadChannel->ExplicitSetUploadStream(uploadStream, + "application/dns-message"_ns, + streamLength, "POST"_ns, false); + NS_ENSURE_SUCCESS(rv, rv); + } + + rv = SetupTRRServiceChannelInternal(httpChannel, useGet); + if (NS_FAILED(rv)) { + return rv; + } + + rv = httpChannel->AsyncOpen(this); + if (NS_FAILED(rv)) { + return rv; + } + + // If the asyncOpen succeeded we can say that we actually attempted to + // use the TRR connection. + RefPtr<AddrHostRecord> addrRec = do_QueryObject(mRec); + if (addrRec) { + addrRec->mTRRUsed = true; + } + + NS_NewTimerWithCallback(getter_AddRefs(mTimeout), this, + gTRRService->GetRequestTimeout(), + nsITimer::TYPE_ONE_SHOT); + + mChannel = channel; + return NS_OK; +} + +// static +nsresult TRR::SetupTRRServiceChannelInternal(nsIHttpChannel* aChannel, + bool aUseGet) { + nsCOMPtr<nsIHttpChannel> httpChannel = aChannel; + MOZ_ASSERT(httpChannel); + + nsresult rv = NS_OK; + if (!aUseGet) { + rv = + httpChannel->SetRequestHeader("Cache-Control"_ns, "no-store"_ns, false); + NS_ENSURE_SUCCESS(rv, rv); + } + + // Sanitize the request by removing the Accept-Language header so we minimize + // the amount of fingerprintable information we send to the server. + if (!StaticPrefs::network_trr_send_accept_language_headers()) { + rv = httpChannel->SetRequestHeader("Accept-Language"_ns, ""_ns, false); + NS_ENSURE_SUCCESS(rv, rv); + } + + // Sanitize the request by removing the User-Agent + if (!StaticPrefs::network_trr_send_user_agent_headers()) { + rv = httpChannel->SetRequestHeader("User-Agent"_ns, ""_ns, false); + NS_ENSURE_SUCCESS(rv, rv); + } + + if (StaticPrefs::network_trr_send_empty_accept_encoding_headers()) { + rv = httpChannel->SetEmptyRequestHeader("Accept-Encoding"_ns); + NS_ENSURE_SUCCESS(rv, rv); + } + + // set the *default* response content type + if (NS_FAILED(httpChannel->SetContentType("application/dns-message"_ns))) { + LOG(("TRR::SetupTRRServiceChannelInternal: couldn't set content-type!\n")); + } + + nsCOMPtr<nsITimedChannel> timedChan(do_QueryInterface(httpChannel)); + if (timedChan) { + timedChan->SetTimingEnabled(true); + } + + return NS_OK; +} + +NS_IMETHODIMP +TRR::GetInterface(const nsIID& iid, void** result) { + if (!iid.Equals(NS_GET_IID(nsIHttpPushListener))) { + return NS_ERROR_NO_INTERFACE; + } + + nsCOMPtr<nsIHttpPushListener> copy(this); + *result = copy.forget().take(); + return NS_OK; +} + +nsresult TRR::DohDecodeQuery(const nsCString& query, nsCString& host, + enum TrrType& type) { + FallibleTArray<uint8_t> binary; + bool found_dns = false; + LOG(("TRR::DohDecodeQuery %s!\n", query.get())); + + // extract "dns=" from the query string + nsAutoCString data; + for (const nsACString& token : + nsCCharSeparatedTokenizer(query, '&').ToRange()) { + nsDependentCSubstring dns = Substring(token, 0, 4); + nsAutoCString check(dns); + if (check.Equals("dns=")) { + nsDependentCSubstring q = Substring(token, 4, -1); + data = q; + found_dns = true; + break; + } + } + if (!found_dns) { + LOG(("TRR::DohDecodeQuery no dns= in pushed URI query string\n")); + return NS_ERROR_ILLEGAL_VALUE; + } + + nsresult rv = + Base64URLDecode(data, Base64URLDecodePaddingPolicy::Ignore, binary); + NS_ENSURE_SUCCESS(rv, rv); + uint32_t avail = binary.Length(); + if (avail < 12) { + return NS_ERROR_FAILURE; + } + // check the query bit and the opcode + if ((binary[2] & 0xf8) != 0) { + return NS_ERROR_FAILURE; + } + uint32_t qdcount = (binary[4] << 8) + binary[5]; + if (!qdcount) { + return NS_ERROR_FAILURE; + } + + uint32_t index = 12; + uint32_t length = 0; + host.Truncate(); + do { + if (avail < (index + 1)) { + return NS_ERROR_UNEXPECTED; + } + + length = binary[index]; + if (length) { + if (host.Length()) { + host.Append("."); + } + if (avail < (index + 1 + length)) { + return NS_ERROR_UNEXPECTED; + } + host.Append((const char*)(&binary[0]) + index + 1, length); + } + index += 1 + length; // skip length byte + label + } while (length); + + LOG(("TRR::DohDecodeQuery host %s\n", host.get())); + + if (avail < (index + 2)) { + return NS_ERROR_UNEXPECTED; + } + uint16_t i16 = 0; + i16 += binary[index] << 8; + i16 += binary[index + 1]; + type = (enum TrrType)i16; + + LOG(("TRR::DohDecodeQuery type %d\n", (int)type)); + + return NS_OK; +} + +nsresult TRR::ReceivePush(nsIHttpChannel* pushed, nsHostRecord* pushedRec) { + if (!mHostResolver) { + return NS_ERROR_UNEXPECTED; + } + + LOG(("TRR::ReceivePush: PUSH incoming!\n")); + + nsCOMPtr<nsIURI> uri; + pushed->GetURI(getter_AddRefs(uri)); + nsAutoCString query; + if (uri) { + uri->GetQuery(query); + } + + PRNetAddr tempAddr; + if (NS_FAILED(DohDecodeQuery(query, mHost, mType)) || + (PR_StringToNetAddr(mHost.get(), &tempAddr) == PR_SUCCESS)) { // literal + LOG(("TRR::ReceivePush failed to decode %s\n", mHost.get())); + return NS_ERROR_UNEXPECTED; + } + + if ((mType != TRRTYPE_A) && (mType != TRRTYPE_AAAA) && + (mType != TRRTYPE_TXT) && (mType != TRRTYPE_HTTPSSVC)) { + LOG(("TRR::ReceivePush unknown type %d\n", mType)); + return NS_ERROR_UNEXPECTED; + } + + if (gTRRService->IsExcludedFromTRR(mHost)) { + return NS_ERROR_FAILURE; + } + + uint32_t type = nsIDNSService::RESOLVE_TYPE_DEFAULT; + if (mType == TRRTYPE_TXT) { + type = nsIDNSService::RESOLVE_TYPE_TXT; + } else if (mType == TRRTYPE_HTTPSSVC) { + type = nsIDNSService::RESOLVE_TYPE_HTTPSSVC; + } + + RefPtr<nsHostRecord> hostRecord; + nsresult rv; + rv = mHostResolver->GetHostRecord( + mHost, ""_ns, type, pushedRec->flags, pushedRec->af, pushedRec->pb, + pushedRec->originSuffix, getter_AddRefs(hostRecord)); + if (NS_FAILED(rv)) { + return rv; + } + + // Since we don't ever call nsHostResolver::NameLookup for this record, + // we need to copy the trr mode from the previous record + if (hostRecord->mEffectiveTRRMode == nsIRequest::TRR_DEFAULT_MODE) { + hostRecord->mEffectiveTRRMode = pushedRec->mEffectiveTRRMode; + } + + rv = mHostResolver->TrrLookup_unlocked(hostRecord, this); + if (NS_FAILED(rv)) { + return rv; + } + + rv = pushed->AsyncOpen(this); + if (NS_FAILED(rv)) { + return rv; + } + + // OK! + mChannel = pushed; + mRec.swap(hostRecord); + + return NS_OK; +} + +NS_IMETHODIMP +TRR::OnPush(nsIHttpChannel* associated, nsIHttpChannel* pushed) { + LOG(("TRR::OnPush entry\n")); + MOZ_ASSERT(associated == mChannel); + if (!mRec) { + return NS_ERROR_FAILURE; + } + if (!UseDefaultServer()) { + return NS_ERROR_FAILURE; + } + + RefPtr<TRR> trr = new TRR(mHostResolver, mPB); + return trr->ReceivePush(pushed, mRec); +} + +NS_IMETHODIMP +TRR::OnStartRequest(nsIRequest* aRequest) { + LOG(("TRR::OnStartRequest %p %s %d\n", this, mHost.get(), mType)); + + nsresult status = NS_OK; + aRequest->GetStatus(&status); + + if (NS_FAILED(status)) { + if (NS_IsOffline()) { + RecordReason(nsHostRecord::TRR_IS_OFFLINE); + } + + switch (status) { + case NS_ERROR_UNKNOWN_HOST: + RecordReason(nsHostRecord::TRR_CHANNEL_DNS_FAIL); + break; + case NS_ERROR_OFFLINE: + RecordReason(nsHostRecord::TRR_IS_OFFLINE); + break; + case NS_ERROR_NET_RESET: + RecordReason(nsHostRecord::TRR_NET_RESET); + break; + case NS_ERROR_NET_TIMEOUT: + RecordReason(nsHostRecord::TRR_NET_TIMEOUT); + break; + case NS_ERROR_PROXY_CONNECTION_REFUSED: + RecordReason(nsHostRecord::TRR_NET_REFUSED); + break; + case NS_ERROR_NET_INTERRUPT: + RecordReason(nsHostRecord::TRR_NET_INTERRUPT); + break; + case NS_ERROR_NET_INADEQUATE_SECURITY: + RecordReason(nsHostRecord::TRR_NET_INADEQ_SEQURITY); + break; + default: + RecordReason(nsHostRecord::TRR_UNKNOWN_CHANNEL_FAILURE); + } + } + + return NS_OK; +} + +void TRR::SaveAdditionalRecords( + const nsClassHashtable<nsCStringHashKey, DOHresp>& aRecords) { + if (!mRec) { + return; + } + nsresult rv; + for (auto iter = aRecords.ConstIter(); !iter.Done(); iter.Next()) { + if (iter.Data() && iter.Data()->mAddresses.IsEmpty()) { + // no point in adding empty records. + continue; + } + RefPtr<nsHostRecord> hostRecord; + rv = mHostResolver->GetHostRecord( + iter.Key(), EmptyCString(), nsIDNSService::RESOLVE_TYPE_DEFAULT, + mRec->flags, AF_UNSPEC, mRec->pb, mRec->originSuffix, + getter_AddRefs(hostRecord)); + if (NS_FAILED(rv)) { + LOG(("Failed to get host record for additional record %s", + nsCString(iter.Key()).get())); + continue; + } + RefPtr<AddrInfo> ai(new AddrInfo(iter.Key(), TRRTYPE_A, + std::move(iter.Data()->mAddresses), + iter.Data()->mTtl)); + mHostResolver->MaybeRenewHostRecord(hostRecord); + + // Since we're not actually calling NameLookup for this record, we need + // to set these fields to avoid assertions in CompleteLookup. + // This is quite hacky, and should be fixed. + hostRecord->mResolving++; + hostRecord->mEffectiveTRRMode = mRec->mEffectiveTRRMode; + RefPtr<AddrHostRecord> addrRec = do_QueryObject(hostRecord); + addrRec->mTrrStart = TimeStamp::Now(); + LOG(("Completing lookup for additional: %s", nsCString(iter.Key()).get())); + (void)mHostResolver->CompleteLookup(hostRecord, NS_OK, ai, mPB, + mOriginSuffix, AddrHostRecord::TRR_OK); + } +} + +void TRR::StoreIPHintAsDNSRecord(const struct SVCB& aSVCBRecord) { + LOG(("TRR::StoreIPHintAsDNSRecord [%p] [%s]", this, + aSVCBRecord.mSvcDomainName.get())); + CopyableTArray<NetAddr> addresses; + aSVCBRecord.GetIPHints(addresses); + if (addresses.IsEmpty()) { + return; + } + + RefPtr<nsHostRecord> hostRecord; + nsresult rv = mHostResolver->GetHostRecord( + aSVCBRecord.mSvcDomainName, EmptyCString(), + nsIDNSService::RESOLVE_TYPE_DEFAULT, + mRec->flags | nsHostResolver::RES_IP_HINT, AF_UNSPEC, mRec->pb, + mRec->originSuffix, getter_AddRefs(hostRecord)); + if (NS_FAILED(rv)) { + LOG(("Failed to get host record")); + return; + } + + mHostResolver->MaybeRenewHostRecord(hostRecord); + + uint32_t ttl = AddrInfo::NO_TTL_DATA; + RefPtr<AddrInfo> ai(new AddrInfo(aSVCBRecord.mSvcDomainName, TRRTYPE_A, + std::move(addresses), ttl)); + + // Since we're not actually calling NameLookup for this record, we need + // to set these fields to avoid assertions in CompleteLookup. + // This is quite hacky, and should be fixed. + hostRecord->mResolving++; + hostRecord->mEffectiveTRRMode = mRec->mEffectiveTRRMode; + RefPtr<AddrHostRecord> addrRec = do_QueryObject(hostRecord); + addrRec->mTrrStart = TimeStamp::Now(); + + (void)mHostResolver->CompleteLookup(hostRecord, NS_OK, ai, mPB, mOriginSuffix, + AddrHostRecord::TRR_OK); +} + +nsresult TRR::ReturnData(nsIChannel* aChannel) { + if (mType != TRRTYPE_TXT && mType != TRRTYPE_HTTPSSVC) { + // create and populate an AddrInfo instance to pass on + RefPtr<AddrInfo> ai( + new AddrInfo(mHost, mType, nsTArray<NetAddr>(), mDNS.mTtl)); + auto builder = ai->Build(); + builder.SetAddresses(std::move(mDNS.mAddresses)); + builder.SetCanonicalHostname(mCname); + + // Set timings. + nsCOMPtr<nsITimedChannel> timedChan = do_QueryInterface(aChannel); + if (timedChan) { + TimeStamp asyncOpen, start, end; + if (NS_SUCCEEDED(timedChan->GetAsyncOpen(&asyncOpen)) && + !asyncOpen.IsNull()) { + builder.SetTrrFetchDuration( + (TimeStamp::Now() - asyncOpen).ToMilliseconds()); + } + if (NS_SUCCEEDED(timedChan->GetRequestStart(&start)) && + NS_SUCCEEDED(timedChan->GetResponseEnd(&end)) && !start.IsNull() && + !end.IsNull()) { + builder.SetTrrFetchDurationNetworkOnly((end - start).ToMilliseconds()); + } + } + ai = builder.Finish(); + + if (!mHostResolver) { + return NS_ERROR_FAILURE; + } + (void)mHostResolver->CompleteLookup(mRec, NS_OK, ai, mPB, mOriginSuffix, + mTRRSkippedReason); + mHostResolver = nullptr; + mRec = nullptr; + } else { + (void)mHostResolver->CompleteLookupByType(mRec, NS_OK, mResult, mTTL, mPB); + } + return NS_OK; +} + +nsresult TRR::FailData(nsresult error) { + if (!mHostResolver) { + return NS_ERROR_FAILURE; + } + + // If we didn't record a reason until now, record a default one. + RecordReason(nsHostRecord::TRR_FAILED); + + if (mType == TRRTYPE_TXT || mType == TRRTYPE_HTTPSSVC) { + TypeRecordResultType empty(Nothing{}); + (void)mHostResolver->CompleteLookupByType(mRec, error, empty, 0, mPB); + } else { + // create and populate an TRR AddrInfo instance to pass on to signal that + // this comes from TRR + nsTArray<NetAddr> noAddresses; + RefPtr<AddrInfo> ai = new AddrInfo(mHost, mType, std::move(noAddresses)); + + (void)mHostResolver->CompleteLookup(mRec, error, ai, mPB, mOriginSuffix, + mTRRSkippedReason); + } + + mHostResolver = nullptr; + mRec = nullptr; + return NS_OK; +} + +nsresult TRR::FollowCname(nsIChannel* aChannel) { + nsresult rv = NS_OK; + nsAutoCString cname; + while (NS_SUCCEEDED(rv) && mDNS.mAddresses.IsEmpty() && !mCname.IsEmpty() && + mCnameLoop > 0) { + mCnameLoop--; + LOG(("TRR::On200Response CNAME %s => %s (%u)\n", mHost.get(), mCname.get(), + mCnameLoop)); + cname = mCname; + mCname.Truncate(); + + LOG(("TRR: check for CNAME record for %s within previous response\n", + cname.get())); + nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords; + rv = mPacket.Decode( + cname, mType, mCname, StaticPrefs::network_trr_allow_rfc1918(), + mTRRSkippedReason, mDNS, mResult, additionalRecords, mTTL); + if (NS_FAILED(rv)) { + LOG(("TRR::On200Response DohDecode %x\n", (int)rv)); + } + } + + // restore mCname as DohDecode() change it + mCname = cname; + if (NS_SUCCEEDED(rv) && !mDNS.mAddresses.IsEmpty()) { + ReturnData(aChannel); + return NS_OK; + } + + if (!mCnameLoop) { + LOG(("TRR::On200Response CNAME loop, eject!\n")); + return NS_ERROR_REDIRECT_LOOP; + } + + LOG(("TRR::On200Response CNAME %s => %s (%u)\n", mHost.get(), mCname.get(), + mCnameLoop)); + RefPtr<TRR> trr = + new TRR(mHostResolver, mRec, mCname, mType, mCnameLoop, mPB); + if (!gTRRService) { + return NS_ERROR_FAILURE; + } + return gTRRService->DispatchTRRRequest(trr); +} + +nsresult TRR::On200Response(nsIChannel* aChannel) { + // decode body and create an AddrInfo struct for the response + nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords; + nsresult rv = mPacket.Decode( + mHost, mType, mCname, StaticPrefs::network_trr_allow_rfc1918(), + mTRRSkippedReason, mDNS, mResult, additionalRecords, mTTL); + + if (NS_FAILED(rv)) { + LOG(("TRR::On200Response DohDecode %x\n", (int)rv)); + RecordReason(nsHostRecord::TRR_DECODE_FAILED); + return rv; + } + SaveAdditionalRecords(additionalRecords); + + if (mResult.is<TypeRecordHTTPSSVC>()) { + auto& results = mResult.as<TypeRecordHTTPSSVC>(); + for (const auto& rec : results) { + StoreIPHintAsDNSRecord(rec); + } + } + + if (!mDNS.mAddresses.IsEmpty() || mType == TRRTYPE_TXT || mCname.IsEmpty()) { + // pass back the response data + ReturnData(aChannel); + return NS_OK; + } + + LOG(("TRR::On200Response trying CNAME %s", mCname.get())); + return FollowCname(aChannel); +} + +static void RecordProcessingTime(nsIChannel* aChannel) { + // This method records the time it took from the last received byte of the + // DoH response until we've notified the consumer with a host record. + nsCOMPtr<nsITimedChannel> timedChan = do_QueryInterface(aChannel); + if (!timedChan) { + return; + } + TimeStamp end; + if (NS_FAILED(timedChan->GetResponseEnd(&end))) { + return; + } + + if (end.IsNull()) { + return; + } + + Telemetry::AccumulateTimeDelta(Telemetry::DNS_TRR_PROCESSING_TIME, end); + + LOG(("Processing DoH response took %f ms", + (TimeStamp::Now() - end).ToMilliseconds())); +} + +NS_IMETHODIMP +TRR::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) { + // The dtor will be run after the function returns + LOG(("TRR:OnStopRequest %p %s %d failed=%d code=%X\n", this, mHost.get(), + mType, mFailed, (unsigned int)aStatusCode)); + nsCOMPtr<nsIChannel> channel; + channel.swap(mChannel); + + { + // Cancel the timer since we don't need it anymore. + nsCOMPtr<nsITimer> timer; + mTimeout.swap(timer); + if (timer) { + timer->Cancel(); + } + } + + if (UseDefaultServer()) { + // Bad content is still considered "okay" if the HTTP response is okay + gTRRService->TRRIsOkay(NS_SUCCEEDED(aStatusCode) ? TRRService::OKAY_NORMAL + : TRRService::OKAY_BAD); + } + + nsresult rv = NS_OK; + // if status was "fine", parse the response and pass on the answer + if (!mFailed && NS_SUCCEEDED(aStatusCode)) { + nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest); + if (!httpChannel) { + return NS_ERROR_UNEXPECTED; + } + nsAutoCString contentType; + httpChannel->GetContentType(contentType); + if (contentType.Length() && + !contentType.LowerCaseEqualsLiteral("application/dns-message")) { + LOG(("TRR:OnStopRequest %p %s %d wrong content type %s\n", this, + mHost.get(), mType, contentType.get())); + FailData(NS_ERROR_UNEXPECTED); + return NS_OK; + } + + uint32_t httpStatus; + rv = httpChannel->GetResponseStatus(&httpStatus); + if (NS_SUCCEEDED(rv) && httpStatus == 200) { + rv = On200Response(channel); + if (NS_SUCCEEDED(rv) && UseDefaultServer()) { + RecordReason(nsHostRecord::TRR_OK); + RecordProcessingTime(channel); + return rv; + } + } else { + RecordReason(nsHostRecord::TRR_SERVER_RESPONSE_ERR); + LOG(("TRR:OnStopRequest:%d %p rv %x httpStatus %d\n", __LINE__, this, + (int)rv, httpStatus)); + } + } + + LOG(("TRR:OnStopRequest %p status %x mFailed %d\n", this, (int)aStatusCode, + mFailed)); + FailData(NS_SUCCEEDED(rv) ? NS_ERROR_UNKNOWN_HOST : rv); + return NS_OK; +} + +NS_IMETHODIMP +TRR::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream, + uint64_t aOffset, const uint32_t aCount) { + LOG(("TRR:OnDataAvailable %p %s %d failed=%d aCount=%u\n", this, mHost.get(), + mType, mFailed, (unsigned int)aCount)); + // receive DNS response into the local buffer + if (mFailed) { + return NS_ERROR_FAILURE; + } + + nsresult rv = + mPacket.OnDataAvailable(aRequest, aInputStream, aOffset, aCount); + if (NS_FAILED(rv)) { + LOG(("TRR::OnDataAvailable:%d fail\n", __LINE__)); + mFailed = true; + return rv; + } + return NS_OK; +} + +class ProxyCancel : public Runnable { + public: + explicit ProxyCancel(TRR* aTRR) : Runnable("proxyTrrCancel"), mTRR(aTRR) {} + + NS_IMETHOD Run() override { + mTRR->Cancel(); + mTRR = nullptr; + return NS_OK; + } + + private: + RefPtr<TRR> mTRR; +}; + +void TRR::Cancel() { + RefPtr<TRRServiceChannel> trrServiceChannel = do_QueryObject(mChannel); + if (trrServiceChannel && !XRE_IsSocketProcess()) { + if (gTRRService) { + nsCOMPtr<nsIThread> thread = gTRRService->TRRThread(); + if (thread && !thread->IsOnCurrentThread()) { + nsCOMPtr<nsIRunnable> r = new ProxyCancel(this); + thread->Dispatch(r.forget()); + return; + } + } + } else { + if (!NS_IsMainThread()) { + NS_DispatchToMainThread(new ProxyCancel(this)); + return; + } + } + + if (mChannel) { + RecordReason(nsHostRecord::TRR_TIMEOUT); + LOG(("TRR: %p canceling Channel %p %s %d\n", this, mChannel.get(), + mHost.get(), mType)); + mChannel->Cancel(NS_ERROR_ABORT); + if (UseDefaultServer()) { + gTRRService->TRRIsOkay(TRRService::OKAY_TIMEOUT); + } + } +} + +bool TRR::UseDefaultServer() { return !mRec || mRec->mTrrServer.IsEmpty(); } + +#undef LOG + +// namespace +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/TRR.h b/netwerk/dns/TRR.h new file mode 100644 index 0000000000..489cca08b8 --- /dev/null +++ b/netwerk/dns/TRR.h @@ -0,0 +1,161 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_TRR_h +#define mozilla_net_TRR_h + +#include "mozilla/net/DNSByTypeRecord.h" +#include "mozilla/Assertions.h" +#include "nsClassHashtable.h" +#include "nsIChannel.h" +#include "nsIHttpPushListener.h" +#include "nsIInterfaceRequestor.h" +#include "nsIStreamListener.h" +#include "nsHostResolver.h" +#include "nsThreadUtils.h" +#include "nsXULAppAPI.h" +#include "DNSPacket.h" + +namespace mozilla { +namespace net { + +class TRRService; +class TRRServiceChannel; +extern TRRService* gTRRService; + +class TRR : public Runnable, + public nsITimerCallback, + public nsIHttpPushListener, + public nsIInterfaceRequestor, + public nsIStreamListener { + public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIHTTPPUSHLISTENER + NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSISTREAMLISTENER + NS_DECL_NSITIMERCALLBACK + + // Number of "steps" we follow CNAME chains + static const unsigned int kCnameChaseMax = 64; + + // when firing off a normal A or AAAA query + explicit TRR(AHostResolver* aResolver, nsHostRecord* aRec, enum TrrType aType) + : mozilla::Runnable("TRR"), + mRec(aRec), + mHostResolver(aResolver), + mType(aType), + mOriginSuffix(aRec->originSuffix) { + mHost = aRec->host; + mPB = aRec->pb; + MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(), + "TRR must be in parent or socket process"); + } + + // when following CNAMEs + explicit TRR(AHostResolver* aResolver, nsHostRecord* aRec, nsCString& aHost, + enum TrrType& aType, unsigned int aLoopCount, bool aPB) + : mozilla::Runnable("TRR"), + mHost(aHost), + mRec(aRec), + mHostResolver(aResolver), + mType(aType), + mPB(aPB), + mCnameLoop(aLoopCount), + mOriginSuffix(aRec ? aRec->originSuffix : ""_ns) { + MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(), + "TRR must be in parent or socket process"); + } + + // used on push + explicit TRR(AHostResolver* aResolver, bool aPB) + : mozilla::Runnable("TRR"), + mHostResolver(aResolver), + mType(TRRTYPE_A), + mPB(aPB) { + MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(), + "TRR must be in parent or socket process"); + } + + // to verify a domain + explicit TRR(AHostResolver* aResolver, nsACString& aHost, enum TrrType aType, + const nsACString& aOriginSuffix, bool aPB) + : mozilla::Runnable("TRR"), + mHost(aHost), + mRec(nullptr), + mHostResolver(aResolver), + mType(aType), + mPB(aPB), + mOriginSuffix(aOriginSuffix) { + MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(), + "TRR must be in parent or socket process"); + } + + NS_IMETHOD Run() override; + void Cancel(); + enum TrrType Type() { return mType; } + nsCString mHost; + RefPtr<nsHostRecord> mRec; + RefPtr<AHostResolver> mHostResolver; + + private: + ~TRR() = default; + nsresult SendHTTPRequest(); + nsresult ReturnData(nsIChannel* aChannel); + + // FailData() must be called to signal that the asynch TRR resolve is + // completed. For failed name resolves ("no such host"), the 'error' it + // passses on in its argument must be NS_ERROR_UNKNOWN_HOST. Other errors + // (if host was blacklisted, there as a bad content-type received, etc) + // other error codes must be used. This distinction is important for the + // subsequent logic to separate the error reasons. + nsresult FailData(nsresult error); + static nsresult DohDecodeQuery(const nsCString& query, nsCString& host, + enum TrrType& type); + nsresult ReceivePush(nsIHttpChannel* pushed, nsHostRecord* pushedRec); + nsresult On200Response(nsIChannel* aChannel); + nsresult FollowCname(nsIChannel* aChannel); + + bool UseDefaultServer(); + void SaveAdditionalRecords( + const nsClassHashtable<nsCStringHashKey, DOHresp>& aRecords); + + nsresult CreateChannelHelper(nsIURI* aUri, nsIChannel** aResult); + + friend class TRRServiceChannel; + static nsresult SetupTRRServiceChannelInternal(nsIHttpChannel* aChannel, + bool aUseGet); + + void StoreIPHintAsDNSRecord(const struct SVCB& aSVCBRecord); + + nsCOMPtr<nsIChannel> mChannel; + enum TrrType mType; + DNSPacket mPacket; + bool mFailed = false; + bool mPB; + DOHresp mDNS; + nsCOMPtr<nsITimer> mTimeout; + nsCString mCname; + uint32_t mCnameLoop = kCnameChaseMax; // loop detection counter + + uint32_t mTTL = UINT32_MAX; + TypeRecordResultType mResult = mozilla::AsVariant(Nothing()); + + nsHostRecord::TRRSkippedReason mTRRSkippedReason = nsHostRecord::TRR_UNSET; + void RecordReason(nsHostRecord::TRRSkippedReason reason) { + if (mTRRSkippedReason == nsHostRecord::TRR_UNSET) { + mTRRSkippedReason = reason; + } + } + + // keep a copy of the originSuffix for the cases where mRec == nullptr */ + const nsCString mOriginSuffix; +}; + +} // namespace net +} // namespace mozilla + +#endif // include guard diff --git a/netwerk/dns/TRRQuery.cpp b/netwerk/dns/TRRQuery.cpp new file mode 100644 index 0000000000..989780aa33 --- /dev/null +++ b/netwerk/dns/TRRQuery.cpp @@ -0,0 +1,288 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "TRRQuery.h" +#include "TRR.h" + +namespace mozilla { +namespace net { + +#undef LOG +extern mozilla::LazyLogModule gHostResolverLog; +#define LOG(args) MOZ_LOG(gHostResolverLog, mozilla::LogLevel::Debug, args) + +static already_AddRefed<AddrInfo> merge_rrset(AddrInfo* rrto, + AddrInfo* rrfrom) { + MOZ_ASSERT(rrto && rrfrom); + // Each of the arguments are all-IPv4 or all-IPv6 hence judging + // by the first element. This is true only for TRR resolutions. + bool isIPv6 = rrfrom->Addresses().Length() > 0 && + rrfrom->Addresses()[0].raw.family == PR_AF_INET6; + + nsTArray<NetAddr> addresses; + if (isIPv6) { + addresses = rrfrom->Addresses().Clone(); + addresses.AppendElements(rrto->Addresses()); + } else { + addresses = rrto->Addresses().Clone(); + addresses.AppendElements(rrfrom->Addresses()); + } + auto builder = rrto->Build(); + builder.SetAddresses(std::move(addresses)); + return builder.Finish(); +} + +void TRRQuery::Cancel() { + MutexAutoLock trrlock(mTrrLock); + if (mTrrA) { + mTrrA->Cancel(); + mTrrA = nullptr; + } + if (mTrrAAAA) { + mTrrAAAA->Cancel(); + mTrrAAAA = nullptr; + } + if (mTrrByType) { + mTrrByType->Cancel(); + mTrrByType = nullptr; + } +} + +nsresult TRRQuery::DispatchLookup(TRR* pushedTRR) { + mTrrStart = TimeStamp::Now(); + + RefPtr<AddrHostRecord> addrRec; + RefPtr<TypeHostRecord> typeRec; + + if (mRecord->IsAddrRecord()) { + addrRec = do_QueryObject(mRecord); + MOZ_ASSERT(addrRec); + } else { + typeRec = do_QueryObject(mRecord); + MOZ_ASSERT(typeRec); + } + + mTrrStart = TimeStamp::Now(); + bool madeQuery = false; + + if (addrRec) { + mTrrAUsed = INIT; + mTrrAAAAUsed = INIT; + + // If asking for AF_UNSPEC, issue both A and AAAA. + // If asking for AF_INET6 or AF_INET, do only that single type + enum TrrType rectype = (mRecord->af == AF_INET6) ? TRRTYPE_AAAA : TRRTYPE_A; + + if (pushedTRR) { + rectype = pushedTRR->Type(); + } + bool sendAgain; + + do { + sendAgain = false; + if ((TRRTYPE_AAAA == rectype) && gTRRService && + (gTRRService->DisableIPv6() || + (StaticPrefs::network_trr_skip_AAAA_when_not_supported() && + mHostResolver->GetNCS() && + mHostResolver->GetNCS()->GetIPv6() == + nsINetworkConnectivityService::NOT_AVAILABLE))) { + break; + } + LOG(("TRR Resolve %s type %d\n", addrRec->host.get(), (int)rectype)); + RefPtr<TRR> trr; + trr = pushedTRR ? pushedTRR : new TRR(this, mRecord, rectype); + if (pushedTRR || NS_SUCCEEDED(gTRRService->DispatchTRRRequest(trr))) { + MutexAutoLock trrlock(mTrrLock); + if (rectype == TRRTYPE_A) { + MOZ_ASSERT(!mTrrA); + mTrrA = trr; + mTrrAUsed = STARTED; + } else if (rectype == TRRTYPE_AAAA) { + MOZ_ASSERT(!mTrrAAAA); + mTrrAAAA = trr; + mTrrAAAAUsed = STARTED; + } else { + LOG(("TrrLookup called with bad type set: %d\n", rectype)); + MOZ_ASSERT(0); + } + madeQuery = true; + if (!pushedTRR && (mRecord->af == AF_UNSPEC) && + (rectype == TRRTYPE_A)) { + rectype = TRRTYPE_AAAA; + sendAgain = true; + } + } + } while (sendAgain); + } else { + typeRec->mStart = TimeStamp::Now(); + enum TrrType rectype; + + // XXX this could use a more extensible approach. + if (mRecord->type == nsIDNSService::RESOLVE_TYPE_TXT) { + rectype = TRRTYPE_TXT; + } else if (mRecord->type == nsIDNSService::RESOLVE_TYPE_HTTPSSVC) { + rectype = TRRTYPE_HTTPSSVC; + } else if (pushedTRR) { + rectype = pushedTRR->Type(); + } else { + MOZ_ASSERT(false, "Not an expected request type"); + return NS_ERROR_UNKNOWN_HOST; + } + + LOG(("TRR Resolve %s type %d\n", typeRec->host.get(), (int)rectype)); + RefPtr<TRR> trr; + trr = pushedTRR ? pushedTRR : new TRR(this, mRecord, rectype); + RefPtr<TRR> trrRequest = trr; + if (pushedTRR || NS_SUCCEEDED(gTRRService->DispatchTRRRequest(trr))) { + MutexAutoLock trrlock(mTrrLock); + MOZ_ASSERT(!mTrrByType); + mTrrByType = trr; + madeQuery = true; + } + } + + return madeQuery ? NS_OK : NS_ERROR_UNKNOWN_HOST; +} + +AHostResolver::LookupStatus TRRQuery::CompleteLookup( + nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb, + const nsACString& aOriginsuffix, nsHostRecord::TRRSkippedReason aReason) { + if (rec != mRecord) { + return mHostResolver->CompleteLookup(rec, status, aNewRRSet, pb, + aOriginsuffix, aReason); + } + + RefPtr<AddrInfo> newRRSet(aNewRRSet); + bool pendingARequest = false; + bool pendingAAAARequest = false; + { + MutexAutoLock trrlock(mTrrLock); + if (newRRSet->IsTRR() == TRRTYPE_A) { + MOZ_ASSERT(mTrrA); + mTRRAFailReason = aReason; + mTrrA = nullptr; + mTrrAUsed = NS_SUCCEEDED(status) ? OK : FAILED; + } else if (newRRSet->IsTRR() == TRRTYPE_AAAA) { + MOZ_ASSERT(mTrrAAAA); + mTRRAAAAFailReason = aReason; + mTrrAAAA = nullptr; + mTrrAAAAUsed = NS_SUCCEEDED(status) ? OK : FAILED; + } else { + MOZ_ASSERT(0); + } + if (mTrrA) { + pendingARequest = true; + } + if (mTrrAAAA) { + pendingAAAARequest = true; + } + } + + if (NS_SUCCEEDED(status)) { + mTRRSuccess++; + if (mTRRSuccess == 1) { + // Store the duration on first succesful TRR response. We + // don't know that there will be a second response nor can we + // tell which of two has useful data. + mTrrDuration = TimeStamp::Now() - mTrrStart; + } + } + + if (pendingARequest || + pendingAAAARequest) { // There are other outstanding requests + mFirstTRRresult = status; + if (NS_FAILED(status)) { + return LOOKUP_OK; // wait for outstanding + } + + // There's another TRR complete pending. Wait for it and keep + // this RRset around until then. + MOZ_ASSERT(!mFirstTRR && newRRSet); + mFirstTRR.swap(newRRSet); // autoPtr.swap() + MOZ_ASSERT(mFirstTRR && !newRRSet); + + if (StaticPrefs::network_trr_wait_for_A_and_AAAA()) { + LOG(("CompleteLookup: waiting for all responses!\n")); + return LOOKUP_OK; + } + + if (pendingARequest && !StaticPrefs::network_trr_early_AAAA()) { + // This is an early AAAA with a pending A response. Allowed + // only by pref. + LOG(("CompleteLookup: avoiding early use of TRR AAAA!\n")); + return LOOKUP_OK; + } + + // we can do some callbacks with this partial result which requires + // a deep copy + newRRSet = mFirstTRR; + + // Increment mResolving so we wait for the next resolve too. + rec->mResolving++; + } else { + // no more outstanding TRRs + // If mFirstTRR is set, merge those addresses into current set! + if (mFirstTRR) { + if (NS_SUCCEEDED(status)) { + LOG(("Merging responses")); + newRRSet = merge_rrset(newRRSet, mFirstTRR); + } else { + LOG(("Will use previous response")); + newRRSet.swap(mFirstTRR); // transfers + // We must use the status of the first response, otherwise we'll + // pass an error result to the consumers. + status = mFirstTRRresult; + } + mFirstTRR = nullptr; + } else { + if (NS_FAILED(status) && status != NS_ERROR_DEFINITIVE_UNKNOWN_HOST && + mFirstTRRresult == NS_ERROR_DEFINITIVE_UNKNOWN_HOST) { + status = NS_ERROR_DEFINITIVE_UNKNOWN_HOST; + } + } + + if (mTRRSuccess && mHostResolver->GetNCS() && + (mHostResolver->GetNCS()->GetNAT64() == + nsINetworkConnectivityService::OK) && + newRRSet) { + newRRSet = mHostResolver->GetNCS()->MapNAT64IPs(newRRSet); + } + } + + if (mTrrAUsed == OK) { + AccumulateCategoricalKeyed( + TRRService::AutoDetectedKey(), + Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::trrAOK); + } else if (mTrrAUsed == FAILED) { + AccumulateCategoricalKeyed( + TRRService::AutoDetectedKey(), + Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::trrAFail); + } + + if (mTrrAAAAUsed == OK) { + AccumulateCategoricalKeyed( + TRRService::AutoDetectedKey(), + Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::trrAAAAOK); + } else if (mTrrAAAAUsed == FAILED) { + AccumulateCategoricalKeyed( + TRRService::AutoDetectedKey(), + Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::trrAAAAFail); + } + + return mHostResolver->CompleteLookup(rec, status, newRRSet, pb, aOriginsuffix, + aReason); +} + +AHostResolver::LookupStatus TRRQuery::CompleteLookupByType( + nsHostRecord* rec, nsresult status, + mozilla::net::TypeRecordResultType& aResult, uint32_t aTtl, bool pb) { + if (rec == mRecord) { + MutexAutoLock trrlock(mTrrLock); + mTrrByType = nullptr; + } + return mHostResolver->CompleteLookupByType(rec, status, aResult, aTtl, pb); +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/TRRQuery.h b/netwerk/dns/TRRQuery.h new file mode 100644 index 0000000000..d99d16d5fc --- /dev/null +++ b/netwerk/dns/TRRQuery.h @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_TRRQuery_h +#define mozilla_net_TRRQuery_h + +namespace mozilla { +namespace net { + +class TRRQuery : public AHostResolver { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TRRQuery, override) + + public: + TRRQuery(nsHostResolver* aHostResolver, nsHostRecord* aHostRecord) + : mHostResolver(aHostResolver), + mRecord(aHostRecord), + mTrrLock("TRRQuery.mTrrLock") {} + + nsresult DispatchLookup(TRR* pushedTRR = nullptr); + + void Cancel(); + + enum TRRState { INIT, STARTED, OK, FAILED }; + TRRState mTrrAUsed = INIT; + TRRState mTrrAAAAUsed = INIT; + + AddrHostRecord::TRRSkippedReason mTRRAFailReason = AddrHostRecord::TRR_UNSET; + AddrHostRecord::TRRSkippedReason mTRRAAAAFailReason = + AddrHostRecord::TRR_UNSET; + + virtual LookupStatus CompleteLookup( + nsHostRecord*, nsresult, mozilla::net::AddrInfo*, bool pb, + const nsACString& aOriginsuffix, + nsHostRecord::TRRSkippedReason aReason) override; + virtual LookupStatus CompleteLookupByType( + nsHostRecord*, nsresult, mozilla::net::TypeRecordResultType& aResult, + uint32_t aTtl, bool pb) override; + virtual nsresult GetHostRecord(const nsACString& host, + const nsACString& aTrrServer, uint16_t type, + uint16_t flags, uint16_t af, bool pb, + const nsCString& originSuffix, + nsHostRecord** result) override { + if (!mHostResolver) { + return NS_ERROR_FAILURE; + } + return mHostResolver->GetHostRecord(host, aTrrServer, type, flags, af, pb, + originSuffix, result); + } + virtual nsresult TrrLookup_unlocked( + nsHostRecord* rec, mozilla::net::TRR* pushedTRR = nullptr) override { + if (!mHostResolver) { + return NS_ERROR_FAILURE; + } + return mHostResolver->TrrLookup_unlocked(rec, pushedTRR); + } + virtual void MaybeRenewHostRecord(nsHostRecord* aRec) override { + if (!mHostResolver) { + return; + } + mHostResolver->MaybeRenewHostRecord(aRec); + } + + mozilla::TimeDuration Duration() { return mTrrDuration; } + + private: + ~TRRQuery() = default; + RefPtr<nsHostResolver> mHostResolver; + RefPtr<nsHostRecord> mRecord; + + Mutex mTrrLock; // lock when accessing the mTrrA[AAA] pointers + RefPtr<mozilla::net::TRR> mTrrA; + RefPtr<mozilla::net::TRR> mTrrAAAA; + RefPtr<mozilla::net::TRR> mTrrByType; + + uint8_t mTRRSuccess = 0; // number of successful TRR responses + + mozilla::TimeDuration mTrrDuration; + mozilla::TimeStamp mTrrStart; + + RefPtr<mozilla::net::AddrInfo> mFirstTRR; // partial TRR storage + nsresult mFirstTRRresult = NS_OK; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_TRRQuery_h diff --git a/netwerk/dns/TRRService.cpp b/netwerk/dns/TRRService.cpp new file mode 100644 index 0000000000..601069dbb2 --- /dev/null +++ b/netwerk/dns/TRRService.cpp @@ -0,0 +1,944 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsAppDirectoryServiceDefs.h" +#include "nsCharSeparatedTokenizer.h" +#include "nsComponentManagerUtils.h" +#include "nsDirectoryServiceUtils.h" +#include "nsICaptivePortalService.h" +#include "nsIFile.h" +#include "nsIParentalControlsService.h" +#include "nsINetworkLinkService.h" +#include "nsIObserverService.h" +#include "nsIOService.h" +#include "nsNetUtil.h" +#include "nsStandardURL.h" +#include "TRR.h" +#include "TRRService.h" + +#include "mozilla/Preferences.h" +#include "mozilla/StaticPrefs_network.h" +#include "mozilla/Telemetry.h" +#include "mozilla/Tokenizer.h" +#include "mozilla/net/rust_helper.h" + +#if defined(XP_WIN) && !defined(__MINGW32__) +# include <shlobj_core.h> // for SHGetSpecialFolderPathA +#endif // XP_WIN + +static const char kOpenCaptivePortalLoginEvent[] = "captive-portal-login"; +static const char kClearPrivateData[] = "clear-private-data"; +static const char kPurge[] = "browser:purge-session-history"; +static const char kDisableIpv6Pref[] = "network.dns.disableIPv6"; +static const char kRolloutURIPref[] = "doh-rollout.uri"; +static const char kRolloutModePref[] = "doh-rollout.mode"; + +#define TRR_PREF_PREFIX "network.trr." +#define TRR_PREF(x) TRR_PREF_PREFIX x + +namespace mozilla { +namespace net { + +#undef LOG +extern mozilla::LazyLogModule gHostResolverLog; +#define LOG(args) MOZ_LOG(gHostResolverLog, mozilla::LogLevel::Debug, args) + +TRRService* gTRRService = nullptr; +StaticRefPtr<nsIThread> sTRRBackgroundThread; +static Atomic<TRRService*> sTRRServicePtr; + +NS_IMPL_ISUPPORTS(TRRService, nsIObserver, nsISupportsWeakReference) + +TRRService::TRRService() + : mInitialized(false), + mBlocklistDurationSeconds(60), + mLock("trrservice"), + mConfirmationNS("example.com"_ns), + mCaptiveIsPassed(false), + mTRRBLStorage("DataMutex::TRRBlocklist"), + mConfirmationState(CONFIRM_INIT), + mRetryConfirmInterval(125), + mTRRFailures(0), + mParentalControlEnabled(false) { + MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); +} + +// static +void TRRService::AddObserver(nsIObserver* aObserver, + nsIObserverService* aObserverService) { + nsCOMPtr<nsIObserverService> observerService; + if (aObserverService) { + observerService = aObserverService; + } else { + observerService = mozilla::services::GetObserverService(); + } + + if (observerService) { + observerService->AddObserver(aObserver, NS_CAPTIVE_PORTAL_CONNECTIVITY, + true); + observerService->AddObserver(aObserver, kOpenCaptivePortalLoginEvent, true); + observerService->AddObserver(aObserver, kClearPrivateData, true); + observerService->AddObserver(aObserver, kPurge, true); + observerService->AddObserver(aObserver, NS_NETWORK_LINK_TOPIC, true); + observerService->AddObserver(aObserver, NS_DNS_SUFFIX_LIST_UPDATED_TOPIC, + true); + observerService->AddObserver(aObserver, "xpcom-shutdown-threads", true); + } +} + +// static +bool TRRService::CheckCaptivePortalIsPassed() { + bool result = false; + nsCOMPtr<nsICaptivePortalService> captivePortalService = + do_GetService(NS_CAPTIVEPORTAL_CID); + if (captivePortalService) { + int32_t captiveState; + MOZ_ALWAYS_SUCCEEDS(captivePortalService->GetState(&captiveState)); + + if ((captiveState == nsICaptivePortalService::UNLOCKED_PORTAL) || + (captiveState == nsICaptivePortalService::NOT_CAPTIVE)) { + result = true; + } + LOG(("TRRService::Init mCaptiveState=%d mCaptiveIsPassed=%d\n", + captiveState, (int)result)); + } + + return result; +} + +constexpr auto kTRRIsAutoDetectedKey = "(auto-detected)"_ns; +constexpr auto kTRRNotAutoDetectedKey = "(default)"_ns; +// static +const nsCString& TRRService::AutoDetectedKey() { + if (gTRRService && gTRRService->IsUsingAutoDetectedURL()) { + return kTRRIsAutoDetectedKey.AsString(); + } + + return kTRRNotAutoDetectedKey.AsString(); +} + +static void RemoveTRRBlocklistFile() { + MOZ_ASSERT(NS_IsMainThread(), "Getting the profile dir on the main thread"); + + nsCOMPtr<nsIFile> file; + nsresult rv = + NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file)); + if (NS_FAILED(rv)) { + return; + } + + rv = file->AppendNative("TRRBlacklist.txt"_ns); + if (NS_FAILED(rv)) { + return; + } + + // Dispatch an async task that removes the blocklist file from the profile. + rv = NS_DispatchBackgroundTask( + NS_NewRunnableFunction("RemoveTRRBlocklistFile::Remove", + [file] { file->Remove(false); }), + NS_DISPATCH_EVENT_MAY_BLOCK); + if (NS_FAILED(rv)) { + return; + } + Preferences::SetBool("network.trr.blocklist_cleanup_done", true); +} + +nsresult TRRService::Init() { + MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); + if (mInitialized) { + return NS_OK; + } + mInitialized = true; + + AddObserver(this); + + nsCOMPtr<nsIPrefBranch> prefBranch; + GetPrefBranch(getter_AddRefs(prefBranch)); + if (prefBranch) { + prefBranch->AddObserver(TRR_PREF_PREFIX, this, true); + prefBranch->AddObserver(kDisableIpv6Pref, this, true); + prefBranch->AddObserver(kRolloutURIPref, this, true); + prefBranch->AddObserver(kRolloutModePref, this, true); + } + + gTRRService = this; + sTRRServicePtr = this; + + ReadPrefs(nullptr); + + if (XRE_IsParentProcess()) { + mCaptiveIsPassed = CheckCaptivePortalIsPassed(); + + mParentalControlEnabled = GetParentalControlEnabledInternal(); + + nsCOMPtr<nsINetworkLinkService> nls = + do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID); + if (nls) { + nsTArray<nsCString> suffixList; + nls->GetDnsSuffixList(suffixList); + RebuildSuffixList(std::move(suffixList)); + } + + nsCOMPtr<nsIThread> thread; + if (NS_FAILED( + NS_NewNamedThread("TRR Background", getter_AddRefs(thread)))) { + NS_WARNING("NS_NewNamedThread failed!"); + return NS_ERROR_FAILURE; + } + + sTRRBackgroundThread = thread; + + if (!StaticPrefs::network_trr_blocklist_cleanup_done()) { + // Dispatch an idle task to the main thread that gets the profile dir + // then attempts to delete the blocklist file on a background thread. + Unused << NS_DispatchToMainThreadQueue( + NS_NewCancelableRunnableFunction("RemoveTRRBlocklistFile::GetDir", + [] { RemoveTRRBlocklistFile(); }), + EventQueuePriority::Idle); + } + } + + LOG(("Initialized TRRService\n")); + return NS_OK; +} + +// static +bool TRRService::GetParentalControlEnabledInternal() { + nsCOMPtr<nsIParentalControlsService> pc = + do_CreateInstance("@mozilla.org/parental-controls-service;1"); + if (pc) { + bool result = false; + pc->GetParentalControlsEnabled(&result); + LOG(("TRRService::GetParentalControlEnabledInternal=%d\n", result)); + return result; + } + + return false; +} + +void TRRService::SetDetectedTrrURI(const nsACString& aURI) { + // If the user has set a custom URI then we don't want to override that. + if (mURIPrefHasUserValue) { + return; + } + + mURISetByDetection = MaybeSetPrivateURI(aURI); +} + +bool TRRService::Enabled(nsIRequest::TRRMode aMode) { + if (mMode == MODE_TRROFF) { + return false; + } + if (mConfirmationState == CONFIRM_INIT && + (!StaticPrefs::network_trr_wait_for_portal() || mCaptiveIsPassed || + (mMode == MODE_TRRONLY || aMode == nsIRequest::TRR_ONLY_MODE))) { + LOG(("TRRService::Enabled => CONFIRM_TRYING\n")); + mConfirmationState = CONFIRM_TRYING; + } + + if (mConfirmationState == CONFIRM_TRYING) { + LOG(("TRRService::Enabled MaybeConfirm()\n")); + MaybeConfirm(); + } + + if (mConfirmationState != CONFIRM_OK) { + LOG(("TRRService::Enabled mConfirmationState=%d mCaptiveIsPassed=%d\n", + (int)mConfirmationState, (int)mCaptiveIsPassed)); + } + + return (mConfirmationState == CONFIRM_OK); +} + +void TRRService::GetPrefBranch(nsIPrefBranch** result) { + MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); + *result = nullptr; + CallGetService(NS_PREFSERVICE_CONTRACTID, result); +} + +bool TRRService::MaybeSetPrivateURI(const nsACString& aURI) { + bool clearCache = false; + nsAutoCString newURI(aURI); + ProcessURITemplate(newURI); + + { + MutexAutoLock lock(mLock); + if (mPrivateURI.Equals(newURI)) { + return false; + } + + if (!mPrivateURI.IsEmpty()) { + LOG(("TRRService clearing blocklist because of change in uri service\n")); + auto bl = mTRRBLStorage.Lock(); + bl->Clear(); + clearCache = true; + } + mPrivateURI = newURI; + } + + // Clear the cache because we changed the URI + if (clearCache) { + ClearEntireCache(); + } + + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + obs->NotifyObservers(nullptr, NS_NETWORK_TRR_URI_CHANGED_TOPIC, nullptr); + } + return true; +} + +nsresult TRRService::ReadPrefs(const char* name) { + MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); + + // Whenever a pref change occurs that would cause us to clear the cache + // we set this to true then do it at the end of the method. + bool clearEntireCache = false; + + if (!name || !strcmp(name, TRR_PREF("mode")) || + !strcmp(name, kRolloutModePref)) { + uint32_t prevMode = Mode(); + + OnTRRModeChange(); + + // When the TRR service gets disabled we should purge the TRR cache to + // make sure we don't use any of the cached entries on a network where + // they are invalid - for example after turning on a VPN. + if (TRR_DISABLED(Mode()) && !TRR_DISABLED(prevMode)) { + clearEntireCache = true; + } + } + if (!name || !strcmp(name, TRR_PREF("uri")) || + !strcmp(name, kRolloutURIPref)) { + OnTRRURIChange(); + } + if (!name || !strcmp(name, TRR_PREF("credentials"))) { + MutexAutoLock lock(mLock); + Preferences::GetCString(TRR_PREF("credentials"), mPrivateCred); + } + if (!name || !strcmp(name, TRR_PREF("confirmationNS"))) { + MutexAutoLock lock(mLock); + nsAutoCString old(mConfirmationNS); + Preferences::GetCString(TRR_PREF("confirmationNS"), mConfirmationNS); + if (name && !old.IsEmpty() && !mConfirmationNS.Equals(old) && + (mConfirmationState > CONFIRM_TRYING) && + (mMode == MODE_TRRFIRST || mMode == MODE_TRRONLY)) { + LOG(("TRR::ReadPrefs: restart confirmationNS state\n")); + mConfirmationState = CONFIRM_TRYING; + MaybeConfirm_locked(); + } + } + if (!name || !strcmp(name, TRR_PREF("bootstrapAddress"))) { + MutexAutoLock lock(mLock); + Preferences::GetCString(TRR_PREF("bootstrapAddress"), mBootstrapAddr); + clearEntireCache = true; + } + if (!name || !strcmp(name, TRR_PREF("blacklist-duration"))) { + // prefs is given in number of seconds + uint32_t secs; + if (NS_SUCCEEDED( + Preferences::GetUint(TRR_PREF("blacklist-duration"), &secs))) { + mBlocklistDurationSeconds = secs; + } + } + if (!name || !strcmp(name, kDisableIpv6Pref)) { + bool tmp; + if (NS_SUCCEEDED(Preferences::GetBool(kDisableIpv6Pref, &tmp))) { + mDisableIPv6 = tmp; + } + } + if (!name || !strcmp(name, TRR_PREF("excluded-domains")) || + !strcmp(name, TRR_PREF("builtin-excluded-domains"))) { + MutexAutoLock lock(mLock); + mExcludedDomains.Clear(); + + auto parseExcludedDomains = [this](const char* aPrefName) { + nsAutoCString excludedDomains; + Preferences::GetCString(aPrefName, excludedDomains); + if (excludedDomains.IsEmpty()) { + return; + } + + for (const nsACString& tokenSubstring : + nsCCharSeparatedTokenizerTemplate< + NS_IsAsciiWhitespace, nsTokenizerFlags::SeparatorOptional>( + excludedDomains, ',') + .ToRange()) { + nsCString token{tokenSubstring}; + LOG(("TRRService::ReadPrefs %s host:[%s]\n", aPrefName, token.get())); + mExcludedDomains.PutEntry(token); + } + }; + + parseExcludedDomains(TRR_PREF("excluded-domains")); + parseExcludedDomains(TRR_PREF("builtin-excluded-domains")); + clearEntireCache = true; + } + + // if name is null, then we're just now initializing. In that case we don't + // need to clear the cache. + if (name && clearEntireCache) { + ClearEntireCache(); + } + + return NS_OK; +} + +void TRRService::ClearEntireCache() { + if (!StaticPrefs::network_trr_clear_cache_on_pref_change()) { + return; + } + nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID); + if (!dns) { + return; + } + dns->ClearCache(true); +} + +void TRRService::AddEtcHosts(const nsTArray<nsCString>& aArray) { + MutexAutoLock lock(mLock); + for (const auto& item : aArray) { + LOG(("Adding %s from /etc/hosts to excluded domains", item.get())); + mEtcHostsDomains.PutEntry(item); + } +} + +void TRRService::ReadEtcHostsFile() { + if (!StaticPrefs::network_trr_exclude_etc_hosts()) { + return; + } + + auto readHostsTask = []() { + MOZ_ASSERT(!NS_IsMainThread(), "Must not run on the main thread"); +#if defined(XP_WIN) && !defined(__MINGW32__) + // Inspired by libevent/evdns.c + // Windows is a little coy about where it puts its configuration + // files. Sure, they're _usually_ in C:\windows\system32, but + // there's no reason in principle they couldn't be in + // W:\hoboken chicken emergency + + nsCString path; + path.SetLength(MAX_PATH + 1); + if (!SHGetSpecialFolderPathA(NULL, path.BeginWriting(), CSIDL_SYSTEM, + false)) { + LOG(("Calling SHGetSpecialFolderPathA failed")); + return; + } + + path.SetLength(strlen(path.get())); + path.Append("\\drivers\\etc\\hosts"); +#elif defined(__MINGW32__) + nsAutoCString path("C:\\windows\\system32\\drivers\\etc\\hosts"_ns); +#else + nsAutoCString path("/etc/hosts"_ns); +#endif + + LOG(("Reading hosts file at %s", path.get())); + rust_parse_etc_hosts(&path, [](const nsTArray<nsCString>* aArray) -> bool { + RefPtr<TRRService> service(sTRRServicePtr); + if (service && aArray) { + service->AddEtcHosts(*aArray); + } + return !!service; + }); + }; + + Unused << NS_DispatchBackgroundTask( + NS_NewRunnableFunction("Read /etc/hosts file", readHostsTask), + NS_DISPATCH_EVENT_MAY_BLOCK); +} + +nsresult TRRService::GetURI(nsACString& result) { + MutexAutoLock lock(mLock); + result = mPrivateURI; + return NS_OK; +} + +nsresult TRRService::GetCredentials(nsCString& result) { + MutexAutoLock lock(mLock); + result = mPrivateCred; + return NS_OK; +} + +uint32_t TRRService::GetRequestTimeout() { + if (mMode == MODE_TRRONLY) { + return StaticPrefs::network_trr_request_timeout_mode_trronly_ms(); + } + + return StaticPrefs::network_trr_request_timeout_ms(); +} + +nsresult TRRService::Start() { + MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); + if (!mInitialized) { + return NS_ERROR_NOT_INITIALIZED; + } + return NS_OK; +} + +TRRService::~TRRService() { + MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); + LOG(("Exiting TRRService\n")); + gTRRService = nullptr; +} + +nsresult TRRService::DispatchTRRRequest(TRR* aTrrRequest) { + return DispatchTRRRequestInternal(aTrrRequest, true); +} + +nsresult TRRService::DispatchTRRRequestInternal(TRR* aTrrRequest, + bool aWithLock) { + NS_ENSURE_ARG_POINTER(aTrrRequest); + if (!StaticPrefs::network_trr_fetch_off_main_thread() || + XRE_IsSocketProcess()) { + return NS_DispatchToMainThread(aTrrRequest); + } + + RefPtr<TRR> trr = aTrrRequest; + nsCOMPtr<nsIThread> thread = aWithLock ? TRRThread() : TRRThread_locked(); + if (!thread) { + return NS_ERROR_FAILURE; + } + + return thread->Dispatch(trr.forget()); +} + +already_AddRefed<nsIThread> TRRService::TRRThread() { + MutexAutoLock lock(mLock); + return TRRThread_locked(); +} + +already_AddRefed<nsIThread> TRRService::TRRThread_locked() { + RefPtr<nsIThread> thread = sTRRBackgroundThread; + return thread.forget(); +} + +bool TRRService::IsOnTRRThread() { + nsCOMPtr<nsIThread> thread; + { + MutexAutoLock lock(mLock); + thread = sTRRBackgroundThread; + } + if (!thread) { + return false; + } + + return thread->IsOnCurrentThread(); +} + +NS_IMETHODIMP +TRRService::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) { + MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); + LOG(("TRR::Observe() topic=%s\n", aTopic)); + if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { + ReadPrefs(NS_ConvertUTF16toUTF8(aData).get()); + + MutexAutoLock lock(mLock); + if (((mConfirmationState == CONFIRM_INIT) && !mBootstrapAddr.IsEmpty() && + (mMode == MODE_TRRONLY)) || + (mConfirmationState == CONFIRM_FAILED)) { + mConfirmationState = CONFIRM_TRYING; + MaybeConfirm_locked(); + } + + } else if (!strcmp(aTopic, kOpenCaptivePortalLoginEvent)) { + // We are in a captive portal + LOG(("TRRservice in captive portal\n")); + mCaptiveIsPassed = false; + } else if (!strcmp(aTopic, NS_CAPTIVE_PORTAL_CONNECTIVITY)) { + nsAutoCString data = NS_ConvertUTF16toUTF8(aData); + LOG(("TRRservice captive portal was %s\n", data.get())); + + // We should avoid doing calling MaybeConfirm in response to a pref change + // unless the service is in a TRR=enabled mode. + if (mMode == MODE_TRRFIRST || mMode == MODE_TRRONLY) { + if (!mCaptiveIsPassed) { + if (mConfirmationState != CONFIRM_OK) { + mConfirmationState = CONFIRM_TRYING; + MaybeConfirm(); + } + } else { + LOG(("TRRservice CP clear when already up!\n")); + } + mCaptiveIsPassed = true; + } + + } else if (!strcmp(aTopic, kClearPrivateData) || !strcmp(aTopic, kPurge)) { + // flush the TRR blocklist + auto bl = mTRRBLStorage.Lock(); + bl->Clear(); + } else if (!strcmp(aTopic, NS_DNS_SUFFIX_LIST_UPDATED_TOPIC) || + !strcmp(aTopic, NS_NETWORK_LINK_TOPIC)) { + // nsINetworkLinkService is only available on parent process. + if (XRE_IsParentProcess()) { + nsCOMPtr<nsINetworkLinkService> link = do_QueryInterface(aSubject); + // The network link service notification normally passes itself as the + // subject, but some unit tests will sometimes pass a null subject. + if (link) { + nsTArray<nsCString> suffixList; + link->GetDnsSuffixList(suffixList); + RebuildSuffixList(std::move(suffixList)); + } + } + + if (!strcmp(aTopic, NS_NETWORK_LINK_TOPIC) && mURISetByDetection) { + // If the URI was set via SetDetectedTrrURI we need to restore it to the + // default pref when a network link change occurs. + CheckURIPrefs(); + } + } else if (!strcmp(aTopic, "xpcom-shutdown-threads")) { + if (sTRRBackgroundThread) { + nsCOMPtr<nsIThread> thread; + { + MutexAutoLock lock(mLock); + thread = sTRRBackgroundThread.get(); + sTRRBackgroundThread = nullptr; + } + MOZ_ALWAYS_SUCCEEDS(thread->Shutdown()); + sTRRServicePtr = nullptr; + } + } + return NS_OK; +} + +void TRRService::RebuildSuffixList(nsTArray<nsCString>&& aSuffixList) { + if (!StaticPrefs::network_trr_split_horizon_mitigations()) { + return; + } + + MutexAutoLock lock(mLock); + mDNSSuffixDomains.Clear(); + for (const auto& item : aSuffixList) { + LOG(("TRRService adding %s to suffix list", item.get())); + mDNSSuffixDomains.PutEntry(item); + } +} + +void TRRService::MaybeConfirm() { + MutexAutoLock lock(mLock); + MaybeConfirm_locked(); +} + +void TRRService::MaybeConfirm_locked() { + mLock.AssertCurrentThreadOwns(); + if (mMode == MODE_TRROFF || mConfirmer || + mConfirmationState != CONFIRM_TRYING) { + LOG( + ("TRRService:MaybeConfirm mode=%d, mConfirmer=%p " + "mConfirmationState=%d\n", + (int)mMode, (void*)mConfirmer, (int)mConfirmationState)); + return; + } + + if (mConfirmationNS.Equals("skip") || mMode == MODE_TRRONLY) { + LOG(("TRRService starting confirmation test %s SKIPPED\n", + mPrivateURI.get())); + mConfirmationState = CONFIRM_OK; + } else { + LOG(("TRRService starting confirmation test %s %s\n", mPrivateURI.get(), + mConfirmationNS.get())); + mConfirmer = new TRR(this, mConfirmationNS, TRRTYPE_NS, ""_ns, false); + DispatchTRRRequestInternal(mConfirmer, false); + } +} + +bool TRRService::MaybeBootstrap(const nsACString& aPossible, + nsACString& aResult) { + MutexAutoLock lock(mLock); + if (mMode == MODE_TRROFF || mBootstrapAddr.IsEmpty()) { + return false; + } + + nsCOMPtr<nsIURI> url; + nsresult rv = + NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID) + .Apply(NS_MutatorMethod(&nsIStandardURLMutator::Init, + nsIStandardURL::URLTYPE_STANDARD, 443, + mPrivateURI, nullptr, nullptr, nullptr)) + .Finalize(url); + if (NS_FAILED(rv)) { + LOG(("TRRService::MaybeBootstrap failed to create URI!\n")); + return false; + } + + nsAutoCString host; + url->GetHost(host); + if (!aPossible.Equals(host)) { + return false; + } + LOG(("TRRService::MaybeBootstrap: use %s instead of %s\n", + mBootstrapAddr.get(), host.get())); + aResult = mBootstrapAddr; + return true; +} + +bool TRRService::IsDomainBlocked(const nsACString& aHost, + const nsACString& aOriginSuffix, + bool aPrivateBrowsing) { + if (!Enabled(nsIRequest::TRR_DEFAULT_MODE)) { + return true; + } + + auto bl = mTRRBLStorage.Lock(); + if (bl->IsEmpty()) { + return false; + } + + // use a unified casing for the hashkey + nsAutoCString hashkey(aHost + aOriginSuffix); + if (int32_t* val = bl->GetValue(hashkey)) { + int32_t until = *val + mBlocklistDurationSeconds; + int32_t expire = NowInSeconds(); + if (until > expire) { + LOG(("Host [%s] is TRR blocklisted\n", nsCString(aHost).get())); + return true; + } + + // the blocklisted entry has expired + bl->Remove(hashkey); + } + return false; +} + +// When running in TRR-only mode, the blocklist is not used and it will also +// try resolving the localhost / .local names. +bool TRRService::IsTemporarilyBlocked(const nsACString& aHost, + const nsACString& aOriginSuffix, + bool aPrivateBrowsing, + bool aParentsToo) // false if domain +{ + if (mMode == MODE_TRRONLY) { + return false; // might as well try + } + + LOG(("Checking if host [%s] is blocklisted", aHost.BeginReading())); + + int32_t dot = aHost.FindChar('.'); + if ((dot == kNotFound) && aParentsToo) { + // Only if a full host name. Domains can be dotless to be able to + // blocklist entire TLDs + return true; + } + + if (IsDomainBlocked(aHost, aOriginSuffix, aPrivateBrowsing)) { + return true; + } + + nsDependentCSubstring domain = Substring(aHost, 0); + while (dot != kNotFound) { + dot++; + domain.Rebind(domain, dot, domain.Length() - dot); + + if (IsDomainBlocked(domain, aOriginSuffix, aPrivateBrowsing)) { + return true; + } + + dot = domain.FindChar('.'); + } + + return false; +} + +bool TRRService::IsExcludedFromTRR(const nsACString& aHost) { + // This method may be called off the main thread. We need to lock so + // mExcludedDomains and mDNSSuffixDomains don't change while this code + // is running. + MutexAutoLock lock(mLock); + + return IsExcludedFromTRR_unlocked(aHost); +} + +bool TRRService::IsExcludedFromTRR_unlocked(const nsACString& aHost) { + if (!NS_IsMainThread()) { + mLock.AssertCurrentThreadOwns(); + } + + int32_t dot = 0; + // iteratively check the sub-domain of |aHost| + while (dot < static_cast<int32_t>(aHost.Length())) { + nsDependentCSubstring subdomain = + Substring(aHost, dot, aHost.Length() - dot); + + if (mExcludedDomains.GetEntry(subdomain)) { + LOG(("Subdomain [%s] of host [%s] Is Excluded From TRR via pref\n", + subdomain.BeginReading(), aHost.BeginReading())); + return true; + } + if (mDNSSuffixDomains.GetEntry(subdomain)) { + LOG(("Subdomain [%s] of host [%s] Is Excluded From TRR via pref\n", + subdomain.BeginReading(), aHost.BeginReading())); + return true; + } + if (mEtcHostsDomains.GetEntry(subdomain)) { + LOG(("Subdomain [%s] of host [%s] Is Excluded From TRR by /etc/hosts\n", + subdomain.BeginReading(), aHost.BeginReading())); + return true; + } + + dot = aHost.FindChar('.', dot + 1); + if (dot == kNotFound) { + break; + } + dot++; + } + + return false; +} + +void TRRService::AddToBlocklist(const nsACString& aHost, + const nsACString& aOriginSuffix, + bool privateBrowsing, bool aParentsToo) { + LOG(("TRR blocklist %s\n", nsCString(aHost).get())); + nsAutoCString hashkey(aHost + aOriginSuffix); + + // this overwrites any existing entry + { + auto bl = mTRRBLStorage.Lock(); + bl->Put(hashkey, NowInSeconds()); + } + + if (aParentsToo) { + // when given a full host name, verify its domain as well + int32_t dot = aHost.FindChar('.'); + if (dot != kNotFound) { + // this has a domain to be checked + dot++; + nsDependentCSubstring domain = + Substring(aHost, dot, aHost.Length() - dot); + nsAutoCString check(domain); + if (IsTemporarilyBlocked(check, aOriginSuffix, privateBrowsing, false)) { + // the domain part is already blocklisted, no need to add this entry + return; + } + // verify 'check' over TRR + LOG(("TRR: verify if '%s' resolves as NS\n", check.get())); + + // check if there's an NS entry for this name + RefPtr<TRR> trr = + new TRR(this, check, TRRTYPE_NS, aOriginSuffix, privateBrowsing); + DispatchTRRRequest(trr); + } + } +} + +NS_IMETHODIMP +TRRService::Notify(nsITimer* aTimer) { + if (aTimer == mRetryConfirmTimer) { + mRetryConfirmTimer = nullptr; + if (mConfirmationState == CONFIRM_FAILED) { + LOG(("TRRService retry NS of %s\n", mConfirmationNS.get())); + mConfirmationState = CONFIRM_TRYING; + MaybeConfirm(); + } + } else { + MOZ_CRASH("Unknown timer"); + } + + return NS_OK; +} + +void TRRService::TRRIsOkay(enum TrrOkay aReason) { + MOZ_ASSERT_IF(XRE_IsParentProcess(), NS_IsMainThread() || IsOnTRRThread()); + MOZ_ASSERT_IF(XRE_IsSocketProcess(), NS_IsMainThread()); + + Telemetry::AccumulateCategoricalKeyed( + AutoDetectedKey(), + aReason == OKAY_NORMAL + ? Telemetry::LABELS_DNS_TRR_SUCCESS2::Fine + : (aReason == OKAY_TIMEOUT + ? Telemetry::LABELS_DNS_TRR_SUCCESS2::Timeout + : Telemetry::LABELS_DNS_TRR_SUCCESS2::Bad)); + if (aReason == OKAY_NORMAL) { + mTRRFailures = 0; + } else if ((mMode == MODE_TRRFIRST) && (mConfirmationState == CONFIRM_OK)) { + // only count failures while in OK state + uint32_t fails = ++mTRRFailures; + if (fails >= StaticPrefs::network_trr_max_fails()) { + LOG(("TRRService goes FAILED after %u failures in a row\n", fails)); + mConfirmationState = CONFIRM_FAILED; + // Fire off a timer and start re-trying the NS domain again + NS_NewTimerWithCallback(getter_AddRefs(mRetryConfirmTimer), this, + mRetryConfirmInterval, nsITimer::TYPE_ONE_SHOT); + mTRRFailures = 0; // clear it again + } + } +} + +AHostResolver::LookupStatus TRRService::CompleteLookup( + nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb, + const nsACString& aOriginSuffix, nsHostRecord::TRRSkippedReason aReason) { + // this is an NS check for the TRR blocklist or confirmationNS check + + MOZ_ASSERT_IF(XRE_IsParentProcess(), NS_IsMainThread() || IsOnTRRThread()); + MOZ_ASSERT_IF(XRE_IsSocketProcess(), NS_IsMainThread()); + MOZ_ASSERT(!rec); + + RefPtr<AddrInfo> newRRSet(aNewRRSet); + MOZ_ASSERT(newRRSet && newRRSet->IsTRR() == TRRTYPE_NS); + +#ifdef DEBUG + { + MutexAutoLock lock(mLock); + MOZ_ASSERT(!mConfirmer || (mConfirmationState == CONFIRM_TRYING)); + } +#endif + if (mConfirmationState == CONFIRM_TRYING) { + { + MutexAutoLock lock(mLock); + MOZ_ASSERT(mConfirmer); + mConfirmationState = NS_SUCCEEDED(status) ? CONFIRM_OK : CONFIRM_FAILED; + LOG(("TRRService finishing confirmation test %s %d %X\n", + mPrivateURI.get(), (int)mConfirmationState, (unsigned int)status)); + mConfirmer = nullptr; + + if (mConfirmationState == CONFIRM_OK) { + // A fresh confirmation means previous blocked entries might not + // be valid anymore. + auto bl = mTRRBLStorage.Lock(); + bl->Clear(); + } + } + if (mConfirmationState == CONFIRM_FAILED) { + // retry failed NS confirmation + NS_NewTimerWithCallback(getter_AddRefs(mRetryConfirmTimer), this, + mRetryConfirmInterval, nsITimer::TYPE_ONE_SHOT); + if (mRetryConfirmInterval < 64000) { + // double the interval up to this point + mRetryConfirmInterval *= 2; + } + } else { + if (mMode != MODE_TRRONLY) { + // don't accumulate trronly data here since trronly failures are + // handled above by trying again, so counting the successes here would + // skew the numbers + Telemetry::Accumulate(Telemetry::DNS_TRR_NS_VERFIFIED2, + TRRService::AutoDetectedKey(), + (mConfirmationState == CONFIRM_OK)); + } + mRetryConfirmInterval = StaticPrefs::network_trr_retry_timeout_ms(); + } + return LOOKUP_OK; + } + + // when called without a host record, this is a domain name check response. + if (NS_SUCCEEDED(status)) { + LOG(("TRR verified %s to be fine!\n", newRRSet->Hostname().get())); + } else { + LOG(("TRR says %s doesn't resolve as NS!\n", newRRSet->Hostname().get())); + AddToBlocklist(newRRSet->Hostname(), aOriginSuffix, pb, false); + } + return LOOKUP_OK; +} + +AHostResolver::LookupStatus TRRService::CompleteLookupByType( + nsHostRecord*, nsresult, mozilla::net::TypeRecordResultType& aResult, + uint32_t aTtl, bool aPb) { + return LOOKUP_OK; +} + +#undef LOG + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/TRRService.h b/netwerk/dns/TRRService.h new file mode 100644 index 0000000000..0a34696a03 --- /dev/null +++ b/netwerk/dns/TRRService.h @@ -0,0 +1,152 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef TRRService_h_ +#define TRRService_h_ + +#include "mozilla/DataMutex.h" +#include "nsHostResolver.h" +#include "nsIObserver.h" +#include "nsITimer.h" +#include "nsWeakReference.h" +#include "TRRServiceBase.h" + +class nsDNSService; +class nsIPrefBranch; +class nsINetworkLinkService; +class nsIObserverService; + +namespace mozilla { +namespace net { + +class TRRServiceChild; +class TRRServiceParent; + +class TRRService : public TRRServiceBase, + public nsIObserver, + public nsITimerCallback, + public nsSupportsWeakReference, + public AHostResolver { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIOBSERVER + NS_DECL_NSITIMERCALLBACK + + TRRService(); + nsresult Init(); + nsresult Start(); + bool Enabled(nsIRequest::TRRMode aMode = nsIRequest::TRR_FIRST_MODE); + bool IsConfirmed() { return mConfirmationState == CONFIRM_OK; } + + bool DisableIPv6() { return mDisableIPv6; } + nsresult GetURI(nsACString& result); + nsresult GetCredentials(nsCString& result); + uint32_t GetRequestTimeout(); + + LookupStatus CompleteLookup(nsHostRecord*, nsresult, mozilla::net::AddrInfo*, + bool pb, const nsACString& aOriginSuffix, + nsHostRecord::TRRSkippedReason aReason) override; + LookupStatus CompleteLookupByType(nsHostRecord*, nsresult, + mozilla::net::TypeRecordResultType&, + uint32_t, bool pb) override; + void AddToBlocklist(const nsACString& host, const nsACString& originSuffix, + bool privateBrowsing, bool aParentsToo); + bool IsTemporarilyBlocked(const nsACString& aHost, + const nsACString& aOriginSuffix, + bool aPrivateBrowsing, bool aParentsToo); + bool IsExcludedFromTRR(const nsACString& aHost); + + bool MaybeBootstrap(const nsACString& possible, nsACString& result); + enum TrrOkay { OKAY_NORMAL = 0, OKAY_TIMEOUT = 1, OKAY_BAD = 2 }; + void TRRIsOkay(enum TrrOkay aReason); + bool ParentalControlEnabled() const { return mParentalControlEnabled; } + + nsresult DispatchTRRRequest(TRR* aTrrRequest); + already_AddRefed<nsIThread> TRRThread(); + bool IsOnTRRThread(); + + bool IsUsingAutoDetectedURL() { return mURISetByDetection; } + static const nsCString& AutoDetectedKey(); + + private: + virtual ~TRRService(); + + friend class TRRServiceChild; + friend class TRRServiceParent; + static void AddObserver(nsIObserver* aObserver, + nsIObserverService* aObserverService = nullptr); + static bool CheckCaptivePortalIsPassed(); + static bool GetParentalControlEnabledInternal(); + static bool CheckPlatformDNSStatus(nsINetworkLinkService* aLinkService); + + nsresult ReadPrefs(const char* name); + void GetPrefBranch(nsIPrefBranch** result); + void MaybeConfirm(); + void MaybeConfirm_locked(); + friend class ::nsDNSService; + void SetDetectedTrrURI(const nsACString& aURI); + + bool IsDomainBlocked(const nsACString& aHost, const nsACString& aOriginSuffix, + bool aPrivateBrowsing); + bool IsExcludedFromTRR_unlocked(const nsACString& aHost); + + void RebuildSuffixList(nsTArray<nsCString>&& aSuffixList); + + nsresult DispatchTRRRequestInternal(TRR* aTrrRequest, bool aWithLock); + already_AddRefed<nsIThread> TRRThread_locked(); + + // This method will process the URI and try to set mPrivateURI to that value. + // Will return true if performed the change (if the value was different) + // or false if mPrivateURI already had that value. + bool MaybeSetPrivateURI(const nsACString& aURI) override; + void ClearEntireCache(); + + virtual void ReadEtcHostsFile() override; + void AddEtcHosts(const nsTArray<nsCString>&); + + bool mInitialized; + Atomic<uint32_t, Relaxed> mBlocklistDurationSeconds; + + Mutex mLock; + + nsCString mPrivateCred; // main thread only + nsCString mConfirmationNS; + nsCString mBootstrapAddr; + + Atomic<bool, Relaxed> + mCaptiveIsPassed; // set when captive portal check is passed + Atomic<bool, Relaxed> mDisableIPv6; // don't even try + + // TRR Blocklist storage + // mTRRBLStorage is only modified on the main thread, but we query whether it + // is initialized or not off the main thread as well. Therefore we need to + // lock while creating it and while accessing it off the main thread. + DataMutex<nsDataHashtable<nsCStringHashKey, int32_t>> mTRRBLStorage; + + // A set of domains that we should not use TRR for. + nsTHashtable<nsCStringHashKey> mExcludedDomains; + nsTHashtable<nsCStringHashKey> mDNSSuffixDomains; + nsTHashtable<nsCStringHashKey> mEtcHostsDomains; + + enum ConfirmationState { + CONFIRM_INIT = 0, + CONFIRM_TRYING = 1, + CONFIRM_OK = 2, + CONFIRM_FAILED = 3 + }; + Atomic<ConfirmationState, Relaxed> mConfirmationState; + RefPtr<TRR> mConfirmer; + nsCOMPtr<nsITimer> mRetryConfirmTimer; + uint32_t mRetryConfirmInterval; // milliseconds until retry + Atomic<uint32_t, Relaxed> mTRRFailures; + bool mParentalControlEnabled; +}; + +extern TRRService* gTRRService; + +} // namespace net +} // namespace mozilla + +#endif // TRRService_h_ diff --git a/netwerk/dns/TRRServiceBase.cpp b/netwerk/dns/TRRServiceBase.cpp new file mode 100644 index 0000000000..122af1103e --- /dev/null +++ b/netwerk/dns/TRRServiceBase.cpp @@ -0,0 +1,144 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "TRRServiceBase.h" + +#include "mozilla/Preferences.h" +#include "nsIOService.h" + +namespace mozilla { +namespace net { + +#undef LOG +extern mozilla::LazyLogModule gHostResolverLog; +#define LOG(args) MOZ_LOG(gHostResolverLog, mozilla::LogLevel::Debug, args) + +TRRServiceBase::TRRServiceBase() : mMode(0), mURISetByDetection(false) {} + +void TRRServiceBase::ProcessURITemplate(nsACString& aURI) { + // URI Template, RFC 6570. + if (aURI.IsEmpty()) { + return; + } + nsAutoCString scheme; + nsCOMPtr<nsIIOService> ios(do_GetIOService()); + if (ios) { + ios->ExtractScheme(aURI, scheme); + } + if (!scheme.Equals("https")) { + LOG(("TRRService TRR URI %s is not https. Not used.\n", + PromiseFlatCString(aURI).get())); + aURI.Truncate(); + return; + } + + // cut off everything from "{" to "}" sequences (potentially multiple), + // as a crude conversion from template into URI. + nsAutoCString uri(aURI); + + do { + nsCCharSeparatedTokenizer openBrace(uri, '{'); + if (openBrace.hasMoreTokens()) { + // the 'nextToken' is the left side of the open brace (or full uri) + nsAutoCString prefix(openBrace.nextToken()); + + // if there is an open brace, there's another token + const nsACString& endBrace = openBrace.nextToken(); + nsCCharSeparatedTokenizer closeBrace(endBrace, '}'); + if (closeBrace.hasMoreTokens()) { + // there is a close brace as well, make a URI out of the prefix + // and the suffix + closeBrace.nextToken(); + nsAutoCString suffix(closeBrace.nextToken()); + uri = prefix + suffix; + } else { + // no (more) close brace + break; + } + } else { + // no (more) open brace + break; + } + } while (true); + + aURI = uri; +} + +void TRRServiceBase::CheckURIPrefs() { + mURISetByDetection = false; + + // The user has set a custom URI so it takes precedence. + if (mURIPrefHasUserValue) { + MaybeSetPrivateURI(mURIPref); + return; + } + + // Check if the rollout addon has set a pref. + if (!mRolloutURIPref.IsEmpty()) { + MaybeSetPrivateURI(mRolloutURIPref); + return; + } + + // Otherwise just use the default value. + MaybeSetPrivateURI(mURIPref); +} + +// static +uint32_t ModeFromPrefs() { + // 0 - off, 1 - reserved, 2 - TRR first, 3 - TRR only, 4 - reserved, + // 5 - explicit off + + auto processPrefValue = [](uint32_t value) -> uint32_t { + if (value == MODE_RESERVED1 || value == MODE_RESERVED4 || + value > MODE_TRROFF) { + return MODE_TRROFF; + } + return value; + }; + + uint32_t tmp = MODE_NATIVEONLY; + if (NS_SUCCEEDED(Preferences::GetUint("network.trr.mode", &tmp))) { + tmp = processPrefValue(tmp); + } + + if (tmp != MODE_NATIVEONLY) { + return tmp; + } + + if (NS_SUCCEEDED(Preferences::GetUint(kRolloutModePref, &tmp))) { + return processPrefValue(tmp); + } + + return MODE_NATIVEONLY; +} + +void TRRServiceBase::OnTRRModeChange() { + uint32_t oldMode = mMode; + mMode = ModeFromPrefs(); + if (mMode != oldMode) { + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + obs->NotifyObservers(nullptr, NS_NETWORK_TRR_MODE_CHANGED_TOPIC, nullptr); + } + } + + static bool readHosts = false; + if ((mMode == MODE_TRRFIRST || mMode == MODE_TRRONLY) && !readHosts) { + readHosts = true; + ReadEtcHostsFile(); + } +} + +void TRRServiceBase::OnTRRURIChange() { + mURIPrefHasUserValue = Preferences::HasUserValue("network.trr.uri"); + Preferences::GetCString("network.trr.uri", mURIPref); + Preferences::GetCString(kRolloutURIPref, mRolloutURIPref); + + CheckURIPrefs(); +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/TRRServiceBase.h b/netwerk/dns/TRRServiceBase.h new file mode 100644 index 0000000000..d552b26099 --- /dev/null +++ b/netwerk/dns/TRRServiceBase.h @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef TRRServiceBase_h_ +#define TRRServiceBase_h_ + +#include "mozilla/Atomics.h" +#include "nsString.h" + +namespace mozilla { +namespace net { + +class TRRServiceBase { + public: + TRRServiceBase(); + uint32_t Mode() { return mMode; } + + protected: + ~TRRServiceBase() = default; + virtual bool MaybeSetPrivateURI(const nsACString& aURI) = 0; + void ProcessURITemplate(nsACString& aURI); + // Checks the network.trr.uri or the doh-rollout.uri prefs and sets the URI + // in order of preference: + // 1. The value of network.trr.uri if it is not the default one, meaning + // is was set by an explicit user action + // 2. The value of doh-rollout.uri if it exists + // this is set by the rollout addon + // 3. The default value of network.trr.uri + void CheckURIPrefs(); + + void OnTRRModeChange(); + void OnTRRURIChange(); + + virtual void ReadEtcHostsFile() {} + + nsCString mPrivateURI; + // Pref caches should only be used on the main thread. + bool mURIPrefHasUserValue = false; + nsCString mURIPref; + nsCString mRolloutURIPref; + + Atomic<uint32_t, Relaxed> mMode; + Atomic<bool, Relaxed> mURISetByDetection; +}; + +} // namespace net +} // namespace mozilla + +#endif // TRRServiceBase_h_ diff --git a/netwerk/dns/TRRServiceChild.cpp b/netwerk/dns/TRRServiceChild.cpp new file mode 100644 index 0000000000..365f061a0d --- /dev/null +++ b/netwerk/dns/TRRServiceChild.cpp @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/net/TRRServiceChild.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/StaticPtr.h" +#include "nsIDNService.h" +#include "nsIObserverService.h" +#include "TRRService.h" + +namespace mozilla { +namespace net { + +static StaticRefPtr<nsIDNSService> sDNSService; + +void TRRServiceChild::Init(const bool& aCaptiveIsPassed, + const bool& aParentalControlEnabled, + nsTArray<nsCString>&& aDNSSuffixList) { + nsCOMPtr<nsIDNSService> dns = + do_GetService("@mozilla.org/network/dns-service;1"); + sDNSService = dns; + ClearOnShutdown(&sDNSService); + MOZ_ASSERT(sDNSService); + MOZ_ASSERT(gTRRService); + + gTRRService->mCaptiveIsPassed = aCaptiveIsPassed; + gTRRService->mParentalControlEnabled = aParentalControlEnabled; + gTRRService->RebuildSuffixList(std::move(aDNSSuffixList)); +} + +mozilla::ipc::IPCResult TRRServiceChild::RecvUpdatePlatformDNSInformation( + nsTArray<nsCString>&& aDNSSuffixList) { + gTRRService->RebuildSuffixList(std::move(aDNSSuffixList)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult TRRServiceChild::RecvUpdateParentalControlEnabled( + const bool& aEnabled) { + gTRRService->mParentalControlEnabled = aEnabled; + return IPC_OK(); +} + +mozilla::ipc::IPCResult TRRServiceChild::RecvClearDNSCache( + const bool& aTrrToo) { + Unused << sDNSService->ClearCache(aTrrToo); + return IPC_OK(); +} + +mozilla::ipc::IPCResult TRRServiceChild::RecvSetDetectedTrrURI( + const nsCString& aURI) { + gTRRService->SetDetectedTrrURI(aURI); + return IPC_OK(); +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/TRRServiceChild.h b/netwerk/dns/TRRServiceChild.h new file mode 100644 index 0000000000..7334a5f3d3 --- /dev/null +++ b/netwerk/dns/TRRServiceChild.h @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_TRRServiceChild_h +#define mozilla_net_TRRServiceChild_h + +#include "mozilla/net/PTRRServiceChild.h" + +namespace mozilla { + +namespace ipc { +class FileDescriptor; +} // namespace ipc + +namespace net { + +class TRRServiceChild : public PTRRServiceChild { + public: + NS_INLINE_DECL_REFCOUNTING(TRRServiceChild, override) + + explicit TRRServiceChild() = default; + + void Init(const bool& aCaptiveIsPassed, const bool& aParentalControlEnabled, + nsTArray<nsCString>&& aDNSSuffixList); + mozilla::ipc::IPCResult RecvNotifyObserver(const nsCString& aTopic, + const nsString& aData); + mozilla::ipc::IPCResult RecvUpdatePlatformDNSInformation( + nsTArray<nsCString>&& aDNSSuffixList); + mozilla::ipc::IPCResult RecvUpdateParentalControlEnabled( + const bool& aEnabled); + mozilla::ipc::IPCResult RecvClearDNSCache(const bool& aTrrToo); + mozilla::ipc::IPCResult RecvSetDetectedTrrURI(const nsCString& aURI); + + private: + virtual ~TRRServiceChild() = default; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_TRRServiceChild_h diff --git a/netwerk/dns/TRRServiceParent.cpp b/netwerk/dns/TRRServiceParent.cpp new file mode 100644 index 0000000000..9002f239fe --- /dev/null +++ b/netwerk/dns/TRRServiceParent.cpp @@ -0,0 +1,155 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/net/TRRServiceParent.h" + +#include "mozilla/ipc/FileDescriptor.h" +#include "mozilla/net/SocketProcessParent.h" +#include "mozilla/psm/PSMIPCTypes.h" +#include "mozilla/Preferences.h" +#include "mozilla/Unused.h" +#include "nsICaptivePortalService.h" +#include "nsIParentalControlsService.h" +#include "nsINetworkLinkService.h" +#include "nsIObserverService.h" +#include "nsIOService.h" +#include "nsNetCID.h" +#include "TRRService.h" + +namespace mozilla { +namespace net { + +static const char kRolloutURIPref[] = "doh-rollout.uri"; +static const char kRolloutModePref[] = "doh-rollout.mode"; + +static const char* gTRRUriCallbackPrefs[] = { + "network.trr.uri", "network.trr.mode", kRolloutURIPref, kRolloutModePref, + nullptr, +}; + +NS_IMPL_ISUPPORTS(TRRServiceParent, nsIObserver, nsISupportsWeakReference) + +void TRRServiceParent::Init() { + MOZ_ASSERT(gIOService); + + if (!gIOService->SocketProcessReady()) { + RefPtr<TRRServiceParent> self = this; + gIOService->CallOrWaitForSocketProcess([self]() { self->Init(); }); + return; + } + + SocketProcessParent* socketParent = SocketProcessParent::GetSingleton(); + if (!socketParent) { + return; + } + + nsCOMPtr<nsIObserverService> obs = + static_cast<nsIObserverService*>(gIOService); + TRRService::AddObserver(this, obs); + + bool captiveIsPassed = TRRService::CheckCaptivePortalIsPassed(); + bool parentalControlEnabled = TRRService::GetParentalControlEnabledInternal(); + + nsCOMPtr<nsINetworkLinkService> nls = + do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID); + nsTArray<nsCString> suffixList; + if (nls) { + nls->GetDnsSuffixList(suffixList); + } + + Preferences::RegisterPrefixCallbacks(TRRServiceParent::PrefsChanged, + gTRRUriCallbackPrefs, this); + prefsChanged(nullptr); + + Unused << socketParent->SendPTRRServiceConstructor( + this, captiveIsPassed, parentalControlEnabled, suffixList); +} + +NS_IMETHODIMP +TRRServiceParent::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) { + if (!strcmp(aTopic, NS_DNS_SUFFIX_LIST_UPDATED_TOPIC) || + !strcmp(aTopic, NS_NETWORK_LINK_TOPIC)) { + nsCOMPtr<nsINetworkLinkService> link = do_QueryInterface(aSubject); + // The network link service notification normally passes itself as the + // subject, but some unit tests will sometimes pass a null subject. + if (link) { + nsTArray<nsCString> suffixList; + link->GetDnsSuffixList(suffixList); + Unused << SendUpdatePlatformDNSInformation(suffixList); + } + + if (!strcmp(aTopic, NS_NETWORK_LINK_TOPIC) && mURISetByDetection) { + CheckURIPrefs(); + } + } + + return NS_OK; +} + +bool TRRServiceParent::MaybeSetPrivateURI(const nsACString& aURI) { + nsAutoCString newURI(aURI); + ProcessURITemplate(newURI); + + if (mPrivateURI.Equals(newURI)) { + return false; + } + + mPrivateURI = newURI; + + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + obs->NotifyObservers(nullptr, NS_NETWORK_TRR_URI_CHANGED_TOPIC, nullptr); + } + return true; +} + +void TRRServiceParent::SetDetectedTrrURI(const nsACString& aURI) { + if (mURIPrefHasUserValue) { + return; + } + + mURISetByDetection = MaybeSetPrivateURI(aURI); + RefPtr<TRRServiceParent> self = this; + nsCString uri(aURI); + gIOService->CallOrWaitForSocketProcess( + [self, uri]() { Unused << self->SendSetDetectedTrrURI(uri); }); +} + +void TRRServiceParent::GetTrrURI(nsACString& aURI) { aURI = mPrivateURI; } + +void TRRServiceParent::UpdateParentalControlEnabled() { + bool enabled = TRRService::GetParentalControlEnabledInternal(); + RefPtr<TRRServiceParent> self = this; + gIOService->CallOrWaitForSocketProcess([self, enabled]() { + Unused << self->SendUpdateParentalControlEnabled(enabled); + }); +} + +// static +void TRRServiceParent::PrefsChanged(const char* aName, void* aSelf) { + static_cast<TRRServiceParent*>(aSelf)->prefsChanged(aName); +} + +void TRRServiceParent::prefsChanged(const char* aName) { + if (!aName || !strcmp(aName, "network.trr.uri") || + !strcmp(aName, kRolloutURIPref)) { + OnTRRURIChange(); + } + + if (!aName || !strcmp(aName, "network.trr.mode") || + !strcmp(aName, kRolloutModePref)) { + OnTRRModeChange(); + } +} + +void TRRServiceParent::ActorDestroy(ActorDestroyReason why) { + Preferences::UnregisterPrefixCallbacks(TRRServiceParent::PrefsChanged, + gTRRUriCallbackPrefs, this); +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/TRRServiceParent.h b/netwerk/dns/TRRServiceParent.h new file mode 100644 index 0000000000..4df599fe1b --- /dev/null +++ b/netwerk/dns/TRRServiceParent.h @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_TRRServiceParent_h +#define mozilla_net_TRRServiceParent_h + +#include "mozilla/net/PTRRServiceParent.h" +#include "mozilla/net/TRRServiceBase.h" +#include "nsIObserver.h" +#include "nsWeakReference.h" + +namespace mozilla { +namespace net { + +class TRRServiceParent : public TRRServiceBase, + public nsIObserver, + public nsSupportsWeakReference, + public PTRRServiceParent { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIOBSERVER + + TRRServiceParent() = default; + void Init(); + void UpdateParentalControlEnabled(); + static void PrefsChanged(const char* aName, void* aSelf); + void SetDetectedTrrURI(const nsACString& aURI); + bool MaybeSetPrivateURI(const nsACString& aURI) override; + void GetTrrURI(nsACString& aURI); + + private: + virtual ~TRRServiceParent() = default; + virtual void ActorDestroy(ActorDestroyReason why) override; + void prefsChanged(const char* aName); +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_TRRServiceParent_h diff --git a/netwerk/dns/effective_tld_names.dat b/netwerk/dns/effective_tld_names.dat new file mode 100644 index 0000000000..038b492cb4 --- /dev/null +++ b/netwerk/dns/effective_tld_names.dat @@ -0,0 +1,13597 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +// Please pull this list from, and only from https://publicsuffix.org/list/public_suffix_list.dat, +// rather than any other VCS sites. Pulling from any other URL is not guaranteed to be supported. + +// Instructions on pulling and using this list can be found at https://publicsuffix.org/list/. + +// ===BEGIN ICANN DOMAINS=== + +// ac : https://en.wikipedia.org/wiki/.ac +ac +com.ac +edu.ac +gov.ac +net.ac +mil.ac +org.ac + +// ad : https://en.wikipedia.org/wiki/.ad +ad +nom.ad + +// ae : https://en.wikipedia.org/wiki/.ae +// see also: "Domain Name Eligibility Policy" at http://www.aeda.ae/eng/aepolicy.php +ae +co.ae +net.ae +org.ae +sch.ae +ac.ae +gov.ae +mil.ae + +// aero : see https://www.information.aero/index.php?id=66 +aero +accident-investigation.aero +accident-prevention.aero +aerobatic.aero +aeroclub.aero +aerodrome.aero +agents.aero +aircraft.aero +airline.aero +airport.aero +air-surveillance.aero +airtraffic.aero +air-traffic-control.aero +ambulance.aero +amusement.aero +association.aero +author.aero +ballooning.aero +broker.aero +caa.aero +cargo.aero +catering.aero +certification.aero +championship.aero +charter.aero +civilaviation.aero +club.aero +conference.aero +consultant.aero +consulting.aero +control.aero +council.aero +crew.aero +design.aero +dgca.aero +educator.aero +emergency.aero +engine.aero +engineer.aero +entertainment.aero +equipment.aero +exchange.aero +express.aero +federation.aero +flight.aero +fuel.aero +gliding.aero +government.aero +groundhandling.aero +group.aero +hanggliding.aero +homebuilt.aero +insurance.aero +journal.aero +journalist.aero +leasing.aero +logistics.aero +magazine.aero +maintenance.aero +media.aero +microlight.aero +modelling.aero +navigation.aero +parachuting.aero +paragliding.aero +passenger-association.aero +pilot.aero +press.aero +production.aero +recreation.aero +repbody.aero +res.aero +research.aero +rotorcraft.aero +safety.aero +scientist.aero +services.aero +show.aero +skydiving.aero +software.aero +student.aero +trader.aero +trading.aero +trainer.aero +union.aero +workinggroup.aero +works.aero + +// af : http://www.nic.af/help.jsp +af +gov.af +com.af +org.af +net.af +edu.af + +// ag : http://www.nic.ag/prices.htm +ag +com.ag +org.ag +net.ag +co.ag +nom.ag + +// ai : http://nic.com.ai/ +ai +off.ai +com.ai +net.ai +org.ai + +// al : http://www.ert.gov.al/ert_alb/faq_det.html?Id=31 +al +com.al +edu.al +gov.al +mil.al +net.al +org.al + +// am : https://www.amnic.net/policy/en/Policy_EN.pdf +am +co.am +com.am +commune.am +net.am +org.am + +// ao : https://en.wikipedia.org/wiki/.ao +// http://www.dns.ao/REGISTR.DOC +ao +ed.ao +gv.ao +og.ao +co.ao +pb.ao +it.ao + +// aq : https://en.wikipedia.org/wiki/.aq +aq + +// ar : https://nic.ar/nic-argentina/normativa-vigente +ar +com.ar +edu.ar +gob.ar +gov.ar +int.ar +mil.ar +musica.ar +net.ar +org.ar +tur.ar + +// arpa : https://en.wikipedia.org/wiki/.arpa +// Confirmed by registry <iana-questions@icann.org> 2008-06-18 +arpa +e164.arpa +in-addr.arpa +ip6.arpa +iris.arpa +uri.arpa +urn.arpa + +// as : https://en.wikipedia.org/wiki/.as +as +gov.as + +// asia : https://en.wikipedia.org/wiki/.asia +asia + +// at : https://en.wikipedia.org/wiki/.at +// Confirmed by registry <it@nic.at> 2008-06-17 +at +ac.at +co.at +gv.at +or.at +sth.ac.at + +// au : https://en.wikipedia.org/wiki/.au +// http://www.auda.org.au/ +au +// 2LDs +com.au +net.au +org.au +edu.au +gov.au +asn.au +id.au +// Historic 2LDs (closed to new registration, but sites still exist) +info.au +conf.au +oz.au +// CGDNs - http://www.cgdn.org.au/ +act.au +nsw.au +nt.au +qld.au +sa.au +tas.au +vic.au +wa.au +// 3LDs +act.edu.au +catholic.edu.au +// eq.edu.au - Removed at the request of the Queensland Department of Education +nsw.edu.au +nt.edu.au +qld.edu.au +sa.edu.au +tas.edu.au +vic.edu.au +wa.edu.au +// act.gov.au Bug 984824 - Removed at request of Greg Tankard +// nsw.gov.au Bug 547985 - Removed at request of <Shae.Donelan@services.nsw.gov.au> +// nt.gov.au Bug 940478 - Removed at request of Greg Connors <Greg.Connors@nt.gov.au> +qld.gov.au +sa.gov.au +tas.gov.au +vic.gov.au +wa.gov.au +// 4LDs +// education.tas.edu.au - Removed at the request of the Department of Education Tasmania +schools.nsw.edu.au + +// aw : https://en.wikipedia.org/wiki/.aw +aw +com.aw + +// ax : https://en.wikipedia.org/wiki/.ax +ax + +// az : https://en.wikipedia.org/wiki/.az +az +com.az +net.az +int.az +gov.az +org.az +edu.az +info.az +pp.az +mil.az +name.az +pro.az +biz.az + +// ba : http://nic.ba/users_data/files/pravilnik_o_registraciji.pdf +ba +com.ba +edu.ba +gov.ba +mil.ba +net.ba +org.ba + +// bb : https://en.wikipedia.org/wiki/.bb +bb +biz.bb +co.bb +com.bb +edu.bb +gov.bb +info.bb +net.bb +org.bb +store.bb +tv.bb + +// bd : https://en.wikipedia.org/wiki/.bd +*.bd + +// be : https://en.wikipedia.org/wiki/.be +// Confirmed by registry <tech@dns.be> 2008-06-08 +be +ac.be + +// bf : https://en.wikipedia.org/wiki/.bf +bf +gov.bf + +// bg : https://en.wikipedia.org/wiki/.bg +// https://www.register.bg/user/static/rules/en/index.html +bg +a.bg +b.bg +c.bg +d.bg +e.bg +f.bg +g.bg +h.bg +i.bg +j.bg +k.bg +l.bg +m.bg +n.bg +o.bg +p.bg +q.bg +r.bg +s.bg +t.bg +u.bg +v.bg +w.bg +x.bg +y.bg +z.bg +0.bg +1.bg +2.bg +3.bg +4.bg +5.bg +6.bg +7.bg +8.bg +9.bg + +// bh : https://en.wikipedia.org/wiki/.bh +bh +com.bh +edu.bh +net.bh +org.bh +gov.bh + +// bi : https://en.wikipedia.org/wiki/.bi +// http://whois.nic.bi/ +bi +co.bi +com.bi +edu.bi +or.bi +org.bi + +// biz : https://en.wikipedia.org/wiki/.biz +biz + +// bj : https://en.wikipedia.org/wiki/.bj +bj +asso.bj +barreau.bj +gouv.bj + +// bm : http://www.bermudanic.bm/dnr-text.txt +bm +com.bm +edu.bm +gov.bm +net.bm +org.bm + +// bn : http://www.bnnic.bn/faqs +bn +com.bn +edu.bn +gov.bn +net.bn +org.bn + +// bo : https://nic.bo/delegacion2015.php#h-1.10 +bo +com.bo +edu.bo +gob.bo +int.bo +org.bo +net.bo +mil.bo +tv.bo +web.bo +// Social Domains +academia.bo +agro.bo +arte.bo +blog.bo +bolivia.bo +ciencia.bo +cooperativa.bo +democracia.bo +deporte.bo +ecologia.bo +economia.bo +empresa.bo +indigena.bo +industria.bo +info.bo +medicina.bo +movimiento.bo +musica.bo +natural.bo +nombre.bo +noticias.bo +patria.bo +politica.bo +profesional.bo +plurinacional.bo +pueblo.bo +revista.bo +salud.bo +tecnologia.bo +tksat.bo +transporte.bo +wiki.bo + +// br : http://registro.br/dominio/categoria.html +// Submitted by registry <fneves@registro.br> +br +9guacu.br +abc.br +adm.br +adv.br +agr.br +aju.br +am.br +anani.br +aparecida.br +app.br +arq.br +art.br +ato.br +b.br +barueri.br +belem.br +bhz.br +bib.br +bio.br +blog.br +bmd.br +boavista.br +bsb.br +campinagrande.br +campinas.br +caxias.br +cim.br +cng.br +cnt.br +com.br +contagem.br +coop.br +coz.br +cri.br +cuiaba.br +curitiba.br +def.br +des.br +det.br +dev.br +ecn.br +eco.br +edu.br +emp.br +enf.br +eng.br +esp.br +etc.br +eti.br +far.br +feira.br +flog.br +floripa.br +fm.br +fnd.br +fortal.br +fot.br +foz.br +fst.br +g12.br +geo.br +ggf.br +goiania.br +gov.br +// gov.br 26 states + df https://en.wikipedia.org/wiki/States_of_Brazil +ac.gov.br +al.gov.br +am.gov.br +ap.gov.br +ba.gov.br +ce.gov.br +df.gov.br +es.gov.br +go.gov.br +ma.gov.br +mg.gov.br +ms.gov.br +mt.gov.br +pa.gov.br +pb.gov.br +pe.gov.br +pi.gov.br +pr.gov.br +rj.gov.br +rn.gov.br +ro.gov.br +rr.gov.br +rs.gov.br +sc.gov.br +se.gov.br +sp.gov.br +to.gov.br +gru.br +imb.br +ind.br +inf.br +jab.br +jampa.br +jdf.br +joinville.br +jor.br +jus.br +leg.br +lel.br +log.br +londrina.br +macapa.br +maceio.br +manaus.br +maringa.br +mat.br +med.br +mil.br +morena.br +mp.br +mus.br +natal.br +net.br +niteroi.br +*.nom.br +not.br +ntr.br +odo.br +ong.br +org.br +osasco.br +palmas.br +poa.br +ppg.br +pro.br +psc.br +psi.br +pvh.br +qsl.br +radio.br +rec.br +recife.br +rep.br +ribeirao.br +rio.br +riobranco.br +riopreto.br +salvador.br +sampa.br +santamaria.br +santoandre.br +saobernardo.br +saogonca.br +seg.br +sjc.br +slg.br +slz.br +sorocaba.br +srv.br +taxi.br +tc.br +tec.br +teo.br +the.br +tmp.br +trd.br +tur.br +tv.br +udi.br +vet.br +vix.br +vlog.br +wiki.br +zlg.br + +// bs : http://www.nic.bs/rules.html +bs +com.bs +net.bs +org.bs +edu.bs +gov.bs + +// bt : https://en.wikipedia.org/wiki/.bt +bt +com.bt +edu.bt +gov.bt +net.bt +org.bt + +// bv : No registrations at this time. +// Submitted by registry <jarle@uninett.no> +bv + +// bw : https://en.wikipedia.org/wiki/.bw +// http://www.gobin.info/domainname/bw.doc +// list of other 2nd level tlds ? +bw +co.bw +org.bw + +// by : https://en.wikipedia.org/wiki/.by +// http://tld.by/rules_2006_en.html +// list of other 2nd level tlds ? +by +gov.by +mil.by +// Official information does not indicate that com.by is a reserved +// second-level domain, but it's being used as one (see www.google.com.by and +// www.yahoo.com.by, for example), so we list it here for safety's sake. +com.by + +// http://hoster.by/ +of.by + +// bz : https://en.wikipedia.org/wiki/.bz +// http://www.belizenic.bz/ +bz +com.bz +net.bz +org.bz +edu.bz +gov.bz + +// ca : https://en.wikipedia.org/wiki/.ca +ca +// ca geographical names +ab.ca +bc.ca +mb.ca +nb.ca +nf.ca +nl.ca +ns.ca +nt.ca +nu.ca +on.ca +pe.ca +qc.ca +sk.ca +yk.ca +// gc.ca: https://en.wikipedia.org/wiki/.gc.ca +// see also: http://registry.gc.ca/en/SubdomainFAQ +gc.ca + +// cat : https://en.wikipedia.org/wiki/.cat +cat + +// cc : https://en.wikipedia.org/wiki/.cc +cc + +// cd : https://en.wikipedia.org/wiki/.cd +// see also: https://www.nic.cd/domain/insertDomain_2.jsp?act=1 +cd +gov.cd + +// cf : https://en.wikipedia.org/wiki/.cf +cf + +// cg : https://en.wikipedia.org/wiki/.cg +cg + +// ch : https://en.wikipedia.org/wiki/.ch +ch + +// ci : https://en.wikipedia.org/wiki/.ci +// http://www.nic.ci/index.php?page=charte +ci +org.ci +or.ci +com.ci +co.ci +edu.ci +ed.ci +ac.ci +net.ci +go.ci +asso.ci +aéroport.ci +int.ci +presse.ci +md.ci +gouv.ci + +// ck : https://en.wikipedia.org/wiki/.ck +*.ck +!www.ck + +// cl : https://www.nic.cl +// Confirmed by .CL registry <hsalgado@nic.cl> +cl +aprendemas.cl +co.cl +gob.cl +gov.cl +mil.cl + +// cm : https://en.wikipedia.org/wiki/.cm plus bug 981927 +cm +co.cm +com.cm +gov.cm +net.cm + +// cn : https://en.wikipedia.org/wiki/.cn +// Submitted by registry <tanyaling@cnnic.cn> +cn +ac.cn +com.cn +edu.cn +gov.cn +net.cn +org.cn +mil.cn +公司.cn +网络.cn +網絡.cn +// cn geographic names +ah.cn +bj.cn +cq.cn +fj.cn +gd.cn +gs.cn +gz.cn +gx.cn +ha.cn +hb.cn +he.cn +hi.cn +hl.cn +hn.cn +jl.cn +js.cn +jx.cn +ln.cn +nm.cn +nx.cn +qh.cn +sc.cn +sd.cn +sh.cn +sn.cn +sx.cn +tj.cn +xj.cn +xz.cn +yn.cn +zj.cn +hk.cn +mo.cn +tw.cn + +// co : https://en.wikipedia.org/wiki/.co +// Submitted by registry <tecnico@uniandes.edu.co> +co +arts.co +com.co +edu.co +firm.co +gov.co +info.co +int.co +mil.co +net.co +nom.co +org.co +rec.co +web.co + +// com : https://en.wikipedia.org/wiki/.com +com + +// coop : https://en.wikipedia.org/wiki/.coop +coop + +// cr : http://www.nic.cr/niccr_publico/showRegistroDominiosScreen.do +cr +ac.cr +co.cr +ed.cr +fi.cr +go.cr +or.cr +sa.cr + +// cu : https://en.wikipedia.org/wiki/.cu +cu +com.cu +edu.cu +org.cu +net.cu +gov.cu +inf.cu + +// cv : https://en.wikipedia.org/wiki/.cv +cv + +// cw : http://www.una.cw/cw_registry/ +// Confirmed by registry <registry@una.net> 2013-03-26 +cw +com.cw +edu.cw +net.cw +org.cw + +// cx : https://en.wikipedia.org/wiki/.cx +// list of other 2nd level tlds ? +cx +gov.cx + +// cy : http://www.nic.cy/ +// Submitted by registry Panayiotou Fotia <cydns@ucy.ac.cy> +cy +ac.cy +biz.cy +com.cy +ekloges.cy +gov.cy +ltd.cy +name.cy +net.cy +org.cy +parliament.cy +press.cy +pro.cy +tm.cy + +// cz : https://en.wikipedia.org/wiki/.cz +cz + +// de : https://en.wikipedia.org/wiki/.de +// Confirmed by registry <ops@denic.de> (with technical +// reservations) 2008-07-01 +de + +// dj : https://en.wikipedia.org/wiki/.dj +dj + +// dk : https://en.wikipedia.org/wiki/.dk +// Confirmed by registry <robert@dk-hostmaster.dk> 2008-06-17 +dk + +// dm : https://en.wikipedia.org/wiki/.dm +dm +com.dm +net.dm +org.dm +edu.dm +gov.dm + +// do : https://en.wikipedia.org/wiki/.do +do +art.do +com.do +edu.do +gob.do +gov.do +mil.do +net.do +org.do +sld.do +web.do + +// dz : http://www.nic.dz/images/pdf_nic/charte.pdf +dz +art.dz +asso.dz +com.dz +edu.dz +gov.dz +org.dz +net.dz +pol.dz +soc.dz +tm.dz + +// ec : http://www.nic.ec/reg/paso1.asp +// Submitted by registry <vabboud@nic.ec> +ec +com.ec +info.ec +net.ec +fin.ec +k12.ec +med.ec +pro.ec +org.ec +edu.ec +gov.ec +gob.ec +mil.ec + +// edu : https://en.wikipedia.org/wiki/.edu +edu + +// ee : http://www.eenet.ee/EENet/dom_reeglid.html#lisa_B +ee +edu.ee +gov.ee +riik.ee +lib.ee +med.ee +com.ee +pri.ee +aip.ee +org.ee +fie.ee + +// eg : https://en.wikipedia.org/wiki/.eg +eg +com.eg +edu.eg +eun.eg +gov.eg +mil.eg +name.eg +net.eg +org.eg +sci.eg + +// er : https://en.wikipedia.org/wiki/.er +*.er + +// es : https://www.nic.es/site_ingles/ingles/dominios/index.html +es +com.es +nom.es +org.es +gob.es +edu.es + +// et : https://en.wikipedia.org/wiki/.et +et +com.et +gov.et +org.et +edu.et +biz.et +name.et +info.et +net.et + +// eu : https://en.wikipedia.org/wiki/.eu +eu + +// fi : https://en.wikipedia.org/wiki/.fi +fi +// aland.fi : https://en.wikipedia.org/wiki/.ax +// This domain is being phased out in favor of .ax. As there are still many +// domains under aland.fi, we still keep it on the list until aland.fi is +// completely removed. +// TODO: Check for updates (expected to be phased out around Q1/2009) +aland.fi + +// fj : http://domains.fj/ +// Submitted by registry <garth.miller@cocca.org.nz> 2020-02-11 +fj +ac.fj +biz.fj +com.fj +gov.fj +info.fj +mil.fj +name.fj +net.fj +org.fj +pro.fj + +// fk : https://en.wikipedia.org/wiki/.fk +*.fk + +// fm : https://en.wikipedia.org/wiki/.fm +com.fm +edu.fm +net.fm +org.fm +fm + +// fo : https://en.wikipedia.org/wiki/.fo +fo + +// fr : http://www.afnic.fr/ +// domaines descriptifs : https://www.afnic.fr/medias/documents/Cadre_legal/Afnic_Naming_Policy_12122016_VEN.pdf +fr +asso.fr +com.fr +gouv.fr +nom.fr +prd.fr +tm.fr +// domaines sectoriels : https://www.afnic.fr/en/products-and-services/the-fr-tld/sector-based-fr-domains-4.html +aeroport.fr +avocat.fr +avoues.fr +cci.fr +chambagri.fr +chirurgiens-dentistes.fr +experts-comptables.fr +geometre-expert.fr +greta.fr +huissier-justice.fr +medecin.fr +notaires.fr +pharmacien.fr +port.fr +veterinaire.fr + +// ga : https://en.wikipedia.org/wiki/.ga +ga + +// gb : This registry is effectively dormant +// Submitted by registry <Damien.Shaw@ja.net> +gb + +// gd : https://en.wikipedia.org/wiki/.gd +edu.gd +gov.gd +gd + +// ge : http://www.nic.net.ge/policy_en.pdf +ge +com.ge +edu.ge +gov.ge +org.ge +mil.ge +net.ge +pvt.ge + +// gf : https://en.wikipedia.org/wiki/.gf +gf + +// gg : http://www.channelisles.net/register-domains/ +// Confirmed by registry <nigel@channelisles.net> 2013-11-28 +gg +co.gg +net.gg +org.gg + +// gh : https://en.wikipedia.org/wiki/.gh +// see also: http://www.nic.gh/reg_now.php +// Although domains directly at second level are not possible at the moment, +// they have been possible for some time and may come back. +gh +com.gh +edu.gh +gov.gh +org.gh +mil.gh + +// gi : http://www.nic.gi/rules.html +gi +com.gi +ltd.gi +gov.gi +mod.gi +edu.gi +org.gi + +// gl : https://en.wikipedia.org/wiki/.gl +// http://nic.gl +gl +co.gl +com.gl +edu.gl +net.gl +org.gl + +// gm : http://www.nic.gm/htmlpages%5Cgm-policy.htm +gm + +// gn : http://psg.com/dns/gn/gn.txt +// Submitted by registry <randy@psg.com> +gn +ac.gn +com.gn +edu.gn +gov.gn +org.gn +net.gn + +// gov : https://en.wikipedia.org/wiki/.gov +gov + +// gp : http://www.nic.gp/index.php?lang=en +gp +com.gp +net.gp +mobi.gp +edu.gp +org.gp +asso.gp + +// gq : https://en.wikipedia.org/wiki/.gq +gq + +// gr : https://grweb.ics.forth.gr/english/1617-B-2005.html +// Submitted by registry <segred@ics.forth.gr> +gr +com.gr +edu.gr +net.gr +org.gr +gov.gr + +// gs : https://en.wikipedia.org/wiki/.gs +gs + +// gt : https://www.gt/sitio/registration_policy.php?lang=en +gt +com.gt +edu.gt +gob.gt +ind.gt +mil.gt +net.gt +org.gt + +// gu : http://gadao.gov.gu/register.html +// University of Guam : https://www.uog.edu +// Submitted by uognoc@triton.uog.edu +gu +com.gu +edu.gu +gov.gu +guam.gu +info.gu +net.gu +org.gu +web.gu + +// gw : https://en.wikipedia.org/wiki/.gw +gw + +// gy : https://en.wikipedia.org/wiki/.gy +// http://registry.gy/ +gy +co.gy +com.gy +edu.gy +gov.gy +net.gy +org.gy + +// hk : https://www.hkirc.hk +// Submitted by registry <hk.tech@hkirc.hk> +hk +com.hk +edu.hk +gov.hk +idv.hk +net.hk +org.hk +公司.hk +教育.hk +敎育.hk +政府.hk +個人.hk +个人.hk +箇人.hk +網络.hk +网络.hk +组織.hk +網絡.hk +网絡.hk +组织.hk +組織.hk +組织.hk + +// hm : https://en.wikipedia.org/wiki/.hm +hm + +// hn : http://www.nic.hn/politicas/ps02,,05.html +hn +com.hn +edu.hn +org.hn +net.hn +mil.hn +gob.hn + +// hr : http://www.dns.hr/documents/pdf/HRTLD-regulations.pdf +hr +iz.hr +from.hr +name.hr +com.hr + +// ht : http://www.nic.ht/info/charte.cfm +ht +com.ht +shop.ht +firm.ht +info.ht +adult.ht +net.ht +pro.ht +org.ht +med.ht +art.ht +coop.ht +pol.ht +asso.ht +edu.ht +rel.ht +gouv.ht +perso.ht + +// hu : http://www.domain.hu/domain/English/sld.html +// Confirmed by registry <pasztor@iszt.hu> 2008-06-12 +hu +co.hu +info.hu +org.hu +priv.hu +sport.hu +tm.hu +2000.hu +agrar.hu +bolt.hu +casino.hu +city.hu +erotica.hu +erotika.hu +film.hu +forum.hu +games.hu +hotel.hu +ingatlan.hu +jogasz.hu +konyvelo.hu +lakas.hu +media.hu +news.hu +reklam.hu +sex.hu +shop.hu +suli.hu +szex.hu +tozsde.hu +utazas.hu +video.hu + +// id : https://pandi.id/en/domain/registration-requirements/ +id +ac.id +biz.id +co.id +desa.id +go.id +mil.id +my.id +net.id +or.id +ponpes.id +sch.id +web.id + +// ie : https://en.wikipedia.org/wiki/.ie +ie +gov.ie + +// il : http://www.isoc.org.il/domains/ +il +ac.il +co.il +gov.il +idf.il +k12.il +muni.il +net.il +org.il + +// im : https://www.nic.im/ +// Submitted by registry <info@nic.im> +im +ac.im +co.im +com.im +ltd.co.im +net.im +org.im +plc.co.im +tt.im +tv.im + +// in : https://en.wikipedia.org/wiki/.in +// see also: https://registry.in/Policies +// Please note, that nic.in is not an official eTLD, but used by most +// government institutions. +in +co.in +firm.in +net.in +org.in +gen.in +ind.in +nic.in +ac.in +edu.in +res.in +gov.in +mil.in + +// info : https://en.wikipedia.org/wiki/.info +info + +// int : https://en.wikipedia.org/wiki/.int +// Confirmed by registry <iana-questions@icann.org> 2008-06-18 +int +eu.int + +// io : http://www.nic.io/rules.html +// list of other 2nd level tlds ? +io +com.io + +// iq : http://www.cmc.iq/english/iq/iqregister1.htm +iq +gov.iq +edu.iq +mil.iq +com.iq +org.iq +net.iq + +// ir : http://www.nic.ir/Terms_and_Conditions_ir,_Appendix_1_Domain_Rules +// Also see http://www.nic.ir/Internationalized_Domain_Names +// Two <iran>.ir entries added at request of <tech-team@nic.ir>, 2010-04-16 +ir +ac.ir +co.ir +gov.ir +id.ir +net.ir +org.ir +sch.ir +// xn--mgba3a4f16a.ir (<iran>.ir, Persian YEH) +ایران.ir +// xn--mgba3a4fra.ir (<iran>.ir, Arabic YEH) +ايران.ir + +// is : http://www.isnic.is/domain/rules.php +// Confirmed by registry <marius@isgate.is> 2008-12-06 +is +net.is +com.is +edu.is +gov.is +org.is +int.is + +// it : https://en.wikipedia.org/wiki/.it +it +gov.it +edu.it +// Reserved geo-names (regions and provinces): +// https://www.nic.it/sites/default/files/archivio/docs/Regulation_assignation_v7.1.pdf +// Regions +abr.it +abruzzo.it +aosta-valley.it +aostavalley.it +bas.it +basilicata.it +cal.it +calabria.it +cam.it +campania.it +emilia-romagna.it +emiliaromagna.it +emr.it +friuli-v-giulia.it +friuli-ve-giulia.it +friuli-vegiulia.it +friuli-venezia-giulia.it +friuli-veneziagiulia.it +friuli-vgiulia.it +friuliv-giulia.it +friulive-giulia.it +friulivegiulia.it +friulivenezia-giulia.it +friuliveneziagiulia.it +friulivgiulia.it +fvg.it +laz.it +lazio.it +lig.it +liguria.it +lom.it +lombardia.it +lombardy.it +lucania.it +mar.it +marche.it +mol.it +molise.it +piedmont.it +piemonte.it +pmn.it +pug.it +puglia.it +sar.it +sardegna.it +sardinia.it +sic.it +sicilia.it +sicily.it +taa.it +tos.it +toscana.it +trentin-sud-tirol.it +trentin-süd-tirol.it +trentin-sudtirol.it +trentin-südtirol.it +trentin-sued-tirol.it +trentin-suedtirol.it +trentino-a-adige.it +trentino-aadige.it +trentino-alto-adige.it +trentino-altoadige.it +trentino-s-tirol.it +trentino-stirol.it +trentino-sud-tirol.it +trentino-süd-tirol.it +trentino-sudtirol.it +trentino-südtirol.it +trentino-sued-tirol.it +trentino-suedtirol.it +trentino.it +trentinoa-adige.it +trentinoaadige.it +trentinoalto-adige.it +trentinoaltoadige.it +trentinos-tirol.it +trentinostirol.it +trentinosud-tirol.it +trentinosüd-tirol.it +trentinosudtirol.it +trentinosüdtirol.it +trentinosued-tirol.it +trentinosuedtirol.it +trentinsud-tirol.it +trentinsüd-tirol.it +trentinsudtirol.it +trentinsüdtirol.it +trentinsued-tirol.it +trentinsuedtirol.it +tuscany.it +umb.it +umbria.it +val-d-aosta.it +val-daosta.it +vald-aosta.it +valdaosta.it +valle-aosta.it +valle-d-aosta.it +valle-daosta.it +valleaosta.it +valled-aosta.it +valledaosta.it +vallee-aoste.it +vallée-aoste.it +vallee-d-aoste.it +vallée-d-aoste.it +valleeaoste.it +valléeaoste.it +valleedaoste.it +valléedaoste.it +vao.it +vda.it +ven.it +veneto.it +// Provinces +ag.it +agrigento.it +al.it +alessandria.it +alto-adige.it +altoadige.it +an.it +ancona.it +andria-barletta-trani.it +andria-trani-barletta.it +andriabarlettatrani.it +andriatranibarletta.it +ao.it +aosta.it +aoste.it +ap.it +aq.it +aquila.it +ar.it +arezzo.it +ascoli-piceno.it +ascolipiceno.it +asti.it +at.it +av.it +avellino.it +ba.it +balsan-sudtirol.it +balsan-südtirol.it +balsan-suedtirol.it +balsan.it +bari.it +barletta-trani-andria.it +barlettatraniandria.it +belluno.it +benevento.it +bergamo.it +bg.it +bi.it +biella.it +bl.it +bn.it +bo.it +bologna.it +bolzano-altoadige.it +bolzano.it +bozen-sudtirol.it +bozen-südtirol.it +bozen-suedtirol.it +bozen.it +br.it +brescia.it +brindisi.it +bs.it +bt.it +bulsan-sudtirol.it +bulsan-südtirol.it +bulsan-suedtirol.it +bulsan.it +bz.it +ca.it +cagliari.it +caltanissetta.it +campidano-medio.it +campidanomedio.it +campobasso.it +carbonia-iglesias.it +carboniaiglesias.it +carrara-massa.it +carraramassa.it +caserta.it +catania.it +catanzaro.it +cb.it +ce.it +cesena-forli.it +cesena-forlì.it +cesenaforli.it +cesenaforlì.it +ch.it +chieti.it +ci.it +cl.it +cn.it +co.it +como.it +cosenza.it +cr.it +cremona.it +crotone.it +cs.it +ct.it +cuneo.it +cz.it +dell-ogliastra.it +dellogliastra.it +en.it +enna.it +fc.it +fe.it +fermo.it +ferrara.it +fg.it +fi.it +firenze.it +florence.it +fm.it +foggia.it +forli-cesena.it +forlì-cesena.it +forlicesena.it +forlìcesena.it +fr.it +frosinone.it +ge.it +genoa.it +genova.it +go.it +gorizia.it +gr.it +grosseto.it +iglesias-carbonia.it +iglesiascarbonia.it +im.it +imperia.it +is.it +isernia.it +kr.it +la-spezia.it +laquila.it +laspezia.it +latina.it +lc.it +le.it +lecce.it +lecco.it +li.it +livorno.it +lo.it +lodi.it +lt.it +lu.it +lucca.it +macerata.it +mantova.it +massa-carrara.it +massacarrara.it +matera.it +mb.it +mc.it +me.it +medio-campidano.it +mediocampidano.it +messina.it +mi.it +milan.it +milano.it +mn.it +mo.it +modena.it +monza-brianza.it +monza-e-della-brianza.it +monza.it +monzabrianza.it +monzaebrianza.it +monzaedellabrianza.it +ms.it +mt.it +na.it +naples.it +napoli.it +no.it +novara.it +nu.it +nuoro.it +og.it +ogliastra.it +olbia-tempio.it +olbiatempio.it +or.it +oristano.it +ot.it +pa.it +padova.it +padua.it +palermo.it +parma.it +pavia.it +pc.it +pd.it +pe.it +perugia.it +pesaro-urbino.it +pesarourbino.it +pescara.it +pg.it +pi.it +piacenza.it +pisa.it +pistoia.it +pn.it +po.it +pordenone.it +potenza.it +pr.it +prato.it +pt.it +pu.it +pv.it +pz.it +ra.it +ragusa.it +ravenna.it +rc.it +re.it +reggio-calabria.it +reggio-emilia.it +reggiocalabria.it +reggioemilia.it +rg.it +ri.it +rieti.it +rimini.it +rm.it +rn.it +ro.it +roma.it +rome.it +rovigo.it +sa.it +salerno.it +sassari.it +savona.it +si.it +siena.it +siracusa.it +so.it +sondrio.it +sp.it +sr.it +ss.it +suedtirol.it +südtirol.it +sv.it +ta.it +taranto.it +te.it +tempio-olbia.it +tempioolbia.it +teramo.it +terni.it +tn.it +to.it +torino.it +tp.it +tr.it +trani-andria-barletta.it +trani-barletta-andria.it +traniandriabarletta.it +tranibarlettaandria.it +trapani.it +trento.it +treviso.it +trieste.it +ts.it +turin.it +tv.it +ud.it +udine.it +urbino-pesaro.it +urbinopesaro.it +va.it +varese.it +vb.it +vc.it +ve.it +venezia.it +venice.it +verbania.it +vercelli.it +verona.it +vi.it +vibo-valentia.it +vibovalentia.it +vicenza.it +viterbo.it +vr.it +vs.it +vt.it +vv.it + +// je : http://www.channelisles.net/register-domains/ +// Confirmed by registry <nigel@channelisles.net> 2013-11-28 +je +co.je +net.je +org.je + +// jm : http://www.com.jm/register.html +*.jm + +// jo : http://www.dns.jo/Registration_policy.aspx +jo +com.jo +org.jo +net.jo +edu.jo +sch.jo +gov.jo +mil.jo +name.jo + +// jobs : https://en.wikipedia.org/wiki/.jobs +jobs + +// jp : https://en.wikipedia.org/wiki/.jp +// http://jprs.co.jp/en/jpdomain.html +// Submitted by registry <info@jprs.jp> +jp +// jp organizational type names +ac.jp +ad.jp +co.jp +ed.jp +go.jp +gr.jp +lg.jp +ne.jp +or.jp +// jp prefecture type names +aichi.jp +akita.jp +aomori.jp +chiba.jp +ehime.jp +fukui.jp +fukuoka.jp +fukushima.jp +gifu.jp +gunma.jp +hiroshima.jp +hokkaido.jp +hyogo.jp +ibaraki.jp +ishikawa.jp +iwate.jp +kagawa.jp +kagoshima.jp +kanagawa.jp +kochi.jp +kumamoto.jp +kyoto.jp +mie.jp +miyagi.jp +miyazaki.jp +nagano.jp +nagasaki.jp +nara.jp +niigata.jp +oita.jp +okayama.jp +okinawa.jp +osaka.jp +saga.jp +saitama.jp +shiga.jp +shimane.jp +shizuoka.jp +tochigi.jp +tokushima.jp +tokyo.jp +tottori.jp +toyama.jp +wakayama.jp +yamagata.jp +yamaguchi.jp +yamanashi.jp +栃木.jp +愛知.jp +愛媛.jp +兵庫.jp +熊本.jp +茨城.jp +北海道.jp +千葉.jp +和歌山.jp +長崎.jp +長野.jp +新潟.jp +青森.jp +静岡.jp +東京.jp +石川.jp +埼玉.jp +三重.jp +京都.jp +佐賀.jp +大分.jp +大阪.jp +奈良.jp +宮城.jp +宮崎.jp +富山.jp +山口.jp +山形.jp +山梨.jp +岩手.jp +岐阜.jp +岡山.jp +島根.jp +広島.jp +徳島.jp +沖縄.jp +滋賀.jp +神奈川.jp +福井.jp +福岡.jp +福島.jp +秋田.jp +群馬.jp +香川.jp +高知.jp +鳥取.jp +鹿児島.jp +// jp geographic type names +// http://jprs.jp/doc/rule/saisoku-1.html +*.kawasaki.jp +*.kitakyushu.jp +*.kobe.jp +*.nagoya.jp +*.sapporo.jp +*.sendai.jp +*.yokohama.jp +!city.kawasaki.jp +!city.kitakyushu.jp +!city.kobe.jp +!city.nagoya.jp +!city.sapporo.jp +!city.sendai.jp +!city.yokohama.jp +// 4th level registration +aisai.aichi.jp +ama.aichi.jp +anjo.aichi.jp +asuke.aichi.jp +chiryu.aichi.jp +chita.aichi.jp +fuso.aichi.jp +gamagori.aichi.jp +handa.aichi.jp +hazu.aichi.jp +hekinan.aichi.jp +higashiura.aichi.jp +ichinomiya.aichi.jp +inazawa.aichi.jp +inuyama.aichi.jp +isshiki.aichi.jp +iwakura.aichi.jp +kanie.aichi.jp +kariya.aichi.jp +kasugai.aichi.jp +kira.aichi.jp +kiyosu.aichi.jp +komaki.aichi.jp +konan.aichi.jp +kota.aichi.jp +mihama.aichi.jp +miyoshi.aichi.jp +nishio.aichi.jp +nisshin.aichi.jp +obu.aichi.jp +oguchi.aichi.jp +oharu.aichi.jp +okazaki.aichi.jp +owariasahi.aichi.jp +seto.aichi.jp +shikatsu.aichi.jp +shinshiro.aichi.jp +shitara.aichi.jp +tahara.aichi.jp +takahama.aichi.jp +tobishima.aichi.jp +toei.aichi.jp +togo.aichi.jp +tokai.aichi.jp +tokoname.aichi.jp +toyoake.aichi.jp +toyohashi.aichi.jp +toyokawa.aichi.jp +toyone.aichi.jp +toyota.aichi.jp +tsushima.aichi.jp +yatomi.aichi.jp +akita.akita.jp +daisen.akita.jp +fujisato.akita.jp +gojome.akita.jp +hachirogata.akita.jp +happou.akita.jp +higashinaruse.akita.jp +honjo.akita.jp +honjyo.akita.jp +ikawa.akita.jp +kamikoani.akita.jp +kamioka.akita.jp +katagami.akita.jp +kazuno.akita.jp +kitaakita.akita.jp +kosaka.akita.jp +kyowa.akita.jp +misato.akita.jp +mitane.akita.jp +moriyoshi.akita.jp +nikaho.akita.jp +noshiro.akita.jp +odate.akita.jp +oga.akita.jp +ogata.akita.jp +semboku.akita.jp +yokote.akita.jp +yurihonjo.akita.jp +aomori.aomori.jp +gonohe.aomori.jp +hachinohe.aomori.jp +hashikami.aomori.jp +hiranai.aomori.jp +hirosaki.aomori.jp +itayanagi.aomori.jp +kuroishi.aomori.jp +misawa.aomori.jp +mutsu.aomori.jp +nakadomari.aomori.jp +noheji.aomori.jp +oirase.aomori.jp +owani.aomori.jp +rokunohe.aomori.jp +sannohe.aomori.jp +shichinohe.aomori.jp +shingo.aomori.jp +takko.aomori.jp +towada.aomori.jp +tsugaru.aomori.jp +tsuruta.aomori.jp +abiko.chiba.jp +asahi.chiba.jp +chonan.chiba.jp +chosei.chiba.jp +choshi.chiba.jp +chuo.chiba.jp +funabashi.chiba.jp +futtsu.chiba.jp +hanamigawa.chiba.jp +ichihara.chiba.jp +ichikawa.chiba.jp +ichinomiya.chiba.jp +inzai.chiba.jp +isumi.chiba.jp +kamagaya.chiba.jp +kamogawa.chiba.jp +kashiwa.chiba.jp +katori.chiba.jp +katsuura.chiba.jp +kimitsu.chiba.jp +kisarazu.chiba.jp +kozaki.chiba.jp +kujukuri.chiba.jp +kyonan.chiba.jp +matsudo.chiba.jp +midori.chiba.jp +mihama.chiba.jp +minamiboso.chiba.jp +mobara.chiba.jp +mutsuzawa.chiba.jp +nagara.chiba.jp +nagareyama.chiba.jp +narashino.chiba.jp +narita.chiba.jp +noda.chiba.jp +oamishirasato.chiba.jp +omigawa.chiba.jp +onjuku.chiba.jp +otaki.chiba.jp +sakae.chiba.jp +sakura.chiba.jp +shimofusa.chiba.jp +shirako.chiba.jp +shiroi.chiba.jp +shisui.chiba.jp +sodegaura.chiba.jp +sosa.chiba.jp +tako.chiba.jp +tateyama.chiba.jp +togane.chiba.jp +tohnosho.chiba.jp +tomisato.chiba.jp +urayasu.chiba.jp +yachimata.chiba.jp +yachiyo.chiba.jp +yokaichiba.chiba.jp +yokoshibahikari.chiba.jp +yotsukaido.chiba.jp +ainan.ehime.jp +honai.ehime.jp +ikata.ehime.jp +imabari.ehime.jp +iyo.ehime.jp +kamijima.ehime.jp +kihoku.ehime.jp +kumakogen.ehime.jp +masaki.ehime.jp +matsuno.ehime.jp +matsuyama.ehime.jp +namikata.ehime.jp +niihama.ehime.jp +ozu.ehime.jp +saijo.ehime.jp +seiyo.ehime.jp +shikokuchuo.ehime.jp +tobe.ehime.jp +toon.ehime.jp +uchiko.ehime.jp +uwajima.ehime.jp +yawatahama.ehime.jp +echizen.fukui.jp +eiheiji.fukui.jp +fukui.fukui.jp +ikeda.fukui.jp +katsuyama.fukui.jp +mihama.fukui.jp +minamiechizen.fukui.jp +obama.fukui.jp +ohi.fukui.jp +ono.fukui.jp +sabae.fukui.jp +sakai.fukui.jp +takahama.fukui.jp +tsuruga.fukui.jp +wakasa.fukui.jp +ashiya.fukuoka.jp +buzen.fukuoka.jp +chikugo.fukuoka.jp +chikuho.fukuoka.jp +chikujo.fukuoka.jp +chikushino.fukuoka.jp +chikuzen.fukuoka.jp +chuo.fukuoka.jp +dazaifu.fukuoka.jp +fukuchi.fukuoka.jp +hakata.fukuoka.jp +higashi.fukuoka.jp +hirokawa.fukuoka.jp +hisayama.fukuoka.jp +iizuka.fukuoka.jp +inatsuki.fukuoka.jp +kaho.fukuoka.jp +kasuga.fukuoka.jp +kasuya.fukuoka.jp +kawara.fukuoka.jp +keisen.fukuoka.jp +koga.fukuoka.jp +kurate.fukuoka.jp +kurogi.fukuoka.jp +kurume.fukuoka.jp +minami.fukuoka.jp +miyako.fukuoka.jp +miyama.fukuoka.jp +miyawaka.fukuoka.jp +mizumaki.fukuoka.jp +munakata.fukuoka.jp +nakagawa.fukuoka.jp +nakama.fukuoka.jp +nishi.fukuoka.jp +nogata.fukuoka.jp +ogori.fukuoka.jp +okagaki.fukuoka.jp +okawa.fukuoka.jp +oki.fukuoka.jp +omuta.fukuoka.jp +onga.fukuoka.jp +onojo.fukuoka.jp +oto.fukuoka.jp +saigawa.fukuoka.jp +sasaguri.fukuoka.jp +shingu.fukuoka.jp +shinyoshitomi.fukuoka.jp +shonai.fukuoka.jp +soeda.fukuoka.jp +sue.fukuoka.jp +tachiarai.fukuoka.jp +tagawa.fukuoka.jp +takata.fukuoka.jp +toho.fukuoka.jp +toyotsu.fukuoka.jp +tsuiki.fukuoka.jp +ukiha.fukuoka.jp +umi.fukuoka.jp +usui.fukuoka.jp +yamada.fukuoka.jp +yame.fukuoka.jp +yanagawa.fukuoka.jp +yukuhashi.fukuoka.jp +aizubange.fukushima.jp +aizumisato.fukushima.jp +aizuwakamatsu.fukushima.jp +asakawa.fukushima.jp +bandai.fukushima.jp +date.fukushima.jp +fukushima.fukushima.jp +furudono.fukushima.jp +futaba.fukushima.jp +hanawa.fukushima.jp +higashi.fukushima.jp +hirata.fukushima.jp +hirono.fukushima.jp +iitate.fukushima.jp +inawashiro.fukushima.jp +ishikawa.fukushima.jp +iwaki.fukushima.jp +izumizaki.fukushima.jp +kagamiishi.fukushima.jp +kaneyama.fukushima.jp +kawamata.fukushima.jp +kitakata.fukushima.jp +kitashiobara.fukushima.jp +koori.fukushima.jp +koriyama.fukushima.jp +kunimi.fukushima.jp +miharu.fukushima.jp +mishima.fukushima.jp +namie.fukushima.jp +nango.fukushima.jp +nishiaizu.fukushima.jp +nishigo.fukushima.jp +okuma.fukushima.jp +omotego.fukushima.jp +ono.fukushima.jp +otama.fukushima.jp +samegawa.fukushima.jp +shimogo.fukushima.jp +shirakawa.fukushima.jp +showa.fukushima.jp +soma.fukushima.jp +sukagawa.fukushima.jp +taishin.fukushima.jp +tamakawa.fukushima.jp +tanagura.fukushima.jp +tenei.fukushima.jp +yabuki.fukushima.jp +yamato.fukushima.jp +yamatsuri.fukushima.jp +yanaizu.fukushima.jp +yugawa.fukushima.jp +anpachi.gifu.jp +ena.gifu.jp +gifu.gifu.jp +ginan.gifu.jp +godo.gifu.jp +gujo.gifu.jp +hashima.gifu.jp +hichiso.gifu.jp +hida.gifu.jp +higashishirakawa.gifu.jp +ibigawa.gifu.jp +ikeda.gifu.jp +kakamigahara.gifu.jp +kani.gifu.jp +kasahara.gifu.jp +kasamatsu.gifu.jp +kawaue.gifu.jp +kitagata.gifu.jp +mino.gifu.jp +minokamo.gifu.jp +mitake.gifu.jp +mizunami.gifu.jp +motosu.gifu.jp +nakatsugawa.gifu.jp +ogaki.gifu.jp +sakahogi.gifu.jp +seki.gifu.jp +sekigahara.gifu.jp +shirakawa.gifu.jp +tajimi.gifu.jp +takayama.gifu.jp +tarui.gifu.jp +toki.gifu.jp +tomika.gifu.jp +wanouchi.gifu.jp +yamagata.gifu.jp +yaotsu.gifu.jp +yoro.gifu.jp +annaka.gunma.jp +chiyoda.gunma.jp +fujioka.gunma.jp +higashiagatsuma.gunma.jp +isesaki.gunma.jp +itakura.gunma.jp +kanna.gunma.jp +kanra.gunma.jp +katashina.gunma.jp +kawaba.gunma.jp +kiryu.gunma.jp +kusatsu.gunma.jp +maebashi.gunma.jp +meiwa.gunma.jp +midori.gunma.jp +minakami.gunma.jp +naganohara.gunma.jp +nakanojo.gunma.jp +nanmoku.gunma.jp +numata.gunma.jp +oizumi.gunma.jp +ora.gunma.jp +ota.gunma.jp +shibukawa.gunma.jp +shimonita.gunma.jp +shinto.gunma.jp +showa.gunma.jp +takasaki.gunma.jp +takayama.gunma.jp +tamamura.gunma.jp +tatebayashi.gunma.jp +tomioka.gunma.jp +tsukiyono.gunma.jp +tsumagoi.gunma.jp +ueno.gunma.jp +yoshioka.gunma.jp +asaminami.hiroshima.jp +daiwa.hiroshima.jp +etajima.hiroshima.jp +fuchu.hiroshima.jp +fukuyama.hiroshima.jp +hatsukaichi.hiroshima.jp +higashihiroshima.hiroshima.jp +hongo.hiroshima.jp +jinsekikogen.hiroshima.jp +kaita.hiroshima.jp +kui.hiroshima.jp +kumano.hiroshima.jp +kure.hiroshima.jp +mihara.hiroshima.jp +miyoshi.hiroshima.jp +naka.hiroshima.jp +onomichi.hiroshima.jp +osakikamijima.hiroshima.jp +otake.hiroshima.jp +saka.hiroshima.jp +sera.hiroshima.jp +seranishi.hiroshima.jp +shinichi.hiroshima.jp +shobara.hiroshima.jp +takehara.hiroshima.jp +abashiri.hokkaido.jp +abira.hokkaido.jp +aibetsu.hokkaido.jp +akabira.hokkaido.jp +akkeshi.hokkaido.jp +asahikawa.hokkaido.jp +ashibetsu.hokkaido.jp +ashoro.hokkaido.jp +assabu.hokkaido.jp +atsuma.hokkaido.jp +bibai.hokkaido.jp +biei.hokkaido.jp +bifuka.hokkaido.jp +bihoro.hokkaido.jp +biratori.hokkaido.jp +chippubetsu.hokkaido.jp +chitose.hokkaido.jp +date.hokkaido.jp +ebetsu.hokkaido.jp +embetsu.hokkaido.jp +eniwa.hokkaido.jp +erimo.hokkaido.jp +esan.hokkaido.jp +esashi.hokkaido.jp +fukagawa.hokkaido.jp +fukushima.hokkaido.jp +furano.hokkaido.jp +furubira.hokkaido.jp +haboro.hokkaido.jp +hakodate.hokkaido.jp +hamatonbetsu.hokkaido.jp +hidaka.hokkaido.jp +higashikagura.hokkaido.jp +higashikawa.hokkaido.jp +hiroo.hokkaido.jp +hokuryu.hokkaido.jp +hokuto.hokkaido.jp +honbetsu.hokkaido.jp +horokanai.hokkaido.jp +horonobe.hokkaido.jp +ikeda.hokkaido.jp +imakane.hokkaido.jp +ishikari.hokkaido.jp +iwamizawa.hokkaido.jp +iwanai.hokkaido.jp +kamifurano.hokkaido.jp +kamikawa.hokkaido.jp +kamishihoro.hokkaido.jp +kamisunagawa.hokkaido.jp +kamoenai.hokkaido.jp +kayabe.hokkaido.jp +kembuchi.hokkaido.jp +kikonai.hokkaido.jp +kimobetsu.hokkaido.jp +kitahiroshima.hokkaido.jp +kitami.hokkaido.jp +kiyosato.hokkaido.jp +koshimizu.hokkaido.jp +kunneppu.hokkaido.jp +kuriyama.hokkaido.jp +kuromatsunai.hokkaido.jp +kushiro.hokkaido.jp +kutchan.hokkaido.jp +kyowa.hokkaido.jp +mashike.hokkaido.jp +matsumae.hokkaido.jp +mikasa.hokkaido.jp +minamifurano.hokkaido.jp +mombetsu.hokkaido.jp +moseushi.hokkaido.jp +mukawa.hokkaido.jp +muroran.hokkaido.jp +naie.hokkaido.jp +nakagawa.hokkaido.jp +nakasatsunai.hokkaido.jp +nakatombetsu.hokkaido.jp +nanae.hokkaido.jp +nanporo.hokkaido.jp +nayoro.hokkaido.jp +nemuro.hokkaido.jp +niikappu.hokkaido.jp +niki.hokkaido.jp +nishiokoppe.hokkaido.jp +noboribetsu.hokkaido.jp +numata.hokkaido.jp +obihiro.hokkaido.jp +obira.hokkaido.jp +oketo.hokkaido.jp +okoppe.hokkaido.jp +otaru.hokkaido.jp +otobe.hokkaido.jp +otofuke.hokkaido.jp +otoineppu.hokkaido.jp +oumu.hokkaido.jp +ozora.hokkaido.jp +pippu.hokkaido.jp +rankoshi.hokkaido.jp +rebun.hokkaido.jp +rikubetsu.hokkaido.jp +rishiri.hokkaido.jp +rishirifuji.hokkaido.jp +saroma.hokkaido.jp +sarufutsu.hokkaido.jp +shakotan.hokkaido.jp +shari.hokkaido.jp +shibecha.hokkaido.jp +shibetsu.hokkaido.jp +shikabe.hokkaido.jp +shikaoi.hokkaido.jp +shimamaki.hokkaido.jp +shimizu.hokkaido.jp +shimokawa.hokkaido.jp +shinshinotsu.hokkaido.jp +shintoku.hokkaido.jp +shiranuka.hokkaido.jp +shiraoi.hokkaido.jp +shiriuchi.hokkaido.jp +sobetsu.hokkaido.jp +sunagawa.hokkaido.jp +taiki.hokkaido.jp +takasu.hokkaido.jp +takikawa.hokkaido.jp +takinoue.hokkaido.jp +teshikaga.hokkaido.jp +tobetsu.hokkaido.jp +tohma.hokkaido.jp +tomakomai.hokkaido.jp +tomari.hokkaido.jp +toya.hokkaido.jp +toyako.hokkaido.jp +toyotomi.hokkaido.jp +toyoura.hokkaido.jp +tsubetsu.hokkaido.jp +tsukigata.hokkaido.jp +urakawa.hokkaido.jp +urausu.hokkaido.jp +uryu.hokkaido.jp +utashinai.hokkaido.jp +wakkanai.hokkaido.jp +wassamu.hokkaido.jp +yakumo.hokkaido.jp +yoichi.hokkaido.jp +aioi.hyogo.jp +akashi.hyogo.jp +ako.hyogo.jp +amagasaki.hyogo.jp +aogaki.hyogo.jp +asago.hyogo.jp +ashiya.hyogo.jp +awaji.hyogo.jp +fukusaki.hyogo.jp +goshiki.hyogo.jp +harima.hyogo.jp +himeji.hyogo.jp +ichikawa.hyogo.jp +inagawa.hyogo.jp +itami.hyogo.jp +kakogawa.hyogo.jp +kamigori.hyogo.jp +kamikawa.hyogo.jp +kasai.hyogo.jp +kasuga.hyogo.jp +kawanishi.hyogo.jp +miki.hyogo.jp +minamiawaji.hyogo.jp +nishinomiya.hyogo.jp +nishiwaki.hyogo.jp +ono.hyogo.jp +sanda.hyogo.jp +sannan.hyogo.jp +sasayama.hyogo.jp +sayo.hyogo.jp +shingu.hyogo.jp +shinonsen.hyogo.jp +shiso.hyogo.jp +sumoto.hyogo.jp +taishi.hyogo.jp +taka.hyogo.jp +takarazuka.hyogo.jp +takasago.hyogo.jp +takino.hyogo.jp +tamba.hyogo.jp +tatsuno.hyogo.jp +toyooka.hyogo.jp +yabu.hyogo.jp +yashiro.hyogo.jp +yoka.hyogo.jp +yokawa.hyogo.jp +ami.ibaraki.jp +asahi.ibaraki.jp +bando.ibaraki.jp +chikusei.ibaraki.jp +daigo.ibaraki.jp +fujishiro.ibaraki.jp +hitachi.ibaraki.jp +hitachinaka.ibaraki.jp +hitachiomiya.ibaraki.jp +hitachiota.ibaraki.jp +ibaraki.ibaraki.jp +ina.ibaraki.jp +inashiki.ibaraki.jp +itako.ibaraki.jp +iwama.ibaraki.jp +joso.ibaraki.jp +kamisu.ibaraki.jp +kasama.ibaraki.jp +kashima.ibaraki.jp +kasumigaura.ibaraki.jp +koga.ibaraki.jp +miho.ibaraki.jp +mito.ibaraki.jp +moriya.ibaraki.jp +naka.ibaraki.jp +namegata.ibaraki.jp +oarai.ibaraki.jp +ogawa.ibaraki.jp +omitama.ibaraki.jp +ryugasaki.ibaraki.jp +sakai.ibaraki.jp +sakuragawa.ibaraki.jp +shimodate.ibaraki.jp +shimotsuma.ibaraki.jp +shirosato.ibaraki.jp +sowa.ibaraki.jp +suifu.ibaraki.jp +takahagi.ibaraki.jp +tamatsukuri.ibaraki.jp +tokai.ibaraki.jp +tomobe.ibaraki.jp +tone.ibaraki.jp +toride.ibaraki.jp +tsuchiura.ibaraki.jp +tsukuba.ibaraki.jp +uchihara.ibaraki.jp +ushiku.ibaraki.jp +yachiyo.ibaraki.jp +yamagata.ibaraki.jp +yawara.ibaraki.jp +yuki.ibaraki.jp +anamizu.ishikawa.jp +hakui.ishikawa.jp +hakusan.ishikawa.jp +kaga.ishikawa.jp +kahoku.ishikawa.jp +kanazawa.ishikawa.jp +kawakita.ishikawa.jp +komatsu.ishikawa.jp +nakanoto.ishikawa.jp +nanao.ishikawa.jp +nomi.ishikawa.jp +nonoichi.ishikawa.jp +noto.ishikawa.jp +shika.ishikawa.jp +suzu.ishikawa.jp +tsubata.ishikawa.jp +tsurugi.ishikawa.jp +uchinada.ishikawa.jp +wajima.ishikawa.jp +fudai.iwate.jp +fujisawa.iwate.jp +hanamaki.iwate.jp +hiraizumi.iwate.jp +hirono.iwate.jp +ichinohe.iwate.jp +ichinoseki.iwate.jp +iwaizumi.iwate.jp +iwate.iwate.jp +joboji.iwate.jp +kamaishi.iwate.jp +kanegasaki.iwate.jp +karumai.iwate.jp +kawai.iwate.jp +kitakami.iwate.jp +kuji.iwate.jp +kunohe.iwate.jp +kuzumaki.iwate.jp +miyako.iwate.jp +mizusawa.iwate.jp +morioka.iwate.jp +ninohe.iwate.jp +noda.iwate.jp +ofunato.iwate.jp +oshu.iwate.jp +otsuchi.iwate.jp +rikuzentakata.iwate.jp +shiwa.iwate.jp +shizukuishi.iwate.jp +sumita.iwate.jp +tanohata.iwate.jp +tono.iwate.jp +yahaba.iwate.jp +yamada.iwate.jp +ayagawa.kagawa.jp +higashikagawa.kagawa.jp +kanonji.kagawa.jp +kotohira.kagawa.jp +manno.kagawa.jp +marugame.kagawa.jp +mitoyo.kagawa.jp +naoshima.kagawa.jp +sanuki.kagawa.jp +tadotsu.kagawa.jp +takamatsu.kagawa.jp +tonosho.kagawa.jp +uchinomi.kagawa.jp +utazu.kagawa.jp +zentsuji.kagawa.jp +akune.kagoshima.jp +amami.kagoshima.jp +hioki.kagoshima.jp +isa.kagoshima.jp +isen.kagoshima.jp +izumi.kagoshima.jp +kagoshima.kagoshima.jp +kanoya.kagoshima.jp +kawanabe.kagoshima.jp +kinko.kagoshima.jp +kouyama.kagoshima.jp +makurazaki.kagoshima.jp +matsumoto.kagoshima.jp +minamitane.kagoshima.jp +nakatane.kagoshima.jp +nishinoomote.kagoshima.jp +satsumasendai.kagoshima.jp +soo.kagoshima.jp +tarumizu.kagoshima.jp +yusui.kagoshima.jp +aikawa.kanagawa.jp +atsugi.kanagawa.jp +ayase.kanagawa.jp +chigasaki.kanagawa.jp +ebina.kanagawa.jp +fujisawa.kanagawa.jp +hadano.kanagawa.jp +hakone.kanagawa.jp +hiratsuka.kanagawa.jp +isehara.kanagawa.jp +kaisei.kanagawa.jp +kamakura.kanagawa.jp +kiyokawa.kanagawa.jp +matsuda.kanagawa.jp +minamiashigara.kanagawa.jp +miura.kanagawa.jp +nakai.kanagawa.jp +ninomiya.kanagawa.jp +odawara.kanagawa.jp +oi.kanagawa.jp +oiso.kanagawa.jp +sagamihara.kanagawa.jp +samukawa.kanagawa.jp +tsukui.kanagawa.jp +yamakita.kanagawa.jp +yamato.kanagawa.jp +yokosuka.kanagawa.jp +yugawara.kanagawa.jp +zama.kanagawa.jp +zushi.kanagawa.jp +aki.kochi.jp +geisei.kochi.jp +hidaka.kochi.jp +higashitsuno.kochi.jp +ino.kochi.jp +kagami.kochi.jp +kami.kochi.jp +kitagawa.kochi.jp +kochi.kochi.jp +mihara.kochi.jp +motoyama.kochi.jp +muroto.kochi.jp +nahari.kochi.jp +nakamura.kochi.jp +nankoku.kochi.jp +nishitosa.kochi.jp +niyodogawa.kochi.jp +ochi.kochi.jp +okawa.kochi.jp +otoyo.kochi.jp +otsuki.kochi.jp +sakawa.kochi.jp +sukumo.kochi.jp +susaki.kochi.jp +tosa.kochi.jp +tosashimizu.kochi.jp +toyo.kochi.jp +tsuno.kochi.jp +umaji.kochi.jp +yasuda.kochi.jp +yusuhara.kochi.jp +amakusa.kumamoto.jp +arao.kumamoto.jp +aso.kumamoto.jp +choyo.kumamoto.jp +gyokuto.kumamoto.jp +kamiamakusa.kumamoto.jp +kikuchi.kumamoto.jp +kumamoto.kumamoto.jp +mashiki.kumamoto.jp +mifune.kumamoto.jp +minamata.kumamoto.jp +minamioguni.kumamoto.jp +nagasu.kumamoto.jp +nishihara.kumamoto.jp +oguni.kumamoto.jp +ozu.kumamoto.jp +sumoto.kumamoto.jp +takamori.kumamoto.jp +uki.kumamoto.jp +uto.kumamoto.jp +yamaga.kumamoto.jp +yamato.kumamoto.jp +yatsushiro.kumamoto.jp +ayabe.kyoto.jp +fukuchiyama.kyoto.jp +higashiyama.kyoto.jp +ide.kyoto.jp +ine.kyoto.jp +joyo.kyoto.jp +kameoka.kyoto.jp +kamo.kyoto.jp +kita.kyoto.jp +kizu.kyoto.jp +kumiyama.kyoto.jp +kyotamba.kyoto.jp +kyotanabe.kyoto.jp +kyotango.kyoto.jp +maizuru.kyoto.jp +minami.kyoto.jp +minamiyamashiro.kyoto.jp +miyazu.kyoto.jp +muko.kyoto.jp +nagaokakyo.kyoto.jp +nakagyo.kyoto.jp +nantan.kyoto.jp +oyamazaki.kyoto.jp +sakyo.kyoto.jp +seika.kyoto.jp +tanabe.kyoto.jp +uji.kyoto.jp +ujitawara.kyoto.jp +wazuka.kyoto.jp +yamashina.kyoto.jp +yawata.kyoto.jp +asahi.mie.jp +inabe.mie.jp +ise.mie.jp +kameyama.mie.jp +kawagoe.mie.jp +kiho.mie.jp +kisosaki.mie.jp +kiwa.mie.jp +komono.mie.jp +kumano.mie.jp +kuwana.mie.jp +matsusaka.mie.jp +meiwa.mie.jp +mihama.mie.jp +minamiise.mie.jp +misugi.mie.jp +miyama.mie.jp +nabari.mie.jp +shima.mie.jp +suzuka.mie.jp +tado.mie.jp +taiki.mie.jp +taki.mie.jp +tamaki.mie.jp +toba.mie.jp +tsu.mie.jp +udono.mie.jp +ureshino.mie.jp +watarai.mie.jp +yokkaichi.mie.jp +furukawa.miyagi.jp +higashimatsushima.miyagi.jp +ishinomaki.miyagi.jp +iwanuma.miyagi.jp +kakuda.miyagi.jp +kami.miyagi.jp +kawasaki.miyagi.jp +marumori.miyagi.jp +matsushima.miyagi.jp +minamisanriku.miyagi.jp +misato.miyagi.jp +murata.miyagi.jp +natori.miyagi.jp +ogawara.miyagi.jp +ohira.miyagi.jp +onagawa.miyagi.jp +osaki.miyagi.jp +rifu.miyagi.jp +semine.miyagi.jp +shibata.miyagi.jp +shichikashuku.miyagi.jp +shikama.miyagi.jp +shiogama.miyagi.jp +shiroishi.miyagi.jp +tagajo.miyagi.jp +taiwa.miyagi.jp +tome.miyagi.jp +tomiya.miyagi.jp +wakuya.miyagi.jp +watari.miyagi.jp +yamamoto.miyagi.jp +zao.miyagi.jp +aya.miyazaki.jp +ebino.miyazaki.jp +gokase.miyazaki.jp +hyuga.miyazaki.jp +kadogawa.miyazaki.jp +kawaminami.miyazaki.jp +kijo.miyazaki.jp +kitagawa.miyazaki.jp +kitakata.miyazaki.jp +kitaura.miyazaki.jp +kobayashi.miyazaki.jp +kunitomi.miyazaki.jp +kushima.miyazaki.jp +mimata.miyazaki.jp +miyakonojo.miyazaki.jp +miyazaki.miyazaki.jp +morotsuka.miyazaki.jp +nichinan.miyazaki.jp +nishimera.miyazaki.jp +nobeoka.miyazaki.jp +saito.miyazaki.jp +shiiba.miyazaki.jp +shintomi.miyazaki.jp +takaharu.miyazaki.jp +takanabe.miyazaki.jp +takazaki.miyazaki.jp +tsuno.miyazaki.jp +achi.nagano.jp +agematsu.nagano.jp +anan.nagano.jp +aoki.nagano.jp +asahi.nagano.jp +azumino.nagano.jp +chikuhoku.nagano.jp +chikuma.nagano.jp +chino.nagano.jp +fujimi.nagano.jp +hakuba.nagano.jp +hara.nagano.jp +hiraya.nagano.jp +iida.nagano.jp +iijima.nagano.jp +iiyama.nagano.jp +iizuna.nagano.jp +ikeda.nagano.jp +ikusaka.nagano.jp +ina.nagano.jp +karuizawa.nagano.jp +kawakami.nagano.jp +kiso.nagano.jp +kisofukushima.nagano.jp +kitaaiki.nagano.jp +komagane.nagano.jp +komoro.nagano.jp +matsukawa.nagano.jp +matsumoto.nagano.jp +miasa.nagano.jp +minamiaiki.nagano.jp +minamimaki.nagano.jp +minamiminowa.nagano.jp +minowa.nagano.jp +miyada.nagano.jp +miyota.nagano.jp +mochizuki.nagano.jp +nagano.nagano.jp +nagawa.nagano.jp +nagiso.nagano.jp +nakagawa.nagano.jp +nakano.nagano.jp +nozawaonsen.nagano.jp +obuse.nagano.jp +ogawa.nagano.jp +okaya.nagano.jp +omachi.nagano.jp +omi.nagano.jp +ookuwa.nagano.jp +ooshika.nagano.jp +otaki.nagano.jp +otari.nagano.jp +sakae.nagano.jp +sakaki.nagano.jp +saku.nagano.jp +sakuho.nagano.jp +shimosuwa.nagano.jp +shinanomachi.nagano.jp +shiojiri.nagano.jp +suwa.nagano.jp +suzaka.nagano.jp +takagi.nagano.jp +takamori.nagano.jp +takayama.nagano.jp +tateshina.nagano.jp +tatsuno.nagano.jp +togakushi.nagano.jp +togura.nagano.jp +tomi.nagano.jp +ueda.nagano.jp +wada.nagano.jp +yamagata.nagano.jp +yamanouchi.nagano.jp +yasaka.nagano.jp +yasuoka.nagano.jp +chijiwa.nagasaki.jp +futsu.nagasaki.jp +goto.nagasaki.jp +hasami.nagasaki.jp +hirado.nagasaki.jp +iki.nagasaki.jp +isahaya.nagasaki.jp +kawatana.nagasaki.jp +kuchinotsu.nagasaki.jp +matsuura.nagasaki.jp +nagasaki.nagasaki.jp +obama.nagasaki.jp +omura.nagasaki.jp +oseto.nagasaki.jp +saikai.nagasaki.jp +sasebo.nagasaki.jp +seihi.nagasaki.jp +shimabara.nagasaki.jp +shinkamigoto.nagasaki.jp +togitsu.nagasaki.jp +tsushima.nagasaki.jp +unzen.nagasaki.jp +ando.nara.jp +gose.nara.jp +heguri.nara.jp +higashiyoshino.nara.jp +ikaruga.nara.jp +ikoma.nara.jp +kamikitayama.nara.jp +kanmaki.nara.jp +kashiba.nara.jp +kashihara.nara.jp +katsuragi.nara.jp +kawai.nara.jp +kawakami.nara.jp +kawanishi.nara.jp +koryo.nara.jp +kurotaki.nara.jp +mitsue.nara.jp +miyake.nara.jp +nara.nara.jp +nosegawa.nara.jp +oji.nara.jp +ouda.nara.jp +oyodo.nara.jp +sakurai.nara.jp +sango.nara.jp +shimoichi.nara.jp +shimokitayama.nara.jp +shinjo.nara.jp +soni.nara.jp +takatori.nara.jp +tawaramoto.nara.jp +tenkawa.nara.jp +tenri.nara.jp +uda.nara.jp +yamatokoriyama.nara.jp +yamatotakada.nara.jp +yamazoe.nara.jp +yoshino.nara.jp +aga.niigata.jp +agano.niigata.jp +gosen.niigata.jp +itoigawa.niigata.jp +izumozaki.niigata.jp +joetsu.niigata.jp +kamo.niigata.jp +kariwa.niigata.jp +kashiwazaki.niigata.jp +minamiuonuma.niigata.jp +mitsuke.niigata.jp +muika.niigata.jp +murakami.niigata.jp +myoko.niigata.jp +nagaoka.niigata.jp +niigata.niigata.jp +ojiya.niigata.jp +omi.niigata.jp +sado.niigata.jp +sanjo.niigata.jp +seiro.niigata.jp +seirou.niigata.jp +sekikawa.niigata.jp +shibata.niigata.jp +tagami.niigata.jp +tainai.niigata.jp +tochio.niigata.jp +tokamachi.niigata.jp +tsubame.niigata.jp +tsunan.niigata.jp +uonuma.niigata.jp +yahiko.niigata.jp +yoita.niigata.jp +yuzawa.niigata.jp +beppu.oita.jp +bungoono.oita.jp +bungotakada.oita.jp +hasama.oita.jp +hiji.oita.jp +himeshima.oita.jp +hita.oita.jp +kamitsue.oita.jp +kokonoe.oita.jp +kuju.oita.jp +kunisaki.oita.jp +kusu.oita.jp +oita.oita.jp +saiki.oita.jp +taketa.oita.jp +tsukumi.oita.jp +usa.oita.jp +usuki.oita.jp +yufu.oita.jp +akaiwa.okayama.jp +asakuchi.okayama.jp +bizen.okayama.jp +hayashima.okayama.jp +ibara.okayama.jp +kagamino.okayama.jp +kasaoka.okayama.jp +kibichuo.okayama.jp +kumenan.okayama.jp +kurashiki.okayama.jp +maniwa.okayama.jp +misaki.okayama.jp +nagi.okayama.jp +niimi.okayama.jp +nishiawakura.okayama.jp +okayama.okayama.jp +satosho.okayama.jp +setouchi.okayama.jp +shinjo.okayama.jp +shoo.okayama.jp +soja.okayama.jp +takahashi.okayama.jp +tamano.okayama.jp +tsuyama.okayama.jp +wake.okayama.jp +yakage.okayama.jp +aguni.okinawa.jp +ginowan.okinawa.jp +ginoza.okinawa.jp +gushikami.okinawa.jp +haebaru.okinawa.jp +higashi.okinawa.jp +hirara.okinawa.jp +iheya.okinawa.jp +ishigaki.okinawa.jp +ishikawa.okinawa.jp +itoman.okinawa.jp +izena.okinawa.jp +kadena.okinawa.jp +kin.okinawa.jp +kitadaito.okinawa.jp +kitanakagusuku.okinawa.jp +kumejima.okinawa.jp +kunigami.okinawa.jp +minamidaito.okinawa.jp +motobu.okinawa.jp +nago.okinawa.jp +naha.okinawa.jp +nakagusuku.okinawa.jp +nakijin.okinawa.jp +nanjo.okinawa.jp +nishihara.okinawa.jp +ogimi.okinawa.jp +okinawa.okinawa.jp +onna.okinawa.jp +shimoji.okinawa.jp +taketomi.okinawa.jp +tarama.okinawa.jp +tokashiki.okinawa.jp +tomigusuku.okinawa.jp +tonaki.okinawa.jp +urasoe.okinawa.jp +uruma.okinawa.jp +yaese.okinawa.jp +yomitan.okinawa.jp +yonabaru.okinawa.jp +yonaguni.okinawa.jp +zamami.okinawa.jp +abeno.osaka.jp +chihayaakasaka.osaka.jp +chuo.osaka.jp +daito.osaka.jp +fujiidera.osaka.jp +habikino.osaka.jp +hannan.osaka.jp +higashiosaka.osaka.jp +higashisumiyoshi.osaka.jp +higashiyodogawa.osaka.jp +hirakata.osaka.jp +ibaraki.osaka.jp +ikeda.osaka.jp +izumi.osaka.jp +izumiotsu.osaka.jp +izumisano.osaka.jp +kadoma.osaka.jp +kaizuka.osaka.jp +kanan.osaka.jp +kashiwara.osaka.jp +katano.osaka.jp +kawachinagano.osaka.jp +kishiwada.osaka.jp +kita.osaka.jp +kumatori.osaka.jp +matsubara.osaka.jp +minato.osaka.jp +minoh.osaka.jp +misaki.osaka.jp +moriguchi.osaka.jp +neyagawa.osaka.jp +nishi.osaka.jp +nose.osaka.jp +osakasayama.osaka.jp +sakai.osaka.jp +sayama.osaka.jp +sennan.osaka.jp +settsu.osaka.jp +shijonawate.osaka.jp +shimamoto.osaka.jp +suita.osaka.jp +tadaoka.osaka.jp +taishi.osaka.jp +tajiri.osaka.jp +takaishi.osaka.jp +takatsuki.osaka.jp +tondabayashi.osaka.jp +toyonaka.osaka.jp +toyono.osaka.jp +yao.osaka.jp +ariake.saga.jp +arita.saga.jp +fukudomi.saga.jp +genkai.saga.jp +hamatama.saga.jp +hizen.saga.jp +imari.saga.jp +kamimine.saga.jp +kanzaki.saga.jp +karatsu.saga.jp +kashima.saga.jp +kitagata.saga.jp +kitahata.saga.jp +kiyama.saga.jp +kouhoku.saga.jp +kyuragi.saga.jp +nishiarita.saga.jp +ogi.saga.jp +omachi.saga.jp +ouchi.saga.jp +saga.saga.jp +shiroishi.saga.jp +taku.saga.jp +tara.saga.jp +tosu.saga.jp +yoshinogari.saga.jp +arakawa.saitama.jp +asaka.saitama.jp +chichibu.saitama.jp +fujimi.saitama.jp +fujimino.saitama.jp +fukaya.saitama.jp +hanno.saitama.jp +hanyu.saitama.jp +hasuda.saitama.jp +hatogaya.saitama.jp +hatoyama.saitama.jp +hidaka.saitama.jp +higashichichibu.saitama.jp +higashimatsuyama.saitama.jp +honjo.saitama.jp +ina.saitama.jp +iruma.saitama.jp +iwatsuki.saitama.jp +kamiizumi.saitama.jp +kamikawa.saitama.jp +kamisato.saitama.jp +kasukabe.saitama.jp +kawagoe.saitama.jp +kawaguchi.saitama.jp +kawajima.saitama.jp +kazo.saitama.jp +kitamoto.saitama.jp +koshigaya.saitama.jp +kounosu.saitama.jp +kuki.saitama.jp +kumagaya.saitama.jp +matsubushi.saitama.jp +minano.saitama.jp +misato.saitama.jp +miyashiro.saitama.jp +miyoshi.saitama.jp +moroyama.saitama.jp +nagatoro.saitama.jp +namegawa.saitama.jp +niiza.saitama.jp +ogano.saitama.jp +ogawa.saitama.jp +ogose.saitama.jp +okegawa.saitama.jp +omiya.saitama.jp +otaki.saitama.jp +ranzan.saitama.jp +ryokami.saitama.jp +saitama.saitama.jp +sakado.saitama.jp +satte.saitama.jp +sayama.saitama.jp +shiki.saitama.jp +shiraoka.saitama.jp +soka.saitama.jp +sugito.saitama.jp +toda.saitama.jp +tokigawa.saitama.jp +tokorozawa.saitama.jp +tsurugashima.saitama.jp +urawa.saitama.jp +warabi.saitama.jp +yashio.saitama.jp +yokoze.saitama.jp +yono.saitama.jp +yorii.saitama.jp +yoshida.saitama.jp +yoshikawa.saitama.jp +yoshimi.saitama.jp +aisho.shiga.jp +gamo.shiga.jp +higashiomi.shiga.jp +hikone.shiga.jp +koka.shiga.jp +konan.shiga.jp +kosei.shiga.jp +koto.shiga.jp +kusatsu.shiga.jp +maibara.shiga.jp +moriyama.shiga.jp +nagahama.shiga.jp +nishiazai.shiga.jp +notogawa.shiga.jp +omihachiman.shiga.jp +otsu.shiga.jp +ritto.shiga.jp +ryuoh.shiga.jp +takashima.shiga.jp +takatsuki.shiga.jp +torahime.shiga.jp +toyosato.shiga.jp +yasu.shiga.jp +akagi.shimane.jp +ama.shimane.jp +gotsu.shimane.jp +hamada.shimane.jp +higashiizumo.shimane.jp +hikawa.shimane.jp +hikimi.shimane.jp +izumo.shimane.jp +kakinoki.shimane.jp +masuda.shimane.jp +matsue.shimane.jp +misato.shimane.jp +nishinoshima.shimane.jp +ohda.shimane.jp +okinoshima.shimane.jp +okuizumo.shimane.jp +shimane.shimane.jp +tamayu.shimane.jp +tsuwano.shimane.jp +unnan.shimane.jp +yakumo.shimane.jp +yasugi.shimane.jp +yatsuka.shimane.jp +arai.shizuoka.jp +atami.shizuoka.jp +fuji.shizuoka.jp +fujieda.shizuoka.jp +fujikawa.shizuoka.jp +fujinomiya.shizuoka.jp +fukuroi.shizuoka.jp +gotemba.shizuoka.jp +haibara.shizuoka.jp +hamamatsu.shizuoka.jp +higashiizu.shizuoka.jp +ito.shizuoka.jp +iwata.shizuoka.jp +izu.shizuoka.jp +izunokuni.shizuoka.jp +kakegawa.shizuoka.jp +kannami.shizuoka.jp +kawanehon.shizuoka.jp +kawazu.shizuoka.jp +kikugawa.shizuoka.jp +kosai.shizuoka.jp +makinohara.shizuoka.jp +matsuzaki.shizuoka.jp +minamiizu.shizuoka.jp +mishima.shizuoka.jp +morimachi.shizuoka.jp +nishiizu.shizuoka.jp +numazu.shizuoka.jp +omaezaki.shizuoka.jp +shimada.shizuoka.jp +shimizu.shizuoka.jp +shimoda.shizuoka.jp +shizuoka.shizuoka.jp +susono.shizuoka.jp +yaizu.shizuoka.jp +yoshida.shizuoka.jp +ashikaga.tochigi.jp +bato.tochigi.jp +haga.tochigi.jp +ichikai.tochigi.jp +iwafune.tochigi.jp +kaminokawa.tochigi.jp +kanuma.tochigi.jp +karasuyama.tochigi.jp +kuroiso.tochigi.jp +mashiko.tochigi.jp +mibu.tochigi.jp +moka.tochigi.jp +motegi.tochigi.jp +nasu.tochigi.jp +nasushiobara.tochigi.jp +nikko.tochigi.jp +nishikata.tochigi.jp +nogi.tochigi.jp +ohira.tochigi.jp +ohtawara.tochigi.jp +oyama.tochigi.jp +sakura.tochigi.jp +sano.tochigi.jp +shimotsuke.tochigi.jp +shioya.tochigi.jp +takanezawa.tochigi.jp +tochigi.tochigi.jp +tsuga.tochigi.jp +ujiie.tochigi.jp +utsunomiya.tochigi.jp +yaita.tochigi.jp +aizumi.tokushima.jp +anan.tokushima.jp +ichiba.tokushima.jp +itano.tokushima.jp +kainan.tokushima.jp +komatsushima.tokushima.jp +matsushige.tokushima.jp +mima.tokushima.jp +minami.tokushima.jp +miyoshi.tokushima.jp +mugi.tokushima.jp +nakagawa.tokushima.jp +naruto.tokushima.jp +sanagochi.tokushima.jp +shishikui.tokushima.jp +tokushima.tokushima.jp +wajiki.tokushima.jp +adachi.tokyo.jp +akiruno.tokyo.jp +akishima.tokyo.jp +aogashima.tokyo.jp +arakawa.tokyo.jp +bunkyo.tokyo.jp +chiyoda.tokyo.jp +chofu.tokyo.jp +chuo.tokyo.jp +edogawa.tokyo.jp +fuchu.tokyo.jp +fussa.tokyo.jp +hachijo.tokyo.jp +hachioji.tokyo.jp +hamura.tokyo.jp +higashikurume.tokyo.jp +higashimurayama.tokyo.jp +higashiyamato.tokyo.jp +hino.tokyo.jp +hinode.tokyo.jp +hinohara.tokyo.jp +inagi.tokyo.jp +itabashi.tokyo.jp +katsushika.tokyo.jp +kita.tokyo.jp +kiyose.tokyo.jp +kodaira.tokyo.jp +koganei.tokyo.jp +kokubunji.tokyo.jp +komae.tokyo.jp +koto.tokyo.jp +kouzushima.tokyo.jp +kunitachi.tokyo.jp +machida.tokyo.jp +meguro.tokyo.jp +minato.tokyo.jp +mitaka.tokyo.jp +mizuho.tokyo.jp +musashimurayama.tokyo.jp +musashino.tokyo.jp +nakano.tokyo.jp +nerima.tokyo.jp +ogasawara.tokyo.jp +okutama.tokyo.jp +ome.tokyo.jp +oshima.tokyo.jp +ota.tokyo.jp +setagaya.tokyo.jp +shibuya.tokyo.jp +shinagawa.tokyo.jp +shinjuku.tokyo.jp +suginami.tokyo.jp +sumida.tokyo.jp +tachikawa.tokyo.jp +taito.tokyo.jp +tama.tokyo.jp +toshima.tokyo.jp +chizu.tottori.jp +hino.tottori.jp +kawahara.tottori.jp +koge.tottori.jp +kotoura.tottori.jp +misasa.tottori.jp +nanbu.tottori.jp +nichinan.tottori.jp +sakaiminato.tottori.jp +tottori.tottori.jp +wakasa.tottori.jp +yazu.tottori.jp +yonago.tottori.jp +asahi.toyama.jp +fuchu.toyama.jp +fukumitsu.toyama.jp +funahashi.toyama.jp +himi.toyama.jp +imizu.toyama.jp +inami.toyama.jp +johana.toyama.jp +kamiichi.toyama.jp +kurobe.toyama.jp +nakaniikawa.toyama.jp +namerikawa.toyama.jp +nanto.toyama.jp +nyuzen.toyama.jp +oyabe.toyama.jp +taira.toyama.jp +takaoka.toyama.jp +tateyama.toyama.jp +toga.toyama.jp +tonami.toyama.jp +toyama.toyama.jp +unazuki.toyama.jp +uozu.toyama.jp +yamada.toyama.jp +arida.wakayama.jp +aridagawa.wakayama.jp +gobo.wakayama.jp +hashimoto.wakayama.jp +hidaka.wakayama.jp +hirogawa.wakayama.jp +inami.wakayama.jp +iwade.wakayama.jp +kainan.wakayama.jp +kamitonda.wakayama.jp +katsuragi.wakayama.jp +kimino.wakayama.jp +kinokawa.wakayama.jp +kitayama.wakayama.jp +koya.wakayama.jp +koza.wakayama.jp +kozagawa.wakayama.jp +kudoyama.wakayama.jp +kushimoto.wakayama.jp +mihama.wakayama.jp +misato.wakayama.jp +nachikatsuura.wakayama.jp +shingu.wakayama.jp +shirahama.wakayama.jp +taiji.wakayama.jp +tanabe.wakayama.jp +wakayama.wakayama.jp +yuasa.wakayama.jp +yura.wakayama.jp +asahi.yamagata.jp +funagata.yamagata.jp +higashine.yamagata.jp +iide.yamagata.jp +kahoku.yamagata.jp +kaminoyama.yamagata.jp +kaneyama.yamagata.jp +kawanishi.yamagata.jp +mamurogawa.yamagata.jp +mikawa.yamagata.jp +murayama.yamagata.jp +nagai.yamagata.jp +nakayama.yamagata.jp +nanyo.yamagata.jp +nishikawa.yamagata.jp +obanazawa.yamagata.jp +oe.yamagata.jp +oguni.yamagata.jp +ohkura.yamagata.jp +oishida.yamagata.jp +sagae.yamagata.jp +sakata.yamagata.jp +sakegawa.yamagata.jp +shinjo.yamagata.jp +shirataka.yamagata.jp +shonai.yamagata.jp +takahata.yamagata.jp +tendo.yamagata.jp +tozawa.yamagata.jp +tsuruoka.yamagata.jp +yamagata.yamagata.jp +yamanobe.yamagata.jp +yonezawa.yamagata.jp +yuza.yamagata.jp +abu.yamaguchi.jp +hagi.yamaguchi.jp +hikari.yamaguchi.jp +hofu.yamaguchi.jp +iwakuni.yamaguchi.jp +kudamatsu.yamaguchi.jp +mitou.yamaguchi.jp +nagato.yamaguchi.jp +oshima.yamaguchi.jp +shimonoseki.yamaguchi.jp +shunan.yamaguchi.jp +tabuse.yamaguchi.jp +tokuyama.yamaguchi.jp +toyota.yamaguchi.jp +ube.yamaguchi.jp +yuu.yamaguchi.jp +chuo.yamanashi.jp +doshi.yamanashi.jp +fuefuki.yamanashi.jp +fujikawa.yamanashi.jp +fujikawaguchiko.yamanashi.jp +fujiyoshida.yamanashi.jp +hayakawa.yamanashi.jp +hokuto.yamanashi.jp +ichikawamisato.yamanashi.jp +kai.yamanashi.jp +kofu.yamanashi.jp +koshu.yamanashi.jp +kosuge.yamanashi.jp +minami-alps.yamanashi.jp +minobu.yamanashi.jp +nakamichi.yamanashi.jp +nanbu.yamanashi.jp +narusawa.yamanashi.jp +nirasaki.yamanashi.jp +nishikatsura.yamanashi.jp +oshino.yamanashi.jp +otsuki.yamanashi.jp +showa.yamanashi.jp +tabayama.yamanashi.jp +tsuru.yamanashi.jp +uenohara.yamanashi.jp +yamanakako.yamanashi.jp +yamanashi.yamanashi.jp + +// ke : http://www.kenic.or.ke/index.php/en/ke-domains/ke-domains +ke +ac.ke +co.ke +go.ke +info.ke +me.ke +mobi.ke +ne.ke +or.ke +sc.ke + +// kg : http://www.domain.kg/dmn_n.html +kg +org.kg +net.kg +com.kg +edu.kg +gov.kg +mil.kg + +// kh : http://www.mptc.gov.kh/dns_registration.htm +*.kh + +// ki : http://www.ki/dns/index.html +ki +edu.ki +biz.ki +net.ki +org.ki +gov.ki +info.ki +com.ki + +// km : https://en.wikipedia.org/wiki/.km +// http://www.domaine.km/documents/charte.doc +km +org.km +nom.km +gov.km +prd.km +tm.km +edu.km +mil.km +ass.km +com.km +// These are only mentioned as proposed suggestions at domaine.km, but +// https://en.wikipedia.org/wiki/.km says they're available for registration: +coop.km +asso.km +presse.km +medecin.km +notaires.km +pharmaciens.km +veterinaire.km +gouv.km + +// kn : https://en.wikipedia.org/wiki/.kn +// http://www.dot.kn/domainRules.html +kn +net.kn +org.kn +edu.kn +gov.kn + +// kp : http://www.kcce.kp/en_index.php +kp +com.kp +edu.kp +gov.kp +org.kp +rep.kp +tra.kp + +// kr : https://en.wikipedia.org/wiki/.kr +// see also: http://domain.nida.or.kr/eng/registration.jsp +kr +ac.kr +co.kr +es.kr +go.kr +hs.kr +kg.kr +mil.kr +ms.kr +ne.kr +or.kr +pe.kr +re.kr +sc.kr +// kr geographical names +busan.kr +chungbuk.kr +chungnam.kr +daegu.kr +daejeon.kr +gangwon.kr +gwangju.kr +gyeongbuk.kr +gyeonggi.kr +gyeongnam.kr +incheon.kr +jeju.kr +jeonbuk.kr +jeonnam.kr +seoul.kr +ulsan.kr + +// kw : https://www.nic.kw/policies/ +// Confirmed by registry <nic.tech@citra.gov.kw> +kw +com.kw +edu.kw +emb.kw +gov.kw +ind.kw +net.kw +org.kw + +// ky : http://www.icta.ky/da_ky_reg_dom.php +// Confirmed by registry <kysupport@perimeterusa.com> 2008-06-17 +ky +edu.ky +gov.ky +com.ky +org.ky +net.ky + +// kz : https://en.wikipedia.org/wiki/.kz +// see also: http://www.nic.kz/rules/index.jsp +kz +org.kz +edu.kz +net.kz +gov.kz +mil.kz +com.kz + +// la : https://en.wikipedia.org/wiki/.la +// Submitted by registry <gavin.brown@nic.la> +la +int.la +net.la +info.la +edu.la +gov.la +per.la +com.la +org.la + +// lb : https://en.wikipedia.org/wiki/.lb +// Submitted by registry <randy@psg.com> +lb +com.lb +edu.lb +gov.lb +net.lb +org.lb + +// lc : https://en.wikipedia.org/wiki/.lc +// see also: http://www.nic.lc/rules.htm +lc +com.lc +net.lc +co.lc +org.lc +edu.lc +gov.lc + +// li : https://en.wikipedia.org/wiki/.li +li + +// lk : https://www.nic.lk/index.php/domain-registration/lk-domain-naming-structure +lk +gov.lk +sch.lk +net.lk +int.lk +com.lk +org.lk +edu.lk +ngo.lk +soc.lk +web.lk +ltd.lk +assn.lk +grp.lk +hotel.lk +ac.lk + +// lr : http://psg.com/dns/lr/lr.txt +// Submitted by registry <randy@psg.com> +lr +com.lr +edu.lr +gov.lr +org.lr +net.lr + +// ls : http://www.nic.ls/ +// Confirmed by registry <lsadmin@nic.ls> +ls +ac.ls +biz.ls +co.ls +edu.ls +gov.ls +info.ls +net.ls +org.ls +sc.ls + +// lt : https://en.wikipedia.org/wiki/.lt +lt +// gov.lt : http://www.gov.lt/index_en.php +gov.lt + +// lu : http://www.dns.lu/en/ +lu + +// lv : http://www.nic.lv/DNS/En/generic.php +lv +com.lv +edu.lv +gov.lv +org.lv +mil.lv +id.lv +net.lv +asn.lv +conf.lv + +// ly : http://www.nic.ly/regulations.php +ly +com.ly +net.ly +gov.ly +plc.ly +edu.ly +sch.ly +med.ly +org.ly +id.ly + +// ma : https://en.wikipedia.org/wiki/.ma +// http://www.anrt.ma/fr/admin/download/upload/file_fr782.pdf +ma +co.ma +net.ma +gov.ma +org.ma +ac.ma +press.ma + +// mc : http://www.nic.mc/ +mc +tm.mc +asso.mc + +// md : https://en.wikipedia.org/wiki/.md +md + +// me : https://en.wikipedia.org/wiki/.me +me +co.me +net.me +org.me +edu.me +ac.me +gov.me +its.me +priv.me + +// mg : http://nic.mg/nicmg/?page_id=39 +mg +org.mg +nom.mg +gov.mg +prd.mg +tm.mg +edu.mg +mil.mg +com.mg +co.mg + +// mh : https://en.wikipedia.org/wiki/.mh +mh + +// mil : https://en.wikipedia.org/wiki/.mil +mil + +// mk : https://en.wikipedia.org/wiki/.mk +// see also: http://dns.marnet.net.mk/postapka.php +mk +com.mk +org.mk +net.mk +edu.mk +gov.mk +inf.mk +name.mk + +// ml : http://www.gobin.info/domainname/ml-template.doc +// see also: https://en.wikipedia.org/wiki/.ml +ml +com.ml +edu.ml +gouv.ml +gov.ml +net.ml +org.ml +presse.ml + +// mm : https://en.wikipedia.org/wiki/.mm +*.mm + +// mn : https://en.wikipedia.org/wiki/.mn +mn +gov.mn +edu.mn +org.mn + +// mo : http://www.monic.net.mo/ +mo +com.mo +net.mo +org.mo +edu.mo +gov.mo + +// mobi : https://en.wikipedia.org/wiki/.mobi +mobi + +// mp : http://www.dot.mp/ +// Confirmed by registry <dcamacho@saipan.com> 2008-06-17 +mp + +// mq : https://en.wikipedia.org/wiki/.mq +mq + +// mr : https://en.wikipedia.org/wiki/.mr +mr +gov.mr + +// ms : http://www.nic.ms/pdf/MS_Domain_Name_Rules.pdf +ms +com.ms +edu.ms +gov.ms +net.ms +org.ms + +// mt : https://www.nic.org.mt/go/policy +// Submitted by registry <help@nic.org.mt> +mt +com.mt +edu.mt +net.mt +org.mt + +// mu : https://en.wikipedia.org/wiki/.mu +mu +com.mu +net.mu +org.mu +gov.mu +ac.mu +co.mu +or.mu + +// museum : http://about.museum/naming/ +// http://index.museum/ +museum +academy.museum +agriculture.museum +air.museum +airguard.museum +alabama.museum +alaska.museum +amber.museum +ambulance.museum +american.museum +americana.museum +americanantiques.museum +americanart.museum +amsterdam.museum +and.museum +annefrank.museum +anthro.museum +anthropology.museum +antiques.museum +aquarium.museum +arboretum.museum +archaeological.museum +archaeology.museum +architecture.museum +art.museum +artanddesign.museum +artcenter.museum +artdeco.museum +arteducation.museum +artgallery.museum +arts.museum +artsandcrafts.museum +asmatart.museum +assassination.museum +assisi.museum +association.museum +astronomy.museum +atlanta.museum +austin.museum +australia.museum +automotive.museum +aviation.museum +axis.museum +badajoz.museum +baghdad.museum +bahn.museum +bale.museum +baltimore.museum +barcelona.museum +baseball.museum +basel.museum +baths.museum +bauern.museum +beauxarts.museum +beeldengeluid.museum +bellevue.museum +bergbau.museum +berkeley.museum +berlin.museum +bern.museum +bible.museum +bilbao.museum +bill.museum +birdart.museum +birthplace.museum +bonn.museum +boston.museum +botanical.museum +botanicalgarden.museum +botanicgarden.museum +botany.museum +brandywinevalley.museum +brasil.museum +bristol.museum +british.museum +britishcolumbia.museum +broadcast.museum +brunel.museum +brussel.museum +brussels.museum +bruxelles.museum +building.museum +burghof.museum +bus.museum +bushey.museum +cadaques.museum +california.museum +cambridge.museum +can.museum +canada.museum +capebreton.museum +carrier.museum +cartoonart.museum +casadelamoneda.museum +castle.museum +castres.museum +celtic.museum +center.museum +chattanooga.museum +cheltenham.museum +chesapeakebay.museum +chicago.museum +children.museum +childrens.museum +childrensgarden.museum +chiropractic.museum +chocolate.museum +christiansburg.museum +cincinnati.museum +cinema.museum +circus.museum +civilisation.museum +civilization.museum +civilwar.museum +clinton.museum +clock.museum +coal.museum +coastaldefence.museum +cody.museum +coldwar.museum +collection.museum +colonialwilliamsburg.museum +coloradoplateau.museum +columbia.museum +columbus.museum +communication.museum +communications.museum +community.museum +computer.museum +computerhistory.museum +comunicações.museum +contemporary.museum +contemporaryart.museum +convent.museum +copenhagen.museum +corporation.museum +correios-e-telecomunicações.museum +corvette.museum +costume.museum +countryestate.museum +county.museum +crafts.museum +cranbrook.museum +creation.museum +cultural.museum +culturalcenter.museum +culture.museum +cyber.museum +cymru.museum +dali.museum +dallas.museum +database.museum +ddr.museum +decorativearts.museum +delaware.museum +delmenhorst.museum +denmark.museum +depot.museum +design.museum +detroit.museum +dinosaur.museum +discovery.museum +dolls.museum +donostia.museum +durham.museum +eastafrica.museum +eastcoast.museum +education.museum +educational.museum +egyptian.museum +eisenbahn.museum +elburg.museum +elvendrell.museum +embroidery.museum +encyclopedic.museum +england.museum +entomology.museum +environment.museum +environmentalconservation.museum +epilepsy.museum +essex.museum +estate.museum +ethnology.museum +exeter.museum +exhibition.museum +family.museum +farm.museum +farmequipment.museum +farmers.museum +farmstead.museum +field.museum +figueres.museum +filatelia.museum +film.museum +fineart.museum +finearts.museum +finland.museum +flanders.museum +florida.museum +force.museum +fortmissoula.museum +fortworth.museum +foundation.museum +francaise.museum +frankfurt.museum +franziskaner.museum +freemasonry.museum +freiburg.museum +fribourg.museum +frog.museum +fundacio.museum +furniture.museum +gallery.museum +garden.museum +gateway.museum +geelvinck.museum +gemological.museum +geology.museum +georgia.museum +giessen.museum +glas.museum +glass.museum +gorge.museum +grandrapids.museum +graz.museum +guernsey.museum +halloffame.museum +hamburg.museum +handson.museum +harvestcelebration.museum +hawaii.museum +health.museum +heimatunduhren.museum +hellas.museum +helsinki.museum +hembygdsforbund.museum +heritage.museum +histoire.museum +historical.museum +historicalsociety.museum +historichouses.museum +historisch.museum +historisches.museum +history.museum +historyofscience.museum +horology.museum +house.museum +humanities.museum +illustration.museum +imageandsound.museum +indian.museum +indiana.museum +indianapolis.museum +indianmarket.museum +intelligence.museum +interactive.museum +iraq.museum +iron.museum +isleofman.museum +jamison.museum +jefferson.museum +jerusalem.museum +jewelry.museum +jewish.museum +jewishart.museum +jfk.museum +journalism.museum +judaica.museum +judygarland.museum +juedisches.museum +juif.museum +karate.museum +karikatur.museum +kids.museum +koebenhavn.museum +koeln.museum +kunst.museum +kunstsammlung.museum +kunstunddesign.museum +labor.museum +labour.museum +lajolla.museum +lancashire.museum +landes.museum +lans.museum +läns.museum +larsson.museum +lewismiller.museum +lincoln.museum +linz.museum +living.museum +livinghistory.museum +localhistory.museum +london.museum +losangeles.museum +louvre.museum +loyalist.museum +lucerne.museum +luxembourg.museum +luzern.museum +mad.museum +madrid.museum +mallorca.museum +manchester.museum +mansion.museum +mansions.museum +manx.museum +marburg.museum +maritime.museum +maritimo.museum +maryland.museum +marylhurst.museum +media.museum +medical.museum +medizinhistorisches.museum +meeres.museum +memorial.museum +mesaverde.museum +michigan.museum +midatlantic.museum +military.museum +mill.museum +miners.museum +mining.museum +minnesota.museum +missile.museum +missoula.museum +modern.museum +moma.museum +money.museum +monmouth.museum +monticello.museum +montreal.museum +moscow.museum +motorcycle.museum +muenchen.museum +muenster.museum +mulhouse.museum +muncie.museum +museet.museum +museumcenter.museum +museumvereniging.museum +music.museum +national.museum +nationalfirearms.museum +nationalheritage.museum +nativeamerican.museum +naturalhistory.museum +naturalhistorymuseum.museum +naturalsciences.museum +nature.museum +naturhistorisches.museum +natuurwetenschappen.museum +naumburg.museum +naval.museum +nebraska.museum +neues.museum +newhampshire.museum +newjersey.museum +newmexico.museum +newport.museum +newspaper.museum +newyork.museum +niepce.museum +norfolk.museum +north.museum +nrw.museum +nyc.museum +nyny.museum +oceanographic.museum +oceanographique.museum +omaha.museum +online.museum +ontario.museum +openair.museum +oregon.museum +oregontrail.museum +otago.museum +oxford.museum +pacific.museum +paderborn.museum +palace.museum +paleo.museum +palmsprings.museum +panama.museum +paris.museum +pasadena.museum +pharmacy.museum +philadelphia.museum +philadelphiaarea.museum +philately.museum +phoenix.museum +photography.museum +pilots.museum +pittsburgh.museum +planetarium.museum +plantation.museum +plants.museum +plaza.museum +portal.museum +portland.museum +portlligat.museum +posts-and-telecommunications.museum +preservation.museum +presidio.museum +press.museum +project.museum +public.museum +pubol.museum +quebec.museum +railroad.museum +railway.museum +research.museum +resistance.museum +riodejaneiro.museum +rochester.museum +rockart.museum +roma.museum +russia.museum +saintlouis.museum +salem.museum +salvadordali.museum +salzburg.museum +sandiego.museum +sanfrancisco.museum +santabarbara.museum +santacruz.museum +santafe.museum +saskatchewan.museum +satx.museum +savannahga.museum +schlesisches.museum +schoenbrunn.museum +schokoladen.museum +school.museum +schweiz.museum +science.museum +scienceandhistory.museum +scienceandindustry.museum +sciencecenter.museum +sciencecenters.museum +science-fiction.museum +sciencehistory.museum +sciences.museum +sciencesnaturelles.museum +scotland.museum +seaport.museum +settlement.museum +settlers.museum +shell.museum +sherbrooke.museum +sibenik.museum +silk.museum +ski.museum +skole.museum +society.museum +sologne.museum +soundandvision.museum +southcarolina.museum +southwest.museum +space.museum +spy.museum +square.museum +stadt.museum +stalbans.museum +starnberg.museum +state.museum +stateofdelaware.museum +station.museum +steam.museum +steiermark.museum +stjohn.museum +stockholm.museum +stpetersburg.museum +stuttgart.museum +suisse.museum +surgeonshall.museum +surrey.museum +svizzera.museum +sweden.museum +sydney.museum +tank.museum +tcm.museum +technology.museum +telekommunikation.museum +television.museum +texas.museum +textile.museum +theater.museum +time.museum +timekeeping.museum +topology.museum +torino.museum +touch.museum +town.museum +transport.museum +tree.museum +trolley.museum +trust.museum +trustee.museum +uhren.museum +ulm.museum +undersea.museum +university.museum +usa.museum +usantiques.museum +usarts.museum +uscountryestate.museum +usculture.museum +usdecorativearts.museum +usgarden.museum +ushistory.museum +ushuaia.museum +uslivinghistory.museum +utah.museum +uvic.museum +valley.museum +vantaa.museum +versailles.museum +viking.museum +village.museum +virginia.museum +virtual.museum +virtuel.museum +vlaanderen.museum +volkenkunde.museum +wales.museum +wallonie.museum +war.museum +washingtondc.museum +watchandclock.museum +watch-and-clock.museum +western.museum +westfalen.museum +whaling.museum +wildlife.museum +williamsburg.museum +windmill.museum +workshop.museum +york.museum +yorkshire.museum +yosemite.museum +youth.museum +zoological.museum +zoology.museum +ירושלים.museum +иком.museum + +// mv : https://en.wikipedia.org/wiki/.mv +// "mv" included because, contra Wikipedia, google.mv exists. +mv +aero.mv +biz.mv +com.mv +coop.mv +edu.mv +gov.mv +info.mv +int.mv +mil.mv +museum.mv +name.mv +net.mv +org.mv +pro.mv + +// mw : http://www.registrar.mw/ +mw +ac.mw +biz.mw +co.mw +com.mw +coop.mw +edu.mw +gov.mw +int.mw +museum.mw +net.mw +org.mw + +// mx : http://www.nic.mx/ +// Submitted by registry <farias@nic.mx> +mx +com.mx +org.mx +gob.mx +edu.mx +net.mx + +// my : http://www.mynic.net.my/ +my +com.my +net.my +org.my +gov.my +edu.my +mil.my +name.my + +// mz : http://www.uem.mz/ +// Submitted by registry <antonio@uem.mz> +mz +ac.mz +adv.mz +co.mz +edu.mz +gov.mz +mil.mz +net.mz +org.mz + +// na : http://www.na-nic.com.na/ +// http://www.info.na/domain/ +na +info.na +pro.na +name.na +school.na +or.na +dr.na +us.na +mx.na +ca.na +in.na +cc.na +tv.na +ws.na +mobi.na +co.na +com.na +org.na + +// name : has 2nd-level tlds, but there's no list of them +name + +// nc : http://www.cctld.nc/ +nc +asso.nc +nom.nc + +// ne : https://en.wikipedia.org/wiki/.ne +ne + +// net : https://en.wikipedia.org/wiki/.net +net + +// nf : https://en.wikipedia.org/wiki/.nf +nf +com.nf +net.nf +per.nf +rec.nf +web.nf +arts.nf +firm.nf +info.nf +other.nf +store.nf + +// ng : http://www.nira.org.ng/index.php/join-us/register-ng-domain/189-nira-slds +ng +com.ng +edu.ng +gov.ng +i.ng +mil.ng +mobi.ng +name.ng +net.ng +org.ng +sch.ng + +// ni : http://www.nic.ni/ +ni +ac.ni +biz.ni +co.ni +com.ni +edu.ni +gob.ni +in.ni +info.ni +int.ni +mil.ni +net.ni +nom.ni +org.ni +web.ni + +// nl : https://en.wikipedia.org/wiki/.nl +// https://www.sidn.nl/ +// ccTLD for the Netherlands +nl + +// no : https://www.norid.no/en/om-domenenavn/regelverk-for-no/ +// Norid geographical second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-b/ +// Norid category second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-c/ +// Norid category second-level domains managed by parties other than Norid : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-d/ +// RSS feed: https://teknisk.norid.no/en/feed/ +no +// Norid category second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-c/ +fhs.no +vgs.no +fylkesbibl.no +folkebibl.no +museum.no +idrett.no +priv.no +// Norid category second-level domains managed by parties other than Norid : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-d/ +mil.no +stat.no +dep.no +kommune.no +herad.no +// Norid geographical second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-b/ +// counties +aa.no +ah.no +bu.no +fm.no +hl.no +hm.no +jan-mayen.no +mr.no +nl.no +nt.no +of.no +ol.no +oslo.no +rl.no +sf.no +st.no +svalbard.no +tm.no +tr.no +va.no +vf.no +// primary and lower secondary schools per county +gs.aa.no +gs.ah.no +gs.bu.no +gs.fm.no +gs.hl.no +gs.hm.no +gs.jan-mayen.no +gs.mr.no +gs.nl.no +gs.nt.no +gs.of.no +gs.ol.no +gs.oslo.no +gs.rl.no +gs.sf.no +gs.st.no +gs.svalbard.no +gs.tm.no +gs.tr.no +gs.va.no +gs.vf.no +// cities +akrehamn.no +åkrehamn.no +algard.no +ålgård.no +arna.no +brumunddal.no +bryne.no +bronnoysund.no +brønnøysund.no +drobak.no +drøbak.no +egersund.no +fetsund.no +floro.no +florø.no +fredrikstad.no +hokksund.no +honefoss.no +hønefoss.no +jessheim.no +jorpeland.no +jørpeland.no +kirkenes.no +kopervik.no +krokstadelva.no +langevag.no +langevåg.no +leirvik.no +mjondalen.no +mjøndalen.no +mo-i-rana.no +mosjoen.no +mosjøen.no +nesoddtangen.no +orkanger.no +osoyro.no +osøyro.no +raholt.no +råholt.no +sandnessjoen.no +sandnessjøen.no +skedsmokorset.no +slattum.no +spjelkavik.no +stathelle.no +stavern.no +stjordalshalsen.no +stjørdalshalsen.no +tananger.no +tranby.no +vossevangen.no +// communities +afjord.no +åfjord.no +agdenes.no +al.no +ål.no +alesund.no +ålesund.no +alstahaug.no +alta.no +áltá.no +alaheadju.no +álaheadju.no +alvdal.no +amli.no +åmli.no +amot.no +åmot.no +andebu.no +andoy.no +andøy.no +andasuolo.no +ardal.no +årdal.no +aremark.no +arendal.no +ås.no +aseral.no +åseral.no +asker.no +askim.no +askvoll.no +askoy.no +askøy.no +asnes.no +åsnes.no +audnedaln.no +aukra.no +aure.no +aurland.no +aurskog-holand.no +aurskog-høland.no +austevoll.no +austrheim.no +averoy.no +averøy.no +balestrand.no +ballangen.no +balat.no +bálát.no +balsfjord.no +bahccavuotna.no +báhccavuotna.no +bamble.no +bardu.no +beardu.no +beiarn.no +bajddar.no +bájddar.no +baidar.no +báidár.no +berg.no +bergen.no +berlevag.no +berlevåg.no +bearalvahki.no +bearalváhki.no +bindal.no +birkenes.no +bjarkoy.no +bjarkøy.no +bjerkreim.no +bjugn.no +bodo.no +bodø.no +badaddja.no +bådåddjå.no +budejju.no +bokn.no +bremanger.no +bronnoy.no +brønnøy.no +bygland.no +bykle.no +barum.no +bærum.no +bo.telemark.no +bø.telemark.no +bo.nordland.no +bø.nordland.no +bievat.no +bievát.no +bomlo.no +bømlo.no +batsfjord.no +båtsfjord.no +bahcavuotna.no +báhcavuotna.no +dovre.no +drammen.no +drangedal.no +dyroy.no +dyrøy.no +donna.no +dønna.no +eid.no +eidfjord.no +eidsberg.no +eidskog.no +eidsvoll.no +eigersund.no +elverum.no +enebakk.no +engerdal.no +etne.no +etnedal.no +evenes.no +evenassi.no +evenášši.no +evje-og-hornnes.no +farsund.no +fauske.no +fuossko.no +fuoisku.no +fedje.no +fet.no +finnoy.no +finnøy.no +fitjar.no +fjaler.no +fjell.no +flakstad.no +flatanger.no +flekkefjord.no +flesberg.no +flora.no +fla.no +flå.no +folldal.no +forsand.no +fosnes.no +frei.no +frogn.no +froland.no +frosta.no +frana.no +fræna.no +froya.no +frøya.no +fusa.no +fyresdal.no +forde.no +førde.no +gamvik.no +gangaviika.no +gáŋgaviika.no +gaular.no +gausdal.no +gildeskal.no +gildeskål.no +giske.no +gjemnes.no +gjerdrum.no +gjerstad.no +gjesdal.no +gjovik.no +gjøvik.no +gloppen.no +gol.no +gran.no +grane.no +granvin.no +gratangen.no +grimstad.no +grong.no +kraanghke.no +kråanghke.no +grue.no +gulen.no +hadsel.no +halden.no +halsa.no +hamar.no +hamaroy.no +habmer.no +hábmer.no +hapmir.no +hápmir.no +hammerfest.no +hammarfeasta.no +hámmárfeasta.no +haram.no +hareid.no +harstad.no +hasvik.no +aknoluokta.no +ákŋoluokta.no +hattfjelldal.no +aarborte.no +haugesund.no +hemne.no +hemnes.no +hemsedal.no +heroy.more-og-romsdal.no +herøy.møre-og-romsdal.no +heroy.nordland.no +herøy.nordland.no +hitra.no +hjartdal.no +hjelmeland.no +hobol.no +hobøl.no +hof.no +hol.no +hole.no +holmestrand.no +holtalen.no +holtålen.no +hornindal.no +horten.no +hurdal.no +hurum.no +hvaler.no +hyllestad.no +hagebostad.no +hægebostad.no +hoyanger.no +høyanger.no +hoylandet.no +høylandet.no +ha.no +hå.no +ibestad.no +inderoy.no +inderøy.no +iveland.no +jevnaker.no +jondal.no +jolster.no +jølster.no +karasjok.no +karasjohka.no +kárášjohka.no +karlsoy.no +galsa.no +gálsá.no +karmoy.no +karmøy.no +kautokeino.no +guovdageaidnu.no +klepp.no +klabu.no +klæbu.no +kongsberg.no +kongsvinger.no +kragero.no +kragerø.no +kristiansand.no +kristiansund.no +krodsherad.no +krødsherad.no +kvalsund.no +rahkkeravju.no +ráhkkerávju.no +kvam.no +kvinesdal.no +kvinnherad.no +kviteseid.no +kvitsoy.no +kvitsøy.no +kvafjord.no +kvæfjord.no +giehtavuoatna.no +kvanangen.no +kvænangen.no +navuotna.no +návuotna.no +kafjord.no +kåfjord.no +gaivuotna.no +gáivuotna.no +larvik.no +lavangen.no +lavagis.no +loabat.no +loabát.no +lebesby.no +davvesiida.no +leikanger.no +leirfjord.no +leka.no +leksvik.no +lenvik.no +leangaviika.no +leaŋgaviika.no +lesja.no +levanger.no +lier.no +lierne.no +lillehammer.no +lillesand.no +lindesnes.no +lindas.no +lindås.no +lom.no +loppa.no +lahppi.no +láhppi.no +lund.no +lunner.no +luroy.no +lurøy.no +luster.no +lyngdal.no +lyngen.no +ivgu.no +lardal.no +lerdal.no +lærdal.no +lodingen.no +lødingen.no +lorenskog.no +lørenskog.no +loten.no +løten.no +malvik.no +masoy.no +måsøy.no +muosat.no +muosát.no +mandal.no +marker.no +marnardal.no +masfjorden.no +meland.no +meldal.no +melhus.no +meloy.no +meløy.no +meraker.no +meråker.no +moareke.no +moåreke.no +midsund.no +midtre-gauldal.no +modalen.no +modum.no +molde.no +moskenes.no +moss.no +mosvik.no +malselv.no +målselv.no +malatvuopmi.no +málatvuopmi.no +namdalseid.no +aejrie.no +namsos.no +namsskogan.no +naamesjevuemie.no +nååmesjevuemie.no +laakesvuemie.no +nannestad.no +narvik.no +narviika.no +naustdal.no +nedre-eiker.no +nes.akershus.no +nes.buskerud.no +nesna.no +nesodden.no +nesseby.no +unjarga.no +unjárga.no +nesset.no +nissedal.no +nittedal.no +nord-aurdal.no +nord-fron.no +nord-odal.no +norddal.no +nordkapp.no +davvenjarga.no +davvenjárga.no +nordre-land.no +nordreisa.no +raisa.no +ráisa.no +nore-og-uvdal.no +notodden.no +naroy.no +nærøy.no +notteroy.no +nøtterøy.no +odda.no +oksnes.no +øksnes.no +oppdal.no +oppegard.no +oppegård.no +orkdal.no +orland.no +ørland.no +orskog.no +ørskog.no +orsta.no +ørsta.no +os.hedmark.no +os.hordaland.no +osen.no +osteroy.no +osterøy.no +ostre-toten.no +østre-toten.no +overhalla.no +ovre-eiker.no +øvre-eiker.no +oyer.no +øyer.no +oygarden.no +øygarden.no +oystre-slidre.no +øystre-slidre.no +porsanger.no +porsangu.no +porsáŋgu.no +porsgrunn.no +radoy.no +radøy.no +rakkestad.no +rana.no +ruovat.no +randaberg.no +rauma.no +rendalen.no +rennebu.no +rennesoy.no +rennesøy.no +rindal.no +ringebu.no +ringerike.no +ringsaker.no +rissa.no +risor.no +risør.no +roan.no +rollag.no +rygge.no +ralingen.no +rælingen.no +rodoy.no +rødøy.no +romskog.no +rømskog.no +roros.no +røros.no +rost.no +røst.no +royken.no +røyken.no +royrvik.no +røyrvik.no +rade.no +råde.no +salangen.no +siellak.no +saltdal.no +salat.no +sálát.no +sálat.no +samnanger.no +sande.more-og-romsdal.no +sande.møre-og-romsdal.no +sande.vestfold.no +sandefjord.no +sandnes.no +sandoy.no +sandøy.no +sarpsborg.no +sauda.no +sauherad.no +sel.no +selbu.no +selje.no +seljord.no +sigdal.no +siljan.no +sirdal.no +skaun.no +skedsmo.no +ski.no +skien.no +skiptvet.no +skjervoy.no +skjervøy.no +skierva.no +skiervá.no +skjak.no +skjåk.no +skodje.no +skanland.no +skånland.no +skanit.no +skánit.no +smola.no +smøla.no +snillfjord.no +snasa.no +snåsa.no +snoasa.no +snaase.no +snåase.no +sogndal.no +sokndal.no +sola.no +solund.no +songdalen.no +sortland.no +spydeberg.no +stange.no +stavanger.no +steigen.no +steinkjer.no +stjordal.no +stjørdal.no +stokke.no +stor-elvdal.no +stord.no +stordal.no +storfjord.no +omasvuotna.no +strand.no +stranda.no +stryn.no +sula.no +suldal.no +sund.no +sunndal.no +surnadal.no +sveio.no +svelvik.no +sykkylven.no +sogne.no +søgne.no +somna.no +sømna.no +sondre-land.no +søndre-land.no +sor-aurdal.no +sør-aurdal.no +sor-fron.no +sør-fron.no +sor-odal.no +sør-odal.no +sor-varanger.no +sør-varanger.no +matta-varjjat.no +mátta-várjjat.no +sorfold.no +sørfold.no +sorreisa.no +sørreisa.no +sorum.no +sørum.no +tana.no +deatnu.no +time.no +tingvoll.no +tinn.no +tjeldsund.no +dielddanuorri.no +tjome.no +tjøme.no +tokke.no +tolga.no +torsken.no +tranoy.no +tranøy.no +tromso.no +tromsø.no +tromsa.no +romsa.no +trondheim.no +troandin.no +trysil.no +trana.no +træna.no +trogstad.no +trøgstad.no +tvedestrand.no +tydal.no +tynset.no +tysfjord.no +divtasvuodna.no +divttasvuotna.no +tysnes.no +tysvar.no +tysvær.no +tonsberg.no +tønsberg.no +ullensaker.no +ullensvang.no +ulvik.no +utsira.no +vadso.no +vadsø.no +cahcesuolo.no +čáhcesuolo.no +vaksdal.no +valle.no +vang.no +vanylven.no +vardo.no +vardø.no +varggat.no +várggát.no +vefsn.no +vaapste.no +vega.no +vegarshei.no +vegårshei.no +vennesla.no +verdal.no +verran.no +vestby.no +vestnes.no +vestre-slidre.no +vestre-toten.no +vestvagoy.no +vestvågøy.no +vevelstad.no +vik.no +vikna.no +vindafjord.no +volda.no +voss.no +varoy.no +værøy.no +vagan.no +vågan.no +voagat.no +vagsoy.no +vågsøy.no +vaga.no +vågå.no +valer.ostfold.no +våler.østfold.no +valer.hedmark.no +våler.hedmark.no + +// np : http://www.mos.com.np/register.html +*.np + +// nr : http://cenpac.net.nr/dns/index.html +// Submitted by registry <technician@cenpac.net.nr> +nr +biz.nr +info.nr +gov.nr +edu.nr +org.nr +net.nr +com.nr + +// nu : https://en.wikipedia.org/wiki/.nu +nu + +// nz : https://en.wikipedia.org/wiki/.nz +// Submitted by registry <jay@nzrs.net.nz> +nz +ac.nz +co.nz +cri.nz +geek.nz +gen.nz +govt.nz +health.nz +iwi.nz +kiwi.nz +maori.nz +mil.nz +māori.nz +net.nz +org.nz +parliament.nz +school.nz + +// om : https://en.wikipedia.org/wiki/.om +om +co.om +com.om +edu.om +gov.om +med.om +museum.om +net.om +org.om +pro.om + +// onion : https://tools.ietf.org/html/rfc7686 +onion + +// org : https://en.wikipedia.org/wiki/.org +org + +// pa : http://www.nic.pa/ +// Some additional second level "domains" resolve directly as hostnames, such as +// pannet.pa, so we add a rule for "pa". +pa +ac.pa +gob.pa +com.pa +org.pa +sld.pa +edu.pa +net.pa +ing.pa +abo.pa +med.pa +nom.pa + +// pe : https://www.nic.pe/InformeFinalComision.pdf +pe +edu.pe +gob.pe +nom.pe +mil.pe +org.pe +com.pe +net.pe + +// pf : http://www.gobin.info/domainname/formulaire-pf.pdf +pf +com.pf +org.pf +edu.pf + +// pg : https://en.wikipedia.org/wiki/.pg +*.pg + +// ph : http://www.domains.ph/FAQ2.asp +// Submitted by registry <jed@email.com.ph> +ph +com.ph +net.ph +org.ph +gov.ph +edu.ph +ngo.ph +mil.ph +i.ph + +// pk : http://pk5.pknic.net.pk/pk5/msgNamepk.PK +pk +com.pk +net.pk +edu.pk +org.pk +fam.pk +biz.pk +web.pk +gov.pk +gob.pk +gok.pk +gon.pk +gop.pk +gos.pk +info.pk + +// pl http://www.dns.pl/english/index.html +// Submitted by registry +pl +com.pl +net.pl +org.pl +// pl functional domains (http://www.dns.pl/english/index.html) +aid.pl +agro.pl +atm.pl +auto.pl +biz.pl +edu.pl +gmina.pl +gsm.pl +info.pl +mail.pl +miasta.pl +media.pl +mil.pl +nieruchomosci.pl +nom.pl +pc.pl +powiat.pl +priv.pl +realestate.pl +rel.pl +sex.pl +shop.pl +sklep.pl +sos.pl +szkola.pl +targi.pl +tm.pl +tourism.pl +travel.pl +turystyka.pl +// Government domains +gov.pl +ap.gov.pl +ic.gov.pl +is.gov.pl +us.gov.pl +kmpsp.gov.pl +kppsp.gov.pl +kwpsp.gov.pl +psp.gov.pl +wskr.gov.pl +kwp.gov.pl +mw.gov.pl +ug.gov.pl +um.gov.pl +umig.gov.pl +ugim.gov.pl +upow.gov.pl +uw.gov.pl +starostwo.gov.pl +pa.gov.pl +po.gov.pl +psse.gov.pl +pup.gov.pl +rzgw.gov.pl +sa.gov.pl +so.gov.pl +sr.gov.pl +wsa.gov.pl +sko.gov.pl +uzs.gov.pl +wiih.gov.pl +winb.gov.pl +pinb.gov.pl +wios.gov.pl +witd.gov.pl +wzmiuw.gov.pl +piw.gov.pl +wiw.gov.pl +griw.gov.pl +wif.gov.pl +oum.gov.pl +sdn.gov.pl +zp.gov.pl +uppo.gov.pl +mup.gov.pl +wuoz.gov.pl +konsulat.gov.pl +oirm.gov.pl +// pl regional domains (http://www.dns.pl/english/index.html) +augustow.pl +babia-gora.pl +bedzin.pl +beskidy.pl +bialowieza.pl +bialystok.pl +bielawa.pl +bieszczady.pl +boleslawiec.pl +bydgoszcz.pl +bytom.pl +cieszyn.pl +czeladz.pl +czest.pl +dlugoleka.pl +elblag.pl +elk.pl +glogow.pl +gniezno.pl +gorlice.pl +grajewo.pl +ilawa.pl +jaworzno.pl +jelenia-gora.pl +jgora.pl +kalisz.pl +kazimierz-dolny.pl +karpacz.pl +kartuzy.pl +kaszuby.pl +katowice.pl +kepno.pl +ketrzyn.pl +klodzko.pl +kobierzyce.pl +kolobrzeg.pl +konin.pl +konskowola.pl +kutno.pl +lapy.pl +lebork.pl +legnica.pl +lezajsk.pl +limanowa.pl +lomza.pl +lowicz.pl +lubin.pl +lukow.pl +malbork.pl +malopolska.pl +mazowsze.pl +mazury.pl +mielec.pl +mielno.pl +mragowo.pl +naklo.pl +nowaruda.pl +nysa.pl +olawa.pl +olecko.pl +olkusz.pl +olsztyn.pl +opoczno.pl +opole.pl +ostroda.pl +ostroleka.pl +ostrowiec.pl +ostrowwlkp.pl +pila.pl +pisz.pl +podhale.pl +podlasie.pl +polkowice.pl +pomorze.pl +pomorskie.pl +prochowice.pl +pruszkow.pl +przeworsk.pl +pulawy.pl +radom.pl +rawa-maz.pl +rybnik.pl +rzeszow.pl +sanok.pl +sejny.pl +slask.pl +slupsk.pl +sosnowiec.pl +stalowa-wola.pl +skoczow.pl +starachowice.pl +stargard.pl +suwalki.pl +swidnica.pl +swiebodzin.pl +swinoujscie.pl +szczecin.pl +szczytno.pl +tarnobrzeg.pl +tgory.pl +turek.pl +tychy.pl +ustka.pl +walbrzych.pl +warmia.pl +warszawa.pl +waw.pl +wegrow.pl +wielun.pl +wlocl.pl +wloclawek.pl +wodzislaw.pl +wolomin.pl +wroclaw.pl +zachpomor.pl +zagan.pl +zarow.pl +zgora.pl +zgorzelec.pl + +// pm : http://www.afnic.fr/medias/documents/AFNIC-naming-policy2012.pdf +pm + +// pn : http://www.government.pn/PnRegistry/policies.htm +pn +gov.pn +co.pn +org.pn +edu.pn +net.pn + +// post : https://en.wikipedia.org/wiki/.post +post + +// pr : http://www.nic.pr/index.asp?f=1 +pr +com.pr +net.pr +org.pr +gov.pr +edu.pr +isla.pr +pro.pr +biz.pr +info.pr +name.pr +// these aren't mentioned on nic.pr, but on https://en.wikipedia.org/wiki/.pr +est.pr +prof.pr +ac.pr + +// pro : http://registry.pro/get-pro +pro +aaa.pro +aca.pro +acct.pro +avocat.pro +bar.pro +cpa.pro +eng.pro +jur.pro +law.pro +med.pro +recht.pro + +// ps : https://en.wikipedia.org/wiki/.ps +// http://www.nic.ps/registration/policy.html#reg +ps +edu.ps +gov.ps +sec.ps +plo.ps +com.ps +org.ps +net.ps + +// pt : http://online.dns.pt/dns/start_dns +pt +net.pt +gov.pt +org.pt +edu.pt +int.pt +publ.pt +com.pt +nome.pt + +// pw : https://en.wikipedia.org/wiki/.pw +pw +co.pw +ne.pw +or.pw +ed.pw +go.pw +belau.pw + +// py : http://www.nic.py/pautas.html#seccion_9 +// Submitted by registry +py +com.py +coop.py +edu.py +gov.py +mil.py +net.py +org.py + +// qa : http://domains.qa/en/ +qa +com.qa +edu.qa +gov.qa +mil.qa +name.qa +net.qa +org.qa +sch.qa + +// re : http://www.afnic.re/obtenir/chartes/nommage-re/annexe-descriptifs +re +asso.re +com.re +nom.re + +// ro : http://www.rotld.ro/ +ro +arts.ro +com.ro +firm.ro +info.ro +nom.ro +nt.ro +org.ro +rec.ro +store.ro +tm.ro +www.ro + +// rs : https://www.rnids.rs/en/domains/national-domains +rs +ac.rs +co.rs +edu.rs +gov.rs +in.rs +org.rs + +// ru : https://cctld.ru/files/pdf/docs/en/rules_ru-rf.pdf +// Submitted by George Georgievsky <gug@cctld.ru> +ru + +// rw : https://www.ricta.org.rw/sites/default/files/resources/registry_registrar_contract_0.pdf +rw +ac.rw +co.rw +coop.rw +gov.rw +mil.rw +net.rw +org.rw + +// sa : http://www.nic.net.sa/ +sa +com.sa +net.sa +org.sa +gov.sa +med.sa +pub.sa +edu.sa +sch.sa + +// sb : http://www.sbnic.net.sb/ +// Submitted by registry <lee.humphries@telekom.com.sb> +sb +com.sb +edu.sb +gov.sb +net.sb +org.sb + +// sc : http://www.nic.sc/ +sc +com.sc +gov.sc +net.sc +org.sc +edu.sc + +// sd : http://www.isoc.sd/sudanic.isoc.sd/billing_pricing.htm +// Submitted by registry <admin@isoc.sd> +sd +com.sd +net.sd +org.sd +edu.sd +med.sd +tv.sd +gov.sd +info.sd + +// se : https://en.wikipedia.org/wiki/.se +// Submitted by registry <patrik.wallstrom@iis.se> +se +a.se +ac.se +b.se +bd.se +brand.se +c.se +d.se +e.se +f.se +fh.se +fhsk.se +fhv.se +g.se +h.se +i.se +k.se +komforb.se +kommunalforbund.se +komvux.se +l.se +lanbib.se +m.se +n.se +naturbruksgymn.se +o.se +org.se +p.se +parti.se +pp.se +press.se +r.se +s.se +t.se +tm.se +u.se +w.se +x.se +y.se +z.se + +// sg : http://www.nic.net.sg/page/registration-policies-procedures-and-guidelines +sg +com.sg +net.sg +org.sg +gov.sg +edu.sg +per.sg + +// sh : http://www.nic.sh/registrar.html +sh +com.sh +net.sh +gov.sh +org.sh +mil.sh + +// si : https://en.wikipedia.org/wiki/.si +si + +// sj : No registrations at this time. +// Submitted by registry <jarle@uninett.no> +sj + +// sk : https://en.wikipedia.org/wiki/.sk +// list of 2nd level domains ? +sk + +// sl : http://www.nic.sl +// Submitted by registry <adam@neoip.com> +sl +com.sl +net.sl +edu.sl +gov.sl +org.sl + +// sm : https://en.wikipedia.org/wiki/.sm +sm + +// sn : https://en.wikipedia.org/wiki/.sn +sn +art.sn +com.sn +edu.sn +gouv.sn +org.sn +perso.sn +univ.sn + +// so : http://sonic.so/policies/ +so +com.so +edu.so +gov.so +me.so +net.so +org.so + +// sr : https://en.wikipedia.org/wiki/.sr +sr + +// ss : https://registry.nic.ss/ +// Submitted by registry <technical@nic.ss> +ss +biz.ss +com.ss +edu.ss +gov.ss +net.ss +org.ss + +// st : http://www.nic.st/html/policyrules/ +st +co.st +com.st +consulado.st +edu.st +embaixada.st +gov.st +mil.st +net.st +org.st +principe.st +saotome.st +store.st + +// su : https://en.wikipedia.org/wiki/.su +su + +// sv : http://www.svnet.org.sv/niveldos.pdf +sv +com.sv +edu.sv +gob.sv +org.sv +red.sv + +// sx : https://en.wikipedia.org/wiki/.sx +// Submitted by registry <jcvignes@openregistry.com> +sx +gov.sx + +// sy : https://en.wikipedia.org/wiki/.sy +// see also: http://www.gobin.info/domainname/sy.doc +sy +edu.sy +gov.sy +net.sy +mil.sy +com.sy +org.sy + +// sz : https://en.wikipedia.org/wiki/.sz +// http://www.sispa.org.sz/ +sz +co.sz +ac.sz +org.sz + +// tc : https://en.wikipedia.org/wiki/.tc +tc + +// td : https://en.wikipedia.org/wiki/.td +td + +// tel: https://en.wikipedia.org/wiki/.tel +// http://www.telnic.org/ +tel + +// tf : https://en.wikipedia.org/wiki/.tf +tf + +// tg : https://en.wikipedia.org/wiki/.tg +// http://www.nic.tg/ +tg + +// th : https://en.wikipedia.org/wiki/.th +// Submitted by registry <krit@thains.co.th> +th +ac.th +co.th +go.th +in.th +mi.th +net.th +or.th + +// tj : http://www.nic.tj/policy.html +tj +ac.tj +biz.tj +co.tj +com.tj +edu.tj +go.tj +gov.tj +int.tj +mil.tj +name.tj +net.tj +nic.tj +org.tj +test.tj +web.tj + +// tk : https://en.wikipedia.org/wiki/.tk +tk + +// tl : https://en.wikipedia.org/wiki/.tl +tl +gov.tl + +// tm : http://www.nic.tm/local.html +tm +com.tm +co.tm +org.tm +net.tm +nom.tm +gov.tm +mil.tm +edu.tm + +// tn : https://en.wikipedia.org/wiki/.tn +// http://whois.ati.tn/ +tn +com.tn +ens.tn +fin.tn +gov.tn +ind.tn +intl.tn +nat.tn +net.tn +org.tn +info.tn +perso.tn +tourism.tn +edunet.tn +rnrt.tn +rns.tn +rnu.tn +mincom.tn +agrinet.tn +defense.tn +turen.tn + +// to : https://en.wikipedia.org/wiki/.to +// Submitted by registry <egullich@colo.to> +to +com.to +gov.to +net.to +org.to +edu.to +mil.to + +// tr : https://nic.tr/ +// https://nic.tr/forms/eng/policies.pdf +// https://nic.tr/index.php?USRACTN=PRICELST +tr +av.tr +bbs.tr +bel.tr +biz.tr +com.tr +dr.tr +edu.tr +gen.tr +gov.tr +info.tr +mil.tr +k12.tr +kep.tr +name.tr +net.tr +org.tr +pol.tr +tel.tr +tsk.tr +tv.tr +web.tr +// Used by Northern Cyprus +nc.tr +// Used by government agencies of Northern Cyprus +gov.nc.tr + +// tt : http://www.nic.tt/ +tt +co.tt +com.tt +org.tt +net.tt +biz.tt +info.tt +pro.tt +int.tt +coop.tt +jobs.tt +mobi.tt +travel.tt +museum.tt +aero.tt +name.tt +gov.tt +edu.tt + +// tv : https://en.wikipedia.org/wiki/.tv +// Not listing any 2LDs as reserved since none seem to exist in practice, +// Wikipedia notwithstanding. +tv + +// tw : https://en.wikipedia.org/wiki/.tw +tw +edu.tw +gov.tw +mil.tw +com.tw +net.tw +org.tw +idv.tw +game.tw +ebiz.tw +club.tw +網路.tw +組織.tw +商業.tw + +// tz : http://www.tznic.or.tz/index.php/domains +// Submitted by registry <manager@tznic.or.tz> +tz +ac.tz +co.tz +go.tz +hotel.tz +info.tz +me.tz +mil.tz +mobi.tz +ne.tz +or.tz +sc.tz +tv.tz + +// ua : https://hostmaster.ua/policy/?ua +// Submitted by registry <dk@cctld.ua> +ua +// ua 2LD +com.ua +edu.ua +gov.ua +in.ua +net.ua +org.ua +// ua geographic names +// https://hostmaster.ua/2ld/ +cherkassy.ua +cherkasy.ua +chernigov.ua +chernihiv.ua +chernivtsi.ua +chernovtsy.ua +ck.ua +cn.ua +cr.ua +crimea.ua +cv.ua +dn.ua +dnepropetrovsk.ua +dnipropetrovsk.ua +donetsk.ua +dp.ua +if.ua +ivano-frankivsk.ua +kh.ua +kharkiv.ua +kharkov.ua +kherson.ua +khmelnitskiy.ua +khmelnytskyi.ua +kiev.ua +kirovograd.ua +km.ua +kr.ua +krym.ua +ks.ua +kv.ua +kyiv.ua +lg.ua +lt.ua +lugansk.ua +lutsk.ua +lv.ua +lviv.ua +mk.ua +mykolaiv.ua +nikolaev.ua +od.ua +odesa.ua +odessa.ua +pl.ua +poltava.ua +rivne.ua +rovno.ua +rv.ua +sb.ua +sebastopol.ua +sevastopol.ua +sm.ua +sumy.ua +te.ua +ternopil.ua +uz.ua +uzhgorod.ua +vinnica.ua +vinnytsia.ua +vn.ua +volyn.ua +yalta.ua +zaporizhzhe.ua +zaporizhzhia.ua +zhitomir.ua +zhytomyr.ua +zp.ua +zt.ua + +// ug : https://www.registry.co.ug/ +ug +co.ug +or.ug +ac.ug +sc.ug +go.ug +ne.ug +com.ug +org.ug + +// uk : https://en.wikipedia.org/wiki/.uk +// Submitted by registry <Michael.Daly@nominet.org.uk> +uk +ac.uk +co.uk +gov.uk +ltd.uk +me.uk +net.uk +nhs.uk +org.uk +plc.uk +police.uk +*.sch.uk + +// us : https://en.wikipedia.org/wiki/.us +us +dni.us +fed.us +isa.us +kids.us +nsn.us +// us geographic names +ak.us +al.us +ar.us +as.us +az.us +ca.us +co.us +ct.us +dc.us +de.us +fl.us +ga.us +gu.us +hi.us +ia.us +id.us +il.us +in.us +ks.us +ky.us +la.us +ma.us +md.us +me.us +mi.us +mn.us +mo.us +ms.us +mt.us +nc.us +nd.us +ne.us +nh.us +nj.us +nm.us +nv.us +ny.us +oh.us +ok.us +or.us +pa.us +pr.us +ri.us +sc.us +sd.us +tn.us +tx.us +ut.us +vi.us +vt.us +va.us +wa.us +wi.us +wv.us +wy.us +// The registrar notes several more specific domains available in each state, +// such as state.*.us, dst.*.us, etc., but resolution of these is somewhat +// haphazard; in some states these domains resolve as addresses, while in others +// only subdomains are available, or even nothing at all. We include the +// most common ones where it's clear that different sites are different +// entities. +k12.ak.us +k12.al.us +k12.ar.us +k12.as.us +k12.az.us +k12.ca.us +k12.co.us +k12.ct.us +k12.dc.us +k12.de.us +k12.fl.us +k12.ga.us +k12.gu.us +// k12.hi.us Bug 614565 - Hawaii has a state-wide DOE login +k12.ia.us +k12.id.us +k12.il.us +k12.in.us +k12.ks.us +k12.ky.us +k12.la.us +k12.ma.us +k12.md.us +k12.me.us +k12.mi.us +k12.mn.us +k12.mo.us +k12.ms.us +k12.mt.us +k12.nc.us +// k12.nd.us Bug 1028347 - Removed at request of Travis Rosso <trossow@nd.gov> +k12.ne.us +k12.nh.us +k12.nj.us +k12.nm.us +k12.nv.us +k12.ny.us +k12.oh.us +k12.ok.us +k12.or.us +k12.pa.us +k12.pr.us +// k12.ri.us Removed at request of Kim Cournoyer <netsupport@staff.ri.net> +k12.sc.us +// k12.sd.us Bug 934131 - Removed at request of James Booze <James.Booze@k12.sd.us> +k12.tn.us +k12.tx.us +k12.ut.us +k12.vi.us +k12.vt.us +k12.va.us +k12.wa.us +k12.wi.us +// k12.wv.us Bug 947705 - Removed at request of Verne Britton <verne@wvnet.edu> +k12.wy.us +cc.ak.us +cc.al.us +cc.ar.us +cc.as.us +cc.az.us +cc.ca.us +cc.co.us +cc.ct.us +cc.dc.us +cc.de.us +cc.fl.us +cc.ga.us +cc.gu.us +cc.hi.us +cc.ia.us +cc.id.us +cc.il.us +cc.in.us +cc.ks.us +cc.ky.us +cc.la.us +cc.ma.us +cc.md.us +cc.me.us +cc.mi.us +cc.mn.us +cc.mo.us +cc.ms.us +cc.mt.us +cc.nc.us +cc.nd.us +cc.ne.us +cc.nh.us +cc.nj.us +cc.nm.us +cc.nv.us +cc.ny.us +cc.oh.us +cc.ok.us +cc.or.us +cc.pa.us +cc.pr.us +cc.ri.us +cc.sc.us +cc.sd.us +cc.tn.us +cc.tx.us +cc.ut.us +cc.vi.us +cc.vt.us +cc.va.us +cc.wa.us +cc.wi.us +cc.wv.us +cc.wy.us +lib.ak.us +lib.al.us +lib.ar.us +lib.as.us +lib.az.us +lib.ca.us +lib.co.us +lib.ct.us +lib.dc.us +// lib.de.us Issue #243 - Moved to Private section at request of Ed Moore <Ed.Moore@lib.de.us> +lib.fl.us +lib.ga.us +lib.gu.us +lib.hi.us +lib.ia.us +lib.id.us +lib.il.us +lib.in.us +lib.ks.us +lib.ky.us +lib.la.us +lib.ma.us +lib.md.us +lib.me.us +lib.mi.us +lib.mn.us +lib.mo.us +lib.ms.us +lib.mt.us +lib.nc.us +lib.nd.us +lib.ne.us +lib.nh.us +lib.nj.us +lib.nm.us +lib.nv.us +lib.ny.us +lib.oh.us +lib.ok.us +lib.or.us +lib.pa.us +lib.pr.us +lib.ri.us +lib.sc.us +lib.sd.us +lib.tn.us +lib.tx.us +lib.ut.us +lib.vi.us +lib.vt.us +lib.va.us +lib.wa.us +lib.wi.us +// lib.wv.us Bug 941670 - Removed at request of Larry W Arnold <arnold@wvlc.lib.wv.us> +lib.wy.us +// k12.ma.us contains school districts in Massachusetts. The 4LDs are +// managed independently except for private (PVT), charter (CHTR) and +// parochial (PAROCH) schools. Those are delegated directly to the +// 5LD operators. <k12-ma-hostmaster _ at _ rsuc.gweep.net> +pvt.k12.ma.us +chtr.k12.ma.us +paroch.k12.ma.us +// Merit Network, Inc. maintains the registry for =~ /(k12|cc|lib).mi.us/ and the following +// see also: http://domreg.merit.edu +// see also: whois -h whois.domreg.merit.edu help +ann-arbor.mi.us +cog.mi.us +dst.mi.us +eaton.mi.us +gen.mi.us +mus.mi.us +tec.mi.us +washtenaw.mi.us + +// uy : http://www.nic.org.uy/ +uy +com.uy +edu.uy +gub.uy +mil.uy +net.uy +org.uy + +// uz : http://www.reg.uz/ +uz +co.uz +com.uz +net.uz +org.uz + +// va : https://en.wikipedia.org/wiki/.va +va + +// vc : https://en.wikipedia.org/wiki/.vc +// Submitted by registry <kshah@ca.afilias.info> +vc +com.vc +net.vc +org.vc +gov.vc +mil.vc +edu.vc + +// ve : https://registro.nic.ve/ +// Submitted by registry +ve +arts.ve +co.ve +com.ve +e12.ve +edu.ve +firm.ve +gob.ve +gov.ve +info.ve +int.ve +mil.ve +net.ve +org.ve +rec.ve +store.ve +tec.ve +web.ve + +// vg : https://en.wikipedia.org/wiki/.vg +vg + +// vi : http://www.nic.vi/newdomainform.htm +// http://www.nic.vi/Domain_Rules/body_domain_rules.html indicates some other +// TLDs are "reserved", such as edu.vi and gov.vi, but doesn't actually say they +// are available for registration (which they do not seem to be). +vi +co.vi +com.vi +k12.vi +net.vi +org.vi + +// vn : https://www.dot.vn/vnnic/vnnic/domainregistration.jsp +vn +com.vn +net.vn +org.vn +edu.vn +gov.vn +int.vn +ac.vn +biz.vn +info.vn +name.vn +pro.vn +health.vn + +// vu : https://en.wikipedia.org/wiki/.vu +// http://www.vunic.vu/ +vu +com.vu +edu.vu +net.vu +org.vu + +// wf : http://www.afnic.fr/medias/documents/AFNIC-naming-policy2012.pdf +wf + +// ws : https://en.wikipedia.org/wiki/.ws +// http://samoanic.ws/index.dhtml +ws +com.ws +net.ws +org.ws +gov.ws +edu.ws + +// yt : http://www.afnic.fr/medias/documents/AFNIC-naming-policy2012.pdf +yt + +// IDN ccTLDs +// When submitting patches, please maintain a sort by ISO 3166 ccTLD, then +// U-label, and follow this format: +// // A-Label ("<Latin renderings>", <language name>[, variant info]) : <ISO 3166 ccTLD> +// // [sponsoring org] +// U-Label + +// xn--mgbaam7a8h ("Emerat", Arabic) : AE +// http://nic.ae/english/arabicdomain/rules.jsp +امارات + +// xn--y9a3aq ("hye", Armenian) : AM +// ISOC AM (operated by .am Registry) +հայ + +// xn--54b7fta0cc ("Bangla", Bangla) : BD +বাংলা + +// xn--90ae ("bg", Bulgarian) : BG +бг + +// xn--mgbcpq6gpa1a ("albahrain", Arabic) : BH +البحرين + +// xn--90ais ("bel", Belarusian/Russian Cyrillic) : BY +// Operated by .by registry +бел + +// xn--fiqs8s ("Zhongguo/China", Chinese, Simplified) : CN +// CNNIC +// http://cnnic.cn/html/Dir/2005/10/11/3218.htm +中国 + +// xn--fiqz9s ("Zhongguo/China", Chinese, Traditional) : CN +// CNNIC +// http://cnnic.cn/html/Dir/2005/10/11/3218.htm +中國 + +// xn--lgbbat1ad8j ("Algeria/Al Jazair", Arabic) : DZ +الجزائر + +// xn--wgbh1c ("Egypt/Masr", Arabic) : EG +// http://www.dotmasr.eg/ +مصر + +// xn--e1a4c ("eu", Cyrillic) : EU +// https://eurid.eu +ею + +// xn--qxa6a ("eu", Greek) : EU +// https://eurid.eu +ευ + +// xn--mgbah1a3hjkrd ("Mauritania", Arabic) : MR +موريتانيا + +// xn--node ("ge", Georgian Mkhedruli) : GE +გე + +// xn--qxam ("el", Greek) : GR +// Hellenic Ministry of Infrastructure, Transport, and Networks +ελ + +// xn--j6w193g ("Hong Kong", Chinese) : HK +// https://www.hkirc.hk +// Submitted by registry <hk.tech@hkirc.hk> +// https://www.hkirc.hk/content.jsp?id=30#!/34 +香港 +公司.香港 +教育.香港 +政府.香港 +個人.香港 +網絡.香港 +組織.香港 + +// xn--2scrj9c ("Bharat", Kannada) : IN +// India +ಭಾರತ + +// xn--3hcrj9c ("Bharat", Oriya) : IN +// India +ଭାରତ + +// xn--45br5cyl ("Bharatam", Assamese) : IN +// India +ভাৰত + +// xn--h2breg3eve ("Bharatam", Sanskrit) : IN +// India +भारतम् + +// xn--h2brj9c8c ("Bharot", Santali) : IN +// India +भारोत + +// xn--mgbgu82a ("Bharat", Sindhi) : IN +// India +ڀارت + +// xn--rvc1e0am3e ("Bharatam", Malayalam) : IN +// India +ഭാരതം + +// xn--h2brj9c ("Bharat", Devanagari) : IN +// India +भारत + +// xn--mgbbh1a ("Bharat", Kashmiri) : IN +// India +بارت + +// xn--mgbbh1a71e ("Bharat", Arabic) : IN +// India +بھارت + +// xn--fpcrj9c3d ("Bharat", Telugu) : IN +// India +భారత్ + +// xn--gecrj9c ("Bharat", Gujarati) : IN +// India +ભારત + +// xn--s9brj9c ("Bharat", Gurmukhi) : IN +// India +ਭਾਰਤ + +// xn--45brj9c ("Bharat", Bengali) : IN +// India +ভারত + +// xn--xkc2dl3a5ee0h ("India", Tamil) : IN +// India +இந்தியா + +// xn--mgba3a4f16a ("Iran", Persian) : IR +ایران + +// xn--mgba3a4fra ("Iran", Arabic) : IR +ايران + +// xn--mgbtx2b ("Iraq", Arabic) : IQ +// Communications and Media Commission +عراق + +// xn--mgbayh7gpa ("al-Ordon", Arabic) : JO +// National Information Technology Center (NITC) +// Royal Scientific Society, Al-Jubeiha +الاردن + +// xn--3e0b707e ("Republic of Korea", Hangul) : KR +한국 + +// xn--80ao21a ("Kaz", Kazakh) : KZ +қаз + +// xn--q7ce6a ("Lao", Lao) : LA +ລາວ + +// xn--fzc2c9e2c ("Lanka", Sinhalese-Sinhala) : LK +// https://nic.lk +ලංකා + +// xn--xkc2al3hye2a ("Ilangai", Tamil) : LK +// https://nic.lk +இலங்கை + +// xn--mgbc0a9azcg ("Morocco/al-Maghrib", Arabic) : MA +المغرب + +// xn--d1alf ("mkd", Macedonian) : MK +// MARnet +мкд + +// xn--l1acc ("mon", Mongolian) : MN +мон + +// xn--mix891f ("Macao", Chinese, Traditional) : MO +// MONIC / HNET Asia (Registry Operator for .mo) +澳門 + +// xn--mix082f ("Macao", Chinese, Simplified) : MO +澳门 + +// xn--mgbx4cd0ab ("Malaysia", Malay) : MY +مليسيا + +// xn--mgb9awbf ("Oman", Arabic) : OM +عمان + +// xn--mgbai9azgqp6j ("Pakistan", Urdu/Arabic) : PK +پاکستان + +// xn--mgbai9a5eva00b ("Pakistan", Urdu/Arabic, variant) : PK +پاكستان + +// xn--ygbi2ammx ("Falasteen", Arabic) : PS +// The Palestinian National Internet Naming Authority (PNINA) +// http://www.pnina.ps +فلسطين + +// xn--90a3ac ("srb", Cyrillic) : RS +// https://www.rnids.rs/en/domains/national-domains +срб +пр.срб +орг.срб +обр.срб +од.срб +упр.срб +ак.срб + +// xn--p1ai ("rf", Russian-Cyrillic) : RU +// https://cctld.ru/files/pdf/docs/en/rules_ru-rf.pdf +// Submitted by George Georgievsky <gug@cctld.ru> +рф + +// xn--wgbl6a ("Qatar", Arabic) : QA +// http://www.ict.gov.qa/ +قطر + +// xn--mgberp4a5d4ar ("AlSaudiah", Arabic) : SA +// http://www.nic.net.sa/ +السعودية + +// xn--mgberp4a5d4a87g ("AlSaudiah", Arabic, variant) : SA +السعودیة + +// xn--mgbqly7c0a67fbc ("AlSaudiah", Arabic, variant) : SA +السعودیۃ + +// xn--mgbqly7cvafr ("AlSaudiah", Arabic, variant) : SA +السعوديه + +// xn--mgbpl2fh ("sudan", Arabic) : SD +// Operated by .sd registry +سودان + +// xn--yfro4i67o Singapore ("Singapore", Chinese) : SG +新加坡 + +// xn--clchc0ea0b2g2a9gcd ("Singapore", Tamil) : SG +சிங்கப்பூர் + +// xn--ogbpf8fl ("Syria", Arabic) : SY +سورية + +// xn--mgbtf8fl ("Syria", Arabic, variant) : SY +سوريا + +// xn--o3cw4h ("Thai", Thai) : TH +// http://www.thnic.co.th +ไทย +ศึกษา.ไทย +ธุรกิจ.ไทย +รัฐบาล.ไทย +ทหาร.ไทย +เน็ต.ไทย +องค์กร.ไทย + +// xn--pgbs0dh ("Tunisia", Arabic) : TN +// http://nic.tn +تونس + +// xn--kpry57d ("Taiwan", Chinese, Traditional) : TW +// http://www.twnic.net/english/dn/dn_07a.htm +台灣 + +// xn--kprw13d ("Taiwan", Chinese, Simplified) : TW +// http://www.twnic.net/english/dn/dn_07a.htm +台湾 + +// xn--nnx388a ("Taiwan", Chinese, variant) : TW +臺灣 + +// xn--j1amh ("ukr", Cyrillic) : UA +укр + +// xn--mgb2ddes ("AlYemen", Arabic) : YE +اليمن + +// xxx : http://icmregistry.com +xxx + +// ye : http://www.y.net.ye/services/domain_name.htm +ye +com.ye +edu.ye +gov.ye +net.ye +mil.ye +org.ye + +// za : https://www.zadna.org.za/content/page/domain-information/ +ac.za +agric.za +alt.za +co.za +edu.za +gov.za +grondar.za +law.za +mil.za +net.za +ngo.za +nic.za +nis.za +nom.za +org.za +school.za +tm.za +web.za + +// zm : https://zicta.zm/ +// Submitted by registry <info@zicta.zm> +zm +ac.zm +biz.zm +co.zm +com.zm +edu.zm +gov.zm +info.zm +mil.zm +net.zm +org.zm +sch.zm + +// zw : https://www.potraz.gov.zw/ +// Confirmed by registry <bmtengwa@potraz.gov.zw> 2017-01-25 +zw +ac.zw +co.zw +gov.zw +mil.zw +org.zw + + +// newGTLDs + +// List of new gTLDs imported from https://www.icann.org/resources/registries/gtlds/v2/gtlds.json on 2021-02-07T16:47:40Z +// This list is auto-generated, don't edit it manually. +// aaa : 2015-02-26 American Automobile Association, Inc. +aaa + +// aarp : 2015-05-21 AARP +aarp + +// abarth : 2015-07-30 Fiat Chrysler Automobiles N.V. +abarth + +// abb : 2014-10-24 ABB Ltd +abb + +// abbott : 2014-07-24 Abbott Laboratories, Inc. +abbott + +// abbvie : 2015-07-30 AbbVie Inc. +abbvie + +// abc : 2015-07-30 Disney Enterprises, Inc. +abc + +// able : 2015-06-25 Able Inc. +able + +// abogado : 2014-04-24 Minds + Machines Group Limited +abogado + +// abudhabi : 2015-07-30 Abu Dhabi Systems and Information Centre +abudhabi + +// academy : 2013-11-07 Binky Moon, LLC +academy + +// accenture : 2014-08-15 Accenture plc +accenture + +// accountant : 2014-11-20 dot Accountant Limited +accountant + +// accountants : 2014-03-20 Binky Moon, LLC +accountants + +// aco : 2015-01-08 ACO Severin Ahlmann GmbH & Co. KG +aco + +// actor : 2013-12-12 Dog Beach, LLC +actor + +// adac : 2015-07-16 Allgemeiner Deutscher Automobil-Club e.V. (ADAC) +adac + +// ads : 2014-12-04 Charleston Road Registry Inc. +ads + +// adult : 2014-10-16 ICM Registry AD LLC +adult + +// aeg : 2015-03-19 Aktiebolaget Electrolux +aeg + +// aetna : 2015-05-21 Aetna Life Insurance Company +aetna + +// afamilycompany : 2015-07-23 Johnson Shareholdings, Inc. +afamilycompany + +// afl : 2014-10-02 Australian Football League +afl + +// africa : 2014-03-24 ZA Central Registry NPC trading as Registry.Africa +africa + +// agakhan : 2015-04-23 Fondation Aga Khan (Aga Khan Foundation) +agakhan + +// agency : 2013-11-14 Binky Moon, LLC +agency + +// aig : 2014-12-18 American International Group, Inc. +aig + +// airbus : 2015-07-30 Airbus S.A.S. +airbus + +// airforce : 2014-03-06 Dog Beach, LLC +airforce + +// airtel : 2014-10-24 Bharti Airtel Limited +airtel + +// akdn : 2015-04-23 Fondation Aga Khan (Aga Khan Foundation) +akdn + +// alfaromeo : 2015-07-31 Fiat Chrysler Automobiles N.V. +alfaromeo + +// alibaba : 2015-01-15 Alibaba Group Holding Limited +alibaba + +// alipay : 2015-01-15 Alibaba Group Holding Limited +alipay + +// allfinanz : 2014-07-03 Allfinanz Deutsche Vermögensberatung Aktiengesellschaft +allfinanz + +// allstate : 2015-07-31 Allstate Fire and Casualty Insurance Company +allstate + +// ally : 2015-06-18 Ally Financial Inc. +ally + +// alsace : 2014-07-02 Region Grand Est +alsace + +// alstom : 2015-07-30 ALSTOM +alstom + +// amazon : 2019-12-19 Amazon Registry Services, Inc. +amazon + +// americanexpress : 2015-07-31 American Express Travel Related Services Company, Inc. +americanexpress + +// americanfamily : 2015-07-23 AmFam, Inc. +americanfamily + +// amex : 2015-07-31 American Express Travel Related Services Company, Inc. +amex + +// amfam : 2015-07-23 AmFam, Inc. +amfam + +// amica : 2015-05-28 Amica Mutual Insurance Company +amica + +// amsterdam : 2014-07-24 Gemeente Amsterdam +amsterdam + +// analytics : 2014-12-18 Campus IP LLC +analytics + +// android : 2014-08-07 Charleston Road Registry Inc. +android + +// anquan : 2015-01-08 Beijing Qihu Keji Co., Ltd. +anquan + +// anz : 2015-07-31 Australia and New Zealand Banking Group Limited +anz + +// aol : 2015-09-17 Oath Inc. +aol + +// apartments : 2014-12-11 Binky Moon, LLC +apartments + +// app : 2015-05-14 Charleston Road Registry Inc. +app + +// apple : 2015-05-14 Apple Inc. +apple + +// aquarelle : 2014-07-24 Aquarelle.com +aquarelle + +// arab : 2015-11-12 League of Arab States +arab + +// aramco : 2014-11-20 Aramco Services Company +aramco + +// archi : 2014-02-06 Afilias Limited +archi + +// army : 2014-03-06 Dog Beach, LLC +army + +// art : 2016-03-24 UK Creative Ideas Limited +art + +// arte : 2014-12-11 Association Relative à la Télévision Européenne G.E.I.E. +arte + +// asda : 2015-07-31 Wal-Mart Stores, Inc. +asda + +// associates : 2014-03-06 Binky Moon, LLC +associates + +// athleta : 2015-07-30 The Gap, Inc. +athleta + +// attorney : 2014-03-20 Dog Beach, LLC +attorney + +// auction : 2014-03-20 Dog Beach, LLC +auction + +// audi : 2015-05-21 AUDI Aktiengesellschaft +audi + +// audible : 2015-06-25 Amazon Registry Services, Inc. +audible + +// audio : 2014-03-20 UNR Corp. +audio + +// auspost : 2015-08-13 Australian Postal Corporation +auspost + +// author : 2014-12-18 Amazon Registry Services, Inc. +author + +// auto : 2014-11-13 XYZ.COM LLC +auto + +// autos : 2014-01-09 XYZ.COM LLC +autos + +// avianca : 2015-01-08 Avianca Holdings S.A. +avianca + +// aws : 2015-06-25 AWS Registry LLC +aws + +// axa : 2013-12-19 AXA Group Operations SAS +axa + +// azure : 2014-12-18 Microsoft Corporation +azure + +// baby : 2015-04-09 XYZ.COM LLC +baby + +// baidu : 2015-01-08 Baidu, Inc. +baidu + +// banamex : 2015-07-30 Citigroup Inc. +banamex + +// bananarepublic : 2015-07-31 The Gap, Inc. +bananarepublic + +// band : 2014-06-12 Dog Beach, LLC +band + +// bank : 2014-09-25 fTLD Registry Services LLC +bank + +// bar : 2013-12-12 Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable +bar + +// barcelona : 2014-07-24 Municipi de Barcelona +barcelona + +// barclaycard : 2014-11-20 Barclays Bank PLC +barclaycard + +// barclays : 2014-11-20 Barclays Bank PLC +barclays + +// barefoot : 2015-06-11 Gallo Vineyards, Inc. +barefoot + +// bargains : 2013-11-14 Binky Moon, LLC +bargains + +// baseball : 2015-10-29 MLB Advanced Media DH, LLC +baseball + +// basketball : 2015-08-20 Fédération Internationale de Basketball (FIBA) +basketball + +// bauhaus : 2014-04-17 Werkhaus GmbH +bauhaus + +// bayern : 2014-01-23 Bayern Connect GmbH +bayern + +// bbc : 2014-12-18 British Broadcasting Corporation +bbc + +// bbt : 2015-07-23 BB&T Corporation +bbt + +// bbva : 2014-10-02 BANCO BILBAO VIZCAYA ARGENTARIA, S.A. +bbva + +// bcg : 2015-04-02 The Boston Consulting Group, Inc. +bcg + +// bcn : 2014-07-24 Municipi de Barcelona +bcn + +// beats : 2015-05-14 Beats Electronics, LLC +beats + +// beauty : 2015-12-03 XYZ.COM LLC +beauty + +// beer : 2014-01-09 Minds + Machines Group Limited +beer + +// bentley : 2014-12-18 Bentley Motors Limited +bentley + +// berlin : 2013-10-31 dotBERLIN GmbH & Co. KG +berlin + +// best : 2013-12-19 BestTLD Pty Ltd +best + +// bestbuy : 2015-07-31 BBY Solutions, Inc. +bestbuy + +// bet : 2015-05-07 Afilias Limited +bet + +// bharti : 2014-01-09 Bharti Enterprises (Holding) Private Limited +bharti + +// bible : 2014-06-19 American Bible Society +bible + +// bid : 2013-12-19 dot Bid Limited +bid + +// bike : 2013-08-27 Binky Moon, LLC +bike + +// bing : 2014-12-18 Microsoft Corporation +bing + +// bingo : 2014-12-04 Binky Moon, LLC +bingo + +// bio : 2014-03-06 Afilias Limited +bio + +// black : 2014-01-16 Afilias Limited +black + +// blackfriday : 2014-01-16 UNR Corp. +blackfriday + +// blockbuster : 2015-07-30 Dish DBS Corporation +blockbuster + +// blog : 2015-05-14 Knock Knock WHOIS There, LLC +blog + +// bloomberg : 2014-07-17 Bloomberg IP Holdings LLC +bloomberg + +// blue : 2013-11-07 Afilias Limited +blue + +// bms : 2014-10-30 Bristol-Myers Squibb Company +bms + +// bmw : 2014-01-09 Bayerische Motoren Werke Aktiengesellschaft +bmw + +// bnpparibas : 2014-05-29 BNP Paribas +bnpparibas + +// boats : 2014-12-04 XYZ.COM LLC +boats + +// boehringer : 2015-07-09 Boehringer Ingelheim International GmbH +boehringer + +// bofa : 2015-07-31 Bank of America Corporation +bofa + +// bom : 2014-10-16 Núcleo de Informação e Coordenação do Ponto BR - NIC.br +bom + +// bond : 2014-06-05 ShortDot SA +bond + +// boo : 2014-01-30 Charleston Road Registry Inc. +boo + +// book : 2015-08-27 Amazon Registry Services, Inc. +book + +// booking : 2015-07-16 Booking.com B.V. +booking + +// bosch : 2015-06-18 Robert Bosch GMBH +bosch + +// bostik : 2015-05-28 Bostik SA +bostik + +// boston : 2015-12-10 Boston TLD Management, LLC +boston + +// bot : 2014-12-18 Amazon Registry Services, Inc. +bot + +// boutique : 2013-11-14 Binky Moon, LLC +boutique + +// box : 2015-11-12 Intercap Registry Inc. +box + +// bradesco : 2014-12-18 Banco Bradesco S.A. +bradesco + +// bridgestone : 2014-12-18 Bridgestone Corporation +bridgestone + +// broadway : 2014-12-22 Celebrate Broadway, Inc. +broadway + +// broker : 2014-12-11 Dotbroker Registry Limited +broker + +// brother : 2015-01-29 Brother Industries, Ltd. +brother + +// brussels : 2014-02-06 DNS.be vzw +brussels + +// budapest : 2013-11-21 Minds + Machines Group Limited +budapest + +// bugatti : 2015-07-23 Bugatti International SA +bugatti + +// build : 2013-11-07 Plan Bee LLC +build + +// builders : 2013-11-07 Binky Moon, LLC +builders + +// business : 2013-11-07 Binky Moon, LLC +business + +// buy : 2014-12-18 Amazon Registry Services, Inc. +buy + +// buzz : 2013-10-02 DOTSTRATEGY CO. +buzz + +// bzh : 2014-02-27 Association www.bzh +bzh + +// cab : 2013-10-24 Binky Moon, LLC +cab + +// cafe : 2015-02-11 Binky Moon, LLC +cafe + +// cal : 2014-07-24 Charleston Road Registry Inc. +cal + +// call : 2014-12-18 Amazon Registry Services, Inc. +call + +// calvinklein : 2015-07-30 PVH gTLD Holdings LLC +calvinklein + +// cam : 2016-04-21 AC Webconnecting Holding B.V. +cam + +// camera : 2013-08-27 Binky Moon, LLC +camera + +// camp : 2013-11-07 Binky Moon, LLC +camp + +// cancerresearch : 2014-05-15 Australian Cancer Research Foundation +cancerresearch + +// canon : 2014-09-12 Canon Inc. +canon + +// capetown : 2014-03-24 ZA Central Registry NPC trading as ZA Central Registry +capetown + +// capital : 2014-03-06 Binky Moon, LLC +capital + +// capitalone : 2015-08-06 Capital One Financial Corporation +capitalone + +// car : 2015-01-22 XYZ.COM LLC +car + +// caravan : 2013-12-12 Caravan International, Inc. +caravan + +// cards : 2013-12-05 Binky Moon, LLC +cards + +// care : 2014-03-06 Binky Moon, LLC +care + +// career : 2013-10-09 dotCareer LLC +career + +// careers : 2013-10-02 Binky Moon, LLC +careers + +// cars : 2014-11-13 XYZ.COM LLC +cars + +// casa : 2013-11-21 Minds + Machines Group Limited +casa + +// case : 2015-09-03 CNH Industrial N.V. +case + +// caseih : 2015-09-03 CNH Industrial N.V. +caseih + +// cash : 2014-03-06 Binky Moon, LLC +cash + +// casino : 2014-12-18 Binky Moon, LLC +casino + +// catering : 2013-12-05 Binky Moon, LLC +catering + +// catholic : 2015-10-21 Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication) +catholic + +// cba : 2014-06-26 COMMONWEALTH BANK OF AUSTRALIA +cba + +// cbn : 2014-08-22 The Christian Broadcasting Network, Inc. +cbn + +// cbre : 2015-07-02 CBRE, Inc. +cbre + +// cbs : 2015-08-06 CBS Domains Inc. +cbs + +// center : 2013-11-07 Binky Moon, LLC +center + +// ceo : 2013-11-07 CEOTLD Pty Ltd +ceo + +// cern : 2014-06-05 European Organization for Nuclear Research ("CERN") +cern + +// cfa : 2014-08-28 CFA Institute +cfa + +// cfd : 2014-12-11 ShortDot SA +cfd + +// chanel : 2015-04-09 Chanel International B.V. +chanel + +// channel : 2014-05-08 Charleston Road Registry Inc. +channel + +// charity : 2018-04-11 Binky Moon, LLC +charity + +// chase : 2015-04-30 JPMorgan Chase Bank, National Association +chase + +// chat : 2014-12-04 Binky Moon, LLC +chat + +// cheap : 2013-11-14 Binky Moon, LLC +cheap + +// chintai : 2015-06-11 CHINTAI Corporation +chintai + +// christmas : 2013-11-21 UNR Corp. +christmas + +// chrome : 2014-07-24 Charleston Road Registry Inc. +chrome + +// church : 2014-02-06 Binky Moon, LLC +church + +// cipriani : 2015-02-19 Hotel Cipriani Srl +cipriani + +// circle : 2014-12-18 Amazon Registry Services, Inc. +circle + +// cisco : 2014-12-22 Cisco Technology, Inc. +cisco + +// citadel : 2015-07-23 Citadel Domain LLC +citadel + +// citi : 2015-07-30 Citigroup Inc. +citi + +// citic : 2014-01-09 CITIC Group Corporation +citic + +// city : 2014-05-29 Binky Moon, LLC +city + +// cityeats : 2014-12-11 Lifestyle Domain Holdings, Inc. +cityeats + +// claims : 2014-03-20 Binky Moon, LLC +claims + +// cleaning : 2013-12-05 Binky Moon, LLC +cleaning + +// click : 2014-06-05 UNR Corp. +click + +// clinic : 2014-03-20 Binky Moon, LLC +clinic + +// clinique : 2015-10-01 The Estée Lauder Companies Inc. +clinique + +// clothing : 2013-08-27 Binky Moon, LLC +clothing + +// cloud : 2015-04-16 Aruba PEC S.p.A. +cloud + +// club : 2013-11-08 .CLUB DOMAINS, LLC +club + +// clubmed : 2015-06-25 Club Méditerranée S.A. +clubmed + +// coach : 2014-10-09 Binky Moon, LLC +coach + +// codes : 2013-10-31 Binky Moon, LLC +codes + +// coffee : 2013-10-17 Binky Moon, LLC +coffee + +// college : 2014-01-16 XYZ.COM LLC +college + +// cologne : 2014-02-05 dotKoeln GmbH +cologne + +// comcast : 2015-07-23 Comcast IP Holdings I, LLC +comcast + +// commbank : 2014-06-26 COMMONWEALTH BANK OF AUSTRALIA +commbank + +// community : 2013-12-05 Binky Moon, LLC +community + +// company : 2013-11-07 Binky Moon, LLC +company + +// compare : 2015-10-08 Registry Services, LLC +compare + +// computer : 2013-10-24 Binky Moon, LLC +computer + +// comsec : 2015-01-08 VeriSign, Inc. +comsec + +// condos : 2013-12-05 Binky Moon, LLC +condos + +// construction : 2013-09-16 Binky Moon, LLC +construction + +// consulting : 2013-12-05 Dog Beach, LLC +consulting + +// contact : 2015-01-08 Dog Beach, LLC +contact + +// contractors : 2013-09-10 Binky Moon, LLC +contractors + +// cooking : 2013-11-21 Minds + Machines Group Limited +cooking + +// cookingchannel : 2015-07-02 Lifestyle Domain Holdings, Inc. +cookingchannel + +// cool : 2013-11-14 Binky Moon, LLC +cool + +// corsica : 2014-09-25 Collectivité de Corse +corsica + +// country : 2013-12-19 DotCountry LLC +country + +// coupon : 2015-02-26 Amazon Registry Services, Inc. +coupon + +// coupons : 2015-03-26 Binky Moon, LLC +coupons + +// courses : 2014-12-04 OPEN UNIVERSITIES AUSTRALIA PTY LTD +courses + +// cpa : 2019-06-10 American Institute of Certified Public Accountants +cpa + +// credit : 2014-03-20 Binky Moon, LLC +credit + +// creditcard : 2014-03-20 Binky Moon, LLC +creditcard + +// creditunion : 2015-01-22 DotCooperation LLC +creditunion + +// cricket : 2014-10-09 dot Cricket Limited +cricket + +// crown : 2014-10-24 Crown Equipment Corporation +crown + +// crs : 2014-04-03 Federated Co-operatives Limited +crs + +// cruise : 2015-12-10 Viking River Cruises (Bermuda) Ltd. +cruise + +// cruises : 2013-12-05 Binky Moon, LLC +cruises + +// csc : 2014-09-25 Alliance-One Services, Inc. +csc + +// cuisinella : 2014-04-03 SCHMIDT GROUPE S.A.S. +cuisinella + +// cymru : 2014-05-08 Nominet UK +cymru + +// cyou : 2015-01-22 ShortDot SA +cyou + +// dabur : 2014-02-06 Dabur India Limited +dabur + +// dad : 2014-01-23 Charleston Road Registry Inc. +dad + +// dance : 2013-10-24 Dog Beach, LLC +dance + +// data : 2016-06-02 Dish DBS Corporation +data + +// date : 2014-11-20 dot Date Limited +date + +// dating : 2013-12-05 Binky Moon, LLC +dating + +// datsun : 2014-03-27 NISSAN MOTOR CO., LTD. +datsun + +// day : 2014-01-30 Charleston Road Registry Inc. +day + +// dclk : 2014-11-20 Charleston Road Registry Inc. +dclk + +// dds : 2015-05-07 Minds + Machines Group Limited +dds + +// deal : 2015-06-25 Amazon Registry Services, Inc. +deal + +// dealer : 2014-12-22 Intercap Registry Inc. +dealer + +// deals : 2014-05-22 Binky Moon, LLC +deals + +// degree : 2014-03-06 Dog Beach, LLC +degree + +// delivery : 2014-09-11 Binky Moon, LLC +delivery + +// dell : 2014-10-24 Dell Inc. +dell + +// deloitte : 2015-07-31 Deloitte Touche Tohmatsu +deloitte + +// delta : 2015-02-19 Delta Air Lines, Inc. +delta + +// democrat : 2013-10-24 Dog Beach, LLC +democrat + +// dental : 2014-03-20 Binky Moon, LLC +dental + +// dentist : 2014-03-20 Dog Beach, LLC +dentist + +// desi : 2013-11-14 Desi Networks LLC +desi + +// design : 2014-11-07 Top Level Design, LLC +design + +// dev : 2014-10-16 Charleston Road Registry Inc. +dev + +// dhl : 2015-07-23 Deutsche Post AG +dhl + +// diamonds : 2013-09-22 Binky Moon, LLC +diamonds + +// diet : 2014-06-26 UNR Corp. +diet + +// digital : 2014-03-06 Binky Moon, LLC +digital + +// direct : 2014-04-10 Binky Moon, LLC +direct + +// directory : 2013-09-20 Binky Moon, LLC +directory + +// discount : 2014-03-06 Binky Moon, LLC +discount + +// discover : 2015-07-23 Discover Financial Services +discover + +// dish : 2015-07-30 Dish DBS Corporation +dish + +// diy : 2015-11-05 Lifestyle Domain Holdings, Inc. +diy + +// dnp : 2013-12-13 Dai Nippon Printing Co., Ltd. +dnp + +// docs : 2014-10-16 Charleston Road Registry Inc. +docs + +// doctor : 2016-06-02 Binky Moon, LLC +doctor + +// dog : 2014-12-04 Binky Moon, LLC +dog + +// domains : 2013-10-17 Binky Moon, LLC +domains + +// dot : 2015-05-21 Dish DBS Corporation +dot + +// download : 2014-11-20 dot Support Limited +download + +// drive : 2015-03-05 Charleston Road Registry Inc. +drive + +// dtv : 2015-06-04 Dish DBS Corporation +dtv + +// dubai : 2015-01-01 Dubai Smart Government Department +dubai + +// duck : 2015-07-23 Johnson Shareholdings, Inc. +duck + +// dunlop : 2015-07-02 The Goodyear Tire & Rubber Company +dunlop + +// dupont : 2015-06-25 E. I. du Pont de Nemours and Company +dupont + +// durban : 2014-03-24 ZA Central Registry NPC trading as ZA Central Registry +durban + +// dvag : 2014-06-23 Deutsche Vermögensberatung Aktiengesellschaft DVAG +dvag + +// dvr : 2016-05-26 DISH Technologies L.L.C. +dvr + +// earth : 2014-12-04 Interlink Co., Ltd. +earth + +// eat : 2014-01-23 Charleston Road Registry Inc. +eat + +// eco : 2016-07-08 Big Room Inc. +eco + +// edeka : 2014-12-18 EDEKA Verband kaufmännischer Genossenschaften e.V. +edeka + +// education : 2013-11-07 Binky Moon, LLC +education + +// email : 2013-10-31 Binky Moon, LLC +email + +// emerck : 2014-04-03 Merck KGaA +emerck + +// energy : 2014-09-11 Binky Moon, LLC +energy + +// engineer : 2014-03-06 Dog Beach, LLC +engineer + +// engineering : 2014-03-06 Binky Moon, LLC +engineering + +// enterprises : 2013-09-20 Binky Moon, LLC +enterprises + +// epson : 2014-12-04 Seiko Epson Corporation +epson + +// equipment : 2013-08-27 Binky Moon, LLC +equipment + +// ericsson : 2015-07-09 Telefonaktiebolaget L M Ericsson +ericsson + +// erni : 2014-04-03 ERNI Group Holding AG +erni + +// esq : 2014-05-08 Charleston Road Registry Inc. +esq + +// estate : 2013-08-27 Binky Moon, LLC +estate + +// etisalat : 2015-09-03 Emirates Telecommunications Corporation (trading as Etisalat) +etisalat + +// eurovision : 2014-04-24 European Broadcasting Union (EBU) +eurovision + +// eus : 2013-12-12 Puntueus Fundazioa +eus + +// events : 2013-12-05 Binky Moon, LLC +events + +// exchange : 2014-03-06 Binky Moon, LLC +exchange + +// expert : 2013-11-21 Binky Moon, LLC +expert + +// exposed : 2013-12-05 Binky Moon, LLC +exposed + +// express : 2015-02-11 Binky Moon, LLC +express + +// extraspace : 2015-05-14 Extra Space Storage LLC +extraspace + +// fage : 2014-12-18 Fage International S.A. +fage + +// fail : 2014-03-06 Binky Moon, LLC +fail + +// fairwinds : 2014-11-13 FairWinds Partners, LLC +fairwinds + +// faith : 2014-11-20 dot Faith Limited +faith + +// family : 2015-04-02 Dog Beach, LLC +family + +// fan : 2014-03-06 Dog Beach, LLC +fan + +// fans : 2014-11-07 ZDNS International Limited +fans + +// farm : 2013-11-07 Binky Moon, LLC +farm + +// farmers : 2015-07-09 Farmers Insurance Exchange +farmers + +// fashion : 2014-07-03 Minds + Machines Group Limited +fashion + +// fast : 2014-12-18 Amazon Registry Services, Inc. +fast + +// fedex : 2015-08-06 Federal Express Corporation +fedex + +// feedback : 2013-12-19 Top Level Spectrum, Inc. +feedback + +// ferrari : 2015-07-31 Fiat Chrysler Automobiles N.V. +ferrari + +// ferrero : 2014-12-18 Ferrero Trading Lux S.A. +ferrero + +// fiat : 2015-07-31 Fiat Chrysler Automobiles N.V. +fiat + +// fidelity : 2015-07-30 Fidelity Brokerage Services LLC +fidelity + +// fido : 2015-08-06 Rogers Communications Canada Inc. +fido + +// film : 2015-01-08 Motion Picture Domain Registry Pty Ltd +film + +// final : 2014-10-16 Núcleo de Informação e Coordenação do Ponto BR - NIC.br +final + +// finance : 2014-03-20 Binky Moon, LLC +finance + +// financial : 2014-03-06 Binky Moon, LLC +financial + +// fire : 2015-06-25 Amazon Registry Services, Inc. +fire + +// firestone : 2014-12-18 Bridgestone Licensing Services, Inc +firestone + +// firmdale : 2014-03-27 Firmdale Holdings Limited +firmdale + +// fish : 2013-12-12 Binky Moon, LLC +fish + +// fishing : 2013-11-21 Minds + Machines Group Limited +fishing + +// fit : 2014-11-07 Minds + Machines Group Limited +fit + +// fitness : 2014-03-06 Binky Moon, LLC +fitness + +// flickr : 2015-04-02 Flickr, Inc. +flickr + +// flights : 2013-12-05 Binky Moon, LLC +flights + +// flir : 2015-07-23 FLIR Systems, Inc. +flir + +// florist : 2013-11-07 Binky Moon, LLC +florist + +// flowers : 2014-10-09 UNR Corp. +flowers + +// fly : 2014-05-08 Charleston Road Registry Inc. +fly + +// foo : 2014-01-23 Charleston Road Registry Inc. +foo + +// food : 2016-04-21 Lifestyle Domain Holdings, Inc. +food + +// foodnetwork : 2015-07-02 Lifestyle Domain Holdings, Inc. +foodnetwork + +// football : 2014-12-18 Binky Moon, LLC +football + +// ford : 2014-11-13 Ford Motor Company +ford + +// forex : 2014-12-11 Dotforex Registry Limited +forex + +// forsale : 2014-05-22 Dog Beach, LLC +forsale + +// forum : 2015-04-02 Fegistry, LLC +forum + +// foundation : 2013-12-05 Binky Moon, LLC +foundation + +// fox : 2015-09-11 FOX Registry, LLC +fox + +// free : 2015-12-10 Amazon Registry Services, Inc. +free + +// fresenius : 2015-07-30 Fresenius Immobilien-Verwaltungs-GmbH +fresenius + +// frl : 2014-05-15 FRLregistry B.V. +frl + +// frogans : 2013-12-19 OP3FT +frogans + +// frontdoor : 2015-07-02 Lifestyle Domain Holdings, Inc. +frontdoor + +// frontier : 2015-02-05 Frontier Communications Corporation +frontier + +// ftr : 2015-07-16 Frontier Communications Corporation +ftr + +// fujitsu : 2015-07-30 Fujitsu Limited +fujitsu + +// fujixerox : 2015-07-23 Xerox DNHC LLC +fujixerox + +// fun : 2016-01-14 DotSpace Inc. +fun + +// fund : 2014-03-20 Binky Moon, LLC +fund + +// furniture : 2014-03-20 Binky Moon, LLC +furniture + +// futbol : 2013-09-20 Dog Beach, LLC +futbol + +// fyi : 2015-04-02 Binky Moon, LLC +fyi + +// gal : 2013-11-07 Asociación puntoGAL +gal + +// gallery : 2013-09-13 Binky Moon, LLC +gallery + +// gallo : 2015-06-11 Gallo Vineyards, Inc. +gallo + +// gallup : 2015-02-19 Gallup, Inc. +gallup + +// game : 2015-05-28 UNR Corp. +game + +// games : 2015-05-28 Dog Beach, LLC +games + +// gap : 2015-07-31 The Gap, Inc. +gap + +// garden : 2014-06-26 Minds + Machines Group Limited +garden + +// gay : 2019-05-23 Top Level Design, LLC +gay + +// gbiz : 2014-07-17 Charleston Road Registry Inc. +gbiz + +// gdn : 2014-07-31 Joint Stock Company "Navigation-information systems" +gdn + +// gea : 2014-12-04 GEA Group Aktiengesellschaft +gea + +// gent : 2014-01-23 COMBELL NV +gent + +// genting : 2015-03-12 Resorts World Inc Pte. Ltd. +genting + +// george : 2015-07-31 Wal-Mart Stores, Inc. +george + +// ggee : 2014-01-09 GMO Internet, Inc. +ggee + +// gift : 2013-10-17 DotGift, LLC +gift + +// gifts : 2014-07-03 Binky Moon, LLC +gifts + +// gives : 2014-03-06 Dog Beach, LLC +gives + +// giving : 2014-11-13 Giving Limited +giving + +// glade : 2015-07-23 Johnson Shareholdings, Inc. +glade + +// glass : 2013-11-07 Binky Moon, LLC +glass + +// gle : 2014-07-24 Charleston Road Registry Inc. +gle + +// global : 2014-04-17 Dot Global Domain Registry Limited +global + +// globo : 2013-12-19 Globo Comunicação e Participações S.A +globo + +// gmail : 2014-05-01 Charleston Road Registry Inc. +gmail + +// gmbh : 2016-01-29 Binky Moon, LLC +gmbh + +// gmo : 2014-01-09 GMO Internet, Inc. +gmo + +// gmx : 2014-04-24 1&1 Mail & Media GmbH +gmx + +// godaddy : 2015-07-23 Go Daddy East, LLC +godaddy + +// gold : 2015-01-22 Binky Moon, LLC +gold + +// goldpoint : 2014-11-20 YODOBASHI CAMERA CO.,LTD. +goldpoint + +// golf : 2014-12-18 Binky Moon, LLC +golf + +// goo : 2014-12-18 NTT Resonant Inc. +goo + +// goodyear : 2015-07-02 The Goodyear Tire & Rubber Company +goodyear + +// goog : 2014-11-20 Charleston Road Registry Inc. +goog + +// google : 2014-07-24 Charleston Road Registry Inc. +google + +// gop : 2014-01-16 Republican State Leadership Committee, Inc. +gop + +// got : 2014-12-18 Amazon Registry Services, Inc. +got + +// grainger : 2015-05-07 Grainger Registry Services, LLC +grainger + +// graphics : 2013-09-13 Binky Moon, LLC +graphics + +// gratis : 2014-03-20 Binky Moon, LLC +gratis + +// green : 2014-05-08 Afilias Limited +green + +// gripe : 2014-03-06 Binky Moon, LLC +gripe + +// grocery : 2016-06-16 Wal-Mart Stores, Inc. +grocery + +// group : 2014-08-15 Binky Moon, LLC +group + +// guardian : 2015-07-30 The Guardian Life Insurance Company of America +guardian + +// gucci : 2014-11-13 Guccio Gucci S.p.a. +gucci + +// guge : 2014-08-28 Charleston Road Registry Inc. +guge + +// guide : 2013-09-13 Binky Moon, LLC +guide + +// guitars : 2013-11-14 UNR Corp. +guitars + +// guru : 2013-08-27 Binky Moon, LLC +guru + +// hair : 2015-12-03 XYZ.COM LLC +hair + +// hamburg : 2014-02-20 Hamburg Top-Level-Domain GmbH +hamburg + +// hangout : 2014-11-13 Charleston Road Registry Inc. +hangout + +// haus : 2013-12-05 Dog Beach, LLC +haus + +// hbo : 2015-07-30 HBO Registry Services, Inc. +hbo + +// hdfc : 2015-07-30 HOUSING DEVELOPMENT FINANCE CORPORATION LIMITED +hdfc + +// hdfcbank : 2015-02-12 HDFC Bank Limited +hdfcbank + +// health : 2015-02-11 DotHealth, LLC +health + +// healthcare : 2014-06-12 Binky Moon, LLC +healthcare + +// help : 2014-06-26 UNR Corp. +help + +// helsinki : 2015-02-05 City of Helsinki +helsinki + +// here : 2014-02-06 Charleston Road Registry Inc. +here + +// hermes : 2014-07-10 HERMES INTERNATIONAL +hermes + +// hgtv : 2015-07-02 Lifestyle Domain Holdings, Inc. +hgtv + +// hiphop : 2014-03-06 UNR Corp. +hiphop + +// hisamitsu : 2015-07-16 Hisamitsu Pharmaceutical Co.,Inc. +hisamitsu + +// hitachi : 2014-10-31 Hitachi, Ltd. +hitachi + +// hiv : 2014-03-13 UNR Corp. +hiv + +// hkt : 2015-05-14 PCCW-HKT DataCom Services Limited +hkt + +// hockey : 2015-03-19 Binky Moon, LLC +hockey + +// holdings : 2013-08-27 Binky Moon, LLC +holdings + +// holiday : 2013-11-07 Binky Moon, LLC +holiday + +// homedepot : 2015-04-02 Home Depot Product Authority, LLC +homedepot + +// homegoods : 2015-07-16 The TJX Companies, Inc. +homegoods + +// homes : 2014-01-09 XYZ.COM LLC +homes + +// homesense : 2015-07-16 The TJX Companies, Inc. +homesense + +// honda : 2014-12-18 Honda Motor Co., Ltd. +honda + +// horse : 2013-11-21 Minds + Machines Group Limited +horse + +// hospital : 2016-10-20 Binky Moon, LLC +hospital + +// host : 2014-04-17 DotHost Inc. +host + +// hosting : 2014-05-29 UNR Corp. +hosting + +// hot : 2015-08-27 Amazon Registry Services, Inc. +hot + +// hoteles : 2015-03-05 Travel Reservations SRL +hoteles + +// hotels : 2016-04-07 Booking.com B.V. +hotels + +// hotmail : 2014-12-18 Microsoft Corporation +hotmail + +// house : 2013-11-07 Binky Moon, LLC +house + +// how : 2014-01-23 Charleston Road Registry Inc. +how + +// hsbc : 2014-10-24 HSBC Global Services (UK) Limited +hsbc + +// hughes : 2015-07-30 Hughes Satellite Systems Corporation +hughes + +// hyatt : 2015-07-30 Hyatt GTLD, L.L.C. +hyatt + +// hyundai : 2015-07-09 Hyundai Motor Company +hyundai + +// ibm : 2014-07-31 International Business Machines Corporation +ibm + +// icbc : 2015-02-19 Industrial and Commercial Bank of China Limited +icbc + +// ice : 2014-10-30 IntercontinentalExchange, Inc. +ice + +// icu : 2015-01-08 ShortDot SA +icu + +// ieee : 2015-07-23 IEEE Global LLC +ieee + +// ifm : 2014-01-30 ifm electronic gmbh +ifm + +// ikano : 2015-07-09 Ikano S.A. +ikano + +// imamat : 2015-08-06 Fondation Aga Khan (Aga Khan Foundation) +imamat + +// imdb : 2015-06-25 Amazon Registry Services, Inc. +imdb + +// immo : 2014-07-10 Binky Moon, LLC +immo + +// immobilien : 2013-11-07 Dog Beach, LLC +immobilien + +// inc : 2018-03-10 Intercap Registry Inc. +inc + +// industries : 2013-12-05 Binky Moon, LLC +industries + +// infiniti : 2014-03-27 NISSAN MOTOR CO., LTD. +infiniti + +// ing : 2014-01-23 Charleston Road Registry Inc. +ing + +// ink : 2013-12-05 Top Level Design, LLC +ink + +// institute : 2013-11-07 Binky Moon, LLC +institute + +// insurance : 2015-02-19 fTLD Registry Services LLC +insurance + +// insure : 2014-03-20 Binky Moon, LLC +insure + +// international : 2013-11-07 Binky Moon, LLC +international + +// intuit : 2015-07-30 Intuit Administrative Services, Inc. +intuit + +// investments : 2014-03-20 Binky Moon, LLC +investments + +// ipiranga : 2014-08-28 Ipiranga Produtos de Petroleo S.A. +ipiranga + +// irish : 2014-08-07 Binky Moon, LLC +irish + +// ismaili : 2015-08-06 Fondation Aga Khan (Aga Khan Foundation) +ismaili + +// ist : 2014-08-28 Istanbul Metropolitan Municipality +ist + +// istanbul : 2014-08-28 Istanbul Metropolitan Municipality +istanbul + +// itau : 2014-10-02 Itau Unibanco Holding S.A. +itau + +// itv : 2015-07-09 ITV Services Limited +itv + +// iveco : 2015-09-03 CNH Industrial N.V. +iveco + +// jaguar : 2014-11-13 Jaguar Land Rover Ltd +jaguar + +// java : 2014-06-19 Oracle Corporation +java + +// jcb : 2014-11-20 JCB Co., Ltd. +jcb + +// jeep : 2015-07-30 FCA US LLC. +jeep + +// jetzt : 2014-01-09 Binky Moon, LLC +jetzt + +// jewelry : 2015-03-05 Binky Moon, LLC +jewelry + +// jio : 2015-04-02 Reliance Industries Limited +jio + +// jll : 2015-04-02 Jones Lang LaSalle Incorporated +jll + +// jmp : 2015-03-26 Matrix IP LLC +jmp + +// jnj : 2015-06-18 Johnson & Johnson Services, Inc. +jnj + +// joburg : 2014-03-24 ZA Central Registry NPC trading as ZA Central Registry +joburg + +// jot : 2014-12-18 Amazon Registry Services, Inc. +jot + +// joy : 2014-12-18 Amazon Registry Services, Inc. +joy + +// jpmorgan : 2015-04-30 JPMorgan Chase Bank, National Association +jpmorgan + +// jprs : 2014-09-18 Japan Registry Services Co., Ltd. +jprs + +// juegos : 2014-03-20 UNR Corp. +juegos + +// juniper : 2015-07-30 JUNIPER NETWORKS, INC. +juniper + +// kaufen : 2013-11-07 Dog Beach, LLC +kaufen + +// kddi : 2014-09-12 KDDI CORPORATION +kddi + +// kerryhotels : 2015-04-30 Kerry Trading Co. Limited +kerryhotels + +// kerrylogistics : 2015-04-09 Kerry Trading Co. Limited +kerrylogistics + +// kerryproperties : 2015-04-09 Kerry Trading Co. Limited +kerryproperties + +// kfh : 2014-12-04 Kuwait Finance House +kfh + +// kia : 2015-07-09 KIA MOTORS CORPORATION +kia + +// kim : 2013-09-23 Afilias Limited +kim + +// kinder : 2014-11-07 Ferrero Trading Lux S.A. +kinder + +// kindle : 2015-06-25 Amazon Registry Services, Inc. +kindle + +// kitchen : 2013-09-20 Binky Moon, LLC +kitchen + +// kiwi : 2013-09-20 DOT KIWI LIMITED +kiwi + +// koeln : 2014-01-09 dotKoeln GmbH +koeln + +// komatsu : 2015-01-08 Komatsu Ltd. +komatsu + +// kosher : 2015-08-20 Kosher Marketing Assets LLC +kosher + +// kpmg : 2015-04-23 KPMG International Cooperative (KPMG International Genossenschaft) +kpmg + +// kpn : 2015-01-08 Koninklijke KPN N.V. +kpn + +// krd : 2013-12-05 KRG Department of Information Technology +krd + +// kred : 2013-12-19 KredTLD Pty Ltd +kred + +// kuokgroup : 2015-04-09 Kerry Trading Co. Limited +kuokgroup + +// kyoto : 2014-11-07 Academic Institution: Kyoto Jyoho Gakuen +kyoto + +// lacaixa : 2014-01-09 Fundación Bancaria Caixa d’Estalvis i Pensions de Barcelona, “la Caixa” +lacaixa + +// lamborghini : 2015-06-04 Automobili Lamborghini S.p.A. +lamborghini + +// lamer : 2015-10-01 The Estée Lauder Companies Inc. +lamer + +// lancaster : 2015-02-12 LANCASTER +lancaster + +// lancia : 2015-07-31 Fiat Chrysler Automobiles N.V. +lancia + +// land : 2013-09-10 Binky Moon, LLC +land + +// landrover : 2014-11-13 Jaguar Land Rover Ltd +landrover + +// lanxess : 2015-07-30 LANXESS Corporation +lanxess + +// lasalle : 2015-04-02 Jones Lang LaSalle Incorporated +lasalle + +// lat : 2014-10-16 ECOM-LAC Federaciòn de Latinoamèrica y el Caribe para Internet y el Comercio Electrònico +lat + +// latino : 2015-07-30 Dish DBS Corporation +latino + +// latrobe : 2014-06-16 La Trobe University +latrobe + +// law : 2015-01-22 LW TLD Limited +law + +// lawyer : 2014-03-20 Dog Beach, LLC +lawyer + +// lds : 2014-03-20 IRI Domain Management, LLC +lds + +// lease : 2014-03-06 Binky Moon, LLC +lease + +// leclerc : 2014-08-07 A.C.D. LEC Association des Centres Distributeurs Edouard Leclerc +leclerc + +// lefrak : 2015-07-16 LeFrak Organization, Inc. +lefrak + +// legal : 2014-10-16 Binky Moon, LLC +legal + +// lego : 2015-07-16 LEGO Juris A/S +lego + +// lexus : 2015-04-23 TOYOTA MOTOR CORPORATION +lexus + +// lgbt : 2014-05-08 Afilias Limited +lgbt + +// lidl : 2014-09-18 Schwarz Domains und Services GmbH & Co. KG +lidl + +// life : 2014-02-06 Binky Moon, LLC +life + +// lifeinsurance : 2015-01-15 American Council of Life Insurers +lifeinsurance + +// lifestyle : 2014-12-11 Lifestyle Domain Holdings, Inc. +lifestyle + +// lighting : 2013-08-27 Binky Moon, LLC +lighting + +// like : 2014-12-18 Amazon Registry Services, Inc. +like + +// lilly : 2015-07-31 Eli Lilly and Company +lilly + +// limited : 2014-03-06 Binky Moon, LLC +limited + +// limo : 2013-10-17 Binky Moon, LLC +limo + +// lincoln : 2014-11-13 Ford Motor Company +lincoln + +// linde : 2014-12-04 Linde Aktiengesellschaft +linde + +// link : 2013-11-14 UNR Corp. +link + +// lipsy : 2015-06-25 Lipsy Ltd +lipsy + +// live : 2014-12-04 Dog Beach, LLC +live + +// living : 2015-07-30 Lifestyle Domain Holdings, Inc. +living + +// lixil : 2015-03-19 LIXIL Group Corporation +lixil + +// llc : 2017-12-14 Afilias Limited +llc + +// llp : 2019-08-26 UNR Corp. +llp + +// loan : 2014-11-20 dot Loan Limited +loan + +// loans : 2014-03-20 Binky Moon, LLC +loans + +// locker : 2015-06-04 Dish DBS Corporation +locker + +// locus : 2015-06-25 Locus Analytics LLC +locus + +// loft : 2015-07-30 Annco, Inc. +loft + +// lol : 2015-01-30 UNR Corp. +lol + +// london : 2013-11-14 Dot London Domains Limited +london + +// lotte : 2014-11-07 Lotte Holdings Co., Ltd. +lotte + +// lotto : 2014-04-10 Afilias Limited +lotto + +// love : 2014-12-22 Merchant Law Group LLP +love + +// lpl : 2015-07-30 LPL Holdings, Inc. +lpl + +// lplfinancial : 2015-07-30 LPL Holdings, Inc. +lplfinancial + +// ltd : 2014-09-25 Binky Moon, LLC +ltd + +// ltda : 2014-04-17 InterNetX, Corp +ltda + +// lundbeck : 2015-08-06 H. Lundbeck A/S +lundbeck + +// luxe : 2014-01-09 Minds + Machines Group Limited +luxe + +// luxury : 2013-10-17 Luxury Partners, LLC +luxury + +// macys : 2015-07-31 Macys, Inc. +macys + +// madrid : 2014-05-01 Comunidad de Madrid +madrid + +// maif : 2014-10-02 Mutuelle Assurance Instituteur France (MAIF) +maif + +// maison : 2013-12-05 Binky Moon, LLC +maison + +// makeup : 2015-01-15 XYZ.COM LLC +makeup + +// man : 2014-12-04 MAN SE +man + +// management : 2013-11-07 Binky Moon, LLC +management + +// mango : 2013-10-24 PUNTO FA S.L. +mango + +// map : 2016-06-09 Charleston Road Registry Inc. +map + +// market : 2014-03-06 Dog Beach, LLC +market + +// marketing : 2013-11-07 Binky Moon, LLC +marketing + +// markets : 2014-12-11 Dotmarkets Registry Limited +markets + +// marriott : 2014-10-09 Marriott Worldwide Corporation +marriott + +// marshalls : 2015-07-16 The TJX Companies, Inc. +marshalls + +// maserati : 2015-07-31 Fiat Chrysler Automobiles N.V. +maserati + +// mattel : 2015-08-06 Mattel Sites, Inc. +mattel + +// mba : 2015-04-02 Binky Moon, LLC +mba + +// mckinsey : 2015-07-31 McKinsey Holdings, Inc. +mckinsey + +// med : 2015-08-06 Medistry LLC +med + +// media : 2014-03-06 Binky Moon, LLC +media + +// meet : 2014-01-16 Charleston Road Registry Inc. +meet + +// melbourne : 2014-05-29 The Crown in right of the State of Victoria, represented by its Department of State Development, Business and Innovation +melbourne + +// meme : 2014-01-30 Charleston Road Registry Inc. +meme + +// memorial : 2014-10-16 Dog Beach, LLC +memorial + +// men : 2015-02-26 Exclusive Registry Limited +men + +// menu : 2013-09-11 Dot Menu Registry, LLC +menu + +// merckmsd : 2016-07-14 MSD Registry Holdings, Inc. +merckmsd + +// miami : 2013-12-19 Minds + Machines Group Limited +miami + +// microsoft : 2014-12-18 Microsoft Corporation +microsoft + +// mini : 2014-01-09 Bayerische Motoren Werke Aktiengesellschaft +mini + +// mint : 2015-07-30 Intuit Administrative Services, Inc. +mint + +// mit : 2015-07-02 Massachusetts Institute of Technology +mit + +// mitsubishi : 2015-07-23 Mitsubishi Corporation +mitsubishi + +// mlb : 2015-05-21 MLB Advanced Media DH, LLC +mlb + +// mls : 2015-04-23 The Canadian Real Estate Association +mls + +// mma : 2014-11-07 MMA IARD +mma + +// mobile : 2016-06-02 Dish DBS Corporation +mobile + +// moda : 2013-11-07 Dog Beach, LLC +moda + +// moe : 2013-11-13 Interlink Co., Ltd. +moe + +// moi : 2014-12-18 Amazon Registry Services, Inc. +moi + +// mom : 2015-04-16 UNR Corp. +mom + +// monash : 2013-09-30 Monash University +monash + +// money : 2014-10-16 Binky Moon, LLC +money + +// monster : 2015-09-11 XYZ.COM LLC +monster + +// mormon : 2013-12-05 IRI Domain Management, LLC +mormon + +// mortgage : 2014-03-20 Dog Beach, LLC +mortgage + +// moscow : 2013-12-19 Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID) +moscow + +// moto : 2015-06-04 Motorola Trademark Holdings, LLC +moto + +// motorcycles : 2014-01-09 XYZ.COM LLC +motorcycles + +// mov : 2014-01-30 Charleston Road Registry Inc. +mov + +// movie : 2015-02-05 Binky Moon, LLC +movie + +// msd : 2015-07-23 MSD Registry Holdings, Inc. +msd + +// mtn : 2014-12-04 MTN Dubai Limited +mtn + +// mtr : 2015-03-12 MTR Corporation Limited +mtr + +// mutual : 2015-04-02 Northwestern Mutual MU TLD Registry, LLC +mutual + +// nab : 2015-08-20 National Australia Bank Limited +nab + +// nagoya : 2013-10-24 GMO Registry, Inc. +nagoya + +// nationwide : 2015-07-23 Nationwide Mutual Insurance Company +nationwide + +// natura : 2015-03-12 NATURA COSMÉTICOS S.A. +natura + +// navy : 2014-03-06 Dog Beach, LLC +navy + +// nba : 2015-07-31 NBA REGISTRY, LLC +nba + +// nec : 2015-01-08 NEC Corporation +nec + +// netbank : 2014-06-26 COMMONWEALTH BANK OF AUSTRALIA +netbank + +// netflix : 2015-06-18 Netflix, Inc. +netflix + +// network : 2013-11-14 Binky Moon, LLC +network + +// neustar : 2013-12-05 NeuStar, Inc. +neustar + +// new : 2014-01-30 Charleston Road Registry Inc. +new + +// newholland : 2015-09-03 CNH Industrial N.V. +newholland + +// news : 2014-12-18 Dog Beach, LLC +news + +// next : 2015-06-18 Next plc +next + +// nextdirect : 2015-06-18 Next plc +nextdirect + +// nexus : 2014-07-24 Charleston Road Registry Inc. +nexus + +// nfl : 2015-07-23 NFL Reg Ops LLC +nfl + +// ngo : 2014-03-06 Public Interest Registry +ngo + +// nhk : 2014-02-13 Japan Broadcasting Corporation (NHK) +nhk + +// nico : 2014-12-04 DWANGO Co., Ltd. +nico + +// nike : 2015-07-23 NIKE, Inc. +nike + +// nikon : 2015-05-21 NIKON CORPORATION +nikon + +// ninja : 2013-11-07 Dog Beach, LLC +ninja + +// nissan : 2014-03-27 NISSAN MOTOR CO., LTD. +nissan + +// nissay : 2015-10-29 Nippon Life Insurance Company +nissay + +// nokia : 2015-01-08 Nokia Corporation +nokia + +// northwesternmutual : 2015-06-18 Northwestern Mutual Registry, LLC +northwesternmutual + +// norton : 2014-12-04 NortonLifeLock Inc. +norton + +// now : 2015-06-25 Amazon Registry Services, Inc. +now + +// nowruz : 2014-09-04 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. +nowruz + +// nowtv : 2015-05-14 Starbucks (HK) Limited +nowtv + +// nra : 2014-05-22 NRA Holdings Company, INC. +nra + +// nrw : 2013-11-21 Minds + Machines GmbH +nrw + +// ntt : 2014-10-31 NIPPON TELEGRAPH AND TELEPHONE CORPORATION +ntt + +// nyc : 2014-01-23 The City of New York by and through the New York City Department of Information Technology & Telecommunications +nyc + +// obi : 2014-09-25 OBI Group Holding SE & Co. KGaA +obi + +// observer : 2015-04-30 Dog Beach, LLC +observer + +// off : 2015-07-23 Johnson Shareholdings, Inc. +off + +// office : 2015-03-12 Microsoft Corporation +office + +// okinawa : 2013-12-05 BRregistry, Inc. +okinawa + +// olayan : 2015-05-14 Crescent Holding GmbH +olayan + +// olayangroup : 2015-05-14 Crescent Holding GmbH +olayangroup + +// oldnavy : 2015-07-31 The Gap, Inc. +oldnavy + +// ollo : 2015-06-04 Dish DBS Corporation +ollo + +// omega : 2015-01-08 The Swatch Group Ltd +omega + +// one : 2014-11-07 One.com A/S +one + +// ong : 2014-03-06 Public Interest Registry +ong + +// onl : 2013-09-16 iRegistry GmbH +onl + +// online : 2015-01-15 DotOnline Inc. +online + +// onyourside : 2015-07-23 Nationwide Mutual Insurance Company +onyourside + +// ooo : 2014-01-09 INFIBEAM AVENUES LIMITED +ooo + +// open : 2015-07-31 American Express Travel Related Services Company, Inc. +open + +// oracle : 2014-06-19 Oracle Corporation +oracle + +// orange : 2015-03-12 Orange Brand Services Limited +orange + +// organic : 2014-03-27 Afilias Limited +organic + +// origins : 2015-10-01 The Estée Lauder Companies Inc. +origins + +// osaka : 2014-09-04 Osaka Registry Co., Ltd. +osaka + +// otsuka : 2013-10-11 Otsuka Holdings Co., Ltd. +otsuka + +// ott : 2015-06-04 Dish DBS Corporation +ott + +// ovh : 2014-01-16 MédiaBC +ovh + +// page : 2014-12-04 Charleston Road Registry Inc. +page + +// panasonic : 2015-07-30 Panasonic Corporation +panasonic + +// paris : 2014-01-30 City of Paris +paris + +// pars : 2014-09-04 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. +pars + +// partners : 2013-12-05 Binky Moon, LLC +partners + +// parts : 2013-12-05 Binky Moon, LLC +parts + +// party : 2014-09-11 Blue Sky Registry Limited +party + +// passagens : 2015-03-05 Travel Reservations SRL +passagens + +// pay : 2015-08-27 Amazon Registry Services, Inc. +pay + +// pccw : 2015-05-14 PCCW Enterprises Limited +pccw + +// pet : 2015-05-07 Afilias Limited +pet + +// pfizer : 2015-09-11 Pfizer Inc. +pfizer + +// pharmacy : 2014-06-19 National Association of Boards of Pharmacy +pharmacy + +// phd : 2016-07-28 Charleston Road Registry Inc. +phd + +// philips : 2014-11-07 Koninklijke Philips N.V. +philips + +// phone : 2016-06-02 Dish DBS Corporation +phone + +// photo : 2013-11-14 UNR Corp. +photo + +// photography : 2013-09-20 Binky Moon, LLC +photography + +// photos : 2013-10-17 Binky Moon, LLC +photos + +// physio : 2014-05-01 PhysBiz Pty Ltd +physio + +// pics : 2013-11-14 UNR Corp. +pics + +// pictet : 2014-06-26 Pictet Europe S.A. +pictet + +// pictures : 2014-03-06 Binky Moon, LLC +pictures + +// pid : 2015-01-08 Top Level Spectrum, Inc. +pid + +// pin : 2014-12-18 Amazon Registry Services, Inc. +pin + +// ping : 2015-06-11 Ping Registry Provider, Inc. +ping + +// pink : 2013-10-01 Afilias Limited +pink + +// pioneer : 2015-07-16 Pioneer Corporation +pioneer + +// pizza : 2014-06-26 Binky Moon, LLC +pizza + +// place : 2014-04-24 Binky Moon, LLC +place + +// play : 2015-03-05 Charleston Road Registry Inc. +play + +// playstation : 2015-07-02 Sony Interactive Entertainment Inc. +playstation + +// plumbing : 2013-09-10 Binky Moon, LLC +plumbing + +// plus : 2015-02-05 Binky Moon, LLC +plus + +// pnc : 2015-07-02 PNC Domain Co., LLC +pnc + +// pohl : 2014-06-23 Deutsche Vermögensberatung Aktiengesellschaft DVAG +pohl + +// poker : 2014-07-03 Afilias Limited +poker + +// politie : 2015-08-20 Politie Nederland +politie + +// porn : 2014-10-16 ICM Registry PN LLC +porn + +// pramerica : 2015-07-30 Prudential Financial, Inc. +pramerica + +// praxi : 2013-12-05 Praxi S.p.A. +praxi + +// press : 2014-04-03 DotPress Inc. +press + +// prime : 2015-06-25 Amazon Registry Services, Inc. +prime + +// prod : 2014-01-23 Charleston Road Registry Inc. +prod + +// productions : 2013-12-05 Binky Moon, LLC +productions + +// prof : 2014-07-24 Charleston Road Registry Inc. +prof + +// progressive : 2015-07-23 Progressive Casualty Insurance Company +progressive + +// promo : 2014-12-18 Afilias Limited +promo + +// properties : 2013-12-05 Binky Moon, LLC +properties + +// property : 2014-05-22 UNR Corp. +property + +// protection : 2015-04-23 XYZ.COM LLC +protection + +// pru : 2015-07-30 Prudential Financial, Inc. +pru + +// prudential : 2015-07-30 Prudential Financial, Inc. +prudential + +// pub : 2013-12-12 Dog Beach, LLC +pub + +// pwc : 2015-10-29 PricewaterhouseCoopers LLP +pwc + +// qpon : 2013-11-14 dotCOOL, Inc. +qpon + +// quebec : 2013-12-19 PointQuébec Inc +quebec + +// quest : 2015-03-26 XYZ.COM LLC +quest + +// qvc : 2015-07-30 QVC, Inc. +qvc + +// racing : 2014-12-04 Premier Registry Limited +racing + +// radio : 2016-07-21 European Broadcasting Union (EBU) +radio + +// raid : 2015-07-23 Johnson Shareholdings, Inc. +raid + +// read : 2014-12-18 Amazon Registry Services, Inc. +read + +// realestate : 2015-09-11 dotRealEstate LLC +realestate + +// realtor : 2014-05-29 Real Estate Domains LLC +realtor + +// realty : 2015-03-19 Dog Beach, LLC +realty + +// recipes : 2013-10-17 Binky Moon, LLC +recipes + +// red : 2013-11-07 Afilias Limited +red + +// redstone : 2014-10-31 Redstone Haute Couture Co., Ltd. +redstone + +// redumbrella : 2015-03-26 Travelers TLD, LLC +redumbrella + +// rehab : 2014-03-06 Dog Beach, LLC +rehab + +// reise : 2014-03-13 Binky Moon, LLC +reise + +// reisen : 2014-03-06 Binky Moon, LLC +reisen + +// reit : 2014-09-04 National Association of Real Estate Investment Trusts, Inc. +reit + +// reliance : 2015-04-02 Reliance Industries Limited +reliance + +// ren : 2013-12-12 ZDNS International Limited +ren + +// rent : 2014-12-04 XYZ.COM LLC +rent + +// rentals : 2013-12-05 Binky Moon, LLC +rentals + +// repair : 2013-11-07 Binky Moon, LLC +repair + +// report : 2013-12-05 Binky Moon, LLC +report + +// republican : 2014-03-20 Dog Beach, LLC +republican + +// rest : 2013-12-19 Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable +rest + +// restaurant : 2014-07-03 Binky Moon, LLC +restaurant + +// review : 2014-11-20 dot Review Limited +review + +// reviews : 2013-09-13 Dog Beach, LLC +reviews + +// rexroth : 2015-06-18 Robert Bosch GMBH +rexroth + +// rich : 2013-11-21 iRegistry GmbH +rich + +// richardli : 2015-05-14 Pacific Century Asset Management (HK) Limited +richardli + +// ricoh : 2014-11-20 Ricoh Company, Ltd. +ricoh + +// ril : 2015-04-02 Reliance Industries Limited +ril + +// rio : 2014-02-27 Empresa Municipal de Informática SA - IPLANRIO +rio + +// rip : 2014-07-10 Dog Beach, LLC +rip + +// rmit : 2015-11-19 Royal Melbourne Institute of Technology +rmit + +// rocher : 2014-12-18 Ferrero Trading Lux S.A. +rocher + +// rocks : 2013-11-14 Dog Beach, LLC +rocks + +// rodeo : 2013-12-19 Minds + Machines Group Limited +rodeo + +// rogers : 2015-08-06 Rogers Communications Canada Inc. +rogers + +// room : 2014-12-18 Amazon Registry Services, Inc. +room + +// rsvp : 2014-05-08 Charleston Road Registry Inc. +rsvp + +// rugby : 2016-12-15 World Rugby Strategic Developments Limited +rugby + +// ruhr : 2013-10-02 regiodot GmbH & Co. KG +ruhr + +// run : 2015-03-19 Binky Moon, LLC +run + +// rwe : 2015-04-02 RWE AG +rwe + +// ryukyu : 2014-01-09 BRregistry, Inc. +ryukyu + +// saarland : 2013-12-12 dotSaarland GmbH +saarland + +// safe : 2014-12-18 Amazon Registry Services, Inc. +safe + +// safety : 2015-01-08 Safety Registry Services, LLC. +safety + +// sakura : 2014-12-18 SAKURA Internet Inc. +sakura + +// sale : 2014-10-16 Dog Beach, LLC +sale + +// salon : 2014-12-11 Binky Moon, LLC +salon + +// samsclub : 2015-07-31 Wal-Mart Stores, Inc. +samsclub + +// samsung : 2014-04-03 SAMSUNG SDS CO., LTD +samsung + +// sandvik : 2014-11-13 Sandvik AB +sandvik + +// sandvikcoromant : 2014-11-07 Sandvik AB +sandvikcoromant + +// sanofi : 2014-10-09 Sanofi +sanofi + +// sap : 2014-03-27 SAP AG +sap + +// sarl : 2014-07-03 Binky Moon, LLC +sarl + +// sas : 2015-04-02 Research IP LLC +sas + +// save : 2015-06-25 Amazon Registry Services, Inc. +save + +// saxo : 2014-10-31 Saxo Bank A/S +saxo + +// sbi : 2015-03-12 STATE BANK OF INDIA +sbi + +// sbs : 2014-11-07 SPECIAL BROADCASTING SERVICE CORPORATION +sbs + +// sca : 2014-03-13 SVENSKA CELLULOSA AKTIEBOLAGET SCA (publ) +sca + +// scb : 2014-02-20 The Siam Commercial Bank Public Company Limited ("SCB") +scb + +// schaeffler : 2015-08-06 Schaeffler Technologies AG & Co. KG +schaeffler + +// schmidt : 2014-04-03 SCHMIDT GROUPE S.A.S. +schmidt + +// scholarships : 2014-04-24 Scholarships.com, LLC +scholarships + +// school : 2014-12-18 Binky Moon, LLC +school + +// schule : 2014-03-06 Binky Moon, LLC +schule + +// schwarz : 2014-09-18 Schwarz Domains und Services GmbH & Co. KG +schwarz + +// science : 2014-09-11 dot Science Limited +science + +// scjohnson : 2015-07-23 Johnson Shareholdings, Inc. +scjohnson + +// scot : 2014-01-23 Dot Scot Registry Limited +scot + +// search : 2016-06-09 Charleston Road Registry Inc. +search + +// seat : 2014-05-22 SEAT, S.A. (Sociedad Unipersonal) +seat + +// secure : 2015-08-27 Amazon Registry Services, Inc. +secure + +// security : 2015-05-14 XYZ.COM LLC +security + +// seek : 2014-12-04 Seek Limited +seek + +// select : 2015-10-08 Registry Services, LLC +select + +// sener : 2014-10-24 Sener Ingeniería y Sistemas, S.A. +sener + +// services : 2014-02-27 Binky Moon, LLC +services + +// ses : 2015-07-23 SES +ses + +// seven : 2015-08-06 Seven West Media Ltd +seven + +// sew : 2014-07-17 SEW-EURODRIVE GmbH & Co KG +sew + +// sex : 2014-11-13 ICM Registry SX LLC +sex + +// sexy : 2013-09-11 UNR Corp. +sexy + +// sfr : 2015-08-13 Societe Francaise du Radiotelephone - SFR +sfr + +// shangrila : 2015-09-03 Shangri‐La International Hotel Management Limited +shangrila + +// sharp : 2014-05-01 Sharp Corporation +sharp + +// shaw : 2015-04-23 Shaw Cablesystems G.P. +shaw + +// shell : 2015-07-30 Shell Information Technology International Inc +shell + +// shia : 2014-09-04 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. +shia + +// shiksha : 2013-11-14 Afilias Limited +shiksha + +// shoes : 2013-10-02 Binky Moon, LLC +shoes + +// shop : 2016-04-08 GMO Registry, Inc. +shop + +// shopping : 2016-03-31 Binky Moon, LLC +shopping + +// shouji : 2015-01-08 Beijing Qihu Keji Co., Ltd. +shouji + +// show : 2015-03-05 Binky Moon, LLC +show + +// showtime : 2015-08-06 CBS Domains Inc. +showtime + +// silk : 2015-06-25 Amazon Registry Services, Inc. +silk + +// sina : 2015-03-12 Sina Corporation +sina + +// singles : 2013-08-27 Binky Moon, LLC +singles + +// site : 2015-01-15 DotSite Inc. +site + +// ski : 2015-04-09 Afilias Limited +ski + +// skin : 2015-01-15 XYZ.COM LLC +skin + +// sky : 2014-06-19 Sky International AG +sky + +// skype : 2014-12-18 Microsoft Corporation +skype + +// sling : 2015-07-30 DISH Technologies L.L.C. +sling + +// smart : 2015-07-09 Smart Communications, Inc. (SMART) +smart + +// smile : 2014-12-18 Amazon Registry Services, Inc. +smile + +// sncf : 2015-02-19 Société Nationale des Chemins de fer Francais S N C F +sncf + +// soccer : 2015-03-26 Binky Moon, LLC +soccer + +// social : 2013-11-07 Dog Beach, LLC +social + +// softbank : 2015-07-02 SoftBank Group Corp. +softbank + +// software : 2014-03-20 Dog Beach, LLC +software + +// sohu : 2013-12-19 Sohu.com Limited +sohu + +// solar : 2013-11-07 Binky Moon, LLC +solar + +// solutions : 2013-11-07 Binky Moon, LLC +solutions + +// song : 2015-02-26 Amazon Registry Services, Inc. +song + +// sony : 2015-01-08 Sony Corporation +sony + +// soy : 2014-01-23 Charleston Road Registry Inc. +soy + +// spa : 2019-09-19 Asia Spa and Wellness Promotion Council Limited +spa + +// space : 2014-04-03 DotSpace Inc. +space + +// sport : 2017-11-16 Global Association of International Sports Federations (GAISF) +sport + +// spot : 2015-02-26 Amazon Registry Services, Inc. +spot + +// spreadbetting : 2014-12-11 Dotspreadbetting Registry Limited +spreadbetting + +// srl : 2015-05-07 InterNetX, Corp +srl + +// stada : 2014-11-13 STADA Arzneimittel AG +stada + +// staples : 2015-07-30 Staples, Inc. +staples + +// star : 2015-01-08 Star India Private Limited +star + +// statebank : 2015-03-12 STATE BANK OF INDIA +statebank + +// statefarm : 2015-07-30 State Farm Mutual Automobile Insurance Company +statefarm + +// stc : 2014-10-09 Saudi Telecom Company +stc + +// stcgroup : 2014-10-09 Saudi Telecom Company +stcgroup + +// stockholm : 2014-12-18 Stockholms kommun +stockholm + +// storage : 2014-12-22 XYZ.COM LLC +storage + +// store : 2015-04-09 DotStore Inc. +store + +// stream : 2016-01-08 dot Stream Limited +stream + +// studio : 2015-02-11 Dog Beach, LLC +studio + +// study : 2014-12-11 OPEN UNIVERSITIES AUSTRALIA PTY LTD +study + +// style : 2014-12-04 Binky Moon, LLC +style + +// sucks : 2014-12-22 Vox Populi Registry Ltd. +sucks + +// supplies : 2013-12-19 Binky Moon, LLC +supplies + +// supply : 2013-12-19 Binky Moon, LLC +supply + +// support : 2013-10-24 Binky Moon, LLC +support + +// surf : 2014-01-09 Minds + Machines Group Limited +surf + +// surgery : 2014-03-20 Binky Moon, LLC +surgery + +// suzuki : 2014-02-20 SUZUKI MOTOR CORPORATION +suzuki + +// swatch : 2015-01-08 The Swatch Group Ltd +swatch + +// swiftcover : 2015-07-23 Swiftcover Insurance Services Limited +swiftcover + +// swiss : 2014-10-16 Swiss Confederation +swiss + +// sydney : 2014-09-18 State of New South Wales, Department of Premier and Cabinet +sydney + +// systems : 2013-11-07 Binky Moon, LLC +systems + +// tab : 2014-12-04 Tabcorp Holdings Limited +tab + +// taipei : 2014-07-10 Taipei City Government +taipei + +// talk : 2015-04-09 Amazon Registry Services, Inc. +talk + +// taobao : 2015-01-15 Alibaba Group Holding Limited +taobao + +// target : 2015-07-31 Target Domain Holdings, LLC +target + +// tatamotors : 2015-03-12 Tata Motors Ltd +tatamotors + +// tatar : 2014-04-24 Limited Liability Company "Coordination Center of Regional Domain of Tatarstan Republic" +tatar + +// tattoo : 2013-08-30 UNR Corp. +tattoo + +// tax : 2014-03-20 Binky Moon, LLC +tax + +// taxi : 2015-03-19 Binky Moon, LLC +taxi + +// tci : 2014-09-12 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. +tci + +// tdk : 2015-06-11 TDK Corporation +tdk + +// team : 2015-03-05 Binky Moon, LLC +team + +// tech : 2015-01-30 Personals TLD Inc. +tech + +// technology : 2013-09-13 Binky Moon, LLC +technology + +// temasek : 2014-08-07 Temasek Holdings (Private) Limited +temasek + +// tennis : 2014-12-04 Binky Moon, LLC +tennis + +// teva : 2015-07-02 Teva Pharmaceutical Industries Limited +teva + +// thd : 2015-04-02 Home Depot Product Authority, LLC +thd + +// theater : 2015-03-19 Binky Moon, LLC +theater + +// theatre : 2015-05-07 XYZ.COM LLC +theatre + +// tiaa : 2015-07-23 Teachers Insurance and Annuity Association of America +tiaa + +// tickets : 2015-02-05 Accent Media Limited +tickets + +// tienda : 2013-11-14 Binky Moon, LLC +tienda + +// tiffany : 2015-01-30 Tiffany and Company +tiffany + +// tips : 2013-09-20 Binky Moon, LLC +tips + +// tires : 2014-11-07 Binky Moon, LLC +tires + +// tirol : 2014-04-24 punkt Tirol GmbH +tirol + +// tjmaxx : 2015-07-16 The TJX Companies, Inc. +tjmaxx + +// tjx : 2015-07-16 The TJX Companies, Inc. +tjx + +// tkmaxx : 2015-07-16 The TJX Companies, Inc. +tkmaxx + +// tmall : 2015-01-15 Alibaba Group Holding Limited +tmall + +// today : 2013-09-20 Binky Moon, LLC +today + +// tokyo : 2013-11-13 GMO Registry, Inc. +tokyo + +// tools : 2013-11-21 Binky Moon, LLC +tools + +// top : 2014-03-20 .TOP Registry +top + +// toray : 2014-12-18 Toray Industries, Inc. +toray + +// toshiba : 2014-04-10 TOSHIBA Corporation +toshiba + +// total : 2015-08-06 Total SA +total + +// tours : 2015-01-22 Binky Moon, LLC +tours + +// town : 2014-03-06 Binky Moon, LLC +town + +// toyota : 2015-04-23 TOYOTA MOTOR CORPORATION +toyota + +// toys : 2014-03-06 Binky Moon, LLC +toys + +// trade : 2014-01-23 Elite Registry Limited +trade + +// trading : 2014-12-11 Dottrading Registry Limited +trading + +// training : 2013-11-07 Binky Moon, LLC +training + +// travel : 2015-10-09 Dog Beach, LLC +travel + +// travelchannel : 2015-07-02 Lifestyle Domain Holdings, Inc. +travelchannel + +// travelers : 2015-03-26 Travelers TLD, LLC +travelers + +// travelersinsurance : 2015-03-26 Travelers TLD, LLC +travelersinsurance + +// trust : 2014-10-16 UNR Corp. +trust + +// trv : 2015-03-26 Travelers TLD, LLC +trv + +// tube : 2015-06-11 Latin American Telecom LLC +tube + +// tui : 2014-07-03 TUI AG +tui + +// tunes : 2015-02-26 Amazon Registry Services, Inc. +tunes + +// tushu : 2014-12-18 Amazon Registry Services, Inc. +tushu + +// tvs : 2015-02-19 T V SUNDRAM IYENGAR & SONS LIMITED +tvs + +// ubank : 2015-08-20 National Australia Bank Limited +ubank + +// ubs : 2014-12-11 UBS AG +ubs + +// unicom : 2015-10-15 China United Network Communications Corporation Limited +unicom + +// university : 2014-03-06 Binky Moon, LLC +university + +// uno : 2013-09-11 DotSite Inc. +uno + +// uol : 2014-05-01 UBN INTERNET LTDA. +uol + +// ups : 2015-06-25 UPS Market Driver, Inc. +ups + +// vacations : 2013-12-05 Binky Moon, LLC +vacations + +// vana : 2014-12-11 Lifestyle Domain Holdings, Inc. +vana + +// vanguard : 2015-09-03 The Vanguard Group, Inc. +vanguard + +// vegas : 2014-01-16 Dot Vegas, Inc. +vegas + +// ventures : 2013-08-27 Binky Moon, LLC +ventures + +// verisign : 2015-08-13 VeriSign, Inc. +verisign + +// versicherung : 2014-03-20 tldbox GmbH +versicherung + +// vet : 2014-03-06 Dog Beach, LLC +vet + +// viajes : 2013-10-17 Binky Moon, LLC +viajes + +// video : 2014-10-16 Dog Beach, LLC +video + +// vig : 2015-05-14 VIENNA INSURANCE GROUP AG Wiener Versicherung Gruppe +vig + +// viking : 2015-04-02 Viking River Cruises (Bermuda) Ltd. +viking + +// villas : 2013-12-05 Binky Moon, LLC +villas + +// vin : 2015-06-18 Binky Moon, LLC +vin + +// vip : 2015-01-22 Minds + Machines Group Limited +vip + +// virgin : 2014-09-25 Virgin Enterprises Limited +virgin + +// visa : 2015-07-30 Visa Worldwide Pte. Limited +visa + +// vision : 2013-12-05 Binky Moon, LLC +vision + +// viva : 2014-11-07 Saudi Telecom Company +viva + +// vivo : 2015-07-31 Telefonica Brasil S.A. +vivo + +// vlaanderen : 2014-02-06 DNS.be vzw +vlaanderen + +// vodka : 2013-12-19 Minds + Machines Group Limited +vodka + +// volkswagen : 2015-05-14 Volkswagen Group of America Inc. +volkswagen + +// volvo : 2015-11-12 Volvo Holding Sverige Aktiebolag +volvo + +// vote : 2013-11-21 Monolith Registry LLC +vote + +// voting : 2013-11-13 Valuetainment Corp. +voting + +// voto : 2013-11-21 Monolith Registry LLC +voto + +// voyage : 2013-08-27 Binky Moon, LLC +voyage + +// vuelos : 2015-03-05 Travel Reservations SRL +vuelos + +// wales : 2014-05-08 Nominet UK +wales + +// walmart : 2015-07-31 Wal-Mart Stores, Inc. +walmart + +// walter : 2014-11-13 Sandvik AB +walter + +// wang : 2013-10-24 Zodiac Wang Limited +wang + +// wanggou : 2014-12-18 Amazon Registry Services, Inc. +wanggou + +// watch : 2013-11-14 Binky Moon, LLC +watch + +// watches : 2014-12-22 Richemont DNS Inc. +watches + +// weather : 2015-01-08 International Business Machines Corporation +weather + +// weatherchannel : 2015-03-12 International Business Machines Corporation +weatherchannel + +// webcam : 2014-01-23 dot Webcam Limited +webcam + +// weber : 2015-06-04 Saint-Gobain Weber SA +weber + +// website : 2014-04-03 DotWebsite Inc. +website + +// wedding : 2014-04-24 Minds + Machines Group Limited +wedding + +// weibo : 2015-03-05 Sina Corporation +weibo + +// weir : 2015-01-29 Weir Group IP Limited +weir + +// whoswho : 2014-02-20 Who's Who Registry +whoswho + +// wien : 2013-10-28 punkt.wien GmbH +wien + +// wiki : 2013-11-07 Top Level Design, LLC +wiki + +// williamhill : 2014-03-13 William Hill Organization Limited +williamhill + +// win : 2014-11-20 First Registry Limited +win + +// windows : 2014-12-18 Microsoft Corporation +windows + +// wine : 2015-06-18 Binky Moon, LLC +wine + +// winners : 2015-07-16 The TJX Companies, Inc. +winners + +// wme : 2014-02-13 William Morris Endeavor Entertainment, LLC +wme + +// wolterskluwer : 2015-08-06 Wolters Kluwer N.V. +wolterskluwer + +// woodside : 2015-07-09 Woodside Petroleum Limited +woodside + +// work : 2013-12-19 Minds + Machines Group Limited +work + +// works : 2013-11-14 Binky Moon, LLC +works + +// world : 2014-06-12 Binky Moon, LLC +world + +// wow : 2015-10-08 Amazon Registry Services, Inc. +wow + +// wtc : 2013-12-19 World Trade Centers Association, Inc. +wtc + +// wtf : 2014-03-06 Binky Moon, LLC +wtf + +// xbox : 2014-12-18 Microsoft Corporation +xbox + +// xerox : 2014-10-24 Xerox DNHC LLC +xerox + +// xfinity : 2015-07-09 Comcast IP Holdings I, LLC +xfinity + +// xihuan : 2015-01-08 Beijing Qihu Keji Co., Ltd. +xihuan + +// xin : 2014-12-11 Elegant Leader Limited +xin + +// xn--11b4c3d : 2015-01-15 VeriSign Sarl +कॉम + +// xn--1ck2e1b : 2015-02-26 Amazon Registry Services, Inc. +セール + +// xn--1qqw23a : 2014-01-09 Guangzhou YU Wei Information Technology Co., Ltd. +佛山 + +// xn--30rr7y : 2014-06-12 Excellent First Limited +慈善 + +// xn--3bst00m : 2013-09-13 Eagle Horizon Limited +集团 + +// xn--3ds443g : 2013-09-08 TLD REGISTRY LIMITED OY +在线 + +// xn--3oq18vl8pn36a : 2015-07-02 Volkswagen (China) Investment Co., Ltd. +大众汽车 + +// xn--3pxu8k : 2015-01-15 VeriSign Sarl +点看 + +// xn--42c2d9a : 2015-01-15 VeriSign Sarl +คอม + +// xn--45q11c : 2013-11-21 Zodiac Gemini Ltd +八卦 + +// xn--4gbrim : 2013-10-04 Fans TLD Limited +موقع + +// xn--55qw42g : 2013-11-08 China Organizational Name Administration Center +公益 + +// xn--55qx5d : 2013-11-14 China Internet Network Information Center (CNNIC) +公司 + +// xn--5su34j936bgsg : 2015-09-03 Shangri‐La International Hotel Management Limited +香格里拉 + +// xn--5tzm5g : 2014-12-22 Global Website TLD Asia Limited +网站 + +// xn--6frz82g : 2013-09-23 Afilias Limited +移动 + +// xn--6qq986b3xl : 2013-09-13 Tycoon Treasure Limited +我爱你 + +// xn--80adxhks : 2013-12-19 Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID) +москва + +// xn--80aqecdr1a : 2015-10-21 Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication) +католик + +// xn--80asehdb : 2013-07-14 CORE Association +онлайн + +// xn--80aswg : 2013-07-14 CORE Association +сайт + +// xn--8y0a063a : 2015-03-26 China United Network Communications Corporation Limited +联通 + +// xn--9dbq2a : 2015-01-15 VeriSign Sarl +קום + +// xn--9et52u : 2014-06-12 RISE VICTORY LIMITED +时尚 + +// xn--9krt00a : 2015-03-12 Sina Corporation +微博 + +// xn--b4w605ferd : 2014-08-07 Temasek Holdings (Private) Limited +淡马锡 + +// xn--bck1b9a5dre4c : 2015-02-26 Amazon Registry Services, Inc. +ファッション + +// xn--c1avg : 2013-11-14 Public Interest Registry +орг + +// xn--c2br7g : 2015-01-15 VeriSign Sarl +नेट + +// xn--cck2b3b : 2015-02-26 Amazon Registry Services, Inc. +ストア + +// xn--cckwcxetd : 2019-12-19 Amazon Registry Services, Inc. +アマゾン + +// xn--cg4bki : 2013-09-27 SAMSUNG SDS CO., LTD +삼성 + +// xn--czr694b : 2014-01-16 Internet DotTrademark Organisation Limited +商标 + +// xn--czrs0t : 2013-12-19 Binky Moon, LLC +商店 + +// xn--czru2d : 2013-11-21 Zodiac Aquarius Limited +商城 + +// xn--d1acj3b : 2013-11-20 The Foundation for Network Initiatives “The Smart Internet” +дети + +// xn--eckvdtc9d : 2014-12-18 Amazon Registry Services, Inc. +ポイント + +// xn--efvy88h : 2014-08-22 Guangzhou YU Wei Information Technology Co., Ltd. +新闻 + +// xn--fct429k : 2015-04-09 Amazon Registry Services, Inc. +家電 + +// xn--fhbei : 2015-01-15 VeriSign Sarl +كوم + +// xn--fiq228c5hs : 2013-09-08 TLD REGISTRY LIMITED OY +中文网 + +// xn--fiq64b : 2013-10-14 CITIC Group Corporation +中信 + +// xn--fjq720a : 2014-05-22 Binky Moon, LLC +娱乐 + +// xn--flw351e : 2014-07-31 Charleston Road Registry Inc. +谷歌 + +// xn--fzys8d69uvgm : 2015-05-14 PCCW Enterprises Limited +電訊盈科 + +// xn--g2xx48c : 2015-01-30 Nawang Heli(Xiamen) Network Service Co., LTD. +购物 + +// xn--gckr3f0f : 2015-02-26 Amazon Registry Services, Inc. +クラウド + +// xn--gk3at1e : 2015-10-08 Amazon Registry Services, Inc. +通販 + +// xn--hxt814e : 2014-05-15 Zodiac Taurus Limited +网店 + +// xn--i1b6b1a6a2e : 2013-11-14 Public Interest Registry +संगठन + +// xn--imr513n : 2014-12-11 Internet DotTrademark Organisation Limited +餐厅 + +// xn--io0a7i : 2013-11-14 China Internet Network Information Center (CNNIC) +网络 + +// xn--j1aef : 2015-01-15 VeriSign Sarl +ком + +// xn--jlq480n2rg : 2019-12-19 Amazon Registry Services, Inc. +亚马逊 + +// xn--jlq61u9w7b : 2015-01-08 Nokia Corporation +诺基亚 + +// xn--jvr189m : 2015-02-26 Amazon Registry Services, Inc. +食品 + +// xn--kcrx77d1x4a : 2014-11-07 Koninklijke Philips N.V. +飞利浦 + +// xn--kput3i : 2014-02-13 Beijing RITT-Net Technology Development Co., Ltd +手机 + +// xn--mgba3a3ejt : 2014-11-20 Aramco Services Company +ارامكو + +// xn--mgba7c0bbn0a : 2015-05-14 Crescent Holding GmbH +العليان + +// xn--mgbaakc7dvf : 2015-09-03 Emirates Telecommunications Corporation (trading as Etisalat) +اتصالات + +// xn--mgbab2bd : 2013-10-31 CORE Association +بازار + +// xn--mgbca7dzdo : 2015-07-30 Abu Dhabi Systems and Information Centre +ابوظبي + +// xn--mgbi4ecexp : 2015-10-21 Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication) +كاثوليك + +// xn--mgbt3dhd : 2014-09-04 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. +همراه + +// xn--mk1bu44c : 2015-01-15 VeriSign Sarl +닷컴 + +// xn--mxtq1m : 2014-03-06 Net-Chinese Co., Ltd. +政府 + +// xn--ngbc5azd : 2013-07-13 International Domain Registry Pty. Ltd. +شبكة + +// xn--ngbe9e0a : 2014-12-04 Kuwait Finance House +بيتك + +// xn--ngbrx : 2015-11-12 League of Arab States +عرب + +// xn--nqv7f : 2013-11-14 Public Interest Registry +机构 + +// xn--nqv7fs00ema : 2013-11-14 Public Interest Registry +组织机构 + +// xn--nyqy26a : 2014-11-07 Stable Tone Limited +健康 + +// xn--otu796d : 2017-08-06 Jiang Yu Liang Cai Technology Company Limited +招聘 + +// xn--p1acf : 2013-12-12 Rusnames Limited +рус + +// xn--pssy2u : 2015-01-15 VeriSign Sarl +大拿 + +// xn--q9jyb4c : 2013-09-17 Charleston Road Registry Inc. +みんな + +// xn--qcka1pmc : 2014-07-31 Charleston Road Registry Inc. +グーグル + +// xn--rhqv96g : 2013-09-11 Stable Tone Limited +世界 + +// xn--rovu88b : 2015-02-26 Amazon Registry Services, Inc. +書籍 + +// xn--ses554g : 2014-01-16 KNET Co., Ltd. +网址 + +// xn--t60b56a : 2015-01-15 VeriSign Sarl +닷넷 + +// xn--tckwe : 2015-01-15 VeriSign Sarl +コム + +// xn--tiq49xqyj : 2015-10-21 Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication) +天主教 + +// xn--unup4y : 2013-07-14 Binky Moon, LLC +游戏 + +// xn--vermgensberater-ctb : 2014-06-23 Deutsche Vermögensberatung Aktiengesellschaft DVAG +vermögensberater + +// xn--vermgensberatung-pwb : 2014-06-23 Deutsche Vermögensberatung Aktiengesellschaft DVAG +vermögensberatung + +// xn--vhquv : 2013-08-27 Binky Moon, LLC +企业 + +// xn--vuq861b : 2014-10-16 Beijing Tele-info Network Technology Co., Ltd. +信息 + +// xn--w4r85el8fhu5dnra : 2015-04-30 Kerry Trading Co. Limited +嘉里大酒店 + +// xn--w4rs40l : 2015-07-30 Kerry Trading Co. Limited +嘉里 + +// xn--xhq521b : 2013-11-14 Guangzhou YU Wei Information Technology Co., Ltd. +广东 + +// xn--zfr164b : 2013-11-08 China Organizational Name Administration Center +政务 + +// xyz : 2013-12-05 XYZ.COM LLC +xyz + +// yachts : 2014-01-09 XYZ.COM LLC +yachts + +// yahoo : 2015-04-02 Yahoo! Domain Services Inc. +yahoo + +// yamaxun : 2014-12-18 Amazon Registry Services, Inc. +yamaxun + +// yandex : 2014-04-10 Yandex Europe B.V. +yandex + +// yodobashi : 2014-11-20 YODOBASHI CAMERA CO.,LTD. +yodobashi + +// yoga : 2014-05-29 Minds + Machines Group Limited +yoga + +// yokohama : 2013-12-12 GMO Registry, Inc. +yokohama + +// you : 2015-04-09 Amazon Registry Services, Inc. +you + +// youtube : 2014-05-01 Charleston Road Registry Inc. +youtube + +// yun : 2015-01-08 Beijing Qihu Keji Co., Ltd. +yun + +// zappos : 2015-06-25 Amazon Registry Services, Inc. +zappos + +// zara : 2014-11-07 Industria de Diseño Textil, S.A. (INDITEX, S.A.) +zara + +// zero : 2014-12-18 Amazon Registry Services, Inc. +zero + +// zip : 2014-05-08 Charleston Road Registry Inc. +zip + +// zone : 2013-11-14 Binky Moon, LLC +zone + +// zuerich : 2014-11-07 Kanton Zürich (Canton of Zurich) +zuerich + + +// ===END ICANN DOMAINS=== +// ===BEGIN PRIVATE DOMAINS=== +// (Note: these are in alphabetical order by company name) + +// 1GB LLC : https://www.1gb.ua/ +// Submitted by 1GB LLC <noc@1gb.com.ua> +cc.ua +inf.ua +ltd.ua + +// 611coin : https://611project.org/ +611.to + +// Adobe : https://www.adobe.com/ +// Submitted by Ian Boston <boston@adobe.com> +adobeaemcloud.com +adobeaemcloud.net +*.dev.adobeaemcloud.com + +// Agnat sp. z o.o. : https://domena.pl +// Submitted by Przemyslaw Plewa <it-admin@domena.pl> +beep.pl + +// alboto.ca : http://alboto.ca +// Submitted by Anton Avramov <avramov@alboto.ca> +barsy.ca + +// Alces Software Ltd : http://alces-software.com +// Submitted by Mark J. Titorenko <mark.titorenko@alces-software.com> +*.compute.estate +*.alces.network + +// all-inkl.com : https://all-inkl.com +// Submitted by Werner Kaltofen <wk@all-inkl.com> +kasserver.com + +// Altervista: https://www.altervista.org +// Submitted by Carlo Cannas <tech_staff@altervista.it> +altervista.org + +// alwaysdata : https://www.alwaysdata.com +// Submitted by Cyril <admin@alwaysdata.com> +alwaysdata.net + +// Amazon CloudFront : https://aws.amazon.com/cloudfront/ +// Submitted by Donavan Miller <donavanm@amazon.com> +cloudfront.net + +// Amazon Elastic Compute Cloud : https://aws.amazon.com/ec2/ +// Submitted by Luke Wells <psl-maintainers@amazon.com> +*.compute.amazonaws.com +*.compute-1.amazonaws.com +*.compute.amazonaws.com.cn +us-east-1.amazonaws.com + +// Amazon Elastic Beanstalk : https://aws.amazon.com/elasticbeanstalk/ +// Submitted by Luke Wells <psl-maintainers@amazon.com> +cn-north-1.eb.amazonaws.com.cn +cn-northwest-1.eb.amazonaws.com.cn +elasticbeanstalk.com +ap-northeast-1.elasticbeanstalk.com +ap-northeast-2.elasticbeanstalk.com +ap-northeast-3.elasticbeanstalk.com +ap-south-1.elasticbeanstalk.com +ap-southeast-1.elasticbeanstalk.com +ap-southeast-2.elasticbeanstalk.com +ca-central-1.elasticbeanstalk.com +eu-central-1.elasticbeanstalk.com +eu-west-1.elasticbeanstalk.com +eu-west-2.elasticbeanstalk.com +eu-west-3.elasticbeanstalk.com +sa-east-1.elasticbeanstalk.com +us-east-1.elasticbeanstalk.com +us-east-2.elasticbeanstalk.com +us-gov-west-1.elasticbeanstalk.com +us-west-1.elasticbeanstalk.com +us-west-2.elasticbeanstalk.com + +// Amazon Elastic Load Balancing : https://aws.amazon.com/elasticloadbalancing/ +// Submitted by Luke Wells <psl-maintainers@amazon.com> +*.elb.amazonaws.com +*.elb.amazonaws.com.cn + +// Amazon Global Accelerator : https://aws.amazon.com/global-accelerator/ +// Submitted by Daniel Massaguer <psl-maintainers@amazon.com> +awsglobalaccelerator.com + +// Amazon S3 : https://aws.amazon.com/s3/ +// Submitted by Luke Wells <psl-maintainers@amazon.com> +s3.amazonaws.com +s3-ap-northeast-1.amazonaws.com +s3-ap-northeast-2.amazonaws.com +s3-ap-south-1.amazonaws.com +s3-ap-southeast-1.amazonaws.com +s3-ap-southeast-2.amazonaws.com +s3-ca-central-1.amazonaws.com +s3-eu-central-1.amazonaws.com +s3-eu-west-1.amazonaws.com +s3-eu-west-2.amazonaws.com +s3-eu-west-3.amazonaws.com +s3-external-1.amazonaws.com +s3-fips-us-gov-west-1.amazonaws.com +s3-sa-east-1.amazonaws.com +s3-us-gov-west-1.amazonaws.com +s3-us-east-2.amazonaws.com +s3-us-west-1.amazonaws.com +s3-us-west-2.amazonaws.com +s3.ap-northeast-2.amazonaws.com +s3.ap-south-1.amazonaws.com +s3.cn-north-1.amazonaws.com.cn +s3.ca-central-1.amazonaws.com +s3.eu-central-1.amazonaws.com +s3.eu-west-2.amazonaws.com +s3.eu-west-3.amazonaws.com +s3.us-east-2.amazonaws.com +s3.dualstack.ap-northeast-1.amazonaws.com +s3.dualstack.ap-northeast-2.amazonaws.com +s3.dualstack.ap-south-1.amazonaws.com +s3.dualstack.ap-southeast-1.amazonaws.com +s3.dualstack.ap-southeast-2.amazonaws.com +s3.dualstack.ca-central-1.amazonaws.com +s3.dualstack.eu-central-1.amazonaws.com +s3.dualstack.eu-west-1.amazonaws.com +s3.dualstack.eu-west-2.amazonaws.com +s3.dualstack.eu-west-3.amazonaws.com +s3.dualstack.sa-east-1.amazonaws.com +s3.dualstack.us-east-1.amazonaws.com +s3.dualstack.us-east-2.amazonaws.com +s3-website-us-east-1.amazonaws.com +s3-website-us-west-1.amazonaws.com +s3-website-us-west-2.amazonaws.com +s3-website-ap-northeast-1.amazonaws.com +s3-website-ap-southeast-1.amazonaws.com +s3-website-ap-southeast-2.amazonaws.com +s3-website-eu-west-1.amazonaws.com +s3-website-sa-east-1.amazonaws.com +s3-website.ap-northeast-2.amazonaws.com +s3-website.ap-south-1.amazonaws.com +s3-website.ca-central-1.amazonaws.com +s3-website.eu-central-1.amazonaws.com +s3-website.eu-west-2.amazonaws.com +s3-website.eu-west-3.amazonaws.com +s3-website.us-east-2.amazonaws.com + +// Amsterdam Wireless: https://www.amsterdamwireless.nl/ +// Submitted by Imre Jonk <hostmaster@amsterdamwireless.nl> +amsw.nl + +// Amune : https://amune.org/ +// Submitted by Team Amune <cert@amune.org> +t3l3p0rt.net +tele.amune.org + +// Apigee : https://apigee.com/ +// Submitted by Apigee Security Team <security@apigee.com> +apigee.io + +// Appspace : https://www.appspace.com +// Submitted by Appspace Security Team <security@appspace.com> +appspacehosted.com +appspaceusercontent.com + +// Aptible : https://www.aptible.com/ +// Submitted by Thomas Orozco <thomas@aptible.com> +on-aptible.com + +// ASEINet : https://www.aseinet.com/ +// Submitted by Asei SEKIGUCHI <mail@aseinet.com> +user.aseinet.ne.jp +gv.vc +d.gv.vc + +// Asociación Amigos de la Informática "Euskalamiga" : http://encounter.eus/ +// Submitted by Hector Martin <marcan@euskalencounter.org> +user.party.eus + +// Association potager.org : https://potager.org/ +// Submitted by Lunar <jardiniers@potager.org> +pimienta.org +poivron.org +potager.org +sweetpepper.org + +// ASUSTOR Inc. : http://www.asustor.com +// Submitted by Vincent Tseng <vincenttseng@asustor.com> +myasustor.com + +// AVM : https://avm.de +// Submitted by Andreas Weise <a.weise@avm.de> +myfritz.net + +// AW AdvisorWebsites.com Software Inc : https://advisorwebsites.com +// Submitted by James Kennedy <domains@advisorwebsites.com> +*.awdev.ca +*.advisor.ws + +// b-data GmbH : https://www.b-data.io +// Submitted by Olivier Benz <olivier.benz@b-data.ch> +b-data.io + +// backplane : https://www.backplane.io +// Submitted by Anthony Voutas <anthony@backplane.io> +backplaneapp.io + +// Balena : https://www.balena.io +// Submitted by Petros Angelatos <petrosagg@balena.io> +balena-devices.com + +// Banzai Cloud +// Submitted by Janos Matyas <info@banzaicloud.com> +*.banzai.cloud +app.banzaicloud.io +*.backyards.banzaicloud.io + + +// BetaInABox +// Submitted by Adrian <adrian@betainabox.com> +betainabox.com + +// BinaryLane : http://www.binarylane.com +// Submitted by Nathan O'Sullivan <nathan@mammoth.com.au> +bnr.la + +// Blackbaud, Inc. : https://www.blackbaud.com +// Submitted by Paul Crowder <paul.crowder@blackbaud.com> +blackbaudcdn.net + +// Blatech : http://www.blatech.net +// Submitted by Luke Bratch <luke@bratch.co.uk> +of.je + +// Boomla : https://boomla.com +// Submitted by Tibor Halter <thalter@boomla.com> +boomla.net + +// Boxfuse : https://boxfuse.com +// Submitted by Axel Fontaine <axel@boxfuse.com> +boxfuse.io + +// bplaced : https://www.bplaced.net/ +// Submitted by Miroslav Bozic <security@bplaced.net> +square7.ch +bplaced.com +bplaced.de +square7.de +bplaced.net +square7.net + +// BrowserSafetyMark +// Submitted by Dave Tharp <browsersafetymark.io@quicinc.com> +browsersafetymark.io + +// Bytemark Hosting : https://www.bytemark.co.uk +// Submitted by Paul Cammish <paul.cammish@bytemark.co.uk> +uk0.bigv.io +dh.bytemark.co.uk +vm.bytemark.co.uk + +// callidomus : https://www.callidomus.com/ +// Submitted by Marcus Popp <admin@callidomus.com> +mycd.eu + +// Carrd : https://carrd.co +// Submitted by AJ <aj@carrd.co> +carrd.co +crd.co +uwu.ai + +// CentralNic : http://www.centralnic.com/names/domains +// Submitted by registry <gavin.brown@centralnic.com> +ae.org +br.com +cn.com +com.de +com.se +de.com +eu.com +gb.net +hu.net +jp.net +jpn.com +mex.com +ru.com +sa.com +se.net +uk.com +uk.net +us.com +za.bz +za.com + +// No longer operated by CentralNic, these entries should be adopted and/or removed by current operators +// Submitted by Gavin Brown <gavin.brown@centralnic.com> +ar.com +gb.com +hu.com +kr.com +no.com +qc.com +uy.com + +// Africa.com Web Solutions Ltd : https://registry.africa.com +// Submitted by Gavin Brown <gavin.brown@centralnic.com> +africa.com + +// iDOT Services Limited : http://www.domain.gr.com +// Submitted by Gavin Brown <gavin.brown@centralnic.com> +gr.com + +// Radix FZC : http://domains.in.net +// Submitted by Gavin Brown <gavin.brown@centralnic.com> +in.net +web.in + +// US REGISTRY LLC : http://us.org +// Submitted by Gavin Brown <gavin.brown@centralnic.com> +us.org + +// co.com Registry, LLC : https://registry.co.com +// Submitted by Gavin Brown <gavin.brown@centralnic.com> +co.com + +// Roar Domains LLC : https://roar.basketball/ +// Submitted by Gavin Brown <gavin.brown@centralnic.com> +aus.basketball +nz.basketball + +// BRS Media : https://brsmedia.com/ +// Submitted by Gavin Brown <gavin.brown@centralnic.com> +radio.am +radio.fm + +// Globe Hosting SRL : https://www.globehosting.com/ +// Submitted by Gavin Brown <gavin.brown@centralnic.com> +co.ro +shop.ro + +// c.la : http://www.c.la/ +c.la + +// certmgr.org : https://certmgr.org +// Submitted by B. Blechschmidt <hostmaster@certmgr.org> +certmgr.org + +// Civilized Discourse Construction Kit, Inc. : https://www.discourse.org/ +// Submitted by Rishabh Nambiar & Michael Brown <team@discourse.org> +discourse.group +discourse.team + +// ClearVox : http://www.clearvox.nl/ +// Submitted by Leon Rowland <leon@clearvox.nl> +virtueeldomein.nl + +// Clever Cloud : https://www.clever-cloud.com/ +// Submitted by Quentin Adam <noc@clever-cloud.com> +cleverapps.io + +// Clerk : https://www.clerk.dev +// Submitted by Colin Sidoti <colin@clerk.dev> +*.lcl.dev +*.stg.dev + +// Clic2000 : https://clic2000.fr +// Submitted by Mathilde Blanchemanche <mathilde@clic2000.fr> +clic2000.net + +// ClickRising : https://clickrising.com/ +// Submitted by Umut Gumeli <infrastructure-publicsuffixlist@clickrising.com> +clickrising.net + +// Cloud66 : https://www.cloud66.com/ +// Submitted by Khash Sajadi <khash@cloud66.com> +c66.me +cloud66.ws +cloud66.zone + +// CloudAccess.net : https://www.cloudaccess.net/ +// Submitted by Pawel Panek <noc@cloudaccess.net> +jdevcloud.com +wpdevcloud.com +cloudaccess.host +freesite.host +cloudaccess.net + +// cloudControl : https://www.cloudcontrol.com/ +// Submitted by Tobias Wilken <tw@cloudcontrol.com> +cloudcontrolled.com +cloudcontrolapp.com + +// Cloudera, Inc. : https://www.cloudera.com/ +// Submitted by Philip Langdale <security@cloudera.com> +cloudera.site + +// Cloudflare, Inc. : https://www.cloudflare.com/ +// Submitted by Cloudflare Team <publicsuffixlist@cloudflare.com> +pages.dev +trycloudflare.com +workers.dev + +// Clovyr : https://clovyr.io +// Submitted by Patrick Nielsen <patrick@clovyr.io> +wnext.app + +// co.ca : http://registry.co.ca/ +co.ca + +// Co & Co : https://co-co.nl/ +// Submitted by Govert Versluis <govert@co-co.nl> +*.otap.co + +// i-registry s.r.o. : http://www.i-registry.cz/ +// Submitted by Martin Semrad <semrad@i-registry.cz> +co.cz + +// CDN77.com : http://www.cdn77.com +// Submitted by Jan Krpes <jan.krpes@cdn77.com> +c.cdn77.org +cdn77-ssl.net +r.cdn77.net +rsc.cdn77.org +ssl.origin.cdn77-secure.org + +// Cloud DNS Ltd : http://www.cloudns.net +// Submitted by Aleksander Hristov <noc@cloudns.net> +cloudns.asia +cloudns.biz +cloudns.club +cloudns.cc +cloudns.eu +cloudns.in +cloudns.info +cloudns.org +cloudns.pro +cloudns.pw +cloudns.us + +// CNPY : https://cnpy.gdn +// Submitted by Angelo Gladding <angelo@lahacker.net> +cnpy.gdn + +// CoDNS B.V. +co.nl +co.no + +// Combell.com : https://www.combell.com +// Submitted by Thomas Wouters <thomas.wouters@combellgroup.com> +webhosting.be +hosting-cluster.nl + +// Coordination Center for TLD RU and XN--P1AI : https://cctld.ru/en/domains/domens_ru/reserved/ +// Submitted by George Georgievsky <gug@cctld.ru> +ac.ru +edu.ru +gov.ru +int.ru +mil.ru +test.ru + +// COSIMO GmbH : http://www.cosimo.de +// Submitted by Rene Marticke <rmarticke@cosimo.de> +dyn.cosidns.de +dynamisches-dns.de +dnsupdater.de +internet-dns.de +l-o-g-i-n.de +dynamic-dns.info +feste-ip.net +knx-server.net +static-access.net + +// Craynic, s.r.o. : http://www.craynic.com/ +// Submitted by Ales Krajnik <ales.krajnik@craynic.com> +realm.cz + +// Cryptonomic : https://cryptonomic.net/ +// Submitted by Andrew Cady <public-suffix-list@cryptonomic.net> +*.cryptonomic.net + +// Cupcake : https://cupcake.io/ +// Submitted by Jonathan Rudenberg <jonathan@cupcake.io> +cupcake.is + +// Curv UG : https://curv-labs.de/ +// Submitted by Marvin Wiesner <Marvin@curv-labs.de> +curv.dev + +// Customer OCI - Oracle Dyn https://cloud.oracle.com/home https://dyn.com/dns/ +// Submitted by Gregory Drake <support@dyn.com> +// Note: This is intended to also include customer-oci.com due to wildcards implicitly including the current label +*.customer-oci.com +*.oci.customer-oci.com +*.ocp.customer-oci.com +*.ocs.customer-oci.com + +// cyon GmbH : https://www.cyon.ch/ +// Submitted by Dominic Luechinger <dol@cyon.ch> +cyon.link +cyon.site + +// Danger Science Group: https://dangerscience.com/ +// Submitted by Skylar MacDonald <skylar@dangerscience.com> +fnwk.site +folionetwork.site +platform0.app + +// Daplie, Inc : https://daplie.com +// Submitted by AJ ONeal <aj@daplie.com> +daplie.me +localhost.daplie.me + +// Datto, Inc. : https://www.datto.com/ +// Submitted by Philipp Heckel <ph@datto.com> +dattolocal.com +dattorelay.com +dattoweb.com +mydatto.com +dattolocal.net +mydatto.net + +// Dansk.net : http://www.dansk.net/ +// Submitted by Anani Voule <digital@digital.co.dk> +biz.dk +co.dk +firm.dk +reg.dk +store.dk + +// dappnode.io : https://dappnode.io/ +// Submitted by Abel Boldu / DAppNode Team <community@dappnode.io> +dyndns.dappnode.io + +// dapps.earth : https://dapps.earth/ +// Submitted by Daniil Burdakov <icqkill@gmail.com> +*.dapps.earth +*.bzz.dapps.earth + +// Dark, Inc. : https://darklang.com +// Submitted by Paul Biggar <ops@darklang.com> +builtwithdark.com + +// Datawire, Inc : https://www.datawire.io +// Submitted by Richard Li <secalert@datawire.io> +edgestack.me + +// Debian : https://www.debian.org/ +// Submitted by Peter Palfrader / Debian Sysadmin Team <dsa-publicsuffixlist@debian.org> +debian.net + +// Deno Land Inc : https://deno.com/ +// Submitted by Luca Casonato <hostmaster@deno.com> +deno.dev +deno-staging.dev + +// deSEC : https://desec.io/ +// Submitted by Peter Thomassen <peter@desec.io> +dedyn.io + +// DNS Africa Ltd https://dns.business +// Submitted by Calvin Browne <calvin@dns.business> +jozi.biz + +// DNShome : https://www.dnshome.de/ +// Submitted by Norbert Auler <mail@dnshome.de> +dnshome.de + +// DotArai : https://www.dotarai.com/ +// Submitted by Atsadawat Netcharadsang <atsadawat@dotarai.co.th> +online.th +shop.th + +// DrayTek Corp. : https://www.draytek.com/ +// Submitted by Paul Fang <mis@draytek.com> +drayddns.com + +// DreamCommerce : https://shoper.pl/ +// Submitted by Konrad Kotarba <konrad.kotarba@dreamcommerce.com> +shoparena.pl + +// DreamHost : http://www.dreamhost.com/ +// Submitted by Andrew Farmer <andrew.farmer@dreamhost.com> +dreamhosters.com + +// Drobo : http://www.drobo.com/ +// Submitted by Ricardo Padilha <rpadilha@drobo.com> +mydrobo.com + +// Drud Holdings, LLC. : https://www.drud.com/ +// Submitted by Kevin Bridges <kevin@drud.com> +drud.io +drud.us + +// DuckDNS : http://www.duckdns.org/ +// Submitted by Richard Harper <richard@duckdns.org> +duckdns.org + +// Bip : https://bip.sh +// Submitted by Joel Kennedy <joel@bip.sh> +bip.sh + +// bitbridge.net : Submitted by Craig Welch, abeliidev@gmail.com +bitbridge.net + +// dy.fi : http://dy.fi/ +// Submitted by Heikki Hannikainen <hessu@hes.iki.fi> +dy.fi +tunk.org + +// DynDNS.com : http://www.dyndns.com/services/dns/dyndns/ +dyndns-at-home.com +dyndns-at-work.com +dyndns-blog.com +dyndns-free.com +dyndns-home.com +dyndns-ip.com +dyndns-mail.com +dyndns-office.com +dyndns-pics.com +dyndns-remote.com +dyndns-server.com +dyndns-web.com +dyndns-wiki.com +dyndns-work.com +dyndns.biz +dyndns.info +dyndns.org +dyndns.tv +at-band-camp.net +ath.cx +barrel-of-knowledge.info +barrell-of-knowledge.info +better-than.tv +blogdns.com +blogdns.net +blogdns.org +blogsite.org +boldlygoingnowhere.org +broke-it.net +buyshouses.net +cechire.com +dnsalias.com +dnsalias.net +dnsalias.org +dnsdojo.com +dnsdojo.net +dnsdojo.org +does-it.net +doesntexist.com +doesntexist.org +dontexist.com +dontexist.net +dontexist.org +doomdns.com +doomdns.org +dvrdns.org +dyn-o-saur.com +dynalias.com +dynalias.net +dynalias.org +dynathome.net +dyndns.ws +endofinternet.net +endofinternet.org +endoftheinternet.org +est-a-la-maison.com +est-a-la-masion.com +est-le-patron.com +est-mon-blogueur.com +for-better.biz +for-more.biz +for-our.info +for-some.biz +for-the.biz +forgot.her.name +forgot.his.name +from-ak.com +from-al.com +from-ar.com +from-az.net +from-ca.com +from-co.net +from-ct.com +from-dc.com +from-de.com +from-fl.com +from-ga.com +from-hi.com +from-ia.com +from-id.com +from-il.com +from-in.com +from-ks.com +from-ky.com +from-la.net +from-ma.com +from-md.com +from-me.org +from-mi.com +from-mn.com +from-mo.com +from-ms.com +from-mt.com +from-nc.com +from-nd.com +from-ne.com +from-nh.com +from-nj.com +from-nm.com +from-nv.com +from-ny.net +from-oh.com +from-ok.com +from-or.com +from-pa.com +from-pr.com +from-ri.com +from-sc.com +from-sd.com +from-tn.com +from-tx.com +from-ut.com +from-va.com +from-vt.com +from-wa.com +from-wi.com +from-wv.com +from-wy.com +ftpaccess.cc +fuettertdasnetz.de +game-host.org +game-server.cc +getmyip.com +gets-it.net +go.dyndns.org +gotdns.com +gotdns.org +groks-the.info +groks-this.info +ham-radio-op.net +here-for-more.info +hobby-site.com +hobby-site.org +home.dyndns.org +homedns.org +homeftp.net +homeftp.org +homeip.net +homelinux.com +homelinux.net +homelinux.org +homeunix.com +homeunix.net +homeunix.org +iamallama.com +in-the-band.net +is-a-anarchist.com +is-a-blogger.com +is-a-bookkeeper.com +is-a-bruinsfan.org +is-a-bulls-fan.com +is-a-candidate.org +is-a-caterer.com +is-a-celticsfan.org +is-a-chef.com +is-a-chef.net +is-a-chef.org +is-a-conservative.com +is-a-cpa.com +is-a-cubicle-slave.com +is-a-democrat.com +is-a-designer.com +is-a-doctor.com +is-a-financialadvisor.com +is-a-geek.com +is-a-geek.net +is-a-geek.org +is-a-green.com +is-a-guru.com +is-a-hard-worker.com +is-a-hunter.com +is-a-knight.org +is-a-landscaper.com +is-a-lawyer.com +is-a-liberal.com +is-a-libertarian.com +is-a-linux-user.org +is-a-llama.com +is-a-musician.com +is-a-nascarfan.com +is-a-nurse.com +is-a-painter.com +is-a-patsfan.org +is-a-personaltrainer.com +is-a-photographer.com +is-a-player.com +is-a-republican.com +is-a-rockstar.com +is-a-socialist.com +is-a-soxfan.org +is-a-student.com +is-a-teacher.com +is-a-techie.com +is-a-therapist.com +is-an-accountant.com +is-an-actor.com +is-an-actress.com +is-an-anarchist.com +is-an-artist.com +is-an-engineer.com +is-an-entertainer.com +is-by.us +is-certified.com +is-found.org +is-gone.com +is-into-anime.com +is-into-cars.com +is-into-cartoons.com +is-into-games.com +is-leet.com +is-lost.org +is-not-certified.com +is-saved.org +is-slick.com +is-uberleet.com +is-very-bad.org +is-very-evil.org +is-very-good.org +is-very-nice.org +is-very-sweet.org +is-with-theband.com +isa-geek.com +isa-geek.net +isa-geek.org +isa-hockeynut.com +issmarterthanyou.com +isteingeek.de +istmein.de +kicks-ass.net +kicks-ass.org +knowsitall.info +land-4-sale.us +lebtimnetz.de +leitungsen.de +likes-pie.com +likescandy.com +merseine.nu +mine.nu +misconfused.org +mypets.ws +myphotos.cc +neat-url.com +office-on-the.net +on-the-web.tv +podzone.net +podzone.org +readmyblog.org +saves-the-whales.com +scrapper-site.net +scrapping.cc +selfip.biz +selfip.com +selfip.info +selfip.net +selfip.org +sells-for-less.com +sells-for-u.com +sells-it.net +sellsyourhome.org +servebbs.com +servebbs.net +servebbs.org +serveftp.net +serveftp.org +servegame.org +shacknet.nu +simple-url.com +space-to-rent.com +stuff-4-sale.org +stuff-4-sale.us +teaches-yoga.com +thruhere.net +traeumtgerade.de +webhop.biz +webhop.info +webhop.net +webhop.org +worse-than.tv +writesthisblog.com + +// ddnss.de : https://www.ddnss.de/ +// Submitted by Robert Niedziela <webmaster@ddnss.de> +ddnss.de +dyn.ddnss.de +dyndns.ddnss.de +dyndns1.de +dyn-ip24.de +home-webserver.de +dyn.home-webserver.de +myhome-server.de +ddnss.org + +// Definima : http://www.definima.com/ +// Submitted by Maxence Bitterli <maxence@definima.com> +definima.net +definima.io + +// DigitalOcean : https://digitalocean.com/ +// Submitted by Braxton Huggins <bhuggins@digitalocean.com> +ondigitalocean.app + +// dnstrace.pro : https://dnstrace.pro/ +// Submitted by Chris Partridge <chris@partridge.tech> +bci.dnstrace.pro + +// Dynu.com : https://www.dynu.com/ +// Submitted by Sue Ye <sue@dynu.com> +ddnsfree.com +ddnsgeek.com +giize.com +gleeze.com +kozow.com +loseyourip.com +ooguy.com +theworkpc.com +casacam.net +dynu.net +accesscam.org +camdvr.org +freeddns.org +mywire.org +webredirect.org +myddns.rocks +blogsite.xyz + +// dynv6 : https://dynv6.com +// Submitted by Dominik Menke <dom@digineo.de> +dynv6.net + +// E4YOU spol. s.r.o. : https://e4you.cz/ +// Submitted by Vladimir Dudr <info@e4you.cz> +e4.cz + +// En root‽ : https://en-root.org +// Submitted by Emmanuel Raviart <emmanuel@raviart.com> +en-root.fr + +// Enalean SAS: https://www.enalean.com +// Submitted by Thomas Cottier <thomas.cottier@enalean.com> +mytuleap.com + +// ECG Robotics, Inc: https://ecgrobotics.org +// Submitted by <frc1533@ecgrobotics.org> +onred.one +staging.onred.one + +// One.com: https://www.one.com/ +// Submitted by Jacob Bunk Nielsen <jbn@one.com> +service.one + +// Enonic : http://enonic.com/ +// Submitted by Erik Kaareng-Sunde <esu@enonic.com> +enonic.io +customer.enonic.io + +// EU.org https://eu.org/ +// Submitted by Pierre Beyssac <hostmaster@eu.org> +eu.org +al.eu.org +asso.eu.org +at.eu.org +au.eu.org +be.eu.org +bg.eu.org +ca.eu.org +cd.eu.org +ch.eu.org +cn.eu.org +cy.eu.org +cz.eu.org +de.eu.org +dk.eu.org +edu.eu.org +ee.eu.org +es.eu.org +fi.eu.org +fr.eu.org +gr.eu.org +hr.eu.org +hu.eu.org +ie.eu.org +il.eu.org +in.eu.org +int.eu.org +is.eu.org +it.eu.org +jp.eu.org +kr.eu.org +lt.eu.org +lu.eu.org +lv.eu.org +mc.eu.org +me.eu.org +mk.eu.org +mt.eu.org +my.eu.org +net.eu.org +ng.eu.org +nl.eu.org +no.eu.org +nz.eu.org +paris.eu.org +pl.eu.org +pt.eu.org +q-a.eu.org +ro.eu.org +ru.eu.org +se.eu.org +si.eu.org +sk.eu.org +tr.eu.org +uk.eu.org +us.eu.org + +// Evennode : http://www.evennode.com/ +// Submitted by Michal Kralik <support@evennode.com> +eu-1.evennode.com +eu-2.evennode.com +eu-3.evennode.com +eu-4.evennode.com +us-1.evennode.com +us-2.evennode.com +us-3.evennode.com +us-4.evennode.com + +// eDirect Corp. : https://hosting.url.com.tw/ +// Submitted by C.S. chang <cschang@corp.url.com.tw> +twmail.cc +twmail.net +twmail.org +mymailer.com.tw +url.tw + +// Fabrica Technologies, Inc. : https://www.fabrica.dev/ +// Submitted by Eric Jiang <eric@fabrica.dev> +onfabrica.com + +// Facebook, Inc. +// Submitted by Peter Ruibal <public-suffix@fb.com> +apps.fbsbx.com + +// FAITID : https://faitid.org/ +// Submitted by Maxim Alzoba <tech.contact@faitid.org> +// https://www.flexireg.net/stat_info +ru.net +adygeya.ru +bashkiria.ru +bir.ru +cbg.ru +com.ru +dagestan.ru +grozny.ru +kalmykia.ru +kustanai.ru +marine.ru +mordovia.ru +msk.ru +mytis.ru +nalchik.ru +nov.ru +pyatigorsk.ru +spb.ru +vladikavkaz.ru +vladimir.ru +abkhazia.su +adygeya.su +aktyubinsk.su +arkhangelsk.su +armenia.su +ashgabad.su +azerbaijan.su +balashov.su +bashkiria.su +bryansk.su +bukhara.su +chimkent.su +dagestan.su +east-kazakhstan.su +exnet.su +georgia.su +grozny.su +ivanovo.su +jambyl.su +kalmykia.su +kaluga.su +karacol.su +karaganda.su +karelia.su +khakassia.su +krasnodar.su +kurgan.su +kustanai.su +lenug.su +mangyshlak.su +mordovia.su +msk.su +murmansk.su +nalchik.su +navoi.su +north-kazakhstan.su +nov.su +obninsk.su +penza.su +pokrovsk.su +sochi.su +spb.su +tashkent.su +termez.su +togliatti.su +troitsk.su +tselinograd.su +tula.su +tuva.su +vladikavkaz.su +vladimir.su +vologda.su + +// Fancy Bits, LLC : http://getchannels.com +// Submitted by Aman Gupta <aman@getchannels.com> +channelsdvr.net +u.channelsdvr.net + +// Fastly Inc. : http://www.fastly.com/ +// Submitted by Fastly Security <security@fastly.com> +fastly-terrarium.com +fastlylb.net +map.fastlylb.net +freetls.fastly.net +map.fastly.net +a.prod.fastly.net +global.prod.fastly.net +a.ssl.fastly.net +b.ssl.fastly.net +global.ssl.fastly.net + +// FASTVPS EESTI OU : https://fastvps.ru/ +// Submitted by Likhachev Vasiliy <lihachev@fastvps.ru> +fastvps-server.com +fastvps.host +myfast.host +fastvps.site +myfast.space + +// Fedora : https://fedoraproject.org/ +// submitted by Patrick Uiterwijk <puiterwijk@fedoraproject.org> +fedorainfracloud.org +fedorapeople.org +cloud.fedoraproject.org +app.os.fedoraproject.org +app.os.stg.fedoraproject.org + +// FearWorks Media Ltd. : https://fearworksmedia.co.uk +// submitted by Keith Fairley <domains@fearworksmedia.co.uk> +conn.uk +copro.uk +couk.me +ukco.me + +// Fermax : https://fermax.com/ +// submitted by Koen Van Isterdael <k.vanisterdael@fermax.be> +mydobiss.com + +// FH Muenster : https://www.fh-muenster.de +// Submitted by Robin Naundorf <r.naundorf@fh-muenster.de> +fh-muenster.io + +// Filegear Inc. : https://www.filegear.com +// Submitted by Jason Zhu <jason@owtware.com> +filegear.me +filegear-au.me +filegear-de.me +filegear-gb.me +filegear-ie.me +filegear-jp.me +filegear-sg.me + +// Firebase, Inc. +// Submitted by Chris Raynor <chris@firebase.com> +firebaseapp.com + +// Firewebkit : https://www.firewebkit.com +// Submitted by Majid Qureshi <mqureshi@amrayn.com> +fireweb.app + +// FLAP : https://www.flap.cloud +// Submitted by Louis Chemineau <louis@chmn.me> +flap.id + +// fly.io: https://fly.io +// Submitted by Kurt Mackey <kurt@fly.io> +fly.dev +edgeapp.net +shw.io + +// Flynn : https://flynn.io +// Submitted by Jonathan Rudenberg <jonathan@flynn.io> +flynnhosting.net + +// Frederik Braun https://frederik-braun.com +// Submitted by Frederik Braun <fb@frederik-braun.com> +0e.vc + +// Freebox : http://www.freebox.fr +// Submitted by Romain Fliedel <rfliedel@freebox.fr> +freebox-os.com +freeboxos.com +fbx-os.fr +fbxos.fr +freebox-os.fr +freeboxos.fr + +// freedesktop.org : https://www.freedesktop.org +// Submitted by Daniel Stone <daniel@fooishbar.org> +freedesktop.org + +// freemyip.com : https://freemyip.com +// Submitted by Cadence <contact@freemyip.com> +freemyip.com + +// FunkFeuer - Verein zur Förderung freier Netze : https://www.funkfeuer.at +// Submitted by Daniel A. Maierhofer <vorstand@funkfeuer.at> +wien.funkfeuer.at + +// Futureweb OG : http://www.futureweb.at +// Submitted by Andreas Schnederle-Wagner <schnederle@futureweb.at> +*.futurecms.at +*.ex.futurecms.at +*.in.futurecms.at +futurehosting.at +futuremailing.at +*.ex.ortsinfo.at +*.kunden.ortsinfo.at +*.statics.cloud + +// GDS : https://www.gov.uk/service-manual/operations/operating-servicegovuk-subdomains +// Submitted by David Illsley <david.illsley@digital.cabinet-office.gov.uk> +service.gov.uk + +// Gehirn Inc. : https://www.gehirn.co.jp/ +// Submitted by Kohei YOSHIDA <tech@gehirn.co.jp> +gehirn.ne.jp +usercontent.jp + +// Gentlent, Inc. : https://www.gentlent.com +// Submitted by Tom Klein <tom@gentlent.com> +gentapps.com +gentlentapis.com +lab.ms +cdn-edges.net + +// Ghost Foundation : https://ghost.org +// Submitted by Matt Hanley <security@ghost.org> +ghost.io + +// GitHub, Inc. +// Submitted by Patrick Toomey <security@github.com> +github.io +githubusercontent.com + +// GitLab, Inc. +// Submitted by Alex Hanselka <alex@gitlab.com> +gitlab.io + +// Gitplac.si - https://gitplac.si +// Submitted by Aljaž Starc <me@aljaxus.eu> +gitapp.si +gitpage.si + +// Glitch, Inc : https://glitch.com +// Submitted by Mads Hartmann <mads@glitch.com> +glitch.me + +// GMO Pepabo, Inc. : https://pepabo.com/ +// Submitted by dojineko <admin@pepabo.com> +lolipop.io + +// GOV.UK Platform as a Service : https://www.cloud.service.gov.uk/ +// Submitted by Tom Whitwell <tom.whitwell@digital.cabinet-office.gov.uk> +cloudapps.digital +london.cloudapps.digital + +// GOV.UK Pay : https://www.payments.service.gov.uk/ +// Submitted by Richard Baker <richard.baker@digital.cabinet-office.gov.uk> +pymnt.uk + +// UKHomeOffice : https://www.gov.uk/government/organisations/home-office +// Submitted by Jon Shanks <jon.shanks@digital.homeoffice.gov.uk> +homeoffice.gov.uk + +// GlobeHosting, Inc. +// Submitted by Zoltan Egresi <egresi@globehosting.com> +ro.im + +// GoIP DNS Services : http://www.goip.de +// Submitted by Christian Poulter <milchstrasse@goip.de> +goip.de + +// Google, Inc. +// Submitted by Eduardo Vela <evn@google.com> +run.app +a.run.app +web.app +*.0emm.com +appspot.com +*.r.appspot.com +codespot.com +googleapis.com +googlecode.com +pagespeedmobilizer.com +publishproxy.com +withgoogle.com +withyoutube.com +*.gateway.dev +cloud.goog +translate.goog +cloudfunctions.net + +blogspot.ae +blogspot.al +blogspot.am +blogspot.ba +blogspot.be +blogspot.bg +blogspot.bj +blogspot.ca +blogspot.cf +blogspot.ch +blogspot.cl +blogspot.co.at +blogspot.co.id +blogspot.co.il +blogspot.co.ke +blogspot.co.nz +blogspot.co.uk +blogspot.co.za +blogspot.com +blogspot.com.ar +blogspot.com.au +blogspot.com.br +blogspot.com.by +blogspot.com.co +blogspot.com.cy +blogspot.com.ee +blogspot.com.eg +blogspot.com.es +blogspot.com.mt +blogspot.com.ng +blogspot.com.tr +blogspot.com.uy +blogspot.cv +blogspot.cz +blogspot.de +blogspot.dk +blogspot.fi +blogspot.fr +blogspot.gr +blogspot.hk +blogspot.hr +blogspot.hu +blogspot.ie +blogspot.in +blogspot.is +blogspot.it +blogspot.jp +blogspot.kr +blogspot.li +blogspot.lt +blogspot.lu +blogspot.md +blogspot.mk +blogspot.mr +blogspot.mx +blogspot.my +blogspot.nl +blogspot.no +blogspot.pe +blogspot.pt +blogspot.qa +blogspot.re +blogspot.ro +blogspot.rs +blogspot.ru +blogspot.se +blogspot.sg +blogspot.si +blogspot.sk +blogspot.sn +blogspot.td +blogspot.tw +blogspot.ug +blogspot.vn + +// Aaron Marais' Gitlab pages: https://lab.aaronleem.co.za +// Submitted by Aaron Marais <its_me@aaronleem.co.za> +graphox.us + +// Group 53, LLC : https://www.group53.com +// Submitted by Tyler Todd <noc@nova53.net> +awsmppl.com + +// GünstigBestellen : https://günstigbestellen.de +// Submitted by Furkan Akkoc <info@hendelzon.de> +günstigbestellen.de +günstigliefern.de + +// Hakaran group: http://hakaran.cz +// Submited by Arseniy Sokolov <security@hakaran.cz> +fin.ci +free.hr +caa.li +ua.rs +conf.se + +// Handshake : https://handshake.org +// Submitted by Mike Damm <md@md.vc> +hs.zone +hs.run + +// Hashbang : https://hashbang.sh +hashbang.sh + +// Hasura : https://hasura.io +// Submitted by Shahidh K Muhammed <shahidh@hasura.io> +hasura.app +hasura-app.io + +// Hepforge : https://www.hepforge.org +// Submitted by David Grellscheid <admin@hepforge.org> +hepforge.org + +// Heroku : https://www.heroku.com/ +// Submitted by Tom Maher <tmaher@heroku.com> +herokuapp.com +herokussl.com + +// Hibernating Rhinos +// Submitted by Oren Eini <oren@ravendb.net> +myravendb.com +ravendb.community +ravendb.me +development.run +ravendb.run + +// Hong Kong Productivity Council: https://www.hkpc.org/ +// Submitted by SECaaS Team <summchan@hkpc.org> +secaas.hk + +// HOSTBIP REGISTRY : https://www.hostbip.com/ +// Submitted by Atanunu Igbunuroghene <publicsuffixlist@hostbip.com> +orx.biz +biz.gl +col.ng +firm.ng +gen.ng +ltd.ng +ngo.ng +edu.scot +sch.so +org.yt + +// HostyHosting (hostyhosting.com) +hostyhosting.io + +// Häkkinen.fi +// Submitted by Eero Häkkinen <Eero+psl@Häkkinen.fi> +häkkinen.fi + +// Ici la Lune : http://www.icilalune.com/ +// Submitted by Simon Morvan <simon@icilalune.com> +*.moonscale.io +moonscale.net + +// iki.fi +// Submitted by Hannu Aronsson <haa@iki.fi> +iki.fi + +// Incsub, LLC: https://incsub.com/ +// Submitted by Aaron Edwards <sysadmins@incsub.com> +smushcdn.com +wphostedmail.com +wpmucdn.com +tempurl.host +wpmudev.host + +// Individual Network Berlin e.V. : https://www.in-berlin.de/ +// Submitted by Christian Seitz <chris@in-berlin.de> +dyn-berlin.de +in-berlin.de +in-brb.de +in-butter.de +in-dsl.de +in-dsl.net +in-dsl.org +in-vpn.de +in-vpn.net +in-vpn.org + +// info.at : http://www.info.at/ +biz.at +info.at + +// info.cx : http://info.cx +// Submitted by Jacob Slater <whois@igloo.to> +info.cx + +// Interlegis : http://www.interlegis.leg.br +// Submitted by Gabriel Ferreira <registrobr@interlegis.leg.br> +ac.leg.br +al.leg.br +am.leg.br +ap.leg.br +ba.leg.br +ce.leg.br +df.leg.br +es.leg.br +go.leg.br +ma.leg.br +mg.leg.br +ms.leg.br +mt.leg.br +pa.leg.br +pb.leg.br +pe.leg.br +pi.leg.br +pr.leg.br +rj.leg.br +rn.leg.br +ro.leg.br +rr.leg.br +rs.leg.br +sc.leg.br +se.leg.br +sp.leg.br +to.leg.br + +// intermetrics GmbH : https://pixolino.com/ +// Submitted by Wolfgang Schwarz <admin@intermetrics.de> +pixolino.com + +// Internet-Pro, LLP: https://netangels.ru/ +// Submited by Vasiliy Sheredeko <piphon@gmail.com> +na4u.ru + +// iopsys software solutions AB : https://iopsys.eu/ +// Submitted by Roman Azarenko <roman.azarenko@iopsys.eu> +iopsys.se + +// IPiFony Systems, Inc. : https://www.ipifony.com/ +// Submitted by Matthew Hardeman <mhardeman@ipifony.com> +ipifony.net + +// IServ GmbH : https://iserv.eu +// Submitted by Kim-Alexander Brodowski <info@iserv.eu> +mein-iserv.de +schulserver.de +test-iserv.de +iserv.dev + +// I-O DATA DEVICE, INC. : http://www.iodata.com/ +// Submitted by Yuji Minagawa <domains-admin@iodata.jp> +iobb.net + +// Jelastic, Inc. : https://jelastic.com/ +// Submited by Ihor Kolodyuk <ik@jelastic.com> +mel.cloudlets.com.au +cloud.interhostsolutions.be +users.scale.virtualcloud.com.br +mycloud.by +alp1.ae.flow.ch +appengine.flow.ch +es-1.axarnet.cloud +diadem.cloud +vip.jelastic.cloud +jele.cloud +it1.eur.aruba.jenv-aruba.cloud +it1.jenv-aruba.cloud +it1-eur.jenv-arubabiz.cloud +oxa.cloud +tn.oxa.cloud +uk.oxa.cloud +primetel.cloud +uk.primetel.cloud +ca.reclaim.cloud +uk.reclaim.cloud +us.reclaim.cloud +ch.trendhosting.cloud +de.trendhosting.cloud +jele.club +clicketcloud.com +ams.cloudswitches.com +au.cloudswitches.com +sg.cloudswitches.com +dopaas.com +elastyco.com +nv.elastyco.com +hidora.com +paas.hosted-by-previder.com +rag-cloud.hosteur.com +rag-cloud-ch.hosteur.com +jcloud.ik-server.com +jcloud-ver-jpc.ik-server.com +demo.jelastic.com +kilatiron.com +paas.massivegrid.com +jed.wafaicloud.com +lon.wafaicloud.com +ryd.wafaicloud.com +j.scaleforce.com.cy +jelastic.dogado.eu +paas.leviracloud.eu +fi.cloudplatform.fi +demo.datacenter.fi +paas.datacenter.fi +jele.host +mircloud.host +jele.io +ocs.opusinteractive.io +cloud.unispace.io +cloud-de.unispace.io +cloud-fr1.unispace.io +jc.neen.it +cloud.jelastic.open.tim.it +jcloud.kz +upaas.kazteleport.kz +jl.serv.net.mx +cloudjiffy.net +fra1-de.cloudjiffy.net +west1-us.cloudjiffy.net +ams1.jls.docktera.net +jls-sto1.elastx.net +jls-sto2.elastx.net +jls-sto3.elastx.net +fr-1.paas.massivegrid.net +lon-1.paas.massivegrid.net +lon-2.paas.massivegrid.net +ny-1.paas.massivegrid.net +ny-2.paas.massivegrid.net +sg-1.paas.massivegrid.net +jelastic.saveincloud.net +nordeste-idc.saveincloud.net +j.scaleforce.net +jelastic.tsukaeru.net +atl.jelastic.vps-host.net +njs.jelastic.vps-host.net +unicloud.pl +mircloud.ru +jelastic.regruhosting.ru +enscaled.sg +jele.site +jelastic.team +orangecloud.tn +j.layershift.co.uk +phx.enscaled.us +mircloud.us + +// Jino : https://www.jino.ru +// Submitted by Sergey Ulyashin <ulyashin@jino.ru> +myjino.ru +*.hosting.myjino.ru +*.landing.myjino.ru +*.spectrum.myjino.ru +*.vps.myjino.ru + +// Joyent : https://www.joyent.com/ +// Submitted by Brian Bennett <brian.bennett@joyent.com> +*.triton.zone +*.cns.joyent.com + +// JS.ORG : http://dns.js.org +// Submitted by Stefan Keim <admin@js.org> +js.org + +// KaasHosting : http://www.kaashosting.nl/ +// Submitted by Wouter Bakker <hostmaster@kaashosting.nl> +kaas.gg +khplay.nl + +// Keyweb AG : https://www.keyweb.de +// Submitted by Martin Dannehl <postmaster@keymachine.de> +keymachine.de + +// KingHost : https://king.host +// Submitted by Felipe Keller Braz <felipebraz@kinghost.com.br> +kinghost.net +uni5.net + +// KnightPoint Systems, LLC : http://www.knightpoint.com/ +// Submitted by Roy Keene <rkeene@knightpoint.com> +knightpoint.systems + +// KUROKU LTD : https://kuroku.ltd/ +// Submitted by DisposaBoy <security@oya.to> +oya.to + +// Katholieke Universiteit Leuven: https://www.kuleuven.be +// Submitted by Abuse KU Leuven <abuse@kuleuven.be> +kuleuven.cloud +ezproxy.kuleuven.be + +// .KRD : http://nic.krd/data/krd/Registration%20Policy.pdf +co.krd +edu.krd + +// Krellian Ltd. : https://krellian.com +// Submitted by Ben Francis <ben@krellian.com> +krellian.net +webthings.io + +// LCube - Professional hosting e.K. : https://www.lcube-webhosting.de +// Submitted by Lars Laehn <info@lcube.de> +git-repos.de +lcube-server.de +svn-repos.de + +// Leadpages : https://www.leadpages.net +// Submitted by Greg Dallavalle <domains@leadpages.net> +leadpages.co +lpages.co +lpusercontent.com + +// Lelux.fi : https://lelux.fi/ +// Submitted by Lelux Admin <publisuffix@lelux.site> +lelux.site + +// Lifetime Hosting : https://Lifetime.Hosting/ +// Submitted by Mike Fillator <support@lifetime.hosting> +co.business +co.education +co.events +co.financial +co.network +co.place +co.technology + +// Lightmaker Property Manager, Inc. : https://app.lmpm.com/ +// Submitted by Greg Holland <greg.holland@lmpm.com> +app.lmpm.com + +// linkyard ldt: https://www.linkyard.ch/ +// Submitted by Mario Siegenthaler <mario.siegenthaler@linkyard.ch> +linkyard.cloud +linkyard-cloud.ch + +// Linode : https://linode.com +// Submitted by <security@linode.com> +members.linode.com +*.nodebalancer.linode.com +*.linodeobjects.com + +// LiquidNet Ltd : http://www.liquidnetlimited.com/ +// Submitted by Victor Velchev <admin@liquidnetlimited.com> +we.bs + +// localzone.xyz +// Submitted by Kenny Niehage <hello@yahe.sh> +localzone.xyz + +// Log'in Line : https://www.loginline.com/ +// Submitted by Rémi Mach <remi.mach@loginline.com> +loginline.app +loginline.dev +loginline.io +loginline.services +loginline.site + +// Lõhmus Family, The +// Submitted by Heiki Lõhmus <hostmaster at lohmus dot me> +lohmus.me + +// LubMAN UMCS Sp. z o.o : https://lubman.pl/ +// Submitted by Ireneusz Maliszewski <ireneusz.maliszewski@lubman.pl> +krasnik.pl +leczna.pl +lubartow.pl +lublin.pl +poniatowa.pl +swidnik.pl + +// Lug.org.uk : https://lug.org.uk +// Submitted by Jon Spriggs <admin@lug.org.uk> +glug.org.uk +lug.org.uk +lugs.org.uk + +// Lukanet Ltd : https://lukanet.com +// Submitted by Anton Avramov <register@lukanet.com> +barsy.bg +barsy.co.uk +barsyonline.co.uk +barsycenter.com +barsyonline.com +barsy.club +barsy.de +barsy.eu +barsy.in +barsy.info +barsy.io +barsy.me +barsy.menu +barsy.mobi +barsy.net +barsy.online +barsy.org +barsy.pro +barsy.pub +barsy.shop +barsy.site +barsy.support +barsy.uk + +// Magento Commerce +// Submitted by Damien Tournoud <dtournoud@magento.cloud> +*.magentosite.cloud + +// May First - People Link : https://mayfirst.org/ +// Submitted by Jamie McClelland <info@mayfirst.org> +mayfirst.info +mayfirst.org + +// Mail.Ru Group : https://hb.cldmail.ru +// Submitted by Ilya Zaretskiy <zaretskiy@corp.mail.ru> +hb.cldmail.ru + +// mcpe.me : https://mcpe.me +// Submitted by Noa Heyl <hi@noa.dev> +mcpe.me + +// McHost : https://mchost.ru +// Submitted by Evgeniy Subbotin <e.subbotin@mchost.ru> +mcdir.me +mcdir.ru +mcpre.ru +vps.mcdir.ru + +// Memset hosting : https://www.memset.com +// Submitted by Tom Whitwell <domains@memset.com> +miniserver.com +memset.net + +// MetaCentrum, CESNET z.s.p.o. : https://www.metacentrum.cz/en/ +// Submitted by Zdeněk Šustr <zdenek.sustr@cesnet.cz> +*.cloud.metacentrum.cz +custom.metacentrum.cz + +// MetaCentrum, CESNET z.s.p.o. : https://www.metacentrum.cz/en/ +// Submitted by Radim Janča <janca@cesnet.cz> +flt.cloud.muni.cz +usr.cloud.muni.cz + +// Meteor Development Group : https://www.meteor.com/hosting +// Submitted by Pierre Carrier <pierre@meteor.com> +meteorapp.com +eu.meteorapp.com + +// Michau Enterprises Limited : http://www.co.pl/ +co.pl + +// Microsoft Corporation : http://microsoft.com +// Submitted by Mitch Webster <miwebst@microsoft.com> +*.azurecontainer.io +azurewebsites.net +azure-mobile.net +cloudapp.net +azurestaticapps.net +centralus.azurestaticapps.net +eastasia.azurestaticapps.net +eastus2.azurestaticapps.net +westeurope.azurestaticapps.net +westus2.azurestaticapps.net + +// minion.systems : http://minion.systems +// Submitted by Robert Böttinger <r@minion.systems> +csx.cc + +// MobileEducation, LLC : https://joinforte.com +// Submitted by Grayson Martin <grayson.martin@mobileeducation.us> +forte.id + +// Mozilla Corporation : https://mozilla.com +// Submitted by Ben Francis <bfrancis@mozilla.com> +mozilla-iot.org + +// Mozilla Foundation : https://mozilla.org/ +// Submitted by glob <glob@mozilla.com> +bmoattachments.org + +// MSK-IX : https://www.msk-ix.ru/ +// Submitted by Khannanov Roman <r.khannanov@msk-ix.ru> +net.ru +org.ru +pp.ru + +// Mythic Beasts : https://www.mythic-beasts.com +// Submitted by Paul Cammish <kelduum@mythic-beasts.com> +hostedpi.com +customer.mythic-beasts.com +caracal.mythic-beasts.com +fentiger.mythic-beasts.com +lynx.mythic-beasts.com +ocelot.mythic-beasts.com +oncilla.mythic-beasts.com +onza.mythic-beasts.com +sphinx.mythic-beasts.com +vs.mythic-beasts.com +x.mythic-beasts.com +yali.mythic-beasts.com +cust.retrosnub.co.uk + +// Nabu Casa : https://www.nabucasa.com +// Submitted by Paulus Schoutsen <infra@nabucasa.com> +ui.nabu.casa + +// Names.of.London : https://names.of.london/ +// Submitted by James Stevens <registry[at]names.of.london> or <publiclist[at]jrcs.net> +pony.club +of.fashion +in.london +of.london +from.marketing +with.marketing +for.men +repair.men +and.mom +for.mom +for.one +under.one +for.sale +that.win +from.work +to.work + +// NCTU.ME : https://nctu.me/ +// Submitted by Tocknicsu <admin@nctu.me> +nctu.me + +// Netlify : https://www.netlify.com +// Submitted by Jessica Parsons <jessica@netlify.com> +netlify.app + +// Neustar Inc. +// Submitted by Trung Tran <Trung.Tran@neustar.biz> +4u.com + +// ngrok : https://ngrok.com/ +// Submitted by Alan Shreve <alan@ngrok.com> +ngrok.io + +// Nimbus Hosting Ltd. : https://www.nimbushosting.co.uk/ +// Submitted by Nicholas Ford <nick@nimbushosting.co.uk> +nh-serv.co.uk + +// NFSN, Inc. : https://www.NearlyFreeSpeech.NET/ +// Submitted by Jeff Wheelhouse <support@nearlyfreespeech.net> +nfshost.com + +// Northflank Ltd. : https://northflank.com/ +// Submitted by Marco Suter <marco@northflank.com> +northflank.app +code.run + +// Now-DNS : https://now-dns.com +// Submitted by Steve Russell <steve@now-dns.com> +dnsking.ch +mypi.co +n4t.co +001www.com +ddnslive.com +myiphost.com +forumz.info +16-b.it +32-b.it +64-b.it +soundcast.me +tcp4.me +dnsup.net +hicam.net +now-dns.net +ownip.net +vpndns.net +dynserv.org +now-dns.org +x443.pw +now-dns.top +ntdll.top +freeddns.us +crafting.xyz +zapto.xyz + +// nsupdate.info : https://www.nsupdate.info/ +// Submitted by Thomas Waldmann <info@nsupdate.info> +nsupdate.info +nerdpol.ovh + +// No-IP.com : https://noip.com/ +// Submitted by Deven Reza <publicsuffixlist@noip.com> +blogsyte.com +brasilia.me +cable-modem.org +ciscofreak.com +collegefan.org +couchpotatofries.org +damnserver.com +ddns.me +ditchyourip.com +dnsfor.me +dnsiskinky.com +dvrcam.info +dynns.com +eating-organic.net +fantasyleague.cc +geekgalaxy.com +golffan.us +health-carereform.com +homesecuritymac.com +homesecuritypc.com +hopto.me +ilovecollege.info +loginto.me +mlbfan.org +mmafan.biz +myactivedirectory.com +mydissent.net +myeffect.net +mymediapc.net +mypsx.net +mysecuritycamera.com +mysecuritycamera.net +mysecuritycamera.org +net-freaks.com +nflfan.org +nhlfan.net +no-ip.ca +no-ip.co.uk +no-ip.net +noip.us +onthewifi.com +pgafan.net +point2this.com +pointto.us +privatizehealthinsurance.net +quicksytes.com +read-books.org +securitytactics.com +serveexchange.com +servehumour.com +servep2p.com +servesarcasm.com +stufftoread.com +ufcfan.org +unusualperson.com +workisboring.com +3utilities.com +bounceme.net +ddns.net +ddnsking.com +gotdns.ch +hopto.org +myftp.biz +myftp.org +myvnc.com +no-ip.biz +no-ip.info +no-ip.org +noip.me +redirectme.net +servebeer.com +serveblog.net +servecounterstrike.com +serveftp.com +servegame.com +servehalflife.com +servehttp.com +serveirc.com +serveminecraft.net +servemp3.com +servepics.com +servequake.com +sytes.net +webhop.me +zapto.org + +// NodeArt : https://nodeart.io +// Submitted by Konstantin Nosov <Nosov@nodeart.io> +stage.nodeart.io + +// Nodum B.V. : https://nodum.io/ +// Submitted by Wietse Wind <hello+publicsuffixlist@nodum.io> +nodum.co +nodum.io + +// Nucleos Inc. : https://nucleos.com +// Submitted by Piotr Zduniak <piotr@nucleos.com> +pcloud.host + +// NYC.mn : http://www.information.nyc.mn +// Submitted by Matthew Brown <mattbrown@nyc.mn> +nyc.mn + +// NymNom : https://nymnom.com/ +// Submitted by NymNom <psl@nymnom.com> +nom.ae +nom.af +nom.ai +nom.al +nym.by +nom.bz +nym.bz +nom.cl +nym.ec +nom.gd +nom.ge +nom.gl +nym.gr +nom.gt +nym.gy +nym.hk +nom.hn +nym.ie +nom.im +nom.ke +nym.kz +nym.la +nym.lc +nom.li +nym.li +nym.lt +nym.lu +nom.lv +nym.me +nom.mk +nym.mn +nym.mx +nom.nu +nym.nz +nym.pe +nym.pt +nom.pw +nom.qa +nym.ro +nom.rs +nom.si +nym.sk +nom.st +nym.su +nym.sx +nom.tj +nym.tw +nom.ug +nom.uy +nom.vc +nom.vg + +// Observable, Inc. : https://observablehq.com +// Submitted by Mike Bostock <dns@observablehq.com> +static.observableusercontent.com + +// Octopodal Solutions, LLC. : https://ulterius.io/ +// Submitted by Andrew Sampson <andrew@ulterius.io> +cya.gg + +// OMG.LOL : <https://omg.lol> +// Submitted by Adam Newbold <adam@omg.lol> +omg.lol + +// Omnibond Systems, LLC. : https://www.omnibond.com +// Submitted by Cole Estep <cole@omnibond.com> +cloudycluster.net + +// OmniWe Limited: https://omniwe.com +// Submitted by Vicary Archangel <vicary@omniwe.com> +omniwe.site + +// One Fold Media : http://www.onefoldmedia.com/ +// Submitted by Eddie Jones <eddie@onefoldmedia.com> +nid.io + +// Open Social : https://www.getopensocial.com/ +// Submitted by Alexander Varwijk <security@getopensocial.com> +opensocial.site + +// OpenCraft GmbH : http://opencraft.com/ +// Submitted by Sven Marnach <sven@opencraft.com> +opencraft.hosting + +// OpenResearch GmbH: https://openresearch.com/ +// Submitted by Philipp Schmid <ops@openresearch.com> +orsites.com + +// Opera Software, A.S.A. +// Submitted by Yngve Pettersen <yngve@opera.com> +operaunite.com + +// Oursky Limited : https://skygear.io/ +// Submited by Skygear Developer <hello@skygear.io> +skygearapp.com + +// OutSystems +// Submitted by Duarte Santos <domain-admin@outsystemscloud.com> +outsystemscloud.com + +// OVHcloud: https://ovhcloud.com +// Submitted by Vincent Cassé <vincent.casse@ovhcloud.com> +*.webpaas.ovh.net +*.hosting.ovh.net + +// OwnProvider GmbH: http://www.ownprovider.com +// Submitted by Jan Moennich <jan.moennich@ownprovider.com> +ownprovider.com +own.pm + +// OwO : https://whats-th.is/ +// Submitted by Dean Sheather <dean@deansheather.com> +*.owo.codes + +// OX : http://www.ox.rs +// Submitted by Adam Grand <webmaster@mail.ox.rs> +ox.rs + +// oy.lc +// Submitted by Charly Coste <changaco@changaco.oy.lc> +oy.lc + +// Pagefog : https://pagefog.com/ +// Submitted by Derek Myers <derek@pagefog.com> +pgfog.com + +// Pagefront : https://www.pagefronthq.com/ +// Submitted by Jason Kriss <jason@pagefronthq.com> +pagefrontapp.com + +// PageXL : https://pagexl.com +// Submitted by Yann Guichard <yann@pagexl.com> +pagexl.com + +// pcarrier.ca Software Inc: https://pcarrier.ca/ +// Submitted by Pierre Carrier <pc@rrier.ca> +bar0.net +bar1.net +bar2.net +rdv.to + +// .pl domains (grandfathered) +art.pl +gliwice.pl +krakow.pl +poznan.pl +wroc.pl +zakopane.pl + +// Pantheon Systems, Inc. : https://pantheon.io/ +// Submitted by Gary Dylina <gary@pantheon.io> +pantheonsite.io +gotpantheon.com + +// Peplink | Pepwave : http://peplink.com/ +// Submitted by Steve Leung <steveleung@peplink.com> +mypep.link + +// Perspecta : https://perspecta.com/ +// Submitted by Kenneth Van Alstyne <kvanalstyne@perspecta.com> +perspecta.cloud + +// PE Ulyanov Kirill Sergeevich : https://airy.host +// Submitted by Kirill Ulyanov <k.ulyanov@airy.host> +lk3.ru +ra-ru.ru +zsew.ru + +// Planet-Work : https://www.planet-work.com/ +// Submitted by Frédéric VANNIÈRE <f.vanniere@planet-work.com> +on-web.fr + +// Platform.sh : https://platform.sh +// Submitted by Nikola Kotur <nikola@platform.sh> +bc.platform.sh +ent.platform.sh +eu.platform.sh +us.platform.sh +*.platformsh.site + +// Platter: https://platter.dev +// Submitted by Patrick Flor <patrick@platter.dev> +platter-app.com +platter-app.dev +platterp.us + +// Plesk : https://www.plesk.com/ +// Submitted by Anton Akhtyamov <program-managers@plesk.com> +pdns.page +plesk.page +pleskns.com + +// Port53 : https://port53.io/ +// Submitted by Maximilian Schieder <maxi@zeug.co> +dyn53.io + +// Positive Codes Technology Company : http://co.bn/faq.html +// Submitted by Zulfais <pc@co.bn> +co.bn + +// prgmr.com : https://prgmr.com/ +// Submitted by Sarah Newman <owner@prgmr.com> +xen.prgmr.com + +// priv.at : http://www.nic.priv.at/ +// Submitted by registry <lendl@nic.at> +priv.at + +// privacytools.io : https://www.privacytools.io/ +// Submitted by Jonah Aragon <jonah@privacytools.io> +prvcy.page + +// Protocol Labs : https://protocol.ai/ +// Submitted by Michael Burns <noc@protocol.ai> +*.dweb.link + +// Protonet GmbH : http://protonet.io +// Submitted by Martin Meier <admin@protonet.io> +protonet.io + +// Publication Presse Communication SARL : https://ppcom.fr +// Submitted by Yaacov Akiba Slama <admin@chirurgiens-dentistes-en-france.fr> +chirurgiens-dentistes-en-france.fr +byen.site + +// pubtls.org: https://www.pubtls.org +// Submitted by Kor Nielsen <kor@pubtls.org> +pubtls.org + +// QOTO, Org. +// Submitted by Jeffrey Phillips Freeman <jeffrey.freeman@qoto.org> +qoto.io + +// Qualifio : https://qualifio.com/ +// Submitted by Xavier De Cock <xdecock@gmail.com> +qualifioapp.com + +// QuickBackend: https://www.quickbackend.com +// Submitted by Dani Biro <dani@pymet.com> +qbuser.com + +// Redstar Consultants : https://www.redstarconsultants.com/ +// Submitted by Jons Slemmer <jons@redstarconsultants.com> +instantcloud.cn + +// Russian Academy of Sciences +// Submitted by Tech Support <support@rasnet.ru> +ras.ru + +// QA2 +// Submitted by Daniel Dent (https://www.danieldent.com/) +qa2.com + +// QCX +// Submitted by Cassandra Beelen <cassandra@beelen.one> +qcx.io +*.sys.qcx.io + +// QNAP System Inc : https://www.qnap.com +// Submitted by Nick Chang <nickchang@qnap.com> +dev-myqnapcloud.com +alpha-myqnapcloud.com +myqnapcloud.com + +// Quip : https://quip.com +// Submitted by Patrick Linehan <plinehan@quip.com> +*.quipelements.com + +// Qutheory LLC : http://qutheory.io +// Submitted by Jonas Schwartz <jonas@qutheory.io> +vapor.cloud +vaporcloud.io + +// Rackmaze LLC : https://www.rackmaze.com +// Submitted by Kirill Pertsev <kika@rackmaze.com> +rackmaze.com +rackmaze.net + +// Rakuten Games, Inc : https://dev.viberplay.io +// Submitted by Joshua Zhang <public-suffix@rgames.jp> +g.vbrplsbx.io + +// Rancher Labs, Inc : https://rancher.com +// Submitted by Vincent Fiduccia <domains@rancher.com> +*.on-k3s.io +*.on-rancher.cloud +*.on-rio.io + +// Read The Docs, Inc : https://www.readthedocs.org +// Submitted by David Fischer <team@readthedocs.org> +readthedocs.io + +// Red Hat, Inc. OpenShift : https://openshift.redhat.com/ +// Submitted by Tim Kramer <tkramer@rhcloud.com> +rhcloud.com + +// Render : https://render.com +// Submitted by Anurag Goel <dev@render.com> +app.render.com +onrender.com + +// Repl.it : https://repl.it +// Submitted by Mason Clayton <mason@repl.it> +repl.co +repl.run + +// Resin.io : https://resin.io +// Submitted by Tim Perry <tim@resin.io> +resindevice.io +devices.resinstaging.io + +// RethinkDB : https://www.rethinkdb.com/ +// Submitted by Chris Kastorff <info@rethinkdb.com> +hzc.io + +// Revitalised Limited : http://www.revitalised.co.uk +// Submitted by Jack Price <jack@revitalised.co.uk> +wellbeingzone.eu +wellbeingzone.co.uk + +// Rochester Institute of Technology : http://www.rit.edu/ +// Submitted by Jennifer Herting <jchits@rit.edu> +git-pages.rit.edu + +// Sandstorm Development Group, Inc. : https://sandcats.io/ +// Submitted by Asheesh Laroia <asheesh@sandstorm.io> +sandcats.io + +// SBE network solutions GmbH : https://www.sbe.de/ +// Submitted by Norman Meilick <nm@sbe.de> +logoip.de +logoip.com + +// schokokeks.org GbR : https://schokokeks.org/ +// Submitted by Hanno Böck <hanno@schokokeks.org> +schokokeks.net + +// Scottish Government: https://www.gov.scot +// Submitted by Martin Ellis <martin.ellis@gov.scot> +gov.scot + +// Scry Security : http://www.scrysec.com +// Submitted by Shante Adam <shante@skyhat.io> +scrysec.com + +// Securepoint GmbH : https://www.securepoint.de +// Submitted by Erik Anders <erik.anders@securepoint.de> +firewall-gateway.com +firewall-gateway.de +my-gateway.de +my-router.de +spdns.de +spdns.eu +firewall-gateway.net +my-firewall.org +myfirewall.org +spdns.org + +// Seidat : https://www.seidat.com +// Submitted by Artem Kondratev <accounts@seidat.com> +seidat.net + +// Senseering GmbH : https://www.senseering.de +// Submitted by Felix Mönckemeyer <f.moenckemeyer@senseering.de> +senseering.net + +// Service Online LLC : http://drs.ua/ +// Submitted by Serhii Bulakh <support@drs.ua> +biz.ua +co.ua +pp.ua + +// ShiftEdit : https://shiftedit.net/ +// Submitted by Adam Jimenez <adam@shiftcreate.com> +shiftedit.io + +// Shopblocks : http://www.shopblocks.com/ +// Submitted by Alex Bowers <alex@shopblocks.com> +myshopblocks.com + +// Shopify : https://www.shopify.com +// Submitted by Alex Richter <alex.richter@shopify.com> +myshopify.com + +// Shopit : https://www.shopitcommerce.com/ +// Submitted by Craig McMahon <craig@shopitcommerce.com> +shopitsite.com + +// shopware AG : https://shopware.com +// Submitted by Jens Küper <cloud@shopware.com> +shopware.store + +// Siemens Mobility GmbH +// Submitted by Oliver Graebner <security@mo-siemens.io> +mo-siemens.io + +// SinaAppEngine : http://sae.sina.com.cn/ +// Submitted by SinaAppEngine <saesupport@sinacloud.com> +1kapp.com +appchizi.com +applinzi.com +sinaapp.com +vipsinaapp.com + +// Siteleaf : https://www.siteleaf.com/ +// Submitted by Skylar Challand <support@siteleaf.com> +siteleaf.net + +// Skyhat : http://www.skyhat.io +// Submitted by Shante Adam <shante@skyhat.io> +bounty-full.com +alpha.bounty-full.com +beta.bounty-full.com + +// Small Technology Foundation : https://small-tech.org +// Submitted by Aral Balkan <aral@small-tech.org> +small-web.org + +// Snowplow Analytics : https://snowplowanalytics.com/ +// Submitted by Ian Streeter <ian@snowplowanalytics.com> +try-snowplow.com + +// Stackhero : https://www.stackhero.io +// Submitted by Adrien Gillon <adrien+public-suffix-list@stackhero.io> +stackhero-network.com + +// staticland : https://static.land +// Submitted by Seth Vincent <sethvincent@gmail.com> +static.land +dev.static.land +sites.static.land + +// Sony Interactive Entertainment LLC : https://sie.com/ +// Submitted by David Coles <david.coles@sony.com> +playstation-cloud.com + +// SourceLair PC : https://www.sourcelair.com +// Submitted by Antonis Kalipetis <akalipetis@sourcelair.com> +apps.lair.io +*.stolos.io + +// SpaceKit : https://www.spacekit.io/ +// Submitted by Reza Akhavan <spacekit.io@gmail.com> +spacekit.io + +// SpeedPartner GmbH: https://www.speedpartner.de/ +// Submitted by Stefan Neufeind <info@speedpartner.de> +customer.speedpartner.de + +// Standard Library : https://stdlib.com +// Submitted by Jacob Lee <jacob@stdlib.com> +api.stdlib.com + +// Storj Labs Inc. : https://storj.io/ +// Submitted by Philip Hutchins <hostmaster@storj.io> +storj.farm + +// Studenten Net Twente : http://www.snt.utwente.nl/ +// Submitted by Silke Hofstra <syscom@snt.utwente.nl> +utwente.io + +// Student-Run Computing Facility : https://www.srcf.net/ +// Submitted by Edwin Balani <sysadmins@srcf.net> +soc.srcf.net +user.srcf.net + +// Sub 6 Limited: http://www.sub6.com +// Submitted by Dan Miller <dm@sub6.com> +temp-dns.com + +// Symfony, SAS : https://symfony.com/ +// Submitted by Fabien Potencier <fabien@symfony.com> +*.s5y.io +*.sensiosite.cloud + +// Syncloud : https://syncloud.org +// Submitted by Boris Rybalkin <syncloud@syncloud.it> +syncloud.it + +// Synology, Inc. : https://www.synology.com/ +// Submitted by Rony Weng <ronyweng@synology.com> +diskstation.me +dscloud.biz +dscloud.me +dscloud.mobi +dsmynas.com +dsmynas.net +dsmynas.org +familyds.com +familyds.net +familyds.org +i234.me +myds.me +synology.me +vpnplus.to +direct.quickconnect.to + +// TAIFUN Software AG : http://taifun-software.de +// Submitted by Bjoern Henke <dev-server@taifun-software.de> +taifun-dns.de + +// TASK geographical domains (www.task.gda.pl/uslugi/dns) +gda.pl +gdansk.pl +gdynia.pl +med.pl +sopot.pl + +// Teckids e.V. : https://www.teckids.org +// Submitted by Dominik George <dominik.george@teckids.org> +edugit.org + +// Telebit : https://telebit.cloud +// Submitted by AJ ONeal <aj@telebit.cloud> +telebit.app +telebit.io +*.telebit.xyz + +// The Gwiddle Foundation : https://gwiddlefoundation.org.uk +// Submitted by Joshua Bayfield <joshua.bayfield@gwiddlefoundation.org.uk> +gwiddle.co.uk + +// Thingdust AG : https://thingdust.com/ +// Submitted by Adrian Imboden <adi@thingdust.com> +thingdustdata.com +cust.dev.thingdust.io +cust.disrec.thingdust.io +cust.prod.thingdust.io +cust.testing.thingdust.io +*.firenet.ch +*.svc.firenet.ch + +// Tlon.io : https://tlon.io +// Submitted by Mark Staarink <mark@tlon.io> +arvo.network +azimuth.network +tlon.network + +// Tor Project, Inc. : https://torproject.org +// Submitted by Antoine Beaupré <anarcat@torproject.org +torproject.net +pages.torproject.net + +// TownNews.com : http://www.townnews.com +// Submitted by Dustin Ward <dward@townnews.com> +bloxcms.com +townnews-staging.com + +// TrafficPlex GmbH : https://www.trafficplex.de/ +// Submitted by Phillipp Röll <phillipp.roell@trafficplex.de> +12hp.at +2ix.at +4lima.at +lima-city.at +12hp.ch +2ix.ch +4lima.ch +lima-city.ch +trafficplex.cloud +de.cool +12hp.de +2ix.de +4lima.de +lima-city.de +1337.pictures +clan.rip +lima-city.rocks +webspace.rocks +lima.zone + +// TransIP : https://www.transip.nl +// Submitted by Rory Breuk <rbreuk@transip.nl> +*.transurl.be +*.transurl.eu +*.transurl.nl + +// TuxFamily : http://tuxfamily.org +// Submitted by TuxFamily administrators <adm@staff.tuxfamily.org> +tuxfamily.org + +// TwoDNS : https://www.twodns.de/ +// Submitted by TwoDNS-Support <support@two-dns.de> +dd-dns.de +diskstation.eu +diskstation.org +dray-dns.de +draydns.de +dyn-vpn.de +dynvpn.de +mein-vigor.de +my-vigor.de +my-wan.de +syno-ds.de +synology-diskstation.de +synology-ds.de + +// Uberspace : https://uberspace.de +// Submitted by Moritz Werner <mwerner@jonaspasche.com> +uber.space +*.uberspace.de + +// UDR Limited : http://www.udr.hk.com +// Submitted by registry <hostmaster@udr.hk.com> +hk.com +hk.org +ltd.hk +inc.hk + +// United Gameserver GmbH : https://united-gameserver.de +// Submitted by Stefan Schwarz <sysadm@united-gameserver.de> +virtualuser.de +virtual-user.de + +// urown.net : https://urown.net +// Submitted by Hostmaster <hostmaster@urown.net> +urown.cloud +dnsupdate.info + +// .US +// Submitted by Ed Moore <Ed.Moore@lib.de.us> +lib.de.us + +// VeryPositive SIA : http://very.lv +// Submitted by Danko Aleksejevs <danko@very.lv> +2038.io + +// Vercel, Inc : https://vercel.com/ +// Submitted by Connor Davis <security@vercel.com> +vercel.app +vercel.dev +now.sh + +// Viprinet Europe GmbH : http://www.viprinet.com +// Submitted by Simon Kissel <hostmaster@viprinet.com> +router.management + +// Virtual-Info : https://www.virtual-info.info/ +// Submitted by Adnan RIHAN <hostmaster@v-info.info> +v-info.info + +// Voorloper.com: https://voorloper.com +// Submitted by Nathan van Bakel <info@voorloper.com> +voorloper.cloud + +// Voxel.sh DNS : https://voxel.sh/dns/ +// Submitted by Mia Rehlinger <dns@voxel.sh> +neko.am +nyaa.am +be.ax +cat.ax +es.ax +eu.ax +gg.ax +mc.ax +us.ax +xy.ax +nl.ci +xx.gl +app.gp +blog.gt +de.gt +to.gt +be.gy +cc.hn +blog.kg +io.kg +jp.kg +tv.kg +uk.kg +us.kg +de.ls +at.md +de.md +jp.md +to.md +uwu.nu +indie.porn +vxl.sh +ch.tc +me.tc +we.tc +nyan.to +at.vg +blog.vu +dev.vu +me.vu + +// V.UA Domain Administrator : https://domain.v.ua/ +// Submitted by Serhii Rostilo <sergey@rostilo.kiev.ua> +v.ua + +// Waffle Computer Inc., Ltd. : https://docs.waffleinfo.com +// Submitted by Masayuki Note <masa@blade.wafflecell.com> +wafflecell.com + +// WapBlog.ID : https://www.wapblog.id +// Submitted by Fajar Sodik <official@wapblog.id> +idnblogger.com +indowapblog.com +bloger.id +wblog.id +wbq.me +fastblog.net + +// WebHare bv: https://www.webhare.com/ +// Submitted by Arnold Hendriks <info@webhare.com> +*.webhare.dev + +// WeDeploy by Liferay, Inc. : https://www.wedeploy.com +// Submitted by Henrique Vicente <security@wedeploy.com> +wedeploy.io +wedeploy.me +wedeploy.sh + +// Western Digital Technologies, Inc : https://www.wdc.com +// Submitted by Jung Jin <jungseok.jin@wdc.com> +remotewd.com + +// WIARD Enterprises : https://wiardweb.com +// Submitted by Kidd Hustle <kiddhustle@wiardweb.com> +pages.wiardweb.com + +// Wikimedia Labs : https://wikitech.wikimedia.org +// Submitted by Arturo Borrero Gonzalez <aborrero@wikimedia.org> +wmflabs.org +toolforge.org +wmcloud.org + +// WISP : https://wisp.gg +// Submitted by Stepan Fedotov <stepan@wisp.gg> +panel.gg +daemon.panel.gg + +// WoltLab GmbH : https://www.woltlab.com +// Submitted by Tim Düsterhus <security@woltlab.cloud> +woltlab-demo.com +myforum.community +community-pro.de +diskussionsbereich.de +community-pro.net +meinforum.net + +// www.com.vc : http://www.com.vc +// Submitted by Li Hui <lihui@sinopub.com> +cn.vu + +// XenonCloud GbR: https://xenoncloud.net +// Submitted by Julian Uphoff <publicsuffixlist@xenoncloud.net> +half.host + +// XnBay Technology : http://www.xnbay.com/ +// Submitted by XnBay Developer <developer.xncloud@gmail.com> +xnbay.com +u2.xnbay.com +u2-local.xnbay.com + +// XS4ALL Internet bv : https://www.xs4all.nl/ +// Submitted by Daniel Mostertman <unixbeheer+publicsuffix@xs4all.net> +cistron.nl +demon.nl +xs4all.space + +// Yandex.Cloud LLC: https://cloud.yandex.com +// Submitted by Alexander Lodin <security+psl@yandex-team.ru> +yandexcloud.net +storage.yandexcloud.net +website.yandexcloud.net + +// YesCourse Pty Ltd : https://yescourse.com +// Submitted by Atul Bhouraskar <atul@yescourse.com> +official.academy + +// Yola : https://www.yola.com/ +// Submitted by Stefano Rivera <stefano@yola.com> +yolasite.com + +// Yombo : https://yombo.net +// Submitted by Mitch Schwenk <mitch@yombo.net> +ybo.faith +yombo.me +homelink.one +ybo.party +ybo.review +ybo.science +ybo.trade + +// Yunohost : https://yunohost.org +// Submitted by Valentin Grimaud <security@yunohost.org> +nohost.me +noho.st + +// ZaNiC : http://www.za.net/ +// Submitted by registry <hostmaster@nic.za.net> +za.net +za.org + +// Zine EOOD : https://zine.bg/ +// Submitted by Martin Angelov <martin@zine.bg> +bss.design + +// Zitcom A/S : https://www.zitcom.dk +// Submitted by Emil Stahl <esp@zitcom.dk> +basicserver.io +virtualserver.io +enterprisecloud.nu + +// Mintere : https://mintere.com/ +// Submitted by Ben Aubin <security@mintere.com> +mintere.site + +// Cityhost LLC : https://cityhost.ua +// Submitted by Maksym Rivtin <support@cityhost.net.ua> +cx.ua + +// WP Engine : https://wpengine.com/ +// Submitted by Michael Smith <michael.smith@wpengine.com> +// Submitted by Brandon DuRette <brandon.durette@wpengine.com> +wpenginepowered.com +js.wpenginepowered.com + +// Impertrix Solutions : <https://impertrixcdn.com> +// Submitted by Zhixiang Zhao <csuite@impertrix.com> +impertrixcdn.com +impertrix.com + +// GignoSystemJapan: http://gsj.bz +// Submitted by GignoSystemJapan <kakutou-ec@gsj.bz> +gsj.bz + +// Rusnames Limited: http://rusnames.ru/ +// Submitted by Sergey Zotov <admin@rusnames.ru> +биз.рус +ком.рус +крым.рус +мир.рус +мск.рус +орг.рус +самара.рус +сочи.рус +спб.рус +я.рус +// ===END PRIVATE DOMAINS=== diff --git a/netwerk/dns/mdns/libmdns/DNSServiceDiscovery.jsm b/netwerk/dns/mdns/libmdns/DNSServiceDiscovery.jsm new file mode 100644 index 0000000000..bf153ea1bd --- /dev/null +++ b/netwerk/dns/mdns/libmdns/DNSServiceDiscovery.jsm @@ -0,0 +1,219 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { AppConstants } = ChromeUtils.import( + "resource://gre/modules/AppConstants.jsm" +); +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); + +const { MulticastDNS } = ChromeUtils.import( + AppConstants.platform == "android" && + !Services.prefs.getBoolPref("network.mdns.use_js_fallback") + ? "resource://gre/modules/MulticastDNSAndroid.jsm" + : "resource://gre/modules/MulticastDNS.jsm" +); + +const DNSSERVICEINFO_CONTRACT_ID = + "@mozilla.org/toolkit/components/mdnsresponder/dns-info;1"; + +function log(aMsg) { + dump("-*- nsDNSServiceDiscovery.js : " + aMsg + "\n"); +} + +function generateUuid() { + var uuidGenerator = Cc["@mozilla.org/uuid-generator;1"].getService( + Ci.nsIUUIDGenerator + ); + return uuidGenerator.generateUUID().toString(); +} + +// Helper class to transform return objects to correct type. +function ListenerWrapper(aListener, aMdns) { + this.listener = aListener; + this.mdns = aMdns; + + this.discoveryStarting = false; + this.stopDiscovery = false; + + this.registrationStarting = false; + this.stopRegistration = false; + + this.uuid = generateUuid(); +} + +ListenerWrapper.prototype = { + // Helper function for transforming an Object into nsIDNSServiceInfo. + makeServiceInfo(aServiceInfo) { + let serviceInfo = Cc[DNSSERVICEINFO_CONTRACT_ID].createInstance( + Ci.nsIDNSServiceInfo + ); + + for (let name of [ + "host", + "address", + "port", + "serviceName", + "serviceType", + ]) { + try { + serviceInfo[name] = aServiceInfo[name]; + } catch (e) { + // ignore exceptions + } + } + + let attributes; + try { + attributes = _toPropertyBag2(aServiceInfo.attributes); + } catch (err) { + // Ignore unset attributes in object. + log("Caught unset attributes error: " + err + " - " + err.stack); + attributes = Cc["@mozilla.org/hash-property-bag;1"].createInstance( + Ci.nsIWritablePropertyBag2 + ); + } + serviceInfo.attributes = attributes; + + return serviceInfo; + }, + + /* transparent types */ + onDiscoveryStarted(aServiceType) { + this.discoveryStarting = false; + this.listener.onDiscoveryStarted(aServiceType); + + if (this.stopDiscovery) { + this.mdns.stopDiscovery(aServiceType, this); + } + }, + onDiscoveryStopped(aServiceType) { + this.listener.onDiscoveryStopped(aServiceType); + }, + onStartDiscoveryFailed(aServiceType, aErrorCode) { + log("onStartDiscoveryFailed: " + aServiceType + " (" + aErrorCode + ")"); + this.discoveryStarting = false; + this.stopDiscovery = true; + this.listener.onStartDiscoveryFailed(aServiceType, aErrorCode); + }, + onStopDiscoveryFailed(aServiceType, aErrorCode) { + log("onStopDiscoveryFailed: " + aServiceType + " (" + aErrorCode + ")"); + this.listener.onStopDiscoveryFailed(aServiceType, aErrorCode); + }, + + /* transform types */ + onServiceFound(aServiceInfo) { + this.listener.onServiceFound(this.makeServiceInfo(aServiceInfo)); + }, + onServiceLost(aServiceInfo) { + this.listener.onServiceLost(this.makeServiceInfo(aServiceInfo)); + }, + onServiceRegistered(aServiceInfo) { + this.registrationStarting = false; + this.listener.onServiceRegistered(this.makeServiceInfo(aServiceInfo)); + + if (this.stopRegistration) { + this.mdns.unregisterService(aServiceInfo, this); + } + }, + onServiceUnregistered(aServiceInfo) { + this.listener.onServiceUnregistered(this.makeServiceInfo(aServiceInfo)); + }, + onServiceResolved(aServiceInfo) { + this.listener.onServiceResolved(this.makeServiceInfo(aServiceInfo)); + }, + + onRegistrationFailed(aServiceInfo, aErrorCode) { + log("onRegistrationFailed: (" + aErrorCode + ")"); + this.registrationStarting = false; + this.stopRegistration = true; + this.listener.onRegistrationFailed( + this.makeServiceInfo(aServiceInfo), + aErrorCode + ); + }, + onUnregistrationFailed(aServiceInfo, aErrorCode) { + log("onUnregistrationFailed: (" + aErrorCode + ")"); + this.listener.onUnregistrationFailed( + this.makeServiceInfo(aServiceInfo), + aErrorCode + ); + }, + onResolveFailed(aServiceInfo, aErrorCode) { + log("onResolveFailed: (" + aErrorCode + ")"); + this.listener.onResolveFailed( + this.makeServiceInfo(aServiceInfo), + aErrorCode + ); + }, +}; + +function nsDNSServiceDiscovery() { + log("nsDNSServiceDiscovery"); + this.mdns = new MulticastDNS(); +} + +nsDNSServiceDiscovery.prototype = { + QueryInterface: ChromeUtils.generateQI([ + "nsISupportsWeakReference", + "nsIDNSServiceDiscovery", + ]), + + startDiscovery(aServiceType, aListener) { + log("startDiscovery"); + let listener = new ListenerWrapper(aListener, this.mdns); + listener.discoveryStarting = true; + this.mdns.startDiscovery(aServiceType, listener); + + return { + QueryInterface: ChromeUtils.generateQI(["nsICancelable"]), + cancel: function() { + if (this.discoveryStarting || this.stopDiscovery) { + this.stopDiscovery = true; + return; + } + this.mdns.stopDiscovery(aServiceType, listener); + }.bind(listener), + }; + }, + + registerService(aServiceInfo, aListener) { + log("registerService"); + let listener = new ListenerWrapper(aListener, this.mdns); + listener.registrationStarting = true; + this.mdns.registerService(aServiceInfo, listener); + + return { + QueryInterface: ChromeUtils.generateQI(["nsICancelable"]), + cancel: function() { + if (this.registrationStarting || this.stopRegistration) { + this.stopRegistration = true; + return; + } + this.mdns.unregisterService(aServiceInfo, listener); + }.bind(listener), + }; + }, + + resolveService(aServiceInfo, aListener) { + log("resolveService"); + this.mdns.resolveService(aServiceInfo, new ListenerWrapper(aListener)); + }, +}; + +var EXPORTED_SYMBOLS = ["nsDNSServiceDiscovery"]; + +function _toPropertyBag2(obj) { + if (obj.QueryInterface) { + return obj.QueryInterface(Ci.nsIPropertyBag2); + } + + let result = Cc["@mozilla.org/hash-property-bag;1"].createInstance( + Ci.nsIWritablePropertyBag2 + ); + for (let name in obj) { + result.setPropertyAsAString(name, obj[name]); + } + return result; +} diff --git a/netwerk/dns/mdns/libmdns/MDNSResponderOperator.cpp b/netwerk/dns/mdns/libmdns/MDNSResponderOperator.cpp new file mode 100644 index 0000000000..ae780b614d --- /dev/null +++ b/netwerk/dns/mdns/libmdns/MDNSResponderOperator.cpp @@ -0,0 +1,719 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "MDNSResponderOperator.h" +#include "MDNSResponderReply.h" +#include "mozilla/EndianUtils.h" +#include "mozilla/Logging.h" +#include "mozilla/ScopeExit.h" +#include "mozilla/Unused.h" +#include "nsComponentManagerUtils.h" +#include "nsCOMPtr.h" +#include "nsDebug.h" +#include "nsDNSServiceInfo.h" +#include "nsHashPropertyBag.h" +#include "nsIProperty.h" +#include "nsISimpleEnumerator.h" +#include "nsIVariant.h" +#include "nsServiceManagerUtils.h" +#include "nsNetAddr.h" +#include "nsNetCID.h" +#include "nsSocketTransportService2.h" +#include "nsThreadUtils.h" +#include "nsXPCOMCID.h" +#include "private/pprio.h" + +#include "nsASocketHandler.h" + +namespace mozilla { +namespace net { + +static LazyLogModule gMDNSLog("MDNSResponderOperator"); +#undef LOG_I +#define LOG_I(...) \ + MOZ_LOG(mozilla::net::gMDNSLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) +#undef LOG_E +#define LOG_E(...) \ + MOZ_LOG(mozilla::net::gMDNSLog, mozilla::LogLevel::Error, (__VA_ARGS__)) + +class MDNSResponderOperator::ServiceWatcher final : public nsASocketHandler { + public: + NS_DECL_THREADSAFE_ISUPPORTS + + // nsASocketHandler methods + virtual void OnSocketReady(PRFileDesc* fd, int16_t outFlags) override { + MOZ_ASSERT(OnSocketThread(), "not on socket thread"); + MOZ_ASSERT(fd == mFD); + + if (outFlags & (PR_POLL_ERR | PR_POLL_HUP | PR_POLL_NVAL)) { + LOG_E("error polling on listening socket (%p)", fd); + mCondition = NS_ERROR_UNEXPECTED; + } + + if (!(outFlags & PR_POLL_READ)) { + return; + } + + DNSServiceProcessResult(mService); + } + + virtual void OnSocketDetached(PRFileDesc* fd) override { + MOZ_ASSERT(OnSocketThread(), "not on socket thread"); + MOZ_ASSERT(mThread); + MOZ_ASSERT(fd == mFD); + + if (!mFD) { + return; + } + + // Bug 1175387: do not double close the handle here. + PR_ChangeFileDescNativeHandle(mFD, -1); + PR_Close(mFD); + mFD = nullptr; + + mThread->Dispatch( + NewRunnableMethod("MDNSResponderOperator::ServiceWatcher::Deallocate", + this, &ServiceWatcher::Deallocate), + NS_DISPATCH_NORMAL); + } + + virtual void IsLocal(bool* aIsLocal) override { *aIsLocal = true; } + + virtual void KeepWhenOffline(bool* aKeepWhenOffline) override { + *aKeepWhenOffline = true; + } + + virtual uint64_t ByteCountSent() override { return 0; } + virtual uint64_t ByteCountReceived() override { return 0; } + + explicit ServiceWatcher(DNSServiceRef aService, + MDNSResponderOperator* aOperator) + : mThread(nullptr), + mSts(nullptr), + mOperatorHolder(aOperator), + mService(aService), + mFD(nullptr), + mAttached(false) { + if (!gSocketTransportService) { + nsCOMPtr<nsISocketTransportService> sts = + do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID); + } + } + + nsresult Init() { + MOZ_ASSERT(!OnSocketThread(), "on socket thread"); + mThread = NS_GetCurrentThread(); + + if (!mService) { + return NS_OK; + } + + if (!gSocketTransportService) { + return NS_ERROR_FAILURE; + } + mSts = gSocketTransportService; + + int osfd = DNSServiceRefSockFD(mService); + if (osfd == -1) { + return NS_ERROR_FAILURE; + } + + mFD = PR_ImportFile(osfd); + return PostEvent("MDNSResponderOperator::ServiceWatcher::OnMsgAttach", + &ServiceWatcher::OnMsgAttach); + } + + void Close() { + MOZ_ASSERT(!OnSocketThread(), "on socket thread"); + + if (!gSocketTransportService) { + Deallocate(); + return; + } + + PostEvent("MDNSResponderOperator::ServiceWatcher::OnMsgClose", + &ServiceWatcher::OnMsgClose); + } + + private: + ~ServiceWatcher() = default; + + void Deallocate() { + if (mService) { + DNSServiceRefDeallocate(mService); + mService = nullptr; + } + mOperatorHolder = nullptr; + } + + nsresult PostEvent(const char* aName, void (ServiceWatcher::*func)(void)) { + return gSocketTransportService->Dispatch( + NewRunnableMethod(aName, this, func), NS_DISPATCH_NORMAL); + } + + void OnMsgClose() { + MOZ_ASSERT(OnSocketThread(), "not on socket thread"); + + if (NS_FAILED(mCondition)) { + return; + } + + // tear down socket. this signals the STS to detach our socket handler. + mCondition = NS_BINDING_ABORTED; + + // if we are attached, then socket transport service will call our + // OnSocketDetached method automatically. Otherwise, we have to call it + // (and thus close the socket) manually. + if (!mAttached) { + OnSocketDetached(mFD); + } + } + + void OnMsgAttach() { + MOZ_ASSERT(OnSocketThread(), "not on socket thread"); + + if (NS_FAILED(mCondition)) { + return; + } + + mCondition = TryAttach(); + + // if we hit an error while trying to attach then bail... + if (NS_FAILED(mCondition)) { + NS_ASSERTION(!mAttached, "should not be attached already"); + OnSocketDetached(mFD); + } + } + + nsresult TryAttach() { + MOZ_ASSERT(OnSocketThread(), "not on socket thread"); + + nsresult rv; + + if (!gSocketTransportService) { + return NS_ERROR_FAILURE; + } + + // + // find out if it is going to be ok to attach another socket to the STS. + // if not then we have to wait for the STS to tell us that it is ok. + // the notification is asynchronous, which means that when we could be + // in a race to call AttachSocket once notified. for this reason, when + // we get notified, we just re-enter this function. as a result, we are + // sure to ask again before calling AttachSocket. in this way we deal + // with the race condition. though it isn't the most elegant solution, + // it is far simpler than trying to build a system that would guarantee + // FIFO ordering (which wouldn't even be that valuable IMO). see bug + // 194402 for more info. + // + if (!gSocketTransportService->CanAttachSocket()) { + nsCOMPtr<nsIRunnable> event = NewRunnableMethod( + "MDNSResponderOperator::ServiceWatcher::OnMsgAttach", this, + &ServiceWatcher::OnMsgAttach); + + nsresult rv = gSocketTransportService->NotifyWhenCanAttachSocket(event); + if (NS_FAILED(rv)) { + return rv; + } + } + + // + // ok, we can now attach our socket to the STS for polling + // + rv = gSocketTransportService->AttachSocket(mFD, this); + if (NS_FAILED(rv)) { + return rv; + } + + mAttached = true; + + // + // now, configure our poll flags for listening... + // + mPollFlags = (PR_POLL_READ | PR_POLL_EXCEPT); + + return NS_OK; + } + + nsCOMPtr<nsIThread> mThread; + RefPtr<nsSocketTransportService> mSts; + RefPtr<MDNSResponderOperator> mOperatorHolder; + DNSServiceRef mService; + PRFileDesc* mFD; + bool mAttached; +}; + +NS_IMPL_ISUPPORTS(MDNSResponderOperator::ServiceWatcher, nsISupports) + +MDNSResponderOperator::MDNSResponderOperator() + : mService(nullptr), + mWatcher(nullptr), + mThread(NS_GetCurrentThread()), + mIsCancelled(false) {} + +MDNSResponderOperator::~MDNSResponderOperator() { Stop(); } + +nsresult MDNSResponderOperator::Start() { + if (mIsCancelled) { + return NS_OK; + } + + if (IsServing()) { + Stop(); + } + + return NS_OK; +} + +nsresult MDNSResponderOperator::Stop() { return ResetService(nullptr); } + +nsresult MDNSResponderOperator::ResetService(DNSServiceRef aService) { + nsresult rv; + + if (aService != mService) { + if (mWatcher) { + mWatcher->Close(); + mWatcher = nullptr; + } + + if (aService) { + RefPtr<ServiceWatcher> watcher = new ServiceWatcher(aService, this); + if (NS_WARN_IF(NS_FAILED(rv = watcher->Init()))) { + return rv; + } + mWatcher = watcher; + } + + mService = aService; + } + return NS_OK; +} + +BrowseOperator::BrowseOperator(const nsACString& aServiceType, + nsIDNSServiceDiscoveryListener* aListener) + : MDNSResponderOperator(), + mServiceType(aServiceType), + mListener(aListener) {} + +nsresult BrowseOperator::Start() { + nsresult rv; + if (NS_WARN_IF(NS_FAILED(rv = MDNSResponderOperator::Start()))) { + return rv; + } + + DNSServiceRef service = nullptr; + DNSServiceErrorType err = DNSServiceBrowse( + &service, 0, kDNSServiceInterfaceIndexAny, mServiceType.get(), nullptr, + &BrowseReplyRunnable::Reply, this); + NS_WARNING_ASSERTION(kDNSServiceErr_NoError == err, "DNSServiceBrowse fail"); + + if (mListener) { + if (kDNSServiceErr_NoError == err) { + mListener->OnDiscoveryStarted(mServiceType); + } else { + mListener->OnStartDiscoveryFailed(mServiceType, err); + } + } + + if (NS_WARN_IF(kDNSServiceErr_NoError != err)) { + return NS_ERROR_FAILURE; + } + + return ResetService(service); +} + +nsresult BrowseOperator::Stop() { + bool isServing = IsServing(); + nsresult rv = MDNSResponderOperator::Stop(); + + if (isServing && mListener) { + if (NS_SUCCEEDED(rv)) { + mListener->OnDiscoveryStopped(mServiceType); + } else { + mListener->OnStopDiscoveryFailed(mServiceType, static_cast<uint32_t>(rv)); + } + } + + return rv; +} + +void BrowseOperator::Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, + const nsACString& aServiceName, + const nsACString& aRegType, + const nsACString& aReplyDomain) { + MOZ_ASSERT(GetThread() == NS_GetCurrentThread()); + + if (NS_WARN_IF(kDNSServiceErr_NoError != aErrorCode)) { + LOG_E("BrowseOperator::Reply (%d)", aErrorCode); + if (mListener) { + mListener->OnStartDiscoveryFailed(mServiceType, aErrorCode); + } + return; + } + + if (!mListener) { + return; + } + nsCOMPtr<nsIDNSServiceInfo> info = new nsDNSServiceInfo(); + + if (NS_WARN_IF(!info)) { + return; + } + if (NS_WARN_IF(NS_FAILED(info->SetServiceName(aServiceName)))) { + return; + } + if (NS_WARN_IF(NS_FAILED(info->SetServiceType(aRegType)))) { + return; + } + if (NS_WARN_IF(NS_FAILED(info->SetDomainName(aReplyDomain)))) { + return; + } + + if (aFlags & kDNSServiceFlagsAdd) { + mListener->OnServiceFound(info); + } else { + mListener->OnServiceLost(info); + } +} + +RegisterOperator::RegisterOperator(nsIDNSServiceInfo* aServiceInfo, + nsIDNSRegistrationListener* aListener) + : MDNSResponderOperator(), + mServiceInfo(aServiceInfo), + mListener(aListener) {} + +nsresult RegisterOperator::Start() { + nsresult rv; + + rv = MDNSResponderOperator::Start(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + uint16_t port; + if (NS_WARN_IF(NS_FAILED(rv = mServiceInfo->GetPort(&port)))) { + return rv; + } + nsAutoCString type; + if (NS_WARN_IF(NS_FAILED(rv = mServiceInfo->GetServiceType(type)))) { + return rv; + } + + TXTRecordRef txtRecord; + char buf[TXT_BUFFER_SIZE] = {0}; + TXTRecordCreate(&txtRecord, TXT_BUFFER_SIZE, buf); + + nsCOMPtr<nsIPropertyBag2> attributes; + if (NS_FAILED(rv = mServiceInfo->GetAttributes(getter_AddRefs(attributes)))) { + LOG_I("register: no attributes"); + } else { + nsCOMPtr<nsISimpleEnumerator> enumerator; + if (NS_WARN_IF(NS_FAILED( + rv = attributes->GetEnumerator(getter_AddRefs(enumerator))))) { + return rv; + } + + bool hasMoreElements; + while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreElements)) && + hasMoreElements) { + nsCOMPtr<nsISupports> element; + MOZ_ALWAYS_SUCCEEDS(enumerator->GetNext(getter_AddRefs(element))); + nsCOMPtr<nsIProperty> property = do_QueryInterface(element); + MOZ_ASSERT(property); + + nsAutoString name; + nsCOMPtr<nsIVariant> value; + MOZ_ALWAYS_SUCCEEDS(property->GetName(name)); + MOZ_ALWAYS_SUCCEEDS(property->GetValue(getter_AddRefs(value))); + + nsAutoCString str; + if (NS_WARN_IF(NS_FAILED(value->GetAsACString(str)))) { + continue; + } + + TXTRecordSetValue(&txtRecord, + /* it's safe because key name is ASCII only. */ + NS_LossyConvertUTF16toASCII(name).get(), str.Length(), + str.get()); + } + } + + nsAutoCString host; + nsAutoCString name; + nsAutoCString domain; + + DNSServiceRef service = nullptr; + DNSServiceErrorType err = DNSServiceRegister( + &service, 0, 0, + NS_SUCCEEDED(mServiceInfo->GetServiceName(name)) ? name.get() : nullptr, + type.get(), + NS_SUCCEEDED(mServiceInfo->GetDomainName(domain)) ? domain.get() + : nullptr, + NS_SUCCEEDED(mServiceInfo->GetHost(host)) ? host.get() : nullptr, + NativeEndian::swapToNetworkOrder(port), TXTRecordGetLength(&txtRecord), + TXTRecordGetBytesPtr(&txtRecord), &RegisterReplyRunnable::Reply, this); + NS_WARNING_ASSERTION(kDNSServiceErr_NoError == err, + "DNSServiceRegister fail"); + + TXTRecordDeallocate(&txtRecord); + + if (NS_WARN_IF(kDNSServiceErr_NoError != err)) { + if (mListener) { + mListener->OnRegistrationFailed(mServiceInfo, err); + } + return NS_ERROR_FAILURE; + } + + return ResetService(service); +} + +nsresult RegisterOperator::Stop() { + bool isServing = IsServing(); + nsresult rv = MDNSResponderOperator::Stop(); + + if (isServing && mListener) { + if (NS_SUCCEEDED(rv)) { + mListener->OnServiceUnregistered(mServiceInfo); + } else { + mListener->OnUnregistrationFailed(mServiceInfo, + static_cast<uint32_t>(rv)); + } + } + + return rv; +} + +void RegisterOperator::Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags, + DNSServiceErrorType aErrorCode, + const nsACString& aName, + const nsACString& aRegType, + const nsACString& aDomain) { + MOZ_ASSERT(GetThread() == NS_GetCurrentThread()); + + if (kDNSServiceErr_NoError != aErrorCode) { + LOG_E("RegisterOperator::Reply (%d)", aErrorCode); + } + + if (!mListener) { + return; + } + nsCOMPtr<nsIDNSServiceInfo> info = new nsDNSServiceInfo(mServiceInfo); + if (NS_WARN_IF(NS_FAILED(info->SetServiceName(aName)))) { + return; + } + if (NS_WARN_IF(NS_FAILED(info->SetServiceType(aRegType)))) { + return; + } + if (NS_WARN_IF(NS_FAILED(info->SetDomainName(aDomain)))) { + return; + } + + if (kDNSServiceErr_NoError == aErrorCode) { + if (aFlags & kDNSServiceFlagsAdd) { + mListener->OnServiceRegistered(info); + } else { + // If a successfully-registered name later suffers a name conflict + // or similar problem and has to be deregistered, the callback will + // be invoked with the kDNSServiceFlagsAdd flag not set. + LOG_E("RegisterOperator::Reply: deregister"); + if (NS_WARN_IF(NS_FAILED(Stop()))) { + return; + } + } + } else { + mListener->OnRegistrationFailed(info, aErrorCode); + } +} + +ResolveOperator::ResolveOperator(nsIDNSServiceInfo* aServiceInfo, + nsIDNSServiceResolveListener* aListener) + : MDNSResponderOperator(), + mServiceInfo(aServiceInfo), + mListener(aListener) {} + +nsresult ResolveOperator::Start() { + nsresult rv; + if (NS_WARN_IF(NS_FAILED(rv = MDNSResponderOperator::Start()))) { + return rv; + } + + nsAutoCString name; + mServiceInfo->GetServiceName(name); + nsAutoCString type; + mServiceInfo->GetServiceType(type); + nsAutoCString domain; + mServiceInfo->GetDomainName(domain); + + LOG_I("Resolve: (%s), (%s), (%s)", name.get(), type.get(), domain.get()); + + DNSServiceRef service = nullptr; + DNSServiceErrorType err = DNSServiceResolve( + &service, 0, kDNSServiceInterfaceIndexAny, name.get(), type.get(), + domain.get(), (DNSServiceResolveReply)&ResolveReplyRunnable::Reply, this); + + if (NS_WARN_IF(kDNSServiceErr_NoError != err)) { + if (mListener) { + mListener->OnResolveFailed(mServiceInfo, err); + } + return NS_ERROR_FAILURE; + } + + return ResetService(service); +} + +void ResolveOperator::Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, + const nsACString& aFullName, + const nsACString& aHostTarget, uint16_t aPort, + uint16_t aTxtLen, const unsigned char* aTxtRecord) { + MOZ_ASSERT(GetThread() == NS_GetCurrentThread()); + + auto guard = MakeScopeExit([&] { Unused << NS_WARN_IF(NS_FAILED(Stop())); }); + + if (NS_WARN_IF(kDNSServiceErr_NoError != aErrorCode)) { + LOG_E("ResolveOperator::Reply (%d)", aErrorCode); + return; + } + + // Resolve TXT record + int count = TXTRecordGetCount(aTxtLen, aTxtRecord); + LOG_I("resolve: txt count = %d, len = %d", count, aTxtLen); + nsCOMPtr<nsIWritablePropertyBag2> attributes = new nsHashPropertyBag(); + if (NS_WARN_IF(!attributes)) { + return; + } + if (count) { + for (int i = 0; i < count; ++i) { + char key[TXT_BUFFER_SIZE] = {'\0'}; + uint8_t vSize = 0; + const void* value = nullptr; + if (kDNSServiceErr_NoError != + TXTRecordGetItemAtIndex(aTxtLen, aTxtRecord, i, TXT_BUFFER_SIZE, key, + &vSize, &value)) { + break; + } + + nsAutoCString str(reinterpret_cast<const char*>(value), vSize); + LOG_I("resolve TXT: (%d) %s=%s", vSize, key, str.get()); + + if (NS_WARN_IF(NS_FAILED(attributes->SetPropertyAsACString( + /* it's safe to convert because key name is ASCII only. */ + NS_ConvertASCIItoUTF16(key), str)))) { + break; + } + } + } + + if (!mListener) { + return; + } + nsCOMPtr<nsIDNSServiceInfo> info = new nsDNSServiceInfo(mServiceInfo); + if (NS_WARN_IF(NS_FAILED(info->SetHost(aHostTarget)))) { + return; + } + if (NS_WARN_IF(NS_FAILED(info->SetPort(aPort)))) { + return; + } + if (NS_WARN_IF(NS_FAILED(info->SetAttributes(attributes)))) { + return; + } + + if (kDNSServiceErr_NoError == aErrorCode) { + GetAddrInfor(info); + } else { + mListener->OnResolveFailed(info, aErrorCode); + Unused << NS_WARN_IF(NS_FAILED(Stop())); + } +} + +void ResolveOperator::GetAddrInfor(nsIDNSServiceInfo* aServiceInfo) { + RefPtr<GetAddrInfoOperator> getAddreOp = + new GetAddrInfoOperator(aServiceInfo, mListener); + Unused << NS_WARN_IF(NS_FAILED(getAddreOp->Start())); +} + +GetAddrInfoOperator::GetAddrInfoOperator( + nsIDNSServiceInfo* aServiceInfo, nsIDNSServiceResolveListener* aListener) + : MDNSResponderOperator(), + mServiceInfo(aServiceInfo), + mListener(aListener) {} + +nsresult GetAddrInfoOperator::Start() { + nsresult rv; + if (NS_WARN_IF(NS_FAILED(rv = MDNSResponderOperator::Start()))) { + return rv; + } + + nsAutoCString host; + mServiceInfo->GetHost(host); + + LOG_I("GetAddrInfo: (%s)", host.get()); + + DNSServiceRef service = nullptr; + DNSServiceErrorType err = DNSServiceGetAddrInfo( + &service, kDNSServiceFlagsForceMulticast, kDNSServiceInterfaceIndexAny, + kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, host.get(), + (DNSServiceGetAddrInfoReply)&GetAddrInfoReplyRunnable::Reply, this); + + if (NS_WARN_IF(kDNSServiceErr_NoError != err)) { + if (mListener) { + mListener->OnResolveFailed(mServiceInfo, err); + } + return NS_ERROR_FAILURE; + } + + return ResetService(service); +} + +void GetAddrInfoOperator::Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, + const nsACString& aHostName, + const NetAddr& aAddress, uint32_t aTTL) { + MOZ_ASSERT(GetThread() == NS_GetCurrentThread()); + + auto guard = MakeScopeExit([&] { Unused << NS_WARN_IF(NS_FAILED(Stop())); }); + + if (NS_WARN_IF(kDNSServiceErr_NoError != aErrorCode)) { + LOG_E("GetAddrInfoOperator::Reply (%d)", aErrorCode); + return; + } + + if (!mListener) { + return; + } + + NetAddr addr = aAddress; + nsCOMPtr<nsINetAddr> address = new nsNetAddr(&addr); + nsCString addressStr; + if (NS_WARN_IF(NS_FAILED(address->GetAddress(addressStr)))) { + return; + } + + nsCOMPtr<nsIDNSServiceInfo> info = new nsDNSServiceInfo(mServiceInfo); + if (NS_WARN_IF(NS_FAILED(info->SetAddress(addressStr)))) { + return; + } + + /** + * |kDNSServiceFlagsMoreComing| means this callback will be one or more + * callback events later, so this instance should be kept alive until all + * follow-up events are processed. + */ + if (aFlags & kDNSServiceFlagsMoreComing) { + guard.release(); + } + + if (kDNSServiceErr_NoError == aErrorCode) { + mListener->OnServiceResolved(info); + } else { + mListener->OnResolveFailed(info, aErrorCode); + } +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/mdns/libmdns/MDNSResponderOperator.h b/netwerk/dns/mdns/libmdns/MDNSResponderOperator.h new file mode 100644 index 0000000000..394f68777c --- /dev/null +++ b/netwerk/dns/mdns/libmdns/MDNSResponderOperator.h @@ -0,0 +1,133 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_netwerk_dns_mdns_libmdns_MDNSResponderOperator_h +#define mozilla_netwerk_dns_mdns_libmdns_MDNSResponderOperator_h + +#include "dns_sd.h" +#include "mozilla/Atomics.h" +#include "mozilla/RefPtr.h" +#include "nsCOMPtr.h" +#include "nsIDNSServiceDiscovery.h" +#include "nsIThread.h" +#include "nsString.h" + +namespace mozilla { +namespace net { + +class MDNSResponderOperator { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MDNSResponderOperator) + + public: + MDNSResponderOperator(); + + virtual nsresult Start(); + virtual nsresult Stop(); + void Cancel() { mIsCancelled = true; } + nsIThread* GetThread() const { return mThread; } + + protected: + virtual ~MDNSResponderOperator(); + + bool IsServing() const { return mService; } + nsresult ResetService(DNSServiceRef aService); + + private: + class ServiceWatcher; + + DNSServiceRef mService; + RefPtr<ServiceWatcher> mWatcher; + nsCOMPtr<nsIThread> mThread; // remember caller thread for callback + Atomic<bool> mIsCancelled; +}; + +class BrowseOperator final : public MDNSResponderOperator { + public: + BrowseOperator(const nsACString& aServiceType, + nsIDNSServiceDiscoveryListener* aListener); + + nsresult Start() override; + nsresult Stop() override; + + void Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, DNSServiceErrorType aErrorCode, + const nsACString& aServiceName, const nsACString& aRegType, + const nsACString& aReplyDomain); + + private: + ~BrowseOperator() = default; + + nsCString mServiceType; + nsCOMPtr<nsIDNSServiceDiscoveryListener> mListener; +}; + +class RegisterOperator final : public MDNSResponderOperator { + enum { TXT_BUFFER_SIZE = 256 }; + + public: + RegisterOperator(nsIDNSServiceInfo* aServiceInfo, + nsIDNSRegistrationListener* aListener); + + nsresult Start() override; + nsresult Stop() override; + + void Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags, + DNSServiceErrorType aErrorCode, const nsACString& aName, + const nsACString& aRegType, const nsACString& aDomain); + + private: + ~RegisterOperator() = default; + + nsCOMPtr<nsIDNSServiceInfo> mServiceInfo; + nsCOMPtr<nsIDNSRegistrationListener> mListener; +}; + +class ResolveOperator final : public MDNSResponderOperator { + enum { TXT_BUFFER_SIZE = 256 }; + + public: + ResolveOperator(nsIDNSServiceInfo* aServiceInfo, + nsIDNSServiceResolveListener* aListener); + + nsresult Start() override; + + void Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, DNSServiceErrorType aErrorCode, + const nsACString& aFullName, const nsACString& aHostTarget, + uint16_t aPort, uint16_t aTxtLen, const unsigned char* aTxtRecord); + + private: + ~ResolveOperator() = default; + void GetAddrInfor(nsIDNSServiceInfo* aServiceInfo); + + nsCOMPtr<nsIDNSServiceInfo> mServiceInfo; + nsCOMPtr<nsIDNSServiceResolveListener> mListener; +}; + +union NetAddr; + +class GetAddrInfoOperator final : public MDNSResponderOperator { + public: + GetAddrInfoOperator(nsIDNSServiceInfo* aServiceInfo, + nsIDNSServiceResolveListener* aListener); + + nsresult Start() override; + + void Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, DNSServiceErrorType aErrorCode, + const nsACString& aHostName, const NetAddr& aAddress, + uint32_t aTTL); + + private: + ~GetAddrInfoOperator() = default; + + nsCOMPtr<nsIDNSServiceInfo> mServiceInfo; + nsCOMPtr<nsIDNSServiceResolveListener> mListener; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_netwerk_dns_mdns_libmdns_MDNSResponderOperator_h diff --git a/netwerk/dns/mdns/libmdns/MDNSResponderReply.cpp b/netwerk/dns/mdns/libmdns/MDNSResponderReply.cpp new file mode 100644 index 0000000000..ee08a87211 --- /dev/null +++ b/netwerk/dns/mdns/libmdns/MDNSResponderReply.cpp @@ -0,0 +1,215 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "MDNSResponderReply.h" +#include "mozilla/EndianUtils.h" +#include "private/pprio.h" + +namespace mozilla { +namespace net { + +BrowseReplyRunnable::BrowseReplyRunnable( + DNSServiceRef aSdRef, DNSServiceFlags aFlags, uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, const nsACString& aServiceName, + const nsACString& aRegType, const nsACString& aReplyDomain, + BrowseOperator* aContext) + : Runnable("net::BrowseReplyRunnable"), + mSdRef(aSdRef), + mFlags(aFlags), + mInterfaceIndex(aInterfaceIndex), + mErrorCode(aErrorCode), + mServiceName(aServiceName), + mRegType(aRegType), + mReplyDomain(aReplyDomain), + mContext(aContext) {} + +NS_IMETHODIMP +BrowseReplyRunnable::Run() { + MOZ_ASSERT(mContext); + mContext->Reply(mSdRef, mFlags, mInterfaceIndex, mErrorCode, mServiceName, + mRegType, mReplyDomain); + return NS_OK; +} + +void BrowseReplyRunnable::Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, + const char* aServiceName, const char* aRegType, + const char* aReplyDomain, void* aContext) { + MOZ_ASSERT(OnSocketThread(), "not on socket thread"); + + BrowseOperator* obj(reinterpret_cast<BrowseOperator*>(aContext)); + if (!obj) { + return; + } + + nsCOMPtr<nsIThread> thread(obj->GetThread()); + if (!thread) { + return; + } + + thread->Dispatch( + new BrowseReplyRunnable(aSdRef, aFlags, aInterfaceIndex, aErrorCode, + nsCString(aServiceName), nsCString(aRegType), + nsCString(aReplyDomain), obj), + NS_DISPATCH_NORMAL); +} + +RegisterReplyRunnable::RegisterReplyRunnable(DNSServiceRef aSdRef, + DNSServiceFlags aFlags, + DNSServiceErrorType aErrorCode, + const nsACString& aName, + const nsACString& aRegType, + const nsACString& domain, + RegisterOperator* aContext) + : Runnable("net::RegisterReplyRunnable"), + mSdRef(aSdRef), + mFlags(aFlags), + mErrorCode(aErrorCode), + mName(aName), + mRegType(aRegType), + mDomain(domain), + mContext(aContext) {} + +NS_IMETHODIMP +RegisterReplyRunnable::Run() { + MOZ_ASSERT(mContext); + + mContext->Reply(mSdRef, mFlags, mErrorCode, mName, mRegType, mDomain); + return NS_OK; +} + +void RegisterReplyRunnable::Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags, + DNSServiceErrorType aErrorCode, + const char* aName, const char* aRegType, + const char* domain, void* aContext) { + MOZ_ASSERT(OnSocketThread(), "not on socket thread"); + + RegisterOperator* obj(reinterpret_cast<RegisterOperator*>(aContext)); + if (!obj) { + return; + } + + nsCOMPtr<nsIThread> thread(obj->GetThread()); + if (!thread) { + return; + } + + thread->Dispatch( + new RegisterReplyRunnable(aSdRef, aFlags, aErrorCode, nsCString(aName), + nsCString(aRegType), nsCString(domain), obj), + NS_DISPATCH_NORMAL); +} + +ResolveReplyRunnable::ResolveReplyRunnable( + DNSServiceRef aSdRef, DNSServiceFlags aFlags, uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, const nsACString& aFullName, + const nsACString& aHostTarget, uint16_t aPort, uint16_t aTxtLen, + const unsigned char* aTxtRecord, ResolveOperator* aContext) + : Runnable("net::ResolveReplyRunnable"), + mSdRef(aSdRef), + mFlags(aFlags), + mInterfaceIndex(aInterfaceIndex), + mErrorCode(aErrorCode), + mFullname(aFullName), + mHosttarget(aHostTarget), + mPort(aPort), + mTxtLen(aTxtLen), + mTxtRecord(new unsigned char[aTxtLen]), + mContext(aContext) { + if (mTxtRecord) { + memcpy(mTxtRecord.get(), aTxtRecord, aTxtLen); + } +} + +NS_IMETHODIMP +ResolveReplyRunnable::Run() { + MOZ_ASSERT(mContext); + mContext->Reply(mSdRef, mFlags, mInterfaceIndex, mErrorCode, mFullname, + mHosttarget, mPort, mTxtLen, mTxtRecord.get()); + return NS_OK; +} + +void ResolveReplyRunnable::Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, + const char* aFullName, const char* aHostTarget, + uint16_t aPort, uint16_t aTxtLen, + const unsigned char* aTxtRecord, + void* aContext) { + MOZ_ASSERT(OnSocketThread(), "not on socket thread"); + + ResolveOperator* obj(reinterpret_cast<ResolveOperator*>(aContext)); + if (!obj) { + return; + } + + nsCOMPtr<nsIThread> thread(obj->GetThread()); + if (!thread) { + return; + } + + thread->Dispatch( + new ResolveReplyRunnable(aSdRef, aFlags, aInterfaceIndex, aErrorCode, + nsCString(aFullName), nsCString(aHostTarget), + NativeEndian::swapFromNetworkOrder(aPort), + aTxtLen, aTxtRecord, obj), + NS_DISPATCH_NORMAL); +} + +GetAddrInfoReplyRunnable::GetAddrInfoReplyRunnable( + DNSServiceRef aSdRef, DNSServiceFlags aFlags, uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, const nsACString& aHostName, + const mozilla::net::NetAddr& aAddress, uint32_t aTTL, + GetAddrInfoOperator* aContext) + : Runnable("net::GetAddrInfoReplyRunnable"), + mSdRef(aSdRef), + mFlags(aFlags), + mInterfaceIndex(aInterfaceIndex), + mErrorCode(aErrorCode), + mHostName(aHostName), + mAddress(aAddress), + mTTL(aTTL), + mContext(aContext) {} + +NS_IMETHODIMP +GetAddrInfoReplyRunnable::Run() { + MOZ_ASSERT(mContext); + mContext->Reply(mSdRef, mFlags, mInterfaceIndex, mErrorCode, mHostName, + mAddress, mTTL); + return NS_OK; +} + +void GetAddrInfoReplyRunnable::Reply( + DNSServiceRef aSdRef, DNSServiceFlags aFlags, uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, const char* aHostName, + const struct sockaddr* aAddress, uint32_t aTTL, void* aContext) { + MOZ_ASSERT(OnSocketThread(), "not on socket thread"); + + GetAddrInfoOperator* obj(reinterpret_cast<GetAddrInfoOperator*>(aContext)); + if (!obj) { + return; + } + + nsCOMPtr<nsIThread> thread(obj->GetThread()); + if (!thread) { + return; + } + + NetAddr address; + address.raw.family = aAddress->sa_family; + + static_assert(sizeof(address.raw.data) >= sizeof(aAddress->sa_data), + "size of sockaddr.sa_data is too big"); + memcpy(&address.raw.data, aAddress->sa_data, sizeof(aAddress->sa_data)); + + thread->Dispatch( + new GetAddrInfoReplyRunnable(aSdRef, aFlags, aInterfaceIndex, aErrorCode, + nsCString(aHostName), address, aTTL, obj), + NS_DISPATCH_NORMAL); +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/mdns/libmdns/MDNSResponderReply.h b/netwerk/dns/mdns/libmdns/MDNSResponderReply.h new file mode 100644 index 0000000000..120bb6dae7 --- /dev/null +++ b/netwerk/dns/mdns/libmdns/MDNSResponderReply.h @@ -0,0 +1,130 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_netwerk_dns_mdns_libmdns_MDNSResponderReply_h +#define mozilla_netwerk_dns_mdns_libmdns_MDNSResponderReply_h + +#include "dns_sd.h" +#include "MDNSResponderOperator.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/net/DNS.h" +#include "mozilla/RefPtr.h" +#include "nsThreadUtils.h" + +namespace mozilla { +namespace net { + +class BrowseReplyRunnable final : public Runnable { + public: + BrowseReplyRunnable(DNSServiceRef aSdRef, DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, DNSServiceErrorType aErrorCode, + const nsACString& aServiceName, + const nsACString& aRegType, + const nsACString& aReplyDomain, BrowseOperator* aContext); + + NS_IMETHOD Run() override; + + static void Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, DNSServiceErrorType aErrorCode, + const char* aServiceName, const char* aRegType, + const char* aReplyDomain, void* aContext); + + private: + DNSServiceRef mSdRef; + DNSServiceFlags mFlags; + uint32_t mInterfaceIndex; + DNSServiceErrorType mErrorCode; + nsCString mServiceName; + nsCString mRegType; + nsCString mReplyDomain; + RefPtr<BrowseOperator> mContext; +}; + +class RegisterReplyRunnable final : public Runnable { + public: + RegisterReplyRunnable(DNSServiceRef aSdRef, DNSServiceFlags aFlags, + DNSServiceErrorType aErrorCode, const nsACString& aName, + const nsACString& aRegType, const nsACString& aDomain, + RegisterOperator* aContext); + + NS_IMETHOD Run() override; + + static void Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags, + DNSServiceErrorType aErrorCode, const char* aName, + const char* aRegType, const char* aDomain, void* aContext); + + private: + DNSServiceRef mSdRef; + DNSServiceFlags mFlags; + DNSServiceErrorType mErrorCode; + nsCString mName; + nsCString mRegType; + nsCString mDomain; + RefPtr<RegisterOperator> mContext; +}; + +class ResolveReplyRunnable final : public Runnable { + public: + ResolveReplyRunnable(DNSServiceRef aSdRef, DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, DNSServiceErrorType aErrorCode, + const nsACString& aFullName, + const nsACString& aHostTarget, uint16_t aPort, + uint16_t aTxtLen, const unsigned char* aTxtRecord, + ResolveOperator* aContext); + ~ResolveReplyRunnable() = default; + + NS_IMETHOD Run() override; + + static void Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, DNSServiceErrorType aErrorCode, + const char* aFullName, const char* aHostTarget, + uint16_t aPort, uint16_t aTxtLen, + const unsigned char* aTxtRecord, void* aContext); + + private: + DNSServiceRef mSdRef; + DNSServiceFlags mFlags; + uint32_t mInterfaceIndex; + DNSServiceErrorType mErrorCode; + nsCString mFullname; + nsCString mHosttarget; + uint16_t mPort; + uint16_t mTxtLen; + UniquePtr<unsigned char> mTxtRecord; + RefPtr<ResolveOperator> mContext; +}; + +class GetAddrInfoReplyRunnable final : public Runnable { + public: + GetAddrInfoReplyRunnable(DNSServiceRef aSdRef, DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, + const nsACString& aHostName, + const mozilla::net::NetAddr& aAddress, uint32_t aTTL, + GetAddrInfoOperator* aContext); + ~GetAddrInfoReplyRunnable() = default; + + NS_IMETHOD Run() override; + + static void Reply(DNSServiceRef aSdRef, DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, DNSServiceErrorType aErrorCode, + const char* aHostName, const struct sockaddr* aAddress, + uint32_t aTTL, void* aContext); + + private: + DNSServiceRef mSdRef; + DNSServiceFlags mFlags; + uint32_t mInterfaceIndex; + DNSServiceErrorType mErrorCode; + nsCString mHostName; + mozilla::net::NetAddr mAddress; + uint32_t mTTL; + RefPtr<GetAddrInfoOperator> mContext; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_netwerk_dns_mdns_libmdns_MDNSResponderReply_h diff --git a/netwerk/dns/mdns/libmdns/MulticastDNSAndroid.jsm b/netwerk/dns/mdns/libmdns/MulticastDNSAndroid.jsm new file mode 100644 index 0000000000..d94e90efaa --- /dev/null +++ b/netwerk/dns/mdns/libmdns/MulticastDNSAndroid.jsm @@ -0,0 +1,262 @@ +// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +var EXPORTED_SYMBOLS = ["MulticastDNS"]; + +const { EventDispatcher } = ChromeUtils.import( + "resource://gre/modules/Messaging.jsm" +); + +const DEBUG = false; + +var log = function(s) {}; +if (DEBUG) { + log = ChromeUtils.import( + "resource://gre/modules/AndroidLog.jsm", + {} + ).AndroidLog.d.bind(null, "MulticastDNS"); +} + +const FAILURE_INTERNAL_ERROR = -65537; + +// Helper function for sending commands to Java. +function send(type, data, callback) { + let msg = { + type, + }; + + for (let i in data) { + try { + msg[i] = data[i]; + } catch (e) {} + } + + EventDispatcher.instance.sendRequestForResult(msg).then( + result => callback(result, null), + err => + callback(null, typeof err === "number" ? err : FAILURE_INTERNAL_ERROR) + ); +} + +// Receives service found/lost event from NsdManager +function ServiceManager() {} + +ServiceManager.prototype = { + listeners: {}, + numListeners: 0, + + onEvent(event, data, callback) { + if (event === "NsdManager:ServiceFound") { + this.onServiceFound(); + } else if (event === "NsdManager:ServiceLost") { + this.onServiceLost(); + } + }, + + registerEvent() { + log("registerEvent"); + EventDispatcher.instance.registerListener(this, [ + "NsdManager:ServiceFound", + "NsdManager:ServiceLost", + ]); + }, + + unregisterEvent() { + log("unregisterEvent"); + EventDispatcher.instance.unregisterListener(this, [ + "NsdManager:ServiceFound", + "NsdManager:ServiceLost", + ]); + }, + + addListener(aServiceType, aListener) { + log("addListener: " + aServiceType + ", " + aListener); + + if (!this.listeners[aServiceType]) { + this.listeners[aServiceType] = []; + } + if (this.listeners[aServiceType].includes(aListener)) { + log("listener already exists"); + return; + } + + this.listeners[aServiceType].push(aListener); + ++this.numListeners; + + if (this.numListeners === 1) { + this.registerEvent(); + } + + log("listener added: " + this); + }, + + removeListener(aServiceType, aListener) { + log("removeListener: " + aServiceType + ", " + aListener); + + if (!this.listeners[aServiceType]) { + log("listener doesn't exist"); + return; + } + let index = this.listeners[aServiceType].indexOf(aListener); + if (index < 0) { + log("listener doesn't exist"); + return; + } + + this.listeners[aServiceType].splice(index, 1); + --this.numListeners; + + if (this.numListeners === 0) { + this.unregisterEvent(); + } + + log("listener removed" + this); + }, + + onServiceFound(aServiceInfo) { + let listeners = this.listeners[aServiceInfo.serviceType]; + if (listeners) { + for (let listener of listeners) { + listener.onServiceFound(aServiceInfo); + } + } else { + log("no listener"); + } + return {}; + }, + + onServiceLost(aServiceInfo) { + let listeners = this.listeners[aServiceInfo.serviceType]; + if (listeners) { + for (let listener of listeners) { + listener.onServiceLost(aServiceInfo); + } + } else { + log("no listener"); + } + return {}; + }, +}; + +// make an object from nsIPropertyBag2 +function parsePropertyBag2(bag) { + if (!bag || !(bag instanceof Ci.nsIPropertyBag2)) { + throw new TypeError("Not a property bag"); + } + + let attributes = []; + for (let { name } of bag.enumerator) { + let value = bag.getPropertyAsACString(name); + attributes.push({ + name, + value, + }); + } + + return attributes; +} + +function MulticastDNS() { + this.serviceManager = new ServiceManager(); +} + +MulticastDNS.prototype = { + startDiscovery(aServiceType, aListener) { + this.serviceManager.addListener(aServiceType, aListener); + + let serviceInfo = { + serviceType: aServiceType, + uniqueId: aListener.uuid, + }; + + send("NsdManager:DiscoverServices", serviceInfo, (result, err) => { + if (err) { + log("onStartDiscoveryFailed: " + aServiceType + " (" + err + ")"); + this.serviceManager.removeListener(aServiceType, aListener); + aListener.onStartDiscoveryFailed(aServiceType, err); + } else { + aListener.onDiscoveryStarted(result); + } + }); + }, + + stopDiscovery(aServiceType, aListener) { + this.serviceManager.removeListener(aServiceType, aListener); + + let serviceInfo = { + uniqueId: aListener.uuid, + }; + + send("NsdManager:StopServiceDiscovery", serviceInfo, (result, err) => { + if (err) { + log("onStopDiscoveryFailed: " + aServiceType + " (" + err + ")"); + aListener.onStopDiscoveryFailed(aServiceType, err); + } else { + aListener.onDiscoveryStopped(aServiceType); + } + }); + }, + + registerService(aServiceInfo, aListener) { + let serviceInfo = { + port: aServiceInfo.port, + serviceType: aServiceInfo.serviceType, + uniqueId: aListener.uuid, + }; + + try { + serviceInfo.host = aServiceInfo.host; + } catch (e) { + // host unspecified + } + try { + serviceInfo.serviceName = aServiceInfo.serviceName; + } catch (e) { + // serviceName unspecified + } + try { + serviceInfo.attributes = parsePropertyBag2(aServiceInfo.attributes); + } catch (e) { + // attributes unspecified + } + + send("NsdManager:RegisterService", serviceInfo, (result, err) => { + if (err) { + log("onRegistrationFailed: (" + err + ")"); + aListener.onRegistrationFailed(aServiceInfo, err); + } else { + aListener.onServiceRegistered(result); + } + }); + }, + + unregisterService(aServiceInfo, aListener) { + let serviceInfo = { + uniqueId: aListener.uuid, + }; + + send("NsdManager:UnregisterService", serviceInfo, (result, err) => { + if (err) { + log("onUnregistrationFailed: (" + err + ")"); + aListener.onUnregistrationFailed(aServiceInfo, err); + } else { + aListener.onServiceUnregistered(aServiceInfo); + } + }); + }, + + resolveService(aServiceInfo, aListener) { + send("NsdManager:ResolveService", aServiceInfo, (result, err) => { + if (err) { + log("onResolveFailed: (" + err + ")"); + aListener.onResolveFailed(aServiceInfo, err); + } else { + aListener.onServiceResolved(result); + } + }); + }, +}; diff --git a/netwerk/dns/mdns/libmdns/components.conf b/netwerk/dns/mdns/libmdns/components.conf new file mode 100644 index 0000000000..6e64140c82 --- /dev/null +++ b/netwerk/dns/mdns/libmdns/components.conf @@ -0,0 +1,24 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +Classes = [ + { + 'cid': '{14a50f2b-7ff6-48a5-88e3-615fd111f5d3}', + 'contract_ids': ['@mozilla.org/toolkit/components/mdnsresponder/dns-info;1'], + 'type': 'mozilla::net::nsDNSServiceInfo', + 'headers': ['/netwerk/dns/mdns/libmdns/nsDNSServiceInfo.h'], + }, +] + +if buildconfig.substs['MOZ_WIDGET_TOOLKIT'] != 'cocoa': + Classes += [ + { + 'cid': '{f9346d98-f27a-4e89-b744-493843416480}', + 'contract_ids': ['@mozilla.org/toolkit/components/mdnsresponder/dns-sd;1'], + 'jsm': 'resource://gre/modules/DNSServiceDiscovery.jsm', + 'constructor': 'nsDNSServiceDiscovery', + }, + ] diff --git a/netwerk/dns/mdns/libmdns/fallback/DNSPacket.jsm b/netwerk/dns/mdns/libmdns/fallback/DNSPacket.jsm new file mode 100644 index 0000000000..1dd859c6d0 --- /dev/null +++ b/netwerk/dns/mdns/libmdns/fallback/DNSPacket.jsm @@ -0,0 +1,304 @@ +/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +var EXPORTED_SYMBOLS = ["DNSPacket"]; + +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); + +const { DataReader } = ChromeUtils.import( + "resource://gre/modules/DataReader.jsm" +); +const { DataWriter } = ChromeUtils.import( + "resource://gre/modules/DataWriter.jsm" +); +const { DNSRecord } = ChromeUtils.import( + "resource://gre/modules/DNSRecord.jsm" +); +const { DNSResourceRecord } = ChromeUtils.import( + "resource://gre/modules/DNSResourceRecord.jsm" +); + +const DEBUG = true; + +function debug(msg) { + Services.console.logStringMessage("DNSPacket: " + msg); +} + +let DNS_PACKET_SECTION_TYPES = [ + "QD", // Question + "AN", // Answer + "NS", // Authority + "AR", // Additional +]; + +/** + * DNS Packet Structure + * ************************************************* + * + * Header + * ====== + * + * 00 2-Bytes 15 + * ------------------------------------------------- + * |00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15| + * ------------------------------------------------- + * |<==================== ID =====================>| + * |QR|<== OP ===>|AA|TC|RD|RA|UN|AD|CD|<== RC ===>| + * |<================== QDCOUNT ==================>| + * |<================== ANCOUNT ==================>| + * |<================== NSCOUNT ==================>| + * |<================== ARCOUNT ==================>| + * ------------------------------------------------- + * + * ID: 2-Bytes + * FLAGS: 2-Bytes + * - QR: 1-Bit + * - OP: 4-Bits + * - AA: 1-Bit + * - TC: 1-Bit + * - RD: 1-Bit + * - RA: 1-Bit + * - UN: 1-Bit + * - AD: 1-Bit + * - CD: 1-Bit + * - RC: 4-Bits + * QDCOUNT: 2-Bytes + * ANCOUNT: 2-Bytes + * NSCOUNT: 2-Bytes + * ARCOUNT: 2-Bytes + * + * + * Data + * ==== + * + * 00 2-Bytes 15 + * ------------------------------------------------- + * |00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15| + * ------------------------------------------------- + * |<???=============== QD[...] ===============???>| + * |<???=============== AN[...] ===============???>| + * |<???=============== NS[...] ===============???>| + * |<???=============== AR[...] ===============???>| + * ------------------------------------------------- + * + * QD: ??-Bytes + * AN: ??-Bytes + * NS: ??-Bytes + * AR: ??-Bytes + * + * + * Question Record + * =============== + * + * 00 2-Bytes 15 + * ------------------------------------------------- + * |00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15| + * ------------------------------------------------- + * |<???================ NAME =================???>| + * |<=================== TYPE ====================>| + * |<=================== CLASS ===================>| + * ------------------------------------------------- + * + * NAME: ??-Bytes + * TYPE: 2-Bytes + * CLASS: 2-Bytes + * + * + * Resource Record + * =============== + * + * 00 4-Bytes 31 + * ------------------------------------------------- + * |00|02|04|06|08|10|12|14|16|18|20|22|24|26|28|30| + * ------------------------------------------------- + * |<???================ NAME =================???>| + * |<======= TYPE ========>|<======= CLASS =======>| + * |<==================== TTL ====================>| + * |<====== DATALEN ======>|<???==== DATA =====???>| + * ------------------------------------------------- + * + * NAME: ??-Bytes + * TYPE: 2-Bytes + * CLASS: 2-Bytes + * DATALEN: 2-Bytes + * DATA: ??-Bytes (Specified By DATALEN) + */ +class DNSPacket { + constructor() { + this._flags = _valueToFlags(0x0000); + this._records = {}; + + DNS_PACKET_SECTION_TYPES.forEach(sectionType => { + this._records[sectionType] = []; + }); + } + + static parse(data) { + let reader = new DataReader(data); + if (reader.getValue(2) !== 0x0000) { + throw new Error("Packet must start with 0x0000"); + } + + let packet = new DNSPacket(); + packet._flags = _valueToFlags(reader.getValue(2)); + + let recordCounts = {}; + + // Parse the record counts. + DNS_PACKET_SECTION_TYPES.forEach(sectionType => { + recordCounts[sectionType] = reader.getValue(2); + }); + + // Parse the actual records. + DNS_PACKET_SECTION_TYPES.forEach(sectionType => { + let recordCount = recordCounts[sectionType]; + for (let i = 0; i < recordCount; i++) { + if (sectionType === "QD") { + packet.addRecord( + sectionType, + DNSRecord.parseFromPacketReader(reader) + ); + } else { + packet.addRecord( + sectionType, + DNSResourceRecord.parseFromPacketReader(reader) + ); + } + } + }); + + if (!reader.eof) { + DEBUG && debug("Did not complete parsing packet data"); + } + + return packet; + } + + getFlag(flag) { + return this._flags[flag]; + } + + setFlag(flag, value) { + this._flags[flag] = value; + } + + addRecord(sectionType, record) { + this._records[sectionType].push(record); + } + + getRecords(sectionTypes, recordType) { + let records = []; + + sectionTypes.forEach(sectionType => { + records = records.concat(this._records[sectionType]); + }); + + if (!recordType) { + return records; + } + + return records.filter(r => r.recordType === recordType); + } + + serialize() { + let writer = new DataWriter(); + + // Write leading 0x0000 (2 bytes) + writer.putValue(0x0000, 2); + + // Write `flags` (2 bytes) + writer.putValue(_flagsToValue(this._flags), 2); + + // Write lengths of record sections (2 bytes each) + DNS_PACKET_SECTION_TYPES.forEach(sectionType => { + writer.putValue(this._records[sectionType].length, 2); + }); + + // Write records + DNS_PACKET_SECTION_TYPES.forEach(sectionType => { + this._records[sectionType].forEach(record => { + writer.putBytes(record.serialize()); + }); + }); + + return writer.data; + } + + toJSON() { + return JSON.stringify(this.toJSONObject()); + } + + toJSONObject() { + let result = { flags: this._flags }; + DNS_PACKET_SECTION_TYPES.forEach(sectionType => { + result[sectionType] = []; + + let records = this._records[sectionType]; + records.forEach(record => { + result[sectionType].push(record.toJSONObject()); + }); + }); + + return result; + } +} + +/** + * @private + */ +function _valueToFlags(value) { + return { + QR: (value & 0x8000) >> 15, + OP: (value & 0x7800) >> 11, + AA: (value & 0x0400) >> 10, + TC: (value & 0x0200) >> 9, + RD: (value & 0x0100) >> 8, + RA: (value & 0x0080) >> 7, + UN: (value & 0x0040) >> 6, + AD: (value & 0x0020) >> 5, + CD: (value & 0x0010) >> 4, + RC: (value & 0x000f) >> 0, + }; +} + +/** + * @private + */ +function _flagsToValue(flags) { + let value = 0x0000; + + value += flags.QR & 0x01; + + value <<= 4; + value += flags.OP & 0x0f; + + value <<= 1; + value += flags.AA & 0x01; + + value <<= 1; + value += flags.TC & 0x01; + + value <<= 1; + value += flags.RD & 0x01; + + value <<= 1; + value += flags.RA & 0x01; + + value <<= 1; + value += flags.UN & 0x01; + + value <<= 1; + value += flags.AD & 0x01; + + value <<= 1; + value += flags.CD & 0x01; + + value <<= 4; + value += flags.RC & 0x0f; + + return value; +} diff --git a/netwerk/dns/mdns/libmdns/fallback/DNSRecord.jsm b/netwerk/dns/mdns/libmdns/fallback/DNSRecord.jsm new file mode 100644 index 0000000000..682fd8b103 --- /dev/null +++ b/netwerk/dns/mdns/libmdns/fallback/DNSRecord.jsm @@ -0,0 +1,71 @@ +/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +var EXPORTED_SYMBOLS = ["DNSRecord"]; + +const { DataWriter } = ChromeUtils.import( + "resource://gre/modules/DataWriter.jsm" +); +const { DNS_CLASS_CODES, DNS_RECORD_TYPES } = ChromeUtils.import( + "resource://gre/modules/DNSTypes.jsm" +); + +class DNSRecord { + constructor(properties = {}) { + this.name = properties.name || ""; + this.recordType = properties.recordType || DNS_RECORD_TYPES.ANY; + this.classCode = properties.classCode || DNS_CLASS_CODES.IN; + this.cacheFlush = properties.cacheFlush || false; + } + + static parseFromPacketReader(reader) { + let name = reader.getLabel(); + let recordType = reader.getValue(2); + let classCode = reader.getValue(2); + let cacheFlush = !!(classCode & 0x8000); + classCode &= 0xff; + + return new this({ + name, + recordType, + classCode, + cacheFlush, + }); + } + + serialize() { + let writer = new DataWriter(); + + // Write `name` (ends with trailing 0x00 byte) + writer.putLabel(this.name); + + // Write `recordType` (2 bytes) + writer.putValue(this.recordType, 2); + + // Write `classCode` (2 bytes) + let classCode = this.classCode; + if (this.cacheFlush) { + classCode |= 0x8000; + } + writer.putValue(classCode, 2); + + return writer.data; + } + + toJSON() { + return JSON.stringify(this.toJSONObject()); + } + + toJSONObject() { + return { + name: this.name, + recordType: this.recordType, + classCode: this.classCode, + cacheFlush: this.cacheFlush, + }; + } +} diff --git a/netwerk/dns/mdns/libmdns/fallback/DNSResourceRecord.jsm b/netwerk/dns/mdns/libmdns/fallback/DNSResourceRecord.jsm new file mode 100644 index 0000000000..e89f418410 --- /dev/null +++ b/netwerk/dns/mdns/libmdns/fallback/DNSResourceRecord.jsm @@ -0,0 +1,221 @@ +/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +var EXPORTED_SYMBOLS = ["DNSResourceRecord"]; + +const { DataReader } = ChromeUtils.import( + "resource://gre/modules/DataReader.jsm" +); +const { DataWriter } = ChromeUtils.import( + "resource://gre/modules/DataWriter.jsm" +); +const { DNSRecord } = ChromeUtils.import( + "resource://gre/modules/DNSRecord.jsm" +); +const { DNS_RECORD_TYPES } = ChromeUtils.import( + "resource://gre/modules/DNSTypes.jsm" +); + +const DNS_RESOURCE_RECORD_DEFAULT_TTL = 120; // 120 seconds + +class DNSResourceRecord extends DNSRecord { + constructor(properties = {}) { + super(properties); + + this.ttl = properties.ttl || DNS_RESOURCE_RECORD_DEFAULT_TTL; + this.data = properties.data || {}; + } + + static parseFromPacketReader(reader) { + let record = super.parseFromPacketReader(reader); + + let ttl = reader.getValue(4); + let recordData = reader.getBytes(reader.getValue(2)); + let packetData = reader.data; + + let data; + + switch (record.recordType) { + case DNS_RECORD_TYPES.A: + data = _parseA(recordData, packetData); + break; + case DNS_RECORD_TYPES.PTR: + data = _parsePTR(recordData, packetData); + break; + case DNS_RECORD_TYPES.TXT: + data = _parseTXT(recordData, packetData); + break; + case DNS_RECORD_TYPES.SRV: + data = _parseSRV(recordData, packetData); + break; + default: + data = null; + break; + } + + record.ttl = ttl; + record.data = data; + + return record; + } + + serialize() { + let writer = new DataWriter(super.serialize()); + + // Write `ttl` (4 bytes) + writer.putValue(this.ttl, 4); + + let data; + + switch (this.recordType) { + case DNS_RECORD_TYPES.A: + data = _serializeA(this.data); + break; + case DNS_RECORD_TYPES.PTR: + data = _serializePTR(this.data); + break; + case DNS_RECORD_TYPES.TXT: + data = _serializeTXT(this.data); + break; + case DNS_RECORD_TYPES.SRV: + data = _serializeSRV(this.data); + break; + default: + data = new Uint8Array(); + break; + } + + // Write `data` length. + writer.putValue(data.length, 2); + + // Write `data` (ends with trailing 0x00 byte) + writer.putBytes(data); + + return writer.data; + } + + toJSON() { + return JSON.stringify(this.toJSONObject()); + } + + toJSONObject() { + let result = super.toJSONObject(); + result.ttl = this.ttl; + result.data = this.data; + return result; + } +} + +/** + * @private + */ +function _parseA(recordData, packetData) { + let reader = new DataReader(recordData); + + let parts = []; + for (let i = 0; i < 4; i++) { + parts.push(reader.getValue(1)); + } + + return parts.join("."); +} + +/** + * @private + */ +function _parsePTR(recordData, packetData) { + let reader = new DataReader(recordData); + + return reader.getLabel(packetData); +} + +/** + * @private + */ +function _parseTXT(recordData, packetData) { + let reader = new DataReader(recordData); + + let result = {}; + + let label = reader.getLabel(packetData); + if (label.length > 0) { + let parts = label.split("."); + parts.forEach(part => { + let [name] = part.split("=", 1); + let value = part.substr(name.length + 1); + result[name] = value; + }); + } + + return result; +} + +/** + * @private + */ +function _parseSRV(recordData, packetData) { + let reader = new DataReader(recordData); + + let priority = reader.getValue(2); + let weight = reader.getValue(2); + let port = reader.getValue(2); + let target = reader.getLabel(packetData); + + return { priority, weight, port, target }; +} + +/** + * @private + */ +function _serializeA(data) { + let writer = new DataWriter(); + + let parts = data.split("."); + for (let i = 0; i < 4; i++) { + writer.putValue(parseInt(parts[i], 10) || 0); + } + + return writer.data; +} + +/** + * @private + */ +function _serializePTR(data) { + let writer = new DataWriter(); + + writer.putLabel(data); + + return writer.data; +} + +/** + * @private + */ +function _serializeTXT(data) { + let writer = new DataWriter(); + + for (let name in data) { + writer.putLengthString(name + "=" + data[name]); + } + + return writer.data; +} + +/** + * @private + */ +function _serializeSRV(data) { + let writer = new DataWriter(); + + writer.putValue(data.priority || 0, 2); + writer.putValue(data.weight || 0, 2); + writer.putValue(data.port || 0, 2); + writer.putLabel(data.target); + + return writer.data; +} diff --git a/netwerk/dns/mdns/libmdns/fallback/DNSTypes.jsm b/netwerk/dns/mdns/libmdns/fallback/DNSTypes.jsm new file mode 100644 index 0000000000..878e22917d --- /dev/null +++ b/netwerk/dns/mdns/libmdns/fallback/DNSTypes.jsm @@ -0,0 +1,99 @@ +/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +var EXPORTED_SYMBOLS = [ + "DNS_QUERY_RESPONSE_CODES", + "DNS_AUTHORITATIVE_ANSWER_CODES", + "DNS_CLASS_CODES", + "DNS_RECORD_TYPES", +]; + +let DNS_QUERY_RESPONSE_CODES = { + QUERY: 0, // RFC 1035 - Query + RESPONSE: 1, // RFC 1035 - Reponse +}; + +let DNS_AUTHORITATIVE_ANSWER_CODES = { + NO: 0, // RFC 1035 - Not Authoritative + YES: 1, // RFC 1035 - Is Authoritative +}; + +let DNS_CLASS_CODES = { + IN: 0x01, // RFC 1035 - Internet + CS: 0x02, // RFC 1035 - CSNET + CH: 0x03, // RFC 1035 - CHAOS + HS: 0x04, // RFC 1035 - Hesiod + NONE: 0xfe, // RFC 2136 - None + ANY: 0xff, // RFC 1035 - Any +}; + +let DNS_RECORD_TYPES = { + SIGZERO: 0, // RFC 2931 + A: 1, // RFC 1035 + NS: 2, // RFC 1035 + MD: 3, // RFC 1035 + MF: 4, // RFC 1035 + CNAME: 5, // RFC 1035 + SOA: 6, // RFC 1035 + MB: 7, // RFC 1035 + MG: 8, // RFC 1035 + MR: 9, // RFC 1035 + NULL: 10, // RFC 1035 + WKS: 11, // RFC 1035 + PTR: 12, // RFC 1035 + HINFO: 13, // RFC 1035 + MINFO: 14, // RFC 1035 + MX: 15, // RFC 1035 + TXT: 16, // RFC 1035 + RP: 17, // RFC 1183 + AFSDB: 18, // RFC 1183 + X25: 19, // RFC 1183 + ISDN: 20, // RFC 1183 + RT: 21, // RFC 1183 + NSAP: 22, // RFC 1706 + NSAP_PTR: 23, // RFC 1348 + SIG: 24, // RFC 2535 + KEY: 25, // RFC 2535 + PX: 26, // RFC 2163 + GPOS: 27, // RFC 1712 + AAAA: 28, // RFC 1886 + LOC: 29, // RFC 1876 + NXT: 30, // RFC 2535 + EID: 31, // RFC ???? + NIMLOC: 32, // RFC ???? + SRV: 33, // RFC 2052 + ATMA: 34, // RFC ???? + NAPTR: 35, // RFC 2168 + KX: 36, // RFC 2230 + CERT: 37, // RFC 2538 + DNAME: 39, // RFC 2672 + OPT: 41, // RFC 2671 + APL: 42, // RFC 3123 + DS: 43, // RFC 4034 + SSHFP: 44, // RFC 4255 + IPSECKEY: 45, // RFC 4025 + RRSIG: 46, // RFC 4034 + NSEC: 47, // RFC 4034 + DNSKEY: 48, // RFC 4034 + DHCID: 49, // RFC 4701 + NSEC3: 50, // RFC ???? + NSEC3PARAM: 51, // RFC ???? + HIP: 55, // RFC 5205 + SPF: 99, // RFC 4408 + UINFO: 100, // RFC ???? + UID: 101, // RFC ???? + GID: 102, // RFC ???? + UNSPEC: 103, // RFC ???? + TKEY: 249, // RFC 2930 + TSIG: 250, // RFC 2931 + IXFR: 251, // RFC 1995 + AXFR: 252, // RFC 1035 + MAILB: 253, // RFC 1035 + MAILA: 254, // RFC 1035 + ANY: 255, // RFC 1035 + DLV: 32769, // RFC 4431 +}; diff --git a/netwerk/dns/mdns/libmdns/fallback/DataReader.jsm b/netwerk/dns/mdns/libmdns/fallback/DataReader.jsm new file mode 100644 index 0000000000..978ab4dde0 --- /dev/null +++ b/netwerk/dns/mdns/libmdns/fallback/DataReader.jsm @@ -0,0 +1,134 @@ +/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +var EXPORTED_SYMBOLS = ["DataReader"]; + +class DataReader { + // `data` is `Uint8Array` + constructor(data, startByte = 0) { + this._data = data; + this._cursor = startByte; + } + + get buffer() { + return this._data.buffer; + } + + get data() { + return this._data; + } + + get eof() { + return this._cursor >= this._data.length; + } + + getBytes(length = 1) { + if (!length) { + return new Uint8Array(); + } + + let end = this._cursor + length; + if (end > this._data.length) { + return new Uint8Array(); + } + + let uint8Array = new Uint8Array(this.buffer.slice(this._cursor, end)); + this._cursor += length; + + return uint8Array; + } + + getString(length) { + let uint8Array = this.getBytes(length); + return _uint8ArrayToString(uint8Array); + } + + getValue(length) { + let uint8Array = this.getBytes(length); + return _uint8ArrayToValue(uint8Array); + } + + getLabel(decompressData) { + let parts = []; + let partLength; + + while ((partLength = this.getValue(1))) { + // If a length has been specified instead of a pointer, + // read the string of the specified length. + if (partLength !== 0xc0) { + parts.push(this.getString(partLength)); + continue; + } + + // TODO: Handle case where we have a pointer to the label + parts.push(String.fromCharCode(0xc0) + this.getString(1)); + break; + } + + let label = parts.join("."); + + return _decompressLabel(label, decompressData || this._data); + } +} + +/** + * @private + */ +function _uint8ArrayToValue(uint8Array) { + let length = uint8Array.length; + if (length === 0) { + return null; + } + + let value = 0; + for (let i = 0; i < length; i++) { + value = value << 8; + value += uint8Array[i]; + } + + return value; +} + +/** + * @private + */ +function _uint8ArrayToString(uint8Array) { + let length = uint8Array.length; + if (length === 0) { + return ""; + } + + let results = []; + for (let i = 0; i < length; i += 1024) { + results.push( + String.fromCharCode.apply(null, uint8Array.subarray(i, i + 1024)) + ); + } + + return results.join(""); +} + +/** + * @private + */ +function _decompressLabel(label, decompressData) { + let result = ""; + + for (let i = 0, length = label.length; i < length; i++) { + if (label.charCodeAt(i) !== 0xc0) { + result += label.charAt(i); + continue; + } + + i++; + + let reader = new DataReader(decompressData, label.charCodeAt(i)); + result += _decompressLabel(reader.getLabel(), decompressData); + } + + return result; +} diff --git a/netwerk/dns/mdns/libmdns/fallback/DataWriter.jsm b/netwerk/dns/mdns/libmdns/fallback/DataWriter.jsm new file mode 100644 index 0000000000..05949a1d05 --- /dev/null +++ b/netwerk/dns/mdns/libmdns/fallback/DataWriter.jsm @@ -0,0 +1,97 @@ +/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +var EXPORTED_SYMBOLS = ["DataWriter"]; + +class DataWriter { + constructor(data, maxBytes = 512) { + if (typeof data === "number") { + maxBytes = data; + data = undefined; + } + + this._buffer = new ArrayBuffer(maxBytes); + this._data = new Uint8Array(this._buffer); + this._cursor = 0; + + if (data) { + this.putBytes(data); + } + } + + get buffer() { + return this._buffer.slice(0, this._cursor); + } + + get data() { + return new Uint8Array(this.buffer); + } + + // `data` is `Uint8Array` + putBytes(data) { + if (this._cursor + data.length > this._data.length) { + throw new Error("DataWriter buffer is exceeded"); + } + + for (let i = 0, length = data.length; i < length; i++) { + this._data[this._cursor] = data[i]; + this._cursor++; + } + } + + putByte(byte) { + if (this._cursor + 1 > this._data.length) { + throw new Error("DataWriter buffer is exceeded"); + } + + this._data[this._cursor] = byte; + this._cursor++; + } + + putValue(value, length) { + length = length || 1; + if (length == 1) { + this.putByte(value); + } else { + this.putBytes(_valueToUint8Array(value, length)); + } + } + + putLabel(label) { + // Eliminate any trailing '.'s in the label (valid in text representation). + label = label.replace(/\.$/, ""); + let parts = label.split("."); + parts.forEach(part => { + this.putLengthString(part); + }); + this.putValue(0); + } + + putLengthString(string) { + if (string.length > 0xff) { + throw new Error("String too long."); + } + this.putValue(string.length); + for (let i = 0; i < string.length; i++) { + this.putValue(string.charCodeAt(i)); + } + } +} + +/** + * @private + */ +function _valueToUint8Array(value, length) { + let arrayBuffer = new ArrayBuffer(length); + let uint8Array = new Uint8Array(arrayBuffer); + for (let i = length - 1; i >= 0; i--) { + uint8Array[i] = value & 0xff; + value = value >> 8; + } + + return uint8Array; +} diff --git a/netwerk/dns/mdns/libmdns/fallback/MulticastDNS.jsm b/netwerk/dns/mdns/libmdns/fallback/MulticastDNS.jsm new file mode 100644 index 0000000000..efc21fe523 --- /dev/null +++ b/netwerk/dns/mdns/libmdns/fallback/MulticastDNS.jsm @@ -0,0 +1,985 @@ +/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +var EXPORTED_SYMBOLS = ["MulticastDNS"]; + +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); +const { clearTimeout, setTimeout } = ChromeUtils.import( + "resource://gre/modules/Timer.jsm" +); + +const { DNSPacket } = ChromeUtils.import( + "resource://gre/modules/DNSPacket.jsm" +); +const { DNSRecord } = ChromeUtils.import( + "resource://gre/modules/DNSRecord.jsm" +); +const { DNSResourceRecord } = ChromeUtils.import( + "resource://gre/modules/DNSResourceRecord.jsm" +); +const { + DNS_AUTHORITATIVE_ANSWER_CODES, + DNS_CLASS_CODES, + DNS_QUERY_RESPONSE_CODES, + DNS_RECORD_TYPES, +} = ChromeUtils.import("resource://gre/modules/DNSTypes.jsm"); + +const NS_NETWORK_LINK_TOPIC = "network:link-status-changed"; + +let networkInfoService = Cc[ + "@mozilla.org/network-info-service;1" +].createInstance(Ci.nsINetworkInfoService); + +const DEBUG = true; + +const MDNS_MULTICAST_GROUP = "224.0.0.251"; +const MDNS_PORT = 5353; +const DEFAULT_TTL = 120; + +function debug(msg) { + dump("MulticastDNS: " + msg + "\n"); +} + +function ServiceKey(svc) { + return ( + "" + + svc.serviceType.length + + "/" + + svc.serviceType + + "|" + + svc.serviceName.length + + "/" + + svc.serviceName + + "|" + + svc.port + ); +} + +function TryGet(obj, name) { + try { + return obj[name]; + } catch (err) { + return undefined; + } +} + +function IsIpv4Address(addr) { + let parts = addr.split("."); + if (parts.length != 4) { + return false; + } + for (let part of parts) { + let partInt = Number.parseInt(part, 10); + if (partInt.toString() != part) { + return false; + } + if (partInt < 0 || partInt >= 256) { + return false; + } + } + return true; +} + +class PublishedService { + constructor(attrs) { + this.serviceType = attrs.serviceType.replace(/\.$/, ""); + this.serviceName = attrs.serviceName; + this.domainName = TryGet(attrs, "domainName") || "local"; + this.address = TryGet(attrs, "address") || "0.0.0.0"; + this.port = attrs.port; + this.serviceAttrs = _propertyBagToObject(TryGet(attrs, "attributes") || {}); + this.host = TryGet(attrs, "host"); + this.key = this.generateKey(); + this.lastAdvertised = undefined; + this.advertiseTimer = undefined; + } + + equals(svc) { + return ( + this.port == svc.port && + this.serviceName == svc.serviceName && + this.serviceType == svc.serviceType + ); + } + + generateKey() { + return ServiceKey(this); + } + + ptrMatch(name) { + return name == this.serviceType + "." + this.domainName; + } + + clearAdvertiseTimer() { + if (!this.advertiseTimer) { + return; + } + clearTimeout(this.advertiseTimer); + this.advertiseTimer = undefined; + } +} + +class MulticastDNS { + constructor() { + this._listeners = new Map(); + this._sockets = new Map(); + this._services = new Map(); + this._discovered = new Map(); + this._querySocket = undefined; + this._broadcastReceiverSocket = undefined; + this._broadcastTimer = undefined; + + this._networkLinkObserver = { + observe: (subject, topic, data) => { + DEBUG && + debug( + NS_NETWORK_LINK_TOPIC + + "(" + + data + + "); Clearing list of previously discovered services" + ); + this._discovered.clear(); + }, + }; + } + + _attachNetworkLinkObserver() { + if (this._networkLinkObserverTimeout) { + clearTimeout(this._networkLinkObserverTimeout); + } + + if (!this._isNetworkLinkObserverAttached) { + DEBUG && debug("Attaching observer " + NS_NETWORK_LINK_TOPIC); + Services.obs.addObserver( + this._networkLinkObserver, + NS_NETWORK_LINK_TOPIC + ); + this._isNetworkLinkObserverAttached = true; + } + } + + _detachNetworkLinkObserver() { + if (this._isNetworkLinkObserverAttached) { + if (this._networkLinkObserverTimeout) { + clearTimeout(this._networkLinkObserverTimeout); + } + + this._networkLinkObserverTimeout = setTimeout(() => { + DEBUG && debug("Detaching observer " + NS_NETWORK_LINK_TOPIC); + Services.obs.removeObserver( + this._networkLinkObserver, + NS_NETWORK_LINK_TOPIC + ); + this._isNetworkLinkObserverAttached = false; + this._networkLinkObserverTimeout = null; + }, 5000); + } + } + + startDiscovery(aServiceType, aListener) { + DEBUG && debug('startDiscovery("' + aServiceType + '")'); + let { serviceType } = _parseServiceDomainName(aServiceType); + + this._attachNetworkLinkObserver(); + this._addServiceListener(serviceType, aListener); + + try { + this._query(serviceType + ".local"); + aListener.onDiscoveryStarted(serviceType); + } catch (e) { + DEBUG && debug('startDiscovery("' + serviceType + '") FAILED: ' + e); + this._removeServiceListener(serviceType, aListener); + aListener.onStartDiscoveryFailed(serviceType, Cr.NS_ERROR_FAILURE); + } + } + + stopDiscovery(aServiceType, aListener) { + DEBUG && debug('stopDiscovery("' + aServiceType + '")'); + let { serviceType } = _parseServiceDomainName(aServiceType); + + this._detachNetworkLinkObserver(); + this._removeServiceListener(serviceType, aListener); + + aListener.onDiscoveryStopped(serviceType); + + this._checkCloseSockets(); + } + + resolveService(aServiceInfo, aListener) { + DEBUG && debug("resolveService(): " + aServiceInfo.serviceName); + + // Address info is already resolved during discovery + setTimeout(() => aListener.onServiceResolved(aServiceInfo)); + } + + registerService(aServiceInfo, aListener) { + DEBUG && debug("registerService(): " + aServiceInfo.serviceName); + + // Initialize the broadcast receiver socket in case it + // hasn't already been started so we can listen for + // multicast queries/announcements on all interfaces. + this._getBroadcastReceiverSocket(); + + for (let name of ["port", "serviceName", "serviceType"]) { + if (!TryGet(aServiceInfo, name)) { + aListener.onRegistrationFailed(aServiceInfo, Cr.NS_ERROR_FAILURE); + throw new Error('Invalid nsIDNSServiceInfo; Missing "' + name + '"'); + } + } + + let publishedService; + try { + publishedService = new PublishedService(aServiceInfo); + } catch (e) { + DEBUG && + debug("Error constructing PublishedService: " + e + " - " + e.stack); + setTimeout(() => + aListener.onRegistrationFailed(aServiceInfo, Cr.NS_ERROR_FAILURE) + ); + return; + } + + // Ensure such a service does not already exist. + if (this._services.get(publishedService.key)) { + setTimeout(() => + aListener.onRegistrationFailed(aServiceInfo, Cr.NS_ERROR_FAILURE) + ); + return; + } + + // Make sure that the service addr is '0.0.0.0', or there is at least one + // socket open on the address the service is open on. + this._getSockets().then(sockets => { + if ( + publishedService.address != "0.0.0.0" && + !sockets.get(publishedService.address) + ) { + setTimeout(() => + aListener.onRegistrationFailed(aServiceInfo, Cr.NS_ERROR_FAILURE) + ); + return; + } + + this._services.set(publishedService.key, publishedService); + + // Service registered.. call onServiceRegistered on next tick. + setTimeout(() => aListener.onServiceRegistered(aServiceInfo)); + + // Set a timeout to start advertising the service too. + publishedService.advertiseTimer = setTimeout(() => { + this._advertiseService(publishedService.key, /* firstAdv = */ true); + }); + }); + } + + unregisterService(aServiceInfo, aListener) { + DEBUG && debug("unregisterService(): " + aServiceInfo.serviceName); + + let serviceKey; + try { + serviceKey = ServiceKey(aServiceInfo); + } catch (e) { + setTimeout(() => + aListener.onUnregistrationFailed(aServiceInfo, Cr.NS_ERROR_FAILURE) + ); + return; + } + + let publishedService = this._services.get(serviceKey); + if (!publishedService) { + setTimeout(() => + aListener.onUnregistrationFailed(aServiceInfo, Cr.NS_ERROR_FAILURE) + ); + return; + } + + // Clear any advertise timeout for this published service. + publishedService.clearAdvertiseTimer(); + + // Delete the service from the service map. + if (!this._services.delete(serviceKey)) { + setTimeout(() => + aListener.onUnregistrationFailed(aServiceInfo, Cr.NS_ERROR_FAILURE) + ); + return; + } + + // Check the broadcast timer again to rejig when it should run next. + this._checkStartBroadcastTimer(); + + // Check to see if sockets should be closed, and if so close them. + this._checkCloseSockets(); + + aListener.onServiceUnregistered(aServiceInfo); + } + + _respondToQuery(serviceKey, message) { + let address = message.fromAddr.address; + let port = message.fromAddr.port; + DEBUG && + debug( + "_respondToQuery(): key=" + + serviceKey + + ", fromAddr=" + + address + + ":" + + port + ); + + let publishedService = this._services.get(serviceKey); + if (!publishedService) { + debug("_respondToQuery Could not find service (key=" + serviceKey + ")"); + return; + } + + DEBUG && + debug("_respondToQuery(): key=" + serviceKey + ": SENDING RESPONSE"); + this._advertiseServiceHelper(publishedService, { address, port }); + } + + _advertiseService(serviceKey, firstAdv) { + DEBUG && debug("_advertiseService(): key=" + serviceKey); + let publishedService = this._services.get(serviceKey); + if (!publishedService) { + debug( + "_advertiseService Could not find service to advertise (key=" + + serviceKey + + ")" + ); + return; + } + + publishedService.advertiseTimer = undefined; + + this._advertiseServiceHelper(publishedService, null).then(() => { + // If first advertisement, re-advertise in 1 second. + // Otherwise, set the lastAdvertised time. + if (firstAdv) { + publishedService.advertiseTimer = setTimeout(() => { + this._advertiseService(serviceKey); + }, 1000); + } else { + publishedService.lastAdvertised = Date.now(); + this._checkStartBroadcastTimer(); + } + }); + } + + _advertiseServiceHelper(svc, target) { + if (!target) { + target = { address: MDNS_MULTICAST_GROUP, port: MDNS_PORT }; + } + + return this._getSockets().then(sockets => { + sockets.forEach((socket, address) => { + if (svc.address == "0.0.0.0" || address == svc.address) { + let packet = this._makeServicePacket(svc, [address]); + let data = packet.serialize(); + try { + socket.send(target.address, target.port, data); + } catch (err) { + DEBUG && + debug( + "Failed to send packet to " + target.address + ":" + target.port + ); + } + } + }); + }); + } + + _cancelBroadcastTimer() { + if (!this._broadcastTimer) { + return; + } + clearTimeout(this._broadcastTimer); + this._broadcastTimer = undefined; + } + + _checkStartBroadcastTimer() { + DEBUG && debug("_checkStartBroadcastTimer()"); + // Cancel any existing broadcasting timer. + this._cancelBroadcastTimer(); + + let now = Date.now(); + + // Go through services and find services to broadcast. + let bcastServices = []; + let nextBcastWait = undefined; + for (let [, publishedService] of this._services) { + // if lastAdvertised is undefined, service hasn't finished it's initial + // two broadcasts. + if (publishedService.lastAdvertised === undefined) { + continue; + } + + // Otherwise, check lastAdvertised against now. + let msSinceAdv = now - publishedService.lastAdvertised; + + // If msSinceAdv is more than 90% of the way to the TTL, advertise now. + if (msSinceAdv > DEFAULT_TTL * 1000 * 0.9) { + bcastServices.push(publishedService); + continue; + } + + // Otherwise, calculate the next time to advertise for this service. + // We set that at 95% of the time to the TTL expiry. + let nextAdvWait = DEFAULT_TTL * 1000 * 0.95 - msSinceAdv; + if (nextBcastWait === undefined || nextBcastWait > nextAdvWait) { + nextBcastWait = nextAdvWait; + } + } + + // Schedule an immediate advertisement of all services to be advertised now. + for (let svc of bcastServices) { + svc.advertiseTimer = setTimeout(() => this._advertiseService(svc.key)); + } + + // Schedule next broadcast check for the next bcast time. + if (nextBcastWait !== undefined) { + DEBUG && + debug( + "_checkStartBroadcastTimer(): Scheduling next check in " + + nextBcastWait + + "ms" + ); + this._broadcastTimer = setTimeout( + () => this._checkStartBroadcastTimer(), + nextBcastWait + ); + } + } + + _query(name) { + DEBUG && debug('query("' + name + '")'); + let packet = new DNSPacket(); + packet.setFlag("QR", DNS_QUERY_RESPONSE_CODES.QUERY); + + // PTR Record + packet.addRecord( + "QD", + new DNSRecord({ + name, + recordType: DNS_RECORD_TYPES.PTR, + classCode: DNS_CLASS_CODES.IN, + cacheFlush: true, + }) + ); + + let data = packet.serialize(); + + // Initialize the broadcast receiver socket in case it + // hasn't already been started so we can listen for + // multicast queries/announcements on all interfaces. + this._getBroadcastReceiverSocket(); + + this._getQuerySocket().then(querySocket => { + DEBUG && debug('sending query on query socket ("' + name + '")'); + querySocket.send(MDNS_MULTICAST_GROUP, MDNS_PORT, data); + }); + + // Automatically announce previously-discovered + // services that match and haven't expired yet. + setTimeout(() => { + DEBUG && + debug('announcing previously discovered services ("' + name + '")'); + let { serviceType } = _parseServiceDomainName(name); + + this._clearExpiredDiscoveries(); + this._discovered.forEach((discovery, key) => { + let serviceInfo = discovery.serviceInfo; + if (serviceInfo.serviceType !== serviceType) { + return; + } + + let listeners = this._listeners.get(serviceInfo.serviceType) || []; + listeners.forEach(listener => { + listener.onServiceFound(serviceInfo); + }); + }); + }); + } + + _clearExpiredDiscoveries() { + this._discovered.forEach((discovery, key) => { + if (discovery.expireTime < Date.now()) { + this._discovered.delete(key); + } + }); + } + + _handleQueryPacket(packet, message) { + packet.getRecords(["QD"]).forEach(record => { + // Don't respond if the query's class code is not IN or ANY. + if ( + record.classCode !== DNS_CLASS_CODES.IN && + record.classCode !== DNS_CLASS_CODES.ANY + ) { + return; + } + + // Don't respond if the query's record type is not PTR or ANY. + if ( + record.recordType !== DNS_RECORD_TYPES.PTR && + record.recordType !== DNS_RECORD_TYPES.ANY + ) { + return; + } + + for (let [serviceKey, publishedService] of this._services) { + DEBUG && debug("_handleQueryPacket: " + packet.toJSON()); + if (publishedService.ptrMatch(record.name)) { + this._respondToQuery(serviceKey, message); + } + } + }); + } + + _makeServicePacket(service, addresses) { + let packet = new DNSPacket(); + packet.setFlag("QR", DNS_QUERY_RESPONSE_CODES.RESPONSE); + packet.setFlag("AA", DNS_AUTHORITATIVE_ANSWER_CODES.YES); + + let host = service.host || _hostname; + + // e.g.: foo-bar-service._http._tcp.local + let serviceDomainName = + service.serviceName + "." + service.serviceType + ".local"; + + // PTR Record + packet.addRecord( + "AN", + new DNSResourceRecord({ + name: service.serviceType + ".local", // e.g.: _http._tcp.local + recordType: DNS_RECORD_TYPES.PTR, + data: serviceDomainName, + }) + ); + + // SRV Record + packet.addRecord( + "AR", + new DNSResourceRecord({ + name: serviceDomainName, + recordType: DNS_RECORD_TYPES.SRV, + classCode: DNS_CLASS_CODES.IN, + cacheFlush: true, + data: { + priority: 0, + weight: 0, + port: service.port, + target: host, // e.g.: My-Android-Phone.local + }, + }) + ); + + // A Records + for (let address of addresses) { + packet.addRecord( + "AR", + new DNSResourceRecord({ + name: host, + recordType: DNS_RECORD_TYPES.A, + data: address, + }) + ); + } + + // TXT Record + packet.addRecord( + "AR", + new DNSResourceRecord({ + name: serviceDomainName, + recordType: DNS_RECORD_TYPES.TXT, + classCode: DNS_CLASS_CODES.IN, + cacheFlush: true, + data: service.serviceAttrs || {}, + }) + ); + + return packet; + } + + _handleResponsePacket(packet, message) { + let services = {}; + let hosts = {}; + + let srvRecords = packet.getRecords(["AN", "AR"], DNS_RECORD_TYPES.SRV); + let txtRecords = packet.getRecords(["AN", "AR"], DNS_RECORD_TYPES.TXT); + let ptrRecords = packet.getRecords(["AN", "AR"], DNS_RECORD_TYPES.PTR); + let aRecords = packet.getRecords(["AN", "AR"], DNS_RECORD_TYPES.A); + + srvRecords.forEach(record => { + let data = record.data || {}; + + services[record.name] = { + host: data.target, + port: data.port, + ttl: record.ttl, + }; + }); + + txtRecords.forEach(record => { + if (!services[record.name]) { + return; + } + + services[record.name].attributes = record.data; + }); + + aRecords.forEach(record => { + if (IsIpv4Address(record.data)) { + hosts[record.name] = record.data; + } + }); + + ptrRecords.forEach(record => { + let name = record.data; + if (!services[name]) { + return; + } + + let { host, port } = services[name]; + if (!host || !port) { + return; + } + + let { serviceName, serviceType, domainName } = _parseServiceDomainName( + name + ); + if (!serviceName || !serviceType || !domainName) { + return; + } + + let address = hosts[host]; + if (!address) { + return; + } + + let ttl = services[name].ttl || 0; + let serviceInfo = { + serviceName, + serviceType, + host, + address, + port, + domainName, + attributes: services[name].attributes || {}, + }; + + this._onServiceFound(serviceInfo, ttl); + }); + } + + _onServiceFound(serviceInfo, ttl = 0) { + let expireTime = Date.now() + ttl * 1000; + let key = + serviceInfo.serviceName + + "." + + serviceInfo.serviceType + + "." + + serviceInfo.domainName + + " @" + + serviceInfo.address + + ":" + + serviceInfo.port; + + // If this service was already discovered, just update + // its expiration time and don't re-emit it. + if (this._discovered.has(key)) { + this._discovered.get(key).expireTime = expireTime; + return; + } + + this._discovered.set(key, { + serviceInfo, + expireTime, + }); + + let listeners = this._listeners.get(serviceInfo.serviceType) || []; + listeners.forEach(listener => { + listener.onServiceFound(serviceInfo); + }); + + DEBUG && debug("_onServiceFound()" + serviceInfo.serviceName); + } + + /** + * Gets a non-exclusive socket on 0.0.0.0:{random} to send + * multicast queries on all interfaces. This socket does + * not need to join a multicast group since it is still + * able to *send* multicast queries, but it does not need + * to *listen* for multicast queries/announcements since + * the `_broadcastReceiverSocket` is already handling them. + */ + _getQuerySocket() { + return new Promise((resolve, reject) => { + if (!this._querySocket) { + this._querySocket = _openSocket("0.0.0.0", 0, { + onPacketReceived: this._onPacketReceived.bind(this), + onStopListening: this._onStopListening.bind(this), + }); + } + resolve(this._querySocket); + }); + } + + /** + * Gets a non-exclusive socket on 0.0.0.0:5353 to listen + * for multicast queries/announcements on all interfaces. + * Since this socket needs to listen for multicast queries + * and announcements, this socket joins the multicast + * group on *all* interfaces (0.0.0.0). + */ + _getBroadcastReceiverSocket() { + return new Promise((resolve, reject) => { + if (!this._broadcastReceiverSocket) { + this._broadcastReceiverSocket = _openSocket( + "0.0.0.0", + MDNS_PORT, + { + onPacketReceived: this._onPacketReceived.bind(this), + onStopListening: this._onStopListening.bind(this), + }, + /* multicastInterface = */ "0.0.0.0" + ); + } + resolve(this._broadcastReceiverSocket); + }); + } + + /** + * Gets a non-exclusive socket for each interface on + * {iface-ip}:5353 for sending query responses as + * well as for listening for unicast queries. These + * sockets do not need to join a multicast group + * since they are still able to *send* multicast + * query responses, but they do not need to *listen* + * for multicast queries since the `_querySocket` is + * already handling them. + */ + _getSockets() { + return new Promise(resolve => { + if (this._sockets.size > 0) { + resolve(this._sockets); + return; + } + + Promise.all([getAddresses(), getHostname()]).then(() => { + _addresses.forEach(address => { + let socket = _openSocket(address, MDNS_PORT, null); + this._sockets.set(address, socket); + }); + + resolve(this._sockets); + }); + }); + } + + _checkCloseSockets() { + // Nothing to do if no sockets to close. + if (this._sockets.size == 0) { + return; + } + + // Don't close sockets if discovery listeners are still present. + if (this._listeners.size > 0) { + return; + } + + // Don't close sockets if advertised services are present. + // Since we need to listen for service queries and respond to them. + if (this._services.size > 0) { + return; + } + + this._closeSockets(); + } + + _closeSockets() { + this._sockets.forEach(socket => socket.close()); + this._sockets.clear(); + } + + _onPacketReceived(socket, message) { + let packet = DNSPacket.parse(message.rawData); + + switch (packet.getFlag("QR")) { + case DNS_QUERY_RESPONSE_CODES.QUERY: + this._handleQueryPacket(packet, message); + break; + case DNS_QUERY_RESPONSE_CODES.RESPONSE: + this._handleResponsePacket(packet, message); + break; + default: + break; + } + } + + _onStopListening(socket, status) { + DEBUG && debug("_onStopListening() " + status); + } + + _addServiceListener(serviceType, listener) { + let listeners = this._listeners.get(serviceType); + if (!listeners) { + listeners = []; + this._listeners.set(serviceType, listeners); + } + + if (!listeners.find(l => l === listener)) { + listeners.push(listener); + } + } + + _removeServiceListener(serviceType, listener) { + let listeners = this._listeners.get(serviceType); + if (!listeners) { + return; + } + + let index = listeners.findIndex(l => l === listener); + if (index >= 0) { + listeners.splice(index, 1); + } + + if (listeners.length === 0) { + this._listeners.delete(serviceType); + } + } +} + +let _addresses; + +/** + * @private + */ +function getAddresses() { + return new Promise((resolve, reject) => { + if (_addresses) { + resolve(_addresses); + return; + } + + networkInfoService.listNetworkAddresses({ + onListedNetworkAddresses(aAddressArray) { + _addresses = aAddressArray.filter(address => { + return ( + !address.includes("%p2p") && // No WiFi Direct interfaces + !address.includes(":") && // XXX: No IPv6 for now + address != "127.0.0.1" + ); // No ipv4 loopback addresses. + }); + + DEBUG && debug("getAddresses(): " + _addresses); + resolve(_addresses); + }, + + onListNetworkAddressesFailed() { + DEBUG && debug("getAddresses() FAILED!"); + resolve([]); + }, + }); + }); +} + +let _hostname; + +/** + * @private + */ +function getHostname() { + return new Promise(resolve => { + if (_hostname) { + resolve(_hostname); + return; + } + + networkInfoService.getHostname({ + onGotHostname(aHostname) { + _hostname = aHostname.replace(/\s/g, "-") + ".local"; + + DEBUG && debug("getHostname(): " + _hostname); + resolve(_hostname); + }, + + onGetHostnameFailed() { + DEBUG && debug("getHostname() FAILED"); + resolve("localhost"); + }, + }); + }); +} + +/** + * Parse fully qualified domain name to service name, instance name, + * and domain name. See https://tools.ietf.org/html/rfc6763#section-7. + * + * Example: 'foo-bar-service._http._tcp.local' -> { + * serviceName: 'foo-bar-service', + * serviceType: '_http._tcp', + * domainName: 'local' + * } + * + * @private + */ +function _parseServiceDomainName(serviceDomainName) { + let parts = serviceDomainName.split("."); + let index = Math.max(parts.lastIndexOf("_tcp"), parts.lastIndexOf("_udp")); + + return { + serviceName: parts.splice(0, index - 1).join("."), + serviceType: parts.splice(0, 2).join("."), + domainName: parts.join("."), + }; +} + +/** + * @private + */ +function _propertyBagToObject(propBag) { + let result = {}; + if (propBag.QueryInterface) { + propBag.QueryInterface(Ci.nsIPropertyBag2); + for (let prop of propBag.enumerator) { + result[prop.name] = prop.value.toString(); + } + } else { + for (let name in propBag) { + result[name] = propBag[name].toString(); + } + } + return result; +} + +/** + * @private + */ +function _openSocket(addr, port, handler, multicastInterface) { + let socket = Cc["@mozilla.org/network/udp-socket;1"].createInstance( + Ci.nsIUDPSocket + ); + socket.init2( + addr, + port, + Services.scriptSecurityManager.getSystemPrincipal(), + true + ); + + if (handler) { + socket.asyncListen({ + onPacketReceived: handler.onPacketReceived, + onStopListening: handler.onStopListening, + }); + } + + if (multicastInterface) { + socket.joinMulticast(MDNS_MULTICAST_GROUP, multicastInterface); + } + + return socket; +} diff --git a/netwerk/dns/mdns/libmdns/moz.build b/netwerk/dns/mdns/libmdns/moz.build new file mode 100644 index 0000000000..f9c025fa82 --- /dev/null +++ b/netwerk/dns/mdns/libmdns/moz.build @@ -0,0 +1,51 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +if CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa": + UNIFIED_SOURCES += [ + "MDNSResponderOperator.cpp", + "MDNSResponderReply.cpp", + "nsDNSServiceDiscovery.cpp", + ] + + LOCAL_INCLUDES += [ + "/netwerk/base", + ] + +else: + EXTRA_JS_MODULES += [ + "DNSServiceDiscovery.jsm", + "fallback/DataReader.jsm", + "fallback/DataWriter.jsm", + "fallback/DNSPacket.jsm", + "fallback/DNSRecord.jsm", + "fallback/DNSResourceRecord.jsm", + "fallback/DNSTypes.jsm", + "fallback/MulticastDNS.jsm", + ] + + if CONFIG["MOZ_WIDGET_TOOLKIT"] == "android": + EXTRA_JS_MODULES += [ + "MulticastDNSAndroid.jsm", + ] + +UNIFIED_SOURCES += [ + "nsDNSServiceInfo.cpp", +] + +XPCOM_MANIFESTS += [ + "components.conf", +] + +XPCOM_MANIFESTS += [ + "components.conf", +] + +include("/ipc/chromium/chromium-config.mozbuild") +FINAL_LIBRARY = "xul" + +if CONFIG["CC_TYPE"] in ("clang", "gcc"): + CXXFLAGS += ["-Wno-error=shadow"] diff --git a/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.cpp b/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.cpp new file mode 100644 index 0000000000..12a23e56a4 --- /dev/null +++ b/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.cpp @@ -0,0 +1,228 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsDNSServiceDiscovery.h" +#include "MDNSResponderOperator.h" +#include "nsICancelable.h" +#include "nsXULAppAPI.h" +#include "private/pprio.h" + +namespace mozilla { +namespace net { + +namespace { + +inline void StartService() {} + +inline void StopService() {} + +class ServiceCounter { + public: + static bool IsServiceRunning() { return !!sUseCount; } + + private: + static uint32_t sUseCount; + + protected: + ServiceCounter() { + MOZ_ASSERT(NS_IsMainThread()); + if (!sUseCount++) { + StartService(); + } + } + + virtual ~ServiceCounter() { + MOZ_ASSERT(NS_IsMainThread()); + if (!--sUseCount) { + StopService(); + } + } +}; + +uint32_t ServiceCounter::sUseCount = 0; + +class DiscoveryRequest final : public nsICancelable, private ServiceCounter { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSICANCELABLE + + explicit DiscoveryRequest(nsDNSServiceDiscovery* aService, + nsIDNSServiceDiscoveryListener* aListener); + + private: + virtual ~DiscoveryRequest() { Cancel(NS_OK); } + + RefPtr<nsDNSServiceDiscovery> mService; + nsIDNSServiceDiscoveryListener* mListener; +}; + +NS_IMPL_ISUPPORTS(DiscoveryRequest, nsICancelable) + +DiscoveryRequest::DiscoveryRequest(nsDNSServiceDiscovery* aService, + nsIDNSServiceDiscoveryListener* aListener) + : mService(aService), mListener(aListener) {} + +NS_IMETHODIMP +DiscoveryRequest::Cancel(nsresult aReason) { + if (mService) { + mService->StopDiscovery(mListener); + } + + mService = nullptr; + return NS_OK; +} + +class RegisterRequest final : public nsICancelable, private ServiceCounter { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSICANCELABLE + + explicit RegisterRequest(nsDNSServiceDiscovery* aService, + nsIDNSRegistrationListener* aListener); + + private: + virtual ~RegisterRequest() { Cancel(NS_OK); } + + RefPtr<nsDNSServiceDiscovery> mService; + nsIDNSRegistrationListener* mListener; +}; + +NS_IMPL_ISUPPORTS(RegisterRequest, nsICancelable) + +RegisterRequest::RegisterRequest(nsDNSServiceDiscovery* aService, + nsIDNSRegistrationListener* aListener) + : mService(aService), mListener(aListener) {} + +NS_IMETHODIMP +RegisterRequest::Cancel(nsresult aReason) { + if (mService) { + mService->UnregisterService(mListener); + } + + mService = nullptr; + return NS_OK; +} + +} // namespace + +NS_IMPL_ISUPPORTS(nsDNSServiceDiscovery, nsIDNSServiceDiscovery) + +nsresult nsDNSServiceDiscovery::Init() { + if (!XRE_IsParentProcess()) { + MOZ_ASSERT(false, + "nsDNSServiceDiscovery can only be used in parent process"); + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +NS_IMETHODIMP +nsDNSServiceDiscovery::StartDiscovery(const nsACString& aServiceType, + nsIDNSServiceDiscoveryListener* aListener, + nsICancelable** aRetVal) { + MOZ_ASSERT(aRetVal); + + nsresult rv; + if (NS_WARN_IF(NS_FAILED(rv = StopDiscovery(aListener)))) { + return rv; + } + + nsCOMPtr<nsICancelable> req = new DiscoveryRequest(this, aListener); + RefPtr<BrowseOperator> browserOp = + new BrowseOperator(aServiceType, aListener); + if (NS_WARN_IF(NS_FAILED(rv = browserOp->Start()))) { + return rv; + } + + mDiscoveryMap.Put(aListener, std::move(browserOp)); + + req.forget(aRetVal); + + return NS_OK; +} + +NS_IMETHODIMP +nsDNSServiceDiscovery::StopDiscovery( + nsIDNSServiceDiscoveryListener* aListener) { + nsresult rv; + + RefPtr<BrowseOperator> browserOp; + if (!mDiscoveryMap.Get(aListener, getter_AddRefs(browserOp))) { + return NS_OK; + } + + browserOp->Cancel(); // cancel non-started operation + if (NS_WARN_IF(NS_FAILED(rv = browserOp->Stop()))) { + return rv; + } + + mDiscoveryMap.Remove(aListener); + return NS_OK; +} + +NS_IMETHODIMP +nsDNSServiceDiscovery::RegisterService(nsIDNSServiceInfo* aServiceInfo, + nsIDNSRegistrationListener* aListener, + nsICancelable** aRetVal) { + MOZ_ASSERT(aRetVal); + + nsresult rv; + if (NS_WARN_IF(NS_FAILED(rv = UnregisterService(aListener)))) { + return rv; + } + + nsCOMPtr<nsICancelable> req = new RegisterRequest(this, aListener); + RefPtr<RegisterOperator> registerOp = + new RegisterOperator(aServiceInfo, aListener); + if (NS_WARN_IF(NS_FAILED(rv = registerOp->Start()))) { + return rv; + } + + mRegisterMap.Put(aListener, std::move(registerOp)); + + req.forget(aRetVal); + + return NS_OK; +} + +NS_IMETHODIMP +nsDNSServiceDiscovery::UnregisterService( + nsIDNSRegistrationListener* aListener) { + nsresult rv; + + RefPtr<RegisterOperator> registerOp; + if (!mRegisterMap.Get(aListener, getter_AddRefs(registerOp))) { + return NS_OK; + } + + registerOp->Cancel(); // cancel non-started operation + if (NS_WARN_IF(NS_FAILED(rv = registerOp->Stop()))) { + return rv; + } + + mRegisterMap.Remove(aListener); + return NS_OK; +} + +NS_IMETHODIMP +nsDNSServiceDiscovery::ResolveService(nsIDNSServiceInfo* aServiceInfo, + nsIDNSServiceResolveListener* aListener) { + if (!ServiceCounter::IsServiceRunning()) { + return NS_ERROR_FAILURE; + } + + nsresult rv; + + RefPtr<ResolveOperator> resolveOp = + new ResolveOperator(aServiceInfo, aListener); + if (NS_WARN_IF(NS_FAILED(rv = resolveOp->Start()))) { + return rv; + } + + return NS_OK; +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.h b/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.h new file mode 100644 index 0000000000..0acc9198bd --- /dev/null +++ b/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.h @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_netwerk_dns_mdns_libmdns_nsDNSServiceDiscovery_h +#define mozilla_netwerk_dns_mdns_libmdns_nsDNSServiceDiscovery_h + +#include "nsIDNSServiceDiscovery.h" +#include "nsCOMPtr.h" +#include "mozilla/RefPtr.h" +#include "nsRefPtrHashtable.h" + +namespace mozilla { +namespace net { + +class BrowseOperator; +class RegisterOperator; + +class nsDNSServiceDiscovery final : public nsIDNSServiceDiscovery { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIDNSSERVICEDISCOVERY + + explicit nsDNSServiceDiscovery() = default; + + /* + ** The mDNS service is started on demand. If no one uses, mDNS service will + ** not start. Therefore, all operations before service started will fail + ** and get error code |kDNSServiceErr_ServiceNotRunning| defined in dns_sd.h. + **/ + nsresult Init(); + + nsresult StopDiscovery(nsIDNSServiceDiscoveryListener* aListener); + nsresult UnregisterService(nsIDNSRegistrationListener* aListener); + + private: + virtual ~nsDNSServiceDiscovery() = default; + + nsRefPtrHashtable<nsISupportsHashKey, BrowseOperator> mDiscoveryMap; + nsRefPtrHashtable<nsISupportsHashKey, RegisterOperator> mRegisterMap; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_netwerk_dns_mdns_libmdns_nsDNSServiceDiscovery_h diff --git a/netwerk/dns/mdns/libmdns/nsDNSServiceInfo.cpp b/netwerk/dns/mdns/libmdns/nsDNSServiceInfo.cpp new file mode 100644 index 0000000000..a6825a8d31 --- /dev/null +++ b/netwerk/dns/mdns/libmdns/nsDNSServiceInfo.cpp @@ -0,0 +1,170 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsDNSServiceInfo.h" +#include "nsHashPropertyBag.h" +#include "nsIProperty.h" +#include "nsISimpleEnumerator.h" +#include "nsISupportsImpl.h" +#include "mozilla/Unused.h" + +namespace mozilla { +namespace net { + +NS_IMPL_ISUPPORTS(nsDNSServiceInfo, nsIDNSServiceInfo) + +nsDNSServiceInfo::nsDNSServiceInfo(nsIDNSServiceInfo* aServiceInfo) { + if (NS_WARN_IF(!aServiceInfo)) { + return; + } + + nsAutoCString str; + uint16_t value; + + if (NS_SUCCEEDED(aServiceInfo->GetHost(str))) { + Unused << NS_WARN_IF(NS_FAILED(SetHost(str))); + } + if (NS_SUCCEEDED(aServiceInfo->GetAddress(str))) { + Unused << NS_WARN_IF(NS_FAILED(SetAddress(str))); + } + if (NS_SUCCEEDED(aServiceInfo->GetPort(&value))) { + Unused << NS_WARN_IF(NS_FAILED(SetPort(value))); + } + if (NS_SUCCEEDED(aServiceInfo->GetServiceName(str))) { + Unused << NS_WARN_IF(NS_FAILED(SetServiceName(str))); + } + if (NS_SUCCEEDED(aServiceInfo->GetServiceType(str))) { + Unused << NS_WARN_IF(NS_FAILED(SetServiceType(str))); + } + if (NS_SUCCEEDED(aServiceInfo->GetDomainName(str))) { + Unused << NS_WARN_IF(NS_FAILED(SetDomainName(str))); + } + + nsCOMPtr<nsIPropertyBag2> attributes; // deep copy + if (NS_SUCCEEDED(aServiceInfo->GetAttributes(getter_AddRefs(attributes)))) { + RefPtr<nsHashPropertyBag> newAttributes = new nsHashPropertyBag(); + newAttributes->CopyFrom(attributes); + Unused << NS_WARN_IF(NS_FAILED(SetAttributes(newAttributes))); + } +} + +NS_IMETHODIMP +nsDNSServiceInfo::GetHost(nsACString& aHost) { + if (!mIsHostSet) { + return NS_ERROR_NOT_INITIALIZED; + } + aHost = mHost; + return NS_OK; +} + +NS_IMETHODIMP +nsDNSServiceInfo::SetHost(const nsACString& aHost) { + mHost = aHost; + mIsHostSet = true; + return NS_OK; +} + +NS_IMETHODIMP +nsDNSServiceInfo::GetAddress(nsACString& aAddress) { + if (!mIsAddressSet) { + return NS_ERROR_NOT_INITIALIZED; + } + aAddress = mAddress; + return NS_OK; +} + +NS_IMETHODIMP +nsDNSServiceInfo::SetAddress(const nsACString& aAddress) { + mAddress = aAddress; + mIsAddressSet = true; + return NS_OK; +} + +NS_IMETHODIMP +nsDNSServiceInfo::GetPort(uint16_t* aPort) { + if (NS_WARN_IF(!aPort)) { + return NS_ERROR_INVALID_ARG; + } + if (!mIsPortSet) { + return NS_ERROR_NOT_INITIALIZED; + } + *aPort = mPort; + return NS_OK; +} + +NS_IMETHODIMP +nsDNSServiceInfo::SetPort(uint16_t aPort) { + mPort = aPort; + mIsPortSet = true; + return NS_OK; +} + +NS_IMETHODIMP +nsDNSServiceInfo::GetServiceName(nsACString& aServiceName) { + if (!mIsServiceNameSet) { + return NS_ERROR_NOT_INITIALIZED; + } + aServiceName = mServiceName; + return NS_OK; +} + +NS_IMETHODIMP +nsDNSServiceInfo::SetServiceName(const nsACString& aServiceName) { + mServiceName = aServiceName; + mIsServiceNameSet = true; + return NS_OK; +} + +NS_IMETHODIMP +nsDNSServiceInfo::GetServiceType(nsACString& aServiceType) { + if (!mIsServiceTypeSet) { + return NS_ERROR_NOT_INITIALIZED; + } + aServiceType = mServiceType; + return NS_OK; +} + +NS_IMETHODIMP +nsDNSServiceInfo::SetServiceType(const nsACString& aServiceType) { + mServiceType = aServiceType; + mIsServiceTypeSet = true; + return NS_OK; +} + +NS_IMETHODIMP +nsDNSServiceInfo::GetDomainName(nsACString& aDomainName) { + if (!mIsDomainNameSet) { + return NS_ERROR_NOT_INITIALIZED; + } + aDomainName = mDomainName; + return NS_OK; +} + +NS_IMETHODIMP +nsDNSServiceInfo::SetDomainName(const nsACString& aDomainName) { + mDomainName = aDomainName; + mIsDomainNameSet = true; + return NS_OK; +} + +NS_IMETHODIMP +nsDNSServiceInfo::GetAttributes(nsIPropertyBag2** aAttributes) { + if (!mIsAttributesSet) { + return NS_ERROR_NOT_INITIALIZED; + } + nsCOMPtr<nsIPropertyBag2> attributes(mAttributes); + attributes.forget(aAttributes); + return NS_OK; +} + +NS_IMETHODIMP +nsDNSServiceInfo::SetAttributes(nsIPropertyBag2* aAttributes) { + mAttributes = aAttributes; + mIsAttributesSet = aAttributes ? true : false; + return NS_OK; +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/mdns/libmdns/nsDNSServiceInfo.h b/netwerk/dns/mdns/libmdns/nsDNSServiceInfo.h new file mode 100644 index 0000000000..31fffcc74e --- /dev/null +++ b/netwerk/dns/mdns/libmdns/nsDNSServiceInfo.h @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_netwerk_dns_mdns_libmdns_nsDNSServiceInfo_h +#define mozilla_netwerk_dns_mdns_libmdns_nsDNSServiceInfo_h + +#include "nsCOMPtr.h" +#include "nsIDNSServiceDiscovery.h" +#include "nsIPropertyBag2.h" +#include "nsString.h" + +namespace mozilla { +namespace net { + +class nsDNSServiceInfo final : public nsIDNSServiceInfo { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIDNSSERVICEINFO + + explicit nsDNSServiceInfo() = default; + explicit nsDNSServiceInfo(nsIDNSServiceInfo* aServiceInfo); + + private: + virtual ~nsDNSServiceInfo() = default; + + private: + nsCString mHost; + nsCString mAddress; + uint16_t mPort = 0; + nsCString mServiceName; + nsCString mServiceType; + nsCString mDomainName; + nsCOMPtr<nsIPropertyBag2> mAttributes; + + bool mIsHostSet = false; + bool mIsAddressSet = false; + bool mIsPortSet = false; + bool mIsServiceNameSet = false; + bool mIsServiceTypeSet = false; + bool mIsDomainNameSet = false; + bool mIsAttributesSet = false; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_netwerk_dns_mdns_libmdns_nsDNSServiceInfo_h diff --git a/netwerk/dns/mdns/moz.build b/netwerk/dns/mdns/moz.build new file mode 100644 index 0000000000..561031af96 --- /dev/null +++ b/netwerk/dns/mdns/moz.build @@ -0,0 +1,13 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DIRS += ["libmdns"] + +XPIDL_SOURCES += [ + "nsIDNSServiceDiscovery.idl", +] + +XPIDL_MODULE = "necko_mdns" diff --git a/netwerk/dns/mdns/nsIDNSServiceDiscovery.idl b/netwerk/dns/mdns/nsIDNSServiceDiscovery.idl new file mode 100644 index 0000000000..23c678eccc --- /dev/null +++ b/netwerk/dns/mdns/nsIDNSServiceDiscovery.idl @@ -0,0 +1,219 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsICancelable; +interface nsIPropertyBag2; + +/** + * Service information + */ +[scriptable, uuid(670ed0f9-2fa5-4544-bf1e-ea58ac179374)] +interface nsIDNSServiceInfo : nsISupports +{ + /** + * The host name of the service. (E.g. "Android.local.") + * @throws NS_ERROR_NOT_INITIALIZED when getting unset value. + */ + attribute AUTF8String host; + + /** + * The IP address of the service. + * @throws NS_ERROR_NOT_INITIALIZED when getting unset value. + */ + attribute AUTF8String address; + + /** + * The port number of the service. (E.g. 80) + * @throws NS_ERROR_NOT_INITIALIZED when getting unset value. + */ + attribute unsigned short port; + + /** + * The service name of the service for display. (E.g. "My TV") + * @throws NS_ERROR_NOT_INITIALIZED when getting unset value. + */ + attribute AUTF8String serviceName; + + /** + * The type of the service. (E.g. "_http._tcp") + * @throws NS_ERROR_NOT_INITIALIZED when getting unset value. + */ + attribute AUTF8String serviceType; + + /** + * The domain name of the service. (E.g. "local.") + * @throws NS_ERROR_NOT_INITIALIZED when getting unset value. + */ + attribute AUTF8String domainName; + + /** + * The attributes of the service. + */ + attribute nsIPropertyBag2 attributes; +}; + +/** + * The callback interface for service discovery + */ +[scriptable, uuid(3025b7f2-97bb-435b-b43d-26731b3f5fc4)] +interface nsIDNSServiceDiscoveryListener : nsISupports +{ + /** + * Callback when the discovery begins. + * @param aServiceType + * the service type of |startDiscovery|. + */ + void onDiscoveryStarted(in AUTF8String aServiceType); + + /** + * Callback when the discovery ends. + * @param aServiceType + * the service type of |startDiscovery|. + */ + void onDiscoveryStopped(in AUTF8String aServiceType); + + /** + * Callback when the a service is found. + * @param aServiceInfo + * the info about the found service, where |serviceName|, |aServiceType|, and |domainName| are set. + */ + void onServiceFound(in nsIDNSServiceInfo aServiceInfo); + + /** + * Callback when the a service is lost. + * @param aServiceInfo + * the info about the lost service, where |serviceName|, |aServiceType|, and |domainName| are set. + */ + void onServiceLost(in nsIDNSServiceInfo aServiceInfo); + + /** + * Callback when the discovery cannot start. + * @param aServiceType + * the service type of |startDiscovery|. + * @param aErrorCode + * the error code. + */ + void onStartDiscoveryFailed(in AUTF8String aServiceType, in long aErrorCode); + + /** + * Callback when the discovery cannot stop. + * @param aServiceType + * the service type of |startDiscovery|. + * @param aErrorCode + * the error code. + */ + void onStopDiscoveryFailed(in AUTF8String aServiceType, in long aErrorCode); +}; + +/** + * The callback interface for service registration + */ +[scriptable, uuid(e165e4be-abf4-4963-a66d-ed3ca116e5e4)] +interface nsIDNSRegistrationListener : nsISupports +{ + const long ERROR_SERVICE_NOT_RUNNING = -65563; + + /** + * Callback when the service is registered successfully. + * @param aServiceInfo + * the info about the registered service, + * where |serviceName|, |aServiceType|, and |domainName| are set. + */ + void onServiceRegistered(in nsIDNSServiceInfo aServiceInfo); + + /** + * Callback when the service is unregistered successfully. + * @param aServiceInfo + * the info about the unregistered service. + */ + void onServiceUnregistered(in nsIDNSServiceInfo aServiceInfo); + + /** + * Callback when the service cannot be registered. + * @param aServiceInfo + * the info about the service to be registered. + * @param aErrorCode + * the error code. + */ + void onRegistrationFailed(in nsIDNSServiceInfo aServiceInfo, in long aErrorCode); + + /** + * Callback when the service cannot be unregistered. + * @param aServiceInfo + * the info about the registered service. + * @param aErrorCode + * the error code. + */ + void onUnregistrationFailed(in nsIDNSServiceInfo aServiceInfo, in long aErrorCode); +}; + +/** + * The callback interface for service resolve + */ +[scriptable, uuid(24ee6408-648e-421d-accf-c6e5adeccf97)] +interface nsIDNSServiceResolveListener : nsISupports +{ + /** + * Callback when the service is resolved successfully. + * @param aServiceInfo + * the info about the resolved service, where |host| and |port| are set. + */ + void onServiceResolved(in nsIDNSServiceInfo aServiceInfo); + + /** + * Callback when the service cannot be resolved. + * @param aServiceInfo + * the info about the service to be resolved. + * @param aErrorCode + * the error code. + */ + void onResolveFailed(in nsIDNSServiceInfo aServiceInfo, in long aErrorCode); +}; + +/** + * The interface for DNS service discovery/registration/resolve + */ +[scriptable, uuid(6487899b-beb1-455a-ba65-e4fd465066d7)] +interface nsIDNSServiceDiscovery : nsISupports +{ + /** + * Browse for instances of a service. + * @param aServiceType + * the service type to be discovered, E.g. "_http._tcp". + * @param aListener + * callback interface for discovery notifications. + * @return An object that can be used to cancel the service discovery. + */ + nsICancelable startDiscovery(in AUTF8String aServiceType, in nsIDNSServiceDiscoveryListener aListener); + + /** + * Register a service that is discovered via |startDiscovery| and |resolveService| calls. + * @param aServiceInfo + * the service information to be registered. + * |port| and |aServiceType| are required attributes. + * @param aListener + * callback interface for registration notifications. + * @return An object that can be used to cancel the service registration. + */ + nsICancelable registerService(in nsIDNSServiceInfo aServiceInfo, in nsIDNSRegistrationListener aListener); + + /** + * Resolve a service name discovered via |startDiscovery| to a target host name, port number. + * @param aServiceInfo + * the service information to be registered. + * |serviceName|, |aServiceType|, and |domainName| are required attributes as reported to the |onServiceFound| callback. + * @param aListener + * callback interface for registration notifications. + */ + void resolveService(in nsIDNSServiceInfo aServiceInfo, in nsIDNSServiceResolveListener aListener); +}; + +%{ C++ +#define DNSSERVICEDISCOVERY_CONTRACT_ID \ + "@mozilla.org/toolkit/components/mdnsresponder/dns-sd;1" +#define DNSSERVICEINFO_CONTRACT_ID \ + "@mozilla.org/toolkit/components/mdnsresponder/dns-info;1" +%} diff --git a/netwerk/dns/moz.build b/netwerk/dns/moz.build new file mode 100644 index 0000000000..bc5ff1e58c --- /dev/null +++ b/netwerk/dns/moz.build @@ -0,0 +1,107 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +with Files("**"): + BUG_COMPONENT = ("Core", "Networking: DNS") + +DIRS += ["mdns", "tests"] + +XPIDL_SOURCES += [ + "nsIDNSByTypeRecord.idl", + "nsIDNSListener.idl", + "nsIDNSRecord.idl", + "nsIDNSResolverInfo.idl", + "nsIDNSService.idl", + "nsIEffectiveTLDService.idl", + "nsIIDNService.idl", + "nsINativeDNSResolverOverride.idl", + "nsPIDNSService.idl", +] + +XPIDL_MODULE = "necko_dns" + +EXTRA_JS_MODULES["netwerk-dns"] += [ + "PublicSuffixList.jsm", +] + +XPCSHELL_TESTS_MANIFESTS += ["tests/unit/xpcshell.ini"] + +EXPORTS += [ + "nsEffectiveTLDService.h", +] + +EXPORTS.mozilla.net += [ + "ChildDNSService.h", + "DNS.h", + "DNSByTypeRecord.h", + "DNSListenerProxy.h", + "DNSRequestBase.h", + "DNSRequestChild.h", + "DNSRequestParent.h", + "HTTPSSVC.h", + "IDNBlocklistUtils.h", + "NativeDNSResolverOverrideChild.h", + "NativeDNSResolverOverrideParent.h", + "TRRService.h", + "TRRServiceBase.h", + "TRRServiceChild.h", + "TRRServiceParent.h", +] + +SOURCES += [ + "GetAddrInfo.cpp", # Undefines UNICODE + "nsEffectiveTLDService.cpp", # Excluded from UNIFIED_SOURCES due to special build flags. + "nsHostResolver.cpp", # Redefines LOG +] + +UNIFIED_SOURCES += [ + "ChildDNSService.cpp", + "DNS.cpp", + "DNSListenerProxy.cpp", + "DNSPacket.cpp", + "DNSRequestChild.cpp", + "DNSRequestParent.cpp", + "DNSResolverInfo.cpp", + "HTTPSSVC.cpp", + "IDNBlocklistUtils.cpp", + "NativeDNSResolverOverrideChild.cpp", + "NativeDNSResolverOverrideParent.cpp", + "nsDNSService2.cpp", + "nsIDNService.cpp", + "punycode.c", + "TRR.cpp", + "TRRQuery.cpp", + "TRRService.cpp", + "TRRServiceBase.cpp", + "TRRServiceChild.cpp", + "TRRServiceParent.cpp", +] + +IPDL_SOURCES = [ + "PDNSRequest.ipdl", + "PDNSRequestParams.ipdlh", + "PNativeDNSResolverOverride.ipdl", + "PTRRService.ipdl", +] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul" + +GeneratedFile( + "etld_data.inc", script="prepare_tlds.py", inputs=["effective_tld_names.dat"] +) + +# need to include etld_data.inc +LOCAL_INCLUDES += [ + "/netwerk/base", + "/netwerk/protocol/http", +] + +USE_LIBS += ["icu"] + +if CONFIG["CC_TYPE"] in ("clang", "gcc"): + CXXFLAGS += ["-Wno-error=shadow"] diff --git a/netwerk/dns/nsDNSService2.cpp b/netwerk/dns/nsDNSService2.cpp new file mode 100644 index 0000000000..443c226116 --- /dev/null +++ b/netwerk/dns/nsDNSService2.cpp @@ -0,0 +1,1453 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsDNSService2.h" +#include "nsIDNSRecord.h" +#include "nsIDNSListener.h" +#include "nsIDNSByTypeRecord.h" +#include "nsICancelable.h" +#include "nsIPrefBranch.h" +#include "nsIOService.h" +#include "nsIXPConnect.h" +#include "nsProxyRelease.h" +#include "nsReadableUtils.h" +#include "nsString.h" +#include "nsNetCID.h" +#include "nsError.h" +#include "nsDNSPrefetch.h" +#include "nsThreadUtils.h" +#include "nsIProtocolProxyService.h" +#include "prsystem.h" +#include "prnetdb.h" +#include "prmon.h" +#include "prio.h" +#include "plstr.h" +#include "nsCharSeparatedTokenizer.h" +#include "nsNetAddr.h" +#include "nsProxyRelease.h" +#include "nsQueryObject.h" +#include "nsIObserverService.h" +#include "nsINetworkLinkService.h" +#include "DNSResolverInfo.h" +#include "TRRService.h" + +#include "mozilla/Attributes.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/net/NeckoCommon.h" +#include "mozilla/net/ChildDNSService.h" +#include "mozilla/net/DNSListenerProxy.h" +#include "mozilla/Services.h" +#include "mozilla/StaticPrefs_network.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/SyncRunnable.h" +#include "mozilla/TextUtils.h" +#include "mozilla/Utf8.h" + +using namespace mozilla; +using namespace mozilla::net; + +static const char kPrefDnsCacheEntries[] = "network.dnsCacheEntries"; +static const char kPrefDnsCacheExpiration[] = "network.dnsCacheExpiration"; +static const char kPrefDnsCacheGrace[] = + "network.dnsCacheExpirationGracePeriod"; +static const char kPrefIPv4OnlyDomains[] = "network.dns.ipv4OnlyDomains"; +static const char kPrefDisableIPv6[] = "network.dns.disableIPv6"; +static const char kPrefDisablePrefetch[] = "network.dns.disablePrefetch"; +static const char kPrefBlockDotOnion[] = "network.dns.blockDotOnion"; +static const char kPrefDnsLocalDomains[] = "network.dns.localDomains"; +static const char kPrefDnsForceResolve[] = "network.dns.forceResolve"; +static const char kPrefDnsOfflineLocalhost[] = "network.dns.offline-localhost"; +static const char kPrefDnsNotifyResolution[] = "network.dns.notifyResolution"; +static const char kPrefNetworkProxySOCKS[] = "network.proxy.socks"; + +//----------------------------------------------------------------------------- + +class nsDNSRecord : public nsIDNSAddrRecord { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIDNSRECORD + NS_DECL_NSIDNSADDRRECORD + + explicit nsDNSRecord(nsHostRecord* hostRecord) + : mIterGenCnt(-1), mDone(false) { + mHostRecord = do_QueryObject(hostRecord); + } + + private: + virtual ~nsDNSRecord() = default; + + RefPtr<AddrHostRecord> mHostRecord; + // Since mIter is holding a weak reference to the NetAddr array we must + // make sure it is not released. So we also keep a RefPtr to the AddrInfo + // which is immutable. + RefPtr<AddrInfo> mAddrInfo; + nsTArray<NetAddr>::const_iterator mIter; + const NetAddr* iter() { + if (!mIter.GetArray()) { + return nullptr; + } + if (mIter.GetArray()->end() == mIter) { + return nullptr; + } + return &*mIter; + } + + int mIterGenCnt; // the generation count of + // mHostRecord->addr_info when we + // start iterating + bool mDone; +}; + +NS_IMPL_ISUPPORTS(nsDNSRecord, nsIDNSRecord, nsIDNSAddrRecord) + +NS_IMETHODIMP +nsDNSRecord::GetCanonicalName(nsACString& result) { + // this method should only be called if we have a CNAME + NS_ENSURE_TRUE(mHostRecord->flags & nsHostResolver::RES_CANON_NAME, + NS_ERROR_NOT_AVAILABLE); + + MutexAutoLock lock(mHostRecord->addr_info_lock); + + // if the record is for an IP address literal, then the canonical + // host name is the IP address literal. + if (!mHostRecord->addr_info) { + result = mHostRecord->host; + return NS_OK; + } + + if (mHostRecord->addr_info->CanonicalHostname().IsEmpty()) { + result = mHostRecord->addr_info->Hostname(); + } else { + result = mHostRecord->addr_info->CanonicalHostname(); + } + return NS_OK; +} + +NS_IMETHODIMP +nsDNSRecord::IsTRR(bool* retval) { + MutexAutoLock lock(mHostRecord->addr_info_lock); + if (mHostRecord->addr_info) { + *retval = mHostRecord->addr_info->IsTRR(); + } else { + *retval = false; + } + return NS_OK; +} + +NS_IMETHODIMP +nsDNSRecord::GetTrrFetchDuration(double* aTime) { + MutexAutoLock lock(mHostRecord->addr_info_lock); + if (mHostRecord->addr_info && mHostRecord->addr_info->IsTRR()) { + *aTime = mHostRecord->addr_info->GetTrrFetchDuration(); + } else { + *aTime = 0; + } + return NS_OK; +} + +NS_IMETHODIMP +nsDNSRecord::GetTrrFetchDurationNetworkOnly(double* aTime) { + MutexAutoLock lock(mHostRecord->addr_info_lock); + if (mHostRecord->addr_info && mHostRecord->addr_info->IsTRR()) { + *aTime = mHostRecord->addr_info->GetTrrFetchDurationNetworkOnly(); + } else { + *aTime = 0; + } + return NS_OK; +} + +NS_IMETHODIMP +nsDNSRecord::GetNextAddr(uint16_t port, NetAddr* addr) { + if (mDone) { + return NS_ERROR_NOT_AVAILABLE; + } + + mHostRecord->addr_info_lock.Lock(); + if (mHostRecord->addr_info) { + if (mIterGenCnt != mHostRecord->addr_info_gencnt) { + // mHostRecord->addr_info has changed, restart the iteration. + mIter = nsTArray<NetAddr>::const_iterator(); + mIterGenCnt = mHostRecord->addr_info_gencnt; + // Make sure to hold a RefPtr to the AddrInfo so we can iterate through + // the NetAddr array. + mAddrInfo = mHostRecord->addr_info; + } + + bool startedFresh = !iter(); + + do { + if (!iter()) { + mIter = mAddrInfo->Addresses().begin(); + } else { + mIter++; + } + } while (iter() && mHostRecord->Blocklisted(iter())); + + if (!iter() && startedFresh) { + // If everything was blocklisted we want to reset the blocklist (and + // likely relearn it) and return the first address. That is better + // than nothing. + mHostRecord->ResetBlocklist(); + mIter = mAddrInfo->Addresses().begin(); + } + + if (iter()) { + *addr = *mIter; + } + + mHostRecord->addr_info_lock.Unlock(); + + if (!iter()) { + mDone = true; + mIter = nsTArray<NetAddr>::const_iterator(); + mAddrInfo = nullptr; + mIterGenCnt = -1; + return NS_ERROR_NOT_AVAILABLE; + } + } else { + mHostRecord->addr_info_lock.Unlock(); + + if (!mHostRecord->addr) { + // Both mHostRecord->addr_info and mHostRecord->addr are null. + // This can happen if mHostRecord->addr_info expired and the + // attempt to reresolve it failed. + return NS_ERROR_NOT_AVAILABLE; + } + memcpy(addr, mHostRecord->addr.get(), sizeof(NetAddr)); + mDone = true; + } + + // set given port + port = htons(port); + if (addr->raw.family == AF_INET) { + addr->inet.port = port; + } else if (addr->raw.family == AF_INET6) { + addr->inet6.port = port; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsDNSRecord::GetAddresses(nsTArray<NetAddr>& aAddressArray) { + if (mDone) { + return NS_ERROR_NOT_AVAILABLE; + } + + mHostRecord->addr_info_lock.Lock(); + if (mHostRecord->addr_info) { + for (const auto& address : mHostRecord->addr_info->Addresses()) { + if (mHostRecord->Blocklisted(&address)) { + continue; + } + NetAddr* addr = aAddressArray.AppendElement(address); + if (addr->raw.family == AF_INET) { + addr->inet.port = 0; + } else if (addr->raw.family == AF_INET6) { + addr->inet6.port = 0; + } + } + mHostRecord->addr_info_lock.Unlock(); + } else { + mHostRecord->addr_info_lock.Unlock(); + + if (!mHostRecord->addr) { + return NS_ERROR_NOT_AVAILABLE; + } + NetAddr* addr = aAddressArray.AppendElement(NetAddr()); + memcpy(addr, mHostRecord->addr.get(), sizeof(NetAddr)); + if (addr->raw.family == AF_INET) { + addr->inet.port = 0; + } else if (addr->raw.family == AF_INET6) { + addr->inet6.port = 0; + } + } + return NS_OK; +} + +NS_IMETHODIMP +nsDNSRecord::GetScriptableNextAddr(uint16_t port, nsINetAddr** result) { + NetAddr addr; + nsresult rv = GetNextAddr(port, &addr); + if (NS_FAILED(rv)) { + return rv; + } + + RefPtr<nsNetAddr> netaddr = new nsNetAddr(&addr); + netaddr.forget(result); + + return NS_OK; +} + +NS_IMETHODIMP +nsDNSRecord::GetNextAddrAsString(nsACString& result) { + NetAddr addr; + nsresult rv = GetNextAddr(0, &addr); + if (NS_FAILED(rv)) { + return rv; + } + + char buf[kIPv6CStrBufSize]; + if (addr.ToStringBuffer(buf, sizeof(buf))) { + result.Assign(buf); + return NS_OK; + } + NS_ERROR("NetAddrToString failed unexpectedly"); + return NS_ERROR_FAILURE; // conversion failed for some reason +} + +NS_IMETHODIMP +nsDNSRecord::HasMore(bool* result) { + if (mDone) { + *result = false; + return NS_OK; + } + + nsTArray<NetAddr>::const_iterator iterCopy = mIter; + int iterGenCntCopy = mIterGenCnt; + + NetAddr addr; + *result = NS_SUCCEEDED(GetNextAddr(0, &addr)); + + mIter = iterCopy; + mIterGenCnt = iterGenCntCopy; + mDone = false; + + return NS_OK; +} + +NS_IMETHODIMP +nsDNSRecord::Rewind() { + mIter = nsTArray<NetAddr>::const_iterator(); + mIterGenCnt = -1; + mDone = false; + return NS_OK; +} + +NS_IMETHODIMP +nsDNSRecord::ReportUnusable(uint16_t aPort) { + // right now we don't use the port in the blocklist + + MutexAutoLock lock(mHostRecord->addr_info_lock); + + // Check that we are using a real addr_info (as opposed to a single + // constant address), and that the generation count is valid. Otherwise, + // ignore the report. + + if (mHostRecord->addr_info && mIterGenCnt == mHostRecord->addr_info_gencnt && + iter()) { + mHostRecord->ReportUnusable(iter()); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsDNSRecord::GetEffectiveTRRMode(uint32_t* aMode) { + *aMode = mHostRecord->EffectiveTRRMode(); + return NS_OK; +} + +class nsDNSByTypeRecord : public nsIDNSByTypeRecord, + public nsIDNSTXTRecord, + public nsIDNSHTTPSSVCRecord { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIDNSRECORD + NS_DECL_NSIDNSBYTYPERECORD + NS_DECL_NSIDNSTXTRECORD + NS_DECL_NSIDNSHTTPSSVCRECORD + + explicit nsDNSByTypeRecord(nsHostRecord* hostRecord) { + mHostRecord = do_QueryObject(hostRecord); + } + + private: + virtual ~nsDNSByTypeRecord() = default; + RefPtr<TypeHostRecord> mHostRecord; +}; + +NS_IMPL_ISUPPORTS(nsDNSByTypeRecord, nsIDNSRecord, nsIDNSByTypeRecord, + nsIDNSTXTRecord, nsIDNSHTTPSSVCRecord) + +NS_IMETHODIMP +nsDNSByTypeRecord::GetType(uint32_t* aType) { + *aType = mHostRecord->GetType(); + return NS_OK; +} + +NS_IMETHODIMP +nsDNSByTypeRecord::GetRecords(CopyableTArray<nsCString>& aRecords) { + // deep copy + return mHostRecord->GetRecords(aRecords); +} + +NS_IMETHODIMP +nsDNSByTypeRecord::GetRecordsAsOneString(nsACString& aRecords) { + // deep copy + return mHostRecord->GetRecordsAsOneString(aRecords); +} + +NS_IMETHODIMP +nsDNSByTypeRecord::GetRecords(nsTArray<RefPtr<nsISVCBRecord>>& aRecords) { + return mHostRecord->GetRecords(aRecords); +} + +NS_IMETHODIMP +nsDNSByTypeRecord::GetServiceModeRecord(bool aNoHttp2, bool aNoHttp3, + nsISVCBRecord** aRecord) { + return mHostRecord->GetServiceModeRecord(aNoHttp2, aNoHttp3, aRecord); +} + +NS_IMETHODIMP +nsDNSByTypeRecord::GetAllRecordsWithEchConfig( + bool aNoHttp2, bool aNoHttp3, bool* aAllRecordsHaveEchConfig, + bool* aAllRecordsInH3ExcludedList, + nsTArray<RefPtr<nsISVCBRecord>>& aResult) { + return mHostRecord->GetAllRecordsWithEchConfig( + aNoHttp2, aNoHttp3, aAllRecordsHaveEchConfig, aAllRecordsInH3ExcludedList, + aResult); +} + +NS_IMETHODIMP +nsDNSByTypeRecord::GetHasIPAddresses(bool* aResult) { + return mHostRecord->GetHasIPAddresses(aResult); +} + +NS_IMETHODIMP +nsDNSByTypeRecord::GetAllRecordsExcluded(bool* aResult) { + return mHostRecord->GetAllRecordsExcluded(aResult); +} + +NS_IMETHODIMP +nsDNSByTypeRecord::GetResults(mozilla::net::TypeRecordResultType* aResults) { + *aResults = mHostRecord->GetResults(); + return NS_OK; +} + +//----------------------------------------------------------------------------- + +class nsDNSAsyncRequest final : public nsResolveHostCallback, + public nsICancelable { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSICANCELABLE + + nsDNSAsyncRequest(nsHostResolver* res, const nsACString& host, + const nsACString& trrServer, uint16_t type, + const OriginAttributes& attrs, nsIDNSListener* listener, + uint16_t flags, uint16_t af) + : mResolver(res), + mHost(host), + mTrrServer(trrServer), + mType(type), + mOriginAttributes(attrs), + mListener(listener), + mFlags(flags), + mAF(af) {} + + void OnResolveHostComplete(nsHostResolver*, nsHostRecord*, nsresult) override; + // Returns TRUE if the DNS listener arg is the same as the member listener + // Used in Cancellations to remove DNS requests associated with a + // particular hostname and nsIDNSListener + bool EqualsAsyncListener(nsIDNSListener* aListener) override; + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const override; + + RefPtr<nsHostResolver> mResolver; + nsCString mHost; // hostname we're resolving + nsCString mTrrServer; // A trr server to be used. + uint16_t mType; + const OriginAttributes + mOriginAttributes; // The originAttributes for this resolving + nsCOMPtr<nsIDNSListener> mListener; + uint16_t mFlags; + uint16_t mAF; + + private: + virtual ~nsDNSAsyncRequest() = default; +}; + +NS_IMPL_ISUPPORTS(nsDNSAsyncRequest, nsICancelable) + +void nsDNSAsyncRequest::OnResolveHostComplete(nsHostResolver* resolver, + nsHostRecord* hostRecord, + nsresult status) { + // need to have an owning ref when we issue the callback to enable + // the caller to be able to addref/release multiple times without + // destroying the record prematurely. + nsCOMPtr<nsIDNSRecord> rec; + if (NS_SUCCEEDED(status)) { + MOZ_ASSERT(hostRecord, "no host record"); + if (hostRecord->type != nsDNSService::RESOLVE_TYPE_DEFAULT) { + rec = new nsDNSByTypeRecord(hostRecord); + } else { + rec = new nsDNSRecord(hostRecord); + } + } + + mListener->OnLookupComplete(this, rec, status); + mListener = nullptr; +} + +bool nsDNSAsyncRequest::EqualsAsyncListener(nsIDNSListener* aListener) { + uintptr_t originalListenerAddr = reinterpret_cast<uintptr_t>(mListener.get()); + RefPtr<DNSListenerProxy> wrapper = do_QueryObject(mListener); + if (wrapper) { + originalListenerAddr = wrapper->GetOriginalListenerAddress(); + } + + uintptr_t listenerAddr = reinterpret_cast<uintptr_t>(aListener); + return (listenerAddr == originalListenerAddr); +} + +size_t nsDNSAsyncRequest::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const { + size_t n = mallocSizeOf(this); + + // The following fields aren't measured. + // - mHost, because it's a non-owning pointer + // - mResolver, because it's a non-owning pointer + // - mListener, because it's a non-owning pointer + + return n; +} + +NS_IMETHODIMP +nsDNSAsyncRequest::Cancel(nsresult reason) { + NS_ENSURE_ARG(NS_FAILED(reason)); + mResolver->DetachCallback(mHost, mTrrServer, mType, mOriginAttributes, mFlags, + mAF, this, reason); + return NS_OK; +} + +//----------------------------------------------------------------------------- + +class nsDNSSyncRequest : public nsResolveHostCallback { + NS_DECL_THREADSAFE_ISUPPORTS + public: + explicit nsDNSSyncRequest(PRMonitor* mon) + : mDone(false), mStatus(NS_OK), mMonitor(mon) {} + + void OnResolveHostComplete(nsHostResolver*, nsHostRecord*, nsresult) override; + bool EqualsAsyncListener(nsIDNSListener* aListener) override; + size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const override; + + bool mDone; + nsresult mStatus; + RefPtr<nsHostRecord> mHostRecord; + + private: + virtual ~nsDNSSyncRequest() = default; + + PRMonitor* mMonitor; +}; + +NS_IMPL_ISUPPORTS0(nsDNSSyncRequest) + +void nsDNSSyncRequest::OnResolveHostComplete(nsHostResolver* resolver, + nsHostRecord* hostRecord, + nsresult status) { + // store results, and wake up nsDNSService::Resolve to process results. + PR_EnterMonitor(mMonitor); + mDone = true; + mStatus = status; + mHostRecord = hostRecord; + PR_Notify(mMonitor); + PR_ExitMonitor(mMonitor); +} + +bool nsDNSSyncRequest::EqualsAsyncListener(nsIDNSListener* aListener) { + // Sync request: no listener to compare + return false; +} + +size_t nsDNSSyncRequest::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const { + size_t n = mallocSizeOf(this); + + // The following fields aren't measured. + // - mHostRecord, because it's a non-owning pointer + + // Measurement of the following members may be added later if DMD finds it + // is worthwhile: + // - mMonitor + + return n; +} + +class NotifyDNSResolution : public Runnable { + public: + explicit NotifyDNSResolution(const nsACString& aHostname) + : mozilla::Runnable("NotifyDNSResolution"), mHostname(aHostname) {} + + NS_IMETHOD Run() override { + MOZ_ASSERT(NS_IsMainThread()); + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); + if (obs) { + obs->NotifyObservers(nullptr, "dns-resolution-request", + NS_ConvertUTF8toUTF16(mHostname).get()); + } + return NS_OK; + } + + private: + nsCString mHostname; +}; + +//----------------------------------------------------------------------------- + +nsDNSService::nsDNSService() + : mLock("nsDNSServer.mLock"), + mDisableIPv6(false), + mDisablePrefetch(false), + mBlockDotOnion(false), + mNotifyResolution(false), + mOfflineLocalhost(false), + mForceResolveOn(false), + mTrrService(nullptr), + mHasSocksProxy(false), + mResCacheEntries(0), + mResCacheExpiration(0), + mResCacheGrace(0), + mResolverPrefsUpdated(false) {} + +nsDNSService::~nsDNSService() = default; + +NS_IMPL_ISUPPORTS(nsDNSService, nsIDNSService, nsPIDNSService, nsIObserver, + nsIMemoryReporter) + +/****************************************************************************** + * nsDNSService impl: + * singleton instance ctor/dtor methods + ******************************************************************************/ +static StaticRefPtr<nsDNSService> gDNSService; + +already_AddRefed<nsIDNSService> nsDNSService::GetXPCOMSingleton() { + if (nsIOService::UseSocketProcess()) { + if (XRE_IsSocketProcess()) { + return GetSingleton(); + } + + if (XRE_IsContentProcess() || XRE_IsParentProcess()) { + return ChildDNSService::GetSingleton(); + } + + return nullptr; + } + + if (XRE_IsParentProcess()) { + return GetSingleton(); + } + + if (XRE_IsContentProcess() || XRE_IsSocketProcess()) { + return ChildDNSService::GetSingleton(); + } + + return nullptr; +} + +already_AddRefed<nsDNSService> nsDNSService::GetSingleton() { + MOZ_ASSERT_IF(nsIOService::UseSocketProcess(), XRE_IsSocketProcess()); + MOZ_ASSERT_IF(!nsIOService::UseSocketProcess(), XRE_IsParentProcess()); + + if (!gDNSService) { + if (!NS_IsMainThread()) { + return nullptr; + } + gDNSService = new nsDNSService(); + if (NS_SUCCEEDED(gDNSService->Init())) { + ClearOnShutdown(&gDNSService); + } else { + gDNSService = nullptr; + } + } + + return do_AddRef(gDNSService); +} + +nsresult nsDNSService::ReadPrefs(const char* name) { + bool tmpbool; + uint32_t tmpint; + mResolverPrefsUpdated = false; + + // resolver-specific prefs first + if (!name || !strcmp(name, kPrefDnsCacheEntries)) { + if (NS_SUCCEEDED(Preferences::GetUint(kPrefDnsCacheEntries, &tmpint))) { + if (!name || (tmpint != mResCacheEntries)) { + mResCacheEntries = tmpint; + mResolverPrefsUpdated = true; + } + } + } + if (!name || !strcmp(name, kPrefDnsCacheExpiration)) { + if (NS_SUCCEEDED(Preferences::GetUint(kPrefDnsCacheExpiration, &tmpint))) { + if (!name || (tmpint != mResCacheExpiration)) { + mResCacheExpiration = tmpint; + mResolverPrefsUpdated = true; + } + } + } + if (!name || !strcmp(name, kPrefDnsCacheGrace)) { + if (NS_SUCCEEDED(Preferences::GetUint(kPrefDnsCacheGrace, &tmpint))) { + if (!name || (tmpint != mResCacheGrace)) { + mResCacheGrace = tmpint; + mResolverPrefsUpdated = true; + } + } + } + + // DNSservice prefs + if (!name || !strcmp(name, kPrefDisableIPv6)) { + if (NS_SUCCEEDED(Preferences::GetBool(kPrefDisableIPv6, &tmpbool))) { + mDisableIPv6 = tmpbool; + } + } + if (!name || !strcmp(name, kPrefDnsOfflineLocalhost)) { + if (NS_SUCCEEDED( + Preferences::GetBool(kPrefDnsOfflineLocalhost, &tmpbool))) { + mOfflineLocalhost = tmpbool; + } + } + if (!name || !strcmp(name, kPrefDisablePrefetch)) { + if (NS_SUCCEEDED(Preferences::GetBool(kPrefDisablePrefetch, &tmpbool))) { + mDisablePrefetch = tmpbool; + } + } + if (!name || !strcmp(name, kPrefBlockDotOnion)) { + if (NS_SUCCEEDED(Preferences::GetBool(kPrefBlockDotOnion, &tmpbool))) { + mBlockDotOnion = tmpbool; + } + } + if (!name || !strcmp(name, kPrefDnsNotifyResolution)) { + if (NS_SUCCEEDED( + Preferences::GetBool(kPrefDnsNotifyResolution, &tmpbool))) { + mNotifyResolution = tmpbool; + } + } + if (!name || !strcmp(name, kPrefNetworkProxySOCKS)) { + nsAutoCString socks; + if (NS_SUCCEEDED(Preferences::GetCString(kPrefNetworkProxySOCKS, socks))) { + mHasSocksProxy = !socks.IsEmpty(); + } + } + if (!name || !strcmp(name, kPrefIPv4OnlyDomains)) { + Preferences::GetCString(kPrefIPv4OnlyDomains, mIPv4OnlyDomains); + } + if (!name || !strcmp(name, kPrefDnsLocalDomains)) { + nsCString localDomains; + Preferences::GetCString(kPrefDnsLocalDomains, localDomains); + MutexAutoLock lock(mLock); + mLocalDomains.Clear(); + for (const auto& token : + nsCCharSeparatedTokenizerTemplate<NS_IsAsciiWhitespace, + nsTokenizerFlags::SeparatorOptional>( + localDomains, ',') + .ToRange()) { + mLocalDomains.PutEntry(token); + } + } + if (!name || !strcmp(name, kPrefDnsForceResolve)) { + Preferences::GetCString(kPrefDnsForceResolve, mForceResolve); + mForceResolveOn = !mForceResolve.IsEmpty(); + } + + if (StaticPrefs::network_proxy_type() == + nsIProtocolProxyService::PROXYCONFIG_MANUAL) { + // Disable prefetching either by explicit preference or if a + // manual proxy is configured + mDisablePrefetch = true; + } + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::Init() { + MOZ_ASSERT(!mResolver); + MOZ_ASSERT(NS_IsMainThread()); + + ReadPrefs(nullptr); + + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + if (observerService) { + observerService->AddObserver(this, "last-pb-context-exited", false); + observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, false); + observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); + } + + RefPtr<nsHostResolver> res; + nsresult rv = nsHostResolver::Create(mResCacheEntries, mResCacheExpiration, + mResCacheGrace, getter_AddRefs(res)); + if (NS_SUCCEEDED(rv)) { + // now, set all of our member variables while holding the lock + MutexAutoLock lock(mLock); + mResolver = res; + } + + nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); + if (prefs) { + // register as prefs observer + prefs->AddObserver(kPrefDnsCacheEntries, this, false); + prefs->AddObserver(kPrefDnsCacheExpiration, this, false); + prefs->AddObserver(kPrefDnsCacheGrace, this, false); + prefs->AddObserver(kPrefIPv4OnlyDomains, this, false); + prefs->AddObserver(kPrefDnsLocalDomains, this, false); + prefs->AddObserver(kPrefDnsForceResolve, this, false); + prefs->AddObserver(kPrefDisableIPv6, this, false); + prefs->AddObserver(kPrefDnsOfflineLocalhost, this, false); + prefs->AddObserver(kPrefDisablePrefetch, this, false); + prefs->AddObserver(kPrefBlockDotOnion, this, false); + prefs->AddObserver(kPrefDnsNotifyResolution, this, false); + + // Monitor these to see if there is a change in proxy configuration + prefs->AddObserver(kPrefNetworkProxySOCKS, this, false); + } + + nsDNSPrefetch::Initialize(this); + + RegisterWeakMemoryReporter(this); + + mTrrService = new TRRService(); + if (NS_FAILED(mTrrService->Init())) { + mTrrService = nullptr; + } + + nsCOMPtr<nsIIDNService> idn = do_GetService(NS_IDNSERVICE_CONTRACTID); + mIDN = idn; + + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::Shutdown() { + UnregisterWeakMemoryReporter(this); + + RefPtr<nsHostResolver> res; + { + MutexAutoLock lock(mLock); + res = mResolver; + mResolver = nullptr; + } + if (res) { + res->Shutdown(); + } + + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + if (observerService) { + observerService->RemoveObserver(this, NS_NETWORK_LINK_TOPIC); + observerService->RemoveObserver(this, "last-pb-context-exited"); + observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID); + } + + return NS_OK; +} + +bool nsDNSService::GetOffline() const { + bool offline = false; + nsCOMPtr<nsIIOService> io = do_GetService(NS_IOSERVICE_CONTRACTID); + if (io) { + io->GetOffline(&offline); + } + return offline; +} + +NS_IMETHODIMP +nsDNSService::GetPrefetchEnabled(bool* outVal) { + MutexAutoLock lock(mLock); + *outVal = !mDisablePrefetch; + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::SetPrefetchEnabled(bool inVal) { + MutexAutoLock lock(mLock); + mDisablePrefetch = !inVal; + return NS_OK; +} + +bool nsDNSService::DNSForbiddenByActiveProxy(const nsACString& aHostname, + uint32_t flags) { + if (flags & nsIDNSService::RESOLVE_IGNORE_SOCKS_DNS) { + return false; + } + + // We should avoid doing DNS when a proxy is in use. + PRNetAddr tempAddr; + if (StaticPrefs::network_proxy_type() == + nsIProtocolProxyService::PROXYCONFIG_MANUAL && + mHasSocksProxy && StaticPrefs::network_proxy_socks_remote_dns()) { + // Allow IP lookups through, but nothing else. + if (PR_StringToNetAddr(nsCString(aHostname).get(), &tempAddr) != + PR_SUCCESS) { + return true; + } + } + return false; +} + +nsresult nsDNSService::PreprocessHostname(bool aLocalDomain, + const nsACString& aInput, + nsIIDNService* aIDN, + nsACString& aACE) { + // Enforce RFC 7686 + if (mBlockDotOnion && StringEndsWith(aInput, ".onion"_ns)) { + return NS_ERROR_UNKNOWN_HOST; + } + + if (aLocalDomain) { + aACE.AssignLiteral("localhost"); + return NS_OK; + } + + if (mTrrService && mTrrService->MaybeBootstrap(aInput, aACE)) { + return NS_OK; + } + + if (mForceResolveOn) { + MutexAutoLock lock(mLock); + if (!aInput.LowerCaseEqualsASCII("localhost") && + !aInput.LowerCaseEqualsASCII("127.0.0.1")) { + aACE.Assign(mForceResolve); + return NS_OK; + } + } + + if (!aIDN || IsAscii(aInput)) { + aACE = aInput; + return NS_OK; + } + + if (!(IsUtf8(aInput) && NS_SUCCEEDED(aIDN->ConvertUTF8toACE(aInput, aACE)))) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +nsresult nsDNSService::AsyncResolveInternal( + const nsACString& aHostname, uint16_t type, uint32_t flags, + nsIDNSResolverInfo* aResolver, nsIDNSListener* aListener, + nsIEventTarget* target_, const OriginAttributes& aOriginAttributes, + nsICancelable** result) { + // grab reference to global host resolver and IDN service. beware + // simultaneous shutdown!! + RefPtr<nsHostResolver> res; + nsCOMPtr<nsIIDNService> idn; + nsCOMPtr<nsIEventTarget> target = target_; + nsCOMPtr<nsIDNSListener> listener = aListener; + bool localDomain = false; + { + MutexAutoLock lock(mLock); + + if (mDisablePrefetch && (flags & RESOLVE_SPECULATE)) { + return NS_ERROR_DNS_LOOKUP_QUEUE_FULL; + } + + res = mResolver; + idn = mIDN; + localDomain = mLocalDomains.GetEntry(aHostname); + } + + if (mNotifyResolution) { + NS_DispatchToMainThread(new NotifyDNSResolution(aHostname)); + } + + if (!res) { + return NS_ERROR_OFFLINE; + } + + if ((type != RESOLVE_TYPE_DEFAULT) && (type != RESOLVE_TYPE_TXT) && + (type != RESOLVE_TYPE_HTTPSSVC)) { + return NS_ERROR_INVALID_ARG; + } + + if (DNSForbiddenByActiveProxy(aHostname, flags)) { + // nsHostResolver returns NS_ERROR_UNKNOWN_HOST for lots of reasons. + // We use a different error code to differentiate this failure and to make + // it clear(er) where this error comes from. + return NS_ERROR_UNKNOWN_PROXY_HOST; + } + + nsCString hostname; + nsresult rv = PreprocessHostname(localDomain, aHostname, idn, hostname); + if (NS_FAILED(rv)) { + return rv; + } + + if (GetOffline() && + (!mOfflineLocalhost || !hostname.LowerCaseEqualsASCII("localhost"))) { + flags |= RESOLVE_OFFLINE; + } + + // make sure JS callers get notification on the main thread + nsCOMPtr<nsIXPConnectWrappedJS> wrappedListener = do_QueryInterface(listener); + if (wrappedListener && !target) { + target = GetMainThreadEventTarget(); + } + + if (target) { + listener = new DNSListenerProxy(listener, target); + } + + uint16_t af = + (type != RESOLVE_TYPE_DEFAULT) ? 0 : GetAFForLookup(hostname, flags); + + MOZ_ASSERT(listener); + RefPtr<nsDNSAsyncRequest> req = + new nsDNSAsyncRequest(res, hostname, DNSResolverInfo::URL(aResolver), + type, aOriginAttributes, listener, flags, af); + if (!req) { + return NS_ERROR_OUT_OF_MEMORY; + } + + rv = res->ResolveHost(req->mHost, DNSResolverInfo::URL(aResolver), type, + req->mOriginAttributes, flags, af, req); + req.forget(result); + return rv; +} + +nsresult nsDNSService::CancelAsyncResolveInternal( + const nsACString& aHostname, uint16_t aType, uint32_t aFlags, + nsIDNSResolverInfo* aResolver, nsIDNSListener* aListener, nsresult aReason, + const OriginAttributes& aOriginAttributes) { + // grab reference to global host resolver and IDN service. beware + // simultaneous shutdown!! + RefPtr<nsHostResolver> res; + nsCOMPtr<nsIIDNService> idn; + bool localDomain = false; + { + MutexAutoLock lock(mLock); + + if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE)) { + return NS_ERROR_DNS_LOOKUP_QUEUE_FULL; + } + + res = mResolver; + idn = mIDN; + localDomain = mLocalDomains.GetEntry(aHostname); + } + if (!res) { + return NS_ERROR_OFFLINE; + } + + nsCString hostname; + nsresult rv = PreprocessHostname(localDomain, aHostname, idn, hostname); + if (NS_FAILED(rv)) { + return rv; + } + + uint16_t af = + (aType != RESOLVE_TYPE_DEFAULT) ? 0 : GetAFForLookup(hostname, aFlags); + + res->CancelAsyncRequest(hostname, DNSResolverInfo::URL(aResolver), aType, + aOriginAttributes, aFlags, af, aListener, aReason); + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::AsyncResolve(const nsACString& aHostname, + nsIDNSService::ResolveType aType, uint32_t flags, + nsIDNSResolverInfo* aResolver, + nsIDNSListener* listener, nsIEventTarget* target_, + JS::HandleValue aOriginAttributes, JSContext* aCx, + uint8_t aArgc, nsICancelable** result) { + OriginAttributes attrs; + + if (aArgc == 1) { + if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) { + return NS_ERROR_INVALID_ARG; + } + } + + return AsyncResolveInternal(aHostname, aType, flags, aResolver, listener, + target_, attrs, result); +} + +NS_IMETHODIMP +nsDNSService::AsyncResolveNative(const nsACString& aHostname, + nsIDNSService::ResolveType aType, + uint32_t flags, nsIDNSResolverInfo* aResolver, + nsIDNSListener* aListener, + nsIEventTarget* target_, + const OriginAttributes& aOriginAttributes, + nsICancelable** result) { + return AsyncResolveInternal(aHostname, aType, flags, aResolver, aListener, + target_, aOriginAttributes, result); +} + +NS_IMETHODIMP +nsDNSService::NewTRRResolverInfo(const nsACString& aTrrURL, + nsIDNSResolverInfo** aResolver) { + RefPtr<DNSResolverInfo> res = new DNSResolverInfo(aTrrURL); + res.forget(aResolver); + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::CancelAsyncResolve(const nsACString& aHostname, + nsIDNSService::ResolveType aType, + uint32_t aFlags, nsIDNSResolverInfo* aResolver, + nsIDNSListener* aListener, nsresult aReason, + JS::HandleValue aOriginAttributes, + JSContext* aCx, uint8_t aArgc) { + OriginAttributes attrs; + + if (aArgc == 1) { + if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) { + return NS_ERROR_INVALID_ARG; + } + } + + return CancelAsyncResolveInternal(aHostname, aType, aFlags, aResolver, + aListener, aReason, attrs); +} + +NS_IMETHODIMP +nsDNSService::CancelAsyncResolveNative( + const nsACString& aHostname, nsIDNSService::ResolveType aType, + uint32_t aFlags, nsIDNSResolverInfo* aResolver, nsIDNSListener* aListener, + nsresult aReason, const OriginAttributes& aOriginAttributes) { + return CancelAsyncResolveInternal(aHostname, aType, aFlags, aResolver, + aListener, aReason, aOriginAttributes); +} + +NS_IMETHODIMP +nsDNSService::Resolve(const nsACString& aHostname, uint32_t flags, + JS::HandleValue aOriginAttributes, JSContext* aCx, + uint8_t aArgc, nsIDNSRecord** result) { + OriginAttributes attrs; + + if (aArgc == 1) { + if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) { + return NS_ERROR_INVALID_ARG; + } + } + + return ResolveNative(aHostname, flags, attrs, result); +} + +NS_IMETHODIMP +nsDNSService::ResolveNative(const nsACString& aHostname, uint32_t flags, + const OriginAttributes& aOriginAttributes, + nsIDNSRecord** result) { + // Synchronous resolution is not available on the main thread. + if (NS_IsMainThread()) { + return NS_ERROR_NOT_AVAILABLE; + } + + return ResolveInternal(aHostname, flags, aOriginAttributes, result); +} + +nsresult nsDNSService::DeprecatedSyncResolve( + const nsACString& aHostname, uint32_t flags, + const OriginAttributes& aOriginAttributes, nsIDNSRecord** result) { + return ResolveInternal(aHostname, flags, aOriginAttributes, result); +} + +nsresult nsDNSService::ResolveInternal( + const nsACString& aHostname, uint32_t flags, + const OriginAttributes& aOriginAttributes, nsIDNSRecord** result) { + // grab reference to global host resolver and IDN service. beware + // simultaneous shutdown!! + RefPtr<nsHostResolver> res; + nsCOMPtr<nsIIDNService> idn; + bool localDomain = false; + { + MutexAutoLock lock(mLock); + res = mResolver; + idn = mIDN; + localDomain = mLocalDomains.GetEntry(aHostname); + } + + if (mNotifyResolution) { + NS_DispatchToMainThread(new NotifyDNSResolution(aHostname)); + } + + NS_ENSURE_TRUE(res, NS_ERROR_OFFLINE); + + nsCString hostname; + nsresult rv = PreprocessHostname(localDomain, aHostname, idn, hostname); + if (NS_FAILED(rv)) { + return rv; + } + + if (GetOffline() && + (!mOfflineLocalhost || !hostname.LowerCaseEqualsASCII("localhost"))) { + flags |= RESOLVE_OFFLINE; + } + + if (DNSForbiddenByActiveProxy(aHostname, flags)) { + return NS_ERROR_UNKNOWN_PROXY_HOST; + } + + // + // sync resolve: since the host resolver only works asynchronously, we need + // to use a mutex and a condvar to wait for the result. however, since the + // result may be in the resolvers cache, we might get called back recursively + // on the same thread. so, our mutex needs to be re-entrant. in other words, + // we need to use a monitor! ;-) + // + + PRMonitor* mon = PR_NewMonitor(); + if (!mon) { + return NS_ERROR_OUT_OF_MEMORY; + } + + PR_EnterMonitor(mon); + RefPtr<nsDNSSyncRequest> syncReq = new nsDNSSyncRequest(mon); + + uint16_t af = GetAFForLookup(hostname, flags); + + // TRR uses the main thread for the HTTPS channel to the DoH server. + // If this were to block the main thread while waiting for TRR it would + // likely cause a deadlock. Instead we intentionally choose to not use TRR + // for this. + if (NS_IsMainThread()) { + flags |= RESOLVE_DISABLE_TRR; + } + + rv = res->ResolveHost(hostname, ""_ns, RESOLVE_TYPE_DEFAULT, + aOriginAttributes, flags, af, syncReq); + if (NS_SUCCEEDED(rv)) { + // wait for result + while (!syncReq->mDone) { + PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT); + } + + if (NS_FAILED(syncReq->mStatus)) { + rv = syncReq->mStatus; + } else { + NS_ASSERTION(syncReq->mHostRecord, "no host record"); + RefPtr<nsDNSRecord> rec = new nsDNSRecord(syncReq->mHostRecord); + rec.forget(result); + } + } + + PR_ExitMonitor(mon); + PR_DestroyMonitor(mon); + return rv; +} + +NS_IMETHODIMP +nsDNSService::GetMyHostName(nsACString& result) { + char name[100]; + if (PR_GetSystemInfo(PR_SI_HOSTNAME, name, sizeof(name)) == PR_SUCCESS) { + result = name; + return NS_OK; + } + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsDNSService::Observe(nsISupports* subject, const char* topic, + const char16_t* data) { + bool flushCache = false; + if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) { + nsAutoCString converted = NS_ConvertUTF16toUTF8(data); + if (mResolver && !strcmp(converted.get(), NS_NETWORK_LINK_DATA_CHANGED)) { + flushCache = true; + } + } else if (!strcmp(topic, "last-pb-context-exited")) { + flushCache = true; + } else if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { + ReadPrefs(NS_ConvertUTF16toUTF8(data).get()); + NS_ENSURE_TRUE(mResolver, NS_ERROR_NOT_INITIALIZED); + if (mResolverPrefsUpdated && mResolver) { + mResolver->SetCacheLimits(mResCacheEntries, mResCacheExpiration, + mResCacheGrace); + } + } else if (!strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { + Shutdown(); + } + + if (flushCache && mResolver) { + mResolver->FlushCache(false); + return NS_OK; + } + + return NS_OK; +} + +uint16_t nsDNSService::GetAFForLookup(const nsACString& host, uint32_t flags) { + if (mDisableIPv6 || (flags & RESOLVE_DISABLE_IPV6)) { + return PR_AF_INET; + } + + MutexAutoLock lock(mLock); + + uint16_t af = PR_AF_UNSPEC; + + if (!mIPv4OnlyDomains.IsEmpty()) { + const char *domain, *domainEnd, *end; + uint32_t hostLen, domainLen; + + // see if host is in one of the IPv4-only domains + domain = mIPv4OnlyDomains.BeginReading(); + domainEnd = mIPv4OnlyDomains.EndReading(); + + nsACString::const_iterator hostStart; + host.BeginReading(hostStart); + hostLen = host.Length(); + + do { + // skip any whitespace + while (*domain == ' ' || *domain == '\t') { + ++domain; + } + + // find end of this domain in the string + end = strchr(domain, ','); + if (!end) { + end = domainEnd; + } + + // to see if the hostname is in the domain, check if the domain + // matches the end of the hostname. + domainLen = end - domain; + if (domainLen && hostLen >= domainLen) { + const char* hostTail = hostStart.get() + hostLen - domainLen; + if (PL_strncasecmp(domain, hostTail, domainLen) == 0) { + // now, make sure either that the hostname is a direct match or + // that the hostname begins with a dot. + if (hostLen == domainLen || *hostTail == '.' || + *(hostTail - 1) == '.') { + af = PR_AF_INET; + break; + } + } + } + + domain = end + 1; + } while (*end); + } + + if ((af != PR_AF_INET) && (flags & RESOLVE_DISABLE_IPV4)) { + af = PR_AF_INET6; + } + + return af; +} + +NS_IMETHODIMP +nsDNSService::GetDNSCacheEntries( + nsTArray<mozilla::net::DNSCacheEntries>* args) { + NS_ENSURE_TRUE(mResolver, NS_ERROR_NOT_INITIALIZED); + mResolver->GetDNSCacheEntries(args); + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::ClearCache(bool aTrrToo) { + NS_ENSURE_TRUE(mResolver, NS_ERROR_NOT_INITIALIZED); + mResolver->FlushCache(aTrrToo); + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::ReloadParentalControlEnabled() { + if (mTrrService) { + mTrrService->mParentalControlEnabled = + TRRService::GetParentalControlEnabledInternal(); + } + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::SetDetectedTrrURI(const nsACString& aURI) { + if (mTrrService) { + mTrrService->SetDetectedTrrURI(aURI); + } + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::GetCurrentTrrURI(nsACString& aURI) { + if (mTrrService) { + return mTrrService->GetURI(aURI); + } + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::GetCurrentTrrMode(uint32_t* aMode) { + *aMode = 0; // The default mode. + if (mTrrService) { + *aMode = mTrrService->Mode(); + } + return NS_OK; +} + +size_t nsDNSService::SizeOfIncludingThis( + mozilla::MallocSizeOf mallocSizeOf) const { + // Measurement of the following members may be added later if DMD finds it + // is worthwhile: + // - mIDN + // - mLock + + size_t n = mallocSizeOf(this); + n += mResolver ? mResolver->SizeOfIncludingThis(mallocSizeOf) : 0; + n += mIPv4OnlyDomains.SizeOfExcludingThisIfUnshared(mallocSizeOf); + n += mLocalDomains.SizeOfExcludingThis(mallocSizeOf); + n += mFailedSVCDomainNames.ShallowSizeOfExcludingThis(mallocSizeOf); + for (auto iter = mFailedSVCDomainNames.ConstIter(); !iter.Done(); + iter.Next()) { + n += iter.UserData()->ShallowSizeOfExcludingThis(mallocSizeOf); + for (const auto& name : *iter.UserData()) { + n += name.SizeOfExcludingThisIfUnshared(mallocSizeOf); + } + } + return n; +} + +MOZ_DEFINE_MALLOC_SIZE_OF(DNSServiceMallocSizeOf) + +NS_IMETHODIMP +nsDNSService::CollectReports(nsIHandleReportCallback* aHandleReport, + nsISupports* aData, bool aAnonymize) { + MOZ_COLLECT_REPORT("explicit/network/dns-service", KIND_HEAP, UNITS_BYTES, + SizeOfIncludingThis(DNSServiceMallocSizeOf), + "Memory used for the DNS service."); + + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::ReportFailedSVCDomainName(const nsACString& aOwnerName, + const nsACString& aSVCDomainName) { + MutexAutoLock lock(mLock); + + nsTArray<nsCString>* failedList = mFailedSVCDomainNames.Get(aOwnerName); + if (!failedList) { + failedList = new nsTArray<nsCString>(1); + mFailedSVCDomainNames.Put(aOwnerName, failedList); + } + + failedList->AppendElement(aSVCDomainName); + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::IsSVCDomainNameFailed(const nsACString& aOwnerName, + const nsACString& aSVCDomainName, + bool* aResult) { + NS_ENSURE_ARG(aResult); + + MutexAutoLock lock(mLock); + *aResult = false; + nsTArray<nsCString>* failedList = mFailedSVCDomainNames.Get(aOwnerName); + if (!failedList) { + return NS_OK; + } + + *aResult = failedList->Contains(aSVCDomainName); + return NS_OK; +} + +NS_IMETHODIMP +nsDNSService::ResetExcludedSVCDomainName(const nsACString& aOwnerName) { + MutexAutoLock lock(mLock); + mFailedSVCDomainNames.Remove(aOwnerName); + return NS_OK; +} diff --git a/netwerk/dns/nsDNSService2.h b/netwerk/dns/nsDNSService2.h new file mode 100644 index 0000000000..06e901ddbb --- /dev/null +++ b/netwerk/dns/nsDNSService2.h @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsDNSService2_h__ +#define nsDNSService2_h__ + +#include "nsClassHashtable.h" +#include "nsPIDNSService.h" +#include "nsIIDNService.h" +#include "nsIMemoryReporter.h" +#include "nsIObserver.h" +#include "nsHostResolver.h" +#include "nsString.h" +#include "nsTHashtable.h" +#include "nsHashKeys.h" +#include "mozilla/Mutex.h" +#include "mozilla/Attributes.h" +#include "TRRService.h" + +class nsAuthSSPI; + +class nsDNSService final : public nsPIDNSService, + public nsIObserver, + public nsIMemoryReporter { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSPIDNSSERVICE + NS_DECL_NSIDNSSERVICE + NS_DECL_NSIOBSERVER + NS_DECL_NSIMEMORYREPORTER + + nsDNSService(); + + static already_AddRefed<nsIDNSService> GetXPCOMSingleton(); + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; + + bool GetOffline() const; + + protected: + friend class nsAuthSSPI; + + nsresult DeprecatedSyncResolve( + const nsACString& aHostname, uint32_t flags, + const mozilla::OriginAttributes& aOriginAttributes, + nsIDNSRecord** result); + + private: + ~nsDNSService(); + + nsresult ReadPrefs(const char* name); + static already_AddRefed<nsDNSService> GetSingleton(); + + uint16_t GetAFForLookup(const nsACString& host, uint32_t flags); + + nsresult PreprocessHostname(bool aLocalDomain, const nsACString& aInput, + nsIIDNService* aIDN, nsACString& aACE); + + nsresult AsyncResolveInternal( + const nsACString& aHostname, uint16_t type, uint32_t flags, + nsIDNSResolverInfo* aResolver, nsIDNSListener* aListener, + nsIEventTarget* target_, + const mozilla::OriginAttributes& aOriginAttributes, + nsICancelable** result); + + nsresult CancelAsyncResolveInternal( + const nsACString& aHostname, uint16_t aType, uint32_t aFlags, + nsIDNSResolverInfo* aResolver, nsIDNSListener* aListener, + nsresult aReason, const mozilla::OriginAttributes& aOriginAttributes); + + nsresult ResolveInternal(const nsACString& aHostname, uint32_t flags, + const mozilla::OriginAttributes& aOriginAttributes, + nsIDNSRecord** result); + + bool DNSForbiddenByActiveProxy(const nsACString& aHostname, uint32_t flags); + + RefPtr<nsHostResolver> mResolver; + nsCOMPtr<nsIIDNService> mIDN; + + // mLock protects access to mResolver, mLocalDomains, mIPv4OnlyDomains and + // mFailedSVCDomainNames + mozilla::Mutex mLock; + + // mIPv4OnlyDomains is a comma-separated list of domains for which only + // IPv4 DNS lookups are performed. This allows the user to disable IPv6 on + // a per-domain basis and work around broken DNS servers. See bug 68796. + nsCString mIPv4OnlyDomains; + nsCString mForceResolve; + bool mDisableIPv6; + bool mDisablePrefetch; + bool mBlockDotOnion; + bool mNotifyResolution; + bool mOfflineLocalhost; + bool mForceResolveOn; + nsTHashtable<nsCStringHashKey> mLocalDomains; + RefPtr<mozilla::net::TRRService> mTrrService; + mozilla::Atomic<bool, mozilla::Relaxed> mHasSocksProxy; + + uint32_t mResCacheEntries; + uint32_t mResCacheExpiration; + uint32_t mResCacheGrace; + bool mResolverPrefsUpdated; + nsClassHashtable<nsCStringHashKey, nsTArray<nsCString>> mFailedSVCDomainNames; +}; + +#endif // nsDNSService2_h__ diff --git a/netwerk/dns/nsEffectiveTLDService.cpp b/netwerk/dns/nsEffectiveTLDService.cpp new file mode 100644 index 0000000000..3a2270a212 --- /dev/null +++ b/netwerk/dns/nsEffectiveTLDService.cpp @@ -0,0 +1,492 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// This service reads a file of rules describing TLD-like domain names. For a +// complete description of the expected file format and parsing rules, see +// http://wiki.mozilla.org/Gecko:Effective_TLD_Service + +#include "mozilla/ArrayUtils.h" +#include "mozilla/HashFunctions.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/ResultExtensions.h" +#include "mozilla/TextUtils.h" + +#include "MainThreadUtils.h" +#include "nsCRT.h" +#include "nsEffectiveTLDService.h" +#include "nsIFile.h" +#include "nsIIDNService.h" +#include "nsIObserverService.h" +#include "nsIURI.h" +#include "nsNetCID.h" +#include "nsNetUtil.h" +#include "nsServiceManagerUtils.h" +#include "prnetdb.h" + +namespace etld_dafsa { + +// Generated file that includes kDafsa +#include "etld_data.inc" + +} // namespace etld_dafsa + +using namespace mozilla; + +NS_IMPL_ISUPPORTS(nsEffectiveTLDService, nsIEffectiveTLDService, + nsIMemoryReporter, nsIObserver) + +// ---------------------------------------------------------------------- + +static nsEffectiveTLDService* gService = nullptr; + +nsEffectiveTLDService::nsEffectiveTLDService() + : mIDNService(), mGraphLock("nsEffectiveTLDService::mGraph") { + mGraph.emplace(etld_dafsa::kDafsa); +} + +nsresult nsEffectiveTLDService::Init() { + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + obs->AddObserver(this, "public-suffix-list-updated", false); + + if (gService) { + return NS_ERROR_ALREADY_INITIALIZED; + } + + nsresult rv; + mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv); + if (NS_FAILED(rv)) { + return rv; + } + + gService = this; + RegisterWeakMemoryReporter(this); + + return NS_OK; +} + +NS_IMETHODIMP nsEffectiveTLDService::Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) { + /** + * Signal sent from netwerk/dns/PublicSuffixList.jsm + * aSubject is the nsIFile object for dafsa.bin + * aData is the absolute path to the dafsa.bin file (not used) + */ + if (aSubject && (nsCRT::strcmp(aTopic, "public-suffix-list-updated") == 0)) { + nsCOMPtr<nsIFile> mDafsaBinFile(do_QueryInterface(aSubject)); + NS_ENSURE_TRUE(mDafsaBinFile, NS_ERROR_ILLEGAL_VALUE); + + AutoWriteLock lock(mGraphLock); + // Reset mGraph with kDafsa in case reassigning to mDafsaMap fails + mGraph.reset(); + mGraph.emplace(etld_dafsa::kDafsa); + + mDafsaMap.reset(); + mMruTable.Clear(); + + MOZ_TRY(mDafsaMap.init(mDafsaBinFile)); + + size_t size = mDafsaMap.size(); + const uint8_t* remoteDafsaPtr = mDafsaMap.get<uint8_t>().get(); + + auto remoteDafsa = mozilla::Span(remoteDafsaPtr, size); + + mGraph.reset(); + mGraph.emplace(remoteDafsa); + } + return NS_OK; +} + +nsEffectiveTLDService::~nsEffectiveTLDService() { + UnregisterWeakMemoryReporter(this); + if (mIDNService) { + // Only clear gService if Init() finished successfully. + gService = nullptr; + } +} + +// static +nsEffectiveTLDService* nsEffectiveTLDService::GetInstance() { + if (gService) { + return gService; + } + nsCOMPtr<nsIEffectiveTLDService> tldService = + do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID); + if (!tldService) { + return nullptr; + } + MOZ_ASSERT( + gService, + "gService must have been initialized in nsEffectiveTLDService::Init"); + return gService; +} + +MOZ_DEFINE_MALLOC_SIZE_OF(EffectiveTLDServiceMallocSizeOf) + +// The amount of heap memory measured here is tiny. It used to be bigger when +// nsEffectiveTLDService used a separate hash table instead of binary search. +// Nonetheless, we keep this code here in anticipation of bug 1083971 which will +// change ETLDEntries::entries to a heap-allocated array modifiable at runtime. +NS_IMETHODIMP +nsEffectiveTLDService::CollectReports(nsIHandleReportCallback* aHandleReport, + nsISupports* aData, bool aAnonymize) { + MOZ_COLLECT_REPORT("explicit/network/effective-TLD-service", KIND_HEAP, + UNITS_BYTES, + SizeOfIncludingThis(EffectiveTLDServiceMallocSizeOf), + "Memory used by the effective TLD service."); + + return NS_OK; +} + +size_t nsEffectiveTLDService::SizeOfIncludingThis( + mozilla::MallocSizeOf aMallocSizeOf) { + size_t n = aMallocSizeOf(this); + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - mIDNService + + return n; +} + +// External function for dealing with URI's correctly. +// Pulls out the host portion from an nsIURI, and calls through to +// GetPublicSuffixFromHost(). +NS_IMETHODIMP +nsEffectiveTLDService::GetPublicSuffix(nsIURI* aURI, + nsACString& aPublicSuffix) { + NS_ENSURE_ARG_POINTER(aURI); + + nsAutoCString host; + nsresult rv = NS_GetInnermostURIHost(aURI, host); + if (NS_FAILED(rv)) { + return rv; + } + + return GetBaseDomainInternal(host, 0, false, aPublicSuffix); +} + +NS_IMETHODIMP +nsEffectiveTLDService::GetKnownPublicSuffix(nsIURI* aURI, + nsACString& aPublicSuffix) { + NS_ENSURE_ARG_POINTER(aURI); + + nsAutoCString host; + nsresult rv = NS_GetInnermostURIHost(aURI, host); + if (NS_FAILED(rv)) { + return rv; + } + + return GetBaseDomainInternal(host, 0, true, aPublicSuffix); +} + +// External function for dealing with URI's correctly. +// Pulls out the host portion from an nsIURI, and calls through to +// GetBaseDomainFromHost(). +NS_IMETHODIMP +nsEffectiveTLDService::GetBaseDomain(nsIURI* aURI, uint32_t aAdditionalParts, + nsACString& aBaseDomain) { + NS_ENSURE_ARG_POINTER(aURI); + NS_ENSURE_TRUE(((int32_t)aAdditionalParts) >= 0, NS_ERROR_INVALID_ARG); + + nsAutoCString host; + nsresult rv = NS_GetInnermostURIHost(aURI, host); + if (NS_FAILED(rv)) { + return rv; + } + + return GetBaseDomainInternal(host, aAdditionalParts + 1, false, aBaseDomain); +} + +// External function for dealing with a host string directly: finds the public +// suffix (e.g. co.uk) for the given hostname. See GetBaseDomainInternal(). +NS_IMETHODIMP +nsEffectiveTLDService::GetPublicSuffixFromHost(const nsACString& aHostname, + nsACString& aPublicSuffix) { + // Create a mutable copy of the hostname and normalize it to ACE. + // This will fail if the hostname includes invalid characters. + nsAutoCString normHostname(aHostname); + nsresult rv = NormalizeHostname(normHostname); + if (NS_FAILED(rv)) { + return rv; + } + + return GetBaseDomainInternal(normHostname, 0, false, aPublicSuffix); +} + +NS_IMETHODIMP +nsEffectiveTLDService::GetKnownPublicSuffixFromHost(const nsACString& aHostname, + nsACString& aPublicSuffix) { + // Create a mutable copy of the hostname and normalize it to ACE. + // This will fail if the hostname includes invalid characters. + nsAutoCString normHostname(aHostname); + nsresult rv = NormalizeHostname(normHostname); + if (NS_FAILED(rv)) { + return rv; + } + + return GetBaseDomainInternal(normHostname, 0, true, aPublicSuffix); +} + +// External function for dealing with a host string directly: finds the base +// domain (e.g. www.co.uk) for the given hostname and number of subdomain parts +// requested. See GetBaseDomainInternal(). +NS_IMETHODIMP +nsEffectiveTLDService::GetBaseDomainFromHost(const nsACString& aHostname, + uint32_t aAdditionalParts, + nsACString& aBaseDomain) { + NS_ENSURE_TRUE(((int32_t)aAdditionalParts) >= 0, NS_ERROR_INVALID_ARG); + + // Create a mutable copy of the hostname and normalize it to ACE. + // This will fail if the hostname includes invalid characters. + nsAutoCString normHostname(aHostname); + nsresult rv = NormalizeHostname(normHostname); + if (NS_FAILED(rv)) { + return rv; + } + + return GetBaseDomainInternal(normHostname, aAdditionalParts + 1, false, + aBaseDomain); +} + +NS_IMETHODIMP +nsEffectiveTLDService::GetNextSubDomain(const nsACString& aHostname, + nsACString& aBaseDomain) { + // Create a mutable copy of the hostname and normalize it to ACE. + // This will fail if the hostname includes invalid characters. + nsAutoCString normHostname(aHostname); + nsresult rv = NormalizeHostname(normHostname); + NS_ENSURE_SUCCESS(rv, rv); + + return GetBaseDomainInternal(normHostname, -1, false, aBaseDomain); +} + +// Finds the base domain for a host, with requested number of additional parts. +// This will fail, generating an error, if the host is an IPv4/IPv6 address, +// if more subdomain parts are requested than are available, or if the hostname +// includes characters that are not valid in a URL. Normalization is performed +// on the host string and the result will be in UTF8. +nsresult nsEffectiveTLDService::GetBaseDomainInternal( + nsCString& aHostname, int32_t aAdditionalParts, bool aOnlyKnownPublicSuffix, + nsACString& aBaseDomain) { + const int kExceptionRule = 1; + const int kWildcardRule = 2; + + if (aHostname.IsEmpty()) { + return NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS; + } + + // chomp any trailing dot, and keep track of it for later + bool trailingDot = aHostname.Last() == '.'; + if (trailingDot) { + aHostname.Truncate(aHostname.Length() - 1); + } + + // check the edge cases of the host being '.' or having a second trailing '.', + // since subsequent checks won't catch it. + if (aHostname.IsEmpty() || aHostname.Last() == '.') { + return NS_ERROR_INVALID_ARG; + } + + // Lookup in the cache if this is a normal query. This is restricted to + // main thread-only as the cache is not thread-safe. + Maybe<TldCache::Entry> entry; + if (aAdditionalParts == 1 && NS_IsMainThread()) { + auto p = mMruTable.Lookup(aHostname); + if (p) { + if (NS_FAILED(p.Data().mResult)) { + return p.Data().mResult; + } + + // There was a match, just return the cached value. + aBaseDomain = p.Data().mBaseDomain; + if (trailingDot) { + aBaseDomain.Append('.'); + } + + return NS_OK; + } + + entry = Some(p); + } + + // Check if we're dealing with an IPv4/IPv6 hostname, and return + PRNetAddr addr; + PRStatus result = PR_StringToNetAddr(aHostname.get(), &addr); + if (result == PR_SUCCESS) { + // Update the MRU table if in use. + if (entry) { + entry->Set(TLDCacheEntry{aHostname, ""_ns, NS_ERROR_HOST_IS_IP_ADDRESS}); + } + + return NS_ERROR_HOST_IS_IP_ADDRESS; + } + + // Walk up the domain tree, most specific to least specific, + // looking for matches at each level. Note that a given level may + // have multiple attributes (e.g. IsWild() and IsNormal()). + const char* prevDomain = nullptr; + const char* currDomain = aHostname.get(); + const char* nextDot = strchr(currDomain, '.'); + const char* end = currDomain + aHostname.Length(); + // Default value of *eTLD is currDomain as set in the while loop below + const char* eTLD = nullptr; + bool hasKnownPublicSuffix = false; + while (true) { + // sanity check the string we're about to look up: it should not begin + // with a '.'; this would mean the hostname began with a '.' or had an + // embedded '..' sequence. + if (*currDomain == '.') { + // Update the MRU table if in use. + if (entry) { + entry->Set(TLDCacheEntry{aHostname, ""_ns, NS_ERROR_INVALID_ARG}); + } + + return NS_ERROR_INVALID_ARG; + } + + int result; + { + AutoReadLock lock(mGraphLock); + // Perform the lookup. + result = mGraph->Lookup(Substring(currDomain, end)); + } + if (result != Dafsa::kKeyNotFound) { + hasKnownPublicSuffix = true; + if (result == kWildcardRule && prevDomain) { + // wildcard rules imply an eTLD one level inferior to the match. + eTLD = prevDomain; + break; + } + if (result != kExceptionRule || !nextDot) { + // specific match, or we've hit the top domain level + eTLD = currDomain; + break; + } + if (result == kExceptionRule) { + // exception rules imply an eTLD one level superior to the match. + eTLD = nextDot + 1; + break; + } + } + + if (!nextDot) { + // we've hit the top domain level; use it by default. + eTLD = currDomain; + break; + } + + prevDomain = currDomain; + currDomain = nextDot + 1; + nextDot = strchr(currDomain, '.'); + } + + if (aOnlyKnownPublicSuffix && !hasKnownPublicSuffix) { + aBaseDomain.Truncate(); + return NS_OK; + } + + const char *begin, *iter; + if (aAdditionalParts < 0) { + NS_ASSERTION(aAdditionalParts == -1, + "aAdditionalParts can't be negative and different from -1"); + + for (iter = aHostname.get(); iter != eTLD && *iter != '.'; iter++) { + ; + } + + if (iter != eTLD) { + iter++; + } + if (iter != eTLD) { + aAdditionalParts = 0; + } + } else { + // count off the number of requested domains. + begin = aHostname.get(); + iter = eTLD; + + while (true) { + if (iter == begin) { + break; + } + + if (*(--iter) == '.' && aAdditionalParts-- == 0) { + ++iter; + ++aAdditionalParts; + break; + } + } + } + + if (aAdditionalParts != 0) { + // Update the MRU table if in use. + if (entry) { + entry->Set( + TLDCacheEntry{aHostname, ""_ns, NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS}); + } + + return NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS; + } + + aBaseDomain = Substring(iter, end); + + // Update the MRU table if in use. + if (entry) { + entry->Set(TLDCacheEntry{aHostname, nsCString(aBaseDomain), NS_OK}); + } + + // add on the trailing dot, if applicable + if (trailingDot) { + aBaseDomain.Append('.'); + } + + return NS_OK; +} + +// Normalizes the given hostname, component by component. ASCII/ACE +// components are lower-cased, and UTF-8 components are normalized per +// RFC 3454 and converted to ACE. +nsresult nsEffectiveTLDService::NormalizeHostname(nsCString& aHostname) { + if (!IsAscii(aHostname)) { + nsresult rv = mIDNService->ConvertUTF8toACE(aHostname, aHostname); + if (NS_FAILED(rv)) { + return rv; + } + } + + ToLowerCase(aHostname); + return NS_OK; +} + +NS_IMETHODIMP +nsEffectiveTLDService::HasRootDomain(const nsACString& aInput, + const nsACString& aHost, bool* aResult) { + if (NS_WARN_IF(!aResult)) { + return NS_ERROR_FAILURE; + } + + *aResult = false; + + // If the strings are the same, we obviously have a match. + if (aInput == aHost) { + *aResult = true; + return NS_OK; + } + + // If aHost is not found, we know we do not have it as a root domain. + int32_t index = nsAutoCString(aInput).Find(aHost.BeginReading()); + if (index == kNotFound) { + return NS_OK; + } + + // Otherwise, we have aHost as our root domain iff the index of aHost is + // aHost.length subtracted from our length and (since we do not have an + // exact match) the character before the index is a dot or slash. + *aResult = index > 0 && (uint32_t)index == aInput.Length() - aHost.Length() && + (aInput[index - 1] == '.' || aInput[index - 1] == '/'); + return NS_OK; +} diff --git a/netwerk/dns/nsEffectiveTLDService.h b/netwerk/dns/nsEffectiveTLDService.h new file mode 100644 index 0000000000..9a9ad22977 --- /dev/null +++ b/netwerk/dns/nsEffectiveTLDService.h @@ -0,0 +1,92 @@ +//* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef EffectiveTLDService_h +#define EffectiveTLDService_h + +#include "nsIEffectiveTLDService.h" + +#include "mozilla/AutoMemMap.h" +#include "mozilla/Attributes.h" +#include "mozilla/Dafsa.h" +#include "mozilla/Maybe.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/MruCache.h" +#include "mozilla/RWLock.h" + +#include "nsCOMPtr.h" +#include "nsHashKeys.h" +#include "nsIMemoryReporter.h" +#include "nsIObserver.h" +#include "nsString.h" + +class nsIIDNService; + +class nsEffectiveTLDService final : public nsIEffectiveTLDService, + public nsIObserver, + public nsIMemoryReporter { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIEFFECTIVETLDSERVICE + NS_DECL_NSIMEMORYREPORTER + NS_DECL_NSIOBSERVER + + nsEffectiveTLDService(); + nsresult Init(); + + static nsEffectiveTLDService* GetInstance(); + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf); + + private: + nsresult GetBaseDomainInternal(nsCString& aHostname, int32_t aAdditionalParts, + bool aOnlyKnownPublicSuffix, + nsACString& aBaseDomain); + nsresult NormalizeHostname(nsCString& aHostname); + ~nsEffectiveTLDService(); + + nsCOMPtr<nsIIDNService> mIDNService; + + // The DAFSA provides a compact encoding of the rather large eTLD list. + mozilla::Maybe<mozilla::Dafsa> mGraph; + + // Memory map used for a new updated dafsa + mozilla::loader::AutoMemMap mDafsaMap; + + // Lock for mGraph and mDafsaMap + mozilla::RWLock mGraphLock; + + // Note that the cache entries here can record entries that were cached + // successfully or unsuccessfully. mResult must be checked before using an + // entry. If it's a success error code, the cache entry is valid and can be + // used. + struct TLDCacheEntry { + nsCString mHost; + nsCString mBaseDomain; + nsresult mResult; + }; + + // We use a small most recently used cache to compensate for DAFSA lookups + // being slightly slower than a binary search on a larger table of strings. + // + // We first check the cache for a matching result and avoid a DAFSA lookup + // if a match is found. Otherwise we lookup the domain in the DAFSA and then + // cache the result. During standard browsing the same domains are repeatedly + // fed into |GetBaseDomainInternal| so this ends up being an effective + // mitigation getting about a 99% hit rate with four tabs open. + struct TldCache + : public mozilla::MruCache<nsACString, TLDCacheEntry, TldCache> { + static mozilla::HashNumber Hash(const nsACString& aKey) { + return mozilla::HashString(aKey); + } + static bool Match(const nsACString& aKey, const TLDCacheEntry& aVal) { + return aKey == aVal.mHost; + } + }; + + TldCache mMruTable; +}; + +#endif // EffectiveTLDService_h diff --git a/netwerk/dns/nsHostResolver.cpp b/netwerk/dns/nsHostResolver.cpp new file mode 100644 index 0000000000..268e2ec38b --- /dev/null +++ b/netwerk/dns/nsHostResolver.cpp @@ -0,0 +1,2325 @@ +/* vim:set ts=4 sw=2 sts=2 et cin: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#if defined(HAVE_RES_NINIT) +# include <sys/types.h> +# include <netinet/in.h> +# include <arpa/inet.h> +# include <arpa/nameser.h> +# include <resolv.h> +# define RES_RETRY_ON_FAILURE +#endif + +#include <stdlib.h> +#include <ctime> +#include "nsHostResolver.h" +#include "nsError.h" +#include "nsISupportsBase.h" +#include "nsISupportsUtils.h" +#include "nsIThreadManager.h" +#include "nsComponentManagerUtils.h" +#include "nsNetUtil.h" +#include "nsPrintfCString.h" +#include "nsXPCOMCIDInternal.h" +#include "prthread.h" +#include "prerror.h" +#include "prtime.h" +#include "mozilla/Logging.h" +#include "PLDHashTable.h" +#include "plstr.h" +#include "nsQueryObject.h" +#include "nsURLHelper.h" +#include "nsThreadUtils.h" +#include "nsThreadPool.h" +#include "GetAddrInfo.h" +#include "GeckoProfiler.h" +#include "TRR.h" +#include "TRRQuery.h" +#include "TRRService.h" + +#include "mozilla/Atomics.h" +#include "mozilla/HashFunctions.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/Telemetry.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/Preferences.h" +#include "mozilla/StaticPrefs_network.h" + +using namespace mozilla; +using namespace mozilla::net; + +// None of our implementations expose a TTL for negative responses, so we use a +// constant always. +static const unsigned int NEGATIVE_RECORD_LIFETIME = 60; + +//---------------------------------------------------------------------------- + +// Use a persistent thread pool in order to avoid spinning up new threads all +// the time. In particular, thread creation results in a res_init() call from +// libc which is quite expensive. +// +// The pool dynamically grows between 0 and MAX_RESOLVER_THREADS in size. New +// requests go first to an idle thread. If that cannot be found and there are +// fewer than MAX_RESOLVER_THREADS currently in the pool a new thread is created +// for high priority requests. If the new request is at a lower priority a new +// thread will only be created if there are fewer than HighThreadThreshold +// currently outstanding. If a thread cannot be created or an idle thread +// located for the request it is queued. +// +// When the pool is greater than HighThreadThreshold in size a thread will be +// destroyed after ShortIdleTimeoutSeconds of idle time. Smaller pools use +// LongIdleTimeoutSeconds for a timeout period. + +#define HighThreadThreshold MAX_RESOLVER_THREADS_FOR_ANY_PRIORITY +#define LongIdleTimeoutSeconds 300 // for threads 1 -> HighThreadThreshold +#define ShortIdleTimeoutSeconds \ + 60 // for threads HighThreadThreshold+1 -> MAX_RESOLVER_THREADS + +static_assert( + HighThreadThreshold <= MAX_RESOLVER_THREADS, + "High Thread Threshold should be less equal Maximum allowed thread"); + +//---------------------------------------------------------------------------- + +namespace mozilla::net { +LazyLogModule gHostResolverLog("nsHostResolver"); +#define LOG(args) \ + MOZ_LOG(mozilla::net::gHostResolverLog, mozilla::LogLevel::Debug, args) +#define LOG1(args) \ + MOZ_LOG(mozilla::net::gHostResolverLog, mozilla::LogLevel::Error, args) +#define LOG_ENABLED() \ + MOZ_LOG_TEST(mozilla::net::gHostResolverLog, mozilla::LogLevel::Debug) +} // namespace mozilla::net + +//---------------------------------------------------------------------------- + +#if defined(RES_RETRY_ON_FAILURE) + +// this class represents the resolver state for a given thread. if we +// encounter a lookup failure, then we can invoke the Reset method on an +// instance of this class to reset the resolver (in case /etc/resolv.conf +// for example changed). this is mainly an issue on GNU systems since glibc +// only reads in /etc/resolv.conf once per thread. it may be an issue on +// other systems as well. + +class nsResState { + public: + nsResState() + // initialize mLastReset to the time when this object + // is created. this means that a reset will not occur + // if a thread is too young. the alternative would be + // to initialize this to the beginning of time, so that + // the first failure would cause a reset, but since the + // thread would have just started up, it likely would + // already have current /etc/resolv.conf info. + : mLastReset(PR_IntervalNow()) {} + + bool Reset() { + // reset no more than once per second + if (PR_IntervalToSeconds(PR_IntervalNow() - mLastReset) < 1) { + return false; + } + + LOG(("Calling 'res_ninit'.\n")); + + mLastReset = PR_IntervalNow(); + return (res_ninit(&_res) == 0); + } + + private: + PRIntervalTime mLastReset; +}; + +#endif // RES_RETRY_ON_FAILURE + +//---------------------------------------------------------------------------- + +static inline bool IsHighPriority(uint16_t flags) { + return !(flags & (nsHostResolver::RES_PRIORITY_LOW | + nsHostResolver::RES_PRIORITY_MEDIUM)); +} + +static inline bool IsMediumPriority(uint16_t flags) { + return flags & nsHostResolver::RES_PRIORITY_MEDIUM; +} + +static inline bool IsLowPriority(uint16_t flags) { + return flags & nsHostResolver::RES_PRIORITY_LOW; +} + +//---------------------------------------------------------------------------- +// this macro filters out any flags that are not used when constructing the +// host key. the significant flags are those that would affect the resulting +// host record (i.e., the flags that are passed down to PR_GetAddrInfoByName). +#define RES_KEY_FLAGS(_f) \ + ((_f) & \ + (nsHostResolver::RES_CANON_NAME | nsHostResolver::RES_DISABLE_TRR | \ + nsIDNSService::RESOLVE_TRR_MODE_MASK | nsHostResolver::RES_IP_HINT)) + +#define IS_ADDR_TYPE(_type) ((_type) == nsIDNSService::RESOLVE_TYPE_DEFAULT) +#define IS_OTHER_TYPE(_type) ((_type) != nsIDNSService::RESOLVE_TYPE_DEFAULT) + +nsHostKey::nsHostKey(const nsACString& aHost, const nsACString& aTrrServer, + uint16_t aType, uint16_t aFlags, uint16_t aAf, bool aPb, + const nsACString& aOriginsuffix) + : host(aHost), + mTrrServer(aTrrServer), + type(aType), + flags(aFlags), + af(aAf), + pb(aPb), + originSuffix(aOriginsuffix) {} + +bool nsHostKey::operator==(const nsHostKey& other) const { + return host == other.host && mTrrServer == other.mTrrServer && + type == other.type && + RES_KEY_FLAGS(flags) == RES_KEY_FLAGS(other.flags) && af == other.af && + originSuffix == other.originSuffix; +} + +PLDHashNumber nsHostKey::Hash() const { + return AddToHash(HashString(host.get()), HashString(mTrrServer.get()), type, + RES_KEY_FLAGS(flags), af, HashString(originSuffix.get())); +} + +size_t nsHostKey::SizeOfExcludingThis( + mozilla::MallocSizeOf mallocSizeOf) const { + size_t n = 0; + n += host.SizeOfExcludingThisIfUnshared(mallocSizeOf); + n += mTrrServer.SizeOfExcludingThisIfUnshared(mallocSizeOf); + n += originSuffix.SizeOfExcludingThisIfUnshared(mallocSizeOf); + return n; +} + +NS_IMPL_ISUPPORTS0(nsHostRecord) + +nsHostRecord::nsHostRecord(const nsHostKey& key) + : nsHostKey(key), + mEffectiveTRRMode(nsIRequest::TRR_DEFAULT_MODE), + mTRRQuery("nsHostRecord.mTRRQuery"), + mResolving(0), + negative(false), + mDoomed(false) {} + +void nsHostRecord::Invalidate() { mDoomed = true; } + +void nsHostRecord::Cancel() { + RefPtr<TRRQuery> query; + { + auto lock = mTRRQuery.Lock(); + query.swap(lock.ref()); + } + + if (query) { + query->Cancel(); + } +} + +nsHostRecord::ExpirationStatus nsHostRecord::CheckExpiration( + const mozilla::TimeStamp& now) const { + if (!mGraceStart.IsNull() && now >= mGraceStart && !mValidEnd.IsNull() && + now < mValidEnd) { + return nsHostRecord::EXP_GRACE; + } + if (!mValidEnd.IsNull() && now < mValidEnd) { + return nsHostRecord::EXP_VALID; + } + + return nsHostRecord::EXP_EXPIRED; +} + +void nsHostRecord::SetExpiration(const mozilla::TimeStamp& now, + unsigned int valid, unsigned int grace) { + mValidStart = now; + if ((valid + grace) < 60) { + grace = 60 - valid; + LOG(("SetExpiration: artificially bumped grace to %d\n", grace)); + } + mGraceStart = now + TimeDuration::FromSeconds(valid); + mValidEnd = now + TimeDuration::FromSeconds(valid + grace); +} + +void nsHostRecord::CopyExpirationTimesAndFlagsFrom( + const nsHostRecord* aFromHostRecord) { + // This is used to copy information from a cache entry to a record. All + // information necessary for HasUsableRecord needs to be copied. + mValidStart = aFromHostRecord->mValidStart; + mValidEnd = aFromHostRecord->mValidEnd; + mGraceStart = aFromHostRecord->mGraceStart; + mDoomed = aFromHostRecord->mDoomed; +} + +bool nsHostRecord::HasUsableResult(const mozilla::TimeStamp& now, + uint16_t queryFlags) const { + if (mDoomed) { + return false; + } + + // don't use cached negative results for high priority queries. + if (negative && IsHighPriority(queryFlags)) { + return false; + } + + if (CheckExpiration(now) == EXP_EXPIRED) { + return false; + } + + if (negative) { + return true; + } + + return HasUsableResultInternal(); +} + +static size_t SizeOfResolveHostCallbackListExcludingHead( + const mozilla::LinkedList<RefPtr<nsResolveHostCallback>>& aCallbacks, + MallocSizeOf mallocSizeOf) { + size_t n = aCallbacks.sizeOfExcludingThis(mallocSizeOf); + + for (const nsResolveHostCallback* t = aCallbacks.getFirst(); t; + t = t->getNext()) { + n += t->SizeOfIncludingThis(mallocSizeOf); + } + + return n; +} + +NS_IMPL_ISUPPORTS_INHERITED(AddrHostRecord, nsHostRecord, AddrHostRecord) + +AddrHostRecord::AddrHostRecord(const nsHostKey& key) + : nsHostRecord(key), + addr_info_lock("AddrHostRecord.addr_info_lock"), + addr_info_gencnt(0), + addr_info(nullptr), + addr(nullptr), + mTRRUsed(false), + mTRRSuccess(0), + mNativeSuccess(0) {} + +AddrHostRecord::~AddrHostRecord() { + mCallbacks.clear(); + Telemetry::Accumulate(Telemetry::DNS_BLACKLIST_COUNT, mUnusableCount); +} + +bool AddrHostRecord::Blocklisted(const NetAddr* aQuery) { + addr_info_lock.AssertCurrentThreadOwns(); + LOG(("Checking unusable list for host [%s], host record [%p].\n", host.get(), + this)); + + // skip the string conversion for the common case of no blocklist + if (!mUnusableItems.Length()) { + return false; + } + + char buf[kIPv6CStrBufSize]; + if (!aQuery->ToStringBuffer(buf, sizeof(buf))) { + return false; + } + nsDependentCString strQuery(buf); + + for (uint32_t i = 0; i < mUnusableItems.Length(); i++) { + if (mUnusableItems.ElementAt(i).Equals(strQuery)) { + LOG(("Address [%s] is blocklisted for host [%s].\n", buf, host.get())); + return true; + } + } + + return false; +} + +void AddrHostRecord::ReportUnusable(const NetAddr* aAddress) { + addr_info_lock.AssertCurrentThreadOwns(); + LOG( + ("Adding address to blocklist for host [%s], host record [%p]." + "used trr=%d\n", + host.get(), this, mTRRSuccess)); + + ++mUnusableCount; + + char buf[kIPv6CStrBufSize]; + if (aAddress->ToStringBuffer(buf, sizeof(buf))) { + LOG( + ("Successfully adding address [%s] to blocklist for host " + "[%s].\n", + buf, host.get())); + mUnusableItems.AppendElement(nsCString(buf)); + } +} + +void AddrHostRecord::ResetBlocklist() { + addr_info_lock.AssertCurrentThreadOwns(); + LOG(("Resetting blocklist for host [%s], host record [%p].\n", host.get(), + this)); + mUnusableItems.Clear(); +} + +size_t AddrHostRecord::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const { + size_t n = mallocSizeOf(this); + + n += nsHostKey::SizeOfExcludingThis(mallocSizeOf); + n += SizeOfResolveHostCallbackListExcludingHead(mCallbacks, mallocSizeOf); + + n += addr_info ? addr_info->SizeOfIncludingThis(mallocSizeOf) : 0; + n += mallocSizeOf(addr.get()); + + n += mUnusableItems.ShallowSizeOfExcludingThis(mallocSizeOf); + for (size_t i = 0; i < mUnusableItems.Length(); i++) { + n += mUnusableItems[i].SizeOfExcludingThisIfUnshared(mallocSizeOf); + } + return n; +} + +bool AddrHostRecord::HasUsableResultInternal() const { + return addr_info || addr; +} + +// Returns true if the entry can be removed, or false if it should be left. +// Sets ResolveAgain true for entries being resolved right now. +bool AddrHostRecord::RemoveOrRefresh(bool aTrrToo) { + // no need to flush TRRed names, they're not resolved "locally" + MutexAutoLock lock(addr_info_lock); + if (addr_info && !aTrrToo && addr_info->IsTRR()) { + return false; + } + if (LoadNative()) { + if (!onQueue()) { + // The request has been passed to the OS resolver. The resultant DNS + // record should be considered stale and not trusted; set a flag to + // ensure it is called again. + StoreResolveAgain(true); + } + // if onQueue is true, the host entry is already added to the cache + // but is still pending to get resolved: just leave it in hash. + return false; + } + // Already resolved; not in a pending state; remove from cache + return true; +} + +void AddrHostRecord::ResolveComplete() { + if (LoadNativeUsed()) { + if (mNativeSuccess) { + uint32_t millis = static_cast<uint32_t>(mNativeDuration.ToMilliseconds()); + Telemetry::Accumulate(Telemetry::DNS_NATIVE_LOOKUP_TIME, millis); + } + AccumulateCategoricalKeyed( + TRRService::AutoDetectedKey(), + mNativeSuccess ? Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::osOK + : Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::osFail); + } + + if (mTRRUsed) { + if (mTRRSuccess) { + uint32_t millis = static_cast<uint32_t>(mTrrDuration.ToMilliseconds()); + Telemetry::Accumulate(Telemetry::DNS_TRR_LOOKUP_TIME2, + TRRService::AutoDetectedKey(), millis); + } + AccumulateCategoricalKeyed( + TRRService::AutoDetectedKey(), + mTRRSuccess ? Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::trrOK + : Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::trrFail); + } + + if (nsHostResolver::Mode() == MODE_TRRFIRST) { + Telemetry::Accumulate(Telemetry::TRR_SKIP_REASON_TRR_FIRST, + mTRRTRRSkippedReason); + + if (mNativeSuccess) { + Telemetry::Accumulate(Telemetry::TRR_SKIP_REASON_DNS_WORKED, + mTRRTRRSkippedReason); + } + } + + if (mEffectiveTRRMode == nsIRequest::TRR_FIRST_MODE) { + if (flags & nsIDNSService::RESOLVE_DISABLE_TRR) { + // TRR is disabled on request, which is a next-level back-off method. + Telemetry::Accumulate(Telemetry::DNS_TRR_DISABLED2, + TRRService::AutoDetectedKey(), mNativeSuccess); + } else { + if (mTRRSuccess) { + AccumulateCategoricalKeyed(TRRService::AutoDetectedKey(), + Telemetry::LABELS_DNS_TRR_FIRST3::TRR); + } else if (mNativeSuccess) { + if (mTRRUsed) { + AccumulateCategoricalKeyed( + TRRService::AutoDetectedKey(), + Telemetry::LABELS_DNS_TRR_FIRST3::NativeAfterTRR); + } else { + AccumulateCategoricalKeyed(TRRService::AutoDetectedKey(), + Telemetry::LABELS_DNS_TRR_FIRST3::Native); + } + } else { + AccumulateCategoricalKeyed( + TRRService::AutoDetectedKey(), + Telemetry::LABELS_DNS_TRR_FIRST3::BothFailed); + } + } + } + + switch (mEffectiveTRRMode) { + case nsIRequest::TRR_DISABLED_MODE: + AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::nativeOnly); + break; + case nsIRequest::TRR_FIRST_MODE: + AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::trrFirst); + break; + case nsIRequest::TRR_ONLY_MODE: + AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::trrOnly); + break; + case nsIRequest::TRR_DEFAULT_MODE: + MOZ_ASSERT_UNREACHABLE("We should not have a default value here"); + break; + } + + if (mTRRUsed && !mTRRSuccess && mNativeSuccess && gTRRService) { + gTRRService->AddToBlocklist(nsCString(host), originSuffix, pb, true); + } +} + +AddrHostRecord::DnsPriority AddrHostRecord::GetPriority(uint16_t aFlags) { + if (IsHighPriority(aFlags)) { + return AddrHostRecord::DNS_PRIORITY_HIGH; + } + if (IsMediumPriority(aFlags)) { + return AddrHostRecord::DNS_PRIORITY_MEDIUM; + } + + return AddrHostRecord::DNS_PRIORITY_LOW; +} + +NS_IMPL_ISUPPORTS_INHERITED(TypeHostRecord, nsHostRecord, TypeHostRecord, + nsIDNSTXTRecord, nsIDNSHTTPSSVCRecord) + +TypeHostRecord::TypeHostRecord(const nsHostKey& key) + : nsHostRecord(key), + DNSHTTPSSVCRecordBase(key.host), + mResultsLock("TypeHostRecord.mResultsLock"), + mAllRecordsExcluded(false) {} + +TypeHostRecord::~TypeHostRecord() { mCallbacks.clear(); } + +bool TypeHostRecord::HasUsableResultInternal() const { + return !mResults.is<Nothing>(); +} + +NS_IMETHODIMP TypeHostRecord::GetRecords(CopyableTArray<nsCString>& aRecords) { + // deep copy + MutexAutoLock lock(mResultsLock); + + if (!mResults.is<TypeRecordTxt>()) { + return NS_ERROR_NOT_AVAILABLE; + } + aRecords = mResults.as<CopyableTArray<nsCString>>(); + return NS_OK; +} + +NS_IMETHODIMP TypeHostRecord::GetRecordsAsOneString(nsACString& aRecords) { + // deep copy + MutexAutoLock lock(mResultsLock); + + if (!mResults.is<TypeRecordTxt>()) { + return NS_ERROR_NOT_AVAILABLE; + } + auto& results = mResults.as<CopyableTArray<nsCString>>(); + for (uint32_t i = 0; i < results.Length(); i++) { + aRecords.Append(results[i]); + } + return NS_OK; +} + +size_t TypeHostRecord::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const { + size_t n = mallocSizeOf(this); + + n += nsHostKey::SizeOfExcludingThis(mallocSizeOf); + n += SizeOfResolveHostCallbackListExcludingHead(mCallbacks, mallocSizeOf); + + return n; +} + +uint32_t TypeHostRecord::GetType() { + MutexAutoLock lock(mResultsLock); + + return mResults.match( + [](TypeRecordEmpty&) { + MOZ_ASSERT(false, "This should never be the case"); + return nsIDNSService::RESOLVE_TYPE_DEFAULT; + }, + [](TypeRecordTxt&) { return nsIDNSService::RESOLVE_TYPE_TXT; }, + [](TypeRecordHTTPSSVC&) { return nsIDNSService::RESOLVE_TYPE_HTTPSSVC; }); +} + +TypeRecordResultType TypeHostRecord::GetResults() { + MutexAutoLock lock(mResultsLock); + return mResults; +} + +NS_IMETHODIMP +TypeHostRecord::GetRecords(nsTArray<RefPtr<nsISVCBRecord>>& aRecords) { + MutexAutoLock lock(mResultsLock); + if (!mResults.is<TypeRecordHTTPSSVC>()) { + return NS_ERROR_NOT_AVAILABLE; + } + + auto& results = mResults.as<TypeRecordHTTPSSVC>(); + + for (const SVCB& r : results) { + RefPtr<nsISVCBRecord> rec = new mozilla::net::SVCBRecord(r); + aRecords.AppendElement(rec); + } + + return NS_OK; +} + +NS_IMETHODIMP +TypeHostRecord::GetServiceModeRecord(bool aNoHttp2, bool aNoHttp3, + nsISVCBRecord** aRecord) { + MutexAutoLock lock(mResultsLock); + if (!mResults.is<TypeRecordHTTPSSVC>()) { + return NS_ERROR_NOT_AVAILABLE; + } + + auto& results = mResults.as<TypeRecordHTTPSSVC>(); + nsCOMPtr<nsISVCBRecord> result = GetServiceModeRecordInternal( + aNoHttp2, aNoHttp3, results, mAllRecordsExcluded); + if (!result) { + return NS_ERROR_NOT_AVAILABLE; + } + + result.forget(aRecord); + return NS_OK; +} + +NS_IMETHODIMP +TypeHostRecord::GetAllRecordsWithEchConfig( + bool aNoHttp2, bool aNoHttp3, bool* aAllRecordsHaveEchConfig, + bool* aAllRecordsInH3ExcludedList, + nsTArray<RefPtr<nsISVCBRecord>>& aResult) { + MutexAutoLock lock(mResultsLock); + if (!mResults.is<TypeRecordHTTPSSVC>()) { + return NS_ERROR_NOT_AVAILABLE; + } + + auto& records = mResults.as<TypeRecordHTTPSSVC>(); + GetAllRecordsWithEchConfigInternal(aNoHttp2, aNoHttp3, records, + aAllRecordsHaveEchConfig, + aAllRecordsInH3ExcludedList, aResult); + return NS_OK; +} + +NS_IMETHODIMP +TypeHostRecord::GetHasIPAddresses(bool* aResult) { + NS_ENSURE_ARG(aResult); + + if (!mResults.is<TypeRecordHTTPSSVC>()) { + return NS_ERROR_NOT_AVAILABLE; + } + + auto& results = mResults.as<TypeRecordHTTPSSVC>(); + *aResult = HasIPAddressesInternal(results); + return NS_OK; +} + +NS_IMETHODIMP +TypeHostRecord::GetAllRecordsExcluded(bool* aResult) { + NS_ENSURE_ARG(aResult); + + if (!mResults.is<TypeRecordHTTPSSVC>()) { + return NS_ERROR_NOT_AVAILABLE; + } + + *aResult = mAllRecordsExcluded; + return NS_OK; +} + +//---------------------------------------------------------------------------- + +static const char kPrefGetTtl[] = "network.dns.get-ttl"; +static const char kPrefNativeIsLocalhost[] = "network.dns.native-is-localhost"; +static const char kPrefThreadIdleTime[] = + "network.dns.resolver-thread-extra-idle-time-seconds"; +static bool sGetTtlEnabled = false; +mozilla::Atomic<bool, mozilla::Relaxed> gNativeIsLocalhost; + +static void DnsPrefChanged(const char* aPref, void* aSelf) { + MOZ_ASSERT(NS_IsMainThread(), + "Should be getting pref changed notification on main thread!"); + + MOZ_ASSERT(aSelf); + + if (!strcmp(aPref, kPrefGetTtl)) { +#ifdef DNSQUERY_AVAILABLE + sGetTtlEnabled = Preferences::GetBool(kPrefGetTtl); +#endif + } else if (!strcmp(aPref, kPrefNativeIsLocalhost)) { + gNativeIsLocalhost = Preferences::GetBool(kPrefNativeIsLocalhost); + } +} + +NS_IMPL_ISUPPORTS0(nsHostResolver) + +nsHostResolver::nsHostResolver(uint32_t maxCacheEntries, + uint32_t defaultCacheEntryLifetime, + uint32_t defaultGracePeriod) + : mMaxCacheEntries(maxCacheEntries), + mDefaultCacheLifetime(defaultCacheEntryLifetime), + mDefaultGracePeriod(defaultGracePeriod), + mLock("nsHostResolver.mLock"), + mIdleTaskCV(mLock, "nsHostResolver.mIdleTaskCV"), + mEvictionQSize(0), + mShutdown(true), + mNumIdleTasks(0), + mActiveTaskCount(0), + mActiveAnyThreadCount(0), + mPendingCount(0) { + mCreationTime = PR_Now(); + + mLongIdleTimeout = TimeDuration::FromSeconds(LongIdleTimeoutSeconds); + mShortIdleTimeout = TimeDuration::FromSeconds(ShortIdleTimeoutSeconds); +} + +nsHostResolver::~nsHostResolver() = default; + +nsresult nsHostResolver::Init() { + MOZ_ASSERT(NS_IsMainThread()); + if (NS_FAILED(GetAddrInfoInit())) { + return NS_ERROR_FAILURE; + } + + LOG(("nsHostResolver::Init this=%p", this)); + + mShutdown = false; + mNCS = NetworkConnectivityService::GetSingleton(); + + // The preferences probably haven't been loaded from the disk yet, so we + // need to register a callback that will set up the experiment once they + // are. We also need to explicitly set a value for the props otherwise the + // callback won't be called. + { + DebugOnly<nsresult> rv = Preferences::RegisterCallbackAndCall( + &DnsPrefChanged, kPrefGetTtl, this); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "Could not register DNS TTL pref callback."); + rv = Preferences::RegisterCallbackAndCall(&DnsPrefChanged, + kPrefNativeIsLocalhost, this); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "Could not register DNS pref callback."); + } + +#if defined(HAVE_RES_NINIT) + // We want to make sure the system is using the correct resolver settings, + // so we force it to reload those settings whenever we startup a subsequent + // nsHostResolver instance. We assume that there is no reason to do this + // for the first nsHostResolver instance since that is usually created + // during application startup. + static int initCount = 0; + if (initCount++ > 0) { + LOG(("Calling 'res_ninit'.\n")); + res_ninit(&_res); + } +#endif + + // We can configure the threadpool to keep threads alive for a while after + // the last ThreadFunc task has been executed. + int32_t poolTimeoutSecs = Preferences::GetInt(kPrefThreadIdleTime, 60); + uint32_t poolTimeoutMs; + if (poolTimeoutSecs < 0) { + // This means never shut down the idle threads + poolTimeoutMs = UINT32_MAX; + } else { + // We clamp down the idle time between 0 and one hour. + poolTimeoutMs = + mozilla::clamped<uint32_t>(poolTimeoutSecs * 1000, 0, 3600 * 1000); + } + + nsCOMPtr<nsIThreadPool> threadPool = new nsThreadPool(); + MOZ_ALWAYS_SUCCEEDS(threadPool->SetThreadLimit(MAX_RESOLVER_THREADS)); + MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadLimit(MAX_RESOLVER_THREADS)); + MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadTimeout(poolTimeoutMs)); + MOZ_ALWAYS_SUCCEEDS( + threadPool->SetThreadStackSize(nsIThreadManager::kThreadPoolStackSize)); + MOZ_ALWAYS_SUCCEEDS(threadPool->SetName("DNS Resolver"_ns)); + mResolverThreads = ToRefPtr(std::move(threadPool)); + + return NS_OK; +} + +void nsHostResolver::ClearPendingQueue( + LinkedList<RefPtr<nsHostRecord>>& aPendingQ) { + // loop through pending queue, erroring out pending lookups. + if (!aPendingQ.isEmpty()) { + for (const RefPtr<nsHostRecord>& rec : aPendingQ) { + rec->Cancel(); + if (rec->IsAddrRecord()) { + CompleteLookup(rec, NS_ERROR_ABORT, nullptr, rec->pb, rec->originSuffix, + rec->mTRRTRRSkippedReason); + } else { + mozilla::net::TypeRecordResultType empty(Nothing{}); + CompleteLookupByType(rec, NS_ERROR_ABORT, empty, 0, rec->pb); + } + } + } +} + +// +// FlushCache() is what we call when the network has changed. We must not +// trust names that were resolved before this change. They may resolve +// differently now. +// +// This function removes all existing resolved host entries from the hash. +// Names that are in the pending queues can be left there. Entries in the +// cache that have 'Resolve' set true but not 'OnQueue' are being resolved +// right now, so we need to mark them to get re-resolved on completion! + +void nsHostResolver::FlushCache(bool aTrrToo) { + MutexAutoLock lock(mLock); + mEvictionQSize = 0; + + // Clear the evictionQ and remove all its corresponding entries from + // the cache first + if (!mEvictionQ.isEmpty()) { + for (const RefPtr<nsHostRecord>& rec : mEvictionQ) { + rec->Cancel(); + mRecordDB.Remove(*static_cast<nsHostKey*>(rec)); + } + mEvictionQ.clear(); + } + + // Refresh the cache entries that are resolving RIGHT now, remove the rest. + for (auto iter = mRecordDB.Iter(); !iter.Done(); iter.Next()) { + nsHostRecord* record = iter.UserData(); + // Try to remove the record, or mark it for refresh. + // By-type records are from TRR. We do not need to flush those entry + // when the network has change, because they are not local. + if (record->IsAddrRecord()) { + RefPtr<AddrHostRecord> addrRec = do_QueryObject(record); + MOZ_ASSERT(addrRec); + if (addrRec->RemoveOrRefresh(aTrrToo)) { + if (record->isInList()) { + record->remove(); + } + iter.Remove(); + } + } + } +} + +void nsHostResolver::Shutdown() { + LOG(("Shutting down host resolver.\n")); + + { + DebugOnly<nsresult> rv = + Preferences::UnregisterCallback(&DnsPrefChanged, kPrefGetTtl, this); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "Could not unregister DNS TTL pref callback."); + } + + LinkedList<RefPtr<nsHostRecord>> pendingQHigh, pendingQMed, pendingQLow, + evictionQ; + + { + MutexAutoLock lock(mLock); + + mShutdown = true; + + // Move queues to temporary lists. + pendingQHigh = std::move(mHighQ); + pendingQMed = std::move(mMediumQ); + pendingQLow = std::move(mLowQ); + evictionQ = std::move(mEvictionQ); + + mEvictionQSize = 0; + mPendingCount = 0; + + if (mNumIdleTasks) { + mIdleTaskCV.NotifyAll(); + } + + for (auto iter = mRecordDB.Iter(); !iter.Done(); iter.Next()) { + iter.UserData()->Cancel(); + } + // empty host database + mRecordDB.Clear(); + + mNCS = nullptr; + } + + ClearPendingQueue(pendingQHigh); + ClearPendingQueue(pendingQMed); + ClearPendingQueue(pendingQLow); + + if (!evictionQ.isEmpty()) { + for (const RefPtr<nsHostRecord>& rec : evictionQ) { + rec->Cancel(); + } + } + + pendingQHigh.clear(); + pendingQMed.clear(); + pendingQLow.clear(); + evictionQ.clear(); + + // Shutdown the resolver threads, but with a timeout of 2 seconds (prefable). + // If the timeout is exceeded, any stuck threads will be leaked. + mResolverThreads->ShutdownWithTimeout( + StaticPrefs::network_dns_resolver_shutdown_timeout_ms()); + + { + mozilla::DebugOnly<nsresult> rv = GetAddrInfoShutdown(); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to shutdown GetAddrInfo"); + } +} + +nsresult nsHostResolver::GetHostRecord(const nsACString& host, + const nsACString& aTrrServer, + uint16_t type, uint16_t flags, + uint16_t af, bool pb, + const nsCString& originSuffix, + nsHostRecord** result) { + MutexAutoLock lock(mLock); + nsHostKey key(host, aTrrServer, type, flags, af, pb, originSuffix); + + RefPtr<nsHostRecord>& entry = mRecordDB.GetOrInsert(key); + if (!entry) { + entry = InitRecord(key); + } + + RefPtr<nsHostRecord> rec = entry; + if (rec->IsAddrRecord()) { + RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec); + if (addrRec->addr) { + return NS_ERROR_FAILURE; + } + } + + if (rec->mResolving) { + return NS_ERROR_FAILURE; + } + + *result = rec.forget().take(); + return NS_OK; +} + +nsHostRecord* nsHostResolver::InitRecord(const nsHostKey& key) { + if (IS_ADDR_TYPE(key.type)) { + return new AddrHostRecord(key); + } + return new TypeHostRecord(key); +} + +already_AddRefed<nsHostRecord> nsHostResolver::InitLoopbackRecord( + const nsHostKey& key, nsresult* aRv) { + MOZ_ASSERT(aRv); + MOZ_ASSERT(IS_ADDR_TYPE(key.type)); + + *aRv = NS_ERROR_FAILURE; + RefPtr<nsHostRecord> rec = InitRecord(key); + + nsTArray<NetAddr> addresses; + PRNetAddr prAddr; + memset(&prAddr, 0, sizeof(prAddr)); + if (key.af == PR_AF_INET || key.af == PR_AF_UNSPEC) { + MOZ_RELEASE_ASSERT(PR_StringToNetAddr("127.0.0.1", &prAddr) == PR_SUCCESS); + addresses.AppendElement(NetAddr(&prAddr)); + } + if (key.af == PR_AF_INET6 || key.af == PR_AF_UNSPEC) { + MOZ_RELEASE_ASSERT(PR_StringToNetAddr("::1", &prAddr) == PR_SUCCESS); + addresses.AppendElement(NetAddr(&prAddr)); + } + + RefPtr<AddrInfo> ai = new AddrInfo(rec->host, 0, std::move(addresses)); + + RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec); + MutexAutoLock lock(addrRec->addr_info_lock); + addrRec->addr_info = ai; + addrRec->SetExpiration(TimeStamp::NowLoRes(), mDefaultCacheLifetime, + mDefaultGracePeriod); + addrRec->negative = false; + + *aRv = NS_OK; + return rec.forget(); +} + +nsresult nsHostResolver::ResolveHost(const nsACString& aHost, + const nsACString& aTrrServer, + uint16_t type, + const OriginAttributes& aOriginAttributes, + uint16_t flags, uint16_t af, + nsResolveHostCallback* aCallback) { + nsAutoCString host(aHost); + NS_ENSURE_TRUE(!host.IsEmpty(), NS_ERROR_UNEXPECTED); + + nsAutoCString originSuffix; + aOriginAttributes.CreateSuffix(originSuffix); + LOG(("Resolving host [%s]<%s>%s%s type %d. [this=%p]\n", host.get(), + originSuffix.get(), flags & RES_BYPASS_CACHE ? " - bypassing cache" : "", + flags & RES_REFRESH_CACHE ? " - refresh cache" : "", type, this)); + + // ensure that we are working with a valid hostname before proceeding. see + // bug 304904 for details. + if (!net_IsValidHostName(host)) { + return NS_ERROR_UNKNOWN_HOST; + } + + // By-Type requests use only TRR. If TRR is disabled we can return + // immediately. + if (IS_OTHER_TYPE(type) && Mode() == MODE_TRROFF) { + return NS_ERROR_UNKNOWN_HOST; + } + + // Used to try to parse to an IP address literal. + PRNetAddr tempAddr; + // Unfortunately, PR_StringToNetAddr does not properly initialize + // the output buffer in the case of IPv6 input. See bug 223145. + memset(&tempAddr, 0, sizeof(PRNetAddr)); + + if (IS_OTHER_TYPE(type) && + (PR_StringToNetAddr(host.get(), &tempAddr) == PR_SUCCESS)) { + // For by-type queries the host cannot be IP literal. + return NS_ERROR_UNKNOWN_HOST; + } + memset(&tempAddr, 0, sizeof(PRNetAddr)); + + RefPtr<nsResolveHostCallback> callback(aCallback); + // if result is set inside the lock, then we need to issue the + // callback before returning. + RefPtr<nsHostRecord> result; + nsresult status = NS_OK, rv = NS_OK; + { + MutexAutoLock lock(mLock); + + if (mShutdown) { + return NS_ERROR_NOT_INITIALIZED; + } + + // check to see if there is already an entry for this |host| + // in the hash table. if so, then check to see if we can't + // just reuse the lookup result. otherwise, if there are + // any pending callbacks, then add to pending callbacks queue, + // and return. otherwise, add ourselves as first pending + // callback, and proceed to do the lookup. + + bool excludedFromTRR = false; + + if (gTRRService && gTRRService->IsExcludedFromTRR(host)) { + flags |= RES_DISABLE_TRR; + excludedFromTRR = true; + + if (!aTrrServer.IsEmpty()) { + return NS_ERROR_UNKNOWN_HOST; + } + } + + nsHostKey key(host, aTrrServer, type, flags, af, + (aOriginAttributes.mPrivateBrowsingId > 0), originSuffix); + + // Check if we have a localhost domain, if so hardcode to loopback + if (IS_ADDR_TYPE(type) && IsLoopbackHostname(host)) { + nsresult rv; + RefPtr<nsHostRecord> result = InitLoopbackRecord(key, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + MOZ_ASSERT(result); + aCallback->OnResolveHostComplete(this, result, NS_OK); + return NS_OK; + } + + RefPtr<nsHostRecord>& entry = mRecordDB.GetOrInsert(key); + if (!entry) { + entry = InitRecord(key); + } + + RefPtr<nsHostRecord> rec = entry; + RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec); + MOZ_ASSERT(rec, "Record should not be null"); + MOZ_ASSERT((IS_ADDR_TYPE(type) && rec->IsAddrRecord() && addrRec) || + (IS_OTHER_TYPE(type) && !rec->IsAddrRecord())); + + if (excludedFromTRR) { + rec->RecordReason(nsHostRecord::TRR_EXCLUDED); + } + + if (!(flags & RES_BYPASS_CACHE) && + rec->HasUsableResult(TimeStamp::NowLoRes(), flags)) { + LOG((" Using cached record for host [%s].\n", host.get())); + // put reference to host record on stack... + result = rec; + if (IS_ADDR_TYPE(type)) { + Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT); + } + + // For entries that are in the grace period + // or all cached negative entries, use the cache but start a new + // lookup in the background + ConditionallyRefreshRecord(rec, host); + + if (rec->negative) { + LOG((" Negative cache entry for host [%s].\n", host.get())); + if (IS_ADDR_TYPE(type)) { + Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, + METHOD_NEGATIVE_HIT); + } + status = NS_ERROR_UNKNOWN_HOST; + } + + // Check whether host is a IP address for A/AAAA queries. + // For by-type records we have already checked at the beginning of + // this function. + } else if (addrRec && addrRec->addr) { + // if the host name is an IP address literal and has been + // parsed, go ahead and use it. + LOG((" Using cached address for IP Literal [%s].\n", host.get())); + Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_LITERAL); + result = rec; + } else if (addrRec && + PR_StringToNetAddr(host.get(), &tempAddr) == PR_SUCCESS) { + // try parsing the host name as an IP address literal to short + // circuit full host resolution. (this is necessary on some + // platforms like Win9x. see bug 219376 for more details.) + LOG((" Host is IP Literal [%s].\n", host.get())); + + // ok, just copy the result into the host record, and be + // done with it! ;-) + addrRec->addr = MakeUnique<NetAddr>(); + PRNetAddrToNetAddr(&tempAddr, addrRec->addr.get()); + // put reference to host record on stack... + Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_LITERAL); + result = rec; + + // Check if we have received too many requests. + } else if (mPendingCount >= MAX_NON_PRIORITY_REQUESTS && + !IsHighPriority(flags) && !rec->mResolving) { + LOG( + (" Lookup queue full: dropping %s priority request for " + "host [%s].\n", + IsMediumPriority(flags) ? "medium" : "low", host.get())); + if (IS_ADDR_TYPE(type)) { + Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_OVERFLOW); + } + // This is a lower priority request and we are swamped, so refuse it. + rv = NS_ERROR_DNS_LOOKUP_QUEUE_FULL; + + // Check if the offline flag is set. + } else if (flags & RES_OFFLINE) { + LOG((" Offline request for host [%s]; ignoring.\n", host.get())); + rv = NS_ERROR_OFFLINE; + + // We do not have a valid result till here. + // A/AAAA request can check for an alternative entry like AF_UNSPEC. + // Otherwise we need to start a new query. + } else if (!rec->mResolving) { + // If this is an IPV4 or IPV6 specific request, check if there is + // an AF_UNSPEC entry we can use. Otherwise, hit the resolver... + if (addrRec && !(flags & RES_BYPASS_CACHE) && + ((af == PR_AF_INET) || (af == PR_AF_INET6))) { + // Check for an AF_UNSPEC entry. + + const nsHostKey unspecKey( + host, aTrrServer, nsIDNSService::RESOLVE_TYPE_DEFAULT, flags, + PR_AF_UNSPEC, (aOriginAttributes.mPrivateBrowsingId > 0), + originSuffix); + RefPtr<nsHostRecord> unspecRec = mRecordDB.Get(unspecKey); + + TimeStamp now = TimeStamp::NowLoRes(); + if (unspecRec && unspecRec->HasUsableResult(now, flags)) { + MOZ_ASSERT(unspecRec->IsAddrRecord()); + + RefPtr<AddrHostRecord> addrUnspecRec = do_QueryObject(unspecRec); + MOZ_ASSERT(addrUnspecRec); + MOZ_ASSERT(addrUnspecRec->addr_info || addrUnspecRec->negative, + "Entry should be resolved or negative."); + + LOG((" Trying AF_UNSPEC entry for host [%s] af: %s.\n", host.get(), + (af == PR_AF_INET) ? "AF_INET" : "AF_INET6")); + + // We need to lock in case any other thread is reading + // addr_info. + MutexAutoLock lock(addrRec->addr_info_lock); + + addrRec->addr_info = nullptr; + addrRec->addr_info_gencnt++; + if (unspecRec->negative) { + rec->negative = unspecRec->negative; + rec->CopyExpirationTimesAndFlagsFrom(unspecRec); + } else if (addrUnspecRec->addr_info) { + MutexAutoLock lock(addrUnspecRec->addr_info_lock); + if (addrUnspecRec->addr_info) { + // Search for any valid address in the AF_UNSPEC entry + // in the cache (not blocklisted and from the right + // family). + nsTArray<NetAddr> addresses; + for (const auto& addr : addrUnspecRec->addr_info->Addresses()) { + if ((af == addr.inet.family) && + !addrUnspecRec->Blocklisted(&addr)) { + addresses.AppendElement(addr); + } + } + if (!addresses.IsEmpty()) { + addrRec->addr_info = new AddrInfo( + addrUnspecRec->addr_info->Hostname(), + addrUnspecRec->addr_info->CanonicalHostname(), + addrUnspecRec->addr_info->IsTRR(), std::move(addresses)); + addrRec->addr_info_gencnt++; + rec->CopyExpirationTimesAndFlagsFrom(unspecRec); + } + } + } + // Now check if we have a new record. + if (rec->HasUsableResult(now, flags)) { + result = rec; + if (rec->negative) { + status = NS_ERROR_UNKNOWN_HOST; + } + Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT); + ConditionallyRefreshRecord(rec, host); + } else if (af == PR_AF_INET6) { + // For AF_INET6, a new lookup means another AF_UNSPEC + // lookup. We have already iterated through the + // AF_UNSPEC addresses, so we mark this record as + // negative. + LOG( + (" No AF_INET6 in AF_UNSPEC entry: " + "host [%s] unknown host.", + host.get())); + result = rec; + rec->negative = true; + status = NS_ERROR_UNKNOWN_HOST; + Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, + METHOD_NEGATIVE_HIT); + } + } + } + + // If this is a by-type request or if no valid record was found + // in the cache or this is an AF_UNSPEC request, then start a + // new lookup. + if (!result) { + LOG((" No usable record in cache for host [%s] type %d.", host.get(), + type)); + + if (flags & RES_REFRESH_CACHE) { + rec->Invalidate(); + } + + // Add callback to the list of pending callbacks. + rec->mCallbacks.insertBack(callback); + rec->flags = flags; + rv = NameLookup(rec); + if (IS_ADDR_TYPE(type)) { + Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, + METHOD_NETWORK_FIRST); + } + if (NS_FAILED(rv) && callback->isInList()) { + callback->remove(); + } else { + LOG( + (" DNS lookup for host [%s] blocking " + "pending 'getaddrinfo' or trr query: " + "callback [%p]", + host.get(), callback.get())); + } + } + } else { + LOG( + (" Host [%s] is being resolved. Appending callback " + "[%p].", + host.get(), callback.get())); + + rec->mCallbacks.insertBack(callback); + + // Only A/AAAA records are place in a queue. The queues are for + // the native resolver, therefore by-type request are never put + // into a queue. + if (addrRec && addrRec->onQueue()) { + Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, + METHOD_NETWORK_SHARED); + + // Consider the case where we are on a pending queue of + // lower priority than the request is being made at. + // In that case we should upgrade to the higher queue. + + if (IsHighPriority(flags) && !IsHighPriority(rec->flags)) { + // Move from (low|med) to high. + rec->remove(); + mHighQ.insertBack(rec); + rec->flags = flags; + ConditionallyCreateThread(rec); + } else if (IsMediumPriority(flags) && IsLowPriority(rec->flags)) { + // Move from low to med. + rec->remove(); + mMediumQ.insertBack(rec); + rec->flags = flags; + mIdleTaskCV.Notify(); + } + } + } + + if (result && callback->isInList()) { + callback->remove(); + } + } // lock + + if (result) { + callback->OnResolveHostComplete(this, result, status); + } + + return rv; +} + +void nsHostResolver::DetachCallback( + const nsACString& host, const nsACString& aTrrServer, uint16_t aType, + const OriginAttributes& aOriginAttributes, uint16_t flags, uint16_t af, + nsResolveHostCallback* aCallback, nsresult status) { + RefPtr<nsHostRecord> rec; + RefPtr<nsResolveHostCallback> callback(aCallback); + + { + MutexAutoLock lock(mLock); + + nsAutoCString originSuffix; + aOriginAttributes.CreateSuffix(originSuffix); + + nsHostKey key(host, aTrrServer, aType, flags, af, + (aOriginAttributes.mPrivateBrowsingId > 0), originSuffix); + RefPtr<nsHostRecord> entry = mRecordDB.Get(key); + if (entry) { + // walk list looking for |callback|... we cannot assume + // that it will be there! + + for (nsResolveHostCallback* c : entry->mCallbacks) { + if (c == callback) { + rec = entry; + c->remove(); + break; + } + } + } + } + + // complete callback with the given status code; this would only be done if + // the record was in the process of being resolved. + if (rec) { + callback->OnResolveHostComplete(this, rec, status); + } +} + +nsresult nsHostResolver::ConditionallyCreateThread(nsHostRecord* rec) { + if (mNumIdleTasks) { + // wake up idle tasks to process this lookup + mIdleTaskCV.Notify(); + } else if ((mActiveTaskCount < HighThreadThreshold) || + (IsHighPriority(rec->flags) && + mActiveTaskCount < MAX_RESOLVER_THREADS)) { + nsCOMPtr<nsIRunnable> event = mozilla::NewRunnableMethod( + "nsHostResolver::ThreadFunc", this, &nsHostResolver::ThreadFunc); + mActiveTaskCount++; + nsresult rv = + mResolverThreads->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL); + if (NS_FAILED(rv)) { + mActiveTaskCount--; + } + } else { + LOG((" Unable to find a thread for looking up host [%s].\n", + rec->host.get())); + } + return NS_OK; +} + +nsresult nsHostResolver::TrrLookup_unlocked(nsHostRecord* rec, TRR* pushedTRR) { + MutexAutoLock lock(mLock); + return TrrLookup(rec, pushedTRR); +} + +void nsHostResolver::MaybeRenewHostRecord(nsHostRecord* aRec) { + MutexAutoLock lock(mLock); + MaybeRenewHostRecordLocked(aRec); +} + +void nsHostResolver::MaybeRenewHostRecordLocked(nsHostRecord* aRec) { + mLock.AssertCurrentThreadOwns(); + if (aRec->isInList()) { + // we're already on the eviction queue. This is a renewal + MOZ_ASSERT(mEvictionQSize); + AssertOnQ(aRec, mEvictionQ); + aRec->remove(); + mEvictionQSize--; + } +} + +// returns error if no TRR resolve is issued +// it is impt this is not called while a native lookup is going on +nsresult nsHostResolver::TrrLookup(nsHostRecord* aRec, TRR* pushedTRR) { + if (Mode() == MODE_TRROFF || StaticPrefs::network_dns_disabled()) { + return NS_ERROR_UNKNOWN_HOST; + } + LOG(("TrrLookup host:%s af:%" PRId16, aRec->host.get(), aRec->af)); + + RefPtr<nsHostRecord> rec(aRec); + mLock.AssertCurrentThreadOwns(); + + RefPtr<AddrHostRecord> addrRec; + RefPtr<TypeHostRecord> typeRec; + + if (rec->IsAddrRecord()) { + addrRec = do_QueryObject(rec); + MOZ_ASSERT(addrRec); + } else { + typeRec = do_QueryObject(rec); + MOZ_ASSERT(typeRec); + } + + MOZ_ASSERT(!rec->mResolving); + + auto hasConnectivity = [this]() -> bool { + if (!mNCS) { + return true; + } + nsINetworkConnectivityService::ConnectivityState ipv4 = mNCS->GetIPv4(); + nsINetworkConnectivityService::ConnectivityState ipv6 = mNCS->GetIPv6(); + + if (ipv4 == nsINetworkConnectivityService::OK || + ipv6 == nsINetworkConnectivityService::OK) { + return true; + } + + if (ipv4 == nsINetworkConnectivityService::UNKNOWN || + ipv6 == nsINetworkConnectivityService::UNKNOWN) { + // One of the checks hasn't completed yet. Optimistically assume we'll + // have network connectivity. + return true; + } + + return false; + }; + + nsIRequest::TRRMode reqMode = rec->mEffectiveTRRMode; + if (rec->mTrrServer.IsEmpty() && + (!gTRRService || !gTRRService->Enabled(reqMode))) { + if (NS_IsOffline()) { + // If we are in the NOT_CONFIRMED state _because_ we lack connectivity, + // then we should report that the browser is offline instead. + rec->RecordReason(nsHostRecord::TRR_IS_OFFLINE); + } + if (!hasConnectivity()) { + rec->RecordReason(nsHostRecord::TRR_NO_CONNECTIVITY); + } else { + rec->RecordReason(nsHostRecord::TRR_NOT_CONFIRMED); + } + + LOG(("TrrLookup:: %s service not enabled\n", rec->host.get())); + return NS_ERROR_UNKNOWN_HOST; + } + + MaybeRenewHostRecordLocked(rec); + + RefPtr<TRRQuery> query = new TRRQuery(this, rec); + nsresult rv = query->DispatchLookup(pushedTRR); + if (NS_FAILED(rv)) { + rec->RecordReason(nsHostRecord::TRR_DID_NOT_MAKE_QUERY); + return rv; + } + + { + auto lock = rec->mTRRQuery.Lock(); + MOZ_ASSERT(!lock.ref(), "TRR already in progress"); + lock.ref() = query; + } + + rec->mResolving++; + return NS_OK; +} + +void nsHostResolver::AssertOnQ(nsHostRecord* rec, + LinkedList<RefPtr<nsHostRecord>>& q) { +#ifdef DEBUG + MOZ_ASSERT(!q.isEmpty()); + MOZ_ASSERT(rec->isInList()); + for (const RefPtr<nsHostRecord>& r : q) { + if (rec == r) { + return; + } + } + MOZ_ASSERT(false, "Did not find element"); +#endif +} + +nsresult nsHostResolver::NativeLookup(nsHostRecord* aRec) { + if (StaticPrefs::network_dns_disabled()) { + return NS_ERROR_UNKNOWN_HOST; + } + LOG(("NativeLookup host:%s af:%" PRId16, aRec->host.get(), aRec->af)); + + // Only A/AAAA request are resolve natively. + MOZ_ASSERT(aRec->IsAddrRecord()); + mLock.AssertCurrentThreadOwns(); + + RefPtr<nsHostRecord> rec(aRec); + RefPtr<AddrHostRecord> addrRec; + addrRec = do_QueryObject(rec); + MOZ_ASSERT(addrRec); + + addrRec->mNativeStart = TimeStamp::Now(); + + // Add rec to one of the pending queues, possibly removing it from mEvictionQ. + MaybeRenewHostRecordLocked(rec); + + switch (AddrHostRecord::GetPriority(rec->flags)) { + case AddrHostRecord::DNS_PRIORITY_HIGH: + mHighQ.insertBack(rec); + break; + + case AddrHostRecord::DNS_PRIORITY_MEDIUM: + mMediumQ.insertBack(rec); + break; + + case AddrHostRecord::DNS_PRIORITY_LOW: + mLowQ.insertBack(rec); + break; + } + mPendingCount++; + + addrRec->StoreNative(true); + addrRec->StoreNativeUsed(true); + addrRec->mResolving++; + + nsresult rv = ConditionallyCreateThread(rec); + + LOG((" DNS thread counters: total=%d any-live=%d idle=%d pending=%d\n", + static_cast<uint32_t>(mActiveTaskCount), + static_cast<uint32_t>(mActiveAnyThreadCount), + static_cast<uint32_t>(mNumIdleTasks), + static_cast<uint32_t>(mPendingCount))); + + return rv; +} + +// static +ResolverMode nsHostResolver::Mode() { + if (gTRRService) { + return static_cast<ResolverMode>(gTRRService->Mode()); + } + + // If we don't have a TRR service just return MODE_TRROFF so we don't make + // any TRR requests by mistake. + return MODE_TRROFF; +} + +nsIRequest::TRRMode nsHostRecord::TRRMode() { + return nsIDNSService::GetTRRModeFromFlags(flags); +} + +// static +void nsHostResolver::ComputeEffectiveTRRMode(nsHostRecord* aRec) { + ResolverMode resolverMode = nsHostResolver::Mode(); + nsIRequest::TRRMode requestMode = aRec->TRRMode(); + + // For domains that are excluded from TRR or when parental control is enabled, + // we fallback to NativeLookup. This happens even in MODE_TRRONLY. By default + // localhost and local are excluded (so we cover *.local hosts) See the + // network.trr.excluded-domains pref. + + if (!gTRRService) { + aRec->RecordReason(nsHostRecord::TRR_NO_GSERVICE); + aRec->mEffectiveTRRMode = requestMode; + return; + } + + if (!aRec->mTrrServer.IsEmpty()) { + aRec->mEffectiveTRRMode = nsIRequest::TRR_ONLY_MODE; + return; + } + + if (gTRRService->IsExcludedFromTRR(aRec->host)) { + aRec->RecordReason(nsHostRecord::TRR_EXCLUDED); + aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE; + return; + } + + if (StaticPrefs::network_dns_skipTRR_when_parental_control_enabled() && + gTRRService->ParentalControlEnabled()) { + aRec->RecordReason(nsHostRecord::TRR_PARENTAL_CONTROL); + aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE; + return; + } + + if (resolverMode == MODE_TRROFF) { + aRec->RecordReason(nsHostRecord::TRR_OFF_EXPLICIT); + aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE; + return; + } + + if (requestMode == nsIRequest::TRR_DISABLED_MODE) { + aRec->RecordReason(nsHostRecord::TRR_REQ_MODE_DISABLED); + aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE; + return; + } + + if ((requestMode == nsIRequest::TRR_DEFAULT_MODE && + resolverMode == MODE_NATIVEONLY)) { + aRec->RecordReason(nsHostRecord::TRR_MODE_NOT_ENABLED); + aRec->mEffectiveTRRMode = nsIRequest::TRR_DISABLED_MODE; + return; + } + + if (requestMode == nsIRequest::TRR_DEFAULT_MODE && + resolverMode == MODE_TRRFIRST) { + aRec->mEffectiveTRRMode = nsIRequest::TRR_FIRST_MODE; + return; + } + + if (requestMode == nsIRequest::TRR_DEFAULT_MODE && + resolverMode == MODE_TRRONLY) { + aRec->mEffectiveTRRMode = nsIRequest::TRR_ONLY_MODE; + return; + } + + aRec->mEffectiveTRRMode = requestMode; +} + +// Kick-off a name resolve operation, using native resolver and/or TRR +nsresult nsHostResolver::NameLookup(nsHostRecord* rec) { + LOG(("NameLookup host:%s af:%" PRId16, rec->host.get(), rec->af)); + + if (rec->flags & RES_IP_HINT) { + LOG(("Skip lookup if RES_IP_HINT is set\n")); + return NS_ERROR_UNKNOWN_HOST; + } + + nsresult rv = NS_ERROR_UNKNOWN_HOST; + if (rec->mResolving) { + LOG(("NameLookup %s while already resolving\n", rec->host.get())); + return NS_OK; + } + + // Make sure we reset the reason each time we attempt to do a new lookup + // so we don't wronly report the reason for the previous one. + rec->mTRRTRRSkippedReason = nsHostRecord::TRR_UNSET; + + ComputeEffectiveTRRMode(rec); + + if (rec->IsAddrRecord()) { + RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec); + MOZ_ASSERT(addrRec); + + addrRec->StoreNativeUsed(false); + addrRec->mTRRUsed = false; + addrRec->mNativeSuccess = false; + } + + if (!rec->mTrrServer.IsEmpty()) { + LOG(("NameLookup: %s use trr:%s", rec->host.get(), rec->mTrrServer.get())); + if (rec->mEffectiveTRRMode != nsIRequest::TRR_ONLY_MODE) { + return NS_ERROR_UNKNOWN_HOST; + } + + if (rec->flags & RES_DISABLE_TRR) { + LOG(("TRR with server and DISABLE_TRR flag. Returning error.")); + return NS_ERROR_UNKNOWN_HOST; + } + return TrrLookup(rec); + } + + LOG(("NameLookup: %s effectiveTRRmode: %d flags: %X", rec->host.get(), + rec->mEffectiveTRRMode, rec->flags)); + + if (rec->flags & RES_DISABLE_TRR) { + rec->RecordReason(nsHostRecord::TRR_DISABLED_FLAG); + } + + if (rec->mEffectiveTRRMode != nsIRequest::TRR_DISABLED_MODE && + !((rec->flags & RES_DISABLE_TRR))) { + rv = TrrLookup(rec); + } + + bool serviceNotReady = !gTRRService || !gTRRService->IsConfirmed(); + + if (rec->mEffectiveTRRMode == nsIRequest::TRR_DISABLED_MODE || + (rec->mEffectiveTRRMode == nsIRequest::TRR_FIRST_MODE && + (rec->flags & RES_DISABLE_TRR || serviceNotReady || NS_FAILED(rv)))) { + if (!rec->IsAddrRecord()) { + return rv; + } + +#ifdef DEBUG + // If we use this branch then the mTRRUsed flag should not be set + // Even if we did call TrrLookup above, the fact that it failed sync-ly + // means that we didn't actually succeed in opening the channel. + RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec); + MOZ_ASSERT(addrRec && !addrRec->mTRRUsed); +#endif + + rv = NativeLookup(rec); + } + + return rv; +} + +nsresult nsHostResolver::ConditionallyRefreshRecord(nsHostRecord* rec, + const nsACString& host) { + if ((rec->CheckExpiration(TimeStamp::NowLoRes()) != nsHostRecord::EXP_VALID || + rec->negative) && + !rec->mResolving) { + LOG((" Using %s cache entry for host [%s] but starting async renewal.", + rec->negative ? "negative" : "positive", host.BeginReading())); + NameLookup(rec); + + if (rec->IsAddrRecord() && !rec->negative) { + // negative entries are constantly being refreshed, only + // track positive grace period induced renewals + Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_RENEWAL); + } + } + return NS_OK; +} + +void nsHostResolver::DeQueue(LinkedList<RefPtr<nsHostRecord>>& aQ, + AddrHostRecord** aResult) { + RefPtr<nsHostRecord> rec = aQ.popFirst(); + mPendingCount--; + MOZ_ASSERT(rec->IsAddrRecord()); + RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec); + MOZ_ASSERT(addrRec); + addrRec.forget(aResult); +} + +bool nsHostResolver::GetHostToLookup(AddrHostRecord** result) { + bool timedOut = false; + TimeDuration timeout; + TimeStamp epoch, now; + + MutexAutoLock lock(mLock); + + timeout = (mNumIdleTasks >= HighThreadThreshold) ? mShortIdleTimeout + : mLongIdleTimeout; + epoch = TimeStamp::Now(); + + while (!mShutdown) { + // remove next record from Q; hand over owning reference. Check high, then + // med, then low + +#define SET_GET_TTL(var, val) (var)->StoreGetTtl(sGetTtlEnabled && (val)) + + if (!mHighQ.isEmpty()) { + DeQueue(mHighQ, result); + SET_GET_TTL(*result, false); + return true; + } + + if (mActiveAnyThreadCount < HighThreadThreshold) { + if (!mMediumQ.isEmpty()) { + DeQueue(mMediumQ, result); + mActiveAnyThreadCount++; + (*result)->StoreUsingAnyThread(true); + SET_GET_TTL(*result, true); + return true; + } + + if (!mLowQ.isEmpty()) { + DeQueue(mLowQ, result); + mActiveAnyThreadCount++; + (*result)->StoreUsingAnyThread(true); + SET_GET_TTL(*result, true); + return true; + } + } + + // Determining timeout is racy, so allow one cycle through checking the + // queues before exiting. + if (timedOut) { + break; + } + + // wait for one or more of the following to occur: + // (1) the pending queue has a host record to process + // (2) the shutdown flag has been set + // (3) the thread has been idle for too long + + mNumIdleTasks++; + mIdleTaskCV.Wait(timeout); + mNumIdleTasks--; + + now = TimeStamp::Now(); + + if (now - epoch >= timeout) { + timedOut = true; + } else { + // It is possible that CondVar::Wait() was interrupted and returned + // early, in which case we will loop back and re-enter it. In that + // case we want to do so with the new timeout reduced to reflect + // time already spent waiting. + timeout -= now - epoch; + epoch = now; + } + } + + // tell thread to exit... + return false; +} + +void nsHostResolver::PrepareRecordExpirationAddrRecord( + AddrHostRecord* rec) const { + // NOTE: rec->addr_info_lock is already held by parent + MOZ_ASSERT(((bool)rec->addr_info) != rec->negative); + mLock.AssertCurrentThreadOwns(); + if (!rec->addr_info) { + rec->SetExpiration(TimeStamp::NowLoRes(), NEGATIVE_RECORD_LIFETIME, 0); + LOG(("Caching host [%s] negative record for %u seconds.\n", rec->host.get(), + NEGATIVE_RECORD_LIFETIME)); + return; + } + + unsigned int lifetime = mDefaultCacheLifetime; + unsigned int grace = mDefaultGracePeriod; + + unsigned int ttl = mDefaultCacheLifetime; + if (sGetTtlEnabled || rec->addr_info->IsTRR()) { + if (rec->addr_info && rec->addr_info->TTL() != AddrInfo::NO_TTL_DATA) { + ttl = rec->addr_info->TTL(); + } + lifetime = ttl; + grace = 0; + } + + rec->SetExpiration(TimeStamp::NowLoRes(), lifetime, grace); + LOG(("Caching host [%s] record for %u seconds (grace %d).", rec->host.get(), + lifetime, grace)); +} + +static bool different_rrset(AddrInfo* rrset1, AddrInfo* rrset2) { + if (!rrset1 || !rrset2) { + return true; + } + + LOG(("different_rrset %s\n", rrset1->Hostname().get())); + + if (rrset1->IsTRR() != rrset2->IsTRR()) { + return true; + } + + if (rrset1->Addresses().Length() != rrset2->Addresses().Length()) { + LOG(("different_rrset true due to length change\n")); + return true; + } + + nsTArray<NetAddr> orderedSet1 = rrset1->Addresses().Clone(); + nsTArray<NetAddr> orderedSet2 = rrset2->Addresses().Clone(); + orderedSet1.Sort(); + orderedSet2.Sort(); + + bool eq = orderedSet1 == orderedSet2; + if (!eq) { + LOG(("different_rrset true due to content change\n")); + } else { + LOG(("different_rrset false\n")); + } + return !eq; +} + +void nsHostResolver::AddToEvictionQ(nsHostRecord* rec) { + if (rec->isInList()) { + MOZ_DIAGNOSTIC_ASSERT(!mEvictionQ.contains(rec), + "Already in eviction queue"); + MOZ_DIAGNOSTIC_ASSERT(!mHighQ.contains(rec), "Already in high queue"); + MOZ_DIAGNOSTIC_ASSERT(!mMediumQ.contains(rec), "Already in med queue"); + MOZ_DIAGNOSTIC_ASSERT(!mLowQ.contains(rec), "Already in low queue"); + MOZ_DIAGNOSTIC_ASSERT(false, "Already on some other queue?"); + + // Bug 1678117 - it's not clear why this can happen, but let's fix it + // for release users. + rec->remove(); + } + mEvictionQ.insertBack(rec); + if (mEvictionQSize < mMaxCacheEntries) { + mEvictionQSize++; + } else { + // remove first element on mEvictionQ + RefPtr<nsHostRecord> head = mEvictionQ.popFirst(); + mRecordDB.Remove(*static_cast<nsHostKey*>(head.get())); + + if (!head->negative) { + // record the age of the entry upon eviction. + TimeDuration age = TimeStamp::NowLoRes() - head->mValidStart; + if (rec->IsAddrRecord()) { + Telemetry::Accumulate(Telemetry::DNS_CLEANUP_AGE, + static_cast<uint32_t>(age.ToSeconds() / 60)); + } else { + Telemetry::Accumulate(Telemetry::DNS_BY_TYPE_CLEANUP_AGE, + static_cast<uint32_t>(age.ToSeconds() / 60)); + } + if (head->CheckExpiration(TimeStamp::Now()) != + nsHostRecord::EXP_EXPIRED) { + if (rec->IsAddrRecord()) { + Telemetry::Accumulate(Telemetry::DNS_PREMATURE_EVICTION, + static_cast<uint32_t>(age.ToSeconds() / 60)); + } else { + Telemetry::Accumulate(Telemetry::DNS_BY_TYPE_PREMATURE_EVICTION, + static_cast<uint32_t>(age.ToSeconds() / 60)); + } + } + } + } +} + +// +// CompleteLookup() checks if the resolving should be redone and if so it +// returns LOOKUP_RESOLVEAGAIN, but only if 'status' is not NS_ERROR_ABORT. +nsHostResolver::LookupStatus nsHostResolver::CompleteLookup( + nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb, + const nsACString& aOriginsuffix, nsHostRecord::TRRSkippedReason aReason) { + MutexAutoLock lock(mLock); + MOZ_ASSERT(rec); + MOZ_ASSERT(rec->pb == pb); + MOZ_ASSERT(rec->IsAddrRecord()); + + RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec); + MOZ_ASSERT(addrRec); + + RefPtr<AddrInfo> newRRSet(aNewRRSet); + MOZ_ASSERT(NS_FAILED(status) || newRRSet->Addresses().Length() > 0); + + bool trrResult = newRRSet && newRRSet->IsTRR(); + if (NS_FAILED(status)) { + newRRSet = nullptr; + } + + if (addrRec->LoadResolveAgain() && (status != NS_ERROR_ABORT) && !trrResult) { + LOG(("nsHostResolver record %p resolve again due to flushcache\n", + addrRec.get())); + addrRec->StoreResolveAgain(false); + return LOOKUP_RESOLVEAGAIN; + } + + MOZ_ASSERT(addrRec->mResolving); + addrRec->mResolving--; + LOG(("nsHostResolver::CompleteLookup %s %p %X trr=%d stillResolving=%d\n", + addrRec->host.get(), aNewRRSet, (unsigned int)status, + aNewRRSet ? aNewRRSet->IsTRR() : 0, int(addrRec->mResolving))); + + if (trrResult) { + if (NS_FAILED(status) && status != NS_ERROR_UNKNOWN_HOST && + status != NS_ERROR_DEFINITIVE_UNKNOWN_HOST) { + // the errors are not failed resolves, that means + // something else failed, consider this as *TRR not used* + // for actually trying to resolve the host + addrRec->mTRRUsed = false; + } else { + addrRec->mTRRUsed = true; + } + + if (NS_FAILED(status)) { + if (aReason != nsHostRecord::TRR_UNSET) { + addrRec->RecordReason(aReason); + } else { + // Unknown failed reason. + addrRec->RecordReason(nsHostRecord::TRR_FAILED); + } + } else { + addrRec->mTRRSuccess++; + addrRec->RecordReason(nsHostRecord::TRR_OK); + } + + if (NS_FAILED(status) && + addrRec->mEffectiveTRRMode == nsIRequest::TRR_FIRST_MODE && + status != NS_ERROR_DEFINITIVE_UNKNOWN_HOST) { + MOZ_ASSERT(!addrRec->mResolving); + NativeLookup(addrRec); + MOZ_ASSERT(addrRec->mResolving); + return LOOKUP_OK; + } + + if (!addrRec->mTRRSuccess) { + // no TRR success + newRRSet = nullptr; + } + + if (NS_FAILED(status)) { + // This is the error that consumers expect. + status = NS_ERROR_UNKNOWN_HOST; + } + } else { // native resolve completed + if (addrRec->LoadUsingAnyThread()) { + mActiveAnyThreadCount--; + addrRec->StoreUsingAnyThread(false); + } + + addrRec->mNativeSuccess = static_cast<bool>(newRRSet); + if (addrRec->mNativeSuccess) { + addrRec->mNativeDuration = TimeStamp::Now() - addrRec->mNativeStart; + } + } + + // This should always be cleared when a request is completed. + addrRec->StoreNative(false); + + // update record fields. We might have a addrRec->addr_info already if a + // previous lookup result expired and we're reresolving it or we get + // a late second TRR response. + if (!mShutdown) { + MutexAutoLock lock(addrRec->addr_info_lock); + RefPtr<AddrInfo> old_addr_info; + if (different_rrset(addrRec->addr_info, newRRSet)) { + LOG(("nsHostResolver record %p new gencnt\n", addrRec.get())); + old_addr_info = addrRec->addr_info; + addrRec->addr_info = std::move(newRRSet); + addrRec->addr_info_gencnt++; + } else { + if (addrRec->addr_info && newRRSet) { + auto builder = addrRec->addr_info->Build(); + builder.SetTTL(newRRSet->TTL()); + // Update trr timings + builder.SetTrrFetchDuration(newRRSet->GetTrrFetchDuration()); + builder.SetTrrFetchDurationNetworkOnly( + newRRSet->GetTrrFetchDurationNetworkOnly()); + + addrRec->addr_info = builder.Finish(); + } + old_addr_info = std::move(newRRSet); + } + addrRec->negative = !addrRec->addr_info; + PrepareRecordExpirationAddrRecord(addrRec); + } + + if (LOG_ENABLED()) { + MutexAutoLock lock(addrRec->addr_info_lock); + if (addrRec->addr_info) { + for (const auto& elem : addrRec->addr_info->Addresses()) { + char buf[128]; + elem.ToStringBuffer(buf, sizeof(buf)); + LOG(("CompleteLookup: %s has %s\n", addrRec->host.get(), buf)); + } + } else { + LOG(("CompleteLookup: %s has NO address\n", addrRec->host.get())); + } + } + + // get the list of pending callbacks for this lookup, and notify + // them that the lookup is complete. + mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs = + std::move(rec->mCallbacks); + + LOG(("nsHostResolver record %p calling back dns users status:%X\n", + addrRec.get(), int(status))); + + for (nsResolveHostCallback* c = cbs.getFirst(); c; + c = c->removeAndGetNext()) { + c->OnResolveHostComplete(this, rec, status); + } + + if (!addrRec->mResolving && !mShutdown) { + { + auto trrQuery = addrRec->mTRRQuery.Lock(); + if (trrQuery.ref()) { + addrRec->mTrrDuration = trrQuery.ref()->Duration(); + } + trrQuery.ref() = nullptr; + } + addrRec->ResolveComplete(); + + AddToEvictionQ(rec); + } + +#ifdef DNSQUERY_AVAILABLE + // Unless the result is from TRR, resolve again to get TTL + bool hasNativeResult = false; + { + MutexAutoLock lock(addrRec->addr_info_lock); + if (addrRec->addr_info && !addrRec->addr_info->IsTRR()) { + hasNativeResult = true; + } + } + if (hasNativeResult && !mShutdown && !addrRec->LoadGetTtl() && + !rec->mResolving && sGetTtlEnabled) { + LOG(("Issuing second async lookup for TTL for host [%s].", + addrRec->host.get())); + addrRec->flags = (addrRec->flags & ~RES_PRIORITY_MEDIUM) | RES_PRIORITY_LOW; + DebugOnly<nsresult> rv = NativeLookup(rec); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "Could not issue second async lookup for TTL."); + } +#endif + return LOOKUP_OK; +} + +nsHostResolver::LookupStatus nsHostResolver::CompleteLookupByType( + nsHostRecord* rec, nsresult status, + mozilla::net::TypeRecordResultType& aResult, uint32_t aTtl, bool pb) { + MutexAutoLock lock(mLock); + MOZ_ASSERT(rec); + MOZ_ASSERT(rec->pb == pb); + MOZ_ASSERT(!rec->IsAddrRecord()); + + RefPtr<TypeHostRecord> typeRec = do_QueryObject(rec); + MOZ_ASSERT(typeRec); + + MOZ_ASSERT(typeRec->mResolving); + typeRec->mResolving--; + + { + auto lock = rec->mTRRQuery.Lock(); + lock.ref() = nullptr; + } + + uint32_t duration = static_cast<uint32_t>( + (TimeStamp::Now() - typeRec->mStart).ToMilliseconds()); + + if (NS_FAILED(status)) { + LOG(("nsHostResolver::CompleteLookupByType record %p [%s] status %x\n", + typeRec.get(), typeRec->host.get(), (unsigned int)status)); + typeRec->SetExpiration(TimeStamp::NowLoRes(), NEGATIVE_RECORD_LIFETIME, 0); + MOZ_ASSERT(aResult.is<TypeRecordEmpty>()); + status = NS_ERROR_UNKNOWN_HOST; + typeRec->negative = true; + Telemetry::Accumulate(Telemetry::DNS_BY_TYPE_FAILED_LOOKUP_TIME, duration); + } else { + size_t recordCount = 0; + if (aResult.is<TypeRecordTxt>()) { + recordCount = aResult.as<TypeRecordTxt>().Length(); + } else if (aResult.is<TypeRecordHTTPSSVC>()) { + recordCount = aResult.as<TypeRecordHTTPSSVC>().Length(); + } + LOG( + ("nsHostResolver::CompleteLookupByType record %p [%s], number of " + "records %zu\n", + typeRec.get(), typeRec->host.get(), recordCount)); + MutexAutoLock typeLock(typeRec->mResultsLock); + typeRec->mResults = aResult; + typeRec->SetExpiration(TimeStamp::NowLoRes(), aTtl, mDefaultGracePeriod); + typeRec->negative = false; + Telemetry::Accumulate(Telemetry::DNS_BY_TYPE_SUCCEEDED_LOOKUP_TIME, + duration); + } + + mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs = + std::move(typeRec->mCallbacks); + + LOG( + ("nsHostResolver::CompleteLookupByType record %p calling back dns " + "users\n", + typeRec.get())); + + for (nsResolveHostCallback* c = cbs.getFirst(); c; + c = c->removeAndGetNext()) { + c->OnResolveHostComplete(this, rec, status); + } + + AddToEvictionQ(rec); + return LOOKUP_OK; +} + +void nsHostResolver::CancelAsyncRequest( + const nsACString& host, const nsACString& aTrrServer, uint16_t aType, + const OriginAttributes& aOriginAttributes, uint16_t flags, uint16_t af, + nsIDNSListener* aListener, nsresult status) + +{ + MutexAutoLock lock(mLock); + + nsAutoCString originSuffix; + aOriginAttributes.CreateSuffix(originSuffix); + + // Lookup the host record associated with host, flags & address family + + nsHostKey key(host, aTrrServer, aType, flags, af, + (aOriginAttributes.mPrivateBrowsingId > 0), originSuffix); + RefPtr<nsHostRecord> rec = mRecordDB.Get(key); + if (rec) { + nsHostRecord* recPtr = nullptr; + + for (const RefPtr<nsResolveHostCallback>& c : rec->mCallbacks) { + if (c->EqualsAsyncListener(aListener)) { + c->remove(); + recPtr = rec; + c->OnResolveHostComplete(this, recPtr, status); + break; + } + } + + // If there are no more callbacks, remove the hash table entry + if (recPtr && recPtr->mCallbacks.isEmpty()) { + mRecordDB.Remove(*static_cast<nsHostKey*>(recPtr)); + // If record is on a Queue, remove it and then deref it + if (recPtr->isInList()) { + recPtr->remove(); + } + } + } +} + +size_t nsHostResolver::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const { + MutexAutoLock lock(mLock); + + size_t n = mallocSizeOf(this); + + n += mRecordDB.ShallowSizeOfExcludingThis(mallocSizeOf); + for (auto iter = mRecordDB.ConstIter(); !iter.Done(); iter.Next()) { + auto* entry = iter.UserData(); + n += entry->SizeOfIncludingThis(mallocSizeOf); + } + + // The following fields aren't measured. + // - mHighQ, mMediumQ, mLowQ, mEvictionQ, because they just point to + // nsHostRecords that also pointed to by entries |mRecordDB|, and + // measured when |mRecordDB| is measured. + + return n; +} + +void nsHostResolver::ThreadFunc() { + LOG(("DNS lookup thread - starting execution.\n")); + +#if defined(RES_RETRY_ON_FAILURE) + nsResState rs; +#endif + RefPtr<AddrHostRecord> rec; + RefPtr<AddrInfo> ai; + + do { + if (!rec) { + RefPtr<AddrHostRecord> tmpRec; + if (!GetHostToLookup(getter_AddRefs(tmpRec))) { + break; // thread shutdown signal + } + // GetHostToLookup() returns an owning reference + MOZ_ASSERT(tmpRec); + rec.swap(tmpRec); + } + + LOG1(("DNS lookup thread - Calling getaddrinfo for host [%s].\n", + rec->host.get())); + + TimeStamp startTime = TimeStamp::Now(); + bool getTtl = rec->LoadGetTtl(); + TimeDuration inQueue = startTime - rec->mNativeStart; + uint32_t ms = static_cast<uint32_t>(inQueue.ToMilliseconds()); + Telemetry::Accumulate(Telemetry::DNS_NATIVE_QUEUING, ms); + nsresult status = + GetAddrInfo(rec->host, rec->af, rec->flags, getter_AddRefs(ai), getTtl); +#if defined(RES_RETRY_ON_FAILURE) + if (NS_FAILED(status) && rs.Reset()) { + status = GetAddrInfo(rec->host, rec->af, rec->flags, getter_AddRefs(ai), + getTtl); + } +#endif + + { // obtain lock to check shutdown and manage inter-module telemetry + MutexAutoLock lock(mLock); + + if (!mShutdown) { + TimeDuration elapsed = TimeStamp::Now() - startTime; + uint32_t millis = static_cast<uint32_t>(elapsed.ToMilliseconds()); + + if (NS_SUCCEEDED(status)) { + Telemetry::HistogramID histogramID; + if (!rec->addr_info_gencnt) { + // Time for initial lookup. + histogramID = Telemetry::DNS_LOOKUP_TIME; + } else if (!getTtl) { + // Time for renewal; categorized by expiration strategy. + histogramID = Telemetry::DNS_RENEWAL_TIME; + } else { + // Time to get TTL; categorized by expiration strategy. + histogramID = Telemetry::DNS_RENEWAL_TIME_FOR_TTL; + } + Telemetry::Accumulate(histogramID, millis); + } else { + Telemetry::Accumulate(Telemetry::DNS_FAILED_LOOKUP_TIME, millis); + } + } + } + + LOG1(("DNS lookup thread - lookup completed for host [%s]: %s.\n", + rec->host.get(), ai ? "success" : "failure: unknown host")); + + if (LOOKUP_RESOLVEAGAIN == CompleteLookup(rec, status, ai, rec->pb, + rec->originSuffix, + rec->mTRRTRRSkippedReason)) { + // leave 'rec' assigned and loop to make a renewed host resolve + LOG(("DNS lookup thread - Re-resolving host [%s].\n", rec->host.get())); + } else { + rec = nullptr; + } + } while (true); + + mActiveTaskCount--; + LOG(("DNS lookup thread - queue empty, task finished.\n")); +} + +void nsHostResolver::SetCacheLimits(uint32_t aMaxCacheEntries, + uint32_t aDefaultCacheEntryLifetime, + uint32_t aDefaultGracePeriod) { + MutexAutoLock lock(mLock); + mMaxCacheEntries = aMaxCacheEntries; + mDefaultCacheLifetime = aDefaultCacheEntryLifetime; + mDefaultGracePeriod = aDefaultGracePeriod; +} + +nsresult nsHostResolver::Create(uint32_t maxCacheEntries, + uint32_t defaultCacheEntryLifetime, + uint32_t defaultGracePeriod, + nsHostResolver** result) { + RefPtr<nsHostResolver> res = new nsHostResolver( + maxCacheEntries, defaultCacheEntryLifetime, defaultGracePeriod); + + nsresult rv = res->Init(); + if (NS_FAILED(rv)) { + return rv; + } + + res.forget(result); + return NS_OK; +} + +void nsHostResolver::GetDNSCacheEntries(nsTArray<DNSCacheEntries>* args) { + MutexAutoLock lock(mLock); + for (auto iter = mRecordDB.Iter(); !iter.Done(); iter.Next()) { + // We don't pay attention to address literals, only resolved domains. + // Also require a host. + nsHostRecord* rec = iter.UserData(); + MOZ_ASSERT(rec, "rec should never be null here!"); + + if (!rec) { + continue; + } + + // For now we only show A/AAAA records. + if (!rec->IsAddrRecord()) { + continue; + } + + RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec); + MOZ_ASSERT(addrRec); + if (!addrRec || !addrRec->addr_info) { + continue; + } + + DNSCacheEntries info; + info.hostname = rec->host; + info.family = rec->af; + info.expiration = + (int64_t)(rec->mValidEnd - TimeStamp::NowLoRes()).ToSeconds(); + if (info.expiration <= 0) { + // We only need valid DNS cache entries + continue; + } + + { + MutexAutoLock lock(addrRec->addr_info_lock); + for (const auto& addr : addrRec->addr_info->Addresses()) { + char buf[kIPv6CStrBufSize]; + if (addr.ToStringBuffer(buf, sizeof(buf))) { + info.hostaddr.AppendElement(buf); + } + } + info.TRR = addrRec->addr_info->IsTRR(); + } + + info.originAttributesSuffix = iter.Key().originSuffix; + + args->AppendElement(std::move(info)); + } +} + +#undef LOG +#undef LOG_ENABLED diff --git a/netwerk/dns/nsHostResolver.h b/netwerk/dns/nsHostResolver.h new file mode 100644 index 0000000000..798d0d0065 --- /dev/null +++ b/netwerk/dns/nsHostResolver.h @@ -0,0 +1,654 @@ +/* vim:set ts=4 sw=2 sts=2 et cin: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsHostResolver_h__ +#define nsHostResolver_h__ + +#include "nscore.h" +#include "prnetdb.h" +#include "PLDHashTable.h" +#include "mozilla/CondVar.h" +#include "mozilla/DataMutex.h" +#include "mozilla/Mutex.h" +#include "nsISupportsImpl.h" +#include "nsIDNSListener.h" +#include "nsIDNSService.h" +#include "nsTArray.h" +#include "GetAddrInfo.h" +#include "mozilla/net/DNS.h" +#include "mozilla/net/DashboardTypes.h" +#include "mozilla/AtomicBitfields.h" +#include "mozilla/Atomics.h" +#include "mozilla/LinkedList.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/UniquePtr.h" +#include "nsRefPtrHashtable.h" +#include "nsIThreadPool.h" +#include "mozilla/net/NetworkConnectivityService.h" +#include "nsIDNSByTypeRecord.h" +#include "mozilla/net/DNSByTypeRecord.h" +#include "mozilla/Maybe.h" + +class nsHostResolver; +class nsResolveHostCallback; +namespace mozilla { +namespace net { +class TRR; +class TRRQuery; +enum ResolverMode { + MODE_NATIVEONLY, // 0 - TRR OFF (by default) + MODE_RESERVED1, // 1 - Reserved value. Used to be parallel resolve. + MODE_TRRFIRST, // 2 - fallback to native on TRR failure + MODE_TRRONLY, // 3 - don't even fallback + MODE_RESERVED4, // 4 - Reserved value. Used to be race TRR with native. + MODE_TRROFF // 5 - identical to MODE_NATIVEONLY but explicitly selected +}; +} // namespace net +} // namespace mozilla + +#define TRR_DISABLED(x) (((x) == MODE_NATIVEONLY) || ((x) == MODE_TRROFF)) + +extern mozilla::Atomic<bool, mozilla::Relaxed> gNativeIsLocalhost; + +#define MAX_RESOLVER_THREADS_FOR_ANY_PRIORITY 3 +#define MAX_RESOLVER_THREADS_FOR_HIGH_PRIORITY 5 +#define MAX_NON_PRIORITY_REQUESTS 150 + +#define MAX_RESOLVER_THREADS \ + (MAX_RESOLVER_THREADS_FOR_ANY_PRIORITY + \ + MAX_RESOLVER_THREADS_FOR_HIGH_PRIORITY) + +struct nsHostKey { + const nsCString host; + const nsCString mTrrServer; + uint16_t type; + uint16_t flags; + uint16_t af; + bool pb; + const nsCString originSuffix; + explicit nsHostKey(const nsACString& host, const nsACString& aTrrServer, + uint16_t type, uint16_t flags, uint16_t af, bool pb, + const nsACString& originSuffix); + bool operator==(const nsHostKey& other) const; + size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; + PLDHashNumber Hash() const; +}; + +/** + * nsHostRecord - ref counted object type stored in host resolver cache. + */ +class nsHostRecord : public mozilla::LinkedListElement<RefPtr<nsHostRecord>>, + public nsHostKey, + public nsISupports { + public: + NS_DECL_THREADSAFE_ISUPPORTS + + virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { + return 0; + } + + // Returns the TRR mode encoded by the flags + nsIRequest::TRRMode TRRMode(); + + // IMPORTANT: when adding new values, always add them to the end, otherwise + // it will mess up telemetry. + enum TRRSkippedReason : uint32_t { + TRR_UNSET = 0, + TRR_OK = 1, // Only set when we actually got a positive TRR result + TRR_NO_GSERVICE = 2, // no gService + TRR_PARENTAL_CONTROL = 3, // parental control is on + TRR_OFF_EXPLICIT = 4, // user has set mode5 + TRR_REQ_MODE_DISABLED = 5, // request has disabled flags set + TRR_MODE_NOT_ENABLED = 6, // mode0 + TRR_FAILED = 7, // unknown failure + TRR_MODE_UNHANDLED_DEFAULT = 8, // Unhandled case in ComputeEffectiveMode + TRR_MODE_UNHANDLED_DISABLED = 9, // Unhandled case in ComputeEffectiveMode + TRR_DISABLED_FLAG = 10, // the DISABLE_TRR flag was set + TRR_TIMEOUT = 11, // the TRR channel timed out + TRR_CHANNEL_DNS_FAIL = 12, // DoH server name failed to resolve + TRR_IS_OFFLINE = 13, // The browser is offline/no interfaces up + TRR_NOT_CONFIRMED = 14, // TRR confirmation is not done yet + TRR_DID_NOT_MAKE_QUERY = 15, // TrrLookup exited without doing a TRR query + TRR_UNKNOWN_CHANNEL_FAILURE = 16, // unknown channel failure reason + TRR_HOST_BLOCKED_TEMPORARY = 17, // host blocklisted + TRR_SEND_FAILED = 18, // The call to TRR::SendHTTPRequest failed + TRR_NET_RESET = 19, // NS_ERROR_NET_RESET + TRR_NET_TIMEOUT = 20, // NS_ERROR_NET_TIMEOUT + TRR_NET_REFUSED = 21, // NS_ERROR_CONNECTION_REFUSED + TRR_NET_INTERRUPT = 22, // NS_ERROR_NET_INTERRUPT + TRR_NET_INADEQ_SEQURITY = 23, // NS_ERROR_NET_INADEQUATE_SECURITY + TRR_NO_ANSWERS = 24, // TRR returned no answers + TRR_DECODE_FAILED = 25, // DohDecode failed + TRR_EXCLUDED = 26, // ExcludedFromTRR + TRR_SERVER_RESPONSE_ERR = 27, // Server responded with non-200 code + TRR_RCODE_FAIL = 28, // DNS response contains a non-NOERROR rcode + TRR_NO_CONNECTIVITY = 29, // Not confirmed because of no connectivity + }; + + // Records the first reason that caused TRR to be skipped or to fail. + void RecordReason(TRRSkippedReason reason) { + if (mTRRTRRSkippedReason == TRR_UNSET) { + mTRRTRRSkippedReason = reason; + } + } + + protected: + friend class nsHostResolver; + friend class mozilla::net::TRR; + friend class mozilla::net::TRRQuery; + + explicit nsHostRecord(const nsHostKey& key); + virtual ~nsHostRecord() = default; + + // Mark hostrecord as not usable + void Invalidate(); + + enum ExpirationStatus { + EXP_VALID, + EXP_GRACE, + EXP_EXPIRED, + }; + + ExpirationStatus CheckExpiration(const mozilla::TimeStamp& now) const; + + // Convenience function for setting the timestamps above (mValidStart, + // mValidEnd, and mGraceStart). valid and grace are durations in seconds. + void SetExpiration(const mozilla::TimeStamp& now, unsigned int valid, + unsigned int grace); + void CopyExpirationTimesAndFlagsFrom(const nsHostRecord* aFromHostRecord); + + // Checks if the record is usable (not expired and has a value) + bool HasUsableResult(const mozilla::TimeStamp& now, + uint16_t queryFlags = 0) const; + + enum DnsPriority { + DNS_PRIORITY_LOW, + DNS_PRIORITY_MEDIUM, + DNS_PRIORITY_HIGH, + }; + static DnsPriority GetPriority(uint16_t aFlags); + + virtual void Cancel(); + virtual bool HasUsableResultInternal() const { return false; } + + mozilla::LinkedList<RefPtr<nsResolveHostCallback>> mCallbacks; + + bool IsAddrRecord() const { + return type == nsIDNSService::RESOLVE_TYPE_DEFAULT; + } + + // When the record began being valid. Used mainly for bookkeeping. + mozilla::TimeStamp mValidStart; + + // When the record is no longer valid (it's time of expiration) + mozilla::TimeStamp mValidEnd; + + // When the record enters its grace period. This must be before mValidEnd. + // If a record is in its grace period (and not expired), it will be used + // but a request to refresh it will be made. + mozilla::TimeStamp mGraceStart; + + // The computed TRR mode that is actually used by the request. + // It is set in nsHostResolver::NameLookup and is based on the mode of the + // default resolver and the TRRMode encoded in the flags. + // The mode into account if the TRR service is disabled, + // parental controls are on, domain matches exclusion list, etc. + nsIRequest::TRRMode mEffectiveTRRMode; + + TRRSkippedReason mTRRTRRSkippedReason = TRR_UNSET; + TRRSkippedReason mTRRAFailReason = TRR_UNSET; + TRRSkippedReason mTRRAAAAFailReason = TRR_UNSET; + + mozilla::DataMutex<RefPtr<mozilla::net::TRRQuery>> mTRRQuery; + + mozilla::Atomic<int32_t> + mResolving; // counter of outstanding resolving calls + + uint8_t negative : 1; /* True if this record is a cache of a failed + lookup. Negative cache entries are valid just + like any other (though never for more than 60 + seconds), but a use of that negative entry + forces an asynchronous refresh. */ + uint8_t mDoomed : 1; // explicitly expired +}; + +// b020e996-f6ab-45e5-9bf5-1da71dd0053a +#define ADDRHOSTRECORD_IID \ + { \ + 0xb020e996, 0xf6ab, 0x45e5, { \ + 0x9b, 0xf5, 0x1d, 0xa7, 0x1d, 0xd0, 0x05, 0x3a \ + } \ + } + +class AddrHostRecord final : public nsHostRecord { + typedef mozilla::Mutex Mutex; + + public: + NS_DECLARE_STATIC_IID_ACCESSOR(ADDRHOSTRECORD_IID) + NS_DECL_ISUPPORTS_INHERITED + + /* a fully resolved host record has either a non-null |addr_info| or |addr| + * field. if |addr_info| is null, it implies that the |host| is an IP + * address literal. in which case, |addr| contains the parsed address. + * otherwise, if |addr_info| is non-null, then it contains one or many + * IP addresses corresponding to the given host name. if both |addr_info| + * and |addr| are null, then the given host has not yet been fully resolved. + * |af| is the address family of the record we are querying for. + */ + + /* the lock protects |addr_info| and |addr_info_gencnt| because they + * are mutable and accessed by the resolver worker thread and the + * nsDNSService2 class. |addr| doesn't change after it has been + * assigned a value. only the resolver worker thread modifies + * nsHostRecord (and only in nsHostResolver::CompleteLookup); + * the other threads just read it. therefore the resolver worker + * thread doesn't need to lock when reading |addr_info|. + */ + Mutex addr_info_lock; + int addr_info_gencnt; /* generation count of |addr_info| */ + RefPtr<mozilla::net::AddrInfo> addr_info; + mozilla::UniquePtr<mozilla::net::NetAddr> addr; + + // hold addr_info_lock when calling the blocklist functions + bool Blocklisted(const mozilla::net::NetAddr* query); + void ResetBlocklist(); + void ReportUnusable(const mozilla::net::NetAddr* aAddress); + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const override; + + nsIRequest::TRRMode EffectiveTRRMode() const { return mEffectiveTRRMode; } + + private: + friend class nsHostResolver; + friend class mozilla::net::TRR; + friend class mozilla::net::TRRQuery; + + explicit AddrHostRecord(const nsHostKey& key); + ~AddrHostRecord(); + + // Checks if the record is usable (not expired and has a value) + bool HasUsableResultInternal() const override; + + bool RemoveOrRefresh(bool aTrrToo); // Mark records currently being resolved + // as needed to resolve again. + + void ResolveComplete(); + + enum DnsPriority { + DNS_PRIORITY_LOW, + DNS_PRIORITY_MEDIUM, + DNS_PRIORITY_HIGH, + }; + static DnsPriority GetPriority(uint16_t aFlags); + + // true if pending and on the queue (not yet given to getaddrinfo()) + bool onQueue() { return LoadNative() && isInList(); } + + // When the lookups of this record started and their durations + mozilla::TimeStamp mTrrStart; + mozilla::TimeStamp mNativeStart; + mozilla::TimeDuration mTrrDuration; + mozilla::TimeDuration mNativeDuration; + + mozilla::Atomic<bool> mTRRUsed; // TRR was used on this record + uint8_t mTRRSuccess; // number of successful TRR responses + uint8_t mNativeSuccess; // number of native lookup responses + + // clang-format off + MOZ_ATOMIC_BITFIELDS(mAtomicBitfields, 8, ( + // true if this record is being resolved "natively", which means that + // it is either on the pending queue or owned by one of the worker threads. + (uint16_t, Native, 1), + (uint16_t, NativeUsed, 1), + // true if off queue and contributing to mActiveAnyThreadCount + (uint16_t, UsingAnyThread, 1), + (uint16_t, GetTtl, 1), + (uint16_t, ResolveAgain, 1) + )) + // clang-format on + + // The number of times ReportUnusable() has been called in the record's + // lifetime. + uint32_t mUnusableCount = 0; + + // a list of addresses associated with this record that have been reported + // as unusable. the list is kept as a set of strings to make it independent + // of gencnt. + nsTArray<nsCString> mUnusableItems; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(AddrHostRecord, ADDRHOSTRECORD_IID) + +// 77b786a7-04be-44f2-987c-ab8aa96676e0 +#define TYPEHOSTRECORD_IID \ + { \ + 0x77b786a7, 0x04be, 0x44f2, { \ + 0x98, 0x7c, 0xab, 0x8a, 0xa9, 0x66, 0x76, 0xe0 \ + } \ + } + +class TypeHostRecord final : public nsHostRecord, + public nsIDNSTXTRecord, + public nsIDNSHTTPSSVCRecord, + public mozilla::net::DNSHTTPSSVCRecordBase { + public: + NS_DECLARE_STATIC_IID_ACCESSOR(TYPEHOSTRECORD_IID) + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIDNSTXTRECORD + NS_DECL_NSIDNSHTTPSSVCRECORD + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const override; + uint32_t GetType(); + mozilla::net::TypeRecordResultType GetResults(); + + private: + friend class nsHostResolver; + friend class mozilla::net::TRRQuery; + + explicit TypeHostRecord(const nsHostKey& key); + ~TypeHostRecord(); + + // Checks if the record is usable (not expired and has a value) + bool HasUsableResultInternal() const override; + + bool HasUsableResult(); + + mozilla::net::TypeRecordResultType mResults = AsVariant(mozilla::Nothing()); + mozilla::Mutex mResultsLock; + + // When the lookups of this record started (for telemetry). + mozilla::TimeStamp mStart; + bool mAllRecordsExcluded = false; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(TypeHostRecord, TYPEHOSTRECORD_IID) + +/** + * This class is used to notify listeners when a ResolveHost operation is + * complete. Classes that derive it must implement threadsafe nsISupports + * to be able to use RefPtr with this class. + */ +class nsResolveHostCallback + : public mozilla::LinkedListElement<RefPtr<nsResolveHostCallback>>, + public nsISupports { + public: + /** + * OnResolveHostComplete + * + * this function is called to complete a host lookup initiated by + * nsHostResolver::ResolveHost. it may be invoked recursively from + * ResolveHost or on an unspecified background thread. + * + * NOTE: it is the responsibility of the implementor of this method + * to handle the callback in a thread safe manner. + * + * @param resolver + * nsHostResolver object associated with this result + * @param record + * the host record containing the results of the lookup + * @param status + * if successful, |record| contains non-null results + */ + virtual void OnResolveHostComplete(nsHostResolver* resolver, + nsHostRecord* record, nsresult status) = 0; + /** + * EqualsAsyncListener + * + * Determines if the listener argument matches the listener member var. + * For subclasses not implementing a member listener, should return false. + * For subclasses having a member listener, the function should check if + * they are the same. Used for cases where a pointer to an object + * implementing nsResolveHostCallback is unknown, but a pointer to + * the original listener is known. + * + * @param aListener + * nsIDNSListener object associated with the original request + */ + virtual bool EqualsAsyncListener(nsIDNSListener* aListener) = 0; + + virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const = 0; + + protected: + virtual ~nsResolveHostCallback() = default; +}; + +class AHostResolver { + public: + AHostResolver() = default; + virtual ~AHostResolver() = default; + NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING + + enum LookupStatus { + LOOKUP_OK, + LOOKUP_RESOLVEAGAIN, + }; + + virtual LookupStatus CompleteLookup( + nsHostRecord*, nsresult, mozilla::net::AddrInfo*, bool pb, + const nsACString& aOriginsuffix, + nsHostRecord::TRRSkippedReason aReason) = 0; + virtual LookupStatus CompleteLookupByType( + nsHostRecord*, nsresult, mozilla::net::TypeRecordResultType& aResult, + uint32_t aTtl, bool pb) = 0; + virtual nsresult GetHostRecord(const nsACString& host, + const nsACString& aTrrServer, uint16_t type, + uint16_t flags, uint16_t af, bool pb, + const nsCString& originSuffix, + nsHostRecord** result) { + return NS_ERROR_FAILURE; + } + virtual nsresult TrrLookup_unlocked(nsHostRecord*, + mozilla::net::TRR* pushedTRR = nullptr) { + return NS_ERROR_FAILURE; + } + virtual void MaybeRenewHostRecord(nsHostRecord* aRec) {} +}; + +/** + * nsHostResolver - an asynchronous host name resolver. + */ +class nsHostResolver : public nsISupports, public AHostResolver { + using CondVar = mozilla::CondVar; + using Mutex = mozilla::Mutex; + + public: + NS_DECL_THREADSAFE_ISUPPORTS + + /** + * creates an addref'd instance of a nsHostResolver object. + */ + static nsresult Create(uint32_t maxCacheEntries, // zero disables cache + uint32_t defaultCacheEntryLifetime, // seconds + uint32_t defaultGracePeriod, // seconds + nsHostResolver** result); + + /** + * Set (new) cache limits. + */ + void SetCacheLimits(uint32_t maxCacheEntries, // zero disables cache + uint32_t defaultCacheEntryLifetime, // seconds + uint32_t defaultGracePeriod); // seconds + + /** + * puts the resolver in the shutdown state, which will cause any pending + * callbacks to be detached. any future calls to ResolveHost will fail. + */ + void Shutdown(); + + /** + * resolve the given hostname and originAttributes asynchronously. the caller + * can synthesize a synchronous host lookup using a lock and a cvar. as noted + * above the callback will occur re-entrantly from an unspecified thread. the + * host lookup cannot be canceled (cancelation can be layered above this by + * having the callback implementation return without doing anything). + */ + nsresult ResolveHost(const nsACString& aHost, const nsACString& trrServer, + uint16_t type, + const mozilla::OriginAttributes& aOriginAttributes, + uint16_t flags, uint16_t af, + nsResolveHostCallback* callback); + + nsHostRecord* InitRecord(const nsHostKey& key); + mozilla::net::NetworkConnectivityService* GetNCS() { return mNCS; } + + /** + * return a resolved hard coded loopback dns record for the specified key + */ + already_AddRefed<nsHostRecord> InitLoopbackRecord(const nsHostKey& key, + nsresult* aRv); + + /** + * removes the specified callback from the nsHostRecord for the given + * hostname, originAttributes, flags, and address family. these parameters + * should correspond to the parameters passed to ResolveHost. this function + * executes the callback if the callback is still pending with the given + * status. + */ + void DetachCallback(const nsACString& hostname, const nsACString& trrServer, + uint16_t type, + const mozilla::OriginAttributes& aOriginAttributes, + uint16_t flags, uint16_t af, + nsResolveHostCallback* callback, nsresult status); + + /** + * Cancels an async request associated with the hostname, originAttributes, + * flags, address family and listener. Cancels first callback found which + * matches these criteria. These parameters should correspond to the + * parameters passed to ResolveHost. If this is the last callback associated + * with the host record, it is removed from any request queues it might be on. + */ + void CancelAsyncRequest(const nsACString& host, const nsACString& trrServer, + uint16_t type, + const mozilla::OriginAttributes& aOriginAttributes, + uint16_t flags, uint16_t af, + nsIDNSListener* aListener, nsresult status); + /** + * values for the flags parameter passed to ResolveHost and DetachCallback + * that may be bitwise OR'd together. + * + * NOTE: in this implementation, these flags correspond exactly in value + * to the flags defined on nsIDNSService. + */ + enum { + RES_BYPASS_CACHE = nsIDNSService::RESOLVE_BYPASS_CACHE, + RES_CANON_NAME = nsIDNSService::RESOLVE_CANONICAL_NAME, + RES_PRIORITY_MEDIUM = nsIDNSService::RESOLVE_PRIORITY_MEDIUM, + RES_PRIORITY_LOW = nsIDNSService::RESOLVE_PRIORITY_LOW, + RES_SPECULATE = nsIDNSService::RESOLVE_SPECULATE, + // RES_DISABLE_IPV6 = nsIDNSService::RESOLVE_DISABLE_IPV6, // Not used + RES_OFFLINE = nsIDNSService::RESOLVE_OFFLINE, + // RES_DISABLE_IPv4 = nsIDNSService::RESOLVE_DISABLE_IPV4, // Not Used + RES_ALLOW_NAME_COLLISION = nsIDNSService::RESOLVE_ALLOW_NAME_COLLISION, + RES_DISABLE_TRR = nsIDNSService::RESOLVE_DISABLE_TRR, + RES_REFRESH_CACHE = nsIDNSService::RESOLVE_REFRESH_CACHE, + RES_IP_HINT = nsIDNSService::RESOLVE_IP_HINT + }; + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; + + /** + * Flush the DNS cache. + */ + void FlushCache(bool aTrrToo); + + LookupStatus CompleteLookup(nsHostRecord*, nsresult, mozilla::net::AddrInfo*, + bool pb, const nsACString& aOriginsuffix, + nsHostRecord::TRRSkippedReason aReason) override; + LookupStatus CompleteLookupByType(nsHostRecord*, nsresult, + mozilla::net::TypeRecordResultType& aResult, + uint32_t aTtl, bool pb) override; + nsresult GetHostRecord(const nsACString& host, const nsACString& trrServer, + uint16_t type, uint16_t flags, uint16_t af, bool pb, + const nsCString& originSuffix, + nsHostRecord** result) override; + nsresult TrrLookup_unlocked(nsHostRecord*, + mozilla::net::TRR* pushedTRR = nullptr) override; + static mozilla::net::ResolverMode Mode(); + + virtual void MaybeRenewHostRecord(nsHostRecord* aRec) override; + + private: + explicit nsHostResolver(uint32_t maxCacheEntries, + uint32_t defaultCacheEntryLifetime, + uint32_t defaultGracePeriod); + virtual ~nsHostResolver(); + + nsresult Init(); + // In debug builds it asserts that the element is in the list. + void AssertOnQ(nsHostRecord*, mozilla::LinkedList<RefPtr<nsHostRecord>>&); + static void ComputeEffectiveTRRMode(nsHostRecord* aRec); + nsresult NativeLookup(nsHostRecord*); + nsresult TrrLookup(nsHostRecord*, mozilla::net::TRR* pushedTRR = nullptr); + + // Kick-off a name resolve operation, using native resolver and/or TRR + nsresult NameLookup(nsHostRecord*); + bool GetHostToLookup(AddrHostRecord** result); + void MaybeRenewHostRecordLocked(nsHostRecord* aRec); + + // Removes the first element from the list and returns it AddRef-ed in aResult + // Should not be called for an empty linked list. + void DeQueue(mozilla::LinkedList<RefPtr<nsHostRecord>>& aQ, + AddrHostRecord** aResult); + // Cancels host records in the pending queue and also + // calls CompleteLookup with the NS_ERROR_ABORT result code. + void ClearPendingQueue(mozilla::LinkedList<RefPtr<nsHostRecord>>& aPendingQ); + nsresult ConditionallyCreateThread(nsHostRecord* rec); + + /** + * Starts a new lookup in the background for entries that are in the grace + * period with a failed connect or all cached entries are negative. + */ + nsresult ConditionallyRefreshRecord(nsHostRecord* rec, + const nsACString& host); + + void AddToEvictionQ(nsHostRecord* rec); + + void ThreadFunc(); + + enum { + METHOD_HIT = 1, + METHOD_RENEWAL = 2, + METHOD_NEGATIVE_HIT = 3, + METHOD_LITERAL = 4, + METHOD_OVERFLOW = 5, + METHOD_NETWORK_FIRST = 6, + METHOD_NETWORK_SHARED = 7 + }; + + uint32_t mMaxCacheEntries; + uint32_t mDefaultCacheLifetime; // granularity seconds + uint32_t mDefaultGracePeriod; // granularity seconds + mutable Mutex mLock; // mutable so SizeOfIncludingThis can be const + CondVar mIdleTaskCV; + nsRefPtrHashtable<nsGenericHashKey<nsHostKey>, nsHostRecord> mRecordDB; + mozilla::LinkedList<RefPtr<nsHostRecord>> mHighQ; + mozilla::LinkedList<RefPtr<nsHostRecord>> mMediumQ; + mozilla::LinkedList<RefPtr<nsHostRecord>> mLowQ; + mozilla::LinkedList<RefPtr<nsHostRecord>> mEvictionQ; + uint32_t mEvictionQSize; + PRTime mCreationTime; + mozilla::TimeDuration mLongIdleTimeout; + mozilla::TimeDuration mShortIdleTimeout; + + RefPtr<nsIThreadPool> mResolverThreads; + RefPtr<mozilla::net::NetworkConnectivityService> mNCS; + + mozilla::Atomic<bool> mShutdown; + mozilla::Atomic<uint32_t> mNumIdleTasks; + mozilla::Atomic<uint32_t> mActiveTaskCount; + mozilla::Atomic<uint32_t> mActiveAnyThreadCount; + mozilla::Atomic<uint32_t> mPendingCount; + + // Set the expiration time stamps appropriately. + void PrepareRecordExpirationAddrRecord(AddrHostRecord* rec) const; + + public: + /* + * Called by the networking dashboard via the DnsService2 + */ + void GetDNSCacheEntries(nsTArray<mozilla::net::DNSCacheEntries>*); +}; + +#endif // nsHostResolver_h__ diff --git a/netwerk/dns/nsIDNKitInterface.h b/netwerk/dns/nsIDNKitInterface.h new file mode 100644 index 0000000000..3e0f0d60ac --- /dev/null +++ b/netwerk/dns/nsIDNKitInterface.h @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2000-2002 Japan Network Information Center. All rights reserved. + * + * By using this file, you agree to the terms and conditions set forth bellow. + * + * LICENSE TERMS AND CONDITIONS + * + * The following License Terms and Conditions apply, unless a different + * license is obtained from Japan Network Information Center ("JPNIC"), + * a Japanese association, Kokusai-Kougyou-Kanda Bldg 6F, 2-3-4 Uchi-Kanda, + * Chiyoda-ku, Tokyo 101-0047, Japan. + + * 1. Use, Modification and Redistribution (including distribution of any + * modified or derived work) in source and/or binary forms is permitted + * under this License Terms and Conditions. + * + * 2. Redistribution of source code must retain the copyright notices as they + * appear in each source code file, this License Terms and Conditions. + * + * 3. Redistribution in binary form must reproduce the Copyright Notice, + * this License Terms and Conditions, in the documentation and/or other + * materials provided with the distribution. For the purposes of binary + * distribution the "Copyright Notice" refers to the following language: + * "Copyright (c) 2000-2002 Japan Network Information Center. All rights reserved." + * + * 4. The name of JPNIC may not be used to endorse or promote products + * derived from this Software without specific prior written approval of + * JPNIC. + * + * 5. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY JPNIC + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JPNIC BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef nsIDNKitWrapper_h__ +#define nsIDNKitWrapper_h__ + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * libidnkit result code. + */ +typedef enum { + idn_success, + idn_notfound, + idn_invalid_encoding, + idn_invalid_syntax, + idn_invalid_name, + idn_invalid_message, + idn_invalid_action, + idn_invalid_codepoint, + idn_invalid_length, + idn_buffer_overflow, + idn_noentry, + idn_nomemory, + idn_nofile, + idn_nomapping, + idn_context_required, + idn_prohibited, + idn_failure /* !!This must be the last one!! */ +} idn_result_t; + +/* + * BIDI type codes. + */ +typedef enum { + idn_biditype_r_al, + idn_biditype_l, + idn_biditype_others +} idn_biditype_t; + +/* + * A Handle for nameprep operations. + */ +typedef struct idn_nameprep *idn_nameprep_t; + + +/* + * The latest version of nameprep. + */ +#define IDN_NAMEPREP_CURRENT "nameprep-11" + +#undef assert +#define assert(a) +#define TRACE(a) + + +/* race.c */ +idn_result_t race_decode_decompress(const char *from, + uint16_t *buf, + size_t buflen); +idn_result_t race_compress_encode(const uint16_t *p, + int compress_mode, + char *to, size_t tolen); +int get_compress_mode(uint16_t *p); + + +/* nameprep.c */ + +/* + * Create a handle for nameprep operations. + * The handle is stored in '*handlep', which is used other functions + * in this module. + * The version of the NAMEPREP specification can be specified with + * 'version' parameter. If 'version' is nullptr, the latest version + * is used. + * + * Returns: + * idn_success -- ok. + * idn_notfound -- specified version not found. + */ +idn_result_t +idn_nameprep_create(const char *version, idn_nameprep_t *handlep); + +/* + * Close a handle, which was created by 'idn_nameprep_create'. + */ +void +idn_nameprep_destroy(idn_nameprep_t handle); + +/* + * Perform character mapping on an UCS4 string specified by 'from', and + * store the result into 'to', whose length is specified by 'tolen'. + * + * Returns: + * idn_success -- ok. + * idn_buffer_overflow -- result buffer is too small. + */ +idn_result_t +idn_nameprep_map(idn_nameprep_t handle, const uint32_t *from, + uint32_t *to, size_t tolen); + +/* + * Check if an UCS4 string 'str' contains any prohibited characters specified + * by the draft. If found, the pointer to the first such character is stored + * into '*found'. Otherwise '*found' will be nullptr. + * + * Returns: + * idn_success -- check has been done properly. (But this + * does not mean that no prohibited character + * was found. Check '*found' to see the + * result.) + */ +idn_result_t +idn_nameprep_isprohibited(idn_nameprep_t handle, const uint32_t *str, + const uint32_t **found); + +/* + * Check if an UCS4 string 'str' contains any unassigned characters specified + * by the draft. If found, the pointer to the first such character is stored + * into '*found'. Otherwise '*found' will be nullptr. + * + * Returns: + * idn_success -- check has been done properly. (But this + * does not mean that no unassinged character + * was found. Check '*found' to see the + * result.) + */ +idn_result_t +idn_nameprep_isunassigned(idn_nameprep_t handle, const uint32_t *str, + const uint32_t **found); + +/* + * Check if an UCS4 string 'str' is valid string specified by ``bidi check'' + * of the draft. If it is not valid, the pointer to the first invalid + * character is stored into '*found'. Otherwise '*found' will be nullptr. + * + * Returns: + * idn_success -- check has been done properly. (But this + * does not mean that the string was valid. + * Check '*found' to see the result.) + */ +idn_result_t +idn_nameprep_isvalidbidi(idn_nameprep_t handle, const uint32_t *str, + const uint32_t **found); + + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* nsIDNKitWrapper_h__ */ diff --git a/netwerk/dns/nsIDNSByTypeRecord.idl b/netwerk/dns/nsIDNSByTypeRecord.idl new file mode 100644 index 0000000000..e311b34a1b --- /dev/null +++ b/netwerk/dns/nsIDNSByTypeRecord.idl @@ -0,0 +1,126 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsIDNSRecord.idl" + +%{ C++ + +#include "mozilla/Maybe.h" +#include "mozilla/Tuple.h" +#include "nsTArrayForwardDeclare.h" +#include "nsStringFwd.h" + +namespace mozilla { + +template <typename... Ts> class Variant; +struct Nothing; + +namespace net { + struct SVCB; + using TypeRecordResultType = + Variant<Nothing, CopyableTArray<nsCString>, CopyableTArray<SVCB>>; +} +} + +%} + +[ref] native CStringArrayRef(CopyableTArray<nsCString>); +native TypeResult(mozilla::net::TypeRecordResultType); + +native MaybePort(mozilla::Maybe<uint16_t>); +native MaybeAlpnTuple(mozilla::Maybe<mozilla::Tuple<nsCString, bool>>); + +[scriptable, uuid(5d13241b-9d46-448a-90d8-77c418491026)] +interface nsIDNSByTypeRecord : nsIDNSRecord +{ + /** + * Returns DNS request type that was made for this request. + */ + readonly attribute unsigned long type; + + [noscript] readonly attribute TypeResult results; +}; + +[scriptable, uuid(2a71750d-cb21-45f1-9e1c-666d18dd7645)] +interface nsIDNSTXTRecord : nsISupports +{ + CStringArrayRef getRecords(); + + /* + * Return concatenated strings. + */ + ACString getRecordsAsOneString(); +}; + +[scriptable, uuid(2979ceaa-9c7e-49de-84b8-ea81c16aebf1)] +interface nsISVCParam : nsISupports { + readonly attribute uint16_t type; +}; + +[scriptable, uuid(0dc58309-4d67-4fc4-a4e3-38dbde9d9f4c)] +interface nsISVCParamAlpn : nsISupports { + readonly attribute Array<ACString> alpn; +}; + +[scriptable, uuid(b3ed89c3-2ae6-4c92-8176-b76bc2437fcb)] +interface nsISVCParamNoDefaultAlpn : nsISupports { +}; + +[scriptable, uuid(a37c7bcb-bfcd-4ab4-b826-cc583859ba73)] +interface nsISVCParamPort : nsISupports { + readonly attribute uint16_t port; +}; + +[scriptable, uuid(d3163d2f-0bbe-47d4-bcac-db3fb1433b39)] +interface nsISVCParamIPv4Hint : nsISupports { + readonly attribute Array<nsINetAddr> ipv4Hint; +}; + +[scriptable, uuid(1f31e41d-b6d8-4796-b12a-82ef8d2b0e43)] +interface nsISVCParamEchConfig : nsISupports { + readonly attribute ACString echconfig; +}; + +[scriptable, uuid(5100bce4-9d3b-42e1-a3c9-0f386bbc9dad)] +interface nsISVCParamIPv6Hint : nsISupports { + readonly attribute Array<nsINetAddr> ipv6Hint; +}; + +[scriptable, uuid(bdcef040-452e-11eb-b378-0242ac130002)] +interface nsISVCParamODoHConfig : nsISupports { + readonly attribute ACString ODoHConfig; +}; + +[scriptable, builtinclass, uuid(a4da5645-2160-4439-bd11-540a2d26c989)] +interface nsISVCBRecord : nsISupports { + readonly attribute uint16_t priority; + readonly attribute ACString name; + [noscript, nostdcall, notxpcom] readonly attribute MaybePort port; + [noscript, nostdcall, notxpcom] readonly attribute MaybeAlpnTuple alpn; + readonly attribute ACString echConfig; + readonly attribute ACString ODoHConfig; + readonly attribute bool hasIPHintAddress; + readonly attribute Array<nsISVCParam> values; +}; + +[scriptable, uuid(5b649e95-e0d3-422b-99a6-79d70a041387)] +interface nsIDNSHTTPSSVCRecord : nsISupports +{ + readonly attribute Array<nsISVCBRecord> records; + nsISVCBRecord GetServiceModeRecord(in boolean aNoHttp2, in boolean aNoHttp3); + /** + * Returns true if one of SVCB records has IPv4 or IPv6 hint addresses. + */ + readonly attribute boolean hasIPAddresses; + + /** + * Returns true when all names of SVCB records are in exclusion list. + */ + readonly attribute boolean allRecordsExcluded; + + Array<nsISVCBRecord> GetAllRecordsWithEchConfig(in boolean aNoHttp2, + in boolean aNoHttp3, + out boolean aAllRecordsHaveEchConfig, + out boolean aAllRecordsInH3ExcludedList); +}; diff --git a/netwerk/dns/nsIDNSListener.idl b/netwerk/dns/nsIDNSListener.idl new file mode 100644 index 0000000000..d925294733 --- /dev/null +++ b/netwerk/dns/nsIDNSListener.idl @@ -0,0 +1,34 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsICancelable; +interface nsIDNSRecord; +interface nsIDNSByTypeRecord; + +/** + * nsIDNSListener + */ +[scriptable, uuid(27d49bfe-280c-49e0-bbaa-f6200c232c3d)] +interface nsIDNSListener : nsISupports +{ + /** + * called when an asynchronous host lookup completes. + * + * @param aRequest + * the value returned from asyncResolve. + * @param aRecord + * the DNS record corresponding to the hostname that was resolved. + * this parameter is null if there was an error. + * depending on the type parameter passed to asyncResolve() the + * caller should QueryInterface to either nsIDNSAddrRecord or + * nsIDNSByTypeRecord. + * @param aStatus + * if the lookup failed, this parameter gives the reason. + */ + void onLookupComplete(in nsICancelable aRequest, + in nsIDNSRecord aRecord, + in nsresult aStatus); +}; diff --git a/netwerk/dns/nsIDNSRecord.idl b/netwerk/dns/nsIDNSRecord.idl new file mode 100644 index 0000000000..4ac3156fb0 --- /dev/null +++ b/netwerk/dns/nsIDNSRecord.idl @@ -0,0 +1,136 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +%{ C++ +namespace mozilla { +namespace net { +union NetAddr; +} +} +#include "nsTArrayForwardDeclare.h" +%} +native NetAddr(mozilla::net::NetAddr); +[ref] native nsNetAddrTArrayRef(nsTArray<mozilla::net::NetAddr>); +interface nsINetAddr; + +/** + * nsIDNSRecord + * + * this interface represents the result of a DNS lookup. since a DNS + * query may return more than one resolved IP address, the record acts + * like an enumerator, allowing the caller to easily step through the + * list of IP addresses. + */ +[scriptable, uuid(f92228ae-c417-4188-a604-0830a95e7eb9)] +interface nsIDNSRecord : nsISupports +{ +}; + +[scriptable, uuid(cb260e20-943f-4309-953b-78c90d3a7638)] +interface nsIDNSAddrRecord : nsIDNSRecord +{ + /** + * @return the canonical hostname for this record. this value is empty if + * the record was not fetched with the RESOLVE_CANONICAL_NAME flag. + * + * e.g., www.mozilla.org --> rheet.mozilla.org + * + * That the result, if IDN will be returned as punycode. + * e.g., élève.w3c-test.org --> xn--lve-6lad.w3c-test.org + */ + readonly attribute ACString canonicalName; + + /** + * this function copies the value of the next IP address into the + * given NetAddr struct and increments the internal address iterator. + * + * @param aPort + * A port number to initialize the NetAddr with. + * + * @throws NS_ERROR_NOT_AVAILABLE if there is not another IP address in + * the record. + */ + [noscript] NetAddr getNextAddr(in uint16_t aPort); + + /** + * this function copies the value of all working members of the RR + * set into the output array. + * + * @param aAddressArray + * The result set + */ + [noscript] void getAddresses(out nsNetAddrTArrayRef aAddressArray); + + /** + * this function returns the value of the next IP address as a + * scriptable address and increments the internal address iterator. + * + * @param aPort + * A port number to initialize the nsINetAddr with. + * + * @throws NS_ERROR_NOT_AVAILABLE if there is not another IP address in + * the record. + */ + nsINetAddr getScriptableNextAddr(in uint16_t aPort); + + /** + * this function returns the value of the next IP address as a + * string and increments the internal address iterator. + * + * @throws NS_ERROR_NOT_AVAILABLE if there is not another IP address in + * the record. + */ + ACString getNextAddrAsString(); + + /** + * this function returns true if there is another address in the record. + */ + boolean hasMore(); + + /** + * this function resets the internal address iterator to the first + * address in the record. + */ + void rewind(); + + /** + * This function indicates that the last address obtained via getNextAddr*() + * was not usuable and should be skipped in future uses of this + * record if other addresses are available. + * + * @param aPort is the port number associated with the failure, if any. + * It may be zero if not applicable. + */ + void reportUnusable(in uint16_t aPort); + + /** + * Record retreived with TRR. + */ + bool IsTRR(); + + /** + * This attribute is only set if TRR is used and it measures time between + * asyncOpen on a channel and the time parsing of response if done. + * Thee time is measured in milliseconds. + */ + readonly attribute double trrFetchDuration; + + /** + * This attribute is only set if TRR is used and it measures time between + * sending a request and the time response is received from the network. + * This time is similat to the time above, but exludes a time needed to + * make a connection and a time neededto parse results (this also does not + * include delays that may be introduce because parsing is perform on the main + * thread). + * Thee time is measured in milliseconds. + */ + readonly attribute double trrFetchDurationNetworkOnly; + + /** + * The TRR mode this record is used. + */ + readonly attribute unsigned long effectiveTRRMode; +}; diff --git a/netwerk/dns/nsIDNSResolverInfo.idl b/netwerk/dns/nsIDNSResolverInfo.idl new file mode 100644 index 0000000000..546d2d789c --- /dev/null +++ b/netwerk/dns/nsIDNSResolverInfo.idl @@ -0,0 +1,11 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +[scriptable, builtinclass, uuid(74db2955-6298-4d82-a3b9-7f9e8ba9e854)] +interface nsIDNSResolverInfo : nsISupports +{ + readonly attribute ACString URL; +}; diff --git a/netwerk/dns/nsIDNSService.idl b/netwerk/dns/nsIDNSService.idl new file mode 100644 index 0000000000..c999385bf7 --- /dev/null +++ b/netwerk/dns/nsIDNSService.idl @@ -0,0 +1,341 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" +#include "nsIRequest.idl" + +%{ C++ +#include "mozilla/BasePrincipal.h" +%} + +interface nsICancelable; +interface nsIEventTarget; +interface nsIDNSRecord; +interface nsIDNSListener; +interface nsIDNSResolverInfo; + +%{C++ +#include "nsTArrayForwardDeclare.h" +namespace mozilla { namespace net { + struct DNSCacheEntries; +} } +%} + +[ptr] native EntriesArray(nsTArray<mozilla::net::DNSCacheEntries>); +[ref] native OriginAttributes(const mozilla::OriginAttributes); + +/** + * nsIDNSService + */ +[scriptable, builtinclass, uuid(de5642c6-61fc-4fcf-9a47-03226b0d4e21)] +interface nsIDNSService : nsISupports +{ + /** + * These are the dns request types that are currently supported. + * RESOLVE_TYPE_DEFAULT is standard A/AAAA lookup + */ + cenum ResolveType : 16 { + RESOLVE_TYPE_DEFAULT = 0, + RESOLVE_TYPE_TXT = 16, + RESOLVE_TYPE_HTTPSSVC = 65, + }; + + /** + * kicks off an asynchronous host lookup. + * + * @param aHostName + * the hostname or IP-address-literal to resolve. + * @param aType + * one of RESOLVE_TYPE_*. + * @param aFlags + * a bitwise OR of the RESOLVE_ prefixed constants defined below. + * @param aResolver + * a resolverInfo object that holds information about the resolver + * to be used such as TRR URL. If null we use the default configuration. + * @param aListener + * the listener to be notified when the result is available. + * @param aListenerTarget + * optional parameter (may be null). if non-null, this parameter + * specifies the nsIEventTarget of the thread on which the + * listener's onLookupComplete should be called. however, if this + * parameter is null, then onLookupComplete will be called on an + * unspecified thread (possibly recursively). + * @param aOriginAttributes + * the originAttribute for this resolving, the DNS cache will be + * separated according to this originAttributes. This attribute is + * optional to avoid breaking add-ons. + * + * @return An object that can be used to cancel the host lookup. + */ + [implicit_jscontext, optional_argc] + nsICancelable asyncResolve(in AUTF8String aHostName, + in nsIDNSService_ResolveType aType, + in unsigned long aFlags, + in nsIDNSResolverInfo aResolver, + in nsIDNSListener aListener, + in nsIEventTarget aListenerTarget, + [optional] in jsval aOriginAttributes); + + [notxpcom] + nsresult asyncResolveNative(in AUTF8String aHostName, + in nsIDNSService_ResolveType aType, + in unsigned long aFlags, + in nsIDNSResolverInfo aResolver, + in nsIDNSListener aListener, + in nsIEventTarget aListenerTarget, + in OriginAttributes aOriginAttributes, + out nsICancelable aResult); + + /** + * Returns a new resolverInfo object containing the URL we pass to it. + */ + nsIDNSResolverInfo newTRRResolverInfo(in AUTF8String aTrrURL); + + /** + * Attempts to cancel a previously requested async DNS lookup + * + * @param aHostName + * the hostname or IP-address-literal to resolve. + * @param aType + * one of RESOLVE_TYPE_*. + * @param aFlags + * a bitwise OR of the RESOLVE_ prefixed constants defined below. + * @param aResolver + * a resolverInfo object that holds information about the resolver + * to be used such as TRR URL. If null we use the default configuration. + * @param aListener + * the original listener which was to be notified about the host lookup + * result - used to match request information to requestor. + * @param aReason + * nsresult reason for the cancellation + * @param aOriginAttributes + * the originAttribute for this resolving. This attribute is optional + * to avoid breaking add-ons. + */ + [implicit_jscontext, optional_argc] + void cancelAsyncResolve(in AUTF8String aHostName, + in nsIDNSService_ResolveType aType, + in unsigned long aFlags, + in nsIDNSResolverInfo aResolver, + in nsIDNSListener aListener, + in nsresult aReason, + [optional] in jsval aOriginAttributes); + + [notxpcom] + nsresult cancelAsyncResolveNative(in AUTF8String aHostName, + in nsIDNSService_ResolveType aType, + in unsigned long aFlags, + in nsIDNSResolverInfo aResolver, + in nsIDNSListener aListener, + in nsresult aReason, + in OriginAttributes aOriginAttributes); + + /** + * called to synchronously resolve a hostname. + * + * Since this method may block the calling thread for a long period of + * time, it may not be accessed from the main thread. + * + * @param aHostName + * the hostname or IP-address-literal to resolve. + * @param aFlags + * a bitwise OR of the RESOLVE_ prefixed constants defined below. + * @param aOriginAttributes + * the originAttribute for this resolving, the DNS cache will be + * separated according to this originAttributes. This attribute is + * optional to avoid breaking add-ons. + * + * @return DNS record corresponding to the given hostname. + * @throws NS_ERROR_UNKNOWN_HOST if host could not be resolved. + * @throws NS_ERROR_NOT_AVAILABLE if accessed from the main thread. + */ + [implicit_jscontext, optional_argc] + nsIDNSRecord resolve(in AUTF8String aHostName, + in unsigned long aFlags, + [optional] in jsval aOriginAttributes); + + [notxpcom] + nsresult resolveNative(in AUTF8String aHostName, + in unsigned long aFlags, + in OriginAttributes aOriginAttributes, + out nsIDNSRecord aResult); + + /** + * The method takes a pointer to an nsTArray + * and fills it with cache entry data + * Called by the networking dashboard + */ + [noscript] void getDNSCacheEntries(in EntriesArray args); + + + /** + * Clears the DNS cache. + * @param aTrrToo + * If true we will clear TRR cached entries too. Since these + * are resolved remotely it's not necessary to clear them when + * the network status changes, but it's sometimes useful to do so + * for tests or other situations. + */ + void clearCache(in boolean aTrrToo); + + /** + * The method is used only for test purpose. We use this to recheck if + * parental control is enabled or not. + */ + void reloadParentalControlEnabled(); + + /** + * Notifies the TRR service of a TRR that was automatically detected based + * on network preferences. + */ + void setDetectedTrrURI(in AUTF8String aURI); + + /** + * Notifies the DNS service that we failed to connect to this alternative + * endpoint. + * @param aOwnerName + * The owner name of this HTTPS RRs. + * @param aSVCDomainName + * The domain name of this alternative endpoint. + */ + [noscript] void ReportFailedSVCDomainName(in ACString aOwnerName, + in ACString aSVCDomainName); + + /** + * Check if the given domain name was failed to connect to before. + * @param aOwnerName + * The owner name of this HTTPS RRs. + * @param aSVCDomainName + * The domain name of this alternative endpoint. + */ + [noscript] boolean IsSVCDomainNameFailed(in ACString aOwnerName, + in ACString aSVCDomainName); + + /** + * Reset the exclusion list. + * @param aOwnerName + * The owner name of this HTTPS RRs. + */ + [noscript] void ResetExcludedSVCDomainName(in ACString aOwnerName); + + /** + * Returns a string containing the URI currently used by the TRR service. + */ + readonly attribute AUTF8String currentTrrURI; + + /** + * Returns the value of the TRR Service's current default mode. + */ + readonly attribute unsigned long currentTrrMode; + + /** + * @return the hostname of the operating system. + */ + readonly attribute AUTF8String myHostName; + + /************************************************************************* + * Listed below are the various flags that may be OR'd together to form + * the aFlags parameter passed to asyncResolve() and resolve(). + */ + + /** + * if set, this flag suppresses the internal DNS lookup cache. + */ + const unsigned long RESOLVE_BYPASS_CACHE = (1 << 0); + + /** + * if set, the canonical name of the specified host will be queried. + */ + const unsigned long RESOLVE_CANONICAL_NAME = (1 << 1); + + /** + * if set, the query is given lower priority. Medium takes precedence + * if both are used. + */ + const unsigned long RESOLVE_PRIORITY_MEDIUM = (1 << 2); + const unsigned long RESOLVE_PRIORITY_LOW = (1 << 3); + + /** + * if set, indicates request is speculative. Speculative requests + * return errors if prefetching is disabled by configuration. + */ + const unsigned long RESOLVE_SPECULATE = (1 << 4); + + /** + * If set, only IPv4 addresses will be returned from resolve/asyncResolve. + */ + const unsigned long RESOLVE_DISABLE_IPV6 = (1 << 5); + + /** + * If set, only literals and cached entries will be returned from resolve/ + * asyncResolve. + */ + const unsigned long RESOLVE_OFFLINE = (1 << 6); + + /** + * If set, only IPv6 addresses will be returned from resolve/asyncResolve. + */ + const unsigned long RESOLVE_DISABLE_IPV4 = (1 << 7); + + /** + * If set, allow name collision results (127.0.53.53) which are normally filtered. + */ + const unsigned long RESOLVE_ALLOW_NAME_COLLISION = (1 << 8); + + /** + * If set, do not use TRR for resolving the host name. + */ + const unsigned long RESOLVE_DISABLE_TRR = (1 << 9); + + /** + * if set (together with RESOLVE_BYPASS_CACHE), invalidate the DNS + * existing cache entry first (if existing) then make a new resolve. + */ + const unsigned long RESOLVE_REFRESH_CACHE = (1 << 10); + + /** + * These two bits encode the TRR mode of the request. + * Use the static helper methods convert between the TRR mode and flags. + */ + const unsigned long RESOLVE_TRR_MODE_MASK = (1 << 11) | (1 << 12); + const unsigned long RESOLVE_TRR_DISABLED_MODE = (1 << 11); +%{C++ + static uint32_t GetFlagsFromTRRMode(nsIRequest::TRRMode aMode) { + return static_cast<uint32_t>(aMode) << 11; + } + + static nsIRequest::TRRMode GetTRRModeFromFlags(uint32_t aFlags) { + return static_cast<nsIRequest::TRRMode>((aFlags & RESOLVE_TRR_MODE_MASK) >> 11); + } +%} + + /** + * Force resolution even when SOCKS proxy with DNS forwarding is configured. + * Only to be used for the proxy host resolution. + */ + const unsigned long RESOLVE_IGNORE_SOCKS_DNS = 1 << 13; + + /** + * If set, only cached IP hint addresses will be returned from + * resolve/asyncResolve. + */ + const unsigned long RESOLVE_IP_HINT = 1 << 14; +}; + +%{C++ + +/** + * An observer notification for this topic is sent whenever the URI that the + * TRR service is using has changed. + */ +#define NS_NETWORK_TRR_URI_CHANGED_TOPIC "network:trr-uri-changed" + +/** + * An observer notification for this topic is sent whenever the mode that the + * TRR service is using has changed. + */ +#define NS_NETWORK_TRR_MODE_CHANGED_TOPIC "network:trr-mode-changed" + +%} + diff --git a/netwerk/dns/nsIDNService.cpp b/netwerk/dns/nsIDNService.cpp new file mode 100644 index 0000000000..2932a28444 --- /dev/null +++ b/netwerk/dns/nsIDNService.cpp @@ -0,0 +1,925 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "MainThreadUtils.h" +#include "mozilla/Preferences.h" +#include "nsIDNService.h" +#include "nsReadableUtils.h" +#include "nsCRT.h" +#include "nsServiceManagerUtils.h" +#include "nsUnicharUtils.h" +#include "nsUnicodeProperties.h" +#include "nsUnicodeScriptCodes.h" +#include "harfbuzz/hb.h" +#include "punycode.h" +#include "mozilla/ArrayUtils.h" +#include "mozilla/TextUtils.h" +#include "mozilla/Utf8.h" + +// Currently we use the non-transitional processing option -- see +// http://unicode.org/reports/tr46/ +// To switch to transitional processing, change the value of this flag +// and kTransitionalProcessing in netwerk/test/unit/test_idna2008.js to true +// (revert bug 1218179). +const bool kIDNA2008_TransitionalProcessing = false; + +#include "ICUUtils.h" +#include "unicode/uscript.h" + +using namespace mozilla; +using namespace mozilla::unicode; +using namespace mozilla::net; +using mozilla::Preferences; + +//----------------------------------------------------------------------------- +// RFC 1034 - 3.1. Name space specifications and terminology +static const uint32_t kMaxDNSNodeLen = 63; +// RFC 3490 - 5. ACE prefix +static const char kACEPrefix[] = "xn--"; +#define kACEPrefixLen 4 + +//----------------------------------------------------------------------------- + +#define NS_NET_PREF_EXTRAALLOWED "network.IDN.extra_allowed_chars" +#define NS_NET_PREF_EXTRABLOCKED "network.IDN.extra_blocked_chars" +#define NS_NET_PREF_SHOWPUNYCODE "network.IDN_show_punycode" +#define NS_NET_PREF_IDNWHITELIST "network.IDN.whitelist." +#define NS_NET_PREF_IDNUSEWHITELIST "network.IDN.use_whitelist" +#define NS_NET_PREF_IDNRESTRICTION "network.IDN.restriction_profile" + +static inline bool isOnlySafeChars(const nsString& in, + const nsTArray<BlocklistRange>& aBlocklist) { + if (aBlocklist.IsEmpty()) { + return true; + } + const char16_t* cur = in.BeginReading(); + const char16_t* end = in.EndReading(); + + for (; cur < end; ++cur) { + if (CharInBlocklist(*cur, aBlocklist)) { + return false; + } + } + return true; +} + +//----------------------------------------------------------------------------- +// nsIDNService +//----------------------------------------------------------------------------- + +/* Implementation file */ +NS_IMPL_ISUPPORTS(nsIDNService, nsIIDNService, nsISupportsWeakReference) + +static const char* gCallbackPrefs[] = { + NS_NET_PREF_EXTRAALLOWED, NS_NET_PREF_EXTRABLOCKED, + NS_NET_PREF_SHOWPUNYCODE, NS_NET_PREF_IDNRESTRICTION, + NS_NET_PREF_IDNUSEWHITELIST, nullptr, +}; + +nsresult nsIDNService::Init() { + MOZ_ASSERT(NS_IsMainThread()); + MutexAutoLock lock(mLock); + + nsCOMPtr<nsIPrefService> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID)); + if (prefs) { + prefs->GetBranch(NS_NET_PREF_IDNWHITELIST, + getter_AddRefs(mIDNWhitelistPrefBranch)); + } + + Preferences::RegisterPrefixCallbacks(PrefChanged, gCallbackPrefs, this); + prefsChanged(nullptr); + InitializeBlocklist(mIDNBlocklist); + + return NS_OK; +} + +void nsIDNService::prefsChanged(const char* pref) { + MOZ_ASSERT(NS_IsMainThread()); + mLock.AssertCurrentThreadOwns(); + + if (pref && nsLiteralCString(NS_NET_PREF_EXTRAALLOWED).Equals(pref)) { + InitializeBlocklist(mIDNBlocklist); + } + if (pref && nsLiteralCString(NS_NET_PREF_EXTRABLOCKED).Equals(pref)) { + InitializeBlocklist(mIDNBlocklist); + } + if (!pref || nsLiteralCString(NS_NET_PREF_SHOWPUNYCODE).Equals(pref)) { + bool val; + if (NS_SUCCEEDED(Preferences::GetBool(NS_NET_PREF_SHOWPUNYCODE, &val))) { + mShowPunycode = val; + } + } + if (!pref || nsLiteralCString(NS_NET_PREF_IDNUSEWHITELIST).Equals(pref)) { + bool val; + if (NS_SUCCEEDED(Preferences::GetBool(NS_NET_PREF_IDNUSEWHITELIST, &val))) { + mIDNUseWhitelist = val; + } + } + if (!pref || nsLiteralCString(NS_NET_PREF_IDNRESTRICTION).Equals(pref)) { + nsAutoCString profile; + if (NS_FAILED( + Preferences::GetCString(NS_NET_PREF_IDNRESTRICTION, profile))) { + profile.Truncate(); + } + if (profile.EqualsLiteral("moderate")) { + mRestrictionProfile = eModeratelyRestrictiveProfile; + } else if (profile.EqualsLiteral("high")) { + mRestrictionProfile = eHighlyRestrictiveProfile; + } else { + mRestrictionProfile = eASCIIOnlyProfile; + } + } +} + +nsIDNService::nsIDNService() + : mLock("DNService pref value lock"), + mShowPunycode(false), + mRestrictionProfile(static_cast<restrictionProfile>(0)), + mIDNUseWhitelist(false) { + MOZ_ASSERT(NS_IsMainThread()); + + uint32_t IDNAOptions = UIDNA_CHECK_BIDI | UIDNA_CHECK_CONTEXTJ; + if (!kIDNA2008_TransitionalProcessing) { + IDNAOptions |= UIDNA_NONTRANSITIONAL_TO_UNICODE; + } + UErrorCode errorCode = U_ZERO_ERROR; + mIDNA = uidna_openUTS46(IDNAOptions, &errorCode); +} + +nsIDNService::~nsIDNService() { + MOZ_ASSERT(NS_IsMainThread()); + + Preferences::UnregisterPrefixCallbacks(PrefChanged, gCallbackPrefs, this); + + uidna_close(mIDNA); +} + +nsresult nsIDNService::IDNA2008ToUnicode(const nsACString& input, + nsAString& output) { + NS_ConvertUTF8toUTF16 inputStr(input); + UIDNAInfo info = UIDNA_INFO_INITIALIZER; + UErrorCode errorCode = U_ZERO_ERROR; + int32_t inLen = inputStr.Length(); + int32_t outMaxLen = kMaxDNSNodeLen + 1; + UChar outputBuffer[kMaxDNSNodeLen + 1]; + + int32_t outLen = + uidna_labelToUnicode(mIDNA, (const UChar*)inputStr.get(), inLen, + outputBuffer, outMaxLen, &info, &errorCode); + if (info.errors != 0) { + return NS_ERROR_MALFORMED_URI; + } + + if (U_SUCCESS(errorCode)) { + ICUUtils::AssignUCharArrayToString(outputBuffer, outLen, output); + } + + nsresult rv = ICUUtils::UErrorToNsResult(errorCode); + if (rv == NS_ERROR_FAILURE) { + rv = NS_ERROR_MALFORMED_URI; + } + return rv; +} + +nsresult nsIDNService::IDNA2008StringPrep(const nsAString& input, + nsAString& output, + stringPrepFlag flag) { + UIDNAInfo info = UIDNA_INFO_INITIALIZER; + UErrorCode errorCode = U_ZERO_ERROR; + int32_t inLen = input.Length(); + int32_t outMaxLen = kMaxDNSNodeLen + 1; + UChar outputBuffer[kMaxDNSNodeLen + 1]; + + int32_t outLen = + uidna_labelToUnicode(mIDNA, (const UChar*)PromiseFlatString(input).get(), + inLen, outputBuffer, outMaxLen, &info, &errorCode); + nsresult rv = ICUUtils::UErrorToNsResult(errorCode); + if (rv == NS_ERROR_FAILURE) { + rv = NS_ERROR_MALFORMED_URI; + } + NS_ENSURE_SUCCESS(rv, rv); + + // Output the result of nameToUnicode even if there were errors. + // But in the case of invalid punycode, the uidna_labelToUnicode result + // appears to get an appended U+FFFD REPLACEMENT CHARACTER, which will + // confuse our subsequent processing, so we drop that. + // (https://bugzilla.mozilla.org/show_bug.cgi?id=1399540#c9) + if ((info.errors & UIDNA_ERROR_PUNYCODE) && outLen > 0 && + outputBuffer[outLen - 1] == 0xfffd) { + --outLen; + } + ICUUtils::AssignUCharArrayToString(outputBuffer, outLen, output); + + if (flag == eStringPrepIgnoreErrors) { + return NS_OK; + } + + if (info.errors != 0) { + if (flag == eStringPrepForDNS) { + output.Truncate(); + } + rv = NS_ERROR_MALFORMED_URI; + } + + return rv; +} + +NS_IMETHODIMP nsIDNService::ConvertUTF8toACE(const nsACString& input, + nsACString& ace) { + return UTF8toACE(input, ace, eStringPrepForDNS); +} + +nsresult nsIDNService::UTF8toACE(const nsACString& input, nsACString& ace, + stringPrepFlag flag) { + nsresult rv; + NS_ConvertUTF8toUTF16 ustr(input); + + // map ideographic period to ASCII period etc. + normalizeFullStops(ustr); + + uint32_t len, offset; + len = 0; + offset = 0; + nsAutoCString encodedBuf; + + nsAString::const_iterator start, end; + ustr.BeginReading(start); + ustr.EndReading(end); + ace.Truncate(); + + // encode nodes if non ASCII + while (start != end) { + len++; + if (*start++ == (char16_t)'.') { + rv = stringPrepAndACE(Substring(ustr, offset, len - 1), encodedBuf, flag); + NS_ENSURE_SUCCESS(rv, rv); + + ace.Append(encodedBuf); + ace.Append('.'); + offset += len; + len = 0; + } + } + + // encode the last node if non ASCII + if (len) { + rv = stringPrepAndACE(Substring(ustr, offset, len), encodedBuf, flag); + NS_ENSURE_SUCCESS(rv, rv); + + ace.Append(encodedBuf); + } + + return NS_OK; +} + +NS_IMETHODIMP nsIDNService::ConvertACEtoUTF8(const nsACString& input, + nsACString& _retval) { + return ACEtoUTF8(input, _retval, eStringPrepForDNS); +} + +nsresult nsIDNService::ACEtoUTF8(const nsACString& input, nsACString& _retval, + stringPrepFlag flag) { + // RFC 3490 - 4.2 ToUnicode + // ToUnicode never fails. If any step fails, then the original input + // sequence is returned immediately in that step. + // + // Note that this refers to the decoding of a single label. + // ACEtoUTF8 may be called with a sequence of labels separated by dots; + // this test applies individually to each label. + + uint32_t len = 0, offset = 0; + nsAutoCString decodedBuf; + + nsACString::const_iterator start, end; + input.BeginReading(start); + input.EndReading(end); + _retval.Truncate(); + + // loop and decode nodes + while (start != end) { + len++; + if (*start++ == '.') { + nsDependentCSubstring origLabel(input, offset, len - 1); + if (NS_FAILED(decodeACE(origLabel, decodedBuf, flag))) { + // If decoding failed, use the original input sequence + // for this label. + _retval.Append(origLabel); + } else { + _retval.Append(decodedBuf); + } + + _retval.Append('.'); + offset += len; + len = 0; + } + } + // decode the last node + if (len) { + nsDependentCSubstring origLabel(input, offset, len); + if (NS_FAILED(decodeACE(origLabel, decodedBuf, flag))) { + _retval.Append(origLabel); + } else { + _retval.Append(decodedBuf); + } + } + + return NS_OK; +} + +NS_IMETHODIMP nsIDNService::IsACE(const nsACString& input, bool* _retval) { + const char* data = input.BeginReading(); + uint32_t dataLen = input.Length(); + + // look for the ACE prefix in the input string. it may occur + // at the beginning of any segment in the domain name. for + // example: "www.xn--ENCODED.com" + + const char* p = PL_strncasestr(data, kACEPrefix, dataLen); + + *_retval = p && (p == data || *(p - 1) == '.'); + return NS_OK; +} + +NS_IMETHODIMP nsIDNService::Normalize(const nsACString& input, + nsACString& output) { + // protect against bogus input + NS_ENSURE_TRUE(IsUtf8(input), NS_ERROR_UNEXPECTED); + + NS_ConvertUTF8toUTF16 inUTF16(input); + normalizeFullStops(inUTF16); + + // pass the domain name to stringprep label by label + nsAutoString outUTF16, outLabel; + + uint32_t len = 0, offset = 0; + nsresult rv; + nsAString::const_iterator start, end; + inUTF16.BeginReading(start); + inUTF16.EndReading(end); + + while (start != end) { + len++; + if (*start++ == char16_t('.')) { + rv = stringPrep(Substring(inUTF16, offset, len - 1), outLabel, + eStringPrepIgnoreErrors); + NS_ENSURE_SUCCESS(rv, rv); + + outUTF16.Append(outLabel); + outUTF16.Append(char16_t('.')); + offset += len; + len = 0; + } + } + if (len) { + rv = stringPrep(Substring(inUTF16, offset, len), outLabel, + eStringPrepIgnoreErrors); + NS_ENSURE_SUCCESS(rv, rv); + + outUTF16.Append(outLabel); + } + + CopyUTF16toUTF8(outUTF16, output); + return NS_OK; +} + +namespace { + +class MOZ_STACK_CLASS MutexSettableAutoUnlock final { + Mutex* mMutex; + + public: + MutexSettableAutoUnlock() : mMutex(nullptr) {} + + void Acquire(mozilla::Mutex& aMutex) { + MOZ_ASSERT(!mMutex); + mMutex = &aMutex; + mMutex->Lock(); + } + + ~MutexSettableAutoUnlock() { + if (mMutex) { + mMutex->Unlock(); + } + } +}; + +} // anonymous namespace + +NS_IMETHODIMP nsIDNService::ConvertToDisplayIDN(const nsACString& input, + bool* _isASCII, + nsACString& _retval) { + MutexSettableAutoUnlock lock; + if (!NS_IsMainThread()) { + lock.Acquire(mLock); + } + + // If host is ACE, then convert to UTF-8 if the host is in the IDN whitelist. + // Else, if host is already UTF-8, then make sure it is normalized per IDN. + + nsresult rv = NS_OK; + + // Even if the hostname is not ASCII, individual labels may still be ACE, so + // test IsACE before testing IsASCII + bool isACE; + IsACE(input, &isACE); + + if (IsAscii(input)) { + // first, canonicalize the host to lowercase, for whitelist lookup + _retval = input; + ToLowerCase(_retval); + + if (isACE && !mShowPunycode) { + // ACEtoUTF8() can't fail, but might return the original ACE string + nsAutoCString temp(_retval); + // If the domain is in the whitelist, return the host in UTF-8. + // Otherwise convert from ACE to UTF8 only those labels which are + // considered safe for display + ACEtoUTF8( + temp, _retval, + isInWhitelist(temp) ? eStringPrepIgnoreErrors : eStringPrepForUI); + *_isASCII = IsAscii(_retval); + } else { + *_isASCII = true; + } + } else { + // We have to normalize the hostname before testing against the domain + // whitelist (see bug 315411), and to ensure the entire string gets + // normalized. + // + // Normalization and the tests for safe display below, assume that the + // input is Unicode, so first convert any ACE labels to UTF8 + if (isACE) { + nsAutoCString temp; + ACEtoUTF8(input, temp, eStringPrepIgnoreErrors); + rv = Normalize(temp, _retval); + } else { + rv = Normalize(input, _retval); + } + if (NS_FAILED(rv)) { + return rv; + } + + if (mShowPunycode && + NS_SUCCEEDED(UTF8toACE(_retval, _retval, eStringPrepIgnoreErrors))) { + *_isASCII = true; + return NS_OK; + } + + // normalization could result in an ASCII-only hostname. alternatively, if + // the host is converted to ACE by the normalizer, then the host may contain + // unsafe characters, so leave it ACE encoded. see bug 283016, bug 301694, + // and bug 309311. + *_isASCII = IsAscii(_retval); + if (!*_isASCII && !isInWhitelist(_retval)) { + // UTF8toACE with eStringPrepForUI may return a domain name where + // some labels are in UTF-8 and some are in ACE, depending on + // whether they are considered safe for display + rv = UTF8toACE(_retval, _retval, eStringPrepForUI); + *_isASCII = IsAscii(_retval); + return rv; + } + } + + return NS_OK; +} + +//----------------------------------------------------------------------------- + +static nsresult utf16ToUcs4(const nsAString& in, uint32_t* out, + uint32_t outBufLen, uint32_t* outLen) { + uint32_t i = 0; + nsAString::const_iterator start, end; + in.BeginReading(start); + in.EndReading(end); + + while (start != end) { + char16_t curChar; + + curChar = *start++; + + if (start != end && NS_IS_SURROGATE_PAIR(curChar, *start)) { + out[i] = SURROGATE_TO_UCS4(curChar, *start); + ++start; + } else { + out[i] = curChar; + } + + i++; + if (i >= outBufLen) { + return NS_ERROR_MALFORMED_URI; + } + } + out[i] = (uint32_t)'\0'; + *outLen = i; + return NS_OK; +} + +static nsresult punycode(const nsAString& in, nsACString& out) { + uint32_t ucs4Buf[kMaxDNSNodeLen + 1]; + uint32_t ucs4Len = 0u; + nsresult rv = utf16ToUcs4(in, ucs4Buf, kMaxDNSNodeLen, &ucs4Len); + NS_ENSURE_SUCCESS(rv, rv); + + // need maximum 20 bits to encode 16 bit Unicode character + // (include null terminator) + const uint32_t kEncodedBufSize = kMaxDNSNodeLen * 20 / 8 + 1 + 1; + char encodedBuf[kEncodedBufSize]; + punycode_uint encodedLength = kEncodedBufSize; + + enum punycode_status status = + punycode_encode(ucs4Len, ucs4Buf, nullptr, &encodedLength, encodedBuf); + + if (punycode_success != status || encodedLength >= kEncodedBufSize) { + return NS_ERROR_MALFORMED_URI; + } + + encodedBuf[encodedLength] = '\0'; + out.Assign(nsDependentCString(kACEPrefix) + nsDependentCString(encodedBuf)); + + return rv; +} + +// RFC 3454 +// +// 1) Map -- For each character in the input, check if it has a mapping +// and, if so, replace it with its mapping. This is described in section 3. +// +// 2) Normalize -- Possibly normalize the result of step 1 using Unicode +// normalization. This is described in section 4. +// +// 3) Prohibit -- Check for any characters that are not allowed in the +// output. If any are found, return an error. This is described in section +// 5. +// +// 4) Check bidi -- Possibly check for right-to-left characters, and if any +// are found, make sure that the whole string satisfies the requirements +// for bidirectional strings. If the string does not satisfy the requirements +// for bidirectional strings, return an error. This is described in section 6. +// +// 5) Check unassigned code points -- If allowUnassigned is false, check for +// any unassigned Unicode points and if any are found return an error. +// This is described in section 7. +// +nsresult nsIDNService::stringPrep(const nsAString& in, nsAString& out, + stringPrepFlag flag) { + return IDNA2008StringPrep(in, out, flag); +} + +nsresult nsIDNService::stringPrepAndACE(const nsAString& in, nsACString& out, + stringPrepFlag flag) { + nsresult rv = NS_OK; + + out.Truncate(); + + if (in.Length() > kMaxDNSNodeLen) { + NS_WARNING("IDN node too large"); + return NS_ERROR_MALFORMED_URI; + } + + if (IsAscii(in)) { + LossyCopyUTF16toASCII(in, out); + return NS_OK; + } + + nsAutoString strPrep; + rv = stringPrep(in, strPrep, flag); + if (flag == eStringPrepForDNS) { + NS_ENSURE_SUCCESS(rv, rv); + } + + if (IsAscii(strPrep)) { + LossyCopyUTF16toASCII(strPrep, out); + return NS_OK; + } + + if (flag == eStringPrepForUI && NS_SUCCEEDED(rv) && isLabelSafe(in)) { + CopyUTF16toUTF8(strPrep, out); + return NS_OK; + } + + rv = punycode(strPrep, out); + // Check that the encoded output isn't larger than the maximum length + // of a DNS node per RFC 1034. + // This test isn't necessary in the code paths above where the input + // is ASCII (since the output will be the same length as the input) or + // where we convert to UTF-8 (since the output is only used for + // display in the UI and not passed to DNS and can legitimately be + // longer than the limit). + if (out.Length() > kMaxDNSNodeLen) { + NS_WARNING("IDN node too large"); + return NS_ERROR_MALFORMED_URI; + } + + return rv; +} + +// RFC 3490 +// 1) Whenever dots are used as label separators, the following characters +// MUST be recognized as dots: U+002E (full stop), U+3002 (ideographic full +// stop), U+FF0E (fullwidth full stop), U+FF61 (halfwidth ideographic full +// stop). + +void nsIDNService::normalizeFullStops(nsAString& s) { + nsAString::const_iterator start, end; + s.BeginReading(start); + s.EndReading(end); + int32_t index = 0; + + while (start != end) { + switch (*start) { + case 0x3002: + case 0xFF0E: + case 0xFF61: + s.ReplaceLiteral(index, 1, u"."); + break; + default: + break; + } + start++; + index++; + } +} + +nsresult nsIDNService::decodeACE(const nsACString& in, nsACString& out, + stringPrepFlag flag) { + bool isAce; + IsACE(in, &isAce); + if (!isAce) { + out.Assign(in); + return NS_OK; + } + + nsAutoString utf16; + nsresult result = IDNA2008ToUnicode(in, utf16); + NS_ENSURE_SUCCESS(result, result); + + if (flag != eStringPrepForUI || isLabelSafe(utf16)) { + CopyUTF16toUTF8(utf16, out); + } else { + out.Assign(in); + return NS_OK; + } + + // Validation: encode back to ACE and compare the strings + nsAutoCString ace; + nsresult rv = UTF8toACE(out, ace, flag); + NS_ENSURE_SUCCESS(rv, rv); + + if (flag == eStringPrepForDNS && + !ace.Equals(in, nsCaseInsensitiveCStringComparator)) { + return NS_ERROR_MALFORMED_URI; + } + + return NS_OK; +} + +bool nsIDNService::isInWhitelist(const nsACString& host) { + if (!NS_IsMainThread()) { + mLock.AssertCurrentThreadOwns(); + } + + if (mIDNUseWhitelist && mIDNWhitelistPrefBranch) { + nsAutoCString tld(host); + // make sure the host is ACE for lookup and check that there are no + // unassigned codepoints + if (!IsAscii(tld) && NS_FAILED(UTF8toACE(tld, tld, eStringPrepForDNS))) { + return false; + } + + // truncate trailing dots first + tld.Trim("."); + int32_t pos = tld.RFind("."); + if (pos == kNotFound) { + return false; + } + + tld.Cut(0, pos + 1); + + bool safe; + if (NS_SUCCEEDED(mIDNWhitelistPrefBranch->GetBoolPref(tld.get(), &safe))) { + return safe; + } + } + + return false; +} + +bool nsIDNService::isLabelSafe(const nsAString& label) { + if (!NS_IsMainThread()) { + mLock.AssertCurrentThreadOwns(); + } + + if (!isOnlySafeChars(PromiseFlatString(label), mIDNBlocklist)) { + return false; + } + + // We should never get here if the label is ASCII + NS_ASSERTION(!IsAscii(label), "ASCII label in IDN checking"); + if (mRestrictionProfile == eASCIIOnlyProfile) { + return false; + } + + nsAString::const_iterator current, end; + label.BeginReading(current); + label.EndReading(end); + + Script lastScript = Script::INVALID; + uint32_t previousChar = 0; + uint32_t baseChar = 0; // last non-diacritic seen (base char for marks) + uint32_t savedNumberingSystem = 0; +// Simplified/Traditional Chinese check temporarily disabled -- bug 857481 +#if 0 + HanVariantType savedHanVariant = HVT_NotHan; +#endif + + int32_t savedScript = -1; + + while (current != end) { + uint32_t ch = *current++; + + if (current != end && NS_IS_SURROGATE_PAIR(ch, *current)) { + ch = SURROGATE_TO_UCS4(ch, *current++); + } + + IdentifierType idType = GetIdentifierType(ch); + if (idType == IDTYPE_RESTRICTED) { + return false; + } + MOZ_ASSERT(idType == IDTYPE_ALLOWED); + + // Check for mixed script + Script script = GetScriptCode(ch); + if (script != Script::COMMON && script != Script::INHERITED && + script != lastScript) { + if (illegalScriptCombo(script, savedScript)) { + return false; + } + } + + // Check for mixed numbering systems + auto genCat = GetGeneralCategory(ch); + if (genCat == HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) { + uint32_t zeroCharacter = ch - GetNumericValue(ch); + if (savedNumberingSystem == 0) { + // If we encounter a decimal number, save the zero character from that + // numbering system. + savedNumberingSystem = zeroCharacter; + } else if (zeroCharacter != savedNumberingSystem) { + return false; + } + } + + if (genCat == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) { + // Check for consecutive non-spacing marks. + if (previousChar != 0 && previousChar == ch) { + return false; + } + // Check for marks whose expected script doesn't match the base script. + if (lastScript != Script::INVALID) { + const size_t kMaxScripts = 32; // more than ample for current values + // of ScriptExtensions property + UScriptCode scripts[kMaxScripts]; + UErrorCode errorCode = U_ZERO_ERROR; + int nScripts = + uscript_getScriptExtensions(ch, scripts, kMaxScripts, &errorCode); + MOZ_ASSERT(U_SUCCESS(errorCode), "uscript_getScriptExtensions failed"); + if (U_FAILURE(errorCode)) { + return false; + } + // nScripts will always be >= 1, because even for undefined characters + // uscript_getScriptExtensions will return Script::INVALID. + // If the mark just has script=COMMON or INHERITED, we can't check any + // more carefully, but if it has specific scriptExtension codes, then + // assume those are the only valid scripts to use it with. + if (nScripts > 1 || (Script(scripts[0]) != Script::COMMON && + Script(scripts[0]) != Script::INHERITED)) { + while (--nScripts >= 0) { + if (Script(scripts[nScripts]) == lastScript) { + break; + } + } + if (nScripts == -1) { + return false; + } + } + } + // Check for diacritics on dotless-i, which would be indistinguishable + // from normal accented letter i. + if (baseChar == 0x0131 && + ((ch >= 0x0300 && ch <= 0x0314) || ch == 0x031a)) { + return false; + } + } else { + baseChar = ch; + } + + if (script != Script::COMMON && script != Script::INHERITED) { + lastScript = script; + } + + // Simplified/Traditional Chinese check temporarily disabled -- bug 857481 +#if 0 + + // Check for both simplified-only and traditional-only Chinese characters + HanVariantType hanVariant = GetHanVariant(ch); + if (hanVariant == HVT_SimplifiedOnly || hanVariant == HVT_TraditionalOnly) { + if (savedHanVariant == HVT_NotHan) { + savedHanVariant = hanVariant; + } else if (hanVariant != savedHanVariant) { + return false; + } + } +#endif + + previousChar = ch; + } + return true; +} + +// Scripts that we care about in illegalScriptCombo +static const Script scriptTable[] = { + Script::BOPOMOFO, Script::CYRILLIC, Script::GREEK, Script::HANGUL, + Script::HAN, Script::HIRAGANA, Script::KATAKANA, Script::LATIN}; + +#define BOPO 0 +#define CYRL 1 +#define GREK 2 +#define HANG 3 +#define HANI 4 +#define HIRA 5 +#define KATA 6 +#define LATN 7 +#define OTHR 8 +#define JPAN 9 // Latin + Han + Hiragana + Katakana +#define CHNA 10 // Latin + Han + Bopomofo +#define KORE 11 // Latin + Han + Hangul +#define HNLT 12 // Latin + Han (could be any of the above combinations) +#define FAIL 13 + +static inline int32_t findScriptIndex(Script aScript) { + int32_t tableLength = mozilla::ArrayLength(scriptTable); + for (int32_t index = 0; index < tableLength; ++index) { + if (aScript == scriptTable[index]) { + return index; + } + } + return OTHR; +} + +static const int32_t scriptComboTable[13][9] = { + /* thisScript: BOPO CYRL GREK HANG HANI HIRA KATA LATN OTHR + * savedScript */ + /* BOPO */ {BOPO, FAIL, FAIL, FAIL, CHNA, FAIL, FAIL, CHNA, FAIL}, + /* CYRL */ {FAIL, CYRL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL}, + /* GREK */ {FAIL, FAIL, GREK, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL}, + /* HANG */ {FAIL, FAIL, FAIL, HANG, KORE, FAIL, FAIL, KORE, FAIL}, + /* HANI */ {CHNA, FAIL, FAIL, KORE, HANI, JPAN, JPAN, HNLT, FAIL}, + /* HIRA */ {FAIL, FAIL, FAIL, FAIL, JPAN, HIRA, JPAN, JPAN, FAIL}, + /* KATA */ {FAIL, FAIL, FAIL, FAIL, JPAN, JPAN, KATA, JPAN, FAIL}, + /* LATN */ {CHNA, FAIL, FAIL, KORE, HNLT, JPAN, JPAN, LATN, OTHR}, + /* OTHR */ {FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, OTHR, FAIL}, + /* JPAN */ {FAIL, FAIL, FAIL, FAIL, JPAN, JPAN, JPAN, JPAN, FAIL}, + /* CHNA */ {CHNA, FAIL, FAIL, FAIL, CHNA, FAIL, FAIL, CHNA, FAIL}, + /* KORE */ {FAIL, FAIL, FAIL, KORE, KORE, FAIL, FAIL, KORE, FAIL}, + /* HNLT */ {CHNA, FAIL, FAIL, KORE, HNLT, JPAN, JPAN, HNLT, FAIL}}; + +bool nsIDNService::illegalScriptCombo(Script script, int32_t& savedScript) { + if (!NS_IsMainThread()) { + mLock.AssertCurrentThreadOwns(); + } + + if (savedScript == -1) { + savedScript = findScriptIndex(script); + return false; + } + + savedScript = scriptComboTable[savedScript][findScriptIndex(script)]; + /* + * Special case combinations that depend on which profile is in use + * In the Highly Restrictive profile Latin is not allowed with any + * other script + * + * In the Moderately Restrictive profile Latin mixed with any other + * single script is allowed. + */ + return ((savedScript == OTHR && + mRestrictionProfile == eHighlyRestrictiveProfile) || + savedScript == FAIL); +} + +#undef BOPO +#undef CYRL +#undef GREK +#undef HANG +#undef HANI +#undef HIRA +#undef KATA +#undef LATN +#undef OTHR +#undef JPAN +#undef CHNA +#undef KORE +#undef HNLT +#undef FAIL diff --git a/netwerk/dns/nsIDNService.h b/netwerk/dns/nsIDNService.h new file mode 100644 index 0000000000..6fef8e7670 --- /dev/null +++ b/netwerk/dns/nsIDNService.h @@ -0,0 +1,205 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsIDNService_h__ +#define nsIDNService_h__ + +#include "nsIIDNService.h" +#include "nsCOMPtr.h" +#include "nsUnicodeScriptCodes.h" +#include "nsWeakReference.h" + +#include "unicode/uidna.h" +#include "mozilla/Mutex.h" +#include "mozilla/net/IDNBlocklistUtils.h" + +#include "nsString.h" + +class nsIPrefBranch; + +//----------------------------------------------------------------------------- +// nsIDNService +//----------------------------------------------------------------------------- + +class nsIDNService final : public nsIIDNService, + public nsSupportsWeakReference { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIIDNSERVICE + + nsIDNService(); + + nsresult Init(); + + protected: + virtual ~nsIDNService(); + + private: + enum stringPrepFlag { + eStringPrepForDNS, + eStringPrepForUI, + eStringPrepIgnoreErrors + }; + + /** + * Convert the following characters that must be recognized as label + * separators per RFC 3490 to ASCII full stop characters + * + * U+3002 (ideographic full stop) + * U+FF0E (fullwidth full stop) + * U+FF61 (halfwidth ideographic full stop) + */ + void normalizeFullStops(nsAString& s); + + /** + * Convert and encode a DNS label in ACE/punycode. + * @param flag + * if eStringPrepIgnoreErrors, all non-ASCII labels are + * converted to punycode. + * if eStringPrepForUI, only labels that are considered safe + * for display are converted. + * @see isLabelSafe + * if eStringPrepForDNS and stringPrep finds an illegal + * character, returns NS_FAILURE and out is empty + */ + nsresult stringPrepAndACE(const nsAString& in, nsACString& out, + stringPrepFlag flag); + + /** + * Convert a DNS label using the stringprep profile defined in RFC 3454 + */ + nsresult stringPrep(const nsAString& in, nsAString& out, stringPrepFlag flag); + + /** + * Decode an ACE-encoded DNS label to UTF-8 + * + * @param flag + * if eStringPrepForUI and the label is not considered safe to + * display, the output is the same as the input + * @see isLabelSafe + */ + nsresult decodeACE(const nsACString& in, nsACString& out, + stringPrepFlag flag); + + /** + * Convert complete domain names between UTF8 and ACE and vice versa + * + * @param flag is passed to decodeACE or stringPrepAndACE for each + * label individually, so the output may contain some labels in + * punycode and some in UTF-8 + */ + nsresult UTF8toACE(const nsACString& input, nsACString& ace, + stringPrepFlag flag); + nsresult ACEtoUTF8(const nsACString& input, nsACString& _retval, + stringPrepFlag flag); + + bool isInWhitelist(const nsACString& host); + void prefsChanged(const char* pref); + + static void PrefChanged(const char* aPref, void* aSelf) { + auto* self = static_cast<nsIDNService*>(aSelf); + mozilla::MutexAutoLock lock(self->mLock); + self->prefsChanged(aPref); + } + + /** + * Determine whether a label is considered safe to display to the user + * according to the algorithm defined in UTR 39 and the profile + * selected in mRestrictionProfile. + * + * For the ASCII-only profile, returns false for all labels containing + * non-ASCII characters. + * + * For the other profiles, returns false for labels containing any of + * the following: + * + * Characters in scripts other than the "recommended scripts" and + * "aspirational scripts" defined in + * http://www.unicode.org/reports/tr31/#Table_Recommended_Scripts + * and http://www.unicode.org/reports/tr31/#Aspirational_Use_Scripts + * This includes codepoints that are not defined as Unicode + * characters + * + * Illegal combinations of scripts (@see illegalScriptCombo) + * + * Numbers from more than one different numbering system + * + * Sequences of the same non-spacing mark + * + * Both simplified-only and traditional-only Chinese characters + * XXX this test was disabled by bug 857481 + */ + bool isLabelSafe(const nsAString& label); + + /** + * Determine whether a combination of scripts in a single label is + * permitted according to the algorithm defined in UTR 39 and the + * profile selected in mRestrictionProfile. + * + * For the "Highly restrictive" profile, all characters in each + * identifier must be from a single script, or from the combinations: + * Latin + Han + Hiragana + Katakana; + * Latin + Han + Bopomofo; or + * Latin + Han + Hangul + * + * For the "Moderately restrictive" profile, Latin is also allowed + * with other scripts except Cyrillic and Greek + */ + bool illegalScriptCombo(mozilla::unicode::Script script, + int32_t& savedScript); + + /** + * Convert a DNS label from ASCII to Unicode using IDNA2008 + */ + nsresult IDNA2008ToUnicode(const nsACString& input, nsAString& output); + + /** + * Convert a DNS label to a normalized form conforming to IDNA2008 + */ + nsresult IDNA2008StringPrep(const nsAString& input, nsAString& output, + stringPrepFlag flag); + + UIDNA* mIDNA; + + // We use this mutex to guard access to: + // |mIDNBlocklist|, |mShowPunycode|, |mRestrictionProfile|, + // |mIDNUseWhitelist|. + // + // These members can only be updated on the main thread and + // read on any thread. Therefore, acquiring the mutex is required + // only for threads other than the main thread. + mozilla::Mutex mLock; + + // guarded by mLock + nsTArray<mozilla::net::BlocklistRange> mIDNBlocklist; + + /** + * Flag set by the pref network.IDN_show_punycode. When it is true, + * IDNs containing non-ASCII characters are always displayed to the + * user in punycode + * + * guarded by mLock + */ + bool mShowPunycode; + + /** + * Restriction-level Detection profiles defined in UTR 39 + * http://www.unicode.org/reports/tr39/#Restriction_Level_Detection, + * and selected by the pref network.IDN.restriction_profile + */ + enum restrictionProfile { + eASCIIOnlyProfile, + eHighlyRestrictiveProfile, + eModeratelyRestrictiveProfile + }; + // guarded by mLock; + restrictionProfile mRestrictionProfile; + // guarded by mLock; + nsCOMPtr<nsIPrefBranch> mIDNWhitelistPrefBranch; + // guarded by mLock + bool mIDNUseWhitelist; +}; + +#endif // nsIDNService_h__ diff --git a/netwerk/dns/nsIEffectiveTLDService.idl b/netwerk/dns/nsIEffectiveTLDService.idl new file mode 100644 index 0000000000..32addd31fe --- /dev/null +++ b/netwerk/dns/nsIEffectiveTLDService.idl @@ -0,0 +1,158 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsIURI; + +[scriptable, uuid(68067eb5-ad8d-43cb-a043-1cc85ebe06e7)] +interface nsIEffectiveTLDService : nsISupports +{ + /** + * Returns the public suffix of a URI. A public suffix is the highest-level domain + * under which individual domains may be registered; it may therefore contain one + * or more dots. For example, the public suffix for "www.bbc.co.uk" is "co.uk", + * because the .uk TLD does not allow the registration of domains at the + * second level ("bbc.uk" is forbidden). + * + * The public suffix will be returned encoded in ASCII/ACE and will be normalized + * according to RFC 3454, i.e. the same encoding returned by nsIURI::GetAsciiHost(). + * If consumers wish to compare the result of this method against the host from + * another nsIURI, the host should be obtained using nsIURI::GetAsciiHost(). + * In the case of nested URIs, the innermost URI will be used. + * + * @param aURI The URI to be analyzed + * + * @returns the public suffix + * + * @throws NS_ERROR_UNEXPECTED + * or other error returned by nsIIDNService::normalize when + * the hostname contains characters disallowed in URIs + * @throws NS_ERROR_HOST_IS_IP_ADDRESS + * if the host is a numeric IPv4 or IPv6 address (as determined by + * the success of a call to PR_StringToNetAddr()). + */ + ACString getPublicSuffix(in nsIURI aURI); + + /** + * Similar to getPublicSuffix, but the suffix is validated against + * the Public Suffix List. If the suffix is unknown this will return + * an empty string. + * + * @param aURI The URI to be analyzed + * @returns the public suffix if known, an empty string otherwise + * @see getPublicSuffixFromHost() + */ + ACString getKnownPublicSuffix(in nsIURI aURI); + + /** + * Returns the base domain of a URI; that is, the public suffix with a given + * number of additional domain name parts. For example, the result of this method + * for "www.bbc.co.uk", depending on the value of aAdditionalParts parameter, will + * be: + * + * 0 (default) -> bbc.co.uk + * 1 -> www.bbc.co.uk + * + * Similarly, the public suffix for "www.developer.mozilla.org" is "org", and the base + * domain will be: + * + * 0 (default) -> mozilla.org + * 1 -> developer.mozilla.org + * 2 -> www.developer.mozilla.org + * + * The base domain will be returned encoded in ASCII/ACE and will be normalized + * according to RFC 3454, i.e. the same encoding returned by nsIURI::GetAsciiHost(). + * If consumers wish to compare the result of this method against the host from + * another nsIURI, the host should be obtained using nsIURI::GetAsciiHost(). + * In the case of nested URIs, the innermost URI will be used. + * + * @param aURI The URI to be analyzed + * @param aAdditionalParts Number of domain name parts to be + * returned in addition to the public suffix + * + * @returns the base domain (public suffix plus the requested number of additional parts) + * + * @throws NS_ERROR_UNEXPECTED + * or other error returned by nsIIDNService::normalize when + * the hostname contains characters disallowed in URIs + * @throws NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS + * when there are insufficient subdomain levels in the hostname to satisfy the + * requested aAdditionalParts value. + * @throws NS_ERROR_HOST_IS_IP_ADDRESS + * if aHost is a numeric IPv4 or IPv6 address (as determined by + * the success of a call to PR_StringToNetAddr()). + * + * @see getPublicSuffix() + */ + ACString getBaseDomain(in nsIURI aURI, [optional] in uint32_t aAdditionalParts); + + /** + * NOTE: It is strongly recommended to use getPublicSuffix() above if a suitable + * nsIURI is available. Only use this method if this is not the case. + * + * Returns the public suffix of a host string. Otherwise identical to getPublicSuffix(). + * + * @param aHost The host to be analyzed. Any additional parts (e.g. scheme, + * port, or path) will cause this method to throw. ASCII/ACE and + * UTF8 encodings are acceptable as input; normalization will + * be performed as specified in getBaseDomain(). + * + * @see getPublicSuffix() + */ + ACString getPublicSuffixFromHost(in AUTF8String aHost); + + /** + * Similar to getPublicSuffixFromHost, but the suffix is validated against + * the Public Suffix List. If the suffix is unknown this will return + * an empty string. + * + * @param aHost The host to be analyzed. + * @returns the public suffix if known, an empty string otherwise + * @see getPublicSuffixFromHost() + */ + ACString getKnownPublicSuffixFromHost(in AUTF8String aHost); + + /** + * NOTE: It is strongly recommended to use getBaseDomain() above if a suitable + * nsIURI is available. Only use this method if this is not the case. + * + * Returns the base domain of a host string. Otherwise identical to getBaseDomain(). + * + * @param aHost The host to be analyzed. Any additional parts (e.g. scheme, + * port, or path) will cause this method to throw. ASCII/ACE and + * UTF8 encodings are acceptable as input; normalization will + * be performed as specified in getBaseDomain(). + * + * @see getBaseDomain() + */ + ACString getBaseDomainFromHost(in AUTF8String aHost, [optional] in uint32_t aAdditionalParts); + + /** + * Returns the parent sub-domain of a host string. If the host is a base + * domain, it will throw NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS. + * + * For example: "player.bbc.co.uk" would return "bbc.co.uk" and + * "bbc.co.uk" would throw NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS. + * + * @param aHost The host to be analyzed. Any additional parts (e.g. scheme, + * port, or path) will cause this method to throw. ASCII/ACE and + * UTF8 encodings are acceptable as input; normalization will + * be performed as specified in getBaseDomain(). + */ + ACString getNextSubDomain(in AUTF8String aHost); + + /** + * Returns true if the |aInput| in is part of the root domain of |aHost|. + * For example, if |aInput| is "www.mozilla.org", and we pass in + * "mozilla.org" as |aHost|, this will return true. It would return false + * the other way around. + * + * @param aInput The host to be analyzed. + * @param aHost The host to compare to. + */ + bool hasRootDomain(in AUTF8String aInput, in AUTF8String aHost); +}; + diff --git a/netwerk/dns/nsIIDNService.idl b/netwerk/dns/nsIIDNService.idl new file mode 100644 index 0000000000..47ef561237 --- /dev/null +++ b/netwerk/dns/nsIIDNService.idl @@ -0,0 +1,58 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +/** + * nsIIDNService interface. + * + * IDN (Internationalized Domain Name) support. Provides facilities + * for manipulating IDN hostnames according to the specification set + * forth by the IETF. + * + * IDN effort: + * http://www.ietf.org/html.characters/idn-charter.html + * http://www.i-dns.net + * + * IDNA specification: + * http://search.ietf.org/internet-drafts/draft-ietf-idn-idna-06.txt + */ + +[scriptable, uuid(a592a60e-3621-4f19-a318-2bf233cfad3e)] +interface nsIIDNService : nsISupports +{ + /** + * Prepares the input hostname according to IDNA ToASCII operation, + * the input hostname is assumed to be UTF8-encoded. + */ + ACString convertUTF8toACE(in AUTF8String input); + + + /** + * This is the ToUnicode operation as specified in the IDNA proposal, + * with an additional step to encode the result in UTF-8. + * It takes an ACE-encoded hostname and performs ToUnicode to it, then + * encodes the resulting string into UTF8. + */ + AUTF8String convertACEtoUTF8(in ACString input); + + /** + * Checks if the input string is ACE encoded or not. + */ + boolean isACE(in ACString input); + + /** + * Performs the unicode normalization needed for hostnames in IDN, + * for callers that want early normalization. + */ + AUTF8String normalize(in AUTF8String input); + + /** + * Normalizes and converts a host to UTF-8 if the host is in the IDN + * whitelist, otherwise converts it to ACE. This is useful for display + * purposes and to ensure an encoding consistent with nsIURI::GetHost(). + * If the result is ASCII or ACE encoded, |isASCII| will be true. + */ + AUTF8String convertToDisplayIDN(in AUTF8String input, out boolean isASCII); +}; diff --git a/netwerk/dns/nsINativeDNSResolverOverride.idl b/netwerk/dns/nsINativeDNSResolverOverride.idl new file mode 100644 index 0000000000..874328fabc --- /dev/null +++ b/netwerk/dns/nsINativeDNSResolverOverride.idl @@ -0,0 +1,29 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +[scriptable, builtinclass, uuid(8e38d536-5501-48c0-a412-6c450040c8c8)] +interface nsINativeDNSResolverOverride : nsISupports +{ + /** + * Adds an IP override for this specific host. + */ + void addIPOverride(in AUTF8String aHost, in ACString aIPLiteral); + + /** + * Sets a CNAME override for this specific host. + */ + void setCnameOverride(in AUTF8String aHost, in ACString aCNAME); + + /** + * Clears the overrides for this specific host + */ + void clearHostOverride(in AUTF8String aHost); + + /** + * Clears all the host overrides that were previously set. + */ + void clearOverrides(); +}; diff --git a/netwerk/dns/nsPIDNSService.idl b/netwerk/dns/nsPIDNSService.idl new file mode 100644 index 0000000000..6d65e6be41 --- /dev/null +++ b/netwerk/dns/nsPIDNSService.idl @@ -0,0 +1,34 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsIDNSService.idl" + +/** + * This is a private interface used by the internals of the networking library. + * It will never be frozen. Do not use it in external code. + */ +[scriptable, builtinclass, uuid(24e598fd-7b1a-436c-9154-14d8b38df8a5)] +interface nsPIDNSService : nsIDNSService +{ + /** + * called to initialize the DNS service. + */ + void init(); + + /** + * called to shutdown the DNS service. any pending asynchronous + * requests will be canceled, and the local cache of DNS records + * will be cleared. NOTE: the operating system may still have + * its own cache of DNS records, which would be unaffected by + * this method. + */ + void shutdown(); + + /** + * Whether or not DNS prefetching (aka RESOLVE_SPECULATE) is enabled + */ + attribute boolean prefetchEnabled; +}; diff --git a/netwerk/dns/prepare_tlds.py b/netwerk/dns/prepare_tlds.py new file mode 100644 index 0000000000..53d0bf526d --- /dev/null +++ b/netwerk/dns/prepare_tlds.py @@ -0,0 +1,152 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import codecs +import encodings.idna +import imp +import os +import re +import sys +from make_dafsa import words_to_cxx, words_to_bin + +""" +Processes a file containing effective TLD data. See the following URL for a +description of effective TLDs and of the file format that this script +processes (although for the latter you're better off just reading this file's +short source code). + +http://wiki.mozilla.org/Gecko:Effective_TLD_Service +""" + + +def getEffectiveTLDs(path): + file = codecs.open(path, "r", "UTF-8") + entries = [] + domains = set() + for line in file: + # line always contains a line terminator unless the file is empty + if len(line) == 0: + raise StopIteration + line = line.rstrip() + # comment, empty, or superfluous line for explicitness purposes + if line.startswith("//") or not line.strip(): + continue + line = re.split(r"[ \t\n]", line, 1)[0] + entry = EffectiveTLDEntry(line) + domain = entry.domain() + assert domain not in domains, "repeating domain %s makes no sense" % domain + domains.add(domain) + yield entry + + +def _normalizeHostname(domain): + """ + Normalizes the given domain, component by component. ASCII components are + lowercased, while non-ASCII components are processed using the ToASCII + algorithm. + """ + + def convertLabel(label): + if _isASCII(label): + return label.lower() + return encodings.idna.ToASCII(label).decode("utf-8") + + return ".".join(map(convertLabel, domain.split("."))) + + +def _isASCII(s): + "True if s consists entirely of ASCII characters, false otherwise." + for c in s: + if ord(c) > 127: + return False + return True + + +class EffectiveTLDEntry: + """ + Stores an entry in an effective-TLD name file. + """ + + _exception = False + _wild = False + + def __init__(self, line): + """ + Creates a TLD entry from a line of data, which must have been stripped of + the line ending. + """ + if line.startswith("!"): + self._exception = True + domain = line[1:] + elif line.startswith("*."): + self._wild = True + domain = line[2:] + else: + domain = line + self._domain = _normalizeHostname(domain) + + def domain(self): + "The domain this represents." + return self._domain + + def exception(self): + "True if this entry's domain denotes does not denote an effective TLD." + return self._exception + + def wild(self): + "True if this entry represents a class of effective TLDs." + return self._wild + + +################# +# DO EVERYTHING # +################# + + +def main(output, effective_tld_filename, output_format="cxx"): + """ + effective_tld_filename is the effective TLD file to parse. + based on the output format, either a C++ array of a binary representation + of a DAFSA representing the eTLD file is then printed to standard output + or a binary file is written to disk. + """ + + def typeEnum(etld): + """ + Maps the flags to the DAFSA's enum types. + """ + if etld.exception(): + return 1 + elif etld.wild(): + return 2 + else: + return 0 + + def dafsa_words(): + """ + make_dafsa expects lines of the form "<domain_name><enum_value>" + """ + for etld in getEffectiveTLDs(effective_tld_filename): + yield "%s%d" % (etld.domain(), typeEnum(etld)) + + """ words_to_bin() returns a bytes while words_to_cxx() returns string """ + if output_format == "bin": + output.write(words_to_bin(dafsa_words())) + else: + output.write(words_to_cxx(dafsa_words())) + + +if __name__ == "__main__": + """ + This program can output the DAFSA in two formats: + as C++ code that will be included and compiled at build time + or as a binary file that will be published in Remote Settings. + + Flags for format options: + "cxx" -> C++ array [default] + "bin" -> Binary file + """ + + output_format = "bin" if "--bin" in sys.argv else "cxx" + main(sys.stdout, sys.argv[1], output_format=output_format) diff --git a/netwerk/dns/punycode.c b/netwerk/dns/punycode.c new file mode 100644 index 0000000000..4653216507 --- /dev/null +++ b/netwerk/dns/punycode.c @@ -0,0 +1,325 @@ +/* +punycode.c from RFC 3492 +http://www.nicemice.net/idn/ +Adam M. Costello +http://www.nicemice.net/amc/ + +This is ANSI C code (C89) implementing Punycode (RFC 3492). + + +C. Disclaimer and license + + Regarding this entire document or any portion of it (including + the pseudocode and C code), the author makes no guarantees and + is not responsible for any damage resulting from its use. The + author grants irrevocable permission to anyone to use, modify, + and distribute it in any way that does not diminish the rights + of anyone else to use, modify, and distribute it, provided that + redistributed derivative works do not contain misleading author or + version information. Derivative works need not be licensed under + similar terms. +*/ + +#include "punycode.h" + +/**********************************************************/ +/* Implementation (would normally go in its own .c file): */ + +#include <string.h> + +/*** Bootstring parameters for Punycode ***/ + +enum { + base = 36, + tmin = 1, + tmax = 26, + skew = 38, + damp = 700, + initial_bias = 72, + initial_n = 0x80, + delimiter = 0x2D +}; + +/* basic(cp) tests whether cp is a basic code point: */ +#define basic(cp) ((punycode_uint)(cp) < 0x80) + +/* delim(cp) tests whether cp is a delimiter: */ +#define delim(cp) ((cp) == delimiter) + +/* decode_digit(cp) returns the numeric value of a basic code */ +/* point (for use in representing integers) in the range 0 to */ +/* base-1, or base if cp is does not represent a value. */ + +static punycode_uint decode_digit(punycode_uint cp) { + return cp - 48 < 10 ? cp - 22 + : cp - 65 < 26 ? cp - 65 + : cp - 97 < 26 ? cp - 97 + : base; +} + +/* encode_digit(d,flag) returns the basic code point whose value */ +/* (when used for representing integers) is d, which needs to be in */ +/* the range 0 to base-1. The lowercase form is used unless flag is */ +/* nonzero, in which case the uppercase form is used. The behavior */ +/* is undefined if flag is nonzero and digit d has no uppercase form. */ + +static char encode_digit(punycode_uint d, int flag) { + return d + 22 + 75 * (d < 26) - ((flag != 0) << 5); + /* 0..25 map to ASCII a..z or A..Z */ + /* 26..35 map to ASCII 0..9 */ +} + +/* flagged(bcp) tests whether a basic code point is flagged */ +/* (uppercase). The behavior is undefined if bcp is not a */ +/* basic code point. */ + +#define flagged(bcp) ((punycode_uint)(bcp)-65 < 26) + +/* encode_basic(bcp,flag) forces a basic code point to lowercase */ +/* if flag is zero, uppercase if flag is nonzero, and returns */ +/* the resulting code point. The code point is unchanged if it */ +/* is caseless. The behavior is undefined if bcp is not a basic */ +/* code point. */ + +static char encode_basic(punycode_uint bcp, int flag) { + bcp -= (bcp - 97 < 26) << 5; + return bcp + ((!flag && (bcp - 65 < 26)) << 5); +} + +/*** Platform-specific constants ***/ + +/* maxint is the maximum value of a punycode_uint variable: */ +static const punycode_uint maxint = (punycode_uint)-1; +/* Because maxint is unsigned, -1 becomes the maximum value. */ + +/*** Bias adaptation function ***/ + +static punycode_uint adapt(punycode_uint delta, punycode_uint numpoints, + int firsttime) { + punycode_uint k; + + delta = firsttime ? delta / damp : delta >> 1; + /* delta >> 1 is a faster way of doing delta / 2 */ + delta += delta / numpoints; + + for (k = 0; delta > ((base - tmin) * tmax) / 2; k += base) { + delta /= base - tmin; + } + + return k + (base - tmin + 1) * delta / (delta + skew); +} + +/*** Main encode function ***/ + +enum punycode_status punycode_encode(punycode_uint input_length, + const punycode_uint input[], + const unsigned char case_flags[], + punycode_uint* output_length, + char output[]) { + punycode_uint n, delta, h, b, out, max_out, bias, j, m, q, k, t; + + /* Initialize the state: */ + + n = initial_n; + delta = out = 0; + max_out = *output_length; + bias = initial_bias; + + /* Handle the basic code points: */ + + for (j = 0; j < input_length; ++j) { + if (basic(input[j])) { + if (max_out - out < 2) { + return punycode_big_output; + } + output[out++] = + case_flags ? encode_basic(input[j], case_flags[j]) : (char)input[j]; + } + /* else if (input[j] < n) return punycode_bad_input; */ + /* (not needed for Punycode with unsigned code points) */ + } + + h = b = out; + + /* h is the number of code points that have been handled, b is the */ + /* number of basic code points, and out is the number of characters */ + /* that have been output. */ + + if (b > 0) { + output[out++] = delimiter; + } + + /* Main encoding loop: */ + + while (h < input_length) { + /* All non-basic code points < n have been */ + /* handled already. Find the next larger one: */ + + for (m = maxint, j = 0; j < input_length; ++j) { + /* if (basic(input[j])) continue; */ + /* (not needed for Punycode) */ + if (input[j] >= n && input[j] < m) { + m = input[j]; + } + } + + /* Increase delta enough to advance the decoder's */ + /* <n,i> state to <m,0>, but guard against overflow: */ + + if (m - n > (maxint - delta) / (h + 1)) { + return punycode_overflow; + } + delta += (m - n) * (h + 1); + n = m; + + for (j = 0; j < input_length; ++j) { + /* Punycode does not need to check whether input[j] is basic: */ + if (input[j] < n /* || basic(input[j]) */) { + if (++delta == 0) { + return punycode_overflow; + } + } + + if (input[j] == n) { + /* Represent delta as a generalized variable-length integer: */ + + for (q = delta, k = base;; k += base) { + if (out >= max_out) { + return punycode_big_output; + } + t = k <= bias /* + tmin */ ? tmin : /* +tmin not needed */ + k >= bias + tmax ? tmax + : k - bias; + if (q < t) { + break; + } + output[out++] = encode_digit(t + (q - t) % (base - t), 0); + q = (q - t) / (base - t); + } + + output[out++] = encode_digit(q, case_flags && case_flags[j]); + bias = adapt(delta, h + 1, h == b); + delta = 0; + ++h; + } + } + + ++delta, ++n; + } + + *output_length = out; + return punycode_success; +} + +/*** Main decode function ***/ + +enum punycode_status punycode_decode(punycode_uint input_length, + const char input[], + punycode_uint* output_length, + punycode_uint output[], + unsigned char case_flags[]) { + punycode_uint n, out, i, max_out, bias, b, j, in, oldi, w, k, digit, t; + + if (!input_length) { + return punycode_bad_input; + } + + /* Initialize the state: */ + + n = initial_n; + out = i = 0; + max_out = *output_length; + bias = initial_bias; + + /* Handle the basic code points: Let b be the number of input code */ + /* points before the last delimiter, or 0 if there is none, then */ + /* copy the first b code points to the output. */ + + for (b = 0, j = input_length - 1; j > 0; --j) { + if (delim(input[j])) { + b = j; + break; + } + } + if (b > max_out) { + return punycode_big_output; + } + + for (j = 0; j < b; ++j) { + if (case_flags) { + case_flags[out] = flagged(input[j]); + } + if (!basic(input[j])) { + return punycode_bad_input; + } + output[out++] = input[j]; + } + + /* Main decoding loop: Start just after the last delimiter if any */ + /* basic code points were copied; start at the beginning otherwise. */ + + for (in = b > 0 ? b + 1 : 0; in < input_length; ++out) { + /* in is the index of the next character to be consumed, and */ + /* out is the number of code points in the output array. */ + + /* Decode a generalized variable-length integer into delta, */ + /* which gets added to i. The overflow checking is easier */ + /* if we increase i as we go, then subtract off its starting */ + /* value at the end to obtain delta. */ + + for (oldi = i, w = 1, k = base;; k += base) { + if (in >= input_length) { + return punycode_bad_input; + } + digit = decode_digit(input[in++]); + if (digit >= base) { + return punycode_bad_input; + } + if (digit > (maxint - i) / w) { + return punycode_overflow; + } + i += digit * w; + t = k <= bias /* + tmin */ ? tmin : /* +tmin not needed */ + k >= bias + tmax ? tmax + : k - bias; + if (digit < t) { + break; + } + if (w > maxint / (base - t)) { + return punycode_overflow; + } + w *= (base - t); + } + + bias = adapt(i - oldi, out + 1, oldi == 0); + + /* i was supposed to wrap around from out+1 to 0, */ + /* incrementing n each time, so we'll fix that now: */ + + if (i / (out + 1) > maxint - n) { + return punycode_overflow; + } + n += i / (out + 1); + i %= (out + 1); + + /* Insert n at position i of the output: */ + + /* not needed for Punycode: */ + /* if (decode_digit(n) <= base) return punycode_invalid_input; */ + if (out >= max_out) { + return punycode_big_output; + } + + if (case_flags) { + memmove(case_flags + i + 1, case_flags + i, out - i); + /* Case of last character determines uppercase flag: */ + case_flags[i] = flagged(input[in - 1]); + } + + memmove(output + i + 1, output + i, (out - i) * sizeof *output); + output[i++] = n; + } + + *output_length = out; + return punycode_success; +} diff --git a/netwerk/dns/punycode.h b/netwerk/dns/punycode.h new file mode 100644 index 0000000000..6a626c7a07 --- /dev/null +++ b/netwerk/dns/punycode.h @@ -0,0 +1,106 @@ +/* +punycode.c from RFC 3492 +http://www.nicemice.net/idn/ +Adam M. Costello +http://www.nicemice.net/amc/ + +This is ANSI C code (C89) implementing Punycode (RFC 3492). + + + +C. Disclaimer and license + + Regarding this entire document or any portion of it (including + the pseudocode and C code), the author makes no guarantees and + is not responsible for any damage resulting from its use. The + author grants irrevocable permission to anyone to use, modify, + and distribute it in any way that does not diminish the rights + of anyone else to use, modify, and distribute it, provided that + redistributed derivative works do not contain misleading author or + version information. Derivative works need not be licensed under + similar terms. +*/ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/************************************************************/ +/* Public interface (would normally go in its own .h file): */ + +#include <limits.h> + +enum punycode_status { + punycode_success, + punycode_bad_input, /* Input is invalid. */ + punycode_big_output, /* Output would exceed the space provided. */ + punycode_overflow /* Input needs wider integers to process. */ +}; + +#if UINT_MAX >= (1 << 26) - 1 +typedef unsigned int punycode_uint; +#else +typedef unsigned long punycode_uint; +#endif + +enum punycode_status punycode_encode(punycode_uint input_length, + const punycode_uint input[], + const unsigned char case_flags[], + punycode_uint* output_length, + char output[]); + +/* punycode_encode() converts Unicode to Punycode. The input */ +/* is represented as an array of Unicode code points (not code */ +/* units; surrogate pairs are not allowed), and the output */ +/* will be represented as an array of ASCII code points. The */ +/* output string is *not* null-terminated; it will contain */ +/* zeros if and only if the input contains zeros. (Of course */ +/* the caller can leave room for a terminator and add one if */ +/* needed.) The input_length is the number of code points in */ +/* the input. The output_length is an in/out argument: the */ +/* caller passes in the maximum number of code points that it */ +/* can receive, and on successful return it will contain the */ +/* number of code points actually output. The case_flags array */ +/* holds input_length boolean values, where nonzero suggests that */ +/* the corresponding Unicode character be forced to uppercase */ +/* after being decoded (if possible), and zero suggests that */ +/* it be forced to lowercase (if possible). ASCII code points */ +/* are encoded literally, except that ASCII letters are forced */ +/* to uppercase or lowercase according to the corresponding */ +/* uppercase flags. If case_flags is a null pointer then ASCII */ +/* letters are left as they are, and other code points are */ +/* treated as if their uppercase flags were zero. The return */ +/* value can be any of the punycode_status values defined above */ +/* except punycode_bad_input; if not punycode_success, then */ +/* output_size and output might contain garbage. */ + +enum punycode_status punycode_decode(punycode_uint input_length, + const char input[], + punycode_uint* output_length, + punycode_uint output[], + unsigned char case_flags[]); + +/* punycode_decode() converts Punycode to Unicode. The input is */ +/* represented as an array of ASCII code points, and the output */ +/* will be represented as an array of Unicode code points. The */ +/* input_length is the number of code points in the input. The */ +/* output_length is an in/out argument: the caller passes in */ +/* the maximum number of code points that it can receive, and */ +/* on successful return it will contain the actual number of */ +/* code points output. The case_flags array needs room for at */ +/* least output_length values, or it can be a null pointer if the */ +/* case information is not needed. A nonzero flag suggests that */ +/* the corresponding Unicode character be forced to uppercase */ +/* by the caller (if possible), while zero suggests that it be */ +/* forced to lowercase (if possible). ASCII code points are */ +/* output already in the proper case, but their flags will be set */ +/* appropriately so that applying the flags would be harmless. */ +/* The return value can be any of the punycode_status values */ +/* defined above; if not punycode_success, then output_length, */ +/* output, and case_flags might contain garbage. On success, the */ +/* decoder will never need to write an output_length greater than */ +/* input_length, because of how the encoding is defined. */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/netwerk/dns/tests/moz.build b/netwerk/dns/tests/moz.build new file mode 100644 index 0000000000..fb176c1bd3 --- /dev/null +++ b/netwerk/dns/tests/moz.build @@ -0,0 +1,7 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DIRS += ["unit"] diff --git a/netwerk/dns/tests/unit/data/fake_public_suffix_list.dat b/netwerk/dns/tests/unit/data/fake_public_suffix_list.dat new file mode 100644 index 0000000000..ca1da32eeb --- /dev/null +++ b/netwerk/dns/tests/unit/data/fake_public_suffix_list.dat @@ -0,0 +1,57 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +// This is a fake suffix list created only for the purposes of testing, +// The real PSL is rather large thus this file is kept small by cutting out most of the suffixes + +// The Original list can be found at https://publicsuffix.org/list/public_suffix_list.dat, +// Learn more about the PSL at https://publicsuffix.org. + +// .xpcshelltest is the fake domain created specifically for the the tests while +// the others are ripped from the real list, serving as examples. + +// This fake public suffix list was used to create the binary file fake_remote_dafsa.bin +// The binary is built at compile time and can be found at +// obj-dir/_tests/xpcshell/netwerk/dns/tests/unit/data/ + +// The list created with help of netwerk/dns/prepare_tlds.py and xpcom/ds/tools/make_dafsa.py +// The build directive for the binary is at moz.build at netwerk/dns/tests/unit/data/ + + + +// ===BEGIN ICANN DOMAINS=== + +// xpcshelltest : Used in tests +xpcshelltest +website.xpcshelltest +com.xpcshelltest +edu.xpcshelltest +gov.xpcshelltest +net.xpcshelltest +mil.xpcshelltest +org.xpcshelltest + +// ac : https://en.wikipedia.org/wiki/.ac +ac +coc.ac +com.ac +edu.ac +gov.ac +net.ac +mil.ac +org.ac + +// bj : https://en.wikipedia.org/wiki/.bj +bj +asso.bj +barreau.bj +gouv.bj + +// bm : http://www.bermudanic.bm/dnr-text.txt +bm +com.bm +edu.bm +gov.bm +net.bm +org.bm diff --git a/netwerk/dns/tests/unit/data/moz.build b/netwerk/dns/tests/unit/data/moz.build new file mode 100644 index 0000000000..0f5a164f77 --- /dev/null +++ b/netwerk/dns/tests/unit/data/moz.build @@ -0,0 +1,14 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +GeneratedFile( + "fake_remote_dafsa.bin", + script="../../../prepare_tlds.py", + inputs=["fake_public_suffix_list.dat"], + flags=["bin"], +) + +TEST_HARNESS_FILES.xpcshell.netwerk.dns.tests.unit.data += ["!fake_remote_dafsa.bin"] diff --git a/netwerk/dns/tests/unit/moz.build b/netwerk/dns/tests/unit/moz.build new file mode 100644 index 0000000000..e5ce568e70 --- /dev/null +++ b/netwerk/dns/tests/unit/moz.build @@ -0,0 +1,7 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DIRS += ["data"] diff --git a/netwerk/dns/tests/unit/test_PublicSuffixList.js b/netwerk/dns/tests/unit/test_PublicSuffixList.js new file mode 100644 index 0000000000..6e0a9400cd --- /dev/null +++ b/netwerk/dns/tests/unit/test_PublicSuffixList.js @@ -0,0 +1,176 @@ +/* Any copyright is dedicated to the Public Domain. + * https://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { PublicSuffixList } = ChromeUtils.import( + "resource://gre/modules/netwerk-dns/PublicSuffixList.jsm" +); +const { TestUtils } = ChromeUtils.import( + "resource://testing-common/TestUtils.jsm" +); + +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); + +const CLIENT = PublicSuffixList.CLIENT; +const SIGNAL = "public-suffix-list-updated"; + +const PAYLOAD_UPDATED_RECORDS = { + current: [{ id: "tld-dafsa", "commit-hash": "current-commit-hash" }], + created: [], + updated: [ + { + old: { id: "tld-dafsa", "commit-hash": "current-commit-hash" }, + new: { id: "tld-dafsa", "commit-hash": "new-commit-hash" }, + }, + ], + deleted: [], +}; +const PAYLOAD_CREATED_RECORDS = { + current: [], + created: [ + { + id: "tld-dafsa", + "commit-hash": "new-commit-hash", + attachment: {}, + }, + ], + updated: [], + deleted: [], +}; +const PAYLOAD_UPDATED_AND_CREATED_RECORDS = { + current: [{ id: "tld-dafsa", "commit-hash": "current-commit-hash" }], + created: [{ id: "tld-dafsa", "commit-hash": "another-commit-hash" }], + updated: [ + { + old: { id: "tld-dafsa", "commit-hash": "current-commit-hash" }, + new: { id: "tld-dafsa", "commit-hash": "new-commit-hash" }, + }, + ], + deleted: [], +}; + +const fakeDafsaBinFile = do_get_file("data/fake_remote_dafsa.bin"); +const mockedFilePath = fakeDafsaBinFile.path; + +function setup() { + Services.prefs.setBoolPref("network.psl.onUpdate_notify", true); +} +setup(); +registerCleanupFunction(() => { + Services.prefs.clearUserPref("network.psl.onUpdate_notify"); +}); + +/** + * downloadCalled is used by mockDownload() and resetMockDownload() + * to keep track weather CLIENT.attachments.download is called or not + * downloadBackup will help restore CLIENT.attachments.download to original definition + * notifyUpdateBackup will help restore PublicSuffixList.notifyUpdate to original definition + */ +let downloadCalled = false; +const downloadBackup = CLIENT.attachments.download; + +// returns a fake fileURI and sends a signal with filePath and no nsifile +const mockDownload = () => { + downloadCalled = false; + CLIENT.attachments.download = async rec => { + downloadCalled = true; + return `file://${mockedFilePath}`; // Create a fake file URI + }; +}; + +// resetMockDownload() must be run at the end of the test that uses mockDownload() +const resetMockDownload = () => { + CLIENT.attachments.download = downloadBackup; +}; + +add_task(async () => { + info("File path sent when record is in DB."); + + await CLIENT.db.clear(); // Make sure there's no record initially + await CLIENT.db.create({ + id: "tld-dafsa", + "commit-hash": "fake-commit-hash", + attachment: {}, + }); + + mockDownload(); + + const promiseSignal = TestUtils.topicObserved(SIGNAL); + await PublicSuffixList.init(); + const observed = await promiseSignal; + + Assert.equal( + observed[1], + mockedFilePath, + "File path sent when record is in DB." + ); + await CLIENT.db.clear(); // Clean up the mockDownloaded record + resetMockDownload(); +}); + +add_task(async () => { + info("File path sent when record updated."); + + mockDownload(); + + const promiseSignal = TestUtils.topicObserved(SIGNAL); + await PublicSuffixList.init(); + await CLIENT.emit("sync", { data: PAYLOAD_UPDATED_RECORDS }); + const observed = await promiseSignal; + + Assert.equal( + observed[1], + mockedFilePath, + "File path sent when record updated." + ); + resetMockDownload(); +}); + +add_task(async () => { + info("Attachment downloaded when record created."); + + mockDownload(); + + await PublicSuffixList.init(); + await CLIENT.emit("sync", { data: PAYLOAD_CREATED_RECORDS }); + + Assert.equal( + downloadCalled, + true, + "Attachment downloaded when record created." + ); + resetMockDownload(); +}); + +add_task(async () => { + info("Attachment downloaded when record updated."); + + mockDownload(); + + await PublicSuffixList.init(); + await CLIENT.emit("sync", { data: PAYLOAD_UPDATED_RECORDS }); + + Assert.equal( + downloadCalled, + true, + "Attachment downloaded when record updated." + ); + resetMockDownload(); +}); + +add_task(async () => { + info("No download when more than one record is changed."); + + mockDownload(); + + await PublicSuffixList.init(); + await CLIENT.emit("sync", { data: PAYLOAD_UPDATED_AND_CREATED_RECORDS }); + + Assert.equal( + downloadCalled, + false, + "No download when more than one record is changed." + ); + resetMockDownload(); +}); diff --git a/netwerk/dns/tests/unit/test_nsEffectiveTLDService_Reload_DAFSA.js b/netwerk/dns/tests/unit/test_nsEffectiveTLDService_Reload_DAFSA.js new file mode 100644 index 0000000000..2758d2627f --- /dev/null +++ b/netwerk/dns/tests/unit/test_nsEffectiveTLDService_Reload_DAFSA.js @@ -0,0 +1,34 @@ +/* Any copyright is dedicated to the Public Domain. + * https://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); + +const SIGNAL = "public-suffix-list-updated"; + +add_task(async () => { + info("Before fake dafsa reload."); + + let suffix = Services.eTLD.getPublicSuffixFromHost("website.xpcshelltest"); + Assert.equal( + suffix, + "xpcshelltest", + "Fake Suffix does not exist in current PSL." + ); +}); + +add_task(async () => { + info("After fake dafsa reload."); + + // reload the PSL with fake data containing .xpcshelltest + const fakeDafsaFile = do_get_file("data/fake_remote_dafsa.bin"); + Services.obs.notifyObservers(fakeDafsaFile, SIGNAL, fakeDafsaFile.path); + + let suffix = Services.eTLD.getPublicSuffixFromHost("website.xpcshelltest"); + Assert.equal( + suffix, + "website.xpcshelltest", + "Fake Suffix now exists in PSL after DAFSA reload." + ); +}); diff --git a/netwerk/dns/tests/unit/test_nsEffectiveTLDService_getKnownPublicSuffix.js b/netwerk/dns/tests/unit/test_nsEffectiveTLDService_getKnownPublicSuffix.js new file mode 100644 index 0000000000..3d63fc29b4 --- /dev/null +++ b/netwerk/dns/tests/unit/test_nsEffectiveTLDService_getKnownPublicSuffix.js @@ -0,0 +1,46 @@ +/* Any copyright is dedicated to the Public Domain. + * https://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests getPublicSuffix with the validate argument. + */ + +"use strict"; + +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); + +add_task(() => { + for (let [suffix, isKnown] of [ + ["", false], + [null, false], + ["mozbacon", false], + ["com", true], + ["circle", true], + ["bd", true], + ["gov.bd", true], + ["ck", true], + ["www.ck", true], + ["bs", true], + ["com.bs", true], + ["網絡.cn", true], + ["valléedaoste.it", true], + ["aurskog-høland.no", true], + ["公司.香港", true], + ["भारतम्", true], + ["فلسطين", true], + ]) { + let origin = "test." + suffix; + Assert.equal( + !!Services.eTLD.getKnownPublicSuffixFromHost(origin), + isKnown, + `"${suffix}" should ${isKnown ? " " : "not "}be a known public suffix` + ); + Assert.equal( + !!Services.eTLD.getKnownPublicSuffix( + Services.io.newURI("http://" + origin) + ), + isKnown, + `"${suffix}" should ${isKnown ? " " : "not "}be a known public suffix` + ); + } +}); diff --git a/netwerk/dns/tests/unit/xpcshell.ini b/netwerk/dns/tests/unit/xpcshell.ini new file mode 100644 index 0000000000..1b72bc700a --- /dev/null +++ b/netwerk/dns/tests/unit/xpcshell.ini @@ -0,0 +1,12 @@ +[DEFAULT] +head = ../../../../services/common/tests/unit/head_global.js ../../../../services/common/tests/unit/head_helpers.js +firefox-appdir = browser +support-files = + data/** + !/services/common/tests/unit/head_global.js + !/services/common/tests/unit/head_helpers.js + +[test_PublicSuffixList.js] +tags = remote-settings +[test_nsEffectiveTLDService_getKnownPublicSuffix.js] +[test_nsEffectiveTLDService_Reload_DAFSA.js] |