зеркало из https://github.com/mozilla/gecko-dev.git
530 строки
17 KiB
C++
530 строки
17 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 "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;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ChildDNSService::GetODoHActivated(bool* aResult) {
|
|
NS_ENSURE_ARG(aResult);
|
|
|
|
*aResult = mODoHActivated;
|
|
return NS_OK;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
nsCOMPtr<nsIObserverService> observerService =
|
|
mozilla::services::GetObserverService();
|
|
if (observerService) {
|
|
observerService->AddObserver(this, "odoh-service-activated", false);
|
|
}
|
|
|
|
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());
|
|
} else if (!strcmp(topic, "odoh-service-activated")) {
|
|
mODoHActivated = u"true"_ns.Equals(data);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void ChildDNSService::SetTRRDomain(const nsACString& aTRRDomain) {
|
|
mTRRDomain = aTRRDomain;
|
|
TRRService::SetProviderDomain(aTRRDomain);
|
|
}
|
|
|
|
void ChildDNSService::GetTRRDomainKey(nsACString& aTRRDomain) {
|
|
aTRRDomain = TRRService::ProviderKey();
|
|
}
|
|
|
|
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
|