123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.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 "nsICancelable.h"
- #include "nsIPrefService.h"
- #include "nsIPrefBranch.h"
- #include "nsIServiceManager.h"
- #include "nsIXPConnect.h"
- #include "nsProxyRelease.h"
- #include "nsReadableUtils.h"
- #include "nsString.h"
- #include "nsAutoPtr.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 "nsIOService.h"
- #include "nsCharSeparatedTokenizer.h"
- #include "nsNetAddr.h"
- #include "nsProxyRelease.h"
- #include "nsIObserverService.h"
- #include "nsINetworkLinkService.h"
- #include "mozilla/Attributes.h"
- #include "mozilla/net/NeckoCommon.h"
- #include "mozilla/net/ChildDNSService.h"
- #include "mozilla/net/DNSListenerProxy.h"
- #include "mozilla/Services.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 kPrefDnsOfflineLocalhost[] = "network.dns.offline-localhost";
- static const char kPrefDnsNotifyResolution[] = "network.dns.notifyResolution";
- //-----------------------------------------------------------------------------
- class nsDNSRecord : public nsIDNSRecord
- {
- public:
- NS_DECL_THREADSAFE_ISUPPORTS
- NS_DECL_NSIDNSRECORD
- explicit nsDNSRecord(nsHostRecord *hostRecord)
- : mHostRecord(hostRecord)
- , mIter(nullptr)
- , mIterGenCnt(-1)
- , mDone(false) {}
- private:
- virtual ~nsDNSRecord() = default;
- RefPtr<nsHostRecord> mHostRecord;
- NetAddrElement *mIter;
- int mIterGenCnt; // the generation count of
- // mHostRecord->addr_info when we
- // start iterating
- bool mDone;
- };
- NS_IMPL_ISUPPORTS(nsDNSRecord, nsIDNSRecord)
- 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);
- // if the record is for an IP address literal, then the canonical
- // host name is the IP address literal.
- const char *cname;
- {
- MutexAutoLock lock(mHostRecord->addr_info_lock);
- if (mHostRecord->addr_info)
- cname = mHostRecord->addr_info->mCanonicalName ?
- mHostRecord->addr_info->mCanonicalName :
- mHostRecord->addr_info->mHostName;
- else
- cname = mHostRecord->host;
- result.Assign(cname);
- }
- 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 = nullptr;
- mIterGenCnt = mHostRecord->addr_info_gencnt;
- }
- bool startedFresh = !mIter;
- do {
- if (!mIter) {
- mIter = mHostRecord->addr_info->mAddresses.getFirst();
- } else {
- mIter = mIter->getNext();
- }
- }
- while (mIter && mHostRecord->Blacklisted(&mIter->mAddress));
- if (!mIter && startedFresh) {
- // If everything was blacklisted we want to reset the blacklist (and
- // likely relearn it) and return the first address. That is better
- // than nothing.
- mHostRecord->ResetBlacklist();
- mIter = mHostRecord->addr_info->mAddresses.getFirst();
- }
- if (mIter) {
- memcpy(addr, &mIter->mAddress, sizeof(NetAddr));
- }
- mHostRecord->addr_info_lock.Unlock();
- if (!mIter) {
- mDone = true;
- 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, 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 (NetAddrElement *iter = mHostRecord->addr_info->mAddresses.getFirst();
- iter; iter = iter->getNext()) {
- if (mHostRecord->Blacklisted(&iter->mAddress)) {
- continue;
- }
- NetAddr *addr = aAddressArray.AppendElement(NetAddr());
- memcpy(addr, &iter->mAddress, sizeof(NetAddr));
- 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, 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;
- NS_ADDREF(*result = new nsNetAddr(&addr));
- 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 (NetAddrToString(&addr, 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;
- }
- NetAddrElement *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 = nullptr;
- mIterGenCnt = -1;
- mDone = false;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsDNSRecord::ReportUnusable(uint16_t aPort)
- {
- // right now we don't use the port in the blacklist
- 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 &&
- mIter) {
- mHostRecord->ReportUnusable(&mIter->mAddress);
- }
- return NS_OK;
- }
- //-----------------------------------------------------------------------------
- class nsDNSAsyncRequest final : public nsResolveHostCallback
- , public nsICancelable
- {
- ~nsDNSAsyncRequest() = default;
- public:
- NS_DECL_THREADSAFE_ISUPPORTS
- NS_DECL_NSICANCELABLE
- nsDNSAsyncRequest(nsHostResolver *res,
- const nsACString &host,
- nsIDNSListener *listener,
- uint16_t flags,
- uint16_t af,
- const nsACString &netInterface)
- : mResolver(res)
- , mHost(host)
- , mListener(listener)
- , mFlags(flags)
- , mAF(af)
- , mNetworkInterface(netInterface) {}
- void OnLookupComplete(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
- nsCOMPtr<nsIDNSListener> mListener;
- uint16_t mFlags;
- uint16_t mAF;
- nsCString mNetworkInterface;
- };
- void
- nsDNSAsyncRequest::OnLookupComplete(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)) {
- NS_ASSERTION(hostRecord, "no host record");
- rec = new nsDNSRecord(hostRecord);
- }
- mListener->OnLookupComplete(this, rec, status);
- mListener = nullptr;
- // release the reference to ourselves that was added before we were
- // handed off to the host resolver.
- NS_RELEASE_THIS();
- }
- bool
- nsDNSAsyncRequest::EqualsAsyncListener(nsIDNSListener *aListener)
- {
- nsCOMPtr<nsIDNSListenerProxy> wrapper = do_QueryInterface(mListener);
- if (wrapper) {
- nsCOMPtr<nsIDNSListener> originalListener;
- wrapper->GetOriginalListener(getter_AddRefs(originalListener));
- return aListener == originalListener;
- }
- return (aListener == mListener);
- }
- 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_IMPL_ISUPPORTS(nsDNSAsyncRequest, nsICancelable)
- NS_IMETHODIMP
- nsDNSAsyncRequest::Cancel(nsresult reason)
- {
- NS_ENSURE_ARG(NS_FAILED(reason));
- mResolver->DetachCallback(mHost.get(), mFlags, mAF, mNetworkInterface.get(),
- this, reason);
- return NS_OK;
- }
- //-----------------------------------------------------------------------------
- class nsDNSSyncRequest : public nsResolveHostCallback
- {
- public:
- explicit nsDNSSyncRequest(PRMonitor *mon)
- : mDone(false)
- , mStatus(NS_OK)
- , mMonitor(mon) {}
- virtual ~nsDNSSyncRequest() = default;
- void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult) override;
- bool EqualsAsyncListener(nsIDNSListener *aListener) override;
- size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const override;
- bool mDone;
- nsresult mStatus;
- RefPtr<nsHostRecord> mHostRecord;
- private:
- PRMonitor *mMonitor;
- };
- void
- nsDNSSyncRequest::OnLookupComplete(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)
- : 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)
- , mFirstTime(true)
- , mNotifyResolution(false)
- , mOfflineLocalhost(false)
- {
- }
- nsDNSService::~nsDNSService() = default;
- NS_IMPL_ISUPPORTS(nsDNSService, nsIDNSService, nsPIDNSService, nsIObserver,
- nsIMemoryReporter)
- /******************************************************************************
- * nsDNSService impl:
- * singleton instance ctor/dtor methods
- ******************************************************************************/
- static nsDNSService *gDNSService;
- nsIDNSService*
- nsDNSService::GetXPCOMSingleton()
- {
- if (IsNeckoChild()) {
- return ChildDNSService::GetSingleton();
- }
- return GetSingleton();
- }
- nsDNSService*
- nsDNSService::GetSingleton()
- {
- NS_ASSERTION(!IsNeckoChild(), "not a parent process");
- if (gDNSService) {
- NS_ADDREF(gDNSService);
- return gDNSService;
- }
- gDNSService = new nsDNSService();
- if (gDNSService) {
- NS_ADDREF(gDNSService);
- if (NS_FAILED(gDNSService->Init())) {
- NS_RELEASE(gDNSService);
- }
- }
- return gDNSService;
- }
- NS_IMETHODIMP
- nsDNSService::Init()
- {
- if (mResolver)
- return NS_OK;
- NS_ENSURE_TRUE(!mResolver, NS_ERROR_ALREADY_INITIALIZED);
- // prefs
- uint32_t maxCacheEntries = 400;
- uint32_t defaultCacheLifetime = 120; // seconds
- uint32_t defaultGracePeriod = 60; // seconds
- bool disableIPv6 = false;
- bool offlineLocalhost = true;
- bool disablePrefetch = false;
- bool blockDotOnion = true;
- int proxyType = nsIProtocolProxyService::PROXYCONFIG_DIRECT;
- bool notifyResolution = false;
- nsAdoptingCString ipv4OnlyDomains;
- nsAdoptingCString localDomains;
- // read prefs
- nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
- if (prefs) {
- int32_t val;
- if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheEntries, &val)))
- maxCacheEntries = (uint32_t) val;
- if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheExpiration, &val)))
- defaultCacheLifetime = val;
- if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheGrace, &val)))
- defaultGracePeriod = val;
- // ASSUMPTION: pref branch does not modify out params on failure
- prefs->GetBoolPref(kPrefDisableIPv6, &disableIPv6);
- prefs->GetCharPref(kPrefIPv4OnlyDomains, getter_Copies(ipv4OnlyDomains));
- prefs->GetCharPref(kPrefDnsLocalDomains, getter_Copies(localDomains));
- prefs->GetBoolPref(kPrefDnsOfflineLocalhost, &offlineLocalhost);
- prefs->GetBoolPref(kPrefDisablePrefetch, &disablePrefetch);
- prefs->GetBoolPref(kPrefBlockDotOnion, &blockDotOnion);
- // If a manual proxy is in use, disable prefetch implicitly
- prefs->GetIntPref("network.proxy.type", &proxyType);
- prefs->GetBoolPref(kPrefDnsNotifyResolution, ¬ifyResolution);
- if (mFirstTime) {
- mFirstTime = false;
- // 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(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
- // If a manual proxy is in use, disable prefetch implicitly
- prefs->AddObserver("network.proxy.type", this, false);
- }
- }
- nsCOMPtr<nsIObserverService> observerService =
- mozilla::services::GetObserverService();
- if (observerService) {
- observerService->AddObserver(this, "last-pb-context-exited", false);
- observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
- }
- nsDNSPrefetch::Initialize(this);
- nsCOMPtr<nsIIDNService> idn = do_GetService(NS_IDNSERVICE_CONTRACTID);
- RefPtr<nsHostResolver> res;
- nsresult rv = nsHostResolver::Create(maxCacheEntries,
- defaultCacheLifetime,
- defaultGracePeriod,
- getter_AddRefs(res));
- if (NS_SUCCEEDED(rv)) {
- // now, set all of our member variables while holding the lock
- MutexAutoLock lock(mLock);
- mResolver = res;
- mIDN = idn;
- mIPv4OnlyDomains = ipv4OnlyDomains; // exchanges buffer ownership
- mOfflineLocalhost = offlineLocalhost;
- mDisableIPv6 = disableIPv6;
- mBlockDotOnion = blockDotOnion;
- // Disable prefetching either by explicit preference or if a manual proxy is configured
- mDisablePrefetch = disablePrefetch || (proxyType == nsIProtocolProxyService::PROXYCONFIG_MANUAL);
- mLocalDomains.Clear();
- if (localDomains) {
- nsCCharSeparatedTokenizer tokenizer(localDomains, ',',
- nsCCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
- while (tokenizer.hasMoreTokens()) {
- mLocalDomains.PutEntry(tokenizer.nextToken());
- }
- }
- mNotifyResolution = notifyResolution;
- }
- RegisterWeakMemoryReporter(this);
- return rv;
- }
- 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");
- }
- 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;
- }
- nsresult
- nsDNSService::PreprocessHostname(bool aLocalDomain,
- const nsACString &aInput,
- nsIIDNService *aIDN,
- nsACString &aACE)
- {
- // Enforce RFC 7686
- if (mBlockDotOnion &&
- StringEndsWith(aInput, NS_LITERAL_CSTRING(".onion"))) {
- return NS_ERROR_UNKNOWN_HOST;
- }
- if (aLocalDomain) {
- aACE.AssignLiteral("localhost");
- 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;
- }
- NS_IMETHODIMP
- nsDNSService::AsyncResolve(const nsACString &aHostname,
- uint32_t flags,
- nsIDNSListener *listener,
- nsIEventTarget *target_,
- nsICancelable **result)
- {
- return AsyncResolveExtended(aHostname, flags, EmptyCString(), listener, target_,
- result);
- }
- NS_IMETHODIMP
- nsDNSService::AsyncResolveExtended(const nsACString &aHostname,
- uint32_t flags,
- const nsACString &aNetworkInterface,
- nsIDNSListener *listener,
- nsIEventTarget *target_,
- nsICancelable **result)
- {
- // grab reference to global host resolver and IDN service. beware
- // simultaneous shutdown!!
- RefPtr<nsHostResolver> res;
- nsCOMPtr<nsIIDNService> idn;
- nsCOMPtr<nsIEventTarget> target = target_;
- 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;
- 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) {
- nsCOMPtr<nsIThread> mainThread;
- NS_GetMainThread(getter_AddRefs(mainThread));
- target = do_QueryInterface(mainThread);
- }
- if (target) {
- listener = new DNSListenerProxy(listener, target);
- }
- uint16_t af = GetAFForLookup(hostname, flags);
- auto *req =
- new nsDNSAsyncRequest(res, hostname, listener, flags, af,
- aNetworkInterface);
- if (!req)
- return NS_ERROR_OUT_OF_MEMORY;
- NS_ADDREF(*result = req);
- // addref for resolver; will be released when OnLookupComplete is called.
- NS_ADDREF(req);
- rv = res->ResolveHost(req->mHost.get(), flags, af,
- req->mNetworkInterface.get(), req);
- if (NS_FAILED(rv)) {
- NS_RELEASE(req);
- NS_RELEASE(*result);
- }
- return rv;
- }
- NS_IMETHODIMP
- nsDNSService::CancelAsyncResolve(const nsACString &aHostname,
- uint32_t aFlags,
- nsIDNSListener *aListener,
- nsresult aReason)
- {
- return CancelAsyncResolveExtended(aHostname, aFlags, EmptyCString(), aListener,
- aReason);
- }
- NS_IMETHODIMP
- nsDNSService::CancelAsyncResolveExtended(const nsACString &aHostname,
- uint32_t aFlags,
- const nsACString &aNetworkInterface,
- nsIDNSListener *aListener,
- nsresult aReason)
- {
- // 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 = GetAFForLookup(hostname, aFlags);
- res->CancelAsyncRequest(hostname.get(), aFlags, af,
- nsPromiseFlatCString(aNetworkInterface).get(), aListener,
- aReason);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsDNSService::Resolve(const nsACString &aHostname,
- uint32_t flags,
- 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;
- }
- //
- // 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);
- nsDNSSyncRequest syncReq(mon);
- uint16_t af = GetAFForLookup(hostname, flags);
- rv = res->ResolveHost(hostname.get(), 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");
- auto *rec = new nsDNSRecord(syncReq.mHostRecord);
- if (!rec)
- rv = NS_ERROR_OUT_OF_MEMORY;
- else
- NS_ADDREF(*result = rec);
- }
- }
- 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)
- {
- // We are only getting called if a preference has changed or there's a
- // network link event.
- NS_ASSERTION(strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0 ||
- strcmp(topic, "last-pb-context-exited") == 0 ||
- strcmp(topic, NS_NETWORK_LINK_TOPIC) == 0,
- "unexpected observe call");
- 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;
- }
- if (flushCache) {
- mResolver->FlushCache();
- return NS_OK;
- }
- //
- // Shutdown and this function are both only called on the UI thread, so we don't
- // have to worry about mResolver being cleared out from under us.
- //
- // NOTE Shutting down and reinitializing the service like this is obviously
- // suboptimal if Observe gets called several times in a row, but we don't
- // expect that to be the case.
- //
- if (mResolver) {
- Shutdown();
- }
- Init();
- 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;
- }
- 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);
- 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;
- }
|