summaryrefslogtreecommitdiffstats
path: root/netwerk/dns/ChildDNSService.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--netwerk/dns/ChildDNSService.cpp513
1 files changed, 513 insertions, 0 deletions
diff --git a/netwerk/dns/ChildDNSService.cpp b/netwerk/dns/ChildDNSService.cpp
new file mode 100644
index 0000000000..8ef9f33329
--- /dev/null
+++ b/netwerk/dns/ChildDNSService.cpp
@@ -0,0 +1,513 @@
+/* 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 "DNSAdditionalInfo.h"
+#include "TRRService.h"
+
+namespace mozilla {
+namespace net {
+
+//-----------------------------------------------------------------------------
+// ChildDNSService
+//-----------------------------------------------------------------------------
+
+static StaticRefPtr<ChildDNSService> gChildDNSService;
+
+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_WARN_IF(!NS_IsMainThread())) {
+ return nullptr;
+ }
+ gChildDNSService = new ChildDNSService();
+ gChildDNSService->Init();
+ ClearOnShutdown(&gChildDNSService);
+ }
+
+ return do_AddRef(gChildDNSService);
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(ChildDNSService, DNSServiceBase, nsIDNSService,
+ nsPIDNSService)
+
+ChildDNSService::ChildDNSService() {
+ 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, int32_t aPort,
+ uint16_t aType, const OriginAttributes& aOriginAttributes,
+ nsIDNSService::DNSFlags aFlags, uintptr_t aListenerAddr,
+ nsACString& aHashKey) {
+ aHashKey.Assign(aHost);
+ aHashKey.Assign(aTrrServer);
+ aHashKey.AppendInt(aPort);
+ 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, nsIDNSService::DNSFlags flags,
+ nsIDNSAdditionalInfo* aInfo, nsIDNSListener* listener,
+ nsIEventTarget* target_, const OriginAttributes& aOriginAttributes,
+ nsICancelable** result) {
+ if (XRE_IsContentProcess()) {
+ NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE);
+ }
+
+ if (DNSForbiddenByActiveProxy(hostname, 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;
+ }
+
+ bool resolveDNSInSocketProcess = false;
+ if (XRE_IsParentProcess() && nsIOService::UseSocketProcess()) {
+ resolveDNSInSocketProcess = true;
+ if (type != nsIDNSService::RESOLVE_TYPE_DEFAULT &&
+ (mTRRServiceParent->Mode() != nsIDNSService::MODE_TRRFIRST &&
+ mTRRServiceParent->Mode() != nsIDNSService::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, DNSAdditionalInfo::URL(aInfo), DNSAdditionalInfo::Port(aInfo),
+ type, aOriginAttributes, flags, listener, target);
+ RefPtr<DNSRequestActor> dnsReq;
+ if (resolveDNSInSocketProcess) {
+ dnsReq = new DNSRequestParent(sender);
+ if (!mTRRServiceParent->TRRConnectionInfoInited()) {
+ mTRRServiceParent->InitTRRConnectionInfo();
+ }
+ } else {
+ dnsReq = new DNSRequestChild(sender);
+ }
+
+ {
+ MutexAutoLock lock(mPendingRequestsLock);
+ nsCString key;
+ GetDNSRecordHashKey(hostname, DNSAdditionalInfo::URL(aInfo),
+ DNSAdditionalInfo::Port(aInfo), type, aOriginAttributes,
+ flags, originalListenerAddr, key);
+ mPendingRequests.GetOrInsertNew(key)->AppendElement(sender);
+ }
+
+ sender->StartRequest();
+
+ sender.forget(result);
+ return NS_OK;
+}
+
+nsresult ChildDNSService::CancelAsyncResolveInternal(
+ const nsACString& aHostname, uint16_t aType, nsIDNSService::DNSFlags aFlags,
+ nsIDNSAdditionalInfo* aInfo, 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, DNSAdditionalInfo::URL(aInfo),
+ DNSAdditionalInfo::Port(aInfo), 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,
+ nsIDNSService::DNSFlags flags,
+ nsIDNSAdditionalInfo* aInfo,
+ nsIDNSListener* listener, nsIEventTarget* target_,
+ JS::Handle<JS::Value> 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, aInfo, listener, target_,
+ attrs, result);
+}
+
+NS_IMETHODIMP
+ChildDNSService::AsyncResolveNative(
+ const nsACString& hostname, nsIDNSService::ResolveType aType,
+ nsIDNSService::DNSFlags flags, nsIDNSAdditionalInfo* aInfo,
+ nsIDNSListener* listener, nsIEventTarget* target_,
+ const OriginAttributes& aOriginAttributes, nsICancelable** result) {
+ return AsyncResolveInternal(hostname, aType, flags, aInfo, listener, target_,
+ aOriginAttributes, result);
+}
+
+NS_IMETHODIMP
+ChildDNSService::NewAdditionalInfo(const nsACString& aTrrURL, int32_t aPort,
+ nsIDNSAdditionalInfo** aInfo) {
+ RefPtr<DNSAdditionalInfo> res = new DNSAdditionalInfo(aTrrURL, aPort);
+ res.forget(aInfo);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSService::CancelAsyncResolve(const nsACString& aHostname,
+ nsIDNSService::ResolveType aType,
+ nsIDNSService::DNSFlags aFlags,
+ nsIDNSAdditionalInfo* aInfo,
+ nsIDNSListener* aListener, nsresult aReason,
+ JS::Handle<JS::Value> 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, aInfo, aListener,
+ aReason, attrs);
+}
+
+NS_IMETHODIMP
+ChildDNSService::CancelAsyncResolveNative(
+ const nsACString& aHostname, nsIDNSService::ResolveType aType,
+ nsIDNSService::DNSFlags aFlags, nsIDNSAdditionalInfo* aInfo,
+ nsIDNSListener* aListener, nsresult aReason,
+ const OriginAttributes& aOriginAttributes) {
+ return CancelAsyncResolveInternal(aHostname, aType, aFlags, aInfo, aListener,
+ aReason, aOriginAttributes);
+}
+
+NS_IMETHODIMP
+ChildDNSService::Resolve(const nsACString& hostname,
+ nsIDNSService::DNSFlags flags,
+ JS::Handle<JS::Value> 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,
+ nsIDNSService::DNSFlags 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::SetHeuristicDetectionResult(nsITRRSkipReason::value aValue) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+ChildDNSService::GetHeuristicDetectionResult(nsITRRSkipReason::value* aValue) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+ChildDNSService::GetTRRSkipReasonName(nsITRRSkipReason::value aValue,
+ nsACString& aName) {
+ return mozilla::net::GetTRRSkipReasonName(aValue, aName);
+}
+
+NS_IMETHODIMP
+ChildDNSService::GetCurrentTrrURI(nsACString& aURI) {
+ if (!mTRRServiceParent) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ mTRRServiceParent->GetURI(aURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSService::GetCurrentTrrMode(nsIDNSService::ResolverMode* aMode) {
+ if (XRE_IsContentProcess()) {
+ *aMode = mTRRMode;
+ return NS_OK;
+ }
+ if (!mTRRServiceParent) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ *aMode = mTRRServiceParent->Mode();
+ return NS_OK;
+}
+
+void ChildDNSService::SetTRRModeInChild(
+ nsIDNSService::ResolverMode mode,
+ nsIDNSService::ResolverMode modeFromPref) {
+ if (!XRE_IsContentProcess()) {
+ MOZ_ASSERT(false, "Why are we calling this?");
+ return;
+ }
+ mTRRMode = mode;
+ TRRService::SetCurrentTRRMode(modeFromPref);
+}
+
+NS_IMETHODIMP
+ChildDNSService::GetCurrentTrrConfirmationState(uint32_t* aConfirmationState) {
+ if (!mTRRServiceParent) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ *aConfirmationState = mTRRServiceParent->GetConfirmationState();
+ 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.
+ nsIDNSService::DNSFlags 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->mPort, 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() {
+ ReadPrefs(nullptr);
+
+ nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+ if (prefs) {
+ AddPrefObserver(prefs);
+ }
+
+ 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) {
+ if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
+ // Reread prefs
+ ReadPrefs(NS_ConvertUTF16toUTF8(data).get());
+ }
+ return NS_OK;
+}
+
+void ChildDNSService::SetTRRDomain(const nsACString& aTRRDomain) {
+ mTRRDomain = aTRRDomain;
+ TRRService::SetProviderDomain(aTRRDomain);
+}
+
+nsresult ChildDNSService::GetTRRDomainKey(nsACString& aTRRDomain) {
+ aTRRDomain = TRRService::ProviderKey();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSService::GetTrrDomain(nsACString& aTRRDomain) {
+ aTRRDomain = mTRRDomain;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSService::GetLastConfirmationStatus(nsresult* aConfirmationStatus) {
+ // XXX(valentin): Fix for socket process
+ *aConfirmationStatus = NS_OK;
+ return NS_OK;
+}
+
+NS_IMETHODIMP ChildDNSService::GetLastConfirmationSkipReason(
+ TRRSkippedReason* aSkipReason) {
+ // XXX(valentin): Fix for socket process
+ *aSkipReason = nsITRRSkipReason::TRR_UNSET;
+ return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla