summaryrefslogtreecommitdiffstats
path: root/netwerk/dns/ChildDNSService.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'netwerk/dns/ChildDNSService.cpp')
-rw-r--r--netwerk/dns/ChildDNSService.cpp330
1 files changed, 330 insertions, 0 deletions
diff --git a/netwerk/dns/ChildDNSService.cpp b/netwerk/dns/ChildDNSService.cpp
new file mode 100644
index 000000000..a3a1f3347
--- /dev/null
+++ b/netwerk/dns/ChildDNSService.cpp
@@ -0,0 +1,330 @@
+/* This Source Code Form is subject to the terms of 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