gecko-dev/netwerk/dns/ChildDNSService.cpp
Nathan Froyd 19a0b532bc Bug 1443613 - don't allocate temporary strings for DNSRequestChild; r=mayhemer
DNSRequestChild's constructor takes `const nsCString&` parameters.  If
you have a `const nsACString&` argument to pass in to the constructor,
you're forced to construct a temporary nsCString object to satisfy the
prototype.  But the temporary string will just get copied inside the
constructor and you'll have to destroy the temporary string object you
created.

Let's skip all this, and just have DNSRequestChild take `const
nsACString&` arguments instead, and avoid the temporary object.
2018-03-07 12:22:03 -05:00

453 lines
15 KiB
C++

/* 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/ClearOnShutdown.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/SystemGroup.h"
#include "mozilla/net/NeckoChild.h"
#include "mozilla/net/DNSListenerProxy.h"
#include "nsServiceManagerUtils.h"
namespace mozilla {
namespace net {
//-----------------------------------------------------------------------------
// ChildDNSService
//-----------------------------------------------------------------------------
static StaticRefPtr<ChildDNSService> gChildDNSService;
static const char kPrefNameDisablePrefetch[] = "network.dns.disablePrefetch";
already_AddRefed<ChildDNSService> ChildDNSService::GetSingleton()
{
MOZ_ASSERT(IsNeckoChild());
if (!gChildDNSService) {
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(IsNeckoChild());
}
ChildDNSService::~ChildDNSService()
{
}
void
ChildDNSService::GetDNSRecordHashKey(const nsACString &aHost,
const OriginAttributes &aOriginAttributes,
uint32_t aFlags,
const nsACString &aNetworkInterface,
nsIDNSListener* aListener,
nsACString &aHashKey)
{
aHashKey.Assign(aHost);
nsAutoCString originSuffix;
aOriginAttributes.CreateSuffix(originSuffix);
aHashKey.Assign(originSuffix);
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_,
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 AsyncResolveExtendedNative(hostname, flags, EmptyCString(),
listener, target_, attrs,
result);
}
NS_IMETHODIMP
ChildDNSService::AsyncResolveNative(const nsACString &hostname,
uint32_t flags,
nsIDNSListener *listener,
nsIEventTarget *target_,
const OriginAttributes &aOriginAttributes,
nsICancelable **result)
{
return AsyncResolveExtendedNative(hostname, flags, EmptyCString(),
listener, target_, aOriginAttributes,
result);
}
NS_IMETHODIMP
ChildDNSService::AsyncResolveExtended(const nsACString &aHostname,
uint32_t flags,
const nsACString &aNetworkInterface,
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 AsyncResolveExtendedNative(aHostname, flags, aNetworkInterface,
listener, target_, attrs,
result);
}
NS_IMETHODIMP
ChildDNSService::AsyncResolveExtendedNative(const nsACString &hostname,
uint32_t flags,
const nsACString &aNetworkInterface,
nsIDNSListener *listener,
nsIEventTarget *target_,
const OriginAttributes &aOriginAttributes,
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) {
target = SystemGroup::EventTargetFor(TaskCategory::Network);
}
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(hostname,
aOriginAttributes,
flags,
aNetworkInterface,
listener, target);
{
MutexAutoLock lock(mPendingRequestsLock);
nsCString key;
GetDNSRecordHashKey(hostname, aOriginAttributes, 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,
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 CancelAsyncResolveExtendedNative(aHostname, aFlags, EmptyCString(),
aListener, aReason, attrs);
}
NS_IMETHODIMP
ChildDNSService::CancelAsyncResolveNative(const nsACString &aHostname,
uint32_t aFlags,
nsIDNSListener *aListener,
nsresult aReason,
const OriginAttributes &aOriginAttributes)
{
return CancelAsyncResolveExtendedNative(aHostname, aFlags, EmptyCString(),
aListener, aReason, aOriginAttributes);
}
NS_IMETHODIMP
ChildDNSService::CancelAsyncResolveExtended(const nsACString &aHostname,
uint32_t aFlags,
const nsACString &aNetworkInterface,
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 CancelAsyncResolveExtendedNative(aHostname, aFlags, aNetworkInterface,
aListener, aReason, attrs);
}
NS_IMETHODIMP
ChildDNSService::CancelAsyncResolveExtendedNative(const nsACString &aHostname,
uint32_t aFlags,
const nsACString &aNetworkInterface,
nsIDNSListener *aListener,
nsresult aReason,
const OriginAttributes &aOriginAttributes)
{
if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE)) {
return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
}
MutexAutoLock lock(mPendingRequestsLock);
nsTArray<RefPtr<DNSRequestChild>> *hashEntry;
nsCString key;
GetDNSRecordHashKey(aHostname, aOriginAttributes, 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,
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::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, aDnsRequest->mOriginAttributes, 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