123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.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 "nsIDNSListener.h"
- #include "nsIIOService.h"
- #include "nsIThread.h"
- #include "nsThreadUtils.h"
- #include "nsIXPConnect.h"
- #include "nsIPrefService.h"
- #include "nsIProtocolProxyService.h"
- #include "nsNetCID.h"
- #include "mozilla/net/NeckoChild.h"
- #include "mozilla/net/DNSListenerProxy.h"
- #include "nsServiceManagerUtils.h"
- namespace mozilla {
- namespace net {
- //-----------------------------------------------------------------------------
- // ChildDNSService
- //-----------------------------------------------------------------------------
- static ChildDNSService *gChildDNSService;
- static const char kPrefNameDisablePrefetch[] = "network.dns.disablePrefetch";
- ChildDNSService* ChildDNSService::GetSingleton()
- {
- MOZ_ASSERT(IsNeckoChild());
- if (!gChildDNSService) {
- gChildDNSService = new ChildDNSService();
- }
- NS_ADDREF(gChildDNSService);
- return gChildDNSService;
- }
- NS_IMPL_ISUPPORTS(ChildDNSService,
- nsIDNSService,
- nsPIDNSService,
- nsIObserver)
- ChildDNSService::ChildDNSService()
- : mFirstTime(true)
- , mDisablePrefetch(false)
- , mPendingRequestsLock("DNSPendingRequestsLock")
- {
- MOZ_ASSERT(IsNeckoChild());
- }
- ChildDNSService::~ChildDNSService()
- {
- }
- void
- ChildDNSService::GetDNSRecordHashKey(const nsACString &aHost,
- uint32_t aFlags,
- const nsACString &aNetworkInterface,
- nsIDNSListener* aListener,
- nsACString &aHashKey)
- {
- aHashKey.Assign(aHost);
- aHashKey.AppendInt(aFlags);
- if (!aNetworkInterface.IsEmpty()) {
- aHashKey.Append(aNetworkInterface);
- }
- aHashKey.AppendPrintf("%p", aListener);
- }
- //-----------------------------------------------------------------------------
- // ChildDNSService::nsIDNSService
- //-----------------------------------------------------------------------------
- NS_IMETHODIMP
- ChildDNSService::AsyncResolve(const nsACString &hostname,
- uint32_t flags,
- nsIDNSListener *listener,
- nsIEventTarget *target_,
- nsICancelable **result)
- {
- return AsyncResolveExtended(hostname, flags, EmptyCString(), listener,
- target_, result);
- }
- NS_IMETHODIMP
- ChildDNSService::AsyncResolveExtended(const nsACString &hostname,
- uint32_t flags,
- const nsACString &aNetworkInterface,
- nsIDNSListener *listener,
- nsIEventTarget *target_,
- nsICancelable **result)
- {
- NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE);
- if (mDisablePrefetch && (flags & RESOLVE_SPECULATE)) {
- return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
- }
- // We need original flags for the pending requests hash.
- uint32_t originalFlags = flags;
- // Support apps being 'offline' even if parent is not: avoids DNS traffic by
- // apps that have been told they are offline.
- if (GetOffline()) {
- flags |= RESOLVE_OFFLINE;
- }
- // We need original listener for the pending requests hash.
- nsIDNSListener *originalListener = listener;
- // make sure JS callers get notification on the main thread
- nsCOMPtr<nsIEventTarget> target = target_;
- nsCOMPtr<nsIXPConnectWrappedJS> wrappedListener = do_QueryInterface(listener);
- if (wrappedListener && !target) {
- nsCOMPtr<nsIThread> mainThread;
- NS_GetMainThread(getter_AddRefs(mainThread));
- target = do_QueryInterface(mainThread);
- }
- 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<DNSRequestChild> childReq =
- new DNSRequestChild(nsCString(hostname), flags,
- nsCString(aNetworkInterface),
- listener, target);
- {
- MutexAutoLock lock(mPendingRequestsLock);
- nsCString key;
- GetDNSRecordHashKey(hostname, originalFlags, aNetworkInterface,
- originalListener, key);
- nsTArray<RefPtr<DNSRequestChild>> *hashEntry;
- if (mPendingRequests.Get(key, &hashEntry)) {
- hashEntry->AppendElement(childReq);
- } else {
- hashEntry = new nsTArray<RefPtr<DNSRequestChild>>();
- hashEntry->AppendElement(childReq);
- mPendingRequests.Put(key, hashEntry);
- }
- }
- childReq->StartRequest();
- childReq.forget(result);
- return NS_OK;
- }
- NS_IMETHODIMP
- ChildDNSService::CancelAsyncResolve(const nsACString &aHostname,
- uint32_t aFlags,
- nsIDNSListener *aListener,
- nsresult aReason)
- {
- return CancelAsyncResolveExtended(aHostname, aFlags, EmptyCString(),
- aListener, aReason);
- }
- NS_IMETHODIMP
- ChildDNSService::CancelAsyncResolveExtended(const nsACString &aHostname,
- uint32_t aFlags,
- const nsACString &aNetworkInterface,
- nsIDNSListener *aListener,
- nsresult aReason)
- {
- if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE)) {
- return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
- }
- MutexAutoLock lock(mPendingRequestsLock);
- nsTArray<RefPtr<DNSRequestChild>> *hashEntry;
- nsCString key;
- GetDNSRecordHashKey(aHostname, aFlags, aNetworkInterface, aListener, key);
- if (mPendingRequests.Get(key, &hashEntry)) {
- // We cancel just one.
- hashEntry->ElementAt(0)->Cancel(aReason);
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- ChildDNSService::Resolve(const nsACString &hostname,
- uint32_t flags,
- 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::GetMyHostName(nsACString &result)
- {
- // TODO: get value from parent during PNecko construction?
- return NS_ERROR_NOT_AVAILABLE;
- }
- void
- ChildDNSService::NotifyRequestDone(DNSRequestChild *aDnsRequest)
- {
- // We need the original flags and listener for the pending requests hash.
- uint32_t originalFlags = aDnsRequest->mFlags & ~RESOLVE_OFFLINE;
- nsCOMPtr<nsIDNSListener> originalListener = aDnsRequest->mListener;
- nsCOMPtr<nsIDNSListenerProxy> wrapper = do_QueryInterface(originalListener);
- if (wrapper) {
- wrapper->GetOriginalListener(getter_AddRefs(originalListener));
- if (NS_WARN_IF(!originalListener)) {
- MOZ_ASSERT(originalListener);
- return;
- }
- }
- MutexAutoLock lock(mPendingRequestsLock);
- nsCString key;
- GetDNSRecordHashKey(aDnsRequest->mHost, originalFlags,
- aDnsRequest->mNetworkInterface, originalListener, key);
- nsTArray<RefPtr<DNSRequestChild>> *hashEntry;
- if (mPendingRequests.Get(key, &hashEntry)) {
- int idx;
- if ((idx = hashEntry->IndexOf(aDnsRequest))) {
- 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;
- int proxyType = nsIProtocolProxyService::PROXYCONFIG_DIRECT;
- nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
- if (prefs) {
- prefs->GetIntPref("network.proxy.type", &proxyType);
- 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 ||
- (proxyType == 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;
- }
- bool
- ChildDNSService::GetOffline() const
- {
- bool offline = false;
- nsCOMPtr<nsIIOService> io = do_GetService(NS_IOSERVICE_CONTRACTID);
- if (io) {
- io->GetOffline(&offline);
- }
- return offline;
- }
- //-----------------------------------------------------------------------------
- // 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
|